diff --git a/CODE-mp/Final/jk2/winquake.res b/CODE-mp/Final/jk2/winquake.res new file mode 100644 index 0000000..9bc4bc4 Binary files /dev/null and b/CODE-mp/Final/jk2/winquake.res differ diff --git a/CODE-mp/Release/jk2/winquake.res b/CODE-mp/Release/jk2/winquake.res new file mode 100644 index 0000000..9bc4bc4 Binary files /dev/null and b/CODE-mp/Release/jk2/winquake.res differ diff --git a/CODE-mp/Splines/math_angles.cpp b/CODE-mp/Splines/math_angles.cpp deleted file mode 100644 index 5da1510..0000000 --- a/CODE-mp/Splines/math_angles.cpp +++ /dev/null @@ -1,129 +0,0 @@ -#include "q_shared.h" -#include - -angles_t ang_zero( 0.0f, 0.0f, 0.0f ); - -void toAngles( mat3_t &src, angles_t &dst ) { - double theta; - double cp; - double sp; - - sp = src[ 0 ][ 2 ]; - - // cap off our sin value so that we don't get any NANs - if ( sp > 1.0 ) { - sp = 1.0; - } else if ( sp < -1.0 ) { - sp = -1.0; - } - - theta = -asin( sp ); - cp = cos( theta ); - - if ( cp > 8192 * FLT_EPSILON ) { - dst.pitch = theta * 180 / M_PI; - dst.yaw = atan2( src[ 0 ][ 1 ], src[ 0 ][ 0 ] ) * 180 / M_PI; - dst.roll = atan2( src[ 1 ][ 2 ], src[ 2 ][ 2 ] ) * 180 / M_PI; - } else { - dst.pitch = theta * 180 / M_PI; - dst.yaw = -atan2( src[ 1 ][ 0 ], src[ 1 ][ 1 ] ) * 180 / M_PI; - dst.roll = 0; - } -} - -void toAngles( quat_t &src, angles_t &dst ) { - mat3_t temp; - - toMatrix( src, temp ); - toAngles( temp, dst ); -} - -void toAngles( idVec3_t &src, angles_t &dst ) { - dst.pitch = src[ 0 ]; - dst.yaw = src[ 1 ]; - dst.roll = src[ 2 ]; -} - -void angles_t::toVectors( idVec3_t *forward, idVec3_t *right, idVec3_t *up ) { - float angle; - static float sr, sp, sy, cr, cp, cy; // static to help MS compiler fp bugs - - angle = yaw * ( M_PI * 2 / 360 ); - sy = sin( angle ); - cy = cos( angle ); - - angle = pitch * ( M_PI * 2 / 360 ); - sp = sin( angle ); - cp = cos( angle ); - - angle = roll * ( M_PI * 2 / 360 ); - sr = sin( angle ); - cr = cos( angle ); - - if ( forward ) { - forward->set( cp * cy, cp * sy, -sp ); - } - - if ( right ) { - right->set( -sr * sp * cy + cr * sy, -sr * sp * sy + -cr * cy, -sr * cp ); - } - - if ( up ) { - up->set( cr * sp * cy + -sr * -sy, cr * sp * sy + -sr * cy, cr * cp ); - } -} - -idVec3_t angles_t::toForward( void ) { - float angle; - static float sp, sy, cp, cy; // static to help MS compiler fp bugs - - angle = yaw * ( M_PI * 2 / 360 ); - sy = sin( angle ); - cy = cos( angle ); - - angle = pitch * ( M_PI * 2 / 360 ); - sp = sin( angle ); - cp = cos( angle ); - - return idVec3_t( cp * cy, cp * sy, -sp ); -} - -/* -================= -Normalize360 - -returns angles normalized to the range [0 <= angle < 360] -================= -*/ -angles_t& angles_t::Normalize360( void ) { - pitch = (360.0 / 65536) * ( ( int )( pitch * ( 65536 / 360.0 ) ) & 65535 ); - yaw = (360.0 / 65536) * ( ( int )( yaw * ( 65536 / 360.0 ) ) & 65535 ); - roll = (360.0 / 65536) * ( ( int )( roll * ( 65536 / 360.0 ) ) & 65535 ); - - return *this; -} - - -/* -================= -Normalize180 - -returns angles normalized to the range [-180 < angle <= 180] -================= -*/ -angles_t& angles_t::Normalize180( void ) { - Normalize360(); - - if ( pitch > 180.0 ) { - pitch -= 360.0; - } - - if ( yaw > 180.0 ) { - yaw -= 360.0; - } - - if ( roll > 180.0 ) { - roll -= 360.0; - } - return *this; -} diff --git a/CODE-mp/Splines/math_angles.h b/CODE-mp/Splines/math_angles.h deleted file mode 100644 index fdf1bfa..0000000 --- a/CODE-mp/Splines/math_angles.h +++ /dev/null @@ -1,174 +0,0 @@ -#ifndef __MATH_ANGLES_H__ -#define __MATH_ANGLES_H__ - -#include -#include - -#include "math_vector.h" - -class mat3_t; -class quat_t; -class idVec3_t; -typedef idVec3_t &vec3_p; - -class angles_t { -public: - float pitch; - float yaw; - float roll; - - angles_t(); - angles_t( float pitch, float yaw, float roll ); - angles_t( const idVec3_t &vec ); - - friend void toAngles( idVec3_t &src, angles_t &dst ); - friend void toAngles( quat_t &src, angles_t &dst ); - friend void toAngles( mat3_t &src, angles_t &dst ); - - operator vec3_p(); - - float operator[]( int index ) const; - float& operator[]( int index ); - - void set( float pitch, float yaw, float roll ); - - void operator=( angles_t const &a ); - void operator=( idVec3_t const &a ); - - friend angles_t operator+( const angles_t &a, const angles_t &b ); - angles_t &operator+=( angles_t const &a ); - angles_t &operator+=( idVec3_t const &a ); - - friend angles_t operator-( angles_t &a, angles_t &b ); - angles_t &operator-=( angles_t &a ); - - friend angles_t operator*( const angles_t &a, float b ); - friend angles_t operator*( float a, const angles_t &b ); - angles_t &operator*=( float a ); - - friend int operator==( angles_t &a, angles_t &b ); - - friend int operator!=( angles_t &a, angles_t &b ); - - void toVectors( idVec3_t *forward, idVec3_t *right = NULL, idVec3_t *up = NULL ); - idVec3_t toForward( void ); - - angles_t &Zero( void ); - - angles_t &Normalize360( void ); - angles_t &Normalize180( void ); -}; - -extern angles_t ang_zero; - -inline angles_t::angles_t() {} - -inline angles_t::angles_t( float pitch, float yaw, float roll ) { - this->pitch = pitch; - this->yaw = yaw; - this->roll = roll; -} - -inline angles_t::angles_t( const idVec3_t &vec ) { - this->pitch = vec.x; - this->yaw = vec.y; - this->roll = vec.z; -} - -inline float angles_t::operator[]( int index ) const { - assert( ( index >= 0 ) && ( index < 3 ) ); - return ( &pitch )[ index ]; -} - -inline float& angles_t::operator[]( int index ) { - assert( ( index >= 0 ) && ( index < 3 ) ); - return ( &pitch )[ index ]; -} - -inline angles_t::operator vec3_p( void ) { - return *( idVec3_t * )&pitch; -} - -inline void angles_t::set( float pitch, float yaw, float roll ) { - this->pitch = pitch; - this->yaw = yaw; - this->roll = roll; -} - -inline void angles_t::operator=( angles_t const &a ) { - pitch = a.pitch; - yaw = a.yaw; - roll = a.roll; -} - -inline void angles_t::operator=( idVec3_t const &a ) { - pitch = a[ 0 ]; - yaw = a[ 1 ]; - roll = a[ 2 ]; -} - -inline angles_t operator+( const angles_t &a, const angles_t &b ) { - return angles_t( a.pitch + b.pitch, a.yaw + b.yaw, a.roll + b.roll ); -} - -inline angles_t& angles_t::operator+=( angles_t const &a ) { - pitch += a.pitch; - yaw += a.yaw; - roll += a.roll; - - return *this; -} - -inline angles_t& angles_t::operator+=( idVec3_t const &a ) { - pitch += a.x; - yaw += a.y; - roll += a.z; - - return *this; -} - -inline angles_t operator-( angles_t &a, angles_t &b ) { - return angles_t( a.pitch - b.pitch, a.yaw - b.yaw, a.roll - b.roll ); -} - -inline angles_t& angles_t::operator-=( angles_t &a ) { - pitch -= a.pitch; - yaw -= a.yaw; - roll -= a.roll; - - return *this; -} - -inline angles_t operator*( const angles_t &a, float b ) { - return angles_t( a.pitch * b, a.yaw * b, a.roll * b ); -} - -inline angles_t operator*( float a, const angles_t &b ) { - return angles_t( a * b.pitch, a * b.yaw, a * b.roll ); -} - -inline angles_t& angles_t::operator*=( float a ) { - pitch *= a; - yaw *= a; - roll *= a; - - return *this; -} - -inline int operator==( angles_t &a, angles_t &b ) { - return ( ( a.pitch == b.pitch ) && ( a.yaw == b.yaw ) && ( a.roll == b.roll ) ); -} - -inline int operator!=( angles_t &a, angles_t &b ) { - return ( ( a.pitch != b.pitch ) || ( a.yaw != b.yaw ) || ( a.roll != b.roll ) ); -} - -inline angles_t& angles_t::Zero( void ) { - pitch = 0.0f; - yaw = 0.0f; - roll = 0.0f; - - return *this; -} - -#endif /* !__MATH_ANGLES_H__ */ diff --git a/CODE-mp/Splines/math_matrix.cpp b/CODE-mp/Splines/math_matrix.cpp deleted file mode 100644 index b02b6ac..0000000 --- a/CODE-mp/Splines/math_matrix.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include "q_shared.h" - -mat3_t mat3_default( idVec3_t( 1, 0, 0 ), idVec3_t( 0, 1, 0 ), idVec3_t( 0, 0, 1 ) ); - -void toMatrix( quat_t const &src, mat3_t &dst ) { - float wx, wy, wz; - float xx, yy, yz; - float xy, xz, zz; - float x2, y2, z2; - - x2 = src.x + src.x; - y2 = src.y + src.y; - z2 = src.z + src.z; - - xx = src.x * x2; - xy = src.x * y2; - xz = src.x * z2; - - yy = src.y * y2; - yz = src.y * z2; - zz = src.z * z2; - - wx = src.w * x2; - wy = src.w * y2; - wz = src.w * z2; - - dst[ 0 ][ 0 ] = 1.0f - ( yy + zz ); - dst[ 0 ][ 1 ] = xy - wz; - dst[ 0 ][ 2 ] = xz + wy; - - dst[ 1 ][ 0 ] = xy + wz; - dst[ 1 ][ 1 ] = 1.0f - ( xx + zz ); - dst[ 1 ][ 2 ] = yz - wx; - - dst[ 2 ][ 0 ] = xz - wy; - dst[ 2 ][ 1 ] = yz + wx; - dst[ 2 ][ 2 ] = 1.0f - ( xx + yy ); -} - -void toMatrix( angles_t const &src, mat3_t &dst ) { - float angle; - static float sr, sp, sy, cr, cp, cy; // static to help MS compiler fp bugs - - angle = src.yaw * ( M_PI * 2.0f / 360.0f ); - sy = sin( angle ); - cy = cos( angle ); - - angle = src.pitch * ( M_PI * 2.0f / 360.0f ); - sp = sin( angle ); - cp = cos( angle ); - - angle = src.roll * ( M_PI * 2.0f / 360.0f ); - sr = sin( angle ); - cr = cos( angle ); - - dst[ 0 ].set( cp * cy, cp * sy, -sp ); - dst[ 1 ].set( sr * sp * cy + cr * -sy, sr * sp * sy + cr * cy, sr * cp ); - dst[ 2 ].set( cr * sp * cy + -sr * -sy, cr * sp * sy + -sr * cy, cr * cp ); -} - -void toMatrix( idVec3_t const &src, mat3_t &dst ) { - angles_t sup = src; - toMatrix(sup, dst); -} - -void mat3_t::ProjectVector( const idVec3_t &src, idVec3_t &dst ) const { - dst.x = src * mat[ 0 ]; - dst.y = src * mat[ 1 ]; - dst.z = src * mat[ 2 ]; -} - -void mat3_t::UnprojectVector( const idVec3_t &src, idVec3_t &dst ) const { - dst = mat[ 0 ] * src.x + mat[ 1 ] * src.y + mat[ 2 ] * src.z; -} - -void mat3_t::Transpose( mat3_t &matrix ) { - int i; - int j; - - for( i = 0; i < 3; i++ ) { - for( j = 0; j < 3; j++ ) { - matrix[ i ][ j ] = mat[ j ][ i ]; - } - } -} - -void mat3_t::Transpose( void ) { - float temp; - int i; - int j; - - for( i = 0; i < 3; i++ ) { - for( j = i + 1; j < 3; j++ ) { - temp = mat[ i ][ j ]; - mat[ i ][ j ] = mat[ j ][ i ]; - mat[ j ][ i ] = temp; - } - } -} - -mat3_t mat3_t::Inverse( void ) const { - mat3_t inv( *this ); - - inv.Transpose(); - - return inv; -} - -void mat3_t::Clear( void ) { - mat[0].set( 1, 0, 0 ); - mat[1].set( 0, 1, 0 ); - mat[2].set( 0, 0, 1 ); -} diff --git a/CODE-mp/Splines/math_matrix.h b/CODE-mp/Splines/math_matrix.h deleted file mode 100644 index 38a077d..0000000 --- a/CODE-mp/Splines/math_matrix.h +++ /dev/null @@ -1,202 +0,0 @@ -#ifndef __MATH_MATRIX_H__ -#define __MATH_MATRIX_H__ - -#include -#include "math_vector.h" - -#ifndef ID_INLINE -#ifdef _WIN32 -#define ID_INLINE __inline -#else -#define ID_INLINE inline -#endif -#endif - -class quat_t; -class angles_t; - -class mat3_t { -public: - idVec3_t mat[ 3 ]; - - mat3_t(); - mat3_t( float src[ 3 ][ 3 ] ); - mat3_t( idVec3_t const &x, idVec3_t const &y, idVec3_t const &z ); - mat3_t( const float xx, const float xy, const float xz, const float yx, const float yy, const float yz, const float zx, const float zy, const float zz ); - - friend void toMatrix( quat_t const &src, mat3_t &dst ); - friend void toMatrix( angles_t const &src, mat3_t &dst ); - friend void toMatrix( idVec3_t const &src, mat3_t &dst ); - - idVec3_t operator[]( int index ) const; - idVec3_t &operator[]( int index ); - - idVec3_t operator*( const idVec3_t &vec ) const; - mat3_t operator*( const mat3_t &a ) const; - mat3_t operator*( float a ) const; - mat3_t operator+( mat3_t const &a ) const; - mat3_t operator-( mat3_t const &a ) const; - - friend idVec3_t operator*( const idVec3_t &vec, const mat3_t &mat ); - friend mat3_t operator*( float a, mat3_t const &b ); - - mat3_t &operator*=( float a ); - mat3_t &operator+=( mat3_t const &a ); - mat3_t &operator-=( mat3_t const &a ); - - void Clear( void ); - - void ProjectVector( const idVec3_t &src, idVec3_t &dst ) const; - void UnprojectVector( const idVec3_t &src, idVec3_t &dst ) const; - - void OrthoNormalize( void ); - void Transpose( mat3_t &matrix ); - void Transpose( void ); - mat3_t Inverse( void ) const; - void Identity( void ); - - friend void InverseMultiply( const mat3_t &inv, const mat3_t &b, mat3_t &dst ); - friend mat3_t SkewSymmetric( idVec3_t const &src ); -}; - -ID_INLINE mat3_t::mat3_t() { -} - -ID_INLINE mat3_t::mat3_t( float src[ 3 ][ 3 ] ) { - memcpy( mat, src, sizeof( src ) ); -} - -ID_INLINE mat3_t::mat3_t( idVec3_t const &x, idVec3_t const &y, idVec3_t const &z ) { - mat[ 0 ].x = x.x; mat[ 0 ].y = x.y; mat[ 0 ].z = x.z; - mat[ 1 ].x = y.x; mat[ 1 ].y = y.y; mat[ 1 ].z = y.z; - mat[ 2 ].x = z.x; mat[ 2 ].y = z.y; mat[ 2 ].z = z.z; -} - -ID_INLINE mat3_t::mat3_t( const float xx, const float xy, const float xz, const float yx, const float yy, const float yz, const float zx, const float zy, const float zz ) { - mat[ 0 ].x = xx; mat[ 0 ].y = xy; mat[ 0 ].z = xz; - mat[ 1 ].x = yx; mat[ 1 ].y = yy; mat[ 1 ].z = yz; - mat[ 2 ].x = zx; mat[ 2 ].y = zy; mat[ 2 ].z = zz; -} - -ID_INLINE idVec3_t mat3_t::operator[]( int index ) const { - assert( ( index >= 0 ) && ( index < 3 ) ); - return mat[ index ]; -} - -ID_INLINE idVec3_t& mat3_t::operator[]( int index ) { - assert( ( index >= 0 ) && ( index < 3 ) ); - return mat[ index ]; -} - -ID_INLINE idVec3_t mat3_t::operator*( const idVec3_t &vec ) const { - return idVec3_t( - mat[ 0 ].x * vec.x + mat[ 1 ].x * vec.y + mat[ 2 ].x * vec.z, - mat[ 0 ].y * vec.x + mat[ 1 ].y * vec.y + mat[ 2 ].y * vec.z, - mat[ 0 ].z * vec.x + mat[ 1 ].z * vec.y + mat[ 2 ].z * vec.z ); -} - -ID_INLINE mat3_t mat3_t::operator*( const mat3_t &a ) const { - return mat3_t( - mat[0].x * a[0].x + mat[0].y * a[1].x + mat[0].z * a[2].x, - mat[0].x * a[0].y + mat[0].y * a[1].y + mat[0].z * a[2].y, - mat[0].x * a[0].z + mat[0].y * a[1].z + mat[0].z * a[2].z, - mat[1].x * a[0].x + mat[1].y * a[1].x + mat[1].z * a[2].x, - mat[1].x * a[0].y + mat[1].y * a[1].y + mat[1].z * a[2].y, - mat[1].x * a[0].z + mat[1].y * a[1].z + mat[1].z * a[2].z, - mat[2].x * a[0].x + mat[2].y * a[1].x + mat[2].z * a[2].x, - mat[2].x * a[0].y + mat[2].y * a[1].y + mat[2].z * a[2].y, - mat[2].x * a[0].z + mat[2].y * a[1].z + mat[2].z * a[2].z ); -} - -ID_INLINE mat3_t mat3_t::operator*( float a ) const { - return mat3_t( - mat[0].x * a, mat[0].y * a, mat[0].z * a, - mat[1].x * a, mat[1].y * a, mat[1].z * a, - mat[2].x * a, mat[2].y * a, mat[2].z * a ); -} - -ID_INLINE mat3_t mat3_t::operator+( mat3_t const &a ) const { - return mat3_t( - mat[0].x + a[0].x, mat[0].y + a[0].y, mat[0].z + a[0].z, - mat[1].x + a[1].x, mat[1].y + a[1].y, mat[1].z + a[1].z, - mat[2].x + a[2].x, mat[2].y + a[2].y, mat[2].z + a[2].z ); -} - -ID_INLINE mat3_t mat3_t::operator-( mat3_t const &a ) const { - return mat3_t( - mat[0].x - a[0].x, mat[0].y - a[0].y, mat[0].z - a[0].z, - mat[1].x - a[1].x, mat[1].y - a[1].y, mat[1].z - a[1].z, - mat[2].x - a[2].x, mat[2].y - a[2].y, mat[2].z - a[2].z ); -} - -ID_INLINE idVec3_t operator*( const idVec3_t &vec, const mat3_t &mat ) { - return idVec3_t( - mat[ 0 ].x * vec.x + mat[ 1 ].x * vec.y + mat[ 2 ].x * vec.z, - mat[ 0 ].y * vec.x + mat[ 1 ].y * vec.y + mat[ 2 ].y * vec.z, - mat[ 0 ].z * vec.x + mat[ 1 ].z * vec.y + mat[ 2 ].z * vec.z ); -} - -ID_INLINE mat3_t operator*( float a, mat3_t const &b ) { - return mat3_t( - b[0].x * a, b[0].y * a, b[0].z * a, - b[1].x * a, b[1].y * a, b[1].z * a, - b[2].x * a, b[2].y * a, b[2].z * a ); -} - -ID_INLINE mat3_t &mat3_t::operator*=( float a ) { - mat[0].x *= a; mat[0].y *= a; mat[0].z *= a; - mat[1].x *= a; mat[1].y *= a; mat[1].z *= a; - mat[2].x *= a; mat[2].y *= a; mat[2].z *= a; - - return *this; -} - -ID_INLINE mat3_t &mat3_t::operator+=( mat3_t const &a ) { - mat[0].x += a[0].x; mat[0].y += a[0].y; mat[0].z += a[0].z; - mat[1].x += a[1].x; mat[1].y += a[1].y; mat[1].z += a[1].z; - mat[2].x += a[2].x; mat[2].y += a[2].y; mat[2].z += a[2].z; - - return *this; -} - -ID_INLINE mat3_t &mat3_t::operator-=( mat3_t const &a ) { - mat[0].x -= a[0].x; mat[0].y -= a[0].y; mat[0].z -= a[0].z; - mat[1].x -= a[1].x; mat[1].y -= a[1].y; mat[1].z -= a[1].z; - mat[2].x -= a[2].x; mat[2].y -= a[2].y; mat[2].z -= a[2].z; - - return *this; -} - -ID_INLINE void mat3_t::OrthoNormalize( void ) { - mat[ 0 ].Normalize(); - mat[ 2 ].Cross( mat[ 0 ], mat[ 1 ] ); - mat[ 2 ].Normalize(); - mat[ 1 ].Cross( mat[ 2 ], mat[ 0 ] ); - mat[ 1 ].Normalize(); -} - -ID_INLINE void mat3_t::Identity( void ) { - mat[ 0 ].x = 1.f; mat[ 0 ].y = 0.f; mat[ 0 ].z = 0.f; - mat[ 1 ].x = 0.f; mat[ 1 ].y = 1.f; mat[ 1 ].z = 0.f; - mat[ 2 ].x = 0.f; mat[ 2 ].y = 0.f; mat[ 2 ].z = 1.f; -} - -ID_INLINE void InverseMultiply( const mat3_t &inv, const mat3_t &b, mat3_t &dst ) { - dst[0].x = inv[0].x * b[0].x + inv[1].x * b[1].x + inv[2].x * b[2].x; - dst[0].y = inv[0].x * b[0].y + inv[1].x * b[1].y + inv[2].x * b[2].y; - dst[0].z = inv[0].x * b[0].z + inv[1].x * b[1].z + inv[2].x * b[2].z; - dst[1].x = inv[0].y * b[0].x + inv[1].y * b[1].x + inv[2].y * b[2].x; - dst[1].y = inv[0].y * b[0].y + inv[1].y * b[1].y + inv[2].y * b[2].y; - dst[1].z = inv[0].y * b[0].z + inv[1].y * b[1].z + inv[2].y * b[2].z; - dst[2].x = inv[0].z * b[0].x + inv[1].z * b[1].x + inv[2].z * b[2].x; - dst[2].y = inv[0].z * b[0].y + inv[1].z * b[1].y + inv[2].z * b[2].y; - dst[2].z = inv[0].z * b[0].z + inv[1].z * b[1].z + inv[2].z * b[2].z; -} - -ID_INLINE mat3_t SkewSymmetric( idVec3_t const &src ) { - return mat3_t( 0.0f, -src.z, src.y, src.z, 0.0f, -src.x, -src.y, src.x, 0.0f ); -} - -extern mat3_t mat3_default; - -#endif /* !__MATH_MATRIX_H__ */ diff --git a/CODE-mp/Splines/math_quaternion.cpp b/CODE-mp/Splines/math_quaternion.cpp deleted file mode 100644 index 6e27eca..0000000 --- a/CODE-mp/Splines/math_quaternion.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "math_quaternion.h" -#include "math_matrix.h" - -void toQuat( idVec3_t &src, quat_t &dst ) { - dst.x = src.x; - dst.y = src.y; - dst.z = src.z; - dst.w = 0.0f; -} - -void toQuat( angles_t &src, quat_t &dst ) { - mat3_t temp; - - toMatrix( src, temp ); - toQuat( temp, dst ); -} - -void toQuat( mat3_t &src, quat_t &dst ) { - float trace; - float s; - int i; - int j; - int k; - - static int next[ 3 ] = { 1, 2, 0 }; - - trace = src[ 0 ][ 0 ] + src[ 1 ][ 1 ] + src[ 2 ][ 2 ]; - if ( trace > 0.0f ) { - s = ( float )sqrt( trace + 1.0f ); - dst.w = s * 0.5f; - s = 0.5f / s; - - dst.x = ( src[ 2 ][ 1 ] - src[ 1 ][ 2 ] ) * s; - dst.y = ( src[ 0 ][ 2 ] - src[ 2 ][ 0 ] ) * s; - dst.z = ( src[ 1 ][ 0 ] - src[ 0 ][ 1 ] ) * s; - } else { - i = 0; - if ( src[ 1 ][ 1 ] > src[ 0 ][ 0 ] ) { - i = 1; - } - if ( src[ 2 ][ 2 ] > src[ i ][ i ] ) { - i = 2; - } - - j = next[ i ]; - k = next[ j ]; - - s = ( float )sqrt( ( src[ i ][ i ] - ( src[ j ][ j ] + src[ k ][ k ] ) ) + 1.0f ); - dst[ i ] = s * 0.5f; - - s = 0.5f / s; - - dst.w = ( src[ k ][ j ] - src[ j ][ k ] ) * s; - dst[ j ] = ( src[ j ][ i ] + src[ i ][ j ] ) * s; - dst[ k ] = ( src[ k ][ i ] + src[ i ][ k ] ) * s; - } -} diff --git a/CODE-mp/Splines/math_quaternion.h b/CODE-mp/Splines/math_quaternion.h deleted file mode 100644 index 4792ba7..0000000 --- a/CODE-mp/Splines/math_quaternion.h +++ /dev/null @@ -1,169 +0,0 @@ -#ifndef __MATH_QUATERNION_H__ -#define __MATH_QUATERNION_H__ - -#include -#include - -class idVec3_t; -class angles_t; -class mat3_t; - -class quat_t { -public: - float x; - float y; - float z; - float w; - - quat_t(); - quat_t( float x, float y, float z, float w ); - - friend void toQuat( idVec3_t &src, quat_t &dst ); - friend void toQuat( angles_t &src, quat_t &dst ); - friend void toQuat( mat3_t &src, quat_t &dst ); - - float *vec4( void ); - - float operator[]( int index ) const; - float &operator[]( int index ); - - void set( float x, float y, float z, float w ); - - void operator=( quat_t a ); - - friend quat_t operator+( quat_t a, quat_t b ); - quat_t &operator+=( quat_t a ); - - friend quat_t operator-( quat_t a, quat_t b ); - quat_t &operator-=( quat_t a ); - - friend quat_t operator*( quat_t a, float b ); - friend quat_t operator*( float a, quat_t b ); - quat_t &operator*=( float a ); - - friend int operator==( quat_t a, quat_t b ); - friend int operator!=( quat_t a, quat_t b ); - - float Length( void ); - quat_t &Normalize( void ); - - quat_t operator-(); -}; - -inline quat_t::quat_t() { -} - -inline quat_t::quat_t( float x, float y, float z, float w ) { - this->x = x; - this->y = y; - this->z = z; - this->w = w; -} - -inline float *quat_t::vec4( void ) { - return &x; -} - -inline float quat_t::operator[]( int index ) const { - assert( ( index >= 0 ) && ( index < 4 ) ); - return ( &x )[ index ]; -} - -inline float& quat_t::operator[]( int index ) { - assert( ( index >= 0 ) && ( index < 4 ) ); - return ( &x )[ index ]; -} - -inline void quat_t::set( float x, float y, float z, float w ) { - this->x = x; - this->y = y; - this->z = z; - this->w = w; -} - -inline void quat_t::operator=( quat_t a ) { - x = a.x; - y = a.y; - z = a.z; - w = a.w; -} - -inline quat_t operator+( quat_t a, quat_t b ) { - return quat_t( a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w ); -} - -inline quat_t& quat_t::operator+=( quat_t a ) { - x += a.x; - y += a.y; - z += a.z; - w += a.w; - - return *this; -} - -inline quat_t operator-( quat_t a, quat_t b ) { - return quat_t( a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w ); -} - -inline quat_t& quat_t::operator-=( quat_t a ) { - x -= a.x; - y -= a.y; - z -= a.z; - w -= a.w; - - return *this; -} - -inline quat_t operator*( quat_t a, float b ) { - return quat_t( a.x * b, a.y * b, a.z * b, a.w * b ); -} - -inline quat_t operator*( float a, quat_t b ) { - return b * a; -} - -inline quat_t& quat_t::operator*=( float a ) { - x *= a; - y *= a; - z *= a; - w *= a; - - return *this; -} - -inline int operator==( quat_t a, quat_t b ) { - return ( ( a.x == b.x ) && ( a.y == b.y ) && ( a.z == b.z ) && ( a.w == b.w ) ); -} - -inline int operator!=( quat_t a, quat_t b ) { - return ( ( a.x != b.x ) || ( a.y != b.y ) || ( a.z != b.z ) && ( a.w != b.w ) ); -} - -inline float quat_t::Length( void ) { - float length; - - length = x * x + y * y + z * z + w * w; - return ( float )sqrt( length ); -} - -inline quat_t& quat_t::Normalize( void ) { - float length; - float ilength; - - length = this->Length(); - if ( length ) { - ilength = 1 / length; - x *= ilength; - y *= ilength; - z *= ilength; - w *= ilength; - } - - return *this; -} - -inline quat_t quat_t::operator-() { - return quat_t( -x, -y, -z, -w ); -} - -#endif /* !__MATH_QUATERNION_H__ */ diff --git a/CODE-mp/Splines/math_vector.cpp b/CODE-mp/Splines/math_vector.cpp deleted file mode 100644 index 8c3b1bb..0000000 --- a/CODE-mp/Splines/math_vector.cpp +++ /dev/null @@ -1,123 +0,0 @@ -//#include "../game/q_shared.h" -#include "math_vector.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h - -#define LERP_DELTA 1e-6 - -idVec3_t vec_zero( 0.0f, 0.0f, 0.0f ); - -Bounds boundsZero; - -float idVec3_t::toYaw( void ) { - float yaw; - - if ( ( y == 0 ) && ( x == 0 ) ) { - yaw = 0; - } else { - yaw = atan2( y, x ) * 180 / M_PI; - if ( yaw < 0 ) { - yaw += 360; - } - } - - return yaw; -} - -float idVec3_t::toPitch( void ) { - float forward; - float pitch; - - if ( ( x == 0 ) && ( y == 0 ) ) { - if ( z > 0 ) { - pitch = 90; - } else { - pitch = 270; - } - } else { - forward = ( float )idSqrt( x * x + y * y ); - pitch = atan2( z, forward ) * 180 / M_PI; - if ( pitch < 0 ) { - pitch += 360; - } - } - - return pitch; -} - -/* -angles_t idVec3_t::toAngles( void ) { - float forward; - float yaw; - float pitch; - - if ( ( x == 0 ) && ( y == 0 ) ) { - yaw = 0; - if ( z > 0 ) { - pitch = 90; - } else { - pitch = 270; - } - } else { - yaw = atan2( y, x ) * 180 / M_PI; - if ( yaw < 0 ) { - yaw += 360; - } - - forward = ( float )idSqrt( x * x + y * y ); - pitch = atan2( z, forward ) * 180 / M_PI; - if ( pitch < 0 ) { - pitch += 360; - } - } - - return angles_t( -pitch, yaw, 0 ); -} -*/ - -idVec3_t LerpVector( idVec3_t &w1, idVec3_t &w2, const float t ) { - float omega, cosom, sinom, scale0, scale1; - - cosom = w1 * w2; - if ( ( 1.0 - cosom ) > LERP_DELTA ) { - omega = acos( cosom ); - sinom = sin( omega ); - scale0 = sin( ( 1.0 - t ) * omega ) / sinom; - scale1 = sin( t * omega ) / sinom; - } else { - scale0 = 1.0 - t; - scale1 = t; - } - - return ( w1 * scale0 + w2 * scale1 ); -} - -/* -============= -idVec3_t::string - -This is just a convenience function -for printing vectors -============= -*/ -char *idVec3_t::string( void ) { - static int index = 0; - static char str[ 8 ][ 36 ]; - char *s; - - // use an array so that multiple toString's won't collide - s = str[ index ]; - index = (index + 1)&7; - - sprintf( s, "%.2f %.2f %.2f", x, y, z ); - - return s; -} diff --git a/CODE-mp/Splines/math_vector.h b/CODE-mp/Splines/math_vector.h deleted file mode 100644 index 0350de1..0000000 --- a/CODE-mp/Splines/math_vector.h +++ /dev/null @@ -1,553 +0,0 @@ -#ifndef __MATH_VECTOR_H__ -#define __MATH_VECTOR_H__ - -#if defined(_WIN32) -#pragma warning(disable : 4244) -#endif - -#include -#include - -//#define DotProduct(a,b) ((a)[0]*(b)[0]+(a)[1]*(b)[1]+(a)[2]*(b)[2]) -//#define VectorSubtract(a,b,c) ((c)[0]=(a)[0]-(b)[0],(c)[1]=(a)[1]-(b)[1],(c)[2]=(a)[2]-(b)[2]) -//#define VectorAdd(a,b,c) ((c)[0]=(a)[0]+(b)[0],(c)[1]=(a)[1]+(b)[1],(c)[2]=(a)[2]+(b)[2]) -//#define VectorCopy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2]) -//#define VectorCopy(a,b) ((b).x=(a).x,(b).y=(a).y,(b).z=(a).z]) - -//#define VectorScale(v, s, o) ((o)[0]=(v)[0]*(s),(o)[1]=(v)[1]*(s),(o)[2]=(v)[2]*(s)) -#define __VectorMA(v, s, b, o) ((o)[0]=(v)[0]+(b)[0]*(s),(o)[1]=(v)[1]+(b)[1]*(s),(o)[2]=(v)[2]+(b)[2]*(s)) -//#define CrossProduct(a,b,c) ((c)[0]=(a)[1]*(b)[2]-(a)[2]*(b)[1],(c)[1]=(a)[2]*(b)[0]-(a)[0]*(b)[2],(c)[2]=(a)[0]*(b)[1]-(a)[1]*(b)[0]) - -#define DotProduct4(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2]+(x)[3]*(y)[3]) -#define VectorSubtract4(a,b,c) ((c)[0]=(a)[0]-(b)[0],(c)[1]=(a)[1]-(b)[1],(c)[2]=(a)[2]-(b)[2],(c)[3]=(a)[3]-(b)[3]) -#define VectorAdd4(a,b,c) ((c)[0]=(a)[0]+(b)[0],(c)[1]=(a)[1]+(b)[1],(c)[2]=(a)[2]+(b)[2],(c)[3]=(a)[3]+(b)[3]) -#define VectorCopy4(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3]) -#define VectorScale4(v, s, o) ((o)[0]=(v)[0]*(s),(o)[1]=(v)[1]*(s),(o)[2]=(v)[2]*(s),(o)[3]=(v)[3]*(s)) -#define VectorMA4(v, s, b, o) ((o)[0]=(v)[0]+(b)[0]*(s),(o)[1]=(v)[1]+(b)[1]*(s),(o)[2]=(v)[2]+(b)[2]*(s),(o)[3]=(v)[3]+(b)[3]*(s)) - - -//#define VectorClear(a) ((a)[0]=(a)[1]=(a)[2]=0) -#define VectorNegate(a,b) ((b)[0]=-(a)[0],(b)[1]=-(a)[1],(b)[2]=-(a)[2]) -//#define VectorSet(v, x, y, z) ((v)[0]=(x), (v)[1]=(y), (v)[2]=(z)) -#define Vector4Copy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3]) - -#define SnapVector(v) {v[0]=(int)v[0];v[1]=(int)v[1];v[2]=(int)v[2];} - - -//#include "util_heap.h" - -#ifndef EQUAL_EPSILON -#define EQUAL_EPSILON 0.001 -#endif - -float Q_fabs( float f ); - -#ifndef ID_INLINE -#ifdef _WIN32 -#define ID_INLINE __inline -#else -#define ID_INLINE inline -#endif -#endif - -// if this is defined, vec3 will take four elements, which may allow -// easier SIMD optimizations -//#define FAT_VEC3 -//#ifdef __ppc__ -//#pragma align(16) -//#endif - -class angles_t; -#ifdef __ppc__ -// Vanilla PPC code, but since PPC has a reciprocal square root estimate instruction, -// runs *much* faster than calling sqrt(). We'll use two Newton-Raphson -// refinement steps to get bunch more precision in the 1/sqrt() value for very little cost. -// We'll then multiply 1/sqrt times the original value to get the sqrt. -// This is about 12.4 times faster than sqrt() and according to my testing (not exhaustive) -// it returns fairly accurate results (error below 1.0e-5 up to 100000.0 in 0.1 increments). - -static inline float idSqrt(float x) { - const float half = 0.5; - const float one = 1.0; - float B, y0, y1; - - // This'll NaN if it hits frsqrte. Handle both +0.0 and -0.0 - if (fabs(x) == 0.0) - return x; - B = x; - -#ifdef __GNUC__ - asm("frsqrte %0,%1" : "=f" (y0) : "f" (B)); -#else - y0 = __frsqrte(B); -#endif - /* First refinement step */ - - y1 = y0 + half*y0*(one - B*y0*y0); - - /* Second refinement step -- copy the output of the last step to the input of this step */ - - y0 = y1; - y1 = y0 + half*y0*(one - B*y0*y0); - - /* Get sqrt(x) from x * 1/sqrt(x) */ - return x * y1; -} -#else -static inline double idSqrt(double x) { - return sqrt(x); -} -#endif - - -//class idVec3_t : public idHeap { -class idVec3_t { -public: -#ifndef FAT_VEC3 - float x,y,z; -#else - float x,y,z,dist; -#endif - -#ifndef FAT_VEC3 - idVec3_t() {}; -#else - idVec3_t() {dist = 0.0f;}; -#endif - idVec3_t( const float x, const float y, const float z ); - - operator float *(); - - float operator[]( const int index ) const; - float &operator[]( const int index ); - - void set( const float x, const float y, const float z ); - - idVec3_t operator-() const; - - idVec3_t &operator=( const idVec3_t &a ); - - float operator*( const idVec3_t &a ) const; - idVec3_t operator*( const float a ) const; - friend idVec3_t operator*( float a, idVec3_t b ); - - idVec3_t operator+( const idVec3_t &a ) const; - idVec3_t operator-( const idVec3_t &a ) const; - - idVec3_t &operator+=( const idVec3_t &a ); - idVec3_t &operator-=( const idVec3_t &a ); - idVec3_t &operator*=( const float a ); - - int operator==( const idVec3_t &a ) const; - int operator!=( const idVec3_t &a ) const; - - idVec3_t Cross( const idVec3_t &a ) const; - idVec3_t &Cross( const idVec3_t &a, const idVec3_t &b ); - - float Length( void ) const; - float Normalize( void ); - - void Zero( void ); - void Snap( void ); - void SnapTowards( const idVec3_t &to ); - - float toYaw( void ); - float toPitch( void ); - angles_t toAngles( void ); - friend idVec3_t LerpVector( const idVec3_t &w1, const idVec3_t &w2, const float t ); - - char *string( void ); -}; - -extern idVec3_t vec_zero; - -ID_INLINE idVec3_t::idVec3_t( const float x, const float y, const float z ) { - this->x = x; - this->y = y; - this->z = z; -#ifdef FAT_VEC3 - this->dist = 0.0f; -#endif -} - -ID_INLINE float idVec3_t::operator[]( const int index ) const { - return ( &x )[ index ]; -} - -ID_INLINE float &idVec3_t::operator[]( const int index ) { - return ( &x )[ index ]; -} - -ID_INLINE idVec3_t::operator float *( void ) { - return &x; -} - -ID_INLINE idVec3_t idVec3_t::operator-() const { - return idVec3_t( -x, -y, -z ); -} - -ID_INLINE idVec3_t &idVec3_t::operator=( const idVec3_t &a ) { - x = a.x; - y = a.y; - z = a.z; - - return *this; -} - -ID_INLINE void idVec3_t::set( const float x, const float y, const float z ) { - this->x = x; - this->y = y; - this->z = z; -} - -ID_INLINE idVec3_t idVec3_t::operator-( const idVec3_t &a ) const { - return idVec3_t( x - a.x, y - a.y, z - a.z ); -} - -ID_INLINE float idVec3_t::operator*( const idVec3_t &a ) const { - return x * a.x + y * a.y + z * a.z; -} - -ID_INLINE idVec3_t idVec3_t::operator*( const float a ) const { - return idVec3_t( x * a, y * a, z * a ); -} - -ID_INLINE idVec3_t operator*( const float a, const idVec3_t b ) { - return idVec3_t( b.x * a, b.y * a, b.z * a ); -} - -ID_INLINE idVec3_t idVec3_t::operator+( const idVec3_t &a ) const { - return idVec3_t( x + a.x, y + a.y, z + a.z ); -} - -ID_INLINE idVec3_t &idVec3_t::operator+=( const idVec3_t &a ) { - x += a.x; - y += a.y; - z += a.z; - - return *this; -} - -ID_INLINE idVec3_t &idVec3_t::operator-=( const idVec3_t &a ) { - x -= a.x; - y -= a.y; - z -= a.z; - - return *this; -} - -ID_INLINE idVec3_t &idVec3_t::operator*=( const float a ) { - x *= a; - y *= a; - z *= a; - - return *this; -} - -ID_INLINE int idVec3_t::operator==( const idVec3_t &a ) const { - if ( Q_fabs( x - a.x ) > EQUAL_EPSILON ) { - return false; - } - - if ( Q_fabs( y - a.y ) > EQUAL_EPSILON ) { - return false; - } - - if ( Q_fabs( z - a.z ) > EQUAL_EPSILON ) { - return false; - } - - return true; -} - -ID_INLINE int idVec3_t::operator!=( const idVec3_t &a ) const { - if ( Q_fabs( x - a.x ) > EQUAL_EPSILON ) { - return true; - } - - if ( Q_fabs( y - a.y ) > EQUAL_EPSILON ) { - return true; - } - - if ( Q_fabs( z - a.z ) > EQUAL_EPSILON ) { - return true; - } - - return false; -} - -ID_INLINE idVec3_t idVec3_t::Cross( const idVec3_t &a ) const { - return idVec3_t( y * a.z - z * a.y, z * a.x - x * a.z, x * a.y - y * a.x ); -} - -ID_INLINE idVec3_t &idVec3_t::Cross( const idVec3_t &a, const idVec3_t &b ) { - x = a.y * b.z - a.z * b.y; - y = a.z * b.x - a.x * b.z; - z = a.x * b.y - a.y * b.x; - - return *this; -} - -ID_INLINE float idVec3_t::Length( void ) const { - float length; - - length = x * x + y * y + z * z; - return ( float )idSqrt( length ); -} - -ID_INLINE float idVec3_t::Normalize( void ) { - float length; - float ilength; - - length = this->Length(); - if ( length ) { - ilength = 1.0f / length; - x *= ilength; - y *= ilength; - z *= ilength; - } - - return length; -} - -ID_INLINE void idVec3_t::Zero( void ) { - x = 0.0f; - y = 0.0f; - z = 0.0f; -} - -ID_INLINE void idVec3_t::Snap( void ) { - x = float( int( x ) ); - y = float( int( y ) ); - z = float( int( z ) ); -} - -/* -====================== -SnapTowards - -Round a vector to integers for more efficient network -transmission, but make sure that it rounds towards a given point -rather than blindly truncating. This prevents it from truncating -into a wall. -====================== -*/ -ID_INLINE void idVec3_t::SnapTowards( const idVec3_t &to ) { - if ( to.x <= x ) { - x = float( int( x ) ); - } else { - x = float( int( x ) + 1 ); - } - - if ( to.y <= y ) { - y = float( int( y ) ); - } else { - y = float( int( y ) + 1 ); - } - - if ( to.z <= z ) { - z = float( int( z ) ); - } else { - z = float( int( z ) + 1 ); - } -} - -//=============================================================== - -class Bounds { -public: - idVec3_t b[2]; - - Bounds(); - Bounds( const idVec3_t &mins, const idVec3_t &maxs ); - - void Clear(); - void Zero(); - float Radius(); // radius from origin, not from center - idVec3_t Center(); - void AddPoint( const idVec3_t &v ); - void AddBounds( const Bounds &bb ); - bool IsCleared(); - bool ContainsPoint( const idVec3_t &p ); - bool IntersectsBounds( const Bounds &b2 ); // touching is NOT intersecting -}; - -extern Bounds boundsZero; - -ID_INLINE Bounds::Bounds(){ -} - -ID_INLINE bool Bounds::IsCleared() { - return b[0][0] > b[1][0]; -} - -ID_INLINE bool Bounds::ContainsPoint( const idVec3_t &p ) { - if ( p[0] < b[0][0] || p[1] < b[0][1] || p[2] < b[0][2] - || p[0] > b[1][0] || p[1] > b[1][1] || p[2] > b[1][2] ) { - return false; - } - return true; -} - -ID_INLINE bool Bounds::IntersectsBounds( const Bounds &b2 ) { - if ( b2.b[1][0] < b[0][0] || b2.b[1][1] < b[0][1] || b2.b[1][2] < b[0][2] - || b2.b[0][0] > b[1][0] || b2.b[0][1] > b[1][1] || b2.b[0][2] > b[1][2] ) { - return false; - } - return true; -} - -ID_INLINE Bounds::Bounds( const idVec3_t &mins, const idVec3_t &maxs ) { - b[0] = mins; - b[1] = maxs; -} - -ID_INLINE idVec3_t Bounds::Center() { - return idVec3_t( ( b[1][0] + b[0][0] ) * 0.5f, ( b[1][1] + b[0][1] ) * 0.5f, ( b[1][2] + b[0][2] ) * 0.5f ); -} - -ID_INLINE void Bounds::Clear() { - b[0][0] = b[0][1] = b[0][2] = 99999; - b[1][0] = b[1][1] = b[1][2] = -99999; -} - -ID_INLINE void Bounds::Zero() { - b[0][0] = b[0][1] = b[0][2] = - b[1][0] = b[1][1] = b[1][2] = 0; -} - -ID_INLINE void Bounds::AddPoint( const idVec3_t &v ) { - if ( v[0] < b[0][0]) { - b[0][0] = v[0]; - } - if ( v[0] > b[1][0]) { - b[1][0] = v[0]; - } - if ( v[1] < b[0][1] ) { - b[0][1] = v[1]; - } - if ( v[1] > b[1][1]) { - b[1][1] = v[1]; - } - if ( v[2] < b[0][2] ) { - b[0][2] = v[2]; - } - if ( v[2] > b[1][2]) { - b[1][2] = v[2]; - } -} - - -ID_INLINE void Bounds::AddBounds( const Bounds &bb ) { - if ( bb.b[0][0] < b[0][0]) { - b[0][0] = bb.b[0][0]; - } - if ( bb.b[0][1] < b[0][1]) { - b[0][1] = bb.b[0][1]; - } - if ( bb.b[0][2] < b[0][2]) { - b[0][2] = bb.b[0][2]; - } - - if ( bb.b[1][0] > b[1][0]) { - b[1][0] = bb.b[1][0]; - } - if ( bb.b[1][1] > b[1][1]) { - b[1][1] = bb.b[1][1]; - } - if ( bb.b[1][2] > b[1][2]) { - b[1][2] = bb.b[1][2]; - } -} - -ID_INLINE float Bounds::Radius( ) { - int i; - float total; - float a, aa; - - total = 0; - for (i=0 ; i<3 ; i++) { - a = (float)fabs( b[0][i] ); - aa = (float)fabs( b[1][i] ); - if ( aa > a ) { - a = aa; - } - total += a * a; - } - - return (float)idSqrt( total ); -} - -//=============================================================== - - -class idVec2_t { -public: - float x; - float y; - - operator float *(); - float operator[]( int index ) const; - float &operator[]( int index ); -}; - -ID_INLINE float idVec2_t::operator[]( int index ) const { - return ( &x )[ index ]; -} - -ID_INLINE float& idVec2_t::operator[]( int index ) { - return ( &x )[ index ]; -} - -ID_INLINE idVec2_t::operator float *( void ) { - return &x; -} - -class vec4_t : public idVec3_t { -public: -#ifndef FAT_VEC3 - float dist; -#endif - vec4_t(); - ~vec4_t() {}; - - vec4_t( float x, float y, float z, float dist ); - float operator[]( int index ) const; - float &operator[]( int index ); -}; - -ID_INLINE vec4_t::vec4_t() {} -ID_INLINE vec4_t::vec4_t( float x, float y, float z, float dist ) { - this->x = x; - this->y = y; - this->z = z; - this->dist = dist; -} - -ID_INLINE float vec4_t::operator[]( int index ) const { - return ( &x )[ index ]; -} - -ID_INLINE float& vec4_t::operator[]( int index ) { - return ( &x )[ index ]; -} - - -class idVec5_t : public idVec3_t { -public: - float s; - float t; - float operator[]( int index ) const; - float &operator[]( int index ); -}; - - -ID_INLINE float idVec5_t::operator[]( int index ) const { - return ( &x )[ index ]; -} - -ID_INLINE float& idVec5_t::operator[]( int index ) { - return ( &x )[ index ]; -} - -#endif /* !__MATH_VECTOR_H__ */ diff --git a/CODE-mp/Splines/mssccprj.scc b/CODE-mp/Splines/mssccprj.scc deleted file mode 100644 index a2ee319..0000000 --- a/CODE-mp/Splines/mssccprj.scc +++ /dev/null @@ -1,5 +0,0 @@ -SCC = This is a Source Code Control file - -[Splines.dsp] -SCC_Aux_Path = "\\ravend\vss_projects\jk2sof2MP" -SCC_Project_Name = "$/General/code/Splines", GAAAAAAA diff --git a/CODE-mp/Splines/q_parse.cpp b/CODE-mp/Splines/q_parse.cpp deleted file mode 100644 index 6413d82..0000000 --- a/CODE-mp/Splines/q_parse.cpp +++ /dev/null @@ -1,514 +0,0 @@ -// q_parse.c -- support for parsing text files - -#include "q_shared.h" - -/* -============================================================================ - -PARSING - -============================================================================ -*/ - -// multiple character punctuation tokens -static const char *punctuation[] = { - "+=", "-=", "*=", "/=", "&=", "|=", "++", "--", - "&&", "||", "<=", ">=", "==", "!=", - NULL -}; - -typedef struct { - char token[MAX_TOKEN_CHARS]; - int lines; - qboolean ungetToken; - char parseFile[MAX_QPATH]; -} parseInfo_t; - -#define MAX_PARSE_INFO 16 -static parseInfo_t parseInfo[MAX_PARSE_INFO]; -static int parseInfoNum; -static parseInfo_t *pi = &parseInfo[0]; - -/* -=================== -Com_BeginParseSession -=================== -*/ -void Com_BeginParseSession( const char *filename ) { - if ( parseInfoNum == MAX_PARSE_INFO - 1 ) { - Com_Error( ERR_FATAL, "Com_BeginParseSession: session overflow" ); - } - parseInfoNum++; - pi = &parseInfo[parseInfoNum]; - - pi->lines = 1; - Q_strncpyz( pi->parseFile, filename, sizeof( pi->parseFile ) ); -} - -/* -=================== -Com_EndParseSession -=================== -*/ -void Com_EndParseSession( void ) { - if ( parseInfoNum == 0 ) { - Com_Error( ERR_FATAL, "Com_EndParseSession: session underflow" ); - } - parseInfoNum--; - pi = &parseInfo[parseInfoNum]; -} - -/* -=================== -Com_GetCurrentParseLine -=================== -*/ -int Com_GetCurrentParseLine( void ) { - return pi->lines; -} - -/* -=================== -Com_ScriptError - -Prints the script name and line number in the message -=================== -*/ -void Com_ScriptError( const char *msg, ... ) { - va_list argptr; - char string[32000]; - - va_start( argptr, msg ); - vsprintf( string, msg,argptr ); - va_end( argptr ); - - Com_Error( ERR_DROP, "File %s, line %i: %s", pi->parseFile, pi->lines, string ); -} - -void Com_ScriptWarning( const char *msg, ... ) { - va_list argptr; - char string[32000]; - - va_start( argptr, msg ); - vsprintf( string, msg,argptr ); - va_end( argptr ); - - Com_Printf( "File %s, line %i: %s", pi->parseFile, pi->lines, string ); -} - - -/* -=================== -Com_UngetToken - -Calling this will make the next Com_Parse return -the current token instead of advancing the pointer -=================== -*/ -void Com_UngetToken( void ) { - if ( pi->ungetToken ) { - Com_ScriptError( "UngetToken called twice" ); - } - pi->ungetToken = qtrue; -} - - -static const char *SkipWhitespace( const char (*data), qboolean *hasNewLines ) { - int c; - - while( (c = *data) <= ' ') { - if( !c ) { - return NULL; - } - if( c == '\n' ) { - pi->lines++; - *hasNewLines = qtrue; - } - data++; - } - - return data; -} - -/* -============== -Com_ParseExt - -Parse a token out of a string -Will never return NULL, just empty strings. -An empty string will only be returned at end of file. - -If "allowLineBreaks" is qtrue then an empty -string will be returned if the next token is -a newline. -============== -*/ -static char *Com_ParseExt( const char *(*data_p), qboolean allowLineBreaks ) { - int c = 0, len; - qboolean hasNewLines = qfalse; - const char *data; - const char **punc; - - if ( !data_p ) { - Com_Error( ERR_FATAL, "Com_ParseExt: NULL data_p" ); - } - - data = *data_p; - len = 0; - pi->token[0] = 0; - - // make sure incoming data is valid - if ( !data ) { - *data_p = NULL; - return pi->token; - } - - // skip any leading whitespace - while ( 1 ) { - // skip whitespace - data = SkipWhitespace( data, &hasNewLines ); - if ( !data ) { - *data_p = NULL; - return pi->token; - } - if ( hasNewLines && !allowLineBreaks ) { - *data_p = data; - return pi->token; - } - - c = *data; - - // skip double slash comments - if ( c == '/' && data[1] == '/' ) { - while (*data && *data != '\n') { - data++; - } - continue; - } - - // skip /* */ comments - if ( c=='/' && data[1] == '*' ) { - while ( *data && ( *data != '*' || data[1] != '/' ) ) { - if( *data == '\n' ) { - pi->lines++; - } - data++; - } - if ( *data ) { - data += 2; - } - continue; - } - - // a real token to parse - break; - } - - // handle quoted strings - if ( c == '\"' ) { - data++; - while( 1 ) { - c = *data++; - if ( ( c=='\\' ) && ( *data == '\"' ) ) { - // allow quoted strings to use \" to indicate the " character - data++; - } else if ( c=='\"' || !c ) { - pi->token[len] = 0; - *data_p = ( char * ) data; - return pi->token; - } else if( *data == '\n' ) { - pi->lines++; - } - if ( len < MAX_TOKEN_CHARS - 1 ) { - pi->token[len] = c; - len++; - } - } - } - - // check for a number - // is this parsing of negative numbers going to cause expression problems - if ( ( c >= '0' && c <= '9' ) || ( c == '-' && data[ 1 ] >= '0' && data[ 1 ] <= '9' ) || - ( c == '.' && data[ 1 ] >= '0' && data[ 1 ] <= '9' ) ) { - do { - - if (len < MAX_TOKEN_CHARS - 1) { - pi->token[len] = c; - len++; - } - data++; - - c = *data; - } while ( ( c >= '0' && c <= '9' ) || c == '.' ); - - // parse the exponent - if ( c == 'e' || c == 'E' ) { - if (len < MAX_TOKEN_CHARS - 1) { - pi->token[len] = c; - len++; - } - data++; - c = *data; - - if ( c == '-' || c == '+' ) { - if (len < MAX_TOKEN_CHARS - 1) { - pi->token[len] = c; - len++; - } - data++; - c = *data; - } - - do { - if (len < MAX_TOKEN_CHARS - 1) { - pi->token[len] = c; - len++; - } - data++; - - c = *data; - } while ( c >= '0' && c <= '9' ); - } - - if (len == MAX_TOKEN_CHARS) { - len = 0; - } - pi->token[len] = 0; - - *data_p = ( char * ) data; - return pi->token; - } - - // check for a regular word - // we still allow forward and back slashes in name tokens for pathnames - // and also colons for drive letters - if ( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || c == '_' || c == '/' || c == '\\' ) { - do { - if (len < MAX_TOKEN_CHARS - 1) { - pi->token[len] = c; - len++; - } - data++; - - c = *data; - } while ( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || c == '_' - || ( c >= '0' && c <= '9' ) || c == '/' || c == '\\' || c == ':' || c == '.' ); - - if (len == MAX_TOKEN_CHARS) { - len = 0; - } - pi->token[len] = 0; - - *data_p = ( char * ) data; - return pi->token; - } - - // check for multi-character punctuation token - for ( punc = punctuation ; *punc ; punc++ ) { - int l; - int j; - - l = strlen( *punc ); - for ( j = 0 ; j < l ; j++ ) { - if ( data[j] != (*punc)[j] ) { - break; - } - } - if ( j == l ) { - // a valid multi-character punctuation - memcpy( pi->token, *punc, l ); - pi->token[l] = 0; - data += l; - *data_p = (char *)data; - return pi->token; - } - } - - // single character punctuation - pi->token[0] = *data; - pi->token[1] = 0; - data++; - *data_p = (char *)data; - - return pi->token; -} - -/* -=================== -Com_Parse -=================== -*/ -const char *Com_Parse( const char *(*data_p) ) { - if ( pi->ungetToken ) { - pi->ungetToken = qfalse; - return pi->token; - } - return Com_ParseExt( data_p, qtrue ); -} - -/* -=================== -Com_ParseOnLine -=================== -*/ -const char *Com_ParseOnLine( const char *(*data_p) ) { - if ( pi->ungetToken ) { - pi->ungetToken = qfalse; - return pi->token; - } - return Com_ParseExt( data_p, qfalse ); -} - - - -/* -================== -Com_MatchToken -================== -*/ -void Com_MatchToken( const char *(*buf_p), const char *match, qboolean warning ) { - const char *token; - - token = Com_Parse( buf_p ); - if ( strcmp( token, match ) ) { - if (warning) { - Com_ScriptWarning( "MatchToken: %s != %s", token, match ); - } else { - Com_ScriptError( "MatchToken: %s != %s", token, match ); - } - } -} - - -/* -================= -Com_SkipBracedSection - -The next token should be an open brace. -Skips until a matching close brace is found. -Internal brace depths are properly skipped. -================= -*/ -void Com_SkipBracedSection( const char *(*program) ) { - const char *token; - int depth; - - depth = 0; - do { - token = Com_Parse( program ); - if( token[1] == 0 ) { - if( token[0] == '{' ) { - depth++; - } - else if( token[0] == '}' ) { - depth--; - } - } - } while( depth && *program ); -} - -/* -================= -Com_SkipRestOfLine -================= -*/ -void Com_SkipRestOfLine ( const char *(*data) ) { - const char *p; - int c; - - p = *data; - while ( (c = *p++) != 0 ) { - if ( c == '\n' ) { - pi->lines++; - break; - } - } - - *data = p; -} - -/* -==================== -Com_ParseRestOfLine -==================== -*/ -const char *Com_ParseRestOfLine( const char *(*data_p) ) { - static char line[MAX_TOKEN_CHARS]; - const char *token; - - line[0] = 0; - while( 1 ) { - token = Com_ParseOnLine( data_p ); - if ( !token[0] ) { - break; - } - if ( line[0] ) { - Q_strcat( line, sizeof(line), " " ); - } - Q_strcat( line, sizeof(line), token ); - } - - return line; -} - - -float Com_ParseFloat( const char *(*buf_p) ) { - const char *token; - - token = Com_Parse( buf_p ); - if ( !token[0] ) { - return 0; - } - return atof( token ); -} - -int Com_ParseInt( const char *(*buf_p) ) { - const char *token; - - token = Com_Parse( buf_p ); - if ( !token[0] ) { - return 0; - } - return atoi( token ); -} - - - -void Com_Parse1DMatrix( const char *(*buf_p), int x, float *m ) { - const char *token; - int i; - - Com_MatchToken( buf_p, "(" ); - - for (i = 0 ; i < x ; i++) { - token = Com_Parse(buf_p); - m[i] = atof(token); - } - - Com_MatchToken( buf_p, ")" ); -} - -void Com_Parse2DMatrix( const char *(*buf_p), int y, int x, float *m ) { - int i; - - Com_MatchToken( buf_p, "(" ); - - for (i = 0 ; i < y ; i++) { - Com_Parse1DMatrix (buf_p, x, m + i * x); - } - - Com_MatchToken( buf_p, ")" ); -} - -void Com_Parse3DMatrix( const char *(*buf_p), int z, int y, int x, float *m ) { - int i; - - Com_MatchToken( buf_p, "(" ); - - for (i = 0 ; i < z ; i++) { - Com_Parse2DMatrix (buf_p, y, x, m + i * x*y); - } - - Com_MatchToken( buf_p, ")" ); -} - diff --git a/CODE-mp/Splines/q_shared.cpp b/CODE-mp/Splines/q_shared.cpp deleted file mode 100644 index 23757ea..0000000 --- a/CODE-mp/Splines/q_shared.cpp +++ /dev/null @@ -1,955 +0,0 @@ -// q_shared.c -- stateless support routines that are included in each code dll -#include "q_shared.h" - -/* -============================================================================ - -GROWLISTS - -============================================================================ -*/ - -// malloc / free all in one place for debugging -extern "C" void *Com_Allocate( int bytes ); -extern "C" void Com_Dealloc( void *ptr ); - -void Com_InitGrowList( growList_t *list, int maxElements ) { - list->maxElements = maxElements; - list->currentElements = 0; - list->elements = (void **)Com_Allocate( list->maxElements * sizeof( void * ) ); -} - -int Com_AddToGrowList( growList_t *list, void *data ) { - void **old; - - if ( list->currentElements != list->maxElements ) { - list->elements[list->currentElements] = data; - return list->currentElements++; - } - - // grow, reallocate and move - old = list->elements; - - if ( list->maxElements < 0 ) { - Com_Error( ERR_FATAL, "Com_AddToGrowList: maxElements = %i", list->maxElements ); - } - - if ( list->maxElements == 0 ) { - // initialize the list to hold 100 elements - Com_InitGrowList( list, 100 ); - return Com_AddToGrowList( list, data ); - } - - list->maxElements *= 2; - - Com_DPrintf( "Resizing growlist to %i maxElements\n", list->maxElements ); - - list->elements = (void **)Com_Allocate( list->maxElements * sizeof( void * ) ); - - if ( !list->elements ) { - Com_Error( ERR_DROP, "Growlist alloc failed" ); - } - - memcpy( list->elements, old, list->currentElements * sizeof( void * ) ); - - Com_Dealloc( old ); - - return Com_AddToGrowList( list, data ); -} - -void *Com_GrowListElement( const growList_t *list, int index ) { - if ( index < 0 || index >= list->currentElements ) { - Com_Error( ERR_DROP, "Com_GrowListElement: %i out of range of %i", - index, list->currentElements ); - } - return list->elements[index]; -} - -int Com_IndexForGrowListElement( const growList_t *list, const void *element ) { - int i; - - for ( i = 0 ; i < list->currentElements ; i++ ) { - if ( list->elements[i] == element ) { - return i; - } - } - return -1; -} - -//============================================================================ - - -float Com_Clamp( float min, float max, float value ) { - if ( value < min ) { - return min; - } - if ( value > max ) { - return max; - } - return value; -} - -/* -============ -Com_StringContains -============ -*/ -const char *Com_StringContains( const char *str1, const char *str2, int casesensitive) { - int len, i, j; - - len = strlen(str1) - strlen(str2); - for (i = 0; i <= len; i++, str1++) { - for (j = 0; str2[j]; j++) { - if (casesensitive) { - if (str1[j] != str2[j]) { - break; - } - } - else { - if (toupper(str1[j]) != toupper(str2[j])) { - break; - } - } - } - if (!str2[j]) { - return str1; - } - } - return NULL; -} - -/* -============ -Com_Filter -============ -*/ -int Com_Filter( const char *filter, const char *name, int casesensitive) -{ - char buf[MAX_TOKEN_CHARS]; - const char *ptr; - int i, found; - - while(*filter) { - if (*filter == '*') { - filter++; - for (i = 0; *filter; i++) { - if (*filter == '*' || *filter == '?') break; - buf[i] = *filter; - filter++; - } - buf[i] = '\0'; - if (strlen(buf)) { - ptr = Com_StringContains(name, buf, casesensitive); - if (!ptr) return qfalse; - name = ptr + strlen(buf); - } - } - else if (*filter == '?') { - filter++; - name++; - } - else if (*filter == '[' && *(filter+1) == '[') { - filter++; - } - else if (*filter == '[') { - filter++; - found = qfalse; - while(*filter && !found) { - if (*filter == ']' && *(filter+1) != ']') break; - if (*(filter+1) == '-' && *(filter+2) && (*(filter+2) != ']' || *(filter+3) == ']')) { - if (casesensitive) { - if (*name >= *filter && *name <= *(filter+2)) found = qtrue; - } - else { - if (toupper(*name) >= toupper(*filter) && - toupper(*name) <= toupper(*(filter+2))) found = qtrue; - } - filter += 3; - } - else { - if (casesensitive) { - if (*filter == *name) found = qtrue; - } - else { - if (toupper(*filter) == toupper(*name)) found = qtrue; - } - filter++; - } - } - if (!found) return qfalse; - while(*filter) { - if (*filter == ']' && *(filter+1) != ']') break; - filter++; - } - filter++; - name++; - } - else { - if (casesensitive) { - if (*filter != *name) return qfalse; - } - else { - if (toupper(*filter) != toupper(*name)) return qfalse; - } - filter++; - name++; - } - } - return qtrue; -} - - -/* -================ -Com_HashString - -================ -*/ -int Com_HashString( const char *fname ) { - int i; - long hash; - char letter; - - hash = 0; - i = 0; - while (fname[i] != '\0') { - letter = tolower(fname[i]); - if (letter =='.') break; // don't include extension - if (letter =='\\') letter = '/'; // damn path names - hash+=(long)(letter)*(i+119); - i++; - } - hash &= (FILE_HASH_SIZE-1); - return hash; -} - - -/* -============ -Com_SkipPath -============ -*/ -char *Com_SkipPath (char *pathname) -{ - char *last; - - last = pathname; - while (*pathname) - { - if (*pathname=='/') - last = pathname+1; - pathname++; - } - return last; -} - -/* -============ -Com_StripExtension -============ -*/ -void Com_StripExtension( const char *in, char *out ) { - while ( *in && *in != '.' ) { - *out++ = *in++; - } - *out = 0; -} - - -/* -================== -Com_DefaultExtension -================== -*/ -void Com_DefaultExtension (char *path, int maxSize, const char *extension ) { - char oldPath[MAX_QPATH]; - char *src; - -// -// if path doesn't have a .EXT, append extension -// (extension should include the .) -// - src = path + strlen(path) - 1; - - while (*src != '/' && src != path) { - if ( *src == '.' ) { - return; // it has an extension - } - src--; - } - - Q_strncpyz( oldPath, path, sizeof( oldPath ) ); - Com_sprintf( path, maxSize, "%s%s", oldPath, extension ); -} - -/* -============================================================================ - - BYTE ORDER FUNCTIONS - -============================================================================ -*/ - -// can't just use function pointers, or dll linkage can -// mess up when qcommon is included in multiple places -static short (*_BigShort) (short l); -static short (*_LittleShort) (short l); -static int (*_BigLong) (int l); -static int (*_LittleLong) (int l); -static float (*_BigFloat) (float l); -static float (*_LittleFloat) (float l); - -short BigShort(short l){return _BigShort(l);} -short LittleShort(short l) {return _LittleShort(l);} -int BigLong (int l) {return _BigLong(l);} -int LittleLong (int l) {return _LittleLong(l);} -float BigFloat (float l) {return _BigFloat(l);} -float LittleFloat (float l) {return _LittleFloat(l);} - -short ShortSwap (short l) -{ - byte b1,b2; - - b1 = l&255; - b2 = (l>>8)&255; - - return (b1<<8) + b2; -} - -short ShortNoSwap (short l) -{ - return l; -} - -int LongSwap (int l) -{ - byte b1,b2,b3,b4; - - b1 = l&255; - b2 = (l>>8)&255; - b3 = (l>>16)&255; - b4 = (l>>24)&255; - - return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; -} - -int LongNoSwap (int l) -{ - return l; -} - -float FloatSwap (float f) -{ - union - { - float f; - byte b[4]; - } dat1, dat2; - - - dat1.f = f; - dat2.b[0] = dat1.b[3]; - dat2.b[1] = dat1.b[2]; - dat2.b[2] = dat1.b[1]; - dat2.b[3] = dat1.b[0]; - return dat2.f; -} - -float FloatNoSwap (float f) -{ - return f; -} - -/* -================ -Swap_Init -================ -*/ -void Swap_Init (void) -{ - byte swaptest[2] = {1,0}; - -// set the byte swapping variables in a portable manner - if ( *(short *)swaptest == 1) - { - _BigShort = ShortSwap; - _LittleShort = ShortNoSwap; - _BigLong = LongSwap; - _LittleLong = LongNoSwap; - _BigFloat = FloatSwap; - _LittleFloat = FloatNoSwap; - } - else - { - _BigShort = ShortNoSwap; - _LittleShort = ShortSwap; - _BigLong = LongNoSwap; - _LittleLong = LongSwap; - _BigFloat = FloatNoSwap; - _LittleFloat = FloatSwap; - } - -} - -/* -=============== -Com_ParseInfos -=============== -*/ -int Com_ParseInfos( const char *buf, int max, char infos[][MAX_INFO_STRING] ) { - const char *token; - int count; - char key[MAX_TOKEN_CHARS]; - - count = 0; - - while ( 1 ) { - token = Com_Parse( &buf ); - if ( !token[0] ) { - break; - } - if ( strcmp( token, "{" ) ) { - Com_Printf( "Missing { in info file\n" ); - break; - } - - if ( count == max ) { - Com_Printf( "Max infos exceeded\n" ); - break; - } - - infos[count][0] = 0; - while ( 1 ) { - token = Com_Parse( &buf ); - if ( !token[0] ) { - Com_Printf( "Unexpected end of info file\n" ); - break; - } - if ( !strcmp( token, "}" ) ) { - break; - } - Q_strncpyz( key, token, sizeof( key ) ); - - token = Com_ParseOnLine( &buf ); - if ( !token[0] ) { - token = ""; - } - Info_SetValueForKey( infos[count], key, token ); - } - count++; - } - - return count; -} - - - -/* -============================================================================ - - LIBRARY REPLACEMENT FUNCTIONS - -============================================================================ -*/ - -int Q_isprint( int c ) -{ - if ( c >= 0x20 && c <= 0x7E ) - return ( 1 ); - return ( 0 ); -} - -int Q_islower( int c ) -{ - if (c >= 'a' && c <= 'z') - return ( 1 ); - return ( 0 ); -} - -int Q_isupper( int c ) -{ - if (c >= 'A' && c <= 'Z') - return ( 1 ); - return ( 0 ); -} - -int Q_isalpha( int c ) -{ - if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) - return ( 1 ); - return ( 0 ); -} - -char* Q_strrchr( const char* string, int c ) -{ - char cc = c; - char *s; - char *sp=(char *)0; - - s = (char*)string; - - while (*s) - { - if (*s == cc) - sp = s; - s++; - } - if (cc == 0) - sp = s; - - return sp; -} - -/* -============= -Q_strncpyz - -Safe strncpy that ensures a trailing zero -============= -*/ -void Q_strncpyz( char *dest, const char *src, int destsize ) { - if ( !src ) { - Com_Error( ERR_FATAL, "Q_strncpyz: NULL src" ); - } - if ( destsize < 1 ) { - Com_Error(ERR_FATAL,"Q_strncpyz: destsize < 1" ); - } - - strncpy( dest, src, destsize-1 ); - dest[destsize-1] = 0; -} - -int Q_stricmpn (const char *s1, const char *s2, int n) { - int c1, c2; - - do { - c1 = *s1++; - c2 = *s2++; - - if (!n--) { - return 0; // strings are equal until end point - } - - if (c1 != c2) { - if (c1 >= 'a' && c1 <= 'z') { - c1 -= ('a' - 'A'); - } - if (c2 >= 'a' && c2 <= 'z') { - c2 -= ('a' - 'A'); - } - if (c1 != c2) { - return c1 < c2 ? -1 : 1; - } - } - } while (c1); - - return 0; // strings are equal -} - -int Q_strncmp (const char *s1, const char *s2, int n) { - int c1, c2; - - do { - c1 = *s1++; - c2 = *s2++; - - if (!n--) { - return 0; // strings are equal until end point - } - - if (c1 != c2) { - return c1 < c2 ? -1 : 1; - } - } while (c1); - - return 0; // strings are equal -} - -int Q_stricmp (const char *s1, const char *s2) { - return Q_stricmpn (s1, s2, 99999); -} - - -char *Q_strlwr( char *s1 ) { - char *s; - - s = s1; - while ( *s ) { - *s = tolower(*s); - s++; - } - return s1; -} - -char *Q_strupr( char *s1 ) { - char *s; - - s = s1; - while ( *s ) { - *s = toupper(*s); - s++; - } - return s1; -} - - -// never goes past bounds or leaves without a terminating 0 -void Q_strcat( char *dest, int size, const char *src ) { - int l1; - - l1 = strlen( dest ); - if ( l1 >= size ) { - Com_Error( ERR_FATAL, "Q_strcat: already overflowed" ); - } - Q_strncpyz( dest + l1, src, size - l1 ); -} - - -int Q_PrintStrlen( const char *string ) { - int len; - const char *p; - - if( !string ) { - return 0; - } - - len = 0; - p = string; - while( *p ) { - if( Q_IsColorString( p ) ) { - p += 2; - continue; - } - p++; - len++; - } - - return len; -} - - -char *Q_CleanStr( char *string ) { - char* d; - char* s; - int c; - - s = string; - d = string; - while ((c = *s) != 0 ) { - if ( Q_IsColorString( s ) ) { - s++; - } - else if ( c >= 0x20 && c <= 0x7E ) { - *d++ = c; - } - s++; - } - *d = '\0'; - - return string; -} - - -void QDECL Com_sprintf( char *dest, int size, const char *fmt, ...) { - int len; - va_list argptr; - char bigbuffer[32000]; // big, but small enough to fit in PPC stack - - va_start (argptr,fmt); - len = vsprintf (bigbuffer,fmt,argptr); - va_end (argptr); - if ( len >= sizeof( bigbuffer ) ) { - Com_Error( ERR_FATAL, "Com_sprintf: overflowed bigbuffer" ); - } - if (len >= size) { - Com_Printf ("Com_sprintf: overflow of %i in %i\n", len, size); - } - Q_strncpyz (dest, bigbuffer, size ); -} - - -/* -============ -va - -does a varargs printf into a temp buffer, so I don't need to have -varargs versions of all text functions. -FIXME: make this buffer size safe someday -============ -*/ -char * QDECL va( char *format, ... ) { - va_list argptr; - static char string[2][32000]; // in case va is called by nested functions - static int index = 0; - char *buf; - - buf = string[index & 1]; - index++; - - va_start (argptr, format); - vsprintf (buf, format,argptr); - va_end (argptr); - - return buf; -} - - -/* -===================================================================== - - INFO STRINGS - -===================================================================== -*/ - -/* -=============== -Info_ValueForKey - -Searches the string for the given -key and returns the associated value, or an empty string. -FIXME: overflow check? -=============== -*/ -char *Info_ValueForKey( const char *s, const char *key ) { - char pkey[MAX_INFO_KEY]; - static char value[2][MAX_INFO_VALUE]; // use two buffers so compares - // work without stomping on each other - static int valueindex = 0; - char *o; - - if ( !s || !key ) { - return ""; - } - - if ( strlen( s ) >= MAX_INFO_STRING ) { - Com_Error( ERR_DROP, "Info_ValueForKey: oversize infostring" ); - } - - valueindex ^= 1; - if (*s == '\\') - s++; - while (1) - { - o = pkey; - while (*s != '\\') - { - if (!*s) - return ""; - *o++ = *s++; - } - *o = 0; - s++; - - o = value[valueindex]; - - while (*s != '\\' && *s) - { - *o++ = *s++; - } - *o = 0; - - if (!Q_stricmp (key, pkey) ) - return value[valueindex]; - - if (!*s) - break; - s++; - } - - return ""; -} - - -/* -=================== -Info_NextPair - -Used to itterate through all the key/value pairs in an info string -=================== -*/ -void Info_NextPair( const char *(*head), char key[MAX_INFO_KEY], char value[MAX_INFO_VALUE] ) { - char *o; - const char *s; - - s = *head; - - if ( *s == '\\' ) { - s++; - } - key[0] = 0; - value[0] = 0; - - o = key; - while ( *s != '\\' ) { - if ( !*s ) { - *o = 0; - *head = s; - return; - } - *o++ = *s++; - } - *o = 0; - s++; - - o = value; - while ( *s != '\\' && *s ) { - *o++ = *s++; - } - *o = 0; - - *head = s; -} - - -/* -=================== -Info_RemoveKey -=================== -*/ -void Info_RemoveKey( char *s, const char *key ) { - char *start; - char pkey[MAX_INFO_KEY]; - char value[MAX_INFO_VALUE]; - char *o; - - if ( strlen( s ) >= MAX_INFO_STRING ) { - Com_Error( ERR_DROP, "Info_RemoveKey: oversize infostring" ); - } - - if (strchr (key, '\\')) { - return; - } - - while (1) - { - start = s; - if (*s == '\\') - s++; - o = pkey; - while (*s != '\\') - { - if (!*s) - return; - *o++ = *s++; - } - *o = 0; - s++; - - o = value; - while (*s != '\\' && *s) - { - if (!*s) - return; - *o++ = *s++; - } - *o = 0; - - if (!strcmp (key, pkey) ) - { - strcpy (start, s); // remove this part - return; - } - - if (!*s) - return; - } - -} - - -/* -================== -Info_Validate - -Some characters are illegal in info strings because they -can mess up the server's parsing -================== -*/ -qboolean Info_Validate( const char *s ) { - if ( strchr( s, '\"' ) ) { - return qfalse; - } - if ( strchr( s, ';' ) ) { - return qfalse; - } - return qtrue; -} - -/* -================== -Info_SetValueForKey - -Changes or adds a key/value pair -================== -*/ -void Info_SetValueForKey( char *s, const char *key, const char *value ) { - char newi[MAX_INFO_STRING]; - - if ( strlen( s ) >= MAX_INFO_STRING ) { - Com_Error( ERR_DROP, "Info_SetValueForKey: oversize infostring" ); - } - - if (strchr (key, '\\') || strchr (value, '\\')) - { - Com_Printf ("Can't use keys or values with a \\\n"); - return; - } - - if (strchr (key, ';') || strchr (value, ';')) - { - Com_Printf ("Can't use keys or values with a semicolon\n"); - return; - } - - if (strchr (key, '\"') || strchr (value, '\"')) - { - Com_Printf ("Can't use keys or values with a \"\n"); - return; - } - - Info_RemoveKey (s, key); - if (!value || !strlen(value)) - return; - - Com_sprintf (newi, sizeof(newi), "\\%s\\%s", key, value); - - if (strlen(newi) + strlen(s) > MAX_INFO_STRING) - { - Com_Printf ("Info string length exceeded\n"); - return; - } - - strcat (s, newi); -} - -//==================================================================== - - -/* -=============== -ParseHex -=============== -*/ -int ParseHex( const char *text ) { - int value; - int c; - - value = 0; - while ( ( c = *text++ ) != 0 ) { - if ( c >= '0' && c <= '9' ) { - value = value * 16 + c - '0'; - continue; - } - if ( c >= 'a' && c <= 'f' ) { - value = value * 16 + 10 + c - 'a'; - continue; - } - if ( c >= 'A' && c <= 'F' ) { - value = value * 16 + 10 + c - 'A'; - continue; - } - } - - return value; -} diff --git a/CODE-mp/Splines/q_shared.h b/CODE-mp/Splines/q_shared.h deleted file mode 100644 index 5f381ab..0000000 --- a/CODE-mp/Splines/q_shared.h +++ /dev/null @@ -1,789 +0,0 @@ -#ifndef __Q_SHARED_H -#define __Q_SHARED_H - -// q_shared.h -- included first by ALL program modules. -// these are the definitions that have no dependance on -// central system services, and can be used by any part -// of the program without any state issues. - -// A user mod should never modify this file - -// incursion of DOOM code into the Q3A codebase -//#define Q3_VERSION "DOOM 0.01" - -// alignment macros for SIMD -#define ALIGN_ON -#define ALIGN_OFF - -#ifdef _WIN32 - -#pragma warning(disable : 4018) // signed/unsigned mismatch -#pragma warning(disable : 4032) -#pragma warning(disable : 4051) -#pragma warning(disable : 4057) // slightly different base types -#pragma warning(disable : 4100) // unreferenced formal parameter -#pragma warning(disable : 4115) -#pragma warning(disable : 4125) // decimal digit terminates octal escape sequence -#pragma warning(disable : 4127) // conditional expression is constant -#pragma warning(disable : 4136) -#pragma warning(disable : 4201) -#pragma warning(disable : 4214) -#pragma warning(disable : 4244) -#pragma warning(disable : 4305) // truncation from const double to float -#pragma warning(disable : 4310) // cast truncates constant value -#pragma warning(disable : 4514) -#pragma warning(disable : 4711) // selected for automatic inline expansion -#pragma warning(disable : 4220) // varargs matches remaining parameters - -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef WIN32 // mac doesn't have malloc.h -#include // for _alloca() -#endif -#ifdef _WIN32 - -//#pragma intrinsic( memset, memcpy ) - -#endif - - -// this is the define for determining if we have an asm version of a C function -#if (defined _M_IX86 || defined __i386__) && !defined __sun__ && !defined __LCC__ -#define id386 1 -#else -#define id386 0 -#endif - -// for windows fastcall option - -#define QDECL - -//======================= WIN32 DEFINES ================================= - -#ifdef WIN32 - -#define MAC_STATIC - -#undef QDECL -#define QDECL __cdecl - -// buildstring will be incorporated into the version string -#ifdef NDEBUG -#ifdef _M_IX86 -#define CPUSTRING "win-x86" -#elif defined _M_ALPHA -#define CPUSTRING "win-AXP" -#endif -#else -#ifdef _M_IX86 -#define CPUSTRING "win-x86-debug" -#elif defined _M_ALPHA -#define CPUSTRING "win-AXP-debug" -#endif -#endif - - -#define PATH_SEP '\\' - -#endif - -//======================= MAC OS X SERVER DEFINES ===================== - -#if defined(__MACH__) && defined(__APPLE__) - -#define MAC_STATIC - -#ifdef __ppc__ -#define CPUSTRING "MacOSXS-ppc" -#elif defined __i386__ -#define CPUSTRING "MacOSXS-i386" -#else -#define CPUSTRING "MacOSXS-other" -#endif - -#define PATH_SEP '/' - -#define GAME_HARD_LINKED -#define CGAME_HARD_LINKED -#define UI_HARD_LINKED -#define _alloca alloca - -#undef ALIGN_ON -#undef ALIGN_OFF -#define ALIGN_ON #pragma align(16) -#define ALIGN_OFF #pragma align() - -#ifdef __cplusplus - extern "C" { -#endif - -void *osxAllocateMemory(long size); -void osxFreeMemory(void *pointer); - -#ifdef __cplusplus - } -#endif - -#endif - -//======================= MAC DEFINES ================================= - -#ifdef __MACOS__ - -#define MAC_STATIC static - -#define CPUSTRING "MacOS-PPC" - -#define PATH_SEP ':' - -void Sys_PumpEvents( void ); - -#endif - -#ifdef __MRC__ - -#define MAC_STATIC - -#define CPUSTRING "MacOS-PPC" - -#define PATH_SEP ':' - -void Sys_PumpEvents( void ); - -#undef QDECL -#define QDECL __cdecl - -#define _alloca alloca -#endif - -//======================= LINUX DEFINES ================================= - -// the mac compiler can't handle >32k of locals, so we -// just waste space and make big arrays static... -#ifdef __linux__ - -// bk001205 - from Makefile -#define stricmp strcasecmp - -#define MAC_STATIC // bk: FIXME - -#ifdef __i386__ -#define CPUSTRING "linux-i386" -#elif defined __axp__ -#define CPUSTRING "linux-alpha" -#else -#define CPUSTRING "linux-other" -#endif - -#define PATH_SEP '/' - -// bk001205 - try -#ifdef Q3_STATIC -#define GAME_HARD_LINKED -#define CGAME_HARD_LINKED -#define UI_HARD_LINKED -#define BOTLIB_HARD_LINKED -#endif - -#endif - -//============================================================= - - - -typedef enum {qfalse, qtrue} qboolean; - -typedef unsigned char byte; - -#define EQUAL_EPSILON 0.001 - -typedef int qhandle_t; -typedef int sfxHandle_t; -typedef int fileHandle_t; -typedef int clipHandle_t; - -typedef enum { - INVALID_JOINT = -1 -} jointHandle_t; - -#ifndef NULL -#define NULL ((void *)0) -#endif - -#define MAX_QINT 0x7fffffff -#define MIN_QINT (-MAX_QINT-1) - -#ifndef max -#define max( x, y ) ( ( ( x ) > ( y ) ) ? ( x ) : ( y ) ) -#define min( x, y ) ( ( ( x ) < ( y ) ) ? ( x ) : ( y ) ) -#endif - -#ifndef sign -#define sign( f ) ( ( f > 0 ) ? 1 : ( ( f < 0 ) ? -1 : 0 ) ) -#endif - -// angle indexes -#define PITCH 0 // up / down -#define YAW 1 // left / right -#define ROLL 2 // fall over - -// the game guarantees that no string from the network will ever -// exceed MAX_STRING_CHARS -#define MAX_STRING_CHARS 1024 // max length of a string passed to Cmd_TokenizeString -#define MAX_STRING_TOKENS 256 // max tokens resulting from Cmd_TokenizeString -#define MAX_TOKEN_CHARS 1024 // max length of an individual token - -#define MAX_INFO_STRING 1024 -#define MAX_INFO_KEY 1024 -#define MAX_INFO_VALUE 1024 - - -#define MAX_QPATH 64 // max length of a quake game pathname -#define MAX_OSPATH 128 // max length of a filesystem pathname - -#define MAX_NAME_LENGTH 32 // max length of a client name - -// paramters for command buffer stuffing -typedef enum { - EXEC_NOW, // don't return until completed, a VM should NEVER use this, - // because some commands might cause the VM to be unloaded... - EXEC_INSERT, // insert at current position, but don't run yet - EXEC_APPEND // add to end of the command buffer (normal case) -} cbufExec_t; - - -// -// these aren't needed by any of the VMs. put in another header? -// -#define MAX_MAP_AREA_BYTES 32 // bit vector of area visibility - -#undef ERR_FATAL // malloc.h on unix - -// parameters to the main Error routine -typedef enum { - ERR_NONE, - ERR_FATAL, // exit the entire game with a popup window - ERR_DROP, // print to console and disconnect from game - ERR_DISCONNECT, // don't kill server - ERR_NEED_CD // pop up the need-cd dialog -} errorParm_t; - - -// font rendering values used by ui and cgame - -#define PROP_GAP_WIDTH 3 -#define PROP_SPACE_WIDTH 8 -#define PROP_HEIGHT 27 -#define PROP_SMALL_SIZE_SCALE 0.75 - -#define BLINK_DIVISOR 200 -#define PULSE_DIVISOR 75 - -#define UI_LEFT 0x00000000 // default -#define UI_CENTER 0x00000001 -#define UI_RIGHT 0x00000002 -#define UI_FORMATMASK 0x00000007 -#define UI_SMALLFONT 0x00000010 -#define UI_BIGFONT 0x00000020 // default -#define UI_GIANTFONT 0x00000040 -#define UI_DROPSHADOW 0x00000800 -#define UI_BLINK 0x00001000 -#define UI_INVERSE 0x00002000 -#define UI_PULSE 0x00004000 - - -/* -============================================================== - -MATHLIB - -============================================================== -*/ -#ifdef __cplusplus // so we can include this in C code -#define SIDE_FRONT 0 -#define SIDE_BACK 1 -#define SIDE_ON 2 -#define SIDE_CROSS 3 - -#define Q_PI 3.14159265358979323846 -#ifndef M_PI -#define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h -#endif - -#include "math_vector.h" -#include "math_angles.h" -#include "math_matrix.h" -#include "math_quaternion.h" - -class idVec3_t; // for defining vectors -typedef idVec3_t &vec3_p; // for passing vectors as function arguments -typedef const idVec3_t &vec3_c; // for passing vectors as const function arguments - -class angles_t; // for defining angle vectors -typedef angles_t &angles_p; // for passing angles as function arguments -typedef const angles_t &angles_c; // for passing angles as const function arguments - -class mat3_t; // for defining matrices -typedef mat3_t &mat3_p; // for passing matrices as function arguments -typedef const mat3_t &mat3_c; // for passing matrices as const function arguments - - - -#define NUMVERTEXNORMALS 162 -extern idVec3_t bytedirs[NUMVERTEXNORMALS]; - -// all drawing is done to a 640*480 virtual screen size -// and will be automatically scaled to the real resolution -#define SCREEN_WIDTH 640 -#define SCREEN_HEIGHT 480 - -#define TINYCHAR_WIDTH (SMALLCHAR_WIDTH) -#define TINYCHAR_HEIGHT (SMALLCHAR_HEIGHT/2) - -#define SMALLCHAR_WIDTH 8 -#define SMALLCHAR_HEIGHT 16 - -#define BIGCHAR_WIDTH 16 -#define BIGCHAR_HEIGHT 16 - -#define GIANTCHAR_WIDTH 32 -#define GIANTCHAR_HEIGHT 48 - -extern vec4_t colorBlack; -extern vec4_t colorRed; -extern vec4_t colorGreen; -extern vec4_t colorBlue; -extern vec4_t colorYellow; -extern vec4_t colorMagenta; -extern vec4_t colorCyan; -extern vec4_t colorWhite; -extern vec4_t colorLtGrey; -extern vec4_t colorMdGrey; -extern vec4_t colorDkGrey; - -#define Q_COLOR_ESCAPE '^' -#define Q_IsColorString(p) ( p && *(p) == Q_COLOR_ESCAPE && *((p)+1) && *((p)+1) != Q_COLOR_ESCAPE ) - -#define COLOR_BLACK '0' -#define COLOR_RED '1' -#define COLOR_GREEN '2' -#define COLOR_YELLOW '3' -#define COLOR_BLUE '4' -#define COLOR_CYAN '5' -#define COLOR_MAGENTA '6' -#define COLOR_WHITE '7' -#define ColorIndex(c) ( ( (c) - '0' ) & 7 ) - -#define S_COLOR_BLACK "^0" -#define S_COLOR_RED "^1" -#define S_COLOR_GREEN "^2" -#define S_COLOR_YELLOW "^3" -#define S_COLOR_BLUE "^4" -#define S_COLOR_CYAN "^5" -#define S_COLOR_MAGENTA "^6" -#define S_COLOR_WHITE "^7" - -extern vec4_t g_color_table[8]; - -#define MAKERGB( v, r, g, b ) v[0]=r;v[1]=g;v[2]=b -#define MAKERGBA( v, r, g, b, a ) v[0]=r;v[1]=g;v[2]=b;v[3]=a - -#define DEG2RAD( a ) ( ( (a) * M_PI ) / 180.0F ) -#define RAD2DEG( a ) ( ( (a) * 180.0f ) / M_PI ) - -struct cplane_s; - -extern idVec3_t vec3_origin; -extern vec4_t vec4_origin; -extern mat3_t axisDefault; - -#define nanmask (255<<23) - -#define IS_NAN(x) (((*(int *)&x)&nanmask)==nanmask) - -float Q_fabs( float f ); -float Q_rsqrt( float f ); // reciprocal square root - -#define SQRTFAST( x ) ( 1.0f / Q_rsqrt( x ) ) - -signed char ClampChar( int i ); -signed short ClampShort( int i ); - -// this isn't a real cheap function to call! -int DirToByte( const idVec3_t &dir ); -void ByteToDir( int b, vec3_p dir ); - -#define DotProduct(a,b) ((a)[0]*(b)[0]+(a)[1]*(b)[1]+(a)[2]*(b)[2]) -#define VectorSubtract(a,b,c) ((c)[0]=(a)[0]-(b)[0],(c)[1]=(a)[1]-(b)[1],(c)[2]=(a)[2]-(b)[2]) -#define VectorAdd(a,b,c) ((c)[0]=(a)[0]+(b)[0],(c)[1]=(a)[1]+(b)[1],(c)[2]=(a)[2]+(b)[2]) -#define VectorCopy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2]) -//#define VectorCopy(a,b) ((b).x=(a).x,(b).y=(a).y,(b).z=(a).z]) - -#define VectorScale(v, s, o) ((o)[0]=(v)[0]*(s),(o)[1]=(v)[1]*(s),(o)[2]=(v)[2]*(s)) -#define VectorMA(v, s, b, o) ((o)[0]=(v)[0]+(b)[0]*(s),(o)[1]=(v)[1]+(b)[1]*(s),(o)[2]=(v)[2]+(b)[2]*(s)) -#define CrossProduct(a,b,c) ((c)[0]=(a)[1]*(b)[2]-(a)[2]*(b)[1],(c)[1]=(a)[2]*(b)[0]-(a)[0]*(b)[2],(c)[2]=(a)[0]*(b)[1]-(a)[1]*(b)[0]) - -#define DotProduct4(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2]+(x)[3]*(y)[3]) -#define VectorSubtract4(a,b,c) ((c)[0]=(a)[0]-(b)[0],(c)[1]=(a)[1]-(b)[1],(c)[2]=(a)[2]-(b)[2],(c)[3]=(a)[3]-(b)[3]) -#define VectorAdd4(a,b,c) ((c)[0]=(a)[0]+(b)[0],(c)[1]=(a)[1]+(b)[1],(c)[2]=(a)[2]+(b)[2],(c)[3]=(a)[3]+(b)[3]) -#define VectorCopy4(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3]) -#define VectorScale4(v, s, o) ((o)[0]=(v)[0]*(s),(o)[1]=(v)[1]*(s),(o)[2]=(v)[2]*(s),(o)[3]=(v)[3]*(s)) -#define VectorMA4(v, s, b, o) ((o)[0]=(v)[0]+(b)[0]*(s),(o)[1]=(v)[1]+(b)[1]*(s),(o)[2]=(v)[2]+(b)[2]*(s),(o)[3]=(v)[3]+(b)[3]*(s)) - - -#define VectorClear(a) ((a)[0]=(a)[1]=(a)[2]=0) -#define VectorNegate(a,b) ((b)[0]=-(a)[0],(b)[1]=-(a)[1],(b)[2]=-(a)[2]) -#define VectorSet(v, x, y, z) ((v)[0]=(x), (v)[1]=(y), (v)[2]=(z)) -#define Vector4Copy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3]) - -#define SnapVector(v) {v[0]=(int)v[0];v[1]=(int)v[1];v[2]=(int)v[2];} - -float NormalizeColor( vec3_c in, vec3_p out ); - -int VectorCompare( vec3_c v1, vec3_c v2 ); -float VectorLength( vec3_c v ); -float Distance( vec3_c p1, vec3_c p2 ); -float DistanceSquared( vec3_c p1, vec3_c p2 ); -float VectorNormalize (vec3_p v); // returns vector length -void VectorNormalizeFast(vec3_p v); // does NOT return vector length, uses rsqrt approximation -float VectorNormalize2( vec3_c v, vec3_p out ); -void VectorInverse (vec3_p v); -void VectorRotate( vec3_c in, mat3_c matrix, vec3_p out ); -void VectorPolar(vec3_p v, float radius, float theta, float phi); -void VectorSnap(vec3_p v); -void Vector53Copy( const idVec5_t &in, vec3_p out); -void Vector5Scale( const idVec5_t &v, float scale, idVec5_t &out); -void Vector5Add( const idVec5_t &va, const idVec5_t &vb, idVec5_t &out); -void VectorRotate3( vec3_c vIn, vec3_c vRotation, vec3_p out); -void VectorRotate3Origin(vec3_c vIn, vec3_c vRotation, vec3_c vOrigin, vec3_p out); - - -int Q_log2(int val); - -int Q_rand( int *seed ); -float Q_random( int *seed ); -float Q_crandom( int *seed ); - -#define random() ((rand () & 0x7fff) / ((float)0x7fff)) -#define crandom() (2.0 * (random() - 0.5)) - -float Q_rint( float in ); - -void vectoangles( vec3_c value1, angles_p angles); -void AnglesToAxis( angles_c angles, mat3_p axis ); - -void AxisCopy( mat3_c in, mat3_p out ); -qboolean AxisRotated( mat3_c in ); // assumes a non-degenerate axis - -int SignbitsForNormal( vec3_c normal ); -int BoxOnPlaneSide( const Bounds &b, struct cplane_s *p ); - -float AngleMod(float a); -float LerpAngle (float from, float to, float frac); -float AngleSubtract( float a1, float a2 ); -void AnglesSubtract( angles_c v1, angles_c v2, angles_p v3 ); - -float AngleNormalize360 ( float angle ); -float AngleNormalize180 ( float angle ); -float AngleDelta ( float angle1, float angle2 ); - -qboolean PlaneFromPoints( vec4_t &plane, vec3_c a, vec3_c b, vec3_c c ); -void ProjectPointOnPlane( vec3_p dst, vec3_c p, vec3_c normal ); -void RotatePointAroundVector( vec3_p dst, vec3_c dir, vec3_c point, float degrees ); -void RotateAroundDirection( mat3_p axis, float yaw ); -void MakeNormalVectors( vec3_c forward, vec3_p right, vec3_p up ); -// perpendicular vector could be replaced by this - -int PlaneTypeForNormal( vec3_c normal ); - -void MatrixMultiply( mat3_c in1, mat3_c in2, mat3_p out ); -void MatrixInverseMultiply( mat3_c in1, mat3_c in2, mat3_p out ); // in2 is transposed during multiply -void MatrixTransformVector( vec3_c in, mat3_c matrix, vec3_p out ); -void MatrixProjectVector( vec3_c in, mat3_c matrix, vec3_p out ); // Places the vector into a new coordinate system. -void AngleVectors( angles_c angles, vec3_p forward, vec3_p right, vec3_p up); -void PerpendicularVector( vec3_p dst, vec3_c src ); - -float TriangleArea( vec3_c a, vec3_c b, vec3_c c ); -#endif // __cplusplus - -//============================================= - -float Com_Clamp( float min, float max, float value ); - -#define FILE_HASH_SIZE 1024 -int Com_HashString( const char *fname ); - -char *Com_SkipPath( char *pathname ); - -// it is ok for out == in -void Com_StripExtension( const char *in, char *out ); - -// "extension" should include the dot: ".map" -void Com_DefaultExtension( char *path, int maxSize, const char *extension ); - -int Com_ParseInfos( const char *buf, int max, char infos[][MAX_INFO_STRING] ); - -/* -===================================================================================== - -SCRIPT PARSING - -===================================================================================== -*/ - -// this just controls the comment printing, it doesn't actually load a file -void Com_BeginParseSession( const char *filename ); -void Com_EndParseSession( void ); - -int Com_GetCurrentParseLine( void ); - -// Will never return NULL, just empty strings. -// An empty string will only be returned at end of file. -// ParseOnLine will return empty if there isn't another token on this line - -// this funny typedef just means a moving pointer into a const char * buffer -const char *Com_Parse( const char *(*data_p) ); -const char *Com_ParseOnLine( const char *(*data_p) ); -const char *Com_ParseRestOfLine( const char *(*data_p) ); - -void Com_UngetToken( void ); - -#ifdef __cplusplus -void Com_MatchToken( const char *(*buf_p), const char *match, qboolean warning = qfalse ); -#else -void Com_MatchToken( const char *(*buf_p), const char *match, qboolean warning ); -#endif - -void Com_ScriptError( const char *msg, ... ); -void Com_ScriptWarning( const char *msg, ... ); - -void Com_SkipBracedSection( const char *(*program) ); -void Com_SkipRestOfLine( const char *(*data) ); - -float Com_ParseFloat( const char *(*buf_p) ); -int Com_ParseInt( const char *(*buf_p) ); - -void Com_Parse1DMatrix( const char *(*buf_p), int x, float *m ); -void Com_Parse2DMatrix( const char *(*buf_p), int y, int x, float *m ); -void Com_Parse3DMatrix( const char *(*buf_p), int z, int y, int x, float *m ); - -//===================================================================================== -#ifdef __cplusplus - extern "C" { -#endif - -void QDECL Com_sprintf (char *dest, int size, const char *fmt, ...); - - -// mode parm for FS_FOpenFile -typedef enum { - FS_READ, - FS_WRITE, - FS_APPEND, - FS_APPEND_SYNC -} fsMode_t; - -typedef enum { - FS_SEEK_CUR, - FS_SEEK_END, - FS_SEEK_SET -} fsOrigin_t; - -//============================================= - -int Q_isprint( int c ); -int Q_islower( int c ); -int Q_isupper( int c ); -int Q_isalpha( int c ); - -// portable case insensitive compare -int Q_stricmp (const char *s1, const char *s2); -int Q_strncmp (const char *s1, const char *s2, int n); -int Q_stricmpn (const char *s1, const char *s2, int n); -char *Q_strlwr( char *s1 ); -char *Q_strupr( char *s1 ); -char *Q_strrchr( const char* string, int c ); - -// buffer size safe library replacements -void Q_strncpyz( char *dest, const char *src, int destsize ); -void Q_strcat( char *dest, int size, const char *src ); - -// strlen that discounts Quake color sequences -int Q_PrintStrlen( const char *string ); -// removes color sequences from string -char *Q_CleanStr( char *string ); - -int Com_Filter( const char *filter, const char *name, int casesensitive ); -const char *Com_StringContains( const char *str1, const char *str2, int casesensitive ); - - -//============================================= - -short BigShort(short l); -short LittleShort(short l); -int BigLong (int l); -int LittleLong (int l); -float BigFloat (float l); -float LittleFloat (float l); - -void Swap_Init (void); -char * QDECL va(char *format, ...); - -#ifdef __cplusplus - } -#endif - - -//============================================= -#ifdef __cplusplus -// -// mapfile parsing -// -typedef struct ePair_s { - char *key; - char *value; -} ePair_t; - -typedef struct mapSide_s { - char material[MAX_QPATH]; - vec4_t plane; - vec4_t textureVectors[2]; -} mapSide_t; - -typedef struct { - int numSides; - mapSide_t **sides; -} mapBrush_t; - -typedef struct { - idVec3_t xyz; - float st[2]; -} patchVertex_t; - -typedef struct { - char material[MAX_QPATH]; - int width, height; - patchVertex_t *patchVerts; -} mapPatch_t; - -typedef struct { - char modelName[MAX_QPATH]; - float matrix[16]; -} mapModel_t; - -typedef struct mapPrimitive_s { - int numEpairs; - ePair_t **ePairs; - - // only one of these will be non-NULL - mapBrush_t *brush; - mapPatch_t *patch; - mapModel_t *model; -} mapPrimitive_t; - -typedef struct mapEntity_s { - int numPrimitives; - mapPrimitive_t **primitives; - - int numEpairs; - ePair_t **ePairs; -} mapEntity_t; - -typedef struct { - int numEntities; - mapEntity_t **entities; -} mapFile_t; - - -// the order of entities, brushes, and sides will be maintained, the -// lists won't be swapped on each load or save -mapFile_t *ParseMapFile( const char *text ); -void FreeMapFile( mapFile_t *mapFile ); -void WriteMapFile( const mapFile_t *mapFile, FILE *f ); - -// key names are case-insensitive -const char *ValueForMapEntityKey( const mapEntity_t *ent, const char *key ); -float FloatForMapEntityKey( const mapEntity_t *ent, const char *key ); -qboolean GetVectorForMapEntityKey( const mapEntity_t *ent, const char *key, idVec3_t &vec ); - -typedef struct { - idVec3_t xyz; - idVec2_t st; - idVec3_t normal; - idVec3_t tangents[2]; - byte smoothing[4]; // colors for silhouette smoothing -} drawVert_t; - -typedef struct { - int width, height; - drawVert_t *verts; -} drawVertMesh_t; - -// Tesselate a map patch into smoothed, drawable vertexes -// MaxError of around 4 is reasonable -drawVertMesh_t *SubdivideMapPatch( const mapPatch_t *patch, float maxError ); -#endif // __cplusplus - -//========================================= - -#ifdef __cplusplus - extern "C" { -#endif - -void QDECL Com_Error( int level, const char *error, ... ); -void QDECL Com_Printf( const char *msg, ... ); -void QDECL Com_DPrintf( const char *msg, ... ); - -#ifdef __cplusplus - } -#endif - - -typedef struct { - qboolean frameMemory; - int currentElements; - int maxElements; // will reallocate and move when exceeded - void **elements; -} growList_t; - -// you don't need to init the growlist if you don't mind it growing and moving -// the list as it expands -void Com_InitGrowList( growList_t *list, int maxElements ); -int Com_AddToGrowList( growList_t *list, void *data ); -void *Com_GrowListElement( const growList_t *list, int index ); -int Com_IndexForGrowListElement( const growList_t *list, const void *element ); - - -// -// key / value info strings -// -char *Info_ValueForKey( const char *s, const char *key ); -void Info_RemoveKey( char *s, const char *key ); -void Info_SetValueForKey( char *s, const char *key, const char *value ); -qboolean Info_Validate( const char *s ); -void Info_NextPair( const char *(*s), char key[MAX_INFO_KEY], char value[MAX_INFO_VALUE] ); - -// get cvar defs, collision defs, etc -//#include "../shared/interface.h" - -// get key code numbers for events -//#include "../shared/keycodes.h" - -#ifdef __cplusplus -// get the polygon winding functions -//#include "../shared/windings.h" - -// get the flags class -//#include "../shared/idflags.h" -#endif // __cplusplus - -#endif // __Q_SHARED_H - diff --git a/CODE-mp/Splines/splines.cpp b/CODE-mp/Splines/splines.cpp deleted file mode 100644 index bb6c4af..0000000 --- a/CODE-mp/Splines/splines.cpp +++ /dev/null @@ -1,1226 +0,0 @@ - -//#include "stdafx.h" -//#include "qe3.h" - -#include "q_shared.h" -#include "splines.h" - -extern "C" { -int FS_Write( const void *buffer, int len, fileHandle_t h ); -int FS_ReadFile( const char *qpath, void **buffer ); -void FS_FreeFile( void *buffer ); -fileHandle_t FS_FOpenFileWrite( const char *filename ); -void FS_FCloseFile( fileHandle_t f ); -} - -float Q_fabs( float f ) { - int tmp = * ( int * ) &f; - tmp &= 0x7FFFFFFF; - return * ( float * ) &tmp; -} - - -//#include "../shared/windings.h" -//#include "../qcommon/qcommon.h" -//#include "../sys/sys_public.h" -//#include "../game/game_entity.h" - -idCameraDef splineList; -idCameraDef *g_splineList = &splineList; - -idVec3_t idSplineList::zero(0,0,0); - -void glLabeledPoint(idVec3_t &color, idVec3_t &point, float size, const char *label) { - qglColor3fv(color); - qglPointSize(size); - qglBegin(GL_POINTS); - qglVertex3fv(point); - qglEnd(); - idVec3_t v = point; - v.x += 1; - v.y += 1; - v.z += 1; - qglRasterPos3fv (v); - qglCallLists (strlen(label), GL_UNSIGNED_BYTE, label); -} - - -void glBox(idVec3_t &color, idVec3_t &point, float size) { - idVec3_t mins(point); - idVec3_t maxs(point); - mins[0] -= size; - mins[1] += size; - mins[2] -= size; - maxs[0] += size; - maxs[1] -= size; - maxs[2] += size; - qglColor3fv(color); - qglBegin(GL_LINE_LOOP); - qglVertex3f(mins[0],mins[1],mins[2]); - qglVertex3f(maxs[0],mins[1],mins[2]); - qglVertex3f(maxs[0],maxs[1],mins[2]); - qglVertex3f(mins[0],maxs[1],mins[2]); - qglEnd(); - qglBegin(GL_LINE_LOOP); - qglVertex3f(mins[0],mins[1],maxs[2]); - qglVertex3f(maxs[0],mins[1],maxs[2]); - qglVertex3f(maxs[0],maxs[1],maxs[2]); - qglVertex3f(mins[0],maxs[1],maxs[2]); - qglEnd(); - - qglBegin(GL_LINES); - qglVertex3f(mins[0],mins[1],mins[2]); - qglVertex3f(mins[0],mins[1],maxs[2]); - qglVertex3f(mins[0],maxs[1],maxs[2]); - qglVertex3f(mins[0],maxs[1],mins[2]); - qglVertex3f(maxs[0],mins[1],mins[2]); - qglVertex3f(maxs[0],mins[1],maxs[2]); - qglVertex3f(maxs[0],maxs[1],maxs[2]); - qglVertex3f(maxs[0],maxs[1],mins[2]); - qglEnd(); - -} - -void splineTest() { - //g_splineList->load("p:/doom/base/maps/test_base1.camera"); -} - -void splineDraw() { - //g_splineList->addToRenderer(); -} - - -//extern void D_DebugLine( const idVec3_t &color, const idVec3_t &start, const idVec3_t &end ); - -void debugLine(idVec3_t &color, float x, float y, float z, float x2, float y2, float z2) { - //idVec3_t from(x, y, z); - //idVec3_t to(x2, y2, z2); - //D_DebugLine(color, from, to); -} - -void idSplineList::addToRenderer() { - - if (controlPoints.Num() == 0) { - return; - } - - idVec3_t mins, maxs; - idVec3_t yellow(1.0, 1.0, 0); - idVec3_t white(1.0, 1.0, 1.0); - int i; - - for(i = 0; i < controlPoints.Num(); i++) { - VectorCopy(*controlPoints[i], mins); - VectorCopy(mins, maxs); - mins[0] -= 8; - mins[1] += 8; - mins[2] -= 8; - maxs[0] += 8; - maxs[1] -= 8; - maxs[2] += 8; - debugLine( yellow, mins[0], mins[1], mins[2], maxs[0], mins[1], mins[2]); - debugLine( yellow, maxs[0], mins[1], mins[2], maxs[0], maxs[1], mins[2]); - debugLine( yellow, maxs[0], maxs[1], mins[2], mins[0], maxs[1], mins[2]); - debugLine( yellow, mins[0], maxs[1], mins[2], mins[0], mins[1], mins[2]); - - debugLine( yellow, mins[0], mins[1], maxs[2], maxs[0], mins[1], maxs[2]); - debugLine( yellow, maxs[0], mins[1], maxs[2], maxs[0], maxs[1], maxs[2]); - debugLine( yellow, maxs[0], maxs[1], maxs[2], mins[0], maxs[1], maxs[2]); - debugLine( yellow, mins[0], maxs[1], maxs[2], mins[0], mins[1], maxs[2]); - - } - - int step = 0; - idVec3_t step1; - for(i = 3; i < controlPoints.Num(); i++) { - for (float tension = 0.0f; tension < 1.001f; tension += 0.1f) { - float x = 0; - float y = 0; - float z = 0; - for (int j = 0; j < 4; j++) { - x += controlPoints[i - (3 - j)]->x * calcSpline(j, tension); - y += controlPoints[i - (3 - j)]->y * calcSpline(j, tension); - z += controlPoints[i - (3 - j)]->z * calcSpline(j, tension); - } - if (step == 0) { - step1[0] = x; - step1[1] = y; - step1[2] = z; - step = 1; - } else { - debugLine( white, step1[0], step1[1], step1[2], x, y, z); - step = 0; - } - - } - } -} - -void idSplineList::buildSpline() { - //int start = Sys_Milliseconds(); - clearSpline(); - for(int i = 3; i < controlPoints.Num(); i++) { - for (float tension = 0.0f; tension < 1.001f; tension += granularity) { - float x = 0; - float y = 0; - float z = 0; - for (int j = 0; j < 4; j++) { - x += controlPoints[i - (3 - j)]->x * calcSpline(j, tension); - y += controlPoints[i - (3 - j)]->y * calcSpline(j, tension); - z += controlPoints[i - (3 - j)]->z * calcSpline(j, tension); - } - splinePoints.Append(new idVec3_t(x, y, z)); - } - } - dirty = false; - //Com_Printf("Spline build took %f seconds\n", (float)(Sys_Milliseconds() - start) / 1000); -} - - -void idSplineList::draw(bool editMode) { - int i; - vec4_t yellow(1, 1, 0, 1); - - if (controlPoints.Num() == 0) { - return; - } - - if (dirty) { - buildSpline(); - } - - - qglColor3fv(controlColor); - qglPointSize(5); - - qglBegin(GL_POINTS); - for (i = 0; i < controlPoints.Num(); i++) { - qglVertex3fv(*controlPoints[i]); - } - qglEnd(); - - if (editMode) { - for(i = 0; i < controlPoints.Num(); i++) { - glBox(activeColor, *controlPoints[i], 4); - } - } - - //Draw the curve - qglColor3fv(pathColor); - qglBegin(GL_LINE_STRIP); - int count = splinePoints.Num(); - for (i = 0; i < count; i++) { - qglVertex3fv(*splinePoints[i]); - } - qglEnd(); - - if (editMode) { - qglColor3fv(segmentColor); - qglPointSize(3); - qglBegin(GL_POINTS); - for (i = 0; i < count; i++) { - qglVertex3fv(*splinePoints[i]); - } - qglEnd(); - } - if (count > 0) { - //assert(activeSegment >=0 && activeSegment < count); - if (activeSegment >=0 && activeSegment < count) { - glBox(activeColor, *splinePoints[activeSegment], 6); - glBox(yellow, *splinePoints[activeSegment], 8); - } - } - -} - -float idSplineList::totalDistance() { - - if (controlPoints.Num() == 0) { - return 0.0; - } - - if (dirty) { - buildSpline(); - } - - float dist = 0.0; - idVec3_t temp; - int count = splinePoints.Num(); - for(int i = 1; i < count; i++) { - temp = *splinePoints[i-1]; - temp -= *splinePoints[i]; - dist += temp.Length(); - } - return dist; -} - -void idSplineList::initPosition(long bt, long totalTime) { - - if (dirty) { - buildSpline(); - } - - if (splinePoints.Num() == 0) { - return; - } - - baseTime = bt; - time = totalTime; - - // calc distance to travel ( this will soon be broken into time segments ) - splineTime.Clear(); - splineTime.Append(bt); - double dist = totalDistance(); - double distSoFar = 0.0; - idVec3_t temp; - int count = splinePoints.Num(); - //for(int i = 2; i < count - 1; i++) { - for(int i = 1; i < count; i++) { - temp = *splinePoints[i-1]; - temp -= *splinePoints[i]; - distSoFar += temp.Length(); - double percent = distSoFar / dist; - percent *= totalTime; - splineTime.Append(percent + bt); - } - assert(splineTime.Num() == splinePoints.Num()); - activeSegment = 0; -} - - - -float idSplineList::calcSpline(int step, float tension) { - switch(step) { - case 0: return (pow(1 - tension, 3)) / 6; - case 1: return (3 * pow(tension, 3) - 6 * pow(tension, 2) + 4) / 6; - case 2: return (-3 * pow(tension, 3) + 3 * pow(tension, 2) + 3 * tension + 1) / 6; - case 3: return pow(tension, 3) / 6; - } - return 0.0; -} - - - -void idSplineList::updateSelection(const idVec3_t &move) { - if (selected) { - dirty = true; - VectorAdd(*selected, move, *selected); - } -} - - -void idSplineList::setSelectedPoint(idVec3_t *p) { - if (p) { - p->Snap(); - for(int i = 0; i < controlPoints.Num(); i++) { - if (*p == *controlPoints[i]) { - selected = controlPoints[i]; - } - } - } else { - selected = NULL; - } -} - -const idVec3_t *idSplineList::getPosition(long t) { - static idVec3_t interpolatedPos; - //static long lastTime = -1; - - int count = splineTime.Num(); - if (count == 0) { - return &zero; - } - - Com_Printf("Time: %d\n", t); - assert(splineTime.Num() == splinePoints.Num()); - - while (activeSegment < count) { - if (splineTime[activeSegment] >= t) { - if (activeSegment > 0 && activeSegment < count - 1) { - double timeHi = splineTime[activeSegment + 1]; - double timeLo = splineTime[activeSegment - 1]; - double percent = (timeHi - t) / (timeHi - timeLo); - // pick two bounding points - idVec3_t v1 = *splinePoints[activeSegment-1]; - idVec3_t v2 = *splinePoints[activeSegment+1]; - v2 *= (1.0 - percent); - v1 *= percent; - v2 += v1; - interpolatedPos = v2; - return &interpolatedPos; - } - return splinePoints[activeSegment]; - } else { - activeSegment++; - } - } - return splinePoints[count-1]; -} - -void idSplineList::parse(const char *(*text) ) { - const char *token; - //Com_MatchToken( text, "{" ); - do { - token = Com_Parse( text ); - - if ( !token[0] ) { - break; - } - if ( !Q_stricmp (token, "}") ) { - break; - } - - do { - // if token is not a brace, it is a key for a key/value pair - if ( !token[0] || !Q_stricmp (token, "(") || !Q_stricmp(token, "}")) { - break; - } - - Com_UngetToken(); - idStr key = Com_ParseOnLine(text); - const char *token = Com_Parse(text); - if (Q_stricmp(key.c_str(), "granularity") == 0) { - granularity = atof(token); - } else if (Q_stricmp(key.c_str(), "name") == 0) { - name = token; - } - token = Com_Parse(text); - - } while (1); - - if ( !Q_stricmp (token, "}") ) { - break; - } - - Com_UngetToken(); - // read the control point - idVec3_t point; - Com_Parse1DMatrix( text, 3, point ); - addPoint(point.x, point.y, point.z); - } while (1); - - //Com_UngetToken(); - //Com_MatchToken( text, "}" ); - dirty = true; -} - -void idSplineList::write(fileHandle_t file, const char *p) { - idStr s = va("\t\t%s {\n", p); - FS_Write(s.c_str(), s.length(), file); - //s = va("\t\tname %s\n", name.c_str()); - //FS_Write(s.c_str(), s.length(), file); - s = va("\t\t\tgranularity %f\n", granularity); - FS_Write(s.c_str(), s.length(), file); - int count = controlPoints.Num(); - for (int i = 0; i < count; i++) { - s = va("\t\t\t( %f %f %f )\n", controlPoints[i]->x, controlPoints[i]->y, controlPoints[i]->z); - FS_Write(s.c_str(), s.length(), file); - } - s = "\t\t}\n"; - FS_Write(s.c_str(), s.length(), file); -} - - -void idCameraDef::getActiveSegmentInfo(int segment, idVec3_t &origin, idVec3_t &direction, float *fov) { -#if 0 - if (!cameraSpline.validTime()) { - buildCamera(); - } - double d = (double)segment / numSegments(); - getCameraInfo(d * totalTime * 1000, origin, direction, fov); -#endif -/* - if (!cameraSpline.validTime()) { - buildCamera(); - } - origin = *cameraSpline.getSegmentPoint(segment); - - - idVec3_t temp; - - int numTargets = getTargetSpline()->controlPoints.Num(); - int count = cameraSpline.splineTime.Num(); - if (numTargets == 0) { - // follow the path - if (cameraSpline.getActiveSegment() < count - 1) { - temp = *cameraSpline.splinePoints[cameraSpline.getActiveSegment()+1]; - } - } else if (numTargets == 1) { - temp = *getTargetSpline()->controlPoints[0]; - } else { - temp = *getTargetSpline()->getSegmentPoint(segment); - } - - temp -= origin; - temp.Normalize(); - direction = temp; -*/ -} - -bool idCameraDef::getCameraInfo(long time, idVec3_t &origin, idVec3_t &direction, float *fv) { - - - if ((time - startTime) / 1000 > totalTime) { - return false; - } - - - for (int i = 0; i < events.Num(); i++) { - if (time >= startTime + events[i]->getTime() && !events[i]->getTriggered()) { - events[i]->setTriggered(true); - if (events[i]->getType() == idCameraEvent::EVENT_TARGET) { - setActiveTargetByName(events[i]->getParam()); - getActiveTarget()->start(startTime + events[i]->getTime()); - //Com_Printf("Triggered event switch to target: %s\n",events[i]->getParam()); - } else if (events[i]->getType() == idCameraEvent::EVENT_TRIGGER) { - //idEntity *ent = NULL; - //ent = level.FindTarget( ent, events[i]->getParam()); - //if (ent) { - // ent->signal( SIG_TRIGGER ); - // ent->ProcessEvent( &EV_Activate, world ); - //} - } else if (events[i]->getType() == idCameraEvent::EVENT_FOV) { - //*fv = fov = atof(events[i]->getParam()); - } else if (events[i]->getType() == idCameraEvent::EVENT_STOP) { - return false; - } - } - } - - origin = *cameraPosition->getPosition(time); - - *fv = fov.getFOV(time); - - idVec3_t temp = origin; - - int numTargets = targetPositions.Num(); - if (numTargets == 0) { -/* - // follow the path - if (cameraSpline.getActiveSegment() < count - 1) { - temp = *cameraSpline.splinePoints[cameraSpline.getActiveSegment()+1]; - if (temp == origin) { - int index = cameraSpline.getActiveSegment() + 2; - while (temp == origin && index < count - 1) { - temp = *cameraSpline.splinePoints[index++]; - } - } - } -*/ - } else { - temp = *getActiveTarget()->getPosition(time); - } - - temp -= origin; - temp.Normalize(); - direction = temp; - - return true; -} - -bool idCameraDef::waitEvent(int index) { - //for (int i = 0; i < events.Num(); i++) { - // if (events[i]->getSegment() == index && events[i]->getType() == idCameraEvent::EVENT_WAIT) { - // return true; - // } - //} - return false; -} - - -#define NUM_CCELERATION_SEGS 10 -#define CELL_AMT 5 - -void idCameraDef::buildCamera() { - int i; - //int lastSwitch = 0; - idList waits; - idList targets; - - totalTime = baseTime; - cameraPosition->setTime(totalTime * 1000); - // we have a base time layout for the path and the target path - // now we need to layer on any wait or speed changes - for (i = 0; i < events.Num(); i++) { - //idCameraEvent *ev = events[i]; - events[i]->setTriggered(false); - switch (events[i]->getType()) { - case idCameraEvent::EVENT_TARGET : { - targets.Append(i); - break; - } - case idCameraEvent::EVENT_WAIT : { - waits.Append(atof(events[i]->getParam())); - cameraPosition->addVelocity(events[i]->getTime(), atof(events[i]->getParam()) * 1000, 0); - break; - } - case idCameraEvent::EVENT_TARGETWAIT : { - //targetWaits.Append(i); - break; - } - case idCameraEvent::EVENT_SPEED : { -/* - // take the average delay between up to the next five segments - float adjust = atof(events[i]->getParam()); - int index = events[i]->getSegment(); - total = 0; - count = 0; - - // get total amount of time over the remainder of the segment - for (j = index; j < cameraSpline.numSegments() - 1; j++) { - total += cameraSpline.getSegmentTime(j + 1) - cameraSpline.getSegmentTime(j); - count++; - } - - // multiply that by the adjustment - double newTotal = total * adjust; - // what is the difference.. - newTotal -= total; - totalTime += newTotal / 1000; - - // per segment difference - newTotal /= count; - int additive = newTotal; - - // now propogate that difference out to each segment - for (j = index; j < cameraSpline.numSegments(); j++) { - cameraSpline.addSegmentTime(j, additive); - additive += newTotal; - } - break; -*/ - } - default: break; // FIXME: what about other idCameraEvent? - } - } - - - for (i = 0; i < waits.Num(); i++) { - totalTime += waits[i]; - } - - // on a new target switch, we need to take time to this point ( since last target switch ) - // and allocate it across the active target, then reset time to this point - long timeSoFar = 0; - long total = (int)(totalTime * 1000); - for (i = 0; i < targets.Num(); i++) { - long t; - if (i < targets.Num() - 1) { - t = events[targets[i+1]]->getTime(); - } else { - t = total - timeSoFar; - } - // t is how much time to use for this target - setActiveTargetByName(events[targets[i]]->getParam()); - getActiveTarget()->setTime(t); - timeSoFar += t; - } - - -} - -void idCameraDef::startCamera(long t) { - buildCamera(); - cameraPosition->start(t); - //for (int i = 0; i < targetPositions.Num(); i++) { - // targetPositions[i]-> - //} - startTime = t; - cameraRunning = true; -} - - -void idCameraDef::parse(const char *(*text) ) { - - const char *token; - do { - token = Com_Parse( text ); - - if ( !token[0] ) { - break; - } - if ( !Q_stricmp (token, "}") ) { - break; - } - - if (Q_stricmp(token, "time") == 0) { - baseTime = Com_ParseFloat(text); - } - - if (Q_stricmp(token, "camera_fixed") == 0) { - cameraPosition = new idFixedPosition(); - cameraPosition->parse(text); - } - - if (Q_stricmp(token, "camera_interpolated") == 0) { - cameraPosition = new idInterpolatedPosition(); - cameraPosition->parse(text); - } - - if (Q_stricmp(token, "camera_spline") == 0) { - cameraPosition = new idSplinePosition(); - cameraPosition->parse(text); - } - - if (Q_stricmp(token, "target_fixed") == 0) { - idFixedPosition *pos = new idFixedPosition(); - pos->parse(text); - targetPositions.Append(pos); - } - - if (Q_stricmp(token, "target_interpolated") == 0) { - idInterpolatedPosition *pos = new idInterpolatedPosition(); - pos->parse(text); - targetPositions.Append(pos); - } - - if (Q_stricmp(token, "target_spline") == 0) { - idSplinePosition *pos = new idSplinePosition(); - pos->parse(text); - targetPositions.Append(pos); - } - - if (Q_stricmp(token, "fov") == 0) { - fov.parse(text); - } - - if (Q_stricmp(token, "event") == 0) { - idCameraEvent *event = new idCameraEvent(); - event->parse(text); - addEvent(event); - } - - - } while (1); - - Com_UngetToken(); - Com_MatchToken( text, "}" ); - -} - -qboolean idCameraDef::load(const char *filename) { - char *buf; - const char *buf_p; - //int length = - FS_ReadFile( filename, (void **)&buf ); - if ( !buf ) { - return qfalse; - } - - clear(); - Com_BeginParseSession( filename ); - buf_p = buf; - parse(&buf_p); - Com_EndParseSession(); - FS_FreeFile( buf ); - - return qtrue; -} - -void idCameraDef::save(const char *filename) { - fileHandle_t file = FS_FOpenFileWrite(filename); - if (file) { - int i; - idStr s = "cameraPathDef { \n"; - FS_Write(s.c_str(), s.length(), file); - s = va("\ttime %f\n", baseTime); - FS_Write(s.c_str(), s.length(), file); - - cameraPosition->write(file, va("camera_%s",cameraPosition->typeStr())); - - for (i = 0; i < numTargets(); i++) { - targetPositions[i]->write(file, va("target_%s", targetPositions[i]->typeStr())); - } - - for (i = 0; i < events.Num(); i++) { - events[i]->write(file, "event"); - } - - fov.write(file, "fov"); - - s = "}\n"; - FS_Write(s.c_str(), s.length(), file); - } - FS_FCloseFile(file); -} - -int idCameraDef::sortEvents(const void *p1, const void *p2) { - idCameraEvent *ev1 = (idCameraEvent*)(p1); - idCameraEvent *ev2 = (idCameraEvent*)(p2); - - if (ev1->getTime() > ev2->getTime()) { - return -1; - } - if (ev1->getTime() < ev2->getTime()) { - return 1; - } - return 0; -} - -void idCameraDef::addEvent(idCameraEvent *event) { - events.Append(event); - //events.Sort(&sortEvents); - -} -void idCameraDef::addEvent(idCameraEvent::eventType t, const char *param, long time) { - addEvent(new idCameraEvent(t, param, time)); - buildCamera(); -} - - -const char *idCameraEvent::eventStr[] = { - "NA", - "WAIT", - "TARGETWAIT", - "SPEED", - "TARGET", - "SNAPTARGET", - "FOV", - "SCRIPT", - "TRIGGER", - "STOP" -}; - -void idCameraEvent::parse(const char *(*text) ) { - const char *token; - Com_MatchToken( text, "{" ); - do { - token = Com_Parse( text ); - - if ( !token[0] ) { - break; - } - if ( !strcmp (token, "}") ) { - break; - } - - // here we may have to jump over brush epairs ( only used in editor ) - do { - // if token is not a brace, it is a key for a key/value pair - if ( !token[0] || !strcmp (token, "(") || !strcmp(token, "}")) { - break; - } - - Com_UngetToken(); - idStr key = Com_ParseOnLine(text); - const char *token = Com_Parse(text); - if (Q_stricmp(key.c_str(), "type") == 0) { - type = static_cast(atoi(token)); - } else if (Q_stricmp(key.c_str(), "param") == 0) { - paramStr = token; - } else if (Q_stricmp(key.c_str(), "time") == 0) { - time = atoi(token); - } - token = Com_Parse(text); - - } while (1); - - if ( !strcmp (token, "}") ) { - break; - } - - } while (1); - - Com_UngetToken(); - Com_MatchToken( text, "}" ); -} - -void idCameraEvent::write(fileHandle_t file, const char *name) { - idStr s = va("\t%s {\n", name); - FS_Write(s.c_str(), s.length(), file); - s = va("\t\ttype %d\n", static_cast(type)); - FS_Write(s.c_str(), s.length(), file); - s = va("\t\tparam %s\n", paramStr.c_str()); - FS_Write(s.c_str(), s.length(), file); - s = va("\t\ttime %d\n", time); - FS_Write(s.c_str(), s.length(), file); - s = "\t}\n"; - FS_Write(s.c_str(), s.length(), file); -} - - -const char *idCameraPosition::positionStr[] = { - "Fixed", - "Interpolated", - "Spline", -}; - - - -const idVec3_t *idInterpolatedPosition::getPosition(long t) { - static idVec3_t interpolatedPos; - - float velocity = getVelocity(t); - float timePassed = t - lastTime; - lastTime = t; - - // convert to seconds - timePassed /= 1000; - - float distToTravel = timePassed *= velocity; - - idVec3_t temp = startPos; - temp -= endPos; - float distance = temp.Length(); - - distSoFar += distToTravel; - float percent = (float)(distSoFar) / distance; - - if (percent > 1.0) { - percent = 1.0; - } else if (percent < 0.0) { - percent = 0.0; - } - - // the following line does a straigt calc on percentage of time - // float percent = (float)(startTime + time - t) / time; - - idVec3_t v1 = startPos; - idVec3_t v2 = endPos; - v1 *= (1.0 - percent); - v2 *= percent; - v1 += v2; - interpolatedPos = v1; - return &interpolatedPos; -} - - -void idCameraFOV::parse(const char *(*text) ) { - const char *token; - Com_MatchToken( text, "{" ); - do { - token = Com_Parse( text ); - - if ( !token[0] ) { - break; - } - if ( !strcmp (token, "}") ) { - break; - } - - // here we may have to jump over brush epairs ( only used in editor ) - do { - // if token is not a brace, it is a key for a key/value pair - if ( !token[0] || !strcmp (token, "(") || !strcmp(token, "}")) { - break; - } - - Com_UngetToken(); - idStr key = Com_ParseOnLine(text); - const char *token = Com_Parse(text); - if (Q_stricmp(key.c_str(), "fov") == 0) { - fov = atof(token); - } else if (Q_stricmp(key.c_str(), "startFOV") == 0) { - startFOV = atof(token); - } else if (Q_stricmp(key.c_str(), "endFOV") == 0) { - endFOV = atof(token); - } else if (Q_stricmp(key.c_str(), "time") == 0) { - time = atoi(token); - } - token = Com_Parse(text); - - } while (1); - - if ( !strcmp (token, "}") ) { - break; - } - - } while (1); - - Com_UngetToken(); - Com_MatchToken( text, "}" ); -} - -bool idCameraPosition::parseToken(const char *key, const char *(*text)) { - const char *token = Com_Parse(text); - if (Q_stricmp(key, "time") == 0) { - time = atol(token); - return true; - } else if (Q_stricmp(key, "type") == 0) { - type = static_cast(atoi(token)); - return true; - } else if (Q_stricmp(key, "velocity") == 0) { - long t = atol(token); - token = Com_Parse(text); - long d = atol(token); - token = Com_Parse(text); - float s = atof(token); - addVelocity(t, d, s); - return true; - } else if (Q_stricmp(key, "baseVelocity") == 0) { - baseVelocity = atof(token); - return true; - } else if (Q_stricmp(key, "name") == 0) { - name = token; - return true; - } else if (Q_stricmp(key, "time") == 0) { - time = atoi(token); - return true; - } - Com_UngetToken(); - return false; -} - - - -void idFixedPosition::parse(const char *(*text) ) { - const char *token; - Com_MatchToken( text, "{" ); - do { - token = Com_Parse( text ); - - if ( !token[0] ) { - break; - } - if ( !strcmp (token, "}") ) { - break; - } - - // here we may have to jump over brush epairs ( only used in editor ) - do { - // if token is not a brace, it is a key for a key/value pair - if ( !token[0] || !strcmp (token, "(") || !strcmp(token, "}")) { - break; - } - - Com_UngetToken(); - idStr key = Com_ParseOnLine(text); - - const char *token = Com_Parse(text); - if (Q_stricmp(key.c_str(), "pos") == 0) { - Com_UngetToken(); - Com_Parse1DMatrix( text, 3, pos ); - } else { - Com_UngetToken(); - idCameraPosition::parseToken(key.c_str(), text); - } - token = Com_Parse(text); - - } while (1); - - if ( !strcmp (token, "}") ) { - break; - } - - } while (1); - - Com_UngetToken(); - Com_MatchToken( text, "}" ); -} - -void idInterpolatedPosition::parse(const char *(*text) ) { - const char *token; - Com_MatchToken( text, "{" ); - do { - token = Com_Parse( text ); - - if ( !token[0] ) { - break; - } - if ( !strcmp (token, "}") ) { - break; - } - - // here we may have to jump over brush epairs ( only used in editor ) - do { - // if token is not a brace, it is a key for a key/value pair - if ( !token[0] || !strcmp (token, "(") || !strcmp(token, "}")) { - break; - } - - Com_UngetToken(); - idStr key = Com_ParseOnLine(text); - - const char *token = Com_Parse(text); - if (Q_stricmp(key.c_str(), "startPos") == 0) { - Com_UngetToken(); - Com_Parse1DMatrix( text, 3, startPos ); - } else if (Q_stricmp(key.c_str(), "endPos") == 0) { - Com_UngetToken(); - Com_Parse1DMatrix( text, 3, endPos ); - } else { - Com_UngetToken(); - idCameraPosition::parseToken(key.c_str(), text); - } - token = Com_Parse(text); - - } while (1); - - if ( !strcmp (token, "}") ) { - break; - } - - } while (1); - - Com_UngetToken(); - Com_MatchToken( text, "}" ); -} - - -void idSplinePosition::parse(const char *(*text) ) { - const char *token; - Com_MatchToken( text, "{" ); - do { - token = Com_Parse( text ); - - if ( !token[0] ) { - break; - } - if ( !strcmp (token, "}") ) { - break; - } - - // here we may have to jump over brush epairs ( only used in editor ) - do { - // if token is not a brace, it is a key for a key/value pair - if ( !token[0] || !strcmp (token, "(") || !strcmp(token, "}")) { - break; - } - - Com_UngetToken(); - idStr key = Com_ParseOnLine(text); - - const char *token = Com_Parse(text); - if (Q_stricmp(key.c_str(), "target") == 0) { - target.parse(text); - } else { - Com_UngetToken(); - idCameraPosition::parseToken(key.c_str(), text); - } - token = Com_Parse(text); - - } while (1); - - if ( !strcmp (token, "}") ) { - break; - } - - } while (1); - - Com_UngetToken(); - Com_MatchToken( text, "}" ); -} - - - -void idCameraFOV::write(fileHandle_t file, const char *p) { - idStr s = va("\t%s {\n", p); - FS_Write(s.c_str(), s.length(), file); - - s = va("\t\tfov %f\n", fov); - FS_Write(s.c_str(), s.length(), file); - - s = va("\t\tstartFOV %f\n", startFOV); - FS_Write(s.c_str(), s.length(), file); - - s = va("\t\tendFOV %f\n", endFOV); - FS_Write(s.c_str(), s.length(), file); - - s = va("\t\ttime %i\n", time); - FS_Write(s.c_str(), s.length(), file); - - s = "\t}\n"; - FS_Write(s.c_str(), s.length(), file); -} - - -void idCameraPosition::write(fileHandle_t file, const char *p) { - - idStr s = va("\t\ttime %i\n", time); - FS_Write(s.c_str(), s.length(), file); - - s = va("\t\ttype %i\n", static_cast(type)); - FS_Write(s.c_str(), s.length(), file); - - s = va("\t\tname %s\n", name.c_str()); - FS_Write(s.c_str(), s.length(), file); - - s = va("\t\tbaseVelocity %f\n", baseVelocity); - FS_Write(s.c_str(), s.length(), file); - - for (int i = 0; i < velocities.Num(); i++) { - s = va("\t\tvelocity %i %i %f\n", velocities[i]->startTime, velocities[i]->time, velocities[i]->speed); - FS_Write(s.c_str(), s.length(), file); - } - -} - -void idFixedPosition::write(fileHandle_t file, const char *p) { - idStr s = va("\t%s {\n", p); - FS_Write(s.c_str(), s.length(), file); - idCameraPosition::write(file, p); - s = va("\t\tpos ( %f %f %f )\n", pos.x, pos.y, pos.z); - FS_Write(s.c_str(), s.length(), file); - s = "\t}\n"; - FS_Write(s.c_str(), s.length(), file); -} - -void idInterpolatedPosition::write(fileHandle_t file, const char *p) { - idStr s = va("\t%s {\n", p); - FS_Write(s.c_str(), s.length(), file); - idCameraPosition::write(file, p); - s = va("\t\tstartPos ( %f %f %f )\n", startPos.x, startPos.y, startPos.z); - FS_Write(s.c_str(), s.length(), file); - s = va("\t\tendPos ( %f %f %f )\n", endPos.x, endPos.y, endPos.z); - FS_Write(s.c_str(), s.length(), file); - s = "\t}\n"; - FS_Write(s.c_str(), s.length(), file); -} - -void idSplinePosition::write(fileHandle_t file, const char *p) { - idStr s = va("\t%s {\n", p); - FS_Write(s.c_str(), s.length(), file); - idCameraPosition::write(file, p); - target.write(file, "target"); - s = "\t}\n"; - FS_Write(s.c_str(), s.length(), file); -} - -void idCameraDef::addTarget(const char *name, idCameraPosition::positionType type) { - //const char *text = (name == NULL) ? va("target0%d", numTargets()+1) : name; // TTimo: unused - idCameraPosition *pos = newFromType(type); - if (pos) { - pos->setName(name); - targetPositions.Append(pos); - activeTarget = numTargets()-1; - if (activeTarget == 0) { - // first one - addEvent(idCameraEvent::EVENT_TARGET, name, 0); - } - } -} - - - -idCameraDef camera; - -extern "C" { -qboolean loadCamera(const char *name) { - camera.clear(); - return static_cast(camera.load(name)); -} - -qboolean getCameraInfo(int time, float *origin, float*angles) { - idVec3_t dir, org; - org[0] = origin[0]; - org[1] = origin[1]; - org[2] = origin[2]; - float fov = 90; - if (camera.getCameraInfo(time, org, dir, &fov)) { - origin[0] = org[0]; - origin[1] = org[1]; - origin[2] = org[2]; - angles[1] = atan2 (dir[1], dir[0])*180/3.14159; - angles[0] = asin (dir[2])*180/3.14159; - return qtrue; - } - return qfalse; -} - -void startCamera(int time) { - camera.startCamera(time); -} - -} - - diff --git a/CODE-mp/Splines/splines.h b/CODE-mp/Splines/splines.h deleted file mode 100644 index 5454aff..0000000 --- a/CODE-mp/Splines/splines.h +++ /dev/null @@ -1,1061 +0,0 @@ -#ifndef __SPLINES_H -#define __SPLINES_H - -extern "C" { -#ifdef Q3RADIANT -#include "../qgl.h" -#else -#include "../renderer/qgl.h" -#endif -} -#include "util_list.h" -#include "util_str.h" -#include "math_vector.h" - -typedef int fileHandle_t; - -extern void glBox(idVec3_t &color, idVec3_t &point, float size); -extern void glLabeledPoint(idVec3_t &color, idVec3_t &point, float size, const char *label); - -static vec4_t blue(0, 0, 1, 1); -static vec4_t red(1, 0, 0, 1); - -class idPointListInterface { -public: - idPointListInterface() { - selectedPoints.Clear(); - } - virtual ~idPointListInterface() {} - - virtual int numPoints() { - return 0; - } - - virtual void addPoint(const float x, const float y, const float z) {} - virtual void addPoint(const idVec3_t &v) {} - virtual void removePoint(int index) {} - virtual idVec3_t *getPoint(int index) { return NULL; } - - int selectPointByRay(float ox, float oy, float oz, float dx, float dy, float dz, bool single) { - idVec3_t origin(ox, oy, oz); - idVec3_t dir(dx, dy, dz); - return selectPointByRay(origin, dir, single); - } - - int selectPointByRay(const idVec3_t origin, const idVec3_t direction, bool single) { - int i, besti, count; - float d, bestd; - idVec3_t temp, temp2; - - // find the point closest to the ray - besti = -1; - bestd = 8; - count = numPoints(); - - for (i=0; i < count; i++) { - temp = *getPoint(i); - temp2 = temp; - temp -= origin; - d = DotProduct(temp, direction); - __VectorMA (origin, d, direction, temp); - temp2 -= temp; - d = temp2.Length(); - if (d <= bestd) { - bestd = d; - besti = i; - } - } - - if (besti >= 0) { - selectPoint(besti, single); - } - - return besti; - } - - int isPointSelected(int index) { - int count = selectedPoints.Num(); - for (int i = 0; i < count; i++) { - if (selectedPoints[i] == index) { - return i; - } - } - return -1; - } - - int selectPoint(int index, bool single) { - if (index >= 0 && index < numPoints()) { - if (single) { - deselectAll(); - } else { - if (isPointSelected(index) >= 0) { - selectedPoints.Remove(index); - } - } - return selectedPoints.Append(index); - } - return -1; - } - - void selectAll() { - selectedPoints.Clear(); - for (int i = 0; i < numPoints(); i++) { - selectedPoints.Append(i); - } - } - - void deselectAll() { - selectedPoints.Clear(); - } - - int numSelectedPoints(); - - idVec3_t *getSelectedPoint(int index) { - assert(index >= 0 && index < numSelectedPoints()); - return getPoint(selectedPoints[index]); - } - - virtual void updateSelection(float x, float y, float z) { - idVec3_t move(x, y, z); - updateSelection(move); - } - - virtual void updateSelection(const idVec3_t &move) { - int count = selectedPoints.Num(); - for (int i = 0; i < count; i++) { - *getPoint(selectedPoints[i]) += move; - } - } - - void drawSelection() { - int count = selectedPoints.Num(); - for (int i = 0; i < count; i++) { - glBox(red, *getPoint(selectedPoints[i]), 4); - } - } - -protected: - idList selectedPoints; - -}; - - -class idSplineList { - -public: - - idSplineList() { - clear(); - } - - idSplineList(const char *p) { - clear(); - name = p; - }; - - ~idSplineList() { - clear(); - }; - - void clearControl() { - for (int i = 0; i < controlPoints.Num(); i++) { - delete controlPoints[i]; - } - controlPoints.Clear(); - } - - void clearSpline() { - for (int i = 0; i < splinePoints.Num(); i++) { - delete splinePoints[i]; - } - splinePoints.Clear(); - } - - void parse(const char *(*text)); - void write(fileHandle_t file, const char *name); - - void clear() { - clearControl(); - clearSpline(); - splineTime.Clear(); - selected = NULL; - dirty = true; - activeSegment = 0; - granularity = 0.025; - pathColor.set(1.0, 0.5, 0.0); - controlColor.set(0.7, 0.0, 1.0); - segmentColor.set(0.0, 0.0, 1.0); - activeColor.set(1.0, 0.0, 0.0); - } - - void initPosition(long startTime, long totalTime); - const idVec3_t *getPosition(long time); - - - void draw(bool editMode); - void addToRenderer(); - - void setSelectedPoint(idVec3_t *p); - idVec3_t *getSelectedPoint() { - return selected; - } - - void addPoint(const idVec3_t &v) { - controlPoints.Append(new idVec3_t(v)); - dirty = true; - } - - void addPoint(float x, float y, float z) { - controlPoints.Append(new idVec3_t(x, y, z)); - dirty = true; - } - - void updateSelection(const idVec3_t &move); - - void startEdit() { - editMode = true; - } - - void stopEdit() { - editMode = false; - } - - void buildSpline(); - - void setGranularity(float f) { - granularity = f; - } - - float getGranularity() { - return granularity; - } - - int numPoints() { - return controlPoints.Num(); - } - - idVec3_t *getPoint(int index) { - assert(index >= 0 && index < controlPoints.Num()); - return controlPoints[index]; - } - - idVec3_t *getSegmentPoint(int index) { - assert(index >= 0 && index < splinePoints.Num()); - return splinePoints[index]; - } - - - void setSegmentTime(int index, int time) { - assert(index >= 0 && index < splinePoints.Num()); - splineTime[index] = time; - } - - double getSegmentTime(int index) { - assert(index >= 0 && index < splinePoints.Num()); - return splineTime[index]; - } - void addSegmentTime(int index, int time) { - assert(index >= 0 && index < splinePoints.Num()); - splineTime[index] += time; - } - - float totalDistance(); - - static idVec3_t zero; - - int getActiveSegment() { - return activeSegment; - } - - void setActiveSegment(int i) { - //assert(i >= 0 && (splinePoints.Num() > 0 && i < splinePoints.Num())); - activeSegment = i; - } - - int numSegments() { - return splinePoints.Num(); - } - - void setColors(idVec3_t &path, idVec3_t &segment, idVec3_t &control, idVec3_t &active) { - pathColor = path; - segmentColor = segment; - controlColor = control; - activeColor = active; - } - - const char *getName() { - return name.c_str(); - } - - void setName(const char *p) { - name = p; - } - - bool validTime() { - if (dirty) { - buildSpline(); - } - // gcc doesn't allow static casting away from bools - // why? I've no idea... - return (bool)(splineTime.Num() > 0 && splineTime.Num() == splinePoints.Num()); - } - - void setTime(long t) { - time = t; - } - - void setBaseTime(long t) { - baseTime = t; - } - -protected: - idStr name; - float calcSpline(int step, float tension); - idList controlPoints; - idList splinePoints; - idList splineTime; - idVec3_t *selected; - idVec3_t pathColor, segmentColor, controlColor, activeColor; - float granularity; - bool editMode; - bool dirty; - int activeSegment; - long baseTime; - long time; - friend class idCamera; -}; - -// time in milliseconds -// velocity where 1.0 equal rough walking speed -struct idVelocity { - idVelocity(long start, long duration, float s) { - startTime = start; - time = duration; - speed = s; - } - long startTime; - long time; - float speed; -}; - -// can either be a look at or origin position for a camera -// -class idCameraPosition : public idPointListInterface { -public: - - virtual void clear() { - editMode = false; - for (int i = 0; i < velocities.Num(); i++) { - delete velocities[i]; - velocities[i] = NULL; - } - velocities.Clear(); - } - - idCameraPosition(const char *p) { - name = p; - } - - idCameraPosition() { - time = 0; - name = "position"; - } - - idCameraPosition(long t) { - time = t; - } - - virtual ~idCameraPosition() { - clear(); - } - - - // this can be done with RTTI syntax but i like the derived classes setting a type - // makes serialization a bit easier to see - // - enum positionType { - FIXED = 0x00, - INTERPOLATED, - SPLINE, - POSITION_COUNT - }; - - - virtual void start(long t) { - startTime = t; - } - - long getTime() { - return time; - } - - virtual void setTime(long t) { - time = t; - } - - float getVelocity(long t) { - long check = t - startTime; - for (int i = 0; i < velocities.Num(); i++) { - if (check >= velocities[i]->startTime && check <= velocities[i]->startTime + velocities[i]->time) { - return velocities[i]->speed; - } - } - return baseVelocity; - } - - void addVelocity(long start, long duration, float speed) { - velocities.Append(new idVelocity(start, duration, speed)); - } - - virtual const idVec3_t *getPosition(long t) { - assert(true); - return NULL; - } - - virtual void draw(bool editMode) {}; - - virtual void parse(const char *(*text)) {}; - virtual void write(fileHandle_t file, const char *name); - virtual bool parseToken(const char *key, const char *(*text)); - - const char *getName() { - return name.c_str(); - } - - void setName(const char *p) { - name = p; - } - - virtual void startEdit() { - editMode = true; - } - - virtual void stopEdit() { - editMode = false; - } - - virtual void draw() {}; - - const char *typeStr() { - return positionStr[static_cast(type)]; - } - - void calcVelocity(float distance) { - float secs = (float)time / 1000; - baseVelocity = distance / secs; - } - -protected: - static const char* positionStr[POSITION_COUNT]; - long startTime; - long time; - idCameraPosition::positionType type; - idStr name; - bool editMode; - idList velocities; - float baseVelocity; -}; - -class idFixedPosition : public idCameraPosition { -public: - - void init() { - pos.Zero(); - type = idCameraPosition::FIXED; - } - - idFixedPosition() : idCameraPosition() { - init(); - } - - idFixedPosition(idVec3_t p) : idCameraPosition() { - init(); - pos = p; - } - - virtual void addPoint(const idVec3_t &v) { - pos = v; - } - - virtual void addPoint(const float x, const float y, const float z) { - pos.set(x, y, z); - } - - - ~idFixedPosition() { - } - - virtual const idVec3_t *getPosition(long t) { - return &pos; - } - - void parse(const char *(*text)); - void write(fileHandle_t file, const char *name); - - virtual int numPoints() { - return 1; - } - - virtual idVec3_t *getPoint(int index) { - if (index != 0) { - assert(true); - }; - return &pos; - } - - virtual void draw(bool editMode) { - glLabeledPoint(blue, pos, (editMode) ? 5 : 3, "Fixed point"); - } - -protected: - idVec3_t pos; -}; - -class idInterpolatedPosition : public idCameraPosition { -public: - - void init() { - type = idCameraPosition::INTERPOLATED; - first = true; - startPos.Zero(); - endPos.Zero(); - } - - idInterpolatedPosition() : idCameraPosition() { - init(); - } - - idInterpolatedPosition(idVec3_t start, idVec3_t end, long time) : idCameraPosition(time) { - init(); - startPos = start; - endPos = end; - } - - ~idInterpolatedPosition() { - } - - virtual const idVec3_t *getPosition(long t); - - void parse(const char *(*text)); - void write(fileHandle_t file, const char *name); - - virtual int numPoints() { - return 2; - } - - virtual idVec3_t *getPoint(int index) { - assert(index >= 0 && index < 2); - if (index == 0) { - return &startPos; - } - return &endPos; - } - - virtual void addPoint(const float x, const float y, const float z) { - if (first) { - startPos.set(x, y, z); - first = false; - } else { - endPos.set(x, y, z); - first = true; - } - } - - virtual void addPoint(const idVec3_t &v) { - if (first) { - startPos = v; - first = false; - } else { - endPos = v; - first = true; - } - } - - virtual void draw(bool editMode) { - glLabeledPoint(blue, startPos, (editMode) ? 5 : 3, "Start interpolated"); - glLabeledPoint(blue, endPos, (editMode) ? 5 : 3, "End interpolated"); - qglBegin(GL_LINES); - qglVertex3fv(startPos); - qglVertex3fv(endPos); - qglEnd(); - } - - virtual void start(long t) { - idCameraPosition::start(t); - lastTime = startTime; - distSoFar = 0.0; - idVec3_t temp = startPos; - temp -= endPos; - calcVelocity(temp.Length()); - } - -protected: - bool first; - idVec3_t startPos; - idVec3_t endPos; - long lastTime; - float distSoFar; -}; - -class idSplinePosition : public idCameraPosition { -public: - - void init() { - type = idCameraPosition::SPLINE; - } - - idSplinePosition() : idCameraPosition() { - init(); - } - - idSplinePosition(long time) : idCameraPosition(time) { - init(); - } - - ~idSplinePosition() { - } - - virtual void start(long t) { - idCameraPosition::start(t); - target.initPosition(t, time); - calcVelocity(target.totalDistance()); - } - - virtual const idVec3_t *getPosition(long t) { - return target.getPosition(t); - } - - //virtual const idVec3_t *getPosition(long t) const { - - void addControlPoint(idVec3_t &v) { - target.addPoint(v); - } - - void parse(const char *(*text)); - void write(fileHandle_t file, const char *name); - - virtual int numPoints() { - return target.numPoints(); - } - - virtual idVec3_t *getPoint(int index) { - return target.getPoint(index); - } - - virtual void addPoint(const idVec3_t &v) { - target.addPoint(v); - } - - virtual void addPoint(const float x, const float y, const float z) { - target.addPoint(x, y, z); - } - - virtual void draw(bool editMode) { - target.draw(editMode); - } - - virtual void updateSelection(const idVec3_t &move) { - idCameraPosition::updateSelection(move); - target.buildSpline(); - } - -protected: - idSplineList target; -}; - -class idCameraFOV { -public: - - idCameraFOV() { - time = 0; - fov = 90; - } - - idCameraFOV(int v) { - time = 0; - fov = v; - } - - idCameraFOV(int s, int e, long t) { - startFOV = s; - endFOV = e; - time = t; - } - - - ~idCameraFOV(){} - - void setFOV(float f) { - fov = f; - } - - float getFOV(long t) { - if (time) { - assert(startTime); - float percent = t / startTime; - float temp = startFOV - endFOV; - temp *= percent; - fov = startFOV + temp; - } - return fov; - } - - void start(long t) { - startTime = t; - } - - void parse(const char *(*text)); - void write(fileHandle_t file, const char *name); - -protected: - float fov; - float startFOV; - float endFOV; - int startTime; - int time; -}; - - - - -class idCameraEvent { -public: - enum eventType { - EVENT_NA = 0x00, - EVENT_WAIT, - EVENT_TARGETWAIT, - EVENT_SPEED, - EVENT_TARGET, - EVENT_SNAPTARGET, - EVENT_FOV, - EVENT_SCRIPT, - EVENT_TRIGGER, - EVENT_STOP, - EVENT_COUNT - }; - - static const char* eventStr[EVENT_COUNT]; - - idCameraEvent() { - paramStr = ""; - type = EVENT_NA; - time = 0; - } - - idCameraEvent(eventType t, const char *param, long n) { - type = t; - paramStr = param; - time = n; - } - - ~idCameraEvent() {}; - - eventType getType() { - return type; - } - - const char *typeStr() { - return eventStr[static_cast(type)]; - } - - const char *getParam() { - return paramStr.c_str(); - } - - long getTime() { - return time; - } - - void setTime(long n) { - time = n; - } - - void parse(const char *(*text)); - void write(fileHandle_t file, const char *name); - - void setTriggered(bool b) { - triggered = b; - } - - bool getTriggered() { - return triggered; - } - -protected: - eventType type; - idStr paramStr; - long time; - bool triggered; - -}; - -class idCameraDef { -public: - - void clear() { - currentCameraPosition = 0; - cameraRunning = false; - lastDirection.Zero(); - baseTime = 30; - activeTarget = 0; - name = "camera01"; - fov.setFOV(90); - int i; - for (i = 0; i < targetPositions.Num(); i++) { - delete targetPositions[i]; - } - for (i = 0; i < events.Num(); i++) { - delete events[i]; - } - delete cameraPosition; - cameraPosition = NULL; - events.Clear(); - targetPositions.Clear(); - } - - idCameraPosition *startNewCamera(idCameraPosition::positionType type) { - clear(); - if (type == idCameraPosition::SPLINE) { - cameraPosition = new idSplinePosition(); - } else if (type == idCameraPosition::INTERPOLATED) { - cameraPosition = new idInterpolatedPosition(); - } else { - cameraPosition = new idFixedPosition(); - } - return cameraPosition; - } - - idCameraDef() { - clear(); - } - - ~idCameraDef() { - clear(); - } - - void addEvent(idCameraEvent::eventType t, const char *param, long time); - - void addEvent(idCameraEvent *event); - - static int sortEvents(const void *p1, const void *p2); - - int numEvents() { - return events.Num(); - } - - idCameraEvent *getEvent(int index) { - assert(index >= 0 && index < events.Num()); - return events[index]; - } - - void parse(const char *(*text)); - qboolean load(const char *filename); - void save(const char *filename); - - void buildCamera(); - - //idSplineList *getcameraPosition() { - // return &cameraPosition; - //} - - static idCameraPosition *newFromType(idCameraPosition::positionType t) { - switch (t) { - case idCameraPosition::FIXED : return new idFixedPosition(); - case idCameraPosition::INTERPOLATED : return new idInterpolatedPosition(); - case idCameraPosition::SPLINE : return new idSplinePosition(); - default: - break; - }; - return NULL; - } - - void addTarget(const char *name, idCameraPosition::positionType type); - - idCameraPosition *getActiveTarget() { - if (targetPositions.Num() == 0) { - addTarget(NULL, idCameraPosition::FIXED); - } - return targetPositions[activeTarget]; - } - - idCameraPosition *getActiveTarget(int index) { - if (targetPositions.Num() == 0) { - addTarget(NULL, idCameraPosition::FIXED); - return targetPositions[0]; - } - return targetPositions[index]; - } - - int numTargets() { - return targetPositions.Num(); - } - - - void setActiveTargetByName(const char *name) { - for (int i = 0; i < targetPositions.Num(); i++) { - if (Q_stricmp(name, targetPositions[i]->getName()) == 0) { - setActiveTarget(i); - return; - } - } - } - - void setActiveTarget(int index) { - assert(index >= 0 && index < targetPositions.Num()); - activeTarget = index; - } - - void setRunning(bool b) { - cameraRunning = b; - } - - void setBaseTime(float f) { - baseTime = f; - } - - float getBaseTime() { - return baseTime; - } - - float getTotalTime() { - return totalTime; - } - - void startCamera(long t); - void stopCamera() { - cameraRunning = true; - } - void getActiveSegmentInfo(int segment, idVec3_t &origin, idVec3_t &direction, float *fv); - - bool getCameraInfo(long time, idVec3_t &origin, idVec3_t &direction, float *fv); - bool getCameraInfo(long time, float *origin, float *direction, float *fv) { - idVec3_t org, dir; - org[0] = origin[0]; - org[1] = origin[1]; - org[2] = origin[2]; - dir[0] = direction[0]; - dir[1] = direction[1]; - dir[2] = direction[2]; - bool b = getCameraInfo(time, org, dir, fv); - origin[0] = org[0]; - origin[1] = org[1]; - origin[2] = org[2]; - direction[0] = dir[0]; - direction[1] = dir[1]; - direction[2] = dir[2]; - return b; - } - - void draw(bool editMode) { - // gcc doesn't allow casting away from bools - // why? I've no idea... - if (cameraPosition) { - cameraPosition->draw((bool)((editMode || cameraRunning) && cameraEdit)); - int count = targetPositions.Num(); - for (int i = 0; i < count; i++) { - targetPositions[i]->draw((bool)((editMode || cameraRunning) && i == activeTarget && !cameraEdit)); - } - } - } - -/* - int numSegments() { - if (cameraEdit) { - return cameraPosition.numSegments(); - } - return getTargetSpline()->numSegments(); - } - - int getActiveSegment() { - if (cameraEdit) { - return cameraPosition.getActiveSegment(); - } - return getTargetSpline()->getActiveSegment(); - } - - void setActiveSegment(int i) { - if (cameraEdit) { - cameraPosition.setActiveSegment(i); - } else { - getTargetSpline()->setActiveSegment(i); - } - } -*/ - int numPoints() { - if (cameraEdit) { - return cameraPosition->numPoints(); - } - return getActiveTarget()->numPoints(); - } - - const idVec3_t *getPoint(int index) { - if (cameraEdit) { - return cameraPosition->getPoint(index); - } - return getActiveTarget()->getPoint(index); - } - - void stopEdit() { - editMode = false; - if (cameraEdit) { - cameraPosition->stopEdit(); - } else { - getActiveTarget()->stopEdit(); - } - } - - void startEdit(bool camera) { - cameraEdit = camera; - if (camera) { - cameraPosition->startEdit(); - for (int i = 0; i < targetPositions.Num(); i++) { - targetPositions[i]->stopEdit(); - } - } else { - getActiveTarget()->startEdit(); - cameraPosition->stopEdit(); - } - editMode = true; - } - - bool waitEvent(int index); - - const char *getName() { - return name.c_str(); - } - - void setName(const char *p) { - name = p; - } - - idCameraPosition *getPositionObj() { - if (cameraPosition == NULL) { - cameraPosition = new idFixedPosition(); - } - return cameraPosition; - } - -protected: - idStr name; - int currentCameraPosition; - idVec3_t lastDirection; - bool cameraRunning; - idCameraPosition *cameraPosition; - idList targetPositions; - idList events; - idCameraFOV fov; - int activeTarget; - float totalTime; - float baseTime; - long startTime; - - bool cameraEdit; - bool editMode; -}; - -extern bool g_splineMode; - -extern idCameraDef *g_splineList; - - -#endif diff --git a/CODE-mp/Splines/util_list.h b/CODE-mp/Splines/util_list.h deleted file mode 100644 index 90d162b..0000000 --- a/CODE-mp/Splines/util_list.h +++ /dev/null @@ -1,325 +0,0 @@ -#ifndef __UTIL_LIST_H__ -#define __UTIL_LIST_H__ - -#include -#include - -template< class type > -class idList { -private: - int m_num; - int m_size; - int m_granularity; - type *m_list; - -public: - idList( int granularity = 16 ); - ~idList(); - void Clear( void ); - int Num( void ); - void SetNum( int num ); - void SetGranularity( int granularity ); - void Condense( void ); - int Size( void ); - void Resize( int size ); - type operator[]( int index ) const; - type &operator[]( int index ); - int Append( type const & obj ); - int AddUnique( type const & obj ); - type *Find( type const & obj, int *index = NULL ); - bool RemoveIndex( int index ); - bool Remove( type const & obj ); - typedef int cmp_t(const void *, const void *); - void Sort( cmp_t *compare ); -}; - -/* -================ -idList::idList( int ) -================ -*/ -template< class type > -inline idList::idList( int granularity ) { - assert( granularity > 0 ); - - m_list = NULL; - m_granularity = granularity; - Clear(); -} - -/* -================ -idList::~idList -================ -*/ -template< class type > -inline idList::~idList() { - Clear(); -} - -/* -================ -idList::Clear -================ -*/ -template< class type > -inline void idList::Clear( void ) { - if ( m_list ) { - delete[] m_list; - } - - m_list = NULL; - m_num = 0; - m_size = 0; -} - -/* -================ -idList::Num -================ -*/ -template< class type > -inline int idList::Num( void ) { - return m_num; -} - -/* -================ -idList::SetNum -================ -*/ -template< class type > -inline void idList::SetNum( int num ) { - assert( num >= 0 ); - if ( num > m_size ) { - // resize it up to the closest level of granularity - Resize( ( ( num + m_granularity - 1 ) / m_granularity ) * m_granularity ); - } - m_num = num; -} - -/* -================ -idList::SetGranularity -================ -*/ -template< class type > -inline void idList::SetGranularity( int granularity ) { - int newsize; - - assert( granularity > 0 ); - m_granularity = granularity; - - if ( m_list ) { - // resize it to the closest level of granularity - newsize = ( ( m_num + m_granularity - 1 ) / m_granularity ) * m_granularity; - if ( newsize != m_size ) { - Resize( newsize ); - } - } -} - -/* -================ -idList::Condense - -Resizes the array to exactly the number of elements it contains -================ -*/ -template< class type > -inline void idList::Condense( void ) { - if ( m_list ) { - if ( m_num ) { - Resize( m_num ); - } else { - Clear(); - } - } -} - -/* -================ -idList::Size -================ -*/ -template< class type > -inline int idList::Size( void ) { - return m_size; -} - -/* -================ -idList::Resize -================ -*/ -template< class type > -inline void idList::Resize( int size ) { - type *temp; - int i; - - assert( size > 0 ); - - if ( size <= 0 ) { - Clear(); - return; - } - - temp = m_list; - m_size = size; - if ( m_size < m_num ) { - m_num = m_size; - } - - m_list = new type[ m_size ]; - for( i = 0; i < m_num; i++ ) { - m_list[ i ] = temp[ i ]; - } - - if ( temp ) { - delete[] temp; - } -} - -/* -================ -idList::operator[] const -================ -*/ -template< class type > -inline type idList::operator[]( int index ) const { - assert( index >= 0 ); - assert( index < m_num ); - - return m_list[ index ]; -} - -/* -================ -idList::operator[] -================ -*/ -template< class type > -inline type &idList::operator[]( int index ) { - assert( index >= 0 ); - assert( index < m_num ); - - return m_list[ index ]; -} - -/* -================ -idList::Append -================ -*/ -template< class type > -inline int idList::Append( type const & obj ) { - if ( !m_list ) { - Resize( m_granularity ); - } - - if ( m_num == m_size ) { - Resize( m_size + m_granularity ); - } - - m_list[ m_num ] = obj; - m_num++; - - return m_num - 1; -} - -/* -================ -idList::AddUnique -================ -*/ -template< class type > -inline int idList::AddUnique( type const & obj ) { - int index; - - if ( !Find( obj, &index ) ) { - index = Append( obj ); - } - - return index; -} - -/* -================ -idList::Find -================ -*/ -template< class type > -inline type *idList::Find( type const & obj, int *index ) { - int i; - - for( i = 0; i < m_num; i++ ) { - if ( m_list[ i ] == obj ) { - if ( index ) { - *index = i; - } - return &m_list[ i ]; - } - } - - return NULL; -} - -/* -================ -idList::RemoveIndex -================ -*/ -template< class type > -inline bool idList::RemoveIndex( int index ) { - int i; - - if ( !m_list || !m_num ) { - return false; - } - - assert( index >= 0 ); - assert( index < m_num ); - - if ( ( index < 0 ) || ( index >= m_num ) ) { - return false; - } - - m_num--; - for( i = index; i < m_num; i++ ) { - m_list[ i ] = m_list[ i + 1 ]; - } - - return true; -} - -/* -================ -idList::Remove -================ -*/ -template< class type > -inline bool idList::Remove( type const & obj ) { - int index; - - if ( Find( obj, &index ) ) { - return RemoveIndex( index ); - } - - return false; -} - -/* -================ -idList::Sort -================ -*/ -template< class type > -inline void idList::Sort( cmp_t *compare ) { - if ( !m_list ) { - return; - } - - qsort( ( void * )m_list, ( size_t )m_num, sizeof( type ), compare ); -} - -#endif /* !__UTIL_LIST_H__ */ diff --git a/CODE-mp/Splines/util_str.cpp b/CODE-mp/Splines/util_str.cpp deleted file mode 100644 index f68ce6d..0000000 --- a/CODE-mp/Splines/util_str.cpp +++ /dev/null @@ -1,598 +0,0 @@ -//need to rewrite this - -#include "util_str.h" -#include -#include -#include -#include - -#ifdef _WIN32 -#pragma warning(disable : 4244) // 'conversion' conversion from 'type1' to 'type2', possible loss of data -#pragma warning(disable : 4710) // function 'blah' not inlined -#endif - -static const int STR_ALLOC_GRAN = 20; - -char *idStr::tolower - ( - char *s1 - ) - - { - char *s; - - s = s1; - while( *s ) - { - *s = ::tolower( *s ); - s++; - } - - return s1; - } - -char *idStr::toupper - ( - char *s1 - ) - - { - char *s; - - s = s1; - while( *s ) - { - *s = ::toupper( *s ); - s++; - } - - return s1; - } - -int idStr::icmpn - ( - const char *s1, - const char *s2, - int n - ) - - { - int c1; - int c2; - - do - { - c1 = *s1++; - c2 = *s2++; - - if ( !n-- ) - { - // idStrings are equal until end point - return 0; - } - - if ( c1 != c2 ) - { - if ( c1 >= 'a' && c1 <= 'z' ) - { - c1 -= ( 'a' - 'A' ); - } - - if ( c2 >= 'a' && c2 <= 'z' ) - { - c2 -= ( 'a' - 'A' ); - } - - if ( c1 < c2 ) - { - // strings less than - return -1; - } - else if ( c1 > c2 ) - { - // strings greater than - return 1; - } - } - } - while( c1 ); - - // strings are equal - return 0; - } - -int idStr::icmp - ( - const char *s1, - const char *s2 - ) - - { - int c1; - int c2; - - do - { - c1 = *s1++; - c2 = *s2++; - - if ( c1 != c2 ) - { - if ( c1 >= 'a' && c1 <= 'z' ) - { - c1 -= ( 'a' - 'A' ); - } - - if ( c2 >= 'a' && c2 <= 'z' ) - { - c2 -= ( 'a' - 'A' ); - } - - if ( c1 < c2 ) - { - // strings less than - return -1; - } - else if ( c1 > c2 ) - { - // strings greater than - return 1; - } - } - } - while( c1 ); - - // strings are equal - return 0; - } - -int idStr::cmpn - ( - const char *s1, - const char *s2, - int n - ) - - { - int c1; - int c2; - - do - { - c1 = *s1++; - c2 = *s2++; - - if ( !n-- ) - { - // strings are equal until end point - return 0; - } - - if ( c1 < c2 ) - { - // strings less than - return -1; - } - else if ( c1 > c2 ) - { - // strings greater than - return 1; - } - } - while( c1 ); - - // strings are equal - return 0; - } - -int idStr::cmp - ( - const char *s1, - const char *s2 - ) - - { - int c1; - int c2; - - do - { - c1 = *s1++; - c2 = *s2++; - - if ( c1 < c2 ) - { - // strings less than - return -1; - } - else if ( c1 > c2 ) - { - // strings greater than - return 1; - } - } - while( c1 ); - - // strings are equal - return 0; - } - -/* -============ -IsNumeric - -Checks a string to see if it contains only numerical values. -============ -*/ -bool idStr::isNumeric - ( - const char *str - ) - - { - int len; - int i; - bool dot; - - if ( *str == '-' ) - { - str++; - } - - dot = false; - len = strlen( str ); - for( i = 0; i < len; i++ ) - { - if ( !isdigit( str[ i ] ) ) - { - if ( ( str[ i ] == '.' ) && !dot ) - { - dot = true; - continue; - } - return false; - } - } - - return true; - } - -idStr operator+ - ( - const idStr& a, - const float b - ) - - { - char text[ 20 ]; - - idStr result( a ); - - sprintf( text, "%f", b ); - result.append( text ); - - return result; - } - -idStr operator+ - ( - const idStr& a, - const int b - ) - - { - char text[ 20 ]; - - idStr result( a ); - - sprintf( text, "%d", b ); - result.append( text ); - - return result; - } - -idStr operator+ - ( - const idStr& a, - const unsigned b - ) - - { - char text[ 20 ]; - - idStr result( a ); - - sprintf( text, "%u", b ); - result.append( text ); - - return result; - } - -idStr& idStr::operator+= - ( - const float a - ) - - { - char text[ 20 ]; - - sprintf( text, "%f", a ); - append( text ); - - return *this; - } - -idStr& idStr::operator+= - ( - const int a - ) - - { - char text[ 20 ]; - - sprintf( text, "%d", a ); - append( text ); - - return *this; - } - -idStr& idStr::operator+= - ( - const unsigned a - ) - - { - char text[ 20 ]; - - sprintf( text, "%u", a ); - append( text ); - - return *this; - } - -void idStr::CapLength - ( - int newlen - ) - - { - assert ( m_data ); - - if ( length() <= newlen ) - return; - - EnsureDataWritable (); - - m_data->data[newlen] = 0; - m_data->len = newlen; - } - -void idStr::EnsureDataWritable - ( - void - ) - - { - assert ( m_data ); - strdata *olddata; - int len; - - if ( !m_data->refcount ) - return; - - olddata = m_data; - len = length(); - - m_data = new strdata; - - EnsureAlloced ( len + 1, false ); - strncpy ( m_data->data, olddata->data, len+1 ); - m_data->len = len; - - olddata->DelRef (); - } - -void idStr::EnsureAlloced (int amount, bool keepold) { - - if ( !m_data ) { - m_data = new strdata(); - } - - // Now, let's make sure it's writable - EnsureDataWritable (); - - char *newbuffer; - bool wasalloced = ( m_data->alloced != 0 ); - - if ( amount < m_data->alloced ) { - return; - } - - assert ( amount ); - if ( amount == 1 ) { - m_data->alloced = 1; - } else { - int newsize, mod; - mod = amount % STR_ALLOC_GRAN; - if ( !mod ) { - newsize = amount; - } else { - newsize = amount + STR_ALLOC_GRAN - mod; - } - m_data->alloced = newsize; - } - - newbuffer = new char[m_data->alloced]; - if ( wasalloced && keepold ) { - strcpy ( newbuffer, m_data->data ); - } - - if ( m_data->data ) { - delete [] m_data->data; - } - m_data->data = newbuffer; -} - -void idStr::BackSlashesToSlashes - ( - void - ) - - { - int i; - - EnsureDataWritable (); - - for ( i=0; i < m_data->len; i++ ) - { - if ( m_data->data[i] == '\\' ) - m_data->data[i] = '/'; - } - } - -void idStr::snprintf - ( - char *dst, - int size, - const char *fmt, - ... - ) - - { - char buffer[0x10000]; - int len; - va_list argptr; - - va_start (argptr,fmt); - len = vsprintf (buffer,fmt,argptr); - va_end (argptr); - - assert ( len < size ); - - strncpy (dst, buffer, size-1); - } - -#ifdef _WIN32 -#pragma warning(disable : 4189) // local variable is initialized but not referenced -#endif - -/* -================= -TestStringClass - -This is a fairly rigorous test of the idStr class's functionality. -Because of the fairly global and subtle ramifications of a bug occuring -in this class, it should be run after any changes to the class. -Add more tests as functionality is changed. Tests should include -any possible bounds violation and NULL data tests. -================= -*/ -void TestStringClass - ( - void - ) - - { - char ch; // ch == ? - idStr *t; // t == ? - idStr a; // a.len == 0, a.data == "\0" - idStr b; // b.len == 0, b.data == "\0" - idStr c( "test" ); // c.len == 4, c.data == "test\0" - idStr d( c ); // d.len == 4, d.data == "test\0" - idStr e( reinterpret_cast(NULL) ); - // e.len == 0, e.data == "\0" ASSERT! - int i; // i == ? - - i = a.length(); // i == 0 - i = c.length(); // i == 4 - - // TTimo: not used -// const char *s1 = a.c_str(); // s1 == "\0" -// const char *s2 = c.c_str(); // s2 == "test\0" - - t = new idStr(); // t->len == 0, t->data == "\0" - delete t; // t == ? - - b = "test"; // b.len == 4, b.data == "test\0" - t = new idStr( "test" ); // t->len == 4, t->data == "test\0" - delete t; // t == ? - - a = c; // a.len == 4, a.data == "test\0" -// a = ""; - a = NULL; // a.len == 0, a.data == "\0" ASSERT! - a = c + d; // a.len == 8, a.data == "testtest\0" - a = c + "wow"; // a.len == 7, a.data == "testwow\0" - a = c + reinterpret_cast(NULL); - // a.len == 4, a.data == "test\0" ASSERT! - a = "this" + d; // a.len == 8, a.data == "thistest\0" - a = reinterpret_cast(NULL) + d; - // a.len == 4, a.data == "test\0" ASSERT! - a += c; // a.len == 8, a.data == "testtest\0" - a += "wow"; // a.len == 11, a.data == "testtestwow\0" - a += reinterpret_cast(NULL); - // a.len == 11, a.data == "testtestwow\0" ASSERT! - - a = "test"; // a.len == 4, a.data == "test\0" - ch = a[ 0 ]; // ch == 't' - ch = a[ -1 ]; // ch == 0 ASSERT! - ch = a[ 1000 ]; // ch == 0 ASSERT! - ch = a[ 0 ]; // ch == 't' - ch = a[ 1 ]; // ch == 'e' - ch = a[ 2 ]; // ch == 's' - ch = a[ 3 ]; // ch == 't' - ch = a[ 4 ]; // ch == '\0' ASSERT! - ch = a[ 5 ]; // ch == '\0' ASSERT! - - a[ 1 ] = 'b'; // a.len == 4, a.data == "tbst\0" - a[ -1 ] = 'b'; // a.len == 4, a.data == "tbst\0" ASSERT! - a[ 0 ] = '0'; // a.len == 4, a.data == "0bst\0" - a[ 1 ] = '1'; // a.len == 4, a.data == "01st\0" - a[ 2 ] = '2'; // a.len == 4, a.data == "012t\0" - a[ 3 ] = '3'; // a.len == 4, a.data == "0123\0" - a[ 4 ] = '4'; // a.len == 4, a.data == "0123\0" ASSERT! - a[ 5 ] = '5'; // a.len == 4, a.data == "0123\0" ASSERT! - a[ 7 ] = '7'; // a.len == 4, a.data == "0123\0" ASSERT! - - a = "test"; // a.len == 4, a.data == "test\0" - b = "no"; // b.len == 2, b.data == "no\0" - - i = ( a == b ); // i == 0 - i = ( a == c ); // i == 1 - - i = ( a == "blow" ); // i == 0 - i = ( a == "test" ); // i == 1 - i = ( a == NULL ); // i == 0 ASSERT! - - i = ( "test" == b ); // i == 0 - i = ( "test" == a ); // i == 1 - i = ( NULL == a ); // i == 0 ASSERT! - - i = ( a != b ); // i == 1 - i = ( a != c ); // i == 0 - - i = ( a != "blow" ); // i == 1 - i = ( a != "test" ); // i == 0 - i = ( a != NULL ); // i == 1 ASSERT! - - i = ( "test" != b ); // i == 1 - i = ( "test" != a ); // i == 0 - i = ( NULL != a ); // i == 1 ASSERT! - - a = "test"; // a.data == "test" - b = a; // b.data == "test" - - a = "not"; // a.data == "not", b.data == "test" - - a = b; // a.data == b.data == "test" - - a += b; // a.data == "testtest", b.data = "test" - - a = b; - - a[1] = '1'; // a.data = "t1st", b.data = "test" - } - -#ifdef _WIN32 -#pragma warning(default : 4189) // local variable is initialized but not referenced -#pragma warning(disable : 4514) // unreferenced inline function has been removed -#endif diff --git a/CODE-mp/Splines/util_str.h b/CODE-mp/Splines/util_str.h deleted file mode 100644 index 0a6c00f..0000000 --- a/CODE-mp/Splines/util_str.h +++ /dev/null @@ -1,796 +0,0 @@ -//need to rewrite this - -#ifndef __UTIL_STR_H__ -#define __UTIL_STR_H__ - -#include -#include -#include - -#ifdef _WIN32 -#pragma warning(disable : 4710) // function 'blah' not inlined -#endif - -void TestStringClass (); - -class strdata - { - public: - strdata () : len( 0 ), refcount ( 0 ), data ( NULL ), alloced ( 0 ) {} - ~strdata () - { - if ( data ) - delete [] data; - } - - void AddRef () { refcount++; } - bool DelRef () // True if killed - { - refcount--; - if ( refcount < 0 ) - { - delete this; - return true; - } - - return false; - } - - int len; - int refcount; - char *data; - int alloced; - }; - -class idStr { -protected: - strdata *m_data; - void EnsureAlloced ( int, bool keepold = true ); - void EnsureDataWritable (); - -public: - ~idStr(); - idStr(); - idStr( const char *text ); - idStr( const idStr& string ); - idStr( const idStr string, int start, int end ); - idStr( const char ch ); - idStr( const int num ); - idStr( const float num ); - idStr( const unsigned num ); - int length( void ) const; - int allocated( void ) const; - const char * c_str( void ) const; - - void append( const char *text ); - void append( const idStr& text ); - char operator[]( int index ) const; - char& operator[]( int index ); - - void operator=( const idStr& text ); - void operator=( const char *text ); - - friend idStr operator+( const idStr& a, const idStr& b ); - friend idStr operator+( const idStr& a, const char *b ); - friend idStr operator+( const char *a, const idStr& b ); - - friend idStr operator+( const idStr& a, const float b ); - friend idStr operator+( const idStr& a, const int b ); - friend idStr operator+( const idStr& a, const unsigned b ); - friend idStr operator+( const idStr& a, const bool b ); - friend idStr operator+( const idStr& a, const char b ); - - idStr& operator+=( const idStr& a ); - idStr& operator+=( const char *a ); - idStr& operator+=( const float a ); - idStr& operator+=( const char a ); - idStr& operator+=( const int a ); - idStr& operator+=( const unsigned a ); - idStr& operator+=( const bool a ); - - friend bool operator==( const idStr& a, const idStr& b ); - friend bool operator==( const idStr& a, const char *b ); - friend bool operator==( const char *a, const idStr& b ); - - friend bool operator!=( const idStr& a, const idStr& b ); - friend bool operator!=( const idStr& a, const char *b ); - friend bool operator!=( const char *a, const idStr& b ); - - operator const char * () const; - operator const char * (); - - int icmpn( const char *text, int n ) const; - int icmpn( const idStr& text, int n ) const; - int icmp( const char *text ) const; - int icmp( const idStr& text ) const; - int cmpn( const char *text, int n ) const; - int cmpn( const idStr& text, int n ) const; - int cmp( const char *text ) const; - int cmp( const idStr& text ) const; - - void tolower( void ); - void toupper( void ); - - static char *tolower( char *s1 ); - static char *toupper( char *s1 ); - - static int icmpn( const char *s1, const char *s2, int n ); - static int icmp( const char *s1, const char *s2 ); - static int cmpn( const char *s1, const char *s2, int n ); - static int cmp( const char *s1, const char *s2 ); - - static void snprintf ( char *dst, int size, const char *fmt, ... ); - - static bool isNumeric( const char *str ); - bool isNumeric( void ) const; - - void CapLength ( int ); - - void BackSlashesToSlashes (); - -}; - -inline idStr::~idStr() - { - if ( m_data ) - { - m_data->DelRef (); - m_data = NULL; - } - } - -inline idStr::idStr() : m_data ( NULL ) - { - EnsureAlloced ( 1 ); - m_data->data[ 0 ] = 0; - } - -inline idStr::idStr - ( - const char *text - ) : m_data ( NULL ) - - { - int len; - - assert( text ); - - if ( text ) - { - len = strlen( text ); - EnsureAlloced ( len + 1 ); - strcpy( m_data->data, text ); - m_data->len = len; - } - else - { - EnsureAlloced ( 1 ); - m_data->data[ 0 ] = 0; - m_data->len = 0; - } - } - -inline idStr::idStr - ( - const idStr& text - ) : m_data ( NULL ) - - { - m_data = text.m_data; - m_data->AddRef (); - } - -inline idStr::idStr - ( - const idStr text, - int start, - int end - ) : m_data ( NULL ) - - { - int i; - int len; - - if ( end > text.length() ) - { - end = text.length(); - } - - if ( start > text.length() ) - { - start = text.length(); - } - - len = end - start; - if ( len < 0 ) - { - len = 0; - } - - EnsureAlloced ( len + 1 ); - - for( i = 0; i < len; i++ ) - { - m_data->data[ i ] = text[ start + i ]; - } - - m_data->data[ len ] = 0; - m_data->len = len; - } - -inline idStr::idStr - ( - const char ch - ) : m_data ( NULL ) - - { - EnsureAlloced ( 2 ); - - m_data->data[ 0 ] = ch; - m_data->data[ 1 ] = 0; - m_data->len = 1; - } - -inline idStr::idStr - ( - const float num - ) : m_data ( NULL ) - - { - char text[ 32 ]; - int len; - - sprintf( text, "%.3f", num ); - len = strlen( text ); - EnsureAlloced( len + 1 ); - strcpy( m_data->data, text ); - m_data->len = len; - } - -inline idStr::idStr - ( - const int num - ) : m_data ( NULL ) - - { - char text[ 32 ]; - int len; - - sprintf( text, "%d", num ); - len = strlen( text ); - EnsureAlloced( len + 1 ); - strcpy( m_data->data, text ); - m_data->len = len; - } - -inline idStr::idStr - ( - const unsigned num - ) : m_data ( NULL ) - - { - char text[ 32 ]; - int len; - - sprintf( text, "%u", num ); - len = strlen( text ); - EnsureAlloced( len + 1 ); - strcpy( m_data->data, text ); - m_data->len = len; - } - -inline int idStr::length( void ) const - { - return ( m_data != NULL ) ? m_data->len : 0; - } - -inline int idStr::allocated( void ) const - { - return ( m_data != NULL ) ? m_data->alloced + sizeof( *m_data ) : 0; - } - -inline const char *idStr::c_str( void ) const - { - assert( m_data ); - - return m_data->data; - } - -inline void idStr::append - ( - const char *text - ) - - { - int len; - - assert( text ); - - if ( text ) - { - len = length() + strlen( text ); - EnsureAlloced( len + 1 ); - - strcat( m_data->data, text ); - m_data->len = len; - } - } - -inline void idStr::append - ( - const idStr& text - ) - - { - int len; - - len = length() + text.length(); - EnsureAlloced ( len + 1 ); - - strcat ( m_data->data, text.c_str () ); - m_data->len = len; - } - -inline char idStr::operator[]( int index ) const - { - assert ( m_data ); - - if ( !m_data ) - return 0; - - // don't include the '/0' in the test, because technically, it's out of bounds - assert( ( index >= 0 ) && ( index < m_data->len ) ); - - // In release mode, give them a null character - // don't include the '/0' in the test, because technically, it's out of bounds - if ( ( index < 0 ) || ( index >= m_data->len ) ) - { - return 0; - } - - return m_data->data[ index ]; - } - -inline char& idStr::operator[] - ( - int index - ) - - { - // Used for result for invalid indices - static char dummy = 0; - assert ( m_data ); - - // We don't know if they'll write to it or not - // if it's not a const object - EnsureDataWritable (); - - if ( !m_data ) - return dummy; - - // don't include the '/0' in the test, because technically, it's out of bounds - assert( ( index >= 0 ) && ( index < m_data->len ) ); - - // In release mode, let them change a safe variable - // don't include the '/0' in the test, because technically, it's out of bounds - if ( ( index < 0 ) || ( index >= m_data->len ) ) - { - return dummy; - } - - return m_data->data[ index ]; - } - -inline void idStr::operator= - ( - const idStr& text - ) - - { - // adding the reference before deleting our current reference prevents - // us from deleting our string if we are copying from ourself - text.m_data->AddRef(); - m_data->DelRef(); - m_data = text.m_data; - } - -inline void idStr::operator= - ( - const char *text - ) - - { - int len; - - assert( text ); - - if ( !text ) - { - // safe behaviour if NULL - EnsureAlloced ( 1, false ); - m_data->data[0] = 0; - m_data->len = 0; - return; - } - - if ( !m_data ) - { - len = strlen ( text ); - EnsureAlloced( len + 1, false ); - strcpy ( m_data->data, text ); - m_data->len = len; - return; - } - - if ( text == m_data->data ) - return; // Copying same thing. Punt. - - // If we alias and I don't do this, I could corrupt other strings... This - // will get called with EnsureAlloced anyway - EnsureDataWritable (); - - // Now we need to check if we're aliasing.. - if ( text >= m_data->data && text <= m_data->data + m_data->len ) - { - // Great, we're aliasing. We're copying from inside ourselves. - // This means that I don't have to ensure that anything is alloced, - // though I'll assert just in case. - int diff = text - m_data->data; - int i; - - assert ( strlen ( text ) < (unsigned) m_data->len ); - - for ( i = 0; text[i]; i++ ) - { - m_data->data[i] = text[i]; - } - - m_data->data[i] = 0; - - m_data->len -= diff; - - return; - } - - len = strlen( text ); - EnsureAlloced ( len + 1, false ); - strcpy( m_data->data, text ); - m_data->len = len; - } - -inline idStr operator+ - ( - const idStr& a, - const idStr& b - ) - - { - idStr result( a ); - - result.append( b ); - - return result; - } - -inline idStr operator+ - ( - const idStr& a, - const char *b - ) - - { - idStr result( a ); - - result.append( b ); - - return result; - } - -inline idStr operator+ - ( - const char *a, - const idStr& b - ) - - { - idStr result( a ); - - result.append( b ); - - return result; - } - -inline idStr operator+ - ( - const idStr& a, - const bool b - ) - - { - idStr result( a ); - - result.append( b ? "true" : "false" ); - - return result; - } - -inline idStr operator+ - ( - const idStr& a, - const char b - ) - - { - char text[ 2 ]; - - text[ 0 ] = b; - text[ 1 ] = 0; - - return a + text; - } - -inline idStr& idStr::operator+= - ( - const idStr& a - ) - - { - append( a ); - return *this; - } - -inline idStr& idStr::operator+= - ( - const char *a - ) - - { - append( a ); - return *this; - } - -inline idStr& idStr::operator+= - ( - const char a - ) - - { - char text[ 2 ]; - - text[ 0 ] = a; - text[ 1 ] = 0; - append( text ); - - return *this; - } - -inline idStr& idStr::operator+= - ( - const bool a - ) - - { - append( a ? "true" : "false" ); - return *this; - } - -inline bool operator== - ( - const idStr& a, - const idStr& b - ) - - { - return ( !strcmp( a.c_str(), b.c_str() ) ); - } - -inline bool operator== - ( - const idStr& a, - const char *b - ) - - { - assert( b ); - if ( !b ) - { - return false; - } - return ( !strcmp( a.c_str(), b ) ); - } - -inline bool operator== - ( - const char *a, - const idStr& b - ) - - { - assert( a ); - if ( !a ) - { - return false; - } - return ( !strcmp( a, b.c_str() ) ); - } - -inline bool operator!= - ( - const idStr& a, - const idStr& b - ) - - { - return !( a == b ); - } - -inline bool operator!= - ( - const idStr& a, - const char *b - ) - - { - return !( a == b ); - } - -inline bool operator!= - ( - const char *a, - const idStr& b - ) - - { - return !( a == b ); - } - -inline int idStr::icmpn - ( - const char *text, - int n - ) const - - { - assert( m_data ); - assert( text ); - - return idStr::icmpn( m_data->data, text, n ); - } - -inline int idStr::icmpn - ( - const idStr& text, - int n - ) const - - { - assert( m_data ); - assert( text.m_data ); - - return idStr::icmpn( m_data->data, text.m_data->data, n ); - } - -inline int idStr::icmp - ( - const char *text - ) const - - { - assert( m_data ); - assert( text ); - - return idStr::icmp( m_data->data, text ); - } - -inline int idStr::icmp - ( - const idStr& text - ) const - - { - assert( c_str () ); - assert( text.c_str () ); - - return idStr::icmp( c_str () , text.c_str () ); - } - -inline int idStr::cmp - ( - const char *text - ) const - - { - assert( m_data ); - assert( text ); - - return idStr::cmp( m_data->data, text ); - } - -inline int idStr::cmp - ( - const idStr& text - ) const - - { - assert( c_str () ); - assert( text.c_str () ); - - return idStr::cmp( c_str () , text.c_str () ); - } - -inline int idStr::cmpn - ( - const char *text, - int n - ) const - - { - assert( c_str () ); - assert( text ); - - return idStr::cmpn( c_str () , text, n ); - } - -inline int idStr::cmpn - ( - const idStr& text, - int n - ) const - - { - assert( c_str () ); - assert( text.c_str () ); - - return idStr::cmpn( c_str () , text.c_str () , n ); - } - -inline void idStr::tolower - ( - void - ) - - { - assert( m_data ); - - EnsureDataWritable (); - - idStr::tolower( m_data->data ); - } - -inline void idStr::toupper - ( - void - ) - - { - assert( m_data ); - - EnsureDataWritable (); - - idStr::toupper( m_data->data ); - } - -inline bool idStr::isNumeric - ( - void - ) const - - { - assert( m_data ); - return idStr::isNumeric( m_data->data ); - } - -inline idStr::operator const char *() { - return c_str(); -} - -inline idStr::operator const char * - ( - void - ) const - - { - return c_str (); - } - -#endif \ No newline at end of file diff --git a/CODE-mp/Splines/vssver.scc b/CODE-mp/Splines/vssver.scc deleted file mode 100644 index f4e973b..0000000 Binary files a/CODE-mp/Splines/vssver.scc and /dev/null differ diff --git a/CODE-mp/botlib/mssccprj.scc b/CODE-mp/botlib/mssccprj.scc deleted file mode 100644 index 122048b..0000000 --- a/CODE-mp/botlib/mssccprj.scc +++ /dev/null @@ -1,5 +0,0 @@ -SCC = This is a Source Code Control file - -[botlib.dsp] -SCC_Aux_Path = "\\ravend\vss_projects\jk2sof2MP" -SCC_Project_Name = "$/General/code/botlib", ACAAAAAA diff --git a/CODE-mp/botlib/vssver.scc b/CODE-mp/botlib/vssver.scc deleted file mode 100644 index 8ecae85..0000000 Binary files a/CODE-mp/botlib/vssver.scc and /dev/null differ diff --git a/CODE-mp/buildvms.bat b/CODE-mp/buildvms.bat deleted file mode 100644 index 00d402b..0000000 --- a/CODE-mp/buildvms.bat +++ /dev/null @@ -1,8 +0,0 @@ -set include= -cd game -call game -cd ..\cgame -call cgame -cd ..\ui -call ui -cd .. diff --git a/CODE-mp/cgame/JK2_cgame.def b/CODE-mp/cgame/JK2_cgame.def deleted file mode 100644 index 01861ba..0000000 --- a/CODE-mp/cgame/JK2_cgame.def +++ /dev/null @@ -1,3 +0,0 @@ -EXPORTS - vmMain - dllEntry diff --git a/CODE-mp/cgame/cg_draw.c b/CODE-mp/cgame/cg_draw.c index 361b4dc..08d0202 100644 --- a/CODE-mp/cgame/cg_draw.c +++ b/CODE-mp/cgame/cg_draw.c @@ -443,42 +443,17 @@ CG_DrawHead Used for both the status bar and the scoreboard ================ */ -void CG_DrawHead( float x, float y, float w, float h, int clientNum, vec3_t headAngles ) { - clipHandle_t cm; +void CG_DrawHead( float x, float y, float w, float h, int clientNum, vec3_t headAngles ) +{ clientInfo_t *ci; - float len; - vec3_t origin; - vec3_t mins, maxs; ci = &cgs.clientinfo[ clientNum ]; - if ( cg_draw3dIcons.integer ) { - cm = ci->headModel; - if ( !cm ) { - return; - } - - // offset the origin y and z to center the head - trap_R_ModelBounds( cm, mins, maxs ); - - origin[2] = -0.5 * ( mins[2] + maxs[2] ); - origin[1] = 0.5 * ( mins[1] + maxs[1] ); - - // calculate distance so the head nearly fills the box - // assume heads are taller than wide - len = 0.7 * ( maxs[2] - mins[2] ); - origin[0] = len / 0.268; // len / tan( fov/2 ) - - // allow per-model tweaking - VectorAdd( origin, ci->headOffset, origin ); - - CG_Draw3DModel( x, y, w, h, ci->headModel, ci->headSkin, origin, headAngles ); - } else if ( cg_drawIcons.integer ) { - CG_DrawPic( x, y, w, h, ci->modelIcon ); - } + CG_DrawPic( x, y, w, h, ci->modelIcon ); // if they are deferred, draw a cross out - if ( ci->deferred ) { + if ( ci->deferred ) + { CG_DrawPic( x, y, w, h, cgs.media.deferShader ); } } @@ -917,7 +892,20 @@ static void CG_DrawAmmo(centity_t *cent,int x,int y) if ( cent->currentState.weapon == WP_SABER ) { -// value = cent->gent->client->ps.forcePower; + trap_R_SetColor( colorTable[CT_WHITE] ); + // don't need to draw ammo, but we will draw the current saber style in this window + switch ( cg.predictedPlayerState.fd.saberDrawAnimLevel ) + { + case 1://FORCE_LEVEL_1: + CG_DrawPic( x, y, 80, 40, cgs.media.HUDSaberStyle1 ); + break; + case 2://FORCE_LEVEL_2: + CG_DrawPic( x, y, 80, 40, cgs.media.HUDSaberStyle2 ); + break; + case 3://FORCE_LEVEL_3: + CG_DrawPic( x, y, 80, 40, cgs.media.HUDSaberStyle3 ); + break; + } return; } else @@ -1551,48 +1539,109 @@ void CG_DrawTeamBackground( int x, int y, int w, int h, float alpha, int team ) /* ================ -CG_DrawAttacker - +CG_DrawMiniScoreboard ================ */ -static float CG_DrawAttacker( float y ) { - int t; +static float CG_DrawMiniScoreboard ( float y ) +{ + char temp[MAX_QPATH]; + + if ( !cg_drawScores.integer ) + { + return y; + } + + if ( cgs.gametype >= GT_TEAM ) + { + strcpy ( temp, "Red: " ); + Q_strcat ( temp, MAX_QPATH, cgs.scores1==SCORE_NOT_PRESENT?"-":(va("%i",cgs.scores1)) ); + Q_strcat ( temp, MAX_QPATH, " Blue: " ); + Q_strcat ( temp, MAX_QPATH, cgs.scores2==SCORE_NOT_PRESENT?"-":(va("%i",cgs.scores2)) ); + + CG_Text_Paint( 630 - CG_Text_Width ( temp, 0.75f, FONT_SMALL ), y, 0.75f, colorWhite, temp, 0, 0, 0, FONT_SMALL ); + y += 15; + } + else + { + strcpy ( temp, "1st: " ); + Q_strcat ( temp, MAX_QPATH, cgs.scores1==SCORE_NOT_PRESENT?"-":(va("%i",cgs.scores1)) ); + Q_strcat ( temp, MAX_QPATH, " 2nd: " ); + Q_strcat ( temp, MAX_QPATH, cgs.scores2==SCORE_NOT_PRESENT?"-":(va("%i",cgs.scores2)) ); + CG_Text_Paint( 630 - CG_Text_Width ( temp, 0.75f, FONT_SMALL ), y, 0.75f, colorWhite, temp, 0, 0, 0, FONT_SMALL ); + y += 15; + } + + + return y; +} + +/* +================ +CG_DrawEnemyInfo +================ +*/ +static float CG_DrawEnemyInfo ( float y ) +{ float size; - vec3_t angles; - const char *info; - const char *name; int clientNum; + const char *title; + clientInfo_t *ci; - if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) { + if ( !cg_drawEnemyInfo.integer ) + { return y; } - if ( !cg.attackerTime ) { + if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) + { return y; } + + if ( cgs.gametype == GT_JEDIMASTER ) + { + title = "Jedi Master"; + clientNum = cgs.jediMaster; + + if ( clientNum < 0 ) + { + return y; + } + } + else if ( cg.snap->ps.duelInProgress ) + { + title = "Dueling"; + clientNum = cg.snap->ps.duelIndex; + } + else + { + title = "Attacker"; + clientNum = cg.predictedPlayerState.persistant[PERS_ATTACKER]; + + if ( clientNum < 0 || clientNum >= MAX_CLIENTS || clientNum == cg.snap->ps.clientNum ) + { + return y; + } + + if ( cg.time - cg.attackerTime > ATTACKER_HEAD_TIME ) + { + cg.attackerTime = 0; + return y; + } + } - clientNum = cg.predictedPlayerState.persistant[PERS_ATTACKER]; - if ( clientNum < 0 || clientNum >= MAX_CLIENTS || clientNum == cg.snap->ps.clientNum ) { - return y; - } - - t = cg.time - cg.attackerTime; - if ( t > ATTACKER_HEAD_TIME ) { - cg.attackerTime = 0; - return y; - } + ci = &cgs.clientinfo[ clientNum ]; size = ICON_SIZE * 1.25; + y += 5; - angles[PITCH] = 0; - angles[YAW] = 180; - angles[ROLL] = 0; - CG_DrawHead( 640 - size, y, size, size, clientNum, angles ); + CG_DrawPic( 640 - size - 5, y, size, size, ci->modelIcon ); - info = CG_ConfigString( CS_PLAYERS + clientNum ); - name = Info_ValueForKey( info, "n" ); y += size; - CG_DrawBigString( 640 - ( Q_PrintStrlen( name ) * BIGCHAR_WIDTH), y, name, 0.5 ); + + CG_Text_Paint( 630 - CG_Text_Width ( ci->name, 0.75f, FONT_SMALL ), y, 0.75f, colorWhite, ci->name, 0, 0, 0, FONT_SMALL ); + + y += 15; + CG_Text_Paint( 630 - CG_Text_Width ( title, 0.75f, FONT_SMALL ), y, 0.75f, colorWhite, title, 0, 0, 0, FONT_SMALL ); return y + BIGCHAR_HEIGHT + 2; } @@ -1884,10 +1933,10 @@ static void CG_DrawUpperRight( void ) { if ( cg_drawTimer.integer ) { y = CG_DrawTimer( y ); } - if ( cg_drawAttacker.integer ) { - y = CG_DrawAttacker( y ); - } + + y = CG_DrawEnemyInfo ( y ); + y = CG_DrawMiniScoreboard ( y ); } /* @@ -2037,6 +2086,18 @@ static void CG_DrawDisconnect( void ) { const char *s; int w; // bk010215 - FIXME char message[1024]; + if (cg.mMapChange) + { + s = "Server Changing Maps"; + w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; + CG_DrawBigString( 320 - w/2, 100, s, 1.0F); + + s = "Please wait..."; + w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; + CG_DrawBigString( 320 - w/2, 200, s, 1.0F); + return; + } + // draw the phone jack if we are completely past our buffers cmdNum = trap_GetCurrentCmdNumber() - CMD_BACKUP + 1; trap_GetUserCmd( cmdNum, &cmd ); @@ -2634,6 +2695,17 @@ static void CG_DrawRocketLocking( int lockEntNum, int lockTime ) return; } + //We can't check to see in pmove if players are on the same team, so we resort + //to just not drawing the lock if a teammate is the locked on ent + if (cg.snap->ps.rocketLockIndex >= 0 && + cg.snap->ps.rocketLockIndex < MAX_CLIENTS) + { + if (cgs.clientinfo[cg.snap->ps.rocketLockIndex].team == cgs.clientinfo[cg.snap->ps.clientNum].team) + { + return; + } + } + if (cg.snap->ps.rocketLockTime != -1) { lastvalidlockdif = dif; @@ -2865,13 +2937,15 @@ static void CG_DrawCrosshairNames( void ) { if ( !cg_drawCrosshair.integer ) { return; } - if ( !cg_drawCrosshairNames.integer ) { - return; - } // scan the known entities to see if the crosshair is sighted on one CG_ScanForCrosshairEntity(); + if ( !cg_drawCrosshairNames.integer ) { + return; + } + //rww - still do the trace, our dynamic crosshair depends on it + if (cg.crosshairClientNum >= MAX_CLIENTS) { return; @@ -3015,12 +3089,49 @@ static void CG_DrawTeamVote(void) { if ( sec < 0 ) { sec = 0; } - s = va("TEAMVOTE(%i):%s yes:%i no:%i", sec, cgs.teamVoteString[cs_offset], - cgs.teamVoteYes[cs_offset], cgs.teamVoteNo[cs_offset] ); + if (strstr(cgs.teamVoteString[cs_offset], "leader")) + { + int i = 0; + + while (cgs.teamVoteString[cs_offset][i] && cgs.teamVoteString[cs_offset][i] != ' ') + { + i++; + } + + if (cgs.teamVoteString[cs_offset][i] == ' ') + { + int voteIndex = 0; + char voteIndexStr[256]; + + i++; + + while (cgs.teamVoteString[cs_offset][i]) + { + voteIndexStr[voteIndex] = cgs.teamVoteString[cs_offset][i]; + voteIndex++; + i++; + } + voteIndexStr[voteIndex] = 0; + + voteIndex = atoi(voteIndexStr); + + s = va("TEAMVOTE(%i):(Make %s the new team leader) yes:%i no:%i", sec, cgs.clientinfo[voteIndex].name, + cgs.teamVoteYes[cs_offset], cgs.teamVoteNo[cs_offset] ); + } + else + { + s = va("TEAMVOTE(%i):%s yes:%i no:%i", sec, cgs.teamVoteString[cs_offset], + cgs.teamVoteYes[cs_offset], cgs.teamVoteNo[cs_offset] ); + } + } + else + { + s = va("TEAMVOTE(%i):%s yes:%i no:%i", sec, cgs.teamVoteString[cs_offset], + cgs.teamVoteYes[cs_offset], cgs.teamVoteNo[cs_offset] ); + } CG_DrawSmallString( 0, 90, s, 1.0F ); } - static qboolean CG_DrawScoreboard() { return CG_DrawOldScoreboard(); #if 0 diff --git a/CODE-mp/cgame/cg_ents.c b/CODE-mp/cgame/cg_ents.c index abc61e7..332db51 100644 --- a/CODE-mp/cgame/cg_ents.c +++ b/CODE-mp/cgame/cg_ents.c @@ -644,7 +644,7 @@ static void CG_General( centity_t *cent ) { else */ { - anim = &ci->animations[ limb_anim ]; + anim = &bgGlobalAnimations[ limb_anim ]; } } @@ -1140,9 +1140,20 @@ Ghoul2 Insert End } else { //neutral - ent.shaderRGBA[0] = 0; - ent.shaderRGBA[1] = wv*255; - ent.shaderRGBA[2] = wv*255; + if ((s1->modelindex+128) == FP_SABERATTACK || + (s1->modelindex+128) == FP_SABERDEFEND || + (s1->modelindex+128) == FP_SABERTHROW) + { //saber power + ent.shaderRGBA[0] = 0; + ent.shaderRGBA[1] = wv*255; + ent.shaderRGBA[2] = 0; + } + else + { + ent.shaderRGBA[0] = 0; + ent.shaderRGBA[1] = wv*255; + ent.shaderRGBA[2] = wv*255; + } } ent.modelScale[0] = 1.1; @@ -1175,8 +1186,8 @@ Ghoul2 Insert End if (cent->currentState.trickedentindex3 == 1) { //dark - fxSArgs.sAlpha *= 2; - fxSArgs.eAlpha *= 2; + fxSArgs.sAlpha *= 3; + fxSArgs.eAlpha *= 3; fxSArgs.shader = cgs.media.redSaberGlowShader; trap_FX_AddSprite(&fxSArgs); } @@ -1193,12 +1204,24 @@ Ghoul2 Insert End } else { //neutral - fxSArgs.sAlpha *= 0.5; - fxSArgs.eAlpha *= 0.5; - fxSArgs.shader = cgs.media.greenSaberGlowShader; - trap_FX_AddSprite(&fxSArgs); - fxSArgs.shader = cgs.media.blueSaberGlowShader; - trap_FX_AddSprite(&fxSArgs); + if ((s1->modelindex+128) == FP_SABERATTACK || + (s1->modelindex+128) == FP_SABERDEFEND || + (s1->modelindex+128) == FP_SABERTHROW) + { //saber power + fxSArgs.sAlpha *= 1.5; + fxSArgs.eAlpha *= 1.5; + fxSArgs.shader = cgs.media.greenSaberGlowShader; + trap_FX_AddSprite(&fxSArgs); + } + else + { + fxSArgs.sAlpha *= 0.5; + fxSArgs.eAlpha *= 0.5; + fxSArgs.shader = cgs.media.greenSaberGlowShader; + trap_FX_AddSprite(&fxSArgs); + fxSArgs.shader = cgs.media.blueSaberGlowShader; + trap_FX_AddSprite(&fxSArgs); + } } } diff --git a/CODE-mp/cgame/cg_event.c b/CODE-mp/cgame/cg_event.c index 059b5cc..0ea7602 100644 --- a/CODE-mp/cgame/cg_event.c +++ b/CODE-mp/cgame/cg_event.c @@ -322,13 +322,13 @@ clientkilled: message = "KILLED_TELEFRAG"; break; case MOD_CRUSH: - message = "KILLED_FORCETOSS"; + message = "KILLED_GENERIC";//"KILLED_FORCETOSS"; break; case MOD_FALLING: message = "KILLED_FORCETOSS"; break; case MOD_TRIGGER_HURT: - message = "KILLED_FORCETOSS"; + message = "KILLED_GENERIC";//"KILLED_FORCETOSS"; break; default: message = "KILLED_GENERIC"; @@ -439,7 +439,7 @@ static void CG_UseItem( centity_t *cent ) { ci = &cgs.clientinfo[ clientNum ]; ci->medkitUsageTime = cg.time; } - trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.medkitSound ); + trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.medkitSound ); break; } @@ -649,14 +649,14 @@ static void CG_BodyQueueCopy(centity_t *cent, int clientNum, int knownWeapon) trap_G2API_CopySpecificGhoul2Model(g2WeaponInstances[knownWeapon], 0, cent->ghoul2, 1); } - anim = &ci->animations[ cent->currentState.torsoAnim ]; + anim = &bgGlobalAnimations[ cent->currentState.torsoAnim ]; animSpeed = 50.0f / anim->frameLerp; //this will just set us to the last frame of the animation, in theory if (source->isATST) { int aNum = cgs.clientinfo[source->currentState.number].frame+1; - anim = &ci->animations[ BOTH_DEAD1 ]; + anim = &bgGlobalAnimations[ BOTH_DEAD1 ]; animSpeed = 1; flags &= ~BONE_ANIM_OVERRIDE_LOOP; @@ -837,14 +837,14 @@ void DoFall(centity_t *cent, entityState_t *es, int clientNum) { trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.fallSound ); trap_S_StartSound( NULL, cent->currentState.number, CHAN_VOICE, - CG_CustomSound( cent->currentState.number, "*fall1.wav" ) ); + CG_CustomSound( cent->currentState.number, "*land1.wav" ) ); cent->pe.painTime = cg.time; // don't play a pain sound right after this } else if (delta > 44) { trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.fallSound ); trap_S_StartSound( NULL, cent->currentState.number, CHAN_VOICE, - CG_CustomSound( cent->currentState.number, "*fall1.wav" ) ); + CG_CustomSound( cent->currentState.number, "*land1.wav" ) ); cent->pe.painTime = cg.time; // don't play a pain sound right after this } else @@ -1022,23 +1022,6 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) { case EV_JUMP_PAD: DEBUGNAME("EV_JUMP_PAD"); -// CG_Printf( "EV_JUMP_PAD w/effect #%i\n", es->eventParm ); - { - localEntity_t *smoke; - vec3_t up = {0, 0, 1}; - - - smoke = CG_SmokePuff( cent->lerpOrigin, up, - 32, - 1, 1, 1, 0.33f, - 1000, - cg.time, 0, - LEF_PUFF_DONT_SCALE, - cgs.media.smokePuffShader ); - } - - // boing sound at origin, jump sound on player - trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*jump1.wav" ) ); break; case EV_PRIVATE_DUEL: @@ -1142,7 +1125,7 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) { index = cg_entities[es->eventParm].currentState.modelindex; // player predicted - if (index < 1) + if (index < 1 && cg_entities[es->eventParm].currentState.isJediMaster) { //a holocron most likely index = cg_entities[es->eventParm].currentState.trickedentindex4; trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.holocronPickup ); @@ -1444,11 +1427,35 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) { case EV_DISRUPTOR_MAIN_SHOT: DEBUGNAME("EV_DISRUPTOR_MAIN_SHOT"); + if (cent->currentState.eventParm != cg.snap->ps.clientNum || + cg.renderingThirdPerson) + { //h4q3ry + CG_GetClientWeaponMuzzleBoltPoint(cent->currentState.eventParm, cent->currentState.origin2); + } + else + { + if (cg.lastFPFlashPoint[0] ||cg.lastFPFlashPoint[1] || cg.lastFPFlashPoint[2]) + { //get the position of the muzzle flash for the first person weapon model from the last frame + VectorCopy(cg.lastFPFlashPoint, cent->currentState.origin2); + } + } FX_DisruptorMainShot( cent->currentState.origin2, cent->lerpOrigin ); break; case EV_DISRUPTOR_SNIPER_SHOT: DEBUGNAME("EV_DISRUPTOR_SNIPER_SHOT"); + if (cent->currentState.eventParm != cg.snap->ps.clientNum || + cg.renderingThirdPerson) + { //h4q3ry + CG_GetClientWeaponMuzzleBoltPoint(cent->currentState.eventParm, cent->currentState.origin2); + } + else + { + if (cg.lastFPFlashPoint[0] ||cg.lastFPFlashPoint[1] || cg.lastFPFlashPoint[2]) + { //get the position of the muzzle flash for the first person weapon model from the last frame + VectorCopy(cg.lastFPFlashPoint, cent->currentState.origin2); + } + } FX_DisruptorAltShot( cent->currentState.origin2, cent->lerpOrigin, cent->currentState.shouldtarget ); break; @@ -1779,6 +1786,9 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) { case EFFECT_EXPLOSION: eID = trap_FX_RegisterEffect("emplaced/explode.efx"); break; + case EFFECT_EXPLOSION_PAS: + eID = trap_FX_RegisterEffect("turret/explode.efx"); + break; case EFFECT_SPARK_EXPLOSION: eID = trap_FX_RegisterEffect("spark_explosion.efx"); break; @@ -1805,18 +1815,54 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) { if (eID != -1) { - trap_FX_PlayEffectID(eID, es->origin, es->angles); + vec3_t fxDir; + + VectorCopy(es->angles, fxDir); + + if (!fxDir[0] && !fxDir[1] && !fxDir[2]) + { + fxDir[1] = 1; + } + + trap_FX_PlayEffectID(eID, es->origin, fxDir); + } + break; + + case EV_PLAY_EFFECT_ID: + DEBUGNAME("EV_PLAY_EFFECT_ID"); + { + vec3_t fxDir; + + AngleVectors(es->angles, fxDir, 0, 0); + + if (!fxDir[0] && !fxDir[1] && !fxDir[2]) + { + fxDir[1] = 1; + } + + if ( cgs.gameEffects[ es->eventParm ] ) + { + trap_FX_PlayEffectID(cgs.gameEffects[es->eventParm], es->origin, fxDir ); + } + else + { + s = CG_ConfigString( CS_EFFECTS + es->eventParm ); + if (s && s[0]) + { + trap_FX_PlayEffectID(trap_FX_RegisterEffect(s), es->origin, fxDir ); + } + } } break; case EV_MUTE_SOUND: DEBUGNAME("EV_MUTE_SOUND"); - if (cg_entities[es->eventParm].currentState.eFlags & EF_SOUNDTRACKER) + if (cg_entities[es->trickedentindex2].currentState.eFlags & EF_SOUNDTRACKER) { - cg_entities[es->eventParm].currentState.eFlags -= EF_SOUNDTRACKER; + cg_entities[es->trickedentindex2].currentState.eFlags -= EF_SOUNDTRACKER; } - trap_S_MuteSound(es->eventParm, es->trickedentindex); - trap_S_StopLoopingSound(es->eventParm); + trap_S_MuteSound(es->trickedentindex2, es->trickedentindex); + trap_S_StopLoopingSound(es->trickedentindex2); break; case EV_GENERAL_SOUND: diff --git a/CODE-mp/cgame/cg_local.h b/CODE-mp/cgame/cg_local.h index f1b6cba..a48cab4 100644 --- a/CODE-mp/cgame/cg_local.h +++ b/CODE-mp/cgame/cg_local.h @@ -515,8 +515,6 @@ typedef struct { qhandle_t modelIcon; - animation_t animations[MAX_TOTALANIMATIONS]; - qhandle_t bolt_rhand; qhandle_t bolt_lhand; @@ -530,6 +528,9 @@ typedef struct { int saberHitWallSoundDebounceTime; sfxHandle_t sounds[MAX_CUSTOM_SOUNDS]; + + int legsAnim; + int torsoAnim; } clientInfo_t; @@ -649,6 +650,8 @@ typedef struct { float frameInterpolation; // (float)( cg.time - cg.frame->serverTime ) / (cg.nextFrame->serverTime - cg.frame->serverTime) + qboolean mMapChange; + qboolean thisFrameTeleport; qboolean nextFrameTeleport; @@ -840,6 +843,8 @@ typedef struct { float invenSelectTime; float forceSelectTime; + vec3_t lastFPFlashPoint; + /* Ghoul2 Insert Start */ @@ -1159,6 +1164,10 @@ typedef struct { qhandle_t HUDLeftStatic; qhandle_t HUDLeft; + qhandle_t HUDSaberStyle1; + qhandle_t HUDSaberStyle2; + qhandle_t HUDSaberStyle3; + qhandle_t HUDRightFrame; qhandle_t HUDInnerRight; @@ -1301,6 +1310,7 @@ typedef struct { int levelStartTime; int scores1, scores2; // from configstrings + int jediMaster; int redflag, blueflag; // flag status from configstrings int flagStatus; @@ -1311,6 +1321,7 @@ typedef struct { // qhandle_t gameModels[MAX_MODELS]; sfxHandle_t gameSounds[MAX_SOUNDS]; + fxHandle_t gameEffects[MAX_FX]; /* Ghoul2 Insert Start */ @@ -1383,6 +1394,7 @@ extern vmCvar_t cg_drawIcons; extern vmCvar_t cg_drawAmmoWarning; extern vmCvar_t cg_drawCrosshair; extern vmCvar_t cg_drawCrosshairNames; +extern vmCvar_t cg_drawScores; extern vmCvar_t cg_dynamicCrosshair; extern vmCvar_t cg_drawRewards; extern vmCvar_t cg_drawTeamOverlay; @@ -1441,7 +1453,7 @@ extern vmCvar_t cg_thirdPersonHorzOffset; extern vmCvar_t cg_stereoSeparation; extern vmCvar_t cg_lagometer; -extern vmCvar_t cg_drawAttacker; +extern vmCvar_t cg_drawEnemyInfo; extern vmCvar_t cg_synchronousClients; extern vmCvar_t cg_teamChatTime; extern vmCvar_t cg_teamChatHeight; @@ -1685,6 +1697,8 @@ void TurretClientRun(centity_t *ent); // // cg_weapons.c // +void CG_GetClientWeaponMuzzleBoltPoint(int clIndex, vec3_t to); + void CG_NextWeapon_f( void ); void CG_PrevWeapon_f( void ); void CG_Weapon_f( void ); @@ -2175,10 +2189,3 @@ extern void *g2WeaponInstances[MAX_WEAPONS]; /* Ghoul2 Insert End */ - -enum { - FONT_NONE, - FONT_SMALL=1, - FONT_MEDIUM, - FONT_LARGE -}; diff --git a/CODE-mp/cgame/cg_main.c b/CODE-mp/cgame/cg_main.c index 26c4798..0e2d745 100644 --- a/CODE-mp/cgame/cg_main.c +++ b/CODE-mp/cgame/cg_main.c @@ -235,6 +235,13 @@ int vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int a C_ImpactMark(); return 0; + case CG_MAP_CHANGE: + // this trap map be called more than once for a given map change, as the + // server is going to attempt to send out multiple broadcasts in hopes that + // the client will receive one of them + cg.mMapChange = qtrue; + return 0; + default: CG_Error( "vmMain: unknown command %i", command ); break; @@ -369,6 +376,7 @@ vmCvar_t cg_drawCrosshair; vmCvar_t cg_drawCrosshairNames; vmCvar_t cg_dynamicCrosshair; vmCvar_t cg_drawRewards; +vmCvar_t cg_drawScores; vmCvar_t cg_crosshairSize; vmCvar_t cg_crosshairX; vmCvar_t cg_crosshairY; @@ -424,7 +432,7 @@ vmCvar_t cg_thirdPersonHorzOffset; vmCvar_t cg_stereoSeparation; vmCvar_t cg_lagometer; -vmCvar_t cg_drawAttacker; +vmCvar_t cg_drawEnemyInfo; vmCvar_t cg_synchronousClients; vmCvar_t cg_teamChatTime; vmCvar_t cg_teamChatHeight; @@ -502,9 +510,10 @@ static cvarTable_t cvarTable[] = { // bk001129 { &cg_draw3dIcons, "cg_draw3dIcons", "1", CVAR_ARCHIVE }, { &cg_drawIcons, "cg_drawIcons", "1", CVAR_ARCHIVE }, { &cg_drawAmmoWarning, "cg_drawAmmoWarning", "0", CVAR_ARCHIVE }, - { &cg_drawAttacker, "cg_drawAttacker", "1", CVAR_ARCHIVE }, + { &cg_drawEnemyInfo, "cg_drawEnemyInfo", "1", CVAR_ARCHIVE }, { &cg_drawCrosshair, "cg_drawCrosshair", "1", CVAR_ARCHIVE }, { &cg_drawCrosshairNames, "cg_drawCrosshairNames", "1", CVAR_ARCHIVE }, + { &cg_drawScores, "cg_drawScores", "1", CVAR_ARCHIVE }, { &cg_dynamicCrosshair, "cg_dynamicCrosshair", "1", CVAR_ARCHIVE }, { &cg_drawRewards, "cg_drawRewards", "1", CVAR_ARCHIVE }, { &cg_crosshairSize, "cg_crosshairSize", "24", CVAR_ARCHIVE }, @@ -549,14 +558,14 @@ static cvarTable_t cvarTable[] = { // bk001129 { &cg_dismember, "cg_dismember", "0", 0 }, { &cg_thirdPerson, "cg_thirdPerson", "0", 0 }, - { &cg_thirdPersonRange, "cg_thirdPersonRange", "80", 0 }, - { &cg_thirdPersonAngle, "cg_thirdPersonAngle", "0", 0 }, - { &cg_thirdPersonPitchOffset, "cg_thirdPersonPitchOffset", "0", 0 }, - { &cg_thirdPersonVertOffset, "cg_thirdPersonVertOffset", "16", 0}, - { &cg_thirdPersonCameraDamp, "cg_thirdPersonCameraDamp", "0.3", 0}, - { &cg_thirdPersonTargetDamp, "cg_thirdPersonTargetDamp", "0.5", 0}, + { &cg_thirdPersonRange, "cg_thirdPersonRange", "80", CVAR_CHEAT }, + { &cg_thirdPersonAngle, "cg_thirdPersonAngle", "0", CVAR_CHEAT }, + { &cg_thirdPersonPitchOffset, "cg_thirdPersonPitchOffset", "0", CVAR_CHEAT }, + { &cg_thirdPersonVertOffset, "cg_thirdPersonVertOffset", "16", CVAR_CHEAT }, + { &cg_thirdPersonCameraDamp, "cg_thirdPersonCameraDamp", "0.3", CVAR_CHEAT }, + { &cg_thirdPersonTargetDamp, "cg_thirdPersonTargetDamp", "0.5", CVAR_CHEAT }, - { &cg_thirdPersonHorzOffset, "cg_thirdPersonHorzOffset", "0", 0}, + { &cg_thirdPersonHorzOffset, "cg_thirdPersonHorzOffset", "0", CVAR_CHEAT }, { &cg_thirdPersonAlpha, "cg_thirdPersonAlpha", "1.0", CVAR_CHEAT }, { &cg_teamChatTime, "cg_teamChatTime", "3000", CVAR_ARCHIVE }, @@ -923,10 +932,12 @@ static void CG_RegisterSounds( void ) { trap_S_RegisterSound(va("sound/weapons/saber/bounce%i.wav", i)); } - trap_S_RegisterSound( "sound/weapons/saber/saberhum.wav" ); + trap_S_RegisterSound( "sound/weapons/saber/saberhum1.wav" ); trap_S_RegisterSound( "sound/weapons/saber/saberon.wav" ); trap_S_RegisterSound( "sound/weapons/saber/saberoffquick.wav" ); - trap_S_RegisterSound( "sound/weapons/saber/saberhitwall.wav" ); + trap_S_RegisterSound( "sound/weapons/saber/saberhitwall1" ); + trap_S_RegisterSound( "sound/weapons/saber/saberhitwall2" ); + trap_S_RegisterSound( "sound/weapons/saber/saberhitwall3" ); trap_S_RegisterSound("sound/weapons/saber/saberhit.wav"); trap_S_RegisterSound("sound/weapons/force/heal.wav"); @@ -1073,7 +1084,7 @@ static void CG_RegisterSounds( void ) { cgs.media.zoomEnd = trap_S_RegisterSound( "sound/interface/zoomend.wav" ); for (i=0 ; i<4 ; i++) { - Com_sprintf (name, sizeof(name), "sound/player/footsteps/step%i.wav", i+1); + Com_sprintf (name, sizeof(name), "sound/player/footsteps/boot%i.wav", i+1); cgs.media.footsteps[FOOTSTEP_NORMAL][i] = trap_S_RegisterSound (name); Com_sprintf (name, sizeof(name), "sound/player/footsteps/splash%i.wav", i+1); @@ -1083,7 +1094,7 @@ static void CG_RegisterSounds( void ) { cgs.media.footsteps[FOOTSTEP_METAL][i] = trap_S_RegisterSound (name); // should these always be registered?? - Com_sprintf (name, sizeof(name), "sound/player/footsteps/boot%i.wav", i+1); + Com_sprintf (name, sizeof(name), "sound/player/footsteps/step%i.wav", i+1); trap_S_RegisterSound (name); } @@ -1107,6 +1118,17 @@ static void CG_RegisterSounds( void ) { cgs.gameSounds[i] = trap_S_RegisterSound( soundName ); } + for ( i = 1 ; i < MAX_FX ; i++ ) { + soundName = CG_ConfigString( CS_EFFECTS+i ); + if ( !soundName[0] ) { + break; + } + if ( soundName[0] == '*' ) { + continue; // custom sound + } + cgs.gameEffects[i] = trap_FX_RegisterEffect( soundName ); + } + cg.loadLCARSStage = 2; // FIXME: only needed with item @@ -1254,10 +1276,8 @@ static void CG_RegisterGraphics( void ) { trap_FX_RegisterEffect("emplaced/dead_smoke.efx"); trap_FX_RegisterEffect("emplaced/explode.efx"); - if (cg_buildScript.integer) - { - trap_FX_RegisterEffect("emplaced/explode.efx"); - } + trap_FX_RegisterEffect("turret/explode.efx"); + trap_FX_RegisterEffect("spark_explosion.efx"); trap_FX_RegisterEffect("effects/turret/muzzle_flash.efx"); @@ -2328,6 +2348,7 @@ Ghoul2 Insert End cgs.media.loadBarLEDSurround= trap_R_RegisterShaderNoMip( "gfx/hud/mp_levelload" ); //rww - precache HUD weapon icons here + //actually, these should be stored in the icon field of each item def cgs.media.weaponIcons[WP_STUN_BATON] = trap_R_RegisterShaderNoMip("gfx/hud/w_icon_stunbaton"); cgs.media.weaponIcons_NA[WP_STUN_BATON] = trap_R_RegisterShaderNoMip("gfx/hud/w_icon_stunbaton_na"); @@ -2417,6 +2438,10 @@ Ghoul2 Insert End cgs.media.HUDLeftStatic = cgs.media.HUDLeftFrame;//trap_R_RegisterShaderNoMip( "gfx/hud/static_test" ); cgs.media.HUDLeft = cgs.media.HUDInnerLeft;//trap_R_RegisterShaderNoMip( "gfx/hud/hudleft" ); + cgs.media.HUDSaberStyle1 = trap_R_RegisterShader( "gfx/hud/saber_styles1" ); + cgs.media.HUDSaberStyle2 = trap_R_RegisterShader( "gfx/hud/saber_styles2" ); + cgs.media.HUDSaberStyle3 = trap_R_RegisterShader( "gfx/hud/saber_styles3" ); + cgs.media.HUDRightFrame = trap_R_RegisterShaderNoMip("gfx/hud/hudrightframe"); cgs.media.HUDInnerRight = trap_R_RegisterShaderNoMip( "gfx/hud/hudright_innerframe" ); diff --git a/CODE-mp/cgame/cg_players.c b/CODE-mp/cgame/cg_players.c index eb70991..ca43789 100644 --- a/CODE-mp/cgame/cg_players.c +++ b/CODE-mp/cgame/cg_players.c @@ -4,7 +4,6 @@ #include "cg_local.h" #include "..\ghoul2\g2.h" - extern stringID_table_t animTable [MAX_ANIMATIONS+1]; char *cg_customSoundNames[MAX_CUSTOM_SOUNDS] = { @@ -21,7 +20,7 @@ char *cg_customSoundNames[MAX_CUSTOM_SOUNDS] = { "*choke2.wav", "*choke3.wav", "*gasp.wav", - "*fall1.wav", + "*land1.wav", "*falling1.wav", "*taunt.wav" }; @@ -288,6 +287,10 @@ static qboolean CG_RegisterClientSkin( clientInfo_t *ci, const char *teamName, c if ( !ci->legsSkin || !ci->torsoSkin || !ci->headSkin ) { return qfalse; } + + // Model icon for drawing the portrait on screen + ci->modelIcon = trap_R_RegisterShaderNoMip ( va ( "models/players/%s/icon_%s", modelName, skinName ) ); + return qtrue; } @@ -405,66 +408,67 @@ retryModel: GLAName[0] = 0; - //get the location of the animation.cfg - //GLAName = trap_G2API_GetGLAName( ci->ghoul2Model, 0); - trap_G2API_GetGLAName( ci->ghoul2Model, 0, GLAName); - if (GLAName[0] == 0/*GLAName == NULL*/) + if (!BGPAFtextLoaded) { - if (!BG_ParseAnimationFile("models/players/_humanoid/animation.cfg", ci->animations)) + trap_G2API_GetGLAName( ci->ghoul2Model, 0, GLAName); + if (GLAName[0] == 0/*GLAName == NULL*/) { - Com_Printf( "Failed to load animation file %s\n", afilename ); + if (!BG_ParseAnimationFile("models/players/_humanoid/animation.cfg")) + { + Com_Printf( "Failed to load animation file %s\n", afilename ); + return qfalse; + } + return qtrue; + } + Q_strncpyz( afilename, GLAName, sizeof( afilename )); + slash = Q_strrchr( afilename, '/' ); + if ( slash ) + { + strcpy(slash, "/animation.cfg"); + } // Now afilename holds just the path to the animation.cfg + else + { // Didn't find any slashes, this is a raw filename right in base (whish isn't a good thing) return qfalse; } - return qtrue; - } - Q_strncpyz( afilename, GLAName, sizeof( afilename )); - slash = Q_strrchr( afilename, '/' ); - if ( slash ) - { - strcpy(slash, "/animation.cfg"); - } // Now afilename holds just the path to the animation.cfg - else - { // Didn't find any slashes, this is a raw filename right in base (whish isn't a good thing) - return qfalse; - } -/* - // Try to load the animation.cfg for this model then. - if ( !BG_ParseAnimationFile( afilename, ci->animations ) ) - { // The GLA's animations failed - if (!BG_ParseAnimationFile("models/players/_humanoid/animation.cfg", ci->animations)) - { - Com_Printf( "Failed to load animation file %s\n", afilename ); - return qfalse; - } - } -*/ -//rww - For now, we'll just ignore what animation file it wants. In theory all multiplayer-supported models -//should want _humanoid/animation.cfg, so if it doesn't want that then throw it away - if (Q_stricmp(afilename, "models/players/_humanoid/animation.cfg")) - { - Com_Printf( "Model does not use supported animation config.\n"); - return qfalse; - } - else if (!BG_ParseAnimationFile("models/players/_humanoid/animation.cfg", ci->animations)) - { - Com_Printf( "Failed to load animation file models/players/_humanoid/animation.cfg\n" ); - return qfalse; - } - else if (!retriedAlready) - { - int i; - for(i = 0; i < MAX_ANIMATIONS; i++) + /* + // Try to load the animation.cfg for this model then. + if ( !BG_ParseAnimationFile( afilename, ci->animations ) ) + { // The GLA's animations failed + if (!BG_ParseAnimationFile("models/players/_humanoid/animation.cfg", ci->animations)) + { + Com_Printf( "Failed to load animation file %s\n", afilename ); + return qfalse; + } + } + */ + //rww - For now, we'll just ignore what animation file it wants. In theory all multiplayer-supported models + //should want _humanoid/animation.cfg, so if it doesn't want that then throw it away + if (Q_stricmp(afilename, "models/players/_humanoid/animation.cfg")) { - if (!ci->animations[i].firstFrame && !ci->animations[i].numFrames && CG_NeedAnimSequence(i)) - { //using default for this animation so it obviously never got filled in. - //if it's a sequence that we need, this model must be an unsupported one. - badModel = qtrue; - goto retryModel; + Com_Printf( "Model does not use supported animation config.\n"); + return qfalse; + } + else if (!BG_ParseAnimationFile("models/players/_humanoid/animation.cfg")) + { + Com_Printf( "Failed to load animation file models/players/_humanoid/animation.cfg\n" ); + return qfalse; + } + else if (!retriedAlready) + { + int i; + + for(i = 0; i < MAX_ANIMATIONS; i++) + { + if (!bgGlobalAnimations[i].firstFrame && !bgGlobalAnimations[i].numFrames && CG_NeedAnimSequence(i)) + { //using default for this animation so it obviously never got filled in. + //if it's a sequence that we need, this model must be an unsupported one. + badModel = qtrue; + goto retryModel; + } } } } - // NOTE - ensure this sequence of bolt and bone accessing are always the same because the client expects them in a certain order if (clientNum != -1 && cg_entities[clientNum].currentState.teamowner && !cg_entities[clientNum].isATST) { @@ -523,7 +527,7 @@ retryModel: { animation_t *anim; - anim = &ci->animations[ (cg_entities[clientNum].currentState.legsAnim & ~ANIM_TOGGLEBIT) ]; + anim = &bgGlobalAnimations[ (cg_entities[clientNum].currentState.legsAnim & ~ANIM_TOGGLEBIT) ]; if (anim) { @@ -542,7 +546,7 @@ retryModel: cg_entities[clientNum].currentState.legsAnim = 0; } - anim = &ci->animations[ (cg_entities[clientNum].currentState.torsoAnim & ~ANIM_TOGGLEBIT) ]; + anim = &bgGlobalAnimations[ (cg_entities[clientNum].currentState.torsoAnim & ~ANIM_TOGGLEBIT) ]; if (anim) { @@ -575,6 +579,9 @@ retryModel: Com_sprintf(ci->teamName, sizeof(ci->teamName), teamName); + // Model icon for drawing the portrait on screen + ci->modelIcon = trap_R_RegisterShaderNoMip ( va ( "models/players/%s/icon_%s", modelName, skinName ) ); + return qtrue; } @@ -848,7 +855,6 @@ static void CG_CopyClientInfoModel( clientInfo_t *from, clientInfo_t *to ) { // to->ATST = from->ATST; - memcpy( to->animations, from->animations, sizeof( to->animations ) ); memcpy( to->sounds, from->sounds, sizeof( to->sounds ) ); } @@ -1192,7 +1198,7 @@ void CG_NewClientInfo( int clientNum, qboolean entitiesInitialized ) { animation_t *anim; // First check if we have a ghoul2 model on the client entity. - anim = &ci->animations[ (cg_entities[clientNum].currentState.legsAnim & ~ANIM_TOGGLEBIT) ]; + anim = &bgGlobalAnimations[ (cg_entities[clientNum].currentState.legsAnim & ~ANIM_TOGGLEBIT) ]; if (anim) { @@ -1211,7 +1217,7 @@ void CG_NewClientInfo( int clientNum, qboolean entitiesInitialized ) { cg_entities[clientNum].currentState.legsAnim = 0; } - anim = &ci->animations[ (cg_entities[clientNum].currentState.torsoAnim & ~ANIM_TOGGLEBIT) ]; + anim = &bgGlobalAnimations[ (cg_entities[clientNum].currentState.torsoAnim & ~ANIM_TOGGLEBIT) ]; if (anim) { @@ -1494,7 +1500,7 @@ static void CG_SetLerpFrameAnimation( centity_t *cent, clientInfo_t *ci, lerpFra CG_Error( "Bad animation number: %i", newAnimation ); } - anim = &ci->animations[ newAnimation ]; + anim = &bgGlobalAnimations[ newAnimation ]; lf->animation = anim; lf->animationTime = lf->frameTime + anim->initialLerp; @@ -1576,10 +1582,13 @@ static void CG_SetLerpFrameAnimation( centity_t *cent, clientInfo_t *ci, lerpFra if (torsoOnly) { trap_G2API_SetBoneAnim(cent->ghoul2, 0, "upper_lumbar", anim->firstFrame, anim->firstFrame + anim->numFrames, flags, animSpeed, lf->frameTime, -1, 150); + cgs.clientinfo[cent->currentState.number].torsoAnim = newAnimation; } else { trap_G2API_SetBoneAnim(cent->ghoul2, 0, "model_root", anim->firstFrame, anim->firstFrame + anim->numFrames, flags, animSpeed, lf->frameTime, -1, 150); + cgs.clientinfo[cent->currentState.number].torsoAnim = newAnimation; + cgs.clientinfo[cent->currentState.number].legsAnim = newAnimation; } if ((cent->currentState.torsoAnim&~ANIM_TOGGLEBIT) == newAnimation && @@ -1669,7 +1678,21 @@ int CG_InWalkingAnim(int animNum) { if (anim == BOTH_RUN1) { - return 12; + return 18;//12; + } + else + { + //return 9; + return 18; + } + } + + if (anim >= BOTH_WALKBACK1 && + anim <= BOTH_RUNBACK2) + { + if (anim == BOTH_WALKBACK1) + { + return 18; } else { @@ -1677,16 +1700,17 @@ int CG_InWalkingAnim(int animNum) } } - if (anim >= BOTH_WALKBACK1 && - anim <= BOTH_RUNBACK2) - { - return 9; - } - if (anim >= LEGS_WALKBACK1 && anim <= LEGS_RUNBACK2) { - return 9; + if (anim == LEGS_WALKBACK1) + { + return 18; + } + else + { + return 9; + } } return qfalse; @@ -2373,6 +2397,16 @@ void CG_G2ClientSpineAngles( centity_t *cent, vec3_t viewAngles, const vec3_t an !BG_SaberInSpecialAttack(cent->currentState.torsoAnim) && !BG_SaberInSpecialAttack(cent->currentState.legsAnim) && + !BG_FlippingAnim( cgs.clientinfo[cent->currentState.number].legsAnim ) && + !BG_SpinningSaberAnim( cgs.clientinfo[cent->currentState.number].legsAnim ) && + !BG_SpinningSaberAnim( cgs.clientinfo[cent->currentState.number].torsoAnim ) && + !BG_InSpecialJump( cgs.clientinfo[cent->currentState.number].legsAnim ) && + !BG_InSpecialJump( cgs.clientinfo[cent->currentState.number].torsoAnim ) && + !BG_InDeathAnim(cgs.clientinfo[cent->currentState.number].legsAnim) && + !BG_InDeathAnim(cgs.clientinfo[cent->currentState.number].torsoAnim) && + !BG_SaberInSpecialAttack(cgs.clientinfo[cent->currentState.number].torsoAnim) && + !BG_SaberInSpecialAttack(cgs.clientinfo[cent->currentState.number].legsAnim) && + /* !BG_FlippingAnim( cent->rootBone ) && !BG_SpinningSaberAnim( cent->rootBone ) && @@ -2399,17 +2433,26 @@ void CG_G2ClientSpineAngles( centity_t *cent, vec3_t viewAngles, const vec3_t an } //distribute the angles differently up the spine //NOTE: each of these distributions must add up to 1.0f - thoracicAngles[PITCH] = viewAngles[PITCH]*0.20f; - llAngles[PITCH] = viewAngles[PITCH]*0.40f; - ulAngles[PITCH] = viewAngles[PITCH]*0.40f; + thoracicAngles[PITCH] = 0;//viewAngles[PITCH]*0.20f; + llAngles[PITCH] = 0;//viewAngles[PITCH]*0.40f; + ulAngles[PITCH] = 0;//viewAngles[PITCH]*0.40f; -// thoracicAngles[YAW] = viewAngles[YAW]*0.20f; -// ulAngles[YAW] = viewAngles[YAW]*0.35f; -// llAngles[YAW] = viewAngles[YAW]*0.45f; + thoracicAngles[YAW] = viewAngles[YAW]*0.20f - (viewAngles[PITCH]*(viewAngles[YAW]*.020f)); + ulAngles[YAW] = viewAngles[YAW]*0.25f - (viewAngles[PITCH]*(viewAngles[YAW]*.0005f)); + llAngles[YAW] = viewAngles[YAW]*0.25f - (viewAngles[PITCH]*(viewAngles[YAW]*.0005f)); - thoracicAngles[YAW] = viewAngles[YAW]*0.20f; - ulAngles[YAW] = viewAngles[YAW]*0.25f; - llAngles[YAW] = viewAngles[YAW]*0.25f; + if (thoracicAngles[YAW] > 20) + { + thoracicAngles[YAW] = 20; + } + if (ulAngles[YAW] > 20) + { + ulAngles[YAW] = 20; + } + if (llAngles[YAW] > 20) + { + llAngles[YAW] = 20; + } thoracicAngles[ROLL] = viewAngles[ROLL]*0.20f; ulAngles[ROLL] = viewAngles[ROLL]*0.35f; @@ -2555,6 +2598,12 @@ static void CG_G2PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t legsAngle velPos[0] = cent->lerpOrigin[0] + velocity[0]; velPos[1] = cent->lerpOrigin[1] + velocity[1]; velPos[2] = cent->lerpOrigin[2];// + velocity[2]; + + if (cent->currentState.groundEntityNum == ENTITYNUM_NONE) + { //off the ground, no direction-based leg angles + VectorCopy(cent->lerpOrigin, velPos); + } + VectorSubtract(cent->lerpOrigin, velPos, velAng); if (!VectorCompare(velAng, vec3_origin)) @@ -2686,68 +2735,6 @@ static void CG_G2PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t legsAngle llAngles[ROLL] += torsoAngles[ROLL]*0.3; thoracicAngles[ROLL] += torsoAngles[ROLL]*0.4; - //We must compensate yaw for pitch so that the player can't bend all the way over and contort his spine - //to the left/right at an unnatural angle - if (ulAngles[PITCH] > 0) - { - if (ulAngles[YAW] > 0) - { - ulAngles[YAW] -= ulAngles[PITCH]; - if (ulAngles[YAW] < 0) - { - ulAngles[YAW] = 0; - } - } - else if (ulAngles[YAW] < 0) - { - ulAngles[YAW] += ulAngles[PITCH]; - if (ulAngles[YAW] > 0) - { - ulAngles[YAW] = 0; - } - } - } - - if (llAngles[PITCH] > 0) - { - if (llAngles[YAW] > 0) - { - llAngles[YAW] -= llAngles[PITCH]; - if (llAngles[YAW] < 0) - { - llAngles[YAW] = 0; - } - } - else if (llAngles[YAW] < 0) - { - llAngles[YAW] += llAngles[PITCH]; - if (llAngles[YAW] > 0) - { - llAngles[YAW] = 0; - } - } - } - - if (thoracicAngles[PITCH] > 0) - { - if (thoracicAngles[YAW] > 0) - { - thoracicAngles[YAW] -= thoracicAngles[PITCH]; - if (thoracicAngles[YAW] < 0) - { - thoracicAngles[YAW] = 0; - } - } - else if (thoracicAngles[YAW] < 0) - { - thoracicAngles[YAW] += thoracicAngles[PITCH]; - if (thoracicAngles[YAW] > 0) - { - thoracicAngles[YAW] = 0; - } - } - } - if ( cent->currentState.otherEntityNum2 && !(cent->currentState.eFlags & EF_DEAD) ) { //using an emplaced gun centity_t *empEnt = &cg_entities[cent->currentState.otherEntityNum2]; @@ -2968,6 +2955,12 @@ static void CG_PlayerFlag( centity_t *cent, qhandle_t hModel ) { vec3_t boltOrg, tAng, getAng, right; mdxaBone_t boltMatrix; + if (cent->currentState.number == cg.snap->ps.clientNum && + !cg.renderingThirdPerson) + { + return; + } + if (!cent->ghoul2) { return; @@ -4026,7 +4019,7 @@ Ghoul2 Insert Start if ( cg.time - client->saberHitWallSoundDebounceTime >= 100 ) {//ugh, need to have a real sound debouncer... or do this game-side client->saberHitWallSoundDebounceTime = cg.time; - trap_S_StartSound ( trace.endpos, -1, CHAN_WEAPON, trap_S_RegisterSound( "sound/weapons/saber/saberhitwall.wav" ) ); + trap_S_StartSound ( trace.endpos, -1, CHAN_WEAPON, trap_S_RegisterSound( va("sound/weapons/saber/saberhitwall%i", Q_irand(1, 3)) ) ); } } } @@ -4893,67 +4886,6 @@ void CG_Player( centity_t *cent ) { goto doEssentialOne; } - if ( (cent->currentState.powerups & (1 << PW_YSALIMARI)) || - (cgs.gametype == GT_CTY && ((cent->currentState.powerups & (1 << PW_REDFLAG)) || (cent->currentState.powerups & (1 << PW_BLUEFLAG)))) ) - { - vec3_t efColors; - - if (cgs.gametype == GT_CTY && (cent->currentState.powerups & (1 << PW_REDFLAG))) - { - efColors[0] = 255; - efColors[1] = 50; - efColors[2] = -1; - } - else if (cgs.gametype == GT_CTY && (cent->currentState.powerups & (1 << PW_BLUEFLAG))) - { - efColors[0] = 50; - efColors[1] = 50; - efColors[2] = 255; - } - else - { - efColors[0] = 255; - efColors[1] = 255; - efColors[2] = -1; - } - CG_DrawYsalimariSphere(cent, cent->lerpOrigin, efColors); - } - - if (cent->currentState.powerups & (1 << PW_FORCE_BOON)) - { - vec3_t efColors; - efColors[0] = -1; - efColors[1] = -1; - efColors[2] = 255; - CG_DrawYsalimariSphere(cent, cent->lerpOrigin, efColors); - } - - if (cent->currentState.powerups & (1 << PW_FORCE_ENLIGHTENED_DARK)) - { - vec3_t efColors; - efColors[0] = 255; - efColors[1] = -1; - efColors[2] = -1; - CG_DrawYsalimariSphere(cent, cent->lerpOrigin, efColors); - } - else if (cent->currentState.powerups & (1 << PW_FORCE_ENLIGHTENED_LIGHT)) - { - vec3_t efColors; - efColors[0] = 255; - efColors[1] = 255; - efColors[2] = 255; - CG_DrawYsalimariSphere(cent, cent->lerpOrigin, efColors); - } - - if (cent->currentState.eFlags & EF_INVULNERABLE) - { - vec3_t efColors; - efColors[0] = 0; - efColors[1] = 255; - efColors[2] = 0; - CG_DrawYsalimariSphere(cent, cent->lerpOrigin, efColors); - } - // add the shadow shadow = CG_PlayerShadow( cent, &shadowPlane ); @@ -5026,139 +4958,6 @@ void CG_Player( centity_t *cent ) { trap_R_AddRefEntityToScene( &seeker ); } - if (cgs.gametype == GT_HOLOCRON && cent->currentState.time2 && (cg.renderingThirdPerson || cg.snap->ps.clientNum != cent->currentState.number)) - { - int i = 0; - int renderedHolos = 0; - refEntity_t holoRef; - - while (i < NUM_FORCE_POWERS && renderedHolos < 3) - { - if (cent->currentState.time2 & (1 << i)) - { - memset( &holoRef, 0, sizeof(holoRef) ); - - VectorCopy(cent->lerpOrigin, elevated); - elevated[2] += 8; - - VectorCopy( elevated, holoRef.lightingOrigin ); - holoRef.shadowPlane = shadowPlane; - holoRef.renderfx = 0;//RF_THIRD_PERSON; - - if (renderedHolos == 0) - { - angle = ((cg.time / 8) & 255) * (M_PI * 2) / 255; - dir[0] = cos(angle) * 20; - dir[1] = sin(angle) * 20; - dir[2] = cos(angle) * 20; - VectorAdd(elevated, dir, holoRef.origin); - - angles[0] = sin(angle) * 30; - angles[1] = (angle * 180 / M_PI) + 90; - if (angles[1] > 360) - angles[1] -= 360; - angles[2] = 0; - AnglesToAxis( angles, holoRef.axis ); - } - else if (renderedHolos == 1) - { - angle = ((cg.time / 8) & 255) * (M_PI * 2) / 255 + M_PI; - if (angle > M_PI * 2) - angle -= (float)M_PI * 2; - dir[0] = sin(angle) * 20; - dir[1] = cos(angle) * 20; - dir[2] = cos(angle) * 20; - VectorAdd(elevated, dir, holoRef.origin); - - angles[0] = cos(angle - 0.5 * M_PI) * 30; - angles[1] = 360 - (angle * 180 / M_PI); - if (angles[1] > 360) - angles[1] -= 360; - angles[2] = 0; - AnglesToAxis( angles, holoRef.axis ); - } - else - { - angle = ((cg.time / 6) & 255) * (M_PI * 2) / 255 + 0.5 * M_PI; - if (angle > M_PI * 2) - angle -= (float)M_PI * 2; - dir[0] = sin(angle) * 20; - dir[1] = cos(angle) * 20; - dir[2] = 0; - VectorAdd(elevated, dir, holoRef.origin); - - VectorCopy(dir, holoRef.axis[1]); - VectorNormalize(holoRef.axis[1]); - VectorSet(holoRef.axis[2], 0, 0, 1); - CrossProduct(holoRef.axis[1], holoRef.axis[2], holoRef.axis[0]); - } - - holoRef.modelScale[0] = 0.5; - holoRef.modelScale[1] = 0.5; - holoRef.modelScale[2] = 0.5; - ScaleModelAxis(&holoRef); - - { - float wv; - addspriteArgStruct_t fxSArgs; - vec3_t holoCenter; - - holoCenter[0] = holoRef.origin[0] + holoRef.axis[2][0]*18; - holoCenter[1] = holoRef.origin[1] + holoRef.axis[2][1]*18; - holoCenter[2] = holoRef.origin[2] + holoRef.axis[2][2]*18; - - wv = sin( cg.time * 0.004f ) * 0.08f + 0.1f; - - VectorCopy(holoCenter, fxSArgs.origin); - VectorClear(fxSArgs.vel); - VectorClear(fxSArgs.accel); - fxSArgs.scale = wv*60; - fxSArgs.dscale = wv*60; - fxSArgs.sAlpha = wv*12; - fxSArgs.eAlpha = wv*12; - fxSArgs.rotation = 0.0f; - fxSArgs.bounce = 0.0f; - fxSArgs.life = 1.0f; - - fxSArgs.flags = 0x08000000|0x00000001; - - if (forcePowerDarkLight[i] == FORCE_DARKSIDE) - { //dark - fxSArgs.sAlpha *= 2; - fxSArgs.eAlpha *= 2; - fxSArgs.shader = cgs.media.redSaberGlowShader; - trap_FX_AddSprite(&fxSArgs); - } - else if (forcePowerDarkLight[i] == FORCE_LIGHTSIDE) - { //light - fxSArgs.sAlpha *= 1.5; - fxSArgs.eAlpha *= 1.5; - fxSArgs.shader = cgs.media.redSaberGlowShader; - trap_FX_AddSprite(&fxSArgs); - fxSArgs.shader = cgs.media.greenSaberGlowShader; - trap_FX_AddSprite(&fxSArgs); - fxSArgs.shader = cgs.media.blueSaberGlowShader; - trap_FX_AddSprite(&fxSArgs); - } - else - { //neutral - fxSArgs.sAlpha *= 0.5; - fxSArgs.eAlpha *= 0.5; - fxSArgs.shader = cgs.media.greenSaberGlowShader; - trap_FX_AddSprite(&fxSArgs); - fxSArgs.shader = cgs.media.blueSaberGlowShader; - trap_FX_AddSprite(&fxSArgs); - } - } - - holoRef.hModel = trap_R_RegisterModel(forceHolocronModels[i]); - trap_R_AddRefEntityToScene( &holoRef ); - - renderedHolos++; - } - i++; - } - } doEssentialOne: // add a water splash if partially in and out of water CG_PlayerSplash( cent ); @@ -5558,7 +5357,7 @@ doEssentialTwo: if (cent->currentState.weapon == WP_STUN_BATON && (cent->currentState.eFlags & EF_FIRING)) { //note: This is a stupid placeholder so that it's at least the same as SP - trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, trap_S_RegisterSound("sound/weapons/saber/saberhum") ); + trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, trap_S_RegisterSound("sound/weapons/saber/saberhum1") ); } //NOTE: All effects that should be visible during mindtrick should go above here @@ -5578,6 +5377,213 @@ doEssentialTwo: legs.shaderRGBA[3] = 1; } } + + if (cgs.gametype == GT_HOLOCRON && cent->currentState.time2 && (cg.renderingThirdPerson || cg.snap->ps.clientNum != cent->currentState.number)) + { + int i = 0; + int renderedHolos = 0; + refEntity_t holoRef; + + while (i < NUM_FORCE_POWERS && renderedHolos < 3) + { + if (cent->currentState.time2 & (1 << i)) + { + memset( &holoRef, 0, sizeof(holoRef) ); + + VectorCopy(cent->lerpOrigin, elevated); + elevated[2] += 8; + + VectorCopy( elevated, holoRef.lightingOrigin ); + holoRef.shadowPlane = shadowPlane; + holoRef.renderfx = 0;//RF_THIRD_PERSON; + + if (renderedHolos == 0) + { + angle = ((cg.time / 8) & 255) * (M_PI * 2) / 255; + dir[0] = cos(angle) * 20; + dir[1] = sin(angle) * 20; + dir[2] = cos(angle) * 20; + VectorAdd(elevated, dir, holoRef.origin); + + angles[0] = sin(angle) * 30; + angles[1] = (angle * 180 / M_PI) + 90; + if (angles[1] > 360) + angles[1] -= 360; + angles[2] = 0; + AnglesToAxis( angles, holoRef.axis ); + } + else if (renderedHolos == 1) + { + angle = ((cg.time / 8) & 255) * (M_PI * 2) / 255 + M_PI; + if (angle > M_PI * 2) + angle -= (float)M_PI * 2; + dir[0] = sin(angle) * 20; + dir[1] = cos(angle) * 20; + dir[2] = cos(angle) * 20; + VectorAdd(elevated, dir, holoRef.origin); + + angles[0] = cos(angle - 0.5 * M_PI) * 30; + angles[1] = 360 - (angle * 180 / M_PI); + if (angles[1] > 360) + angles[1] -= 360; + angles[2] = 0; + AnglesToAxis( angles, holoRef.axis ); + } + else + { + angle = ((cg.time / 6) & 255) * (M_PI * 2) / 255 + 0.5 * M_PI; + if (angle > M_PI * 2) + angle -= (float)M_PI * 2; + dir[0] = sin(angle) * 20; + dir[1] = cos(angle) * 20; + dir[2] = 0; + VectorAdd(elevated, dir, holoRef.origin); + + VectorCopy(dir, holoRef.axis[1]); + VectorNormalize(holoRef.axis[1]); + VectorSet(holoRef.axis[2], 0, 0, 1); + CrossProduct(holoRef.axis[1], holoRef.axis[2], holoRef.axis[0]); + } + + holoRef.modelScale[0] = 0.5; + holoRef.modelScale[1] = 0.5; + holoRef.modelScale[2] = 0.5; + ScaleModelAxis(&holoRef); + + { + float wv; + addspriteArgStruct_t fxSArgs; + vec3_t holoCenter; + + holoCenter[0] = holoRef.origin[0] + holoRef.axis[2][0]*18; + holoCenter[1] = holoRef.origin[1] + holoRef.axis[2][1]*18; + holoCenter[2] = holoRef.origin[2] + holoRef.axis[2][2]*18; + + wv = sin( cg.time * 0.004f ) * 0.08f + 0.1f; + + VectorCopy(holoCenter, fxSArgs.origin); + VectorClear(fxSArgs.vel); + VectorClear(fxSArgs.accel); + fxSArgs.scale = wv*60; + fxSArgs.dscale = wv*60; + fxSArgs.sAlpha = wv*12; + fxSArgs.eAlpha = wv*12; + fxSArgs.rotation = 0.0f; + fxSArgs.bounce = 0.0f; + fxSArgs.life = 1.0f; + + fxSArgs.flags = 0x08000000|0x00000001; + + if (forcePowerDarkLight[i] == FORCE_DARKSIDE) + { //dark + fxSArgs.sAlpha *= 3; + fxSArgs.eAlpha *= 3; + fxSArgs.shader = cgs.media.redSaberGlowShader; + trap_FX_AddSprite(&fxSArgs); + } + else if (forcePowerDarkLight[i] == FORCE_LIGHTSIDE) + { //light + fxSArgs.sAlpha *= 1.5; + fxSArgs.eAlpha *= 1.5; + fxSArgs.shader = cgs.media.redSaberGlowShader; + trap_FX_AddSprite(&fxSArgs); + fxSArgs.shader = cgs.media.greenSaberGlowShader; + trap_FX_AddSprite(&fxSArgs); + fxSArgs.shader = cgs.media.blueSaberGlowShader; + trap_FX_AddSprite(&fxSArgs); + } + else + { //neutral + if (i == FP_SABERATTACK || + i == FP_SABERDEFEND || + i == FP_SABERTHROW) + { //saber power + fxSArgs.sAlpha *= 1.5; + fxSArgs.eAlpha *= 1.5; + fxSArgs.shader = cgs.media.greenSaberGlowShader; + trap_FX_AddSprite(&fxSArgs); + } + else + { + fxSArgs.sAlpha *= 0.5; + fxSArgs.eAlpha *= 0.5; + fxSArgs.shader = cgs.media.greenSaberGlowShader; + trap_FX_AddSprite(&fxSArgs); + fxSArgs.shader = cgs.media.blueSaberGlowShader; + trap_FX_AddSprite(&fxSArgs); + } + } + } + + holoRef.hModel = trap_R_RegisterModel(forceHolocronModels[i]); + trap_R_AddRefEntityToScene( &holoRef ); + + renderedHolos++; + } + i++; + } + } + + if ( (cent->currentState.powerups & (1 << PW_YSALIMARI)) || + (cgs.gametype == GT_CTY && ((cent->currentState.powerups & (1 << PW_REDFLAG)) || (cent->currentState.powerups & (1 << PW_BLUEFLAG)))) ) + { + vec3_t efColors; + + if (cgs.gametype == GT_CTY && (cent->currentState.powerups & (1 << PW_REDFLAG))) + { + efColors[0] = 255; + efColors[1] = 50; + efColors[2] = -1; + } + else if (cgs.gametype == GT_CTY && (cent->currentState.powerups & (1 << PW_BLUEFLAG))) + { + efColors[0] = 50; + efColors[1] = 50; + efColors[2] = 255; + } + else + { + efColors[0] = 255; + efColors[1] = 255; + efColors[2] = -1; + } + CG_DrawYsalimariSphere(cent, cent->lerpOrigin, efColors); + } + + if (cent->currentState.powerups & (1 << PW_FORCE_BOON)) + { + vec3_t efColors; + efColors[0] = -1; + efColors[1] = -1; + efColors[2] = 255; + CG_DrawYsalimariSphere(cent, cent->lerpOrigin, efColors); + } + + if (cent->currentState.powerups & (1 << PW_FORCE_ENLIGHTENED_DARK)) + { + vec3_t efColors; + efColors[0] = 255; + efColors[1] = -1; + efColors[2] = -1; + CG_DrawYsalimariSphere(cent, cent->lerpOrigin, efColors); + } + else if (cent->currentState.powerups & (1 << PW_FORCE_ENLIGHTENED_LIGHT)) + { + vec3_t efColors; + efColors[0] = 255; + efColors[1] = 255; + efColors[2] = 255; + CG_DrawYsalimariSphere(cent, cent->lerpOrigin, efColors); + } + + if (cent->currentState.eFlags & EF_INVULNERABLE) + { + vec3_t efColors; + efColors[0] = 0; + efColors[1] = 255; + efColors[2] = 0; + CG_DrawYsalimariSphere(cent, cent->lerpOrigin, efColors); + } stillDoSaber: if (cent->currentState.weapon == WP_SABER && !cent->currentState.shouldtarget) { @@ -5586,12 +5592,12 @@ stillDoSaber: if (cg.snap->ps.clientNum == cent->currentState.number) { trap_S_AddLoopingSound( cent->currentState.number, cg.refdef.vieworg, vec3_origin, - trap_S_RegisterSound( "sound/weapons/saber/saberhum.wav" ) ); + trap_S_RegisterSound( "sound/weapons/saber/saberhum1.wav" ) ); } else { trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, - trap_S_RegisterSound( "sound/weapons/saber/saberhum.wav" ) ); + trap_S_RegisterSound( "sound/weapons/saber/saberhum1.wav" ) ); } } diff --git a/CODE-mp/cgame/cg_playerstate.c b/CODE-mp/cgame/cg_playerstate.c index 88db5ad..8dddf5f 100644 --- a/CODE-mp/cgame/cg_playerstate.c +++ b/CODE-mp/cgame/cg_playerstate.c @@ -329,8 +329,8 @@ void CG_CheckLocalSounds( playerState_t *ps, playerState_t *ops ) { //trap_S_StartLocalSound( cgs.media.hitTeamSound, CHAN_LOCAL_SOUND ); } - // health changes of more than -1 should make pain sounds - if ( ps->stats[STAT_HEALTH] < (ops->stats[STAT_HEALTH] - 1)) { + // health changes of more than -3 should make pain sounds + if ( ps->stats[STAT_HEALTH] < (ops->stats[STAT_HEALTH] - 3)) { if ( ps->stats[STAT_HEALTH] > 0 ) { CG_PainEvent( &cg.predictedPlayerEntity, ps->stats[STAT_HEALTH] ); } diff --git a/CODE-mp/cgame/cg_predict.c b/CODE-mp/cgame/cg_predict.c index 33c3605..c8eda1c 100644 --- a/CODE-mp/cgame/cg_predict.c +++ b/CODE-mp/cgame/cg_predict.c @@ -730,7 +730,7 @@ void CG_PredictPlayerState( void ) { cg_pmove.cmd.serverTime = ((cg_pmove.cmd.serverTime + pmove_msec.integer-1) / pmove_msec.integer) * pmove_msec.integer; } - cg_pmove.animations = cgs.clientinfo[cg.clientNum].animations; + cg_pmove.animations = bgGlobalAnimations; cg_pmove.gametype = cgs.gametype; diff --git a/CODE-mp/cgame/cg_public.h b/CODE-mp/cgame/cg_public.h index 4831038..d533b83 100644 --- a/CODE-mp/cgame/cg_public.h +++ b/CODE-mp/cgame/cg_public.h @@ -320,6 +320,8 @@ typedef enum { // float orientation, float red, float green, float blue, float alpha, // qboolean alphaFade, float radius, qboolean temporary ) + CG_MAP_CHANGE, + } cgameExport_t; // CG_POINT_CONTENTS diff --git a/CODE-mp/cgame/cg_scoreboard.c b/CODE-mp/cgame/cg_scoreboard.c index 25bce0b..fee3b2d 100644 --- a/CODE-mp/cgame/cg_scoreboard.c +++ b/CODE-mp/cgame/cg_scoreboard.c @@ -84,17 +84,17 @@ static void CG_DrawClientScore( int y, score_t *score, float *color, float fade, } } else if ( ci->powerups & ( 1 << PW_REDFLAG ) ) { if( largeFormat ) { - CG_DrawFlagModel( iconx, y - ( 32 - BIGCHAR_HEIGHT ) / 2, 32, 32, TEAM_RED, qfalse ); + CG_DrawFlagModel( iconx, y*1.63, 32, 32, TEAM_RED, qfalse ); } else { - CG_DrawFlagModel( iconx, y, 16, 16, TEAM_RED, qfalse ); + CG_DrawFlagModel( iconx, y*1.63, 32, 32, TEAM_RED, qfalse ); } } else if ( ci->powerups & ( 1 << PW_BLUEFLAG ) ) { if( largeFormat ) { - CG_DrawFlagModel( iconx, y - ( 32 - BIGCHAR_HEIGHT ) / 2, 32, 32, TEAM_BLUE, qfalse ); + CG_DrawFlagModel( iconx, y*1.63, 32, 32, TEAM_BLUE, qfalse ); } else { - CG_DrawFlagModel( iconx, y, 16, 16, TEAM_BLUE, qfalse ); + CG_DrawFlagModel( iconx, y*1.63, 32, 32, TEAM_BLUE, qfalse ); } } else { /* @@ -182,7 +182,7 @@ static void CG_DrawClientScore( int y, score_t *score, float *color, float fade, // add the "ready" marker for intermission exiting if ( cg.snap->ps.stats[ STAT_CLIENTS_READY ] & ( 1 << score->client ) ) { - CG_DrawBigStringColor( iconx, y, "READY", color ); + CG_DrawBigStringColor( iconx-64, y+6, "READY", color ); } } @@ -383,10 +383,12 @@ qboolean CG_DrawOldScoreboard( void ) { } } +/* // load any models that have been deferred if ( ++cg.deferredPlayerLoading > 10 ) { CG_LoadDeferredPlayers(); } +*/ return qtrue; } diff --git a/CODE-mp/cgame/cg_servercmds.c b/CODE-mp/cgame/cg_servercmds.c index 2eb0f84..b4e81af 100644 --- a/CODE-mp/cgame/cg_servercmds.c +++ b/CODE-mp/cgame/cg_servercmds.c @@ -175,7 +175,8 @@ CG_SetConfigValues Called on load to set the initial values from configure strings ================ */ -void CG_SetConfigValues( void ) { +void CG_SetConfigValues( void ) +{ const char *s; cgs.scores1 = atoi( CG_ConfigString( CS_SCORES1 ) ); @@ -187,6 +188,9 @@ void CG_SetConfigValues( void ) { cgs.blueflag = s[1] - '0'; } cg.warmup = atoi( CG_ConfigString( CS_WARMUP ) ); + + // Track who the jedi master is + cgs.jediMaster = atoi ( CG_ConfigString ( CS_CLIENT_JEDIMASTER ) ); } /* @@ -259,6 +263,8 @@ static void CG_ConfigStringModified( void ) { cgs.scores1 = atoi( str ); } else if ( num == CS_SCORES2 ) { cgs.scores2 = atoi( str ); + } else if ( num == CS_CLIENT_JEDIMASTER ) { + cgs.jediMaster = atoi ( str ); } else if ( num == CS_LEVEL_START_TIME ) { cgs.levelStartTime = atoi( str ); } else if ( num == CS_VOTE_TIME ) { @@ -295,6 +301,10 @@ static void CG_ConfigStringModified( void ) { if ( str[0] != '*' ) { // player specific sounds don't register here cgs.gameSounds[ num-CS_SOUNDS] = trap_S_RegisterSound( str ); } + } else if ( num >= CS_EFFECTS && num < CS_SOUNDS+MAX_SOUNDS ) { + if ( str[0] != '*' ) { // player specific sounds don't register here + cgs.gameEffects[ num-CS_EFFECTS] = trap_FX_RegisterEffect( str ); + } } else if ( num >= CS_PLAYERS && num < CS_PLAYERS+MAX_CLIENTS ) { CG_NewClientInfo( num - CS_PLAYERS, qtrue); CG_BuildSpectatorString(); diff --git a/CODE-mp/cgame/cg_weaponinit.c b/CODE-mp/cgame/cg_weaponinit.c index 5dc4ce2..e093687 100644 --- a/CODE-mp/cgame/cg_weaponinit.c +++ b/CODE-mp/cgame/cg_weaponinit.c @@ -126,7 +126,7 @@ void CG_RegisterWeapon( int weaponNum) { break; case WP_SABER: MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f ); - weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/saber/saberhum.wav" ); + weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/saber/saberhum.wav1" ); weaponInfo->missileModel = trap_R_RegisterModel( "models/weapons2/saber/saber_w.glm" ); break; diff --git a/CODE-mp/cgame/cg_weapons.c b/CODE-mp/cgame/cg_weapons.c index bd943da..9247a6b 100644 --- a/CODE-mp/cgame/cg_weapons.c +++ b/CODE-mp/cgame/cg_weapons.c @@ -118,7 +118,7 @@ CG_MapTorsoToWeaponFrame ================= */ static int CG_MapTorsoToWeaponFrame( clientInfo_t *ci, int frame, int animNum ) { - animation_t *animations = ci->animations; + animation_t *animations = bgGlobalAnimations; switch( animNum ) { @@ -148,6 +148,7 @@ static int CG_MapTorsoToWeaponFrame( clientInfo_t *ci, int frame, int animNum ) case BOTH_ATTACK10: case BOTH_ATTACK11: case BOTH_ATTACK12: + case BOTH_THERMAL_THROW: if ( frame >= animations[animNum].firstFrame && frame < animations[animNum].firstFrame + 6 ) { return 1 + ( frame - animations[animNum].firstFrame ); @@ -393,6 +394,7 @@ void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent weapon_t weaponNum; weaponInfo_t *weapon; centity_t *nonPredictedCent; + refEntity_t flash; weaponNum = cent->currentState.weapon; @@ -484,6 +486,11 @@ Ghoul2 Insert Start Ghoul2 Insert End */ + memset (&flash, 0, sizeof(flash)); + CG_PositionEntityOnTag( &flash, &gun, gun.hModel, "tag_flash"); + + VectorCopy(flash.origin, cg.lastFPFlashPoint); + // Do special charge bits //----------------------- if ( (ps || cg.renderingThirdPerson || cg.predictedPlayerState.clientNum != cent->currentState.number) && @@ -496,13 +503,9 @@ Ghoul2 Insert End float scale = 1.0f; addspriteArgStruct_t fxSArgs; vec3_t flashorigin, flashdir; - refEntity_t flash; - - memset (&flash, 0, sizeof(flash)); if (!thirdPerson) { - CG_PositionEntityOnTag( &flash, &gun, gun.hModel, "tag_flash"); VectorCopy(flash.origin, flashorigin); VectorCopy(flash.axis[0], flashdir); } @@ -1310,7 +1313,8 @@ void CG_Weapon_f( void ) { { if (cg.snap->ps.weaponTime < 1) { - trap_SendClientCommand("sv_saberswitch"); + //trap_SendClientCommand("sv_saberswitch"); + trap_SendConsoleCommand("sv_saberswitch"); } return; } @@ -1332,7 +1336,7 @@ void CG_Weapon_f( void ) { } } - if (num > WP_DET_PACK) + if (num > WP_DET_PACK+1) { //other weapons are off limits due to not actually being weapon weapons return; } @@ -1500,6 +1504,28 @@ void CG_FireATST(centity_t *cent, qboolean altFire) trap_S_StartSound(NULL, cent->currentState.number, CHAN_WEAPON, trap_S_RegisterSound(va("sound/weapons/atst/ATSTfire1.wav"/*, Q_irand(1,4)*/))); } +void CG_GetClientWeaponMuzzleBoltPoint(int clIndex, vec3_t to) +{ + centity_t *cent; + mdxaBone_t boltMatrix; + + if (clIndex < 0 || clIndex >= MAX_CLIENTS) + { + return; + } + + cent = &cg_entities[clIndex]; + + if (!cent || !cent->ghoul2 || !trap_G2_HaveWeGhoul2Models(cent->ghoul2) || + !trap_G2API_HasGhoul2ModelOnIndex(&(cent->ghoul2), 1)) + { + return; + } + + trap_G2API_GetBoltMatrix(cent->ghoul2, 1, 0, &boltMatrix, cent->turAngles, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale); + trap_G2API_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, to); +} + /* ================ CG_FireWeapon diff --git a/CODE-mp/cgame/cgame.bat b/CODE-mp/cgame/cgame.bat deleted file mode 100644 index 7fda2fd..0000000 --- a/CODE-mp/cgame/cgame.bat +++ /dev/null @@ -1,102 +0,0 @@ -del /q vm -mkdir vm -cd vm -set cc=lcc -DQ3_VM -DMISSIONPACK -DCGAME -S -Wf-target=bytecode -Wf-g -I..\..\cgame -I..\..\game -I..\..\ui %1 - -%cc% ../../game/bg_misc.c -@if errorlevel 1 goto quit -%cc% ../../game/bg_weapons.c -@if errorlevel 1 goto quit -%cc% ../../game/bg_panimate.c -@if errorlevel 1 goto quit -%cc% ../../game/bg_pmove.c -@if errorlevel 1 goto quit -%cc% ../../game/bg_slidemove.c -@if errorlevel 1 goto quit -%cc% ../../game/bg_lib.c -@if errorlevel 1 goto quit -%cc% ../../game/bg_saber.c -@if errorlevel 1 goto quit -%cc% ../../game/q_math.c -@if errorlevel 1 goto quit -%cc% ../../game/q_shared.c -@if errorlevel 1 goto quit -%cc% ../cg_consolecmds.c -@if errorlevel 1 goto quit -%cc% ../cg_draw.c -@if errorlevel 1 goto quit -%cc% ../cg_drawtools.c -@if errorlevel 1 goto quit -%cc% ../cg_effects.c -@if errorlevel 1 goto quit -%cc% ../cg_ents.c -@if errorlevel 1 goto quit -%cc% ../cg_event.c -@if errorlevel 1 goto quit -%cc% ../cg_info.c -@if errorlevel 1 goto quit -%cc% ../cg_light.c -@if errorlevel 1 goto quit -%cc% ../cg_localents.c -@if errorlevel 1 goto quit -%cc% ../cg_main.c -@if errorlevel 1 goto quit -%cc% ../cg_marks.c -@if errorlevel 1 goto quit -%cc% ../cg_players.c -@if errorlevel 1 goto quit -%cc% ../cg_playerstate.c -@if errorlevel 1 goto quit -%cc% ../cg_predict.c -@if errorlevel 1 goto quit -%cc% ../cg_saga.c -@if errorlevel 1 goto quit -%cc% ../cg_scoreboard.c -@if errorlevel 1 goto quit -%cc% ../cg_servercmds.c -@if errorlevel 1 goto quit -%cc% ../cg_snapshot.c -@if errorlevel 1 goto quit -%cc% ../cg_turret.c -@if errorlevel 1 goto quit -%cc% ../cg_view.c -@if errorlevel 1 goto quit -%cc% ../cg_weaponinit.c -@if errorlevel 1 goto quit -%cc% ../cg_weapons.c -@if errorlevel 1 goto quit -%cc% ../fx_blaster.c -@if errorlevel 1 goto quit -%cc% ../fx_bowcaster.c -@if errorlevel 1 goto quit -%cc% ../fx_bryarpistol.c -@if errorlevel 1 goto quit -%cc% ../fx_demp2.c -@if errorlevel 1 goto quit -%cc% ../fx_disruptor.c -@if errorlevel 1 goto quit -%cc% ../fx_flechette.c -@if errorlevel 1 goto quit -%cc% ../fx_heavyrepeater.c -@if errorlevel 1 goto quit -%cc% ../fx_rocketlauncher.c -@if errorlevel 1 goto quit -%cc% ../fx_force.c -@if errorlevel 1 goto quit -%cc% ../../ui/ui_shared.c -@if errorlevel 1 goto quit -%cc% ../cg_newDraw.c -@if errorlevel 1 goto quit - -sysmaker ../cg_public.h ../cg_syscalls.c ../cg_syscalls.asm -@if errorlevel 1 goto quit - -q3asm -f ../cgame -@if errorlevel 1 goto quit - -mkdir "..\..\base\vm" -copy *.map "..\..\base\vm" -copy *.qvm "..\..\base\vm" - -:quit -cd .. diff --git a/CODE-mp/cgame/cgame.q3asm b/CODE-mp/cgame/cgame.q3asm deleted file mode 100644 index a88d978..0000000 --- a/CODE-mp/cgame/cgame.q3asm +++ /dev/null @@ -1,44 +0,0 @@ --o "cgame" -cg_main -..\cg_syscalls -cg_consolecmds -cg_draw -cg_drawtools -cg_effects -cg_ents -cg_event -cg_info -cg_light -cg_localents -cg_marks -cg_players -cg_playerstate -cg_predict -cg_saga -cg_scoreboard -cg_servercmds -cg_snapshot -cg_turret -cg_view -cg_weaponinit -cg_weapons -fx_blaster -fx_bowcaster -fx_bryarpistol -fx_demp2 -fx_disruptor -fx_flechette -fx_heavyrepeater -fx_rocketlauncher -fx_force -bg_slidemove -bg_weapons -bg_panimate -bg_pmove -bg_lib -bg_misc -bg_saber -q_math -q_shared -ui_shared -cg_newDraw diff --git a/CODE-mp/cgame/mssccprj.scc b/CODE-mp/cgame/mssccprj.scc deleted file mode 100644 index eede0df..0000000 --- a/CODE-mp/cgame/mssccprj.scc +++ /dev/null @@ -1,5 +0,0 @@ -SCC = This is a Source Code Control file - -[JK2_cgame.dsp] -SCC_Aux_Path = "\\ravend\vss_projects\jk2sof2MP" -SCC_Project_Name = "$/General/code/cgame", UPCAAAAA diff --git a/CODE-mp/cgame/vssver.scc b/CODE-mp/cgame/vssver.scc deleted file mode 100644 index 38a1a6f..0000000 Binary files a/CODE-mp/cgame/vssver.scc and /dev/null differ diff --git a/CODE-mp/client/cl_input.cpp b/CODE-mp/client/cl_input.cpp index 1ef68a3..46ae2d6 100644 --- a/CODE-mp/client/cl_input.cpp +++ b/CODE-mp/client/cl_input.cpp @@ -48,6 +48,126 @@ void IN_MLookUp( void ) { } } +void IN_GenCMD1( void ) +{ + cl.gcmdSendValue = qtrue; + cl.gcmdValue = GENCMD_SABERSWITCH; +} + +void IN_GenCMD2( void ) +{ + cl.gcmdSendValue = qtrue; + cl.gcmdValue = GENCMD_ENGAGE_DUEL; +} + +void IN_GenCMD3( void ) +{ + cl.gcmdSendValue = qtrue; + cl.gcmdValue = GENCMD_FORCE_HEAL; +} + +void IN_GenCMD4( void ) +{ + cl.gcmdSendValue = qtrue; + cl.gcmdValue = GENCMD_FORCE_SPEED; +} + +void IN_GenCMD5( void ) +{ + cl.gcmdSendValue = qtrue; + cl.gcmdValue = GENCMD_FORCE_PULL; +} + +void IN_GenCMD6( void ) +{ + cl.gcmdSendValue = qtrue; + cl.gcmdValue = GENCMD_FORCE_DISTRACT; +} + +void IN_GenCMD7( void ) +{ + cl.gcmdSendValue = qtrue; + cl.gcmdValue = GENCMD_FORCE_RAGE; +} + +void IN_GenCMD8( void ) +{ + cl.gcmdSendValue = qtrue; + cl.gcmdValue = GENCMD_FORCE_PROTECT; +} + +void IN_GenCMD9( void ) +{ + cl.gcmdSendValue = qtrue; + cl.gcmdValue = GENCMD_FORCE_ABSORB; +} + +void IN_GenCMD10( void ) +{ + cl.gcmdSendValue = qtrue; + cl.gcmdValue = GENCMD_FORCE_HEALOTHER; +} + +void IN_GenCMD11( void ) +{ + cl.gcmdSendValue = qtrue; + cl.gcmdValue = GENCMD_FORCE_FORCEPOWEROTHER; +} + +void IN_GenCMD12( void ) +{ + cl.gcmdSendValue = qtrue; + cl.gcmdValue = GENCMD_FORCE_SEEING; +} + +void IN_GenCMD13( void ) +{ + cl.gcmdSendValue = qtrue; + cl.gcmdValue = GENCMD_USE_SEEKER; +} + +void IN_GenCMD14( void ) +{ + cl.gcmdSendValue = qtrue; + cl.gcmdValue = GENCMD_USE_FIELD; +} + +void IN_GenCMD15( void ) +{ + cl.gcmdSendValue = qtrue; + cl.gcmdValue = GENCMD_USE_BACTA; +} + +void IN_GenCMD16( void ) +{ + cl.gcmdSendValue = qtrue; + cl.gcmdValue = GENCMD_USE_ELECTROBINOCULARS; +} + +void IN_GenCMD17( void ) +{ + cl.gcmdSendValue = qtrue; + cl.gcmdValue = GENCMD_ZOOM; +} + +void IN_GenCMD18( void ) +{ + cl.gcmdSendValue = qtrue; + cl.gcmdValue = GENCMD_USE_SENTRY; +} + +void IN_GenCMD19( void ) +{ + cl.gcmdSendValue = qtrue; + cl.gcmdValue = GENCMD_SABERATTACKCYCLE; +} + +void IN_GenCMD20( void ) +{ + cl.gcmdSendValue = qtrue; + cl.gcmdValue = GENCMD_FORCE_THROW; +} + void IN_KeyDown( kbutton_t *b ) { int k; char *c; @@ -298,7 +418,7 @@ void CL_KeyMove( usercmd_t *cmd ) { cmd->buttons &= ~BUTTON_WALKING; } else { cmd->buttons |= BUTTON_WALKING; - movespeed = 64; + movespeed = 32; } forward = 0; @@ -491,6 +611,16 @@ void CL_FinishMove( usercmd_t *cmd ) { cmd->forcesel = cl.cgameForceSelection; cmd->invensel = cl.cgameInvenSelection; + if (cl.gcmdSendValue) + { + cmd->generic_cmd = cl.gcmdValue; + cl.gcmdSendValue = qfalse; + } + else + { + cmd->generic_cmd = 0; + } + // send the current server time so the amount of movement // can be determined without allowing cheating cmd->serverTime = cl.serverTime; @@ -962,6 +1092,27 @@ void CL_InitInput( void ) { Cmd_AddCommand ("+mlook", IN_MLookDown); Cmd_AddCommand ("-mlook", IN_MLookUp); + Cmd_AddCommand ("sv_saberswitch", IN_GenCMD1); + Cmd_AddCommand ("engage_duel", IN_GenCMD2); + Cmd_AddCommand ("force_heal", IN_GenCMD3); + Cmd_AddCommand ("force_speed", IN_GenCMD4); + Cmd_AddCommand ("force_pull", IN_GenCMD5); + Cmd_AddCommand ("force_distract", IN_GenCMD6); + Cmd_AddCommand ("force_rage", IN_GenCMD7); + Cmd_AddCommand ("force_protect", IN_GenCMD8); + Cmd_AddCommand ("force_absorb", IN_GenCMD9); + Cmd_AddCommand ("force_healother", IN_GenCMD10); + Cmd_AddCommand ("force_forcepowerother", IN_GenCMD11); + Cmd_AddCommand ("force_seeing", IN_GenCMD12); + Cmd_AddCommand ("use_seeker", IN_GenCMD13); + Cmd_AddCommand ("use_field", IN_GenCMD14); + Cmd_AddCommand ("use_bacta", IN_GenCMD15); + Cmd_AddCommand ("use_electrobinoculars", IN_GenCMD16); + Cmd_AddCommand ("zoom", IN_GenCMD17); + Cmd_AddCommand ("use_sentry", IN_GenCMD18); + Cmd_AddCommand ("saberAttackCycle", IN_GenCMD19); + Cmd_AddCommand ("force_throw", IN_GenCMD20); + cl_nodelta = Cvar_Get ("cl_nodelta", "0", 0); cl_debugMove = Cvar_Get ("cl_debugMove", "0", 0); } diff --git a/CODE-mp/client/cl_keys.cpp b/CODE-mp/client/cl_keys.cpp index 5d72486..68def1b 100644 --- a/CODE-mp/client/cl_keys.cpp +++ b/CODE-mp/client/cl_keys.cpp @@ -1165,7 +1165,12 @@ void CL_KeyEvent (int key, qboolean down, unsigned time) { return; } - +#ifdef FINAL_BUILD + if (!(cls.keyCatchers & KEYCATCH_CONSOLE) && !keys[K_SHIFT].down ) //we're not in the console + {//so we require the control keys to get in + return; + } +#endif Con_ToggleConsole_f (); return; } diff --git a/CODE-mp/client/cl_main.cpp b/CODE-mp/client/cl_main.cpp index 1363c41..99123c7 100644 --- a/CODE-mp/client/cl_main.cpp +++ b/CODE-mp/client/cl_main.cpp @@ -506,7 +506,14 @@ void CL_PlayDemo_f( void ) { FS_FOpenFileRead( name, &clc.demofile, qtrue ); if (!clc.demofile) { - Com_Error( ERR_DROP, "couldn't open %s", name); + if (!Q_stricmp(arg, "(null)")) + { + Com_Error( ERR_DROP, "No demo selected.", name); + } + else + { + Com_Error( ERR_DROP, "couldn't open %s", name); + } return; } Q_strncpyz( clc.demoName, Cmd_Argv(1), sizeof( clc.demoName ) ); @@ -1918,7 +1925,8 @@ void CL_CheckTimeout( void ) { && cls.realtime - clc.lastPacketTime > cl_timeout->value*1000) { if (++cl.timeoutcount > 5) { // timeoutcount saves debugger Com_Printf ("\nServer connection timed out.\n"); - CL_Disconnect( qtrue ); + Com_Error(ERR_DROP, "Server connection timed out."); + //CL_Disconnect( qtrue ); return; } } else { diff --git a/CODE-mp/client/cl_parse.cpp b/CODE-mp/client/cl_parse.cpp index 8d15505..6600e0c 100644 --- a/CODE-mp/client/cl_parse.cpp +++ b/CODE-mp/client/cl_parse.cpp @@ -13,7 +13,8 @@ char *svc_strings[256] = { "svc_baseline", "svc_serverCommand", "svc_download", - "svc_snapshot" + "svc_snapshot", + "svc_mapchange", }; void SHOWNET( msg_t *msg, char *s) { @@ -634,6 +635,12 @@ void CL_ParseServerMessage( msg_t *msg ) { case svc_download: CL_ParseDownload( msg ); break; + case svc_mapchange: + if (cgvm) + { + VM_Call( cgvm, CG_MAP_CHANGE ); + } + break; } } } diff --git a/CODE-mp/client/client.h b/CODE-mp/client/client.h index d1d0611..bb18265 100644 --- a/CODE-mp/client/client.h +++ b/CODE-mp/client/client.h @@ -96,6 +96,9 @@ typedef struct { int cgameForceSelection; int cgameInvenSelection; + qboolean gcmdSendValue; + byte gcmdValue; + float lastViewYaw; // cmds[cmdNumber] is the predicted command, [cmdNumber-1] is the last diff --git a/CODE-mp/client/snd_dma.cpp b/CODE-mp/client/snd_dma.cpp index 2938f69..bea9677 100644 --- a/CODE-mp/client/snd_dma.cpp +++ b/CODE-mp/client/snd_dma.cpp @@ -138,6 +138,8 @@ cvar_t *s_musicVolume; cvar_t *s_separation; cvar_t *s_doppler; cvar_t *s_CPUType; +cvar_t *s_language; + static loopSound_t loopSounds[MAX_GENTITIES]; static channel_t *freelist = NULL; @@ -219,6 +221,8 @@ void S_Init( void ) { s_show = Cvar_Get ("s_show", "0", CVAR_CHEAT); s_testsound = Cvar_Get ("s_testsound", "0", CVAR_CHEAT); + s_language = Cvar_Get("s_language","english",CVAR_ARCHIVE | CVAR_NORESTART); + MP3_InitCvars(); s_CPUType = Cvar_Get("sys_cpuid","",0); diff --git a/CODE-mp/client/snd_mem.cpp b/CODE-mp/client/snd_mem.cpp index 03dd2ca..39cbbb5 100644 --- a/CODE-mp/client/snd_mem.cpp +++ b/CODE-mp/client/snd_mem.cpp @@ -215,6 +215,81 @@ void S_LoadSound_Finalize(wavinfo_t *info, sfx_t *sfx, byte *data) +// adjust filename for foreign languages and WAV/MP3 issues. +// +// returns qfalse if failed to load, else fills in *pData +// +static qboolean S_LoadSound_FileLoadAndNameAdjuster(char *psFilename, byte **pData, int *piSize, int iNameStrlen) +{ + char *psVoice = strstr(psFilename,"chars"); + if (psVoice) + { + // account for foreign voices... + // + extern cvar_t* s_language; + if (s_language && stricmp("DEUTSCH",s_language->string)==0) + { + strncpy(psVoice,"chr_d",5); // same number of letters as "chars" + } + else + if (s_language && stricmp("FRANCAIS",s_language->string)==0) + { + strncpy(psVoice,"chr_f",5); // same number of letters as "chars" + } + else + { + psVoice = NULL; // use this ptr as a flag as to whether or not we substituted with a foreign version + } + } + + *piSize = FS_ReadFile( psFilename, (void **)pData ); // try WAV + if ( !*pData ) { + psFilename[iNameStrlen-3] = 'm'; + psFilename[iNameStrlen-2] = 'p'; + psFilename[iNameStrlen-1] = '3'; + *piSize = FS_ReadFile( psFilename, (void **)pData ); // try MP3 + + if ( !*pData ) + { + //hmmm, not found, ok, maybe we were trying a foreign noise ("arghhhhh.mp3" that doesn't matter?) but it + // was missing? Can't tell really, since both types are now in sound/chars. Oh well, fall back to English for now... + + if (psVoice) // were we trying to load foreign? + { + // yep, so fallback to re-try the english... + // +#ifndef FINAL_BUILD + Com_Printf(S_COLOR_YELLOW "Foreign file missing: \"%s\"! (using English...)\n",psFilename); +#endif + + strncpy(psVoice,"chars",5); + + psFilename[iNameStrlen-3] = 'w'; + psFilename[iNameStrlen-2] = 'a'; + psFilename[iNameStrlen-1] = 'v'; + *piSize = FS_ReadFile( psFilename, (void **)pData ); // try English WAV + if ( !*pData ) + { + psFilename[iNameStrlen-3] = 'm'; + psFilename[iNameStrlen-2] = 'p'; + psFilename[iNameStrlen-1] = '3'; + *piSize = FS_ReadFile( psFilename, (void **)pData ); // try English MP3 + } + } + + if (!*pData) + { + return qfalse; // sod it, give up... + } + } + } + + return qtrue; +} + + + + //============================================================================= /* @@ -243,34 +318,22 @@ static qboolean S_LoadSound_Actual( sfx_t *sfx ) // char sRootName[MAX_QPATH]; char sLoadName[MAX_QPATH]; - bool isMp3 = false; COM_StripExtension(sfx->sSoundName, sRootName); Com_sprintf(sLoadName, MAX_QPATH, "%s.wav", sRootName); - // - // 1st attempt, try whichever wav or mp3 is specified... - // - size = FS_ReadFile( sLoadName, (void **)&data ); - if ( !data ) - { - // 2nd attempt, try wav instead of mp3 or vice versa... - // - Com_sprintf(sLoadName, MAX_QPATH, "%s.mp3", sRootName); + const char *psExt = &sLoadName[strlen(sLoadName)-4]; - size = FS_ReadFile( sLoadName, (void **)&data ); - if ( !data ) - { - return qfalse; - } - isMp3 = true; + if (!S_LoadSound_FileLoadAndNameAdjuster(sLoadName, &data, &size, strlen(sLoadName))) + { + return qfalse; } SND_TouchSFX(sfx); sfx->iLastTimeUsed = Com_Milliseconds()+1; // why +1? Hmmm, leave it for now I guess //========= - if (isMp3) + if (strnicmp(psExt,".mp3",4)==0) { // load MP3 file instead... // diff --git a/CODE-mp/client/vssver.scc b/CODE-mp/client/vssver.scc deleted file mode 100644 index fa80f3b..0000000 Binary files a/CODE-mp/client/vssver.scc and /dev/null differ diff --git a/CODE-mp/encryption/vssver.scc b/CODE-mp/encryption/vssver.scc deleted file mode 100644 index 362953c..0000000 Binary files a/CODE-mp/encryption/vssver.scc and /dev/null differ diff --git a/CODE-mp/game/JK2_game.def b/CODE-mp/game/JK2_game.def deleted file mode 100644 index ea649b4..0000000 --- a/CODE-mp/game/JK2_game.def +++ /dev/null @@ -1,3 +0,0 @@ -EXPORTS - dllEntry - vmMain diff --git a/CODE-mp/game/ai_main.c b/CODE-mp/game/ai_main.c index 41fce2c..95ad7d6 100644 --- a/CODE-mp/game/ai_main.c +++ b/CODE-mp/game/ai_main.c @@ -1862,6 +1862,11 @@ int BotCanHear(bot_state_t *bs, gentity_t *en, float endist) break; } checkStep: + if (BotMindTricked(bs->client, en->s.number)) + { //if mindtricked by this person, cut down on the minlen + minlen /= 4; + } + if (endist <= minlen) { return 1; @@ -2261,6 +2266,32 @@ gentity_t *GetNearestBadThing(bot_state_t *bs) } } + if (ent && !ent->client && ent->inuse && ent->damage && ent->s.weapon && ent->r.ownerNum < MAX_CLIENTS && ent->r.ownerNum >= 0) + { //if we're in danger of a projectile belonging to someone and don't have an enemy, set the enemy to them + gentity_t *projOwner = &g_entities[ent->r.ownerNum]; + + if (projOwner && projOwner->inuse && projOwner->client) + { + if (!bs->currentEnemy) + { + if (PassStandardEnemyChecks(bs, projOwner)) + { + if (PassLovedOneCheck(bs, projOwner)) + { + VectorSubtract(bs->origin, ent->r.currentOrigin, hold); + glen = VectorLength(hold); + + if (glen < 512) + { + bs->currentEnemy = projOwner; + bs->enemySeenTime = level.time + ENEMY_FORGET_MS; + } + } + } + } + } + } + i++; } @@ -6182,11 +6213,15 @@ void StandardBotAI(bot_state_t *bs, float thinktime) { /*if (g_gametype.integer == GT_TOURNAMENT)*/ { //helps them get out of messy situations - if ((level.time - bs->forceJumpChargeTime) > /*500*/3500) + /*if ((level.time - bs->forceJumpChargeTime) > 3500) { bs->forceJumpChargeTime = level.time + 2000; trap_EA_MoveForward(bs->client); } + */ + bs->jumpTime = level.time + 1500; + bs->jumpHoldTime = level.time + 1500; + bs->jDelay = 0; } doingFallback = BotFallbackNavigation(bs); } @@ -6335,12 +6370,13 @@ void StandardBotAI(bot_state_t *bs, float thinktime) } else if (bs->saberThrowTime < level.time && !bs->cur_ps.saberInFlight && (bs->cur_ps.fd.forcePowersKnown & (1 << FP_SABERTHROW)) && - InFieldOfVision(bs->viewangles, 30, a_fo)) + InFieldOfVision(bs->viewangles, 30, a_fo) && + bs->frame_Enemy_Len < BOT_SABER_THROW_RANGE) { bs->doAltAttack = 1; bs->doAttack = 0; } - else if (bs->cur_ps.saberInFlight && bs->frame_Enemy_Len > 300) + else if (bs->cur_ps.saberInFlight && bs->frame_Enemy_Len > 300 && bs->frame_Enemy_Len < BOT_SABER_THROW_RANGE) { bs->doAltAttack = 1; bs->doAttack = 0; @@ -6569,9 +6605,29 @@ void StandardBotAI(bot_state_t *bs, float thinktime) } #endif + if (bs->forceJumpChargeTime > level.time) + { + bs->jumpHoldTime = ((bs->forceJumpChargeTime - level.time)/2) + level.time; + bs->forceJumpChargeTime = 0; + } + + if (bs->jumpHoldTime > level.time) + { + bs->jumpTime = bs->jumpHoldTime; + } + if (bs->jumpTime > level.time && bs->jDelay < level.time) { - if (!(bs->cur_ps.pm_flags & PMF_JUMP_HELD)) + if (bs->jumpHoldTime > level.time) + { + trap_EA_Jump(bs->client); + trap_EA_MoveForward(bs->client); + if (g_entities[bs->client].client->ps.groundEntityNum == ENTITYNUM_NONE) + { + g_entities[bs->client].client->ps.pm_flags |= PMF_JUMP_HELD; + } + } + else if (!(bs->cur_ps.pm_flags & PMF_JUMP_HELD)) { trap_EA_Jump(bs->client); } diff --git a/CODE-mp/game/ai_main.h b/CODE-mp/game/ai_main.h index 2804371..01353d5 100644 --- a/CODE-mp/game/ai_main.h +++ b/CODE-mp/game/ai_main.h @@ -74,6 +74,8 @@ #define BOT_FLAG_GET_DISTANCE 256 +#define BOT_SABER_THROW_RANGE 800 + typedef enum { CTFSTATE_NONE, @@ -238,6 +240,7 @@ typedef struct bot_state_s float beStill; float duckTime; float jumpTime; + float jumpHoldTime; float forceJumping; float jDelay; diff --git a/CODE-mp/game/bg_misc.c b/CODE-mp/game/bg_misc.c index 0b98442..97f2488 100644 --- a/CODE-mp/game/bg_misc.c +++ b/CODE-mp/game/bg_misc.c @@ -104,8 +104,8 @@ int WeaponReadyAnim[WP_NUM_WEAPONS] = TORSO_WEAPONREADY3,//TORSO_WEAPONREADY8,//WP_FLECHETTE, TORSO_WEAPONREADY3,//TORSO_WEAPONREADY9,//WP_ROCKET_LAUNCHER, TORSO_WEAPONREADY10,//WP_THERMAL, - TORSO_WEAPONREADY3,//TORSO_WEAPONREADY11,//WP_TRIP_MINE, - TORSO_WEAPONREADY3,//TORSO_WEAPONREADY12,//WP_DET_PACK, + TORSO_WEAPONREADY10,//TORSO_WEAPONREADY11,//WP_TRIP_MINE, + TORSO_WEAPONREADY10,//TORSO_WEAPONREADY12,//WP_DET_PACK, //NOT VALID (e.g. should never really be used): BOTH_STAND1,//WP_EMPLACED_GUN, @@ -126,7 +126,7 @@ int WeaponAttackAnim[WP_NUM_WEAPONS] = BOTH_ATTACK3,//BOTH_ATTACK7,//WP_DEMP2, BOTH_ATTACK3,//BOTH_ATTACK8,//WP_FLECHETTE, BOTH_ATTACK3,//BOTH_ATTACK9,//WP_ROCKET_LAUNCHER, - BOTH_ATTACK10,//WP_THERMAL, + BOTH_THERMAL_THROW,//WP_THERMAL, BOTH_ATTACK3,//BOTH_ATTACK11,//WP_TRIP_MINE, BOTH_ATTACK3,//BOTH_ATTACK12,//WP_DET_PACK, @@ -182,7 +182,7 @@ Instant shield pickup, restores 25 { "models/map_objects/mp/psd_sm.md3", 0, 0, 0}, /* view */ NULL, -/* icon */ "gfx/hud/w_icon_blaster", +/* icon */ "gfx/mp/small_shield", /* pickup */ "Shield Small", 25, IT_ARMOR, @@ -200,7 +200,7 @@ Instant shield pickup, restores 100 { "models/map_objects/mp/psd.md3", 0, 0, 0}, /* view */ NULL, -/* icon */ "gfx/hud/w_icon_blaster", +/* icon */ "gfx/mp/large_shield", /* pickup */ "Shield Large", 100, IT_ARMOR, @@ -265,7 +265,7 @@ Portable shield IT_HOLDABLE, HI_SHIELD, /* precache */ "", -/* sounds */ "sound/weapons/detpack/stick.wav sound/movers/doors/forcefield_on.wav sound/movers/doors/forcefield_off.wav sound/effects/bumpfield.wav", +/* sounds */ "sound/weapons/detpack/stick.wav sound/movers/doors/forcefield_on.wav sound/movers/doors/forcefield_off.wav sound/movers/doors/forcefield_lp.wav sound/effects/bumpfield.wav", }, /*QUAKED item_medpac (.3 .3 1) (-8 -8 -0) (8 8 16) suspended @@ -723,7 +723,7 @@ Ammo for Tenloss Disruptor, Wookie Bowcaster, and the Destructive Electro Magnet { "models/items/power_cell.md3", 0, 0, 0}, /* view */ NULL, -/* icon */ "gfx/hud/w_icon_blaster", +/* icon */ "gfx/mp/ammo_power_cell", /* pickup */ "Power Cell", 100, IT_AMMO, @@ -741,7 +741,7 @@ Ammo for Imperial Heavy Repeater and the Golan Arms Flechette { "models/items/metallic_bolts.md3", 0, 0, 0}, /* view */ NULL, -/* icon */ "gfx/hud/w_icon_blaster", +/* icon */ "gfx/mp/ammo_metallic_bolts", /* pickup */ "Metallic Bolts", 100, IT_AMMO, @@ -759,7 +759,7 @@ Ammo for Merr-Sonn portable missile launcher { "models/items/rockets.md3", 0, 0, 0}, /* view */ NULL, -/* icon */ "gfx/hud/w_icon_blaster", +/* icon */ "gfx/mp/ammo_rockets", /* pickup */ "Rockets", 100, IT_AMMO, @@ -1227,6 +1227,11 @@ qboolean BG_CanItemBeGrabbed( int gametype, const entityState_t *ent, const play case IT_HEALTH: // small and mega healths will go over the max, otherwise // don't pick up if already at max + if ((ps->fd.forcePowersActive & (1 << FP_RAGE))) + { + return qfalse; + } + if ( item->quantity == 5 || item->quantity == 100 ) { if ( ps->stats[STAT_HEALTH] >= ps->stats[STAT_MAX_HEALTH] * 2 ) { return qfalse; @@ -1449,6 +1454,7 @@ char *eventnames[] = { "EV_GRENADE_BOUNCE", // eventParm will be the soundindex "EV_PLAY_EFFECT", + "EV_PLAY_EFFECT_ID", //finally gave in and added it.. "EV_MUTE_SOUND", "EV_GENERAL_SOUND", @@ -1562,7 +1568,7 @@ void BG_TouchJumpPad( playerState_t *ps, entityState_t *jumppad ) { } else { effectNum = 1; } - BG_AddPredictableEventToPlayerstate( EV_JUMP_PAD, effectNum, ps ); + //BG_AddPredictableEventToPlayerstate( EV_JUMP_PAD, effectNum, ps ); } // remember hitting this jumppad this frame ps->jumppad_ent = jumppad->number; diff --git a/CODE-mp/game/bg_panimate.c b/CODE-mp/game/bg_panimate.c index a2ee725..5b331b8 100644 --- a/CODE-mp/game/bg_panimate.c +++ b/CODE-mp/game/bg_panimate.c @@ -544,7 +544,7 @@ char BGPAFtext[40000]; qboolean BGPAFtextLoaded = qfalse; animation_t bgGlobalAnimations[MAX_TOTALANIMATIONS]; -qboolean BG_ParseAnimationFile( const char *filename, animation_t *animations) +qboolean BG_ParseAnimationFile(const char *filename) { char *text_p; int len; @@ -554,7 +554,7 @@ qboolean BG_ParseAnimationFile( const char *filename, animation_t *animations) int skip; fileHandle_t f; - int animNum; + int animNum; // load the file @@ -567,7 +567,7 @@ qboolean BG_ParseAnimationFile( const char *filename, animation_t *animations) } if ( len >= sizeof( BGPAFtext ) - 1 ) { - // gi.Printf( "File %s too long\n", filename ); + //Com_Printf( "File %s too long\n", filename ); return qfalse; } @@ -577,17 +577,6 @@ qboolean BG_ParseAnimationFile( const char *filename, animation_t *animations) } else { - for(i = 0; i < MAX_ANIMATIONS; i++) - { - animations[i].firstFrame = bgGlobalAnimations[i].firstFrame; - animations[i].flipflop = bgGlobalAnimations[i].flipflop; - animations[i].frameLerp = bgGlobalAnimations[i].frameLerp; - animations[i].initialLerp = bgGlobalAnimations[i].initialLerp; - animations[i].loopFrames = bgGlobalAnimations[i].loopFrames; - animations[i].numFrames = bgGlobalAnimations[i].numFrames; - animations[i].reversed = bgGlobalAnimations[i].reversed; - } - return qtrue; } @@ -600,11 +589,11 @@ qboolean BG_ParseAnimationFile( const char *filename, animation_t *animations) //initialize anim array so that from 0 to MAX_ANIMATIONS, set default values of 0 1 0 100 for(i = 0; i < MAX_ANIMATIONS; i++) { - animations[i].firstFrame = 0; - animations[i].numFrames = 0; - animations[i].loopFrames = -1; - animations[i].frameLerp = 100; - animations[i].initialLerp = 100; + bgGlobalAnimations[i].firstFrame = 0; + bgGlobalAnimations[i].numFrames = 0; + bgGlobalAnimations[i].loopFrames = -1; + bgGlobalAnimations[i].frameLerp = 100; + bgGlobalAnimations[i].initialLerp = 100; } // read information for each frame @@ -632,21 +621,21 @@ qboolean BG_ParseAnimationFile( const char *filename, animation_t *animations) { break; } - animations[animNum].firstFrame = atoi( token ); + bgGlobalAnimations[animNum].firstFrame = atoi( token ); token = COM_Parse( (const char **)(&text_p) ); if ( !token ) { break; } - animations[animNum].numFrames = atoi( token ); + bgGlobalAnimations[animNum].numFrames = atoi( token ); token = COM_Parse( (const char **)(&text_p) ); if ( !token ) { break; } - animations[animNum].loopFrames = atoi( token ); + bgGlobalAnimations[animNum].loopFrames = atoi( token ); token = COM_Parse( (const char **)(&text_p) ); if ( !token ) @@ -660,25 +649,14 @@ qboolean BG_ParseAnimationFile( const char *filename, animation_t *animations) } if ( fps < 0 ) {//backwards - animations[animNum].frameLerp = floor(1000.0f / fps); + bgGlobalAnimations[animNum].frameLerp = floor(1000.0f / fps); } else { - animations[animNum].frameLerp = ceil(1000.0f / fps); + bgGlobalAnimations[animNum].frameLerp = ceil(1000.0f / fps); } - animations[animNum].initialLerp = ceil(1000.0f / fabs(fps)); - } - - for(i = 0; i < MAX_ANIMATIONS; i++) - { - bgGlobalAnimations[i].firstFrame = animations[i].firstFrame; - bgGlobalAnimations[i].flipflop = animations[i].flipflop; - bgGlobalAnimations[i].frameLerp = animations[i].frameLerp; - bgGlobalAnimations[i].initialLerp = animations[i].initialLerp; - bgGlobalAnimations[i].loopFrames = animations[i].loopFrames; - bgGlobalAnimations[i].numFrames = animations[i].numFrames; - bgGlobalAnimations[i].reversed = animations[i].reversed; + bgGlobalAnimations[animNum].initialLerp = ceil(1000.0f / fabs(fps)); } BGPAFtextLoaded = qtrue; diff --git a/CODE-mp/game/bg_pmove.c b/CODE-mp/game/bg_pmove.c index 2299bb5..f412325 100644 --- a/CODE-mp/game/bg_pmove.c +++ b/CODE-mp/game/bg_pmove.c @@ -537,6 +537,22 @@ static qboolean PM_CheckJump( void ) } */ + if (pm->ps->fd.forcePowersActive & (1 << FP_LEVITATION)) + { + if (pm->ps->fd.forcePowerDebounce[FP_LEVITATION] < pm->cmd.serverTime) + { + BG_ForcePowerDrain( pm->ps, FP_LEVITATION, 5 ); + if (pm->ps->fd.forcePowerLevel[FP_LEVITATION] >= FORCE_LEVEL_2) + { + pm->ps->fd.forcePowerDebounce[FP_LEVITATION] = pm->cmd.serverTime + 300; + } + else + { + pm->ps->fd.forcePowerDebounce[FP_LEVITATION] = pm->cmd.serverTime + 200; + } + } + } + if (pm->ps->forceJumpFlip) { int anim = BOTH_FORCEINAIR1; @@ -1501,6 +1517,13 @@ static void PM_WalkMove( void ) { wishspeed = pm->ps->speed * pm_duckScale; } } + else if ( (pm->ps->pm_flags & PMF_ROLLING) && !BG_InRoll(pm->ps, pm->ps->legsAnim) && + !PM_InRollComplete(pm->ps, pm->ps->legsAnim)) + { + if ( wishspeed > pm->ps->speed * pm_duckScale ) { + wishspeed = pm->ps->speed * pm_duckScale; + } + } // clamp the speed lower if wading or walking on the bottom if ( pm->waterlevel ) { @@ -2381,7 +2404,7 @@ static void PM_Footsteps( void ) { if ( !pm->cmd.forwardmove && !pm->cmd.rightmove ) { if ( pm->xyspeed < 5 ) { pm->ps->bobCycle = 0; // start at beginning of cycle again - if ( pm->ps->pm_flags & PMF_DUCKED ) { + if ( (pm->ps->pm_flags & PMF_DUCKED) || (pm->ps->pm_flags & PMF_ROLLING) ) { PM_ContinueLegsAnim( BOTH_CROUCH1IDLE ); } else { if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1) @@ -2447,7 +2470,23 @@ static void PM_Footsteps( void ) { } PM_ContinueLegsAnim( LEGS_BACK ); */ - } else { + } + else if ((pm->ps->pm_flags & PMF_ROLLING) && !BG_InRoll(pm->ps, pm->ps->legsAnim) && + !PM_InRollComplete(pm->ps, pm->ps->legsAnim)) + { + bobmove = 0.5; // ducked characters bob much faster + + if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) + { + PM_ContinueLegsAnim( BOTH_CROUCH1WALKBACK ); + } + else + { + PM_ContinueLegsAnim( BOTH_CROUCH1WALK ); + } + } + else + { if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) { bobmove = 0.4f; // faster speeds bob faster if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) { @@ -2458,7 +2497,7 @@ static void PM_Footsteps( void ) { } footstep = qtrue; } else { - bobmove = 0.3f; // walking bobs slow + bobmove = 0.2f; // walking bobs slow if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) { PM_ContinueLegsAnim( BOTH_WALKBACK1 ); } @@ -2638,16 +2677,27 @@ static qboolean PM_DoChargedWeapons( void ) // implement our alt-fire locking stuff if ( (pm->cmd.buttons & BUTTON_ALT_ATTACK) && pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] >= weaponData[pm->ps->weapon].altEnergyPerShot ) { + vec3_t muzzleOffPoint, muzzlePoint, forward, right, up; + + AngleVectors( pm->ps->viewangles, forward, right, up ); + charging = qtrue; altFire = qtrue; AngleVectors(pm->ps->viewangles, ang, NULL, NULL); - ang[0] = pm->ps->origin[0] + ang[0]*2048; - ang[1] = pm->ps->origin[1] + ang[1]*2048; - ang[2] = pm->ps->origin[2] + ang[2]*2048; + VectorCopy( pm->ps->origin, muzzlePoint ); + VectorCopy(WP_MuzzlePoint[WP_ROCKET_LAUNCHER], muzzleOffPoint); - pm->trace(&tr, pm->ps->origin, NULL, NULL, ang, pm->ps->clientNum, MASK_PLAYERSOLID); + VectorMA(muzzlePoint, muzzleOffPoint[0], forward, muzzlePoint); + VectorMA(muzzlePoint, muzzleOffPoint[1], right, muzzlePoint); + muzzlePoint[2] += pm->ps->viewheight + muzzleOffPoint[2]; + + ang[0] = muzzlePoint[0] + ang[0]*2048; + ang[1] = muzzlePoint[1] + ang[1]*2048; + ang[2] = muzzlePoint[2] + ang[2]*2048; + + pm->trace(&tr, muzzlePoint, NULL, NULL, ang, pm->ps->clientNum, MASK_PLAYERSOLID); if (tr.fraction != 1 && tr.entityNum < MAX_CLIENTS && tr.entityNum != pm->ps->clientNum) { @@ -2669,7 +2719,10 @@ static qboolean PM_DoChargedWeapons( void ) } } - pm->ps->rocketTargetTime = pm->cmd.serverTime + 500; + if (pm->ps->rocketLockIndex == tr.entityNum) + { + pm->ps->rocketTargetTime = pm->cmd.serverTime + 500; + } } else if (pm->ps->rocketTargetTime < pm->cmd.serverTime) { @@ -3127,6 +3180,28 @@ static void PM_Weapon( void ) { pm->ps->saberHolstered = qfalse; } + if (pm->ps->weapon == WP_THERMAL || + pm->ps->weapon == WP_TRIP_MINE || + pm->ps->weapon == WP_DET_PACK) + { + if (pm->ps->weapon == WP_THERMAL) + { + if ((pm->ps->torsoAnim&~ANIM_TOGGLEBIT) == WeaponAttackAnim[pm->ps->weapon] && + (pm->ps->weaponTime-200) <= 0) + { + PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] ); + } + } + else + { + if ((pm->ps->torsoAnim&~ANIM_TOGGLEBIT) == WeaponAttackAnim[pm->ps->weapon] && + (pm->ps->weaponTime-700) <= 0) + { + PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] ); + } + } + } + // don't allow attack until all buttons are up if ( pm->ps->pm_flags & PMF_RESPAWNED ) { return; @@ -3464,6 +3539,7 @@ static void PM_Animate( void ) { PM_StartTorsoAnim( BOTH_TALKGESTURE3 ); pm->ps->torsoTimer = TIMER_GESTURE; */ + pm->ps->forceHandExtend = HANDEXTEND_TAUNT; //FIXME: random taunt anims? @@ -3471,6 +3547,8 @@ static void PM_Animate( void ) { pm->ps->forceHandExtendTime = pm->cmd.serverTime + 1000; + pm->ps->weaponTime = 100; + PM_AddEvent( EV_TAUNT ); } #if 0 @@ -3599,7 +3677,7 @@ void PM_AdjustAttackStates( pmove_t *pm ) } // disruptor alt-fire should toggle the zoom mode, but only bother doing this for the player? - if ( pm->ps->weapon == WP_DISRUPTOR) + if ( pm->ps->weapon == WP_DISRUPTOR && pm->ps->weaponstate == WEAPON_READY ) { if ( !(pm->ps->eFlags & EF_ALT_FIRING) && (pm->cmd.buttons & BUTTON_ALT_ATTACK) && pm->cmd.upmove <= 0 && !pm->cmd.forwardmove && !pm->cmd.rightmove) diff --git a/CODE-mp/game/bg_public.h b/CODE-mp/game/bg_public.h index 5e84952..bcf28e5 100644 --- a/CODE-mp/game/bg_public.h +++ b/CODE-mp/game/bg_public.h @@ -71,6 +71,8 @@ #define CS_ITEMS 27 // string of 0's and 1's that tell which items are present +#define CS_CLIENT_JEDIMASTER 28 // current jedi master + // these are also in be_aas_def.h - argh (rjr) #define CS_MODELS 32 #define CS_SOUNDS (CS_MODELS+MAX_MODELS) @@ -163,6 +165,8 @@ typedef struct animation_s { int flipflop; // true if animation should flipflop back to base } animation_t; +extern qboolean BGPAFtextLoaded; +extern animation_t bgGlobalAnimations[MAX_TOTALANIMATIONS]; // flip the togglebit every time an animation // changes so a restart of the same anim can be detected @@ -362,6 +366,7 @@ typedef enum { EFFECT_NONE = 0, EFFECT_SMOKE, EFFECT_EXPLOSION, + EFFECT_EXPLOSION_PAS, EFFECT_SPARK_EXPLOSION, EFFECT_EXPLOSION_TRIPMINE, EFFECT_EXPLOSION_DETPACK, @@ -525,6 +530,7 @@ typedef enum { EV_MISSILE_STICK, // eventParm will be the soundindex EV_PLAY_EFFECT, + EV_PLAY_EFFECT_ID, EV_MUTE_SOUND, EV_GENERAL_SOUND, @@ -973,6 +979,8 @@ qboolean BG_InDeathAnim( int anim ); void BG_SaberStartTransAnim( int saberAnimLevel, int anim, float *animSpeed ); +void BG_ForcePowerDrain( playerState_t *ps, forcePowers_t forcePower, int overrideAmt ); + void BG_EvaluateTrajectory( const trajectory_t *tr, int atTime, vec3_t result ); void BG_EvaluateTrajectoryDelta( const trajectory_t *tr, int atTime, vec3_t result ); @@ -988,7 +996,7 @@ void BG_G2PlayerAngles( vec3_t startAngles, vec3_t legs[3], vec3_t legsAngles, i qboolean BG_PlayerTouchesItem( playerState_t *ps, entityState_t *item, int atTime ); -qboolean BG_ParseAnimationFile( const char *filename, animation_t *animations); +qboolean BG_ParseAnimationFile(const char *filename); int BG_GetItemIndexByTag(int tag, int type); diff --git a/CODE-mp/game/bg_saber.c b/CODE-mp/game/bg_saber.c index ec258db..ec335dc 100644 --- a/CODE-mp/game/bg_saber.c +++ b/CODE-mp/game/bg_saber.c @@ -18,6 +18,76 @@ int PM_irand_timesync(int val1, int val2) return i; } + +void BG_ForcePowerDrain( playerState_t *ps, forcePowers_t forcePower, int overrideAmt ) +{ + //take away the power + int drain = overrideAmt; + + if (ps->powerups[PW_FORCE_BOON]) + { + return; + } + + if ( !drain ) + { + drain = forcePowerNeeded[ps->fd.forcePowerLevel[forcePower]][forcePower]; + } + if ( !drain ) + { + return; + } + + if (forcePower == FP_LEVITATION) + { //special case + int jumpDrain = 0; + + if (ps->velocity[2] > 250) + { + jumpDrain = 20; + } + else if (ps->velocity[2] > 200) + { + jumpDrain = 16; + } + else if (ps->velocity[2] > 150) + { + jumpDrain = 12; + } + else if (ps->velocity[2] > 100) + { + jumpDrain = 8; + } + else if (ps->velocity[2] > 50) + { + jumpDrain = 6; + } + else if (ps->velocity[2] > 0) + { + jumpDrain = 4; + } + + if (jumpDrain) + { + jumpDrain /= ps->fd.forcePowerLevel[FP_LEVITATION]; + } + + ps->fd.forcePower -= jumpDrain; + if ( ps->fd.forcePower < 0 ) + { + ps->fd.forcePower = 0; + } + + return; + } + + ps->fd.forcePower -= drain; + if ( ps->fd.forcePower < 0 ) + { + ps->fd.forcePower = 0; + } +} + // Silly, but I'm replacing these macros so they are shorter! #define AFLAG_IDLE (SETANIM_FLAG_NORMAL) #define AFLAG_ACTIVE (/*SETANIM_FLAG_OVERRIDE | */SETANIM_FLAG_HOLD | SETANIM_FLAG_HOLDLESS) @@ -783,16 +853,20 @@ int PM_BrokenParryForParry( int move ) return LS_NONE; } -#define BACK_STAB_DISTANCE 64 +#define BACK_STAB_DISTANCE 128//64 qboolean PM_CanBackstab(void) { trace_t tr; + vec3_t flatAng; vec3_t fwd, back; vec3_t trmins = {-15, -15, -8}; vec3_t trmaxs = {15, 15, 8}; - AngleVectors(pm->ps->viewangles, fwd, 0, 0); + VectorCopy(pm->ps->viewangles, flatAng); + flatAng[PITCH] = 0; + + AngleVectors(flatAng, fwd, 0, 0); back[0] = pm->ps->origin[0] - fwd[0]*BACK_STAB_DISTANCE; back[1] = pm->ps->origin[1] - fwd[1]*BACK_STAB_DISTANCE; @@ -866,15 +940,19 @@ saberMoveName_t PM_SaberFlipOverAttackMove(trace_t *tr) } } -#define FLIPHACK_DISTANCE 128 +#define FLIPHACK_DISTANCE 200 qboolean PM_SomeoneInFront(trace_t *tr) { //Also a very simplified version of the sp counterpart + vec3_t flatAng; vec3_t fwd, back; vec3_t trmins = {-15, -15, -8}; vec3_t trmaxs = {15, 15, 8}; - AngleVectors(pm->ps->viewangles, fwd, 0, 0); + VectorCopy(pm->ps->viewangles, flatAng); + flatAng[PITCH] = 0; + + AngleVectors(flatAng, fwd, 0, 0); back[0] = pm->ps->origin[0] + fwd[0]*FLIPHACK_DISTANCE; back[1] = pm->ps->origin[1] + fwd[1]*FLIPHACK_DISTANCE; @@ -1184,7 +1262,7 @@ void PM_WeaponLightsaber(void) if (pm->ps->weaponstate == WEAPON_READY || pm->ps->weaponstate == WEAPON_IDLE) { - if (pm->ps->saberMove != LS_READY) + if (pm->ps->saberMove != LS_READY && pm->ps->weaponTime <= 0 && !pm->ps->saberBlocked) { PM_SetSaberMove( LS_READY ); } diff --git a/CODE-mp/game/g_active.c b/CODE-mp/game/g_active.c index 4473d7b..8c7227d 100644 --- a/CODE-mp/game/g_active.c +++ b/CODE-mp/game/g_active.c @@ -730,7 +730,7 @@ void ClientEvents( gentity_t *ent, int oldEventSequence ) { case EV_FALL: case EV_ROLL: { - int delta = ent->s.eventParm; + int delta = client->ps.eventParms[ i & (MAX_PS_EVENTS-1) ]; if (ent->client && ent->client->ps.fallingToDeath) { @@ -1331,7 +1331,7 @@ void ClientThink_real( gentity_t *ent ) { pm.pmove_fixed = pmove_fixed.integer | client->pers.pmoveFixed; pm.pmove_msec = pmove_msec.integer; - pm.animations = client->animations;//NULL; + pm.animations = bgGlobalAnimations;//NULL; pm.gametype = g_gametype.integer; @@ -1393,6 +1393,122 @@ void ClientThink_real( gentity_t *ent ) { Pmove (&pm); + switch(pm.cmd.generic_cmd) + { + case 0: + break; + case GENCMD_SABERSWITCH: + Cmd_ToggleSaber_f(ent); + break; + case GENCMD_ENGAGE_DUEL: + Cmd_EngageDuel_f(ent); + break; + case GENCMD_FORCE_HEAL: + ForceHeal(ent); + break; + case GENCMD_FORCE_SPEED: + ForceSpeed(ent, 0); + break; + case GENCMD_FORCE_THROW: + ForceThrow(ent, qfalse); + break; + case GENCMD_FORCE_PULL: + ForceThrow(ent, qtrue); + break; + case GENCMD_FORCE_DISTRACT: + ForceTelepathy(ent); + break; + case GENCMD_FORCE_RAGE: + ForceRage(ent); + break; + case GENCMD_FORCE_PROTECT: + ForceProtect(ent); + break; + case GENCMD_FORCE_ABSORB: + ForceAbsorb(ent); + break; + case GENCMD_FORCE_HEALOTHER: + ForceTeamHeal(ent); + break; + case GENCMD_FORCE_FORCEPOWEROTHER: + ForceTeamForceReplenish(ent); + break; + case GENCMD_FORCE_SEEING: + ForceSeeing(ent); + break; + case GENCMD_USE_SEEKER: + if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_SEEKER)) && + G_ItemUsable(&ent->client->ps, HI_SEEKER) ) + { + ItemUse_Seeker(ent); + G_AddEvent(ent, EV_USE_ITEM0+HI_SEEKER, 0); + ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_SEEKER); + } + break; + case GENCMD_USE_FIELD: + if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_SHIELD)) && + G_ItemUsable(&ent->client->ps, HI_SHIELD) ) + { + ItemUse_Shield(ent); + G_AddEvent(ent, EV_USE_ITEM0+HI_SHIELD, 0); + ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_SHIELD); + } + break; + case GENCMD_USE_BACTA: + if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_MEDPAC)) && + G_ItemUsable(&ent->client->ps, HI_MEDPAC) ) + { + ItemUse_MedPack(ent); + G_AddEvent(ent, EV_USE_ITEM0+HI_MEDPAC, 0); + ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_MEDPAC); + } + break; + case GENCMD_USE_ELECTROBINOCULARS: + if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_BINOCULARS)) && + G_ItemUsable(&ent->client->ps, HI_BINOCULARS) ) + { + ItemUse_Binoculars(ent); + if (ent->client->ps.zoomMode == 0) + { + G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 1); + } + else + { + G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 2); + } + } + break; + case GENCMD_ZOOM: + if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_BINOCULARS)) && + G_ItemUsable(&ent->client->ps, HI_BINOCULARS) ) + { + ItemUse_Binoculars(ent); + if (ent->client->ps.zoomMode == 0) + { + G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 1); + } + else + { + G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 2); + } + } + break; + case GENCMD_USE_SENTRY: + if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_SENTRY_GUN)) && + G_ItemUsable(&ent->client->ps, HI_SENTRY_GUN) ) + { + ItemUse_Sentry(ent); + G_AddEvent(ent, EV_USE_ITEM0+HI_SENTRY_GUN, 0); + ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_SENTRY_GUN); + } + break; + case GENCMD_SABERATTACKCYCLE: + Cmd_SaberAttackCycle_f(ent); + break; + default: + break; + } + // save results of pmove if ( ent->client->ps.eventSequence != oldEventSequence ) { ent->eventTime = level.time; diff --git a/CODE-mp/game/g_bot.c b/CODE-mp/game/g_bot.c index c5d9be6..889d0a3 100644 --- a/CODE-mp/game/g_bot.c +++ b/CODE-mp/game/g_bot.c @@ -158,8 +158,52 @@ int G_GetMapTypeBits(char *type) return typeBits; } +qboolean G_DoesMapSupportGametype(const char *mapname, int gametype) +{ + int typeBits = 0; + int thisLevel = -1; + int n = 0; + char *type = NULL; + + if (!g_arenaInfos[0]) + { + return qfalse; + } + + if (!mapname || !mapname[0]) + { + return qfalse; + } + + for( n = 0; n < g_numArenas; n++ ) + { + type = Info_ValueForKey( g_arenaInfos[n], "map" ); + + if (Q_stricmp(mapname, type) == 0) + { + thisLevel = n; + break; + } + } + + if (thisLevel == -1) + { + return qfalse; + } + + type = Info_ValueForKey(g_arenaInfos[thisLevel], "type"); + + typeBits = G_GetMapTypeBits(type); + if (typeBits & (1 << gametype)) + { //the map in question supports the gametype in question, so.. + return qtrue; + } + + return qfalse; +} + //rww - auto-obtain nextmap. I could've sworn Q3 had something like this, but I guess not. -void G_RefreshNextMap(int gametype) +const char *G_RefreshNextMap(int gametype, qboolean forced) { int typeBits = 0; int thisLevel = 0; @@ -169,14 +213,14 @@ void G_RefreshNextMap(int gametype) qboolean loopingUp = qfalse; vmCvar_t mapname; - if (!g_autoMapCycle.integer) + if (!g_autoMapCycle.integer && !forced) { - return; + return NULL; } if (!g_arenaInfos[0]) { - return; + return NULL; } trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM ); @@ -229,6 +273,8 @@ void G_RefreshNextMap(int gametype) type = Info_ValueForKey( g_arenaInfos[desiredMap], "map" ); trap_Cvar_Set( "nextmap", va("map %s", type)); } + + return Info_ValueForKey( g_arenaInfos[desiredMap], "map" ); } /* @@ -270,7 +316,7 @@ static void G_LoadArenas( void ) { Info_SetValueForKey( g_arenaInfos[n], "num", va( "%i", n ) ); } - G_RefreshNextMap(g_gametype.integer); + G_RefreshNextMap(g_gametype.integer, qfalse); } diff --git a/CODE-mp/game/g_client.c b/CODE-mp/game/g_client.c index f54bc57..227eef5 100644 --- a/CODE-mp/game/g_client.c +++ b/CODE-mp/game/g_client.c @@ -81,6 +81,8 @@ void ThrowSaberToAttacker(gentity_t *self, gentity_t *attacker) return; } + trap_SetConfigstring ( CS_CLIENT_JEDIMASTER, "-1" ); + if (attacker && attacker->client && self->client->ps.saberInFlight) { //someone killed us and we had the saber thrown, so actually move this saber to the saber location //if we killed ourselves with saber thrown, however, same suicide rules of respawning at spawn spot still @@ -211,6 +213,9 @@ void JMSaberTouch(gentity_t *self, gentity_t *other, trace_t *trace) other->s.weapon = WP_SABER; G_AddEvent(other, EV_BECOME_JEDIMASTER, 0); + // Track the jedi master + trap_SetConfigstring ( CS_CLIENT_JEDIMASTER, va("%i", other->s.number ) ); + if (g_spawnInvulnerability.integer) { other->client->ps.eFlags |= EF_INVULNERABLE; @@ -973,10 +978,22 @@ void SetupGameGhoul2Model(gclient_t *client, char *modelname) */ //rww - just load the "standard" model for the server" - Com_sprintf( afilename, sizeof( afilename ), "models/players/kyle/model.glm" ); - handle = trap_G2API_InitGhoul2Model(&client->ghoul2, afilename, 0, 0, -20, 0, 0); + if (!precachedKyle) + { + Com_sprintf( afilename, sizeof( afilename ), "models/players/kyle/model.glm" ); + handle = trap_G2API_InitGhoul2Model(&precachedKyle, afilename, 0, 0, -20, 0, 0); - if (handle<0) + if (handle<0) + { + return; + } + } + + if (precachedKyle && trap_G2_HaveWeGhoul2Models(precachedKyle)) + { + trap_G2API_DuplicateGhoul2Instance(precachedKyle, &client->ghoul2); + } + else { return; } @@ -985,38 +1002,41 @@ void SetupGameGhoul2Model(gclient_t *client, char *modelname) GLAName[0] = 0; - //get the location of the animation.cfg - //GLAName = trap_G2API_GetGLAName( client->ghoul2, 0); - trap_G2API_GetGLAName( client->ghoul2, 0, GLAName); - - if (!GLAName[0]) + if (!BGPAFtextLoaded) { - if (!BG_ParseAnimationFile("models/players/_humanoid/animation.cfg", client->animations)) + //get the location of the animation.cfg + //GLAName = trap_G2API_GetGLAName( client->ghoul2, 0); + trap_G2API_GetGLAName( client->ghoul2, 0, GLAName); + + if (!GLAName[0]) { - Com_Printf( "Failed to load animation file %s\n", afilename ); + if (!BG_ParseAnimationFile("models/players/_humanoid/animation.cfg")) + { + Com_Printf( "Failed to load animation file %s\n", afilename ); + return; + } return; } - return; - } - Q_strncpyz( afilename, GLAName, sizeof( afilename )); - slash = Q_strrchr( afilename, '/' ); - if ( slash ) - { - strcpy(slash, "/animation.cfg"); - } // Now afilename holds just the path to the animation.cfg - else - { // Didn't find any slashes, this is a raw filename right in base (whish isn't a good thing) - return; - } - - // Try to load the animation.cfg for this model then. - if ( !BG_ParseAnimationFile( afilename, client->animations ) ) - { // The GLA's animations failed - if (!BG_ParseAnimationFile("models/players/_humanoid/animation.cfg", client->animations)) + Q_strncpyz( afilename, GLAName, sizeof( afilename )); + slash = Q_strrchr( afilename, '/' ); + if ( slash ) { - Com_Printf( "Failed to load animation file %s\n", afilename ); + strcpy(slash, "/animation.cfg"); + } // Now afilename holds just the path to the animation.cfg + else + { // Didn't find any slashes, this is a raw filename right in base (whish isn't a good thing) return; } + + // Try to load the animation.cfg for this model then. + if ( !BG_ParseAnimationFile( afilename ) ) + { // The GLA's animations failed + if (!BG_ParseAnimationFile("models/players/_humanoid/animation.cfg")) + { + Com_Printf( "Failed to load animation file %s\n", afilename ); + return; + } + } } trap_G2API_AddBolt(client->ghoul2, 0, "*r_hand"); @@ -1459,7 +1479,6 @@ void ClientSpawn(gentity_t *ent) { // char userinfo[MAX_INFO_STRING]; forcedata_t savedForce; void *ghoul2save; - animation_t animations[MAX_TOTALANIMATIONS]; int saveSaberNum = ENTITYNUM_NONE; index = ent - g_entities; @@ -1543,23 +1562,10 @@ void ClientSpawn(gentity_t *ent) { saveSaberNum = client->ps.saberEntityNum; - i = 0; - while (i < MAX_TOTALANIMATIONS) - { - animations[i] = client->animations[i]; - i++; - } - memset (client, 0, sizeof(*client)); // bk FIXME: Com_Memset? //rww - Don't wipe the ghoul2 instance or the animation data client->ghoul2 = ghoul2save; - i = 0; - while (i < MAX_TOTALANIMATIONS) - { - client->animations[i] = animations[i]; - i++; - } //or the saber ent num client->ps.saberEntityNum = saveSaberNum; diff --git a/CODE-mp/game/g_cmds.c b/CODE-mp/game/g_cmds.c index 2ee03df..eda581f 100644 --- a/CODE-mp/game/g_cmds.c +++ b/CODE-mp/game/g_cmds.c @@ -816,6 +816,10 @@ Cmd_Team_f */ void Cmd_ForceChanged_f( gentity_t *ent ) { + char fpChStr[1024]; + const char *buf; + int i = 0; + int ccount = 0; // Cmd_Kill_f(ent); if (ent->client->sess.sessionTeam == TEAM_SPECTATOR) { //if it's a spec, just make the changes now @@ -823,7 +827,23 @@ void Cmd_ForceChanged_f( gentity_t *ent ) WP_InitForcePowers( ent ); return; } - trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "FORCEPOWERCHANGED")) ); + + buf = G_GetStripEdString("SVINGAME", "FORCEPOWERCHANGED"); + + strcpy(fpChStr, buf); + + while (i < 1024 && fpChStr[i]) + { + if (ccount > 24 && fpChStr[i] == ' ') + { + fpChStr[i] = '\n'; + ccount = 0; + } + ccount++; + i++; + } + + trap_SendServerCommand( ent-g_entities, va("cp \"%s\n\"", fpChStr) ); ent->client->ps.fd.forceDoInit = 1; } @@ -1412,6 +1432,12 @@ void Cmd_CallVote_f( gentity_t *ent ) { // this allows a player to change maps, but not upset the map rotation char s[MAX_STRING_CHARS]; + if (!G_DoesMapSupportGametype(arg2, trap_Cvar_VariableIntegerValue("g_gametype"))) + { + trap_SendServerCommand( ent-g_entities, "print \"You can't vote for this map, it isn't supported by the current gametype.\n\"" ); + return; + } + trap_Cvar_VariableStringBuffer( "nextmap", s, sizeof(s) ); if (*s) { Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %s; set nextmap \"%s\"", arg1, arg2, s ); @@ -1753,6 +1779,11 @@ int G_ItemUsable(playerState_t *ps, int forcedUse) return 0; } + if (ps->stats[STAT_HEALTH] <= 0) + { + return 0; + } + return 1; case HI_SEEKER: if (ps->eFlags & EF_SEEKERDRONE) @@ -1911,13 +1942,13 @@ void Cmd_SaberAttackCycle_f(gentity_t *ent) switch ( selectLevel ) { case FORCE_LEVEL_1: - trap_SendServerCommand( ent-g_entities, "print \"Saber Attack Set: fast\n\"" ); + trap_SendServerCommand( ent-g_entities, va("print \"Saber Attack Set: %sfast\n\"", S_COLOR_GREEN) ); break; case FORCE_LEVEL_2: - trap_SendServerCommand( ent-g_entities, "print \"Saber Attack Set: medium\n\"" ); + trap_SendServerCommand( ent-g_entities, va("print \"Saber Attack Set: %smedium\n\"", S_COLOR_YELLOW) ); break; case FORCE_LEVEL_3: - trap_SendServerCommand( ent-g_entities, "print \"Saber Attack Set: strong\n\"" ); + trap_SendServerCommand( ent-g_entities, va("print \"Saber Attack Set: %sstrong\n\"", S_COLOR_RED) ); break; } @@ -2153,8 +2184,100 @@ void ClientCommand( int clientNum ) { } // ignore all other commands when at intermission - if (level.intermissiontime) { - Cmd_Say_f (ent, qfalse, qtrue); + if (level.intermissiontime) + { + qboolean giveError = qfalse; + + if (!Q_stricmp(cmd, "give")) + { + giveError = qtrue; + } + else if (!Q_stricmp(cmd, "god")) + { + giveError = qtrue; + } + else if (!Q_stricmp(cmd, "notarget")) + { + giveError = qtrue; + } + else if (!Q_stricmp(cmd, "noclip")) + { + giveError = qtrue; + } + else if (!Q_stricmp(cmd, "kill")) + { + giveError = qtrue; + } + else if (!Q_stricmp(cmd, "teamtask")) + { + giveError = qtrue; + } + else if (!Q_stricmp(cmd, "levelshot")) + { + giveError = qtrue; + } + else if (!Q_stricmp(cmd, "follow")) + { + giveError = qtrue; + } + else if (!Q_stricmp(cmd, "follownext")) + { + giveError = qtrue; + } + else if (!Q_stricmp(cmd, "followprev")) + { + giveError = qtrue; + } + else if (!Q_stricmp(cmd, "team")) + { + giveError = qtrue; + } + else if (!Q_stricmp(cmd, "forcechanged")) + { //special case: still update force change + Cmd_ForceChanged_f (ent); + return; + } + else if (!Q_stricmp(cmd, "where")) + { + giveError = qtrue; + } + else if (!Q_stricmp(cmd, "callvote")) + { + giveError = qtrue; + } + else if (!Q_stricmp(cmd, "vote")) + { + giveError = qtrue; + } + else if (!Q_stricmp(cmd, "callteamvote")) + { + giveError = qtrue; + } + else if (!Q_stricmp(cmd, "teamvote")) + { + giveError = qtrue; + } + else if (!Q_stricmp(cmd, "gc")) + { + giveError = qtrue; + } + else if (!Q_stricmp(cmd, "setviewpos")) + { + giveError = qtrue; + } + else if (!Q_stricmp(cmd, "stats")) + { + giveError = qtrue; + } + + if (giveError) + { + trap_SendServerCommand( clientNum, va("print \"You cannot perform this task (%s) during the intermission.\n\"", cmd ) ); + } + else + { + Cmd_Say_f (ent, qfalse, qtrue); + } return; } @@ -2200,112 +2323,6 @@ void ClientCommand( int clientNum ) { Cmd_SetViewpos_f( ent ); else if (Q_stricmp (cmd, "stats") == 0) Cmd_Stats_f( ent ); - else if (Q_stricmp (cmd, "sv_saberswitch") == 0) - Cmd_ToggleSaber_f(ent); - else if (Q_stricmp (cmd, "engage_duel") == 0) - Cmd_EngageDuel_f(ent); - else if (Q_stricmp (cmd, "force_heal") == 0) - ForceHeal(ent); - else if (Q_stricmp (cmd, "force_speed") == 0) - ForceSpeed(ent, 0); - else if (Q_stricmp (cmd, "force_throw") == 0) - ForceThrow( ent, qfalse ); - else if (Q_stricmp (cmd, "force_pull") == 0) - ForceThrow( ent, qtrue ); - else if (Q_stricmp (cmd, "force_distract") == 0) - ForceTelepathy( ent ); - else if (Q_stricmp (cmd, "force_rage") == 0) - ForceRage(ent); - else if (Q_stricmp (cmd, "force_protect") == 0) - ForceProtect(ent); - else if (Q_stricmp (cmd, "force_absorb") == 0) - ForceAbsorb(ent); - else if (Q_stricmp (cmd, "force_healother") == 0) - ForceTeamHeal(ent); - else if (Q_stricmp (cmd, "force_forcepowerother") == 0) - ForceTeamForceReplenish(ent); - else if (Q_stricmp (cmd, "force_seeing") == 0) - ForceSeeing(ent); - else if (Q_stricmp (cmd, "use_seeker") == 0) - { - if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_SEEKER)) && - G_ItemUsable(&ent->client->ps, HI_SEEKER) ) - { - ItemUse_Seeker(ent); - G_AddEvent(ent, EV_USE_ITEM0+HI_SEEKER, 0); - ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_SEEKER); - } - } - else if (Q_stricmp (cmd, "use_field") == 0) - { - if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_SHIELD)) && - G_ItemUsable(&ent->client->ps, HI_SHIELD) ) - { - ItemUse_Shield(ent); - G_AddEvent(ent, EV_USE_ITEM0+HI_SHIELD, 0); - ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_SHIELD); - } - } - else if (Q_stricmp (cmd, "use_bacta") == 0) - { - if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_MEDPAC)) && - G_ItemUsable(&ent->client->ps, HI_MEDPAC) ) - { - ItemUse_MedPack(ent); - G_AddEvent(ent, EV_USE_ITEM0+HI_MEDPAC, 0); - ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_MEDPAC); - } - } - else if (Q_stricmp (cmd, "use_electrobinoculars") == 0) - { - if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_BINOCULARS)) && - G_ItemUsable(&ent->client->ps, HI_BINOCULARS) ) - { - ItemUse_Binoculars(ent); - if (ent->client->ps.zoomMode == 0) - { - G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 1); - } - else - { - G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 2); - } - } - } - else if (Q_stricmp (cmd, "zoom") == 0) - { //cheap way of doing this - if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_BINOCULARS)) && - G_ItemUsable(&ent->client->ps, HI_BINOCULARS) ) - { - ItemUse_Binoculars(ent); - if (ent->client->ps.zoomMode == 0) - { - G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 1); - } - else - { - G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 2); - } - } - } - else if (Q_stricmp (cmd, "use_sentry") == 0) - { - if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_SENTRY_GUN)) && - G_ItemUsable(&ent->client->ps, HI_SENTRY_GUN) ) - { - ItemUse_Sentry(ent); - G_AddEvent(ent, EV_USE_ITEM0+HI_SENTRY_GUN, 0); - ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_SENTRY_GUN); - } - } - else if (Q_stricmp (cmd, "bot_order") == 0) - { - BotOrder(ent, atoi(ConcatArgs( 1 )), atoi(ConcatArgs( 2 ))); - } - else if (Q_stricmp (cmd, "saberAttackCycle") == 0) - { - Cmd_SaberAttackCycle_f(ent); - } else if (Q_stricmp(cmd, "#mm") == 0 && CheatsOk( ent )) { G_PlayerBecomeATST(ent); @@ -2361,5 +2378,14 @@ void ClientCommand( int clientNum ) { } */ else - trap_SendServerCommand( clientNum, va("print \"unknown cmd %s\n\"", cmd ) ); + { + if (Q_stricmp(cmd, "addbot") == 0) + { //because addbot isn't a recognized command unless you're the server, but it is in the menus regardless + trap_SendServerCommand( clientNum, va("print \"You can only add bots as the server.\n\"" ) ); + } + else + { + trap_SendServerCommand( clientNum, va("print \"unknown cmd %s\n\"", cmd ) ); + } + } } diff --git a/CODE-mp/game/g_items.c b/CODE-mp/game/g_items.c index 9c28744..4a3f99d 100644 --- a/CODE-mp/game/g_items.c +++ b/CODE-mp/game/g_items.c @@ -868,7 +868,7 @@ void turret_die(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int // hack the effect angle so that explode death can orient the effect properly VectorSet( self->s.angles, 0, 0, 1 ); - G_PlayEffect(EFFECT_EXPLOSION, self->s.pos.trBase, self->s.angles); + G_PlayEffect(EFFECT_EXPLOSION_PAS, self->s.pos.trBase, self->s.angles); G_RadiusDamage(self->s.pos.trBase, &g_entities[self->boltpoint3], 30, 256, self, MOD_UNKNOWN); g_entities[self->boltpoint3].client->ps.fd.sentryDeployed = qfalse; @@ -910,6 +910,8 @@ void SP_PAS( gentity_t *base ) base->takedamage = qtrue; base->die = turret_die; + base->physicsObject = qtrue; + G_Sound( base, CHAN_BODY, G_SoundIndex( "sound/chars/turret/startup.wav" )); } @@ -1512,6 +1514,10 @@ gentity_t *LaunchItem( gitem_t *item, vec3_t origin, vec3_t velocity ) { dropped->s.eType = ET_ITEM; dropped->s.modelindex = item - bg_itemlist; // store item number in modelindex + if (dropped->s.modelindex < 0) + { + dropped->s.modelindex = 0; + } dropped->s.modelindex2 = 1; // This is non-zero is it's a dropped item dropped->classname = item->classname; @@ -1571,6 +1577,8 @@ gentity_t *LaunchItem( gitem_t *item, vec3_t origin, vec3_t velocity ) { dropped->s.angles[ROLL] = -90; } + dropped->physicsObject = qtrue; + trap_LinkEntity (dropped); return dropped; diff --git a/CODE-mp/game/g_local.h b/CODE-mp/game/g_local.h index 60d7e18..a12e610 100644 --- a/CODE-mp/game/g_local.h +++ b/CODE-mp/game/g_local.h @@ -85,6 +85,9 @@ typedef enum //============================================================================ +extern void *precachedKyle; +extern void *g2SaberInstance; + typedef struct gentity_s gentity_t; typedef struct gclient_s gclient_t; @@ -394,7 +397,6 @@ struct gclient_s { char *areabits; - animation_t animations[MAX_TOTALANIMATIONS]; void *ghoul2; // In parallel with the centity, there is a corresponding ghoul2 model for players. // This is an instance that is maintained on the server side that is used for // determining saber position and per-poly collision @@ -520,6 +522,10 @@ void BroadcastTeamChange( gclient_t *client, int oldTeam ); void SetTeam( gentity_t *ent, char *s ); void Cmd_FollowCycle_f( gentity_t *ent, int dir ); void Cmd_SaberAttackCycle_f(gentity_t *ent); +int G_ItemUsable(playerState_t *ps, int forcedUse); +void Cmd_ToggleSaber_f(gentity_t *ent); +void Cmd_EngageDuel_f(gentity_t *ent); + gentity_t *G_GetDuelWinner(gclient_t *client); // @@ -556,6 +562,7 @@ void SaveRegisteredItems( void ); // int G_ModelIndex( char *name ); int G_SoundIndex( char *name ); +int G_EffectIndex( char *name ); void G_TeamCommand( team_t team, char *cmd ); void G_KillBox (gentity_t *ent); gentity_t *G_Find (gentity_t *from, int fieldofs, const char *match); @@ -829,7 +836,8 @@ qboolean G_BotConnect( int clientNum, qboolean restart ); void Svcmd_AddBot_f( void ); void Svcmd_BotList_f( void ); void BotInterbreedEndMatch( void ); -void G_RefreshNextMap(int gametype); +qboolean G_DoesMapSupportGametype(const char *mapname, int gametype); +const char *G_RefreshNextMap(int gametype, qboolean forced); // w_saber.c qboolean HasSetSaberOnly(void); diff --git a/CODE-mp/game/g_main.c b/CODE-mp/game/g_main.c index 78f9030..69bd059 100644 --- a/CODE-mp/game/g_main.c +++ b/CODE-mp/game/g_main.c @@ -111,7 +111,7 @@ static cvarTable_t gameCvarTable[] = { // change anytime vars { &g_ff_objectives, "g_ff_objectives", "0", /*CVAR_SERVERINFO |*/ CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue }, - { &g_autoMapCycle, "g_autoMapCycle", "1", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue }, + { &g_autoMapCycle, "g_autoMapCycle", "0", CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue }, { &g_dmflags, "dmflags", "0", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue }, { &g_maxForceRank, "g_maxForceRank", "0", CVAR_SERVERINFO | CVAR_USERINFO | CVAR_LATCH, 0, qfalse }, @@ -520,6 +520,10 @@ void G_InitGame( int levelTime, int randomSeed, int restart ) { if( g_gametype.integer >= GT_TEAM ) { G_CheckTeamItems(); } + else if ( g_gametype.integer == GT_JEDIMASTER ) + { + trap_SetConfigstring ( CS_CLIENT_JEDIMASTER, "-1" ); + } SaveRegisteredItems(); @@ -1614,7 +1618,20 @@ void CheckVote( void ) { if (level.votingGametype) { - G_RefreshNextMap(level.votingGametypeTo); + if (trap_Cvar_VariableIntegerValue("g_gametype") != level.votingGametypeTo) + { //If we're voting to a different game type, be sure to refresh all the map stuff + const char *nextMap = G_RefreshNextMap(level.votingGametypeTo, qtrue); + + if (nextMap && nextMap[0]) + { + trap_SendConsoleCommand( EXEC_APPEND, va("map %s\n", nextMap ) ); + } + + } + else + { //otherwise, just leave the map until a restart + G_RefreshNextMap(level.votingGametypeTo, qfalse); + } level.votingGametype = qfalse; level.votingGametypeTo = 0; } diff --git a/CODE-mp/game/g_misc.c b/CODE-mp/game/g_misc.c index 8a742a0..446a3c8 100644 --- a/CODE-mp/game/g_misc.c +++ b/CODE-mp/game/g_misc.c @@ -540,6 +540,8 @@ void SP_misc_holocron(gentity_t *ent) } } + ent->s.isJediMaster = qtrue; + VectorSet( ent->r.maxs, 8, 8, 8 ); VectorSet( ent->r.mins, -8, -8, -8 ); @@ -1360,3 +1362,175 @@ int G_PlayerBecomeATST(gentity_t *ent) return 1; } + +/*QUAKED fx_runner (0 0 1) (-8 -8 -8) (8 8 8) STARTOFF ONESHOT + STARTOFF - effect starts off, toggles on/off when used + ONESHOT - effect fires only when used + + "angles" - 3-float vector, angle the effect should play (unless fxTarget is supplied) + "fxFile" - name of the effect file to play + "fxTarget" - aim the effect toward this object, otherwise defaults to up + "target" - uses its target when the fx gets triggered + "delay" - how often to call the effect, don't over-do this ( default 400 ) + note that it has to send an event each time it plays, so don't kill bandwidth or I will cry + "random" - random amount of time to add to delay, ( default 0, 200 = 0ms to 200ms ) +*/ +#define FX_RUNNER_RESERVED 0x800000 +#define FX_ENT_RADIUS 8 //32 + +//---------------------------------------------------------- +void fx_runner_think( gentity_t *ent ) +{ + // call the effect with the desired position and orientation + G_AddEvent( ent, EV_PLAY_EFFECT_ID, ent->bolt_Head ); + + ent->nextthink = level.time + ent->delay + random() * ent->random; + + if ( ent->target ) + { + // let our target know that we have spawned an effect + G_UseTargets( ent, ent ); + } +} + +//---------------------------------------------------------- +void fx_runner_use( gentity_t *self, gentity_t *other, gentity_t *activator ) +{ + if ( self->spawnflags & 2 ) // ONESHOT + { + // call the effect with the desired position and orientation, as a safety thing, + // make sure we aren't thinking at all. + fx_runner_think( self ); + self->nextthink = -1; + + if ( self->target ) + { + // let our target know that we have spawned an effect + G_UseTargets( self, self ); + } + } + else + { + // ensure we are working with the right think function + self->think = fx_runner_think; + + // toggle our state + if ( self->nextthink == -1 ) + { + // NOTE: we fire the effect immediately on use, the fx_runner_think func will set + // up the nextthink time. + fx_runner_think( self ); + } + else + { + // turn off for now + self->nextthink = -1; + } + } +} + +//---------------------------------------------------------- +void fx_runner_link( gentity_t *ent ) +{ + vec3_t dir; + + if ( ent->roffname && ent->roffname[0] ) + { + // try to use the target to override the orientation + gentity_t *target = NULL; + + target = G_Find( target, FOFS(targetname), ent->roffname ); + + if ( !target ) + { + // Bah, no good, dump a warning, but continue on and use the UP vector + Com_Printf( "fx_runner_link: target specified but not found: %s\n", ent->roffname ); + Com_Printf( " -assuming UP orientation.\n" ); + } + else + { + // Our target is valid so let's override the default UP vector + VectorSubtract( target->s.origin, ent->s.origin, dir ); + VectorNormalize( dir ); + vectoangles( dir, ent->s.angles ); + } + } + + // don't really do anything with this right now other than do a check to warn the designers if the target is bogus + if ( ent->target ) + { + gentity_t *target = NULL; + + target = G_Find( target, FOFS(targetname), ent->target ); + + if ( !target ) + { + // Target is bogus, but we can still continue + Com_Printf( "fx_runner_link: target was specified but is not valid: %s\n", ent->target ); + } + } + + G_SetAngles( ent, ent->s.angles ); + + if ( ent->spawnflags & 1 || ent->spawnflags & 2 ) // STARTOFF || ONESHOT + { + // We won't even consider thinking until we are used + ent->nextthink = -1; + } + else + { + // Let's get to work right now! + ent->think = fx_runner_think; + ent->nextthink = level.time + 100; // wait a small bit, then start working + } +} + +//---------------------------------------------------------- +void SP_fx_runner( gentity_t *ent ) +{ + char *fxFile; + + // Get our defaults + G_SpawnInt( "delay", "400", &ent->delay ); + G_SpawnFloat( "random", "0", &ent->random ); + + if (!ent->s.angles[0] && !ent->s.angles[1] && !ent->s.angles[2]) + { + // didn't have angles, so give us the default of up + VectorSet( ent->s.angles, -90, 0, 0 ); + } + + // make us useable if we can be targeted + if ( ent->targetname ) + { + ent->use = fx_runner_use; + } + + G_SpawnString( "fxFile", "", &fxFile ); + + G_SpawnString( "fxTarget", "", &ent->roffname ); + + if ( !fxFile || !fxFile[0] ) + { + Com_Printf( S_COLOR_RED"ERROR: fx_runner %s at %s has no fxFile specified\n", ent->targetname, vtos(ent->s.origin) ); + G_FreeEntity( ent ); + return; + } + + // Try and associate an effect file, unfortunately we won't know if this worked or not + // until the CGAME trys to register it... + ent->bolt_Head = G_EffectIndex( fxFile ); + //It is dirty, yes. But no one likes adding things to the entity structure. + + // Give us a bit of time to spawn in the other entities, since we may have to target one of 'em + ent->think = fx_runner_link; + ent->nextthink = level.time + 300; + + // Save our position and link us up! + G_SetOrigin( ent, ent->s.origin ); + + VectorSet( ent->r.maxs, FX_ENT_RADIUS, FX_ENT_RADIUS, FX_ENT_RADIUS ); + VectorScale( ent->r.maxs, -1, ent->r.mins ); + + trap_LinkEntity( ent ); +} diff --git a/CODE-mp/game/g_missile.c b/CODE-mp/game/g_missile.c index 90a5f8b..3904bac 100644 --- a/CODE-mp/game/g_missile.c +++ b/CODE-mp/game/g_missile.c @@ -350,6 +350,25 @@ void G_MissileImpact( gentity_t *ent, trace_t *trace ) { } */ + if (other->r.contents & CONTENTS_LIGHTSABER) + { //hit this person's saber, so.. + gentity_t *otherOwner = &g_entities[other->r.ownerNum]; + + if (otherOwner->takedamage && otherOwner->client && otherOwner->client->ps.duelInProgress && + otherOwner->client->ps.duelIndex != ent->r.ownerNum) + { + goto killProj; + } + } + else + { + if (other->takedamage && other->client && other->client->ps.duelInProgress && + other->client->ps.duelIndex != ent->r.ownerNum) + { + goto killProj; + } + } + if (other->takedamage && other->client && ent->s.weapon != WP_ROCKET_LAUNCHER && ent->s.weapon != WP_THERMAL && diff --git a/CODE-mp/game/g_mover.c b/CODE-mp/game/g_mover.c index acf4e2d..f68fb22 100644 --- a/CODE-mp/game/g_mover.c +++ b/CODE-mp/game/g_mover.c @@ -728,7 +728,14 @@ void Blocked_Door( gentity_t *ent, gentity_t *other ) { } if ( ent->damage ) { - G_Damage( other, ent, ent, NULL, NULL, ent->damage, DAMAGE_NO_ARMOR, MOD_CRUSH ); + if (ent->activator && ent->activator->inuse && ent->activator->client) + { + G_Damage( other, ent->activator, ent->activator, NULL, NULL, ent->damage, DAMAGE_NO_ARMOR, MOD_CRUSH ); + } + else + { + G_Damage( other, ent, ent, NULL, NULL, ent->damage, DAMAGE_NO_ARMOR, MOD_CRUSH ); + } } if ( ent->spawnflags & 4 ) { return; // crushers don't reverse diff --git a/CODE-mp/game/g_spawn.c b/CODE-mp/game/g_spawn.c index 8a3569e..a8d005f 100644 --- a/CODE-mp/game/g_spawn.c +++ b/CODE-mp/game/g_spawn.c @@ -173,6 +173,8 @@ void SP_misc_model_shield_power_converter( gentity_t *ent ); void SP_misc_model_ammo_power_converter( gentity_t *ent ); void SP_misc_model_health_power_converter( gentity_t *ent ); +void SP_fx_runner( gentity_t *ent ); + void SP_misc_holocron(gentity_t *ent); void SP_shooter_blaster( gentity_t *ent ); @@ -259,6 +261,8 @@ spawn_t spawns[] = { {"misc_model_ammo_power_converter", SP_misc_model_ammo_power_converter}, {"misc_model_health_power_converter", SP_misc_model_health_power_converter}, + {"fx_runner", SP_fx_runner}, + {"misc_holocron", SP_misc_holocron}, {"shooter_blaster", SP_shooter_blaster}, @@ -721,6 +725,7 @@ static char *defaultStyles[32][3] = } }; +void *precachedKyle = 0; /*QUAKED worldspawn (0 0 0) ? @@ -740,6 +745,31 @@ void SP_worldspawn( void ) G_Error( "SP_worldspawn: The first entity isn't 'worldspawn'" ); } + //The server will precache the standard model and animations, so that there is no hit + //when the first client connnects. + if (!BGPAFtextLoaded) + { + BG_ParseAnimationFile("models/players/_humanoid/animation.cfg"); + } + + if (!precachedKyle) + { + trap_G2API_InitGhoul2Model(&precachedKyle, "models/players/kyle/model.glm", 0, 0, -20, 0, 0); + } + + if (!g2SaberInstance) + { + trap_G2API_InitGhoul2Model(&g2SaberInstance, "models/weapons2/saber/saber_w.glm", 0, 0, -20, 0, 0); + + if (g2SaberInstance) + { + // indicate we will be bolted to model 0 (ie the player) on bolt 0 (always the right hand) when we get copied + trap_G2API_SetBoltInfo(g2SaberInstance, 0, 0); + // now set up the gun bolt on it + trap_G2API_AddBolt(g2SaberInstance, 0, "*flash"); + } + } + // make some data visible to connecting client trap_SetConfigstring( CS_GAME_VERSION, GAME_VERSION ); diff --git a/CODE-mp/game/g_trigger.c b/CODE-mp/game/g_trigger.c index 0bae9f6..3b46c46 100644 --- a/CODE-mp/game/g_trigger.c +++ b/CODE-mp/game/g_trigger.c @@ -408,6 +408,15 @@ If dmg is set to -1 this brush will use the fade-kill method */ void hurt_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { + if (activator && activator->inuse && activator->client) + { + self->activator = activator; + } + else + { + self->activator = NULL; + } + if ( self->r.linked ) { trap_UnlinkEntity( self ); } else { @@ -468,7 +477,14 @@ void hurt_touch( gentity_t *self, gentity_t *other, trace_t *trace ) { } else { - G_Damage (other, self, self, NULL, NULL, self->damage, dflags, MOD_TRIGGER_HURT); + if (self->activator && self->activator->inuse && self->activator->client) + { + G_Damage (other, self->activator, self->activator, NULL, NULL, self->damage, dflags, MOD_TRIGGER_HURT); + } + else + { + G_Damage (other, self, self, NULL, NULL, self->damage, dflags, MOD_TRIGGER_HURT); + } } } diff --git a/CODE-mp/game/g_utils.c b/CODE-mp/game/g_utils.c index 469af23..2791d8c 100644 --- a/CODE-mp/game/g_utils.c +++ b/CODE-mp/game/g_utils.c @@ -111,6 +111,11 @@ int G_SoundIndex( char *name ) { return G_FindConfigstringIndex (name, CS_SOUNDS, MAX_SOUNDS, qtrue); } +int G_EffectIndex( char *name ) +{ + return G_FindConfigstringIndex (name, CS_EFFECTS, MAX_FX, qtrue); +} + //===================================================================== @@ -807,7 +812,7 @@ void G_MuteSound( int entnum, int channel ) te = G_TempEntity( vec3_origin, EV_MUTE_SOUND ); te->r.svFlags = SVF_BROADCAST; - te->s.eventParm = entnum; + te->s.trickedentindex2 = entnum; te->s.trickedentindex = channel; e = &g_entities[entnum]; diff --git a/CODE-mp/game/g_weapon.c b/CODE-mp/game/g_weapon.c index 0b38841..324a4c7 100644 --- a/CODE-mp/game/g_weapon.c +++ b/CODE-mp/game/g_weapon.c @@ -432,6 +432,15 @@ static void WP_DisruptorMainFire( gentity_t *ent ) traceEnt = &g_entities[tr.entityNum]; + if (traceEnt && traceEnt->client && traceEnt->client->ps.duelInProgress && + traceEnt->client->ps.duelIndex != ent->s.number) + { + VectorCopy( tr.endpos, start ); + ignore = tr.entityNum; + traces++; + continue; + } + if ( Jedi_DodgeEvasion( traceEnt, ent, &tr, G_GetHitLocation(traceEnt, tr.endpos) ) ) {//act like we didn't even hit him VectorCopy( tr.endpos, start ); @@ -447,6 +456,7 @@ static void WP_DisruptorMainFire( gentity_t *ent ) tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_MAIN_SHOT ); VectorCopy( muzzle, tent->s.origin2 ); + tent->s.eventParm = ent->s.number; te = G_TempEntity( tr.endpos, EV_SABER_BLOCK ); VectorCopy(tr.endpos, te->s.origin); @@ -472,6 +482,7 @@ static void WP_DisruptorMainFire( gentity_t *ent ) // always render a shot beam, doing this the old way because I don't much feel like overriding the effect. tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_MAIN_SHOT ); VectorCopy( muzzle, tent->s.origin2 ); + tent->s.eventParm = ent->s.number; traceEnt = &g_entities[tr.entityNum]; @@ -582,6 +593,14 @@ void WP_DisruptorAltFire( gentity_t *ent ) traceEnt = &g_entities[tr.entityNum]; + if (traceEnt && traceEnt->client && traceEnt->client->ps.duelInProgress && + traceEnt->client->ps.duelIndex != ent->s.number) + { + skip = tr.entityNum; + VectorCopy(tr.endpos, start); + continue; + } + if (Jedi_DodgeEvasion(traceEnt, ent, &tr, G_GetHitLocation(traceEnt, tr.endpos))) { skip = tr.entityNum; @@ -597,6 +616,7 @@ void WP_DisruptorAltFire( gentity_t *ent ) tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_SNIPER_SHOT ); VectorCopy( muzzle, tent->s.origin2 ); tent->s.shouldtarget = fullCharge; + tent->s.eventParm = ent->s.number; te = G_TempEntity( tr.endpos, EV_SABER_BLOCK ); VectorCopy(tr.endpos, te->s.origin); @@ -615,6 +635,7 @@ void WP_DisruptorAltFire( gentity_t *ent ) tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_SNIPER_SHOT ); VectorCopy( muzzle, tent->s.origin2 ); tent->s.shouldtarget = fullCharge; + tent->s.eventParm = ent->s.number; // If the beam hits a skybox, etc. it would look foolish to add impact effects if ( render_impact ) @@ -1627,6 +1648,8 @@ THERMAL DETONATOR #define TD_ALT_MIN_CHARGE 0.15f #define TD_ALT_TIME 3000 +void thermalThinkStandard(gentity_t *ent); + //--------------------------------------------------------- void thermalDetonatorExplode( gentity_t *ent ) //--------------------------------------------------------- @@ -1635,7 +1658,9 @@ void thermalDetonatorExplode( gentity_t *ent ) { G_Sound( ent, CHAN_VOICE, G_SoundIndex( "sound/weapons/thermal/warning.wav" ) ); ent->count = 1; - ent->nextthink = level.time + 500; + ent->bolt_Head = level.time + 500; + ent->think = thermalThinkStandard; + ent->nextthink = level.time; ent->r.svFlags |= SVF_BROADCAST;//so everyone hears/sees the explosion? } else @@ -1663,6 +1688,18 @@ void thermalDetonatorExplode( gentity_t *ent ) } } +void thermalThinkStandard(gentity_t *ent) +{ + if (ent->bolt_Head < level.time) + { + ent->think = thermalDetonatorExplode; + ent->nextthink = level.time; + return; + } + + G_RunObject(ent); + ent->nextthink = level.time; +} //--------------------------------------------------------- gentity_t *WP_FireThermalDetonator( gentity_t *ent, qboolean altFire ) @@ -1677,8 +1714,12 @@ gentity_t *WP_FireThermalDetonator( gentity_t *ent, qboolean altFire ) bolt = G_Spawn(); + bolt->physicsObject = qtrue; + bolt->classname = "thermal_detonator"; - bolt->think = thermalDetonatorExplode; + bolt->think = thermalThinkStandard; + bolt->nextthink = level.time; + bolt->touch = touch_NULL; // bolt->mass = 10; // NOTENOTE No mass implementation yet // How 'bout we give this thing a size... @@ -1706,7 +1747,7 @@ gentity_t *WP_FireThermalDetonator( gentity_t *ent, qboolean altFire ) } // normal ones bounce, alt ones explode on impact - bolt->nextthink = level.time + TD_TIME; // How long 'til she blows + bolt->bolt_Head = level.time + TD_TIME; // How long 'til she blows bolt->s.pos.trType = TR_GRAVITY; bolt->parent = ent; bolt->r.ownerNum = ent->s.number; @@ -2141,6 +2182,9 @@ void charge_stick (gentity_t *self, gentity_t *other, trace_t *trace) self->s.pos.trDelta[0] += vNor[0]*(tN[0]*(((float)Q_irand(1, 10))*0.1)); self->s.pos.trDelta[1] += vNor[1]*(tN[1]*(((float)Q_irand(1, 10))*0.1)); self->s.pos.trDelta[2] += vNor[1]*(tN[2]*(((float)Q_irand(1, 10))*0.1)); + + vectoangles(vNor, self->s.angles); + vectoangles(vNor, self->s.apos.trBase); self->touch = charge_stick; return; } @@ -2272,6 +2316,8 @@ void drop_charge (gentity_t *self, vec3_t start, vec3_t dir) bolt->r.contents = MASK_SHOT; bolt->touch = charge_stick; + bolt->physicsObject = qtrue; + bolt->s.genericenemyindex = self->s.number+1024; //rww - so client prediction knows we own this and won't hit it @@ -2463,6 +2509,21 @@ void WP_FireStunBaton( gentity_t *ent, qboolean alt_fire ) tr_ent = &g_entities[tr.entityNum]; + if (tr_ent && tr_ent->takedamage && tr_ent->client) + { + if (tr_ent->client->ps.duelInProgress && + tr_ent->client->ps.duelIndex != ent->s.number) + { + return; + } + + if (ent->client->ps.duelInProgress && + ent->client->ps.duelIndex != tr_ent->s.number) + { + return; + } + } + if ( tr_ent && tr_ent->takedamage ) { G_PlayEffect( EFFECT_STUNHIT, tr.endpos, tr.plane.normal ); diff --git a/CODE-mp/game/game.bat b/CODE-mp/game/game.bat deleted file mode 100644 index 8036ef4..0000000 --- a/CODE-mp/game/game.bat +++ /dev/null @@ -1,99 +0,0 @@ -del /q vm -mkdir vm -cd vm -set cc=lcc -A -DQ3_VM -DMISSIONPACK -S -Wf-target=bytecode -Wf-g -I..\..\cgame -I..\..\game -I..\..\ui %1 - -%cc% ../g_main.c -@if errorlevel 1 goto quit - -%cc% ../g_syscalls.c -@if errorlevel 1 goto quit - -%cc% ../bg_misc.c -@if errorlevel 1 goto quit -%cc% ../bg_lib.c -@if errorlevel 1 goto quit -%cc% ../bg_pmove.c -@if errorlevel 1 goto quit -%cc% ../bg_saber.c -@if errorlevel 1 goto quit -%cc% ../bg_slidemove.c -@if errorlevel 1 goto quit -%cc% ../bg_panimate.c -@if errorlevel 1 goto quit -%cc% ../bg_weapons.c -@if errorlevel 1 goto quit -%cc% ../q_math.c -@if errorlevel 1 goto quit -%cc% ../q_shared.c -@if errorlevel 1 goto quit - -%cc% ../ai_main.c -@if errorlevel 1 goto quit -%cc% ../ai_util.c -@if errorlevel 1 goto quit -%cc% ../ai_wpnav.c -@if errorlevel 1 goto quit - -%cc% ../g_active.c -@if errorlevel 1 goto quit - -%cc% ../g_arenas.c -@if errorlevel 1 goto quit -%cc% ../g_bot.c -@if errorlevel 1 goto quit -%cc% ../g_client.c -@if errorlevel 1 goto quit -%cc% ../g_cmds.c -@if errorlevel 1 goto quit -%cc% ../g_combat.c -@if errorlevel 1 goto quit -%cc% ../g_items.c -@if errorlevel 1 goto quit -%cc% ../g_log.c -@if errorlevel 1 goto quit -%cc% ../g_mem.c -@if errorlevel 1 goto quit -%cc% ../g_misc.c -@if errorlevel 1 goto quit -%cc% ../g_missile.c -@if errorlevel 1 goto quit -%cc% ../g_mover.c -@if errorlevel 1 goto quit -%cc% ../g_object.c -@if errorlevel 1 goto quit -%cc% ../g_saga.c -@if errorlevel 1 goto quit -%cc% ../g_session.c -@if errorlevel 1 goto quit -%cc% ../g_spawn.c -@if errorlevel 1 goto quit -%cc% ../g_svcmds.c -@if errorlevel 1 goto quit -%cc% ../g_target.c -@if errorlevel 1 goto quit -%cc% ../g_team.c -@if errorlevel 1 goto quit -%cc% ../g_trigger.c -@if errorlevel 1 goto quit -%cc% ../g_utils.c -@if errorlevel 1 goto quit -%cc% ../g_weapon.c -@if errorlevel 1 goto quit -%cc% ../w_force.c -@if errorlevel 1 goto quit -%cc% ../w_saber.c -@if errorlevel 1 goto quit - -sysmaker ../g_public.h ../g_syscalls.c ../g_syscalls.asm -@if errorlevel 1 goto quit - -q3asm -f ../game -@if errorlevel 1 goto quit - -mkdir "..\..\base\vm" -copy *.map "..\..\base\vm" -copy *.qvm "..\..\base\vm" - -:quit -cd .. diff --git a/CODE-mp/game/game.q3asm b/CODE-mp/game/game.q3asm deleted file mode 100644 index a60a599..0000000 --- a/CODE-mp/game/game.q3asm +++ /dev/null @@ -1,40 +0,0 @@ --o "jk2mpgame" - -g_main -ai_main -ai_util -ai_wpnav -bg_lib -bg_misc -bg_pmove -bg_panimate -bg_slidemove -bg_weapons -bg_saber -g_active -g_arenas -g_bot -g_client -g_cmds -g_combat -g_items -g_log -g_mem -g_misc -g_missile -g_mover -g_object -g_saga -g_session -g_spawn -g_svcmds -..\g_syscalls -g_target -g_team -g_trigger -g_utils -g_weapon -w_force -w_saber -q_math -q_shared diff --git a/CODE-mp/game/mssccprj.scc b/CODE-mp/game/mssccprj.scc deleted file mode 100644 index 5d4910b..0000000 --- a/CODE-mp/game/mssccprj.scc +++ /dev/null @@ -1,5 +0,0 @@ -SCC = This is a Source Code Control file - -[JK2_game.dsp] -SCC_Aux_Path = "\\ravend\vss_projects\jk2sof2MP" -SCC_Project_Name = "$/General/code/game", VPCAAAAA diff --git a/CODE-mp/game/q_shared.h b/CODE-mp/game/q_shared.h index 76c7b0b..54eac5b 100644 --- a/CODE-mp/game/q_shared.h +++ b/CODE-mp/game/q_shared.h @@ -6,7 +6,7 @@ // q_shared.h -- included first by ALL program modules. // A user mod should never modify this file -#define Q3_VERSION "JK2MP: v0.55" +#define Q3_VERSION "JK2MP: v0.56" #define MAX_TEAMNAME 32 @@ -1397,6 +1397,7 @@ typedef struct forcedata_s { qboolean sentryDeployed; int saberAnimLevel; + int saberDrawAnimLevel; int suicides; @@ -1680,6 +1681,29 @@ typedef struct playerState_s { #define MOVE_RUN 120 // if forwardmove or rightmove are >= MOVE_RUN, // then BUTTON_WALKING should be set +typedef enum +{ + GENCMD_SABERSWITCH = 1, + GENCMD_ENGAGE_DUEL, + GENCMD_FORCE_HEAL, + GENCMD_FORCE_SPEED, + GENCMD_FORCE_THROW, + GENCMD_FORCE_PULL, + GENCMD_FORCE_DISTRACT, + GENCMD_FORCE_RAGE, + GENCMD_FORCE_PROTECT, + GENCMD_FORCE_ABSORB, + GENCMD_FORCE_HEALOTHER, + GENCMD_FORCE_FORCEPOWEROTHER, + GENCMD_FORCE_SEEING, + GENCMD_USE_SEEKER, + GENCMD_USE_FIELD, + GENCMD_USE_BACTA, + GENCMD_USE_ELECTROBINOCULARS, + GENCMD_ZOOM, + GENCMD_USE_SENTRY, + GENCMD_SABERATTACKCYCLE +} genCmds_t; // usercmd_t is sent to the server each client frame typedef struct usercmd_s { @@ -1689,6 +1713,7 @@ typedef struct usercmd_s { byte weapon; // weapon byte forcesel; byte invensel; + byte generic_cmd; signed char forwardmove, rightmove, upmove; } usercmd_t; @@ -2039,4 +2064,13 @@ typedef enum } ForceReload_e; + +enum { + FONT_NONE, + FONT_SMALL=1, + FONT_MEDIUM, + FONT_LARGE +}; + + #endif // __Q_SHARED_H diff --git a/CODE-mp/game/vssver.scc b/CODE-mp/game/vssver.scc deleted file mode 100644 index aa97145..0000000 Binary files a/CODE-mp/game/vssver.scc and /dev/null differ diff --git a/CODE-mp/game/w_force.c b/CODE-mp/game/w_force.c index 8b24949..24f82bb 100644 --- a/CODE-mp/game/w_force.c +++ b/CODE-mp/game/w_force.c @@ -441,9 +441,12 @@ validitycheck: { if (ent->client->ps.fd.forcePowersKnown & (1 << i)) { - ent->client->ps.fd.forcePowersKnown &= ~(1 << i); - ent->client->ps.fd.forcePowerLevel[i] = 0; - warnClientLimit = qtrue; + if (i != FP_SABERATTACK && i != FP_LEVITATION && i != FP_SABERDEFEND) + { + ent->client->ps.fd.forcePowersKnown &= ~(1 << i); + ent->client->ps.fd.forcePowerLevel[i] = 0; + warnClientLimit = qtrue; + } } } i++; @@ -499,12 +502,15 @@ validitycheck: if (!(ent->r.svFlags & SVF_BOT) && g_gametype.integer != GT_TOURNAMENT) { - //Make them a spectator so they can set their powerups up without being bothered. - ent->client->sess.sessionTeam = TEAM_SPECTATOR; - ent->client->sess.spectatorState = SPECTATOR_FREE; - ent->client->sess.spectatorClient = 0; + if (g_gametype.integer < GT_TEAM || !g_teamAutoJoin.integer) + { + //Make them a spectator so they can set their powerups up without being bothered. + ent->client->sess.sessionTeam = TEAM_SPECTATOR; + ent->client->sess.spectatorState = SPECTATOR_FREE; + ent->client->sess.spectatorClient = 0; - ent->client->pers.teamState.state = TEAM_BEGIN; + ent->client->pers.teamState.state = TEAM_BEGIN; + } } } ent->client->sess.setForce = qtrue; @@ -848,75 +854,6 @@ void WP_ForcePowerRegenerate( gentity_t *self, int overrideAmt ) } } -void WP_ForcePowerDrain( gentity_t *self, forcePowers_t forcePower, int overrideAmt ) -{ - //take away the power - int drain = overrideAmt; - - if (self->client->ps.powerups[PW_FORCE_BOON]) - { - return; - } - - if ( !drain ) - { - drain = forcePowerNeeded[self->client->ps.fd.forcePowerLevel[forcePower]][forcePower]; - } - if ( !drain ) - { - return; - } - - if (forcePower == FP_LEVITATION) - { //special case - int jumpDrain = 0; - - if (self->client->ps.velocity[2] > 250) - { - jumpDrain = 20; - } - else if (self->client->ps.velocity[2] > 200) - { - jumpDrain = 16; - } - else if (self->client->ps.velocity[2] > 150) - { - jumpDrain = 12; - } - else if (self->client->ps.velocity[2] > 100) - { - jumpDrain = 8; - } - else if (self->client->ps.velocity[2] > 50) - { - jumpDrain = 6; - } - else if (self->client->ps.velocity[2] > 0) - { - jumpDrain = 4; - } - - if (jumpDrain) - { - jumpDrain /= self->client->ps.fd.forcePowerLevel[FP_LEVITATION]; - } - - self->client->ps.fd.forcePower -= jumpDrain; - if ( self->client->ps.fd.forcePower < 0 ) - { - self->client->ps.fd.forcePower = 0; - } - - return; - } - - self->client->ps.fd.forcePower -= drain; - if ( self->client->ps.fd.forcePower < 0 ) - { - self->client->ps.fd.forcePower = 0; - } -} - void WP_ForcePowerStart( gentity_t *self, forcePowers_t forcePower, int overrideAmt ) { int duration = 0; @@ -1021,15 +958,15 @@ void WP_ForcePowerStart( gentity_t *self, forcePowers_t forcePower, int override hearDist = 256; if (self->client->ps.fd.forcePowerLevel[FP_RAGE] == FORCE_LEVEL_1) { - duration = 10000; + duration = 8000; } else if (self->client->ps.fd.forcePowerLevel[FP_RAGE] == FORCE_LEVEL_2) { - duration = 16000; + duration = 14000; } else if (self->client->ps.fd.forcePowerLevel[FP_RAGE] == FORCE_LEVEL_3) { - duration = 23000; + duration = 20000; } else //shouldn't get here { @@ -1118,11 +1055,11 @@ void WP_ForcePowerStart( gentity_t *self, forcePowers_t forcePower, int override if ((int)forcePower == FP_SPEED && overrideAmt) { - WP_ForcePowerDrain( self, forcePower, overrideAmt*0.025 ); + BG_ForcePowerDrain( &self->client->ps, forcePower, overrideAmt*0.025 ); } else if ((int)forcePower != FP_GRIP && (int)forcePower != FP_DRAIN) { //grip and drain drain as damage is done - WP_ForcePowerDrain( self, forcePower, overrideAmt ); + BG_ForcePowerDrain( &self->client->ps, forcePower, overrideAmt ); } } @@ -1157,12 +1094,36 @@ void ForceHeal( gentity_t *self ) { self->health = self->client->ps.stats[STAT_MAX_HEALTH]; } - WP_ForcePowerDrain( self, FP_HEAL, 0 ); + BG_ForcePowerDrain( &self->client->ps, FP_HEAL, 0 ); } + else if (self->client->ps.fd.forcePowerLevel[FP_HEAL] == FORCE_LEVEL_2) + { + self->health += 25; + + if (self->health > self->client->ps.stats[STAT_MAX_HEALTH]) + { + self->health = self->client->ps.stats[STAT_MAX_HEALTH]; + } + BG_ForcePowerDrain( &self->client->ps, FP_HEAL, 0 ); + } + else + { + self->health += 10; + + if (self->health > self->client->ps.stats[STAT_MAX_HEALTH]) + { + self->health = self->client->ps.stats[STAT_MAX_HEALTH]; + } + BG_ForcePowerDrain( &self->client->ps, FP_HEAL, 0 ); + } + /* else { WP_ForcePowerStart( self, FP_HEAL, 0 ); } + */ + //NOTE: Decided to make all levels instant. + G_Sound( self, CHAN_ITEM, G_SoundIndex("sound/weapons/force/heal.wav") ); // No character heal voices // G_Sound( self, CHAN_VOICE, G_SoundIndex(va( "sound/weapons/force/heal%d.mp3", Q_irand( 1, 4 ) )) ); @@ -1233,7 +1194,7 @@ void ForceTeamHeal( gentity_t *self ) healthadd = 25; } - WP_ForcePowerDrain( self, FP_TEAM_HEAL, forcePowerNeeded[self->client->ps.fd.forcePowerLevel[FP_TEAM_HEAL]][FP_TEAM_HEAL] ); + BG_ForcePowerDrain( &self->client->ps, FP_TEAM_HEAL, forcePowerNeeded[self->client->ps.fd.forcePowerLevel[FP_TEAM_HEAL]][FP_TEAM_HEAL] ); i = 0; @@ -1321,7 +1282,7 @@ void ForceTeamForceReplenish( gentity_t *self ) poweradd = 25; } - WP_ForcePowerDrain( self, FP_TEAM_FORCE, forcePowerNeeded[self->client->ps.fd.forcePowerLevel[FP_TEAM_FORCE]][FP_TEAM_FORCE] ); + BG_ForcePowerDrain( &self->client->ps, FP_TEAM_FORCE, forcePowerNeeded[self->client->ps.fd.forcePowerLevel[FP_TEAM_FORCE]][FP_TEAM_FORCE] ); i = 0; @@ -1911,11 +1872,11 @@ void ForceDrainDamage( gentity_t *self, gentity_t *traceEnt, vec3_t dir, vec3_t /* if (self->client->ps.fd.forcePowerLevel[FP_DRAIN] == FORCE_LEVEL_1) { - WP_ForcePowerDrain( self, FP_DRAIN, 0 ); + BG_ForcePowerDrain( &self->client->ps, FP_DRAIN, 0 ); } else { - WP_ForcePowerDrain( self, FP_DRAIN, forcePowerNeeded[self->client->ps.fd.forcePowerLevel[FP_DRAIN]][FP_DRAIN]/5 ); + BG_ForcePowerDrain( &self->client->ps, FP_DRAIN, forcePowerNeeded[self->client->ps.fd.forcePowerLevel[FP_DRAIN]][FP_DRAIN]/5 ); } if (self->client->ps.fd.forcePowerLevel[FP_DRAIN] == FORCE_LEVEL_1) @@ -2077,11 +2038,11 @@ int ForceShootDrain( gentity_t *self ) /* if (self->client->ps.fd.forcePowerLevel[FP_DRAIN] == FORCE_LEVEL_1) { - WP_ForcePowerDrain( self, FP_DRAIN, 0 ); + BG_ForcePowerDrain( &self->client->ps, FP_DRAIN, 0 ); } else*/ { - WP_ForcePowerDrain( self, FP_DRAIN, 1 ); + BG_ForcePowerDrain( &self->client->ps, FP_DRAIN, 1 ); } self->client->ps.fd.forcePowerRegenDebounceTime = level.time + 500; @@ -3720,7 +3681,7 @@ static void WP_ForcePowerRun( gentity_t *self, forcePowers_t forcePower, usercmd self->client->ps.fd.forceHealTime = level.time + 1000; self->health++; self->client->ps.fd.forceHealAmount++; - //WP_ForcePowerDrain( self, forcePower, 0 ); + //BG_ForcePowerDrain( &self->client->ps, forcePower, 0 ); if ( self->health > self->client->ps.stats[STAT_MAX_HEALTH]) // Past max health { self->health = self->client->ps.stats[STAT_MAX_HEALTH]; @@ -3751,9 +3712,10 @@ static void WP_ForcePowerRun( gentity_t *self, forcePowers_t forcePower, usercmd } else { + /* if (self->client->ps.fd.forcePowerDebounce[FP_LEVITATION] < level.time) { - WP_ForcePowerDrain( self, forcePower, 5 ); + BG_ForcePowerDrain( &self->client->ps, forcePower, 5 ); if (self->client->ps.fd.forcePowerLevel[FP_LEVITATION] >= FORCE_LEVEL_2) { self->client->ps.fd.forcePowerDebounce[FP_LEVITATION] = level.time + 300; @@ -3763,6 +3725,8 @@ static void WP_ForcePowerRun( gentity_t *self, forcePowers_t forcePower, usercmd self->client->ps.fd.forcePowerDebounce[FP_LEVITATION] = level.time + 200; } } + */ + //NOTE: Now handled in bg code for prediction } break; case FP_RAGE: @@ -3779,15 +3743,15 @@ static void WP_ForcePowerRun( gentity_t *self, forcePowers_t forcePower, usercmd if (self->client->ps.fd.forcePowerLevel[FP_RAGE] == FORCE_LEVEL_1) { - addTime = 100; + addTime = 150; } else if (self->client->ps.fd.forcePowerLevel[FP_RAGE] == FORCE_LEVEL_2) { - addTime = 250; + addTime = 300; } else if (self->client->ps.fd.forcePowerLevel[FP_RAGE] == FORCE_LEVEL_3) { - addTime = 400; + addTime = 450; } self->client->ps.forceRageDrainTime = level.time + addTime; } @@ -3847,7 +3811,7 @@ static void WP_ForcePowerRun( gentity_t *self, forcePowers_t forcePower, usercmd else { ForceShootLightning( self ); - WP_ForcePowerDrain( self, forcePower, 0 ); + BG_ForcePowerDrain( &self->client->ps, forcePower, 0 ); } break; case FP_TELEPATHY: @@ -3863,7 +3827,7 @@ static void WP_ForcePowerRun( gentity_t *self, forcePowers_t forcePower, usercmd case FP_ABSORB: if (self->client->ps.fd.forcePowerDebounce[forcePower] < level.time) { - WP_ForcePowerDrain( self, forcePower, 1 ); + BG_ForcePowerDrain( &self->client->ps, forcePower, 1 ); if (self->client->ps.fd.forcePower < 1) { WP_ForcePowerStop(self, forcePower); @@ -3933,7 +3897,7 @@ int WP_DoSpecificPower( gentity_t *self, usercmd_t *ucmd, forcePowers_t forcepow if (!(self->client->ps.fd.forcePowersActive & (1 << FP_GRIP))) { WP_ForcePowerStart( self, FP_GRIP, 0 ); - WP_ForcePowerDrain( self, FP_GRIP, GRIP_DRAIN_AMOUNT ); + BG_ForcePowerDrain( &self->client->ps, FP_GRIP, GRIP_DRAIN_AMOUNT ); } } else @@ -4548,14 +4512,11 @@ void WP_ForcePowersUpdate( gentity_t *self, usercmd_t *ucmd ) { self->client->ps.fd.forcePowerBaseLevel[i] = self->client->ps.fd.forcePowerLevel[i]; - if (self->client->ps.fd.forcePowerLevel[i] >= FORCE_LEVEL_1 && - self->client->ps.fd.forcePowerLevel[i] < FORCE_LEVEL_3) + if (!forcePowerDarkLight[i] || + self->client->ps.fd.forceSide == forcePowerDarkLight[i]) { - if (!forcePowerDarkLight[i] || - self->client->ps.fd.forceSide == forcePowerDarkLight[i]) - { - self->client->ps.fd.forcePowerLevel[i]++; - } + self->client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_3; + self->client->ps.fd.forcePowersKnown |= (1 << i); } i++; @@ -4571,6 +4532,14 @@ void WP_ForcePowersUpdate( gentity_t *self, usercmd_t *ucmd ) while (i < NUM_FORCE_POWERS) { self->client->ps.fd.forcePowerLevel[i] = self->client->ps.fd.forcePowerBaseLevel[i]; + if (!self->client->ps.fd.forcePowerLevel[i]) + { + if (self->client->ps.fd.forcePowersActive & (1 << i)) + { + WP_ForcePowerStop(self, i); + } + self->client->ps.fd.forcePowersKnown &= ~(1 << i); + } i++; } diff --git a/CODE-mp/game/w_saber.c b/CODE-mp/game/w_saber.c index bd4d8f4..a3710e6 100644 --- a/CODE-mp/game/w_saber.c +++ b/CODE-mp/game/w_saber.c @@ -5,7 +5,6 @@ #include "..\ghoul2\g2.h" extern bot_state_t *botstates[MAX_CLIENTS]; -extern void *g2SaberInstance; extern qboolean InFront( vec3_t spot, vec3_t from, vec3_t fromAngles, float threshHold ); int saberSpinSound = 0; @@ -110,7 +109,7 @@ void WP_SaberInitBladeData( gentity_t *ent ) saberSpinSound = G_SoundIndex("sound/weapons/saber/saberspin.wav"); saberOffSound = G_SoundIndex("sound/weapons/saber/saberoffquick.wav"); saberOnSound = G_SoundIndex("sound/weapons/saber/saberon.wav"); - saberHumSound = G_SoundIndex("sound/weapons/saber/saberhum.wav"); + saberHumSound = G_SoundIndex("sound/weapons/saber/saberhum1.wav"); } static void G_SwingAngles( float destination, float swingTolerance, float clampTolerance, @@ -546,7 +545,7 @@ qboolean WP_SabersCheckLock2( gentity_t *attacker, gentity_t *defender, sabersLo memset (&pmv, 0, sizeof(pmv)); pmv.ps = &attacker->client->ps; - pmv.animations = attacker->client->animations; + pmv.animations = bgGlobalAnimations; pmv.cmd = attacker->client->pers.cmd; pmv.trace = trap_Trace; pmv.pointcontents = trap_PointContents; @@ -555,15 +554,15 @@ qboolean WP_SabersCheckLock2( gentity_t *attacker, gentity_t *defender, sabersLo //This is a rare exception, you should never really call PM_ utility functions from game or cgame (despite the fact that it's technically possible) pm = &pmv; PM_SetAnim(SETANIM_BOTH, attAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0); - attacker->client->ps.saberLockFrame = attacker->client->animations[attAnim].firstFrame+(attacker->client->animations[attAnim].numFrames*0.5); + attacker->client->ps.saberLockFrame = bgGlobalAnimations[attAnim].firstFrame+(bgGlobalAnimations[attAnim].numFrames*0.5); pmv.ps = &defender->client->ps; - pmv.animations = defender->client->animations; + pmv.animations = bgGlobalAnimations; pmv.cmd = defender->client->pers.cmd; pm = &pmv; PM_SetAnim(SETANIM_BOTH, defAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0); - defender->client->ps.saberLockFrame = defender->client->animations[defAnim].firstFrame+(defender->client->animations[defAnim].numFrames*0.5); + defender->client->ps.saberLockFrame = bgGlobalAnimations[defAnim].firstFrame+(bgGlobalAnimations[defAnim].numFrames*0.5); attacker->client->ps.saberLockHits = 0; defender->client->ps.saberLockHits = 0; @@ -1086,7 +1085,7 @@ qboolean CheckSaberDamage(gentity_t *self, vec3_t saberStart, vec3_t saberEnd, q VectorSubtract(saberEnd, saberStart, dir); VectorNormalize(dir); - if (tr.fraction != 1 && + if ((tr.fraction != 1 || tr.startsolid) && /*(!g_entities[tr.entityNum].client || !g_entities[tr.entityNum].client->ps.usingATST) &&*/ //g_entities[tr.entityNum].client && g_entities[tr.entityNum].takedamage && @@ -1103,6 +1102,20 @@ qboolean CheckSaberDamage(gentity_t *self, vec3_t saberStart, vec3_t saberEnd, q return qfalse; } + if (g_entities[tr.entityNum].inuse && g_entities[tr.entityNum].client && + g_entities[tr.entityNum].client->ps.duelInProgress && + g_entities[tr.entityNum].client->ps.duelIndex != self->s.number) + { + return qfalse; + } + + if (g_entities[tr.entityNum].inuse && g_entities[tr.entityNum].client && + self->client->ps.duelInProgress && + self->client->ps.duelIndex != g_entities[tr.entityNum].s.number) + { + return qfalse; + } + didHit = qtrue; if (self->client->ps.saberMove == LS_A_BACK || @@ -1143,7 +1156,7 @@ qboolean CheckSaberDamage(gentity_t *self, vec3_t saberStart, vec3_t saberEnd, q } //our attack was blocked, so bounce back? - if (dmg > 5 && (self->client->ps.fd.saberAnimLevel < FORCE_LEVEL_3 || g_entities[tr.entityNum].client->ps.fd.forcePowerLevel[FP_SABERDEFEND] >= FORCE_LEVEL_3)) + if (dmg > 5) { self->client->ps.weaponTime = 0; self->client->ps.weaponstate = WEAPON_READY; @@ -1153,6 +1166,15 @@ qboolean CheckSaberDamage(gentity_t *self, vec3_t saberStart, vec3_t saberEnd, q } self->client->ps.saberAttackWound = level.time + 300; + if (self->client->ps.fd.saberAnimLevel >= FORCE_LEVEL_3 && dmg > 5 && g_entities[tr.entityNum].client->ps.saberMove != LS_READY && g_entities[tr.entityNum].client->ps.saberMove != LS_NONE) + { + g_entities[tr.entityNum].client->ps.weaponTime = 0; + g_entities[tr.entityNum].client->ps.weaponstate = WEAPON_READY; + g_entities[tr.entityNum].client->ps.saberBlocked = BLOCKED_ATK_BOUNCE; + + g_entities[tr.entityNum].client->ps.saberBlockTime = level.time + (350 - (g_entities[tr.entityNum].client->ps.fd.forcePowerLevel[FP_SABERDEFEND]*100));//300; + } + //NOTE: Actual blocking is handled in WP_SaberCanBlock /* if (dmg > 5) @@ -1212,7 +1234,7 @@ qboolean CheckSaberDamage(gentity_t *self, vec3_t saberStart, vec3_t saberEnd, q self->client->ps.saberAttackWound = level.time + 100; } } - else if (tr.fraction != 1 && + else if ((tr.fraction != 1 || tr.startsolid) && (g_entities[tr.entityNum].r.contents & CONTENTS_LIGHTSABER) && g_entities[tr.entityNum].r.contents != -1) { //saber clash @@ -1220,6 +1242,7 @@ qboolean CheckSaberDamage(gentity_t *self, vec3_t saberStart, vec3_t saberEnd, q gentity_t *otherOwner = &g_entities[g_entities[tr.entityNum].r.ownerNum]; if (otherOwner && + otherOwner->inuse && otherOwner->client && OnSameTeam(self, otherOwner) && !g_friendlySaber.integer) @@ -1227,6 +1250,20 @@ qboolean CheckSaberDamage(gentity_t *self, vec3_t saberStart, vec3_t saberEnd, q return qfalse; } + if (otherOwner && otherOwner->client && + otherOwner->client->ps.duelInProgress && + otherOwner->client->ps.duelIndex != self->s.number) + { + return qfalse; + } + + if (otherOwner && otherOwner->client && + self->client->ps.duelInProgress && + self->client->ps.duelIndex != otherOwner->s.number) + { + return qfalse; + } + didHit = qtrue; te = G_TempEntity( tr.endpos, EV_SABER_BLOCK ); @@ -1242,7 +1279,7 @@ qboolean CheckSaberDamage(gentity_t *self, vec3_t saberStart, vec3_t saberEnd, q return qfalse; } - if (self->client->ps.fd.saberAnimLevel < FORCE_LEVEL_3 || otherOwner->client->ps.fd.forcePowerLevel[FP_SABERDEFEND] >= FORCE_LEVEL_3) + if (self->client->ps.fd.saberAnimLevel < FORCE_LEVEL_3) { self->client->ps.weaponTime = 0; self->client->ps.weaponstate = WEAPON_READY; @@ -1261,7 +1298,13 @@ qboolean CheckSaberDamage(gentity_t *self, vec3_t saberStart, vec3_t saberEnd, q { //block if (otherOwner->client->ps.weaponTime < 1 || otherOwner->client->ps.fd.saberAnimLevel < FORCE_LEVEL_3) { - WP_SaberCanBlock(otherOwner, tr.endpos, 0, MOD_SABER, qfalse, 1); + if (WP_SaberCanBlock(otherOwner, tr.endpos, 0, MOD_SABER, qfalse, 1) && dmg > 5) + { + otherOwner->client->ps.weaponTime = 0; + otherOwner->client->ps.weaponstate = WEAPON_READY; + otherOwner->client->ps.saberBlocked = BLOCKED_ATK_BOUNCE; + otherOwner->client->ps.saberBlockTime = level.time + (350 - (otherOwner->client->ps.fd.forcePowerLevel[FP_SABERDEFEND]*100));//300; + } } } @@ -1300,11 +1343,11 @@ qboolean CheckSaberDamage(gentity_t *self, vec3_t saberStart, vec3_t saberEnd, q return didHit; } -#define MIN_SABER_SLICE_DISTANCE 80 +#define MIN_SABER_SLICE_DISTANCE 60 #define MIN_SABER_SLICE_RETURN_DISTANCE 30 -#define SABER_THROWN_HIT_DAMAGE 60 +#define SABER_THROWN_HIT_DAMAGE 40 #define SABER_THROWN_RETURN_HIT_DAMAGE 20 void thrownSaberTouch (gentity_t *saberent, gentity_t *other, trace_t *trace); @@ -1316,7 +1359,7 @@ void saberCheckRadiusDamage(gentity_t *saberent, int returning) gentity_t *ent, *te; gentity_t *saberOwner = &g_entities[saberent->r.ownerNum]; - if (returning) + if (returning && returning != 2) { dist = MIN_SABER_SLICE_RETURN_DISTANCE; } @@ -1346,6 +1389,22 @@ void saberCheckRadiusDamage(gentity_t *saberent, int returning) vec3_t vecsub; float veclen; + if (ent->inuse && ent->client && + ent->client->ps.duelInProgress && + ent->client->ps.duelIndex != saberOwner->s.number) + { + i++; + continue; + } + + if (ent->inuse && ent->client && + saberOwner->client->ps.duelInProgress && + saberOwner->client->ps.duelIndex != ent->s.number) + { + i++; + continue; + } + VectorSubtract(saberent->r.currentOrigin, ent->client->ps.origin, vecsub); veclen = VectorLength(vecsub); @@ -1381,6 +1440,11 @@ void saberCheckRadiusDamage(gentity_t *saberent, int returning) VectorSubtract(tr.endpos, saberent->r.currentOrigin, dir); VectorNormalize(dir); + if (!dir[0] && !dir[1] && !dir[2]) + { + dir[1] = 1; + } + if (saberOwner->client->ps.isJediMaster) { //2x damage for the Jedi Master G_Damage(ent, saberOwner, saberOwner, dir, tr.endpos, saberent->damage*2, 0, MOD_SABER); @@ -1448,7 +1512,9 @@ void saberCheckRadiusDamage(gentity_t *saberent, int returning) } } -void saberMoveBack( gentity_t *ent ) +#define THROWN_SABER_COMP + +void saberMoveBack( gentity_t *ent, qboolean goingBack ) { vec3_t origin, oldOrg; @@ -1460,6 +1526,42 @@ void saberMoveBack( gentity_t *ent ) //Get current angles? BG_EvaluateTrajectory( &ent->s.apos, level.time, ent->r.currentAngles ); + //compensation test code.. +#ifdef THROWN_SABER_COMP + if (!goingBack) + { //acts as a fallback in case touch code fails, keeps saber from going through things between predictions + float originalLength = 0; + int iCompensationLength = 32; + trace_t tr; + vec3_t mins, maxs; + vec3_t calcComp, compensatedOrigin; + VectorSet( mins, -24.0f, -24.0f, -8.0f ); + VectorSet( maxs, 24.0f, 24.0f, 8.0f ); + + VectorSubtract(origin, oldOrg, calcComp); + originalLength = VectorLength(calcComp); + + VectorNormalize(calcComp); + + compensatedOrigin[0] = oldOrg[0] + calcComp[0]*(originalLength+iCompensationLength); + compensatedOrigin[1] = oldOrg[1] + calcComp[1]*(originalLength+iCompensationLength); + compensatedOrigin[2] = oldOrg[2] + calcComp[2]*(originalLength+iCompensationLength); + + trap_Trace(&tr, oldOrg, mins, maxs, compensatedOrigin, ent->r.ownerNum, MASK_PLAYERSOLID); + + if ((tr.fraction != 1 || tr.startsolid || tr.allsolid) && tr.entityNum != ent->r.ownerNum) + { + VectorClear(ent->s.pos.trDelta); + + //Unfortunately doing this would defeat the purpose of the compensation. We will have to settle for a jerk on the client. + //VectorCopy( origin, ent->r.currentOrigin ); + + thrownSaberTouch(ent, &g_entities[tr.entityNum], &tr); + return; + } + } +#endif + VectorCopy( origin, ent->r.currentOrigin ); } @@ -1549,7 +1651,7 @@ void MakeDeadSaber(gentity_t *ent) //fall off in the direction the real saber was headed VectorCopy(ent->s.pos.trDelta, saberent->s.pos.trDelta); - saberMoveBack(saberent); + saberMoveBack(saberent, qtrue); saberent->s.pos.trType = TR_GRAVITY; trap_LinkEntity(saberent); @@ -1602,8 +1704,6 @@ void saberBackToOwner(gentity_t *saberent) return; } - saberent->s.saberInFlight = qfalse; - saberent->r.contents = CONTENTS_LIGHTSABER; //saberent->s.apos.trDelta[1] = 0; @@ -1616,7 +1716,7 @@ void saberBackToOwner(gentity_t *saberent) { VectorNormalize(dir); - saberMoveBack(saberent); + saberMoveBack(saberent, qtrue); VectorCopy(saberent->r.currentOrigin, saberent->s.pos.trBase); if (g_entities[saberent->r.ownerNum].client->ps.fd.forcePowerLevel[FP_SABERTHROW] >= FORCE_LEVEL_3) @@ -1640,6 +1740,12 @@ void saberBackToOwner(gentity_t *saberent) saberent->s.pos.trTime = level.time; } + if (ownerLen <= 256) + { + saberent->s.saberInFlight = qfalse; + saberent->s.loopSound = saberHumSound; + } + if (ownerLen <= 32) { saberOwner->client->ps.saberInFlight = qfalse; @@ -1654,9 +1760,16 @@ void saberBackToOwner(gentity_t *saberent) return; } - saberCheckRadiusDamage(saberent, 1); + if (!saberent->s.saberInFlight) + { + saberCheckRadiusDamage(saberent, 1); + } + else + { + saberCheckRadiusDamage(saberent, 2); + } - saberMoveBack(saberent); + saberMoveBack(saberent, qtrue); //G_RunObject(saberent); saberent->nextthink = level.time; @@ -1672,14 +1785,14 @@ void thrownSaberTouch (gentity_t *saberent, gentity_t *other, trace_t *trace) saberent->s.apos.trDelta[1] = 800; saberent->s.apos.trDelta[2] = 0; - saberent->s.loopSound = saberHumSound; - VectorCopy(saberent->r.currentOrigin, saberent->s.pos.trBase); - saberent->damage = SABER_THROWN_RETURN_HIT_DAMAGE; + //saberent->damage = SABER_THROWN_RETURN_HIT_DAMAGE; saberent->think = saberBackToOwner; saberent->nextthink = level.time; + + saberent->speed = 0; } #define SABER_MAX_THROW_DISTANCE 700 @@ -1796,7 +1909,7 @@ void saberFirstThrown(gentity_t *saberent) VectorNormalize(dir); - saberMoveBack(saberent); + saberMoveBack(saberent, qfalse); VectorCopy(saberent->r.currentOrigin, saberent->s.pos.trBase); VectorScale(dir, 700, saberent->s.pos.trDelta ); @@ -1834,6 +1947,18 @@ void WP_SaberPositionUpdate( gentity_t *self, usercmd_t *ucmd ) int returnAfterUpdate = 0; float animSpeedScale = 1; + if (self && self->inuse && self->client) + { + if (self->client->saberCycleQueue) + { + self->client->ps.fd.saberDrawAnimLevel = self->client->saberCycleQueue; + } + else + { + self->client->ps.fd.saberDrawAnimLevel = self->client->ps.fd.saberAnimLevel; + } + } + if (self && self->inuse && self->client && @@ -2130,7 +2255,7 @@ finalUpdate: if (self->client->ps.legsAnimExecute != legsAnim) { - trap_G2API_SetBoneAnim(self->client->ghoul2, 0, "model_root", self->client->animations[legsAnim].firstFrame, self->client->animations[legsAnim].firstFrame+self->client->animations[legsAnim].numFrames, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, animSpeedScale, level.time, -1, 150); + trap_G2API_SetBoneAnim(self->client->ghoul2, 0, "model_root", bgGlobalAnimations[legsAnim].firstFrame, bgGlobalAnimations[legsAnim].firstFrame+bgGlobalAnimations[legsAnim].numFrames, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, animSpeedScale, level.time, -1, 150); self->client->ps.legsAnimExecute = legsAnim; if ((self->client->ps.torsoAnim&~ANIM_TOGGLEBIT) == legsAnim) @@ -2142,7 +2267,7 @@ finalUpdate: !BG_SaberInSpecial(self->client->ps.saberMove) && !BG_SaberInSpecialAttack(self->client->ps.legsAnim) ) { - trap_G2API_SetBoneAnim(self->client->ghoul2, 0, "Motion", self->client->animations[legsAnim].firstFrame, self->client->animations[legsAnim].firstFrame+self->client->animations[legsAnim].numFrames, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, animSpeedScale, level.time, -1, 150); + trap_G2API_SetBoneAnim(self->client->ghoul2, 0, "Motion", bgGlobalAnimations[legsAnim].firstFrame, bgGlobalAnimations[legsAnim].firstFrame+bgGlobalAnimations[legsAnim].numFrames, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, animSpeedScale, level.time, -1, 150); } } } @@ -2152,20 +2277,20 @@ finalUpdate: f = torsoAnim; - initialFrame = self->client->animations[f].firstFrame; + initialFrame = bgGlobalAnimations[f].firstFrame; - if (self->client->animations[f].numFrames > 20) + if (bgGlobalAnimations[f].numFrames > 20) { initialFrame += 6; } - else if (self->client->animations[f].numFrames > 3) + else if (bgGlobalAnimations[f].numFrames > 3) { //HACK: Force it a couple frames into the animation so it doesn't lag behind the client visual position as much.. initialFrame += 2; } BG_SaberStartTransAnim(self->client->ps.fd.saberAnimLevel, f, &animSpeedScale); - trap_G2API_SetBoneAnim(self->client->ghoul2, 0, "upper_lumbar", initialFrame, self->client->animations[f].firstFrame+self->client->animations[f].numFrames, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, animSpeedScale, level.time, initialFrame, 150); + trap_G2API_SetBoneAnim(self->client->ghoul2, 0, "upper_lumbar", initialFrame, bgGlobalAnimations[f].firstFrame+bgGlobalAnimations[f].numFrames, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, animSpeedScale, level.time, initialFrame, 150); self->client->ps.torsoAnimExecute = torsoAnim; @@ -2178,7 +2303,7 @@ finalUpdate: !BG_SaberInSpecial(self->client->ps.saberMove) && !BG_SaberInSpecialAttack(self->client->ps.torsoAnim) ) { - trap_G2API_SetBoneAnim(self->client->ghoul2, 0, "Motion", self->client->animations[f].firstFrame, self->client->animations[f].firstFrame+self->client->animations[f].numFrames, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, animSpeedScale, level.time, initialFrame, 150); + trap_G2API_SetBoneAnim(self->client->ghoul2, 0, "Motion", bgGlobalAnimations[f].firstFrame, bgGlobalAnimations[f].firstFrame+bgGlobalAnimations[f].numFrames, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, animSpeedScale, level.time, initialFrame, 150); } } } @@ -2497,6 +2622,12 @@ int WP_SaberCanBlock(gentity_t *self, vec3_t point, int dflags, int mod, qboolea return 0; } + if (self->client->ps.saberMove != LS_READY && + !self->client->ps.saberBlocking) + { + return 0; + } + if (self->client->ps.saberBlockTime >= level.time) { return 0; diff --git a/CODE-mp/ghoul2/G2_misc.cpp b/CODE-mp/ghoul2/G2_misc.cpp index ef7aab1..01b8ca1 100644 --- a/CODE-mp/ghoul2/G2_misc.cpp +++ b/CODE-mp/ghoul2/G2_misc.cpp @@ -224,16 +224,16 @@ void G2_List_Model_Bones(const char *fileName, int frame) mdxaSkelOffsets_t *offsets; model_t *mod_m = R_GetModelByHandle(RE_RegisterModel(fileName)); model_t *mod_a = R_GetModelByHandle(mod_m->mdxm->animIndex); - mdxaFrame_t *aframe=0; - int frameSize; +// mdxaFrame_t *aframe=0; +// int frameSize; mdxaHeader_t *header = mod_a->mdxa; // figure out where the offset list is offsets = (mdxaSkelOffsets_t *)((byte *)header + sizeof(mdxaHeader_t)); - frameSize = (int)( &((mdxaFrame_t *)0)->boneIndexes[ header->numBones ] ); +// frameSize = (int)( &((mdxaFrame_t *)0)->boneIndexes[ header->numBones ] ); - aframe = (mdxaFrame_t *)((byte *)header + header->ofsFrames + (frame * frameSize)); +// aframe = (mdxaFrame_t *)((byte *)header + header->ofsFrames + (frame * frameSize)); // walk each bone and list it's name for (x=0; x< mod_a->mdxa->numBones; x++) { @@ -329,28 +329,35 @@ void R_TransformEachSurface( mdxmSurface_t *surface, vec3_t scale, CMiniHeap *G2 int *piBoneRefs = (int*) ((byte*)surface + surface->ofsBoneReferences); numVerts = surface->numVerts; v = (mdxmVertex_t *) ((byte *)surface + surface->ofsVerts); + mdxmVertexTexCoord_t *pTexCoords = (mdxmVertexTexCoord_t *) &v[numVerts]; // optimisation issue if ((scale[0] != 1.0) || (scale[1] != 1.0) || (scale[2] != 1.0)) { for ( j = 0; j < numVerts; j++ ) { vec3_t tempVert, tempNormal; - mdxmWeight_t *w; +// mdxmWeight_t *w; VectorClear( tempVert ); VectorClear( tempNormal ); - w = v->weights; - for ( k = 0 ; k < v->numWeights ; k++, w++ ) +// w = v->weights; + + const int iNumWeights = G2_GetVertWeights( v ); + + for ( k = 0 ; k < iNumWeights ; k++ ) { + int iBoneIndex = G2_GetVertBoneIndex( v, k ); + float fBoneWeight = G2_GetVertBoneWeight( v, k ); + //bone = bonePtr + piBoneRefs[w->boneIndex]; - tempVert[0] += w->boneWeight * ( DotProduct( bonePtr[piBoneRefs[w->boneIndex]].matrix[0], v->vertCoords ) + bonePtr[piBoneRefs[w->boneIndex]].matrix[0][3] ); - tempVert[1] += w->boneWeight * ( DotProduct( bonePtr[piBoneRefs[w->boneIndex]].matrix[1], v->vertCoords ) + bonePtr[piBoneRefs[w->boneIndex]].matrix[1][3] ); - tempVert[2] += w->boneWeight * ( DotProduct( bonePtr[piBoneRefs[w->boneIndex]].matrix[2], v->vertCoords ) + bonePtr[piBoneRefs[w->boneIndex]].matrix[2][3] ); + tempVert[0] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].matrix[0], v->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].matrix[0][3] ); + tempVert[1] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].matrix[1], v->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].matrix[1][3] ); + tempVert[2] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].matrix[2], v->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].matrix[2][3] ); - tempNormal[0] += w->boneWeight * DotProduct( bonePtr[piBoneRefs[w->boneIndex]].matrix[0], v->normal ); - tempNormal[1] += w->boneWeight * DotProduct( bonePtr[piBoneRefs[w->boneIndex]].matrix[1], v->normal ); - tempNormal[2] += w->boneWeight * DotProduct( bonePtr[piBoneRefs[w->boneIndex]].matrix[2], v->normal ); + tempNormal[0] += fBoneWeight * DotProduct( bonePtr[piBoneRefs[iBoneIndex]].matrix[0], v->normal ); + tempNormal[1] += fBoneWeight * DotProduct( bonePtr[piBoneRefs[iBoneIndex]].matrix[1], v->normal ); + tempNormal[2] += fBoneWeight * DotProduct( bonePtr[piBoneRefs[iBoneIndex]].matrix[2], v->normal ); } int pos = j * 5; @@ -359,10 +366,10 @@ void R_TransformEachSurface( mdxmSurface_t *surface, vec3_t scale, CMiniHeap *G2 TransformedVerts[pos++] = tempVert[1] * scale[1]; TransformedVerts[pos++] = tempVert[2] * scale[2]; // we will need the S & T coors too for hitlocation and hitmaterial stuff - TransformedVerts[pos++] = v->texCoords[0]; - TransformedVerts[pos] = v->texCoords[1]; + TransformedVerts[pos++] = pTexCoords[j].texCoords[0]; + TransformedVerts[pos] = pTexCoords[j].texCoords[1]; - v = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surface->maxVertBoneWeights]; + v++;// = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surface->maxVertBoneWeights]; } } else @@ -370,22 +377,28 @@ void R_TransformEachSurface( mdxmSurface_t *surface, vec3_t scale, CMiniHeap *G2 for ( j = 0; j < numVerts; j++ ) { vec3_t tempVert, tempNormal; - mdxmWeight_t *w; +// mdxmWeight_t *w; VectorClear( tempVert ); VectorClear( tempNormal ); - w = v->weights; - for ( k = 0 ; k < v->numWeights ; k++, w++ ) +// w = v->weights; + + const int iNumWeights = G2_GetVertWeights( v ); + + for ( k = 0 ; k < iNumWeights ; k++ ) { + int iBoneIndex = G2_GetVertBoneIndex( v, k ); + float fBoneWeight = G2_GetVertBoneWeight( v, k ); + //bone = bonePtr + piBoneRefs[w->boneIndex]; - tempVert[0] += w->boneWeight * ( DotProduct( bonePtr[piBoneRefs[w->boneIndex]].matrix[0], v->vertCoords ) + bonePtr[piBoneRefs[w->boneIndex]].matrix[0][3] ); - tempVert[1] += w->boneWeight * ( DotProduct( bonePtr[piBoneRefs[w->boneIndex]].matrix[1], v->vertCoords ) + bonePtr[piBoneRefs[w->boneIndex]].matrix[1][3] ); - tempVert[2] += w->boneWeight * ( DotProduct( bonePtr[piBoneRefs[w->boneIndex]].matrix[2], v->vertCoords ) + bonePtr[piBoneRefs[w->boneIndex]].matrix[2][3] ); + tempVert[0] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].matrix[0], v->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].matrix[0][3] ); + tempVert[1] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].matrix[1], v->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].matrix[1][3] ); + tempVert[2] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].matrix[2], v->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].matrix[2][3] ); - tempNormal[0] += w->boneWeight * DotProduct( bonePtr[piBoneRefs[w->boneIndex]].matrix[0], v->normal ); - tempNormal[1] += w->boneWeight * DotProduct( bonePtr[piBoneRefs[w->boneIndex]].matrix[1], v->normal ); - tempNormal[2] += w->boneWeight * DotProduct( bonePtr[piBoneRefs[w->boneIndex]].matrix[2], v->normal ); + tempNormal[0] += fBoneWeight * DotProduct( bonePtr[piBoneRefs[iBoneIndex]].matrix[0], v->normal ); + tempNormal[1] += fBoneWeight * DotProduct( bonePtr[piBoneRefs[iBoneIndex]].matrix[1], v->normal ); + tempNormal[2] += fBoneWeight * DotProduct( bonePtr[piBoneRefs[iBoneIndex]].matrix[2], v->normal ); } int pos = j * 5; @@ -394,10 +407,10 @@ void R_TransformEachSurface( mdxmSurface_t *surface, vec3_t scale, CMiniHeap *G2 TransformedVerts[pos++] = tempVert[1]; TransformedVerts[pos++] = tempVert[2]; // we will need the S & T coors too for hitlocation and hitmaterial stuff - TransformedVerts[pos++] = v->texCoords[0]; - TransformedVerts[pos] = v->texCoords[1]; + TransformedVerts[pos++] = pTexCoords[j].texCoords[0]; + TransformedVerts[pos] = pTexCoords[j].texCoords[1]; - v = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surface->maxVertBoneWeights]; + v++;// = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surface->maxVertBoneWeights]; } } } diff --git a/CODE-mp/ghoul2/vssver.scc b/CODE-mp/ghoul2/vssver.scc deleted file mode 100644 index 7a1b52f..0000000 Binary files a/CODE-mp/ghoul2/vssver.scc and /dev/null differ diff --git a/CODE-mp/installvms.bat b/CODE-mp/installvms.bat deleted file mode 100644 index 43c7700..0000000 --- a/CODE-mp/installvms.bat +++ /dev/null @@ -1,4 +0,0 @@ -xcopy/d/y release\jk2mp.exe w:\game -xcopy/d/y base\vm\cgame.* w:\game\base\vm -xcopy/d/y base\vm\jk2mpgame.* w:\game\base\vm -xcopy/d/y base\vm\ui.* w:\game\base\vm diff --git a/CODE-mp/jpeg-6/vssver.scc b/CODE-mp/jpeg-6/vssver.scc deleted file mode 100644 index 0f669ce..0000000 Binary files a/CODE-mp/jpeg-6/vssver.scc and /dev/null differ diff --git a/CODE-mp/mp3code/vssver.scc b/CODE-mp/mp3code/vssver.scc deleted file mode 100644 index 92aab9a..0000000 Binary files a/CODE-mp/mp3code/vssver.scc and /dev/null differ diff --git a/CODE-mp/mssccprj.scc b/CODE-mp/mssccprj.scc deleted file mode 100644 index 3ec70d1..0000000 --- a/CODE-mp/mssccprj.scc +++ /dev/null @@ -1,5 +0,0 @@ -SCC = This is a Source Code Control file - -[jk2mp.dsp] -SCC_Aux_Path = "\\ravend\vss_projects\jk2sof2MP" -SCC_Project_Name = "$/General/code", EAAAAAAA diff --git a/CODE-mp/png/vssver.scc b/CODE-mp/png/vssver.scc deleted file mode 100644 index 9f270ba..0000000 Binary files a/CODE-mp/png/vssver.scc and /dev/null differ diff --git a/CODE-mp/put.bat b/CODE-mp/put.bat deleted file mode 100644 index ae62aae..0000000 --- a/CODE-mp/put.bat +++ /dev/null @@ -1,2 +0,0 @@ -xcopy /d /y release\*.dll w:\game -xcopy /d /y release\jk2mp.exe w:\game\ diff --git a/CODE-mp/qcommon/common.cpp b/CODE-mp/qcommon/common.cpp index 08f95e1..b9d2e9d 100644 --- a/CODE-mp/qcommon/common.cpp +++ b/CODE-mp/qcommon/common.cpp @@ -4,7 +4,9 @@ #include "qcommon.h" #include "strip.h" #include +#ifndef __linux__ #include +#endif #define MAXPRINTMSG 4096 @@ -215,8 +217,11 @@ void QDECL Com_OPrintf( const char *fmt, ...) va_start (argptr,fmt); vsprintf (msg,fmt,argptr); va_end (argptr); - +#ifndef __linux__ OutputDebugString(msg); +#else + printf(msg); +#endif } /* @@ -1522,7 +1527,7 @@ void Hunk_Log( void) { #ifdef HUNK_DEBUG Com_sprintf(buf, sizeof(buf), "size\t%8d\t%s\tline\t%d\t(%s)\r\n", block->size, block->file, block->line, block->label); FS_Write(buf, strlen(buf), logfile); -// OutputDebugString(buf); + OutputDebugString(buf); #endif size += block->size; numBlocks++; @@ -2533,6 +2538,9 @@ void Com_Init( char *commandLine ) { com_introPlayed = Cvar_Get( "com_introplayed", "0", CVAR_ARCHIVE); + Cvar_Get ("com_othertasks", "0", CVAR_ROM ); + Cvar_Get ("com_ignoreothertasks", "0", CVAR_ARCHIVE ); + #if defined(_WIN32) && defined(_DEBUG) com_noErrorInterrupt = Cvar_Get( "com_noErrorInterrupt", "0", 0 ); #endif diff --git a/CODE-mp/qcommon/msg.cpp b/CODE-mp/qcommon/msg.cpp index b26aa3e..c22f004 100644 --- a/CODE-mp/qcommon/msg.cpp +++ b/CODE-mp/qcommon/msg.cpp @@ -643,6 +643,8 @@ void MSG_WriteDeltaUsercmd( msg_t *msg, usercmd_t *from, usercmd_t *to ) { MSG_WriteDelta( msg, from->forcesel, to->forcesel, 8 ); MSG_WriteDelta( msg, from->invensel, to->invensel, 8 ); + + MSG_WriteDelta( msg, from->generic_cmd, to->generic_cmd, 8 ); } @@ -668,6 +670,8 @@ void MSG_ReadDeltaUsercmd( msg_t *msg, usercmd_t *from, usercmd_t *to ) { to->forcesel = MSG_ReadDelta( msg, from->forcesel, 8); to->invensel = MSG_ReadDelta( msg, from->invensel, 8); + + to->generic_cmd = MSG_ReadDelta( msg, from->generic_cmd, 8); } /* @@ -692,7 +696,8 @@ void MSG_WriteDeltaUsercmdKey( msg_t *msg, int key, usercmd_t *from, usercmd_t * from->buttons == to->buttons && from->weapon == to->weapon && from->forcesel == to->forcesel && - from->invensel == to->invensel) { + from->invensel == to->invensel && + from->generic_cmd == to->generic_cmd) { MSG_WriteBits( msg, 0, 1 ); // no change oldsize += 7; return; @@ -710,6 +715,8 @@ void MSG_WriteDeltaUsercmdKey( msg_t *msg, int key, usercmd_t *from, usercmd_t * MSG_WriteDeltaKey( msg, key, from->forcesel, to->forcesel, 8 ); MSG_WriteDeltaKey( msg, key, from->invensel, to->invensel, 8 ); + + MSG_WriteDeltaKey( msg, key, from->generic_cmd, to->generic_cmd, 8 ); } @@ -737,6 +744,8 @@ void MSG_ReadDeltaUsercmdKey( msg_t *msg, int key, usercmd_t *from, usercmd_t *t to->forcesel = MSG_ReadDeltaKey( msg, key, from->forcesel, 8); to->invensel = MSG_ReadDeltaKey( msg, key, from->invensel, 8); + + to->generic_cmd = MSG_ReadDeltaKey( msg, key, from->generic_cmd, 8); } else { to->angles[0] = from->angles[0]; to->angles[1] = from->angles[1]; @@ -749,6 +758,8 @@ void MSG_ReadDeltaUsercmdKey( msg_t *msg, int key, usercmd_t *from, usercmd_t *t to->forcesel = from->forcesel; to->invensel = from->invensel; + + to->generic_cmd = from->generic_cmd; } } @@ -1204,6 +1215,8 @@ netField_t playerStateFields[] = { PSF(fallingToDeath), 32 }, { PSF(electrifyTime), 32 }, +{ PSF(fd.forcePowerDebounce[FP_LEVITATION]), 32 }, + { PSF(saberMove), 32 }, //This value sometimes exceeds the max LS_ value and gets set to a crazy amount, so it needs 32 bits { PSF(saberActive), 1 }, { PSF(saberInFlight), 1 }, @@ -1213,6 +1226,7 @@ netField_t playerStateFields[] = { PSF(forceHandExtend), 8 }, { PSF(forceDodgeAnim), 16 }, { PSF(fd.saberAnimLevel), 4 }, +{ PSF(fd.saberDrawAnimLevel), 4 }, { PSF(saberAttackChainCount), 32 }, { PSF(saberHolstered), 1 }, { PSF(usingATST), 1 }, diff --git a/CODE-mp/qcommon/qcommon.h b/CODE-mp/qcommon/qcommon.h index ed3dd24..8cd7f70 100644 --- a/CODE-mp/qcommon/qcommon.h +++ b/CODE-mp/qcommon/qcommon.h @@ -227,6 +227,7 @@ enum svc_ops_e { svc_serverCommand, // [string] to be executed by client game module svc_download, // [short] size [size bytes] svc_snapshot, + svc_mapchange, svc_EOF }; diff --git a/CODE-mp/qcommon/strip.cpp b/CODE-mp/qcommon/strip.cpp index 19cb212..81eb708 100644 --- a/CODE-mp/qcommon/strip.cpp +++ b/CODE-mp/qcommon/strip.cpp @@ -154,16 +154,16 @@ sFlagPair FlagPairs[] = sFlagPair LanguagePairs[] = { - { TK_TEXT_LANGUAGE1, TEXT_LANGUAGE1 }, - { TK_TEXT_LANGUAGE2, TEXT_LANGUAGE2 }, - { TK_TEXT_LANGUAGE3, TEXT_LANGUAGE3 }, - { TK_TEXT_LANGUAGE4, TEXT_LANGUAGE4 }, - { TK_TEXT_LANGUAGE5, TEXT_LANGUAGE5 }, - { TK_TEXT_LANGUAGE6, TEXT_LANGUAGE6 }, - { TK_TEXT_LANGUAGE7, TEXT_LANGUAGE7 }, - { TK_TEXT_LANGUAGE8, TEXT_LANGUAGE8 }, - { TK_TEXT_LANGUAGE9, TEXT_LANGUAGE9 }, - { TK_TEXT_LANGUAGE10, TEXT_LANGUAGE10}, + { TK_TEXT_LANGUAGE1, SP_LANGUAGE_ENGLISH }, + { TK_TEXT_LANGUAGE2, SP_LANGUAGE_FRENCH }, + { TK_TEXT_LANGUAGE3, SP_LANGUAGE_GERMAN }, + { TK_TEXT_LANGUAGE4, SP_LANGUAGE_BRITISH }, + { TK_TEXT_LANGUAGE5, SP_LANGUAGE_KOREAN }, + { TK_TEXT_LANGUAGE6, SP_LANGUAGE_TAIWANESE }, + { TK_TEXT_LANGUAGE7, SP_LANGUAGE_ITALIAN }, + { TK_TEXT_LANGUAGE8, SP_LANGUAGE_SPANISH }, + { TK_TEXT_LANGUAGE9, SP_LANGUAGE_JAPANESE }, + { TK_TEXT_LANGUAGE10, SP_LANGUAGE_10}, { TK_INVALID, 0 } }; @@ -187,14 +187,14 @@ int FindToken(char *token, bool whole) { if (whole) { - if (strcmpi(token, Tokens[token_value]) == 0) + if (Q_stricmp(token, Tokens[token_value]) == 0) { return token_value; } } else { - if (_strnicmp(token, Tokens[token_value], strlen(Tokens[token_value])) == 0) + if (Q_stricmpn(token, Tokens[token_value], strlen(Tokens[token_value])) == 0) { i = strlen(Tokens[token_value]); while(token[i] == ' ') @@ -681,6 +681,7 @@ void cStringsED::SetNotes(char *newNotes) strcpy(Notes, newNotes); } + bool cStringsED::UnderstandToken(int token, char *data, cCriteria &Criteria) { sFlagPair *LanguagePair; @@ -816,6 +817,64 @@ void cStringsSingle::SetText(const char *newText) strcpy(Dest, newText); } +// fix problems caused by fucking morons entering clever "rich" chars in to new text files *after* the auto-stripper +// removed them all in the first place... +// +// ONLY DO THIS FOR ENGLISH, OR IT BREAKS ASIAN STRINGS!!!!!!!!!!!!!!!!!!!!! +// +static void FixIllegalChars(char *psText) +{ + char *p; + +// strXLS_Speech.Replace(va("%c",0x92),va("%c",0x27)); // "'" + while ((p=strchr(psText,0x92))!=NULL) // "rich" (and illegal) apostrophe + { + *p = 0x27; + } + +// strXLS_Speech.Replace(va("%c",0x93),"\""); // smart quotes -> '"' + while ((p=strchr(psText,0x93))!=NULL) // "rich" (and illegal) apostrophe + { + *p = '"'; + } + +// strXLS_Speech.Replace(va("%c",0x94),"\""); // smart quotes -> '"' + while ((p=strchr(psText,0x94))!=NULL) // "rich" (and illegal) apostrophe + { + *p = '"'; + } + +// strXLS_Speech.Replace(va("%c",0x0B),"."); // full stop + while ((p=strchr(psText,0x0B))!=NULL) // "rich" (and illegal) apostrophe + { + *p = '.'; + } + +// strXLS_Speech.Replace(va("%c",0x85),"..."); // "..."-char -> 3-char "..." + while ((p=strchr(psText,0x85))!=NULL) // "rich" (and illegal) apostrophe + { + *p = '.'; // can't do in-string replace of "." with "...", so just forget it + } + +// strXLS_Speech.Replace(va("%c",0x91),va("%c",0x27)); // "'" + while ((p=strchr(psText,0x91))!=NULL) // "rich" (and illegal) apostrophe + { + *p = 0x27; + } + +// strXLS_Speech.Replace(va("%c",0x96),va("%c",0x2D)); // "-" + while ((p=strchr(psText,0x96))!=NULL) + { + *p = 0x2D; + } + +// strXLS_Speech.Replace(va("%c",0x97),va("%c",0x2D)); // "-" + while ((p=strchr(psText,0x97))!=NULL) + { + *p = 0x2D; + } +} + bool cStringsSingle::UnderstandToken(int token, char *data) { sFlagPair *LanguagePair; @@ -823,15 +882,24 @@ bool cStringsSingle::UnderstandToken(int token, char *data) // switch(token) // { // default: + for(LanguagePair = LanguagePairs; LanguagePair->Name != TK_INVALID; LanguagePair++) { if (LanguagePair->Name == TK_TEXT_LANGUAGE1 && token == TK_TEXT_LANGUAGE1 && !Text) { // default to english in case there is no foreign + if (LanguagePair->Name == TK_TEXT_LANGUAGE1) + { + FixIllegalChars(data); + } SetText(data); return true; } else if (LanguagePair->Name == token && LanguagePair->Value == (int)sp_language->value) { + if (LanguagePair->Name == TK_TEXT_LANGUAGE1) + { + FixIllegalChars(data); + } SetText(data); return true; } @@ -1412,7 +1480,7 @@ cStringPackageSingle *SP_Register(const char *inPackage, unsigned char Registrat assert(SP_ListByName.size() == SP_ListByID.size()); Q_strncpyz(Package, inPackage, MAX_QPATH); - strupr(Package); + Q_strupr(Package); i = SP_ListByName.find(Package); if (i != SP_ListByName.end()) @@ -1616,11 +1684,7 @@ static void SP_UpdateLanguage(void) void SP_Init(void) { - sp_language = Cvar_Get("sp_language", va("%d", TEXT_LANGUAGE1), CVAR_ARCHIVE); - if (sp_language->integer>= TK_TEXT_LANGUAGE1){ // error correction for badly archived old stuff - sp_language->integer = TEXT_LANGUAGE1; - } - + sp_language = Cvar_Get("sp_language", va("%d", SP_LANGUAGE_ENGLISH), CVAR_ARCHIVE); sp_show_strip = Cvar_Get ("sv_show_strip", "0", 0); SP_UpdateLanguage(); diff --git a/CODE-mp/qcommon/strip.h b/CODE-mp/qcommon/strip.h index 94deb16..b461fb7 100644 --- a/CODE-mp/qcommon/strip.h +++ b/CODE-mp/qcommon/strip.h @@ -36,31 +36,13 @@ enum SP_LANGUAGE_TAIWANESE, SP_LANGUAGE_ITALIAN, SP_LANGUAGE_SPANISH, - SP_LANGUAGE_9, + SP_LANGUAGE_JAPANESE, SP_LANGUAGE_10, SP_LANGUGAGE_MAX, SP_LANGUAGE_ALL = 255 }; - -enum -{ - TEXT_LANGUAGE1 = 0, - TEXT_LANGUAGE2, - TEXT_LANGUAGE3, - TEXT_LANGUAGE4, - TEXT_LANGUAGE5, - TEXT_LANGUAGE6, - TEXT_LANGUAGE7, - TEXT_LANGUAGE8, - TEXT_LANGUAGE9, - TEXT_LANGUAGE10, - TEXT_MAX, - TEXT_ALL = 255 -}; - - #define SP_PACKAGE 0xff00 #define SP_STRING 0x00ff @@ -90,7 +72,7 @@ class cCriteria public: int WhichLanguage; - cCriteria(int initWhichLanguage = TEXT_ALL); + cCriteria(int initWhichLanguage = SP_LANGUAGE_ALL); }; #ifdef _STRIPED_ @@ -102,7 +84,7 @@ class cCriteriaED : public cCriteria public: cStringPackageED *Merge; - cCriteriaED(int initWhichLanguage = TEXT_ALL, cStringPackageED *initMerge = NULL); + cCriteriaED(int initWhichLanguage = SP_LANGUAGE_ALL, cStringPackageED *initMerge = NULL); }; @@ -325,5 +307,6 @@ void SP_Init(void); #endif +extern int Language_GetIntegerValue(void); #endif // __STRIP_H diff --git a/CODE-mp/qcommon/vm_ppc.cpp b/CODE-mp/qcommon/vm_ppc.cpp deleted file mode 100644 index 266c34d..0000000 --- a/CODE-mp/qcommon/vm_ppc.cpp +++ /dev/null @@ -1,1274 +0,0 @@ -// vm_ppc.c -// ppc dynamic compiler - -#include "vm_local.h" - -#pragma opt_pointer_analysis off - - -typedef enum { - R_REAL_STACK = 1, - // registers 3-11 are the parameter passing registers - - // state - R_STACK = 3, // local - R_OPSTACK, // global - - // constants - R_MEMBASE, // global - R_MEMMASK, - R_ASMCALL, // global - R_INSTRUCTIONS, // global - R_NUM_INSTRUCTIONS, // global - R_CVM, // currentVM - - // temps - R_TOP = 12, - R_SECOND = 13, - R_EA = 14 // effective address calculation - -} regNums_t; - -#define RG_REAL_STACK r1 -#define RG_STACK r3 -#define RG_OPSTACK r4 -#define RG_MEMBASE r5 -#define RG_MEMMASK r6 -#define RG_ASMCALL r7 -#define RG_INSTRUCTIONS r8 -#define RG_NUM_INSTRUCTIONS r9 -#define RG_CVM r10 -#define RG_TOP r12 -#define RG_SECOND r13 -#define RG_EA r14 - -// this doesn't have the low order bits set for instructions i'm not using... -typedef enum { - PPC_TDI = 0x08000000, - PPC_TWI = 0x0c000000, - PPC_MULLI = 0x1c000000, - PPC_SUBFIC = 0x20000000, - PPC_CMPI = 0x28000000, - PPC_CMPLI = 0x2c000000, - PPC_ADDIC = 0x30000000, - PPC_ADDIC_ = 0x34000000, - PPC_ADDI = 0x38000000, - PPC_ADDIS = 0x3c000000, - PPC_BC = 0x40000000, - PPC_SC = 0x44000000, - PPC_B = 0x48000000, - - PPC_MCRF = 0x4c000000, - PPC_BCLR = 0x4c000020, - PPC_RFID = 0x4c000000, - PPC_CRNOR = 0x4c000000, - PPC_RFI = 0x4c000000, - PPC_CRANDC = 0x4c000000, - PPC_ISYNC = 0x4c000000, - PPC_CRXOR = 0x4c000000, - PPC_CRNAND = 0x4c000000, - PPC_CREQV = 0x4c000000, - PPC_CRORC = 0x4c000000, - PPC_CROR = 0x4c000000, -//------------ - PPC_BCCTR = 0x4c000420, - PPC_RLWIMI = 0x50000000, - PPC_RLWINM = 0x54000000, - PPC_RLWNM = 0x5c000000, - PPC_ORI = 0x60000000, - PPC_ORIS = 0x64000000, - PPC_XORI = 0x68000000, - PPC_XORIS = 0x6c000000, - PPC_ANDI_ = 0x70000000, - PPC_ANDIS_ = 0x74000000, - PPC_RLDICL = 0x78000000, - PPC_RLDICR = 0x78000000, - PPC_RLDIC = 0x78000000, - PPC_RLDIMI = 0x78000000, - PPC_RLDCL = 0x78000000, - PPC_RLDCR = 0x78000000, - PPC_CMP = 0x7c000000, - PPC_TW = 0x7c000000, - PPC_SUBFC = 0x7c000010, - PPC_MULHDU = 0x7c000000, - PPC_ADDC = 0x7c000014, - PPC_MULHWU = 0x7c000000, - PPC_MFCR = 0x7c000000, - PPC_LWAR = 0x7c000000, - PPC_LDX = 0x7c000000, - PPC_LWZX = 0x7c00002e, - PPC_SLW = 0x7c000030, - PPC_CNTLZW = 0x7c000000, - PPC_SLD = 0x7c000000, - PPC_AND = 0x7c000038, - PPC_CMPL = 0x7c000040, - PPC_SUBF = 0x7c000050, - PPC_LDUX = 0x7c000000, -//------------ - PPC_DCBST = 0x7c000000, - PPC_LWZUX = 0x7c00006c, - PPC_CNTLZD = 0x7c000000, - PPC_ANDC = 0x7c000000, - PPC_TD = 0x7c000000, - PPC_MULHD = 0x7c000000, - PPC_MULHW = 0x7c000000, - PPC_MTSRD = 0x7c000000, - PPC_MFMSR = 0x7c000000, - PPC_LDARX = 0x7c000000, - PPC_DCBF = 0x7c000000, - PPC_LBZX = 0x7c0000ae, - PPC_NEG = 0x7c000000, - PPC_MTSRDIN = 0x7c000000, - PPC_LBZUX = 0x7c000000, - PPC_NOR = 0x7c0000f8, - PPC_SUBFE = 0x7c000000, - PPC_ADDE = 0x7c000000, - PPC_MTCRF = 0x7c000000, - PPC_MTMSR = 0x7c000000, - PPC_STDX = 0x7c000000, - PPC_STWCX_ = 0x7c000000, - PPC_STWX = 0x7c00012e, - PPC_MTMSRD = 0x7c000000, - PPC_STDUX = 0x7c000000, - PPC_STWUX = 0x7c00016e, - PPC_SUBFZE = 0x7c000000, - PPC_ADDZE = 0x7c000000, - PPC_MTSR = 0x7c000000, - PPC_STDCX_ = 0x7c000000, - PPC_STBX = 0x7c0001ae, - PPC_SUBFME = 0x7c000000, - PPC_MULLD = 0x7c000000, -//------------ - PPC_ADDME = 0x7c000000, - PPC_MULLW = 0x7c0001d6, - PPC_MTSRIN = 0x7c000000, - PPC_DCBTST = 0x7c000000, - PPC_STBUX = 0x7c000000, - PPC_ADD = 0x7c000214, - PPC_DCBT = 0x7c000000, - PPC_LHZX = 0x7c00022e, - PPC_EQV = 0x7c000000, - PPC_TLBIE = 0x7c000000, - PPC_ECIWX = 0x7c000000, - PPC_LHZUX = 0x7c000000, - PPC_XOR = 0x7c000278, - PPC_MFSPR = 0x7c0002a6, - PPC_LWAX = 0x7c000000, - PPC_LHAX = 0x7c000000, - PPC_TLBIA = 0x7c000000, - PPC_MFTB = 0x7c000000, - PPC_LWAUX = 0x7c000000, - PPC_LHAUX = 0x7c000000, - PPC_STHX = 0x7c00032e, - PPC_ORC = 0x7c000338, - PPC_SRADI = 0x7c000000, - PPC_SLBIE = 0x7c000000, - PPC_ECOWX = 0x7c000000, - PPC_STHUX = 0x7c000000, - PPC_OR = 0x7c000378, - PPC_DIVDU = 0x7c000000, - PPC_DIVWU = 0x7c000396, - PPC_MTSPR = 0x7c0003a6, - PPC_DCBI = 0x7c000000, - PPC_NAND = 0x7c000000, - PPC_DIVD = 0x7c000000, -//------------ - PPC_DIVW = 0x7c0003d6, - PPC_SLBIA = 0x7c000000, - PPC_MCRXR = 0x7c000000, - PPC_LSWX = 0x7c000000, - PPC_LWBRX = 0x7c000000, - PPC_LFSX = 0x7c000000, - PPC_SRW = 0x7c000430, - PPC_SRD = 0x7c000000, - PPC_TLBSYNC = 0x7c000000, - PPC_LFSUX = 0x7c000000, - PPC_MFSR = 0x7c000000, - PPC_LSWI = 0x7c000000, - PPC_SYNC = 0x7c000000, - PPC_LFDX = 0x7c000000, - PPC_LFDUX = 0x7c000000, - PPC_MFSRIN = 0x7c000000, - PPC_STSWX = 0x7c000000, - PPC_STWBRX = 0x7c000000, - PPC_STFSX = 0x7c000000, - PPC_STFSUX = 0x7c000000, - PPC_STSWI = 0x7c000000, - PPC_STFDX = 0x7c000000, - PPC_DCBA = 0x7c000000, - PPC_STFDUX = 0x7c000000, - PPC_LHBRX = 0x7c000000, - PPC_SRAW = 0x7c000630, - PPC_SRAD = 0x7c000000, - PPC_SRAWI = 0x7c000000, - PPC_EIEIO = 0x7c000000, - PPC_STHBRX = 0x7c000000, - PPC_EXTSH = 0x7c000734, - PPC_EXTSB = 0x7c000774, - PPC_ICBI = 0x7c000000, -//------------ - PPC_STFIWX = 0x7c0007ae, - PPC_EXTSW = 0x7c000000, - PPC_DCBZ = 0x7c000000, - PPC_LWZ = 0x80000000, - PPC_LWZU = 0x84000000, - PPC_LBZ = 0x88000000, - PPC_LBZU = 0x8c000000, - PPC_STW = 0x90000000, - PPC_STWU = 0x94000000, - PPC_STB = 0x98000000, - PPC_STBU = 0x9c000000, - PPC_LHZ = 0xa0000000, - PPC_LHZU = 0xa4000000, - PPC_LHA = 0xa8000000, - PPC_LHAU = 0xac000000, - PPC_STH = 0xb0000000, - PPC_STHU = 0xb4000000, - PPC_LMW = 0xb8000000, - PPC_STMW = 0xbc000000, - PPC_LFS = 0xc0000000, - PPC_LFSU = 0xc4000000, - PPC_LFD = 0xc8000000, - PPC_LFDU = 0xcc000000, - PPC_STFS = 0xd0000000, - PPC_STFSU = 0xd4000000, - PPC_STFD = 0xd8000000, - PPC_STFDU = 0xdc000000, - PPC_LD = 0xe8000000, - PPC_LDU = 0xe8000001, - PPC_LWA = 0xe8000002, - PPC_FDIVS = 0xec000024, - PPC_FSUBS = 0xec000028, - PPC_FADDS = 0xec00002a, -//------------ - PPC_FSQRTS = 0xec000000, - PPC_FRES = 0xec000000, - PPC_FMULS = 0xec000032, - PPC_FMSUBS = 0xec000000, - PPC_FMADDS = 0xec000000, - PPC_FNMSUBS = 0xec000000, - PPC_FNMADDS = 0xec000000, - PPC_STD = 0xf8000000, - PPC_STDU = 0xf8000001, - PPC_FCMPU = 0xfc000000, - PPC_FRSP = 0xfc000018, - PPC_FCTIW = 0xfc000000, - PPC_FCTIWZ = 0xfc00001e, - PPC_FDIV = 0xfc000000, - PPC_FSUB = 0xfc000028, - PPC_FADD = 0xfc000000, - PPC_FSQRT = 0xfc000000, - PPC_FSEL = 0xfc000000, - PPC_FMUL = 0xfc000000, - PPC_FRSQRTE = 0xfc000000, - PPC_FMSUB = 0xfc000000, - PPC_FMADD = 0xfc000000, - PPC_FNMSUB = 0xfc000000, - PPC_FNMADD = 0xfc000000, - PPC_FCMPO = 0xfc000000, - PPC_MTFSB1 = 0xfc000000, - PPC_FNEG = 0xfc000050, - PPC_MCRFS = 0xfc000000, - PPC_MTFSB0 = 0xfc000000, - PPC_FMR = 0xfc000000, - PPC_MTFSFI = 0xfc000000, - PPC_FNABS = 0xfc000000, - PPC_FABS = 0xfc000000, -//------------ - PPC_MFFS = 0xfc000000, - PPC_MTFSF = 0xfc000000, - PPC_FCTID = 0xfc000000, - PPC_FCTIDZ = 0xfc000000, - PPC_FCFID = 0xfc000000 - -} ppcOpcodes_t; - - -// the newly generated code -static unsigned *buf; -static int compiledOfs; // in dwords - -// fromt the original bytecode -static byte *code; -static int pc; - -void AsmCall( void ); - -double itofConvert[2]; - -static int Constant4( void ) { - int v; - - v = code[pc] | (code[pc+1]<<8) | (code[pc+2]<<16) | (code[pc+3]<<24); - pc += 4; - return v; -} - -static int Constant1( void ) { - int v; - - v = code[pc]; - pc += 1; - return v; -} - -static void Emit4( int i ) { - buf[ compiledOfs ] = i; - compiledOfs++; -} - -static void Inst( int opcode, int destReg, int aReg, int bReg ) { - unsigned r; - - r = opcode | ( destReg << 21 ) | ( aReg << 16 ) | ( bReg << 11 ) ; - buf[ compiledOfs ] = r; - compiledOfs++; -} - -static void Inst4( int opcode, int destReg, int aReg, int bReg, int cReg ) { - unsigned r; - - r = opcode | ( destReg << 21 ) | ( aReg << 16 ) | ( bReg << 11 ) | ( cReg << 6 ); - buf[ compiledOfs ] = r; - compiledOfs++; -} - -static void InstImm( int opcode, int destReg, int aReg, int immediate ) { - unsigned r; - - if ( immediate > 32767 || immediate < -32768 ) { - Com_Error( ERR_FATAL, "VM_Compile: immediate value %i out of range, opcode %x,%d,%d", immediate, opcode, destReg, aReg ); - } - r = opcode | ( destReg << 21 ) | ( aReg << 16 ) | ( immediate & 0xffff ); - buf[ compiledOfs ] = r; - compiledOfs++; -} - -static void InstImmU( int opcode, int destReg, int aReg, int immediate ) { - unsigned r; - - if ( immediate > 0xffff || immediate < 0 ) { - Com_Error( ERR_FATAL, "VM_Compile: immediate value %i out of range", immediate ); - } - r = opcode | ( destReg << 21 ) | ( aReg << 16 ) | ( immediate & 0xffff ); - buf[ compiledOfs ] = r; - compiledOfs++; -} - -static qboolean rtopped; -static int pop0, pop1, oc0, oc1; -static vm_t *tvm; -static int instruction; -static byte *jused; -static int pass; - -static void ltop() { - if (rtopped == qfalse) { - InstImm( PPC_LWZ, R_TOP, R_OPSTACK, 0 ); // get value from opstack - } -} - -static void ltopandsecond() { - if (pass>=0 && buf[compiledOfs-1] == (PPC_STWU | R_TOP<<21 | R_OPSTACK<<16 | 4 ) && jused[instruction]==0 ) { - compiledOfs--; - if (!pass) { - tvm->instructionPointers[instruction] = compiledOfs * 4; - } - InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); - } else if (pass>=0 && buf[compiledOfs-1] == (PPC_STW | R_TOP<<21 | R_OPSTACK<<16 | 0 ) && jused[instruction]==0 ) { - compiledOfs--; - if (!pass) { - tvm->instructionPointers[instruction] = compiledOfs * 4; - } - InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -8 ); - } else { - ltop(); // get value from opstack - InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -8 ); - } - rtopped = qfalse; -} - -// TJW: Unused -#if 0 -static void fltop() { - if (rtopped == qfalse) { - InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack - } -} -#endif - -static void fltopandsecond() { - InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_LFS, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -8 ); - rtopped = qfalse; - return; -} - -/* -================= -VM_Compile -================= -*/ -void VM_Compile( vm_t *vm, vmHeader_t *header ) { - int op; - int maxLength; - int v; - int i; - - // set up the into-to-float variables - ((int *)itofConvert)[0] = 0x43300000; - ((int *)itofConvert)[1] = 0x80000000; - ((int *)itofConvert)[2] = 0x43300000; - - // allocate a very large temp buffer, we will shrink it later - maxLength = header->codeLength * 8; - buf = Z_Malloc( maxLength ); - jused = Z_Malloc(header->instructionCount + 2); - Com_Memset(jused, 0, header->instructionCount+2); - - // compile everything twice, so the second pass will have valid instruction - // pointers for branches - for ( pass = -1 ; pass < 2 ; pass++ ) { - - rtopped = qfalse; - // translate all instructions - pc = 0; - - pop0 = 343545; - pop1 = 2443545; - oc0 = -2343535; - oc1 = 24353454; - tvm = vm; - - code = (byte *)header + header->codeOffset; - compiledOfs = 0; -#ifndef __GNUC__ - // metrowerks seems to require this header in front of functions - Emit4( (int)(buf+2) ); - Emit4( 0 ); -#endif - - for ( instruction = 0 ; instruction < header->instructionCount ; instruction++ ) { - if ( compiledOfs*4 > maxLength - 16 ) { - Com_Error( ERR_DROP, "VM_Compile: maxLength exceeded" ); - } - - op = code[ pc ]; - if ( !pass ) { - vm->instructionPointers[ instruction ] = compiledOfs * 4; - } - pc++; - switch ( op ) { - case 0: - break; - case OP_BREAK: - InstImmU( PPC_ADDI, R_TOP, 0, 0 ); - InstImm( PPC_LWZ, R_TOP, R_TOP, 0 ); // *(int *)0 to crash to debugger - rtopped = qfalse; - break; - case OP_ENTER: - InstImm( PPC_ADDI, R_STACK, R_STACK, -Constant4() ); // sub R_STACK, R_STACK, imm - rtopped = qfalse; - break; - case OP_CONST: - v = Constant4(); - if (code[pc] == OP_LOAD4 || code[pc] == OP_LOAD2 || code[pc] == OP_LOAD1) { - v &= vm->dataMask; - } - if ( v < 32768 && v >= -32768 ) { - InstImmU( PPC_ADDI, R_TOP, 0, v & 0xffff ); - } else { - InstImmU( PPC_ADDIS, R_TOP, 0, (v >> 16)&0xffff ); - if ( v & 0xffff ) { - InstImmU( PPC_ORI, R_TOP, R_TOP, v & 0xffff ); - } - } - if (code[pc] == OP_LOAD4) { - Inst( PPC_LWZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base - pc++; - instruction++; - } else if (code[pc] == OP_LOAD2) { - Inst( PPC_LHZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base - pc++; - instruction++; - } else if (code[pc] == OP_LOAD1) { - Inst( PPC_LBZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base - pc++; - instruction++; - } - if (code[pc] == OP_STORE4) { - InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); - //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it - Inst( PPC_STWX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base - pc++; - instruction++; - rtopped = qfalse; - break; - } else if (code[pc] == OP_STORE2) { - InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); - //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it - Inst( PPC_STHX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base - pc++; - instruction++; - rtopped = qfalse; - break; - } else if (code[pc] == OP_STORE1) { - InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); - //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it - Inst( PPC_STBX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base - pc++; - instruction++; - rtopped = qfalse; - break; - } - if (code[pc] == OP_JUMP) { - jused[v] = 1; - } - InstImm( PPC_STWU, R_TOP, R_OPSTACK, 4 ); - rtopped = qtrue; - break; - case OP_LOCAL: - oc0 = oc1; - oc1 = Constant4(); - if (code[pc] == OP_LOAD4 || code[pc] == OP_LOAD2 || code[pc] == OP_LOAD1) { - oc1 &= vm->dataMask; - } - InstImm( PPC_ADDI, R_TOP, R_STACK, oc1 ); - if (code[pc] == OP_LOAD4) { - Inst( PPC_LWZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base - pc++; - instruction++; - } else if (code[pc] == OP_LOAD2) { - Inst( PPC_LHZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base - pc++; - instruction++; - } else if (code[pc] == OP_LOAD1) { - Inst( PPC_LBZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base - pc++; - instruction++; - } - if (code[pc] == OP_STORE4) { - InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); - //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it - Inst( PPC_STWX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base - pc++; - instruction++; - rtopped = qfalse; - break; - } else if (code[pc] == OP_STORE2) { - InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); - //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it - Inst( PPC_STHX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base - pc++; - instruction++; - rtopped = qfalse; - break; - } else if (code[pc] == OP_STORE1) { - InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); - //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it - Inst( PPC_STBX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base - pc++; - instruction++; - rtopped = qfalse; - break; - } - InstImm( PPC_STWU, R_TOP, R_OPSTACK, 4 ); - rtopped = qtrue; - break; - case OP_ARG: - ltop(); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); - InstImm( PPC_ADDI, R_EA, R_STACK, Constant1() ); // location to put it - Inst( PPC_STWX, R_TOP, R_EA, R_MEMBASE ); - rtopped = qfalse; - break; - case OP_CALL: - Inst( PPC_MFSPR, R_SECOND, 8, 0 ); // move from link register - InstImm( PPC_STWU, R_SECOND, R_REAL_STACK, -16 ); // save off the old return address - - Inst( PPC_MTSPR, R_ASMCALL, 9, 0 ); // move to count register - Inst( PPC_BCCTR | 1, 20, 0, 0 ); // jump and link to the count register - - InstImm( PPC_LWZ, R_SECOND, R_REAL_STACK, 0 ); // fetch the old return address - InstImm( PPC_ADDI, R_REAL_STACK, R_REAL_STACK, 16 ); - Inst( PPC_MTSPR, R_SECOND, 8, 0 ); // move to link register - rtopped = qfalse; - break; - case OP_PUSH: - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, 4 ); - rtopped = qfalse; - break; - case OP_POP: - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); - rtopped = qfalse; - break; - case OP_LEAVE: - InstImm( PPC_ADDI, R_STACK, R_STACK, Constant4() ); // add R_STACK, R_STACK, imm - Inst( PPC_BCLR, 20, 0, 0 ); // branch unconditionally to link register - rtopped = qfalse; - break; - case OP_LOAD4: - ltop(); // get value from opstack - //Inst( PPC_AND, R_MEMMASK, R_TOP, R_TOP ); // mask it - Inst( PPC_LWZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); - rtopped = qtrue; - break; - case OP_LOAD2: - ltop(); // get value from opstack - //Inst( PPC_AND, R_MEMMASK, R_TOP, R_TOP ); // mask it - Inst( PPC_LHZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); - rtopped = qtrue; - break; - case OP_LOAD1: - ltop(); // get value from opstack - //Inst( PPC_AND, R_MEMMASK, R_TOP, R_TOP ); // mask it - Inst( PPC_LBZX, R_TOP, R_TOP, R_MEMBASE ); // load from memory base - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); - rtopped = qtrue; - break; - case OP_STORE4: - ltopandsecond(); // get value from opstack - //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it - Inst( PPC_STWX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base - rtopped = qfalse; - break; - case OP_STORE2: - ltopandsecond(); // get value from opstack - //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it - Inst( PPC_STHX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base - rtopped = qfalse; - break; - case OP_STORE1: - ltopandsecond(); // get value from opstack - //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND ); // mask it - Inst( PPC_STBX, R_TOP, R_SECOND, R_MEMBASE ); // store from memory base - rtopped = qfalse; - break; - - case OP_EQ: - ltopandsecond(); // get value from opstack - Inst( PPC_CMP, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - InstImm( PPC_BC, 4, 2, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4(PPC_B | (v&0x3ffffff) ); - rtopped = qfalse; - break; - case OP_NE: - ltopandsecond(); // get value from opstack - Inst( PPC_CMP, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - InstImm( PPC_BC, 12, 2, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4(PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( PPC_BC, 4, 2, v ); - - rtopped = qfalse; - break; - case OP_LTI: - ltopandsecond(); // get value from opstack - Inst( PPC_CMP, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - InstImm( PPC_BC, 4, 0, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4(PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( PPC_BC, 12, 0, v ); - rtopped = qfalse; - break; - case OP_LEI: - ltopandsecond(); // get value from opstack - Inst( PPC_CMP, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - InstImm( PPC_BC, 12, 1, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4(PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( PPC_BC, 4, 1, v ); - rtopped = qfalse; - break; - case OP_GTI: - ltopandsecond(); // get value from opstack - Inst( PPC_CMP, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - InstImm( PPC_BC, 4, 1, 8 ); - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - Emit4(PPC_B | (unsigned int)(v&0x3ffffff) ); -// InstImm( PPC_BC, 12, 1, v ); - rtopped = qfalse; - break; - case OP_GEI: - ltopandsecond(); // get value from opstack - Inst( PPC_CMP, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - InstImm( PPC_BC, 4, 0, v ); - rtopped = qfalse; - break; - case OP_LTU: - ltopandsecond(); // get value from opstack - Inst( PPC_CMPL, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - InstImm( PPC_BC, 12, 0, v ); - rtopped = qfalse; - break; - case OP_LEU: - ltopandsecond(); // get value from opstack - Inst( PPC_CMPL, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - InstImm( PPC_BC, 4, 1, v ); - rtopped = qfalse; - break; - case OP_GTU: - ltopandsecond(); // get value from opstack - Inst( PPC_CMPL, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - InstImm( PPC_BC, 12, 1, v ); - rtopped = qfalse; - break; - case OP_GEU: - ltopandsecond(); // get value from opstack - Inst( PPC_CMPL, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - InstImm( PPC_BC, 4, 0, v ); - rtopped = qfalse; - break; - - case OP_EQF: - fltopandsecond(); // get value from opstack - Inst( PPC_FCMPU, 0, R_TOP, R_SECOND ); - i = Constant4(); - jused[i] = 1; - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - InstImm( PPC_BC, 12, 2, v ); - rtopped = qfalse; - break; - case OP_NEF: - fltopandsecond(); // get value from opstack - Inst( PPC_FCMPU, 0, R_TOP, R_SECOND ); - i = Constant4(); - jused[i] = 1; - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - InstImm( PPC_BC, 4, 2, v ); - rtopped = qfalse; - break; - case OP_LTF: - fltopandsecond(); // get value from opstack - Inst( PPC_FCMPU, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - InstImm( PPC_BC, 12, 0, v ); - rtopped = qfalse; - break; - case OP_LEF: - fltopandsecond(); // get value from opstack - Inst( PPC_FCMPU, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - InstImm( PPC_BC, 4, 1, v ); - rtopped = qfalse; - break; - case OP_GTF: - fltopandsecond(); // get value from opstack - Inst( PPC_FCMPU, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - InstImm( PPC_BC, 12, 1, v ); - rtopped = qfalse; - break; - case OP_GEF: - fltopandsecond(); // get value from opstack - Inst( PPC_FCMPU, 0, R_SECOND, R_TOP ); - i = Constant4(); - jused[i] = 1; - if ( pass==1 ) { - v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs]; - } else { - v = 0; - } - InstImm( PPC_BC, 4, 0, v ); - rtopped = qfalse; - break; - - case OP_NEGI: - ltop(); // get value from opstack - InstImm( PPC_SUBFIC, R_TOP, R_TOP, 0 ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_ADD: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_ADD, R_TOP, R_TOP, R_SECOND ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_SUB: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_SUBF, R_TOP, R_TOP, R_SECOND ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_DIVI: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_DIVW, R_TOP, R_SECOND, R_TOP ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_DIVU: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_DIVWU, R_TOP, R_SECOND, R_TOP ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_MODI: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_DIVW, R_EA, R_SECOND, R_TOP ); - Inst( PPC_MULLW, R_EA, R_TOP, R_EA ); - Inst( PPC_SUBF, R_TOP, R_EA, R_SECOND ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_MODU: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_DIVWU, R_EA, R_SECOND, R_TOP ); - Inst( PPC_MULLW, R_EA, R_TOP, R_EA ); - Inst( PPC_SUBF, R_TOP, R_EA, R_SECOND ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_MULI: - case OP_MULU: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_MULLW, R_TOP, R_SECOND, R_TOP ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_BAND: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_AND, R_SECOND, R_TOP, R_TOP ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_BOR: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_OR, R_SECOND, R_TOP, R_TOP ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_BXOR: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_XOR, R_SECOND, R_TOP, R_TOP ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_BCOM: - ltop(); // get value from opstack - Inst( PPC_NOR, R_TOP, R_TOP, R_TOP ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_LSH: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_SLW, R_SECOND, R_TOP, R_TOP ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_RSHI: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_SRAW, R_SECOND, R_TOP, R_TOP ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - case OP_RSHU: - ltop(); // get value from opstack - InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_SRW, R_SECOND, R_TOP, R_TOP ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qtrue; - break; - - case OP_NEGF: - InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack - Inst( PPC_FNEG, R_TOP, 0, R_TOP ); - InstImm( PPC_STFS, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qfalse; - break; - case OP_ADDF: - InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_LFSU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_FADDS, R_TOP, R_SECOND, R_TOP ); - InstImm( PPC_STFS, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qfalse; - break; - case OP_SUBF: - InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_LFSU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_FSUBS, R_TOP, R_SECOND, R_TOP ); - InstImm( PPC_STFS, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qfalse; - break; - case OP_DIVF: - InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_LFSU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst( PPC_FDIVS, R_TOP, R_SECOND, R_TOP ); - InstImm( PPC_STFS, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qfalse; - break; - case OP_MULF: - InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack - InstImm( PPC_LFSU, R_SECOND, R_OPSTACK, -4 ); // get value from opstack - Inst4( PPC_FMULS, R_TOP, R_SECOND, 0, R_TOP ); - InstImm( PPC_STFS, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qfalse; - break; - - case OP_CVIF: - v = (int)&itofConvert; - InstImmU( PPC_ADDIS, R_EA, 0, (v >> 16)&0xffff ); - InstImmU( PPC_ORI, R_EA, R_EA, v & 0xffff ); - InstImm( PPC_LWZ, R_TOP, R_OPSTACK, 0 ); // get value from opstack - InstImmU( PPC_XORIS, R_TOP, R_TOP, 0x8000 ); - InstImm( PPC_STW, R_TOP, R_EA, 12 ); - InstImm( PPC_LFD, R_TOP, R_EA, 0 ); - InstImm( PPC_LFD, R_SECOND, R_EA, 8 ); - Inst( PPC_FSUB, R_TOP, R_SECOND, R_TOP ); - // Inst( PPC_FRSP, R_TOP, 0, R_TOP ); - InstImm( PPC_STFS, R_TOP, R_OPSTACK, 0 ); // save value to opstack - rtopped = qfalse; - break; - case OP_CVFI: - InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack - Inst( PPC_FCTIWZ, R_TOP, 0, R_TOP ); - Inst( PPC_STFIWX, R_TOP, 0, R_OPSTACK ); // save value to opstack - rtopped = qfalse; - break; - case OP_SEX8: - ltop(); // get value from opstack - Inst( PPC_EXTSB, R_TOP, R_TOP, 0 ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); - rtopped = qtrue; - break; - case OP_SEX16: - ltop(); // get value from opstack - Inst( PPC_EXTSH, R_TOP, R_TOP, 0 ); - InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 ); - rtopped = qtrue; - break; - - case OP_BLOCK_COPY: - v = Constant4() >> 2; - ltop(); // source - InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, -4 ); // dest - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -8 ); - InstImmU( PPC_ADDI, R_EA, 0, v ); // count - // FIXME: range check - Inst( PPC_MTSPR, R_EA, 9, 0 ); // move to count register - - Inst( PPC_ADD, R_TOP, R_TOP, R_MEMBASE ); - InstImm( PPC_ADDI, R_TOP, R_TOP, -4 ); - Inst( PPC_ADD, R_SECOND, R_SECOND, R_MEMBASE ); - InstImm( PPC_ADDI, R_SECOND, R_SECOND, -4 ); - - InstImm( PPC_LWZU, R_EA, R_TOP, 4 ); // source - InstImm( PPC_STWU, R_EA, R_SECOND, 4 ); // dest - Inst( PPC_BC | 0xfff8 , 16, 0, 0 ); // loop - rtopped = qfalse; - break; - - case OP_JUMP: - ltop(); // get value from opstack - InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 ); - Inst( PPC_RLWINM | ( 29 << 1 ), R_TOP, R_TOP, 2 ); - // FIXME: range check - Inst( PPC_LWZX, R_TOP, R_TOP, R_INSTRUCTIONS ); - Inst( PPC_MTSPR, R_TOP, 9, 0 ); // move to count register - Inst( PPC_BCCTR, 20, 0, 0 ); // jump to the count register - rtopped = qfalse; - break; - default: - Com_Error( ERR_DROP, "VM_CompilePPC: bad opcode %i at instruction %i, offset %i", op, instruction, pc ); - } - pop0 = pop1; - pop1 = op; - } - - Com_Printf( "VM file %s pass %d compiled to %i bytes of code\n", vm->name, (pass+1), compiledOfs*4 ); - - if ( pass == 0 ) { - // copy to an exact size buffer on the hunk - vm->codeLength = compiledOfs * 4; - vm->codeBase = Hunk_Alloc( vm->codeLength, h_low ); - Com_Memcpy( vm->codeBase, buf, vm->codeLength ); - Z_Free( buf ); - - // offset all the instruction pointers for the new location - for ( i = 0 ; i < header->instructionCount ; i++ ) { - vm->instructionPointers[i] += (int)vm->codeBase; - } - - // go back over it in place now to fixup reletive jump targets - buf = (unsigned *)vm->codeBase; - } - } - Z_Free( jused ); -} - -/* -============== -VM_CallCompiled - -This function is called directly by the generated code -============== -*/ -int VM_CallCompiled( vm_t *vm, int *args ) { - int stack[1024]; - int programStack; - int stackOnEntry; - byte *image; - - currentVM = vm; - - // interpret the code - vm->currentlyInterpreting = qtrue; - - // we might be called recursively, so this might not be the very top - programStack = vm->programStack; - stackOnEntry = programStack; - image = vm->dataBase; - - // set up the stack frame - programStack -= 48; - - *(int *)&image[ programStack + 44] = args[9]; - *(int *)&image[ programStack + 40] = args[8]; - *(int *)&image[ programStack + 36] = args[7]; - *(int *)&image[ programStack + 32] = args[6]; - *(int *)&image[ programStack + 28] = args[5]; - *(int *)&image[ programStack + 24] = args[4]; - *(int *)&image[ programStack + 20] = args[3]; - *(int *)&image[ programStack + 16] = args[2]; - *(int *)&image[ programStack + 12] = args[1]; - *(int *)&image[ programStack + 8 ] = args[0]; - *(int *)&image[ programStack + 4 ] = 0; // return stack - *(int *)&image[ programStack ] = -1; // will terminate the loop on return - - // off we go into generated code... - // the PPC calling standard says the parms will all go into R3 - R11, so - // no special asm code is needed here -#ifdef __GNUC__ - ((void(*)(int, int, int, int, int, int, int, int))(vm->codeBase))( - programStack, (int)&stack, - (int)image, vm->dataMask, (int)&AsmCall, - (int)vm->instructionPointers, vm->instructionPointersLength, - (int)vm ); -#else - ((void(*)(int, int, int, int, int, int, int, int))(vm->codeBase))( - programStack, (int)&stack, - (int)image, vm->dataMask, *(int *)&AsmCall /* skip function pointer header */, - (int)vm->instructionPointers, vm->instructionPointersLength, - (int)vm ); -#endif - vm->programStack = stackOnEntry; - - vm->currentlyInterpreting = qfalse; - - return stack[1]; -} - - -/* -================== -AsmCall - -Put this at end of file because gcc messes up debug line numbers -================== -*/ -#ifdef __GNUC__ - -void AsmCall( void ) { -asm (" - // pop off the destination instruction - lwz r12,0(r4) // RG_TOP, 0(RG_OPSTACK) - addi r4,r4,-4 // RG_OPSTACK, RG_OPSTACK, -4 - - // see if it is a system trap - cmpwi r12,0 // RG_TOP, 0 - bc 12,0, systemTrap - - // calling another VM function, so lookup in instructionPointers - slwi r12,r12,2 // RG_TOP,RG_TOP,2 - // FIXME: range check - lwzx r12, r8, r12 // RG_TOP, RG_INSTRUCTIONS(RG_TOP) - mtctr r12 // RG_TOP -"); - - -#if defined(MACOS_X) && defined(__OPTIMIZE__) - // On Mac OS X, gcc doesn't push a frame when we are optimized, so trying to tear it down results in grave disorder. -#warning Mac OS X optimization on, not popping GCC AsmCall frame -#else - // Mac OS X Server and unoptimized compiles include a GCC AsmCall frame - asm (" - lwz r1,0(r1) // pop off the GCC AsmCall frame - lmw r30,-8(r1) -"); -#endif - -asm (" - bcctr 20,0 // when it hits a leave, it will branch to the current link register - - // calling a system trap -systemTrap: - // convert to positive system call number - subfic r12,r12,-1 - - // save all our registers, including the current link register - mflr r13 // RG_SECOND // copy off our link register - addi r1,r1,-92 // required 24 byets of linkage, 32 bytes of parameter, plus our saves - stw r3,56(r1) // RG_STACK, -36(REAL_STACK) - stw r4,60(r1) // RG_OPSTACK, 4(RG_REAL_STACK) - stw r5,64(r1) // RG_MEMBASE, 8(RG_REAL_STACK) - stw r6,68(r1) // RG_MEMMASK, 12(RG_REAL_STACK) - stw r7,72(r1) // RG_ASMCALL, 16(RG_REAL_STACK) - stw r8,76(r1) // RG_INSTRUCTIONS, 20(RG_REAL_STACK) - stw r9,80(r1) // RG_NUM_INSTRUCTIONS, 24(RG_REAL_STACK) - stw r10,84(r1) // RG_VM, 28(RG_REAL_STACK) - stw r13,88(r1) // RG_SECOND, 32(RG_REAL_STACK) // link register - - // save the vm stack position to allow recursive VM entry - addi r13,r3,-4 // RG_TOP, RG_STACK, -4 - stw r13,0(r10) //RG_TOP, VM_OFFSET_PROGRAM_STACK(RG_VM) - - // save the system call number as the 0th parameter - add r3,r3,r5 // r3, RG_STACK, RG_MEMBASE // r3 is the first parameter to vm->systemCalls - stwu r12,4(r3) // RG_TOP, 4(r3) - - // make the system call with the address of all the VM parms as a parameter - // vm->systemCalls( &parms ) - lwz r12,4(r10) // RG_TOP, VM_OFFSET_SYSTEM_CALL(RG_VM) - mtctr r12 // RG_TOP - bcctrl 20,0 - mr r12,r3 // RG_TOP, r3 - - // pop our saved registers - lwz r3,56(r1) // RG_STACK, 0(RG_REAL_STACK) - lwz r4,60(r1) // RG_OPSTACK, 4(RG_REAL_STACK) - lwz r5,64(r1) // RG_MEMBASE, 8(RG_REAL_STACK) - lwz r6,68(r1) // RG_MEMMASK, 12(RG_REAL_STACK) - lwz r7,72(r1) // RG_ASMCALL, 16(RG_REAL_STACK) - lwz r8,76(r1) // RG_INSTRUCTIONS, 20(RG_REAL_STACK) - lwz r9,80(r1) // RG_NUM_INSTRUCTIONS, 24(RG_REAL_STACK) - lwz r10,84(r1) // RG_VM, 28(RG_REAL_STACK) - lwz r13,88(r1) // RG_SECOND, 32(RG_REAL_STACK) - addi r1,r1,92 // RG_REAL_STACK, RG_REAL_STACK, 36 - - // restore the old link register - mtlr r13 // RG_SECOND - - // save off the return value - stwu r12,4(r4) // RG_TOP, 0(RG_OPSTACK) - - // GCC adds its own prolog / epliog code -" ); -} - -#endif diff --git a/CODE-mp/qcommon/vssver.scc b/CODE-mp/qcommon/vssver.scc deleted file mode 100644 index 68b468e..0000000 Binary files a/CODE-mp/qcommon/vssver.scc and /dev/null differ diff --git a/CODE-mp/renderer/matcomp.c b/CODE-mp/renderer/matcomp.c index 7275098..024dfb7 100644 --- a/CODE-mp/renderer/matcomp.c +++ b/CODE-mp/renderer/matcomp.c @@ -216,7 +216,7 @@ void MC_UnCompress(float mat[3][4],const unsigned char * comp) } -void MC_UnCompressQuat(float mat[3][4],const unsigned short * pwIn) +void MC_UnCompressQuat(float mat[3][4],const unsigned char * comp) { float w,x,y,z,f; float fTx; @@ -232,6 +232,8 @@ void MC_UnCompressQuat(float mat[3][4],const unsigned short * pwIn) float fTyz; float fTzz; + const unsigned short *pwIn = (unsigned short *) comp; + w = *pwIn++; w/=16383.0f; w-=2.0f; diff --git a/CODE-mp/renderer/matcomp.h b/CODE-mp/renderer/matcomp.h index 89c27f8..8d2a0aa 100644 --- a/CODE-mp/renderer/matcomp.h +++ b/CODE-mp/renderer/matcomp.h @@ -21,7 +21,8 @@ extern "C" void MC_Compress(const float mat[3][4],unsigned char * comp); void MC_UnCompress(float mat[3][4],const unsigned char * comp); -void MC_UnCompressQuat(float mat[3][4],const unsigned short * pwIn); +void MC_UnCompressQuat(float mat[3][4],const unsigned char * comp); + #ifdef __cplusplus diff --git a/CODE-mp/renderer/mdx_format.h b/CODE-mp/renderer/mdx_format.h index e8239d0..113c9f9 100644 --- a/CODE-mp/renderer/mdx_format.h +++ b/CODE-mp/renderer/mdx_format.h @@ -25,9 +25,8 @@ // // normal version numbers... // -#define MDXM_VERSION 4 -#define MDXA_VERSION 4 -#define MDXA_VERSION_QUAT 5 +#define MDXM_VERSION 6 +#define MDXA_VERSION 6 // (Note that since there is now a "_info.txt" file written out by carcass any changes made in here that // introduce new data should also be reflected in the info-output) @@ -54,12 +53,19 @@ // triangle side-ordering stuff for tags... // -#define iG2_TRISIDE_MIDDLE 1 -#define iG2_TRISIDE_LONGEST 0 -#define iG2_TRISIDE_SHORTEST 2 +#define iG2_TRISIDE_MIDDLE 1 +#define iG2_TRISIDE_LONGEST 0 +#define iG2_TRISIDE_SHORTEST 2 + +#define fG2_BONEWEIGHT_RECIPROCAL_MULT ((float)(1.0f/1023.0f)) +#define iG2_BITS_PER_BONEREF 5 +#define iMAX_G2_BONEREFS_PER_SURFACE (1<... + // (note that I've defined it using '>' internally, so it sorts with higher weights being "less", for distance weight-culling + // + #ifdef __cplusplus + bool operator < (const mdxmWeight_t& _X) const {return (boneWeight>_X.boneWeight);} + #endif +} +#ifndef __cplusplus +mdxmWeight_t +#endif +; +*/ +/* #ifdef __cplusplus struct mdxaCompBone_t #else @@ -91,7 +114,7 @@ typedef struct mdxaCompBone_t #endif ; - +*/ #ifdef __cplusplus struct mdxaCompQuatBone_t #else @@ -206,8 +229,6 @@ typedef struct { int numTriangles; int ofsTriangles; - int maxVertBoneWeights; // ... per vertex for hardware to reference. This number subtract the vert->numWeights gives # pad weights (which software will ignore) - // Bone references are a set of ints representing all the bones // present in any vertex weights for this surface. This is // needed because a model may have surfaces that need to be @@ -235,17 +256,72 @@ typedef struct { // for each vert... (mdxmSurface_t->numVerts) // { // mdxVertex_t - this is an array with number of verts from the surface definition as its bounds. It contains normal info, texture coors and number of weightings for this bone - + // (this is now kept at 32 bytes for cache-aligning) typedef struct { vec3_t normal; vec3_t vertCoords; - vec2_t texCoords; - int numWeights; // remember, this is for software counts, look at mdxmSurface_t->numActualWeights for skipping purposes to account for padded weights - mdxmWeight_t weights[1]; // variable sized + + // packed int... + unsigned int uiNmWeightsAndBoneIndexes; // 32 bits. format: + // 31 & 30: 0..3 (= 1..4) weight count + // 29 & 28 (spare) + // 2 bit pairs at 20,22,24,26 are 2-bit overflows from 4 BonWeights below (20=[0], 22=[1]) etc) + // 5-bits each (iG2_BITS_PER_BONEREF) for boneweights + // effectively a packed int, each bone weight converted from 0..1 float to 0..255 int... + // promote each entry to float and multiply by fG2_BONEWEIGHT_RECIPROCAL_MULT to convert. + byte BoneWeightings[iMAX_G2_BONEWEIGHTS_PER_VERT]; // 4 + } mdxmVertex_t; // } vert +#ifdef __cplusplus + +// these are convenience functions that I can invoked in code. Do NOT change them (because this is a shared file), +// but if you want to copy the logic out and use your own versions then fine... +// +static inline int G2_GetVertWeights( const mdxmVertex_t *pVert ) +{ + int iNumWeights = (pVert->uiNmWeightsAndBoneIndexes >> 30)+1; // 1..4 count + + return iNumWeights; +} + +static inline int G2_GetVertBoneIndex( const mdxmVertex_t *pVert, const int iWeightNum) +{ + int iBoneIndex = (pVert->uiNmWeightsAndBoneIndexes>>(iG2_BITS_PER_BONEREF*iWeightNum))&((1<BoneWeightings[iWeightNum]; + iTemp|= (pVert->uiNmWeightsAndBoneIndexes >> (iG2_BONEWEIGHT_TOPBITS_SHIFT+(iWeightNum*2)) ) & iG2_BONEWEIGHT_TOPBITS_AND; + + float fBoneWeight = fG2_BONEWEIGHT_RECIPROCAL_MULT * iTemp; + + if (fBoneWeight == 0.0f) + { + // special case to fix flatten-to-zero cases for extremely light weightings. Could probably be omitted if desperate... + // + fBoneWeight = 0.00045f; + } + + return fBoneWeight; +} +#endif + // for each vert... (mdxmSurface_t->numVerts) (seperated from mdxmVertex_t struct for cache reasons) + // { + // mdxVertex_t - this is an array with number of verts from the surface definition as its bounds. It contains normal info, texture coors and number of weightings for this bone + + typedef struct { + vec2_t texCoords; + } mdxmVertexTexCoord_t; + + // } vert + + // } surface // } LOD @@ -271,7 +347,7 @@ typedef struct { // frames and bones are shared by all levels of detail // int numFrames; - int ofsFrames; + int ofsFrames; // points at mdxaFrame_t array int numBones; // (no offset to these since they're inside the frames array) int ofsCompBonePool; // offset to global compressed-bone pool that all frames use int ofsSkel; // offset to mdxaSkel_t info @@ -307,18 +383,24 @@ typedef struct { // } - -// for each frame... (mdxaHeader_t->numFrames) -// { - // mdxaFrame_t - which contains the header for the bones for this surface, plus the actual bone matrices themselves - - typedef struct { - // (used to contain frame bounds info etc as well, doesn't now) - // - int boneIndexes[1]; // [numBones] ... into compressed bone pool - } mdxaFrame_t; // struct size = (int)( &((mdxaFrame_t *)0)->bones[ mdxaHeader_t->numBones ] ); - -// } + // (offset @ mdxaHeader_t->ofsFrames) + // + // array of 3 byte indices here (hey, 25% saving over 4-byte really adds up)... + // + // + // access as follows to get the index for a given + // + // (iFrameNum * mdxaHeader_t->numBones * 3) + (iBoneNum * 3) + // + // then read the int at that location and AND it with 0x00FFFFFF. I use the struct below simply for easy searches + typedef struct + { + int iIndex; // this struct for pointing purposes, need to and with 0x00FFFFFF to be meaningful + } mdxaIndex_t; + // + // (note that there's then an alignement-pad here to get the next struct back onto 32-bit alignement) + // + // this index then points into the following... // Compressed-bone pool that all frames use (mdxaHeader_t->ofsCompBonePool) (defined at end because size unknown until end) diff --git a/CODE-mp/renderer/mssccprj.scc b/CODE-mp/renderer/mssccprj.scc deleted file mode 100644 index ff8df37..0000000 --- a/CODE-mp/renderer/mssccprj.scc +++ /dev/null @@ -1,5 +0,0 @@ -SCC = This is a Source Code Control file - -[renderer.dsp] -SCC_Aux_Path = "\\ravend\vss_projects\jk2sof2MP" -SCC_Project_Name = "$/General/code/renderer", YAAAAAAA diff --git a/CODE-mp/renderer/ref_trin.def b/CODE-mp/renderer/ref_trin.def deleted file mode 100644 index 2fefbd3..0000000 --- a/CODE-mp/renderer/ref_trin.def +++ /dev/null @@ -1,2 +0,0 @@ -EXPORTS - GetRefAPI diff --git a/CODE-mp/renderer/renderer.dsp b/CODE-mp/renderer/renderer.dsp deleted file mode 100644 index 044ff63..0000000 --- a/CODE-mp/renderer/renderer.dsp +++ /dev/null @@ -1,914 +0,0 @@ -# Microsoft Developer Studio Project File - Name="renderer" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=renderer - Win32 Debug TA -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "renderer.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "renderer.mak" CFG="renderer - Win32 Debug TA" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "renderer - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "renderer - Win32 Debug" (based on "Win32 (x86) Static Library") -!MESSAGE "renderer - Win32 Release TA" (based on "Win32 (x86) Static Library") -!MESSAGE "renderer - Win32 Debug TA" (based on "Win32 (x86) Static Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName ""$/MissionPack/code/renderer", EJBAAAAA" -# PROP Scc_LocalPath "." -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "renderer - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /G6 /W4 /GX /O2 /Ob2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo - -!ELSEIF "$(CFG)" == "renderer - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /G5 /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /YX /FD /GZ /c -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo - -!ELSEIF "$(CFG)" == "renderer - Win32 Release TA" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "renderer___Win32_Release_TA" -# PROP BASE Intermediate_Dir "renderer___Win32_Release_TA" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release_TA" -# PROP Intermediate_Dir "Release_TA" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /G6 /W4 /GX /O2 /Ob2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D "__USEA3D" /D "__A3D_GEOM" /YX /FD /c -# ADD CPP /nologo /G6 /W4 /GX /O2 /Ob2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo - -!ELSEIF "$(CFG)" == "renderer - Win32 Debug TA" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "renderer___Win32_Debug_TA" -# PROP BASE Intermediate_Dir "renderer___Win32_Debug_TA" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug_TA" -# PROP Intermediate_Dir "Debug_TA" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /G5 /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /D "__USEA3D" /D "__A3D_GEOM" /FR /YX /FD /GZ /c -# ADD CPP /nologo /G5 /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /YX /FD /GZ /c -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo - -!ENDIF - -# Begin Target - -# Name "renderer - Win32 Release" -# Name "renderer - Win32 Debug" -# Name "renderer - Win32 Release TA" -# Name "renderer - Win32 Debug TA" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\ref_trin.def -# End Source File -# Begin Source File - -SOURCE=.\tr_animation.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_backend.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_bsp.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_cmds.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_curve.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_flares.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_font.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_image.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_init.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_light.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_main.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_marks.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_mesh.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_model.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_noise.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_scene.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_shade.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_shade_calc.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_shader.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_shadows.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_sky.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_surface.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_world.cpp -# End Source File -# Begin Source File - -SOURCE=..\win32\win_gamma.cpp -# End Source File -# Begin Source File - -SOURCE=..\win32\win_glimp.cpp -# End Source File -# Begin Source File - -SOURCE=..\win32\win_qgl.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=..\qcommon\cm_public.h -# End Source File -# Begin Source File - -SOURCE=..\win32\glw_win.h -# End Source File -# Begin Source File - -SOURCE=..\game\q_shared.h -# End Source File -# Begin Source File - -SOURCE=..\qcommon\qcommon.h -# End Source File -# Begin Source File - -SOURCE=..\qcommon\qfiles.h -# End Source File -# Begin Source File - -SOURCE=.\qgl.h -# End Source File -# Begin Source File - -SOURCE=..\game\surfaceflags.h -# End Source File -# Begin Source File - -SOURCE=.\tr_local.h -# End Source File -# Begin Source File - -SOURCE=.\tr_public.h -# End Source File -# Begin Source File - -SOURCE=..\cgame\tr_types.h -# End Source File -# Begin Source File - -SOURCE=..\win32\win_local.h -# End Source File -# End Group -# Begin Group "jpeg" - -# PROP Default_Filter "" -# Begin Group "Source Files No. 1" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE="..\jpeg-6\jcapimin.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jccoefct.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jccolor.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jcdctmgr.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jchuff.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jcinit.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jcmainct.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jcmarker.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jcmaster.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jcomapi.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jcparam.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jcphuff.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jcprepct.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jcsample.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jctrans.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jdapimin.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jdapistd.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jdatadst.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jdatasrc.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jdcoefct.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jdcolor.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jddctmgr.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jdhuff.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jdinput.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jdmainct.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jdmarker.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jdmaster.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jdpostct.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jdsample.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jdtrans.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jerror.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jfdctflt.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jidctflt.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jmemmgr.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jmemnobs.cpp" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jutils.cpp" -# End Source File -# End Group -# Begin Group "Header Files No. 1" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE="..\jpeg-6\jchuff.h" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jconfig.h" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jdct.h" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jdhuff.h" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jerror.h" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jinclude.h" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jmemsys.h" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jmorecfg.h" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jpegint.h" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jpeglib.h" -# End Source File -# Begin Source File - -SOURCE="..\jpeg-6\jversion.h" -# End Source File -# Begin Source File - -SOURCE=..\ft2\sfdriver.h -# End Source File -# Begin Source File - -SOURCE=..\ft2\sfobjs.h -# End Source File -# Begin Source File - -SOURCE=..\ft2\ttcmap.h -# End Source File -# Begin Source File - -SOURCE=..\ft2\ttload.h -# End Source File -# Begin Source File - -SOURCE=..\ft2\ttpost.h -# End Source File -# Begin Source File - -SOURCE=..\ft2\ttsbit.h -# End Source File -# End Group -# End Group -# Begin Group "FreeType2" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Group "Include files" - -# PROP Default_Filter "*.h" -# Begin Source File - -SOURCE=..\ft2\ahangles.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ahglobal.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ahglyph.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ahhint.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ahloader.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ahmodule.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ahoptim.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ahtypes.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\autohint.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\freetype.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftbbox.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftcalc.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftconfig.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftdebug.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftdriver.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\fterrors.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftextend.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftglyph.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftgrays.h -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftimage.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftlist.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftmemory.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftmm.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftmodule.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftnames.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftobjs.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftoption.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftoutln.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftraster.h -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftrend1.h -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftrender.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftsmooth.h -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftstream.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftsystem.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\fttypes.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\psnames.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\sfnt.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\t1errors.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\t1tables.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\t1types.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\t2errors.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\t2types.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ttdriver.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\tterrors.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ttgload.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ttinterp.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ttnameid.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ttobjs.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\ttpload.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\tttables.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\tttags.h -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\ft2\tttypes.h -# PROP Exclude_From_Build 1 -# End Source File -# End Group -# Begin Source File - -SOURCE=..\ft2\ahangles.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ahglobal.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ahglyph.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ahhint.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ahmodule.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ahoptim.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftcalc.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftdebug.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftextend.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftglyph.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftgrays.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftinit.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftlist.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftmm.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftnames.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftobjs.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftoutln.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftraster.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftrend1.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftsmooth.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftstream.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ftsystem.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\sfdriver.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\sfobjs.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ttcmap.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ttdriver.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ttgload.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ttinterp.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ttload.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ttobjs.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ttpload.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ttpost.cpp -# End Source File -# Begin Source File - -SOURCE=..\ft2\ttsbit.cpp -# End Source File -# End Group -# End Target -# End Project diff --git a/CODE-mp/renderer/tr_ghoul2.cpp b/CODE-mp/renderer/tr_ghoul2.cpp index 7725914..f7fe5fb 100644 --- a/CODE-mp/renderer/tr_ghoul2.cpp +++ b/CODE-mp/renderer/tr_ghoul2.cpp @@ -426,23 +426,24 @@ void Multiply_3x4Matrix(mdxaBone_t *out, mdxaBone_t *in2, mdxaBone_t *in) } -static inline void UnCompressBone(float mat[3][4], int iBonePoolIndex, const mdxaHeader_t *pMDXAHeader) +static int G2_GetBonePoolIndex( const mdxaHeader_t *pMDXAHeader, int iFrame, int iBone) { - if (pMDXAHeader->version == MDXA_VERSION) - { - const mdxaCompBone_t * const pCompBonePool = (mdxaCompBone_t *) ((byte *)pMDXAHeader + pMDXAHeader->ofsCompBonePool); - MC_UnCompress(mat, pCompBonePool[iBonePoolIndex].Comp); - } - else - { - // quat... - // - const mdxaCompQuatBone_t * const pCompBonePool = (mdxaCompQuatBone_t *) ((byte *)pMDXAHeader + pMDXAHeader->ofsCompBonePool); - MC_UnCompressQuat(mat, (unsigned short*)pCompBonePool[iBonePoolIndex].Comp); - } + const int iOffsetToIndex = (iFrame * pMDXAHeader->numBones * 3) + (iBone * 3); + + mdxaIndex_t *pIndex = (mdxaIndex_t *) ((byte*) pMDXAHeader + pMDXAHeader->ofsFrames + iOffsetToIndex); + + return pIndex->iIndex & 0x00FFFFFF; // this will cause problems for big-endian machines... ;-) } +/*static inline*/ void UnCompressBone(float mat[3][4], int iBoneIndex, const mdxaHeader_t *pMDXAHeader, int iFrame) +{ + mdxaCompQuatBone_t *pCompBonePool = (mdxaCompQuatBone_t *) ((byte *)pMDXAHeader + pMDXAHeader->ofsCompBonePool); + MC_UnCompressQuat(mat, pCompBonePool[ G2_GetBonePoolIndex( pMDXAHeader, iFrame, iBoneIndex ) ].Comp); +} + + + // transform each individual bone's information - making sure to use any override information provided, both for angles and for animations, as // well as multiplying each bone's matrix by it's parents matrix void G2_TransformBone (CTransformBone &TB) @@ -724,13 +725,13 @@ void G2_TransformBone (CTransformBone &TB) const float frontlerp = 1.0 - backlerp; // figure out where the location of the blended animation data is - const mdxaFrame_t *bFrame = (mdxaFrame_t *)((byte *)TB.header + TB.header->ofsFrames + (int)TB.blendFrame * TB.frameSize ); - const mdxaFrame_t *boldFrame =(mdxaFrame_t *)((byte *)TB.header + TB.header->ofsFrames + TB.blendOldFrame * TB.frameSize ); +// const mdxaFrame_t *bFrame = (mdxaFrame_t *)((byte *)TB.header + TB.header->ofsFrames + (int)TB.blendFrame * TB.frameSize ); +// const mdxaFrame_t *boldFrame =(mdxaFrame_t *)((byte *)TB.header + TB.header->ofsFrames + TB.blendOldFrame * TB.frameSize ); // MC_UnCompress(tbone[3].matrix,compBonePointer[bFrame->boneIndexes[TB.child]].Comp); // MC_UnCompress(tbone[4].matrix,compBonePointer[boldFrame->boneIndexes[TB.child]].Comp); - UnCompressBone(tbone[3].matrix,bFrame->boneIndexes[TB.child], TB.header); - UnCompressBone(tbone[4].matrix,boldFrame->boneIndexes[TB.child], TB.header); + UnCompressBone(tbone[3].matrix,TB.child, TB.header, TB.blendFrame); + UnCompressBone(tbone[4].matrix,TB.child, TB.header, TB.blendOldFrame); for ( j = 0 ; j < 12 ; j++ ) { @@ -741,7 +742,7 @@ void G2_TransformBone (CTransformBone &TB) // figure out where the location of the bone animation data is assert (TB.header->numFrames > TB.currentFrame);//validate the frame we're about to grab - const mdxaFrame_t *aoldFrame = (mdxaFrame_t *)((byte *)TB.header + TB.header->ofsFrames + TB.currentFrame * TB.frameSize ); +// const mdxaFrame_t *aoldFrame = (mdxaFrame_t *)((byte *)TB.header + TB.header->ofsFrames + TB.currentFrame * TB.frameSize ); // figure out where the bone hirearchy info is offsets = (mdxaSkelOffsets_t *)((byte *)TB.header + sizeof(mdxaHeader_t)); @@ -752,7 +753,7 @@ void G2_TransformBone (CTransformBone &TB) if (!TB.backlerp) { // MC_UnCompress(tbone[2].matrix,compBonePointer[aoldFrame->boneIndexes[TB.child]].Comp); - UnCompressBone(tbone[2].matrix,aoldFrame->boneIndexes[TB.child], TB.header); + UnCompressBone(tbone[2].matrix,TB.child, TB.header, TB.currentFrame); // blend in the other frame if we need to if (TB.blendMode) @@ -776,12 +777,12 @@ void G2_TransformBone (CTransformBone &TB) const float frontlerp = 1.0 - TB.backlerp; // figure out where the location of the bone animation data is assert (TB.header->numFrames > TB.newFrame);//validate the frame we're about to grab - const mdxaFrame_t *aFrame = (mdxaFrame_t *)((byte *)TB.header + TB.header->ofsFrames + TB.newFrame * TB.frameSize ); +// const mdxaFrame_t *aFrame = (mdxaFrame_t *)((byte *)TB.header + TB.header->ofsFrames + TB.newFrame * TB.frameSize ); // MC_UnCompress(tbone[0].matrix,compBonePointer[aFrame->boneIndexes[TB.child]].Comp); // MC_UnCompress(tbone[1].matrix,compBonePointer[aoldFrame->boneIndexes[TB.child]].Comp); - UnCompressBone(tbone[0].matrix,aFrame->boneIndexes[TB.child], TB.header); - UnCompressBone(tbone[1].matrix,aoldFrame->boneIndexes[TB.child], TB.header); + UnCompressBone(tbone[0].matrix,TB.child, TB.header, TB.newFrame); + UnCompressBone(tbone[1].matrix,TB.child, TB.header, TB.currentFrame); for ( j = 0 ; j < 12 ; j++ ) { @@ -1018,7 +1019,7 @@ void G2_TransformGhoulBones( mdxaHeader_t *header, int *usedBoneList, boneInfo_v int frameSize, i; int rootBoneIndex = 0; - frameSize = (int)( &((mdxaFrame_t *)0)->boneIndexes[ header->numBones ] ); + frameSize = 0;// unused (int)( &((mdxaFrame_t *)0)->boneIndexes[ header->numBones ] ); // figure out where our rootbone is for (i=0; iofsVerts); - for (j = 0; jweights[/*vert0->numWeights*/originalSurf->maxVertBoneWeights]; - } + vert0+= index0; + vert1 = (mdxmVertex_t *) ((byte *)originalSurf + originalSurf->ofsVerts); - for (j = 0; jweights[/*vert1->numWeights*/originalSurf->maxVertBoneWeights]; - } + vert1+= index1; + vert2 = (mdxmVertex_t *) ((byte *)originalSurf + originalSurf->ofsVerts); - for (j = 0; jweights[/*vert2->numWeights*/originalSurf->maxVertBoneWeights]; - } + vert2+= index2; // clear out the triangle verts to be VectorClear( pTri[0] ); VectorClear( pTri[1] ); VectorClear( pTri[2] ); - mdxmWeight_t *w; +// mdxmWeight_t *w; int *piBoneRefs = (int*) ((byte*)originalSurf + originalSurf->ofsBoneReferences); // now go and transform just the points we need from the surface that was hit originally - w = vert0->weights; - for ( k = 0 ; k < vert0->numWeights ; k++, w++ ) +// w = vert0->weights; + int iNumWeights = G2_GetVertWeights( vert0 ); + for ( k = 0 ; k < iNumWeights ; k++ ) { - pTri[0][0] += w->boneWeight * ( DotProduct( bonePtr[piBoneRefs[w->boneIndex]].matrix[0], vert0->vertCoords ) + bonePtr[piBoneRefs[w->boneIndex]].matrix[0][3] ); - pTri[0][1] += w->boneWeight * ( DotProduct( bonePtr[piBoneRefs[w->boneIndex]].matrix[1], vert0->vertCoords ) + bonePtr[piBoneRefs[w->boneIndex]].matrix[1][3] ); - pTri[0][2] += w->boneWeight * ( DotProduct( bonePtr[piBoneRefs[w->boneIndex]].matrix[2], vert0->vertCoords ) + bonePtr[piBoneRefs[w->boneIndex]].matrix[2][3] ); + int iBoneIndex = G2_GetVertBoneIndex( vert0, k ); + float fBoneWeight = G2_GetVertBoneWeight( vert0, k ); + + pTri[0][0] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].matrix[0], vert0->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].matrix[0][3] ); + pTri[0][1] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].matrix[1], vert0->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].matrix[1][3] ); + pTri[0][2] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].matrix[2], vert0->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].matrix[2][3] ); } - w = vert1->weights; - for ( k = 0 ; k < vert1->numWeights ; k++, w++ ) +// w = vert1->weights; + iNumWeights = G2_GetVertWeights( vert1 ); + for ( k = 0 ; k < iNumWeights ; k++ ) { - pTri[1][0] += w->boneWeight * ( DotProduct( bonePtr[piBoneRefs[w->boneIndex]].matrix[0], vert1->vertCoords ) + bonePtr[piBoneRefs[w->boneIndex]].matrix[0][3] ); - pTri[1][1] += w->boneWeight * ( DotProduct( bonePtr[piBoneRefs[w->boneIndex]].matrix[1], vert1->vertCoords ) + bonePtr[piBoneRefs[w->boneIndex]].matrix[1][3] ); - pTri[1][2] += w->boneWeight * ( DotProduct( bonePtr[piBoneRefs[w->boneIndex]].matrix[2], vert1->vertCoords ) + bonePtr[piBoneRefs[w->boneIndex]].matrix[2][3] ); + int iBoneIndex = G2_GetVertBoneIndex( vert1, k ); + float fBoneWeight = G2_GetVertBoneWeight( vert1, k ); + + pTri[1][0] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].matrix[0], vert1->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].matrix[0][3] ); + pTri[1][1] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].matrix[1], vert1->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].matrix[1][3] ); + pTri[1][2] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].matrix[2], vert1->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].matrix[2][3] ); } - w = vert2->weights; - for ( k = 0 ; k < vert2->numWeights ; k++, w++ ) +// w = vert2->weights; + iNumWeights = G2_GetVertWeights( vert2 ); + for ( k = 0 ; k < iNumWeights ; k++ ) { - pTri[2][0] += w->boneWeight * ( DotProduct( bonePtr[piBoneRefs[w->boneIndex]].matrix[0], vert2->vertCoords ) + bonePtr[piBoneRefs[w->boneIndex]].matrix[0][3] ); - pTri[2][1] += w->boneWeight * ( DotProduct( bonePtr[piBoneRefs[w->boneIndex]].matrix[1], vert2->vertCoords ) + bonePtr[piBoneRefs[w->boneIndex]].matrix[1][3] ); - pTri[2][2] += w->boneWeight * ( DotProduct( bonePtr[piBoneRefs[w->boneIndex]].matrix[2], vert2->vertCoords ) + bonePtr[piBoneRefs[w->boneIndex]].matrix[2][3] ); + int iBoneIndex = G2_GetVertBoneIndex( vert2, k ); + float fBoneWeight = G2_GetVertBoneWeight( vert2, k ); + + pTri[2][0] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].matrix[0], vert2->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].matrix[0][3] ); + pTri[2][1] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].matrix[1], vert2->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].matrix[1][3] ); + pTri[2][2] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].matrix[2], vert2->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].matrix[2][3] ); } vec3_t normal; @@ -1177,20 +1184,26 @@ void G2_ProcessSurfaceBolt(mdxaBone_v &bonePtr, mdxmSurface_t *surface, int bolt v = (mdxmVertex_t *) ((byte *)surface + surface->ofsVerts); for ( j = 0; j < 3; j++ ) { - mdxmWeight_t *w; +// mdxmWeight_t *w; VectorClear( pTri[j] ); - w = v->weights; - for ( k = 0 ; k < v->numWeights ; k++, w++ ) + // w = v->weights; + + const int iNumWeights = G2_GetVertWeights( v ); + + for ( k = 0 ; k < iNumWeights ; k++ ) { + int iBoneIndex = G2_GetVertBoneIndex( v, k ); + float fBoneWeight = G2_GetVertBoneWeight( v, k ); + //bone = bonePtr + piBoneRefs[w->boneIndex]; - pTri[j][0] += w->boneWeight * ( DotProduct( bonePtr[piBoneRefs[w->boneIndex]].matrix[0], v->vertCoords ) + bonePtr[piBoneRefs[w->boneIndex]].matrix[0][3] ); - pTri[j][1] += w->boneWeight * ( DotProduct( bonePtr[piBoneRefs[w->boneIndex]].matrix[1], v->vertCoords ) + bonePtr[piBoneRefs[w->boneIndex]].matrix[1][3] ); - pTri[j][2] += w->boneWeight * ( DotProduct( bonePtr[piBoneRefs[w->boneIndex]].matrix[2], v->vertCoords ) + bonePtr[piBoneRefs[w->boneIndex]].matrix[2][3] ); + pTri[j][0] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].matrix[0], v->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].matrix[0][3] ); + pTri[j][1] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].matrix[1], v->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].matrix[1][3] ); + pTri[j][2] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].matrix[2], v->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].matrix[2][3] ); } - v = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surface->maxVertBoneWeights]; + v++;// = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surface->maxVertBoneWeights]; } // clear out used arrays @@ -2017,32 +2030,36 @@ void RB_SurfaceGhoul( CRenderableSurface *surf ) { const int *piBoneRefs = (int*) ((byte*)surface + surface->ofsBoneReferences); const int numVerts = surface->numVerts; const mdxmVertex_t *v = (mdxmVertex_t *) ((byte *)surface + surface->ofsVerts); + mdxmVertexTexCoord_t *pTexCoords = (mdxmVertexTexCoord_t *) &v[numVerts]; int baseVert = tess.numVertexes; for ( j = 0; j < numVerts; j++, baseVert++ ) { - const int numWeights = v->numWeights; - const mdxmWeight_t *w = v->weights; + const int iNumWeights = G2_GetVertWeights( v ); +// const mdxmWeight_t *w = v->weights; VectorClear( tess.xyz[baseVert]); VectorClear( tess.normal[baseVert]); - for ( k = 0 ; k < numWeights ; k++, w++ ) + for ( k = 0 ; k < iNumWeights ; k++ ) { - mdxaBone_t &bone = bonePtr[piBoneRefs[w->boneIndex]]; + int iBoneIndex = G2_GetVertBoneIndex( v, k ); + float fBoneWeight = G2_GetVertBoneWeight( v, k ); - tess.xyz[baseVert][0] += w->boneWeight * ( DotProduct( bone.matrix[0], v->vertCoords ) + bone.matrix[0][3] ); - tess.xyz[baseVert][1] += w->boneWeight * ( DotProduct( bone.matrix[1], v->vertCoords ) + bone.matrix[1][3] ); - tess.xyz[baseVert][2] += w->boneWeight * ( DotProduct( bone.matrix[2], v->vertCoords ) + bone.matrix[2][3] ); + mdxaBone_t &bone = bonePtr[piBoneRefs[iBoneIndex]]; - tess.normal[baseVert][0] += w->boneWeight * DotProduct( bone.matrix[0], v->normal ); - tess.normal[baseVert][1] += w->boneWeight * DotProduct( bone.matrix[1], v->normal ); - tess.normal[baseVert][2] += w->boneWeight * DotProduct( bone.matrix[2], v->normal ); + tess.xyz[baseVert][0] += fBoneWeight * ( DotProduct( bone.matrix[0], v->vertCoords ) + bone.matrix[0][3] ); + tess.xyz[baseVert][1] += fBoneWeight * ( DotProduct( bone.matrix[1], v->vertCoords ) + bone.matrix[1][3] ); + tess.xyz[baseVert][2] += fBoneWeight * ( DotProduct( bone.matrix[2], v->vertCoords ) + bone.matrix[2][3] ); + + tess.normal[baseVert][0] += fBoneWeight * DotProduct( bone.matrix[0], v->normal ); + tess.normal[baseVert][1] += fBoneWeight * DotProduct( bone.matrix[1], v->normal ); + tess.normal[baseVert][2] += fBoneWeight * DotProduct( bone.matrix[2], v->normal ); } - tess.texCoords[baseVert][0][0] = v->texCoords[0]; - tess.texCoords[baseVert][0][1] = v->texCoords[1]; + tess.texCoords[baseVert][0][0] = pTexCoords[j].texCoords[0]; + tess.texCoords[baseVert][0][1] = pTexCoords[j].texCoords[1]; - v = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surface->maxVertBoneWeights]; + v++;// = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surface->maxVertBoneWeights]; } tess.numVertexes += surface->numVerts; @@ -2182,7 +2199,7 @@ qboolean R_LoadMDXM( model_t *mod, void *buffer, const char *mod_name, qboolean LL(surf->ofsHeader); LL(surf->numBoneReferences); LL(surf->ofsBoneReferences); - LL(surf->maxVertBoneWeights); +// LL(surf->maxVertBoneWeights); triCount += surf->numTriangles; @@ -2304,9 +2321,9 @@ qboolean R_LoadMDXA( model_t *mod, void *buffer, const char *mod_name, qboolean size = LittleLong(size); } - if (version != MDXA_VERSION && version != MDXA_VERSION_QUAT) { - ri.Printf( PRINT_WARNING, "R_LoadMDXA: %s has wrong version (%i should be %i or %i)\n", - mod_name, version, MDXA_VERSION, MDXA_VERSION_QUAT); + if (version != MDXA_VERSION) { + ri.Printf( PRINT_WARNING, "R_LoadMDXA: %s has wrong version (%i should be %i)\n", + mod_name, version, MDXA_VERSION); return qfalse; } diff --git a/CODE-mp/renderer/tr_init.cpp b/CODE-mp/renderer/tr_init.cpp index 78441e3..5ebb56b 100644 --- a/CODE-mp/renderer/tr_init.cpp +++ b/CODE-mp/renderer/tr_init.cpp @@ -148,7 +148,6 @@ cvar_t *r_directedScale; cvar_t *r_debugLight; cvar_t *r_debugSort; cvar_t *r_printShaders; -cvar_t *r_saveFontData; cvar_t *r_maxpolys; int max_polys; @@ -849,10 +848,11 @@ void R_Register( void ) r_subdivisions = ri.Cvar_Get ("r_subdivisions", "4", CVAR_ARCHIVE | CVAR_LATCH); #ifdef MACOS_X // Default to using SMP on Mac OS X if we have multiple processors - r_smp = ri.Cvar_Get( "r_smp", Sys_ProcessorCount() > 1 ? "1" : "0", CVAR_ARCHIVE | CVAR_LATCH); +// r_smp = ri.Cvar_Get( "r_smp", Sys_ProcessorCount() > 1 ? "1" : "0", CVAR_ARCHIVE | CVAR_LATCH); #else - r_smp = ri.Cvar_Get( "r_smp", "0", CVAR_ARCHIVE | CVAR_LATCH); +// r_smp = ri.Cvar_Get( "r_smp", "0", CVAR_ARCHIVE | CVAR_LATCH); #endif + r_smp = ri.Cvar_Get( "r_smp", "0", CVAR_ROM); r_ignoreFastPath = ri.Cvar_Get( "r_ignoreFastPath", "1", CVAR_ARCHIVE | CVAR_LATCH ); // @@ -897,12 +897,11 @@ void R_Register( void ) // // temporary variables that can change at any time // - r_showImages = ri.Cvar_Get( "r_showImages", "0", CVAR_TEMP ); + r_showImages = ri.Cvar_Get( "r_showImages", "0", CVAR_CHEAT ); r_debugLight = ri.Cvar_Get( "r_debuglight", "0", CVAR_TEMP ); r_debugSort = ri.Cvar_Get( "r_debugSort", "0", CVAR_CHEAT ); r_printShaders = ri.Cvar_Get( "r_printShaders", "0", 0 ); - r_saveFontData = ri.Cvar_Get( "r_saveFontData", "0", 0 ); r_surfaceSprites = ri.Cvar_Get ("r_surfaceSprites", "1", CVAR_CHEAT); r_surfaceWeather = ri.Cvar_Get ("r_surfaceWeather", "0", 0); @@ -955,8 +954,8 @@ void R_Register( void ) /* Ghoul2 Insert Start */ - r_noServerGhoul2 = ri.Cvar_Get( "r_noserverghoul2", "0", 0); - r_noGhoul2 = ri.Cvar_Get( "r_noghoul2", "0", 0); + r_noServerGhoul2 = ri.Cvar_Get( "r_noserverghoul2", "0", CVAR_CHEAT); + r_noGhoul2 = ri.Cvar_Get( "r_noghoul2", "0", CVAR_CHEAT); /* Ghoul2 Insert End */ @@ -997,9 +996,12 @@ void R_Init( void ) { // Swap_Init(); +#ifndef FINAL_BUILD if ( (int)tess.xyz & 15 ) { - Com_Printf( "WARNING: tess.xyz not 16 byte aligned\n" ); + Com_Printf( "WARNING: tess.xyz not 16 byte aligned (%x)\n",(int)tess.xyz & 15 ); } +#endif + Com_Memset( tess.constantColor255, 255, sizeof( tess.constantColor255 ) ); // diff --git a/CODE-mp/renderer/tr_light.cpp b/CODE-mp/renderer/tr_light.cpp index a94b9f2..51fcec8 100644 --- a/CODE-mp/renderer/tr_light.cpp +++ b/CODE-mp/renderer/tr_light.cpp @@ -44,7 +44,7 @@ void R_DlightBmodel( bmodel_t *bmodel ) { msurface_t *surf; // transform all the lights - R_TransformDlights( tr.refdef.num_dlights, tr.refdef.dlights, &tr.or ); + R_TransformDlights( tr.refdef.num_dlights, tr.refdef.dlights, &tr.ori ); mask = 0; for ( i=0 ; iinteger, but set to 0 if no hw gamma - orientationr_t or; // for current entity + orientationr_t ori; // for current entity trRefdef_t refdef; @@ -1213,7 +1213,6 @@ extern cvar_t *r_showImages; extern cvar_t *r_debugSort; extern cvar_t *r_printShaders; -extern cvar_t *r_saveFontData; /* Ghoul2 Insert Start */ @@ -1424,7 +1423,7 @@ typedef struct stageVars #define NUM_TEX_COORDS (MAXLIGHTMAPS+1) -typedef struct shaderCommands_s +struct shaderCommands_s { glIndex_t indexes[SHADER_MAX_INDEXES]; vec4_t xyz[SHADER_MAX_VERTEXES]; @@ -1454,7 +1453,9 @@ typedef struct shaderCommands_s qboolean SSInitializedWind; -} shaderCommands_t; +}; + +typedef __declspec(align(16)) shaderCommands_s shaderCommands_t; extern shaderCommands_t tess; extern color4ub_t styleColors[MAX_LIGHT_STYLES]; diff --git a/CODE-mp/renderer/tr_main.cpp b/CODE-mp/renderer/tr_main.cpp index 98d2f90..ad6bde9 100644 --- a/CODE-mp/renderer/tr_main.cpp +++ b/CODE-mp/renderer/tr_main.cpp @@ -47,10 +47,10 @@ int R_CullLocalBox (vec3_t bounds[2]) { v[1] = bounds[(i>>1)&1][1]; v[2] = bounds[(i>>2)&1][2]; - VectorCopy( tr.or.origin, transformed[i] ); - VectorMA( transformed[i], v[0], tr.or.axis[0], transformed[i] ); - VectorMA( transformed[i], v[1], tr.or.axis[1], transformed[i] ); - VectorMA( transformed[i], v[2], tr.or.axis[2], transformed[i] ); + VectorCopy( tr.ori.origin, transformed[i] ); + VectorMA( transformed[i], v[0], tr.ori.axis[0], transformed[i] ); + VectorMA( transformed[i], v[1], tr.ori.axis[1], transformed[i] ); + VectorMA( transformed[i], v[2], tr.ori.axis[2], transformed[i] ); } // check against frustum planes @@ -155,9 +155,9 @@ R_LocalNormalToWorld ================= */ void R_LocalNormalToWorld (vec3_t local, vec3_t world) { - world[0] = local[0] * tr.or.axis[0][0] + local[1] * tr.or.axis[1][0] + local[2] * tr.or.axis[2][0]; - world[1] = local[0] * tr.or.axis[0][1] + local[1] * tr.or.axis[1][1] + local[2] * tr.or.axis[2][1]; - world[2] = local[0] * tr.or.axis[0][2] + local[1] * tr.or.axis[1][2] + local[2] * tr.or.axis[2][2]; + world[0] = local[0] * tr.ori.axis[0][0] + local[1] * tr.ori.axis[1][0] + local[2] * tr.ori.axis[2][0]; + world[1] = local[0] * tr.ori.axis[0][1] + local[1] * tr.ori.axis[1][1] + local[2] * tr.ori.axis[2][1]; + world[2] = local[0] * tr.ori.axis[0][2] + local[1] * tr.ori.axis[1][2] + local[2] * tr.ori.axis[2][2]; } /* @@ -167,9 +167,9 @@ R_LocalPointToWorld ================= */ void R_LocalPointToWorld (vec3_t local, vec3_t world) { - world[0] = local[0] * tr.or.axis[0][0] + local[1] * tr.or.axis[1][0] + local[2] * tr.or.axis[2][0] + tr.or.origin[0]; - world[1] = local[0] * tr.or.axis[0][1] + local[1] * tr.or.axis[1][1] + local[2] * tr.or.axis[2][1] + tr.or.origin[1]; - world[2] = local[0] * tr.or.axis[0][2] + local[1] * tr.or.axis[1][2] + local[2] * tr.or.axis[2][2] + tr.or.origin[2]; + world[0] = local[0] * tr.ori.axis[0][0] + local[1] * tr.ori.axis[1][0] + local[2] * tr.ori.axis[2][0] + tr.ori.origin[0]; + world[1] = local[0] * tr.ori.axis[0][1] + local[1] * tr.ori.axis[1][1] + local[2] * tr.ori.axis[2][1] + tr.ori.origin[1]; + world[2] = local[0] * tr.ori.axis[0][2] + local[1] * tr.ori.axis[1][2] + local[2] * tr.ori.axis[2][2] + tr.ori.origin[2]; } float preTransEntMatrix[16]; @@ -208,9 +208,9 @@ R_WorldToLocal ================= */ void R_WorldToLocal (vec3_t world, vec3_t local) { - local[0] = DotProduct(world, tr.or.axis[0]); - local[1] = DotProduct(world, tr.or.axis[1]); - local[2] = DotProduct(world, tr.or.axis[2]); + local[0] = DotProduct(world, tr.ori.axis[0]); + local[1] = DotProduct(world, tr.ori.axis[1]); + local[2] = DotProduct(world, tr.ori.axis[2]); } /* @@ -361,11 +361,11 @@ void R_RotateForViewer (void) float viewerMatrix[16]; vec3_t origin; - Com_Memset (&tr.or, 0, sizeof(tr.or)); - tr.or.axis[0][0] = 1; - tr.or.axis[1][1] = 1; - tr.or.axis[2][2] = 1; - VectorCopy (tr.viewParms.or.origin, tr.or.viewOrigin); + Com_Memset (&tr.ori, 0, sizeof(tr.ori)); + tr.ori.axis[0][0] = 1; + tr.ori.axis[1][1] = 1; + tr.ori.axis[2][2] = 1; + VectorCopy (tr.viewParms.or.origin, tr.ori.viewOrigin); // transform by the camera placement VectorCopy( tr.viewParms.or.origin, origin ); @@ -392,9 +392,9 @@ void R_RotateForViewer (void) // convert from our coordinate system (looking down X) // to OpenGL's coordinate system (looking down -Z) - myGlMultMatrix( viewerMatrix, s_flipMatrix, tr.or.modelMatrix ); + myGlMultMatrix( viewerMatrix, s_flipMatrix, tr.ori.modelMatrix ); - tr.viewParms.world = tr.or; + tr.viewParms.world = tr.ori; } @@ -657,15 +657,15 @@ qboolean R_GetPortalOrientations( drawSurf_t *drawSurf, int entityNum, tr.currentEntity = &tr.refdef.entities[entityNum]; // get the orientation of the entity - R_RotateForEntity( tr.currentEntity, &tr.viewParms, &tr.or ); + R_RotateForEntity( tr.currentEntity, &tr.viewParms, &tr.ori ); // rotate the plane, but keep the non-rotated version for matching // against the portalSurface entities R_LocalNormalToWorld( originalPlane.normal, plane.normal ); - plane.dist = originalPlane.dist + DotProduct( plane.normal, tr.or.origin ); + plane.dist = originalPlane.dist + DotProduct( plane.normal, tr.ori.origin ); // translate the original plane - originalPlane.dist = originalPlane.dist + DotProduct( originalPlane.normal, tr.or.origin ); + originalPlane.dist = originalPlane.dist + DotProduct( originalPlane.normal, tr.ori.origin ); } else { plane = originalPlane; } @@ -775,15 +775,15 @@ static qboolean IsMirror( const drawSurf_t *drawSurf, int entityNum ) tr.currentEntity = &tr.refdef.entities[entityNum]; // get the orientation of the entity - R_RotateForEntity( tr.currentEntity, &tr.viewParms, &tr.or ); + R_RotateForEntity( tr.currentEntity, &tr.viewParms, &tr.ori ); // rotate the plane, but keep the non-rotated version for matching // against the portalSurface entities R_LocalNormalToWorld( originalPlane.normal, plane.normal ); - plane.dist = originalPlane.dist + DotProduct( plane.normal, tr.or.origin ); + plane.dist = originalPlane.dist + DotProduct( plane.normal, tr.ori.origin ); // translate the original plane - originalPlane.dist = originalPlane.dist + DotProduct( originalPlane.normal, tr.or.origin ); + originalPlane.dist = originalPlane.dist + DotProduct( originalPlane.normal, tr.ori.origin ); } else { @@ -852,7 +852,7 @@ static qboolean SurfIsOffscreen( const drawSurf_t *drawSurf, vec4_t clipDest[128 int j; unsigned int pointFlags = 0; - R_TransformModelToClip( tess.xyz[i], tr.or.modelMatrix, tr.viewParms.projectionMatrix, eye, clip ); + R_TransformModelToClip( tess.xyz[i], tr.ori.modelMatrix, tr.viewParms.projectionMatrix, eye, clip ); for ( j = 0; j < 3; j++ ) { @@ -1358,8 +1358,8 @@ void R_AddEntitySurfaces (void) { break; case RT_MODEL: - // we must set up parts of tr.or for model culling - R_RotateForEntity( ent, &tr.viewParms, &tr.or ); + // we must set up parts of tr.ori for model culling + R_RotateForEntity( ent, &tr.viewParms, &tr.ori ); tr.currentModel = R_GetModelByHandle( ent->e.hModel ); if (!tr.currentModel) { diff --git a/CODE-mp/renderer/tr_model.cpp b/CODE-mp/renderer/tr_model.cpp index 0e43d20..081547b 100644 --- a/CODE-mp/renderer/tr_model.cpp +++ b/CODE-mp/renderer/tr_model.cpp @@ -90,25 +90,25 @@ void RE_RegisterModels_StoreShaderRequest(const char *psModelFileName, const cha static const byte FakeGLAFile[] = { -0x32, 0x4C, 0x47, 0x41, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x6F, 0x64, 0x65, 0x6C, 0x73, 0x2F, 0x63, -0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x2F, 0x62, 0x6F, 0x6C, 0x74, 0x5F, 0x6F, -0x6E, 0x73, 0x2F, 0x67, 0x32, 0x68, 0x65, 0x6C, 0x6D, 0x65, 0x74, 0x5F, 0x68, 0x65, 0x6C, 0x69, +0x32, 0x4C, 0x47, 0x41, 0x06, 0x00, 0x00, 0x00, 0x2A, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x01, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, -0x30, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x47, 0x65, 0x6E, 0x65, 0x72, 0x61, 0x74, 0x65, -0x64, 0x20, 0x62, 0x79, 0x20, 0x4D, 0x44, 0x33, 0x56, 0x69, 0x65, 0x77, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, +0x26, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x4D, 0x6F, 0x64, 0x56, 0x69, 0x65, 0x77, 0x20, +0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, +0x00, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, +0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, +0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0xFE, 0xFF, -0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0xFE, 0xFF, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0xFE, 0xFF +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xBF, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, +0x00, 0x80, 0x00, 0x80, 0x00, 0x80 }; @@ -676,7 +676,7 @@ qboolean ServerLoadMDXA( model_t *mod, void *buffer, const char *mod_name, qbool size = LittleLong(size); } - if (version != MDXA_VERSION && version != MDXA_VERSION_QUAT) { + if (version != MDXA_VERSION) { return qfalse; } @@ -874,7 +874,7 @@ qboolean ServerLoadMDXM( model_t *mod, void *buffer, const char *mod_name, qbool LL(surf->ofsHeader); LL(surf->numBoneReferences); LL(surf->ofsBoneReferences); - LL(surf->maxVertBoneWeights); +// LL(surf->maxVertBoneWeights); triCount += surf->numTriangles; diff --git a/CODE-mp/renderer/tr_shade.cpp b/CODE-mp/renderer/tr_shade.cpp index 4076635..a632cdd 100644 --- a/CODE-mp/renderer/tr_shade.cpp +++ b/CODE-mp/renderer/tr_shade.cpp @@ -11,6 +11,9 @@ This file deals with applying shaders to surface data in the tess struct. */ +shaderCommands_t tess; +static qboolean setArraysOnce; + color4ub_t styleColors[MAX_LIGHT_STYLES]; /* @@ -190,9 +193,6 @@ SURFACE SHADERS ============================================================= */ -shaderCommands_t tess; -static qboolean setArraysOnce; - /* ================= R_BindAnimatedImage diff --git a/CODE-mp/renderer/tr_shader.cpp b/CODE-mp/renderer/tr_shader.cpp index c861ba9..a7ae72f 100644 --- a/CODE-mp/renderer/tr_shader.cpp +++ b/CODE-mp/renderer/tr_shader.cpp @@ -3062,7 +3062,8 @@ static shader_t *FinishShader( void ) { // if we are in r_vertexLight mode, never use a lightmap texture // if ( stage > 1 && (r_vertexLight->integer && !r_uiFullScreen->integer) ) { - stage = VertexLightingCollapse(); + //stage = VertexLightingCollapse(); + //rww - since this does bad things, I am commenting it out for now. If you want to attempt a fix, feel free. hasLightmapStage = qfalse; } diff --git a/CODE-mp/renderer/tr_world.cpp b/CODE-mp/renderer/tr_world.cpp index 56693be..6e62c54 100644 --- a/CODE-mp/renderer/tr_world.cpp +++ b/CODE-mp/renderer/tr_world.cpp @@ -120,7 +120,7 @@ static qboolean R_CullSurface( surfaceType_t *surface, shader_t *shader ) { } sface = ( srfSurfaceFace_t * ) surface; - d = DotProduct (tr.or.viewOrigin, sface->plane.normal); + d = DotProduct (tr.ori.viewOrigin, sface->plane.normal); // don't cull exactly on the plane, because there are levels of rounding // through the BSP, ICD, and hardware that may cause pixel gaps if an diff --git a/CODE-mp/renderer/vssver.scc b/CODE-mp/renderer/vssver.scc deleted file mode 100644 index 479bcdb..0000000 Binary files a/CODE-mp/renderer/vssver.scc and /dev/null differ diff --git a/CODE-mp/server/server.h b/CODE-mp/server/server.h index c40cdee..d677b9b 100644 --- a/CODE-mp/server/server.h +++ b/CODE-mp/server/server.h @@ -1,4 +1,6 @@ +#if defined (_MSC_VER) && (_MSC_VER >= 1020) #pragma once +#endif #if !defined(SERVER_H_INC) #define SERVER_H_INC @@ -254,6 +256,7 @@ void SV_DirectConnect( netadr_t from ); void SV_AuthorizeIpPacket( netadr_t from ); +void SV_SendClientMapChange( client_t *client ); void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ); void SV_UserinfoChanged( client_t *cl ); diff --git a/CODE-mp/server/sv_ccmds.cpp b/CODE-mp/server/sv_ccmds.cpp index 9fdc48c..29faf07 100644 --- a/CODE-mp/server/sv_ccmds.cpp +++ b/CODE-mp/server/sv_ccmds.cpp @@ -1,7 +1,7 @@ #include "server.h" -#include "..\strings\str_server.h" -#include "..\qcommon\strip.h" +#include "../strings/str_server.h" +#include "../qcommon/strip.h" /* =============================================================================== diff --git a/CODE-mp/server/sv_client.cpp b/CODE-mp/server/sv_client.cpp index ee972d5..0dcbebe 100644 --- a/CODE-mp/server/sv_client.cpp +++ b/CODE-mp/server/sv_client.cpp @@ -579,6 +579,30 @@ void SV_SendClientGameState( client_t *client ) { } +void SV_SendClientMapChange( client_t *client ) +{ + msg_t msg; + byte msgBuffer[MAX_MSGLEN]; + + MSG_Init( &msg, msgBuffer, sizeof( msgBuffer ) ); + + // NOTE, MRE: all server->client messages now acknowledge + // let the client know which reliable clientCommands we have received + MSG_WriteLong( &msg, client->lastClientCommand ); + + // send any server commands waiting to be sent first. + // we have to do this cause we send the client->reliableSequence + // with a gamestate and it sets the clc.serverCommandSequence at + // the client side + SV_UpdateServerCommandsToClient( client, &msg ); + + // send the gamestate + MSG_WriteByte( &msg, svc_mapchange ); + + // deliver this to the client + SV_SendMessageToClient( &msg, client ); +} + /* ================== SV_ClientEnterWorld diff --git a/CODE-mp/server/sv_init.cpp b/CODE-mp/server/sv_init.cpp index 77b2564..1c81b61 100644 --- a/CODE-mp/server/sv_init.cpp +++ b/CODE-mp/server/sv_init.cpp @@ -8,7 +8,7 @@ Ghoul2 Insert Start #include "../renderer/tr_local.h" #endif -#include "..\qcommon\strip.h" +#include "../qcommon/strip.h" /* =============== @@ -143,7 +143,7 @@ int SV_AddConfigstring (const char *name, int start, int max) SV_SetConfigstring ((start+i), name); break; } - else if (!strcmpi(sv.configstrings[start+i], name)) + else if (!Q_stricmp(sv.configstrings[start+i], name)) { return i; } @@ -390,6 +390,25 @@ void SV_TouchCGame(void) { } } +void SV_SendMapChange(void) +{ + int i; + + if (svs.clients) + { + for (i=0 ; iinteger ; i++) + { + if (svs.clients[i].state >= CS_CONNECTED) + { + if ( svs.clients[i].netchan.remoteAddress.type != NA_BOT ) + { + SV_SendClientMapChange( &svs.clients[i] ) ; + } + } + } + } +} + void R_SVModelInit(); /* @@ -409,6 +428,8 @@ void SV_SpawnServer( char *server, qboolean killBots, ForceReload_e eForceReload char systemInfo[16384]; const char *p; + SV_SendMapChange(); + RE_RegisterMedia_LevelLoadBegin(server, eForceReload); // shut down the existing game if it is running @@ -430,6 +451,8 @@ Ghoul2 Insert Start Ghoul2 Insert End */ + SV_SendMapChange(); + // if not running a dedicated server CL_MapLoading will connect the client to the server // also print some status stuff CL_MapLoading(); @@ -458,6 +481,8 @@ Ghoul2 Insert Start R_SVModelInit(); } + SV_SendMapChange(); + // clear collision map data CM_ClearMap(); @@ -471,6 +496,8 @@ Ghoul2 Insert Start } } + SV_SendMapChange(); + // clear pak references FS_ClearPakReferences(0); @@ -516,6 +543,8 @@ Ghoul2 Insert End CM_LoadMap( va("maps/%s.bsp", server), qfalse, &checksum ); + SV_SendMapChange(); + // set serverinfo visible name Cvar_Set( "mapname", server ); diff --git a/CODE-mp/server/sv_rankings.cpp b/CODE-mp/server/sv_rankings.cpp deleted file mode 100644 index 0290069..0000000 --- a/CODE-mp/server/sv_rankings.cpp +++ /dev/null @@ -1,1516 +0,0 @@ -// sv_rankings.c -- global rankings interface - -#include "server.h" -#include "..\rankings\1.0\gr\grapi.h" -#include "..\rankings\1.0\gr\grlog.h" - -typedef struct -{ - GR_CONTEXT context; - uint64_t game_id; - uint64_t match; - uint64_t player_id; - GR_PLAYER_TOKEN token; - grank_status_t grank_status; - grank_status_t final_status; // status to set after cleanup - uint32_t grank; // global rank - char name[32]; -} ranked_player_t; - -static int s_rankings_contexts = 0; -static qboolean s_rankings_active = qfalse; -static GR_CONTEXT s_server_context = 0; -static uint64_t s_server_match = 0; -static char* s_rankings_game_key = NULL; -static uint64_t s_rankings_game_id = 0; -static ranked_player_t* s_ranked_players = NULL; -static qboolean s_server_quitting = qfalse; -static const char s_ascii_encoding[] = - "0123456789abcdef" - "ghijklmnopqrstuv" - "wxyzABCDEFGHIJKL" - "MNOPQRSTUVWXYZ[]"; - -// private functions -static void SV_RankNewGameCBF( GR_NEWGAME* gr_newgame, void* cbf_arg ); -static void SV_RankUserCBF( GR_LOGIN* gr_login, void* cbf_arg ); -static void SV_RankJoinGameCBF( GR_JOINGAME* gr_joingame, void* cbf_arg ); -static void SV_RankSendReportsCBF( GR_STATUS* gr_status, void* cbf_arg ); -static void SV_RankCleanupCBF( GR_STATUS* gr_status, void* cbf_arg ); -static void SV_RankCloseContext( ranked_player_t* ranked_player ); -static int SV_RankAsciiEncode( char* dest, const unsigned char* src, - int src_len ); -static int SV_RankAsciiDecode( unsigned char* dest, const char* src, - int src_len ); -static void SV_RankEncodeGameID( uint64_t game_id, char* result, - int len ); -static uint64_t SV_RankDecodePlayerID( const char* string ); -static void SV_RankDecodePlayerKey( const char* string, GR_PLAYER_TOKEN key ); -static char* SV_RankStatusString( GR_STATUS status ); -static void SV_RankError( const char* fmt, ... ); -static char SV_RankGameKey[64]; - -/* -================ -SV_RankBegin -================ -*/ -void SV_RankBegin( char *gamekey ) -{ - GR_INIT init; - GR_STATUS status; - - assert( s_rankings_contexts == 0 ); - assert( !s_rankings_active ); - assert( s_ranked_players == NULL ); - - if( sv_enableRankings->integer == 0 || Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER ) - { - s_rankings_active = qfalse; - if( sv_rankingsActive->integer == 1 ) - { - Cvar_Set( "sv_rankingsActive", "0" ); - } - return; - } - - // only allow official game key on pure servers - if( strcmp(gamekey, GR_GAMEKEY) == 0 ) - { -/* - if( Cvar_VariableValue("sv_pure") != 1 ) - { - Cvar_Set( "sv_enableRankings", "0" ); - return; - } -*/ - - // substitute game-specific game key - switch( (int)Cvar_VariableValue("g_gametype") ) - { - case GT_FFA: - gamekey = "Q3 Free For All"; - break; - case GT_TOURNAMENT: - gamekey = "Q3 Tournament"; - break; - case GT_TEAM: - gamekey = "Q3 Team Deathmatch"; - break; - case GT_CTF: - gamekey = "Q3 Capture the Flag"; - break; - case GT_1FCTF: - gamekey = "Q3 One Flag CTF"; - break; - case GT_OBELISK: - gamekey = "Q3 Overload"; - break; - case GT_HARVESTER: - gamekey = "Q3 Harvester"; - break; - default: - break; - } - } - s_rankings_game_key = gamekey; - - // initialize rankings - GRankLogLevel( GRLOG_OFF ); - memset(SV_RankGameKey,0,sizeof(SV_RankGameKey)); - strncpy(SV_RankGameKey,gamekey,sizeof(SV_RankGameKey)-1); - init = GRankInit( 1, SV_RankGameKey, GR_OPT_POLL, GR_OPT_END ); - s_server_context = init.context; - s_rankings_contexts++; - Com_DPrintf( "SV_RankBegin(); GR_GAMEKEY is %s\n", gamekey ); - Com_DPrintf( "SV_RankBegin(); s_rankings_contexts=%d\n",s_rankings_contexts ); - Com_DPrintf( "SV_RankBegin(); s_server_context=%d\n",init.context ); - - // new game - if(!strlen(Cvar_VariableString( "sv_leagueName" ))) - { - status = GRankNewGameAsync - ( - s_server_context, - SV_RankNewGameCBF, - NULL, - GR_OPT_LEAGUENAME, - (void*)(Cvar_VariableString( "sv_leagueName" )), - GR_OPT_END - ); - } - else - { - status = GRankNewGameAsync - ( - s_server_context, - SV_RankNewGameCBF, - NULL, - GR_OPT_END - ); - } - - if( status != GR_STATUS_PENDING ) - { - SV_RankError( "SV_RankBegin: Expected GR_STATUS_PENDING, got %s", - SV_RankStatusString( status ) ); - return; - } - - // logging - if( com_developer->value ) - { - GRankLogLevel( GRLOG_TRACE ); - } - - // allocate rankings info for each player - s_ranked_players = Z_Malloc( sv_maxclients->value * - sizeof(ranked_player_t) ); - memset( (void*)s_ranked_players, 0 ,sv_maxclients->value - * sizeof(ranked_player_t)); -} - -/* -================ -SV_RankEnd -================ -*/ -void SV_RankEnd( void ) -{ - GR_STATUS status; - int i; - - Com_DPrintf( "SV_RankEnd();\n" ); - - if( !s_rankings_active ) - { - // cleanup after error during game - if( s_ranked_players != NULL ) - { - for( i = 0; i < sv_maxclients->value; i++ ) - { - if( s_ranked_players[i].context != 0 ) - { - SV_RankCloseContext( &(s_ranked_players[i]) ); - } - } - } - if( s_server_context != 0 ) - { - SV_RankCloseContext( NULL ); - } - - return; - } - - for( i = 0; i < sv_maxclients->value; i++ ) - { - if( s_ranked_players[i].grank_status == QGR_STATUS_ACTIVE ) - { - SV_RankUserLogout( i ); - Com_DPrintf( "SV_RankEnd: SV_RankUserLogout %d\n",i ); - } - } - - assert( s_server_context != 0 ); - - // send match reports, proceed to SV_RankSendReportsCBF - status = GRankSendReportsAsync - ( - s_server_context, - 0, - SV_RankSendReportsCBF, - NULL, - GR_OPT_END - ); - - if( status != GR_STATUS_PENDING ) - { - SV_RankError( "SV_RankEnd: Expected GR_STATUS_PENDING, got %s", - SV_RankStatusString( status ) ); - } - - s_rankings_active = qfalse; - Cvar_Set( "sv_rankingsActive", "0" ); -} - -/* -================ -SV_RankPoll -================ -*/ -void SV_RankPoll( void ) -{ - GRankPoll(); -} - -/* -================ -SV_RankCheckInit -================ -*/ -qboolean SV_RankCheckInit( void ) -{ - return (s_rankings_contexts > 0); -} - -/* -================ -SV_RankActive -================ -*/ -qboolean SV_RankActive( void ) -{ - return s_rankings_active; -} - -/* -================= -SV_RankUserStatus -================= -*/ -grank_status_t SV_RankUserStatus( int index ) -{ - if( !s_rankings_active ) - { - return GR_STATUS_ERROR; - } - - assert( s_ranked_players != NULL ); - assert( index >= 0 ); - assert( index < sv_maxclients->value ); - - return s_ranked_players[index].grank_status; -} - -/* -================ -SV_RankUserGRank -================ -*/ -int SV_RankUserGrank( int index ) -{ - if( !s_rankings_active ) - { - return 0; - } - - assert( s_ranked_players != NULL ); - assert( index >= 0 ); - assert( index < sv_maxclients->value ); - - return s_ranked_players[index].grank; -} - -/* -================ -SV_RankUserReset -================ -*/ -void SV_RankUserReset( int index ) -{ - if( !s_rankings_active ) - { - return; - } - - assert( s_ranked_players != NULL ); - assert( index >= 0 ); - assert( index < sv_maxclients->value ); - - switch( s_ranked_players[index].grank_status ) - { - case QGR_STATUS_SPECTATOR: - case QGR_STATUS_NO_USER: - case QGR_STATUS_BAD_PASSWORD: - case QGR_STATUS_USER_EXISTS: - case QGR_STATUS_NO_MEMBERSHIP: - case QGR_STATUS_TIMEOUT: - case QGR_STATUS_ERROR: - s_ranked_players[index].grank_status = QGR_STATUS_NEW; - break; - default: - break; - } -} - -/* -================ -SV_RankUserSpectate -================ -*/ -void SV_RankUserSpectate( int index ) -{ - if( !s_rankings_active ) - { - return; - } - - assert( s_ranked_players != NULL ); - assert( index >= 0 ); - assert( index < sv_maxclients->value ); - - // GRANK_FIXME - check current status? - s_ranked_players[index].grank_status = QGR_STATUS_SPECTATOR; -} - -/* -================ -SV_RankUserCreate -================ -*/ -void SV_RankUserCreate( int index, char* username, char* password, - char* email ) -{ - GR_INIT init; - GR_STATUS status; - - assert( index >= 0 ); - assert( index < sv_maxclients->value ); - assert( username != NULL ); - assert( password != NULL ); - assert( email != NULL ); - assert( s_ranked_players ); - assert( s_ranked_players[index].grank_status != QGR_STATUS_ACTIVE ); - - Com_DPrintf( "SV_RankUserCreate( %d, %s, \"****\", %s );\n", index, - username, email ); - - if( !s_rankings_active ) - { - Com_DPrintf( "SV_RankUserCreate: Not ready to create\n" ); - s_ranked_players[index].grank_status = QGR_STATUS_ERROR; - return; - } - - if( s_ranked_players[index].grank_status == QGR_STATUS_ACTIVE ) - { - Com_DPrintf( "SV_RankUserCreate: Got Create from active player\n" ); - return; - } - - // get a separate context for the new user - init = GRankInit( 0, SV_RankGameKey, GR_OPT_POLL, GR_OPT_END ); - s_ranked_players[index].context = init.context; - s_rankings_contexts++; - Com_DPrintf( "SV_RankUserCreate(); s_rankings_contexts=%d\n",s_rankings_contexts ); - Com_DPrintf( "SV_RankUserCreate(); s_ranked_players[%d].context=%d\n",index,init.context ); - - // attempt to create a new account, proceed to SV_RankUserCBF - status = GRankUserCreateAsync - ( - s_ranked_players[index].context, - username, - password, - email, - SV_RankUserCBF, - (void*)&s_ranked_players[index], - GR_OPT_END - ); - - if( status == GR_STATUS_PENDING ) - { - s_ranked_players[index].grank_status = QGR_STATUS_PENDING; - s_ranked_players[index].final_status = QGR_STATUS_NEW; - } - else - { - SV_RankError( "SV_RankUserCreate: Expected GR_STATUS_PENDING, got %s", - SV_RankStatusString( status ) ); - } -} - -/* -================ -SV_RankUserLogin -================ -*/ -void SV_RankUserLogin( int index, char* username, char* password ) -{ - GR_INIT init; - GR_STATUS status; - - assert( index >= 0 ); - assert( index < sv_maxclients->value ); - assert( username != NULL ); - assert( password != NULL ); - assert( s_ranked_players ); - assert( s_ranked_players[index].grank_status != QGR_STATUS_ACTIVE ); - - Com_DPrintf( "SV_RankUserLogin( %d, %s, \"****\" );\n", index, username ); - - if( !s_rankings_active ) - { - Com_DPrintf( "SV_RankUserLogin: Not ready for login\n" ); - s_ranked_players[index].grank_status = QGR_STATUS_ERROR; - return; - } - - if( s_ranked_players[index].grank_status == QGR_STATUS_ACTIVE ) - { - Com_DPrintf( "SV_RankUserLogin: Got Login from active player\n" ); - return; - } - - // get a separate context for the new user - init = GRankInit( 0, SV_RankGameKey, GR_OPT_POLL, GR_OPT_END ); - s_ranked_players[index].context = init.context; - s_rankings_contexts++; - Com_DPrintf( "SV_RankUserLogin(); s_rankings_contexts=%d\n",s_rankings_contexts ); - Com_DPrintf( "SV_RankUserLogin(); s_ranked_players[%d].context=%d\n",index,init.context ); - - // login user, proceed to SV_RankUserCBF - status = GRankUserLoginAsync - ( - s_ranked_players[index].context, - username, - password, - SV_RankUserCBF, - (void*)&s_ranked_players[index], - GR_OPT_END - ); - - if( status == GR_STATUS_PENDING ) - { - s_ranked_players[index].grank_status = QGR_STATUS_PENDING; - s_ranked_players[index].final_status = QGR_STATUS_NEW; - } - else - { - SV_RankError( "SV_RankUserLogin: Expected GR_STATUS_PENDING, got %s", - SV_RankStatusString( status ) ); - } -} - -/* -=================== -SV_RankUserValidate -=================== -*/ -qboolean SV_RankUserValidate( int index, const char* player_id, const char* key, int token_len, int rank, char* name ) -{ - GR_INIT init; - GR_STATUS status; - qboolean rVal; - ranked_player_t* ranked_player; - int i; - - assert( s_ranked_players ); - assert( s_ranked_players[index].grank_status != QGR_STATUS_ACTIVE ); - - rVal = qfalse; - - if( !s_rankings_active ) - { - Com_DPrintf( "SV_RankUserValidate: Not ready to validate\n" ); - s_ranked_players[index].grank_status = QGR_STATUS_ERROR; - return rVal; - } - - ranked_player = &(s_ranked_players[index]); - - if ( (player_id != NULL) && (key != NULL)) - { - // the real player_id and key is set when SV_RankJoinGameCBF - // is called we do this so that SV_RankUserValidate - // can be shared by both server side login and client side login - - // for client side logined in players - // server is creating GR_OPT_PLAYERCONTEXT - init = GRankInit( 0, SV_RankGameKey, GR_OPT_POLL, GR_OPT_END ); - ranked_player->context = init.context; - s_rankings_contexts++; - Com_DPrintf( "SV_RankUserValidate(); s_rankings_contexts=%d\n",s_rankings_contexts ); - Com_DPrintf( "SV_RankUserValidate(); s_ranked_players[%d].context=%d\n",index,init.context ); - - // uudecode player id and player token - ranked_player->player_id = SV_RankDecodePlayerID(player_id); - Com_DPrintf( "SV_RankUserValidate(); ranked_player->player_id =%u\n", (uint32_t)ranked_player->player_id ); - SV_RankDecodePlayerKey(key, ranked_player->token); - - // save name and check for duplicates - Q_strncpyz( ranked_player->name, name, sizeof(ranked_player->name) ); - for( i = 0; i < sv_maxclients->value; i++ ) - { - if( (i != index) && (s_ranked_players[i].grank_status == QGR_STATUS_ACTIVE) && - (strcmp( s_ranked_players[i].name, name ) == 0) ) - { - Com_DPrintf( "SV_RankUserValidate: Duplicate login\n" ); - ranked_player->grank_status = QGR_STATUS_NO_USER; - ranked_player->final_status = QGR_STATUS_NEW; - ranked_player->grank = 0; - return qfalse; - } - } - - // then validate - status = GRankPlayerValidate( - s_server_context, - ranked_player->player_id, - ranked_player->token, - token_len, - GR_OPT_PLAYERCONTEXT, - ranked_player->context, - GR_OPT_END); - } - else - { - // make server side login (bots) happy - status = GR_STATUS_OK; - } - - if (status == GR_STATUS_OK) - { - ranked_player->grank_status = QGR_STATUS_ACTIVE; - ranked_player->final_status = QGR_STATUS_NEW; - ranked_player->grank = rank; - rVal = qtrue; - } - else if (status == GR_STATUS_INVALIDUSER) - { - ranked_player->grank_status = QGR_STATUS_INVALIDUSER; - ranked_player->final_status = QGR_STATUS_NEW; - ranked_player->grank = 0; - rVal = qfalse; - } - else - { - SV_RankError( "SV_RankUserValidate: Unexpected status %s", - SV_RankStatusString( status ) ); - s_ranked_players[index].grank_status = QGR_STATUS_ERROR; - ranked_player->grank = 0; - } - - return rVal; -} - -/* -================ -SV_RankUserLogout -================ -*/ -void SV_RankUserLogout( int index ) -{ - GR_STATUS status; - GR_STATUS cleanup_status; - - if( !s_rankings_active ) - { - return; - } - - assert( index >= 0 ); - assert( index < sv_maxclients->value ); - assert( s_ranked_players ); - - if( s_ranked_players[index].context == 0 ) { - return; - } - - Com_DPrintf( "SV_RankUserLogout( %d );\n", index ); - - // masqueraded player may not be active yet, if they fail validation, - // but still they have a context needs to be cleaned - // what matters is the s_ranked_players[index].context - - // send reports, proceed to SV_RankSendReportsCBF - status = GRankSendReportsAsync - ( - s_ranked_players[index].context, - 0, - SV_RankSendReportsCBF, - (void*)&s_ranked_players[index], - GR_OPT_END - ); - - if( status == GR_STATUS_PENDING ) - { - s_ranked_players[index].grank_status = QGR_STATUS_PENDING; - s_ranked_players[index].final_status = QGR_STATUS_NEW; - } - else - { - SV_RankError( "SV_RankUserLogout: Expected GR_STATUS_PENDING, got %s", - SV_RankStatusString( status ) ); - - cleanup_status = GRankCleanupAsync - ( - s_ranked_players[index].context, - 0, - SV_RankCleanupCBF, - (void*)&s_ranked_players[index], - GR_OPT_END - ); - - if( cleanup_status != GR_STATUS_PENDING ) - { - SV_RankError( "SV_RankUserLogout: Expected " - "GR_STATUS_PENDING from GRankCleanupAsync, got %s", - SV_RankStatusString( cleanup_status ) ); - SV_RankCloseContext( &(s_ranked_players[index]) ); - } - } -} - -/* -================ -SV_RankReportInt -================ -*/ -void SV_RankReportInt( int index1, int index2, int key, int value, - qboolean accum ) -{ - GR_STATUS status; - GR_CONTEXT context; - uint64_t match; - uint64_t user1; - uint64_t user2; - int opt_accum; - - if( !s_rankings_active ) - { - return; - } - - assert( index1 >= -1 ); - assert( index1 < sv_maxclients->value ); - assert( index2 >= -1 ); - assert( index2 < sv_maxclients->value ); - assert( s_ranked_players ); - -// Com_DPrintf( "SV_RankReportInt( %d, %d, %d, %d, %d );\n", index1, index2, -// key, value, accum ); - - // get context, match, and player_id for player index1 - if( index1 == -1 ) - { - context = s_server_context; - match = s_server_match; - user1 = 0; - } - else - { - if( s_ranked_players[index1].grank_status != QGR_STATUS_ACTIVE ) - { - Com_DPrintf( "SV_RankReportInt: Expecting QGR_STATUS_ACTIVE" - " Got Unexpected status %d for player %d\n", - s_ranked_players[index1].grank_status, index1 ); - return; - } - - context = s_ranked_players[index1].context; - match = s_ranked_players[index1].match; - user1 = s_ranked_players[index1].player_id; - } - - // get player_id for player index2 - if( index2 == -1 ) - { - user2 = 0; - } - else - { - if( s_ranked_players[index2].grank_status != QGR_STATUS_ACTIVE ) - { - Com_DPrintf( "SV_RankReportInt: Expecting QGR_STATUS_ACTIVE" - " Got Unexpected status %d for player %d\n", - s_ranked_players[index2].grank_status, index2 ); - return; - } - - user2 = s_ranked_players[index2].player_id; - } - - opt_accum = accum ? GR_OPT_ACCUM : GR_OPT_END; - - status = GRankReportInt - ( - context, - match, - user1, - user2, - key, - value, - opt_accum, - GR_OPT_END - ); - - if( status != GR_STATUS_OK ) - { - SV_RankError( "SV_RankReportInt: Unexpected status %s", - SV_RankStatusString( status ) ); - } - - if( user2 != 0 ) - { - context = s_ranked_players[index2].context; - match = s_ranked_players[index2].match; - - status = GRankReportInt - ( - context, - match, - user1, - user2, - key, - value, - opt_accum, - GR_OPT_END - ); - - if( status != GR_STATUS_OK ) - { - SV_RankError( "SV_RankReportInt: Unexpected status %s", - SV_RankStatusString( status ) ); - } - } -} - -/* -================ -SV_RankReportStr -================ -*/ -void SV_RankReportStr( int index1, int index2, int key, char* value ) -{ - GR_STATUS status; - GR_CONTEXT context; - uint64_t match; - uint64_t user1; - uint64_t user2; - - if( !s_rankings_active ) - { - return; - } - - assert( index1 >= -1 ); - assert( index1 < sv_maxclients->value ); - assert( index2 >= -1 ); - assert( index2 < sv_maxclients->value ); - assert( s_ranked_players ); - -// Com_DPrintf( "SV_RankReportStr( %d, %d, %d, \"%s\" );\n", index1, index2, -// key, value ); - - // get context, match, and player_id for player index1 - if( index1 == -1 ) - { - context = s_server_context; - match = s_server_match; - user1 = 0; - } - else - { - if( s_ranked_players[index1].grank_status != QGR_STATUS_ACTIVE ) - { - Com_DPrintf( "SV_RankReportStr: Unexpected status %d\n", - s_ranked_players[index1].grank_status ); - return; - } - - context = s_ranked_players[index1].context; - match = s_ranked_players[index1].match; - user1 = s_ranked_players[index1].player_id; - } - - // get player_id for player index2 - if( index2 == -1 ) - { - user2 = 0; - } - else - { - if( s_ranked_players[index2].grank_status != QGR_STATUS_ACTIVE ) - { - Com_DPrintf( "SV_RankReportStr: Unexpected status %d\n", - s_ranked_players[index2].grank_status ); - return; - } - - user2 = s_ranked_players[index2].player_id; - } - - status = GRankReportStr - ( - context, - match, - user1, - user2, - key, - value, - GR_OPT_END - ); - - if( status != GR_STATUS_OK ) - { - SV_RankError( "SV_RankReportStr: Unexpected status %s", - SV_RankStatusString( status ) ); - } - - if( user2 != 0 ) - { - context = s_ranked_players[index2].context; - match = s_ranked_players[index2].match; - - status = GRankReportStr - ( - context, - match, - user1, - user2, - key, - value, - GR_OPT_END - ); - - if( status != GR_STATUS_OK ) - { - SV_RankError( "SV_RankReportInt: Unexpected status %s", - SV_RankStatusString( status ) ); - } - } -} - -/* -================ -SV_RankQuit -================ -*/ -void SV_RankQuit( void ) -{ - int i; - int j = 0; - // yuck - - while( s_rankings_contexts > 1 ) - { - assert(s_ranked_players); - if( s_ranked_players != NULL ) - { - for( i = 0; i < sv_maxclients->value; i++ ) - { - // check for players that weren't yet active in SV_RankEnd - if( s_ranked_players[i].grank_status == QGR_STATUS_ACTIVE ) - { - SV_RankUserLogout( i ); - Com_DPrintf( "SV_RankQuit: SV_RankUserLogout %d\n",i ); - } - else - { - if( s_ranked_players[i].context ) - { - GR_STATUS cleanup_status; - cleanup_status = GRankCleanupAsync - ( - s_ranked_players[i].context, - 0, - SV_RankCleanupCBF, - (void*)&(s_ranked_players[i]), - GR_OPT_END - ); - - if( cleanup_status != GR_STATUS_PENDING ) - { - SV_RankError( "SV_RankQuit: Expected " - "GR_STATUS_PENDING from GRankCleanupAsync, got %s", - SV_RankStatusString( cleanup_status ) ); - } - } - } - } - } - SV_RankPoll(); - - // should've finished by now - assert( (j++) < 68 ); - } -} - -/* -============================================================================== - -Private Functions - -============================================================================== -*/ - -/* -================= -SV_RankNewGameCBF -================= -*/ -static void SV_RankNewGameCBF( GR_NEWGAME* gr_newgame, void* cbf_arg ) -{ - GR_MATCH match; - int i; - - assert( gr_newgame != NULL ); - assert( cbf_arg == NULL ); - - Com_DPrintf( "SV_RankNewGameCBF( %08X, %08X );\n", gr_newgame, cbf_arg ); - - if( gr_newgame->status == GR_STATUS_OK ) - { - char info[MAX_INFO_STRING]; - char gameid[sizeof(s_ranked_players[i].game_id) * 4 / 3 + 2]; - - // save game id - s_rankings_game_id = gr_newgame->game_id; - - // encode gameid - memset(gameid,0,sizeof(gameid)); - SV_RankEncodeGameID(s_rankings_game_id,gameid,sizeof(gameid)); - - // set CS_GRANK rankingsGameID to pass to client - memset(info,0,sizeof(info)); - Info_SetValueForKey( info, "rankingsGameKey", s_rankings_game_key ); - Info_SetValueForKey( info, "rankingsGameID", gameid ); - SV_SetConfigstring( CS_GRANK, info ); - - // initialize client status - for( i = 0; i < sv_maxclients->value; i++ ) - s_ranked_players[i].grank_status = QGR_STATUS_NEW; - - // start new match - match = GRankStartMatch( s_server_context ); - s_server_match = match.match; - - // ready to go - s_rankings_active = qtrue; - Cvar_Set( "sv_rankingsActive", "1" ); - - } - else if( gr_newgame->status == GR_STATUS_BADLEAGUE ) - { - SV_RankError( "SV_RankNewGameCBF: Invalid League name\n" ); - } - else - { - //GRank handle new game failure - // force SV_RankEnd() to run - //SV_RankEnd(); - SV_RankError( "SV_RankNewGameCBF: Unexpected status %s", - SV_RankStatusString( gr_newgame->status ) ); - } -} - -/* -================ -SV_RankUserCBF -================ -*/ -static void SV_RankUserCBF( GR_LOGIN* gr_login, void* cbf_arg ) -{ - ranked_player_t* ranked_player; - GR_STATUS join_status; - GR_STATUS cleanup_status; - - assert( gr_login != NULL ); - assert( cbf_arg != NULL ); - - Com_DPrintf( "SV_RankUserCBF( %08X, %08X );\n", gr_login, cbf_arg ); - - ranked_player = (ranked_player_t*)cbf_arg; - assert(ranked_player); - assert( ranked_player->context ); - - switch( gr_login->status ) - { - case GR_STATUS_OK: - // attempt to join the game, proceed to SV_RankJoinGameCBF - join_status = GRankJoinGameAsync - ( - ranked_player->context, - s_rankings_game_id, - SV_RankJoinGameCBF, - cbf_arg, - GR_OPT_END - ); - - if( join_status != GR_STATUS_PENDING ) - { - SV_RankError( "SV_RankUserCBF: Expected GR_STATUS_PENDING " - "from GRankJoinGameAsync, got %s", - SV_RankStatusString( join_status ) ); - } - break; - case GR_STATUS_NOUSER: - Com_DPrintf( "SV_RankUserCBF: Got status %s\n", - SV_RankStatusString( gr_login->status ) ); - ranked_player->final_status = QGR_STATUS_NO_USER; - break; - case GR_STATUS_BADPASSWORD: - Com_DPrintf( "SV_RankUserCBF: Got status %s\n", - SV_RankStatusString( gr_login->status ) ); - ranked_player->final_status = QGR_STATUS_BAD_PASSWORD; - break; - case GR_STATUS_TIMEOUT: - Com_DPrintf( "SV_RankUserCBF: Got status %s\n", - SV_RankStatusString( gr_login->status ) ); - ranked_player->final_status = QGR_STATUS_TIMEOUT; - break; - default: - Com_DPrintf( "SV_RankUserCBF: Unexpected status %s\n", - SV_RankStatusString( gr_login->status ) ); - ranked_player->final_status = QGR_STATUS_ERROR; - break; - } - - if( ranked_player->final_status != QGR_STATUS_NEW ) - { - // login or create failed, so clean up before the next attempt - cleanup_status = GRankCleanupAsync - ( - ranked_player->context, - 0, - SV_RankCleanupCBF, - (void*)ranked_player, - GR_OPT_END - ); - - if( cleanup_status != GR_STATUS_PENDING ) - { - SV_RankError( "SV_RankUserCBF: Expected GR_STATUS_PENDING " - "from GRankCleanupAsync, got %s", - SV_RankStatusString( cleanup_status ) ); - SV_RankCloseContext( ranked_player ); - } - } -} - -/* -================ -SV_RankJoinGameCBF -================ -*/ -static void SV_RankJoinGameCBF( GR_JOINGAME* gr_joingame, void* cbf_arg ) -{ - ranked_player_t* ranked_player; - GR_MATCH match; - GR_STATUS cleanup_status; - - assert( gr_joingame != NULL ); - assert( cbf_arg != NULL ); - - Com_DPrintf( "SV_RankJoinGameCBF( %08X, %08X );\n", gr_joingame, cbf_arg ); - - ranked_player = (ranked_player_t*)cbf_arg; - - assert( ranked_player ); - assert( ranked_player->context != 0 ); - - if( gr_joingame->status == GR_STATUS_OK ) - { - int i; - // save user id - ranked_player->player_id = gr_joingame->player_id; - memcpy(ranked_player->token,gr_joingame->token, - sizeof(GR_PLAYER_TOKEN)) ; - match = GRankStartMatch( ranked_player->context ); - ranked_player->match = match.match; - ranked_player->grank = gr_joingame->rank; - - // find the index and call SV_RankUserValidate - for (i=0;ivalue;i++) - if ( ranked_player == &s_ranked_players[i] ) - SV_RankUserValidate(i,NULL,NULL,0, gr_joingame->rank,ranked_player->name); - } - else - { - //GRand handle join game failure - SV_RankError( "SV_RankJoinGameCBF: Unexpected status %s", - SV_RankStatusString( gr_joingame->status ) ); - - cleanup_status = GRankCleanupAsync - ( - ranked_player->context, - 0, - SV_RankCleanupCBF, - cbf_arg, - GR_OPT_END - ); - - if( cleanup_status != GR_STATUS_PENDING ) - { - SV_RankError( "SV_RankJoinGameCBF: Expected " - "GR_STATUS_PENDING from GRankCleanupAsync, got %s", - SV_RankStatusString( cleanup_status ) ); - SV_RankCloseContext( ranked_player ); - } - } -} - -/* -================ -SV_RankSendReportsCBF -================ -*/ -static void SV_RankSendReportsCBF( GR_STATUS* status, void* cbf_arg ) -{ - ranked_player_t* ranked_player; - GR_CONTEXT context; - GR_STATUS cleanup_status; - - assert( status != NULL ); - // NULL cbf_arg means server is sending match reports - - Com_DPrintf( "SV_RankSendReportsCBF( %08X, %08X );\n", status, cbf_arg ); - - ranked_player = (ranked_player_t*)cbf_arg; - if( ranked_player == NULL ) - { - Com_DPrintf( "SV_RankSendReportsCBF: server\n" ); - context = s_server_context; - } - else - { - Com_DPrintf( "SV_RankSendReportsCBF: player\n" ); - context = ranked_player->context; - } - - //assert( context != 0 ); - if( *status != GR_STATUS_OK ) - { - SV_RankError( "SV_RankSendReportsCBF: Unexpected status %s", - SV_RankStatusString( *status ) ); - } - - if( context == 0 ) - { - Com_DPrintf( "SV_RankSendReportsCBF: WARNING: context == 0" ); - SV_RankCloseContext( ranked_player ); - } - else - { - cleanup_status = GRankCleanupAsync - ( - context, - 0, - SV_RankCleanupCBF, - cbf_arg, - GR_OPT_END - ); - - if( cleanup_status != GR_STATUS_PENDING ) - { - SV_RankError( "SV_RankSendReportsCBF: Expected " - "GR_STATUS_PENDING from GRankCleanupAsync, got %s", - SV_RankStatusString( cleanup_status ) ); - SV_RankCloseContext( ranked_player ); - } - } -} - -/* -================ -SV_RankCleanupCBF -================ -*/ -static void SV_RankCleanupCBF( GR_STATUS* status, void* cbf_arg ) -{ - ranked_player_t* ranked_player; - ranked_player = (ranked_player_t*)cbf_arg; - - assert( status != NULL ); - // NULL cbf_arg means server is cleaning up - - Com_DPrintf( "SV_RankCleanupCBF( %08X, %08X );\n", status, cbf_arg ); - - if( *status != GR_STATUS_OK ) - { - SV_RankError( "SV_RankCleanupCBF: Unexpected status %s", - SV_RankStatusString( *status ) ); - } - - SV_RankCloseContext( ranked_player ); -} - -/* -================ -SV_RankCloseContext -================ -*/ -static void SV_RankCloseContext( ranked_player_t* ranked_player ) -{ - if( ranked_player == NULL ) - { - // server cleanup - if( s_server_context == 0 ) - { - return; - } - s_server_context = 0; - s_server_match = 0; - } - else - { - // player cleanup - if( s_ranked_players == NULL ) - { - return; - } - if( ranked_player->context == 0 ) - { - return; - } - ranked_player->context = 0; - ranked_player->match = 0; - ranked_player->player_id = 0; - memset( ranked_player->token, 0, sizeof(GR_PLAYER_TOKEN) ); - ranked_player->grank_status = ranked_player->final_status; - ranked_player->final_status = QGR_STATUS_NEW; - ranked_player->name[0] = '\0'; - } - - assert( s_rankings_contexts > 0 ); - s_rankings_contexts--; - Com_DPrintf( "SV_RankCloseContext: s_rankings_contexts = %d\n", - s_rankings_contexts ); - - if( s_rankings_contexts == 0 ) - { - GRankLogLevel( GRLOG_OFF ); - - if( s_ranked_players != NULL ) - { - Z_Free( s_ranked_players ); - s_ranked_players = NULL; - } - - s_rankings_active = qfalse; - Cvar_Set( "sv_rankingsActive", "0" ); - } -} - -/* -================ -SV_RankAsciiEncode - -Encodes src_len bytes of binary data from the src buffer as ASCII text, -using 6 bits per character. The result string is null-terminated and -stored in the dest buffer. - -The dest buffer must be at least (src_len * 4) / 3 + 2 bytes in length. - -Returns the length of the result string, not including the null. -================ -*/ -static int SV_RankAsciiEncode( char* dest, const unsigned char* src, - int src_len ) -{ - unsigned char bin[3]; - unsigned char txt[4]; - int dest_len = 0; - int i; - int j; - int num_chars; - - assert( dest != NULL ); - assert( src != NULL ); - - for( i = 0; i < src_len; i += 3 ) - { - // read three bytes of input - for( j = 0; j < 3; j++ ) - { - bin[j] = (i + j < src_len) ? src[i + j] : 0; - } - - // get four 6-bit values from three bytes - txt[0] = bin[0] >> 2; - txt[1] = ((bin[0] << 4) | (bin[1] >> 4)) & 63; - txt[2] = ((bin[1] << 2) | (bin[2] >> 6)) & 63; - txt[3] = bin[2] & 63; - - // store ASCII encoding of 6-bit values - num_chars = (i + 2 < src_len) ? 4 : ((src_len - i) * 4) / 3 + 1; - for( j = 0; j < num_chars; j++ ) - { - dest[dest_len++] = s_ascii_encoding[txt[j]]; - } - } - - dest[dest_len] = '\0'; - - return dest_len; -} - -/* -================ -SV_RankAsciiDecode - -Decodes src_len characters of ASCII text from the src buffer, stores -the binary result in the dest buffer. - -The dest buffer must be at least (src_len * 3) / 4 bytes in length. - -Returns the length of the binary result, or zero for invalid input. -================ -*/ -static int SV_RankAsciiDecode( unsigned char* dest, const char* src, - int src_len ) -{ - static unsigned char s_inverse_encoding[256]; - static char s_init = 0; - - unsigned char bin[3]; - unsigned char txt[4]; - int dest_len = 0; - int i; - int j; - int num_bytes; - - assert( dest != NULL ); - assert( src != NULL ); - - if( !s_init ) - { - // initialize lookup table for decoding - memset( s_inverse_encoding, 255, sizeof(s_inverse_encoding) ); - for( i = 0; i < 64; i++ ) - { - s_inverse_encoding[s_ascii_encoding[i]] = i; - } - s_init = 1; - } - - for( i = 0; i < src_len; i += 4 ) - { - // read four characters of input, decode them to 6-bit values - for( j = 0; j < 4; j++ ) - { - txt[j] = (i + j < src_len) ? s_inverse_encoding[src[i + j]] : 0; - if (txt[j] == 255) - { - return 0; // invalid input character - } - } - - // get three bytes from four 6-bit values - bin[0] = (txt[0] << 2) | (txt[1] >> 4); - bin[1] = (txt[1] << 4) | (txt[2] >> 2); - bin[2] = (txt[2] << 6) | txt[3]; - - // store binary data - num_bytes = (i + 3 < src_len) ? 3 : ((src_len - i) * 3) / 4; - for( j = 0; j < num_bytes; j++ ) - { - dest[dest_len++] = bin[j]; - } - } - - return dest_len; -} - -/* -================ -SV_RankEncodeGameID -================ -*/ -static void SV_RankEncodeGameID( uint64_t game_id, char* result, - int len ) -{ - assert( result != NULL ); - - if( len < ( ( sizeof(game_id) * 4) / 3 + 2) ) - { - Com_DPrintf( "SV_RankEncodeGameID: result buffer too small\n" ); - result[0] = '\0'; - } - else - { - qint64 gameid = LittleLong64(*(qint64*)&game_id); - SV_RankAsciiEncode( result, (unsigned char*)&gameid, - sizeof(qint64) ); - } -} - -/* -================ -SV_RankDecodePlayerID -================ -*/ -static uint64_t SV_RankDecodePlayerID( const char* string ) -{ - unsigned char buffer[9]; - int len; - qint64 player_id; - - assert( string != NULL ); - - len = strlen (string) ; - Com_DPrintf( "SV_RankDecodePlayerID: string length %d\n",len ); - SV_RankAsciiDecode( buffer, string, len ); - player_id = LittleLong64(*(qint64*)buffer); - return *(uint64_t*)&player_id; -} - -/* -================ -SV_RankDecodePlayerKey -================ -*/ -static void SV_RankDecodePlayerKey( const char* string, GR_PLAYER_TOKEN key ) -{ - unsigned char buffer[1400]; - int len; - assert( string != NULL ); - - len = strlen (string) ; - Com_DPrintf( "SV_RankDecodePlayerKey: string length %d\n",len ); - - memset(key,0,sizeof(GR_PLAYER_TOKEN)); - memset(buffer,0,sizeof(buffer)); - memcpy( key, buffer, SV_RankAsciiDecode( buffer, string, len ) ); -} - -/* -================ -SV_RankStatusString -================ -*/ -static char* SV_RankStatusString( GR_STATUS status ) -{ - switch( status ) - { - case GR_STATUS_OK: return "GR_STATUS_OK"; - case GR_STATUS_ERROR: return "GR_STATUS_ERROR"; - case GR_STATUS_BADPARAMS: return "GR_STATUS_BADPARAMS"; - case GR_STATUS_NETWORK: return "GR_STATUS_NETWORK"; - case GR_STATUS_NOUSER: return "GR_STATUS_NOUSER"; - case GR_STATUS_BADPASSWORD: return "GR_STATUS_BADPASSWORD"; - case GR_STATUS_BADGAME: return "GR_STATUS_BADGAME"; - case GR_STATUS_PENDING: return "GR_STATUS_PENDING"; - case GR_STATUS_BADDOMAIN: return "GR_STATUS_BADDOMAIN"; - case GR_STATUS_DOMAINLOCK: return "GR_STATUS_DOMAINLOCK"; - case GR_STATUS_TIMEOUT: return "GR_STATUS_TIMEOUT"; - case GR_STATUS_INVALIDUSER: return "GR_STATUS_INVALIDUSER"; - case GR_STATUS_INVALIDCONTEXT: return "GR_STATUS_INVALIDCONTEXT"; - default: return "(UNKNOWN)"; - } -} - -/* -================ -SV_RankError -================ -*/ -static void SV_RankError( const char* fmt, ... ) -{ - va_list arg_ptr; - char text[1024]; - - va_start( arg_ptr, fmt ); - vsprintf( text, fmt, arg_ptr ); - va_end( arg_ptr ); - - Com_DPrintf( "****************************************\n" ); - Com_DPrintf( "SV_RankError: %s\n", text ); - Com_DPrintf( "****************************************\n" ); - - s_rankings_active = qfalse; - Cvar_Set( "sv_rankingsActive", "0" ); - // FIXME - attempt clean shutdown? -} - diff --git a/CODE-mp/server/vssver.scc b/CODE-mp/server/vssver.scc deleted file mode 100644 index 40f384d..0000000 Binary files a/CODE-mp/server/vssver.scc and /dev/null differ diff --git a/CODE-mp/strings/vssver.scc b/CODE-mp/strings/vssver.scc deleted file mode 100644 index 6836433..0000000 Binary files a/CODE-mp/strings/vssver.scc and /dev/null differ diff --git a/CODE-mp/tosend.bat b/CODE-mp/tosend.bat deleted file mode 100644 index ddd325a..0000000 --- a/CODE-mp/tosend.bat +++ /dev/null @@ -1,5 +0,0 @@ -xcopy/y final\*.exe c:\send\game -xcopy/y final\*.dll c:\send\game -xcopy/y base\vm\cgame.* w:\game\base\vm -xcopy/y base\vm\jk2mpgame.* w:\game\base\vm -xcopy/y base\vm\ui.* w:\game\base\vm diff --git a/CODE-mp/ui/mssccprj.scc b/CODE-mp/ui/mssccprj.scc deleted file mode 100644 index 3ad6dae..0000000 --- a/CODE-mp/ui/mssccprj.scc +++ /dev/null @@ -1,5 +0,0 @@ -SCC = This is a Source Code Control file - -[ui.dsp] -SCC_Aux_Path = "\\ravend\vss_projects\jk2sof2MP" -SCC_Project_Name = "$/General/code/ui", ZAAAAAAA diff --git a/CODE-mp/ui/ui.bat b/CODE-mp/ui/ui.bat deleted file mode 100644 index cb45913..0000000 --- a/CODE-mp/ui/ui.bat +++ /dev/null @@ -1,43 +0,0 @@ -del /q vm -mkdir vm -cd vm - -set cc=lcc -DQ3_VM -S -Wf-target=bytecode -Wf-g -I..\..\cgame -I..\..\game -I..\..\ui %1 - -%cc% ../ui_main.c -@if errorlevel 1 goto quit -%cc% ../../game/bg_misc.c -@if errorlevel 1 goto quit -%cc% ../../game/bg_weapons.c -@if errorlevel 1 goto quit -%cc% ../../game/bg_lib.c -@if errorlevel 1 goto quit -%cc% ../../game/q_math.c -@if errorlevel 1 goto quit -%cc% ../../game/q_shared.c -@if errorlevel 1 goto quit -%cc% ../ui_atoms.c -@if errorlevel 1 goto quit -%cc% ../ui_force.c -@if errorlevel 1 goto quit -%cc% ../ui_players.c -@if errorlevel 1 goto quit -%cc% ../ui_util.c -@if errorlevel 1 goto quit -%cc% ../ui_shared.c -@if errorlevel 1 goto quit -%cc% ../ui_gameinfo.c -@if errorlevel 1 goto quit - -sysmaker ../ui_public.h ../ui_syscalls.c ../ui_syscalls.asm -@if errorlevel 1 goto quit - -q3asm -f ../ui -@if errorlevel 1 goto quit - -mkdir "..\..\base\vm" -copy *.map "..\..\base\vm" -copy *.qvm "..\..\base\vm" - -:quit -cd .. diff --git a/CODE-mp/ui/ui.def b/CODE-mp/ui/ui.def deleted file mode 100644 index 01861ba..0000000 --- a/CODE-mp/ui/ui.def +++ /dev/null @@ -1,3 +0,0 @@ -EXPORTS - vmMain - dllEntry diff --git a/CODE-mp/ui/ui.q3asm b/CODE-mp/ui/ui.q3asm deleted file mode 100644 index 81ffc5f..0000000 --- a/CODE-mp/ui/ui.q3asm +++ /dev/null @@ -1,14 +0,0 @@ --o "ui" -ui_main -..\ui_syscalls -ui_atoms -ui_force -ui_players -ui_util -ui_shared -ui_gameinfo -bg_misc -bg_weapons -bg_lib -q_math -q_shared diff --git a/CODE-mp/ui/ui_force.c b/CODE-mp/ui/ui_force.c index 75fb52d..9fa9a2d 100644 --- a/CODE-mp/ui/ui_force.c +++ b/CODE-mp/ui/ui_force.c @@ -176,6 +176,49 @@ void UI_UpdateClientForcePowers() gTouchedForce = qfalse; } +void UI_SaveForceTemplate() +{ + char *selectedName = UI_Cvar_VariableString("ui_SaveFCF"); + char fcfString[512]; + char forceStringValue[4]; + fileHandle_t f; + int strPlace = 0; + int forcePlace = 0; + + if (!selectedName || !selectedName[0]) + { + Com_Printf("You did not provide a name for the template.\n"); + return; + } + + trap_FS_FOpenFile(va("forcecfg/%s.fcf", selectedName), &f, FS_WRITE); + + if (!f) + { + Com_Printf("There was an error writing the template file (read-only?).\n"); + return; + } + + Com_sprintf(fcfString, sizeof(fcfString), "%i-%i-", uiForceRank, uiForceSide); + strPlace = strlen(fcfString); + + while (forcePlace < NUM_FORCE_POWERS) + { + Com_sprintf(forceStringValue, sizeof(forceStringValue), "%i", uiForcePowersRank[forcePlace]); + //Just use the force digit even if multiple digits. Shouldn't be longer than 1. + fcfString[strPlace] = forceStringValue[0]; + strPlace++; + forcePlace++; + } + fcfString[strPlace] = '\n'; + fcfString[strPlace+1] = 0; + + trap_FS_Write(fcfString, strlen(fcfString), f); + trap_FS_FCloseFile(f); + + Com_Printf("Template saved as \"%s\".\n", selectedName); +} + // void UpdateForceUsed() diff --git a/CODE-mp/ui/ui_force.h b/CODE-mp/ui/ui_force.h index c4d933e..ed72e58 100644 --- a/CODE-mp/ui/ui_force.h +++ b/CODE-mp/ui/ui_force.h @@ -19,6 +19,7 @@ void UI_InitForceShaders(void); void UI_DrawTotalForceStars(rectDef_t *rect, float scale, vec4_t color, int textStyle); void UI_DrawForceStars(rectDef_t *rect, float scale, vec4_t color, int textStyle, int findex, int val, int min, int max) ; void UI_UpdateClientForcePowers(); +void UI_SaveForceTemplate(); void UI_UpdateForcePowers(); qboolean UI_SkinColor_HandleKey(int flags, float *special, int key, int num, int min, int max, int type); qboolean UI_ForceSide_HandleKey(int flags, float *special, int key, int num, int min, int max, int type); diff --git a/CODE-mp/ui/ui_local.h b/CODE-mp/ui/ui_local.h index 65cde93..c1fc578 100644 --- a/CODE-mp/ui/ui_local.h +++ b/CODE-mp/ui/ui_local.h @@ -615,6 +615,8 @@ typedef struct { #define MAX_MOVIES 256 #define MAX_PLAYERMODELS 256 +#define MAX_SCROLLTEXT_SIZE 4096 +#define MAX_SCROLLTEXT_LINES 64 typedef struct { const char *name; @@ -793,6 +795,10 @@ typedef struct { int movieIndex; int previewMovie; + char scrolltext[MAX_SCROLLTEXT_SIZE]; + const char *scrolltextLine[MAX_SCROLLTEXT_LINES]; + int scrolltextLineCount; + serverStatus_t serverStatus; // for the showing the status of a server diff --git a/CODE-mp/ui/ui_main.c b/CODE-mp/ui/ui_main.c index 6e2a3ed..60665fa 100644 --- a/CODE-mp/ui/ui_main.c +++ b/CODE-mp/ui/ui_main.c @@ -21,14 +21,6 @@ void UpdateForceUsed(); char holdSPString[1024]={0}; -enum { - FONT_NONE, - FONT_SMALL=1, - FONT_MEDIUM, - FONT_LARGE -}; - - uiInfo_t uiInfo; static const char *MonthAbbrev[] = { @@ -851,7 +843,7 @@ void UI_LoadMenus(const char *menuFile, qboolean reset) { handle = trap_PC_LoadSource( menuFile ); if (!handle) { - trap_Error( va( S_COLOR_YELLOW "menu file not found: %s, using default\n", menuFile ) ); + Com_Printf( S_COLOR_YELLOW "menu file not found: %s, using default\n", menuFile ); handle = trap_PC_LoadSource( "ui/jk2mpmenus.txt" ); if (!handle) { trap_Error( va( S_COLOR_RED "default menu file not found: ui/menus.txt, unable to continue!\n", menuFile ) ); @@ -3549,12 +3541,15 @@ static void UI_Update(const char *name) { int gUISelectedMap = 0; -static void UI_RunMenuScript(char **args) { +static void UI_RunMenuScript(char **args) +{ const char *name, *name2; char buff[1024]; - if (String_Parse(args, &name)) { - if (Q_stricmp(name, "StartServer") == 0) { + if (String_Parse(args, &name)) + { + if (Q_stricmp(name, "StartServer") == 0) + { int i, added = 0; float skill; trap_Cvar_Set("cg_thirdPerson", "0"); @@ -3567,11 +3562,13 @@ static void UI_RunMenuScript(char **args) { trap_Cmd_ExecuteText( EXEC_APPEND, va( "wait ; wait ; map %s\n", uiInfo.mapList[ui_currentNetMap.integer].mapLoadName ) ); skill = trap_Cvar_VariableValue( "g_spSkill" ); - for (i = 0; i < PLAYERS_PER_TEAM; i++) { + for (i = 0; i < PLAYERS_PER_TEAM; i++) + { int bot = trap_Cvar_VariableValue( va("ui_blueteam%i", i+1)); int maxcl = trap_Cvar_VariableValue( "sv_maxClients" ); - if (bot > 1) { + if (bot > 1) + { int numval = i+1; numval *= 2; @@ -3949,6 +3946,8 @@ static void UI_RunMenuScript(char **args) { } } else if (Q_stricmp(name, "setForce") == 0) { UI_UpdateClientForcePowers(); + } else if (Q_stricmp(name, "saveTemplate") == 0) { + UI_SaveForceTemplate(); } else if (Q_stricmp(name, "refreshForce") == 0) { UI_UpdateForcePowers(); } else if (Q_stricmp(name, "glCustom") == 0) { @@ -3998,14 +3997,17 @@ static void UI_RunMenuScript(char **args) { trap_Cvar_Set("g_weaponDisable", va("%i",weaponDisable)); } } - else if (Q_stricmp(name, "update") == 0) { - if (String_Parse(args, &name2)) { + else if (Q_stricmp(name, "update") == 0) + { + if (String_Parse(args, &name2)) + { UI_Update(name2); - } - else { - Com_Printf("unknown UI script %s\n", name); } } + else + { + Com_Printf("unknown UI script %s\n", name); + } } } @@ -4703,40 +4705,58 @@ static void UI_BuildServerStatus(qboolean force) { UI_FeederCount ================== */ -static int UI_FeederCount(float feederID) { - if (feederID == FEEDER_HEADS) { - return UI_HeadCountByTeam(); - } else if (feederID == FEEDER_Q3HEADS) { - return UI_HeadCountByColor(); - } else if (feederID == FEEDER_FORCECFG) { - return uiInfo.forceConfigCount; - } else if (feederID == FEEDER_CINEMATICS) { - return uiInfo.movieCount; - } else if (feederID == FEEDER_MAPS || feederID == FEEDER_ALLMAPS) { - return UI_MapCountByGameType(feederID == FEEDER_MAPS ? qtrue : qfalse); - } else if (feederID == FEEDER_SERVERS) { - return uiInfo.serverStatus.numDisplayServers; - } else if (feederID == FEEDER_SERVERSTATUS) { - return uiInfo.serverStatusInfo.numLines; - } else if (feederID == FEEDER_FINDPLAYER) { - return uiInfo.numFoundPlayerServers; - } else if (feederID == FEEDER_PLAYER_LIST) { - if (uiInfo.uiDC.realTime > uiInfo.playerRefresh) { - uiInfo.playerRefresh = uiInfo.uiDC.realTime + 3000; - UI_BuildPlayerList(); - } - return uiInfo.playerCount; - } else if (feederID == FEEDER_TEAM_LIST) { - if (uiInfo.uiDC.realTime > uiInfo.playerRefresh) { - uiInfo.playerRefresh = uiInfo.uiDC.realTime + 3000; - UI_BuildPlayerList(); - } - return uiInfo.myTeamCount; - } else if (feederID == FEEDER_MODS) { - return uiInfo.modCount; - } else if (feederID == FEEDER_DEMOS) { - return uiInfo.demoCount; +static int UI_FeederCount(float feederID) +{ + switch ( (int)feederID ) + { + case FEEDER_HEADS: + return UI_HeadCountByTeam(); + + case FEEDER_Q3HEADS: + return UI_HeadCountByColor(); + + case FEEDER_FORCECFG: + return uiInfo.forceConfigCount; + + case FEEDER_CINEMATICS: + return uiInfo.movieCount; + + case FEEDER_MAPS: + case FEEDER_ALLMAPS: + return UI_MapCountByGameType(feederID == FEEDER_MAPS ? qtrue : qfalse); + + case FEEDER_SERVERS: + return uiInfo.serverStatus.numDisplayServers; + + case FEEDER_SERVERSTATUS: + return uiInfo.serverStatusInfo.numLines; + + case FEEDER_FINDPLAYER: + return uiInfo.numFoundPlayerServers; + + case FEEDER_PLAYER_LIST: + if (uiInfo.uiDC.realTime > uiInfo.playerRefresh) + { + uiInfo.playerRefresh = uiInfo.uiDC.realTime + 3000; + UI_BuildPlayerList(); + } + return uiInfo.playerCount; + + case FEEDER_TEAM_LIST: + if (uiInfo.uiDC.realTime > uiInfo.playerRefresh) + { + uiInfo.playerRefresh = uiInfo.uiDC.realTime + 3000; + UI_BuildPlayerList(); + } + return uiInfo.myTeamCount; + + case FEEDER_MODS: + return uiInfo.modCount; + + case FEEDER_DEMOS: + return uiInfo.demoCount; } + return 0; } @@ -4948,7 +4968,7 @@ static const char *UI_FeederItemText(float feederID, int index, int column, qhan if (index >= 0 && index < uiInfo.demoCount) { return uiInfo.demoList[index]; } - } + } return ""; } @@ -5035,12 +5055,21 @@ static void UI_FeederSelection(float feederID, int index) { else if (feederID == FEEDER_MAPS || feederID == FEEDER_ALLMAPS) { int actual, map; + const char *checkValid = NULL; + map = (feederID == FEEDER_ALLMAPS) ? ui_currentNetMap.integer : ui_currentMap.integer; if (uiInfo.mapList[map].cinematic >= 0) { trap_CIN_StopCinematic(uiInfo.mapList[map].cinematic); uiInfo.mapList[map].cinematic = -1; } - UI_SelectedMap(index, &actual); + checkValid = UI_SelectedMap(index, &actual); + + if (!checkValid || !checkValid[0]) + { //this isn't a valid map to select, so reselect the current + index = ui_mapIndex.integer; + UI_SelectedMap(index, &actual); + } + trap_Cvar_Set("ui_mapIndex", va("%d", index)); gUISelectedMap = index; ui_mapIndex.integer = index; @@ -6361,6 +6390,8 @@ vmCvar_t ui_recordSPDemo; vmCvar_t ui_realCaptureLimit; vmCvar_t ui_realWarmUp; vmCvar_t ui_serverStatusTimeOut; +vmCvar_t s_language; +vmCvar_t k_language; // bk001129 - made static to avoid aliasing static cvarTable_t cvarTable[] = { @@ -6488,7 +6519,8 @@ static cvarTable_t cvarTable[] = { { &ui_realWarmUp, "g_warmup", "20", CVAR_ARCHIVE}, { &ui_realCaptureLimit, "capturelimit", "8", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART}, { &ui_serverStatusTimeOut, "ui_serverStatusTimeOut", "7000", CVAR_ARCHIVE}, - + { &s_language, "s_language", "english", CVAR_ARCHIVE | CVAR_NORESTART}, + { &k_language, "k_language", "", CVAR_ARCHIVE | CVAR_NORESTART}, // any default ("" or "american") is fine, only foreign strings ("deutsch" etc) make a different keyboard table get looked at }; // bk001129 - made static to avoid aliasing diff --git a/CODE-mp/ui/ui_shared.c b/CODE-mp/ui/ui_shared.c index 26a2fcb..9b085ee 100644 --- a/CODE-mp/ui/ui_shared.c +++ b/CODE-mp/ui/ui_shared.c @@ -101,13 +101,14 @@ char *types [] = { "ITEM_TYPE_EDITFIELD", "ITEM_TYPE_COMBO", "ITEM_TYPE_LISTBOX", -"ITEM_TYPE_OWNERDRAW", "ITEM_TYPE_MODEL", +"ITEM_TYPE_OWNERDRAW", "ITEM_TYPE_NUMERICFIELD", "ITEM_TYPE_SLIDER", "ITEM_TYPE_YESNO", "ITEM_TYPE_MULTI", "ITEM_TYPE_BIND", +"ITEM_TYPE_TEXTSCROLL", NULL }; @@ -534,7 +535,7 @@ qboolean PC_String_Parse(int handle, const char **out) if (token.string[0] == '@') // Is it a localized text? { char *temp; - char text[MAX_STRING_CHARS]; + char text[MAX_STRING_CHARS*4]; temp = &token.string[0]; // The +1 is to offset the @ at the beginning of the text @@ -543,14 +544,18 @@ qboolean PC_String_Parse(int handle, const char **out) if (text[0] == 0) // Couldn't find it { Com_Printf(va(S_COLOR_YELLOW "Unable to locate StripEd text '%s'\n", token.string)); + *(out) = String_Alloc( token.string ); } else { - memcpy(token.string,text,sizeof(text)); + *(out) = String_Alloc(text); } } - - *(out) = String_Alloc(token.string); + else + { + *(out) = String_Alloc(token.string); + } + return qtrue; } @@ -1527,6 +1532,231 @@ qboolean Item_SetFocus(itemDef_t *item, float x, float y) { return qtrue; } +int Item_TextScroll_MaxScroll ( itemDef_t *item ) +{ + textScrollDef_t *scrollPtr = (textScrollDef_t*)item->typeData; + + int count = scrollPtr->lineCount; + int max = count - (item->window.rect.h / scrollPtr->lineHeight) + 1; + + if (max < 0) + { + return 0; + } + + return max; +} + +int Item_TextScroll_ThumbPosition ( itemDef_t *item ) +{ + float max, pos, size; + textScrollDef_t *scrollPtr = (textScrollDef_t*)item->typeData; + + max = Item_TextScroll_MaxScroll ( item ); + size = item->window.rect.h - (SCROLLBAR_SIZE * 2) - 2; + + if (max > 0) + { + pos = (size-SCROLLBAR_SIZE) / (float) max; + } + else + { + pos = 0; + } + + pos *= scrollPtr->startPos; + + return item->window.rect.y + 1 + SCROLLBAR_SIZE + pos; +} + +int Item_TextScroll_ThumbDrawPosition ( itemDef_t *item ) +{ + int min, max; + + if (itemCapture == item) + { + min = item->window.rect.y + SCROLLBAR_SIZE + 1; + max = item->window.rect.y + item->window.rect.h - 2*SCROLLBAR_SIZE - 1; + + if (DC->cursory >= min + SCROLLBAR_SIZE/2 && DC->cursory <= max + SCROLLBAR_SIZE/2) + { + return DC->cursory - SCROLLBAR_SIZE/2; + } + + return Item_TextScroll_ThumbPosition(item); + } + + return Item_TextScroll_ThumbPosition(item); +} + +int Item_TextScroll_OverLB ( itemDef_t *item, float x, float y ) +{ + rectDef_t r; + textScrollDef_t *scrollPtr; + int thumbstart; + int count; + + scrollPtr = (textScrollDef_t*)item->typeData; + count = scrollPtr->lineCount; + + r.x = item->window.rect.x + item->window.rect.w - SCROLLBAR_SIZE; + r.y = item->window.rect.y; + r.h = r.w = SCROLLBAR_SIZE; + if (Rect_ContainsPoint(&r, x, y)) + { + return WINDOW_LB_LEFTARROW; + } + + r.y = item->window.rect.y + item->window.rect.h - SCROLLBAR_SIZE; + if (Rect_ContainsPoint(&r, x, y)) + { + return WINDOW_LB_RIGHTARROW; + } + + thumbstart = Item_TextScroll_ThumbPosition(item); + r.y = thumbstart; + if (Rect_ContainsPoint(&r, x, y)) + { + return WINDOW_LB_THUMB; + } + + r.y = item->window.rect.y + SCROLLBAR_SIZE; + r.h = thumbstart - r.y; + if (Rect_ContainsPoint(&r, x, y)) + { + return WINDOW_LB_PGUP; + } + + r.y = thumbstart + SCROLLBAR_SIZE; + r.h = item->window.rect.y + item->window.rect.h - SCROLLBAR_SIZE; + if (Rect_ContainsPoint(&r, x, y)) + { + return WINDOW_LB_PGDN; + } + + return 0; +} + +void Item_TextScroll_MouseEnter (itemDef_t *item, float x, float y) +{ + item->window.flags &= ~(WINDOW_LB_LEFTARROW | WINDOW_LB_RIGHTARROW | WINDOW_LB_THUMB | WINDOW_LB_PGUP | WINDOW_LB_PGDN); + item->window.flags |= Item_TextScroll_OverLB(item, x, y); +} + +qboolean Item_TextScroll_HandleKey ( itemDef_t *item, int key, qboolean down, qboolean force) +{ + textScrollDef_t *scrollPtr = (textScrollDef_t*)item->typeData; + int max; + int viewmax; + + if (force || (Rect_ContainsPoint(&item->window.rect, DC->cursorx, DC->cursory) && item->window.flags & WINDOW_HASFOCUS)) + { + max = Item_TextScroll_MaxScroll(item); + + viewmax = (item->window.rect.h / scrollPtr->lineHeight); + if ( key == K_UPARROW || key == K_KP_UPARROW ) + { + scrollPtr->startPos--; + if (scrollPtr->startPos < 0) + { + scrollPtr->startPos = 0; + } + return qtrue; + } + + if ( key == K_DOWNARROW || key == K_KP_DOWNARROW ) + { + scrollPtr->startPos++; + if (scrollPtr->startPos > max) + { + scrollPtr->startPos = max; + } + + return qtrue; + } + + // mouse hit + if (key == K_MOUSE1 || key == K_MOUSE2) + { + if (item->window.flags & WINDOW_LB_LEFTARROW) + { + scrollPtr->startPos--; + if (scrollPtr->startPos < 0) + { + scrollPtr->startPos = 0; + } + } + else if (item->window.flags & WINDOW_LB_RIGHTARROW) + { + // one down + scrollPtr->startPos++; + if (scrollPtr->startPos > max) + { + scrollPtr->startPos = max; + } + } + else if (item->window.flags & WINDOW_LB_PGUP) + { + // page up + scrollPtr->startPos -= viewmax; + if (scrollPtr->startPos < 0) + { + scrollPtr->startPos = 0; + } + } + else if (item->window.flags & WINDOW_LB_PGDN) + { + // page down + scrollPtr->startPos += viewmax; + if (scrollPtr->startPos > max) + { + scrollPtr->startPos = max; + } + } + else if (item->window.flags & WINDOW_LB_THUMB) + { + // Display_SetCaptureItem(item); + } + + return qtrue; + } + + if ( key == K_HOME || key == K_KP_HOME) + { + // home + scrollPtr->startPos = 0; + return qtrue; + } + if ( key == K_END || key == K_KP_END) + { + // end + scrollPtr->startPos = max; + return qtrue; + } + if (key == K_PGUP || key == K_KP_PGUP ) + { + scrollPtr->startPos -= viewmax; + if (scrollPtr->startPos < 0) + { + scrollPtr->startPos = 0; + } + + return qtrue; + } + if ( key == K_PGDN || key == K_KP_PGDN ) + { + scrollPtr->startPos += viewmax; + if (scrollPtr->startPos > max) + { + scrollPtr->startPos = max; + } + return qtrue; + } + } + + return qfalse; +} + int Item_ListBox_MaxScroll(itemDef_t *item) { listBoxDef_t *listPtr = (listBoxDef_t*)item->typeData; int count = DC->feederCount(item->special); @@ -1797,6 +2027,10 @@ void Item_MouseEnter(itemDef_t *item, float x, float y) { if (item->type == ITEM_TYPE_LISTBOX) { Item_ListBox_MouseEnter(item, x, y); } + else if ( item->type == ITEM_TYPE_TEXTSCROLL ) + { + Item_TextScroll_MouseEnter ( item, x, y ); + } } } } @@ -2292,6 +2526,79 @@ qboolean Item_TextField_HandleKey(itemDef_t *item, int key) { } +static void Scroll_TextScroll_AutoFunc (void *p) +{ + scrollInfo_t *si = (scrollInfo_t*)p; + + if (DC->realTime > si->nextScrollTime) + { + // need to scroll which is done by simulating a click to the item + // this is done a bit sideways as the autoscroll "knows" that the item is a listbox + // so it calls it directly + Item_TextScroll_HandleKey(si->item, si->scrollKey, qtrue, qfalse); + si->nextScrollTime = DC->realTime + si->adjustValue; + } + + if (DC->realTime > si->nextAdjustTime) + { + si->nextAdjustTime = DC->realTime + SCROLL_TIME_ADJUST; + if (si->adjustValue > SCROLL_TIME_FLOOR) + { + si->adjustValue -= SCROLL_TIME_ADJUSTOFFSET; + } + } +} + +static void Scroll_TextScroll_ThumbFunc(void *p) +{ + scrollInfo_t *si = (scrollInfo_t*)p; + rectDef_t r; + int pos; + int max; + + textScrollDef_t *scrollPtr = (textScrollDef_t*)si->item->typeData; + + if (DC->cursory != si->yStart) + { + r.x = si->item->window.rect.x + si->item->window.rect.w - SCROLLBAR_SIZE - 1; + r.y = si->item->window.rect.y + SCROLLBAR_SIZE + 1; + r.h = si->item->window.rect.h - (SCROLLBAR_SIZE*2) - 2; + r.w = SCROLLBAR_SIZE; + max = Item_TextScroll_MaxScroll(si->item); + // + pos = (DC->cursory - r.y - SCROLLBAR_SIZE/2) * max / (r.h - SCROLLBAR_SIZE); + if (pos < 0) + { + pos = 0; + } + else if (pos > max) + { + pos = max; + } + + scrollPtr->startPos = pos; + si->yStart = DC->cursory; + } + + if (DC->realTime > si->nextScrollTime) + { + // need to scroll which is done by simulating a click to the item + // this is done a bit sideways as the autoscroll "knows" that the item is a listbox + // so it calls it directly + Item_TextScroll_HandleKey(si->item, si->scrollKey, qtrue, qfalse); + si->nextScrollTime = DC->realTime + si->adjustValue; + } + + if (DC->realTime > si->nextAdjustTime) + { + si->nextAdjustTime = DC->realTime + SCROLL_TIME_ADJUST; + if (si->adjustValue > SCROLL_TIME_FLOOR) + { + si->adjustValue -= SCROLL_TIME_ADJUSTOFFSET; + } + } +} + static void Scroll_ListBox_AutoFunc(void *p) { scrollInfo_t *si = (scrollInfo_t*)p; if (DC->realTime > si->nextScrollTime) { @@ -2396,12 +2703,13 @@ static void Scroll_Slider_ThumbFunc(void *p) { DC->setCVar(si->item->cvar, va("%f", value)); } -void Item_StartCapture(itemDef_t *item, int key) { +void Item_StartCapture(itemDef_t *item, int key) +{ int flags; - switch (item->type) { - case ITEM_TYPE_EDITFIELD: - case ITEM_TYPE_NUMERICFIELD: - + switch (item->type) + { + case ITEM_TYPE_EDITFIELD: + case ITEM_TYPE_NUMERICFIELD: case ITEM_TYPE_LISTBOX: { flags = Item_ListBox_OverLB(item, DC->cursorx, DC->cursory); @@ -2426,6 +2734,33 @@ void Item_StartCapture(itemDef_t *item, int key) { } break; } + + case ITEM_TYPE_TEXTSCROLL: + flags = Item_TextScroll_OverLB (item, DC->cursorx, DC->cursory); + if (flags & (WINDOW_LB_LEFTARROW | WINDOW_LB_RIGHTARROW)) + { + scrollInfo.nextScrollTime = DC->realTime + SCROLL_TIME_START; + scrollInfo.nextAdjustTime = DC->realTime + SCROLL_TIME_ADJUST; + scrollInfo.adjustValue = SCROLL_TIME_START; + scrollInfo.scrollKey = key; + scrollInfo.scrollDir = (flags & WINDOW_LB_LEFTARROW) ? qtrue : qfalse; + scrollInfo.item = item; + captureData = &scrollInfo; + captureFunc = &Scroll_TextScroll_AutoFunc; + itemCapture = item; + } + else if (flags & WINDOW_LB_THUMB) + { + scrollInfo.scrollKey = key; + scrollInfo.item = item; + scrollInfo.xStart = DC->cursorx; + scrollInfo.yStart = DC->cursory; + captureData = &scrollInfo; + captureFunc = &Scroll_TextScroll_ThumbFunc; + itemCapture = item; + } + break; + case ITEM_TYPE_SLIDER: { flags = Item_Slider_OverSlider(item, DC->cursorx, DC->cursory); @@ -2536,6 +2871,9 @@ qboolean Item_HandleKey(itemDef_t *item, int key, qboolean down) { break; case ITEM_TYPE_LISTBOX: return Item_ListBox_HandleKey(item, key, down, qfalse); + break; + case ITEM_TYPE_TEXTSCROLL: + return Item_TextScroll_HandleKey(item, key, down, qfalse); break; case ITEM_TYPE_YESNO: return Item_YesNo_HandleKey(item, key); @@ -3727,8 +4065,8 @@ qboolean Item_Bind_HandleKey(itemDef_t *item, int key, qboolean down) { return qtrue; } - -void Item_Model_Paint(itemDef_t *item) { +void Item_Model_Paint(itemDef_t *item) +{ float x, y, w, h; refdef_t refdef; refEntity_t ent; @@ -3736,7 +4074,8 @@ void Item_Model_Paint(itemDef_t *item) { vec3_t angles; modelDef_t *modelPtr = (modelDef_t*)item->typeData; - if (modelPtr == NULL) { + if (modelPtr == NULL) + { return; } @@ -3749,10 +4088,10 @@ void Item_Model_Paint(itemDef_t *item) { w = item->window.rect.w-2; h = item->window.rect.h-2; - refdef.x = x; - refdef.y = y; - refdef.width = w; - refdef.height = h; + refdef.x = x * DC->xscale; + refdef.y = y * DC->yscale; + refdef.width = w * DC->xscale; + refdef.height = h * DC->yscale; DC->modelBounds( item->asset, mins, maxs ); @@ -3760,16 +4099,22 @@ void Item_Model_Paint(itemDef_t *item) { origin[1] = 0.5 * ( mins[1] + maxs[1] ); // calculate distance so the model nearly fills the box - if (qtrue) { + if (qtrue) + { float len = 0.5 * ( maxs[2] - mins[2] ); origin[0] = len / 0.268; // len / tan( fov/2 ) //origin[0] = len / tan(w/2); - } else { + } + else + { origin[0] = item->textscale; } refdef.fov_x = (modelPtr->fov_x) ? modelPtr->fov_x : w; refdef.fov_y = (modelPtr->fov_y) ? modelPtr->fov_y : h; + refdef.fov_x = 45; + refdef.fov_y = 45; + //refdef.fov_x = (int)((float)refdef.width / 640.0f * 90.0f); //xx = refdef.width / tan( refdef.fov_x / 360 * M_PI ); //refdef.fov_y = atan2( refdef.height, xx ); @@ -3788,13 +4133,18 @@ void Item_Model_Paint(itemDef_t *item) { //VectorSet( angles, 0, 0, 1 ); // use item storage to track - if (modelPtr->rotationSpeed) { - if (DC->realTime > item->window.nextTime) { +/* if (modelPtr->rotationSpeed) + { + if (DC->realTime > item->window.nextTime) + { item->window.nextTime = DC->realTime + modelPtr->rotationSpeed; modelPtr->angle = (int)(modelPtr->angle + 1) % 360; } } VectorSet( angles, 0, modelPtr->angle, 0 ); +*/ + VectorSet( angles, 0, (float)(refdef.time/20.0f), 0); + AnglesToAxis( angles, ent.axis ); ent.hModel = item->asset; @@ -3816,6 +4166,63 @@ void Item_Image_Paint(itemDef_t *item) { DC->drawHandlePic(item->window.rect.x+1, item->window.rect.y+1, item->window.rect.w-2, item->window.rect.h-2, item->asset); } +void Item_TextScroll_Paint(itemDef_t *item) +{ + float x, y, size, count, thumb; + int i; + textScrollDef_t *scrollPtr = (textScrollDef_t*)item->typeData; + + count = scrollPtr->lineCount; + + // draw scrollbar to right side of the window + x = item->window.rect.x + item->window.rect.w - SCROLLBAR_SIZE - 1; + y = item->window.rect.y + 1; + DC->drawHandlePic(x, y, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarArrowUp); + y += SCROLLBAR_SIZE - 1; + + scrollPtr->endPos = scrollPtr->startPos; + size = item->window.rect.h - (SCROLLBAR_SIZE * 2); + DC->drawHandlePic(x, y, SCROLLBAR_SIZE, size+1, DC->Assets.scrollBar); + y += size - 1; + DC->drawHandlePic(x, y, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarArrowDown); + + // thumb + thumb = Item_TextScroll_ThumbDrawPosition(item); + if (thumb > y - SCROLLBAR_SIZE - 1) + { + thumb = y - SCROLLBAR_SIZE - 1; + } + DC->drawHandlePic(x, thumb, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarThumb); + + // adjust size for item painting + size = item->window.rect.h - 2; + x = item->window.rect.x + 1; + y = item->window.rect.y + 1; + + for (i = scrollPtr->startPos; i < count; i++) + { + char *text; + + text = scrollPtr->lines[i]; + if (!text) + { + continue; + } + + DC->drawText(x + 4, y, item->textscale, item->window.foreColor, text, 0, 0, item->textStyle, item->iMenuFont); + + size -= scrollPtr->lineHeight; + if (size < scrollPtr->lineHeight) + { + scrollPtr->drawPadding = scrollPtr->lineHeight - size; + break; + } + + scrollPtr->endPos++; + y += scrollPtr->lineHeight; + } +} + void Item_ListBox_Paint(itemDef_t *item) { float x, y, size, count, i, thumb; qhandle_t image; @@ -4063,7 +4470,7 @@ void Item_Paint(itemDef_t *item) { if (item->descText) { - textWidth = DC->textWidth(item->descText,(float) 0.7f, 0); + textWidth = DC->textWidth(item->descText,(float) 0.7f, FONT_MEDIUM); if (parent->descAlignment == ITEM_ALIGN_RIGHT) { @@ -4079,7 +4486,13 @@ void Item_Paint(itemDef_t *item) } Item_TextColor(item, &color); - DC->drawText(xPos, parent->descY, (float) 0.7f, parent->descColor, item->descText, 0, 0, item->textStyle, item->iMenuFont); + + if (!parent->descScale) + { + parent->descScale = 1; + } + + DC->drawText(xPos, parent->descY, (float) parent->descScale, parent->descColor, item->descText, 0, 0, item->textStyle, FONT_MEDIUM); } } @@ -4244,6 +4657,9 @@ void Item_Paint(itemDef_t *item) case ITEM_TYPE_LISTBOX: Item_ListBox_Paint(item); break; + case ITEM_TYPE_TEXTSCROLL: + Item_TextScroll_Paint ( item ); + break; //case ITEM_TYPE_IMAGE: // Item_Image_Paint(item); // break; @@ -4515,27 +4931,42 @@ void Menu_Paint(menuDef_t *menu, qboolean forcePaint) { Item_ValidateTypeData =============== */ -void Item_ValidateTypeData(itemDef_t *item) { - if (item->typeData) { +void Item_ValidateTypeData(itemDef_t *item) +{ + if (item->typeData) + { return; } - if (item->type == ITEM_TYPE_LISTBOX) { + if (item->type == ITEM_TYPE_LISTBOX) + { item->typeData = UI_Alloc(sizeof(listBoxDef_t)); memset(item->typeData, 0, sizeof(listBoxDef_t)); - } else if (item->type == ITEM_TYPE_EDITFIELD || item->type == ITEM_TYPE_NUMERICFIELD || item->type == ITEM_TYPE_YESNO || item->type == ITEM_TYPE_BIND || item->type == ITEM_TYPE_SLIDER || item->type == ITEM_TYPE_TEXT) { + } + else if (item->type == ITEM_TYPE_EDITFIELD || item->type == ITEM_TYPE_NUMERICFIELD || item->type == ITEM_TYPE_YESNO || item->type == ITEM_TYPE_BIND || item->type == ITEM_TYPE_SLIDER || item->type == ITEM_TYPE_TEXT) + { item->typeData = UI_Alloc(sizeof(editFieldDef_t)); memset(item->typeData, 0, sizeof(editFieldDef_t)); - if (item->type == ITEM_TYPE_EDITFIELD) { - if (!((editFieldDef_t *) item->typeData)->maxPaintChars) { + if (item->type == ITEM_TYPE_EDITFIELD) + { + if (!((editFieldDef_t *) item->typeData)->maxPaintChars) + { ((editFieldDef_t *) item->typeData)->maxPaintChars = MAX_EDITFIELD; } } - } else if (item->type == ITEM_TYPE_MULTI) { + } + else if (item->type == ITEM_TYPE_MULTI) + { item->typeData = UI_Alloc(sizeof(multiDef_t)); - } else if (item->type == ITEM_TYPE_MODEL) { + } + else if (item->type == ITEM_TYPE_MODEL) + { item->typeData = UI_Alloc(sizeof(modelDef_t)); } + else if (item->type == ITEM_TYPE_TEXTSCROLL ) + { + item->typeData = UI_Alloc(sizeof(textScrollDef_t)); + } } /* @@ -5289,7 +5720,45 @@ qboolean ItemParse_maxPaintChars( itemDef_t *item, int handle ) { return qtrue; } +qboolean ItemParse_maxLineChars( itemDef_t *item, int handle ) +{ + textScrollDef_t *scrollPtr; + int maxChars; + Item_ValidateTypeData(item); + if (!item->typeData) + return qfalse; + + if (!PC_Int_Parse(handle, &maxChars)) + { + return qfalse; + } + + scrollPtr = (textScrollDef_t*)item->typeData; + scrollPtr->maxLineChars = maxChars; + + return qtrue; +} + +qboolean ItemParse_lineHeight( itemDef_t *item, int handle ) +{ + textScrollDef_t *scrollPtr; + int height; + + Item_ValidateTypeData(item); + if (!item->typeData) + return qfalse; + + if (!PC_Int_Parse(handle, &height)) + { + return qfalse; + } + + scrollPtr = (textScrollDef_t*)item->typeData; + scrollPtr->lineHeight = height; + + return qtrue; +} qboolean ItemParse_cvarFloat( itemDef_t *item, int handle ) { editFieldDef_t *editPtr; @@ -5545,6 +6014,11 @@ keywordHash_t itemParseKeywords[] = { {"type", ItemParse_type, NULL }, {"visible", ItemParse_visible, NULL }, {"wrapped", ItemParse_wrapped, NULL }, + + // Text scroll specific + {"maxLineChars", ItemParse_maxLineChars, NULL }, + {"lineHeight", ItemParse_lineHeight, NULL }, + {0, 0, 0 } }; @@ -5602,21 +6076,117 @@ qboolean Item_Parse(int handle, itemDef_t *item) { return qfalse; // bk001205 - LCC missing return value } +static void Item_TextScroll_BuildLines ( itemDef_t* item ) +{ + textScrollDef_t* scrollPtr = (textScrollDef_t*) item->typeData; + int len; + int width; + char* lineStart; + + scrollPtr->lineCount = 0; + len = strlen ( item->text ); + width = scrollPtr->maxLineChars; + lineStart = (char*)item->text; + + // Keep going as long as there are more lines + while ( len > width && scrollPtr->lineCount < MAX_TEXTSCROLL_LINES ) + { + char* lineEnd; + + // Carriage returns break the line earlier than the width + lineEnd = lineStart; + while ( lineEnd - lineStart < width && *lineEnd ) + { + if ( *lineEnd == '\n' ) + { + break; + } + + lineEnd++; + } + + // If a wasnt found then start with the width + if ( !lineEnd || *lineEnd != '\n' ) + { + // Start with a position right at the width and then backtrack until we + // find a space. + lineEnd = lineStart + width; + while ( *lineEnd != ' ' && *lineEnd != '\t' && lineEnd > lineStart ) + { + lineEnd--; + } + } + + // This probably isnt too graceful, but if a line has a word that is longer + // than the width in characters then the word will be skipped all together. + if ( lineEnd >= lineStart ) + { + // throw down a null terminator + *lineEnd = '\0'; + + // Add the line to the list, leave any trailing spaces + scrollPtr->lines[ scrollPtr->lineCount++ ] = lineStart; + + // Skip past the space that we landed on + lineEnd++; + + // Skip any whitespaces + while ( (*lineEnd == ' ' || *lineEnd == '\t') && *lineEnd ) + { + lineEnd++; + } + + // Subtract out the number of characters used in the last line + len -= ( lineEnd - lineStart); + + lineStart = lineEnd; + } + } + + if ( len && scrollPtr->lineCount < MAX_TEXTSCROLL_LINES ) + { + scrollPtr->lines[ scrollPtr->lineCount++ ] = lineStart; + } +} // Item_InitControls // init's special control types -void Item_InitControls(itemDef_t *item) { - if (item == NULL) { +void Item_InitControls(itemDef_t *item) +{ + if (item == NULL) + { return; } - if (item->type == ITEM_TYPE_LISTBOX) { - listBoxDef_t *listPtr = (listBoxDef_t*)item->typeData; - item->cursorPos = 0; - if (listPtr) { - listPtr->cursorPos = 0; - listPtr->startPos = 0; - listPtr->endPos = 0; - listPtr->cursorPos = 0; + + switch ( item->type ) + { + case ITEM_TYPE_LISTBOX: + { + listBoxDef_t *listPtr = (listBoxDef_t*)item->typeData; + item->cursorPos = 0; + if (listPtr) + { + listPtr->cursorPos = 0; + listPtr->startPos = 0; + listPtr->endPos = 0; + listPtr->cursorPos = 0; + } + + break; + } + + case ITEM_TYPE_TEXTSCROLL: + { + textScrollDef_t *scrollPtr = (textScrollDef_t*)item->typeData; + if ( scrollPtr ) + { + scrollPtr->startPos = 0; + scrollPtr->endPos = 0; + } + + Item_TextScroll_BuildLines ( item ); + + break; } } } @@ -5645,7 +6215,7 @@ qboolean MenuParse_name( itemDef_t *item, int handle ) { if (!PC_String_Parse(handle, &menu->window.name)) { return qfalse; } - if (Q_stricmp(menu->window.name, "main") == 0) { + if (Q_stricmp(menu->window.name, "mainMenu") == 0) { // default main as having focus //menu->window.flags |= WINDOW_HASFOCUS; } @@ -5804,6 +6374,23 @@ qboolean MenuParse_descY( itemDef_t *item, int handle ) } return qtrue; } + +/* +================= +MenuParse_descScale +================= +*/ +qboolean MenuParse_descScale( itemDef_t *item, int handle) +{ + menuDef_t *menu = (menuDef_t*)item; + + if (!PC_Float_Parse(handle, &menu->descScale)) + { + return qfalse; + } + return qtrue; +} + /* ================= MenuParse_descColor @@ -6026,6 +6613,7 @@ keywordHash_t menuParseKeywords[] = { {"desccolor", MenuParse_descColor, NULL }, {"descX", MenuParse_descX, NULL }, {"descY", MenuParse_descY, NULL }, + {"descScale", MenuParse_descScale, NULL }, {"disablecolor", MenuParse_disablecolor, NULL }, {"fadeAmount", MenuParse_fadeAmount, NULL }, {"fadeClamp", MenuParse_fadeClamp, NULL }, diff --git a/CODE-mp/ui/ui_shared.h b/CODE-mp/ui/ui_shared.h index 28e357d..df33718 100644 --- a/CODE-mp/ui/ui_shared.h +++ b/CODE-mp/ui/ui_shared.h @@ -17,6 +17,7 @@ #define MAX_MENUITEMS 128 #define MAX_COLOR_RANGES 10 #define MAX_OPEN_MENUS 16 +#define MAX_TEXTSCROLL_LINES 256 #define WINDOW_MOUSEOVER 0x00000001 // mouse is over it, non exclusive #define WINDOW_HASFOCUS 0x00000002 // has cursor focus, exclusive @@ -194,6 +195,20 @@ typedef struct modelDef_s { int rotationSpeed; } modelDef_t; +typedef struct textScrollDef_s +{ + int startPos; + int endPos; + + float lineHeight; + int maxLineChars; + int drawPadding; + + int lineCount; + char* lines[MAX_TEXTSCROLL_LINES]; + +} textScrollDef_t; + #define ITEM_ALIGN_LEFT 0 // left alignment #define ITEM_ALIGN_CENTER 1 // center alignment #define ITEM_ALIGN_RIGHT 2 // right alignment @@ -263,6 +278,7 @@ typedef struct { int descY; // X position of description vec4_t descColor; // description text color for items int descAlignment; // Description of alignment + float descScale; // Description scale float appearanceTime; // when next item should appear int appearanceCnt; // current item displayed float appearanceIncrement; // diff --git a/CODE-mp/ui/vssver.scc b/CODE-mp/ui/vssver.scc deleted file mode 100644 index 69aab8a..0000000 Binary files a/CODE-mp/ui/vssver.scc and /dev/null differ diff --git a/CODE-mp/vssver.scc b/CODE-mp/vssver.scc deleted file mode 100644 index 3728913..0000000 Binary files a/CODE-mp/vssver.scc and /dev/null differ diff --git a/CODE-mp/win32/vssver.scc b/CODE-mp/win32/vssver.scc deleted file mode 100644 index 5ce0ab8..0000000 Binary files a/CODE-mp/win32/vssver.scc and /dev/null differ diff --git a/CODE-mp/win32/win_glimp.cpp b/CODE-mp/win32/win_glimp.cpp index 64eb0dd..d498f1e 100644 --- a/CODE-mp/win32/win_glimp.cpp +++ b/CODE-mp/win32/win_glimp.cpp @@ -19,7 +19,7 @@ #include "resource.h" #include "glw_win.h" #include "win_local.h" - +#include "../qcommon/strip.h" extern void WG_CheckHardwareGamma( void ); extern void WG_RestoreGamma( void ); @@ -724,15 +724,45 @@ static rserr_t GLW_SetMode( int mode, { if ( colorbits == 0 || ( !cdsFullscreen && colorbits >= 15 ) ) { - if ( MessageBox( NULL, - "It is highly unlikely that a correct\n" - "windowed display can be initialized with\n" - "the current desktop display depth. Select\n" - "'OK' to try anyway. Press 'Cancel' if you\n" - "have a 3Dfx Voodoo, Voodoo-2, or Voodoo Rush\n" - "3D accelerator installed, or if you otherwise\n" - "wish to quit.", - "Low Desktop Color Depth", + const char *psErrorTitle_English = "Low Desktop Color Depth"; + const char *psErrorBody_English = "It is highly unlikely that a correct windowed\n" + "display can be initialized with the current\n" + "desktop display depth. Select 'OK' to try\n" + "anyway. Select 'Cancel' to try a fullscreen\n" + "mode instead."; + + const char *psErrorTitle_German = "Falsche Desktop-Farbtiefe"; + const char *psErrorBody_German = "Es ist unwahrscheinlich, dass bei der momentanen\n" + "Desktop-Farbiefe ein Fenstermodus initialisiert\n" + "werden kann. Mit 'OK' versuchen Sie es dennoch,\n" + "mit 'Abbrechen' wechselt das Spiel in den\n" + "Vollbildmodus."; + + const char *psErrorTitle_French = "Basse Intensité De la Couleur DeskTop"; + const char *psErrorBody_French = "Il est fortement peu probable qu'un correct windowed\n" + "l'affichage peut être initialisé avec la profondeur\n" + "de bureau actuelle d'affichage. Choisissez 'OK'\n" + "pour essayer de toute façon. Choisissez 'ANNUL'\n" + "pour essayer a fullscreen le mode à la place."; + + const char *psHeadText = psErrorTitle_English; + const char *psBodyText = psErrorBody_English; + + if (Language_GetIntegerValue() == SP_LANGUAGE_GERMAN) + { + psHeadText = psErrorTitle_German; + psBodyText = psErrorBody_German; + } + else + if (Language_GetIntegerValue() == SP_LANGUAGE_FRENCH) + { + psHeadText = psErrorTitle_French; + psBodyText = psErrorBody_French; + } + + if ( MessageBox( NULL, + psBodyText, + psHeadText, MB_OKCANCEL | MB_ICONEXCLAMATION ) != IDOK ) { return RSERR_INVALID_MODE; diff --git a/CODE-mp/win32/win_input.cpp b/CODE-mp/win32/win_input.cpp index 52cc8f1..f66bfe5 100644 --- a/CODE-mp/win32/win_input.cpp +++ b/CODE-mp/win32/win_input.cpp @@ -52,6 +52,7 @@ typedef struct { static joystickInfo_t joy; +cvar_t *k_language; cvar_t *in_midi; cvar_t *in_midiport; @@ -656,6 +657,8 @@ void IN_Init( void ) { joy_threshold = Cvar_Get ("joy_threshold", "0.15", CVAR_ARCHIVE); + k_language = Cvar_Get ("k_language", "american", CVAR_ARCHIVE | CVAR_NORESTART); + IN_Startup(); } diff --git a/CODE-mp/win32/win_main.cpp b/CODE-mp/win32/win_main.cpp index 3742310..6a012f9 100644 --- a/CODE-mp/win32/win_main.cpp +++ b/CODE-mp/win32/win_main.cpp @@ -1468,7 +1468,6 @@ void QuickMemTest(void) LPCSTR psContinue = "Your machine failed to allocate %dMB in a memory test, which may mean you'll have problems running this game all the way through.\n\nContinue anyway?"; LPCSTR psNoMem = "Insufficient memory to run this game!\n"; - extern int Language_GetIntegerValue(void); switch (Language_GetIntegerValue()) { case SP_LANGUAGE_GERMAN: diff --git a/CODE-mp/win32/win_syscon.cpp b/CODE-mp/win32/win_syscon.cpp index bde2720..e5d0360 100644 --- a/CODE-mp/win32/win_syscon.cpp +++ b/CODE-mp/win32/win_syscon.cpp @@ -473,8 +473,10 @@ char *Sys_ConsoleInput( void ) void Conbuf_AppendText( const char *pMsg ) { #define CONSOLE_BUFFER_SIZE 16384 - - char buffer[CONSOLE_BUFFER_SIZE*2]; + if ( !s_wcd.hWnd ) { + return; + } + char buffer[CONSOLE_BUFFER_SIZE*4]; char *b = buffer; const char *msg; int bufLen; @@ -538,10 +540,6 @@ void Conbuf_AppendText( const char *pMsg ) // if ( s_totalChars > 0x7fff ) { -/* SendMessage( s_wcd.hwndBuffer, EM_SETSEL, 0, s_totalChars - 0x7fff ); - SendMessage( s_wcd.hwndBuffer, EM_REPLACESEL, 0, (LPARAM) "" ); - s_totalChars = 0x7fff; - SendMessage( s_wcd.hwndBuffer, EM_SETSEL, s_totalChars, s_totalChars );*/ SendMessage( s_wcd.hwndBuffer, EM_SETSEL, 0, -1 ); s_totalChars = bufLen; } diff --git a/CODE-mp/win32/win_wndproc.cpp b/CODE-mp/win32/win_wndproc.cpp index d8f9003..8ad1f42 100644 --- a/CODE-mp/win32/win_wndproc.cpp +++ b/CODE-mp/win32/win_wndproc.cpp @@ -116,6 +116,96 @@ static byte s_scantokey[128] = 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // 7 }; +static byte s_scantokey_german[128] = + { +// 0 1 2 3 4 5 6 7 +// 8 9 A B C D E F + 0 , 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '?', '\'', K_BACKSPACE, 9, // 0 + 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', + 'o', 'p', '=', '+', 13 , K_CTRL, 'a', 's', // 1 + 'd', 'f', 'g', 'h', 'j', 'k', 'l', '[', + ']' , '`', K_SHIFT,'#', 'y', 'x', 'c', 'v', // 2 + 'b', 'n', 'm', ',', '.', '-', K_SHIFT,'*', + K_ALT,' ', K_CAPSLOCK , K_F1, K_F2, K_F3, K_F4, K_F5, // 3 + K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, 0 , K_HOME, + K_UPARROW,K_PGUP,K_KP_MINUS,K_LEFTARROW,K_KP_5,K_RIGHTARROW, K_KP_PLUS,K_END, //4 + K_DOWNARROW,K_PGDN,K_INS,K_DEL,0,0, '<', K_F11, + K_F12,0 , 0 , 0 , 0 , 0 , 0 , 0, // 5 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, // 6 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // 7 +}; + +static byte s_scantokey_french[128] = + { +// 0 1 2 3 4 5 6 7 +// 8 9 A B C D E F + 0 , 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', ')', '=', K_BACKSPACE, 9, // 0 + 'a', 'z', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '^', '$', 13 , K_CTRL, 'q', 's', // 1 + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', + '%' , '`', K_SHIFT,'*', 'w', 'x', 'c', 'v', // 2 + 'b', 'n', ',', ';', ':', '!', K_SHIFT,'*', + K_ALT,' ', K_CAPSLOCK , K_F1, K_F2, K_F3, K_F4, K_F5, // 3 + K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, 0 , K_HOME, + K_UPARROW,K_PGUP,K_KP_MINUS,K_LEFTARROW,K_KP_5,K_RIGHTARROW, K_KP_PLUS,K_END, //4 + K_DOWNARROW,K_PGDN,K_INS,K_DEL,0,0, '<', K_F11, + K_F12,0 , 0 , 0 , 0 , 0 , 0 , 0, // 5 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, // 6 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // 7 +}; + +static byte s_scantokey_spanish[128] = +{ +// 0 1 2 3 4 5 6 7 +// 8 9 A B C D E F + 0 , 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '\'', '!', K_BACKSPACE, 9, // 0 + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', 13 , K_CTRL, 'a', 's', // 1 + 'd', 'f', 'g', 'h', 'j', 'k', 'l', '=', + '{' , '`', K_SHIFT,'}', 'z', 'x', 'c', 'v', // 2 + 'b', 'n', 'm', ',', '.', '-', K_SHIFT,'*', + K_ALT,' ', K_CAPSLOCK , K_F1, K_F2, K_F3, K_F4, K_F5, // 3 + K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, 0 , K_HOME, + K_UPARROW,K_PGUP,K_KP_MINUS,K_LEFTARROW,K_KP_5,K_RIGHTARROW, K_KP_PLUS,K_END, //4 + K_DOWNARROW,K_PGDN,K_INS,K_DEL,0,0, '<', K_F11, + K_F12,0 , 0 , 0 , 0 , 0 , 0 , 0, // 5 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, // 6 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // 7 +}; + +static byte s_scantokey_italian[128] = +{ +// 0 1 2 3 4 5 6 7 +// 8 9 A B C D E F + 0 , 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '\'', '^', K_BACKSPACE, 9, // 0 + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', 13 , K_CTRL, 'a', 's', // 1 + 'd', 'f', 'g', 'h', 'j', 'k', 'l', '@', + '#' , '`', K_SHIFT,'=', 'z', 'x', 'c', 'v', // 2 + 'b', 'n', 'm', ',', '.', '-', K_SHIFT,'*', + K_ALT,' ', K_CAPSLOCK , K_F1, K_F2, K_F3, K_F4, K_F5, // 3 + K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, 0 , K_HOME, + K_UPARROW,K_PGUP,K_KP_MINUS,K_LEFTARROW,K_KP_5,K_RIGHTARROW, K_KP_PLUS,K_END, //4 + K_DOWNARROW,K_PGDN,K_INS,K_DEL,0,0, '<', K_F11, + K_F12,0 , 0 , 0 , 0 , 0 , 0 , 0, // 5 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, // 6 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // 7 +}; + +extern cvar_t *k_language; + /* ======= MapKey @@ -146,6 +236,16 @@ static int MapKey (int key) } result = s_scantokey[modified]; + if ( !Q_stricmp(k_language->string, "deutsch") ) { + result = s_scantokey_german[modified]; + } else if ( !Q_stricmp(k_language->string, "francais") ) { + result = s_scantokey_french[modified]; + } else if ( !Q_stricmp(k_language->string, "espanol") ) { + result = s_scantokey_spanish[modified]; + } else if ( !Q_stricmp(k_language->string, "italiano") ) { + result = s_scantokey_italian[modified]; + } + if ( !is_extended ) { diff --git a/CODE-mp/win32/winquake.res b/CODE-mp/win32/winquake.res new file mode 100644 index 0000000..9bc4bc4 Binary files /dev/null and b/CODE-mp/win32/winquake.res differ diff --git a/CODE-mp/zlib/vssver.scc b/CODE-mp/zlib/vssver.scc deleted file mode 100644 index e6f9f26..0000000 Binary files a/CODE-mp/zlib/vssver.scc and /dev/null differ diff --git a/code/0_compiled_first/vssver.scc b/code/0_compiled_first/vssver.scc deleted file mode 100644 index f854df8..0000000 Binary files a/code/0_compiled_first/vssver.scc and /dev/null differ diff --git a/code/ALut.lib b/code/ALut.lib deleted file mode 100644 index 67cde9f..0000000 Binary files a/code/ALut.lib and /dev/null differ diff --git a/code/EaxMan.dll b/code/EaxMan.dll deleted file mode 100644 index 0a41c53..0000000 Binary files a/code/EaxMan.dll and /dev/null differ diff --git a/code/FFC10.dll b/code/FFC10.dll deleted file mode 100644 index 0536f23..0000000 Binary files a/code/FFC10.dll and /dev/null differ diff --git a/code/FFC10d.dll b/code/FFC10d.dll deleted file mode 100644 index 1f546c2..0000000 Binary files a/code/FFC10d.dll and /dev/null differ diff --git a/code/FinalBuild/exe/winquake.res b/code/FinalBuild/exe/winquake.res new file mode 100644 index 0000000..9ef8365 Binary files /dev/null and b/code/FinalBuild/exe/winquake.res differ diff --git a/code/OpenAL32.dll b/code/OpenAL32.dll deleted file mode 100644 index 4a8dfde..0000000 Binary files a/code/OpenAL32.dll and /dev/null differ diff --git a/code/OpenAL32.lib b/code/OpenAL32.lib deleted file mode 100644 index 86de420..0000000 Binary files a/code/OpenAL32.lib and /dev/null differ diff --git a/code/Release/exe/winquake.res b/code/Release/exe/winquake.res new file mode 100644 index 0000000..9ef8365 Binary files /dev/null and b/code/Release/exe/winquake.res differ diff --git a/code/SHDebug/HA312W32.DLL b/code/SHDebug/HA312W32.DLL deleted file mode 100644 index 2b1ea82..0000000 Binary files a/code/SHDebug/HA312W32.DLL and /dev/null differ diff --git a/code/SHDebug/SHW32.DLL b/code/SHDebug/SHW32.DLL deleted file mode 100644 index 42b2f67..0000000 Binary files a/code/SHDebug/SHW32.DLL and /dev/null differ diff --git a/code/SHDebug/vssver.scc b/code/SHDebug/vssver.scc deleted file mode 100644 index 964c420..0000000 Binary files a/code/SHDebug/vssver.scc and /dev/null differ diff --git a/code/base/ext_data/NPCs.cfg b/code/base/ext_data/NPCs.cfg deleted file mode 100644 index 6ede82a..0000000 --- a/code/base/ext_data/NPCs.cfg +++ /dev/null @@ -1,2082 +0,0 @@ -//Star Wars -/* -defaults and explanations of fields: - aggression 3 How likely they are to attack (from 1 (least) to 5 (most)) - aim 3 How good their aim is (from 1 (worst) to 5 (best)) - earshot 1024 How far in map units they can hear, in map units - evasion 3 How likely they are to take cover or defensive maneuvers (from 1 (least) to 5 (most)) - hfov 45 Horizontal field of view, in angles - intelligence 3 How smart they are, in general (from 1 (least) to 5 (most)) - move 3 How complex their moves are when evading or in combat (from 1 (least) to 5 (most)) - reactions 3 How quickly they react (from 1 (worst) to 5 (best)) - shootDistance 0 Overrides current weapon's max range - vfov 34 Vertical field of view, in angles - vigilance 0.1 How likely they are to notice something (from 0 (never) to 1 (always)) - visrange 2048 How far away they can see something, in map units - race none human, borg, parasite, klingon, malon, hirogen, stasis, species8472, dreadnought, harvester, reaver, avatar, vulcan - playerTeam none player, enemy, neutral - enemyTeam none player, enemy, neutral - - health 100 Health of entity (if not supplied by designer) - - moveType "runjump" Which movetype they can be (other choices are "static", "walk" and "flyswim" - yawSpeed 50 How quickly they can turn - walkSpeed 150 How fast they walk - runSpeed 300 How fast they run - acceleration 15 Acceleration (accel x 20fps = speed up per second, so accel of 15 means they can go from 0 to 300 in one second) - Accel of 0 means don't accel/decel - just start/stop (good if you're a slow mover anyway and/or robotic - like a Borg) - - scaleX 100 X (horiz) scale, 100 is normal 100% scale - scaleY 100 Y (horiz) scale, 100 is normal 100% scale - scaleZ 100 Z (vert) scale, 100 is normal 100% scale - scale 100 Sets all above 3 to what you specify - headModel "hazard" model directory/skin name - torsoModel "hazard" model directory/skin name - legsModel "hazard" model directory/skin name - headYawRangeLeft 70 How far left you can turn your head (angles) - headYawRangeRight 70 How far right you can turn your head (angles) - headPitchRangeUp 60 How far up you can tilt your head (angles) - headPitchRangeDown 60 How far down you can tilt your head (angles) - torsoYawRangeLeft 60 How far left you can turn your torso (angles) - torsoYawRangeRight 60 How far right you can turn your torso (angles) - torsoPitchRangeUp 30 How far up you can tilt your torso (angles) - torsoPitchRangeDown 70 How far down you can tilt your torso (angles) - - snd "munro" subdirectory of sound/player from which to get custom sounds (pain, death, jump, etc.) - dismemberProbHead 0 probability of head being dismembered ( from 0 (never) to 100 (always) ) - dismemberProbArms 0 probability of arms being dismembered ( from 0 (never) to 100 (always) ) - dismemberProbLegs 0 probability of legs being dismembered ( from 0 (never) to 100 (always) ) - dismemberProbWaist 0 probability of waist being dismembered ( from 0 (never) to 100 (always) ) -*/ - -//Characters -munro -{ - fullName "Katarn, Kyle" - playerModel kyle - saberColor blue - reactions 4 - aim 5 - move 3 - aggression 5 - evasion 5 - intelligence 5 - playerTeam player -// race human - class kyle - snd munro -} - -Kyle -{ - fullName "Katarn, Kyle" - playerModel kyle - saberColor blue - reactions 4 - aim 5 - move 3 - aggression 5 - evasion 5 - intelligence 5 - playerTeam player -// race human - class kyle - snd kyle - sndcombat kyle - sndjedi kyle - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -Tavion -{ - playerModel tavion - rank commander - saberColor red - reactions 3 - aim 3 - move 5 - aggression 3 - evasion 4 - intelligence 5 - hfov 160 - vfov 160 - playerTeam enemy - enemyTeam player -// race human - class tavion - snd tavion - sndcombat tavion - sndjedi tavion - yawSpeed 120 - walkSpeed 55 - runSpeed 200 - health 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -Lando -{ - fullName "Calrissian, Lando" - playerModel lando - snd lando - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - rank crewman - playerTeam player - enemyTeam enemy -// race human - class lando - snd lando - sndcombat lando - walkSpeed 55 - runSpeed 200 - yawspeed 90 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -Reelo -{ - fullName "Baruk, Reelo" - playerModel reelo - snd reelo - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - rank crewman - playerTeam enemy - enemyTeam player -// race klingon - class reelo - snd reelo - sndcombat reelo - yawspeed 90 - walkSpeed 55 - runSpeed 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -Jan -{ - fullName "Ors, Jan" - playerModel jan - rank lt - reactions 3 - aim 5 - move 3 - aggression 3 - evasion 3 - intelligence 3 - playerTeam player - enemyTeam enemy - class jan - snd jan - sndcombat jan - yawSpeed 140 - walkSpeed 55 - runSpeed 200 -// race human - snd jan - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -Galak -{ - fullName "Fyyar, Galak" - playerModel galak - snd galak - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - rank captain - playerTeam enemy - enemyTeam player -// race klingon - class imperial - yawspeed 90 - walkSpeed 55 - runSpeed 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -Galak_Mech -{ - fullName "Fyyar, Galak" - playerModel galak_mech - health 1000 - width 20 - height 88 - crouchheight 88 - snd galak_mech - reactions 3 - aim 5 - move 3 - aggression 3 - evasion 1 - intelligence 5 - rank crewman - playerTeam enemy - enemyTeam player -// race klingon - class galak_mech - snd galak - sndcombat galak - yawSpeed 50 - walkSpeed 45 - runSpeed 150 - dismemberProbHead 0 - dismemberProbArms 50 - dismemberProbLegs 0 - dismemberProbWaist 10 -} - - -Desann -{ - fullName "Desann" - playerModel desann - saberColor red - rank captain - reactions 3 - aim 3 - move 5 - aggression 3 - evasion 5 - intelligence 5 - hfov 160 - vfov 160 - scale 135 - height 78 - crouchheight 42 - width 18 - playerTeam enemy - enemyTeam player -// race human - class desann - yawSpeed 120 - walkSpeed 55 - runSpeed 200 - snd desann - sndcombat desann - sndjedi desann - health 500 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -Luke -{ - fullName "Skywalker, Luke" - playerModel luke - saberColor green - rank captain - reactions 3 - aim 3 - move 3 - aggression 3 - evasion 5 - intelligence 3 - playerTeam player - enemyTeam enemy - class luke - yawSpeed 140 - walkSpeed 55 - runSpeed 200 -// race human - snd luke - sndcombat luke - sndjedi luke - health 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -MonMothma -{ - fullName "Mon Mothma" - playerModel monmothma - snd monmothma - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - rank crewman - playerTeam player - enemyTeam enemy -// race human - class monmotha - walkSpeed 55 - runSpeed 200 - yawspeed 90 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -Bartender -{ - fullName "Bartender" - playerModel chiss - snd bartender - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - rank crewman - playerTeam neutral - enemyTeam neutral -// race human - class bartender - walkSpeed 55 - runSpeed 200 - yawspeed 90 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -MorganKatarn -{ - fullName "MorganKatarn" - playerModel morgan - snd morgan - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - rank crewman - playerTeam neutral - enemyTeam neutral -// race human - class morgan - walkSpeed 55 - runSpeed 200 - yawspeed 90 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -Prisoner -{ - playerModel prisoner - snd prisoner - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - rank crewman - playerTeam player - enemyTeam enemy - class prisoner - snd prisoner - sndcombat prisoner - walkSpeed 55 - runSpeed 200 - yawspeed 90 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -Prisoner2 -{ - playerModel prisoner - snd prisoner - surfOff "head head_face" - surfOn "head_off head_face_off" - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - rank crewman - playerTeam player - enemyTeam enemy - class prisoner - snd prisoner - sndcombat prisoner - walkSpeed 55 - runSpeed 200 - yawspeed 90 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -//NPC Humanoids -Jedi -{ - playerModel jedi - saberColor random - rank lt - reactions 3 - aim 3 - move 3 - aggression 3 - evasion 2 - intelligence 3 - playerTeam player - enemyTeam enemy - class jedi - yawSpeed 140 - walkSpeed 55 - runSpeed 200 - snd jedi1 - sndcombat jedi1 - sndjedi jedi1 - health 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -Jedi2 -{ - playerModel jedi - saberColor random - rank lt - customSkin j2 - surfOff "head head_face" - surfOn "head_off head_face_off" - reactions 3 - aim 3 - move 3 - aggression 3 - evasion 2 - intelligence 3 - playerTeam player - enemyTeam enemy - class jedi - yawSpeed 140 - walkSpeed 55 - runSpeed 200 - snd jedi2 - sndcombat jedi2 - sndjedi jedi2 - health 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -JediF -{ - playerModel jan - surfOff "torso_vest hips_chaps torso_computer head_goggles torso_comp hips_belt" - surfOn "torso_augment_off hips_augment_off hips_torso_off" - saberColor random - rank lt - reactions 3 - aim 3 - move 3 - aggression 3 - evasion 2 - intelligence 3 - playerTeam player - enemyTeam enemy - class jedi - yawSpeed 140 - walkSpeed 55 - runSpeed 200 - snd jan - sndcombat jan - sndjedi jan - health 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -JediTrainer -{ - playerModel jeditrainer - saberColor random - rank commander - reactions 5 - aim 5 - move 5 - aggression 5 - evasion 5 - intelligence 5 - playerTeam player - enemyTeam enemy - class jedi - yawSpeed 140 - walkSpeed 55 - runSpeed 200 - snd jeditrainer - sndcombat jeditrainer - sndjedi jeditrainer - health 400 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -Rebel -{ - playerModel rebel - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - rank crewman - playerTeam player - enemyTeam enemy -// race human - class rebel - snd rebel - sndcombat rebel - yawspeed 90 - walkSpeed 55 - runSpeed 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -BespinCop -{ - playerModel bespin_cop - health 40 - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - rank crewman - playerTeam player - enemyTeam enemy - walkSpeed 55 - runSpeed 200 - yawspeed 90 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -// race bespincop - class bespin_cop - snd bespincop1 - sndcombat bespincop1 -} - -Ugnaught -{ - playerModel ugnaught - scale 75 - health 10 - snd ugnaught - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - rank crewman - playerTeam neutral - enemyTeam player -// race klingon - class ugnaught - yawspeed 90 - walkSpeed 55 - runSpeed 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -Gran -{ - playerModel gran - snd gran - sndcombat gran - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - rank crewman - health 30 - playerTeam enemy - enemyTeam player -// race klingon - class gran - snd gran1 - sndcombat gran1 - yawspeed 90 - walkSpeed 55 - runSpeed 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -GranShooter -{ - playerModel gran - surfOff "l_leg_kneeguard" - snd gran - sndcombat gran - reactions 3 - aim 5 - move 3 - aggression 3 - evasion 1 - intelligence 5 - health 40 - rank crewman - playerTeam enemy - enemyTeam player -// race klingon - class gran - snd gran2 - sndcombat gran2 - yawspeed 90 - walkSpeed 55 - runSpeed 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -GranBoxer -{ - playerModel gran - surfOff "l_leg_kneeguard r_leg_kneeguard" - snd gran - sndcombat gran - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - health 50 - rank crewman - playerTeam enemy - enemyTeam player -// race klingon - class gran - snd gran2 - sndcombat gran2 - yawspeed 90 - walkSpeed 55 - runSpeed 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -Rodian -{ - playerModel rodian - snd rodian - sndcombat rodian - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - health 25 - rank crewman - playerTeam enemy - enemyTeam player -// race klingon - class rodian - snd rodian1 - sndcombat rodian1 - yawspeed 90 - walkSpeed 55 - runSpeed 200 - visrange 8192 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -Rodian2 -{ - playerModel rodian - surfOff "hips_belt torso_vest" - surfOn "torso_augment_off" - snd rodian - sndcombat rodian - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - health 20 - rank crewman - playerTeam enemy - enemyTeam player -// race klingon - class rodian - snd rodian2 - sndcombat rodian2 - yawspeed 90 - walkSpeed 55 - runSpeed 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -Weequay -{ - playerModel weequay -//FIXME: randomize these somehow, also belt... - surfOff "hair_l_hairshoulder hair_r_hairshoulder head_l_hairback" - snd weequay - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - health 30 - rank crewman - playerTeam enemy - enemyTeam player -// race klingon - class weequay - snd weequay - sndcombat weequay - yawspeed 90 - walkSpeed 55 - runSpeed 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -Trandoshan -{ - playerModel trandoshan - snd trandoshan - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - health 40 - rank crewman - playerTeam enemy - enemyTeam player -// race klingon - class trandoshan - snd trandoshan1 - sndcombat trandoshan1 - yawspeed 90 - walkSpeed 55 - runSpeed 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -StormTrooper -{ - playerModel stormtrooper - surfOff torso_pauldron_off - surfOn "torso_armor_neck_augment torso_body_neck_augment" - health 30 - headPitchRangeUp 30 - headPitchRangeDown 30 - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - rank crewman - playerTeam enemy - enemyTeam player -// race klingon - class stormtrooper - height 64 - crouchheight 48 - walkSpeed 51 - runSpeed 200 - snd st1 - sndcombat st1 - sndextra st1 - yawspeed 70 - walkSpeed 55 - runSpeed 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -StormTrooper2 -{ - playerModel stormtrooper - surfOff torso_pauldron_off - surfOn "torso_armor_neck_augment torso_body_neck_augment" - health 30 - headPitchRangeUp 30 - headPitchRangeDown 30 - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - rank crewman - playerTeam enemy - enemyTeam player -// race klingon - class stormtrooper - height 64 - crouchheight 48 - walkSpeed 51 - runSpeed 200 - snd st2 - sndcombat st2 - sndextra st2 - yawspeed 70 - walkSpeed 55 - runSpeed 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -STOfficer -{ - playerModel stormtrooper - surfOn torso_pauldron_off - surfOff "torso_armor_neck_augment torso_body_neck_augment" - health 60 - headPitchRangeUp 30 - headPitchRangeDown 30 - reactions 5 - aim 5 - move 5 - aggression 5 - evasion 5 - intelligence 5 - rank ensign - scale 110 - playerTeam enemy - enemyTeam player -// race klingon - class stormtrooper - height 68 - crouchheight 52 - walkSpeed 51 - runSpeed 200 - snd stofficer1 - sndcombat stofficer1 - sndextra stofficer1 - yawspeed 90 - walkSpeed 55 - runSpeed 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -StormPilot -{ - playerModel stormpilot - health 30 - headPitchRangeUp 30 - headPitchRangeDown 30 - reactions 3 - aim 5 - move 3 - aggression 3 - evasion 1 - intelligence 5 - rank crewman - playerTeam enemy - enemyTeam player - class stormtrooper - height 64 - crouchheight 48 - walkSpeed 51 - runSpeed 200 - snd st3 - sndcombat st3 - sndextra st3 - yawspeed 80 - walkSpeed 55 - runSpeed 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -STOfficerAlt -{ - playerModel stormtrooper - surfOn torso_pauldron_off - surfOff "torso_armor_neck_augment torso_body_neck_augment" - health 60 - headPitchRangeUp 30 - headPitchRangeDown 30 - reactions 5 - aim 5 - move 5 - aggression 5 - evasion 5 - intelligence 5 - rank ensign - scale 110 - playerTeam enemy - enemyTeam player -// race klingon - class stormtrooper - height 68 - crouchheight 52 - walkSpeed 51 - runSpeed 200 - snd stofficer2 - sndcombat stofficer2 - sndextra stofficer2 - yawspeed 90 - walkSpeed 55 - runSpeed 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -STCommander -{ - playerModel stormtrooper - surfOn torso_pauldron_off - surfOff "torso_armor_neck_augment torso_body_neck_augment" - health 60 - headPitchRangeUp 30 - headPitchRangeDown 30 - reactions 5 - aim 5 - move 5 - aggression 5 - evasion 5 - intelligence 5 - rank ensign - scale 110 - playerTeam enemy - enemyTeam player -// race klingon - class stormtrooper - height 68 - crouchheight 52 - walkSpeed 51 - runSpeed 200 - snd stofficer2 - sndcombat stofficer2 - sndextra stofficer2 - yawspeed 110 - walkSpeed 55 - runSpeed 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -SwampTrooper -{ - playerModel swamptrooper - headPitchRangeUp 30 - headPitchRangeDown 30 - health 40 - reactions 3 - aim 3 - move 3 - aggression 3 - evasion 3 - intelligence 3 - scale 110 - playerTeam enemy - enemyTeam player -// race klingon - class swamptrooper - height 68 - crouchheight 52 - snd swamp1 - sndcombat swamp1 - sndextra swamp1 - yawspeed 100 - walkSpeed 55 - runSpeed 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -SwampTrooper2 -{ - playerModel swamptrooper - headPitchRangeUp 30 - headPitchRangeDown 30 - health 40 - reactions 3 - aim 3 - move 3 - aggression 3 - evasion 3 - intelligence 3 - scale 110 - playerTeam enemy - enemyTeam player -// race klingon - class swamptrooper - height 68 - crouchheight 52 - snd swamp2 - sndcombat swamp2 - sndextra swamp2 - yawspeed 100 - walkSpeed 55 - runSpeed 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -RocketTrooper -{ - playerModel stormtrooper - surfOn torso_pauldron_off - surfOff "torso_armor_neck_augment torso_body_neck_augment" - health 60 - headPitchRangeUp 30 - headPitchRangeDown 30 - reactions 5 - aim 5 - move 5 - aggression 5 - evasion 5 - intelligence 5 - rank ensign - scale 110 - playerTeam enemy - enemyTeam player -// race klingon - class stormtrooper - height 68 - crouchheight 52 - walkSpeed 51 - runSpeed 200 - snd st3 - sndcombat st3 - sndextra st3 - yawspeed 100 - walkSpeed 55 - runSpeed 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -Imperial -{ - playerModel imperial - surfOff "l_arm_pocket l_arm_key" - health 20 - reactions 2 - aim 2 - move 2 - aggression 2 - evasion 2 - intelligence 2 - rank lt - playerTeam enemy - enemyTeam player -// race klingon - class imperial - snd io2 - sndcombat io2 - sndextra io2 - yawspeed 110 - walkSpeed 55 - runSpeed 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -ImpOfficer -{ - playerModel imperial - surfOff "l_arm_pocket l_arm_key" - customSkin officer - health 40 - reactions 3 - aim 3 - move 3 - aggression 3 - evasion 3 - intelligence 3 - rank ltcomm - playerTeam enemy - enemyTeam player -// race klingon - class imperial - snd io1 - sndcombat io1 - sndextra io1 - yawspeed 110 - walkSpeed 55 - runSpeed 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -ImpCommander -{ - playerModel imperial - surfOff l_arm_key - customSkin commander - health 80 - reactions 4 - aim 4 - move 4 - aggression 4 - evasion 4 - intelligence 4 - rank commander - playerTeam enemy - enemyTeam player -// race klingon - class imperial -// snd io3 -// sndcombat io3 -// sndextra io3 - snd io1 - sndcombat io1 - sndextra io1 - yawspeed 110 - walkSpeed 55 - runSpeed 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -ImpWorker -{ - playerModel imperial_worker - headPitchRangeUp 30 - headPitchRangeDown 30 - health 30 - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - rank crewman - playerTeam enemy - enemyTeam player -// race imperial - class impworker - height 64 - crouchheight 48 - walkSpeed 51 - runSpeed 200 - snd worker1 - sndcombat worker1 - sndextra worker1 - yawspeed 90 - walkSpeed 55 - runSpeed 200 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -RebornAcrobat -{ - playerModel reborn - customSkin acrobat - saberColor red - rank crewman - reactions 3 - aim 3 - move 5 - aggression 3 - evasion 3 - intelligence 5 - hfov 160 - vfov 160 - scale 96 - playerTeam enemy - enemyTeam player -// race human - class reborn - snd reborn1 - sndcombat reborn1 - sndjedi reborn1 - yawSpeed 140 - walkSpeed 55 - runSpeed 200 - health 100 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -Reborn -{ - playerModel reborn - saberColor red - reactions 1 - aim 1 - move 1 - aggression 1 - evasion 1 - intelligence 1 - hfov 120 - vfov 120 - scale 94 - playerTeam enemy - enemyTeam player -// race human - class reborn - snd reborn1 - sndcombat reborn1 - sndjedi reborn1 - yawSpeed 60 - walkSpeed 45 - runSpeed 180 - health 40 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -RebornForceUser -{ - playerModel reborn - customSkin forceuser - saberColor red - rank ensign - reactions 3 - aim 3 - move 5 - aggression 2 - evasion 2 - intelligence 5 - hfov 160 - vfov 160 - scale 96 - playerTeam enemy - enemyTeam player -// race human - class reborn - snd reborn2 - sndcombat reborn2 - sndjedi reborn2 - yawSpeed 80 - walkSpeed 55 - runSpeed 200 - health 100 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -RebornFencer -{ - playerModel reborn - customSkin fencer - saberColor red - rank ltjg - reactions 3 - aim 3 - move 5 - aggression 4 - evasion 2 - intelligence 5 - hfov 160 - vfov 160 - scale 96 - playerTeam enemy - enemyTeam player -// race human - class reborn - snd reborn2 - sndcombat reborn2 - sndjedi reborn2 - yawSpeed 140 - walkSpeed 55 - runSpeed 200 - health 100 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -RebornBoss -{ - playerModel reborn - customSkin boss - saberColor red - rank lt - reactions 3 - aim 3 - move 5 - aggression 4 - evasion 3 - intelligence 5 - hfov 160 - vfov 160 - playerTeam enemy - enemyTeam player -// race human - class reborn - snd reborn3 - sndcombat reborn3 - sndjedi reborn3 - yawSpeed 140 - walkSpeed 55 - runSpeed 200 - health 150 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -ShadowTrooper -{ - playerModel shadowtrooper - saberColor red - rank ltcomm - reactions 5 - aim 5 - move 5 - aggression 5 - evasion 4 - intelligence 5 - hfov 160 - vfov 160 - playerTeam enemy - enemyTeam player -// race human - class shadowtrooper - snd shadow1 - sndcombat shadow1 - yawSpeed 140 - walkSpeed 55 - runSpeed 200 - health 100 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -ShadowTrooper2 -{ - playerModel shadowtrooper - saberColor red - rank lt - reactions 5 - aim 5 - move 5 - aggression 5 - evasion 4 - intelligence 5 - hfov 160 - vfov 160 - playerTeam enemy - enemyTeam player -// race human - class shadowtrooper - snd shadow2 - sndcombat shadow2 - yawSpeed 140 - walkSpeed 55 - runSpeed 200 - health 100 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} -//NPC Monsters -Howler -{ - playerModel howler - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - playerTeam enemy - enemyTeam player - class howler - yawSpeed 60 - runSpeed 150 - walkSpeed 50 - hFOV 120 - vfov 45 - snd howler - health 60 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -Minemonster -{ - playerModel minemonster - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - playerTeam enemy - enemyTeam player - class minemonster - snd mine - yawSpeed 60 - runSpeed 150 - walkSpeed 50 - hFOV 120 - vfov 45 - height 30 - width 9 - snd mine - health 40 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - - -Glider -{ - playerModel glider - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - playerTeam neutral - enemyTeam neutral -// race harvester - class glider - yawSpeed 60 - runSpeed 150 - walkSpeed 50 - hFOV 120 - vfov 45 - snd glider - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - - -//NPC Droids -protocol -{ - playerModel protocol - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - playerTeam neutral - enemyTeam neutral -// race bot - class protocol - snd protocol - yawSpeed 60 - runSpeed 150 - walkSpeed 50 - height 48 - width 12 - hFOV 120 - vfov 45 - snd protocol - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -protocol_imp -{ - playerModel protocol - surfOn head_off - surfOff head - customSkin imp - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - playerTeam neutral - enemyTeam neutral -// race bot - class protocol - snd protocol - yawSpeed 60 - runSpeed 150 - walkSpeed 50 - height 48 - width 12 - hFOV 120 - vfov 45 - snd protocol - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -r2d2 -{ - playermodel r2d2 - headYawRangeLeft 180 - headYawRangeRight 180 - headPitchRangeUp 0 - headPitchRangeDown 0 - torsoYawRangeLeft 0 - torsoYawRangeRight 0 - torsoPitchRangeUp 10 - torsoPitchRangeDown 10 - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - playerTeam neutral - enemyTeam neutral -// race bot - class r2d2 - yawSpeed 120 - runSpeed 150 - walkSpeed 50 - height 40 - width 12 - hFOV 120 - vfov 45 - snd r2d2 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -r2d2_imp -{ - playermodel r2d2 - customSkin imp - headYawRangeLeft 180 - headYawRangeRight 180 - headPitchRangeUp 0 - headPitchRangeDown 0 - torsoYawRangeLeft 0 - torsoYawRangeRight 0 - torsoPitchRangeUp 10 - torsoPitchRangeDown 10 - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - playerTeam neutral - enemyTeam neutral -// race bot - class r2d2 - yawSpeed 120 - runSpeed 150 - walkSpeed 50 - height 40 - width 12 - hFOV 120 - vfov 45 - snd r2d2 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -r5d2 -{ - playerModel r5d2 - headYawRangeLeft 180 - headYawRangeRight 180 - headPitchRangeUp 0 - headPitchRangeDown 0 - torsoYawRangeLeft 0 - torsoYawRangeRight 0 - torsoPitchRangeUp 10 - torsoPitchRangeDown 10 - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - playerTeam neutral - enemyTeam neutral -// race bot - class r5d2 - yawSpeed 60 - runSpeed 150 - walkSpeed 50 - height 40 - width 12 - hFOV 120 - vfov 45 - snd r5d2 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -r5d2_imp -{ - playerModel r5d2 - customSkin imp - headYawRangeLeft 180 - headYawRangeRight 180 - headPitchRangeUp 0 - headPitchRangeDown 0 - torsoYawRangeLeft 0 - torsoYawRangeRight 0 - torsoPitchRangeUp 10 - torsoPitchRangeDown 10 - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - playerTeam neutral - enemyTeam neutral -// race bot - class r5d2 - yawSpeed 60 - runSpeed 150 - walkSpeed 50 - height 40 - width 12 - hFOV 120 - vfov 45 - snd r5d2 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -gonk -{ - playerModel gonk - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - playerTeam neutral - enemyTeam neutral -// race bot - class gonk - yawSpeed 60 - runSpeed 40 - walkSpeed 30 - height 32 - width 12 - hFOV 120 - vfov 45 - snd gonk - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - - -mouse -{ - headmodel none - torsomodel none - legsmodel mouse - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - playerTeam neutral - enemyTeam neutral -// race bot - class mouse - yawSpeed 120 - runSpeed 500 - walkSpeed 150 - height 16 - width 8 - hFOV 120 - vfov 45 - snd mouse - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - - -seeker -{ - headmodel none - torsomodel none - legsmodel remote - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 3 - intelligence 5 - playerTeam player - enemyTeam enemy -// race bot - class seeker - yawSpeed 120 - runSpeed 500 - walkSpeed 150 - height 32 - width 8 - hFOV 160 - vfov 45 - snd remote - moveType "flyswim" - dismemberProbHead 0 - dismemberProbArms 0 - dismemberProbLegs 0 - dismemberProbWaist 0 -} - -remote -{ - headmodel none - torsomodel none - legsmodel remote - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 3 - intelligence 5 - playerTeam enemy - enemyTeam player -// race bot - class remote - yawSpeed 120 - runSpeed 500 - walkSpeed 150 - height 32 - width 8 - hFOV 160 - vfov 45 - snd remote - moveType "flyswim" - dismemberProbHead 0 - dismemberProbArms 0 - dismemberProbLegs 0 - dismemberProbWaist 0 -} - -sentry -{ - playermodel sentry - health 100 - reactions 3 - aim 3 - move 3 - aggression 3 - evasion 1 - intelligence 5 - playerTeam enemy - enemyTeam player -// race bot - class sentry - health 150 - yawSpeed 120 - runSpeed 400 - walkSpeed 250 - height 48 - width 24 - hFOV 120 - vfov 160 - snd sentry - dismemberProbHead 0 - dismemberProbArms 0 - dismemberProbLegs 0 - dismemberProbWaist 0 -} - -interrogator -{ - playermodel interrogator - health 100 - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - playerTeam enemy - enemyTeam player -// race bot - class interrogator - yawSpeed 120 - runSpeed 150 - walkSpeed 50 - height 24 - width 12 - hFOV 120 - vfov 45 - snd interrogator - dismemberProbHead 0 - dismemberProbArms 0 - dismemberProbLegs 0 - dismemberProbWaist 0 -} - - -probe -{ - playerModel probe - health 200 - headYawRangeLeft 180 - headYawRangeRight 180 - headPitchRangeUp 0 - headPitchRangeDown 0 - torsoYawRangeLeft 0 - torsoYawRangeRight 0 - torsoPitchRangeUp 10 - torsoPitchRangeDown 10 - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - playerTeam enemy - enemyTeam player -// race bot - class probe - yawSpeed 60 - runSpeed 150 - walkSpeed 50 - height 110 - width 24 - hFOV 120 - vfov 45 - snd probe - moveType "flyswim" - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -mark1 -{ - playerModel mark1 - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - height 120 - width 36 - playerTeam enemy - enemyTeam player - health 300 -// race bot - class mark1 - yawSpeed 60 - runSpeed 150 - walkSpeed 70 - hFOV 120 - vfov 45 - snd mark1 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -mark2 -{ - playerModel mark2 - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - playerTeam enemy - enemyTeam player -// race bot - class mark2 - yawSpeed 60 - runSpeed 150 - walkSpeed 75 - hFOV 120 - vfov 45 - snd mark2 - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -atst -{ - playerModel atst -// headModel atst - //torsoModel atst - //legsModel atst - headYawRangeLeft 80 - headYawRangeRight 80 - headPitchRangeUp 30 - headPitchRangeDown 30 - torsoYawRangeLeft 0 - torsoYawRangeRight 0 - torsoPitchRangeUp 0 - torsoPitchRangeDown 0 - health 200 - reactions 3 - aim 1 - move 3 - aggression 3 - evasion 1 - intelligence 5 - playerTeam enemy - enemyTeam player - height 272 - width 80 -// race bot - class atst - yawSpeed 60 - runSpeed 150 - walkSpeed 150 - hFOV 120 - vfov 45 - snd atst - dismemberProbHead 50 - dismemberProbArms 50 - dismemberProbLegs 50 - dismemberProbWaist 50 -} - -test -{ - playerModel test - playerTeam player - enemyTeam neutral - class kyle -} diff --git a/code/base/ext_data/dms.dat b/code/base/ext_data/dms.dat deleted file mode 100644 index 039ab50..0000000 --- a/code/base/ext_data/dms.dat +++ /dev/null @@ -1,1233 +0,0 @@ -musicfiles -{ - kejimbase_explore - { - entry - { - marker0 0.000 - marker1 22.070 - marker2 53.723 - marker3 90.926 - } - exit - { - nextfile kejimbase_etr00 - time00 3.337 - time01 5.668 - time02 22.040 - time03 29.889 - time04 50.124 - time05 37.168 - time06 53.473 - time07 65.923 - time08 90.478 - time09 143.233 - time10 156.166 - time11 176.608 - time12 193.505 - } - exit - { - nextfile kejimbase_etr01 - time00 45.555 - time01 81.525 - time02 106.406 - time03 128.648 - time04 185.168 - time05 209.309 - } - } - kejimbase_action - { - entry - { - marker0 0.00 - marker1 42.585 - marker2 87.664 - } - exit - { - nextfile kejimbase_atr00 - nextmark marker0 - time00 69.347 - time01 72.142 - time02 80.444 - time03 87.608 - time04 90.185 - } - exit - { - nextfile kejimbase_atr01 - nextmark marker1 - time00 2.434 - time01 5.667 - time02 20.281 - } - exit - { - nextfile kejimbase_atr02 - nextmark marker2 - time00 62.894 - } - exit - { - nextfile kejimbase_atr03 - nextmark marker3 - time00 12.397 - time01 28.679 - time02 35.492 - time03 45.328 - } - } - ImpBaseB_Explore - { - entry - { - marker0 0 - marker1 37 - marker2 69.81 - marker3 119.97 - } - exit - { - nextfile ImpBaseB_Etr00 - time0 37 - time1 54.0 - time2 62.35 - time3 69.81 - time4 79.85 - time5 119.97 - time6 132.75 - time7 146.88 - } - exit - { - nextfile ImpBaseB_Etr01 - time0 13.67 - time1 26.96 - time2 89.42 - time3 96.92 - time4 107.77 - } - - } - ImpBaseB_Action - { - entry - { - marker0 0 - marker1 30.23 - marker2 45.45 - marker3 104.48 - } - exit - { - nextfile ImpBaseB_Atr00 - nextmark marker3 - time0 38.22 - time1 50.31 - time2 59.23 - time3 64.47 - time4 80.41 - time5 87.69 - time6 92.01 - time7 98.07 - time8 104.48 - } - exit - { - nextfile ImpBaseB_Atr01 - nextmark marker2 - time0 8.91 - time1 20.89 - } - exit - { - nextfile ImpBaseB_Atr02 - nextmark marker1 - time0 25.45 - time1 30.23 - } - exit - { - nextfile ImpBaseB_Atr03 - nextmark marker0 - time0 4.97 - time1 11.33 - time2 16.11 - time3 45.45 - time4 70.61 - time5 74.66 - } - } - ImpBaseC_explore - { - entry - { - marker0 0.000 - marker1 55.831 - marker2 11.160 - marker3 11.160 - - } - exit - { - nextfile ImpBaseC_etr00 - time00 42.904 - time01 71.172 - time02 127.721 - time03 150.290 - time04 171.618 - - } - exit - { - nextfile ImpBaseC_etr01 - time00 19.096 - time01 26.412 - time02 88.211 - time03 101.169 - - } - } - ImpBaseC_action - { - entry - { - marker0 0.00 - marker1 19.468 - marker2 33.480 - } - exit - { - nextfile ImpBaseC_atr00 - nextmark marker0 - time00 2.542 - time01 5.440 - time02 9.470 - time03 19.366 - } - exit - { - nextfile ImpBaseC_atr01 - nextmark marker1 - time00 25.544 - time01 29.140 - } - exit - { - nextfile ImpBaseC_atr02 - nextmark marker2 - time00 44.888 - } - exit - { - nextfile ImpBaseC_atr03 - nextmark marker3 - time00 34.272 - time01 65.224 - } - } - BespinA_Explore - { - entry - { - marker0 6.74 - marker1 17.51 - marker2 95.95 - marker3 149.87 - } - exit - { - nextfile BespinA_Etr00 - time00 42.01 - } - exit - { - nextfile BespinA_Etr01 - time00 165.65 - } - exit - { - nextfile BespinA_Etr02 - time00 17.51 - time01 29.50 - time02 54.80 - time03 70.21 - time04 90.25 - time05 106.40 - time06 120.53 - time07 149.87 - time08 178.36 - } - } - BespinA_Action - { - entry - { - marker0 0.00 - marker1 42.585 - marker2 87.664 - } - exit - { - nextfile BespinA_Atr00 - nextmark marker0 - time00 3.08 - time01 6.19 - time02 35.26 - } - exit - { - nextfile BespinA_Atr01 - nextmark marker1 - time00 8.98 - time01 14.76 - time02 20.65 - time03 44.34 - time04 47.28 - } - exit - { - nextfile BespinA_Atr02 - nextmark marker2 - time00 51.82 - time01 58.00 - } - exit - { - nextfile BespinA_Atr03 - nextmark marker3 - time00 28.90 - time01 64.78 - } - } - - besplat_explore - { - entry - { - marker0 46.78 - marker1 69.05 - marker2 85.85 - } - exit - { - nextfile besplat_etr00 - time0 6.89 - time1 21.26 - time3 55.52 - time4 69.75 - time5 85.87 - time6 98.12 - time7 119.44 - time8 131.95 - time9 157.58 - } - exit - { - nextfile besplat_etr01 - time0 34.15 - time1 141.84 - time2 174.72 - time3 183.05 - } - } - - besplat_action - { - entry - { - marker0 0 - marker1 16.56 - marker2 24.86 - marker3 62.37 - } - exit - { - nextfile besplat_atr00 - nextmark marker1 - time0 2.82 - time1 68.46 - } - exit - { - nextfile besplat_atr01 - nextmark marker2 - time0 41.23 - time1 54.20 - time2 59.31 - time3 76.51 - } - exit - { - nextfile besplat_atr02 - nextmark marker0 - time0 13.47 - time1 21.19 - time2 24.65 - time3 28.82 - time4 83.53 - } - } - - besplat_boss - { - } - - yavtrial_explore - { - entry - { - marker0 124.126 - marker1 102.326 - marker2 24.853 - marker3 0.00 - } - exit - { - nextfile yavtrial_etr00 - time00 3.095 - time01 23.931 - time02 24.937 - time03 46.905 - time04 61.268 - time05 79.042 - time06 101.750 - time07 118.136 - time08 147.951 - } - exit - { - nextfile yavtrial_etr01 - time00 161.783 - } - } - yavtrial_action - { - entry - { - marker0 0.00 - marker1 60.145 - marker2 85.922 - } - exit - { - nextfile yavtrial_atr00 - nextmark marker0 - time00 3.553 - time01 6.118 - time02 8.718 - time03 41.152 - time04 103.559 - } - exit - { - nextfile yavtrial_atr01 - nextmark marker1 - time00 9.307 - time01 12.310 - time02 16.719 - time03 20.045 - time04 26.229 - time05 35.160 - } - exit - { - nextfile yavtrial_atr02 - nextmark marker2 - time00 59.302 - time01 65.837 - time02 74.429 - } - exit - { - nextfile yavtrial_atr03 - nextmark marker3 - time00 38.325 - time01 49.857 - } - } - alienha_explore - { - entry - { - marker0 0 - marker1 65.84 - marker2 93.63 - } - exit - { - nextfile alienha_etr00 - time0 5.80 - time1 33.04 - time3 67.61 - } - exit - { - nextfile alienha_etr01 - time0 94.47 - time1 103.06 - time2 115.75 - time3 127.32 - time4 138.36 - time5 152.10 - time6 166.11 - time7 180.29 - } - } - alienha_action - { - entry - { - marker0 0 - marker1 40.96 - marker2 63.33 - } - exit - { - nextfile alienha_atr00 - nextmark marker2 - time0 3.35 - time1 6.85 - time3 15.70 - time4 22.61 - } - exit - { - nextfile alienha_atr01 - nextmark marker0 - time0 31.85 - time1 41.42 - time2 47.06 - } - exit - { - nextfile alienha_atr03 - nextmark marker1 - time0 53.01 - time1 58.57 - time2 62.87 - time3 72.03 - time4 89.64 - time5 96.94 - } - } - tunnels_explore - { - entry - { - marker0 0 - marker1 64.20 - marker2 96.47 - } - exit - { - nextfile tunnels_etr00 - time0 6.06 - time1 18.26 - time3 35.01 - time4 42.98 - time5 69.73 - time6 84.16 - } - exit - { - nextfile tunnels_etr01 - time0 94.81 - time1 111.25 - time2 121.77 - time3 134.68 - } - } - tunnels_action - { - entry - { - marker0 0 - marker1 22.39 - } - exit - { - nextfile tunnels_atr00 - nextmark marker0 - time0 0.29 - } - exit - { - nextfile tunnels_atr01 - nextmark marker2 - time0 15.62 - time1 22.94 - } - exit - { - nextfile tunnels_atr02 - nextmark marker2 - time0 29.14 - time1 35.63 - time2 45.33 - time3 51.78 - time4 58.67 - } - exit - { - nextfile tunnels_atr03 - nextmark marker1 - time0 64.81 - time1 68.53 - time2 72.28 - time3 75.89 - } - } - IMPBaseD_explore - { - entry - { - marker0 0.000 - marker1 66.790 - marker2 102.874 - marker3 150.554 - } - exit - { - nextfile IMPBaseD_etr00 - time00 7.997 - time01 16.678 - time02 44.664 - time03 70.836 - - } - exit - { - nextfile IMPBaseD_etr01 - time00 89.986 - time01 111.971 - time02 130.629 - time03 166.389 - time04 172.530 - } - } - IMPBaseD_action - { - entry - { - marker0 6.607 - marker1 60.118 - marker2 140.053 - } - exit - { - nextfile IMPBaseD_atr00 - nextmark marker0 - time00 6.457 - time01 13.265 - time02 18.757 - time03 25.194 - time04 152.772 - } - exit - { - nextfile IMPBaseD_atr01 - nextmark marker1 - time00 30.336 - time01 37.883 - time02 46.802 - time03 61.122 - } - exit - { - nextfile IMPBaseD_atr02 - nextmark marker2 - time00 78.257 - time01 85.312 - time02 92.170 - time03 140.866 - time04 149.597 - } - exit - { - nextfile IMPBaseD_atr03 - nextmark marker3 - time00 105.982 - time01 115.569 - time02 128.476 - } - } - swamp_explore - { - entry - { - marker0 0.000 - marker1 16.916 - marker2 80.714 - marker3 31.761 - - } - exit - { - nextfile swamp_etr00 - time00 11.185 - time01 20.989 - time02 51.408 - time03 63.196 - time04 71.293 - - } - exit - { - nextfile swamp_etr01 - time00 42.044 - time01 78.362 - time02 95.485 - time03 113.023 - - } - } - swamp_action - { - entry - { - marker0 0.00 - marker1 36.318 - marker2 45.982 - } - exit - { - nextfile swamp_atr00 - nextmark marker0 - time00 1.035 - } - exit - { - nextfile swamp_atr01 - nextmark marker1 - time00 6.835 - time01 11.323 - time02 18.592 - time03 36.230 - time04 59.793 - } - exit - { - nextfile swamp_atr02 - nextmark marker2 - time00 34.108 - time01 79.955 - time02 89.847 - time03 126.994 - - } - exit - { - nextfile swamp_atr03 - nextmark marker3 - time00 102.414 - time01 115.382 - time02 120.039 - } - } - yavtemp2_explore - { - entry - { - marker0 88.28 - marker1 48.10 - marker2 117.47 - marker3 0 - } - exit - { - nextfile yavtemp2_etr00 - time0 46.23 - time1 53.44 - time2 62.08 - time3 69.09 - time4 77.08 - time5 87.03 - time6 96.49 - } - exit - { - nextfile yavtemp2_etr01 - time0 15.24 - time1 31.79 - time2 42.78 - time3 114.18 - time4 134.25 - time5 144.89 - time6 160.03 - time7 168.03 - } - } - yavtemp2_action - { - entry - { - marker0 0 - marker1 17.35 - marker2 43.31 - marker3 60.39 - } - exit - { - nextfile yavtemp2_atr00 - nextmark marker0 - time0 0 - time1 8.78 - time2 13.47 - } - exit - { - nextfile yavtemp2_atr01 - nextmark marker1 - time0 23.89 - time1 28.62 - time2 41.11 - time3 46.73 - } - exit - { - nextfile yavtemp2_atr02 - nextmark marker2 - time0 48.95 - time1 60.59 - time2 69.13 - time3 97.63 - } - exit - { - nextfile yavtemp2_atr03 - nextmark marker3 - time0 91.11 - } - } - ImpBaseE_explore - { - entry - { - marker0 0.000 - marker1 13.805 - marker2 29.265 - marker3 137.915 - - } - exit - { - nextfile ImpBaseE_etr00 - time00 13.712 - time01 37.872 - time02 52.541 - time03 131.875 - time04 232.256 - - } - exit - { - nextfile ImpBaseE_etr01 - time00 120.057 - time01 157.080 - time02 176.388 - - } - } - ImpBaseE_action - { - entry - { - marker0 0.00 - marker1 18.336 - marker2 52.491 - } - exit - { - nextfile ImpBaseE_atr00 - nextmark marker0 - time00 22.012 - time01 32.861 - time02 73.853 - time03 77.808 - } - exit - { - nextfile ImpBaseE_atr01 - nextmark marker1 - time00 5.824 - time01 18.457 - time02 81.088 - time03 114.805 - } - exit - { - nextfile ImpBaseE_atr02 - nextmark marker2 - time00 1.923 - time01 26.892 - time02 30.703 - time03 59.294 - time04 65.331 - time05 88.803 - time06 91.876 - - } - exit - { - nextfile ImpBaseE_atr03 - nextmark marker3 - time00 11.958 - time01 43.027 - time02 48.014 - time04 96.757 - time05 107.858 - time06 130.437 - } - } - alienhb_explore - { - entry - { - marker0 127.71 - marker1 36.56 - marker2 89.94 - } - exit - { - nextfile alienhb_etr00 - time0 13.60 - time1 22.58 - time3 31.69 - time4 41.08 - time5 53.08 - time6 67.91 - time7 89.29 - time8 181.41 - } - exit - { - nextfile alienhb_etr01 - time0 109.08 - time1 123.18 - time2 134.76 - time3 149.54 - time4 160.53 - time5 174.10 - } - } - alienhb_action - { - entry - { - marker0 0 - marker1 6.85 - marker2 31.84 - marker3 66.77 - } - exit - { - nextfile alienhb_atr00 - nextmark marker0 - time0 5.29 - time1 21.54 - time3 26.21 - time4 120.30 - } - exit - { - nextfile alienhb_atr01 - nextmark marker1 - time0 10.35 - time1 17.03 - } - exit - { - nextfile alienhb_atr02 - nextmark marker2 - time0 60.27 - time1 70.21 - time2 80.36 - time3 93.55 - time4 100.47 - time5 111.75 - } - } - yavfinal_explore - { - entry - { - marker0 0.000 - marker1 18.664 - marker2 53.390 - marker3 97.161 - - } - exit - { - nextfile yavfinal_etr00 - time00 53.434 - time01 83.935 - - } - exit - { - nextfile yavfinal_etr01 - time00 9.852 - time01 94.171 - time02 104.106 - - } - } - yavfinal_action - { - entry - { - marker0 0.00 - marker1 43.094 - marker2 73.785 - } - exit - { - nextfile yavfinal_atr00 - nextmark marker0 - time00 53.502 - time01 61.386 - } - exit - { - nextfile yavfinal_atr01 - nextmark marker1 - time00 3.623 - time01 96.341 - } - exit - { - nextfile yavfinal_atr02 - nextmark marker2 - time00 9.019 - time01 11.707 - time02 15.535 - time03 29.107 - time04 35.847 - time05 75.327 - time06 101.992 - - } - exit - { - nextfile yavfinal_atr03 - nextmark marker3 - time00 21.257 - time01 25.167 - time02 43.800 - time03 106.249 - time04 113.638 - - } - } - yavfinal_boss - { - } - narshaada_explore - { - entry - { - marker0 123.18 - marker1 0 - marker2 43.30 - marker3 12.27 - } - exit - { - nextfile narshaada_etr00 - time0 55.86 - time1 70.17 - time2 78.63 - time3 88.61 - time4 106.57 - - } - exit - { - nextfile narshaada_etr01 - time0 13.75 - time1 31.29 - time2 43.84 - time4 123.91 - time5 134.92 - time6 149.29 - time7 168.16 - time8 184.19 - } - } - narshaada_action - { - entry - { - marker0 0 - marker1 15.65 - marker2 47.07 - } - exit - { - nextfile narshaada_atr00 - nextmark marker0 - time0 2.72 - time1 59.84 - time2 63.90 - time3 74.16 - time4 82.37 - } - exit - { - nextfile narshaada_atr01 - nextmark marker1 - time0 10.89 - time1 20.59 - time2 28.15 - time3 39.65 - time4 46.35 - } - exit - { - nextfile narshaada_atr02 - nextmark marker2 - time0 48.98 - time1 69.05 - } - exit - { - nextfile narshaada_atr03 - nextmark marker3 - time0 86.14 - time1 90.73 - time2 96.40 - time3 101.09 - } - } - -} -levelmusic -{ - kejim_post - { - explore ImpBaseB_Explore - action ImpBaseB_Action - } - kejim_base - { - explore kejimbase_explore - action kejimbase_action - } - artus_mine - { - explore tunnels_explore - action tunnels_action - } - artus_detention - { - explore ImpBaseC_explore - action ImpBaseC_action - useboss bespin_platform - } - artus_topside - { - explore ImpBaseD_explore - action ImpBaseD_action - } - ns_streets - { - explore narshaada_explore - action narshaada_action - } - ns_hideout - { - explore alienha_explore - action alienha_action - } - ns_starpad - { - explore alienhb_explore - action alienhb_action - } - bespin_undercity - { - uses artus_mine - } - bespin_streets - { - explore BespinA_Explore - action BespinA_Action - } - bespin_platform - { - explore besplat_explore - action besplat_action - boss besplat_boss - } - cairn_bay - { - explore ImpBaseE_explore - action ImpBaseE_action - } - cairn_dock1 - { - uses kejim_base - } - cairn_assembly - { - uses kejim_post - } - cairn_stockpile - { - uses artus_detention - } - cairn_reactor - { - uses artus_topside - } - cairn_dock2 - { - uses cairn_bay - } - doom_comm - { - uses kejim_base - } - doom_detention - { - uses kejim_post - } - doom_shields - { - uses artus_detention - } - yavin_swamp - { - explore swamp_explore - action swamp_action - } - yavin_canyon - { - uses ns_hideout - } - yavin_courtyard - { - explore yavtemp2_explore - action yavtemp2_action - } - yavin_final - { - explore yavfinal_explore - action yavfinal_action - boss yavfinal_boss - } - yavin_temple - { - explore placeholder - action placeholder - } - yavin_trial - { - explore yavtrial_explore - action yavtrial_action - } -} diff --git a/code/base/ext_data/items.dat b/code/base/ext_data/items.dat deleted file mode 100644 index 69d1ab1..0000000 --- a/code/base/ext_data/items.dat +++ /dev/null @@ -1,742 +0,0 @@ -// EXTERNAL ITEM DATA -// - -//Fields -//pickupsound STRING; DEFAULT = sound/weapons/w_pkup.wav -//itemname STRING; -//classname STRING; -//count INT; ammount of ammo or health given with item -//icon STRING; -//min VECTOR; item bounds min, DEFAULT = -16 -16 -2 -//max VECTOR; item bounds max, DEFAULT = 16 16 16 -//pickupname STRING; name to show in inventory -//tag ENUM; WP_, or AMMO_ -//type ENUM; IT_WEAPON, IT_AMMO, IT_ARMOR, IT_HEALTH -//worldmodel STRING; model to show on ground or in hand - -{ -itemname ITM_SABER_PICKUP - -classname weapon_saber -worldmodel models/weapons2/saber/saber_w.md3 -icon icons/w_icon_saber -// Amount of ammo given with weapon -count 50 -pickupname "Saber" -type IT_WEAPON -tag WP_SABER -min -16 -16 -8 -max 16 16 16 -} - - -{ -itemname ITM_BRYAR_PISTOL_PICKUP - -classname weapon_bryar_pistol -worldmodel models/weapons2/briar_pistol/briar_pistol_w.glm -icon icons/w_icon_rifle -// Amount of ammo given with weapon -count 50 -pickupname "Bryar Pistol" -type IT_WEAPON -tag WP_BRYAR_PISTOL -} - - -{ -itemname ITM_BLASTER_PICKUP - -classname weapon_blaster -worldmodel models/weapons2/blaster_r/blaster_w.glm -icon icons/w_icon_blaster -// Amount of ammo given with weapon -count 50 -pickupname "E11 Blaster Rifle" -type IT_WEAPON -tag WP_BLASTER -} - -{ -itemname ITM_DISRUPTOR_PICKUP - -classname weapon_disruptor -worldmodel models/weapons2/disruptor/disruptor_w.glm -icon icons/w_icon_disruptor -// Amount of ammo given with weapon -count 50 -pickupname "Tenloss Disruptor Rifle" -type IT_WEAPON -tag WP_DISRUPTOR -} - -{ -itemname ITM_BOWCASTER_PICKUP - -classname weapon_bowcaster -worldmodel models/weapons2/bowcaster/bowcaster_w.glm -icon icons/w_icon_bowcaster -// Amount of ammo given with weapon -count 50 -pickupname "Wookiee Bowcaster" -type IT_WEAPON -tag WP_BOWCASTER -} - -{ -itemname ITM_REPEATER_PICKUP - -classname weapon_repeater -worldmodel models/weapons2/heavy_repeater/heavy_repeater_w.glm -icon icons/w_icon_repeater -// Amount of ammo given with weapon -count 50 -pickupname "Imperial Heavy Repeater" -type IT_WEAPON -tag WP_REPEATER -} - -{ -itemname ITM_DEMP2_PICKUP - -classname weapon_demp2 -worldmodel models/weapons2/demp2/demp2_w.glm -icon icons/w_icon_demp2 -// Amount of ammo given with weapon -count 50 -pickupname "DEMP2" -type IT_WEAPON -tag WP_DEMP2 -} - - -{ -itemname ITM_FLECHETTE_PICKUP - -classname weapon_flechette -worldmodel models/weapons2/golan_arms/golan_arms_w.glm -icon icons/w_icon_flechette -// Amount of ammo given with weapon -count 50 -pickupname "Golan Arms Flechette" -type IT_WEAPON -tag WP_FLECHETTE -} - - -{ -itemname ITM_ROCKET_LAUNCHER_PICKUP - -classname weapon_rocket_launcher -worldmodel models/weapons2/merr_sonn/merr_sonn_w.glm -icon icons/w_icon_launcher -// Amount of ammo given with weapon -count 50 -pickupname "Merr-Sonn Missile System" -type IT_WEAPON -tag WP_ROCKET_LAUNCHER -} - - -{ -itemname ITM_THERMAL_DET_PICKUP - -classname weapon_thermal -worldmodel models/weapons2/thermal/thermal_w.glm -icon icons/w_icon_thermal -// Amount of ammo given with weapon -count 50 -pickupname "Thermal Detonator" -type IT_WEAPON -tag WP_THERMAL -} - -{ -itemname ITM_TRIP_MINE_PICKUP - -classname weapon_trip_mine -worldmodel models/weapons2/laser_trap/laser_trap_w.glm -icon icons/w_icon_trip_mine -// Amount of ammo given with weapon -count 50 -pickupname "Trip Mine" -type IT_WEAPON -tag WP_TRIP_MINE -} - -{ -itemname ITM_DET_PACK_PICKUP - -classname weapon_det_pack -worldmodel models/weapons2/detpack/det_pack_w.glm -icon icons/w_icon_det_pack -// Amount of ammo given with weapon -count 50 -pickupname "Det Pack" -type IT_WEAPON -tag WP_DET_PACK -} - -{ -itemname ITM_STUN_BATON_PICKUP - -classname weapon_stun_baton -worldmodel models/weapons2/stun_baton/stunbaton_w.glm -icon icons/w_icon_stun_baton -// Amount of ammo given with weapon -count 50 -pickupname "Stun Baton" -type IT_WEAPON -tag WP_STUN_BATON -} - - -{ -itemname ITM_BOT_LASER_PICKUP - -classname weapon_botwelder -worldmodel models/weapons2/noweap/noweap.md3 -icon icons/w_icon_phaser -// Amount of ammo given with weapon -count 400 -pickupname "Bot Laster" -type IT_WEAPON -tag WP_BOT_LASER -} - -{ -itemname ITM_MELEE - -classname weapon_melee -worldmodel models/weapons2/noweap/noweap.md3 -icon icons/w_icon_phaser -// Amount of ammo given with weapon -count 400 -pickupname "Melee" -type IT_WEAPON -tag WP_MELEE -min -16 -16 -2 -max 16 16 16 -} - -{ -itemname ITM_EMPLACED_GUN_PICKUP - -classname weapon_emplaced_gun -worldmodel models/weapons2/noweap/noweap.md3 -icon icons/w_icon_emp_gun -// Amount of ammo given with weapon -count 800 -pickupname "Emplaced Gun" -type IT_WEAPON -tag WP_EMPLACED_GUN -} - -{ -itemname ITM_TURRET_PICKUP - -classname weapon_turret -worldmodel models/weapons2/noweap/noweap.md3 -icon icons/w_icon_stasis -// Amount of ammo given with weapon -count 400 -pickupname "Turret Weapon" -type IT_WEAPON -tag WP_TURRET -} - -{ -itemname ITM_ATST_MAIN_PICKUP - -classname weapon_atst_main -worldmodel models/weapons2/noweap/noweap.md3 -icon icons/w_icon_atst -// Amount of ammo given with weapon -count 400 -pickupname "ATST Main" -type IT_WEAPON -tag WP_ATST_MAIN -} - -{ -itemname ITM_ATST_SIDE_PICKUP - -classname weapon_atst_side -worldmodel models/weapons2/noweap/noweap.md3 -icon icons/w_icon_atst -// Amount of ammo given with weapon -count 400 -pickupname "ATST Side" -type IT_WEAPON -tag WP_ATST_SIDE -} - -{ -itemname ITM_TIE_FIGHTER_PICKUP - -classname weapon_tie_fighter -worldmodel models/weapons2/noweap/noweap.md3 -icon icons/w_icon_tie -// Amount of ammo given with weapon -count 400 -pickupname "Tie Fighter" -type IT_WEAPON -tag WP_TIE_FIGHTER -} - -{ -itemname ITM_RAPID_FIRE_CONC_PICKUP - -classname weapon_rapid_concussion -worldmodel models/weapons2/noweap/noweap.md3 -icon icons/w_icon_rapid -// Amount of ammo given with weapon -count 400 -pickupname "Rapid Fire Concussion" -type IT_WEAPON -tag WP_RAPID_FIRE_CONC -} - -{ -itemname ITM_BLASTER_PISTOL_PICKUP - -classname weapon_blaster_pistol -worldmodel models/weapons2/imp_pistol/pistol_w.md3 -icon icons/w_icon_rifle -// Amount of ammo given with weapon -count 400 -pickupname "Blaster Pistol" -type IT_WEAPON -tag WP_BLASTER_PISTOL -} - -// -//Items -// - -// AMMO Items -//------------- -{ -itemname ITM_AMMO_FORCE_PICKUP - -classname ammo_force -worldmodel models/items/forcegem.md3 -icon icons/w_icon_blaster -count 100 -pickupname "Force??" -type IT_AMMO -tag AMMO_FORCE -max 8 8 16 -min -8 -8 -0 -} - -{ -itemname ITM_AMMO_BLASTER_PICKUP - -classname ammo_blaster -worldmodel models/items/energy_cell.md3 -icon icons/w_icon_blaster -count 100 -pickupname "Blaster Pack" -type IT_AMMO -tag AMMO_BLASTER -max 8 8 16 -min -8 -8 -0 -} - -{ -itemname ITM_AMMO_POWERCELL_PICKUP - -classname ammo_powercell -worldmodel models/items/power_cell.md3 -icon icons/w_icon_blaster -count 100 -pickupname "Power Cell" -type IT_AMMO -tag AMMO_POWERCELL -max 8 8 16 -min -8 -8 -0 -} - - -{ -itemname ITM_AMMO_METAL_BOLTS_PICKUP - -classname ammo_metallic_bolts -worldmodel models/items/metallic_bolts.md3 -icon icons/w_icon_blaster -count 100 -pickupname "Metallic Bolts" -type IT_AMMO -tag AMMO_METAL_BOLTS -max 8 8 16 -min -8 -8 -0 -} - -{ -itemname ITM_AMMO_ROCKETS_PICKUP - -classname ammo_rockets -worldmodel models/items/rockets.md3 -icon icons/w_icon_blaster -count 100 -pickupname "Rockets" -type IT_AMMO -tag AMMO_ROCKETS -max 8 8 16 -min -8 -8 -0 -} - -{ -itemname ITM_AMMO_EMPLACED_PICKUP - -classname ammo_emplaced -worldmodel models/mapobjects/scavenger/alien_ammo.md3 -icon icons/w_icon_blaster -count 100 -pickupname "Emplaced ammo" -type IT_AMMO -tag AMMO_EMPLACED -max 8 8 16 -min -8 -8 -0 -} - -{ -itemname ITM_AMMO_THERMAL_PICKUP - -classname ammo_thermal -worldmodel models/weapons2/thermal/thermal_pu.md3 -icon icons/w_icon_thermal -count 100 -pickupname "Thermal Detonator" -type IT_AMMO -tag AMMO_THERMAL -max 8 8 16 -min -8 -8 -0 -} - -{ -itemname ITM_AMMO_TRIPMINE_PICKUP - -classname ammo_tripmine -worldmodel models/weapons2/laser_trap/laser_trap_pu.md3 -icon icons/w_icon_lasertrap -count 100 -pickupname "Trip Mine" -type IT_AMMO -tag AMMO_TRIPMINE -max 8 8 16 -min -8 -8 -0 -} - -{ -itemname ITM_AMMO_DETPACK_PICKUP - -classname ammo_detpack -worldmodel models/weapons2/detpack/detpack_pu.md3 -icon icons/w_icon_detpack -count 100 -pickupname "Det Pack" -type IT_AMMO -tag AMMO_DETPACK -max 8 8 16 -min -8 -8 -0 -} - - - -{ -itemname ITM_BATTERY_PICKUP - -classname item_battery -worldmodel models/items/battery.md3 -icon icons/w_icon_blaster -count 1000 -pickupname "Batteries" -type IT_BATTERY -tag ITM_BATTERY_PICKUP -max 8 8 16 -min -8 -8 -0 -} - -{ -itemname ITM_SEEKER_PICKUP -classname item_seeker -worldmodel models/items/remote.md3 -icon gfx/hud/i_icon_seeker -count 120 -pickupname "Seeker Drone" -type IT_HOLDABLE -tag INV_SEEKER -max 8 8 16 -min -8 -8 -4 -} - -{ -itemname ITM_SHIELD_PICKUP -classname item_enviro -worldmodel models/items/shield.md3 -icon gfx/interface/inv_shield -count 100 -pickupname "Personal Shield" -type IT_HOLDABLE -tag ITM_SHIELD_PICKUP -max 8 8 16 -min -8 -8 -0 -} - -{ -itemname ITM_BACTA_PICKUP -classname item_bacta -worldmodel models/items/bacta.md3 -icon gfx/hud/i_icon_bacta -count 25 -pickupname "Medpac" -type IT_HOLDABLE -tag INV_BACTA_CANISTER -max 8 8 16 -min -8 -8 -0 -} - -{ -itemname ITM_DATAPAD_PICKUP -classname item_datapad -worldmodel models/items/datapad.md3 -icon icons/envirosuit -count 1 -pickupname "Datapad" -type IT_HOLDABLE -tag ITM_DATAPAD_PICKUP -max 8 8 16 -min -8 -8 -0 -} - -{ -itemname ITM_BINOCULARS_PICKUP -classname item_binoculars -worldmodel models/items/binoculars.md3 -icon gfx/hud/i_icon_zoom -count 1 -pickupname "Binoculars" -type IT_HOLDABLE -tag INV_ELECTROBINOCULARS -max 8 8 16 -min -8 -8 -0 -} - -{ -itemname ITM_SENTRY_GUN_PICKUP -classname item_sentry_gun -worldmodel models/items/psgun.glm -icon gfx/hud/i_icon_sentrygun -count 120 -pickupname "Sentry Gun" -type IT_HOLDABLE -tag INV_SENTRY -max 8 8 16 -min -8 -8 -0 -} - -{ -itemname ITM_LA_GOGGLES_PICKUP -classname item_la_goggles -worldmodel models/items/binoculars.md3 -icon gfx/hud/i_icon_goggles -count 30 -pickupname "LA Goggles" -type IT_HOLDABLE -tag INV_LIGHTAMP_GOGGLES -max 8 8 16 -min -8 -8 -0 -} - -{ -itemname ITM_MEDPAK_PICKUP -classname item_medpak_instant -worldmodel models/items/medpac.md3 -icon icons/w_icon_blaster -count 20 -pickupname "Medpack" -type IT_HEALTH -tag ITM_MEDPAK_PICKUP -max 8 8 16 -min -8 -8 -4 -} - -{ -itemname ITM_SHIELD_SM_PICKUP -classname item_shield_sm_instant -worldmodel models/items/psd_sm.md3 -icon icons/w_icon_blaster -count 25 -pickupname "Shield Small" -type IT_ARMOR -tag ITM_SHIELD_SM_PICKUP -max 8 8 16 -min -8 -8 -4 -} - -{ -itemname ITM_SHIELD_LRG_PICKUP -classname item_shield_lrg_instant -worldmodel models/items/psd.md3 -icon icons/w_icon_blaster -count 50 -pickupname "Shield Large" -type IT_ARMOR -tag ITM_SHIELD_LRG_PICKUP -max 8 8 16 -min -8 -8 -4 -} - - -{ -itemname ITM_GOODIE_KEY_PICKUP -classname item_goodie_key -worldmodel models/items/key.md3 -icon gfx/hud/i_icon_goodie_key -pickupname "Key" -type IT_HOLDABLE -tag INV_GOODIE_KEY -max 8 8 16 -min -8 -8 -0 -} - - -{ -itemname ITM_SECURITY_KEY_PICKUP -classname item_security_key -worldmodel models/items/key.md3 -icon gfx/hud/i_icon_security_key -pickupname "Security Key" -type IT_HOLDABLE -tag INV_SECURITY_KEY -max 8 8 16 -min -8 -8 -0 -} - -{ -itemname ITM_FORCE_HEAL_PICKUP - -classname holocron_force_heal -worldmodel models/map_objects/force_holocrons/heal.md3 -icon icons/f_icon_heal -count 1 -pickupname "Force Heal" -type IT_HOLOCRON -tag FP_HEAL -max 8 8 16 -min -8 -8 -0 -} - -{ -itemname ITM_FORCE_LEVITATION_PICKUP - -classname holocron_force_levitation -worldmodel models/map_objects/force_holocrons/jump.md3 -icon icons/f_icon_levitation -count 1 -pickupname "Force Levitation" -type IT_HOLOCRON -tag FP_LEVITATION -max 8 8 16 -min -8 -8 -0 -} - -{ -itemname ITM_FORCE_SPEED_PICKUP - -classname holocron_force_speed -worldmodel models/map_objects/force_holocrons/speed.md3 -icon icons/f_icon_speed -count 1 -pickupname "Force SPEED" -type IT_HOLOCRON -tag FP_SPEED -max 8 8 16 -min -8 -8 -0 -} - -{ -itemname ITM_FORCE_PUSH_PICKUP - -classname holocron_force_push -worldmodel models/map_objects/force_holocrons/push.md3 -icon icons/f_icon_push -count 1 -pickupname "Force Push" -type IT_HOLOCRON -tag FP_PUSH -max 8 8 16 -min -8 -8 -0 -} - -{ -itemname ITM_FORCE_PULL_PICKUP - -classname holocron_force_pull -worldmodel models/map_objects/force_holocrons/pull.md3 -icon icons/f_icon_pull -count 1 -pickupname "Force Pull" -type IT_HOLOCRON -tag FP_PULL -max 8 8 16 -min -8 -8 -0 -} - -{ -itemname ITM_FORCE_TELEPATHY_PICKUP - -classname holocron_force_telepathy -worldmodel models/map_objects/force_holocrons/telepathy.md3 -icon icons/f_icon_telepathy -count 1 -pickupname "Force Telepathy" -type IT_HOLOCRON -tag FP_TELEPATHY -max 8 8 16 -min -8 -8 -0 -} - -{ -itemname ITM_FORCE_GRIP_PICKUP - -classname holocron_force_grip -worldmodel models/map_objects/force_holocrons/grip.md3 -icon icons/f_icon_grip -count 1 -pickupname "Force Grip" -type IT_HOLOCRON -tag FP_GRIP -max 8 8 16 -min -8 -8 -0 -} - -{ -itemname ITM_FORCE_LIGHTNING_PICKUP - -classname holocron_force_lightning -worldmodel models/map_objects/force_holocrons/lightning.md3 -icon icons/f_icon_lightning -count 1 -pickupname "Force Lightning" -type IT_HOLOCRON -tag FP_LIGHTNING -max 8 8 16 -min -8 -8 -0 -} - -{ -itemname ITM_FORCE_SABERTHROW_PICKUP - -classname holocron_force_saberthrow -worldmodel models/map_objects/force_holocrons/saberthrow.md3 -icon icons/f_icon_saberthrow -count 1 -pickupname "Force SaberThrow" -type IT_HOLOCRON -tag FP_SABERTHROW -max 8 8 16 -min -8 -8 -0 -} diff --git a/code/base/ext_data/vssver.scc b/code/base/ext_data/vssver.scc deleted file mode 100644 index 1e832b6..0000000 Binary files a/code/base/ext_data/vssver.scc and /dev/null differ diff --git a/code/base/ext_data/weapons.dat b/code/base/ext_data/weapons.dat deleted file mode 100644 index fdd0f3c..0000000 --- a/code/base/ext_data/weapons.dat +++ /dev/null @@ -1,609 +0,0 @@ -// EXTERNAL WEAPON & AMMO DATA -// -// NOTE!!!!!!!!! Weapontype must start the block of weapon data. -// NOTE!!!!!!!!! Ammo must start the block of ammo data. -// -// Weapontype - weapon data is associated with which weapon (must be first) -// WP_NONE -// WP_PHASER -// WP_COMPRESSION_RIFLE -// WP_IMOD -// WP_SCAVENGER_RIFLE -// WP_STASIS -// WP_GRENADE_LAUNCHER, -// WP_TETRION_DISRUPTOR, -// WP_DREADNOUGHT, -// WP_QUANTUM_BURST, -// WP_BORG_WEAPON -// WP_BORG_TASER -// WP_BORG_ASSIMILATOR -// WP_BORG_DRILL -// WP_TRICORDER -// -// Weaponclass - weapon name -// Weaponmodel - weapon model used in game -// weaponicon - interface image -// Ammotype - type of power weapon needs to fire -// 0 - No power -// 1 - Star Fleet power -// 2 - Alien Crystal power -// 3 - Phaser power -// Ammolowcount - amount when "Low ammo" warning appears on screen -// Flashcolor - color generate by weapon flash (R,G,B) -// Firingsound - sound file used when firing -// altfiringsound - sound file used when alt-firing -// flashsound - sound file used by flash -// altflashsound - sound file used by an alt-fire flash -// stopsound - sound file used when a firing sound stops -// Firetime - amount of time between firings -// altfireTime - for alt fire -// Range - range of weapon -// energyPerShot - amount of energy used per shot -// altenergypershot- for alt fire -// barrelcount - number of barrels the model has (weaponname_b?.md3) -// missileModel - missile .md3 -// altmissileModel - alternate missile .md3 -// missileSound - played while flying -// altmissileSound - alternate missile launch sound -// missileLight - intensity of lightsource for missile - if 0.0 then none (float) -// altmissileLight - alternate missile light -// missileLightColor - color in three float style R, G, B (0.0 to 1.0) - NOTE - if you have a light, you MUST HAVE THESE -// altmissileLightColor - alternate color in three float style R, G, B (0.0 to 1.0) -// missileHitSound - played on impact -// altmissileHitSound - for alt fire -// missileFuncName - missile fly function -// altmissileFuncName - for alt fire -// -// FUNCTION NAMES -// borgfunc -// scavengerfunc -// altscavengerfunc -// stasisfunc -// grenadefunc -// altgrenadefunc -// tetrionfunc -// dreadnoughtfunc -// quantumfunc -// quantumaltfunc -// botrocketfunc -// forgeprojfunc -// forgeprojfunc2 -// forgepsychfunc -// parasiteacidfunc -// stasisattackfunc -// loaderlaserfunc -// botprojfunc - -// -// For AMMO Types -// ammoicon - STRING -// ammomax - INT - - -// WP_NULL -{ -WEAPONTYPE WP_NONE -} - -// WP_STUN_BATON -{ -weapontype WP_STUN_BATON -weaponclass weapon_stun_baton -weaponmodel models/weapons2/stun_baton/baton.md3 -weaponIcon gfx/hud/w_icon_stunbaton -firingsound sound/weapons/baton/fire.wav -barrelcount 3 -ammotype 1 -ammolowcount 5 -energypershot 0 -firetime 400 -range 8192 -altenergypershot 0 -altfiretime 800 -altrange 8192 -} - -// WP_SABER -{ -weapontype WP_SABER -weaponclass weapon_saber -weaponmodel models/weapons2/saber/saber_w.md3 -weaponIcon gfx/hud/w_icon_lightsaber -firingsound sound/weapons/saber/saberhum1.wav -ammotype 1 -ammolowcount 5 -energypershot 1 -firetime 100 -range 8192 -altenergypershot 3 -altfiretime 100 -altrange 8192 -missilemodel models/weapons2/saber/saber_w.md3 -} - - -// WP_BRYAR_PISTOL -{ -weapontype WP_BRYAR_PISTOL -weaponclass weapon_bryar_pistol -weaponmodel models/weapons2/briar_pistol/briar_pistol.md3 -weaponIcon gfx/hud/w_icon_briar -missileFuncName bryar_func -altmissileFuncName bryar_alt_func -ammotype 2 -ammolowcount 15 -energypershot 1 -firetime 400 -range 8192 -altenergypershot 1 -altfiretime 400 -altrange 8192 -muzzleEffect bryar/muzzle_flash -altmuzzleEffect bryar/altmuzzle_flash -altchargesound sound/weapons/bryar/altcharge.wav -selectSound sound/weapons/bryar/select.wav -} - -// WP_BLASTER -{ -weapontype WP_BLASTER -weaponclass weapon_blaster -weaponmodel models/weapons2/blaster_r/blaster.md3 -weaponIcon gfx/hud/w_icon_blaster -ammotype 2 -ammolowcount 5 -energypershot 1 -firetime 350 -range 8192 -altenergypershot 2 -altfiretime 150 -altrange 8192 -missileFuncName blaster_func -altmissileFuncName blaster_alt_func -muzzleEffect blaster/muzzle_flash -altmuzzleEffect blaster/altmuzzle_flash -selectSound sound/weapons/blaster/select.wav -} - -// WP_DISRUPTOR -{ -weapontype WP_DISRUPTOR -weaponclass weapon_disruptor -weaponmodel models/weapons2/disruptor/disruptor.md3 -weaponIcon gfx/hud/w_icon_disruptor -ammotype 3 -ammolowcount 5 -energypershot 4 -barrelcount 1 -firetime 600 -range 8192 -altenergypershot 1 -altfiretime 1300 -altrange 8192 -muzzleEffect disruptor/muzzle_flash -altmuzzleEffect disruptor/altmuzzle_flash -selectSound sound/weapons/disruptor/select.wav -altchargesound sound/weapons/disruptor/altCharge.wav -} - -// WP_BOWCASTER -{ -weapontype WP_BOWCASTER -weaponclass weapon_bowcaster -weaponmodel models/weapons2/bowcaster/bowcaster.md3 -weaponIcon gfx/hud/w_icon_bowcaster -altchargesound sound/weapons/bowcaster/altcharge.wav -ammotype 3 -ammolowcount 5 -energypershot 5 -firetime 750 -range 8192 -altenergypershot 5 -altfiretime 400 -altrange 8192 -missileFuncName bowcaster_func -altmissileFuncName bowcaster_func -muzzleEffect bowcaster/muzzle_flash -altmuzzleEffect bowcaster/altmuzzle_flash -selectSound sound/weapons/bowcaster/select.wav -chargesound sound/weapons/bowcaster/altcharge.wav -} - -// WP_REPEATER -{ -weapontype WP_REPEATER -weaponclass weapon_repeater -weaponmodel models/weapons2/heavy_repeater/heavy_repeater.md3 -weaponIcon gfx/hud/w_icon_repeater -ammotype 4 -ammolowcount 5 -energypershot 1 -firetime 50 -range 8192 -altenergypershot 8 -altfiretime 800 -altrange 8192 -barrelcount 1 -missileFuncName repeater_func -altmissileFuncName repeater_alt_func -muzzleEffect repeater/muzzle_flash -altmuzzleEffect repeater/altmuzzle_flash -selectSound sound/weapons/repeater/select.wav -} - -// WP_DEMP2 -{ -weapontype WP_DEMP2 -weaponclass weapon_demp2 -weaponmodel models/weapons2/demp2/demp2.md3 -weaponIcon gfx/hud/w_icon_demp2 -ammotype 3 -ammolowcount 5 -energypershot 8 -firetime 450 -range 8192 -altenergypershot 10 -altfiretime 1200 -altrange 8192 -missileFuncName demp2_func -muzzleEffect demp2/muzzle_flash -altmissileFuncName demp2_alt_func -altmuzzleEffect demp2/altmuzzle_flash -selectSound sound/weapons/demp2/select.wav -altchargesound sound/weapons/demp2/altCharge.wav -} - - -// WP_FLECHETTE -{ -weapontype WP_FLECHETTE -weaponclass weapon_flechette -weaponmodel models/weapons2/golan_arms/golan_arms.md3 -barrelcount 1 -ammotype 4 -ammolowcount 5 -firetime 550 -energypershot 8 -range 8192 -weaponIcon gfx/hud/w_icon_flechette -altenergypershot 8 -altfiretime 400 -altrange 8192 -missileFuncName flechette_func -altmissileFuncName flechette_alt_func -muzzleEffect flechette/muzzle_flash -altmuzzleEffect flechette/altmuzzle_flash -altmissileModel models/weapons2/golan_arms/projectile.md3 -selectSound sound/weapons/flechette/select.wav -} - -// WP_ROCKET_LAUNCHER -{ -weapontype WP_ROCKET_LAUNCHER -weaponclass weapon_rocket_launcher -weaponmodel models/weapons2/merr_sonn/merr_sonn.md3 -ammotype 5 -ammolowcount 5 -firetime 600 -energypershot 1 -range 8192 -weaponIcon gfx/hud/w_icon_merrsonn -barrelcount 1 -altenergypershot 1 -altfiretime 1000 -altrange 8192 -missileLight 125 -missileLightColor 1.0 1.0 0.5 -altmissileLight 125 -altmissileLightColor 1.0 1.0 0.5 -missileFuncName rocket_func -altmissileFuncName rocket_alt_func -muzzleEffect rocket/muzzle_flash2 -altmuzzleEffect rocket/altmuzzle_flash -missileModel models/weapons2/merr_sonn/projectile.md3 -altmissileModel models/weapons2/merr_sonn/projectile.md3 -missilesound sound/weapons/rocket/missileloop.wav -altmissilesound sound/weapons/rocket/missileloop.wav -selectSound sound/weapons/rocket/select.wav -} - - -// WP_THERMAL -{ -weapontype WP_THERMAL -weaponclass weapon_thermal -weaponmodel models/weapons2/thermal/thermal.md3 -weaponIcon gfx/hud/w_icon_thermal -ammotype 7 -ammolowcount 5 -energypershot 1 -firetime 800 -range 8192 -altenergypershot 1 -altfiretime 400 -altrange 8192 -missileModel models/weapons2/thermal/thermal_proj.md3 -altmissileModel models/weapons2/thermal/thermal_proj.md3 -barrelcount 0 -chargesound sound/weapons/thermal/charge.wav -altchargesound sound/weapons/thermal/charge.wav -selectSound sound/weapons/thermal/select.wav -muzzleEffect thermal/muzzle_flash -} - -// WP_TRIP_MINE -{ -weapontype WP_TRIP_MINE -weaponclass weapon_trip_mine -weaponmodel models/weapons2/laser_trap/laser_trap.md3 -weaponIcon gfx/hud/w_icon_tripmine -ammotype 8 -ammolowcount 5 -energypershot 1 -firetime 800 -range 8192 -altenergypershot 1 -altfiretime 400 -altrange 8192 -missileModel models/weapons2/laser_trap/laser_trap_w.glm -altmissileModel models/weapons2/laser_trap/laser_trap_w.glm -selectSound sound/weapons/tripmine/select.wav -muzzleEffect tripmine/muzzle_flash - -} - -// WP_DET_PACK -{ -weapontype WP_DET_PACK -weaponclass weapon_det_pack -weaponmodel models/weapons2/detpack/det_pack.md3 -weaponIcon gfx/hud/w_icon_detpack -ammotype 9 -ammolowcount 5 -energypershot 1 -firetime 800 -range 8192 -altenergypershot 0 -altfiretime 400 -altrange 8192 -missileModel models/weapons2/detpack/det_pack_proj.glm -selectSound sound/weapons/detpack/select.wav -muzzleEffect detpack/muzzle_flash -} - -// WP_EMPLACED_GUN -{ -weapontype WP_EMPLACED_GUN -weaponclass weapon_emplaced_gun -weaponmodel models/weapons2/noweap/noweap.md3 - -altenergypershot 1 -altrange 8192 -missileFuncName emplaced_func -altmissileFuncName emplaced_func -ammotype 6 -ammolowcount 15 -energypershot 1 -firetime 150 -altfiretime 150 -range 8192 -muzzleEffect emplaced/muzzle_flash -} - -// WP_BOT_LASER -{ -weapontype WP_BOT_LASER -weaponclass weapon_bryar_pistol -weaponmodel models/weapons2/noweap/noweap.md3 - -//flashsound sound/weapons/probe/fire.wav -//altflashsound sound/weapons/probe/alt_fire.wav -altenergypershot 0 -altrange 8192 -missileFuncName bryar_func -ammotype 1 -ammolowcount 15 -energypershot 2 -firetime 1600 -range 8192 -} - -// WP_MELEE -{ -weapontype WP_MELEE -weaponclass weapon_melee -weaponmodel models/weapons2/noweap/noweap.md3 - -ammotype 3 -ammolowcount 5 -energypershot 0 -firetime 1000 -range 1024 -} - -// WP_ATST_MAIN -{ -weapontype WP_ATST_MAIN -weaponclass weapon_atst_main -weaponmodel models/weapons2/noweap/noweap.md3 -weaponIcon gfx/hud/w_icon_atst -//flashsound sound/weapons/atst/ATSTfire1.wav -//altflashsound sound/weapons/atst/ATSTfire2.wav -altenergypershot 1 -altrange 8192 -missileFuncName atstmain_func -altmissileFuncName atstmain_func -ammotype 6 -ammolowcount 15 -energypershot 1 -firetime 400 -altfiretime 400 -range 8192 -muzzleEffect emplaced/muzzle_flash -} - -// WP_ATST_SIDE -{ -weapontype WP_ATST_SIDE -weaponclass weapon_atst_side -weaponmodel models/weapons2/noweap/noweap.md3 -weaponIcon gfx/hud/w_icon_atstside -//flashsound sound/weapons/atst/ATSTfire3.wav -//altflashsound sound/weapons/atst/ATSTfire4.wav -altenergypershot 1 -altrange 8192 - -altmissileModel models/weapons2/merr_sonn/projectile.md3 - -missileFuncName atst_side_main_func -altmissileFuncName atst_side_alt_func -muzzleEffect emplaced/muzzle_flash -altmuzzleEffect emplaced/muzzle_flash - -ammotype 6 -ammolowcount 15 -energypershot 1 -firetime 400 -altfiretime 1000 -range 8192 -} - -// WP_TIE_FIGHTER -{ -weapontype WP_TIE_FIGHTER -weaponclass weapon_tie_fighter -weaponmodel models/weapons2/noweap/noweap.md3 -weaponIcon icons/w_icon_tie -//flashsound sound/weapons/tie_fighter/tie_fire.wav -//altflashsound sound/weapons/tie_fighter/tie_fire2.wav -altenergypershot 1 -altrange 8192 -missileFuncName emplaced_func -altmissileFuncName emplaced_func -ammotype 6 -ammolowcount 15 -energypershot 1 -firetime 400 -altfiretime 400 -range 8192 -muzzleEffect emplaced/muzzle_flash -} - -// WP_RAPID_FIRE_CONC -{ -weapontype WP_RAPID_FIRE_CONC -weaponclass weapon_radid_concussion -weaponmodel models/weapons2/noweap/noweap.md3 -weaponIcon icons/w_icon_tie -//flashsound sound/weapons/rapid_conc/fire.wav -//altflashsound sound/weapons/rapid_conc/alt_fire.wav -altenergypershot 1 -altrange 8192 -missileFuncName emplaced_func -altmissileFuncName repeater_alt_func -ammotype 6 -ammolowcount 15 -energypershot 1 -firetime 400 -altfiretime 1000 -range 8192 -muzzleEffect emplaced/muzzle_flash -} - -// WP_BLASTER_PISTOL -{ -weapontype WP_BLASTER_PISTOL -weaponclass weapon_blaster_pistol -weaponmodel models/weapons2/imp_pistol/pistol.md3 - -//flashsound sound/weapons/npc_blaster/fire.wav -//altflashsound sound/weapons/npc_blaster/alt_fire.wav -missileFuncName bryar_func -altmissileFuncName bryar_alt_func -ammotype 2 -ammolowcount 15 -energypershot 2 -firetime 400 -range 8192 -altenergypershot 2 -altfiretime 400 -altrange 8192 -muzzleEffect bryar/muzzle_flash -} - -// WP_TURRET -{ -weapontype WP_TURRET -weaponclass weapon_turret -weaponmodel models/weapons2/noweap/noweap.md3 -weaponIcon icons/w_icon_turret -altenergypershot 1 -altrange 8192 -missileFuncName turret_func -ammotype 6 -ammolowcount 15 -energypershot 1 -firetime 400 -altfiretime 400 -range 8192 -muzzleEffect turret/muzzle_flash -} - -// AMMO_NONE -{ -AMMOTYPE AMMO_NONE -} - -// AMMO_FORCE -{ -AMMO AMMO_FORCE -AMMOMAX 100 -} - -// AMMO_BLASTER -{ -AMMO AMMO_BLASTER -AMMOMAX 300 -} - -// AMMO_POWERCELL -{ -AMMO AMMO_POWERCELL -AMMOMAX 300 -} - -// AMMO_METAL_BOLTS -{ -AMMO AMMO_METAL_BOLTS -AMMOMAX 400 -} - -// AMMO_ROCKETS -{ -AMMO AMMO_ROCKETS -AMMOMAX 10 -} - -// AMMO_EMPLACED -{ -AMMO AMMO_EMPLACED -AMMOMAX 800 -} - -// AMMO_THERMAL -{ -AMMO AMMO_THERMAL -AMMOMAX 10 -} - -// AMMO_TRIPMINE -{ -AMMO AMMO_TRIPMINE -AMMOMAX 5 -} - -// AMMO_DETPACK -{ -AMMO AMMO_DETPACK -AMMOMAX 5 -} \ No newline at end of file diff --git a/code/cgame/FX_ATSTMain.cpp b/code/cgame/FX_ATSTMain.cpp index d8cd3de..b60c493 100644 --- a/code/cgame/FX_ATSTMain.cpp +++ b/code/cgame/FX_ATSTMain.cpp @@ -17,9 +17,27 @@ void FX_ATSTMainProjectileThink( centity_t *cent, const struct weaponInfo_s *wea { vec3_t forward; - if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f ) + if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f ) { - forward[2] = 1.0f; + if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f ) + { + forward[2] = 1.0f; + } + } + + // hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly + int dif = cg.time - cent->gent->s.pos.trTime; + + if ( dif < 30 ) + { + if ( dif < 0 ) + { + dif = 0; + } + + float scale = ( dif / 30.0f ) * 0.95f + 0.05f; + + VectorScale( forward, scale, forward ); } theFxScheduler.PlayEffect( "atst/shot", cent->lerpOrigin, forward ); diff --git a/code/cgame/FX_Blaster.cpp b/code/cgame/FX_Blaster.cpp index da152ef..d35d676 100644 --- a/code/cgame/FX_Blaster.cpp +++ b/code/cgame/FX_Blaster.cpp @@ -17,9 +17,27 @@ void FX_BlasterProjectileThink( centity_t *cent, const struct weaponInfo_s *weap { vec3_t forward; - if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f ) + if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f ) { - forward[2] = 1.0f; + if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f ) + { + forward[2] = 1.0f; + } + } + + // hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly + int dif = cg.time - cent->gent->s.pos.trTime; + + if ( dif < 75 ) + { + if ( dif < 0 ) + { + dif = 0; + } + + float scale = ( dif / 75.0f ) * 0.95f + 0.05f; + + VectorScale( forward, scale, forward ); } if ( cent->gent && cent->gent->owner && cent->gent->owner->s.number > 0 ) diff --git a/code/cgame/FX_Bowcaster.cpp b/code/cgame/FX_Bowcaster.cpp index 4e8fd57..5f40aaa 100644 --- a/code/cgame/FX_Bowcaster.cpp +++ b/code/cgame/FX_Bowcaster.cpp @@ -25,6 +25,21 @@ void FX_BowcasterProjectileThink( centity_t *cent, const struct weaponInfo_s *we } } + // hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly + int dif = cg.time - cent->gent->s.pos.trTime; + + if ( dif < 75 ) + { + if ( dif < 0 ) + { + dif = 0; + } + + float scale = ( dif / 75.0f ) * 0.95f + 0.05f; + + VectorScale( forward, scale, forward ); + } + theFxScheduler.PlayEffect( cgs.effects.bowcasterShotEffect, cent->lerpOrigin, forward ); } diff --git a/code/cgame/FX_BryarPistol.cpp b/code/cgame/FX_BryarPistol.cpp index 11281b3..caa4c6b 100644 --- a/code/cgame/FX_BryarPistol.cpp +++ b/code/cgame/FX_BryarPistol.cpp @@ -20,9 +20,27 @@ void FX_BryarProjectileThink( centity_t *cent, const struct weaponInfo_s *weapo { vec3_t forward; - if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f ) + if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f ) { - forward[2] = 1.0f; + if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f ) + { + forward[2] = 1.0f; + } + } + + // hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly + int dif = cg.time - cent->gent->s.pos.trTime; + + if ( dif < 75 ) + { + if ( dif < 0 ) + { + dif = 0; + } + + float scale = ( dif / 75.0f ) * 0.95f + 0.05f; + + VectorScale( forward, scale, forward ); } if ( cent->gent && cent->gent->owner && cent->gent->owner->s.number > 0 ) @@ -69,9 +87,27 @@ void FX_BryarAltProjectileThink( centity_t *cent, const struct weaponInfo_s *we { vec3_t forward; - if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f ) + if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f ) { - forward[2] = 1.0f; + if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f ) + { + forward[2] = 1.0f; + } + } + + // hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly + int dif = cg.time - cent->gent->s.pos.trTime; + + if ( dif < 75 ) + { + if ( dif < 0 ) + { + dif = 0; + } + + float scale = ( dif / 75.0f ) * 0.95f + 0.05f; + + VectorScale( forward, scale, forward ); } // see if we have some sort of extra charge going on diff --git a/code/cgame/FX_Emplaced.cpp b/code/cgame/FX_Emplaced.cpp index 1de7075..a3f81eb 100644 --- a/code/cgame/FX_Emplaced.cpp +++ b/code/cgame/FX_Emplaced.cpp @@ -17,9 +17,27 @@ void FX_EmplacedProjectileThink( centity_t *cent, const struct weaponInfo_s *wea { vec3_t forward; - if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f ) + if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f ) { - forward[2] = 1.0f; + if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f ) + { + forward[2] = 1.0f; + } + } + + // hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly + int dif = cg.time - cent->gent->s.pos.trTime; + + if ( dif < 75 ) + { + if ( dif < 0 ) + { + dif = 0; + } + + float scale = ( dif / 75.0f ) * 0.95f + 0.05f; + + VectorScale( forward, scale, forward ); } if ( cent->gent && cent->gent->owner && cent->gent->owner->activator && cent->gent->owner->activator->s.number > 0 ) @@ -73,9 +91,27 @@ void FX_TurretProjectileThink( centity_t *cent, const struct weaponInfo_s *weapo { vec3_t forward; - if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f ) + if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f ) { - forward[2] = 1.0f; + if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f ) + { + forward[2] = 1.0f; + } + } + + // hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly + int dif = cg.time - cent->gent->s.pos.trTime; + + if ( dif < 75 ) + { + if ( dif < 0 ) + { + dif = 0; + } + + float scale = ( dif / 75.0f ) * 0.95f + 0.05f; + + VectorScale( forward, scale, forward ); } theFxScheduler.PlayEffect( "turret/shot", cent->lerpOrigin, forward ); diff --git a/code/cgame/FxScheduler.cpp b/code/cgame/FxScheduler.cpp index b45b9be..583808e 100644 --- a/code/cgame/FxScheduler.cpp +++ b/code/cgame/FxScheduler.cpp @@ -1157,31 +1157,41 @@ void CFxScheduler::AddScheduledEffects( void ) else { + mdxaBone_t boltMatrix; + + doesBoltExist=qfalse; // do we need to go and re-get the bolt matrix again? Since it takes time lets try and do it only once if (((*itr)->mModelNum != oldModelNum) || ((*itr)->mEntNum != oldEntNum) || ((*itr)->mBoltNum != oldBoltIndex)) { - mdxaBone_t boltMatrix; + if (cg_entities[(*itr)->mEntNum].gent->ghoul2.IsValid()) + { + if ((*itr)->mModelNum>=0&&(*itr)->mModelNummEntNum].gent->ghoul2.size()) + { + if (cg_entities[(*itr)->mEntNum].gent->ghoul2[(*itr)->mModelNum].mModelindex>=0) + { - // go away and get me the bolt position for this frame please - doesBoltExist = gi.G2API_GetBoltMatrix(cg_entities[(*itr)->mEntNum].gent->ghoul2, (*itr)->mModelNum, (*itr)->mBoltNum, &boltMatrix, cg_entities[(*itr)->mEntNum].lerpAngles, cg_entities[(*itr)->mEntNum].lerpOrigin, cg.time, cgs.model_draw, cg_entities[(*itr)->mEntNum].currentState.modelScale); + // go away and get me the bolt position for this frame please + doesBoltExist = gi.G2API_GetBoltMatrix(cg_entities[(*itr)->mEntNum].gent->ghoul2, (*itr)->mModelNum, (*itr)->mBoltNum, &boltMatrix, cg_entities[(*itr)->mEntNum].lerpAngles, cg_entities[(*itr)->mEntNum].lerpOrigin, cg.time, cgs.model_draw, cg_entities[(*itr)->mEntNum].currentState.modelScale); + // set up the axis and origin we need for the actual effect spawning + origin[0] = boltMatrix.matrix[0][3]; + origin[1] = boltMatrix.matrix[1][3]; + origin[2] = boltMatrix.matrix[2][3]; - // set up the axis and origin we need for the actual effect spawning - origin[0] = boltMatrix.matrix[0][3]; - origin[1] = boltMatrix.matrix[1][3]; - origin[2] = boltMatrix.matrix[2][3]; + axis[0][0] = boltMatrix.matrix[0][0]; + axis[0][1] = boltMatrix.matrix[1][0]; + axis[0][2] = boltMatrix.matrix[2][0]; - axis[0][0] = boltMatrix.matrix[0][0]; - axis[0][1] = boltMatrix.matrix[1][0]; - axis[0][2] = boltMatrix.matrix[2][0]; - - axis[1][0] = boltMatrix.matrix[0][1]; - axis[1][1] = boltMatrix.matrix[1][1]; - axis[1][2] = boltMatrix.matrix[2][1]; - - axis[2][0] = boltMatrix.matrix[0][2]; - axis[2][1] = boltMatrix.matrix[1][2]; - axis[2][2] = boltMatrix.matrix[2][2]; + axis[1][0] = boltMatrix.matrix[0][1]; + axis[1][1] = boltMatrix.matrix[1][1]; + axis[1][2] = boltMatrix.matrix[2][1]; + axis[2][0] = boltMatrix.matrix[0][2]; + axis[2][1] = boltMatrix.matrix[1][2]; + axis[2][2] = boltMatrix.matrix[2][2]; + } + } + } + oldModelNum = (*itr)->mModelNum; oldEntNum = (*itr)->mEntNum; oldBoltIndex = (*itr)->mBoltNum; @@ -1568,7 +1578,14 @@ void CFxScheduler::CreateEffect( CPrimitiveTemplate *fx, vec3_t origin, vec3_t a case Sound: //--------- - theFxHelper.PlaySound( org, ENTITYNUM_NONE, CHAN_AUTO, fx->mMediaHandles.GetHandle() ); + if ( fx->mSpawnFlags & FX_SND_LESS_ATTENUATION ) + { + theFxHelper.PlaySound( org, ENTITYNUM_NONE, CHAN_LESS_ATTEN, fx->mMediaHandles.GetHandle() ); + } + else + { + theFxHelper.PlaySound( org, ENTITYNUM_NONE, CHAN_AUTO, fx->mMediaHandles.GetHandle() ); + } break; //--------- diff --git a/code/cgame/FxScheduler.h b/code/cgame/FxScheduler.h index 0dd03df..ed20238 100644 --- a/code/cgame/FxScheduler.h +++ b/code/cgame/FxScheduler.h @@ -28,26 +28,30 @@ using namespace std; // These are spawn flags for primitiveTemplates //----------------------------------------------- -#define FX_ORG_ON_SPHERE 0x0001 // Pretty dang expensive, calculates a point on a sphere/ellipsoid -#define FX_AXIS_FROM_SPHERE 0x0002 // Can be used in conjunction with org_on_sphere to cause particles to move out +#define FX_ORG_ON_SPHERE 0x00001 // Pretty dang expensive, calculates a point on a sphere/ellipsoid +#define FX_AXIS_FROM_SPHERE 0x00002 // Can be used in conjunction with org_on_sphere to cause particles to move out // from the center of the sphere -#define FX_ORG_ON_CYLINDER 0x0004 // calculate point on cylinder/disk +#define FX_ORG_ON_CYLINDER 0x00004 // calculate point on cylinder/disk -#define FX_ORG2_FROM_TRACE 0x0010 -#define FX_TRACE_IMPACT_FX 0x0020 // if trace impacts, we should play one of the specified impact fx files -#define FX_ORG2_IS_OFFSET 0x0040 // template specified org2 should be the offset from a trace endpos or +#define FX_ORG2_FROM_TRACE 0x00010 +#define FX_TRACE_IMPACT_FX 0x00020 // if trace impacts, we should play one of the specified impact fx files +#define FX_ORG2_IS_OFFSET 0x00040 // template specified org2 should be the offset from a trace endpos or // passed in org2. You might use this to lend a random flair to the endpos. // Note: this is done pre-trace, so you may have to specify large numbers for this -#define FX_CHEAP_ORG_CALC 0x0100 // Origin is calculated relative to passed in axis unless this is on. -#define FX_CHEAP_ORG2_CALC 0x0200 // Origin2 is calculated relative to passed in axis unless this is on. -#define FX_VEL_IS_ABSOLUTE 0x0400 // Velocity isn't relative to passed in axis with this flag on. -#define FX_ACCEL_IS_ABSOLUTE 0x0800 // Acceleration isn't relative to passed in axis with this flag on. +#define FX_CHEAP_ORG_CALC 0x00100 // Origin is calculated relative to passed in axis unless this is on. +#define FX_CHEAP_ORG2_CALC 0x00200 // Origin2 is calculated relative to passed in axis unless this is on. +#define FX_VEL_IS_ABSOLUTE 0x00400 // Velocity isn't relative to passed in axis with this flag on. +#define FX_ACCEL_IS_ABSOLUTE 0x00800 // Acceleration isn't relative to passed in axis with this flag on. -#define FX_RAND_ROT_AROUND_FWD 0x1000 // Randomly rotates up and right around forward vector -#define FX_EVEN_DISTRIBUTION 0x2000 // When you have a delay, it normally picks a random time to play. When +#define FX_RAND_ROT_AROUND_FWD 0x01000 // Randomly rotates up and right around forward vector +#define FX_EVEN_DISTRIBUTION 0x02000 // When you have a delay, it normally picks a random time to play. When // this flag is on, it generates an even time distribution -#define FX_RGB_COMPONENT_INTERP 0x4000 // Picks a color on the line defined by RGB min & max, default is to pick color in cube defined by min & max +#define FX_RGB_COMPONENT_INTERP 0x04000 // Picks a color on the line defined by RGB min & max, default is to pick color in cube defined by min & max + +#define FX_AFFECTED_BY_WIND 0x10000 // we take into account our wind vector when we spawn in + +#define FX_SND_LESS_ATTENUATION 0x20000 // attenuate sounds less //----------------------------------------------------------------- // diff --git a/code/cgame/FxTemplate.cpp b/code/cgame/FxTemplate.cpp index 42215b4..d2201e2 100644 --- a/code/cgame/FxTemplate.cpp +++ b/code/cgame/FxTemplate.cpp @@ -815,6 +815,10 @@ bool CPrimitiveTemplate::ParseSpawnFlags( const char *val ) { mSpawnFlags |= FX_RGB_COMPONENT_INTERP; } + else if ( !stricmp( flag[i], "lessAttenuation" )) + { + mSpawnFlags |= FX_SND_LESS_ATTENUATION; + } else { // we have badness going on, but continue on in case there are any valid fields in here ok = false; diff --git a/code/cgame/FxUtil.cpp b/code/cgame/FxUtil.cpp index 32662f9..174ecff 100644 --- a/code/cgame/FxUtil.cpp +++ b/code/cgame/FxUtil.cpp @@ -1528,7 +1528,7 @@ void FX_AddSprite( vec3_t origin, vec3_t vel, vec3_t accel, rotation, 0, vec3_origin, vec3_origin, bounce, 0, 0, - life, shader, 0 ); + life, shader, flags ); } //--------------------------------------------------- diff --git a/code/cgame/animtable.h b/code/cgame/animtable.h index 9c85ec7..cde00d7 100644 --- a/code/cgame/animtable.h +++ b/code/cgame/animtable.h @@ -23,8 +23,6 @@ stringID_table_t animTable [MAX_ANIMATIONS+1] = ENUM2STRING(BOTH_DEATH12), //# ENUM2STRING(BOTH_DEATH13), //# ENUM2STRING(BOTH_DEATH14), //# - ENUM2STRING(BOTH_DEATH14_UNGRIP), //# Desann's end death (cin #35) - ENUM2STRING(BOTH_DEATH14_SITUP), //# Tavion sitting up after having been thrown (cin #23) ENUM2STRING(BOTH_DEATH15), //# ENUM2STRING(BOTH_DEATH16), //# ENUM2STRING(BOTH_DEATH17), //# @@ -124,6 +122,7 @@ stringID_table_t animTable [MAX_ANIMATIONS+1] = //# #sep ENUM2STRING(BOTH_ ATTACKS ENUM2STRING(BOTH_ATTACK1), //# Attack with stun baton ENUM2STRING(BOTH_ATTACK2), //# Attack with one-handed pistol + ENUM2STRING(BOTH_ATTACK2IDLE1), //# Idle with one-handed pistol ENUM2STRING(BOTH_ATTACK3), //# Attack with blaster rifle ENUM2STRING(BOTH_ATTACK4), //# Attack with disruptor ENUM2STRING(BOTH_ATTACK5), //# Attack with bow caster @@ -609,17 +608,17 @@ stringID_table_t animTable [MAX_ANIMATIONS+1] = ENUM2STRING(BOTH_CCWCIRCLELOCK), //# //# #sep ENUM2STRING(BOTH_ STANDING - ENUM2STRING(BOTH_STAND1), //# Standing idle), no weapon), hands down - ENUM2STRING(BOTH_STAND1_RANDOM1), //# Random standing idle - ENUM2STRING(BOTH_STAND1_RANDOM2), //# Random standing idle - ENUM2STRING(BOTH_STAND2), //# Standing idle with a weapon - ENUM2STRING(BOTH_STAND2_RANDOM1), //# Random standing idle - ENUM2STRING(BOTH_STAND2_RANDOM2), //# Random standing idle - ENUM2STRING(BOTH_STAND2_RANDOM3), //# Random standing idle - ENUM2STRING(BOTH_STAND2_RANDOM4), //# Random standing idle - ENUM2STRING(BOTH_STAND3), //# Standing hands behind back), at ease), etc. + ENUM2STRING(BOTH_STAND1), //# Standing idle, no weapon, hands down + ENUM2STRING(BOTH_STAND1IDLE1), //# Random standing idle + ENUM2STRING(BOTH_STAND2), //# Standing idle with a saber + ENUM2STRING(BOTH_STAND2IDLE1), //# Random standing idle + ENUM2STRING(BOTH_STAND2IDLE2), + ENUM2STRING(BOTH_STAND3), //# Standing idle with 2-handed weapon + ENUM2STRING(BOTH_STAND3IDLE1), //# Random standing idle ENUM2STRING(BOTH_STAND4), //# hands clasp behind back - ENUM2STRING(BOTH_STAND5), //# standing idle), no weapon), hand down), back straight + ENUM2STRING(BOTH_STAND4IDLE1), //# Random standing idle + ENUM2STRING(BOTH_STAND5), //# standing idle, no weapon, hand down, back straight + ENUM2STRING(BOTH_STAND5IDLE1), //# Random standing idle ENUM2STRING(BOTH_STAND6), //# one handed), gun at side), relaxed stand ENUM2STRING(BOTH_STAND7), //# both hands on hips (female) ENUM2STRING(BOTH_STAND8), //# both hands on hips (male) @@ -638,7 +637,11 @@ stringID_table_t animTable [MAX_ANIMATIONS+1] = ENUM2STRING(BOTH_STAND5_REELO), //# Reelo in his stand5 position (cin #18) ENUM2STRING(BOTH_STAND1TOSTAND5), //# Transition from stand1 to stand5 ENUM2STRING(BOTH_STAND5TOSTAND1), //# Transition from stand5 to stand1 + ENUM2STRING(BOTH_STAND5TOAIM), //# Transition of Kye aiming his gun at Desann (cin #9) + ENUM2STRING(BOTH_STAND5STARTLEDLOOKLEFT), //# Kyle turning to watch the bridge drop (cin #9) + ENUM2STRING(BOTH_STARTLEDLOOKLEFTTOSTAND5), //# Kyle returning to stand 5 from watching the bridge drop (cin #9) ENUM2STRING(BOTH_STAND5TOSTAND8), //# Transition from stand5 to stand8 + ENUM2STRING(BOTH_STAND7TOSTAND8), //# Tavion putting hands on back of chair (cin #11) ENUM2STRING(BOTH_STAND8TOSTAND5), //# Transition from stand8 to stand5 ENUM2STRING(BOTH_CONSOLE1START), //# typing at a console @@ -647,6 +650,8 @@ stringID_table_t animTable [MAX_ANIMATIONS+1] = ENUM2STRING(BOTH_CONSOLE2START), //# typing at a console with comm link in hand (cin #5) ENUM2STRING(BOTH_CONSOLE2), //# typing at a console with comm link in hand (cin #5) ENUM2STRING(BOTH_CONSOLE2STOP), //# typing at a console with comm link in hand (cin #5) + ENUM2STRING(BOTH_CONSOLE2HOLDCOMSTART), //# lean in to type at console while holding comm link in hand (cin #5) + ENUM2STRING(BOTH_CONSOLE2HOLDCOMSTOP), //# lean away after typing at console while holding comm link in hand (cin #5) ENUM2STRING(BOTH_GUARD_LOOKAROUND1), //# Cradling weapon and looking around ENUM2STRING(BOTH_GUARD_IDLE1), //# Cradling weapon and standing @@ -654,6 +659,7 @@ stringID_table_t animTable [MAX_ANIMATIONS+1] = ENUM2STRING(BOTH_GESTURE1), //# Generic gesture), non-specific ENUM2STRING(BOTH_GESTURE2), //# Generic gesture), non-specific ENUM2STRING(BOTH_GESTURE3), //# Generic gesture), non-specific + ENUM2STRING(BOTH_WALK1TALKCOMM1), //# Talking into coom link while walking ENUM2STRING(BOTH_TALK1), //# Generic talk anim ENUM2STRING(BOTH_TALKCOMM1START), //# Start talking into a comm link ENUM2STRING(BOTH_TALKCOMM1), //# Talking into a comm link @@ -696,6 +702,9 @@ stringID_table_t animTable [MAX_ANIMATIONS+1] = ENUM2STRING(BOTH_TALKGESTURE19START), //# Desann lifting his arm "Join me" (cin #34) ENUM2STRING(BOTH_TALKGESTURE19STOP), //# Desann lifting his arm "Join me" (cin #34) ENUM2STRING(BOTH_TALKGESTURE20START), //# Kyle lifting his arm "Join us" (cin #34) + ENUM2STRING(BOTH_TALKGESTURE21), //# generic talk gesture from stand3 + ENUM2STRING(BOTH_TALKGESTURE22), //# generic talk gesture from stand3 + ENUM2STRING(BOTH_TALKGESTURE23), //# generic talk gesture from stand3 ENUM2STRING(BOTH_PAUSE1START), //# Luke pauses to warn Kyle (cin #24) start ENUM2STRING(BOTH_PAUSE1STOP), //# Luke pauses to warn Kyle (cin #24) stop @@ -726,13 +735,6 @@ stringID_table_t animTable [MAX_ANIMATIONS+1] = ENUM2STRING(BOTH_EXAMINE3), //# Hold Lando looking around corner ENUM2STRING(BOTH_EXAMINE3STOP), //# End Lando looking around corner - ENUM2STRING(BOTH_THROW1START), //# Kyle thrown to the right - ENUM2STRING(BOTH_THROW1), //# Kyle thrown to the right - ENUM2STRING(BOTH_THROW1STOP), //# Kyle thrown to the right - ENUM2STRING(BOTH_THROW2START), //# Kyle thrown to the left - ENUM2STRING(BOTH_THROW2), //# Kyle thrown to the left - ENUM2STRING(BOTH_THROW3), //# Kyle thrown backwards in cin #9 - ENUM2STRING(BOTH_LEANLEFT2START), //# Start leaning left in chair ENUM2STRING(BOTH_LEANLEFT2STOP), //# Stop leaning left in chair ENUM2STRING(BOTH_LEANRIGHT3START), //# Start Lando leaning on wall @@ -797,6 +799,7 @@ stringID_table_t animTable [MAX_ANIMATIONS+1] = ENUM2STRING(BOTH_THREATEN1), //# Kyle threatening Bartender with lightsaber (cin #16) ENUM2STRING(BOTH_RADIO_ONOFF), //# Mech Galak turning on his suit radio (cin #32) ENUM2STRING(BOTH_TRIUMPHANT1START), //# Mech Galak raising his arms in victory (cin #32) + ENUM2STRING(BOTH_TRIUMPHANT1STARTGESTURE), //# Mech Galak raising his arms in victory (cin #32) ENUM2STRING(BOTH_TRIUMPHANT1STOP), //# Mech Galak lowering his arms in victory (cin #32) ENUM2STRING(BOTH_SABERTHROW1START), //# Desann throwing his light saber (cin #26) @@ -820,6 +823,8 @@ stringID_table_t animTable [MAX_ANIMATIONS+1] = ENUM2STRING(BOTH_SIT2TO3), //# Trans from sit2 to sit3? ENUM2STRING(BOTH_SIT2TOSTAND5), //# Transition from sit 2 to stand 5 + ENUM2STRING(BOTH_STAND5TOSIT2), //# Transition from stand 5 to sit 2 + ENUM2STRING(BOTH_SIT2TOSIT4), //# Trans from sit2 to sit4 (cin #12) Luke leaning back from lotus position. ENUM2STRING(BOTH_SIT3TO1), //# Trans from sit3 to sit1? ENUM2STRING(BOTH_SIT3TO2), //# Trans from sit3 to sit2? ENUM2STRING(BOTH_SIT3TOSTAND5), //# transition from sit 3 to stand 5 @@ -840,23 +845,22 @@ stringID_table_t animTable [MAX_ANIMATIONS+1] = ENUM2STRING(BOTH_UNCROUCH1), //# Transition from crouch to standing ENUM2STRING(BOTH_CROUCH2IDLE), //# crouch and resting on back righ heel), no weapon ENUM2STRING(BOTH_CROUCH2TOSTAND1), //# going from crouch2 to stand1 - ENUM2STRING(BOTH_UNCROUCH3), //# Desann uncrouching down to Kyle (cin 9) ENUM2STRING(BOTH_CROUCH3), //# Desann crouching down to Kyle (cin 9) + ENUM2STRING(BOTH_UNCROUCH3), //# Desann uncrouching down to Kyle (cin 9) + ENUM2STRING(BOTH_CROUCH4), //# Slower version of crouch1 for cinematics + ENUM2STRING(BOTH_UNCROUCH4), //# Slower version of uncrouch1 for cinematics ENUM2STRING(BOTH_GET_UP1), //# Get up from the ground), face down ENUM2STRING(BOTH_GET_UP2), //# Get up from the ground), face up - ENUM2STRING(BOTH_COCKPIT_CONSOLE1), //# work console1 while sitting in a cockpit. - ENUM2STRING(BOTH_COCKPIT_CONSOLE2), //# work console2 while sitting in a cockpit. ENUM2STRING(BOTH_COCKPIT_SIT), //# sit in a cockpit. ENUM2STRING(BOTH_GUNSIT1), //# sitting on an emplaced gun. + ENUM2STRING(BOTH_DEATH14_UNGRIP), //# Desann's end death (cin #35) + ENUM2STRING(BOTH_DEATH14_SITUP), //# Tavion sitting up after having been thrown (cin #23) ENUM2STRING(BOTH_KNEES1), //# Tavion on her knees ENUM2STRING(BOTH_KNEES2), //# Tavion on her knees looking down ENUM2STRING(BOTH_KNEES2TO1), //# Transition of KNEES2 to KNEES1 - ENUM2STRING(BOTH_STRUGGLE1START), //# Kyle struggling under crate - ENUM2STRING(BOTH_STRUGGLE1), //# Kyle struggling under crate - ENUM2STRING(BOTH_STRUGGLE1STOP), //# Kyle struggling under crate ENUM2STRING(BOTH_RUMMAGE1START), //# Kyle rummaging for crystal (cin 2) ENUM2STRING(BOTH_RUMMAGE1), //# Kyle rummaging for crystal (cin 2) @@ -886,10 +890,12 @@ stringID_table_t animTable [MAX_ANIMATIONS+1] = ENUM2STRING(BOTH_RUNSTRAFE_RIGHT1), //# Sidestep right), should loop ENUM2STRING(BOTH_TURN_LEFT1), //# Turn left), should loop ENUM2STRING(BOTH_TURN_RIGHT1), //# Turn right), should loop + ENUM2STRING(BOTH_TURNSTAND1), //# Turn from STAND1 position ENUM2STRING(BOTH_TURNSTAND2), //# Turn from STAND2 position ENUM2STRING(BOTH_TURNSTAND3), //# Turn from STAND3 position ENUM2STRING(BOTH_TURNSTAND4), //# Turn from STAND4 position ENUM2STRING(BOTH_TURNSTAND5), //# Turn from STAND5 position + ENUM2STRING(BOTH_TURNCROUCH1), //# Turn from CROUCH1 position ENUM2STRING(BOTH_RUNAWAY1), //# Running scared ENUM2STRING(BOTH_SWIM1), //# Swimming @@ -1029,6 +1035,7 @@ stringID_table_t animTable [MAX_ANIMATIONS+1] = ENUM2STRING(BOTH_POSE2),//# ENUM2STRING(BOTH_POSE3),//# ENUM2STRING(BOTH_POSE4),//# + ENUM2STRING(BOTH_POSE5),//# //# #sep BOTH_ MISC MOVEMENT ENUM2STRING(BOTH_HIT1), //# Kyle hit by crate in cin #9 @@ -1142,6 +1149,7 @@ stringID_table_t animTable [MAX_ANIMATIONS+1] = ENUM2STRING(BOTH_MINDTRICK1), //# Use off-hand to do mind trick ENUM2STRING(BOTH_MINDTRICK2), //# Use off-hand to do distraction ENUM2STRING(BOTH_FORCELIGHTNING), //# Use off-hand to do lightning + ENUM2STRING(BOTH_FORCELIGHTNING_START), //# Use off-hand to do lightning - start ENUM2STRING(BOTH_FORCELIGHTNING_HOLD), //# Use off-hand to do lightning - hold ENUM2STRING(BOTH_FORCELIGHTNING_RELEASE),//# Use off-hand to do lightning - release ENUM2STRING(BOTH_FORCEHEAL_START), //# Healing meditation pose start @@ -1149,13 +1157,49 @@ stringID_table_t animTable [MAX_ANIMATIONS+1] = ENUM2STRING(BOTH_FORCEHEAL_QUICK), //# Healing meditation gesture ENUM2STRING(BOTH_SABERPULL), //# Use off-hand to do force power. ENUM2STRING(BOTH_FORCEGRIP1), //# force-gripping (no anim?) - ENUM2STRING(BOTH_FORCEGRIP2), //# force-gripping (?) ENUM2STRING(BOTH_FORCEGRIP3), //# force-gripping (right-hand) ENUM2STRING(BOTH_FORCEGRIP_HOLD), //# Use off-hand to do grip - hold ENUM2STRING(BOTH_FORCEGRIP_RELEASE),//# Use off-hand to do grip - release ENUM2STRING(BOTH_TOSS1), //# throwing to left after force gripping ENUM2STRING(BOTH_TOSS2), //# throwing to right after force gripping + ENUM2STRING(BOTH_COCKPIT_TALKR1START), //# turn head from straight forward to looking full right + ENUM2STRING(BOTH_COCKPIT_TALKR1STARTTOMID), //# from TALKR1START to looking at hologram (cin #1) + ENUM2STRING(BOTH_COCKPIT_TALKR1MIDTOSTART), //# from looking at hologram to TALKR1START (cin #1) + ENUM2STRING(BOTH_COCKPIT_TALKR1STOP), //# return head to straight forward from BOTH_COCKPIT_TALKR1 + ENUM2STRING(BOTH_COCKPIT_TALKR1STOPTOMID), //# from TALKR1STOP to TALKR1MID + ENUM2STRING(BOTH_COCKPIT_TALKR1MIDTOSTOP), //# from looking at hologram to TALKR1STOP (cin #1) + ENUM2STRING(BOTH_COCKPIT_TALKR1), //# talk to right side + + ENUM2STRING(BOTH_COCKPIT_TALKL1START), //# turn head from straight forward to looking full left + ENUM2STRING(BOTH_COCKPIT_TALKL1STARTTOMID), //# from TALKL1START to looking at hologram (cin #1) + ENUM2STRING(BOTH_COCKPIT_TALKL1MIDTOSTART), //# from looking at hologram to TALKL1START (cin #1) + ENUM2STRING(BOTH_COCKPIT_TALKL1STOP), //# return head to straight forward from BOTH_COCKPIT_TALKL1 + ENUM2STRING(BOTH_COCKPIT_TALKL1STOPTOMID), //# from TALKL1STOP to TALKL1MID + ENUM2STRING(BOTH_COCKPIT_TALKL1MIDTOSTOP), //# from looking at hologram to TALKL1STOP (cin #1) + ENUM2STRING(BOTH_COCKPIT_TALKL1), //# talk to left side + + ENUM2STRING(BOTH_COCKPIT_CONSOLE1), //# type at controls + ENUM2STRING(BOTH_COCKPIT_CONSOLE2), //# type at controls + ENUM2STRING(BOTH_COCKPIT_CONSOLE2_PARTIAL), //# last part of console2 anim (cin #1) used by Jan + + ENUM2STRING(BOTH_COCKPIT_HEADNOD), //# nod head yes while sitting + ENUM2STRING(BOTH_COCKPIT_HEADSHAKE), //# shake head no while sitting + + ENUM2STRING(BOTH_COCKPIT_HEADTILTLSTART), //# start tilt head left while sitting + ENUM2STRING(BOTH_COCKPIT_HEADTILTLSTOP), //# stop tilt head left while sitting + ENUM2STRING(BOTH_COCKPIT_HEADTILTRSTART), //# start tilt head right while sitting + ENUM2STRING(BOTH_COCKPIT_HEADTILTRSTOP), //# stop tilt head right while sitting + + ENUM2STRING(BOTH_COCKPIT_TALKGESTURE7START), //# + ENUM2STRING(BOTH_COCKPIT_TALKGESTURE7STOP), //# + ENUM2STRING(BOTH_COCKPIT_TALKGESTURE8), //# + ENUM2STRING(BOTH_COCKPIT_TALKGESTURE11START), //# + ENUM2STRING(BOTH_COCKPIT_TALKGESTURE11STOP), //# + + ENUM2STRING(BOTH_COCKPIT_SLEEP6START), //# + ENUM2STRING(BOTH_COCKPIT_SLEEP6STOP), //# + //================================================= //ANIMS IN WHICH ONLY THE UPPER OBJECTS ARE IN MD3 //================================================= @@ -1197,11 +1241,9 @@ stringID_table_t animTable [MAX_ANIMATIONS+1] = //# #sep ENUM2STRING(TORSO_ MISC ENUM2STRING(TORSO_TALKR1START), //# begin turning head for ENUM2STRING(BOTH_ENUM2STRING(TORSO_TALKR - ENUM2STRING(TORSO_TALKR1HOLD), //# non-looping version of talk to right side ENUM2STRING(TORSO_TALKR1STOP), //# return head to straight forward from ENUM2STRING(BOTH_ENUM2STRING(TORSO_TALKL ENUM2STRING(TORSO_TALKR1), //# talk to right side ENUM2STRING(TORSO_TALKL1START), //# begin turning head for ENUM2STRING(BOTH_ENUM2STRING(TORSO_TALKL - ENUM2STRING(TORSO_TALKL1HOLD), //# non-looping version of talk to left side ENUM2STRING(TORSO_TALKL1STOP), //# return head to straight forward from ENUM2STRING(BOTH_ENUM2STRING(TORSO_TALKL ENUM2STRING(TORSO_TALKL1), //# talk to left side ENUM2STRING(TORSO_LOOKL1), //# looking left diff --git a/code/cgame/cg_camera.cpp b/code/cgame/cg_camera.cpp index 9e4382e..ccc764c 100644 --- a/code/cgame/cg_camera.cpp +++ b/code/cgame/cg_camera.cpp @@ -79,6 +79,12 @@ void CGCam_Enable( void ) VectorClear( g_entities[0].client->ps.velocity ); g_entities[0].contents = 0; + if ( cg.zoomMode ) + { + // need to shut off some form of zooming + cg.zoomMode = 0; + } + for ( int i = 0; i < NUM_FORCE_POWERS; i++ ) {//deactivate any active force powers g_entities[0].client->ps.forcePowerDuration[i] = 0; @@ -113,7 +119,7 @@ void CGCam_Disable( void ) if ( &g_entities[0] && g_entities[0].client ) { - g_entities[0].contents = MASK_PLAYERSOLID; + g_entities[0].contents = CONTENTS_BODY;//MASK_PLAYERSOLID; } gi.SendServerCommand( NULL, "cts"); @@ -719,10 +725,16 @@ void CGCam_FollowUpdate ( void ) if ( client_camera.followInitLerp ) {//Lerping + float frac = cg.frametime/100.0f * client_camera.followSpeed/100.f; for( i = 0; i < 3; i++ ) { - cameraAngles[i] = LerpAngle( client_camera.angles[i], cameraAngles[i], cg.frametime/100.0f * client_camera.followSpeed/100.f ); + cameraAngles[i] = AngleNormalize180( cameraAngles[i] ); + cameraAngles[i] = AngleNormalize180( client_camera.angles[i] + frac * AngleNormalize180(cameraAngles[i] - client_camera.angles[i]) ); + cameraAngles[i] = AngleNormalize180( cameraAngles[i] ); } +#ifdef _DEBUG + Com_Printf( "%s\n", vtos(cameraAngles) ); +#endif } else {//Snapping, should do this first time if follow_lerp_to_start_duration is zero @@ -1098,6 +1110,7 @@ void CGCam_Update( void ) CGCam_UpdateFade(); //Update shaking if there's any + //CGCam_UpdateSmooth( cg.refdef.vieworg, cg.refdefViewAngles ); CGCam_UpdateShake( cg.refdef.vieworg, cg.refdefViewAngles ); } @@ -1205,6 +1218,47 @@ void CGCam_UpdateShake( vec3_t origin, vec3_t angles ) VectorAdd( angles, moveDir, angles ); } +void CGCam_Smooth( float intensity, int duration ) +{ + client_camera.smooth_active=false; // means smooth_origin and angles are valid + if ( intensity>1.0f||intensity==0.0f||duration<1) + { + client_camera.info_state &= ~CAMERA_SMOOTHING; + return; + } + client_camera.info_state |= CAMERA_SMOOTHING; + client_camera.smooth_intensity = intensity; + client_camera.smooth_duration = duration; + client_camera.smooth_start = cg.time; +} + +void CGCam_UpdateSmooth( vec3_t origin, vec3_t angles ) +{ + if (!(client_camera.info_state&CAMERA_SMOOTHING)||cg.time > ( client_camera.smooth_start + client_camera.smooth_duration )) + { + client_camera.info_state &= ~CAMERA_SMOOTHING; + return; + } + if (!client_camera.smooth_active) + { + client_camera.smooth_active=true; + VectorCopy(origin,client_camera.smooth_origin); + return; + } + float factor=client_camera.smooth_intensity; + if (client_camera.smooth_duration>200&&cg.time > ( client_camera.smooth_start + client_camera.smooth_duration-100 )) + { + factor+=(1.0f-client_camera.smooth_intensity)* + (100.0f-(client_camera.smooth_start + client_camera.smooth_duration-cg.time))/100.0f; + } + int i; + for (i=0;i<3;i++) + { + client_camera.smooth_origin[i]*=(1.0f-factor); + client_camera.smooth_origin[i]+=factor*origin[i]; + origin[i]=client_camera.smooth_origin[i]; + } +} /* ------------------------- CGCam_StartRoff diff --git a/code/cgame/cg_camera.h b/code/cgame/cg_camera.h index ef1023a..7f7fc8a 100644 --- a/code/cgame/cg_camera.h +++ b/code/cgame/cg_camera.h @@ -19,6 +19,7 @@ #define CAMERA_FOLLOWING 0x00000020 #define CAMERA_TRACKING 0x00000040 #define CAMERA_ROFFING 0x00000080 +#define CAMERA_SMOOTHING 0x00000100 // NOTE!! This structure is now saved out as part of the load/save game, so tell me if you put any pointers or // other goofy crap in... -Ste @@ -92,6 +93,14 @@ typedef struct camera_s int shake_duration; int shake_start; + //Smooth information + float smooth_intensity; + int smooth_duration; + int smooth_start; + vec3_t smooth_origin; + bool smooth_active; // means smooth_origin and angles are valid + + // ROFF information char sRoff[MAX_QPATH]; // name of a cached roff int roff_frame; // current frame in the roff data @@ -133,4 +142,7 @@ void CGCam_Roll( float dest, float duration ); void CGCam_StartRoff( char *roff ); +void CGCam_Smooth( float intensity, int duration ); +void CGCam_UpdateSmooth( vec3_t origin, vec3_t angles ); + #endif //__CG_CAMERA__ \ No newline at end of file diff --git a/code/cgame/cg_consolecmds.cpp b/code/cgame/cg_consolecmds.cpp index c8d6034..0deb8a6 100644 --- a/code/cgame/cg_consolecmds.cpp +++ b/code/cgame/cg_consolecmds.cpp @@ -12,7 +12,6 @@ extern qboolean player_locked; extern void CMD_CGCam_Disable( void ); void CG_NextInventory_f( void ); void CG_PrevInventory_f( void ); -void CG_UseInventory_f( void ); void CG_NextForcePower_f( void ); void CG_PrevForcePower_f( void ); void CG_LoadHud_f( void ); diff --git a/code/cgame/cg_draw.cpp b/code/cgame/cg_draw.cpp index f74b6f0..286d30c 100644 --- a/code/cgame/cg_draw.cpp +++ b/code/cgame/cg_draw.cpp @@ -35,7 +35,6 @@ int infoStringCount; //=============================================================== - /* ================ CG_Draw3DModel @@ -326,6 +325,8 @@ static void CG_DrawAmmo(centity_t *cent,int x,int y) if ( cent->currentState.weapon == WP_SABER && cent->gent ) { + cgi_R_SetColor( colorTable[CT_WHITE] ); + // don't need to draw ammo, but we will draw the current saber style in this window switch ( cg.saberAnimLevelPending ) { @@ -368,11 +369,6 @@ static void CG_DrawAmmo(centity_t *cent,int x,int y) { numColor_i = CT_LTGREY; } - // Overcharged? - else if ( cent->gent->s.powerups & ( 1 << PW_WEAPON_OVERCHARGE ) ) - { - numColor_i = CT_WHITE; - } else { if ( value > 0 ) @@ -758,6 +754,32 @@ void CG_DrawDataPadHUD( centity_t *cent ) x = 526; + if ((cg_updatedDataPadForcePower.integer) || (cg_updatedDataPadObjective.integer)) + { + // Stop flashing light + cg.missionInfoFlashTime = 0; + missionInfo_Updated = qfalse; + + // Set which force power to show. + // cg_updatedDataPadForcePower is set from Q3_Interface, because force powers would only be given + // from a script. + cg.DataPadforcepowerSelect = cg_updatedDataPadForcePower.integer - 1; // Not pretty, I know + if (cg.DataPadforcepowerSelect > MAX_SHOWPOWERS) + { //duh + cg.DataPadforcepowerSelect = MAX_SHOWPOWERS; + } + else if (cg.DataPadforcepowerSelect<0) + { + cg.DataPadforcepowerSelect=0; + } + + cgi_Cvar_Set( "cg_updatedDataPadForcePower", "0" ); + cg_updatedDataPadForcePower.integer = 0; //don't wait for the cvar-refresh. + + cgi_Cvar_Set( "cg_updatedDataPadObjective", "0" ); + cg_updatedDataPadObjective.integer = 0; //don't wait for the cvar-refresh. + } + CG_DrawHUDRightFrame1(x,y); CG_DrawForcePower(cent,x,y); CG_DrawAmmo(cent,x,y); @@ -1065,7 +1087,7 @@ static void CG_DrawZoomMask( void ) // Black out the area behind the battery display cgi_R_SetColor( colorTable[CT_DKGREY]); - CG_DrawPic( 236, 373, 163, 16, cgs.media.whiteShader ); + CG_DrawPic( 236, 357, 163, 16, cgs.media.whiteShader ); if ( power ) { @@ -1076,7 +1098,7 @@ static void CG_DrawZoomMask( void ) color1[3] = 0.4f; cgi_R_SetColor( color1 ); - CG_DrawPic( 247.0f, 378.5f, charge * 143.0f, 6, cgs.media.whiteShader ); + CG_DrawPic( 247.0f, 362.5f, charge * 143.0f, 6, cgs.media.whiteShader ); // pulsing dot bit color1[0] = sin( cg.time * 0.01f ) * 0.5f + 0.5f; @@ -1245,8 +1267,8 @@ static void CG_DrawCrosshair( vec3_t worldPoint ) if ( cg_crosshairForceHint.integer == 1 ) { // force-affectable targets are blue..do subtle for level 1 hint - ecolor[0] = 0.55f; - ecolor[1] = 0.8f; + ecolor[0] = 0.4f; + ecolor[1] = 0.7f; ecolor[2] = 1.0f; } else @@ -1258,7 +1280,7 @@ static void CG_DrawCrosshair( vec3_t worldPoint ) } corona = qtrue; } - else + else if ( cg_crosshairIdentifyTarget.integer ) { gentity_t *crossEnt = &g_entities[g_crosshairEntNum]; @@ -1288,8 +1310,8 @@ static void CG_DrawCrosshair( vec3_t worldPoint ) { //Enemies are red ecolor[0] = 1.0f;//R - ecolor[1] = 0.0f;//G - ecolor[2] = 0.0f;//B + ecolor[1] = 0.1f;//G + ecolor[2] = 0.1f;//B } } else if ( crossEnt->s.weapon == WP_TURRET && (crossEnt->svFlags&SVF_NONNPC_ENEMY) ) @@ -1330,6 +1352,10 @@ static void CG_DrawCrosshair( vec3_t worldPoint ) } } } + else // cg_crosshairIdentifyTarget is not on, so make it white + { + ecolor[0] = ecolor[1] = ecolor[2] = 1.0f; + } ecolor[3] = 1.0; cgi_R_SetColor( ecolor ); @@ -1418,15 +1444,18 @@ static void CG_DrawCrosshair( vec3_t worldPoint ) y = cg_crosshairY.integer; } - if ( cg.snap->ps.viewEntity > 0 && cg.snap->ps.viewEntity < ENTITYNUM_WORLD && - !Q_stricmp( "misc_panel_turret", g_entities[cg.snap->ps.viewEntity].classname )) + if ( cg.snap->ps.viewEntity > 0 && cg.snap->ps.viewEntity < ENTITYNUM_WORLD ) { - // draws a custom crosshair that is twice as large as normal - cgi_R_DrawStretchPic( x + cg.refdef.x + 320 - w, - y + cg.refdef.y + 240 - h, - w * 2, h * 2, 0, 0, 1, 1, cgs.media.turretCrossHairShader ); + if ( !Q_stricmp( "misc_panel_turret", g_entities[cg.snap->ps.viewEntity].classname )) + { + // draws a custom crosshair that is twice as large as normal + cgi_R_DrawStretchPic( x + cg.refdef.x + 320 - w, + y + cg.refdef.y + 240 - h, + w * 2, h * 2, 0, 0, 1, 1, cgs.media.turretCrossHairShader ); + + } } - else + else { hShader = cgs.media.crosshairShader[ cg_drawCrosshair.integer % NUM_CROSSHAIRS ]; @@ -1438,13 +1467,17 @@ static void CG_DrawCrosshair( vec3_t worldPoint ) if ( cg.forceCrosshairStartTime && cg_crosshairForceHint.integer > 1 ) // drawing extra bits { ecolor[0] = ecolor[1] = ecolor[2] = 1.0f; - ecolor[3] = (1 - ecolor[3]) * 0.25f; - float sc = 1.0f + sin( cg.time * 0.0005f ) * 0.4f; + ecolor[3] = (1 - ecolor[3]) * ( sin( cg.time * 0.001f ) * 0.05f + 0.2f ); cgi_R_SetColor( ecolor ); - cgi_R_DrawStretchPic( x + cg.refdef.x + 0.5 * (640 - w * 2.5f * (sc+1)), - y + cg.refdef.y + 0.5 * (480 - h * 2), w * 2.5f * (sc+1), h * 2, 0, 0, 1, 1, cgs.media.forceCoronaShader ); + w *= 2.5f; + h *= 1.6f; + + cgi_R_DrawStretchPic( x + cg.refdef.x + 0.5f * ( 640 - w ), y + cg.refdef.y + 0.5f * ( 480 - h ), + w, h, + 0, 0, 1, 1, + cgs.media.forceCoronaShader ); } } @@ -2268,6 +2301,18 @@ static void CG_Draw2D( void ) { if (cg.predicted_player_state.pm_type != PM_DEAD) { + // Was a objective given? + if ((cg_updatedDataPadForcePower.integer) || (cg_updatedDataPadObjective.integer)) + { + // How long has the game been running? If within 15 seconds of starting, throw up the datapad. + if (cg.dataPadLevelStartTime>cg.time) + { + // Make it pop up + cgi_SendConsoleCommand( "datapad" ); + cg.dataPadLevelStartTime=cg.time; //and don't do it again this level! + } + } + if (!cg.missionInfoFlashTime) { cg.missionInfoFlashTime = cg.time + cg_missionInfoFlashTime.integer; diff --git a/code/cgame/cg_effects.cpp b/code/cgame/cg_effects.cpp index 9723360..3e54af0 100644 --- a/code/cgame/cg_effects.cpp +++ b/code/cgame/cg_effects.cpp @@ -402,7 +402,7 @@ void CG_Chunks( int owner, vec3_t origin, const vec3_t normal, const vec3_t mins return; break; case MAT_GRATE1: - cgi_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.chunkSound ); // own sound? + cgi_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.grateSound ); return; break; case MAT_ELECTRICAL:// (sparks) diff --git a/code/cgame/cg_ents.cpp b/code/cgame/cg_ents.cpp index d7e6ca3..0e035f1 100644 --- a/code/cgame/cg_ents.cpp +++ b/code/cgame/cg_ents.cpp @@ -153,76 +153,35 @@ static void CG_EntityEffects( centity_t *cent ) { void CG_AddRefEntWithTransportEffect ( centity_t *cent, refEntity_t *ent ) { - //NOTE: This WILL NOT work on BMODELS!!! BMODELS cannot take custom shaders - if ( cent->currentState.solid != SOLID_BMODEL && ((cent->gent->s.eFlags & EF_BEAM_IN) || (cent->gent->s.eFlags & EF_BEAM_OUT) || (cent->gent->s.eFlags & EF_DISINTEGRATION)) ) + // We are a normal thing.... + cgi_R_AddRefEntityToScene (ent); + + if ( ent->renderfx & RF_PULSATE && cent->gent->owner && cent->gent->owner->health && + !cent->gent->owner->s.number && cent->gent->owner->client && //only for player + cent->gent->owner->client->ps.saberEntityState == SES_RETURNING && + cent->currentState.saberActive == qfalse ) { - //---------- - // FIXME: I think this whole section can just go away?....since beaming things in or out isn't very Star Warzy - //---------- - if ( cent->gent->s.eFlags & EF_BEAM_OUT && cent->gent->delay > cg.time + 2000 ) - { - cgi_R_AddRefEntityToScene (ent); - } - else if ( cent->gent->s.eFlags & EF_BEAM_IN && cent->gent->delay > 0 && cent->gent->delay < cg.time + 2000 ) - { - cgi_R_AddRefEntityToScene (ent); - } + // if we are the saber and we have been dropped, do a glow so it can be spotted easier + float wv; + vec3_t org; - // Clear the flags if necessary - if ( cent->gent->s.eFlags & EF_BEAM_IN && cent->gent->delay < cg.time ) - { - cent->gent->s.eFlags &= ~EF_BEAM_IN; - } - - if ( cent->gent->s.eFlags & EF_BEAM_OUT && cent->gent->delay < cg.time ) - { - cent->gent->s.eFlags &= ~EF_BEAM_OUT; - } - - // We are beaming in or out, so it's ok to add in the beaming effect - if ( cent->gent->s.eFlags & EF_DISINTEGRATION ) - { - ent->customShader = cgs.media.disruptorShader; - } - else - { -// ent->customShader = cgs.media.transportShader; - } - ent->shaderTime = cent->gent->fx_time / 1000.0f; - cgi_R_AddRefEntityToScene( ent ); - } - else - { - // We are a normal thing.... + ent->customShader = cgi_R_RegisterShader( "gfx/effects/solidWhite_cull" ); + ent->renderfx = RF_RGB_TINT; + wv = sin( cg.time * 0.003f ) * 0.08f + 0.1f; + ent->shaderRGBA[0] = wv * 255; + ent->shaderRGBA[1] = wv * 255; + ent->shaderRGBA[2] = wv * 0; cgi_R_AddRefEntityToScene (ent); - if ( ent->renderfx & RF_PULSATE && cent->gent->owner && cent->gent->owner->health && - !cent->gent->owner->s.number && cent->gent->owner->client && //only for player - cent->gent->owner->client->ps.saberEntityState == SES_RETURNING && - cent->currentState.saberActive == qfalse ) + for ( int i = -4; i < 10; i += 1 ) { - // if we are the saber and we have been dropped, do a glow so it can be spotted easier - float wv; - vec3_t org; + VectorMA( ent->origin, -i, ent->axis[2], org ); - ent->customShader = cgi_R_RegisterShader( "gfx/effects/solidWhite_cull" ); - ent->renderfx = RF_RGB_TINT; - wv = sin( cg.time * 0.003f ) * 0.08f + 0.1f; - ent->shaderRGBA[0] = wv * 255; - ent->shaderRGBA[1] = wv * 255; - ent->shaderRGBA[2] = wv * 0; - cgi_R_AddRefEntityToScene (ent); - - for ( int i = -4; i < 10; i += 1 ) - { - VectorMA( ent->origin, -i, ent->axis[2], org ); - - FX_AddSprite( org, NULL, NULL, 5.5f, 5.5f, wv, wv, 0.0f, 0.0f, 1.0f, cgs.media.yellowDroppedSaberShader, 0x08000000 ); - } - if ( cent->gent->owner->s.weapon == WP_SABER ) - {//he's still controlling me - FX_AddSprite( cent->gent->owner->client->renderInfo.handRPoint, NULL, NULL, 8.0f, 8.0f, wv, wv, 0.0f, 0.0f, 1.0f, cgs.media.yellowDroppedSaberShader, 0x08000000 ); - } + FX_AddSprite( org, NULL, NULL, 5.5f, 5.5f, wv, wv, 0.0f, 0.0f, 1.0f, cgs.media.yellowDroppedSaberShader, 0x08000000 ); + } + if ( cent->gent->owner->s.weapon == WP_SABER ) + {//he's still controlling me + FX_AddSprite( cent->gent->owner->client->renderInfo.handRPoint, NULL, NULL, 8.0f, 8.0f, wv, wv, 0.0f, 0.0f, 1.0f, cgs.media.yellowDroppedSaberShader, 0x08000000 ); } } } @@ -510,8 +469,10 @@ const weaponData_t *wData = NULL; if ( !( cc->currentState.eFlags & EF_FIRING ) && !( cc->currentState.eFlags & EF_ALT_FIRING )) { // not animating..pausing was leaving the barrels in a bad state - gi.G2API_SetBoneAnimIndex( ¢->gent->ghoul2[cent->gent->playerModel], cent->gent->rootBone, - 0, 0, BONE_ANIM_OVERRIDE, 1.0f, cg.time ); + gi.G2API_PauseBoneAnim( ¢->gent->ghoul2[cent->gent->playerModel], "model_root", cg.time ); + +// gi.G2API_SetBoneAnimIndex( ¢->gent->ghoul2[cent->gent->playerModel], cent->gent->rootBone, +// 0, 0, BONE_ANIM_OVERRIDE, 1.0f, cg.time ); } // get alternating muzzle end bolts @@ -589,11 +550,15 @@ const weaponData_t *wData = NULL; { if ( cent->gent->owner->client->ps.saberLength > 0 ) { - CG_AddSaberBlade( &cg_entities[cent->gent->owner->s.number], &cg_entities[cent->gent->s.number], NULL, ent.renderfx, cent->gent->weaponModel, cent->lerpOrigin, cent->lerpAngles ); + CG_AddSaberBlade( &cg_entities[cent->gent->owner->s.number], + &cg_entities[cent->gent->s.number], NULL, ent.renderfx, + cent->gent->weaponModel, cent->lerpOrigin, cent->lerpAngles ); } else if ( cent->gent->owner->client->ps.saberEventFlags & SEF_INWATER ) { - CG_CheckSaberInWater( &cg_entities[cent->gent->owner->s.number], &cg_entities[cent->gent->s.number], cent->gent->weaponModel, cent->lerpOrigin, cent->lerpAngles ); + CG_CheckSaberInWater( &cg_entities[cent->gent->owner->s.number], + &cg_entities[cent->gent->s.number], cent->gent->weaponModel, + cent->lerpOrigin, cent->lerpAngles ); } } } @@ -610,7 +575,22 @@ const weaponData_t *wData = NULL; } else { - cgi_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgi_S_RegisterSound("sound/weapons/saber/saberspin.wav") ); + int spinSound; + switch ( cent->gent->owner->client->ps.forcePowerLevel[FP_SABERTHROW] ) + { + case FORCE_LEVEL_1: + spinSound = cgi_S_RegisterSound( "sound/weapons/saber/saberspin3.wav" ); + break; + case FORCE_LEVEL_2: + spinSound = cgi_S_RegisterSound( "sound/weapons/saber/saberspin2.wav" ); + break; + default: + case FORCE_LEVEL_3: + spinSound = cgi_S_RegisterSound( "sound/weapons/saber/saberspin1.wav" ); + break; + } + cgi_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, + vec3_origin, spinSound ); /* if ( cg_weapons[WP_SABER].missileSound ) { @@ -623,11 +603,15 @@ const weaponData_t *wData = NULL; { if ( cent->gent->owner->client->ps.saberLength > 0 ) {//only add the blade if it's on - CG_AddSaberBlade( &cg_entities[cent->gent->owner->s.number], &cg_entities[cent->gent->s.number], NULL, ent.renderfx, 0, cent->lerpOrigin, cent->lerpAngles); + CG_AddSaberBlade( &cg_entities[cent->gent->owner->s.number], + &cg_entities[cent->gent->s.number], NULL, ent.renderfx, + 0, cent->lerpOrigin, cent->lerpAngles ); } else if ( cent->gent->owner->client->ps.saberEventFlags & SEF_INWATER ) { - CG_CheckSaberInWater( &cg_entities[cent->gent->owner->s.number], &cg_entities[cent->gent->s.number], 0, cent->lerpOrigin, cent->lerpAngles ); + CG_CheckSaberInWater( &cg_entities[cent->gent->owner->s.number], + &cg_entities[cent->gent->s.number], 0, cent->lerpOrigin, + cent->lerpAngles ); } } if ( cent->gent->owner->health ) @@ -649,12 +633,28 @@ const weaponData_t *wData = NULL; { if ( cg_weapons[WP_SABER].firingSound ) { - cgi_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cg_weapons[WP_SABER].firingSound ); + cgi_S_AddLoopingSound( cent->currentState.number, + cent->lerpOrigin, vec3_origin, cg_weapons[WP_SABER].firingSound ); } } else { - cgi_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgi_S_RegisterSound("sound/weapons/saber/saberspin.wav") ); + int spinSound; + switch ( cent->gent->owner->client->ps.forcePowerLevel[FP_SABERTHROW] ) + { + case FORCE_LEVEL_1: + spinSound = cgi_S_RegisterSound( "sound/weapons/saber/saberspin3.wav" ); + break; + case FORCE_LEVEL_2: + spinSound = cgi_S_RegisterSound( "sound/weapons/saber/saberspin2.wav" ); + break; + default: + case FORCE_LEVEL_3: + spinSound = cgi_S_RegisterSound( "sound/weapons/saber/saberspin1.wav" ); + break; + } + cgi_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, + vec3_origin, spinSound ); /* if ( cg_weapons[WP_SABER].missileSound ) { @@ -663,7 +663,8 @@ const weaponData_t *wData = NULL; */ } } - CG_AddSaberBlade( &cg_entities[cent->gent->owner->s.number], NULL, &ent, ent.renderfx, 0, NULL, NULL ); + CG_AddSaberBlade( &cg_entities[cent->gent->owner->s.number], + NULL, &ent, ent.renderfx, 0, NULL, NULL ); if ( cent->gent->owner->health ) { @@ -1487,53 +1488,6 @@ void CG_AdjustPositionForMover( const vec3_t in, int moverNum, int atTime, vec3_ // FIXME: origin change when on a rotating object } -/* -Ghoul2 Insert Start -*/ -/* -static void LerpBoneAngleOverrides( centity_t *cent) -{ - // loop each model - for (int i=0; i< cent->gent->ghoul2.size(); i++) - { - if (cent->gent->ghoul2[i].mModelindex != -1) - { - // now walk the bone list - for (int x = 0; x < cent->gent->ghoul2[i].mBlist.size(); x++) - { - boneInfo_t &bone = cent->gent->ghoul2[i].mBlist[x]; - // sure we have one to lerp to? - if ((cent->nextState.ghoul2.size() > i) && - (cent->nextState.ghoul2[i].mModelindex != -1) && - (cent->nextState.ghoul2[i].mBlist.size() > x) && - (cent->nextState.ghoul2[i].mBlist[x].boneNumber != -1)) - { - boneInfo_t &nextBone = cent->nextState.ghoul2[i].mBlist[x]; - // does this bone override actually have anything in it, and if it does, is it a bone angles override? - if ((bone.boneNumber != -1) && ((bone.flags) & (BONE_ANGLES_TOTAL))) - { - float *nowMatrix = (float*) &bone.matrix; - float *nextMatrix = (float*) &nextBone.matrix; - float *newMatrix = (float*) &bone.newMatrix; - // now interpolate the matrix - for (int z=0; z < 12; z++) - { - newMatrix[z] = nowMatrix[z] + cg.frameInterpolation * ( nextMatrix[z] - nowMatrix[z] ); - } - } - } - else - { - memcpy(¢->gent->ghoul2[i].mBlist[x].newMatrix, ¢->gent->ghoul2[i].mBlist[x].matrix, sizeof(mdxaBone_t)); - } - } - } - } -} -*/ -/* -Ghoul2 Insert End -*/ /* =============== CG_CalcEntityLerpPositions @@ -1541,6 +1495,7 @@ CG_CalcEntityLerpPositions =============== */ extern char *vtos( const vec3_t v ); +#if 1 void CG_CalcEntityLerpPositions( centity_t *cent ) { if ( cent->currentState.number == cg.snap->ps.clientNum) { @@ -1689,7 +1644,107 @@ Ghoul2 Insert End */ // FIXME: perform general error decay? } +#else +void CG_CalcEntityLerpPositions( centity_t *cent ) +{ + if ( cent->currentState.number == cg.snap->ps.clientNum) + { + // if the player, take position from prediction + VectorCopy( cg.predicted_player_state.origin, cent->lerpOrigin ); + VectorCopy( cg.predicted_player_state.viewangles, cent->lerpAngles ); +OutputDebugString(va("b=(%6.2f,%6.2f,%6.2f)\n",cent->lerpOrigin[0],cent->lerpOrigin[1],cent->lerpOrigin[2])); + return; + } + +if (cent->currentState.number != cg.snap->ps.clientNum&¢->interpolate && cent->currentState.pos.trType == TR_INTERPOLATE) +{ + if (cent->interpolate) + { +OutputDebugString(va("[%3d] interp %4.2f t=%6d st = %6d nst = %6d b=%6.2f nb=%6.2f\n", + cent->currentState.number, + cg.frameInterpolation, + cg.time, + cg.snap->serverTime, + cg.nextSnap->serverTime, + cent->currentState.pos.trBase[0], + cent->nextState.pos.trBase[0])); + } + else + { +OutputDebugString(va("[%3d] nonext %4.2f t=%6d st = %6d nst = %6d b=%6.2f nb=%6.2f\n", + cent->currentState.number, + cg.frameInterpolation, + cg.time, + cg.snap->serverTime, + 0, + cent->currentState.pos.trBase[0], + 0.0f)); + } +} + + //FIXME: prediction on clients in timescale results in jerky positional translation + if (cent->interpolate && + (cent->currentState.number == cg.snap->ps.clientNum || + cent->interpolate && cent->currentState.pos.trType == TR_INTERPOLATE ) ) + { + vec3_t current, next; + float f; + + // it would be an internal error to find an entity that interpolates without + // a snapshot ahead of the current one + if ( cg.nextSnap == NULL ) + { + CG_Error( "CG_AddCEntity: cg.nextSnap == NULL" ); + } + + f = cg.frameInterpolation; + + EvaluateTrajectory( ¢->currentState.apos, cg.snap->serverTime, current ); + EvaluateTrajectory( ¢->nextState.apos, cg.nextSnap->serverTime, next ); + + cent->lerpAngles[0] = LerpAngle( current[0], next[0], f ); + cent->lerpAngles[1] = LerpAngle( current[1], next[1], f ); + cent->lerpAngles[2] = LerpAngle( current[2], next[2], f ); + + EvaluateTrajectory( ¢->currentState.pos, cg.snap->serverTime, current ); + EvaluateTrajectory( ¢->nextState.pos, cg.nextSnap->serverTime, next ); + + cent->lerpOrigin[0] = current[0] + f * ( next[0] - current[0] ); + cent->lerpOrigin[1] = current[1] + f * ( next[1] - current[1] ); + cent->lerpOrigin[2] = current[2] + f * ( next[2] - current[2] ); + return; + } + // just use the current frame and evaluate as best we can + trajectory_t *posData = ¢->currentState.pos; + { + gentity_t *ent = &g_entities[cent->currentState.number]; + + if ( ent && ent->inuse) + { + if ( ent->s.eFlags & EF_BLOCKED_MOVER || ent->s.pos.trType == TR_STATIONARY ) + {//this mover has stopped moving and is going to wig out if we predict it + //based on last frame's info- cut across the network and use the currentOrigin + VectorCopy( ent->currentOrigin, cent->lerpOrigin ); + posData = NULL; + } + else + { + posData = &ent->s.pos; + EvaluateTrajectory(&ent->s.pos,cg.time, cent->lerpOrigin ); + } + } + else + { + EvaluateTrajectory( ¢->currentState.pos, cg.snap->serverTime, cent->lerpOrigin ); + } + } + EvaluateTrajectory( ¢->currentState.apos, cg.time, cent->lerpAngles ); + + // adjust for riding a mover + CG_AdjustPositionForMover( cent->lerpOrigin, cent->currentState.groundEntityNum, cg.time, cent->lerpOrigin ); +} +#endif /* =============== CG_AddLocalSet @@ -1825,6 +1880,63 @@ void CG_DLightThink ( centity_t *cent ) } } +void CG_Limb ( centity_t *cent ) +{//first time we're drawn, remove us from the owner ent + if ( cent->gent && cent->gent->owner && cent->gent->owner->ghoul2.size() ) + { + gentity_t *owner = cent->gent->owner; + if ( cent->gent->aimDebounceTime ) + {//done with dismemberment, just waiting to mark owner dismemberable again + if ( cent->gent->aimDebounceTime > cg.time ) + {//still waiting + return; + } + //done! + owner->client->dismembered = qfalse; + } + else + { +extern cvar_t *g_dismemberment; +extern cvar_t *g_realisticSaberDamage; + //3) turn off w/descendants that surf in original model + if ( cent->gent->target )//stubTagName ) + {//add smoke to cap surf, spawn effect + int newBolt = gi.G2API_AddBolt( &owner->ghoul2[owner->playerModel], cent->gent->target ); + if ( newBolt != -1 ) + { + G_PlayEffect( "blaster/smoke_bolton", owner->playerModel, newBolt, owner->s.number ); + } + } + if ( cent->gent->target2 )//limbName + {//turn the limb off + gi.G2API_SetSurfaceOnOff( &owner->ghoul2[owner->playerModel], cent->gent->target2, 0x00000100 );//G2SURFACEFLAG_NODESCENDANTS + } + if ( cent->gent->target3 )//stubCapName ) + {//turn on caps + gi.G2API_SetSurfaceOnOff( &owner->ghoul2[owner->playerModel], cent->gent->target3, 0 ); + } + if ( owner->weaponModel != -1 ) + {//the corpse hasn't dropped their weapon + if ( cent->gent->count == BOTH_DISMEMBER_RARM || cent->gent->count == BOTH_DISMEMBER_TORSO1 )//&& ent->s.weapon == WP_SABER && ent->weaponModel != -1 ) + {//FIXME: is this first check needed with this lower one? + gi.G2API_RemoveGhoul2Model( owner->ghoul2, owner->weaponModel ); + owner->weaponModel = -1; + } + } + if ( owner->client->NPC_class == CLASS_PROTOCOL + || g_dismemberment->integer > 3 + || g_realisticSaberDamage->integer ) + { + //wait 100ms before allowing owner to be dismembered again + cent->gent->aimDebounceTime = cg.time + 100; + return; + } + } + } + //done! + cent->gent->e_clThinkFunc = clThinkF_NULL; +} + qboolean MatrixMode = qfalse; void CG_MatrixEffect ( centity_t *cent ) { @@ -2070,19 +2182,18 @@ void CG_AddPacketEntities( void ) { if ( delta == 0 ) { cg.frameInterpolation = 0; -// Com_Printf("identical frames\n"); } else { cg.frameInterpolation = (float)( cg.time - cg.snap->serverTime ) / delta; -// Com_Printf("interp %6.4f\n",cg.frameInterpolation); } +//OutputDebugString(va("interp %4.2f ct=%6d nt=%6d st=%6d\n",cg.frameInterpolation,cg.time,cg.nextSnap->serverTime,cg.snap->serverTime)); } else { cg.frameInterpolation = 0; // actually, it should never be used, because // no entities should be marked as interpolating -// Com_Printf("no next snap!\n"); +//OutputDebugString(va("noterp %4.2f ct=%6d nt=%6d st=%6d\n",cg.frameInterpolation,cg.time,0,cg.snap->serverTime)); } // the auto-rotating items will all have the same axis diff --git a/code/cgame/cg_event.cpp b/code/cgame/cg_event.cpp index 1332cea..77b4601 100644 --- a/code/cgame/cg_event.cpp +++ b/code/cgame/cg_event.cpp @@ -123,12 +123,15 @@ void CG_ItemPickup( int itemNum ) { } } } - if (bg_itemlist[itemNum].pickup_name && bg_itemlist[itemNum].pickup_name[0]) + if (bg_itemlist[itemNum].classname && bg_itemlist[itemNum].classname[0]) { - char text[1024]; + char text[1024], data[1024]; if (cgi_SP_GetStringTextString("INGAME_PICKUPLINE",text, sizeof(text)) ) - { - Com_Printf("%s %s\n", text, bg_itemlist[itemNum].pickup_name); + { + if ( cgi_SP_GetStringTextString( va("INGAME_%s",bg_itemlist[itemNum].classname ), data, sizeof( data ))) + { + Com_Printf("%s %s\n", text, data ); + } } } } @@ -269,7 +272,7 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) { case EV_FOOTSTEP: DEBUGNAME("EV_FOOTSTEP"); if (cg_footsteps.integer) { - if ( cent->gent && cent->gent->s.number == 0 && !cg_thirdPerson.integer ) + if ( cent->gent && cent->gent->s.number == 0 && !cg.renderingThirdPerson )//!cg_thirdPerson.integer ) {//Everyone else has keyframed footsteps in animsounds.cfg cgi_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.footsteps[ FOOTSTEP_NORMAL ][rand()&3] ); @@ -280,7 +283,7 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) { DEBUGNAME("EV_FOOTSTEP_METAL"); if (cg_footsteps.integer) { - if ( cent->gent && cent->gent->s.number == 0 && !cg_thirdPerson.integer ) + if ( cent->gent && cent->gent->s.number == 0 && !cg.renderingThirdPerson )//!cg_thirdPerson.integer ) {//Everyone else has keyframed footsteps in animsounds.cfg cgi_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.footsteps[ FOOTSTEP_METAL ][rand()&3] ); } @@ -339,7 +342,8 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) { break; case EV_FALL_FAR: DEBUGNAME("EV_FALL_FAR"); - CG_TryPlayCustomSound(NULL, es->number, CHAN_AUTO, "*fall1.wav", CS_BASIC ); + CG_TryPlayCustomSound( NULL, es->number, CHAN_BODY, "*land1.wav", CS_BASIC ); + cgi_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.landSound ); cent->pe.painTime = cg.time; // don't play a pain sound right after this if ( clientNum == cg.predicted_player_state.clientNum ) { // smooth landing z changes @@ -513,8 +517,7 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) { case EV_DISRUPTOR_SNIPER_MISS: DEBUGNAME("EV_DISRUPTOR_SNIPER_MISS"); - ByteToDir( es->eventParm, dir ); - FX_DisruptorAltMiss( cent->lerpOrigin, dir ); + FX_DisruptorAltMiss( cent->lerpOrigin, cent->gent->pos1 ); break; case EV_DEMP2_ALT_IMPACT: @@ -571,13 +574,14 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) { disintLength = 2000; makeNotSolid = qtrue; break; - case PW_SHOCKED:// arc welder +/* case PW_SHOCKED:// arc welder disintEffect = EF_DISINT_1;//ef_ disintSound1 = NULL;//with scream disintSound2 = NULL;//no scream disintSound3 = NULL;//with inhuman scream disintLength = 4000; break; +*/ default: return; break; diff --git a/code/cgame/cg_info.cpp b/code/cgame/cg_info.cpp index e3c6bb7..8a93296 100644 --- a/code/cgame/cg_info.cpp +++ b/code/cgame/cg_info.cpp @@ -235,30 +235,6 @@ void MissionInformation_Draw( centity_t *cent ) } /* -==================== -CG_DrawMissionInformation -==================== -*/ -/* -void CG_DrawMissionInformation( void ) -{ - centity_t *cent; - - // Don't show if dead - if (cg.predicted_player_state.pm_type == PM_DEAD) - { - return; - } - - cent = &cg_entities[cg.snap->ps.clientNum]; - - MissionInformation_Draw(cent); - - missionInfo_Updated = qfalse; // Player saw it - cg.missionInfoFlashTime = 0; - -} -*/ //------------------------------------------------------- static void CG_DrawForceCount( const int force, int x, float *y, const int pad,qboolean *hasForcePowers ) @@ -276,15 +252,6 @@ static void CG_DrawForceCount( const int force, int x, float *y, const int pad,q return; } -/* if (( val < 1 ) || (val> NUM_FORCE_POWERS)) - { - textColor = CT_MDGREY; - } - else - { - textColor = CT_ICON_BLUE; - } -*/ textColor = CT_ICON_BLUE; // Draw title @@ -316,6 +283,7 @@ static void CG_DrawForceCount( const int force, int x, float *y, const int pad,q CG_LoadScreen_PersonalInfo ==================== */ +/* static void CG_LoadScreen_PersonalInfo(void) { float x, y; @@ -356,6 +324,7 @@ static void CG_LoadScreen_PersonalInfo(void) } } +*/ static void CG_LoadBar(void) { @@ -368,6 +337,8 @@ static void CG_LoadBar(void) xLength = 21; cgi_R_SetColor( colorTable[CT_WHITE]); + CG_DrawPic(166,428,640-(164*2), 32, cgs.media.levelLoad); + for (i=0;i < cg.loadLCARSStage;i++) { CG_DrawPic(x + (i*pad) + (i*xLength),y, 32, 8, cgs.media.loadTick); @@ -383,16 +354,13 @@ overylays UI_DrawConnectText from ui_connect.cpp ==================== */ void CG_DrawInformation( void ) { - const char *s; - qhandle_t levelshot; int y; // draw the dialog background -// const char *info = CG_ConfigString( CS_SERVERINFO ); -// s = Info_ValueForKey( info, "mapname" ); -// levelshot = cgi_R_RegisterShaderNoMip( va( "levelshots/%s.tga", s ) ); - levelshot = cgs.media.levelLoad; + const char *info = CG_ConfigString( CS_SERVERINFO ); + const char *s = Info_ValueForKey( info, "mapname" ); + const qhandle_t levelshot = cgi_R_RegisterShaderNoMip( va( "levelshots/%s", s ) ); extern SavedGameJustLoaded_e g_eSavedGameJustLoaded; // hack! (hey, it's the last week of coding, ok? if ( !levelshot || g_eSavedGameJustLoaded == eFULL ) @@ -408,13 +376,17 @@ void CG_DrawInformation( void ) { CG_DrawPic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, levelshot ); } - if (cg_missionstatusscreen.integer && cg.loadLCARSStage < 7 ) + if ( !strcmp(s,"kejim_post") )//special case for first map! { - CG_MissionCompletion(); + char text[1024]={0}; + cgi_SP_GetStringTextString( "INGAME_ALONGTIME", text, sizeof(text) ); + int w = cgi_R_Font_StrLenPixels(text,cgs.media.qhFontMedium, 1.5f); + cgi_R_Font_DrawString((320)-(w/2), 140, text, colorTable[CT_ICON_BLUE], cgs.media.qhFontMedium, -1, 1.5f); } else + if (cg_missionstatusscreen.integer ) { - CG_LoadScreen_PersonalInfo(); + CG_MissionCompletion(); } CG_LoadBar(); diff --git a/code/cgame/cg_local.h b/code/cgame/cg_local.h index 130da4f..4b32fba 100644 --- a/code/cgame/cg_local.h +++ b/code/cgame/cg_local.h @@ -319,7 +319,7 @@ typedef struct { float landChange; // for landing hard int landTime; - + // input state sent to server int weaponSelect; int saberAnimLevelPending; @@ -413,6 +413,8 @@ typedef struct { int DataPadWeaponSelect; // Current weapon item chosen on Data Pad int DataPadforcepowerSelect; // Current force power chosen on Data Pad + int dataPadLevelStartTime; // Time until datapad won't pop up when level starts + qboolean messageLitActive; // Flag to show of message lite is active int weaponSelectTime; @@ -482,6 +484,7 @@ Ghoul2 Insert End } cg_t; +#define MAX_SHOWPOWERS 6 extern int showPowers[8]; //============================================================================== @@ -537,6 +540,7 @@ extern vmCvar_t cg_drawAmmoWarning; extern vmCvar_t cg_drawCrosshair; extern vmCvar_t cg_dynamicCrosshair; extern vmCvar_t cg_crosshairForceHint; +extern vmCvar_t cg_crosshairIdentifyTarget; extern vmCvar_t cg_crosshairX; extern vmCvar_t cg_crosshairY; extern vmCvar_t cg_crosshairSize; @@ -561,6 +565,8 @@ extern vmCvar_t cg_simpleItems; extern vmCvar_t cg_fov; extern vmCvar_t cg_missionstatusscreen; extern vmCvar_t cg_endcredits; +extern vmCvar_t cg_updatedDataPadForcePower; +extern vmCvar_t cg_updatedDataPadObjective; extern vmCvar_t cg_thirdPerson; extern vmCvar_t cg_thirdPersonRange; @@ -595,6 +601,8 @@ extern vmCvar_t cg_debugBB; /* Ghoul2 Insert End */ +extern vmCvar_t cg_turnAnims; +extern vmCvar_t cg_smoothPlayerPos; void CG_NewClientinfo( int clientNum ); // diff --git a/code/cgame/cg_localents.cpp b/code/cgame/cg_localents.cpp index 19aea17..04ce528 100644 --- a/code/cgame/cg_localents.cpp +++ b/code/cgame/cg_localents.cpp @@ -371,6 +371,12 @@ static void CG_AddFadeModel( localEntity_t *le ) { refEntity_t *ent = &le->refEntity; + if ( cg.time < le->startTime ) + { + CG_FreeLocalEntity( le ); + return; + } + float frac = 1.0f - ((float)( cg.time - le->startTime )/(float)( le->endTime - le->startTime )); ent->shaderRGBA[0] = le->color[0] * frac; diff --git a/code/cgame/cg_main.cpp b/code/cgame/cg_main.cpp index 990fe6f..7e0404d 100644 --- a/code/cgame/cg_main.cpp +++ b/code/cgame/cg_main.cpp @@ -218,6 +218,7 @@ vmCvar_t cg_drawFPS; vmCvar_t cg_drawSnapshot; vmCvar_t cg_drawAmmoWarning; vmCvar_t cg_drawCrosshair; +vmCvar_t cg_crosshairIdentifyTarget; vmCvar_t cg_dynamicCrosshair; vmCvar_t cg_crosshairForceHint; vmCvar_t cg_crosshairX; @@ -244,6 +245,8 @@ vmCvar_t cg_simpleItems; vmCvar_t cg_fov; vmCvar_t cg_missionstatusscreen; vmCvar_t cg_endcredits; +vmCvar_t cg_updatedDataPadForcePower; +vmCvar_t cg_updatedDataPadObjective; vmCvar_t cg_thirdPerson; vmCvar_t cg_thirdPersonRange; @@ -281,6 +284,9 @@ Ghoul2 Insert End */ vmCvar_t cg_VariantSoundCap; // 0 = no capping, else cap to (n) max (typically just 1, but allows more) +vmCvar_t cg_turnAnims; + +vmCvar_t cg_smoothPlayerPos; typedef struct { vmCvar_t *vmCvar; @@ -304,9 +310,12 @@ cvarTable_t cvarTable[] = { { &cg_drawAmmoWarning, "cg_drawAmmoWarning", "1", CVAR_ARCHIVE }, { &cg_drawCrosshair, "cg_drawCrosshair", "1", CVAR_ARCHIVE }, { &cg_dynamicCrosshair, "cg_dynamicCrosshair", "1", CVAR_ARCHIVE }, + { &cg_crosshairIdentifyTarget, "cg_crosshairIdentifyTarget", "1", CVAR_ARCHIVE }, { &cg_crosshairForceHint, "cg_crosshairForceHint", "1", CVAR_ARCHIVE }, { &cg_missionstatusscreen, "cg_missionstatusscreen", "0", CVAR_ROM}, { &cg_endcredits, "cg_endcredits", "0", 0}, + { &cg_updatedDataPadForcePower, "cg_updatedDataPadForcePower", "0", 0}, + { &cg_updatedDataPadObjective, "cg_updatedDataPadObjective", "0", 0}, { &cg_crosshairSize, "cg_crosshairSize", "24", CVAR_ARCHIVE }, { &cg_crosshairX, "cg_crosshairX", "0", CVAR_ARCHIVE }, @@ -371,7 +380,9 @@ Ghoul2 Insert Start /* Ghoul2 Insert End */ - { &cg_VariantSoundCap, "cg_VariantSoundCap", "0" }, + { &cg_VariantSoundCap, "cg_VariantSoundCap", "0", 0 }, + { &cg_turnAnims, "cg_turnAnims", "0", CVAR_ARCHIVE }, + { &cg_smoothPlayerPos, "cg_smoothPlayerPos", "0.5", 0}, }; int cvarTableSize = sizeof( cvarTable ) / sizeof( cvarTable[0] ); @@ -628,7 +639,7 @@ static void CG_RegisterSounds( void ) { Com_sprintf (name, sizeof(name), "sound/player/footsteps/step%i.wav", i+1); cgs.media.footsteps[FOOTSTEP_NORMAL][i] = cgi_S_RegisterSound (name); - Com_sprintf (name, sizeof(name), "sound/player/footsteps/splash%i.wav", i+1); + Com_sprintf (name, sizeof(name), "sound/player/footsteps/water_wade0%i.wav", i+1); cgs.media.footsteps[FOOTSTEP_SPLASH][i] = cgi_S_RegisterSound (name); Com_sprintf (name, sizeof(name), "sound/player/footsteps/clank%i.wav", i+1); @@ -638,6 +649,7 @@ static void CG_RegisterSounds( void ) { Com_sprintf (name, sizeof(name), "sound/player/footsteps/boot%i.wav", i+1); cgi_S_RegisterSound (name); } + theFxScheduler.RegisterEffect( "water_impact" ); cg.loadLCARSStage = 1; CG_LoadingString( "item sounds" ); @@ -1150,7 +1162,6 @@ static void CG_RegisterGraphics( void ) { cgs.media.solidWhiteShader = cgi_R_RegisterShader( "gfx/effects/solidWhite" ); //on players - cgs.media.disruptorShader = cgi_R_RegisterShader( "powerups/disruptor"); cgs.media.personalShieldShader = cgi_R_RegisterShader( "gfx/misc/personalshield" ); cgs.media.cloakedShader = cgi_R_RegisterShader( "gfx/effects/cloakedShader" ); cgi_R_RegisterShader( "gfx/misc/ion_shield" ); @@ -1194,7 +1205,8 @@ static void CG_RegisterGraphics( void ) { } cgs.media.chunkSound = cgi_S_RegisterSound("sound/weapons/explosions/glasslcar.wav"); - cgs.media.rockBreakSound = cgi_S_RegisterSound("sound/effects/stone_break.wav"); + cgs.media.grateSound = cgi_S_RegisterSound( "sound/effects/grate_destroy.mp3" ); + cgs.media.rockBreakSound = cgi_S_RegisterSound("sound/effects/wall_smash.mp3"); cgs.media.rockBounceSound[0]= cgi_S_RegisterSound("sound/effects/stone_bounce.wav"); cgs.media.rockBounceSound[1]= cgi_S_RegisterSound("sound/effects/stone_bounce2.wav"); cgs.media.metalBounceSound[0] = cgi_S_RegisterSound("sound/effects/metal_bounce.wav"); @@ -1243,12 +1255,19 @@ static void CG_RegisterGraphics( void ) { for ( i = 1 ; i < bg_numItems ; i++ ) { if ( items[ i ] == '1' ) { - if (bg_itemlist[i].pickup_name) + if (bg_itemlist[i].classname) { - CG_LoadingString( bg_itemlist[i].pickup_name ); + CG_LoadingString( bg_itemlist[i].classname ); CG_RegisterItemVisuals( i ); } } + if (bg_itemlist[i].giType == IT_HOLDABLE) + { + if (bg_itemlist[i].giTag < INV_MAX) + { + inv_icons[bg_itemlist[i].giTag] = cgi_R_RegisterShaderNoMip( bg_itemlist[i].icon ); + } + } } cgi_R_RegisterShader( "gfx/misc/test_crackle" ); @@ -1285,45 +1304,6 @@ static void CG_RegisterGraphics( void ) { } } - // Precache inventory icons - for ( i = 1 ; i < bg_numItems ; i++ ) - { - bg_itemlist[0] = bg_itemlist[0]; - if (!bg_itemlist[i].classname) - { - continue; - } - - if (!Q_stricmp(bg_itemlist[i].classname, "ITEM_BACTA" )) - { - inv_icons[INV_BACTA_CANISTER] = cgi_R_RegisterShaderNoMip( bg_itemlist[i].icon ); - } - else if (!Q_stricmp(bg_itemlist[i].classname,"ITEM_BINOCULARS" )) - { - inv_icons[INV_ELECTROBINOCULARS] = cgi_R_RegisterShaderNoMip( bg_itemlist[i].icon ); - } - else if (!Q_stricmp(bg_itemlist[i].classname,"ITEM_SEEKER" )) - { - inv_icons[INV_SEEKER] = cgi_R_RegisterShaderNoMip( bg_itemlist[i].icon ); - } - else if (!Q_stricmp(bg_itemlist[i].classname,"ITEM_LA_GOGGLES" )) - { - inv_icons[INV_LIGHTAMP_GOGGLES] = cgi_R_RegisterShaderNoMip( bg_itemlist[i].icon ); - } - else if (!Q_stricmp(bg_itemlist[i].classname, "ITEM_SENTRY_GUN" )) - { - inv_icons[INV_SENTRY] = cgi_R_RegisterShaderNoMip( bg_itemlist[i].icon ); - } - else if (!Q_stricmp(bg_itemlist[i].classname, "ITEM_GOODIE_KEY" )) - { - inv_icons[INV_GOODIE_KEY1] = cgi_R_RegisterShaderNoMip( bg_itemlist[i].icon ); - } - else if (!Q_stricmp(bg_itemlist[i].classname, "ITEM_SECURITY_KEY" )) - { - inv_icons[INV_SECURITY_KEY1] = cgi_R_RegisterShaderNoMip( bg_itemlist[i].icon ); - } - } - cg.loadLCARSStage = 7; CG_LoadingString("map models"); // register all the server specified models @@ -1551,7 +1531,7 @@ Ghoul2 Insert End CG_ParseServerinfo(); // load the new map - cgs.media.levelLoad = cgi_R_RegisterShaderNoMip( "gfx/hud/levelload.tga" ); + cgs.media.levelLoad = cgi_R_RegisterShaderNoMip( "gfx/hud/mp_levelload" ); CG_LoadingString( "collision map" ); cgi_CM_LoadMap( cgs.mapname ); @@ -1573,6 +1553,9 @@ Ghoul2 Insert End CGCam_Init(); CG_ClearLightStyles(); + + cg.dataPadLevelStartTime = cg.time+15000; + } void CG_WriteTheEvilCGHackStuff(void) @@ -2551,55 +2534,9 @@ void CG_NextInventory_f( void ) CG_UseInventory_f =============== */ -void CG_UseInventory_f( void ) -{ - - int count,i; - -// const int bits = cg.snap->ps.stats[ STAT_ITEMS ]; - - // count the number of items owned - count = 0; - for ( i = 0 ; i < INV_MAX ; i++ ) - { - if (CG_InventorySelectable(i)) - { - count++; - } - } - - if (!count) // Trying to use an empty inventory - { - // FIXME: need a negative sound. - return; - } - - switch (cg.inventorySelect) - { - case INV_ELECTROBINOCULARS: - case INV_BACTA_CANISTER: - case INV_SEEKER: - case INV_LIGHTAMP_GOGGLES: - case INV_SENTRY: - case INV_GOODIE_KEY1: - case INV_GOODIE_KEY2: - case INV_GOODIE_KEY3: - case INV_GOODIE_KEY4: - case INV_GOODIE_KEY5: - UseItem(cg.inventorySelect ); - break; - case INV_SECURITY_KEY1: - case INV_SECURITY_KEY2: - case INV_SECURITY_KEY3: - case INV_SECURITY_KEY4: - case INV_SECURITY_KEY5: - UseItem(cg.inventorySelect ); - break; - default: - CG_Error( "Unknown inventory item: %d", cg.inventorySelect ); - break; - } -} +/* +this func was moved to Cmd_UseInventory_f in g_cmds.cpp +*/ /* =============== @@ -2651,7 +2588,7 @@ void CG_PrevInventory_f( void ) FindInventoryItemTag =================== */ -int FindInventoryItemTag(int tag) +gitem_t *FindInventoryItemTag(int tag) { int i; @@ -2687,9 +2624,9 @@ int FindInventoryItemTag(int tag) for ( i = 1 ; i < bg_numItems ; i++ ) { - if (bg_itemlist[i].giTag == tag) + if ( bg_itemlist[i].giTag == tag && bg_itemlist[i].giType == IT_HOLDABLE ) // I guess giTag's aren't unique amongst items..must also make sure it's a holdable { - return (i); + return &bg_itemlist[i]; } } @@ -2718,7 +2655,7 @@ void CG_DrawInventorySelect( void ) vec4_t textColor = { .312f, .75f, .621f, 1.0f }; // don't display if dead - if ( cg.predicted_player_state.stats[STAT_HEALTH] <= 0 ) + if ( cg.predicted_player_state.stats[STAT_HEALTH] <= 0 || ( cg.snap->ps.viewEntity > 0 && cg.snap->ps.viewEntity < ENTITYNUM_WORLD )) { return; } @@ -2836,13 +2773,23 @@ void CG_DrawInventorySelect( void ) if (inv_names[cg.inventorySelect]) { - // FIXME :this has to use the bg_itemlist pickup name -// tag = FindInventoryItemTag(cg.inventorySelect); + // FIXME: This is ONLY a temp solution, the icon stuff, etc, should all just use items.dat for everything + gitem_t *item = FindInventoryItemTag( cg.inventorySelect ); - int w = cgi_R_Font_StrLenPixels(inv_names[cg.inventorySelect], cgs.media.qhFontSmall, 1.0f); - int x = ( SCREEN_WIDTH - w ) / 2; - cgi_R_Font_DrawString(x, (SCREEN_HEIGHT - 24), inv_names[cg.inventorySelect], textColor, cgs.media.qhFontSmall, -1, 1.0f); + if ( item && item->classname && item->classname[0] ) + { + char itemName[256], data[1024]; // FIXME: do these really need to be this large?? does it matter? + sprintf( itemName, "INGAME_%s", item->classname ); + + if ( cgi_SP_GetStringTextString( itemName, data, sizeof( data ))) + { + int w = cgi_R_Font_StrLenPixels( data, cgs.media.qhFontSmall, 1.0f ); + int x = ( SCREEN_WIDTH - w ) / 2; + + cgi_R_Font_DrawString( x, (SCREEN_HEIGHT - 24), data, textColor, cgs.media.qhFontSmall, -1, 1.0f); + } + } // if (tag) // { // CG_DrawProportionalString(320, y + 53, inv_names[cg.inventorySelect], CG_CENTER | CG_SMALLFONT, colorTable[CT_ICON_BLUE]); @@ -3130,8 +3077,7 @@ int showPowers[] = FP_LIGHTNING, NUM_FORCE_POWERS, }; -#define MAX_SHOWPOWERS 6 - +/* char *showPowersName[] = { "Heal", @@ -3143,7 +3089,50 @@ char *showPowersName[] = "Lightning", NULL, }; +*/ +char *showPowersName[] = +{ + "HEAL2", + "SPEED2", + "PUSH2", + "PULL2", + "MINDTRICK2", + "GRIP2", + "LIGHTNING2", + NULL, +}; +int showDataPadPowers[] = +{ + FP_HEAL, + FP_LEVITATION, + FP_SPEED, + FP_PUSH, + FP_PULL, + FP_TELEPATHY, + FP_GRIP, + FP_LIGHTNING, + FP_SABERTHROW, + FP_SABER_DEFENSE, + FP_SABER_OFFENSE, + NUM_FORCE_POWERS, +}; + +char *showDataPadPowersName[] = +{ + "HEAL2", + "JUMP2", + "SPEED2", + "PUSH2", + "PULL2", + "MINDTRICK2", + "GRIP2", + "LIGHTNING2", + "SABER_THROW2", + "SABER_DEFENSE2", + "SABER_OFFENSE2", + NULL, +}; /* =============== ForcePower_Valid @@ -3154,7 +3143,7 @@ qboolean ForcePower_Valid(int index) gentity_t *player = &g_entities[0]; if (player->client->ps.forcePowersKnown & (1 << showPowers[index]) && - (player->client->ps.forcePowerLevel[showPowers[index]]>0)) // Does he have the force power? + player->client->ps.forcePowerLevel[showPowers[index]]) // Does he have the force power? { return qtrue; } @@ -3258,10 +3247,11 @@ void CG_DrawForceSelect( void ) int holdX,x,y,pad,length; int sideLeftIconCnt,sideRightIconCnt; int sideMax,holdCount,iconCnt; + char text[1024]={0}; // don't display if dead - if ( cg.predicted_player_state.stats[STAT_HEALTH] <= 0 ) + if ( cg.predicted_player_state.stats[STAT_HEALTH] <= 0 || ( cg.snap->ps.viewEntity > 0 && cg.snap->ps.viewEntity < ENTITYNUM_WORLD )) { return; } @@ -3396,11 +3386,12 @@ void CG_DrawForceSelect( void ) } } - if ( showPowersName[cg.forcepowerSelect] ) + // This only a temp solution. + if (cgi_SP_GetStringTextString( va("INGAME_%s",showPowersName[cg.forcepowerSelect]), text, sizeof(text) )) { - int w = cgi_R_Font_StrLenPixels(showPowersName[cg.forcepowerSelect], cgs.media.qhFontSmall, 1.0f); + int w = cgi_R_Font_StrLenPixels(text, cgs.media.qhFontSmall, 1.0f); int x = ( SCREEN_WIDTH - w ) / 2; - cgi_R_Font_DrawString(x, (SCREEN_HEIGHT - 24), showPowersName[cg.forcepowerSelect], colorTable[CT_ICON_BLUE], cgs.media.qhFontSmall, -1, 1.0f); + cgi_R_Font_DrawString(x, (SCREEN_HEIGHT - 24), text, colorTable[CT_ICON_BLUE], cgs.media.qhFontSmall, -1, 1.0f); } } @@ -3421,11 +3412,11 @@ void CG_DPNextForcePower_f( void ) original = cg.DataPadforcepowerSelect; - for ( i = 0 ;showPowers[i]!=NUM_FORCE_POWERS ; i++ ) + for ( i = 0 ;showDataPadPowers[i]!=NUM_FORCE_POWERS ; i++ ) { cg.DataPadforcepowerSelect++; - if (showPowers[cg.DataPadforcepowerSelect] >= NUM_FORCE_POWERS) + if (showDataPadPowers[cg.DataPadforcepowerSelect] >= NUM_FORCE_POWERS) { cg.DataPadforcepowerSelect = 0; } @@ -3456,13 +3447,13 @@ void CG_DPPrevForcePower_f( void ) original = cg.DataPadforcepowerSelect; - for ( i = 0 ;showPowers[i]!=NUM_FORCE_POWERS ; i++ ) + for ( i = 0 ;showDataPadPowers[i]!=NUM_FORCE_POWERS ; i++ ) { cg.DataPadforcepowerSelect--; if (cg.DataPadforcepowerSelect < 0) { - cg.DataPadforcepowerSelect = MAX_SHOWPOWERS; + cg.DataPadforcepowerSelect = NUM_FORCE_POWERS-1; } if (ForcePower_Valid(i)) // Does he have the force power? @@ -3482,7 +3473,7 @@ char *forcepowerDesc[NUM_FORCE_POWERS] = "FORCE_SPEED_DESC", "FORCE_PUSH_DESC", "FORCE_PULL_DESC", -"FORCE_MIND_TRICK", +"FORCE_MIND_TRICK_DESC", "FORCE_GRIP_DESC", "FORCE_LIGHTNING_DESC", "FORCE_SABER_DEFENSE_DESC", @@ -3490,7 +3481,68 @@ char *forcepowerDesc[NUM_FORCE_POWERS] = "FORCE_SABER_THROW_DESC", }; +char *forcepowerLvl1Desc[NUM_FORCE_POWERS] = +{ +"FORCE_HEAL_LVL1_DESC", +"FORCE_JUMP_LVL1_DESC", +"FORCE_SPEED_LVL1_DESC", +"FORCE_PUSH_LVL1_DESC", +"FORCE_PULL_LVL1_DESC", +"FORCE_MIND_TRICK_LVL1_DESC", +"FORCE_GRIP_LVL1_DESC", +"FORCE_LIGHTNING_LVL1_DESC", +"FORCE_SABER_DEFENSE_LVL1_DESC", +"FORCE_SABER_OFFENSE_LVL1_DESC", +"FORCE_SABER_THROW_LVL1_DESC", +}; +char *forcepowerLvl2Desc[NUM_FORCE_POWERS] = +{ +"FORCE_HEAL_LVL2_DESC", +"FORCE_JUMP_LVL2_DESC", +"FORCE_SPEED_LVL2_DESC", +"FORCE_PUSH_LVL2_DESC", +"FORCE_PULL_LVL2_DESC", +"FORCE_MIND_TRICK_LVL2_DESC", +"FORCE_GRIP_LVL2_DESC", +"FORCE_LIGHTNING_LVL2_DESC", +"FORCE_SABER_DEFENSE_LVL2_DESC", +"FORCE_SABER_OFFENSE_LVL2_DESC", +"FORCE_SABER_THROW_LVL2_DESC", +}; + +char *forcepowerLvl3Desc[NUM_FORCE_POWERS] = +{ +"FORCE_HEAL_LVL3_DESC", +"FORCE_JUMP_LVL3_DESC", +"FORCE_SPEED_LVL3_DESC", +"FORCE_PUSH_LVL3_DESC", +"FORCE_PULL_LVL3_DESC", +"FORCE_MIND_TRICK_LVL3_DESC", +"FORCE_GRIP_LVL3_DESC", +"FORCE_LIGHTNING_LVL3_DESC", +"FORCE_SABER_DEFENSE_LVL3_DESC", +"FORCE_SABER_OFFENSE_LVL3_DESC", +"FORCE_SABER_THROW_LVL3_DESC", +}; + +/* +=============== +ForcePowerDataPad_Valid +=============== +*/ +qboolean ForcePowerDataPad_Valid(int index) +{ + gentity_t *player = &g_entities[0]; + + if (player->client->ps.forcePowersKnown & (1 << showDataPadPowers[index]) && + player->client->ps.forcePowerLevel[showDataPadPowers[index]]) // Does he have the force power? + { + return qtrue; + } + + return qfalse; +} /* =================== CG_DrawDataPadForceSelect @@ -3505,13 +3557,14 @@ void CG_DrawDataPadForceSelect( void ) int sideLeftIconCnt,sideRightIconCnt; int sideMax,holdCount,iconCnt; char text[1024]={0}; + char text2[1024]={0}; // count the number of powers owned count = 0; - for (i=0;showPowers[i]!=NUM_FORCE_POWERS;++i) + for (i=0;showDataPadPowers[i]!=NUM_FORCE_POWERS;++i) { - if (ForcePower_Valid(i)) + if (ForcePowerDataPad_Valid(i)) { count++; } @@ -3555,11 +3608,10 @@ void CG_DrawDataPadForceSelect( void ) length = (sideLeftIconCnt * smallIconSize) + (sideLeftIconCnt*pad) + bigIconSize + (sideRightIconCnt * smallIconSize) + (sideRightIconCnt*pad) + 12; - i = cg.DataPadforcepowerSelect - 1; if (i < 0) { - i = MAX_SHOWPOWERS; + i = NUM_FORCE_POWERS-1; } cgi_R_SetColor(NULL); @@ -3569,32 +3621,32 @@ void CG_DrawDataPadForceSelect( void ) { if (i < 0) { - i = MAX_SHOWPOWERS; + i = NUM_FORCE_POWERS-1; } - if (!ForcePower_Valid(i)) // Does he have this power? + if (!ForcePowerDataPad_Valid(i)) // Does he have this power? { continue; } ++iconCnt; // Good icon - if (force_icons[showPowers[i]]) + if (force_icons[showDataPadPowers[i]]) { - CG_DrawPic( holdX, y, smallIconSize, smallIconSize, force_icons[showPowers[i]] ); + CG_DrawPic( holdX, y, smallIconSize, smallIconSize, force_icons[showDataPadPowers[i]] ); holdX -= (smallIconSize+pad); } } // Current Center Icon - if (force_icons[showPowers[cg.DataPadforcepowerSelect]]) + if (force_icons[showDataPadPowers[cg.DataPadforcepowerSelect]]) { - CG_DrawPic( x-(bigIconSize/2), (y-((bigIconSize-smallIconSize)/2)), bigIconSize, bigIconSize, force_icons[showPowers[cg.DataPadforcepowerSelect]] ); //only cache the icon for display + CG_DrawPic( x-(bigIconSize/2), (y-((bigIconSize-smallIconSize)/2)), bigIconSize, bigIconSize, force_icons[showDataPadPowers[cg.DataPadforcepowerSelect]] ); //only cache the icon for display } i = cg.DataPadforcepowerSelect + 1; - if (i>MAX_SHOWPOWERS) + if (i>=NUM_FORCE_POWERS) { i = 0; } @@ -3603,31 +3655,44 @@ void CG_DrawDataPadForceSelect( void ) holdX = x + (bigIconSize/2) + pad; for (iconCnt=1;iconCnt<(sideRightIconCnt+1);i++) { - if (i>MAX_SHOWPOWERS) + if (i>=NUM_FORCE_POWERS) { i = 0; } - if (!ForcePower_Valid(i)) // Does he have this power? + if (!ForcePowerDataPad_Valid(i)) // Does he have this power? { continue; } ++iconCnt; // Good icon - if (force_icons[showPowers[i]]) + if (force_icons[showDataPadPowers[i]]) { - CG_DrawPic( holdX, y, smallIconSize, smallIconSize, force_icons[showPowers[i]] ); //only cache the icon for display + CG_DrawPic( holdX, y, smallIconSize, smallIconSize, force_icons[showDataPadPowers[i]] ); //only cache the icon for display holdX += (smallIconSize+pad); } } cgi_SP_GetStringTextString( va("INGAME_%s",forcepowerDesc[cg.DataPadforcepowerSelect]), text, sizeof(text) ); + if (player->client->ps.forcePowerLevel[cg.DataPadforcepowerSelect]==1) + { + cgi_SP_GetStringTextString( va("INGAME_%s",forcepowerLvl1Desc[cg.DataPadforcepowerSelect]), text2, sizeof(text2) ); + } + else if (player->client->ps.forcePowerLevel[cg.DataPadforcepowerSelect]==2) + { + cgi_SP_GetStringTextString( va("INGAME_%s",forcepowerLvl2Desc[cg.DataPadforcepowerSelect]), text2, sizeof(text2) ); + } + else + { + cgi_SP_GetStringTextString( va("INGAME_%s",forcepowerLvl3Desc[cg.DataPadforcepowerSelect]), text2, sizeof(text2) ); + } + if (text) { - CG_DisplayBoxedText(70,50,500,300,text, + CG_DisplayBoxedText(70,50,500,300,va("%s%s",text,text2), cgs.media.qhFontSmall, 0.7f, colorTable[CT_WHITE] diff --git a/code/cgame/cg_media.h b/code/cgame/cg_media.h index a54d922..10166a8 100644 --- a/code/cgame/cg_media.h +++ b/code/cgame/cg_media.h @@ -82,6 +82,7 @@ typedef struct { //Chunks qhandle_t chunkModels[NUM_CHUNK_TYPES][4]; sfxHandle_t chunkSound; + sfxHandle_t grateSound; sfxHandle_t rockBreakSound; sfxHandle_t rockBounceSound[2]; sfxHandle_t metalBounceSound[2]; @@ -110,7 +111,6 @@ typedef struct { qhandle_t surfaceExplosionShader; qhandle_t solidWhiteShader; - qhandle_t disruptorShader; qhandle_t electricBodyShader; qhandle_t electricBody2Shader; qhandle_t shieldShader; @@ -293,7 +293,7 @@ typedef struct // loaded or calculated from the gamestate. It will NOT // be cleared when a tournement restart is done, allowing // all clients to begin playing instantly -#define STRIPED_LEVELNAME_VARIATIONS 3 // sigh, to cope with levels that use text from >1 SP file +#define STRIPED_LEVELNAME_VARIATIONS 3 // sigh, to cope with levels that use text from >1 SP file (plus 1 for common) typedef struct { gameState_t gameState; // gamestate from server glconfig_t glconfig; // rendering configuration diff --git a/code/cgame/cg_players.cpp b/code/cgame/cg_players.cpp index ff86ffb..d5771bb 100644 --- a/code/cgame/cg_players.cpp +++ b/code/cgame/cg_players.cpp @@ -50,7 +50,7 @@ const char *cg_customBasicSoundNames[MAX_CUSTOM_BASIC_SOUNDS] = "*gurp2.wav", "*drown.wav", "*gasp.wav", - "*fall1.wav", + "*land1.wav", "*falling1.wav", }; @@ -151,21 +151,21 @@ const char *cg_customJediSoundNames[MAX_CUSTOM_JEDI_SOUNDS] = // cuts down on sound-variant registration for low end machines, // eg *gloat1.wav (plus...2,...3) can be capped to all be just *gloat1.wav // -static const char *GetCustomSound_VariantCapped(const char *ppsTable[], int iEntryNum) +static const char *GetCustomSound_VariantCapped(const char *ppsTable[], int iEntryNum, qboolean bForceVariant1) { extern vmCvar_t cg_VariantSoundCap; // const int iVariantCap = 2; // test const int &iVariantCap = cg_VariantSoundCap.integer; - if (iVariantCap) + if (iVariantCap || bForceVariant1) { char *p = strchr(ppsTable[iEntryNum],'.'); if (p && p-2 > ppsTable[iEntryNum] && isdigit(p[-1]) && !isdigit(p[-2])) { int iThisVariant = p[-1]-'0'; - if (iThisVariant > iVariantCap) + if (iThisVariant > iVariantCap || bForceVariant1) { // ok, let's not load this variant, so pick a random one below the cap value... // @@ -180,7 +180,7 @@ static const char *GetCustomSound_VariantCapped(const char *ppsTable[], int iEnt *p = '\0'; sName[strlen(sName)-1] = '\0'; // strip the digit - int iRandom = !i ? Q_irand(1,iVariantCap) : 1; + int iRandom = bForceVariant1 ? 1 : (!i ? Q_irand(1,iVariantCap) : 1); strcat(sName,va("%d.wav",iRandom)); @@ -208,6 +208,35 @@ static const char *GetCustomSound_VariantCapped(const char *ppsTable[], int iEnt return ppsTable[iEntryNum]; } +static void CG_RegisterCustomSounds(clientInfo_t *ci, int iSoundEntryBase, + int iTableEntries, const char *ppsTable[], const char *psDir + ) +{ + for ( int i=0 ; isounds[i + iSoundEntryBase] = hSFX; + } +} + + /* ================ @@ -357,8 +386,8 @@ void CG_NewClientinfo( int clientNum ) clientInfo_t *ci; const char *configstring; const char *v; - const char *s; - int i; +// const char *s; +// int i; configstring = CG_ConfigString( clientNum + CS_PLAYERS ); @@ -415,15 +444,12 @@ void CG_NewClientinfo( int clientNum ) } //player uses only the basic custom sound set, not the combat or extra - for ( i = 0 ; i < MAX_CUSTOM_BASIC_SOUNDS ; i++ ) - { - s = GetCustomSound_VariantCapped(cg_customBasicSoundNames,i); // s = cg_customBasicSoundNames[i]; - if ( !s ) - { - break; - } - ci->sounds[i] = cgi_S_RegisterSound( va("sound/chars/%s/misc/%s", ci->customBasicSoundDir, s + 1) ); - } + CG_RegisterCustomSounds(ci, + 0, // int iSoundEntryBase, + MAX_CUSTOM_BASIC_SOUNDS, // int iTableEntries, + cg_customBasicSoundNames, // const char *ppsTable[], + ci->customBasicSoundDir // const char *psDir + ); ci->infoValid = qfalse; } @@ -433,77 +459,49 @@ CG_RegisterNPCCustomSounds */ void CG_RegisterNPCCustomSounds( clientInfo_t *ci ) { - const char *s; - int i; +// const char *s; +// int i; // sounds if ( ci->customBasicSoundDir && ci->customBasicSoundDir[0] ) { - for ( i = 0 ; i < MAX_CUSTOM_BASIC_SOUNDS ; i++ ) - { - s = GetCustomSound_VariantCapped(cg_customBasicSoundNames,i); // s = cg_customBasicSoundNames[i]; - if ( !s ) - { - break; - } - - char finalName[MAX_QPATH]; - - Q_strncpyz(finalName, s+1, sizeof(finalName), qtrue); - ci->sounds[i] = cgi_S_RegisterSound( va("sound/chars/%s/misc/%s", ci->customBasicSoundDir, finalName) ); - } + CG_RegisterCustomSounds(ci, + 0, // int iSoundEntryBase, + MAX_CUSTOM_BASIC_SOUNDS, // int iTableEntries, + cg_customBasicSoundNames, // const char *ppsTable[], + ci->customBasicSoundDir // const char *psDir + ); } if ( ci->customCombatSoundDir && ci->customCombatSoundDir[0] ) { - for ( i = 0 ; i < MAX_CUSTOM_COMBAT_SOUNDS ; i++ ) - { - s = GetCustomSound_VariantCapped(cg_customCombatSoundNames,i); // s = cg_customCombatSoundNames[i]; - if ( !s ) - { - break; - } - - char finalName[MAX_QPATH]; - - Q_strncpyz(finalName, s+1, sizeof(finalName), qtrue); - ci->sounds[i+MAX_CUSTOM_BASIC_SOUNDS] = cgi_S_RegisterSound( va("sound/chars/%s/misc/%s", ci->customCombatSoundDir, finalName) ); - } + CG_RegisterCustomSounds(ci, + MAX_CUSTOM_BASIC_SOUNDS, // int iSoundEntryBase, + MAX_CUSTOM_COMBAT_SOUNDS, // int iTableEntries, + cg_customCombatSoundNames, // const char *ppsTable[], + ci->customCombatSoundDir // const char *psDir + ); } if ( ci->customExtraSoundDir && ci->customExtraSoundDir[0] ) { - for ( i = 0 ; i < MAX_CUSTOM_EXTRA_SOUNDS ; i++ ) - { - s = GetCustomSound_VariantCapped(cg_customExtraSoundNames,i); // s = cg_customExtraSoundNames[i]; - if ( !s ) - { - break; - } - - char finalName[MAX_QPATH]; - - Q_strncpyz(finalName, s+1, sizeof(finalName), qtrue); - ci->sounds[i+MAX_CUSTOM_BASIC_SOUNDS+MAX_CUSTOM_COMBAT_SOUNDS] = cgi_S_RegisterSound( va("sound/chars/%s/misc/%s", ci->customExtraSoundDir, finalName) ); - } + CG_RegisterCustomSounds(ci, + MAX_CUSTOM_BASIC_SOUNDS+MAX_CUSTOM_COMBAT_SOUNDS, // int iSoundEntryBase, + MAX_CUSTOM_EXTRA_SOUNDS, // int iTableEntries, + cg_customExtraSoundNames, // const char *ppsTable[], + ci->customExtraSoundDir // const char *psDir + ); } if ( ci->customJediSoundDir && ci->customJediSoundDir[0] ) { - for ( i = 0 ; i < MAX_CUSTOM_JEDI_SOUNDS ; i++ ) - { - s = GetCustomSound_VariantCapped(cg_customJediSoundNames,i); // s = cg_customJediSoundNames[i]; - if ( !s ) - { - break; - } - - char finalName[MAX_QPATH]; - - Q_strncpyz(finalName, s+1, sizeof(finalName), qtrue); - ci->sounds[i+MAX_CUSTOM_BASIC_SOUNDS+MAX_CUSTOM_COMBAT_SOUNDS+MAX_CUSTOM_EXTRA_SOUNDS] = cgi_S_RegisterSound( va("sound/chars/%s/misc/%s", ci->customJediSoundDir, finalName) ); - } + CG_RegisterCustomSounds(ci, + MAX_CUSTOM_BASIC_SOUNDS+MAX_CUSTOM_COMBAT_SOUNDS+MAX_CUSTOM_EXTRA_SOUNDS, // int iSoundEntryBase, + MAX_CUSTOM_JEDI_SOUNDS, // int iTableEntries, + cg_customJediSoundNames, // const char *ppsTable[], + ci->customJediSoundDir // const char *psDir + ); } } @@ -1083,7 +1081,7 @@ void CG_PlayerAnimSounds( animsounds_t *animSounds, int frame, int entNum ) int holdSnd = -1; qboolean playSound = qfalse; - if ( entNum == 0 && !cg_thirdPerson.integer ) + if ( entNum == 0 && !cg.renderingThirdPerson )//!cg_thirdPerson.integer ) {//player in first person view does not play any keyframed sounds return; } @@ -1134,18 +1132,22 @@ void CG_PlayerAnimSounds( animsounds_t *animSounds, int frame, int entNum ) } void CGG2_AnimSounds( centity_t *cent ) -{//FIXME: this doesn't work because Ghoul is skipping frames like crazy - if ( !cent || !cent->gent || !cent->gent->client ) +{ + if ( !cent || !cent->gent || !cent->gent->client) { return; } + assert(cent->gent->playerModel>=0&¢->gent->playerModelgent->ghoul2.size()); if ( ValidAnimFileIndex( cent->gent->client->clientInfo.animFileIndex ) ) { - int junk, curFrame; - float currentFrame, animSpeed; + int junk, curFrame=0; + float currentFrame=0, animSpeed; - gi.G2API_GetBoneAnimIndex( ¢->gent->ghoul2[cent->gent->playerModel], cent->gent->rootBone, cg.time, ¤tFrame, &junk, &junk, &junk, &animSpeed, cgs.model_draw ); - curFrame = floor( currentFrame ); + if (cent->gent->rootBone>=0&&gi.G2API_GetBoneAnimIndex( ¢->gent->ghoul2[cent->gent->playerModel], cent->gent->rootBone, cg.time, ¤tFrame, &junk, &junk, &junk, &animSpeed, cgs.model_draw )) + { + // the above may have failed, not sure what to do about it, current frame will be zero in that case + curFrame = floor( currentFrame ); + } if ( curFrame != cent->gent->client->renderInfo.legsFrame ) { CG_PlayerAnimSounds( level.knownAnimFileSets[cent->gent->client->clientInfo.animFileIndex].legsAnimSnds, curFrame, cent->currentState.clientNum ); @@ -1153,7 +1155,7 @@ void CGG2_AnimSounds( centity_t *cent ) cent->gent->client->renderInfo.legsFrame = curFrame; cent->pe.legs.frame = curFrame; - if ( gi.G2API_GetBoneAnimIndex(¢->gent->ghoul2[cent->gent->playerModel], cent->gent->lowerLumbarBone, cg.time, ¤tFrame, &junk, &junk, &junk, &animSpeed, cgs.model_draw ) ) + if (cent->gent->lowerLumbarBone>=0&& gi.G2API_GetBoneAnimIndex(¢->gent->ghoul2[cent->gent->playerModel], cent->gent->lowerLumbarBone, cg.time, ¤tFrame, &junk, &junk, &junk, &animSpeed, cgs.model_draw ) ) { curFrame = floor( currentFrame ); } @@ -1470,7 +1472,7 @@ static qboolean CG_CheckLookTarget( centity_t *cent, vec3_t lookAngles, float *l //FIXME: Ignore small deltas from current angles so we don't bob our head in synch with theirs? - if ( cent->gent->client->renderInfo.lookTarget == 0 && !cg_thirdPerson.integer ) + if ( cent->gent->client->renderInfo.lookTarget == 0 && !cg.renderingThirdPerson )//!cg_thirdPerson.integer ) {//Special case- use cg.refdef.vieworg if looking at player and not in third person view VectorCopy( cg.refdef.vieworg, lookOrg ); } @@ -1493,7 +1495,7 @@ static qboolean CG_CheckLookTarget( centity_t *cent, vec3_t lookAngles, float *l //Look in dir of lookTarget } } - else if ( cent->gent->client->renderInfo.lookMode == LM_ENT && cent->gent->client->renderInfo.lookTarget > -1 && cent->gent->client->renderInfo.lookTarget < MAX_INTEREST_POINTS ) + else if ( cent->gent->client->renderInfo.lookMode == LM_INTEREST && cent->gent->client->renderInfo.lookTarget > -1 && cent->gent->client->renderInfo.lookTarget < MAX_INTEREST_POINTS ) { VectorCopy( level.interestPoints[cent->gent->client->renderInfo.lookTarget].origin, lookOrg ); } @@ -1923,6 +1925,8 @@ Handles seperate torso motion =============== */ +extern int PM_TurnAnimForLegsAnim( gentity_t *gent, int anim ); +extern float PM_GetTimeScaleMod( gentity_t *gent ); void CG_G2PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t angles ) { vec3_t headAngles, neckAngles, chestAngles, thoracicAngles = {0,0,0};//legsAngles, torsoAngles, @@ -1948,6 +1952,11 @@ void CG_G2PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t angles ) // Dead entity if ( cent->gent && cent->gent->health <= 0 ) { + if ( cent->gent->hipsBone != -1 ) + { + gi.G2API_StopBoneAnimIndex( ¢->gent->ghoul2[cent->gent->playerModel], cent->gent->hipsBone ); + } + VectorCopy( cent->lerpAngles, angles ); BG_G2SetBoneAngles( cent, cent->gent, cent->gent->craniumBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw ); @@ -2017,6 +2026,45 @@ void CG_G2PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t angles ) } else { + if ( cg_turnAnims.integer && !in_camera && cent->gent->hipsBone >= 0 ) + { + //override the hips bone with a turn anim when turning + //and clear it when we're not... does blend from and to parent actually work? + int startFrame, endFrame; + const qboolean animatingHips = gi.G2API_GetAnimRangeIndex( ¢->gent->ghoul2[cent->gent->playerModel], cent->gent->hipsBone, &startFrame, &endFrame ); + + //FIXME: make legs lag behind when turning in place, only play turn anim when legs have to catch up + if ( angles[YAW] == cent->pe.legs.yawAngle ) + { + gi.G2API_StopBoneAnimIndex( ¢->gent->ghoul2[cent->gent->playerModel], cent->gent->hipsBone ); + } + else if ( VectorCompare( vec3_origin, cent->gent->client->ps.velocity ) ) + {//FIXME: because of LegsYawFromMovement, we play the turnAnims when we stop running, which looks really bad. + int turnAnim = PM_TurnAnimForLegsAnim( cent->gent, cent->gent->client->ps.legsAnim ); + if ( turnAnim != -1 && cent->gent->health > 0 ) + { + animation_t *animations = level.knownAnimFileSets[cent->gent->client->clientInfo.animFileIndex].animations; + + if ( !animatingHips || ( animations[turnAnim].firstFrame != startFrame ) )// only set the anim if we aren't going to do the same animation again + { + float animSpeed = 50.0f / animations[turnAnim].frameLerp * PM_GetTimeScaleMod( cent->gent ); + + gi.G2API_SetBoneAnimIndex( ¢->gent->ghoul2[cent->gent->playerModel], cent->gent->hipsBone, + animations[turnAnim].firstFrame, animations[turnAnim].firstFrame+animations[turnAnim].numFrames, + BONE_ANIM_OVERRIDE_LOOP/*|BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND*/, animSpeed, cg.time, -1, 100 ); + } + } + else + { + gi.G2API_StopBoneAnimIndex( ¢->gent->ghoul2[cent->gent->playerModel], cent->gent->hipsBone ); + } + } + else + { + gi.G2API_StopBoneAnimIndex( ¢->gent->ghoul2[cent->gent->playerModel], cent->gent->hipsBone ); + } + } + CG_G2ClientSpineAngles( cent, viewAngles, angles, thoracicAngles, ulAngles, llAngles ); } @@ -2029,10 +2077,8 @@ void CG_G2PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t angles ) } else { - //FIXME: we need to override the hips bone with a turn anim when turning - // and clear it when we're not... does beld from and to parent actually work? - //FIXME: this needs to properly set the legs.yawing field so we don't erroneously play the turning anim, but we do play it when turning in place + //set the legs.yawing field so we play the turning anim when turning in place if ( angles[YAW] == cent->pe.legs.yawAngle ) { cent->pe.legs.yawing = qfalse; @@ -2041,7 +2087,6 @@ void CG_G2PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t angles ) { cent->pe.legs.yawing = qtrue; } - cent->pe.legs.yawAngle = angles[YAW]; if ( cent->gent->client ) { @@ -2272,8 +2317,6 @@ void CG_G2PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t angles ) void CG_PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t torso[3], vec3_t head[3] ) { vec3_t legsAngles, torsoAngles, headAngles; - float speed; - vec3_t velocity; vec3_t lookAngles, viewAngles; float headYawClampMin, headYawClampMax; float headPitchClampMin, headPitchClampMax; @@ -2499,36 +2542,6 @@ void CG_PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t torso[3], vec3_t h torsoAngles[PITCH] = cent->pe.torso.pitchAngle; // --------- roll ------------- - // lean towards the direction of travel - if ( cent->gent->s.eFlags & EF_BANK_STRAFE ) - { - VectorCopy( cent->gent->client->ps.velocity, velocity ); - speed = VectorNormalize( velocity ); - - if ( speed ) - { - vec3_t axis[3]; - float side; - - // Magic number fun! Speed is used for banking, so modulate the speed by a sine wave - speed *= 0.4 * sin( (cg.time - cent->gent->fx_time + 200 ) * 0.003 ); - - // Clamp to prevent harsh rolling - if ( speed > 12 ) - speed = 12; - - AnglesToAxis( legsAngles, axis ); - side = speed * DotProduct( velocity, axis[1] ); - legsAngles[ROLL] -= side; - torsoAngles[ROLL] -= side; - - - side = speed * DotProduct( velocity, axis[0] ); - legsAngles[PITCH] += side; - torsoAngles[PITCH] += side; - } - } - // pain twitch - FIXME: don't do this if you have no head (like droids?) // Maybe need to have clamp angles for roll as well as pitch and yaw? //CG_AddPainTwitch( cent, torsoAngles ); @@ -3077,49 +3090,71 @@ static void CG_ForcePushBodyBlur( centity_t *cent, const vec3_t origin, vec3_t t CG_ForcePushBlur( cent->gent->client->renderInfo.eyePoint ); // Do a torso based blur - gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->torsoBolt, - &boltMatrix, tempAngles, origin, cg.time, - cgs.model_draw, cent->currentState.modelScale); - gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg ); - CG_ForcePushBlur( fxOrg ); + if (cent->gent->torsoBolt>=0) + { + gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->torsoBolt, + &boltMatrix, tempAngles, origin, cg.time, + cgs.model_draw, cent->currentState.modelScale); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg ); + CG_ForcePushBlur( fxOrg ); + } - // Do a right-hand based blur - gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->handRBolt, - &boltMatrix, tempAngles, origin, cg.time, - cgs.model_draw, cent->currentState.modelScale); - gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg ); - CG_ForcePushBlur( fxOrg ); + if (cent->gent->handRBolt>=0) + { + // Do a right-hand based blur + gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->handRBolt, + &boltMatrix, tempAngles, origin, cg.time, + cgs.model_draw, cent->currentState.modelScale); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg ); + CG_ForcePushBlur( fxOrg ); + } - // Do a left-hand based blur - gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->handLBolt, - &boltMatrix, tempAngles, origin, cg.time, - cgs.model_draw, cent->currentState.modelScale); - gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg ); - CG_ForcePushBlur( fxOrg ); + if (cent->gent->handLBolt>=0) + { + // Do a left-hand based blur + gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->handLBolt, + &boltMatrix, tempAngles, origin, cg.time, + cgs.model_draw, cent->currentState.modelScale); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg ); + CG_ForcePushBlur( fxOrg ); + } // Do the knees - gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->kneeLBolt, - &boltMatrix, tempAngles, origin, cg.time, - cgs.model_draw, cent->currentState.modelScale); - gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg ); - CG_ForcePushBlur( fxOrg ); - gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->kneeRBolt, - &boltMatrix, tempAngles, origin, cg.time, - cgs.model_draw, cent->currentState.modelScale); - gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg ); - CG_ForcePushBlur( fxOrg ); + if (cent->gent->kneeLBolt>=0) + { + gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->kneeLBolt, + &boltMatrix, tempAngles, origin, cg.time, + cgs.model_draw, cent->currentState.modelScale); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg ); + CG_ForcePushBlur( fxOrg ); + } - // Do the elbows - gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->elbowLBolt, - &boltMatrix, tempAngles, origin, cg.time, - cgs.model_draw, cent->currentState.modelScale); - gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg ); - CG_ForcePushBlur( fxOrg ); - gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->elbowRBolt, - &boltMatrix, tempAngles, origin, cg.time, - cgs.model_draw, cent->currentState.modelScale); - gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg ); - CG_ForcePushBlur( fxOrg ); + if (cent->gent->kneeRBolt>=0) + { + gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->kneeRBolt, + &boltMatrix, tempAngles, origin, cg.time, + cgs.model_draw, cent->currentState.modelScale); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg ); + CG_ForcePushBlur( fxOrg ); + } + + if (cent->gent->elbowLBolt>=0) + { + // Do the elbows + gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->elbowLBolt, + &boltMatrix, tempAngles, origin, cg.time, + cgs.model_draw, cent->currentState.modelScale); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg ); + CG_ForcePushBlur( fxOrg ); + } + if (cent->gent->elbowRBolt>=0) + { + gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->elbowRBolt, + &boltMatrix, tempAngles, origin, cg.time, + cgs.model_draw, cent->currentState.modelScale); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg ); + CG_ForcePushBlur( fxOrg ); + } } static void CG_ForceElectrocution( centity_t *cent, const vec3_t origin, vec3_t tempAngles ) @@ -3130,54 +3165,61 @@ static void CG_ForceElectrocution( centity_t *cent, const vec3_t origin, vec3_t vec3_t rgb = {1.0f,1.0f,1.0f}; mdxaBone_t boltMatrix; + int bolt=-1; + int iter=0; // Pick a random start point - switch( Q_irand(0,6)) + while (bolt<0) { - case 0: - // Right Elbow - found = gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->elbowRBolt, - &boltMatrix, tempAngles, origin, cg.time, - cgs.model_draw, cent->currentState.modelScale); - break; - case 1: - // Left Hand - found = gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->handLBolt, - &boltMatrix, tempAngles, origin, cg.time, - cgs.model_draw, cent->currentState.modelScale); - break; - case 2: - // Right hand - found = gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->handRBolt, - &boltMatrix, tempAngles, origin, cg.time, - cgs.model_draw, cent->currentState.modelScale); - break; - case 3: - // Left Foot - found = gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->footLBolt, - &boltMatrix, tempAngles, origin, cg.time, - cgs.model_draw, cent->currentState.modelScale); - break; - case 4: - // Right foot - found = gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->footRBolt, - &boltMatrix, tempAngles, origin, cg.time, - cgs.model_draw, cent->currentState.modelScale); - break; - case 5: - // Torso - found = gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->torsoBolt, - &boltMatrix, tempAngles, origin, cg.time, - cgs.model_draw, cent->currentState.modelScale); - break; - case 6: - default: - // Left Elbow - found = gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->elbowLBolt, - &boltMatrix, tempAngles, origin, cg.time, - cgs.model_draw, cent->currentState.modelScale); - break; + int test; + if (iter>5) + { + test=iter-5; + } + else + { + test=Q_irand(0,6); + } + switch(test) + { + case 0: + // Right Elbow + bolt=cent->gent->elbowRBolt; + break; + case 1: + // Left Hand + bolt=cent->gent->handLBolt; + break; + case 2: + // Right hand + bolt=cent->gent->handRBolt; + break; + case 3: + // Left Foot + bolt=cent->gent->footLBolt; + break; + case 4: + // Right foot + bolt=cent->gent->footRBolt; + break; + case 5: + // Torso + bolt=cent->gent->torsoBolt; + break; + case 6: + default: + // Left Elbow + bolt=cent->gent->elbowLBolt; + break; + } + if (++iter==20) + break; + } + if (bolt>=0) + { + found = gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, bolt, + &boltMatrix, tempAngles, origin, cg.time, + cgs.model_draw, cent->currentState.modelScale); } - // Make sure that it's safe to even try and get these values out of the Matrix, otherwise the values could be garbage if ( found ) { @@ -3211,6 +3253,9 @@ static void CG_ForceElectrocution( centity_t *cent, const vec3_t origin, vec3_t case CLASS_MARK1: fxOrg[2] += 50; break; + case CLASS_ATST: + fxOrg[2] += 120; + break; } } } @@ -3260,11 +3305,11 @@ void CG_AddRefEntityWithPowerups( refEntity_t *ent, int powerups, centity_t *cen } } - if ( gent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 ) - { - centity_t *cent = &cg_entities[gent->s.number]; - cgi_S_AddLoopingSound( 0, cent->lerpOrigin, vec3_origin, cgs.media.overchargeLoopSound ); - } +// if ( gent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 ) +// { +// centity_t *cent = &cg_entities[gent->s.number]; +// cgi_S_AddLoopingSound( 0, cent->lerpOrigin, vec3_origin, cgs.media.overchargeLoopSound ); +// } // If certain states are active, we don't want to add in the regular body if ( !gent->client->ps.powerups[PW_CLOAKED] && @@ -3528,7 +3573,7 @@ static void CG_G2SetHeadBlink( centity_t *cent, qboolean bStart ) gentity_t *gent = cent->gent; //FIXME: get these boneIndices game-side and pass it down? //FIXME: need a version of this that *doesn't* need the mFileName in the ghoul2 - const int hLeye = gi.G2API_GetBoneIndex( &gent->ghoul2[0], "leye", qtrue ); + const int hLeye = gi.G2API_GetBoneIndex( &gent->ghoul2[0], "leye", qfalse ); if (hLeye == -1) { return; @@ -3549,9 +3594,14 @@ static void CG_G2SetHeadBlink( centity_t *cent, qboolean bStart ) } gi.G2API_SetBoneAnglesIndex( &gent->ghoul2[gent->playerModel], hLeye, desiredAngles, BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL, blendTime, cg.time ); + const int hReye = gi.G2API_GetBoneIndex( &gent->ghoul2[0], "reye", qfalse ); + if (hReye == -1) + { + return; + } if (!bWink) - gi.G2API_SetBoneAngles( &gent->ghoul2[gent->playerModel], "reye", desiredAngles, + gi.G2API_SetBoneAnglesIndex( &gent->ghoul2[gent->playerModel], hReye, desiredAngles, BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL, blendTime, cg.time ); } @@ -4476,11 +4526,17 @@ Ghoul2 Insert End { if ( (trace.contents&CONTENTS_WATER) || (trace.contents&CONTENTS_SLIME) ) { + /* if ( !(cent->gent->client->ps.saberEventFlags&SEF_INWATER) ) { + } + */ + if ( !Q_irand( 0, 10 ) ) + {//FIXME: don't do this this way.... :) + G_PlayEffect( "saber/boil", trace.endpos ); cgi_S_StartSound ( trace.endpos, -1, CHAN_AUTO, cgi_S_RegisterSound( "sound/weapons/saber/hitwater.wav" ) ); } - cent->gent->client->ps.saberEventFlags |= SEF_INWATER; + //cent->gent->client->ps.saberEventFlags |= SEF_INWATER; //don't do other trace i = 1; } @@ -4733,7 +4789,7 @@ void CG_Player( centity_t *cent ) { return; } - if(cent->currentState.number == 0 && !cg_thirdPerson.integer ) + if(cent->currentState.number == 0 && !cg.renderingThirdPerson )//!cg_thirdPerson.integer ) { calcedMp = qtrue; } @@ -5009,7 +5065,7 @@ extern vmCvar_t cg_thirdPersonAlpha; && ( cg.snap->ps.weapon == WP_SABER || cg.snap->ps.weapon == WP_MELEE ) && !cent->gent->s.number ) {// Yeah um, this needs to not do this quite this way - ent.customSkin = cgi_R_RegisterSkin( "models/players/kyle/model_fpls2.skin" ); + ent.customSkin = cgi_R_RegisterSkin( "models/players/kyle/model_fpls2.skin" ); //precached in g_client.cpp //turn on arm caps gi.G2API_SetSurfaceOnOff( ¢->gent->ghoul2[cent->gent->playerModel], "r_arm_cap_torso_off", 0 ); gi.G2API_SetSurfaceOnOff( ¢->gent->ghoul2[cent->gent->playerModel], "l_arm_cap_torso_off", 0 ); @@ -5203,13 +5259,28 @@ extern vmCvar_t cg_thirdPersonAlpha; VectorMA( cent->gent->client->renderInfo.eyePoint, 8, tempAxis, cent->gent->client->renderInfo.headPoint ); } //Get torsoPoint & torsoAngles - gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->chestBolt, &boltMatrix, tempAngles, ent.origin, cg.time, cgs.model_draw, cent->currentState.modelScale ); - gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, cent->gent->client->renderInfo.torsoPoint ); - gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Z, tempAxis ); - vectoangles( tempAxis, cent->gent->client->renderInfo.torsoAngles ); + if (cent->gent->chestBolt>=0) + { + gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->chestBolt, &boltMatrix, tempAngles, ent.origin, cg.time, cgs.model_draw, cent->currentState.modelScale ); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, cent->gent->client->renderInfo.torsoPoint ); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Z, tempAxis ); + vectoangles( tempAxis, cent->gent->client->renderInfo.torsoAngles ); + } + else + { + VectorCopy( ent.origin, cent->gent->client->renderInfo.torsoPoint); + VectorClear(cent->gent->client->renderInfo.torsoAngles); + } //get crotchPoint - gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->crotchBolt, &boltMatrix, tempAngles, ent.origin, cg.time, cgs.model_draw, cent->currentState.modelScale ); - gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, cent->gent->client->renderInfo.crotchPoint ); + if (cent->gent->crotchBolt>=0) + { + gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->crotchBolt, &boltMatrix, tempAngles, ent.origin, cg.time, cgs.model_draw, cent->currentState.modelScale ); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, cent->gent->client->renderInfo.crotchPoint ); + } + else + { + VectorCopy( ent.origin, cent->gent->client->renderInfo.crotchPoint); + } if( !calcedMp ) { @@ -5490,7 +5561,7 @@ Ghoul2 Insert End renderfx |= RF_SHADOW_PLANE; } renderfx |= RF_LIGHTING_ORIGIN; // use the same origin for all - if ( cent->gent->NPC->scriptFlags & SCF_MORELIGHT ) + if ( cent->gent->NPC && cent->gent->NPC->scriptFlags & SCF_MORELIGHT ) { renderfx |= RF_MORELIGHT; //bigger than normal min light } @@ -5794,6 +5865,57 @@ Ghoul2 Insert End } //FIXME: for debug, allow to draw a cone of the NPC's FOV... + if ( cent->currentState.number == 0 && cg.renderingThirdPerson ) + { + playerState_t *ps = &cg.predicted_player_state; + + if (( ps->weaponstate == WEAPON_CHARGING_ALT && ps->weapon == WP_BRYAR_PISTOL ) + || ( ps->weapon == WP_BOWCASTER && ps->weaponstate == WEAPON_CHARGING ) + || ( ps->weapon == WP_DEMP2 && ps->weaponstate == WEAPON_CHARGING_ALT )) + { + int shader = 0; + float val = 0.0f, scale = 1.0f; + vec3_t WHITE = {1.0f,1.0f,1.0f}; + + if ( ps->weapon == WP_BRYAR_PISTOL ) + { + // Hardcoded max charge time of 1 second + val = ( cg.time - ps->weaponChargeTime ) * 0.001f; + shader = cgi_R_RegisterShader( "gfx/effects/bryarFrontFlash" ); + } + else if ( ps->weapon == WP_BOWCASTER ) + { + // Hardcoded max charge time of 1 second + val = ( cg.time - ps->weaponChargeTime ) * 0.001f; + shader = cgi_R_RegisterShader( "gfx/effects/greenFrontFlash" ); + } + else if ( ps->weapon == WP_DEMP2 ) + { + // Hardcoded max charge time of 1 second + val = ( cg.time - ps->weaponChargeTime ) * 0.001f; + shader = cgi_R_RegisterShader( "gfx/misc/lightningFlash" ); + scale = 1.75f; + } + + if ( val < 0.0f ) + { + val = 0.0f; + } + else if ( val > 1.0f ) + { + val = 1.0f; + CGCam_Shake( 0.1f, 100 ); + } + else + { + CGCam_Shake( val * val * 0.3f, 100 ); + } + + val += random() * 0.5f; + + FX_AddSprite( cent->gent->client->renderInfo.muzzlePoint, NULL, NULL, 3.0f * val * scale, 0.0f, 0.7f, 0.7f, WHITE, WHITE, random() * 360, 0.0f, 1.0f, shader, FX_USE_ALPHA ); + } + } } //===================================================================== diff --git a/code/cgame/cg_predict.cpp b/code/cgame/cg_predict.cpp index c84d9e1..26d228f 100644 --- a/code/cgame/cg_predict.cpp +++ b/code/cgame/cg_predict.cpp @@ -292,11 +292,13 @@ void CG_InterpolatePlayerState( qboolean grabAngles ) { playerState_t *out; snapshot_t *prev, *next; qboolean skip = qfalse; + vec3_t oldOrg; out = &cg.predicted_player_state; prev = cg.snap; next = cg.nextSnap; + VectorCopy(out->origin,oldOrg); *out = cg.snap->ps; // if we are still allowing local input, short circuit the view angles @@ -317,32 +319,43 @@ void CG_InterpolatePlayerState( qboolean grabAngles ) { } // if the next frame is a teleport, we can't lerp to it - if ( cg.nextFrameTeleport ) { + if ( cg.nextFrameTeleport ) + { return; } - if ( !next || next->serverTime <= prev->serverTime ) { - return; - } + if (!( !next || next->serverTime <= prev->serverTime ) ) + { - f = (float)( cg.time - prev->serverTime ) / ( next->serverTime - prev->serverTime ); - - i = next->ps.bobCycle; - if ( i < prev->ps.bobCycle ) { - i += 256; // handle wraparound - } - out->bobCycle = prev->ps.bobCycle + f * ( i - prev->ps.bobCycle ); - - for ( i = 0 ; i < 3 ; i++ ) { - out->origin[i] = prev->ps.origin[i] + f * (next->ps.origin[i] - prev->ps.origin[i] ); - if ( !grabAngles ) { - out->viewangles[i] = LerpAngle( - prev->ps.viewangles[i], next->ps.viewangles[i], f ); + f = (float)( cg.time - prev->serverTime ) / ( next->serverTime - prev->serverTime ); + + i = next->ps.bobCycle; + if ( i < prev->ps.bobCycle ) + { + i += 256; // handle wraparound } - out->velocity[i] = prev->ps.velocity[i] + - f * (next->ps.velocity[i] - prev->ps.velocity[i] ); - } + out->bobCycle = prev->ps.bobCycle + f * ( i - prev->ps.bobCycle ); + for ( i = 0 ; i < 3 ; i++ ) + { + out->origin[i] = prev->ps.origin[i] + f * (next->ps.origin[i] - prev->ps.origin[i] ); + if ( !grabAngles ) + { + out->viewangles[i] = LerpAngle( + prev->ps.viewangles[i], next->ps.viewangles[i], f ); + } + out->velocity[i] = prev->ps.velocity[i] + + f * (next->ps.velocity[i] - prev->ps.velocity[i] ); + } + } + if (cg.validPPS && cg_smoothPlayerPos.value>0.0f && cg_smoothPlayerPos.value<1.0f) + { + // 0 = no smoothing, 1 = no movement + for (i=0;i<3;i++) + { + out->origin[i]=cg_smoothPlayerPos.value*(oldOrg[i]-out->origin[i])+out->origin[i]; + } + } } /* @@ -496,7 +509,7 @@ void CG_PredictPlayerState( void ) { } - if ( cg_timescale.value >= 1.0f ) + if ( 1 )//cg_timescale.value >= 1.0f ) { // demo playback just copies the moves /* diff --git a/code/cgame/cg_scoreboard.cpp b/code/cgame/cg_scoreboard.cpp index e01a039..86f4c36 100644 --- a/code/cgame/cg_scoreboard.cpp +++ b/code/cgame/cg_scoreboard.cpp @@ -140,6 +140,9 @@ void CG_MissionFailed(void) case MISSIONFAILED_KYLECAPTURE: cgi_SP_GetStringTextString( "INGAME_MISSIONFAILED_KYLECAPTURE", text, sizeof(text) ); break; + case MISSIONFAILED_TOOMANYALLIESDIED: + cgi_SP_GetStringTextString( "INGAME_MISSIONFAILED_TOOMANYALLIESDIED:", text, sizeof(text) ); + break; default: cgi_SP_GetStringTextString( "INGAME_MISSIONFAILED_UNKNOWN", text, sizeof(text) ); break; @@ -181,13 +184,14 @@ w = cgi_R_Font_StrLenPixels(text, cgs.media.qhFontSmall, 0.8f); cgi_R_Font_DrawString(x+w, y, va("%d of %d", cg_entities[0].gent->client->sess.missionStats.secretsFound, cg_entities[0].gent->client->sess.missionStats.totalSecrets), colorTable[CT_WHITE], cgs.media.qhFontSmall, -1, 0.8f); - y +=20; + y +=pad; cgi_SP_GetStringTextString( "INGAME_ENEMIESKILLED", text, sizeof(text) ); w = cgi_R_Font_StrLenPixels(text, cgs.media.qhFontSmall, 0.8f); cgi_R_Font_DrawString(x, y, text, colorTable[CT_LTGOLD1], cgs.media.qhFontSmall, -1, 0.8f); cgi_R_Font_DrawString(x+w,y, va("%d",cg_entities[0].gent->client->sess.missionStats.enemiesKilled), colorTable[CT_WHITE], cgs.media.qhFontSmall, -1, 0.8f); - y +=20; + y +=pad; + y +=pad; cgi_SP_GetStringTextString( "INGAME_FAVORITEWEAPON", text, sizeof(text) ); w = cgi_R_Font_StrLenPixels(text, cgs.media.qhFontSmall, 0.8f); cgi_R_Font_DrawString(x, y, text, colorTable[CT_LTGOLD1], cgs.media.qhFontSmall, -1, 0.8f); @@ -202,10 +206,16 @@ w = cgi_R_Font_StrLenPixels(text, cgs.media.qhFontSmall, 0.8f); wpn = i; } } - cgi_R_Font_DrawString(x+w, y, va("%d",wpn), colorTable[CT_WHITE], cgs.media.qhFontSmall, -1, 0.8f); + if ( wpn ) + { + gitem_t *wItem= FindItemForWeapon( (weapon_t)wpn); + cgi_SP_GetStringTextString( va("INGAME_%s",wItem->classname ), text, sizeof( text )); + // cgi_R_Font_DrawString(x+w, y, va("%d",wpn), colorTable[CT_WHITE], cgs.media.qhFontSmall, -1, 0.8f); + cgi_R_Font_DrawString(x+w, y, text, colorTable[CT_WHITE], cgs.media.qhFontSmall, -1, 0.8f); + } - x = 334; + x = 334+70; y = 86; cgi_SP_GetStringTextString( "INGAME_SHOTSFIRED", text, sizeof(text) ); w = cgi_R_Font_StrLenPixels(text, cgs.media.qhFontSmall, 0.8f); @@ -230,7 +240,7 @@ w = cgi_R_Font_StrLenPixels(text, cgs.media.qhFontSmall, 0.8f); y =180; cgi_SP_GetStringTextString( "INGAME_FORCEUSE", text, sizeof(text) ); - cgi_R_Font_DrawString(x, y, text, colorTable[CT_LTGOLD1], cgs.media.qhFontSmall, -1, 0.8f); + cgi_R_Font_DrawString(x, y, text, colorTable[CT_WHITE], cgs.media.qhFontSmall, -1, 0.8f); y +=pad; cgi_SP_GetStringTextString( "INGAME_HEAL", text, sizeof(text) ); diff --git a/code/cgame/cg_servercmds.cpp b/code/cgame/cg_servercmds.cpp index 3251f38..6237a2c 100644 --- a/code/cgame/cg_servercmds.cpp +++ b/code/cgame/cg_servercmds.cpp @@ -34,6 +34,10 @@ void CG_ParseServerinfo( void ) { { cgs.stripLevelName[i][0]='\0'; } + // be careful with the []-numbers here. Currently I use 0,1,2 for replacements or substitution, and [3] for "INGAME" + // I know, if I'd known there was going to be this much messing about I'd have subroutinised it all and done it + // neater, but it kinda evolved... Feel free to bug me if you want to add to it... ? -Ste. + // if (!cgi_SP_Register(cgs.stripLevelName[0], qfalse)) { // failed to load SP file, maybe it's one of the ones they renamed?... diff --git a/code/cgame/cg_text.cpp b/code/cgame/cg_text.cpp index 9287750..058df65 100644 --- a/code/cgame/cg_text.cpp +++ b/code/cgame/cg_text.cpp @@ -383,6 +383,13 @@ static int cg_SP_GetStringTextStringWithRetry( LPCSTR psReference, char *psDest, { int iReturn; + if (psReference[0] == '#') + { + // then we know the striped package name is already built in, so do NOT try prepending anything else... + // + return cgi_SP_GetStringTextString( va("%s",psReference+1), psDest, iSizeofDest ); + } + for (int i=0; ips ) ) + { + float perc, animLen = (float)PM_AnimLength( g_entities[0].client->clientInfo.animFileIndex, (animNumber_t)g_entities[0].client->ps.legsAnim ); + if ( PM_InGetUp( &g_entities[0].client->ps ) || PM_InForceGetUp( &g_entities[0].client->ps ) ) + {//start righting the view + perc = (float)g_entities[0].client->ps.legsAnimTimer/animLen*2; + } + else + {//tilt the view + perc = (animLen-g_entities[0].client->ps.legsAnimTimer)/animLen*2; + } + if ( perc > 1.0f ) + { + perc = 1.0f; + } + angles[ROLL] = perc*40; + angles[PITCH] = perc*-15; + } + // add angles based on weapon kick VectorAdd (angles, cg.kick_angles, angles); @@ -1157,6 +1180,27 @@ qboolean CG_CalcFOVFromX( float fov_x ) return (inwater); } + +float CG_ForceSpeedFOV( void ) +{ + float fov; + float timeLeft = player->client->ps.forcePowerDuration[FP_SPEED] - cg.time; + float length = FORCE_SPEED_DURATION*forceSpeedValue[player->client->ps.forcePowerLevel[FP_SPEED]]; + float amt = forceSpeedFOVMod[player->client->ps.forcePowerLevel[FP_SPEED]]; + if ( timeLeft < 500 ) + {//start going back + fov = cg_fov.value + (timeLeft)/500*amt; + } + else if ( length - timeLeft < 1000 ) + {//start zooming in + fov = cg_fov.value + (length - timeLeft)/1000*amt; + } + else + {//stay at this FOV + fov = cg_fov.value+amt; + } + return fov; +} /* ==================== CG_CalcFov @@ -1202,23 +1246,9 @@ static qboolean CG_CalcFov( void ) { } } } - else if ( cg.renderingThirdPerson && (cg.snap->ps.forcePowersActive&(1<client->ps.forcePowerDuration[FP_SPEED] ) + else if ( (cg.snap->ps.forcePowersActive&(1<client->ps.forcePowerDuration[FP_SPEED] )//cg.renderingThirdPerson && { - float timeLeft = player->client->ps.forcePowerDuration[FP_SPEED] - cg.time; - float length = FORCE_SPEED_DURATION*forceSpeedValue[player->client->ps.forcePowerLevel[FP_SPEED]]; - float amt = forceSpeedFOVMod[player->client->ps.forcePowerLevel[FP_SPEED]]; - if ( timeLeft < 500 ) - {//start going back - fov_x = cg_fov.value + (timeLeft)/500*amt; - } - else if ( length - timeLeft < 1000 ) - {//start zooming in - fov_x = cg_fov.value + (length - timeLeft)/1000*amt; - } - else - {//stay at this FOV - fov_x = cg_fov.value+amt; - } + fov_x = CG_ForceSpeedFOV(); } else { // user selectable if ( cg.overrides.active & CG_OVERRIDE_FOV ) @@ -1419,11 +1449,9 @@ static qboolean CG_CalcViewValues( void ) { CG_CalcVrect(); ps = &cg.predicted_player_state; - -#if _DEBUG +#ifndef FINAL_BUILD trap_Com_SetOrgAngles(ps->origin,ps->viewangles); #endif - // intermission view if ( ps->pm_type == PM_INTERMISSION ) { VectorCopy( ps->origin, cg.refdef.vieworg ); @@ -1537,6 +1565,7 @@ static qboolean CG_CalcViewValues( void ) { //VectorCopy( cg.refdef.vieworg, cgRefdefVieworg ); // shake the camera if necessary + CGCam_UpdateSmooth( cg.refdef.vieworg, cg.refdefViewAngles ); CGCam_UpdateShake( cg.refdef.vieworg, cg.refdefViewAngles ); // see if we are drugged by an interrogator. We muck with the angles here, just a bit earlier, we mucked with the FOV @@ -1577,12 +1606,12 @@ static void CG_PowerupTimerSounds( void ) if ( time > 0 && time < cg.time ) { // add any special powerup expiration sounds here - switch( i ) - { - case PW_WEAPON_OVERCHARGE: - cgi_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_ITEM, cgs.media.overchargeEndSound ); - break; - } +// switch( i ) +// { +// case PW_WEAPON_OVERCHARGE: +// cgi_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_ITEM, cgs.media.overchargeEndSound ); +// break; +// } } } } @@ -1680,6 +1709,18 @@ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView ) { //FIXME: should really send forcePowersActive over network onto cg.snap->ps... float speed = cg.refdef.fov_y / 75.0 * ((cg_entities[0].gent->client->ps.forcePowersActive&(1<client->ps.forcePowersActive&(1<s.eFlags & EF_LOCKED_TO_WEAPON && cg.snap->ps.clientNum == 0 ) { @@ -1694,7 +1735,7 @@ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView ) { CG_PredictPlayerState(); // decide on third person view - cg.renderingThirdPerson = cg_thirdPerson.integer || (cg.snap->ps.stats[STAT_HEALTH] <= 0); + cg.renderingThirdPerson = cg_thirdPerson.integer || (cg.snap->ps.stats[STAT_HEALTH] <= 0) || (g_entities[0].client&&g_entities[0].client->NPC_class==CLASS_ATST); if ( cg.zoomMode ) { @@ -1759,9 +1800,13 @@ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView ) { CG_RunEmplacedWeapon(); // Don't draw the in-view weapon when in camera mode - if ( !in_camera && !cg_pano.integer && - ( cg.snap->ps.viewEntity == 0 || cg.snap->ps.viewEntity >= ENTITYNUM_WORLD ) ) + if ( !in_camera + && !cg_pano.integer + && cg.snap->ps.weapon != WP_SABER + && ( cg.snap->ps.viewEntity == 0 || cg.snap->ps.viewEntity >= ENTITYNUM_WORLD ) ) + { CG_AddViewWeapon( &cg.predicted_player_state ); + } if ( !cg.hyperspace ) { diff --git a/code/cgame/cg_weapons.cpp b/code/cgame/cg_weapons.cpp index cefea2c..77ce5d7 100644 --- a/code/cgame/cg_weapons.cpp +++ b/code/cgame/cg_weapons.cpp @@ -222,7 +222,10 @@ void CG_RegisterWeapon( int weaponNum ) { { cgi_S_RegisterSound( va( "sound/weapons/saber/saberbounce%d.wav", i ) ); } - cgi_S_RegisterSound( "sound/weapons/saber/saberhit.wav" ); + for ( i = 1; i < 4; i++ ) + { + cgi_S_RegisterSound( va( "sound/weapons/saber/saberhit%d.wav", i ) ); + } for ( i = 1; i < 4; i++ ) { cgi_S_RegisterSound( va( "sound/weapons/saber/saberhitwall%d.wav", i ) ); @@ -235,11 +238,15 @@ void CG_RegisterWeapon( int weaponNum ) { { cgi_S_RegisterSound( va( "sound/weapons/saber/saberhum%d.wav", i ) ); } - for ( i = 1; i < 9; i++ ) + for ( i = 1; i < 10; i++ ) { cgi_S_RegisterSound( va( "sound/weapons/saber/saberhup%d.wav", i ) ); } - cgi_S_RegisterSound( "sound/weapons/saber/saberspin.wav" ); + for ( i = 1; i < 4; i++ ) + { + cgi_S_RegisterSound( va( "sound/weapons/saber/saberspin%d.wav", i ) ); + } + cgi_S_RegisterSound( "sound/weapons/saber/saber_catch.wav" ); for ( i = 1; i < 4; i++ ) { cgi_S_RegisterSound( va( "sound/weapons/saber/bounce%d.wav", i ) ); @@ -349,6 +356,7 @@ void CG_RegisterWeapon( int weaponNum ) { cgs.effects.bowcasterShotEffect = theFxScheduler.RegisterEffect( "bowcaster/shot" ); cgs.effects.bowcasterBounceEffect = theFxScheduler.RegisterEffect( "bowcaster/bounce" ); cgs.effects.bowcasterImpactEffect = theFxScheduler.RegisterEffect( "bowcaster/explosion" ); + theFxScheduler.RegisterEffect( "bowcaster/deflect" ); break; case WP_REPEATER: @@ -518,10 +526,29 @@ void CG_RegisterItemVisuals( int itemNum ) { // itemInfo->icon = cgi_R_RegisterShaderNoMip( item->icon ); - if ( item->giType == IT_WEAPON ) { + if ( item->giType == IT_WEAPON ) + { CG_RegisterWeapon( item->giTag ); } + // some ammo types are actually the weapon, like in the case of explosives + if ( item->giType == IT_AMMO ) + { + switch( item->giTag ) + { + case AMMO_THERMAL: + CG_RegisterWeapon( WP_THERMAL ); + break; + case AMMO_TRIPMINE: + CG_RegisterWeapon( WP_TRIP_MINE ); + break; + case AMMO_DETPACK: + CG_RegisterWeapon( WP_DET_PACK ); + break; + } + } + + if ( item->giType == IT_HOLDABLE ) { // This should be set up to actually work. @@ -557,6 +584,13 @@ void CG_RegisterItemVisuals( int itemNum ) { cgs.media.laGogglesBracket = cgi_R_RegisterShader( "gfx/2d/bracket" ); cgs.media.laGogglesArrow = cgi_R_RegisterShader( "gfx/2d/bracket2" ); break; + + case INV_BACTA_CANISTER: + for ( int i = 1; i < 5; i++ ) + { + cgi_S_RegisterSound( va( "sound/weapons/force/heal%d.mp3", i )); + } + break; } } } @@ -574,6 +608,13 @@ VIEW WEAPON ================= CG_MapTorsoToWeaponFrame +animations MUST match the defined pattern! +the weapon hand animation has 3 anims, + 6 frames of attack + 4 frames of drop + 5 frames of raise + + if the torso anim does not match these lengths, it will not animate correctly! ================= */ extern qboolean ValidAnimFileIndex ( int index ); @@ -585,36 +626,55 @@ int CG_MapTorsoToWeaponFrame( const clientInfo_t *ci, int frame, int animNum, in return 0; } animation_t *animations = level.knownAnimFileSets[ci->animFileIndex].animations; + int ret=0; switch( animNum ) { + case TORSO_WEAPONREADY3: + ret = 0; + break; + case TORSO_DROPWEAP1: if ( frame >= animations[animNum].firstFrame && frame < animations[animNum].firstFrame + 5 ) { - return frame - animations[animNum].firstFrame + 6; + ret = frame - animations[animNum].firstFrame + 6; + } + else + { +// assert(0); } break; case TORSO_RAISEWEAP1: if ( frame >= animations[animNum].firstFrame && frame < animations[animNum].firstFrame + 4 ) { - return frame - animations[animNum].firstFrame + 6 + 4; + ret = frame - animations[animNum].firstFrame + 6 + 5; + } + else + { +// assert(0); } break; case BOTH_ATTACK1: case BOTH_ATTACK2: case BOTH_ATTACK3: + case BOTH_ATTACK4: if ( frame >= animations[animNum].firstFrame && frame < animations[animNum].firstFrame + 6 ) { - return 1 + ( frame - animations[animNum].firstFrame ); + ret = 1 + ( frame - animations[animNum].firstFrame ); } - + else + { +// assert(0); + } + break; + default: break; } - return 0; -} + return ret; +} /* ============== @@ -780,6 +840,7 @@ Add the weapon, and flash for the player's view ============== */ extern int PM_TorsoAnimForFrame( gentity_t *ent, int torsoFrame ); +extern float CG_ForceSpeedFOV( void ); void CG_AddViewWeapon( playerState_t *ps ) { @@ -859,7 +920,16 @@ void CG_AddViewWeapon( playerState_t *ps ) } // drop gun lower at higher fov - float actualFOV = (cg.overrides.active&CG_OVERRIDE_FOV) ? cg.overrides.fov : cg_fov.value; + float actualFOV; + if ( (cg.snap->ps.forcePowersActive&(1<client->ps.forcePowerDuration[FP_SPEED] )//cg.renderingThirdPerson && + { + actualFOV = CG_ForceSpeedFOV(); + } + else + { + actualFOV = (cg.overrides.active&CG_OVERRIDE_FOV) ? cg.overrides.fov : cg_fov.value; + } + if ( actualFOV > 80 ) { fovOffset = -0.1 * ( actualFOV - 80 ); @@ -895,15 +965,27 @@ void CG_AddViewWeapon( playerState_t *ps ) { // get clientinfo for animation map const clientInfo_t *ci = ¢->gent->client->clientInfo; - int torsoAnim = PM_TorsoAnimForFrame( cent->gent, cent->pe.torso.frame ); - hand.frame = CG_MapTorsoToWeaponFrame( ci, cent->pe.torso.frame, torsoAnim, cent->currentState.weapon, ( cent->currentState.eFlags & EF_FIRING ) ); - hand.oldframe = CG_MapTorsoToWeaponFrame( ci, cent->pe.torso.oldFrame, torsoAnim, cent->currentState.weapon, ( cent->currentState.eFlags & EF_FIRING ) ); - hand.backlerp = cent->pe.torso.backlerp; - if ( hand.frame && cg_debugAnim.integer == 1 && cent->currentState.clientNum == 0 ) + int torsoAnim = cent->pe.torso.animationNumber; + float currentFrame; + int startFrame,endFrame,flags; + float animSpeed; + if (cent->gent->lowerLumbarBone>=0&& gi.G2API_GetBoneAnimIndex(¢->gent->ghoul2[cent->gent->playerModel], cent->gent->lowerLumbarBone, cg.time, ¤tFrame, &startFrame, &endFrame, &flags, &animSpeed,0) ) { - Com_Printf( "Torso frame %d to %d makes Weapon frame %d to %d\n", cent->pe.torso.oldFrame, cent->pe.torso.frame, hand.oldframe, hand.frame ); + hand.oldframe = CG_MapTorsoToWeaponFrame( ci,floor(currentFrame), torsoAnim, cent->currentState.weapon, ( cent->currentState.eFlags & EF_FIRING ) ); + hand.frame = CG_MapTorsoToWeaponFrame( ci,ceil(currentFrame), torsoAnim, cent->currentState.weapon, ( cent->currentState.eFlags & EF_FIRING ) ); + hand.backlerp=1.0f-(currentFrame-floor(currentFrame)); +// if ( cg_debugAnim.integer == 1 && cent->currentState.clientNum == 0 ) +// { +// Com_Printf( "Torso frame %d to %d makes Weapon frame %d to %d\n", cent->pe.torso.oldFrame, cent->pe.torso.frame, hand.oldframe, hand.frame ); +// } + } + else + { +// assert(0); // no idea what to do here + hand.oldframe=0; + hand.frame=0; + hand.backlerp=0.0f; } - } // add the weapon @@ -990,7 +1072,7 @@ void CG_AddViewWeapon( playerState_t *ps ) CG_DoMuzzleFlash( cent, flash.origin, flash.axis[0], wData ); - if (cent->gent && cent->gent->client) + if ( cent->gent && cent->gent->client ) { VectorCopy(flash.origin, cent->gent->client->renderInfo.muzzlePoint); VectorCopy(flash.axis[0], cent->gent->client->renderInfo.muzzleDir); @@ -1046,8 +1128,7 @@ void CG_AddViewWeapon( playerState_t *ps ) val += random() * 0.5f; - // FIXME! This shader should technically be registered....but it's ugly and it should be registered properly just in case - FX_AddSprite( flash.origin, NULL, NULL, 3.0f * val * scale, 0.0f, 0.7f, 0.7f, WHITE, WHITE, random() * 360, 0.0f, 1.0f, shader, FX_USE_ALPHA ); + FX_AddSprite( flash.origin, NULL, NULL, 3.0f * val * scale, 0.0f, 0.7f, 0.7f, WHITE, WHITE, random() * 360, 0.0f, 1.0f, shader, FX_USE_ALPHA | FX_DEPTH_HACK ); } // Check if the heavy repeater is finishing up a sustained burst @@ -1624,17 +1705,16 @@ CG_DrawWeaponSelect */ void CG_DrawWeaponSelect( void ) { - int i; - int bits; - int count; - char *name; - int smallIconSize,bigIconSize; - int holdX,x,y,x2,y2,pad; - int sideLeftIconCnt,sideRightIconCnt; - int sideMax,holdCount,iconCnt; - int height; - vec4_t calcColor; - vec4_t textColor = { .875f, .718f, .121f, 1.0f }; + int i; + int bits; + int count; + int smallIconSize,bigIconSize; + int holdX,x,y,x2,y2,pad; + int sideLeftIconCnt,sideRightIconCnt; + int sideMax,holdCount,iconCnt; + int height; + vec4_t calcColor; + vec4_t textColor = { .875f, .718f, .121f, 1.0f }; if (!cgi_UI_GetMenuInfo("weaponselecthud",&x2,&y2)) { @@ -1815,17 +1895,22 @@ void CG_DrawWeaponSelect( void ) } } + gitem_t *item = cg_weapons[ cg.weaponSelect ].item; + // draw the selected name - if ( cg_weapons[ cg.weaponSelect ].item ) + if ( item && item->classname && item->classname[0] ) { - name = cg_weapons[ cg.weaponSelect ].item->pickup_name; - if ( name ) + char itemName[256], data[1024]; + + sprintf( itemName, "INGAME_%s", item->classname ); + + if ( cgi_SP_GetStringTextString( itemName, data, sizeof( data ))) { // Just doing this for now...... //#ifdef _DEBUG - int w = cgi_R_Font_StrLenPixels(name, cgs.media.qhFontSmall, 1.0f); + int w = cgi_R_Font_StrLenPixels(data, cgs.media.qhFontSmall, 1.0f); int x = ( SCREEN_WIDTH - w ) / 2; - cgi_R_Font_DrawString(x, (SCREEN_HEIGHT - 24), name, textColor, cgs.media.qhFontSmall, -1, 1.0f); + cgi_R_Font_DrawString(x, (SCREEN_HEIGHT - 24), data, textColor, cgs.media.qhFontSmall, -1, 1.0f); //#endif } } @@ -1839,7 +1924,7 @@ void CG_DrawWeaponSelect( void ) CG_WeaponSelectable =============== */ -qboolean CG_WeaponSelectable( int i, int original ) +qboolean CG_WeaponSelectable( int i, int original, qboolean dpMode ) { int usage_for_weap; @@ -1862,7 +1947,7 @@ qboolean CG_WeaponSelectable( int i, int original ) return qfalse; } - if ( weaponData[i].ammoIndex != AMMO_NONE ) + if (( weaponData[i].ammoIndex != AMMO_NONE ) && !dpMode ) {//weapon uses ammo, see if we have any usage_for_weap = weaponData[i].energyPerShot < weaponData[i].altEnergyPerShot ? weaponData[i].energyPerShot @@ -1989,7 +2074,7 @@ void CG_NextWeapon_f( void ) { cg.weaponSelect = FIRST_WEAPON; } - if ( CG_WeaponSelectable( cg.weaponSelect, original ) ) + if ( CG_WeaponSelectable( cg.weaponSelect, original, qfalse ) ) { // cg.weaponSelectTime = cg.time; SetWeaponSelectTime(); @@ -2028,7 +2113,7 @@ void CG_DPNextWeapon_f( void ) { cg.DataPadWeaponSelect = FIRST_WEAPON; } - if ( CG_WeaponSelectable( cg.DataPadWeaponSelect, original ) ) + if ( CG_WeaponSelectable( cg.DataPadWeaponSelect, original, qtrue ) ) { return; } @@ -2069,7 +2154,7 @@ void CG_DPPrevWeapon_f( void ) cg.DataPadWeaponSelect = MAX_PLAYER_WEAPONS; } - if ( CG_WeaponSelectable( cg.DataPadWeaponSelect, original ) ) + if ( CG_WeaponSelectable( cg.DataPadWeaponSelect, original, qtrue ) ) { return; } @@ -2134,7 +2219,7 @@ void CG_PrevWeapon_f( void ) { cg.weaponSelect = MAX_PLAYER_WEAPONS; } - if ( CG_WeaponSelectable( cg.weaponSelect, original ) ) + if ( CG_WeaponSelectable( cg.weaponSelect, original, qfalse ) ) { SetWeaponSelectTime(); // cg.weaponSelectTime = cg.time; @@ -2274,7 +2359,7 @@ void CG_Weapon_f( void ) { weap = WP_THERMAL; } - if ( CG_WeaponSelectable( weap, cg.snap->ps.weapon ) ) + if ( CG_WeaponSelectable( weap, cg.snap->ps.weapon, qfalse ) ) { num = weap; break; @@ -2336,7 +2421,7 @@ void CG_OutOfAmmoChange( void ) { original = cg.weaponSelect; for ( i = WP_DET_PACK; i > 0 ; i-- ) { //We don't want the emplaced, or melee here - if ( original != i && CG_WeaponSelectable( i, original ) ) + if ( original != i && CG_WeaponSelectable( i, original, qfalse ) ) { if ( 1 == cg_autoswitch.integer && ( i == WP_TRIP_MINE || i == WP_DET_PACK || i == WP_THERMAL || i == WP_ROCKET_LAUNCHER) ) // safe weapon switch @@ -2454,7 +2539,7 @@ void CG_FireWeapon( centity_t *cent, qboolean alt_fire ) } // Do overcharge sound that get's added to the top - if (( ent->powerups & ( 1<powerups & ( 1<value; + cinTable[currentHandle].startTime = cinTable[currentHandle].lastTime = Sys_Milliseconds()*com_timescale->value; } if (cinTable[currentHandle].numQuads != 1) cinTable[currentHandle].numQuads = 0; break; @@ -1158,7 +1158,7 @@ redump: static void RoQ_init( void ) { - cinTable[currentHandle].startTime = cinTable[currentHandle].lastTime = Com_Milliseconds()*com_timescale->value; + cinTable[currentHandle].startTime = cinTable[currentHandle].lastTime = Sys_Milliseconds()*com_timescale->value; cinTable[currentHandle].RoQPlayed = 24; @@ -1316,11 +1316,11 @@ e_status CIN_RunCinematic (int handle) return cinTable[currentHandle].status; } - thisTime = Com_Milliseconds()*com_timescale->value; + thisTime = Sys_Milliseconds()*com_timescale->value; if (cinTable[currentHandle].shader && (abs(thisTime - cinTable[currentHandle].lastTime))>100) { cinTable[currentHandle].startTime += thisTime - cinTable[currentHandle].lastTime; } - cinTable[currentHandle].tfps = ((((Com_Milliseconds()*com_timescale->value) - cinTable[currentHandle].startTime)*cinTable[currentHandle].roqFPS)/1000); + cinTable[currentHandle].tfps = ((((Sys_Milliseconds()*com_timescale->value) - cinTable[currentHandle].startTime)*cinTable[currentHandle].roqFPS)/1000); start = cinTable[currentHandle].startTime; while( (cinTable[currentHandle].tfps != cinTable[currentHandle].numQuads) @@ -1328,7 +1328,7 @@ e_status CIN_RunCinematic (int handle) { RoQInterrupt(); if (start != cinTable[currentHandle].startTime) { - cinTable[currentHandle].tfps = ((((Com_Milliseconds()*com_timescale->value) + cinTable[currentHandle].tfps = ((((Sys_Milliseconds()*com_timescale->value) - cinTable[currentHandle].startTime)*cinTable[currentHandle].roqFPS)/1000); start = cinTable[currentHandle].startTime; } @@ -1601,7 +1601,7 @@ static void PlayCinematic(const char *arg, const char *s, qboolean qbInGame) // work out associated audio-overlay file, if any... // extern cvar_t *s_language; - qboolean bIsForeign = s_language && stricmp(s_language->string,"english"); + qboolean bIsForeign = s_language && !(!stricmp(s_language->string,"english") || !stricmp(s_language->string,"")); LPCSTR psAudioFile = NULL; if (bIsForeign && !stricmp(arg,"video/jk05.roq")) { @@ -1617,7 +1617,6 @@ static void PlayCinematic(const char *arg, const char *s, qboolean qbInGame) // //////////////////////////////////////////////////////////////////// - CL_handle = CIN_PlayCinematic( arg, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, bits, psAudioFile ); if (CL_handle >= 0) { diff --git a/code/client/cl_keys.cpp b/code/client/cl_keys.cpp index a30bdb2..0349c88 100644 --- a/code/client/cl_keys.cpp +++ b/code/client/cl_keys.cpp @@ -793,6 +793,11 @@ void Console_Key (int key) { return; } +/* extern qboolean SwallowBadNumLockedKPKey( int iKey ); + if (SwallowBadNumLockedKPKey(key)){ + return; + } +*/ // enter finishes the line if ( key == K_ENTER || key == K_KP_ENTER ) { Cbuf_AddText( g_consoleField.buffer ); // valid command @@ -1253,10 +1258,16 @@ void CL_KeyEvent (int key, qboolean down, unsigned time) { // console key is hardcoded, so the user can never unbind it if (key == '`' || key == '~') { - if (!down) { + if (!down ) { return; } +#ifdef FINAL_BUILD + if (!(cls.keyCatchers & KEYCATCH_CONSOLE) && !keys[K_SHIFT].down ) //we're not in the console + {//so we require the control keys to get in + return; + } +#endif Con_ToggleConsole_f (); return; } diff --git a/code/client/cl_main.cpp b/code/client/cl_main.cpp index 12b2329..cd82605 100644 --- a/code/client/cl_main.cpp +++ b/code/client/cl_main.cpp @@ -435,6 +435,9 @@ void CL_Snd_Restart_f( void ) { s_soundMuted = qfalse; // we can play again S_RestartMusic(); + + extern void S_ReloadAllUsedSounds(void); + S_ReloadAllUsedSounds(); } /* ================== diff --git a/code/client/cl_mp3.cpp b/code/client/cl_mp3.cpp index e49acab..074aa3b 100644 --- a/code/client/cl_mp3.cpp +++ b/code/client/cl_mp3.cpp @@ -40,7 +40,9 @@ int MP3_GetUnpackedSize( const char *psLocalFilename, void *pvData, int iDataLen { int iUnpackedSize = 0; - if (qbIgnoreID3Tag || !MP3_ReadSpecialTagInfo((byte *)pvData, iDataLen, NULL, &iUnpackedSize)) + // always do this now that we have fast-unpack code for measuring output size... (much safer than relying on tags that may have been edited, or if MP3 has been re-saved with same tag) + // + if (1)//qbIgnoreID3Tag || !MP3_ReadSpecialTagInfo((byte *)pvData, iDataLen, NULL, &iUnpackedSize)) { char *psError = C_MP3_GetUnpackedSize( pvData, iDataLen, &iUnpackedSize, bStereoDesired); @@ -159,9 +161,10 @@ const char sKEY_UNCOMP[]="#UNCOMP"; // " " // returns qtrue for success... // -qboolean MP3_ReadSpecialTagInfo(byte *pbLoadedFile, int iLoadedFileLen, // (in) - id3v1_1** ppTAG, // (out), can be NULL - int *piUncompressedSize, float *pfMaxVol // (out), can be NULL +qboolean MP3_ReadSpecialTagInfo(byte *pbLoadedFile, int iLoadedFileLen, + id3v1_1** ppTAG /* = NULL */, + int *piUncompressedSize /* = NULL */, + float *pfMaxVol /* = NULL */ ) { qboolean qbError = qfalse; diff --git a/code/client/cl_mp3.org b/code/client/cl_mp3.org deleted file mode 100644 index 1db5f52..0000000 --- a/code/client/cl_mp3.org +++ /dev/null @@ -1,419 +0,0 @@ -// Filename:- cl_mp3.cpp -// -// (The interface module between all the MP3 stuff and Trek) -// -#include "client.h" -#include "cl_mp3.h" //(only included directly from snd_mem.cpp, so not in client.h) -#include "../mp3code/mp3struct.h" // keep this rather awful file secret from the rest of the program - -// call the real worker code in the messy C stuff... -// -#ifdef __cplusplus -extern "C" -{ -#endif - -char* C_MP3_IsValid (void *pvData, int iDataLen); -char* C_MP3_GetUnpackedSize (void *pvData, int iDataLen, int *piUnpackedSize); -char* C_MP3_UnpackRawPCM (void *pvData, int iDataLen, int *piUnpackedSize, void *pbUnpackBuffer); -char* C_MP3_GetHeaderData (void *pvData, int iDataLen, int *piRate, int *piWidth, int *piChannels); -char* C_MP3Stream_DecodeInit (LP_MP3STREAM pSFX_MP3Stream, void *pvSourceData, int iSourceBytesRemaining, - int iGameAudioSampleRate, int iGameAudioSampleBits ); -unsigned int C_MP3Stream_Decode (LP_MP3STREAM pSFX_MP3Stream); -char* C_MP3Stream_Rewind (LP_MP3STREAM pSFX_MP3Stream); - - -// these two are temp and will eventually be deleted... honest... -// -char* C_TEST_MP3_GetUnpackedSize( const char *_FILENAME1, const char *_FILENAME2, const char *_FILENAME3, - void *data1,void *data2,void *data3, - int size1,int size2,int size3, - int *iUnpackedSize1,int *iUnpackedSize2,int *iUnpackedSize3 - ); -char * C_TEST_MP3_UnpackRawPCM(const char *_FILENAME1, const char *_FILENAME2, const char *_FILENAME3, - void *data1,void *data2,void *data3, - int iSourceBytesRemaining1,int iSourceBytesRemaining2,int iSourceBytesRemaining3, - int *piUnpackedSize1,int *piUnpackedSize2,int *piUnpackedSize3, - void *pbUnpackBuffer1,void *pbUnpackBuffer2,void *pbUnpackBuffer3 - ); - - -#ifdef __cplusplus -} -#endif - - - -// expects data already loaded, filename arg is for error printing only -// -// returns success/fail -// -qboolean MP3_IsValid( const char *psLocalFilename, void *pvData, int iDataLen ) -{ - char *psError = C_MP3_IsValid(pvData, iDataLen); - - if (psError) - { - Com_Printf(va(S_COLOR_RED"%s\n(File: %s)\n",psError, psLocalFilename)); - } - - return !psError; -} - - - -// expects data already loaded, filename arg is for error printing only -// -// returns unpacked length, or 0 for errors (which will be printed internally) -// -int MP3_GetUnpackedSize( const char *psLocalFilename, void *pvData, int iDataLen, qboolean qbIgnoreID3Tag /* = qfalse */) -{ - int iUnpackedSize = 0; - - if (qbIgnoreID3Tag || !MP3_ReadSpecialTagInfo((byte *)pvData, iDataLen, NULL, &iUnpackedSize)) - { - char *psError = C_MP3_GetUnpackedSize( pvData, iDataLen, &iUnpackedSize); - - if (psError) - { - Com_Printf(va(S_COLOR_RED"%s\n(File: %s)\n",psError, psLocalFilename)); - return 0; - } - } - - return iUnpackedSize; -} - - - -// expects data already loaded, filename arg is for error printing only -// -// returns byte count of unpacked data (effectively a success/fail bool) -// -int MP3_UnpackRawPCM( const char *psLocalFilename, void *pvData, int iDataLen, byte *pbUnpackBuffer ) -{ - int iUnpackedSize; - char *psError = C_MP3_UnpackRawPCM( pvData, iDataLen, &iUnpackedSize, pbUnpackBuffer); - - if (psError) - { - Com_Printf(va(S_COLOR_RED"%s\n(File: %s)\n",psError, psLocalFilename)); - return 0; - } - - return iUnpackedSize; -} - - - -// expects data already loaded, filename arg is for error printing only -// -qboolean MP3_FakeUpWAVInfo( const char *psLocalFilename, void *pvData, int iDataLen, int iUnpackedDataLength, int &format, int &rate, int &width, int &channels, int &samples, int &dataofs) -{ - // some things can be done instantly... - // - format = 1; // 1 for MS format - dataofs= 0; // will be 0 for me (since there's no header in the unpacked data) - - // some things need to be read... - // - char *psError = C_MP3_GetHeaderData(pvData, iDataLen, &rate, &width, &channels); - if (psError) - { - Com_Printf(va(S_COLOR_RED"%s\n(File: %s)\n",psError, psLocalFilename)); - } - - // and some stuff needs calculating... - // - samples = iUnpackedDataLength / width; - - - return !psError; - -} - - - -const char sKEY_MAXVOL[]="#MAXVOL"; // formerly #defines -const char sKEY_UNCOMP[]="#UNCOMP"; // " " - -// returns qtrue for success... -// -qboolean MP3_ReadSpecialTagInfo(byte *pbLoadedFile, int iLoadedFileLen, // (in) - id3v1_1** ppTAG, // (out), can be NULL - int *piUncompressedSize, float *pfMaxVol // (out), can be NULL - ) -{ - qboolean qbError = qfalse; - - id3v1_1* pTAG = (id3v1_1*) ((pbLoadedFile+iLoadedFileLen)-sizeof(id3v1_1)); // sizeof = 128 - - if (!strncmp(pTAG->id, "TAG", 3)) - { - // TAG found... - // - - // read MAXVOL key... - // - if (strncmp(pTAG->comment, sKEY_MAXVOL, strlen(sKEY_MAXVOL))) - { - qbError = qtrue; - } - else - { - if ( pfMaxVol) - { - *pfMaxVol = atof(pTAG->comment + strlen(sKEY_MAXVOL)); - } - } - - // - // read UNCOMP key... - // - if (strncmp(pTAG->album, sKEY_UNCOMP, strlen(sKEY_UNCOMP))) - { - qbError = qtrue; - } - else - { - if ( piUncompressedSize) - { - *piUncompressedSize = atoi(pTAG->album + strlen(sKEY_UNCOMP)); - } - } - } - else - { - pTAG = NULL; - } - - if (ppTAG) - { - *ppTAG = pTAG; - } - - return (pTAG && !qbError); -} - - - -qboolean TEST_MP3_GetUnpackedSize(const char *_FILENAME1, const char *_FILENAME2, const char *_FILENAME3, - void *data1,void *data2,void *data3, - int size1,int size2,int size3, - int *iUnpackedSize1,int *iUnpackedSize2,int *iUnpackedSize3 - ) -{ - char *psError = C_TEST_MP3_GetUnpackedSize(_FILENAME1, _FILENAME2, _FILENAME3, - data1,data2,data3, - size1,size2,size3, - iUnpackedSize1,iUnpackedSize2,iUnpackedSize3 - ); - - if (psError) - { - Com_Printf(va(S_COLOR_RED"%s\n",psError)); - return qfalse; - } - - return qtrue; -} - - -// expects data already loaded, filename arg is for error printing only -// -// returns byte count of unpacked data (effectively a success/fail bool) -// -qboolean TEST_MP3_UnpackRawPCM( const char *_FILENAME1, const char *_FILENAME2, const char *_FILENAME3, - void *data1,void *data2,void *data3, - int iSourceBytesRemaining1,int iSourceBytesRemaining2,int iSourceBytesRemaining3, - int *piUnpackedSize1,int *piUnpackedSize2,int *piUnpackedSize3, - void *pbUnpackBuffer1,void *pbUnpackBuffer2,void *pbUnpackBuffer3 - ) -{ - char *psError = C_TEST_MP3_UnpackRawPCM(_FILENAME1, _FILENAME2, _FILENAME3, - data1,data2,data3, - iSourceBytesRemaining1,iSourceBytesRemaining2,iSourceBytesRemaining3, - piUnpackedSize1,piUnpackedSize2,piUnpackedSize3, - pbUnpackBuffer1,pbUnpackBuffer2,pbUnpackBuffer3 - ); - if (psError) - { - Com_Printf(va(S_COLOR_RED"%s\n",psError)); - return qfalse; - } - - return qtrue; -} - - - - - -// a file has been loaded in memory, see if we want to keep it as MP3, else as normal WAV... -// -// return = qtrue if keeping as MP3 -// -// (note: the reason I pass in the unpacked size rather than working it out here is simply because I already have it) -// -qboolean MP3Stream_InitFromFile( sfx_t* sfx, byte *pbSrcData, int iSrcDatalen, const char *psSrcDataFilename, int iMP3UnPackedSize ) -{ - // first, make a decision based on size here as to whether or not it's worth it because of MP3 buffer space - // making small files much bigger (and therefore best left as WAV)... - // -#define FUZZY_AMOUNT (5*1024) // so it has to be significantly over, not just break even, because of - // the xtra CPU time versus memory saving - - if (iSrcDatalen + sizeof(MP3STREAM) + FUZZY_AMOUNT < iMP3UnPackedSize) - { - // ok, let's keep it as MP3 then... - // - - float fMaxVol = 128; // seems to be a reasonable typical default for maxvol (for lip synch). Naturally there's no #define I can use instead... - - MP3_ReadSpecialTagInfo(pbSrcData, iSrcDatalen, NULL, NULL, &fMaxVol ); // try and read a read maxvol from MP3 header - - // fill in some sfx_t fields... - // - sfx->eCompressionType = ct_MP3; - sfx->data = (byte*) Hunk_Alloc( iSrcDatalen ); // will err_drop if fails - memcpy ( sfx->data, pbSrcData, iSrcDatalen ); // ... so the -> data field is MP3, not PCM - sfx->width = 2;//(s_compression->value == 1)?1:2; - sfx->length = (iMP3UnPackedSize / sfx->width) / (44100 / dma.speed); - sfx->vol_range = fMaxVol; - - // now init the low-level MP3 stuff... - // - MP3STREAM SFX_MP3Stream = {0}; - char *psError = C_MP3Stream_DecodeInit( &SFX_MP3Stream, sfx->data, iSrcDatalen, - dma.speed,//(s_khz->value == 44)?44100:(s_khz->value == 22)?22050:11025, - sfx->width * 8 - ); - if (psError) - { - // This should never happen, since any errors or problems with the MP3 file would have stopped us getting - // to this whole function, but just in case... - // - Com_Printf(va(S_COLOR_YELLOW"File \"%s\": %s\n",psSrcDataFilename,psError)); - - // This will leave iSrcDatalen bytes on the hunk stack (since you can't dealloc that), but MP3 files are - // usually small, and like I say, it should never happen. - // - // Strictly speaking, I should do a Z_Malloc above, then I could do a Z_Free if failed, else do a Hunk_Alloc - // to copy the Z_Malloc data into, then Z_Free, but for something that shouldn't happen it seemed bad to - // penalise the rest of the game with extra malloc demands. - // - return qfalse; - } - - // success ( ...on a plate). - // - // make a copy of the filled-in stream struct and attach to the sfx_t struct... - // - sfx->pMP3StreamHeader = (MP3STREAM *) Hunk_Alloc( sizeof(MP3STREAM) ); - memcpy( sfx->pMP3StreamHeader, &SFX_MP3Stream, sizeof(MP3STREAM) ); - // - return qtrue; - } - - return qfalse; -} - - -// return is decoded byte count, else 0 for finished -// -int MP3Stream_Decode( LP_MP3STREAM lpMP3Stream ) -{ - lpMP3Stream->iCopyOffset = 0; - return C_MP3Stream_Decode( lpMP3Stream ); -} - -// returns qtrue for all ok -// -// (this can be optimised by copying the whole header from the sfx struct sometime) -// -qboolean MP3Stream_Rewind( channel_t *ch ) -{ -/* char *psError = C_MP3Stream_Rewind( lpMP3Stream ); - - if (psError) - { - Com_Printf(S_COLOR_YELLOW"%s\n",psError); - return qfalse; - } - - return qtrue; -*/ - memcpy(&ch->MP3StreamHeader, ch->sfx->pMP3StreamHeader, sizeof(ch->MP3StreamHeader)); - return qtrue; -} - -void MP3Stream_GetSamples( channel_t *ch, int startingSampleNum, int count, short *buf ) -{ - static const int iQuarterOfSlidingBuffer = sizeof(ch->MP3SlidingDecodeBuffer)/4; - static const int iThreeQuartersOfSlidingBuffer = (sizeof(ch->MP3SlidingDecodeBuffer)*3)/4; - -// Com_Printf("startingSampleNum %d\n",startingSampleNum); - - count *= ch->sfx->width; // count arg was for words, so double it for bytes; - - startingSampleNum *= ch->sfx->width; - - if ( startingSampleNum < ch->iMP3SlidingDecodeWindowPos) - { - // what?!?!?! Fucking time travel needed or something?, forget it - memset(buf,0,count); - return; - } - -// OutputDebugString(va("\nRequest: startingSampleNum %d, count %d\n",startingSampleNum,count)); -// OutputDebugString(va("WindowPos %d, WindowWritePos %d\n",ch->iMP3SlidingDecodeWindowPos,ch->iMP3SlidingDecodeWritePos)); - - while (! - ( - (startingSampleNum >= ch->iMP3SlidingDecodeWindowPos) - && - (startingSampleNum + count < ch->iMP3SlidingDecodeWindowPos + ch->iMP3SlidingDecodeWritePos) - ) - ) - { -// OutputDebugString("Scrolling..."); - - int _iBytesDecoded = MP3Stream_Decode( (LP_MP3STREAM) &ch->MP3StreamHeader ); -// OutputDebugString(va("%d bytes decoded\n",_iBytesDecoded)); - if (_iBytesDecoded == 0) - { - // no more source data left so clear the remainder of the buffer... - // - memset(ch->MP3SlidingDecodeBuffer + ch->iMP3SlidingDecodeWritePos, 0, sizeof(ch->MP3SlidingDecodeBuffer)-ch->iMP3SlidingDecodeWritePos); - //MP3Stream_Rewind(ch); // should I do this??? -// OutputDebugString("Finished\n"); - break; - } - else - { - memcpy(ch->MP3SlidingDecodeBuffer + ch->iMP3SlidingDecodeWritePos,ch->MP3StreamHeader.bDecodeBuffer,_iBytesDecoded); - - ch->iMP3SlidingDecodeWritePos += _iBytesDecoded; - - // if reached 3/4 of buffer pos, backscroll the decode window by one quarter... - // - if (ch->iMP3SlidingDecodeWritePos > (sizeof(ch->MP3SlidingDecodeBuffer)*3)/4) - { - memmove(ch->MP3SlidingDecodeBuffer, ((byte *)ch->MP3SlidingDecodeBuffer + (sizeof(ch->MP3SlidingDecodeBuffer)/4)), (sizeof(ch->MP3SlidingDecodeBuffer)*3)/4); - ch->iMP3SlidingDecodeWritePos -= sizeof(ch->MP3SlidingDecodeBuffer)/4; - ch->iMP3SlidingDecodeWindowPos+= sizeof(ch->MP3SlidingDecodeBuffer)/4; - } - } -// OutputDebugString(va("WindowPos %d, WindowWritePos %d\n",ch->iMP3SlidingDecodeWindowPos,ch->iMP3SlidingDecodeWritePos)); - } - - assert(startingSampleNum >= ch->iMP3SlidingDecodeWindowPos); - memcpy( buf, ch->MP3SlidingDecodeBuffer + (startingSampleNum-ch->iMP3SlidingDecodeWindowPos), count); - - -// OutputDebugString("OK\n"); -} - - -///////////// eof ///////////// - diff --git a/code/client/cl_ui.cpp b/code/client/cl_ui.cpp index 9ff0758..6e49456 100644 --- a/code/client/cl_ui.cpp +++ b/code/client/cl_ui.cpp @@ -245,6 +245,8 @@ void CL_InitUI( void ) { uii.R_AddLightToScene = re.AddLightToScene; uii.R_RenderScene = re.RenderScene; + uii.R_ModelBounds = re.ModelBounds; + uii.R_SetColor = re.SetColor; uii.R_DrawStretchPic = re.DrawStretchPic; uii.UpdateScreen = SCR_UpdateScreen; @@ -363,6 +365,9 @@ int CL_UISystemCalls( int *args ) Cvar_Update( (vmCvar_t *) VMA(1) ); return 0; + case UI_R_REGISTERMODEL: + return re.RegisterModel((const char *) VMA(1) ); + case UI_R_REGISTERSHADERNOMIP: return re.RegisterShaderNoMip((const char *) VMA(1) ); diff --git a/code/client/eax/vssver.scc b/code/client/eax/vssver.scc deleted file mode 100644 index c3eaf05..0000000 Binary files a/code/client/eax/vssver.scc and /dev/null differ diff --git a/code/client/snd_ambient.cpp b/code/client/snd_ambient.cpp index 9e42b46..c36bf1a 100644 --- a/code/client/snd_ambient.cpp +++ b/code/client/snd_ambient.cpp @@ -876,13 +876,17 @@ static void AS_UpdateCurrentSet( int id ) old = aSets.GetSet( oldSet ); current = aSets.GetSet( currentSet ); - + // Ste, I just put this null check in for now, not sure if there's a more graceful way to exit this function - dmv + if( !current ) + { + return; + } if ( old ) { old->masterVolume = MAX_SET_VOLUME; old->fadeTime = cls.realtime; } - + current->masterVolume = 0; //Set the fading starts diff --git a/code/client/snd_dma.cpp b/code/client/snd_dma.cpp index 25ee294..b42cbd3 100644 --- a/code/client/snd_dma.cpp +++ b/code/client/snd_dma.cpp @@ -200,13 +200,13 @@ int s_UseOpenAL = true; // Determines if using Open AL or the default softwar ALfloat listener_pos[3]; // Listener Position ALfloat listener_ori[6]; // Listener Orientation int s_numChannels; // Number of AL Sources == Num of Channels -short s_rawdata[MAX_RAW_SAMPLES*4]; // Used for Raw Samples (Music etc...) +short s_rawdata[MAX_RAW_SAMPLES*2]; // Used for Raw Samples (Music etc...) channel_t *S_OpenALPickChannel(int entnum, int entchannel); int S_MP3PreProcessLipSync(channel_t *ch, short *data); void UpdateSingleShotSounds(); void UpdateLoopingSounds(); -void UpdateRawSamples(); +void AL_UpdateRawSamples(); void S_SetLipSyncs(); // EAX Related @@ -356,7 +356,7 @@ void S_Init( void ) { s_volumeVoice= Cvar_Get ("s_volumeVoice", "1.0", CVAR_ARCHIVE); s_musicVolume = Cvar_Get ("s_musicvolume", "0.5", CVAR_ARCHIVE); s_separation = Cvar_Get ("s_separation", "0.5", CVAR_ARCHIVE); - s_khz = Cvar_Get ("s_khz", "22", CVAR_ARCHIVE); + s_khz = Cvar_Get ("s_khz", "22", CVAR_ARCHIVE|CVAR_LATCH); s_mixahead = Cvar_Get ("s_mixahead", "0.2", CVAR_ARCHIVE); s_mixPreStep = Cvar_Get ("s_mixPreStep", "0.05", CVAR_ARCHIVE); @@ -493,6 +493,22 @@ void S_Init( void ) { dma.samples = 0; dma.submission_chunk = 0; dma.buffer = NULL; + + // Clamp sound volumes between 0.0f and 1.0f + if (s_volume->value < 0.f) + s_volume->value = 0.f; + if (s_volume->value > 1.f) + s_volume->value = 1.f; + + if (s_volumeVoice->value < 0.f) + s_volumeVoice->value = 0.f; + if (s_volumeVoice->value > 1.f) + s_volumeVoice->value = 1.f; + + if (s_musicVolume->value < 0.f) + s_musicVolume->value = 0.f; + if (s_musicVolume->value > 1.f) + s_musicVolume->value = 1.f; } else { @@ -519,6 +535,21 @@ void S_Init( void ) { AS_Init(); } +// only called from snd_restart. QA request... +// +void S_ReloadAllUsedSounds(void) +{ + // new bit, reload all soundsthat are used on the current level... + // + for (int i=1 ; i < s_numSfx ; i++) // start @ 1 to skip freeing default sound + { + sfx_t *sfx = &s_knownSfx[i]; + + if (!sfx->bInMemory && !sfx->bDefaultSound && sfx->iLastLevelUsedOn == RE_RegisterMedia_GetLevel()){ + S_memoryLoad(sfx); + } + } +} // ======================================================================= // Shutdown sound engine @@ -956,11 +987,11 @@ channel_t *S_PickChannel(int entnum, int entchannel) firstToDie = &s_channels[0]; - for ( int pass = 0; (pass < ((entchannel == CHAN_AUTO)?1:2)) && !foundChan; pass++ ) + for ( int pass = 0; (pass < ((entchannel == CHAN_AUTO || entchannel == CHAN_LESS_ATTEN)?1:2)) && !foundChan; pass++ ) { for (ch_idx = 0, ch = &s_channels[0]; ch_idx < MAX_CHANNELS ; ch_idx++, ch++ ) { - if ( entchannel == CHAN_AUTO || pass > 0 ) + if ( entchannel == CHAN_AUTO || entchannel == CHAN_LESS_ATTEN || pass > 0 ) {//if we're on the second pass, just find the first open chan if ( !ch->thesfx ) {//grab the first open channel @@ -1149,6 +1180,10 @@ void S_SpatializeOrigin (const vec3_t origin, float master_vol, int *left_vol, i { dist -= SOUND_FULLVOLUME * 3.0f; } + else if ( channel == CHAN_LESS_ATTEN ) + { + dist -= SOUND_FULLVOLUME * 8.0f; // maybe is too large + } else if ( channel == CHAN_VOICE_ATTEN ) { dist -= SOUND_FULLVOLUME * 1.35f; // used to be 0.15f, dropped off too sharply - dmv @@ -1399,6 +1434,28 @@ void S_StartLocalLoopingSound( sfxHandle_t sfxHandle) { } +// returns length in milliseconds of supplied sound effect... (else 0 for bad handle now) +// +float S_GetSampleLengthInMilliSeconds( sfxHandle_t sfxHandle) +{ + sfx_t *sfx; + + if (!s_soundStarted) + { //we have no sound, so let's just make a reasonable guess + return 512 * 1000; + } + + if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) + return 0.0f; + + sfx = &s_knownSfx[ sfxHandle ]; + + float f = (float)sfx->iSoundLengthInSamples / (float)dma.speed; + + return (f * 1000); +} + + /* ================== S_ClearSoundBuffer @@ -1434,6 +1491,11 @@ void S_ClearSoundBuffer( void ) { memset(dma.buffer, clear, dma.samples * dma.samplebits/8); SNDDMA_Submit (); } + else + { + s_paintedtime = 0; + s_soundtime = 0; + } } @@ -1453,13 +1515,13 @@ void S_CIN_StopSound(sfxHandle_t sfxHandle) if (ch->thesfx == sfx) { alSourceStop(s_channels[i].alSource); + SND_FreeSFXMem(ch->thesfx); // heh, may as well... ch->thesfx = NULL; memset(&ch->MP3StreamHeader, 0, sizeof(MP3STREAM)); ch->bLooping = false; ch->bProcessed = false; ch->bPlaying = false; ch->bStreaming = false; - SND_FreeSFXMem(ch->thesfx); // heh, may as well... break; } } @@ -1609,6 +1671,12 @@ void S_AddAmbientLoopingSound( const vec3_t origin, unsigned char volume, sfxHan return; } + if (s_UseOpenAL) + { + if (volume == 0) + return; + } + if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) { Com_Error( ERR_DROP, "S_StartSound: handle %i out of range", sfxHandle ); } @@ -2471,7 +2539,11 @@ void S_Update_(void) { //#endif alSourcei(s_channels[source].alSource, AL_LOOPING, AL_FALSE); - alSourcef(s_channels[source].alSource, AL_GAIN, (float)(ch->master_vol) / 255.0f); + + if ( ch->entchannel == CHAN_VOICE || ch->entchannel == CHAN_VOICE_ATTEN || ch->entchannel == CHAN_VOICE_GLOBAL ) + alSourcef(s_channels[source].alSource, AL_GAIN, ((float)(ch->master_vol) * s_volumeVoice->value) / 255.0f); + else + alSourcef(s_channels[source].alSource, AL_GAIN, ((float)(ch->master_vol) * s_volume->value) / 255.f); if (s_bEALFileLoaded) UpdateEAXBuffer(ch); @@ -2594,7 +2666,7 @@ void S_Update_(void) { UpdateLoopingSounds(); - UpdateRawSamples(); + AL_UpdateRawSamples(); EAXMorph(); } @@ -2812,7 +2884,6 @@ void UpdateLoopingSounds() channel_t *ch; loopSound_t *loop; float pos[3]; - char szString[256]; // First check to see if any of the looping sounds are already playing at the correct positions ch = s_channels + 1; @@ -2846,24 +2917,30 @@ void UpdateLoopingSounds() } // Make sure Gain is set correctly - ch->master_vol = loop->volume; - alSourcef(s_channels[i].alSource, AL_GAIN, (float)(ch->master_vol) / 255.f); + if (ch->master_vol != loop->volume) + { + ch->master_vol = loop->volume; + alSourcef(s_channels[i].alSource, AL_GAIN, ((float)(ch->master_vol) * s_volume->value) / 255.f); + } ch->bProcessed = true; loop->bProcessed = true; } - } - else if ((loop->bProcessed == false) && (ch->thesfx == loop->sfx) && (!memcmp(ch->origin, loop->origin, sizeof(ch->origin)))) - { - // Match ! - ch->bProcessed = true; - loop->bProcessed = true; + else if ((loop->bProcessed == false) && (ch->thesfx == loop->sfx) && (!memcmp(ch->origin, loop->origin, sizeof(ch->origin)))) + { + // Match ! + ch->bProcessed = true; + loop->bProcessed = true; - // Make sure Gain is set correctly - ch->master_vol = loop->volume; - alSourcef(s_channels[i].alSource, AL_GAIN, (float)(ch->master_vol) / 255.f); + // Make sure Gain is set correctly + if (ch->master_vol != loop->volume) + { + ch->master_vol = loop->volume; + alSourcef(s_channels[i].alSource, AL_GAIN, ((float)(ch->master_vol) * s_volume->value) / 255.f); + } - break; + break; + } } } } @@ -2881,13 +2958,17 @@ void UpdateLoopingSounds() if ((loop->bProcessed == false) && (ch->thesfx == loop->sfx)) { // Same sound - wrong position + ch->origin[0] = loop->origin[0]; + ch->origin[1] = loop->origin[1]; + ch->origin[2] = loop->origin[2]; + pos[0] = loop->origin[0]; pos[1] = loop->origin[2]; pos[2] = -loop->origin[1]; alSourcefv(s_channels[i].alSource, AL_POSITION, pos); ch->master_vol = loop->volume; - alSourcef(s_channels[i].alSource, AL_GAIN, (float)(ch->master_vol) / 255.f); + alSourcef(s_channels[i].alSource, AL_GAIN, ((float)(ch->master_vol) * s_volume->value) / 255.f); if (s_bEALFileLoaded) UpdateEAXBuffer(ch); @@ -2909,12 +2990,6 @@ void UpdateLoopingSounds() { // Sound no longer needed alSourceStop(s_channels[i].alSource); - if (alGetError() != AL_NO_ERROR) - { - sprintf(szString, "OAL Error - S_Update - failed to stop looping sound on Source %d\n", i); - OutputDebugString(szString); - } - ch->thesfx = NULL; ch->bPlaying = false; } @@ -2964,7 +3039,7 @@ void UpdateLoopingSounds() alSourcei(s_channels[source].alSource, AL_BUFFER, ch->thesfx->Buffer); alSourcefv(s_channels[source].alSource, AL_POSITION, pos); alSourcei(s_channels[source].alSource, AL_LOOPING, AL_TRUE); - alSourcef(s_channels[source].alSource, AL_GAIN, (float)(ch->master_vol) / 255.0f); + alSourcef(s_channels[source].alSource, AL_GAIN, ((float)(ch->master_vol) * s_volume->value) / 255.0f); alSourcei(s_channels[source].alSource, AL_SOURCE_RELATIVE, ch->fixed_origin ? AL_TRUE : AL_FALSE); if (s_bEALFileLoaded) @@ -2981,7 +3056,8 @@ void UpdateLoopingSounds() -void UpdateRawSamples() + +void AL_UpdateRawSamples() { ALuint buffer; ALint size; @@ -2994,6 +3070,8 @@ void UpdateRawSamples() alGetError(); #endif + S_UpdateBackgroundTrack(); + // Find out how many buffers have been processed (played) by the Source alGetSourcei(s_channels[0].alSource, AL_BUFFERS_PROCESSED, &processed); @@ -3010,8 +3088,6 @@ void UpdateRawSamples() processed--; } - S_UpdateBackgroundTrack(); - // Add new data to a new Buffer and queue it on the Source if (s_rawend > s_paintedtime) { @@ -3019,8 +3095,8 @@ void UpdateRawSamples() if (size > (MAX_RAW_SAMPLES<<2)) { OutputDebugString("UpdateRawSamples :- Raw Sample buffer has overflowed !!!\n"); - s_rawend = s_paintedtime + MAX_RAW_SAMPLES; size = MAX_RAW_SAMPLES<<2; + s_paintedtime = s_rawend - MAX_RAW_SAMPLES; } // Copy samples from RawSamples to audio buffer (sg.rawdata) @@ -3090,7 +3166,6 @@ void UpdateRawSamples() #ifdef _DEBUG OutputDebugString("Restarting / Starting playback of Raw Samples\n"); #endif - alSourcePlay(s_channels[0].alSource); } } @@ -4049,6 +4124,11 @@ void S_StartBackgroundTrack( const char *intro, const char *loop, qboolean bCall { bMusic_IsDynamic = qfalse; + if (!s_soundStarted) + { //we have no sound, so don't even bother trying + return; + } + if ( !intro ) { intro = ""; } diff --git a/code/client/snd_mem.cpp b/code/client/snd_mem.cpp index 2a9f3ff..b6eba57 100644 --- a/code/client/snd_mem.cpp +++ b/code/client/snd_mem.cpp @@ -292,6 +292,7 @@ int iFilesUpdated; int iErrors; qboolean qbForceRescan; qboolean qbForceStereo; +string strErrors; void R_CheckMP3s( const char *psDir ) { @@ -445,32 +446,36 @@ void R_CheckMP3s( const char *psDir ) } else { - Com_Printf("*********** Failed write to file!\n"); + Com_Printf("*********** Failed write to file \"%s\"!\n",sFilename); iErrors++; + strErrors += va("Failed to write: \"%s\"\n",sFilename); } } else { - Com_Printf("*********** Failed write to file!\n"); + Com_Printf("*********** Failed write to file \"%s\"!\n",sFilename); iErrors++; + strErrors += va("Failed to write: \"%s\"\n",sFilename); } FS_FCloseFile( f ); } else { - Com_Printf("*********** Failed to re-open for write!\n"); + Com_Printf("*********** Failed to re-open for write \"%s\"!\n",sFilename); iErrors++; + strErrors += va("Failed to re-open for write: \"%s\"\n",sFilename); } } else { - Com_Error(ERR_DROP, "******* This MP3 should be deleted: %s\n",sFilename); + Com_Error(ERR_DROP, "******* This MP3 should be deleted: \"%s\"\n",sFilename); } } else { - Com_Printf("*********** File was not a valid MP3!\n"); + Com_Printf("*********** File was not a valid MP3!: \"%s\"\n",sFilename); iErrors++; + strErrors += va("Not game-legal MP3 format: \"%s\"\n",sFilename); } } else @@ -507,6 +512,7 @@ void S_MP3_CalcVols_f( void ) iFilesFound = 0; iFilesUpdated = 0; iErrors = 0; + strErrors = ""; for (int i=1; i= s_numSfx ) - return 0.0f; - - sfx = &s_knownSfx[ sfxHandle ]; - - float f = (float)sfx->iSoundLengthInSamples / (float)dma.speed; - - return (f * 1000); -} - - /* Precalculate the lipsync values for the whole sample */ diff --git a/code/client/vssver.scc b/code/client/vssver.scc deleted file mode 100644 index 568b4bf..0000000 Binary files a/code/client/vssver.scc and /dev/null differ diff --git a/code/encryption/vssver.scc b/code/encryption/vssver.scc deleted file mode 100644 index 255a848..0000000 Binary files a/code/encryption/vssver.scc and /dev/null differ diff --git a/code/game/AI_Atst.cpp b/code/game/AI_Atst.cpp index 1a2e430..8bcb4bf 100644 --- a/code/game/AI_Atst.cpp +++ b/code/game/AI_Atst.cpp @@ -30,6 +30,7 @@ void NPC_ATST_Precache(void) G_EffectIndex( "mouseexplosion1" ); G_EffectIndex( "smaller_chunks" ); G_EffectIndex( "blaster/smoke_bolton" ); + G_EffectIndex( "droidexplosion1" ); } /* ------------------------- @@ -112,9 +113,9 @@ ATST_Ranged void ATST_Ranged( qboolean visible, qboolean advance, qboolean altAttack ) { - if ( TIMER_Done( NPC, "attackDelay" ) ) // Attack? + if ( TIMER_Done( NPC, "atkDelay" ) ) // Attack? { - TIMER_Set( NPC, "attackDelay", Q_irand( 500, 3000 ) ); + TIMER_Set( NPC, "atkDelay", Q_irand( 500, 3000 ) ); if (altAttack) { @@ -262,7 +263,10 @@ void NPC_BSATST_Default( void ) { if ( NPC->enemy ) { - NPCInfo->goalEntity = NPC->enemy; + if( (NPCInfo->scriptFlags & SCF_CHASE_ENEMIES) ) + { + NPCInfo->goalEntity = NPC->enemy; + } ATST_Attack(); } else if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES ) diff --git a/code/game/AI_Default.cpp b/code/game/AI_Default.cpp index 39cdf69..b5a0fca 100644 --- a/code/game/AI_Default.cpp +++ b/code/game/AI_Default.cpp @@ -728,6 +728,13 @@ void NPC_BSDefault( void ) WeaponThink( qtrue ); } + if ( NPCInfo->scriptFlags & SCF_FORCED_MARCH ) + {//being forced to walk + if( NPC->client->ps.torsoAnim != TORSO_SURRENDER_START ) + { + NPC_SetAnim( NPC, SETANIM_TORSO, TORSO_SURRENDER_START, SETANIM_FLAG_HOLD ); + } + } //look for a new enemy if don't have one and are allowed to look, validate current enemy if have one NPC_CheckEnemy( (NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES), qfalse ); if ( !NPC->enemy ) @@ -921,6 +928,10 @@ void NPC_BSDefault( void ) NPC_MoveToGoal( qtrue ); } } + else if ( !NPC->enemy && NPC->client->leader ) + { + NPC_BSFollowLeader(); + } //update angles NPC_UpdateAngles( qtrue, qtrue ); diff --git a/code/game/AI_Droid.cpp b/code/game/AI_Droid.cpp index 76d2c3f..3dea7d9 100644 --- a/code/game/AI_Droid.cpp +++ b/code/game/AI_Droid.cpp @@ -286,7 +286,7 @@ void NPC_Droid_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, ve { gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "head", TURN_OFF ); - G_PlayEffect( "small_chunks" , self->currentOrigin ); +// G_PlayEffect( "small_chunks" , self->currentOrigin ); G_PlayEffect( "r5d2head", self->currentOrigin ); self->s.powerups |= ( 1 << PW_SHOCKED ); @@ -416,8 +416,10 @@ void NPC_R5D2_Precache(void) { G_SoundIndex( va( "sound/chars/r5d2/misc/r5talk%d.wav", i ) ); } - G_EffectIndex( "droidexplosion1"); - G_EffectIndex( "small_chunks"); + G_SoundIndex( "sound/chars/r2d2/misc/r2_move_lp2.wav" ); + G_EffectIndex( "r5_droidexplosion"); + G_EffectIndex( "droid_smoke" ); +// G_EffectIndex( "small_chunks"); G_EffectIndex( "r5d2head"); // G_EffectIndex( "r5d2headland"); } @@ -433,8 +435,9 @@ void NPC_R2D2_Precache(void) { G_SoundIndex( va( "sound/chars/r2d2/misc/r2d2talk0%d.wav", i ) ); } - G_EffectIndex( "droidexplosion1"); - G_EffectIndex( "small_chunks"); + G_SoundIndex( "sound/chars/r2d2/misc/r2_move_lp.wav" ); + G_EffectIndex( "r2_droidexplosion"); +// G_EffectIndex( "small_chunks"); } /* diff --git a/code/game/AI_GalakMech.cpp b/code/game/AI_GalakMech.cpp index d11e338..227eff8 100644 --- a/code/game/AI_GalakMech.cpp +++ b/code/game/AI_GalakMech.cpp @@ -80,7 +80,7 @@ void NPC_GalakMech_Init( gentity_t *ent ) else { // gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "helmet", TURN_OFF ); - gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "torso_shield", TURN_OFF ); + gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "torso_shield_off", TURN_OFF ); gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "torso_galakface_off", TURN_ON ); gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "torso_galakhead_off", TURN_ON ); gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "torso_eyes_mouth_off", TURN_ON ); @@ -211,7 +211,7 @@ void NPC_GM_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, ve NPC_Mark1_Part_Explode(self,newBolt); } - gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "torso_shield", TURN_OFF ); + gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "torso_shield_off", TURN_OFF ); gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "torso_antenna", TURN_OFF ); gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "torso_antenna_base_cap_off", TURN_ON ); self->client->ps.powerups[PW_GALAK_SHIELD] = 0;//temp, for effect @@ -536,8 +536,9 @@ void NPC_BSGM_Attack( void ) knockAnim = BOTH_KNOCKDOWN4; } //throw them - smackDir[2] -= 20; - G_Throw( NPC->enemy, smackDir, 30 ); + smackDir[2] = 1; + VectorNormalize( smackDir ); + G_Throw( NPC->enemy, smackDir, 50 ); NPC_SetAnim( NPC->enemy, SETANIM_BOTH, knockAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); } else @@ -727,8 +728,11 @@ void NPC_BSGM_Attack( void ) else { int hit = NPC_ShotEntity( NPC->enemy, impactPos ); - if ( hit == NPC->enemy->s.number || (&g_entities[hit] != NULL && g_entities[hit].takedamage && ((g_entities[hit].svFlags&SVF_GLASS_BRUSH)||g_entities[hit].health < 40||NPC->s.weapon == WP_EMPLACED_GUN) ) ) - {//can hit enemy or will hit glass or other minor breakable (or in emplaced gun), so shoot anyway + gentity_t *hitEnt = &g_entities[hit]; + if ( hit == NPC->enemy->s.number + || ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam ) + || ( hitEnt && hitEnt->takedamage ) ) + {//can hit enemy or will hit glass or other breakable, so shoot anyway enemyCS = qtrue; NPC_AimAdjust( 2 );//adjust aim better longer we have clear shot at enemy VectorCopy( NPC->enemy->currentOrigin, NPCInfo->enemyLastSeenLocation ); @@ -736,7 +740,6 @@ void NPC_BSGM_Attack( void ) else {//Hmm, have to get around this bastard NPC_AimAdjust( 1 );//adjust aim better longer we can see enemy - gentity_t *hitEnt = &g_entities[hit]; if ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->playerTeam ) {//would hit an ally, don't fire!!! hitAlly = qtrue; @@ -750,9 +753,20 @@ void NPC_BSGM_Attack( void ) } else if ( gi.inPVS( NPC->enemy->currentOrigin, NPC->currentOrigin ) ) { - NPCInfo->enemyLastSeenTime = level.time; - faceEnemy = qtrue; - NPC_AimAdjust( -1 );//adjust aim worse longer we cannot see enemy + int hit = NPC_ShotEntity( NPC->enemy, impactPos ); + gentity_t *hitEnt = &g_entities[hit]; + if ( hit == NPC->enemy->s.number + || ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam ) + || ( hitEnt && hitEnt->takedamage ) ) + {//can hit enemy or will hit glass or other breakable, so shoot anyway + enemyCS = qtrue; + } + else + { + NPCInfo->enemyLastSeenTime = level.time; + faceEnemy = qtrue; + NPC_AimAdjust( -1 );//adjust aim worse longer we cannot see enemy + } } if ( enemyLOS ) @@ -804,9 +818,9 @@ void NPC_BSGM_Attack( void ) VectorCopy( NPC->enemy->currentOrigin, target ); - target[0] += Q_flrand( -5, 5 )+(crandom()*(6-NPCInfo->currentAim)*4); - target[1] += Q_flrand( -5, 5 )+(crandom()*(6-NPCInfo->currentAim)*4); - target[2] += Q_flrand( -5, 5 )+(crandom()*(6-NPCInfo->currentAim)*4); + target[0] += Q_flrand( -5, 5 )+(crandom()*(6-NPCInfo->currentAim)*2); + target[1] += Q_flrand( -5, 5 )+(crandom()*(6-NPCInfo->currentAim)*2); + target[2] += Q_flrand( -5, 5 )+(crandom()*(6-NPCInfo->currentAim)*2); //Find the desired angles qboolean clearshot = WP_LobFire( NPC, muzzle, target, mins, maxs, MASK_SHOT|CONTENTS_LIGHTSABER, @@ -876,10 +890,14 @@ void NPC_BSGM_Attack( void ) if ( !faceEnemy ) {//we want to face in the dir we're running - NPCInfo->desiredYaw = NPCInfo->lastPathAngles[YAW]; - NPCInfo->desiredPitch = 0; + if ( !move ) + {//if we haven't moved, we should look in the direction we last looked? + VectorCopy( NPC->client->ps.viewangles, NPCInfo->lastPathAngles ); + } if ( move ) {//don't run away and shoot + NPCInfo->desiredYaw = NPCInfo->lastPathAngles[YAW]; + NPCInfo->desiredPitch = 0; shoot = qfalse; } } @@ -951,7 +969,7 @@ void NPC_BSGM_Default( void ) {//armor gone if ( !NPCInfo->investigateDebounceTime ) {//start regenerating the armor - gi.G2API_SetSurfaceOnOff( &NPC->ghoul2[NPC->playerModel], "torso_shield", TURN_OFF ); + gi.G2API_SetSurfaceOnOff( &NPC->ghoul2[NPC->playerModel], "torso_shield_off", TURN_OFF ); NPC->flags &= ~FL_SHIELDED;//no more reflections VectorSet( NPC->mins, -20, -20, -24 ); VectorSet( NPC->maxs, 20, 20, 64 ); @@ -979,7 +997,7 @@ void NPC_BSGM_Default( void ) NPCInfo->investigateDebounceTime = 0; NPC->flags |= FL_SHIELDED;//reflect normal shots NPC->fx_time = level.time; - gi.G2API_SetSurfaceOnOff( &NPC->ghoul2[NPC->playerModel], "torso_shield", TURN_ON ); + gi.G2API_SetSurfaceOnOff( &NPC->ghoul2[NPC->playerModel], "torso_shield_off", TURN_ON ); } } } diff --git a/code/game/AI_Grenadier.cpp b/code/game/AI_Grenadier.cpp index f3911ac..282718b 100644 --- a/code/game/AI_Grenadier.cpp +++ b/code/game/AI_Grenadier.cpp @@ -538,7 +538,9 @@ void NPC_BSGrenadier_Attack( void ) //can we shoot our target? //FIXME: how accurate/necessary is this check? int hit = NPC_ShotEntity( NPC->enemy ); - if ( hit == NPC->enemy->s.number ) + gentity_t *hitEnt = &g_entities[hit]; + if ( hit == NPC->enemy->s.number + || ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam ) ) { VectorCopy( NPC->enemy->currentOrigin, NPCInfo->enemyLastSeenLocation ); float enemyHorzDist = DistanceHorizontalSquared( NPC->enemy->currentOrigin, NPC->currentOrigin ); diff --git a/code/game/AI_Interrogator.cpp b/code/game/AI_Interrogator.cpp index 78923e2..8394131 100644 --- a/code/game/AI_Interrogator.cpp +++ b/code/game/AI_Interrogator.cpp @@ -22,20 +22,19 @@ NPC_Interrogator_Precache */ void NPC_Interrogator_Precache(gentity_t *self) { - G_SoundIndex( "sound/chars/probe/misc/talk.wav"); - + G_SoundIndex( "sound/chars/interrogator/misc/torture_droid_inject.mp3" ); // G_SoundIndex( "sound/chars/probe/misc/probedroidloop.wav" ); // G_SoundIndex("sound/chars/probe/misc/anger1.wav"); G_EffectIndex( "probeexplosion1"); - + G_EffectIndex( "droidexplosion1" ); } /* ------------------------- Interrogator_die ------------------------- */ -void Interrogator_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc ) +void Interrogator_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags,int hitLoc ) { self->client->ps.velocity[2] = -100; self->locationDamage[HL_NONE] += damage; @@ -350,6 +349,8 @@ void Interrogator_Melee( qboolean visible, qboolean advance ) // Drug our enemy up and do the wonky vision thing gentity_t *tent = G_TempEntity( NPC->enemy->currentOrigin, EV_DRUGGED ); tent->owner = NPC->enemy; + + G_Sound( NPC, G_SoundIndex( "sound/chars/interrogator/misc/torture_droid_inject.mp3" )); } } diff --git a/code/game/AI_Jedi.cpp b/code/game/AI_Jedi.cpp index 33391ee..b420247 100644 --- a/code/game/AI_Jedi.cpp +++ b/code/game/AI_Jedi.cpp @@ -734,7 +734,7 @@ static void Jedi_CombatDistance( int enemy_dist ) } if ( NPC->client->ps.weapon == WP_SABER && //using saber NPC->client->ps.saberEntityState == SES_LEAVING && //not returning yet - NPC->client->ps.forcePowerLevel[FP_SABERTHROW] > FORCE_LEVEL_2 )//3rd level lightsaber + NPC->client->ps.forcePowerLevel[FP_SABERTHROW] > FORCE_LEVEL_1 )//2nd or 3rd level lightsaber {//hold it out there ucmd.buttons |= BUTTON_ALT_ATTACK; //FIXME: time limit? @@ -1184,12 +1184,16 @@ qboolean Jedi_DodgeEvasion( gentity_t *self, gentity_t *shooter, trace_t *tr, in } else { - self->client->ps.forceJumpCharge = 320;//FIXME: calc this intelligently? - WP_ForcePowerStop( self, FP_GRIP ); if ( !self->enemy ) { G_SetEnemy( self, shooter ); } + if ( self->NPC && (self->NPC->scriptFlags&SCF_NO_ACROBATICS) ) + { + return qfalse; + } + self->client->ps.forceJumpCharge = 320;//FIXME: calc this intelligently? + WP_ForcePowerStop( self, FP_GRIP ); return qtrue; } break; @@ -1270,12 +1274,17 @@ qboolean Jedi_DodgeEvasion( gentity_t *self, gentity_t *shooter, trace_t *tr, in return qfalse; } -qboolean Jedi_CheckFlipEvasions( gentity_t *self, float rightdot, float zdiff ) +evasionType_t Jedi_CheckFlipEvasions( gentity_t *self, float rightdot, float zdiff ) { + if ( self->NPC && (self->NPC->scriptFlags&SCF_NO_ACROBATICS) ) + { + return EVASION_NONE; + } //Check for: //ARIALS/CARTWHEELS //WALL-RUNS //WALL-FLIPS + //FIXME: if facing a wall, do forward wall-walk-backflip if ( self->client->ps.legsAnim == BOTH_WALL_RUN_LEFT || self->client->ps.legsAnim == BOTH_WALL_RUN_RIGHT ) {//already running on a wall vec3_t right, fwdAngles = {0, self->client->ps.viewangles[YAW], 0}; @@ -1324,7 +1333,7 @@ qboolean Jedi_CheckFlipEvasions( gentity_t *self, float rightdot, float zdiff ) NPC_SetAnim( self, parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); self->client->ps.pm_flags |= (PMF_JUMPING|PMF_SLOW_MO_FALL); G_AddEvent( self, EV_JUMP, 0 ); - return qtrue; + return EVASION_OTHER; } } else if ( self->client->NPC_class != CLASS_DESANN //desann doesn't do these kind of frilly acrobatics @@ -1393,7 +1402,7 @@ qboolean Jedi_CheckFlipEvasions( gentity_t *self, float rightdot, float zdiff ) self->client->ps.pm_flags |= PMF_JUMPING; G_SoundOnEnt( self, CHAN_BODY, "sound/weapons/force/jump.wav" ); //ucmd.upmove = 0; - return qtrue; + return EVASION_CARTWHEEL; } else if ( !(trace.contents&CONTENTS_BOTCLIP) ) {//hit a wall, not a do-not-enter brush @@ -1444,13 +1453,13 @@ qboolean Jedi_CheckFlipEvasions( gentity_t *self, float rightdot, float zdiff ) self->client->ps.forceJumpZStart = self->currentOrigin[2];//so we don't take damage if we land at same height self->client->ps.pm_flags |= (PMF_JUMPING|PMF_SLOW_MO_FALL); G_SoundOnEnt( self, CHAN_BODY, "sound/weapons/force/jump.wav" ); - return qtrue; + return EVASION_OTHER; } else {//boxed in on both sides if ( DotProduct( self->client->ps.velocity, fwd ) < 0 ) {//moving backwards - return qfalse; + return EVASION_NONE; } if ( (trace.fraction*checkDist) <= 32 && (trace.fraction*checkDist) < bestCheckDist ) { @@ -1470,7 +1479,7 @@ qboolean Jedi_CheckFlipEvasions( gentity_t *self, float rightdot, float zdiff ) } else {//neither side has a wall within 32 - return qfalse; + return EVASION_NONE; } } } @@ -1497,15 +1506,16 @@ qboolean Jedi_CheckFlipEvasions( gentity_t *self, float rightdot, float zdiff ) self->client->ps.forceJumpZStart = self->currentOrigin[2];//so we don't take damage if we land at same height self->client->ps.pm_flags |= (PMF_JUMPING|PMF_SLOW_MO_FALL); G_SoundOnEnt( self, CHAN_BODY, "sound/weapons/force/jump.wav" ); - return qtrue; + return EVASION_OTHER; } + //else check for wall in front, do backflip off wall } } } - return qfalse; + return EVASION_NONE; } -int Jedi_ReCalcParryTime( gentity_t *self ) +int Jedi_ReCalcParryTime( gentity_t *self, evasionType_t evasionType ) { if ( !self->client ) { @@ -1517,46 +1527,105 @@ int Jedi_ReCalcParryTime( gentity_t *self ) } else if ( self->NPC ) { - int baseTime = 500; - - switch ( g_spskill->integer ) + int baseTime; + if ( evasionType == EVASION_DODGE ) { - case 0: - baseTime = 500; - break; - case 1: - baseTime = 300; - break; - case 2: - default: - baseTime = 100; - break; + baseTime = self->client->ps.torsoAnimTimer; } - - if ( self->client->NPC_class == CLASS_TAVION ) - {//Tavion is faster - baseTime = baseTime/2; - } - else if ( self->NPC->rank >= RANK_LT_JG ) - {//fencers, bosses, shadowtroopers, luke, desann, et al use the norm - //baseTime = baseTime; - } - else if ( self->NPC->rank == RANK_CIVILIAN ) - {//grunts are slowest - baseTime = baseTime*5; - } - else if ( self->NPC->rank == RANK_CREWMAN ) - {//acrobats aren't so bad - baseTime = baseTime*3; + else if ( evasionType == EVASION_CARTWHEEL ) + { + baseTime = self->client->ps.torsoAnimTimer; } else - {//force users, are kinda slow - baseTime = baseTime*4; + { + baseTime = 300;//500; + + switch ( g_spskill->integer ) + { + case 0: + baseTime = 300;//500; + break; + case 1: + baseTime = 150;//300; + break; + case 2: + default: + baseTime = 50;//100; + break; + } + + if ( self->client->NPC_class == CLASS_TAVION ) + {//Tavion is faster + baseTime = baseTime/2; + } + else if ( self->NPC->rank >= RANK_LT_JG ) + {//fencers, bosses, shadowtroopers, luke, desann, et al use the norm + //baseTime = baseTime; + } + else if ( self->NPC->rank == RANK_CIVILIAN ) + {//grunts are slowest + baseTime = baseTime*5; + } + else if ( self->NPC->rank == RANK_CREWMAN ) + {//acrobats aren't so bad + baseTime = baseTime*3; + } + else + {//force users, are kinda slow + baseTime = baseTime*4; + } + if ( evasionType == EVASION_DUCK || evasionType == EVASION_DUCK_PARRY ) + { + baseTime += 300; + } + else if ( evasionType == EVASION_JUMP || evasionType == EVASION_JUMP_PARRY ) + { + baseTime += 100; + } + else if ( evasionType == EVASION_OTHER ) + { + baseTime += 300; + } + else if ( evasionType == EVASION_FJUMP ) + { + baseTime += 200; + } } + return baseTime; } return 0; } + +qboolean Jedi_QuickReactions( gentity_t *self ) +{ + if ( ( self->client->NPC_class == CLASS_JEDI && NPCInfo->rank == RANK_COMMANDER ) || + self->client->NPC_class == CLASS_TAVION || + (self->client->ps.forcePowerLevel[FP_SABER_DEFENSE]>FORCE_LEVEL_1&&g_spskill->integer>1) || + (self->client->ps.forcePowerLevel[FP_SABER_DEFENSE]>FORCE_LEVEL_2&&g_spskill->integer>0) ) + { + return qtrue; + } + return qfalse; +} + +qboolean Jedi_SaberBusy( gentity_t *self ) +{ + if ( self->client->ps.torsoAnimTimer > 300 + && ( (PM_SaberInAttack( self->client->ps.saberMove )&&self->client->ps.saberAnimLevel==FORCE_LEVEL_3) + || PM_SpinningSaberAnim( self->client->ps.torsoAnim ) + || PM_SaberInSpecialAttack( self->client->ps.torsoAnim ) + //|| PM_SaberInBounce( self->client->ps.saberMove ) + || PM_SaberInBrokenParry( self->client->ps.saberMove ) + //|| PM_SaberInDeflect( self->client->ps.saberMove ) + || PM_FlippingAnim( self->client->ps.torsoAnim ) + || PM_RollingAnim( self->client->ps.torsoAnim ) ) ) + {//my saber is not in a parrying position + return qtrue; + } + return qfalse; +} + /* ------------------------- Jedi_SaberBlock @@ -1570,38 +1639,30 @@ NOTE: always blocking projectiles in this func! ------------------------- */ extern qboolean G_FindClosestPointOnLineSegment( const vec3_t start, const vec3_t end, const vec3_t from, vec3_t result ); -qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming, float dist = 0.0f ) +evasionType_t Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, vec3_t phitDir, gentity_t *incoming, float dist = 0.0f ) { - vec3_t hitloc, diff, fwdangles={0,0,0}, right; + vec3_t hitloc, hitdir, diff, fwdangles={0,0,0}, right; float rightdot; float zdiff; int duckChance = 0; int dodgeAnim = -1; qboolean saberBusy = qfalse, evaded = qfalse, doDodge = qfalse; + evasionType_t evasionType = EVASION_NONE; //FIXME: if we don't have our saber in hand, pick the force throw option or a jump or strafe! - + //FIXME: reborn don't block enough anymore if ( !incoming ) { VectorCopy( pHitloc, hitloc ); + VectorCopy( phitDir, hitdir ); //FIXME: maybe base this on rank some? And/or g_spskill? - if ( ( NPC->client->NPC_class == CLASS_JEDI && NPCInfo->rank == RANK_COMMANDER ) || - NPC->client->NPC_class == CLASS_TAVION || - (NPC->client->ps.forcePowerLevel[FP_SABER_DEFENSE]>FORCE_LEVEL_1&&g_spskill->integer>1) || - (NPC->client->ps.forcePowerLevel[FP_SABER_DEFENSE]>FORCE_LEVEL_2&&g_spskill->integer>0)) + if ( Jedi_QuickReactions( self ) ) {//jedi trainer and tavion are must faster at parrying and can do it whenever they like //Also, on medium, all level 3 people can parry any time and on hard, all level 2 or 3 people can parry any time } - else if ( PM_SaberInAttack( NPC->client->ps.saberMove ) || - PM_SpinningSaberAnim( NPC->client->ps.torsoAnim ) || - PM_SaberInSpecialAttack( NPC->client->ps.torsoAnim ) || - PM_SaberInBounce( NPC->client->ps.saberMove ) || - PM_SaberInBrokenParry( NPC->client->ps.saberMove ) || - PM_SaberInDeflect( NPC->client->ps.saberMove ) || - PM_FlippingAnim( NPC->client->ps.torsoAnim ) || - PM_RollingAnim( NPC->client->ps.torsoAnim ) ) - {//my saber is not in a parrying position - saberBusy = qtrue; + else + { + saberBusy = Jedi_SaberBusy( self ); } } else @@ -1612,6 +1673,7 @@ qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming //Jedi_FaceEntity( self, incoming, qtrue ); } VectorCopy( incoming->currentOrigin, hitloc ); + VectorNormalize2( incoming->s.pos.trDelta, hitdir ); } VectorSubtract( hitloc, self->client->renderInfo.eyePoint, diff ); diff[2] = 0; @@ -1625,9 +1687,9 @@ qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming zdiff = hitloc[2] - self->client->renderInfo.eyePoint[2];// + Q_irand(-6,6); //see if we can dodge if need-be - if ( (dist>16&&Q_irand( 0, 2 )) || self->client->ps.saberInFlight || !self->client->ps.saberActive ) + if ( (dist>16&&(Q_irand( 0, 2 )||saberBusy)) || self->client->ps.saberInFlight || !self->client->ps.saberActive ) {//either it will miss by a bit (and 25% chance) OR our saber is not in-hand OR saber is off - if ( self->NPC->rank == RANK_CREWMAN || self->NPC->rank >= RANK_LT_JG ) + if ( self->NPC && (self->NPC->rank == RANK_CREWMAN || self->NPC->rank >= RANK_LT_JG) ) {//acrobat or fencer or above if ( self->client->ps.groundEntityNum != ENTITYNUM_NONE &&//on the ground !(self->client->ps.pm_flags&PMF_DUCKED)&&ucmd.upmove>=0&&TIMER_Done( self, "duck" )//not ducking @@ -1647,15 +1709,18 @@ qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming { gi.Printf( "(%d) evading attack from height %4.2f, zdiff: %4.2f, rightdot: %4.2f\n", level.time, hitloc[2]-self->absmin[2],zdiff,rightdot); } - //UL = > -6 - //UR = > -9 - //TOP = > +4 + + //UL = > -1//-6 + //UR = > -6//-9 + //TOP = > +6//+4 //FIXME: take FP_SABER_DEFENSE into account here somehow? if ( zdiff >= -5 )//was 0 { if ( incoming || !saberBusy ) { - if ( rightdot > 12 || (rightdot > 3 && zdiff < 5) )//was normalized, 0.3 + if ( rightdot > 12 + || (rightdot > 3 && zdiff < 5) + || (!incoming&&fabs(hitdir[2])<0.25f) )//was normalized, 0.3 { if ( doDodge ) { @@ -1671,9 +1736,23 @@ qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming else { self->client->ps.saberBlocked = BLOCKED_UPPER_RIGHT; + evasionType = EVASION_PARRY; if ( self->client->ps.groundEntityNum != ENTITYNUM_NONE ) { - duckChance = 6; + if ( zdiff > 5 ) + { + TIMER_Start( self, "duck", Q_irand( 500, 1500 ) ); + evasionType = EVASION_DUCK_PARRY; + evaded = qtrue; + if ( d_JediAI->integer ) + { + gi.Printf( "duck " ); + } + } + else + { + duckChance = 6; + } } } if ( d_JediAI->integer ) @@ -1681,7 +1760,9 @@ qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming gi.Printf( "UR block\n" ); } } - else if ( rightdot < -12 || (rightdot < -3 && zdiff < 5) )//was normalized, -0.3 + else if ( rightdot < -12 + || (rightdot < -3 && zdiff < 5) + || (!incoming&&fabs(hitdir[2])<0.25f) )//was normalized, -0.3 { if ( doDodge ) { @@ -1697,9 +1778,23 @@ qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming else { self->client->ps.saberBlocked = BLOCKED_UPPER_LEFT; + evasionType = EVASION_PARRY; if ( self->client->ps.groundEntityNum != ENTITYNUM_NONE ) { - duckChance = 6; + if ( zdiff > 5 ) + { + TIMER_Start( self, "duck", Q_irand( 500, 1500 ) ); + evasionType = EVASION_DUCK_PARRY; + evaded = qtrue; + if ( d_JediAI->integer ) + { + gi.Printf( "duck " ); + } + } + else + { + duckChance = 6; + } } } if ( d_JediAI->integer ) @@ -1710,6 +1805,7 @@ qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming else { self->client->ps.saberBlocked = BLOCKED_TOP; + evasionType = EVASION_PARRY; if ( self->client->ps.groundEntityNum != ENTITYNUM_NONE ) { duckChance = 4; @@ -1721,10 +1817,24 @@ qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming } evaded = qtrue; } + else + { + if ( self->client->ps.groundEntityNum != ENTITYNUM_NONE ) + { + //duckChance = 2; + TIMER_Start( self, "duck", Q_irand( 500, 1500 ) ); + evasionType = EVASION_DUCK; + evaded = qtrue; + if ( d_JediAI->integer ) + { + gi.Printf( "duck " ); + } + } + } } - //LL = -18 to -39 - //LR = -20 to -41 - else if ( zdiff > -15 ) + //LL = -22//= -18 to -39 + //LR = -23//= -20 to -41 + else if ( zdiff > -22 )//was-15 ) { if ( 1 )//zdiff < -10 ) {//hmm, pretty low, but not low enough to use the low block, so we need to duck @@ -1732,6 +1842,7 @@ qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming { //duckChance = 2; TIMER_Start( self, "duck", Q_irand( 500, 1500 ) ); + evasionType = EVASION_DUCK; evaded = qtrue; if ( d_JediAI->integer ) { @@ -1753,6 +1864,14 @@ qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming else { self->client->ps.saberBlocked = BLOCKED_UPPER_RIGHT; + if ( evasionType == EVASION_DUCK ) + { + evasionType = EVASION_DUCK_PARRY; + } + else + { + evasionType = EVASION_PARRY; + } } if ( d_JediAI->integer ) { @@ -1768,6 +1887,14 @@ qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming else { self->client->ps.saberBlocked = BLOCKED_UPPER_LEFT; + if ( evasionType == EVASION_DUCK ) + { + evasionType = EVASION_DUCK_PARRY; + } + else + { + evasionType = EVASION_PARRY; + } } if ( d_JediAI->integer ) { @@ -1777,6 +1904,14 @@ qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming else { self->client->ps.saberBlocked = BLOCKED_TOP; + if ( evasionType == EVASION_DUCK ) + { + evasionType = EVASION_DUCK_PARRY; + } + else + { + evasionType = EVASION_PARRY; + } if ( d_JediAI->integer ) { gi.Printf( "mid-TOP block\n" ); @@ -1785,40 +1920,72 @@ qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming evaded = qtrue; } } - else if ( zdiff < -30 && ( zdiff < -40 || !Q_irand( 0, 2 ) ) )//2nd one was -46 + else if ( saberBusy || (zdiff < -36 && ( zdiff < -44 || !Q_irand( 0, 2 ) ) ) )//was -30 and -40//2nd one was -46 {//jump! if ( self->client->ps.groundEntityNum == ENTITYNUM_NONE ) {//already in air, duck to pull up legs TIMER_Start( self, "duck", Q_irand( 500, 1500 ) ); + evasionType = EVASION_DUCK; evaded = qtrue; if ( d_JediAI->integer ) { gi.Printf( "legs up\n" ); } + if ( incoming || !saberBusy ) + { + //since the jump may be cleared if not safe, set a lower block too + if ( rightdot >= 0 ) + { + self->client->ps.saberBlocked = BLOCKED_LOWER_RIGHT; + evasionType = EVASION_DUCK_PARRY; + if ( d_JediAI->integer ) + { + gi.Printf( "LR block\n" ); + } + } + else + { + self->client->ps.saberBlocked = BLOCKED_LOWER_LEFT; + evasionType = EVASION_DUCK_PARRY; + if ( d_JediAI->integer ) + { + gi.Printf( "LL block\n" ); + } + } + evaded = qtrue; + } } else {//gotta jump! - if ( (self->NPC->rank == RANK_CREWMAN || self->NPC->rank > RANK_LT_JG ) && + if ( self->NPC && (self->NPC->rank == RANK_CREWMAN || self->NPC->rank > RANK_LT_JG ) && (!Q_irand( 0, 10 ) || (!Q_irand( 0, 2 ) && (ucmd.forwardmove || ucmd.rightmove))) ) {//superjump //FIXME: check the jump, if can't, then block - self->client->ps.forceJumpCharge = 320;//FIXME: calc this intelligently - evaded = qtrue; - if ( d_JediAI->integer ) + if ( self->NPC && !(self->NPC->scriptFlags&SCF_NO_ACROBATICS) ) { - gi.Printf( "force jump + " ); + self->client->ps.forceJumpCharge = 320;//FIXME: calc this intelligently + evasionType = EVASION_FJUMP; + evaded = qtrue; + if ( d_JediAI->integer ) + { + gi.Printf( "force jump + " ); + } } } else {//normal jump //FIXME: check the jump, if can't, then block - ucmd.upmove = 127; - evaded = qtrue; - if ( d_JediAI->integer ) + if ( self->NPC && !(self->NPC->scriptFlags&SCF_NO_ACROBATICS) ) { - gi.Printf( "jump + " ); + ucmd.upmove = 127; + evasionType = EVASION_JUMP; + evaded = qtrue; + if ( d_JediAI->integer ) + { + gi.Printf( "jump + " ); + } } - if ( self->client->NPC_class == CLASS_TAVION && !incoming && NPC->client->ps.groundEntityNum < ENTITYNUM_NONE && !Q_irand( 0, 2 ) ) + if ( self->client->NPC_class == CLASS_TAVION && !incoming && self->client->ps.groundEntityNum < ENTITYNUM_NONE && !Q_irand( 0, 2 ) ) { if ( !PM_SaberInAttack( self->client->ps.saberMove ) && !PM_SaberInStart( self->client->ps.saberMove ) @@ -1835,6 +2002,7 @@ qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming { butterflyAnim = BOTH_BUTTERFLY_RIGHT; } + evasionType = EVASION_CARTWHEEL; NPC_SetAnim( self, SETANIM_BOTH, butterflyAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); self->client->ps.velocity[2] = 225; self->client->ps.forceJumpZStart = self->currentOrigin[2];//so we don't take damage if we land at same height @@ -1844,10 +2012,11 @@ qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming G_SoundOnEnt( self, CHAN_BODY, "sound/weapons/force/jump.wav" ); ucmd.upmove = 0; saberBusy = qtrue; + evaded = qtrue; } } } - if ( Jedi_CheckFlipEvasions( self, rightdot, zdiff ) ) + if ( ((evasionType = Jedi_CheckFlipEvasions( self, rightdot, zdiff ))!=EVASION_NONE) ) { if ( d_slowmodeath->integer > 5 && self->enemy && !self->enemy->s.number ) { @@ -1862,6 +2031,14 @@ qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming if ( rightdot >= 0 ) { self->client->ps.saberBlocked = BLOCKED_LOWER_RIGHT; + if ( evasionType == EVASION_JUMP ) + { + evasionType = EVASION_JUMP_PARRY; + } + else if ( evasionType == EVASION_NONE ) + { + evasionType = EVASION_PARRY; + } if ( d_JediAI->integer ) { gi.Printf( "LR block\n" ); @@ -1870,6 +2047,14 @@ qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming else { self->client->ps.saberBlocked = BLOCKED_LOWER_LEFT; + if ( evasionType == EVASION_JUMP ) + { + evasionType = EVASION_JUMP_PARRY; + } + else if ( evasionType == EVASION_NONE ) + { + evasionType = EVASION_PARRY; + } if ( d_JediAI->integer ) { gi.Printf( "LL block\n" ); @@ -1886,6 +2071,7 @@ qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming if ( rightdot >= 0 ) { self->client->ps.saberBlocked = BLOCKED_LOWER_RIGHT; + evasionType = EVASION_PARRY; if ( d_JediAI->integer ) { gi.Printf( "LR block\n" ); @@ -1894,6 +2080,7 @@ qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming else { self->client->ps.saberBlocked = BLOCKED_LOWER_LEFT; + evasionType = EVASION_PARRY; if ( d_JediAI->integer ) { gi.Printf( "LL block\n" ); @@ -1903,18 +2090,21 @@ qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming } } - if ( evaded ) + if ( evasionType == EVASION_NONE ) { - TIMER_Set( self, "taunting", 0 ); - //stop gripping - TIMER_Set( self, "gripping", -level.time ); - WP_ForcePowerStop( NPC, FP_GRIP ); + return EVASION_NONE; } + //stop taunting + TIMER_Set( self, "taunting", 0 ); + //stop gripping + TIMER_Set( self, "gripping", -level.time ); + WP_ForcePowerStop( self, FP_GRIP ); if ( dodgeAnim != -1 ) {//dodged - NPC_SetAnim( NPC, SETANIM_BOTH, dodgeAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); - NPC->client->ps.weaponTime = NPC->client->ps.torsoAnimTimer; + evasionType = EVASION_DODGE; + NPC_SetAnim( self, SETANIM_BOTH, dodgeAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + self->client->ps.weaponTime = self->client->ps.torsoAnimTimer; //force them to stop moving in this case self->client->ps.pm_time = self->client->ps.torsoAnimTimer; //FIXME: maybe make a sound? Like a grunt? EV_JUMP? @@ -1924,7 +2114,6 @@ qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming { G_StartMatrixEffect( self ); } - return qfalse; } else { @@ -1933,6 +2122,14 @@ qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming if ( !Q_irand( 0, duckChance ) ) { TIMER_Start( self, "duck", Q_irand( 500, 1500 ) ); + if ( evasionType == EVASION_PARRY ) + { + evasionType = EVASION_DUCK_PARRY; + } + else + { + evasionType = EVASION_DUCK; + } /* if ( d_JediAI->integer ) { @@ -1947,24 +2144,25 @@ qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming self->client->ps.saberBlocked = WP_MissileBlockForBlock( self->client->ps.saberBlocked ); } - if ( self->client->ps.saberBlocked != BLOCKED_NONE ) + } + //if ( self->client->ps.saberBlocked != BLOCKED_NONE ) + { + int parryReCalcTime = Jedi_ReCalcParryTime( self, evasionType ); + if ( self->client->ps.forcePowerDebounce[FP_SABER_DEFENSE] < level.time + parryReCalcTime ) { - int parryReCalcTime = Jedi_ReCalcParryTime( self ); - if ( self->client->ps.forcePowerDebounce[FP_SABER_DEFENSE] < level.time + parryReCalcTime ) - { - self->client->ps.forcePowerDebounce[FP_SABER_DEFENSE] = level.time + parryReCalcTime; - } + self->client->ps.forcePowerDebounce[FP_SABER_DEFENSE] = level.time + parryReCalcTime; } } - //blocked, not dodge - return qtrue; + return evasionType; } extern float ShortestLineSegBewteen2LineSegs( vec3_t start1, vec3_t end1, vec3_t start2, vec3_t end2, vec3_t close_pnt1, vec3_t close_pnt2 ); +extern int WPDEBUG_SaberColor( saber_colors_t saberColor ); static qboolean Jedi_SaberBlock( void ) { - vec3_t hitloc, saberTip, saberTipNext, top, bottom, axisPoint, saberPoint, dir; + vec3_t hitloc, saberTipOld, saberTip, top, bottom, axisPoint, saberPoint, dir;//saberBase, + //FIXME: reborn don't block enough anymore /* //maybe do this on easy only... or only on grunt-level reborn if ( NPC->client->ps.weaponTime ) @@ -1982,7 +2180,7 @@ static qboolean Jedi_SaberBlock( void ) {//can't move the saber to another position yet return qfalse; } - + /* if ( NPCInfo->rank < RANK_LT_JG && Q_irand( 0, (2 - g_spskill->integer) ) ) {//lower rank reborn have a random chance of not doing it at all @@ -1995,21 +2193,25 @@ static qboolean Jedi_SaberBlock( void ) {//don't keep blocking him once he's dead (or if not a client) return qfalse; } + /* //VectorMA( NPC->enemy->client->renderInfo.muzzlePoint, NPC->enemy->client->ps.saberLength, NPC->enemy->client->renderInfo.muzzleDir, saberTip ); //VectorMA( NPC->enemy->client->renderInfo.muzzlePointNext, NPC->enemy->client->ps.saberLength, NPC->enemy->client->renderInfo.muzzleDirNext, saberTipNext ); - VectorMA( NPC->enemy->client->renderInfo.muzzlePointOld, NPC->enemy->client->ps.saberLength, NPC->enemy->client->renderInfo.muzzleDirOld, saberTip ); - VectorMA( NPC->enemy->client->renderInfo.muzzlePoint, NPC->enemy->client->ps.saberLength, NPC->enemy->client->renderInfo.muzzleDir, saberTipNext ); + VectorMA( NPC->enemy->client->renderInfo.muzzlePointOld, NPC->enemy->client->ps.saberLength, NPC->enemy->client->renderInfo.muzzleDirOld, saberTipOld ); + VectorMA( NPC->enemy->client->renderInfo.muzzlePoint, NPC->enemy->client->ps.saberLength, NPC->enemy->client->renderInfo.muzzleDir, saberTip ); - VectorSubtract( saberTipNext, saberTip, dir );//get the dir - VectorAdd( dir, saberTipNext, saberTip );//extrapolate + VectorSubtract( NPC->enemy->client->renderInfo.muzzlePoint, NPC->enemy->client->renderInfo.muzzlePointOld, dir );//get the dir + VectorAdd( dir, NPC->enemy->client->renderInfo.muzzlePoint, saberBase );//extrapolate + + VectorSubtract( saberTip, saberTipOld, dir );//get the dir + VectorAdd( dir, saberTip, saberTipOld );//extrapolate VectorCopy( NPC->currentOrigin, top ); top[2] = NPC->absmax[2]; VectorCopy( NPC->currentOrigin, bottom ); bottom[2] = NPC->absmin[2]; - float dist = ShortestLineSegBewteen2LineSegs( NPC->enemy->client->renderInfo.muzzlePoint, saberTip, bottom, top, axisPoint, saberPoint ); - if ( dist > NPC->maxs[0]*3 ) + float dist = ShortestLineSegBewteen2LineSegs( saberBase, saberTipOld, bottom, top, saberPoint, axisPoint ); + if ( 0 )//dist > NPC->maxs[0]*4 )//was *3 {//FIXME: sometimes he reacts when you're too far away to actually hit him if ( d_JediAI->integer ) { @@ -2032,10 +2234,100 @@ static qboolean Jedi_SaberBlock( void ) { VectorCopy( tr.endpos, hitloc ); } + */ + vec3_t pointDir, baseDir, tipDir, saberHitPoint, saberMins={-4,-4,-4}, saberMaxs={4,4,4}; + float pointDist, baseDirPerc; + VectorMA( NPC->enemy->client->renderInfo.muzzlePointOld, NPC->enemy->client->ps.saberLength, NPC->enemy->client->renderInfo.muzzleDirOld, saberTipOld ); + VectorMA( NPC->enemy->client->renderInfo.muzzlePoint, NPC->enemy->client->ps.saberLength, NPC->enemy->client->renderInfo.muzzleDir, saberTip ); + VectorCopy( NPC->currentOrigin, top ); + top[2] = NPC->absmax[2]; + VectorCopy( NPC->currentOrigin, bottom ); + bottom[2] = NPC->absmin[2]; + + float dist = ShortestLineSegBewteen2LineSegs( NPC->enemy->client->renderInfo.muzzlePoint, saberTip, bottom, top, saberPoint, axisPoint ); + if ( dist > NPC->maxs[0]*5 )//was *3 + {//FIXME: sometimes he reacts when you're too far away to actually hit him + if ( d_JediAI->integer ) + { + gi.Printf( S_COLOR_RED"enemy saber dist: %4.2f\n", dist ); + } + if ( dist < 300 //close + && !Jedi_QuickReactions( NPC )//quick reaction people can interrupt themselves + && (PM_SaberInStart( NPC->enemy->client->ps.saberMove ) || PM_SaberInAttack( NPC->enemy->client->ps.saberMove )) )//enemy is swinging at me + {//he's swinging at me and close enough to be a threat, don't start an attack right now + TIMER_Set( NPC, "parryTime", 100 ); + } + else + { + TIMER_Set( NPC, "parryTime", -1 ); + } + return qfalse; + } + if ( d_JediAI->integer ) + { + gi.Printf( S_COLOR_GREEN"enemy saber dist: %4.2f\n", dist ); + } + + VectorSubtract( saberPoint, NPC->enemy->client->renderInfo.muzzlePoint, pointDir ); + pointDist = VectorLength( pointDir ); + + if ( NPC->enemy->client->ps.saberLength <= 0 ) + { + baseDirPerc = 0.5f; + } + else + { + baseDirPerc = pointDist/NPC->enemy->client->ps.saberLength; + } + VectorSubtract( NPC->enemy->client->renderInfo.muzzlePoint, NPC->enemy->client->renderInfo.muzzlePointOld, baseDir ); + VectorSubtract( saberTip, saberTipOld, tipDir ); + VectorScale( baseDir, baseDirPerc, baseDir ); + VectorMA( baseDir, 1.0f-baseDirPerc, tipDir, dir ); + VectorMA( saberPoint, 200, dir, hitloc ); + + //get the actual point of impact + trace_t tr; + gi.trace( &tr, saberPoint, saberMins, saberMaxs, hitloc, NPC->enemy->s.number, CONTENTS_BODY );//, G2_RETURNONHIT, 10 ); + if ( tr.allsolid || tr.startsolid || tr.fraction >= 1.0f ) + {//estimate + vec3_t dir2Me; + VectorSubtract( axisPoint, saberPoint, dir2Me ); + dist = VectorNormalize( dir2Me ); + if ( DotProduct( dir, dir2Me ) < 0.2f ) + {//saber is not swinging in my direction + if ( dist < 300 //close + && !Jedi_QuickReactions( NPC )//quick reaction people can interrupt themselves + && (PM_SaberInStart( NPC->enemy->client->ps.saberMove ) || PM_SaberInAttack( NPC->enemy->client->ps.saberMove )) )//enemy is swinging at me + {//he's swinging at me and close enough to be a threat, don't start an attack right now + TIMER_Set( NPC, "parryTime", 100 ); + } + else + { + TIMER_Set( NPC, "parryTime", -1 ); + } + return qfalse; + } + ShortestLineSegBewteen2LineSegs( saberPoint, hitloc, bottom, top, saberHitPoint, hitloc ); + /* + VectorSubtract( saberPoint, axisPoint, dir ); + VectorNormalize( dir ); + VectorMA( axisPoint, NPC->maxs[0]*1.22, dir, hitloc ); + */ + } + else + { + VectorCopy( tr.endpos, hitloc ); + } + + if ( d_JediAI->integer ) + { + G_DebugLine( saberPoint, hitloc, FRAMETIME, WPDEBUG_SaberColor( NPC->enemy->client->ps.saberColor ), qtrue ); + } //FIXME: if saber is off and/or we have force speed and want to be really cocky, // and the swing misses by some amount, we can use the dodges here... :) - if ( Jedi_SaberBlockGo( NPC, hitloc, NULL, dist ) ) + evasionType_t evasionType; + if ( (evasionType=Jedi_SaberBlockGo( NPC, hitloc, dir, NULL, dist )) != EVASION_DODGE ) {//we did block (not dodge) if ( !NPC->client->ps.saberInFlight ) {//make sure saber is on @@ -2043,7 +2335,7 @@ static qboolean Jedi_SaberBlock( void ) } //debounce our parry recalc time - int parryReCalcTime = Jedi_ReCalcParryTime( NPC ); + int parryReCalcTime = Jedi_ReCalcParryTime( NPC, evasionType ); TIMER_Set( NPC, "parryReCalcTime", Q_irand( 0, parryReCalcTime ) ); if ( d_JediAI->integer ) { @@ -2063,7 +2355,7 @@ static qboolean Jedi_SaberBlock( void ) } else {//others hold it longer - TIMER_Set( NPC, "parryTime", Q_irand( 2, 5 ) ); + TIMER_Set( NPC, "parryTime", Q_irand( 2, 4 )*parryReCalcTime ); } } } @@ -2301,7 +2593,7 @@ static void Jedi_EvasionSaber( vec3_t enemy_movedir, float enemy_dist, vec3_t en {//FIXME: check forcePushRadius[NPC->client->ps.forcePowerLevel[FP_PUSH]] ForceThrow( NPC, qfalse ); } - else if ( NPCInfo->rank == RANK_CREWMAN || NPCInfo->rank > RANK_LT_JG ) + else if ( (NPCInfo->rank==RANK_CREWMAN||NPCInfo->rank>RANK_LT_JG) && !(NPCInfo->scriptFlags&SCF_NO_ACROBATICS) ) {//FIXME: make this a function call? //FIXME: check for clearance, safety of landing spot? NPC->client->ps.forceJumpCharge = 480; @@ -2340,7 +2632,7 @@ static void Jedi_EvasionSaber( vec3_t enemy_movedir, float enemy_dist, vec3_t en { gi.Printf( "def strafe\n" ); } - if ( (NPCInfo->rank == RANK_CREWMAN || NPCInfo->rank > RANK_LT_JG ) && !Q_irand( 0, 5 ) ) + if ( !(NPCInfo->scriptFlags&SCF_NO_ACROBATICS) && (NPCInfo->rank == RANK_CREWMAN || NPCInfo->rank > RANK_LT_JG ) && !Q_irand( 0, 5 ) ) {//FIXME: make this a function call? //FIXME: check for clearance, safety of landing spot? NPC->client->ps.forceJumpCharge = 320; @@ -2783,6 +3075,7 @@ static void Jedi_CombatTimersUpdate( int enemy_dist ) if ( NPC->client->ps.saberEventFlags&SEF_PARRIED ) {//parried TIMER_Set( NPC, "parryTime", -1 ); + /* if ( NPCInfo->rank >= RANK_LT_JG ) { NPC->client->ps.forcePowerDebounce[FP_SABER_DEFENSE] = level.time + 100; @@ -2791,14 +3084,15 @@ static void Jedi_CombatTimersUpdate( int enemy_dist ) { NPC->client->ps.forcePowerDebounce[FP_SABER_DEFENSE] = level.time + 500; } - if ( PM_SaberInKnockaway( NPC->client->ps.saberMove ) ) + */ + if ( NPC->enemy && PM_SaberInKnockaway( NPC->enemy->client->ps.saberMove ) ) {//advance! Jedi_Aggression( NPC, 1 );//get closer Jedi_AdjustSaberAnimLevel( NPC, (NPC->client->ps.saberAnimLevel-1) );//use a faster attack } else { - if ( !Q_irand( 0, 2 ) )//FIXME: dependant on rank/diff? + if ( !Q_irand( 0, 1 ) )//FIXME: dependant on rank/diff? { //Com_Printf( "(%d) drop agg - we parried\n", level.time ); Jedi_Aggression( NPC, -1 ); @@ -2861,7 +3155,7 @@ static void Jedi_CombatTimersUpdate( int enemy_dist ) } else { - if ( !Q_irand( 0, 3 ) )//FIXME: dependant on rank/diff? + if ( !Q_irand( 0, 2 ) )//FIXME: dependant on rank/diff? { //Com_Printf( "(%d) drop agg - we were blocked\n", level.time ); Jedi_Aggression( NPC, -1 ); @@ -3346,6 +3640,10 @@ static qboolean Jedi_Jump( vec3_t dest, int goalEntNum ) static qboolean Jedi_TryJump( gentity_t *goal ) {//FIXME: never does a simple short, regular jump... //FIXME: I need to be on ground too! + if ( (NPCInfo->scriptFlags&SCF_NO_ACROBATICS) ) + { + return qfalse; + } if ( TIMER_Done( NPC, "jumpChaseDebounce" ) && (!goal->client || goal->client->ps.groundEntityNum != ENTITYNUM_NONE) && !PM_InKnockDown( &NPC->client->ps ) && !PM_InRoll( &NPC->client->ps ) ) @@ -3367,10 +3665,13 @@ static qboolean Jedi_TryJump( gentity_t *goal ) } else { + /* + //NO! All Jedi can jump-navigate now... if ( NPCInfo->rank != RANK_CREWMAN && NPCInfo->rank <= RANK_LT_JG ) {//can't do acrobatics return qfalse; } + */ if ( goal_z_diff > 0 || goal_xy_dist > 128 ) {//Fake a force-jump //Screw it, just do my own calc & throw @@ -3427,8 +3728,17 @@ static qboolean Jedi_TryJump( gentity_t *goal ) else */ {//FIXME: make this a function call + int jumpAnim; //FIXME: this should be more intelligent, like the normal force jump anim logic - NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_FLIP_F, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + if ( NPCInfo->rank != RANK_CREWMAN && NPCInfo->rank <= RANK_LT_JG ) + {//can't do acrobatics + jumpAnim = BOTH_FORCEJUMP1; + } + else + { + jumpAnim = BOTH_FLIP_F; + } + NPC_SetAnim( NPC, SETANIM_BOTH, jumpAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); } NPC->client->ps.forceJumpZStart = NPC->currentOrigin[2]; @@ -3532,6 +3842,12 @@ static void Jedi_CheckEnemyMovement( void ) static void Jedi_CheckJumps( void ) { + if ( (NPCInfo->scriptFlags&SCF_NO_ACROBATICS) ) + { + NPC->client->ps.forceJumpCharge = 0; + ucmd.upmove = 0; + return; + } //FIXME: should probably check this before AI decides that best move is to jump? Otherwise, they may end up just standing there and looking dumb //FIXME: all this work and he still jumps off ledges... *sigh*... need CONTENTS_BOTCLIP do-not-enter brushes...? vec3_t jumpVel = {0,0,0}; @@ -3659,7 +3975,7 @@ static void Jedi_Combat( void ) if ( !Jedi_ClearPathToSpot( enemy_dest, NPC->enemy->s.number ) ) {//hunt him down //gi.Printf( "No Clear Path\n" ); - if ( ( NPCInfo->rank == RANK_CREWMAN || NPCInfo->rank > RANK_LT_JG ) && (NPC_ClearLOS( NPC->enemy )||NPCInfo->enemyLastSeenTime>level.time-500) && NPC_FaceEnemy( qtrue ) ) + if ( (NPC_ClearLOS( NPC->enemy )||NPCInfo->enemyLastSeenTime>level.time-500) && NPC_FaceEnemy( qtrue ) )//( NPCInfo->rank == RANK_CREWMAN || NPCInfo->rank > RANK_LT_JG ) && { //try to jump to him? /* @@ -3844,21 +4160,21 @@ void NPC_Jedi_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, vec TIMER_Set( self, "parryTime", -1 ); if ( self->client->NPC_class == CLASS_DESANN ) {//less for Desann - self->client->ps.forcePowerDebounce[FP_SABER_DEFENSE] = level.time + 200; + self->client->ps.forcePowerDebounce[FP_SABER_DEFENSE] = level.time + (3-g_spskill->integer)*100; } else if ( self->NPC->rank >= RANK_LT_JG ) { - self->client->ps.forcePowerDebounce[FP_SABER_DEFENSE] = level.time + 500; + self->client->ps.forcePowerDebounce[FP_SABER_DEFENSE] = level.time + (3-g_spskill->integer)*300; } else { - self->client->ps.forcePowerDebounce[FP_SABER_DEFENSE] = level.time + 1500; + self->client->ps.forcePowerDebounce[FP_SABER_DEFENSE] = level.time + (3-g_spskill->integer)*500; } if ( !Q_irand( 0, 3 ) ) {//ouch... maybe switch up which saber power level we're using Jedi_AdjustSaberAnimLevel( self, Q_irand( FORCE_LEVEL_1, FORCE_LEVEL_3 ) ); } - if ( !Q_irand( 0, 2 ) )//damage > 20 || self->health < 40 || + if ( !Q_irand( 0, 1 ) )//damage > 20 || self->health < 40 || { //Com_Printf( "(%d) drop agg - hit by saber\n", level.time ); Jedi_Aggression( self, -1 ); @@ -4302,9 +4618,8 @@ static void Jedi_Attack( void ) ForceThrow( NPC, qfalse ); } //based on my skill, hit attack button every other to every several frames in order to push enemy back - else if ( Q_irand( 0, NPCInfo->rank ) > RANK_CIVILIAN - && !(NPC->client->ps.pm_flags&PMF_ATTACK_HELD) - && !Q_irand( 0, (3-g_spskill->integer) )) + else if ( Q_flrand( -2.0f, (float)(NPCInfo->rank)/2.0f+g_spskill->value ) >= 0.0f + && !(NPC->client->ps.pm_flags&PMF_ATTACK_HELD) ) { ucmd.buttons |= BUTTON_ATTACK; } @@ -4460,6 +4775,12 @@ static void Jedi_Attack( void ) ucmd.buttons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK); } + if ( NPCInfo->scriptFlags&SCF_NO_ACROBATICS ) + { + ucmd.upmove = 0; + NPC->client->ps.forceJumpCharge = 0; + } + Jedi_CheckDecreaseSaberAnimLevel(); if ( ucmd.buttons & BUTTON_ATTACK && NPC->client->playerTeam == TEAM_ENEMY ) diff --git a/code/game/AI_Mark1.cpp b/code/game/AI_Mark1.cpp index dfe1793..629276c 100644 --- a/code/game/AI_Mark1.cpp +++ b/code/game/AI_Mark1.cpp @@ -71,6 +71,7 @@ void NPC_Mark1_Precache(void) G_EffectIndex( "probeexplosion1"); G_EffectIndex( "blaster/smoke_bolton"); G_EffectIndex( "bryar/muzzle_flash"); + G_EffectIndex( "droidexplosion1" ); RegisterItem( FindItemForAmmo( AMMO_METAL_BOLTS)); RegisterItem( FindItemForAmmo( AMMO_POWERCELL )); @@ -190,7 +191,7 @@ void Mark1Dead_FireBlaster (void) Mark1_die ------------------------- */ -void Mark1_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc ) +void Mark1_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags,int hitLoc ) { int anim; @@ -204,6 +205,7 @@ void Mark1_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int DeathFX(self); self->client->ps.eFlags |= EF_NODRAW; self->contents = CONTENTS_CORPSE; + G_FreeEntity( self ); // Is this safe? I can't see why we'd mark it nodraw and then just leave it around?? } return; } diff --git a/code/game/AI_Mark2.cpp b/code/game/AI_Mark2.cpp index 96446a0..958c751 100644 --- a/code/game/AI_Mark2.cpp +++ b/code/game/AI_Mark2.cpp @@ -27,6 +27,17 @@ enum gentity_t *CreateMissile( vec3_t org, vec3_t dir, float vel, int life, gentity_t *owner, qboolean altFire = qfalse ); +void NPC_Mark2_Precache( void ) +{ + G_EffectIndex( "small_chunks" ); + G_EffectIndex( "droidexplosion1" ); + G_EffectIndex( "mouseexplosion1" ); + G_EffectIndex( "blaster/smoke_bolton" ); + G_EffectIndex( "bryar/muzzle_flash" ); + G_SoundIndex( "sound/chars/mark1/misc/shoot1.wav" ); + +} + /* ------------------------- NPC_Mark2_Part_Explode @@ -116,8 +127,6 @@ void Mark2_FireBlaster(qboolean advance) gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, muzzle1 ); - G_PlayEffect( "blaster/muzzle_flash", muzzle1 ); - if (NPC->health) { CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_org1 ); @@ -130,6 +139,8 @@ void Mark2_FireBlaster(qboolean advance) AngleVectors (NPC->currentAngles, forward, vright, up); } + G_PlayEffect( "bryar/muzzle_flash", muzzle1, forward ); + G_Sound( NPC, G_SoundIndex("sound/chars/mark1/misc/shoot1.wav")); missile = CreateMissile( muzzle1, forward, 1600, 10000, NPC ); diff --git a/code/game/AI_MineMonster.cpp b/code/game/AI_MineMonster.cpp index 43eeca5..5f0b860 100644 --- a/code/game/AI_MineMonster.cpp +++ b/code/game/AI_MineMonster.cpp @@ -120,11 +120,11 @@ void MineMonster_TryDamage( gentity_t *enemy, int damage ) if ( tr.entityNum >= 0 && tr.entityNum < ENTITYNUM_NONE ) { G_Damage( &g_entities[tr.entityNum], NPC, NPC, dir, tr.endpos, damage, DAMAGE_NO_KNOCKBACK, MOD_MELEE ); - G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/mine/misc/bite%i.wav", Q_irand(1,4))); + G_SoundOnEnt( NPC, CHAN_VOICE_ATTEN, va("sound/chars/mine/misc/bite%i.wav", Q_irand(1,4))); } else { - G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/mine/misc/miss%i.wav", Q_irand(1,4))); + G_SoundOnEnt( NPC, CHAN_VOICE_ATTEN, va("sound/chars/mine/misc/miss%i.wav", Q_irand(1,4))); } } @@ -138,7 +138,7 @@ void MineMonster_Attack( void ) || random() > 0.8f )) { // Going to do ATTACK4 - TIMER_Set( NPC, "attacking", 1650 + random() * 350 ); + TIMER_Set( NPC, "attacking", 2350 + random() * 250 ); NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK4, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD ); TIMER_Set( NPC, "attack2_dmg", 1250 ); // level two damage diff --git a/code/game/AI_Sentry.cpp b/code/game/AI_Sentry.cpp index 818d823..eef0060 100644 --- a/code/game/AI_Sentry.cpp +++ b/code/game/AI_Sentry.cpp @@ -38,10 +38,13 @@ NPC_Sentry_Precache */ void NPC_Sentry_Precache(void) { - G_SoundIndex( "sound/chars/sentry/misc/death.wav"); - G_SoundIndex( "sound/chars/sentry/misc/hover.wav"); - G_SoundIndex( "sound/chars/sentry/misc/pain01.wav"); - G_SoundIndex( "sound/chars/sentry/misc/shieldsopen.wav"); + G_SoundIndex( "sound/chars/sentry/misc/death.wav" ); + G_SoundIndex( "sound/chars/sentry/misc/sentry_pain.mp3" ); + G_SoundIndex( "sound/chars/sentry/misc/sentry_shield_open.mp3" ); + G_SoundIndex( "sound/chars/sentry/misc/sentry_shield_close.mp3" ); + G_SoundIndex( "sound/chars/sentry/misc/sentry_hover_1_lp.wav" ); + G_SoundIndex( "sound/chars/sentry/misc/sentry_hover_2_lp.wav" ); + // G_SoundIndex( "sound/chars/sentry/misc/shoot.wav"); for ( int i = 1; i < 4; i++) @@ -50,10 +53,9 @@ void NPC_Sentry_Precache(void) } G_EffectIndex( "bryar/muzzle_flash"); - G_EffectIndex( "env/med_explosion"); + G_EffectIndex( "env/med_explode"); RegisterItem( FindItemForAmmo( AMMO_BLASTER )); - } /* @@ -67,7 +69,8 @@ void sentry_use( gentity_t *self, gentity_t *other, gentity_t *activator) self->flags &= ~FL_SHIELDED; NPC_SetAnim( self, SETANIM_BOTH, BOTH_POWERUP1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); - self->NPC->localState = LSTATE_WAKEUP; +// self->NPC->localState = LSTATE_WAKEUP; + self->NPC->localState = LSTATE_ACTIVE; } /* @@ -85,7 +88,8 @@ void NPC_Sentry_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, v TIMER_Set( self, "attackDelay", Q_irand( 9000, 12000) ); self->flags |= FL_SHIELDED; NPC_SetAnim( self, SETANIM_BOTH, BOTH_FLY_SHIELDED, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); - G_Sound( self, G_SoundIndex( "sound/chars/turret/shutdown.wav" )); + G_Sound( NPC, G_SoundIndex( "sound/chars/misc/sentry/sentry_pain.mp3" )); + self->NPC->localState = LSTATE_ACTIVE; } @@ -132,7 +136,7 @@ void Sentry_Fire (void) { NPCInfo->localState = LSTATE_POWERING_UP; - G_Sound( NPC, G_SoundIndex( "sound/chars/turret/startup.wav" )); + G_Sound( NPC, G_SoundIndex( "sound/chars/sentry/misc/sentry_shield_open.mp3" )); NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_POWERUP1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); TIMER_Set( NPC, "powerup", 250 ); return; @@ -206,6 +210,8 @@ void Sentry_MaintainHeight( void ) { float dif; + NPC->s.loopSound = G_SoundIndex( "sound/chars/sentry/misc/sentry_hover_1_lp.wav" ); + // Update our angles regardless NPC_UpdateAngles( qtrue, qtrue ); @@ -419,7 +425,7 @@ void Sentry_RangedAttack( qboolean visible, qboolean advance ) TIMER_Set( NPC, "attackDelay", Q_irand( 2000, 3500) ); NPC->flags |= FL_SHIELDED; NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_FLY_SHIELDED, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); - G_Sound( NPC, G_SoundIndex( "sound/chars/turret/shutdown.wav" )); + G_Sound( NPC, G_SoundIndex( "sound/chars/sentry/misc/sentry_shield_close.mp3" )); } else { @@ -443,6 +449,8 @@ void Sentry_AttackDecision( void ) // Always keep a good height off the ground Sentry_MaintainHeight(); + NPC->s.loopSound = G_SoundIndex( "sound/chars/sentry/misc/sentry_hover_2_lp.wav" ); + //randomly talk if ( TIMER_Done(NPC,"patrolNoise") ) { @@ -513,7 +521,6 @@ void NPC_Sentry_Patrol( void ) if ( UpdateGoal() ) { //start loop sound once we move - NPC->s.loopSound = G_SoundIndex( "sound/chars/sentry/misc/hover.wav" ); ucmd.buttons |= BUTTON_WALKING; NPC_MoveToGoal( qtrue ); } @@ -542,9 +549,9 @@ void NPC_BSSentry_Default( void ) NPC->e_UseFunc = useF_sentry_use; } - // Don't attack if waking up or if no enemy if (( NPC->enemy ) && (NPCInfo->localState != LSTATE_WAKEUP)) { + // Don't attack if waking up or if no enemy Sentry_AttackDecision(); } else if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES ) diff --git a/code/game/AI_Sniper.cpp b/code/game/AI_Sniper.cpp index 311a06c..eaa67bb 100644 --- a/code/game/AI_Sniper.cpp +++ b/code/game/AI_Sniper.cpp @@ -492,7 +492,11 @@ qboolean Sniper_EvaluateShot( int hit ) return qfalse; } - if ( hit == NPC->enemy->s.number || (&g_entities[hit] != NULL && (g_entities[hit].svFlags&SVF_GLASS_BRUSH)) ) + gentity_t *hitEnt = &g_entities[hit]; + if ( hit == NPC->enemy->s.number + || ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam ) + || ( hitEnt && hitEnt->takedamage && ((hitEnt->svFlags&SVF_GLASS_BRUSH)||hitEnt->health < 40||NPC->s.weapon == WP_EMPLACED_GUN) ) + || ( hitEnt && (hitEnt->svFlags&SVF_GLASS_BRUSH)) ) {//can hit enemy or will hit glass, so shoot anyway return qtrue; } @@ -702,10 +706,7 @@ void NPC_BSSniper_Attack( void ) gi.trace ( &tr, muzzle, NULL, NULL, end, NPC->s.number, MASK_SHOT, G2_RETURNONHIT, 0 ); int hit = tr.entityNum; - /* //can we shoot our target? - int hit = NPC_ShotEntity( NPC->enemy ); - */ if ( Sniper_EvaluateShot( hit ) ) { VectorCopy( NPC->enemy->currentOrigin, NPCInfo->enemyLastSeenLocation ); diff --git a/code/game/AI_Stormtrooper.cpp b/code/game/AI_Stormtrooper.cpp index ca860ae..6f637c2 100644 --- a/code/game/AI_Stormtrooper.cpp +++ b/code/game/AI_Stormtrooper.cpp @@ -48,6 +48,7 @@ qboolean NPC_CheckPlayerTeamStealth( void ); static qboolean enemyLOS; static qboolean enemyCS; +static qboolean enemyInFOV; static qboolean hitAlly; static qboolean faceEnemy; static qboolean move; @@ -55,6 +56,8 @@ static qboolean shoot; static float enemyDist; static vec3_t impactPos; +int groupSpeechDebounceTime[TEAM_NUM_TEAMS];//used to stop several group AI from speaking all at once + //Local state enums enum { @@ -137,14 +140,19 @@ static void ST_Speech( gentity_t *self, int speechType, float failChance ) if ( failChance >= 0 ) {//a negative failChance makes it always talk if ( self->NPC->group ) - { + {//group AI speech debounce timer if ( self->NPC->group->speechDebounceTime > level.time ) { return; } } else if ( !TIMER_Done( self, "chatter" ) ) - { + {//personal timer + return; + } + else if ( groupSpeechDebounceTime[self->client->playerTeam] > level.time ) + {//for those not in group AI + //FIXME: let certain speech types interrupt others? Let closer NPCs interrupt farther away ones? return; } } @@ -157,6 +165,7 @@ static void ST_Speech( gentity_t *self, int speechType, float failChance ) else { TIMER_Set( self, "chatter", Q_irand( 2000, 4000 ) ); + groupSpeechDebounceTime[self->client->playerTeam] = level.time + Q_irand( 1000, 2000 ); } if ( self->NPC->blockedSpeechDebounceTime > level.time ) @@ -727,6 +736,8 @@ qboolean NPC_CheckPlayerTeamStealth( void ) gentity_t *enemy; for ( int i = 0; i < ENTITYNUM_WORLD; i++ ) { + if(!PInUse(i)) + continue; enemy = &g_entities[i]; if ( enemy && enemy->client && NPC_ValidEnemy( enemy ) && enemy->client->playerTeam == NPC->client->enemyTeam ) { @@ -1389,10 +1400,14 @@ static void ST_CheckFireState( void ) //See if we should continue to fire on their last position //!TIMER_Done( NPC, "stick" ) || - if ( !hitAlly && NPCInfo->enemyLastSeenTime > 0 && NPCInfo->group && (NPCInfo->group->numState[SQUAD_RETREAT]>0||NPCInfo->group->numState[SQUAD_TRANSITION]>0||NPCInfo->group->numState[SQUAD_SCOUT]>0) ) + if ( !hitAlly //we're not going to hit an ally + && enemyInFOV //enemy is in our FOV //FIXME: or we don't have a clear LOS? + && NPCInfo->enemyLastSeenTime > 0 //we've seen the enemy + && NPCInfo->group //have a group + && (NPCInfo->group->numState[SQUAD_RETREAT]>0||NPCInfo->group->numState[SQUAD_TRANSITION]>0||NPCInfo->group->numState[SQUAD_SCOUT]>0) )//laying down covering fire { - if ( level.time - NPCInfo->enemyLastSeenTime < 10000 && - (!NPCInfo->group || level.time - NPCInfo->group->lastSeenEnemyTime < 10000 )) + if ( level.time - NPCInfo->enemyLastSeenTime < 10000 &&//we have seem the enemy in the last 10 seconds + (!NPCInfo->group || level.time - NPCInfo->group->lastSeenEnemyTime < 10000 ))//we are not in a group or the group has seen the enemy in the last 10 seconds { if ( !Q_irand( 0, 10 ) ) { @@ -2427,7 +2442,7 @@ void NPC_BSST_Attack( void ) return; } - enemyLOS = enemyCS = qfalse; + enemyLOS = enemyCS = enemyInFOV = qfalse; move = qtrue; faceEnemy = qfalse; shoot = qfalse; @@ -2435,6 +2450,16 @@ void NPC_BSST_Attack( void ) VectorClear( impactPos ); enemyDist = DistanceSquared( NPC->currentOrigin, NPC->enemy->currentOrigin ); + vec3_t enemyDir, shootDir; + VectorSubtract( NPC->enemy->currentOrigin, NPC->currentOrigin, enemyDir ); + VectorNormalize( enemyDir ); + AngleVectors( NPC->client->ps.viewangles, shootDir, NULL, NULL ); + float dot = DotProduct( enemyDir, shootDir ); + if ( dot > 0.5f ||( enemyDist * (1.0f-dot)) < 10000 ) + {//enemy is in front of me or they're very close and not behind me + enemyInFOV = qtrue; + } + if ( enemyDist < MIN_ROCKET_DIST_SQUARED )//128 {//enemy within 128 if ( (NPC->client->ps.weapon == WP_FLECHETTE || NPC->client->ps.weapon == WP_REPEATER) && @@ -2465,11 +2490,15 @@ void NPC_BSST_Attack( void ) hitAlly = qtrue;//us! //FIXME: if too close, run away! } - else - { + else if ( enemyInFOV ) + {//if enemy is FOV, go ahead and check for shooting int hit = NPC_ShotEntity( NPC->enemy, impactPos ); - if ( hit == NPC->enemy->s.number || (&g_entities[hit] != NULL && g_entities[hit].takedamage && ((g_entities[hit].svFlags&SVF_GLASS_BRUSH)||g_entities[hit].health < 40||NPC->s.weapon == WP_EMPLACED_GUN) ) ) - {//can hit enemy or will hit glass or other minor breakable (or in emplaced gun), so shoot anyway + gentity_t *hitEnt = &g_entities[hit]; + + if ( hit == NPC->enemy->s.number + || ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam ) + || ( hitEnt && hitEnt->takedamage && ((hitEnt->svFlags&SVF_GLASS_BRUSH)||hitEnt->health < 40||NPC->s.weapon == WP_EMPLACED_GUN) ) ) + {//can hit enemy or enemy ally or will hit glass or other minor breakable (or in emplaced gun), so shoot anyway AI_GroupUpdateClearShotTime( NPCInfo->group ); enemyCS = qtrue; NPC_AimAdjust( 2 );//adjust aim better longer we have clear shot at enemy @@ -2479,7 +2508,6 @@ void NPC_BSST_Attack( void ) {//Hmm, have to get around this bastard NPC_AimAdjust( 1 );//adjust aim better longer we can see enemy ST_ResolveBlockedShot( hit ); - gentity_t *hitEnt = &g_entities[hit]; if ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->playerTeam ) {//would hit an ally, don't fire!!! hitAlly = qtrue; @@ -2489,6 +2517,10 @@ void NPC_BSST_Attack( void ) } } } + else + { + enemyCS = qfalse;//not true, but should stop us from firing + } } } else if ( gi.inPVS( NPC->enemy->currentOrigin, NPC->currentOrigin ) ) @@ -2568,6 +2600,10 @@ void NPC_BSST_Attack( void ) if ( !faceEnemy ) {//we want to face in the dir we're running + if ( !move ) + {//if we haven't moved, we should look in the direction we last looked? + VectorCopy( NPC->client->ps.viewangles, NPCInfo->lastPathAngles ); + } NPCInfo->desiredYaw = NPCInfo->lastPathAngles[YAW]; NPCInfo->desiredPitch = 0; NPC_UpdateAngles( qtrue, qtrue ); diff --git a/code/game/AI_Utils.cpp b/code/game/AI_Utils.cpp index b8e95cf..587c59f 100644 --- a/code/game/AI_Utils.cpp +++ b/code/game/AI_Utils.cpp @@ -452,8 +452,13 @@ void AI_GetGroup( gentity_t *self ) VectorCopy( self->NPC->group->enemy->currentOrigin, self->NPC->group->enemyLastSeenPos ); } - for ( i = 0, member = &g_entities[0]; i < globals.num_entities ; i++, member++) +// for ( i = 0, member = &g_entities[0]; i < globals.num_entities ; i++, member++) + for ( i = 0; i < globals.num_entities ; i++) { + if(!PInUse(i)) + continue; + member = &g_entities[i]; + if ( !AI_ValidateGroupMember( self->NPC->group, member ) ) {//FIXME: keep track of those who aren't angry yet and see if we should wake them after we assemble the core group continue; @@ -570,7 +575,7 @@ void AI_GroupMemberKilled( gentity_t *self ) else { ST_AggressionAdjust( member, -1 ); - member->NPC->currentAim -= Q_irand( 0, 2);//drop their aim accuracy + member->NPC->currentAim -= Q_irand( 0, 10 );//Q_irand( 0, 2);//drop their aim accuracy } } //okay, if I'm the group commander, make everyone else flee @@ -610,9 +615,9 @@ void AI_GroupMemberKilled( gentity_t *self ) ST_MarkToCover( member ); } } - member->NPC->currentAim -= Q_irand( 1, 3 );//drop their aim accuracy even more + member->NPC->currentAim -= Q_irand( 1, 15 ); //Q_irand( 1, 3 );//drop their aim accuracy even more } - member->NPC->currentAim -= Q_irand( 1, 3 );//drop their aim accuracy even more + member->NPC->currentAim -= Q_irand( 1, 15 ); //Q_irand( 1, 3 );//drop their aim accuracy even more } } } diff --git a/code/game/NPC.cpp b/code/game/NPC.cpp index 6ab1bf3..b48e1f9 100644 --- a/code/game/NPC.cpp +++ b/code/game/NPC.cpp @@ -32,6 +32,9 @@ extern void NPC_BSCinematic( void ); extern int GetTime ( int lastTime ); extern void NPC_BSGM_Default( void ); +extern cvar_t *g_dismemberment; +extern cvar_t *g_realisticSaberDamage; + //Local Variables // ai debug cvars cvar_t *debugNPCAI; // used to print out debug info about the bot AI @@ -86,6 +89,14 @@ void CorpsePhysics( gentity_t *self ) } } + if ( level.time - self->s.time > 3000 ) + {//been dead for 3 seconds + if ( g_dismemberment->integer < 4 && !g_realisticSaberDamage->integer ) + {//can't be dismembered once dead + self->client->dismembered = qtrue; + } + } + if ( level.time - self->s.time > 500 ) {//don't turn "nonsolid" until about 1 second after actual death @@ -153,7 +164,7 @@ void NPC_RemoveBody( gentity_t *self ) /// if ( self->client->playerTeam == TEAM_SCAVENGERS || self->client->playerTeam == TEAM_KLINGON // || self->client->playerTeam == TEAM_HIROGEN || self->client->playerTeam == TEAM_MALON ) // should I check NPC_class here instead of TEAM ? - dmv - if( self->client->playerTeam == TEAM_ENEMY ) + if( self->client->playerTeam == TEAM_ENEMY || self->client->NPC_class == CLASS_PROTOCOL ) { self->nextthink = level.time + FRAMETIME; // try back in a second @@ -258,7 +269,7 @@ int BodyRemovalPadTime( gentity_t *ent ) case CLASS_GONK: case CLASS_R2D2: case CLASS_R5D2: - case CLASS_PROTOCOL: + //case CLASS_PROTOCOL: case CLASS_MARK1: case CLASS_MARK2: case CLASS_PROBE: @@ -330,7 +341,7 @@ static void NPC_RemoveBodyEffect(void) case CLASS_GONK: case CLASS_R2D2: case CLASS_R5D2: - case CLASS_PROTOCOL: + //case CLASS_PROTOCOL: case CLASS_MARK1: case CLASS_MARK2: case CLASS_INTERROGATOR: @@ -776,7 +787,7 @@ static void DeadThink ( void ) // check for droids if ( npc_class == CLASS_SEEKER || npc_class == CLASS_REMOTE || npc_class == CLASS_PROBE || npc_class == CLASS_MOUSE || npc_class == CLASS_GONK || npc_class == CLASS_R2D2 || npc_class == CLASS_R5D2 || - npc_class == CLASS_PROTOCOL || npc_class == CLASS_MARK2 || npc_class == CLASS_SENTRY ) + npc_class == CLASS_MARK2 || npc_class == CLASS_SENTRY )//npc_class == CLASS_PROTOCOL || { NPC->client->ps.eFlags |= EF_NODRAW; NPCInfo->timeOfDeath = level.time + FRAMETIME * 8; @@ -996,7 +1007,8 @@ void NPC_HandleAIFlags (void) if ( NPCInfo->ffireFadeDebounce < level.time ) { NPCInfo->ffireCount--; - NPCInfo->ffireFadeDebounce = level.time + 2000; + //Com_Printf( "drop: %d < %d\n", NPCInfo->ffireCount, 3+((2-g_spskill->integer)*2) ); + NPCInfo->ffireFadeDebounce = level.time + 3000; } } @@ -1580,6 +1592,19 @@ void NPC_RunBehavior( int team, int bState ) else if ( NPC->client->ps.weapon == WP_EMPLACED_GUN ) { NPC_BSEmplaced(); + if ( NPC->client->playerTeam == TEAM_PLAYER && NPCInfo->charmedTime && NPCInfo->charmedTime < level.time && NPC->client ) + {//we were charmed, set us back! + //NOTE: presumptions here... + team_t savTeam = NPC->client->enemyTeam; + NPC->client->enemyTeam = NPC->client->playerTeam; + NPC->client->playerTeam = savTeam; + NPC->client->leader = NULL; + if ( NPCInfo->tempBehavior == BS_FOLLOW_LEADER ) + { + NPCInfo->tempBehavior = BS_DEFAULT; + } + G_ClearEnemy( NPC ); + } return; } else if ( NPC->client->ps.weapon == WP_SABER ) @@ -1959,7 +1984,7 @@ void NPC_Think ( gentity_t *self)//, int msec ) VectorCopy( self->client->ps.moveDir, oldMoveDir ); VectorClear( self->client->ps.moveDir ); // see if NPC ai is frozen - if ( debugNPCFreeze->value ) + if ( debugNPCFreeze->value || (NPC->svFlags&SVF_ICARUS_FREEZE) ) { NPC_UpdateAngles( qtrue, qtrue ); ClientThink(self->s.number, &ucmd); @@ -2082,16 +2107,16 @@ void NPC_Think ( gentity_t *self)//, int msec ) void NPC_InitAI ( void ) { - debugNoRoam = gi.cvar ( "d_noroam", "0", 0 ); - debugNPCAimingBeam = gi.cvar ( "d_npcaiming", "0", 0 ); - debugBreak = gi.cvar ( "d_break", "0", 0 ); + debugNoRoam = gi.cvar ( "d_noroam", "0", CVAR_CHEAT ); + debugNPCAimingBeam = gi.cvar ( "d_npcaiming", "0", CVAR_CHEAT ); + debugBreak = gi.cvar ( "d_break", "0", CVAR_CHEAT ); - debugNPCAI = gi.cvar ( "d_npcai", "0", 0 ); - debugNPCFreeze = gi.cvar ( "d_npcfreeze", "0", 0 ); - d_JediAI = gi.cvar ( "d_JediAI", "0", 0 ); - d_noGroupAI = gi.cvar ( "d_noGroupAI", "0", 0 ); - d_asynchronousGroupAI = gi.cvar ( "d_asynchronousGroupAI", "1", 0 ); - d_altRoutes = gi.cvar ( "d_altRoutes", "0", 0 ); + debugNPCAI = gi.cvar ( "d_npcai", "0", CVAR_CHEAT ); + debugNPCFreeze = gi.cvar ( "d_npcfreeze", "0", CVAR_CHEAT); + d_JediAI = gi.cvar ( "d_JediAI", "0", CVAR_CHEAT ); + d_noGroupAI = gi.cvar ( "d_noGroupAI", "0", CVAR_CHEAT ); + d_asynchronousGroupAI = gi.cvar ( "d_asynchronousGroupAI", "1", CVAR_CHEAT ); + d_altRoutes = gi.cvar ( "d_altRoutes", "1", CVAR_CHEAT ); //0 = never (BORING) //1 = kyle only @@ -2102,7 +2127,7 @@ void NPC_InitAI ( void ) //6 = also when kyle takes pain or enemy jedi dodges player saber swing or does an acrobatic evasion d_slowmodeath = gi.cvar ( "d_slowmodeath", "3", CVAR_ARCHIVE );//save this setting - d_saberCombat = gi.cvar ( "d_saberCombat", "0", 0 ); + d_saberCombat = gi.cvar ( "d_saberCombat", "0", CVAR_CHEAT ); } /* diff --git a/code/game/NPC_behavior.cpp b/code/game/NPC_behavior.cpp index cb7958a..f4ac19b 100644 --- a/code/game/NPC_behavior.cpp +++ b/code/game/NPC_behavior.cpp @@ -548,9 +548,20 @@ void NPC_BSFollowLeader (void) NPCInfo->enemyCheckDebounceTime = level.time + Q_irand( 3000, 10000 ); } } - else if ( NPC->client->ps.weapon && NPCInfo->enemyCheckDebounceTime < level.time ) + else { - NPC_CheckEnemy( (NPCInfo->confusionTimetempBehavior!=BS_FOLLOW_LEADER), qfalse );//don't find new enemy if this is tempbehav + if ( NPC->enemy->health <= 0 || (NPC->enemy->flags&FL_NOTARGET) ) + { + G_ClearEnemy( NPC ); + if ( NPCInfo->enemyCheckDebounceTime > level.time + 1000 ) + { + NPCInfo->enemyCheckDebounceTime = level.time + Q_irand( 1000, 2000 ); + } + } + else if ( NPC->client->ps.weapon && NPCInfo->enemyCheckDebounceTime < level.time ) + { + NPC_CheckEnemy( (NPCInfo->confusionTimetempBehavior!=BS_FOLLOW_LEADER), qfalse );//don't find new enemy if this is tempbehav + } } if ( NPC->enemy && NPC->client->ps.weapon ) @@ -609,11 +620,12 @@ void NPC_BSFollowLeader (void) } //leader visible? - leaderVis = NPC_CheckVisibility( NPC->client->leader, CHECK_PVS|CHECK_360|CHECK_FOV|CHECK_SHOOT ); + leaderVis = NPC_CheckVisibility( NPC->client->leader, CHECK_PVS|CHECK_360|CHECK_SHOOT );// ent->e_UseFunc = useF_NULL; + //Follow leader, stay within visibility and a certain distance, maintain a distance from. curAnim = NPC->client->ps.legsAnim; - if(curAnim != BOTH_ATTACK1 && curAnim != BOTH_ATTACK2 && curAnim != BOTH_ATTACK3 && curAnim != BOTH_MELEE1 && curAnim != BOTH_MELEE2 ) + if ( curAnim != BOTH_ATTACK1 && curAnim != BOTH_ATTACK2 && curAnim != BOTH_ATTACK3 && curAnim != BOTH_MELEE1 && curAnim != BOTH_MELEE2 ) {//Don't move toward leader if we're in a full-body attack anim //FIXME, use IdealDistance to determine if we need to close distance float followDist = 96.0f;//FIXME: If there are enmies, make this larger? @@ -1601,7 +1613,9 @@ void NPC_BSEmplaced( void ) enemyLOS = qtrue; int hit = NPC_ShotEntity( NPC->enemy, impactPos ); - if ( hit == NPC->enemy->s.number || (&g_entities[hit] != NULL && g_entities[hit].takedamage ) ) + gentity_t *hitEnt = &g_entities[hit]; + + if ( hit == NPC->enemy->s.number || ( hitEnt && hitEnt->takedamage ) ) {//can hit enemy or will hit glass or other minor breakable (or in emplaced gun), so shoot anyway enemyCS = qtrue; NPC_AimAdjust( 2 );//adjust aim better longer we have clear shot at enemy diff --git a/code/game/NPC_combat.cpp b/code/game/NPC_combat.cpp index 8c44489..0f71754 100644 --- a/code/game/NPC_combat.cpp +++ b/code/game/NPC_combat.cpp @@ -123,6 +123,193 @@ qboolean G_TeamEnemy( gentity_t *self ) return qfalse; } +void G_AttackDelay( gentity_t *self, gentity_t *enemy ) +{ + if ( enemy && self->client && self->NPC ) + {//delay their attack based on how far away they're facing from enemy + vec3_t fwd, dir; + int attDelay; + + VectorSubtract( self->client->renderInfo.eyePoint, enemy->currentOrigin, dir );//purposely backwards + AngleVectors( self->client->renderInfo.eyeAngles, fwd, NULL, NULL ); + //dir[2] = fwd[2] = 0;//ignore z diff? + + attDelay = (4-g_spskill->integer)*500;//initial: from 1000ms delay on hard to 2000ms delay on easy + if ( self->client->playerTeam == TEAM_PLAYER ) + {//invert + attDelay = 2000-attDelay; + } + attDelay += floor( (DotProduct( fwd, dir )+1.0f) * 2000.0f );//add up to 4000ms delay if they're facing away + + //FIXME: should distance matter, too? + + //Now modify the delay based on NPC_class, weapon, and team + //NOTE: attDelay should be somewhere between 1000 to 6000 milliseconds + switch ( self->client->NPC_class ) + { + case CLASS_IMPERIAL://they give orders and hang back + attDelay += Q_irand( 500, 1500 ); + break; + case CLASS_STORMTROOPER://stormtroopers shoot sooner + if ( self->NPC->rank >= RANK_LT ) + {//officers shoot even sooner + attDelay -= Q_irand( 500, 1500 ); + } + else + {//normal stormtroopers don't have as fast reflexes as officers + attDelay -= Q_irand( 0, 1000 ); + } + break; + case CLASS_SWAMPTROOPER://shoot very quickly? What about guys in water? + attDelay -= Q_irand( 1000, 2000 ); + break; + case CLASS_IMPWORKER://they panic, don't fire right away + attDelay += Q_irand( 1000, 2500 ); + break; + case CLASS_TRANDOSHAN: + attDelay -= Q_irand( 500, 1500 ); + break; + case CLASS_JAN: + case CLASS_LANDO: + case CLASS_PRISONER: + case CLASS_REBEL: + attDelay -= Q_irand( 500, 1500 ); + break; + case CLASS_GALAKMECH: + case CLASS_ATST: + attDelay -= Q_irand( 1000, 2000 ); + break; + case CLASS_REELO: + case CLASS_UGNAUGHT: + return; + break; + case CLASS_MINEMONSTER: + case CLASS_MURJJ: + return; + break; + case CLASS_INTERROGATOR: + case CLASS_PROBE: + case CLASS_MARK1: + case CLASS_MARK2: + case CLASS_SENTRY: + return; + break; + case CLASS_REMOTE: + case CLASS_SEEKER: + return; + break; + /* + case CLASS_GRAN: + case CLASS_RODIAN: + case CLASS_WEEQUAY: + break; + case CLASS_JEDI: + case CLASS_SHADOWTROOPER: + case CLASS_TAVION: + case CLASS_REBORN: + case CLASS_LUKE: + case CLASS_DESANN: + break; + */ + } + + switch ( self->s.weapon ) + { + case WP_NONE: + case WP_SABER: + return; + break; + case WP_BRYAR_PISTOL: + break; + case WP_BLASTER: + if ( self->NPC->scriptFlags & SCF_ALT_FIRE ) + {//rapid-fire blasters + attDelay += Q_irand( 0, 500 ); + } + else + {//regular blaster + attDelay -= Q_irand( 0, 500 ); + } + break; + case WP_BOWCASTER: + attDelay += Q_irand( 0, 500 ); + break; + case WP_REPEATER: + if ( !(self->NPC->scriptFlags&SCF_ALT_FIRE) ) + {//rapid-fire blasters + attDelay += Q_irand( 0, 500 ); + } + break; + case WP_FLECHETTE: + attDelay += Q_irand( 500, 1500 ); + break; + case WP_ROCKET_LAUNCHER: + attDelay += Q_irand( 500, 1500 ); + break; + case WP_BLASTER_PISTOL: // apparently some enemy only version of the blaster + attDelay -= Q_irand( 500, 1500 ); + break; + case WP_DISRUPTOR://sniper's don't delay? + return; + break; + case WP_THERMAL://grenade-throwing has a built-in delay + return; + break; + case WP_MELEE: // Any ol' melee attack + return; + break; + case WP_EMPLACED_GUN: + return; + break; + case WP_TURRET: // turret guns + return; + break; + case WP_BOT_LASER: // Probe droid - Laser blast + return; + break; + /* + case WP_DEMP2: + break; + case WP_TRIP_MINE: + break; + case WP_DET_PACK: + break; + case WP_STUN_BATON: + break; + case WP_ATST_MAIN: + break; + case WP_ATST_SIDE: + break; + case WP_TIE_FIGHTER: + break; + case WP_RAPID_FIRE_CONC: + break; + */ + } + + if ( self->client->playerTeam == TEAM_PLAYER ) + {//clamp it + if ( attDelay > 2000 ) + { + attDelay = 2000; + } + } + + //don't shoot right away + TIMER_Set( self, "attackDelay", attDelay );//Q_irand( 1500, 4500 ) ); + //don't move right away either + if ( attDelay > 4000 ) + { + attDelay = 4000 - Q_irand(500, 1500); + } + else + { + attDelay -= Q_irand(500, 1500); + } + + TIMER_Set( self, "roamTime", attDelay );//was Q_irand( 1000, 3500 ); + } +} /* ------------------------- G_SetEnemy @@ -221,10 +408,12 @@ void G_SetEnemy( gentity_t *self, gentity_t *enemy ) } if ( self->s.weapon == WP_BLASTER || self->s.weapon == WP_REPEATER || - self->s.weapon == WP_THERMAL || self->s.weapon == WP_BLASTER_PISTOL ) - { + self->s.weapon == WP_THERMAL || self->s.weapon == WP_BLASTER_PISTOL + || self->s.weapon == WP_BOWCASTER ) + {//Hmm, how about sniper and bowcaster? //When first get mad, aim is bad - G_AimSet( self, Q_irand( self->NPC->stats.aim - 9, self->NPC->stats.aim - 3 ) ); + //Hmm, base on game difficulty, too? Rank? + G_AimSet( self, Q_irand( self->NPC->stats.aim - (15*(3-g_spskill->integer)), self->NPC->stats.aim - (5*(3-g_spskill->integer)) ) ); } //Alert anyone else in the area @@ -233,18 +422,9 @@ void G_SetEnemy( gentity_t *self, gentity_t *enemy ) G_AngerAlert( self ); } - //TEMP HACK: Stormtroopers don't fire right away! - if ( self->s.weapon == WP_BLASTER ) - { - if ( self->NPC && self->NPC->rank == RANK_LT ) - { - TIMER_Set( self, "attackDelay", Q_irand( 400, 1000 ) ); - } - else - { - TIMER_Set( self, "attackDelay", Q_irand( 750, 2500 ) ); - } - } + //Stormtroopers don't fire right away! + G_AttackDelay( self, enemy ); + //FIXME: this is a disgusting hack that is supposed to make the Imperials start with their weapon holstered- need a better way if ( self->client->ps.weapon == WP_NONE && !Q_strncmp( self->NPC_type, "imp", 3 ) && !(self->NPC->scriptFlags & SCF_FORCED_MARCH) ) { @@ -1829,11 +2009,12 @@ int NPC_ShotEntity( gentity_t *ent, vec3_t impactPos ) {//they want to know *where* the hit would be, too VectorCopy( tr.endpos, impactPos ); } +/* // NPCs should be able to shoot even if the muzzle would be inside their target if ( tr.startsolid || tr.allsolid ) { return ENTITYNUM_NONE; } - +*/ return tr.entityNum; } @@ -2592,13 +2773,19 @@ gentity_t *NPC_SearchForWeapons( void ) { gentity_t *found = g_entities, *bestFound = NULL; float dist, bestDist = Q3_INFINITE; - - for ( found = g_entities; found < &g_entities[globals.num_entities] ; found++) + int i; +// for ( found = g_entities; found < &g_entities[globals.num_entities] ; found++) + for ( i = 0; iinuse ) - { +// if ( !found->inuse ) +// { +// continue; +// } + if(!PInUse(i)) continue; - } + + found=&g_entities[i]; + if ( found->s.eType != ET_ITEM ) { continue; @@ -2671,7 +2858,10 @@ void NPC_AimAdjust( int change ) { if ( !TIMER_Exists( NPC, "aimDebounce" ) ) { - TIMER_Set( NPC, "aimDebounce", Q_irand( 1500, 4000 ) ); + int debounce = 500+(3-g_spskill->integer)*100; + TIMER_Set( NPC, "aimDebounce", Q_irand( debounce,debounce+1000 ) ); + //int debounce = 1000+(3-g_spskill->integer)*500; + //TIMER_Set( NPC, "aimDebounce", Q_irand( debounce, debounce+2000 ) ); return; } if ( TIMER_Done( NPC, "aimDebounce" ) ) @@ -2681,12 +2871,30 @@ void NPC_AimAdjust( int change ) {//can never be better than max aim NPCInfo->currentAim = NPCInfo->stats.aim; } - TIMER_Set( NPC, "aimDebounce", Q_irand( 1500, 4000 ) ); + else if ( NPCInfo->currentAim < -30 ) + {//can never be worse than this + NPCInfo->currentAim = -30; + } + + //Com_Printf( "%s new aim = %d\n", NPC->NPC_type, NPCInfo->currentAim ); + + int debounce = 500+(3-g_spskill->integer)*100; + TIMER_Set( NPC, "aimDebounce", Q_irand( debounce,debounce+1000 ) ); + //int debounce = 1000+(3-g_spskill->integer)*500; + //TIMER_Set( NPC, "aimDebounce", Q_irand( debounce, debounce+2000 ) ); } } void G_AimSet( gentity_t *self, int aim ) { - self->NPC->currentAim = aim; - TIMER_Set( self, "aimDebounce", Q_irand( 1500, 4000 ) ); + if ( self->NPC ) + { + self->NPC->currentAim = aim; + //Com_Printf( "%s new aim = %d\n", self->NPC_type, self->NPC->currentAim ); + + int debounce = 500+(3-g_spskill->integer)*100; + TIMER_Set( self, "aimDebounce", Q_irand( debounce,debounce+1000 ) ); + // int debounce = 1000+(3-g_spskill->integer)*500; + // TIMER_Set( self, "aimDebounce", Q_irand( debounce,debounce+2000 ) ); + } } diff --git a/code/game/NPC_formation.cpp b/code/game/NPC_formation.cpp deleted file mode 100644 index 0260bf6..0000000 --- a/code/game/NPC_formation.cpp +++ /dev/null @@ -1,2478 +0,0 @@ -//NPC_formation.cpp - -//Question- possible to make one formation one unit of another larger formation? -//#define NPC_FORMATION_CPP - -// leave this line at the top for all NPC_xxxx.cpp files... -#include "g_headers.h" - - - - - -#include "b_local.h" -#include "anims.h" -#include "g_nav.h" -#include "g_squad.h" - -#include "g_navigator.h" - -extern CNavigator navigator; - -#define MAX_INTEREST_DIST ( 256 * 256 ) -extern qboolean G_ClearLineOfSight( const vec3_t point1, const vec3_t point2, int ignore, int clipmask ); -extern void G_AddVoiceEvent( gentity_t *self, int event, int speakDebounceTime ); -extern void NPC_AimWiggle( vec3_t enemy_org ); -extern int PM_PickAnim( gentity_t *self, int minAnim, int maxAnim ); -extern qboolean PM_HasAnimation( gentity_t *ent, int animation ); -//========================================================================================= -/* -------------------------- -NPC_SquadIdle -------------------------- -*/ - -//FIXME: This is a lovely legacy solution... -extern int teamLastEnemyTime[]; -static int squadSpeechDebounceTime = 0; -extern int teamEnemyCount[TEAM_NUM_TEAMS]; -void NPC_SquadIdle( void ) -{ - //say something when we've finished fighting for about 3 seconds and it's all clear - if ( ( level.time - teamLastEnemyTime[TEAM_STARFLEET] > 3000 ) && - ( level.time - teamLastEnemyTime[TEAM_STARFLEET] < 3500 ) ) - { - if ( Q_irand(0, 3) == 0 ) - {//indicate all clear - if ( squadSpeechDebounceTime < level.time ) - { - G_AddVoiceEvent( NPC, Q_irand( EV_SETTLE1, EV_SETTLE3 ), 3000 ); - squadSpeechDebounceTime = level.time + 2000; - } - } - else - {//don't say it this time - squadSpeechDebounceTime = level.time + 2000; - } - } - - //regenerate 10 points of health per second when not in combat - if ( NPC->health < NPC->max_health && (NPC->flags & FL_UNDYING) ) - { - NPC->health++; - } -} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -#define FORMATION_GAP_DIAG 32 -#define FORMATION_GAP_FBLR 64 -#define MAX_DIST_TO_LEADER_SQUARED (128 * 128) -#define MAX_IDLE_TIME 7 - -static vec3_t form_forward; -static vec3_t form_right; -static float form_speed; -static float form_forward_speed; -static vec3_t form_dir; -static vec3_t form_angles; -extern int form_shot_traces; -extern int form_updateseg_traces; -extern int form_leaderseg_traces; -extern int form_closestSP_traces; -extern int form_clearpath_traces; - -extern qboolean NAV_ClearPathToPoint( gentity_t *self, vec3_t pmins, vec3_t pmaxs, vec3_t point, int clipmask ); -extern lookMode_t NPC_FindLocalInterestPoint( gentity_t *self, vec3_t lookVec ); -extern int PM_AnimLength( int index, animNumber_t anim ); -extern void NPC_UpdateLastSquadPoint ( gentity_t *self ); -extern void G_UpdateFormationGoals( gentity_t *self ); -extern qboolean ValidAnimFileIndex( int index ); -extern qboolean NPC_BSPointCombat( void ); -extern void G_AddVoiceEvent( gentity_t *self, int event, int speakDebounceTime ); - -/* -------------------------- -NPC_BlockingPlayer -------------------------- -*/ - -static qboolean NPC_BlockingPlayer( vec3_t blockDir ) -{ - vec3_t playerDir, velocityDir; - - //Get the player's move direction and speed - if ( VectorNormalize2( g_entities[0].client->ps.velocity, velocityDir ) == 0 ) - return qfalse; - - //Get our direction to the player - VectorSubtract( NPC->currentOrigin, g_entities[0].currentOrigin, playerDir ); - float distance = VectorNormalize( playerDir ); - - //Getting really close, let's back off - if ( distance < 32 ) - { - VectorCopy( playerDir, blockDir ); - return qtrue; - } - - //Too far, forget it - if ( distance > 128 ) - return qfalse; - - //See if it's even feasible - float dot = DotProduct( playerDir, velocityDir ); - - if ( dot < 0.85 ) - return qfalse; - - vec3_t testPos; - vec3_t ptmins, ptmaxs, tmins, tmaxs; - - VectorMA( g_entities[0].currentOrigin, distance, velocityDir, testPos ); - - VectorAdd( NPC->currentOrigin, NPC->mins, tmins ); - VectorAdd( NPC->currentOrigin, NPC->maxs, tmaxs ); - - VectorAdd( testPos, g_entities[0].mins, ptmins ); - VectorAdd( testPos, g_entities[0].maxs, ptmaxs ); - - if ( G_BoundsOverlap( ptmins, ptmaxs, tmins, tmaxs ) ) - { - VectorCopy( velocityDir, blockDir ); - return qtrue; - } - - return qfalse; -} - -/* -------------------------- -NPC_Unobstruct -------------------------- -*/ - -#define AVOID_ITERATIONS 5 - -void NPC_Unobstruct( vec3_t dir ) -{ - vec3_t moveAngles, testPos; - trace_t tr; - - vectoangles( dir, moveAngles ); - - moveAngles[YAW] = AngleNormalize360( moveAngles[YAW] - 90 ); - - for ( int i = 0; i < AVOID_ITERATIONS; i++ ) - { - AngleVectors( moveAngles, testPos, NULL, NULL ); - VectorMA( NPC->currentOrigin, 64, testPos, testPos ); - - if ( NAV_CheckAhead( NPC, testPos, tr, NPC->clipmask|CONTENTS_BOTCLIP ) ) - { - NPCInfo->combatMove = qtrue; - ucmd.buttons |= BUTTON_WALKING; - NPC_SetMoveGoal( NPC, testPos, 4, qtrue ); - NPC_SlideMoveToGoal(); - NPC_UpdateAngles( qtrue, qtrue ); - return; - } - - moveAngles[YAW] = AngleNormalize360( moveAngles[YAW] + 45 ); - } -} - -/* -------------------------- -G_CreateFormation -------------------------- -*/ - -void G_CreateFormation( gentity_t *self ) -{ - gentity_t *found = NULL, *last; - int num = 0, i, j; - - //Must be a valid target - if( ( self == NULL ) || ( self->client == NULL ) ) - return; - - //Must have a squad name - if( VALIDSTRING( self->client->squadname ) == false ) - return; - - //assign all the followers: - last = self; - for ( found = &g_entities[1], j = 1; j < ENTITYNUM_WORLD; j++, found++ ) - { - //found = &g_entities[j]; - - //Don't need to assign self - if ( found == self ) - continue; - - if ( !found || !found->client ) - continue; - - //Can't assign a dead NPC or non-NPC - if ( !found->NPC || found->health<=0 ) - continue; - - //Must have a valid squad name - if ( VALIDSTRING( found->client->squadname ) == false ) - continue; - - //Must share our squadname - if ( Q_stricmp( self->client->squadname, found->client->squadname ) != 0 ) - continue; - - //Can't possibly give him a path to follow - if ( VALIDSTRING( found->script_targetname ) == false ) - continue; - - //Go through each squadPath and give them theirs - for ( i = 0; i < num_squad_paths; i++ ) - { - if ( Q_stricmp( found->script_targetname, squadPaths[i].ownername ) == 0 ) - { - found->NPC->iSquadPathIndex = i; - found->NPC->iSquadRouteIndex= i; - break; - } - } - - //See if we succeeded putting them on a path - if ( found->NPC->iSquadPathIndex == -1 ) - { - //FIXME: What to do if they don't have a squadpath? Just follow player and keep a distance? - //FIXME: Emit this warning properly -#ifndef FINAL_BUILD - gi.Printf("No squadPath for %s\n", found->script_targetname); -#endif//FINAL_BUILD - } - else - { - //New member added - num++; - - //link them up - last->client->follower = found; - found->client->leader = last; - - //make self the leader - found->client->team_leader = self; - - //Put them in BS_BEHAVIOR - found->NPC->behaviorState = BS_FORMATION; - found->NPC->tempBehavior = BS_DEFAULT; - - //we need to make them head to their first squadPoint - found->NPC->sPCurSegPoint1 = found->NPC->sPCurSegPoint2 = -1; - found->NPC->sPDestSegPoint1 = found->NPC->sPDestSegPoint2 = 0;//head for the first one - VectorCopy(squadPaths[found->NPC->iSquadPathIndex].waypoints[0].origin, found->NPC->sPDestPos); - found->NPC->aiFlags |= NPCAI_OFF_PATH; - found->NPC->aiFlags &= ~NPCAI_FORM_TELE_NAV; - found->NPC->lastSquadPoint = -1; -// found->NPC->scriptFlags |= SCF_CAREFUL;//Set them to always use walk2/stand2/run2 - VectorClear(found->NPC->leaderTeleportSpot); - } - - last = found; - } - - //Set ourself as the team leader - self->client->team_leader = self; - - //Debug warning - if ( num == 0 ) - { - //FIXME: Or... this means that no one was around to form up -#ifndef FINAL_BUILD - gi.Printf( S_COLOR_RED"ERROR: formation %s could not be formed!\n", self->client->squadname ); -#endif//FINAL_BUILD - } -} - -/* -------------------------- -G_FindClosestPointOnLineSegment -------------------------- -*/ - -qboolean G_FindClosestPointOnLineSegment( const vec3_t start, const vec3_t end, const vec3_t from, vec3_t result ) -{ - vec3_t vecStart2From, vecStart2End, vecEnd2Start, vecEnd2From; - float distEnd2From, distEnd2Result, theta, cos_theta; - - //Find the perpendicular vector to vec from start to end - VectorSubtract( from, start, vecStart2From); - VectorSubtract( end, start, vecStart2End); - - float dot = DotProductNormalize( vecStart2From, vecStart2End ); - - if ( dot <= 0 ) - { - //The perpendicular would be beyond or through the start point - VectorCopy( start, result ); - return qfalse; - } - - if ( dot == 1 ) - { - //parallel, closer of 2 points will be the target - if( (VectorLengthSquared( vecStart2From )) < (VectorLengthSquared( vecStart2End )) ) - { - VectorCopy( from, result ); - } - else - { - VectorCopy( end, result ); - } - return qfalse; - } - - //Try other end - VectorSubtract( from, end, vecEnd2From); - VectorSubtract( start, end, vecEnd2Start); - - dot = DotProductNormalize( vecEnd2From, vecEnd2Start ); - - if ( dot <= 0 ) - {//The perpendicular would be beyond or through the start point - VectorCopy( end, result ); - return qfalse; - } - - if ( dot == 1 ) - {//parallel, closer of 2 points will be the target - if( (VectorLengthSquared( vecEnd2From )) < (VectorLengthSquared( vecEnd2Start ))) - { - VectorCopy( from, result ); - } - else - { - VectorCopy( end, result ); - } - return qfalse; - } - - // /| - // c / | - // / |a - // theta /)__| - // b - //cos(theta) = b / c - //solve for b - //b = cos(theta) * c - - //angle between vecs end2from and end2start, should be between 0 and 90 - theta = 90 * (1 - dot);//theta - - //Get length of side from End2Result using sine of theta - distEnd2From = VectorLength( vecEnd2From );//c - cos_theta = cos(DEG2RAD(theta));//cos(theta) - distEnd2Result = cos_theta * distEnd2From;//b - - //Extrapolate to find result - VectorNormalize( vecEnd2Start ); - VectorMA( end, distEnd2Result, vecEnd2Start, result ); - - //perpendicular intersection is between the 2 endpoints - return qtrue; -} - -float G_PointDistFromLineSegment( const vec3_t start, const vec3_t end, const vec3_t from ) -{ - vec3_t vecStart2From, vecStart2End, vecEnd2Start, vecEnd2From, intersection; - float distEnd2From, distStart2From, distEnd2Result, theta, cos_theta; - - //Find the perpendicular vector to vec from start to end - VectorSubtract( from, start, vecStart2From); - VectorSubtract( end, start, vecStart2End); - VectorSubtract( from, end, vecEnd2From); - VectorSubtract( start, end, vecEnd2Start); - - float dot = DotProductNormalize( vecStart2From, vecStart2End ); - - distStart2From = Distance( start, from ); - distEnd2From = Distance( end, from ); - - if ( dot <= 0 ) - { - //The perpendicular would be beyond or through the start point - return distStart2From; - } - - if ( dot == 1 ) - { - //parallel, closer of 2 points will be the target - return ((distStart2FromNPC->lastSPCalcedOrg ) ) - return keyWpIndex; - - //Save this as our last calc spot - VectorCopy( center, self->NPC->lastSPCalcedOrg ); - - //calc relative distances for all - for ( int i = 0; i < squadPath->numWaypoints; i++ ) - { - VectorSubtract( squadPath->waypoints[i].origin, center, vec ); - - //Exaggerate the z diff to weight the cost towards level connections - vec[2] *= 10; - - dists[i] = (unsigned long) VectorLengthSquared( vec ); - } - - //Clear the double check - memset( &distAlreadyRanked, qfalse, sizeof( distAlreadyRanked ) ); - - minDist = 0; - - //Rank all waypoints - for ( i = 0; i < squadPath->numWaypoints; i++ ) - { - bestDist = (unsigned long) -1; - - for ( int j = 0; j < squadPath->numWaypoints; j++ ) - { - if ( distAlreadyRanked[j] ) - continue; - - if ( dists[j] <= bestDist && dists[j] >= minDist ) - { - bestDist = dists[j]; - squadPath->closestWaypoints[i] = j; - if ( keyWp != -1 ) - { - if ( keyWp == j ) - { - keyWpIndex = i; - } - } - } - } - - distAlreadyRanked[squadPath->closestWaypoints[i]] = qtrue; - minDist = bestDist; - } - - return keyWpIndex; - -#else //NOTENOTE: Old version - - float dists[MAX_WAYPOINTS_IN_PATH]; - float minDist, bestDist; - qboolean distAlreadyRanked[MAX_WAYPOINTS_IN_PATH]; - int i, j; - vec3_t vec; - - if ( VectorCompare( center, self->NPC->lastSPCalcedOrg ) ) - {//We calced distances for this position last time, just return; - return -1; - } - - VectorCopy( center, self->NPC->lastSPCalcedOrg ); - - //FIXME: init these to -1 each frame??? - //calc relative distances for all - for ( i = 0; i < squadPath->numWaypoints; i++ ) - { - VectorSubtract( squadPath->waypoints[i].origin, center, vec ); - //Ranges are too large (> Q3_INFINITE), we have to sqrt them - //Exaggerate the z diff - vec[2] *= 10; - - dists[i] = VectorLength( vec ); - } - - //rank them - for ( j = 0; j < squadPath->numWaypoints; j++ ) - { - distAlreadyRanked[j] = qfalse; - } - - minDist = -1; - for ( i = 0; i < squadPath->numWaypoints; i++ ) - { - bestDist = Q3_INFINITE; - for ( j = 0; j < squadPath->numWaypoints; j++ ) - { - if ( distAlreadyRanked[j] ) - { - continue; - } - - if ( dists[j] <= bestDist && dists[j] >= minDist ) - { - bestDist = dists[j]; - squadPath->closestWaypoints[i] = j; - if ( keyWp != -1 ) - { - if ( keyWp == j ) - { - keyWpIndex = i; - } - } - } - } - distAlreadyRanked[squadPath->closestWaypoints[i]] = qtrue; - minDist = bestDist; - } - - return keyWpIndex; -#endif - -} - - -/* -------------------------- -NPC_SetRelativeGoalOrgOnPath - - FIXME: Try to push NPC ahead or back along path if in the player's line of fire. - - ALSO: If being pushed out of someone's way, apply it here. - - ALSO: If blocked by another NPC, ask him to move (in main routine) - - Currently, with the "pushing" of other NPCs out of our way removed, NPCs wiggle - back and forth endlessly when trying to get around someone. -------------------------- -*/ - -void NPC_SetRelativeGoalOrgOnPath(gentity_t *self, vec3_t goalOrg) -{ - squadPath_t *squadPath = &squadPaths[self->NPC->iSquadPathIndex]; - vec3_t pathDir, vec, segVec, goalVec; - float toNextWpDist, lead, point1valuemult, point2valuemult, segLength, goalLength; - int nextWp, lastWp, i; - qboolean backwards = qfalse; - - //For the segment passed in, see how far ahead/behind we should be - //dest segment dir/length - VectorSubtract( squadPath->waypoints[self->NPC->sPDestSegPoint1].origin, squadPath->waypoints[self->NPC->sPDestSegPoint2].origin, segVec ); - segLength = VectorNormalize( segVec ); - //dir/dist from leader to dest segment corner - VectorSubtract( squadPath->waypoints[self->NPC->sPDestSegPoint1].origin, goalOrg, goalVec ); - goalLength = VectorNormalize( goalVec ); - - //GoalLength should never be larger than segLength! That would mean leader is - //farther away from one of the segment points than the other end of the segment! - assert(goalLength <= segLength); - - point1valuemult = (segLength - goalLength) / segLength;//1 if close to point 1 - point2valuemult = 1 - point1valuemult;//1 if far from point 1 - - lead = ( (squadPath->waypoints[self->NPC->sPDestSegPoint1].leadDist * point1valuemult) + (squadPath->waypoints[self->NPC->sPDestSegPoint2].leadDist * point2valuemult) ); - //This way it gradually changes, never snaps - - //IDEA: If in the player's way, move! - if ( self->NPC->goalDistToPathSeg < DEFAULT_PLAYER_RADIUS*2 ) - {//Leader is very close to our path - if ( (lead == 0) || (lead < 0 && lead > -64) ) - {//Back us up - lead = -128 + self->NPC->goalDistToPathSeg; - } - else if ( lead > 0 && lead < 64 ) - {//move us ahead - lead = 128 - self->NPC->goalDistToPathSeg; - } - } - - //In the correct spot? - if ( lead == 0 ) - return; - - if ( lead < 0.0f ) - { - backwards = qtrue; - lead = fabs( lead ); - } - - //What is the general forward direction of our path? - if ( self->NPC->sPDestSegPoint1 < self->NPC->sPDestSegPoint2 ) - { - if ( !backwards ) - { - nextWp = self->NPC->sPDestSegPoint2; - lastWp = self->NPC->sPDestSegPoint1; - VectorSubtract( squadPath->waypoints[self->NPC->sPDestSegPoint2].origin, squadPath->waypoints[self->NPC->sPDestSegPoint1].origin, pathDir ); - } - else - { - nextWp = self->NPC->sPDestSegPoint1; - lastWp = self->NPC->sPDestSegPoint2; - VectorSubtract( squadPath->waypoints[self->NPC->sPDestSegPoint1].origin, squadPath->waypoints[self->NPC->sPDestSegPoint2].origin, pathDir ); - } - } - else - { - if ( !backwards ) - { - nextWp = self->NPC->sPDestSegPoint1; - lastWp = self->NPC->sPDestSegPoint2; - VectorSubtract( squadPath->waypoints[self->NPC->sPDestSegPoint1].origin, squadPath->waypoints[self->NPC->sPDestSegPoint2].origin, pathDir ); - } - else - { - nextWp = self->NPC->sPDestSegPoint2; - lastWp = self->NPC->sPDestSegPoint1; - VectorSubtract( squadPath->waypoints[self->NPC->sPDestSegPoint2].origin, squadPath->waypoints[self->NPC->sPDestSegPoint1].origin, pathDir ); - } - } - - VectorNormalize( pathDir ); - - while ( lead > 0.0f ) - {//FIXME: do we need to calc toNextWpDist? Shouldn't it be the cost of the route between them? - VectorSubtract( squadPath->waypoints[nextWp].origin, goalOrg, vec ); - toNextWpDist = VectorLength( vec ); - if ( toNextWpDist == lead ) - { - VectorCopy( squadPath->waypoints[nextWp].origin, goalOrg ); - lead = 0.0f; - } - else if ( toNextWpDist > lead ) - {//We want to stop somewhere before this next Wp - VectorMA( goalOrg, lead, pathDir, goalOrg ); - lead = 0.0f; - } - else - {//We have more to go - VectorCopy( squadPath->waypoints[nextWp].origin, goalOrg ); - if ( squadPath->waypoints[nextWp].flags & SPF_BRANCH ) - {//This one is a branch, we don't want to go past it until told to - //IDEA: Maybe pick the branch they're closest to? Bad thing here is if - // the player is standing on this point, we don't want to crowd him, ever - lead = 0.0f; - } - else - {//Only has one possible nextWp - for ( i = 0; i < MAX_PATH_BRANCHES; i++ ) - { - if ( squadPath->waypoints[nextWp].nextWp[i] != -1 && squadPath->waypoints[nextWp].nextWp[i] != lastWp ) - {//Don't double back, we do this because one of the neighbors is going to be - //the one we just came from! - lastWp = nextWp; - nextWp = squadPath->waypoints[nextWp].nextWp[i]; - break; - } - } - - if ( i == MAX_PATH_BRANCHES ) - {//checked all branches, no nextwp, stop here - lead = 0.0f; - } - else - {//Follow the branch - //Set the dir of the next branch - VectorSubtract( squadPath->waypoints[nextWp].origin, squadPath->waypoints[lastWp].origin, pathDir ); - VectorNormalize( pathDir ); - //subtract the lead dist - lead -= toNextWpDist; - } - } - } - } - - self->NPC->sPDestSegPoint1 = lastWp; - self->NPC->sPDestSegPoint2 = nextWp; - return; -} - -/* -------------------------- -NPC_FindClosestSquadPoint -------------------------- -*/ - -int NPC_FindClosestSquadPoint( gentity_t *self ) -{ - int maxToCheck; - - //Build our distances - NPC_BuildSquadPointDistances( self, self->currentOrigin, &squadPaths[self->NPC->iSquadPathIndex], WAYPOINT_NONE ); - - //Only check the closest eight - //FIXME: This is an ugly implementation - if ( squadPaths[self->NPC->iSquadPathIndex].numWaypoints < 8 ) - { - maxToCheck = squadPaths[self->NPC->iSquadPathIndex].numWaypoints; - } - else - { - maxToCheck = 8; - } - - //Check those points - for ( int i = 0; i < maxToCheck; i++ ) - { - //FIXME: Remove these in release - form_closestSP_traces++; - - //If the point is good, go there - if ( NAV_ClearPathToPoint(self, self->mins, self->maxs, squadPaths[self->NPC->iSquadPathIndex].waypoints[squadPaths[self->NPC->iSquadPathIndex].closestWaypoints[i]].origin, self->clipmask) )//MASK_DEADSOLID)) - return squadPaths[self->NPC->iSquadPathIndex].closestWaypoints[i]; - } - - return -1; -} - -/* -------------------------- -NPC_UpdatePathSegment - - NOTE: any bad side effects of not calling this every formation movement frame? -------------------------- -*/ - -void NPC_UpdatePathSegment( gentity_t *self ) -{ - squadPath_t *squadPath = &squadPaths[self->NPC->iSquadPathIndex]; - int wp1, wp2, nextWp, firstPoint = -1, firstPointIndex = -1; - vec3_t point, vec, pathPoint, mins, maxs; - float newSegClearPath; - float newSegPathDist; - float newSegDist; - float newSegCost, oldSegCost; - float newSegActualCost, oldSegActualCost; - float wp1Dist, wp2Dist; - trace_t trace; - int numsegschecked = 0; - qboolean foundOne = qfalse; - - VectorCopy( self->currentOrigin, point ); - - //=== TESTING === - if ( self->NPC->sPCurSegPoint1 != -1 && self->NPC->sPCurSegPoint2 != -1 ) - {//test our last first, see if we've gotten farther away, if not, keep that - qboolean forceFullCheck = qfalse; - - //First, see if we got to the end of our current segment, if so, need to move on - if ( self->NPC->sPCurSegPoint1 == self->NPC->curSegNextWp ) - { - firstPoint = self->NPC->sPCurSegPoint1; - } - else if ( self->NPC->sPCurSegPoint2 == self->NPC->curSegNextWp ) - { - firstPoint = self->NPC->sPCurSegPoint2; - } - - if ( firstPoint != -1 ) - { - VectorSubtract( squadPath->waypoints[firstPoint].origin, point, vec ); - newSegDist = VectorLengthSquared(vec); - - if ( newSegDist <= self->NPC->sPLastSegDist || (fabs(vec[0])+fabs(vec[1]))/2 <= (self->maxs[0]*self->maxs[1])/2 ) - {//at the end of the segment, must do full check, but we know one of the points - forceFullCheck = qtrue; - } - } - - if ( !forceFullCheck ) - { - G_FindClosestPointOnLineSegment( squadPath->waypoints[self->NPC->sPCurSegPoint1].origin, squadPath->waypoints[self->NPC->sPCurSegPoint2].origin, point, pathPoint ); - - VectorSubtract(pathPoint, point, vec); - newSegDist = VectorLengthSquared(vec); - - /*if ( gi.inPVSIgnorePortals( point, pathPoint ) ) - {//make sure we're in PVS - form_updateseg_traces++; - //see if we have a clear path to it - gi.trace( &trace, point, mins, maxs, pathPoint, self->s.number, CONTENTS_SOLID|CONTENTS_MONSTERCLIP ); - newSegClearPath = trace.fraction; - - //Calculate final overall cost - newSegDist *= (1.1f - newSegClearPath);*/ - - if ( newSegDist <= self->NPC->sPLastSegDist || (fabs(vec[0])+fabs(vec[1]))/2 <= (self->maxs[0]*self->maxs[1])/2 ) - {//haven't gotten any farther from the segment we were heading to or we're physically on our segment - self->NPC->sPLastSegDist = newSegDist; - return; - } - //} - } - } - //=============== - - VectorCopy( self->mins, mins ); - VectorCopy( self->maxs, maxs ); - mins[2] += STEPSIZE; - - firstPointIndex = NPC_BuildSquadPointDistances( self, point, &squadPaths[self->NPC->iSquadPathIndex], firstPoint ); - - //Which squadPathPoint, if any, are we in? - NPC_UpdateLastSquadPoint( NPC ); - - self->NPC->sPCurSegPoint1 = self->NPC->sPCurSegPoint2 = -1; - - oldSegCost = oldSegActualCost = Q3_INFINITE; - - //Go through all waypoint pairs, starting with 2 closest and see if you can find any 2 that are neighbors - for( wp1 = (firstPointIndex == -1) ? 0 : firstPointIndex; wp1 < squadPath->numWaypoints && wp1 != -1; wp1 = (firstPointIndex == -1) ? (wp1+1) : -1 ) - { - for( wp2 = 0; wp2 < squadPath->numWaypoints; wp2++ ) - { - if(wp2 == wp1) - continue; - - //NOTE: using this array (assuming it's saved and loaded) would - // eliminate some of this looping... or just make a list - // of neighbors and iterate through that? Must go from - // closest out, though - //if ( branchFound[wp1][wp2] || branchFound[wp2][wp1] ) - - for(nextWp = 0; nextWp < MAX_PATH_BRANCHES; nextWp++) - { - if((squadPath->waypoints[squadPath->closestWaypoints[wp1]].nextWp[nextWp] == squadPath->closestWaypoints[wp2])|| - (squadPath->waypoints[squadPath->closestWaypoints[wp2]].nextWp[nextWp] == squadPath->closestWaypoints[wp1])) - {//They are neighbors! Woo! - newSegClearPath = 0.0f; - newSegPathDist = Q3_INFINITE; - newSegDist = Q3_INFINITE; - newSegCost = newSegActualCost = Q3_INFINITE; - wp1Dist = wp2Dist = Q3_INFINITE; - - numsegschecked++; - //Criteria #1 : Segment has a route to dest - note there should ALWAYS be a route - if(self->NPC->destSegLastWp != -1) - {//goal here is to find the path segment closest to our dest segment (by leader) - if(squadPath->closestWaypoints[wp1] == self->NPC->destSegLastWp) - { - wp1Dist = 0; - } - else if(squadRoutes[self->NPC->iSquadRouteIndex].nextSquadPoint[squadPath->closestWaypoints[wp1]][self->NPC->destSegLastWp] != -1) - {//find dist from this seg to our dest seg - wp1Dist = squadRoutes[self->NPC->iSquadRouteIndex].cost[squadPath->closestWaypoints[wp1]][self->NPC->destSegLastWp]; - } - else - { - wp1Dist = Q3_INFINITE; - } - - if(squadPath->closestWaypoints[wp2] == self->NPC->destSegLastWp) - { - wp2Dist = 0; - } - else if(squadRoutes[self->NPC->iSquadRouteIndex].nextSquadPoint[squadPath->closestWaypoints[wp2]][self->NPC->destSegLastWp] != -1) - { - wp2Dist = squadRoutes[self->NPC->iSquadRouteIndex].cost[squadPath->closestWaypoints[wp2]][self->NPC->destSegLastWp]; - } - else - { - wp2Dist = Q3_INFINITE; - } - if(wp2Dist < wp1Dist) - { - newSegPathDist = wp2Dist; - } - else - { - newSegPathDist = wp2Dist; - } - } - else - { - newSegPathDist = Q3_INFINITE; - } - - if ( newSegPathDist < Q3_INFINITE ) - { - //Criteria #2 : Segment's distance from us - G_FindClosestPointOnLineSegment( squadPath->waypoints[squadPath->closestWaypoints[wp1]].origin, squadPath->waypoints[squadPath->closestWaypoints[wp2]].origin, point, pathPoint ); - - VectorSubtract(pathPoint, point, vec); - newSegDist = VectorLengthSquared(vec); - - //Calculate cost - newSegCost = (newSegPathDist + newSegDist);// * (1.1f - newSegClearPath); - - //Compare value - what if they're equal...? - if ( newSegCost < oldSegCost ) - {//This one is better - //Criteria #3 : Can get to the segment - if ( gi.inPVSIgnorePortals( point, pathPoint ) )//FIXME: IgnorePortals? - { - form_updateseg_traces++; - //gi.trace( &trace, point, NULL, NULL, pathPoint, self->s.number, CONTENTS_SOLID|CONTENTS_MONSTERCLIP ); - //NOTE: A point trace would be faster here, but to be sure, - // this really should be a full size trace, otherwise - // we can possibly pick a closer segment we can see but - // not get to! Plus, we don't even check for ledges - // here... - gi.trace( &trace, point, mins, maxs, pathPoint, self->s.number, CONTENTS_SOLID|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP ); - newSegClearPath = trace.fraction; - - //Calculate final overall cost - newSegActualCost = newSegCost * (1.1f - newSegClearPath); - - if ( newSegActualCost < oldSegActualCost ) - { - foundOne = qtrue; - oldSegCost = newSegCost; - oldSegActualCost = newSegActualCost; - self->NPC->sPCurSegPoint1 = squadPath->closestWaypoints[wp1]; - self->NPC->sPCurSegPoint2 = squadPath->closestWaypoints[wp2]; - self->NPC->sPLastSegDist = newSegDist;// * (1.1f - newSegClearPath); - } - } - } - } - - if ( numsegschecked > 16 && foundOne ) - {//Only check the 16 nearest segments before settling for our best so far - return; - } - //Don't need to look at rest of neighbors - nextWp = MAX_PATH_BRANCHES; - } - } - } - } -} - -/* -qboolean NPC_FindClosestGoalPathPointToEnt(gentity_t *self, gentity_t *targEnt, vec3_t goalOrg, qboolean inclusive, qboolean clearPath) - - Finds 2 closest adjacent squadpoints to the targEnt -*/ - -qboolean NPC_SetSegDest( gentity_t *self, float bestSegDist, int closest1, int closest2 ) -{ - self->NPC->goalDistToPathSeg = bestSegDist; - self->NPC->sPDestSegPoint1 = closest1; - self->NPC->sPDestSegPoint2 = closest2; - return qtrue; -} - -qboolean NPC_FindClosestGoalPathPointToEnt(gentity_t *self, gentity_t *targEnt, vec3_t goalOrg, float maxDist, qboolean inclusive, qboolean clearPath) -{ - squadPath_t *squadPath = &squadPaths[self->NPC->iSquadPathIndex]; - int wp1, wp2, nextWp; - int closest1 = -1; - int closest2 = -1; - int numTraces = 0; - int maxTraces = 8; - float segDist; - float bestSegDist = 128; - vec3_t point, vec; - - //=== TESTING === - //test our last first, see if leader has gotten farther away, if not, keep current dest seg - if ( self->NPC->sPDestSegPoint1 != -1 && self->NPC->sPDestSegPoint2 != -1 ) - { - G_FindClosestPointOnLineSegment( squadPath->waypoints[self->NPC->sPDestSegPoint1].origin, squadPath->waypoints[self->NPC->sPDestSegPoint2].origin, targEnt->currentOrigin, goalOrg ); - - VectorSubtract( goalOrg, targEnt->currentOrigin, vec ); - segDist = VectorLengthSquared(vec); - - if ( segDist <= self->NPC->goalDistToPathSeg || (fabs(vec[0])+fabs(vec[1]))/2 <= (targEnt->maxs[0]+targEnt->maxs[1])/2 ) - {//leader hasn't gotten any farther from the segment we were heading to or they're physically on our segment - //FIXME: do we need to check PVS and trace too? - self->NPC->goalDistToPathSeg = segDist; - return qtrue; - //FIXME: If this does fail, don't check segment again below? - } - } - //=============== - - VectorCopy( targEnt->currentOrigin, point ); - - //If in BS_FORMATION, do this once at the beginning of the frame - //so other places can use it without having to rebuild it - NPC_BuildSquadPointDistances( self, point, squadPath, WAYPOINT_NONE ); - - self->NPC->sPDestSegPoint1 = self->NPC->sPDestSegPoint2 = -1; - //Go through all waypoint pairs, starting with 2 closest and see if you can - //find any 2 that are neighbors - - for ( wp1 = 0; wp1 < squadPath->numWaypoints && numTraces < maxTraces; wp1++ ) - { - for ( wp2 = 0; wp2 < squadPath->numWaypoints && numTraces < maxTraces; wp2++ ) - { - if ( wp2 == wp1 ) - { - continue; - } - - for ( nextWp = 0; nextWp < MAX_PATH_BRANCHES; nextWp++ ) - { - if ( (squadPath->waypoints[squadPath->closestWaypoints[wp1]].nextWp[nextWp] == squadPath->closestWaypoints[wp2])|| - (squadPath->waypoints[squadPath->closestWaypoints[wp2]].nextWp[nextWp] == squadPath->closestWaypoints[wp1]) ) - {//They are neighbors! Woo! - //FIXME: sometimes we want just the closest segment... when? - if ( inclusive ) - {//Does the perp intersection from point have to be within the line segment? - //Save these in case we don't find anything else - if ( closest1 == -1 && closest2 == -1 ) - {//Only save the first (best), theoretically - closest1 = squadPath->closestWaypoints[wp1]; - closest2 = squadPath->closestWaypoints[wp2]; - } - - //Here, goalOrg is closest point on that segment to point passed in, - //segment does not necc contains his perp intersection! This is bad - //because we have no idea which of the two segments that come off of - //this point are the ones we want, we may want a totally different - //segment altogether? - //G_FindClosestPointOnLineSegment( squadPath->waypoints[squadPath->closestWaypoints[wp1]].origin, squadPath->waypoints[squadPath->closestWaypoints[wp2]].origin, point, goalOrg ); - - if ( G_FindClosestPointOnLineSegment( squadPath->waypoints[squadPath->closestWaypoints[wp1]].origin, squadPath->waypoints[squadPath->closestWaypoints[wp2]].origin, point, goalOrg ) ) - //found a segment that contains leader's perpendicular intersection - { - VectorSubtract( goalOrg, point, vec ); - segDist = VectorLengthSquared( vec ); - if ( segDist <= maxDist ) - {//It's close enough to the leader to consider (<=128) - if ( clearPath ) - { - //FIXME: without IgnorePortals this may fail if a door closes on a squadPath segment... - if ( gi.inPVSIgnorePortals( point, squadPath->waypoints[squadPath->closestWaypoints[wp1]].origin ) && - gi.inPVSIgnorePortals( point, squadPath->waypoints[squadPath->closestWaypoints[wp2]].origin ) ) - { - //FIXME: could optimize by keeping a list, then just tracing to the closest ones first until we find a clear one? - numTraces++; - form_leaderseg_traces++; - if ( NAV_ClearPathToPoint( targEnt, self->mins, self->maxs, goalOrg, MASK_DEADSOLID ) ) - {//Can the leader get there? - //NOTE: this doesn't necc find the closest, - // just the first! - bestSegDist = segDist; - return NPC_SetSegDest( self, bestSegDist, squadPath->closestWaypoints[wp1], squadPath->closestWaypoints[wp2] ); - } - } - } - else - { - bestSegDist = segDist; - return NPC_SetSegDest( self, bestSegDist, squadPath->closestWaypoints[wp1], squadPath->closestWaypoints[wp2] ); - } - } - } - } - else - { - return NPC_SetSegDest( self, bestSegDist, squadPath->closestWaypoints[wp1], squadPath->closestWaypoints[wp2] ); - } - } - } - } - } - - if ( closest1 != -1 && closest2 != -1 ) - {//Okay, none inclusive, just return the closest, if any - G_FindClosestPointOnLineSegment( squadPath->waypoints[closest1].origin, squadPath->waypoints[closest2].origin, point, goalOrg ); - form_leaderseg_traces++; - if ( NAV_ClearPathToPoint( targEnt, self->mins, self->maxs, goalOrg, MASK_DEADSOLID ) ) - { - //FIXME: should we make sure it's close enough first??? - return NPC_SetSegDest( self, bestSegDist, closest1, closest2 ); - } - } - - return qfalse; -} - -/* -void NPC_FindClosestFormationSpot (gentity_t *self) - -*/ -void NPC_FindsPDestSegPoint(gentity_t *self) -{ - vec3_t goalOrg, bottom; - squadPath_t *squadPath; - trace_t trace; - int wpNum; - gentity_t *targEnt; - float maxDist = MAX_SEGMENT_DIST_FROM_LEADER_SQUARED; - - if ( !self->NPC ) - { - return; - } - - if ( self->NPC->aiFlags&NPCAI_FORM_TELE_NAV ) - {//Head for teleport spot - if ( NPCInfo->touchedByPlayer == self->client->team_leader ) - {//Hey, bumped into our leader, follow him now - VectorClear( NPCInfo->leaderTeleportSpot ); - NPCInfo->aiFlags &= ~NPCAI_FORM_TELE_NAV; - } - else - { - VectorCopy( self->NPC->leaderTeleportSpot, self->NPC->sPDestPos); - return; - } - } - - targEnt = self->client->team_leader; - - if ( self->NPC->aiFlags & NPCAI_OFF_PATH ) - {//Need to get on our path first - //FIXME: see if we can get to our last sPDestPos and go there? - wpNum = NPC_FindClosestSquadPoint( self ); - if ( wpNum != -1 ) - { - self->NPC->sPDestSegPoint1 = self->NPC->sPDestSegPoint2 = wpNum; - VectorCopy( squadPaths[self->NPC->iSquadPathIndex].waypoints[wpNum].origin, self->NPC->sPDestPos ); - return; - } - else - { -#ifdef _DEBUG - //gi.Printf("%s: Hold on, I'm a little lost here...\n", self->script_targetname); -#endif - targEnt = self; - maxDist = 500*500; - } - } - - //FIXME: this is not really a good idea, visibility is not reliable - //and can be cut unnaturally (by area portals, etc.) - we might want - //an NPC to be able to rejoin the squad from outside visibility... - /* - if(targEnt != self && !gi.inPVSIgnorePortals(self->currentOrigin, targEnt->currentOrigin))//FIXME: IgnorePortals bad? - {//Don't know where my leader actually is! What if we were following him? Should be a flag to not follow in for in PVS - //Don't try to get close to him/her - return; - } - */ - squadPath = &squadPaths[self->NPC->iSquadPathIndex]; // note: this isn't actually used past here at the moment, but stuff might get unREM'd... - -/* - if(form_forward_speed <= 0) - {//Leader moving backwards - VectorCopy(self->team_leader->currentOrigin, leaderSpot); - } - else - {//leader moving forwards at least a little - VectorMA(self->team_leader->currentOrigin, form_forward_speed/2, form_dir, leaderSpot); - } -*/ - //if(!NPC_UpdatePathSegmentForEnt(self, targEnt, goalOrg)) - no, no, no - if ( !NPC_FindClosestGoalPathPointToEnt( self, targEnt, goalOrg, maxDist, qtrue, qtrue ) ) - {//Can't find a segment close to leader??? - if ( self->NPC->aiFlags & NPCAI_OFF_PATH ) - { - if ( self->NPC->blockedSpeechDebounceTime < level.time ) - { -#ifdef _DEBUG - //gi.Printf("%s: Help! I'm stuck!!!\n", self->script_targetname); -#endif - self->NPC->blockedSpeechDebounceTime = level.time + 3000;//FIXME: make a define - //FIXME: What do we do now? - //Maybe we should try to use the waypoint network- - // Find goal's closest waypoint, then see if we have a waypoint we can - // get to that has a route to that waypoint, if so, use it. - //Blind bump and turn? Pick a direction toward your goal...? - // Trace that and outward until you find a trace.fraction of 1 - // If don't find a trace fraction of 1, choose longest? - //Or just move toward goal and slide along walls Heretic II style? - } - } - else - {//Will this screw us up when he gets back on it? - VectorCopy( self->currentOrigin, self->NPC->sPDestPos ); - } - //Maybe we should try to use the waypoint network- - // Find goal's closest waypoint, then see if we have a waypoint we can - // get to that has a route to that waypoint, if so, use it? Or just wait? - return; - } - - //Now we have the closest point on our path to our leader - // We now have to determine how far ahead or behind the leader we should be - if ( !(self->NPC->aiFlags & NPCAI_OFF_PATH) ) - {//if we're trying to get on our path, skip this step - NPC_SetRelativeGoalOrgOnPath( self, goalOrg ); - } - else - { - NPCInfo->aiFlags |= NPCAI_STRAIGHT_TO_DESTPOS; - } - //Will set the two wp's of the segment our goalOrg is on - - //Now trace-test this spot and see where exactly (z-wise) we should be heading. - VectorCopy( goalOrg, bottom ); - bottom[2] -= 1024; - //FIXME: should we not choose this spot if it's got someone in it? Or let - // collision avoidance handle this? - form_clearpath_traces++; - gi.trace( &trace, goalOrg, self->mins, self->maxs, bottom, self->s.number, CONTENTS_SOLID|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP ); - - if ( trace.startsolid || trace.allsolid ) - {//Position is in solid, move up - goalOrg[2] += STEPSIZE; - form_clearpath_traces++; - gi.trace( &trace, goalOrg, self->mins, self->maxs, bottom, self->s.number, CONTENTS_SOLID|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP ); - if ( trace.startsolid || trace.allsolid ) - {//Position is in solid, move up again - goalOrg[2] += STEPSIZE; - form_clearpath_traces++; - gi.trace( &trace, goalOrg, self->mins, self->maxs, bottom, self->s.number, CONTENTS_SOLID|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP ); - if ( trace.startsolid || trace.allsolid ) - {//Position is in solid -#ifdef _DEBUG - //gi.Printf("Warning: squadPath %s between %s and %s has in-solid point at %s\n", - // squadPath->ownername, vtos(squadPath->waypoints[self->NPC->sPDestSegPoint1].origin), vtos(squadPath->waypoints[self->NPC->sPDestSegPoint2].origin), vtos(goalOrg)); -#endif - return; - } - } - } - - if ( trace.fraction == 1.0 ) - {//No bottom! -#ifdef _DEBUG - //gi.Printf("Warning: squadPath %s between %s and %s has no floor at %s\n", - // squadPath->ownername, vtos(squadPath->waypoints[self->NPC->sPDestSegPoint1].origin), vtos(squadPath->waypoints[self->NPC->sPDestSegPoint2].origin), vtos(goalOrg)); -#endif - return; - } - - //Ok, we found a point along our path to head to. - - //FIXME: pick the proper squadPoint on the path and figure out which way - // up or down the path you should head. Follow the path(don't head straight for formGoalPos) - - VectorCopy( trace.endpos, self->NPC->sPDestPos ); - //if this fails, should we just try to follow our leader or stay where we are? -} - -/* -void G_UpdateFormationGoals (gentity_t *self) - -//Only do this if we have moved... changed surfaces? - -//loop through all NPCs in formation - -//Step 1: Search for the closest waypoint with a ownername == NPC's script_targetname -//When create formation first time, put the waypoints into a linked list on each NPC? - -//Step 2: Find surface of player, NPC & wp. - -//Step 3: See if there is a valid route between player & wp and NPC & wp. If not, continue... - -//Step 4: Set NPC's formationGoal to the new wp - -//Step 5: Repeat for all NPCs in formation. - -*/ -void G_MaintainFormations (gentity_t *self) -{ - //vec3_t angles; - - //DISABLED FOR NOW - return; - - /* - if(!self) - { - return; - } - - if(self->team_leader != self) - {//ONLY team_leaders call this function - return; - } - - //FIXME: make this just set whether or not NPCs should try to catch up or stop? - - angles[PITCH] = angles[ROLL] = 0; - angles[YAW] = self->client->ps.viewangles[YAW]; - AngleVectors(angles, form_forward, form_right, NULL); - - form_speed = VectorNormalize2( self->client->ps.velocity, form_dir ); - form_forward_speed = DotProduct(form_dir, form_forward) * form_speed; - vectoangles( form_dir, form_angles );*/ -} - -//================================================================================================ - -//OLD: - -void NPC_DropFormation (gentity_t *self) -{ - //remove leader and team_leader from all and self - //unassign all the followers - //unnumber them - //unlink them -} - -void NPC_AppendOntoFormation (gentity_t *self, gentity_t *leader) -{//Append self onto the leader's list - gentity_t *ent = self->client->team_leader; - - while(ent->client->follower) - { - ent = ent->client->follower; - } - ent->client->follower = self; - self->client->team_leader = leader; - //leader->numFollowers++; -} - -void NPC_DeleteFromFormation (gentity_t *self) -{ - gentity_t *ent = self->client->team_leader; - //Remove self from the list - //relink them - while(ent->client->follower) - { - if(ent->client->follower == self) - { - ent->client->follower = self->client->follower; - //self->team_leader->numFollowers--; - return; - } - ent = ent->client->follower; - } -} - -//===Actual NPC BState funcs============================================================ - -void NPC_UpdateLastSquadPoint (gentity_t *self) -{ - vec3_t vec; - int i; - float dist, bestDist; - // Check which point you're closest to, keep track of last and next. - // When your next is your target point, start checking for proximity to - // formationGoalPos- when close to it, start to slow down, when there, stop and look around or do - // whatever your formation idle behavior is. - // When you get to a waypoint, set it as your last and set your next to it's next, if any. - // When you set your next waypoint, see if it has special instructions, like to wait - // for others to go through it first. When it's your turn, go through and flag it. - // While you're waiting, where do you stand? 128 away? Coded into it? Waitscript? - // When get to a branch, wait there until leader is >= 128 from your point AND - // leader's range to one of the next points is less than yours- so if he's - // closer to the right branch than you, go right, etc. Instead of branch, - // could come to a dead end and have to pick from a new one??? - - //See what waypoint, if any, we're in/by - bestDist = MAX_WAYPOINT_REACHED_DIST_SQUARED; - self->NPC->aiFlags &= ~NPCAI_IN_SQUADPOINT; - self->NPC->currentSquadPoint = -1; - - //This presumes that buildsquadpointdistances was called on our origin! - for(i = 0; i < squadPaths[self->NPC->iSquadPathIndex].numWaypoints; i++) - { - VectorSubtract(squadPaths[self->NPC->iSquadPathIndex].waypoints[squadPaths[self->NPC->iSquadPathIndex].closestWaypoints[i]].origin, self->currentOrigin, vec); - dist = VectorLengthSquared(vec); - if(dist < bestDist) - { - bestDist = dist; - self->NPC->currentSquadPoint = self->NPC->lastSquadPoint = squadPaths[self->NPC->iSquadPathIndex].closestWaypoints[i]; - self->NPC->aiFlags |= NPCAI_IN_SQUADPOINT; - self->NPC->aiFlags &= ~NPCAI_OFF_PATH; - } - } - - //FIXME: Avoidance can make him miss this squadpoint, maybe we - // should do this when he finds his 2 closest and use the - // target of the squadpoint behind us on our path? - /* - if ( self->NPC->aiFlags & NPCAI_IN_SQUADPOINT ) - { - if ( squadPaths[self->NPC->iSquadPathIndex].waypoints[self->NPC->lastSquadPoint].target && squadPaths[self->NPC->iSquadPathIndex].waypoints[self->NPC->lastSquadPoint].target[0] ) - {//Fire the target - G_UseTargets2( NPC, NPC, squadPaths[self->NPC->iSquadPathIndex].waypoints[self->NPC->lastSquadPoint].target ); - //For now, does it only once - squadPaths[self->NPC->iSquadPathIndex].waypoints[self->NPC->lastSquadPoint].target = NULL; - } - } - */ -} - -/* -void NPC_SetPathDistToGoalPos( gentity_t *self ) -How far along our path are we from where we want to be? - -Sets my next squadpoint, my last one and my goalPos' next and last -*/ -qboolean NPC_FindPathToGoalPos( gentity_t *self ) -{ - squadPath_t *path = &squadPaths [self->NPC->iSquadPathIndex]; - squadRoute_t *routes = &squadRoutes[self->NPC->iSquadRouteIndex]; - float cost[2][2], distToCurNext, distToDestLast; - float bestCost = Q3_INFINITE; - qboolean foundRoute = qfalse; - qboolean onSameSeg = qfalse; - int i, j, bestCurNext = -1, bestDestLast = -1; - vec3_t vecToCurNext, vecToDestLast; - - self->NPC->curSegLastWp = -1; - self->NPC->curSegNextWp = -1; - self->NPC->destSegLastWp = -1; - self->NPC->destSegNextWp = -1; - - //if we're on the same segment as our destPos, we do something different - if(self->NPC->sPCurSegPoint1 == self->NPC->sPDestSegPoint1) - { - if(self->NPC->sPCurSegPoint2 == self->NPC->sPDestSegPoint2) - { - onSameSeg = qtrue; - } - } - - if(self->NPC->sPCurSegPoint1 == self->NPC->sPDestSegPoint2) - { - if(self->NPC->sPCurSegPoint2 == self->NPC->sPDestSegPoint1) - { - onSameSeg = qtrue; - } - } - - if(onSameSeg) - {//We're on the same segment as our destPos - //head right for it! - self->NPC->aiFlags |= NPCAI_STRAIGHT_TO_DESTPOS; - //How do we set these? Do we need to? - self->NPC->destSegNextWp = self->NPC->curSegLastWp = self->NPC->sPDestSegPoint2; - self->NPC->destSegLastWp = self->NPC->curSegNextWp = self->NPC->sPDestSegPoint1;//one we want to head to - - //dist is our dist to our destPos - VectorSubtract(self->NPC->sPDestPos, self->currentOrigin, vecToCurNext); - self->NPC->sPDestPosPathDist = VectorLength(vecToCurNext); - return qtrue; - } - - //Ok, we're on different segs, could be adjacent, so let's see - if(self->NPC->sPCurSegPoint1 == self->NPC->sPDestSegPoint1) - {//One of our segment points is the same as thiers - cost[0][0] = 0; - foundRoute = qtrue; - } - else if(routes->nextSquadPoint[self->NPC->sPCurSegPoint1][self->NPC->sPDestSegPoint1] != -1) - { - cost[0][0] = routes->cost[self->NPC->sPCurSegPoint1][self->NPC->sPDestSegPoint1]; - foundRoute = qtrue; - } - else - { - cost[0][0] = Q3_INFINITE; - } - - if(self->NPC->sPCurSegPoint1 == self->NPC->sPDestSegPoint2) - {//One of our segment points is the same as thiers - cost[0][1] = 0; - foundRoute = qtrue; - } - else if(routes->nextSquadPoint[self->NPC->sPCurSegPoint1][self->NPC->sPDestSegPoint2] != -1) - { - cost[0][1] = routes->cost[self->NPC->sPCurSegPoint1][self->NPC->sPDestSegPoint2]; - foundRoute = qtrue; - } - else - { - cost[0][1] = Q3_INFINITE; - } - - if(self->NPC->sPCurSegPoint2 == self->NPC->sPDestSegPoint1) - {//One of our segment points is the same as thiers - cost[1][0] = 0; - foundRoute = qtrue; - } - else if(routes->nextSquadPoint[self->NPC->sPCurSegPoint2][self->NPC->sPDestSegPoint1] != -1) - { - cost[1][0] = routes->cost[self->NPC->sPCurSegPoint2][self->NPC->sPDestSegPoint1]; - foundRoute = qtrue; - } - else - { - cost[1][0] = Q3_INFINITE; - } - - if(self->NPC->sPCurSegPoint2 == self->NPC->sPDestSegPoint2) - {//One of our segment points is the same as thiers - cost[1][1] = 0; - foundRoute = qtrue; - } - else if(routes->nextSquadPoint[self->NPC->sPCurSegPoint2][self->NPC->sPDestSegPoint2] != -1) - { - cost[1][1] = routes->cost[self->NPC->sPCurSegPoint2][self->NPC->sPDestSegPoint2]; - foundRoute = qtrue; - } - else - { - cost[1][1] = Q3_INFINITE; - } - - if(!foundRoute) - {//No route?!! Impossible! - return qfalse; - } - - for(i = 0; i < 2; i++) - { - for(j = 0; j < 2; j++) - { - if(cost[i][j] < bestCost) - { - bestCost = cost[i][j]; - bestCurNext = i; - bestDestLast = j; - } - } - } - - if(bestCurNext == 0) - { - self->NPC->curSegNextWp = self->NPC->sPCurSegPoint1; - self->NPC->curSegLastWp = self->NPC->sPCurSegPoint2; - } - else//bestCurNext = 1 - { - self->NPC->curSegNextWp = self->NPC->sPCurSegPoint2; - self->NPC->curSegLastWp = self->NPC->sPCurSegPoint1; - } - - if(bestDestLast == 0) - { - self->NPC->destSegLastWp = self->NPC->sPDestSegPoint1; - self->NPC->destSegNextWp = self->NPC->sPDestSegPoint2; - } - else//bestDestLast = 1 - { - self->NPC->destSegLastWp = self->NPC->sPDestSegPoint2; - self->NPC->destSegNextWp = self->NPC->sPDestSegPoint1; - } - - VectorSubtract(path->waypoints[self->NPC->curSegNextWp].origin, self->currentOrigin, vecToCurNext); - distToCurNext = VectorLength(vecToCurNext); - - VectorSubtract(path->waypoints[self->NPC->destSegLastWp].origin, self->NPC->sPDestPos, vecToDestLast); - distToDestLast = VectorLength(vecToDestLast); - - self->NPC->sPDestPosPathDist = distToCurNext + bestCost + distToDestLast; - - //It's as easy as that! - return qtrue; -} - -/* -------------------------- -NPC_FindLocalEnemies -------------------------- -*/ - -qboolean NPC_FindLocalEnemies( gentity_t *self, vec3_t lookVec ) -{//FIXME: calc this and store as a lookTarg and redo it only if no lookTarg? - gentity_t *newenemy = NULL; - int entNum; - vec3_t diff, eyes, enemySpot, bestSpot; - float relDist; - float bestDist = Q3_INFINITE; - - VectorClear( bestSpot ); - CalcEntitySpot( self, SPOT_HEAD_LEAN, eyes ); - for ( entNum = 0; entNum < globals.num_entities; entNum++ ) - { - newenemy = &g_entities[entNum]; - - if ( !gi.inPVS( newenemy->currentOrigin, NPC->currentOrigin ) )//FIXME: IgnorePortals bad? - { - continue; - } - - if ( newenemy->client && !(newenemy->flags & FL_NOTARGET) ) - { - if ( newenemy->client->playerTeam == self->client->enemyTeam ) - {//FIXME: check for range and FOV or vis? - VectorSubtract( NPC->currentOrigin, newenemy->currentOrigin, diff ); - relDist = VectorLengthSquared( diff ); - if ( relDist < bestDist ) - { - if( NPC_CheckVisibility ( newenemy, CHECK_VISRANGE ) >= VIS_360 )//CHECK_360| - { - if(newenemy->health > 0) - { - CalcEntitySpot( newenemy, SPOT_HEAD, enemySpot ); - } - else - {//SPOT_GROUND? - VectorCopy( newenemy->currentOrigin, enemySpot ); - } - - if( G_ClearLineOfSight( eyes, enemySpot, self->s.number, MASK_OPAQUE ) ) - { - bestDist = relDist; - VectorCopy( enemySpot, bestSpot ); - } - } - } - } - } - } - - if(VectorLengthSquared(bestSpot)) - { - VectorSubtract(bestSpot, eyes, lookVec); - return qtrue; - } - - return qfalse; -} - - -/* -------------------------- -NPC_BSFormation -------------------------- -*/ - -void NPC_BSFormation(void) -{ - vec3_t weapspot, enemyorg, aimdir, desiredAngles, diff, dir; - qboolean faced = qfalse; - - //See if we need to move, first and foremost - vec3_t collisionDir; - - if ( NPC_BlockingPlayer( collisionDir ) ) - { - NPC_Unobstruct( collisionDir ); - //Com_Printf("Blocking player\n"); - return; - } - - NPCInfo->aiFlags &= ~NPCAI_STRAIGHT_TO_DESTPOS; - ucmd.buttons |= BUTTON_CAREFUL;//always use careful walk, stand, run, etc. - - if ( !NPC->enemy ) - { - gentity_t *newenemy; - newenemy = NPC_PickEnemy(NPC, NPC->client->enemyTeam, qtrue, qfalse, qtrue); - if(newenemy) - {//Just acquired one - G_SetEnemy( NPC, newenemy ); - } - else - { - NPC_SquadIdle(); - } - } - else - { - NPC_CheckEnemy( qfalse, qfalse ); - } - - if ( NPC->enemy ) - { - /* - if(NPC_BSPointCombat()) - return; - */ - - //Just stand still and fire at targets - //NPC_BSStandAndShoot();//NOTE: It is not valid to call this from here, it breaks stuff - - //if(NPC->enemy) - {//Old style behavior - //Keeping in formation, but fighting - //FIXME: should we check for VIS_PVS and VIS_360 before looking at enemy? - CalcEntitySpot(NPC, SPOT_WEAPON, weapspot); - CalcEntitySpot(NPC->enemy, SPOT_HEAD, enemyorg); - NPC_AimWiggle( enemyorg ); - - //FIXME - SPOT_ORIGIN looks down when they're close! - VectorSubtract(enemyorg, weapspot, aimdir); - VectorNormalize(aimdir); - vectoangles(aimdir, desiredAngles); - NPCInfo->desiredYaw = desiredAngles[YAW]; - NPCInfo->desiredPitch = desiredAngles[PITCH]; - - faced = qtrue; - NPC_UpdateAngles(qtrue, qtrue); - //FIXME: these guys shoot each other! - //Need to check for a clear shot... - //Maybe tell the person in the way to duck or sidestep? - //If in combat, should probably script to run to actual hiding points, etc. - if ( NPC_CheckAttack( 1.0 ) ) - { - form_shot_traces++; - enemyVisibility = NPC_CheckVisibility ( NPC->enemy, CHECK_FOV|CHECK_SHOOT|CHECK_VISRANGE );//CHECK_PVS|CHECK_360| - if(enemyVisibility == VIS_SHOOT) - { - WeaponThink(qtrue); - } - } - - NPC->aimDebounceTime = 0; - } - } - else if ( NPCInfo->combatPoint != -1 ) - {//FIXME: make a func call to clear your point - level.combatPoints[NPCInfo->combatPoint].occupied = qfalse; - NPCInfo->combatPoint = -1; - } - - if ( NPCInfo->iSquadPathIndex != -1 ) - {//We have a path - vec3_t angles, forward, right, pathAngles, goalPos, vec; - float fDot, rDot; - int move = 127; - qboolean lost = qfalse; - qboolean reachedDest = qfalse; - qboolean blocked = qfalse; - int goalWp = -1; - qboolean dontCatchUp = qfalse; - qboolean checkedClearPath = qfalse; - //FIXME: less likely to update further we are from player? - //FIXME: still start/stop too suddenly and in synch with player, - // make them keep walkinga few steps after the player stops and - // not start right away when player moves. Also, make them move - // slower more, does that mean slow the player down? Or just better - // anims? Need better transitioning anims. Also, altering fps of - // your anim a little may help too. - //FIXME: only do this if player has moved more than 32 from the last spot we checked him at (when we got to our dest) - - NPCInfo->distToGoal = 0; - - if(VectorLengthSquared(NPC->client->ps.velocity) < 1.0f && - !(NPCInfo->aiFlags & NPCAI_OFF_PATH) && !(NPCInfo->aiFlags & NPCAI_FORM_TELE_NAV)) - {//We're not really moving and we're not trying to get on to our path - VectorSubtract(NPC->client->team_leader->currentOrigin, NPC->currentOrigin, vec); - if(VectorLengthSquared(vec) > 10000)//100 squared - {//We're more than 100 away from leader - VectorSubtract(NPC->client->team_leader->currentOrigin, NPCInfo->lastLeaderPoint, vec); - if(VectorLengthSquared(vec) < 10000)//100 squared - {//Leader has moved less than 100 since last we caught up - dontCatchUp = qtrue; - } - } - } - - if(dontCatchUp) - { - reachedDest = qtrue; - } - else - { - /*int old1, old2, new1, new2; - if(NPCInfo->sPCurSegPoint1 < NPCInfo->sPCurSegPoint2) - { - old1 = NPCInfo->sPCurSegPoint1; - old2 = NPCInfo->sPCurSegPoint2; - } - else - { - old2 = NPCInfo->sPCurSegPoint1; - old1 = NPCInfo->sPCurSegPoint2; - }*/ - NPC_UpdatePathSegment( NPC );//Where on our path are we? - /*if(NPCInfo->sPCurSegPoint1 < NPCInfo->sPCurSegPoint2) - { - new1 = NPCInfo->sPCurSegPoint1; - new2 = NPCInfo->sPCurSegPoint2; - } - else - { - new2 = NPCInfo->sPCurSegPoint1; - new1 = NPCInfo->sPCurSegPoint2; - } - - if(old1!=new1 && old2 != new2) - { - gi.Printf("%s NewSeg: %d : %d\n", NPC->script_targetname, NPCInfo->sPCurSegPoint1, NPCInfo->sPCurSegPoint2); - }*/ - - //FIXME: On Stasis levels, chug occurs when the leader starts moving around... - // Need to figure out what exactly is so expensive... - // Is it "ClearPathTo" traces? - // Is it collision avoidance? - // Is it enemy visibility/check shoot traces? - // Is it inPVS checks? - // Is it math overhead from processing the path and picking our destPos? - // Or is it just movement physics? - // Whatever it is, it accentuates the fact that patch traces are much, - // much more expensive than regular traces. - NPC_FindsPDestSegPoint( NPC );//Where on our path do we want to be - - if ( NPCInfo->aiFlags & NPCAI_STRAIGHT_TO_DESTPOS ) - {//We're heading straight for our sPDestPos - VectorSubtract( NPCInfo->sPDestPos, NPC->currentOrigin, diff ); - NPCInfo->sPDestPosPathDist = VectorLength( diff ); - } - else - {//we're trying to use our path to get somewhere - NPC_FindPathToGoalPos( NPC );//How far along our path are we from where we want to be? - if ( NPCInfo->curSegNextWp == -1 ) - {//We don't have a path to our goalPos, go to our closest one - goalWp = NPC_FindClosestSquadPoint( NPC );//NPCInfo->sPDestSegPoint1; - if ( goalWp != -1 ) - { - VectorCopy( squadPaths[NPCInfo->iSquadPathIndex].waypoints[goalWp].origin, NPCInfo->sPDestPos ); - VectorSubtract( NPCInfo->sPDestPos, NPC->currentOrigin, diff ); - NPCInfo->sPDestPosPathDist = VectorLength( diff ); - } - } - else - {//head for our next - goalWp = NPCInfo->curSegNextWp; - } - } - - angles[PITCH] = angles[ROLL] = 0; - angles[YAW] = NPC->client->ps.viewangles[YAW]; - AngleVectors(angles, forward, right, NULL); - - if ( goalWp != -1 || NPCInfo->aiFlags & NPCAI_STRAIGHT_TO_DESTPOS ) - {//We have a next wp or heading straight to our sPDestPos, start moving - int neededDistSquared; - //Are we close to our sPDestPos? - if ( NPCInfo->aiFlags & NPCAI_FORM_TELE_NAV ) - {//We're trying to go through a teleporter, get right up to this point - neededDistSquared = 0; - } - else if ( NPCInfo->aiFlags & NPCAI_STRAIGHT_TO_DESTPOS ) - {//We're trying to reacquire our path, so require us to get much closer to it point - neededDistSquared = 64;//8 squared - } - else - { - neededDistSquared = MAX_WAYPOINT_REACHED_DIST_SQUARED; - } - - if ( NPCInfo->sPDestPosPathDist*NPCInfo->sPDestPosPathDist > neededDistSquared ) - {//Still on our way there - //FIXME: If use our clipmask: when someone is blocking our goalPos, - //this makes us run to the waypoint unnecessarily, so using MASK_DEADSOLID - if ( NPCInfo->aiFlags & NPCAI_STRAIGHT_TO_DESTPOS ) - {//head straight there - //gi.Printf("%s heading straight for dest\n", NPC->script_targetname); - VectorCopy( NPCInfo->sPDestPos, goalPos ); - } - else if ( NPCInfo->currentSquadPoint == goalWp && NPCInfo->destSegLastWp == goalWp ) - {//head straight there - //gi.Printf("%s hit dest lastwp, heading straight for dest\n", NPC->script_targetname); - VectorCopy( NPCInfo->sPDestPos, goalPos ); - } - else - {//using squadPoints to get there - //gi.Printf("%s heading forWp %d\n", NPC->script_targetname, goalWp); - //Head to the goalWp - VectorCopy( squadPaths[NPCInfo->iSquadPathIndex].waypoints[goalWp].origin, goalPos ); - - //Now let's see if we can head straight for our sPDestPos - if ( goalWp == NPCInfo->destSegLastWp || (NPCInfo->destSegLastWp == -1 && goalWp == NPCInfo->curSegNextWp) ) - {//we're en route to our goalWp - if ( gi.inPVS( NPC->currentOrigin, NPCInfo->sPDestPos ) )//FIXME: IgnorePortals? - {//In PVS of our goalPos - form_clearpath_traces++; - if ( NAV_ClearPathToPoint( NPC, NPC->mins, NPC->maxs, NPCInfo->sPDestPos, MASK_DEADSOLID ) ) - {//Nothing blocking us, head right for it - //gi.Printf("%s can get to dest wp, heading straight for it\n", NPC->script_targetname); - VectorCopy( NPCInfo->sPDestPos, goalPos ); - checkedClearPath = qtrue; - } - } - } - } - } - else - {//We're there, stop - reachedDest = qtrue; - //VectorCopy(NPCInfo->formGoalPos, goalPos); - //We're now on our path and ready to roll - NPCInfo->aiFlags &= ~NPCAI_OFF_PATH; - NPCInfo->aiFlags &= ~NPCAI_STRAIGHT_TO_DESTPOS; - //This should make us wait here - VectorCopy( NPC->currentOrigin, goalPos ); - VectorCopy( NPC->client->team_leader->currentOrigin, NPCInfo->lastLeaderPoint ); - } - } - else - {//Can't get there, now what? For now, stop. - //This should make us wait here - VectorCopy( NPC->currentOrigin, goalPos ); - lost = qtrue; - //Ok, we're fucked, time to do a more expensive search... - //Find the closest point to ourself on our path and head there - NPCInfo->aiFlags |= NPCAI_OFF_PATH; - } - - ucmd.forwardmove = 0; - ucmd.rightmove = 0; - NPCInfo->distToGoal = 0; - - //FIXME: if the path will go through a teleporter, don't go through - //until leader does (vectorlength of leaderTeleportSpot is non-zero) - - if ( !reachedDest && !lost ) - {//Moving - if ( NPCInfo->aiFlags & NPCAI_FORM_TELE_NAV ) - {//NOTE: This could be made more generic...? - //Nav to goalPos - qboolean clearPath = qfalse; - - /* - VectorCopy( goalPos, NPCInfo->tempGoal->currentOrigin ); - NPCInfo->goalEntity = NPCInfo->tempGoal; - */ - - NPC_SetMoveGoal( NPC, goalPos, 16, qtrue ); - - if ( !checkedClearPath ) - { - clearPath = NPC_ClearPathToGoal( dir, NPCInfo->goalEntity ); - } - - if ( !clearPath ) - {//need to Nav there - vec3_t dir; - navInfo_t info; - - if ( NAV_MoveToGoal( NPC, info ) == WAYPOINT_NONE ) - { - //Try to follow them for 10 seconds, then see if they're close, - //if not, try for another 10 seconds - if ( NPCInfo->navTime > level.time ) - { - //head straight there anyway - VectorSubtract( goalPos, NPC->currentOrigin, dir ); - } - else - { - vec3_t leaderVec; - - //if leader right here with me, - // forget about teleport thing... - VectorSubtract( NPC->client->team_leader->currentOrigin, NPC->currentOrigin, leaderVec ); - if ( VectorLengthSquared( leaderVec ) < 128*128 ) - {//FIXME: do some more expensive, intelligent check? - //Can't nav there, so... screw it - //Back into normal formation - VectorClear( NPCInfo->leaderTeleportSpot ); - NPCInfo->aiFlags &= ~NPCAI_FORM_TELE_NAV; - //Just stand around this time - VectorClear( dir ); - } - else - {//Tryagain for another 10 seconds - NPCInfo->navTime = level.time + 10000; - } - } - } - } - } - else - { - VectorSubtract( goalPos, NPC->currentOrigin, dir ); - - if ( fabs(dir[0]) <= NPC->maxs[0] && fabs(dir[1]) <= NPC->maxs[1] ) - {//If we're close to it on X & Y, and Z is within reasonable distance, eliminate Z diff - if ( dir[2] < 0 ) - { - if ( dir[2] > NPC->mins[2] ) - { - dir[2] = 0; - } - } - else if ( dir[2] < 64 ) - { - dir[2] = 0; - } - } - } - - if ( ( NPCInfo->distToGoal = VectorNormalize( dir ) ) )//We're actually trying to move - { - navInfo_t info; - - VectorCopy( dir, info.direction ); - VectorCopy( dir, info.pathDirection ); - info.distance = NPCInfo->distToGoal; - - if ( NAV_AvoidCollision( NPC, NPCInfo->goalEntity, info ) == qfalse ) - {//Blocked - //FIXME: if totally blocked, STOP!!!! - if ( NPCInfo->consecutiveBlockedMoves >= 100 ) - {//Blocked for a whole second straight - if ( NPCInfo->blockingEntNum > -1 && NPCInfo->blockingEntNum < ENTITYNUM_NONE ) - { - gentity_t *blocker = &g_entities[NPCInfo->blockingEntNum]; - if ( NPCInfo->blockingEntNum < ENTITYNUM_WORLD || (blocker && !blocker->client) ) - {//We're being blocked by a non-player - //We need to reacquire our path - NPCInfo->aiFlags |= NPCAI_OFF_PATH; - } - } - } - blocked = qtrue; - VectorClear( dir ); - } - } - - dir[2] = 0; - vectoangles( dir, pathAngles ); - if ( !blocked ) - { - if ( NPCInfo->distToGoal == 0 ) - NPCInfo->distToGoal = VectorNormalize( dir ); - - //compare dir to angles to get proper move commands for left and right - fDot = DotProduct( forward, dir ); - rDot = DotProduct( right, dir ); - if ( fabs( fDot ) < 0.01 ) - { - fDot = 0; - } - - if ( fabs( rDot ) < 0.01 ) - { - rDot = 0; - } - //FIXME: should this always be a run? If we're close should we slow down? - // Or should we actually modify our speed field to speed up and slow down? - ucmd.forwardmove = floor( move * fDot ); - ucmd.rightmove = floor( move * rDot ); - } - } - } - - if ( !lost ) - { - //FIXME: base speed on speed of leader? - - //SPEED - - //Now calculate the desired speed - if ( blocked ) - {//Sudden stop - NPCInfo->desiredSpeed = NPCInfo->currentSpeed = 0; - NPCInfo->aiFlags &= ~NPCAI_STRAIGHT_TO_DESTPOS; - } - else - { - if ( reachedDest ) - {//we're there, full stop (don't overshoot) - NPCInfo->desiredSpeed = 0; - NPCInfo->currentSpeed = 0; - } - else if ( NPCInfo->sPDestPosPathDist > 200 && NPCInfo->distToGoal > 64 ) - {//We're far from our destPos - NPCInfo->desiredSpeed = NPCInfo->stats.runSpeed + 10; - } - else if ( NPCInfo->sPDestPosPathDist > 64 ) - {//We're closer to our destPos - NPCInfo->desiredSpeed = NPCInfo->stats.walkSpeed - 10; - } - else - {//We want to slow to a stop - if ( NPCInfo->sPDestPosPathDist > 32 ) - {//We're still a bit from our dest keep moving - NPCInfo->desiredSpeed = 64; - } - else if ( NPCInfo->currentSpeed <= 0 ) - {//we're close but not there yet, what are we stopped for? Get moving! - NPCInfo->desiredSpeed = 32; - } - /* - else - {//We're still moving but very close, stop! - //Full stop - NPCInfo->aiFlags &= ~NPCAI_OFF_PATH; - NPCInfo->aiFlags &= ~NPCAI_STRAIGHT_TO_DESTPOS; - NPCInfo->desiredSpeed = 0; - NPCInfo->currentSpeed = 0; - ucmd.forwardmove = ucmd.rightmove = 0; - }*/ - } - } - - if ( ucmd.forwardmove == 0 && ucmd.rightmove == 0 && !NPC->enemy ) - {//Waiting around at sPDestPos - if ( NPC->aimDebounceTime < level.time ) - {//FIXME: need to remember last lookMode - vec3_t lookVec; - lookMode_t lookMode = LT_NONE; - - //FIXME: maybe look for dead buddies' bodies first... - if ( NPC_FindLocalEnemies( NPC, lookVec) ) - {//Found an enemy to look at - lookMode = LT_AIMSOFT; - } - else - { - //ANGLES - NPCInfo->idleCounter++; - if ( NPCInfo->idleCounter > Q_irand( 5, 10 ) ) - {//See if there are any interest spots to look at - NPCInfo->idleCounter = 11;//No jitter - lookMode = (lookMode_t)NPC_FindLocalInterestPoint( NPC, lookVec ); - } - } - - if ( lookMode != LT_NONE ) - {//FIXME: based on intensity of lookMode, - //look away (down path) every now and then? - vectoangles( lookVec, pathAngles ); - NPCInfo->lookMode = lookMode; - } - else - {//If not, look in the dir of our path - VectorCopy( NPCInfo->lastPathAngles, pathAngles ); - } - - //face toward front of formation and look around. - //FIXME: randomly play guard idle (occais) and guard_lookaround (rare) - int changelook = Q_irand( 0, 100 ); - int torsoAnim = (NPC->client->ps.torsoAnim&~ANIM_TOGGLEBIT); - - //FIXME: make these transition ONLY when finished with last frame, otherwise they snap - if ( ( changelook < 20 || torsoAnim == BOTH_STAND2TO4 || torsoAnim == BOTH_STAND4 || torsoAnim == BOTH_STAND4TO2 ) && - NPC->client->ps.weapon != WP_NONE && NPC->client->ps.weapon != WP_SABER && - torsoAnim != BOTH_GUARD_LOOKAROUND1 && - torsoAnim != BOTH_GUARD_IDLE1 && - torsoAnim != BOTH_STAND2_RANDOM1 && - torsoAnim != BOTH_STAND2_RANDOM2 && - torsoAnim != BOTH_STAND2_RANDOM3 && - torsoAnim != BOTH_STAND2_RANDOM4 ) - {//Play one of the lookarounds instead - //NPCInfo->lookMode = LT_NONE; - - NPCInfo->desiredYaw = AngleNormalize360(pathAngles[YAW]); - NPCInfo->desiredPitch = AngleNormalize360(pathAngles[PITCH]); - if ( torsoAnim == BOTH_STAND2TO4 ) - { - if ( PM_HasAnimation( NPC, BOTH_STAND4 ) ) - { - NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_STAND4, SETANIM_FLAG_NORMAL ); - NPC->aimDebounceTime = level.time + PM_AnimLength( NPC->client->clientInfo.animFileIndex, BOTH_STAND4 ) - FRAMETIME; - } - } - else if ( torsoAnim == BOTH_STAND4TO2 ) - { - if ( PM_HasAnimation( NPC, BOTH_STAND2 ) ) - { - NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_STAND2, SETANIM_FLAG_NORMAL ); - NPC->aimDebounceTime = level.time + PM_AnimLength( NPC->client->clientInfo.animFileIndex, BOTH_STAND2 ); - } - } - else if ( torsoAnim == BOTH_STAND4 ) - { - if ( PM_HasAnimation( NPC, BOTH_STAND4TO2 ) ) - { - NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_STAND4TO2, SETANIM_FLAG_NORMAL ); - NPC->aimDebounceTime = level.time + PM_AnimLength( NPC->client->clientInfo.animFileIndex, BOTH_STAND4TO2 ) - FRAMETIME; - } - } - else if ( changelook < 4 && (torsoAnim != BOTH_GUARD_LOOKAROUND1) ) - {//guard look around - NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_GUARD_LOOKAROUND1, SETANIM_FLAG_NORMAL ); - if ( (NPC->client->ps.torsoAnim&~ANIM_TOGGLEBIT) == BOTH_GUARD_LOOKAROUND1 ) - { - NPC->aimDebounceTime = level.time + PM_AnimLength( NPC->client->clientInfo.animFileIndex, BOTH_GUARD_LOOKAROUND1 ); - } - } - else if ( changelook < 10 && torsoAnim != BOTH_GUARD_IDLE1 ) - {//guard idle - NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_GUARD_IDLE1, SETANIM_FLAG_NORMAL ); - if ( (NPC->client->ps.torsoAnim&~ANIM_TOGGLEBIT) == BOTH_GUARD_IDLE1 ) - { - NPC->aimDebounceTime = level.time + PM_AnimLength( NPC->client->clientInfo.animFileIndex, BOTH_GUARD_IDLE1 ); - } - } - else if ( changelook < 18 ) - {//random stand2 anim - int standAnim = PM_PickAnim( NPC, BOTH_STAND2_RANDOM1, BOTH_STAND2_RANDOM4 ); - if ( standAnim != -1 ) - { - NPC_SetAnim( NPC, SETANIM_BOTH, standAnim, SETANIM_FLAG_NORMAL ); - NPC->aimDebounceTime = level.time + PM_AnimLength( NPC->client->clientInfo.animFileIndex, (animNumber_t)standAnim ); - }//else??? - } - else - {//transition to stand4 - if ( PM_HasAnimation( NPC, BOTH_STAND2TO4 ) ) - { - NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_STAND2TO4, SETANIM_FLAG_NORMAL ); - NPC->aimDebounceTime = level.time + PM_AnimLength( NPC->client->clientInfo.animFileIndex, BOTH_STAND2TO4 ) - FRAMETIME; - } - } - - } - else - {//FIXME: do this also when walking around? - //FIXME: only glance - NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_STAND2, SETANIM_FLAG_NORMAL ); - if ( changelook > 90 ) - {//FIXME: this turning should be torso only... - NPCInfo->desiredYaw = AngleNormalize360( pathAngles[YAW] + Q_irand( 10, 30 ) ); - NPC->aimDebounceTime = level.time + Q_irand( 500, 2000 ); - } - else if ( changelook > 80 ) - { - NPCInfo->desiredYaw = AngleNormalize360( pathAngles[YAW] - Q_irand( 10, 30 ) ); - NPC->aimDebounceTime = level.time + Q_irand( 500, 2000 ); - } - else if ( changelook > 40 ) - { - NPCInfo->desiredYaw = AngleNormalize360( pathAngles[YAW] ); - NPC->aimDebounceTime = level.time + Q_irand( 500, 2000 ); - } - - if ( lookMode != LT_NONE ) - { - changelook = Q_irand( 0, 100 ); - if ( changelook > 90 ) - {//FIXME: this turning should be torso only... - NPCInfo->desiredPitch = AngleNormalize360( pathAngles[PITCH] + Q_irand( 1, 20 ) ); - NPC->aimDebounceTime = level.time + Q_irand( 500, 2000 ); - } - else if ( changelook > 80 ) - { - NPCInfo->desiredPitch = AngleNormalize360( pathAngles[PITCH] - Q_irand( 1, 20 ) ); - NPC->aimDebounceTime = level.time + Q_irand( 500, 2000 ); - } - else if ( changelook > 40 ) - { - NPCInfo->desiredPitch = AngleNormalize360( pathAngles[PITCH] ); - NPC->aimDebounceTime = level.time + Q_irand( 500, 2000 ); - } - } - } - } - } - else - { - //ANGLES - VectorCopy( pathAngles, NPCInfo->lastPathAngles ); - - NPCInfo->idleCounter = 0; - if ( ucmd.forwardmove || ucmd.rightmove ) - {//When moving, always face forward - NPCInfo->lookMode = LT_NONE; - } - else if ( NPC->enemy ) - {//Angles set at beginning of func, turn off lookmode - NPCInfo->lookMode = LT_AIM; - } - - if ( !NPC->enemy ) - {//Moving toward goal without an enemy - if ( NPC->aimDebounceTime > level.time ) - { - NPCInfo->desiredYaw = pathAngles[YAW]; - NPCInfo->desiredPitch = 0; - } - else - {//Play one of the idles - int changelook = Q_irand(0, 100); - - //FIXME: make these transition ONLY when finished with last frame, otherwise they snap - if ( changelook < 10 && - ((NPC->client->ps.torsoAnim&~ANIM_TOGGLEBIT) != BOTH_GUARD_IDLE1) ) - {//Play one of the lookarounds instead - //NPCInfo->lookMode = LT_NONE; - - NPCInfo->desiredYaw = AngleNormalize360( pathAngles[YAW] ); - NPCInfo->desiredPitch = AngleNormalize360( pathAngles[PITCH] ); - NPC_SetAnim( NPC, SETANIM_TORSO, BOTH_GUARD_IDLE1, SETANIM_FLAG_NORMAL ); - if ( (NPC->client->ps.torsoAnim&~ANIM_TOGGLEBIT) == BOTH_GUARD_IDLE1 ) - { - NPC->aimDebounceTime = level.time + PM_AnimLength( NPC->client->clientInfo.animFileIndex, BOTH_GUARD_IDLE1 ); - } - }//else the normal upper should take over... - } - } - } - } - //Should we make them strafe more, such as looking around a corner? - // Maybe we should always face them at the NEXT point in the path - // AFTER the one they're heading to. - } - else - {//Stand here or just follow my leader at a distance? - } - - if ( !faced ) - { - NPC_UpdateAngles( qtrue, qtrue ); - } - //FIXME: non-combat squadmates shouldn't do this... - /* - if(ucmd.forwardmove > 0 || ucmd.rightmove > 0) - { - //Moving anims - if(!(ucmd.buttons & BUTTON_ATTACK)) - {//not shooting - if(ucmd.forwardmove <= 64 || (ucmd.buttons & BUTTON_WALKING) || (ucmd.upmove < 0) || NPCInfo->distToGoal < 100) - {//walking or crouching or close to goal - NPC_SetAnim(NPC,SETANIM_TORSO,TORSO_WEAPONIDLE1,SETANIM_FLAG_NORMAL); - } - } - } - else - {//standing around - if(NPC->s.weapon == WP_SABER) - {//one handed weapon - NPC_SetAnim(NPC,SETANIM_TORSO,TORSO_WEAPONREADY1,SETANIM_FLAG_NORMAL); - } - else - {//two handed weapon - NPC_SetAnim(NPC,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); - } - - NPC_SetAnim(NPC,SETANIM_LEGS,BOTH_ATTACK2,SETANIM_FLAG_NORMAL); - - } - */ -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/* -------------------------- -NPC_FindLocalInterestPoint -------------------------- -*/ - -lookMode_t NPC_FindLocalInterestPoint (gentity_t *self, vec3_t lookVec) -{ - int i, bestPoint = -1; - float dist, bestDist = Q3_INFINITE; - vec3_t diffVec, eyes; - - CalcEntitySpot(self, SPOT_HEAD_LEAN, eyes); - for(i = 0; i < level.numInterestPoints; i++) - { - //Don't ignore portals? If through a portal, need to look at portal! - if( gi.inPVS(level.interestPoints[i].origin, eyes) ) - { - VectorSubtract(level.interestPoints[i].origin, eyes, diffVec); - if((fabs(diffVec[0]) + fabs(diffVec[1])) / 2 < 48 && - fabs(diffVec[2]) > (fabs(diffVec[0]) + fabs(diffVec[1])) / 2 ) - {//Too close to look so far up or down - continue; - } - dist = VectorLengthSquared(diffVec); - //Some priority to more interesting points - dist -= ((int)level.interestPoints[i].lookMode * 5) * ((int)level.interestPoints[i].lookMode * 5); - if( dist < MAX_INTEREST_DIST && dist < bestDist ) - { - if(G_ClearLineOfSight(eyes, level.interestPoints[i].origin, self->s.number, MASK_OPAQUE)) - { - bestDist = dist; - bestPoint = i; - } - } - } - } - - if(bestPoint != -1) - { - VectorSubtract(level.interestPoints[bestPoint].origin, eyes, lookVec); - return level.interestPoints[bestPoint].lookMode; - } - - return LT_NONE; -} - -/*QUAKED target_interest (1 0.8 0.5) (-4 -4 -4) (4 4 4) -A point that a squadmate will look at if standing still - -target - thing to fire when someone looks at this thing - -interest: - 0 = just glance at it (default) - 1 = look at it, kind of aim at it, keep feet forward - 2 = look at it, kind of aim at it, turn feet some - 3 = aim fully at it, keep feet forward - 4 = aim fully at it, turn feet some - 5 = fully face it with whole body -*/ - -void SP_target_interest( gentity_t *self ) -{//FIXME: rename point_interest - if(level.numInterestPoints >= MAX_INTEREST_POINTS) - { - gi.Printf("ERROR: Too many interest points, limit is %d\n", MAX_INTEREST_POINTS); - G_FreeEntity(self); - return; - } - - VectorCopy(self->currentOrigin, level.interestPoints[level.numInterestPoints].origin); - self->health += 1; - if(self->health > LT_FULLFACE) - { - level.interestPoints[level.numInterestPoints].lookMode = LT_FULLFACE; - } - else if(self->health < LT_GLANCE) - { - level.interestPoints[level.numInterestPoints].lookMode = LT_GLANCE; - } - else - { - level.interestPoints[level.numInterestPoints].lookMode = (lookMode_t)self->health; - } - - if(self->target && self->target[0]) - { - level.interestPoints[level.numInterestPoints].target = G_NewString( self->target ); - } - - level.numInterestPoints++; - - G_FreeEntity(self); -} diff --git a/code/game/NPC_move.cpp b/code/game/NPC_move.cpp index 028fb4f..5d42341 100644 --- a/code/game/NPC_move.cpp +++ b/code/game/NPC_move.cpp @@ -22,6 +22,7 @@ extern int GetTime ( int lastTime ); navInfo_t frameNavInfo; extern qboolean FlyingCreature( gentity_t *ent ); +extern qboolean PM_InKnockDown( playerState_t *ps ); /* ------------------------- @@ -341,7 +342,7 @@ qboolean NPC_MoveToGoal( qboolean tryStraight ) int startTime = GetTime(0); #endif// AI_TIMERS //If taking full body pain, don't move - if( ( NPC->s.legsAnim >= BOTH_PAIN1 ) && ( NPC->s.legsAnim <= BOTH_PAIN19 ) ) + if ( PM_InKnockDown( &NPC->client->ps ) || ( ( NPC->s.legsAnim >= BOTH_PAIN1 ) && ( NPC->s.legsAnim <= BOTH_PAIN19 ) ) ) { return qtrue; } diff --git a/code/game/NPC_reactions.cpp b/code/game/NPC_reactions.cpp index b41cd46..e6d53a9 100644 --- a/code/game/NPC_reactions.cpp +++ b/code/game/NPC_reactions.cpp @@ -39,6 +39,7 @@ extern qboolean PM_FlippingAnim( int anim ); extern qboolean PM_RollingAnim( int anim ); extern qboolean PM_InCartwheel( int anim ); +extern qboolean stop_icarus; /* ------------------------- NPC_CheckAttacker @@ -212,6 +213,10 @@ void NPC_ChoosePainAnimation( gentity_t *self, gentity_t *other, vec3_t point, i pain_chance = (200.0f-self->health)/100.0f + damage/50.0f; } } + else if ( self->client && self->client->playerTeam == TEAM_PLAYER && other && !other->s.number ) + {//ally shot by player always complains + pain_chance = 1.1f; + } else { if ( other && other->s.weapon == WP_SABER || mod == MOD_ELECTROCUTE || mod == MOD_CRUSH/*FIXME:MOD_FORCE_GRIP*/ ) @@ -291,7 +296,7 @@ void NPC_ChoosePainAnimation( gentity_t *self, gentity_t *other, vec3_t point, i { self->painDebounceTime = level.time + 4000; } - self->painDebounceTime = level.time + PM_AnimLength( NPC->client->clientInfo.animFileIndex, (animNumber_t) pain_anim ); + self->painDebounceTime = level.time + PM_AnimLength( self->client->clientInfo.animFileIndex, (animNumber_t) pain_anim ); self->client->fireDelay = 0; } } @@ -332,7 +337,9 @@ void NPC_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, vec3_t p {//hit by a teammate if ( other != self->enemy && self != other->enemy ) {//we weren't already enemies - if ( self->enemy || other->enemy || (other->s.number&&other->s.number!=player->client->ps.viewEntity) || (!other->s.number&&Q_irand( 0, 3 )) ) + if ( self->enemy || other->enemy + || (other->s.number&&other->s.number!=player->client->ps.viewEntity) + /*|| (!other->s.number&&Q_irand( 0, 3 ))*/ ) {//if one of us actually has an enemy already, it's okay, just an accident OR wasn't hit by player or someone controlled by player OR player hit ally and didn't get 25% chance of getting mad (FIXME:accumulate anger+base on diff?) //FIXME: player should have to do a certain amount of damage to ally or hit them several times to make them mad //Still run pain and flee scripts @@ -347,15 +354,36 @@ void NPC_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, vec3_t p G_ActivateBehavior(self, BSET_PAIN); } } + if ( damage != -1 ) + {//-1 == don't play pain anim + //Set our proper pain animation + NPC_ChoosePainAnimation( self, other, point, damage, mod, hitLoc ); + } return; } else if ( self->NPC && !other->s.number )//should be assumed, but... {//dammit, stop that! - //FIXME: still turn on you too easily! if ( self->NPC->ffireCount < 3+((2-g_spskill->integer)*2) ) {//not mad enough yet + //Com_Printf( "chck: %d < %d\n", self->NPC->ffireCount, 3+((2-g_spskill->integer)*2) ); + if ( damage != -1 ) + {//-1 == don't play pain anim + //Set our proper pain animation + NPC_ChoosePainAnimation( self, other, point, damage, mod, hitLoc ); + } return; } + else + {//okay, we're going to turn on our ally, we need to + self->NPC->behaviorState = self->NPC->tempBehavior = self->NPC->defaultBehavior = BS_DEFAULT; + other->flags &= ~FL_NOTARGET; + self->svFlags &= ~(SVF_IGNORE_ENEMIES|SVF_ICARUS_FREEZE|SVF_NO_COMBAT_SOUNDS); + G_SetEnemy( self, other ); + self->svFlags |= SVF_LOCKEDENEMY; + self->NPC->scriptFlags &= ~(SCF_DONT_FIRE|SCF_CROUCHED|SCF_WALKING|SCF_NO_COMBAT_TALK|SCF_FORCED_MARCH); + self->NPC->scriptFlags |= (SCF_CHASE_ENEMIES|SCF_NO_MIND_TRICK); + stop_icarus = qtrue; + } } } } @@ -439,22 +467,22 @@ void NPC_Touch(gentity_t *self, gentity_t *other, trace_t *trace) {//a goodie key if ( (keyTaken = INV_GoodieKeyGive( other )) == qtrue ) { - text = "cp \"You took the Imperial's goodie key\""; + text = "cp @INGAME_TOOK_IMPERIAL_GOODIE_KEY"; } else { - text = "cp \"You can't carry any more goodie keys\""; + text = "cp @INGAME_CANT_CARRY_GOODIE_KEY"; } } else {//a named security key if ( (keyTaken = INV_SecurityKeyGive( player, self->message )) == qtrue ) { - text = "cp \"You took the Imperial's security key\""; + text = "cp @INGAME_TOOK_IMPERIAL_SECURITY_KEY"; } else { - text = "cp \"You can't carry any more security keys\""; + text = "cp @INGAME_CANT_CARRY_SECURITY_KEY"; } } if ( keyTaken ) @@ -610,38 +638,6 @@ void WaitNPCRespond ( gentity_t *self ) } } -/* -void G_PlayerGreet( gentity_t *self, thinkFunc_t thinkFunc ) - - Makes the player say a greeting and waits until he's done to make the NPC respond -*/ -qboolean G_PlayerGreet( gentity_t *self, qboolean useWhenDone ) -{ - if ( !gi.VoiceVolume[0] ) - {//used by the player and the player isn't talking - sfxHandle_t greeting = NULL; - //FIXME: If it's the player doing this, see who they're talking to and - //have Kyle play the right greeting sound (character name if a known - //character else just lt., ensign or crewman). - - if ( greeting != NULL ) - { - cgi_S_StartSound (NULL, 0, CHAN_VOICE, greeting ); - - //Responder waits until Kyle is done talking then makes the NPC respond. - gentity_t *responder = G_Spawn(); - responder->enemy = self; - responder->alt_fire = useWhenDone; - responder->e_ThinkFunc = thinkF_WaitNPCRespond; - responder->nextthink = level.time + 500; - //set self to not respond for a bit longer - self->NPC->blockedSpeechDebounceTime = level.time + 1000; - return qtrue; - } - } - - return qfalse; -} /* ------------------------- NPC_UseResponse @@ -650,8 +646,6 @@ NPC_UseResponse void NPC_UseResponse( gentity_t *self, gentity_t *user, qboolean useWhenDone ) { - qboolean noGreet = qfalse; - if ( !self->NPC || !self->client ) { return; @@ -686,15 +680,6 @@ void NPC_UseResponse( gentity_t *self, gentity_t *user, qboolean useWhenDone ) {//you're not trying to use me return; } - else - {//I'm talking, so don't greet me - noGreet = qtrue; - } - } - - if ( !noGreet && user->s.number == 0 && G_PlayerGreet( self, useWhenDone ) ) - { - return; } if ( useWhenDone ) diff --git a/code/game/NPC_spawn.cpp b/code/game/NPC_spawn.cpp index f41ec47..001f05a 100644 --- a/code/game/NPC_spawn.cpp +++ b/code/game/NPC_spawn.cpp @@ -23,6 +23,7 @@ extern void Jedi_Cloak( gentity_t *self ); //extern void FX_BorgTeleport( vec3_t org ); +extern void G_MatchPlayerWeapon( gentity_t *ent ); extern void Q3_SetParm (int entID, int parmNum, const char *parmValue); extern team_t TranslateTeamName( const char *name ); extern char *TeamNames[TEAM_NUM_TEAMS]; @@ -52,6 +53,7 @@ extern void NPC_Howler_Precache( void ); extern void NPC_ATST_Precache(void); extern void NPC_Sentry_Precache(void); extern void NPC_Mark1_Precache(void); +extern void NPC_Mark2_Precache(void); extern void NPC_GalakMech_Precache( void ); extern void NPC_GalakMech_Init( gentity_t *ent ); @@ -366,7 +368,7 @@ void NPC_SetMiscDefaultData( gentity_t *ent ) } - if ( ent->client->NPC_class == CLASS_ATST) + if ( ent->client->NPC_class == CLASS_ATST || ent->client->NPC_class == CLASS_MARK1 ) // chris/steve/kevin requested that the mark1 be shielded also { ent->flags |= (FL_SHIELDED|FL_NO_KNOCKBACK); } @@ -457,7 +459,7 @@ int NPC_WeaponsForTeam( team_t team, int spawnflags, const char *NPC_type ) { return WP_NONE; } - if ( Q_stricmp( "gran", NPC_type ) == 0 ) + if ( Q_strncmp( "gran", NPC_type, 4 ) == 0 ) { return (( 1 << WP_THERMAL)|( 1 << WP_MELEE)); } @@ -820,6 +822,15 @@ void NPC_Begin (gentity_t *ent) } else if ( ent->NPC->stats.health ) // Was health supplied in NPC.cfg? { + /* + if ( ent->client->NPC_class == CLASS_REBORN + || ent->client->NPC_class == CLASS_SHADOWTROOPER + || ent->client->NPC_class == CLASS_TAVION + || ent->client->NPC_class == CLASS_DESANN ) + {//hmm, up Jedi + ent->NPC->stats.health += ent->NPC->stats.health/2 * g_spskill->integer; + } + */ ent->max_health = client->pers.maxHealth = client->ps.stats[STAT_MAX_HEALTH] = ent->NPC->stats.health; } else @@ -827,10 +838,27 @@ void NPC_Begin (gentity_t *ent) ent->max_health = client->pers.maxHealth = client->ps.stats[STAT_MAX_HEALTH] = 100; } + if ( ent->client->NPC_class == CLASS_STORMTROOPER + || ent->client->NPC_class == CLASS_SWAMPTROOPER + || ent->client->NPC_class == CLASS_IMPWORKER + || !Q_stricmp( "rodian2", ent->NPC_type ) ) + {//tweak yawspeed for these NPCs based on difficulty + switch ( g_spskill->integer ) + { + case 0: + ent->NPC->stats.yawSpeed *= 0.75f; + break; + case 2: + ent->NPC->stats.yawSpeed *= 1.5f; + break; + } + } + ent->s.groundEntityNum = ENTITYNUM_NONE; ent->mass = 10; ent->takedamage = qtrue; ent->inuse = qtrue; + SetInUse(ent); ent->classname = "NPC"; // if ( ent->client->race == RACE_HOLOGRAM ) // {//can shoot through holograms, but not walk through them @@ -974,6 +1002,12 @@ void NPC_Begin (gentity_t *ent) _VectorCopy( client->pers.cmd_angles, ucmd.angles ); ent->client->ps.groundEntityNum = ENTITYNUM_NONE; + + if ( ent->NPC->aiFlags & NPCAI_MATCHPLAYERWEAPON ) + { + G_MatchPlayerWeapon( ent ); + } + ClientThink( ent->s.number, &ucmd ); gi.linkentity( ent ); @@ -1182,7 +1216,7 @@ void NPC_Spawn_Go( gentity_t *ent ) if ( ent->NPC_type ) { - if ( strstr( ent->NPC_type, "Kyle" ) != NULL || strstr( ent->NPC_type, "alexa" ) != NULL ) + if ( !Q_stricmp( ent->NPC_type, "kyle" ) ) { newent->NPC->aiFlags |= NPCAI_MATCHPLAYERWEAPON; } @@ -1825,7 +1859,7 @@ void SP_NPC_Prisoner( gentity_t *self) } else { - self->NPC_type = "Prisoner"; + self->NPC_type = "Prisoner2"; } } @@ -1843,7 +1877,14 @@ void SP_NPC_Rebel( gentity_t *self) { if(!self->NPC_type) { - self->NPC_type = "Rebel"; + if ( Q_irand( 0, 1 ) ) + { + self->NPC_type = "Rebel"; + } + else + { + self->NPC_type = "Rebel2"; + } } SP_NPC_spawner( self ); @@ -1962,7 +2003,14 @@ void SP_NPC_Gran( gentity_t *self) } else { - self->NPC_type = "gran"; + if ( Q_irand( 0, 1 ) ) + { + self->NPC_type = "gran"; + } + else + { + self->NPC_type = "gran2"; + } } } @@ -2106,7 +2154,18 @@ void SP_NPC_ImpWorker( gentity_t *self) { if ( !self->NPC_type ) { - self->NPC_type = "ImpWorker"; + if ( !Q_irand( 0, 2 ) ) + { + self->NPC_type = "ImpWorker"; + } + else if ( Q_irand( 0, 1 ) ) + { + self->NPC_type = "ImpWorker2"; + } + else + { + self->NPC_type = "ImpWorker3"; + } } SP_NPC_spawner( self ); @@ -2123,7 +2182,14 @@ void SP_NPC_BespinCop( gentity_t *self) { if ( !self->NPC_type ) { - self->NPC_type = "BespinCop"; + if ( !Q_irand( 0, 1 ) ) + { + self->NPC_type = "BespinCop"; + } + else + { + self->NPC_type = "BespinCop2"; + } } SP_NPC_spawner( self ); @@ -2407,6 +2473,7 @@ void SP_NPC_Droid_Mark2( gentity_t *self) SP_NPC_spawner( self ); + NPC_Mark2_Precache(); RegisterItem( FindItemForAmmo( AMMO_BLASTER )); RegisterItem( FindItemForAmmo( AMMO_METAL_BOLTS)); } @@ -2684,6 +2751,10 @@ static void NPC_Spawn_f(void) { NPC_Mark1_Precache(); } + else if ( !Q_stricmp( "mark2", NPCspawner->NPC_type)) + { + NPC_Mark2_Precache(); + } else if ( !Q_stricmp( "interrogator", NPCspawner->NPC_type)) { NPC_Interrogator_Precache(NULL); @@ -2712,6 +2783,11 @@ static void NPC_Spawn_f(void) { NPC_Howler_Precache(); } + else if ( !Q_stricmp( "sentry", NPCspawner->NPC_type )) + { + NPC_Sentry_Precache(); + } + NPC_Spawn (NPCspawner, NPCspawner, NPCspawner); } diff --git a/code/game/NPC_stats.cpp b/code/game/NPC_stats.cpp index 40d3581..8110e3e 100644 --- a/code/game/NPC_stats.cpp +++ b/code/game/NPC_stats.cpp @@ -243,6 +243,10 @@ static saber_colors_t TranslateSaberColor( const char *name ) { return SABER_PURPLE; } + if ( !Q_stricmp( name, "random" ) ) + { + return ((saber_colors_t)(Q_irand( SABER_ORANGE, SABER_PURPLE ))); + } return SABER_BLUE; } @@ -326,26 +330,24 @@ qboolean G_ParseAnimationFile( const char *af_filename ) float fps; int skip; char text[40000]; - fileHandle_t f; int animNum; animation_t *animations = level.knownAnimFileSets[level.numKnownAnimFileSets].animations; - // load the file - len = gi.FS_FOpenFile( af_filename, &f, FS_READ ); + len = gi.RE_GetAnimationCFG(af_filename, NULL, 0); + if (len <= 0) + { + return qfalse; + } if ( len <= 0 ) { return qfalse; } if ( len >= sizeof( text ) - 1 ) { - gi.FS_FCloseFile( f ); G_Error( "G_ParseAnimationFile: File %s too long\n (%d > %d)", af_filename, len, sizeof( text ) - 1); return qfalse; } - - gi.FS_Read( text, len, f ); - text[len] = 0; - gi.FS_FCloseFile( f ); + len = gi.RE_GetAnimationCFG(af_filename, text, sizeof(text)); // parse the text text_p = text; @@ -493,13 +495,22 @@ void G_LoadAnimFileSet( gentity_t *ent, const char *modelName ) } //get the location of the animation.cfg GLAName = gi.G2API_GetGLAName( &ent->ghoul2[ent->playerModel] ); - Q_strncpyz( animName, GLAName, sizeof( animName ), qtrue ); - slash = strrchr( animName, '/' ); - if ( slash ) + //now load and parse the animation.cfg, animsounds.cfg and set the animFileIndex + if ( !GLAName) { - *slash = 0; + Com_Printf( S_COLOR_RED"Failed find animation file name models/players/%s/animation.cfg\n", modelName ); + strippedName="broken"; + } + else + { + Q_strncpyz( animName, GLAName, sizeof( animName ), qtrue ); + slash = strrchr( animName, '/' ); + if ( slash ) + { + *slash = 0; + } + strippedName = COM_SkipPath( animName ); } - strippedName = COM_SkipPath( animName ); //now load and parse the animation.cfg, animsounds.cfg and set the animFileIndex if ( !G_ParseAnimFileSet( modelName, strippedName, &ent->client->clientInfo.animFileIndex ) ) @@ -663,12 +674,13 @@ void NPC_Precache ( gentity_t *spawner ) char sound[MAX_QPATH]; qboolean md3Model = qfalse; char playerModel[MAX_QPATH]; - char *customSkin = NULL; + char customSkin[MAX_QPATH]; if ( !Q_stricmp( "random", spawner->NPC_type ) ) {//sorry, can't precache a random just yet return; } + strcpy(customSkin,"default"); p = NPCParms; COM_BeginParseSession(); @@ -782,7 +794,7 @@ void NPC_Precache ( gentity_t *spawner ) { continue; } - customSkin = G_NewString( value ); + Q_strncpyz( customSkin, value, sizeof(customSkin), qtrue); continue; } @@ -1005,7 +1017,7 @@ qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) char *patch; char sound[MAX_QPATH]; char playerModel[MAX_QPATH]; - char *customSkin = NULL; + char customSkin[MAX_QPATH]; clientInfo_t *ci = &NPC->client->clientInfo; renderInfo_t *ri = &NPC->client->renderInfo; gNPCstats_t *stats = NULL; @@ -1013,6 +1025,7 @@ qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) char surfOff[1024]; char surfOn[1024]; + strcpy(customSkin,"default"); if ( !NPCName || !NPCName[0]) { NPCName = "Player"; @@ -1101,6 +1114,7 @@ qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) NPC->client->dismemberProbHead = 100; NPC->client->dismemberProbArms = 100; + NPC->client->dismemberProbHands = 100; NPC->client->dismemberProbWaist = 100; NPC->client->dismemberProbLegs = 100; @@ -1235,7 +1249,7 @@ qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) { continue; } - customSkin = G_NewString( value ); + Q_strncpyz( customSkin, value, sizeof(customSkin), qtrue); continue; } @@ -1843,6 +1857,24 @@ qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) continue; } + // dismemberment probability for hands + if ( !Q_stricmp( token, "dismemberProbHands" ) ) { + if ( COM_ParseInt( &p, &n ) ) { + SkipRestOfLine( &p ); + continue; + } + if ( n < 0 ) + { + gi.Printf( "bad %s in NPC '%s'\n", token, NPCName ); + continue; + } + if ( NPC->NPC ) + { + NPC->client->dismemberProbHands = n; + } + continue; + } + // dismemberment probability for waist if ( !Q_stricmp( token, "dismemberProbWaist" ) ) { if ( COM_ParseInt( &p, &n ) ) { diff --git a/code/game/NPC_utils.cpp b/code/game/NPC_utils.cpp index 73edbf0..bae02ac 100644 --- a/code/game/NPC_utils.cpp +++ b/code/game/NPC_utils.cpp @@ -62,6 +62,11 @@ void CalcEntitySpot ( const gentity_t *ent, const spot_t spot, vec3_t point ) {//adjust up some point[2] += 28;//magic number :) } + if ( ent->NPC ) + {//always aim from the center of my bbox, so we don't wiggle when we lean forward or backwards + point[0] = ent->currentOrigin[0]; + point[1] = ent->currentOrigin[1]; + } } else { @@ -89,6 +94,11 @@ void CalcEntitySpot ( const gentity_t *ent, const spot_t spot, vec3_t point ) {//adjust up some point[2] += 28;//magic number :) } + if ( ent->NPC ) + {//always aim from the center of my bbox, so we don't wiggle when we lean forward or backwards + point[0] = ent->currentOrigin[0]; + point[1] = ent->currentOrigin[1]; + } //NOTE: automatically takes leaning into account! } else diff --git a/code/game/Q3_Interface.cpp b/code/game/Q3_Interface.cpp index 86761bd..f1eadaf 100644 --- a/code/game/Q3_Interface.cpp +++ b/code/game/Q3_Interface.cpp @@ -321,6 +321,7 @@ stringID_table_t setTable[] = ENUM2STRING(SET_TACTICAL_SHOW), ENUM2STRING(SET_TACTICAL_HIDE), ENUM2STRING(SET_FOLLOWDIST), + ENUM2STRING(SET_SCALE), ENUM2STRING(SET_OBJECTIVE_CLEARALL), ENUM2STRING(SET_MISSIONSTATUSTEXT), ENUM2STRING(SET_WIDTH), @@ -352,8 +353,15 @@ stringID_table_t setTable[] = ENUM2STRING(SET_MISSION_STATUS_SCREEN), ENUM2STRING(SET_END_SCREENDISSOLVE), ENUM2STRING(SET_LOOPSOUND), + ENUM2STRING(SET_ICARUS_FREEZE), + ENUM2STRING(SET_ICARUS_UNFREEZE), ENUM2STRING(SET_USE_CP_NEAREST), - + ENUM2STRING(SET_MORELIGHT), + ENUM2STRING(SET_CINEMATIC_SKIPSCRIPT), + ENUM2STRING(SET_NO_FORCE), + ENUM2STRING(SET_NO_FALLTODEATH), + ENUM2STRING(SET_DISMEMBERABLE), + ENUM2STRING(SET_NO_ACROBATICS), //FIXME: add BOTH_ attributes here too "", SET_, }; @@ -982,6 +990,7 @@ static int Q3_PlaySound( int taskID, int entID, const char *name, const char *ch { voice_chan = CHAN_VOICE_GLOBAL; type_voice = qtrue; + bBroadcast = true; } // if we're in-camera, check for skipping cinematic and ifso, no subtitle print (since screen is not being @@ -998,7 +1007,7 @@ static int Q3_PlaySound( int taskID, int entID, const char *name, const char *ch { gi.SendServerCommand( NULL, "ct \"%s\" %i", finalName, soundHandle ); } -/* else if (precacheWav[i].speaker==SP_NONE) // lower screen text + else //if (precacheWav[i].speaker==SP_NONE) // lower screen text { gentity_t *ent2 = &g_entities[0]; // the numbers in here were either the original ones Bob entered (350), or one arrived at from checking the distance Chell stands at in stasis2 by the computer core that was submitted as a bug report... @@ -1008,7 +1017,6 @@ static int Q3_PlaySound( int taskID, int entID, const char *name, const char *ch gi.SendServerCommand( NULL, "ct \"%s\" %i", finalName, soundHandle ); } } -*/ } // Cinematic only else if (g_subtitles->integer == 2) // Show only talking head text and CINEMATIC @@ -1433,8 +1441,17 @@ void moveAndRotateCallback( gentity_t *ent ) } void Blocked_Mover( gentity_t *ent, gentity_t *other ) { - // remove anything other than a client - if ( !other->client ) { + // remove anything other than a client -- no longer the case + + // don't remove security keys or goodie keys + if ( (other->s.eType == ET_ITEM) && (other->item->giTag >= INV_GOODIE_KEY1 && other->item->giTag <= INV_SECURITY_KEY5) ) + { + // should we be doing anything special if a key blocks it... move it somehow..? + } + // if your not a client, or your a dead client remove yourself... + else if ( other->s.number && (!other->client || (other->client && other->health <= 0 && other->contents == CONTENTS_CORPSE && !other->message)) ) + { + // if an item or weapon can we do a little explosion..? G_FreeEntity( other ); return; } @@ -1868,6 +1885,21 @@ static void Q3_SetNavGoal( int entID, const char *name ) gentity_t *ent = &g_entities[ entID ]; vec3_t goalPos; + if ( !ent->health ) + { + Q3_DebugPrint( WL_ERROR, "Q3_SetNavGoal: tried to set a navgoal (\"%s\") on a corpse! \"%s\"\n", name, ent->script_targetname ); + return; + } + if ( !ent->NPC ) + { + Q3_DebugPrint( WL_ERROR, "Q3_SetNavGoal: tried to set a navgoal (\"%s\") on a non-NPC: \"%s\"\n", name, ent->script_targetname ); + return; + } + if ( !ent->NPC->tempGoal ) + { + Q3_DebugPrint( WL_ERROR, "Q3_SetNavGoal: tried to set a navgoal (\"%s\") on a dead NPC: \"%s\"\n", name, ent->script_targetname ); + return; + } //Get the position of the goal if ( TAG_GetOrigin2( NULL, name, goalPos ) == false ) { @@ -1877,33 +1909,25 @@ static void Q3_SetNavGoal( int entID, const char *name ) Q3_DebugPrint( WL_ERROR, "Q3_SetNavGoal: can't find NAVGOAL \"%s\"\n", name ); return; } - else if ( ent->NPC ) + else { ent->NPC->goalEntity = targ; ent->NPC->goalRadius = sqrt(ent->maxs[0]+ent->maxs[0]) + sqrt(targ->maxs[0]+targ->maxs[0]); ent->NPC->aiFlags &= ~NPCAI_TOUCHED_GOAL; - return; } } else { int goalRadius = TAG_GetRadius( NULL, name ); - - if ( ent->NPC ) - { - NPC_SetMoveGoal( ent, goalPos, goalRadius, qtrue ); - //We know we want to clear the lastWaypoint here - ent->NPC->goalEntity->lastWaypoint = WAYPOINT_NONE; - ent->NPC->aiFlags &= ~NPCAI_TOUCHED_GOAL; + NPC_SetMoveGoal( ent, goalPos, goalRadius, qtrue ); + //We know we want to clear the lastWaypoint here + ent->NPC->goalEntity->lastWaypoint = WAYPOINT_NONE; + ent->NPC->aiFlags &= ~NPCAI_TOUCHED_GOAL; #ifdef _DEBUG - //this is *only* for debugging navigation - ent->NPC->tempGoal->target = G_NewString( name ); + //this is *only* for debugging navigation + ent->NPC->tempGoal->target = G_NewString( name ); #endif// _DEBUG - return; - } } - - Q3_DebugPrint( WL_ERROR, "Q3_SetNavGoal: tried to set a navgoal (\"%s\") on a non-NPC: \"%s\"\n", name, ent->script_targetname ); } //----------------------------------------------- @@ -3119,6 +3143,7 @@ void Q3_SetLoopSound(int entID, const char *name) if ( Q_stricmp( "NULL", name ) == 0 || Q_stricmp( "NONE", name )==0) { self->s.loopSound = 0; + return; } index = G_SoundIndex( name ); @@ -3133,7 +3158,29 @@ void Q3_SetLoopSound(int entID, const char *name) } } +void Q3_SetICARUSFreeze( int entID, const char *name, qboolean freeze ) +{ + gentity_t *self = G_Find( NULL, FOFS(targetname), name ); + if ( !self ) + {//hmm, targetname failed, try script_targetname? + self = G_Find( NULL, FOFS(script_targetname), name ); + } + if ( !self ) + { + Q3_DebugPrint( WL_WARNING, "Q3_SetICARUSFreeze: invalid ent %s\n", name); + return; + } + + if ( freeze ) + { + self->svFlags |= SVF_ICARUS_FREEZE; + } + else + { + self->svFlags &= ~SVF_ICARUS_FREEZE; + } +} /* ============ @@ -3214,6 +3261,11 @@ static void Q3_SetWeapon (int entID, const char *wp_name) return; } + if ( self->NPC ) + {//since a script sets a weapon, we presume we don't want to auto-match the player's weapon anymore + self->NPC->aiFlags &= ~NPCAI_MATCHPLAYERWEAPON; + } + if(!Q_stricmp("drop", wp_name)) {//no weapon, drop it TossClientItems( self ); @@ -3629,6 +3681,29 @@ static void Q3_SetFollowDist(int entID, float float_data) } +/* +============ +Q3_SetScale + Description : + Return type : static void + Argument : int entID + Argument : float float_data +============ +*/ +static void Q3_SetScale(int entID, float float_data) +{ + gentity_t *self = &g_entities[entID]; + + if ( !self ) + { + Q3_DebugPrint( WL_WARNING, "Q3_SetScale: invalid entID %d\n", entID); + return; + } + + self->s.scale = float_data; +} + + /* ============ Q3_SetCount @@ -3914,6 +3989,12 @@ static void Q3_SetForcePowerLevel ( int entID, int forcePower, int forceLevel ) return; } + if (forceLevel > self->client->ps.forcePowerLevel[forcePower]) + { + missionInfo_Updated = qtrue; // Activate flashing text + gi.cvar_set("cg_updatedDataPadForcePower", va("%d",forcePower+1)); // The +1 is offset in the print routine. It ain't pretty, I know. + } + self->client->ps.forcePowerLevel[forcePower] = forceLevel; if ( forceLevel ) { @@ -4517,6 +4598,28 @@ static void Q3_SetLockedEnemy ( int entID, qboolean locked) } } +char cinematicSkipScript[1024]; + +/* +============ +Q3_SetCinematicSkipScript + +============ +*/ +static Q3_SetCinematicSkipScript( char *scriptname ) +{ + + if(Q_stricmp("none", scriptname) == 0 || Q_stricmp("NULL", scriptname) == 0) + { + memset( cinematicSkipScript, 0, sizeof( cinematicSkipScript ) ); + } + else + { + Q_strncpyz(cinematicSkipScript,scriptname,sizeof(cinematicSkipScript)); + } + +} + /* ============ Q3_SetNoMindTrick @@ -4949,6 +5052,165 @@ static void Q3_SetUseCpNearest( int entID, qboolean add) } } +/* +============ +Q3_SetNoForce + +? +============ +*/ +static void Q3_SetNoForce( int entID, qboolean add) +{ + gentity_t *ent = &g_entities[entID]; + + if ( !ent ) + { + Q3_DebugPrint( WL_WARNING, "Q3_SetNoForce: invalid entID %d\n", entID); + return; + } + + if ( !ent->NPC ) + { + Q3_DebugPrint( WL_ERROR, "Q3_SetNoForce: '%s' is not an NPC!\n", ent->targetname ); + return; + } + + if ( add ) + { + ent->NPC->scriptFlags |= SCF_NO_FORCE; + } + else + { + ent->NPC->scriptFlags &= ~SCF_NO_FORCE; + } +} + +/* +============ +Q3_SetNoAcrobatics + +? +============ +*/ +static void Q3_SetNoAcrobatics( int entID, qboolean add) +{ + gentity_t *ent = &g_entities[entID]; + + if ( !ent ) + { + Q3_DebugPrint( WL_WARNING, "Q3_SetNoAcrobatics: invalid entID %d\n", entID); + return; + } + + if ( !ent->NPC ) + { + Q3_DebugPrint( WL_ERROR, "Q3_SetNoAcrobatics: '%s' is not an NPC!\n", ent->targetname ); + return; + } + + if ( add ) + { + ent->NPC->scriptFlags |= SCF_NO_ACROBATICS; + } + else + { + ent->NPC->scriptFlags &= ~SCF_NO_ACROBATICS; + } +} + +/* +============ +Q3_SetNoFallToDeath + +? +============ +*/ +static void Q3_SetNoFallToDeath( int entID, qboolean add) +{ + gentity_t *ent = &g_entities[entID]; + + if ( !ent ) + { + Q3_DebugPrint( WL_WARNING, "Q3_SetNoFallToDeath: invalid entID %d\n", entID); + return; + } + + if ( !ent->NPC ) + { + Q3_DebugPrint( WL_ERROR, "Q3_SetNoFallToDeath: '%s' is not an NPC!\n", ent->targetname ); + return; + } + + if ( add ) + { + ent->NPC->scriptFlags |= SCF_NO_FALLTODEATH; + } + else + { + ent->NPC->scriptFlags &= ~SCF_NO_FALLTODEATH; + } +} + +/* +============ +Q3_SetDismemberable + +? +============ +*/ +static void Q3_SetDismemberable( int entID, qboolean dismemberable) +{ + gentity_t *ent = &g_entities[entID]; + + if ( !ent ) + { + Q3_DebugPrint( WL_WARNING, "Q3_SetDismemberable: invalid entID %d\n", entID); + return; + } + + if ( !ent->client ) + { + Q3_DebugPrint( WL_ERROR, "Q3_SetDismemberable: '%s' is not an client!\n", ent->targetname ); + return; + } + + ent->client->dismembered = !dismemberable; +} + + +/* +============ +Q3_SetMoreLight + +? +============ +*/ +static void Q3_SetMoreLight( int entID, qboolean add ) +{ + gentity_t *ent = &g_entities[entID]; + + if ( !ent ) + { + Q3_DebugPrint( WL_WARNING, "Q3_SetMoreLight: invalid entID %d\n", entID); + return; + } + + if ( !ent->NPC ) + { + Q3_DebugPrint( WL_ERROR, "Q3_SetMoreLight: '%s' is not an NPC!\n", ent->targetname ); + return; + } + + if ( add ) + { + ent->NPC->scriptFlags |= SCF_MORELIGHT; + } + else + { + ent->NPC->scriptFlags &= ~SCF_MORELIGHT; + } +} + /* ============ Q3_SetUndying @@ -5652,6 +5914,7 @@ static void Q3_SetStartFrame( int entID, int startFrame ) if ( startFrame >= 0 ) { + ent->s.frame = startFrame; ent->startFrame = startFrame; } } @@ -6270,6 +6533,11 @@ static void Q3_Set( int taskID, int entID, const char *type_name, const char *da Q3_SetLoopSound( entID, (char *) data ); break; + case SET_ICARUS_FREEZE: + case SET_ICARUS_UNFREEZE: + Q3_SetICARUSFreeze( entID, (char *) data, (toSet==SET_ICARUS_FREEZE) ); + break; + case SET_WEAPON: Q3_SetWeapon ( entID, (char *) data); break; @@ -6329,6 +6597,11 @@ static void Q3_Set( int taskID, int entID, const char *type_name, const char *da Q3_SetFollowDist( entID, float_data); break; + case SET_SCALE: + float_data = atof((char *) data); + Q3_SetScale( entID, float_data); + break; + case SET_COUNT: Q3_SetCount( entID, (char *) data); break; @@ -6503,6 +6776,11 @@ static void Q3_Set( int taskID, int entID, const char *type_name, const char *da Q3_SetNoMindTrick( entID, qfalse); break; + case SET_CINEMATIC_SKIPSCRIPT : + Q3_SetCinematicSkipScript((char *) data); + break; + + case SET_DELAYSCRIPTTIME: int_data = atoi((char *) data); Q3_SetDelayScriptTime( entID, int_data ); @@ -6592,6 +6870,42 @@ static void Q3_Set( int taskID, int entID, const char *type_name, const char *da Q3_SetUseCpNearest( entID, qfalse); break; + case SET_NO_FORCE: + if(!stricmp("true", ((char *)data))) + Q3_SetNoForce( entID, qtrue); + else + Q3_SetNoForce( entID, qfalse); + break; + + case SET_NO_ACROBATICS: + if(!stricmp("true", ((char *)data))) + Q3_SetNoAcrobatics( entID, qtrue); + else + Q3_SetNoAcrobatics( entID, qfalse); + break; + + case SET_NO_FALLTODEATH: + if(!stricmp("true", ((char *)data))) + Q3_SetNoFallToDeath( entID, qtrue); + else + Q3_SetNoFallToDeath( entID, qfalse); + break; + + case SET_DISMEMBERABLE: + if(!stricmp("true", ((char *)data))) + Q3_SetDismemberable( entID, qtrue); + else + Q3_SetDismemberable( entID, qfalse); + break; + + case SET_MORELIGHT: + if(!stricmp("true", ((char *)data))) + Q3_SetMoreLight( entID, qtrue); + else + Q3_SetMoreLight( entID, qfalse); + break; + + case SET_TREASONED: Q3_DebugPrint( WL_VERBOSE, "SET_TREASONED is disabled, do not use\n" ); /* @@ -6979,6 +7293,9 @@ extern void LockDoors(gentity_t *const ent); break; case SET_OBJECTIVE_SHOW: + missionInfo_Updated = qtrue; // Activate flashing text + gi.cvar_set("cg_updatedDataPadObjective", "1"); + Q3_SetObjective((const char *) data ,SET_OBJ_SHOW); Q3_SetObjective((const char *) data ,SET_OBJ_PENDING); break; @@ -6986,6 +7303,8 @@ extern void LockDoors(gentity_t *const ent); Q3_SetObjective((const char *) data ,SET_OBJ_HIDE); break; case SET_OBJECTIVE_SUCCEEDED: + missionInfo_Updated = qtrue; // Activate flashing text + gi.cvar_set("cg_updatedDataPadObjective", "1"); Q3_SetObjective((const char *) data ,SET_OBJ_SUCCEEDED); break; case SET_OBJECTIVE_FAILED: @@ -7150,6 +7469,19 @@ static void Q3_RemoveEnt( gentity_t *victim ) victim->health = 0; victim->targetname = NULL; + if ( victim->NPC && victim->NPC->tempGoal != NULL ) + { + G_FreeEntity( victim->NPC->tempGoal ); + victim->NPC->tempGoal = NULL; + } + if ( victim->client->ps.saberEntityNum != ENTITYNUM_NONE && victim->client->ps.saberEntityNum > 0 ) + { + if ( g_entities[victim->client->ps.saberEntityNum].inuse ) + { + G_FreeEntity( &g_entities[victim->client->ps.saberEntityNum] ); + } + victim->client->ps.saberEntityNum = ENTITYNUM_NONE; + } //Disappear in half a second victim->e_ThinkFunc = thinkF_G_FreeEntity; victim->nextthink = level.time + 500; @@ -7818,6 +8150,46 @@ static int Q3_GetFloat( int entID, int type, const char *name, float *value ) } *value = (ent->NPC->scriptFlags&SCF_USE_CP_NEAREST); break; + case SET_DISMEMBERABLE://## %t="BOOL_TYPES" # NPC will not be affected by force powers + if ( ent->client == NULL ) + { + Q3_DebugPrint( WL_WARNING, "Q3_GetFloat: SET_DISMEMBERABLE, %s not a client\n", ent->targetname ); + return false; + } + *value = !(ent->client->dismembered); + break; + case SET_NO_FORCE: + if ( ent->NPC == NULL ) + { + Q3_DebugPrint( WL_WARNING, "Q3_GetFloat: SET_NO_FORCE, %s not an NPC\n", ent->targetname ); + return false; + } + *value = (ent->NPC->scriptFlags&SCF_NO_FORCE); + break; + case SET_NO_ACROBATICS: + if ( ent->NPC == NULL ) + { + Q3_DebugPrint( WL_WARNING, "Q3_GetFloat: SET_NO_ACROBATICS, %s not an NPC\n", ent->targetname ); + return false; + } + *value = (ent->NPC->scriptFlags&SCF_NO_ACROBATICS); + break; + case SET_NO_FALLTODEATH://## %t="BOOL_TYPES" # NPC will not be affected by force powers + if ( ent->NPC == NULL ) + { + Q3_DebugPrint( WL_WARNING, "Q3_GetFloat: SET_NO_FALLTODEATH, %s not an NPC\n", ent->targetname ); + return false; + } + *value = (ent->NPC->scriptFlags&SCF_NO_FALLTODEATH); + break; + case SET_MORELIGHT://## %t="BOOL_TYPES" # NPCs will use their closest combat points, not try and find ones next to the player, or flank player + if ( ent->NPC == NULL ) + { + Q3_DebugPrint( WL_WARNING, "Q3_GetFloat: SET_MORELIGHT, %s not an NPC\n", ent->targetname ); + return false; + } + *value = (ent->NPC->scriptFlags&SCF_MORELIGHT); + break; case SET_TREASONED://## %t="BOOL_TYPES" # Player has turned on his own- scripts will stop: NPCs will turn on him and level changes load the brig Q3_DebugPrint( WL_VERBOSE, "SET_TREASONED is disabled, do not use\n" ); *value = 0;//(ffireLevel>=FFIRE_LEVEL_RETALIATION); diff --git a/code/game/Q3_Interface.h b/code/game/Q3_Interface.h index 7ac3068..223e942 100644 --- a/code/game/Q3_Interface.h +++ b/code/game/Q3_Interface.h @@ -43,6 +43,7 @@ typedef enum //# setType_e SET_FFDEATHSCRIPT,//## %s="NULL" !!"W:\game\base\scripts\!!#*.txt" # Script to run when player kills a teammate SET_MINDTRICKSCRIPT,//## %s="NULL" !!"W:\game\base\scripts\!!#*.txt" # Script to run when player kills a teammate SET_VIDEO_PLAY,//## %s="filename" !!"W:\game\base\video\!!#*.roq" # Play a video (inGame) + SET_CINEMATIC_SKIPSCRIPT, //## %s="filename" !!"W:\game\base\scripts\!!#*.txt" # Script to run when skipping the running cinematic //# #sep Standard strings SET_ENEMY,//## %s="NULL" # Set enemy by targetname @@ -74,6 +75,8 @@ typedef enum //# setType_e SET_FULLNAME,//## %s="NULL" # This name will appear when ent is scanned by tricorder SET_VIEWENTITY,//## %s="NULL" # Make the player look through this ent's eyes - also shunts player movement control to this ent SET_LOOPSOUND,//## %s="NULL" # Looping sound to play on entity + SET_ICARUS_FREEZE,//## %s="NULL" # Specify name of entity to freeze - !!!NOTE!!! since the ent is frozen, it cannot unfreeze itself, you must have some other entity unfreeze a frozen ent!!! + SET_ICARUS_UNFREEZE,//## %s="NULL" # Specify name of entity to unfreeze - !!!NOTE!!! since the ent is frozen, it cannot unfreeze itself, you must have some other entity unfreeze a frozen ent!!! SET_SCROLLTEXT, //## %s="" # key of text string to print SET_LCARSTEXT, //## %s="" # key of text string to print in LCARS frame @@ -105,6 +108,7 @@ typedef enum //# setType_e SET_FACEEYESOPENED, //## %f="0.0" # Set face to Eyes open SET_WAIT, //## %f="0.0" # Change an entity's wait field SET_FOLLOWDIST, //## %f="0.0" # How far away to stay from leader in BS_FOLLOW_LEADER + SET_SCALE, //## %f="0.0" # Scale the entity model //# #sep ints SET_ANIM_HOLDTIME_LOWER,//## %d="0" # Hold lower anim for number of milliseconds @@ -185,6 +189,11 @@ typedef enum //# setType_e SET_MISSION_STATUS_SCREEN,//## %t="BOOL_TYPES" # Display Mission Status screen before advancing to next level SET_END_SCREENDISSOLVE,//## %t="BOOL_TYPES" # End of game dissolve into star background and credits SET_USE_CP_NEAREST,//## %t="BOOL_TYPES" # NPCs will use their closest combat points, not try and find ones next to the player, or flank player + SET_MORELIGHT,//## %t="BOOL_TYPES" # NPC will have a minlight of 96 + SET_NO_FORCE,//## %t="BOOL_TYPES" # NPC will not be affected by force powers + SET_NO_FALLTODEATH,//## %t="BOOL_TYPES" # NPC will not scream and tumble and fall to hit death over large drops + SET_DISMEMBERABLE,//## %t="BOOL_TYPES" # NPC will not be dismemberable if you set this to false (default is true) + SET_NO_ACROBATICS,//## %t="BOOL_TYPES" # Jedi won't jump, roll or cartwheel //# #sep calls SET_SKILL,//## %r%d="0" # Cannot set this, only get it - valid values are 0 through 3 @@ -255,6 +264,8 @@ typedef enum //# playType_e const int Q3_TIME_SCALE = 1; //MILLISECONDS +extern char cinematicSkipScript[1024]; + //General extern void Q3_TaskIDClear( int *taskID ); extern qboolean Q3_TaskIDPending( gentity_t *ent, taskID_t taskType ); diff --git a/code/game/anims.h b/code/game/anims.h index fcf1aa8..9564d40 100644 --- a/code/game/anims.h +++ b/code/game/anims.h @@ -23,8 +23,6 @@ typedef enum //# animNumber_e BOTH_DEATH12, //# BOTH_DEATH13, //# BOTH_DEATH14, //# - BOTH_DEATH14_UNGRIP, //# Desann's end death (cin #35) - BOTH_DEATH14_SITUP, //# Tavion sitting up after having been thrown (cin #23) BOTH_DEATH15, //# BOTH_DEATH16, //# BOTH_DEATH17, //# @@ -124,6 +122,7 @@ typedef enum //# animNumber_e //# #sep BOTH_ ATTACKS BOTH_ATTACK1, //# Attack with stun baton BOTH_ATTACK2, //# Attack with one-handed pistol + BOTH_ATTACK2IDLE1, //# Idle with one-handed pistol BOTH_ATTACK3, //# Attack with blaster rifle BOTH_ATTACK4, //# Attack with disruptor BOTH_ATTACK5, //# Attack with bow caster @@ -611,16 +610,16 @@ typedef enum //# animNumber_e //# #sep BOTH_ STANDING BOTH_STAND1, //# Standing idle, no weapon, hands down - BOTH_STAND1_RANDOM1, //# Random standing idle - BOTH_STAND1_RANDOM2, //# Random standing idle - BOTH_STAND2, //# Standing idle with a weapon - BOTH_STAND2_RANDOM1, //# Random standing idle - BOTH_STAND2_RANDOM2, //# Random standing idle - BOTH_STAND2_RANDOM3, //# Random standing idle - BOTH_STAND2_RANDOM4, //# Random standing idle - BOTH_STAND3, //# Standing hands behind back, at ease, etc. + BOTH_STAND1IDLE1, //# Random standing idle + BOTH_STAND2, //# Standing idle with a saber + BOTH_STAND2IDLE1, //# Random standing idle + BOTH_STAND2IDLE2, //# Random standing idle + BOTH_STAND3, //# Standing idle with 2-handed weapon + BOTH_STAND3IDLE1, //# Random standing idle BOTH_STAND4, //# hands clasp behind back + BOTH_STAND4IDLE1, //# Random standing idle BOTH_STAND5, //# standing idle, no weapon, hand down, back straight + BOTH_STAND5IDLE1, //# Random standing idle BOTH_STAND6, //# one handed, gun at side, relaxed stand BOTH_STAND7, //# both hands on hips (female) BOTH_STAND8, //# both hands on hips (male) @@ -639,8 +638,12 @@ typedef enum //# animNumber_e BOTH_STAND5_REELO, //# Reelo in his stand5 position (cin #18) BOTH_STAND1TOSTAND5, //# Transition from stand1 to stand5 BOTH_STAND5TOSTAND1, //# Transition from stand5 to stand1 + BOTH_STAND5TOAIM, //# Transition of Kye aiming his gun at Desann (cin #9) + BOTH_STAND5STARTLEDLOOKLEFT, //# Kyle turning to watch the bridge drop (cin #9) + BOTH_STARTLEDLOOKLEFTTOSTAND5, //# Kyle returning to stand 5 from watching the bridge drop (cin #9) BOTH_STAND5TOSTAND8, //# Transition from stand5 to stand8 - BOTH_STAND8TOSTAND5, //# Transition from stand5 to stand8 + BOTH_STAND7TOSTAND8, //# Tavion putting hands on back of chair (cin #11) + BOTH_STAND8TOSTAND5, //# Transition from stand8 to stand5 BOTH_CONSOLE1START, //# typing at a console BOTH_CONSOLE1, //# typing at a console @@ -648,6 +651,8 @@ typedef enum //# animNumber_e BOTH_CONSOLE2START, //# typing at a console with comm link in hand (cin #5) BOTH_CONSOLE2, //# typing at a console with comm link in hand (cin #5) BOTH_CONSOLE2STOP, //# typing at a console with comm link in hand (cin #5) + BOTH_CONSOLE2HOLDCOMSTART, //# lean in to type at console while holding comm link in hand (cin #5) + BOTH_CONSOLE2HOLDCOMSTOP, //# lean away after typing at console while holding comm link in hand (cin #5) BOTH_GUARD_LOOKAROUND1, //# Cradling weapon and looking around BOTH_GUARD_IDLE1, //# Cradling weapon and standing @@ -655,6 +660,7 @@ typedef enum //# animNumber_e BOTH_GESTURE1, //# Generic gesture, non-specific BOTH_GESTURE2, //# Generic gesture, non-specific BOTH_GESTURE3, //# Generic gesture, non-specific + BOTH_WALK1TALKCOMM1, //# Talking into coom link while walking BOTH_TALK1, //# Generic talk anim BOTH_TALKCOMM1START, //# Start talking into a comm link BOTH_TALKCOMM1, //# Talking into a comm link @@ -695,6 +701,9 @@ typedef enum //# animNumber_e BOTH_TALKGESTURE19START, //# Desann lifting his arm "Join me" (cin #34) BOTH_TALKGESTURE19STOP, //# Desann lifting his arm "Join me" (cin #34) BOTH_TALKGESTURE20START, //# Kyle lifting his arm "Join us" (cin #34) + BOTH_TALKGESTURE21, //# generic talk gesture from stand3 + BOTH_TALKGESTURE22, //# generic talk gesture from stand3 + BOTH_TALKGESTURE23, //# generic talk gesture from stand3 BOTH_PAUSE1START, //# Luke pauses to warn Kyle (cin #24) start BOTH_PAUSE1STOP, //# Luke pauses to warn Kyle (cin #24) stop @@ -725,13 +734,6 @@ typedef enum //# animNumber_e BOTH_EXAMINE3, //# Hold Lando looking around corner BOTH_EXAMINE3STOP, //# End Lando looking around corner - BOTH_THROW1START, //# Kyle thrown to the right - BOTH_THROW1, //# Kyle thrown to the right - BOTH_THROW1STOP, //# Kyle thrown to the right - BOTH_THROW2START, //# Kyle thrown to the left - BOTH_THROW2, //# Kyle thrown to the left - BOTH_THROW3, //# Kyle thrown backwards in cin #9 - BOTH_LEANLEFT2START, //# Start leaning left in chair BOTH_LEANLEFT2STOP, //# Stop leaning left in chair BOTH_LEANRIGHT3START, //# Start Lando leaning on wall @@ -749,10 +751,14 @@ typedef enum //# animNumber_e BOTH_SILENCEGESTURE1, //# Luke silencing Kyle with a raised hand (cin #37) BOTH_REACHFORSABER1, //# Luke holding hand out for Kyle's saber (cin #37) BOTH_PUNCHER1, //# Jan punching Kyle in the shoulder (cin #37) - BOTH_CONSTRAINER1STAND, //# Tavion constraining Jan in a stand pose (cin #9) - BOTH_CONSTRAINEE1STAND, //# Jan being constrained in a stand pose (cin #9) - BOTH_CONSTRAINER1WALK, //# Tavion constraining Jan in a walking loop (cin #9) - BOTH_CONSTRAINEE1WALK, //# Jan being constrained in a walking loop (cin #9) + BOTH_CONSTRAINER1HOLD, //# Static pose of starting Tavion constraining Jan (cin #9) + BOTH_CONSTRAINEE1HOLD, //# Static pose of starting Jan being constrained by Tavion (cin #9) + BOTH_CONSTRAINER1STAND, //# Tavion constraining Jan in a stand pose (cin #9) + BOTH_CONSTRAINEE1STAND, //# Jan being constrained in a stand pose (cin #9) + BOTH_CONSTRAINER1WALK, //# Tavion shoving jan forward (cin #9) + BOTH_CONSTRAINEE1WALK, //# Jan being shoved forward by Tavion (cin #9) + BOTH_CONSTRAINER1LOOP, //# Tavion walking with Jan in a loop (cin #9) + BOTH_CONSTRAINEE1LOOP, //# Jan walking with Tavion in a loop (cin #9) BOTH_SABERKILLER1, //# Tavion about to strike Jan with saber (cin #9) BOTH_SABERKILLEE1, //# Jan about to be struck by Tavion with saber (cin #9) BOTH_HANDSHAKER1START, //# Luke shaking Kyle's hand (cin #37) @@ -792,6 +798,7 @@ typedef enum //# animNumber_e BOTH_THREATEN1, //# Kyle threatening Bartender with lightsaber (cin #16) BOTH_RADIO_ONOFF, //# Mech Galak turning on his suit radio (cin #32) BOTH_TRIUMPHANT1START, //# Mech Galak raising his arms in victory (cin #32) + BOTH_TRIUMPHANT1STARTGESTURE, //# Mech Galak raising his arms in victory (cin #32) BOTH_TRIUMPHANT1STOP, //# Mech Galak lowering his arms in victory (cin #32) BOTH_SABERTHROW1START, //# Desann throwing his light saber (cin #26) @@ -815,6 +822,8 @@ typedef enum //# animNumber_e BOTH_SIT2TO3, //# Trans from sit2 to sit3? BOTH_SIT2TOSTAND5, //# Transition from sit 2 to stand 5 + BOTH_STAND5TOSIT2, //# Transition from stand 5 to sit 2 + BOTH_SIT2TOSIT4, //# Trans from sit2 to sit4 (cin #12) Luke leaning back from lotus position. BOTH_SIT3TO1, //# Trans from sit3 to sit1? BOTH_SIT3TO2, //# Trans from sit3 to sit2? BOTH_SIT3TOSTAND5, //# transition from sit 3 to stand 5 @@ -837,21 +846,20 @@ typedef enum //# animNumber_e BOTH_CROUCH2TOSTAND1, //# going from crouch2 to stand1 BOTH_CROUCH3, //# Desann crouching down to Kyle (cin 9) BOTH_UNCROUCH3, //# Desann uncrouching down to Kyle (cin 9) + BOTH_CROUCH4, //# Slower version of crouch1 for cinematics + BOTH_UNCROUCH4, //# Slower version of uncrouch1 for cinematics BOTH_GET_UP1, //# Get up from the ground, face down BOTH_GET_UP2, //# Get up from the ground, face up - BOTH_COCKPIT_CONSOLE1, //# work console1 while sitting in a cockpit. - BOTH_COCKPIT_CONSOLE2, //# work console2 while sitting in a cockpit. BOTH_COCKPIT_SIT, //# sit in a cockpit. BOTH_GUNSIT1, //# sitting on an emplaced gun. + BOTH_DEATH14_UNGRIP, //# Desann's end death (cin #35) + BOTH_DEATH14_SITUP, //# Tavion sitting up after having been thrown (cin #23) BOTH_KNEES1, //# Tavion on her knees BOTH_KNEES2, //# Tavion on her knees looking down BOTH_KNEES2TO1, //# Transition of KNEES2 to KNEES1 - BOTH_STRUGGLE1START, //# Kyle struggling under crate - BOTH_STRUGGLE1, //# Kyle struggling under crate - BOTH_STRUGGLE1STOP, //# Kyle struggling under crate BOTH_RUMMAGE1START, //# Kyle rummaging for crystal (cin 2) BOTH_RUMMAGE1, //# Kyle rummaging for crystal (cin 2) BOTH_RUMMAGE1STOP, //# Kyle rummaging for crystal (cin 2) @@ -879,10 +887,12 @@ typedef enum //# animNumber_e BOTH_RUNSTRAFE_RIGHT1, //# Sidestep right, should loop BOTH_TURN_LEFT1, //# Turn left, should loop BOTH_TURN_RIGHT1, //# Turn right, should loop + BOTH_TURNSTAND1, //# Turn from STAND1 position BOTH_TURNSTAND2, //# Turn from STAND2 position BOTH_TURNSTAND3, //# Turn from STAND3 position BOTH_TURNSTAND4, //# Turn from STAND4 position BOTH_TURNSTAND5, //# Turn from STAND5 position + BOTH_TURNCROUCH1, //# Turn from CROUCH1 position BOTH_RUNAWAY1, //# Running scared BOTH_SWIM1, //# Swimming @@ -953,10 +963,16 @@ typedef enum //# animNumber_e BOTH_DIVE1, //# Dive! + BOTH_SABERFAST_STANCE, + BOTH_SABERSLOW_STANCE, + BOTH_ENGAGETAUNT, BOTH_A2_STABBACK1, //# Stab saber backward BOTH_ATTACK_BACK, //# Swing around backwards and attack - BOTH_FJSS_TR_BL, //# jump spin slash tr to bl - BOTH_FJSS_TL_BR, //# jump spin slash bl to tr + BOTH_JUMPFLIPSLASHDOWN1,//# + BOTH_JUMPFLIPSTABDOWN,//# + BOTH_FORCELEAP2_T__B_,//# + BOTH_LUNGE2_B__T_,//# + BOTH_CROUCHATTACKBACK1,//# BOTH_ARIAL_LEFT, //# BOTH_ARIAL_RIGHT, //# BOTH_CARTWHEEL_LEFT, //# @@ -1003,18 +1019,20 @@ typedef enum //# animNumber_e BOTH_CEILING_DROP, //# dropping from ceiling cling //TESTING + BOTH_FJSS_TR_BL, //# jump spin slash tr to bl + BOTH_FJSS_TL_BR, //# jump spin slash bl to tr BOTH_DEATHFROMBACKSLASH,//# - BOTH_DEFLECTSLASH__R__L_FIN,//# BOTH_RIGHTHANDCHOPPEDOFF,//# - BOTH_JUMPFLIPSLASHDOWN1,//# - BOTH_JUMPFLIPSTABDOWN,//# - BOTH_FORCELEAP2_T__B_,//# - BOTH_LUNGE2_B__T_,//# + BOTH_DEFLECTSLASH__R__L_FIN,//# BOTH_BASHED1,//# BOTH_ARIAL_F1,//# BOTH_BUTTERFLY_FR1,//# BOTH_BUTTERFLY_FL1,//# - BOTH_CROUCHATTACKBACK1,//# + BOTH_POSE1,//# + BOTH_POSE2,//# + BOTH_POSE3,//# + BOTH_POSE4,//# + BOTH_POSE5,//# //# #sep BOTH_ MISC MOVEMENT BOTH_HIT1, //# Kyle hit by crate in cin #9 @@ -1075,6 +1093,7 @@ typedef enum //# animNumber_e BOTH_INJURED6POINT, //# Chang points to door while in injured state BOTH_INJUREDTOSTAND1, //# Runinjured to stand1 + BOTH_PROPUP1, //# Kyle getting up from having been knocked down (cin #9 end) BOTH_CRAWLBACK1, //# Lying on back, crawling backwards with elbows BOTH_SITWALL1, //# Sitting against a wall BOTH_SLEEP1, //# laying on back-rknee up-rhand on torso @@ -1128,6 +1147,7 @@ typedef enum //# animNumber_e BOTH_MINDTRICK1, //# Use off-hand to do mind trick BOTH_MINDTRICK2, //# Use off-hand to do distraction BOTH_FORCELIGHTNING, //# Use off-hand to do lightning + BOTH_FORCELIGHTNING_START, //# Use off-hand to do lightning - start BOTH_FORCELIGHTNING_HOLD, //# Use off-hand to do lightning - hold BOTH_FORCELIGHTNING_RELEASE,//# Use off-hand to do lightning - release BOTH_FORCEHEAL_START, //# Healing meditation pose start @@ -1135,13 +1155,49 @@ typedef enum //# animNumber_e BOTH_FORCEHEAL_QUICK, //# Healing meditation gesture BOTH_SABERPULL, //# Use off-hand to do force power. BOTH_FORCEGRIP1, //# force-gripping (no anim?) - BOTH_FORCEGRIP2, //# force-gripping (?) BOTH_FORCEGRIP3, //# force-gripping (right hand) BOTH_FORCEGRIP_HOLD, //# Use off-hand to do grip - hold BOTH_FORCEGRIP_RELEASE,//# Use off-hand to do grip - release BOTH_TOSS1, //# throwing to left after force gripping BOTH_TOSS2, //# throwing to right after force gripping + BOTH_COCKPIT_TALKR1START, //# turn head from straight forward to looking full right + BOTH_COCKPIT_TALKR1STARTTOMID, //# from TALKR1START to looking at hologram (cin #1) + BOTH_COCKPIT_TALKR1MIDTOSTART, //# from looking at hologram to TALKR1START (cin #1) + BOTH_COCKPIT_TALKR1STOP, //# return head to straight forward from BOTH_COCKPIT_TALKR1 + BOTH_COCKPIT_TALKR1STOPTOMID, //# from TALKR1STOP to TALKR1MID + BOTH_COCKPIT_TALKR1MIDTOSTOP, //# from looking at hologram to TALKR1STOP (cin #1) + BOTH_COCKPIT_TALKR1, //# talk to right side + + BOTH_COCKPIT_TALKL1START, //# turn head from straight forward to looking full left + BOTH_COCKPIT_TALKL1STARTTOMID, //# from TALKL1START to looking at hologram (cin #1) + BOTH_COCKPIT_TALKL1MIDTOSTART, //# from looking at hologram to TALKL1START (cin #1) + BOTH_COCKPIT_TALKL1STOP, //# return head to straight forward from BOTH_COCKPIT_TALKL1 + BOTH_COCKPIT_TALKL1STOPTOMID, //# from TALKL1STOP to TALKL1MID + BOTH_COCKPIT_TALKL1MIDTOSTOP, //# from looking at hologram to TALKL1STOP (cin #1) + BOTH_COCKPIT_TALKL1, //# talk to left side + + BOTH_COCKPIT_CONSOLE1, //# type at controls + BOTH_COCKPIT_CONSOLE2, //# type at controls + BOTH_COCKPIT_CONSOLE2_PARTIAL, //# last part of console2 anim (cin #1) used by Jan + + BOTH_COCKPIT_HEADNOD, //# nod head yes while sitting + BOTH_COCKPIT_HEADSHAKE, //# shake head no while sitting + + BOTH_COCKPIT_HEADTILTLSTART, //# start tilt head left while sitting + BOTH_COCKPIT_HEADTILTLSTOP, //# stop tilt head left while sitting + BOTH_COCKPIT_HEADTILTRSTART, //# start tilt head right while sitting + BOTH_COCKPIT_HEADTILTRSTOP, //# stop tilt head right while sitting + + BOTH_COCKPIT_TALKGESTURE7START, //# + BOTH_COCKPIT_TALKGESTURE7STOP, //# + BOTH_COCKPIT_TALKGESTURE8, //# + BOTH_COCKPIT_TALKGESTURE11START, //# + BOTH_COCKPIT_TALKGESTURE11STOP, //# + + BOTH_COCKPIT_SLEEP6START, //# + BOTH_COCKPIT_SLEEP6STOP, //# + //================================================= //ANIMS IN WHICH ONLY THE UPPER OBJECTS ARE IN MD3 //================================================= @@ -1183,11 +1239,9 @@ typedef enum //# animNumber_e //# #sep TORSO_ MISC TORSO_TALKR1START, //# begin turning head for BOTH_TORSO_TALKR - TORSO_TALKR1HOLD, //# non-looping version of talk to right side TORSO_TALKR1STOP, //# return head to straight forward from BOTH_TORSO_TALKL TORSO_TALKR1, //# talk to right side TORSO_TALKL1START, //# begin turning head for BOTH_TORSO_TALKL - TORSO_TALKL1HOLD, //# non-looping version of talk to left side TORSO_TALKL1STOP, //# return head to straight forward from BOTH_TORSO_TALKL TORSO_TALKL1, //# talk to left side TORSO_LOOKL1, //# looking left @@ -1247,7 +1301,46 @@ typedef enum //# animNumber_e LEGS_RIGHTUP3, //# On a slope with RIGHT foot 12 higher than left LEGS_RIGHTUP4, //# On a slope with RIGHT foot 16 higher than left LEGS_RIGHTUP5, //# On a slope with RIGHT foot 20 higher than left - + LEGS_S1_LUP1, + LEGS_S1_LUP2, + LEGS_S1_LUP3, + LEGS_S1_LUP4, + LEGS_S1_LUP5, + LEGS_S1_RUP1, + LEGS_S1_RUP2, + LEGS_S1_RUP3, + LEGS_S1_RUP4, + LEGS_S1_RUP5, + LEGS_S3_LUP1, + LEGS_S3_LUP2, + LEGS_S3_LUP3, + LEGS_S3_LUP4, + LEGS_S3_LUP5, + LEGS_S3_RUP1, + LEGS_S3_RUP2, + LEGS_S3_RUP3, + LEGS_S3_RUP4, + LEGS_S3_RUP5, + LEGS_S4_LUP1, + LEGS_S4_LUP2, + LEGS_S4_LUP3, + LEGS_S4_LUP4, + LEGS_S4_LUP5, + LEGS_S4_RUP1, + LEGS_S4_RUP2, + LEGS_S4_RUP3, + LEGS_S4_RUP4, + LEGS_S4_RUP5, + LEGS_S5_LUP1, + LEGS_S5_LUP2, + LEGS_S5_LUP3, + LEGS_S5_LUP4, + LEGS_S5_LUP5, + LEGS_S5_RUP1, + LEGS_S5_RUP2, + LEGS_S5_RUP3, + LEGS_S5_RUP4, + LEGS_S5_RUP5, //================================================= //HEAD ANIMS //================================================= diff --git a/code/game/b_local.h b/code/game/b_local.h index 9c091b9..df29e9a 100644 --- a/code/game/b_local.h +++ b/code/game/b_local.h @@ -206,7 +206,7 @@ extern void CalcMuzzlePoint ( gentity_t *const ent, vec3_t forward, vec3_t right //g_combat extern void ExplodeDeath( gentity_t *self ); -extern void ExplodeDeath_Wait( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath,int hitLoc ); +extern void ExplodeDeath_Wait( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath,int dFlags,int hitLoc ); extern void GoExplodeDeath( gentity_t *self, gentity_t *other, gentity_t *activator); extern float IdealDistance ( gentity_t *self ); diff --git a/code/game/b_public.h b/code/game/b_public.h index a1feab9..57e2eb2 100644 --- a/code/game/b_public.h +++ b/code/game/b_public.h @@ -26,7 +26,7 @@ //Script flags #define SCF_CROUCHED 0x00000001 //Force ucmd.upmove to be -127 #define SCF_WALKING 0x00000002 //Force BUTTON_WALKING to be pressed - +#define SCF_MORELIGHT 0x00000004 //NPC will have a minlight of 96 #define SCF_LEAN_RIGHT 0x00000008 //Force rightmove+BUTTON_USE #define SCF_LEAN_LEFT 0x00000010 //Force leftmove+BUTTON_USE #define SCF_RUNNING 0x00000020 //Takes off walking button, overrides SCF_WALKING @@ -45,6 +45,9 @@ #define SCF_FIRE_WEAPON 0x00040000 //NPC will fire his (her) weapon #define SCF_NO_MIND_TRICK 0x00080000 //Not succeptible to mind tricks #define SCF_USE_CP_NEAREST 0x00100000 //Will use combat point close to it, not next to player or try and flank player +#define SCF_NO_FORCE 0x00200000 //Not succeptible to force powers +#define SCF_NO_FALLTODEATH 0x00400000 //NPC will not scream and tumble and fall to hit death over large drops +#define SCF_NO_ACROBATICS 0x00800000 //Jedi won't jump, roll or cartwheel //#ifdef __DEBUG @@ -240,10 +243,12 @@ typedef struct //FIXME: These may be redundant - int pauseTime; //Time to stand still + /* int weaponTime; //Time until refire is valid - int standTime; int jumpTime; + */ + int pauseTime; //Time to stand still + int standTime; int localState; //Tracking information local to entity int squadState; //Tracking information for team level interaction diff --git a/code/game/bg_misc.cpp b/code/game/bg_misc.cpp index 0d0a844..569aae0 100644 --- a/code/game/bg_misc.cpp +++ b/code/game/bg_misc.cpp @@ -257,11 +257,11 @@ FindItem =============== */ -gitem_t *FindItem( const char *pickupName ) { +gitem_t *FindItem( const char *className ) { int i; for ( i = 1 ; i < bg_numItems ; i++ ) { - if ( !Q_stricmp( bg_itemlist[i].pickup_name, pickupName ) ) + if ( !Q_stricmp( bg_itemlist[i].classname, className ) ) return &bg_itemlist[i]; } diff --git a/code/game/bg_pangles.cpp b/code/game/bg_pangles.cpp index fe53aaf..59ece6a 100644 --- a/code/game/bg_pangles.cpp +++ b/code/game/bg_pangles.cpp @@ -104,8 +104,8 @@ qboolean PM_AdjustAngleForWallRun( gentity_t *ent, usercmd_t *ucmd, qboolean doM } VectorMA( ent->currentOrigin, dist, rt, traceTo ); gi.trace( &trace, ent->currentOrigin, mins, maxs, traceTo, ent->s.number, ent->clipmask ); - if ( trace.fraction < 1.0f ) - {//still a wall there + if ( trace.fraction < 1.0f && trace.plane.normal[2] == 0.0f ) + {//still a vertical wall there //FIXME: don't pull around 90 turns //FIXME: simulate stepping up steps here, somehow? if ( ent->client->ps.legsAnim == BOTH_WALL_RUN_RIGHT ) @@ -146,8 +146,9 @@ qboolean PM_AdjustAngleForWallRun( gentity_t *ent, usercmd_t *ucmd, qboolean doM VectorScale( fwd, speed, ent->client->ps.velocity ); } ent->client->ps.velocity[2] = zVel;//preserve z velocity + VectorMA( ent->client->ps.velocity, -128, trace.plane.normal, ent->client->ps.velocity ); //pull me toward the wall, too - VectorMA( ent->client->ps.velocity, dist, rt, ent->client->ps.velocity ); + //VectorMA( ent->client->ps.velocity, dist, rt, ent->client->ps.velocity ); } ucmd->forwardmove = 0; return qtrue; @@ -194,7 +195,8 @@ qboolean PM_AdjustAnglesForSpinningFlip( gentity_t *ent, usercmd_t *ucmd, qboole { if ( !ent->s.number ) { - cg.overrides.thirdPersonVertOffset = cg_thirdPersonVertOffset.value; + cg.overrides.active &= ~CG_OVERRIDE_3RD_PERSON_VOF; + cg.overrides.thirdPersonVertOffset = 0; } } return qfalse; @@ -241,6 +243,7 @@ qboolean PM_AdjustAnglesForSpinningFlip( gentity_t *ent, usercmd_t *ucmd, qboole {//ending anim viewDip = ((animLength-elapsedTime)/animLength)*-120.0f; } + cg.overrides.active |= CG_OVERRIDE_3RD_PERSON_VOF; cg.overrides.thirdPersonVertOffset = cg_thirdPersonVertOffset.value+viewDip; //pm->ps->viewheight = standheight + viewDip; } @@ -361,8 +364,8 @@ void PM_UpdateViewAngles( playerState_t *ps, usercmd_t *cmd, gentity_t *gent ) } else { - pitchMin = -gent->client->renderInfo.headPitchRangeUp; - pitchMax = gent->client->renderInfo.headPitchRangeDown; + pitchMin = -gent->client->renderInfo.headPitchRangeUp-gent->client->renderInfo.torsoPitchRangeUp; + pitchMax = gent->client->renderInfo.headPitchRangeDown+gent->client->renderInfo.torsoPitchRangeDown; } } @@ -399,12 +402,12 @@ void PM_UpdateViewAngles( playerState_t *ps, usercmd_t *cmd, gentity_t *gent ) { if ( temp > pitchClampMax ) { - ps->delta_angles[i] = pitchClampMax - cmd->angles[i]; + ps->delta_angles[i] = (pitchClampMax - cmd->angles[i]) & 0xffff; temp = pitchClampMax; } else if ( temp < pitchClampMin ) { - ps->delta_angles[i] = pitchClampMin - cmd->angles[i]; + ps->delta_angles[i] = (pitchClampMin - cmd->angles[i]) & 0xffff; temp = pitchClampMin; } } @@ -425,7 +428,7 @@ void PM_UpdateViewAngles( playerState_t *ps, usercmd_t *cmd, gentity_t *gent ) } } - if ( (cmd->buttons & BUTTON_USE) && cmd->rightmove != 0 && !cmd->forwardmove && cmd->upmove <= 0 ) + if ( (!cg.renderingThirdPerson||cg.zoomMode) && (cmd->buttons & BUTTON_USE) && cmd->rightmove != 0 && !cmd->forwardmove && cmd->upmove <= 0 ) {//Only lean if holding use button, strafing and not moving forward or back and not jumping if ( gent ) { @@ -434,11 +437,13 @@ void PM_UpdateViewAngles( playerState_t *ps, usercmd_t *cmd, gentity_t *gent ) if ( cmd->rightmove > 0 ) { + /* if( pm->ps->legsAnim != LEGS_LEAN_RIGHT1) { PM_SetAnim(pm, SETANIM_LEGS, LEGS_LEAN_RIGHT1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); } pm->ps->legsAnimTimer = 500;//Force it to hold the anim for at least half a sec + */ if ( ps->leanofs <= 28 ) { @@ -451,11 +456,13 @@ void PM_UpdateViewAngles( playerState_t *ps, usercmd_t *cmd, gentity_t *gent ) } else { + /* if ( pm->ps->legsAnim != LEGS_LEAN_LEFT1 ) { PM_SetAnim(pm, SETANIM_LEGS, LEGS_LEAN_LEFT1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); } pm->ps->legsAnimTimer = 500;//Force it to hold the anim for at least half a sec + */ if ( ps->leanofs >= -28 ) { diff --git a/code/game/bg_panimate.cpp b/code/game/bg_panimate.cpp index 3174ac1..412f389 100644 --- a/code/game/bg_panimate.cpp +++ b/code/game/bg_panimate.cpp @@ -37,6 +37,9 @@ extern qboolean PM_InSlopeAnim( int anim ); extern qboolean PM_ForceAnim( int anim ); extern qboolean PM_InKnockDownOnGround( playerState_t *ps ); extern qboolean PM_InSpecialJump( int anim ); +extern qboolean PM_RunningAnim( int anim ); +extern qboolean PM_WalkingAnim( int anim ); +extern qboolean PM_SwimmingAnim( int anim ); int PM_AnimLength( int index, animNumber_t anim ); // Okay, here lies the much-dreaded Pat-created FSM movement chart... Heretic II strikes again! @@ -486,7 +489,7 @@ qboolean PM_InAnimForSaberMove( int anim, int saberMove ) } int animLevel = PM_AnimLevelForSaberAnim( anim ); if ( animLevel <= 0 ) - { + {//NOTE: this will always return false for the ready poses and putaway/draw... return qfalse; } //drop the anim to the first level and start the checks there @@ -849,7 +852,84 @@ saberMoveName_t PM_AttackMoveForQuad( int quad ) return LS_NONE; } -qboolean PM_SaberKataDone( void ) +int saberMoveTransitionAngle[Q_NUM_QUADS][Q_NUM_QUADS] = +{ + 0,//Q_BR,Q_BR, + 45,//Q_BR,Q_R, + 90,//Q_BR,Q_TR, + 135,//Q_BR,Q_T, + 180,//Q_BR,Q_TL, + 215,//Q_BR,Q_L, + 270,//Q_BR,Q_BL, + 45,//Q_BR,Q_B, + 45,//Q_R,Q_BR, + 0,//Q_R,Q_R, + 45,//Q_R,Q_TR, + 90,//Q_R,Q_T, + 135,//Q_R,Q_TL, + 180,//Q_R,Q_L, + 215,//Q_R,Q_BL, + 90,//Q_R,Q_B, + 90,//Q_TR,Q_BR, + 45,//Q_TR,Q_R, + 0,//Q_TR,Q_TR, + 45,//Q_TR,Q_T, + 90,//Q_TR,Q_TL, + 135,//Q_TR,Q_L, + 180,//Q_TR,Q_BL, + 135,//Q_TR,Q_B, + 135,//Q_T,Q_BR, + 90,//Q_T,Q_R, + 45,//Q_T,Q_TR, + 0,//Q_T,Q_T, + 45,//Q_T,Q_TL, + 90,//Q_T,Q_L, + 135,//Q_T,Q_BL, + 180,//Q_T,Q_B, + 180,//Q_TL,Q_BR, + 135,//Q_TL,Q_R, + 90,//Q_TL,Q_TR, + 45,//Q_TL,Q_T, + 0,//Q_TL,Q_TL, + 45,//Q_TL,Q_L, + 90,//Q_TL,Q_BL, + 135,//Q_TL,Q_B, + 215,//Q_L,Q_BR, + 180,//Q_L,Q_R, + 135,//Q_L,Q_TR, + 90,//Q_L,Q_T, + 45,//Q_L,Q_TL, + 0,//Q_L,Q_L, + 45,//Q_L,Q_BL, + 90,//Q_L,Q_B, + 270,//Q_BL,Q_BR, + 215,//Q_BL,Q_R, + 180,//Q_BL,Q_TR, + 135,//Q_BL,Q_T, + 90,//Q_BL,Q_TL, + 45,//Q_BL,Q_L, + 0,//Q_BL,Q_BL, + 45,//Q_BL,Q_B, + 45,//Q_B,Q_BR, + 90,//Q_B,Q_R, + 135,//Q_B,Q_TR, + 180,//Q_B,Q_T, + 135,//Q_B,Q_TL, + 90,//Q_B,Q_L, + 45,//Q_B,Q_BL, + 0//Q_B,Q_B, +}; + +int PM_SaberAttackChainAngle( int move1, int move2 ) +{ + if ( move1 == -1 || move2 == -1 ) + { + return -1; + } + return saberMoveTransitionAngle[saberMoveData[move1].endQuad][saberMoveData[move2].startQuad]; +} + +qboolean PM_SaberKataDone( int curmove = LS_NONE, int newmove = LS_NONE ) { /* if ( pm->gent && pm->gent->client ) @@ -869,10 +949,48 @@ qboolean PM_SaberKataDone( void ) // not you can chain? Like if you were completely missed, you can't chain as much, or...? // And/Or based on FP_SABER_OFFENSE level? So number of attacks you can chain // increases with your FP_SABER_OFFENSE skill? - if ( (pm->ps->saberAnimLevel >= FORCE_LEVEL_3 && pm->ps->saberAttackChainCount > Q_irand( 0, 1 )) || - ( pm->ps->saberAnimLevel == FORCE_LEVEL_2 && pm->ps->saberAttackChainCount > Q_irand( 2, 5 ) ) ) + if ( pm->ps->saberAnimLevel == FORCE_LEVEL_3 ) { - return qtrue; + if ( curmove == LS_NONE || newmove == LS_NONE ) + { + if ( pm->ps->saberAnimLevel >= FORCE_LEVEL_3 && pm->ps->saberAttackChainCount > Q_irand( 0, 1 ) ) + { + return qtrue; + } + } + else if ( pm->ps->saberAttackChainCount > Q_irand( 2, 3 ) ) + { + return qtrue; + } + else if ( pm->ps->saberAttackChainCount > 0 ) + { + int chainAngle = PM_SaberAttackChainAngle( curmove, newmove ); + if ( chainAngle < 135 || chainAngle > 215 ) + {//if trying to chain to a move that doesn't continue the momentum + return qtrue; + } + else if ( chainAngle == 180 ) + {//continues the momentum perfectly, allow it to chain 66% of the time + if ( pm->ps->saberAttackChainCount > 1 ) + { + return qtrue; + } + } + else + {//would continue the movement somewhat, 50% chance of continuing + if ( pm->ps->saberAttackChainCount > 2 ) + { + return qtrue; + } + } + } + } + else + {//FIXME: have chainAngle influence fast and medium chains as well? + if ( pm->ps->saberAnimLevel == FORCE_LEVEL_2 && pm->ps->saberAttackChainCount > Q_irand( 2, 5 ) ) + { + return qtrue; + } } return qfalse; } @@ -1295,7 +1413,7 @@ int PM_SaberAttackForMovement( int forwardmove, int rightmove, int move ) if ( (pm->ps->saberAnimLevel == FORCE_LEVEL_1 || (pm->gent&&pm->gent->client&&pm->gent->client->NPC_class == CLASS_DESANN && !Q_irand( 0, 2 ))) //&& !PM_InKnockDown( pm->ps ) && (pm->cmd.upmove < 0 || pm->ps->pm_flags&PMF_DUCKED) - && (pm->ps->legsAnim == BOTH_STAND2||level.time-pm->ps->lastStationary<=500) ) + && (pm->ps->legsAnim == BOTH_STAND2||pm->ps->legsAnim == BOTH_SABERFAST_STANCE||pm->ps->legsAnim == BOTH_SABERSLOW_STANCE||level.time-pm->ps->lastStationary<=500) ) {//not moving (or just started), ducked and using fast attacks if ( !pm->ps->clientNum || ( pm->gent && pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG ) ) @@ -1311,7 +1429,7 @@ int PM_SaberAttackForMovement( int forwardmove, int rightmove, int move ) && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 //can force jump && pm->gent && !(pm->gent->flags&FL_LOCK_PLAYER_WEAPONS) // yes this locked weapons check also includes force powers, if we need a separate check later I'll make one && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=500) //on ground or just jumped (if not player) - //&& (pm->ps->legsAnim == BOTH_STAND2||level.time-pm->ps->lastStationary<=500)//standing or just started moving + //&& (pm->ps->legsAnim == BOTH_STAND2||pm->ps->legsAnim == BOTH_SABERFAST_STANCE||pm->ps->legsAnim == BOTH_SABERSLOW_STANCE||level.time-pm->ps->lastStationary<=500)//standing or just started moving && (pm->cmd.upmove||pm->ps->pm_flags&PMF_JUMPING))//jumping {//strong attack: jump-hack if ( //!pm->ps->clientNum || @@ -1406,46 +1524,50 @@ int PM_SaberAttackForMovement( int forwardmove, int rightmove, int move ) } else if ( PM_SaberInBounce( move ) ) {//bounces should go to their default attack if you don't specify a direction but are attacking - if ( PM_SaberKataDone() ) + int newmove; + if ( pm->ps->clientNum && Q_irand( 0, 3 ) ) + {//use NPC random + newmove = PM_NPCSaberAttackFromQuad( saberMoveData[move].endQuad ); + } + else + {//player uses chain-attack + newmove = saberMoveData[move].chain_attack; + } + if ( PM_SaberKataDone( move, newmove ) ) { return saberMoveData[move].chain_idle; } else { - if ( pm->ps->clientNum && Q_irand( 0, 3 ) ) - {//use NPC random - PM_NPCSaberAttackFromQuad( saberMoveData[move].endQuad ); - } - else - {//player uses chain-attack - return saberMoveData[move].chain_attack; - } + return newmove; } } else if ( PM_SaberInKnockaway( move ) ) {//bounces should go to their default attack if you don't specify a direction but are attacking - if ( PM_SaberKataDone() ) + int newmove; + if ( pm->ps->clientNum && Q_irand( 0, 3 ) ) + {//use NPC random + newmove = PM_NPCSaberAttackFromQuad( saberMoveData[move].endQuad ); + } + else + { + if ( pm->ps->saberAnimLevel == FORCE_LEVEL_1 || + pm->ps->saberAnimLevel == FORCE_LEVEL_5 ) + {//player is in fast attacks, so come right back down from the same spot + newmove = PM_AttackMoveForQuad( saberMoveData[move].endQuad ); + } + else + {//use a transition to wrap to another attack from a different dir + newmove = saberMoveData[move].chain_attack; + } + } + if ( PM_SaberKataDone( move, newmove ) ) { return saberMoveData[move].chain_idle; } else { - if ( pm->ps->clientNum && Q_irand( 0, 3 ) ) - {//use NPC random - PM_NPCSaberAttackFromQuad( saberMoveData[move].endQuad ); - } - else - { - if ( pm->ps->saberAnimLevel == FORCE_LEVEL_1 || - pm->ps->saberAnimLevel == FORCE_LEVEL_5 ) - {//player is in fast attacks, so come right back down from the same spot - return PM_AttackMoveForQuad( saberMoveData[move].endQuad ); - } - else - {//use a transition to wrap to another attack from a different dir - return saberMoveData[move].chain_attack; - } - } + return newmove; } } else if ( move == LS_READY || move == LS_A_FLIP_STAB || move == LS_A_FLIP_SLASH ) @@ -1524,7 +1646,7 @@ int PM_SaberAnimTransitionAnim( int curmove, int newmove ) //going into another attack... //allow endless chaining in level 1 attacks, several in level 2 and only one or a few in level 3 //FIXME: don't let strong attacks chain to an attack in the opposite direction ( > 45 degrees?) - if ( PM_SaberKataDone() ) + if ( PM_SaberKataDone( curmove, newmove ) ) {//done with this kata, must return to ready before attack again retmove = LS_R_TL2BR + (newmove-LS_A_TL2BR); } @@ -1967,7 +2089,10 @@ void PM_SetAnimFinal(int *torsoAnim,int *legsAnim, {//see if we need to tell ghoul2 to play it again because of a animSpeed change int blah; float junk; - gi.G2API_GetBoneAnimIndex( &gent->ghoul2[gent->playerModel], gent->lowerLumbarBone, actualTime, &junk, &blah, &blah, &blah, &oldAnimSpeed, NULL );//gent->upperLumbarBone + if (!gi.G2API_GetBoneAnimIndex( &gent->ghoul2[gent->playerModel], gent->lowerLumbarBone, actualTime, &junk, &blah, &blah, &blah, &oldAnimSpeed, NULL )) + { + animSpeed = oldAnimSpeed; + } } if ( oldAnimSpeed == animSpeed ) @@ -2184,7 +2309,10 @@ setAnimLegs: {//see if we need to tell ghoul2 to play it again because of a animSpeed change int blah; float junk; - gi.G2API_GetBoneAnimIndex( &gent->ghoul2[gent->playerModel], gent->rootBone, actualTime, &junk, &blah, &blah, &blah, &oldAnimSpeed, NULL ); + if (!gi.G2API_GetBoneAnimIndex( &gent->ghoul2[gent->playerModel], gent->rootBone, actualTime, &junk, &blah, &blah, &blah, &oldAnimSpeed, NULL )) + { + animSpeed = oldAnimSpeed; + } } if ( oldAnimSpeed == animSpeed ) @@ -2505,24 +2633,14 @@ void PM_TorsoAnimLightsaber() PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUARD_IDLE1,SETANIM_FLAG_NORMAL); pm->ps->saberMove = LS_READY; } - else if( pm->ps->legsAnim == BOTH_STAND2_RANDOM1 ) + else if( pm->ps->legsAnim == BOTH_STAND1IDLE1 + || pm->ps->legsAnim == BOTH_STAND2IDLE1 + || pm->ps->legsAnim == BOTH_STAND2IDLE2 + || pm->ps->legsAnim == BOTH_STAND3IDLE1 + || pm->ps->legsAnim == BOTH_STAND4IDLE1 + || pm->ps->legsAnim == BOTH_STAND5IDLE1 ) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND2_RANDOM1,SETANIM_FLAG_NORMAL); - pm->ps->saberMove = LS_READY; - } - else if( pm->ps->legsAnim == BOTH_STAND2_RANDOM2 ) - { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND2_RANDOM2,SETANIM_FLAG_NORMAL); - pm->ps->saberMove = LS_READY; - } - else if( pm->ps->legsAnim == BOTH_STAND2_RANDOM3 ) - { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND2_RANDOM3,SETANIM_FLAG_NORMAL); - pm->ps->saberMove = LS_READY; - } - else if( pm->ps->legsAnim == BOTH_STAND2_RANDOM4 ) - { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND2_RANDOM4,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); pm->ps->saberMove = LS_READY; } else if( pm->ps->legsAnim == BOTH_STAND2TO4 ) @@ -2629,6 +2747,11 @@ void PM_TorsoAnimation( void ) return; } + if( pm->gent && pm->gent->NPC && (pm->gent->NPC->scriptFlags & SCF_FORCED_MARCH) ) + { + return; + } + if(pm->gent != NULL && pm->gent->client) { pm->gent->client->renderInfo.torsoFpsMod = 1.0f; @@ -2730,6 +2853,29 @@ void PM_TorsoAnimation( void ) } + qboolean weaponBusy = qfalse; + + if ( pm->ps->weapon == WP_NONE ) + { + weaponBusy = qfalse; + } + else if ( pm->ps->weaponstate == WEAPON_FIRING || pm->ps->weaponstate == WEAPON_CHARGING || pm->ps->weaponstate == WEAPON_CHARGING_ALT ) + { + weaponBusy = qtrue; + } + else if ( pm->ps->lastShotTime > level.time - 3000 ) + { + weaponBusy = qtrue; + } + else if ( pm->ps->weaponTime ) + { + weaponBusy = qtrue; + } + else if ( pm->gent && pm->gent->client->fireDelay > 0 ) + { + weaponBusy = qtrue; + } + if ( pm->ps->weapon == WP_NONE || pm->ps->weaponstate == WEAPON_READY || pm->ps->weaponstate == WEAPON_CHARGING || @@ -2739,19 +2885,19 @@ void PM_TorsoAnimation( void ) { PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_NORMAL);//TORSO_WEAPONREADY1 } - else if( pm->ps->legsAnim == BOTH_RUN1 ) + else if( pm->ps->legsAnim == BOTH_RUN1 && !weaponBusy ) { PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN1,SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_RUN2 && (!pm->gent || pm->gent->attackDebounceTime < level.time - 2000) ) + else if( pm->ps->legsAnim == BOTH_RUN2 && !weaponBusy ) { PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN2,SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_WALK1 ) + else if( pm->ps->legsAnim == BOTH_WALK1 && !weaponBusy ) { PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK1,SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_WALK2 ) + else if( pm->ps->legsAnim == BOTH_WALK2 && !weaponBusy ) { PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK2,SETANIM_FLAG_NORMAL); } @@ -2760,23 +2906,23 @@ void PM_TorsoAnimation( void ) //??? Why nothing? What if you were running??? //PM_SetAnim(pm,SETANIM_TORSO,BOTH_CROUCH1IDLE,SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_JUMP1 ) + else if( pm->ps->legsAnim == BOTH_JUMP1 && !weaponBusy ) { PM_SetAnim(pm,SETANIM_TORSO,BOTH_JUMP1,SETANIM_FLAG_NORMAL, 100); // Only blend over 100ms } - else if( pm->ps->legsAnim == BOTH_SWIM_IDLE1 ) + else if( pm->ps->legsAnim == BOTH_SWIM_IDLE1 && !weaponBusy ) { PM_SetAnim(pm,SETANIM_TORSO,BOTH_SWIM_IDLE1,SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_SWIMFORWARDSTART ) + else if( pm->ps->legsAnim == BOTH_SWIMFORWARDSTART && !weaponBusy ) { PM_SetAnim(pm,SETANIM_TORSO,BOTH_SWIMFORWARDSTART,SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_SWIMFORWARD ) + else if( pm->ps->legsAnim == BOTH_SWIMFORWARD && !weaponBusy ) { PM_SetAnim(pm,SETANIM_TORSO,BOTH_SWIMFORWARD,SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_SWIMFORWARDSTOP ) + else if( pm->ps->legsAnim == BOTH_SWIMFORWARDSTOP && !weaponBusy ) { PM_SetAnim(pm,SETANIM_TORSO,BOTH_SWIMFORWARDSTOP,SETANIM_FLAG_NORMAL); } @@ -2839,20 +2985,52 @@ void PM_TorsoAnimation( void ) // ******************************************************** case WP_BRYAR_PISTOL: + //FIXME: if recently fired, hold the ready! + if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) || PM_WalkingAnim( pm->ps->legsAnim ) || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + break; case WP_BLASTER_PISTOL: - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) || PM_WalkingAnim( pm->ps->legsAnim ) || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } break; case WP_NONE: //NOTE: should never get here break; case WP_MELEE: - if ( pm->gent && pm->gent->client && !PM_DroidMelee( pm->gent->client->NPC_class ) ) - { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND6,SETANIM_FLAG_NORMAL); + if ( PM_RunningAnim( pm->ps->legsAnim ) || PM_WalkingAnim( pm->ps->legsAnim ) || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); } else { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + if ( pm->gent && pm->gent->client && !PM_DroidMelee( pm->gent->client->NPC_class ) ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND6,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + } } break; case WP_BLASTER: @@ -2866,13 +3044,20 @@ void PM_TorsoAnimation( void ) PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); break; case WP_THERMAL: - if ( !pm->ps->clientNum && (pm->ps->weaponstate == WEAPON_CHARGING || pm->ps->weaponstate == WEAPON_CHARGING_ALT) ) - {//player pulling back to throw - PM_SetAnim( pm, SETANIM_TORSO, BOTH_THERMAL_READY, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + if ( PM_RunningAnim( pm->ps->legsAnim ) || PM_WalkingAnim( pm->ps->legsAnim ) || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); } else { - PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY10, SETANIM_FLAG_NORMAL ); + if ( !pm->ps->clientNum && (pm->ps->weaponstate == WEAPON_CHARGING || pm->ps->weaponstate == WEAPON_CHARGING_ALT) ) + {//player pulling back to throw + PM_SetAnim( pm, SETANIM_TORSO, BOTH_THERMAL_READY, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY10, SETANIM_FLAG_NORMAL ); + } } break; case WP_REPEATER: @@ -2892,6 +3077,17 @@ void PM_TorsoAnimation( void ) PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); } break; + case WP_TRIP_MINE: + case WP_DET_PACK: + if ( PM_RunningAnim( pm->ps->legsAnim ) || PM_WalkingAnim( pm->ps->legsAnim ) || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY10, SETANIM_FLAG_NORMAL ); + } + break; default: PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); break; @@ -2909,21 +3105,15 @@ void PM_TorsoAnimation( void ) { PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUARD_IDLE1,SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_STAND2_RANDOM1 ) + else if( pm->ps->legsAnim == BOTH_STAND1IDLE1 + || pm->ps->legsAnim == BOTH_STAND2IDLE1 + || pm->ps->legsAnim == BOTH_STAND2IDLE2 + || pm->ps->legsAnim == BOTH_STAND3IDLE1 + || pm->ps->legsAnim == BOTH_STAND4IDLE1 + || pm->ps->legsAnim == BOTH_STAND5IDLE1 ) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND2_RANDOM1,SETANIM_FLAG_NORMAL); - } - else if( pm->ps->legsAnim == BOTH_STAND2_RANDOM2 ) - { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND2_RANDOM2,SETANIM_FLAG_NORMAL); - } - else if( pm->ps->legsAnim == BOTH_STAND2_RANDOM3 ) - { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND2_RANDOM3,SETANIM_FLAG_NORMAL); - } - else if( pm->ps->legsAnim == BOTH_STAND2_RANDOM4 ) - { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND2_RANDOM4,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; } else if( pm->ps->legsAnim == BOTH_STAND2TO4 ) { @@ -2967,71 +3157,153 @@ void PM_TorsoAnimation( void ) } else { - switch(pm->ps->weapon) + if ( !weaponBusy + && pm->ps->weapon != WP_BOWCASTER + && pm->ps->weapon != WP_REPEATER + && pm->ps->weapon != WP_FLECHETTE + && pm->ps->weapon != WP_ROCKET_LAUNCHER + && ( PM_RunningAnim( pm->ps->legsAnim ) || PM_WalkingAnim( pm->ps->legsAnim ) || PM_SwimmingAnim( pm->ps->legsAnim ) ) ) + {//running w/1-handed or light 2-handed weapon uses full-body anim if you're not using the weapon right now + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else { - // ******************************************************** - case WP_SABER: // WP_LIGHTSABER - // Shouldn't get here, should go to TorsoAnimLightsaber - break; - // ******************************************************** - - case WP_BRYAR_PISTOL: - case WP_BLASTER_PISTOL: - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_NORMAL); - break; - - case WP_NONE: - //NOTE: should never get here - break; - - case WP_MELEE: - if ( pm->gent && pm->gent->client && !PM_DroidMelee( pm->gent->client->NPC_class ) ) + switch ( pm->ps->weapon ) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND6,SETANIM_FLAG_NORMAL); - } - else - { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); - } - break; + // ******************************************************** + case WP_SABER: // WP_LIGHTSABER + // Shouldn't get here, should go to TorsoAnimLightsaber + break; + // ******************************************************** - case WP_BLASTER: - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); - break; - - case WP_DISRUPTOR: - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL);//TORSO_WEAPONIDLE4//SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD - break; - - case WP_BOT_LASER: - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); - break; - - case WP_THERMAL: - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE10,SETANIM_FLAG_NORMAL);//SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD - break; - - case WP_REPEATER: - if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_GALAKMECH ) - {// - if ( pm->gent->alt_fire ) + case WP_BRYAR_PISTOL: + if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy ) { - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) || PM_WalkingAnim( pm->ps->legsAnim ) || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); } else { - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_NORMAL); } - } - else - { - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); - } - break; + break; + case WP_BLASTER_PISTOL: + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) || PM_WalkingAnim( pm->ps->legsAnim ) || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_NORMAL); + } + break; - default: - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); - break; + case WP_NONE: + //NOTE: should never get here + break; + + case WP_MELEE: + if ( PM_RunningAnim( pm->ps->legsAnim ) || PM_WalkingAnim( pm->ps->legsAnim ) || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( pm->gent && pm->gent->client && !PM_DroidMelee( pm->gent->client->NPC_class ) ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND6,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + } + } + break; + + case WP_BLASTER: + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + break; + + case WP_DISRUPTOR: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL);//TORSO_WEAPONIDLE4//SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD + break; + + case WP_BOT_LASER: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + break; + + case WP_THERMAL: + if ( PM_RunningAnim( pm->ps->legsAnim ) || PM_WalkingAnim( pm->ps->legsAnim ) || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE10,SETANIM_FLAG_NORMAL);//SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD + } + break; + + case WP_REPEATER: + if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_GALAKMECH ) + {// + if ( pm->gent->alt_fire ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE1,SETANIM_FLAG_NORMAL); + } + } + else + { + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + } + break; + case WP_TRIP_MINE: + case WP_DET_PACK: + if ( PM_RunningAnim( pm->ps->legsAnim ) || PM_WalkingAnim( pm->ps->legsAnim ) || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONIDLE10, SETANIM_FLAG_NORMAL ); + } + break; + + default: + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + break; + } } } } @@ -3051,16 +3323,18 @@ int PM_GetTurnAnim( gentity_t *gent, int anim ) switch( anim ) { case BOTH_STAND1: //# Standing idle: no weapon: hands down - case BOTH_STAND1_RANDOM1: //# Random standing idle - case BOTH_STAND1_RANDOM2: //# Random standing idle + case BOTH_STAND1IDLE1: //# Random standing idle case BOTH_STAND2: //# Standing idle with a weapon - case BOTH_STAND2_RANDOM1: //# Random standing idle - case BOTH_STAND2_RANDOM2: //# Random standing idle - case BOTH_STAND2_RANDOM3: //# Random standing idle - case BOTH_STAND2_RANDOM4: //# Random standing idle + case BOTH_SABERFAST_STANCE: + case BOTH_SABERSLOW_STANCE: + case BOTH_STAND2IDLE1: //# Random standing idle + case BOTH_STAND2IDLE2: //# Random standing idle case BOTH_STAND3: //# Standing hands behind back: at ease: etc. + case BOTH_STAND3IDLE1: //# Random standing idle case BOTH_STAND4: //# two handed: gun down: relaxed stand + case BOTH_STAND4IDLE1: //# Random standing idle case BOTH_STAND5: //# standing idle, no weapon, hand down, back straight + case BOTH_STAND5IDLE1: //# Random standing idle case BOTH_STAND6: //# one handed: gun at side: relaxed stand case BOTH_STAND1TO3: //# Transition from stand1 to stand3 case BOTH_STAND3TO1: //# Transition from stand3 to stand1 @@ -3110,6 +3384,99 @@ int PM_GetTurnAnim( gentity_t *gent, int anim ) } } +int PM_TurnAnimForLegsAnim( gentity_t *gent, int anim ) +{ + if ( !gent ) + { + return -1; + } + + switch( anim ) + { + case BOTH_STAND1: //# Standing idle: no weapon: hands down + case BOTH_STAND1IDLE1: //# Random standing idle + if ( PM_HasAnimation( gent, BOTH_TURNSTAND1 ) ) + { + return BOTH_TURNSTAND1; + } + else + { + return -1; + } + break; + case BOTH_STAND2: //# Standing idle with a weapon + case BOTH_SABERFAST_STANCE: + case BOTH_SABERSLOW_STANCE: + case BOTH_STAND2IDLE1: //# Random standing idle + case BOTH_STAND2IDLE2: //# Random standing idle + if ( PM_HasAnimation( gent, BOTH_TURNSTAND2 ) ) + { + return BOTH_TURNSTAND2; + } + else + { + return -1; + } + break; + case BOTH_STAND3: //# Standing hands behind back: at ease: etc. + case BOTH_STAND3IDLE1: //# Random standing idle + if ( PM_HasAnimation( gent, BOTH_TURNSTAND3 ) ) + { + return BOTH_TURNSTAND3; + } + else + { + return -1; + } + break; + case BOTH_STAND4: //# two handed: gun down: relaxed stand + case BOTH_STAND4IDLE1: //# Random standing idle + if ( PM_HasAnimation( gent, BOTH_TURNSTAND4 ) ) + { + return BOTH_TURNSTAND4; + } + else + { + return -1; + } + break; + case BOTH_STAND5: //# standing idle, no weapon, hand down, back straight + case BOTH_STAND5IDLE1: //# Random standing idle + if ( PM_HasAnimation( gent, BOTH_TURNSTAND5 ) ) + { + return BOTH_TURNSTAND5; + } + else + { + return -1; + } + break; + case BOTH_CROUCH1: //# Transition from standing to crouch + case BOTH_CROUCH1IDLE: //# Crouching idle + /* + case BOTH_UNCROUCH1: //# Transition from crouch to standing + case BOTH_CROUCH2IDLE: //# crouch and resting on back righ heel: no weapon + case BOTH_CROUCH2TOSTAND1: //# going from crouch2 to stand1 + case BOTH_CROUCH3: //# Desann crouching down to Kyle (cin 9) + case BOTH_UNCROUCH3: //# Desann uncrouching down to Kyle (cin 9) + case BOTH_CROUCH4: //# Slower version of crouch1 for cinematics + case BOTH_UNCROUCH4: //# Slower version of uncrouch1 for cinematics + */ + if ( PM_HasAnimation( gent, BOTH_TURNCROUCH1 ) ) + { + return BOTH_TURNCROUCH1; + } + else + { + return -1; + } + break; + default: + return -1; + break; + } +} + qboolean PM_InOnGroundAnim ( playerState_t *ps ) { switch( ps->legsAnim ) @@ -3311,3 +3678,17 @@ qboolean PM_InCartwheel( int anim ) } return qfalse; } + +qboolean PM_StandingAnim( int anim ) +{//NOTE: does not check idles or special (cinematic) stands + switch ( anim ) + { + case BOTH_STAND1: + case BOTH_STAND2: + case BOTH_STAND3: + case BOTH_STAND4: + return qtrue; + break; + } + return qfalse; +} \ No newline at end of file diff --git a/code/game/bg_pmove.cpp b/code/game/bg_pmove.cpp index 6225fc9..61e821e 100644 --- a/code/game/bg_pmove.cpp +++ b/code/game/bg_pmove.cpp @@ -18,6 +18,7 @@ #include "wp_saber.h" #include +extern qboolean G_DoDismemberment( gentity_t *self, vec3_t point, int mod, int damage, int hitLoc, qboolean force = qfalse ); extern qboolean G_EntIsUnlockedDoor( int entityNum ); extern qboolean G_EntIsDoor( int entityNum ); extern qboolean InFront( vec3_t spot, vec3_t from, vec3_t fromAngles, float threshHold = 0.0f ); @@ -26,7 +27,7 @@ extern qboolean Q3_TaskIDPending( gentity_t *ent, taskID_t taskType ); extern void WP_SaberInitBladeData( gentity_t *ent ); extern void WP_SaberLose( gentity_t *self, vec3_t throwDir ); extern void G_SoundOnEnt( gentity_t *ent, soundChannel_t channel, const char *soundPath ); -extern int Jedi_ReCalcParryTime( gentity_t *self ); +extern int Jedi_ReCalcParryTime( gentity_t *self, evasionType_t evasionType ); extern qboolean PM_HasAnimation( gentity_t *ent, int animation ); extern int PM_SaberAnimTransitionAnim( int curmove, int newmove ); extern saberMoveName_t PM_AttackMoveForQuad( int quad ); @@ -46,7 +47,7 @@ extern qboolean PM_SaberInBrokenParry( int move ); extern qboolean PM_SaberInReflect( int move ); extern qboolean PM_SaberInIdle( int move ); extern qboolean PM_SaberInStart( int move ); -extern qboolean PM_SaberKataDone( void ); +extern qboolean PM_SaberKataDone( int curmove, int newmove ); extern qboolean PM_SaberInSpecial( int move ); extern qboolean PM_InDeathAnim ( void ); extern int PM_SaberFlipOverAttackMove( void ); @@ -480,6 +481,10 @@ qboolean PM_ForceJumpingUp( gentity_t *gent ) return qfalse; } + if ( !gent->s.number && in_camera ) + {//player can't use force powers in cinematic + return qfalse; + } if ( gent->client->ps.groundEntityNum == ENTITYNUM_NONE && //in air gent->client->ps.pm_flags & PMF_JUMPING &&//forceJumpZStart && //jumped gent->client->ps.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0 && //force-jump capable @@ -590,7 +595,10 @@ static qboolean PM_CheckJump( void ) { //start force jump pm->ps->forcePowersActive |= (1<gent, CHAN_BODY, "sound/weapons/force/jump.wav" ); + if ( pm->gent ) + { + G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" ); + } //play flip //FIXME: do this only when they stop the jump (below) or when they're just about to hit the peak of the jump if ((pm->cmd.forwardmove || pm->cmd.rightmove) && //pushing in a dir @@ -917,86 +925,150 @@ static qboolean PM_CheckJump( void ) vec3_t idealNormal; if ( doTrace ) { + //FIXME: all these jump ones should check for head clearance pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents ); VectorSubtract( pm->ps->origin, traceto, idealNormal ); VectorNormalize( idealNormal ); + if ( anim == BOTH_WALL_FLIP_LEFT ) + {//sigh.. check for bottomless pit to the right + trace_t trace2; + vec3_t start; + VectorMA( pm->ps->origin, 128, right, traceto ); + pm->trace( &trace2, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents ); + if ( !trace2.allsolid && !trace2.startsolid ) + { + VectorCopy( trace2.endpos, traceto ); + VectorCopy( traceto, start ); + traceto[2] -= 384; + pm->trace( &trace2, start, mins, maxs, traceto, pm->ps->clientNum, contents ); + if ( !trace2.allsolid && !trace2.startsolid && trace2.fraction >= 1.0f ) + {//bottomless pit! + trace.fraction = 1.0f;//way to stop it from doing the side-flip + } + } + } + else if ( anim == BOTH_WALL_FLIP_RIGHT ) + {//sigh.. check for bottomless pit to the left + trace_t trace2; + vec3_t start; + VectorMA( pm->ps->origin, -128, right, traceto ); + pm->trace( &trace2, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents ); + if ( !trace2.allsolid && !trace2.startsolid ) + { + VectorCopy( trace2.endpos, traceto ); + VectorCopy( traceto, start ); + traceto[2] -= 384; + pm->trace( &trace2, start, mins, maxs, traceto, pm->ps->clientNum, contents ); + if ( !trace2.allsolid && !trace2.startsolid && trace2.fraction >= 1.0f ) + {//bottomless pit! + trace.fraction = 1.0f;//way to stop it from doing the side-flip + } + } + } + else if ( anim == BOTH_WALL_FLIP_BACK1 ) + {//sigh.. check for bottomless pit to the rear + trace_t trace2; + vec3_t start; + VectorMA( pm->ps->origin, -128, fwd, traceto ); + pm->trace( &trace2, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents ); + if ( !trace2.allsolid && !trace2.startsolid ) + { + VectorCopy( trace2.endpos, traceto ); + VectorCopy( traceto, start ); + traceto[2] -= 384; + pm->trace( &trace2, start, mins, maxs, traceto, pm->ps->clientNum, contents ); + if ( !trace2.allsolid && !trace2.startsolid && trace2.fraction >= 1.0f ) + {//bottomless pit! + trace.fraction = 1.0f;//way to stop it from doing the side-flip + } + } + } } gentity_t *traceEnt = &g_entities[trace.entityNum]; if ( !doTrace || (trace.fraction < 1.0f&&((trace.entityNums.solid!=SOLID_BMODEL)||DotProduct(trace.plane.normal,idealNormal)>0.7)) ) {//there is a wall there - //move me to side - if ( anim == BOTH_WALL_FLIP_LEFT ) - { - pm->ps->velocity[0] = pm->ps->velocity[1] = 0; - VectorMA( pm->ps->velocity, 150, right, pm->ps->velocity ); - } - else if ( anim == BOTH_WALL_FLIP_RIGHT ) - { - pm->ps->velocity[0] = pm->ps->velocity[1] = 0; - VectorMA( pm->ps->velocity, -150, right, pm->ps->velocity ); - } - else if ( anim == BOTH_FLIP_BACK1 - || anim == BOTH_FLIP_BACK2 - || anim == BOTH_FLIP_BACK3 - || anim == BOTH_WALL_FLIP_BACK1 ) - { - pm->ps->velocity[0] = pm->ps->velocity[1] = 0; - VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity ); - } - //kick if jumping off an ent - if ( doTrace && anim != BOTH_WALL_RUN_LEFT && anim != BOTH_WALL_RUN_RIGHT ) - { - if ( pm->gent && trace.entityNum < ENTITYNUM_WORLD ) + if ( (anim != BOTH_WALL_RUN_LEFT && anim != BOTH_WALL_RUN_RIGHT) || trace.plane.normal[2] == 0.0f ) + {//wall-runs can only run on perfectly flat walls, sorry. + //move me to side + if ( anim == BOTH_WALL_FLIP_LEFT ) { - if ( traceEnt && traceEnt->client && traceEnt->health > 0 && traceEnt->takedamage ) - {//push them away and do pain - vec3_t oppDir; - float strength = VectorNormalize2( pm->ps->velocity, oppDir ); - VectorScale( oppDir, -1, oppDir ); - //FIXME: need knockdown anim - G_Damage( traceEnt, pm->gent, pm->gent, oppDir, traceEnt->currentOrigin, 10, DAMAGE_NO_ARMOR|DAMAGE_NO_HIT_LOC|DAMAGE_NO_KNOCKBACK, MOD_MELEE ); - NPC_SetAnim( traceEnt, SETANIM_BOTH, BOTH_KNOCKDOWN2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); - G_Throw( traceEnt, oppDir, strength ); - G_Sound( traceEnt, G_SoundIndex( va("sound/weapons/melee/punch%d", Q_irand(1, 4)) ) ); + pm->ps->velocity[0] = pm->ps->velocity[1] = 0; + VectorMA( pm->ps->velocity, 150, right, pm->ps->velocity ); + } + else if ( anim == BOTH_WALL_FLIP_RIGHT ) + { + pm->ps->velocity[0] = pm->ps->velocity[1] = 0; + VectorMA( pm->ps->velocity, -150, right, pm->ps->velocity ); + } + else if ( anim == BOTH_FLIP_BACK1 + || anim == BOTH_FLIP_BACK2 + || anim == BOTH_FLIP_BACK3 + || anim == BOTH_WALL_FLIP_BACK1 ) + { + pm->ps->velocity[0] = pm->ps->velocity[1] = 0; + VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity ); + } + //kick if jumping off an ent + if ( doTrace && anim != BOTH_WALL_RUN_LEFT && anim != BOTH_WALL_RUN_RIGHT ) + { + if ( pm->gent && trace.entityNum < ENTITYNUM_WORLD ) + { + if ( traceEnt && traceEnt->client && traceEnt->health > 0 && traceEnt->takedamage ) + {//push them away and do pain + vec3_t oppDir; + float strength = VectorNormalize2( pm->ps->velocity, oppDir ); + VectorScale( oppDir, -1, oppDir ); + //FIXME: need knockdown anim + G_Damage( traceEnt, pm->gent, pm->gent, oppDir, traceEnt->currentOrigin, 10, DAMAGE_NO_ARMOR|DAMAGE_NO_HIT_LOC|DAMAGE_NO_KNOCKBACK, MOD_MELEE ); + NPC_SetAnim( traceEnt, SETANIM_BOTH, BOTH_KNOCKDOWN2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + G_Throw( traceEnt, oppDir, strength ); + G_Sound( traceEnt, G_SoundIndex( va("sound/weapons/melee/punch%d", Q_irand(1, 4)) ) ); + } } } + //up + if ( vertPush ) + { + pm->ps->velocity[2] = vertPush; + } + //animate me + int parts = SETANIM_LEGS; + if ( anim == BOTH_BUTTERFLY_LEFT || + anim == BOTH_BUTTERFLY_RIGHT || + anim == BOTH_FJSS_TR_BL || + anim == BOTH_FJSS_TL_BR ) + { + parts = SETANIM_BOTH; + pm->cmd.buttons&=~BUTTON_ATTACK; + pm->ps->saberMove = LS_NONE; + pm->gent->client->saberTrail.inAction = qtrue;//FIXME: reset this when done! + pm->gent->client->saberTrail.duration = 300;//FIXME: reset this when done! + } + else if ( !pm->ps->weaponTime ) + { + parts = SETANIM_BOTH; + } + PM_SetAnim( pm, parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 ); + if ( anim == BOTH_BUTTERFLY_LEFT + || anim == BOTH_BUTTERFLY_RIGHT + || anim == BOTH_FJSS_TR_BL + || anim == BOTH_FJSS_TL_BR ) + { + pm->ps->weaponTime = pm->ps->torsoAnimTimer; + } + else if ( anim == BOTH_WALL_FLIP_LEFT + || anim == BOTH_WALL_FLIP_RIGHT + || anim == BOTH_WALL_FLIP_BACK1 ) + {//let us do some more moves after this + pm->ps->saberAttackChainCount = 0; + } + pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height + pm->ps->pm_flags |= (PMF_JUMPING|PMF_SLOW_MO_FALL); + pm->cmd.upmove = 0; + G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" ); + WP_ForcePowerDrain( pm->gent, FP_LEVITATION, 0 ); } - //up - if ( vertPush ) - { - pm->ps->velocity[2] = vertPush; - } - //animate me - int parts = SETANIM_LEGS; - if ( anim == BOTH_BUTTERFLY_LEFT || - anim == BOTH_BUTTERFLY_RIGHT || - anim == BOTH_FJSS_TR_BL || - anim == BOTH_FJSS_TL_BR ) - { - parts = SETANIM_BOTH; - pm->cmd.buttons&=~BUTTON_ATTACK; - pm->ps->saberMove = LS_NONE; - pm->gent->client->saberTrail.inAction = qtrue;//FIXME: reset this when done! - pm->gent->client->saberTrail.duration = 300;//FIXME: reset this when done! - } - else if ( !pm->ps->weaponTime ) - { - parts = SETANIM_BOTH; - } - PM_SetAnim( pm, parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 ); - if ( anim == BOTH_BUTTERFLY_LEFT || - anim == BOTH_BUTTERFLY_RIGHT || - anim == BOTH_FJSS_TR_BL || - anim == BOTH_FJSS_TL_BR ) - { - pm->ps->weaponTime = pm->ps->torsoAnimTimer; - } - pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height - pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL; - pm->cmd.upmove = 0; - G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" ); - WP_ForcePowerDrain( pm->gent, FP_LEVITATION, 0 ); } } } @@ -1188,7 +1260,7 @@ static qboolean PM_CheckJump( void ) if ( pm->cmd.forwardmove > 0 //going forward && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 //can force jump && !(pm->gent->flags&FL_LOCK_PLAYER_WEAPONS) // yes this locked weapons check also includes force powers, if we need a separate check later I'll make one - && (pm->ps->legsAnim == BOTH_STAND2||level.time-pm->ps->lastStationary<=500)//standing or just started moving + && (pm->ps->legsAnim == BOTH_STAND2||pm->ps->legsAnim == BOTH_SABERFAST_STANCE||pm->ps->legsAnim == BOTH_SABERSLOW_STANCE||level.time-pm->ps->lastStationary<=500)//standing or just started moving && (pm->ps->groundEntityNum != ENTITYNUM_NONE||(pm->ps->clientNum&&level.time-pm->ps->lastOnGround<=500)))//on ground or just jumped if non-player {//strong attack: jump-hack if ( !pm->ps->clientNum || @@ -1700,7 +1772,23 @@ static void PM_WalkMove( void ) { { pm->ps->pm_flags &= ~PMF_TIME_KNOCKBACK; } - PM_Friction (); + + qboolean slide = qfalse; + if ( pm->ps->pm_type == PM_DEAD ) + {//corpse + if ( g_entities[pm->ps->groundEntityNum].client ) + {//on a client + if ( g_entities[pm->ps->groundEntityNum].health > 0 ) + {//a living client + //no friction + slide = qtrue; + } + } + } + if ( !slide ) + { + PM_Friction (); + } fmove = pm->cmd.forwardmove; smove = pm->cmd.rightmove; @@ -1743,8 +1831,10 @@ static void PM_WalkMove( void ) { } // clamp the speed lower if ducking - if ( pm->ps->pm_flags & PMF_DUCKED ) { - if ( wishspeed > pm->ps->speed * pm_duckScale ) { + if ( pm->ps->pm_flags & PMF_DUCKED && !PM_InKnockDown( pm->ps ) ) + { + if ( wishspeed > pm->ps->speed * pm_duckScale ) + { wishspeed = pm->ps->speed * pm_duckScale; } } @@ -1779,7 +1869,10 @@ static void PM_WalkMove( void ) { } else { - pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime; + if ( !(pm->ps->eFlags&EF_FORCE_GRIPPED) ) + { + pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime; + } } } else { // don't reset the z velocity for slopes @@ -1857,10 +1950,10 @@ static void PM_NoclipMove( void ) { if(pm->gent && pm->gent->client) { pm->ps->viewheight = pm->gent->client->standheight + STANDARD_VIEWHEIGHT_OFFSET; - if ( !pm->gent->mins[0] || !pm->gent->mins[1] || !pm->gent->mins[2] || !pm->gent->maxs[0] || !pm->gent->maxs[1] || !pm->gent->maxs[2] ) - { - assert(0); - } +// if ( !pm->gent->mins[0] || !pm->gent->mins[1] || !pm->gent->mins[2] || !pm->gent->maxs[0] || !pm->gent->maxs[1] || !pm->gent->maxs[2] ) +// { +// assert(0); +// } VectorCopy( pm->gent->mins, pm->mins ); VectorCopy( pm->gent->maxs, pm->maxs ); @@ -1995,7 +2088,7 @@ static void PM_CrashLandDamage( int damage ) if ( damage ) { pm->gent->painDebounceTime = level.time + 200; // no normal pain sound - G_Damage( pm->gent, NULL, NULL, NULL, NULL, damage, 0, MOD_FALLING ); + G_Damage( pm->gent, NULL, NULL, NULL, NULL, damage, DAMAGE_NO_ARMOR, MOD_FALLING ); } } } @@ -2168,9 +2261,16 @@ static qboolean PM_TryRoll( void ) {//only jedi return qfalse; } - if ( pm->gent && pm->gent->NPC && pm->gent->NPC->rank != RANK_CREWMAN && pm->gent->NPC->rank < RANK_LT_JG ) - {//reborn who are not acrobats or fencers can't do any of these acrobatics - return qfalse; + if ( pm->gent && pm->gent->NPC ) + { + if ( pm->gent->NPC->rank != RANK_CREWMAN && pm->gent->NPC->rank < RANK_LT_JG ) + {//reborn who are not acrobats or fencers can't do any of these acrobatics + return qfalse; + } + if ( pm->gent->NPC->scriptFlags&SCF_NO_ACROBATICS ) + { + return qfalse; + } } vec3_t fwd, right, traceto, mins = {pm->mins[0],pm->mins[1],pm->mins[2]+STEPSIZE}, maxs = {pm->maxs[0],pm->maxs[1],pm->gent->client->crouchheight}, fwdAngles = {0, pm->ps->viewangles[YAW], 0}; trace_t trace; @@ -2380,10 +2480,15 @@ static void PM_CrashLand( void ) pm->ps->gravity = 1.0; //PM_CrashLandDamage( delta ); if ( pm->gent ) - { - if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) ) + { + if ((!(pml.groundTrace.surfaceFlags & SURF_NODAMAGE)) && +// (!(pml.groundTrace.contents & CONTENTS_NODROP)) && + (!(pm->pointcontents(pm->ps->origin,pm->ps->clientNum) & CONTENTS_NODROP))) { - G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/player/fallsplat.wav" ); + if ( pm->waterlevel < 2 ) + {//don't play fallsplat when impact in the water + G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/player/fallsplat.wav" ); + } if ( gi.VoiceVolume[pm->ps->clientNum] && pm->gent->NPC && (pm->gent->NPC->aiFlags&NPCAI_DIE_ON_IMPACT) ) {//I was talking, so cut it off... with a jump sound? @@ -2615,132 +2720,146 @@ static void PM_GroundTraceMissed( void ) { //FIXME: if in a contents_falldeath brush, play the falling death anim and sound? - if ( pm->ps->clientNum != 0 && pm->gent && pm->gent->NPC && pm->gent->client && pm->gent->client->NPC_class != CLASS_DESANN &&//desann never falls to his death - pm->ps->groundEntityNum == ENTITYNUM_NONE && - pm->ps->stats[STAT_HEALTH] > 0 && - !(pm->gent->NPC->aiFlags&NPCAI_DIE_ON_IMPACT) && - (level.time - pm->gent->client->respawnTime > 2000) &&//been in the world for at least 2 seconds - (!pm->gent->NPC->timeOfDeath || level.time - pm->gent->NPC->timeOfDeath < 1000) && pm->gent->e_ThinkFunc != thinkF_NPC_RemoveBody && //Have to do this now because timeOfDeath is used by thinkF_NPC_RemoveBody to debounce removal checks - !(pm->gent->client->ps.forcePowersActive&(1<gent ) && - g_gravity->value > 0 && - !(pm->gent->flags&FL_UNDYING) && - !(pm->gent->flags&FL_GODMODE) && - !(pm->ps->eFlags&EF_FORCE_GRIPPED) && - !(pm->ps->pm_flags&PMF_TRIGGER_PUSHED) && - (!pm->ps->forceJumpZStart || pm->ps->forceJumpZStart > pm->ps->origin[2])// && fabs(pm->ps->velocity[0])<10 && fabs(pm->ps->velocity[1])<10 && pm->ps->velocity[2]<0)//either not force-jumping or force-jumped and now fell below original jump start height - /*&& - pm->ps->legsAnim = BOTH_FALLDEATH1 && - pm->ps->legsAnim != BOTH_DEATH1 && - PM_HasAnimation( pm->gent, BOTH_FALLDEATH1 )*/ ) + if ( pm->ps->clientNum != 0 && pm->gent && pm->gent->NPC && pm->gent->client && pm->gent->client->NPC_class != CLASS_DESANN )//desann never falls to his death { - //New method: predict impact, 400 ahead - vec3_t vel; - float time; - - VectorCopy( pm->ps->velocity, vel ); - float speed = VectorLength( vel ); - if ( !speed ) - {//damn divide by zero - speed = 1; - } - time = 400/speed; - vel[2] -= 0.5 * time * pm->ps->gravity; - speed = VectorLength( vel ); - if ( !speed ) - {//damn divide by zero - speed = 1; - } - time = 400/speed; - VectorScale( vel, time, vel ); - VectorAdd( pm->ps->origin, vel, point ); - - pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask ); - - if ( !trace.allsolid && !trace.startsolid && (pm->ps->origin[2] - trace.endpos[2]) >= 128 )//>=128 so we don't die on steps! + if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) { - if ( trace.fraction == 1.0 ) - {//didn't hit, we're probably going to die - if ( pm->ps->velocity[2] < 0 && pm->ps->origin[2] - point[2] > 256 ) - {//going down, into a bottomless pit, apparently - PM_FallToDeath(); - cliff_fall = qtrue; - } - } - else if ( trace.entityNum < ENTITYNUM_NONE && pm->ps->weapon != WP_SABER ) - {//Jedi don't scream and die if they're heading for a hard impact - gentity_t *traceEnt = &g_entities[trace.entityNum]; - if ( trace.entityNum == ENTITYNUM_WORLD || (traceEnt && traceEnt->bmodel) ) - {//hit architecture, find impact force - float dmg; - - VectorCopy( pm->ps->velocity, vel ); - time = Distance( trace.endpos, pm->ps->origin )/VectorLength( vel ); - vel[2] -= 0.5 * time * pm->ps->gravity; - - if ( trace.plane.normal[2] > 0.5 ) - {//use falling damage - int waterlevel, junk; - PM_SetWaterLevelAtPoint( trace.endpos, &waterlevel, &junk ); - dmg = PM_CrashLandDelta( vel, waterlevel ); - if ( dmg >= 30 ) - {//there is a minimum fall threshhold - dmg = PM_DamageForDelta( dmg ); - } - else - { - dmg = 0; - } - } - else - {//use impact damage - //guestimate - if ( pm->gent->client && pm->gent->client->ps.forceJumpZStart ) - {//we were force-jumping - if ( pm->gent->currentOrigin[2] >= pm->gent->client->ps.forceJumpZStart ) - {//we landed at same height or higher than we landed - dmg = 0; - } - else - {//FIXME: take off some of it, at least? - dmg = (pm->gent->client->ps.forceJumpZStart-pm->gent->currentOrigin[2])/3; - } - } - dmg = 10 * VectorLength( pm->ps->velocity ); - if ( !pm->ps->clientNum ) - { - dmg /= 2; - } - dmg *= 0.01875f;//magic number - } - if ( dmg >= pm->ps->stats[STAT_HEALTH] )//armor? + if ( pm->ps->stats[STAT_HEALTH] > 0 + && !(pm->gent->NPC->aiFlags&NPCAI_DIE_ON_IMPACT) + && !(pm->gent->NPC->scriptFlags&SCF_NO_FALLTODEATH) ) + { + if ( (level.time - pm->gent->client->respawnTime > 2000)//been in the world for at least 2 seconds + && (!pm->gent->NPC->timeOfDeath || level.time - pm->gent->NPC->timeOfDeath < 1000) && pm->gent->e_ThinkFunc != thinkF_NPC_RemoveBody //Have to do this now because timeOfDeath is used by thinkF_NPC_RemoveBody to debounce removal checks + && !(pm->gent->client->ps.forcePowersActive&(1<gent ) + && g_gravity->value > 0 ) { - PM_FallToDeath(); - cliff_fall = qtrue; + if ( !(pm->gent->flags&FL_UNDYING) + && !(pm->gent->flags&FL_GODMODE) ) + { + if ( !(pm->ps->eFlags&EF_FORCE_GRIPPED) + && !(pm->ps->pm_flags&PMF_TRIGGER_PUSHED) ) + { + if ( !pm->ps->forceJumpZStart || pm->ps->forceJumpZStart > pm->ps->origin[2] )// && fabs(pm->ps->velocity[0])<10 && fabs(pm->ps->velocity[1])<10 && pm->ps->velocity[2]<0)//either not force-jumping or force-jumped and now fell below original jump start height + { + /*if ( pm->ps->legsAnim = BOTH_FALLDEATH1 + && pm->ps->legsAnim != BOTH_DEATH1 + && PM_HasAnimation( pm->gent, BOTH_FALLDEATH1 )*/ + //New method: predict impact, 400 ahead + vec3_t vel; + float time; + + VectorCopy( pm->ps->velocity, vel ); + float speed = VectorLength( vel ); + if ( !speed ) + {//damn divide by zero + speed = 1; + } + time = 400/speed; + vel[2] -= 0.5 * time * pm->ps->gravity; + speed = VectorLength( vel ); + if ( !speed ) + {//damn divide by zero + speed = 1; + } + time = 400/speed; + VectorScale( vel, time, vel ); + VectorAdd( pm->ps->origin, vel, point ); + + pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask ); + + if ( !trace.allsolid && !trace.startsolid && (pm->ps->origin[2] - trace.endpos[2]) >= 128 )//>=128 so we don't die on steps! + { + if ( trace.fraction == 1.0 ) + {//didn't hit, we're probably going to die + if ( pm->ps->velocity[2] < 0 && pm->ps->origin[2] - point[2] > 256 ) + {//going down, into a bottomless pit, apparently + PM_FallToDeath(); + cliff_fall = qtrue; + } + } + else if ( trace.entityNum < ENTITYNUM_NONE && pm->ps->weapon != WP_SABER ) + {//Jedi don't scream and die if they're heading for a hard impact + gentity_t *traceEnt = &g_entities[trace.entityNum]; + if ( trace.entityNum == ENTITYNUM_WORLD || (traceEnt && traceEnt->bmodel) ) + {//hit architecture, find impact force + float dmg; + + VectorCopy( pm->ps->velocity, vel ); + time = Distance( trace.endpos, pm->ps->origin )/VectorLength( vel ); + vel[2] -= 0.5 * time * pm->ps->gravity; + + if ( trace.plane.normal[2] > 0.5 ) + {//use falling damage + int waterlevel, junk; + PM_SetWaterLevelAtPoint( trace.endpos, &waterlevel, &junk ); + dmg = PM_CrashLandDelta( vel, waterlevel ); + if ( dmg >= 30 ) + {//there is a minimum fall threshhold + dmg = PM_DamageForDelta( dmg ); + } + else + { + dmg = 0; + } + } + else + {//use impact damage + //guestimate + if ( pm->gent->client && pm->gent->client->ps.forceJumpZStart ) + {//we were force-jumping + if ( pm->gent->currentOrigin[2] >= pm->gent->client->ps.forceJumpZStart ) + {//we landed at same height or higher than we landed + dmg = 0; + } + else + {//FIXME: take off some of it, at least? + dmg = (pm->gent->client->ps.forceJumpZStart-pm->gent->currentOrigin[2])/3; + } + } + dmg = 10 * VectorLength( pm->ps->velocity ); + if ( !pm->ps->clientNum ) + { + dmg /= 2; + } + dmg *= 0.01875f;//magic number + } + if ( dmg >= pm->ps->stats[STAT_HEALTH] )//armor? + { + PM_FallToDeath(); + cliff_fall = qtrue; + } + } + } + } + + /* + vec3_t start; + //okay, kind of expensive temp hack here, but let's check to see if we should scream + //FIXME: we should either do a better check (predict using actual velocity) or we should wait until they've been over a bottomless pit for a certain amount of time... + VectorCopy( pm->ps->origin, start ); + if ( pm->ps->forceJumpZStart < start[2] ) + {//Jedi who are force-jumping should only do this from landing point down? + start[2] = pm->ps->forceJumpZStart; + } + VectorCopy( start, point ); + point[2] -= 400;//320 + pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); + //FIXME: somehow, people can still get stuck on ledges and not splat when hit...? + if ( !trace.allsolid && !trace.startsolid && trace.fraction == 1.0 ) + { + PM_FallToDeath(); + cliff_fall = qtrue; + } + */ + } + } + } } } } } - - /* - vec3_t start; - //okay, kind of expensive temp hack here, but let's check to see if we should scream - //FIXME: we should either do a better check (predict using actual velocity) or we should wait until they've been over a bottomless pit for a certain amount of time... - VectorCopy( pm->ps->origin, start ); - if ( pm->ps->forceJumpZStart < start[2] ) - {//Jedi who are force-jumping should only do this from landing point down? - start[2] = pm->ps->forceJumpZStart; - } - VectorCopy( start, point ); - point[2] -= 400;//320 - pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); - //FIXME: somehow, people can still get stuck on ledges and not splat when hit...? - if ( !trace.allsolid && !trace.startsolid && trace.fraction == 1.0 ) - { - PM_FallToDeath(); - cliff_fall = qtrue; - } - */ } if ( !cliff_fall ) { @@ -3096,8 +3215,15 @@ static void PM_CheckDuck (void) //assert(0); } - standheight = pm->gent->client->standheight; - crouchheight = pm->gent->client->crouchheight; + if ( !pm->ps->clientNum && pm->gent->client->NPC_class == CLASS_ATST && !cg.renderingThirdPerson ) + { + standheight = crouchheight = 128; + } + else + { + standheight = pm->gent->client->standheight; + crouchheight = pm->gent->client->crouchheight; + } } else { @@ -3257,6 +3383,7 @@ qboolean PM_ForceAnim( int anim ) case BOTH_MINDTRICK1: //# Use off-hand to do mind trick case BOTH_MINDTRICK2: //# Use off-hand to do distraction case BOTH_FORCELIGHTNING: //# Use off-hand to do lightning + case BOTH_FORCELIGHTNING_START: case BOTH_FORCELIGHTNING_HOLD: //# Use off-hand to do lightning case BOTH_FORCELIGHTNING_RELEASE: //# Use off-hand to do lightning case BOTH_FORCEHEAL_START: //# Healing meditation pose start @@ -3738,6 +3865,26 @@ qboolean PM_FlippingAnim( int anim ) return qfalse; } +qboolean PM_WalkingAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_WALK1: //# Normal walk + case BOTH_WALK2: //# Normal walk + case BOTH_WALK3: //# Goes with stand3 + case BOTH_WALK4: //# Walk cycle goes to a stand4 + case BOTH_WALK5: //# Tavion taunting Kyle (cin 22) + case BOTH_WALK6: //# Slow walk for Luke (cin 12) + case BOTH_WALK7: //# Fast walk + case BOTH_WALKTORUN1: //# transition from walk to run + case BOTH_WALKBACK1: //# Walk1 backwards + case BOTH_WALKBACK2: //# Walk2 backwards + return qtrue; + break; + } + return qfalse; +} + qboolean PM_RunningAnim( int anim ) { switch ( anim ) @@ -3746,7 +3893,13 @@ qboolean PM_RunningAnim( int anim ) case BOTH_RUN2: case BOTH_RUNBACK1: case BOTH_RUNBACK2: - case BOTH_RUNAWAY1: + case BOTH_WALKTORUN1: //# transition from walk to run + case BOTH_RUN1START: //# Start into full run1 + case BOTH_RUN1STOP: //# Stop from full run1 + case BOTH_RUNINJURED1: //# Run with injured left leg + case BOTH_RUNSTRAFE_LEFT1: //# Sidestep left: should loop + case BOTH_RUNSTRAFE_RIGHT1: //# Sidestep right: should loop + case BOTH_RUNAWAY1: //# Running scared return qtrue; break; } @@ -3775,6 +3928,7 @@ qboolean PM_SwimmingAnim( int anim ) { switch ( anim ) { + case BOTH_SWIM1: //# Swimming case BOTH_SWIM_IDLE1: //# Swimming Idle 1 case BOTH_SWIMFORWARDSTART: //# Swim forward start case BOTH_SWIMFORWARD: //# Swim forward loop @@ -4049,6 +4203,46 @@ qboolean PM_InSlopeAnim( int anim ) case LEGS_RIGHTUP3: //# On a slope with RIGHT foot 12 higher than left case LEGS_RIGHTUP4: //# On a slope with RIGHT foot 16 higher than left case LEGS_RIGHTUP5: //# On a slope with RIGHT foot 20 higher than left + case LEGS_S1_LUP1: + case LEGS_S1_LUP2: + case LEGS_S1_LUP3: + case LEGS_S1_LUP4: + case LEGS_S1_LUP5: + case LEGS_S1_RUP1: + case LEGS_S1_RUP2: + case LEGS_S1_RUP3: + case LEGS_S1_RUP4: + case LEGS_S1_RUP5: + case LEGS_S3_LUP1: + case LEGS_S3_LUP2: + case LEGS_S3_LUP3: + case LEGS_S3_LUP4: + case LEGS_S3_LUP5: + case LEGS_S3_RUP1: + case LEGS_S3_RUP2: + case LEGS_S3_RUP3: + case LEGS_S3_RUP4: + case LEGS_S3_RUP5: + case LEGS_S4_LUP1: + case LEGS_S4_LUP2: + case LEGS_S4_LUP3: + case LEGS_S4_LUP4: + case LEGS_S4_LUP5: + case LEGS_S4_RUP1: + case LEGS_S4_RUP2: + case LEGS_S4_RUP3: + case LEGS_S4_RUP4: + case LEGS_S4_RUP5: + case LEGS_S5_LUP1: + case LEGS_S5_LUP2: + case LEGS_S5_LUP3: + case LEGS_S5_LUP4: + case LEGS_S5_LUP5: + case LEGS_S5_RUP1: + case LEGS_S5_RUP2: + case LEGS_S5_RUP3: + case LEGS_S5_RUP4: + case LEGS_S5_RUP5: return qtrue; break; } @@ -4128,10 +4322,93 @@ qboolean PM_AdjustStandAnimForSlope( void ) return qfalse; } - //step 5: based on the chosen interval and the current legsAnim, pick the correct anim int legsAnim = pm->ps->legsAnim; + if ( pm->gent->client->NPC_class != CLASS_ATST ) + { + //adjust for current legs anim + switch ( legsAnim ) + { + case BOTH_STAND1: + case LEGS_S1_LUP1: + case LEGS_S1_LUP2: + case LEGS_S1_LUP3: + case LEGS_S1_LUP4: + case LEGS_S1_LUP5: + case LEGS_S1_RUP1: + case LEGS_S1_RUP2: + case LEGS_S1_RUP3: + case LEGS_S1_RUP4: + case LEGS_S1_RUP5: + destAnim = LEGS_S1_LUP1 + (destAnim-LEGS_LEFTUP1); + break; + case BOTH_STAND2: + case BOTH_SABERFAST_STANCE: + case BOTH_SABERSLOW_STANCE: + case BOTH_CROUCH1IDLE: + case LEGS_LEFTUP1: //# On a slope with left foot 4 higher than right + case LEGS_LEFTUP2: //# On a slope with left foot 8 higher than right + case LEGS_LEFTUP3: //# On a slope with left foot 12 higher than right + case LEGS_LEFTUP4: //# On a slope with left foot 16 higher than right + case LEGS_LEFTUP5: //# On a slope with left foot 20 higher than right + case LEGS_RIGHTUP1: //# On a slope with RIGHT foot 4 higher than left + case LEGS_RIGHTUP2: //# On a slope with RIGHT foot 8 higher than left + case LEGS_RIGHTUP3: //# On a slope with RIGHT foot 12 higher than left + case LEGS_RIGHTUP4: //# On a slope with RIGHT foot 16 higher than left + case LEGS_RIGHTUP5: //# On a slope with RIGHT foot 20 higher than left + //fine + break; + case BOTH_STAND3: + case LEGS_S3_LUP1: + case LEGS_S3_LUP2: + case LEGS_S3_LUP3: + case LEGS_S3_LUP4: + case LEGS_S3_LUP5: + case LEGS_S3_RUP1: + case LEGS_S3_RUP2: + case LEGS_S3_RUP3: + case LEGS_S3_RUP4: + case LEGS_S3_RUP5: + destAnim = LEGS_S3_LUP1 + (destAnim-LEGS_LEFTUP1); + break; + case BOTH_STAND4: + case LEGS_S4_LUP1: + case LEGS_S4_LUP2: + case LEGS_S4_LUP3: + case LEGS_S4_LUP4: + case LEGS_S4_LUP5: + case LEGS_S4_RUP1: + case LEGS_S4_RUP2: + case LEGS_S4_RUP3: + case LEGS_S4_RUP4: + case LEGS_S4_RUP5: + destAnim = LEGS_S4_LUP1 + (destAnim-LEGS_LEFTUP1); + break; + case BOTH_STAND5: + case LEGS_S5_LUP1: + case LEGS_S5_LUP2: + case LEGS_S5_LUP3: + case LEGS_S5_LUP4: + case LEGS_S5_LUP5: + case LEGS_S5_RUP1: + case LEGS_S5_RUP2: + case LEGS_S5_RUP3: + case LEGS_S5_RUP4: + case LEGS_S5_RUP5: + destAnim = LEGS_S5_LUP1 + (destAnim-LEGS_LEFTUP1); + break; + case BOTH_STAND6: + default: + return qfalse; + break; + } + } + //step 5: based on the chosen interval and the current legsAnim, pick the correct anim //step 6: increment/decrement to the dest anim, not instant - if ( legsAnim >= LEGS_LEFTUP1 && legsAnim <= LEGS_LEFTUP5 ) + if ( (legsAnim >= LEGS_LEFTUP1 && legsAnim <= LEGS_LEFTUP5) + || (legsAnim >= LEGS_S1_LUP1 && legsAnim <= LEGS_S1_LUP5) + || (legsAnim >= LEGS_S3_LUP1 && legsAnim <= LEGS_S3_LUP5) + || (legsAnim >= LEGS_S4_LUP1 && legsAnim <= LEGS_S4_LUP5) + || (legsAnim >= LEGS_S5_LUP1 && legsAnim <= LEGS_S5_LUP5) ) {//already in left-side up if ( destAnim > legsAnim && pm->gent->client->slopeRecalcTime < level.time ) { @@ -4148,7 +4425,11 @@ qboolean PM_AdjustStandAnimForSlope( void ) destAnim = legsAnim; } } - else if ( legsAnim >= LEGS_RIGHTUP1 && legsAnim <= LEGS_RIGHTUP5 ) + else if ( (legsAnim >= LEGS_RIGHTUP1 && legsAnim <= LEGS_RIGHTUP5) + || (legsAnim >= LEGS_S1_RUP1 && legsAnim <= LEGS_S1_RUP5) + || (legsAnim >= LEGS_S3_RUP1 && legsAnim <= LEGS_S3_RUP5) + || (legsAnim >= LEGS_S4_RUP1 && legsAnim <= LEGS_S4_RUP5) + || (legsAnim >= LEGS_S5_RUP1 && legsAnim <= LEGS_S5_RUP5) ) {//already in right-side up if ( destAnim > legsAnim && pm->gent->client->slopeRecalcTime < level.time ) { @@ -4167,33 +4448,118 @@ qboolean PM_AdjustStandAnimForSlope( void ) } else {//in a stand of some sort? - switch ( legsAnim ) + if ( pm->gent->client->NPC_class == CLASS_ATST ) { - case BOTH_STAND1: - case BOTH_STAND2: - case BOTH_STAND3: - case BOTH_STAND4: - case BOTH_STAND5: - case BOTH_STAND6: - case BOTH_CROUCH1IDLE: - if ( destAnim >= LEGS_LEFTUP1 && destAnim <= LEGS_LEFTUP5 ) - {//going into right side up - destAnim = LEGS_LEFTUP1; - pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; + if ( legsAnim == BOTH_STAND2 || legsAnim == BOTH_CROUCH1IDLE ) + { + if ( destAnim >= LEGS_LEFTUP1 && destAnim <= LEGS_LEFTUP5 ) + {//going into left side up + destAnim = LEGS_LEFTUP1; + pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; + } + else if ( destAnim >= LEGS_RIGHTUP1 && destAnim <= LEGS_RIGHTUP5 ) + {//going into right side up + destAnim = LEGS_RIGHTUP1; + pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; + } + else + {//will never get here + return qfalse; + } } - else if ( destAnim >= LEGS_RIGHTUP1 && destAnim <= LEGS_RIGHTUP5 ) - {//going into right side up - destAnim = LEGS_RIGHTUP1; - pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; - } - else - {//will never get here + } + else + { + switch ( legsAnim ) + { + case BOTH_STAND1: + if ( destAnim >= LEGS_S1_LUP1 && destAnim <= LEGS_S1_LUP5 ) + {//going into left side up + destAnim = LEGS_S1_LUP1; + pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; + } + else if ( destAnim >= LEGS_S1_RUP1 && destAnim <= LEGS_S1_RUP5 ) + {//going into right side up + destAnim = LEGS_S1_RUP1; + pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; + } + else + {//will never get here + return qfalse; + } + break; + case BOTH_STAND2: + case BOTH_SABERFAST_STANCE: + case BOTH_SABERSLOW_STANCE: + case BOTH_CROUCH1IDLE: + if ( destAnim >= LEGS_LEFTUP1 && destAnim <= LEGS_LEFTUP5 ) + {//going into left side up + destAnim = LEGS_LEFTUP1; + pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; + } + else if ( destAnim >= LEGS_RIGHTUP1 && destAnim <= LEGS_RIGHTUP5 ) + {//going into right side up + destAnim = LEGS_RIGHTUP1; + pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; + } + else + {//will never get here + return qfalse; + } + break; + case BOTH_STAND3: + if ( destAnim >= LEGS_S3_LUP1 && destAnim <= LEGS_S3_LUP5 ) + {//going into left side up + destAnim = LEGS_S3_LUP1; + pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; + } + else if ( destAnim >= LEGS_S3_RUP1 && destAnim <= LEGS_S3_RUP5 ) + {//going into right side up + destAnim = LEGS_S3_RUP1; + pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; + } + else + {//will never get here + return qfalse; + } + break; + case BOTH_STAND4: + if ( destAnim >= LEGS_S4_LUP1 && destAnim <= LEGS_S4_LUP5 ) + {//going into left side up + destAnim = LEGS_S4_LUP1; + pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; + } + else if ( destAnim >= LEGS_S4_RUP1 && destAnim <= LEGS_S4_RUP5 ) + {//going into right side up + destAnim = LEGS_S4_RUP1; + pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; + } + else + {//will never get here + return qfalse; + } + break; + case BOTH_STAND5: + if ( destAnim >= LEGS_S5_LUP1 && destAnim <= LEGS_S5_LUP5 ) + {//going into left side up + destAnim = LEGS_S5_LUP1; + pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; + } + else if ( destAnim >= LEGS_S5_RUP1 && destAnim <= LEGS_S5_RUP5 ) + {//going into right side up + destAnim = LEGS_S5_RUP1; + pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; + } + else + {//will never get here + return qfalse; + } + break; + case BOTH_STAND6: + default: return qfalse; + break; } - break; - default: - return qfalse; - break; } } //step 7: set the anim @@ -4221,28 +4587,31 @@ void PM_SwimFloatAnim( void ) } } else - { + {//moving backward or stopping if ( legsAnim == BOTH_SWIMFORWARD ) - { + {//I was swimming forward, not stop PM_SetAnim(pm,SETANIM_LEGS,BOTH_SWIMFORWARDSTOP,SETANIM_FLAG_NORMAL|SETANIM_FLAG_HOLD); } else if ( legsAnim == BOTH_SWIMFORWARDSTOP ) - { + {//was stopping, go to idle if ( !pm->ps->legsAnimTimer ) { PM_SetAnim(pm,SETANIM_LEGS,BOTH_SWIM_IDLE1,SETANIM_FLAG_NORMAL); } } else if ( legsAnim == BOTH_SWIMFORWARDSTART ) - { + {//was starting, stop if ( !pm->ps->legsAnimTimer ) { PM_SetAnim(pm,SETANIM_LEGS,BOTH_SWIMFORWARDSTOP,SETANIM_FLAG_NORMAL|SETANIM_FLAG_HOLD); } } else - { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_SWIM_IDLE1,SETANIM_FLAG_NORMAL); + {//idle + if ( !(pm->ps->pm_flags&PMF_DUCKED) && pm->cmd.upmove >= 0 ) + {//not crouching + PM_SetAnim(pm,SETANIM_LEGS,BOTH_SWIM_IDLE1,SETANIM_FLAG_NORMAL); + } } } } @@ -4296,7 +4665,7 @@ static void PM_Footsteps( void ) flipping = qtrue; } - if ( pm->ps->groundEntityNum == ENTITYNUM_NONE || pm->waterlevel > 2 ) + if ( pm->ps->groundEntityNum == ENTITYNUM_NONE || pm->waterlevel > 1 ) {//in air or totally submerged in water // airborne leaves position in cycle intact, but doesn't advance if ( pm->waterlevel > 0 ) @@ -4339,8 +4708,30 @@ static void PM_Footsteps( void ) } return; } - else if ( pm->waterlevel > 1 ) //off ground and in deep water + else if ( pm->waterlevel > 1 ) //in deep water { + if ( (pm->cmd.forwardmove<=0||pm->ps->groundEntityNum == ENTITYNUM_NONE) + && (!PM_SwimmingAnim( pm->ps->legsAnim ) || pm->ps->legsAnim == BOTH_SWIM_IDLE1) + && pm->ps->pm_flags & PMF_DUCKED ) + {//not moving or on ground, in swim idle and ducking + if ( !flipping ) + {//not flipping + if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN + || pm->cmd.forwardmove < 0 ) + { + PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1WALKBACK,setAnimFlags); + } + else if ( pm->cmd.rightmove ) + { + PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1WALK,setAnimFlags); + } + else + { + PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1,SETANIM_FLAG_NORMAL); + } + } + return; + } PM_SwimFloatAnim(); return; } @@ -4416,15 +4807,32 @@ static void PM_Footsteps( void ) { if ( !PM_AdjustStandAnimForSlope() ) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND2,SETANIM_FLAG_NORMAL); + int legsAnim; + switch ( pm->ps->saberAnimLevel ) + { + case FORCE_LEVEL_1: + case FORCE_LEVEL_5: + legsAnim = BOTH_SABERFAST_STANCE; + break; + case FORCE_LEVEL_3: + legsAnim = BOTH_SABERSLOW_STANCE; + break; + case FORCE_LEVEL_0: + case FORCE_LEVEL_2: + case FORCE_LEVEL_4: + default: + legsAnim = BOTH_STAND2; + break; + } + PM_SetAnim(pm,SETANIM_LEGS,legsAnim,SETANIM_FLAG_NORMAL); } } else if( (validNPC && pm->ps->weapon > WP_SABER && pm->ps->weapon < WP_DET_PACK ))// && pm->gent->client->race != RACE_BORG))//Being careful or carrying a 2-handed weapon {//Squadmates use BOTH_STAND3 oldAnim = pm->ps->legsAnim; if(oldAnim != BOTH_GUARD_LOOKAROUND1 && oldAnim != BOTH_GUARD_IDLE1 && - oldAnim != BOTH_STAND2_RANDOM1 && oldAnim != BOTH_STAND2_RANDOM2 && oldAnim != BOTH_STAND2_RANDOM3 && oldAnim != BOTH_STAND2_RANDOM4 && - oldAnim != BOTH_STAND2TO4 && oldAnim != BOTH_STAND4TO2 && oldAnim != BOTH_STAND4 ) + oldAnim != BOTH_STAND3IDLE1 && oldAnim != BOTH_STAND2TO4 + && oldAnim != BOTH_STAND4TO2 && oldAnim != BOTH_STAND4 ) {//Don't auto-override the guard idles if ( !PM_AdjustStandAnimForSlope() ) { @@ -4467,6 +4875,8 @@ static void PM_Footsteps( void ) || pm->ps->legsAnim == BOTH_STAND1TO2 || pm->ps->legsAnim == BOTH_STAND2TO1 || pm->ps->legsAnim == BOTH_STAND2 + || pm->ps->legsAnim == BOTH_SABERFAST_STANCE + || pm->ps->legsAnim == BOTH_SABERSLOW_STANCE || pm->ps->legsAnim == BOTH_BUTTON_HOLD || pm->ps->legsAnim == BOTH_BUTTON_RELEASE || PM_LandingAnim( pm->ps->legsAnim ) @@ -4691,6 +5101,8 @@ Generate sound events for entering and leaving water */ static void PM_WaterEvents( void ) { // FIXME? + qboolean impact_splash = qfalse; + if ( pm->watertype & CONTENTS_LADDER ) //fake water for ladder { return; @@ -4700,6 +5112,10 @@ static void PM_WaterEvents( void ) { // FIXME? // if (!pml.previous_waterlevel && pm->waterlevel) { PM_AddEvent( EV_WATER_TOUCH ); + if ( pm->gent && VectorLengthSquared( pm->ps->velocity ) > 40000 ) + { + impact_splash = qtrue; + } if ( pm->gent && !pm->ps->clientNum ) { AddSoundEvent( pm->gent, pm->ps->origin, 384, AEL_SUSPICIOUS ); @@ -4712,6 +5128,10 @@ static void PM_WaterEvents( void ) { // FIXME? // if (pml.previous_waterlevel && !pm->waterlevel) { PM_AddEvent( EV_WATER_LEAVE ); + if ( pm->gent && VectorLengthSquared( pm->ps->velocity ) > 40000 ) + { + impact_splash = qtrue; + } if ( pm->gent && !pm->ps->clientNum ) { AddSoundEvent( pm->gent, pm->ps->origin, 384, AEL_SUSPICIOUS ); @@ -4719,6 +5139,30 @@ static void PM_WaterEvents( void ) { // FIXME? } } + if ( impact_splash ) + { + //play the splash effect + trace_t tr; + vec3_t axis[3], angs, start, end; + + VectorSet( angs, 0, pm->gent->currentAngles[YAW], 0 ); + AngleVectors( angs, axis[2], axis[1], axis[0] ); + + VectorCopy( pm->ps->origin, start ); + VectorCopy( pm->ps->origin, end ); + + // FIXME: set start and end better + start[2] += 10; + end[2] -= 40; + + gi.trace( &tr, start, vec3_origin, vec3_origin, end, pm->gent->s.number, CONTENTS_WATER ); + + if ( tr.fraction < 1.0f ) + { + G_PlayEffect( "water_impact", tr.endpos, axis ); + } + } + // // check for head just going under water // @@ -4759,6 +5203,15 @@ PM_BeginWeaponChange */ extern void G_CreateG2AttachedWeaponModel( gentity_t *ent, const char *weaponModel ); static void PM_BeginWeaponChange( int weapon ) { + + if ( pm->gent && pm->gent->client && pm->gent->client->pers.enterTime >= level.time - 500 ) + {//just entered map + if ( weapon == WP_NONE && pm->ps->weapon != weapon ) + {//don't switch to weapon none if just entered map + return; + } + } + if ( weapon < WP_NONE || weapon >= WP_NUM_WEAPONS ) { return; } @@ -4804,7 +5257,10 @@ static void PM_BeginWeaponChange( int weapon ) { if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ATST ) { - gi.cvar_set( "cg_thirdperson", "1" ); + if ( !pm->ps->clientNum ) + { + gi.cvar_set( "cg_thirdperson", "1" ); + } } else if ( weapon == WP_SABER ) {//going to switch to lightsaber @@ -4833,7 +5289,15 @@ PM_FinishWeaponChange */ static void PM_FinishWeaponChange( void ) { int weapon; + qboolean trueSwitch = qtrue; + if ( pm->gent && pm->gent->client && pm->gent->client->pers.enterTime >= level.time - 500 ) + {//just entered map + if ( pm->cmd.weapon == WP_NONE && pm->ps->weapon != pm->cmd.weapon ) + {//don't switch to weapon none if just entered map + return; + } + } weapon = pm->cmd.weapon; if ( weapon < WP_NONE || weapon >= WP_NUM_WEAPONS ) { weapon = WP_NONE; @@ -4843,6 +5307,11 @@ static void PM_FinishWeaponChange( void ) { weapon = WP_NONE; } + if ( pm->ps->weapon == weapon ) + { + trueSwitch = qfalse; + } + int oldWeap = pm->ps->weapon; pm->ps->weapon = weapon; pm->ps->weaponstate = WEAPON_RAISING; pm->ps->weaponTime += 250; @@ -4869,8 +5338,13 @@ static void PM_FinishWeaponChange( void ) { if ( !pm->ps->saberInFlight ) {//if it's not in flight or lying around, turn it on! //FIXME: AddSound/Sight Event - pm->ps->saberActive = qtrue; - pm->ps->saberLength = 0; + //FIXME: don't do this if just loaded a game + if ( trueSwitch ) + {//actually did switch weapons, turn it on + pm->ps->saberActive = qtrue; + pm->ps->saberLength = 0; + } + if ( pm->gent ) { G_CreateG2AttachedWeaponModel( pm->gent, pm->ps->saberModel ); @@ -4883,12 +5357,15 @@ static void PM_FinishWeaponChange( void ) { if ( pm->gent ) { WP_SaberInitBladeData( pm->gent ); - if ( cg_saberAutoThird.value ) + if ( !pm->ps->clientNum && cg_saberAutoThird.value ) { gi.cvar_set( "cg_thirdperson", "1" ); } } - PM_SetSaberMove(LS_DRAW); + if ( trueSwitch ) + {//actually did switch weapons, play anim + PM_SetSaberMove(LS_DRAW); + } } else {//switched away from saber @@ -4920,7 +5397,7 @@ static void PM_FinishWeaponChange( void ) { { PM_SetAnim(pm,SETANIM_TORSO,TORSO_RAISEWEAP1,SETANIM_FLAG_HOLD); } - if ( cg_gunAutoFirst.value ) + if ( !pm->ps->clientNum && cg_gunAutoFirst.value && oldWeap == WP_SABER ) { gi.cvar_set( "cg_thirdperson", "0" ); } @@ -4943,8 +5420,8 @@ void PM_SetSaberMove(short newMove) saberMoveData[newMove].name); } - if ( newMove == LS_READY ) - {//finished with a kata, reset attack counter + if ( newMove == LS_READY || newMove == LS_A_FLIP_STAB || newMove == LS_A_FLIP_SLASH ) + {//finished with a kata (or in a special move) reset attack counter pm->ps->saberAttackChainCount = 0; } else if ( PM_SaberInAttack( newMove ) ) @@ -4952,7 +5429,26 @@ void PM_SetSaberMove(short newMove) pm->ps->saberAttackChainCount++; } - if ( pm->ps->saberAnimLevel > FORCE_LEVEL_1 && + if ( newMove == LS_READY ) + { + switch ( pm->ps->saberAnimLevel ) + { + case FORCE_LEVEL_1: + case FORCE_LEVEL_5: + anim = BOTH_SABERFAST_STANCE; + break; + case FORCE_LEVEL_3: + anim = BOTH_SABERSLOW_STANCE; + break; + case FORCE_LEVEL_0: + case FORCE_LEVEL_2: + case FORCE_LEVEL_4: + default: + anim = BOTH_STAND2; + break; + } + } + else if ( pm->ps->saberAnimLevel > FORCE_LEVEL_1 && !PM_SaberInIdle( newMove ) && !PM_SaberInParry( newMove ) && !PM_SaberInKnockaway( newMove ) && !PM_SaberInBrokenParry( newMove ) && !PM_SaberInReflect( newMove ) && !PM_SaberInSpecial( newMove )) {//readies, parries and reflections have only 1 level //increment the anim to the next level of saber anims @@ -4965,7 +5461,9 @@ void PM_SetSaberMove(short newMove) setflags |= SETANIM_FLAG_RESTART; } - if ( anim == BOTH_STAND2 ) + if ( anim == BOTH_STAND2 + || anim == BOTH_SABERFAST_STANCE + || anim == BOTH_SABERSLOW_STANCE ) { //FIXME: play both_stand2_random1 when you've been idle for a while if( pm->ps->legsAnim == BOTH_WALK1 ) @@ -5023,9 +5521,33 @@ void PM_SetSaberMove(short newMove) {//playing an attack if ( pm->ps->saberMove != newMove ) {//wasn't playing that attack before - G_SoundOnEnt( pm->gent, CHAN_WEAPON, va( "sound/weapons/saber/saberhup%d.wav", Q_irand( 1, 8 ) ) ); + if ( PM_SaberInSpecialAttack( anim ) ) + { + G_SoundOnEnt( pm->gent, CHAN_WEAPON, va( "sound/weapons/saber/saberhup%d.wav", Q_irand( 1, 3 ) ) ); + } + else + { + switch ( pm->ps->saberAnimLevel ) + { + case FORCE_LEVEL_4: + case FORCE_LEVEL_3: + G_SoundOnEnt( pm->gent, CHAN_WEAPON, va( "sound/weapons/saber/saberhup%d.wav", Q_irand( 7, 9 ) ) ); + break; + case FORCE_LEVEL_2: + G_SoundOnEnt( pm->gent, CHAN_WEAPON, va( "sound/weapons/saber/saberhup%d.wav", Q_irand( 4, 6 ) ) ); + break; + case FORCE_LEVEL_5: + case FORCE_LEVEL_1: + G_SoundOnEnt( pm->gent, CHAN_WEAPON, va( "sound/weapons/saber/saberhup%d.wav", Q_irand( 1, 3 ) ) ); + break; + } + } } } + else if ( PM_SaberInStart( newMove ) && pm->ps->saberAnimLevel == FORCE_LEVEL_3 ) + { + G_SoundOnEnt( pm->gent, CHAN_WEAPON, va( "sound/weapons/saber/saberhup%d.wav", Q_irand( 1, 3 ) ) ); + } } pm->ps->saberMove = newMove; @@ -5276,17 +5798,17 @@ void PM_SetAnimFrame( gentity_t *gent, int frame, qboolean torso, qboolean legs if ( torso && gent->lowerLumbarBone != -1 )//gent->upperLumbarBone { gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], gent->lowerLumbarBone, //gent->upperLumbarBone - frame, frame+1, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, 1, actualTime, frame+1, 150 ); + frame, frame+1, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, 1, actualTime, frame, 150 ); if ( gent->motionBone != -1 ) { gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], gent->motionBone, //gent->upperLumbarBone - frame, frame+1, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, 1, actualTime, frame+1, 150 ); + frame, frame+1, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, 1, actualTime, frame, 150 ); } } if ( legs && gent->rootBone != -1 ) { gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], gent->rootBone, - frame, frame+1, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, 1, actualTime, frame+1, 150 ); + frame, frame+1, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, 1, actualTime, frame, 150 ); } } @@ -5439,7 +5961,7 @@ void PM_SaberLockBreak( gentity_t *gent, gentity_t *genemy, saberLockResult_t re } G_AddVoiceEvent( genemy, Q_irand( EV_PUSHED1, EV_PUSHED3 ), 500 ); } - if ( result == LOCK_VICTORY ) + if ( result == LOCK_VICTORY && winAnim != BOTH_CCWCIRCLEBREAK ) {//one person won if ( Q_irand( FORCE_LEVEL_1, FORCE_LEVEL_2 ) < pm->ps->forcePowerLevel[FP_SABER_OFFENSE] ) { @@ -5471,9 +5993,10 @@ void PM_SaberLockBreak( gentity_t *gent, gentity_t *genemy, saberLockResult_t re if ( Q_irand( 0, 25 ) < victoryStrength && ((!genemy->s.number&&genemy->health<=10)||genemy->s.number) ) { - genemy->client->dismemberable = qtrue; NPC_SetAnim( genemy, SETANIM_BOTH, BOTH_RIGHTHANDCHOPPEDOFF, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );//force this - G_Damage( genemy, gent, gent, throwDir, genemy->client->renderInfo.handRPoint, genemy->health+10, DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC|DAMAGE_DISMEMBER, MOD_SABER, HL_HAND_RT ); + genemy->client->dismembered = qfalse; + G_DoDismemberment( genemy, genemy->client->renderInfo.handRPoint, MOD_SABER, 1000, HL_HAND_RT, qtrue ); + G_Damage( genemy, gent, gent, throwDir, genemy->client->renderInfo.handRPoint, genemy->health+10, DAMAGE_NO_PROTECTION|DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, MOD_SABER, HL_NONE ); PM_SetAnim( pm, SETANIM_BOTH, winAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); pm->ps->weaponTime = pm->ps->torsoAnimTimer + 500; @@ -5499,16 +6022,16 @@ int G_SaberLockStrength( gentity_t *gent ) { if ( gent->client->NPC_class == CLASS_DESANN || gent->client->NPC_class == CLASS_LUKE ) { - return 5; + return 5+Q_irand(0,g_spskill->integer); } else { - return gent->client->ps.forcePowerLevel[FP_SABER_OFFENSE]; + return gent->client->ps.forcePowerLevel[FP_SABER_OFFENSE]+Q_irand(0,g_spskill->integer); } } else {//player - return (gent->client->ps.forcePowerLevel[FP_SABER_OFFENSE]+1); + return gent->client->ps.forcePowerLevel[FP_SABER_OFFENSE]+Q_irand(0,g_spskill->integer); } } @@ -5578,7 +6101,10 @@ qboolean PM_SaberLocked( void ) int curFrame, junk; int strength = 1; anim = &level.knownAnimFileSets[gent->client->clientInfo.animFileIndex].animations[pm->ps->torsoAnim]; - gi.G2API_GetBoneAnimIndex( &gent->ghoul2[gent->playerModel], gent->lowerLumbarBone, (cg.time?cg.time:level.time), ¤tFrame, &junk, &junk, &junk, &junk2, NULL ); + qboolean ret; + ret=gi.G2API_GetBoneAnimIndex( &gent->ghoul2[gent->playerModel], gent->lowerLumbarBone, (cg.time?cg.time:level.time), ¤tFrame, &junk, &junk, &junk, &junk2, NULL ); + + assert(ret); // this would be pretty bad, the below code seems to assume the call succeeds. -gil strength = G_SaberLockStrength( gent ); if ( pm->ps->torsoAnim == BOTH_CCWCIRCLELOCK || @@ -5624,7 +6150,14 @@ qboolean PM_SaberLocked( void ) { if ( !pm->ps->clientNum ) { - PM_AddEvent( EV_JUMP ); + if ( !Q_irand( 0, 3 ) ) + { + PM_AddEvent( EV_JUMP ); + } + else + { + PM_AddEvent( Q_irand( EV_PUSHED1, EV_PUSHED3 ) ); + } } else { @@ -5779,7 +6312,9 @@ void PM_WeaponLightsaber(void) weaponInfo_t *weapon; int anim=-1, curmove, newmove=LS_NONE; - if ( !pm->ps->clientNum && cg.saberAnimLevelPending != pm->ps->saberAnimLevel ) + if ( !pm->ps->clientNum + && cg.saberAnimLevelPending > FORCE_LEVEL_0 + && cg.saberAnimLevelPending != pm->ps->saberAnimLevel ) { if ( !PM_SaberInStart( pm->ps->saberMove ) && !PM_SaberInTransition( pm->ps->saberMove ) @@ -5952,7 +6487,7 @@ void PM_WeaponLightsaber(void) //pm->ps->weaponTime = parryDebounce[pm->ps->forcePowerLevel[FP_SABER_DEFENSE]] * 2; if ( pm->gent ) { - pm->ps->weaponTime = Jedi_ReCalcParryTime( pm->gent ); + pm->ps->weaponTime = Jedi_ReCalcParryTime( pm->gent, EVASION_PARRY ); } else {//WTF??? @@ -6340,7 +6875,7 @@ void PM_WeaponLightsaber(void) {//NPCs must play sequential attack //going into another attack... //allow endless chaining in level 1 attacks, several in level 2 and only one or a few in level 3 - if ( PM_SaberKataDone() ) + if ( PM_SaberKataDone( LS_NONE, LS_NONE ) ) {//done with this kata, must return to ready before attack again newmove = saberMoveData[curmove].chain_idle; } @@ -6452,11 +6987,13 @@ void PM_WeaponLightsaber(void) } else//if ( pm->cmd.buttons&BUTTON_ATTACK && !(pm->ps->pm_flags&PMF_ATTACK_HELD) )//only do this if just pressed attack button? {//get attack move from movement command + /* if ( PM_SaberKataDone() ) {//we came from a bounce and cannot chain to another attack because our kata is done newmove = saberMoveData[curmove].chain_idle; } - else if ( pm->ps->clientNum && Q_irand( 0, pm->ps->saberAnimLevel-1 ) ) + else */ + if ( pm->ps->clientNum && Q_irand( 0, pm->ps->saberAnimLevel-1 ) ) {//NPCs use more randomized attacks the more skilled they are newmove = PM_NPCSaberAttackFromQuad( saberMoveData[curmove].endQuad ); } @@ -6469,6 +7006,10 @@ void PM_WeaponLightsaber(void) newmove = saberMoveData[curmove].chain_attack; } } + if ( PM_SaberKataDone( curmove, newmove ) ) + {//cannot chain this time + newmove = saberMoveData[curmove].chain_idle; + } } /* if ( newmove == LS_NONE ) @@ -6982,9 +7523,16 @@ static void PM_Weapon( void ) PM_WeaponLightsaber(); if ( pm->gent && pm->gent->client && pm->ps->saberActive && pm->ps->saberInFlight ) {//FIXME: put saberTrail in playerState - //turn on the saber trail - pm->gent->client->saberTrail.inAction = qtrue; - pm->gent->client->saberTrail.duration = 150; + if ( pm->gent->client->ps.saberEntityState == SES_RETURNING ) + {//turn off the saber trail + pm->gent->client->saberTrail.inAction = qfalse; + pm->gent->client->saberTrail.duration = 75; + } + else + {//turn on the saber trail + pm->gent->client->saberTrail.inAction = qtrue; + pm->gent->client->saberTrail.duration = 150; + } } return; } @@ -7117,6 +7665,7 @@ static void PM_Weapon( void ) return; } + // start the animation even if out of ammo switch(pm->ps->weapon) { @@ -7173,7 +7722,7 @@ static void PM_Weapon( void ) break; case WP_BLASTER: - PM_SetAnim( pm, SETANIM_TORSO, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); + PM_SetAnim( pm, SETANIM_TORSO, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART); break; case WP_DISRUPTOR: @@ -7184,7 +7733,7 @@ static void PM_Weapon( void ) } else {//in primary fire mode - PM_SetAnim( pm, SETANIM_TORSO, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); + PM_SetAnim( pm, SETANIM_TORSO, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART); } break; @@ -7199,7 +7748,14 @@ static void PM_Weapon( void ) } else { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_THERMAL_THROW,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + if ( cg.renderingThirdPerson ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_THERMAL_THROW,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + } } break; @@ -7268,6 +7824,7 @@ static void PM_Weapon( void ) return; } } + if ( pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0 ) {//FIXME: this is going to fire off one frame before you expect, actually // Clear these out since we're not actually firing yet @@ -7328,6 +7885,7 @@ static void PM_Weapon( void ) } pm->ps->weaponTime += addTime; + pm->ps->lastShotTime = level.time;//so we know when the last time we fired our gun is } @@ -7534,7 +8092,7 @@ void PM_AdjustAttackStates( pmove_t *pm ) } // disruptor alt-fire should toggle the zoom mode, but only bother doing this for the player? - if ( pm->ps->weapon == WP_DISRUPTOR && pm->gent && pm->gent->s.number == 0 ) + if ( pm->ps->weapon == WP_DISRUPTOR && pm->gent && pm->gent->s.number == 0 && pm->ps->weaponstate != WEAPON_DROPPING ) { // we are not alt-firing yet, but the alt-attack button was just pressed and // we either are ducking ( in which case we don't care if they are moving )...or they are not ducking...and also not moving right/forward. @@ -7548,7 +8106,7 @@ void PM_AdjustAttackStates( pmove_t *pm ) // not already zooming, so do it now cg.zoomMode = 2; cg.zoomLocked = qfalse; - cg_zoomFov = 80.0f;//cg.overrides.fov ? cg.overrides.fov : cg_fov.value; + cg_zoomFov = 80.0f;//(cg.overrides.active&CG_OVERRIDE_FOV) ? cg.overrides.fov : cg_fov.value; } else if ( cg.zoomMode == 2 ) { diff --git a/code/game/bg_public.h b/code/game/bg_public.h index 1b3e8a5..40a9ea8 100644 --- a/code/game/bg_public.h +++ b/code/game/bg_public.h @@ -182,45 +182,38 @@ typedef enum { // entityState_t->eFlags -#define EF_DEAD 0x00000001 // don't draw a foe marker over players with EF_DEAD -#define EF_NPC 0x00000002 // An NPC -#define EF_TELEPORT_BIT 0x00000004 // toggled every time the origin abruptly changes -#define EF_SHADER_ANIM 0x00000008 // Animating shader (by s.frame) -#define EF_BOUNCE 0x00000010 // for missiles -#define EF_BOUNCE_HALF 0x00000020 // for missiles -#define EF_MISSILE_STICK 0x00000040 // missiles that stick to the wall. -#define EF_NODRAW 0x00000080 // may have an event, but no model (unspawned items) -#define EF_FIRING 0x00000100 // for lightning gun -#define EF_ALT_FIRING 0x00000200 // for alt-fires, mostly for lightning guns though -//#define EF_MOVER_STOP 0x00000400 // will push otherwise -#define EF_NO_TED 0x00000400 // won't show up on TED unless actively scan with tricorder -#define EF_AUTO_SIZE 0x00000800 //CG_Ents will create the mins & max itself based on model bounds -//#define EF_TALK 0x00001000 // draw a talk balloon -#define EF_BOUNCE_SHRAPNEL 0x00001000 // special shrapnel flag -#define EF_CONNECTION 0x00002000 // draw a connection trouble sprite -#define EF_ANIM_ALLFAST 0x00004000 // automatically cycle through all frames at 10hz -#define EF_ANIM_ONCE 0x00008000 // cycle through all frames just once then stop - -#define EF_PLANTED_CHARGE 0x00010000 // For detpack charge -#define EF_PROX_TRIP 0x00020000 // Proximity trip mine has been activated -#define EF_LOCKED_TO_WEAPON 0x00040000 // When we use an emplaced weapon, we turn this on to lock us to that weapon -#define EF_FIXING 0x00080000 // Etherian fixits set this on their "enemy" when they start to fix him -#define EF_SCALE_UP 0x00100000 // When something scales up -#define EF_SCALE_DOWN 0x00200000 // When something scales down -#define EF_BEAM_IN 0x00400000 // When something beams in -#define EF_BEAM_OUT 0x00800000 // When something beams out -//#define EF_SCAV_BEAM_OUT 0x01000000 // When scav beams something out -#define EF_IN_ATST 0x01000000 // Driving an ATST -#define EF_DISINTEGRATION 0x02000000 // Disruptor effect -#define EF_DISINT_1 0x02000000 // Disintegration effect 1 -#define EF_DISINT_2 0x04000000 // Disintegration effect 2 -#define EF_DISINT_3 0x08000000 // Disintegration effect 3 -#define EF_DISABLE_SHADER_ANIM 0x10000000 // Normally shader animation chugs along, but movers can force shader animation to be on frame 1 -#define EF_FORCE_GRIPPED 0x20000000 // Force gripped effect -#define EF_DISINT_6 0x40000000 // - -#define EF_BANK_STRAFE 0x80000000 // hunterseeker- shared with next -#define EF_BLOCKED_MOVER 0x80000000 // for movers that are blocked - shared with previous +#define EF_DEAD 0x00000001 // don't draw a foe marker over players with EF_DEAD +#define EF_NPC 0x00000002 // An NPC +#define EF_TELEPORT_BIT 0x00000004 // toggled every time the origin abruptly changes +#define EF_SHADER_ANIM 0x00000008 // Animating shader (by s.frame) +#define EF_BOUNCE 0x00000010 // for missiles +#define EF_BOUNCE_HALF 0x00000020 // for missiles +#define EF_MISSILE_STICK 0x00000040 // missiles that stick to the wall. +#define EF_NODRAW 0x00000080 // may have an event, but no model (unspawned items) +#define EF_FIRING 0x00000100 // for lightning gun +#define EF_ALT_FIRING 0x00000200 // for alt-fires, mostly for lightning guns though +#define EF_NO_TED 0x00000400 // won't show up on TED unless actively scan with tricorder +#define EF_AUTO_SIZE 0x00000800 // CG_Ents will create the mins & max itself based on model bounds +#define EF_BOUNCE_SHRAPNEL 0x00001000 // special shrapnel flag +#define EF_CONNECTION 0x00002000 // draw a connection trouble sprite +#define EF_ANIM_ALLFAST 0x00004000 // automatically cycle through all frames at 10hz +#define EF_ANIM_ONCE 0x00008000 // cycle through all frames just once then stop +#define EF_PLANTED_CHARGE 0x00010000 // For detpack charge +#define EF_PROX_TRIP 0x00020000 // Proximity trip mine has been activated +#define EF_LOCKED_TO_WEAPON 0x00040000 // When we use an emplaced weapon, we turn this on to lock us to that weapon +//#define EF_use_me 0x00080000 // Not used +//#define EF_use_me 0x00100000 // Not used +//#define EF_use_me 0x00200000 // Not used +//#define EF_use_me 0x00400000 // Not used +//#define EF_use_me 0x00800000 // Not used +#define EF_IN_ATST 0x01000000 // Driving an ATST +#define EF_DISINTEGRATION 0x02000000 // Disruptor effect +//#define EF_use_me 0x04000000 // Not used +//#define EF_use_me 0x08000000 // Not used +#define EF_DISABLE_SHADER_ANIM 0x10000000 // Normally shader animation chugs along, but movers can force shader animation to be on frame 1 +#define EF_FORCE_GRIPPED 0x20000000 // Force gripped effect +//#define EF_use_me 0x40000000 // Not used +#define EF_BLOCKED_MOVER 0x80000000 // for movers that are blocked - shared with previous typedef enum { PW_NONE, @@ -231,7 +224,7 @@ typedef enum { PW_UNCLOAKING, PW_DISRUPTION, PW_GALAK_SHIELD, - PW_WEAPON_OVERCHARGE, +// PW_WEAPON_OVERCHARGE, PW_SEEKER, PW_SHOCKED,//electricity effect PW_DISINT_2,//ghost @@ -518,7 +511,6 @@ typedef struct gitem_s { char *world_model; char *icon; - char *pickup_name; // for printing on pickup int quantity; // for ammo how much, or duration of powerup itemType_t giType; // IT_* flags @@ -556,7 +548,7 @@ extern ammoData_t ammoData[]; //============================================================================== -gitem_t *FindItem( const char *pickupName ); +gitem_t *FindItem( const char *className ); gitem_t *FindItemForWeapon( weapon_t weapon ); gitem_t *FindItemForInventory( int inv ); diff --git a/code/game/bg_slidemove.cpp b/code/game/bg_slidemove.cpp index c89732f..c302dff 100644 --- a/code/game/bg_slidemove.cpp +++ b/code/game/bg_slidemove.cpp @@ -45,7 +45,10 @@ qboolean PM_SlideMove( float gravMod ) { if ( gravMod ) { VectorCopy( pm->ps->velocity, endVelocity ); - endVelocity[2] -= pm->ps->gravity * pml.frametime * gravMod; + if ( !(pm->ps->eFlags&EF_FORCE_GRIPPED) ) + { + endVelocity[2] -= pm->ps->gravity * pml.frametime * gravMod; + } pm->ps->velocity[2] = ( pm->ps->velocity[2] + endVelocity[2] ) * 0.5; primal_velocity[2] = endVelocity[2]; if ( pml.groundPlane ) { diff --git a/code/game/boltons.h b/code/game/boltons.h deleted file mode 100644 index 522b346..0000000 --- a/code/game/boltons.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef __BOLTONS_H__ -#define __BOLTONS_H__ - -#define MAX_GAME_BOLTONS 64 - -typedef struct boltOn_s -{//a tagged object - char name[32]; //If there is a name, it is considered active (look up in boltOns.cfg) - modelInfo_t model; - targetModel_t targetModel;// name of object to attach to - char targetTag[20];// name of tag on target object to attach to - vec3_t angleOffsets;// angle offsets to apply when attaching to target object's tag - vec3_t originOffsets;// angle offsets to apply when attaching to target object's tag -} boltOn_t; - -extern boltOn_t knownBoltOns[MAX_GAME_BOLTONS]; -extern int numBoltOns; - -extern void G_RegisterBoltOns(void); -//extern byte G_AddBoltOn( gentity_t *ent, const char *boltOnName ); -//extern void G_RemoveBoltOn( gentity_t *ent, const char *boltOnName ); -//extern void Q3_SetActiveBoltOn( int entID, const char *boltOnName ); -//extern void Q3_SetActiveBoltOnStartFrame( int entID, int startFrame ); -//extern void Q3_SetActiveBoltOnEndFrame( int entID, int endFrame ); -//extern void Q3_SetActiveBoltOnAnimLoop( int entID, qboolean loopAnim ); -extern void G_DropBoltOn( gentity_t *ent, const char *boltOnName ); -extern byte G_BoltOnNumberForName( gentity_t *ent, const char *boltOnName ); -extern void G_InitBoltOnData ( gentity_t *ent ); - -#endif// #ifndef __BOLTONS_H__ diff --git a/code/game/channels.h b/code/game/channels.h index 66d8008..4ed6b0e 100644 --- a/code/game/channels.h +++ b/code/game/channels.h @@ -10,8 +10,8 @@ typedef enum //# soundChannel_e CHAN_AMBIENT,//## %s !!"W:\game\base\!!sound\*.wav;*.mp3" # added for ambient sounds CHAN_LOCAL_SOUND, //## %s !!"W:\game\base\!!sound\*.wav;*.mp3" #chat messages, etc CHAN_ANNOUNCER, //## %s !!"W:\game\base\!!sound\*.wav;*.mp3" #announcer voices, etc + CHAN_LESS_ATTEN, //## %s !!"W:\game\base\!!sound\*.wav;*.mp3" #attenuates similar to chan_voice, but uses empty channel auto-pick behaviour CHAN_MENU1, //## %s !!"W:\game\base\!!sound\*.wav;*.mp3" #menu stuff, etc - CHAN_MENU2, //## %s !!"W:\game\base\!!sound\*.wav;*.mp3" #menu stuff, etc CHAN_VOICE_GLOBAL, //## %s !!"W:\game\base\!!sound\voice\*.wav;*.mp3" # Causes mouth animation and is broadcast, like announcer } soundChannel_t; diff --git a/code/game/g_active.cpp b/code/game/g_active.cpp index 9d0a6cb..68650eb 100644 --- a/code/game/g_active.cpp +++ b/code/game/g_active.cpp @@ -7,6 +7,11 @@ #include "..\cgame\cg_local.h" #include "Q3_Interface.h" #include "wp_saber.h" +#include "g_icarus.h" + +#ifdef _DEBUG + #include +#endif //_DEBUG #define SLOWDOWN_DIST 128.0f #define MIN_NPC_SPEED 16.0f @@ -21,6 +26,7 @@ extern void WP_SaberReflectCheck( gentity_t *self, usercmd_t *ucmd ); extern void WP_SaberUpdate( gentity_t *self, usercmd_t *ucmd ); extern void WP_SaberStartMissileBlockCheck( gentity_t *self, usercmd_t *ucmd ); extern void WP_ForcePowersUpdate( gentity_t *self, usercmd_t *ucmd ); +extern void WP_SaberInitBladeData( gentity_t *ent ); extern gentity_t *SeekerAcquiresTarget ( gentity_t *ent, vec3_t pos ); extern void FireSeeker( gentity_t *owner, gentity_t *target, vec3_t origin, vec3_t dir ); extern qboolean InFront( vec3_t spot, vec3_t from, vec3_t fromAngles, float threshHold = 0.0f ); @@ -31,6 +37,7 @@ extern qboolean PM_AdjustAnglesForSpinningFlip( gentity_t *ent, usercmd_t *ucmd, extern qboolean PM_AdjustAnglesForBackAttack( gentity_t *ent, usercmd_t *ucmd ); extern qboolean PM_AdjustAnglesForSaberLock( gentity_t *ent, usercmd_t *ucmd ); extern qboolean PM_AdjustAnglesForKnockdown( gentity_t *ent, usercmd_t *ucmd, qboolean angleClampOnly ); +extern qboolean PM_HasAnimation( gentity_t *ent, int animation ); extern qboolean PM_SpinningSaberAnim( int anim ); extern qboolean PM_SaberInAttack( int move ); extern int PM_AnimLength( int index, animNumber_t anim ); @@ -40,8 +47,10 @@ extern void PM_CmdForRoll( int anim, usercmd_t *pCmd ); extern qboolean PM_CrouchAnim( int anim ); extern qboolean PM_FlippingAnim( int anim ); extern qboolean PM_InCartwheel( int anim ); +extern qboolean PM_StandingAnim( int anim ); extern qboolean PM_InForceGetUp( playerState_t *ps ); extern void G_CreateG2AttachedWeaponModel( gentity_t *ent, const char *weaponModel ); +extern qboolean FlyingCreature( gentity_t *ent ); extern bool in_camera; extern qboolean player_locked; @@ -225,7 +234,9 @@ void G_ChooseLookEnemy( gentity_t *self, usercmd_t *ucmd ) float rating, bestRating = 0.0f; //FIXME: no need to do this in 1st person? + fwdangles[0] = 0; //Must initialize data! fwdangles[1] = self->client->ps.viewangles[1]; + fwdangles[2] = 0; AngleVectors( fwdangles, forward, NULL, NULL ); VectorCopy( self->currentOrigin, center ); @@ -523,12 +534,12 @@ void DoImpact( gentity_t *self, gentity_t *other, qboolean damageSelf ) magnitude = VectorLength( velocity ) * my_mass / 50; - if ( !self->client || self->client->ps.lastOnGround+300client->ps.lastOnGround+100 < level.time && other->material >= MAT_GLASS ) ) + if ( !self->client || self->client->ps.lastOnGround+300client->ps.lastOnGround+100 < level.time ) ) { vec3_t dir1, dir2; float force = 0, dot; - if ( other->material >= MAT_GLASS || ((other->svFlags&SVF_BBRUSH)&&(other->spawnflags&4/*THIN*/)) )//(other->absmax[0]-other->absmin[0]<=32||other->absmax[1]-other->absmin[1]<=32||other->absmax[2]-other->absmin[2]<=32)) ) + if ( other->material == MAT_GLASS || other->material == MAT_GLASS_METAL || other->material == MAT_GRATE1 || ((other->svFlags&SVF_BBRUSH)&&(other->spawnflags&4/*THIN*/)) )//(other->absmax[0]-other->absmin[0]<=32||other->absmax[1]-other->absmin[1]<=32||other->absmax[2]-other->absmin[2]<=32)) ) {//glass and thin breakable brushes (axially aligned only, unfortunately) take more impact damage magnitude *= 2; } @@ -599,7 +610,7 @@ void DoImpact( gentity_t *self, gentity_t *other, qboolean damageSelf ) { if ( !self->client || !other->s.number || !other->client ) {//aw, fuck it, clients no longer take impact damage from other clients, unless you're the player - G_Damage( other, self, self, velocity, self->currentOrigin, force, 0, MOD_IMPACT ); + G_Damage( other, self, self, velocity, self->currentOrigin, force, DAMAGE_NO_ARMOR, MOD_IMPACT ); } else { @@ -665,7 +676,7 @@ void DoImpact( gentity_t *self, gentity_t *other, qboolean damageSelf ) {//FIXME: for now Jedi take no falling damage, but really they should if pushed off? magnitude = 0; } - G_Damage( self, NULL, NULL, NULL, self->currentOrigin, magnitude/2, 0, MOD_FALLING );//FIXME: MOD_IMPACT + G_Damage( self, NULL, NULL, NULL, self->currentOrigin, magnitude/2, DAMAGE_NO_ARMOR, MOD_FALLING );//FIXME: MOD_IMPACT } } } @@ -747,6 +758,13 @@ void G_TouchTriggersLerped( gentity_t *ent ) { } } +#ifdef _DEBUG + for ( int j = 0; j < 3; j++ ) + { + assert( !_isnan(ent->currentOrigin[j])); + assert( !_isnan(ent->lastOrigin[j])); + } +#endif// _DEBUG VectorSubtract( ent->currentOrigin, ent->lastOrigin, diff ); dist = VectorNormalize( diff ); @@ -973,30 +991,55 @@ void G_MoverTouchPushTriggers( gentity_t *ent, vec3_t oldOrg ) } } +void G_MatchPlayerWeapon( gentity_t *ent ) +{ + if ( g_entities[0].inuse && g_entities[0].client ) + {//player is around + int newWeap; + if ( g_entities[0].client->ps.weapon > WP_DET_PACK ) + { + newWeap = WP_BRYAR_PISTOL; + } + else + { + newWeap = g_entities[0].client->ps.weapon; + } + if ( newWeap != WP_NONE && ent->client->ps.weapon != newWeap ) + { + if ( ent->weaponModel != -1 ) + { + gi.G2API_RemoveGhoul2Model(ent->ghoul2, ent->weaponModel); + } + ent->client->ps.stats[STAT_WEAPONS] = ( 1 << newWeap ); + ent->client->ps.ammo[weaponData[newWeap].ammoIndex] = 999; + ChangeWeapon( ent, newWeap ); + ent->client->ps.weapon = newWeap; + ent->client->ps.weaponstate = WEAPON_READY; + if ( newWeap == WP_SABER ) + { + //FIXME: AddSound/Sight Event + WP_SaberInitBladeData( ent ); + G_CreateG2AttachedWeaponModel( ent, ent->client->ps.saberModel ); + ent->client->ps.saberActive = g_entities[0].client->ps.saberActive; + ent->client->ps.saberLength = g_entities[0].client->ps.saberLength; + ent->client->ps.saberAnimLevel = g_entities[0].client->ps.saberAnimLevel; + } + else + { + G_CreateG2AttachedWeaponModel( ent, weaponData[newWeap].weaponMdl ); + } + } + } +} + void G_NPCMunroMatchPlayerWeapon( gentity_t *ent ) { //special uber hack for cinematic Munro's to match player's weapon if ( !in_camera ) { if ( ent && ent->client && ent->NPC && (ent->NPC->aiFlags&NPCAI_MATCHPLAYERWEAPON) ) - {//we're a Munro NPC - int newWeap; - if ( g_entities[0].client->ps.weapon == WP_SABER || g_entities[0].client->ps.weapon > WP_DET_PACK ) - { - newWeap = WP_BRYAR_PISTOL; - } - else - { - newWeap = g_entities[0].client->ps.weapon; - } - if ( newWeap != WP_NONE && ent->client->ps.weapon != newWeap ) - { - ent->client->ps.stats[STAT_WEAPONS] = ( 1 << newWeap ); - ent->client->ps.ammo[weaponData[newWeap].ammoIndex] = 999; - ChangeWeapon( ent, newWeap ); - ent->client->ps.weapon = newWeap; - ent->client->ps.weaponstate = WEAPON_READY; - } + {//we're a Kyle NPC + G_MatchPlayerWeapon( ent ); } } } @@ -1185,8 +1228,10 @@ void ClientEvents( gentity_t *ent, int oldEventSequence ) { } } -void G_CheckClampUcmd( gentity_t *ent, usercmd_t *ucmd ) +qboolean G_CheckClampUcmd( gentity_t *ent, usercmd_t *ucmd ) { + qboolean overridAngles = qfalse; + if ( (!ent->s.number&&ent->aimDebounceTime>level.time) || (ent->client->ps.pm_time && (ent->client->ps.pm_flags&PMF_TIME_KNOCKBACK)) || ent->forcePushTime > level.time ) @@ -1201,20 +1246,21 @@ void G_CheckClampUcmd( gentity_t *ent, usercmd_t *ucmd ) } } - PM_AdjustAnglesForKnockdown( ent, ucmd, qfalse ); + overridAngles = (PM_AdjustAnglesForKnockdown( ent, ucmd, qfalse )?qtrue:overridAngles); if ( ent->client->ps.saberLockTime > level.time ) { ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0; if ( ent->client->ps.saberLockTime - level.time > SABER_LOCK_DELAYED_TIME ) {//2 second delay before either can push + //FIXME: base on difficulty ucmd->buttons = 0; } else { ucmd->buttons &= ~(ucmd->buttons&~BUTTON_ATTACK); } - PM_AdjustAnglesForSaberLock( ent, ucmd ); + overridAngles = (PM_AdjustAnglesForSaberLock( ent, ucmd )?qtrue:overridAngles); if ( ent->NPC ) { VectorClear( ent->client->ps.moveDir ); @@ -1247,7 +1293,7 @@ void G_CheckClampUcmd( gentity_t *ent, usercmd_t *ucmd ) || ent->client->ps.saberMove == LS_A_BACKSTAB ) {//can't move or turn during back attacks ucmd->forwardmove = ucmd->rightmove = 0; - if ( PM_AdjustAnglesForBackAttack( ent, ucmd ) ) + if ( (overridAngles = (PM_AdjustAnglesForBackAttack( ent, ucmd )?qtrue:overridAngles)) == qtrue ) { //pull back the view if ( !ent->s.number ) @@ -1263,6 +1309,7 @@ void G_CheckClampUcmd( gentity_t *ent, usercmd_t *ucmd ) {//ending anim backDist = ((animLength-elapsedTime)/animLength)*120.0f; } + cg.overrides.active |= CG_OVERRIDE_3RD_PERSON_RNG; cg.overrides.thirdPersonRange = cg_thirdPersonRange.value+backDist; } } @@ -1284,12 +1331,17 @@ void G_CheckClampUcmd( gentity_t *ent, usercmd_t *ucmd ) {//ending anim backDist = ((animLength-elapsedTime)/animLength)*120.0f; } + cg.overrides.active |= CG_OVERRIDE_3RD_PERSON_RNG; cg.overrides.thirdPersonRange = cg_thirdPersonRange.value+backDist; } } else if ( !ent->s.number ) { - cg.overrides.thirdPersonRange = 0; + if ( ent->client->NPC_class != CLASS_ATST ) + { + cg.overrides.active &= ~CG_OVERRIDE_3RD_PERSON_RNG; + cg.overrides.thirdPersonRange = 0; + } } @@ -1320,7 +1372,9 @@ void G_CheckClampUcmd( gentity_t *ent, usercmd_t *ucmd ) } } - PM_AdjustAngleForWallRun( ent, ucmd, qtrue ); + overridAngles = (PM_AdjustAngleForWallRun( ent, ucmd, qtrue )?qtrue:overridAngles); + + return overridAngles; } void BG_AddPushVecToUcmd(gentity_t *self, usercmd_t *ucmd) @@ -1618,11 +1672,34 @@ void RunEmplacedWeapon( gentity_t *ent, usercmd_t **ucmd ) { if (( (*ucmd)->buttons & BUTTON_USE || (*ucmd)->forwardmove < 0 ) && ent->owner && ent->owner->delay + 500 < level.time ) { + ent->owner->s.loopSound = 0; + ExitEmplacedWeapon( ent ); (*ucmd)->buttons &= ~BUTTON_USE; + + G_Sound( ent, G_SoundIndex( "sound/weapons/emplaced/emplaced_dismount.mp3" )); } else { + // this is a crappy way to put sounds on a moving emplaced gun.... + if ( ent->owner ) + { + if ( !VectorCompare( ent->owner->pos3, ent->owner->movedir )) + { + ent->owner->s.loopSound = G_SoundIndex( "sound/weapons/emplaced/emplaced_move_lp.wav" ); + ent->owner->fly_sound_debounce_time = level.time; + } + else + { + if ( ent->owner->fly_sound_debounce_time + 100 <= level.time ) + { + ent->owner->s.loopSound = 0; + } + } + + VectorCopy( ent->owner->pos3, ent->owner->movedir ); + } + // don't allow movement, weapon switching, and most kinds of button presses (*ucmd)->forwardmove = 0; (*ucmd)->rightmove = 0; @@ -1630,6 +1707,11 @@ void RunEmplacedWeapon( gentity_t *ent, usercmd_t **ucmd ) (*ucmd)->buttons &= (BUTTON_ATTACK|BUTTON_ALT_ATTACK); (*ucmd)->weapon = ent->client->ps.weapon; //WP_EMPLACED_GUN; + + if ( ent->health <= 0 ) + { + ExitEmplacedWeapon( ent ); + } } } @@ -1639,12 +1721,133 @@ void G_StopCinematicSkip( void ) gi.cvar_set("skippingCinematic", "0"); gi.cvar_set("timescale", "1"); } + void G_StartCinematicSkip( void ) { - // no... so start skipping... - gi.cvar_set("skippingCinematic", "1"); - gi.cvar_set("timescale", "100"); + + if (cinematicSkipScript[0]) + { + ICARUS_RunScript( &g_entities[0], va( "%s/%s", Q3_SCRIPT_DIR, cinematicSkipScript ) ); + memset( cinematicSkipScript, 0, sizeof( cinematicSkipScript ) ); + gi.cvar_set("skippingCinematic", "1"); + gi.cvar_set("timescale", "100"); + } + else + { + // no... so start skipping... + gi.cvar_set("skippingCinematic", "1"); + gi.cvar_set("timescale", "100"); + } } + +void G_CheckClientIdle( gentity_t *ent, usercmd_t *ucmd ) +{ + if ( !ent || !ent->client || ent->health <= 0 ) + { + return; + } + if ( !ent->s.number && ( !cg.renderingThirdPerson || cg.zoomMode ) ) + { + if ( ent->client->idleTime < level.time ) + { + ent->client->idleTime = level.time; + } + return; + } + if ( !VectorCompare( vec3_origin, ent->client->ps.velocity ) + || ucmd->buttons || ucmd->forwardmove || ucmd->rightmove || ucmd->upmove + || !PM_StandingAnim( ent->client->ps.legsAnim ) + || ent->enemy + || ent->client->ps.legsAnimTimer + || ent->client->ps.torsoAnimTimer ) + {//FIXME: also check for turning? + if ( !VectorCompare( vec3_origin, ent->client->ps.velocity ) + || ucmd->buttons || ucmd->forwardmove || ucmd->rightmove || ucmd->upmove + || ent->enemy ) + { + //if in an idle, break out + switch ( ent->client->ps.legsAnim ) + { + case BOTH_STAND1IDLE1: + case BOTH_STAND2IDLE1: + case BOTH_STAND2IDLE2: + case BOTH_STAND3IDLE1: + case BOTH_STAND4IDLE1: + ent->client->ps.legsAnimTimer = 0; + break; + } + switch ( ent->client->ps.torsoAnim ) + { + case BOTH_STAND1IDLE1: + case BOTH_STAND2IDLE1: + case BOTH_STAND2IDLE2: + case BOTH_STAND3IDLE1: + case BOTH_STAND4IDLE1: + ent->client->ps.torsoAnimTimer = 0; + break; + } + } + // + if ( ent->client->idleTime < level.time ) + { + ent->client->idleTime = level.time; + } + } + else if ( level.time - ent->client->idleTime > 5000 ) + {//been idle for 5 seconds + int idleAnim = -1; + switch ( ent->client->ps.legsAnim ) + { + case BOTH_STAND1: + idleAnim = BOTH_STAND1IDLE1; + break; + case BOTH_STAND2: + idleAnim = Q_irand(BOTH_STAND2IDLE1,BOTH_STAND2IDLE2); + break; + case BOTH_STAND3: + idleAnim = BOTH_STAND3IDLE1; + break; + case BOTH_STAND4: + idleAnim = BOTH_STAND4IDLE1; + break; + } + if ( idleAnim != -1 && PM_HasAnimation( ent, idleAnim ) ) + { + NPC_SetAnim( ent, SETANIM_BOTH, idleAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + //don't idle again after this anim for a while + ent->client->idleTime = level.time + PM_AnimLength( ent->client->clientInfo.animFileIndex, (animNumber_t)idleAnim ) + Q_irand( 0, 2000 ); + } + } +} + +void G_CheckMovingLoopingSounds( gentity_t *ent, usercmd_t *ucmd ) +{ + if ( ent->client ) + { + if ( (ent->NPC&&!VectorCompare( vec3_origin, ent->client->ps.moveDir ))//moving using moveDir + || ucmd->forwardmove || ucmd->rightmove//moving using ucmds + || (ucmd->upmove&&FlyingCreature( ent ))//flier using ucmds to move + || (FlyingCreature( ent )&&!VectorCompare( vec3_origin, ent->client->ps.velocity )&&ent->health>0))//flier using velocity to move + { + if ( ent->client->NPC_class == CLASS_R2D2 ) + { + ent->s.loopSound = G_SoundIndex( "sound/chars/r2d2/misc/r2_move_lp.wav" ); + } + if ( ent->client->NPC_class == CLASS_R5D2 ) + { + ent->s.loopSound = G_SoundIndex( "sound/chars/r2d2/misc/r2_move_lp2.wav" ); + } + } + else + {//not moving under your own control, stop loopSound + if ( ent->client->NPC_class == CLASS_R2D2 || ent->client->NPC_class == CLASS_R5D2 ) + { + ent->s.loopSound = 0; + } + } + } +} + /* ============== ClientThink @@ -1886,60 +2089,67 @@ extern cvar_t *g_skippingcin; && !(groundEnt->client->ps.eFlags&EF_LOCKED_TO_WEAPON) && !inSpinFlipAttack ) {//landed on a live client who is on the ground, jump off them and knock them down - if ( !PM_InRoll( &ent->client->ps ) - && !PM_FlippingAnim( ent->client->ps.legsAnim ) ) + if ( ent->health > 0 ) { - if ( ent->s.number && ent->s.weapon == WP_SABER ) + if ( !PM_InRoll( &ent->client->ps ) + && !PM_FlippingAnim( ent->client->ps.legsAnim ) ) { - ent->client->ps.forceJumpCharge = 320;//FIXME: calc this intelligently? - } - else if ( !ucmd->upmove ) - {//if not ducking (which should cause a roll), then jump - ucmd->upmove = 127; - } - if ( !ucmd->forwardmove && !ucmd->rightmove ) - {// If not moving, don't want to jump straight up - //FIXME: trace for clear di? - if ( !Q_irand( 0, 3 ) ) + if ( ent->s.number && ent->s.weapon == WP_SABER ) { - ucmd->forwardmove = 127; + ent->client->ps.forceJumpCharge = 320;//FIXME: calc this intelligently? } - else if ( !Q_irand( 0, 3 ) ) - { - ucmd->forwardmove = -127; + else if ( !ucmd->upmove ) + {//if not ducking (which should cause a roll), then jump + ucmd->upmove = 127; } - else if ( !Q_irand( 0, 1 ) ) - { - ucmd->rightmove = 127; + if ( !ucmd->forwardmove && !ucmd->rightmove ) + {// If not moving, don't want to jump straight up + //FIXME: trace for clear di? + if ( !Q_irand( 0, 3 ) ) + { + ucmd->forwardmove = 127; + } + else if ( !Q_irand( 0, 3 ) ) + { + ucmd->forwardmove = -127; + } + else if ( !Q_irand( 0, 1 ) ) + { + ucmd->rightmove = 127; + } + else + { + ucmd->rightmove = -127; + } } - else - { - ucmd->rightmove = -127; + if ( !ent->s.number && ucmd->upmove < 0 ) + {//player who should roll- force it + int rollAnim = BOTH_ROLL_F; + if ( ucmd->forwardmove >= 0 ) + { + rollAnim = BOTH_ROLL_F; + } + else if ( ucmd->forwardmove < 0 ) + { + rollAnim = BOTH_ROLL_B; + } + else if ( ucmd->rightmove > 0 ) + { + rollAnim = BOTH_ROLL_R; + } + else if ( ucmd->rightmove < 0 ) + { + rollAnim = BOTH_ROLL_L; + } + NPC_SetAnim(ent,SETANIM_BOTH,rollAnim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); + G_AddEvent( ent, EV_ROLL, 0 ); + ent->client->ps.saberMove = LS_NONE; } } - if ( !ent->s.number && ucmd->upmove < 0 ) - {//player who should roll- force it - int rollAnim = BOTH_ROLL_F; - if ( ucmd->forwardmove >= 0 ) - { - rollAnim = BOTH_ROLL_F; - } - else if ( ucmd->forwardmove < 0 ) - { - rollAnim = BOTH_ROLL_B; - } - else if ( ucmd->rightmove > 0 ) - { - rollAnim = BOTH_ROLL_R; - } - else if ( ucmd->rightmove < 0 ) - { - rollAnim = BOTH_ROLL_L; - } - NPC_SetAnim(ent,SETANIM_BOTH,rollAnim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); - G_AddEvent( ent, EV_ROLL, 0 ); - ent->client->ps.saberMove = LS_NONE; - } + } + else + {//a corpse? Shit + ent->clipmask &= ~CONTENTS_BODY; } //FIXME: need impact sound event GEntity_PainFunc( groundEnt, ent, ent, groundEnt->currentOrigin, 0, MOD_CRUSH ); @@ -2115,8 +2325,17 @@ extern cvar_t *g_skippingcin; else { //Slow down on turns - don't orbit!!! - float turndelta = (180 - fabs( AngleDelta( ent->currentAngles[YAW], ent->NPC->desiredYaw ) ))/180; - + float turndelta = 0; + // if the NPC is locked into a Yaw, we want to check the lockedDesiredYaw...otherwise the NPC can't walk backwards, because it always thinks it trying to turn according to desiredYaw + if( client->renderInfo.renderFlags & RF_LOCKEDANGLE ) // yeah I know the RF_ flag is a pretty ugly hack... + { + turndelta = (180 - fabs( AngleDelta( ent->currentAngles[YAW], ent->NPC->lockedDesiredYaw ) ))/180; + } + else + { + turndelta = (180 - fabs( AngleDelta( ent->currentAngles[YAW], ent->NPC->desiredYaw ) ))/180; + } + if ( turndelta < 0.75f ) { client->ps.speed = 0; @@ -2320,11 +2539,13 @@ extern cvar_t *g_skippingcin; //NEED to do this every frame, since these overrides do not go into the save/load data if ( ent->client->ps.vehicleModel != 0 ) { + cg.overrides.active |= (CG_OVERRIDE_3RD_PERSON_RNG|CG_OVERRIDE_FOV); cg.overrides.thirdPersonRange = 240; cg.overrides.fov = 100; } else if ( client->ps.eFlags&EF_IN_ATST ) { + cg.overrides.active |= (CG_OVERRIDE_3RD_PERSON_RNG|CG_OVERRIDE_3RD_PERSON_POF|CG_OVERRIDE_3RD_PERSON_VOF); cg.overrides.thirdPersonRange = 240; if ( ent->client->ps.viewangles[PITCH] > 0 ) { @@ -2350,6 +2571,10 @@ extern cvar_t *g_skippingcin; cg.overrides.thirdPersonVertOffset = 200; } } + + //play/stop any looping sounds tied to controlled movement + G_CheckMovingLoopingSounds( ent, ucmd ); + //remember your last angles VectorCopy ( ent->client->ps.viewangles, ent->lastAngles ); // set up for pmove @@ -2499,6 +2724,8 @@ extern cvar_t *g_skippingcin; } } */ + //try some idle anims on ent if getting no input and not moving for some time + G_CheckClientIdle( ent, ucmd ); } /* @@ -2518,10 +2745,10 @@ void ClientThink( int clientNum, usercmd_t *ucmd ) { if ( ent->client->ps.viewEntity > 0 && ent->client->ps.viewEntity < ENTITYNUM_WORLD ) {//you're controlling another NPC - if ( (ucmd->buttons&BUTTON_BLOCKING) ) - {//firing gets you out of it FIXME: check some other button instead... like ESCAPE... so you could even have total control over an NPC? + if ( ucmd->upmove > 0 )//if ( (ucmd->buttons&BUTTON_BLOCKING) ) + {//jumping gets you out of it FIXME: check some other button instead... like ESCAPE... so you could even have total control over an NPC? G_ClearViewEntity( ent ); - ucmd->buttons = 0; + ucmd->upmove = 0;//ucmd->buttons = 0; } else { @@ -2533,6 +2760,15 @@ void ClientThink( int clientNum, usercmd_t *ucmd ) { ucmd->angles[YAW] = ANGLE2SHORT( ent->client->ps.viewangles[YAW] ) - ent->client->ps.delta_angles[YAW]; } } + else if ( ent->client->NPC_class == CLASS_ATST ) + { + if ( ucmd->upmove > 0 )//if ( (ucmd->buttons&BUTTON_BLOCKING) ) + {//get out of ATST + GEntity_UseFunc( ent->activator, ent, ent ); + ucmd->upmove = 0;//ucmd->buttons = 0; + } + } + if ( (ucmd->buttons&BUTTON_BLOCKING) ) { diff --git a/code/game/g_breakable.cpp b/code/game/g_breakable.cpp index 5e6843f..2e40ba5 100644 --- a/code/game/g_breakable.cpp +++ b/code/game/g_breakable.cpp @@ -159,7 +159,7 @@ void funcBBrushDieGo (gentity_t *self) //G_FreeEntity( self ); } -void funcBBrushDie (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc) +void funcBBrushDie (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags,int hitLoc) { self->takedamage = qfalse;//stop chain reaction runaway loops @@ -399,7 +399,7 @@ void misc_model_breakable_pain ( gentity_t *self, gentity_t *inflictor, gentity_ } } -void misc_model_breakable_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath,int hitLoc ) +void misc_model_breakable_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath,int dFlags,int hitLoc ) { int numChunks; float size = 0, scale; @@ -483,11 +483,19 @@ void misc_model_breakable_die( gentity_t *self, gentity_t *inflictor, gentity_t // Ok, we are allowed to explode, so do it now! if(self->splashDamage > 0 && self->splashRadius > 0) {//explode + vec3_t org; AddSightEvent( attacker, self->currentOrigin, 256, AEL_DISCOVERED, 100 ); AddSoundEvent( attacker, self->currentOrigin, 128, AEL_DISCOVERED ); //FIXME: specify type of explosion? (barrel, electrical, etc.) Also, maybe just use the explosion effect below since it's // a bit better? - G_RadiusDamage( self->currentOrigin, self, self->splashDamage, self->splashRadius, self, MOD_UNKNOWN ); + // up the origin a little for the damage check, because several models have their origin on the ground, so they don't alwasy do damage, not the optimal solution... + VectorCopy( self->currentOrigin, org ); + if ( self->mins[2] > -4 ) + {//origin is going to be below it or very very low in the model + //center the origin + org[2] = self->currentOrigin[2] + self->mins[2] + (self->maxs[2] - self->mins[2])/2.0f; + } + G_RadiusDamage( org, self, self->splashDamage, self->splashRadius, self, MOD_UNKNOWN ); if ( self->model && Q_stricmp( "models/map_objects/ships/tie_fighter.md3", self->model ) == 0 ) {//TEMP HACK for Tie Fighters- they're HUGE @@ -939,7 +947,7 @@ extern void CG_DoGlass( vec3_t verts[4], vec3_t normal, vec3_t dmgPt, vec3_t dmg extern cgs_t cgs; //----------------------------------------------------- -void funcGlassDie( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc ) +void funcGlassDie( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags,int hitLoc ) { vec3_t verts[4], normal; diff --git a/code/game/g_client.cpp b/code/game/g_client.cpp index f5b31fb..71bbd05 100644 --- a/code/game/g_client.cpp +++ b/code/game/g_client.cpp @@ -10,6 +10,8 @@ extern void Q3_DebugPrint( int level, const char *format, ... ); extern void WP_SaberInitBladeData( gentity_t *ent ); +extern void G_CreateG2AttachedWeaponModel( gentity_t *ent, const char *weaponModel ); +extern qboolean CheatsOk( gentity_t *ent ); // g_client.c -- client functions that don't happen every frame @@ -37,6 +39,7 @@ void SP_info_player_deathmatch(gentity_t *ent) { else { RegisterItem( FindItemForWeapon( WP_SABER ) ); //these are given in ClientSpawn(), but we register them now before cgame starts + G_SkinIndex("models/players/kyle/model_fpls2.skin"); //preache the skin used in cg_players.cpp } } @@ -95,7 +98,7 @@ qboolean SpotWouldTelefrag( gentity_t *spot, team_t checkteam ) for (i=0 ; iclient && hit->client->ps.stats[STAT_HEALTH] > 0 ) + if ( hit != spot && hit->client && hit->client->ps.stats[STAT_HEALTH] > 0 ) { if ( hit->contents & CONTENTS_BODY ) { @@ -307,8 +310,9 @@ void SetClientViewAngle( gentity_t *ent, vec3_t angle ) { int i; // set the delta angle - for (i=0 ; i<3 ; i++) { - ent->client->ps.delta_angles[i] = ANGLE2SHORT(angle[i]) - ent->client->pers.cmd_angles[i]; + for (i=0 ; i<3 ; i++) + { + ent->client->ps.delta_angles[i] = (ANGLE2SHORT(angle[i]) - ent->client->pers.cmd_angles[i])&0xffff; } VectorCopy( angle, ent->s.angles ); VectorCopy (ent->s.angles, ent->client->ps.viewangles); @@ -573,11 +577,11 @@ void ClientBegin( int clientNum, usercmd_t *cmd, SavedGameJustLoaded_e eSavedGam ent->client = client; client->pers.connected = CON_CONNECTED; - client->pers.enterTime = level.time; client->pers.teamState.state = TEAM_BEGIN; _VectorCopy( cmd->angles, client->pers.cmd_angles ); memset( &client->ps, 0, sizeof( client->ps ) ); + memset( &client->sess.missionStats, 0, sizeof( client->sess.missionStats ) ); // locate ent at a spawn point if ( ClientSpawn( ent, eSavedGameJustLoaded) ) // SavedGameJustLoaded_e @@ -701,6 +705,19 @@ void Player_RestoreFromPrevLevel(gentity_t *ent) gi.Cvar_VariableStringBuffer( "playerfp", s, sizeof(s) ); sscanf( s, "%i", &client->ps.forcePower); + + gi.Cvar_VariableStringBuffer( "plsa", s, sizeof(s) ); + sscanf( s, "%i", &client->ps.saberActive); + + gi.Cvar_VariableStringBuffer( "plcs", s, sizeof(s) ); + sscanf( s, "%i", &client->ps.saberAnimLevel); + + gi.Cvar_VariableStringBuffer( "plle", s, sizeof(s) ); + sscanf( s, "%i", &client->ps.saberLockEnemy); + + gi.Cvar_VariableStringBuffer( "pllt", s, sizeof(s) ); + sscanf( s, "%i", &client->ps.saberLockTime); + client->ps.forcePowerMax = FORCE_POWER_MAX; client->ps.forceGripEntityNum = ENTITYNUM_NONE; } @@ -825,15 +842,23 @@ qboolean G_SetG2PlayerModelInfo( gentity_t *ent, const char *modelName, const ch {//temp hack because only the gran are set up right so far ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*head_eyes"); ent->cervicalBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "cervical" ); + if ( !Q_stricmp("protocol", modelName ) ) + {//*sigh*, no thoracic bone + ent->gutBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "upper_lumbar"); + ent->chestBolt = ent->gutBolt; + } + else + { + ent->chestBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "thoracic"); + ent->gutBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "upper_lumbar"); + } ent->torsoBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "lower_lumbar"); - ent->gutBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "upper_lumbar"); - ent->chestBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "thoracic"); ent->crotchBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "pelvis"); ent->elbowLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*l_arm_elbow"); ent->elbowRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*r_arm_elbow"); ent->handLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*l_hand"); ent->handRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*r_hand"); - ent->kneeLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*hips_1_knee"); + ent->kneeLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*hips_l_knee"); ent->kneeRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*hips_r_knee"); ent->footLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*l_leg_foot"); ent->footRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*r_leg_foot"); @@ -868,7 +893,7 @@ qboolean G_SetG2PlayerModelInfo( gentity_t *ent, const char *modelName, const ch else if (!Q_stricmp( "mark1",modelName)) { ent->headBolt = -1; - ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash1"); // Blaster Gun 1 + ent->handRBolt = ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash1"); // Blaster Gun 1 ent->genericBolt2 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash2"); // Blaster Gun 2 ent->genericBolt3 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash3"); // Blaster Gun 3 ent->genericBolt4 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash4"); // Blaster Gun 4 @@ -877,7 +902,7 @@ qboolean G_SetG2PlayerModelInfo( gentity_t *ent, const char *modelName, const ch else if (!Q_stricmp( "mark2",modelName)) { ent->headBolt = -1; - ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash"); // Blaster Gun 1 + ent->handRBolt = ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash"); // Blaster Gun 1 } else if (!Q_stricmp( "atst",modelName) )//&& (ent->client->playerTeam != TEAM_PLAYER)) { @@ -894,7 +919,7 @@ qboolean G_SetG2PlayerModelInfo( gentity_t *ent, const char *modelName, const ch } else if ( !Q_stricmp( "minemonster", modelName )) { - ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*head_f1"); + ent->handRBolt = ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*head_f1"); } else if ( !Q_stricmp( "howler", modelName )) { @@ -939,6 +964,7 @@ qboolean G_SetG2PlayerModelInfo( gentity_t *ent, const char *modelName, const ch ent->upperLumbarBone = BONE_INDEX_INVALID; ent->lowerLumbarBone = BONE_INDEX_INVALID; ent->motionBone = BONE_INDEX_INVALID; + ent->hipsBone = BONE_INDEX_INVALID; ent->rootBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "model_root", qtrue ); ent->footLBone = BONE_INDEX_INVALID; ent->footRBone = BONE_INDEX_INVALID; @@ -952,9 +978,15 @@ qboolean G_SetG2PlayerModelInfo( gentity_t *ent, const char *modelName, const ch else if (!Q_strncmp( "probe", modelName, 5 )) { ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + if (ent->craniumBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + } ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "pelvis", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + if (ent->thoracicBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + } } else if (!Q_strncmp( "protocol",modelName,8)) { @@ -962,93 +994,174 @@ qboolean G_SetG2PlayerModelInfo( gentity_t *ent, const char *modelName, const ch else if (!Q_stricmp( "interrogator", modelName )) { ent->genericBone1 = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "left_arm", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->genericBone1, angles, BONE_ANGLES_POSTMULT, NEGATIVE_Y, NEGATIVE_X, NEGATIVE_Z, NULL ); + if (ent->genericBone1>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->genericBone1, angles, BONE_ANGLES_POSTMULT, NEGATIVE_Y, NEGATIVE_X, NEGATIVE_Z, NULL ); + } ent->genericBone2 = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "right_arm", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->genericBone2, angles, BONE_ANGLES_POSTMULT, NEGATIVE_Y, NEGATIVE_X, NEGATIVE_Z, NULL ); + if (ent->genericBone2>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->genericBone2, angles, BONE_ANGLES_POSTMULT, NEGATIVE_Y, NEGATIVE_X, NEGATIVE_Z, NULL ); + } ent->genericBone3 = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "claw", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->genericBone3, angles, BONE_ANGLES_POSTMULT, NEGATIVE_Y, NEGATIVE_X, NEGATIVE_Z, NULL ); + if (ent->genericBone3>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->genericBone3, angles, BONE_ANGLES_POSTMULT, NEGATIVE_Y, NEGATIVE_X, NEGATIVE_Z, NULL ); + } } else if (!Q_strncmp( "r2d2", modelName, 4 )) { ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + if (ent->craniumBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + } ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "body", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + if (ent->thoracicBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + } ent->genericBone1 = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "f_eye", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->genericBone1, angles, BONE_ANGLES_POSTMULT, NEGATIVE_Y, NEGATIVE_X, NEGATIVE_Z, NULL ); + if (ent->genericBone1>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->genericBone1, angles, BONE_ANGLES_POSTMULT, NEGATIVE_Y, NEGATIVE_X, NEGATIVE_Z, NULL ); + } } else if (!Q_strncmp( "r5d2", modelName, 4 )) { ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + if (ent->craniumBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + } ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "body", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + if (ent->thoracicBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + } } else if ( !Q_stricmp( "atst", modelName )) { ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + if (ent->craniumBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + } ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "thoracic", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + if (ent->thoracicBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + } ent->footLBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "l_tarsal", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->footLBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL ); + if (ent->footLBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->footLBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL ); + } ent->footRBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "r_tarsal", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->footRBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL ); + if (ent->footRBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->footRBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL ); + } } else if ( !Q_stricmp( "mark1", modelName )) { ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + if (ent->craniumBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + } ent->upperLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->upperLumbarBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + if (ent->upperLumbarBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->upperLumbarBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + } } else if ( !Q_stricmp( "mark2", modelName )) { ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + if (ent->craniumBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + } ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "thoracic", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); -/* - ent->upperLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cervical", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->upperLumbarBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); - ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); - */ + if (ent->thoracicBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + } } else if ( !Q_stricmp( "minemonster", modelName )) { ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "thoracic1", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + if (ent->thoracicBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + } ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + if (ent->craniumBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + } } else if ( !Q_stricmp( "howler", modelName )) { ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "thoracic", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + if (ent->thoracicBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + } ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + if (ent->craniumBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + } } else { //special case motion bone - to match up split anims ent->motionBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "Motion", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->motionBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_X, NEGATIVE_Y, NULL ); + if (ent->motionBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->motionBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_X, NEGATIVE_Y, NULL ); + } ent->motionBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "Motion"); + //bone needed for turning anims + ent->hipsBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "pelvis", qtrue ); + if (ent->hipsBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->hipsBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + } //regular bones we need ent->upperLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "upper_lumbar", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->upperLumbarBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + if (ent->upperLumbarBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->upperLumbarBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + } ent->lowerLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "lower_lumbar", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->lowerLumbarBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + if (ent->lowerLumbarBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->lowerLumbarBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + } ent->faceBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "face", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->faceBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + if (ent->faceBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->faceBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + } ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + if (ent->craniumBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + } ent->cervicalBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cervical", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->cervicalBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + if (ent->cervicalBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->cervicalBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + } ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "thoracic", qtrue ); - gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + if (ent->thoracicBone>=0) + { + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL ); + } } ent->client->clientInfo.infoValid = qtrue; @@ -1160,6 +1273,10 @@ void G_ActivatePersonalShield( gentity_t *ent ) extern void CG_ChangeWeapon( int num ); void G_PilotXWing( gentity_t *ent ) { + if ( !CheatsOk( ent ) ) + { + return; + } if ( ent->client->ps.vehicleModel != 0 ) { CG_ChangeWeapon( WP_SABER ); @@ -1173,7 +1290,9 @@ void G_PilotXWing( gentity_t *ent ) { gi.cvar_set( "cg_thirdperson", "0" ); } + cg.overrides.active |= CG_OVERRIDE_3RD_PERSON_RNG; cg.overrides.thirdPersonRange = 240; + cg.overrides.active &= ~CG_OVERRIDE_FOV; cg.overrides.fov = 0; } else @@ -1195,6 +1314,7 @@ void G_PilotXWing( gentity_t *ent ) gi.cvar_set( "m_pitchOverride", "0.01" );//ignore inverse mouse look gi.cvar_set( "m_yawOverride", "0.0075" ); gi.cvar_set( "cg_thirdperson", "1" ); + cg.overrides.active |= (CG_OVERRIDE_3RD_PERSON_RNG|CG_OVERRIDE_FOV); cg.overrides.thirdPersonRange = 240; cg.overrides.fov = 100; } @@ -1241,9 +1361,8 @@ void G_DriveATST( gentity_t *ent, gentity_t *atst ) { gi.cvar_set( "cg_thirdperson", "0" ); } - cg.overrides.thirdPersonRange = 0; - cg.overrides.thirdPersonVertOffset = cg_thirdPersonVertOffset.value; - cg.overrides.thirdPersonPitchOffset = 0; + cg.overrides.active &= ~(CG_OVERRIDE_3RD_PERSON_RNG|CG_OVERRIDE_3RD_PERSON_VOF|CG_OVERRIDE_3RD_PERSON_POF); + cg.overrides.thirdPersonRange = cg.overrides.thirdPersonVertOffset = cg.overrides.thirdPersonPitchOffset = 0; ent->client->ps.viewheight = ent->maxs[2] + STANDARD_VIEWHEIGHT_OFFSET; //ent->mass = 10; } @@ -1310,6 +1429,7 @@ void G_DriveATST( gentity_t *ent, gentity_t *atst ) gi.cvar_set( "m_yawOverride", "0.0075" ); //camera gi.cvar_set( "cg_thirdperson", "1" ); + cg.overrides.active |= CG_OVERRIDE_3RD_PERSON_RNG; cg.overrides.thirdPersonRange = 240; //cg.overrides.thirdPersonVertOffset = 100; //cg.overrides.thirdPersonPitchOffset = -30; @@ -1438,6 +1558,7 @@ qboolean ClientSpawn(gentity_t *ent, SavedGameJustLoaded_e eSavedGameJustLoaded ent->mass = 10; ent->takedamage = qtrue; ent->inuse = qtrue; + SetInUse(ent); ent->classname = "player"; client->squadname = ent->targetname = ent->script_targetname = ent->NPC_type = "kyle"; if ( ent->client->NPC_class == CLASS_NONE ) @@ -1480,7 +1601,7 @@ qboolean ClientSpawn(gentity_t *ent, SavedGameJustLoaded_e eSavedGameJustLoaded client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_SABER ); //this is precached in SP_info_player_deathmatch } - for ( i = 0; i < AMMO_MAX; i++ ) + for ( i = 0; i < AMMO_THERMAL; i++ ) // don't give ammo for explosives { client->ps.ammo[i] = ammoData[i].max; } @@ -1493,10 +1614,11 @@ qboolean ClientSpawn(gentity_t *ent, SavedGameJustLoaded_e eSavedGameJustLoaded // ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH]; - ent->client->dismemberProbHead = 50; - ent->client->dismemberProbArms = 50; - ent->client->dismemberProbWaist = 50; - ent->client->dismemberProbLegs = 50; + ent->client->dismemberProbHead = 1; + ent->client->dismemberProbArms = 5; + ent->client->dismemberProbHands = 20; + ent->client->dismemberProbWaist = 1; + ent->client->dismemberProbLegs = 1; ent->client->ps.batteryCharge = 200; // starts out with a small battery charge @@ -1595,6 +1717,14 @@ qboolean ClientSpawn(gentity_t *ent, SavedGameJustLoaded_e eSavedGameJustLoaded if ( ent->client->ps.stats[STAT_WEAPONS] & ( 1 << WP_SABER ) ) {//set up so has lightsaber WP_SaberInitBladeData( ent ); + if ( ent->weaponModel == -1 && ent->client->ps.weapon == WP_SABER ) + { + G_CreateG2AttachedWeaponModel( ent, ent->client->ps.saberModel ); + } + } + if ( ent->weaponModel == -1 && ent->client->ps.weapon != WP_NONE ) + { + G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].weaponMdl ); } { @@ -1616,6 +1746,7 @@ qboolean ClientSpawn(gentity_t *ent, SavedGameJustLoaded_e eSavedGameJustLoaded } } + client->pers.enterTime = level.time;//needed mainly to stop the weapon switch to WP_NONE that happens on loads ent->max_health = client->ps.stats[STAT_MAX_HEALTH]; return beamInEffect; @@ -1648,6 +1779,7 @@ void ClientDisconnect( int clientNum ) { gi.unlinkentity (ent); ent->s.modelindex = 0; ent->inuse = qfalse; + ClearInUse(ent); ent->classname = "disconnected"; ent->client->pers.connected = CON_DISCONNECTED; ent->client->ps.persistant[PERS_TEAM] = TEAM_FREE; diff --git a/code/game/g_cmds.cpp b/code/game/g_cmds.cpp index 1805fca..7244bd8 100644 --- a/code/game/g_cmds.cpp +++ b/code/game/g_cmds.cpp @@ -615,11 +615,14 @@ void Cmd_Where_f( gentity_t *ent ) { gi.Printf("usage: where classname\n"); return; } - for (int i = 0; i < globals.num_entities; i++) { - check = &g_entities[i]; - if(!check || !check->inuse) { + for (int i = 0; i < globals.num_entities; i++) + { + if(!PInUse(i)) continue; - } +// if(!check || !check->inuse) { +// continue; +// } + check = &g_entities[i]; if (!Q_stricmpn(s, check->classname, len) ) { gi.SendServerCommand( ent-g_entities, "print \"%s %s\n\"", check->classname, vtos( check->s.pos.trBase ) ); } @@ -973,9 +976,17 @@ void ClientCommand( int clientNum ) { else if (Q_stricmp (cmd, "notarget") == 0) Cmd_Notarget_f (ent); else if (Q_stricmp (cmd, "noclip") == 0) + { Cmd_Noclip_f (ent); + } else if (Q_stricmp (cmd, "kill") == 0) + { + if ( !CheatsOk( ent ) ) + { + return; + } Cmd_Kill_f (ent); + } else if (Q_stricmp (cmd, "levelshot") == 0) Cmd_LevelShot_f (ent); else if (Q_stricmp (cmd, "where") == 0) @@ -1007,7 +1018,12 @@ void ClientCommand( int clientNum ) { else if (Q_stricmp (cmd, "fly_xwing") == 0) G_PilotXWing( ent ); else if (Q_stricmp (cmd, "drive_atst") == 0) - G_DriveATST( ent, NULL ); + { + if ( CheatsOk( ent ) ) + { + G_DriveATST( ent, NULL ); + } + } else if (Q_stricmp (cmd, "thereisnospoon") == 0) G_StartMatrixEffect( ent ); else if (Q_stricmp (cmd, "use_electrobinoculars") == 0) diff --git a/code/game/g_combat.cpp b/code/game/g_combat.cpp index 17b1bc2..26ab7fa 100644 --- a/code/game/g_combat.cpp +++ b/code/game/g_combat.cpp @@ -19,6 +19,7 @@ extern cvar_t *g_debugDamage; extern qboolean stop_icarus; extern cvar_t *g_dismemberment; +extern cvar_t *g_dismemberProbabilities; extern cvar_t *g_realisticSaberDamage; extern cvar_t *g_timescale; extern cvar_t *d_slowmodeath; @@ -63,7 +64,6 @@ extern int PM_AnimLength( int index, animNumber_t anim ); qboolean G_CheckForLedge( gentity_t *self, vec3_t fallCheckDir ); static int G_CheckSpecialDeathAnim( gentity_t *self, vec3_t point, int damage, int mod, int hitLoc ); static int G_PickDeathAnim( gentity_t *self, vec3_t point, int damage, int mod, int hitLoc ); -static void G_DoDismemberment( gentity_t *self, vec3_t point, int mod, int damage, int hitLoc ); extern gitem_t *FindItemForAmmo( ammo_t ammo ); /* ============ @@ -189,11 +189,11 @@ gentity_t *TossClientItems( gentity_t *self ) dropped->s.radius = 10; } - else if (( self->client->NPC_class == CLASS_SENTRY ) || ( self->client->NPC_class == CLASS_PROBE )) - { - item = FindItemForAmmo( AMMO_BLASTER ); - Drop_Item( self, item, 0, qtrue ); - } +// else if (( self->client->NPC_class == CLASS_SENTRY ) || ( self->client->NPC_class == CLASS_PROBE )) // Looks dumb, Steve told us to take it out. +// { +// item = FindItemForAmmo( AMMO_BLASTER ); +// Drop_Item( self, item, 0, qtrue ); +// } else if ( self->client->NPC_class == CLASS_MARK1 ) { @@ -286,7 +286,7 @@ ExplodeDeath void ExplodeDeath( gentity_t *self ) { // gentity_t *tent; - vec3_t forward={0,0,1}; + vec3_t forward; self->takedamage = qfalse;//stop chain reaction runaway loops @@ -295,7 +295,7 @@ void ExplodeDeath( gentity_t *self ) VectorCopy( self->currentOrigin, self->s.pos.trBase ); // tent = G_TempEntity( self->s.origin, EV_FX_EXPLOSION ); -// AngleVectors(self->s.angles, forward, NULL, NULL); // FIXME: letting effect always shoot up? Might be ok. + AngleVectors(self->s.angles, forward, NULL, NULL); // FIXME: letting effect always shoot up? Might be ok. if ( self->fxID > 0 ) { @@ -321,14 +321,14 @@ void ExplodeDeath( gentity_t *self ) ObjectDie( self, self, self, 20, 0 ); } -void ExplodeDeath_Wait( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath,int hitLoc ) +void ExplodeDeath_Wait( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath,int dFlags,int hitLoc ) { self->e_DieFunc = dieF_NULL; self->nextthink = level.time + Q_irand(100, 500); self->e_ThinkFunc = thinkF_ExplodeDeath; } -void ExplodeDeath( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath,int hitLoc ) +void ExplodeDeath( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath,int dFlags,int hitLoc ) { self->currentOrigin[2] += 16; // me bad for hacking this. should either do it in the effect file or make a custom explode death?? ExplodeDeath( self ); @@ -400,6 +400,7 @@ qboolean OnSameTeam( gentity_t *ent1, gentity_t *ent2 ) return ( ent1->client->playerTeam == ent2->client->playerTeam ); } + /* ------------------------- G_AlertTeam @@ -411,6 +412,7 @@ void G_AlertTeam( gentity_t *victim, gentity_t *attacker, float radius, float so gentity_t *radiusEnts[ 128 ]; vec3_t mins, maxs; int numEnts; + float distSq, sndDistSq = (soundDist*soundDist); if ( attacker == NULL || attacker->client == NULL ) return; @@ -465,22 +467,24 @@ void G_AlertTeam( gentity_t *victim, gentity_t *attacker, float radius, float so continue; if ( radiusEnts[i]->enemy == NULL ) - { - if ( soundDist <= 0 ||DistanceSquared( radiusEnts[i]->currentOrigin, victim->currentOrigin ) > (soundDist*soundDist) ) - { - if ( InFOV( victim, radiusEnts[i], radiusEnts[i]->NPC->stats.hfov, radiusEnts[i]->NPC->stats.vfov ) && NPC_ClearLOS( radiusEnts[i], victim->currentOrigin ) ) - { - G_SetEnemy( radiusEnts[i], attacker ); - } - + {//only do this if they're not already mad at someone + distSq = DistanceSquared( radiusEnts[i]->currentOrigin, victim->currentOrigin ); + if ( distSq > 16384 /*128 squared*/ && !gi.inPVS( victim->currentOrigin, radiusEnts[i]->currentOrigin ) ) + {//not even potentially visible/hearable continue; } + //NOTE: this allows sound alerts to still go through doors/PVS if the teammate is within 128 of the victim... + if ( soundDist <= 0 || distSq > sndDistSq ) + {//out of sound range + if ( !InFOV( victim, radiusEnts[i], radiusEnts[i]->NPC->stats.hfov, radiusEnts[i]->NPC->stats.vfov ) + || !NPC_ClearLOS( radiusEnts[i], victim->currentOrigin ) ) + {//out of FOV or no LOS + continue; + } + } //FIXME: This can have a nasty cascading effect if setup wrong... G_SetEnemy( radiusEnts[i], attacker ); - //FIXME: base this delay on distance and facing? Or NPC type? Rank? - TIMER_Set( radiusEnts[i], "attackDelay", Q_irand( 7500, 4500 ) ); - TIMER_Set( radiusEnts[i], "roamTime", Q_irand( 1000, 3500 ) ); } } } @@ -492,7 +496,7 @@ G_DeathAlert */ #define DEATH_ALERT_RADIUS 512 -#define DEATH_ALERT_SOUND_RADIUS 256 +#define DEATH_ALERT_SOUND_RADIUS 512 void G_DeathAlert( gentity_t *victim, gentity_t *attacker ) {//FIXME: with all the other alert stuff, do we really need this? @@ -551,7 +555,7 @@ void DeathFX( gentity_t *ent ) } */ // team no longer indicates species/race. NPC_class should be used to identify certain npc types - vec3_t effectPos; + vec3_t effectPos, right; switch(ent->client->NPC_class) { case CLASS_MOUSE: @@ -564,14 +568,17 @@ void DeathFX( gentity_t *ent ) VectorCopy( ent->currentOrigin, effectPos ); effectPos[2] += 50; G_PlayEffect( "probeexplosion1", effectPos ); - G_PlayEffect( "small_chunks", effectPos ); +// G_PlayEffect( "small_chunks", effectPos ); break; case CLASS_ATST: - VectorCopy( ent->currentOrigin, effectPos ); - effectPos[2] -= 15; + AngleVectors( ent->currentAngles, NULL, right, NULL ); + VectorMA( ent->currentOrigin, 20, right, effectPos ); + effectPos[2] += 180; + G_PlayEffect( "droidexplosion1", effectPos ); +// G_PlayEffect( "small_chunks", effectPos ); + VectorMA( effectPos, -40, right, effectPos ); G_PlayEffect( "droidexplosion1", effectPos ); - G_PlayEffect( "small_chunks", effectPos ); break; case CLASS_SEEKER: @@ -586,16 +593,37 @@ void DeathFX( gentity_t *ent ) // should list all remaining droids here, hope I didn't miss any case CLASS_R2D2: - case CLASS_R5D2: - case CLASS_PROTOCOL: - case CLASS_MARK1: - case CLASS_MARK2: - case CLASS_INTERROGATOR: + VectorCopy( ent->currentOrigin, effectPos ); + effectPos[2] -= 15; + G_PlayEffect( "r2_droidexplosion", effectPos ); +// G_PlayEffect( "small_chunks", effectPos ); + break; + + case CLASS_R5D2: + VectorCopy( ent->currentOrigin, effectPos ); + effectPos[2] -= 15; + G_PlayEffect( "r5_droidexplosion", effectPos ); + + case CLASS_PROTOCOL: + case CLASS_MARK2: + case CLASS_INTERROGATOR: VectorCopy( ent->currentOrigin, effectPos ); effectPos[2] -= 15; G_PlayEffect( "droidexplosion1", effectPos ); - G_PlayEffect( "small_chunks", effectPos ); +// G_PlayEffect( "small_chunks", effectPos ); + break; + + case CLASS_MARK1: + AngleVectors( ent->currentAngles, NULL, right, NULL ); + VectorMA( ent->currentOrigin, 10, right, effectPos ); + effectPos[2] -= 15; + G_PlayEffect( "droidexplosion1", effectPos ); + VectorMA( effectPos, -20, right, effectPos ); + G_PlayEffect( "droidexplosion1", effectPos ); + VectorMA( effectPos, -20, right, effectPos ); + G_PlayEffect( "droidexplosion1", effectPos ); +// G_PlayEffect( "small_chunks", effectPos ); break; case CLASS_SENTRY: @@ -688,23 +716,27 @@ void G_SetMissionStatusText( gentity_t *attacker, int mod ) void G_MakeTeamVulnerable( void ) { int i, newhealth; - gentity_t *ent = &g_entities[0]; + gentity_t *ent; gentity_t *self = &g_entities[0]; if ( !self->client ) { return; } - for ( i = 0; i < globals.num_entities ; i++, ent++) +// for ( i = 0; i < globals.num_entities ; i++, ent++) + for ( i = 0; i < globals.num_entities ; i++) { - if ( !ent ) - { + if(!PInUse(i)) continue; - } - if ( !ent->inuse ) - { - continue; - } +// if ( !ent->inuse ) +// { +// continue; +// } +// if ( !ent ) +// { +// continue; +// } + ent=&g_entities[i]; if ( !ent->client ) { continue; @@ -767,18 +799,22 @@ void G_StartMatrixEffect( gentity_t *ent, qboolean falling = qfalse, int length qboolean G_JediInRoom( vec3_t from ) { - gentity_t *ent = NULL; + gentity_t *ent; int i; - for ( i = 1, ent = &g_entities[1]; i < globals.num_entities; i++, ent++ ) +// for ( i = 1, ent = &g_entities[1]; i < globals.num_entities; i++, ent++ ) + for ( i = 1; i < globals.num_entities; i++) { - if ( !ent ) - { + if(!PInUse(i)) continue; - } - if ( !ent->inuse ) - { - continue; - } +// if ( !ent->inuse ) +// { +// continue; +// } +// if ( !ent ) +// { +// continue; +// } + ent = &g_entities[i]; if ( !ent->NPC ) { continue; @@ -804,6 +840,1822 @@ qboolean G_JediInRoom( vec3_t from ) return qfalse; } +qboolean G_GetHitLocFromSurfName( gentity_t *ent, const char *surfName, int *hitLoc, vec3_t point, vec3_t dir, vec3_t bladeDir ) +{ + qboolean dismember = qfalse; + + *hitLoc = HL_NONE; + + if ( !surfName || !surfName[0] ) + { + return qfalse; + } + + if( !ent->client ) + { + return qfalse; + } + + if ( ent->client + && ( ent->client->NPC_class == CLASS_R2D2 + || ent->client->NPC_class == CLASS_R2D2 + || ent->client->NPC_class == CLASS_GONK + || ent->client->NPC_class == CLASS_MOUSE + || ent->client->NPC_class == CLASS_SENTRY + || ent->client->NPC_class == CLASS_INTERROGATOR + || ent->client->NPC_class == CLASS_SENTRY + || ent->client->NPC_class == CLASS_PROBE ) ) + {//we don't care about per-surface hit-locations or dismemberment for these guys + return qfalse; + } + + if ( ent->client && (ent->client->NPC_class == CLASS_ATST) ) + { + //FIXME: almost impossible to hit these... perhaps we should + // check for splashDamage and do radius damage to these parts? + // Or, if we ever get bbox G2 traces, that may fix it, too + if (!Q_stricmp("head_light_blaster_cann",surfName)) + { + *hitLoc = HL_ARM_LT; + } + else if (!Q_stricmp("head_concussion_charger",surfName)) + { + *hitLoc = HL_ARM_RT; + } + return(qfalse); + } + else if ( ent->client && (ent->client->NPC_class == CLASS_MARK1) ) + { + if (!Q_stricmp("l_arm",surfName)) + { + *hitLoc = HL_ARM_LT; + } + else if (!Q_stricmp("r_arm",surfName)) + { + *hitLoc = HL_ARM_RT; + } + else if (!Q_stricmp("torso_front",surfName)) + { + *hitLoc = HL_CHEST; + } + else if (!Q_stricmp("torso_tube1",surfName)) + { + *hitLoc = HL_GENERIC1; + } + else if (!Q_stricmp("torso_tube2",surfName)) + { + *hitLoc = HL_GENERIC2; + } + else if (!Q_stricmp("torso_tube3",surfName)) + { + *hitLoc = HL_GENERIC3; + } + else if (!Q_stricmp("torso_tube4",surfName)) + { + *hitLoc = HL_GENERIC4; + } + else if (!Q_stricmp("torso_tube5",surfName)) + { + *hitLoc = HL_GENERIC5; + } + else if (!Q_stricmp("torso_tube6",surfName)) + { + *hitLoc = HL_GENERIC6; + } + return(qfalse); + } + else if ( ent->client && (ent->client->NPC_class == CLASS_MARK2) ) + { + if (!Q_stricmp("torso_canister1",surfName)) + { + *hitLoc = HL_GENERIC1; + } + else if (!Q_stricmp("torso_canister2",surfName)) + { + *hitLoc = HL_GENERIC2; + } + else if (!Q_stricmp("torso_canister3",surfName)) + { + *hitLoc = HL_GENERIC3; + } + return(qfalse); + } + else if ( ent->client && (ent->client->NPC_class == CLASS_GALAKMECH) ) + { + if (!Q_stricmp("torso_antenna",surfName)||!Q_stricmp("torso_antenna_base",surfName)) + { + *hitLoc = HL_GENERIC1; + } + else if (!Q_stricmp("torso_shield",surfName)) + { + *hitLoc = HL_GENERIC2; + } + else + { + *hitLoc = HL_CHEST; + } + return(qfalse); + } + + //FIXME: check the hitLoc and hitDir against the cap tag for the place + //where the split will be- if the hit dir is roughly perpendicular to + //the direction of the cap, then the split is allowed, otherwise we + //hit it at the wrong angle and should not dismember... + int actualTime = (cg.time?cg.time:level.time); + if ( !Q_strncmp( "hips", surfName, 4 ) ) + {//FIXME: test properly for legs + *hitLoc = HL_WAIST; + if ( ent->client != NULL && ent->ghoul2.size() ) + { + mdxaBone_t boltMatrix; + vec3_t tagOrg, angles; + + VectorSet( angles, 0, ent->currentAngles[YAW], 0 ); + if (ent->kneeLBolt>=0) + { + gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, ent->kneeLBolt, + &boltMatrix, angles, ent->currentOrigin, + actualTime, NULL, ent->s.modelScale ); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, tagOrg ); + if ( DistanceSquared( point, tagOrg ) < 100 ) + {//actually hit the knee + *hitLoc = HL_LEG_LT; + } + } + if (*hitLoc == HL_WAIST) + { + if (ent->kneeRBolt>=0) + { + gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, ent->kneeRBolt, + &boltMatrix, angles, ent->currentOrigin, + actualTime, NULL, ent->s.modelScale ); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, tagOrg ); + if ( DistanceSquared( point, tagOrg ) < 100 ) + {//actually hit the knee + *hitLoc = HL_LEG_RT; + } + } + } + } + } + else if ( !Q_strncmp( "torso", surfName, 5 ) ) + { + if ( !ent->client ) + { + *hitLoc = HL_CHEST; + } + else + { + vec3_t t_fwd, t_rt, t_up, dirToImpact; + float frontSide, rightSide, upSide; + AngleVectors( ent->client->renderInfo.torsoAngles, t_fwd, t_rt, t_up ); + VectorSubtract( point, ent->client->renderInfo.torsoPoint, dirToImpact ); + frontSide = DotProduct( t_fwd, dirToImpact ); + rightSide = DotProduct( t_rt, dirToImpact ); + upSide = DotProduct( t_up, dirToImpact ); + if ( upSide < -10 ) + {//hit at waist + *hitLoc = HL_WAIST; + } + else if ( upSide > -3 ) + {//hit at waist + *hitLoc = HL_HEAD; + } + else + {//hit on upper torso + if ( rightSide > 10 ) + { + *hitLoc = HL_ARM_RT; + } + else if ( rightSide < -10 ) + { + *hitLoc = HL_ARM_LT; + } + else if ( rightSide > 4 ) + { + if ( frontSide > 0 ) + { + *hitLoc = HL_CHEST_RT; + } + else + { + *hitLoc = HL_BACK_RT; + } + } + else if ( rightSide < -4 ) + { + if ( frontSide > 0 ) + { + *hitLoc = HL_CHEST_LT; + } + else + { + *hitLoc = HL_BACK_LT; + } + } + else if ( upSide > -3 ) + { + *hitLoc = HL_HEAD; + } + else if ( frontSide > 0 ) + { + *hitLoc = HL_CHEST; + } + else + { + *hitLoc = HL_BACK; + } + } + } + } + else if ( !Q_strncmp( "head", surfName, 4 ) ) + { + *hitLoc = HL_HEAD; + } + else if ( !Q_strncmp( "r_arm", surfName, 5 ) ) + { + *hitLoc = HL_ARM_RT; + if ( ent->client != NULL && ent->ghoul2.size() ) + { + mdxaBone_t boltMatrix; + vec3_t tagOrg, angles; + + VectorSet( angles, 0, ent->currentAngles[YAW], 0 ); + if (ent->handRBolt>=0) + { + gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, ent->handRBolt, + &boltMatrix, angles, ent->currentOrigin, + actualTime, NULL, ent->s.modelScale ); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, tagOrg ); + if ( DistanceSquared( point, tagOrg ) < 256 ) + {//actually hit the hand + *hitLoc = HL_HAND_RT; + } + } + } + } + else if ( !Q_strncmp( "l_arm", surfName, 5 ) ) + { + *hitLoc = HL_ARM_LT; + if ( ent->client != NULL && ent->ghoul2.size() ) + { + mdxaBone_t boltMatrix; + vec3_t tagOrg, angles; + + VectorSet( angles, 0, ent->currentAngles[YAW], 0 ); + if (ent->handLBolt>=0) + { + gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, ent->handLBolt, + &boltMatrix, angles, ent->currentOrigin, + actualTime, NULL, ent->s.modelScale ); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, tagOrg ); + if ( DistanceSquared( point, tagOrg ) < 256 ) + {//actually hit the hand + *hitLoc = HL_HAND_LT; + } + } + } + } + else if ( !Q_strncmp( "r_leg", surfName, 5 ) ) + { + *hitLoc = HL_LEG_RT; + if ( ent->client != NULL && ent->ghoul2.size() ) + { + mdxaBone_t boltMatrix; + vec3_t tagOrg, angles; + + VectorSet( angles, 0, ent->currentAngles[YAW], 0 ); + if (ent->footRBolt>=0) + { + gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, ent->footRBolt, + &boltMatrix, angles, ent->currentOrigin, + actualTime, NULL, ent->s.modelScale ); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, tagOrg ); + if ( DistanceSquared( point, tagOrg ) < 100 ) + {//actually hit the foot + *hitLoc = HL_FOOT_RT; + } + } + } + } + else if ( !Q_strncmp( "l_leg", surfName, 5 ) ) + { + *hitLoc = HL_LEG_LT; + if ( ent->client != NULL && ent->ghoul2.size() ) + { + mdxaBone_t boltMatrix; + vec3_t tagOrg, angles; + + VectorSet( angles, 0, ent->currentAngles[YAW], 0 ); + if (ent->footLBolt>=0) + { + gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, ent->footLBolt, + &boltMatrix, angles, ent->currentOrigin, + actualTime, NULL, ent->s.modelScale ); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, tagOrg ); + if ( DistanceSquared( point, tagOrg ) < 100 ) + {//actually hit the foot + *hitLoc = HL_FOOT_LT; + } + } + } + } + else if ( !Q_strncmp( "r_hand", surfName, 6 ) ) + { + *hitLoc = HL_HAND_RT; + } + else if ( !Q_strncmp( "l_hand", surfName, 6 ) ) + { + *hitLoc = HL_HAND_LT; + } + if ( g_realisticSaberDamage->integer ) + { + dismember = qtrue; + } + else if ( g_dismemberment->integer > 3 || !ent->client->dismembered ) + { + if ( dir && (dir[0] || dir[1] || dir[2]) && + bladeDir && (bladeDir[0] || bladeDir[1] || bladeDir[2]) ) + {//we care about direction (presumably for dismemberment) + char *tagName = NULL; + float aoa = 0.5f; + //dir must be roughly perpendicular to the hitLoc's cap bolt + switch ( *hitLoc ) + { + case HL_LEG_RT: + tagName = "*hips_cap_r_leg"; + break; + case HL_LEG_LT: + tagName = "*hips_cap_l_leg"; + break; + case HL_WAIST: + tagName = "*hips_cap_torso"; + aoa = 0.25f; + break; + case HL_CHEST_RT: + case HL_ARM_RT: + case HL_BACK_RT: + tagName = "*torso_cap_r_arm"; + break; + case HL_CHEST_LT: + case HL_ARM_LT: + case HL_BACK_LT: + tagName = "*torso_cap_l_arm"; + break; + case HL_HAND_RT: + tagName = "*r_arm_cap_r_hand"; + break; + case HL_HAND_LT: + tagName = "*l_arm_cap_l_hand"; + break; + case HL_HEAD: + tagName = "*torso_cap_head"; + aoa = 0.25f; + break; + case HL_CHEST: + case HL_BACK: + case HL_FOOT_RT: + case HL_FOOT_LT: + default: + //no dismemberment possible with these, so no checks needed + break; + } + if ( tagName ) + { + int tagBolt = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], tagName ); + if ( tagBolt != -1 ) + { + mdxaBone_t boltMatrix; + vec3_t tagOrg, tagDir, angles; + VectorSet( angles, 0, ent->currentAngles[YAW], 0 ); + gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, tagBolt, + &boltMatrix, angles, ent->currentOrigin, + actualTime, NULL, ent->s.modelScale ); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, tagOrg ); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, tagDir ); + if ( DistanceSquared( point, tagOrg ) < 256 ) + {//hit close + float dot = DotProduct( dir, tagDir ); + if ( dot < aoa && dot > -aoa ) + {//hit roughly perpendicular + dot = DotProduct( bladeDir, tagDir ); + if ( dot < aoa && dot > -aoa ) + {//blade was roughly perpendicular + dismember = qtrue; + } + } + } + } + } + } + } + + return dismember; +} + +int G_GetHitLocation ( gentity_t *target, vec3_t ppoint ) +{ + vec3_t point, point_dir; + vec3_t forward, right, up; + vec3_t tangles, tcenter; + float tradius; + float udot, fdot, rdot; + int Vertical, Forward, Lateral; + int HitLoc; + +//get target forward, right and up + if(target->client) + {//ignore player's pitch and roll + VectorSet(tangles, 0, target->currentAngles[YAW], 0); + } + + AngleVectors(tangles, forward, right, up); + +//get center of target + VectorAdd(target->absmin, target->absmax, tcenter); + VectorScale(tcenter, 0.5, tcenter); + +//get radius width of target + tradius = (fabs(target->maxs[0]) + fabs(target->maxs[1]) + fabs(target->mins[0]) + fabs(target->mins[1]))/4; + +//get impact point + if(ppoint && !VectorCompare(ppoint, vec3_origin)) + { + VectorCopy(ppoint, point); + } + else + { + return HL_NONE; + } + +/* +//get impact dir + if(pdir && !VectorCompare(pdir, vec3_origin)) + { + VectorCopy(pdir, dir); + } + else + { + return; + } + +//put point at controlled distance from center + VectorSubtract(point, tcenter, tempvec); + tempvec[2] = 0; + hdist = VectorLength(tempvec); + + VectorMA(point, hdist - tradius, dir, point); + //now a point on the surface of a cylinder with a radius of tradius +*/ + VectorSubtract(point, tcenter, point_dir); + VectorNormalize(point_dir); + + //Get bottom to top (Vertical) position index + udot = DotProduct(up, point_dir); + if(udot>.800) + Vertical = 4; + else if(udot>.400) + Vertical = 3; + else if(udot>-.333) + Vertical = 2; + else if(udot>-.666) + Vertical = 1; + else + Vertical = 0; + + //Get back to front (Forward) position index + fdot = DotProduct(forward, point_dir); + if(fdot>.666) + Forward = 4; + else if(fdot>.333) + Forward = 3; + else if(fdot>-.333) + Forward = 2; + else if(fdot>-.666) + Forward = 1; + else + Forward = 0; + + //Get left to right (Lateral) position index + rdot = DotProduct(right, point_dir); + if(rdot>.666) + Lateral = 4; + else if(rdot>.333) + Lateral = 3; + else if(rdot>-.333) + Lateral = 2; + else if(rdot>-.666) + Lateral = 1; + else + Lateral = 0; + + HitLoc = Vertical * 25 + Forward * 5 + Lateral; + + if(HitLoc <= 10) + {//feet + if ( rdot > 0 ) + { + return HL_FOOT_RT; + } + else + { + return HL_FOOT_LT; + } + } + else if(HitLoc <= 50) + {//legs + if ( rdot > 0 ) + { + return HL_LEG_RT; + } + else + { + return HL_LEG_LT; + } + } + else if ( HitLoc == 56||HitLoc == 60||HitLoc == 61||HitLoc == 65||HitLoc == 66||HitLoc == 70 ) + {//hands + if ( rdot > 0 ) + { + return HL_HAND_RT; + } + else + { + return HL_HAND_LT; + } + } + else if ( HitLoc == 83||HitLoc == 87||HitLoc == 88||HitLoc == 92||HitLoc == 93||HitLoc == 97 ) + {//arms + if ( rdot > 0 ) + { + return HL_ARM_RT; + } + else + { + return HL_ARM_LT; + } + } + else if((HitLoc >= 107 && HitLoc <= 109)|| + (HitLoc >= 112 && HitLoc <= 114)|| + (HitLoc >= 117 && HitLoc <= 119)) + {//head + return HL_HEAD; + } + else + { + if ( udot < 0.3 ) + { + return HL_WAIST; + } + else if ( fdot < 0 ) + { + if ( rdot > 0.4 ) + { + return HL_BACK_RT; + } + else if ( rdot < -0.4 ) + { + return HL_BACK_LT; + } + else + { + return HL_BACK; + } + } + else + { + if ( rdot > 0.3 ) + { + return HL_CHEST_RT; + } + else if ( rdot < -0.3 ) + { + return HL_CHEST_LT; + } + else + { + return HL_CHEST; + } + } + } + //return HL_NONE; +} + +int G_PickPainAnim( gentity_t *self, vec3_t point, int damage, int hitLoc = HL_NONE ) +{ + if ( hitLoc == HL_NONE ) + { + hitLoc = G_GetHitLocation( self, point ); + } + switch( hitLoc ) + { + case HL_FOOT_RT: + return BOTH_PAIN12; + //PAIN12 = right foot + break; + case HL_FOOT_LT: + return -1; + break; + case HL_LEG_RT: + if ( !Q_irand( 0, 1 ) ) + { + return BOTH_PAIN11; + } + else + { + return BOTH_PAIN13; + } + //PAIN11 = twitch right leg + //PAIN13 = right knee + break; + case HL_LEG_LT: + return BOTH_PAIN14; + //PAIN14 = twitch left leg + break; + case HL_BACK_RT: + return BOTH_PAIN7; + //PAIN7 = med left shoulder + break; + case HL_BACK_LT: + return Q_irand( BOTH_PAIN15, BOTH_PAIN16 ); + //PAIN15 = med right shoulder + //PAIN16 = twitch right shoulder + break; + case HL_BACK: + if ( !Q_irand( 0, 1 ) ) + { + return BOTH_PAIN1; + } + else + { + return BOTH_PAIN5; + } + //PAIN1 = back + //PAIN5 = same as 1 + break; + case HL_CHEST_RT: + return BOTH_PAIN3; + //PAIN3 = long, right shoulder + break; + case HL_CHEST_LT: + return BOTH_PAIN2; + //PAIN2 = long, left shoulder + break; + case HL_WAIST: + case HL_CHEST: + if ( !Q_irand( 0, 3 ) ) + { + return BOTH_PAIN6; + } + else if ( !Q_irand( 0, 2 ) ) + { + return BOTH_PAIN8; + } + else if ( !Q_irand( 0, 1 ) ) + { + return BOTH_PAIN17; + } + else + { + return BOTH_PAIN19; + } + //PAIN6 = gut + //PAIN8 = chest + //PAIN17 = twitch crotch + //PAIN19 = med crotch + break; + case HL_ARM_RT: + case HL_HAND_RT: + return BOTH_PAIN9; + //PAIN9 = twitch right arm + break; + case HL_ARM_LT: + case HL_HAND_LT: + return BOTH_PAIN10; + //PAIN10 = twitch left arm + break; + case HL_HEAD: + return BOTH_PAIN4; + //PAIN4 = head + break; + default: + return -1; + break; + } +} + +extern void G_BounceMissile( gentity_t *ent, trace_t *trace ); +void LimbThink( gentity_t *ent ) +{//FIXME: just use object thinking? + vec3_t origin; + trace_t tr; + + ent->nextthink = level.time + FRAMETIME; + + if ( ent->enemy ) + {//alert people that I am a piece of one of their friends + AddSightEvent( ent->enemy, ent->currentOrigin, 384, AEL_DISCOVERED ); + } + + if ( ent->s.pos.trType == TR_STATIONARY ) + {//stopped + if ( level.time > ent->s.apos.trTime + ent->s.apos.trDuration ) + { + ent->nextthink = level.time + Q_irand( 5000, 15000 ); + ent->e_ThinkFunc = thinkF_G_FreeEntity; + //FIXME: these keep drawing for a frame or so after being freed?! See them lerp to origin of world... + } + else + { + EvaluateTrajectory( &ent->s.apos, level.time, ent->currentAngles ); + } + return; + } + + // get current position + EvaluateTrajectory( &ent->s.pos, level.time, origin ); + // get current angles + EvaluateTrajectory( &ent->s.apos, level.time, ent->currentAngles ); + + // trace a line from the previous position to the current position, + // ignoring interactions with the missile owner + gi.trace( &tr, ent->currentOrigin, ent->mins, ent->maxs, origin, + ent->owner ? ent->owner->s.number : ENTITYNUM_NONE, ent->clipmask ); + + VectorCopy( tr.endpos, ent->currentOrigin ); + if ( tr.startsolid ) + { + tr.fraction = 0; + } + + + gi.linkentity( ent ); + + if ( tr.fraction != 1 ) + { + G_BounceMissile( ent, &tr ); + if ( ent->s.pos.trType == TR_STATIONARY ) + {//stopped, stop spinning + //lay flat + //pitch + VectorCopy( ent->currentAngles, ent->s.apos.trBase ); + vec3_t flatAngles; + if ( ent->s.angles2[0] == -1 ) + {//any pitch is okay + flatAngles[0] = ent->currentAngles[0]; + } + else + {//lay flat + if ( ent->currentAngles[0] > 90 || ent->currentAngles[0] < -90 ) + { + flatAngles[0] = 180; + } + else + { + flatAngles[0] = 0; + } + } + //yaw + flatAngles[1] = ent->currentAngles[1]; + //roll + if ( ent->s.angles2[2] == -1 ) + {//any roll is okay + flatAngles[2] = ent->currentAngles[2]; + } + else + { + if ( ent->currentAngles[2] > 90 || ent->currentAngles[2] < -90 ) + { + flatAngles[2] = 180; + } + else + { + flatAngles[2] = 0; + } + } + VectorSubtract( flatAngles, ent->s.apos.trBase, ent->s.apos.trDelta ); + for ( int i = 0; i < 3; i++ ) + { + ent->s.apos.trDelta[i] = AngleNormalize180( ent->s.apos.trDelta[i] ); + } + ent->s.apos.trTime = level.time; + ent->s.apos.trDuration = 1000; + ent->s.apos.trType = TR_LINEAR_STOP; + //VectorClear( ent->s.apos.trDelta ); + } + } +} + +float hitLocHealthPercentage[HL_MAX] = +{ + 0.0f, //HL_NONE = 0, + 0.05f, //HL_FOOT_RT, + 0.05f, //HL_FOOT_LT, + 0.20f, //HL_LEG_RT, + 0.20f, //HL_LEG_LT, + 0.30f, //HL_WAIST, + 0.15f, //HL_BACK_RT, + 0.15f, //HL_BACK_LT, + 0.30f, //HL_BACK, + 0.15f, //HL_CHEST_RT, + 0.15f, //HL_CHEST_LT, + 0.30f, //HL_CHEST, + 0.05f, //HL_ARM_RT, + 0.05f, //HL_ARM_LT, + 0.01f, //HL_HAND_RT, + 0.01f, //HL_HAND_LT, + 0.10f //HL_HEAD +}; + +qboolean G_LimbLost( gentity_t *ent, int hitLoc ) +{ + switch ( hitLoc ) + { + case HL_FOOT_RT: + if ( ent->locationDamage[HL_FOOT_RT] >= Q3_INFINITE ) + { + return qtrue; + } + //NOTE: falls through + case HL_LEG_RT: + //NOTE: feet fall through + if ( ent->locationDamage[HL_LEG_RT] >= Q3_INFINITE ) + { + return qtrue; + } + return qfalse; + break; + + case HL_FOOT_LT: + if ( ent->locationDamage[HL_FOOT_LT] >= Q3_INFINITE ) + { + return qtrue; + } + //NOTE: falls through + case HL_LEG_LT: + //NOTE: feet fall through + if ( ent->locationDamage[HL_LEG_LT] >= Q3_INFINITE ) + { + return qtrue; + } + return qfalse; + break; + + case HL_HAND_LT: + if ( ent->locationDamage[HL_HAND_LT] >= Q3_INFINITE ) + { + return qtrue; + } + //NOTE: falls through + case HL_ARM_LT: + case HL_CHEST_LT: + case HL_BACK_RT: + //NOTE: hand falls through + if ( ent->locationDamage[HL_ARM_LT] >= Q3_INFINITE + || ent->locationDamage[HL_CHEST_LT] >= Q3_INFINITE + || ent->locationDamage[HL_BACK_RT] >= Q3_INFINITE + || ent->locationDamage[HL_WAIST] >= Q3_INFINITE ) + { + return qtrue; + } + return qfalse; + break; + + case HL_HAND_RT: + if ( ent->locationDamage[HL_HAND_RT] >= Q3_INFINITE ) + { + return qtrue; + } + //NOTE: falls through + case HL_ARM_RT: + case HL_CHEST_RT: + case HL_BACK_LT: + //NOTE: hand falls through + if ( ent->locationDamage[HL_ARM_RT] >= Q3_INFINITE + || ent->locationDamage[HL_CHEST_RT] >= Q3_INFINITE + || ent->locationDamage[HL_BACK_LT] >= Q3_INFINITE + || ent->locationDamage[HL_WAIST] >= Q3_INFINITE ) + { + return qtrue; + } + return qfalse; + break; + + case HL_HEAD: + if ( ent->locationDamage[HL_HEAD] >= Q3_INFINITE ) + { + return qtrue; + } + //NOTE: falls through + case HL_WAIST: + //NOTE: head falls through + if ( ent->locationDamage[HL_WAIST] >= Q3_INFINITE ) + { + return qtrue; + } + return qfalse; + break; + default: + return (ent->locationDamage[hitLoc]>=Q3_INFINITE); + break; + } +} + +qboolean G_Dismember( gentity_t *ent, vec3_t point, + const char *limbBone, const char *rotateBone, char *limbName, + char *limbCapName, char *stubCapName, char *limbTagName, char *stubTagName, + int limbAnim, float limbRollBase, float limbPitchBase, + int damage, int hitLoc ) +{ + int newBolt; + vec3_t dir, newPoint, limbAngles = {0,ent->client->ps.legsYaw,0}; + gentity_t *limb; + trace_t trace; + + //make sure this limb hasn't been lopped off already! + if ( gi.G2API_GetSurfaceRenderStatus( &ent->ghoul2[ent->playerModel], limbName ) ) + {//already lost this limb + return qfalse; + } + + //NOTE: only reason I have this next part is because G2API_GetSurfaceRenderStatus is *not* working + if ( G_LimbLost( ent, hitLoc ) ) + {//already lost this limb + return qfalse; + } + + //FIXME: when timescale is high, can sometimes cut off a surf that includes a surf that was already cut off +//0) create a limb ent + VectorCopy( point, newPoint ); + newPoint[2] += 6; + limb = G_Spawn(); + G_SetOrigin( limb, newPoint ); + //VectorCopy(ent->currentAngles,limbAngles); + //G_SetAngles( limb, ent->currentAngles ); + VectorCopy( newPoint, limb->s.pos.trBase ); +//1) copy the g2 instance of the victim into the limb + gi.G2API_CopyGhoul2Instance( ent->ghoul2, limb->ghoul2 ); + limb->playerModel = 0;//assumption! + limb->craniumBone = ent->craniumBone; + limb->cervicalBone = ent->cervicalBone; + limb->thoracicBone = ent->thoracicBone; + limb->upperLumbarBone = ent->upperLumbarBone; + limb->lowerLumbarBone = ent->lowerLumbarBone; + limb->hipsBone = ent->hipsBone; + limb->rootBone = ent->rootBone; +//2) set the root surf on the limb + if ( limbTagName ) + {//add smoke to cap tag + newBolt = gi.G2API_AddBolt( &limb->ghoul2[limb->playerModel], limbTagName ); + if ( newBolt != -1 ) + { + G_PlayEffect( "blaster/smoke_bolton", limb->playerModel, newBolt, limb->s.number); + } + } + /* + if ( limbBone && hitLoc == HL_HEAD ) + {//stop the current anim on the limb? + gi.G2API_StopBoneAnim( &limb->ghoul2[limb->playerModel], "model_root" ); + gi.G2API_StopBoneAnim( &limb->ghoul2[limb->playerModel], "motion" ); + gi.G2API_StopBoneAnim( &limb->ghoul2[limb->playerModel], "upper_lumbar" ); + } + */ + gi.G2API_StopBoneAnimIndex( &limb->ghoul2[limb->playerModel], limb->hipsBone ); + + gi.G2API_SetRootSurface( limb->ghoul2, limb->playerModel, limbName ); + /* + if ( limbBone && hitLoc != HL_WAIST ) + {//play the dismember anim on the limb? + //FIXME: screws up origin + animation_t *animations = level.knownAnimFileSets[ent->client->clientInfo.animFileIndex].animations; + //play the proper dismember anim on the limb + gi.G2API_SetBoneAnim(&limb->ghoul2[limb->playerModel], 0, animations[limbAnim].firstFrame - 1, + animations[limbAnim].numFrames + animations[limbAnim].firstFrame - 1, + BONE_ANIM_OVERRIDE_FREEZE, 1, cg.time); + } + */ + if ( rotateBone ) + { + gi.G2API_SetNewOrigin( &limb->ghoul2[0], gi.G2API_AddBolt( &limb->ghoul2[0], rotateBone ) ); + + //now let's try to position the limb at the *exact* right spot + int newBolt = gi.G2API_AddBolt( &ent->ghoul2[0], rotateBone ); + if ( newBolt != -1 ) + { + int actualTime = (cg.time?cg.time:level.time); + mdxaBone_t boltMatrix; + vec3_t angles; + + VectorSet( angles, 0, ent->currentAngles[YAW], 0 ); + gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, newBolt, + &boltMatrix, angles, ent->currentOrigin, + actualTime, NULL, ent->s.modelScale ); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, limb->s.origin ); + G_SetOrigin( limb, limb->s.origin ); + VectorCopy( limb->s.origin, limb->s.pos.trBase ); + //angles, too + /* + vec3_t limbF, limbR; + newBolt = gi.G2API_AddBolt( &ent->ghoul2[0], limbBone ); + if ( newBolt != -1 ) + { + gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, newBolt, + &boltMatrix, angles, ent->currentOrigin, + actualTime, NULL, ent->s.modelScale ); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, POSITIVE_X, limbF ); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, limbR ); + vectoangles( limbF, limbAngles ); + vectoangles( limbR, angles ); + limbAngles[YAW] += 180; + limbAngles[ROLL] = angles[PITCH]*-1.0f; + } + */ + } + } + if ( limbCapName ) + {//turn on caps + gi.G2API_SetSurfaceOnOff( &limb->ghoul2[limb->playerModel], limbCapName, 0 ); + } +//3) turn off w/descendants that surf in original model +//NOTE: we actually change the ent's stuff on the cgame side so that there is no 50ms lag +// also, if the limb was going to start in solid, we can delete it and return + if ( stubTagName ) + {//add smoke to cap surf, spawn effect + limb->target = G_NewString( stubTagName ); + /* + newBolt = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], stubTagName ); + if ( newBolt != -1 ) + { + G_PlayEffect( "blaster/smoke_bolton", ent->playerModel, newBolt, ent->s.number); + } + */ + } + if ( limbName ) + { + limb->target2 = G_NewString( limbName ); + //gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], limbName, 0x00000100 );//G2SURFACEFLAG_NODESCENDANTS + } + if ( stubCapName ) + {//turn on caps + limb->target3 = G_NewString( stubCapName ); + //gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], stubCapName, 0 ); + } + limb->count = limbAnim; +// + limb->s.radius = 60; +//4) toss the limb away + limb->classname = "limb"; + limb->owner = ent; + limb->enemy = ent->enemy; + if ( ent->weaponModel != -1 && !ent->client->ps.saberInFlight ) + {//the corpse hasn't dropped their weapon + if ( limbAnim == BOTH_DISMEMBER_RARM || limbAnim == BOTH_DISMEMBER_TORSO1 )//&& ent->s.weapon == WP_SABER && ent->weaponModel != -1 ) + {//FIXME: is this first check needed with this lower one? + if ( !gi.G2API_GetSurfaceRenderStatus( &limb->ghoul2[0], "r_hand" ) ) + {//only copy the weapon over if the right hand is actually on this limb... + limb->s.weapon = ent->s.weapon; + limb->weaponModel = ent->weaponModel; + } + else + { + gi.G2API_RemoveGhoul2Model( limb->ghoul2, ent->weaponModel ); + limb->weaponModel = -1; + } + //remove the owner ent's saber model and entity + if ( ent->client->ps.saberEntityNum != ENTITYNUM_NONE && ent->client->ps.saberEntityNum > 0 ) + { + gi.G2API_RemoveGhoul2Model( ent->ghoul2, ent->weaponModel ); + ent->weaponModel = -1; + if ( g_entities[ent->client->ps.saberEntityNum].inuse ) + { + G_FreeEntity( &g_entities[ent->client->ps.saberEntityNum] ); + } + ent->client->ps.saberEntityNum = ENTITYNUM_NONE; + } + } + else + { + gi.G2API_RemoveGhoul2Model( limb->ghoul2, ent->weaponModel ); + limb->weaponModel = -1; + } + } + + limb->e_clThinkFunc = clThinkF_CG_Limb; + limb->e_ThinkFunc = thinkF_LimbThink; + limb->nextthink = level.time + FRAMETIME; + gi.linkentity( limb ); + //need size, contents, clipmask + limb->svFlags = SVF_USE_CURRENT_ORIGIN; + limb->clipmask = MASK_SOLID; + limb->contents = CONTENTS_CORPSE; + VectorSet( limb->mins, -3.0f, -3.0f, -6.0f ); + VectorSet( limb->maxs, 3.0f, 3.0f, 6.0f ); + + //make sure it doesn't start in solid + gi.trace( &trace, limb->s.pos.trBase, limb->mins, limb->maxs, limb->s.pos.trBase, limb->s.number, limb->clipmask ); + if ( trace.startsolid ) + { + limb->s.pos.trBase[2] -= limb->mins[2]; + gi.trace( &trace, limb->s.pos.trBase, limb->mins, limb->maxs, limb->s.pos.trBase, limb->s.number, limb->clipmask ); + if ( trace.startsolid ) + { + limb->s.pos.trBase[2] += limb->mins[2]; + gi.trace( &trace, limb->s.pos.trBase, limb->mins, limb->maxs, limb->s.pos.trBase, limb->s.number, limb->clipmask ); + if ( trace.startsolid ) + {//stuck? don't remove + G_FreeEntity( limb ); + return qfalse; + } + } + } + + //move it + VectorCopy( limb->s.pos.trBase, limb->currentOrigin ); + gi.linkentity( limb ); + + limb->s.eType = ET_THINKER;//ET_GENERAL; + limb->physicsBounce = 0.2f; + limb->s.pos.trType = TR_GRAVITY; + limb->s.pos.trTime = level.time; // move a bit on the very first frame + VectorSubtract( point, ent->currentOrigin, dir ); + VectorNormalize( dir ); + //no trDuration? + //spin it + //new way- try to preserve the exact angle and position of the limb as it was when attached + VectorSet( limb->s.angles2, limbPitchBase, 0, limbRollBase ); + VectorCopy( limbAngles, limb->s.apos.trBase ); + /* + //old way- just set an angle... + limb->s.apos.trBase[0] += limbPitchBase; + limb->s.apos.trBase[1] = ent->client->ps.viewangles[1]; + limb->s.apos.trBase[2] += limbRollBase; + */ + limb->s.apos.trTime = level.time; + limb->s.apos.trType = TR_LINEAR; + VectorClear( limb->s.apos.trDelta ); + + if ( hitLoc == HL_HAND_RT || hitLoc == HL_HAND_LT ) + {//hands fly farther + VectorMA( ent->client->ps.velocity, 200, dir, limb->s.pos.trDelta ); + //make it bounce some + limb->s.eFlags |= EF_BOUNCE_HALF; + limb->s.apos.trDelta[0] = Q_irand( -300, 300 ); + limb->s.apos.trDelta[1] = Q_irand( -800, 800 ); + } + else if ( limbAnim == BOTH_DISMEMBER_HEAD1 + || limbAnim == BOTH_DISMEMBER_LARM + || limbAnim == BOTH_DISMEMBER_RARM ) + {//head and arms don't fly as far + limb->s.eFlags |= EF_BOUNCE_SHRAPNEL; + VectorMA( ent->client->ps.velocity, 150, dir, limb->s.pos.trDelta ); + limb->s.apos.trDelta[0] = Q_irand( -200, 200 ); + limb->s.apos.trDelta[1] = Q_irand( -400, 400 ); + } + else// if ( limbAnim == BOTH_DISMEMBER_TORSO1 || limbAnim == BOTH_DISMEMBER_LLEG || limbAnim == BOTH_DISMEMBER_RLEG ) + {//everything else just kinda falls off + limb->s.eFlags |= EF_BOUNCE_SHRAPNEL; + VectorMA( ent->client->ps.velocity, 100, dir, limb->s.pos.trDelta ); + limb->s.apos.trDelta[0] = Q_irand( -100, 100 ); + limb->s.apos.trDelta[1] = Q_irand( -200, 200 ); + } + //roll? No, doesn't work... + //limb->s.apos.trDelta[2] = Q_irand( -300, 300 );//FIXME: this scales it down @ 80% and does weird stuff in timescale != 1.0 + //limb->s.apos.trDelta[2] = limbRoll; + + //preserve scale so giants don't have tiny limbs + VectorCopy( ent->s.modelScale, limb->s.modelScale ); + + //mark ent as dismembered + ent->locationDamage[hitLoc] = Q3_INFINITE;//mark this limb as gone + ent->client->dismembered = qtrue; + + return qtrue; +} + +static qboolean G_Dismemberable( gentity_t *self, int hitLoc, int damage ) +{ + if ( self->client->dismembered ) + {//cannot dismember me right now + return qfalse; + } + if ( g_dismemberment->integer < 4 && !g_realisticSaberDamage->integer ) + { + if ( g_dismemberProbabilities->value > 0.0f ) + {//use the ent-specific dismemberProbabilities + float dismemberProb = 0; + // check which part of the body it is. Then check the npc's probability + // of that body part coming off, if it doesn't pass, return out. + switch ( hitLoc ) + { + case HL_LEG_RT: + case HL_LEG_LT: + dismemberProb = self->client->dismemberProbLegs; + break; + case HL_WAIST: + dismemberProb = self->client->dismemberProbWaist; + break; + case HL_BACK_RT: + case HL_BACK_LT: + case HL_CHEST_RT: + case HL_CHEST_LT: + case HL_ARM_RT: + case HL_ARM_LT: + dismemberProb = self->client->dismemberProbArms; + break; + case HL_HAND_RT: + case HL_HAND_LT: + dismemberProb = self->client->dismemberProbHands; + break; + case HL_HEAD: + dismemberProb = self->client->dismemberProbHead; + break; + default: + return qfalse; + break; + } + + //check probability of this hapening on this npc + if ( floor((Q_flrand( 0, 100 )*g_dismemberProbabilities->value)) > dismemberProb ) + { + return qfalse; + } + } + else + {//else add the passed-in damage to the locationDamage array, check to see if it's taken enough damage to actually dismember + if ( self->locationDamage[hitLoc] < (self->client->ps.stats[STAT_MAX_HEALTH]*hitLocHealthPercentage[hitLoc]) ) + {//this location has not taken enough damage to dismember + return qfalse; + } + } + } + return qtrue; +} + +extern qboolean G_StandardHumanoid( const char *modelName ); +qboolean G_DoDismemberment( gentity_t *self, vec3_t point, int mod, int damage, int hitLoc, qboolean force = qfalse ) +{ + // dismemberment -- FIXME: should have a check for how long npc has been dead so people can't + // continue to dismember a dead body long after it's been dead + //NOTE that you can only cut one thing off unless the super dismemberment is > 3 + if ( g_dismemberment->integer && mod == MOD_SABER )//only lightsaber + {//FIXME: don't do strcmps here + if ( G_StandardHumanoid( self->NPC_type ) && (force||G_Dismemberable( self, hitLoc, damage)) ) + {//temp hack because only these models are set up the right way so far + //FIXME: check the hitLoc and hitDir against the cap tag for the place + //where the split will be- if the hit dir is roughly perpendicular to + //the direction of the cap, then the split is allowed, otherwise we + //hit it at the wrong angle and should not dismember... + char *limbBone = NULL, *rotateBone = NULL, *limbName = NULL, *limbCapName = NULL, *stubCapName = NULL, *limbTagName = NULL, *stubTagName = NULL; + int anim = -1; + float limbRollBase = 0, limbPitchBase = 0; + qboolean doDismemberment = qfalse; + + switch( hitLoc )//self->hitLoc + { + case HL_LEG_RT: + if ( g_dismemberment->integer > 1 ) + { + doDismemberment = qtrue; + limbBone = "rtibia"; + rotateBone = "rtalus"; + limbName = "r_leg"; + limbCapName = "r_leg_cap_hips_off"; + stubCapName = "hips_cap_r_leg_off"; + limbTagName = "*r_leg_cap_hips"; + stubTagName = "*hips_cap_r_leg"; + anim = BOTH_DISMEMBER_RLEG; + limbRollBase = 0; + limbPitchBase = 0; + } + break; + case HL_LEG_LT: + if ( g_dismemberment->integer > 1 ) + { + doDismemberment = qtrue; + limbBone = "ltibia"; + rotateBone = "ltalus"; + limbName = "l_leg"; + limbCapName = "l_leg_cap_hips_off"; + stubCapName = "hips_cap_l_leg_off"; + limbTagName = "*l_leg_cap_hips"; + stubTagName = "*hips_cap_l_leg"; + anim = BOTH_DISMEMBER_LLEG; + limbRollBase = 0; + limbPitchBase = 0; + } + break; + case HL_WAIST: + if ( g_dismemberment->integer > 2 && + (!self->s.number||!self->message)) + { + doDismemberment = qtrue; + limbBone = "pelvis"; + rotateBone = "thoracic"; + limbName = "torso"; + limbCapName = "torso_cap_hips_off"; + stubCapName = "hips_cap_torso_off"; + limbTagName = "*torso_cap_hips"; + stubTagName = "*hips_cap_torso"; + anim = BOTH_DISMEMBER_TORSO1; + limbRollBase = 0; + limbPitchBase = 0; + } + break; + case HL_CHEST_RT: + case HL_ARM_RT: + case HL_BACK_RT: + if ( g_dismemberment->integer ) + { + doDismemberment = qtrue; + limbBone = "rhumerus"; + rotateBone = "rradius"; + limbName = "r_arm"; + limbCapName = "r_arm_cap_torso_off"; + stubCapName = "torso_cap_r_arm_off"; + limbTagName = "*r_arm_cap_torso"; + stubTagName = "*torso_cap_r_arm"; + anim = BOTH_DISMEMBER_RARM; + limbRollBase = 0; + limbPitchBase = 0; + } + break; + case HL_CHEST_LT: + case HL_ARM_LT: + case HL_BACK_LT: + if ( g_dismemberment->integer && + (!self->s.number||!self->message)) + {//either the player or not carrying a key on my arm + doDismemberment = qtrue; + limbBone = "lhumerus"; + rotateBone = "lradius"; + limbName = "l_arm"; + limbCapName = "l_arm_cap_torso_off"; + stubCapName = "torso_cap_l_arm_off"; + limbTagName = "*l_arm_cap_torso"; + stubTagName = "*torso_cap_l_arm"; + anim = BOTH_DISMEMBER_LARM; + limbRollBase = 0; + limbPitchBase = 0; + } + break; + case HL_HAND_RT: + if ( g_dismemberment->integer ) + { + doDismemberment = qtrue; + limbBone = "rradiusX"; + rotateBone = "rhand"; + limbName = "r_hand"; + limbCapName = "r_hand_cap_r_arm_off"; + stubCapName = "r_arm_cap_r_hand_off"; + limbTagName = "*r_hand_cap_r_arm"; + stubTagName = "*r_arm_cap_r_hand"; + anim = BOTH_DISMEMBER_RARM; + limbRollBase = 0; + limbPitchBase = 0; + } + break; + case HL_HAND_LT: + if ( g_dismemberment->integer ) + { + doDismemberment = qtrue; + limbBone = "lradiusX"; + rotateBone = "lhand"; + limbName = "l_hand"; + limbCapName = "l_hand_cap_l_arm_off"; + stubCapName = "l_arm_cap_l_hand_off"; + limbTagName = "*l_hand_cap_l_arm"; + stubTagName = "*l_arm_cap_l_hand"; + anim = BOTH_DISMEMBER_RARM; + limbRollBase = 0; + limbPitchBase = 0; + } + break; + case HL_HEAD: + if ( g_dismemberment->integer > 2 ) + { + doDismemberment = qtrue; + limbBone = "cervical"; + rotateBone = "cranium"; + limbName = "head"; + limbCapName = "head_cap_torso_off"; + stubCapName = "torso_cap_head_off"; + limbTagName = "*head_cap_torso"; + stubTagName = "*torso_cap_head"; + anim = BOTH_DISMEMBER_HEAD1; + limbRollBase = -1; + limbPitchBase = -1; + } + break; + case HL_FOOT_RT: + case HL_FOOT_LT: + case HL_CHEST: + case HL_BACK: + default: + break; + } + if ( doDismemberment ) + { + return G_Dismember( self, point, limbBone, rotateBone, limbName, + limbCapName, stubCapName, limbTagName, stubTagName, + anim, limbRollBase, limbPitchBase, damage, hitLoc ); + } + } + } + return qfalse; +} + +static int G_CheckSpecialDeathAnim( gentity_t *self, vec3_t point, int damage, int mod, int hitLoc ) +{ + int deathAnim = -1; + + if ( PM_InRoll( &self->client->ps ) ) + { + deathAnim = BOTH_DEATH_ROLL; //# Death anim from a roll + } + else if ( PM_FlippingAnim( self->client->ps.legsAnim ) ) + { + deathAnim = BOTH_DEATH_FLIP; //# Death anim from a flip + } + else if ( PM_SpinningAnim( self->client->ps.legsAnim ) ) + { + float yawDiff = AngleNormalize180(AngleNormalize180(self->client->renderInfo.torsoAngles[YAW]) - AngleNormalize180(self->client->ps.viewangles[YAW])); + if ( yawDiff > 135 || yawDiff < -135 ) + { + deathAnim = BOTH_DEATH_SPIN_180; //# Death anim when facing backwards + } + else if ( yawDiff < -60 ) + { + deathAnim = BOTH_DEATH_SPIN_90_R; //# Death anim when facing 90 degrees right + } + else if ( yawDiff > 60 ) + { + deathAnim = BOTH_DEATH_SPIN_90_L; //# Death anim when facing 90 degrees left + } + } + else if ( PM_InOnGroundAnim( &self->client->ps ) ) + { + if ( AngleNormalize180(self->client->renderInfo.torsoAngles[PITCH]) < 0 ) + { + deathAnim = BOTH_DEATH_LYING_UP; //# Death anim when lying on back + } + else + { + deathAnim = BOTH_DEATH_LYING_DN; //# Death anim when lying on front + } + } + else if ( PM_InKnockDown( &self->client->ps ) ) + { + if ( AngleNormalize180(self->client->renderInfo.torsoAngles[PITCH]) < 0 ) + { + deathAnim = BOTH_DEATH_FALLING_UP; //# Death anim when falling on back + } + else + { + deathAnim = BOTH_DEATH_FALLING_DN; //# Death anim when falling on face + } + } + else if ( PM_CrouchAnim( self->client->ps.legsAnim ) ) + { + deathAnim = BOTH_DEATH_CROUCHED; //# Death anim when crouched + } + + return deathAnim; +} +extern qboolean PM_FinishedCurrentLegsAnim( gentity_t *self ); +static int G_PickDeathAnim( gentity_t *self, vec3_t point, int damage, int mod, int hitLoc ) +{//FIXME: play dead flop anims on body if in an appropriate _DEAD anim when this func is called + int deathAnim = -1; + if ( hitLoc == HL_NONE ) + { + hitLoc = G_GetHitLocation( self, point );//self->hitLoc + } + //dead flops...if you are already playing a death animation, I guess it can just return directly + switch( self->client->ps.legsAnim ) + { + case BOTH_DEATH1: //# First Death anim + case BOTH_DEAD1: + case BOTH_DEATH2: //# Second Death anim + case BOTH_DEAD2: + case BOTH_DEATH8: //# + case BOTH_DEAD8: + case BOTH_DEATH13: //# + case BOTH_DEAD13: + case BOTH_DEATH14: //# + case BOTH_DEAD14: + case BOTH_DEATH16: //# + case BOTH_DEAD16: + case BOTH_DEADBACKWARD1: //# First thrown backward death finished pose + case BOTH_DEADBACKWARD2: //# Second thrown backward death finished pose + return -2; + break; + /* + if ( PM_FinishedCurrentLegsAnim( self ) ) + {//done with the anim + deathAnim = BOTH_DEADFLOP2; + } + else + { + deathAnim = -2; + } + break; + case BOTH_DEADFLOP2: + deathAnim = BOTH_DEADFLOP2; + break; + */ + case BOTH_DEATH10: //# + case BOTH_DEAD10: + case BOTH_DEATH15: //# + case BOTH_DEAD15: + case BOTH_DEADFORWARD1: //# First thrown forward death finished pose + case BOTH_DEADFORWARD2: //# Second thrown forward death finished pose + return -2; + break; + /* + if ( PM_FinishedCurrentLegsAnim( self ) ) + {//done with the anim + deathAnim = BOTH_DEADFLOP1; + } + else + { + deathAnim = -2; + } + break; + */ + case BOTH_DEADFLOP1: + //deathAnim = BOTH_DEADFLOP1; + return -2; + break; + case BOTH_DEAD3: //# Third Death finished pose + case BOTH_DEAD4: //# Fourth Death finished pose + case BOTH_DEAD5: //# Fifth Death finished pose + case BOTH_DEAD6: //# Sixth Death finished pose + case BOTH_DEAD7: //# Seventh Death finished pose + case BOTH_DEAD9: //# + case BOTH_DEAD11: //# + case BOTH_DEAD12: //# + case BOTH_DEAD17: //# + case BOTH_DEAD18: //# + case BOTH_DEAD19: //# + case BOTH_LYINGDEAD1: //# Killed lying down death finished pose + case BOTH_STUMBLEDEAD1: //# Stumble forward death finished pose + case BOTH_FALLDEAD1LAND: //# Fall forward and splat death finished pose + case BOTH_DEATH3: //# Third Death anim + case BOTH_DEATH4: //# Fourth Death anim + case BOTH_DEATH5: //# Fifth Death anim + case BOTH_DEATH6: //# Sixth Death anim + case BOTH_DEATH7: //# Seventh Death anim + case BOTH_DEATH9: //# + case BOTH_DEATH11: //# + case BOTH_DEATH12: //# + case BOTH_DEATH17: //# + case BOTH_DEATH18: //# + case BOTH_DEATH19: //# + case BOTH_DEATHFORWARD1: //# First Death in which they get thrown forward + case BOTH_DEATHFORWARD2: //# Second Death in which they get thrown forward + case BOTH_DEATHBACKWARD1: //# First Death in which they get thrown backward + case BOTH_DEATHBACKWARD2: //# Second Death in which they get thrown backward + case BOTH_DEATH1IDLE: //# Idle while close to death + case BOTH_LYINGDEATH1: //# Death to play when killed lying down + case BOTH_STUMBLEDEATH1: //# Stumble forward and fall face first death + case BOTH_FALLDEATH1: //# Fall forward off a high cliff and splat death - start + case BOTH_FALLDEATH1INAIR: //# Fall forward off a high cliff and splat death - loop + case BOTH_FALLDEATH1LAND: //# Fall forward off a high cliff and splat death - hit bottom + return -2; + break; + case BOTH_DEATH_ROLL: //# Death anim from a roll + case BOTH_DEATH_FLIP: //# Death anim from a flip + case BOTH_DEATH_SPIN_90_R: //# Death anim when facing 90 degrees right + case BOTH_DEATH_SPIN_90_L: //# Death anim when facing 90 degrees left + case BOTH_DEATH_SPIN_180: //# Death anim when facing backwards + case BOTH_DEATH_LYING_UP: //# Death anim when lying on back + case BOTH_DEATH_LYING_DN: //# Death anim when lying on front + case BOTH_DEATH_FALLING_DN: //# Death anim when falling on face + case BOTH_DEATH_FALLING_UP: //# Death anim when falling on back + case BOTH_DEATH_CROUCHED: //# Death anim when crouched + case BOTH_RIGHTHANDCHOPPEDOFF: + return -2; + break; + } + // Not currently playing a death animation, so try and get an appropriate one now. + if ( deathAnim == -1 ) + { + deathAnim = G_CheckSpecialDeathAnim( self, point, damage, mod, hitLoc ); + + if ( deathAnim == -1 ) + {//base on hitLoc + //death anims + switch( hitLoc ) + { + case HL_FOOT_RT: + case HL_FOOT_LT: + if ( !Q_irand( 0, 2 ) ) + { + deathAnim = BOTH_DEATH4;//back: forward + } + else if ( !Q_irand( 0, 1 ) ) + { + deathAnim = BOTH_DEATH5;//same as 4 + } + else + { + deathAnim = BOTH_DEATH15;//back: forward + } + break; + case HL_LEG_RT: + if ( !Q_irand( 0, 2 ) ) + { + deathAnim = BOTH_DEATH4;//back: forward + } + else if ( !Q_irand( 0, 1 ) ) + { + deathAnim = BOTH_DEATH5;//same as 4 + } + else + { + deathAnim = BOTH_DEATH15;//back: forward + } + break; + case HL_LEG_LT: + if ( !Q_irand( 0, 2 ) ) + { + deathAnim = BOTH_DEATH4;//back: forward + } + else if ( !Q_irand( 0, 1 ) ) + { + deathAnim = BOTH_DEATH5;//same as 4 + } + else + { + deathAnim = BOTH_DEATH15;//back: forward + } + break; + case HL_BACK: + if ( VectorLengthSquared( self->client->ps.velocity ) < 256 ) + { + if ( Q_irand( 0, 1 ) ) + { + deathAnim = BOTH_DEATH17;//head/back: croak + } + else + { + deathAnim = BOTH_DEATH10;//back: bend back, fall forward + } + } + else + { + if ( !Q_irand( 0, 2 ) ) + { + deathAnim = BOTH_DEATH4;//back: forward + } + else if ( !Q_irand( 0, 1 ) ) + { + deathAnim = BOTH_DEATH5;//same as 4 + } + else + { + deathAnim = BOTH_DEATH15;//back: forward + } + } + break; + case HL_HAND_RT: + case HL_CHEST_RT: + case HL_ARM_RT: + case HL_BACK_RT: + if ( deathAnim == -1 ) + { + if ( damage <= self->max_health*0.25 ) + { + deathAnim = BOTH_DEATH9;//chest right: snap, fall forward + } + else if ( damage <= self->max_health*0.5 ) + { + deathAnim = BOTH_DEATH3;//chest right: back + } + else if ( damage <= self->max_health*0.75 ) + { + deathAnim = BOTH_DEATH6;//chest right: spin + } + else + { + //TEMP HACK: play spinny deaths less often + if ( Q_irand( 0, 1 ) ) + { + deathAnim = BOTH_DEATH8;//chest right: spin high + } + else + { + switch ( Q_irand( 0, 2 ) ) + { + default: + case 0: + deathAnim = BOTH_DEATH9;//chest right: snap, fall forward + break; + case 1: + deathAnim = BOTH_DEATH3;//chest right: back + break; + case 2: + deathAnim = BOTH_DEATH6;//chest right: spin + break; + } + } + } + } + break; + case HL_CHEST_LT: + case HL_ARM_LT: + case HL_HAND_LT: + case HL_BACK_LT: + if ( damage <= self->max_health*0.25 ) + { + deathAnim = BOTH_DEATH11;//chest left: snap, fall forward + } + else if ( damage <= self->max_health*0.5 ) + { + deathAnim = BOTH_DEATH7;//chest left: back + } + else if ( damage <= self->max_health*0.75 ) + { + deathAnim = BOTH_DEATH12;//chest left: spin + } + else + { + //TEMP HACK: play spinny deaths less often + if ( Q_irand( 0, 1 ) ) + { + deathAnim = BOTH_DEATH14;//chest left: spin high + } + else + { + switch ( Q_irand( 0, 2 ) ) + { + default: + case 0: + deathAnim = BOTH_DEATH11;//chest left: snap, fall forward + break; + case 1: + deathAnim = BOTH_DEATH7;//chest left: back + break; + case 2: + deathAnim = BOTH_DEATH12;//chest left: spin + break; + } + } + } + break; + case HL_CHEST: + case HL_WAIST: + if ( damage <= self->max_health*0.25 || !VectorLengthSquared( self->client->ps.velocity ) ) + { + if ( !Q_irand( 0, 1 ) ) + { + deathAnim = BOTH_DEATH18;//gut: fall right + } + else + { + deathAnim = BOTH_DEATH19;//gut: fall left + } + } + else if ( damage <= self->max_health*0.5 ) + { + deathAnim = BOTH_DEATH2;//chest: backward short + } + else //if ( damage <= self->max_health*0.75 ) + { + if ( !Q_irand( 0, 1 ) ) + { + deathAnim = BOTH_DEATH1;//chest: backward med + } + else + { + deathAnim = BOTH_DEATH16;//same as 1 + } + } + break; + case HL_HEAD: + if ( damage <= self->max_health*0.5 ) + { + deathAnim = BOTH_DEATH17;//head/back: croak + } + else + { + deathAnim = BOTH_DEATH13;//head: stumble, fall back + } + break; + default: + break; + } + } + } + + // Validate..... + if ( deathAnim == -1 || !PM_HasAnimation( self, deathAnim )) + { + // I guess we'll take what we can get..... + deathAnim = PM_PickAnim( self, BOTH_DEATH1, BOTH_DEATH19 ); + } + + return deathAnim; +} /* ================== player_die @@ -814,18 +2666,23 @@ extern void AI_DeleteSelfFromGroup( gentity_t *self ); extern void AI_GroupMemberKilled( gentity_t *self ); extern qboolean FlyingCreature( gentity_t *ent ); extern void G_DrivableATSTDie( gentity_t *self ); -void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath,int hitLoc ) +void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath, int dflags, int hitLoc ) { int anim; int contents; qboolean deathScript = qfalse; qboolean lastInGroup = qfalse; + qboolean cliff_fall = qfalse; + qboolean specialAnim = qfalse; //FIXME: somehow people are sometimes not completely dying??? if ( self->client->ps.pm_type == PM_DEAD && (meansOfDeath != MOD_SNIPER || (self->flags & FL_DISINTEGRATED)) ) {//do dismemberment/twitching anim = G_PickDeathAnim( self, self->pos1, damage, meansOfDeath, hitLoc ); - G_DoDismemberment( self, self->pos1, meansOfDeath, damage, hitLoc ); + if ( dflags & DAMAGE_DISMEMBER ) + { + G_DoDismemberment( self, self->pos1, meansOfDeath, damage, hitLoc ); + } if ( anim >= 0 ) { NPC_SetAnim(self, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); @@ -833,10 +2690,12 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int return; } +#ifndef _FINAL_BUILD if ( d_saberCombat->integer && attacker && attacker->client ) { - gi.Printf( S_COLOR_RED"combatant %s died, killer anim = %s\n", self->targetname, animTable[attacker->client->ps.torsoAnim].name ); + gi.Printf( S_COLOR_YELLOW"combatant %s died, killer anim = %s\n", self->targetname, animTable[attacker->client->ps.torsoAnim].name ); } +#endif//_FINAL_BUILD if ( self->NPC ) { @@ -855,6 +2714,7 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int if ( self->NPC->tempGoal ) { G_FreeEntity( self->NPC->tempGoal ); + self->NPC->tempGoal = NULL; } if ( self->s.eFlags & EF_LOCKED_TO_WEAPON ) { @@ -892,7 +2752,7 @@ extern void RunEmplacedWeapon( gentity_t *ent, usercmd_t **ucmd ); else { if ( (hitLoc != HL_HAND_RT - || !self->client->dismemberable + || self->client->dismembered || meansOfDeath != MOD_SABER )//if might get hand cut off, leave saber in hand && ( Q_irand( 0, 1 ) || meansOfDeath == MOD_EXPLOSIVE @@ -933,8 +2793,8 @@ extern void RunEmplacedWeapon( gentity_t *ent, usercmd_t **ucmd ); {//falling to death, have not hit yet G_StartMatrixEffect( self, qtrue, 10000 ); } - else - { + else if ( meansOfDeath != MOD_CRUSH ) + {//for all deaths except being crushed G_StartMatrixEffect( self ); } } @@ -1209,6 +3069,7 @@ extern void RunEmplacedWeapon( gentity_t *ent, usercmd_t **ucmd ); } } NPC_SetAnim(self, SETANIM_BOTH, deathAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); + //HMM: check for nodrop? G_SoundOnEnt( self, CHAN_BODY, "sound/player/fallsplat.wav" ); if ( gi.VoiceVolume[self->s.number] && self->NPC && (self->NPC->aiFlags&NPCAI_DIE_ON_IMPACT) ) @@ -1219,7 +3080,6 @@ extern void RunEmplacedWeapon( gentity_t *ent, usercmd_t **ucmd ); } else {// normal death - qboolean cliff_fall = qfalse; anim = G_CheckSpecialDeathAnim( self, self->pos1, damage, meansOfDeath, hitLoc ); if ( anim == -1 ) { @@ -1231,6 +3091,10 @@ extern void RunEmplacedWeapon( gentity_t *ent, usercmd_t **ucmd ); {//on ground, need different death anim anim = BOTH_LYINGDEATH1; } + else if ( meansOfDeath == MOD_TRIGGER_HURT && (self->s.powerups&(1<client->ps.gravity *= 0.8; self->client->ps.friction = 0; - if ( PM_HasAnimation( self, BOTH_DEATHBACKWARD2) && !Q_irand( 0, 3 ) ) - { - anim = Q_irand(BOTH_DEATHBACKWARD1, BOTH_DEATHBACKWARD2); - } - else + switch ( Q_irand( 0, 12 ) ) { + case 0: + case 1: + case 2: + case 3: + anim = BOTH_DEATH1; + break; + case 4: + case 5: + case 6: + case 7: + anim = BOTH_DEATH2; + break; + case 8: + case 9: + case 10: + case 11: anim = BOTH_DEATHBACKWARD1; + break; + case 12: + if ( thrown >= 250 ) + { + anim = BOTH_DEATHBACKWARD2; + } + else + { + anim = BOTH_DEATHBACKWARD1; + } + break; } + if ( !PM_HasAnimation( self, anim ) ) + { + anim = -1; + } } else {//falling to one of the sides @@ -1374,6 +3265,10 @@ extern void RunEmplacedWeapon( gentity_t *ent, usercmd_t **ucmd ); } } } + else + { + specialAnim = qtrue; + } if ( anim == -1 ) { @@ -1464,8 +3359,12 @@ extern void RunEmplacedWeapon( gentity_t *ent, usercmd_t **ucmd ); { G_AddEvent( self, Q_irand(EV_DEATH1, EV_DEATH3), self->health ); } + G_DeathAlert( self, attacker ); + } + else + {//screaming death is louder + G_AlertTeam( self, attacker, 512, 1024 ); } - G_DeathAlert( self, attacker ); } } @@ -1483,7 +3382,13 @@ extern void RunEmplacedWeapon( gentity_t *ent, usercmd_t **ucmd ); } //do any dismemberment if there's any to do... - G_DoDismemberment( self, self->pos1, meansOfDeath, damage, hitLoc ); + if ( (dflags&DAMAGE_DISMEMBER) && G_DoDismemberment( self, self->pos1, meansOfDeath, damage, hitLoc ) && !specialAnim ) + {//we did dismemberment and our death anim is okay to override + if ( hitLoc == HL_HAND_RT && self->locationDamage[hitLoc] >= Q3_INFINITE && !cliff_fall && self->client->ps.groundEntityNum != ENTITYNUM_NONE ) + {//just lost our right hand and we're on the ground, use the special anim + NPC_SetAnim( self, SETANIM_BOTH, BOTH_RIGHTHANDCHOPPEDOFF, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } + } // don't allow player to respawn for a few seconds self->client->respawnTime = level.time + 2000;//self->client->ps.legsAnimTimer; @@ -1513,7 +3418,7 @@ extern void RunEmplacedWeapon( gentity_t *ent, usercmd_t **ucmd ); deathScript = qtrue; } - if ( self->NPC && (self->NPC->scriptFlags&SCF_FFDEATH) && G_ActivateBehavior( self, BSET_FFDEATH ) ) + if ( self->NPC && (self->NPC->scriptFlags&SCF_FFDEATH) && G_ActivateBehavior( self, BSET_FFDEATH ) ) {//FIXME: should running this preclude running the normal deathscript? deathScript = qtrue; } @@ -1834,1445 +3739,13 @@ int G_CheckForLedge( gentity_t *self, vec3_t fallCheckDir ) return 0; } -qboolean G_GetHitLocFromSurfName( gentity_t *ent, const char *surfName, int *hitLoc, vec3_t point, vec3_t dir, vec3_t bladeDir ) -{ - qboolean dismember = qfalse; - - *hitLoc = HL_NONE; - - if ( !surfName || !surfName[0] ) - { - return qfalse; - } - - - if ( ent->client && (ent->client->NPC_class == CLASS_ATST) ) - { - //FIXME: almost impossible to hit these... perhaps we should - // check for splashDamage and do radius damage to these parts? - // Or, if we ever get bbox G2 traces, that may fix it, too - if (!Q_stricmp("head_light_blaster_cann",surfName)) - { - *hitLoc = HL_ARM_LT; - } - else if (!Q_stricmp("head_concussion_charger",surfName)) - { - *hitLoc = HL_ARM_RT; - } - return(qfalse); - } - else if ( ent->client && (ent->client->NPC_class == CLASS_MARK1) ) - { - if (!Q_stricmp("l_arm",surfName)) - { - *hitLoc = HL_ARM_LT; - } - else if (!Q_stricmp("r_arm",surfName)) - { - *hitLoc = HL_ARM_RT; - } - else if (!Q_stricmp("torso_front",surfName)) - { - *hitLoc = HL_CHEST; - } - else if (!Q_stricmp("torso_tube1",surfName)) - { - *hitLoc = HL_GENERIC1; - } - else if (!Q_stricmp("torso_tube2",surfName)) - { - *hitLoc = HL_GENERIC2; - } - else if (!Q_stricmp("torso_tube3",surfName)) - { - *hitLoc = HL_GENERIC3; - } - else if (!Q_stricmp("torso_tube4",surfName)) - { - *hitLoc = HL_GENERIC4; - } - else if (!Q_stricmp("torso_tube5",surfName)) - { - *hitLoc = HL_GENERIC5; - } - else if (!Q_stricmp("torso_tube6",surfName)) - { - *hitLoc = HL_GENERIC6; - } - return(qfalse); - } - else if ( ent->client && (ent->client->NPC_class == CLASS_MARK2) ) - { - if (!Q_stricmp("torso_canister1",surfName)) - { - *hitLoc = HL_GENERIC1; - } - else if (!Q_stricmp("torso_canister2",surfName)) - { - *hitLoc = HL_GENERIC2; - } - else if (!Q_stricmp("torso_canister3",surfName)) - { - *hitLoc = HL_GENERIC3; - } - return(qfalse); - } - else if ( ent->client && (ent->client->NPC_class == CLASS_GALAKMECH) ) - { - if (!Q_stricmp("torso_antenna",surfName)||!Q_stricmp("torso_antenna_base",surfName)) - { - *hitLoc = HL_GENERIC1; - } - else if (!Q_stricmp("torso_shield",surfName)) - { - *hitLoc = HL_GENERIC2; - } - else - { - *hitLoc = HL_CHEST; - } - return(qfalse); - } - - //FIXME: check the hitLoc and hitDir against the cap tag for the place - //where the split will be- if the hit dir is roughly perpendicular to - //the direction of the cap, then the split is allowed, otherwise we - //hit it at the wrong angle and should not dismember... - int actualTime = (cg.time?cg.time:level.time); - if ( !Q_strncmp( "hips", surfName, 4 ) ) - {//FIXME: test properly for legs - *hitLoc = HL_WAIST; - if ( ent->client != NULL && ent->ghoul2.size() ) - { - mdxaBone_t boltMatrix; - vec3_t tagOrg, angles; - - VectorSet( angles, 0, ent->currentAngles[YAW], 0 ); - gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, ent->kneeLBolt, - &boltMatrix, angles, ent->currentOrigin, - actualTime, NULL, ent->s.modelScale ); - gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, tagOrg ); - if ( DistanceSquared( point, tagOrg ) < 100 ) - {//actually hit the knee - *hitLoc = HL_LEG_LT; - } - else - { - gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, ent->kneeRBolt, - &boltMatrix, angles, ent->currentOrigin, - actualTime, NULL, ent->s.modelScale ); - gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, tagOrg ); - if ( DistanceSquared( point, tagOrg ) < 100 ) - {//actually hit the knee - *hitLoc = HL_LEG_RT; - } - } - } - } - else if ( !Q_strncmp( "torso", surfName, 5 ) ) - { - if ( !ent->client ) - { - *hitLoc = HL_CHEST; - } - else - { - vec3_t t_fwd, t_rt, t_up, dirToImpact; - float frontSide, rightSide, upSide; - AngleVectors( ent->client->renderInfo.torsoAngles, t_fwd, t_rt, t_up ); - VectorSubtract( point, ent->client->renderInfo.torsoPoint, dirToImpact ); - frontSide = DotProduct( t_fwd, dirToImpact ); - rightSide = DotProduct( t_rt, dirToImpact ); - upSide = DotProduct( t_up, dirToImpact ); - if ( upSide < 0 ) - {//hit at waist - *hitLoc = HL_WAIST; - } - else - {//hit on upper torso - if ( rightSide > 10 ) - { - *hitLoc = HL_ARM_RT; - } - else if ( rightSide < -10 ) - { - *hitLoc = HL_ARM_LT; - } - else if ( rightSide > 4 ) - { - if ( frontSide > 0 ) - { - *hitLoc = HL_CHEST_RT; - } - else - { - *hitLoc = HL_BACK_RT; - } - } - else if ( rightSide < -4 ) - { - if ( frontSide > 0 ) - { - *hitLoc = HL_CHEST_LT; - } - else - { - *hitLoc = HL_BACK_LT; - } - } - else if ( upSide > 6 ) - { - *hitLoc = HL_HEAD; - } - else if ( frontSide > 0 ) - { - *hitLoc = HL_CHEST; - } - else - { - *hitLoc = HL_BACK; - } - } - } - } - else if ( !Q_strncmp( "head", surfName, 4 ) ) - { - *hitLoc = HL_HEAD; - } - else if ( !Q_strncmp( "r_arm", surfName, 5 ) ) - { - *hitLoc = HL_ARM_RT; - if ( ent->client != NULL && ent->ghoul2.size() ) - { - mdxaBone_t boltMatrix; - vec3_t tagOrg, angles; - - VectorSet( angles, 0, ent->currentAngles[YAW], 0 ); - gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, ent->handRBolt, - &boltMatrix, angles, ent->currentOrigin, - actualTime, NULL, ent->s.modelScale ); - gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, tagOrg ); - if ( DistanceSquared( point, tagOrg ) < 256 ) - {//actually hit the hand - *hitLoc = HL_HAND_RT; - } - } - } - else if ( !Q_strncmp( "l_arm", surfName, 5 ) ) - { - *hitLoc = HL_ARM_LT; - if ( ent->client != NULL && ent->ghoul2.size() ) - { - mdxaBone_t boltMatrix; - vec3_t tagOrg, angles; - - VectorSet( angles, 0, ent->currentAngles[YAW], 0 ); - gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, ent->handLBolt, - &boltMatrix, angles, ent->currentOrigin, - actualTime, NULL, ent->s.modelScale ); - gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, tagOrg ); - if ( DistanceSquared( point, tagOrg ) < 256 ) - {//actually hit the hand - *hitLoc = HL_HAND_LT; - } - } - } - else if ( !Q_strncmp( "r_leg", surfName, 5 ) ) - { - *hitLoc = HL_LEG_RT; - if ( ent->client != NULL && ent->ghoul2.size() ) - { - mdxaBone_t boltMatrix; - vec3_t tagOrg, angles; - - VectorSet( angles, 0, ent->currentAngles[YAW], 0 ); - gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, ent->footRBolt, - &boltMatrix, angles, ent->currentOrigin, - actualTime, NULL, ent->s.modelScale ); - gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, tagOrg ); - if ( DistanceSquared( point, tagOrg ) < 100 ) - {//actually hit the foot - *hitLoc = HL_FOOT_RT; - } - } - } - else if ( !Q_strncmp( "l_leg", surfName, 5 ) ) - { - *hitLoc = HL_LEG_LT; - if ( ent->client != NULL && ent->ghoul2.size() ) - { - mdxaBone_t boltMatrix; - vec3_t tagOrg, angles; - - VectorSet( angles, 0, ent->currentAngles[YAW], 0 ); - gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, ent->footLBolt, - &boltMatrix, angles, ent->currentOrigin, - actualTime, NULL, ent->s.modelScale ); - gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, tagOrg ); - if ( DistanceSquared( point, tagOrg ) < 100 ) - {//actually hit the foot - *hitLoc = HL_FOOT_LT; - } - } - } - else if ( !Q_strncmp( "r_hand", surfName, 6 ) ) - { - *hitLoc = HL_HAND_RT; - } - else if ( !Q_strncmp( "l_hand", surfName, 6 ) ) - { - *hitLoc = HL_HAND_LT; - } - if ( g_realisticSaberDamage->integer ) - { - dismember = qtrue; - } - else - { - if ( dir && (dir[0] || dir[1] || dir[2]) && - bladeDir && (bladeDir[0] || bladeDir[1] || bladeDir[2]) ) - {//we care about direction (presumably for dismemberment) - char *tagName = NULL; - //dir must be roughly perpendicular to the hitLoc's cap bolt - switch ( *hitLoc ) - { - case HL_LEG_RT: - tagName = "*hips_cap_r_leg"; - break; - case HL_LEG_LT: - tagName = "*hips_cap_l_leg"; - break; - case HL_WAIST: - tagName = "*hips_cap_torso"; - break; - case HL_CHEST_RT: - case HL_ARM_RT: - case HL_BACK_RT: - tagName = "*torso_cap_r_arm"; - break; - case HL_CHEST_LT: - case HL_ARM_LT: - case HL_BACK_LT: - tagName = "*torso_cap_l_arm"; - break; - case HL_HAND_RT: - tagName = "*r_arm_cap_r_hand"; - break; - case HL_HAND_LT: - tagName = "*l_arm_cap_l_hand"; - break; - case HL_HEAD: - tagName = "*torso_cap_head"; - break; - case HL_CHEST: - case HL_BACK: - case HL_FOOT_RT: - case HL_FOOT_LT: - default: - //no dismemberment possible with these, so no checks needed - break; - } - if ( tagName ) - { - int tagBolt = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], tagName ); - if ( tagBolt != -1 ) - { - mdxaBone_t boltMatrix; - vec3_t tagOrg, tagDir, angles; - VectorSet( angles, 0, ent->currentAngles[YAW], 0 ); - gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, tagBolt, - &boltMatrix, angles, ent->currentOrigin, - actualTime, NULL, ent->s.modelScale ); - gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, tagOrg ); - gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, tagDir ); - if ( DistanceSquared( point, tagOrg ) < 256 ) - {//hit close - float dot = DotProduct( dir, tagDir ); - if ( dot < 0.25 && dot > -0.25 ) - {//hit roughly perpendicular - dot = DotProduct( bladeDir, tagDir ); - if ( dot < 0.25 && dot > -0.25 ) - {//blade was roughly perpendicular - dismember = qtrue; - } - } - } - } - } - } - } - - return dismember; -} - -int G_GetHitLocation ( gentity_t *target, vec3_t ppoint ) -{ - vec3_t point, point_dir; - vec3_t forward, right, up; - vec3_t tangles, tcenter; - float tradius; - float udot, fdot, rdot; - int Vertical, Forward, Lateral; - int HitLoc; - -//get target forward, right and up - if(target->client) - {//ignore player's pitch and roll - VectorSet(tangles, 0, target->currentAngles[YAW], 0); - } - - AngleVectors(tangles, forward, right, up); - -//get center of target - VectorAdd(target->absmin, target->absmax, tcenter); - VectorScale(tcenter, 0.5, tcenter); - -//get radius width of target - tradius = (fabs(target->maxs[0]) + fabs(target->maxs[1]) + fabs(target->mins[0]) + fabs(target->mins[1]))/4; - -//get impact point - if(ppoint && !VectorCompare(ppoint, vec3_origin)) - { - VectorCopy(ppoint, point); - } - else - { - return HL_NONE; - } - -/* -//get impact dir - if(pdir && !VectorCompare(pdir, vec3_origin)) - { - VectorCopy(pdir, dir); - } - else - { - return; - } - -//put point at controlled distance from center - VectorSubtract(point, tcenter, tempvec); - tempvec[2] = 0; - hdist = VectorLength(tempvec); - - VectorMA(point, hdist - tradius, dir, point); - //now a point on the surface of a cylinder with a radius of tradius -*/ - VectorSubtract(point, tcenter, point_dir); - VectorNormalize(point_dir); - - //Get bottom to top (Vertical) position index - udot = DotProduct(up, point_dir); - if(udot>.800) - Vertical = 4; - else if(udot>.400) - Vertical = 3; - else if(udot>-.333) - Vertical = 2; - else if(udot>-.666) - Vertical = 1; - else - Vertical = 0; - - //Get back to front (Forward) position index - fdot = DotProduct(forward, point_dir); - if(fdot>.666) - Forward = 4; - else if(fdot>.333) - Forward = 3; - else if(fdot>-.333) - Forward = 2; - else if(fdot>-.666) - Forward = 1; - else - Forward = 0; - - //Get left to right (Lateral) position index - rdot = DotProduct(right, point_dir); - if(rdot>.666) - Lateral = 4; - else if(rdot>.333) - Lateral = 3; - else if(rdot>-.333) - Lateral = 2; - else if(rdot>-.666) - Lateral = 1; - else - Lateral = 0; - - HitLoc = Vertical * 25 + Forward * 5 + Lateral; - - if(HitLoc <= 10) - {//feet - if ( rdot > 0 ) - { - return HL_FOOT_RT; - } - else - { - return HL_FOOT_LT; - } - } - else if(HitLoc <= 50) - {//legs - if ( rdot > 0 ) - { - return HL_LEG_RT; - } - else - { - return HL_LEG_LT; - } - } - else if ( HitLoc == 56||HitLoc == 60||HitLoc == 61||HitLoc == 65||HitLoc == 66||HitLoc == 70 ) - {//hands - if ( rdot > 0 ) - { - return HL_HAND_RT; - } - else - { - return HL_HAND_LT; - } - } - else if ( HitLoc == 83||HitLoc == 87||HitLoc == 88||HitLoc == 92||HitLoc == 93||HitLoc == 97 ) - {//arms - if ( rdot > 0 ) - { - return HL_ARM_RT; - } - else - { - return HL_ARM_LT; - } - } - else if((HitLoc >= 107 && HitLoc <= 109)|| - (HitLoc >= 112 && HitLoc <= 114)|| - (HitLoc >= 117 && HitLoc <= 119)) - {//head - return HL_HEAD; - } - else - { - if ( udot < 0.3 ) - { - return HL_WAIST; - } - else if ( fdot < 0 ) - { - if ( rdot > 0.4 ) - { - return HL_BACK_RT; - } - else if ( rdot < -0.4 ) - { - return HL_BACK_LT; - } - else if ( fdot < 0 ) - { - return HL_BACK; - } - } - else - { - if ( rdot > 0.3 ) - { - return HL_CHEST_RT; - } - else if ( rdot < -0.3 ) - { - return HL_CHEST_LT; - } - else if ( fdot < 0 ) - { - return HL_CHEST; - } - } - } - return HL_NONE; -} - -int G_PickPainAnim( gentity_t *self, vec3_t point, int damage, int hitLoc = HL_NONE ) -{ - if ( hitLoc == HL_NONE ) - { - hitLoc = G_GetHitLocation( self, point ); - } - switch( hitLoc ) - { - case HL_FOOT_RT: - return BOTH_PAIN12; - //PAIN12 = right foot - break; - case HL_FOOT_LT: - return -1; - break; - case HL_LEG_RT: - if ( !Q_irand( 0, 1 ) ) - { - return BOTH_PAIN11; - } - else - { - return BOTH_PAIN13; - } - //PAIN11 = twitch right leg - //PAIN13 = right knee - break; - case HL_LEG_LT: - return BOTH_PAIN14; - //PAIN14 = twitch left leg - break; - case HL_BACK_RT: - return BOTH_PAIN7; - //PAIN7 = med left shoulder - break; - case HL_BACK_LT: - return Q_irand( BOTH_PAIN15, BOTH_PAIN16 ); - //PAIN15 = med right shoulder - //PAIN16 = twitch right shoulder - break; - case HL_BACK: - if ( !Q_irand( 0, 1 ) ) - { - return BOTH_PAIN1; - } - else - { - return BOTH_PAIN5; - } - //PAIN1 = back - //PAIN5 = same as 1 - break; - case HL_CHEST_RT: - return BOTH_PAIN3; - //PAIN3 = long, right shoulder - break; - case HL_CHEST_LT: - return BOTH_PAIN2; - //PAIN2 = long, left shoulder - break; - case HL_WAIST: - case HL_CHEST: - if ( !Q_irand( 0, 3 ) ) - { - return BOTH_PAIN6; - } - else if ( !Q_irand( 0, 2 ) ) - { - return BOTH_PAIN8; - } - else if ( !Q_irand( 0, 1 ) ) - { - return BOTH_PAIN17; - } - else - { - return BOTH_PAIN19; - } - //PAIN6 = gut - //PAIN8 = chest - //PAIN17 = twitch crotch - //PAIN19 = med crotch - break; - case HL_ARM_RT: - case HL_HAND_RT: - return BOTH_PAIN9; - //PAIN9 = twitch right arm - break; - case HL_ARM_LT: - case HL_HAND_LT: - return BOTH_PAIN10; - //PAIN10 = twitch left arm - break; - case HL_HEAD: - return BOTH_PAIN4; - //PAIN4 = head - break; - default: - return -1; - break; - } -} - -extern void G_BounceMissile( gentity_t *ent, trace_t *trace ); -void LimbThink( gentity_t *ent ) -{//FIXME: just use object thinking? - vec3_t origin; - trace_t tr; - - ent->nextthink = level.time + FRAMETIME; - if ( ent->enemy ) - {//alert people that I am a piece of one of their friends - AddSightEvent( ent->enemy, ent->currentOrigin, 384, AEL_DISCOVERED ); - } - - // get current position - EvaluateTrajectory( &ent->s.pos, level.time, origin ); - // get current angles - EvaluateTrajectory( &ent->s.apos, level.time, ent->currentAngles ); - - // trace a line from the previous position to the current position, - // ignoring interactions with the missile owner - gi.trace( &tr, ent->currentOrigin, ent->mins, ent->maxs, origin, - ent->owner ? ent->owner->s.number : ENTITYNUM_NONE, ent->clipmask ); - - VectorCopy( tr.endpos, ent->currentOrigin ); - - if ( tr.startsolid ) - { - tr.fraction = 0; - } - - gi.linkentity( ent ); - - if ( tr.fraction != 1 ) - { - G_BounceMissile( ent, &tr ); - if ( ent->s.pos.trType == TR_STATIONARY ) - {//stopped, stop spinning - //lay flat - //pitch - if ( ent->s.apos.trBase[0] == -1 ) - {//any pitch is okay - ent->s.apos.trBase[0] = ent->currentAngles[0]; - } - else - {//lay flat - if ( ent->currentAngles[0] > ent->s.apos.trBase[0]+90 || ent->currentAngles[0] < ent->s.apos.trBase[0]-90 ) - { - ent->s.apos.trBase[0] += 180; - } - } - //yaw - ent->s.apos.trBase[1] = ent->currentAngles[1]; - //roll - if ( ent->s.apos.trBase[2] == -1 ) - {//any roll is okay - ent->s.apos.trBase[2] = ent->currentAngles[2]; - } - else - { - if ( ent->currentAngles[2] > ent->s.apos.trBase[2]+90 || ent->currentAngles[2] < ent->s.apos.trBase[2]-90 ) - { - ent->s.apos.trBase[2] += 180; - } - } - VectorClear( ent->s.apos.trDelta ); - ent->nextthink = level.time + Q_irand( 5000, 15000 ); - ent->e_ThinkFunc = thinkF_G_FreeEntity; - //FIXME: these keep drawing for a frame or so after being freed?! See them lerp to origin of world... - } - } -} - -float hitLocHealthPercentage[HL_MAX] = -{ - 0.0f, //HL_NONE = 0, - 0.05f, //HL_FOOT_RT, - 0.05f, //HL_FOOT_LT, - 0.20f, //HL_LEG_RT, - 0.20f, //HL_LEG_LT, - 0.30f, //HL_WAIST, - 0.15f, //HL_BACK_RT, - 0.15f, //HL_BACK_LT, - 0.30f, //HL_BACK, - 0.15f, //HL_CHEST_RT, - 0.15f, //HL_CHEST_LT, - 0.30f, //HL_CHEST, - 0.05f, //HL_ARM_RT, - 0.05f, //HL_ARM_LT, - 0.01f, //HL_HAND_RT, - 0.01f, //HL_HAND_LT, - 0.10f //HL_HEAD -}; - -void G_Dismember( gentity_t *ent, vec3_t point, - const char *limbBone, const char *rotateBone, char *limbName, - char *limbCapName, char *stubCapName, char *limbTagName, char *stubTagName, - int limbAnim, float limbRollBase, float limbPitchBase, - int damage, int hitLoc ) -{ - int newBolt; - vec3_t dir, newPoint; - gentity_t *limb; - - //make sure this limb hasn't been lopped off already! - if ( gi.G2API_GetSurfaceRenderStatus( &ent->ghoul2[ent->playerModel], limbName ) ) - {//already lost this limb - return; - } - - //add the passed-in damage to the locationDamage array, check to see if it's taken enough damage to actually dismember - if ( g_dismemberment->integer < 4 - && !g_realisticSaberDamage->integer && - ent->locationDamage[hitLoc] < ent->client->ps.stats[STAT_MAX_HEALTH]*hitLocHealthPercentage[hitLoc] ) - {//this location has not taken enough damage to dismember - return; - } - - /* - float dismemberProb = 0; - // check which part of the body it is. Then check the npc's probability - // of that body part coming off, if it doesn't pass, return out. - switch(limbAnim) - { - case BOTH_DISMEMBER_RLEG: - case BOTH_DISMEMBER_LLEG: - dismemberProb = ent->client->dismemberProbLegs; - break; - case BOTH_DISMEMBER_TORSO1: - dismemberProb = ent->client->dismemberProbWaist; - break; - case BOTH_DISMEMBER_RARM: - case BOTH_DISMEMBER_LARM: - dismemberProb = ent->client->dismemberProbArms; - break; - case BOTH_DISMEMBER_HEAD1: - dismemberProb = ent->client->dismemberProbHead; - break; - } - - //check probability of this hapening on this npc - if ( (random()*100) > dismemberProb ) - { - return; - } - */ - - - ent->client->dismemberable = qfalse; - -//0) create a limb ent - VectorCopy( point, newPoint ); - newPoint[2] += 6; - limb = G_Spawn(); - G_SetOrigin( limb, newPoint ); - VectorCopy( newPoint, limb->s.pos.trBase ); -//1) copy the g2 instance of the victim into the limb - gi.G2API_CopyGhoul2Instance( ent->ghoul2, limb->ghoul2 );//, int modelIndex = -1); - limb->playerModel = 0;//assumption! - limb->craniumBone = ent->craniumBone; - limb->cervicalBone = ent->cervicalBone; - limb->thoracicBone = ent->thoracicBone; - limb->upperLumbarBone = ent->upperLumbarBone; - limb->lowerLumbarBone = ent->lowerLumbarBone; - limb->rootBone = ent->rootBone; -//2) set the root surf on the limb - if ( limbTagName ) - {//add smoke to cap tag - newBolt = gi.G2API_AddBolt( &limb->ghoul2[limb->playerModel], limbTagName ); - if ( newBolt != -1 ) - { - G_PlayEffect( "blaster/smoke_bolton", limb->playerModel, newBolt, limb->s.number); - } - } - /* - if ( limbBone ) - { - gi.G2API_StopBoneAnim( &limb->ghoul2[limb->playerModel], "model_root" ); - gi.G2API_StopBoneAnim( &limb->ghoul2[limb->playerModel], "upper_lumbar" ); - } - */ - gi.G2API_SetRootSurface( limb->ghoul2, limb->playerModel, limbName ); - /* - if ( limbBone ) - { - animation_t *animations = knownAnimFileSets[ent->client->clientInfo.animFileIndex].animations; - //play the proper dismember anim on the limb - gi.G2API_SetBoneAnim(&limb->ghoul2[limb->playerModel], limbBone, animations[limbAnim].firstFrame - 1, - animations[limbAnim].numFrames + animations[limbAnim].firstFrame - 1, - BONE_ANIM_OVERRIDE_FREEZE, 1, cg.time); - } - */ - if ( rotateBone ) - { - gi.G2API_SetNewOrigin( &limb->ghoul2[0], gi.G2API_AddBolt( &limb->ghoul2[0], rotateBone ) ); - } - if ( limbCapName ) - {//turn on caps - gi.G2API_SetSurfaceOnOff( &limb->ghoul2[limb->playerModel], limbCapName, 0 ); - } -//3) turn off w/descendants that surf in original model - if ( stubTagName ) - {//add smoke to cap surf, spawn effect - newBolt = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], stubTagName ); - if ( newBolt != -1 ) - { - G_PlayEffect( "blaster/smoke_bolton", ent->playerModel, newBolt, ent->s.number); - } - } - gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], limbName, 0x00000100 );//G2SURFACEFLAG_NODESCENDANTS - if ( stubCapName ) - {//turn on caps - gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], stubCapName, 0 ); - } - limb->s.radius = 60; -//4) toss the limb away - limb->classname = "limb"; - limb->owner = ent; - limb->enemy = ent->enemy; - if ( limbAnim == BOTH_DISMEMBER_RARM || limbAnim == BOTH_DISMEMBER_TORSO1 )//&& ent->s.weapon == WP_SABER && ent->weaponModel != -1 ) - {//FIXME: is this first check needed with this lower one? - if ( !gi.G2API_GetSurfaceRenderStatus( &limb->ghoul2[0], "r_hand" ) ) - {//only copy the weapon over if the right hand is actually on this limb... - limb->s.weapon = ent->s.weapon; - limb->weaponModel = ent->weaponModel; - } - } - limb->e_ThinkFunc = thinkF_LimbThink; - limb->nextthink = level.time + FRAMETIME; - gi.linkentity( limb ); - //need size, contents, clipmask - limb->svFlags = SVF_USE_CURRENT_ORIGIN; - limb->clipmask = MASK_SOLID; - limb->contents = CONTENTS_CORPSE; - VectorSet( limb->mins, -3.0f, -3.0f, -6.0f ); - VectorSet( limb->maxs, 3.0f, 3.0f, 6.0f ); - //move it - limb->s.eType = ET_GENERAL; - limb->physicsBounce = 0.2f; - limb->s.pos.trType = TR_GRAVITY; - limb->s.pos.trTime = level.time; // move a bit on the very first frame - VectorSubtract( point, ent->currentOrigin, dir ); - VectorNormalize( dir ); - VectorMA( ent->client->ps.velocity, 200, dir, limb->s.pos.trDelta ); - //make it bounce some - limb->s.eFlags |= EF_BOUNCE_HALF; - //no trDuration? - //spin it - VectorClear( limb->s.apos.trBase ); - limb->s.apos.trBase[0] = limbPitchBase; - limb->s.apos.trBase[1] = ent->client->ps.viewangles[1]; - limb->s.apos.trBase[2] = limbRollBase; - limb->s.apos.trTime = level.time; - limb->s.apos.trType = TR_LINEAR; - VectorClear( limb->s.apos.trDelta ); - - limb->s.apos.trDelta[0] = Q_irand( -300, 300 ); - limb->s.apos.trDelta[1] = Q_irand( -800, 800 ); - //limb->s.apos.trDelta[2] = Q_irand( -300, 300 );//FIXME: this scales it down @ 80% and does weird stuff in timescale != 1.0 - //limb->s.apos.trDelta[2] = limbRoll; - - //preserve scale so giants don't have tiny limbs - VectorCopy( ent->s.modelScale, limb->s.modelScale ); - - //FIXME: make limb dismemberable? -} - -extern qboolean G_StandardHumanoid( const char *modelName ); -static void G_DoDismemberment( gentity_t *self, vec3_t point, int mod, int damage, int hitLoc ) -{ - // dismemberment -- FIXME: should have a check for how long npc has been dead so people can't - // continue to dismember a dead body long after it's been dead - //NOTE that you can only cut one thing off unless the super dismemberment is > 3 - if ( g_dismemberment->integer && self->client->dismemberable && mod == MOD_SABER )//only lightsaber - {//FIXME: don't do strcmps here - if ( G_StandardHumanoid( self->NPC_type ) ) - {//temp hack because only these models are set up the right way so far - //FIXME: check the hitLoc and hitDir against the cap tag for the place - //where the split will be- if the hit dir is roughly perpendicular to - //the direction of the cap, then the split is allowed, otherwise we - //hit it at the wrong angle and should not dismember... - switch( hitLoc )//self->hitLoc - { - case HL_LEG_RT: - if ( g_dismemberment->integer > 1 ) - { - G_Dismember( self, point, "rtibia", "rtalus", "r_leg", "r_leg_cap_hips_off", "hips_cap_r_leg_off", "*r_leg_cap_hips", "*hips_cap_r_leg", BOTH_DISMEMBER_RLEG, 90, 0, damage, hitLoc ); - } - break; - case HL_LEG_LT: - if ( g_dismemberment->integer > 1 ) - { - G_Dismember( self, point, "ltibia", "ltalus", "l_leg", "l_leg_cap_hips_off", "hips_cap_l_leg_off", "*l_leg_cap_hips", "*hips_cap_l_leg", BOTH_DISMEMBER_LLEG, 90, 0, damage, hitLoc ); - } - break; - case HL_WAIST: - if ( g_dismemberment->integer > 2 && - (!self->s.number||!self->message)) - { - G_Dismember( self, point, "pelvis", "thoracic", "torso", "torso_cap_hips_off", "hips_cap_torso_off", "*torso_cap_hips", "*hips_cap_torso", BOTH_DISMEMBER_TORSO1, 0, 0, damage, hitLoc );//deathAnim - } - break; - case HL_CHEST_RT: - case HL_ARM_RT: - case HL_BACK_RT: - if ( g_dismemberment->integer ) - { - G_Dismember( self, point, "rhumerus", "rradius", "r_arm", "r_arm_cap_torso_off", "torso_cap_r_arm_off", "*r_arm_cap_torso", "*torso_cap_r_arm", BOTH_DISMEMBER_RARM, 90, 0, damage, hitLoc ); - } - break; - case HL_CHEST_LT: - case HL_ARM_LT: - case HL_BACK_LT: - if ( g_dismemberment->integer && - (!self->s.number||!self->message)) - {//either the player or not carrying a key on my arm - G_Dismember( self, point, "lhumerus", "lradius", "l_arm", "l_arm_cap_torso_off", "torso_cap_l_arm_off", "*l_arm_cap_torso", "*torso_cap_l_arm", BOTH_DISMEMBER_LARM, 90, 0, damage, hitLoc ); - } - break; - case HL_HAND_RT: - if ( g_dismemberment->integer ) - { - G_Dismember( self, point, "rradiusX", "rhand", "r_hand", "r_hand_cap_r_arm_off", "r_arm_cap_r_hand_off", "*r_hand_cap_r_arm", "*r_arm_cap_r_hand", BOTH_DISMEMBER_RARM, 90, 0, damage, hitLoc ); - } - break; - case HL_HAND_LT: - if ( g_dismemberment->integer ) - { - G_Dismember( self, point, "lradiusX", "lhand", "l_hand", "l_hand_cap_l_arm_off", "l_arm_cap_l_hand_off", "*l_hand_cap_l_arm", "*l_arm_cap_l_hand", BOTH_DISMEMBER_RARM, 90, 0, damage, hitLoc ); - } - break; - case HL_HEAD: - if ( g_dismemberment->integer > 2 ) - { - G_Dismember( self, point, "cervical", "cranium", "head", "head_cap_torso_off", "torso_cap_head_off", "*head_cap_torso", "*torso_cap_head", BOTH_DISMEMBER_HEAD1, -1, -1, damage, hitLoc ); - } - break; - case HL_FOOT_RT: - case HL_FOOT_LT: - case HL_CHEST: - case HL_BACK: - default: - break; - } - } - } -} - -static int G_CheckSpecialDeathAnim( gentity_t *self, vec3_t point, int damage, int mod, int hitLoc ) -{ - int deathAnim = -1; - - if ( PM_InRoll( &self->client->ps ) ) - { - deathAnim = BOTH_DEATH_ROLL; //# Death anim from a roll - } - else if ( PM_FlippingAnim( self->client->ps.legsAnim ) ) - { - deathAnim = BOTH_DEATH_FLIP; //# Death anim from a flip - } - else if ( PM_SpinningAnim( self->client->ps.legsAnim ) ) - { - float yawDiff = AngleNormalize180(AngleNormalize180(self->client->renderInfo.torsoAngles[YAW]) - AngleNormalize180(self->client->ps.viewangles[YAW])); - if ( yawDiff > 135 || yawDiff < -135 ) - { - deathAnim = BOTH_DEATH_SPIN_180; //# Death anim when facing backwards - } - else if ( yawDiff < -60 ) - { - deathAnim = BOTH_DEATH_SPIN_90_R; //# Death anim when facing 90 degrees right - } - else if ( yawDiff > 60 ) - { - deathAnim = BOTH_DEATH_SPIN_90_L; //# Death anim when facing 90 degrees left - } - } - else if ( PM_InOnGroundAnim( &self->client->ps ) ) - { - if ( AngleNormalize180(self->client->renderInfo.torsoAngles[PITCH]) < 0 ) - { - deathAnim = BOTH_DEATH_LYING_UP; //# Death anim when lying on back - } - else - { - deathAnim = BOTH_DEATH_LYING_DN; //# Death anim when lying on front - } - } - else if ( PM_InKnockDown( &self->client->ps ) ) - { - if ( AngleNormalize180(self->client->renderInfo.torsoAngles[PITCH]) < 0 ) - { - deathAnim = BOTH_DEATH_FALLING_UP; //# Death anim when falling on back - } - else - { - deathAnim = BOTH_DEATH_FALLING_DN; //# Death anim when falling on face - } - } - else if ( hitLoc == HL_HAND_RT ) - { - if ( g_dismemberment->integer - && self->client->dismemberable - && mod == MOD_SABER )//only lightsaber - {//FIXME: don't do strcmps here - if ( G_StandardHumanoid( self->NPC_type ) ) - { - if ( g_dismemberment->integer >= 4 - || g_realisticSaberDamage->integer - || self->locationDamage[hitLoc] >= self->client->ps.stats[STAT_MAX_HEALTH]*hitLocHealthPercentage[hitLoc] ) - {//this location has taken enough damage to dismember - deathAnim = BOTH_RIGHTHANDCHOPPEDOFF; - } - } - } - } - else if ( PM_CrouchAnim( self->client->ps.legsAnim ) ) - { - deathAnim = BOTH_DEATH_CROUCHED; //# Death anim when crouched - } - - return deathAnim; -} -extern qboolean PM_FinishedCurrentLegsAnim( gentity_t *self ); -static int G_PickDeathAnim( gentity_t *self, vec3_t point, int damage, int mod, int hitLoc ) -{//FIXME: play dead flop anims on body if in an appropriate _DEAD anim when this func is called - int deathAnim = -1; - if ( hitLoc == HL_NONE ) - { - hitLoc = G_GetHitLocation( self, point );//self->hitLoc - } - //dead flops...if you are already playing a death animation, I guess it can just return directly - switch( self->client->ps.legsAnim ) - { - case BOTH_DEATH1: //# First Death anim - case BOTH_DEAD1: - case BOTH_DEATH2: //# Second Death anim - case BOTH_DEAD2: - case BOTH_DEATH8: //# - case BOTH_DEAD8: - case BOTH_DEATH13: //# - case BOTH_DEAD13: - case BOTH_DEATH14: //# - case BOTH_DEAD14: - case BOTH_DEATH16: //# - case BOTH_DEAD16: - case BOTH_DEADBACKWARD1: //# First thrown backward death finished pose - case BOTH_DEADBACKWARD2: //# Second thrown backward death finished pose - return -2; - break; - /* - if ( PM_FinishedCurrentLegsAnim( self ) ) - {//done with the anim - deathAnim = BOTH_DEADFLOP2; - } - else - { - deathAnim = -2; - } - break; - case BOTH_DEADFLOP2: - deathAnim = BOTH_DEADFLOP2; - break; - */ - case BOTH_DEATH10: //# - case BOTH_DEAD10: - case BOTH_DEATH15: //# - case BOTH_DEAD15: - case BOTH_DEADFORWARD1: //# First thrown forward death finished pose - case BOTH_DEADFORWARD2: //# Second thrown forward death finished pose - return -2; - break; - /* - if ( PM_FinishedCurrentLegsAnim( self ) ) - {//done with the anim - deathAnim = BOTH_DEADFLOP1; - } - else - { - deathAnim = -2; - } - break; - */ - case BOTH_DEADFLOP1: - //deathAnim = BOTH_DEADFLOP1; - return -2; - break; - case BOTH_DEAD3: //# Third Death finished pose - case BOTH_DEAD4: //# Fourth Death finished pose - case BOTH_DEAD5: //# Fifth Death finished pose - case BOTH_DEAD6: //# Sixth Death finished pose - case BOTH_DEAD7: //# Seventh Death finished pose - case BOTH_DEAD9: //# - case BOTH_DEAD11: //# - case BOTH_DEAD12: //# - case BOTH_DEAD17: //# - case BOTH_DEAD18: //# - case BOTH_DEAD19: //# - case BOTH_LYINGDEAD1: //# Killed lying down death finished pose - case BOTH_STUMBLEDEAD1: //# Stumble forward death finished pose - case BOTH_FALLDEAD1LAND: //# Fall forward and splat death finished pose - case BOTH_DEATH3: //# Third Death anim - case BOTH_DEATH4: //# Fourth Death anim - case BOTH_DEATH5: //# Fifth Death anim - case BOTH_DEATH6: //# Sixth Death anim - case BOTH_DEATH7: //# Seventh Death anim - case BOTH_DEATH9: //# - case BOTH_DEATH11: //# - case BOTH_DEATH12: //# - case BOTH_DEATH17: //# - case BOTH_DEATH18: //# - case BOTH_DEATH19: //# - case BOTH_DEATHFORWARD1: //# First Death in which they get thrown forward - case BOTH_DEATHFORWARD2: //# Second Death in which they get thrown forward - case BOTH_DEATHBACKWARD1: //# First Death in which they get thrown backward - case BOTH_DEATHBACKWARD2: //# Second Death in which they get thrown backward - case BOTH_DEATH1IDLE: //# Idle while close to death - case BOTH_LYINGDEATH1: //# Death to play when killed lying down - case BOTH_STUMBLEDEATH1: //# Stumble forward and fall face first death - case BOTH_FALLDEATH1: //# Fall forward off a high cliff and splat death - start - case BOTH_FALLDEATH1INAIR: //# Fall forward off a high cliff and splat death - loop - case BOTH_FALLDEATH1LAND: //# Fall forward off a high cliff and splat death - hit bottom - return -2; - break; - case BOTH_DEATH_ROLL: //# Death anim from a roll - case BOTH_DEATH_FLIP: //# Death anim from a flip - case BOTH_DEATH_SPIN_90_R: //# Death anim when facing 90 degrees right - case BOTH_DEATH_SPIN_90_L: //# Death anim when facing 90 degrees left - case BOTH_DEATH_SPIN_180: //# Death anim when facing backwards - case BOTH_DEATH_LYING_UP: //# Death anim when lying on back - case BOTH_DEATH_LYING_DN: //# Death anim when lying on front - case BOTH_DEATH_FALLING_DN: //# Death anim when falling on face - case BOTH_DEATH_FALLING_UP: //# Death anim when falling on back - case BOTH_DEATH_CROUCHED: //# Death anim when crouched - case BOTH_RIGHTHANDCHOPPEDOFF: - return -2; - break; - } - // Not currently playing a death animation, so try and get an appropriate one now. - if ( deathAnim == -1 ) - { - deathAnim = G_CheckSpecialDeathAnim( self, point, damage, mod, hitLoc ); - - if ( deathAnim == -1 ) - {//base on hitLoc - //death anims - switch( hitLoc ) - { - case HL_FOOT_RT: - case HL_FOOT_LT: - if ( !Q_irand( 0, 2 ) ) - { - deathAnim = BOTH_DEATH4;//back: forward - } - else if ( !Q_irand( 0, 1 ) ) - { - deathAnim = BOTH_DEATH5;//same as 4 - } - else - { - deathAnim = BOTH_DEATH15;//back: forward - } - break; - case HL_LEG_RT: - if ( !Q_irand( 0, 2 ) ) - { - deathAnim = BOTH_DEATH4;//back: forward - } - else if ( !Q_irand( 0, 1 ) ) - { - deathAnim = BOTH_DEATH5;//same as 4 - } - else - { - deathAnim = BOTH_DEATH15;//back: forward - } - break; - case HL_LEG_LT: - if ( !Q_irand( 0, 2 ) ) - { - deathAnim = BOTH_DEATH4;//back: forward - } - else if ( !Q_irand( 0, 1 ) ) - { - deathAnim = BOTH_DEATH5;//same as 4 - } - else - { - deathAnim = BOTH_DEATH15;//back: forward - } - break; - case HL_BACK: - if ( VectorLengthSquared( self->client->ps.velocity ) < 256 ) - { - if ( Q_irand( 0, 1 ) ) - { - deathAnim = BOTH_DEATH17;//head/back: croak - } - else - { - deathAnim = BOTH_DEATH10;//back: bend back, fall forward - } - } - else - { - if ( !Q_irand( 0, 2 ) ) - { - deathAnim = BOTH_DEATH4;//back: forward - } - else if ( !Q_irand( 0, 1 ) ) - { - deathAnim = BOTH_DEATH5;//same as 4 - } - else - { - deathAnim = BOTH_DEATH15;//back: forward - } - } - break; - case HL_HAND_RT: - if ( g_dismemberment->integer && self->client->dismemberable && mod == MOD_SABER )//only lightsaber - {//FIXME: don't do strcmps here - if ( G_StandardHumanoid( self->NPC_type ) ) - { - if ( g_dismemberment->integer >= 4 - || g_realisticSaberDamage->integer - || self->locationDamage[hitLoc] >= self->client->ps.stats[STAT_MAX_HEALTH]*hitLocHealthPercentage[hitLoc] ) - {//this location has taken enough damage to dismember - deathAnim = BOTH_RIGHTHANDCHOPPEDOFF; - } - } - } - //NOTE: falls through - case HL_CHEST_RT: - case HL_ARM_RT: - case HL_BACK_RT: - //NOTE: falls through from HL_HAND_RT - if ( deathAnim == -1 ) - { - if ( damage <= self->max_health*0.25 ) - { - deathAnim = BOTH_DEATH9;//chest right: snap, fall forward - } - else if ( damage <= self->max_health*0.5 ) - { - deathAnim = BOTH_DEATH3;//chest right: back - } - else if ( damage <= self->max_health*0.75 ) - { - deathAnim = BOTH_DEATH6;//chest right: spin - } - else - { - //TEMP HACK: play spinny deaths less often - if ( Q_irand( 0, 1 ) ) - { - deathAnim = BOTH_DEATH8;//chest right: spin high - } - else - { - switch ( Q_irand( 0, 2 ) ) - { - default: - case 0: - deathAnim = BOTH_DEATH9;//chest right: snap, fall forward - break; - case 1: - deathAnim = BOTH_DEATH3;//chest right: back - break; - case 2: - deathAnim = BOTH_DEATH6;//chest right: spin - break; - } - } - } - } - break; - case HL_CHEST_LT: - case HL_ARM_LT: - case HL_HAND_LT: - case HL_BACK_LT: - if ( damage <= self->max_health*0.25 ) - { - deathAnim = BOTH_DEATH11;//chest left: snap, fall forward - } - else if ( damage <= self->max_health*0.5 ) - { - deathAnim = BOTH_DEATH7;//chest left: back - } - else if ( damage <= self->max_health*0.75 ) - { - deathAnim = BOTH_DEATH12;//chest left: spin - } - else - { - //TEMP HACK: play spinny deaths less often - if ( Q_irand( 0, 1 ) ) - { - deathAnim = BOTH_DEATH14;//chest left: spin high - } - else - { - switch ( Q_irand( 0, 2 ) ) - { - default: - case 0: - deathAnim = BOTH_DEATH11;//chest left: snap, fall forward - break; - case 1: - deathAnim = BOTH_DEATH7;//chest left: back - break; - case 2: - deathAnim = BOTH_DEATH12;//chest left: spin - break; - } - } - } - break; - case HL_CHEST: - case HL_WAIST: - if ( damage <= self->max_health*0.25 || !VectorLengthSquared( self->client->ps.velocity ) ) - { - if ( !Q_irand( 0, 1 ) ) - { - deathAnim = BOTH_DEATH18;//gut: fall right - } - else - { - deathAnim = BOTH_DEATH19;//gut: fall left - } - } - else if ( damage <= self->max_health*0.5 ) - { - deathAnim = BOTH_DEATH2;//chest: backward short - } - else //if ( damage <= self->max_health*0.75 ) - { - if ( !Q_irand( 0, 1 ) ) - { - deathAnim = BOTH_DEATH1;//chest: backward med - } - else - { - deathAnim = BOTH_DEATH16;//same as 1 - } - } - break; - case HL_HEAD: - if ( damage <= self->max_health*0.5 ) - { - deathAnim = BOTH_DEATH17;//head/back: croak - } - else - { - deathAnim = BOTH_DEATH13;//head: stumble, fall back - } - break; - default: - break; - } - } - } - - // Validate..... - if ( deathAnim == -1 || !PM_HasAnimation( self, deathAnim )) - { - // I guess we'll take what we can get..... - deathAnim = PM_PickAnim( self, BOTH_DEATH1, BOTH_DEATH19 ); - } - - return deathAnim; -} - void G_FriendlyFireReaction( gentity_t *self, gentity_t *other, int dflags ) { if ( (!player->client->ps.viewEntity || other->s.number != player->client->ps.viewEntity)) {//hit by a teammate if ( other != self->enemy && self != other->enemy ) {//we weren't already enemies - if ( self->enemy || other->enemy || (other->s.number&&other->s.number!=player->client->ps.viewEntity) || (!other->s.number&&Q_irand( 0, 3 )) ) + if ( self->enemy || other->enemy || (other->s.number&&other->s.number!=player->client->ps.viewEntity) ) {//if one of us actually has an enemy already, it's okay, just an accident OR wasn't hit by player or someone controlled by player OR player hit ally and didn't get 25% chance of getting mad (FIXME:accumulate anger+base on diff?) return; } @@ -3285,6 +3758,7 @@ void G_FriendlyFireReaction( gentity_t *self, gentity_t *other, int dflags ) { //FIXME: way something? NEED DIALOGUE self->NPC->ffireCount++; + //Com_Printf( "incr: %d < %d\n", self->NPC->ffireCount, 3+((2-g_spskill->integer)*2) ); self->NPC->ffireDebounce = level.time + 500; } } @@ -3396,10 +3870,16 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_ // disruptor does not hurt an atst return; } - if ( mod == MOD_SABER ) + } + if ( mod == MOD_SABER ) + {//sabers do less damage to mark1's and atst's + if ( targ->client && (targ->client->NPC_class == CLASS_ATST || targ->client->NPC_class == CLASS_MARK1) ) { // I guess always do 5 points of damage...feel free to tweak as needed - damage = 5; + if ( damage > 5 ) + { + damage = 5; + } } } @@ -3450,6 +3930,10 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_ { dflags |= DAMAGE_NO_KNOCKBACK; } + if ( !attacker->s.number && targ->client && attacker->client && targ->client->playerTeam == attacker->client->playerTeam ) + {//player doesn't do knockback against allies unless he kills them + dflags |= DAMAGE_DEATH_KNOCKBACK; + } if ( client && client->NPC_class == CLASS_GALAKMECH ) {//hit Galak @@ -3474,20 +3958,27 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_ } } - if ( client && ( mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT )) + if ( mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT ) { - if ( client->NPC_class == CLASS_PROTOCOL || client->NPC_class == CLASS_SEEKER || - client->NPC_class == CLASS_R2D2 || client->NPC_class == CLASS_R5D2 || - client->NPC_class == CLASS_MOUSE || client->NPC_class == CLASS_GONK ) + if ( client ) { - // DEMP2 does more damage to these guys. - damage *= 2; + if ( client->NPC_class == CLASS_PROTOCOL || client->NPC_class == CLASS_SEEKER || + client->NPC_class == CLASS_R2D2 || client->NPC_class == CLASS_R5D2 || + client->NPC_class == CLASS_MOUSE || client->NPC_class == CLASS_GONK ) + { + // DEMP2 does more damage to these guys. + damage *= 2; + } + else if ( client->NPC_class == CLASS_PROBE || client->NPC_class == CLASS_INTERROGATOR || + client->NPC_class == CLASS_MARK1 || client->NPC_class == CLASS_MARK2 || client->NPC_class == CLASS_SENTRY ) + { + // DEMP2 does way more damage to these guys. + damage *= 5; + } } - else if ( client->NPC_class == CLASS_PROBE || client->NPC_class == CLASS_INTERROGATOR || - client->NPC_class == CLASS_MARK1 || client->NPC_class == CLASS_MARK2 || client->NPC_class == CLASS_SENTRY ) + else if ( targ->s.weapon == WP_TURRET ) { - // DEMP2 does way more damage to these guys. - damage *= 5; + damage *= 6;// more damage to turret things } } knockback = damage; @@ -3518,7 +4009,6 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_ { knockback = 0; } - // figure momentum add, even if the damage won't be taken if ( knockback && !(dflags&DAMAGE_DEATH_KNOCKBACK) ) //&& targ->client { @@ -3656,7 +4146,7 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_ } add += asave; //FIXME: need to check the MOD to find out what weapon (if *any*) actually did the killing - attacker->client->sess.missionStats.weaponUsed[attacker->client->ps.weapon] += add; + attacker->client->sess.missionStats.weaponUsed[attacker->client->ps.weapon]++; } } if ( take || (dflags&DAMAGE_NO_DAMAGE) ) @@ -3781,6 +4271,10 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_ else if (targ->health < 0) { targ->health = 0; + if( attacker->s.number == 0 && targ->NPC ) + { + targ->NPC->scriptFlags |= SCF_FFDEATH; + } } } @@ -3825,7 +4319,10 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_ //add up the damage to the location if ( targ->client ) { - targ->locationDamage[hitLoc] += take; + if ( targ->locationDamage[hitLoc] < Q3_INFINITE ) + { + targ->locationDamage[hitLoc] += take; + } } if ( targ->health > 0 && targ->NPC && targ->NPC->surrenderTime > level.time ) {//he was surrendering, goes down with one hit @@ -3870,7 +4367,7 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_ {//just killed or didn't have an enemy before targ->enemy = attacker; } - GEntity_DieFunc (targ, inflictor, attacker, take, mod, hitLoc); + GEntity_DieFunc( targ, inflictor, attacker, take, mod, dflags, hitLoc ); } else { diff --git a/code/game/g_functions.cpp b/code/game/g_functions.cpp index e9f4af4..1069177 100644 --- a/code/game/g_functions.cpp +++ b/code/game/g_functions.cpp @@ -113,6 +113,7 @@ void GEntity_ThinkFunc(gentity_t *self) THINKCASE( welder_think ) THINKCASE( gas_random_jet ) THINKCASE( poll_converter ) // dumb loop sound handling + THINKCASE( spawn_rack_goods ) // delay spawn of goods to help on ents default: Com_Error(ERR_DROP, "GEntity_ThinkFunc: case %d not handled!\n",self->e_ThinkFunc); @@ -134,6 +135,7 @@ void CEntity_ThinkFunc(centity_s *cent) CLTHINKCASE( CG_DLightThink ) CLTHINKCASE( CG_MatrixEffect ) + CLTHINKCASE( CG_Limb ) default: Com_Error(ERR_DROP, "CEntity_ThinkFunc: case %d not handled!\n",cent->gent->e_clThinkFunc); @@ -347,10 +349,10 @@ void GEntity_PainFunc(gentity_t *self, gentity_t *inflictor, gentity_t *attacker } -void GEntity_DieFunc(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc) +void GEntity_DieFunc(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod, int dFlags, int hitLoc) { //#define DIECASE(blah) case dieF_ ## blah: blah(self,inflictor,attacker,damage,mod); OutputDebugString(va("%s\n",#blah));break; -#define DIECASE(blah) case dieF_ ## blah: blah(self,inflictor,attacker,damage,mod,hitLoc); break; +#define DIECASE(blah) case dieF_ ## blah: blah(self,inflictor,attacker,damage,mod,dFlags,hitLoc); break; switch (self->e_DieFunc) { diff --git a/code/game/g_functions.h b/code/game/g_functions.h index ba1994e..f81a753 100644 --- a/code/game/g_functions.h +++ b/code/game/g_functions.h @@ -110,6 +110,7 @@ typedef enum thinkF_welder_think, thinkF_gas_random_jet, thinkF_poll_converter, + thinkF_spawn_rack_goods, } thinkFunc_t; @@ -208,6 +209,7 @@ extern void panel_turret_think ( gentity_t *self ); extern void welder_think ( gentity_t *self ); extern void gas_random_jet ( gentity_t *self ); extern void poll_converter ( gentity_t *self ); +extern void spawn_rack_goods ( gentity_t *self ); // void (*clThink)(centity_s *cent); //Think func for equivalent centity typedef enum @@ -216,6 +218,7 @@ typedef enum // clThinkF_CG_DLightThink, clThinkF_CG_MatrixEffect, + clThinkF_CG_Limb, } clThinkFunc_t; @@ -223,8 +226,7 @@ typedef enum // extern void CG_DLightThink ( centity_t *cent ); extern void CG_MatrixEffect ( centity_t *cent ); - - +extern void CG_Limb ( centity_t *cent ); // void (*reached)(gentity_t *self); // movers call this when hitting endpoint typedef enum @@ -550,27 +552,27 @@ typedef enum // DIE functions... // -extern void funcBBrushDie (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc=HL_NONE); -extern void misc_model_breakable_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc=HL_NONE); -extern void misc_model_cargo_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc=HL_NONE); -extern void func_train_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc=HL_NONE); -extern void player_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc=HL_NONE); -extern void ExplodeDeath_Wait (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc=HL_NONE); -extern void ExplodeDeath (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc=HL_NONE); -extern void func_usable_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc=HL_NONE); -extern void turret_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc=HL_NONE); -extern void funcGlassDie (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc=HL_NONE); -extern void laserTrapDelayedExplode (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc=HL_NONE); -extern void emplaced_gun_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc=HL_NONE); -extern void WP_ExplosiveDie (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc=HL_NONE); -extern void ion_cannon_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc=HL_NONE); -extern void maglock_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc=HL_NONE); -extern void camera_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc=HL_NONE); -extern void Mark1_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc=HL_NONE); -extern void Interrogator_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc=HL_NONE); -extern void misc_atst_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc=HL_NONE); -extern void misc_panel_turret_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc=HL_NONE); -extern void thermal_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc=HL_NONE); +extern void funcBBrushDie (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags=0,int hitLoc=HL_NONE); +extern void misc_model_breakable_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags=0,int hitLoc=HL_NONE); +extern void misc_model_cargo_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags=0,int hitLoc=HL_NONE); +extern void func_train_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags=0,int hitLoc=HL_NONE); +extern void player_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags=0,int hitLoc=HL_NONE); +extern void ExplodeDeath_Wait (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags=0,int hitLoc=HL_NONE); +extern void ExplodeDeath (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags=0,int hitLoc=HL_NONE); +extern void func_usable_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags=0,int hitLoc=HL_NONE); +extern void turret_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags=0,int hitLoc=HL_NONE); +extern void funcGlassDie (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags=0,int hitLoc=HL_NONE); +extern void laserTrapDelayedExplode (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags=0,int hitLoc=HL_NONE); +extern void emplaced_gun_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags=0,int hitLoc=HL_NONE); +extern void WP_ExplosiveDie (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags=0,int hitLoc=HL_NONE); +extern void ion_cannon_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags=0,int hitLoc=HL_NONE); +extern void maglock_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags=0,int hitLoc=HL_NONE); +extern void camera_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags=0,int hitLoc=HL_NONE); +extern void Mark1_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags=0,int hitLoc=HL_NONE); +extern void Interrogator_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags=0,int hitLoc=HL_NONE); +extern void misc_atst_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags=0,int hitLoc=HL_NONE); +extern void misc_panel_turret_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags=0,int hitLoc=HL_NONE); +extern void thermal_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags=0,int hitLoc=HL_NONE); void GEntity_ThinkFunc(gentity_t *self); @@ -580,7 +582,7 @@ void GEntity_BlockedFunc(gentity_t *self, gentity_t *other); void GEntity_TouchFunc(gentity_t *self, gentity_t *other, trace_t *trace); void GEntity_UseFunc(gentity_t *self, gentity_t *other, gentity_t *activator); void GEntity_PainFunc(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, vec3_t point, int damage, int mod,int hitLoc=HL_NONE); -void GEntity_DieFunc(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc=HL_NONE); +void GEntity_DieFunc(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags=0,int hitLoc=HL_NONE); // external functions that I now refer to... diff --git a/code/game/g_fx.cpp b/code/game/g_fx.cpp index b93fa60..b566ad2 100644 --- a/code/game/g_fx.cpp +++ b/code/game/g_fx.cpp @@ -10,36 +10,46 @@ int G_FindConfigstringIndex( const char *name, int start, int max, qboolean crea //---------------------------------------------------------- -/*QUAKED fx_runner (0 0 1) (-8 -8 -8) (8 8 8) STARTOFF ONESHOT +/*QUAKED fx_runner (0 0 1) (-8 -8 -8) (8 8 8) STARTOFF ONESHOT DAMAGE Runs the specified effect, can also be targeted at an info_notnull to orient the effect STARTOFF - effect starts off, toggles on/off when used ONESHOT - effect fires only when used + DAMAGE - does radius damage around effect every "delay" milliseonds "fxFile" - name of the effect file to play "target" - direction to aim the effect in, otherwise defaults to up "target2" - uses its target2 when the fx gets triggered "delay" - how often to call the effect, don't over-do this ( default 200 ) "random" - random amount of time to add to delay, ( default 0, 200 = 0ms to 200ms ) + "splashRadius" - only works when damage is checked ( default 16 ) + "splashDamage" - only works when damage is checked ( default 5 ) + */ #define FX_RUNNER_RESERVED 0x800000 //---------------------------------------------------------- void fx_runner_think( gentity_t *ent ) { - vec3_t temp; - + vec3_t temp; + EvaluateTrajectory( &ent->s.pos, level.time, ent->currentOrigin ); -// EvaluateTrajectory( &ent->s.apos, level.time, ent->currentAngles ); + EvaluateTrajectory( &ent->s.apos, level.time, ent->currentAngles ); // call the effect with the desired position and orientation G_AddEvent( ent, EV_PLAY_EFFECT, ent->fxID ); // Assume angles, we'll do a cross product on the other end to finish up - MakeNormalVectors( ent->s.angles, ent->s.angles2, temp ); // temp value gets chucked + AngleVectors( ent->currentAngles, ent->pos3, NULL, NULL ); + MakeNormalVectors( ent->pos3, ent->pos4, temp ); // there IS a reason this is done...it's so that it doesn't break every effect in the game... ent->nextthink = level.time + ent->delay + random() * ent->random; + if ( ent->spawnflags & 4 ) // damage + { + G_RadiusDamage( ent->currentOrigin, ent, ent->splashDamage, ent->splashRadius, ent, MOD_UNKNOWN ); + } + if ( ent->target2 ) { // let our target know that we have spawned an effect @@ -54,7 +64,7 @@ void fx_runner_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { // call the effect with the desired position and orientation, as a safety thing, // make sure we aren't thinking at all. - G_PlayEffect( self->fxID, self->s.origin, self->s.angles ); + fx_runner_think( self ); self->nextthink = -1; if ( self->target2 ) @@ -88,9 +98,6 @@ void fx_runner_link( gentity_t *ent ) { vec3_t dir; - // Default effect orientation - VectorSet( dir, 0.0f, 0.0f, 1.0f ); - if ( ent->target ) { // try to use the target to override the orientation @@ -109,13 +116,9 @@ void fx_runner_link( gentity_t *ent ) // Our target is valid so let's override the default UP vector VectorSubtract( target->s.origin, ent->s.origin, dir ); VectorNormalize( dir ); + vectoangles( dir, ent->s.angles ); } } - else if ( ent->spawnflags & FX_RUNNER_RESERVED ) // can't try spawning from the spawn strings at this point because it's too late, use this instead - { - // already have angles, so use those - AngleVectors( ent->s.angles, dir, NULL, NULL ); - } // don't really do anything with this right now other than do a check to warn the designers if the target2 is bogus if ( ent->target2 ) @@ -131,8 +134,7 @@ void fx_runner_link( gentity_t *ent ) } } - // NOTE: this really isn't an angle, but rather an orientation vector - G_SetAngles( ent, dir ); + G_SetAngles( ent, ent->s.angles ); if ( ent->spawnflags & 1 || ent->spawnflags & 2 ) // STARTOFF || ONESHOT { @@ -153,9 +155,16 @@ void SP_fx_runner( gentity_t *ent ) // Get our defaults G_SpawnInt( "delay", "200", &ent->delay ); G_SpawnFloat( "random", "0", &ent->random ); - ent->spawnflags |= ( G_SpawnAngleHack( "angle", "0", ent->s.angles ) ? FX_RUNNER_RESERVED : 0 ); + G_SpawnInt( "splashRadius", "16", &ent->splashRadius ); + G_SpawnInt( "splashDamage", "5", &ent->splashDamage ); - // make us useable if we can be targeted``` + if ( !G_SpawnAngleHack( "angle", "0", ent->s.angles )) + { + // didn't have angles, so give us the default of up + VectorSet( ent->s.angles, -90, 0, 0 ); + } + + // make us useable if we can be targeted if ( ent->targetname ) { ent->e_UseFunc = useF_fx_runner_use; diff --git a/code/game/g_itemLoad.cpp b/code/game/g_itemLoad.cpp index e19f5bd..c704a70 100644 --- a/code/game/g_itemLoad.cpp +++ b/code/game/g_itemLoad.cpp @@ -30,7 +30,6 @@ static void IT_Icon (const char **holdBuf); static void IT_Min (const char **holdBuf); static void IT_Max (const char **holdBuf); static void IT_Name (const char **holdBuf); -static void IT_PickupName (const char **holdBuf); static void IT_PickupSound (const char **holdBuf); static void IT_Tag (const char **holdBuf); static void IT_Type (const char **holdBuf); @@ -54,7 +53,6 @@ itemParms_t ItemParms[IT_PARM_MAX] = "icon", IT_Icon, "min", IT_Min, "max", IT_Max, - "pickupname", IT_PickupName, "pickupsound", IT_PickupSound, "tag", IT_Tag, "type", IT_Type, @@ -229,27 +227,6 @@ static void IT_Name(const char **holdBuf) IT_SetDefaults(); } -static void IT_PickupName(const char **holdBuf) -{ - int len; - const char *tokenStr; - - if (COM_ParseString(holdBuf,&tokenStr)) - { - return; - } - - len = strlen(tokenStr); - len++; - if (len > 32) - { - len = 32; - gi.Printf("WARNING: weaponclass too long in external ITEMS.DAT '%s'\n", tokenStr); - } - - bg_itemlist[itemParms.itemNum].pickup_name = G_NewString(tokenStr); -} - static void IT_ClassName(const char **holdBuf) { int len; diff --git a/code/game/g_items.cpp b/code/game/g_items.cpp index b5153c7..e115c7b 100644 --- a/code/game/g_items.cpp +++ b/code/game/g_items.cpp @@ -112,6 +112,20 @@ int Add_Ammo2 (gentity_t *ent, int ammoType, int count) { ent->client->ps.ammo[ammoType] += count; + // since the ammo is the weapon in this case, picking up ammo should actually give you the weapon + switch( ammoType ) + { + case AMMO_THERMAL: + ent->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_THERMAL ); + break; + case AMMO_DETPACK: + ent->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_DET_PACK ); + break; + case AMMO_TRIPMINE: + ent->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_TRIP_MINE ); + break; + } + if ( ent->client->ps.ammo[ammoType] > ammoData[ammoType].max ) { ent->client->ps.ammo[ammoType] = ammoData[ammoType].max; @@ -582,8 +596,12 @@ gentity_t *LaunchItem( gitem_t *item, vec3_t origin, vec3_t velocity, char *targ else { // if not targeting something, auto-remove after 30 seconds - dropped->e_ThinkFunc = thinkF_G_FreeEntity; - dropped->nextthink = level.time + 30000; + // only if it's NOT a security or goodie key + if( (dropped->item->giTag < INV_SECURITY_KEY1 && dropped->item->giTag > INV_SECURITY_KEY5) ) + { + dropped->e_ThinkFunc = thinkF_G_FreeEntity; + dropped->nextthink = level.time + 30000; + } } dropped->e_TouchFunc = touchF_Touch_Item; diff --git a/code/game/g_local.h b/code/game/g_local.h index ec610e2..b68f12e 100644 --- a/code/game/g_local.h +++ b/code/game/g_local.h @@ -43,9 +43,10 @@ #define FL_DROPPED_ITEM 0x00001000 #define FL_DONT_SHOOT 0x00002000 // Can target him, but not shoot him #define FL_UNDYING 0x00004000 // Takes damage down to 1 point, but does not die -#define FL_OVERCHARGED 0x00008000 // weapon shot is an overcharged version....probably a lame place to be putting this flag... +//#define FL_OVERCHARGED 0x00008000 // weapon shot is an overcharged version....probably a lame place to be putting this flag... #define FL_LOCK_PLAYER_WEAPONS 0x00010000 // player can't switch weapons... ask james if there's a better spot for this #define FL_DISINTEGRATED 0x00020000 // marks that the corpse has already been disintegrated +#define FL_FORCE_PULLABLE_ONLY 0x00040000 // cannot be force pushed //Pointer safety utilities #define VALID( a ) ( a != NULL ) @@ -303,7 +304,7 @@ qboolean CanDamage (gentity_t *targ, vec3_t origin); void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_t dir, vec3_t point, int damage, int dflags, int mod, int hitLoc=HL_NONE ); void G_RadiusDamage (vec3_t origin, gentity_t *attacker, float damage, float radius, gentity_t *ignore, int mod); gentity_t *TossClientItems( gentity_t *self ); -void ExplodeDeath_Wait( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath,int hitLoc ); +void ExplodeDeath_Wait( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath,int dFlags,int hitLoc ); void ExplodeDeath( gentity_t *self ); void GoExplodeDeath( gentity_t *self, gentity_t *other, gentity_t *activator); void G_ApplyKnockback( gentity_t *targ, vec3_t newDir, float knockback ); @@ -367,7 +368,7 @@ void respawn (gentity_t *ent); void InitClientPersistant (gclient_t *client); void InitClientResp (gclient_t *client); qboolean ClientSpawn( gentity_t *ent, SavedGameJustLoaded_e eSavedGameJustLoaded ); -void player_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc); +void player_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod, int dFlags, int hitLoc); void AddScore( gentity_t *ent, int score ); qboolean SpotWouldTelefrag( gentity_t *spot, team_t checkteam ); @@ -403,6 +404,13 @@ static void G_SayTo( gentity_t *ent, gentity_t *other, int mode, int color, cons // void G_RunThink (gentity_t *ent); void QDECL G_Error( const char *fmt, ... ); +void ClearAllInUse(void); +void SetInUse(gentity_t *ent); +void ClearInUse(gentity_t *ent); +qboolean PInUse(unsigned int entNum); +qboolean PInUse2(gentity_t *ent); +void WriteInUseBits(void); +void ReadInUseBits(void); // // g_nav.cpp diff --git a/code/game/g_main.cpp b/code/game/g_main.cpp index 0a8c088..8d57098 100644 --- a/code/game/g_main.cpp +++ b/code/game/g_main.cpp @@ -22,8 +22,83 @@ static int navCalcPathTime = 0; level_locals_t level; game_import_t gi; game_export_t globals; - gentity_t g_entities[MAX_GENTITIES]; +unsigned int g_entityInUseBits[MAX_GENTITIES/32]; + +void ClearAllInUse(void) +{ + memset(g_entityInUseBits,0,sizeof(g_entityInUseBits)); +} + +void SetInUse(gentity_t *ent) +{ + assert(((unsigned int)ent)>=(unsigned int)g_entities); + assert(((unsigned int)ent)<=(unsigned int)(g_entities+MAX_GENTITIES-1)); + unsigned int entNum=ent-g_entities; + g_entityInUseBits[entNum/32]|=((unsigned int)1)<<(entNum&0x1f); +} + +void ClearInUse(gentity_t *ent) +{ + assert(((unsigned int)ent)>=(unsigned int)g_entities); + assert(((unsigned int)ent)<=(unsigned int)(g_entities+MAX_GENTITIES-1)); + unsigned int entNum=ent-g_entities; + g_entityInUseBits[entNum/32]&=~(((unsigned int)1)<<(entNum&0x1f)); +} + +qboolean PInUse(unsigned int entNum) +{ + assert(entNum>=0); + assert(entNum=(unsigned int)g_entities); + assert(((unsigned int)ent)<=(unsigned int)(g_entities+MAX_GENTITIES-1)); + unsigned int entNum=ent-g_entities; + return((g_entityInUseBits[entNum/32]&(((unsigned int)1)<<(entNum&0x1f)))!=0); +} + +void WriteInUseBits(void) +{ + gi.AppendToSaveGame('INUS', &g_entityInUseBits, sizeof(g_entityInUseBits) ); +} + +void ReadInUseBits(void) +{ + gi.ReadFromSaveGame('INUS', &g_entityInUseBits, sizeof(g_entityInUseBits)); + // This is only temporary. Once I have converted all the ent->inuse refs, + // it won;t be needed -MW. + for(int i=0;iinuse) +// for ( i=1, e=g_entities,i ; i < globals.num_entities ; i++,e++ ) + for ( i=1 ; i < globals.num_entities ; i++ ) + { +// if (!e->inuse) +// continue; + if(!PInUse(i)) continue; + e=&g_entities[i]; + if (!e->team) continue; if (e->flags & FL_TEAMSLAVE) @@ -336,10 +418,15 @@ void G_FindTeams( void ) { e->teammaster = e; c++; c2++; - for (j=i+1, e2=e+1 ; j < globals.num_entities ; j++,e2++) +// for (j=i+1, e2=e+1 ; j < globals.num_entities ; j++,e2++) + for (j=i+1; j < globals.num_entities ; j++) { - if (!e2->inuse) +// if (!e2->inuse) +// continue; + if(!PInUse(j)) continue; + + e2=&g_entities[j]; if (!e2->team) continue; if (e2->flags & FL_TEAMSLAVE) @@ -384,25 +471,26 @@ void G_InitCvars( void ) { // latched vars // change anytime vars - g_speed = gi.cvar( "g_speed", "250", 0 ); - g_gravity = gi.cvar( "g_gravity", "800", CVAR_USERINFO ); //using userinfo as savegame flag + g_speed = gi.cvar( "g_speed", "250", CVAR_CHEAT ); + g_gravity = gi.cvar( "g_gravity", "800", CVAR_USERINFO|CVAR_CHEAT ); //using userinfo as savegame flag g_sex = gi.cvar ("sex", "male", CVAR_USERINFO | CVAR_ARCHIVE ); g_spskill = gi.cvar ("g_spskill", "0", CVAR_ARCHIVE | CVAR_USERINFO); //using userinfo as savegame flag - g_knockback = gi.cvar( "g_knockback", "1000", 0 ); + g_knockback = gi.cvar( "g_knockback", "1000", CVAR_CHEAT ); g_dismemberment = gi.cvar ( "g_dismemberment", "3", CVAR_ARCHIVE );//0 = none, 1 = arms and hands, 2 = legs, 3 = waist and head, 4 = mega dismemberment + g_dismemberProbabilities = gi.cvar ( "g_dismemberProbabilities", "1", CVAR_ARCHIVE );//0 = ignore probabilities, 1 = use probabilities g_inactivity = gi.cvar ("g_inactivity", "0", 0); - g_debugMove = gi.cvar ("g_debugMove", "0", 0); - g_debugDamage = gi.cvar ("g_debugDamage", "0", 0); - g_ICARUSDebug = gi.cvar( "g_ICARUSDebug", "0", 0 ); + g_debugMove = gi.cvar ("g_debugMove", "0", CVAR_CHEAT ); + g_debugDamage = gi.cvar ("g_debugDamage", "0", CVAR_CHEAT ); + g_ICARUSDebug = gi.cvar( "g_ICARUSDebug", "0", CVAR_CHEAT ); g_timescale = gi.cvar( "timescale", "1", 0 ); - g_subtitles = gi.cvar ("g_subtitles", "2", CVAR_ARCHIVE); + g_subtitles = gi.cvar( "g_subtitles", "2", CVAR_ARCHIVE ); com_buildScript = gi.cvar ("com_buildscript", "0", 0); g_autoBlocking = gi.cvar( "autoBlocking", "1", 0 ); - g_realisticSaberDamage = gi.cvar( "g_realisticSaberDamage", "0", 0 ); - g_AIsurrender = gi.cvar( "g_AIsurrender", "0", 0 ); + g_realisticSaberDamage = gi.cvar( "g_realisticSaberDamage", "0", CVAR_CHEAT ); + g_AIsurrender = gi.cvar( "g_AIsurrender", "0", CVAR_CHEAT ); } /* @@ -459,7 +547,7 @@ void InitGame( const char *mapname, const char *spawntarget, int checkSum, cons // initialize all entities for this game memset( g_entities, 0, MAX_GENTITIES * sizeof(g_entities[0]) ); globals.gentities = g_entities; - + ClearAllInUse(); // initialize all clients for this game level.maxclients = 1; level.clients = (struct gclient_s *) G_Alloc( level.maxclients * sizeof(level.clients[0]) ); @@ -517,7 +605,7 @@ void InitGame( const char *mapname, const char *spawntarget, int checkSum, cons } if ( navCalculatePaths ) - { + {//not loaded - need to calc paths navCalcPathTime = level.time + START_TIME_NAV_CALC;//make sure all ents are in and linked } else @@ -529,6 +617,11 @@ void InitGame( const char *mapname, const char *spawntarget, int checkSum, cons //need to do this, because combatpoint waypoints aren't saved out...? CP_FindCombatPointWaypoints(); navCalcPathTime = 0; + + if ( g_eSavedGameJustLoaded == eNO ) + {//clear all the failed edges unless we just loaded the game (which would include failed edges) + navigator.ClearAllFailedEdges(); + } } player = &g_entities[0]; @@ -839,8 +932,8 @@ void G_Animate ( gentity_t *self ) gi.G2API_GetBoneAnimIndex( &self->ghoul2[self->playerModel], self->rootBone, (cg.time?cg.time:level.time), &frame, &junk, &junk, &junk, &junk2, NULL ); - // this might be a frame off? - if ( frame == self->endFrame ) + // It NEVER seems to get to what you'd think the last frame would be, so I'm doing this to try and catch when the animation has stopped + if ( frame + 1 >= self->endFrame ) { self->svFlags &= ~SVF_ANIMATING; Q3_TaskIDComplete( self, TID_ANIM_BOTH ); @@ -1016,10 +1109,11 @@ void G_CheckEndLevelTimers( gentity_t *ent ) void NAV_CheckCalcPaths( void ) { if ( navCalcPathTime && navCalcPathTime < level.time ) - { - //Calculate all paths + {//first time we've ever loaded this map... + //clear all the failed edges navigator.ClearAllFailedEdges(); + //Calculate all paths NAV_CalculatePaths( level.mapname, giMapChecksum ); navigator.CalculatePaths(); @@ -1079,10 +1173,17 @@ void G_RunFrame( int levelTime ) { navigator.ClearCheckedNodes(); //remember last waypoint, clear current one - for ( i = 0, ent = &g_entities[0]; i < globals.num_entities ; i++, ent++) +// for ( i = 0, ent = &g_entities[0]; i < globals.num_entities ; i++, ent++) + for ( i = 0; i < globals.num_entities ; i++) { - if ( !ent->inuse ) +// if ( !ent->inuse ) +// continue; + + if(!PInUse(i)) continue; + + ent = &g_entities[i]; + if ( ent->waypoint != WAYPOINT_NONE && ent->noWaypointTime < level.time ) { @@ -1103,11 +1204,17 @@ void G_RunFrame( int levelTime ) { } //Run the frame for all entities - for ( i = 0, ent = &g_entities[0]; i < globals.num_entities ; i++, ent++) +// for ( i = 0, ent = &g_entities[0]; i < globals.num_entities ; i++, ent++) + for ( i = 0; i < globals.num_entities ; i++) { - if ( !ent->inuse ) +// if ( !ent->inuse ) +// continue; + + if(!PInUse(i)) continue; + ent = &g_entities[i]; + // clear events that are too old if ( level.time - ent->eventTime > EVENT_VALID_MSEC ) { if ( ent->s.event ) { @@ -1154,7 +1261,10 @@ void G_RunFrame( int levelTime ) { Ghoul2 Insert Start */ - gi.G2API_AnimateG2Models(ent->ghoul2, cg.time); + if (ent->ghoul2.IsValid()) + { + gi.G2API_AnimateG2Models(ent->ghoul2, cg.time); + } /* Ghoul2 Insert End */ @@ -1273,6 +1383,13 @@ Ghoul2 Insert End G_Error( "Game Errors. Scroll up the console to read them.\n" ); } #endif + +#ifdef _DEBUG + if(!(level.framenum&0xff)) + { + ValidateInUseBits(); + } +#endif } diff --git a/code/game/g_misc.cpp b/code/game/g_misc.cpp index 50e3803..82fc644 100644 --- a/code/game/g_misc.cpp +++ b/code/game/g_misc.cpp @@ -501,7 +501,7 @@ void SP_misc_portal_camera(gentity_t *ent) { extern qboolean G_ClearViewEntity( gentity_t *ent ); extern void G_SetViewEntity( gentity_t *self, gentity_t *viewEntity ); extern void SP_fx_runner( gentity_t *ent ); -void camera_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc ) +void camera_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags,int hitLoc ) { if ( player && player->client && player->client->ps.viewEntity == self->s.number ) { @@ -611,8 +611,13 @@ void camera_aim( gentity_t *self ) vectoangles( dir, angles ); //FIXME: if a G2 model, do a bone override..??? VectorCopy( self->currentAngles, self->s.apos.trBase ); - VectorSubtract( angles, self->currentAngles, self->s.apos.trDelta ); - VectorScale( self->s.apos.trDelta, 10, self->s.apos.trDelta ); + for( int i = 0; i < 3; i++ ) + { + angles[i] = AngleNormalize180( angles[i] ); + self->s.apos.trDelta[i] = AngleNormalize180( (angles[i]-self->currentAngles[i])*10 ); + } + //VectorSubtract( angles, self->currentAngles, self->s.apos.trDelta ); + //VectorScale( self->s.apos.trDelta, 10, self->s.apos.trDelta ); self->s.apos.trTime = level.time; self->s.apos.trDuration = FRAMETIME; VectorCopy( angles, self->currentAngles ); @@ -1663,8 +1668,9 @@ Place facing a door (using the angle, not a targetname) and it will lock that do NOTE: place these half-way in the door to make it flush with the door's surface. "target" thing to use when destoryed (not doors - it automatically unlocks the door it was angled at) +"health" default is 10 */ -void maglock_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod, int hitLoc ) +void maglock_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod, int dFlags, int hitLoc ) { //unlock our door if we're the last lock pointed at the door if ( self->activator ) @@ -1686,6 +1692,9 @@ void SP_misc_maglock ( gentity_t *self ) //NOTE: May have to make these only work on doors that are either untargeted // or are targeted by a trigger, not doors fired off by scripts, counters // or other such things? + self->s.modelindex = G_ModelIndex( "models/map_objects/imp_detention/door_lock.md3" ); + self->fxID = G_EffectIndex( "maglock/explosion" ); + G_SetOrigin( self, self->s.origin ); self->e_ThinkFunc = thinkF_maglock_link; @@ -1747,7 +1756,7 @@ void maglock_link( gentity_t *self ) //make it hittable //FIXME: if rotated/inclined this bbox may be off... but okay if we're a ghoul model? - self->s.modelindex = G_ModelIndex( "models/map_objects/imp_detention/door_lock.md3" ); + //self->s.modelindex = G_ModelIndex( "models/map_objects/imp_detention/door_lock.md3" ); VectorSet( self->mins, -8, -8, -8 ); VectorSet( self->maxs, 8, 8, 8 ); self->contents = CONTENTS_CORPSE; @@ -1755,9 +1764,9 @@ void maglock_link( gentity_t *self ) //make it destroyable self->flags |= FL_SHIELDED;//only damagable by lightsabers self->takedamage = qtrue; - self->health = 100; + self->health = 10; self->e_DieFunc = dieF_maglock_die; - self->fxID = G_EffectIndex( "maglock/explosion" ); + //self->fxID = G_EffectIndex( "maglock/explosion" ); gi.linkentity( self ); } @@ -1982,10 +1991,8 @@ ammo_power_converter_use */ void ammo_power_converter_use( gentity_t *self, gentity_t *other, gentity_t *activator) { - int add,highest; - qboolean overcharge; + int add; int difBlaster,difPowerCell,difMetalBolts; - float percent; playerState_t *ps; if ( !activator || activator->s.number != 0 ) @@ -1999,14 +2006,19 @@ void ammo_power_converter_use( gentity_t *self, gentity_t *other, gentity_t *act if ( self->setTime < level.time ) { - overcharge = qfalse; + difBlaster = ammoData[AMMO_BLASTER].max - ps->ammo[AMMO_BLASTER]; + difPowerCell = ammoData[AMMO_POWERCELL].max - ps->ammo[AMMO_POWERCELL]; + difMetalBolts = ammoData[AMMO_METAL_BOLTS].max - ps->ammo[AMMO_METAL_BOLTS]; - if ( self->count ) // Has it got any power left? + // Has it got any power left...and can we even use any of it? + if ( self->count && ( difBlaster > 0 || difPowerCell > 0 || difMetalBolts > 0 )) { + // at least one of the ammo types could stand to take on a bit more ammo self->setTime = level.time + 100; self->s.loopSound = G_SoundIndex( "sound/interface/ammocon_run.wav" ); - if (self->count > MAX_AMMO_GIVE) + // dole out ammo in little packets + if ( self->count > MAX_AMMO_GIVE ) { add = MAX_AMMO_GIVE; } @@ -2019,34 +2031,28 @@ void ammo_power_converter_use( gentity_t *self, gentity_t *other, gentity_t *act add = self->count; } - ps->ammo[AMMO_BLASTER] += add; - ps->ammo[AMMO_POWERCELL] += add; - ps->ammo[AMMO_METAL_BOLTS] += add; + // all weapons fill at same rate... + ps->ammo[AMMO_BLASTER] += add; + ps->ammo[AMMO_POWERCELL] += add; + ps->ammo[AMMO_METAL_BOLTS] += add; + + // ...then get clamped to max + if ( ps->ammo[AMMO_BLASTER] > ammoData[AMMO_BLASTER].max ) + { + ps->ammo[AMMO_BLASTER] = ammoData[AMMO_BLASTER].max; + } + + if ( ps->ammo[AMMO_POWERCELL] > ammoData[AMMO_POWERCELL].max ) + { + ps->ammo[AMMO_POWERCELL] = ammoData[AMMO_POWERCELL].max; + } + + if ( ps->ammo[AMMO_METAL_BOLTS] > ammoData[AMMO_METAL_BOLTS].max ) + { + ps->ammo[AMMO_METAL_BOLTS] = ammoData[AMMO_METAL_BOLTS].max; + } self->count -= add; - - difBlaster = ps->ammo[AMMO_BLASTER] - ammoData[AMMO_BLASTER].max; - difPowerCell = ps->ammo[AMMO_POWERCELL] - ammoData[AMMO_POWERCELL].max; - difMetalBolts = ps->ammo[AMMO_METAL_BOLTS] - ammoData[AMMO_METAL_BOLTS].max; - - // Find the highest one - highest = difBlaster; - if (difPowerCell>difBlaster) - { - highest = difPowerCell; - } - - if (difMetalBolts > highest) - { - highest = difMetalBolts; - } - - // Overcharge - if ( highest > 0 ) - { - percent = (float) highest/100.0f; - ps->powerups[PW_WEAPON_OVERCHARGE] = level.time + ( 10000 * percent ); - } } } @@ -2065,7 +2071,6 @@ void ammo_power_converter_use( gentity_t *self, gentity_t *other, gentity_t *act && ps->ammo[AMMO_POWERCELL] >= ammoData[AMMO_POWERCELL].max && ps->ammo[AMMO_METAL_BOLTS] >= ammoData[AMMO_METAL_BOLTS].max ) { - // FIXME: at this point, overcharging needs to be working properly...this probably isn't the right way to play this sound. G_Sound( self, G_SoundIndex( "sound/interface/ammocon_done.wav" )); self->setTime = level.time + 1000; // extra debounce so that the sounds don't overlap too much self->s.loopSound = 0; @@ -2138,9 +2143,9 @@ void SP_misc_ammo_floor_unit( gentity_t *ent ) EnergyAmmoStationSettings(ent); - G_SoundIndex("sound/interface/shieldcon_run.wav"); - G_SoundIndex("sound/interface/shieldcon_done.mp3"); - G_SoundIndex("sound/interface/shieldcon_empty.mp3"); + G_SoundIndex("sound/interface/ammocon_run.wav"); + G_SoundIndex("sound/interface/ammocon_done.mp3"); + G_SoundIndex("sound/interface/ammocon_empty.mp3"); ent->s.modelindex = G_ModelIndex("models/items/a_pwr_converter.md3"); // Precache model ent->s.eFlags |= EF_SHADER_ANIM; @@ -2223,7 +2228,7 @@ void SP_misc_model_welder( gentity_t *ent ) G_EffectIndex( "blueWeldSparks" ); ent->s.modelindex = G_ModelIndex( "models/map_objects/cairn/welder.glm" ); - ent->s.modelindex2 = G_ModelIndex( "models/map_objects/cairn/welder.md3" ); +// ent->s.modelindex2 = G_ModelIndex( "models/map_objects/cairn/welder.md3" ); ent->playerModel = gi.G2API_InitGhoul2Model( ent->ghoul2, "models/map_objects/cairn/welder.glm", ent->s.modelindex ); ent->s.radius = 400.0f;// the origin of the welder is offset by approximately 352, so we need the big radius @@ -2535,12 +2540,17 @@ void misc_atst_setanim( gentity_t *self, int bone, int anim ) } if ( firstFrame != -1 && lastFrame != -1 && animSpeed != 0 ) { - gi.G2API_SetBoneAnimIndex( &self->ghoul2[self->playerModel], bone, firstFrame, + if (!gi.G2API_SetBoneAnimIndex( &self->ghoul2[self->playerModel], bone, firstFrame, lastFrame, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, animSpeed, + (cg.time?cg.time:level.time), -1, 150 )) + { + gi.G2API_SetBoneAnimIndex( &self->ghoul2[self->playerModel], bone, firstFrame, + lastFrame, BONE_ANIM_OVERRIDE_FREEZE, animSpeed, (cg.time?cg.time:level.time), -1, 150 ); + } } } -void misc_atst_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc ) +void misc_atst_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags,int hitLoc ) {//ATST was destroyed while you weren't in it //can't be used anymore self->e_UseFunc = useF_NULL; @@ -2562,6 +2572,7 @@ void misc_atst_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, extern void G_DriveATST( gentity_t *ent, gentity_t *atst ); extern void SetClientViewAngle( gentity_t *ent, vec3_t angle ); +extern qboolean PM_InSlopeAnim( int anim ); void misc_atst_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { if ( !activator || activator->s.number ) @@ -2609,10 +2620,9 @@ void misc_atst_use( gentity_t *self, gentity_t *other, gentity_t *activator ) else {//get out of ATST int legsAnim = activator->client->ps.legsAnim; - if ( legsAnim != BOTH_STAND1 && - ( legsAnim < LEGS_LEFTUP1 || legsAnim > LEGS_LEFTUP5 )&& - ( legsAnim < LEGS_RIGHTUP1 || legsAnim > LEGS_RIGHTUP5 )&& - legsAnim != BOTH_TURN_RIGHT1 && legsAnim != BOTH_TURN_LEFT1 ) + if ( legsAnim != BOTH_STAND1 + && !PM_InSlopeAnim( legsAnim ) + && legsAnim != BOTH_TURN_RIGHT1 && legsAnim != BOTH_TURN_LEFT1 ) {//can't get out of it while it's still moving return; } @@ -2672,22 +2682,12 @@ void SP_misc_atst_drivable( gentity_t *ent ) VectorSet( ent->s.modelScale, 1.0f, 1.0f, 1.0f ); //register my weapons, sounds and model - gitem_t *item = FindItemForWeapon( WP_ATST_MAIN ); //precache the weapon - CG_RegisterItemSounds( (item-bg_itemlist) ); - CG_RegisterItemVisuals( (item-bg_itemlist) ); - item = FindItemForWeapon( WP_ATST_SIDE ); //precache the weapon - CG_RegisterItemSounds( (item-bg_itemlist) ); - CG_RegisterItemVisuals( (item-bg_itemlist) ); + RegisterItem( FindItemForWeapon( WP_ATST_MAIN )); //precache the weapon + RegisterItem( FindItemForWeapon( WP_ATST_SIDE )); //precache the weapon //HACKHACKHACKTEMP - until ATST gets real weapons of it's own? - item = FindItemForWeapon( WP_EMPLACED_GUN ); - CG_RegisterItemSounds( (item-bg_itemlist) ); - CG_RegisterItemVisuals( (item-bg_itemlist) ); - item = FindItemForWeapon( WP_ROCKET_LAUNCHER ); - CG_RegisterItemSounds( (item-bg_itemlist) ); - CG_RegisterItemVisuals( (item-bg_itemlist) ); - item = FindItemForWeapon( WP_BOWCASTER ); - CG_RegisterItemSounds( (item-bg_itemlist) ); - CG_RegisterItemVisuals( (item-bg_itemlist) ); + RegisterItem( FindItemForWeapon( WP_EMPLACED_GUN )); //precache the weapon + RegisterItem( FindItemForWeapon( WP_ROCKET_LAUNCHER )); //precache the weapon + RegisterItem( FindItemForWeapon( WP_BOWCASTER )); //precache the weapon //HACKHACKHACKTEMP - until ATST gets real weapons of it's own? NPC_ATST_Precache(); diff --git a/code/game/g_misc_model.cpp b/code/game/g_misc_model.cpp index 38063b5..70b2685 100644 --- a/code/game/g_misc_model.cpp +++ b/code/game/g_misc_model.cpp @@ -273,10 +273,24 @@ void GunRackAddItem( gitem_t *gun, vec3_t org, vec3_t angs, float ffwd, float fr it_ent->count = 2; break; } + + // then scale based on skill + switch ( g_spskill->integer ) + { + case 0: + it_ent->count *= 5; + break; + case 1: + it_ent->count *= 3; + break; + case 2: // No soup for you!, or something.. + break; + } } else { rotate = qfalse; + // must deliberately make it small, or else the objects will spawn inside of each other. VectorSet( it_ent->maxs, 6.75f, 6.75f, 6.75f ); VectorScale( it_ent->maxs, -1, it_ent->mins ); @@ -288,6 +302,23 @@ void GunRackAddItem( gitem_t *gun, vec3_t org, vec3_t angs, float ffwd, float fr // FinishSpawningItem handles everything, so clear the thinkFunc that was set in G_SpawnItem FinishSpawningItem( it_ent ); + + if ( gun->giType == IT_AMMO ) + { + // scale ammo based on skill + switch ( g_spskill->integer ) + { + case 0: // do default + break; + case 1: + it_ent->count *= 0.75f; + break; + case 2: + it_ent->count *= 0.5f; + break; + } + } + it_ent->nextthink = 0; VectorCopy( org, it_ent->s.origin ); @@ -298,20 +329,7 @@ void GunRackAddItem( gitem_t *gun, vec3_t org, vec3_t angs, float ffwd, float fr VectorCopy( angs, it_ent->s.angles ); // by doing this, we can force the amount of ammo we desire onto the weapon for when it gets picked-up - it_ent->flags |= FL_DROPPED_ITEM; - - // then scale based on skill - switch ( g_spskill->integer ) - { - case 0: - it_ent->count *= 5; - break; - case 1: - it_ent->count *= 3; - break; - case 2: // No soup for you!, or something.. - break; - } + it_ent->flags |= ( FL_DROPPED_ITEM | FL_FORCE_PULLABLE_ONLY ); for ( int t = 0; t < 3; t++ ) { @@ -352,17 +370,17 @@ void SP_misc_model_gun_rack( gentity_t *ent ) // If BLASTER is checked...or nothing is checked then we'll do blasters if (( ent->spawnflags & RACK_BLASTER ) || !(ent->spawnflags & ( RACK_BLASTER | RACK_REPEATER | RACK_ROCKET ))) { - blaster = FindItem( "E11 Blaster Rifle" ); + blaster = FindItemForWeapon( WP_BLASTER ); } if (( ent->spawnflags & RACK_REPEATER )) { - repeater = FindItem( "Imperial Heavy Repeater" ); + repeater = FindItemForWeapon( WP_REPEATER ); } if (( ent->spawnflags & RACK_ROCKET )) { - rocket = FindItem( "Merr-Sonn Missile System" ); + rocket = FindItemForWeapon( WP_ROCKET_LAUNCHER ); } //---------weapon types @@ -434,8 +452,62 @@ PWR_CELL - Adds one or more power cell packs that are compatible with the Disupt NO_FILL - Only puts selected ammo on the rack, it never fills up all three slots if only one or two items were checked */ +extern gitem_t *FindItemForAmmo( ammo_t ammo ); + //--------------------------------------------- void SP_misc_model_ammo_rack( gentity_t *ent ) +{ +// If BLASTER is checked...or nothing is checked then we'll do blasters + if (( ent->spawnflags & RACK_BLASTER ) || !(ent->spawnflags & ( RACK_BLASTER | RACK_METAL_BOLTS | RACK_ROCKETS | RACK_PWR_CELL ))) + { + if ( ent->spawnflags & RACK_WEAPONS ) + { + RegisterItem( FindItemForWeapon( WP_BLASTER )); + } + RegisterItem( FindItemForAmmo( AMMO_BLASTER )); + } + + if (( ent->spawnflags & RACK_METAL_BOLTS )) + { + if ( ent->spawnflags & RACK_WEAPONS ) + { + RegisterItem( FindItemForWeapon( WP_REPEATER )); + } + RegisterItem( FindItemForAmmo( AMMO_METAL_BOLTS )); + } + + if (( ent->spawnflags & RACK_ROCKETS )) + { + if ( ent->spawnflags & RACK_WEAPONS ) + { + RegisterItem( FindItemForWeapon( WP_ROCKET_LAUNCHER )); + } + RegisterItem( FindItemForAmmo( AMMO_ROCKETS )); + } + + if (( ent->spawnflags & RACK_PWR_CELL )) + { + RegisterItem( FindItemForAmmo( AMMO_POWERCELL )); + } + + if (( ent->spawnflags & RACK_HEALTH )) + { + RegisterItem( FindItem( "item_medpak_instant" )); + } + + ent->e_ThinkFunc = thinkF_spawn_rack_goods; + ent->nextthink = level.time + 100; + + G_SetOrigin( ent, ent->s.origin ); + G_SetAngles( ent, ent->s.angles ); + + ent->contents = CONTENTS_SHOTCLIP|CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP;//CONTENTS_SOLID;//so use traces can go through them + + gi.linkentity( ent ); +} + +// AMMO RACK!! +void spawn_rack_goods( gentity_t *ent ) { float v_off = 0; gitem_t *blaster = NULL, *metal_bolts = NULL, *rockets = NULL, *it = NULL; @@ -444,49 +516,44 @@ void SP_misc_model_ammo_rack( gentity_t *ent ) int pos = 0, ct = 0; gitem_t *itemList[4]; // allocating 4, but we only use 3. done so I don't have to validate that the array isn't full before I add another + gi.unlinkentity( ent ); + // If BLASTER is checked...or nothing is checked then we'll do blasters if (( ent->spawnflags & RACK_BLASTER ) || !(ent->spawnflags & ( RACK_BLASTER | RACK_METAL_BOLTS | RACK_ROCKETS | RACK_PWR_CELL ))) { if ( ent->spawnflags & RACK_WEAPONS ) { - blaster = FindItem( "E11 Blaster Rifle" ); - RegisterItem( blaster ); + blaster = FindItemForWeapon( WP_BLASTER ); } - am_blaster = FindItem( "Blaster Pack" ); - RegisterItem( am_blaster ); + am_blaster = FindItemForAmmo( AMMO_BLASTER ); } if (( ent->spawnflags & RACK_METAL_BOLTS )) { if ( ent->spawnflags & RACK_WEAPONS ) { - metal_bolts = FindItem( "Imperial Heavy Repeater" ); - RegisterItem( metal_bolts ); + metal_bolts = FindItemForWeapon( WP_REPEATER ); } - am_metal_bolts = FindItem( "Metallic Bolts" ); - RegisterItem( am_metal_bolts ); + am_metal_bolts = FindItemForAmmo( AMMO_METAL_BOLTS ); } if (( ent->spawnflags & RACK_ROCKETS )) { if ( ent->spawnflags & RACK_WEAPONS ) { - rockets = FindItem( "Merr-Sonn Missile System" ); - RegisterItem( rockets ); + rockets = FindItemForWeapon( WP_ROCKET_LAUNCHER ); } - am_rockets = FindItem( "Rockets" ); - RegisterItem( am_rockets ); + am_rockets = FindItemForAmmo( AMMO_ROCKETS ); } if (( ent->spawnflags & RACK_PWR_CELL )) { - am_pwr_cell = FindItem( "Power Cell" ); - RegisterItem( am_pwr_cell ); + am_pwr_cell = FindItemForAmmo( AMMO_POWERCELL ); } if (( ent->spawnflags & RACK_HEALTH )) { - health = FindItem( "Medpack" ); + health = FindItem( "item_medpak_instant" ); RegisterItem( health ); } @@ -511,7 +578,7 @@ void SP_misc_model_ammo_rack( gentity_t *ent ) itemList[ct++] = am_rockets; } - if ( !(ent->spawnflags & RACK_NO_FILL) && ct ) //double negative...basically do this if the flag isn't checked..should always have at least one item on their, but just being safe + if ( !(ent->spawnflags & RACK_NO_FILL) && ct ) //double negative..should always have at least one item on there, but just being safe { for ( ; ct < 3 ; ct++ ) { @@ -588,8 +655,6 @@ void SP_misc_model_ammo_rack( gentity_t *ent ) G_SetOrigin( ent, ent->s.origin ); G_SetAngles( ent, ent->s.angles ); - ent->contents = CONTENTS_SHOTCLIP|CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP;//CONTENTS_SOLID;//so use traces can go through them - gi.linkentity( ent ); } @@ -608,14 +673,14 @@ SHIELDS - instant shields BACTA - bacta tanks BATTERIES - -"health" - how much damage to take before blowing up ( default 60 ) +"health" - how much damage to take before blowing up ( default 25 ) "splashRadius" - damage range when it explodes ( default 96 ) -"splashDamage" - damage to do within explode range ( default 24 ) +"splashDamage" - damage to do within explode range ( default 1 ) */ extern gentity_t *LaunchItem( gitem_t *item, vec3_t origin, vec3_t velocity, char *target ); -void misc_model_cargo_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod, int hitLoc ) +void misc_model_cargo_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod, int dFlags, int hitLoc ) { int flags; vec3_t org, temp; @@ -629,7 +694,7 @@ void misc_model_cargo_die( gentity_t *self, gentity_t *inflictor, gentity_t *att self->spawnflags = 8; // NO_DMODEL // pass through to get the effects and such - misc_model_breakable_die( self, inflictor, attacker, damage, mod, hitLoc ); + misc_model_breakable_die( self, inflictor, attacker, damage, mod ); // now that the model is broken, we can safely spawn these in it's place without them being in solid temp[2] = org[2] + 16; @@ -637,7 +702,7 @@ void misc_model_cargo_die( gentity_t *self, gentity_t *inflictor, gentity_t *att // annoying, but spawn each thing in its own little quadrant so that they don't end up on top of each other if (( flags & DROP_MEDPACK )) { - health = FindItem( "Medpack" ); + health = FindItem( "item_medpak_instant" ); if ( health ) { @@ -649,7 +714,7 @@ void misc_model_cargo_die( gentity_t *self, gentity_t *inflictor, gentity_t *att } if (( flags & DROP_SHIELDS )) { - shields = FindItem( "Shield Small" ); + shields = FindItem( "item_shield_sm_instant" ); if ( shields ) { @@ -662,7 +727,7 @@ void misc_model_cargo_die( gentity_t *self, gentity_t *inflictor, gentity_t *att if (( flags & DROP_BACTA )) { - bacta = FindItem( "Medpac" ); // name is dangerously close to the instant medpack + bacta = FindItem( "item_bacta" ); if ( bacta ) { @@ -675,7 +740,7 @@ void misc_model_cargo_die( gentity_t *self, gentity_t *inflictor, gentity_t *att if (( flags & DROP_BATTERIES )) { - batteries = FindItem( "Batteries" ); + batteries = FindItem( "item_battery" ); if ( batteries ) { @@ -691,29 +756,29 @@ void misc_model_cargo_die( gentity_t *self, gentity_t *inflictor, gentity_t *att void SP_misc_model_cargo_small( gentity_t *ent ) { G_SpawnInt( "splashRadius", "96", &ent->splashRadius ); - G_SpawnInt( "splashDamage", "24", &ent->splashDamage ); + G_SpawnInt( "splashDamage", "1", &ent->splashDamage ); if (( ent->spawnflags & DROP_MEDPACK )) { - RegisterItem( FindItem( "Medpack" )); + RegisterItem( FindItem( "item_medpak_instant" )); } if (( ent->spawnflags & DROP_SHIELDS )) { - RegisterItem( FindItem( "Shield Small" )); + RegisterItem( FindItem( "item_shield_sm_instant" )); } if (( ent->spawnflags & DROP_BACTA )) { - RegisterItem( FindItem( "Medpac" )); // name is dangerously close to the instant medpack + RegisterItem( FindItem( "item_bacta" )); } if (( ent->spawnflags & DROP_BATTERIES )) { - RegisterItem( FindItem( "Batteries" )); + RegisterItem( FindItem( "item_battery" )); } - G_SpawnInt( "health", "60", &ent->health ); + G_SpawnInt( "health", "25", &ent->health ); SetMiscModelDefaults( ent, useF_NULL, "11", CONTENTS_SOLID, NULL, qtrue, NULL ); ent->s.modelindex2 = G_ModelIndex("/models/map_objects/kejim/cargo_small.md3"); // Precache model diff --git a/code/game/g_missile.cpp b/code/game/g_missile.cpp index 79a29d6..4756c26 100644 --- a/code/game/g_missile.cpp +++ b/code/game/g_missile.cpp @@ -10,6 +10,12 @@ extern qboolean InFront( vec3_t spot, vec3_t from, vec3_t fromAngles, float threshHold = 0.0f ); qboolean LogAccuracyHit( gentity_t *target, gentity_t *attacker ); extern qboolean G_GetHitLocFromSurfName( gentity_t *ent, const char *surfName, int *hitLoc, vec3_t point, vec3_t dir, vec3_t bladeDir ); +extern qboolean PM_SaberInParry( int move ); +extern qboolean PM_SaberInReflect( int move ); +extern qboolean PM_SaberInIdle( int move ); +extern qboolean PM_SaberInAttack( int move ); +extern qboolean PM_SaberInTransitionAny( int move ); +extern qboolean PM_SaberInSpecialAttack( int anim ); //------------------------------------------------------------------------- void G_MissileBounceEffect( gentity_t *ent, vec3_t org, vec3_t dir ) @@ -17,9 +23,11 @@ void G_MissileBounceEffect( gentity_t *ent, vec3_t org, vec3_t dir ) //FIXME: have an EV_BOUNCE_MISSILE event that checks the s.weapon and does the appropriate effect switch( ent->s.weapon ) { + case WP_BOWCASTER: + G_PlayEffect( "bowcaster/deflect", ent->currentOrigin, dir ); + break; case WP_BLASTER: case WP_BRYAR_PISTOL: - case WP_BOWCASTER: G_PlayEffect( "blaster/deflect", ent->currentOrigin, dir ); break; default: @@ -38,9 +46,19 @@ static void G_MissileStick( gentity_t *missile, gentity_t *other, trace_t *tr ) if ( other->NPC || !Q_stricmp( other->classname, "misc_model_breakable" )) { // we bounce off of NPC's and misc model breakables because sticking to them requires too much effort - VectorScale( tr->plane.normal, -6, missile->s.pos.trDelta ); + vec3_t dir; + G_SetOrigin( missile, tr->endpos ); - missile->s.pos.trTime = level.previousTime + ( level.time - level.previousTime ) * tr->fraction + 30; // move a bit on the first frame + + // get our bounce direction, not accurate, but it simplifies things. + dir[0] = missile->s.pos.trDelta[0] + crandom() * 40.0f; // randomize bounce a bit + dir[1] = missile->s.pos.trDelta[1] + crandom() * 40.0f; + dir[2] = 0;//missile->s.pos.trDelta[2] * 0.1f;// kill upward thrust + + VectorNormalize( dir ); + + VectorScale( dir, -72, missile->s.pos.trDelta ); + missile->s.pos.trTime = level.time - 100; // move a bit on the first frame // check for stop if ( tr->entityNum >= 0 && tr->entityNum < ENTITYNUM_WORLD && @@ -97,7 +115,7 @@ void G_ReflectMissile( gentity_t *ent, gentity_t *missile, vec3_t forward ) if ( ent && owner && owner->client && !owner->client->ps.saberInFlight && (owner->client->ps.forcePowerLevel[FP_SABER_DEFENSE] > FORCE_LEVEL_2 || (owner->client->ps.forcePowerLevel[FP_SABER_DEFENSE]>FORCE_LEVEL_1&&!Q_irand( 0, 3 )) ) ) - {//if high enough defense skill and saber in-hand, reflections are perfectly deflected toward an enemy + {//if high enough defense skill and saber in-hand (100% at level 3, 25% at level 2, 0% at level 1), reflections are perfectly deflected toward an enemy gentity_t *enemy; if ( owner->enemy && Q_irand( 0, 3 ) ) {//toward current enemy 75% of the time @@ -116,6 +134,28 @@ void G_ReflectMissile( gentity_t *ent, gentity_t *missile, vec3_t forward ) bullseye[2] += Q_irand( -16, 4 ); VectorSubtract( bullseye, missile->currentOrigin, bounce_dir ); VectorNormalize( bounce_dir ); + if ( !PM_SaberInParry( owner->client->ps.saberMove ) + && !PM_SaberInReflect( owner->client->ps.saberMove ) + && !PM_SaberInIdle( owner->client->ps.saberMove ) ) + {//a bit more wild + if ( PM_SaberInAttack( owner->client->ps.saberMove ) + || PM_SaberInTransitionAny( owner->client->ps.saberMove ) + || PM_SaberInSpecialAttack( owner->client->ps.torsoAnim ) ) + {//moderately more wild + for ( i = 0; i < 3; i++ ) + { + bounce_dir[i] += Q_flrand( -0.2f, 0.2f ); + } + } + else + {//mildly more wild + for ( i = 0; i < 3; i++ ) + { + bounce_dir[i] += Q_flrand( -0.1f, 0.1f ); + } + } + } + VectorNormalize( bounce_dir ); reflected = qtrue; } } @@ -135,9 +175,57 @@ void G_ReflectMissile( gentity_t *ent, gentity_t *missile, vec3_t forward ) VectorScale( bounce_dir, DotProduct( forward, missile_dir ), bounce_dir ); VectorNormalize( bounce_dir ); } - for ( i = 0; i < 3; i++ ) - { - bounce_dir[i] += Q_flrand( -0.2f, 0.2f ); + if ( owner->s.weapon == WP_SABER && owner->client ) + {//saber + if ( owner->client->ps.saberInFlight ) + {//reflecting off a thrown saber is totally wild + for ( i = 0; i < 3; i++ ) + { + bounce_dir[i] += Q_flrand( -0.8f, 0.8f ); + } + } + else if ( owner->client->ps.forcePowerLevel[FP_SABER_DEFENSE] <= FORCE_LEVEL_1 ) + {// at level 1 + for ( i = 0; i < 3; i++ ) + { + bounce_dir[i] += Q_flrand( -0.4f, 0.4f ); + } + } + else + {// at level 2 + for ( i = 0; i < 3; i++ ) + { + bounce_dir[i] += Q_flrand( -0.2f, 0.2f ); + } + } + if ( !PM_SaberInParry( owner->client->ps.saberMove ) + && !PM_SaberInReflect( owner->client->ps.saberMove ) + && !PM_SaberInIdle( owner->client->ps.saberMove ) ) + {//a bit more wild + if ( PM_SaberInAttack( owner->client->ps.saberMove ) + || PM_SaberInTransitionAny( owner->client->ps.saberMove ) + || PM_SaberInSpecialAttack( owner->client->ps.torsoAnim ) ) + {//really wild + for ( i = 0; i < 3; i++ ) + { + bounce_dir[i] += Q_flrand( -0.3f, 0.3f ); + } + } + else + {//mildly more wild + for ( i = 0; i < 3; i++ ) + { + bounce_dir[i] += Q_flrand( -0.1f, 0.1f ); + } + } + } + } + else + {//some other kind of reflection + for ( i = 0; i < 3; i++ ) + { + bounce_dir[i] += Q_flrand( -0.2f, 0.2f ); + } } } VectorNormalize( bounce_dir ); @@ -281,15 +369,14 @@ void G_BounceMissile( gentity_t *ent, trace_t *trace ) { #else // NEW--It would seem that we want to set our trBase to the trace endpos // and set the trTime to the actual time of impact.... - // FIXME: Should we still consider adding the normal though?? - VectorCopy( trace->endpos, ent->currentOrigin ); + VectorAdd( trace->endpos, trace->plane.normal, ent->currentOrigin ); if ( hitTime >= level.time ) {//trace fraction must have been 1 ent->s.pos.trTime = level.time - 10; } else { - ent->s.pos.trTime = hitTime; + ent->s.pos.trTime = hitTime - 10; // this is kinda dumb hacking, but it pushes the missile away from the impact plane a bit } #endif @@ -558,25 +645,34 @@ void G_MissileImpact( gentity_t *ent, trace_t *trace, int hitLoc=HL_NONE ) } // check for hitting a lightsaber - if ( other->contents & CONTENTS_LIGHTSABER && (!ent->splashDamage || !ent->splashRadius) )//this would be cool, though, to "bat" the thermal det away... + if ( other->contents & CONTENTS_LIGHTSABER + && ( g_spskill->integer <= 0//on easy, it reflects all shots + || (g_spskill->integer == 1 && ent->s.weapon != WP_FLECHETTE && ent->s.weapon != WP_DEMP2 )//on medium it won't reflect flechette or demp shots + || (g_spskill->integer >= 2 && ent->s.weapon != WP_FLECHETTE && ent->s.weapon != WP_DEMP2 && ent->s.weapon != WP_BOWCASTER && ent->s.weapon != WP_REPEATER )//on hard it won't reflect flechette, demp, repeater or bowcaster shots + ) + && (!ent->splashDamage || !ent->splashRadius) )//this would be cool, though, to "bat" the thermal det away... { //FIXME: take other's owner's FP_SABER_DEFENSE into account here somehow? if ( !other->owner || !other->owner->client || other->owner->client->ps.saberInFlight || InFront( ent->currentOrigin, other->owner->currentOrigin, other->owner->client->ps.viewangles, SABER_REFLECT_MISSILE_CONE ) )//other->owner->s.number != 0 || {//Jedi cannot block shots from behind! - VectorSubtract(ent->currentOrigin, other->currentOrigin, diff); - VectorNormalize(diff); - //FIXME: take other's owner's FP_SABER_DEFENSE into account here somehow? - G_ReflectMissile( other, ent, diff); - //WP_SaberBlock( other, ent->currentOrigin, qtrue ); - if ( other->owner && other->owner->client ) - { - other->owner->client->ps.saberEventFlags |= SEF_DEFLECTED; + if ( (other->owner->client->ps.forcePowerLevel[FP_SABER_DEFENSE] > FORCE_LEVEL_1 && Q_irand( 0, 3 )) + ||(other->owner->client->ps.forcePowerLevel[FP_SABER_DEFENSE] > FORCE_LEVEL_0 && Q_irand( 0, 1 ))) + {//level 1 reflects 50% of the time, level 2 reflects 75% of the time + VectorSubtract(ent->currentOrigin, other->currentOrigin, diff); + VectorNormalize(diff); + //FIXME: take other's owner's FP_SABER_DEFENSE into account here somehow? + G_ReflectMissile( other, ent, diff); + //WP_SaberBlock( other, ent->currentOrigin, qtrue ); + if ( other->owner && other->owner->client ) + { + other->owner->client->ps.saberEventFlags |= SEF_DEFLECTED; + } + //do the effect + VectorCopy( ent->s.pos.trDelta, diff ); + VectorNormalize( diff ); + G_MissileBounceEffect( ent, trace->endpos, trace->plane.normal ); + return; } - //do the effect - VectorCopy( ent->s.pos.trDelta, diff ); - VectorNormalize( diff ); - G_MissileBounceEffect( ent, trace->endpos, trace->plane.normal ); - return; } } diff --git a/code/game/g_mover.cpp b/code/game/g_mover.cpp index deb2c8a..44b063b 100644 --- a/code/game/g_mover.cpp +++ b/code/game/g_mover.cpp @@ -385,7 +385,8 @@ qboolean G_MoverPush( gentity_t *pusher, vec3_t move, vec3_t amove, gentity_t ** } } - if ( (pusher->spawnflags&2) && !Q_stricmp( "func_breakable", pusher->classname ) ) + if ( ((pusher->spawnflags&2)&&!Q_stricmp("func_breakable",pusher->classname)) + ||((pusher->spawnflags&16)&&!Q_stricmp("func_static",pusher->classname)) ) {//ugh, avoid stricmp with a unique flag //Damage on impact if ( pusher->damage ) @@ -713,10 +714,11 @@ void Reached_BinaryMover( gentity_t *ent ) // play sound G_PlayDoorSound( ent, BMS_END ); - if(ent->wait < 0) + if ( ent->wait < 0 ) {//Done for good ent->e_ThinkFunc = thinkF_NULL; ent->nextthink = -1; + ent->e_UseFunc = useF_NULL; } else { @@ -826,15 +828,31 @@ void Use_BinaryMover_Go( gentity_t *ent ) // only partway down before reversing if ( ent->moverState == MOVER_2TO1 ) { - total = ent->s.pos.trDuration; - partial = level.time - ent->s.pos.trTime;//ent->s.time; - ent->s.pos.trTime = level.time;//ent->s.time; + total = ent->s.pos.trDuration-50; + if ( ent->s.pos.trType == TR_NONLINEAR_STOP ) + { + vec3_t curDelta; + VectorSubtract( ent->currentOrigin, ent->pos1, curDelta ); + float fPartial = VectorLength( curDelta )/VectorLength( ent->s.pos.trDelta ); + VectorScale( ent->s.pos.trDelta, fPartial, curDelta ); + fPartial /= ent->s.pos.trDuration; + fPartial /= 0.001f; + fPartial = acos( fPartial ); + fPartial = RAD2DEG( fPartial ); + fPartial = (90.0f - fPartial)/90.0f*ent->s.pos.trDuration; + partial = total - floor( fPartial ); + } + else + { + partial = level.time - ent->s.pos.trTime;//ent->s.time; + } if ( partial > total ) { partial = total; } + ent->s.pos.trTime = level.time - ( total - partial );//ent->s.time; - MatchTeam( ent, MOVER_1TO2, level.time - ( total - partial ) ); + MatchTeam( ent, MOVER_1TO2, ent->s.pos.trTime ); G_PlayDoorSound( ent, BMS_START ); @@ -844,15 +862,30 @@ void Use_BinaryMover_Go( gentity_t *ent ) // only partway up before reversing if ( ent->moverState == MOVER_1TO2 ) { - total = ent->s.pos.trDuration; - partial = level.time - ent->s.pos.trTime;//ent->s.time; - ent->s.pos.trTime = level.time;//ent->s.time; - + total = ent->s.pos.trDuration-50; + if ( ent->s.pos.trType == TR_NONLINEAR_STOP ) + { + vec3_t curDelta; + VectorSubtract( ent->currentOrigin, ent->pos2, curDelta ); + float fPartial = VectorLength( curDelta )/VectorLength( ent->s.pos.trDelta ); + VectorScale( ent->s.pos.trDelta, fPartial, curDelta ); + fPartial /= ent->s.pos.trDuration; + fPartial /= 0.001f; + fPartial = acos( fPartial ); + fPartial = RAD2DEG( fPartial ); + fPartial = (90.0f - fPartial)/90.0f*ent->s.pos.trDuration; + partial = total - floor( fPartial ); + } + else + { + partial = level.time - ent->s.pos.trTime;//ent->s.time; + } if ( partial > total ) { partial = total; } - MatchTeam( ent, MOVER_2TO1, level.time - ( total - partial ) ); + ent->s.pos.trTime = level.time - ( total - partial );//ent->s.time; + MatchTeam( ent, MOVER_2TO1, ent->s.pos.trTime ); G_PlayDoorSound( ent, BMS_START ); @@ -895,6 +928,11 @@ void Use_BinaryMover( gentity_t *ent, gentity_t *other, gentity_t *activator ) int key; char *text; + if ( ent->e_UseFunc == useF_NULL ) + {//I cannot be used anymore, must be a door with a wait of -1 that's opened. + return; + } + // only the master should be used if ( ent->flags & FL_TEAMSLAVE ) { @@ -1008,7 +1046,7 @@ void InitMover( gentity_t *ent ) ent->rootBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "model_root", qtrue ); } - ent->s.radius = 90; + ent->s.radius = 120; } else { @@ -1079,8 +1117,18 @@ Blocked_Door ================ */ void Blocked_Door( gentity_t *ent, gentity_t *other ) { - // remove anything other than a client - if ( !other->client ) { + + // remove anything other than a client -- no longer the case + + // don't remove security keys or goodie keys + if ( (other->s.eType == ET_ITEM) && (other->item->giTag >= INV_GOODIE_KEY1 && other->item->giTag <= INV_SECURITY_KEY5) ) + { + // should we be doing anything special if a key blocks it... move it somehow..? + } + // if your not a client, or your a dead client remove yourself... + else if ( other->s.number && (!other->client || (other->client && other->health <= 0 && other->contents == CONTENTS_CORPSE && !other->message)) ) + { + // if an item or weapon can we do a little explosion..? G_FreeEntity( other ); return; } @@ -1790,12 +1838,6 @@ void Reached_Train( gentity_t *ent ) { ent->e_ThinkFunc = thinkF_Think_BeginMoving; ent->s.pos.trType = TR_STATIONARY; } - - if ( ent->playerModel >= 0 && ent->spawnflags & 32 ) // loop...dunno, could support it on other things, but for now I need it for the glider...so...kill the flag - { - // this is a bad place for this - gi.G2API_SetBoneAnim( &ent->ghoul2[ent->playerModel], "model_root", ent->startFrame, ent->endFrame, BONE_ANIM_OVERRIDE_LOOP, 1, 0 ); // looping, always use time zero as a base? yeah..yeah, I know - } } void TrainUse( gentity_t *ent, gentity_t *other, gentity_t *activator ) @@ -1896,7 +1938,7 @@ void SP_path_corner( gentity_t *self ) { } -void func_train_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod, int hitLoc ) +void func_train_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod, int dFlags, int hitLoc ) { if ( self->target3 ) { @@ -1996,6 +2038,15 @@ void SP_func_train (gentity_t *self) { // a chance to spawn self->nextthink = level.time + START_TIME_LINK_ENTS; self->e_ThinkFunc = thinkF_Think_SetupTrainTargets; + + + if ( self->playerModel >= 0 && self->spawnflags & 32 ) // loop...dunno, could support it on other things, but for now I need it for the glider...so...kill the flag + { + self->spawnflags &= ~32; // once only + + gi.G2API_SetBoneAnim( &self->ghoul2[self->playerModel], "model_root", self->startFrame, self->endFrame, BONE_ANIM_OVERRIDE_LOOP, 1.0f + crandom() * 0.1f, 0 ); + self->endFrame = 0; // don't allow it to do anything with the animation function in G_main + } } /* @@ -2006,11 +2057,12 @@ STATIC =============================================================================== */ -/*QUAKED func_static (0 .5 .8) ? F_PUSH F_PULL SWITCH_SHADER CRUSHER x x PLAYER_USE INACTIVE BROADCAST +/*QUAKED func_static (0 .5 .8) ? F_PUSH F_PULL SWITCH_SHADER CRUSHER IMPACT x PLAYER_USE INACTIVE BROADCAST F_PUSH Will be used when you Force-Push it F_PULL Will be used when you Force-Pull it SWITCH_SHADER Toggle the shader anim frame between 1 and 2 when used CRUSHER Make it do damage when it's blocked +IMPACT Make it do damage when it hits any entity PLAYER_USE Player can use it with the use button INACTIVE must be used by a target_activate before it can be used BROADCAST don't ever use this, it's evil @@ -2403,12 +2455,15 @@ void security_panel_use( gentity_t *self, gentity_t *other, gentity_t *activator if ( INV_SecurityKeyCheck( activator, self->message ) ) {//congrats! - gi.SendServerCommand( NULL, "cp \"Your security key unlocked the door\"" ); + gi.SendServerCommand( NULL, "cp @INGAME_SECURITY_KEY_UNLOCKEDDOOR" ); //use targets G_UseTargets( self, activator ); //take key INV_SecurityKeyTake( activator, self->message ); - gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "l_arm_key", 0x00000002 ); + if ( activator->ghoul2.size() ) + { + gi.G2API_SetSurfaceOnOff( &activator->ghoul2[activator->playerModel], "l_arm_key", 0x00000002 ); + } //FIXME: maybe set frame on me to have key sticking out? //self->s.frame = 1; //play sound @@ -2418,7 +2473,14 @@ void security_panel_use( gentity_t *self, gentity_t *other, gentity_t *activator } else {//failure sound/display - gi.SendServerCommand( NULL, "cp \"You need a security key!\"" ); + if ( self->message ) + {//have a key, just the wrong one + gi.SendServerCommand( NULL, "cp @INGAME_INCORRECT_KEY" ); + } + else + {//don't have a key at all + gi.SendServerCommand( NULL, "cp @INGAME_NEED_SECURITY_KEY" ); + } G_UseTargets2( self, activator, self->target2 ); //FIXME: change display? Maybe shader animmap change? //play sound diff --git a/code/game/g_nav.cpp b/code/game/g_nav.cpp index c533397..21f353f 100644 --- a/code/game/g_nav.cpp +++ b/code/game/g_nav.cpp @@ -9,6 +9,8 @@ //Global navigator CNavigator navigator; +extern qboolean G_EntIsUnlockedDoor( int entityNum ); +extern qboolean G_EntIsDoor( int entityNum ); extern qboolean G_FindClosestPointOnLineSegment( const vec3_t start, const vec3_t end, const vec3_t from, vec3_t result ); extern void G_AddVoiceEvent( gentity_t *self, int event, int speakDebounceTime ); //For debug graphics @@ -77,7 +79,14 @@ void NPC_SetMoveGoal( gentity_t *ent, vec3_t point, int radius, qboolean isNavGo { //Must be an NPC if ( ent->NPC == NULL ) + { return; + } + + if ( ent->NPC->tempGoal == NULL ) + {//must still have a goal + return; + } //Copy the origin //VectorCopy( point, ent->NPC->goalPoint ); //FIXME: Make it use this, and this alone! @@ -94,7 +103,10 @@ void NPC_SetMoveGoal( gentity_t *ent, vec3_t point, int radius, qboolean isNavGo ent->NPC->tempGoal->noWaypointTime = 0; if ( isNavGoal ) + { + assert(ent->NPC->tempGoal->owner); ent->NPC->tempGoal->svFlags |= SVF_NAVGOAL; + } ent->NPC->tempGoal->combatPoint = combatPoint; ent->NPC->tempGoal->enemy = targetEnt; @@ -171,7 +183,7 @@ qboolean NAV_ClearPathToPoint( gentity_t *self, vec3_t pmins, vec3_t pmaxs, vec3 if ( !self->owner ) { //SHOULD NEVER HAPPEN!!! - assert(0); + assert(self->owner); return qfalse; } VectorCopy( self->owner->mins, mins ); @@ -462,7 +474,8 @@ qboolean NAV_CheckAhead( gentity_t *self, vec3_t end, trace_t &trace, int clipma if VALIDSTRING( blocker->classname ) { - if ( Q_stricmp( blocker->classname, "func_door" ) == 0 ) + if ( G_EntIsUnlockedDoor( blocker->s.number ) ) + //if ( Q_stricmp( blocker->classname, "func_door" ) == 0 ) { //We're too close, try and avoid the door (most likely stuck on a lip) if ( DistanceSquared( self->currentOrigin, trace.endpos ) < MIN_DOOR_BLOCK_DIST_SQR ) @@ -756,7 +769,8 @@ qboolean NAV_ResolveEntityCollision( gentity_t *self, gentity_t *blocker, vec3_t vec3_t blocked_dir; //Doors are ignored - if ( Q_stricmp( blocker->classname, "func_door" ) == 0 ) + if ( G_EntIsUnlockedDoor( blocker->s.number ) ) + //if ( Q_stricmp( blocker->classname, "func_door" ) == 0 ) { if ( DistanceSquared( self->currentOrigin, blocker->currentOrigin ) > MIN_DOOR_BLOCK_DIST_SQR ) return qtrue; @@ -892,36 +906,79 @@ NAV_TestBestNode ------------------------- */ -int NAV_TestBestNode( vec3_t position, int startID, int endID ) -{ - int bestNode = startID; - int testNode = navigator.GetBestNode( bestNode, endID ); +int NAV_TestBestNode( gentity_t *self, int startID, int endID, qboolean failEdge ) +{//check only against architectrure + vec3_t end; + trace_t trace; + vec3_t mins; + int clipmask = (NPC->clipmask&~CONTENTS_BODY)|CONTENTS_BOTCLIP; - vec3_t p1, p2, dir1, dir2; + //get the position for the test choice + navigator.GetNodePosition( endID, end ); - navigator.GetNodePosition( bestNode, p1 ); - navigator.GetNodePosition( testNode, p2 ); - - /* - if ( DistanceSquared( p1, p2 ) < DistanceSquared( position, p1 ) ) - return testNode; - */ + //Offset the step height + VectorSet( mins, self->mins[0], self->mins[1], self->mins[2] + STEPSIZE ); - VectorSubtract( p2, p1, dir1 ); - //VectorNormalize( dir1 ); + gi.trace( &trace, self->currentOrigin, mins, self->maxs, end, self->s.number, clipmask ); - VectorSubtract( position, p1, dir2 ); - //VectorNormalize( dir2 ); - - if ( DotProduct( dir1, dir2 ) > 0 ) - { - trace_t trace; - - if ( NAV_CheckAhead( NPC, p2, trace, (NPC->clipmask&~CONTENTS_BODY)|CONTENTS_BOTCLIP ) ) - bestNode = testNode; + if ( trace.startsolid&&(trace.contents&CONTENTS_BOTCLIP) ) + {//started inside do not enter, so ignore them + clipmask &= ~CONTENTS_BOTCLIP; + gi.trace( &trace, self->currentOrigin, mins, self->maxs, end, self->s.number, clipmask ); } - - return bestNode; + //Do a simple check + if ( ( trace.allsolid == qfalse ) && ( trace.startsolid == qfalse ) && ( trace.fraction == 1.0f ) ) + {//it's clear + return endID; + } + + //See if we're too far above + if ( fabs( self->currentOrigin[2] - end[2] ) > 48 ) + { + return startID; + } + + //This is a work around + float radius = ( self->maxs[0] > self->maxs[1] ) ? self->maxs[0] : self->maxs[1]; + float dist = Distance( self->currentOrigin, end ); + float tFrac = 1.0f - ( radius / dist ); + + if ( trace.fraction >= tFrac ) + {//it's clear + return endID; + } + + //Do a special check for doors + if ( trace.entityNum < ENTITYNUM_WORLD ) + { + gentity_t *blocker = &g_entities[trace.entityNum]; + + if VALIDSTRING( blocker->classname ) + {//special case: doors are architecture, but are dynamic, like entitites + if ( G_EntIsUnlockedDoor( blocker->s.number ) ) + //if ( Q_stricmp( blocker->classname, "func_door" ) == 0 ) + {//it's unlocked, go for it + //We're too close, try and avoid the door (most likely stuck on a lip) + if ( DistanceSquared( self->currentOrigin, trace.endpos ) < MIN_DOOR_BLOCK_DIST_SQR ) + { + return startID; + } + //we can keep heading to the door, it should open + return endID; + } + else if ( G_EntIsDoor( blocker->s.number ) ) + {//a locked door! + //path is blocked by a locked door, mark it as such if instructed to do so + if ( failEdge ) + { + // navigator.AddFailedEdge( self->s.number, startID, endID ); + } + } + } + } + //path is blocked + //use the fallback choice + return startID; } /* @@ -1006,7 +1063,7 @@ int NAV_MoveToGoal( gentity_t *self, navInfo_t &info ) } //Check this node - bestNode = NAV_TestBestNode( self->currentOrigin, bestNode, self->NPC->goalEntity->waypoint ); + bestNode = NAV_TestBestNode( self, bestNode, self->NPC->goalEntity->waypoint, qfalse ); vec3_t origin, end; //trace_t trace; @@ -1727,7 +1784,7 @@ void NAV_ShowDebugInfo( void ) int nearestNode = navigator.GetNearestNode( &g_entities[0], g_entities[0].waypoint, NF_ANY, WAYPOINT_NONE ); int testNode = navigator.GetBestNode( nearestNode, NAVDEBUG_curGoal ); - nearestNode = NAV_TestBestNode( g_entities[0].currentOrigin, nearestNode, testNode ); + nearestNode = NAV_TestBestNode( &g_entities[0], nearestNode, testNode, qfalse ); //Show the connection vec3_t dest, start; diff --git a/code/game/g_nav.h b/code/game/g_nav.h index a7b7a42..7642820 100644 --- a/code/game/g_nav.h +++ b/code/game/g_nav.h @@ -37,7 +37,7 @@ void NAV_CalculateSquadPaths( const char *filename, int checksum ); void NAV_ShowDebugInfo( void ); int NAV_GetNearestNode( gentity_t *self, int lastNode ); -int NAV_TestBestNode( vec3_t position, int startID, int endID ); +extern int NAV_TestBestNode( gentity_t *self, int startID, int endID, qboolean failEdge ); qboolean NPC_GetMoveDirection( vec3_t out, float *distance ); void NPC_MoveToGoalExt( vec3_t point ); diff --git a/code/game/g_navigator.cpp b/code/game/g_navigator.cpp index 61d715a..a6e9cb1 100644 --- a/code/game/g_navigator.cpp +++ b/code/game/g_navigator.cpp @@ -10,6 +10,7 @@ extern int NAVNEW_ClearPathBetweenPoints(vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, int ignore, int clipmask); +extern qboolean NAV_CheckNodeFailedForEnt( gentity_t *ent, int nodeNum ); extern qboolean G_EntIsUnlockedDoor( int entityNum ); extern qboolean G_EntIsDoor( int entityNum ); extern qboolean G_EntIsBreakable( int entityNum ); @@ -25,6 +26,23 @@ static vec3_t wpMins = { -16, -16, -24+STEPSIZE };//WTF: was 16??!!! static byte CHECKED_NO = 0; static byte CHECKED_FAILED = 1; static byte CHECKED_PASSED = 2; + + +int GetTime ( int lastTime ) +{ + int curtime; + static int timeBase = 0; + static qboolean initialized = qfalse; + + if (!initialized) { + timeBase = timeGetTime(); + initialized = qtrue; + } + curtime = timeGetTime() - timeBase - lastTime; + + return curtime; +} + /* ------------------------- CEdge @@ -106,6 +124,21 @@ AddEdge void CNode::AddEdge( int ID, int cost, int flags ) { + if ( m_numEdges ) + {//already have at least 1 + //see if it exists already + edge_v::iterator ei; + STL_ITERATE( ei, m_edges ) + { + if ( (*ei).ID == ID ) + {//found it + (*ei).cost = cost; + (*ei).flags = flags; + return; + } + } + } + edge_t edge; edge.ID = ID; @@ -115,6 +148,8 @@ void CNode::AddEdge( int ID, int cost, int flags ) STL_INSERT( m_edges, edge ); m_numEdges++; + + assert( m_numEdges < 9 );//8 is the max } /* @@ -569,6 +604,11 @@ bool CNavigator::Load( const char *filename, int checksum ) //read in the failed edges gi.FS_Read( &failedEdges, sizeof( failedEdges ), file ); + for ( int j = 0; j < MAX_FAILED_EDGES; j++ ) + { + m_edgeLookupMap.insert(pair(failedEdges[j].startID, j)); + } + gi.FS_FCloseFile( file ); @@ -673,47 +713,29 @@ int CNavigator::GetEdgeCost( CNode *first, CNode *second ) return Distance( start, end ); } -/* -------------------------- -ConnectNodes -------------------------- -*/ - -void CNavigator::ConnectNodes( void ) +void CNavigator::SetEdgeCost( int ID1, int ID2, int cost ) { - node_v::iterator ni, ni2; - int id = 0; - int cost; - - //Attempt to connect all nodes - STL_ITERATE( ni, m_nodes ) - { - //Attempt connection against all nodes in the system - //TODO: Culling routines could speed this up - STL_ITERATE( ni2, m_nodes ) - { - if ( (*ni) == (*ni2 ) ) - continue; - - if ( EdgeFailed((*ni)->GetID(), (*ni2)->GetID()) ) - { - continue; - } - cost = GetEdgeCost( (*ni), (*ni2) ); - - //No connection was made - if( cost >= Q3_INFINITE ) // GetEdgeCost used to return -1 as a fail, now it returns Q3_INFINITE - continue; - // if ( cost < 0 ) - // continue; - - //Connect the edges - (*ni)->AddEdge( (*ni2)->GetID(), cost ); - (*ni2)->AddEdge( (*ni)->GetID(), cost ); - } - - id++; + if( (ID1 == -1) || (ID2 == -1) ) + {//not valid nodes, must have come from the ClearAllFailedEdges initization-type calls + return; } + + CNode *node1 = m_nodes[ID1]; + CNode *node2 = m_nodes[ID2]; + + if ( cost == -1 ) + {//they want us to calc it + //FIXME: can we just remember this instead of recalcing every time? + vec3_t pos1, pos2; + + node1->GetPosition( pos1 ); + node2->GetPosition( pos2 ); + cost = Distance( pos1, pos2 ); + } + + //set it + node1->AddEdge( ID2, cost ); + node2->AddEdge( ID1, cost ); } /* @@ -817,21 +839,6 @@ void CNavigator::CalculatePath( CNode *node ) CalculatePaths ------------------------- */ -int GetTime ( int lastTime ) -{ - int curtime; - static int timeBase = 0; - static qboolean initialized = qfalse; - - if (!initialized) { - timeBase = timeGetTime(); - initialized = qtrue; - } - curtime = timeGetTime() - timeBase - lastTime; - - return curtime; -} - extern void CP_FindCombatPointWaypoints( void ); void CNavigator::CalculatePaths( bool recalc ) { @@ -839,17 +846,9 @@ void CNavigator::CalculatePaths( bool recalc ) int startTime = GetTime(0); #endif #if _HARD_CONNECT - #else - - ConnectNodes(); - #endif - if (recalc) - { - ConnectNodes(); - } for ( int i = 0; i < m_nodes.size(); i++ ) { //Allocate the needed memory @@ -876,28 +875,6 @@ void CNavigator::CalculatePaths( bool recalc ) pathsCalculated = qtrue; } -/* -------------------------- -RecalculatePaths -------------------------- -*/ -/* -void CNavigator::RecalculatePaths( void ) -{//Basically just verify the edges, ranks and paths? - ConnectNodes(); - - for ( int i = 0; i < m_nodes.size(); i++ ) - { - //Allocate the needed memory - m_nodes[i]->InitRanks( m_nodes.size() ); - } - - for ( i = 0; i < m_nodes.size(); i++ ) - { - CalculatePath( m_nodes[i] ); - } -} -*/ /* ------------------------- ShowNodes @@ -917,21 +894,27 @@ void CNavigator::ShowNodes( void ) { (*ni)->GetPosition( position ); - if ( gi.inPVS( g_entities[0].currentOrigin, position ) ) - { - showRadius = qfalse; - if( NAVDEBUG_showRadius ) - { - - dist = DistanceSquared( g_entities[0].currentOrigin, position ); - radius = (*ni)->GetRadius(); - // if player within node radius or 256, draw radius (sometimes the radius is really small, so we check for 256 to catch everything) - if( (dist <= radius*radius) || dist <= 65536 ) - { - showRadius = qtrue; - } + showRadius = qfalse; + if( NAVDEBUG_showRadius ) + { + dist = DistanceSquared( g_entities[0].currentOrigin, position ); + radius = (*ni)->GetRadius(); + // if player within node radius or 256, draw radius (sometimes the radius is really small, so we check for 256 to catch everything) + if( (dist <= radius*radius) || dist <= 65536 ) + { + showRadius = qtrue; + } + } + else + { + dist = DistanceSquared( g_entities[0].currentOrigin, position ); + } + if ( dist < 1048576 ) + { + if ( gi.inPVS( g_entities[0].currentOrigin, position ) ) + { + (*ni)->Draw(showRadius); } - (*ni)->Draw(showRadius); } } } @@ -955,6 +938,17 @@ void CNavigator::ShowEdges( void ) STL_ITERATE( ni, m_nodes ) { + (*ni)->GetPosition( start ); + if ( DistanceSquared( g_entities[0].currentOrigin, start ) >= 1048576 ) + { + continue; + } + + if ( !gi.inPVSIgnorePortals( g_entities[0].currentOrigin, start ) ) + { + continue; + } + //Attempt to draw each connection for ( int i = 0; i < (*ni)->GetNumEdges(); i++ ) { @@ -972,12 +966,16 @@ void CNavigator::ShowEdges( void ) CNode *node = m_nodes[id]; node->GetPosition( end ); - (*ni)->GetPosition( start ); //Set this as drawn drawMap[id][(*ni)->GetID()] = true; - if ( ( gi.inPVS( g_entities[0].currentOrigin, start ) == false ) && ( gi.inPVS( g_entities[0].currentOrigin, end ) == false ) ) + if ( DistanceSquared( g_entities[0].currentOrigin, end ) >= 1048576 ) + { + continue; + } + + if ( !gi.inPVSIgnorePortals( g_entities[0].currentOrigin, end ) ) continue; if ( EdgeFailed( id, (*ni)->GetID() ) != -1 )//flags & EFLAG_FAILED ) @@ -1287,7 +1285,7 @@ int CNavigator::GetBestPathBetweenEnts( gentity_t *ent, gentity_t *goal, int fla { //Must have nodes if ( m_nodes.size() == 0 ) - return qfalse; + return NODE_NONE; #define MAX_Z_DELTA 18 @@ -1318,6 +1316,54 @@ int CNavigator::GetBestPathBetweenEnts( gentity_t *ent, gentity_t *goal, int fla { node = m_nodes[(*nci).nodeID]; nodeNum = (*nci).nodeID; + + node->GetPosition( position ); + + if ( CheckedNode(nodeNum,ent->s.number) == CHECKED_FAILED ) + {//already checked this node against ent and it failed + continue; + } + if ( CheckedNode(nodeNum,ent->s.number) == CHECKED_PASSED ) + {//already checked this node against ent and it passed + } + else + {//haven't checked this node against ent yet + if ( NodeFailed( ent, nodeNum ) ) + { + SetCheckedNode( nodeNum, ent->s.number, CHECKED_FAILED ); + continue; + } + //okay, since we only have to do this once, let's check to see if this node is even usable (could help us short-circuit a whole loop of the dest nodes) + radius = node->GetRadius(); + + //If we're not within the known clear radius of this node OR out of Z height range... + if ( (*nci).distance >= (radius*radius) || ( fabs( position[2] - ent->currentOrigin[2] ) >= MAX_Z_DELTA ) ) + { + //We're not *within* this node, so check clear path, etc. + + //FIXME: any way to call G_FindClosestPointOnLineSegment and see if I can at least get to the waypoint's path + if ( flags & NF_CLEAR_PATH )//|| flags & NF_CLEAR_LOS ) + {//need a clear path or LOS + if ( !gi.inPVS( ent->currentOrigin, position ) ) + {//not even potentially clear + SetCheckedNode( nodeNum, ent->s.number, CHECKED_FAILED ); + continue; + } + } + + //Do we need a clear path? + if ( flags & NF_CLEAR_PATH ) + { + if ( TestNodePath( ent, goal->s.number, position, qtrue ) == false ) + { + SetCheckedNode( nodeNum, ent->s.number, CHECKED_FAILED ); + continue; + } + } + }//otherwise, inside the node so it must be clear (?) + SetCheckedNode( nodeNum, ent->s.number, CHECKED_PASSED ); + } + if ( d_altRoutes->integer ) { //calc the paths for this node if they're out of date @@ -1344,7 +1390,6 @@ int CNavigator::GetBestPathBetweenEnts( gentity_t *ent, gentity_t *goal, int fla } } - node->GetPosition( position ); node2->GetPosition( position2 ); //Okay, first get the entire path cost, including distance to first node from ents' positions cost = floor(Distance( ent->currentOrigin, position ) + Distance( goal->currentOrigin, position2 )); @@ -1363,70 +1408,22 @@ int CNavigator::GetBestPathBetweenEnts( gentity_t *ent, gentity_t *goal, int fla { continue; } + //okay, this is the shortest path we've found yet, check clear path, etc. - - if ( CheckedNode(nodeNum,ent->s.number) == CHECKED_FAILED ) + if ( CheckedNode( nodeNum2, goal->s.number ) == CHECKED_FAILED ) {//already checked this node against goal and it failed -// recalc = true; continue; } - if ( CheckedNode(nodeNum,ent->s.number) == CHECKED_PASSED ) + if ( CheckedNode( nodeNum2, goal->s.number ) == CHECKED_PASSED ) {//already checked this node against goal and it passed } else {//haven't checked this node against goal yet - radius = node->GetRadius(); - - //If we're not within the known clear radius of this node OR out of Z height range... - if ( (*nci).distance >= (radius*radius) || ( fabs( position[2] - ent->currentOrigin[2] ) >= MAX_Z_DELTA ) ) + if ( NodeFailed( goal, nodeNum2 ) ) { - //We're not *within* this node, so check clear path, etc. - - //FIXME: any way to call G_FindClosestPointOnLineSegment and see if I can at least get to the waypoint's path - if ( flags & NF_CLEAR_PATH )//|| flags & NF_CLEAR_LOS ) - {//need a clear path or LOS - if ( !gi.inPVS( ent->currentOrigin, position ) ) - {//not even potentially clear - SetCheckedNode(nodeNum,ent->s.number,CHECKED_FAILED); - continue; - } - } - - //Do we need a clear path? - if ( flags & NF_CLEAR_PATH ) - { - if ( TestNodePath( ent, goal->s.number, position, qtrue ) == false ) - { - SetCheckedNode(nodeNum,ent->s.number,CHECKED_FAILED); - continue; - } - } - - //Do we need a clear line of sight? - /* - if ( flags & NF_CLEAR_LOS ) - { - if ( TestNodeLOS( ent, position ) == false ) - { - nodeChecked[nodeNum][ent->s.number] = CHECKED_FAILED; - continue; - } - } - */ - }//otherwise, inside the node so it must be clear (?) - SetCheckedNode(nodeNum,ent->s.number,CHECKED_PASSED); - } - - if ( CheckedNode(nodeNum2,goal->s.number) == CHECKED_FAILED ) - {//already checked this node against goal and it failed -// recalc = true; - continue; - } - if ( CheckedNode(nodeNum2,goal->s.number) == CHECKED_PASSED ) - {//already checked this node against goal and it passed - } - else - {//haven't checked this node against goal yet + SetCheckedNode( nodeNum2, goal->s.number, CHECKED_FAILED ); + continue; + } radius = node2->GetRadius(); //If we're not within the known clear radius of this node OR out of Z height range... @@ -1438,7 +1435,7 @@ int CNavigator::GetBestPathBetweenEnts( gentity_t *ent, gentity_t *goal, int fla {//need a clear path or LOS if ( !gi.inPVS( goal->currentOrigin, position2 ) ) {//not even potentially clear - SetCheckedNode(nodeNum2,goal->s.number,CHECKED_FAILED); + SetCheckedNode( nodeNum2, goal->s.number, CHECKED_FAILED ); continue; } } @@ -1447,24 +1444,12 @@ int CNavigator::GetBestPathBetweenEnts( gentity_t *ent, gentity_t *goal, int fla { if ( TestNodePath( goal, ent->s.number, position2, qfalse ) == false )//qtrue? { - SetCheckedNode(nodeNum2,goal->s.number,CHECKED_FAILED); + SetCheckedNode( nodeNum2, goal->s.number, CHECKED_FAILED ); continue; } } - - //Do we need a clear line of sight? - /* - if ( flags & NF_CLEAR_LOS ) - { - if ( TestNodeLOS( goal, position2 ) == false ) - { - nodeChecked[nodeNum2][goal->s.number] = CHECKED_FAILED; - continue; - } - } - */ }//otherwise, inside the node so it must be clear (?) - SetCheckedNode(nodeNum2,goal->s.number,CHECKED_PASSED); + SetCheckedNode( nodeNum2, goal->s.number, CHECKED_PASSED ); } bestCost = cost; @@ -1482,24 +1467,6 @@ int CNavigator::GetBestPathBetweenEnts( gentity_t *ent, gentity_t *goal, int fla } } return bestNode; - -/* - if(recalc) - { - - int startTime = GetTime(0); - if ( m_nodes[ent->waypoint] && (ent->waypoint!=-1) ) - { - CalculatePath(m_nodes[ent->waypoint]); - } - if ( m_nodes[goal->waypoint] && (goal->waypoint!=-1) ) - { - CalculatePath(m_nodes[goal->waypoint]); - } - gi.Printf( S_COLOR_CYAN"%s recalced two paths in %d ms\n", (NPC!=NULL?NPC->targetname:"NULL"), GetTime(startTime) ); - } - return (ent->waypoint != NODE_NONE && goal->waypoint != NODE_NONE); -*/ } /* @@ -1824,12 +1791,15 @@ void CNavigator::ClearFailedEdge( failedEdge_t *failedEdge ) } */ //clear failedEdge info + SetEdgeCost( failedEdge->startID, failedEdge->endID, -1 ); failedEdge->startID = failedEdge->endID = WAYPOINT_NONE; + failedEdge->entID = ENTITYNUM_NONE; failedEdge->checkTime = 0; } void CNavigator::ClearAllFailedEdges( void ) { + memset( &failedEdges, WAYPOINT_NONE, sizeof( failedEdges ) ); for ( int j = 0; j < MAX_FAILED_EDGES; j++ ) { ClearFailedEdge( &failedEdges[j] ); @@ -1837,7 +1807,33 @@ void CNavigator::ClearAllFailedEdges( void ) } int CNavigator::EdgeFailed( int startID, int endID ) -{ +{ + //OPTIMIZED WAY (bjg 01/02) + //find in lookup map + pair findValue; + findValue = m_edgeLookupMap.equal_range(startID); + while ( findValue.first != findValue.second ) + { + if( failedEdges[findValue.first->second].endID == endID) + { + return findValue.first->second; + } + ++findValue.first; + } + findValue = m_edgeLookupMap.equal_range(endID); + while ( findValue.first != findValue.second ) + { + if( failedEdges[findValue.first->second].endID == startID) + { + return findValue.first->second; + } + ++findValue.first; + } + + return -1; + + //Old way (linear search) + /* for ( int j = 0; j < MAX_FAILED_EDGES; j++ ) { if ( failedEdges[j].startID == startID ) @@ -1855,8 +1851,8 @@ int CNavigator::EdgeFailed( int startID, int endID ) } } } - return -1; + */ } void CNavigator::AddFailedEdge( int entID, int startID, int endID ) @@ -1915,6 +1911,8 @@ void CNavigator::AddFailedEdge( int entID, int startID, int endID ) //Check one second from now to see if it's clear failedEdges[j].checkTime = level.time + CHECK_FAILED_EDGE_INTERVAL + Q_irand( 0, 1000 ); + m_edgeLookupMap.insert(pair(startID, j)); + /* //DISABLED this for now, makes people stand around too long when // collision avoidance just wasn't at it's best but path is clear @@ -1962,14 +1960,15 @@ void CNavigator::AddFailedEdge( int entID, int startID, int endID ) } */ + //stuff the index to this one in our lookup map + //now recalc all the paths! if ( pathsCalculated ) { //reconnect the nodes and mark every node's flag NF_RECALC //gi.Printf( S_COLOR_CYAN"%d marking all nodes for recalc\n", level.time ); - ConnectNodes(); + SetEdgeCost( startID, endID, Q3_INFINITE ); FlagAllNodes( NF_RECALC ); - //CalculatePaths(true); } return; } @@ -2046,6 +2045,10 @@ qboolean CNavigator::CheckFailedEdge( failedEdge_t *failedEdge ) { hitEntNum = ENTITYNUM_NONE; } + else if ( hitEntNum == failedEdge->entID ) + {//don't hit the person who initially marked the edge failed + hitEntNum = ENTITYNUM_NONE; + } if ( hitEntNum == ENTITYNUM_NONE ) { //If so, clear it @@ -2083,9 +2086,7 @@ void CNavigator::CheckAllFailedEdges( void ) { //reconnect the nodes and mark every node's flag NF_RECALC //gi.Printf( S_COLOR_CYAN"%d marking all nodes for recalc\n", level.time ); - ConnectNodes(); FlagAllNodes( NF_RECALC ); - //CalculatePaths(true); } } } @@ -2099,6 +2100,7 @@ qboolean CNavigator::RouteBlocked( int startID, int testEdgeID, int endID, int r CNode *end; CNode *next; + if ( EdgeFailed( startID, testEdgeID ) != -1 ) { return qtrue; @@ -2214,7 +2216,7 @@ int CNavigator::GetBestNodeAltRoute( int startID, int endID, int *pathCost, int int testRank, rejectRank = Q3_INFINITE; int bestCost = Q3_INFINITE; - *pathCost = Q3_INFINITE; + *pathCost = 0; //Find the minimum rank of the edge(s) we want to reject as paths if ( rejectID != WAYPOINT_NONE ) @@ -2504,7 +2506,9 @@ unsigned int CNavigator::GetPathCost( int startID, int endID ) //No possible connection if ( testRank == NODE_NONE ) + { return Q3_INFINITE; // return 0; + } //Found a better one if ( testRank < bestRank ) diff --git a/code/game/g_navigator.h b/code/game/g_navigator.h index 9d0bffa..51595ab 100644 --- a/code/game/g_navigator.h +++ b/code/game/g_navigator.h @@ -21,6 +21,10 @@ #define NAV_HEADER_ID 'JNV5' #define NODE_HEADER_ID 'NODE' +typedef multimap EdgeMultimap; +typedef multimap::iterator EdgeMultimapIt; + + /* ------------------------- CEdge @@ -200,7 +204,8 @@ public: void FlagAllNodes( int newFlag ); qboolean pathsCalculated; - failedEdge_t failedEdges[MAX_FAILED_EDGES]; + failedEdge_t failedEdges[MAX_FAILED_EDGES]; + //MCG Added END protected: @@ -220,13 +225,15 @@ protected: float GetFloat( fileHandle_t file ); long GetLong( fileHandle_t file ); - void ConnectNodes( void ); + //void ConnectNodes( void ); + void SetEdgeCost( int ID1, int ID2, int cost ); int GetEdgeCost( CNode *first, CNode *second ); void AddNodeEdges( CNode *node, int addDist, edge_l &edgeList, bool *checkedNodes ); void CalculatePath( CNode *node ); node_v m_nodes; + EdgeMultimap m_edgeLookupMap; }; ////////////////////////////////////////////////////////////////////// diff --git a/code/game/g_navnew.cpp b/code/game/g_navnew.cpp index 5e63128..cf7d2fb 100644 --- a/code/game/g_navnew.cpp +++ b/code/game/g_navnew.cpp @@ -13,6 +13,18 @@ extern cvar_t *d_altRoutes; qboolean NAV_CheckAhead( gentity_t *self, vec3_t end, trace_t &trace, int clipmask ); qboolean NAV_TestForBlocked( gentity_t *self, gentity_t *goal, gentity_t *blocker, float distance, int &flags ); +qboolean NAV_CheckNodeFailedForEnt( gentity_t *ent, int nodeNum ) +{ + //FIXME: must be a better way to do this + for ( int j = 0; j < MAX_FAILED_NODES; j++ ) + { + if ( ent->failedWaypoints[j] == nodeNum+1 )//+1 because 0 is a valid nodeNum, but also the default + {//we failed against this node + return qtrue; + } + } + return qfalse; +} /* ------------------------- NPC_UnBlocked @@ -479,7 +491,7 @@ qboolean NAVNEW_AvoidCollision( gentity_t *self, gentity_t *goal, navInfo_t &inf //Our path is clear, just move there if ( NAVDEBUG_showCollision ) { - CG_DrawEdge( self->currentOrigin, movepos, EDGE_PATH ); + CG_DrawEdge( self->currentOrigin, movepos, EDGE_MOVEDIR ); } return qtrue; @@ -497,7 +509,7 @@ int NAVNEW_MoveToGoal( gentity_t *self, navInfo_t &info ) vec3_t origin; navInfo_t tempInfo; qboolean setBlockedInfo = qtrue; - qboolean inBestWP; + qboolean inBestWP, inGoalWP, goalWPFailed = qfalse; int numTries = 0; memcpy( &tempInfo, &info, sizeof( tempInfo ) ); @@ -542,7 +554,7 @@ int NAVNEW_MoveToGoal( gentity_t *self, navInfo_t &info ) while( !foundClearPath ) { - inBestWP = qfalse; + inBestWP = inGoalWP = qfalse; /* bestNode = navigator.GetBestNodeAltRoute( self->waypoint, self->NPC->goalEntity->waypoint, bestNode ); */ @@ -554,12 +566,9 @@ int NAVNEW_MoveToGoal( gentity_t *self, navInfo_t &info ) //see if we can get directly to the next node off bestNode en route to goal's node... //NOTE: shouldn't be necc. now - int oldBestNode = bestNode; /* - navigator.GetNodePosition( bestNode, origin ); - Com_Printf( "%d bestNode = %d, at %s\n", level.time, bestNode, vtos( origin ) ); - */ - bestNode = NAV_TestBestNode( self->currentOrigin, self->waypoint, bestNode );//, self->NPC->goalEntity->waypoint );// + int oldBestNode = bestNode; + bestNode = NAV_TestBestNode( self, self->waypoint, bestNode, qtrue );//, self->NPC->goalEntity->waypoint );// //NOTE: Guaranteed to return something if ( bestNode != oldBestNode ) {//we were blocked somehow @@ -569,15 +578,49 @@ int NAVNEW_MoveToGoal( gentity_t *self, navInfo_t &info ) navigator.GetNodePosition( oldBestNode, NPCInfo->blockedDest ); } } - + */ navigator.GetNodePosition( bestNode, origin ); - - if ( bestNode == self->NPC->goalEntity->waypoint && bestNode == self->waypoint && NAV_HitNavGoal( self->currentOrigin, self->mins, self->maxs, origin, navigator.GetNodeRadius( bestNode ) ) ) - {//we're in the wp we're heading for already - inBestWP = qtrue; - //we're in the goalEntity's waypoint already, so head for the goalEntity - //VectorCopy( self->NPC->goalEntity->currentOrigin, origin ); + if ( !goalWPFailed ) + {//we haven't already tried to go straight to goal or goal's wp + if ( bestNode == self->NPC->goalEntity->waypoint ) + {//our bestNode is the goal's wp + if ( NAV_HitNavGoal( self->currentOrigin, self->mins, self->maxs, origin, navigator.GetNodeRadius( bestNode ) ) ) + {//we're in the goal's wp + inGoalWP = qtrue; + //we're in the goalEntity's waypoint already + //so head for the goalEntity since we know it's clear of architecture + //FIXME: this is pretty stupid because the NPCs try to go straight + // towards their goal before then even try macro_nav... + VectorCopy( self->NPC->goalEntity->currentOrigin, origin ); + } + } } + if ( !inGoalWP ) + {//not heading straight for goal + if ( bestNode == self->waypoint ) + {//we know it's clear or architecture + //navigator.GetNodePosition( self->waypoint, origin ); + /* + if ( NAV_HitNavGoal( self->currentOrigin, self->mins, self->maxs, origin, navigator.GetNodeRadius( bestNode ) ) ) + {//we're in the wp we're heading for already + inBestWP = qtrue; + } + */ + } + else + {//heading to an edge off our confirmed clear waypoint... make sure it's clear + //it it's not, bestNode will fall back to our waypoint + int oldBestNode = bestNode; + bestNode = NAV_TestBestNode( self, self->waypoint, bestNode, qtrue ); + if ( bestNode == self->waypoint ) + {//we fell back to our waypoint, reset the origin + self->NPC->aiFlags |= NPCAI_BLOCKED; + navigator.GetNodePosition( oldBestNode, NPCInfo->blockedDest ); + navigator.GetNodePosition( bestNode, origin ); + } + } + } + //Com_Printf( "goalwp = %d, mywp = %d, node = %d, origin = %s\n", self->NPC->goalEntity->waypoint, self->waypoint, bestNode, vtos(origin) ); memcpy( &tempInfo, &info, sizeof( tempInfo ) ); VectorSubtract( origin, self->currentOrigin, tempInfo.direction ); @@ -586,19 +629,26 @@ int NAVNEW_MoveToGoal( gentity_t *self, navInfo_t &info ) //NOTE: One very important thing NAVNEW_AvoidCollision does is // it actually CHANGES the value of "direction" - it changes it to // whatever dir you need to go in to avoid the obstacle... - //Com_Printf( "%d bestNode (clear) = %d, at %s\n", level.time, bestNode, vtos( origin ) ); foundClearPath = NAVNEW_AvoidCollision( self, self->NPC->goalEntity, tempInfo, setBlockedInfo, 5 ); - //foundClearPath = NAV_CheckAvoidCollision( self, origin, tempDir, 0, setBlockedInfo, self->NPC->goalEntity->s.number ); + + if ( !foundClearPath ) + {//blocked by an ent + if ( inGoalWP ) + {//we were heading straight for the goal, head for the goal's wp instead + navigator.GetNodePosition( bestNode, origin ); + foundClearPath = NAVNEW_AvoidCollision( self, self->NPC->goalEntity, tempInfo, setBlockedInfo, 5 ); + } + } if ( foundClearPath ) - { + {//clear! //If we got set to blocked, clear it - NPC_ClearBlocked( self );//NAV_ClearBlockedInfo( self ); + NPC_ClearBlocked( self ); //Take the dir memcpy( &info, &tempInfo, sizeof( info ) ); } else - { + {//blocked by ent! if ( setBlockedInfo ) { self->NPC->aiFlags |= NPCAI_BLOCKED; @@ -607,34 +657,49 @@ int NAVNEW_MoveToGoal( gentity_t *self, navInfo_t &info ) //Only set blocked info first time setBlockedInfo = qfalse; - if ( inBestWP ) - {//we're in our bestWP, so we headed toward our goal and still failed - //we should stop + if ( inGoalWP ) + {//we headed for our goal and failed and our goal's WP and failed + if ( self->waypoint == self->NPC->goalEntity->waypoint ) + {//our waypoint is our goal's waypoint, nothing we can do + //remember that this node is blocked + navigator.AddFailedNode( self, self->waypoint ); + goto failed; + } + else + {//try going for our waypoint this time + goalWPFailed = qtrue; + inGoalWP = qfalse; + } + } + else if ( bestNode != self->waypoint ) + {//we headed toward our next waypoint (instead of our waypoint) and failed + if ( d_altRoutes->integer ) + {//mark this edge failed and try our waypoint + navigator.AddFailedEdge( self->s.number, self->waypoint, bestNode ); + bestNode = self->waypoint; + } + else + { + //we should stop + goto failed; + } } else - { + {//we headed for *our* waypoint and couldn't get to it if ( d_altRoutes->integer ) { - if ( self->waypoint == bestNode ) - {//couldn't get to our waypoint - //remember that this node is blocked - navigator.AddFailedNode( self, self->waypoint ); - } - else - { - //Remember that this node is blocked - navigator.AddFailedEdge( self->s.number, self->waypoint, bestNode ); - } - + //remember that this node is blocked + navigator.AddFailedNode( self, self->waypoint ); //Now we should get our waypoints again //FIXME: cache the trace-data for subsequent calls as only the route info would have changed - if ( (bestNode = navigator.GetBestPathBetweenEnts( self, self->NPC->goalEntity, NF_CLEAR_PATH )) == NODE_NONE )//!NAVNEW_GetWaypoints( self, qfalse ) ) + //if ( (bestNode = navigator.GetBestPathBetweenEnts( self, self->NPC->goalEntity, NF_CLEAR_PATH )) == NODE_NONE )//!NAVNEW_GetWaypoints( self, qfalse ) ) {//one of our waypoints is WAYPOINT_NONE now goto failed; } } else { + //we should stop goto failed; } } @@ -644,7 +709,6 @@ int NAVNEW_MoveToGoal( gentity_t *self, navInfo_t &info ) goto failed; } } - //MCG - End } //finish: @@ -659,22 +723,18 @@ int NAVNEW_MoveToGoal( gentity_t *self, navInfo_t &info ) //Draw the route CG_DrawNode( start, NODE_START ); + if ( bestNode != self->waypoint ) + { + vec3_t wpPos; + navigator.GetNodePosition( self->waypoint, wpPos ); + CG_DrawNode( wpPos, NODE_NAVGOAL ); + } CG_DrawNode( dest, NODE_GOAL ); - navigator.ShowPath( self->waypoint, self->NPC->goalEntity->waypoint ); + CG_DrawEdge( dest, self->NPC->goalEntity->currentOrigin, EDGE_PATH ); + CG_DrawNode( self->NPC->goalEntity->currentOrigin, NODE_GOAL ); + navigator.ShowPath( bestNode, self->NPC->goalEntity->waypoint ); } - //FIXME: Is this really necessary? - - //Calculate this for other functions - //MCG - Done above - /* - vec3_t origin; - - navigator.GetNodePosition( bestNode, origin ); - VectorSubtract( origin, self->currentOrigin, dir ); - VectorNormalize( dir ); - */ - self->NPC->shoveCount = 0; //let me keep this waypoint for a while diff --git a/code/game/g_public.h b/code/game/g_public.h index b3ca839..3bcfd68 100644 --- a/code/game/g_public.h +++ b/code/game/g_public.h @@ -24,7 +24,7 @@ #define SVF_KILLED_SELF 0x00001000 // ent killed itself in a script, so don't do ICARUS_FreeEnt on it... or else! #define SVF_NAVGOAL 0x00002000 // Navgoal #define SVF_NOPUSH 0x00004000 // Can't be pushed around -#define SVF_MUST_HEAL 0x00008000 // Medic has to heal this guy +#define SVF_ICARUS_FREEZE 0x00008000 // NPCs are frozen, ents don't execute ICARUS commands #define SVF_PLAT 0x00010000 // A func_plat or door acting like one #define SVF_BBRUSH 0x00020000 // breakable brush #define SVF_LOCKEDENEMY 0x00040000 // keep current enemy until dead @@ -232,7 +232,6 @@ qboolean (*G2API_SetSkin)(CGhoul2Info *ghlInfo, qhandle_t customSkin); qboolean (*G2API_SetShader)(CGhoul2Info *ghlInfo, qhandle_t customShader); qboolean (*G2API_RemoveGhoul2Model)(CGhoul2Info_v &ghlInfo, const int modelIndex); qboolean (*G2API_SetSurfaceOnOff)(CGhoul2Info *ghlInfo, const char *surfaceName, const int flags); -int (*G2API_GetSurfaceOnOff)(CGhoul2Info *ghlInfo, const char *surfaceName); qboolean (*G2API_SetRootSurface)(CGhoul2Info_v &ghlInfo, const int modelIndex, const char *surfaceName); qboolean (*G2API_RemoveSurface)(CGhoul2Info *ghlInfo, const int index); int (*G2API_AddSurface)(CGhoul2Info *ghlInfo, int surfaceNumber, int polyNumber, float BarycentricI, float BarycentricJ, int lod ); @@ -303,6 +302,7 @@ char *(*G2API_GetAnimFileNameIndex)(qhandle_t modelIndex); int (*G2API_GetSurfaceRenderStatus)(CGhoul2Info *ghlInfo, const char *surfaceName); int (*RE_RegisterSkin)(const char *name); +int (*RE_GetAnimationCFG)(const char *psCFGFilename, char *psDest, int iDestSize); /* Ghoul2 Insert End diff --git a/code/game/g_ref.cpp b/code/game/g_ref.cpp index 4c1c2a7..a1f4d46 100644 --- a/code/game/g_ref.cpp +++ b/code/game/g_ref.cpp @@ -236,6 +236,12 @@ int TAG_GetOrigin( const char *owner, const char *name, vec3_t origin ) { reference_tag_t *tag = TAG_Find( owner, name ); + if (!tag) + { + VectorClear(origin); + return false; + } + VALIDATEB( tag ); VectorCopy( tag->origin, origin ); diff --git a/code/game/g_savegame.cpp b/code/game/g_savegame.cpp index ca63ddc..78c2d89 100644 --- a/code/game/g_savegame.cpp +++ b/code/game/g_savegame.cpp @@ -901,7 +901,7 @@ void WriteLevel(qboolean qbAutosave) extern void CG_WriteTheEvilCGHackStuff(void); CG_WriteTheEvilCGHackStuff(); - + WriteInUseBits(); // (Do NOT put any write-code below this line) // @@ -957,6 +957,7 @@ void ReadLevel(qboolean qbAutosave, qboolean qbLoadTransition) extern void CG_ReadTheEvilCGHackStuff(void); CG_ReadTheEvilCGHackStuff(); + ReadInUseBits(); // (Do NOT put any read-code below this line) // diff --git a/code/game/g_session.cpp b/code/game/g_session.cpp index c4fe0d4..1febebc 100644 --- a/code/game/g_session.cpp +++ b/code/game/g_session.cpp @@ -62,19 +62,19 @@ void G_WriteClientSessionData( gclient_t *client ) { gi.cvar_set( var, s2 ); + s2 = ""; for (i=0;i< NUM_FORCE_POWERS; i++) { - s2 = va("%s %i ", - s2, client->sess.missionStats.forceUsed[i]); + s2 = va("%s %i",s2, client->sess.missionStats.forceUsed[i]); } var = va( "sessionpowers%i", client - level.clients ); gi.cvar_set( var, s2 ); + s2 = ""; for (i=0;i< WP_NUM_WEAPONS; i++) { - s2 = va("%s %i ", - s2, client->sess.missionStats.weaponUsed[i]); + s2 = va("%s %i",s2, client->sess.missionStats.weaponUsed[i]); } var = va( "sessionweapons%i", client - level.clients ); gi.cvar_set( var, s2 ); @@ -135,8 +135,7 @@ void G_ReadSessionData( gclient_t *client ) { var++; for (i=0;i< NUM_FORCE_POWERS; i++) { - sscanf( var, "%i", - &client->sess.missionStats.forceUsed[i]); + sscanf( var, "%i", &client->sess.missionStats.forceUsed[i]); var+=2; } @@ -148,8 +147,7 @@ void G_ReadSessionData( gclient_t *client ) { var++; for (i=0;i< WP_NUM_WEAPONS; i++) { - sscanf( var, "%i", - &client->sess.missionStats.weaponUsed[i]); + sscanf( var, "%i", &client->sess.missionStats.weaponUsed[i]); var+=2; } diff --git a/code/game/g_shared.h b/code/game/g_shared.h index c028e9b..5a09ad1 100644 --- a/code/game/g_shared.h +++ b/code/game/g_shared.h @@ -59,6 +59,7 @@ typedef enum //# taskID_e typedef enum //# material_e { MAT_METAL = 0, // scorched blue-grey metal + MAT_GLASS, // not a real chunk type, just plays an effect with glass sprites MAT_ELECTRICAL, // sparks only MAT_ELEC_METAL, // sparks/electrical type metal MAT_DRK_STONE, // brown @@ -69,12 +70,10 @@ typedef enum //# material_e MAT_GREY_STONE, // grey MAT_METAL3, // METAL and METAL2 chunks MAT_CRATE1, // yellow multi-colored crate chunks - MAT_CRATE2, // read multi-colored crate chunks MAT_GRATE1, // grate chunks MAT_ROPE, // for yavin trial...no chunks, just wispy bits + MAT_CRATE2, // read multi-colored crate chunks -//NOTE: Anything after MAT_GLASS will take extra impact damage! - MAT_GLASS, // not a real chunk type, just plays an effect with glass sprites NUM_MATERIALS } material_t; @@ -446,6 +445,7 @@ struct gclient_s { int respawnTime; // can respawn when time > this, force after g_forcerespwan int inactivityTime; // kick players when time > this qboolean inactivityWarning; // qtrue if the five seoond warning has been given + int idleTime; // for playing idleAnims int airOutTime; @@ -486,10 +486,11 @@ struct gclient_s { saberTrail_t saberTrail; //dismember tracker - qboolean dismemberable; + qboolean dismembered; char dismemberProbLegs; // probability of the legs being dismembered (located in NPC.cfg, 0 = never, 100 = always) char dismemberProbHead; // probability of the head being dismembered (located in NPC.cfg, 0 = never, 100 = always) char dismemberProbArms; // probability of the arms being dismembered (located in NPC.cfg, 0 = never, 100 = always) + char dismemberProbHands; // probability of the hands being dismembered (located in NPC.cfg, 0 = never, 100 = always) char dismemberProbWaist; // probability of the waist being dismembered (located in NPC.cfg, 0 = never, 100 = always) int standheight; @@ -734,6 +735,7 @@ Ghoul2 Insert End int thoracicBone; int upperLumbarBone; int lowerLumbarBone; + int hipsBone; int motionBone; int rootBone; int footLBone; @@ -880,6 +882,7 @@ enum EDGE_PATH, EDGE_BLOCKED, EDGE_FAILED, + EDGE_MOVEDIR }; enum diff --git a/code/game/g_svcmds.cpp b/code/game/g_svcmds.cpp index cc8a017..8d33a5a 100644 --- a/code/game/g_svcmds.cpp +++ b/code/game/g_svcmds.cpp @@ -183,6 +183,11 @@ void Svcmd_ForceJump_f( void ) { return; } + if ( !g_cheats->integer ) + { + gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); + return; + } char *newVal = gi.argv(1); if ( !VALIDSTRING( newVal ) ) { @@ -216,6 +221,11 @@ void Svcmd_SaberThrow_f( void ) { return; } + if ( !g_cheats->integer ) + { + gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); + return; + } char *newVal = gi.argv(1); if ( !VALIDSTRING( newVal ) ) { @@ -249,6 +259,11 @@ void Svcmd_ForceHeal_f( void ) { return; } + if ( !g_cheats->integer ) + { + gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); + return; + } char *newVal = gi.argv(1); if ( !VALIDSTRING( newVal ) ) { @@ -282,6 +297,11 @@ void Svcmd_ForcePush_f( void ) { return; } + if ( !g_cheats->integer ) + { + gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); + return; + } char *newVal = gi.argv(1); if ( !VALIDSTRING( newVal ) ) { @@ -315,6 +335,11 @@ void Svcmd_ForcePull_f( void ) { return; } + if ( !g_cheats->integer ) + { + gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); + return; + } char *newVal = gi.argv(1); if ( !VALIDSTRING( newVal ) ) { @@ -348,6 +373,11 @@ void Svcmd_ForceSpeed_f( void ) { return; } + if ( !g_cheats->integer ) + { + gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); + return; + } char *newVal = gi.argv(1); if ( !VALIDSTRING( newVal ) ) { @@ -380,6 +410,11 @@ void Svcmd_ForceGrip_f( void ) { return; } + if ( !g_cheats->integer ) + { + gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); + return; + } char *newVal = gi.argv(1); if ( !VALIDSTRING( newVal ) ) { @@ -412,6 +447,11 @@ void Svcmd_ForceLightning_f( void ) { return; } + if ( !g_cheats->integer ) + { + gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); + return; + } char *newVal = gi.argv(1); if ( !VALIDSTRING( newVal ) ) { @@ -444,6 +484,11 @@ void Svcmd_MindTrick_f( void ) { return; } + if ( !g_cheats->integer ) + { + gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); + return; + } char *newVal = gi.argv(1); if ( !VALIDSTRING( newVal ) ) { @@ -476,6 +521,11 @@ void Svcmd_SaberDefense_f( void ) { return; } + if ( !g_cheats->integer ) + { + gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); + return; + } char *newVal = gi.argv(1); if ( !VALIDSTRING( newVal ) ) { @@ -508,6 +558,11 @@ void Svcmd_SaberOffense_f( void ) { return; } + if ( !g_cheats->integer ) + { + gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); + return; + } char *newVal = gi.argv(1); if ( !VALIDSTRING( newVal ) ) { @@ -597,7 +652,8 @@ qboolean ConsoleCommand( void ) { cmd = gi.argv(0); - if ( Q_stricmp (cmd, "entitylist") == 0 ) { + if ( Q_stricmp (cmd, "entitylist") == 0 ) + { Svcmd_EntityList_f(); return qtrue; } @@ -612,28 +668,57 @@ qboolean ConsoleCommand( void ) { // return qtrue; // } - if (Q_stricmp (cmd, "nav") == 0) { + if (Q_stricmp (cmd, "nav") == 0) + { + if ( !g_cheats->integer ) + { + gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); + return qfalse; + } Svcmd_Nav_f (); return qtrue; } - if (Q_stricmp (cmd, "npc") == 0) { + if (Q_stricmp (cmd, "npc") == 0) + { + if ( !g_cheats->integer ) + { + gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); + return qfalse; + } Svcmd_NPC_f (); return qtrue; } - if (Q_stricmp (cmd, "use") == 0) { + if (Q_stricmp (cmd, "use") == 0) + { + if ( !g_cheats->integer ) + { + gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); + return qfalse; + } Svcmd_Use_f (); return qtrue; } - if ( Q_stricmp( cmd, "ICARUS" ) == 0 ) { + if ( Q_stricmp( cmd, "ICARUS" ) == 0 ) + { + if ( !g_cheats->integer ) + { + gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); + return qfalse; + } Svcmd_ICARUS_f(); return qtrue; } if ( Q_stricmp( cmd, "saberColor" ) == 0 ) { + if ( !g_cheats->integer ) + { + gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); + return qfalse; + } Svcmd_SaberColor_f(); return qtrue; } @@ -715,6 +800,11 @@ qboolean ConsoleCommand( void ) { } if ( Q_stricmp( cmd, "runscript" ) == 0 ) { + if ( !g_cheats->integer ) + { + gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); + return qfalse; + } char *cmd2 = gi.argv(1); if ( cmd2 && cmd2[0] ) @@ -748,6 +838,11 @@ qboolean ConsoleCommand( void ) { if ( Q_stricmp( cmd, "playerteam" ) == 0 ) { + if ( !g_cheats->integer ) + { + gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); + return qfalse; + } char *cmd2 = gi.argv(1); int n; @@ -785,6 +880,11 @@ qboolean ConsoleCommand( void ) { if ( Q_stricmp( cmd, "control" ) == 0 ) { + if ( !g_cheats->integer ) + { + gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); + return qfalse; + } char *cmd2 = gi.argv(1); if ( !*cmd2 || !cmd2[0] ) { diff --git a/code/game/g_target.cpp b/code/game/g_target.cpp index 9b18454..3871b64 100644 --- a/code/game/g_target.cpp +++ b/code/game/g_target.cpp @@ -462,7 +462,7 @@ void SP_target_relay (gentity_t *self) //========================================================== -/*QUAKED target_kill (.5 .5 .5) (-8 -8 -8) (8 8 8) FALLING +/*QUAKED target_kill (.5 .5 .5) (-8 -8 -8) (8 8 8) FALLING ELECTRICAL Kills the activator. */ void target_kill_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { @@ -479,6 +479,16 @@ void target_kill_use( gentity_t *self, gentity_t *other, gentity_t *activator ) CGCam_Fade( src, dst, 10000 ); } } + else if ( self->spawnflags & 2 ) // electrical + { + G_Damage ( activator, NULL, NULL, NULL, NULL, 100000, DAMAGE_NO_PROTECTION, MOD_ELECTROCUTE ); + + if ( activator->client ) + { + activator->s.powerups |= ( 1 << PW_SHOCKED ); + activator->client->ps.powerups[PW_SHOCKED] = level.time + 4000; + } + } else { G_Damage ( activator, NULL, NULL, NULL, NULL, 100000, DAMAGE_NO_PROTECTION, MOD_UNKNOWN); @@ -994,9 +1004,18 @@ void SP_target_play_music( gentity_t *self ) self->message = G_NewString (s); self->e_UseFunc = useF_target_play_music_use; extern cvar_t *com_buildScript; - //FIXME: Precache! + //Precache! if (com_buildScript->integer) {//copy this puppy over - gi.SendConsoleCommand(va("touchFile %s\n",s)); + char buffer[MAX_QPATH]; + fileHandle_t hFile; + + Q_strncpyz( buffer, s, sizeof(buffer) ); + COM_DefaultExtension( buffer, sizeof(buffer), ".mp3"); + + gi.FS_FOpenFile(buffer, &hFile, FS_READ); + if (hFile) { + gi.FS_FCloseFile( hFile ); + } } } @@ -1023,6 +1042,7 @@ void target_secret_use(gentity_t *self, gentity_t *other, gentity_t *activator) gclient_t* const client = &level.clients[0]; client->sess.missionStats.secretsFound++; G_Sound( self, self->noise_index ); + gi.SendServerCommand( NULL, "cp \"Secret Area!\"" ); } /*QUAKED target_secret (1 0 1) (-4 -4 -4) (4 4 4) diff --git a/code/game/g_trigger.cpp b/code/game/g_trigger.cpp index 1374e58..a1ca1d8 100644 --- a/code/game/g_trigger.cpp +++ b/code/game/g_trigger.cpp @@ -51,6 +51,10 @@ void multi_trigger_run( gentity_t *ent ) } G_UseTargets (ent, ent->activator); + if ( ent->noise_index ) + { + G_Sound( ent->activator, ent->noise_index ); + } if ( ent->target2 && ent->target2[0] && ent->wait >= 0 ) { @@ -307,6 +311,11 @@ void trigger_cleared_fire (gentity_t *self) { G_UseTargets2( self, self->activator, self->target2 ); self->e_ThinkFunc = thinkF_NULL; + // should start the wait timer now, because the trigger's just been cleared, so we must "wait" from this point + if ( self->wait > 0 ) + { + self->nextthink = level.time + ( self->wait + self->random * crandom() ) * 1000; + } } qboolean G_TriggerActive( gentity_t *self ) @@ -363,6 +372,7 @@ MULTIPLE - multiple entities can touch this trigger in a single frame *and* if n "hiderange" As long as NPC's head is in this trigger, NPCs out of this hiderange cannot see him. If you set an angle on the trigger, they're only hidden from enemies looking in that direction. the player's crouch viewheight is 36, his standing viewheight is 54. So a trigger thast should hide you when crouched but not standing should be 48 tall. "target2" The trigger will fire this only when the trigger has been activated and subsequently 'cleared'( once any of the conditions on the trigger have not been satisfied). This will not fire the "target" more than once until the "target2" is fired (trigger field is 'cleared') "speed" How many seconds to wait to fire the target2, default is 1 +"noise" Sound to play when the trigger fires (plays at activator's origin) Variable sized repeatable trigger. Must be targeted at one or more entities. so, the basic time between firing is a random time between @@ -379,6 +389,14 @@ so, the basic time between firing is a random time between team_t TranslateTeamName( const char *name ); void SP_trigger_multiple( gentity_t *ent ) { + char buffer[MAX_QPATH]; + char *s; + if ( G_SpawnString( "noise", "*NOSOUND*", &s ) ) + { + Q_strncpyz( buffer, s, sizeof(buffer) ); + COM_DefaultExtension( buffer, sizeof(buffer), ".wav"); + ent->noise_index = G_SoundIndex(buffer); + } G_SpawnFloat( "wait", "0", &ent->wait );//was 0.5 ... but that means wait can never be zero... we should probably put it back to 0.5, though... G_SpawnFloat( "random", "0", &ent->random ); @@ -426,6 +444,7 @@ MULTIPLE - multiple entities can touch this trigger in a single frame *and* if n Variable sized repeatable trigger. Must be targeted at one or more entities. so, the basic time between firing is a random time between (wait - random) and (wait + random) +"noise" Sound to play when the trigger fires (plays at activator's origin) "NPC_targetname" - If set, only an NPC with a matching NPC_targetname will trip this trigger "team" - If set, only this team can trip this trigger @@ -437,6 +456,15 @@ so, the basic time between firing is a random time between */ void SP_trigger_once( gentity_t *ent ) { + char buffer[MAX_QPATH]; + char *s; + if ( G_SpawnString( "noise", "*NOSOUND*", &s ) ) + { + Q_strncpyz( buffer, s, sizeof(buffer) ); + COM_DefaultExtension( buffer, sizeof(buffer), ".wav"); + ent->noise_index = G_SoundIndex(buffer); + } + ent->wait = -1; ent->e_TouchFunc = touchF_Touch_Multi; @@ -1011,13 +1039,14 @@ trigger_hurt ============================================================================== */ -/*QUAKED trigger_hurt (.1 .5 .1) ? START_OFF PLAYERONLY SILENT NO_PROTECTION x FALLING ELECTRICAL INACTIVE MULTIPLE +/*QUAKED trigger_hurt (.1 .5 .1) ? START_OFF PLAYERONLY SILENT NO_PROTECTION LOCKCAM FALLING ELECTRICAL INACTIVE MULTIPLE Any entity that touches this will be hurt. It does dmg points of damage each server frame PLAYERONLY only the player is hurt by it SILENT supresses playing the sound NO_PROTECTION *nothing* stops the damage +LOCKCAM Falling death results in camera locking in place FALLING Forces a falling scream and anim ELECTRICAL does electrical damage MULTIPLE multiple entities can touch this trigger in a single frame *and* if needed, the trigger can have a wait of > 0 @@ -1025,6 +1054,7 @@ MULTIPLE multiple entities can touch this trigger in a single frame *and* "dmg" default 5 (whole numbers only) "delay" How many seconds it takes to get from 0 to "dmg" (default is 0) "wait" Use in instead of "SLOW" - determines how often the player gets hurt, 0.1 is every frame, 1.0 is once per second. -1 will stop after one use +"count" If set, FALLING death causes a fade to black in this many milliseconds (default is 10000 = 10 seconds) */ void hurt_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { @@ -1125,14 +1155,27 @@ void hurt_touch( gentity_t *self, gentity_t *other, trace_t *trace ) other->client->ps.powerups[PW_SHOCKED] = level.time + 1000; } - if(self->spawnflags & 32) + if ( self->spawnflags & 32 ) {//falling death - G_Damage (other, self, self, NULL, NULL, actualDmg, dflags, MOD_FALLING); - if ( !other->s.number && other->health <= 0 && 1 ) + G_Damage (other, self, self, NULL, NULL, actualDmg, dflags|DAMAGE_NO_ARMOR, MOD_FALLING); + if ( !other->s.number && other->health <= 0 ) { - extern void CGCam_Fade( vec4_t source, vec4_t dest, float duration ); - float src[4] = {0,0,0,0},dst[4]={0,0,0,1}; - CGCam_Fade( src, dst, 10000 ); + if ( self->count ) + { + extern void CGCam_Fade( vec4_t source, vec4_t dest, float duration ); + float src[4] = {0,0,0,0},dst[4]={0,0,0,1}; + CGCam_Fade( src, dst, self->count ); + } + if ( self->spawnflags & 16 ) + {//lock cam + cg.overrides.active |= CG_OVERRIDE_3RD_PERSON_CDP; + cg.overrides.thirdPersonCameraDamp = 0; + } + if ( other->client ) + { + other->client->ps.pm_flags |= PMF_SLOW_MO_FALL; + } + //G_SoundOnEnt( other, CHAN_VOICE, "*falling1.wav" );//CHAN_VOICE_ATTEN? } } else diff --git a/code/game/g_turret.cpp b/code/game/g_turret.cpp index 6c06fa9..e5f7407 100644 --- a/code/game/g_turret.cpp +++ b/code/game/g_turret.cpp @@ -8,8 +8,9 @@ extern team_t TranslateTeamName( const char *name ); extern cvar_t *g_spskill; -extern void G_SetEnemy( gentity_t *self, gentity_t *enemy ); +void G_SetEnemy( gentity_t *self, gentity_t *enemy ); void finish_spawning_turret( gentity_t *base ); +void ObjectDie (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ); #define ARM_ANGLE_RANGE 60 #define HEAD_ANGLE_RANGE 90 @@ -34,9 +35,11 @@ void TurretPain( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, vec } //------------------------------------------------------------------------------------------------------------ -void turret_die ( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath,int hitLoc ) +void turret_die ( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath,int dFlags,int hitLoc ) //------------------------------------------------------------------------------------------------------------ { + vec3_t forward = { 0,0,-1 }, pos; + // Turn off the thinking of the base & use it's targets self->e_ThinkFunc = thinkF_NULL; self->e_UseFunc = useF_NULL; @@ -45,34 +48,51 @@ void turret_die ( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, in self->e_DieFunc = dieF_NULL; self->takedamage = qfalse; self->health = 0; + self->s.loopSound = 0; // hack the effect angle so that explode death can orient the effect properly - VectorSet( self->s.angles, 0, 0, -1 ); + if ( self->spawnflags & 2 ) + { + VectorSet( forward, 0, 0, 1 ); + } + + VectorCopy( self->currentOrigin, self->s.pos.trBase ); + + if ( self->fxID > 0 ) + { + VectorMA( self->currentOrigin, 12, forward, pos ); + G_PlayEffect( self->fxID, pos, forward ); + } + + if ( self->splashDamage > 0 && self->splashRadius > 0 ) + { + G_RadiusDamage( self->currentOrigin, attacker, self->splashDamage, self->splashRadius, attacker, MOD_UNKNOWN ); + } + + if ( self->s.eFlags & EF_SHADER_ANIM ) + { + self->s.frame = 1; // black + } + + self->s.weapon = 0; // crosshair code uses this to mark crosshair red if ( self->s.modelindex2 ) { - gentity_t *dead = G_Spawn(); + // switch to damage model if we should + self->s.modelindex = self->s.modelindex2; - if ( dead ) + VectorCopy( self->currentAngles, self->s.apos.trBase ); + VectorClear( self->s.apos.trDelta ); + + if ( self->target ) { - G_SetAngles( dead, self->currentAngles ); - G_SetOrigin( dead, self->currentOrigin ); // self->s.origin ); - VectorSet( dead->maxs, 8.0f, 8.0f, 8.0f ); - VectorScale( dead->maxs, -1, dead->mins ); - - dead->s.modelindex = self->s.modelindex2; - dead->contents = CONTENTS_SOLID; - gi.linkentity( dead ); - - if ( self->s.eFlags & EF_SHADER_ANIM ) - { - dead->s.eFlags = EF_SHADER_ANIM; - dead->s.frame = 1; // black - } + G_UseTargets( self, attacker ); } } - - ExplodeDeath( self ); + else + { + ObjectDie( self, inflictor, attacker, damage, meansOfDeath ); + } } //---------------------------------------------------------------- @@ -565,7 +585,7 @@ void finish_spawning_turret( gentity_t *base ) } // Set up our explosion effect for the ExplodeDeath code.... - base->fxID = G_EffectIndex( "tripMine/explosion" ); + base->fxID = G_EffectIndex( "turret/explode" ); G_EffectIndex( "spark_exp_nosnd" ); base->e_UseFunc = useF_turret_base_use; @@ -616,13 +636,13 @@ void finish_spawning_turret( gentity_t *base ) if ( base->spawnflags & 2 ) {//upside-down, invert mins and maxe - VectorSet( base->maxs, 8.0f, 8.0f, 22.0f ); - VectorSet( base->mins, -8.0f, -8.0f, 0.0f ); + VectorSet( base->maxs, 10.0f, 10.0f, 30.0f ); + VectorSet( base->mins, -10.0f, -10.0f, 0.0f ); } else { - VectorSet( base->maxs, 8.0f, 8.0f, 0.0f ); - VectorSet( base->mins, -8.0f, -8.0f, -22.0f ); + VectorSet( base->maxs, 10.0f, 10.0f, 0.0f ); + VectorSet( base->mins, -10.0f, -10.0f, -30.0f ); } // Precache moving sounds G_SoundIndex( "sound/chars/turret/startup.wav" ); @@ -630,7 +650,8 @@ void finish_spawning_turret( gentity_t *base ) G_SoundIndex( "sound/chars/turret/ping.wav" ); G_SoundIndex( "sound/chars/turret/move.wav" ); - base->contents = CONTENTS_BODY; + base->contents = MASK_SHOT;//CONTENTS_BODY; + base->max_health = base->health; base->takedamage = qtrue; base->e_DieFunc = dieF_turret_die; @@ -1315,9 +1336,11 @@ void pas_think( gentity_t *ent ) } } -/*QUAKED misc_sentry_turret (1 0 0) (-16 -16 0) (16 16 24) START_OFF +/*QUAKED misc_sentry_turret (1 0 0) (-16 -16 0) (16 16 24) START_OFF RESERVED personal assault sentry, like the ones you can carry in your inventory + RESERVED - do no use this flag for anything, does nothing..etc. + radius - How far away an enemy can be for it to pick it up (default 512) count - number of shots before thing deactivates. -1 = infinite, default 150 health - How much damage it can take before exploding (default 50) @@ -1399,6 +1422,11 @@ void SP_PAS( gentity_t *base ) base->e_PainFunc = painF_TurretPain; base->e_DieFunc = dieF_turret_die; + // hack this flag on so that when it calls the turret die code, it will orient the effect up + // HACK + //-------------------------------------- + base->spawnflags |= 2; + // Use this for our missile effect RegisterItem( FindItemForWeapon( WP_TURRET )); base->s.weapon = WP_TURRET; @@ -1452,6 +1480,9 @@ qboolean place_portable_assault_sentry( gentity_t *self, vec3_t origin, vec3_t a { VectorCopy( tr.endpos, pas->s.origin ); SP_PAS( pas ); + + pas->contents |= CONTENTS_PLAYERCLIP; // player placed ones can block players but not npcs + pas->e_UseFunc = useF_NULL; // placeable ones never need to be used // we don't hurt us or anyone who belongs to the same team as us. @@ -1522,7 +1553,7 @@ void ion_cannon_think( gentity_t *self ) } //---------------------------------------------------------------------------------------------- -void ion_cannon_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc ) +void ion_cannon_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags,int hitLoc ) //---------------------------------------------------------------------------------------------- { vec3_t org; @@ -1866,7 +1897,7 @@ void panel_turret_shoot( gentity_t *self, vec3_t org, vec3_t dir) } //----------------------------------------- -void misc_panel_turret_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod, int hitLoc ) +void misc_panel_turret_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod, int dFlags, int hitLoc ) { if ( self->target3 ) { @@ -1876,6 +1907,7 @@ void misc_panel_turret_die( gentity_t *self, gentity_t *inflictor, gentity_t *at // FIXME: might need some other kind of logic or functionality in here?? G_UseTargets2( self, player, self->target2 ); G_ClearViewEntity( player ); + cg.overrides.active &= ~CG_OVERRIDE_FOV; cg.overrides.fov = 0; } @@ -1941,20 +1973,22 @@ void panel_turret_think( gentity_t *self ) G_ClearViewEntity( player ); G_Sound( player, self->soundPos2 ); - cg.overrides.fov = 0; + cg.overrides.active &= ~CG_OVERRIDE_FOV; + cg.overrides.fov = 0; // can be drawn - self->s.eFlags &= ~EF_NODRAW; +// self->s.eFlags &= ~EF_NODRAW; } else { // don't draw me when being looked through - self->s.eFlags |= EF_NODRAW; - self->s.modelindex = 0; +// self->s.eFlags |= EF_NODRAW; +// self->s.modelindex = 0; // we only need to think when we are being used self->nextthink = level.time + 50; + cg.overrides.active |= CG_OVERRIDE_FOV; cg.overrides.fov = 50; } @@ -1967,7 +2001,7 @@ void panel_turret_think( gentity_t *self ) AngleVectors( self->s.apos.trBase, dir, NULL, NULL ); VectorCopy( self->currentOrigin, pt ); - pt[2] -= 3; + pt[2] -= 4; panel_turret_shoot( self, pt, dir ); self->attackDebounceTime = level.time + self->delay; @@ -2029,7 +2063,7 @@ void SP_misc_panel_turret( gentity_t *self ) self->dflags |= DAMAGE_CUSTOM_HUD; // dumb, but we draw a custom hud } - self->s.modelindex = G_ModelIndex( "models/map_objects/kejim/impcam.md3" ); + self->s.modelindex = G_ModelIndex( "models/map_objects/imp_mine/ladyluck_gun.md3" ); self->soundPos1 = G_SoundIndex( "sound/movers/camera_on.mp3" ); self->soundPos2 = G_SoundIndex( "sound/movers/camera_off.mp3" ); diff --git a/code/game/g_usable.cpp b/code/game/g_usable.cpp index 86a54d6..a3dabe4 100644 --- a/code/game/g_usable.cpp +++ b/code/game/g_usable.cpp @@ -151,7 +151,7 @@ void func_usable_pain(gentity_t *self, gentity_t *inflictor, gentity_t *attacker GEntity_UseFunc( self, attacker, attacker ); } -void func_usable_die(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc) +void func_usable_die(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags,int hitLoc) { self->takedamage = qfalse; GEntity_UseFunc( self, inflictor, attacker ); diff --git a/code/game/g_utils.cpp b/code/game/g_utils.cpp index ca57ac2..a55e6d9 100644 --- a/code/game/g_utils.cpp +++ b/code/game/g_utils.cpp @@ -112,10 +112,10 @@ void G_PlayEffect( int fxID, vec3_t origin, vec3_t fwd ) VectorSet( tent->maxs, FX_ENT_RADIUS, FX_ENT_RADIUS, FX_ENT_RADIUS ); VectorScale( tent->maxs, -1, tent->mins ); - VectorCopy( fwd, tent->s.angles ); + VectorCopy( fwd, tent->pos3 ); // Assume angles, we'll do a cross product on the other end to finish up - MakeNormalVectors( fwd, tent->s.angles2, temp ); + MakeNormalVectors( fwd, tent->pos4, temp ); gi.linkentity( tent ); } @@ -131,10 +131,10 @@ void G_PlayEffect( int fxID, int entNum, vec3_t fwd ) tent->s.otherEntityNum = entNum; VectorSet( tent->maxs, FX_ENT_RADIUS, FX_ENT_RADIUS, FX_ENT_RADIUS ); VectorScale( tent->maxs, -1, tent->mins ); - VectorCopy( fwd, tent->s.angles ); + VectorCopy( fwd, tent->pos3 ); // Assume angles, we'll do a cross product on the other end to finish up - MakeNormalVectors( fwd, tent->s.angles2, temp ); + MakeNormalVectors( fwd, tent->pos4, temp ); } // Play an effect bolted onto the muzzle of the specified client @@ -162,8 +162,8 @@ void G_PlayEffect( int fxID, vec3_t origin, vec3_t axis[3] ) VectorScale( tent->maxs, -1, tent->mins ); // We can just assume axis[2] from doing a cross product on these. - VectorCopy( axis[0], tent->s.angles ); - VectorCopy( axis[1], tent->s.angles2 ); + VectorCopy( axis[0], tent->pos3 ); + VectorCopy( axis[1], tent->pos4 ); } // Effect playing utilities - bolt an effect to a ghoul2 models bolton point @@ -382,7 +382,7 @@ NULL will be returned if the end of the list is reached. gentity_t *G_Find (gentity_t *from, int fieldofs, const char *match) { char *s; - + if(!match || !match[0]) { return NULL; @@ -393,10 +393,15 @@ gentity_t *G_Find (gentity_t *from, int fieldofs, const char *match) else from++; - for ( ; from < &g_entities[globals.num_entities] ; from++) +// for ( ; from < &g_entities[globals.num_entities] ; from++) + int i=from-g_entities; + for ( ; i < globals.num_entities ; i++) { - if (!from->inuse) +// if (!from->inuse) + if(!PInUse(i)) continue; + + from=&g_entities[i]; s = *(char **) ((byte *)from + fieldofs); if (!s) continue; @@ -666,10 +671,14 @@ float vectoyaw( const vec3_t vec ) { void G_InitGentity( gentity_t *e ) { e->inuse = qtrue; + SetInUse(e); e->classname = "noclass"; e->s.number = e - g_entities; ICARUS_FreeEnt( e ); //ICARUS information must be added after this point + + // remove any ghoul2 models here + //let not gi.G2API_CleanGhoul2Models(e->ghoul2); //Navigational setups e->waypoint = WAYPOINT_NONE; @@ -699,14 +708,24 @@ gentity_t *G_Spawn( void ) e = NULL; // shut up warning i = 0; // shut up warning - for ( force = 0 ; force < 2 ; force++ ) { + for ( force = 0 ; force < 2 ; force++ ) + { // if we go through all entities and can't find one to free, // override the normal minimum times before use e = &g_entities[MAX_CLIENTS]; - for ( i = MAX_CLIENTS ; iinuse ) { +// for ( i = MAX_CLIENTS ; iinuse ) +// { +// continue; +// } + for ( i = MAX_CLIENTS ; iclassname = "freed"; ed->freetime = level.time; ed->inuse = qfalse; + ClearInUse(ed); } /* @@ -855,9 +878,24 @@ void G_KillBox (gentity_t *ent) { if ( hit == ent ) { continue; } - if ( !(hit->contents & ent->contents) ) { + if ( ent->s.number && hit->client->ps.stats[STAT_HEALTH] <= 0 ) + {//NPC continue; } + if ( ent->s.number ) + {//NPC + if ( !(hit->contents&CONTENTS_BODY) ) + { + continue; + } + } + else + {//player + if ( !(hit->contents & ent->contents) ) + { + continue; + } + } // nail it G_Damage ( hit, ent, ent, NULL, NULL, diff --git a/code/game/g_weapon.cpp b/code/game/g_weapon.cpp index 4448047..fafc350 100644 --- a/code/game/g_weapon.cpp +++ b/code/game/g_weapon.cpp @@ -43,7 +43,7 @@ static gentity_t *ent_list[MAX_GENTITIES]; // Tenloss Disruptor //---------- -#define DISRUPTOR_MAIN_DAMAGE 18 +#define DISRUPTOR_MAIN_DAMAGE 14 #define DISRUPTOR_NPC_MAIN_DAMAGE_CUT 0.25f #define DISRUPTOR_ALT_DAMAGE 4 @@ -222,26 +222,26 @@ static void WP_TraceSetStart( const gentity_t *ent, vec3_t start, const vec3_t m { //make sure our start point isn't on the other side of a wall trace_t tr; - vec3_t entMins; + vec3_t entMins, newstart; vec3_t entMaxs; - VectorAdd( ent->currentOrigin, ent->mins, entMins ); - VectorAdd( ent->currentOrigin, ent->maxs, entMaxs ); - - if ( G_BoxInBounds( start, mins, maxs, entMins, entMaxs ) ) - { - return; - } + VectorSet( entMaxs, 5, 5, 5 ); + VectorScale( entMaxs, -1, entMins ); if ( !ent->client ) { return; } - gi.trace( &tr, ent->client->renderInfo.eyePoint, mins, maxs, start, ent->s.number, MASK_SOLID|CONTENTS_SHOTCLIP ); + // man, I'm not so sure about this, but....was having some nasty problems with weapon muzzles in walls so this is sort of a last + // desperate attempt to remedy the situation + VectorMA( start, -20, forward, newstart ); + + gi.trace( &tr, newstart, entMins, entMaxs, start, ent->s.number, MASK_SOLID|CONTENTS_SHOTCLIP ); if ( tr.startsolid || tr.allsolid ) { + // there is a problem here.. return; } @@ -267,7 +267,7 @@ gentity_t *CreateMissile( vec3_t org, vec3_t dir, float vel, int life, gentity_t missile->alt_fire = altFire; missile->s.pos.trType = TR_LINEAR; - missile->s.pos.trTime = level.time - 10; // move a bit on the very first frame + missile->s.pos.trTime = level.time;// - 10; // move a bit on the very first frame VectorCopy( org, missile->s.pos.trBase ); VectorScale( dir, vel, missile->s.pos.trDelta ); VectorCopy( org, missile->currentOrigin); @@ -325,6 +325,10 @@ void WP_Explode( gentity_t *self ) { attacker = self->owner; } + else if ( self->activator ) + { + attacker = self->activator; + } if ( self->splashDamage > 0 && self->splashRadius > 0 ) { @@ -336,12 +340,15 @@ void WP_Explode( gentity_t *self ) G_UseTargets( self, attacker ); } - G_FreeEntity( self ); + G_SetOrigin( self, self->currentOrigin ); + + self->nextthink = level.time + 50; + self->e_ThinkFunc = thinkF_G_FreeEntity; } // We need to have a dieFunc, otherwise G_Damage won't actually make us die. I could modify G_Damage, but that entails too many changes //----------------------------------------------------------------------------- -void WP_ExplosiveDie( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath,int hitLoc ) +void WP_ExplosiveDie( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath,int dFlags,int hitLoc ) //----------------------------------------------------------------------------- { self->enemy = attacker; @@ -477,8 +484,16 @@ static void WP_FireBryarPistol( gentity_t *ent, qboolean alt_fire ) vectoangles( forward, angs ); - angs[PITCH] += ( crandom() * ((5-ent->NPC->currentAim)*0.5f) ); - angs[YAW] += ( crandom() * ((5-ent->NPC->currentAim)*0.5f) ); + if ( ent->client->NPC_class == CLASS_IMPWORKER ) + {//*sigh*, hack to make impworkers less accurate without affecteing imperial officer accuracy + angs[PITCH] += ( crandom() * (BLASTER_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f));//was 0.5f + angs[YAW] += ( crandom() * (BLASTER_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f));//was 0.5f + } + else + { + angs[PITCH] += ( crandom() * ((5-ent->NPC->currentAim)*0.25f) ); + angs[YAW] += ( crandom() * ((5-ent->NPC->currentAim)*0.25f) ); + } AngleVectors( angs, forward, NULL, NULL ); } @@ -505,12 +520,12 @@ static void WP_FireBryarPistol( gentity_t *ent, qboolean alt_fire ) missile->count = count; // this will get used in the projectile rendering code to make a beefier effect } - if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) - { - // in overcharge mode, so doing double damage - missile->flags |= FL_OVERCHARGED; - damage *= 2; - } +// if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) +// { +// // in overcharge mode, so doing double damage +// missile->flags |= FL_OVERCHARGED; +// damage *= 2; +// } missile->damage = damage; missile->dflags = DAMAGE_DEATH_KNOCKBACK; @@ -572,15 +587,15 @@ static void WP_FireBlasterMissile( gentity_t *ent, vec3_t start, vec3_t dir, qbo } } - if ( ent->client ) - { - if ( ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) - { - // in overcharge mode, so doing double damage - missile->flags |= FL_OVERCHARGED; - damage *= 2; - } - } +// if ( ent->client ) +// { +// if ( ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) +// { +// // in overcharge mode, so doing double damage +// missile->flags |= FL_OVERCHARGED; +// damage *= 2; +// } +// } missile->damage = damage; missile->dflags = DAMAGE_DEATH_KNOCKBACK; @@ -619,7 +634,6 @@ static void WP_FireBlaster( gentity_t *ent, qboolean alt_fire ) if ( ent->client && ent->NPC && ( ent->client->NPC_class == CLASS_STORMTROOPER || ent->client->NPC_class == CLASS_SWAMPTROOPER ) ) - { angs[PITCH] += ( crandom() * (BLASTER_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f));//was 0.5f angs[YAW] += ( crandom() * (BLASTER_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f));//was 0.5f @@ -681,12 +695,13 @@ static void WP_DisruptorMainFire( gentity_t *ent ) } VectorCopy( muzzle, start ); + WP_TraceSetStart( ent, start, vec3_origin, vec3_origin ); - if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) - { - // in overcharge mode, so doing double damage - damage *= 2; - } +// if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) +// { +// // in overcharge mode, so doing double damage +// damage *= 2; +// } VectorMA( start, shotRange, forward, end ); @@ -756,6 +771,8 @@ static void WP_DisruptorMainFire( gentity_t *ent ) VectorMA( start, dist, forward, spot ); AddSightEvent( ent, spot, 256, AEL_DISCOVERED, 50 ); } + VectorMA( start, shotDist-4, forward, spot ); + AddSightEvent( ent, spot, 256, AEL_DISCOVERED, 50 ); } //--------------------------------------------------------- @@ -815,11 +832,11 @@ void WP_DisruptorAltFire( gentity_t *ent ) skip = ent->s.number; - if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) - { - // in overcharge mode, so doing double damage - damage *= 2; - } +// if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) +// { +// // in overcharge mode, so doing double damage +// damage *= 2; +// } for ( int i = 0; i < traces; i++ ) { @@ -892,7 +909,7 @@ void WP_DisruptorAltFire( gentity_t *ent ) { // we only make this mark on things that can't break or move tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_SNIPER_MISS ); - tent->s.eventParm = DirToByte( tr.plane.normal ); + VectorCopy( tr.plane.normal, tent->pos1 ); break; // hit solid, but doesn't take damage, so stop the shot...we _could_ allow it to shoot through walls, might be cool? } } @@ -913,13 +930,16 @@ void WP_DisruptorAltFire( gentity_t *ent ) shotDist = VectorNormalize( dir ); + //FIXME: if shoot *really* close to someone, the alert could be way out of their FOV for ( dist = 0; dist < shotDist; dist += 64 ) { //FIXME: on a really long shot, this could make a LOT of alerts in one frame... VectorMA( muzzle, dist, dir, spot ); AddSightEvent( ent, spot, 256, AEL_DISCOVERED, 50 ); } - + //FIXME: spawn a temp ent that continuously spawns sight alerts here? And 1 sound alert to draw their attention? + VectorMA( start, shotDist-4, forward, spot ); + AddSightEvent( ent, spot, 256, AEL_DISCOVERED, 50 ); } //--------------------------------------------------------- @@ -989,11 +1009,11 @@ static void WP_BowcasterMainFire( gentity_t *ent ) count--; } - if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) - { - // in overcharge mode, so doing double damage - damage *= 2; - } +// if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) +// { +// // in overcharge mode, so doing double damage +// damage *= 2; +// } for ( int i = 0; i < count; i++ ) { @@ -1016,10 +1036,10 @@ static void WP_BowcasterMainFire( gentity_t *ent ) VectorSet( missile->maxs, BOWCASTER_SIZE, BOWCASTER_SIZE, BOWCASTER_SIZE ); VectorScale( missile->maxs, -1, missile->mins ); - if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) - { - missile->flags |= FL_OVERCHARGED; - } +// if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) +// { +// missile->flags |= FL_OVERCHARGED; +// } missile->damage = damage; missile->dflags = DAMAGE_DEATH_KNOCKBACK; @@ -1068,12 +1088,12 @@ static void WP_BowcasterAltFire( gentity_t *ent ) VectorSet( missile->maxs, BOWCASTER_SIZE, BOWCASTER_SIZE, BOWCASTER_SIZE ); VectorScale( missile->maxs, -1, missile->mins ); - if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) - { - // in overcharge mode, so doing double damage - missile->flags |= FL_OVERCHARGED; - damage *= 2; - } +// if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) +// { +// // in overcharge mode, so doing double damage +// missile->flags |= FL_OVERCHARGED; +// damage *= 2; +// } missile->s.eFlags |= EF_BOUNCE; missile->bounceCount = 3; @@ -1137,12 +1157,12 @@ static void WP_RepeaterMainFire( gentity_t *ent, vec3_t dir ) } } - if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) - { - // in overcharge mode, so doing double damage - missile->flags |= FL_OVERCHARGED; - damage *= 2; - } +// if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) +// { +// // in overcharge mode, so doing double damage +// missile->flags |= FL_OVERCHARGED; +// damage *= 2; +// } missile->damage = damage; missile->dflags = DAMAGE_DEATH_KNOCKBACK; @@ -1199,12 +1219,12 @@ static void WP_RepeaterAltFire( gentity_t *ent ) missile->s.pos.trType = TR_GRAVITY; missile->s.pos.trDelta[2] += 40.0f; //give a slight boost in the upward direction - if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) - { - // in overcharge mode, so doing double damage - missile->flags |= FL_OVERCHARGED; - damage *= 2; - } +// if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) +// { +// // in overcharge mode, so doing double damage +// missile->flags |= FL_OVERCHARGED; +// damage *= 2; +// } missile->damage = damage; missile->dflags = DAMAGE_DEATH_KNOCKBACK; @@ -1239,8 +1259,8 @@ static void WP_FireRepeater( gentity_t *ent, qboolean alt_fire ) ent->client->NPC_class == CLASS_SWAMPTROOPER || ent->client->NPC_class == CLASS_SHADOWTROOPER ) ) { - angs[PITCH] += ( crandom() * (REPEATER_NPC_SPREAD+(6-ent->NPC->currentAim)*0.5f) ); - angs[YAW] += ( crandom() * (REPEATER_NPC_SPREAD+(6-ent->NPC->currentAim)*0.5f) ); + angs[PITCH] += ( crandom() * (REPEATER_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f) ); + angs[YAW] += ( crandom() * (REPEATER_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f) ); } else { @@ -1295,12 +1315,12 @@ static void WP_DEMP2_MainFire( gentity_t *ent ) VectorSet( missile->maxs, DEMP2_SIZE, DEMP2_SIZE, DEMP2_SIZE ); VectorScale( missile->maxs, -1, missile->mins ); - if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) - { - // in overcharge mode, so doing double damage - missile->flags |= FL_OVERCHARGED; - damage *= 2; - } +// if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) +// { +// // in overcharge mode, so doing double damage +// missile->flags |= FL_OVERCHARGED; +// damage *= 2; +// } missile->damage = damage; missile->dflags = DAMAGE_DEATH_KNOCKBACK; @@ -1508,11 +1528,11 @@ static void WP_FlechetteMainFire( gentity_t *ent ) vel *= 0.5f; } - if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) - { - // in overcharge mode, so doing double damage - damage *= 2; - } +// if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) +// { +// // in overcharge mode, so doing double damage +// damage *= 2; +// } for ( int i = 0; i < FLECHETTE_SHOTS; i++ ) { @@ -1540,10 +1560,10 @@ static void WP_FlechetteMainFire( gentity_t *ent ) missile->damage = damage; - if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) - { - missile->flags |= FL_OVERCHARGED; - } +// if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) +// { +// missile->flags |= FL_OVERCHARGED; +// } missile->dflags = (DAMAGE_DEATH_KNOCKBACK|DAMAGE_EXTRA_KNOCKBACK); @@ -1655,7 +1675,7 @@ static void WP_FlechetteProxMine( gentity_t *ent ) missile->clipmask = MASK_SHOT; // we don't want it to bounce forever - missile->bounceCount = 0; + missile->bounceCount = 0; } */ //---------------------------------------------- @@ -1674,7 +1694,7 @@ void WP_flechette_alt_blow( gentity_t *ent ) static void WP_CreateFlechetteBouncyThing( vec3_t start, vec3_t fwd, gentity_t *self ) //------------------------------------------------------------------------------ { - gentity_t *missile = CreateMissile( start, fwd, 700 + random() * 700, 1500 + random() * 2000, self, qtrue ); + gentity_t *missile = CreateMissile( start, fwd, 950 + random() * 700, 1500 + random() * 2000, self, qtrue ); missile->e_ThinkFunc = thinkF_WP_flechette_alt_blow; @@ -2021,7 +2041,7 @@ static void WP_DropDetPack( gentity_t *self, vec3_t start, vec3_t dir ) missile->splashRadius = FLECHETTE_MINE_SPLASH_RADIUS; missile->splashMethodOfDeath = MOD_DETPACK;// ?SPLASH; - missile->clipmask = MASK_SHOT; + missile->clipmask = (CONTENTS_SOLID|CONTENTS_BODY|CONTENTS_SHOTCLIP);//MASK_SHOT; // we don't want it to ever bounce missile->bounceCount = 0; @@ -2248,7 +2268,7 @@ void CreateLaserTrap( gentity_t *laserTrap, vec3_t start, gentity_t *owner ) laserTrap->owner = owner; // VectorSet( laserTrap->mins, -LT_SIZE, -LT_SIZE, -LT_SIZE ); // VectorSet( laserTrap->maxs, LT_SIZE, LT_SIZE, LT_SIZE ); - laserTrap->clipmask = MASK_SHOT; + laserTrap->clipmask = (CONTENTS_SOLID|CONTENTS_BODY|CONTENTS_SHOTCLIP);//MASK_SHOT; laserTrap->s.pos.trTime = level.time; // move a bit on the very first frame VectorCopy( start, laserTrap->s.pos.trBase ); @@ -2403,7 +2423,7 @@ void thermalDetonatorExplode( gentity_t *ent ) } //------------------------------------------------------------------------------------------------------------- -void thermal_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod, int hitLoc ) +void thermal_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod, int dFlags, int hitLoc ) //------------------------------------------------------------------------------------------------------------- { thermalDetonatorExplode( self ); @@ -2695,9 +2715,9 @@ gentity_t *WP_FireThermalDetonator( gentity_t *ent, qboolean alt_fire ) VectorMA( target, Q_flrand( 0, -32 ), vec, target );//throw a little short } - target[0] += Q_flrand( -5, 5 )+(crandom()*(6-ent->NPC->currentAim)*4); - target[1] += Q_flrand( -5, 5 )+(crandom()*(6-ent->NPC->currentAim)*4); - target[2] += Q_flrand( -5, 5 )+(crandom()*(6-ent->NPC->currentAim)*4); + target[0] += Q_flrand( -5, 5 )+(crandom()*(6-ent->NPC->currentAim)*2); + target[1] += Q_flrand( -5, 5 )+(crandom()*(6-ent->NPC->currentAim)*2); + target[2] += Q_flrand( -5, 5 )+(crandom()*(6-ent->NPC->currentAim)*2); WP_LobFire( ent, start, target, bolt->mins, bolt->maxs, bolt->clipmask, bolt->s.pos.trDelta, qtrue, ent->s.number, ent->enemy->s.number ); } @@ -2941,16 +2961,19 @@ void WP_FireStunBaton( gentity_t *ent, qboolean alt_fire ) { gentity_t *tr_ent; trace_t tr; - vec3_t mins, maxs, end; + vec3_t mins, maxs, end, start; G_Sound( ent, G_SoundIndex( "sound/weapons/baton/fire" )); - VectorMA( muzzle, STUN_BATON_RANGE, forward, end ); + VectorCopy( muzzle, start ); + WP_TraceSetStart( ent, start, vec3_origin, vec3_origin ); + + VectorMA( start, STUN_BATON_RANGE, forward, end ); VectorSet( maxs, 6, 6, 6 ); VectorScale( maxs, -1, mins ); - gi.trace ( &tr, muzzle, mins, maxs, end, ent->s.number, MASK_SHOT ); + gi.trace ( &tr, start, mins, maxs, end, ent->s.number, MASK_SHOT ); if ( tr.entityNum >= ENTITYNUM_WORLD ) { @@ -3602,6 +3625,8 @@ extern void ChangeWeapon( gentity_t *ent, int newWeapon ); gi.Printf( S_COLOR_RED"ERROR: no waypoint for emplaced_gun %s at %s\n", self->targetname, vtos(self->currentOrigin) ); } #endif + + G_Sound( self, G_SoundIndex( "sound/weapons/emplaced/emplaced_mount.mp3" )); } } @@ -3632,7 +3657,7 @@ void emplaced_blow( gentity_t *ent ) } //---------------------------------------------------------- -void emplaced_gun_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int hitLoc ) +void emplaced_gun_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags,int hitLoc ) { vec3_t org; @@ -3685,10 +3710,12 @@ void emplaced_gun_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacke G_RadiusDamage( self->currentOrigin, self, self->splashDamage, self->splashRadius, self, MOD_UNKNOWN ); // when the gun is dead, add some ugliness to it. - self->lastAngles[YAW] += 4; - self->lastAngles[PITCH] += 6; - self->lastAngles[ROLL] += 7; - gi.G2API_SetBoneAnglesIndex( &self->ghoul2[self->playerModel], self->lowerLumbarBone, self->lastAngles, BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL ); + vec3_t ugly; + + ugly[YAW] = 4; + ugly[PITCH] = self->lastAngles[PITCH] * 0.8f + crandom() * 6; + ugly[ROLL] = crandom() * 7; + gi.G2API_SetBoneAnglesIndex( &self->ghoul2[self->playerModel], self->lowerLumbarBone, ugly, BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL ); VectorSet( org, 0, 0, 1 ); @@ -3713,7 +3740,8 @@ void emplaced_gun_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacke G_SetOrigin( ent, org ); VectorCopy( org, ent->s.origin ); - VectorSet( ent->s.angles, 0, 0, 1 ); // up + VectorSet( ent->s.angles, -90, 0, 0 ); // up + G_SetAngles( ent, ent->s.angles ); gi.linkentity( ent ); } @@ -3734,35 +3762,8 @@ void SP_emplaced_gun( gentity_t *ent ) ent->svFlags |= SVF_INACTIVE; } - // We'll set a base bounding box, then rotate it to match the spawn angles below - VectorSet( ent->mins, -30, -20, -5 ); - VectorSet( ent->maxs, 90, 20, 60 ); - - // rotate - float temp, ang = ent->s.angles[YAW] * 0.0174533f; - float c, s; - - c = cos( ang ); - s = sin( ang ); - - temp = c * ent->mins[0] - s * ent->mins[1]; - ent->mins[1] = s * ent->mins[0] + c * ent->mins[1]; - ent->mins[0] = temp; - - temp = c * ent->maxs[0] - s * ent->maxs[1]; - ent->maxs[1] = s * ent->maxs[0] + c * ent->maxs[1]; - ent->maxs[0] = temp; - - // ensure that maxs is greater than mins - for ( int i = 0; i < 2; i++ ) - { - if ( ent->maxs[i] < ent->mins[i] ) - { - temp = ent->maxs[i]; - ent->maxs[i] = ent->mins[i]; - ent->mins[i] = temp; - } - } + VectorSet( ent->mins, -30, -30, -5 ); + VectorSet( ent->maxs, 30, 30, 60 ); if ( ent->spawnflags & EMPLACED_VULNERABLE ) { @@ -3779,6 +3780,10 @@ void SP_emplaced_gun( gentity_t *ent ) G_EffectIndex( "emplaced/explode" ); G_EffectIndex( "emplaced/dead_smoke" ); + G_SoundIndex( "sound/weapons/emplaced/emplaced_mount.mp3" ); + G_SoundIndex( "sound/weapons/emplaced/emplaced_dismount.mp3" ); + G_SoundIndex( "sound/weapons/emplaced/emplaced_move_lp.wav" ); + // Set up our defaults and override with custom amounts as necessary G_SpawnInt( "count", "600", &ent->count ); G_SpawnInt( "health", "250", &ent->health ); @@ -3797,7 +3802,7 @@ void SP_emplaced_gun( gentity_t *ent ) ent->headBolt = gi.G2API_AddBolt( &ent->ghoul2[0], "*seat" ); ent->handLBolt = gi.G2API_AddBolt( &ent->ghoul2[0], "*flash01" ); ent->handRBolt = gi.G2API_AddBolt( &ent->ghoul2[0], "*flash02" ); - ent->rootBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "model_root", qtrue ); + ent->rootBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "base_bone", qtrue ); ent->lowerLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[0], "swivel_bone", qtrue ); gi.G2API_SetBoneAngles( &ent->ghoul2[0], "swivel_bone", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL); diff --git a/code/game/game.def b/code/game/game.def deleted file mode 100644 index d405f05..0000000 --- a/code/game/game.def +++ /dev/null @@ -1,4 +0,0 @@ -EXPORTS - GetGameAPI - vmMain - dllEntry diff --git a/code/game/game.dsp b/code/game/game.dsp index 40e40a0..e0b1f59 100644 --- a/code/game/game.dsp +++ b/code/game/game.dsp @@ -1008,10 +1008,6 @@ SOURCE=.\g_icarus.h # End Source File # Begin Source File -SOURCE=.\g_infostrings.h -# End Source File -# Begin Source File - SOURCE=.\g_items.h # End Source File # Begin Source File diff --git a/code/game/ghoul2_shared.h b/code/game/ghoul2_shared.h index 6aaaa16..0f2bd9d 100644 --- a/code/game/ghoul2_shared.h +++ b/code/game/ghoul2_shared.h @@ -16,6 +16,14 @@ using namespace std; Ghoul2 Insert End */ +#define G2T_SV_TIME (0) +#define G2T_CG_TIME (1) +#define NUM_G2T_TIME (2) + +void G2API_SetTime(int currentTime,int clock); +int G2API_GetTime(int argTime); // this may or may not return arg depending on ghoul2_time cvar + + //=================================================================== // // G H O U L I I D E F I N E S @@ -52,17 +60,18 @@ surfaceInfo_t(): #define BONE_ANIM_OVERRIDE_DEFAULT (0x0020 + BONE_ANIM_OVERRIDE) #define BONE_ANIM_OVERRIDE_FREEZE (0x0040 + BONE_ANIM_OVERRIDE) // Causes Last Frame To Freeze And Not Loop To Beginning #define BONE_ANIM_BLEND 0x0080 // Blends to and from previously played frame on same bone for given time -#define BONE_ANIM_BLEND_FROM_PARENT 0x0100 // Used to start an animation, begins playback but blends from animation on parent bone to current animation for given time -#define BONE_ANIM_BLEND_TO_PARENT 0x0200 // Used to stop an animation, ends playback on bone and blends last frame to frames of parent bone for given time -#define BONE_ANIM_TOTAL ( BONE_ANIM_OVERRIDE | BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE_DEFAULT | BONE_ANIM_OVERRIDE_FREEZE | BONE_ANIM_BLEND | BONE_ANIM_BLEND_TO_PARENT | BONE_ANIM_BLEND_FROM_PARENT ) +#define BONE_ANIM_NO_LERP 0x1000 +#define BONE_ANIM_TOTAL (BONE_ANIM_NO_LERP| BONE_ANIM_OVERRIDE | BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE_DEFAULT | BONE_ANIM_OVERRIDE_FREEZE | BONE_ANIM_BLEND ) #define BONE_INDEX_INVALID -1 -#define MDXABONEDEF // used in the mdxformat.h file to stop redefinitions of the bone struct. +/*#define MDXABONEDEF // used in the mdxformat.h file to stop redefinitions of the bone struct. typedef struct { float matrix[3][4]; } mdxaBone_t; +*/ +#include "../renderer/mdx_format.h" // we save the whole structure here. struct boneInfo_t @@ -112,7 +121,6 @@ struct boltInfo_t{ int surfaceNumber; // surface number bolt attaches to int surfaceType; // if we attach to a surface, this tells us if it is an original surface or a generated one - doesn't go across the network int boltUsed; // nor does this - mdxaBone_t position; // this does not go across the network boltInfo_t(): boneNumber(-1), surfaceNumber(-1), @@ -137,6 +145,10 @@ typedef vector mdxaBone_v; // NOTE order in here matters. We save out from mModelindex to mFlags, but not the STL vectors that are at the top or the bottom. +class CBoneCache; +struct model_s; +//struct mdxaHeader_t; + class CGhoul2Info { public: @@ -149,7 +161,6 @@ public: qhandle_t mCustomSkin; int mModelBoltLink; int mSurfaceRoot; - int mCreationID; int mLodBias; int mNewOrigin; // this contains the bolt index of the new origin for this model qhandle_t mModel; // this and the next entries do NOT go across the network. They are for gameside access ONLY @@ -160,9 +171,18 @@ public: int mFlags; // used for determining whether to do full collision detection against this object // to here int *mTransformedVertsArray; // used to create an array of pointers to transformed verts per surface for collision detection - mdxaBone_v mTempBoneList; + CBoneCache *mBoneCache; int mSkin; + // these occasionally are not valid (like after a vid_restart) + // call the questionably efficient G2_SetupModelPointers(this) to insure validity + bool mValid; // all the below are proper and valid + const model_s *currentModel; + int currentModelSize; + const model_s *animModel; + int currentAnimModelSize; + const mdxaHeader_t *aHeader; + CGhoul2Info(): mModelindex(-1), mCustomShader(0), @@ -173,23 +193,21 @@ public: mAnimFrameDefault(0), mSkelFrameNum(-1), mMeshFrameNum(-1), - mCreationID(0), mFlags(0), mTransformedVertsArray(0), mLodBias(0), mSkin(0), - mNewOrigin(-1) + mNewOrigin(-1), + mBoneCache(0), + currentModel(0), + currentModelSize(0), + animModel(0), + currentAnimModelSize(0), + aHeader(0), + mValid(false) { mFileName[0] = 0; } - - ~CGhoul2Info(void) - { - mSlist.~surfaceInfo_v(); - mBltlist.~boltInfo_v(); - mBlist.~boneInfo_v(); - mTempBoneList.~mdxaBone_v(); - } }; class CGhoul2Info_v; @@ -265,6 +283,14 @@ public: { Alloc(); Array()=other.Array(); + int i; + for (i=0;i CCollisionRecord_m; - -enum EWraithInstFlags -{ - WF_BASEMODEL = (1<<0), - WF_CLIENTONLY = (1<<1), - WF_SERVERONLY = (1<<2), - WF_NPC = (1<<3), -}; //==================================================================== diff --git a/code/game/objectives.h b/code/game/objectives.h index 2c622b8..e61dc26 100644 --- a/code/game/objectives.h +++ b/code/game/objectives.h @@ -68,6 +68,7 @@ typedef enum //# Objective_e KEJIM_POST_OBJ5, //# KEJIM POST - GRAPHICS IN IT. ARTUS_DETENTION_OBJ3, //# ARTUS DETENTION DOOM_COMM_OBJ4, //# DOOM COMM - GRAPHICS IN IT. + DOOM_SHIELDS_OBJ3, //# DOOM SHIELDS //# #eol MAX_OBJECTIVES, @@ -85,7 +86,7 @@ typedef enum //# MissionFailed_e MISSIONFAILED_EMPLACEDGUNS, //# MISSIONFAILED_LADYLUCK, //# MISSIONFAILED_KYLECAPTURE, //# - + MISSIONFAILED_TOOMANYALLIESDIED, //# //# #eol MAX_MISSIONFAILED, } missionFailed_t; @@ -203,6 +204,7 @@ stringID_table_t objectiveTable [] = ENUM2STRING(KEJIM_POST_OBJ5), //# KEJIM POST - GRAPHICS IN IT. ENUM2STRING(ARTUS_DETENTION_OBJ3), //# ARTUS DETENTION ENUM2STRING(DOOM_COMM_OBJ4), //# DOOM COMM - GRAPHICS IN IT. IT MUST BE LAST IN THE LIST + ENUM2STRING(DOOM_SHIELDS_OBJ3), //# DOOM SHIELDS //stringID_table_t Must end with a null entry "", NULL @@ -219,6 +221,7 @@ stringID_table_t missionFailedTable [] = ENUM2STRING(MISSIONFAILED_EMPLACEDGUNS),//# ALL EMPLACED GUNS GONE ENUM2STRING(MISSIONFAILED_LADYLUCK), //# LADY LUCK DISTROYED ENUM2STRING(MISSIONFAILED_KYLECAPTURE), //# KYLE HAS BEEN CAPTURED + ENUM2STRING(MISSIONFAILED_TOOMANYALLIESDIED), //# TOO MANY ALLIES DIED //stringID_table_t Must end with a null entry "", NULL diff --git a/code/game/q_shared.cpp b/code/game/q_shared.cpp index 631e435..50c306b 100644 --- a/code/game/q_shared.cpp +++ b/code/game/q_shared.cpp @@ -54,7 +54,6 @@ COM_DefaultExtension ================== */ void COM_DefaultExtension (char *path, int maxSize, const char *extension ) { - char oldPath[MAX_QPATH]; char *src; if (path[0]) // or the strlen()-1 stuff gets a bad ptr for blank string @@ -73,8 +72,14 @@ void COM_DefaultExtension (char *path, int maxSize, const char *extension ) { } } - Q_strncpyz( oldPath, path, sizeof( oldPath ) ); - Com_sprintf( path, maxSize, "%s%s", oldPath, extension ); + if (strlen(path)+strlen(extension) >= maxSize) + { + Com_Printf ("COM_DefaultExtension: overflow adding %s to %s\n", extension, path); + } + else + { + strcat(path, extension); + } } /* diff --git a/code/game/q_shared.h b/code/game/q_shared.h index 11eef56..7e646a1 100644 --- a/code/game/q_shared.h +++ b/code/game/q_shared.h @@ -1358,6 +1358,7 @@ typedef struct playerState_s { // not communicated over the net at all // !! int useTime; //not sent + int lastShotTime;//last time you shot your weapon int ping; // server to game info for scoreboard int lastOnGround; //last time you were on the ground int lastStationary; //last time you were on the ground diff --git a/code/game/wp_saber.cpp b/code/game/wp_saber.cpp index aa49240..d967ab5 100644 --- a/code/game/wp_saber.cpp +++ b/code/game/wp_saber.cpp @@ -20,14 +20,15 @@ static int hitLoc[MAX_SABER_VICTIMS]; static qboolean hitDismember[MAX_SABER_VICTIMS]; static vec3_t saberHitLocation, saberHitNormal={0,0,1.0}; static float saberHitFraction; +static float sabersCrossed; static int saberHitEntity; static int numVictims = 0; #define SABER_PITCH_HACK 90 -#define REALISTIC_SABER_COLLISION 1 extern cvar_t *g_timescale; +extern cvar_t *g_dismemberment; extern void G_AddVoiceEvent( gentity_t *self, int event, int speakDebounceTime ); extern void CG_ChangeWeapon( int num ); @@ -38,7 +39,7 @@ extern void G_SoundOnEnt( gentity_t *ent, soundChannel_t channel, const char *so extern void NPC_UseResponse( gentity_t *self, gentity_t *user, qboolean useWhenDone ); extern void WP_FireDreadnoughtBeam( gentity_t *ent ); extern void G_MissileImpacted( gentity_t *ent, gentity_t *other, vec3_t impactPos, vec3_t normal, int hitLoc=HL_NONE ); -extern qboolean Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, gentity_t *incoming, float dist = 0.0f ); +extern evasionType_t Jedi_SaberBlockGo( gentity_t *self, vec3_t pHitloc, vec3_t phitDir, gentity_t *incoming, float dist = 0.0f ); extern int PM_PickAnim( gentity_t *self, int minAnim, int maxAnim ); extern void NPC_SetPainEvent( gentity_t *self ); extern qboolean PM_InAnimForSaberMove( int anim, int saberMove ); @@ -66,12 +67,13 @@ extern qboolean PM_InKnockDown( playerState_t *ps ); extern int PM_PowerLevelForSaberAnim( playerState_t *ps ); extern void PM_VelocityForSaberMove( playerState_t *ps, vec3_t throwDir ); extern qboolean PM_VelocityForBlockedMove( playerState_t *ps, vec3_t throwDir ); -extern int Jedi_ReCalcParryTime( gentity_t *self ); +extern int Jedi_ReCalcParryTime( gentity_t *self, evasionType_t evasionType ); extern qboolean Jedi_DodgeEvasion( gentity_t *self, gentity_t *shooter, trace_t *tr, int hitLoc ); extern void Jedi_PlayDeflectSound( gentity_t *self ); extern void Jedi_PlayBlockedPushSound( gentity_t *self ); extern qboolean Jedi_WaitingAmbush( gentity_t *self ); extern void Jedi_Ambush( gentity_t *self ); +extern qboolean Jedi_SaberBusy( gentity_t *self ); void WP_ForcePowerStart( gentity_t *self, forcePowers_t forcePower, int overrideAmt ); qboolean WP_ForcePowerUsable( gentity_t *self, forcePowers_t forcePower, int overrideAmt ); @@ -97,8 +99,8 @@ int forcePowerNeeded[NUM_FORCE_POWERS] = 4,//FP_HEAL,//instant 10,//FP_LEVITATION,//hold/duration 20,//FP_SPEED,//duration - 20,//FP_PUSH,//hold/duration - 20,//FP_PULL,//hold/duration + 15,//FP_PUSH,//hold/duration + 15,//FP_PULL,//hold/duration 20,//FP_TELEPATHY,//instant 1,//FP_GRIP,//hold/duration - FIXME: 30? 1,//FP_LIGHTNING,//hold/duration @@ -241,7 +243,8 @@ void G_CreateG2AttachedWeaponModel( gentity_t *ent, const char *psWeaponModel ) if (char *spot = strstr(weaponModel, ".md3") ) { *spot = 0; spot = strstr(weaponModel, "_w");//i'm using the in view weapon array instead of scanning the item list, so put the _w back on - if (!spot) { + if (!spot&&!strstr(weaponModel, "noweap")) + { strcat (weaponModel, "_w"); } strcat (weaponModel, ".glm"); //and change to ghoul2 @@ -335,6 +338,8 @@ void WP_SaberInitBladeData( gentity_t *ent ) VectorClear( ent->client->renderInfo.muzzleDirOld ); //VectorClear( ent->client->renderInfo.muzzleDirNext ); ent->client->ps.saberLengthOld = ent->client->ps.saberLength = 0; + ent->client->ps.saberLockEnemy = ENTITYNUM_NONE; + ent->client->ps.saberLockTime = 0; if ( ent->s.number ) { if ( ent->client->NPC_class == CLASS_DESANN ) @@ -361,6 +366,10 @@ void WP_SaberInitBladeData( gentity_t *ent ) {//boss always starts with strong attacks ent->client->ps.saberAnimLevel = FORCE_LEVEL_3; } + else if ( ent->client->NPC_class == CLASS_KYLE ) + { + ent->client->ps.saberAnimLevel = g_entities[0].client->ps.saberAnimLevel; + } else {//? ent->client->ps.saberAnimLevel = Q_irand( FORCE_LEVEL_1, FORCE_LEVEL_3 ); @@ -627,10 +636,12 @@ qboolean WP_GetSaberDeflectionAngle( gentity_t *attacker, gentity_t *defender ) } } } +#ifndef _FINAL_BUILD if ( d_saberCombat->integer ) { gi.Printf( S_COLOR_BLUE"%s deflected from %s to %s\n", attacker->targetname, saberMoveData[attacker->client->ps.saberMove].name, saberMoveData[attacker->client->ps.saberBounceMove].name ); } +#endif return qtrue; } } @@ -638,6 +649,15 @@ qboolean WP_GetSaberDeflectionAngle( gentity_t *attacker, gentity_t *defender ) void WP_SaberClearDamageForEntNum( int entityNum ) { +#ifndef _FINAL_BUILD + if ( d_saberCombat->integer ) + { + if ( entityNum ) + { + Com_Printf( "clearing damage for entnum %d\n", entityNum ); + } + } +#endif// _FINAL_BUILD for ( int i = 0; i < numVictims; i++ ) { if ( victimEntityNum[i] == entityNum ) @@ -650,11 +670,14 @@ void WP_SaberClearDamageForEntNum( int entityNum ) } } +extern float damageModifier[]; +extern float hitLocHealthPercentage[]; qboolean WP_SaberApplyDamage( gentity_t *ent, float baseDamage, int baseDFlags, qboolean brokenParry ) { qboolean didDamage = qfalse; gentity_t *victim; int dFlags = baseDFlags; + float maxDmg; if ( !numVictims ) @@ -674,6 +697,23 @@ qboolean WP_SaberApplyDamage( gentity_t *ent, float baseDamage, int baseDFlags, continue; } + if ( victim->s.weapon == WP_SABER && victim->client && !g_realisticSaberDamage->integer ) + {//dmg vs other saber fighters is modded by hitloc and capped + totalDmg[i] *= damageModifier[hitLoc[i]]; + if ( hitLoc[i] == HL_NONE ) + { + maxDmg = 33*baseDamage; + } + else + { + maxDmg = 50*hitLocHealthPercentage[hitLoc[i]]*baseDamage;//*victim->client->ps.stats[STAT_MAX_HEALTH]*2.0f; + } + if ( maxDmg < totalDmg[i] ) + { + totalDmg[i] = maxDmg; + } + dFlags |= DAMAGE_NO_HIT_LOC; + } //clamp the dmg between 5 and 100 if ( totalDmg[i] > 100 ) { @@ -686,9 +726,12 @@ qboolean WP_SaberApplyDamage( gentity_t *ent, float baseDamage, int baseDFlags, totalDmg[i] = 25; } } - else if ( totalDmg[i] < 5 ) + else { - totalDmg[i] = 5; + if ( totalDmg[i] < 5 ) + { + totalDmg[i] = 5; + } } if ( totalDmg[i] > 0 ) @@ -700,7 +743,18 @@ qboolean WP_SaberApplyDamage( gentity_t *ent, float baseDamage, int baseDFlags, {//already being knocked around dFlags |= DAMAGE_NO_KNOCKBACK; } - victim->client->dismemberable = hitDismember[i]; + if ( g_dismemberment->integer > 3 || g_realisticSaberDamage->integer ) + { + dFlags |= DAMAGE_DISMEMBER; + if ( hitDismember[i] ) + { + victim->client->dismembered = qfalse; + } + } + else if ( hitDismember[i] ) + { + dFlags |= DAMAGE_DISMEMBER; + } if ( baseDamage <= 1.0f ) {//very mild damage if ( victim->s.number == 0 || victim->client->ps.weapon == WP_SABER || victim->client->NPC_class == CLASS_GALAKMECH ) @@ -720,6 +774,8 @@ qboolean WP_SaberApplyDamage( gentity_t *ent, float baseDamage, int baseDFlags, } //victim->hitLoc = hitLoc[i]; + dFlags |= DAMAGE_NO_KNOCKBACK;//okay, let's try no knockback whatsoever... + dFlags &= ~DAMAGE_DEATH_KNOCKBACK; if ( g_realisticSaberDamage->integer ) { dFlags |= DAMAGE_NO_KNOCKBACK; @@ -757,10 +813,12 @@ qboolean WP_SaberApplyDamage( gentity_t *ent, float baseDamage, int baseDFlags, } } G_Damage( victim, ent, ent, dmgDir[i], dmgSpot[i], ceil(totalDmg[i]), dFlags, MOD_SABER, hitLoc[i] ); +#ifndef _FINAL_BUILD if ( d_saberCombat->integer ) { - gi.Printf( S_COLOR_RED"damage: %4.2f\n", totalDmg[i] ); + gi.Printf( S_COLOR_RED"damage: %4.2f, hitLoc %d\n", totalDmg[i], hitLoc[i] ); } +#endif //do the effect //G_PlayEffect( G_EffectIndex( "blood_sparks" ), dmgSpot[i], dmgDir[i] ); if ( ent->s.number == 0 ) @@ -819,7 +877,14 @@ void WP_SaberDamageAdd( float trDmg, int trVictimEntityNum, vec3_t trDmgDir, vec curVictim = numVictims; victimEntityNum[numVictims++] = trVictimEntityNum; } - totalDmg[curVictim] += trDmg*dmg; + + float addDmg = trDmg*dmg; + if ( trHitLoc!=HL_NONE && (hitLoc[curVictim]==HL_NONE||hitLocHealthPercentage[trHitLoc]>hitLocHealthPercentage[hitLoc[curVictim]]) ) + {//this hitLoc is more critical than the previous one this frame + hitLoc[curVictim] = trHitLoc; + } + + totalDmg[curVictim] += addDmg; if ( !VectorLengthSquared( dmgDir[curVictim] ) ) { VectorCopy( trDmgDir, dmgDir[curVictim] ); @@ -832,10 +897,6 @@ void WP_SaberDamageAdd( float trDmg, int trVictimEntityNum, vec3_t trDmgDir, vec // Make sure we keep track of the fraction. Why? // Well, if the saber hits something that stops it, the damage isn't done past that point. dmgFraction[curVictim] = fraction; - if ( trHitLoc != HL_NONE && hitLoc[curVictim] == HL_NONE ) - { - hitLoc[curVictim] = trHitLoc; - } if ( trDismember && !hitDismember[curVictim] ) { hitDismember[curVictim] = trDismember; @@ -858,10 +919,12 @@ qboolean WP_SabersIntersect( gentity_t *ent1, gentity_t *ent2, qboolean checkDir vec3_t saberBase2, saberTip2, saberBaseNext2, saberTipNext2; /* +#ifndef _FINAL_BUILD if ( d_saberCombat->integer ) { gi.Printf( S_COLOR_GREEN"Doing precise saber intersection check\n" ); } +#endif */ if ( !ent1 || !ent2 ) @@ -956,10 +1019,12 @@ float WP_SabersDistance( gentity_t *ent1, gentity_t *ent2 ) vec3_t saberBaseNext2, saberTipNext2, saberPoint2; /* +#ifndef _FINAL_BUILD if ( d_saberCombat->integer ) { gi.Printf( S_COLOR_GREEN"Doing precise saber intersection check\n" ); } +#endif */ if ( !ent1 || !ent2 ) @@ -1003,11 +1068,32 @@ float WP_SabersDistance( gentity_t *ent1, gentity_t *ent2 ) float sabersDist = ShortestLineSegBewteen2LineSegs( saberBaseNext1, saberTipNext1, saberBaseNext2, saberTipNext2, saberPoint1, saberPoint2 ); + //okay, this is a super hack, but makes saber collisions look better from the player point of view + /* + if ( sabersDist < 16.0f ) + { + vec3_t saberDistDir, saberMidPoint, camLookDir; + + VectorSubtract( saberPoint2, saberPoint1, saberDistDir ); + VectorMA( saberPoint1, 0.5f, saberDistDir, saberMidPoint ); + VectorSubtract( saberMidPoint, cg.refdef.vieworg, camLookDir ); + VectorNormalize( saberDistDir ); + VectorNormalize( camLookDir ); + float dot = fabs(DotProduct( camLookDir, saberDistDir )); + sabersDist -= 8.0f*dot; + if ( sabersDist < 0.0f ) + { + sabersDist = 0.0f; + } + } + */ + +#ifndef _FINAL_BUILD if ( d_saberCombat->integer > 2 ) { G_DebugLine( saberPoint1, saberPoint2, FRAMETIME, 0x00ffffff, qtrue ); } - +#endif return sabersDist; } @@ -1077,10 +1163,12 @@ qboolean WP_SaberDamageEffects( trace_t *tr, const vec3_t start, float length, f //FIXME: (distFromStart/length) is not guaranteed to be from 0 to 1... *sigh*... if ( length && saberHitFraction < 1.0f && (distFromStart/length) < 1.0f && (distFromStart/length) > saberHitFraction ) {//a saber was hit before this point, don't count it +#ifndef _FINAL_BUILD if ( d_saberCombat->integer ) { gi.Printf( S_COLOR_MAGENTA"rejecting G2 collision- %4.2f farther than saberHitFraction %4.2f\n", (distFromStart/length), saberHitFraction ); } +#endif continue; } */ @@ -1143,6 +1231,7 @@ qboolean WP_SaberDamageEffects( trace_t *tr, const vec3_t start, float length, f } } + //FIXME: play less if damage is less? G_PlayEffect( hitEffect, coll.mCollisionPosition, coll.mCollisionNormal ); //Get the hit location based on surface name @@ -1205,10 +1294,12 @@ void WP_SaberKnockaway( gentity_t *attacker, trace_t *tr ) G_Sound( &g_entities[attacker->client->ps.saberEntityNum], G_SoundIndex( va( "sound/weapons/saber/saberblock%d.wav", Q_irand(1, 9) ) ) ); G_PlayEffect( "saber_block", tr->endpos ); saberHitFraction = tr->fraction; +#ifndef _FINAL_BUILD if ( d_saberCombat->integer ) { gi.Printf( S_COLOR_MAGENTA"WP_SaberKnockaway: saberHitFraction %4.2f\n", saberHitFraction ); } +#endif VectorCopy( tr->endpos, saberHitLocation ); saberHitEntity = tr->entityNum; g_saberFlashTime = level.time-50; @@ -1221,7 +1312,9 @@ void WP_SaberKnockaway( gentity_t *attacker, trace_t *tr ) #define SABER_COLLISION_DIST 6//was 2//was 4//was 8//was 16 extern qboolean InFront( vec3_t spot, vec3_t from, vec3_t fromAngles, float threshHold = 0.0f ); -qboolean WP_SaberDamageForTrace( int ignore, vec3_t start, vec3_t end, float dmg, vec3_t bladeDir, qboolean noGhoul, qboolean extrapolate = qtrue ) +qboolean WP_SaberDamageForTrace( int ignore, vec3_t start, vec3_t end, float dmg, + vec3_t bladeDir, qboolean noGhoul, int attackStrength, + qboolean extrapolate = qtrue ) { trace_t tr; vec3_t dir; @@ -1242,14 +1335,21 @@ qboolean WP_SaberDamageForTrace( int ignore, vec3_t start, vec3_t end, float dmg if ( !noGhoul ) { - if ( !attacker->s.number || (attacker->client&&(attacker->client->playerTeam==TEAM_PLAYER||attacker->client->NPC_class==CLASS_SHADOWTROOPER||attacker->client->NPC_class==CLASS_TAVION||attacker->client->NPC_class==CLASS_DESANN) ) ) + if ( !attacker->s.number || (attacker->client&&(attacker->client->playerTeam==TEAM_PLAYER||attacker->client->NPC_class==CLASS_SHADOWTROOPER||attacker->client->NPC_class==CLASS_TAVION||attacker->client->NPC_class==CLASS_DESANN) ) )//&&attackStrength==FORCE_LEVEL_3) {//player,. player allies, shadowtroopers, tavion and desann use larger traces vec3_t traceMins = {-2,-2,-2}, traceMaxs = {2,2,2}; - gi.trace( &tr, start, traceMins, traceMaxs, end2, ignore, mask, G2_COLLIDE, 10 ); + gi.trace( &tr, start, traceMins, traceMaxs, end2, ignore, mask, G2_COLLIDE, 10 );//G2_SUPERSIZEDBBOX } + /* + else if ( !attacker->s.number ) + { + vec3_t traceMins = {-1,-1,-1}, traceMaxs = {1,1,1}; + gi.trace( &tr, start, traceMins, traceMaxs, end2, ignore, mask, G2_COLLIDE, 10 );//G2_SUPERSIZEDBBOX + } + */ else {//reborn use smaller traces - gi.trace( &tr, start, NULL, NULL, end2, ignore, mask, G2_COLLIDE, 10 ); + gi.trace( &tr, start, NULL, NULL, end2, ignore, mask, G2_COLLIDE, 10 );//G2_SUPERSIZEDBBOX } } else @@ -1258,6 +1358,7 @@ qboolean WP_SaberDamageForTrace( int ignore, vec3_t start, vec3_t end, float dmg } +#ifndef _FINAL_BUILD if ( d_saberCombat->integer > 1 ) { if ( attacker != NULL && attacker->client != NULL ) @@ -1265,6 +1366,7 @@ qboolean WP_SaberDamageForTrace( int ignore, vec3_t start, vec3_t end, float dmg G_DebugLine(start, end2, FRAMETIME, WPDEBUG_SaberColor( attacker->client->ps.saberColor ), qtrue); } } +#endif if ( tr.entityNum == ENTITYNUM_NONE ) { @@ -1293,7 +1395,6 @@ qboolean WP_SaberDamageForTrace( int ignore, vec3_t start, vec3_t end, float dmg } //FIXME: take target FP_SABER_DEFENSE and attacker FP_SABER_OFFENSE into account here somehow? float sabersDist; -#if REALISTIC_SABER_COLLISION if ( attacker && attacker->client && attacker->client->ps.saberInFlight && owner && owner->s.number == 0 && (g_autoBlocking->integer||attacker->client->ps.saberBlockingTime>level.time) )//NPC flying saber hit player's saber bounding box {//players have g_autoBlocking, do the more generous check against flying sabers @@ -1305,17 +1406,21 @@ qboolean WP_SaberDamageForTrace( int ignore, vec3_t start, vec3_t end, float dmg else {//sabers must actually collide with the attacking saber sabersDist = WP_SabersDistance( attacker, owner ); +#ifndef _FINAL_BUILD if ( d_saberCombat->integer > 1 ) { gi.Printf( "sabersDist: %4.2f\n", sabersDist ); } +#endif//_FINAL_BUILD } -#else//REALISTIC_SABER_COLLISION - sabersDist = 0; -#endif//REALISTIC_SABER_COLLISION + if ( sabersCrossed == -1 || sabersCrossed > sabersDist ) + { + sabersCrossed = sabersDist; + } + qboolean sabersIntersect = WP_SabersIntersect( attacker, owner, qtrue ); if ( owner && owner->client && (attacker != NULL) && - (sabersDist > SABER_COLLISION_DIST || !InFront( attacker->currentOrigin, owner->currentOrigin, owner->client->ps.viewangles, 0.35f )) - && !WP_SabersIntersect( attacker, owner, qtrue ) )//was qtrue, but missed too much? + (sabersDist > SABER_COLLISION_DIST+4 )//|| !InFront( attacker->currentOrigin, owner->currentOrigin, owner->client->ps.viewangles, 0.35f )) + && !sabersIntersect )//was qtrue, but missed too much? {//swing came from behind and/or was not stopped by a lightsaber //re-try the trace without checking for lightsabers gi.trace ( &tr, start, NULL, NULL, end2, ignore, mask&~CONTENTS_LIGHTSABER, G2_NOCOLLIDE, 10 ); @@ -1326,63 +1431,150 @@ qboolean WP_SaberDamageForTrace( int ignore, vec3_t start, vec3_t end, float dmg if ( tr.entityNum == ENTITYNUM_NONE || &g_entities[tr.entityNum] == NULL ) {//didn't hit the owner /* - if ( && owner->NPC && !owner->client->ps.saberInFlight && owner->client->ps.saberBlocked > BLOCKED_ATK_BOUNCE ) - {//owner parried, just make sure they're saber is in the right spot - only does this if they're already parrying - if ( g_spskill->integer && (g_spskill->integer > 1 || Q_irand( 0, 1 ))) - {//if on easy, they don't cheat like this, if on medium, they cheat 50% of the time, if on hard, they always cheat - //FIXME: also take into account the owner's FP_DEFENSE? - if ( owner->NPC->rank >= RANK_LT_JG ) - {//lower-rank Jedi aren't as good blockers - Jedi_SaberBlockGo( owner, tr.endpos, NULL ); + if ( attacker + && attacker->client + && (PM_SaberInAttack( attacker->client->ps.saberMove ) || PM_SaberInStart( attacker->client->ps.saberMove )) + && DistanceSquared( tr.endpos, owner->currentOrigin ) < 10000 ) + { + if ( owner->NPC + && !owner->client->ps.saberInFlight + && owner->client->ps.saberBlocked != BLOCKED_PARRY_BROKEN + && !Jedi_SaberBusy( owner ) ) + {//owner parried, just make sure they're saber is in the right spot - only does this if they're not already doing something with saber + if ( g_spskill->integer && (g_spskill->integer > 1 || Q_irand( 0, 1 ))) + {//if on easy, they don't cheat like this, if on medium, they cheat 50% of the time, if on hard, they always cheat + //FIXME: also take into account the owner's FP_DEFENSE? + if ( Q_irand( 0, owner->NPC->rank ) >= RANK_CIVILIAN ) + {//lower-rank Jedi aren't as good blockers + vec3_t attDir; + VectorSubtract( end2, start, attDir ); + VectorNormalize( attDir ); + Jedi_SaberBlockGo( owner, start, attDir, NULL ); + } } } } */ return qfalse; // Exit, but we didn't hit the wall. } +#ifndef _FINAL_BUILD + if ( d_saberCombat->integer > 1 ) + { + if ( !attacker->s.number ) + { + gi.Printf( S_COLOR_MAGENTA"%d saber hit owner through saber %4.2f, dist = %4.2f\n", level.time, saberHitFraction, sabersDist ); + } + } +#endif//_FINAL_BUILD hitEnt = &g_entities[tr.entityNum]; owner = g_entities[tr.entityNum].owner; } else {//hit a lightsaber - if ( tr.fraction < saberHitFraction ) + if ( tr.fraction < saberHitFraction + && sabersDist < 16.0f + && sabersIntersect ) { // This saber hit closer than the last one. if ( (tr.allsolid || tr.startsolid) && owner && owner->client ) {//tr.fraction will be 0, unreliable... so calculate actual float dist = Distance( start, end2 ); if ( dist ) { - saberHitFraction = WP_SabersDistance( attacker, owner )/dist; - if ( saberHitFraction > 1.0f ) + float hitFrac = WP_SabersDistance( attacker, owner )/dist; + if ( hitFrac > 1.0f ) {//umm... minimum distance between sabers was longer than trace...? - saberHitFraction = 1.0f; + hitFrac = 1.0f; + } + if ( hitFrac < saberHitFraction ) + { + saberHitFraction = hitFrac; } } else { saberHitFraction = 0.0f; } +#ifndef _FINAL_BUILD + if ( d_saberCombat->integer > 1 ) + { + if ( !attacker->s.number ) + { + gi.Printf( S_COLOR_GREEN"%d saber hit saber dist %4.2f allsolid %4.2f\n", level.time, sabersDist, saberHitFraction ); + } + } +#endif//_FINAL_BUILD } else { - saberHitFraction = tr.fraction; +#ifndef _FINAL_BUILD + if ( d_saberCombat->integer > 1 ) + { + if ( !attacker->s.number ) + { + gi.Printf( S_COLOR_BLUE"%d saber hit saber dist %4.2f, frac %4.2f\n", level.time, sabersDist, saberHitFraction ); + } + saberHitFraction = tr.fraction; + } +#endif//_FINAL_BUILD } +#ifndef _FINAL_BUILD if ( d_saberCombat->integer ) { gi.Printf( S_COLOR_MAGENTA"hit saber: saberHitFraction %4.2f, allsolid %d, startsolid %d\n", saberHitFraction, tr.allsolid, tr.startsolid ); } +#endif//_FINAL_BUILD VectorCopy(tr.endpos, saberHitLocation); saberHitEntity = tr.entityNum; } + /* + if ( owner + && owner->client + && attacker + && attacker->client + && (PM_SaberInAttack( attacker->client->ps.saberMove ) || PM_SaberInStart( attacker->client->ps.saberMove )) + && DistanceSquared( tr.endpos, owner->currentOrigin ) < 10000 ) + { + if ( owner->NPC + && !owner->client->ps.saberInFlight + && owner->client->ps.saberBlocked != BLOCKED_PARRY_BROKEN + && !Jedi_SaberBusy( owner ) ) + {//owner parried, just make sure they're saber is in the right spot - only does this if they're not already doing something with saber + if ( g_spskill->integer && (g_spskill->integer > 1 || Q_irand( 0, 1 ))) + {//if on easy, they don't cheat like this, if on medium, they cheat 50% of the time, if on hard, they always cheat + //FIXME: also take into account the owner's FP_DEFENSE? + if ( Q_irand( 0, owner->NPC->rank ) >= RANK_CIVILIAN ) + {//lower-rank Jedi aren't as good blockers + vec3_t attDir; + VectorSubtract( end2, start, attDir ); + VectorNormalize( attDir ); + Jedi_SaberBlockGo( owner, start, attDir, NULL ); + } + } + } + } + */ return qfalse; // Exit, but we didn't hit the wall. } } + else + { +#ifndef _FINAL_BUILD + if ( d_saberCombat->integer > 1 ) + { + if ( !attacker->s.number ) + { + gi.Printf( S_COLOR_RED"%d saber hit owner directly %4.2f\n", level.time, saberHitFraction ); + } + } +#endif//_FINAL_BUILD + } if ( attacker && attacker->client && attacker->client->ps.saberInFlight ) {//thrown saber hit something - if ( ( hitEnt && hitEnt->client && hitEnt->health > 0 && ( hitEnt->client->NPC_class == CLASS_DESANN || hitEnt->client->NPC_class == CLASS_LUKE ) ) || - ( owner && owner->client && owner->health > 0 && ( owner->client->NPC_class == CLASS_DESANN || owner->client->NPC_class == CLASS_LUKE ) ) ) + if ( ( hitEnt && hitEnt->client && hitEnt->health > 0 && ( hitEnt->client->NPC_class == CLASS_DESANN || hitEnt->client->NPC_class == CLASS_LUKE || (hitEnt->client->NPC_class == CLASS_GALAKMECH&&hitEnt->client->ps.powerups[PW_GALAK_SHIELD] > 0) ) ) || + ( owner && owner->client && owner->health > 0 && ( owner->client->NPC_class == CLASS_DESANN || owner->client->NPC_class == CLASS_LUKE || (owner->client->NPC_class==CLASS_GALAKMECH&&owner->client->ps.powerups[PW_GALAK_SHIELD] > 0) ) ) ) {//Luke and Desann slap thrown sabers aside + //FIXME: control the direction of the thrown saber... if hit Galak's shield, bounce directly away from his origin? WP_SaberKnockaway( attacker, &tr ); if ( hitEnt->client ) { @@ -1410,6 +1602,7 @@ qboolean WP_SaberDamageForTrace( int ignore, vec3_t start, vec3_t end, float dmg { dmg *= len; } +#ifndef _FINAL_BUILD if ( d_saberCombat->integer > 1 ) { if ( !(hitEnt->contents & CONTENTS_LIGHTSABER) ) @@ -1417,6 +1610,7 @@ qboolean WP_SaberDamageForTrace( int ignore, vec3_t start, vec3_t end, float dmg gi.Printf( S_COLOR_GREEN"Hit ent, but no ghoul collisions\n" ); } } +#endif float trFrac, dmgFrac; if ( tr.allsolid ) {//totally inside them @@ -1565,19 +1759,23 @@ qboolean WP_SabersCheckLock2( gentity_t *attacker, gentity_t *defender, sabersLo anim = &level.knownAnimFileSets[attacker->client->clientInfo.animFileIndex].animations[attAnim]; advance = floor( anim->numFrames*attStart ); PM_SetAnimFrame( attacker, anim->firstFrame + advance, qtrue, qtrue ); +#ifndef _FINAL_BUILD if ( d_saberCombat->integer ) { Com_Printf( "%s starting saber lock, anim = %s, %d frames to go!\n", attacker->NPC_type, animTable[attAnim].name, anim->numFrames-advance ); } +#endif } if( ValidAnimFileIndex( defender->client->clientInfo.animFileIndex ) ) { anim = &level.knownAnimFileSets[defender->client->clientInfo.animFileIndex].animations[defAnim]; PM_SetAnimFrame( defender, anim->firstFrame + advance, qtrue, qtrue );//was anim->firstFrame + anim->numFrames - advance, but that's wrong since they are matched anims +#ifndef _FINAL_BUILD if ( d_saberCombat->integer ) { Com_Printf( "%s starting saber lock, anim = %s, %d frames to go!\n", defender->NPC_type, animTable[defAnim].name, advance ); } +#endif } VectorClear( attacker->client->ps.velocity ); VectorClear( defender->client->ps.velocity ); @@ -1589,14 +1787,38 @@ qboolean WP_SabersCheckLock2( gentity_t *attacker, gentity_t *defender, sabersLo //MATCH ANGLES //FIXME: if zDiff in elevation, make lower look up and upper look down and move them closer? + float defPitchAdd = 0, zDiff = ((attacker->currentOrigin[2]+attacker->client->standheight)-(defender->currentOrigin[2]+defender->client->standheight)); + if ( zDiff > 24 ) + { + defPitchAdd = -30; + } + else if ( zDiff < -24 ) + { + defPitchAdd = 30; + } + else + { + defPitchAdd = zDiff/24.0f*-30.0f; + } if ( attacker->NPC && defender->NPC ) {//if 2 NPCs, just set pitch to 0 - attacker->client->ps.viewangles[PITCH] = 0; + attacker->client->ps.viewangles[PITCH] = -defPitchAdd; + defender->client->ps.viewangles[PITCH] = defPitchAdd; } else {//if a player is involved, clamp player's pitch and match NPC's to player if ( !attacker->s.number ) { + //clamp to defPitch + if ( attacker->client->ps.viewangles[PITCH] > -defPitchAdd + 10 ) + { + attacker->client->ps.viewangles[PITCH] = -defPitchAdd + 10; + } + else if ( attacker->client->ps.viewangles[PITCH] < -defPitchAdd-10 ) + { + attacker->client->ps.viewangles[PITCH] = -defPitchAdd-10; + } + //clamp to sane numbers if ( attacker->client->ps.viewangles[PITCH] > 50 ) { attacker->client->ps.viewangles[PITCH] = 50; @@ -1605,9 +1827,21 @@ qboolean WP_SabersCheckLock2( gentity_t *attacker, gentity_t *defender, sabersLo { attacker->client->ps.viewangles[PITCH] = -50; } + defender->client->ps.viewangles[PITCH] = attacker->client->ps.viewangles[PITCH]*-1; + defPitchAdd = defender->client->ps.viewangles[PITCH]; } else if ( !defender->s.number ) { + //clamp to defPitch + if ( defender->client->ps.viewangles[PITCH] > defPitchAdd + 10 ) + { + defender->client->ps.viewangles[PITCH] = defPitchAdd + 10; + } + else if ( defender->client->ps.viewangles[PITCH] < defPitchAdd-10 ) + { + defender->client->ps.viewangles[PITCH] = defPitchAdd-10; + } + //clamp to sane numbers if ( defender->client->ps.viewangles[PITCH] > 50 ) { defender->client->ps.viewangles[PITCH] = 50; @@ -1616,6 +1850,7 @@ qboolean WP_SabersCheckLock2( gentity_t *attacker, gentity_t *defender, sabersLo { defender->client->ps.viewangles[PITCH] = -50; } + defPitchAdd = defender->client->ps.viewangles[PITCH]; attacker->client->ps.viewangles[PITCH] = defender->client->ps.viewangles[PITCH]*-1; } } @@ -1631,6 +1866,20 @@ qboolean WP_SabersCheckLock2( gentity_t *attacker, gentity_t *defender, sabersLo //MATCH POSITIONS vec3_t newOrg; + /* + idealDist -= fabs(defPitchAdd)/8.0f; + */ + float scale = VectorLength( attacker->s.modelScale ); + if ( scale ) + { + idealDist += 8*(scale-1.0f); + } + scale = VectorLength( defender->s.modelScale ); + if ( scale ) + { + idealDist += 8*(scale-1.0f); + } + float diff = VectorNormalize( defDir ) - idealDist;//diff will be the total error in dist //try to move attacker half the diff towards the defender VectorMA( attacker->currentOrigin, diff*0.5f, defDir, newOrg ); @@ -2101,7 +2350,6 @@ void WP_SaberDamageTrace( gentity_t *ent ) void WP_SaberDamageTrace( gentity_t *ent ) { vec3_t mp1, mp2, md1, md2, baseOld, baseNew, baseDiff, endOld, endNew, bladePointOld, bladePointNew; - float aveLength, step, stepsize = 8; float tipDmgMod = 1.0f; float baseDamage; int baseDFlags = 0; @@ -2163,16 +2411,6 @@ void WP_SaberDamageTrace( gentity_t *ent ) } } - if ( ent->client->ps.saberEventFlags&SEF_INWATER ) - { - if ( !Q_irand( 0, 10 ) ) - { - vec3_t end, normal = {0,0,1}; - VectorMA( ent->client->renderInfo.muzzlePoint, ent->client->ps.saberLength, ent->client->renderInfo.muzzleDir, end ); - G_PlayEffect( "saber/boil", end, normal ); - G_Sound( &g_entities[ent->client->ps.saberEntityNum], G_SoundIndex( "sound/weapons/saber/boiling.wav" ) ); - } - } //FIXMEFIXMEFIXME: When in force speed (esp. lvl 3), need to interpolate this because // we animate so much faster that the arc is pretty much flat... @@ -2249,6 +2487,21 @@ void WP_SaberDamageTrace( gentity_t *ent ) //FIXME: more damage for higher attack power levels? // More damage based on length/color of saber? //FIXME: Desann does double damage? + /* + switch ( entPowerLevel ) + { + case FORCE_LEVEL_3: + baseDamage = 5.0f; + break; + case FORCE_LEVEL_2: + baseDamage = 2.0f; + break; + default: + case FORCE_LEVEL_1: + baseDamage = 1.0f; + break; + } + */ baseDamage = 2.5f * (float)entPowerLevel; } else @@ -2256,6 +2509,21 @@ void WP_SaberDamageTrace( gentity_t *ent ) //FIXME: strong attacks and returns should do damage and be unblockable if ( g_timescale->value < 1.0 ) {//in slow mo or force speed, we need to do damage during the transitions + /* + switch ( entPowerLevel ) + { + case FORCE_LEVEL_3: + baseDamage = 5.0f; + break; + case FORCE_LEVEL_2: + baseDamage = 2.0f; + break; + default: + case FORCE_LEVEL_1: + baseDamage = 1.0f; + break; + } + */ baseDamage = 2.5f * (float)entPowerLevel; } else// if ( !ent->s.number ) @@ -2297,8 +2565,8 @@ void WP_SaberDamageTrace( gentity_t *ent ) gentity_t *traceEnt = &g_entities[trace.entityNum]; if ( traceEnt && traceEnt->client && traceEnt->health > 0 && traceEnt->client->playerTeam != ent->client->playerTeam ) {//enemy client, push them away - if ( !traceEnt->client->ps.saberLockTime ) - { + if ( !traceEnt->client->ps.saberLockTime && !traceEnt->message ) + {//don't push people in saberlock or with security keys vec3_t hitDir; VectorSubtract( trace.endpos, ent->currentOrigin, hitDir ); float totalDist = Distance( mp1, ent->currentOrigin ); @@ -2309,10 +2577,12 @@ void WP_SaberDamageTrace( gentity_t *ent ) VectorMA( traceEnt->client->ps.velocity, knockback, hitDir, traceEnt->client->ps.velocity ); traceEnt->client->ps.pm_time = 200; traceEnt->client->ps.pm_flags |= PMF_TIME_NOFRICTION; +#ifndef _FINAL_BUILD if ( d_saberCombat->integer ) { gi.Printf( "%s pushing away %s at %s\n", ent->NPC_type, traceEnt->NPC_type, vtos( traceEnt->client->ps.velocity ) ); } +#endif } } } @@ -2334,14 +2604,16 @@ void WP_SaberDamageTrace( gentity_t *ent ) VectorCopy( mp2, baseNew ); VectorMA( baseNew, ent->client->ps.saberLength, md2, endNew ); + sabersCrossed = -1; if ( VectorCompare2( baseOld, baseNew ) && VectorCompare2( endOld, endNew ) ) { - hit_wall = WP_SaberDamageForTrace( ent->s.number, mp2, endNew, baseDamage*4, md2, qfalse, qfalse ); + hit_wall = WP_SaberDamageForTrace( ent->s.number, mp2, endNew, baseDamage*4, md2, qfalse, entPowerLevel, qfalse ); } else { + float aveLength, step = 8, stepsize = 8; //do the trace at the base first - hit_wall = WP_SaberDamageForTrace( ent->s.number, baseOld, baseNew, baseDamage, md2, qfalse ); + hit_wall = WP_SaberDamageForTrace( ent->s.number, baseOld, baseNew, baseDamage, md2, qfalse, entPowerLevel ); //if hit a saber, shorten rest of traces to match if ( saberHitFraction < 1.0 ) @@ -2384,7 +2656,7 @@ void WP_SaberDamageTrace( gentity_t *ent ) { VectorMA( baseOld, step, curMD1, bladePointOld ); VectorMA( baseNew, step, curMD2, bladePointNew ); - if ( WP_SaberDamageForTrace( ent->s.number, bladePointOld, bladePointNew, baseDamage, curMD2, qfalse ) ) + if ( WP_SaberDamageForTrace( ent->s.number, bladePointOld, bladePointNew, baseDamage, curMD2, qfalse, entPowerLevel ) ) { hit_wall = qtrue; } @@ -2432,13 +2704,13 @@ void WP_SaberDamageTrace( gentity_t *ent ) tipDmgMod = (stepsize-(step-aveLength))/stepsize; } //NOTE: since this is the tip, we do not extrapolate the extra 16 - if ( WP_SaberDamageForTrace( ent->s.number, endOld, endNew, tipDmgMod*baseDamage, md2, qfalse, qfalse ) ) + if ( WP_SaberDamageForTrace( ent->s.number, endOld, endNew, tipDmgMod*baseDamage, md2, qfalse, entPowerLevel, qfalse ) ) { hit_wall = qtrue; } } - if ( saberHitFraction < 1.0 && (ent->client->ps.weaponstate == WEAPON_FIRING || ent->client->ps.saberInFlight) ) + if ( (saberHitFraction < 1.0f||(sabersCrossed>=0&&sabersCrossed<=32.0f)) && (ent->client->ps.weaponstate == WEAPON_FIRING || ent->client->ps.saberInFlight) ) {// The saber (in-hand) hit another saber, mano. qboolean inFlightSaberBlocked = qfalse; qboolean collisionResolved = qfalse; @@ -2556,8 +2828,8 @@ void WP_SaberDamageTrace( gentity_t *ent ) { collisionResolved = qtrue; } - else - { + else if ( saberHitFraction < 1.0f ) + {//an actual collision if ( entPowerLevel < FORCE_LEVEL_3 ) {//strong attacks cannot be deflected //based on angle of attack & angle of defensive saber, see if I should deflect off in another dir rather than bounce back @@ -2591,10 +2863,12 @@ void WP_SaberDamageTrace( gentity_t *ent ) } //just so Jedi knows that he was blocked ent->client->ps.saberEventFlags |= SEF_BLOCKED; +#ifndef _FINAL_BUILD if ( d_saberCombat->integer ) { gi.Printf( S_COLOR_RED"%s knockaway %s's attack, new move = %s, anim = %s\n", hitOwner->NPC_type, ent->NPC_type, saberMoveData[ent->client->ps.saberBounceMove].name, animTable[saberMoveData[ent->client->ps.saberBounceMove].animToUse].name ); } +#endif } else if ( entPowerLevel > FORCE_LEVEL_2 || (!deflected && Q_irand( 0, PM_PowerLevelForSaberAnim( &ent->client->ps ) - hitOwner->client->ps.forcePowerLevel[FP_SABER_DEFENSE]/*PM_PowerLevelForSaberAnim( &hitOwner->client->ps )*/ ) > 0 ) ) {//broke their parry altogether @@ -2637,6 +2911,7 @@ void WP_SaberDamageTrace( gentity_t *ent ) } } } +#ifndef _FINAL_BUILD if ( d_saberCombat->integer ) { if ( ent->client->ps.saberEventFlags&SEF_BLOCKED ) @@ -2648,6 +2923,7 @@ void WP_SaberDamageTrace( gentity_t *ent ) gi.Printf( S_COLOR_RED"%s parry broken (follow-through)!\n", hitOwner->targetname ); } } +#endif } else { @@ -2693,6 +2969,7 @@ void WP_SaberDamageTrace( gentity_t *ent ) } //do some time-consuming saber-knocked-aside broken parry anim ent->client->ps.saberBlocked = BLOCKED_PARRY_BROKEN; +#ifndef _FINAL_BUILD if ( d_saberCombat->integer ) { if ( hitOwner->client->ps.saberEventFlags&SEF_BLOCKED ) @@ -2704,6 +2981,7 @@ void WP_SaberDamageTrace( gentity_t *ent ) gi.Printf( S_COLOR_RED"%s parry broken (follow-through)!\n", ent->targetname ); } } +#endif } else { @@ -2721,160 +2999,182 @@ void WP_SaberDamageTrace( gentity_t *ent ) {//some kind of in-flight collision } - if ( !collisionResolved && baseDamage ) - {//some other kind of in-hand saber collision - //handle my reaction - if ( !ent->client->ps.saberInFlight - && ent->client->ps.saberLockTime < level.time ) - {//my saber is in hand - if ( ent->client->ps.saberBlocked != BLOCKED_PARRY_BROKEN ) - { - if ( PM_SaberInAttack( ent->client->ps.saberMove ) || PM_SaberInSpecialAttack( ent->client->ps.torsoAnim ) || - (entPowerLevel > FORCE_LEVEL_2&&!PM_SaberInIdle(ent->client->ps.saberMove)&&!PM_SaberInParry(ent->client->ps.saberMove)&&!PM_SaberInReflect(ent->client->ps.saberMove)) ) - {//in the middle of attacking - if ( entPowerLevel < FORCE_LEVEL_3 && hitOwner->health > 0 ) - {//don't deflect/bounce in strong attack or when enemy is dead - WP_GetSaberDeflectionAngle( ent, hitOwner ); - ent->client->ps.saberEventFlags |= SEF_BLOCKED; + if ( saberHitFraction < 1.0f ) + { + if ( !collisionResolved && baseDamage ) + {//some other kind of in-hand saber collision + //handle my reaction + if ( !ent->client->ps.saberInFlight + && ent->client->ps.saberLockTime < level.time ) + {//my saber is in hand + if ( ent->client->ps.saberBlocked != BLOCKED_PARRY_BROKEN ) + { + if ( PM_SaberInAttack( ent->client->ps.saberMove ) || PM_SaberInSpecialAttack( ent->client->ps.torsoAnim ) || + (entPowerLevel > FORCE_LEVEL_2&&!PM_SaberInIdle(ent->client->ps.saberMove)&&!PM_SaberInParry(ent->client->ps.saberMove)&&!PM_SaberInReflect(ent->client->ps.saberMove)) ) + {//in the middle of attacking + if ( entPowerLevel < FORCE_LEVEL_3 && hitOwner->health > 0 ) + {//don't deflect/bounce in strong attack or when enemy is dead + WP_GetSaberDeflectionAngle( ent, hitOwner ); + ent->client->ps.saberEventFlags |= SEF_BLOCKED; + //since it was blocked/deflected, take away any damage done + //FIXME: what if the damage was done before the parry? + WP_SaberClearDamageForEntNum( hitOwner->s.number ); + } + } + else + {//saber collided when not attacking, parry it + //since it was blocked/deflected, take away any damage done + //FIXME: what if the damage was done before the parry? + WP_SaberClearDamageForEntNum( hitOwner->s.number ); + /* + if ( ent->s.number || g_autoBlocking->integer || ent->client->ps.saberBlockingTime > level.time ) + {//either an NPC or a player who has blocking + if ( !PM_SaberInTransitionAny( ent->client->ps.saberMove ) && !PM_SaberInBounce( ent->client->ps.saberMove ) ) + {//I'm not attacking, in transition or in a bounce, so play a parry + //just so Jedi knows that he parried something + WP_SaberBlockNonRandom( ent, saberHitLocation, qfalse ); + } + ent->client->ps.saberEventFlags |= SEF_PARRIED; + } + */ } } else - {//saber collided when not attacking, parry it + { + //since it was blocked/deflected, take away any damage done + //FIXME: what if the damage was done before the parry? + WP_SaberClearDamageForEntNum( hitOwner->s.number ); + } + } + else + {//nothing happens to *me* when my inFlight saber hits something + } + //handle their reaction + if ( hitOwner + && hitOwner->health > 0 + && hitOwner->client + && !hitOwner->client->ps.saberInFlight + && hitOwner->client->ps.saberLockTime < level.time ) + {//their saber is in hand + if ( PM_SaberInAttack( hitOwner->client->ps.saberMove ) || PM_SaberInSpecialAttack( hitOwner->client->ps.torsoAnim ) || + (hitOwner->client->ps.saberAnimLevel > FORCE_LEVEL_2&&!PM_SaberInIdle(hitOwner->client->ps.saberMove)&&!PM_SaberInParry(hitOwner->client->ps.saberMove)&&!PM_SaberInReflect(hitOwner->client->ps.saberMove)) ) + {//in the middle of attacking /* - if ( ent->s.number || g_autoBlocking->integer || ent->client->ps.saberBlockingTime > level.time ) - {//either an NPC or a player who has blocking - if ( !PM_SaberInTransitionAny( ent->client->ps.saberMove ) && !PM_SaberInBounce( ent->client->ps.saberMove ) ) - {//I'm not attacking, in transition or in a bounce, so play a parry - //just so Jedi knows that he parried something - WP_SaberBlockNonRandom( ent, saberHitLocation, qfalse ); - } - ent->client->ps.saberEventFlags |= SEF_PARRIED; + if ( hitOwner->client->ps.saberAnimLevel < FORCE_LEVEL_3 ) + {//don't deflect/bounce in strong attack + WP_GetSaberDeflectionAngle( hitOwner, ent ); + hitOwner->client->ps.saberEventFlags |= SEF_BLOCKED; } */ } - } - } - else - {//nothing happens to *me* when my inFlight saber hits something - } - //handle their reaction - if ( hitOwner - && hitOwner->health > 0 - && hitOwner->client - && !hitOwner->client->ps.saberInFlight - && hitOwner->client->ps.saberLockTime < level.time ) - {//their saber is in hand - if ( PM_SaberInAttack( hitOwner->client->ps.saberMove ) || PM_SaberInSpecialAttack( hitOwner->client->ps.torsoAnim ) || - (hitOwner->client->ps.saberAnimLevel > FORCE_LEVEL_2&&!PM_SaberInIdle(hitOwner->client->ps.saberMove)&&!PM_SaberInParry(hitOwner->client->ps.saberMove)&&!PM_SaberInReflect(hitOwner->client->ps.saberMove)) ) - {//in the middle of attacking - /* - if ( hitOwner->client->ps.saberAnimLevel < FORCE_LEVEL_3 ) - {//don't deflect/bounce in strong attack - WP_GetSaberDeflectionAngle( hitOwner, ent ); - hitOwner->client->ps.saberEventFlags |= SEF_BLOCKED; - } - */ - } - else - {//saber collided when not attacking, parry it - if ( !PM_SaberInBrokenParry( hitOwner->client->ps.saberMove ) ) - {//not currently in a broken parry - if ( !WP_SaberParry( hitOwner, ent ) ) - {//FIXME: hitOwner can't parry, do some time-consuming saber-knocked-aside broken parry anim? - //hitOwner->client->ps.saberBlocked = BLOCKED_PARRY_BROKEN; + else + {//saber collided when not attacking, parry it + if ( !PM_SaberInBrokenParry( hitOwner->client->ps.saberMove ) ) + {//not currently in a broken parry + if ( !WP_SaberParry( hitOwner, ent ) ) + {//FIXME: hitOwner can't parry, do some time-consuming saber-knocked-aside broken parry anim? + //hitOwner->client->ps.saberBlocked = BLOCKED_PARRY_BROKEN; + } } } } + else + {//nothing happens to *hitOwner* when their inFlight saber hits something + } } - else - {//nothing happens to *hitOwner* when their inFlight saber hits something + + //collision must have been handled by now + //Set the blocked attack bounce value in saberBlocked so we actually play our saberBounceMove anim + if ( ent->client->ps.saberEventFlags & SEF_BLOCKED ) + { + if ( ent->client->ps.saberBlocked != BLOCKED_PARRY_BROKEN ) + { + ent->client->ps.saberBlocked = BLOCKED_ATK_BOUNCE; + } } + /* + if ( hitOwner && hitOwner->client->ps.saberEventFlags & SEF_BLOCKED ) + { + hitOwner->client->ps.saberBlocked = BLOCKED_ATK_BOUNCE; + } + */ } - //collision must have been handled by now - //Set the blocked attack bounce value in saberBlocked so we actually play our saberBounceMove anim - if ( ent->client->ps.saberEventFlags & SEF_BLOCKED ) - { - if ( ent->client->ps.saberBlocked != BLOCKED_PARRY_BROKEN ) + if ( saberHitFraction < 1.0f || collisionResolved ) + {//either actually hit or locked + if ( ent->client->ps.saberLockTime < level.time ) { - ent->client->ps.saberBlocked = BLOCKED_ATK_BOUNCE; + if ( inFlightSaberBlocked ) + {//FIXME: never hear this sound + G_Sound( &g_entities[ent->client->ps.saberEntityNum], G_SoundIndex( va( "sound/weapons/saber/saberbounce%d.wav", Q_irand(1,3) ) ) ); + } + else + { + if ( deflected ) + { + G_Sound( ent, G_SoundIndex( va( "sound/weapons/saber/saberbounce%d.wav", Q_irand(1,3) ) ) ); + } + else + { + G_Sound( ent, G_SoundIndex( va( "sound/weapons/saber/saberblock%d.wav", Q_irand(1, 9) ) ) ); + } + } + G_PlayEffect( "saber_block", saberHitLocation, saberHitNormal ); } + // Set the little screen flash - only when an attack is blocked + g_saberFlashTime = level.time-50; + VectorCopy( saberHitLocation, g_saberFlashPos ); } - /* - if ( hitOwner && hitOwner->client->ps.saberEventFlags & SEF_BLOCKED ) - { - hitOwner->client->ps.saberBlocked = BLOCKED_ATK_BOUNCE; - } - */ - if ( ent->client->ps.saberLockTime < level.time ) + + if ( saberHitFraction < 1.0f ) { if ( inFlightSaberBlocked ) - {//FIXME: never hear this sound - G_Sound( &g_entities[ent->client->ps.saberEntityNum], G_SoundIndex( va( "sound/weapons/saber/saberbounce%d.wav", Q_irand(1,3) ) ) ); - } - else - { - if ( deflected ) - { - G_Sound( ent, G_SoundIndex( va( "sound/weapons/saber/saberbounce%d.wav", Q_irand(1,3) ) ) ); + {//we threw a saber and it was blocked, do any effects, etc. + int knockAway = 5; + if ( hitEnt + && hitOwner + && hitOwner->client + && (PM_SaberInAttack( hitOwner->client->ps.saberMove ) || PM_SaberInSpecialAttack( hitOwner->client->ps.torsoAnim ) || PM_SpinningSaberAnim( hitOwner->client->ps.torsoAnim )) ) + {//if hit someone who was in an attack or spin anim, more likely to have in-flight saber knocked away + if ( hitOwnerPowerLevel > FORCE_LEVEL_2 ) + {//string attacks almost always knock it aside! + knockAway = 1; + } + else + {//33% chance + knockAway = 2; + } + } + if ( !Q_irand( 0, knockAway ) || //random + ( hitOwner && hitOwner->client && + (hitOwner->client->NPC_class==CLASS_DESANN||hitOwner->client->NPC_class==CLASS_TAVION||hitOwner->client->NPC_class==CLASS_LUKE) + ) //or if blocked by a Boss character FIXME: or base on defense level? + )//FIXME: player should not auto-block a flying saber, let him override the parry with an attack to knock the saber from the air, rather than this random chance + {//knock it aside and turn it off + G_PlayEffect( "saber_cut", saberHitLocation, saberHitNormal ); + if ( hitEnt ) + { + vec3_t newDir; + + VectorSubtract( g_entities[ent->client->ps.saberEntityNum].currentOrigin, hitEnt->currentOrigin, newDir ); + VectorNormalize( newDir ); + G_ReflectMissile( ent, &g_entities[ent->client->ps.saberEntityNum], newDir ); + } + Jedi_PlayDeflectSound( hitOwner ); + WP_SaberDrop( ent, &g_entities[ent->client->ps.saberEntityNum] ); } else { - G_Sound( ent, G_SoundIndex( va( "sound/weapons/saber/saberblock%d.wav", Q_irand(1, 9) ) ) ); + if ( !Q_irand( 0, 2 ) && hitEnt ) + { + vec3_t newDir; + VectorSubtract( g_entities[ent->client->ps.saberEntityNum].currentOrigin, hitEnt->currentOrigin, newDir ); + VectorNormalize( newDir ); + G_ReflectMissile( ent, &g_entities[ent->client->ps.saberEntityNum], newDir ); + } + WP_SaberReturn( ent, &g_entities[ent->client->ps.saberEntityNum] ); } } - G_PlayEffect( "saber_block", saberHitLocation, saberHitNormal ); - } - // Set the little screen flash - only when an attack is blocked - g_saberFlashTime = level.time-50; - VectorCopy( saberHitLocation, g_saberFlashPos ); - - if ( inFlightSaberBlocked ) - {//we threw a saber and it was blocked, do any effects, etc. - int knockAway = 5; - if ( hitEnt - && hitOwner - && hitOwner->client - && (PM_SaberInAttack( hitOwner->client->ps.saberMove ) || PM_SaberInSpecialAttack( hitOwner->client->ps.torsoAnim ) || PM_SpinningSaberAnim( hitOwner->client->ps.torsoAnim )) ) - {//if hit someone who was in an attack or spin anim, more likely to have in-flight saber knocked away - if ( hitOwnerPowerLevel > FORCE_LEVEL_2 ) - {//string attacks almost always knock it aside! - knockAway = 1; - } - else - {//33% chance - knockAway = 2; - } - } - if ( !Q_irand( 0, knockAway ) || //random - ( hitOwner && hitOwner->client && - (hitOwner->client->NPC_class==CLASS_DESANN||hitOwner->client->NPC_class==CLASS_TAVION||hitOwner->client->NPC_class==CLASS_LUKE) - ) //or if blocked by a Boss character FIXME: or base on defense level? - )//FIXME: player should not auto-block a flying saber, let him override the parry with an attack to knock the saber from the air, rather than this random chance - {//knock it aside and turn it off - G_PlayEffect( "saber_cut", saberHitLocation, saberHitNormal ); - if ( hitEnt ) - { - vec3_t newDir; - - VectorSubtract( g_entities[ent->client->ps.saberEntityNum].currentOrigin, hitEnt->currentOrigin, newDir ); - VectorNormalize( newDir ); - G_ReflectMissile( ent, &g_entities[ent->client->ps.saberEntityNum], newDir ); - } - Jedi_PlayDeflectSound( hitOwner ); - WP_SaberDrop( ent, &g_entities[ent->client->ps.saberEntityNum] ); - } - else - { - if ( !Q_irand( 0, 2 ) && hitEnt ) - { - vec3_t newDir; - VectorSubtract( g_entities[ent->client->ps.saberEntityNum].currentOrigin, hitEnt->currentOrigin, newDir ); - VectorNormalize( newDir ); - G_ReflectMissile( ent, &g_entities[ent->client->ps.saberEntityNum], newDir ); - } - WP_SaberReturn( ent, &g_entities[ent->client->ps.saberEntityNum] ); - } } } @@ -2900,11 +3200,13 @@ void WP_SaberDamageTrace( gentity_t *ent ) if ( WP_SaberApplyDamage( ent, baseDamage, baseDFlags, brokenParry ) ) {//actually did damage to something +#ifndef _FINAL_BUILD if ( d_saberCombat->integer ) { gi.Printf( "base damage was %4.2f\n", baseDamage ); } - G_Sound( ent, G_SoundIndex( "sound/weapons/saber/saberhit.wav" ) ); +#endif + G_Sound( ent, G_SoundIndex( va( "sound/weapons/saber/saberhit%d.wav", Q_irand( 1, 3 ) ) ) ); } if ( hit_wall ) @@ -3062,7 +3364,8 @@ void WP_SaberImpact( gentity_t *owner, gentity_t *saber, trace_t *trace ) saber->aimDebounceTime = level.time; } } - else if ( other->client && other->health > 0 && ( other->client->NPC_class == CLASS_DESANN || other->client->NPC_class == CLASS_TAVION || other->client->NPC_class == CLASS_LUKE ) ) + else if ( other->client && other->health > 0 + && ( other->client->NPC_class == CLASS_DESANN || other->client->NPC_class == CLASS_TAVION || other->client->NPC_class == CLASS_LUKE || ( other->client->NPC_class == CLASS_GALAKMECH && other->client->ps.powerups[PW_GALAK_SHIELD] > 0 ) ) ) {//Luke, Desann and Tavion slap thrown sabers aside WP_SaberDrop( owner, saber ); G_Sound( saber, G_SoundIndex( va( "sound/weapons/saber/saberblock%d.wav", Q_irand(1, 9) ) ) ); @@ -3089,7 +3392,6 @@ void WP_SaberInFlightReflectCheck( gentity_t *self, usercmd_t *ucmd ) vec3_t tip; vec3_t up = {0,0,1}; - if ( self->NPC && (self->NPC->scriptFlags&SCF_IGNORE_ALERTS) ) {//don't react to things flying at me... return; @@ -3394,21 +3696,36 @@ void WP_RunSaber( gentity_t *self, gentity_t *saber ) vec3_t forward, saberHome, saberDest, fwdangles = {0}; VectorCopy( self->client->ps.viewangles, fwdangles ); - fwdangles[0] -= 10; + if ( self->s.number ) + { + fwdangles[0] -= 8; + } + else if ( cg.renderingThirdPerson ) + { + fwdangles[0] -= 5; + } - if ( self->client->ps.forcePowerLevel[FP_SABERTHROW] > 1 || self->client->ps.saberEntityState == SES_RETURNING || VectorCompare( saber->s.pos.trDelta, vec3_origin ) ) - {//control + if ( self->client->ps.forcePowerLevel[FP_SABERTHROW] > FORCE_LEVEL_1 + || self->client->ps.saberEntityState == SES_RETURNING + || VectorCompare( saber->s.pos.trDelta, vec3_origin ) ) + {//control if it's returning or just starting float saberSpeed = 500;//FIXME: based on force level? float dist; gentity_t *enemy = NULL; AngleVectors( fwdangles, forward, NULL, NULL ); - //make the saber head to my hand- the bolt it was attached to - VectorCopy( self->client->renderInfo.handRPoint, saberHome ); + if ( self->client->ps.saberEntityDist < 100 ) + {//make the saber head to my hand- the bolt it was attached to + VectorCopy( self->client->renderInfo.handRPoint, saberHome ); + } + else + {//aim saber from eyes + VectorCopy( self->client->renderInfo.eyePoint, saberHome ); + } VectorMA( saberHome, self->client->ps.saberEntityDist, forward, saberDest ); - if ( self->client->ps.forcePowerLevel[FP_SABERTHROW] > 2 && self->client->ps.saberEntityState == SES_LEAVING ) + if ( self->client->ps.forcePowerLevel[FP_SABERTHROW] > FORCE_LEVEL_2 && self->client->ps.saberEntityState == SES_LEAVING ) {//max level //pick an enemy enemy = WP_SaberFindEnemy( self, saber ); @@ -3432,6 +3749,10 @@ void WP_RunSaber( gentity_t *self, gentity_t *saber ) saberSpeed = 400 - (dist*2); } } + else if ( self->client->ps.saberEntityState == SES_LEAVING && dist < 50 ) + { + saberSpeed = dist * 2 + 30; + } /* if ( self->client->ps.saberEntityState == SES_RETURNING ) {//FIXME: if returning, move faster? @@ -3462,26 +3783,43 @@ void WP_RunSaber( gentity_t *self, gentity_t *saber ) } -void WP_SaberLaunch( gentity_t *self, gentity_t *saber ) +qboolean WP_SaberLaunch( gentity_t *self, gentity_t *saber, qboolean thrown ) {//FIXME: probably need a debounce time + vec3_t saberMins={-3.0f,-3.0f,-3.0f}; + vec3_t saberMaxs={3.0f,3.0f,3.0f}; + trace_t trace; + if ( self->client->ps.forcePowerLevel[FP_SABERTHROW] > FORCE_LEVEL_2 ) { if ( !WP_ForcePowerUsable( self, FP_SABERTHROW, 20 ) ) { - return; + return qfalse; } } else { if ( !WP_ForcePowerUsable( self, FP_SABERTHROW, 0 ) ) { - return; + return qfalse; } } - if ( !self->s.number && cg.zoomMode ) - {//can't saber throw when zoomed in - return; + if ( !self->s.number && (cg.zoomMode || in_camera) ) + {//can't saber throw when zoomed in or in cinematic + return qfalse; } + //make sure it won't start in solid + gi.trace( &trace, self->client->renderInfo.handRPoint, saberMins, saberMaxs, self->client->renderInfo.handRPoint, saber->s.number, MASK_SOLID ); + if ( trace.startsolid || trace.allsolid ) + { + return qfalse; + } + //make sure I'm not throwing it on the other side of a door or wall or whatever + gi.trace( &trace, self->currentOrigin, vec3_origin, vec3_origin, self->client->renderInfo.handRPoint, self->s.number, MASK_SOLID ); + if ( trace.startsolid || trace.allsolid || trace.fraction < 1.0f ) + { + return qfalse; + } + if ( self->client->ps.forcePowerLevel[FP_SABERTHROW] > FORCE_LEVEL_2 ) {//at max skill, the cost increases as keep it out WP_ForcePowerStart( self, FP_SABERTHROW, 10 ); @@ -3507,8 +3845,15 @@ void WP_SaberLaunch( gentity_t *self, gentity_t *saber ) VectorClear( saber->s.apos.trBase ); saber->s.apos.trTime = level.time; saber->s.apos.trType = TR_LINEAR; - saber->s.apos.trBase[1] = self->client->ps.viewangles[1]; - saber->s.apos.trBase[0] = SABER_PITCH_HACK; + if ( self->health > 0 && thrown ) + {//throwing it + saber->s.apos.trBase[1] = self->client->ps.viewangles[1]; + saber->s.apos.trBase[0] = SABER_PITCH_HACK; + } + else + {//dropping it + vectoangles( self->client->renderInfo.muzzleDir, saber->s.apos.trBase ); + } VectorClear( saber->s.apos.trDelta ); switch ( self->client->ps.forcePowerLevel[FP_SABERTHROW] ) @@ -3530,7 +3875,7 @@ void WP_SaberLaunch( gentity_t *self, gentity_t *saber ) self->client->ps.saberEntityState = SES_LEAVING; self->client->ps.saberEntityDist = saberThrowDist[self->client->ps.forcePowerLevel[FP_SABERTHROW]]; self->client->ps.saberThrowTime = level.time; - if ( self->client->ps.forcePowerLevel[FP_SABERTHROW] > FORCE_LEVEL_2 ) + //if ( self->client->ps.forcePowerLevel[FP_SABERTHROW] > FORCE_LEVEL_2 ) { self->client->ps.forcePowerDebounce[FP_SABERTHROW] = level.time + 1000;//so we can keep it out for a minimum amount of time } @@ -3543,14 +3888,16 @@ void WP_SaberLaunch( gentity_t *self, gentity_t *saber ) self->client->saberTrail.duration = 150; //reset the mins - VectorSet( saber->mins, -3.0f, -3.0f, -3.0f ); - VectorSet( saber->maxs, 3.0f, 3.0f, 3.0f ); + VectorCopy( saberMins, saber->mins ); + VectorCopy( saberMaxs, saber->maxs ); saber->contents = 0;//CONTENTS_LIGHTSABER; saber->clipmask = MASK_SOLID | CONTENTS_LIGHTSABER; // remove the ghoul2 sabre model on the player gi.G2API_RemoveGhoul2Model(self->ghoul2, self->weaponModel); self->weaponModel = -1; + + return qtrue; } void WP_SaberLose( gentity_t *self, vec3_t throwDir ) @@ -3566,7 +3913,10 @@ void WP_SaberLose( gentity_t *self, vec3_t throwDir ) self->client->ps.forcePowersKnown |= (1<client->ps.forcePowerLevel[FP_SABERTHROW] = FORCE_LEVEL_1; //throw it - WP_SaberLaunch( self, dropped ); + if ( !WP_SaberLaunch( self, dropped, qfalse ) ) + {//couldn't throw it + return; + } } if ( self->client->ps.saberActive ) {//on @@ -3610,6 +3960,8 @@ void WP_SaberCatch( gentity_t *self, gentity_t *saber, qboolean switchToSaber ) saber->contents = CONTENTS_LIGHTSABER;// | CONTENTS_SHOTCLIP; saber->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; + //play catch sound + G_Sound( saber, G_SoundIndex( "sound/weapons/saber/saber_catch.wav" ) ); //FIXME: if an NPC, don't turn it back on if no enemy or enemy is dead... //if it's not our current weapon, make it our current weapon if ( self->client->ps.weapon == WP_SABER ) @@ -3741,13 +4093,19 @@ void WP_SaberThrow( gentity_t *self, usercmd_t *ucmd ) } else if ( ucmd->buttons & BUTTON_ALT_ATTACK && !(self->client->ps.pm_flags&PMF_ALT_ATTACK_HELD) ) {//still holding it, not still holding attack from a previous throw, so throw it. - if ( self->client && !self->s.number ) + if ( WP_SaberLaunch( self, saberent, qtrue ) ) { - self->client->sess.missionStats.saberThrownCnt++; + if ( self->client && !self->s.number ) + { + self->client->sess.missionStats.saberThrownCnt++; + } + //need to recalc this because we just moved it + VectorSubtract( self->client->renderInfo.handRPoint, saberent->currentOrigin, saberDiff ); + } + else + {//couldn't throw it + return; } - WP_SaberLaunch( self, saberent ); - //need to recalc this because we just moved it - VectorSubtract( self->client->renderInfo.handRPoint, saberent->currentOrigin, saberDiff ); } else {//holding it, don't want to throw it, go away. @@ -3761,8 +4119,16 @@ void WP_SaberThrow( gentity_t *self, usercmd_t *ucmd ) {//see if we're close enough to pick it up if ( VectorLengthSquared( saberDiff ) <= 256 )//16 squared//G_BoundsOverlap( self->absmin, self->absmax, saberent->absmin, saberent->absmax ) )// {//caught it - WP_SaberCatch( self, saberent, qtrue ); - NPC_SetAnim( self, SETANIM_TORSO, TORSO_HANDRETRACT1, SETANIM_FLAG_OVERRIDE ); + vec3_t axisPoint; + trace_t trace; + VectorCopy( self->currentOrigin, axisPoint ); + axisPoint[2] = self->client->renderInfo.handRPoint[2]; + gi.trace( &trace, axisPoint, vec3_origin, vec3_origin, self->client->renderInfo.handRPoint, self->s.number, MASK_SOLID ); + if ( !trace.startsolid && trace.fraction >= 1.0f ) + {//our hand isn't through a wall + WP_SaberCatch( self, saberent, qtrue ); + NPC_SetAnim( self, SETANIM_TORSO, TORSO_HANDRETRACT1, SETANIM_FLAG_OVERRIDE ); + } return; } } @@ -3874,9 +4240,23 @@ void WP_SaberThrow( gentity_t *self, usercmd_t *ucmd ) } } } - else if ( saberDist >= self->client->ps.saberEntityDist || level.time - self->client->ps.saberThrowTime > 3000 ) - {//went too far, return to me - WP_SaberReturn( self, saberent ); + else + { + if ( !(ucmd->buttons&BUTTON_ALT_ATTACK) && self->client->ps.forcePowerDebounce[FP_SABERTHROW] < level.time ) + {//not holding button and has been out at least 1 second, return to me + if ( self->client->ps.saberActive ) + {//still on + WP_SaberReturn( self, saberent ); + } + } + else if ( level.time - self->client->ps.saberThrowTime > 3000 + || (self->client->ps.forcePowerLevel[FP_SABERTHROW]==FORCE_LEVEL_1&&saberDist>=self->client->ps.saberEntityDist) ) + {//been out too long, or saber throw 1 went too far, return to me + if ( self->client->ps.saberActive ) + {//still on + WP_SaberReturn( self, saberent ); + } + } } } if ( self->client->ps.saberEntityState == SES_RETURNING ) @@ -3954,7 +4334,7 @@ void WP_SaberBlockNonRandom( gentity_t *self, vec3_t hitloc, qboolean missileBlo zdiff = hitloc[2] - self->client->renderInfo.eyePoint[2]; //FIXME: take torsoAngles into account? - if ( zdiff > 0 )//40 ) + if ( zdiff > -5 )//0 )//40 ) { if ( rightdot > 0.3 ) { @@ -3969,7 +4349,7 @@ void WP_SaberBlockNonRandom( gentity_t *self, vec3_t hitloc, qboolean missileBlo self->client->ps.saberBlocked = BLOCKED_TOP; } } - else if ( zdiff > -20 )//20 ) + else if ( zdiff > -22 )//-20 )//20 ) { if ( zdiff < -10 )//30 ) {//hmm, pretty low, but not low enough to use the low block, so we need to duck @@ -4000,6 +4380,7 @@ void WP_SaberBlockNonRandom( gentity_t *self, vec3_t hitloc, qboolean missileBlo } } +#ifndef _FINAL_BUILD if ( d_saberCombat->integer ) { if ( !self->s.number ) @@ -4027,6 +4408,7 @@ void WP_SaberBlockNonRandom( gentity_t *self, vec3_t hitloc, qboolean missileBlo } } } +#endif if ( missileBlock ) { @@ -4035,7 +4417,7 @@ void WP_SaberBlockNonRandom( gentity_t *self, vec3_t hitloc, qboolean missileBlo if ( self->client->ps.saberBlocked != BLOCKED_NONE ) { - int parryReCalcTime = Jedi_ReCalcParryTime( self ); + int parryReCalcTime = Jedi_ReCalcParryTime( self, EVASION_PARRY ); if ( self->client->ps.forcePowerDebounce[FP_SABER_DEFENSE] < level.time + parryReCalcTime ) { self->client->ps.forcePowerDebounce[FP_SABER_DEFENSE] = level.time + parryReCalcTime; @@ -4154,6 +4536,8 @@ void WP_SaberStartMissileBlockCheck( gentity_t *self, usercmd_t *ucmd ) int i, e; float closestDist, radius = 256; vec3_t forward, dir, missile_dir, fwdangles = {0}; + trace_t trace; + vec3_t traceTo, entDir; if ( self->client->ps.weapon != WP_SABER ) @@ -4387,6 +4771,19 @@ void WP_SaberStartMissileBlockCheck( gentity_t *self, usercmd_t *ucmd ) //FIXME: must have a clear trace to me, too... if ( dist < closestDist ) { + VectorCopy( self->currentOrigin, traceTo ); + traceTo[2] = self->absmax[2] - 4; + gi.trace( &trace, ent->currentOrigin, ent->mins, ent->maxs, traceTo, ent->s.number, ent->clipmask ); + if ( trace.allsolid || trace.startsolid || (trace.fraction < 1.0f && trace.entityNum != self->s.number && trace.entityNum != self->client->ps.saberEntityNum) ) + {//okay, try one more check + VectorNormalize2( ent->s.pos.trDelta, entDir ); + VectorMA( ent->currentOrigin, radius, entDir, traceTo ); + gi.trace( &trace, ent->currentOrigin, ent->mins, ent->maxs, traceTo, ent->s.number, ent->clipmask ); + if ( trace.allsolid || trace.startsolid || (trace.fraction < 1.0f && trace.entityNum != self->s.number && trace.entityNum != self->client->ps.saberEntityNum) ) + {//can't hit me, ignore it + continue; + } + } if ( self->s.number != 0 ) {//An NPC if ( self->NPC && !self->enemy && ent->owner ) @@ -4411,7 +4808,7 @@ void WP_SaberStartMissileBlockCheck( gentity_t *self, usercmd_t *ucmd ) { Jedi_Ambush( self ); } - if ( Jedi_SaberBlockGo( self, NULL, incoming ) ) + if ( Jedi_SaberBlockGo( self, NULL, NULL, incoming ) != EVASION_NONE ) {//make sure to turn on your saber if it's not on self->client->ps.saberActive = qtrue; } @@ -4496,9 +4893,7 @@ void WP_SaberUpdate( gentity_t *self, usercmd_t *ucmd ) else if ( self->client->ps.saberBlocking == BLK_TIGHT || self->client->ps.saberBlocking == BLK_WIDE ) {//FIXME: keep bbox in front of player, even when wide? vec3_t saberOrg; - if ( self->s.number == 0 - && (g_autoBlocking->integer||self->client->ps.saberBlockingTime>level.time) - && self->client->ps.saberBlocking == BLK_WIDE + if ( ( (self->s.number&&!Jedi_SaberBusy(self)) || (self->s.number == 0 && self->client->ps.saberBlocking == BLK_WIDE && (g_autoBlocking->integer||self->client->ps.saberBlockingTime>level.time)) ) && self->client->ps.weaponTime <= 0 ) {//full-size blocking for non-attacking player with g_autoBlocking on vec3_t saberang={0,0,0}, fwd, sabermins={-8,-8,-8}, sabermaxs={8,8,8}; @@ -4617,10 +5012,12 @@ void WP_SaberUpdate( gentity_t *self, usercmd_t *ucmd ) { WP_SaberInFlightReflectCheck( self, ucmd ); } +#ifndef _FINAL_BUILD if ( d_saberCombat->integer > 2 ) { CG_CubeOutline( saberent->absmin, saberent->absmax, 50, WPDEBUG_SaberColor( self->client->ps.saberColor ), 1 ); } +#endif } @@ -4643,7 +5040,7 @@ void WP_DropWeapon( gentity_t *dropper, vec3_t velocity ) {//Hmm, maybe all NPCs should go into melee? Not too many, though, or they mob you and look silly replaceWeap = WP_MELEE; } - if ( dropper->weaponModel != -1 ) + if (dropper->ghoul2.IsValid()&& dropper->weaponModel != -1 ) { gi.G2API_RemoveGhoul2Model( dropper->ghoul2, dropper->weaponModel ); dropper->weaponModel = -1; @@ -4726,7 +5123,8 @@ void WP_ResistForcePush( gentity_t *self, gentity_t *pusher ) && !PM_SpinningSaberAnim( self->client->ps.legsAnim ) && !PM_FlippingAnim( self->client->ps.legsAnim ) && !PM_RollingAnim( self->client->ps.legsAnim ) - && !PM_InKnockDown( &self->client->ps ) ) + && !PM_InKnockDown( &self->client->ps ) + && !PM_CrouchAnim( self->client->ps.legsAnim )) {//if on a surface and not in a spin or flip, play full body resist parts = SETANIM_BOTH; } @@ -4841,6 +5239,12 @@ void WP_ForceKnockdown( gentity_t *self, gentity_t *pusher, qboolean pull, qbool knockAnim = BOTH_KNOCKDOWN2; } NPC_SetAnim( self, SETANIM_BOTH, knockAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + if ( self->s.number ) + {//randomize getup times + int addTime = Q_irand( -300, 1000 ); + self->client->ps.legsAnimTimer += addTime; + self->client->ps.torsoAnimTimer += addTime; + } // if ( pusher->NPC && pusher->enemy == self ) {//pushed pushed down his enemy @@ -4870,6 +5274,31 @@ void ForceThrow( gentity_t *self, qboolean pull ) trace_t tr; int anim, hold, soundIndex; + if ( self->health <= 0 ) + { + return; + } + if ( self->client->ps.leanofs ) + {//can't force-throw while leaning + return; + } + if ( self->client->ps.forcePowerDebounce[FP_PUSH] > level.time )//self->client->ps.powerups[PW_FORCE_PUSH] > level.time ) + {//already pushing- now you can't haul someone across the room, sorry + return; + } + if ( !self->s.number && (cg.zoomMode || in_camera) ) + {//can't force throw/pull when zoomed in or in cinematic + return; + } + if ( self->client->ps.saberLockTime > level.time && ( pull || self->client->ps.forcePowerLevel[FP_PUSH] < FORCE_LEVEL_3 ) ) + {//this can be a way to break out + return; + } + + if ( self->client->ps.legsAnim == BOTH_KNOCKDOWN3 || (self->client->ps.torsoAnim == BOTH_GETUP3 && self->client->ps.torsoAnimTimer > 500) ) + {//we're face-down, so we'd only be force-push/pulling the floor + return; + } if ( pull ) { radius = forcePushPullRadius[self->client->ps.forcePowerLevel[FP_PULL]]; @@ -4883,39 +5312,16 @@ void ForceThrow( gentity_t *self, qboolean pull ) {//no ability to do this yet return; } - if ( self->health <= 0 ) - { - return; - } - if ( self->client->ps.leanofs ) - {//can't force-throw while leaning - return; - } - if ( self->client->ps.forcePowerDebounce[FP_PUSH] > level.time )//self->client->ps.powerups[PW_FORCE_PUSH] > level.time ) - {//already pushing- now you can't haul someone across the room, sorry - return; - } - if ( !self->s.number && cg.zoomMode ) - {//can't force throw/pull when zoomed in - return; - } - if ( self->client->ps.saberLockTime > level.time && ( pull || self->client->ps.forcePowerLevel[FP_PUSH] < FORCE_LEVEL_3 ) ) - {//this can be a way to break out - return; - } - - if ( self->client->ps.legsAnim == BOTH_KNOCKDOWN3 || (self->client->ps.torsoAnim == BOTH_GETUP3 && self->client->ps.torsoAnimTimer > 500) ) - {//we're face-down, so we'd only be force-push/pulling the floor - return; - } if ( pull ) { - if ( !WP_ForcePowerUsable( self, FP_PULL, 0 ) ) + //FIXME: base cost on the number of things you end up pulling? + int cost = forcePowerNeeded[FP_PULL]*self->client->ps.forcePowerLevel[FP_PULL]; + if ( !WP_ForcePowerUsable( self, FP_PULL, cost ) ) { return; } - WP_ForcePowerStart( self, FP_PULL, 0 ); + WP_ForcePowerStart( self, FP_PULL, cost ); //make sure this plays and that you cannot press fire for about 200ms after this anim = BOTH_FORCEPULL; soundIndex = G_SoundIndex( "sound/weapons/force/pull.wav" ); @@ -4923,11 +5329,13 @@ void ForceThrow( gentity_t *self, qboolean pull ) } else { - if ( !WP_ForcePowerUsable( self, FP_PUSH, 0 ) ) + //FIXME: base cost on the number of things you end up pushing? + int cost = forcePowerNeeded[FP_PUSH]*self->client->ps.forcePowerLevel[FP_PUSH]; + if ( !WP_ForcePowerUsable( self, FP_PUSH, cost ) ) { return; } - WP_ForcePowerStart( self, FP_PUSH, 0 ); + WP_ForcePowerStart( self, FP_PUSH, cost ); //make sure this plays and that you cannot press fire for about 1 second after this anim = BOTH_FORCEPUSH; soundIndex = G_SoundIndex( "sound/weapons/force/push.wav" ); @@ -5013,6 +5421,12 @@ void ForceThrow( gentity_t *self, qboolean pull ) continue; if ( !(ent->inuse) ) continue; + if ( ent->NPC && ent->NPC->scriptFlags & SCF_NO_FORCE ) + continue; + if ( (ent->flags&FL_FORCE_PULLABLE_ONLY) && !pull ) + {//simple HACK: cannot force-push ammo rack items (because they may start in solid) + continue; + } //FIXME: don't push it if I already pushed it a little while ago if ( ent->s.eType != ET_MISSILE ) { @@ -5046,7 +5460,7 @@ void ForceThrow( gentity_t *self, qboolean pull ) {//can knock over placed turrets if ( !self->s.number || self->enemy != ent ) {//only NPCs who are actively mad at this turret can push it over - return; + continue; } } else @@ -5376,8 +5790,13 @@ void ForceThrow( gentity_t *self, qboolean pull ) //different effect? } } - else if ( push_list[x]->s.eType == ET_MISSILE && push_list[x]->s.pos.trType != TR_STATIONARY && (push_list[x]->s.pos.trType != TR_INTERPOLATE||push_list[x]->s.weapon != WP_THERMAL) )//rolling and stationary thermal detonators are dealt with below + else if ( push_list[x]->s.eType == ET_MISSILE + && push_list[x]->s.pos.trType != TR_STATIONARY + && (push_list[x]->s.pos.trType != TR_INTERPOLATE||push_list[x]->s.weapon != WP_THERMAL) )//rolling and stationary thermal detonators are dealt with below { + vec3_t dir2Me; + VectorSubtract( self->currentOrigin, push_list[x]->currentOrigin, dir2Me ); + float dot = DotProduct( push_list[x]->s.pos.trDelta, dir2Me ); if ( pull ) {//deflect rather than reflect? } @@ -5393,7 +5812,14 @@ void ForceThrow( gentity_t *self, qboolean pull ) push_list[x]->e_ThinkFunc = thinkF_WP_Explode; push_list[x]->nextthink = level.time + Q_irand( 500, 3000 ); } - G_ReflectMissile( self, push_list[x], forward ); + if ( dot >= 0 ) + {//it's heading towards me + G_ReflectMissile( self, push_list[x], forward ); + } + else + { + VectorScale( push_list[x]->s.pos.trDelta, 1.25f, push_list[x]->s.pos.trDelta ); + } //deflect sound //G_Sound( push_list[x], G_SoundIndex( va("sound/weapons/blaster/reflect%d.wav", Q_irand( 1, 3 ) ) ) ); //push_list[x]->forcePushTime = level.time + 600; // let the push effect last for 600 ms @@ -5559,6 +5985,7 @@ void ForceThrow( gentity_t *self, qboolean pull ) knockback = 100; } } + //FIXME: if pull a FL_FORCE_PULLABLE_ONLY, clear the flag, assuming it's no longer in solid? or check? VectorCopy( push_list[x]->currentOrigin, push_list[x]->s.pos.trBase ); push_list[x]->s.pos.trTime = level.time; // move a bit on the very first frame if ( push_list[x]->s.pos.trType != TR_INTERPOLATE ) @@ -5641,6 +6068,10 @@ void ForceSpeed( gentity_t *self, int duration ) {//FIXME: can this be a way to break out? return; } + if ( !self->s.number && in_camera ) + {//player can't use force powers in cinematic + return; + } WP_ForcePowerStart( self, FP_SPEED, 0 ); if ( duration ) { @@ -5670,6 +6101,10 @@ void ForceHeal( gentity_t *self ) {//FIXME: can this be a way to break out? return; } + if ( !self->s.number && in_camera ) + {//player can't use force powers in cinematic + return; + } /* if ( self->client->ps.forcePowerLevel[FP_HEAL] > FORCE_LEVEL_2 ) {//instant heal @@ -5697,7 +6132,7 @@ void ForceHeal( gentity_t *self ) {//must meditate //FIXME: holster weapon (select WP_NONE?) //FIXME: BOTH_FORCEHEAL_START - NPC_SetAnim( self, SETANIM_BOTH, BOTH_CROUCH3, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + NPC_SetAnim( self, SETANIM_BOTH, BOTH_FORCEHEAL_START, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); self->client->ps.saberMove = self->client->ps.saberBounceMove = LS_READY;//don't finish whatever saber anim you may have been in self->client->ps.saberBlocked = BLOCKED_NONE; self->client->ps.torsoAnimTimer = self->client->ps.legsAnimTimer = FORCE_HEAL_INTERVAL*MAX_FORCE_HEAL + 2000;//??? @@ -5740,6 +6175,7 @@ void ForceTelepathy( gentity_t *self ) return; } + //FIXME: if mind trick 3 and aiming at an enemy need more force power if ( !WP_ForcePowerUsable( self, FP_TELEPATHY, 0 ) ) { return; @@ -5753,13 +6189,10 @@ void ForceTelepathy( gentity_t *self ) {//FIXME: can this be a way to break out? return; } - //make sure this plays and that you cannot press fire for about 1 second after this - //FIXME: BOTH_FORCEMINDTRICK or BOTH_FORCEDISTRACT - NPC_SetAnim( self, SETANIM_TORSO, BOTH_MINDTRICK1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD ); - self->client->ps.saberMove = self->client->ps.saberBounceMove = LS_READY;//don't finish whatever saber anim you may have been in - self->client->ps.saberBlocked = BLOCKED_NONE; - self->client->ps.weaponTime = 1000; - //FIXME: build-up or delay this until in proper part of anim + if ( !self->s.number && in_camera ) + {//player can't use force powers in cinematic + return; + } AngleVectors( self->client->ps.viewangles, forward, NULL, NULL ); VectorNormalize( forward ); @@ -5774,6 +6207,11 @@ void ForceTelepathy( gentity_t *self ) traceEnt = &g_entities[tr.entityNum]; + if( traceEnt->NPC && traceEnt->NPC->scriptFlags & SCF_NO_FORCE ) + { + return; + } + if ( traceEnt && traceEnt->client ) { switch ( traceEnt->client->NPC_class ) @@ -5801,13 +6239,21 @@ void ForceTelepathy( gentity_t *self ) if ( G_ActivateBehavior( traceEnt, BSET_MINDTRICK ) ) {//activated a script on him //FIXME: do the visual sparkles effect on their heads, still? + WP_ForcePowerStart( self, FP_TELEPATHY, 0 ); } else if ( traceEnt->client->playerTeam != self->client->playerTeam ) {//an enemy + int override = 0; if ( traceEnt->s.weapon != WP_SABER ) {//haha! Jedi aren't easily confused! if ( self->client->ps.forcePowerLevel[FP_TELEPATHY] > FORCE_LEVEL_2 ) {//turn them to our side + //if mind trick 3 and aiming at an enemy need more force power + override = 50; + if ( self->client->ps.forcePower < 50 ) + { + return; + } if ( traceEnt->s.weapon != WP_NONE ) {//don't charm people who aren't capable of fighting... like ugnaughts and droids if ( traceEnt->enemy ) @@ -5849,7 +6295,7 @@ void ForceTelepathy( gentity_t *self ) { NPC_Jedi_PlayConfusionSound( traceEnt ); } - WP_ForcePowerStart( self, FP_TELEPATHY, 0 ); + WP_ForcePowerStart( self, FP_TELEPATHY, override ); } else if ( traceEnt->client->playerTeam == self->client->playerTeam ) {//an ally @@ -5864,17 +6310,28 @@ void ForceTelepathy( gentity_t *self ) AngleVectors( traceEnt->client->renderInfo.eyeAngles, eyeDir, NULL, NULL ); VectorNormalize( eyeDir ); G_PlayEffect( "force_touch", traceEnt->client->renderInfo.eyePoint, eyeDir ); + + //make sure this plays and that you cannot press fire for about 1 second after this + //FIXME: BOTH_FORCEMINDTRICK or BOTH_FORCEDISTRACT + NPC_SetAnim( self, SETANIM_TORSO, BOTH_MINDTRICK1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD ); + //FIXME: build-up or delay this until in proper part of anim } - else if ( self->client->ps.forcePowerLevel[FP_TELEPATHY] > FORCE_LEVEL_1 && tr.fraction * 2048 > 64 ) - {//don't create a diversion less than 64 from you of if at power level 1 - //use distraction anim instead + else + { + if ( self->client->ps.forcePowerLevel[FP_TELEPATHY] > FORCE_LEVEL_1 && tr.fraction * 2048 > 64 ) + {//don't create a diversion less than 64 from you of if at power level 1 + //use distraction anim instead + G_PlayEffect( G_EffectIndex( "force_touch" ), tr.endpos, tr.plane.normal ); + //FIXME: these events don't seem to always be picked up...? + AddSoundEvent( self, tr.endpos, 512, AEL_SUSPICIOUS ); + AddSightEvent( self, tr.endpos, 512, AEL_SUSPICIOUS, 50 ); + WP_ForcePowerStart( self, FP_TELEPATHY, 0 ); + } NPC_SetAnim( self, SETANIM_TORSO, BOTH_MINDTRICK2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD ); - G_PlayEffect( G_EffectIndex( "force_touch" ), tr.endpos, tr.plane.normal ); - //FIXME: these events don't seem to always be picked up...? - AddSoundEvent( self, tr.endpos, 512, AEL_SUSPICIOUS ); - AddSightEvent( self, tr.endpos, 512, AEL_SUSPICIOUS, 50 ); - WP_ForcePowerStart( self, FP_TELEPATHY, 0 ); } + self->client->ps.saberMove = self->client->ps.saberBounceMove = LS_READY;//don't finish whatever saber anim you may have been in + self->client->ps.saberBlocked = BLOCKED_NONE; + self->client->ps.weaponTime = 1000; } void ForceGrip( gentity_t *self ) @@ -5887,8 +6344,8 @@ void ForceGrip( gentity_t *self ) { return; } - if ( !self->s.number && cg.zoomMode ) - {//can't force grip when zoomed in + if ( !self->s.number && (cg.zoomMode || in_camera) ) + {//can't force grip when zoomed in or in cinematic return; } if ( self->client->ps.leanofs ) @@ -5958,7 +6415,7 @@ void ForceGrip( gentity_t *self ) traceEnt = &g_entities[tr.entityNum]; } - if ( !traceEnt || traceEnt == self/*???*/ || traceEnt->bmodel || (traceEnt->health <= 0 && traceEnt->takedamage) ) + if ( !traceEnt || traceEnt == self/*???*/ || traceEnt->bmodel || (traceEnt->health <= 0 && traceEnt->takedamage) || (traceEnt->NPC && traceEnt->NPC->scriptFlags & SCF_NO_FORCE) ) { return; } @@ -5983,6 +6440,7 @@ void ForceGrip( gentity_t *self ) case CLASS_MOUSE://? case CLASS_SEEKER: case CLASS_REMOTE: + case CLASS_PROTOCOL: return; break; case CLASS_DESANN://Desann cannot be gripped, he just pushes you back instantly @@ -5990,6 +6448,18 @@ void ForceGrip( gentity_t *self ) ForceThrow( traceEnt, qfalse ); return; break; + case CLASS_REBORN: + case CLASS_SHADOWTROOPER: + case CLASS_TAVION: + case CLASS_JEDI: + case CLASS_LUKE: + if ( traceEnt->NPC && traceEnt->NPC->rank > RANK_CIVILIAN && self->client->ps.forcePowerLevel[FP_GRIP] < FORCE_LEVEL_2 ) + { + Jedi_PlayDeflectSound( traceEnt ); + ForceThrow( traceEnt, qfalse ); + return; + } + break; } if ( traceEnt->s.weapon == WP_EMPLACED_GUN ) {//FIXME: maybe can pull them out? @@ -6040,7 +6510,7 @@ void ForceGrip( gentity_t *self ) if ( self->client->ps.forcePowerLevel[FP_GRIP] < FORCE_LEVEL_2 ) {//just a duration self->client->ps.forcePowerDebounce[FP_GRIP] = level.time + 250; - self->client->ps.forcePowerDuration[FP_GRIP] = level.time + 10000; + self->client->ps.forcePowerDuration[FP_GRIP] = level.time + 5000; traceEnt->s.loopSound = G_SoundIndex( "sound/weapons/force/grip.mp3" ); } else @@ -6068,8 +6538,8 @@ void ForceLightning( gentity_t *self ) { return; } - if ( !self->s.number && cg.zoomMode ) - {//can't force lightning when zoomed in + if ( !self->s.number && (cg.zoomMode || in_camera) ) + {//can't force lightning when zoomed in or in cinematic return; } if ( self->client->ps.leanofs ) @@ -6096,7 +6566,7 @@ void ForceLightning( gentity_t *self ) } else { - NPC_SetAnim( self, SETANIM_TORSO, BOTH_FORCELIGHTNING_HOLD, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + NPC_SetAnim( self, SETANIM_TORSO, BOTH_FORCELIGHTNING_START, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); } self->client->ps.saberMove = self->client->ps.saberBounceMove = LS_READY;//don't finish whatever saber anim you may have been in self->client->ps.saberBlocked = BLOCKED_NONE; @@ -6115,13 +6585,42 @@ void ForceLightning( gentity_t *self ) WP_ForcePowerStart( self, FP_LIGHTNING, self->client->ps.torsoAnimTimer ); } -void ForceLightningDamage( gentity_t *self, gentity_t *traceEnt, vec3_t dir, vec3_t impactPoint ) +void ForceLightningDamage( gentity_t *self, gentity_t *traceEnt, vec3_t dir, float dist, float dot, vec3_t impactPoint ) { + if( traceEnt->NPC && traceEnt->NPC->scriptFlags & SCF_NO_FORCE ) + { + return; + } + if ( traceEnt && traceEnt->takedamage ) { if ( !traceEnt->client || traceEnt->client->playerTeam != self->client->playerTeam || self->enemy == traceEnt || traceEnt->enemy == self ) {//an enemy or object - int dmg = Q_irand( 1, 3 ); + int dmg; + if ( self->client->ps.forcePowerLevel[FP_LIGHTNING] > FORCE_LEVEL_2 ) + {//more damage if closer and more in front + dmg = 1; + if ( dist < 100 ) + { + dmg += 2; + } + else if ( dist < 200 ) + { + dmg += 1; + } + if ( dot > 0.9f ) + { + dmg += 2; + } + else if ( dot > 0.7f ) + { + dmg += 1; + } + } + else + { + dmg = Q_irand( 1, 3 );//*self->client->ps.forcePowerLevel[FP_LIGHTNING]; + } if ( traceEnt->client && traceEnt->health > 0 && ( traceEnt->client->NPC_class == CLASS_DESANN || traceEnt->client->NPC_class == CLASS_LUKE ) ) {//Luke and Desann can shield themselves from the attack //FIXME: shield effect or something? @@ -6273,7 +6772,9 @@ void ForceShootLightning( gentity_t *self ) } // ok, we are within the radius, add us to the incoming list - ForceLightningDamage( self, traceEnt, dir, ent_org ); + //FIXME: maybe add up the ents and do more damage the less ents there are + // as if we're spreading out the damage? + ForceLightningDamage( self, traceEnt, dir, dist, dot, ent_org ); } } @@ -6311,7 +6812,7 @@ void ForceShootLightning( gentity_t *self ) } traceEnt = &g_entities[tr.entityNum]; - ForceLightningDamage( self, traceEnt, forward, tr.endpos ); + ForceLightningDamage( self, traceEnt, forward, 0, 0, tr.endpos ); } } @@ -6442,8 +6943,8 @@ void ForceJump( gentity_t *self, usercmd_t *ucmd ) { return; } - if ( !self->s.number && cg.zoomMode ) - {//can't force jump when zoomed in + if ( !self->s.number && (cg.zoomMode || in_camera) ) + {//can't force jump when zoomed in or in cinematic return; } if ( self->client->ps.saberLockTime > level.time ) @@ -6461,16 +6962,44 @@ void ForceJump( gentity_t *self, usercmd_t *ucmd ) switch( WP_GetVelocityForForceJump( self, jumpVel, ucmd ) ) { case FJ_FORWARD: - anim = BOTH_FLIP_F; + if ( self->NPC && self->NPC->rank != RANK_CREWMAN && self->NPC->rank <= RANK_LT_JG ) + {//can't do acrobatics + anim = BOTH_FORCEJUMP1; + } + else + { + anim = BOTH_FLIP_F; + } break; case FJ_BACKWARD: - anim = BOTH_FLIP_B; + if ( self->NPC && self->NPC->rank != RANK_CREWMAN && self->NPC->rank <= RANK_LT_JG ) + {//can't do acrobatics + anim = BOTH_FORCEJUMPBACK1; + } + else + { + anim = BOTH_FLIP_B; + } break; case FJ_RIGHT: - anim = BOTH_FLIP_R; + if ( self->NPC && self->NPC->rank != RANK_CREWMAN && self->NPC->rank <= RANK_LT_JG ) + {//can't do acrobatics + anim = BOTH_FORCEJUMPRIGHT1; + } + else + { + anim = BOTH_FLIP_R; + } break; case FJ_LEFT: - anim = BOTH_FLIP_L; + if ( self->NPC && self->NPC->rank != RANK_CREWMAN && self->NPC->rank <= RANK_LT_JG ) + {//can't do acrobatics + anim = BOTH_FORCEJUMPLEFT1; + } + else + { + anim = BOTH_FLIP_L; + } break; default: case FJ_UP: @@ -6683,7 +7212,14 @@ void WP_ForcePowerStop( gentity_t *self, forcePowers_t forcePower ) if ( self->client->ps.forcePowerLevel[FP_HEAL] < FORCE_LEVEL_2 ) {//if in meditation pose, must come out of it //FIXME: BOTH_FORCEHEAL_STOP - NPC_SetAnim( self, SETANIM_BOTH, BOTH_UNCROUCH3, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + if ( self->client->ps.legsAnim == BOTH_FORCEHEAL_START ) + { + NPC_SetAnim( self, SETANIM_LEGS, BOTH_FORCEHEAL_STOP, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } + if ( self->client->ps.torsoAnim == BOTH_FORCEHEAL_START ) + { + NPC_SetAnim( self, SETANIM_TORSO, BOTH_FORCEHEAL_STOP, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } self->client->ps.saberMove = self->client->ps.saberBounceMove = LS_READY;//don't finish whatever saber anim you may have been in self->client->ps.saberBlocked = BLOCKED_NONE; } @@ -6718,6 +7254,16 @@ void WP_ForcePowerStop( gentity_t *self, forcePowers_t forcePower ) if ( gripEnt->client ) { gripEnt->client->ps.eFlags &= ~EF_FORCE_GRIPPED; + if ( self->client->ps.forcePowerLevel[FP_GRIP] > FORCE_LEVEL_1 ) + {//sanity-cap the velocity + float gripVel = VectorNormalize( gripEnt->client->ps.velocity ); + if ( gripVel > 500.0f ) + { + gripVel = 500.0f; + } + VectorScale( gripEnt->client->ps.velocity, gripVel, gripEnt->client->ps.velocity ); + } + //FIXME: they probably dropped their weapon, should we make them flee? Or should AI handle no-weapon behavior? if ( gripEnt->health > 0 ) { @@ -6802,7 +7348,8 @@ void WP_ForcePowerStop( gentity_t *self, forcePowers_t forcePower ) } break; case FP_LIGHTNING: - if ( self->client->ps.torsoAnim == BOTH_FORCELIGHTNING_HOLD ) + if ( self->client->ps.torsoAnim == BOTH_FORCELIGHTNING_HOLD + || self->client->ps.torsoAnim == BOTH_FORCELIGHTNING_START ) { NPC_SetAnim( self, SETANIM_TORSO, BOTH_FORCELIGHTNING_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); } @@ -6930,7 +7477,8 @@ static void WP_ForcePowerRun( gentity_t *self, forcePowers_t forcePower, usercmd case FP_TELEPATHY: break; case FP_GRIP: - if ( !WP_ForcePowerAvailable( self, FP_GRIP, 0 ) ) + if ( !WP_ForcePowerAvailable( self, FP_GRIP, 0 ) + || (self->client->ps.forcePowerLevel[FP_GRIP]>FORCE_LEVEL_1&&!self->s.number&&!(cmd->buttons&BUTTON_FORCEGRIP)) ) { WP_ForcePowerStop( self, FP_GRIP ); return; @@ -7010,7 +7558,12 @@ static void WP_ForcePowerRun( gentity_t *self, forcePowers_t forcePower, usercmd VectorSubtract( gripOrg, gripEntOrg, gripEnt->client->ps.velocity ); if ( self->client->ps.forcePowerLevel[FP_GRIP] > FORCE_LEVEL_2 ) {//level 2 just lifts them - VectorScale( gripEnt->client->ps.velocity, 10, gripEnt->client->ps.velocity ); + float gripDist = VectorNormalize( gripEnt->client->ps.velocity )/3.0f; + if ( gripDist < 5.0f ) + { + gripDist = 5.0f; + } + VectorScale( gripEnt->client->ps.velocity, (gripDist*gripDist), gripEnt->client->ps.velocity ); } } //stop them from thinking @@ -7130,7 +7683,21 @@ static void WP_ForcePowerRun( gentity_t *self, forcePowers_t forcePower, usercmd if ( cmd->buttons & BUTTON_FORCE_LIGHTNING ) {//holding it keeps it going self->client->ps.forcePowerDuration[FP_LIGHTNING] = level.time + 500; - NPC_SetAnim( self, SETANIM_TORSO, BOTH_FORCELIGHTNING_HOLD, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + if ( self->client->ps.torsoAnim == BOTH_FORCELIGHTNING_START ) + { + if ( !self->client->ps.torsoAnimTimer ) + { + NPC_SetAnim( self, SETANIM_TORSO, BOTH_FORCELIGHTNING_HOLD, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } + else + { + NPC_SetAnim( self, SETANIM_TORSO, BOTH_FORCELIGHTNING_START, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } + } + else + { + NPC_SetAnim( self, SETANIM_TORSO, BOTH_FORCELIGHTNING_HOLD, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } } } if ( !WP_ForcePowerAvailable( self, forcePower, 0 ) ) @@ -7295,19 +7862,19 @@ void WP_InitForcePowers( gentity_t *ent ) } else if ( ent->client->NPC_class == CLASS_TAVION || ( ent->client->NPC_class == CLASS_JEDI && ent->NPC->rank == RANK_COMMANDER ) ) {//Tavia or trainer Jedi - ent->client->ps.forcePowersKnown = ( 1 << FP_LEVITATION )|( 1 << FP_PUSH )|( 1 << FP_PULL )|( 1 << FP_GRIP )|( 1 << FP_SABERTHROW)|( 1 << FP_SPEED)|( 1 << FP_SABER_DEFENSE )|( 1 << FP_SABER_OFFENSE ); + ent->client->ps.forcePowersKnown = ( 1 << FP_LEVITATION )|( 1 << FP_PUSH )|( 1 << FP_PULL )|( 1 << FP_SABERTHROW)|( 1 << FP_SPEED)|( 1 << FP_SABER_DEFENSE )|( 1 << FP_SABER_OFFENSE ); ent->client->ps.forcePowerLevel[FP_LEVITATION] = FORCE_LEVEL_3; ent->client->ps.forcePowerLevel[FP_PUSH] = FORCE_LEVEL_3; ent->client->ps.forcePowerLevel[FP_PULL] = FORCE_LEVEL_2; ent->client->ps.forcePowerLevel[FP_SABERTHROW] = FORCE_LEVEL_3; - ent->client->ps.forcePowerLevel[FP_GRIP] = FORCE_LEVEL_2; ent->client->ps.forcePowerLevel[FP_SPEED] = FORCE_LEVEL_3; ent->client->ps.forcePowerLevel[FP_SABER_DEFENSE] = FORCE_LEVEL_3; ent->client->ps.forcePowerLevel[FP_SABER_OFFENSE] = FORCE_LEVEL_3; if ( ent->client->NPC_class == CLASS_TAVION ) { - ent->client->ps.forcePowersKnown |= ( 1 << FP_LIGHTNING); + ent->client->ps.forcePowersKnown |= ( 1 << FP_LIGHTNING)|( 1 << FP_GRIP ); ent->client->ps.forcePowerLevel[FP_LIGHTNING] = FORCE_LEVEL_2; + ent->client->ps.forcePowerLevel[FP_GRIP] = FORCE_LEVEL_2; } } else if ( ent->client->NPC_class == CLASS_SHADOWTROOPER ) @@ -7330,10 +7897,14 @@ void WP_InitForcePowers( gentity_t *ent ) ent->client->ps.forcePowerLevel[FP_LEVITATION] = FORCE_LEVEL_2; ent->client->ps.forcePowerLevel[FP_PUSH] = FORCE_LEVEL_2; ent->client->ps.forcePowerLevel[FP_PULL] = FORCE_LEVEL_1; - ent->client->ps.forcePowerLevel[FP_GRIP] = FORCE_LEVEL_2; ent->client->ps.forcePowerLevel[FP_SPEED] = FORCE_LEVEL_2; ent->client->ps.forcePowerLevel[FP_SABER_DEFENSE] = FORCE_LEVEL_3; ent->client->ps.forcePowerLevel[FP_SABER_OFFENSE] = FORCE_LEVEL_3; + if ( ent->client->NPC_class != CLASS_JEDI ) + { + ent->client->ps.forcePowersKnown |= ( 1 << FP_GRIP ); + ent->client->ps.forcePowerLevel[FP_GRIP] = FORCE_LEVEL_2; + } } else if ( ent->NPC->rank == RANK_LT_JG ) {//Reborn Fencer diff --git a/code/game/wp_saber.h b/code/game/wp_saber.h index 1490294..59c915b 100644 --- a/code/game/wp_saber.h +++ b/code/game/wp_saber.h @@ -52,7 +52,7 @@ extern void ForceSpeed( gentity_t *self, int duration = 0 ); extern float forceSpeedValue[]; extern float forceSpeedRangeMod[]; extern float forceSpeedFOVMod[]; -#define FORCE_SPEED_DURATION 15000.0f +#define FORCE_SPEED_DURATION 10000.0f typedef enum { @@ -79,6 +79,21 @@ typedef enum extern void WP_InitForcePowers( gentity_t *ent ); extern int WP_GetVelocityForForceJump( gentity_t *self, vec3_t jumpVel, usercmd_t *ucmd ); +typedef enum +{ + EVASION_NONE = 0, + EVASION_PARRY, + EVASION_DUCK_PARRY, + EVASION_JUMP_PARRY, + EVASION_DODGE, + EVASION_JUMP, + EVASION_DUCK, + EVASION_FJUMP, + EVASION_CARTWHEEL, + EVASION_OTHER, + NUM_EVASION_TYPES +} evasionType_t; + // Okay, here lies the much-dreaded Pat-created FSM movement chart... Heretic II strikes again! // Why am I inflicting this on you? Well, it's better than hardcoded states. // Ideally this will be replaced with an external file or more sophisticated move-picker diff --git a/code/ghoul2/g2.h b/code/ghoul2/g2.h index 25284a0..1eb66cb 100644 --- a/code/ghoul2/g2.h +++ b/code/ghoul2/g2.h @@ -17,54 +17,53 @@ class CMiniHeap; #define MODEL_SHIFT (BOLT_SHIFT + BOLT_WIDTH) #define ENTITY_SHIFT (MODEL_SHIFT + MODEL_WIDTH) +struct model_s; // internal surface calls G2_surfaces.cpp -qboolean G2_SetSurfaceOnOff (const char *fileName, surfaceInfo_v &slist, const char *surfaceName, const int offFlags); -int G2_IsSurfaceOff (const char *fileName, surfaceInfo_v &slist, const char *surfaceName); -qboolean G2_SetRootSurface( CGhoul2Info_v &ghoul2, const int modelIndex, const char *fileName, const char *surfaceName); +qboolean G2_SetSurfaceOnOff (CGhoul2Info *ghlInfo, surfaceInfo_v &slist, const char *surfaceName, const int offFlags); +qboolean G2_SetRootSurface( CGhoul2Info_v &ghoul2, const int modelIndex,const char *surfaceName); int G2_AddSurface(CGhoul2Info *ghoul2, int surfaceNumber, int polyNumber, float BarycentricI, float BarycentricJ, int lod ); qboolean G2_RemoveSurface(surfaceInfo_v &slist, const int index); const surfaceInfo_t *G2_FindOverrideSurface(int surfaceNum, const surfaceInfo_v &surfaceList); -int G2_IsSurfaceLegal(const void *mod, const char *surfaceName, int *flags); -int G2_GetParentSurface(const char *fileName, const int index); -int G2_GetSurfaceIndex(const char *fileName, const char *surfaceName); -int G2_IsSurfaceRendered(const char *fileName, const char *surfaceName, surfaceInfo_v &slist); +int G2_IsSurfaceLegal(const model_s *, const char *surfaceName, int *flags); +int G2_GetParentSurface(CGhoul2Info *ghlInfo, const int index); +int G2_GetSurfaceIndex(CGhoul2Info *ghlInfo, const char *surfaceName); +int G2_IsSurfaceRendered(CGhoul2Info *ghlInfo, const char *surfaceName, surfaceInfo_v &slist); // internal bone calls - G2_Bones.cpp -qboolean G2_Set_Bone_Angles(const char *fileName, boneInfo_v &blist, const char *boneName, const float *angles, const int flags, - const Eorientations up, const Eorientations left, const Eorientations forward, qhandle_t *modelList, - const int modelIndex, const int blendTime, const int currentTime); -qboolean G2_Remove_Bone (const char *fileName, boneInfo_v &blist, const char *boneName); -qboolean G2_Set_Bone_Anim(const char *fileName, boneInfo_v &blist, const char *boneName, const int startFrame, +qboolean G2_Set_Bone_Angles(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName, const float *angles, const int flags, + const Eorientations up, const Eorientations left, const Eorientations forward, + const int blendTime, const int currentTime); +qboolean G2_Remove_Bone (CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName); +qboolean G2_Set_Bone_Anim(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName, const int startFrame, const int endFrame, const int flags, const float animSpeed, const int currentTime, const float setFrame, const int blendTime); -qboolean G2_Get_Bone_Anim(const char *fileName, boneInfo_v &blist, const char *boneName, const int currentTime, - float *currentFrame, int *startFrame, int *endFrame, int *flags, float *retAnimSpeed, qhandle_t *modelList, int modelIndex); -qboolean G2_Get_Bone_Anim_Range(const char *fileName, boneInfo_v &blist, const char *boneName, int *startFrame, int *endFrame); +qboolean G2_Get_Bone_Anim(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName, const int currentTime, + float *currentFrame, int *startFrame, int *endFrame, int *flags, float *retAnimSpeed); +qboolean G2_Get_Bone_Anim_Range(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName, int *startFrame, int *endFrame); qboolean G2_Get_Bone_Anim_Range_Index(boneInfo_v &blist, const int boneIndex, int *startFrame, int *endFrame); -qboolean G2_Pause_Bone_Anim(const char *fileName, boneInfo_v &blist, const char *boneName, const int currentTime ); -qboolean G2_Pause_Bone_Anim_Index(boneInfo_v &blist, const int boneIndex, const int currentTime ); -qboolean G2_IsPaused(const char *fileName, boneInfo_v &blist, const char *boneName); -qboolean G2_Stop_Bone_Anim(const char *fileName, boneInfo_v &blist, const char *boneName); -qboolean G2_Stop_Bone_Angles(const char *fileName, boneInfo_v &blist, const char *boneName); +qboolean G2_Pause_Bone_Anim(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName, const int currentTime); +qboolean G2_Pause_Bone_Anim_Index(boneInfo_v &blist, const int boneIndex, const int currentTime,int numFrames ); +qboolean G2_IsPaused(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName); +qboolean G2_Stop_Bone_Anim(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName); +qboolean G2_Stop_Bone_Angles(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName); void G2_Animate_Bone_List(CGhoul2Info_v &ghoul2, const int currentTime, const int index); void G2_Init_Bone_List(boneInfo_v &blist); int G2_Find_Bone_In_List(boneInfo_v &blist, const int boneNum); -void G2_RemoveRedundantBoneOverrides(boneInfo_v &blist, int *activeBones); -qboolean G2_Set_Bone_Angles_Matrix(const char *fileName, boneInfo_v &blist, const char *boneName, const mdxaBone_t &matrix, - const int flags, qhandle_t *modelList, const int modelIndex, const int blendTime, const int currentTime); +qboolean G2_Set_Bone_Angles_Matrix(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName, const mdxaBone_t &matrix, + const int flags, const int blendTime, const int currentTime); int G2_Get_Bone_Index(CGhoul2Info *ghoul2, const char *boneName, qboolean bAddIfNotFound); -qboolean G2_Set_Bone_Angles_Index(const char *fileName, boneInfo_v &blist, const int index, +qboolean G2_Set_Bone_Angles_Index(CGhoul2Info *ghlInfo, boneInfo_v &blist, const int index, const float *angles, const int flags, const Eorientations yaw, - const Eorientations pitch, const Eorientations roll, qhandle_t *modelList, - const int modelIndex, const int blendTime, const int currentTime); + const Eorientations pitch, const Eorientations roll, + const int blendTime, const int currentTime); qboolean G2_Set_Bone_Angles_Matrix_Index(boneInfo_v &blist, const int index, - const mdxaBone_t &matrix, const int flags, qhandle_t *modelList, - const int modelIndex, const int blendTime, const int currentTime); + const mdxaBone_t &matrix, const int flags, + const int blendTime, const int currentTime); qboolean G2_Stop_Bone_Anim_Index(boneInfo_v &blist, const int index); qboolean G2_Stop_Bone_Angles_Index(boneInfo_v &blist, const int index); qboolean G2_Set_Bone_Anim_Index(boneInfo_v &blist, const int index, const int startFrame, - const int endFrame, const int flags, const float animSpeed, const int currentTime, const float setFrame, const int blendTime); + const int endFrame, const int flags, const float animSpeed, const int currentTime, const float setFrame, const int blendTime,int numFrames); qboolean G2_Get_Bone_Anim_Index( boneInfo_v &blist, const int index, const int currentTime, - float *currentFrame, int *startFrame, int *endFrame, int *flags, float *retAnimSpeed, qhandle_t *modelList, int modelIndex); + float *currentFrame, int *startFrame, int *endFrame, int *flags, float *retAnimSpeed,int numFrames); // misc functions G2_misc.cpp void G2_List_Model_Surfaces(const char *fileName); @@ -76,18 +75,17 @@ void G2_TransformModel(CGhoul2Info_v &ghoul2, const int frameNum, vec3_t scale, void G2_GenerateWorldMatrix(const vec3_t angles, const vec3_t origin); void TransformPoint (const vec3_t in, vec3_t out, mdxaBone_t *mat); void Inverse_Matrix(mdxaBone_t *src, mdxaBone_t *dest); -void *G2_FindSurface(const void *mod, int index, int lod); +void *G2_FindSurface(const model_s *, int index, int lod); qboolean G2_SaveGhoul2Models(CGhoul2Info_v &ghoul2, char **buffer, int *size); void G2_LoadGhoul2Model(CGhoul2Info_v &ghoul2, char *buffer); // internal bolt calls. G2_bolts.cpp -int G2_Add_Bolt(const char *fileName, boltInfo_v &bltlist, surfaceInfo_v &slist, const char *boneName); +int G2_Add_Bolt(CGhoul2Info *ghlInfo, boltInfo_v &bltlist, surfaceInfo_v &slist, const char *boneName); qboolean G2_Remove_Bolt (boltInfo_v &bltlist, int index); void G2_Init_Bolt_List(boltInfo_v &bltlist); int G2_Find_Bolt_Bone_Num(boltInfo_v &bltlist, const int boneNum); int G2_Find_Bolt_Surface_Num(boltInfo_v &bltlist, const int surfaceNum, const int flags); -int G2_Add_Bolt_Surf_Num(const char *fileName, boltInfo_v &bltlist, surfaceInfo_v &slist, const int surfNum); -void G2_RemoveRedundantBolts(boltInfo_v &bltlist, surfaceInfo_v &slist, int *activeSurfaces, int *activeBones); +int G2_Add_Bolt_Surf_Num(CGhoul2Info *ghlInfo, boltInfo_v &bltlist, surfaceInfo_v &slist, const int surfNum); // API calls - G2_API.cpp @@ -100,7 +98,6 @@ qboolean G2API_SetSkin(CGhoul2Info *ghlInfo, qhandle_t customSkin); qboolean G2API_SetShader(CGhoul2Info *ghlInfo, qhandle_t customShader); qboolean G2API_RemoveGhoul2Model(CGhoul2Info_v &ghlInfo, const int modelIndex); qboolean G2API_SetSurfaceOnOff(CGhoul2Info *ghlInfo, const char *surfaceName, const int flags); -int G2API_GetSurfaceOnOff(CGhoul2Info *ghlInfo, const char *surfaceName); qboolean G2API_SetRootSurface(CGhoul2Info_v &ghlInfo, const int modelIndex, const char *surfaceName); qboolean G2API_RemoveSurface(CGhoul2Info *ghlInfo, const int index); int G2API_AddSurface(CGhoul2Info *ghlInfo, int surfaceNumber, int polyNumber, float BarycentricI, float BarycentricJ, int lod ); @@ -176,4 +173,7 @@ void G2_GetBoltMatrixLow(CGhoul2Info &ghoul2,int boltNum,const vec3_t scale,mdx void G2_TimingModel(boneInfo_t &bone,int time,int numFramesInFile,int ¤tFrame,int &newFrame,float &lerp); +bool G2_SetupModelPointers(CGhoul2Info_v &ghoul2); // returns true if any model is properly set up +bool G2_SetupModelPointers(CGhoul2Info *ghlInfo); // returns true if the model is properly set up + #endif // G2_H_INC \ No newline at end of file diff --git a/code/ghoul2/g2_api.cpp b/code/ghoul2/g2_api.cpp index 64a08bc..00f22d3 100644 --- a/code/ghoul2/g2_api.cpp +++ b/code/ghoul2/g2_api.cpp @@ -27,6 +27,9 @@ #include "..\qcommon\MiniHeap.h" #endif +#ifdef _DEBUG + #include +#endif //_DEBUG using namespace std; @@ -35,13 +38,17 @@ extern mdxaBone_t worldMatrixInv; extern cvar_t *r_Ghoul2TimeBase; +#define G2_MODEL_OK(g) ((g)&&(g)->mValid&&(g)->aHeader&&(g)->currentModel&&(g)->animModel) + + #define G2_DEBUG_TIME (0) static int G2TimeBases[NUM_G2T_TIME]; +bool G2_TestModelPointers(CGhoul2Info *ghlInfo); #if _DEBUG -#define MAX_ERROR_PRINTS (25) +#define MAX_ERROR_PRINTS (3) class ErrorReporter { string mName; @@ -80,10 +87,12 @@ public: } else if (kind==1) { +// assert(!"G2API Warning"); full+=":WARNING: "; } else { +// assert(!"G2API Error"); full+=":ERROR : "; } full+=m; @@ -143,7 +152,7 @@ void G2_Bone_Not_Found(const char *boneName,const char *modName) G2ERROR(boneName[0],"Empty Bone Name"); if (boneName) { - G2WARNING(0,va("Bone Not Found (%s:%s)",boneName,modName)); + G2NOTE(0,va("Bone Not Found (%s:%s)",boneName,modName)); } } @@ -153,7 +162,7 @@ void G2_Bolt_Not_Found(const char *boneName,const char *modName) G2ERROR(boneName[0],"Empty Bolt/Bone Name"); if (boneName) { - G2WARNING(0,va("Bolt/Bone Not Found (%s:%s)",boneName,modName)); + G2NOTE(0,va("Bolt/Bone Not Found (%s:%s)",boneName,modName)); } } #endif @@ -179,6 +188,10 @@ int G2API_GetTime(int argTime) // this may or may not return arg depending on gh { timeBase=r_Ghoul2TimeBase->integer; ret=G2TimeBases[r_Ghoul2TimeBase->integer-1]; + if ( !ret ) + { + ret = G2TimeBases[0]; + } } #if _DEBUG if (LastTimeBase!=timeBase) @@ -285,25 +298,6 @@ public: } } #endif - void VidRestart() - { - int j; - for (j=0;j &ghoul2=mInfos[j]; - int i; - - for (i = 0; i < ghoul2.size(); i++) - { - // if we have a name, re-register us so the model thinks it's in the correct place. - if (ghoul2[i].mFileName[0]) - { - ghoul2[i].mModel = RE_RegisterModel(ghoul2[i].mFileName); - assert(ghoul2[i].mModel); - } - } - } - } int New() { if (mFreeIndecies.empty()) @@ -373,6 +367,15 @@ IGhoul2InfoArray &TheGhoul2InfoArray() return singleton; } +const vector &DebugG2Info(int handle) +{ + return TheGhoul2InfoArray().Get(handle); +} + +const CGhoul2Info &DebugG2InfoI(int handle,int item) +{ + return TheGhoul2InfoArray().Get(handle)[item]; +} // this is the ONLY function to read entity states directly void G2API_CleanGhoul2Models(CGhoul2Info_v &ghoul2) @@ -386,11 +389,10 @@ qhandle_t G2API_PrecacheGhoul2Model(const char *fileName) } // initialise all that needs to be on a new Ghoul II model -int G2API_InitGhoul2Model(CGhoul2Info_v &ghoul2, const char *fileName, int modelIndex, qhandle_t customSkin, +int G2API_InitGhoul2Model(CGhoul2Info_v &ghoul2, const char *fileName, int, qhandle_t customSkin, qhandle_t customShader, int modelFlags, int lodBias) { int model = -1; - CGhoul2Info newModel; G2ERROR(fileName&&fileName[0],"NULL filename"); @@ -405,62 +407,35 @@ int G2API_InitGhoul2Model(CGhoul2Info_v &ghoul2, const char *fileName, int model { if (ghoul2[model].mModelindex == -1) { - // this is only valid and used on the game side. Client side ignores this - ghoul2[model].mModelindex = modelIndex; - // on the game side this is valid. On the client side it is valid only after it has been filled in by trap_G2_SetGhoul2ModelIndexes - ghoul2[model].mModel = RE_RegisterModel((char *)fileName); - model_t *mod_m = R_GetModelByHandle(ghoul2[model].mModel); - G2ERROR(mod_m->type != MOD_BAD,va("Bad Model Type (%s)",fileName)); - if (mod_m->type == MOD_BAD) - { - return -1; - } - - // init what is necessary for this ghoul2 model - G2_Init_Bone_List(ghoul2[model].mBlist); - G2_Init_Bolt_List(ghoul2[model].mBltlist); - ghoul2[model].mCustomShader = customShader; - ghoul2[model].mCustomSkin = customSkin; - strcpy(ghoul2[model].mFileName, fileName); - ghoul2[model].mLodBias = lodBias; - ghoul2[model].mAnimFrameDefault = 0; - ghoul2[model].mFlags = 0; - - // we aren't attached to anyone upfront - ghoul2[model].mModelBoltLink = -1; - return model; + ghoul2[model]=CGhoul2Info(); + break; } } - - // if we got this far, then we didn't find a spare position, so lets insert a new one - newModel.mModelindex = modelIndex; - // on the game side this is valid. On the client side it is valid only after it has been filled in by trap_G2_SetGhoul2ModelIndexes - newModel.mModel = RE_RegisterModel((char *)fileName); - model_t *mod_m = R_GetModelByHandle(newModel.mModel); - G2ERROR(mod_m->type != MOD_BAD,va("Bad Model Type (%s)",fileName)); - if (mod_m->type == MOD_BAD) + if (model==ghoul2.size()) { - return -1; + ghoul2.push_back(CGhoul2Info()); } - - // init what is necessary for this ghoul2 model - G2_Init_Bone_List(newModel.mBlist); - G2_Init_Bolt_List(newModel.mBltlist); - newModel.mCustomShader = customShader; - newModel.mCustomSkin = customSkin; - strcpy(newModel.mFileName, fileName); - newModel.mLodBias = lodBias; - newModel.mAnimFrameDefault = 0; - newModel.mFlags = 0; - // we aren't attached to anyone upfront - newModel.mModelBoltLink = -1; + strcpy(ghoul2[model].mFileName, fileName); + ghoul2[model].mModelindex = model; + if (!G2_TestModelPointers(&ghoul2[model])) + { + ghoul2[model].mFileName[0]=0; + ghoul2[model].mModelindex = -1; + } + else + { + G2_Init_Bone_List(ghoul2[model].mBlist); + G2_Init_Bolt_List(ghoul2[model].mBltlist); + ghoul2[model].mCustomShader = customShader; + ghoul2[model].mCustomSkin = customSkin; + ghoul2[model].mLodBias = lodBias; + ghoul2[model].mAnimFrameDefault = 0; + ghoul2[model].mFlags = 0; - // insert into list. - model = ghoul2.size(); - ghoul2.push_back(newModel); - - return model; + ghoul2[model].mModelBoltLink = -1; + } + return ghoul2[model].mModelindex; } qboolean G2API_SetLodBias(CGhoul2Info *ghlInfo, int lodBias) @@ -498,36 +473,27 @@ qboolean G2API_SetShader(CGhoul2Info *ghlInfo, qhandle_t customShader) qboolean G2API_SetSurfaceOnOff(CGhoul2Info *ghlInfo, const char *surfaceName, const int flags) { - G2ERROR(ghlInfo,"NULL ghlInfo"); - if (ghlInfo) + if (G2_SetupModelPointers(ghlInfo)) { + G2ERROR(!(flags&~(G2SURFACEFLAG_OFF | G2SURFACEFLAG_NODESCENDANTS)),"G2API_SetSurfaceOnOff Illegal Flags"); // ensure we flush the cache ghlInfo->mMeshFrameNum = 0; - return G2_SetSurfaceOnOff(ghlInfo->mFileName, ghlInfo->mSlist, surfaceName, flags); + return G2_SetSurfaceOnOff(ghlInfo, ghlInfo->mSlist, surfaceName, flags); } return qfalse; } -int G2API_GetSurfaceOnOff(CGhoul2Info *ghlInfo, const char *surfaceName) -{ - G2ERROR(ghlInfo,"NULL ghlInfo"); - if (ghlInfo) - { - return G2_IsSurfaceOff(ghlInfo->mFileName, ghlInfo->mSlist, surfaceName); - } - return -1; -} qboolean G2API_SetRootSurface(CGhoul2Info_v &ghlInfo, const int modelIndex, const char *surfaceName) { G2ERROR(ghlInfo.IsValid(),"Invalid ghlInfo"); G2ERROR(surfaceName,"Invalid surfaceName"); - if (ghlInfo.IsValid()) + if (G2_SetupModelPointers(ghlInfo)) { G2ERROR(modelIndex>=0&&modelIndex=0&&modelIndexmMeshFrameNum = 0; @@ -548,8 +512,7 @@ int G2API_AddSurface(CGhoul2Info *ghlInfo, int surfaceNumber, int polyNumber, fl qboolean G2API_RemoveSurface(CGhoul2Info *ghlInfo, const int index) { - G2ERROR(ghlInfo,"NULL ghlInfo"); - if (ghlInfo) + if (G2_SetupModelPointers(ghlInfo)) { // ensure we flush the cache ghlInfo->mMeshFrameNum = 0; @@ -560,21 +523,19 @@ qboolean G2API_RemoveSurface(CGhoul2Info *ghlInfo, const int index) int G2API_GetParentSurface(CGhoul2Info *ghlInfo, const int index) { - G2ERROR(ghlInfo,"NULL ghlInfo"); - if (ghlInfo) + if (G2_SetupModelPointers(ghlInfo)) { - return G2_GetParentSurface(ghlInfo->mFileName, index); + return G2_GetParentSurface(ghlInfo, index); } return -1; } int G2API_GetSurfaceRenderStatus(CGhoul2Info *ghlInfo, const char *surfaceName) { - G2ERROR(ghlInfo,"NULL ghlInfo"); G2ERROR(surfaceName,"Invalid surfaceName"); - if (ghlInfo) + if (G2_SetupModelPointers(ghlInfo)) { - return G2_IsSurfaceRendered(ghlInfo->mFileName, surfaceName, ghlInfo->mSlist); + return G2_IsSurfaceRendered(ghlInfo, surfaceName, ghlInfo->mSlist); } return -1; } @@ -590,197 +551,142 @@ qboolean G2API_RemoveGhoul2Model(CGhoul2Info_v &ghlInfo, const int modelIndex) assert(0 && "remove non existing model"); return qfalse; } - - // clear out the vectors this model used. - ghlInfo[modelIndex].mBlist.clear(); - ghlInfo[modelIndex].mBltlist.clear(); - ghlInfo[modelIndex].mSlist.clear(); - RemoveBoneCache(ghlInfo[modelIndex].mBoneCache); ghlInfo[modelIndex].mBoneCache=0; + // set us to be the 'not active' state ghlInfo[modelIndex].mModelindex = -1; + ghlInfo[modelIndex].mFileName[0]=0; - int newSize = ghlInfo.size(); - // now look through the list from the back and see if there is a block of -1's we can resize off the end of the list - for (int i=ghlInfo.size()-1; i>-1; i--) - { - if (ghlInfo[i].mModelindex == -1) - { - newSize = i; - } - // once we hit one that isn't a -1, we are done. - else - { - break; - } - } - // do we need to resize? - if (newSize != ghlInfo.size()) - { - // yes, so lets do it - ghlInfo.resize(newSize); - } - + ghlInfo[modelIndex]=CGhoul2Info(); return qtrue; } + qboolean G2API_SetBoneAnimIndex(CGhoul2Info *ghlInfo, const int index, const int startFrame, const int endFrame, const int flags, const float animSpeed, const int currentTime, const float setFrame, const int blendTime) { qboolean ret=qfalse; - G2ERROR(ghlInfo,"NULL ghlInfo"); - if (ghlInfo) + if (G2_SetupModelPointers(ghlInfo)) { -#if _DEBUG - ghlInfo->mModel = RE_RegisterModel(ghlInfo->mFileName); - const model_t *currentModel = R_GetModelByHandle(ghlInfo->mModel); - G2ERROR(currentModel&¤tModel->mdxm,va("Bad Model (glm) %s",ghlInfo->mFileName)); - if (currentModel&¤tModel->mdxm) + G2ERROR(startFrame>=0,"startframe<0"); + G2ERROR(startFrameaHeader->numFrames,"startframe>=numframes"); + G2ERROR(endFrame>0,"endframe<=0"); + G2ERROR(endFrame<=ghlInfo->aHeader->numFrames,"endframe>numframes"); + G2ERROR(setFrameaHeader->numFrames,"setframe>=numframes"); + G2ERROR(setFrame==-1.0f||setFrame>=0.0f,"setframe<0 but not -1"); + if (startFrame<0||startFrame>=ghlInfo->aHeader->numFrames) { - const model_t *animModel = R_GetModelByHandle(currentModel->mdxm->animIndex); - G2ERROR(animModel,va("Bad Model (gla) %s",ghlInfo->mFileName)); - if (animModel) - { - const mdxaHeader_t *aHeader = animModel->mdxa; - G2ERROR(aHeader,va("Bad Model (gla) %s",ghlInfo->mFileName)); - if (aHeader) - { - G2ERROR(startFrame>=0,"startframe<0"); - G2ERROR(startFramenumFrames,"startframe>=numframes"); - G2ERROR(endFrame>0,"endframe<=0"); - G2ERROR(endFrame<=aHeader->numFrames,"endframe>numframes"); - G2ERROR(setFramenumFrames,"setframe>=numframes"); - G2ERROR(setFrame==-1.0f||setFrame>=0.0f,"setframe<0 but not -1"); - -#else + *(int *)&startFrame=0; // cast away const + } + if (endFrame<=0||endFrame>ghlInfo->aHeader->numFrames) { - { - { -#endif - if (startFrame<0)//||startFrame>=aHeader->numFrames) - { - *(int *)&startFrame=0; // cast away const - } - if (endFrame<=0)//||endFrame>aHeader->numFrames) - { - *(int *)&endFrame=1; - } - if (setFrame!=-1.0f&&(setFrame<0.0f/*||setFrame>=(float)aHeader->numFrames*/)) - { - *(float *)&setFrame=0.0f; - } - ghlInfo->mSkelFrameNum = 0; - G2ERROR(index>=0&&indexmBlist.size(),"Bad Bone Index"); - if (index>=0&&indexmBlist.size()) - { - G2ERROR(ghlInfo->mBlist[index].boneNumber>=0,"Bad Bone Index"); - ret=G2_Set_Bone_Anim_Index(ghlInfo->mBlist, index, startFrame, endFrame, flags, animSpeed, currentTime, setFrame, blendTime); - } - } - } + *(int *)&endFrame=1; + } + if (setFrame!=-1.0f&&(setFrame<0.0f||setFrame>=(float)ghlInfo->aHeader->numFrames)) + { + *(float *)&setFrame=0.0f; + } + ghlInfo->mSkelFrameNum = 0; + G2ERROR(index>=0&&indexmBlist.size(),va("Out of Range Bone Index (%s)",ghlInfo->mFileName)); + if (index>=0&&indexmBlist.size()) + { + G2ERROR(ghlInfo->mBlist[index].boneNumber>=0,va("Bone Index is not Active (%s)",ghlInfo->mFileName)); + ret=G2_Set_Bone_Anim_Index(ghlInfo->mBlist, index, startFrame, endFrame, flags, animSpeed, currentTime, setFrame, blendTime,ghlInfo->aHeader->numFrames); } } - G2WARNING(ret,"G2API_SetBoneAnimIndex Failed"); + G2WARNING(ret,va("G2API_SetBoneAnimIndex Failed (%s)",ghlInfo->mFileName)); return ret; } qboolean G2API_SetBoneAnim(CGhoul2Info *ghlInfo, const char *boneName, const int startFrame, const int endFrame, const int flags, const float animSpeed, const int currentTime, const float setFrame, const int blendTime) { qboolean ret=qfalse; - G2ERROR(ghlInfo,"NULL ghlInfo"); G2ERROR(boneName,"NULL boneName"); - if (ghlInfo&&boneName) + if (boneName&&G2_SetupModelPointers(ghlInfo)) { -#if _DEBUG - ghlInfo->mModel = RE_RegisterModel(ghlInfo->mFileName); - const model_t *currentModel = R_GetModelByHandle(ghlInfo->mModel); - G2ERROR(currentModel&¤tModel->mdxm,va("Bad Model (glm) %s",ghlInfo->mFileName)); - if (currentModel&¤tModel->mdxm) + G2ERROR(startFrame>=0,"startframe<0"); + G2ERROR(startFrameaHeader->numFrames,"startframe>=numframes"); + G2ERROR(endFrame>0,"endframe<=0"); + G2ERROR(endFrame<=ghlInfo->aHeader->numFrames,"endframe>numframes"); + G2ERROR(setFrameaHeader->numFrames,"setframe>=numframes"); + G2ERROR(setFrame==-1.0f||setFrame>=0.0f,"setframe<0 but not -1"); + if (startFrame<0||startFrame>=ghlInfo->aHeader->numFrames) { - const model_t *animModel = R_GetModelByHandle(currentModel->mdxm->animIndex); - G2ERROR(animModel,va("Bad Model (gla) %s",ghlInfo->mFileName)); - if (animModel) - { - const mdxaHeader_t *aHeader = animModel->mdxa; - G2ERROR(aHeader,va("Bad Model (gla) %s",ghlInfo->mFileName)); - if (aHeader) - { - G2ERROR(startFrame>=0,"startframe<0"); - G2ERROR(startFramenumFrames,"startframe>=numframes"); - G2ERROR(endFrame>0,"endframe<=0"); - G2ERROR(endFrame<=aHeader->numFrames,"endframe>numframes"); - G2ERROR(setFramenumFrames,"setframe>=numframes"); - G2ERROR(setFrame==-1.0f||setFrame>=0.0f,"setframe<0 but not -1"); - -#else - { - { - { -#endif - if (startFrame<0)//||startFrame>=aHeader->numFrames) - { - *(int *)&startFrame=0; // cast away const - } - if (endFrame<=0)//||endFrame>aHeader->numFrames) - { - *(int *)&endFrame=1; - } - if (setFrame!=-1.0f&&(setFrame<0.0f/*||setFrame>=(float)aHeader->numFrames*/)) - { - *(float *)&setFrame=0.0f; - } - // ensure we flush the cache - ghlInfo->mSkelFrameNum = 0; - ret=G2_Set_Bone_Anim(ghlInfo->mFileName, ghlInfo->mBlist, boneName, startFrame, endFrame, flags, animSpeed, currentTime, setFrame, blendTime); - } - } + *(int *)&startFrame=0; // cast away const } + if (endFrame<=0||endFrame>ghlInfo->aHeader->numFrames) + { + *(int *)&endFrame=1; + } + if (setFrame!=-1.0f&&(setFrame<0.0f||setFrame>=(float)ghlInfo->aHeader->numFrames)) + { + *(float *)&setFrame=0.0f; + } + ghlInfo->mSkelFrameNum = 0; + ret=G2_Set_Bone_Anim(ghlInfo, ghlInfo->mBlist, boneName, startFrame, endFrame, flags, animSpeed, currentTime, setFrame, blendTime); } G2WARNING(ret,"G2API_SetBoneAnim Failed"); return ret; } qboolean G2API_GetBoneAnim(CGhoul2Info *ghlInfo, const char *boneName, const int AcurrentTime, float *currentFrame, - int *startFrame, int *endFrame, int *flags, float *animSpeed, int *modelList) + int *startFrame, int *endFrame, int *flags, float *animSpeed, int *) { qboolean ret=qfalse; - G2ERROR(ghlInfo,"NULL ghlInfo"); G2ERROR(boneName,"NULL boneName"); - int currentTime=G2API_GetTime(AcurrentTime); - if (ghlInfo&&boneName) + if (G2_SetupModelPointers(ghlInfo)) { - ret=G2_Get_Bone_Anim(ghlInfo->mFileName, ghlInfo->mBlist, boneName, currentTime, currentFrame, - startFrame, endFrame, flags, animSpeed, modelList, ghlInfo->mModelindex); + int currentTime=G2API_GetTime(AcurrentTime); + ret=G2_Get_Bone_Anim(ghlInfo, ghlInfo->mBlist, boneName, currentTime, currentFrame, + startFrame, endFrame, flags, animSpeed); } G2WARNING(ret,"G2API_GetBoneAnim Failed"); return ret; } qboolean G2API_GetBoneAnimIndex(CGhoul2Info *ghlInfo, const int iBoneIndex, const int AcurrentTime, float *currentFrame, - int *startFrame, int *endFrame, int *flags, float *animSpeed, int *modelList) + int *startFrame, int *endFrame, int *flags, float *animSpeed, int *) { qboolean ret=qfalse; - G2ERROR(ghlInfo,"NULL ghlInfo"); - int currentTime=G2API_GetTime(AcurrentTime); - if (ghlInfo) + if (G2_SetupModelPointers(ghlInfo)) { - G2ERROR(iBoneIndex>=0&&iBoneIndexmBlist.size(),"Bad Bone Index"); - G2WARNING(ghlInfo->mBlist[iBoneIndex].flags & (BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE),"GetBoneAnim on non-animating bone."); - if (iBoneIndex>=0&&iBoneIndexmBlist.size()&&(ghlInfo->mBlist[iBoneIndex].flags & (BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE))) + int currentTime=G2API_GetTime(AcurrentTime); + G2ERROR(iBoneIndex>=0&&iBoneIndexmBlist.size(),va("Bad Bone Index (%d:%s)",iBoneIndex,ghlInfo->mFileName)); + if (iBoneIndex>=0&&iBoneIndexmBlist.size()) { - ret=G2_Get_Bone_Anim_Index( ghlInfo->mBlist,// boneInfo_v &blist, - iBoneIndex, // const int index, - currentTime, // const int currentTime, - currentFrame, // float *currentFrame, - startFrame, // int *startFrame, - endFrame, // int *endFrame, - flags, // int *flags, - animSpeed, // float *retAnimSpeed, - modelList, // qhandle_t *modelList, - ghlInfo->mModelindex // int modelIndex - ); + G2NOTE(ghlInfo->mBlist[iBoneIndex].flags & (BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE),"GetBoneAnim on non-animating bone."); + if ((ghlInfo->mBlist[iBoneIndex].flags & (BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE))) + { + int sf,ef; + ret=G2_Get_Bone_Anim_Index( ghlInfo->mBlist,// boneInfo_v &blist, + iBoneIndex, // const int index, + currentTime, // const int currentTime, + currentFrame, // float *currentFrame, + &sf, // int *startFrame, + &ef, // int *endFrame, + flags, // int *flags, + animSpeed, // float *retAnimSpeed, + ghlInfo->aHeader->numFrames + ); + G2ERROR(sf>=0,"returning startframe<0"); + G2ERROR(sfaHeader->numFrames,"returning startframe>=numframes"); + G2ERROR(ef>0,"returning endframe<=0"); + G2ERROR(ef<=ghlInfo->aHeader->numFrames,"returning endframe>numframes"); + G2ERROR(*currentFrame>=0.0f,"returning currentframe<0"); + G2ERROR(((int)(*currentFrame))aHeader->numFrames,"returning currentframe>=numframes"); + *endFrame=ef; + *startFrame=sf; + } } } + if (!ret) + { + *endFrame=1; + *startFrame=0; + *flags=0; + *currentFrame=0.0f; + *animSpeed=1.0f; + } G2NOTE(ret,"G2API_GetBoneAnimIndex Failed"); return ret; } @@ -788,11 +694,10 @@ qboolean G2API_GetBoneAnimIndex(CGhoul2Info *ghlInfo, const int iBoneIndex, cons qboolean G2API_GetAnimRange(CGhoul2Info *ghlInfo, const char *boneName, int *startFrame, int *endFrame) { qboolean ret=qfalse; - G2ERROR(ghlInfo,"NULL ghlInfo"); G2ERROR(boneName,"NULL boneName"); - if (ghlInfo&&boneName) + if (boneName&&G2_SetupModelPointers(ghlInfo)) { - ret=G2_Get_Bone_Anim_Range(ghlInfo->mFileName, ghlInfo->mBlist, boneName, startFrame, endFrame); + ret=G2_Get_Bone_Anim_Range(ghlInfo, ghlInfo->mBlist, boneName, startFrame, endFrame); } // looks like the game checks the return value // G2WARNING(ret,"G2API_GetAnimRange Failed"); @@ -802,8 +707,7 @@ qboolean G2API_GetAnimRange(CGhoul2Info *ghlInfo, const char *boneName, int *sta qboolean G2API_GetAnimRangeIndex(CGhoul2Info *ghlInfo, const int boneIndex, int *startFrame, int *endFrame) { qboolean ret=qfalse; - G2ERROR(ghlInfo,"NULL ghlInfo"); - if (ghlInfo) + if (G2_SetupModelPointers(ghlInfo)) { G2ERROR(boneIndex>=0&&boneIndexmBlist.size(),"Bad Bone Index"); if (boneIndex>=0&&boneIndexmBlist.size()) @@ -819,28 +723,26 @@ qboolean G2API_GetAnimRangeIndex(CGhoul2Info *ghlInfo, const int boneIndex, int qboolean G2API_PauseBoneAnim(CGhoul2Info *ghlInfo, const char *boneName, const int AcurrentTime) { qboolean ret=qfalse; - G2ERROR(ghlInfo,"NULL ghlInfo"); G2ERROR(boneName,"NULL boneName"); - int currentTime=G2API_GetTime(AcurrentTime); - if (ghlInfo&&boneName) + if (boneName&&G2_SetupModelPointers(ghlInfo)) { - ret=G2_Pause_Bone_Anim(ghlInfo->mFileName, ghlInfo->mBlist, boneName, currentTime); + int currentTime=G2API_GetTime(AcurrentTime); + ret=G2_Pause_Bone_Anim(ghlInfo, ghlInfo->mBlist, boneName, currentTime); } - G2WARNING(ret,"G2API_PauseBoneAnim Failed"); + G2NOTE(ret,"G2API_PauseBoneAnim Failed"); return ret; } qboolean G2API_PauseBoneAnimIndex(CGhoul2Info *ghlInfo, const int boneIndex, const int AcurrentTime) { qboolean ret=qfalse; - G2ERROR(ghlInfo,"NULL ghlInfo"); - int currentTime=G2API_GetTime(AcurrentTime); - if (ghlInfo) + if (G2_SetupModelPointers(ghlInfo)) { + int currentTime=G2API_GetTime(AcurrentTime); G2ERROR(boneIndex>=0&&boneIndexmBlist.size(),"Bad Bone Index"); if (boneIndex>=0&&boneIndexmBlist.size()) { - ret=G2_Pause_Bone_Anim_Index(ghlInfo->mBlist, boneIndex, currentTime); + ret=G2_Pause_Bone_Anim_Index(ghlInfo->mBlist, boneIndex, currentTime,ghlInfo->aHeader->numFrames); } } G2WARNING(ret,"G2API_PauseBoneAnimIndex Failed"); @@ -850,11 +752,10 @@ qboolean G2API_PauseBoneAnimIndex(CGhoul2Info *ghlInfo, const int boneIndex, con qboolean G2API_IsPaused(CGhoul2Info *ghlInfo, const char *boneName) { qboolean ret=qfalse; - G2ERROR(ghlInfo,"NULL ghlInfo"); G2ERROR(boneName,"NULL boneName"); - if (ghlInfo&&boneName) + if (boneName&&G2_SetupModelPointers(ghlInfo)) { - ret=G2_IsPaused(ghlInfo->mFileName, ghlInfo->mBlist, boneName); + ret=G2_IsPaused(ghlInfo, ghlInfo->mBlist, boneName); } G2WARNING(ret,"G2API_IsPaused Failed"); return ret; @@ -864,7 +765,7 @@ qboolean G2API_StopBoneAnimIndex(CGhoul2Info *ghlInfo, const int index) { qboolean ret=qfalse; G2ERROR(ghlInfo,"NULL ghlInfo"); - if (ghlInfo) + if (G2_SetupModelPointers(ghlInfo)) { G2ERROR(index>=0&&indexmBlist.size(),"Bad Bone Index"); if (index>=0&&indexmBlist.size()) @@ -872,18 +773,17 @@ qboolean G2API_StopBoneAnimIndex(CGhoul2Info *ghlInfo, const int index) ret=G2_Stop_Bone_Anim_Index(ghlInfo->mBlist, index); } } - G2WARNING(ret,"G2API_StopBoneAnimIndex Failed"); + //G2WARNING(ret,"G2API_StopBoneAnimIndex Failed"); return ret; } qboolean G2API_StopBoneAnim(CGhoul2Info *ghlInfo, const char *boneName) { qboolean ret=qfalse; - G2ERROR(ghlInfo,"NULL ghlInfo"); G2ERROR(boneName,"NULL boneName"); - if (ghlInfo&&boneName) + if (boneName&&G2_SetupModelPointers(ghlInfo)) { - ret=G2_Stop_Bone_Anim(ghlInfo->mFileName, ghlInfo->mBlist, boneName); + ret=G2_Stop_Bone_Anim(ghlInfo, ghlInfo->mBlist, boneName); } G2WARNING(ret,"G2API_StopBoneAnim Failed"); return ret; @@ -891,16 +791,19 @@ qboolean G2API_StopBoneAnim(CGhoul2Info *ghlInfo, const char *boneName) qboolean G2API_SetBoneAnglesIndex(CGhoul2Info *ghlInfo, const int index, const vec3_t angles, const int flags, const Eorientations yaw, const Eorientations pitch, const Eorientations roll, - qhandle_t *modelList, int blendTime, int AcurrentTime) + qhandle_t *, int blendTime, int AcurrentTime) { qboolean ret=qfalse; - G2ERROR(ghlInfo,"NULL ghlInfo"); - int currentTime=G2API_GetTime(AcurrentTime); - if (ghlInfo) + if (G2_SetupModelPointers(ghlInfo)) { + int currentTime=G2API_GetTime(AcurrentTime); // ensure we flush the cache ghlInfo->mSkelFrameNum = 0; - ret=G2_Set_Bone_Angles_Index(ghlInfo->mFileName, ghlInfo->mBlist, index, angles, flags, yaw, pitch, roll, modelList, ghlInfo->mModelindex, blendTime, currentTime); + G2ERROR(index>=0&&indexmBlist.size(),"G2API_SetBoneAnglesIndex:Invalid bone index"); + if (index>=0&&indexmBlist.size()) + { + ret=G2_Set_Bone_Angles_Index(ghlInfo, ghlInfo->mBlist, index, angles, flags, yaw, pitch, roll,blendTime, currentTime); + } } G2WARNING(ret,"G2API_SetBoneAnglesIndex Failed"); return ret; @@ -908,36 +811,34 @@ qboolean G2API_SetBoneAnglesIndex(CGhoul2Info *ghlInfo, const int index, const v qboolean G2API_SetBoneAngles(CGhoul2Info *ghlInfo, const char *boneName, const vec3_t angles, const int flags, const Eorientations up, const Eorientations left, const Eorientations forward, - qhandle_t *modelList, int blendTime, int AcurrentTime ) + qhandle_t *, int blendTime, int AcurrentTime ) { qboolean ret=qfalse; - G2ERROR(ghlInfo,"NULL ghlInfo"); - int currentTime=G2API_GetTime(AcurrentTime); G2ERROR(boneName,"NULL boneName"); - if (ghlInfo&&boneName) + if (boneName&&G2_SetupModelPointers(ghlInfo)) { + int currentTime=G2API_GetTime(AcurrentTime); // ensure we flush the cache ghlInfo->mSkelFrameNum = 0; - ret=G2_Set_Bone_Angles(ghlInfo->mFileName, ghlInfo->mBlist, boneName, angles, flags, up, left, forward, modelList, ghlInfo->mModelindex, blendTime, currentTime); + ret=G2_Set_Bone_Angles(ghlInfo, ghlInfo->mBlist, boneName, angles, flags, up, left, forward, blendTime, currentTime); } G2WARNING(ret,"G2API_SetBoneAngles Failed"); return ret; } qboolean G2API_SetBoneAnglesMatrixIndex(CGhoul2Info *ghlInfo, const int index, const mdxaBone_t &matrix, - const int flags, qhandle_t *modelList, int blendTime, int AcurrentTime) + const int flags, qhandle_t *, int blendTime, int AcurrentTime) { qboolean ret=qfalse; - G2ERROR(ghlInfo,"NULL ghlInfo"); - int currentTime=G2API_GetTime(AcurrentTime); - if (ghlInfo) + if (G2_SetupModelPointers(ghlInfo)) { + int currentTime=G2API_GetTime(AcurrentTime); // ensure we flush the cache ghlInfo->mSkelFrameNum = 0; G2ERROR(index>=0&&indexmBlist.size(),"Bad Bone Index"); if (index>=0&&indexmBlist.size()) { - ret=G2_Set_Bone_Angles_Matrix_Index(ghlInfo->mBlist, index, matrix, flags, modelList, ghlInfo->mModelindex, blendTime, currentTime); + ret=G2_Set_Bone_Angles_Matrix_Index(ghlInfo->mBlist, index, matrix, flags, blendTime, currentTime); } } G2WARNING(ret,"G2API_SetBoneAnglesMatrixIndex Failed"); @@ -948,14 +849,13 @@ qboolean G2API_SetBoneAnglesMatrix(CGhoul2Info *ghlInfo, const char *boneName, c const int flags, qhandle_t *modelList, int blendTime, int AcurrentTime) { qboolean ret=qfalse; - G2ERROR(ghlInfo,"NULL ghlInfo"); - int currentTime=G2API_GetTime(AcurrentTime); G2ERROR(boneName,"NULL boneName"); - if (ghlInfo&&boneName) + if (boneName&&G2_SetupModelPointers(ghlInfo)) { + int currentTime=G2API_GetTime(AcurrentTime); // ensure we flush the cache ghlInfo->mSkelFrameNum = 0; - ret=G2_Set_Bone_Angles_Matrix(ghlInfo->mFileName, ghlInfo->mBlist, boneName, matrix, flags, modelList, ghlInfo->mModelindex, blendTime, currentTime); + ret=G2_Set_Bone_Angles_Matrix(ghlInfo, ghlInfo->mBlist, boneName, matrix, flags, blendTime, currentTime); } G2WARNING(ret,"G2API_SetBoneAnglesMatrix Failed"); return ret; @@ -964,8 +864,7 @@ qboolean G2API_SetBoneAnglesMatrix(CGhoul2Info *ghlInfo, const char *boneName, c qboolean G2API_StopBoneAnglesIndex(CGhoul2Info *ghlInfo, const int index) { qboolean ret=qfalse; - G2ERROR(ghlInfo,"NULL ghlInfo"); - if (ghlInfo) + if (G2_SetupModelPointers(ghlInfo)) { // ensure we flush the cache ghlInfo->mSkelFrameNum = 0; @@ -982,13 +881,12 @@ qboolean G2API_StopBoneAnglesIndex(CGhoul2Info *ghlInfo, const int index) qboolean G2API_StopBoneAngles(CGhoul2Info *ghlInfo, const char *boneName) { qboolean ret=qfalse; - G2ERROR(ghlInfo,"NULL ghlInfo"); G2ERROR(boneName,"NULL boneName"); - if (ghlInfo&&boneName) + if (boneName&&G2_SetupModelPointers(ghlInfo)) { // ensure we flush the cache ghlInfo->mSkelFrameNum = 0; - ret=G2_Stop_Bone_Angles(ghlInfo->mFileName, ghlInfo->mBlist, boneName); + ret=G2_Stop_Bone_Angles(ghlInfo, ghlInfo->mBlist, boneName); } G2WARNING(ret,"G2API_StopBoneAngles Failed"); return ret; @@ -997,13 +895,12 @@ qboolean G2API_StopBoneAngles(CGhoul2Info *ghlInfo, const char *boneName) qboolean G2API_RemoveBone(CGhoul2Info *ghlInfo, const char *boneName) { qboolean ret=qfalse; - G2ERROR(ghlInfo,"NULL ghlInfo"); G2ERROR(boneName,"NULL boneName"); - if (ghlInfo&&boneName) + if (boneName&&G2_SetupModelPointers(ghlInfo)) { // ensure we flush the cache ghlInfo->mSkelFrameNum = 0; - ret=G2_Remove_Bone(ghlInfo->mFileName, ghlInfo->mBlist, boneName); + ret=G2_Remove_Bone(ghlInfo, ghlInfo->mBlist, boneName); } G2WARNING(ret,"G2API_RemoveBone Failed"); return ret; @@ -1012,14 +909,14 @@ qboolean G2API_RemoveBone(CGhoul2Info *ghlInfo, const char *boneName) void G2API_AnimateG2Models(CGhoul2Info_v &ghoul2, float speedVar) { int model; - G2ERROR(ghoul2.IsValid(),"Invalid ghlInfo"); - - // Walk the list and find all models that are active - for (model=0; model< ghoul2.size(); model++) + if (ghoul2.IsValid()) { - if (ghoul2[model].mModel) + for (model=0; model< ghoul2.size(); model++) { - G2_Animate_Bone_List(ghoul2, speedVar, model); + if (ghoul2[model].mModel) + { + G2_Animate_Bone_List(ghoul2, speedVar, model); + } } } } @@ -1027,8 +924,7 @@ void G2API_AnimateG2Models(CGhoul2Info_v &ghoul2, float speedVar) qboolean G2API_RemoveBolt(CGhoul2Info *ghlInfo, const int index) { qboolean ret=qfalse; - G2ERROR(ghlInfo,"NULL ghlInfo"); - if (ghlInfo) + if (G2_SetupModelPointers(ghlInfo)) { ret=G2_Remove_Bolt( ghlInfo->mBltlist, index); } @@ -1039,23 +935,21 @@ qboolean G2API_RemoveBolt(CGhoul2Info *ghlInfo, const int index) int G2API_AddBolt(CGhoul2Info *ghlInfo, const char *boneName) { int ret=-1; - G2ERROR(ghlInfo,"NULL ghlInfo"); G2ERROR(boneName,"NULL boneName"); - if (ghlInfo&&boneName) + if (boneName&&G2_SetupModelPointers(ghlInfo)) { - ret=G2_Add_Bolt(ghlInfo->mFileName, ghlInfo->mBltlist, ghlInfo->mSlist, boneName); + ret=G2_Add_Bolt(ghlInfo, ghlInfo->mBltlist, ghlInfo->mSlist, boneName); + G2NOTE(ret>=0,va("G2API_AddBolt Failed (%s:%s)",boneName,ghlInfo->mFileName)); } - G2WARNING(ret>=0,"G2API_AddBolt Failed"); return ret; } int G2API_AddBoltSurfNum(CGhoul2Info *ghlInfo, const int surfIndex) { int ret=-1; - G2ERROR(ghlInfo,"NULL ghlInfo"); - if (ghlInfo) + if (G2_SetupModelPointers(ghlInfo)) { - ret=G2_Add_Bolt_Surf_Num(ghlInfo->mFileName, ghlInfo->mBltlist, ghlInfo->mSlist, surfIndex); + ret=G2_Add_Bolt_Surf_Num(ghlInfo, ghlInfo->mBltlist, ghlInfo->mSlist, surfIndex); } G2WARNING(ret>=0,"G2API_AddBoltSurfNum Failed"); return ret; @@ -1065,21 +959,21 @@ int G2API_AddBoltSurfNum(CGhoul2Info *ghlInfo, const int surfIndex) qboolean G2API_AttachG2Model(CGhoul2Info *ghlInfo, CGhoul2Info *ghlInfoTo, int toBoltIndex, int toModel) { qboolean ret=qfalse; - G2ERROR(ghlInfo,"NULL ghlInfo"); - G2ERROR(ghlInfoTo,"NULL ghlInfo"); - G2ERROR(ghlInfo,"NULL ghlInfo"); - G2ERROR(toBoltIndex>=0&&toBoltIndexmBltlist.size(),"Invalid Bolt Index"); - assert( toBoltIndex >= 0 ); - if ( toBoltIndex >= 0 ) + if (G2_SetupModelPointers(ghlInfo)&&G2_SetupModelPointers(ghlInfoTo)) { - // make sure we have a model to attach, a model to attach to, and a bolt on that model - if ((ghlInfo) && (ghlInfoTo) && ((ghlInfoTo->mBltlist[toBoltIndex].boneNumber != -1) || (ghlInfoTo->mBltlist[toBoltIndex].surfaceNumber != -1))) + G2ERROR(toBoltIndex>=0&&toBoltIndexmBltlist.size(),"Invalid Bolt Index"); + assert( toBoltIndex >= 0 ); + if ( toBoltIndex >= 0 ) { - // encode the bolt address into the model bolt link - toModel &= MODEL_AND; - toBoltIndex &= BOLT_AND; - ghlInfo->mModelBoltLink = (toModel << MODEL_SHIFT) | (toBoltIndex << BOLT_SHIFT); - ret=qtrue; + // make sure we have a model to attach, a model to attach to, and a bolt on that model + if (((ghlInfoTo->mBltlist[toBoltIndex].boneNumber != -1) || (ghlInfoTo->mBltlist[toBoltIndex].surfaceNumber != -1))) + { + // encode the bolt address into the model bolt link + toModel &= MODEL_AND; + toBoltIndex &= BOLT_AND; + ghlInfo->mModelBoltLink = (toModel << MODEL_SHIFT) | (toBoltIndex << BOLT_SHIFT); + ret=qtrue; + } } } G2WARNING(ret,"G2API_AttachG2Model Failed"); @@ -1089,8 +983,7 @@ qboolean G2API_AttachG2Model(CGhoul2Info *ghlInfo, CGhoul2Info *ghlInfoTo, int t qboolean G2API_DetachG2Model(CGhoul2Info *ghlInfo) { - G2ERROR(ghlInfo,"NULL ghlInfo"); - if (ghlInfo) + if (G2_SetupModelPointers(ghlInfo)) { ghlInfo->mModelBoltLink = -1; return qtrue; @@ -1102,16 +995,18 @@ qboolean G2API_AttachEnt(int *boltInfo, CGhoul2Info *ghlInfoTo, int toBoltIndex, { qboolean ret=qfalse; G2ERROR(boltInfo,"NULL boltInfo"); - G2ERROR(ghlInfoTo,"NULL ghlInfo"); - // make sure we have a model to attach, a model to attach to, and a bolt on that model - if ((ghlInfoTo) && ((ghlInfoTo->mBltlist[toBoltIndex].boneNumber != -1) || (ghlInfoTo->mBltlist[toBoltIndex].surfaceNumber != -1))) + if (boltInfo&&G2_SetupModelPointers(ghlInfoTo)) { - // encode the bolt address into the model bolt link - toModelNum &= MODEL_AND; - toBoltIndex &= BOLT_AND; - entNum &= ENTITY_AND; - *boltInfo = (toBoltIndex << BOLT_SHIFT) | (toModelNum << MODEL_SHIFT) | (entNum << ENTITY_SHIFT); - ret=qtrue; + // make sure we have a model to attach, a model to attach to, and a bolt on that model + if (((ghlInfoTo->mBltlist[toBoltIndex].boneNumber != -1) || (ghlInfoTo->mBltlist[toBoltIndex].surfaceNumber != -1))) + { + // encode the bolt address into the model bolt link + toModelNum &= MODEL_AND; + toBoltIndex &= BOLT_AND; + entNum &= ENTITY_AND; + *boltInfo = (toBoltIndex << BOLT_SHIFT) | (toModelNum << MODEL_SHIFT) | (entNum << ENTITY_SHIFT); + ret=qtrue; + } } G2WARNING(ret,"G2API_AttachEnt Failed"); return ret; @@ -1126,27 +1021,6 @@ void G2API_DetachEnt(int *boltInfo) } } -#if _DEBUG -class GBM_Test -{ -public: - int Hits; - int Misses; - GBM_Test() - { - Hits=0; - Misses=0; - } - ~GBM_Test() - { - char mess[1000]; - sprintf(mess,"GBM stats: %d hits, %d misses\n",Hits,Misses); - OutputDebugString(mess); - } -}; - -static GBM_Test TheGBM_Test; -#endif bool G2_NeedsRecalc(CGhoul2Info *ghlInfo,int frameNum); @@ -1156,71 +1030,76 @@ qboolean G2API_GetBoltMatrix(CGhoul2Info_v &ghoul2, const int modelIndex, const G2ERROR(ghoul2.IsValid(),"Invalid ghlInfo"); G2ERROR(matrix,"NULL matrix"); G2ERROR(modelIndex>=0&&modelIndex=0&&modelIndex= 0 && (boltIndex < ghlInfo->mBltlist.size()),"Invalid Bolt Index"); - - G2_GenerateWorldMatrix(angles, position); - if (boltIndex >= 0 && ghlInfo && (boltIndex < ghlInfo->mBltlist.size()) ) + if (matrix&&modelIndex>=0&&modelIndex= 0 && (boltIndex < ghlInfo->mBltlist.size()),va("Invalid Bolt Index (%d:%s)",boltIndex,ghlInfo->mFileName)); - if (G2_NeedsRecalc(ghlInfo,frameNum)) + if (boltIndex >= 0 && ghlInfo && (boltIndex < ghlInfo->mBltlist.size()) ) { - G2_ConstructGhoulSkeleton(ghoul2,frameNum,true,scale); -#if _DEBUG - TheGBM_Test.Hits++; -#endif - } -#if _DEBUG - else - { - TheGBM_Test.Misses++; - } -#endif - vec3_t newScale; + mdxaBone_t bolt; - VectorClear(newScale); + if (G2_NeedsRecalc(ghlInfo,frameNum)) + { + G2_ConstructGhoulSkeleton(ghoul2,frameNum,true,scale); + } + vec3_t newScale; - G2_GetBoltMatrixLow(*ghlInfo,boltIndex,scale,bolt); - // scale the bolt position by the scale factor for this model since at this point its still in model space - if (scale[0]) - { - bolt.matrix[0][3] *= scale[0]; - } - if (scale[1]) - { - bolt.matrix[1][3] *= scale[1]; - } - if (scale[2]) - { - bolt.matrix[2][3] *= scale[2]; - } - VectorNormalize((float*)&bolt.matrix[0]); - VectorNormalize((float*)&bolt.matrix[1]); - VectorNormalize((float*)&bolt.matrix[2]); + VectorClear(newScale); - Multiply_3x4Matrix(matrix, &worldMatrix, &bolt); - return qtrue; + G2_GetBoltMatrixLow(*ghlInfo,boltIndex,scale,bolt); + // scale the bolt position by the scale factor for this model since at this point its still in model space + if (scale[0]) + { + bolt.matrix[0][3] *= scale[0]; + } + if (scale[1]) + { + bolt.matrix[1][3] *= scale[1]; + } + if (scale[2]) + { + bolt.matrix[2][3] *= scale[2]; + } + VectorNormalize((float*)&bolt.matrix[0]); + VectorNormalize((float*)&bolt.matrix[1]); + VectorNormalize((float*)&bolt.matrix[2]); + + Multiply_3x4Matrix(matrix, &worldMatrix, &bolt); +#ifdef _DEBUG + for ( int i = 0; i < 3; i++ ) + { + for ( int j = 0; j < 4; j++ ) + { + assert( !_isnan(matrix->matrix[i][j])); + } + } +#endif// _DEBUG + return qtrue; + } } } + else + { + G2WARNING(0,"G2API_GetBoltMatrix Failed on empty or bad model"); + } Multiply_3x4Matrix(matrix, &worldMatrix, &identityMatrix); return qfalse; } void G2API_ListSurfaces(CGhoul2Info *ghlInfo) { - G2ERROR(ghlInfo,"NULL ghlInfo"); - if (ghlInfo) + if (G2_SetupModelPointers(ghlInfo)) { G2_List_Model_Surfaces(ghlInfo->mFileName); } @@ -1228,8 +1107,7 @@ void G2API_ListSurfaces(CGhoul2Info *ghlInfo) void G2API_ListBones(CGhoul2Info *ghlInfo, int frame) { - G2ERROR(ghlInfo,"NULL ghlInfo"); - if (ghlInfo) + if (G2_SetupModelPointers(ghlInfo)) { G2_List_Model_Bones(ghlInfo->mFileName, frame); } @@ -1250,15 +1128,6 @@ void G2API_SetGhoul2ModelIndexes(CGhoul2Info_v &ghoul2, qhandle_t *modelList, qh { if (ghoul2[i].mModelindex != -1) { - // broken into 3 lines for debugging, STL is a pain to view... - // - const int iModelIndex = ghoul2[i].mModelindex; - const qhandle_t mModel = modelList[iModelIndex]; -//we have to call this func to clean up the bad mModels from before the renderer started, but this won't be filled in the very next frame -//so, for that frame it will actually zero out the real model, but i don't know which is valid, so i'll just fix it in the renderer. -// assert(mModel); - - ghoul2[i].mModel = mModel; ghoul2[i].mSkin = skinList[ghoul2[i].mCustomSkin]; } } @@ -1290,8 +1159,7 @@ char *G2API_GetAnimFileNameIndex(qhandle_t modelIndex) qboolean G2API_GetAnimFileName(CGhoul2Info *ghlInfo, char **filename) { qboolean ret=qfalse; - G2ERROR(ghlInfo,"NULL ghlInfo"); - if (ghlInfo) + if (G2_SetupModelPointers(ghlInfo)) { ret=G2_GetAnimFileName(ghlInfo->mFileName, filename); } @@ -1321,41 +1189,41 @@ void G2API_CollisionDetect(CCollisionRecord *collRecMap, CGhoul2Info_v &ghoul2, { G2ERROR(ghoul2.IsValid(),"Invalid ghlInfo"); G2ERROR(collRecMap,"NULL Collision Rec"); - int frameNumber=G2API_GetTime(AframeNumber); + if (G2_SetupModelPointers(ghoul2)&&collRecMap) + { + int frameNumber=G2API_GetTime(AframeNumber); - vec3_t transRayStart, transRayEnd; + vec3_t transRayStart, transRayEnd; - // make sure we have transformed the whole skeletons for each model - G2_ConstructGhoulSkeleton(ghoul2, frameNumber,true, scale); + // make sure we have transformed the whole skeletons for each model + G2_ConstructGhoulSkeleton(ghoul2, frameNumber,true, scale); - // pre generate the world matrix - used to transform the incoming ray - G2_GenerateWorldMatrix(angles, position); + // pre generate the world matrix - used to transform the incoming ray + G2_GenerateWorldMatrix(angles, position); - G2VertSpaceServer->ResetHeap(); + G2VertSpaceServer->ResetHeap(); - // now having done that, time to build the model - G2_TransformModel(ghoul2, frameNumber, scale,G2VertSpaceServer, useLod); + // now having done that, time to build the model + G2_TransformModel(ghoul2, frameNumber, scale,G2VertSpaceServer, useLod); - // model is built. Lets check to see if any triangles are actually hit. - // first up, translate the ray to model space - TransformAndTranslatePoint(rayStart, transRayStart, &worldMatrixInv); - TransformAndTranslatePoint(rayEnd, transRayEnd, &worldMatrixInv); - - // now walk each model and check the ray against each poly - sigh, this is SO expensive. I wish there was a better way to do this. - G2_TraceModels(ghoul2, transRayStart, transRayEnd, collRecMap, entNum, eG2TraceType, useLod, fRadius); - - G2VertSpaceServer->ResetHeap(); - // now sort the resulting array of collision records so they are distance ordered - qsort( collRecMap, MAX_G2_COLLISIONS, - sizeof( CCollisionRecord ), QsortDistance ); + // model is built. Lets check to see if any triangles are actually hit. + // first up, translate the ray to model space + TransformAndTranslatePoint(rayStart, transRayStart, &worldMatrixInv); + TransformAndTranslatePoint(rayEnd, transRayEnd, &worldMatrixInv); + // now walk each model and check the ray against each poly - sigh, this is SO expensive. I wish there was a better way to do this. + G2_TraceModels(ghoul2, transRayStart, transRayEnd, collRecMap, entNum, eG2TraceType, useLod, fRadius); + G2VertSpaceServer->ResetHeap(); + // now sort the resulting array of collision records so they are distance ordered + qsort( collRecMap, MAX_G2_COLLISIONS, + sizeof( CCollisionRecord ), QsortDistance ); + } } qboolean G2API_SetGhoul2ModelFlags(CGhoul2Info *ghlInfo, const int flags) { - G2ERROR(ghlInfo,"NULL ghlInfo"); - if (ghlInfo) + if (G2_SetupModelPointers(ghlInfo)) { ghlInfo->mFlags &= GHOUL2_NEWORIGIN; ghlInfo->mFlags |= flags; @@ -1366,8 +1234,7 @@ qboolean G2API_SetGhoul2ModelFlags(CGhoul2Info *ghlInfo, const int flags) int G2API_GetGhoul2ModelFlags(CGhoul2Info *ghlInfo) { - G2ERROR(ghlInfo,"NULL ghlInfo"); - if (ghlInfo) + if (G2_SetupModelPointers(ghlInfo)) { return (ghlInfo->mFlags & ~GHOUL2_NEWORIGIN); } @@ -1433,18 +1300,21 @@ void G2API_CopyGhoul2Instance(CGhoul2Info_v &ghoul2From, CGhoul2Info_v &ghoul2To char *G2API_GetSurfaceName(CGhoul2Info *ghlInfo, int surfNumber) { - G2ERROR(ghlInfo,"NULL ghlInfo"); static char noSurface[1] = ""; - model_t *mod = R_GetModelByHandle(RE_RegisterModel(ghlInfo->mFileName)); - mdxmSurface_t *surf = 0; - mdxmSurfHierarchy_t *surfInfo = 0; - - surf = (mdxmSurface_t *)G2_FindSurface((void *)mod, surfNumber, 0); - if (surf) + if (G2_SetupModelPointers(ghlInfo)) { - mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)mod->mdxm + sizeof(mdxmHeader_t)); - surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surf->thisSurfaceIndex]); - return surfInfo->name; + mdxmSurface_t *surf = 0; + mdxmSurfHierarchy_t *surfInfo = 0; + + + surf = (mdxmSurface_t *)G2_FindSurface(ghlInfo->currentModel, surfNumber, 0); + if (surf) + { + assert(G2_MODEL_OK(ghlInfo)); + mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)ghlInfo->currentModel->mdxm + sizeof(mdxmHeader_t)); + surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surf->thisSurfaceIndex]); + return surfInfo->name; + } } G2WARNING(0,"Surface Not Found"); return noSurface; @@ -1454,11 +1324,10 @@ char *G2API_GetSurfaceName(CGhoul2Info *ghlInfo, int surfNumber) int G2API_GetSurfaceIndex(CGhoul2Info *ghlInfo, const char *surfaceName) { int ret=-1; - G2ERROR(ghlInfo,"NULL ghlInfo"); G2ERROR(surfaceName,"NULL surfaceName"); - if (ghlInfo) + if (surfaceName&&G2_SetupModelPointers(ghlInfo)) { - ret=G2_GetSurfaceIndex(ghlInfo->mFileName, surfaceName); + ret=G2_GetSurfaceIndex(ghlInfo, surfaceName); } G2WARNING(ret>=0,"G2API_GetSurfaceIndex Failed"); return ret; @@ -1466,23 +1335,17 @@ int G2API_GetSurfaceIndex(CGhoul2Info *ghlInfo, const char *surfaceName) char *G2API_GetGLAName(CGhoul2Info *ghlInfo) { - G2ERROR(ghlInfo,"NULL ghlInfo"); - if (ghlInfo) + if (G2_SetupModelPointers(ghlInfo)) { - model_t *mod = R_GetModelByHandle(RE_RegisterModel(ghlInfo->mFileName)); - G2ERROR(mod&&mod->mdxm,"Bad Model"); - if (mod&&mod->mdxm) - { - return mod->mdxm->animName; - } + assert(G2_MODEL_OK(ghlInfo)); + return ghlInfo->currentModel->mdxm->animName; } return 0; } qboolean G2API_SetNewOrigin(CGhoul2Info *ghlInfo, const int boltIndex) { - G2ERROR(ghlInfo,"NULL ghlInfo"); - if (ghlInfo) + if (G2_SetupModelPointers(ghlInfo)) { G2ERROR(boltIndex>=0&&boltIndexmBltlist.size(),"NULL ghlInfo"); @@ -1499,26 +1362,24 @@ qboolean G2API_SetNewOrigin(CGhoul2Info *ghlInfo, const int boltIndex) int G2API_GetBoneIndex(CGhoul2Info *ghlInfo, const char *boneName, qboolean bAddIfNotFound) { int ret=-1; - G2ERROR(ghlInfo,"NULL ghlInfo"); G2ERROR(boneName,"NULL boneName"); - if (ghlInfo&&boneName) + if (boneName&&G2_SetupModelPointers(ghlInfo)) { ret=G2_Get_Bone_Index(ghlInfo, boneName, bAddIfNotFound); } - G2WARNING(ret>=0,"G2API_GetBoneIndex Failed"); + G2NOTE(ret>=0,"G2API_GetBoneIndex Failed"); return ret; } qboolean G2API_SaveGhoul2Models(CGhoul2Info_v &ghoul2, char **buffer, int *size) { - G2ERROR(ghoul2.IsValid(),"Invalid ghlInfo"); return G2_SaveGhoul2Models(ghoul2, buffer, size); } void G2API_LoadGhoul2Models(CGhoul2Info_v &ghoul2, char *buffer) { G2_LoadGhoul2Model(ghoul2, buffer); - G2ERROR(ghoul2.IsValid(),"Invalid ghlInfo after load"); +// G2ERROR(ghoul2.IsValid(),"Invalid ghlInfo after load"); } void G2API_FreeSaveBuffer(char *buffer) @@ -1531,4 +1392,137 @@ void G2API_FreeSaveBuffer(char *buffer) void G2API_LoadSaveCodeDestructGhoul2Info(CGhoul2Info_v &ghoul2) { ghoul2.~CGhoul2Info_v(); // so I can load junk over it then memset to 0 without orphaning -} \ No newline at end of file +} + +bool G2_TestModelPointers(CGhoul2Info *ghlInfo) // returns true if the model is properly set up +{ + G2ERROR(ghlInfo,"NULL ghlInfo"); + if (!ghlInfo) + { + return false; + } + ghlInfo->mValid=false; + if (ghlInfo->mModelindex != -1) + { + ghlInfo->mModel = RE_RegisterModel(ghlInfo->mFileName); + ghlInfo->currentModel = R_GetModelByHandle(ghlInfo->mModel); + if (ghlInfo->currentModel) + { + if (ghlInfo->currentModel->mdxm) + { + if (ghlInfo->currentModelSize) + { + if (ghlInfo->currentModelSize!=ghlInfo->currentModel->mdxm->ofsEnd) + { + Com_Error(ERR_DROP, "Ghoul2 model was reloaded and has changed, map must be restarted.\n"); + } + } + ghlInfo->currentModelSize=ghlInfo->currentModel->mdxm->ofsEnd; + ghlInfo->animModel = R_GetModelByHandle(ghlInfo->currentModel->mdxm->animIndex); + if (ghlInfo->animModel) + { + ghlInfo->aHeader =ghlInfo->animModel->mdxa; + if (ghlInfo->aHeader) + { + if (ghlInfo->currentAnimModelSize) + { + if (ghlInfo->currentAnimModelSize!=ghlInfo->aHeader->ofsEnd) + { + Com_Error(ERR_DROP, "Ghoul2 model was reloaded and has changed, map must be restarted.\n"); + } + } + ghlInfo->currentAnimModelSize=ghlInfo->aHeader->ofsEnd; + ghlInfo->mValid=true; + } + } + } + } + } + if (!ghlInfo->mValid) + { + ghlInfo->currentModel=0; + ghlInfo->currentModelSize=0; + ghlInfo->animModel=0; + ghlInfo->currentAnimModelSize=0; + ghlInfo->aHeader=0; + } + return ghlInfo->mValid; +} + +bool G2_SetupModelPointers(CGhoul2Info *ghlInfo) // returns true if the model is properly set up +{ + G2ERROR(ghlInfo,"NULL ghlInfo"); + if (!ghlInfo) + { + return false; + } + ghlInfo->mValid=false; +// G2WARNING(ghlInfo->mModelindex != -1,"Setup request on non-used info slot?"); + if (ghlInfo->mModelindex != -1) + { + G2ERROR(ghlInfo->mFileName[0],"empty ghlInfo->mFileName"); + ghlInfo->mModel = RE_RegisterModel(ghlInfo->mFileName); + ghlInfo->currentModel = R_GetModelByHandle(ghlInfo->mModel); + G2ERROR(ghlInfo->currentModel,va("NULL Model (glm) %s",ghlInfo->mFileName)); + if (ghlInfo->currentModel) + { + G2ERROR(ghlInfo->currentModel->mdxm,va("Model has no mdxm (glm) %s",ghlInfo->mFileName)); + if (ghlInfo->currentModel->mdxm) + { + if (ghlInfo->currentModelSize) + { + if (ghlInfo->currentModelSize!=ghlInfo->currentModel->mdxm->ofsEnd) + { + Com_Error(ERR_DROP, "Ghoul2 model was reloaded and has changed, map must be restarted.\n"); + } + } + ghlInfo->currentModelSize=ghlInfo->currentModel->mdxm->ofsEnd; + G2ERROR(ghlInfo->currentModelSize,va("Zero sized Model? (glm) %s",ghlInfo->mFileName)); + + ghlInfo->animModel = R_GetModelByHandle(ghlInfo->currentModel->mdxm->animIndex); + G2ERROR(ghlInfo->animModel,va("NULL Model (gla) %s",ghlInfo->mFileName)); + if (ghlInfo->animModel) + { + ghlInfo->aHeader =ghlInfo->animModel->mdxa; + G2ERROR(ghlInfo->aHeader,va("Model has no mdxa (gla) %s",ghlInfo->mFileName)); + if (ghlInfo->aHeader) + { + if (ghlInfo->currentAnimModelSize) + { + if (ghlInfo->currentAnimModelSize!=ghlInfo->aHeader->ofsEnd) + { + Com_Error(ERR_DROP, "Ghoul2 model was reloaded and has changed, map must be restarted.\n"); + } + } + ghlInfo->currentAnimModelSize=ghlInfo->aHeader->ofsEnd; + G2ERROR(ghlInfo->currentAnimModelSize,va("Zero sized Model? (gla) %s",ghlInfo->mFileName)); + ghlInfo->mValid=true; + } + } + } + } + } + if (!ghlInfo->mValid) + { + ghlInfo->currentModel=0; + ghlInfo->currentModelSize=0; + ghlInfo->animModel=0; + ghlInfo->currentAnimModelSize=0; + ghlInfo->aHeader=0; + } + return ghlInfo->mValid; +} + +bool G2_SetupModelPointers(CGhoul2Info_v &ghoul2) // returns true if any model is properly set up +{ + bool ret=false; + int i; + for (i=0; imValid&&(g)->aHeader&&(g)->currentModel&&(g)->animModel) //===================================================================================================================== // Bolt List handling routines - so entities can attach themselves to any part of the model in question @@ -28,12 +29,6 @@ int G2_Find_Bolt_Bone_Num(boltInfo_v &bltlist, const int boneNum) // look through entire list for(i=0; imValid); boltInfo_t tempBolt; int i; + assert(surfNum>=0&&surfNum= slist.size()) { @@ -119,20 +110,21 @@ int G2_Add_Bolt_Surf_Num(const char *fileName, boltInfo_v &bltlist, surfaceInfo_ } void G2_Bolt_Not_Found(const char *boneName,const char *modName); -int G2_Add_Bolt(const char *fileName, boltInfo_v &bltlist, surfaceInfo_v &slist, const char *boneName) +int G2_Add_Bolt(CGhoul2Info *ghlInfo, boltInfo_v &bltlist, surfaceInfo_v &slist, const char *boneName) { - model_t *mod_m = R_GetModelByHandle(RE_RegisterModel(fileName)); - model_t *mod_a = R_GetModelByHandle(mod_m->mdxm->animIndex); + assert(ghlInfo&&ghlInfo->mValid); int i, x, surfNum = -1; mdxaSkel_t *skel; mdxaSkelOffsets_t *offsets; mdxmHierarchyOffsets_t *surfOffsets; boltInfo_t tempBolt; int flags; + + assert(G2_MODEL_OK(ghlInfo)); - surfOffsets = (mdxmHierarchyOffsets_t *)((byte*)mod_m->mdxm + sizeof(mdxmHeader_t)); + surfOffsets = (mdxmHierarchyOffsets_t *)((byte*)ghlInfo->currentModel->mdxm + sizeof(mdxmHeader_t)); // first up, we'll search for that which this bolt names in all the surfaces - surfNum = G2_IsSurfaceLegal((void*)mod_m, boneName, &flags); + surfNum = G2_IsSurfaceLegal(ghlInfo->currentModel, boneName, &flags); // did we find it as a surface? if (surfNum != -1) @@ -174,12 +166,12 @@ int G2_Add_Bolt(const char *fileName, boltInfo_v &bltlist, surfaceInfo_v &slist, // no, check to see if it's a bone then - offsets = (mdxaSkelOffsets_t *)((byte *)mod_a->mdxa + sizeof(mdxaHeader_t)); + offsets = (mdxaSkelOffsets_t *)((byte *)ghlInfo->aHeader + sizeof(mdxaHeader_t)); // walk the entire list of bones in the gla file for this model and see if any match the name of the bone we want to find - for (x=0; x< mod_a->mdxa->numBones; x++) + for (x=0; x< ghlInfo->aHeader->numBones; x++) { - skel = (mdxaSkel_t *)((byte *)mod_a->mdxa + sizeof(mdxaHeader_t) + offsets->offsets[x]); + skel = (mdxaSkel_t *)((byte *)ghlInfo->aHeader + sizeof(mdxaHeader_t) + offsets->offsets[x]); // if name is the same, we found it if (!stricmp(skel->name, boneName)) { @@ -188,12 +180,12 @@ int G2_Add_Bolt(const char *fileName, boltInfo_v &bltlist, surfaceInfo_v &slist, } // check to see we did actually make a match with a bone in the model - if (x == mod_a->mdxa->numBones) + if (x == ghlInfo->aHeader->numBones) { // didn't find it? Error //assert(0&&x == mod_a->mdxa->numBones); #if _DEBUG - G2_Bolt_Not_Found(boneName,fileName); + G2_Bolt_Not_Found(boneName,ghlInfo->mFileName); #endif return -1; } @@ -237,6 +229,7 @@ int G2_Add_Bolt(const char *fileName, boltInfo_v &bltlist, surfaceInfo_v &slist, // Given a model handle, and a bone name, we want to remove this bone from the bone override list qboolean G2_Remove_Bolt (boltInfo_v &bltlist, int index) { + assert(index>=0&&index-1; i--) - { - if ((bltlist[i].surfaceNumber == -1) && (bltlist[i].boneNumber == -1)) - { - newSize = i; - } - // once we hit one that isn't a -1, we are done. - else - { - break; - } - } - // do we need to resize? - if (newSize != bltlist.size()) - { - // yes, so lets do it - bltlist.resize(newSize); - } - } return qtrue; } - - assert(0); - - // no return qfalse; } @@ -284,51 +251,3 @@ void G2_Init_Bolt_List(boltInfo_v &bltlist) bltlist.clear(); } -// remove any bolts that reference original surfaces, generated surfaces, or bones that aren't active anymore -void G2_RemoveRedundantBolts(boltInfo_v &bltlist, surfaceInfo_v &slist, int *activeSurfaces, int *activeBones) -{ - G2_FindOverrideSurface(-1, slist); //reset the quick surface override lookup; - // walk the bolt list - for (int i=0; imdxa + sizeof(mdxaHeader_t)); - skel = (mdxaSkel_t *)((byte *)mod->mdxa + sizeof(mdxaHeader_t) + offsets->offsets[0]); + offsets = (mdxaSkelOffsets_t *)((byte *)ghlInfo->aHeader + sizeof(mdxaHeader_t)); + skel = (mdxaSkel_t *)((byte *)ghlInfo->aHeader + sizeof(mdxaHeader_t) + offsets->offsets[0]); // look through entire list for(i=0; imdxa + sizeof(mdxaHeader_t) + offsets->offsets[blist[i].boneNumber]); + skel = (mdxaSkel_t *)((byte *)ghlInfo->aHeader + sizeof(mdxaHeader_t) + offsets->offsets[blist[i].boneNumber]); // if name is the same, we found it if (!stricmp(skel->name, boneName)) @@ -56,7 +56,7 @@ int G2_Find_Bone(const model_t *mod, boneInfo_v &blist, const char *boneName) } } #if _DEBUG - G2_Bone_Not_Found(boneName,mod->name); + G2_Bone_Not_Found(boneName,ghlInfo->mFileName); #endif // didn't find it return -1; @@ -86,8 +86,9 @@ int G2_Add_Bone (const model_t *mod, boneInfo_v &blist, const char *boneName) // check to see we did actually make a match with a bone in the model if (x == mod->mdxa->numBones) { - // didn't find it? Error -// assert(x != mod->mdxa->numBones); +#if _DEBUG + G2_Bone_Not_Found(boneName,mod->name); +#endif return -1; } @@ -130,37 +131,11 @@ qboolean G2_Remove_Bone_Index ( boneInfo_v &blist, int index) // check the flags first - if it's still being used Do NOT remove it if (!blist[index].flags) { - // set this bone to not used blist[index].boneNumber = -1; - - int newSize = blist.size(); - // now look through the list from the back and see if there is a block of -1's we can resize off the end of the list - for (int i=blist.size()-1; i>-1; i--) - { - if (blist[i].boneNumber == -1) - { - newSize = i; - } - // once we hit one that isn't a -1, we are done. - else - { - break; - } - } - // do we need to resize? - if (newSize != blist.size()) - { - // yes, so lets do it - blist.resize(newSize); - } - - return qtrue; } + return qtrue; } - -// assert(0); - // no return qfalse; } @@ -389,119 +364,72 @@ void G2_Generate_Matrix(const model_t *mod, boneInfo_v &blist, int index, const // Given a model handle, and a bone name, we want to remove this bone from the bone override list -qboolean G2_Remove_Bone (const char *fileName, boneInfo_v &blist, const char *boneName) +qboolean G2_Remove_Bone (CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName) { - model_t *mod_m = R_GetModelByHandle(RE_RegisterModel(fileName)); - model_t *mod_a = R_GetModelByHandle(mod_m->mdxm->animIndex); - int index = G2_Find_Bone(mod_a, blist, boneName); + int index = G2_Find_Bone(ghlInfo, blist, boneName); + if (index==-1) + { + return false; + } return G2_Remove_Bone_Index(blist, index); } // Given a model handle, and a bone name, we want to set angles specifically for overriding -qboolean G2_Set_Bone_Angles_Index(const char *fileName, boneInfo_v &blist, const int index, +qboolean G2_Set_Bone_Angles_Index(CGhoul2Info *ghlInfo, boneInfo_v &blist, const int index, const float *angles, const int flags, const Eorientations yaw, - const Eorientations pitch, const Eorientations roll, qhandle_t *modelList, - const int modelIndex, const int blendTime, const int currentTime) + const Eorientations pitch, const Eorientations roll, + const int blendTime, const int currentTime) { - model_t *mod_m; - - if ((index >= blist.size()) || (blist[index].boneNumber == -1)) + + if (index<0||(index >= blist.size()) || (blist[index].boneNumber == -1)) { // we are attempting to set a bone override that doesn't exist // assert(0); return qfalse; } - if (!fileName[0]) - { - mod_m = R_GetModelByHandle( modelList[modelIndex] ); - } - else - { - mod_m = R_GetModelByHandle(RE_RegisterModel(fileName)); - } - - model_t *mod_a = R_GetModelByHandle(mod_m->mdxm->animIndex); - /* - if (flags & (BONE_ANGLES_PREMULT | BONE_ANGLES_POSTMULT)) - { - // you CANNOT call this with an index with these kinds of bone overrides - we need the model details for these kinds of bone angle overrides - assert(0); - return qfalse; - } - */ - // yes, so set the angles and flags correctly blist[index].flags &= ~(BONE_ANGLES_TOTAL); blist[index].flags |= flags; blist[index].boneBlendStart = currentTime; blist[index].boneBlendTime = blendTime; - G2_Generate_Matrix(mod_a, blist, index, angles, flags, yaw, pitch, roll); + G2_Generate_Matrix(ghlInfo->animModel, blist, index, angles, flags, yaw, pitch, roll); return qtrue; } // Given a model handle, and a bone name, we want to set angles specifically for overriding -qboolean G2_Set_Bone_Angles(const char *fileName, boneInfo_v &blist, const char *boneName, const float *angles, +qboolean G2_Set_Bone_Angles(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName, const float *angles, const int flags, const Eorientations up, const Eorientations left, const Eorientations forward, - qhandle_t *modelList, const int modelIndex, const int blendTime, const int currentTime) + const int blendTime, const int currentTime) { - model_t *mod_m; - - if (!fileName[0]) + int index = G2_Find_Bone(ghlInfo, blist, boneName); + if (index == -1) { - mod_m = R_GetModelByHandle(modelList[modelIndex]); + index = G2_Add_Bone(ghlInfo->animModel, blist, boneName); } - else - { - mod_m = R_GetModelByHandle(RE_RegisterModel(fileName)); - } - - model_t *mod_a = R_GetModelByHandle(mod_m->mdxm->animIndex); - int index = G2_Find_Bone(mod_a, blist, boneName); - - // did we find it? if (index != -1) { - // yes, so set the angles and flags correctly blist[index].flags &= ~(BONE_ANGLES_TOTAL); blist[index].flags |= flags; blist[index].boneBlendStart = currentTime; blist[index].boneBlendTime = blendTime; - G2_Generate_Matrix(mod_a, blist, index, angles, flags, up, left, forward); + G2_Generate_Matrix(ghlInfo->animModel, blist, index, angles, flags, up, left, forward); return qtrue; } - - // no - lets try and add this bone in - index = G2_Add_Bone(mod_a, blist, boneName); - - // did we find a free one? - if (index != -1) - { - // yes, so set the angles and flags correctly - blist[index].flags &= ~(BONE_ANGLES_TOTAL); - blist[index].flags |= flags; - blist[index].boneBlendStart = currentTime; - blist[index].boneBlendTime = blendTime; - - G2_Generate_Matrix(mod_a, blist, index, angles, flags, up, left, forward); - return qtrue; - } - - // no return qfalse; } // Given a model handle, and a bone name, we want to set angles specifically for overriding - using a matrix directly qboolean G2_Set_Bone_Angles_Matrix_Index(boneInfo_v &blist, const int index, - const mdxaBone_t &matrix, const int flags, qhandle_t *modelList, - const int modelIndex, const int blendTime, const int currentTime) + const mdxaBone_t &matrix, const int flags, + const int blendTime, const int currentTime) { - if ((index >= blist.size()) || (blist[index].boneNumber == -1)) + if (index<0||(index >= blist.size()) || (blist[index].boneNumber == -1)) { // we are attempting to set a bone override that doesn't exist assert(0); @@ -520,54 +448,28 @@ qboolean G2_Set_Bone_Angles_Matrix_Index(boneInfo_v &blist, const int index, } // Given a model handle, and a bone name, we want to set angles specifically for overriding - using a matrix directly -qboolean G2_Set_Bone_Angles_Matrix(const char *fileName, boneInfo_v &blist, const char *boneName, const mdxaBone_t &matrix, - const int flags, qhandle_t *modelList, const int modelIndex, const int blendTime, const int currentTime) +qboolean G2_Set_Bone_Angles_Matrix(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName, const mdxaBone_t &matrix, + const int flags,const int blendTime, const int currentTime) { - model_t *mod_m; - if (!fileName[0]) + int index = G2_Find_Bone(ghlInfo, blist, boneName); + if (index == -1) { - mod_m = R_GetModelByHandle(modelList[modelIndex]); + index = G2_Add_Bone(ghlInfo->animModel, blist, boneName); } - else - { - mod_m = R_GetModelByHandle(RE_RegisterModel(fileName)); - } - model_t *mod_a = R_GetModelByHandle(mod_m->mdxm->animIndex); - int index = G2_Find_Bone(mod_a, blist, boneName); - - // did we find it? if (index != -1) { - // yes, so set the angles and flags correctly blist[index].flags &= ~(BONE_ANGLES_TOTAL); blist[index].flags |= flags; - memcpy(&blist[index].matrix, &matrix, sizeof(mdxaBone_t)); memcpy(&blist[index].newMatrix, &matrix, sizeof(mdxaBone_t)); return qtrue; } - - // no - lets try and add this bone in - index = G2_Add_Bone(mod_a, blist, boneName); - - // did we find a free one? - if (index != -1) - { - // yes, so set the angles and flags correctly - blist[index].flags &= ~(BONE_ANGLES_TOTAL); - blist[index].flags |= flags; - - memcpy(&blist[index].matrix, &matrix, sizeof(mdxaBone_t)); - memcpy(&blist[index].newMatrix, &matrix, sizeof(mdxaBone_t)); - return qtrue; - } - // no return qfalse; } // given a model, bone name, a bonelist, a start/end frame number, a anim speed and some anim flags, set up or modify an existing bone entry for a new set of anims qboolean G2_Set_Bone_Anim_Index(boneInfo_v &blist, const int index, const int startFrame, - const int endFrame, const int flags, const float animSpeed, const int currentTime, const float setFrame, const int blendTime) + const int endFrame, const int flags, const float animSpeed, const int currentTime, const float setFrame, const int blendTime,int numFrames) { int modFlags = flags; @@ -577,31 +479,26 @@ qboolean G2_Set_Bone_Anim_Index(boneInfo_v &blist, const int index, const int st Com_Printf("anim %d %5d %5d %5d %5d %3.2f %3.2f %3.2f\n",tr.refdef.time,TB.currentFrame,TB.newFrame,(int)TB.blendFrame,TB.blendOldFrame,TB.backlerp,float(TB.blendFrame - (int)TB.blendFrame),TB.blendLerp); } #endif - // sanity check some stuff - //assert(startFrame < 1000);//do we really need this??? - //assert((animSpeed > 0.01) && (animSpeed < 20.0)); if (index<0||index >= blist.size()||blist[index].boneNumber<0) { - // we are attempting to set a bone override that doesn't exist -// assert(0); return qfalse; } // sanity check to see if setfram is within animation bounds if (setFrame != -1) { - assert((setFrame >= startFrame) && (setFrame <= endFrame)); + assert((setFrame >= startFrame) && (setFrame < endFrame)); } // since we already existed, we can check to see if we want to start some blending - if (flags & (BONE_ANIM_BLEND|BONE_ANIM_BLEND_TO_PARENT)) + if (flags & BONE_ANIM_BLEND) { float currentFrame, animSpeed; int startFrame, endFrame, flags; // figure out where we are now - if (G2_Get_Bone_Anim_Index(blist, index, currentTime, ¤tFrame, &startFrame, &endFrame, &flags, &animSpeed, NULL, 0)) + if (G2_Get_Bone_Anim_Index(blist, index, currentTime, ¤tFrame, &startFrame, &endFrame, &flags, &animSpeed,numFrames)) { if (blist[index].blendStart == currentTime) //we're replacing a blend in progress which hasn't started { @@ -624,6 +521,7 @@ qboolean G2_Set_Bone_Anim_Index(boneInfo_v &blist, const int index, const int st // if we intend to end this anim or freeze after this, then just keep on the last frame else { + assert(blist[index].endFrame>0); blist[index].blendFrame = blist[index].endFrame -1; } } @@ -639,6 +537,7 @@ qboolean G2_Set_Bone_Anim_Index(boneInfo_v &blist, const int index, const int st // if we intend to end this anim or freeze after this, then just keep on the last frame else { + assert(blist[index].endFrame>0); blist[index].blendLerpFrame = blist[index].endFrame - 1; } } @@ -653,26 +552,16 @@ qboolean G2_Set_Bone_Anim_Index(boneInfo_v &blist, const int index, const int st blist[index].blendFrame = blist[index].blendLerpFrame = 0; blist[index].blendTime = 0; // we aren't blending, so remove the option to do so - modFlags &= ~(BONE_ANIM_BLEND|BONE_ANIM_BLEND_TO_PARENT); + modFlags &= ~BONE_ANIM_BLEND; return qfalse; } } else - // if we are doing it from parent, then we only care about blendtime and blend start, since the rest gets constructed in the renderer - if (flags & BONE_ANIM_BLEND_FROM_PARENT) - { - // if we hit this, someone put a BLEND_FROM_PARENT on a root bone - that's a no no - assert(blist[index].boneNumber); - // set the amount of time it's going to take to blend this anim with the last frame of the last one - blist[index].blendTime = blendTime; - blist[index].blendStart = currentTime; - } - else { blist[index].blendFrame = blist[index].blendLerpFrame = 0; blist[index].blendTime = blist[index].blendStart = 0; // we aren't blending, so remove the option to do so - modFlags &= ~(BONE_ANIM_BLEND|BONE_ANIM_BLEND_TO_PARENT); + modFlags &= ~BONE_ANIM_BLEND; } // yes, so set the anim data and flags correctly @@ -680,6 +569,8 @@ qboolean G2_Set_Bone_Anim_Index(boneInfo_v &blist, const int index, const int st blist[index].startFrame = startFrame; blist[index].animSpeed = animSpeed; blist[index].pauseTime = 0; + assert(blist[index].blendFrame>=0&&blist[index].blendFrame=0&&blist[index].blendLerpFramemdxm->animIndex); - int index = G2_Find_Bone(mod_a, blist, boneName); int modFlags = flags; + int index = G2_Find_Bone(ghlInfo, blist, boneName); - // sanity check some stuff - //assert(startFrame < 1000);//do we really need this??? - //assert((animSpeed > 0.01) && (animSpeed < 20.0)); - // sanity check to see if setfram is within animation bounds if (setFrame != -1) { @@ -719,30 +604,18 @@ qboolean G2_Set_Bone_Anim(const char *fileName, boneInfo_v &blist, const char *b // did we find it? if (index != -1) { - return G2_Set_Bone_Anim_Index(blist, index, startFrame, endFrame, flags, animSpeed, currentTime, setFrame, blendTime); + return G2_Set_Bone_Anim_Index(blist, index, startFrame, endFrame, flags, animSpeed, currentTime, setFrame, blendTime,ghlInfo->aHeader->numFrames); } // no - lets try and add this bone in - index = G2_Add_Bone(mod_a, blist, boneName); + index = G2_Add_Bone(ghlInfo->animModel, blist, boneName); // did we find a free one? if (index != -1) { - // if we are doing it from parent, then we only care about blendtime and blend start, since the rest gets constructed in the renderer - if (flags & BONE_ANIM_BLEND_FROM_PARENT) - { - // if we hit this, someone put a BLEND_FROM_PARENT on a root bone - that's a no no - assert(blist[index].boneNumber); - // set the amount of time it's going to take to blend this anim with the last frame of the last one - blist[index].blendTime = blendTime; - blist[index].blendStart = currentTime; - } - else - { - blist[index].blendFrame = blist[index].blendLerpFrame = 0; - blist[index].blendTime = 0; - // we aren't blending, so remove the option to do so - modFlags &= ~(BONE_ANIM_BLEND|BONE_ANIM_BLEND_TO_PARENT); - } + blist[index].blendFrame = blist[index].blendLerpFrame = 0; + blist[index].blendTime = 0; + // we aren't blending, so remove the option to do so + modFlags &= ~BONE_ANIM_BLEND; // yes, so set the anim data and flags correctly blist[index].endFrame = endFrame; blist[index].startFrame = startFrame; @@ -757,9 +630,10 @@ qboolean G2_Set_Bone_Anim(const char *fileName, boneInfo_v &blist, const char *b { blist[index].lastTime = blist[index].startTime = currentTime; } - blist[index].flags &= ~(BONE_ANIM_TOTAL); + blist[index].flags &= ~BONE_ANIM_TOTAL; blist[index].flags |= modFlags; - // assert(blist[index].startTime <= currentTime); + assert(blist[index].blendFrame>=0&&blist[index].blendFrameaHeader->numFrames); + assert(blist[index].blendLerpFrame>=0&&blist[index].blendLerpFrameaHeader->numFrames); return qtrue; } @@ -772,6 +646,7 @@ qboolean G2_Get_Bone_Anim_Range_Index(boneInfo_v &blist, const int boneIndex, in { if (boneIndex != -1) { + assert(boneIndex>=0&&boneIndexmdxm->animIndex); - int index = G2_Find_Bone(mod_a, blist, boneName); - - return (G2_Get_Bone_Anim_Range_Index( blist, index, startFrame, endFrame)); + int index = G2_Find_Bone(ghlInfo, blist, boneName); + if (index==-1) + { + return qfalse; + } + if (G2_Get_Bone_Anim_Range_Index( blist, index, startFrame, endFrame)) + { + assert(*startFrame>=0&&*startFrameaHeader->numFrames); + assert(*endFrame>0&&*endFrame<=ghlInfo->aHeader->numFrames); + return qtrue; + } + return qfalse; } // given a model, bonelist and bonename, return the current frame, startframe and endframe of the current animation // NOTE if we aren't running an animation, then qfalse is returned qboolean G2_Get_Bone_Anim_Index( boneInfo_v &blist, const int index, const int currentTime, - float *retcurrentFrame, int *startFrame, int *endFrame, int *flags, float *retAnimSpeed, qhandle_t *modelList, int modelIndex) + float *retcurrentFrame, int *startFrame, int *endFrame, int *flags, float *retAnimSpeed,int numFrames) { // did we find it? - if ((index != -1) && !((index >= blist.size()) || (blist[index].boneNumber == -1))) + if ((index>=0) && !((index >= blist.size()) || (blist[index].boneNumber == -1))) { // are we an animating bone? if (blist[index].flags & (BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE)) { - int currentFrame,newFrame,numFrames; - numFrames=30000; // I don't have this number, but it will eventually assert if the start and end frame are invalid + int currentFrame,newFrame; float lerp; G2_TimingModel(blist[index],currentTime,numFrames,currentFrame,newFrame,lerp); @@ -817,36 +698,39 @@ qboolean G2_Get_Bone_Anim_Index( boneInfo_v &blist, const int index, const int c return qtrue; } } - *startFrame = *endFrame = *retcurrentFrame = -1; - *flags = 0;/*-1;*/ //cheesees fudging cripes i hate -1 + *startFrame=0; + *endFrame=1; + *retcurrentFrame=0.0f; + *flags=0; + *retAnimSpeed=0.0f; return qfalse; } // given a model, bonelist and bonename, return the current frame, startframe and endframe of the current animation // NOTE if we aren't running an animation, then qfalse is returned -qboolean G2_Get_Bone_Anim(const char *fileName, boneInfo_v &blist, const char *boneName, const int currentTime, - float *currentFrame, int *startFrame, int *endFrame, int *flags, float *retAnimSpeed, qhandle_t *modelList, int modelIndex) +qboolean G2_Get_Bone_Anim(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName, const int currentTime, + float *currentFrame, int *startFrame, int *endFrame, int *flags, float *retAnimSpeed) { - model_t *mod_m; - if (!fileName[0]) + int index = G2_Find_Bone(ghlInfo, blist, boneName); + if (index==-1) { - mod_m = R_GetModelByHandle(modelList[modelIndex]); + return qfalse; } - else - { - mod_m = R_GetModelByHandle(RE_RegisterModel(fileName)); - } - - - model_t *mod_a = R_GetModelByHandle(mod_m->mdxm->animIndex); - int index = G2_Find_Bone(mod_a, blist, boneName); - return G2_Get_Bone_Anim_Index(blist, index, currentTime, currentFrame, startFrame, endFrame, flags, retAnimSpeed, modelList, modelIndex); + assert(ghlInfo->aHeader); + if (G2_Get_Bone_Anim_Index(blist, index, currentTime, currentFrame, startFrame, endFrame, flags, retAnimSpeed,ghlInfo->aHeader->numFrames)) + { + assert(*startFrame>=0&&*startFrameaHeader->numFrames); + assert(*endFrame>0&&*endFrame<=ghlInfo->aHeader->numFrames); + assert(*currentFrame>=0.0f&&((int)(*currentFrame))aHeader->numFrames); + return qtrue; + } + return qfalse; } // given a model, bonelist and bonename, lets pause an anim if it's playing. -qboolean G2_Pause_Bone_Anim_Index( boneInfo_v &blist, const int boneIndex, const int currentTime) +qboolean G2_Pause_Bone_Anim_Index( boneInfo_v &blist, const int boneIndex, const int currentTime,int numFrames) { if (boneIndex>=0&&boneIndexmdxm->animIndex); - int index = G2_Find_Bone(mod_a, blist, boneName); + int index = G2_Find_Bone(ghlInfo, blist, boneName); + if (index==-1) + { + return qfalse; + } - return (G2_Pause_Bone_Anim_Index( blist, index, currentTime) ); + return (G2_Pause_Bone_Anim_Index( blist, index, currentTime,ghlInfo->aHeader->numFrames) ); } -qboolean G2_IsPaused(const char *fileName, boneInfo_v &blist, const char *boneName) +qboolean G2_IsPaused(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName) { - model_t *mod_m = R_GetModelByHandle(RE_RegisterModel(fileName)); - model_t *mod_a = R_GetModelByHandle(mod_m->mdxm->animIndex); - int index = G2_Find_Bone(mod_a, blist, boneName); - - // did we find it? + int index = G2_Find_Bone(ghlInfo, blist, boneName); if (index != -1) { // are we paused? @@ -914,34 +796,26 @@ qboolean G2_IsPaused(const char *fileName, boneInfo_v &blist, const char *boneNa qboolean G2_Stop_Bone_Anim_Index(boneInfo_v &blist, const int index) { - if ((index >= blist.size()) || (blist[index].boneNumber == -1)) + if (index<0 || (index >= blist.size()) || (blist[index].boneNumber == -1)) { // we are attempting to set a bone override that doesn't exist - assert(0); return qfalse; } blist[index].flags &= ~(BONE_ANIM_TOTAL); - // try and remove this bone if we can return G2_Remove_Bone_Index(blist, index); } // given a model, bonelist and bonename, lets stop an anim if it's playing. -qboolean G2_Stop_Bone_Anim(const char *fileName, boneInfo_v &blist, const char *boneName) +qboolean G2_Stop_Bone_Anim(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName) { - model_t *mod_m = R_GetModelByHandle(RE_RegisterModel(fileName)); - model_t *mod_a = R_GetModelByHandle(mod_m->mdxm->animIndex); - int index = G2_Find_Bone(mod_a, blist, boneName); - - // did we find it? + int index = G2_Find_Bone(ghlInfo, blist, boneName); if (index != -1) { blist[index].flags &= ~(BONE_ANIM_TOTAL); - // try and remove this bone if we can return G2_Remove_Bone_Index(blist, index); } assert(0); - return qfalse; } @@ -955,29 +829,21 @@ qboolean G2_Stop_Bone_Angles_Index(boneInfo_v &blist, const int index) assert(0); return qfalse; } - blist[index].flags &= ~(BONE_ANGLES_TOTAL); - // try and remove this bone if we can return G2_Remove_Bone_Index(blist, index); } // given a model, bonelist and bonename, lets stop an anim if it's playing. -qboolean G2_Stop_Bone_Angles(const char *fileName, boneInfo_v &blist, const char *boneName) +qboolean G2_Stop_Bone_Angles(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName) { - model_t *mod_m = R_GetModelByHandle(RE_RegisterModel(fileName)); - model_t *mod_a = R_GetModelByHandle(mod_m->mdxm->animIndex); - int index = G2_Find_Bone(mod_a, blist, boneName); - - // did we find it? + int index = G2_Find_Bone(ghlInfo, blist, boneName); if (index != -1) { blist[index].flags &= ~(BONE_ANGLES_TOTAL); - // try and remove this bone if we can return G2_Remove_Bone_Index(blist, index); } assert(0); - return qfalse; } @@ -986,6 +852,7 @@ qboolean G2_Stop_Bone_Angles(const char *fileName, boneInfo_v &blist, const char void G2_Animate_Bone_List(CGhoul2Info_v &ghoul2, const int currentTime, const int index ) { int i; + assert(index>=0&&index i) && (blist[i].flags & BONE_ANGLES_REPLACE_TO_ANIM)) @@ -1108,35 +965,14 @@ void G2_Init_Bone_List(boneInfo_v &blist) blist.clear(); } -void G2_RemoveRedundantBoneOverrides(boneInfo_v &blist, int *activeBones) -{ - int i; - - // walk the surface list, removing surface overrides or generated surfaces that are pointing at surfaces that aren't active anymore - for (i=0; imFileName)); - model_t *mod_a = R_GetModelByHandle(mod_m->mdxm->animIndex); - if (bAddIfNotFound) { - return G2_Add_Bone(mod_a, ghoul2->mBlist, boneName); + return G2_Add_Bone(ghoul2->animModel, ghoul2->mBlist, boneName); } else { - return G2_Find_Bone(mod_a, ghoul2->mBlist, boneName); + return G2_Find_Bone(ghoul2, ghoul2->mBlist, boneName); } } \ No newline at end of file diff --git a/code/ghoul2/g2_misc.cpp b/code/ghoul2/g2_misc.cpp index f729e3e..6db93a1 100644 --- a/code/ghoul2/g2_misc.cpp +++ b/code/ghoul2/g2_misc.cpp @@ -22,6 +22,8 @@ #include "../qcommon/miniheap.h" #endif +#define G2_MODEL_OK(g) ((g)&&(g)->mValid&&(g)->aHeader&&(g)->currentModel&&(g)->animModel) + #include "../server/server.h" extern mdxaBone_t worldMatrix; @@ -122,16 +124,16 @@ void G2_List_Model_Bones(const char *fileName, int frame) mdxaSkelOffsets_t *offsets; model_t *mod_m = R_GetModelByHandle(RE_RegisterModel(fileName)); model_t *mod_a = R_GetModelByHandle(mod_m->mdxm->animIndex); - mdxaFrame_t *aframe=0; - int frameSize; +// mdxaFrame_t *aframe=0; +// int frameSize; mdxaHeader_t *header = mod_a->mdxa; // figure out where the offset list is offsets = (mdxaSkelOffsets_t *)((byte *)header + sizeof(mdxaHeader_t)); - frameSize = (int)( &((mdxaFrame_t *)0)->boneIndexes[ header->numBones ] ); +// frameSize = (int)( &((mdxaFrame_t *)0)->boneIndexes[ header->numBones ] ); - aframe = (mdxaFrame_t *)((byte *)header + header->ofsFrames + (frame * frameSize)); +// aframe = (mdxaFrame_t *)((byte *)header + header->ofsFrames + (frame * frameSize)); // walk each bone and list it's name for (x=0; x< mod_a->mdxa->numBones; x++) { @@ -184,7 +186,7 @@ qboolean G2_GetAnimFileName(const char *fileName, char **filename) // ///////////////////////////////////////////////////////////////////// -int G2_DecideTraceLod(CGhoul2Info &ghoul2, int useLod, const model_t *mod) +int G2_DecideTraceLod(CGhoul2Info &ghoul2, int useLod) { int returnLod = useLod; @@ -193,13 +195,16 @@ int G2_DecideTraceLod(CGhoul2Info &ghoul2, int useLod, const model_t *mod) { returnLod = ghoul2.mLodBias; } + assert(G2_MODEL_OK(&ghoul2)); + assert(ghoul2.currentModel); + assert(ghoul2.currentModel->mdxm); //what about r_lodBias? // now ensure that we haven't selected a lod that doesn't exist for this model - if ( returnLod >= mod->numLods ) + if ( returnLod >= ghoul2.currentModel->mdxm->numLODs ) { - returnLod = mod->numLods - 1; + returnLod = ghoul2.currentModel->mdxm->numLODs - 1; } return returnLod; @@ -227,28 +232,36 @@ void R_TransformEachSurface( const mdxmSurface_t *surface, vec3_t scale, CMiniHe // whip through and actually transform each vertex const int numVerts = surface->numVerts; v = (mdxmVertex_t *) ((byte *)surface + surface->ofsVerts); + mdxmVertexTexCoord_t *pTexCoords = (mdxmVertexTexCoord_t *) &v[numVerts]; + // optimisation issue if ((scale[0] != 1.0) || (scale[1] != 1.0) || (scale[2] != 1.0)) { for ( j = 0; j < numVerts; j++ ) { vec3_t tempVert, tempNormal; - mdxmWeight_t *w; +// mdxmWeight_t *w; VectorClear( tempVert ); VectorClear( tempNormal ); - w = v->weights; - for ( k = 0 ; k < v->numWeights ; k++, w++ ) +// w = v->weights; + + const int iNumWeights = G2_GetVertWeights( v ); + + for ( k = 0 ; k < iNumWeights ; k++ ) { - const mdxaBone_t &bone=EvalBoneCache(piBoneReferences[w->boneIndex],boneCache); + int iBoneIndex = G2_GetVertBoneIndex( v, k ); + float fBoneWeight = G2_GetVertBoneWeight( v, k ); - tempVert[0] += w->boneWeight * ( DotProduct( bone.matrix[0], v->vertCoords ) + bone.matrix[0][3] ); - tempVert[1] += w->boneWeight * ( DotProduct( bone.matrix[1], v->vertCoords ) + bone.matrix[1][3] ); - tempVert[2] += w->boneWeight * ( DotProduct( bone.matrix[2], v->vertCoords ) + bone.matrix[2][3] ); + const mdxaBone_t &bone=EvalBoneCache(piBoneReferences[iBoneIndex],boneCache); - tempNormal[0] += w->boneWeight * DotProduct( bone.matrix[0], v->normal ); - tempNormal[1] += w->boneWeight * DotProduct( bone.matrix[1], v->normal ); - tempNormal[2] += w->boneWeight * DotProduct( bone.matrix[2], v->normal ); + tempVert[0] += fBoneWeight * ( DotProduct( bone.matrix[0], v->vertCoords ) + bone.matrix[0][3] ); + tempVert[1] += fBoneWeight * ( DotProduct( bone.matrix[1], v->vertCoords ) + bone.matrix[1][3] ); + tempVert[2] += fBoneWeight * ( DotProduct( bone.matrix[2], v->vertCoords ) + bone.matrix[2][3] ); + + tempNormal[0] += fBoneWeight * DotProduct( bone.matrix[0], v->normal ); + tempNormal[1] += fBoneWeight * DotProduct( bone.matrix[1], v->normal ); + tempNormal[2] += fBoneWeight * DotProduct( bone.matrix[2], v->normal ); } int pos = j * 5; @@ -257,10 +270,10 @@ void R_TransformEachSurface( const mdxmSurface_t *surface, vec3_t scale, CMiniHe TransformedVerts[pos++] = tempVert[1] * scale[1]; TransformedVerts[pos++] = tempVert[2] * scale[2]; // we will need the S & T coors too for hitlocation and hitmaterial stuff - TransformedVerts[pos++] = v->texCoords[0]; - TransformedVerts[pos] = v->texCoords[1]; + TransformedVerts[pos++] = pTexCoords[j].texCoords[0]; + TransformedVerts[pos] = pTexCoords[j].texCoords[1]; - v = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surface->maxVertBoneWeights]; + v++;// = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surface->maxVertBoneWeights]; } } else @@ -269,22 +282,28 @@ void R_TransformEachSurface( const mdxmSurface_t *surface, vec3_t scale, CMiniHe for ( j = 0; j < numVerts; j++ ) { vec3_t tempVert, tempNormal; - const mdxmWeight_t *w; +// const mdxmWeight_t *w; VectorClear( tempVert ); VectorClear( tempNormal ); - w = v->weights; - for ( k = 0 ; k < v->numWeights ; k++, w++ ) +// w = v->weights; + + const int iNumWeights = G2_GetVertWeights( v ); + + for ( k = 0 ; k < iNumWeights ; k++ ) { - const mdxaBone_t &bone=EvalBoneCache(piBoneReferences[w->boneIndex],boneCache); + int iBoneIndex = G2_GetVertBoneIndex( v, k ); + float fBoneWeight = G2_GetVertBoneWeight( v, k ); - tempVert[0] += w->boneWeight * ( DotProduct( bone.matrix[0], v->vertCoords ) + bone.matrix[0][3] ); - tempVert[1] += w->boneWeight * ( DotProduct( bone.matrix[1], v->vertCoords ) + bone.matrix[1][3] ); - tempVert[2] += w->boneWeight * ( DotProduct( bone.matrix[2], v->vertCoords ) + bone.matrix[2][3] ); + const mdxaBone_t &bone=EvalBoneCache(piBoneReferences[iBoneIndex],boneCache); - tempNormal[0] += w->boneWeight * DotProduct( bone.matrix[0], v->normal ); - tempNormal[1] += w->boneWeight * DotProduct( bone.matrix[1], v->normal ); - tempNormal[2] += w->boneWeight * DotProduct( bone.matrix[2], v->normal ); + tempVert[0] += fBoneWeight * ( DotProduct( bone.matrix[0], v->vertCoords ) + bone.matrix[0][3] ); + tempVert[1] += fBoneWeight * ( DotProduct( bone.matrix[1], v->vertCoords ) + bone.matrix[1][3] ); + tempVert[2] += fBoneWeight * ( DotProduct( bone.matrix[2], v->vertCoords ) + bone.matrix[2][3] ); + + tempNormal[0] += fBoneWeight * DotProduct( bone.matrix[0], v->normal ); + tempNormal[1] += fBoneWeight * DotProduct( bone.matrix[1], v->normal ); + tempNormal[2] += fBoneWeight * DotProduct( bone.matrix[2], v->normal ); } // copy tranformed verts into temp space @@ -292,10 +311,10 @@ void R_TransformEachSurface( const mdxmSurface_t *surface, vec3_t scale, CMiniHe TransformedVerts[pos++] = tempVert[1]; TransformedVerts[pos++] = tempVert[2]; // we will need the S & T coors too for hitlocation and hitmaterial stuff - TransformedVerts[pos++] = v->texCoords[0]; - TransformedVerts[pos++] = v->texCoords[1]; + TransformedVerts[pos++] = pTexCoords[j].texCoords[0]; + TransformedVerts[pos++] = pTexCoords[j].texCoords[1]; - v = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surface->maxVertBoneWeights]; + v++;// = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surface->maxVertBoneWeights]; } } } @@ -304,8 +323,10 @@ void G2_TransformSurfaces(int surfaceNum, surfaceInfo_v &rootSList, CBoneCache *boneCache, const model_t *currentModel, int lod, vec3_t scale, CMiniHeap *G2VertSpace, int *TransformedVertArray, bool secondTimeAround) { int i; + assert(currentModel); + assert(currentModel->mdxm); // back track and get the surfinfo struct for this surface - const mdxmSurface_t *surface = (mdxmSurface_t *)G2_FindSurface((void *)currentModel, surfaceNum, lod); + const mdxmSurface_t *surface = (mdxmSurface_t *)G2_FindSurface(currentModel, surfaceNum, lod); const mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)currentModel->mdxm + sizeof(mdxmHeader_t)); const mdxmSurfHierarchy_t *surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surface->thisSurfaceIndex]); @@ -343,7 +364,6 @@ void G2_TransformSurfaces(int surfaceNum, surfaceInfo_v &rootSList, void G2_TransformModel(CGhoul2Info_v &ghoul2, const int frameNum, vec3_t scale, CMiniHeap *G2VertSpace, int useLod) { int i, lod; - const model_t *currentModel; vec3_t correctScale; @@ -365,35 +385,32 @@ void G2_TransformModel(CGhoul2Info_v &ghoul2, const int frameNum, vec3_t scale, // walk each possible model for this entity and try rendering it out for (i=0; iMiniHeapAlloc(currentModel->mdxm->numSurfaces * 4); - if (!ghoul2[i].mTransformedVertsArray) + ghoul2[i].mTransformedVertsArray = (int*)G2VertSpace->MiniHeapAlloc(g.currentModel->mdxm->numSurfaces * 4); + if (!g.mTransformedVertsArray) { Com_Error(ERR_DROP, "Ran out of transform space for Ghoul2 Models. Adjust MiniHeapSize in SV_SpawnServer.\n"); } - memset(ghoul2[i].mTransformedVertsArray, 0,(currentModel->mdxm->numSurfaces * 4)); + memset(g.mTransformedVertsArray, 0,(g.currentModel->mdxm->numSurfaces * 4)); - // did we get enough space? - assert(ghoul2[i].mTransformedVertsArray); - - G2_FindOverrideSurface(-1,ghoul2[i].mSlist); //reset the quick surface override lookup; + G2_FindOverrideSurface(-1,g.mSlist); //reset the quick surface override lookup; // recursively call the model surface transform - G2_TransformSurfaces(ghoul2[i].mSurfaceRoot, ghoul2[i].mSlist, ghoul2[i].mBoneCache, currentModel, lod, correctScale, G2VertSpace, ghoul2[i].mTransformedVertsArray, false); + G2_TransformSurfaces(g.mSurfaceRoot, g.mSlist, g.mBoneCache, g.currentModel, lod, correctScale, G2VertSpace, g.mTransformedVertsArray, false); } } @@ -853,7 +870,9 @@ static void G2_TraceSurfaces(CTraceSurface &TS) { int i; // back track and get the surfinfo struct for this surface - const mdxmSurface_t *surface = (mdxmSurface_t *)G2_FindSurface((void *)TS.currentModel, TS.surfaceNum, TS.lod); + assert(TS.currentModel); + assert(TS.currentModel->mdxm); + const mdxmSurface_t *surface = (mdxmSurface_t *)G2_FindSurface(TS.currentModel, TS.surfaceNum, TS.lod); const mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)TS.currentModel->mdxm + sizeof(mdxmHeader_t)); const mdxmSurfHierarchy_t *surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surface->thisSurfaceIndex]); @@ -924,7 +943,6 @@ static void G2_TraceSurfaces(CTraceSurface &TS) void G2_TraceModels(CGhoul2Info_v &ghoul2, vec3_t rayStart, vec3_t rayEnd, CCollisionRecord *collRecMap, int entNum, EG2_Collision eG2TraceType, int useLod, float fRadius) { int i, lod; - const model_t *currentModel; skin_t *skin; shader_t *cust_shader; @@ -932,21 +950,17 @@ void G2_TraceModels(CGhoul2Info_v &ghoul2, vec3_t rayStart, vec3_t rayEnd, CColl for (i=0; imdxm); // point at first lod list byte *current = (byte*)((int)mod->mdxm + (int)mod->mdxm->ofsLODs); @@ -1095,7 +1109,7 @@ qboolean G2_SaveGhoul2Models(CGhoul2Info_v &ghoul2, char **buffer, int *size) { // is there anything to save? - if (!ghoul2.size()) + if (!ghoul2.IsValid()||!ghoul2.size()) { *buffer = (char *)Z_Malloc(4, TAG_GHOUL2, qfalse); int *tempBuffer = (int *)*buffer; @@ -1228,17 +1242,19 @@ void G2_LoadGhoul2Model(CGhoul2Info_v &ghoul2, char *buffer) // now we have enough instances, lets go through each one and load up the relevant details for (int i=0; imValid&&(g)->aHeader&&(g)->currentModel&&(g)->animModel) + #pragma warning(disable : 4512) //assignment op could not be genereated class CQuickOverride @@ -117,11 +119,12 @@ const surfaceInfo_t *G2_FindOverrideSurface(int surfaceNum,const surfaceInfo_v & // given a surface name, lets see if it's legal in the model -int G2_IsSurfaceLegal(const void *mod, const char *surfaceName, int *flags) +int G2_IsSurfaceLegal(const model_s *mod_m, const char *surfaceName, int *flags) { + assert(mod_m); + assert(mod_m->mdxm); // damn include file dependancies mdxmSurfHierarchy_t *surf; - const model_t *mod_m = (const model_t *)mod; surf = (mdxmSurfHierarchy_t *) ( (byte *)mod_m->mdxm + mod_m->mdxm->ofsSurfHierarchy ); for ( int i = 0 ; i < mod_m->mdxm->numSurfaces ; i++) @@ -150,31 +153,21 @@ int G2_IsSurfaceLegal(const void *mod, const char *surfaceName, int *flags) * pointer to surface if successful, false otherwise * ************************************************************************************************/ -const mdxmSurface_t *G2_FindSurface(const char *fileName, surfaceInfo_v &slist, const char *surfaceName, +const mdxmSurface_t *G2_FindSurface(CGhoul2Info *ghlInfo, surfaceInfo_v &slist, const char *surfaceName, int *surfIndex/*NULL*/) { int i = 0; // find the model we want - const model_t *mod = R_GetModelByHandle(RE_RegisterModel(fileName)); - const mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)mod->mdxm + sizeof(mdxmHeader_t)); + assert(G2_MODEL_OK(ghlInfo)); - // did we find a ghoul 2 model or not? - if (!mod->mdxm) - { - assert(0); - if (surfIndex) - { - *surfIndex = -1; - } - return 0; - } + const mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)ghlInfo->currentModel->mdxm + sizeof(mdxmHeader_t)); // first find if we already have this surface in the list for (i = slist.size() - 1; i >= 0; i--) { if ((slist[i].surface != 10000) && (slist[i].surface != -1)) { - const mdxmSurface_t *surf = (mdxmSurface_t *)G2_FindSurface((void *)mod, slist[i].surface, 0); + const mdxmSurface_t *surf = (mdxmSurface_t *)G2_FindSurface(ghlInfo->currentModel, slist[i].surface, 0); // back track and get the surfinfo struct for this surface const mdxmSurfHierarchy_t *surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surf->thisSurfaceIndex]); @@ -199,37 +192,33 @@ const mdxmSurface_t *G2_FindSurface(const char *fileName, surfaceInfo_v &slist, } // set a named surface offFlags - if it doesn't find a surface with this name in the list then it will add one. -qboolean G2_SetSurfaceOnOff (const char *fileName, surfaceInfo_v &slist, const char *surfaceName, const int offFlags) +qboolean G2_SetSurfaceOnOff (CGhoul2Info *ghlInfo, surfaceInfo_v &slist, const char *surfaceName, const int offFlags) { int surfIndex = -1; surfaceInfo_t temp_slist_entry; // find the model we want - const model_t *mod = R_GetModelByHandle(RE_RegisterModel(fileName)); - - // did we find a ghoul 2 model or not? - if (!mod->mdxm) - { - assert(0); - return qfalse; - } - // first find if we already have this surface in the list - const mdxmSurface_t *surf = G2_FindSurface(fileName, slist, surfaceName, &surfIndex); + const mdxmSurface_t *surf = G2_FindSurface(ghlInfo, slist, surfaceName, &surfIndex); if (surf) { // set descendants value - slist[surfIndex].offFlags = offFlags; + + // slist[surfIndex].offFlags = offFlags; + // seems to me that we shouldn't overwrite the other flags. + // the only bit we really care about in the incoming flags is the off bit + slist[surfIndex].offFlags &= ~(G2SURFACEFLAG_OFF | G2SURFACEFLAG_NODESCENDANTS); + slist[surfIndex].offFlags |= offFlags & (G2SURFACEFLAG_OFF | G2SURFACEFLAG_NODESCENDANTS); return qtrue; } else { // ok, not in the list already - in that case, lets verify this surface exists in the model mesh int flags; - int surfaceNum = G2_IsSurfaceLegal((void*)mod, surfaceName, &flags); + int surfaceNum = G2_IsSurfaceLegal(ghlInfo->currentModel, surfaceName, &flags); if (surfaceNum != -1) { // the only bit we really care about in the incoming flags is the off bit - flags &= !(G2SURFACEFLAG_OFF | G2SURFACEFLAG_NODESCENDANTS); + flags &= ~(G2SURFACEFLAG_OFF | G2SURFACEFLAG_NODESCENDANTS); flags |= offFlags & (G2SURFACEFLAG_OFF | G2SURFACEFLAG_NODESCENDANTS); // insert here then @@ -244,47 +233,12 @@ qboolean G2_SetSurfaceOnOff (const char *fileName, surfaceInfo_v &slist, const c return qfalse; } -// return a named surfaces off flags - should tell you if this surface is on or off. -int G2_IsSurfaceOff (const char *fileName, surfaceInfo_v &slist, const char *surfaceName) -{ - const model_t *mod = R_GetModelByHandle(RE_RegisterModel(fileName)); - int surfIndex = -1; - - // did we find a ghoul 2 model or not? - if (!mod->mdxm) - { - return 0; - } - - // first find if we already have this surface in the list - const mdxmSurface_t *surf = surf = G2_FindSurface(fileName, slist, surfaceName, &surfIndex); - if (surf) - { - // set descendants value - return slist[surfIndex].offFlags; - } - // ok, we didn't find it in the surface list. Lets look at the original surface then. - - mdxmSurfHierarchy_t *surface = (mdxmSurfHierarchy_t *) ( (byte *)mod->mdxm + mod->mdxm->ofsSurfHierarchy ); - - for ( int i = 0 ; i < mod->mdxm->numSurfaces ; i++) - { - if (!stricmp(surfaceName, surface->name)) - { - return surface->flags; - } - // find the next surface - surface = (mdxmSurfHierarchy_t *)( (byte *)surface + (int)( &((mdxmSurfHierarchy_t *)0)->childIndexes[ surface->numChildren ] )); - } - - assert(0); - return 0; -} - void G2_FindRecursiveSurface(const model_t *currentModel, int surfaceNum, surfaceInfo_v &rootList, int *activeSurfaces) { + assert(currentModel); + assert(currentModel->mdxm); int i; - const mdxmSurface_t *surface = (mdxmSurface_t *)G2_FindSurface((void *)currentModel, surfaceNum, 0); + const mdxmSurface_t *surface = (mdxmSurface_t *)G2_FindSurface(currentModel, surfaceNum, 0); const mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)currentModel->mdxm + sizeof(mdxmHeader_t)); const mdxmSurfHierarchy_t *surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surface->thisSurfaceIndex]); @@ -321,206 +275,106 @@ void G2_FindRecursiveSurface(const model_t *currentModel, int surfaceNum, surfac } -void G2_RemoveRedundantGeneratedSurfaces(surfaceInfo_v &slist, int *activeSurfaces) +qboolean G2_SetRootSurface( CGhoul2Info_v &ghoul2, const int modelIndex, const char *surfaceName) { - int i; - - // walk the surface list, removing surface overrides or generated surfaces that are pointing at surfaces that aren't active anymore - for (i=0; imdxm) - { - return qfalse; - } - + int flags; + assert(modelIndex>=0&&modelIndexmdxm); // first find if we already have this surface in the list - surf = G2_IsSurfaceLegal(mod_m, surfaceName, &flags); + surf = G2_IsSurfaceLegal(ghoul2[modelIndex].currentModel, surfaceName, &flags); if (surf != -1) { - // first see if this ghoul2 model already has this as a root surface - if (ghoul2[modelIndex].mSurfaceRoot == surf) - { - return qtrue; - } - - // set the root surface ghoul2[modelIndex].mSurfaceRoot = surf; - - // ok, now the tricky bits. - // firstly, generate a list of active / on surfaces below the root point - - // gimme some space to put this list into - activeSurfaces = (int *)Z_Malloc(mod_m->mdxm->numSurfaces * 4, TAG_GHOUL2,qfalse); - memset(activeSurfaces, 0, (mod_m->mdxm->numSurfaces * 4)); - - G2_FindOverrideSurface(-1,ghoul2[modelIndex].mSlist); //reset the quick surface override lookup; - - G2_FindRecursiveSurface(mod_m, surf, ghoul2[modelIndex].mSlist, activeSurfaces); - - // now remove all procedural or override surfaces that refer to surfaces that arent on this list - G2_RemoveRedundantGeneratedSurfaces(ghoul2[modelIndex].mSlist, activeSurfaces); - - // remember to free what we used - Z_Free(activeSurfaces); - return (qtrue); + return qtrue; } assert(0); return qfalse; } -extern int G2_DecideTraceLod(CGhoul2Info &ghoul2, int useLod, const model_t *mod); +extern int G2_DecideTraceLod(CGhoul2Info &ghoul2, int useLod); int G2_AddSurface(CGhoul2Info *ghoul2, int surfaceNumber, int polyNumber, float BarycentricI, float BarycentricJ, int lod ) { - - surfaceInfo_t temp_slist_entry; - - // decide if LOD is legal - const model_t * const currentModel = R_GetModelByHandle(RE_RegisterModel(ghoul2->mFileName)); - lod = G2_DecideTraceLod(*(CGhoul2Info *)(ghoul2), lod, currentModel); + lod = G2_DecideTraceLod(*ghoul2, lod); // first up, see if we have a free one already set up - look only from the end of the constant surfaces onwards - for (int i=0; imSlist.size(); i++) + int i; + for (i=0; imSlist.size(); i++) { // is the surface count -1? That would indicate it's free if (ghoul2->mSlist[i].surface == -1) { - ghoul2->mSlist[i].offFlags = G2SURFACEFLAG_GENERATED; - ghoul2->mSlist[i].surface = 10000; // no model will ever have 10000 surfaces - ghoul2->mSlist[i].genBarycentricI = BarycentricI; - ghoul2->mSlist[i].genBarycentricJ = BarycentricJ; - ghoul2->mSlist[i].genPolySurfaceIndex = ((polyNumber & 0xffff) << 16) | (surfaceNumber & 0xffff); - ghoul2->mSlist[i].genLod = lod; - return i; + break; } } - - // ok, didn't find one. Better create one - - temp_slist_entry.offFlags = G2SURFACEFLAG_GENERATED; - temp_slist_entry.surface = 10000; - temp_slist_entry.genBarycentricI = BarycentricI; - temp_slist_entry.genBarycentricJ = BarycentricJ; - temp_slist_entry.genPolySurfaceIndex = ((polyNumber & 0xffff) << 16) | (surfaceNumber & 0xffff); - temp_slist_entry.genLod = lod; - - ghoul2->mSlist.push_back(temp_slist_entry); - - return (ghoul2->mSlist.size() -1 ); + if (i==ghoul2->mSlist.size()) + { + ghoul2->mSlist.push_back(surfaceInfo_t()); + } + ghoul2->mSlist[i].offFlags = G2SURFACEFLAG_GENERATED; + ghoul2->mSlist[i].surface = 10000; // no model will ever have 10000 surfaces + ghoul2->mSlist[i].genBarycentricI = BarycentricI; + ghoul2->mSlist[i].genBarycentricJ = BarycentricJ; + ghoul2->mSlist[i].genPolySurfaceIndex = ((polyNumber & 0xffff) << 16) | (surfaceNumber & 0xffff); + ghoul2->mSlist[i].genLod = lod; + return i; } qboolean G2_RemoveSurface(surfaceInfo_v &slist, const int index) { - // did we find it? if (index != -1) { - // set us to be the 'not active' state slist[index].surface = -1; - - int newSize = slist.size(); - // now look through the list from the back and see if there is a block of -1's we can resize off the end of the list - for (int i=slist.size()-1; i>-1; i--) - { - if (slist[i].surface == -1) - { - newSize = i; - } - // once we hit one that isn't a -1, we are done. - else - { - break; - } - } - // do we need to resize? - if (newSize != slist.size()) - { - // yes, so lets do it - slist.resize(newSize); - } - return qtrue; } - assert(0); - - // no return qfalse; } -int G2_GetParentSurface(const char *fileName, const int index) +int G2_GetParentSurface(CGhoul2Info *ghlInfo, const int index) { - const model_t *mod = R_GetModelByHandle(RE_RegisterModel(fileName)); - const mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)mod->mdxm + sizeof(mdxmHeader_t)); + assert(ghlInfo->currentModel); + assert(ghlInfo->currentModel->mdxm); + const mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)ghlInfo->currentModel->mdxm + sizeof(mdxmHeader_t)); // walk each surface and see if this index is listed in it's children - const mdxmSurface_t *surf = (mdxmSurface_t *)G2_FindSurface((void *)mod, index, 0); + const mdxmSurface_t *surf = (mdxmSurface_t *)G2_FindSurface(ghlInfo->currentModel, index, 0); const mdxmSurfHierarchy_t *surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surf->thisSurfaceIndex]); return surfInfo->parentIndex; } -int G2_GetSurfaceIndex(const char *fileName, const char *surfaceName) +int G2_GetSurfaceIndex(CGhoul2Info *ghlInfo, const char *surfaceName) { - const model_t *mod = R_GetModelByHandle(RE_RegisterModel(fileName)); int flags; - - return G2_IsSurfaceLegal(mod, surfaceName, &flags); + assert(ghlInfo->currentModel); + return G2_IsSurfaceLegal(ghlInfo->currentModel, surfaceName, &flags); } -int G2_IsSurfaceRendered(const char *fileName, const char *surfaceName, surfaceInfo_v &slist) +int G2_IsSurfaceRendered(CGhoul2Info *ghlInfo, const char *surfaceName, surfaceInfo_v &slist) { - const model_t *mod = R_GetModelByHandle(RE_RegisterModel(fileName)); int surfIndex = -1; const mdxmSurface_t *parentSurf = 0; int flags = 0, surfFlags = 0; int parentFlags = 0; int parentSurfNum = 0; - const mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)mod->mdxm + sizeof(mdxmHeader_t)); - const mdxmSurfHierarchy_t *parentSurfInfo = 0; - - // did we find a ghoul 2 model or not? - if (!mod->mdxm) + assert(ghlInfo->currentModel); + assert(ghlInfo->currentModel->mdxm); + if (!ghlInfo->currentModel->mdxm) { return 0; } + const mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)ghlInfo->currentModel->mdxm + sizeof(mdxmHeader_t)); + const mdxmSurfHierarchy_t *parentSurfInfo = 0; + // now see if we already have overriden this surface in the slist - const mdxmSurface_t *surf = G2_FindSurface(fileName, slist, surfaceName, &surfIndex); + const mdxmSurface_t *surf = G2_FindSurface(ghlInfo, slist, surfaceName, &surfIndex); if (surf) { // set descendants value @@ -531,7 +385,7 @@ int G2_IsSurfaceRendered(const char *fileName, const char *surfaceName, surfaceI // now travel up the skeleton to see if any of it's ancestors have a 'no descendants' turned on // find the original surface in the surface list - int surfNum = G2_IsSurfaceLegal(mod, surfaceName, &flags); + int surfNum = G2_IsSurfaceLegal(ghlInfo->currentModel, surfaceName, &flags); if ( surfNum != -1 ) {//must be legal const mdxmSurfHierarchy_t *surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surfNum]); @@ -542,10 +396,10 @@ int G2_IsSurfaceRendered(const char *fileName, const char *surfaceName, surfaceI parentSurfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surfNum]); // find the original surface in the surface list - parentSurfNum = G2_IsSurfaceLegal(mod, surfaceName, &parentFlags); + parentSurfNum = G2_IsSurfaceLegal(ghlInfo->currentModel, surfaceName, &parentFlags); // now see if we already have overriden this surface in the slist - parentSurf = G2_FindSurface(fileName, slist, parentSurfInfo->name, &surfIndex); + parentSurf = G2_FindSurface(ghlInfo, slist, parentSurfInfo->name, &surfIndex); if (parentSurf) { // set descendants value @@ -554,7 +408,7 @@ int G2_IsSurfaceRendered(const char *fileName, const char *surfaceName, surfaceI // now we have the parent flags, lets see if any have the 'no descendants' flag set if (parentFlags & G2SURFACEFLAG_NODESCENDANTS) { - flags = G2SURFACEFLAG_OFF; + flags |= G2SURFACEFLAG_OFF; } // set up scan of next parent surfNum = parentSurfInfo->parentIndex; diff --git a/code/ghoul2/vssver.scc b/code/ghoul2/vssver.scc deleted file mode 100644 index 343426b..0000000 Binary files a/code/ghoul2/vssver.scc and /dev/null differ diff --git a/code/icarus/BlockStream.cpp b/code/icarus/BlockStream.cpp index 227b6b7..ff04ad8 100644 --- a/code/icarus/BlockStream.cpp +++ b/code/icarus/BlockStream.cpp @@ -166,7 +166,6 @@ CBlockMember *CBlockMember::Duplicate( void ) CBlock::CBlock( void ) { - m_numMembers = 0; m_flags = 0; m_id = 0; } @@ -184,7 +183,6 @@ Init int CBlock::Init( void ) { - m_numMembers = 0; m_flags = 0; m_id = 0; @@ -228,7 +226,6 @@ int CBlock::Free( void ) } m_members.clear(); //List of all CBlockMembers owned by this list - m_numMembers = 0; return true; } @@ -317,8 +314,6 @@ AddMember int CBlock::AddMember( CBlockMember *member ) { m_members.insert( m_members.end(), member ); - m_numMembers++; - return true; } @@ -330,9 +325,10 @@ GetMember CBlockMember *CBlock::GetMember( int memberNum ) { - if ( memberNum > m_numMembers-1 ) + if ( memberNum > GetNumMembers()-1 ) + { return false; - + } return m_members[ memberNum ]; } @@ -344,9 +340,10 @@ GetMemberData void *CBlock::GetMemberData( int memberNum ) { - if ( memberNum > m_numMembers-1 ) + if ( memberNum > GetNumMembers()-1 ) + { return NULL; - + } return (void *) ((GetMember( memberNum ))->GetData()); } diff --git a/code/icarus/Interface.cpp b/code/icarus/Interface.cpp deleted file mode 100644 index 6e5c1fa..0000000 --- a/code/icarus/Interface.cpp +++ /dev/null @@ -1,18 +0,0 @@ -// ICARUS Engine Interface File -// -// This file is the only section of the ICARUS systems that -// is not directly portable from engine to engine. -// -// -- jweier - -#include "Interface.h" - -void Interface_Init( interface_export_t *pe ) -{ - - //TODO: This is where you link up all your functions to the engine - - //Example: - // - // pe->I_GetEntityByName = ENGINE_GetEntityByName; -} diff --git a/code/icarus/TaskManager.cpp b/code/icarus/TaskManager.cpp index ce7c547..f1bb8d6 100644 --- a/code/icarus/TaskManager.cpp +++ b/code/icarus/TaskManager.cpp @@ -98,7 +98,6 @@ void CTaskGroup::Init( void ) { m_completedTasks.clear(); - m_numTasks = 0; m_numCompleted = 0; m_parent = NULL; } @@ -112,8 +111,6 @@ Add int CTaskGroup::Add( CTask *task ) { m_completedTasks[ task->GetGUID() ] = false; - m_numTasks++; - return TASK_OK; } @@ -130,14 +127,6 @@ bool CTaskGroup::MarkTaskComplete( int id ) m_completedTasks[ id ] = true; m_numCompleted++; - //Release the extra baggage, we only need to know that we're done now - /* - if ( m_numCompleted == m_numTasks ) - { - m_completedTasks.clear(); - } - */ - return true; } @@ -183,11 +172,11 @@ int CTaskManager::Init( CSequencer *owner ) if ( owner == NULL ) return TASK_FAILED; + m_tasks.clear(); m_owner = owner; m_ownerID = owner->GetOwnerID(); m_curGroup = NULL; m_GUID = 0; - m_numTasks = 0; m_resident = false; return TASK_OK; @@ -328,6 +317,10 @@ Update int CTaskManager::Update( void ) { + if ( (g_entities[m_ownerID].svFlags&SVF_ICARUS_FREEZE) ) + { + return TASK_FAILED; + } m_count = 0; //Needed for runaway init m_resident = true; @@ -966,14 +959,12 @@ int CTaskManager::PushTask( CTask *task, int flag ) { case PUSH_FRONT: m_tasks.insert(m_tasks.begin(), task); - m_numTasks++; return TASK_OK; break; case PUSH_BACK: m_tasks.insert(m_tasks.end(), task); - m_numTasks++; return TASK_OK; break; @@ -1003,7 +994,6 @@ CTask *CTaskManager::PopTask( int flag ) case POP_FRONT: task = m_tasks.front(); m_tasks.pop_front(); - m_numTasks--; return task; break; @@ -1011,7 +1001,6 @@ CTask *CTaskManager::PopTask( int flag ) case POP_BACK: task = m_tasks.back(); m_tasks.pop_back(); - m_numTasks--; return task; break; @@ -1660,7 +1649,8 @@ void CTaskManager::Save( void ) (m_owner->GetInterface())->I_WriteSaveData( 'TMID', &m_GUID, sizeof( m_GUID ) ); //FIXME: This can be reconstructed //Save out the number of tasks that will follow - (m_owner->GetInterface())->I_WriteSaveData( 'TSK#', &m_numTasks, sizeof( m_numTasks ) ); + int iNumTasks = m_tasks.size(); + (m_owner->GetInterface())->I_WriteSaveData( 'TSK#', &iNumTasks, sizeof(iNumTasks) ); //Save out all the tasks tasks_l::iterator ti; @@ -1703,7 +1693,7 @@ void CTaskManager::Save( void ) (m_owner->GetInterface())->I_WriteSaveData( 'TKGP', &id, sizeof( id ) ); //Save out the number of commands - numCommands = (*tgi)->m_numTasks; + numCommands = (*tgi)->m_completedTasks.size(); (m_owner->GetInterface())->I_WriteSaveData( 'TGNC', &numCommands, sizeof( numCommands ) ); //Save out the command map @@ -1865,6 +1855,10 @@ void CTaskManager::Load( void ) block->Write( ID_TAG, (float) ID_TAG ); break; + case ID_GET: + block->Write( ID_GET, (float) ID_GET ); + break; + default: (m_owner->GetInterface())->I_DPrintf( WL_ERROR, "Invalid Block id %d\n", bID); assert( 0 ); @@ -1878,7 +1872,6 @@ void CTaskManager::Load( void ) task->SetBlock( block ); STL_INSERT( m_tasks, task ); - m_numTasks++; } //Load the task groups @@ -1922,9 +1915,6 @@ void CTaskManager::Load( void ) //Get the number of commands in this group (m_owner->GetInterface())->I_ReadSaveData( 'TGNC', &numMembers, sizeof( numMembers ) ); - //Save the number of members - taskGroup->m_numTasks = numMembers; - //Get each command and its completion state for ( int j = 0; j < numMembers; j++ ) { diff --git a/code/icarus/blockstream.h b/code/icarus/blockstream.h index 2140fba..3b6c9cd 100644 --- a/code/icarus/blockstream.h +++ b/code/icarus/blockstream.h @@ -127,7 +127,7 @@ public: CBlock *Duplicate( void ); int GetBlockID( void ) const { return m_id; } //Get the ID for the block - int GetNumMembers( void ) const { return m_numMembers; } //Get the number of member in the block's list + int GetNumMembers( void ) const { return m_members.size();} //Get the number of member in the block's list void SetFlags( unsigned char flags ) { m_flags = flags; } void SetFlag( unsigned char flag ) { m_flags |= flag; } @@ -138,7 +138,6 @@ public: protected: blockMember_v m_members; //List of all CBlockMembers owned by this list - int m_numMembers; //Number of members in m_members int m_id; //ID of the block unsigned char m_flags; }; diff --git a/code/icarus/module.h b/code/icarus/module.h deleted file mode 100644 index 6ae5319..0000000 --- a/code/icarus/module.h +++ /dev/null @@ -1 +0,0 @@ -//This needs to be present to make the tokenizer happy... \ No newline at end of file diff --git a/code/icarus/taskmanager.h b/code/icarus/taskmanager.h index 633825e..fe15c62 100644 --- a/code/icarus/taskmanager.h +++ b/code/icarus/taskmanager.h @@ -72,7 +72,7 @@ public: void SetGUID( int GUID ); void SetParent( CTaskGroup *group ) { m_parent = group; } - bool Complete( void ) const { return ( m_numTasks == m_numCompleted ); } + bool Complete(void) const { return ( m_numCompleted == m_completedTasks.size() ); } bool MarkTaskComplete( int id ); @@ -85,7 +85,6 @@ public: CTaskGroup *m_parent; - int m_numTasks; int m_numCompleted; int m_GUID; }; @@ -174,7 +173,6 @@ protected: taskGroup_v m_taskGroups; tasks_l m_tasks; - int m_numTasks; int m_GUID; int m_count; diff --git a/code/icarus/vssver.scc b/code/icarus/vssver.scc deleted file mode 100644 index e5f0782..0000000 Binary files a/code/icarus/vssver.scc and /dev/null differ diff --git a/code/jpeg-6/vssver.scc b/code/jpeg-6/vssver.scc deleted file mode 100644 index 88b5c60..0000000 Binary files a/code/jpeg-6/vssver.scc and /dev/null differ diff --git a/code/mac/MacGamma.c b/code/mac/MacGamma.c deleted file mode 100644 index e468450..0000000 --- a/code/mac/MacGamma.c +++ /dev/null @@ -1,487 +0,0 @@ -/* - File: MacGamma.cpp - - Contains: Functions to enable Mac OS device gamma adjustments using Windows common 3 channel 256 element 8 bit gamma ramps - - Written by: Geoff Stahl - - Copyright: Copyright © 1999 Apple Computer, Inc., All Rights Reserved - - Change History (most recent first): - - <4> 5/20/99 GGS Added handling for gamma tables with different data widths, - number of entries, and channels. Forced updates to 3 channels - (poss. could break on rare card, but very unlikely). Added - quick update with BlockMove for 3x256x8 tables. Updated function - names. - <3> 5/20/99 GGS Cleaned up and commented - <2> 5/20/99 GGS Added system wide get and restore gamma functions to enable - restoration of original for all devices. Modified functionality - to return pointers vice squirreling away the memory. - <1> 5/20/99 GGS Initial Add -*/ - - - -// system includes ---------------------------------------------------------- - -#include -#include -#include -#include -#include -#include - - - -// project includes --------------------------------------------------------- - -#include "MacGamma.h" - - - -// functions (external/public) ---------------------------------------------- - -// GetRawDeviceGamma - -// Returns the device gamma table pointer in ppDeviceTable - -OSErr GetGammaTable (GDHandle hGD, GammaTblPtr * ppTableGammaOut) -{ - VDGammaRecord DeviceGammaRec; - CntrlParam cParam; - OSErr err; - - cParam.ioCompletion = NULL; // set up control params - cParam.ioNamePtr = NULL; - cParam.ioVRefNum = 0; - cParam.ioCRefNum = (**hGD).gdRefNum; - cParam.csCode = cscGetGamma; // Get Gamma commnd to device - *(Ptr *)cParam.csParam = (Ptr) &DeviceGammaRec; // record for gamma - - err = PBStatus( (ParmBlkPtr)&cParam, 0 ); // get gamma - - *ppTableGammaOut = (GammaTblPtr)(DeviceGammaRec.csGTable); // pull table out of record - - return err; -} - -// -------------------------------------------------------------------------- - -// CreateEmptyGammaTable - -// creates an empty gamma table of a given size, assume no formula data will be used - -Ptr CreateEmptyGammaTable (short channels, short entries, short bits) -{ - GammaTblPtr pTableGammaOut = NULL; - short tableSize, dataWidth; - - dataWidth = (bits + 7) / 8; // number of bytes per entry - tableSize = sizeof (GammaTbl) + (channels * entries * dataWidth); - pTableGammaOut = (GammaTblPtr) NewPtrClear (tableSize); // allocate new tabel - - if (pTableGammaOut) // if we successfully allocated - { - pTableGammaOut->gVersion = 0; // set parameters based on input - pTableGammaOut->gType = 0; - pTableGammaOut->gFormulaSize = 0; - pTableGammaOut->gChanCnt = channels; - pTableGammaOut->gDataCnt = entries; - pTableGammaOut->gDataWidth = bits; - } - return (Ptr)pTableGammaOut; // return whatever we allocated -} - -// -------------------------------------------------------------------------- - -// CopyGammaTable - -// given a pointer toa device gamma table properly iterates and copies - -Ptr CopyGammaTable (GammaTblPtr pTableGammaIn) -{ - GammaTblPtr pTableGammaOut = NULL; - short tableSize, dataWidth; - - if (pTableGammaIn) // if there is a table to copy - { - dataWidth = (pTableGammaIn->gDataWidth + 7) / 8; // number of bytes per entry - tableSize = sizeof (GammaTbl) + pTableGammaIn->gFormulaSize + - (pTableGammaIn->gChanCnt * pTableGammaIn->gDataCnt * dataWidth); - pTableGammaOut = (GammaTblPtr) NewPtr (tableSize); // allocate new table - if (pTableGammaOut) - BlockMove( (Ptr)pTableGammaIn, (Ptr)pTableGammaOut, tableSize); // move everything - } - return (Ptr)pTableGammaOut; // return whatever we allocated, could be NULL -} - -// -------------------------------------------------------------------------- - -// DisposeGammaTable - -// disposes gamma table returned from GetGammaTable, GetDeviceGamma, or CopyGammaTable -// 5/20/99: (GGS) added - -void DisposeGammaTable (Ptr pGamma) -{ - if (pGamma) - DisposePtr((Ptr) pGamma); // get rid of it -} - -// -------------------------------------------------------------------------- - -// GetDeviceGamma - -// returns pointer to copy of orginal device gamma table in native format (allocates memory for gamma table, call DisposeDeviceGamma to delete) -// 5/20/99: (GGS) change spec to return the allocated pointer vice storing internally - -Ptr GetDeviceGamma (GDHandle hGD) -{ - GammaTblPtr pTableGammaDevice = NULL; - GammaTblPtr pTableGammaReturn = NULL; - OSErr err; - - err = GetGammaTable (hGD, &pTableGammaDevice); // get a pointer to the devices table - if ((err == noErr) && pTableGammaDevice) // if succesful - pTableGammaReturn = (GammaTblPtr) CopyGammaTable (pTableGammaDevice); // copy to global - - return (Ptr) pTableGammaReturn; -} - -// -------------------------------------------------------------------------- - -// RestoreDeviceGamma - -// sets device to saved table -// 5/20/99: (GGS) now does not delete table, avoids confusion - -void RestoreDeviceGamma (GDHandle hGD, Ptr pGammaTable) -{ - VDSetEntryRecord setEntriesRec; - VDGammaRecord gameRecRestore; - CTabHandle hCTabDeviceColors; - Ptr csPtr; - OSErr err = noErr; - - if (pGammaTable) // if we have a table to restore - { - gameRecRestore.csGTable = pGammaTable; // setup restore record - csPtr = (Ptr) &gameRecRestore; - err = Control((**hGD).gdRefNum, cscSetGamma, (Ptr) &csPtr); // restore gamma - - if ((err == noErr) && ((**(**hGD).gdPMap).pixelSize == 8)) // if successful and on an 8 bit device - { - hCTabDeviceColors = (**(**hGD).gdPMap).pmTable; // do SetEntries to force CLUT update - setEntriesRec.csTable = (ColorSpec *) &(**hCTabDeviceColors).ctTable; - setEntriesRec.csStart = 0; - setEntriesRec.csCount = (**hCTabDeviceColors).ctSize; - csPtr = (Ptr) &setEntriesRec; - - err = Control((**hGD).gdRefNum, cscSetEntries, (Ptr) &csPtr); // SetEntries in CLUT - } - } -} - -// -------------------------------------------------------------------------- - -// GetSystemGammas - -// returns a pointer to a set of all current device gammas in native format (returns NULL on failure, which means reseting gamma will not be possible) -// 5/20/99: (GGS) added - -Ptr GetSystemGammas (void) -{ - precSystemGamma pSysGammaOut; // return pointer to system device gamma info - short devCount = 0; // number of devices attached - Boolean fail = false; - GDHandle hGDevice; - - pSysGammaOut = (precSystemGamma) NewPtr (sizeof (recSystemGamma)); // allocate for structure - - hGDevice = GetDeviceList (); // top of device list - do // iterate - { - devCount++; // count devices - hGDevice = GetNextDevice (hGDevice); // next device - } while (hGDevice); - - pSysGammaOut->devGamma = (precDeviceGamma *) NewPtr (sizeof (precDeviceGamma) * devCount); // allocate for array of pointers to device records - if (pSysGammaOut) - { - pSysGammaOut->numDevices = devCount; // stuff count - - devCount = 0; // reset iteration - hGDevice = GetDeviceList (); - do - { - pSysGammaOut->devGamma [devCount] = (precDeviceGamma) NewPtr (sizeof (recDeviceGamma)); // new device record - if (pSysGammaOut->devGamma [devCount]) // if we actually allocated memory - { - pSysGammaOut->devGamma [devCount]->hGD = hGDevice; // stuff handle - pSysGammaOut->devGamma [devCount]->pDeviceGamma = (GammaTblPtr)GetDeviceGamma (hGDevice); // copy gamma table - } - else // otherwise dump record on exit - fail = true; - devCount++; // next device - hGDevice = GetNextDevice (hGDevice); - } while (hGDevice); - } - if (!fail) // if we did not fail - return (Ptr) pSysGammaOut; // return pointer to structure - else - { - DisposeSystemGammas (&(Ptr)pSysGammaOut); // otherwise dump the current structures (dispose does error checking) - return NULL; // could not complete - } -} - -// -------------------------------------------------------------------------- - -// RestoreSystemGammas - -// restores all system devices to saved gamma setting -// 5/20/99: (GGS) added - -void RestoreSystemGammas (Ptr pSystemGammas) -{ - short i; - precSystemGamma pSysGammaIn = (precSystemGamma) pSystemGammas; - if (pSysGammaIn) - for ( i = 0; i < pSysGammaIn->numDevices; i++) // for all devices - RestoreDeviceGamma (pSysGammaIn->devGamma [i]->hGD, (Ptr) pSysGammaIn->devGamma [i]->pDeviceGamma); // restore gamma -} - -// -------------------------------------------------------------------------- - -// DisposeSystemGammas - -// iterates through and deletes stored gamma settings -// 5/20/99: (GGS) added - -void DisposeSystemGammas (Ptr* ppSystemGammas) -{ - precSystemGamma pSysGammaIn; - if (ppSystemGammas) - { - pSysGammaIn = (precSystemGamma) *ppSystemGammas; - if (pSysGammaIn) - { - short i; - for (i = 0; i < pSysGammaIn->numDevices; i++) // for all devices - if (pSysGammaIn->devGamma [i]) // if pointer is valid - { - DisposeGammaTable ((Ptr) pSysGammaIn->devGamma [i]->pDeviceGamma); // dump gamma table - DisposePtr ((Ptr) pSysGammaIn->devGamma [i]); // dump device info - } - DisposePtr ((Ptr) pSysGammaIn->devGamma); // dump device pointer array - DisposePtr ((Ptr) pSysGammaIn); // dump system structure - *ppSystemGammas = NULL; - } - } -} - -// -------------------------------------------------------------------------- - -// GetDeviceGammaRampGD - -// retrieves the gamma ramp from a graphics device (pRamp: 3 arrays of 256 elements each) - -Boolean GetDeviceGammaRampGD (GDHandle hGD, Ptr pRamp) -{ - GammaTblPtr pTableGammaTemp = NULL; - long indexChan, indexEntry; - OSErr err; - - if (pRamp) // ensure pRamp is allocated - { - err = GetGammaTable (hGD, &pTableGammaTemp); // get a pointer to the current gamma - if ((err == noErr) && pTableGammaTemp) // if successful - { - // fill ramp - unsigned char * pEntry = (unsigned char *)&pTableGammaTemp->gFormulaData + pTableGammaTemp->gFormulaSize; // base of table - short bytesPerEntry = (pTableGammaTemp->gDataWidth + 7) / 8; // size, in bytes, of the device table entries - short shiftRightValue = pTableGammaTemp->gDataWidth - 8; // number of right shifts device -> ramp - short channels = pTableGammaTemp->gChanCnt; - short entries = pTableGammaTemp->gDataCnt; - if (channels == 3) // RGB format - { // note, this will create runs of entries if dest. is bigger (not linear interpolate) - for (indexChan = 0; indexChan < channels; indexChan++) - for (indexEntry = 0; indexEntry < 256; indexEntry++) - *((unsigned char *)pRamp + (indexChan << 8) + indexEntry) = - *(pEntry + (indexChan * entries * bytesPerEntry) + indexEntry * ((entries * bytesPerEntry) >> 8)) >> shiftRightValue; - } - else // single channel format - { - for (indexEntry = 0; indexEntry < 256; indexEntry++) // for all entries set vramp value - for (indexChan = 0; indexChan < channels; indexChan++) // repeat for all channels - *((unsigned char *)pRamp + (indexChan << 8) + indexEntry) = - *(pEntry + ((indexEntry * entries * bytesPerEntry) >> 8)) >> shiftRightValue; - } - return true; - } - } - return false; -} - -// -------------------------------------------------------------------------- - -// GetDeviceGammaRampGW - -// retrieves the gamma ramp from a graphics device associated with a GWorld pointer (pRamp: 3 arrays of 256 elements each) - -Boolean GetDeviceGammaRampGW (GWorldPtr pGW, Ptr pRamp) -{ - GDHandle hGD = GetGWorldDevice (pGW); - return GetDeviceGammaRampGD (hGD, pRamp); -} - -// -------------------------------------------------------------------------- - -// GetDeviceGammaRampCGP - -// retrieves the gamma ramp from a graphics device associated with a CGraf pointer (pRamp: 3 arrays of 256 elements each) - -Boolean GetDeviceGammaRampCGP (CGrafPtr pGraf, Ptr pRamp) -{ - CGrafPtr pGrafSave; - GDHandle hGDSave; - GDHandle hGD; - Boolean fResult; - - GetGWorld (&pGrafSave, &hGDSave); - SetGWorld (pGraf, NULL); - hGD = GetGDevice (); - fResult = GetDeviceGammaRampGD (hGD, pRamp); - SetGWorld (pGrafSave, hGDSave); - return fResult; -} - -// -------------------------------------------------------------------------- - -// SetDeviceGammaRampGD - -// sets the gamma ramp for a graphics device (pRamp: 3 arrays of 256 elements each (R,G,B)) - -Boolean SetDeviceGammaRampGD (GDHandle hGD, Ptr pRamp) -{ - VDSetEntryRecord setEntriesRec; - VDGammaRecord gameRecRestore; - GammaTblPtr pTableGammaNew; - GammaTblPtr pTableGammaCurrent = NULL; - CTabHandle hCTabDeviceColors; - Ptr csPtr; - OSErr err; - short dataBits, entries, channels = 3; // force three channels in the gamma table - - if (pRamp) // ensure pRamp is allocated - { - err= GetGammaTable (hGD, &pTableGammaCurrent); // get pointer to current table - if ((err == noErr) && pTableGammaCurrent) - { - dataBits = pTableGammaCurrent->gDataWidth; // table must have same data width - entries = pTableGammaCurrent->gDataCnt; // table must be same size - pTableGammaNew = (GammaTblPtr) CreateEmptyGammaTable (channels, entries, dataBits); // our new table - if (pTableGammaNew) // if successful fill table - { - unsigned char * pGammaBase = (unsigned char *)&pTableGammaNew->gFormulaData + pTableGammaNew->gFormulaSize; // base of table - if (entries == 256 && dataBits == 8) // simple case: direct mapping - BlockMove ((Ptr)pRamp, (Ptr)pGammaBase, channels * entries); // move everything - else // tough case handle entry, channel and data size disparities - { - short bytesPerEntry = (dataBits + 7) / 8; // size, in bytes, of the device table entries - short shiftRightValue = 8 - dataBits; // number of right shifts ramp -> device - short indexChan; - short indexEntry; - short indexByte; - - shiftRightValue += ((bytesPerEntry - 1) * 8); // multibyte entries and the need to map a byte at a time most sig. to least sig. - for ( indexChan = 0; indexChan < channels; indexChan++) // for all the channels - for ( indexEntry = 0; indexEntry < entries; indexEntry++) // for all the entries - { - short currentShift = shiftRightValue; // reset current bit shift - long temp = *((unsigned char *)pRamp + (indexChan << 8) + (indexEntry << 8) / entries); // get data from ramp - for ( indexByte = 0; indexByte < bytesPerEntry; indexByte++) // for all bytes - { - if (currentShift < 0) // shift data correctly for current byte - *(pGammaBase++) = temp << -currentShift; - else - *(pGammaBase++) = temp >> currentShift; - currentShift -= 8; // increment shift to align to next less sig. byte - } - } - } - - // set gamma - gameRecRestore.csGTable = (Ptr) pTableGammaNew; // setup restore record - csPtr = (Ptr) &gameRecRestore; - err = Control((**hGD).gdRefNum, cscSetGamma, (Ptr) &csPtr); // restore gamma - - if (((**(**hGD).gdPMap).pixelSize == 8) && (err == noErr)) // if successful and on an 8 bit device - { - hCTabDeviceColors = (**(**hGD).gdPMap).pmTable; // do SetEntries to force CLUT update - setEntriesRec.csTable = (ColorSpec *) &(**hCTabDeviceColors).ctTable; - setEntriesRec.csStart = 0; - setEntriesRec.csCount = (**hCTabDeviceColors).ctSize; - csPtr = (Ptr) &setEntriesRec; - err = Control((**hGD).gdRefNum, cscSetEntries, (Ptr) &csPtr); // SetEntries in CLUT - } - DisposeGammaTable ((Ptr) pTableGammaNew); // dump table - if (err == noErr) - return true; - } - } - } - else // set NULL gamma -> results in linear map - { - gameRecRestore.csGTable = (Ptr) NULL; // setup restore record - csPtr = (Ptr) &gameRecRestore; - err = Control((**hGD).gdRefNum, cscSetGamma, (Ptr) &csPtr); // restore gamma - - if (((**(**hGD).gdPMap).pixelSize == 8) && (err == noErr)) // if successful and on an 8 bit device - { - hCTabDeviceColors = (**(**hGD).gdPMap).pmTable; // do SetEntries to force CLUT update - setEntriesRec.csTable = (ColorSpec *) &(**hCTabDeviceColors).ctTable; - setEntriesRec.csStart = 0; - setEntriesRec.csCount = (**hCTabDeviceColors).ctSize; - csPtr = (Ptr) &setEntriesRec; - err = Control((**hGD).gdRefNum, cscSetEntries, (Ptr) &csPtr); // SetEntries in CLUT - } - if (err == noErr) - return true; - } - return false; // memory allocation or device control failed if we get here -} - -// -------------------------------------------------------------------------- - -// SetDeviceGammaRampGW - -// sets the gamma ramp for a graphics device associated with a GWorld pointer (pRamp: 3 arrays of 256 elements each (R,G,B)) - -Boolean SetDeviceGammaRampGW (GWorldPtr pGW, Ptr pRamp) -{ - GDHandle hGD = GetGWorldDevice (pGW); - return SetDeviceGammaRampGD (hGD, pRamp); -} - -// -------------------------------------------------------------------------- - -// SetDeviceGammaRampCGP - -// sets the gamma ramp for a graphics device associated with a CGraf pointer (pRamp: 3 arrays of 256 elements each (R,G,B)) - -Boolean SetDeviceGammaRampCGP (CGrafPtr pGraf, Ptr pRamp) -{ - CGrafPtr pGrafSave; - GDHandle hGDSave; - GDHandle hGD; - Boolean fResult; - - GetGWorld (&pGrafSave, &hGDSave); - SetGWorld (pGraf, NULL); - hGD = GetGDevice (); - fResult = SetDeviceGammaRampGD (hGD, pRamp); - SetGWorld (pGrafSave, hGDSave); - return fResult; -} \ No newline at end of file diff --git a/code/mac/MacGamma.cpp b/code/mac/MacGamma.cpp deleted file mode 100644 index a429950..0000000 --- a/code/mac/MacGamma.cpp +++ /dev/null @@ -1,474 +0,0 @@ -/* - File: MacGamma.cpp - - Contains: Functions to enable Mac OS device gamma adjustments using Windows common 3 channel 256 element 8 bit gamma ramps - - Written by: Geoff Stahl - - Copyright: Copyright © 1999 Apple Computer, Inc., All Rights Reserved - - Change History (most recent first): - - <4> 5/20/99 GGS Added handling for gamma tables with different data widths, - number of entries, and channels. Forced updates to 3 channels - (poss. could break on rare card, but very unlikely). Added - quick update with BlockMove for 3x256x8 tables. Updated function - names. - <3> 5/20/99 GGS Cleaned up and commented - <2> 5/20/99 GGS Added system wide get and restore gamma functions to enable - restoration of original for all devices. Modified functionality - to return pointers vice squirreling away the memory. - <1> 5/20/99 GGS Initial Add -*/ - - - -// system includes ---------------------------------------------------------- - -#include -#include -#include -#include -#include -#include - - - -// project includes --------------------------------------------------------- - -#include "MacGamma.h" - - - -// functions (external/public) ---------------------------------------------- - -// GetRawDeviceGamma - -// Returns the device gamma table pointer in ppDeviceTable - -OSErr GetGammaTable (GDHandle hGD, GammaTblPtr * ppTableGammaOut) -{ - VDGammaRecord DeviceGammaRec; - CntrlParam cParam; - OSErr err; - - cParam.ioCompletion = NULL; // set up control params - cParam.ioNamePtr = NULL; - cParam.ioVRefNum = 0; - cParam.ioCRefNum = (**hGD).gdRefNum; - cParam.csCode = cscGetGamma; // Get Gamma commnd to device - *(Ptr *)cParam.csParam = (Ptr) &DeviceGammaRec; // record for gamma - - err = PBStatus( (ParmBlkPtr)&cParam, 0 ); // get gamma - - *ppTableGammaOut = (GammaTblPtr)(DeviceGammaRec.csGTable); // pull table out of record - - return err; -} - -// -------------------------------------------------------------------------- - -// CreateEmptyGammaTable - -// creates an empty gamma table of a given size, assume no formula data will be used - -Ptr CreateEmptyGammaTable (short channels, short entries, short bits) -{ - GammaTblPtr pTableGammaOut = NULL; - short tableSize, dataWidth; - - dataWidth = (bits + 7) / 8; // number of bytes per entry - tableSize = sizeof (GammaTbl) + (channels * entries * dataWidth); - pTableGammaOut = (GammaTblPtr) NewPtrClear (tableSize); // allocate new tabel - - if (pTableGammaOut) // if we successfully allocated - { - pTableGammaOut->gVersion = 0; // set parameters based on input - pTableGammaOut->gType = 0; - pTableGammaOut->gFormulaSize = 0; - pTableGammaOut->gChanCnt = channels; - pTableGammaOut->gDataCnt = entries; - pTableGammaOut->gDataWidth = bits; - } - return (Ptr)pTableGammaOut; // return whatever we allocated -} - -// -------------------------------------------------------------------------- - -// CopyGammaTable - -// given a pointer toa device gamma table properly iterates and copies - -Ptr CopyGammaTable (GammaTblPtr pTableGammaIn) -{ - GammaTblPtr pTableGammaOut = NULL; - short tableSize, dataWidth; - - if (pTableGammaIn) // if there is a table to copy - { - dataWidth = (pTableGammaIn->gDataWidth + 7) / 8; // number of bytes per entry - tableSize = sizeof (GammaTbl) + pTableGammaIn->gFormulaSize + - (pTableGammaIn->gChanCnt * pTableGammaIn->gDataCnt * dataWidth); - pTableGammaOut = (GammaTblPtr) NewPtr (tableSize); // allocate new table - if (pTableGammaOut) - BlockMove( (Ptr)pTableGammaIn, (Ptr)pTableGammaOut, tableSize); // move everything - } - return (Ptr)pTableGammaOut; // return whatever we allocated, could be NULL -} - -// -------------------------------------------------------------------------- - -// DisposeGammaTable - -// disposes gamma table returned from GetGammaTable, GetDeviceGamma, or CopyGammaTable -// 5/20/99: (GGS) added - -void DisposeGammaTable (Ptr pGamma) -{ - if (pGamma) - DisposePtr((Ptr) pGamma); // get rid of it -} - -// -------------------------------------------------------------------------- - -// GetDeviceGamma - -// returns pointer to copy of orginal device gamma table in native format (allocates memory for gamma table, call DisposeDeviceGamma to delete) -// 5/20/99: (GGS) change spec to return the allocated pointer vice storing internally - -Ptr GetDeviceGamma (GDHandle hGD) -{ - GammaTblPtr pTableGammaDevice = NULL; - GammaTblPtr pTableGammaReturn = NULL; - OSErr err; - - err = GetGammaTable (hGD, &pTableGammaDevice); // get a pointer to the devices table - if ((err == noErr) && pTableGammaDevice) // if succesful - pTableGammaReturn = (GammaTblPtr) CopyGammaTable (pTableGammaDevice); // copy to global - - return (Ptr) pTableGammaReturn; -} - -// -------------------------------------------------------------------------- - -// RestoreDeviceGamma - -// sets device to saved table -// 5/20/99: (GGS) now does not delete table, avoids confusion - -void RestoreDeviceGamma (GDHandle hGD, Ptr pGammaTable) -{ - VDSetEntryRecord setEntriesRec; - VDGammaRecord gameRecRestore; - CTabHandle hCTabDeviceColors; - Ptr csPtr; - OSErr err = noErr; - - if (pGammaTable) // if we have a table to restore - { - gameRecRestore.csGTable = pGammaTable; // setup restore record - csPtr = (Ptr) &gameRecRestore; - err = Control((**hGD).gdRefNum, cscSetGamma, (Ptr) &csPtr); // restore gamma - - if ((err == noErr) && ((**(**hGD).gdPMap).pixelSize == 8)) // if successful and on an 8 bit device - { - hCTabDeviceColors = (**(**hGD).gdPMap).pmTable; // do SetEntries to force CLUT update - setEntriesRec.csTable = (ColorSpec *) &(**hCTabDeviceColors).ctTable; - setEntriesRec.csStart = 0; - setEntriesRec.csCount = (**hCTabDeviceColors).ctSize; - csPtr = (Ptr) &setEntriesRec; - - err = Control((**hGD).gdRefNum, cscSetEntries, (Ptr) &csPtr); // SetEntries in CLUT - } - } -} - -// -------------------------------------------------------------------------- - -// GetSystemGammas - -// returns a pointer to a set of all current device gammas in native format (returns NULL on failure, which means reseting gamma will not be possible) -// 5/20/99: (GGS) added - -Ptr GetSystemGammas (void) -{ - precSystemGamma pSysGammaOut; // return pointer to system device gamma info - short devCount = 0; // number of devices attached - Boolean fail = false; - - pSysGammaOut = (precSystemGamma) NewPtr (sizeof (recSystemGamma)); // allocate for structure - - GDHandle hGDevice = GetDeviceList (); // top of device list - do // iterate - { - devCount++; // count devices - hGDevice = GetNextDevice (hGDevice); // next device - } while (hGDevice); - - pSysGammaOut->devGamma = (precDeviceGamma *) NewPtr (sizeof (precDeviceGamma) * devCount); // allocate for array of pointers to device records - if (pSysGammaOut) - { - pSysGammaOut->numDevices = devCount; // stuff count - - devCount = 0; // reset iteration - hGDevice = GetDeviceList (); - do - { - pSysGammaOut->devGamma [devCount] = (precDeviceGamma) NewPtr (sizeof (recDeviceGamma)); // new device record - if (pSysGammaOut->devGamma [devCount]) // if we actually allocated memory - { - pSysGammaOut->devGamma [devCount]->hGD = hGDevice; // stuff handle - pSysGammaOut->devGamma [devCount]->pDeviceGamma = (GammaTblPtr)GetDeviceGamma (hGDevice); // copy gamma table - } - else // otherwise dump record on exit - fail = true; - devCount++; // next device - hGDevice = GetNextDevice (hGDevice); - } while (hGDevice); - } - if (!fail) // if we did not fail - return (Ptr) pSysGammaOut; // return pointer to structure - else - { - DisposeSystemGammas (&(Ptr)pSysGammaOut); // otherwise dump the current structures (dispose does error checking) - return NULL; // could not complete - } -} - -// -------------------------------------------------------------------------- - -// RestoreSystemGammas - -// restores all system devices to saved gamma setting -// 5/20/99: (GGS) added - -void RestoreSystemGammas (Ptr pSystemGammas) -{ - precSystemGamma pSysGammaIn = (precSystemGamma) pSystemGammas; - if (pSysGammaIn) - for (short i = 0; i < pSysGammaIn->numDevices; i++) // for all devices - RestoreDeviceGamma (pSysGammaIn->devGamma [i]->hGD, (Ptr) pSysGammaIn->devGamma [i]->pDeviceGamma); // restore gamma -} - -// -------------------------------------------------------------------------- - -// DisposeSystemGammas - -// iterates through and deletes stored gamma settings -// 5/20/99: (GGS) added - -void DisposeSystemGammas (Ptr* ppSystemGammas) -{ - precSystemGamma pSysGammaIn; - if (ppSystemGammas) - { - pSysGammaIn = (precSystemGamma) *ppSystemGammas; - if (pSysGammaIn) - { - for (short i = 0; i < pSysGammaIn->numDevices; i++) // for all devices - if (pSysGammaIn->devGamma [i]) // if pointer is valid - { - DisposeGammaTable ((Ptr) pSysGammaIn->devGamma [i]->pDeviceGamma); // dump gamma table - DisposePtr ((Ptr) pSysGammaIn->devGamma [i]); // dump device info - } - DisposePtr ((Ptr) pSysGammaIn->devGamma); // dump device pointer array - DisposePtr ((Ptr) pSysGammaIn); // dump system structure - *ppSystemGammas = NULL; - } - } -} - -// -------------------------------------------------------------------------- - -// GetDeviceGammaRampGD - -// retrieves the gamma ramp from a graphics device (pRamp: 3 arrays of 256 elements each) - -Boolean GetDeviceGammaRampGD (GDHandle hGD, Ptr pRamp) -{ - GammaTblPtr pTableGammaTemp = NULL; - long indexChan, indexEntry; - OSErr err; - - if (pRamp) // ensure pRamp is allocated - { - err = GetGammaTable (hGD, &pTableGammaTemp); // get a pointer to the current gamma - if ((err == noErr) && pTableGammaTemp) // if successful - { - // fill ramp - unsigned char * pEntry = (unsigned char *)&pTableGammaTemp->gFormulaData + pTableGammaTemp->gFormulaSize; // base of table - short bytesPerEntry = (pTableGammaTemp->gDataWidth + 7) / 8; // size, in bytes, of the device table entries - short shiftRightValue = pTableGammaTemp->gDataWidth - 8; // number of right shifts device -> ramp - short channels = pTableGammaTemp->gChanCnt; - short entries = pTableGammaTemp->gDataCnt; - if (channels == 3) // RGB format - { // note, this will create runs of entries if dest. is bigger (not linear interpolate) - for (indexChan = 0; indexChan < channels; indexChan++) - for (indexEntry = 0; indexEntry < 256; indexEntry++) - *((unsigned char *)pRamp + (indexChan << 8) + indexEntry) = - *(pEntry + (indexChan * entries * bytesPerEntry) + indexEntry * ((entries * bytesPerEntry) >> 8)) >> shiftRightValue; - } - else // single channel format - { - for (indexEntry = 0; indexEntry < 256; indexEntry++) // for all entries set vramp value - for (indexChan = 0; indexChan < channels; indexChan++) // repeat for all channels - *((unsigned char *)pRamp + (indexChan << 8) + indexEntry) = - *(pEntry + ((indexEntry * entries * bytesPerEntry) >> 8)) >> shiftRightValue; - } - return true; - } - } - return false; -} - -// -------------------------------------------------------------------------- - -// GetDeviceGammaRampGW - -// retrieves the gamma ramp from a graphics device associated with a GWorld pointer (pRamp: 3 arrays of 256 elements each) - -Boolean GetDeviceGammaRampGW (GWorldPtr pGW, Ptr pRamp) -{ - GDHandle hGD = GetGWorldDevice (pGW); - return GetDeviceGammaRampGD (hGD, pRamp); -} - -// -------------------------------------------------------------------------- - -// GetDeviceGammaRampCGP - -// retrieves the gamma ramp from a graphics device associated with a CGraf pointer (pRamp: 3 arrays of 256 elements each) - -Boolean GetDeviceGammaRampCGP (CGrafPtr pGraf, Ptr pRamp) -{ - CGrafPtr pGrafSave; - GDHandle hGDSave; - GetGWorld (&pGrafSave, &hGDSave); - SetGWorld (pGraf, NULL); - GDHandle hGD = GetGDevice (); - Boolean fResult = GetDeviceGammaRampGD (hGD, pRamp); - SetGWorld (pGrafSave, hGDSave); - return fResult; -} - -// -------------------------------------------------------------------------- - -// SetDeviceGammaRampGD - -// sets the gamma ramp for a graphics device (pRamp: 3 arrays of 256 elements each (R,G,B)) - -Boolean SetDeviceGammaRampGD (GDHandle hGD, Ptr pRamp) -{ - VDSetEntryRecord setEntriesRec; - VDGammaRecord gameRecRestore; - GammaTblPtr pTableGammaNew; - GammaTblPtr pTableGammaCurrent = NULL; - CTabHandle hCTabDeviceColors; - Ptr csPtr; - OSErr err; - short dataBits, entries, channels = 3; // force three channels in the gamma table - - if (pRamp) // ensure pRamp is allocated - { - err= GetGammaTable (hGD, &pTableGammaCurrent); // get pointer to current table - if ((err == noErr) && pTableGammaCurrent) - { - dataBits = pTableGammaCurrent->gDataWidth; // table must have same data width - entries = pTableGammaCurrent->gDataCnt; // table must be same size - pTableGammaNew = (GammaTblPtr) CreateEmptyGammaTable (channels, entries, dataBits); // our new table - if (pTableGammaNew) // if successful fill table - { - unsigned char * pGammaBase = (unsigned char *)&pTableGammaNew->gFormulaData + pTableGammaNew->gFormulaSize; // base of table - if (entries == 256 && dataBits == 8) // simple case: direct mapping - BlockMove ((Ptr)pRamp, (Ptr)pGammaBase, channels * entries); // move everything - else // tough case handle entry, channel and data size disparities - { - short bytesPerEntry = (dataBits + 7) / 8; // size, in bytes, of the device table entries - short shiftRightValue = 8 - dataBits; // number of right shifts ramp -> device - shiftRightValue += ((bytesPerEntry - 1) * 8); // multibyte entries and the need to map a byte at a time most sig. to least sig. - for (short indexChan = 0; indexChan < channels; indexChan++) // for all the channels - for (short indexEntry = 0; indexEntry < entries; indexEntry++) // for all the entries - { - short currentShift = shiftRightValue; // reset current bit shift - long temp = *((unsigned char *)pRamp + (indexChan << 8) + (indexEntry << 8) / entries); // get data from ramp - for (short indexByte = 0; indexByte < bytesPerEntry; indexByte++) // for all bytes - { - if (currentShift < 0) // shift data correctly for current byte - *(pGammaBase++) = temp << -currentShift; - else - *(pGammaBase++) = temp >> currentShift; - currentShift -= 8; // increment shift to align to next less sig. byte - } - } - } - - // set gamma - gameRecRestore.csGTable = (Ptr) pTableGammaNew; // setup restore record - csPtr = (Ptr) &gameRecRestore; - err = Control((**hGD).gdRefNum, cscSetGamma, (Ptr) &csPtr); // restore gamma - - if (((**(**hGD).gdPMap).pixelSize == 8) && (err == noErr)) // if successful and on an 8 bit device - { - hCTabDeviceColors = (**(**hGD).gdPMap).pmTable; // do SetEntries to force CLUT update - setEntriesRec.csTable = (ColorSpec *) &(**hCTabDeviceColors).ctTable; - setEntriesRec.csStart = 0; - setEntriesRec.csCount = (**hCTabDeviceColors).ctSize; - csPtr = (Ptr) &setEntriesRec; - err = Control((**hGD).gdRefNum, cscSetEntries, (Ptr) &csPtr); // SetEntries in CLUT - } - DisposeGammaTable ((Ptr) pTableGammaNew); // dump table - if (err == noErr) - return true; - } - } - } - else // set NULL gamma -> results in linear map - { - gameRecRestore.csGTable = (Ptr) NULL; // setup restore record - csPtr = (Ptr) &gameRecRestore; - err = Control((**hGD).gdRefNum, cscSetGamma, (Ptr) &csPtr); // restore gamma - - if (((**(**hGD).gdPMap).pixelSize == 8) && (err == noErr)) // if successful and on an 8 bit device - { - hCTabDeviceColors = (**(**hGD).gdPMap).pmTable; // do SetEntries to force CLUT update - setEntriesRec.csTable = (ColorSpec *) &(**hCTabDeviceColors).ctTable; - setEntriesRec.csStart = 0; - setEntriesRec.csCount = (**hCTabDeviceColors).ctSize; - csPtr = (Ptr) &setEntriesRec; - err = Control((**hGD).gdRefNum, cscSetEntries, (Ptr) &csPtr); // SetEntries in CLUT - } - if (err == noErr) - return true; - } - return false; // memory allocation or device control failed if we get here -} - -// -------------------------------------------------------------------------- - -// SetDeviceGammaRampGW - -// sets the gamma ramp for a graphics device associated with a GWorld pointer (pRamp: 3 arrays of 256 elements each (R,G,B)) - -Boolean SetDeviceGammaRampGW (GWorldPtr pGW, Ptr pRamp) -{ - GDHandle hGD = GetGWorldDevice (pGW); - return SetDeviceGammaRampGD (hGD, pRamp); -} - -// -------------------------------------------------------------------------- - -// SetDeviceGammaRampCGP - -// sets the gamma ramp for a graphics device associated with a CGraf pointer (pRamp: 3 arrays of 256 elements each (R,G,B)) - -Boolean SetDeviceGammaRampCGP (CGrafPtr pGraf, Ptr pRamp) -{ - CGrafPtr pGrafSave; - GDHandle hGDSave; - GetGWorld (&pGrafSave, &hGDSave); - SetGWorld (pGraf, NULL); - GDHandle hGD = GetGDevice (); - Boolean fResult = SetDeviceGammaRampGD (hGD, pRamp); - SetGWorld (pGrafSave, hGDSave); - return fResult; -} \ No newline at end of file diff --git a/code/mac/MacGamma.h b/code/mac/MacGamma.h deleted file mode 100644 index efb145e..0000000 --- a/code/mac/MacGamma.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - File: MacGamma.h - - Contains: Functions to enable Mac OS device gamma adjustments using Windows common 3 channel 256 element 8 bit gamma ramps - - Written by: Geoff Stahl - - Copyright: Copyright © 1999 Apple Computer, Inc., All Rights Reserved - - Change History (most recent first): - - <4> 5/20/99 GGS Updated function names. - <3> 5/20/99 GGS Cleaned up and commented - <2> 5/20/99 GGS Added system wide get and restore gamma functions to enable - restoration of original for all devices. Modified functionality - to return pointers vice squirreling away the memory. - <1> 5/20/99 GGS Initial Add -*/ - - - -// include control -------------------------------------------------- - -#ifndef MacGamma_h -#define MacGamma_h - - - -// includes --------------------------------------------------------- - -#include -#include - - - -// structures/classes ----------------------------------------------- - -typedef struct // storage for device handle and gamma table -{ - GDHandle hGD; // handle to device - GammaTblPtr pDeviceGamma; // pointer to device gamma table -} recDeviceGamma; -typedef recDeviceGamma * precDeviceGamma; - -typedef struct // storage for system devices and gamma tables -{ - short numDevices; // number of devices - precDeviceGamma * devGamma; // array of pointers to device gamma records -} recSystemGamma; -typedef recSystemGamma * precSystemGamma; - - - -// function declarations -------------------------------------------- - -// 5/20/99: (GGS) changed functional specification -OSErr GetGammaTable(GDHandle gd, GammaTblPtr * ppTableGammaOut); // Returns the device gamma table pointer in ppDeviceTable -Ptr CreateEmptyGammaTable (short channels, short entries, short bits); // creates an empty gamma table of a given size, assume no formula data will be used -Ptr CopyGammaTable (GammaTblPtr pTableGammaIn); // given a pointer toa device gamma table properly iterates and copies -void DisposeGammaTable (Ptr pGamma); // disposes gamma table returned from GetGammaTable, GetDeviceGamma, or CopyGammaTable - -Ptr GetDeviceGamma (GDHandle hGD); // returns pointer to copy of orginal device gamma table in native format -void RestoreDeviceGamma (GDHandle hGD, Ptr pGammaTable); // sets device to saved table - -// 5/20/99: (GGS) added system wide gamma get and restore -Ptr GetSystemGammas (void); // returns a pointer to a set of all current device gammas in native format - // (returns NULL on failure, which means reseting gamma will not be possible) -void RestoreSystemGammas (Ptr pSystemGammas); // restores all system devices to saved gamma setting -void DisposeSystemGammas (Ptr* ppSystemGammas); // iterates through and deletes stored gamma settings - -Boolean GetDeviceGammaRampGD (GDHandle hGD, Ptr pRamp); // retrieves the gamma ramp from a graphics device (pRamp: 3 arrays of 256 elements each) -Boolean GetDeviceGammaRampGW (GWorldPtr pGW, Ptr pRamp); // retrieves the gamma ramp from a graphics device associated with a GWorld pointer (pRamp: 3 arrays of 256 elements each) -Boolean GetDeviceGammaRampCGP (CGrafPtr pGraf, Ptr pRamp); // retrieves the gamma ramp from a graphics device associated with a CGraf pointer (pRamp: 3 arrays of 256 elements each) - - -Boolean SetDeviceGammaRampGD (GDHandle hGD, Ptr pRamp); // sets the gamma ramp for a graphics device (pRamp: 3 arrays of 256 elements each (R,G,B)) -Boolean SetDeviceGammaRampGW (GWorldPtr pGW, Ptr pRamp); // sets the gamma ramp for a graphics device associated with a GWorld pointer (pRamp: 3 arrays of 256 elements each (R,G,B)) -Boolean SetDeviceGammaRampCGP (CGrafPtr pGraf, Ptr pRamp); // sets the gamma ramp for a graphics device associated with a CGraf pointer (pRamp: 3 arrays of 256 elements each (R,G,B)) - - - -#endif // MacGamma_h \ No newline at end of file diff --git a/code/mac/MacQuake3 b/code/mac/MacQuake3 deleted file mode 100644 index f6d71f8..0000000 Binary files a/code/mac/MacQuake3 and /dev/null differ diff --git a/code/mac/mac_console.c b/code/mac/mac_console.c deleted file mode 100644 index 839a4ef..0000000 --- a/code/mac/mac_console.c +++ /dev/null @@ -1,119 +0,0 @@ -#include "../client/client.h" -#include "mac_local.h" -#include -#include - -#define CONSOLE_MASK 1023 -static char consoleChars[CONSOLE_MASK+1]; -static int consoleHead, consoleTail; -static qboolean consoleDisplayed; - -/* -================== -Sys_InitConsole -================== -*/ -void Sys_InitConsole( void ) { - SIOUXSettings.initializeTB = 0; - SIOUXSettings.standalone = 0; - SIOUXSettings.setupmenus = 0; - SIOUXSettings.autocloseonquit = 1; - SIOUXSettings.asktosaveonclose = 0; - SIOUXSettings.toppixel = 40; - SIOUXSettings.leftpixel = 10; - -// Sys_ShowConsole( 1, qfalse ); -} - -/* -================== -Sys_ShowConsole -================== -*/ -void Sys_ShowConsole( int level, qboolean quitOnClose ) { - - if ( level ) { - consoleDisplayed = qtrue; - printf( "\n" ); - } else { - // FIXME: I don't know how to hide this window... - consoleDisplayed = qfalse; - } -} - - -/* -================ -Sys_Print - -This is called for all console output, even if the game is running -full screen and the dedicated console window is hidden. -================ -*/ -void Sys_Print( const char *text ) { - if ( !consoleDisplayed ) { - return; - } - printf( "%s", text ); -} - - -/* -================== -Sys_ConsoleEvent -================== -*/ -qboolean Sys_ConsoleEvent( EventRecord *event ) { - qboolean flag; - - flag = SIOUXHandleOneEvent(event); - - // track keyboard events so we can do console input, - // because SIOUX doesn't offer a polled read as far - // as I can tell... - if ( flag && event->what == keyDown ) { - int myCharCode; - - myCharCode = BitAnd( event->message, charCodeMask ); - if ( myCharCode == 8 || myCharCode == 28 ) { - if ( consoleHead > consoleTail ) { - consoleHead--; - } - } else if ( myCharCode >= 32 || myCharCode == 13 ) { - consoleChars[ consoleHead & CONSOLE_MASK ] = myCharCode; - consoleHead++; - } - } - - return flag; -} - - -/* -================ -Sys_ConsoleInput - -Checks for a complete line of text typed in at the console. -Return NULL if a complete line is not ready. -================ -*/ -char *Sys_ConsoleInput( void ) { - static char string[1024]; - int i; - - if ( consoleTail == consoleHead ) { - return NULL; - } - - for ( i = 0 ; i + consoleTail < consoleHead ; i++ ) { - string[i] = consoleChars[ ( consoleTail + i ) & CONSOLE_MASK ]; - if ( string[i] == 13 ) { - consoleTail += i + 1; - string[i] = 0; - return string; - } - } - - return NULL; -} - diff --git a/code/mac/mac_event.c b/code/mac/mac_event.c deleted file mode 100644 index b9ecdf8..0000000 --- a/code/mac/mac_event.c +++ /dev/null @@ -1,357 +0,0 @@ -#include "../client/client.h" -#include "mac_local.h" - -void DoMenuCommand(long menuAndItem); -void DoDrag(WindowPtr myWindow,Point mouseloc); -void DoGoAwayBox(WindowPtr myWindow, Point mouseloc); -void DoCloseWindow(WindowPtr myWindow); -void DoKeyDown(EventRecord *event); -void DoDiskEvent(EventRecord *event); -void DoOSEvent(EventRecord *event); -void DoUpdate(WindowPtr myWindow); -void DoActivate(WindowPtr myWindow, int myModifiers); -void DoAboutBox(void); -void DoMenuCommand(long menuAndItem); -void DoMouseDown(EventRecord *event); -void DoMouseUp(EventRecord *event); -void DoMenuAdjust(void); -void DoKeyUp(EventRecord *event); - -/* -================ -Sys_MsecForMacEvent - -Q3 event records take time in msec, -so convert the mac event record when -(60ths) to msec. The base values -are updated ever frame, so this -is guaranteed to not drift. -================= -*/ -int Sys_MsecForMacEvent( void ) { - int tics; - - tics = sys_lastEventTic - sys_ticBase; - - return sys_msecBase + tics * 16; -} - - - - -void DoMouseDown(EventRecord *event) -{ - int myPart; - WindowPtr myWindow; - Point point; - - myPart = FindWindow(event->where, &myWindow); - - switch(myPart) - { - case inMenuBar: - DrawMenuBar(); - DoMenuCommand(MenuSelect(event->where)); - break; - case inSysWindow: - SystemClick(event, myWindow); - break; - case inDrag: - DoDrag(myWindow, event->where); - - // update the vid_xpos / vid_ypos cvars - point.h = 0; - point.v = 0; - LocalToGlobal( &point ); - Cvar_SetValue( "vid_xpos", point.h ); - Cvar_SetValue( "vid_ypos", point.v ); - return; - break; - case inGoAway: - DoGoAwayBox(myWindow, event->where); - break; - - case inContent: - if (myWindow != FrontWindow()) - { - SelectWindow(myWindow); - } - break; - } -} - -void DoMouseUp(EventRecord *event) -{ -} - -void DoDrag(WindowPtr myWindow, Point mouseloc) -{ - Rect dragBounds; - - dragBounds = (**GetGrayRgn()).rgnBBox; - DragWindow(myWindow,mouseloc,&dragBounds); - - aglUpdateContext(aglGetCurrentContext()); -} - - -void DoGoAwayBox(WindowPtr myWindow, Point mouseloc) -{ - if(TrackGoAway(myWindow,mouseloc)) - { - DoCloseWindow(myWindow); - } -} - -void DoCloseWindow(WindowPtr myWindow) -{ -} - -void DoMenuAdjust(void) -{ -} - -int vkeyToQuakeKey[256] = { -/*0x00*/ 'a', 's', 'd', 'f', 'h', 'g', 'z', 'x', -/*0x08*/ 'c', 'v', '?', 'b', 'q', 'w', 'e', 'r', -/*0x10*/ 'y', 't', '1', '2', '3', '4', '6', '5', -/*0x18*/ '=', '9', '7', '-', '8', '0', ']', 'o', -/*0x20*/ 'u', '[', 'i', 'p', K_ENTER, 'l', 'j', '\'', -/*0x28*/ 'k', ';', '\\', ',', '/', 'n', 'm', '.', -/*0x30*/ K_TAB, K_SPACE, '`', K_BACKSPACE, '?', K_ESCAPE, '?', K_COMMAND, -/*0x38*/ K_SHIFT, K_CAPSLOCK, K_ALT, K_CTRL, '?', '?', '?', '?', -/*0x40*/ '?', K_KP_DEL, '?', K_KP_STAR, '?', K_KP_PLUS, '?', K_KP_NUMLOCK, -/*0x48*/ '?', '?', '?', K_KP_SLASH, K_KP_ENTER, '?', K_KP_MINUS, '?', -/*0x50*/ '?', K_KP_EQUALS, K_KP_INS, K_KP_END, K_KP_DOWNARROW, K_KP_PGDN, K_KP_LEFTARROW, K_KP_5, -/*0x58*/ K_KP_RIGHTARROW, K_KP_HOME, '?', K_KP_UPARROW, K_KP_PGUP, '?', '?', '?', -/*0x60*/ K_F5, K_F6, K_F7, K_F3, K_F8, K_F9, '?', K_F11, -/*0x68*/ '?', K_F13, '?', K_F14, '?', K_F10, '?', K_F12, -/*0x70*/ '?', K_F15, K_INS, K_HOME, K_PGUP, K_DEL, K_F4, K_END, -/*0x78*/ K_F2, K_PGDN, K_F1, K_LEFTARROW, K_RIGHTARROW, K_DOWNARROW, K_UPARROW, K_POWER -}; - -void DoKeyDown(EventRecord *event) -{ - int myCharCode; - int myKeyCode; - - myCharCode = BitAnd(event->message,charCodeMask); - myKeyCode = ( event->message & keyCodeMask ) >> 8; - - Sys_QueEvent( Sys_MsecForMacEvent(), SE_KEY, vkeyToQuakeKey[ myKeyCode ], 1, 0, NULL ); - Sys_QueEvent( Sys_MsecForMacEvent(), SE_CHAR, myCharCode, 0, 0, NULL ); -} - -void DoKeyUp(EventRecord *event) -{ - int myCharCode; - int myKeyCode; - - myCharCode = BitAnd(event->message,charCodeMask); - myKeyCode = ( event->message & keyCodeMask ) >> 8; - - Sys_QueEvent( Sys_MsecForMacEvent(), SE_KEY, vkeyToQuakeKey[ myKeyCode ], 0, 0, NULL ); -} - -/* -================== -Sys_ModifierEvents -================== -*/ -static void Sys_ModifierEvents( int modifiers ) { - static int oldModifiers; - int changed; - int i; - - typedef struct { - int bit; - int keyCode; - } modifierKey_t; - - static modifierKey_t keys[] = { - { 128, K_MOUSE1 }, - { 256, K_COMMAND }, - { 512, K_SHIFT }, - {1024, K_CAPSLOCK }, - {2048, K_ALT }, - {4096, K_CTRL }, - {-1, -1 } - }; - - changed = modifiers ^ oldModifiers; - - for ( i = 0 ; keys[i].bit != -1 ; i++ ) { - // if we have input sprockets running, ignore mouse events we - // get from the debug passthrough driver - if ( inputActive && keys[i].keyCode == K_MOUSE1 ) { - continue; - } - - if ( changed & keys[i].bit ) { - Sys_QueEvent( Sys_MsecForMacEvent(), - SE_KEY, keys[i].keyCode, !!( modifiers & keys[i].bit ), 0, NULL ); - } - } - - oldModifiers = modifiers; -} - - -static void DoDiskEvent(EventRecord *event) -{ - -} - -static void DoOSEvent(EventRecord *event) -{ - -} - -static void DoUpdate(WindowPtr myWindow) -{ - GrafPtr origPort; - - GetPort(&origPort); - SetPort(myWindow); - - BeginUpdate(myWindow); - EndUpdate(myWindow); - - aglUpdateContext(aglGetCurrentContext()); - - SetPort(origPort); -} - -static void DoActivate( WindowPtr myWindow, int myModifiers) { - -} - -static void DoAboutBox( void ) { - DialogPtr myDialog; - short itemHit; - - myDialog = GetNewDialog(kAboutDialog, nil, (WindowPtr) -1); - ModalDialog(nil, &itemHit); - DisposeDialog(myDialog); -} - -static void DoMenuCommand( long menuAndItem ) { - int myMenuNum; - int myItemNum; - int myResult; - Str255 myDAName; - WindowPtr myWindow; - - myMenuNum = HiWord(menuAndItem); - myItemNum = LoWord(menuAndItem); - - GetPort(&myWindow); - - switch (myMenuNum) { - case mApple: - switch( myItemNum ) { - case iAbout: - DoAboutBox(); - break; - default: - GetMenuItemText(GetMenuHandle(mApple), myItemNum, myDAName); - myResult = OpenDeskAcc(myDAName); - break; - } - break; - case mFile: - switch (myItemNum) { - case iQuit: - Com_Quit_f(); - break; - } - break; - } - - HiliteMenu(0); -} - -void TestTime( EventRecord *ev ) { - int msec; - int tics; - static int startTics, startMsec; - - msec = Sys_Milliseconds(); - tics = ev->when; - - if ( !startTics || ev->what == mouseDown ) { - startTics = tics; - startMsec = msec; - } - - msec -= startMsec; - tics -= startTics; - - if ( !tics ) { - return; - } - Com_Printf( "%i msec to tic\n", msec / tics ); -} - -/* -================== -Sys_SendKeyEvents -================== -*/ -void Sys_SendKeyEvents (void) { - Boolean gotEvent; - EventRecord event; - - if ( !glConfig.isFullscreen || sys_waitNextEvent->value ) { - // this call involves 68k code and task switching. - // do it on the desktop, or if they explicitly ask for - // it when fullscreen - gotEvent = WaitNextEvent(everyEvent, &event, 0, nil); - } else { - gotEvent = GetOSEvent( everyEvent, &event ); - } - - // generate faked events from modifer changes - Sys_ModifierEvents( event.modifiers ); - - sys_lastEventTic = event.when; - - if ( !gotEvent ) { - return; - } - if ( Sys_ConsoleEvent(&event) ) { - return; - } - switch(event.what) - { - case mouseDown: - DoMouseDown(&event); - break; - case mouseUp: - DoMouseUp(&event); - break; - case keyDown: - DoKeyDown(&event); - break; - case keyUp: - DoKeyUp(&event); - break; - case autoKey: - DoKeyDown(&event); - break; - case updateEvt: - DoUpdate((WindowPtr) event.message); - break; - case diskEvt: - DoDiskEvent(&event); - break; - case activateEvt: - DoActivate((WindowPtr) event.message, event.modifiers); - break; - case osEvt: - DoOSEvent(&event); - break; - default: - break; - } -} diff --git a/code/mac/mac_glimp.c b/code/mac/mac_glimp.c deleted file mode 100644 index 2f46cc1..0000000 --- a/code/mac/mac_glimp.c +++ /dev/null @@ -1,829 +0,0 @@ - -typedef int sysEventType_t; // FIXME... -#include "../renderer/tr_local.h" -#include "mac_local.h" -#include -#include -#include "MacGamma.h" - -#define MAX_DEVICES 32 - -typedef struct { - GDHandle devices[MAX_DEVICES]; - int numDevices; - - Ptr systemGammas; - - GDHandle device; - - AGLContext context; - AGLDrawable drawable; - AGLPixelFormat fmt; - - GLint textureMemory; - GLint videoMemory; - - DSpContextReference DSpContext; -} macGlInfo; - - -cvar_t *r_device; -cvar_t *r_ext_transform_hint; -glHardwareType_t sys_hardwareType; -macGlInfo sys_gl; - -void GLimp_EndFrame( void ); -static void GLimp_Extensions( void ); - - -void CToPStr(char *cs, Str255 ps) -{ - GLint i, l; - - l = strlen(cs); - if(l > 255) l = 255; - ps[0] = l; - for(i = 0; i < l; i++) ps[i + 1] = cs[i]; -} - -/* -============ -CheckErrors -============ -*/ -void CheckErrors( void ) { - GLenum err; - - err = aglGetError(); - if( err != AGL_NO_ERROR ) { - ri.Error( ERR_FATAL, "aglGetError: %s", - aglErrorString( err ) ); - } -} - -//======================================================================= - -/* -===================== -GLimp_ResetDisplay -===================== -*/ -void GLimp_ResetDisplay( void ) { - if ( !glConfig.isFullscreen ) { - return; - } - glConfig.isFullscreen = qfalse; - - // put the context into the inactive state - DSpContext_SetState( sys_gl.DSpContext, kDSpContextState_Inactive ); - - // release the context - DSpContext_Release( sys_gl.DSpContext ); - - // shutdown draw sprockets - DSpShutdown(); -} - -/* -===================== -GLimp_ChangeDisplay -===================== -*/ -void GLimp_ChangeDisplay( int *actualWidth, int *actualHeight ) { - OSStatus theError; - DSpContextAttributes inAttributes; - int colorBits; - - // startup DrawSprocket - theError = DSpStartup(); - if( theError ) { - ri.Printf( PRINT_ALL, "DSpStartup() failed: %i\n", theError ); - *actualWidth = 640; - *actualHeight = 480; - return; - } - - if ( r_colorbits->integer == 24 || r_colorbits->integer == 32 ) { - colorBits = kDSpDepthMask_32; - } else { - colorBits = kDSpDepthMask_16; - } - - memset( &inAttributes, 0, sizeof( inAttributes ) ); - inAttributes.frequency = 0; - inAttributes.displayWidth = glConfig.vidWidth; - inAttributes.displayHeight = glConfig.vidHeight; - inAttributes.reserved1 = 0; - inAttributes.reserved2 = 0; - inAttributes.colorNeeds = kDSpColorNeeds_Require; - inAttributes.colorTable = NULL; - inAttributes.contextOptions = 0; - inAttributes.backBufferDepthMask = colorBits; - inAttributes.displayDepthMask = colorBits; - inAttributes.backBufferBestDepth = colorBits; - inAttributes.displayBestDepth = colorBits; - inAttributes.pageCount = 1; - inAttributes.gameMustConfirmSwitch = false; - inAttributes.reserved3[0] = 0; - inAttributes.reserved3[1] = 0; - inAttributes.reserved3[2] = 0; - inAttributes.reserved3[3] = 0; - - theError = DSpFindBestContext( &inAttributes, &sys_gl.DSpContext ); - - inAttributes.displayWidth = glConfig.vidWidth; - inAttributes.displayHeight = glConfig.vidHeight; - inAttributes.backBufferDepthMask = colorBits; - inAttributes.displayDepthMask = colorBits; - inAttributes.backBufferBestDepth = colorBits; - inAttributes.displayBestDepth = colorBits; - inAttributes.pageCount = 1; - - theError = DSpContext_Reserve( sys_gl.DSpContext, &inAttributes ); - - // find out what res we actually got - theError = DSpContext_GetAttributes( sys_gl.DSpContext, &inAttributes ); - - *actualWidth = inAttributes.displayWidth; - *actualHeight = inAttributes.displayHeight; - - // put the context into the active state - theError = DSpContext_SetState( sys_gl.DSpContext, kDSpContextState_Active ); - - // fade back in - theError = DSpContext_FadeGammaIn( NULL, NULL ); - - glConfig.isFullscreen = qtrue; -} - -//======================================================================= - - -/* -=================== -GLimp_AglDescribe_f - -=================== -*/ -void GLimp_AglDescribe_f( void ) { - long value; - long r,g,b,a; - long stencil, depth; - - ri.Printf( PRINT_ALL, "Selected pixel format 0x%x\n", (int)sys_gl.fmt ); - - ri.Printf( PRINT_ALL, "TEXTURE_MEMORY: %i\n", sys_gl.textureMemory ); - ri.Printf( PRINT_ALL, "VIDEO_MEMORY: %i\n", sys_gl.videoMemory ); - - aglDescribePixelFormat(sys_gl.fmt, AGL_RED_SIZE, &r); - aglDescribePixelFormat(sys_gl.fmt, AGL_GREEN_SIZE, &g); - aglDescribePixelFormat(sys_gl.fmt, AGL_BLUE_SIZE, &b); - aglDescribePixelFormat(sys_gl.fmt, AGL_ALPHA_SIZE, &a); - aglDescribePixelFormat(sys_gl.fmt, AGL_STENCIL_SIZE, &stencil); - aglDescribePixelFormat(sys_gl.fmt, AGL_DEPTH_SIZE, &depth); - ri.Printf( PRINT_ALL, "red:%i green:%i blue:%i alpha:%i depth:%i stencil:%i\n", - r, g, b, a, depth, stencil ); - - aglDescribePixelFormat(sys_gl.fmt, AGL_BUFFER_SIZE, &value); - ri.Printf( PRINT_ALL, "BUFFER_SIZE: %i\n", value ); - - aglDescribePixelFormat(sys_gl.fmt, AGL_PIXEL_SIZE, &value); - ri.Printf( PRINT_ALL, "PIXEL_SIZE: %i\n", value ); - - aglDescribePixelFormat(sys_gl.fmt, AGL_RENDERER_ID, &value); - ri.Printf( PRINT_ALL, "RENDERER_ID: %i\n", value ); - - // memory functions - value = glmGetInteger( GLM_PAGE_SIZE ); - ri.Printf( PRINT_ALL, "GLM_PAGE_SIZE: %i\n", value ); - - value = glmGetInteger( GLM_NUMBER_PAGES ); - ri.Printf( PRINT_ALL, "GLM_NUMBER_PAGES: %i\n", value ); - - value = glmGetInteger( GLM_CURRENT_MEMORY ); - ri.Printf( PRINT_ALL, "GLM_CURRENT_MEMORY: %i\n", value ); - - value = glmGetInteger( GLM_MAXIMUM_MEMORY ); - ri.Printf( PRINT_ALL, "GLM_MAXIMUM_MEMORY: %i\n", value ); - -} - -/* -=================== -GLimp_AglState_f - -=================== -*/ -void GLimp_AglState_f( void ) { - char *cmd; - int state, value; - - if ( ri.Cmd_Argc() != 3 ) { - ri.Printf( PRINT_ALL, "Usage: aglstate <0/1>\n" ); - return; - } - - cmd = ri.Cmd_Argv( 1 ); - if ( !Q_stricmp( cmd, "rasterization" ) ) { - state = AGL_RASTERIZATION; - } else { - ri.Printf( PRINT_ALL, "Unknown agl state: %s\n", cmd ); - return; - } - - cmd = ri.Cmd_Argv( 2 ); - value = atoi( cmd ); - - if ( value ) { - aglEnable( sys_gl.context, state ); - } else { - aglDisable( sys_gl.context, state ); - } -} - -/* -=================== -GLimp_Extensions - -=================== -*/ -static void GLimp_Extensions( void ) { - const char *extensions; - - // get our config strings - Q_strncpyz( glConfig.vendor_string, (const char *)qglGetString (GL_VENDOR), sizeof( glConfig.vendor_string ) ); - Q_strncpyz( glConfig.renderer_string, (const char *)qglGetString (GL_RENDERER), sizeof( glConfig.renderer_string ) ); - Q_strncpyz( glConfig.version_string, (const char *)qglGetString (GL_VERSION), sizeof( glConfig.version_string ) ); - Q_strncpyz( glConfig.extensions_string, (const char *)qglGetString (GL_EXTENSIONS), sizeof( glConfig.extensions_string ) ); - - extensions = glConfig.extensions_string; - - // GL_ARB_multitexture - qglMultiTexCoord2fARB = NULL; - qglActiveTextureARB = NULL; - qglClientActiveTextureARB = NULL; - - if ( strstr( extensions, "GL_ARB_multitexture" ) ) { - if ( r_ext_multitexture->integer && r_allowExtensions->integer ) { - qglMultiTexCoord2fARB = glMultiTexCoord2fARB; - qglActiveTextureARB = glActiveTextureARB; - qglClientActiveTextureARB = glClientActiveTextureARB; - - ri.Printf( PRINT_ALL, "...using GL_ARB_multitexture\n" ); - } else { - ri.Printf( PRINT_ALL, "...ignoring GL_ARB_multitexture\n" ); - } - } else { - ri.Printf( PRINT_ALL, "...GL_ARB_multitexture not found\n" ); - } - - // GL_EXT_compiled_vertex_array - qglLockArraysEXT = NULL; - qglUnlockArraysEXT = NULL; - - if ( strstr( extensions, "GL_EXT_compiled_vertex_array" ) ) { - if ( r_ext_compiled_vertex_array->integer && r_allowExtensions->integer ) { - qglLockArraysEXT = glLockArraysEXT; - qglUnlockArraysEXT = glUnlockArraysEXT; - - ri.Printf( PRINT_ALL, "...using GL_EXT_compiled_vertex_array\n" ); - } else { - ri.Printf( PRINT_ALL, "...ignoring GL_EXT_compiled_vertex_array\n" ); - } - } else { - ri.Printf( PRINT_ALL, "...GL_EXT_compiled_vertex_array not found\n" ); - } - - // GL_EXT_texture_env_add - glConfig.textureEnvAddAvailable = qfalse; - if ( strstr( glConfig.extensions_string, "EXT_texture_env_add" ) ) { - if ( r_ext_texture_env_add->integer ) { - glConfig.textureEnvAddAvailable = qtrue; - ri.Printf( PRINT_ALL, "...using GL_EXT_texture_env_add\n" ); - } else { - glConfig.textureEnvAddAvailable = qfalse; - ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_env_add\n" ); - } - } else { - ri.Printf( PRINT_ALL, "...GL_EXT_texture_env_add not found\n" ); - } - - // GL_EXT_texture_filter_anisotropic - glConfig.textureFilterAnisotropicAvailable = qfalse; - if ( strstr( glConfig.extensions_string, "EXT_texture_filter_anisotropic" ) ) - { - glConfig.textureFilterAnisotropicAvailable = qtrue; - ri.Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic available\n" ); - - if ( r_ext_texture_filter_anisotropic->integer ) - { - ri.Printf( PRINT_ALL, "...using GL_EXT_texture_filter_anisotropic\n" ); - } - else - { - ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_filter_anisotropic\n" ); - } - ri.Cvar_Set( "r_ext_texture_filter_anisotropic_avail", "1" ); - } - else - { - ri.Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic not found\n" ); - ri.Cvar_Set( "r_ext_texture_filter_anisotropic_avail", "0" ); - } - - // apple transform hint - if ( strstr( extensions, "GL_APPLE_transform_hint" ) ) { - if ( r_ext_compiled_vertex_array->integer && r_allowExtensions->integer ) { - glHint( GL_TRANSFORM_HINT_APPLE, GL_FASTEST ); - ri.Printf( PRINT_ALL, "...using GL_APPLE_transform_hint\n" ); - } else { - ri.Printf( PRINT_ALL, "...ignoring GL_APPLE_transform_hint\n" ); - } - } else { - ri.Printf( PRINT_ALL, "...GL_APPLE_transform_hint not found\n" ); - } -} - -/* -============================ -GLimp_SufficientVideoMemory -============================ -*/ -#if 0 - -qboolean GLimp_SufficientVideoMemory( void ) { - AGLRendererInfo head_info, info; - GLint accelerated; - AGLDevice *device; - GLint i, ndevs; - - device = aglDevicesOfPixelFormat( sys_gl.fmt, &ndevs); - if (!device || ndevs < 1) { - ri.Printf( PRINT_ALL, "aglDevicesOfPixelFormat failed.\n" ); - return 0; - } - - ri.Printf( PRINT_ALL, "%i rendering devices\n", ndevs ); - - head_info = aglQueryRendererInfo( device, 1 ); - info = head_info; - if (!info) { - ri.Printf( PRINT_ALL, "aglQueryRendererInfo failed.\n" ); - return 0; - } - - for ( i = 0 ; i < ndevs ; i++ ) { - // ignore the software renderer listing - aglDescribeRenderer( info, AGL_ACCELERATED, &accelerated ); - if ( accelerated ) { - aglDescribeRenderer( info, AGL_TEXTURE_MEMORY, &sys_gl.textureMemory ); - aglDescribeRenderer( info, AGL_VIDEO_MEMORY, &sys_gl.videoMemory ); - } - info = aglNextRendererInfo(info); - } - - aglDestroyRendererInfo(head_info); -#if 0 - if ( sys_gl.videoMemory < 16000000 ) { - glConfig.hardwareType = GLHW_RAGEPRO; // FIXME when voodoo is available - } else { - glConfig.isRagePro = GLHW_GENERIC; // FIXME when voodoo is available - } -#endif - ri.Printf( PRINT_ALL, "%i texture memory, %i video memory\n", sys_gl.textureMemory, sys_gl.videoMemory ); - - return qtrue; -} - -#endif - -/* -======================= -CheckDeviceRenderers - -======================== -*/ -static void CheckDeviceRenderers( GDHandle device ) { - AGLRendererInfo info, head_info; - GLint inum; - GLint accelerated; - GLint textureMemory, videoMemory; - - head_info = aglQueryRendererInfo(&device, 1); - if( !head_info ) { - ri.Printf( PRINT_ALL, "aglQueryRendererInfo : Info Error\n"); - return; - } - - info = head_info; - inum = 0; - while( info ) { - ri.Printf( PRINT_ALL, " Renderer : %d\n", inum); - - aglDescribeRenderer( info, AGL_ACCELERATED, &accelerated ); - - if ( accelerated ) { - aglDescribeRenderer( info, AGL_TEXTURE_MEMORY, &textureMemory ); - aglDescribeRenderer( info, AGL_VIDEO_MEMORY, &videoMemory ); - ri.Printf( PRINT_ALL, " AGL_VIDEO_MEMORY: %i\n", textureMemory ); - ri.Printf( PRINT_ALL, " AGL_TEXTURE_MEMORY: %i\n", videoMemory ); - - // save the device with the most texture memory - if ( sys_gl.textureMemory < textureMemory ) { - sys_gl.textureMemory = textureMemory; - sys_gl.device = device; - } - } else { - ri.Printf( PRINT_ALL, " Not accelerated.\n" ); - } - - info = aglNextRendererInfo(info); - inum++; - } - - aglDestroyRendererInfo(head_info); -} - - -/* -======================= -CheckDevices - -Make sure there is a device with enough video memory to play -======================= -*/ -static void CheckDevices( void ) { - GDHandle device; - static qboolean checkedFullscreen; - - if ( checkedFullscreen ) { - return; - } - if ( glConfig.isFullscreen ) { - checkedFullscreen = qtrue; - } - - device = GetDeviceList(); - sys_gl.numDevices = 0; - while( device && sys_gl.numDevices < MAX_DEVICES ) { - sys_gl.devices[ sys_gl.numDevices ] = device; - - ri.Printf( PRINT_ALL, "Device : %d\n", sys_gl.numDevices); - CheckDeviceRenderers(device); - - device = GetNextDevice(device); - - sys_gl.numDevices++; - } - - CheckErrors(); - - if ( sys_gl.textureMemory < 4000000 ) { - ri.Error( ERR_FATAL, "You must have at least four megs of video memory to play" ); - } - - if ( sys_gl.textureMemory < 16000000 ) { - sys_hardwareType = GLHW_RAGEPRO; // this will have to change with voodoo - } else { - sys_hardwareType = GLHW_GENERIC; - } -} - -/* -================= -CreateGameWindow -================= -*/ -static qboolean CreateGameWindow( void ) { - cvar_t *vid_xpos; - cvar_t *vid_ypos; - int mode; - int x, y; - Str255 pstr; - - - vid_xpos = ri.Cvar_Get( "vid_xpos", "30", 0 ); - vid_ypos = ri.Cvar_Get( "vid_ypos", "30", 0 ); - - // get mode info - mode = r_mode->integer; - ri.Printf( PRINT_ALL, "...setting mode %d:", mode ); - - if ( !R_GetModeInfo( &glConfig.vidWidth, &glConfig.vidHeight, &glConfig.windowAspect, mode ) ) { - ri.Printf( PRINT_ALL, " invalid mode\n" ); - return false; - } - ri.Printf( PRINT_ALL, " %d %d\n", glConfig.vidWidth, glConfig.vidHeight ); - - /* Create window */ - if ( r_fullscreen->integer ) { - int actualWidth, actualHeight; - - // change display resolution - GLimp_ChangeDisplay( &actualWidth, &actualHeight ); - - x = ( actualWidth - glConfig.vidWidth ) / 2; - y = ( actualHeight - glConfig.vidHeight ) / 2; - sys_gl.drawable = (AGLDrawable) GetNewCWindow(kFullScreenWindow,nil,(WindowPtr)-1L); - } else { - x = vid_xpos->integer; - y = vid_ypos->integer; - sys_gl.drawable = (AGLDrawable) GetNewCWindow(kMainWindow,nil,(WindowPtr)-1L); - } - if( !sys_gl.drawable ) { - return qfalse; - } - - SizeWindow((GrafPort *) sys_gl.drawable, glConfig.vidWidth, glConfig.vidHeight,GL_FALSE); - MoveWindow((GrafPort *) sys_gl.drawable,x, y, GL_FALSE); - ShowWindow((GrafPort *) sys_gl.drawable); - SetPort((GrafPort *) sys_gl.drawable); - CToPStr("Quake3: Arena", pstr); - SetWTitle((GrafPort *) sys_gl.drawable, pstr); - HiliteWindow((GrafPort *) sys_gl.drawable, 1); - - return qtrue; -} - - -/* -=================== -GLimp_SetMode - -Returns false if the mode / fullscrenn / options combination failed, -so another fallback can be tried -=================== -*/ -qboolean GLimp_SetMode( void ) { - GLint value; - GLint attrib[64]; - int i; - - if ( !CreateGameWindow() ) { - ri.Printf( PRINT_ALL, "GLimp_Init: window could not be created" ); - return qfalse; - } - - // check devices now that the game has set the display mode, - // because RAVE devices don't get reported if in an 8 bit desktop - CheckDevices(); - - // set up the attribute list - i = 0; - attrib[i++] = AGL_RGBA; - attrib[i++] = AGL_DOUBLEBUFFER; - attrib[i++] = AGL_NO_RECOVERY; - attrib[i++] = AGL_ACCELERATED; - - if ( r_colorbits->integer >= 16 ) { - attrib[i++] = AGL_RED_SIZE; - attrib[i++] = 8; - attrib[i++] = AGL_GREEN_SIZE; - attrib[i++] = 8; - attrib[i++] = AGL_BLUE_SIZE; - attrib[i++] = 8; - attrib[i++] = AGL_ALPHA_SIZE; - attrib[i++] = 0; - } else { - attrib[i++] = AGL_RED_SIZE; - attrib[i++] = 5; - attrib[i++] = AGL_GREEN_SIZE; - attrib[i++] = 5; - attrib[i++] = AGL_BLUE_SIZE; - attrib[i++] = 5; - attrib[i++] = AGL_ALPHA_SIZE; - attrib[i++] = 0; - } - - attrib[i++] = AGL_STENCIL_SIZE; - if ( r_stencilbits->integer ) { - attrib[i++] = r_stencilbits->integer; - } else { - attrib[i++] = 0; - } - - attrib[i++] = AGL_DEPTH_SIZE; - if ( r_depthbits->integer ) { - attrib[i++] = r_depthbits->integer; - } else { - attrib[i++] = 16; - } - - attrib[i++] = 0; - - /* Choose pixel format */ - ri.Printf( PRINT_ALL, "aglChoosePixelFormat\n" ); - if ( r_device->integer < 0 || r_device->integer >= sys_gl.numDevices ) { - ri.Cvar_Set( "r_device", "0" ); - } - sys_gl.fmt = aglChoosePixelFormat( &sys_gl.devices[ r_device->integer ], 1, attrib); - if(!sys_gl.fmt) { - ri.Printf( PRINT_ALL, "GLimp_Init: Pixel format could not be achieved\n"); - return qfalse; - } - ri.Printf( PRINT_ALL, "Selected pixel format 0x%x\n", (int)sys_gl.fmt ); - - aglDescribePixelFormat(sys_gl.fmt, AGL_RED_SIZE, &value); - glConfig.colorBits = value * 3; - aglDescribePixelFormat(sys_gl.fmt, AGL_STENCIL_SIZE, &value); - glConfig.stencilBits = value; - aglDescribePixelFormat(sys_gl.fmt, AGL_DEPTH_SIZE, &value); - glConfig.depthBits = value; - - CheckErrors(); - - /* Create context */ - sys_gl.context = aglCreateContext(sys_gl.fmt, NULL); - if(!sys_gl.context) { - ri.Printf( PRINT_ALL, "GLimp_init: Context could not be created\n"); - return qfalse; - } - - CheckErrors(); - - /* Make context current */ - - if(!aglSetDrawable(sys_gl.context, sys_gl.drawable)) { - ri.Printf( PRINT_ALL, "GLimp_Init: Could not attach to context\n" ); - return qfalse; - } - - CheckErrors(); - - if( !aglSetCurrentContext(sys_gl.context) ) { - ri.Printf( PRINT_ALL, "GLimp_Init: Could not attach to context"); - return qfalse; - } - - CheckErrors(); - - // check video memory and determine ragePro status -#if 0 - if ( !GLimp_SufficientVideoMemory() ) { - return qfalse; - } -#endif - glConfig.hardwareType = sys_hardwareType; // FIXME: this isn't really right - - // draw something to show that GL is alive - qglClearColor( 1, 0.5, 0.2, 0 ); - qglClear( GL_COLOR_BUFFER_BIT ); - GLimp_EndFrame(); - - CheckErrors(); - - // get the extensions - GLimp_Extensions(); - - CheckErrors(); - - return qtrue; -} - - - -/* -=================== -GLimp_Init - -Don't return unless OpenGL has been properly initialized -=================== -*/ -void GLimp_Init( void ) { - GLint major, minor; - static qboolean registered; - - ri.Printf( PRINT_ALL, "--- GLimp_Init ---\n" ); - - aglGetVersion( &major, &minor ); - ri.Printf( PRINT_ALL, "aglVersion: %i.%i\n", (int)major, (int)minor ); - - r_device = ri.Cvar_Get( "r_device", "0", CVAR_LATCH | CVAR_ARCHIVE ); - r_ext_transform_hint = ri.Cvar_Get( "r_ext_transform_hint", "1", CVAR_LATCH | CVAR_ARCHIVE ); - - if ( !registered ) { - ri.Cmd_AddCommand( "aglDescribe", GLimp_AglDescribe_f ); - ri.Cmd_AddCommand( "aglState", GLimp_AglState_f ); - } - - memset( &glConfig, 0, sizeof( glConfig ) ); - - - r_swapInterval->modified = qtrue; // force a set next frame - - - glConfig.deviceSupportsGamma = qtrue; - - // FIXME: try for a voodoo first - sys_gl.systemGammas = GetSystemGammas(); - - if ( GLimp_SetMode() ) { - ri.Printf( PRINT_ALL, "------------------\n" ); - return; - } - - // fall back to the known-good mode - ri.Cvar_Set( "r_fullscreen", "1" ); - ri.Cvar_Set( "r_mode", "3" ); - ri.Cvar_Set( "r_stereo", "0" ); - ri.Cvar_Set( "r_depthBits", "16" ); - ri.Cvar_Set( "r_colorBits", "16" ); - ri.Cvar_Set( "r_stencilBits", "0" ); - if ( GLimp_SetMode() ) { - ri.Printf( PRINT_ALL, "------------------\n" ); - return; - } - - ri.Error( ERR_FATAL, "Could not initialize OpenGL" ); -} - - -/* -=============== -GLimp_EndFrame - -=============== -*/ -void GLimp_EndFrame( void ) { - // check for variable changes - if ( r_swapInterval->modified ) { - r_swapInterval->modified = qfalse; - ri.Printf( PRINT_ALL, "Changing AGL_SWAP_INTERVAL\n" ); - aglSetInteger( sys_gl.context, AGL_SWAP_INTERVAL, (long *)&r_swapInterval->integer ); - } - - // make sure the event loop is pumped - Sys_SendKeyEvents(); - - aglSwapBuffers( sys_gl.context ); -} - -/* -=============== -GLimp_Shutdown - -=============== -*/ -void GLimp_Shutdown( void ) { - if ( sys_gl.systemGammas ) { - RestoreSystemGammas( sys_gl.systemGammas ); - DisposeSystemGammas( &sys_gl.systemGammas ); - sys_gl.systemGammas = 0; - } - - if ( sys_gl.context ) { - aglDestroyContext(sys_gl.context); - sys_gl.context = 0; - } - if ( sys_gl.fmt ) { - aglDestroyPixelFormat(sys_gl.fmt); - sys_gl.fmt = 0; - } - if ( sys_gl.drawable ) { - DisposeWindow((GrafPort *) sys_gl.drawable); - sys_gl.drawable = 0; - } - GLimp_ResetDisplay(); - - memset( &glConfig, 0, sizeof( glConfig ) ); -} - - -void GLimp_LogComment( char *comment ) { -} -qboolean GLimp_SpawnRenderThread( void (*function)( void ) ) { -} -void *GLimp_RendererSleep( void ) { - -} - -void GLimp_FrontEndSleep( void ) { - -} - -void GLimp_WakeRenderer( void * data ) { - -} - - - -/* -=============== -GLimp_SetGamma - -=============== -*/ -void GLimp_SetGamma( unsigned char red[256], - unsigned char green[256], - unsigned char blue[256] ) { - char color[3][256]; - int i; - - for ( i = 0 ; i < 256 ; i++ ) { - color[0][i] = red[i]; - color[1][i] = green[i]; - color[2][i] = blue[i]; - } - SetDeviceGammaRampGD( sys_gl.device, color[0] ); -} - diff --git a/code/mac/mac_input.c b/code/mac/mac_input.c deleted file mode 100644 index b9e3375..0000000 --- a/code/mac/mac_input.c +++ /dev/null @@ -1,212 +0,0 @@ - -#include "../client/client.h" -#include "mac_local.h" -#include "InputSprocket.h" - -qboolean inputActive; -qboolean inputSuspended; - -#define MAX_DEVICES 100 -ISpDeviceReference devices[MAX_DEVICES]; -ISpElementListReference elementList; - -#define MAX_ELEMENTS 512 -#define MAX_MOUSE_DEVICES 2 -UInt32 numDevices; -UInt32 numElements[MAX_MOUSE_DEVICES]; -ISpElementReference elements[MAX_MOUSE_DEVICES][MAX_ELEMENTS]; - -cvar_t *in_nomouse; - -void Input_Init(void); -void Input_GetState( void ); - -/* -================= -Sys_InitInput -================= -*/ -void Sys_InitInput( void ) { - NumVersion ver; - ISpElementInfo info; - int i, j; - OSStatus err; - - // no input with dedicated servers - if ( com_dedicated->integer ) { - return; - } - - Com_Printf( "------- Input Initialization -------\n" ); - in_nomouse = Cvar_Get( "in_nomouse", "0", 0 ); - if ( in_nomouse->integer != 0 ) { - Com_Printf( "in_nomouse is set, skipping.\n" ); - Com_Printf( "------------------------------------\n" ); - return; - } - - ver = ISpGetVersion(); - Com_Printf( "InputSprocket version: 0x%x\n", ver ); - - err = ISpStartup(); - if ( err ) { - Com_Printf( "ISpStartup failed: %i\n", err ); - Com_Printf( "------------------------------------\n" ); - return; - } - - // disable everything - ISpDevices_Extract( MAX_DEVICES, &numDevices, devices ); - Com_Printf("%i total devices\n", numDevices); - if (numDevices > MAX_DEVICES) { - numDevices = MAX_DEVICES; - } - err = ISpDevices_Deactivate( - numDevices, - devices); - - // enable mouse - err = ISpDevices_ExtractByClass( - kISpDeviceClass_Mouse, - MAX_DEVICES, - &numDevices, - devices); - Com_Printf("%i mouse devices\n", numDevices); - if (numDevices > MAX_MOUSE_DEVICES) { - numDevices = MAX_MOUSE_DEVICES; - } - - err = ISpDevices_Activate( numDevices, devices); - for ( i = 0 ; i < numDevices ; i++ ) { - ISpDevice_GetElementList( devices[i], &elementList ); - -// ISpGetGlobalElementList( &elementList ); - - // go through all the elements and asign them Quake key codes - ISpElementList_Extract( elementList, MAX_ELEMENTS, &numElements[i], elements[i] ); - Com_Printf("%i elements in list\n", numElements[i] ); - - for ( j = 0 ; j < numElements[i] ; j++ ) { - ISpElement_GetInfo( elements[i][j], &info ); - PStringToCString( (char *)info.theString ); - Com_Printf( "%i : %s\n", i, info.theString ); - } - } - - inputActive = true; - - HideCursor(); - - Com_Printf( "------------------------------------\n" ); -} - -/* -================= -Sys_ShutdownInput -================= -*/ -void Sys_ShutdownInput( void ) { - if ( !inputActive ) { - return; - } - ShowCursor(); - ISpShutdown(); - inputActive = qfalse; -} - -void Sys_SuspendInput( void ) { - if ( inputSuspended ) { - return; - } - inputSuspended = true; - ShowCursor(); - ISpSuspend(); -} - -void Sys_ResumeInput( void ) { - if ( !inputSuspended ) { - return; - } - inputSuspended = false; - HideCursor(); - ISpResume(); -} - -/* -================= -Sys_Input -================= -*/ -void Sys_Input( void ) { - ISpElementEvent event; - Boolean wasEvent; - UInt32 state, state2; - int xmove, ymove; - int button; - static int xtotal, ytotal; - int device; - - if ( !inputActive ) { - return; - } - - // during debugging it is sometimes usefull to be able to kill mouse support - if ( in_nomouse->integer ) { - Com_Printf( "Shutting down input.\n"); - Sys_ShutdownInput(); - return; - } - - // always suspend for dedicated - if ( com_dedicated->integer ) { - Sys_SuspendInput(); - return; - } - - // temporarily deactivate if not in the game and - if ( cls.keyCatchers || cls.state != CA_ACTIVE ) { - if ( !glConfig.isFullscreen ) { - Sys_SuspendInput(); - return; - } - } - - Sys_ResumeInput(); - - // send all button events - for ( device = 0 ; device < numDevices ; device++ ) { - // mouse buttons - - for ( button = 2 ; button < numElements[device] ; button++ ) { - while ( 1 ) { - ISpElement_GetNextEvent( elements[device][button], sizeof( event ), &event, &wasEvent ); - if ( !wasEvent ) { - break; - } - if ( event.data ) { - Sys_QueEvent( 0, SE_KEY, K_MOUSE1 + button - 2, 1, 0, NULL ); - } else { - Sys_QueEvent( 0, SE_KEY, K_MOUSE1 + button - 2, 0, 0, NULL ); - } - } - } - - // mouse movement - -#define MAC_MOUSE_SCALE 163 // why this constant? - // send mouse event - ISpElement_GetSimpleState( elements[device][0], &state ); - xmove = (int)state / MAC_MOUSE_SCALE; - - ISpElement_GetSimpleState( elements[device][1], &state2 ); - ymove = (int)state2 / -MAC_MOUSE_SCALE; - - if ( xmove || ymove ) { - xtotal += xmove; - ytotal += ymove; - //Com_Printf("%i %i = %i %i\n", state, state2, xtotal, ytotal ); - Sys_QueEvent( 0, SE_MOUSE, xmove, ymove, 0, NULL ); - } - } - -} diff --git a/code/mac/mac_local.h b/code/mac/mac_local.h deleted file mode 100644 index da86397..0000000 --- a/code/mac/mac_local.h +++ /dev/null @@ -1,321 +0,0 @@ - -/* - * Apple Universal Headers 3.1 - * - * Uncomment any additional #includes you want to add to your MacHeaders. - */ -//#include - - -// #include -// #include - #include - #include - #include - #include - #include -// #include - #include - #include - #include - #include - #include - #include - #include - #include -// #include -// #include - #include -// #include -// #include - #include -// #include -// #include -// #include - #include -// #include -// #include -// #include - #include -// #include - #include -// #include -// #include - #include - #include -// #include -// #include - #include -// #include -// #include -// #include -// #include -// #include - #include -// #include -// #include - #include - #include -// #include -// #include - #include -// #include - #include - #include -// #include -// #include - #include -// #include -// #include -// #include -// #include -// #include -// #include - #include - #include - #include -// #include - #include -// #include -// #include - #include - #include - #include - #include - #include - #include -// #include -// #include -// #include - #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include - #include -// #include - #include -// #include - #include -// #include -// #include - #include -// #include -// #include -// #include - #include -// #include -// #include - #include -// #include - #include -// #include - #include - #include -// #include -// #include - #include // Start using MacMemory.h - #include -// #include - #include - #include -// #include -// #include -// #include - #include -// #include - #include - #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include - #include - #include - #include - #include -// #include - #include - #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include - #include -// #include - #include - #include - #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include - #include -// #include -// #include - #include - #include -// #include -// #include -// #include -// #include -// #include - #include -// #include -// #include -// #include -// #include - #include - #include -// #include - #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include - #include -// #include -// #include -// #include -// #include -// #include -// #include - #include - #include -// #include - #include // Start using TextUtils.h -// #include -// #include -// #include - #include - #include -// #include -// #include - #include - #include - #include - #include -// #include -// #include - #include -// #include - #include // Start using MacTypes.h -// #include -// #include - #include -// #include - #include // Start using MacWindows.h -// #include -// #include - - - - -#include -#include -#include -#include -#include -#include -#include -#include - -/* Menus: */ - #define rMenuBar 128 - /* Apple menu: */ - #define mApple 128 - #define iAbout 1 - /* File menu: */ - #define mFile 129 - #define iQuit 1 - /* Edit menu: */ - #define mEdit 130 - #define iUndo 1 - #define iCut 3 - #define iCopy 4 - #define iPaste 5 - #define iClear 6 -/* Windows: */ - #define kMainWindow 128 - #define kFullScreenWindow 129 -/* Dilogs: */ - #define kAboutDialog 128 - - -// mac_main.c -extern int sys_ticBase; -extern int sys_msecBase; -extern int sys_lastEventTic; - -extern cvar_t *sys_waitNextEvent; - -void Sys_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ); -int PStringToCString( char *s ); -int CStringToPString( char *s ); - -// mac_event.c -extern int vkeyToQuakeKey[256]; -void Sys_SendKeyEvents (void); - -// mac_net.c -void Sys_InitNetworking( void ); -void Sys_ShutdownNetworking( void ); - -// mac_input.c -void Sys_InitInput( void ); -void Sys_ShutdownInput( void ); -void Sys_Input( void ); - -extern qboolean inputActive; - -// mac_glimp.c -extern glconfig_t glConfig; - -// mac_console.c - -void Sys_InitConsole( void ); -void Sys_ShowConsole( int level, qboolean quitOnClose ); -void Sys_Print( const char *text ); -char *Sys_ConsoleInput( void ); -qboolean Sys_ConsoleEvent( EventRecord *event ); - - diff --git a/code/mac/mac_main.c b/code/mac/mac_main.c deleted file mode 100644 index a195b48..0000000 --- a/code/mac/mac_main.c +++ /dev/null @@ -1,693 +0,0 @@ -#include "../client/client.h" -#include "mac_local.h" -#include -#include - -/* - -TODO: - -about box -dir with no extension gives strange results -console input? -dedicated servers -icons -dynamic loading of server game -clipboard pasting - -quit from menu - -*/ - -int sys_ticBase; -int sys_msecBase; -int sys_lastEventTic; - -cvar_t *sys_profile; -cvar_t *sys_waitNextEvent; - -void putenv( char *buffer ) { - // the mac doesn't seem to have the concept of environment vars, so nop this -} - -//=========================================================================== - -void Sys_UnloadGame (void) { -} -void *Sys_GetGameAPI (void *parms) { - void *GetGameAPI (void *import); - // we are hard-linked in, so no need to load anything - return GetGameAPI (parms); -} - -void Sys_UnloadUI (void) { -} -void *Sys_GetUIAPI (void) { - void *GetUIAPI (void); - // we are hard-linked in, so no need to load anything - return GetUIAPI (); -} - -void Sys_UnloadBotLib( void ) { -} - -void *Sys_GetBotLibAPI (void *parms) { - return NULL; -} - -void *Sys_GetBotAIAPI (void *parms) { - return NULL; -} - -void dllEntry( int (*syscallptr)( int arg,... ) ); -int vmMain( int command, ... ); - -void *Sys_LoadDll( const char *name, int (**entryPoint)(int, ...), - int (*systemCalls)(int, ...) ) { - - dllEntry( systemCalls ); - - *entryPoint = vmMain; - - return (void *)1; -} - -void Sys_UnloadDll( void *dllHandle ) { -} - -//=========================================================================== - -char *Sys_GetClipboardData( void ) { // FIXME - return NULL; -} - -int Sys_GetProcessorId( void ) { - return CPUID_GENERIC; -} - -void Sys_Mkdir( const char *path ) { - char ospath[MAX_OSPATH]; - int err; - - Com_sprintf( ospath, sizeof(ospath), "%s:", path ); - - err = mkdir( ospath, 0777 ); -} - -char *Sys_Cwd( void ) { - static char dir[MAX_OSPATH]; - int l; - - getcwd( dir, sizeof( dir ) ); - dir[MAX_OSPATH-1] = 0; - - // strip off the last colon - l = strlen( dir ); - if ( l > 0 ) { - dir[ l - 1 ] = 0; - } - return dir; -} - -char *Sys_DefaultCDPath( void ) { - return ""; -} - -char *Sys_DefaultBasePath( void ) { - return Sys_Cwd(); -} - -/* - ================================================================================= - - FILE FINDING - - ================================================================================= -*/ - -int PStringToCString( char *s ) { - int l; - int i; - - l = ((unsigned char *)s)[0]; - for ( i = 0 ; i < l ; i++ ) { - s[i] = s[i+1]; - } - s[l] = 0; - return l; -} - - -int CStringToPString( char *s ) { - int l; - int i; - - l = strlen( s ); - for ( i = 0 ; i < l ; i++ ) { - s[l-i] = s[l-i-1]; - } - s[0] = l; - return l; -} - -#define MAX_FOUND_FILES 0x1000 - -char **Sys_ListFiles( const char *directory, const char *extension, int *numfiles, qboolean wantsubs ) { - int nfiles; - char **listCopy; - char pdirectory[MAX_OSPATH]; - char *list[MAX_FOUND_FILES]; - int findhandle; - int directoryFlag; - int i; - int extensionLength; - int VRefNum; - int DrDirId; - int index; - FSSpec fsspec; - - // get the volume and directory numbers - // there has to be a better way than this... - { - CInfoPBRec paramBlock; - - Q_strncpyz( pdirectory, directory, sizeof(pdirectory) ); - CStringToPString( pdirectory ); - FSMakeFSSpec( 0, 0, (unsigned char *)pdirectory, &fsspec ); - - VRefNum = fsspec.vRefNum; - - memset( ¶mBlock, 0, sizeof( paramBlock ) ); - paramBlock.hFileInfo.ioNamePtr = (unsigned char *)pdirectory; - PBGetCatInfoSync( ¶mBlock ); - - DrDirId = paramBlock.hFileInfo.ioDirID; - } - - if ( !extension) { - extension = ""; - } - extensionLength = strlen( extension ); - - if ( wantsubs || (extension[0] == '/' && extension[1] == 0) ) { - directoryFlag = 16; - } else { - directoryFlag = 0; - } - - nfiles = 0; - - for ( index = 1 ; ; index++ ) { - CInfoPBRec paramBlock; - char fileName[MAX_OSPATH]; - int length; - OSErr err; - - memset( ¶mBlock, 0, sizeof( paramBlock ) ); - paramBlock.hFileInfo.ioNamePtr = (unsigned char *)fileName; - paramBlock.hFileInfo.ioVRefNum = VRefNum; - paramBlock.hFileInfo.ioFDirIndex = index; - paramBlock.hFileInfo.ioDirID = DrDirId; - - err = PBGetCatInfoSync( ¶mBlock ); - - if ( err != noErr ) { - break; - } - - if ( directoryFlag ^ ( paramBlock.hFileInfo.ioFlAttrib & 16 ) ) { - continue; - } - - // convert filename to C string - length = PStringToCString( fileName ); - - // check the extension - if ( !directoryFlag ) { - if ( length < extensionLength ) { - continue; - } - if ( Q_stricmp( fileName + length - extensionLength, extension ) ) { - continue; - } - } - - // add this file - if ( nfiles == MAX_FOUND_FILES - 1 ) { - break; - } - list[ nfiles ] = CopyString( fileName ); - nfiles++; - } - - list[ nfiles ] = 0; - - - // return a copy of the list - *numfiles = nfiles; - - if ( !nfiles ) { - return NULL; - } - - listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) ); - for ( i = 0 ; i < nfiles ; i++ ) { - listCopy[i] = list[i]; - } - listCopy[i] = NULL; - - return listCopy; -} - -void Sys_FreeFileList( char **list ) { - int i; - - if ( !list ) { - return; - } - - for ( i = 0 ; list[i] ; i++ ) { - Z_Free( list[i] ); - } - - Z_Free( list ); -} - - -//=================================================================== - - -/* -================ -Sys_Init - -The cvar and file system has been setup, so configurations are loaded -================ -*/ -void Sys_Init(void) { - Sys_InitNetworking(); - Sys_InitInput(); -} - -/* -================= -Sys_Shutdown -================= -*/ -void Sys_Shutdown( void ) { - Sys_EndProfiling(); - Sys_ShutdownInput(); - Sys_ShutdownNetworking(); -} - - -/* -================= -Sys_BeginProfiling -================= -*/ -static qboolean sys_profiling; -void Sys_BeginProfiling( void ) { - if ( !sys_profile->integer ) { - return; - } - ProfilerInit(collectDetailed, bestTimeBase, 16384, 64); - sys_profiling = qtrue; -} - -/* -================= -Sys_EndProfiling -================= -*/ -void Sys_EndProfiling( void ) { - unsigned char pstring[1024]; - - if ( !sys_profiling ) { - return; - } - sys_profiling = qfalse; - - sprintf( (char *)pstring + 1, "%s:profile.txt", Cvar_VariableString( "fs_basepath" ) ); - pstring[0] = strlen( (char *)pstring + 1 ); - ProfilerDump( pstring ); - ProfilerTerm(); -} - -//================================================================================ - - -/* -================ -Sys_Milliseconds -================ -*/ -int Sys_Milliseconds (void) { -#if 0 - int c; - - c = clock(); // FIXME, make more accurate - - return c*1000/60; -#else - AbsoluteTime t; - Nanoseconds nano; - double doub; - -#define kTwoPower32 (4294967296.0) /* 2^32 */ - - t = UpTime(); - nano = AbsoluteToNanoseconds( t ); - doub = (((double) nano.hi) * kTwoPower32) + nano.lo; - - return doub * 0.000001; -#endif -} - -/* -================ -Sys_Error -================ -*/ -void Sys_Error( const char *error, ... ) { - va_list argptr; - char string[1024]; - char string2[1024]; - - Sys_Shutdown(); - - va_start (argptr,error); - vsprintf (string2+1,error,argptr); - va_end (argptr); - string2[0] = strlen( string2 + 1 ); - - strcpy( string+1, "Quake 3 Error:" ); - string[0] = strlen( string + 1 ); - - // set the dialog box strings - ParamText( (unsigned char *)string, (unsigned char *)string2, - (unsigned char *)string2, (unsigned char *)string2 ); - - // run a dialog - StopAlert( 128, NULL ); - - exit(0); -} - - -/* -================ -Sys_Quit -================ -*/ -void Sys_Quit( void ) { - Sys_Shutdown(); - exit (0); -} - - -//=================================================================== - -void Sys_BeginStreamedFile( fileHandle_t f, int readAhead ) { -} - -void Sys_EndStreamedFile( fileHandle_t f ) { -} - -int Sys_StreamedRead( void *buffer, int size, int count, fileHandle_t f ) { - return FS_Read( buffer, size, count, f ); -} - -void Sys_StreamSeek( fileHandle_t f, int offset, int origin ) { - FS_Seek( f, offset, origin ); -} - -//================================================================================= - - -/* -======================================================================== - -EVENT LOOP - -======================================================================== -*/ - -qboolean Sys_GetPacket ( netadr_t *net_from, msg_t *net_message ); - -#define MAX_QUED_EVENTS 256 -#define MASK_QUED_EVENTS ( MAX_QUED_EVENTS - 1 ) - -sysEvent_t eventQue[MAX_QUED_EVENTS]; -int eventHead, eventTail; -byte sys_packetReceived[MAX_MSGLEN]; - -/* -================ -Sys_QueEvent - -A time of 0 will get the current time -Ptr should either be null, or point to a block of data that can -be freed by the game later. -================ -*/ -void Sys_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ) { - sysEvent_t *ev; - - ev = &eventQue[ eventHead & MASK_QUED_EVENTS ]; - if ( eventHead - eventTail >= MAX_QUED_EVENTS ) { - Com_Printf("Sys_QueEvent: overflow\n"); - // we are discarding an event, but don't leak memory - if ( ev->evPtr ) { - free( ev->evPtr ); - } - eventTail++; - } - eventHead++; - - if ( time == 0 ) { - time = Sys_Milliseconds(); - } - - ev->evTime = time; - ev->evType = type; - ev->evValue = value; - ev->evValue2 = value2; - ev->evPtrLength = ptrLength; - ev->evPtr = ptr; -} - -/* -================= -Sys_PumpEvents -================= -*/ -void Sys_PumpEvents( void ) { - char *s; - msg_t netmsg; - netadr_t adr; - - // pump the message loop - Sys_SendKeyEvents(); - - // check for console commands - s = Sys_ConsoleInput(); - if ( s ) { - char *b; - int len; - - len = strlen( s ) + 1; - b = malloc( len ); - if ( !b ) { - Com_Error( ERR_FATAL, "malloc failed in Sys_PumpEvents" ); - } - strcpy( b, s ); - Sys_QueEvent( 0, SE_CONSOLE, 0, 0, len, b ); - } - - // check for other input devices - Sys_Input(); - - // check for network packets - MSG_Init( &netmsg, sys_packetReceived, sizeof( sys_packetReceived ) ); - if ( Sys_GetPacket ( &adr, &netmsg ) ) { - netadr_t *buf; - int len; - - // copy out to a seperate buffer for qeueing - len = sizeof( netadr_t ) + netmsg.cursize; - buf = malloc( len ); - if ( !buf ) { - Com_Error( ERR_FATAL, "malloc failed in Sys_PumpEvents" ); - } - *buf = adr; - memcpy( buf+1, netmsg.data, netmsg.cursize ); - Sys_QueEvent( 0, SE_PACKET, 0, 0, len, buf ); - } -} - -/* -================ -Sys_GetEvent - -================ -*/ -sysEvent_t Sys_GetEvent( void ) { - sysEvent_t ev; - - if ( eventHead == eventTail ) { - Sys_PumpEvents(); - } - // return if we have data - if ( eventHead > eventTail ) { - eventTail++; - return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ]; - } - - // create an empty event to return - memset( &ev, 0, sizeof( ev ) ); - ev.evTime = Sys_Milliseconds(); - - // track the mac event "when" to milliseconds rate - sys_ticBase = sys_lastEventTic; - sys_msecBase = ev.evTime; - - return ev; -} - - -/* -============= -InitMacStuff -============= -*/ -void InitMacStuff( void ) { - Handle menuBar; - char dir[MAX_OSPATH]; - - // init toolbox - MaxApplZone(); - MoreMasters(); - - InitGraf(&qd.thePort); - InitFonts(); - FlushEvents(everyEvent, 0); - SetEventMask( -1 ); - InitWindows(); - InitMenus(); - TEInit(); - InitDialogs(nil); - InitCursor(); - - // init menu - menuBar = GetNewMBar(rMenuBar); - if(!menuBar) { - Com_Error( ERR_FATAL, "MenuBar not found."); - } - - SetMenuBar(menuBar); - DisposeHandle(menuBar); - AppendResMenu(GetMenuHandle(mApple),'DRVR'); - DrawMenuBar(); - - Sys_InitConsole(); - - SetEventMask( -1 ); -} - -//================================================================================== - -/* -============= -ReadCommandLineParms - -Read startup options from a text file or dialog box -============= -*/ -char *ReadCommandLineParms( void ) { - FILE *f; - int len; - char *buf; - EventRecord event; - - // flush out all the events and see if shift is held down - // to bring up the args window - while ( WaitNextEvent(everyEvent, &event, 0, nil) ) { - } - if ( event.modifiers & 512 ) { - static char text[1024]; - int argc; - char **argv; - int i; - - argc = ccommand( &argv ); - text[0] = 0; - // concat all the args into a string - // quote each arg seperately, because metrowerks does - // its own quote combining from the dialog - for ( i = 1 ; i < argc ; i++ ) { - if ( argv[i][0] != '+' ) { - Q_strcat( text, sizeof(text), "\"" ); - } - Q_strcat( text, sizeof(text), argv[i] ); - if ( argv[i][0] != '+' ) { - Q_strcat( text, sizeof(text), "\"" ); - } - Q_strcat( text, sizeof(text), " " ); - } - return text; - } - - // otherwise check for a parms file - f = fopen( "MacQuake3Parms.txt", "r" ); - if ( !f ) { - return ""; - } - len = FS_filelength( f ); - buf = malloc( len + 1 ); - if ( !buf ) { - exit( 1 ); - } - buf[len] = 0; - fread( buf, len, 1, f ); - fclose( f ); - - return buf; -} - -/* -============= -main -============= -*/ -void main( void ) { - char *commandLine; - - InitMacStuff(); - - commandLine = ReadCommandLineParms( ); - - Com_Init ( commandLine ); - - sys_profile = Cvar_Get( "sys_profile", "0", 0 ); - sys_profile->modified = qfalse; - - sys_waitNextEvent = Cvar_Get( "sys_waitNextEvent", "0", 0 ); - - while( 1 ) { - // run the frame - Com_Frame(); - - if ( sys_profile->modified ) { - sys_profile->modified = qfalse; - if ( sys_profile->integer ) { - Com_Printf( "Beginning profile.\n" ); - Sys_BeginProfiling() ; - } else { - Com_Printf( "Ending profile.\n" ); - Sys_EndProfiling(); - } - } - } -} - diff --git a/code/mac/mac_net.c b/code/mac/mac_net.c deleted file mode 100644 index 798c878..0000000 --- a/code/mac/mac_net.c +++ /dev/null @@ -1,527 +0,0 @@ -#include "../client/client.h" -#include "mac_local.h" -#include -#include - -static qboolean gOTInited; -static EndpointRef endpoint = kOTInvalidEndpointRef; -static EndpointRef resolverEndpoint = kOTInvalidEndpointRef; - -#define MAX_IPS 16 -static int numIP; -static InetInterfaceInfo sys_inetInfo[MAX_IPS]; - -static TUDErr uderr; - -void RcvUDErr( void ) { - memset( &uderr, 0, sizeof( uderr ) ); - uderr.addr.maxlen = 0; - uderr.opt.maxlen = 0; - OTRcvUDErr( endpoint, &uderr ); -} - -void HandleOTError( int err, const char *func ) { - int r; - static int lastErr; - - if ( err != lastErr ) { - Com_Printf( "%s: error %i\n", func, err ); - } - - // if we don't call OTLook, things wedge - r = OTLook( endpoint ); - if ( err != lastErr ) { - Com_DPrintf( "%s: OTLook %i\n", func, r ); - } - - switch( r ) { - case T_UDERR: - RcvUDErr(); - if ( err != lastErr ) { - Com_DPrintf( "%s: OTRcvUDErr %i\n", func, uderr.error ); - } - break; - default: -// Com_Printf( "%s: Unknown OTLook error %i\n", func, r ); - break; - } - lastErr = err; // don't spew tons of messages -} - -/* -================= -NotifyProc -================= -*/ -pascal void NotifyProc(void* contextPtr, OTEventCode code, - OTResult result, void* cookie) { - switch( code ) { - case T_OPENCOMPLETE: - endpoint = cookie; - break; - case T_UDERR: - RcvUDErr(); - break; - } -} - - -/* -================= -GetFourByteOption -================= -*/ -static OTResult GetFourByteOption(EndpointRef ep, - OTXTILevel level, - OTXTIName name, - UInt32 *value) -{ - OTResult err; - TOption option; - TOptMgmt request; - TOptMgmt result; - - /* Set up the option buffer */ - option.len = kOTFourByteOptionSize; - option.level= level; - option.name = name; - option.status = 0; - option.value[0] = 0;// Ignored because we're getting the value. - - /* Set up the request parameter for OTOptionManagement to point - to the option buffer we just filled out */ - - request.opt.buf= (UInt8 *) &option; - request.opt.len= sizeof(option); - request.flags= T_CURRENT; - - /* Set up the reply parameter for OTOptionManagement. */ - result.opt.buf = (UInt8 *) &option; - result.opt.maxlen = sizeof(option); - - err = OTOptionManagement(ep, &request, &result); - - if (err == noErr) { - switch (option.status) - { - case T_SUCCESS: - case T_READONLY: - *value = option.value[0]; - break; - default: - err = option.status; - break; - } - } - - return (err); -} - - -/* -================= -SetFourByteOption -================= -*/ -static OTResult SetFourByteOption(EndpointRef ep, - OTXTILevel level, - OTXTIName name, - UInt32 value) -{ - OTResult err; - TOption option; - TOptMgmt request; - TOptMgmt result; - - /* Set up the option buffer to specify the option and value to - set. */ - option.len = kOTFourByteOptionSize; - option.level= level; - option.name = name; - option.status = 0; - option.value[0] = value; - - /* Set up request parameter for OTOptionManagement */ - request.opt.buf= (UInt8 *) &option; - request.opt.len= sizeof(option); - request.flags = T_NEGOTIATE; - - /* Set up reply parameter for OTOptionManagement. */ - result.opt.buf = (UInt8 *) &option; - result.opt.maxlen = sizeof(option); - - - err = OTOptionManagement(ep, &request, &result); - - if (err == noErr) { - if (option.status != T_SUCCESS) - err = option.status; - } - - return (err); -} - - -/* -===================== -NET_GetLocalAddress -===================== -*/ -void NET_GetLocalAddress( void ) { - OSStatus err; - - for ( numIP = 0 ; numIP < MAX_IPS ; numIP++ ) { - err = OTInetGetInterfaceInfo( &sys_inetInfo[ numIP ], numIP ); - if ( err ) { - break; - } - Com_Printf( "LocalAddress: %i.%i.%i.%i\n", - ((byte *)&sys_inetInfo[numIP].fAddress)[0], - ((byte *)&sys_inetInfo[numIP].fAddress)[1], - ((byte *)&sys_inetInfo[numIP].fAddress)[2], - ((byte *)&sys_inetInfo[numIP].fAddress)[3] ); - - Com_Printf( "Netmask: %i.%i.%i.%i\n", - ((byte *)&sys_inetInfo[numIP].fNetmask)[0], - ((byte *)&sys_inetInfo[numIP].fNetmask)[1], - ((byte *)&sys_inetInfo[numIP].fNetmask)[2], - ((byte *)&sys_inetInfo[numIP].fNetmask)[3] ); - } -} - - -/* -================== -Sys_InitNetworking - - -struct InetAddress -{ - OTAddressType fAddressType; // always AF_INET - InetPort fPort; // Port number - InetHost fHost; // Host address in net byte order - UInt8 fUnused[8]; // Traditional unused bytes -}; -typedef struct InetAddress InetAddress; - -================== -*/ -void Sys_InitNetworking( void ) { - OSStatus err; - OTConfiguration *config; - TBind bind, bindOut; - InetAddress in, out; - int i; - - Com_Printf( "----- Sys_InitNetworking -----\n" ); - // init OpenTransport - Com_Printf( "... InitOpenTransport()\n" ); - err = InitOpenTransport(); - if ( err != noErr ) { - Com_Printf( "InitOpenTransport() failed\n" ); - Com_Printf( "------------------------------\n" ); - return; - } - - gOTInited = true; - - // get an endpoint - Com_Printf( "... OTOpenEndpoint()\n" ); - config = OTCreateConfiguration( kUDPName ); - -#if 1 - endpoint = OTOpenEndpoint( config, 0, nil, &err); -#else - err = OTAsyncOpenEndpoint( config, 0, 0, NotifyProc, 0 ); - if ( !endpoint ) { - err = 1; - } -#endif - - if ( err != noErr ) { - endpoint = 0; - Com_Printf( "OTOpenEndpoint() failed\n" ); - Com_Printf( "------------------------------\n" ); - return; - } - - // set non-blocking - err = OTSetNonBlocking( endpoint ); - - // scan for a valid port in our range - Com_Printf( "... OTBind()\n" ); - for ( i = 0 ; i < 10 ; i++ ) { - in.fAddressType = AF_INET; - in.fPort = PORT_SERVER + i; - in.fHost = 0; - - bind.addr.maxlen = sizeof( in ); - bind.addr.len = sizeof( in ); - bind.addr.buf = (unsigned char *)∈ - bind.qlen = 0; - - bindOut.addr.maxlen = sizeof( out ); - bindOut.addr.len = sizeof( out ); - bindOut.addr.buf = (unsigned char *)&out; - bindOut.qlen = 0; - - err = OTBind( endpoint, &bind, &bindOut ); - if ( err == noErr ) { - Com_Printf( "Opened UDP endpoint at port %i\n", - out.fPort ); - break; - } - } - - if ( err != noErr ) { - Com_Printf( "Couldn't bind a local port\n" ); - } - - // get the local address for LAN client detection - NET_GetLocalAddress(); - - - // set to allow broadcasts - err = SetFourByteOption( endpoint, INET_IP, IP_BROADCAST, T_YES ); - - if ( err != noErr ) { - Com_Printf( "IP_BROADCAST failed\n" ); - } - - // get an endpoint just for resolving addresses, because - // I was having crashing problems doing it on the same endpoint - config = OTCreateConfiguration( kUDPName ); - resolverEndpoint = OTOpenEndpoint( config, 0, nil, &err); - if ( err != noErr ) { - resolverEndpoint = 0; - Com_Printf( "OTOpenEndpoint() for resolver failed\n" ); - Com_Printf( "------------------------------\n" ); - return; - } - - in.fAddressType = AF_INET; - in.fPort = 0; - in.fHost = 0; - - bind.addr.maxlen = sizeof( in ); - bind.addr.len = sizeof( in ); - bind.addr.buf = (unsigned char *)∈ - bind.qlen = 0; - - bindOut.addr.maxlen = sizeof( out ); - bindOut.addr.len = sizeof( out ); - bindOut.addr.buf = (unsigned char *)&out; - bindOut.qlen = 0; - - err = OTBind( resolverEndpoint, &bind, &bindOut ); - - Com_Printf( "------------------------------\n" ); -} - - -/* -================== -Sys_ShutdownNetworking -================== -*/ -void Sys_ShutdownNetworking( void ) { - Com_Printf( "Sys_ShutdownNetworking();\n" ); - - if ( endpoint != kOTInvalidEndpointRef ) { - OTUnbind( endpoint ); - OTCloseProvider( endpoint ); - endpoint = kOTInvalidEndpointRef; - } - if ( resolverEndpoint != kOTInvalidEndpointRef ) { - OTUnbind( resolverEndpoint ); - OTCloseProvider( resolverEndpoint ); - resolverEndpoint = kOTInvalidEndpointRef; - } - if (gOTInited) { - CloseOpenTransport(); - gOTInited = false; - } -} - -/* -============= -Sys_StringToAdr - - -Does NOT parse port numbers - - -idnewt -192.246.40.70 -============= -*/ -qboolean Sys_StringToAdr( const char *s, netadr_t *a ) { - OSStatus err; - TBind in, out; - InetAddress inAddr; - DNSAddress dnsAddr; - - if ( !resolverEndpoint ) { - return qfalse; - } - - memset( &in, 0, sizeof( in ) ); - in.addr.buf = (UInt8 *) &dnsAddr; - in.addr.len = OTInitDNSAddress(&dnsAddr, (char *)s ); - in.qlen = 0; - - memset( &out, 0, sizeof( out ) ); - out.addr.buf = (byte *)&inAddr; - out.addr.maxlen = sizeof( inAddr ); - out.qlen = 0; - - err = OTResolveAddress( resolverEndpoint, &in, &out, 10000 ); - if ( err ) { - HandleOTError( err, "Sys_StringToAdr" ); - return qfalse; - } - - a->type = NA_IP; - *(int *)a->ip = inAddr.fHost; - - return qtrue; -} - -/* -================== -Sys_SendPacket -================== -*/ -#define MAX_PACKETLEN 1400 -void Sys_SendPacket( int length, const void *data, netadr_t to ) { - TUnitData d; - InetAddress inAddr; - OSStatus err; - - if ( !endpoint ) { - return; - } - - if ( length > MAX_PACKETLEN ) { - Com_Error( ERR_DROP, "Sys_SendPacket: length > MAX_PACKETLEN" ); - } - - inAddr.fAddressType = AF_INET; - inAddr.fPort = to.port; - if ( to.type == NA_BROADCAST ) { - inAddr.fHost = -1; - } else { - inAddr.fHost = *(int *)&to.ip; - } - - memset( &d, 0, sizeof( d ) ); - - d.addr.len = sizeof( inAddr ); - d.addr.maxlen = sizeof( inAddr ); - d.addr.buf = (unsigned char *)&inAddr; - - d.opt.len = 0; - d.opt.maxlen = 0; - d.opt.buf = NULL; - - d.udata.len = length; - d.udata.maxlen = length; - d.udata.buf = (unsigned char *)data; - - err = OTSndUData( endpoint, &d ); - if ( err ) { - HandleOTError( err, "Sys_SendPacket" ); - } -} - -/* -================== -Sys_GetPacket - -Never called by the game logic, just the system event queing -================== -*/ -qboolean Sys_GetPacket ( netadr_t *net_from, msg_t *net_message ) { - TUnitData d; - InetAddress inAddr; - OSStatus err; - OTFlags flags; - - if ( !endpoint ) { - return qfalse; - } - - inAddr.fAddressType = AF_INET; - inAddr.fPort = 0; - inAddr.fHost = 0; - - memset( &d, 0, sizeof( d ) ); - - d.addr.len = sizeof( inAddr ); - d.addr.maxlen = sizeof( inAddr ); - d.addr.buf = (unsigned char *)&inAddr; - - d.opt.len = 0; - d.opt.maxlen = 0; - d.opt.buf = 0; - - d.udata.len = net_message->maxsize; - d.udata.maxlen = net_message->maxsize; - d.udata.buf = net_message->data; - - err = OTRcvUData( endpoint, &d, &flags ); - if ( err ) { - if ( err == kOTNoDataErr ) { - return false; - } - HandleOTError( err, "Sys_GetPacket" ); - return qfalse; - } - - net_from->type = NA_IP; - net_from->port = inAddr.fPort; - *(int *)net_from->ip = inAddr.fHost; - - net_message->cursize = d.udata.len; - - return qtrue; -} - - -/* -================== -Sys_IsLANAddress - -LAN clients will have their rate var ignored -================== -*/ -qboolean Sys_IsLANAddress (netadr_t adr) { - int i; - int ip; - - if ( adr.type == NA_LOOPBACK ) { - return qtrue; - } - - if ( adr.type != NA_IP ) { - return qfalse; - } - - for ( ip = 0 ; ip < numIP ; ip++ ) { - for ( i = 0 ; i < 4 ; i++ ) { - if ( ( adr.ip[i] & ((byte *)&sys_inetInfo[ip].fNetmask)[i] ) - != ( ((byte *)&sys_inetInfo[ip].fAddress)[i] & ((byte *)&sys_inetInfo[ip].fNetmask)[i] ) ) { - break; - } - } - if ( i == 4 ) { - return qtrue; // matches this subnet - } - } - - return qfalse; -} - - -void NET_Sleep( int i ) { -} diff --git a/code/mac/mac_snddma.c b/code/mac/mac_snddma.c deleted file mode 100644 index 9643aa3..0000000 --- a/code/mac/mac_snddma.c +++ /dev/null @@ -1,140 +0,0 @@ - -// mac_snddma.c -// all other sound mixing is portable - -#include "../client/snd_local.h" -#include - -#define MAX_MIXED_SAMPLES 0x8000 -#define SUBMISSION_CHUNK 0x100 - -static short s_mixedSamples[MAX_MIXED_SAMPLES]; -static int s_chunkCount; // number of chunks submitted -static SndChannel *s_sndChan; -static ExtSoundHeader s_sndHeader; - -/* -=============== -S_Callback -=============== -*/ -void S_Callback( SndChannel *sc, SndCommand *cmd ) { - SndCommand mySndCmd; - SndCommand mySndCmd2; - int offset; - - offset = ( s_chunkCount * SUBMISSION_CHUNK ) & (MAX_MIXED_SAMPLES-1); - - // queue up another sound buffer - memset( &s_sndHeader, 0, sizeof( s_sndHeader ) ); - s_sndHeader.samplePtr = (void *)(s_mixedSamples + offset); - s_sndHeader.numChannels = 2; - s_sndHeader.sampleRate = rate22khz; - s_sndHeader.loopStart = 0; - s_sndHeader.loopEnd = 0; - s_sndHeader.encode = extSH; - s_sndHeader.baseFrequency = 1; - s_sndHeader.numFrames = SUBMISSION_CHUNK / 2; - s_sndHeader.markerChunk = NULL; - s_sndHeader.instrumentChunks = NULL; - s_sndHeader.AESRecording = NULL; - s_sndHeader.sampleSize = 16; - - mySndCmd.cmd = bufferCmd; - mySndCmd.param1 = 0; - mySndCmd.param2 = (int)&s_sndHeader; - SndDoCommand( sc, &mySndCmd, true ); - - // and another callback - mySndCmd2.cmd = callBackCmd; - mySndCmd2.param1 = 0; - mySndCmd2.param2 = 0; - SndDoCommand( sc, &mySndCmd2, true ); - - s_chunkCount++; // this is the next buffer we will submit -} - -/* -=============== -S_MakeTestPattern -=============== -*/ -void S_MakeTestPattern( void ) { - int i; - float v; - int sample; - - for ( i = 0 ; i < dma.samples / 2 ; i ++ ) { - v = sin( M_PI * 2 * i / 64 ); - sample = v * 0x4000; - ((short *)dma.buffer)[i*2] = sample; - ((short *)dma.buffer)[i*2+1] = sample; - } -} - -/* -=============== -SNDDMA_Init -=============== -*/ -qboolean SNDDMA_Init(void) { - int err; - - // create a sound channel - s_sndChan = NULL; - err = SndNewChannel( &s_sndChan, sampledSynth, initStereo, NewSndCallBackProc(S_Callback) ); - if ( err ) { - return false; - } - - dma.channels = 2; - dma.samples = MAX_MIXED_SAMPLES; - dma.submission_chunk = SUBMISSION_CHUNK; - dma.samplebits = 16; - dma.speed = 22050; - dma.buffer = (byte *)s_mixedSamples; - - // que up the first submission-chunk sized buffer - s_chunkCount = 0; - - S_Callback( s_sndChan, NULL ); - - return qtrue; -} - -/* -=============== -SNDDMA_GetDMAPos -=============== -*/ -int SNDDMA_GetDMAPos(void) { - return s_chunkCount * SUBMISSION_CHUNK; -} - -/* -=============== -SNDDMA_Shutdown -=============== -*/ -void SNDDMA_Shutdown(void) { - if ( s_sndChan ) { - SndDisposeChannel( s_sndChan, true ); - s_sndChan = NULL; - } -} - -/* -=============== -SNDDMA_BeginPainting -=============== -*/ -void SNDDMA_BeginPainting(void) { -} - -/* -=============== -SNDDMA_Submit -=============== -*/ -void SNDDMA_Submit(void) { -} diff --git a/code/mac/macprefix.h b/code/mac/macprefix.h deleted file mode 100644 index 2836860..0000000 --- a/code/mac/macprefix.h +++ /dev/null @@ -1,3 +0,0 @@ -//#define __MACOS__ // needed for MrC -#define BOTLIB - diff --git a/code/mac/q3.rsrc b/code/mac/q3.rsrc deleted file mode 100644 index e69de29..0000000 diff --git a/code/mac/vssver.scc b/code/mac/vssver.scc deleted file mode 100644 index 370282c..0000000 Binary files a/code/mac/vssver.scc and /dev/null differ diff --git a/code/mp3code/vssver.scc b/code/mp3code/vssver.scc deleted file mode 100644 index 8e24b4f..0000000 Binary files a/code/mp3code/vssver.scc and /dev/null differ diff --git a/code/mssccprj.scc b/code/mssccprj.scc deleted file mode 100644 index db1c382..0000000 --- a/code/mssccprj.scc +++ /dev/null @@ -1,9 +0,0 @@ -SCC = This is a Source Code Control file - -[game.dsp] -SCC_Aux_Path = "\\ravend\vss_projects\StarWars" -SCC_Project_Name = "$/code", JVOAAAAA - -[starwars.dsp] -SCC_Aux_Path = "\\ravend\vss_projects\StarWars" -SCC_Project_Name = "$/code", JVOAAAAA diff --git a/code/null/mac_net.c b/code/null/mac_net.c deleted file mode 100644 index 6232aff..0000000 --- a/code/null/mac_net.c +++ /dev/null @@ -1,44 +0,0 @@ - -#include "../game/q_shared.h" -#include "../qcommon/qcommon.h" - -/* -============= -NET_StringToAdr - -localhost -idnewt -idnewt:28000 -192.246.40.70 -192.246.40.70:28000 -============= -*/ -qboolean NET_StringToAdr (char *s, netadr_t *a) -{ - if (!strcmp (s, "localhost")) { - memset (a, 0, sizeof(*a)); - a->type = NA_LOOPBACK; - return true; - } - - return false; -} - -/* -================== -Sys_SendPacket -================== -*/ -void Sys_SendPacket( int length, void *data, netadr_t to ) { -} - -/* -================== -Sys_GetPacket - -Never called by the game logic, just the system event queing -================== -*/ -qboolean Sys_GetPacket ( netadr_t *net_from, msg_t *net_message ) { - return false; -} diff --git a/code/null/null_glimp.c b/code/null/null_glimp.c deleted file mode 100644 index 30b84f7..0000000 --- a/code/null/null_glimp.c +++ /dev/null @@ -1,39 +0,0 @@ -#include "../renderer/tr_local.h" - - -qboolean ( * qwglSwapIntervalEXT)( int interval ); -void ( * qglMultiTexCoord2fARB )( enum texture, float s, float t ); -void ( * qglActiveTextureARB )( enum texture ); -void ( * qglClientActiveTextureARB )( enum texture ); - - -void ( * qglLockArraysEXT)( int, int); -void ( * qglUnlockArraysEXT) ( void ); - - -void GLimp_EndFrame( void ) { -} - -int GLimp_Init( void ) -{ -} - -void GLimp_Shutdown( void ) { -} - -rserr_t GLimp_SetMode( const char *drivername, int *pWidth, int *pHeight, int mode, qboolean fullscreen ) { -} - - -void GLimp_EnableLogging( qboolean enable ) { -} - -void GLimp_LogComment( char *comment ) { -} - -qboolean QGL_Init( const char *dllname ) { - return true; -} - -void QGL_Shutdown( void ) { -} diff --git a/code/null/null_main.c b/code/null/null_main.c deleted file mode 100644 index 59a5797..0000000 --- a/code/null/null_main.c +++ /dev/null @@ -1,94 +0,0 @@ -// sys_null.h -- null system driver to aid porting efforts - -#include "../qcommon/qcommon.h" -#include "errno.h" - -int sys_curtime; - - -//=================================================================== - -void Sys_BeginStreamedFile( fileHandle_t f, int readAhead ) { -} - -void Sys_EndStreamedFile( fileHandle_t f ) { -} - -int Sys_StreamedRead( void *buffer, int size, int count, fileHandle_t f ) { - return FS_Read( buffer, size, count, f ); -} - -void Sys_StreamSeek( fileHandle_t f, int offset, int origin ) { - FS_Seek( f, offset, origin ); -} - - -//=================================================================== - - -void Sys_mkdir (char *path) { -} - -void Sys_Error (char *error, ...) { - va_list argptr; - - printf ("Sys_Error: "); - va_start (argptr,error); - vprintf (error,argptr); - va_end (argptr); - printf ("\n"); - - exit (1); -} - -void Sys_Quit (void) { - exit (0); -} - -void Sys_UnloadGame (void) { -} - -void *Sys_GetGameAPI (void *parms) { - return NULL; -} - -char *Sys_GetClipboardData( void ) { - return NULL; -} - -int Sys_Milliseconds (void) { - return 0; -} - -void Sys_Mkdir (char *path) { -} - -char *Sys_FindFirst (char *path, unsigned musthave, unsigned canthave) { - return NULL; -} - -char *Sys_FindNext (unsigned musthave, unsigned canthave) { - return NULL; -} - -void Sys_FindClose (void) { -} - -void Sys_Init (void) { -} - - -void Sys_EarlyOutput( char *string ) { - printf( "%s", string ); -} - - -void main (int argc, char **argv) { - Com_Init (argc, argv); - - while (1) { - Com_Frame( ); - } -} - - diff --git a/code/null/null_net.c b/code/null/null_net.c deleted file mode 100644 index 2973a8d..0000000 --- a/code/null/null_net.c +++ /dev/null @@ -1,43 +0,0 @@ - -#include "../qcommon/qcommon.h" - -/* -============= -NET_StringToAdr - -localhost -idnewt -idnewt:28000 -192.246.40.70 -192.246.40.70:28000 -============= -*/ -qboolean NET_StringToAdr (char *s, netadr_t *a) -{ - if (!strcmp (s, "localhost")) { - memset (a, 0, sizeof(*a)); - a->type = NA_LOOPBACK; - return true; - } - - return false; -} - -/* -================== -Sys_SendPacket -================== -*/ -void Sys_SendPacket( int length, void *data, netadr_t to ) { -} - -/* -================== -Sys_GetPacket - -Never called by the game logic, just the system event queing -================== -*/ -qboolean Sys_GetPacket ( netadr_t *net_from, msg_t *net_message ) { - return false; -} diff --git a/code/null/null_snddma.c b/code/null/null_snddma.c deleted file mode 100644 index 2e5d3d4..0000000 --- a/code/null/null_snddma.c +++ /dev/null @@ -1,27 +0,0 @@ - -// snddma_null.c -// all other sound mixing is portable - -#include "../client/client.h" - -qboolean SNDDMA_Init(void) -{ - return qfalse; -} - -int SNDDMA_GetDMAPos(void) -{ - return 0; -} - -void SNDDMA_Shutdown(void) -{ -} - -void SNDDMA_BeginPainting (void) -{ -} - -void SNDDMA_Submit(void) -{ -} diff --git a/code/null/vssver.scc b/code/null/vssver.scc deleted file mode 100644 index 8ae9dab..0000000 Binary files a/code/null/vssver.scc and /dev/null differ diff --git a/code/qcommon/files.cpp b/code/qcommon/files.cpp index 69abb62..f57e118 100644 --- a/code/qcommon/files.cpp +++ b/code/qcommon/files.cpp @@ -557,7 +557,9 @@ qboolean FS_CopyUserGenFile( const char *filename_src, const char *filename_dst Com_Printf( "FS_CopyUserGenFile: %s to %s\n", ospath_src, ospath_dst ); } - return FS_CopyFile( ospath_src, ospath_dst, qtrue ); + remove(ospath_dst); + return (0 == rename (ospath_src, ospath_dst )); + //return FS_CopyFile( ospath_src, ospath_dst, qtrue ); } /* diff --git a/code/qcommon/strip.cpp b/code/qcommon/strip.cpp index 5acb6d1..319f823 100644 --- a/code/qcommon/strip.cpp +++ b/code/qcommon/strip.cpp @@ -495,6 +495,63 @@ void cStringsSingle::SetText(const char *newText) } +// fix problems caused by fucking morons entering clever "rich" chars in to new text files *after* the auto-stripper +// removed them all in the first place... +// +// ONLY DO THIS FOR ENGLISH, OR IT BREAKS ASIAN STRINGS!!!!!!!!!!!!!!!!!!!!! +// +static void FixIllegalChars(char *psText) +{ + char *p; + +// strXLS_Speech.Replace(va("%c",0x92),va("%c",0x27)); // "'" + while ((p=strchr(psText,0x92))!=NULL) // "rich" (and illegal) apostrophe + { + *p = 0x27; + } + +// strXLS_Speech.Replace(va("%c",0x93),"\""); // smart quotes -> '"' + while ((p=strchr(psText,0x93))!=NULL) // "rich" (and illegal) apostrophe + { + *p = '"'; + } + +// strXLS_Speech.Replace(va("%c",0x94),"\""); // smart quotes -> '"' + while ((p=strchr(psText,0x94))!=NULL) // "rich" (and illegal) apostrophe + { + *p = '"'; + } + +// strXLS_Speech.Replace(va("%c",0x0B),"."); // full stop + while ((p=strchr(psText,0x0B))!=NULL) // "rich" (and illegal) apostrophe + { + *p = '.'; + } + +// strXLS_Speech.Replace(va("%c",0x85),"..."); // "..."-char -> 3-char "..." + while ((p=strchr(psText,0x85))!=NULL) // "rich" (and illegal) apostrophe + { + *p = '.'; // can't do in-string replace of "." with "...", so just forget it + } + +// strXLS_Speech.Replace(va("%c",0x91),va("%c",0x27)); // "'" + while ((p=strchr(psText,0x91))!=NULL) // "rich" (and illegal) apostrophe + { + *p = 0x27; + } + +// strXLS_Speech.Replace(va("%c",0x96),va("%c",0x2D)); // "-" + while ((p=strchr(psText,0x96))!=NULL) + { + *p = 0x2D; + } + +// strXLS_Speech.Replace(va("%c",0x97),va("%c",0x2D)); // "-" + while ((p=strchr(psText,0x97))!=NULL) + { + *p = 0x2D; + } +} bool cStringsSingle::UnderstandToken(int token, char *data ) { @@ -507,11 +564,19 @@ bool cStringsSingle::UnderstandToken(int token, char *data ) { if (LanguagePair->Name == TK_TEXT_LANGUAGE1 && token == TK_TEXT_LANGUAGE1 && !Text) { // default to english in case there is no foreign + if (LanguagePair->Name == TK_TEXT_LANGUAGE1) + { + FixIllegalChars(data); + } SetText(data); return true; } else if (LanguagePair->Name == token && LanguagePair->Value == sp_language->integer) { + if (LanguagePair->Name == TK_TEXT_LANGUAGE1) + { + FixIllegalChars(data); + } SetText(data); return true; } @@ -828,10 +893,13 @@ void SP_Unload(unsigned char Registration) // Direct string functions -int SP_GetStringID(const char *Reference) +int SP_GetStringID(const char *inReference) { map::iterator i; int ID; + char Reference[MAX_QPATH]; + Q_strncpyz(Reference, inReference, MAX_QPATH); + strupr(Reference); for(i = SP_ListByID.begin(); i != SP_ListByID.end(); i++) { @@ -946,7 +1014,7 @@ static void SP_UpdateLanguage(void) void SP_Init(void) { sp_language = Cvar_Get("sp_language", va("%d", SP_LANGUAGE_ENGLISH), CVAR_ARCHIVE | CVAR_NORESTART); - sp_show_strip = Cvar_Get ("sv_show_strip", "0", 0); + sp_show_strip = Cvar_Get ("sv_show_strip", "0", 0); // don't switch this on!!!!!!, test only (apparently) // Cvar_Set("sp_language", va("%d", SP_LANGUAGE_JAPANESE)); // stetest, do NOT leave in diff --git a/code/qcommon/stv_version.h b/code/qcommon/stv_version.h index 9e5924d..cbc1f7d 100644 --- a/code/qcommon/stv_version.h +++ b/code/qcommon/stv_version.h @@ -1,6 +1,6 @@ // Current version of the single player game -#define Q3_VERSION "JK2: v0.55" +#define Q3_VERSION "JK2: v0.56" // end diff --git a/code/qcommon/tags.h b/code/qcommon/tags.h index 5faba0c..265b506 100644 --- a/code/qcommon/tags.h +++ b/code/qcommon/tags.h @@ -41,6 +41,7 @@ TAGDEF(GP2), // generic parser 2 TAGDEF(SPECIAL_MEM_TEST), // special usage in one function only!!!!!! TAGDEF(ATI_NPATCH), // special tag for checking mem usage / leak of ATI's NPATCH code + TAGDEF(ANIMATION_CFG), // may as well keep this seperate / readable /* TAGDEF(SHADER), TAGDEF(RMAP), TAGDEF(CURVES), diff --git a/code/qcommon/vssver.scc b/code/qcommon/vssver.scc deleted file mode 100644 index 8337976..0000000 Binary files a/code/qcommon/vssver.scc and /dev/null differ diff --git a/code/renderer/amd3d.h b/code/renderer/amd3d.h deleted file mode 100644 index 45fece2..0000000 --- a/code/renderer/amd3d.h +++ /dev/null @@ -1,471 +0,0 @@ -/****************************************************************** -; * -; * Copyright (c) 1996-1998 ADVANCED MICRO DEVICES, INC. -; * All Rights reserved. -; * -; * This software is unpublished and contains the trade secrets -; * and confidential proprietary information of AMD. Unless -; * otherwise provided in the Software Agreement associated -; * herewith, it is licensed in confidence "AS IS" and -; * is not to be reproduced in whole or part by any means except -; * for backup. Use, duplication, or disclosure by the Government -; * is subject to the restrictions in paragraph(b)(3)(B)of the -; * Rights in Technical Data and Computer Software clause in -; * DFAR 52.227-7013(a)(Oct 1988). Software owned by Advanced -; * Micro Devices Inc., One AMD Place, P.O. Box 3453, Sunnyvale, -; * CA 94088-3453. -; * -; ****************************************************************** - * - * AMD3D.H - * - * MACRO FORMAT - * ============ - * This file contains inline assembly macros that - * generate AMD-3D instructions in binary format. - * Therefore, C or C++ programmer can use AMD-3D instructions - * without any penalty in their C or C++ source code. - * - * The macro's name and format conventions are as follow: - * - * - * 1. First argument of macro is a destination and - * second argument is a source operand. - * ex) _asm PFCMPEQ (m3, m4) - * | | - * dst src - * - * 2. The destination operand can be m0 to m7 only. - * The source operand can be any one of the register - * m0 to m7 or _eax, _ecx, _edx, _ebx, _esi, or _edi - * that contains effective address. - * ex) _asm PFRCP (M7, M6) - * ex) _asm PFRCPIT2 (m0, m4) - * ex) _asm PFMUL (m3, _edi) - * - * 3. The prefetch(w) takes one src operand _eax, ecx, _edx, - * _ebx, _esi, or _edi that contains effective address. - * ex) _asm PREFETCH (_edi) - * - * EXAMPLE - * ======= - * Following program doesn't do anything but it shows you - * how to use inline assembly AMD-3D instructions in C. - * Note that this will only work in flat memory model which - * segment registers cs, ds, ss and es point to the same - * linear address space total less than 4GB. - * - * Used Microsoft VC++ 5.0 - * - * #include - * #include "amd3d.h" - * - * void main () - * { - * float x = (float)1.25; - * float y = (float)1.25; - * float z, zz; - * - * _asm { - * movd mm1, x - * movd mm2, y - * pfmul (m1, m2) - * movd z, mm1 - * femms - * } - * - * printf ("value of z = %f\n", z); - * - * // - * // Demonstration of using the memory instead of - * // multimedia register - * // - * _asm { - * movd mm3, x - * lea esi, y // load effective address of y - * pfmul (m3, _esi) - * movd zz, mm3 - * femms - * } - * - * printf ("value of zz = %f\n", zz); - * } - ******************************************************************/ - -#define M0 0xc0 -#define M1 0xc1 -#define M2 0xc2 -#define M3 0xc3 -#define M4 0xc4 -#define M5 0xc5 -#define M6 0xc6 -#define M7 0xc7 - -#define m0 0xc0 -#define m1 0xc1 -#define m2 0xc2 -#define m3 0xc3 -#define m4 0xc4 -#define m5 0xc5 -#define m6 0xc6 -#define m7 0xc7 -#define _EAX 0x00 -#define _ECX 0x01 -#define _EDX 0x02 -#define _EBX 0x03 -#define _ESI 0x06 -#define _EDI 0x07 -#define _eax 0x00 -#define _ecx 0x01 -#define _edx 0x02 -#define _ebx 0x03 -#define _esi 0x06 -#define _edi 0x07 - -#define PF2ID(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0x1d \ -} - -#define PFACC(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0xae \ -} - -#define PFADD(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0x9e \ -} - -#define PFCMPEQ(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0xb0 \ -} - -#define PFCMPGE(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0x90 \ -} - -#define PFCMPGT(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0xa0 \ -} - -#define PFMAX(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0xa4 \ -} - -#define PFMIN(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0x94 \ -} - -#define PFMUL(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0xb4 \ -} - -#define PFRCP(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0x96 \ -} - -#define PFRCPIT1(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0xa6 \ -} - -#define PFRCPIT2(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0xb6 \ -} - -#define PFRSQRT(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0x97 \ -} - -#define PFRSQIT1(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0xa7 \ -} - -#define PFSUB(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0x9a \ -} - -#define PFSUBR(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0xaa \ -} - -#define PI2FD(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0x0d \ -} - -#define FEMMS \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0e \ -} - -#define PAVGUSB(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0xbf \ -} - -#define PMULHRW(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0xb7 \ -} - -#define PREFETCH(src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0d \ - _asm _emit 0x00 | src \ -} - -#define PREFETCHW(src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0d \ - _asm _emit 0x08 | src \ -} - -// -// Exactly same as above except macro names are all -// lower case latter. -// -#define pf2id(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0x1d \ -} - -#define pfacc(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0xae \ -} - -#define pfadd(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0x9e \ -} - -#define pfcmpeq(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0xb0 \ -} - -#define pfcmpge(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0x90 \ -} - -#define pfcmpgt(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0xa0 \ -} - -#define pfmax(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0xa4 \ -} - -#define pfmin(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0x94 \ -} - -#define pfmul(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0xb4 \ -} - -#define pfrcp(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0x96 \ -} - -#define pfrcpit1(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0xa6 \ -} - -#define pfrcpit2(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0xb6 \ -} - -#define pfrsqrt(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0x97 \ -} - -#define pfrsqit1(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0xa7 \ -} - -#define pfsub(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0x9a \ -} - -#define pfsubr(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0xaa \ -} - -#define pi2fd(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0x0d \ -} - -#define femms \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0e \ -} - -#define pavgusb(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0xbf \ -} - -#define pmulhrw(dst, src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0f \ - _asm _emit ((dst & 0x3f) << 3) | src \ - _asm _emit 0xb7 \ -} - -#define prefetch(src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0d \ - _asm _emit 0x00 | src \ -} - -#define prefetchw(src) \ -{\ - _asm _emit 0x0f \ - _asm _emit 0x0d \ - _asm _emit 0x08 | src \ -} diff --git a/code/renderer/matcomp.c b/code/renderer/matcomp.c index 7275098..796cca8 100644 --- a/code/renderer/matcomp.c +++ b/code/renderer/matcomp.c @@ -216,7 +216,7 @@ void MC_UnCompress(float mat[3][4],const unsigned char * comp) } -void MC_UnCompressQuat(float mat[3][4],const unsigned short * pwIn) +void MC_UnCompressQuat(float mat[3][4],const unsigned char * comp) { float w,x,y,z,f; float fTx; @@ -232,6 +232,8 @@ void MC_UnCompressQuat(float mat[3][4],const unsigned short * pwIn) float fTyz; float fTzz; + const unsigned short *pwIn = (unsigned short *) comp; + w = *pwIn++; w/=16383.0f; w-=2.0f; @@ -288,3 +290,4 @@ void MC_UnCompressQuat(float mat[3][4],const unsigned short * pwIn) mat[2][3] = f; } + diff --git a/code/renderer/matcomp.h b/code/renderer/matcomp.h index 89c27f8..9255e1f 100644 --- a/code/renderer/matcomp.h +++ b/code/renderer/matcomp.h @@ -21,7 +21,7 @@ extern "C" void MC_Compress(const float mat[3][4],unsigned char * comp); void MC_UnCompress(float mat[3][4],const unsigned char * comp); -void MC_UnCompressQuat(float mat[3][4],const unsigned short * pwIn); +void MC_UnCompressQuat(float mat[3][4],const unsigned char * comp); #ifdef __cplusplus diff --git a/code/renderer/mdx_format.h b/code/renderer/mdx_format.h index e8239d0..113c9f9 100644 --- a/code/renderer/mdx_format.h +++ b/code/renderer/mdx_format.h @@ -25,9 +25,8 @@ // // normal version numbers... // -#define MDXM_VERSION 4 -#define MDXA_VERSION 4 -#define MDXA_VERSION_QUAT 5 +#define MDXM_VERSION 6 +#define MDXA_VERSION 6 // (Note that since there is now a "_info.txt" file written out by carcass any changes made in here that // introduce new data should also be reflected in the info-output) @@ -54,12 +53,19 @@ // triangle side-ordering stuff for tags... // -#define iG2_TRISIDE_MIDDLE 1 -#define iG2_TRISIDE_LONGEST 0 -#define iG2_TRISIDE_SHORTEST 2 +#define iG2_TRISIDE_MIDDLE 1 +#define iG2_TRISIDE_LONGEST 0 +#define iG2_TRISIDE_SHORTEST 2 + +#define fG2_BONEWEIGHT_RECIPROCAL_MULT ((float)(1.0f/1023.0f)) +#define iG2_BITS_PER_BONEREF 5 +#define iMAX_G2_BONEREFS_PER_SURFACE (1<... + // (note that I've defined it using '>' internally, so it sorts with higher weights being "less", for distance weight-culling + // + #ifdef __cplusplus + bool operator < (const mdxmWeight_t& _X) const {return (boneWeight>_X.boneWeight);} + #endif +} +#ifndef __cplusplus +mdxmWeight_t +#endif +; +*/ +/* #ifdef __cplusplus struct mdxaCompBone_t #else @@ -91,7 +114,7 @@ typedef struct mdxaCompBone_t #endif ; - +*/ #ifdef __cplusplus struct mdxaCompQuatBone_t #else @@ -206,8 +229,6 @@ typedef struct { int numTriangles; int ofsTriangles; - int maxVertBoneWeights; // ... per vertex for hardware to reference. This number subtract the vert->numWeights gives # pad weights (which software will ignore) - // Bone references are a set of ints representing all the bones // present in any vertex weights for this surface. This is // needed because a model may have surfaces that need to be @@ -235,17 +256,72 @@ typedef struct { // for each vert... (mdxmSurface_t->numVerts) // { // mdxVertex_t - this is an array with number of verts from the surface definition as its bounds. It contains normal info, texture coors and number of weightings for this bone - + // (this is now kept at 32 bytes for cache-aligning) typedef struct { vec3_t normal; vec3_t vertCoords; - vec2_t texCoords; - int numWeights; // remember, this is for software counts, look at mdxmSurface_t->numActualWeights for skipping purposes to account for padded weights - mdxmWeight_t weights[1]; // variable sized + + // packed int... + unsigned int uiNmWeightsAndBoneIndexes; // 32 bits. format: + // 31 & 30: 0..3 (= 1..4) weight count + // 29 & 28 (spare) + // 2 bit pairs at 20,22,24,26 are 2-bit overflows from 4 BonWeights below (20=[0], 22=[1]) etc) + // 5-bits each (iG2_BITS_PER_BONEREF) for boneweights + // effectively a packed int, each bone weight converted from 0..1 float to 0..255 int... + // promote each entry to float and multiply by fG2_BONEWEIGHT_RECIPROCAL_MULT to convert. + byte BoneWeightings[iMAX_G2_BONEWEIGHTS_PER_VERT]; // 4 + } mdxmVertex_t; // } vert +#ifdef __cplusplus + +// these are convenience functions that I can invoked in code. Do NOT change them (because this is a shared file), +// but if you want to copy the logic out and use your own versions then fine... +// +static inline int G2_GetVertWeights( const mdxmVertex_t *pVert ) +{ + int iNumWeights = (pVert->uiNmWeightsAndBoneIndexes >> 30)+1; // 1..4 count + + return iNumWeights; +} + +static inline int G2_GetVertBoneIndex( const mdxmVertex_t *pVert, const int iWeightNum) +{ + int iBoneIndex = (pVert->uiNmWeightsAndBoneIndexes>>(iG2_BITS_PER_BONEREF*iWeightNum))&((1<BoneWeightings[iWeightNum]; + iTemp|= (pVert->uiNmWeightsAndBoneIndexes >> (iG2_BONEWEIGHT_TOPBITS_SHIFT+(iWeightNum*2)) ) & iG2_BONEWEIGHT_TOPBITS_AND; + + float fBoneWeight = fG2_BONEWEIGHT_RECIPROCAL_MULT * iTemp; + + if (fBoneWeight == 0.0f) + { + // special case to fix flatten-to-zero cases for extremely light weightings. Could probably be omitted if desperate... + // + fBoneWeight = 0.00045f; + } + + return fBoneWeight; +} +#endif + // for each vert... (mdxmSurface_t->numVerts) (seperated from mdxmVertex_t struct for cache reasons) + // { + // mdxVertex_t - this is an array with number of verts from the surface definition as its bounds. It contains normal info, texture coors and number of weightings for this bone + + typedef struct { + vec2_t texCoords; + } mdxmVertexTexCoord_t; + + // } vert + + // } surface // } LOD @@ -271,7 +347,7 @@ typedef struct { // frames and bones are shared by all levels of detail // int numFrames; - int ofsFrames; + int ofsFrames; // points at mdxaFrame_t array int numBones; // (no offset to these since they're inside the frames array) int ofsCompBonePool; // offset to global compressed-bone pool that all frames use int ofsSkel; // offset to mdxaSkel_t info @@ -307,18 +383,24 @@ typedef struct { // } - -// for each frame... (mdxaHeader_t->numFrames) -// { - // mdxaFrame_t - which contains the header for the bones for this surface, plus the actual bone matrices themselves - - typedef struct { - // (used to contain frame bounds info etc as well, doesn't now) - // - int boneIndexes[1]; // [numBones] ... into compressed bone pool - } mdxaFrame_t; // struct size = (int)( &((mdxaFrame_t *)0)->bones[ mdxaHeader_t->numBones ] ); - -// } + // (offset @ mdxaHeader_t->ofsFrames) + // + // array of 3 byte indices here (hey, 25% saving over 4-byte really adds up)... + // + // + // access as follows to get the index for a given + // + // (iFrameNum * mdxaHeader_t->numBones * 3) + (iBoneNum * 3) + // + // then read the int at that location and AND it with 0x00FFFFFF. I use the struct below simply for easy searches + typedef struct + { + int iIndex; // this struct for pointing purposes, need to and with 0x00FFFFFF to be meaningful + } mdxaIndex_t; + // + // (note that there's then an alignement-pad here to get the next struct back onto 32-bit alignement) + // + // this index then points into the following... // Compressed-bone pool that all frames use (mdxaHeader_t->ofsCompBonePool) (defined at end because size unknown until end) diff --git a/code/renderer/mssccprj.scc b/code/renderer/mssccprj.scc deleted file mode 100644 index df82911..0000000 --- a/code/renderer/mssccprj.scc +++ /dev/null @@ -1,5 +0,0 @@ -SCC = This is a Source Code Control file - -[renderer.dsp] -SCC_Aux_Path = "\\ravend\vss_projects\StarWars" -SCC_Project_Name = "$/code/renderer", VVOAAAAA diff --git a/code/renderer/qgl_linked.h b/code/renderer/qgl_linked.h deleted file mode 100644 index cf48a7c..0000000 --- a/code/renderer/qgl_linked.h +++ /dev/null @@ -1,336 +0,0 @@ - -#define qglAccum glAccum -#define qglAlphaFunc glAlphaFunc -#define qglAreTexturesResident glAreTexturesResident -#define qglArrayElement glArrayElement -#define qglBegin glBegin -#define qglBindTexture glBindTexture -#define qglBitmap glBitmap -#define qglBlendFunc glBlendFunc -#define qglCallList glCallList -#define qglCallLists glCallLists -#define qglClear glClear -#define qglClearAccum glClearAccum -#define qglClearColor glClearColor -#define qglClearDepth glClearDepth -#define qglClearIndex glClearIndex -#define qglClearStencil glClearStencil -#define qglClipPlane glClipPlane -#define qglColor3b glColor3b -#define qglColor3bv glColor3bv -#define qglColor3d glColor3d -#define qglColor3dv glColor3dv -#define qglColor3f glColor3f -#define qglColor3fv glColor3fv -#define qglColor3i glColor3i -#define qglColor3iv glColor3iv -#define qglColor3s glColor3s -#define qglColor3sv glColor3sv -#define qglColor3ub glColor3ub -#define qglColor3ubv glColor3ubv -#define qglColor3ui glColor3ui -#define qglColor3uiv glColor3uiv -#define qglColor3us glColor3us -#define qglColor3usv glColor3usv -#define qglColor4b glColor4b -#define qglColor4bv glColor4bv -#define qglColor4d glColor4d -#define qglColor4dv glColor4dv -#define qglColor4f glColor4f -#define qglColor4fv glColor4fv -#define qglColor4i glColor4i -#define qglColor4iv glColor4iv -#define qglColor4s glColor4s -#define qglColor4sv glColor4sv -#define qglColor4ub glColor4ub -#define qglColor4ubv glColor4ubv -#define qglColor4ui glColor4ui -#define qglColor4uiv glColor4uiv -#define qglColor4us glColor4us -#define qglColor4usv glColor4usv -#define qglColorMask glColorMask -#define qglColorMaterial glColorMaterial -#define qglColorPointer glColorPointer -#define qglCopyPixels glCopyPixels -#define qglCopyTexImage1D glCopyTexImage1D -#define qglCopyTexImage2D glCopyTexImage2D -#define qglCopyTexSubImage1D glCopyTexSubImage1D -#define qglCopyTexSubImage2D glCopyTexSubImage2D -#define qglCullFace glCullFace -#define qglDeleteLists glDeleteLists -#define qglDeleteTextures glDeleteTextures -#define qglDepthFunc glDepthFunc -#define qglDepthMask glDepthMask -#define qglDepthRange glDepthRange -#define qglDisable glDisable -#define qglDisableClientState glDisableClientState -#define qglDrawArrays glDrawArrays -#define qglDrawBuffer glDrawBuffer -#define qglDrawElements glDrawElements -#define qglDrawPixels glDrawPixels -#define qglEdgeFlag glEdgeFlag -#define qglEdgeFlagPointer glEdgeFlagPointer -#define qglEdgeFlagv glEdgeFlagv -#define qglEnable glEnable -#define qglEnableClientState glEnableClientState -#define qglEnd glEnd -#define qglEndList glEndList -#define qglEvalCoord1d glEvalCoord1d -#define qglEvalCoord1dv glEvalCoord1dv -#define qglEvalCoord1f glEvalCoord1f -#define qglEvalCoord1fv glEvalCoord1fv -#define qglEvalCoord2d glEvalCoord2d -#define qglEvalCoord2dv glEvalCoord2dv -#define qglEvalCoord2f glEvalCoord2f -#define qglEvalCoord2fv glEvalCoord2fv -#define qglEvalMesh1 glEvalMesh1 -#define qglEvalMesh2 glEvalMesh2 -#define qglEvalPoint1 glEvalPoint1 -#define qglEvalPoint2 glEvalPoint2 -#define qglFeedbackBuffer glFeedbackBuffer -#define qglFinish glFinish -#define qglFlush glFlush -#define qglFogf glFogf -#define qglFogfv glFogfv -#define qglFogi glFogi -#define qglFogiv glFogiv -#define qglFrontFace glFrontFace -#define qglFrustum glFrustum -#define qglGenLists glGenLists -#define qglGenTextures glGenTextures -#define qglGetBooleanv glGetBooleanv -#define qglGetClipPlane glGetClipPlane -#define qglGetDoublev glGetDoublev -#define qglGetError glGetError -#define qglGetFloatv glGetFloatv -#define qglGetIntegerv glGetIntegerv -#define qglGetLightfv glGetLightfv -#define qglGetLightiv glGetLightiv -#define qglGetMapdv glGetMapdv -#define qglGetMapfv glGetMapfv -#define qglGetMapiv glGetMapiv -#define qglGetMaterialfv glGetMaterialfv -#define qglGetMaterialiv glGetMaterialiv -#define qglGetPixelMapfv glGetPixelMapfv -#define qglGetPixelMapuiv glGetPixelMapuiv -#define qglGetPixelMapusv glGetPixelMapusv -#define qglGetPointerv glGetPointerv -#define qglGetPolygonStipple glGetPolygonStipple -#define qglGetString glGetString -#define qglGetTexGendv glGetTexGendv -#define qglGetTexGenfv glGetTexGenfv -#define qglGetTexGeniv glGetTexGeniv -#define qglGetTexImage glGetTexImage -#define qglGetTexLevelParameterfv glGetTexLevelParameterfv -#define qglGetTexLevelParameteriv glGetTexLevelParameteriv -#define qglGetTexParameterfv glGetTexParameterfv -#define qglGetTexParameteriv glGetTexParameteriv -#define qglHint glHint -#define qglIndexMask glIndexMask -#define qglIndexPointer glIndexPointer -#define qglIndexd glIndexd -#define qglIndexdv glIndexdv -#define qglIndexf glIndexf -#define qglIndexfv glIndexfv -#define qglIndexi glIndexi -#define qglIndexiv glIndexiv -#define qglIndexs glIndexs -#define qglIndexsv glIndexsv -#define qglIndexub glIndexub -#define qglIndexubv glIndexubv -#define qglInitNames glInitNames -#define qglInterleavedArrays glInterleavedArrays -#define qglIsEnabled glIsEnabled -#define qglIsList glIsList -#define qglIsTexture glIsTexture -#define qglLightModelf glLightModelf -#define qglLightModelfv glLightModelfv -#define qglLightModeli glLightModeli -#define qglLightModeliv glLightModeliv -#define qglLightf glLightf -#define qglLightfv glLightfv -#define qglLighti glLighti -#define qglLightiv glLightiv -#define qglLineStipple glLineStipple -#define qglLineWidth glLineWidth -#define qglListBase glListBase -#define qglLoadIdentity glLoadIdentity -#define qglLoadMatrixd glLoadMatrixd -#define qglLoadMatrixf glLoadMatrixf -#define qglLoadName glLoadName -#define qglLogicOp glLogicOp -#define qglMap1d glMap1d -#define qglMap1f glMap1f -#define qglMap2d glMap2d -#define qglMap2f glMap2f -#define qglMapGrid1d glMapGrid1d -#define qglMapGrid1f glMapGrid1f -#define qglMapGrid2d glMapGrid2d -#define qglMapGrid2f glMapGrid2f -#define qglMaterialf glMaterialf -#define qglMaterialfv glMaterialfv -#define qglMateriali glMateriali -#define qglMaterialiv glMaterialiv -#define qglMatrixMode glMatrixMode -#define qglMultMatrixd glMultMatrixd -#define qglMultMatrixf glMultMatrixf -#define qglNewList glNewList -#define qglNormal3b glNormal3b -#define qglNormal3bv glNormal3bv -#define qglNormal3d glNormal3d -#define qglNormal3dv glNormal3dv -#define qglNormal3f glNormal3f -#define qglNormal3fv glNormal3fv -#define qglNormal3i glNormal3i -#define qglNormal3iv glNormal3iv -#define qglNormal3s glNormal3s -#define qglNormal3sv glNormal3sv -#define qglNormalPointer glNormalPointer -#define qglOrtho glOrtho -#define qglPassThrough glPassThrough -#define qglPixelMapfv glPixelMapfv -#define qglPixelMapuiv glPixelMapuiv -#define qglPixelMapusv glPixelMapusv -#define qglPixelStoref glPixelStoref -#define qglPixelStorei glPixelStorei -#define qglPixelTransferf glPixelTransferf -#define qglPixelTransferi glPixelTransferi -#define qglPixelZoom glPixelZoom -#define qglPointSize glPointSize -#define qglPolygonMode glPolygonMode -#define qglPolygonOffset glPolygonOffset -#define qglPolygonStipple glPolygonStipple -#define qglPopAttrib glPopAttrib -#define qglPopClientAttrib glPopClientAttrib -#define qglPopMatrix glPopMatrix -#define qglPopName glPopName -#define qglPrioritizeTextures glPrioritizeTextures -#define qglPushAttrib glPushAttrib -#define qglPushClientAttrib glPushClientAttrib -#define qglPushMatrix glPushMatrix -#define qglPushName glPushName -#define qglRasterPos2d glRasterPos2d -#define qglRasterPos2dv glRasterPos2dv -#define qglRasterPos2f glRasterPos2f -#define qglRasterPos2fv glRasterPos2fv -#define qglRasterPos2i glRasterPos2i -#define qglRasterPos2iv glRasterPos2iv -#define qglRasterPos2s glRasterPos2s -#define qglRasterPos2sv glRasterPos2sv -#define qglRasterPos3d glRasterPos3d -#define qglRasterPos3dv glRasterPos3dv -#define qglRasterPos3f glRasterPos3f -#define qglRasterPos3fv glRasterPos3fv -#define qglRasterPos3i glRasterPos3i -#define qglRasterPos3iv glRasterPos3iv -#define qglRasterPos3s glRasterPos3s -#define qglRasterPos3sv glRasterPos3sv -#define qglRasterPos4d glRasterPos4d -#define qglRasterPos4dv glRasterPos4dv -#define qglRasterPos4f glRasterPos4f -#define qglRasterPos4fv glRasterPos4fv -#define qglRasterPos4i glRasterPos4i -#define qglRasterPos4iv glRasterPos4iv -#define qglRasterPos4s glRasterPos4s -#define qglRasterPos4sv glRasterPos4sv -#define qglReadBuffer glReadBuffer -#define qglReadPixels glReadPixels -#define qglRectd glRectd -#define qglRectdv glRectdv -#define qglRectf glRectf -#define qglRectfv glRectfv -#define qglRecti glRecti -#define qglRectiv glRectiv -#define qglRects glRects -#define qglRectsv glRectsv -#define qglRenderMode glRenderMode -#define qglRotated glRotated -#define qglRotatef glRotatef -#define qglScaled glScaled -#define qglScalef glScalef -#define qglScissor glScissor -#define qglSelectBuffer glSelectBuffer -#define qglShadeModel glShadeModel -#define qglStencilFunc glStencilFunc -#define qglStencilMask glStencilMask -#define qglStencilOp glStencilOp -#define qglTexCoord1d glTexCoord1d -#define qglTexCoord1dv glTexCoord1dv -#define qglTexCoord1f glTexCoord1f -#define qglTexCoord1fv glTexCoord1fv -#define qglTexCoord1i glTexCoord1i -#define qglTexCoord1iv glTexCoord1iv -#define qglTexCoord1s glTexCoord1s -#define qglTexCoord1sv glTexCoord1sv -#define qglTexCoord2d glTexCoord2d -#define qglTexCoord2dv glTexCoord2dv -#define qglTexCoord2f glTexCoord2f -#define qglTexCoord2fv glTexCoord2fv -#define qglTexCoord2i glTexCoord2i -#define qglTexCoord2iv glTexCoord2iv -#define qglTexCoord2s glTexCoord2s -#define qglTexCoord2sv glTexCoord2sv -#define qglTexCoord3d glTexCoord3d -#define qglTexCoord3dv glTexCoord3dv -#define qglTexCoord3f glTexCoord3f -#define qglTexCoord3fv glTexCoord3fv -#define qglTexCoord3i glTexCoord3i -#define qglTexCoord3iv glTexCoord3iv -#define qglTexCoord3s glTexCoord3s -#define qglTexCoord3sv glTexCoord3sv -#define qglTexCoord4d glTexCoord4d -#define qglTexCoord4dv glTexCoord4dv -#define qglTexCoord4f glTexCoord4f -#define qglTexCoord4fv glTexCoord4fv -#define qglTexCoord4i glTexCoord4i -#define qglTexCoord4iv glTexCoord4iv -#define qglTexCoord4s glTexCoord4s -#define qglTexCoord4sv glTexCoord4sv -#define qglTexCoordPointer glTexCoordPointer -#define qglTexEnvf glTexEnvf -#define qglTexEnvfv glTexEnvfv -#define qglTexEnvi glTexEnvi -#define qglTexEnviv glTexEnviv -#define qglTexGend glTexGend -#define qglTexGendv glTexGendv -#define qglTexGenf glTexGenf -#define qglTexGenfv glTexGenfv -#define qglTexGeni glTexGeni -#define qglTexGeniv glTexGeniv -#define qglTexImage1D glTexImage1D -#define qglTexImage2D glTexImage2D -#define qglTexParameterf glTexParameterf -#define qglTexParameterfv glTexParameterfv -#define qglTexParameteri glTexParameteri -#define qglTexParameteriv glTexParameteriv -#define qglTexSubImage1D glTexSubImage1D -#define qglTexSubImage2D glTexSubImage2D -#define qglTranslated glTranslated -#define qglTranslatef glTranslatef -#define qglVertex2d glVertex2d -#define qglVertex2dv glVertex2dv -#define qglVertex2f glVertex2f -#define qglVertex2fv glVertex2fv -#define qglVertex2i glVertex2i -#define qglVertex2iv glVertex2iv -#define qglVertex2s glVertex2s -#define qglVertex2sv glVertex2sv -#define qglVertex3d glVertex3d -#define qglVertex3dv glVertex3dv -#define qglVertex3f glVertex3f -#define qglVertex3fv glVertex3fv -#define qglVertex3i glVertex3i -#define qglVertex3iv glVertex3iv -#define qglVertex3s glVertex3s -#define qglVertex3sv glVertex3sv -#define qglVertex4d glVertex4d -#define qglVertex4dv glVertex4dv -#define qglVertex4f glVertex4f -#define qglVertex4fv glVertex4fv -#define qglVertex4i glVertex4i -#define qglVertex4iv glVertex4iv -#define qglVertex4s glVertex4s -#define qglVertex4sv glVertex4sv -#define qglVertexPointer glVertexPointer -#define qglViewport glViewport - diff --git a/code/renderer/ref_trin.def b/code/renderer/ref_trin.def deleted file mode 100644 index 2fefbd3..0000000 --- a/code/renderer/ref_trin.def +++ /dev/null @@ -1,2 +0,0 @@ -EXPORTS - GetRefAPI diff --git a/code/renderer/renderer.dsp b/code/renderer/renderer.dsp deleted file mode 100644 index 5ce419f..0000000 --- a/code/renderer/renderer.dsp +++ /dev/null @@ -1,777 +0,0 @@ -# Microsoft Developer Studio Project File - Name="renderer" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=renderer - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "renderer.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "renderer.mak" CFG="renderer - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "renderer - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "renderer - Win32 Debug" (based on "Win32 (x86) Static Library") -!MESSAGE "renderer - Win32 FinalBuild" (based on "Win32 (x86) Static Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName ""$/Code/renderer", NCMAAAAA" -# PROP Scc_LocalPath "." -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "renderer - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "..\Release" -# PROP Intermediate_Dir "..\Release\renderer" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /G6 /MT /W4 /GX /Zi /O2 /Ob2 /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "__USEA3D" /D "__A3D_GEOM" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo - -!ELSEIF "$(CFG)" == "renderer - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "..\Debug" -# PROP Intermediate_Dir "..\Debug\renderer" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /G6 /MTd /W3 /Gm /Gi /GX /ZI /Od /I "..\ICARUS" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "__USEA3D" /D "__A3D_GEOM" /FR /YX /FD /GZ /c -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo - -!ELSEIF "$(CFG)" == "renderer - Win32 FinalBuild" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "renderer___Win32_FinalBuild" -# PROP BASE Intermediate_Dir "renderer___Win32_FinalBuild" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "..\FinalBuild" -# PROP Intermediate_Dir "..\FinalBuild\renderer" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /G6 /MT /W4 /GX /Zi /O2 /Ob2 /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "__USEA3D" /D "__A3D_GEOM" /YX /FD /c -# ADD CPP /nologo /G6 /MT /W4 /GX /Zi /O2 /Ob2 /D "_MBCS" /D "_LIB" /D "__USEA3D" /D "__A3D_GEOM" /D "WIN32" /D "NDEBUG" /D "FINAL_BUILD" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo - -!ENDIF - -# Begin Target - -# Name "renderer - Win32 Release" -# Name "renderer - Win32 Debug" -# Name "renderer - Win32 FinalBuild" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\MatComp.c -# End Source File -# Begin Source File - -SOURCE=.\tr_animation.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_backend.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_bsp.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_cmds.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_curve.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_draw.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_ghoul2.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_image.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_init.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_jpeg_interface.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_light.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_main.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_marks.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_mesh.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_model.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_noise.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_scene.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_shade.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_shade_calc.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_shader.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_shadows.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_sky.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_stl.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_surface.cpp -# End Source File -# Begin Source File - -SOURCE=.\tr_world.cpp -# End Source File -# Begin Source File - -SOURCE=..\win32\win_gamma.cpp -# End Source File -# Begin Source File - -SOURCE=..\win32\win_glimp.cpp -# End Source File -# Begin Source File - -SOURCE=..\win32\win_qgl.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\amd3d.h -# End Source File -# Begin Source File - -SOURCE=..\game\channels.h -# End Source File -# Begin Source File - -SOURCE=..\qcommon\cm_public.h -# End Source File -# Begin Source File - -SOURCE=..\game\ghoul2_shared.h -# End Source File -# Begin Source File - -SOURCE=.\glext.h -# End Source File -# Begin Source File - -SOURCE=..\win32\glw_win.h -# End Source File -# Begin Source File - -SOURCE=.\MatComp.h -# End Source File -# Begin Source File - -SOURCE=.\mdx_format.h -# End Source File -# Begin Source File - -SOURCE=..\game\q_shared.h -# End Source File -# Begin Source File - -SOURCE=..\qcommon\qcommon.h -# End Source File -# Begin Source File - -SOURCE=..\qcommon\qfiles.h -# End Source File -# Begin Source File - -SOURCE=.\qgl.h -# End Source File -# Begin Source File - -SOURCE=..\game\surfaceflags.h -# End Source File -# Begin Source File - -SOURCE=.\tr_jpeg_interface.h -# End Source File -# Begin Source File - -SOURCE=.\tr_local.h -# End Source File -# Begin Source File - -SOURCE=.\tr_public.h -# End Source File -# Begin Source File - -SOURCE=.\tr_stl.h -# End Source File -# Begin Source File - -SOURCE=.\tr_types.h -# End Source File -# Begin Source File - -SOURCE=..\win32\win_local.h -# End Source File -# End Group -# Begin Group "JPEG Source" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE=..\jpeg6\jcomapi.c - -!IF "$(CFG)" == "renderer - Win32 Release" - -# ADD CPP /W3 - -!ELSEIF "$(CFG)" == "renderer - Win32 Debug" - -# ADD CPP /W3 -# SUBTRACT CPP /YX - -!ELSEIF "$(CFG)" == "renderer - Win32 FinalBuild" - -# ADD BASE CPP /W3 -# ADD CPP /W3 - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jdapimin.c - -!IF "$(CFG)" == "renderer - Win32 Release" - -# ADD CPP /W3 - -!ELSEIF "$(CFG)" == "renderer - Win32 Debug" - -# ADD CPP /W3 -# SUBTRACT CPP /YX - -!ELSEIF "$(CFG)" == "renderer - Win32 FinalBuild" - -# ADD BASE CPP /W3 -# ADD CPP /W3 - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jdapistd.c - -!IF "$(CFG)" == "renderer - Win32 Release" - -# ADD CPP /W3 - -!ELSEIF "$(CFG)" == "renderer - Win32 Debug" - -# ADD CPP /W3 -# SUBTRACT CPP /YX - -!ELSEIF "$(CFG)" == "renderer - Win32 FinalBuild" - -# ADD BASE CPP /W3 -# ADD CPP /W3 - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jdatasrc.c - -!IF "$(CFG)" == "renderer - Win32 Release" - -# ADD CPP /W3 - -!ELSEIF "$(CFG)" == "renderer - Win32 Debug" - -# ADD CPP /W3 -# SUBTRACT CPP /YX - -!ELSEIF "$(CFG)" == "renderer - Win32 FinalBuild" - -# ADD BASE CPP /W3 -# ADD CPP /W3 - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jdcoefct.c - -!IF "$(CFG)" == "renderer - Win32 Release" - -# ADD CPP /W3 - -!ELSEIF "$(CFG)" == "renderer - Win32 Debug" - -# ADD CPP /W3 -# SUBTRACT CPP /YX - -!ELSEIF "$(CFG)" == "renderer - Win32 FinalBuild" - -# ADD BASE CPP /W3 -# ADD CPP /W3 - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jdcolor.c - -!IF "$(CFG)" == "renderer - Win32 Release" - -# ADD CPP /W3 - -!ELSEIF "$(CFG)" == "renderer - Win32 Debug" - -# ADD CPP /W3 -# SUBTRACT CPP /YX - -!ELSEIF "$(CFG)" == "renderer - Win32 FinalBuild" - -# ADD BASE CPP /W3 -# ADD CPP /W3 - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jddctmgr.c - -!IF "$(CFG)" == "renderer - Win32 Release" - -# ADD CPP /W3 - -!ELSEIF "$(CFG)" == "renderer - Win32 Debug" - -# ADD CPP /W3 -# SUBTRACT CPP /YX - -!ELSEIF "$(CFG)" == "renderer - Win32 FinalBuild" - -# ADD BASE CPP /W3 -# ADD CPP /W3 - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jdhuff.c - -!IF "$(CFG)" == "renderer - Win32 Release" - -# ADD CPP /W3 - -!ELSEIF "$(CFG)" == "renderer - Win32 Debug" - -# ADD CPP /W3 -# SUBTRACT CPP /YX - -!ELSEIF "$(CFG)" == "renderer - Win32 FinalBuild" - -# ADD BASE CPP /W3 -# ADD CPP /W3 - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jdinput.c - -!IF "$(CFG)" == "renderer - Win32 Release" - -# ADD CPP /W3 - -!ELSEIF "$(CFG)" == "renderer - Win32 Debug" - -# ADD CPP /W3 -# SUBTRACT CPP /YX - -!ELSEIF "$(CFG)" == "renderer - Win32 FinalBuild" - -# ADD BASE CPP /W3 -# ADD CPP /W3 - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jdmainct.c - -!IF "$(CFG)" == "renderer - Win32 Release" - -# ADD CPP /W3 - -!ELSEIF "$(CFG)" == "renderer - Win32 Debug" - -# ADD CPP /W3 -# SUBTRACT CPP /YX - -!ELSEIF "$(CFG)" == "renderer - Win32 FinalBuild" - -# ADD BASE CPP /W3 -# ADD CPP /W3 - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jdmarker.c - -!IF "$(CFG)" == "renderer - Win32 Release" - -# ADD CPP /W3 - -!ELSEIF "$(CFG)" == "renderer - Win32 Debug" - -# ADD CPP /W3 -# SUBTRACT CPP /YX - -!ELSEIF "$(CFG)" == "renderer - Win32 FinalBuild" - -# ADD BASE CPP /W3 -# ADD CPP /W3 - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jdmaster.c - -!IF "$(CFG)" == "renderer - Win32 Release" - -# ADD CPP /W3 - -!ELSEIF "$(CFG)" == "renderer - Win32 Debug" - -# ADD CPP /W3 -# SUBTRACT CPP /YX - -!ELSEIF "$(CFG)" == "renderer - Win32 FinalBuild" - -# ADD BASE CPP /W3 -# ADD CPP /W3 - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jdpostct.c - -!IF "$(CFG)" == "renderer - Win32 Release" - -# ADD CPP /W3 - -!ELSEIF "$(CFG)" == "renderer - Win32 Debug" - -# ADD CPP /W3 -# SUBTRACT CPP /YX - -!ELSEIF "$(CFG)" == "renderer - Win32 FinalBuild" - -# ADD BASE CPP /W3 -# ADD CPP /W3 - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jdsample.c - -!IF "$(CFG)" == "renderer - Win32 Release" - -# ADD CPP /W3 - -!ELSEIF "$(CFG)" == "renderer - Win32 Debug" - -# ADD CPP /W3 -# SUBTRACT CPP /YX - -!ELSEIF "$(CFG)" == "renderer - Win32 FinalBuild" - -# ADD BASE CPP /W3 -# ADD CPP /W3 - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jdtrans.c - -!IF "$(CFG)" == "renderer - Win32 Release" - -# ADD CPP /W3 - -!ELSEIF "$(CFG)" == "renderer - Win32 Debug" - -# ADD CPP /W3 -# SUBTRACT CPP /YX - -!ELSEIF "$(CFG)" == "renderer - Win32 FinalBuild" - -# ADD BASE CPP /W3 -# ADD CPP /W3 - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jerror.c - -!IF "$(CFG)" == "renderer - Win32 Release" - -# ADD CPP /W3 - -!ELSEIF "$(CFG)" == "renderer - Win32 Debug" - -# ADD CPP /W3 -# SUBTRACT CPP /YX - -!ELSEIF "$(CFG)" == "renderer - Win32 FinalBuild" - -# ADD BASE CPP /W3 -# ADD CPP /W3 - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jidctflt.c - -!IF "$(CFG)" == "renderer - Win32 Release" - -# ADD CPP /W3 - -!ELSEIF "$(CFG)" == "renderer - Win32 Debug" - -# ADD CPP /W3 -# SUBTRACT CPP /YX - -!ELSEIF "$(CFG)" == "renderer - Win32 FinalBuild" - -# ADD BASE CPP /W3 -# ADD CPP /W3 - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jmemmgr.c - -!IF "$(CFG)" == "renderer - Win32 Release" - -# ADD CPP /W3 - -!ELSEIF "$(CFG)" == "renderer - Win32 Debug" - -# ADD CPP /W3 -# SUBTRACT CPP /YX - -!ELSEIF "$(CFG)" == "renderer - Win32 FinalBuild" - -# ADD BASE CPP /W3 -# ADD CPP /W3 - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jmemnobs.c - -!IF "$(CFG)" == "renderer - Win32 Release" - -# ADD CPP /W3 - -!ELSEIF "$(CFG)" == "renderer - Win32 Debug" - -# ADD CPP /W3 -# SUBTRACT CPP /YX - -!ELSEIF "$(CFG)" == "renderer - Win32 FinalBuild" - -# ADD BASE CPP /W3 -# ADD CPP /W3 - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jutils.c - -!IF "$(CFG)" == "renderer - Win32 Release" - -# ADD CPP /W3 - -!ELSEIF "$(CFG)" == "renderer - Win32 Debug" - -# ADD CPP /W3 -# SUBTRACT CPP /YX - -!ELSEIF "$(CFG)" == "renderer - Win32 FinalBuild" - -# ADD BASE CPP /W3 -# ADD CPP /W3 - -!ENDIF - -# End Source File -# End Group -# Begin Group "JPEG Headers" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE=..\jpeg6\jconfig.h -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jdct.h -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jdhuff.h -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jerror.h -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jinclude.h -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jmemsys.h -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jmorecfg.h -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jpegint.h -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jpeglib.h -# End Source File -# Begin Source File - -SOURCE=..\jpeg6\jversion.h -# End Source File -# End Group -# End Target -# End Project diff --git a/code/renderer/tr_backend.cpp b/code/renderer/tr_backend.cpp index b66bb58..d8104f1 100644 --- a/code/renderer/tr_backend.cpp +++ b/code/renderer/tr_backend.cpp @@ -1110,8 +1110,6 @@ const void *RB_SwapBuffers( const void *data ) { RB_ShowImages(); } - RB_RenderWorldEffects(); - cmd = (const swapBuffersCommand_t *)data; // we measure overdraw by reading back the stencil buffer and @@ -1146,6 +1144,27 @@ const void *RB_SwapBuffers( const void *data ) { return (const void *)(cmd + 1); } +const void *RB_WorldEffects( const void *data ) +{ + const setModeCommand_t *cmd; + + cmd = (const setModeCommand_t *)data; + + // Always flush the tess buffer + if ( tess.shader && tess.numIndexes ) + { + RB_EndSurface(); + } + RB_RenderWorldEffects(); + + if(tess.shader) + { + RB_BeginSurface( tess.shader, tess.fogNum ); + } + + return (const void *)(cmd + 1); +} + /* ==================== RB_ExecuteRenderCommands @@ -1191,7 +1210,9 @@ void RB_ExecuteRenderCommands( const void *data ) { case RC_SWAP_BUFFERS: data = RB_SwapBuffers( data ); break; - + case RC_WORLD_EFFECTS: + data = RB_WorldEffects( data ); + break; case RC_END_OF_LIST: default: // stop rendering on this thread diff --git a/code/renderer/tr_cmds.cpp b/code/renderer/tr_cmds.cpp index 726bfe2..130a30e 100644 --- a/code/renderer/tr_cmds.cpp +++ b/code/renderer/tr_cmds.cpp @@ -350,6 +350,17 @@ void RE_LAGoggles( void ) fog->tcScale = 2.0f / ( fog->parms.depthForOpaque * (1.0f + cos( tr.refdef.floatTime) * 0.1f)); } +void RE_RenderWorldEffects(void) +{ + setModeCommand_t *cmd; + + cmd = (setModeCommand_t *)R_GetCommandBuffer( sizeof( *cmd ) ); + if ( !cmd ) { + return; + } + cmd->commandId = RC_WORLD_EFFECTS; +} + /* ============= RE_Scissor diff --git a/code/renderer/tr_flares.cpp b/code/renderer/tr_flares.cpp deleted file mode 100644 index a8e9140..0000000 --- a/code/renderer/tr_flares.cpp +++ /dev/null @@ -1,427 +0,0 @@ -// tr_flares.c - -#include "tr_local.h" - -/* -============================================================================= - -LIGHT FLARES - -A light flare is an effect that takes place inside the eye when bright light -sources are visible. The size of the flare reletive to the screen is nearly -constant, irrespective of distance, but the intensity should be proportional to the -projected area of the light source. - -A surface that has been flagged as having a light flare will calculate the depth -buffer value that it's midpoint should have when the surface is added. - -After all opaque surfaces have been rendered, the depth buffer is read back for -each flare in view. If the point has not been obscured by a closer surface, the -flare should be drawn. - -Surfaces that have a repeated texture should never be flagged as flaring, because -there will only be a single flare added at the midpoint of the polygon. - -To prevent abrupt popping, the intensity of the flare is interpolated up and -down as it changes visibility. This involves scene to scene state, unlike almost -all other aspects of the renderer, and is complicated by the fact that a single -frame may have multiple scenes. - -RB_RenderFlares() will be called once per view (twice in a mirrored scene, potentially -up to five or more times in a frame with 3D status bar icons). - -============================================================================= -*/ - - -// flare states maintain visibility over multiple frames for fading -// layers: view, mirror, menu -typedef struct flare_s { - struct flare_s *next; // for active chain - - int addedFrame; - - qboolean inPortal; // true if in a portal view of the scene - int frameSceneNum; - void *surface; - int fogNum; - - int fadeTime; - - qboolean visible; // state of last test - float drawIntensity; // may be non 0 even if !visible due to fading - float lightScale; - int windowX, windowY; - float eyeZ; - - vec3_t color; -} flare_t; - -#define MAX_FLARES 128 - -flare_t r_flareStructs[MAX_FLARES]; -flare_t *r_activeFlares, *r_inactiveFlares; - -/* -================== -R_ClearFlares -================== -*/ -void R_ClearFlares( void ) { - int i; - - memset( r_flareStructs, 0, sizeof( r_flareStructs ) ); - r_activeFlares = NULL; - r_inactiveFlares = NULL; - - for ( i = 0 ; i < MAX_FLARES ; i++ ) { - r_flareStructs[i].next = r_inactiveFlares; - r_inactiveFlares = &r_flareStructs[i]; - } -} - - -/* -================== -RB_AddFlare - -This is called at surface tesselation time -================== -*/ -void RB_AddFlare( void *surface, int fogNum, vec3_t point, vec3_t color, vec3_t normal, float lightScale) { - int i; - flare_t *f, *oldest; - vec3_t local; - float d; - vec4_t eye, clip, normalized, window; - - backEnd.pc.c_flareAdds++; - - // if the point is off the screen, don't bother adding it - // calculate screen coordinates and depth - R_TransformModelToClip( point, backEnd.or.modelMatrix, - backEnd.viewParms.projectionMatrix, eye, clip ); - - // check to see if the point is completely off screen - for ( i = 0 ; i < 3 ; i++ ) { - if ( clip[i] >= clip[3] || clip[i] <= -clip[3] ) { - return; - } - } - - R_TransformClipToWindow( clip, &backEnd.viewParms, normalized, window ); - - if ( window[0] < 0 || window[0] >= backEnd.viewParms.viewportWidth - || window[1] < 0 || window[1] >= backEnd.viewParms.viewportHeight ) { - return; // shouldn't happen, since we check the clip[] above, except for FP rounding - } - - // see if a flare with a matching surface, scene, and view exists - oldest = r_flareStructs; - for ( f = r_activeFlares ; f ; f = f->next ) { - if ( f->surface == surface && f->frameSceneNum == backEnd.viewParms.frameSceneNum - && f->inPortal == backEnd.viewParms.isPortal ) { - break; - } - } - - // allocate a new one - if (!f ) { - if ( !r_inactiveFlares ) { - // the list is completely full - return; - } - f = r_inactiveFlares; - r_inactiveFlares = r_inactiveFlares->next; - f->next = r_activeFlares; - r_activeFlares = f; - - f->surface = surface; - f->frameSceneNum = backEnd.viewParms.frameSceneNum; - f->inPortal = backEnd.viewParms.isPortal; - f->addedFrame = -1; - } - - if ( f->addedFrame != backEnd.viewParms.frameCount - 1 ) { - f->visible = qfalse; - f->fadeTime = backEnd.refdef.time - 2000; - } - - f->addedFrame = backEnd.viewParms.frameCount; - f->fogNum = fogNum; - f->lightScale = lightScale; - - VectorCopy( color, f->color ); - - // fade the intensity of the flare down as the - // light surface turns away from the viewer - if ( normal ) { - VectorSubtract( backEnd.viewParms.or.origin, point, local ); - VectorNormalizeFast( local ); - d = DotProduct( local, normal ); - VectorScale( f->color, d, f->color ); - } - - // save info needed to test - f->windowX = backEnd.viewParms.viewportX + window[0]; - f->windowY = backEnd.viewParms.viewportY + window[1]; - - f->eyeZ = eye[2]; -} - -/* -================== -RB_AddDlightFlares -================== -*/ -void RB_AddDlightFlares( void ) { - dlight_t *l; - int i, j, k; - fog_t *fog; - - if ( !r_flares->integer ) { - return; - } - - l = backEnd.refdef.dlights; - fog = tr.world->fogs; - for (i=0 ; inumfogs ; j++ ) { - fog = &tr.world->fogs[j]; - for ( k = 0 ; k < 3 ; k++ ) { - if ( l->origin[k] < fog->bounds[0][k] || l->origin[k] > fog->bounds[1][k] ) { - break; - } - } - if ( k == 3 ) { - break; - } - } - if ( j == tr.world->numfogs ) { - j = 0; - } - - RB_AddFlare( (void *)l, j, l->origin, l->color, NULL, 1.0f ); - } -} - -/* -=============================================================================== - -FLARE BACK END - -=============================================================================== -*/ - -/* -================== -RB_TestFlare -================== -*/ -void RB_TestFlare( flare_t *f ) { - float depth; - qboolean visible; - float fade; - float screenZ; - - backEnd.pc.c_flareTests++; - - // doing a readpixels is as good as doing a glFinish(), so - // don't bother with another sync - glState.finishCalled = qfalse; - - // read back the z buffer contents - qglReadPixels( f->windowX, f->windowY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth ); - - screenZ = backEnd.viewParms.projectionMatrix[14] / - ( ( 2*depth - 1 ) * backEnd.viewParms.projectionMatrix[11] - backEnd.viewParms.projectionMatrix[10] ); - - visible = ( -f->eyeZ - -screenZ ) < 24; - - if ( visible ) { - if ( !f->visible ) { - f->visible = qtrue; - f->fadeTime = backEnd.refdef.time - 1; - } - fade = ( ( backEnd.refdef.time - f->fadeTime ) /1000.0f ) * r_flareFade->value; - } else { - if ( f->visible ) { - f->visible = qfalse; - f->fadeTime = backEnd.refdef.time - 1; - } - fade = 1.0 - ( ( backEnd.refdef.time - f->fadeTime ) / 1000.0f ) * r_flareFade->value; - } - - if ( fade < 0 ) { - fade = 0; - } - if ( fade > 1 ) { - fade = 1; - } - - f->drawIntensity = fade; -} - - -/* -================== -RB_RenderFlare -================== -*/ -void RB_RenderFlare( flare_t *f ) { - float size; - vec3_t color; - int iColor[3]; - - backEnd.pc.c_flareRenders++; - - VectorScale( f->color, f->drawIntensity*tr.identityLight, color ); - iColor[0] = color[0] * 255; - iColor[1] = color[1] * 255; - iColor[2] = color[2] * 255; - - size = f->lightScale * backEnd.viewParms.viewportWidth * ( r_flareSize->value/640.0 + 8 / -f->eyeZ ); - - RB_BeginSurface( tr.flareShader, f->fogNum ); - - // FIXME: use quadstamp? - tess.xyz[tess.numVertexes][0] = f->windowX - size; - tess.xyz[tess.numVertexes][1] = f->windowY - size; - tess.texCoords[tess.numVertexes][0][0] = 0; - tess.texCoords[tess.numVertexes][0][1] = 0; - tess.vertexColors[tess.numVertexes][0] = iColor[0]; - tess.vertexColors[tess.numVertexes][1] = iColor[1]; - tess.vertexColors[tess.numVertexes][2] = iColor[2]; - tess.vertexColors[tess.numVertexes][3] = 255; - tess.numVertexes++; - - tess.xyz[tess.numVertexes][0] = f->windowX - size; - tess.xyz[tess.numVertexes][1] = f->windowY + size; - tess.texCoords[tess.numVertexes][0][0] = 0; - tess.texCoords[tess.numVertexes][0][1] = 1; - tess.vertexColors[tess.numVertexes][0] = iColor[0]; - tess.vertexColors[tess.numVertexes][1] = iColor[1]; - tess.vertexColors[tess.numVertexes][2] = iColor[2]; - tess.vertexColors[tess.numVertexes][3] = 255; - tess.numVertexes++; - - tess.xyz[tess.numVertexes][0] = f->windowX + size; - tess.xyz[tess.numVertexes][1] = f->windowY + size; - tess.texCoords[tess.numVertexes][0][0] = 1; - tess.texCoords[tess.numVertexes][0][1] = 1; - tess.vertexColors[tess.numVertexes][0] = iColor[0]; - tess.vertexColors[tess.numVertexes][1] = iColor[1]; - tess.vertexColors[tess.numVertexes][2] = iColor[2]; - tess.vertexColors[tess.numVertexes][3] = 255; - tess.numVertexes++; - - tess.xyz[tess.numVertexes][0] = f->windowX + size; - tess.xyz[tess.numVertexes][1] = f->windowY - size; - tess.texCoords[tess.numVertexes][0][0] = 1; - tess.texCoords[tess.numVertexes][0][1] = 0; - tess.vertexColors[tess.numVertexes][0] = iColor[0]; - tess.vertexColors[tess.numVertexes][1] = iColor[1]; - tess.vertexColors[tess.numVertexes][2] = iColor[2]; - tess.vertexColors[tess.numVertexes][3] = 255; - tess.numVertexes++; - - tess.indexes[tess.numIndexes++] = 0; - tess.indexes[tess.numIndexes++] = 1; - tess.indexes[tess.numIndexes++] = 2; - tess.indexes[tess.numIndexes++] = 0; - tess.indexes[tess.numIndexes++] = 2; - tess.indexes[tess.numIndexes++] = 3; - - RB_EndSurface(); -} - -/* -================== -RB_RenderFlares - -Because flares are simulating an occular effect, they should be drawn after -everything (all views) in the entire frame has been drawn. - -Because of the way portals use the depth buffer to mark off areas, the -needed information would be lost after each view, so we are forced to draw -flares after each view. - -The resulting artifact is that flares in mirrors or portals don't dim properly -when occluded by something in the main view, and portal flares that should -extend past the portal edge will be overwritten. -================== -*/ -void RB_RenderFlares (void) { - flare_t *f; - flare_t **prev; - qboolean draw; - - if ( !r_flares->integer ) { - return; - } - -// RB_AddDlightFlares(); - - // perform z buffer readback on each flare in this view - draw = qfalse; - prev = &r_activeFlares; - while ( ( f = *prev ) != NULL ) { - // throw out any flares that weren't added last frame - if ( f->addedFrame < backEnd.viewParms.frameCount - 1 ) { - *prev = f->next; - f->next = r_inactiveFlares; - r_inactiveFlares = f; - continue; - } - - // don't draw any here that aren't from this scene / portal - f->drawIntensity = 0; - if ( f->frameSceneNum == backEnd.viewParms.frameSceneNum - && f->inPortal == backEnd.viewParms.isPortal ) { - RB_TestFlare( f ); - if ( f->drawIntensity ) { - draw = qtrue; - } else { - // this flare has completely faded out, so remove it from the chain - *prev = f->next; - f->next = r_inactiveFlares; - r_inactiveFlares = f; - continue; - } - } - - prev = &f->next; - } - - if ( !draw ) { - return; // none visible - } - - if ( backEnd.viewParms.isPortal ) { - qglDisable (GL_CLIP_PLANE0); - } - - qglPushMatrix(); - qglLoadIdentity(); - qglMatrixMode( GL_PROJECTION ); - qglPushMatrix(); - qglLoadIdentity(); - qglOrtho( backEnd.viewParms.viewportX, backEnd.viewParms.viewportX + backEnd.viewParms.viewportWidth, - backEnd.viewParms.viewportY, backEnd.viewParms.viewportY + backEnd.viewParms.viewportHeight, - -99999, 99999 ); - - for ( f = r_activeFlares ; f ; f = f->next ) { - if ( f->frameSceneNum == backEnd.viewParms.frameSceneNum - && f->inPortal == backEnd.viewParms.isPortal - && f->drawIntensity ) { - RB_RenderFlare( f ); - } - } - - qglPopMatrix(); - qglMatrixMode( GL_MODELVIEW ); - qglPopMatrix(); -} - diff --git a/code/renderer/tr_font.cpp b/code/renderer/tr_font.cpp index 06e6e11..ce58efe 100644 --- a/code/renderer/tr_font.cpp +++ b/code/renderer/tr_font.cpp @@ -395,15 +395,14 @@ CFontInfo::CFontInfo(const char *fontName) char sTemp[MAX_QPATH]; int iGlyphTPs = 0; const char *psLang = NULL; - int iAsianGlyphsAcross; for (int iLang=0; iLang<3; iLang++) { switch (iLang) { - case 0: iAsianGlyphsAcross = Korean_InitFields (iGlyphTPs, psLang); break; - case 1: iAsianGlyphsAcross = Taiwanese_InitFields (iGlyphTPs, psLang); break; - case 2: iAsianGlyphsAcross = Japanese_InitFields (iGlyphTPs, psLang); break; + case 0: m_iAsianGlyphsAcross = Korean_InitFields (iGlyphTPs, psLang); break; + case 1: m_iAsianGlyphsAcross = Taiwanese_InitFields (iGlyphTPs, psLang); break; + case 2: m_iAsianGlyphsAcross = Japanese_InitFields (iGlyphTPs, psLang); break; } for (int i=0; i=0&&index mBones; @@ -121,7 +122,7 @@ public: int mLastTime; int mWraithID; // this is just used for debug prints, can use it for any int of interest in JK2 - CBoneCache(void *amod,const mdxaHeader_t *aheader) : + CBoneCache(const model_t *amod,const mdxaHeader_t *aheader) : mod(amod), header(aheader) { @@ -157,48 +158,64 @@ public: assert(mBones.size()); return mBones[0]; } - const mdxaBone_t &Eval(int index) + const mdxaBone_t &EvalUnsmooth(int index) { EvalLow(index); + if (mSmoothingActive&&mSmoothBones[index].touch) + { + return mSmoothBones[index].boneMatrix; + } return mFinalBones[index].boneMatrix; } - const mdxaBone_t &EvalRender(int index) + const mdxaBone_t &Eval(int index) { - EvalLow(index); + bool wasEval=EvalLow(index); if (mSmoothingActive) { - if (mSmoothBones[index].touch!=mCurrentTouch) + if (mSmoothBones[index].touch!=incomingTime||wasEval) { - if (mSmoothBones[index].touch==mLastTouch) + float dif=float(incomingTime)-float(mSmoothBones[index].touch); + if (mSmoothBones[index].touch&&dif<300.0f) { + + if (dif<16.0f) // 60 fps + { + dif=16.0f; + } + if (dif>100.0f) // 10 fps + { + dif=100.0f; + } + float f=1.0f-pow(1.0f-mSmoothFactor,16.0f/dif); + int i; float *oldM=&mSmoothBones[index].boneMatrix.matrix[0][0]; float *newM=&mFinalBones[index].boneMatrix.matrix[0][0]; for (i=0;i<12;i++,oldM++,newM++) { - *oldM=mSmoothFactor*(*oldM-*newM)+*newM; + *oldM=f*(*oldM-*newM)+*newM; + } + if (mUnsquash) + { + mdxaBone_t tempMatrix; + Multiply_3x4Matrix(&tempMatrix,&mSmoothBones[index].boneMatrix, &mSkels[index]->BasePoseMat); + float maxl; + maxl=VectorLength(&mSkels[index]->BasePoseMat.matrix[0][0]); + VectorNormalize(&tempMatrix.matrix[0][0]); + VectorNormalize(&tempMatrix.matrix[1][0]); + VectorNormalize(&tempMatrix.matrix[2][0]); + + VectorScale(&tempMatrix.matrix[0][0],maxl,&tempMatrix.matrix[0][0]); + VectorScale(&tempMatrix.matrix[1][0],maxl,&tempMatrix.matrix[1][0]); + VectorScale(&tempMatrix.matrix[2][0],maxl,&tempMatrix.matrix[2][0]); + Multiply_3x4Matrix(&mSmoothBones[index].boneMatrix,&tempMatrix,&mSkels[index]->BasePoseMatInv); } } else { memcpy(&mSmoothBones[index].boneMatrix,&mFinalBones[index].boneMatrix,sizeof(mdxaBone_t)); } - if (mUnsquash) - { - mdxaBone_t tempMatrix; - Multiply_3x4Matrix(&tempMatrix,&mSmoothBones[index].boneMatrix, &mSkels[index]->BasePoseMat); - float maxl; - maxl=VectorLength(&mSkels[index]->BasePoseMat.matrix[0][0]); - VectorNormalize(&tempMatrix.matrix[0][0]); - VectorNormalize(&tempMatrix.matrix[1][0]); - VectorNormalize(&tempMatrix.matrix[2][0]); - - VectorScale(&tempMatrix.matrix[0][0],maxl,&tempMatrix.matrix[0][0]); - VectorScale(&tempMatrix.matrix[1][0],maxl,&tempMatrix.matrix[1][0]); - VectorScale(&tempMatrix.matrix[2][0],maxl,&tempMatrix.matrix[2][0]); - Multiply_3x4Matrix(&mSmoothBones[index].boneMatrix,&tempMatrix,&mSkels[index]->BasePoseMatInv); - } - mSmoothBones[index].touch=mCurrentTouch; + mSmoothBones[index].touch=incomingTime; } return mSmoothBones[index].boneMatrix; } @@ -213,6 +230,7 @@ void RemoveBoneCache(CBoneCache *boneCache) const mdxaBone_t &EvalBoneCache(int index,CBoneCache *boneCache) { + assert(boneCache); return boneCache->Eval(index); } @@ -464,23 +482,26 @@ void Multiply_3x4Matrix(mdxaBone_t *out,const mdxaBone_t *in2,const mdxaBone_t out->matrix[2][3] = (in2->matrix[2][0] * in->matrix[0][3]) + (in2->matrix[2][1] * in->matrix[1][3]) + (in2->matrix[2][2] * in->matrix[2][3]) + in2->matrix[2][3]; } -static inline void UnCompressBone(float mat[3][4], int iBonePoolIndex, const mdxaHeader_t *pMDXAHeader) +static int G2_GetBonePoolIndex( const mdxaHeader_t *pMDXAHeader, int iFrame, int iBone) { - if (pMDXAHeader->version == MDXA_VERSION) - { - const mdxaCompBone_t * const pCompBonePool = (mdxaCompBone_t *) ((byte *)pMDXAHeader + pMDXAHeader->ofsCompBonePool); - MC_UnCompress(mat, pCompBonePool[iBonePoolIndex].Comp); - } - else - { - // quat... - // - const mdxaCompQuatBone_t * const pCompBonePool = (mdxaCompQuatBone_t *) ((byte *)pMDXAHeader + pMDXAHeader->ofsCompBonePool); - MC_UnCompressQuat(mat, (unsigned short*)pCompBonePool[iBonePoolIndex].Comp); - } + assert(iFrame>=0&&iFramenumFrames); + assert(iBone>=0&&iBonenumBones); + const int iOffsetToIndex = (iFrame * pMDXAHeader->numBones * 3) + (iBone * 3); + + mdxaIndex_t *pIndex = (mdxaIndex_t *) ((byte*) pMDXAHeader + pMDXAHeader->ofsFrames + iOffsetToIndex); + + return pIndex->iIndex & 0x00FFFFFF; // this will cause problems for big-endian machines... ;-) } +/*static inline*/ void UnCompressBone(float mat[3][4], int iBoneIndex, const mdxaHeader_t *pMDXAHeader, int iFrame) +{ + mdxaCompQuatBone_t *pCompBonePool = (mdxaCompQuatBone_t *) ((byte *)pMDXAHeader + pMDXAHeader->ofsCompBonePool); + MC_UnCompressQuat(mat, pCompBonePool[ G2_GetBonePoolIndex( pMDXAHeader, iFrame, iBoneIndex ) ].Comp); +} + + + #define DEBUG_G2_TIMING (0) void G2_TimingModel(boneInfo_t &bone,int currentTime,int numFramesInFile,int ¤tFrame,int &newFrame,float &lerp) @@ -714,10 +735,10 @@ void G2_TransformBone (int child,CBoneCache &BC) { SBoneCalc &TB=BC.mBones[child]; mdxaBone_t tbone[6]; - mdxaFrame_t *aFrame=0; - mdxaFrame_t *bFrame=0; - mdxaFrame_t *aoldFrame=0; - mdxaFrame_t *boldFrame=0; +// mdxaFrame_t *aFrame=0; +// mdxaFrame_t *bFrame=0; +// mdxaFrame_t *aoldFrame=0; +// mdxaFrame_t *boldFrame=0; mdxaSkel_t *skel; mdxaSkelOffsets_t *offsets; boneInfo_v &boneList = *BC.rootBoneList; @@ -740,7 +761,7 @@ void G2_TransformBone (int child,CBoneCache &BC) } // set blending stuff if we need to - if (boneList[boneListIndex].flags & (BONE_ANIM_BLEND | BONE_ANIM_BLEND_TO_PARENT)) + if (boneList[boneListIndex].flags & BONE_ANIM_BLEND) { float blendTime = BC.incomingTime - boneList[boneListIndex].blendStart; // only set up the blend anim if we actually have some blend time left on this bone anim - otherwise we might corrupt some blend higher up the hiearchy @@ -756,27 +777,8 @@ void G2_TransformBone (int child,CBoneCache &BC) TB.blendMode = false; } } - else - // are blending *from* the parent? If so, grab what the parent is set to, and stick it in the blendFrame info - if (boneList[boneListIndex].flags & BONE_ANIM_BLEND_FROM_PARENT) - { - float blendTime = BC.incomingTime - boneList[boneListIndex].blendStart; - // only set up the blend anim if we actually have some blend time left on this bone anim - otherwise we might corrupt some blend higher up the hiearchy - if (blendTime>0.0f&&blendTime < boneList[boneListIndex].blendTime) - { - TB.blendFrame = TB.newFrame; - TB.blendOldFrame = TB.currentFrame; - TB.blendLerp = (blendTime / boneList[boneListIndex].blendTime); - TB.blendMode = true; - } - else - { - TB.blendMode = false; - } - } - else + else if (r_Ghoul2NoBlend->integer||((boneList[boneListIndex].flags) & (BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE))) // turn off blending if we are just doing a straing animation override - if (r_Ghoul2NoBlend->integer||((boneList[boneListIndex].flags) & (BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE))) { TB.blendMode = false; } @@ -800,13 +802,13 @@ void G2_TransformBone (int child,CBoneCache &BC) { TB.newFrame=0; } - aFrame = (mdxaFrame_t *)((byte *)BC.header + BC.header->ofsFrames + TB.newFrame * BC.frameSize ); +// aFrame = (mdxaFrame_t *)((byte *)BC.header + BC.header->ofsFrames + TB.newFrame * BC.frameSize ); assert(TB.currentFrame>=0&&TB.currentFramenumFrames); if (!(TB.currentFrame>=0&&TB.currentFramenumFrames)) { TB.currentFrame=0; } - aoldFrame = (mdxaFrame_t *)((byte *)BC.header + BC.header->ofsFrames + TB.currentFrame * BC.frameSize ); +// aoldFrame = (mdxaFrame_t *)((byte *)BC.header + BC.header->ofsFrames + TB.currentFrame * BC.frameSize ); // figure out where the location of the blended animation data is assert(!(TB.blendFrame < 0.0 || TB.blendFrame >= (BC.header->numFrames+1))); @@ -814,7 +816,7 @@ void G2_TransformBone (int child,CBoneCache &BC) { TB.blendFrame=0.0; } - bFrame = (mdxaFrame_t *)((byte *)BC.header + BC.header->ofsFrames + (int)TB.blendFrame * BC.frameSize ); +// bFrame = (mdxaFrame_t *)((byte *)BC.header + BC.header->ofsFrames + (int)TB.blendFrame * BC.frameSize ); assert(TB.blendOldFrame>=0&&TB.blendOldFramenumFrames); if (!(TB.blendOldFrame>=0&&TB.blendOldFramenumFrames)) { @@ -835,15 +837,15 @@ void G2_TransformBone (int child,CBoneCache &BC) OutputDebugString(mess); } #endif - boldFrame = (mdxaFrame_t *)((byte *)BC.header + BC.header->ofsFrames + TB.blendOldFrame * BC.frameSize ); +// boldFrame = (mdxaFrame_t *)((byte *)BC.header + BC.header->ofsFrames + TB.blendOldFrame * BC.frameSize ); // mdxaCompBone_t *compBonePointer = (mdxaCompBone_t *)((byte *)BC.header + BC.header->ofsCompBonePool); assert(child>=0&&childnumBones); - assert(bFrame->boneIndexes[child]>=0); - assert(boldFrame->boneIndexes[child]>=0); - assert(aFrame->boneIndexes[child]>=0); - assert(aoldFrame->boneIndexes[child]>=0); +// assert(bFrame->boneIndexes[child]>=0); +// assert(boldFrame->boneIndexes[child]>=0); +// assert(aFrame->boneIndexes[child]>=0); +// assert(aoldFrame->boneIndexes[child]>=0); // decide where the transformed bone is going @@ -855,8 +857,8 @@ void G2_TransformBone (int child,CBoneCache &BC) // MC_UnCompress(tbone[3].matrix,compBonePointer[bFrame->boneIndexes[child]].Comp); // MC_UnCompress(tbone[4].matrix,compBonePointer[boldFrame->boneIndexes[child]].Comp); - UnCompressBone(tbone[3].matrix,bFrame->boneIndexes[child], BC.header); - UnCompressBone(tbone[4].matrix,boldFrame->boneIndexes[child], BC.header); + UnCompressBone(tbone[3].matrix, child, BC.header, TB.blendFrame); + UnCompressBone(tbone[4].matrix, child, BC.header, TB.blendOldFrame); for ( j = 0 ; j < 12 ; j++ ) { @@ -871,7 +873,7 @@ void G2_TransformBone (int child,CBoneCache &BC) if (!TB.backlerp) { // MC_UnCompress(tbone[2].matrix,compBonePointer[aoldFrame->boneIndexes[child]].Comp); - UnCompressBone(tbone[2].matrix,aoldFrame->boneIndexes[child], BC.header); + UnCompressBone(tbone[2].matrix, child, BC.header, TB.currentFrame); // blend in the other frame if we need to if (TB.blendMode) @@ -895,8 +897,8 @@ void G2_TransformBone (int child,CBoneCache &BC) float frontlerp = 1.0 - TB.backlerp; // MC_UnCompress(tbone[0].matrix,compBonePointer[aFrame->boneIndexes[child]].Comp); // MC_UnCompress(tbone[1].matrix,compBonePointer[aoldFrame->boneIndexes[child]].Comp); - UnCompressBone(tbone[0].matrix,aFrame->boneIndexes[child], BC.header); - UnCompressBone(tbone[1].matrix,aoldFrame->boneIndexes[child], BC.header); + UnCompressBone(tbone[0].matrix, child, BC.header, TB.newFrame); + UnCompressBone(tbone[1].matrix, child, BC.header, TB.currentFrame); for ( j = 0 ; j < 12 ; j++ ) { @@ -1116,57 +1118,49 @@ void G2_TransformBone (int child,CBoneCache &BC) // start the recursive hirearchial bone transform and lerp process for this model -void G2_TransformGhoulBones(void *mod,const mdxaHeader_t *header, boneInfo_v &rootBoneList, - mdxaBone_t &rootMatrix, CGhoul2Info &ghoul2, int time) +void G2_TransformGhoulBones(boneInfo_v &rootBoneList,mdxaBone_t &rootMatrix, CGhoul2Info &ghoul2, int time,bool smooth=true) { - if (!header->numBones) + assert(ghoul2.aHeader); + assert(ghoul2.currentModel); + assert(ghoul2.currentModel->mdxm); + if (!ghoul2.aHeader->numBones) { assert(0); // this would be strange return; } if (!ghoul2.mBoneCache) { - ghoul2.mBoneCache=new CBoneCache(mod,header); + ghoul2.mBoneCache=new CBoneCache(ghoul2.currentModel,ghoul2.aHeader); } - ghoul2.mBoneCache->mod=mod; - assert(ghoul2.mBoneCache->mBones.size()==header->numBones); + ghoul2.mBoneCache->mod=ghoul2.currentModel; + ghoul2.mBoneCache->header=ghoul2.aHeader; + assert(ghoul2.mBoneCache->mBones.size()==ghoul2.aHeader->numBones); ghoul2.mBoneCache->mSmoothingActive=false; ghoul2.mBoneCache->mUnsquash=false; - if (HackadelicOnClient) - { - ghoul2.mBoneCache->mLastTouch=ghoul2.mBoneCache->mLastLastTouch; + ghoul2.mBoneCache->mLastTouch=ghoul2.mBoneCache->mLastLastTouch; - // master smoothing control - float val=r_Ghoul2AnimSmooth->value; - if (val>0.0f&&val<1.0f) + // master smoothing control + float val=r_Ghoul2AnimSmooth->value; + if (smooth&&val>0.0f&&val<1.0f) + { + ghoul2.mBoneCache->mSmoothFactor=val; + ghoul2.mBoneCache->mSmoothingActive=true; + if (r_Ghoul2UnSqashAfterSmooth->integer) { - float dif=float(time)-float(ghoul2.mBoneCache->mLastTime); - if (dif<16.6f) // 60 fps - { - dif=16.6f; - } - if (dif>100.0f) // 10 fps - { - dif=100.0f; - } - ghoul2.mBoneCache->mSmoothFactor=1.0f-pow(1.0f-val,50.0f/dif); - ghoul2.mBoneCache->mSmoothingActive=true; - if (r_Ghoul2UnSqashAfterSmooth->integer) - { - ghoul2.mBoneCache->mUnsquash=true; - } + ghoul2.mBoneCache->mUnsquash=true; } + ghoul2.mBoneCache->mLastTime=time; + } + else + { + ghoul2.mBoneCache->mSmoothFactor=1.0f; + ghoul2.mBoneCache->mLastTime=0; } ghoul2.mBoneCache->mCurrentTouch++; - if (HackadelicOnClient) - { - ghoul2.mBoneCache->mLastLastTouch=ghoul2.mBoneCache->mCurrentTouch; - } - assert(ghoul2.mBoneCache->header==header); - assert(ghoul2.mBoneCache->mod==mod); + ghoul2.mBoneCache->mLastLastTouch=ghoul2.mBoneCache->mCurrentTouch; ghoul2.mBoneCache->mWraithID=0; - ghoul2.mBoneCache->frameSize = (int)( &((mdxaFrame_t *)0)->boneIndexes[ header->numBones ] ); + ghoul2.mBoneCache->frameSize = 0;// can be deleted in new G2 format //(int)( &((mdxaFrame_t *)0)->boneIndexes[ ghoul2.aHeader->numBones ] ); ghoul2.mBoneCache->rootBoneList=&rootBoneList; ghoul2.mBoneCache->rootMatrix=rootMatrix; @@ -1206,7 +1200,7 @@ void G2_ProcessSurfaceBolt2(CBoneCache &boneCache, const mdxmSurface_t *surface, int polyNumber = (surfInfo->genPolySurfaceIndex >> 16) & 0x0ffff; // find original surface our original poly was in. - mdxmSurface_t *originalSurf = (mdxmSurface_t *)G2_FindSurface((void*)mod, surfNumber, surfInfo->genLod); + mdxmSurface_t *originalSurf = (mdxmSurface_t *)G2_FindSurface(mod, surfNumber, surfInfo->genLod); mdxmTriangle_t *originalTriangleIndexes = (mdxmTriangle_t *)((byte*)originalSurf + originalSurf->ofsTriangles); // get the original polys indexes @@ -1216,20 +1210,13 @@ void G2_ProcessSurfaceBolt2(CBoneCache &boneCache, const mdxmSurface_t *surface, // decide where the original verts are vert0 = (mdxmVertex_t *) ((byte *)originalSurf + originalSurf->ofsVerts); - for (j = 0; jweights[/*vert0->numWeights*/originalSurf->maxVertBoneWeights]; - } - vert1 = (mdxmVertex_t *) ((byte *)originalSurf + originalSurf->ofsVerts); - for (j = 0; jweights[/*vert1->numWeights*/originalSurf->maxVertBoneWeights]; - } - vert2 = (mdxmVertex_t *) ((byte *)originalSurf + originalSurf->ofsVerts); - for (j = 0; jweights[/*vert2->numWeights*/originalSurf->maxVertBoneWeights]; - } + vert0+=index0; + + vert1 = (mdxmVertex_t *) ((byte *)originalSurf + originalSurf->ofsVerts); + vert1+=index1; + + vert2 = (mdxmVertex_t *) ((byte *)originalSurf + originalSurf->ofsVerts); + vert2+=index2; // clear out the triangle verts to be VectorClear( pTri[0] ); @@ -1237,33 +1224,49 @@ void G2_ProcessSurfaceBolt2(CBoneCache &boneCache, const mdxmSurface_t *surface, VectorClear( pTri[2] ); int *piBoneReferences = (int*) ((byte*)originalSurf + originalSurf->ofsBoneReferences); - mdxmWeight_t *w; - - +// mdxmWeight_t *w; + // now go and transform just the points we need from the surface that was hit originally - w = vert0->weights; - for ( k = 0 ; k < vert0->numWeights ; k++, w++ ) +// w = vert0->weights; + int iNumWeights = G2_GetVertWeights( vert0 ); + for ( k = 0 ; k < iNumWeights ; k++ ) { - const mdxaBone_t &bone=boneCache.Eval(piBoneReferences[w->boneIndex]); - pTri[0][0] += w->boneWeight * ( DotProduct( bone.matrix[0], vert0->vertCoords ) + bone.matrix[0][3] ); - pTri[0][1] += w->boneWeight * ( DotProduct( bone.matrix[1], vert0->vertCoords ) + bone.matrix[1][3] ); - pTri[0][2] += w->boneWeight * ( DotProduct( bone.matrix[2], vert0->vertCoords ) + bone.matrix[2][3] ); + int iBoneIndex = G2_GetVertBoneIndex( vert0, k ); + float fBoneWeight = G2_GetVertBoneWeight( vert0, k ); + + const mdxaBone_t &bone=boneCache.Eval(piBoneReferences[iBoneIndex]); + + pTri[0][0] += fBoneWeight * ( DotProduct( bone.matrix[0], vert0->vertCoords ) + bone.matrix[0][3] ); + pTri[0][1] += fBoneWeight * ( DotProduct( bone.matrix[1], vert0->vertCoords ) + bone.matrix[1][3] ); + pTri[0][2] += fBoneWeight * ( DotProduct( bone.matrix[2], vert0->vertCoords ) + bone.matrix[2][3] ); } - w = vert1->weights; - for ( k = 0 ; k < vert1->numWeights ; k++, w++ ) + +// w = vert1->weights; + iNumWeights = G2_GetVertWeights( vert1 ); + for ( k = 0 ; k < iNumWeights ; k++) { - const mdxaBone_t &bone=boneCache.Eval(piBoneReferences[w->boneIndex]); - pTri[1][0] += w->boneWeight * ( DotProduct( bone.matrix[0], vert1->vertCoords ) + bone.matrix[0][3] ); - pTri[1][1] += w->boneWeight * ( DotProduct( bone.matrix[1], vert1->vertCoords ) + bone.matrix[1][3] ); - pTri[1][2] += w->boneWeight * ( DotProduct( bone.matrix[2], vert1->vertCoords ) + bone.matrix[2][3] ); + int iBoneIndex = G2_GetVertBoneIndex( vert1, k ); + float fBoneWeight = G2_GetVertBoneWeight( vert1, k ); + + const mdxaBone_t &bone=boneCache.Eval(piBoneReferences[iBoneIndex]); + + pTri[1][0] += fBoneWeight * ( DotProduct( bone.matrix[0], vert1->vertCoords ) + bone.matrix[0][3] ); + pTri[1][1] += fBoneWeight * ( DotProduct( bone.matrix[1], vert1->vertCoords ) + bone.matrix[1][3] ); + pTri[1][2] += fBoneWeight * ( DotProduct( bone.matrix[2], vert1->vertCoords ) + bone.matrix[2][3] ); } - w = vert2->weights; - for ( k = 0 ; k < vert2->numWeights ; k++, w++ ) + +// w = vert2->weights; + iNumWeights = G2_GetVertWeights( vert2 ); + for ( k = 0 ; k < iNumWeights ; k++) { - const mdxaBone_t &bone=boneCache.Eval(piBoneReferences[w->boneIndex]); - pTri[2][0] += w->boneWeight * ( DotProduct( bone.matrix[0], vert2->vertCoords ) + bone.matrix[0][3] ); - pTri[2][1] += w->boneWeight * ( DotProduct( bone.matrix[1], vert2->vertCoords ) + bone.matrix[1][3] ); - pTri[2][2] += w->boneWeight * ( DotProduct( bone.matrix[2], vert2->vertCoords ) + bone.matrix[2][3] ); + int iBoneIndex = G2_GetVertBoneIndex( vert2, k ); + float fBoneWeight = G2_GetVertBoneWeight( vert2, k ); + + const mdxaBone_t &bone=boneCache.Eval(piBoneReferences[iBoneIndex]); + + pTri[2][0] += fBoneWeight * ( DotProduct( bone.matrix[0], vert2->vertCoords ) + bone.matrix[0][3] ); + pTri[2][1] += fBoneWeight * ( DotProduct( bone.matrix[1], vert2->vertCoords ) + bone.matrix[1][3] ); + pTri[2][2] += fBoneWeight * ( DotProduct( bone.matrix[2], vert2->vertCoords ) + bone.matrix[2][3] ); } vec3_t normal; @@ -1322,20 +1325,26 @@ void G2_ProcessSurfaceBolt2(CBoneCache &boneCache, const mdxmSurface_t *surface, int *piBoneReferences = (int*) ((byte*)surface + surface->ofsBoneReferences); for ( j = 0; j < 3; j++ ) { - mdxmWeight_t *w; +// mdxmWeight_t *w; VectorClear( pTri[j] ); - w = v->weights; - for ( k = 0 ; k < v->numWeights ; k++, w++ ) - { - const mdxaBone_t &bone=boneCache.Eval(piBoneReferences[w->boneIndex]); + // w = v->weights; - pTri[j][0] += w->boneWeight * ( DotProduct( bone.matrix[0], v->vertCoords ) + bone.matrix[0][3] ); - pTri[j][1] += w->boneWeight * ( DotProduct( bone.matrix[1], v->vertCoords ) + bone.matrix[1][3] ); - pTri[j][2] += w->boneWeight * ( DotProduct( bone.matrix[2], v->vertCoords ) + bone.matrix[2][3] ); + const int iNumWeights = G2_GetVertWeights( v ); + + for ( k = 0 ; k < iNumWeights ; k++) + { + int iBoneIndex = G2_GetVertBoneIndex( v, k ); + float fBoneWeight = G2_GetVertBoneWeight( v, k ); + + const mdxaBone_t &bone=boneCache.Eval(piBoneReferences[iBoneIndex]); + + pTri[j][0] += fBoneWeight * ( DotProduct( bone.matrix[0], v->vertCoords ) + bone.matrix[0][3] ); + pTri[j][1] += fBoneWeight * ( DotProduct( bone.matrix[1], v->vertCoords ) + bone.matrix[1][3] ); + pTri[j][2] += fBoneWeight * ( DotProduct( bone.matrix[2], v->vertCoords ) + bone.matrix[2][3] ); } - v = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surface->maxVertBoneWeights]; + v++;// = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surface->maxVertBoneWeights]; } // clear out used arrays @@ -1402,7 +1411,7 @@ void G2_GetBoltMatrixLow(CGhoul2Info &ghoul2,int boltNum,const vec3_t scale,mdxa mdxaSkelOffsets_t *offsets; offsets = (mdxaSkelOffsets_t *)((byte *)boneCache.header + sizeof(mdxaHeader_t)); skel = (mdxaSkel_t *)((byte *)boneCache.header + sizeof(mdxaHeader_t) + offsets->offsets[boltList[boltNum].boneNumber]); - Multiply_3x4Matrix(&retMatrix, &boneCache.Eval(boltList[boltNum].boneNumber), &skel->BasePoseMat); + Multiply_3x4Matrix(&retMatrix, &boneCache.EvalUnsmooth(boltList[boltNum].boneNumber), &skel->BasePoseMat); } else if (boltList[boltNum].surfaceNumber>=0) { @@ -1434,24 +1443,6 @@ void G2_GetBoltMatrixLow(CGhoul2Info &ghoul2,int boltNum,const vec3_t scale,mdxa // we have a bolt without a bone or surface, not a huge problem but we ought to at least clear the bolt matrix retMatrix=identityMatrix; } -/* - // scale the bolt position by the scale factor for this model since at this point its still in model space - if (scale[0]) - { - retMatrix.matrix[0][3] *= scale[0]; - } - if (scale[1]) - { - retMatrix.matrix[1][3] *= scale[1]; - } - if (scale[2]) - { - retMatrix.matrix[2][3] *= scale[2]; - } - VectorNormalize((float*)&retMatrix.matrix[0]); - VectorNormalize((float*)&retMatrix.matrix[1]); - VectorNormalize((float*)&retMatrix.matrix[2]); -*/ } @@ -1462,9 +1453,11 @@ void RenderSurfaces(CRenderSurface &RS) const shader_t *shader = 0; int offFlags = 0; - + + assert(RS.currentModel); + assert(RS.currentModel->mdxm); // back track and get the surfinfo struct for this surface - mdxmSurface_t *surface = (mdxmSurface_t *)G2_FindSurface((void *)RS.currentModel, RS.surfaceNum, RS.lod); + mdxmSurface_t *surface = (mdxmSurface_t *)G2_FindSurface(RS.currentModel, RS.surfaceNum, RS.lod); mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)RS.currentModel->mdxm + sizeof(mdxmHeader_t)); mdxmSurfHierarchy_t *surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surface->thisSurfaceIndex]); @@ -1586,7 +1579,7 @@ static void G2_Sort_Models(CGhoul2Info_v &ghoul2, int * const modelList, int * c for (i=0; iinteger) { return; } + if (!G2_SetupModelPointers(ghoul2)) + { + return; + } + + int currentTime=G2API_GetTime(tr.refdef.time); + // cull the entire model if merged bounding box of both frames // is outside the view frustum. @@ -1727,31 +1736,9 @@ void R_AddGhoulSurfaces( trRefEntity_t *ent ) { // walk each possible model for this entity and try rendering it out for (j=0; jmdxm) - { - continue; - } - const model_t *animModel = R_GetModelByHandle(currentModel->mdxm->animIndex); - const mdxaHeader_t *aHeader = animModel->mdxa; - // // figure out whether we should be using a custom shader for this model // @@ -1780,15 +1767,15 @@ void R_AddGhoulSurfaces( trRefEntity_t *ent ) { int boltNum = (ghoul2[i].mModelBoltLink >> BOLT_SHIFT) & BOLT_AND; mdxaBone_t bolt; G2_GetBoltMatrixLow(ghoul2[boltMod],boltNum,ent->e.modelScale,bolt); - G2_TransformGhoulBones((void *)currentModel, aHeader, ghoul2[i].mBlist,bolt, ghoul2[i],currentTime); + G2_TransformGhoulBones(ghoul2[i].mBlist,bolt, ghoul2[i],currentTime); } else { - G2_TransformGhoulBones((void *)currentModel, aHeader, ghoul2[i].mBlist, rootMatrix, ghoul2[i],currentTime); + G2_TransformGhoulBones(ghoul2[i].mBlist, rootMatrix, ghoul2[i],currentTime); } - whichLod = G2_ComputeLOD( ent, currentModel, ghoul2[i].mLodBias ); + whichLod = G2_ComputeLOD( ent, ghoul2[i].currentModel, ghoul2[i].mLodBias ); G2_FindOverrideSurface(-1,ghoul2[i].mSlist); //reset the quick surface override lookup; - CRenderSurface RS(ghoul2[i].mSurfaceRoot, ghoul2[i].mSlist, cust_shader, fogNum, personalModel, ghoul2[i].mBoneCache, ent->e.renderfx, skin, currentModel, whichLod, ghoul2[i].mBltlist); + CRenderSurface RS(ghoul2[i].mSurfaceRoot, ghoul2[i].mSlist, cust_shader, fogNum, personalModel, ghoul2[i].mBoneCache, ent->e.renderfx, skin,ghoul2[i].currentModel, whichLod, ghoul2[i].mBltlist); RenderSurfaces(RS); } } @@ -1797,11 +1784,11 @@ void R_AddGhoulSurfaces( trRefEntity_t *ent ) { bool G2_NeedsRecalc(CGhoul2Info *ghlInfo,int frameNum) { - ghlInfo->mModel = RE_RegisterModel(ghlInfo->mFileName); - void *currentModel = R_GetModelByHandle(ghlInfo->mModel); + G2_SetupModelPointers(ghlInfo); + // not sure if I still need this test, probably if (ghlInfo->mSkelFrameNum!=frameNum|| !ghlInfo->mBoneCache|| - ghlInfo->mBoneCache->mod!=currentModel) + ghlInfo->mBoneCache->mod!=ghlInfo->currentModel) { ghlInfo->mSkelFrameNum=frameNum; return true; @@ -1816,9 +1803,7 @@ G2_ConstructGhoulSkeleton - builds a complete skeleton for all ghoul models in a */ void G2_ConstructGhoulSkeleton( CGhoul2Info_v &ghoul2,const int frameNum,bool checkForNewOrigin,const vec3_t scale) { - mdxaHeader_t *aHeader; int i, j; - const model_t *animModel; int modelCount; mdxaBone_t rootMatrix; @@ -1843,32 +1828,8 @@ void G2_ConstructGhoulSkeleton( CGhoul2Info_v &ghoul2,const int frameNum,bool ch // get the sorted model to play with i = modelList[j]; - if (!(ghoul2[i].mFlags & GHOUL2_NOMODEL)) + if (ghoul2[i].mValid) { - // I really hate this code, really hate this code -gil -#if 1 - ghoul2[i].mModel = RE_RegisterModel(ghoul2[i].mFileName); - const model_t *currentModel = R_GetModelByHandle(ghoul2[i].mModel); -#else - if (!ghoul2[i].mModel) - {// probably cleared by G2API_SetGhoul2ModelIndexes - ghoul2[i].mModel = RE_RegisterModel(ghoul2[i].mFileName); - } - const model_t *currentModel = R_GetModelByHandle(ghoul2[i].mModel); - if (!currentModel->mdxm) - { - ghoul2[i].mModel = RE_RegisterModel(ghoul2[i].mFileName); - currentModel = R_GetModelByHandle(ghoul2[i].mModel); - } -#endif - if (!currentModel->mdxm) - { - continue; - } - assert(currentModel->mdxm);//something very bad happened here, it has no glm! - animModel = R_GetModelByHandle(currentModel->mdxm->animIndex); - aHeader = animModel->mdxa; - if (j&&ghoul2[i].mModelBoltLink != -1) { int boltMod = (ghoul2[i].mModelBoltLink >> MODEL_SHIFT) & MODEL_AND; @@ -1876,11 +1837,11 @@ void G2_ConstructGhoulSkeleton( CGhoul2Info_v &ghoul2,const int frameNum,bool ch mdxaBone_t bolt; G2_GetBoltMatrixLow(ghoul2[boltMod],boltNum,scale,bolt); - G2_TransformGhoulBones((void *)currentModel,aHeader,ghoul2[i].mBlist,bolt,ghoul2[i],frameNum); + G2_TransformGhoulBones(ghoul2[i].mBlist,bolt,ghoul2[i],frameNum,checkForNewOrigin); } else { - G2_TransformGhoulBones((void *)currentModel,aHeader,ghoul2[i].mBlist,rootMatrix,ghoul2[i],frameNum); + G2_TransformGhoulBones(ghoul2[i].mBlist,rootMatrix,ghoul2[i],frameNum,checkForNewOrigin); } } } @@ -1931,57 +1892,40 @@ void RB_SurfaceGhoul( CRenderableSurface *surf ) { #endif - // whip through and actually transform each vertex - int *piBoneReferences = (int*) ((byte*)surface + surface->ofsBoneReferences); const int numVerts = surface->numVerts; const mdxmVertex_t *v = (mdxmVertex_t *) ((byte *)surface + surface->ofsVerts); + mdxmVertexTexCoord_t *pTexCoords = (mdxmVertexTexCoord_t *) &v[numVerts]; int baseVert = tess.numVertexes; for ( j = 0; j < numVerts; j++, baseVert++ ) { - const int numWeights = v->numWeights; - assert(numWeights); // if this fires, comment it out please. - // if nobody comments it out, I will remove the below if for performance - const mdxmWeight_t *w = v->weights; + const int iNumWeights = G2_GetVertWeights( v ); +// const mdxmWeight_t *w = v->weights; const mdxaBone_t *bone; - if (numWeights) + VectorClear( tess.xyz[baseVert]); + VectorClear( tess.normal[baseVert]); + for (k = 0 ; k < iNumWeights ; k++) { - bone = &bones->EvalRender(piBoneReferences[w->boneIndex]); + int iBoneIndex = G2_GetVertBoneIndex( v, k ); + float fBoneWeight = G2_GetVertBoneWeight( v, k ); - tess.xyz[baseVert][0] = w->boneWeight * ( DotProduct( bone->matrix[0], v->vertCoords ) + bone->matrix[0][3] ); - tess.xyz[baseVert][1] = w->boneWeight * ( DotProduct( bone->matrix[1], v->vertCoords ) + bone->matrix[1][3] ); - tess.xyz[baseVert][2] = w->boneWeight * ( DotProduct( bone->matrix[2], v->vertCoords ) + bone->matrix[2][3] ); + bone = &bones->Eval(piBoneReferences[iBoneIndex]); - tess.normal[baseVert][0] = w->boneWeight * DotProduct( bone->matrix[0], v->normal ); - tess.normal[baseVert][1] = w->boneWeight * DotProduct( bone->matrix[1], v->normal ); - tess.normal[baseVert][2] = w->boneWeight * DotProduct( bone->matrix[2], v->normal ); + tess.xyz[baseVert][0] += fBoneWeight * ( DotProduct( bone->matrix[0], v->vertCoords ) + bone->matrix[0][3] ); + tess.xyz[baseVert][1] += fBoneWeight * ( DotProduct( bone->matrix[1], v->vertCoords ) + bone->matrix[1][3] ); + tess.xyz[baseVert][2] += fBoneWeight * ( DotProduct( bone->matrix[2], v->vertCoords ) + bone->matrix[2][3] ); - for (w++, k = 1 ; k < numWeights ; k++, w++ ) - { - bone = &bones->EvalRender(piBoneReferences[w->boneIndex]); - - tess.xyz[baseVert][0] += w->boneWeight * ( DotProduct( bone->matrix[0], v->vertCoords ) + bone->matrix[0][3] ); - tess.xyz[baseVert][1] += w->boneWeight * ( DotProduct( bone->matrix[1], v->vertCoords ) + bone->matrix[1][3] ); - tess.xyz[baseVert][2] += w->boneWeight * ( DotProduct( bone->matrix[2], v->vertCoords ) + bone->matrix[2][3] ); - - tess.normal[baseVert][0] += w->boneWeight * DotProduct( bone->matrix[0], v->normal ); - tess.normal[baseVert][1] += w->boneWeight * DotProduct( bone->matrix[1], v->normal ); - tess.normal[baseVert][2] += w->boneWeight * DotProduct( bone->matrix[2], v->normal ); - } - - } - else - { - VectorClear( tess.xyz[baseVert]); - VectorClear( tess.normal[baseVert]); + tess.normal[baseVert][0] += fBoneWeight * DotProduct( bone->matrix[0], v->normal ); + tess.normal[baseVert][1] += fBoneWeight * DotProduct( bone->matrix[1], v->normal ); + tess.normal[baseVert][2] += fBoneWeight * DotProduct( bone->matrix[2], v->normal ); } - tess.texCoords[baseVert][0][0] = v->texCoords[0]; - tess.texCoords[baseVert][0][1] = v->texCoords[1]; + tess.texCoords[baseVert][0][0] = pTexCoords[j].texCoords[0]; + tess.texCoords[baseVert][0][1] = pTexCoords[j].texCoords[1]; - v = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surface->maxVertBoneWeights]; + v++;// = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surface->maxVertBoneWeights]; } tess.numVertexes += surface->numVerts; @@ -2015,9 +1959,10 @@ inline int VectorCompareThresh( const vec3_t v1, const vec3_t v2 ) { return 1; } +// fixme: optimise this out sometime, it's pointless now inline mdxmVertex_t *GetSurfaceVertex( mdxmSurface_t *surf, int vert ) { - int vertSize = sizeof(mdxmVertex_t) + sizeof(mdxmWeight_t) * (surf->maxVertBoneWeights - 1); + int vertSize = sizeof(mdxmVertex_t);// + sizeof(mdxmWeight_t) * (surf->maxVertBoneWeights - 1); return (mdxmVertex_t *) ((byte *)surf + surf->ofsVerts + vert * vertSize); } @@ -2030,6 +1975,7 @@ Returns number of new degenerate triangles and pointer to the triangle data. !!! Very inefficient implementation for now. ================= */ +#if 0 static mdxmTriangle_t *R_SurfFillCreases( mdxmSurface_t *surf, int *triCount ) { mdxmTriangle_t *triFix; @@ -2143,7 +2089,7 @@ static mdxmTriangle_t *R_SurfFillCreases( mdxmSurface_t *surf, int *triCount ) *triCount = 0; return NULL; } - +#endif /* ================= R_LoadAndPatchMDXM - load and patch a Ghoul 2 Mesh file @@ -2151,6 +2097,7 @@ R_LoadAndPatchMDXM - load and patch a Ghoul 2 Mesh file */ qboolean R_LoadAndPatchMDXM( model_t *mod, void *buffer, const char *mod_name, qboolean bAlreadyCached ) { +#if 0 int version; int size; int tempSize; @@ -2275,7 +2222,7 @@ qboolean R_LoadAndPatchMDXM( model_t *mod, void *buffer, const char *mod_name, q LL(tempSurf->numVerts); LL(tempSurf->numTriangles); LL(tempSurf->numBoneReferences); - LL(tempSurf->maxVertBoneWeights); +// LL(tempSurf->maxVertBoneWeights); // change to surface identifier tempSurf->ident = SF_MDX; @@ -2434,6 +2381,9 @@ qboolean R_LoadAndPatchMDXM( model_t *mod, void *buffer, const char *mod_name, q } return qtrue; +#else + return qfalse; +#endif } #endif // _NPATCH @@ -2456,21 +2406,22 @@ qboolean R_LoadMDXM( model_t *mod, void *buffer, const char *mod_name, qboolean #ifndef _M_IX86 int k; int frameSize; - mdxmTag_t *tag; +// mdxmTag_t *tag; mdxmTriangle_t *tri; mdxmVertex_t *v; mdxmFrame_t *cframe; int *boneRef; #endif - + #ifdef _NPATCH // // If n-patches are enabled, load and patch the models // - if (r_ati_pn_triangles->integer && mod->npatchable && !bAlreadyCached) - { - return R_LoadAndPatchMDXM( mod, buffer, mod_name, bAlreadyCached ); - } + // fixme: could probably do with this upgrading sometime +// if (r_ati_pn_triangles->integer && mod->npatchable && !bAlreadyCached) +// { +// return R_LoadAndPatchMDXM( mod, buffer, mod_name, bAlreadyCached ); +// } #endif // _NPATCH pinmodel= (mdxmHeader_t *)buffer; @@ -2487,8 +2438,11 @@ qboolean R_LoadMDXM( model_t *mod, void *buffer, const char *mod_name, qboolean } if (version != MDXM_VERSION) { - ri.Printf( PRINT_WARNING, "R_LoadMDXM: %s has wrong version (%i should be %i)\n", - mod_name, version, MDXM_VERSION); +#ifdef _DEBUG + ri.Error( ERR_DROP, "R_LoadMDXM: %s has wrong version (%i should be %i)\n", mod_name, version, MDXM_VERSION); +#else + ri.Printf( PRINT_WARNING, "R_LoadMDXM: %s has wrong version (%i should be %i)\n", mod_name, version, MDXM_VERSION); +#endif return qfalse; } @@ -2581,7 +2535,7 @@ qboolean R_LoadMDXM( model_t *mod, void *buffer, const char *mod_name, qboolean LL(surf->ofsHeader); LL(surf->numBoneReferences); LL(surf->ofsBoneReferences); - LL(surf->maxVertBoneWeights); +// LL(surf->maxVertBoneWeights); triCount += surf->numTriangles; @@ -2703,9 +2657,9 @@ qboolean R_LoadMDXA( model_t *mod, void *buffer, const char *mod_name, qboolean size = LittleLong(size); } - if (version != MDXA_VERSION && version != MDXA_VERSION_QUAT) { - ri.Printf( PRINT_WARNING, "R_LoadMDXA: %s has wrong version (%i should be %i or %i)\n", - mod_name, version, MDXA_VERSION, MDXA_VERSION_QUAT); + if (version != MDXA_VERSION) { + ri.Printf( PRINT_WARNING, "R_LoadMDXA: %s has wrong version (%i should be %i)\n", + mod_name, version, MDXA_VERSION); return qfalse; } diff --git a/code/renderer/tr_image.cpp b/code/renderer/tr_image.cpp index 66e6bf6..ba20c71 100644 --- a/code/renderer/tr_image.cpp +++ b/code/renderer/tr_image.cpp @@ -713,7 +713,7 @@ done: class CStringComparator { public: - bool operator()(const char *s1, const char *s2) const { return(strcmp(s1, s2) < 0); } + bool operator()(const char *s1, const char *s2) const { return(stricmp(s1, s2) < 0); } }; typedef map AllocatedImages_t; @@ -2046,6 +2046,77 @@ static char *CommaParse( char **data_p ) { return com_token; } +/* +class CStringComparator +{ +public: + bool operator()(const char *s1, const char *s2) const { return(stricmp(s1, s2) < 0); } +}; +*/ +typedef map AnimationCFGs_t; + AnimationCFGs_t AnimationCFGs; + +// I added this function for development purposes (and it's VM-safe) so we don't have problems +// with our use of cached models but uncached animation.cfg files (so frame sequences are out of sync +// if someone rebuild the model while you're ingame and you change levels)... +// +// Usage: call with psDest == NULL for a size enquire (for malloc), +// then with NZ ptr for it to copy to your supplied buffer... +// +int RE_GetAnimationCFG(const char *psCFGFilename, char *psDest, int iDestSize) +{ + char *psText = NULL; + + AnimationCFGs_t::iterator it = AnimationCFGs.find(psCFGFilename); + if (it != AnimationCFGs.end()) + { + psText = (*it).second; + } + else + { + // not found, so load it... + // + fileHandle_t f; + int iLen = FS_FOpenFileRead( psCFGFilename, &f, FS_READ ); + if (iLen <= 0) + { + return 0; + } + + psText = (char *) Z_Malloc( iLen+1, TAG_ANIMATION_CFG, qfalse ); + + FS_Read( psText, iLen, f ); + psText[iLen] = '\0'; + FS_FCloseFile( f ); + + AnimationCFGs[psCFGFilename] = psText; + } + + if (psText) // sanity, but should always be NZ + { + if (psDest) + { + Q_strncpyz(psDest,psText,iDestSize); + } + + return strlen(psText); + } + + return 0; +} + +// only called from devmapbsp, devmapall, or ... +// +void RE_AnimationCFGs_DeleteAll(void) +{ + for (AnimationCFGs_t::iterator it = AnimationCFGs.begin(); it != AnimationCFGs.end(); ++it) + { + char *psText = (*it).second; + Z_Free(psText); + } + + AnimationCFGs.clear(); +} /* =============== diff --git a/code/renderer/tr_init.cpp b/code/renderer/tr_init.cpp index 5834776..09ca3f8 100644 --- a/code/renderer/tr_init.cpp +++ b/code/renderer/tr_init.cpp @@ -152,9 +152,7 @@ cvar_t *r_modelpoolmegs; Ghoul2 Insert Start */ -cvar_t *r_noServerGhoul2; cvar_t *r_noGhoul2; -cvar_t *r_Ghoul2Test; cvar_t *r_Ghoul2AnimSmooth; cvar_t *r_Ghoul2UnSqash; cvar_t *r_Ghoul2TimeBase=0; @@ -410,9 +408,9 @@ void R_TakeScreenshot( int x, int y, int width, int height, char *fileName ) { qglReadPixels( x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer ); // gamma correct -// if ( glConfig.deviceSupportsGamma ) { -// R_GammaCorrect( buffer, glConfig.vidWidth * glConfig.vidHeight * 4 ); -// } + if ( tr.overbrightBits>0 && glConfig.deviceSupportsGamma ) { + R_GammaCorrect( buffer, glConfig.vidWidth * glConfig.vidHeight * 4 ); + } SaveJPG(fileName, 95, width, height, buffer); } @@ -440,9 +438,9 @@ void R_TakeScreenshot( int x, int y, int width, int height, char *fileName ) { } // gamma correct -// if ( glConfig.deviceSupportsGamma ) { -// R_GammaCorrect( buffer + 18, glConfig.vidWidth * glConfig.vidHeight * 3 ); -// } + if ( tr.overbrightBits>0 && glConfig.deviceSupportsGamma ) { + R_GammaCorrect( buffer + 18, glConfig.vidWidth * glConfig.vidHeight * 3 ); + } ri.FS_WriteFile( fileName, buffer, c ); } @@ -1003,7 +1001,8 @@ void R_Register( void ) r_simpleMipMaps = ri.Cvar_Get( "r_simpleMipMaps", "1", CVAR_ARCHIVE | CVAR_LATCH ); r_vertexLight = ri.Cvar_Get( "r_vertexLight", "0", CVAR_ARCHIVE | CVAR_LATCH ); r_subdivisions = ri.Cvar_Get ("r_subdivisions", "4", CVAR_ARCHIVE | CVAR_LATCH); - r_smp = ri.Cvar_Get( "r_smp", "0", CVAR_ARCHIVE | CVAR_LATCH); +// r_smp = ri.Cvar_Get( "r_smp", "0", CVAR_ARCHIVE | CVAR_LATCH); + r_smp = ri.Cvar_Get( "r_smp", "0", CVAR_ROM); r_ignoreFastPath = ri.Cvar_Get( "r_ignoreFastPath", "1", CVAR_ARCHIVE | CVAR_LATCH ); r_intensity = ri.Cvar_Get ("r_intensity", "1", CVAR_LATCH|CVAR_ARCHIVE ); @@ -1059,7 +1058,7 @@ void R_Register( void ) // // temporary variables that can change at any time // - r_showImages = ri.Cvar_Get( "r_showImages", "0", CVAR_TEMP ); + r_showImages = ri.Cvar_Get( "r_showImages", "0", CVAR_CHEAT ); r_debugLight = ri.Cvar_Get( "r_debuglight", "0", CVAR_TEMP ); r_debugStyle = ri.Cvar_Get( "r_debugStyle", "-1", CVAR_CHEAT ); @@ -1079,7 +1078,7 @@ void R_Register( void ) r_skipBackEnd = ri.Cvar_Get ("r_skipBackEnd", "0", CVAR_CHEAT); r_measureOverdraw = ri.Cvar_Get( "r_measureOverdraw", "0", CVAR_CHEAT ); - r_lodscale = ri.Cvar_Get( "r_lodscale", "4", CVAR_CHEAT );//4 was a bit overkill + r_lodscale = ri.Cvar_Get( "r_lodscale", "6", CVAR_CHEAT ); r_norefresh = ri.Cvar_Get ("r_norefresh", "0", CVAR_CHEAT); r_drawentities = ri.Cvar_Get ("r_drawentities", "1", CVAR_CHEAT ); r_ignore = ri.Cvar_Get( "r_ignore", "1", CVAR_TEMP ); @@ -1098,7 +1097,7 @@ void R_Register( void ) r_clear = ri.Cvar_Get ("r_clear", "0", CVAR_CHEAT); r_offsetFactor = ri.Cvar_Get( "r_offsetfactor", "-1", CVAR_CHEAT ); r_offsetUnits = ri.Cvar_Get( "r_offsetunits", "-2", CVAR_CHEAT ); - r_drawBuffer = ri.Cvar_Get( "r_drawBuffer", "GL_BACK", 0 ); + r_drawBuffer = ri.Cvar_Get( "r_drawBuffer", "GL_BACK", CVAR_CHEAT ); r_lockpvs = ri.Cvar_Get ("r_lockpvs", "0", CVAR_CHEAT); r_noportals = ri.Cvar_Get ("r_noportals", "0", CVAR_CHEAT); r_shadows = ri.Cvar_Get( "cg_shadows", "1", 0 ); @@ -1106,10 +1105,8 @@ void R_Register( void ) /* Ghoul2 Insert Start */ - r_noServerGhoul2 = ri.Cvar_Get( "r_noserverghoul2", "0", 0); - r_noGhoul2 = ri.Cvar_Get( "r_noghoul2", "0", 0); - r_Ghoul2Test = ri.Cvar_Get( "r_ghoul2test", "0", 0); - r_Ghoul2AnimSmooth = ri.Cvar_Get( "r_ghoul2animsmooth", "0", 0); + r_noGhoul2 = ri.Cvar_Get( "r_noghoul2", "0", CVAR_CHEAT); + r_Ghoul2AnimSmooth = ri.Cvar_Get( "r_ghoul2animsmooth", "0.25", 0); r_Ghoul2UnSqash = ri.Cvar_Get( "r_ghoul2unsquash", "1", 0); r_Ghoul2TimeBase = ri.Cvar_Get( "r_ghoul2timebase", "2", 0); r_Ghoul2NoLerp = ri.Cvar_Get( "r_ghoul2nolerp", "0", 0); @@ -1355,6 +1352,7 @@ refexport_t *GetRefAPI ( int apiVersion, refimport_t *rimp ) { re.BeginRegistration = RE_BeginRegistration; re.RegisterModel = RE_RegisterModel; re.RegisterSkin = RE_RegisterSkin; + re.GetAnimationCFG = RE_GetAnimationCFG; re.RegisterShader = RE_RegisterShader; re.RegisterShaderNoMip = RE_RegisterShaderNoMip; re.LoadWorld = RE_LoadWorldMap; diff --git a/code/renderer/tr_local.h b/code/renderer/tr_local.h index 9516686..0d05a65 100644 --- a/code/renderer/tr_local.h +++ b/code/renderer/tr_local.h @@ -1174,7 +1174,6 @@ extern cvar_t *r_printShaders; /* Ghoul2 Insert Start */ -extern cvar_t *r_noServerGhoul2; extern cvar_t *r_noGhoul2; /* Ghoul2 Insert End @@ -1279,6 +1278,7 @@ void RE_LoadWorldMap( const char *mapname ); void RE_SetWorldVisData( const byte *vis ); qhandle_t RE_RegisterModel( const char *name ); qhandle_t RE_RegisterSkin( const char *name ); +int RE_GetAnimationCFG(const char *psCFGFilename, char *psDest, int iDestSize); void RE_Shutdown( qboolean destroyWindow ); void RE_RegisterMedia_LevelLoadBegin(const char *psMapName, ForceReload_e eForceReload, qboolean bAllowScreenDissolve); @@ -1698,6 +1698,11 @@ typedef struct { float a; } rotatePicCommand_t; +typedef struct +{ + int commandId; +} setModeCommand_t; + typedef struct { int commandId; @@ -1723,6 +1728,7 @@ typedef enum { RC_DRAW_SURFS, RC_DRAW_BUFFER, RC_SWAP_BUFFERS, + RC_WORLD_EFFECTS, } renderCommand_t; @@ -1769,6 +1775,7 @@ void RE_RotatePic ( float x, float y, float w, float h, float s1, float t1, float s2, float t2,float a, qhandle_t hShader ); void RE_RotatePic2 ( float x, float y, float w, float h, float s1, float t1, float s2, float t2,float a, qhandle_t hShader ); +void RE_RenderWorldEffects(void); void RE_LAGoggles( void ); void RE_Scissor ( float x, float y, float w, float h); void RE_BeginFrame( stereoFrame_t stereoFrame ); diff --git a/code/renderer/tr_main.cpp b/code/renderer/tr_main.cpp index 971bbd1..1aeba01 100644 --- a/code/renderer/tr_main.cpp +++ b/code/renderer/tr_main.cpp @@ -1644,7 +1644,10 @@ void R_RenderView (viewParms_t *parms) { R_SetupFrustum (); - R_SetViewFogIndex (); + if (!(tr.refdef.rdflags & RDF_NOWORLDMODEL)) + { // Trying to do this with no world is not good. + R_SetViewFogIndex (); + } R_GenerateDrawSurfs(); diff --git a/code/renderer/tr_model.cpp b/code/renderer/tr_model.cpp index 45c5fa8..6a1e5b6 100644 --- a/code/renderer/tr_model.cpp +++ b/code/renderer/tr_model.cpp @@ -85,25 +85,25 @@ void RE_RegisterModels_StoreShaderRequest(const char *psModelFileName, const cha static const byte FakeGLAFile[] = { -0x32, 0x4C, 0x47, 0x41, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x6F, 0x64, 0x65, 0x6C, 0x73, 0x2F, 0x63, -0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x2F, 0x62, 0x6F, 0x6C, 0x74, 0x5F, 0x6F, -0x6E, 0x73, 0x2F, 0x67, 0x32, 0x68, 0x65, 0x6C, 0x6D, 0x65, 0x74, 0x5F, 0x68, 0x65, 0x6C, 0x69, +0x32, 0x4C, 0x47, 0x41, 0x06, 0x00, 0x00, 0x00, 0x2A, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x01, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, -0x30, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x47, 0x65, 0x6E, 0x65, 0x72, 0x61, 0x74, 0x65, -0x64, 0x20, 0x62, 0x79, 0x20, 0x4D, 0x44, 0x33, 0x56, 0x69, 0x65, 0x77, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, +0x26, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x4D, 0x6F, 0x64, 0x56, 0x69, 0x65, 0x77, 0x20, +0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, +0x00, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, +0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, +0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0xFE, 0xFF, -0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0xFE, 0xFF, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0xFE, 0xFF +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xBF, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, +0x00, 0x80, 0x00, 0x80, 0x00, 0x80 }; @@ -326,6 +326,9 @@ static void RE_RegisterModels_DeleteAll(void) itModel = CachedModels.erase(itModel); } + + extern void RE_AnimationCFGs_DeleteAll(void); + RE_AnimationCFGs_DeleteAll(); } @@ -600,6 +603,10 @@ Ghoul2 Insert Start // for (mh=mhHashTable[hash]; mh; mh=mh->next) { if (Q_stricmp(mh->name, name) == 0) { + if (tr.models[mh->handle]->type == MOD_BAD) + { + return 0; + } return mh->handle; } } @@ -782,6 +789,7 @@ fail: // we still keep the model_t around, so if the model name is asked for // again, we won't bother scanning the filesystem mod->type = MOD_BAD; + RE_InsertModelIntoHash(name, mod); return 0; } @@ -798,7 +806,9 @@ qhandle_t RE_RegisterModel( const char *name ) qhandle_t q = RE_RegisterModel_Actual( name ); - gbInsideRegisterModel = qfalse; +if (stricmp(&name[strlen(name)-4],".gla")){ + gbInsideRegisterModel = qfalse; // GLA files recursively call this, so don't turn off half way. A reference count would be nice, but if any ERR_DROP ever occurs within the load then the refcount will be knackered from then on +} return q; } diff --git a/code/renderer/tr_public.h b/code/renderer/tr_public.h index 2462259..83b501e 100644 --- a/code/renderer/tr_public.h +++ b/code/renderer/tr_public.h @@ -26,6 +26,7 @@ typedef struct { void (*BeginRegistration)( glconfig_t *config ); qhandle_t (*RegisterModel)( const char *name ); qhandle_t (*RegisterSkin)( const char *name ); + int (*GetAnimationCFG)(const char *psCFGFilename, char *psDest, int iDestSize); qhandle_t (*RegisterShader)( const char *name ); qhandle_t (*RegisterShaderNoMip)( const char *name ); void (*LoadWorld)( const char *name ); diff --git a/code/renderer/tr_scene.cpp b/code/renderer/tr_scene.cpp index 931e30a..a938bd6 100644 --- a/code/renderer/tr_scene.cpp +++ b/code/renderer/tr_scene.cpp @@ -375,4 +375,5 @@ void RE_RenderScene( const refdef_t *fd ) { r_firstScenePoly = r_numpolys; tr.frontEndMsec += ri.Milliseconds() - startTime; + RE_RenderWorldEffects(); } diff --git a/code/renderer/tr_surface.cpp b/code/renderer/tr_surface.cpp index cfaa46e..789439b 100644 --- a/code/renderer/tr_surface.cpp +++ b/code/renderer/tr_surface.cpp @@ -722,10 +722,10 @@ static void DoBoltSeg( vec3_t start, vec3_t end, vec3_t right, float radius ) VectorSubtract( end, start, fwd ); dis = VectorNormalize( fwd ); - if (dis > 2048) //freaky long + if (dis > 2000) //freaky long { - ri.Printf( PRINT_WARNING, "DoBoltSeg: insane distance.\n" ); - dis = 2048; +// ri.Printf( PRINT_WARNING, "DoBoltSeg: insane distance.\n" ); + dis = 2000; } MakeNormalVectors( fwd, rt, up ); diff --git a/code/renderer/tr_worldeffects.cpp b/code/renderer/tr_worldeffects.cpp index 79d0ebd..534149e 100644 --- a/code/renderer/tr_worldeffects.cpp +++ b/code/renderer/tr_worldeffects.cpp @@ -1792,7 +1792,7 @@ CRainSystem::CRainSystem(int maxRain) : mMaxRain(maxRain), mNextWindGust(0), mRainHeight(5), - mAlpha(0.1f), + mAlpha(0.15f), mWindAngle(1.0f), mFadeAlpha(0.0f), @@ -1865,7 +1865,6 @@ void CRainSystem::Init(void) { item->pos[0] = ri.flrand(0.0, mSpread[0]); item->pos[1] = ri.flrand(0.0, mSpread[1]); - item->pos[2] = ri.flrand(-mSpread[2], mSpread[2]); item->pos[2] = ri.flrand(-mSpread[2], 40); item->velocity[0] = ri.flrand(mMinVelocity[0], mMaxVelocity[0]); item->velocity[1] = ri.flrand(mMinVelocity[1], mMaxVelocity[1]); @@ -1978,6 +1977,12 @@ void CRainSystem::Update(float elapseTime) SParticle *item; vec3_t windDifference; + if ( elapseTime < 0.0f ) + { + // sanity check + elapseTime = 0.0f; + } + mWindChange--; if (mWindChange < 0) @@ -2030,11 +2035,10 @@ void CRainSystem::Update(float elapseTime) { VectorMA(item->pos, elapseTime, item->velocity); - if (item->pos[2] < -mSpread[2]) + if (item->pos[2] < -mSpread[2] ) { item->pos[0] = ri.flrand(0.0, mSpread[0]); item->pos[1] = ri.flrand(0.0, mSpread[1]); - item->pos[2] = mSpread[2]; item->pos[2] = 40; item->velocity[0] = ri.flrand(mMinVelocity[0], mMaxVelocity[0]); diff --git a/code/renderer/vssver.scc b/code/renderer/vssver.scc deleted file mode 100644 index d795f3c..0000000 Binary files a/code/renderer/vssver.scc and /dev/null differ diff --git a/code/server/sv_ccmds.cpp b/code/server/sv_ccmds.cpp index cbf720c..6e3370b 100644 --- a/code/server/sv_ccmds.cpp +++ b/code/server/sv_ccmds.cpp @@ -133,6 +133,10 @@ void SV_Player_EndOfLevelSave(void) Cvar_Set( "playerfpknown", va( "%i", pState->forcePowersKnown)); Cvar_Set( "playerfp", va( "%i", pState->forcePower)); + Cvar_Set( "plsa", va( "%i", pState->saberActive)); + Cvar_Set( "plcs", va( "%i", pState->saberAnimLevel)); + Cvar_Set( "plle", va( "%i", pState->saberLockEnemy)); + Cvar_Set( "pllt", va( "%i", pState->saberLockTime)); } } diff --git a/code/server/sv_client.cpp b/code/server/sv_client.cpp index b6f7b1d..ef15388 100644 --- a/code/server/sv_client.cpp +++ b/code/server/sv_client.cpp @@ -493,6 +493,7 @@ static void SV_UserMove( client_t *cl, msg_t *msg ) { if ( eSavedGameJustLoaded == eNO ) { SG_WriteSavegame("auto",qtrue); + SG_WriteSavegame(va("auto_%s",sv_mapname->string),qtrue); } else if ( qbLoadTransition == qtrue ) { diff --git a/code/server/sv_game.cpp b/code/server/sv_game.cpp index ef5d5b2..6b9ca59 100644 --- a/code/server/sv_game.cpp +++ b/code/server/sv_game.cpp @@ -154,10 +154,11 @@ qboolean SV_inPVS (const vec3_t p1, const vec3_t p2) int cluster; int area1, area2; byte *mask; - int start, end; - - start = Sys_Milliseconds (); + int start=0; + if ( com_speeds->integer ) { + start = Sys_Milliseconds (); + } leafnum = CM_PointLeafnum (p1); cluster = CM_LeafCluster (leafnum); area1 = CM_LeafArea (leafnum); @@ -168,20 +169,21 @@ qboolean SV_inPVS (const vec3_t p1, const vec3_t p2) area2 = CM_LeafArea (leafnum); if ( mask && (!(mask[cluster>>3] & (1<<(cluster&7)) ) ) ) { - end = Sys_Milliseconds (); - timeInPVSCheck += end - start; + if ( com_speeds->integer ) { + timeInPVSCheck += Sys_Milliseconds () - start; + } return qfalse; } if (!CM_AreasConnected (area1, area2)) { - end = Sys_Milliseconds (); - timeInPVSCheck += end - start; + timeInPVSCheck += Sys_Milliseconds() - start; return qfalse; // a door blocks sight } - end = Sys_Milliseconds (); - timeInPVSCheck += end - start; + if ( com_speeds->integer ) { + timeInPVSCheck += Sys_Milliseconds() - start; + } return qtrue; } @@ -199,10 +201,12 @@ qboolean SV_inPVSIgnorePortals( const vec3_t p1, const vec3_t p2) int cluster; int area1, area2; byte *mask; - int start, end; - - start = Sys_Milliseconds (); + int start=0; + if ( com_speeds->integer ) { + start = Sys_Milliseconds (); + } + leafnum = CM_PointLeafnum (p1); cluster = CM_LeafCluster (leafnum); area1 = CM_LeafArea (leafnum); @@ -214,13 +218,15 @@ qboolean SV_inPVSIgnorePortals( const vec3_t p1, const vec3_t p2) if ( mask && (!(mask[cluster>>3] & (1<<(cluster&7)) ) ) ) { - end = Sys_Milliseconds (); - timeInPVSCheck += end - start; + if ( com_speeds->integer ) { + timeInPVSCheck += Sys_Milliseconds() - start; + } return qfalse; } - end = Sys_Milliseconds (); - timeInPVSCheck += end - start; + if ( com_speeds->integer ) { + timeInPVSCheck += Sys_Milliseconds() - start; + } return qtrue; } @@ -405,7 +411,6 @@ Ghoul2 Insert Start import.G2API_GetBoneAnim = G2API_GetBoneAnim; import.G2API_GetBoneAnimIndex = G2API_GetBoneAnimIndex; import.G2API_AddSurface = G2API_AddSurface; - import.G2API_GetSurfaceOnOff = G2API_GetSurfaceOnOff; import.G2API_HaveWeGhoul2Models =G2API_HaveWeGhoul2Models; import.G2API_InitGhoul2Model = G2API_InitGhoul2Model; import.G2API_IsPaused = G2API_IsPaused; @@ -456,6 +461,7 @@ Ghoul2 Insert Start import.G2API_GetSurfaceRenderStatus = G2API_GetSurfaceRenderStatus; import.RE_RegisterSkin = RE_RegisterSkin; + import.RE_GetAnimationCFG = RE_GetAnimationCFG; /* Ghoul2 Insert End diff --git a/code/server/sv_savegame.cpp b/code/server/sv_savegame.cpp index d071e16..9d4435a 100644 --- a/code/server/sv_savegame.cpp +++ b/code/server/sv_savegame.cpp @@ -13,7 +13,7 @@ extern byte *Compress_JPG(int *pOutputSize, int quality, int image_width, int im //#define USE_LAST_SAVE_FROM_THIS_MAP // enable this if you want to use the last explicity-loaded savegame from this map - // when respawning after dying, else it'll just load "auto" regardless + // when respawning after dying, else it'll just load "auto" regardless // (EF1 behaviour). I should maybe time/date check them though? #include "server.h" @@ -131,15 +131,17 @@ static qboolean SG_Copy( LPCSTR psPathlessBaseName_Src, LPCSTR psPathlessBaseNam if (!qbCopyWentOk) { - Com_Printf(S_COLOR_RED "Error during savegame-rename. Check \"%s\" for write-protect or disk full!\n", psLocalFilename_Dst ); + Com_Printf(S_COLOR_RED "Error during savegame-rename. Check \"%s\" for write-protect or disk full!\n", psLocalFilename_Dst ); return qfalse; } return qtrue; } +qboolean gbSGWriteFailed = qfalse; static qboolean SG_Create( LPCSTR psPathlessBaseName ) { + gbSGWriteFailed = qfalse; SG_WipeSavegame( psPathlessBaseName ); LPCSTR psLocalFilename = SG_AddSavePath( psPathlessBaseName ); @@ -148,7 +150,7 @@ static qboolean SG_Create( LPCSTR psPathlessBaseName ) if(!fhSaveGame) { - Com_Printf(S_COLOR_RED "Failed to create new savegame file \"%s\"\n", psLocalFilename ); + Com_Printf(S_COLOR_RED "Failed to create new savegame file \"%s\"\n", psLocalFilename ); return qfalse; } @@ -207,6 +209,7 @@ qboolean SG_Close() return qtrue; } + qboolean SG_Open( LPCSTR psPathlessBaseName ) { // if ( fhSaveGame ) // hmmm... @@ -433,9 +436,9 @@ extern void SCR_PrecacheScreenshot(); //scr_scrn.cpp return; } - //Com_Printf (S_COLOR_CYAN "Saving game \"%s\"...\n", psFilename); + Com_Printf (S_COLOR_CYAN "Saving game \"%s\"...\n", psFilename); SG_WriteSavegame(psFilename, qfalse); - //Com_Printf (S_COLOR_CYAN "Done.\n"); + Com_Printf (S_COLOR_CYAN "Done.\n"); } @@ -492,6 +495,18 @@ static void WriteGame(qboolean autosave) Cvar_VariableStringBuffer( "playerfp", s, sizeof(s) ); SG_Append('PLFP', &s, sizeof(s)); + + Cvar_VariableStringBuffer( "plsa", s, sizeof(s) ); + SG_Append('PLSA', &s, sizeof(s)); + + Cvar_VariableStringBuffer( "plcs", s, sizeof(s) ); + SG_Append('PLCS', &s, sizeof(s)); + + Cvar_VariableStringBuffer( "plle", s, sizeof(s) ); + SG_Append('PLLE', &s, sizeof(s)); + + Cvar_VariableStringBuffer( "pllt", s, sizeof(s) ); + SG_Append('PLLT', &s, sizeof(s)); } } @@ -540,6 +555,18 @@ static qboolean ReadGame (void) SG_Read('PLFP', (void *)&s, sizeof(s)); Cvar_Set( "playerfp", s ); + + SG_Read('PLSA', (void *)&s, sizeof(s)); + Cvar_Set( "plsa", s ); + + SG_Read('PLCS', (void *)&s, sizeof(s)); + Cvar_Set( "plcs", s ); + + SG_Read('PLLE', (void *)&s, sizeof(s)); + Cvar_Set( "plle", s ); + + SG_Read('PLLT', (void *)&s, sizeof(s)); + Cvar_Set( "pllt", s ); } return qbAutoSave; @@ -691,7 +718,7 @@ static void SG_WriteComment(qboolean qbAutosave, LPCSTR psMapName) { if (!*saveGameComment) { - Com_sprintf( sComment, sizeof(sComment), "-------> %s", psMapName ); + Com_sprintf( sComment, sizeof(sComment), "---> %s", psMapName ); } else { @@ -700,7 +727,7 @@ static void SG_WriteComment(qboolean qbAutosave, LPCSTR psMapName) } else { - Com_sprintf( sComment, sizeof(sComment), "-------> %s", psMapName ); + Com_sprintf( sComment, sizeof(sComment), "---> %s", psMapName ); } SG_Append('COMM', sComment, sizeof(sComment)); @@ -944,18 +971,16 @@ void SG_WriteSavegame(const char *psPathlessBaseName, qboolean qbAutosave) } ge->WriteLevel(qbAutosave); // always done now, but ent saver only does player if auto - if(!SG_Close()) + SG_Close(); + if (gbSGWriteFailed) { - Com_Printf (S_COLOR_RED "Failed to close savegame\n"); + Com_Printf (S_COLOR_RED "Failed to write savegame!\n"); SG_WipeSavegame( "current" ); sv_testsave->value = fPrevTestSave; return; } - - if (SG_Copy( "current", psPathlessBaseName )) - { - SG_WipeSavegame( "current" ); // may as well leave it there if failed to overwrite 'auto' - } + + SG_Copy( "current", psPathlessBaseName ); sv_testsave->value = fPrevTestSave; } @@ -1197,11 +1222,11 @@ qboolean SG_Append(unsigned long chid, void *pvData, int iLength) { if(*pTest == 0xcdcdcdcd) { - assert(0); + assert(*pTest != 0xcdcdcdcd); } if(*pTest == 0xdddddddd) { - assert(0); + assert(*pTest != 0xdddddddd); } } #endif @@ -1245,6 +1270,7 @@ qboolean SG_Append(unsigned long chid, void *pvData, int iLength) if (uiSaved != sizeof(chid) + sizeof(iLength) + sizeof(uiCksum) + sizeof(iCompressedLength) + iCompressedLength + sizeof(uiMagic)) { Com_Printf(S_COLOR_RED "Failed to write %s chunk\n", SG_GetChidText(chid)); + gbSGWriteFailed = qtrue; return qfalse; } } @@ -1269,6 +1295,7 @@ qboolean SG_Append(unsigned long chid, void *pvData, int iLength) if (uiSaved != sizeof(chid) + sizeof(iLength) + sizeof(uiCksum) + iLength + sizeof(uiMagic)) { Com_Printf(S_COLOR_RED "Failed to write %s chunk\n", SG_GetChidText(chid)); + gbSGWriteFailed = qtrue; return qfalse; } } diff --git a/code/server/sv_world.cpp b/code/server/sv_world.cpp index 95aa6d5..0796c17 100644 --- a/code/server/sv_world.cpp +++ b/code/server/sv_world.cpp @@ -30,6 +30,10 @@ Ghoul2 Insert End #define SV_TRACE_PROFILE (0) #endif +#if 0 //G2_SUPERSIZEDBBOX is not being used +static const float superSizedAdd=64.0f; +#endif + /* ================ SV_ClipHandleForEntity @@ -639,28 +643,39 @@ void SV_ClipMoveToEntities( moveclip_t *clip ) { angles = vec3_origin; // boxes don't rotate } -#ifdef __MACOS__ - // compiler bug with const - CM_TransformedBoxTrace ( &trace, (float *)clip->start, (float *)clip->end, - (float *)clip->mins, (float *)clip->maxs, clipHandle, clip->contentmask, - origin, angles); -#else - CM_TransformedBoxTrace ( &trace, clip->start, clip->end, - clip->mins, clip->maxs, clipHandle, clip->contentmask, - origin, angles); -#endif - //FIXME: when startsolid in another ent, doesn't return correct entityNum - //ALSO: 2 players can be standing next to each other and this function will - //think they're in each other!!! -/* -Ghoul2 Insert Start -*/ +#if 0 //G2_SUPERSIZEDBBOX is not being used + bool shrinkBox=true; - // keep these older variables around for a bit, incase we need to replace them in the Ghoul2 Collision check + if (clip->eG2TraceType != G2_SUPERSIZEDBBOX) + { + shrinkBox=false; + } + else if (trace.entityNum == touch->s.number&&touch->ghoul2.size()&&!(touch->contents & CONTENTS_LIGHTSABER)) + { + shrinkBox=false; + } + if (shrinkBox) + { + vec3_t sh_mins; + vec3_t sh_maxs; + int j; + for ( j=0 ; j<3 ; j++ ) + { + sh_mins[j]=clip->mins[j]+superSizedAdd; + sh_maxs[j]=clip->maxs[j]-superSizedAdd; + } + CM_TransformedBoxTrace ( &trace, clip->start, clip->end, + sh_mins, sh_maxs, clipHandle, clip->contentmask, + origin, angles); + } + else +#endif + { + CM_TransformedBoxTrace ( &trace, clip->start, clip->end, + clip->mins, clip->maxs, clipHandle, clip->contentmask, + origin, angles); + } oldTrace = clip->trace; -/* -Ghoul2 Insert End -*/ if ( trace.allsolid ) { @@ -720,6 +735,17 @@ Ghoul2 Insert Start } // if we are looking at an entity then use the player state to get it's angles and origin from + float radius; +#if 0 //G2_SUPERSIZEDBBOX is not being used + if (clip->eG2TraceType == G2_SUPERSIZEDBBOX) + { + radius=(clip->maxs[0]-clip->mins[0]-2.0f*superSizedAdd)/2.0f; + } + else +#endif + { + radius=(clip->maxs[0]-clip->mins[0])/2.0f; + } if (touch->client) { vec3_t world_angles; @@ -731,14 +757,14 @@ Ghoul2 Insert Start world_angles[ROLL] = 0; G2API_CollisionDetect(clip->trace.G2CollisionMap, touch->ghoul2, - world_angles, touch->client->origin, sv.time, touch->s.number, clip->start, clip->end, touch->s.modelScale, G2VertSpaceServer, clip->eG2TraceType, clip->useLod, (clip->maxs[0]-clip->mins[0])/2); + world_angles, touch->client->origin, sv.time, touch->s.number, clip->start, clip->end, touch->s.modelScale, G2VertSpaceServer, clip->eG2TraceType, clip->useLod,radius); } // no, so use the normal entity state else { //use the correct origin and angles! is this right now? G2API_CollisionDetect(clip->trace.G2CollisionMap, touch->ghoul2, - touch->currentAngles, touch->currentOrigin, sv.time, touch->s.number, clip->start, clip->end, touch->s.modelScale, G2VertSpaceServer, clip->eG2TraceType, clip->useLod, (clip->maxs[0]-clip->mins[0])/2); + touch->currentAngles, touch->currentOrigin, sv.time, touch->s.number, clip->start, clip->end, touch->s.modelScale, G2VertSpaceServer, clip->eG2TraceType, clip->useLod,radius); } // set our new trace record size @@ -758,9 +784,18 @@ Ghoul2 Insert Start } else//this trace was valid, so copy the best collision into quake trace place info { - clip->trace.plane.normal[0] = clip->trace.G2CollisionMap[0].mCollisionNormal[0]; - clip->trace.plane.normal[1] = clip->trace.G2CollisionMap[0].mCollisionNormal[1]; - clip->trace.plane.normal[2] = clip->trace.G2CollisionMap[0].mCollisionNormal[2]; + for (z=0;ztrace.G2CollisionMap[z].mEntityNum==touch->s.number) + { + clip->trace.plane.normal[0] = clip->trace.G2CollisionMap[z].mCollisionNormal[0]; + clip->trace.plane.normal[1] = clip->trace.G2CollisionMap[z].mCollisionNormal[1]; + clip->trace.plane.normal[2] = clip->trace.G2CollisionMap[z].mCollisionNormal[2]; + break; + } + } + assert(ztrace.plane.normal)>0.1f); } } } @@ -851,14 +886,33 @@ Ghoul2 Insert End clip.trace.fraction = 1.0f; //VectorCopy( end, clip.end ); - clip.mins = mins; - clip.maxs = maxs; - clip.passEntityNum = passEntityNum; - // create the bounding box of the entire move // we can limit it to the part of the move not // already clipped off by the world, which can be // a significant savings for line of sight and shot traces + clip.passEntityNum = passEntityNum; + +#if 0 //G2_SUPERSIZEDBBOX is not being used + vec3_t superMin; + vec3_t superMax; // prison, in boscobel + + if (eG2TraceType==G2_SUPERSIZEDBBOX) + { + for ( i=0 ; i<3 ; i++ ) + { + superMin[i]=mins[i]-superSizedAdd; + superMax[i]=maxs[i]+superSizedAdd; + } + clip.mins = superMin; + clip.maxs = superMax; + } + else +#endif + { + clip.mins = mins; + clip.maxs = maxs; + } + for ( i=0 ; i<3 ; i++ ) { if ( end[i] > start[i] ) { clip.boxmins[i] = clip.start[i] + clip.mins[i] - 1; diff --git a/code/server/vssver.scc b/code/server/vssver.scc deleted file mode 100644 index 06cdccb..0000000 Binary files a/code/server/vssver.scc and /dev/null differ diff --git a/code/smartheap/HA312W32.DLL b/code/smartheap/HA312W32.DLL deleted file mode 100644 index 2b1ea82..0000000 Binary files a/code/smartheap/HA312W32.DLL and /dev/null differ diff --git a/code/smartheap/HAW32M.LIB b/code/smartheap/HAW32M.LIB deleted file mode 100644 index 1fc259c..0000000 Binary files a/code/smartheap/HAW32M.LIB and /dev/null differ diff --git a/code/smartheap/SHW32.DLL b/code/smartheap/SHW32.DLL deleted file mode 100644 index 42b2f67..0000000 Binary files a/code/smartheap/SHW32.DLL and /dev/null differ diff --git a/code/smartheap/vssver.scc b/code/smartheap/vssver.scc deleted file mode 100644 index ce22e57..0000000 Binary files a/code/smartheap/vssver.scc and /dev/null differ diff --git a/code/starwars.dsp b/code/starwars.dsp index ed040b3..02471d4 100644 --- a/code/starwars.dsp +++ b/code/starwars.dsp @@ -226,22 +226,18 @@ SOURCE=.\client\OpenAL\alut.h # PROP Default_Filter "" # Begin Source File -SOURCE=.\smartheap\HA312W32.DLL +SOURCE=.\SHDebug\HA312W32.DLL !IF "$(CFG)" == "starwars - Win32 Release" -# PROP Exclude_From_Build 1 - !ELSEIF "$(CFG)" == "starwars - Win32 Debug" -# PROP Exclude_From_Build 1 - !ELSEIF "$(CFG)" == "starwars - Win32 FinalBuild" -# PROP Exclude_From_Build 1 - !ELSEIF "$(CFG)" == "starwars - Win32 SHDebug" +# PROP Exclude_From_Build 1 + !ENDIF # End Source File @@ -268,24 +264,7 @@ SOURCE=.\smartheap\HEAPAGNT.H # End Source File # Begin Source File -SOURCE=.\smartheap\SHW32.DLL - -!IF "$(CFG)" == "starwars - Win32 Release" - -# PROP Exclude_From_Build 1 - -!ELSEIF "$(CFG)" == "starwars - Win32 Debug" - -# PROP Exclude_From_Build 1 - -!ELSEIF "$(CFG)" == "starwars - Win32 FinalBuild" - -# PROP Exclude_From_Build 1 - -!ELSEIF "$(CFG)" == "starwars - Win32 SHDebug" - -!ENDIF - +SOURCE=.\SHDebug\SHW32.DLL # End Source File # Begin Source File diff --git a/code/tonet.bat b/code/tonet.bat deleted file mode 100644 index d004639..0000000 --- a/code/tonet.bat +++ /dev/null @@ -1,2 +0,0 @@ -xcopy/d/y release\jk2sp.exe w:\game -xcopy/d/y release\jk2gamex86.dll w:\game diff --git a/code/tosend.bat b/code/tosend.bat deleted file mode 100644 index 31ea74d..0000000 --- a/code/tosend.bat +++ /dev/null @@ -1,4 +0,0 @@ -xcopy/y finalbuild\jk2sp.exe c:\send\game -xcopy/y finalbuild\jk2gamex86.dll c:\send\game -del/s/q c:\send\game\base\saves -rd c:\send\game\base\saves diff --git a/code/ui/mssccprj.scc b/code/ui/mssccprj.scc deleted file mode 100644 index b702cff..0000000 --- a/code/ui/mssccprj.scc +++ /dev/null @@ -1,5 +0,0 @@ -SCC = This is a Source Code Control file - -[ui.dsp] -SCC_Aux_Path = "\\ravend\vss_projects\StarWars" -SCC_Project_Name = "$/code/ui", XVOAAAAA diff --git a/code/ui/ui.def b/code/ui/ui.def deleted file mode 100644 index b82b7f8..0000000 --- a/code/ui/ui.def +++ /dev/null @@ -1,4 +0,0 @@ -EXPORTS - GetUIAPI - vmMain - dllEntry \ No newline at end of file diff --git a/code/ui/ui.dsp b/code/ui/ui.dsp deleted file mode 100644 index 8aedec7..0000000 --- a/code/ui/ui.dsp +++ /dev/null @@ -1,245 +0,0 @@ -# Microsoft Developer Studio Project File - Name="ui" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 - -CFG=ui - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "ui.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "ui.mak" CFG="ui - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "ui - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE "ui - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE "ui - Win32 FinalBuild" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName ""$/Code/ui", QEMAAAAA" -# PROP Scc_LocalPath "." -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "ui - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "..\Release" -# PROP Intermediate_Dir "..\Release\ui" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "UI_EXPORTS" /YX /FD /c -# ADD CPP /nologo /G6 /W4 /GX /Zi /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "UI_EXPORTS" /YX /FD /c -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 -# ADD LINK32 /nologo /base:"0x40000000" /dll /map /debug /machine:I386 /out:"../Release/efuix86.dll" - -!ELSEIF "$(CFG)" == "ui - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "ui___Win32_Debug" -# PROP BASE Intermediate_Dir "ui___Win32_Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "..\Debug" -# PROP Intermediate_Dir "..\Debug\ui" -# PROP Ignore_Export_Lib 1 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "UI_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /G6 /W4 /Gm /Gi /GX /ZI /Od /I "..\ICARUS" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "UI_EXPORTS" /FR /YX /FD /GZ /c -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept -# ADD LINK32 /nologo /base:"0x40000000" /dll /debug /machine:I386 /out:"../Debug/efuix86.dll" /pdbtype:sept -# SUBTRACT LINK32 /pdb:none /map - -!ELSEIF "$(CFG)" == "ui - Win32 FinalBuild" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "ui___Win32_FinalBuild" -# PROP BASE Intermediate_Dir "ui___Win32_FinalBuild" -# PROP BASE Ignore_Export_Lib 0 -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "..\FinalBuild" -# PROP Intermediate_Dir "..\FinalBuild\ui" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /G6 /W3 /GX /Zi /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "UI_EXPORTS" /YX /FD /c -# ADD CPP /nologo /G6 /W3 /GX /Zi /O2 /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "UI_EXPORTS" /D "WIN32" /D "NDEBUG" /D "FINAL_BUILD" /YX /FD /c -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 /nologo /base:"0x40000000" /dll /map /debug /machine:I386 /out:"../Release/efuix86.dll" -# ADD LINK32 /nologo /base:"0x40000000" /dll /map /debug /machine:I386 /out:"../FinalBuild/efuix86.dll" - -!ENDIF - -# Begin Target - -# Name "ui - Win32 Release" -# Name "ui - Win32 Debug" -# Name "ui - Win32 FinalBuild" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=..\game\q_math.cpp -# ADD CPP /Yu"common_headers.h" -# End Source File -# Begin Source File - -SOURCE=..\game\q_shared.cpp -# ADD CPP /Yu"common_headers.h" -# End Source File -# Begin Source File - -SOURCE=.\ui_atoms.cpp -# ADD CPP /Yu"ui_local.h" -# End Source File -# Begin Source File - -SOURCE=.\ui_connect.cpp -# ADD CPP /Yu"ui_local.h" -# End Source File -# Begin Source File - -SOURCE=.\ui_headers.cpp -# ADD CPP /Yc"ui_local.h" -# End Source File -# Begin Source File - -SOURCE=.\ui_main.cpp -# ADD CPP /Yu"ui_local.h" -# End Source File -# Begin Source File - -SOURCE=.\ui_shared.cpp -# ADD CPP /Yu"ui_local.h" -# End Source File -# Begin Source File - -SOURCE=.\ui_syscalls.cpp -# ADD CPP /Yu"ui_local.h" -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=..\game\channels.h -# End Source File -# Begin Source File - -SOURCE=..\qcommon\cm_public.h -# End Source File -# Begin Source File - -SOURCE=..\game\g_infostrings.h -# End Source File -# Begin Source File - -SOURCE=.\gameinfo.h -# End Source File -# Begin Source File - -SOURCE=..\game\ghoul2_shared.h -# End Source File -# Begin Source File - -SOURCE=..\client\keycodes.h -# End Source File -# Begin Source File - -SOURCE=.\menudef.h -# End Source File -# Begin Source File - -SOURCE=..\game\q_shared.h -# End Source File -# Begin Source File - -SOURCE=..\qcommon\qcommon.h -# End Source File -# Begin Source File - -SOURCE=..\qcommon\qfiles.h -# End Source File -# Begin Source File - -SOURCE=..\qcommon\stv_version.h -# End Source File -# Begin Source File - -SOURCE=..\game\surfaceflags.h -# End Source File -# Begin Source File - -SOURCE=..\qcommon\tags.h -# End Source File -# Begin Source File - -SOURCE=.\ui_headers.h -# End Source File -# Begin Source File - -SOURCE=.\ui_local.h -# End Source File -# Begin Source File - -SOURCE=.\ui_public.h -# End Source File -# Begin Source File - -SOURCE=.\ui_shared.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# Begin Source File - -SOURCE=.\ui.def -# End Source File -# End Target -# End Project diff --git a/code/ui/ui_local.h b/code/ui/ui_local.h index 4ea4844..9159c7b 100644 --- a/code/ui/ui_local.h +++ b/code/ui/ui_local.h @@ -11,7 +11,8 @@ #include "ui_shared.h" -#define MAX_DEMOS 256 +#define MAX_DEMOS 256 +#define MAX_DEFERRED_SCRIPT 1024 // // ui_qmenu.c @@ -115,6 +116,10 @@ typedef struct { int previewMovie; int modCount; + + char deferredScript [ MAX_DEFERRED_SCRIPT ]; + itemDef_t* deferredScriptItem; + } uiInfo_t; extern uiInfo_t uiInfo; @@ -148,6 +153,8 @@ void trap_Key_SetOverstrikeMode( qboolean state ); void trap_R_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader ); void trap_R_ModelBounds( clipHandle_t model, vec3_t mins, vec3_t maxs ); void trap_R_SetColor( const float *rgba ); +void trap_R_ClearScene( void ); +void trap_R_AddRefEntityToScene( const refEntity_t *re ); void trap_R_RenderScene( const refdef_t *fd ); sfxHandle_t trap_S_RegisterSound( const char *sample, qboolean compressed ); void trap_S_StartLocalSound( sfxHandle_t sfx, int channelNum ); diff --git a/code/ui/ui_main.cpp b/code/ui/ui_main.cpp index 9ff2148..4517146 100644 --- a/code/ui/ui_main.cpp +++ b/code/ui/ui_main.cpp @@ -62,16 +62,15 @@ int Text_Width(const char *text, float scale, int iFontIndex ); void _UI_DrawTopBottom(float x, float y, float w, float h, float size); void _UI_DrawSides(float x, float y, float w, float h, float size); void UI_CheckVid1Data(const char *menuTo,const char *warningMenuName); -void UI_GetVid1Data(void); +void UI_GetVideoSetup ( void ); +void UI_UpdateVideoSetup ( void ); void UI_LoadMenus(const char *menuFile, qboolean reset); static void UI_OwnerDraw(float x, float y, float w, float h, float text_x, float text_y, int ownerDraw, int ownerDrawFlags, int align, float special, float scale, vec4_t color, qhandle_t shader, int textStyle, int iFontIndex); static qboolean UI_OwnerDrawVisible(int flags); int UI_OwnerDrawWidth(int ownerDraw, float scale); static void UI_Update(const char *name); void UI_UpdateCvars( void ); -void UI_UpdateVid1Data(void); void UI_ResetDefaults( void ); -void UI_SetVid1Data(const char *menuName); static int gamecodetoui[] = {4,2,3,0,5,1,6}; @@ -171,7 +170,7 @@ void _UI_Refresh( int realtime ) { if (uiInfo.uiDC.cursorShow == qtrue) { - UI_DrawHandlePic( uiInfo.uiDC.cursorx, uiInfo.uiDC.cursory, 32, 32, uiInfo.uiDC.Assets.cursor); + UI_DrawHandlePic( uiInfo.uiDC.cursorx, uiInfo.uiDC.cursory, 48, 48, uiInfo.uiDC.Assets.cursor); } } } @@ -385,12 +384,56 @@ static int CreateNextSaveName(char *fileName) return qfalse; } +/* +=============== +UI_DeferMenuScript + +Return true if the menu script should be deferred for later +=============== +*/ +static qboolean UI_DeferMenuScript ( const char **args ) +{ + const char* name; + + // Whats the reason for being deferred? + if (!String_Parse(args, &name)) + { + return qfalse; + } + + // Handle the custom cases + if ( !Q_stricmp ( name, "VideoSetup" ) ) + { + const char* warningMenuName; + qboolean deferred; + + // No warning menu specified + if ( !String_Parse(args, &warningMenuName) ) + { + return qfalse; + } + + // Defer if the video options were modified + deferred = Cvar_VariableValue ( "ui_r_modified" ) ? qtrue : qfalse; + + if ( deferred ) + { + // Open the warning menu + Menus_OpenByName(warningMenuName); + } + + return deferred; + } + + return qfalse; +} + /* =============== UI_RunMenuScript =============== */ -static void UI_RunMenuScript(const char **args) +static qboolean UI_RunMenuScript ( const char **args ) { const char *name, *name2,*mapName,*menuName,*warningMenuName; @@ -416,13 +459,11 @@ static void UI_RunMenuScript(const char **args) } else if (Q_stricmp(name, "saveControls") == 0) { - // FIXME BOB : is this correct? - Controls_SetConfig2(qtrue); + Controls_SetConfig(qtrue); } else if (Q_stricmp(name, "loadControls") == 0) { - // FIXME BOB : is this correct? - Controls_GetConfig2(); + Controls_GetConfig(); } else if (Q_stricmp(name, "clearError") == 0) { @@ -517,18 +558,13 @@ static void UI_RunMenuScript(const char **args) Menus_CloseAll(); Menus_ActivateByName("mainMenu"); } - else if (Q_stricmp(name, "getvid1data") == 0) + else if (Q_stricmp(name, "getvideosetup") == 0) { - UI_GetVid1Data(); + UI_GetVideoSetup ( ); } - else if (Q_stricmp(name, "setvid1data") == 0) + else if (Q_stricmp(name, "updatevideosetup") == 0) { - String_Parse(args, &menuName); - UI_SetVid1Data(menuName); - } - else if (Q_stricmp(name, "updatevid1data") == 0) - { - UI_UpdateVid1Data(); + UI_UpdateVideoSetup ( ); } else if (Q_stricmp(name, "nextDataPadForcePower") == 0) { @@ -597,7 +633,7 @@ static void UI_RunMenuScript(const char **args) } else if (Q_stricmp(name, "glCustom") == 0) { - Cvar_Set("ui_glCustom", "4"); + Cvar_Set("ui_r_glCustom", "4"); } else if (Q_stricmp(name, "update") == 0) { @@ -611,6 +647,8 @@ static void UI_RunMenuScript(const char **args) } } } + + return qtrue; } /* @@ -797,8 +835,12 @@ void _UI_Init( qboolean inGameLoad ) uiInfo.uiDC.ownerDrawItem = &UI_OwnerDraw; uiInfo.uiDC.Print = &Com_Printf; uiInfo.uiDC.registerSound = &trap_S_RegisterSound; + uiInfo.uiDC.registerModel = ui.R_RegisterModel; + uiInfo.uiDC.clearScene = &trap_R_ClearScene; + uiInfo.uiDC.addRefEntityToScene = &trap_R_AddRefEntityToScene; uiInfo.uiDC.renderScene = &trap_R_RenderScene; uiInfo.uiDC.runScript = &UI_RunMenuScript; + uiInfo.uiDC.deferScript = &UI_DeferMenuScript; uiInfo.uiDC.setBinding = &trap_Key_SetBinding; uiInfo.uiDC.setColor = &UI_SetColor; uiInfo.uiDC.setCVar = Cvar_Set; @@ -1392,102 +1434,118 @@ static void UI_Update(const char *name) { Cvar_Set( "ui_Name", UI_Cvar_VariableString("name")); } - else if (Q_stricmp(name, "r_colorbits") == 0) + else if (Q_stricmp(name, "ui_r_colorbits") == 0) { switch (val) { case 0: - Cvar_SetValue( "r_depthbits", 0 ); - Cvar_SetValue( "r_stencilbits", 8 ); - break; + Cvar_SetValue( "ui_r_depthbits", 0 ); + Cvar_SetValue( "ui_r_stencilbits", 8 ); + break; + case 16: - Cvar_SetValue( "r_depthbits", 16 ); - Cvar_SetValue( "r_stencilbits", 0 ); - break; + Cvar_SetValue( "ui_r_depthbits", 16 ); + Cvar_SetValue( "ui_r_stencilbits", 0 ); + break; + case 32: - Cvar_SetValue( "r_depthbits", 24 ); - Cvar_SetValue( "r_stencilbits", 8 ); - break; + Cvar_SetValue( "ui_r_depthbits", 24 ); + Cvar_SetValue( "ui_r_stencilbits", 8 ); + break; } - } else if (Q_stricmp(name, "r_lodbias") == 0) { - switch (val) { + } + else if (Q_stricmp(name, "ui_r_lodbias") == 0) + { + switch (val) + { case 0: - Cvar_SetValue( "r_subdivisions", 4 ); - break; + Cvar_SetValue( "ui_r_subdivisions", 4 ); + break; case 1: - Cvar_SetValue( "r_subdivisions", 12 ); - break; + Cvar_SetValue( "ui_r_subdivisions", 12 ); + break; + case 2: - Cvar_SetValue( "r_subdivisions", 20 ); - break; + Cvar_SetValue( "ui_r_subdivisions", 20 ); + break; } - } else if (Q_stricmp(name, "ui_glCustom") == 0) { - switch (val) { + } + else if (Q_stricmp(name, "ui_r_glCustom") == 0) + { + switch (val) + { case 0: // high quality - Cvar_SetValue( "r_fullScreen", 1 ); - Cvar_SetValue( "r_subdivisions", 4 ); - Cvar_SetValue( "r_vertexlight", 0 ); - Cvar_SetValue( "r_lodbias", 0 ); - Cvar_SetValue( "r_colorbits", 32 ); - Cvar_SetValue( "r_depthbits", 24 ); - Cvar_SetValue( "r_picmip", 0 ); - Cvar_SetValue( "r_mode", 4 ); - Cvar_SetValue( "r_texturebits", 32 ); - Cvar_SetValue( "r_fastSky", 0 ); - Cvar_SetValue( "r_inGameVideo", 1 ); - Cvar_SetValue( "cg_shadows", 2 );//stencil - Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_LINEAR" ); - break; + + Cvar_SetValue( "ui_r_fullScreen", 1 ); + Cvar_SetValue( "ui_r_subdivisions", 4 ); + Cvar_SetValue( "ui_r_lodbias", 0 ); + Cvar_SetValue( "ui_r_colorbits", 32 ); + Cvar_SetValue( "ui_r_depthbits", 24 ); + Cvar_SetValue( "ui_r_picmip", 0 ); + Cvar_SetValue( "ui_r_mode", 4 ); + Cvar_SetValue( "ui_r_texturebits", 32 ); + Cvar_SetValue( "ui_r_fastSky", 0 ); + Cvar_SetValue( "ui_r_inGameVideo", 1 ); + Cvar_SetValue( "ui_cg_shadows", 2 );//stencil + Cvar_Set( "ui_r_texturemode", "GL_LINEAR_MIPMAP_LINEAR" ); + break; + case 1: // normal - Cvar_SetValue( "r_fullScreen", 1 ); - Cvar_SetValue( "r_subdivisions", 12 ); - Cvar_SetValue( "r_vertexlight", 0 ); - Cvar_SetValue( "r_lodbias", 0 ); - Cvar_SetValue( "r_colorbits", 0 ); - Cvar_SetValue( "r_depthbits", 24 ); - Cvar_SetValue( "r_picmip", 0 ); - Cvar_SetValue( "r_mode", 3 ); - Cvar_SetValue( "r_texturebits", 0 ); - Cvar_SetValue( "r_fastSky", 0 ); - Cvar_SetValue( "r_inGameVideo", 1 ); - Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_LINEAR" ); - Cvar_SetValue( "cg_shadows", 2 ); - break; + Cvar_SetValue( "ui_r_fullScreen", 1 ); + Cvar_SetValue( "ui_r_subdivisions", 12 ); + Cvar_SetValue( "ui_r_lodbias", 0 ); + Cvar_SetValue( "ui_r_colorbits", 0 ); + Cvar_SetValue( "ui_r_depthbits", 24 ); + Cvar_SetValue( "ui_r_picmip", 0 ); + Cvar_SetValue( "ui_r_mode", 3 ); + Cvar_SetValue( "ui_r_texturebits", 0 ); + Cvar_SetValue( "ui_r_fastSky", 0 ); + Cvar_SetValue( "ui_r_inGameVideo", 1 ); + Cvar_Set( "ui_r_texturemode", "GL_LINEAR_MIPMAP_LINEAR" ); + Cvar_SetValue( "ui_cg_shadows", 2 ); + break; + case 2: // fast - Cvar_SetValue( "r_fullScreen", 1 ); - Cvar_SetValue( "r_subdivisions", 8 ); - Cvar_SetValue( "r_vertexlight", 0 ); - Cvar_SetValue( "r_lodbias", 1 ); - Cvar_SetValue( "r_colorbits", 0 ); - Cvar_SetValue( "r_depthbits", 0 ); - Cvar_SetValue( "r_picmip", 1 ); - Cvar_SetValue( "r_mode", 3 ); - Cvar_SetValue( "r_texturebits", 0 ); - Cvar_SetValue( "cg_shadows", 1 ); - Cvar_SetValue( "r_fastSky", 1 ); - Cvar_SetValue( "r_inGameVideo", 0 ); - Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_NEAREST" ); - break; + + Cvar_SetValue( "ui_r_fullScreen", 1 ); + Cvar_SetValue( "ui_r_subdivisions", 8 ); + Cvar_SetValue( "ui_r_lodbias", 1 ); + Cvar_SetValue( "ui_r_colorbits", 0 ); + Cvar_SetValue( "ui_r_depthbits", 0 ); + Cvar_SetValue( "ui_r_picmip", 1 ); + Cvar_SetValue( "ui_r_mode", 3 ); + Cvar_SetValue( "ui_r_texturebits", 0 ); + Cvar_SetValue( "ui_cg_shadows", 1 ); + Cvar_SetValue( "ui_r_fastSky", 1 ); + Cvar_SetValue( "ui_r_inGameVideo", 0 ); + Cvar_Set( "ui_r_texturemode", "GL_LINEAR_MIPMAP_NEAREST" ); + break; + case 3: // fastest - Cvar_SetValue( "r_fullScreen", 1 ); - Cvar_SetValue( "r_subdivisions", 20 ); - Cvar_SetValue( "r_vertexlight", 1 ); - Cvar_SetValue( "r_lodbias", 2 ); - Cvar_SetValue( "r_colorbits", 16 ); - Cvar_SetValue( "r_depthbits", 16 ); - Cvar_SetValue( "r_mode", 3 ); - Cvar_SetValue( "r_picmip", 2 ); - Cvar_SetValue( "r_texturebits", 16 ); - Cvar_SetValue( "cg_shadows", 0 ); - Cvar_SetValue( "r_fastSky", 1 ); - Cvar_SetValue( "r_inGameVideo", 0 ); - Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_NEAREST" ); + + Cvar_SetValue( "ui_r_fullScreen", 1 ); + Cvar_SetValue( "ui_r_subdivisions", 20 ); + Cvar_SetValue( "ui_r_lodbias", 2 ); + Cvar_SetValue( "ui_r_colorbits", 16 ); + Cvar_SetValue( "ui_r_depthbits", 16 ); + Cvar_SetValue( "ui_r_mode", 3 ); + Cvar_SetValue( "ui_r_picmip", 2 ); + Cvar_SetValue( "ui_r_texturebits", 16 ); + Cvar_SetValue( "ui_cg_shadows", 0 ); + Cvar_SetValue( "ui_r_fastSky", 1 ); + Cvar_SetValue( "ui_r_inGameVideo", 0 ); + Cvar_Set( "ui_r_texturemode", "GL_LINEAR_MIPMAP_NEAREST" ); break; } - } else if (Q_stricmp(name, "ui_mousePitch") == 0) { - if (val == 0) { + } + else if (Q_stricmp(name, "ui_mousePitch") == 0) + { + if (val == 0) + { Cvar_SetValue( "m_pitch", 0.022f ); - } else { + } + else + { Cvar_SetValue( "m_pitch", -0.022f ); } } @@ -1620,7 +1678,7 @@ static void UI_DrawKeyBindStatus(rectDef_t *rect, float scale, vec4_t color, int } else { - Text_Paint(rect->x, rect->y, scale, color, ui.SP_GetStringTextString("MENUS_ENTERTOCHANGE"), 0, textStyle, iFontIndex); +// Text_Paint(rect->x, rect->y, scale, color, ui.SP_GetStringTextString("MENUS_ENTERTOCHANGE"), 0, textStyle, iFontIndex); } } @@ -2006,7 +2064,7 @@ int UI_OwnerDrawWidth(int ownerDraw, float scale) } else { - s = ui.SP_GetStringTextString("MENUS_ENTERTOCHANGE"); +// s = ui.SP_GetStringTextString("MENUS_ENTERTOCHANGE"); } break; @@ -2086,6 +2144,11 @@ UI_KeyEvent */ void _UI_KeyEvent( int key, qboolean down ) { +/* extern qboolean SwallowBadNumLockedKPKey( int iKey ); + if (SwallowBadNumLockedKPKey(key)){ + return; + } +*/ if (Menu_Count() > 0) { @@ -2153,11 +2216,27 @@ UI_DataPadMenu */ void UI_DataPadMenu(void) { + int newForcePower,newObjective; + ui.PrecacheScreenshot(); Menus_CloseByName("mainhud"); - Menus_ActivateByName("datapadMissionMenu"); + newForcePower = (int)trap_Cvar_VariableValue("cg_updatedDataPadForcePower"); + newObjective = (int)trap_Cvar_VariableValue("cg_updatedDataPadObjective"); + + if (newForcePower) + { + Menus_ActivateByName("datapadForcePowersMenu"); + } + else if (newObjective) + { + Menus_ActivateByName("datapadMissionMenu"); + } + else + { + Menus_ActivateByName("datapadMissionMenu"); + } ui.Key_SetCatcher( KEYCATCH_UI ); } @@ -2246,325 +2325,86 @@ void Menu_Cache( void ) /* ================= -UI_GetVid1Data +UI_UpdateVideoSetup + +Copies the temporary user interface version of the video cvars into +their real counterparts. This is to create a interface which allows +you to discard your changes if you did something you didnt want ================= */ -void UI_GetVid1Data(void) +void UI_UpdateVideoSetup ( void ) { - itemDef_t *gl_extensions,*video_mode,*color_depth,*fullscreen,*geometric_detail, - *texture_detail,*texture_quality,*texture_filter,*simple_shaders,*compressed_textures; - menuDef_t *menu; - - menu = Menu_GetFocused(); // Get current menu (either video or ingame video, I would assume) - - if (!menu) - { - Com_Printf(S_COLOR_YELLOW"WARNING: No videoMenu was found. Video data could not be set\n"); - return; - } - - gl_extensions = (itemDef_s *) Menu_FindItemByName(menu, "gl_extensions"); - video_mode = (itemDef_s *) Menu_FindItemByName(menu, "video_mode"); - color_depth = (itemDef_s *) Menu_FindItemByName(menu, "color_depth"); - fullscreen = (itemDef_s *) Menu_FindItemByName(menu, "fullscreen"); - geometric_detail = (itemDef_s *) Menu_FindItemByName(menu, "geometric_detail"); - texture_detail = (itemDef_s *) Menu_FindItemByName(menu, "texture_detail"); - texture_quality = (itemDef_s *) Menu_FindItemByName(menu, "texture_quality"); - texture_filter = (itemDef_s *) Menu_FindItemByName(menu, "texture_filter"); - simple_shaders = (itemDef_s *) Menu_FindItemByName(menu, "simple_shaders"); - compressed_textures = (itemDef_s *) Menu_FindItemByName(menu, "compressed_textures"); - - if ((!gl_extensions) || (!video_mode) || (!color_depth) || - (!fullscreen) || (!geometric_detail) || (!texture_detail) || (!texture_quality) || - (!texture_filter) || (!simple_shaders) || (!compressed_textures)) - { - Com_Printf(S_COLOR_YELLOW"WARNING: Unable to locate a video data1 field.\n"); - return; - } - - // GL Driver info - gl_extensions->value = ui.Cvar_VariableValue("r_allowExtensions"); - - // Video mode info - video_mode->value = ui.Cvar_VariableValue( "r_mode" ) - 3; - if ( video_mode->value < 0 ) - { - video_mode->value = 1; - } - - switch ( ( int ) ui.Cvar_VariableValue( "r_colorbits" ) ) - { - default: - case 0: - color_depth->value = 0; - break; - case 16: - color_depth->value = 1; - break; - case 32: - color_depth->value = 2; - break; - } - - // Get fullscreen cvar value - fullscreen->value = ui.Cvar_VariableValue("r_fullscreen"); - - // Get geometric detail - if ( ui.Cvar_VariableValue( "r_lodBias" ) > 0 ) - { - if ( ui.Cvar_VariableValue( "r_subdivisions" ) >= 20 ) - { - geometric_detail->value = 0; // Setting LOW - } - else - { - geometric_detail->value = 1; // Setting MED - } - } - else - { - geometric_detail->value = 2; // Setting HIGH - } - - - texture_detail->value = 3-ui.Cvar_VariableValue( "r_picmip"); - if ( texture_detail->value < 0 ) - { - texture_detail->value = 0; - } - else if ( texture_detail->value > 3 ) - { - texture_detail->value = 3; - } - - switch ( ( int ) ui.Cvar_VariableValue( "r_texturebits" ) ) - { - default: - case 0: - texture_quality->value = 0; - break; - case 16: - texture_quality->value = 1; - break; - case 32: - texture_quality->value = 2; - break; - } - - if ( !Q_stricmp( UI_Cvar_VariableString( "r_textureMode" ), "GL_LINEAR_MIPMAP_NEAREST" ) ) - { - texture_filter->value = 0; - } - else - { - texture_filter->value = 1; - } - - simple_shaders->value = ui.Cvar_VariableValue("r_detailtextures"); - - compressed_textures->value = ui.Cvar_VariableValue("r_ext_compress_textures"); - - if ( fullscreen->value == 0 ) - { - color_depth->value = 0; - color_depth->window.flags |= WINDOW_INACTIVE; - } - else - { - color_depth->window.flags &= ~WINDOW_INACTIVE; - } -} - -/* -================= -UI_SetVid1Data -================= -*/ -void UI_SetVid1Data(const char *menuName) -{ - itemDef_t *gl_extensions,*video_mode,*color_depth,*fullscreen,*geometric_detail, - *texture_detail,*texture_quality,*texture_filter,*simple_shaders,*compressed_textures; - menuDef_t *menu; - - menu = Menus_FindByName(menuName); // Get current menu (either video or ingame video, I would assume) - - if (!menu) - { - Com_Printf(S_COLOR_YELLOW"WARNING: No videoMenu was found. Video data could not be set\n"); - return; - } - - gl_extensions = (itemDef_s *) Menu_FindItemByName(menu, "gl_extensions"); - video_mode = (itemDef_s *) Menu_FindItemByName(menu, "video_mode"); - color_depth = (itemDef_s *) Menu_FindItemByName(menu, "color_depth"); - fullscreen = (itemDef_s *) Menu_FindItemByName(menu, "fullscreen"); - geometric_detail = (itemDef_s *) Menu_FindItemByName(menu, "geometric_detail"); - texture_detail = (itemDef_s *) Menu_FindItemByName(menu, "texture_detail"); - texture_quality = (itemDef_s *) Menu_FindItemByName(menu, "texture_quality"); - texture_filter = (itemDef_s *) Menu_FindItemByName(menu, "texture_filter"); - simple_shaders = (itemDef_s *) Menu_FindItemByName(menu, "simple_shaders"); - compressed_textures = (itemDef_s *) Menu_FindItemByName(menu, "compressed_textures"); - - if ((!gl_extensions) || (!video_mode) || (!color_depth) || - (!fullscreen) || (!geometric_detail) || (!texture_detail) || (!texture_quality) || - (!texture_filter) || (!simple_shaders) || (!compressed_textures)) - { - Com_Printf(S_COLOR_YELLOW"WARNING: Unable to locate a video data1 field.\n"); - return; - } - - - // Set GL Driver info - // GL Extensions - ui.Cvar_SetValue( "r_allowExtensions", gl_extensions->value ); - - // Adding 3 because we don't show 320x200 , 400X300, or 512x384 - // Video Resolution Setting - ui.Cvar_SetValue( "r_mode", (video_mode->value + 3) ); - - // Color Depth - switch ( color_depth->value ) - { - case 0: - ui.Cvar_SetValue( "r_colorbits", 0 ); - ui.Cvar_SetValue( "r_depthbits", 0 ); - ui.Cvar_SetValue( "r_stencilbits", 8 ); - break; - case 1: - ui.Cvar_SetValue( "r_colorbits", 16 ); - ui.Cvar_SetValue( "r_depthbits", 16 ); - ui.Cvar_SetValue( "r_stencilbits", 0 ); - break; - case 2: - ui.Cvar_SetValue( "r_colorbits", 32 ); - ui.Cvar_SetValue( "r_depthbits", 24 ); - ui.Cvar_SetValue( "r_stencilbits", 8 ); - break; - } - - // Set Fullscreen cvar - fullscreen = (itemDef_s *) Menu_FindItemByName(menu, "fullscreen"); - if (fullscreen) - { - ui.Cvar_SetValue( "r_fullscreen", fullscreen->value ); - } - - // Geometric Detail - if ( geometric_detail->value == 2) // Setting is HIGH - { - ui.Cvar_SetValue( "r_lodBias", 0 ); - ui.Cvar_SetValue( "r_subdivisions", 4 ); - } - else if ( geometric_detail->value == 1) // Setting is MED - { - ui.Cvar_SetValue( "r_lodBias", 1 ); - ui.Cvar_SetValue( "r_subdivisions", 12 ); - } - else // Setting is LOW - { - ui.Cvar_SetValue( "r_lodBias", 1 ); - ui.Cvar_SetValue( "r_subdivisions", 20 ); - } - - // Texture detail - ui.Cvar_SetValue( "r_picmip", 3 - texture_detail->value ); - - // Texture Quality - switch ( texture_quality->value ) - { - case 0: - ui.Cvar_SetValue( "r_texturebits", 0 ); - break; - case 1: - ui.Cvar_SetValue( "r_texturebits", 16 ); - break; - case 2: - ui.Cvar_SetValue( "r_texturebits", 32 ); - break; - } - - if ( texture_filter->value ) - { - ui.Cvar_Set( "r_textureMode", "GL_LINEAR_MIPMAP_LINEAR" ); - } - else - { - ui.Cvar_Set( "r_textureMode", "GL_LINEAR_MIPMAP_NEAREST" ); - } - - ui.Cvar_SetValue( "r_detailtextures", simple_shaders->value ); - - ui.Cvar_SetValue( "r_ext_compress_textures", compressed_textures->value ); - + Cvar_Set ( "r_glCustom", Cvar_VariableString ( "ui_r_glCustom" ) ); + Cvar_Set ( "r_mode", Cvar_VariableString ( "ui_r_mode" ) ); + Cvar_Set ( "r_fullscreen", Cvar_VariableString ( "ui_r_fullscreen" ) ); + Cvar_Set ( "r_colorbits", Cvar_VariableString ( "ui_r_colorbits" ) ); + Cvar_Set ( "r_lodbias", Cvar_VariableString ( "ui_r_lodbias" ) ); + Cvar_Set ( "r_picmip", Cvar_VariableString ( "ui_r_picmip" ) ); + Cvar_Set ( "r_texturebits", Cvar_VariableString ( "ui_r_texturebits" ) ); + Cvar_Set ( "r_texturemode", Cvar_VariableString ( "ui_r_texturemode" ) ); + Cvar_Set ( "r_detailtextures", Cvar_VariableString ( "ui_r_detailtextures" ) ); + Cvar_Set ( "r_ext_compress_textures", Cvar_VariableString ( "ui_r_ext_compress_textures" ) ); + Cvar_Set ( "r_depthbits", Cvar_VariableString ( "ui_r_depthbits" ) ); + Cvar_Set ( "r_subdivisions", Cvar_VariableString ( "ui_r_subdivisions" ) ); + Cvar_Set ( "r_fastSky", Cvar_VariableString ( "ui_r_fastSky" ) ); + Cvar_Set ( "r_inGameVideo", Cvar_VariableString ( "ui_r_inGameVideo" ) ); + Cvar_Set ( "cg_shadows", Cvar_VariableString ( "ui_cg_shadows" ) ); + Cvar_Set ( "ui_r_modified", "0" ); Cbuf_ExecuteText( EXEC_APPEND, "vid_restart;" ); - } - /* ================= -UI_UpdateVid1Data +UI_GetVideoSetup + +Retrieves the current actual video settings into the temporary user +interface versions of the cvars. ================= */ -void UI_UpdateVid1Data(void) +void UI_GetVideoSetup ( void ) { - itemDef_t *gl_extensions,*video_mode,*color_depth,*fullscreen,*geometric_detail, - *texture_detail,*texture_quality,*texture_filter,*compressed_textures; - menuDef_t *menu; - - menu = Menu_GetFocused(); // Get current menu (either video or ingame video, I would assume) - - if (!menu) - { - Com_Printf(S_COLOR_YELLOW"WARNING: No videoMenu was found. Video data could not be set\n"); - return; - } - - gl_extensions = (itemDef_s *) Menu_FindItemByName(menu, "gl_extensions"); - video_mode = (itemDef_s *) Menu_FindItemByName(menu, "video_mode"); - color_depth = (itemDef_s *) Menu_FindItemByName(menu, "color_depth"); - fullscreen = (itemDef_s *) Menu_FindItemByName(menu, "fullscreen"); - geometric_detail = (itemDef_s *) Menu_FindItemByName(menu, "geometric_detail"); - texture_detail = (itemDef_s *) Menu_FindItemByName(menu, "texture_detail"); - texture_quality = (itemDef_s *) Menu_FindItemByName(menu, "texture_quality"); - texture_filter = (itemDef_s *) Menu_FindItemByName(menu, "texture_filter"); - compressed_textures = (itemDef_s *) Menu_FindItemByName(menu, "compressed_textures"); - - if ((!gl_extensions) || (!video_mode) || (!color_depth) || - (!fullscreen) || (!geometric_detail) || (!texture_detail) || (!texture_quality) || - (!texture_filter) || (!compressed_textures)) - { - Com_Printf(S_COLOR_YELLOW"WARNING: Unable to locate a video data1 field.\n"); - return; - } - - - if ( fullscreen->value == 0 ) - { - color_depth->value = 0; - color_depth->window.flags |= WINDOW_INACTIVE; - } - else - { - color_depth->window.flags &= ~WINDOW_INACTIVE; - } - - // If you change the extension enable, texture quality changes automatically - if ( gl_extensions->value == 0 ) - { - if ( texture_quality->value == 0 ) - { - texture_quality->value = 1; - } - } + // Make sure the cvars are registered as read only. + Cvar_Register ( NULL, "ui_r_glCustom", "0", CVAR_ROM ); + Cvar_Register ( NULL, "ui_r_mode", "0", CVAR_ROM ); + Cvar_Register ( NULL, "ui_r_fullscreen", "0", CVAR_ROM ); + Cvar_Register ( NULL, "ui_r_colorbits", "0", CVAR_ROM ); + Cvar_Register ( NULL, "ui_r_lodbias", "0", CVAR_ROM ); + Cvar_Register ( NULL, "ui_r_picmip", "0", CVAR_ROM ); + Cvar_Register ( NULL, "ui_r_texturebits", "0", CVAR_ROM ); + Cvar_Register ( NULL, "ui_r_texturemode", "0", CVAR_ROM ); + Cvar_Register ( NULL, "ui_r_detailtextures", "0", CVAR_ROM ); + Cvar_Register ( NULL, "ui_r_ext_compress_textures", "0", CVAR_ROM ); + Cvar_Register ( NULL, "ui_r_depthbits", "0", CVAR_ROM ); + Cvar_Register ( NULL, "ui_r_subdivisions", "0", CVAR_ROM ); + Cvar_Register ( NULL, "ui_r_fastSky", "0", CVAR_ROM ); + Cvar_Register ( NULL, "ui_r_inGameVideo", "0", CVAR_ROM ); + Cvar_Register ( NULL, "ui_cg_shadows", "0", CVAR_ROM ); + Cvar_Register ( NULL, "ui_r_modified", "0", CVAR_ROM ); + + // Copy over the real video cvars into their temporary counterparts + Cvar_Set ( "ui_r_glCustom", Cvar_VariableString ( "r_glCustom" ) ); + Cvar_Set ( "ui_r_mode", Cvar_VariableString ( "r_mode" ) ); + Cvar_Set ( "ui_r_colorbits", Cvar_VariableString ( "r_colorbits" ) ); + Cvar_Set ( "ui_r_fullscreen", Cvar_VariableString ( "r_fullscreen" ) ); + Cvar_Set ( "ui_r_lodbias", Cvar_VariableString ( "r_lodbias" ) ); + Cvar_Set ( "ui_r_picmip", Cvar_VariableString ( "r_picmip" ) ); + Cvar_Set ( "ui_r_texturebits", Cvar_VariableString ( "r_texturebits" ) ); + Cvar_Set ( "ui_r_texturemode", Cvar_VariableString ( "r_texturemode" ) ); + Cvar_Set ( "ui_r_ext_compress_textures", Cvar_VariableString ( "r_ext_compress_textures" ) ); + Cvar_Set ( "ui_r_depthbits", Cvar_VariableString ( "r_depthbits" ) ); + Cvar_Set ( "ui_r_subdivisions", Cvar_VariableString ( "r_subdivisions" ) ); + Cvar_Set ( "ui_r_fastSky", Cvar_VariableString ( "r_fastSky" ) ); + Cvar_Set ( "ui_r_inGameVideo", Cvar_VariableString ( "r_inGameVideo" ) ); + Cvar_Set ( "ui_cg_shadows", Cvar_VariableString ( "cg_shadows" ) ); + Cvar_Set ( "ui_r_modified", "0" ); } char GoToMenu[1024]; /* ================= -UI_CheckVid1Data +Menus_SaveGoToMenu ================= */ void Menus_SaveGoToMenu(const char *menuTo) @@ -2594,21 +2434,21 @@ void UI_CheckVid1Data(const char *menuTo,const char *warningMenuName) if (!applyChanges) { - Menus_CloseAll(); +// Menus_CloseAll(); Menus_OpenByName(menuTo); return; } if ((applyChanges->window.flags & WINDOW_VISIBLE)) // Is the APPLY CHANGES button active? { - Menus_SaveGoToMenu(menuTo); // Save menu you're going to - Menus_HideItems(menu->window.name); // HIDE videMenu in case you have to come back +// Menus_SaveGoToMenu(menuTo); // Save menu you're going to +// Menus_HideItems(menu->window.name); // HIDE videMenu in case you have to come back Menus_OpenByName(warningMenuName); // Give warning } else { - Menus_CloseAll(); - Menus_OpenByName(menuTo); +// Menus_CloseAll(); +// Menus_OpenByName(menuTo); } } diff --git a/code/ui/ui_public.h b/code/ui/ui_public.h index 0a39d8d..c2e2786 100644 --- a/code/ui/ui_public.h +++ b/code/ui/ui_public.h @@ -63,6 +63,8 @@ typedef struct { void (*R_AddLightToScene)( const vec3_t org, float intensity, float r, float g, float b ); void (*R_RenderScene)( const refdef_t *fd ); + void (*R_ModelBounds)( qhandle_t handle, vec3_t mins, vec3_t maxs ); + void (*R_SetColor)( const float *rgba ); // NULL = 1,1,1,1 void (*R_DrawStretchPic) ( float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader ); // 0 = white void (*R_ScissorPic) ( float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader ); // 0 = white diff --git a/code/ui/ui_shared.cpp b/code/ui/ui_shared.cpp index 3508c2c..1f2355d 100644 --- a/code/ui/ui_shared.cpp +++ b/code/ui/ui_shared.cpp @@ -129,8 +129,8 @@ char *types [] = { "ITEM_TYPE_EDITFIELD", "ITEM_TYPE_COMBO", "ITEM_TYPE_LISTBOX", -"ITEM_TYPE_OWNERDRAW", "ITEM_TYPE_MODEL", +"ITEM_TYPE_OWNERDRAW", "ITEM_TYPE_NUMERICFIELD", "ITEM_TYPE_SLIDER", "ITEM_TYPE_YESNO", @@ -1461,11 +1461,37 @@ Menus_CloseByName void Menus_CloseByName(const char *p) { menuDef_t *menu = Menus_FindByName(p); - if (menu != NULL) + + // If the menu wasnt found just exit + if (menu == NULL) { - Menu_RunCloseScript(menu); - menu->window.flags &= ~(WINDOW_VISIBLE | WINDOW_HASFOCUS); + return; } + + // Run the close script for the menu + Menu_RunCloseScript(menu); + + // If this window had the focus then take it away + if ( menu->window.flags & WINDOW_HASFOCUS ) + { + // If there is something still in the open menu list then + // set it to have focus now + if ( openMenuCount ) + { + // Subtract one from the open menu count to prepare to + // remove the top menu from the list + openMenuCount -= 1; + + // Set the top menu to have focus now + menuStack[openMenuCount]->window.flags |= WINDOW_HASFOCUS; + + // Remove the top menu from the list + menuStack[openMenuCount] = NULL; + } + } + + // Window is now invisible and doenst have focus + menu->window.flags &= ~(WINDOW_VISIBLE | WINDOW_HASFOCUS); } /* @@ -1581,13 +1607,15 @@ void Menu_OrbitItemByName(menuDef_t *menu, const char *p, float x, float y, floa Script_FadeIn ================= */ -void Script_FadeIn(itemDef_t *item, const char **args) +qboolean Script_FadeIn(itemDef_t *item, const char **args) { const char *name; if (String_Parse(args, &name)) { Menu_FadeItemByName((menuDef_t *) item->parent, name, qfalse); } + + return qtrue; } /* @@ -1595,13 +1623,15 @@ void Script_FadeIn(itemDef_t *item, const char **args) Script_FadeOut ================= */ -void Script_FadeOut(itemDef_t *item, const char **args) +qboolean Script_FadeOut(itemDef_t *item, const char **args) { const char *name; if (String_Parse(args, &name)) { Menu_FadeItemByName((menuDef_t *) item->parent, name, qtrue); } + + return qtrue; } /* @@ -1609,13 +1639,15 @@ void Script_FadeOut(itemDef_t *item, const char **args) Script_Show ================= */ -void Script_Show(itemDef_t *item, const char **args) +qboolean Script_Show(itemDef_t *item, const char **args) { const char *name; if (String_Parse(args, &name)) { Menu_ShowItemByName((menuDef_t *) item->parent, name, qtrue); } + + return qtrue; } @@ -1625,13 +1657,15 @@ void Script_Show(itemDef_t *item, const char **args) Script_ShowMenu ================= */ -void Script_ShowMenu(itemDef_t *item, const char **args) +qboolean Script_ShowMenu(itemDef_t *item, const char **args) { const char *name; if (String_Parse(args, &name)) { Menus_ShowItems(name); } + + return qtrue; } @@ -1640,13 +1674,15 @@ void Script_ShowMenu(itemDef_t *item, const char **args) Script_Hide ================= */ -void Script_Hide(itemDef_t *item, const char **args) +qboolean Script_Hide(itemDef_t *item, const char **args) { const char *name; if (String_Parse(args, &name)) { Menu_ShowItemByName((menuDef_t *) item->parent, name, qfalse); } + + return qtrue; } /* @@ -1654,7 +1690,7 @@ void Script_Hide(itemDef_t *item, const char **args) Script_SetColor ================= */ -void Script_SetColor(itemDef_t *item, const char **args) +qboolean Script_SetColor(itemDef_t *item, const char **args) { const char *name; int i; @@ -1687,12 +1723,14 @@ void Script_SetColor(itemDef_t *item, const char **args) // if (!Float_Parse(args, &f)) if (COM_ParseFloat( args, &f)) { - return; + return qtrue; } (*out)[i] = f; } } } + + return qtrue; } /* @@ -1700,18 +1738,21 @@ void Script_SetColor(itemDef_t *item, const char **args) Script_Open ================= */ -void Script_Open(itemDef_t *item, const char **args) +qboolean Script_Open(itemDef_t *item, const char **args) { const char *name; if (String_Parse(args, &name)) { Menus_OpenByName(name); } + + return qtrue; } -void Script_OpenGoToMenu(itemDef_t *item, const char **args) +qboolean Script_OpenGoToMenu(itemDef_t *item, const char **args) { Menus_OpenByName(GoToMenu); // Give warning + return qtrue; } @@ -1720,7 +1761,7 @@ void Script_OpenGoToMenu(itemDef_t *item, const char **args) Script_Close ================= */ -void Script_Close(itemDef_t *item, const char **args) +qboolean Script_Close(itemDef_t *item, const char **args) { const char *name; if (String_Parse(args, &name)) @@ -1734,6 +1775,8 @@ void Script_Close(itemDef_t *item, const char **args) Menus_CloseByName(name); } } + + return qtrue; } /* @@ -1741,7 +1784,7 @@ void Script_Close(itemDef_t *item, const char **args) Script_Activate ================= */ -void Script_Activate(itemDef_t *item, const char **args) +qboolean Script_Activate(itemDef_t *item, const char **args) { const char *name, *menu; @@ -1752,6 +1795,8 @@ void Script_Activate(itemDef_t *item, const char **args) Item_ActivateByName(menu,name); } } + + return qtrue; } /* @@ -1759,7 +1804,7 @@ void Script_Activate(itemDef_t *item, const char **args) Script_SetBackground ================= */ -void Script_SetBackground(itemDef_t *item, const char **args) +qboolean Script_SetBackground(itemDef_t *item, const char **args) { const char *name; // expecting name to set asset to @@ -1767,6 +1812,8 @@ void Script_SetBackground(itemDef_t *item, const char **args) { item->window.background = DC->registerShaderNoMip(name); } + + return qtrue; } /* @@ -1774,7 +1821,7 @@ void Script_SetBackground(itemDef_t *item, const char **args) Script_SetAsset ================= */ -void Script_SetAsset(itemDef_t *item, const char **args) +qboolean Script_SetAsset(itemDef_t *item, const char **args) { const char *name; // expecting name to set asset to @@ -1785,6 +1832,8 @@ void Script_SetAsset(itemDef_t *item, const char **args) { } } + + return qtrue; } /* @@ -1792,7 +1841,7 @@ void Script_SetAsset(itemDef_t *item, const char **args) Script_SetFocus ================= */ -void Script_SetFocus(itemDef_t *item, const char **args) +qboolean Script_SetFocus(itemDef_t *item, const char **args) { const char *name; itemDef_t *focusItem; @@ -1814,6 +1863,8 @@ void Script_SetFocus(itemDef_t *item, const char **args) } } } + + return qtrue; } @@ -1822,7 +1873,7 @@ void Script_SetFocus(itemDef_t *item, const char **args) Script_SetItemFlag ================= */ -void Script_SetItemFlag(itemDef_t *item, const char **args) +qboolean Script_SetItemFlag(itemDef_t *item, const char **args) { const char *itemName,*number; @@ -1836,6 +1887,8 @@ void Script_SetItemFlag(itemDef_t *item, const char **args) item->window.flags |= amount; } } + + return qtrue; } /* @@ -1843,7 +1896,7 @@ void Script_SetItemFlag(itemDef_t *item, const char **args) Script_SetItemColor ================= */ -void Script_SetItemColor(itemDef_t *item, const char **args) +qboolean Script_SetItemColor(itemDef_t *item, const char **args) { const char *itemname; const char *name; @@ -1861,7 +1914,7 @@ void Script_SetItemColor(itemDef_t *item, const char **args) // if (!Color_Parse(args, &color)) if (COM_ParseVec4(args, &color)) { - return; + return qtrue; } for (j = 0; j < count; j++) @@ -1894,6 +1947,57 @@ void Script_SetItemColor(itemDef_t *item, const char **args) } } } + + return qtrue; +} + +/* +================= +Script_Defer + +Defers the rest of the script based on the defer condition. The deferred +portion of the script can later be run with the "rundeferred" +================= +*/ +qboolean Script_Defer ( itemDef_t* item, const char **args ) +{ + // Should the script be deferred? + if ( DC->deferScript ( args ) ) + { + // Need the item the script was being run on + uiInfo.deferredScriptItem = item; + + // Save the rest of the script + Q_strncpyz ( uiInfo.deferredScript, *args, MAX_DEFERRED_SCRIPT, qfalse ); + + // No more running + return qfalse; + } + + // Keep running the script, its ok + return qtrue; +} + +/* +================= +Script_RunDeferred + +Runs the last deferred script, there can only be one script deferred at a +time so be careful of recursion +================= +*/ +qboolean Script_RunDeferred ( itemDef_t* item, const char **args ) +{ + // Make sure there is something to run. + if ( !uiInfo.deferredScript[0] || !uiInfo.deferredScriptItem ) + { + return qtrue; + } + + // Run the deferred script now + Item_RunScript ( uiInfo.deferredScriptItem, uiInfo.deferredScript ); + + return qtrue; } /* @@ -1901,7 +2005,7 @@ void Script_SetItemColor(itemDef_t *item, const char **args) Script_Transition ================= */ -void Script_Transition(itemDef_t *item, const char **args) +qboolean Script_Transition(itemDef_t *item, const char **args) { const char *name; rectDef_t rectFrom, rectTo; @@ -1916,6 +2020,8 @@ void Script_Transition(itemDef_t *item, const char **args) Menu_TransitionItemByName((menuDef_t *) item->parent, name, rectFrom, rectTo, time, amt); } } + + return qtrue; } /* @@ -1923,13 +2029,15 @@ void Script_Transition(itemDef_t *item, const char **args) Script_SetCvar ================= */ -void Script_SetCvar(itemDef_t *item, const char **args) +qboolean Script_SetCvar(itemDef_t *item, const char **args) { const char *cvar, *val; if (String_Parse(args, &cvar) && String_Parse(args, &val)) { DC->setCVar(cvar, val); } + + return qtrue; } /* @@ -1937,13 +2045,15 @@ void Script_SetCvar(itemDef_t *item, const char **args) Script_Exec ================= */ -void Script_Exec(itemDef_t *item, const char **args) +qboolean Script_Exec ( itemDef_t *item, const char **args) { const char *val; if (String_Parse(args, &val)) { DC->executeText(EXEC_APPEND, va("%s ; ", val)); } + + return qtrue; } /* @@ -1951,13 +2061,15 @@ void Script_Exec(itemDef_t *item, const char **args) Script_Play ================= */ -void Script_Play(itemDef_t *item, const char **args) +qboolean Script_Play(itemDef_t *item, const char **args) { const char *val; if (String_Parse(args, &val)) { DC->startLocalSound(DC->registerSound(val, qfalse), CHAN_LOCAL_SOUND); } + + return qtrue; } /* @@ -1965,7 +2077,7 @@ void Script_Play(itemDef_t *item, const char **args) Script_playLooped ================= */ -void Script_playLooped(itemDef_t *item, const char **args) +qboolean Script_playLooped(itemDef_t *item, const char **args) { const char *val; if (String_Parse(args, &val)) @@ -1974,6 +2086,8 @@ void Script_playLooped(itemDef_t *item, const char **args) // DC->stopBackgroundTrack(); // DC->startBackgroundTrack(val, val); } + + return qtrue; } /* @@ -1981,7 +2095,7 @@ void Script_playLooped(itemDef_t *item, const char **args) Script_Orbit ================= */ -void Script_Orbit(itemDef_t *item, const char **args) +qboolean Script_Orbit(itemDef_t *item, const char **args) { const char *name; float cx, cy, x, y; @@ -1995,6 +2109,8 @@ void Script_Orbit(itemDef_t *item, const char **args) Menu_OrbitItemByName((menuDef_t *) item->parent, name, x, y, cx, cy, time); } } + + return qtrue; } @@ -2021,6 +2137,8 @@ commandDef_t commandList[] = {"show", &Script_Show}, // group/name {"showMenu", &Script_ShowMenu}, // menu {"transition", &Script_Transition}, // group/name + {"defer", &Script_Defer}, // + {"rundeferred", &Script_RunDeferred}, // }; int scriptCommandCount = sizeof(commandList) / sizeof(commandDef_t); @@ -2228,9 +2346,12 @@ qboolean ItemParse_asset_model( itemDef_t *item) { return qfalse; } - - item->asset = DC->registerModel(temp); - modelPtr->angle = rand() % 360; + + if(!(item->asset)) + { + item->asset = DC->registerModel(temp); +// modelPtr->angle = rand() % 360; + } return qtrue; } @@ -3185,12 +3306,22 @@ qboolean ItemParse_cvar( itemDef_t *item) return qfalse; } - if (item->typeData) + if ( item->typeData) { - editPtr = (editFieldDef_t*)item->typeData; - editPtr->minVal = -1; - editPtr->maxVal = -1; - editPtr->defVal = -1; + switch ( item->type ) + { + case ITEM_TYPE_EDITFIELD: + case ITEM_TYPE_NUMERICFIELD: + case ITEM_TYPE_YESNO: + case ITEM_TYPE_BIND: + case ITEM_TYPE_SLIDER: + case ITEM_TYPE_TEXT: + editPtr = (editFieldDef_t*)item->typeData; + editPtr->minVal = -1; + editPtr->maxVal = -1; + editPtr->defVal = -1; + break; + } } return qtrue; } @@ -3806,7 +3937,11 @@ void Item_RunScript(itemDef_t *item, const char *s) { if (Q_stricmp(command, commandList[i].name) == 0) { - (commandList[i].handler(item, &p)); + if ( !(commandList[i].handler(item, &p)) ) + { + return; + } + bRan = qtrue; break; } @@ -3814,7 +3949,11 @@ void Item_RunScript(itemDef_t *item, const char *s) // not in our auto list, pass to handler if (!bRan) { - DC->runScript(&p); + // Allow any script command to fail + if ( !DC->runScript(&p) ) + { + break; + } } } } @@ -3843,6 +3982,7 @@ void Menu_SetupKeywordHash(void) Menus_ActivateByName =============== */ +void Menu_HandleMouseMove(menuDef_t *menu, float x, float y); menuDef_t *Menus_ActivateByName(const char *p) { int i; @@ -3872,6 +4012,9 @@ menuDef_t *Menus_ActivateByName(const char *p) Com_Printf(S_COLOR_YELLOW"WARNING: Menus_ActivateByName: Unable to find menu '%s'\n",p); } + // Want to handle a mouse move on the new menu in case your already over an item + Menu_HandleMouseMove ( m, DC->cursorx, DC->cursory ); + return m; } @@ -3962,13 +4105,13 @@ static bind_t g_bindings[] = {"+block", -1, -1, -1, -1}, {"+use", K_SPACE, -1, -1, -1}, {"datapad", K_TAB, -1, -1, -1}, - {"save quik*", -1, -1, -1, -1}, + {"save quik*", K_F9, -1, -1, -1}, {"load quik", -1, -1, -1, -1}, {"load auto", -1, -1, -1, -1}, - {"cg_thirdperson !",-1, -1, -1, -1}, + {"cg_thirdperson !",'p', -1, -1, -1}, {"exitview", -1, -1, -1, -1}, - {"uimenu loadmenu", -1, -1, -1, -1}, - {"uimenu savemenu", -1, -1, -1, -1}, + {"uimenu ingameloadmenu", K_F10, -1, -1, -1}, + {"uimenu ingamesavemenu", K_F11, -1, -1, -1}, {"saberAttackCycle",-1, -1, -1, -1}, }; @@ -4222,12 +4365,16 @@ Menus_CloseAll */ void Menus_CloseAll(void) { - int i; - for (i = 0; i < menuCount; i++) - { - Menu_RunCloseScript(&Menus[i]); + int i; + + for (i = 0; i < menuCount; i++) + { + Menu_RunCloseScript ( &Menus[i] ); Menus[i].window.flags &= ~(WINDOW_HASFOCUS | WINDOW_VISIBLE); - } + } + + // Clear the menu stack + openMenuCount = 0; } /* @@ -4309,62 +4456,6 @@ qboolean PC_ParseColor(vec4_t *color) return(COM_ParseVec4(&parseData.bufferCurrent, color)); } -/* -================= -Controls_GetConfig2 -================= -*/ -void Controls_GetConfig2( void ) -{ - int i; - int twokeys[2]; - - // iterate each command, get its numeric binding - for (i=0; i < g_bindCount; i++) - { - - Controls_GetKeyAssignment(g_bindings[i].command, twokeys); - - g_bindings[i].bind1 = twokeys[0]; - g_bindings[i].bind2 = twokeys[1]; - } - - //s_controls.invertmouse.curvalue = DC->getCVarValue( "m_pitch" ) < 0; - //s_controls.smoothmouse.curvalue = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "m_filter" ) ); - //s_controls.alwaysrun.curvalue = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "cl_run" ) ); - //s_controls.autoswitch.curvalue = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "cg_autoswitch" ) ); - //s_controls.sensitivity.curvalue = UI_ClampCvar( 2, 30, Controls_GetCvarValue( "sensitivity" ) ); - //s_controls.joyenable.curvalue = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "in_joystick" ) ); - //s_controls.joythreshold.curvalue = UI_ClampCvar( 0.05, 0.75, Controls_GetCvarValue( "joy_threshold" ) ); - //s_controls.freelook.curvalue = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "cl_freelook" ) ); -} - -/* -================= -Controls_SetConfig2 -================= -*/ -void Controls_SetConfig2(qboolean restart) -{ -// int i; - - // FIXME BOB - make this work - // iterate each command, get its numeric binding - /* - for (i=0; i < g_bindCount; i++) - { - if (g_bindingsNew[i].bind1 != -1) - { - DC->setBinding( g_bindingsNew[i].bind1, g_bindingsNew[i].command ); - - if (g_bindings[i].bind2 != -1) - DC->setBinding( g_bindingsNew[i].bind2, g_bindingsNew[i].command ); - } - } - - DC->executeText(EXEC_APPEND, "in_restart\n"); - */ -} /* ================= @@ -5223,10 +5314,10 @@ void Item_Model_Paint(itemDef_t *item) w = item->window.rect.w-2; h = item->window.rect.h-2; - refdef.x = x; - refdef.y = y; - refdef.width = w; - refdef.height = h; + refdef.x = x * DC->xscale; + refdef.y = y * DC->yscale; + refdef.width = w * DC->xscale; + refdef.height = h * DC->yscale; DC->modelBounds( item->asset, mins, maxs ); @@ -5245,15 +5336,17 @@ void Item_Model_Paint(itemDef_t *item) origin[0] = item->textscale; } refdef.fov_x = (modelPtr->fov_x) ? modelPtr->fov_x : w; - refdef.fov_x = (modelPtr->fov_y) ? modelPtr->fov_y : h; + refdef.fov_y = (modelPtr->fov_y) ? modelPtr->fov_y : h; + refdef.fov_x = 45; + refdef.fov_y = 45; + //refdef.fov_x = (int)((float)refdef.width / 640.0f * 90.0f); //xx = refdef.width / tan( refdef.fov_x / 360 * M_PI ); //refdef.fov_y = atan2( refdef.height, xx ); //refdef.fov_y *= ( 360 / M_PI ); - // FIXME : Bob make this work -// DC->clearScene(); + DC->clearScene(); refdef.time = DC->realTime; @@ -5266,6 +5359,7 @@ void Item_Model_Paint(itemDef_t *item) //VectorSet( angles, 0, 0, 1 ); // use item storage to track +/* if (modelPtr->rotationSpeed) { if (DC->realTime > item->window.nextTime) @@ -5275,15 +5369,20 @@ void Item_Model_Paint(itemDef_t *item) } } VectorSet( angles, 0, modelPtr->angle, 0 ); +*/ + VectorSet( angles, 0, (float)(refdef.time/20.0f), 0); + AnglesToAxis( angles, ent.axis ); ent.hModel = item->asset; VectorCopy( origin, ent.origin ); - VectorCopy( origin, ent.lightingOrigin ); - ent.renderfx = RF_LIGHTING_ORIGIN | RF_NOSHADOW; VectorCopy( ent.origin, ent.oldorigin ); -// DC->addRefEntityToScene( &ent ); + // Set up lighting + VectorCopy( refdef.vieworg, ent.lightingOrigin ); + ent.renderfx = RF_LIGHTING_ORIGIN | RF_NOSHADOW; + + DC->addRefEntityToScene( &ent ); DC->renderScene( &refdef ); } @@ -5568,11 +5667,11 @@ void Item_Slider_Paint(itemDef_t *item) x = item->window.rect.x; } DC->setColor(newColor); - DC->drawHandlePic( x, y+7, SLIDER_WIDTH, SLIDER_HEIGHT, DC->Assets.sliderBar ); + DC->drawHandlePic( x, y+2, SLIDER_WIDTH, SLIDER_HEIGHT, DC->Assets.sliderBar ); x = Item_Slider_ThumbPosition(item); // DC->drawHandlePic( x - (SLIDER_THUMB_WIDTH / 2), y - 2, SLIDER_THUMB_WIDTH, SLIDER_THUMB_HEIGHT, DC->Assets.sliderThumb ); - DC->drawHandlePic( x - (SLIDER_THUMB_WIDTH / 2), y+7, SLIDER_THUMB_WIDTH, SLIDER_THUMB_HEIGHT, DC->Assets.sliderThumb ); + DC->drawHandlePic( x - (SLIDER_THUMB_WIDTH / 2), y+2, SLIDER_THUMB_WIDTH, SLIDER_THUMB_HEIGHT, DC->Assets.sliderThumb ); } @@ -5608,7 +5707,7 @@ void Item_Paint(itemDef_t *item) textPtr = item->descText; } - textWidth = DC->textWidth(textPtr,(float) item->textscale, item->font); + textWidth = DC->textWidth(textPtr,(float) item->textscale, uiInfo.uiDC.Assets.qhMediumFont); // item->font); if (parent->descAlignment == ITEM_ALIGN_RIGHT) { @@ -5631,7 +5730,7 @@ void Item_Paint(itemDef_t *item) parent->descScale = 1; } - DC->drawText(xPos, parent->descY, (float) parent->descScale, parent->descColor, textPtr, 0, 0, item->font); + DC->drawText(xPos, parent->descY, (float) parent->descScale, parent->descColor, textPtr, 0, 0, uiInfo.uiDC.Assets.qhMediumFont); //item->font); } } diff --git a/code/ui/ui_shared.h b/code/ui/ui_shared.h index 91524c5..f14deba 100644 --- a/code/ui/ui_shared.h +++ b/code/ui/ui_shared.h @@ -142,8 +142,8 @@ typedef struct { typedef struct { -// void (*addRefEntityToScene) (const refEntity_t *re ); -// void (*clearScene) (); + void (*addRefEntityToScene) (const refEntity_t *re ); + void (*clearScene) (); void (*drawHandlePic) (float x, float y, float w, float h, qhandle_t asset); void (*drawRect) ( float x, float y, float w, float h, float size, const vec4_t color); void (*drawSides) (float x, float y, float w, float h, float size); @@ -172,7 +172,8 @@ typedef struct { qhandle_t (*registerShaderNoMip) (const char *p); sfxHandle_t (*registerSound)(const char *name, qboolean compressed); void (*renderScene) ( const refdef_t *fd ); - void (*runScript)(const char **p); + qboolean (*runScript)(const char **p); + qboolean (*deferScript)(const char **p); void (*setBinding)( int keynum, const char *binding ); void (*setColor) (const vec4_t v); void (*setCVar)(const char *cvar, const char *value); @@ -279,11 +280,22 @@ typedef struct { } colorRangeDef_t; typedef struct modelDef_s { - int angle; // - vec3_t origin; // - float fov_x; // - float fov_y; // - int rotationSpeed; // + int angle; + vec3_t origin; + float fov_x; + float fov_y; + int rotationSpeed; + + int animated; + int startframe; + int numframes; + int loopframes; + int fps; + + int frame; + int oldframe; + float backlerp; + int frameTime; } modelDef_t; typedef struct itemDef_s { @@ -353,15 +365,17 @@ typedef struct { float descScale; // Description scale } menuDef_t; -typedef struct { - const char *name; - void (*handler) (itemDef_t *item, const char** args); +typedef struct +{ + const char *name; + qboolean (*handler) (itemDef_t *item, const char** args); + } commandDef_t; menuDef_t *Menu_GetFocused(void); -void Controls_GetConfig2( void ); -void Controls_SetConfig2(qboolean restart); +void Controls_GetConfig( void ); +void Controls_SetConfig(qboolean restart); qboolean Display_KeyBindPending(void); qboolean Display_MouseMove(void *p, int x, int y); int Display_VisibleMenuCount(void); diff --git a/code/ui/ui_syscalls.cpp b/code/ui/ui_syscalls.cpp index 646ca97..dbf57b7 100644 --- a/code/ui/ui_syscalls.cpp +++ b/code/ui/ui_syscalls.cpp @@ -32,10 +32,20 @@ float trap_Cvar_VariableValue( const char *var_name ) } +void trap_R_ClearScene( void ) +{ + ui.R_ClearScene(); +} + +void trap_R_AddRefEntityToScene( const refEntity_t *re ) +{ + ui.R_AddRefEntityToScene(re); +} void trap_R_RenderScene( const refdef_t *fd ) { - syscall( UI_R_RENDERSCENE, fd ); +// syscall( UI_R_RENDERSCENE, fd ); + ui.R_RenderScene(fd); } void trap_R_SetColor( const float *rgba ) @@ -56,7 +66,8 @@ void trap_R_DrawStretchPic( float x, float y, float w, float h, float s1, float void trap_R_ModelBounds( clipHandle_t model, vec3_t mins, vec3_t maxs ) { - syscall( UI_R_MODELBOUNDS, model, mins, maxs ); +// syscall( UI_R_MODELBOUNDS, model, mins, maxs ); + ui.R_ModelBounds(model, mins, maxs); } void trap_S_StartLocalSound( sfxHandle_t sfx, int channelNum ) diff --git a/code/ui/vssver.scc b/code/ui/vssver.scc deleted file mode 100644 index 93927ab..0000000 Binary files a/code/ui/vssver.scc and /dev/null differ diff --git a/code/unix/Makefile b/code/unix/Makefile deleted file mode 100644 index 5c8dda8..0000000 --- a/code/unix/Makefile +++ /dev/null @@ -1,988 +0,0 @@ -# -# Quake3 Unix Makefile -# -# Currently build for the following: -# Linux i386 (full client) -# Linux Alpha (dedicated server only) -# SGI IRIX (full client) -# -# Nov '98 by Zoid -# -# GNU Make required -# - -PLATFORM=$(shell uname|tr '[:upper:]' '[:lower:]') -PLATFORM_RELEASE=$(shell uname -r) - -### -### These paths are where you probably want to change things -### - -# Where we are building to, libMesaVoodooGL.so.3.1 should be here, etc. -# the demo pk3 file should be here in demoq3/pak0.pk3 or baseq3/pak0.pk3 -BUILD_DIR=/home/zoid/Quake3 - -# Where we are building from (where the source code should be!) -MOUNT_DIR=/devel/Quake3/q3code - -############################################################################# -## -## You shouldn't have to touch anything below here -## -############################################################################# - -DEMO_PAK=$(BUILD_DIR)/demoq3/pak0.pk3 - -BUILD_DEBUG_DIR=debug$(ARCH)$(GLIBC) -BUILD_RELEASE_DIR=release$(ARCH)$(GLIBC) -CLIENT_DIR=$(MOUNT_DIR)/client -SERVER_DIR=$(MOUNT_DIR)/server -REF_DIR=$(MOUNT_DIR)/renderer -COMMON_DIR=$(MOUNT_DIR)/qcommon -UNIX_DIR=$(MOUNT_DIR)/unix -GAME_DIR=$(MOUNT_DIR)/game -CGAME_DIR=$(MOUNT_DIR)/cgame -NULL_DIR=$(MOUNT_DIR)/null - -#used for linux i386 builds -MESA_DIR=/usr/local/src/Mesa-3.0 - -VERSION=1.05 - -VERSION_FN=$(VERSION)$(GLIBC) -RPM_RELEASE=9 - -############################################################################# -# SETUP AND BUILD -############################################################################# - -ifeq ($(PLATFORM),irix) - -ARCH=mips #default to MIPS -VENDOR=sgi -GLIBC= #libc is irrelevant - -CC=cc -BASE_CFLAGS=-Dstricmp=strcasecmp -Xcpluscomm -woff 1185 -mips3 \ - -nostdinc -I. -I$(ROOT)/usr/include -RELEASE_CFLAGS=$(BASE_CFLAGS) -O3 -DEBUG_CFLAGS=$(BASE_CFLAGS) -g - -SHLIBEXT=so -SHLIBCFLAGS= -SHLIBLDFLAGS=-shared - -LDFLAGS=-ldl -lm -GLLDFLAGS=-L/usr/X11/lib -lGL -lX11 -lXext -lm - -TARGETS=$(BUILDDIR)/sgiquake3 \ - $(BUILDDIR)/qagame$(ARCH).$(SHLIBEXT) \ - $(BUILDDIR)/cgame$(ARCH).$(SHLIBEXT) - -else -ifeq ($(PLATFORM),linux) - -ifneq (,$(findstring libc6,$(shell if [ -e /lib/libc.so.6* ];then echo libc6;fi))) -GLIBC=-glibc -else -GLIBC= -endif #libc6 test - - -ifneq (,$(findstring alpha,$(shell uname -m))) -ARCH=axp -RPMARCH=alpha -VENDOR=dec -else #default to i386 -ARCH=i386 -RPMARCH=i386 -VENDOR=unknown -endif #alpha test - -BASE_CFLAGS=-Dstricmp=strcasecmp -I$(MESA_DIR)/include -I/usr/include/glide \ - -DREF_HARD_LINKED -pipe - -DEBUG_CFLAGS=$(BASE_CFLAGS) -g -ifeq ($(ARCH),axp) -RELEASE_CFLAGS=$(BASE_CFLAGS) -DNDEBUG -O6 -ffast-math -funroll-loops -fomit-frame-pointer -fexpensive-optimizations -else -RELEASE_CFLAGS=$(BASE_CFLAGS) -DNDEBUG -O3 -m486 -fomit-frame-pointer -pipe -ffast-math -fexpensive-optimizations -malign-loops=2 -malign-jumps=2 -malign-functions=2 -endif - -CC=gcc - -SHLIBEXT=so -SHLIBCFLAGS=-fPIC -SHLIBLDFLAGS=-shared - -LDFLAGS=-ldl -lm -GLLDFLAGS=-L/usr/X11R6/lib -L$(MESA_DIR)/lib -lX11 -lXext -lXxf86dga -lXxf86vm -GL_SVGA_LDFLAGS=-L/usr/X11R6/lib -L$(MESA_DIR)/lib -lX11 -lXext -lvga - -ifeq ($(ARCH),axp) -TARGETS=$(BUILDDIR)/linuxq3ded $(BUILDDIR)/qagame$(ARCH).$(SHLIBEXT) -else -# $(BUILDDIR)/linuxquake3.vga -TARGETS=\ - $(BUILDDIR)/linuxquake3 \ - $(BUILDDIR)/qagame$(ARCH).$(SHLIBEXT) \ - $(BUILDDIR)/cgame$(ARCH).$(SHLIBEXT) -#$(BUILDDIR)/quake3 -endif - - -else #generic - -CC=cc -BASE_CFLAGS=-Dstricmp=strcasecmp -DEBUG_CFLAGS=$(BASE_CFLAGS) -g -RELEASE_CFLAGS=$(BASE_CFLAGS) -DNDEBUG -O - -SHLIBEXT=so -SHLIBCFLAGS=-fPIC -SHLIBLDFLAGS=-shared - -LDFLAGS=-ldl -lm - -TARGETS=$(BUILDDIR)/q3ded $(BUILDDIR)/qagame$(ARCH).$(SHLIBEXT) - -endif #Linux -endif #IRIX - -DO_CC=$(CC) $(CFLAGS) -o $@ -c $< -DO_DEBUG_CC=$(CC) $(DEBUG_CFLAGS) -o $@ -c $< -DO_SHLIB_CC=$(CC) $(CFLAGS) $(SHLIBCFLAGS) -o $@ -c $< -DO_SHLIB_DEBUG_CC=$(CC) $(DEBUG_CFLAGS) $(SHLIBCFLAGS) -o $@ -c $< -DO_AS=$(CC) $(CFLAGS) -DELF -x assembler-with-cpp -o $@ -c $< - -#### DEFAULT TARGET -# default: build_release - -build_debug: - $(MAKE) targets BUILDDIR=$(BUILD_DEBUG_DIR) CFLAGS="$(DEBUG_CFLAGS)" - -build_release: - $(MAKE) targets BUILDDIR=$(BUILD_RELEASE_DIR) CFLAGS="$(RELEASE_CFLAGS)" - -#Build both debug and release builds -all: build_debug build_release - -targets: makedirs $(TARGETS) - -makedirs: - @if [ ! -d $(BUILDDIR) ];then mkdir $(BUILDDIR);fi - @if [ ! -d $(BUILDDIR)/client ];then mkdir $(BUILDDIR)/client;fi - @if [ ! -d $(BUILDDIR)/ded ];then mkdir $(BUILDDIR)/ded;fi - @if [ ! -d $(BUILDDIR)/ref ];then mkdir $(BUILDDIR)/ref;fi - @if [ ! -d $(BUILDDIR)/game ];then mkdir $(BUILDDIR)/game;fi - @if [ ! -d $(BUILDDIR)/cgame ];then mkdir $(BUILDDIR)/cgame;fi - -############################################################################# -# CLIENT/SERVER -############################################################################# - -QUAKE3_OBJS = \ - $(BUILDDIR)/client/cl_cgame.o \ - $(BUILDDIR)/client/cl_cin.o \ - $(BUILDDIR)/client/cl_console.o \ - $(BUILDDIR)/client/cl_input.o \ - $(BUILDDIR)/client/cl_keys.o \ - $(BUILDDIR)/client/cl_main.o \ - $(BUILDDIR)/client/cl_parse.o \ - $(BUILDDIR)/client/cl_scrn.o \ - $(BUILDDIR)/client/snd_dma.o \ - $(BUILDDIR)/client/snd_mem.o \ - $(BUILDDIR)/client/snd_mix.o \ - $(BUILDDIR)/client/sv_bot.o \ - $(BUILDDIR)/client/sv_client.o \ - $(BUILDDIR)/client/sv_ccmds.o \ - $(BUILDDIR)/client/sv_game.o \ - $(BUILDDIR)/client/sv_init.o \ - $(BUILDDIR)/client/sv_main.o \ - $(BUILDDIR)/client/sv_snapshot.o \ - $(BUILDDIR)/client/sv_world.o \ - $(BUILDDIR)/client/ui_arena.o \ - $(BUILDDIR)/client/ui_connect.o \ - $(BUILDDIR)/client/ui_controls.o \ - $(BUILDDIR)/client/ui_demo.o \ - $(BUILDDIR)/client/ui_maps.o \ - $(BUILDDIR)/client/ui_menu.o \ - $(BUILDDIR)/client/ui_network.o \ - $(BUILDDIR)/client/ui_preferences.o \ - $(BUILDDIR)/client/ui_qmenu.o \ - $(BUILDDIR)/client/ui_servers.o \ - $(BUILDDIR)/client/ui_startserver.o \ - $(BUILDDIR)/client/ui_video.o \ - \ - $(BUILDDIR)/client/cm_load.o \ - $(BUILDDIR)/client/cm_patch.o \ - $(BUILDDIR)/client/cm_polylib.o \ - $(BUILDDIR)/client/cm_tag.o \ - $(BUILDDIR)/client/cm_test.o \ - $(BUILDDIR)/client/cm_trace.o \ - $(BUILDDIR)/client/cmd.o \ - $(BUILDDIR)/client/common.o \ - $(BUILDDIR)/client/cvar.o \ - $(BUILDDIR)/client/files.o \ - $(BUILDDIR)/client/gameinfo.o \ - $(BUILDDIR)/client/md4.o \ - $(BUILDDIR)/client/msg.o \ - $(BUILDDIR)/client/net_chan.o \ - $(BUILDDIR)/client/vm.o \ - \ - $(BUILDDIR)/client/q_shared.o \ - $(BUILDDIR)/client/q_math.o \ - \ - $(BUILDDIR)/client/tr_backend.o \ - $(BUILDDIR)/client/tr_bsp.o \ - $(BUILDDIR)/client/tr_calc.o \ - $(BUILDDIR)/client/tr_calc_3dnow.o \ - $(BUILDDIR)/client/tr_calc_c.o \ - $(BUILDDIR)/client/tr_calc_kni.o \ - $(BUILDDIR)/client/tr_cmds.o \ - $(BUILDDIR)/client/tr_curve.o \ - $(BUILDDIR)/client/tr_draw.o \ - $(BUILDDIR)/client/tr_flares.o \ - $(BUILDDIR)/client/tr_image.o \ - $(BUILDDIR)/client/tr_init.o \ - $(BUILDDIR)/client/tr_light.o \ - $(BUILDDIR)/client/tr_main.o \ - $(BUILDDIR)/client/tr_mesh.o \ - $(BUILDDIR)/client/tr_misc.o \ - $(BUILDDIR)/client/tr_model.o \ - $(BUILDDIR)/client/tr_noise.o \ - $(BUILDDIR)/client/tr_scene.o \ - $(BUILDDIR)/client/tr_shade.o \ - $(BUILDDIR)/client/tr_shader.o \ - $(BUILDDIR)/client/tr_shade_calc.o \ - $(BUILDDIR)/client/tr_shadows.o \ - $(BUILDDIR)/client/tr_smp.o \ - $(BUILDDIR)/client/tr_sky.o \ - $(BUILDDIR)/client/tr_surf.o \ - $(BUILDDIR)/client/tr_world.o \ - \ - $(BUILDDIR)/client/unix_main.o \ - $(BUILDDIR)/client/unix_net.o \ - $(BUILDDIR)/client/unix_shared.o - -#platform specific objects -ifeq ($(PLATFORM),irix) - QUAKE3_PLATOBJ=\ - $(BUILDDIR)/client/irix_qgl.o \ - $(BUILDDIR)/client/irix_glimp.o \ - $(BUILDDIR)/client/irix_snd.o -else -ifeq ($(PLATFORM),linux) -ifeq ($(ARCH),axp) - QUAKE3_PLATOBJ= -else - QUAKE3_PLATOBJ=\ - $(BUILDDIR)/client/linux_qgl.o \ - $(BUILDDIR)/client/linux_glimp.o \ - $(BUILDDIR)/client/linux_snd.o \ - $(BUILDDIR)/client/snd_mixa.o \ - $(BUILDDIR)/client/matha.o \ - $(BUILDDIR)/client/sys_dosa.o - QUAKE3_VGA=\ - $(BUILDDIR)/client/linux_qgl.o \ - $(BUILDDIR)/client/linux_glimp_vga.o \ - $(BUILDDIR)/client/linux_input_vga.o \ - $(BUILDDIR)/client/linux_snd.o \ - $(BUILDDIR)/client/snd_mixa.o \ - $(BUILDDIR)/client/matha.o \ - $(BUILDDIR)/client/sys_dosa.o - -endif -endif #Linux -endif #IRIX - - -$(BUILDDIR)/linuxquake3 : $(QUAKE3_OBJS) $(QUAKE3_PLATOBJ) - $(CC) $(CFLAGS) -o $@ $(QUAKE3_OBJS) $(QUAKE3_PLATOBJ) $(GLLDFLAGS) $(LDFLAGS) - -ifeq ($(PLATFORM),linux) -ifeq ($(ARCH),i386) -$(BUILDDIR)/linuxquake3.vga : $(QUAKE3_OBJS) $(QUAKE3_VGA) - $(CC) $(CFLAGS) -o $@ $(QUAKE3_OBJS) $(QUAKE3_VGA) $(GL_SVGA_LDFLAGS) $(LDFLAGS) -endif -endif - -$(BUILDDIR)/client/cl_cgame.o : $(CLIENT_DIR)/cl_cgame.c - $(DO_CC) - -$(BUILDDIR)/client/cl_cin.o : $(CLIENT_DIR)/cl_cin.c - $(DO_CC) - -$(BUILDDIR)/client/cl_console.o : $(CLIENT_DIR)/cl_console.c - $(DO_CC) - -$(BUILDDIR)/client/cl_input.o : $(CLIENT_DIR)/cl_input.c - $(DO_CC) - -$(BUILDDIR)/client/cl_keys.o : $(CLIENT_DIR)/cl_keys.c - $(DO_CC) - -$(BUILDDIR)/client/cl_main.o : $(CLIENT_DIR)/cl_main.c - $(DO_CC) - -$(BUILDDIR)/client/cl_parse.o : $(CLIENT_DIR)/cl_parse.c - $(DO_CC) - -$(BUILDDIR)/client/cl_scrn.o : $(CLIENT_DIR)/cl_scrn.c - $(DO_CC) - -$(BUILDDIR)/client/snd_dma.o : $(CLIENT_DIR)/snd_dma.c - $(DO_CC) - -$(BUILDDIR)/client/snd_mem.o : $(CLIENT_DIR)/snd_mem.c - $(DO_CC) - -$(BUILDDIR)/client/snd_mix.o : $(CLIENT_DIR)/snd_mix.c - $(DO_CC) - -$(BUILDDIR)/client/sv_bot.o : $(SERVER_DIR)/sv_bot.c - $(DO_CC) - -$(BUILDDIR)/client/sv_client.o : $(SERVER_DIR)/sv_client.c - $(DO_CC) - -$(BUILDDIR)/client/sv_ccmds.o : $(SERVER_DIR)/sv_ccmds.c - $(DO_CC) - -$(BUILDDIR)/client/sv_game.o : $(SERVER_DIR)/sv_game.c - $(DO_CC) - -$(BUILDDIR)/client/sv_init.o : $(SERVER_DIR)/sv_init.c - $(DO_CC) - -$(BUILDDIR)/client/sv_main.o : $(SERVER_DIR)/sv_main.c - $(DO_CC) - -$(BUILDDIR)/client/sv_snapshot.o : $(SERVER_DIR)/sv_snapshot.c - $(DO_CC) - -$(BUILDDIR)/client/sv_world.o : $(SERVER_DIR)/sv_world.c - $(DO_CC) - -$(BUILDDIR)/client/ui_arena.o : $(CLIENT_DIR)/ui_arena.c - $(DO_CC) - -$(BUILDDIR)/client/ui_connect.o : $(CLIENT_DIR)/ui_connect.c - $(DO_CC) - -$(BUILDDIR)/client/ui_controls.o : $(CLIENT_DIR)/ui_controls.c - $(DO_CC) - -$(BUILDDIR)/client/ui_demo.o : $(CLIENT_DIR)/ui_demo.c - $(DO_CC) - -$(BUILDDIR)/client/ui_maps.o : $(CLIENT_DIR)/ui_maps.c - $(DO_CC) - -$(BUILDDIR)/client/ui_menu.o : $(CLIENT_DIR)/ui_menu.c - $(DO_CC) - -$(BUILDDIR)/client/ui_network.o : $(CLIENT_DIR)/ui_network.c - $(DO_CC) - -$(BUILDDIR)/client/ui_preferences.o : $(CLIENT_DIR)/ui_preferences.c - $(DO_CC) - -$(BUILDDIR)/client/ui_qmenu.o : $(CLIENT_DIR)/ui_qmenu.c - $(DO_CC) - -$(BUILDDIR)/client/ui_servers.o : $(CLIENT_DIR)/ui_servers.c - $(DO_CC) - -$(BUILDDIR)/client/ui_startserver.o : $(CLIENT_DIR)/ui_startserver.c - $(DO_CC) - -$(BUILDDIR)/client/ui_video.o : $(UNIX_DIR)/ui_video.c - $(DO_CC) - -$(BUILDDIR)/client/cm_trace.o : $(COMMON_DIR)/cm_trace.c - $(DO_CC) - -$(BUILDDIR)/client/cm_load.o : $(COMMON_DIR)/cm_load.c - $(DO_CC) - -$(BUILDDIR)/client/cm_test.o : $(COMMON_DIR)/cm_test.c - $(DO_CC) - -$(BUILDDIR)/client/cm_patch.o : $(COMMON_DIR)/cm_patch.c - $(DO_CC) - -$(BUILDDIR)/client/cm_polylib.o : $(COMMON_DIR)/cm_polylib.c - $(DO_CC) - -$(BUILDDIR)/client/cm_tag.o : $(COMMON_DIR)/cm_tag.c - $(DO_CC) - -$(BUILDDIR)/client/cmd.o : $(COMMON_DIR)/cmd.c - $(DO_CC) - -$(BUILDDIR)/client/common.o : $(COMMON_DIR)/common.c - $(DO_CC) - -$(BUILDDIR)/client/cvar.o : $(COMMON_DIR)/cvar.c - $(DO_CC) - -$(BUILDDIR)/client/files.o : $(COMMON_DIR)/files.c - $(DO_CC) - -$(BUILDDIR)/client/gameinfo.o : $(COMMON_DIR)/gameinfo.c - $(DO_CC) - -$(BUILDDIR)/client/md4.o : $(COMMON_DIR)/md4.c - $(DO_CC) - -$(BUILDDIR)/client/msg.o : $(COMMON_DIR)/msg.c - $(DO_CC) - -$(BUILDDIR)/client/net_chan.o : $(COMMON_DIR)/net_chan.c - $(DO_CC) - -$(BUILDDIR)/client/vm.o : $(COMMON_DIR)/vm.c - $(DO_CC) - -$(BUILDDIR)/client/q_shared.o : $(GAME_DIR)/q_shared.c - $(DO_DEBUG_CC) - -$(BUILDDIR)/client/q_math.o : $(GAME_DIR)/q_math.c - $(DO_CC) - -$(BUILDDIR)/client/tr_bsp.o : $(REF_DIR)/tr_bsp.c - $(DO_CC) - -$(BUILDDIR)/client/tr_backend.o : $(REF_DIR)/tr_backend.c - $(DO_CC) - -$(BUILDDIR)/client/tr_calc.o : $(REF_DIR)/tr_calc.c - $(DO_CC) - -$(BUILDDIR)/client/tr_calc_3dnow.o : $(REF_DIR)/tr_calc_3dnow.c - $(DO_CC) - -$(BUILDDIR)/client/tr_calc_c.o : $(REF_DIR)/tr_calc_c.c - $(DO_CC) - -$(BUILDDIR)/client/tr_calc_kni.o : $(REF_DIR)/tr_calc_kni.c - $(DO_CC) - -$(BUILDDIR)/client/tr_cmds.o : $(REF_DIR)/tr_cmds.c - $(DO_CC) - -$(BUILDDIR)/client/tr_curve.o : $(REF_DIR)/tr_curve.c - $(DO_CC) - -$(BUILDDIR)/client/tr_draw.o : $(REF_DIR)/tr_draw.c - $(DO_CC) - -$(BUILDDIR)/client/tr_flares.o : $(REF_DIR)/tr_flares.c - $(DO_CC) - -$(BUILDDIR)/client/tr_image.o : $(REF_DIR)/tr_image.c - $(DO_CC) - -$(BUILDDIR)/client/tr_init.o : $(REF_DIR)/tr_init.c - $(DO_CC) - -$(BUILDDIR)/client/tr_light.o : $(REF_DIR)/tr_light.c - $(DO_CC) - -$(BUILDDIR)/client/tr_main.o : $(REF_DIR)/tr_main.c - $(DO_CC) - -$(BUILDDIR)/client/tr_mesh.o : $(REF_DIR)/tr_mesh.c - $(DO_CC) - -$(BUILDDIR)/client/tr_misc.o : $(REF_DIR)/tr_misc.c - $(DO_CC) - -$(BUILDDIR)/client/tr_model.o : $(REF_DIR)/tr_model.c - $(DO_CC) - -$(BUILDDIR)/client/tr_noise.o : $(REF_DIR)/tr_noise.c - $(DO_CC) - -$(BUILDDIR)/client/tr_scene.o : $(REF_DIR)/tr_scene.c - $(DO_CC) - -$(BUILDDIR)/client/tr_shade.o : $(REF_DIR)/tr_shade.c - $(DO_CC) - -$(BUILDDIR)/client/tr_shader.o : $(REF_DIR)/tr_shader.c - $(DO_CC) - -$(BUILDDIR)/client/tr_shade_calc.o : $(REF_DIR)/tr_shade_calc.c - $(DO_CC) - -$(BUILDDIR)/client/tr_shadows.o : $(REF_DIR)/tr_shadows.c - $(DO_CC) - -$(BUILDDIR)/client/tr_sky.o : $(REF_DIR)/tr_sky.c - $(DO_CC) - -$(BUILDDIR)/client/tr_smp.o : $(REF_DIR)/tr_smp.c - $(DO_CC) - -$(BUILDDIR)/client/tr_stripify.o : $(REF_DIR)/tr_stripify.c - $(DO_CC) - -$(BUILDDIR)/client/tr_subdivide.o : $(REF_DIR)/tr_subdivide.c - $(DO_CC) - -$(BUILDDIR)/client/tr_surf.o : $(REF_DIR)/tr_surf.c - $(DO_CC) - -$(BUILDDIR)/client/tr_world.o : $(REF_DIR)/tr_world.c - $(DO_CC) - -$(BUILDDIR)/client/unix_qgl.o : $(UNIX_DIR)/unix_qgl.c - $(DO_CC) - -$(BUILDDIR)/client/unix_dedicated.o : $(UNIX_DIR)/unix_dedicated.c - $(DO_CC) - -$(BUILDDIR)/client/unix_earlycon.o : $(UNIX_DIR)/unix_earlycon.c - $(DO_CC) - -$(BUILDDIR)/client/unix_main.o : $(UNIX_DIR)/unix_main.c - $(DO_CC) - -$(BUILDDIR)/client/unix_net.o : $(UNIX_DIR)/unix_net.c - $(DO_CC) - -$(BUILDDIR)/client/unix_shared.o : $(UNIX_DIR)/unix_shared.c - $(DO_CC) - -$(BUILDDIR)/client/irix_glimp.o : $(UNIX_DIR)/irix_glimp.c - $(DO_CC) - -$(BUILDDIR)/client/irix_snd.o : $(UNIX_DIR)/irix_snd.c - $(DO_CC) - -$(BUILDDIR)/client/irix_input.o : $(UNIX_DIR)/irix_input.c - $(DO_CC) - -$(BUILDDIR)/client/linux_glimp.o : $(UNIX_DIR)/linux_glimp.c - $(DO_CC) - -$(BUILDDIR)/client/linux_qgl.o : $(UNIX_DIR)/linux_qgl.c - $(DO_CC) - -$(BUILDDIR)/client/linux_input.o : $(UNIX_DIR)/linux_input.c - $(DO_CC) - -$(BUILDDIR)/client/linux_glimp_vga.o : $(UNIX_DIR)/linux_glimp_vga.c - $(DO_CC) - -$(BUILDDIR)/client/linux_snd.o : $(UNIX_DIR)/linux_snd.c - $(DO_CC) - -$(BUILDDIR)/client/snd_mixa.o : $(UNIX_DIR)/snd_mixa.s - $(DO_AS) - -$(BUILDDIR)/client/matha.o : $(UNIX_DIR)/matha.s - $(DO_AS) - -$(BUILDDIR)/client/sys_dosa.o : $(UNIX_DIR)/sys_dosa.s - $(DO_AS) - -$(BUILDDIR)/client/linux_input_vga.o : $(UNIX_DIR)/linux_input_vga.c - $(DO_CC) - -############################################################################# -# DEDICATED SERVER -############################################################################# - -Q3DED_OBJS = \ - $(BUILDDIR)/ded/sv_bot.o \ - $(BUILDDIR)/ded/sv_client.o \ - $(BUILDDIR)/ded/sv_ccmds.o \ - $(BUILDDIR)/ded/sv_game.o \ - $(BUILDDIR)/ded/sv_init.o \ - $(BUILDDIR)/ded/sv_main.o \ - $(BUILDDIR)/ded/sv_snapshot.o \ - $(BUILDDIR)/ded/sv_world.o \ - \ - $(BUILDDIR)/ded/cm_trace.o \ - $(BUILDDIR)/ded/cm_load.o \ - $(BUILDDIR)/ded/cm_test.o \ - $(BUILDDIR)/ded/cm_patch.o \ - $(BUILDDIR)/ded/cm_tag.o \ - $(BUILDDIR)/ded/cmd.o \ - $(BUILDDIR)/ded/common.o \ - $(BUILDDIR)/ded/cvar.o \ - $(BUILDDIR)/ded/files.o \ - $(BUILDDIR)/ded/gameinfo.o \ - $(BUILDDIR)/ded/md4.o \ - $(BUILDDIR)/ded/msg.o \ - $(BUILDDIR)/ded/net_chan.o \ - \ - $(BUILDDIR)/ded/unix_dedicated.o \ - $(BUILDDIR)/ded/unix_main.o \ - $(BUILDDIR)/ded/unix_net.o \ - $(BUILDDIR)/ded/unix_shared.o \ - \ - $(BUILDDIR)/ded/cl_null.o - -$(BUILDDIR)/linuxq3ded : $(Q3DED_OBJS) - $(CC) $(CFLAGS) -o $@ $(Q3DED_OBJS) $(LDFLAGS) - -$(BUILDDIR)/ded/sv_bot.o : $(SERVER_DIR)/sv_bot.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/sv_client.o : $(SERVER_DIR)/sv_client.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/sv_ccmds.o : $(SERVER_DIR)/sv_ccmds.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/sv_game.o : $(SERVER_DIR)/sv_game.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/sv_init.o : $(SERVER_DIR)/sv_init.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/sv_main.o : $(SERVER_DIR)/sv_main.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/sv_snapshot.o : $(SERVER_DIR)/sv_snapshot.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/sv_world.o : $(SERVER_DIR)/sv_world.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/cm_trace.o : $(COMMON_DIR)/cm_trace.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/cm_load.o : $(COMMON_DIR)/cm_load.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/cm_test.o : $(COMMON_DIR)/cm_test.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/cm_patch.o : $(COMMON_DIR)/cm_patch.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/cm_tag.o : $(COMMON_DIR)/cm_tag.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/cmd.o : $(COMMON_DIR)/cmd.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/common.o : $(COMMON_DIR)/common.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/cvar.o : $(COMMON_DIR)/cvar.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/files.o : $(COMMON_DIR)/files.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/gameinfo.o : $(COMMON_DIR)/gameinfo.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/md4.o : $(COMMON_DIR)/md4.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/msg.o : $(COMMON_DIR)/msg.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/net_chan.o : $(COMMON_DIR)/net_chan.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/unix_dedicated.o : $(UNIX_DIR)/unix_dedicated.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/unix_main.o : $(UNIX_DIR)/unix_main.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/unix_net.o : $(UNIX_DIR)/unix_net.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/unix_shared.o : $(UNIX_DIR)/unix_shared.c - $(DO_DED_CC) - -$(BUILDDIR)/ded/cl_null.o : $(NULL_DIR)/cl_null.c - $(DO_DED_CC) - -############################################################################# -# GAME -############################################################################# - -GAME_OBJS = \ - $(BUILDDIR)/game/b_ai.o \ - $(BUILDDIR)/game/b_files.o \ - $(BUILDDIR)/game/b_items.o \ - $(BUILDDIR)/game/b_main.o \ - $(BUILDDIR)/game/b_nav.o \ - $(BUILDDIR)/game/b_navgen.o \ - $(BUILDDIR)/game/bg_misc.o \ - $(BUILDDIR)/game/bg_pmove.o \ - $(BUILDDIR)/game/g_active.o \ - $(BUILDDIR)/game/g_aim.o \ - $(BUILDDIR)/game/g_client.o \ - $(BUILDDIR)/game/g_cmds.o \ - $(BUILDDIR)/game/g_combat.o \ - $(BUILDDIR)/game/g_items.o \ - $(BUILDDIR)/game/g_main.o \ - $(BUILDDIR)/game/g_mem.o \ - $(BUILDDIR)/game/g_misc.o \ - $(BUILDDIR)/game/g_missile.o \ - $(BUILDDIR)/game/g_mover.o \ - $(BUILDDIR)/game/g_spawn.o \ - $(BUILDDIR)/game/g_svcmds.o \ - $(BUILDDIR)/game/g_target.o \ - $(BUILDDIR)/game/g_team.o \ - $(BUILDDIR)/game/g_trigger.o \ - $(BUILDDIR)/game/g_utils.o \ - $(BUILDDIR)/game/g_weapon.o \ - $(BUILDDIR)/game/q_shared.o \ - $(BUILDDIR)/game/q_math.o - -$(BUILDDIR)/qagame$(ARCH).$(SHLIBEXT) : $(GAME_OBJS) - $(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(GAME_OBJS) - -$(BUILDDIR)/game/b_ai.o : $(GAME_DIR)/b_ai.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/b_files.o : $(GAME_DIR)/b_files.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/b_items.o : $(GAME_DIR)/b_items.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/b_main.o : $(GAME_DIR)/b_main.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/b_nav.o : $(GAME_DIR)/b_nav.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/b_navgen.o : $(GAME_DIR)/b_navgen.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/bg_misc.o : $(GAME_DIR)/bg_misc.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/bg_pmove.o : $(GAME_DIR)/bg_pmove.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/g_active.o : $(GAME_DIR)/g_active.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/g_aim.o : $(GAME_DIR)/g_aim.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/g_client.o : $(GAME_DIR)/g_client.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/g_cmds.o : $(GAME_DIR)/g_cmds.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/g_combat.o : $(GAME_DIR)/g_combat.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/g_items.o : $(GAME_DIR)/g_items.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/g_main.o : $(GAME_DIR)/g_main.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/g_mem.o : $(GAME_DIR)/g_mem.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/g_misc.o : $(GAME_DIR)/g_misc.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/g_missile.o : $(GAME_DIR)/g_missile.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/g_mover.o : $(GAME_DIR)/g_mover.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/g_spawn.o : $(GAME_DIR)/g_spawn.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/g_svcmds.o : $(GAME_DIR)/g_svcmds.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/g_target.o : $(GAME_DIR)/g_target.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/g_team.o : $(GAME_DIR)/g_team.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/g_trigger.o : $(GAME_DIR)/g_trigger.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/g_utils.o : $(GAME_DIR)/g_utils.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/g_weapon.o : $(GAME_DIR)/g_weapon.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/game/q_shared.o : $(GAME_DIR)/q_shared.c - $(DO_SHLIB_DEBUG_CC) - -$(BUILDDIR)/game/q_math.o : $(GAME_DIR)/q_math.c - $(DO_SHLIB_CC) - -############################################################################# -# CGAME -############################################################################# - -CGAME_OBJS = \ - $(BUILDDIR)/cgame/bg_misc.o \ - $(BUILDDIR)/cgame/bg_pmove.o \ - $(BUILDDIR)/cgame/cg_draw.o \ - $(BUILDDIR)/cgame/cg_effects.o \ - $(BUILDDIR)/cgame/cg_ents.o \ - $(BUILDDIR)/cgame/cg_event.o \ - $(BUILDDIR)/cgame/cg_info.o \ - $(BUILDDIR)/cgame/cg_localents.o \ - $(BUILDDIR)/cgame/cg_main.o \ - $(BUILDDIR)/cgame/cg_marks.o \ - $(BUILDDIR)/cgame/cg_menu.o \ - $(BUILDDIR)/cgame/cg_players.o \ - $(BUILDDIR)/cgame/cg_predict.o \ - $(BUILDDIR)/cgame/cg_scoreboard.o \ - $(BUILDDIR)/cgame/cg_snapshot.o \ - $(BUILDDIR)/cgame/cg_view.o \ - $(BUILDDIR)/cgame/cg_weapons.o \ - $(BUILDDIR)/cgame/q_shared.o \ - $(BUILDDIR)/cgame/q_math.o - -$(BUILDDIR)/cgame$(ARCH).$(SHLIBEXT) : $(CGAME_OBJS) - $(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(CGAME_OBJS) - -$(BUILDDIR)/cgame/bg_misc.o : $(GAME_DIR)/bg_misc.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/cgame/bg_pmove.o : $(GAME_DIR)/bg_pmove.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/cgame/cg_draw.o : $(CGAME_DIR)/cg_draw.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/cgame/cg_effects.o : $(CGAME_DIR)/cg_effects.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/cgame/cg_ents.o : $(CGAME_DIR)/cg_ents.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/cgame/cg_event.o : $(CGAME_DIR)/cg_event.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/cgame/cg_info.o : $(CGAME_DIR)/cg_info.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/cgame/cg_localents.o : $(CGAME_DIR)/cg_localents.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/cgame/cg_main.o : $(CGAME_DIR)/cg_main.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/cgame/cg_marks.o : $(CGAME_DIR)/cg_marks.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/cgame/cg_menu.o : $(CGAME_DIR)/cg_menu.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/cgame/cg_players.o : $(CGAME_DIR)/cg_players.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/cgame/cg_predict.o : $(CGAME_DIR)/cg_predict.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/cgame/cg_scoreboard.o : $(CGAME_DIR)/cg_scoreboard.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/cgame/cg_snapshot.o : $(CGAME_DIR)/cg_snapshot.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/cgame/cg_view.o : $(CGAME_DIR)/cg_view.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/cgame/cg_weapons.o : $(CGAME_DIR)/cg_weapons.c - $(DO_SHLIB_CC) - -$(BUILDDIR)/cgame/q_shared.o : $(GAME_DIR)/q_shared.c - $(DO_SHLIB_DEBUG_CC) - -$(BUILDDIR)/cgame/q_math.o : $(GAME_DIR)/q_math.c - $(DO_SHLIB_CC) - - -############################################################################# -# RPM -############################################################################# - -###### DISABLED - -TMPDIR=/var/tmp -TARDIR=$(TMPDIR)/q3test -TARFILE = q3test-$(VERSION_FN)-$(RPM_RELEASE).$(ARCH).tar - -tar: - if [ ! -d archives ];then mkdir archives;chmod 755 archives;fi - $(MAKE) copyfiles COPYDIR=$(TARDIR) - cd $(TARDIR)/..; tar cvf $(TARFILE) q3test && gzip -9 $(TARFILE) - mv $(TARDIR)/../$(TARFILE).gz archives/. - chmod 644 archives/$(TARFILE).gz - rm -rf $(TARDIR) - -# Make RPMs. You need to be root to make this work -RPMROOT=/usr/src/redhat -RPM = rpm -RPMFLAGS = -bb -INSTALLDIR = /usr/local/games/q3test -RPMDIR = $(TMPDIR)/q3test-$(VERSION_FN) -DESTDIR= $(RPMDIR)/$(INSTALLDIR) - -rpm: q3test.spec - touch $(RPMROOT)/SOURCES/q3test-$(VERSION_FN).tar.gz - if [ ! -d archives ];then mkdir archives;fi - $(MAKE) copyfiles COPYDIR=$(DESTDIR) - cp $(UNIX_DIR)/quake3.gif $(RPMROOT)/SOURCES/. - cp q3test.spec $(RPMROOT)/SPECS/. - cd $(RPMROOT)/SPECS; $(RPM) $(RPMFLAGS) q3test.spec - rm -rf $(RPMDIR) - mv $(RPMROOT)/RPMS/$(RPMARCH)/q3test-$(VERSION_FN)-$(RPM_RELEASE).$(RPMARCH).rpm archives/q3test-$(VERSION_FN)-$(RPM_RELEASE).$(RPMARCH).rpm - chmod 644 archives/q3test-$(VERSION_FN)-$(RPM_RELEASE).$(RPMARCH).rpm - -copyfiles: - -mkdirhier $(COPYDIR) - cp $(BUILD_RELEASE_DIR)/linuxquake3 $(COPYDIR) - strip $(COPYDIR)/linuxquake3 - chmod 755 $(COPYDIR)/linuxquake3 - cp $(BUILD_RELEASE_DIR)/qagame$(ARCH).$(SHLIBEXT) $(COPYDIR) - chmod 755 $(COPYDIR)/qagame$(ARCH).$(SHLIBEXT) - cp $(BUILD_RELEASE_DIR)/cgame$(ARCH).$(SHLIBEXT) $(COPYDIR) - chmod 755 $(COPYDIR)/cgame$(ARCH).$(SHLIBEXT) - cp $(BUILD_DIR)/libMesaVoodooGL.so.3.1 $(COPYDIR)/. - chmod 755 $(COPYDIR)/libMesaVoodooGL.so.3.1 - -mkdir $(COPYDIR)/demoq3 - chmod 1777 $(COPYDIR)/demoq3 - cp $(DEMO_PAK) $(COPYDIR)/demoq3/pak0.pk3 - cp $(UNIX_DIR)/README.EULA $(COPYDIR) - chmod 644 $(COPYDIR)/README.EULA - cp $(UNIX_DIR)/README.Q3Test $(COPYDIR) - chmod 644 $(COPYDIR)/README.Q3Test - -q3test.spec : $(UNIX_DIR)/q3test.spec.sh Makefile - sh $< $(VERSION_FN) $(RPM_RELEASE) $(ARCH) $(INSTALLDIR) > $@ - -############################################################################# -# MISC -############################################################################# - -clean: clean-debug clean-release - -clean-debug: - $(MAKE) clean2 BUILDDIR=$(BUILD_DEBUG_DIR) CFLAGS="$(DEBUG_CFLAGS)" - -clean-release: - $(MAKE) clean2 BUILDDIR=$(BUILD_RELEASE_DIR) CFLAGS="$(DEBUG_CFLAGS)" - diff --git a/code/unix/linux_glimp.c b/code/unix/linux_glimp.c deleted file mode 100644 index 8bfe671..0000000 --- a/code/unix/linux_glimp.c +++ /dev/null @@ -1,1387 +0,0 @@ -/* -** GLW_IMP.C -** -** This file contains ALL Linux specific stuff having to do with the -** OpenGL refresh. When a port is being made the following functions -** must be implemented by the port: -** -** GLimp_EndFrame -** GLimp_Init -** GLimp_Shutdown -** GLimp_SwitchFullscreen -** -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//#include "../qcommon/qcommon.h" -//#include "../client/keys.h" -#include "../renderer/tr_local.h" -#include "../client/client.h" - -#include "unix_glw.h" - -#include - -#include -#include - -#include -#include - -typedef enum { - RSERR_OK, - - RSERR_INVALID_FULLSCREEN, - RSERR_INVALID_MODE, - - RSERR_UNKNOWN -} rserr_t; - -glwstate_t glw_state; - -static Display *dpy = NULL; -static int scrnum; -static Window win = 0; -static GLXContext ctx = NULL; - -static qboolean autorepeaton = qtrue; - -#define KEY_MASK (KeyPressMask | KeyReleaseMask) -#define MOUSE_MASK (ButtonPressMask | ButtonReleaseMask | \ - PointerMotionMask | ButtonMotionMask ) -#define X_MASK (KEY_MASK | MOUSE_MASK | VisibilityChangeMask | StructureNotifyMask ) - -static qboolean mouse_avail; -static qboolean mouse_active; -static int mx, my; - -static cvar_t *in_mouse; -static cvar_t *in_dgamouse; - -static cvar_t *r_fakeFullscreen; - -qboolean dgamouse = qfalse; -qboolean vidmode_ext = qfalse; - -static int win_x, win_y; - -static XF86VidModeModeInfo **vidmodes; -static int default_dotclock_vidmode; -static int num_vidmodes; -static qboolean vidmode_active = qfalse; - -static int mouse_accel_numerator; -static int mouse_accel_denominator; -static int mouse_threshold; - -/*****************************************************************************/ -/* KEYBOARD */ -/*****************************************************************************/ - -static unsigned int keyshift[256]; // key to map to if shift held down in console -static qboolean shift_down=qfalse; - -static char *XLateKey(XKeyEvent *ev, int *key) -{ - static char buf[64]; - KeySym keysym; - static qboolean setup = qfalse; - int i; - - *key = 0; - - XLookupString(ev, buf, sizeof buf, &keysym, 0); - -// ri.Printf( PRINT_ALL, "keysym=%04X\n", (int)keysym); - switch(keysym) - { - case XK_KP_Page_Up: - case XK_KP_9: *key = K_KP_PGUP; break; - case XK_Page_Up: *key = K_PGUP; break; - - case XK_KP_Page_Down: - case XK_KP_3: *key = K_KP_PGDN; break; - case XK_Page_Down: *key = K_PGDN; break; - - case XK_KP_Home: *key = K_KP_HOME; break; - case XK_KP_7: *key = K_KP_HOME; break; - case XK_Home: *key = K_HOME; break; - - case XK_KP_End: - case XK_KP_1: *key = K_KP_END; break; - case XK_End: *key = K_END; break; - - case XK_KP_Left: *key = K_KP_LEFTARROW; break; - case XK_KP_4: *key = K_KP_LEFTARROW; break; - case XK_Left: *key = K_LEFTARROW; break; - - case XK_KP_Right: *key = K_KP_RIGHTARROW; break; - case XK_KP_6: *key = K_KP_RIGHTARROW; break; - case XK_Right: *key = K_RIGHTARROW; break; - - case XK_KP_Down: - case XK_KP_2: *key = K_KP_DOWNARROW; break; - case XK_Down: *key = K_DOWNARROW; break; - - case XK_KP_Up: - case XK_KP_8: *key = K_KP_UPARROW; break; - case XK_Up: *key = K_UPARROW; break; - - case XK_Escape: *key = K_ESCAPE; break; - - case XK_KP_Enter: *key = K_KP_ENTER; break; - case XK_Return: *key = K_ENTER; break; - - case XK_Tab: *key = K_TAB; break; - - case XK_F1: *key = K_F1; break; - - case XK_F2: *key = K_F2; break; - - case XK_F3: *key = K_F3; break; - - case XK_F4: *key = K_F4; break; - - case XK_F5: *key = K_F5; break; - - case XK_F6: *key = K_F6; break; - - case XK_F7: *key = K_F7; break; - - case XK_F8: *key = K_F8; break; - - case XK_F9: *key = K_F9; break; - - case XK_F10: *key = K_F10; break; - - case XK_F11: *key = K_F11; break; - - case XK_F12: *key = K_F12; break; - -// case XK_BackSpace: *key = K_BACKSPACE; break; - case XK_BackSpace: *key = 8; break; // ctrl-h - - case XK_KP_Delete: - case XK_KP_Decimal: *key = K_KP_DEL; break; - case XK_Delete: *key = K_DEL; break; - - case XK_Pause: *key = K_PAUSE; break; - - case XK_Shift_L: - case XK_Shift_R: *key = K_SHIFT; break; - - case XK_Execute: - case XK_Control_L: - case XK_Control_R: *key = K_CTRL; break; - - case XK_Alt_L: - case XK_Meta_L: - case XK_Alt_R: - case XK_Meta_R: *key = K_ALT; break; - - case XK_KP_Begin: *key = K_KP_5; break; - - case XK_Insert: *key = K_INS; break; - case XK_KP_Insert: - case XK_KP_0: *key = K_KP_INS; break; - - case XK_KP_Multiply: *key = '*'; break; - case XK_KP_Add: *key = K_KP_PLUS; break; - case XK_KP_Subtract: *key = K_KP_MINUS; break; - case XK_KP_Divide: *key = K_KP_SLASH; break; - - default: - *key = *(unsigned char *)buf; - if (*key >= 'A' && *key <= 'Z') - *key = *key - 'A' + 'a'; - break; - } - - return buf; -} - -// ======================================================================== -// makes a null cursor -// ======================================================================== - -static Cursor CreateNullCursor(Display *display, Window root) -{ - Pixmap cursormask; - XGCValues xgc; - GC gc; - XColor dummycolour; - Cursor cursor; - - cursormask = XCreatePixmap(display, root, 1, 1, 1/*depth*/); - xgc.function = GXclear; - gc = XCreateGC(display, cursormask, GCFunction, &xgc); - XFillRectangle(display, cursormask, gc, 0, 0, 1, 1); - dummycolour.pixel = 0; - dummycolour.red = 0; - dummycolour.flags = 04; - cursor = XCreatePixmapCursor(display, cursormask, cursormask, - &dummycolour,&dummycolour, 0,0); - XFreePixmap(display,cursormask); - XFreeGC(display,gc); - return cursor; -} - -static void install_grabs(void) -{ -// inviso cursor - XDefineCursor(dpy, win, CreateNullCursor(dpy, win)); - - XGrabPointer(dpy, win, - False, - MOUSE_MASK, - GrabModeAsync, GrabModeAsync, - win, - None, - CurrentTime); - - XGetPointerControl(dpy, &mouse_accel_numerator, &mouse_accel_denominator, - &mouse_threshold); - - XChangePointerControl(dpy, qtrue, qtrue, 2, 1, 0); - - if (in_dgamouse->value) { - int MajorVersion, MinorVersion; - - if (!XF86DGAQueryVersion(dpy, &MajorVersion, &MinorVersion)) { - // unable to query, probalby not supported - ri.Printf( PRINT_ALL, "Failed to detect XF86DGA Mouse\n" ); - ri.Cvar_Set( "in_dgamouse", "0" ); - } else { - dgamouse = qtrue; - XF86DGADirectVideo(dpy, DefaultScreen(dpy), XF86DGADirectMouse); - XWarpPointer(dpy, None, win, 0, 0, 0, 0, 0, 0); - } - } else { - XWarpPointer(dpy, None, win, - 0, 0, 0, 0, - glConfig.vidWidth / 2, glConfig.vidHeight / 2); - } - - XGrabKeyboard(dpy, win, - False, - GrabModeAsync, GrabModeAsync, - CurrentTime); - -// XSync(dpy, True); -} - -static void uninstall_grabs(void) -{ - if (dgamouse) { - dgamouse = qfalse; - XF86DGADirectVideo(dpy, DefaultScreen(dpy), 0); - } - - XChangePointerControl(dpy, qtrue, qtrue, mouse_accel_numerator, - mouse_accel_denominator, mouse_threshold); - - XUngrabPointer(dpy, CurrentTime); - XUngrabKeyboard(dpy, CurrentTime); - -// inviso cursor - XUndefineCursor(dpy, win); - -// XAutoRepeatOn(dpy); - -// XSync(dpy, True); -} - -static void HandleEvents(void) -{ - int b; - int key; - XEvent event; - qboolean dowarp = qfalse; - int mwx = glConfig.vidWidth/2; - int mwy = glConfig.vidHeight/2; - char *p; - - if (!dpy) - return; - - while (XPending(dpy)) { - XNextEvent(dpy, &event); - switch(event.type) { - case KeyPress: - p = XLateKey(&event.xkey, &key); - if (key) - Sys_QueEvent( 0, SE_KEY, key, qtrue, 0, NULL ); - while (*p) - Sys_QueEvent( 0, SE_CHAR, *p++, 0, 0, NULL ); - break; - case KeyRelease: - XLateKey(&event.xkey, &key); - - Sys_QueEvent( 0, SE_KEY, key, qfalse, 0, NULL ); - break; - -#if 0 - case KeyPress: - case KeyRelease: - key = XLateKey(&event.xkey); - - Sys_QueEvent( 0, SE_KEY, key, event.type == KeyPress, 0, NULL ); - if (key == K_SHIFT) - shift_down = (event.type == KeyPress); - if (key < 128 && (event.type == KeyPress)) { - if (shift_down) - key = keyshift[key]; - Sys_QueEvent( 0, SE_CHAR, key, 0, 0, NULL ); - } -#endif - break; - - case MotionNotify: - if (mouse_active) { - if (dgamouse) { - if (abs(event.xmotion.x_root) > 1) - mx += event.xmotion.x_root * 2; - else - mx += event.xmotion.x_root; - if (abs(event.xmotion.y_root) > 1) - my += event.xmotion.y_root * 2; - else - my += event.xmotion.y_root; -// ri.Printf(PRINT_ALL, "mouse (%d,%d) (root=%d,%d)\n", event.xmotion.x + win_x, event.xmotion.y + win_y, event.xmotion.x_root, event.xmotion.y_root); - } - else - { -// ri.Printf(PRINT_ALL, "mouse x=%d,y=%d\n", (int)event.xmotion.x - mwx, (int)event.xmotion.y - mwy); - mx += ((int)event.xmotion.x - mwx); - my += ((int)event.xmotion.y - mwy); - mwx = event.xmotion.x; - mwy = event.xmotion.y; - - if (mx || my) - dowarp = qtrue; - } - } - break; - - case ButtonPress: - b=-1; - if (event.xbutton.button == 1) - b = 0; - else if (event.xbutton.button == 2) - b = 2; - else if (event.xbutton.button == 3) - b = 1; - Sys_QueEvent( 0, SE_KEY, K_MOUSE1 + b, qtrue, 0, NULL ); - break; - - case ButtonRelease: - b=-1; - if (event.xbutton.button == 1) - b = 0; - else if (event.xbutton.button == 2) - b = 2; - else if (event.xbutton.button == 3) - b = 1; - Sys_QueEvent( 0, SE_KEY, K_MOUSE1 + b, qfalse, 0, NULL ); - break; - - case CreateNotify : - win_x = event.xcreatewindow.x; - win_y = event.xcreatewindow.y; - break; - - case ConfigureNotify : - win_x = event.xconfigure.x; - win_y = event.xconfigure.y; - break; - } - } - - if (dowarp) { - /* move the mouse to the window center again */ - XWarpPointer(dpy,None,win,0,0,0,0, - (glConfig.vidWidth/2),(glConfig.vidHeight/2)); - } - -} - -void KBD_Init(void) -{ -} - -void KBD_Close(void) -{ -} - -void IN_ActivateMouse( void ) -{ - if (!mouse_avail || !dpy || !win) - return; - - if (!mouse_active) { - mx = my = 0; // don't spazz - install_grabs(); - mouse_active = qtrue; - } -} - -void IN_DeactivateMouse( void ) -{ - if (!mouse_avail || !dpy || !win) - return; - - if (mouse_active) { - uninstall_grabs(); - mouse_active = qfalse; - } -} -/*****************************************************************************/ - -static qboolean signalcaught = qfalse;; - -static void signal_handler(int sig) -{ - if (signalcaught) { - printf("DOUBLE SIGNAL FAULT: Received signal %d, exiting...\n", sig); - _exit(1); - } - - signalcaught = qtrue; - printf("Received signal %d, exiting...\n", sig); - GLimp_Shutdown(); - _exit(1); -} - -static void InitSig(void) -{ - signal(SIGHUP, signal_handler); - signal(SIGQUIT, signal_handler); - signal(SIGILL, signal_handler); - signal(SIGTRAP, signal_handler); - signal(SIGIOT, signal_handler); - signal(SIGBUS, signal_handler); - signal(SIGFPE, signal_handler); - signal(SIGSEGV, signal_handler); - signal(SIGTERM, signal_handler); -} - -/* -** GLimp_SetGamma -** -** This routine should only be called if glConfig.deviceSupportsGamma is TRUE -*/ -void GLimp_SetGamma( unsigned char red[256], unsigned char green[256], unsigned char blue[256] ) -{ -} - -/* -** GLimp_Shutdown -** -** This routine does all OS specific shutdown procedures for the OpenGL -** subsystem. Under OpenGL this means NULLing out the current DC and -** HGLRC, deleting the rendering context, and releasing the DC acquired -** for the window. The state structure is also nulled out. -** -*/ -void GLimp_Shutdown( void ) -{ - if (!ctx || !dpy) - return; - IN_DeactivateMouse(); - XAutoRepeatOn(dpy); - if (dpy) { - if (ctx) - qglXDestroyContext(dpy, ctx); - if (win) - XDestroyWindow(dpy, win); - if (vidmode_active) - XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[0]); - XCloseDisplay(dpy); - } - vidmode_active = qfalse; - dpy = NULL; - win = 0; - ctx = NULL; - - memset( &glConfig, 0, sizeof( glConfig ) ); - memset( &glState, 0, sizeof( glState ) ); - - QGL_Shutdown(); -} - -/* -** GLimp_LogComment -*/ -void GLimp_LogComment( char *comment ) -{ - if ( glw_state.log_fp ) { - fprintf( glw_state.log_fp, "%s", comment ); - } -} - -/* -** GLW_StartDriverAndSetMode -*/ -static qboolean GLW_StartDriverAndSetMode( const char *drivername, - int mode, - qboolean fullscreen ) -{ - rserr_t err; - - // don't ever bother going into fullscreen with a voodoo card -#if 1 // JDC: I reenabled this - if ( strstr( drivername, "Voodoo" ) ) { - ri.Cvar_Set( "r_fullscreen", "0" ); - r_fullscreen->modified = qfalse; - fullscreen = qfalse; - } -#endif - - err = GLW_SetMode( drivername, mode, fullscreen ); - - switch ( err ) - { - case RSERR_INVALID_FULLSCREEN: - ri.Printf( PRINT_ALL, "...WARNING: fullscreen unavailable in this mode\n" ); - return qfalse; - case RSERR_INVALID_MODE: - ri.Printf( PRINT_ALL, "...WARNING: could not set the given mode (%d)\n", mode ); - return qfalse; - default: - break; - } - return qtrue; -} - -/* -** GLW_SetMode -*/ -int GLW_SetMode( const char *drivername, int mode, qboolean fullscreen ) -{ - int attrib[] = { - GLX_RGBA, // 0 - GLX_RED_SIZE, 4, // 1, 2 - GLX_GREEN_SIZE, 4, // 3, 4 - GLX_BLUE_SIZE, 4, // 5, 6 - GLX_DOUBLEBUFFER, // 7 - GLX_DEPTH_SIZE, 1, // 8, 9 - GLX_STENCIL_SIZE, 1, // 10, 11 - None - }; -// these match in the array -#define ATTR_RED_IDX 2 -#define ATTR_GREEN_IDX 4 -#define ATTR_BLUE_IDX 6 -#define ATTR_DEPTH_IDX 9 -#define ATTR_STENCIL_IDX 11 - Window root; - XVisualInfo *visinfo; - XSetWindowAttributes attr; - unsigned long mask; - int colorbits, depthbits, stencilbits; - int tcolorbits, tdepthbits, tstencilbits; - int MajorVersion, MinorVersion; - int actualWidth, actualHeight; - int i; - - r_fakeFullscreen = ri.Cvar_Get( "r_fakeFullscreen", "0", CVAR_ARCHIVE); - - ri.Printf( PRINT_ALL, "Initializing OpenGL display\n"); - - ri.Printf (PRINT_ALL, "...setting mode %d:", mode ); - - if ( !R_GetModeInfo( &glConfig.vidWidth, &glConfig.vidHeight, &glConfig.windowAspect, mode ) ) - { - ri.Printf( PRINT_ALL, " invalid mode\n" ); - return RSERR_INVALID_MODE; - } - ri.Printf( PRINT_ALL, " %d %d\n", glConfig.vidWidth, glConfig.vidHeight); - - if (!(dpy = XOpenDisplay(NULL))) { - fprintf(stderr, "Error couldn't open the X display\n"); - return RSERR_INVALID_MODE; - } - - scrnum = DefaultScreen(dpy); - root = RootWindow(dpy, scrnum); - - actualWidth = glConfig.vidWidth; - actualHeight = glConfig.vidHeight; - - // Get video mode list - MajorVersion = MinorVersion = 0; - if (!XF86VidModeQueryVersion(dpy, &MajorVersion, &MinorVersion)) { - vidmode_ext = qfalse; - } else { - ri.Printf(PRINT_ALL, "Using XFree86-VidModeExtension Version %d.%d\n", - MajorVersion, MinorVersion); - vidmode_ext = qtrue; - } - - if (vidmode_ext) { - int best_fit, best_dist, dist, x, y; - - XF86VidModeGetAllModeLines(dpy, scrnum, &num_vidmodes, &vidmodes); - - // Are we going fullscreen? If so, let's change video mode - if (fullscreen && !r_fakeFullscreen->integer) { - best_dist = 9999999; - best_fit = -1; - - for (i = 0; i < num_vidmodes; i++) { - if (glConfig.vidWidth > vidmodes[i]->hdisplay || - glConfig.vidHeight > vidmodes[i]->vdisplay) - continue; - - x = glConfig.vidWidth - vidmodes[i]->hdisplay; - y = glConfig.vidHeight - vidmodes[i]->vdisplay; - dist = (x * x) + (y * y); - if (dist < best_dist) { - best_dist = dist; - best_fit = i; - } - } - - if (best_fit != -1) { - actualWidth = vidmodes[best_fit]->hdisplay; - actualHeight = vidmodes[best_fit]->vdisplay; - - // change to the mode - XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[best_fit]); - vidmode_active = qtrue; - - // Move the viewport to top left - XF86VidModeSetViewPort(dpy, scrnum, 0, 0); - } else - fullscreen = 0; - } - } - - - if (!r_colorbits->value) - colorbits = 24; - else - colorbits = r_colorbits->value; - - if ( !Q_stricmp( r_glDriver->string, _3DFX_DRIVER_NAME ) ) - colorbits = 16; - - if (!r_depthbits->value) - depthbits = 24; - else - depthbits = r_depthbits->value; - stencilbits = r_stencilbits->value; - - for (i = 0; i < 16; i++) { - // 0 - default - // 1 - minus colorbits - // 2 - minus depthbits - // 3 - minus stencil - if ((i % 4) == 0 && i) { - // one pass, reduce - switch (i / 4) { - case 2 : - if (colorbits == 24) - colorbits = 16; - break; - case 1 : - if (depthbits == 24) - depthbits = 16; - else if (depthbits == 16) - depthbits = 8; - case 3 : - if (stencilbits == 24) - stencilbits = 16; - else if (stencilbits == 16) - stencilbits = 8; - } - } - - tcolorbits = colorbits; - tdepthbits = depthbits; - tstencilbits = stencilbits; - - if ((i % 4) == 3) { // reduce colorbits - if (tcolorbits == 24) - tcolorbits = 16; - } - - if ((i % 4) == 2) { // reduce depthbits - if (tdepthbits == 24) - tdepthbits = 16; - else if (tdepthbits == 16) - tdepthbits = 8; - } - - if ((i % 4) == 1) { // reduce stencilbits - if (tstencilbits == 24) - tstencilbits = 16; - else if (tstencilbits == 16) - tstencilbits = 8; - else - tstencilbits = 0; - } - - if (tcolorbits == 24) { - attrib[ATTR_RED_IDX] = 8; - attrib[ATTR_GREEN_IDX] = 8; - attrib[ATTR_BLUE_IDX] = 8; - } else { - // must be 16 bit - attrib[ATTR_RED_IDX] = 4; - attrib[ATTR_GREEN_IDX] = 4; - attrib[ATTR_BLUE_IDX] = 4; - } - - attrib[ATTR_DEPTH_IDX] = tdepthbits; // default to 24 depth - attrib[ATTR_STENCIL_IDX] = tstencilbits; - -#if 0 - ri.Printf( PRINT_DEVELOPER, "Attempting %d/%d/%d Color bits, %d depth, %d stencil display...", - attrib[ATTR_RED_IDX], attrib[ATTR_GREEN_IDX], attrib[ATTR_BLUE_IDX], - attrib[ATTR_DEPTH_IDX], attrib[ATTR_STENCIL_IDX]); -#endif - - visinfo = qglXChooseVisual(dpy, scrnum, attrib); - if (!visinfo) { -#if 0 - ri.Printf( PRINT_DEVELOPER, "failed\n"); -#endif - continue; - } - -#if 0 - ri.Printf( PRINT_DEVELOPER, "Successful\n"); -#endif - - ri.Printf( PRINT_ALL, "Using %d/%d/%d Color bits, %d depth, %d stencil display.\n", - attrib[ATTR_RED_IDX], attrib[ATTR_GREEN_IDX], attrib[ATTR_BLUE_IDX], - attrib[ATTR_DEPTH_IDX], attrib[ATTR_STENCIL_IDX]); - - glConfig.colorBits = tcolorbits; - glConfig.depthBits = tdepthbits; - glConfig.stencilBits = tstencilbits; - break; - } - - if (!visinfo) { - ri.Printf( PRINT_ALL, "Couldn't get a visual\n" ); - return RSERR_INVALID_MODE; - } - - /* window attributes */ - attr.background_pixel = BlackPixel(dpy, scrnum); - attr.border_pixel = 0; - attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone); - attr.event_mask = X_MASK; - if (vidmode_active) { - mask = CWBackPixel | CWColormap | CWSaveUnder | CWBackingStore | - CWEventMask | CWOverrideRedirect; - attr.override_redirect = True; - attr.backing_store = NotUseful; - attr.save_under = False; - } else - mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; - - win = XCreateWindow(dpy, root, 0, 0, - actualWidth, actualHeight, - 0, visinfo->depth, InputOutput, - visinfo->visual, mask, &attr); - XMapWindow(dpy, win); - - if (vidmode_active) - XMoveWindow(dpy, win, 0, 0); - - // Check for DGA - if (in_dgamouse->value) { - if (!XF86DGAQueryVersion(dpy, &MajorVersion, &MinorVersion)) { - // unable to query, probalby not supported - ri.Printf( PRINT_ALL, "Failed to detect XF86DGA Mouse\n" ); - ri.Cvar_Set( "in_dgamouse", "0" ); - } else - ri.Printf( PRINT_ALL, "XF86DGA Mouse (Version %d.%d) initialized\n", - MajorVersion, MinorVersion); - } - - XFlush(dpy); - - ctx = qglXCreateContext(dpy, visinfo, NULL, True); - - qglXMakeCurrent(dpy, win, ctx); - - return RSERR_OK; -} - -/* -** GLW_InitExtensions -*/ -static void GLW_InitExtensions( void ) -{ - // Use modern texture compression extensions - if ( strstr( glConfig.extensions_string, "ARB_texture_compression" ) && strstr( glConfig.extensions_string, "EXT_texture_compression_s3tc" ) ) - { - if ( r_ext_compressed_textures->value ) - { - glConfig.textureCompression = TC_S3TC_DXT; - ri.Printf( PRINT_ALL, "...using GL_EXT_texture_compression_s3tc\n" ); - } - else - { - glConfig.textureCompression = TC_NONE; - ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_compression_s3tc\n" ); - } - } - // Or check for old ones - else if ( strstr( glConfig.extensions_string, "GL_S3_s3tc" ) ) - { - if ( r_ext_compressed_textures->value ) - { - glConfig.textureCompression = TC_S3TC; - ri.Printf( PRINT_ALL, "...using GL_S3_s3tc\n" ); - } - else - { - glConfig.textureCompression = TC_NONE; - ri.Printf( PRINT_ALL, "...ignoring GL_S3_s3tc\n" ); - } - } - else - { - glConfig.textureCompression = TC_NONE; - ri.Printf( PRINT_ALL, "...no texture compression found\n" ); - } - -#if 0 - // WGL_EXT_swap_control - if ( strstr( glConfig.extensions_string, "WGL_EXT_swap_control" ) ) - { - qwglSwapIntervalEXT = ( BOOL (WINAPI *)(int)) qwglGetProcAddress( "wglSwapIntervalEXT" ); - ri.Printf( PRINT_ALL, "...using WGL_EXT_swap_control\n" ); - } - else - { - ri.Printf( PRINT_ALL, "...WGL_EXT_swap_control not found\n" ); - } -#endif - - // GL_ARB_multitexture - qglMultiTexCoord2fARB = NULL; - qglActiveTextureARB = NULL; - qglClientActiveTextureARB = NULL; - if ( strstr( glConfig.extensions_string, "GL_ARB_multitexture" ) ) - { - if ( r_ext_multitexture->value ) - { - qglMultiTexCoord2fARB = ( PFNGLMULTITEXCOORD2FARBPROC ) dlsym( glw_state.OpenGLLib, "glMultiTexCoord2fARB" ); - qglActiveTextureARB = ( PFNGLACTIVETEXTUREARBPROC ) dlsym( glw_state.OpenGLLib, "glActiveTextureARB" ); - qglClientActiveTextureARB = ( PFNGLCLIENTACTIVETEXTUREARBPROC ) dlsym( glw_state.OpenGLLib, "glClientActiveTextureARB" ); - - if ( qglActiveTextureARB ) - { - ri.Printf( PRINT_ALL, "...using GL_ARB_multitexture\n" ); - } - else - { - ri.Printf( PRINT_ALL, "...blind search for ARB_multitexture failed\n" ); - } - } - else - { - ri.Printf( PRINT_ALL, "...ignoring GL_ARB_multitexture\n" ); - } - } - else - { - ri.Printf( PRINT_ALL, "...GL_ARB_multitexture not found\n" ); - } - - // GL_EXT_texture_filter_anisotropic - glConfig.textureFilterAnisotropicAvailable = qfalse; - if ( strstr( glConfig.extensions_string, "EXT_texture_filter_anisotropic" ) ) - { - glConfig.textureFilterAnisotropicAvailable = qtrue; - ri.Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic available\n" ); - - if ( r_ext_texture_filter_anisotropic->integer ) - { - ri.Printf( PRINT_ALL, "...using GL_EXT_texture_filter_anisotropic\n" ); - } - else - { - ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_filter_anisotropic\n" ); - } - ri.Cvar_Set( "r_ext_texture_filter_anisotropic_avail", "1" ); - } - else - { - ri.Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic not found\n" ); - ri.Cvar_Set( "r_ext_texture_filter_anisotropic_avail", "0" ); - } - - // GL_EXT_compiled_vertex_array - if ( strstr( glConfig.extensions_string, "GL_EXT_compiled_vertex_array" ) ) - { - if ( r_ext_compiled_vertex_array->value ) - { - ri.Printf( PRINT_ALL, "...using GL_EXT_compiled_vertex_array\n" ); - qglLockArraysEXT = ( void ( APIENTRY * )( int, int ) ) dlsym( glw_state.OpenGLLib, "glLockArraysEXT" ); - qglUnlockArraysEXT = ( void ( APIENTRY * )( void ) ) dlsym( glw_state.OpenGLLib, "glUnlockArraysEXT" ); - if (!qglLockArraysEXT || !qglUnlockArraysEXT) { - ri.Error (ERR_FATAL, "bad getprocaddress"); - } - } - else - { - ri.Printf( PRINT_ALL, "...ignoring GL_EXT_compiled_vertex_array\n" ); - } - } - else - { - ri.Printf( PRINT_ALL, "...GL_EXT_compiled_vertex_array not found\n" ); - } - -} - -/* -** GLW_LoadOpenGL -** -** GLimp_win.c internal function that that attempts to load and use -** a specific OpenGL DLL. -*/ -static qboolean GLW_LoadOpenGL( const char *name ) -{ - qboolean fullscreen; - - ri.Printf( PRINT_ALL, "...loading %s: ", name ); - - // disable the 3Dfx splash screen and set gamma - // we do this all the time, but it shouldn't hurt anything - // on non-3Dfx stuff - putenv("FX_GLIDE_NO_SPLASH=0"); - - // Mesa VooDoo hacks - putenv("MESA_GLX_FX=fullscreen\n"); - - // load the QGL layer - if ( QGL_Init( name ) ) - { - fullscreen = r_fullscreen->integer; - - // create the window and set up the context - if ( !GLW_StartDriverAndSetMode( name, r_mode->integer, fullscreen ) ) - { - if (r_mode->integer != 3) { - if ( !GLW_StartDriverAndSetMode( name, 3, fullscreen ) ) { - goto fail; - } - } else - goto fail; - } - - return qtrue; - } - else - { - ri.Printf( PRINT_ALL, "failed\n" ); - } -fail: - - QGL_Shutdown(); - - return qfalse; -} - -/* -** GLimp_Init -** -** This routine is responsible for initializing the OS specific portions -** of OpenGL. -*/ -void GLimp_Init( void ) -{ - qboolean attemptedlibGL = qfalse; - qboolean attempted3Dfx = qfalse; - qboolean success = qfalse; - char buf[1024]; - cvar_t *lastValidRenderer = ri.Cvar_Get( "r_lastValidRenderer", "(uninitialized)", CVAR_ARCHIVE ); - cvar_t *cv; - - glConfig.deviceSupportsGamma = qfalse; - - InitSig(); - - // - // load and initialize the specific OpenGL driver - // - if ( !GLW_LoadOpenGL( r_glDriver->string ) ) - { - if ( !Q_stricmp( r_glDriver->string, OPENGL_DRIVER_NAME ) ) - { - attemptedlibGL = qtrue; - } - else if ( !Q_stricmp( r_glDriver->string, _3DFX_DRIVER_NAME ) ) - { - attempted3Dfx = qtrue; - } - - if ( !attempted3Dfx && !success ) - { - attempted3Dfx = qtrue; - if ( GLW_LoadOpenGL( _3DFX_DRIVER_NAME ) ) - { - ri.Cvar_Set( "r_glDriver", _3DFX_DRIVER_NAME ); - r_glDriver->modified = qfalse; - success = qtrue; - } - } - - // try ICD before trying 3Dfx standalone driver - if ( !attemptedlibGL && !success ) - { - attemptedlibGL = qtrue; - if ( GLW_LoadOpenGL( OPENGL_DRIVER_NAME ) ) - { - ri.Cvar_Set( "r_glDriver", OPENGL_DRIVER_NAME ); - r_glDriver->modified = qfalse; - success = qtrue; - } - } - - if (!success) - ri.Error( ERR_FATAL, "GLimp_Init() - could not load OpenGL subsystem\n" ); - - } - - // get our config strings - Q_strncpyz( glConfig.vendor_string, qglGetString (GL_VENDOR), sizeof( glConfig.vendor_string ) ); - Q_strncpyz( glConfig.renderer_string, qglGetString (GL_RENDERER), sizeof( glConfig.renderer_string ) ); - if (*glConfig.renderer_string && glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] == '\n') - glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] = 0; - Q_strncpyz( glConfig.version_string, qglGetString (GL_VERSION), sizeof( glConfig.version_string ) ); - Q_strncpyz( glConfig.extensions_string, qglGetString (GL_EXTENSIONS), sizeof( glConfig.extensions_string ) ); - - // - // chipset specific configuration - // - strcpy( buf, glConfig.renderer_string ); - strlwr( buf ); - - if ( Q_stricmp( lastValidRenderer->string, glConfig.renderer_string ) ) - { - ri.Cvar_Set( "r_picmip", "1" ); - ri.Cvar_Set( "r_twopartfog", "0" ); - ri.Cvar_Set( "r_textureMode", "GL_LINEAR_MIPMAP_NEAREST" ); - - // - // voodoo issues - // - if ( strstr( buf, "voodoo" ) && !strstr( buf, "banshee" ) ) - { - ri.Cvar_Set( "r_fakeFullscreen", "1"); - } - - // - // Riva128 issues - // - if ( strstr( buf, "riva 128" ) ) - { - ri.Cvar_Set( "r_twopartfog", "1" ); - } - - // - // Rage Pro issues - // - if ( strstr( buf, "rage pro" ) ) - { - ri.Cvar_Set( "r_mode", "2" ); - ri.Cvar_Set( "r_twopartfog", "1" ); - } - - // - // Permedia2 issues - // - if ( strstr( buf, "permedia2" ) ) - { - ri.Cvar_Set( "r_vertexLight", "1" ); - } - - // - // Riva TNT issues - // - if ( strstr( buf, "riva tnt " ) ) - { - if ( r_texturebits->integer == 32 || - ( ( r_texturebits->integer == 0 ) && glConfig.colorBits > 16 ) ) - { - ri.Cvar_Set( "r_picmip", "1" ); - } - } - - ri.Cvar_Set( "r_lastValidRenderer", glConfig.renderer_string ); - } - - // initialize extensions - GLW_InitExtensions(); - - InitSig(); - - return; -} - - -/* -** GLimp_EndFrame -** -** Responsible for doing a swapbuffers and possibly for other stuff -** as yet to be determined. Probably better not to make this a GLimp -** function and instead do a call to GLimp_SwapBuffers. -*/ -void GLimp_EndFrame (void) -{ -#if 0 - int err; - - if ( !glState.finishCalled ) - qglFinish(); - - // check for errors - if ( !gl_ignore_errors->value ) { - if ( ( err = qglGetError() ) != GL_NO_ERROR ) - { - ri.Error( ERR_FATAL, "GLimp_EndFrame() - glGetError() failed (0x%x)!\n", err ); - } - } -#endif - - // don't flip if drawing to front buffer - if ( stricmp( r_drawBuffer->string, "GL_FRONT" ) != 0 ) - { - qglXSwapBuffers(dpy, win); - } - - // check logging - QGL_EnableLogging( r_logFile->value ); - -#if 0 - GLimp_LogComment( "*** RE_EndFrame ***\n" ); - - // decrement log - if ( gl_log->value ) - { - ri.Cvar_Set( "gl_log", va("%i",gl_log->value - 1 ) ); - } -#endif -} - -/* -=========================================================== - -SMP acceleration - -=========================================================== -*/ - -sem_t renderCommandsEvent; -sem_t renderCompletedEvent; -sem_t renderActiveEvent; - -void (*glimpRenderThread)( void ); - -void GLimp_RenderThreadWrapper( void *stub ) { - glimpRenderThread(); - -#if 0 - // unbind the context before we die - qglXMakeCurrent(dpy, None, NULL); -#endif -} - - -/* -======================= -GLimp_SpawnRenderThread -======================= -*/ -pthread_t renderThreadHandle; -qboolean GLimp_SpawnRenderThread( void (*function)( void ) ) { - - sem_init( &renderCommandsEvent, 0, 0 ); - sem_init( &renderCompletedEvent, 0, 0 ); - sem_init( &renderActiveEvent, 0, 0 ); - - glimpRenderThread = function; - - if (pthread_create( &renderThreadHandle, NULL, - GLimp_RenderThreadWrapper, NULL)) { - return qfalse; - } - - return qtrue; -} - -static void *smpData; -static int glXErrors; - -void *GLimp_RendererSleep( void ) { - void *data; - -#if 0 - if ( !qglXMakeCurrent(dpy, None, NULL) ) { - glXErrors++; - } -#endif - -// ResetEvent( renderActiveEvent ); - - // after this, the front end can exit GLimp_FrontEndSleep - sem_post ( &renderCompletedEvent ); - - sem_wait ( &renderCommandsEvent ); - -#if 0 - if ( !qglXMakeCurrent(dpy, win, ctx) ) { - glXErrors++; - } -#endif - -// ResetEvent( renderCompletedEvent ); -// ResetEvent( renderCommandsEvent ); - - data = smpData; - - // after this, the main thread can exit GLimp_WakeRenderer - sem_post ( &renderActiveEvent ); - - return data; -} - - -void GLimp_FrontEndSleep( void ) { - sem_wait ( &renderCompletedEvent ); - -#if 0 - if ( !qglXMakeCurrent(dpy, win, ctx) ) { - glXErrors++; - } -#endif -} - - -void GLimp_WakeRenderer( void *data ) { - smpData = data; - -#if 0 - if ( !qglXMakeCurrent(dpy, None, NULL) ) { - glXErrors++; - } -#endif - - // after this, the renderer can continue through GLimp_RendererSleep - sem_post( &renderCommandsEvent ); - - sem_wait( &renderActiveEvent ); -} - -/*===========================================================*/ - -/*****************************************************************************/ -/* MOUSE */ -/*****************************************************************************/ - -void IN_Init(void) -{ - // mouse variables - in_mouse = Cvar_Get ("in_mouse", "1", CVAR_ARCHIVE); - in_dgamouse = Cvar_Get ("in_dgamouse", "1", CVAR_ARCHIVE); - - if (in_mouse->value) - mouse_avail = qtrue; - else - mouse_avail = qfalse; -} - -void IN_Shutdown(void) -{ - mouse_avail = qfalse; -} - -void IN_MouseMove(void) -{ - if (!mouse_avail || !dpy || !win) - return; - -#if 0 - if (!dgamouse) { - Window root, child; - int root_x, root_y; - int win_x, win_y; - unsigned int mask_return; - int mwx = glConfig.vidWidth/2; - int mwy = glConfig.vidHeight/2; - - XQueryPointer(dpy, win, &root, &child, - &root_x, &root_y, &win_x, &win_y, &mask_return); - - mx = win_x - mwx; - my = win_y - mwy; - - XWarpPointer(dpy,None,win,0,0,0,0, mwx, mwy); - } -#endif - - if (mx || my) - Sys_QueEvent( 0, SE_MOUSE, mx, my, 0, NULL ); - mx = my = 0; -} - -void IN_Frame (void) -{ - if ( cls.keyCatchers || cls.state != CA_ACTIVE ) { - // temporarily deactivate if not in the game and - // running on the desktop - // voodoo always counts as full screen - if (Cvar_VariableValue ("r_fullscreen") == 0 - && strcmp( Cvar_VariableString("r_glDriver"), _3DFX_DRIVER_NAME ) ) { - IN_DeactivateMouse (); - return; - } - if (dpy && !autorepeaton) { - XAutoRepeatOn(dpy); - autorepeaton = qtrue; - } - } else if (dpy && autorepeaton) { - XAutoRepeatOff(dpy); - autorepeaton = qfalse; - } - - IN_ActivateMouse(); - - // post events to the system que - IN_MouseMove(); -} - -void IN_Activate(void) -{ -} - -void Sys_SendKeyEvents (void) -{ - XEvent event; - - if (!dpy) - return; - - HandleEvents(); -// while (XCheckMaskEvent(dpy,KEY_MASK|MOUSE_MASK,&event)) -// HandleEvent(&event); -} - diff --git a/code/unix/linux_qgl.c b/code/unix/linux_qgl.c deleted file mode 100644 index 3dac0a7..0000000 --- a/code/unix/linux_qgl.c +++ /dev/null @@ -1,4111 +0,0 @@ -/* -** LINUX_QGL.C -** -** This file implements the operating system binding of GL to QGL function -** pointers. When doing a port of Quake2 you must implement the following -** two functions: -** -** QGL_Init() - loads libraries, assigns function pointers, etc. -** QGL_Shutdown() - unloads libraries, NULLs function pointers -*/ -#include -#include "../renderer/tr_local.h" -#include "unix_glw.h" - -#include -#include - -#include - -//FX Mesa Functions -fxMesaContext (*qfxMesaCreateContext)(GLuint win, GrScreenResolution_t, GrScreenRefresh_t, const GLint attribList[]); -fxMesaContext (*qfxMesaCreateBestContext)(GLuint win, GLint width, GLint height, const GLint attribList[]); -void (*qfxMesaDestroyContext)(fxMesaContext ctx); -void (*qfxMesaMakeCurrent)(fxMesaContext ctx); -fxMesaContext (*qfxMesaGetCurrentContext)(void); -void (*qfxMesaSwapBuffers)(void); - -//GLX Functions -XVisualInfo * (*qglXChooseVisual)( Display *dpy, int screen, int *attribList ); -GLXContext (*qglXCreateContext)( Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct ); -void (*qglXDestroyContext)( Display *dpy, GLXContext ctx ); -Bool (*qglXMakeCurrent)( Display *dpy, GLXDrawable drawable, GLXContext ctx); -void (*qglXCopyContext)( Display *dpy, GLXContext src, GLXContext dst, GLuint mask ); -void (*qglXSwapBuffers)( Display *dpy, GLXDrawable drawable ); - -void ( APIENTRY * qglAccum )(GLenum op, GLfloat value); -void ( APIENTRY * qglAlphaFunc )(GLenum func, GLclampf ref); -GLboolean ( APIENTRY * qglAreTexturesResident )(GLsizei n, const GLuint *textures, GLboolean *residences); -void ( APIENTRY * qglArrayElement )(GLint i); -void ( APIENTRY * qglBegin )(GLenum mode); -void ( APIENTRY * qglBindTexture )(GLenum target, GLuint texture); -void ( APIENTRY * qglBitmap )(GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap); -void ( APIENTRY * qglBlendFunc )(GLenum sfactor, GLenum dfactor); -void ( APIENTRY * qglCallList )(GLuint list); -void ( APIENTRY * qglCallLists )(GLsizei n, GLenum type, const GLvoid *lists); -void ( APIENTRY * qglClear )(GLbitfield mask); -void ( APIENTRY * qglClearAccum )(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); -void ( APIENTRY * qglClearColor )(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); -void ( APIENTRY * qglClearDepth )(GLclampd depth); -void ( APIENTRY * qglClearIndex )(GLfloat c); -void ( APIENTRY * qglClearStencil )(GLint s); -void ( APIENTRY * qglClipPlane )(GLenum plane, const GLdouble *equation); -void ( APIENTRY * qglColor3b )(GLbyte red, GLbyte green, GLbyte blue); -void ( APIENTRY * qglColor3bv )(const GLbyte *v); -void ( APIENTRY * qglColor3d )(GLdouble red, GLdouble green, GLdouble blue); -void ( APIENTRY * qglColor3dv )(const GLdouble *v); -void ( APIENTRY * qglColor3f )(GLfloat red, GLfloat green, GLfloat blue); -void ( APIENTRY * qglColor3fv )(const GLfloat *v); -void ( APIENTRY * qglColor3i )(GLint red, GLint green, GLint blue); -void ( APIENTRY * qglColor3iv )(const GLint *v); -void ( APIENTRY * qglColor3s )(GLshort red, GLshort green, GLshort blue); -void ( APIENTRY * qglColor3sv )(const GLshort *v); -void ( APIENTRY * qglColor3ub )(GLubyte red, GLubyte green, GLubyte blue); -void ( APIENTRY * qglColor3ubv )(const GLubyte *v); -void ( APIENTRY * qglColor3ui )(GLuint red, GLuint green, GLuint blue); -void ( APIENTRY * qglColor3uiv )(const GLuint *v); -void ( APIENTRY * qglColor3us )(GLushort red, GLushort green, GLushort blue); -void ( APIENTRY * qglColor3usv )(const GLushort *v); -void ( APIENTRY * qglColor4b )(GLbyte red, GLbyte green, GLbyte blue, GLbyte alpha); -void ( APIENTRY * qglColor4bv )(const GLbyte *v); -void ( APIENTRY * qglColor4d )(GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha); -void ( APIENTRY * qglColor4dv )(const GLdouble *v); -void ( APIENTRY * qglColor4f )(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); -void ( APIENTRY * qglColor4fv )(const GLfloat *v); -void ( APIENTRY * qglColor4i )(GLint red, GLint green, GLint blue, GLint alpha); -void ( APIENTRY * qglColor4iv )(const GLint *v); -void ( APIENTRY * qglColor4s )(GLshort red, GLshort green, GLshort blue, GLshort alpha); -void ( APIENTRY * qglColor4sv )(const GLshort *v); -void ( APIENTRY * qglColor4ub )(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha); -void ( APIENTRY * qglColor4ubv )(const GLubyte *v); -void ( APIENTRY * qglColor4ui )(GLuint red, GLuint green, GLuint blue, GLuint alpha); -void ( APIENTRY * qglColor4uiv )(const GLuint *v); -void ( APIENTRY * qglColor4us )(GLushort red, GLushort green, GLushort blue, GLushort alpha); -void ( APIENTRY * qglColor4usv )(const GLushort *v); -void ( APIENTRY * qglColorMask )(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); -void ( APIENTRY * qglColorMaterial )(GLenum face, GLenum mode); -void ( APIENTRY * qglColorPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); -void ( APIENTRY * qglCopyPixels )(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type); -void ( APIENTRY * qglCopyTexImage1D )(GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLint border); -void ( APIENTRY * qglCopyTexImage2D )(GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); -void ( APIENTRY * qglCopyTexSubImage1D )(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); -void ( APIENTRY * qglCopyTexSubImage2D )(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); -void ( APIENTRY * qglCullFace )(GLenum mode); -void ( APIENTRY * qglDeleteLists )(GLuint list, GLsizei range); -void ( APIENTRY * qglDeleteTextures )(GLsizei n, const GLuint *textures); -void ( APIENTRY * qglDepthFunc )(GLenum func); -void ( APIENTRY * qglDepthMask )(GLboolean flag); -void ( APIENTRY * qglDepthRange )(GLclampd zNear, GLclampd zFar); -void ( APIENTRY * qglDisable )(GLenum cap); -void ( APIENTRY * qglDisableClientState )(GLenum array); -void ( APIENTRY * qglDrawArrays )(GLenum mode, GLint first, GLsizei count); -void ( APIENTRY * qglDrawBuffer )(GLenum mode); -void ( APIENTRY * qglDrawElements )(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices); -void ( APIENTRY * qglDrawPixels )(GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); -void ( APIENTRY * qglEdgeFlag )(GLboolean flag); -void ( APIENTRY * qglEdgeFlagPointer )(GLsizei stride, const GLvoid *pointer); -void ( APIENTRY * qglEdgeFlagv )(const GLboolean *flag); -void ( APIENTRY * qglEnable )(GLenum cap); -void ( APIENTRY * qglEnableClientState )(GLenum array); -void ( APIENTRY * qglEnd )(void); -void ( APIENTRY * qglEndList )(void); -void ( APIENTRY * qglEvalCoord1d )(GLdouble u); -void ( APIENTRY * qglEvalCoord1dv )(const GLdouble *u); -void ( APIENTRY * qglEvalCoord1f )(GLfloat u); -void ( APIENTRY * qglEvalCoord1fv )(const GLfloat *u); -void ( APIENTRY * qglEvalCoord2d )(GLdouble u, GLdouble v); -void ( APIENTRY * qglEvalCoord2dv )(const GLdouble *u); -void ( APIENTRY * qglEvalCoord2f )(GLfloat u, GLfloat v); -void ( APIENTRY * qglEvalCoord2fv )(const GLfloat *u); -void ( APIENTRY * qglEvalMesh1 )(GLenum mode, GLint i1, GLint i2); -void ( APIENTRY * qglEvalMesh2 )(GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2); -void ( APIENTRY * qglEvalPoint1 )(GLint i); -void ( APIENTRY * qglEvalPoint2 )(GLint i, GLint j); -void ( APIENTRY * qglFeedbackBuffer )(GLsizei size, GLenum type, GLfloat *buffer); -void ( APIENTRY * qglFinish )(void); -void ( APIENTRY * qglFlush )(void); -void ( APIENTRY * qglFogf )(GLenum pname, GLfloat param); -void ( APIENTRY * qglFogfv )(GLenum pname, const GLfloat *params); -void ( APIENTRY * qglFogi )(GLenum pname, GLint param); -void ( APIENTRY * qglFogiv )(GLenum pname, const GLint *params); -void ( APIENTRY * qglFrontFace )(GLenum mode); -void ( APIENTRY * qglFrustum )(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); -GLuint ( APIENTRY * qglGenLists )(GLsizei range); -void ( APIENTRY * qglGenTextures )(GLsizei n, GLuint *textures); -void ( APIENTRY * qglGetBooleanv )(GLenum pname, GLboolean *params); -void ( APIENTRY * qglGetClipPlane )(GLenum plane, GLdouble *equation); -void ( APIENTRY * qglGetDoublev )(GLenum pname, GLdouble *params); -GLenum ( APIENTRY * qglGetError )(void); -void ( APIENTRY * qglGetFloatv )(GLenum pname, GLfloat *params); -void ( APIENTRY * qglGetIntegerv )(GLenum pname, GLint *params); -void ( APIENTRY * qglGetLightfv )(GLenum light, GLenum pname, GLfloat *params); -void ( APIENTRY * qglGetLightiv )(GLenum light, GLenum pname, GLint *params); -void ( APIENTRY * qglGetMapdv )(GLenum target, GLenum query, GLdouble *v); -void ( APIENTRY * qglGetMapfv )(GLenum target, GLenum query, GLfloat *v); -void ( APIENTRY * qglGetMapiv )(GLenum target, GLenum query, GLint *v); -void ( APIENTRY * qglGetMaterialfv )(GLenum face, GLenum pname, GLfloat *params); -void ( APIENTRY * qglGetMaterialiv )(GLenum face, GLenum pname, GLint *params); -void ( APIENTRY * qglGetPixelMapfv )(GLenum map, GLfloat *values); -void ( APIENTRY * qglGetPixelMapuiv )(GLenum map, GLuint *values); -void ( APIENTRY * qglGetPixelMapusv )(GLenum map, GLushort *values); -void ( APIENTRY * qglGetPointerv )(GLenum pname, GLvoid* *params); -void ( APIENTRY * qglGetPolygonStipple )(GLubyte *mask); -const GLubyte * ( APIENTRY * qglGetString )(GLenum name); -void ( APIENTRY * qglGetTexEnvfv )(GLenum target, GLenum pname, GLfloat *params); -void ( APIENTRY * qglGetTexEnviv )(GLenum target, GLenum pname, GLint *params); -void ( APIENTRY * qglGetTexGendv )(GLenum coord, GLenum pname, GLdouble *params); -void ( APIENTRY * qglGetTexGenfv )(GLenum coord, GLenum pname, GLfloat *params); -void ( APIENTRY * qglGetTexGeniv )(GLenum coord, GLenum pname, GLint *params); -void ( APIENTRY * qglGetTexImage )(GLenum target, GLint level, GLenum format, GLenum type, GLvoid *pixels); -void ( APIENTRY * qglGetTexLevelParameterfv )(GLenum target, GLint level, GLenum pname, GLfloat *params); -void ( APIENTRY * qglGetTexLevelParameteriv )(GLenum target, GLint level, GLenum pname, GLint *params); -void ( APIENTRY * qglGetTexParameterfv )(GLenum target, GLenum pname, GLfloat *params); -void ( APIENTRY * qglGetTexParameteriv )(GLenum target, GLenum pname, GLint *params); -void ( APIENTRY * qglHint )(GLenum target, GLenum mode); -void ( APIENTRY * qglIndexMask )(GLuint mask); -void ( APIENTRY * qglIndexPointer )(GLenum type, GLsizei stride, const GLvoid *pointer); -void ( APIENTRY * qglIndexd )(GLdouble c); -void ( APIENTRY * qglIndexdv )(const GLdouble *c); -void ( APIENTRY * qglIndexf )(GLfloat c); -void ( APIENTRY * qglIndexfv )(const GLfloat *c); -void ( APIENTRY * qglIndexi )(GLint c); -void ( APIENTRY * qglIndexiv )(const GLint *c); -void ( APIENTRY * qglIndexs )(GLshort c); -void ( APIENTRY * qglIndexsv )(const GLshort *c); -void ( APIENTRY * qglIndexub )(GLubyte c); -void ( APIENTRY * qglIndexubv )(const GLubyte *c); -void ( APIENTRY * qglInitNames )(void); -void ( APIENTRY * qglInterleavedArrays )(GLenum format, GLsizei stride, const GLvoid *pointer); -GLboolean ( APIENTRY * qglIsEnabled )(GLenum cap); -GLboolean ( APIENTRY * qglIsList )(GLuint list); -GLboolean ( APIENTRY * qglIsTexture )(GLuint texture); -void ( APIENTRY * qglLightModelf )(GLenum pname, GLfloat param); -void ( APIENTRY * qglLightModelfv )(GLenum pname, const GLfloat *params); -void ( APIENTRY * qglLightModeli )(GLenum pname, GLint param); -void ( APIENTRY * qglLightModeliv )(GLenum pname, const GLint *params); -void ( APIENTRY * qglLightf )(GLenum light, GLenum pname, GLfloat param); -void ( APIENTRY * qglLightfv )(GLenum light, GLenum pname, const GLfloat *params); -void ( APIENTRY * qglLighti )(GLenum light, GLenum pname, GLint param); -void ( APIENTRY * qglLightiv )(GLenum light, GLenum pname, const GLint *params); -void ( APIENTRY * qglLineStipple )(GLint factor, GLushort pattern); -void ( APIENTRY * qglLineWidth )(GLfloat width); -void ( APIENTRY * qglListBase )(GLuint base); -void ( APIENTRY * qglLoadIdentity )(void); -void ( APIENTRY * qglLoadMatrixd )(const GLdouble *m); -void ( APIENTRY * qglLoadMatrixf )(const GLfloat *m); -void ( APIENTRY * qglLoadName )(GLuint name); -void ( APIENTRY * qglLogicOp )(GLenum opcode); -void ( APIENTRY * qglMap1d )(GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points); -void ( APIENTRY * qglMap1f )(GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points); -void ( APIENTRY * qglMap2d )(GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points); -void ( APIENTRY * qglMap2f )(GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points); -void ( APIENTRY * qglMapGrid1d )(GLint un, GLdouble u1, GLdouble u2); -void ( APIENTRY * qglMapGrid1f )(GLint un, GLfloat u1, GLfloat u2); -void ( APIENTRY * qglMapGrid2d )(GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2); -void ( APIENTRY * qglMapGrid2f )(GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2); -void ( APIENTRY * qglMaterialf )(GLenum face, GLenum pname, GLfloat param); -void ( APIENTRY * qglMaterialfv )(GLenum face, GLenum pname, const GLfloat *params); -void ( APIENTRY * qglMateriali )(GLenum face, GLenum pname, GLint param); -void ( APIENTRY * qglMaterialiv )(GLenum face, GLenum pname, const GLint *params); -void ( APIENTRY * qglMatrixMode )(GLenum mode); -void ( APIENTRY * qglMultMatrixd )(const GLdouble *m); -void ( APIENTRY * qglMultMatrixf )(const GLfloat *m); -void ( APIENTRY * qglNewList )(GLuint list, GLenum mode); -void ( APIENTRY * qglNormal3b )(GLbyte nx, GLbyte ny, GLbyte nz); -void ( APIENTRY * qglNormal3bv )(const GLbyte *v); -void ( APIENTRY * qglNormal3d )(GLdouble nx, GLdouble ny, GLdouble nz); -void ( APIENTRY * qglNormal3dv )(const GLdouble *v); -void ( APIENTRY * qglNormal3f )(GLfloat nx, GLfloat ny, GLfloat nz); -void ( APIENTRY * qglNormal3fv )(const GLfloat *v); -void ( APIENTRY * qglNormal3i )(GLint nx, GLint ny, GLint nz); -void ( APIENTRY * qglNormal3iv )(const GLint *v); -void ( APIENTRY * qglNormal3s )(GLshort nx, GLshort ny, GLshort nz); -void ( APIENTRY * qglNormal3sv )(const GLshort *v); -void ( APIENTRY * qglNormalPointer )(GLenum type, GLsizei stride, const GLvoid *pointer); -void ( APIENTRY * qglOrtho )(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); -void ( APIENTRY * qglPassThrough )(GLfloat token); -void ( APIENTRY * qglPixelMapfv )(GLenum map, GLsizei mapsize, const GLfloat *values); -void ( APIENTRY * qglPixelMapuiv )(GLenum map, GLsizei mapsize, const GLuint *values); -void ( APIENTRY * qglPixelMapusv )(GLenum map, GLsizei mapsize, const GLushort *values); -void ( APIENTRY * qglPixelStoref )(GLenum pname, GLfloat param); -void ( APIENTRY * qglPixelStorei )(GLenum pname, GLint param); -void ( APIENTRY * qglPixelTransferf )(GLenum pname, GLfloat param); -void ( APIENTRY * qglPixelTransferi )(GLenum pname, GLint param); -void ( APIENTRY * qglPixelZoom )(GLfloat xfactor, GLfloat yfactor); -void ( APIENTRY * qglPointSize )(GLfloat size); -void ( APIENTRY * qglPolygonMode )(GLenum face, GLenum mode); -void ( APIENTRY * qglPolygonOffset )(GLfloat factor, GLfloat units); -void ( APIENTRY * qglPolygonStipple )(const GLubyte *mask); -void ( APIENTRY * qglPopAttrib )(void); -void ( APIENTRY * qglPopClientAttrib )(void); -void ( APIENTRY * qglPopMatrix )(void); -void ( APIENTRY * qglPopName )(void); -void ( APIENTRY * qglPrioritizeTextures )(GLsizei n, const GLuint *textures, const GLclampf *priorities); -void ( APIENTRY * qglPushAttrib )(GLbitfield mask); -void ( APIENTRY * qglPushClientAttrib )(GLbitfield mask); -void ( APIENTRY * qglPushMatrix )(void); -void ( APIENTRY * qglPushName )(GLuint name); -void ( APIENTRY * qglRasterPos2d )(GLdouble x, GLdouble y); -void ( APIENTRY * qglRasterPos2dv )(const GLdouble *v); -void ( APIENTRY * qglRasterPos2f )(GLfloat x, GLfloat y); -void ( APIENTRY * qglRasterPos2fv )(const GLfloat *v); -void ( APIENTRY * qglRasterPos2i )(GLint x, GLint y); -void ( APIENTRY * qglRasterPos2iv )(const GLint *v); -void ( APIENTRY * qglRasterPos2s )(GLshort x, GLshort y); -void ( APIENTRY * qglRasterPos2sv )(const GLshort *v); -void ( APIENTRY * qglRasterPos3d )(GLdouble x, GLdouble y, GLdouble z); -void ( APIENTRY * qglRasterPos3dv )(const GLdouble *v); -void ( APIENTRY * qglRasterPos3f )(GLfloat x, GLfloat y, GLfloat z); -void ( APIENTRY * qglRasterPos3fv )(const GLfloat *v); -void ( APIENTRY * qglRasterPos3i )(GLint x, GLint y, GLint z); -void ( APIENTRY * qglRasterPos3iv )(const GLint *v); -void ( APIENTRY * qglRasterPos3s )(GLshort x, GLshort y, GLshort z); -void ( APIENTRY * qglRasterPos3sv )(const GLshort *v); -void ( APIENTRY * qglRasterPos4d )(GLdouble x, GLdouble y, GLdouble z, GLdouble w); -void ( APIENTRY * qglRasterPos4dv )(const GLdouble *v); -void ( APIENTRY * qglRasterPos4f )(GLfloat x, GLfloat y, GLfloat z, GLfloat w); -void ( APIENTRY * qglRasterPos4fv )(const GLfloat *v); -void ( APIENTRY * qglRasterPos4i )(GLint x, GLint y, GLint z, GLint w); -void ( APIENTRY * qglRasterPos4iv )(const GLint *v); -void ( APIENTRY * qglRasterPos4s )(GLshort x, GLshort y, GLshort z, GLshort w); -void ( APIENTRY * qglRasterPos4sv )(const GLshort *v); -void ( APIENTRY * qglReadBuffer )(GLenum mode); -void ( APIENTRY * qglReadPixels )(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels); -void ( APIENTRY * qglRectd )(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2); -void ( APIENTRY * qglRectdv )(const GLdouble *v1, const GLdouble *v2); -void ( APIENTRY * qglRectf )(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2); -void ( APIENTRY * qglRectfv )(const GLfloat *v1, const GLfloat *v2); -void ( APIENTRY * qglRecti )(GLint x1, GLint y1, GLint x2, GLint y2); -void ( APIENTRY * qglRectiv )(const GLint *v1, const GLint *v2); -void ( APIENTRY * qglRects )(GLshort x1, GLshort y1, GLshort x2, GLshort y2); -void ( APIENTRY * qglRectsv )(const GLshort *v1, const GLshort *v2); -GLint ( APIENTRY * qglRenderMode )(GLenum mode); -void ( APIENTRY * qglRotated )(GLdouble angle, GLdouble x, GLdouble y, GLdouble z); -void ( APIENTRY * qglRotatef )(GLfloat angle, GLfloat x, GLfloat y, GLfloat z); -void ( APIENTRY * qglScaled )(GLdouble x, GLdouble y, GLdouble z); -void ( APIENTRY * qglScalef )(GLfloat x, GLfloat y, GLfloat z); -void ( APIENTRY * qglScissor )(GLint x, GLint y, GLsizei width, GLsizei height); -void ( APIENTRY * qglSelectBuffer )(GLsizei size, GLuint *buffer); -void ( APIENTRY * qglShadeModel )(GLenum mode); -void ( APIENTRY * qglStencilFunc )(GLenum func, GLint ref, GLuint mask); -void ( APIENTRY * qglStencilMask )(GLuint mask); -void ( APIENTRY * qglStencilOp )(GLenum fail, GLenum zfail, GLenum zpass); -void ( APIENTRY * qglTexCoord1d )(GLdouble s); -void ( APIENTRY * qglTexCoord1dv )(const GLdouble *v); -void ( APIENTRY * qglTexCoord1f )(GLfloat s); -void ( APIENTRY * qglTexCoord1fv )(const GLfloat *v); -void ( APIENTRY * qglTexCoord1i )(GLint s); -void ( APIENTRY * qglTexCoord1iv )(const GLint *v); -void ( APIENTRY * qglTexCoord1s )(GLshort s); -void ( APIENTRY * qglTexCoord1sv )(const GLshort *v); -void ( APIENTRY * qglTexCoord2d )(GLdouble s, GLdouble t); -void ( APIENTRY * qglTexCoord2dv )(const GLdouble *v); -void ( APIENTRY * qglTexCoord2f )(GLfloat s, GLfloat t); -void ( APIENTRY * qglTexCoord2fv )(const GLfloat *v); -void ( APIENTRY * qglTexCoord2i )(GLint s, GLint t); -void ( APIENTRY * qglTexCoord2iv )(const GLint *v); -void ( APIENTRY * qglTexCoord2s )(GLshort s, GLshort t); -void ( APIENTRY * qglTexCoord2sv )(const GLshort *v); -void ( APIENTRY * qglTexCoord3d )(GLdouble s, GLdouble t, GLdouble r); -void ( APIENTRY * qglTexCoord3dv )(const GLdouble *v); -void ( APIENTRY * qglTexCoord3f )(GLfloat s, GLfloat t, GLfloat r); -void ( APIENTRY * qglTexCoord3fv )(const GLfloat *v); -void ( APIENTRY * qglTexCoord3i )(GLint s, GLint t, GLint r); -void ( APIENTRY * qglTexCoord3iv )(const GLint *v); -void ( APIENTRY * qglTexCoord3s )(GLshort s, GLshort t, GLshort r); -void ( APIENTRY * qglTexCoord3sv )(const GLshort *v); -void ( APIENTRY * qglTexCoord4d )(GLdouble s, GLdouble t, GLdouble r, GLdouble q); -void ( APIENTRY * qglTexCoord4dv )(const GLdouble *v); -void ( APIENTRY * qglTexCoord4f )(GLfloat s, GLfloat t, GLfloat r, GLfloat q); -void ( APIENTRY * qglTexCoord4fv )(const GLfloat *v); -void ( APIENTRY * qglTexCoord4i )(GLint s, GLint t, GLint r, GLint q); -void ( APIENTRY * qglTexCoord4iv )(const GLint *v); -void ( APIENTRY * qglTexCoord4s )(GLshort s, GLshort t, GLshort r, GLshort q); -void ( APIENTRY * qglTexCoord4sv )(const GLshort *v); -void ( APIENTRY * qglTexCoordPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); -void ( APIENTRY * qglTexEnvf )(GLenum target, GLenum pname, GLfloat param); -void ( APIENTRY * qglTexEnvfv )(GLenum target, GLenum pname, const GLfloat *params); -void ( APIENTRY * qglTexEnvi )(GLenum target, GLenum pname, GLint param); -void ( APIENTRY * qglTexEnviv )(GLenum target, GLenum pname, const GLint *params); -void ( APIENTRY * qglTexGend )(GLenum coord, GLenum pname, GLdouble param); -void ( APIENTRY * qglTexGendv )(GLenum coord, GLenum pname, const GLdouble *params); -void ( APIENTRY * qglTexGenf )(GLenum coord, GLenum pname, GLfloat param); -void ( APIENTRY * qglTexGenfv )(GLenum coord, GLenum pname, const GLfloat *params); -void ( APIENTRY * qglTexGeni )(GLenum coord, GLenum pname, GLint param); -void ( APIENTRY * qglTexGeniv )(GLenum coord, GLenum pname, const GLint *params); -void ( APIENTRY * qglTexImage1D )(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels); -void ( APIENTRY * qglTexImage2D )(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); -void ( APIENTRY * qglTexParameterf )(GLenum target, GLenum pname, GLfloat param); -void ( APIENTRY * qglTexParameterfv )(GLenum target, GLenum pname, const GLfloat *params); -void ( APIENTRY * qglTexParameteri )(GLenum target, GLenum pname, GLint param); -void ( APIENTRY * qglTexParameteriv )(GLenum target, GLenum pname, const GLint *params); -void ( APIENTRY * qglTexSubImage1D )(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels); -void ( APIENTRY * qglTexSubImage2D )(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); -void ( APIENTRY * qglTranslated )(GLdouble x, GLdouble y, GLdouble z); -void ( APIENTRY * qglTranslatef )(GLfloat x, GLfloat y, GLfloat z); -void ( APIENTRY * qglVertex2d )(GLdouble x, GLdouble y); -void ( APIENTRY * qglVertex2dv )(const GLdouble *v); -void ( APIENTRY * qglVertex2f )(GLfloat x, GLfloat y); -void ( APIENTRY * qglVertex2fv )(const GLfloat *v); -void ( APIENTRY * qglVertex2i )(GLint x, GLint y); -void ( APIENTRY * qglVertex2iv )(const GLint *v); -void ( APIENTRY * qglVertex2s )(GLshort x, GLshort y); -void ( APIENTRY * qglVertex2sv )(const GLshort *v); -void ( APIENTRY * qglVertex3d )(GLdouble x, GLdouble y, GLdouble z); -void ( APIENTRY * qglVertex3dv )(const GLdouble *v); -void ( APIENTRY * qglVertex3f )(GLfloat x, GLfloat y, GLfloat z); -void ( APIENTRY * qglVertex3fv )(const GLfloat *v); -void ( APIENTRY * qglVertex3i )(GLint x, GLint y, GLint z); -void ( APIENTRY * qglVertex3iv )(const GLint *v); -void ( APIENTRY * qglVertex3s )(GLshort x, GLshort y, GLshort z); -void ( APIENTRY * qglVertex3sv )(const GLshort *v); -void ( APIENTRY * qglVertex4d )(GLdouble x, GLdouble y, GLdouble z, GLdouble w); -void ( APIENTRY * qglVertex4dv )(const GLdouble *v); -void ( APIENTRY * qglVertex4f )(GLfloat x, GLfloat y, GLfloat z, GLfloat w); -void ( APIENTRY * qglVertex4fv )(const GLfloat *v); -void ( APIENTRY * qglVertex4i )(GLint x, GLint y, GLint z, GLint w); -void ( APIENTRY * qglVertex4iv )(const GLint *v); -void ( APIENTRY * qglVertex4s )(GLshort x, GLshort y, GLshort z, GLshort w); -void ( APIENTRY * qglVertex4sv )(const GLshort *v); -void ( APIENTRY * qglVertexPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); -void ( APIENTRY * qglViewport )(GLint x, GLint y, GLsizei width, GLsizei height); - -void ( APIENTRY * qglMultiTexCoord2fARB )( GLenum texture, GLfloat s, GLfloat t ); -void ( APIENTRY * qglActiveTextureARB )( GLenum texture ); -void ( APIENTRY * qglClientActiveTextureARB )( GLenum texture ); - -void ( APIENTRY * qglLockArraysEXT)( int, int); -void ( APIENTRY * qglUnlockArraysEXT) ( void ); - -void ( APIENTRY * qglPointParameterfEXT)( GLenum param, GLfloat value ); -void ( APIENTRY * qglPointParameterfvEXT)( GLenum param, const GLfloat *value ); -void ( APIENTRY * qglColorTableEXT)( int, int, int, int, int, const void * ); -void ( APIENTRY * qgl3DfxSetPaletteEXT)( GLuint * ); -void ( APIENTRY * qglSelectTextureSGIS)( GLenum ); -void ( APIENTRY * qglMTexCoord2fSGIS)( GLenum, GLfloat, GLfloat ); - -static void ( APIENTRY * dllAccum )(GLenum op, GLfloat value); -static void ( APIENTRY * dllAlphaFunc )(GLenum func, GLclampf ref); -GLboolean ( APIENTRY * dllAreTexturesResident )(GLsizei n, const GLuint *textures, GLboolean *residences); -static void ( APIENTRY * dllArrayElement )(GLint i); -static void ( APIENTRY * dllBegin )(GLenum mode); -static void ( APIENTRY * dllBindTexture )(GLenum target, GLuint texture); -static void ( APIENTRY * dllBitmap )(GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap); -static void ( APIENTRY * dllBlendFunc )(GLenum sfactor, GLenum dfactor); -static void ( APIENTRY * dllCallList )(GLuint list); -static void ( APIENTRY * dllCallLists )(GLsizei n, GLenum type, const GLvoid *lists); -static void ( APIENTRY * dllClear )(GLbitfield mask); -static void ( APIENTRY * dllClearAccum )(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); -static void ( APIENTRY * dllClearColor )(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); -static void ( APIENTRY * dllClearDepth )(GLclampd depth); -static void ( APIENTRY * dllClearIndex )(GLfloat c); -static void ( APIENTRY * dllClearStencil )(GLint s); -static void ( APIENTRY * dllClipPlane )(GLenum plane, const GLdouble *equation); -static void ( APIENTRY * dllColor3b )(GLbyte red, GLbyte green, GLbyte blue); -static void ( APIENTRY * dllColor3bv )(const GLbyte *v); -static void ( APIENTRY * dllColor3d )(GLdouble red, GLdouble green, GLdouble blue); -static void ( APIENTRY * dllColor3dv )(const GLdouble *v); -static void ( APIENTRY * dllColor3f )(GLfloat red, GLfloat green, GLfloat blue); -static void ( APIENTRY * dllColor3fv )(const GLfloat *v); -static void ( APIENTRY * dllColor3i )(GLint red, GLint green, GLint blue); -static void ( APIENTRY * dllColor3iv )(const GLint *v); -static void ( APIENTRY * dllColor3s )(GLshort red, GLshort green, GLshort blue); -static void ( APIENTRY * dllColor3sv )(const GLshort *v); -static void ( APIENTRY * dllColor3ub )(GLubyte red, GLubyte green, GLubyte blue); -static void ( APIENTRY * dllColor3ubv )(const GLubyte *v); -static void ( APIENTRY * dllColor3ui )(GLuint red, GLuint green, GLuint blue); -static void ( APIENTRY * dllColor3uiv )(const GLuint *v); -static void ( APIENTRY * dllColor3us )(GLushort red, GLushort green, GLushort blue); -static void ( APIENTRY * dllColor3usv )(const GLushort *v); -static void ( APIENTRY * dllColor4b )(GLbyte red, GLbyte green, GLbyte blue, GLbyte alpha); -static void ( APIENTRY * dllColor4bv )(const GLbyte *v); -static void ( APIENTRY * dllColor4d )(GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha); -static void ( APIENTRY * dllColor4dv )(const GLdouble *v); -static void ( APIENTRY * dllColor4f )(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); -static void ( APIENTRY * dllColor4fv )(const GLfloat *v); -static void ( APIENTRY * dllColor4i )(GLint red, GLint green, GLint blue, GLint alpha); -static void ( APIENTRY * dllColor4iv )(const GLint *v); -static void ( APIENTRY * dllColor4s )(GLshort red, GLshort green, GLshort blue, GLshort alpha); -static void ( APIENTRY * dllColor4sv )(const GLshort *v); -static void ( APIENTRY * dllColor4ub )(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha); -static void ( APIENTRY * dllColor4ubv )(const GLubyte *v); -static void ( APIENTRY * dllColor4ui )(GLuint red, GLuint green, GLuint blue, GLuint alpha); -static void ( APIENTRY * dllColor4uiv )(const GLuint *v); -static void ( APIENTRY * dllColor4us )(GLushort red, GLushort green, GLushort blue, GLushort alpha); -static void ( APIENTRY * dllColor4usv )(const GLushort *v); -static void ( APIENTRY * dllColorMask )(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); -static void ( APIENTRY * dllColorMaterial )(GLenum face, GLenum mode); -static void ( APIENTRY * dllColorPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); -static void ( APIENTRY * dllCopyPixels )(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type); -static void ( APIENTRY * dllCopyTexImage1D )(GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLint border); -static void ( APIENTRY * dllCopyTexImage2D )(GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); -static void ( APIENTRY * dllCopyTexSubImage1D )(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); -static void ( APIENTRY * dllCopyTexSubImage2D )(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); -static void ( APIENTRY * dllCullFace )(GLenum mode); -static void ( APIENTRY * dllDeleteLists )(GLuint list, GLsizei range); -static void ( APIENTRY * dllDeleteTextures )(GLsizei n, const GLuint *textures); -static void ( APIENTRY * dllDepthFunc )(GLenum func); -static void ( APIENTRY * dllDepthMask )(GLboolean flag); -static void ( APIENTRY * dllDepthRange )(GLclampd zNear, GLclampd zFar); -static void ( APIENTRY * dllDisable )(GLenum cap); -static void ( APIENTRY * dllDisableClientState )(GLenum array); -static void ( APIENTRY * dllDrawArrays )(GLenum mode, GLint first, GLsizei count); -static void ( APIENTRY * dllDrawBuffer )(GLenum mode); -static void ( APIENTRY * dllDrawElements )(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices); -static void ( APIENTRY * dllDrawPixels )(GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); -static void ( APIENTRY * dllEdgeFlag )(GLboolean flag); -static void ( APIENTRY * dllEdgeFlagPointer )(GLsizei stride, const GLvoid *pointer); -static void ( APIENTRY * dllEdgeFlagv )(const GLboolean *flag); -static void ( APIENTRY * dllEnable )(GLenum cap); -static void ( APIENTRY * dllEnableClientState )(GLenum array); -static void ( APIENTRY * dllEnd )(void); -static void ( APIENTRY * dllEndList )(void); -static void ( APIENTRY * dllEvalCoord1d )(GLdouble u); -static void ( APIENTRY * dllEvalCoord1dv )(const GLdouble *u); -static void ( APIENTRY * dllEvalCoord1f )(GLfloat u); -static void ( APIENTRY * dllEvalCoord1fv )(const GLfloat *u); -static void ( APIENTRY * dllEvalCoord2d )(GLdouble u, GLdouble v); -static void ( APIENTRY * dllEvalCoord2dv )(const GLdouble *u); -static void ( APIENTRY * dllEvalCoord2f )(GLfloat u, GLfloat v); -static void ( APIENTRY * dllEvalCoord2fv )(const GLfloat *u); -static void ( APIENTRY * dllEvalMesh1 )(GLenum mode, GLint i1, GLint i2); -static void ( APIENTRY * dllEvalMesh2 )(GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2); -static void ( APIENTRY * dllEvalPoint1 )(GLint i); -static void ( APIENTRY * dllEvalPoint2 )(GLint i, GLint j); -static void ( APIENTRY * dllFeedbackBuffer )(GLsizei size, GLenum type, GLfloat *buffer); -static void ( APIENTRY * dllFinish )(void); -static void ( APIENTRY * dllFlush )(void); -static void ( APIENTRY * dllFogf )(GLenum pname, GLfloat param); -static void ( APIENTRY * dllFogfv )(GLenum pname, const GLfloat *params); -static void ( APIENTRY * dllFogi )(GLenum pname, GLint param); -static void ( APIENTRY * dllFogiv )(GLenum pname, const GLint *params); -static void ( APIENTRY * dllFrontFace )(GLenum mode); -static void ( APIENTRY * dllFrustum )(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); -GLuint ( APIENTRY * dllGenLists )(GLsizei range); -static void ( APIENTRY * dllGenTextures )(GLsizei n, GLuint *textures); -static void ( APIENTRY * dllGetBooleanv )(GLenum pname, GLboolean *params); -static void ( APIENTRY * dllGetClipPlane )(GLenum plane, GLdouble *equation); -static void ( APIENTRY * dllGetDoublev )(GLenum pname, GLdouble *params); -GLenum ( APIENTRY * dllGetError )(void); -static void ( APIENTRY * dllGetFloatv )(GLenum pname, GLfloat *params); -static void ( APIENTRY * dllGetIntegerv )(GLenum pname, GLint *params); -static void ( APIENTRY * dllGetLightfv )(GLenum light, GLenum pname, GLfloat *params); -static void ( APIENTRY * dllGetLightiv )(GLenum light, GLenum pname, GLint *params); -static void ( APIENTRY * dllGetMapdv )(GLenum target, GLenum query, GLdouble *v); -static void ( APIENTRY * dllGetMapfv )(GLenum target, GLenum query, GLfloat *v); -static void ( APIENTRY * dllGetMapiv )(GLenum target, GLenum query, GLint *v); -static void ( APIENTRY * dllGetMaterialfv )(GLenum face, GLenum pname, GLfloat *params); -static void ( APIENTRY * dllGetMaterialiv )(GLenum face, GLenum pname, GLint *params); -static void ( APIENTRY * dllGetPixelMapfv )(GLenum map, GLfloat *values); -static void ( APIENTRY * dllGetPixelMapuiv )(GLenum map, GLuint *values); -static void ( APIENTRY * dllGetPixelMapusv )(GLenum map, GLushort *values); -static void ( APIENTRY * dllGetPointerv )(GLenum pname, GLvoid* *params); -static void ( APIENTRY * dllGetPolygonStipple )(GLubyte *mask); -const GLubyte * ( APIENTRY * dllGetString )(GLenum name); -static void ( APIENTRY * dllGetTexEnvfv )(GLenum target, GLenum pname, GLfloat *params); -static void ( APIENTRY * dllGetTexEnviv )(GLenum target, GLenum pname, GLint *params); -static void ( APIENTRY * dllGetTexGendv )(GLenum coord, GLenum pname, GLdouble *params); -static void ( APIENTRY * dllGetTexGenfv )(GLenum coord, GLenum pname, GLfloat *params); -static void ( APIENTRY * dllGetTexGeniv )(GLenum coord, GLenum pname, GLint *params); -static void ( APIENTRY * dllGetTexImage )(GLenum target, GLint level, GLenum format, GLenum type, GLvoid *pixels); -static void ( APIENTRY * dllGetTexLevelParameterfv )(GLenum target, GLint level, GLenum pname, GLfloat *params); -static void ( APIENTRY * dllGetTexLevelParameteriv )(GLenum target, GLint level, GLenum pname, GLint *params); -static void ( APIENTRY * dllGetTexParameterfv )(GLenum target, GLenum pname, GLfloat *params); -static void ( APIENTRY * dllGetTexParameteriv )(GLenum target, GLenum pname, GLint *params); -static void ( APIENTRY * dllHint )(GLenum target, GLenum mode); -static void ( APIENTRY * dllIndexMask )(GLuint mask); -static void ( APIENTRY * dllIndexPointer )(GLenum type, GLsizei stride, const GLvoid *pointer); -static void ( APIENTRY * dllIndexd )(GLdouble c); -static void ( APIENTRY * dllIndexdv )(const GLdouble *c); -static void ( APIENTRY * dllIndexf )(GLfloat c); -static void ( APIENTRY * dllIndexfv )(const GLfloat *c); -static void ( APIENTRY * dllIndexi )(GLint c); -static void ( APIENTRY * dllIndexiv )(const GLint *c); -static void ( APIENTRY * dllIndexs )(GLshort c); -static void ( APIENTRY * dllIndexsv )(const GLshort *c); -static void ( APIENTRY * dllIndexub )(GLubyte c); -static void ( APIENTRY * dllIndexubv )(const GLubyte *c); -static void ( APIENTRY * dllInitNames )(void); -static void ( APIENTRY * dllInterleavedArrays )(GLenum format, GLsizei stride, const GLvoid *pointer); -GLboolean ( APIENTRY * dllIsEnabled )(GLenum cap); -GLboolean ( APIENTRY * dllIsList )(GLuint list); -GLboolean ( APIENTRY * dllIsTexture )(GLuint texture); -static void ( APIENTRY * dllLightModelf )(GLenum pname, GLfloat param); -static void ( APIENTRY * dllLightModelfv )(GLenum pname, const GLfloat *params); -static void ( APIENTRY * dllLightModeli )(GLenum pname, GLint param); -static void ( APIENTRY * dllLightModeliv )(GLenum pname, const GLint *params); -static void ( APIENTRY * dllLightf )(GLenum light, GLenum pname, GLfloat param); -static void ( APIENTRY * dllLightfv )(GLenum light, GLenum pname, const GLfloat *params); -static void ( APIENTRY * dllLighti )(GLenum light, GLenum pname, GLint param); -static void ( APIENTRY * dllLightiv )(GLenum light, GLenum pname, const GLint *params); -static void ( APIENTRY * dllLineStipple )(GLint factor, GLushort pattern); -static void ( APIENTRY * dllLineWidth )(GLfloat width); -static void ( APIENTRY * dllListBase )(GLuint base); -static void ( APIENTRY * dllLoadIdentity )(void); -static void ( APIENTRY * dllLoadMatrixd )(const GLdouble *m); -static void ( APIENTRY * dllLoadMatrixf )(const GLfloat *m); -static void ( APIENTRY * dllLoadName )(GLuint name); -static void ( APIENTRY * dllLogicOp )(GLenum opcode); -static void ( APIENTRY * dllMap1d )(GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points); -static void ( APIENTRY * dllMap1f )(GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points); -static void ( APIENTRY * dllMap2d )(GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points); -static void ( APIENTRY * dllMap2f )(GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points); -static void ( APIENTRY * dllMapGrid1d )(GLint un, GLdouble u1, GLdouble u2); -static void ( APIENTRY * dllMapGrid1f )(GLint un, GLfloat u1, GLfloat u2); -static void ( APIENTRY * dllMapGrid2d )(GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2); -static void ( APIENTRY * dllMapGrid2f )(GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2); -static void ( APIENTRY * dllMaterialf )(GLenum face, GLenum pname, GLfloat param); -static void ( APIENTRY * dllMaterialfv )(GLenum face, GLenum pname, const GLfloat *params); -static void ( APIENTRY * dllMateriali )(GLenum face, GLenum pname, GLint param); -static void ( APIENTRY * dllMaterialiv )(GLenum face, GLenum pname, const GLint *params); -static void ( APIENTRY * dllMatrixMode )(GLenum mode); -static void ( APIENTRY * dllMultMatrixd )(const GLdouble *m); -static void ( APIENTRY * dllMultMatrixf )(const GLfloat *m); -static void ( APIENTRY * dllNewList )(GLuint list, GLenum mode); -static void ( APIENTRY * dllNormal3b )(GLbyte nx, GLbyte ny, GLbyte nz); -static void ( APIENTRY * dllNormal3bv )(const GLbyte *v); -static void ( APIENTRY * dllNormal3d )(GLdouble nx, GLdouble ny, GLdouble nz); -static void ( APIENTRY * dllNormal3dv )(const GLdouble *v); -static void ( APIENTRY * dllNormal3f )(GLfloat nx, GLfloat ny, GLfloat nz); -static void ( APIENTRY * dllNormal3fv )(const GLfloat *v); -static void ( APIENTRY * dllNormal3i )(GLint nx, GLint ny, GLint nz); -static void ( APIENTRY * dllNormal3iv )(const GLint *v); -static void ( APIENTRY * dllNormal3s )(GLshort nx, GLshort ny, GLshort nz); -static void ( APIENTRY * dllNormal3sv )(const GLshort *v); -static void ( APIENTRY * dllNormalPointer )(GLenum type, GLsizei stride, const GLvoid *pointer); -static void ( APIENTRY * dllOrtho )(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); -static void ( APIENTRY * dllPassThrough )(GLfloat token); -static void ( APIENTRY * dllPixelMapfv )(GLenum map, GLsizei mapsize, const GLfloat *values); -static void ( APIENTRY * dllPixelMapuiv )(GLenum map, GLsizei mapsize, const GLuint *values); -static void ( APIENTRY * dllPixelMapusv )(GLenum map, GLsizei mapsize, const GLushort *values); -static void ( APIENTRY * dllPixelStoref )(GLenum pname, GLfloat param); -static void ( APIENTRY * dllPixelStorei )(GLenum pname, GLint param); -static void ( APIENTRY * dllPixelTransferf )(GLenum pname, GLfloat param); -static void ( APIENTRY * dllPixelTransferi )(GLenum pname, GLint param); -static void ( APIENTRY * dllPixelZoom )(GLfloat xfactor, GLfloat yfactor); -static void ( APIENTRY * dllPointSize )(GLfloat size); -static void ( APIENTRY * dllPolygonMode )(GLenum face, GLenum mode); -static void ( APIENTRY * dllPolygonOffset )(GLfloat factor, GLfloat units); -static void ( APIENTRY * dllPolygonStipple )(const GLubyte *mask); -static void ( APIENTRY * dllPopAttrib )(void); -static void ( APIENTRY * dllPopClientAttrib )(void); -static void ( APIENTRY * dllPopMatrix )(void); -static void ( APIENTRY * dllPopName )(void); -static void ( APIENTRY * dllPrioritizeTextures )(GLsizei n, const GLuint *textures, const GLclampf *priorities); -static void ( APIENTRY * dllPushAttrib )(GLbitfield mask); -static void ( APIENTRY * dllPushClientAttrib )(GLbitfield mask); -static void ( APIENTRY * dllPushMatrix )(void); -static void ( APIENTRY * dllPushName )(GLuint name); -static void ( APIENTRY * dllRasterPos2d )(GLdouble x, GLdouble y); -static void ( APIENTRY * dllRasterPos2dv )(const GLdouble *v); -static void ( APIENTRY * dllRasterPos2f )(GLfloat x, GLfloat y); -static void ( APIENTRY * dllRasterPos2fv )(const GLfloat *v); -static void ( APIENTRY * dllRasterPos2i )(GLint x, GLint y); -static void ( APIENTRY * dllRasterPos2iv )(const GLint *v); -static void ( APIENTRY * dllRasterPos2s )(GLshort x, GLshort y); -static void ( APIENTRY * dllRasterPos2sv )(const GLshort *v); -static void ( APIENTRY * dllRasterPos3d )(GLdouble x, GLdouble y, GLdouble z); -static void ( APIENTRY * dllRasterPos3dv )(const GLdouble *v); -static void ( APIENTRY * dllRasterPos3f )(GLfloat x, GLfloat y, GLfloat z); -static void ( APIENTRY * dllRasterPos3fv )(const GLfloat *v); -static void ( APIENTRY * dllRasterPos3i )(GLint x, GLint y, GLint z); -static void ( APIENTRY * dllRasterPos3iv )(const GLint *v); -static void ( APIENTRY * dllRasterPos3s )(GLshort x, GLshort y, GLshort z); -static void ( APIENTRY * dllRasterPos3sv )(const GLshort *v); -static void ( APIENTRY * dllRasterPos4d )(GLdouble x, GLdouble y, GLdouble z, GLdouble w); -static void ( APIENTRY * dllRasterPos4dv )(const GLdouble *v); -static void ( APIENTRY * dllRasterPos4f )(GLfloat x, GLfloat y, GLfloat z, GLfloat w); -static void ( APIENTRY * dllRasterPos4fv )(const GLfloat *v); -static void ( APIENTRY * dllRasterPos4i )(GLint x, GLint y, GLint z, GLint w); -static void ( APIENTRY * dllRasterPos4iv )(const GLint *v); -static void ( APIENTRY * dllRasterPos4s )(GLshort x, GLshort y, GLshort z, GLshort w); -static void ( APIENTRY * dllRasterPos4sv )(const GLshort *v); -static void ( APIENTRY * dllReadBuffer )(GLenum mode); -static void ( APIENTRY * dllReadPixels )(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels); -static void ( APIENTRY * dllRectd )(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2); -static void ( APIENTRY * dllRectdv )(const GLdouble *v1, const GLdouble *v2); -static void ( APIENTRY * dllRectf )(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2); -static void ( APIENTRY * dllRectfv )(const GLfloat *v1, const GLfloat *v2); -static void ( APIENTRY * dllRecti )(GLint x1, GLint y1, GLint x2, GLint y2); -static void ( APIENTRY * dllRectiv )(const GLint *v1, const GLint *v2); -static void ( APIENTRY * dllRects )(GLshort x1, GLshort y1, GLshort x2, GLshort y2); -static void ( APIENTRY * dllRectsv )(const GLshort *v1, const GLshort *v2); -GLint ( APIENTRY * dllRenderMode )(GLenum mode); -static void ( APIENTRY * dllRotated )(GLdouble angle, GLdouble x, GLdouble y, GLdouble z); -static void ( APIENTRY * dllRotatef )(GLfloat angle, GLfloat x, GLfloat y, GLfloat z); -static void ( APIENTRY * dllScaled )(GLdouble x, GLdouble y, GLdouble z); -static void ( APIENTRY * dllScalef )(GLfloat x, GLfloat y, GLfloat z); -static void ( APIENTRY * dllScissor )(GLint x, GLint y, GLsizei width, GLsizei height); -static void ( APIENTRY * dllSelectBuffer )(GLsizei size, GLuint *buffer); -static void ( APIENTRY * dllShadeModel )(GLenum mode); -static void ( APIENTRY * dllStencilFunc )(GLenum func, GLint ref, GLuint mask); -static void ( APIENTRY * dllStencilMask )(GLuint mask); -static void ( APIENTRY * dllStencilOp )(GLenum fail, GLenum zfail, GLenum zpass); -static void ( APIENTRY * dllTexCoord1d )(GLdouble s); -static void ( APIENTRY * dllTexCoord1dv )(const GLdouble *v); -static void ( APIENTRY * dllTexCoord1f )(GLfloat s); -static void ( APIENTRY * dllTexCoord1fv )(const GLfloat *v); -static void ( APIENTRY * dllTexCoord1i )(GLint s); -static void ( APIENTRY * dllTexCoord1iv )(const GLint *v); -static void ( APIENTRY * dllTexCoord1s )(GLshort s); -static void ( APIENTRY * dllTexCoord1sv )(const GLshort *v); -static void ( APIENTRY * dllTexCoord2d )(GLdouble s, GLdouble t); -static void ( APIENTRY * dllTexCoord2dv )(const GLdouble *v); -static void ( APIENTRY * dllTexCoord2f )(GLfloat s, GLfloat t); -static void ( APIENTRY * dllTexCoord2fv )(const GLfloat *v); -static void ( APIENTRY * dllTexCoord2i )(GLint s, GLint t); -static void ( APIENTRY * dllTexCoord2iv )(const GLint *v); -static void ( APIENTRY * dllTexCoord2s )(GLshort s, GLshort t); -static void ( APIENTRY * dllTexCoord2sv )(const GLshort *v); -static void ( APIENTRY * dllTexCoord3d )(GLdouble s, GLdouble t, GLdouble r); -static void ( APIENTRY * dllTexCoord3dv )(const GLdouble *v); -static void ( APIENTRY * dllTexCoord3f )(GLfloat s, GLfloat t, GLfloat r); -static void ( APIENTRY * dllTexCoord3fv )(const GLfloat *v); -static void ( APIENTRY * dllTexCoord3i )(GLint s, GLint t, GLint r); -static void ( APIENTRY * dllTexCoord3iv )(const GLint *v); -static void ( APIENTRY * dllTexCoord3s )(GLshort s, GLshort t, GLshort r); -static void ( APIENTRY * dllTexCoord3sv )(const GLshort *v); -static void ( APIENTRY * dllTexCoord4d )(GLdouble s, GLdouble t, GLdouble r, GLdouble q); -static void ( APIENTRY * dllTexCoord4dv )(const GLdouble *v); -static void ( APIENTRY * dllTexCoord4f )(GLfloat s, GLfloat t, GLfloat r, GLfloat q); -static void ( APIENTRY * dllTexCoord4fv )(const GLfloat *v); -static void ( APIENTRY * dllTexCoord4i )(GLint s, GLint t, GLint r, GLint q); -static void ( APIENTRY * dllTexCoord4iv )(const GLint *v); -static void ( APIENTRY * dllTexCoord4s )(GLshort s, GLshort t, GLshort r, GLshort q); -static void ( APIENTRY * dllTexCoord4sv )(const GLshort *v); -static void ( APIENTRY * dllTexCoordPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); -static void ( APIENTRY * dllTexEnvf )(GLenum target, GLenum pname, GLfloat param); -static void ( APIENTRY * dllTexEnvfv )(GLenum target, GLenum pname, const GLfloat *params); -static void ( APIENTRY * dllTexEnvi )(GLenum target, GLenum pname, GLint param); -static void ( APIENTRY * dllTexEnviv )(GLenum target, GLenum pname, const GLint *params); -static void ( APIENTRY * dllTexGend )(GLenum coord, GLenum pname, GLdouble param); -static void ( APIENTRY * dllTexGendv )(GLenum coord, GLenum pname, const GLdouble *params); -static void ( APIENTRY * dllTexGenf )(GLenum coord, GLenum pname, GLfloat param); -static void ( APIENTRY * dllTexGenfv )(GLenum coord, GLenum pname, const GLfloat *params); -static void ( APIENTRY * dllTexGeni )(GLenum coord, GLenum pname, GLint param); -static void ( APIENTRY * dllTexGeniv )(GLenum coord, GLenum pname, const GLint *params); -static void ( APIENTRY * dllTexImage1D )(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels); -static void ( APIENTRY * dllTexImage2D )(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); -static void ( APIENTRY * dllTexParameterf )(GLenum target, GLenum pname, GLfloat param); -static void ( APIENTRY * dllTexParameterfv )(GLenum target, GLenum pname, const GLfloat *params); -static void ( APIENTRY * dllTexParameteri )(GLenum target, GLenum pname, GLint param); -static void ( APIENTRY * dllTexParameteriv )(GLenum target, GLenum pname, const GLint *params); -static void ( APIENTRY * dllTexSubImage1D )(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels); -static void ( APIENTRY * dllTexSubImage2D )(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); -static void ( APIENTRY * dllTranslated )(GLdouble x, GLdouble y, GLdouble z); -static void ( APIENTRY * dllTranslatef )(GLfloat x, GLfloat y, GLfloat z); -static void ( APIENTRY * dllVertex2d )(GLdouble x, GLdouble y); -static void ( APIENTRY * dllVertex2dv )(const GLdouble *v); -static void ( APIENTRY * dllVertex2f )(GLfloat x, GLfloat y); -static void ( APIENTRY * dllVertex2fv )(const GLfloat *v); -static void ( APIENTRY * dllVertex2i )(GLint x, GLint y); -static void ( APIENTRY * dllVertex2iv )(const GLint *v); -static void ( APIENTRY * dllVertex2s )(GLshort x, GLshort y); -static void ( APIENTRY * dllVertex2sv )(const GLshort *v); -static void ( APIENTRY * dllVertex3d )(GLdouble x, GLdouble y, GLdouble z); -static void ( APIENTRY * dllVertex3dv )(const GLdouble *v); -static void ( APIENTRY * dllVertex3f )(GLfloat x, GLfloat y, GLfloat z); -static void ( APIENTRY * dllVertex3fv )(const GLfloat *v); -static void ( APIENTRY * dllVertex3i )(GLint x, GLint y, GLint z); -static void ( APIENTRY * dllVertex3iv )(const GLint *v); -static void ( APIENTRY * dllVertex3s )(GLshort x, GLshort y, GLshort z); -static void ( APIENTRY * dllVertex3sv )(const GLshort *v); -static void ( APIENTRY * dllVertex4d )(GLdouble x, GLdouble y, GLdouble z, GLdouble w); -static void ( APIENTRY * dllVertex4dv )(const GLdouble *v); -static void ( APIENTRY * dllVertex4f )(GLfloat x, GLfloat y, GLfloat z, GLfloat w); -static void ( APIENTRY * dllVertex4fv )(const GLfloat *v); -static void ( APIENTRY * dllVertex4i )(GLint x, GLint y, GLint z, GLint w); -static void ( APIENTRY * dllVertex4iv )(const GLint *v); -static void ( APIENTRY * dllVertex4s )(GLshort x, GLshort y, GLshort z, GLshort w); -static void ( APIENTRY * dllVertex4sv )(const GLshort *v); -static void ( APIENTRY * dllVertexPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); -static void ( APIENTRY * dllViewport )(GLint x, GLint y, GLsizei width, GLsizei height); - -static void APIENTRY logAccum(GLenum op, GLfloat value) -{ - fprintf( glw_state.log_fp, "glAccum\n" ); - dllAccum( op, value ); -} - -static void APIENTRY logAlphaFunc(GLenum func, GLclampf ref) -{ - fprintf( glw_state.log_fp, "glAlphaFunc( 0x%x, %f )\n", func, ref ); - dllAlphaFunc( func, ref ); -} - -static GLboolean APIENTRY logAreTexturesResident(GLsizei n, const GLuint *textures, GLboolean *residences) -{ - fprintf( glw_state.log_fp, "glAreTexturesResident\n" ); - return dllAreTexturesResident( n, textures, residences ); -} - -static void APIENTRY logArrayElement(GLint i) -{ - fprintf( glw_state.log_fp, "glArrayElement\n" ); - dllArrayElement( i ); -} - -static void APIENTRY logBegin(GLenum mode) -{ - fprintf( glw_state.log_fp, "glBegin( 0x%x )\n", mode ); - dllBegin( mode ); -} - -static void APIENTRY logBindTexture(GLenum target, GLuint texture) -{ - fprintf( glw_state.log_fp, "glBindTexture( 0x%x, %u )\n", target, texture ); - dllBindTexture( target, texture ); -} - -static void APIENTRY logBitmap(GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap) -{ - fprintf( glw_state.log_fp, "glBitmap\n" ); - dllBitmap( width, height, xorig, yorig, xmove, ymove, bitmap ); -} - -static void APIENTRY logBlendFunc(GLenum sfactor, GLenum dfactor) -{ - fprintf( glw_state.log_fp, "glBlendFunc( 0x%x, 0x%x )\n", sfactor, dfactor ); - dllBlendFunc( sfactor, dfactor ); -} - -static void APIENTRY logCallList(GLuint list) -{ - fprintf( glw_state.log_fp, "glCallList( %u )\n", list ); - dllCallList( list ); -} - -static void APIENTRY logCallLists(GLsizei n, GLenum type, const void *lists) -{ - fprintf( glw_state.log_fp, "glCallLists\n" ); - dllCallLists( n, type, lists ); -} - -static void APIENTRY logClear(GLbitfield mask) -{ - fprintf( glw_state.log_fp, "glClear\n" ); - dllClear( mask ); -} - -static void APIENTRY logClearAccum(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) -{ - fprintf( glw_state.log_fp, "glClearAccum\n" ); - dllClearAccum( red, green, blue, alpha ); -} - -static void APIENTRY logClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) -{ - fprintf( glw_state.log_fp, "glClearColor\n" ); - dllClearColor( red, green, blue, alpha ); -} - -static void APIENTRY logClearDepth(GLclampd depth) -{ - fprintf( glw_state.log_fp, "glClearDepth\n" ); - dllClearDepth( depth ); -} - -static void APIENTRY logClearIndex(GLfloat c) -{ - fprintf( glw_state.log_fp, "glClearIndex\n" ); - dllClearIndex( c ); -} - -static void APIENTRY logClearStencil(GLint s) -{ - fprintf( glw_state.log_fp, "glClearStencil\n" ); - dllClearStencil( s ); -} - -static void APIENTRY logClipPlane(GLenum plane, const GLdouble *equation) -{ - fprintf( glw_state.log_fp, "glClipPlane\n" ); - dllClipPlane( plane, equation ); -} - -static void APIENTRY logColor3b(GLbyte red, GLbyte green, GLbyte blue) -{ - fprintf( glw_state.log_fp, "glColor3b\n" ); - dllColor3b( red, green, blue ); -} - -static void APIENTRY logColor3bv(const GLbyte *v) -{ - fprintf( glw_state.log_fp, "glColor3bv\n" ); - dllColor3bv( v ); -} - -static void APIENTRY logColor3d(GLdouble red, GLdouble green, GLdouble blue) -{ - fprintf( glw_state.log_fp, "glColor3d\n" ); - dllColor3d( red, green, blue ); -} - -static void APIENTRY logColor3dv(const GLdouble *v) -{ - fprintf( glw_state.log_fp, "glColor3dv\n" ); - dllColor3dv( v ); -} - -static void APIENTRY logColor3f(GLfloat red, GLfloat green, GLfloat blue) -{ - fprintf( glw_state.log_fp, "glColor3f\n" ); - dllColor3f( red, green, blue ); -} - -static void APIENTRY logColor3fv(const GLfloat *v) -{ - fprintf( glw_state.log_fp, "glColor3fv\n" ); - dllColor3fv( v ); -} - -static void APIENTRY logColor3i(GLint red, GLint green, GLint blue) -{ - fprintf( glw_state.log_fp, "glColor3i\n" ); - dllColor3i( red, green, blue ); -} - -static void APIENTRY logColor3iv(const GLint *v) -{ - fprintf( glw_state.log_fp, "glColor3iv\n" ); - dllColor3iv( v ); -} - -static void APIENTRY logColor3s(GLshort red, GLshort green, GLshort blue) -{ - fprintf( glw_state.log_fp, "glColor3s\n" ); - dllColor3s( red, green, blue ); -} - -static void APIENTRY logColor3sv(const GLshort *v) -{ - fprintf( glw_state.log_fp, "glColor3sv\n" ); - dllColor3sv( v ); -} - -static void APIENTRY logColor3ub(GLubyte red, GLubyte green, GLubyte blue) -{ - fprintf( glw_state.log_fp, "glColor3ub\n" ); - dllColor3ub( red, green, blue ); -} - -static void APIENTRY logColor3ubv(const GLubyte *v) -{ - fprintf( glw_state.log_fp, "glColor3ubv\n" ); - dllColor3ubv( v ); -} - -#define SIG( x ) fprintf( glw_state.log_fp, x "\n" ) - -static void APIENTRY logColor3ui(GLuint red, GLuint green, GLuint blue) -{ - SIG( "glColor3ui" ); - dllColor3ui( red, green, blue ); -} - -static void APIENTRY logColor3uiv(const GLuint *v) -{ - SIG( "glColor3uiv" ); - dllColor3uiv( v ); -} - -static void APIENTRY logColor3us(GLushort red, GLushort green, GLushort blue) -{ - SIG( "glColor3us" ); - dllColor3us( red, green, blue ); -} - -static void APIENTRY logColor3usv(const GLushort *v) -{ - SIG( "glColor3usv" ); - dllColor3usv( v ); -} - -static void APIENTRY logColor4b(GLbyte red, GLbyte green, GLbyte blue, GLbyte alpha) -{ - SIG( "glColor4b" ); - dllColor4b( red, green, blue, alpha ); -} - -static void APIENTRY logColor4bv(const GLbyte *v) -{ - SIG( "glColor4bv" ); - dllColor4bv( v ); -} - -static void APIENTRY logColor4d(GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha) -{ - SIG( "glColor4d" ); - dllColor4d( red, green, blue, alpha ); -} -static void APIENTRY logColor4dv(const GLdouble *v) -{ - SIG( "glColor4dv" ); - dllColor4dv( v ); -} -static void APIENTRY logColor4f(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) -{ - fprintf( glw_state.log_fp, "glColor4f( %f,%f,%f,%f )\n", red, green, blue, alpha ); - dllColor4f( red, green, blue, alpha ); -} -static void APIENTRY logColor4fv(const GLfloat *v) -{ - fprintf( glw_state.log_fp, "glColor4fv( %f,%f,%f,%f )\n", v[0], v[1], v[2], v[3] ); - dllColor4fv( v ); -} -static void APIENTRY logColor4i(GLint red, GLint green, GLint blue, GLint alpha) -{ - SIG( "glColor4i" ); - dllColor4i( red, green, blue, alpha ); -} -static void APIENTRY logColor4iv(const GLint *v) -{ - SIG( "glColor4iv" ); - dllColor4iv( v ); -} -static void APIENTRY logColor4s(GLshort red, GLshort green, GLshort blue, GLshort alpha) -{ - SIG( "glColor4s" ); - dllColor4s( red, green, blue, alpha ); -} -static void APIENTRY logColor4sv(const GLshort *v) -{ - SIG( "glColor4sv" ); - dllColor4sv( v ); -} -static void APIENTRY logColor4ub(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha) -{ - SIG( "glColor4b" ); - dllColor4b( red, green, blue, alpha ); -} -static void APIENTRY logColor4ubv(const GLubyte *v) -{ - SIG( "glColor4ubv" ); - dllColor4ubv( v ); -} -static void APIENTRY logColor4ui(GLuint red, GLuint green, GLuint blue, GLuint alpha) -{ - SIG( "glColor4ui" ); - dllColor4ui( red, green, blue, alpha ); -} -static void APIENTRY logColor4uiv(const GLuint *v) -{ - SIG( "glColor4uiv" ); - dllColor4uiv( v ); -} -static void APIENTRY logColor4us(GLushort red, GLushort green, GLushort blue, GLushort alpha) -{ - SIG( "glColor4us" ); - dllColor4us( red, green, blue, alpha ); -} -static void APIENTRY logColor4usv(const GLushort *v) -{ - SIG( "glColor4usv" ); - dllColor4usv( v ); -} -static void APIENTRY logColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) -{ - SIG( "glColorMask" ); - dllColorMask( red, green, blue, alpha ); -} -static void APIENTRY logColorMaterial(GLenum face, GLenum mode) -{ - SIG( "glColorMaterial" ); - dllColorMaterial( face, mode ); -} - -static void APIENTRY logColorPointer(GLint size, GLenum type, GLsizei stride, const void *pointer) -{ - SIG( "glColorPointer" ); - dllColorPointer( size, type, stride, pointer ); -} - -static void APIENTRY logCopyPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type) -{ - SIG( "glCopyPixels" ); - dllCopyPixels( x, y, width, height, type ); -} - -static void APIENTRY logCopyTexImage1D(GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLint border) -{ - SIG( "glCopyTexImage1D" ); - dllCopyTexImage1D( target, level, internalFormat, x, y, width, border ); -} - -static void APIENTRY logCopyTexImage2D(GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) -{ - SIG( "glCopyTexImage2D" ); - dllCopyTexImage2D( target, level, internalFormat, x, y, width, height, border ); -} - -static void APIENTRY logCopyTexSubImage1D(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width) -{ - SIG( "glCopyTexSubImage1D" ); - dllCopyTexSubImage1D( target, level, xoffset, x, y, width ); -} - -static void APIENTRY logCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) -{ - SIG( "glCopyTexSubImage2D" ); - dllCopyTexSubImage2D( target, level, xoffset, yoffset, x, y, width, height ); -} - -static void APIENTRY logCullFace(GLenum mode) -{ - SIG( "glCullFace" ); - dllCullFace( mode ); -} - -static void APIENTRY logDeleteLists(GLuint list, GLsizei range) -{ - SIG( "glDeleteLists" ); - dllDeleteLists( list, range ); -} - -static void APIENTRY logDeleteTextures(GLsizei n, const GLuint *textures) -{ - SIG( "glDeleteTextures" ); - dllDeleteTextures( n, textures ); -} - -static void APIENTRY logDepthFunc(GLenum func) -{ - SIG( "glDepthFunc" ); - dllDepthFunc( func ); -} - -static void APIENTRY logDepthMask(GLboolean flag) -{ - SIG( "glDepthMask" ); - dllDepthMask( flag ); -} - -static void APIENTRY logDepthRange(GLclampd zNear, GLclampd zFar) -{ - SIG( "glDepthRange" ); - dllDepthRange( zNear, zFar ); -} - -static void APIENTRY logDisable(GLenum cap) -{ - fprintf( glw_state.log_fp, "glDisable( 0x%x )\n", cap ); - dllDisable( cap ); -} - -static void APIENTRY logDisableClientState(GLenum array) -{ - SIG( "glDisableClientState" ); - dllDisableClientState( array ); -} - -static void APIENTRY logDrawArrays(GLenum mode, GLint first, GLsizei count) -{ - SIG( "glDrawArrays" ); - dllDrawArrays( mode, first, count ); -} - -static void APIENTRY logDrawBuffer(GLenum mode) -{ - SIG( "glDrawBuffer" ); - dllDrawBuffer( mode ); -} - -static void APIENTRY logDrawElements(GLenum mode, GLsizei count, GLenum type, const void *indices) -{ - SIG( "glDrawElements" ); - dllDrawElements( mode, count, type, indices ); -} - -static void APIENTRY logDrawPixels(GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) -{ - SIG( "glDrawPixels" ); - dllDrawPixels( width, height, format, type, pixels ); -} - -static void APIENTRY logEdgeFlag(GLboolean flag) -{ - SIG( "glEdgeFlag" ); - dllEdgeFlag( flag ); -} - -static void APIENTRY logEdgeFlagPointer(GLsizei stride, const void *pointer) -{ - SIG( "glEdgeFlagPointer" ); - dllEdgeFlagPointer( stride, pointer ); -} - -static void APIENTRY logEdgeFlagv(const GLboolean *flag) -{ - SIG( "glEdgeFlagv" ); - dllEdgeFlagv( flag ); -} - -static void APIENTRY logEnable(GLenum cap) -{ - fprintf( glw_state.log_fp, "glEnable( 0x%x )\n", cap ); - dllEnable( cap ); -} - -static void APIENTRY logEnableClientState(GLenum array) -{ - SIG( "glEnableClientState" ); - dllEnableClientState( array ); -} - -static void APIENTRY logEnd(void) -{ - SIG( "glEnd" ); - dllEnd(); -} - -static void APIENTRY logEndList(void) -{ - SIG( "glEndList" ); - dllEndList(); -} - -static void APIENTRY logEvalCoord1d(GLdouble u) -{ - SIG( "glEvalCoord1d" ); - dllEvalCoord1d( u ); -} - -static void APIENTRY logEvalCoord1dv(const GLdouble *u) -{ - SIG( "glEvalCoord1dv" ); - dllEvalCoord1dv( u ); -} - -static void APIENTRY logEvalCoord1f(GLfloat u) -{ - SIG( "glEvalCoord1f" ); - dllEvalCoord1f( u ); -} - -static void APIENTRY logEvalCoord1fv(const GLfloat *u) -{ - SIG( "glEvalCoord1fv" ); - dllEvalCoord1fv( u ); -} -static void APIENTRY logEvalCoord2d(GLdouble u, GLdouble v) -{ - SIG( "glEvalCoord2d" ); - dllEvalCoord2d( u, v ); -} -static void APIENTRY logEvalCoord2dv(const GLdouble *u) -{ - SIG( "glEvalCoord2dv" ); - dllEvalCoord2dv( u ); -} -static void APIENTRY logEvalCoord2f(GLfloat u, GLfloat v) -{ - SIG( "glEvalCoord2f" ); - dllEvalCoord2f( u, v ); -} -static void APIENTRY logEvalCoord2fv(const GLfloat *u) -{ - SIG( "glEvalCoord2fv" ); - dllEvalCoord2fv( u ); -} - -static void APIENTRY logEvalMesh1(GLenum mode, GLint i1, GLint i2) -{ - SIG( "glEvalMesh1" ); - dllEvalMesh1( mode, i1, i2 ); -} -static void APIENTRY logEvalMesh2(GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2) -{ - SIG( "glEvalMesh2" ); - dllEvalMesh2( mode, i1, i2, j1, j2 ); -} -static void APIENTRY logEvalPoint1(GLint i) -{ - SIG( "glEvalPoint1" ); - dllEvalPoint1( i ); -} -static void APIENTRY logEvalPoint2(GLint i, GLint j) -{ - SIG( "glEvalPoint2" ); - dllEvalPoint2( i, j ); -} - -static void APIENTRY logFeedbackBuffer(GLsizei size, GLenum type, GLfloat *buffer) -{ - SIG( "glFeedbackBuffer" ); - dllFeedbackBuffer( size, type, buffer ); -} - -static void APIENTRY logFinish(void) -{ - SIG( "glFinish" ); - dllFinish(); -} - -static void APIENTRY logFlush(void) -{ - SIG( "glFlush" ); - dllFlush(); -} - -static void APIENTRY logFogf(GLenum pname, GLfloat param) -{ - SIG( "glFogf" ); - dllFogf( pname, param ); -} - -static void APIENTRY logFogfv(GLenum pname, const GLfloat *params) -{ - SIG( "glFogfv" ); - dllFogfv( pname, params ); -} - -static void APIENTRY logFogi(GLenum pname, GLint param) -{ - SIG( "glFogi" ); - dllFogi( pname, param ); -} - -static void APIENTRY logFogiv(GLenum pname, const GLint *params) -{ - SIG( "glFogiv" ); - dllFogiv( pname, params ); -} - -static void APIENTRY logFrontFace(GLenum mode) -{ - SIG( "glFrontFace" ); - dllFrontFace( mode ); -} - -static void APIENTRY logFrustum(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar) -{ - SIG( "glFrustum" ); - dllFrustum( left, right, bottom, top, zNear, zFar ); -} - -static GLuint APIENTRY logGenLists(GLsizei range) -{ - SIG( "glGenLists" ); - return dllGenLists( range ); -} - -static void APIENTRY logGenTextures(GLsizei n, GLuint *textures) -{ - SIG( "glGenTextures" ); - dllGenTextures( n, textures ); -} - -static void APIENTRY logGetBooleanv(GLenum pname, GLboolean *params) -{ - SIG( "glGetBooleanv" ); - dllGetBooleanv( pname, params ); -} - -static void APIENTRY logGetClipPlane(GLenum plane, GLdouble *equation) -{ - SIG( "glGetClipPlane" ); - dllGetClipPlane( plane, equation ); -} - -static void APIENTRY logGetDoublev(GLenum pname, GLdouble *params) -{ - SIG( "glGetDoublev" ); - dllGetDoublev( pname, params ); -} - -static GLenum APIENTRY logGetError(void) -{ - SIG( "glGetError" ); - return dllGetError(); -} - -static void APIENTRY logGetFloatv(GLenum pname, GLfloat *params) -{ - SIG( "glGetFloatv" ); - dllGetFloatv( pname, params ); -} - -static void APIENTRY logGetIntegerv(GLenum pname, GLint *params) -{ - SIG( "glGetIntegerv" ); - dllGetIntegerv( pname, params ); -} - -static void APIENTRY logGetLightfv(GLenum light, GLenum pname, GLfloat *params) -{ - SIG( "glGetLightfv" ); - dllGetLightfv( light, pname, params ); -} - -static void APIENTRY logGetLightiv(GLenum light, GLenum pname, GLint *params) -{ - SIG( "glGetLightiv" ); - dllGetLightiv( light, pname, params ); -} - -static void APIENTRY logGetMapdv(GLenum target, GLenum query, GLdouble *v) -{ - SIG( "glGetMapdv" ); - dllGetMapdv( target, query, v ); -} - -static void APIENTRY logGetMapfv(GLenum target, GLenum query, GLfloat *v) -{ - SIG( "glGetMapfv" ); - dllGetMapfv( target, query, v ); -} - -static void APIENTRY logGetMapiv(GLenum target, GLenum query, GLint *v) -{ - SIG( "glGetMapiv" ); - dllGetMapiv( target, query, v ); -} - -static void APIENTRY logGetMaterialfv(GLenum face, GLenum pname, GLfloat *params) -{ - SIG( "glGetMaterialfv" ); - dllGetMaterialfv( face, pname, params ); -} - -static void APIENTRY logGetMaterialiv(GLenum face, GLenum pname, GLint *params) -{ - SIG( "glGetMaterialiv" ); - dllGetMaterialiv( face, pname, params ); -} - -static void APIENTRY logGetPixelMapfv(GLenum map, GLfloat *values) -{ - SIG( "glGetPixelMapfv" ); - dllGetPixelMapfv( map, values ); -} - -static void APIENTRY logGetPixelMapuiv(GLenum map, GLuint *values) -{ - SIG( "glGetPixelMapuiv" ); - dllGetPixelMapuiv( map, values ); -} - -static void APIENTRY logGetPixelMapusv(GLenum map, GLushort *values) -{ - SIG( "glGetPixelMapusv" ); - dllGetPixelMapusv( map, values ); -} - -static void APIENTRY logGetPointerv(GLenum pname, GLvoid* *params) -{ - SIG( "glGetPointerv" ); - dllGetPointerv( pname, params ); -} - -static void APIENTRY logGetPolygonStipple(GLubyte *mask) -{ - SIG( "glGetPolygonStipple" ); - dllGetPolygonStipple( mask ); -} - -static const GLubyte * APIENTRY logGetString(GLenum name) -{ - SIG( "glGetString" ); - return dllGetString( name ); -} - -static void APIENTRY logGetTexEnvfv(GLenum target, GLenum pname, GLfloat *params) -{ - SIG( "glGetTexEnvfv" ); - dllGetTexEnvfv( target, pname, params ); -} - -static void APIENTRY logGetTexEnviv(GLenum target, GLenum pname, GLint *params) -{ - SIG( "glGetTexEnviv" ); - dllGetTexEnviv( target, pname, params ); -} - -static void APIENTRY logGetTexGendv(GLenum coord, GLenum pname, GLdouble *params) -{ - SIG( "glGetTexGendv" ); - dllGetTexGendv( coord, pname, params ); -} - -static void APIENTRY logGetTexGenfv(GLenum coord, GLenum pname, GLfloat *params) -{ - SIG( "glGetTexGenfv" ); - dllGetTexGenfv( coord, pname, params ); -} - -static void APIENTRY logGetTexGeniv(GLenum coord, GLenum pname, GLint *params) -{ - SIG( "glGetTexGeniv" ); - dllGetTexGeniv( coord, pname, params ); -} - -static void APIENTRY logGetTexImage(GLenum target, GLint level, GLenum format, GLenum type, void *pixels) -{ - SIG( "glGetTexImage" ); - dllGetTexImage( target, level, format, type, pixels ); -} -static void APIENTRY logGetTexLevelParameterfv(GLenum target, GLint level, GLenum pname, GLfloat *params ) -{ - SIG( "glGetTexLevelParameterfv" ); - dllGetTexLevelParameterfv( target, level, pname, params ); -} - -static void APIENTRY logGetTexLevelParameteriv(GLenum target, GLint level, GLenum pname, GLint *params) -{ - SIG( "glGetTexLevelParameteriv" ); - dllGetTexLevelParameteriv( target, level, pname, params ); -} - -static void APIENTRY logGetTexParameterfv(GLenum target, GLenum pname, GLfloat *params) -{ - SIG( "glGetTexParameterfv" ); - dllGetTexParameterfv( target, pname, params ); -} - -static void APIENTRY logGetTexParameteriv(GLenum target, GLenum pname, GLint *params) -{ - SIG( "glGetTexParameteriv" ); - dllGetTexParameteriv( target, pname, params ); -} - -static void APIENTRY logHint(GLenum target, GLenum mode) -{ - fprintf( glw_state.log_fp, "glHint( 0x%x, 0x%x )\n", target, mode ); - dllHint( target, mode ); -} - -static void APIENTRY logIndexMask(GLuint mask) -{ - SIG( "glIndexMask" ); - dllIndexMask( mask ); -} - -static void APIENTRY logIndexPointer(GLenum type, GLsizei stride, const void *pointer) -{ - SIG( "glIndexPointer" ); - dllIndexPointer( type, stride, pointer ); -} - -static void APIENTRY logIndexd(GLdouble c) -{ - SIG( "glIndexd" ); - dllIndexd( c ); -} - -static void APIENTRY logIndexdv(const GLdouble *c) -{ - SIG( "glIndexdv" ); - dllIndexdv( c ); -} - -static void APIENTRY logIndexf(GLfloat c) -{ - SIG( "glIndexf" ); - dllIndexf( c ); -} - -static void APIENTRY logIndexfv(const GLfloat *c) -{ - SIG( "glIndexfv" ); - dllIndexfv( c ); -} - -static void APIENTRY logIndexi(GLint c) -{ - SIG( "glIndexi" ); - dllIndexi( c ); -} - -static void APIENTRY logIndexiv(const GLint *c) -{ - SIG( "glIndexiv" ); - dllIndexiv( c ); -} - -static void APIENTRY logIndexs(GLshort c) -{ - SIG( "glIndexs" ); - dllIndexs( c ); -} - -static void APIENTRY logIndexsv(const GLshort *c) -{ - SIG( "glIndexsv" ); - dllIndexsv( c ); -} - -static void APIENTRY logIndexub(GLubyte c) -{ - SIG( "glIndexub" ); - dllIndexub( c ); -} - -static void APIENTRY logIndexubv(const GLubyte *c) -{ - SIG( "glIndexubv" ); - dllIndexubv( c ); -} - -static void APIENTRY logInitNames(void) -{ - SIG( "glInitNames" ); - dllInitNames(); -} - -static void APIENTRY logInterleavedArrays(GLenum format, GLsizei stride, const void *pointer) -{ - SIG( "glInterleavedArrays" ); - dllInterleavedArrays( format, stride, pointer ); -} - -static GLboolean APIENTRY logIsEnabled(GLenum cap) -{ - SIG( "glIsEnabled" ); - return dllIsEnabled( cap ); -} -static GLboolean APIENTRY logIsList(GLuint list) -{ - SIG( "glIsList" ); - return dllIsList( list ); -} -static GLboolean APIENTRY logIsTexture(GLuint texture) -{ - SIG( "glIsTexture" ); - return dllIsTexture( texture ); -} - -static void APIENTRY logLightModelf(GLenum pname, GLfloat param) -{ - SIG( "glLightModelf" ); - dllLightModelf( pname, param ); -} - -static void APIENTRY logLightModelfv(GLenum pname, const GLfloat *params) -{ - SIG( "glLightModelfv" ); - dllLightModelfv( pname, params ); -} - -static void APIENTRY logLightModeli(GLenum pname, GLint param) -{ - SIG( "glLightModeli" ); - dllLightModeli( pname, param ); - -} - -static void APIENTRY logLightModeliv(GLenum pname, const GLint *params) -{ - SIG( "glLightModeliv" ); - dllLightModeliv( pname, params ); -} - -static void APIENTRY logLightf(GLenum light, GLenum pname, GLfloat param) -{ - SIG( "glLightf" ); - dllLightf( light, pname, param ); -} - -static void APIENTRY logLightfv(GLenum light, GLenum pname, const GLfloat *params) -{ - SIG( "glLightfv" ); - dllLightfv( light, pname, params ); -} - -static void APIENTRY logLighti(GLenum light, GLenum pname, GLint param) -{ - SIG( "glLighti" ); - dllLighti( light, pname, param ); -} - -static void APIENTRY logLightiv(GLenum light, GLenum pname, const GLint *params) -{ - SIG( "glLightiv" ); - dllLightiv( light, pname, params ); -} - -static void APIENTRY logLineStipple(GLint factor, GLushort pattern) -{ - SIG( "glLineStipple" ); - dllLineStipple( factor, pattern ); -} - -static void APIENTRY logLineWidth(GLfloat width) -{ - SIG( "glLineWidth" ); - dllLineWidth( width ); -} - -static void APIENTRY logListBase(GLuint base) -{ - SIG( "glListBase" ); - dllListBase( base ); -} - -static void APIENTRY logLoadIdentity(void) -{ - SIG( "glLoadIdentity" ); - dllLoadIdentity(); -} - -static void APIENTRY logLoadMatrixd(const GLdouble *m) -{ - SIG( "glLoadMatrixd" ); - dllLoadMatrixd( m ); -} - -static void APIENTRY logLoadMatrixf(const GLfloat *m) -{ - SIG( "glLoadMatrixf" ); - dllLoadMatrixf( m ); -} - -static void APIENTRY logLoadName(GLuint name) -{ - SIG( "glLoadName" ); - dllLoadName( name ); -} - -static void APIENTRY logLogicOp(GLenum opcode) -{ - SIG( "glLogicOp" ); - dllLogicOp( opcode ); -} - -static void APIENTRY logMap1d(GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points) -{ - SIG( "glMap1d" ); - dllMap1d( target, u1, u2, stride, order, points ); -} - -static void APIENTRY logMap1f(GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points) -{ - SIG( "glMap1f" ); - dllMap1f( target, u1, u2, stride, order, points ); -} - -static void APIENTRY logMap2d(GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points) -{ - SIG( "glMap2d" ); - dllMap2d( target, u1, u2, ustride, uorder, v1, v2, vstride, vorder, points ); -} - -static void APIENTRY logMap2f(GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points) -{ - SIG( "glMap2f" ); - dllMap2f( target, u1, u2, ustride, uorder, v1, v2, vstride, vorder, points ); -} - -static void APIENTRY logMapGrid1d(GLint un, GLdouble u1, GLdouble u2) -{ - SIG( "glMapGrid1d" ); - dllMapGrid1d( un, u1, u2 ); -} - -static void APIENTRY logMapGrid1f(GLint un, GLfloat u1, GLfloat u2) -{ - SIG( "glMapGrid1f" ); - dllMapGrid1f( un, u1, u2 ); -} - -static void APIENTRY logMapGrid2d(GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2) -{ - SIG( "glMapGrid2d" ); - dllMapGrid2d( un, u1, u2, vn, v1, v2 ); -} -static void APIENTRY logMapGrid2f(GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2) -{ - SIG( "glMapGrid2f" ); - dllMapGrid2f( un, u1, u2, vn, v1, v2 ); -} -static void APIENTRY logMaterialf(GLenum face, GLenum pname, GLfloat param) -{ - SIG( "glMaterialf" ); - dllMaterialf( face, pname, param ); -} -static void APIENTRY logMaterialfv(GLenum face, GLenum pname, const GLfloat *params) -{ - SIG( "glMaterialfv" ); - dllMaterialfv( face, pname, params ); -} - -static void APIENTRY logMateriali(GLenum face, GLenum pname, GLint param) -{ - SIG( "glMateriali" ); - dllMateriali( face, pname, param ); -} - -static void APIENTRY logMaterialiv(GLenum face, GLenum pname, const GLint *params) -{ - SIG( "glMaterialiv" ); - dllMaterialiv( face, pname, params ); -} - -static void APIENTRY logMatrixMode(GLenum mode) -{ - SIG( "glMatrixMode" ); - dllMatrixMode( mode ); -} - -static void APIENTRY logMultMatrixd(const GLdouble *m) -{ - SIG( "glMultMatrixd" ); - dllMultMatrixd( m ); -} - -static void APIENTRY logMultMatrixf(const GLfloat *m) -{ - SIG( "glMultMatrixf" ); - dllMultMatrixf( m ); -} - -static void APIENTRY logNewList(GLuint list, GLenum mode) -{ - SIG( "glNewList" ); - dllNewList( list, mode ); -} - -static void APIENTRY logNormal3b(GLbyte nx, GLbyte ny, GLbyte nz) -{ - SIG ("glNormal3b" ); - dllNormal3b( nx, ny, nz ); -} - -static void APIENTRY logNormal3bv(const GLbyte *v) -{ - SIG( "glNormal3bv" ); - dllNormal3bv( v ); -} - -static void APIENTRY logNormal3d(GLdouble nx, GLdouble ny, GLdouble nz) -{ - SIG( "glNormal3d" ); - dllNormal3d( nx, ny, nz ); -} - -static void APIENTRY logNormal3dv(const GLdouble *v) -{ - SIG( "glNormal3dv" ); - dllNormal3dv( v ); -} - -static void APIENTRY logNormal3f(GLfloat nx, GLfloat ny, GLfloat nz) -{ - SIG( "glNormal3f" ); - dllNormal3f( nx, ny, nz ); -} - -static void APIENTRY logNormal3fv(const GLfloat *v) -{ - SIG( "glNormal3fv" ); - dllNormal3fv( v ); -} -static void APIENTRY logNormal3i(GLint nx, GLint ny, GLint nz) -{ - SIG( "glNormal3i" ); - dllNormal3i( nx, ny, nz ); -} -static void APIENTRY logNormal3iv(const GLint *v) -{ - SIG( "glNormal3iv" ); - dllNormal3iv( v ); -} -static void APIENTRY logNormal3s(GLshort nx, GLshort ny, GLshort nz) -{ - SIG( "glNormal3s" ); - dllNormal3s( nx, ny, nz ); -} -static void APIENTRY logNormal3sv(const GLshort *v) -{ - SIG( "glNormal3sv" ); - dllNormal3sv( v ); -} -static void APIENTRY logNormalPointer(GLenum type, GLsizei stride, const void *pointer) -{ - SIG( "glNormalPointer" ); - dllNormalPointer( type, stride, pointer ); -} -static void APIENTRY logOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar) -{ - SIG( "glOrtho" ); - dllOrtho( left, right, bottom, top, zNear, zFar ); -} - -static void APIENTRY logPassThrough(GLfloat token) -{ - SIG( "glPassThrough" ); - dllPassThrough( token ); -} - -static void APIENTRY logPixelMapfv(GLenum map, GLsizei mapsize, const GLfloat *values) -{ - SIG( "glPixelMapfv" ); - dllPixelMapfv( map, mapsize, values ); -} - -static void APIENTRY logPixelMapuiv(GLenum map, GLsizei mapsize, const GLuint *values) -{ - SIG( "glPixelMapuiv" ); - dllPixelMapuiv( map, mapsize, values ); -} - -static void APIENTRY logPixelMapusv(GLenum map, GLsizei mapsize, const GLushort *values) -{ - SIG( "glPixelMapusv" ); - dllPixelMapusv( map, mapsize, values ); -} -static void APIENTRY logPixelStoref(GLenum pname, GLfloat param) -{ - SIG( "glPixelStoref" ); - dllPixelStoref( pname, param ); -} -static void APIENTRY logPixelStorei(GLenum pname, GLint param) -{ - SIG( "glPixelStorei" ); - dllPixelStorei( pname, param ); -} -static void APIENTRY logPixelTransferf(GLenum pname, GLfloat param) -{ - SIG( "glPixelTransferf" ); - dllPixelTransferf( pname, param ); -} - -static void APIENTRY logPixelTransferi(GLenum pname, GLint param) -{ - SIG( "glPixelTransferi" ); - dllPixelTransferi( pname, param ); -} - -static void APIENTRY logPixelZoom(GLfloat xfactor, GLfloat yfactor) -{ - SIG( "glPixelZoom" ); - dllPixelZoom( xfactor, yfactor ); -} - -static void APIENTRY logPointSize(GLfloat size) -{ - SIG( "glPointSize" ); - dllPointSize( size ); -} - -static void APIENTRY logPolygonMode(GLenum face, GLenum mode) -{ - fprintf( glw_state.log_fp, "glPolygonMode( 0x%x, 0x%x )\n", face, mode ); - dllPolygonMode( face, mode ); -} - -static void APIENTRY logPolygonOffset(GLfloat factor, GLfloat units) -{ - SIG( "glPolygonOffset" ); - dllPolygonOffset( factor, units ); -} -static void APIENTRY logPolygonStipple(const GLubyte *mask ) -{ - SIG( "glPolygonStipple" ); - dllPolygonStipple( mask ); -} -static void APIENTRY logPopAttrib(void) -{ - SIG( "glPopAttrib" ); - dllPopAttrib(); -} - -static void APIENTRY logPopClientAttrib(void) -{ - SIG( "glPopClientAttrib" ); - dllPopClientAttrib(); -} - -static void APIENTRY logPopMatrix(void) -{ - SIG( "glPopMatrix" ); - dllPopMatrix(); -} - -static void APIENTRY logPopName(void) -{ - SIG( "glPopName" ); - dllPopName(); -} - -static void APIENTRY logPrioritizeTextures(GLsizei n, const GLuint *textures, const GLclampf *priorities) -{ - SIG( "glPrioritizeTextures" ); - dllPrioritizeTextures( n, textures, priorities ); -} - -static void APIENTRY logPushAttrib(GLbitfield mask) -{ - SIG( "glPushAttrib" ); - dllPushAttrib( mask ); -} - -static void APIENTRY logPushClientAttrib(GLbitfield mask) -{ - SIG( "glPushClientAttrib" ); - dllPushClientAttrib( mask ); -} - -static void APIENTRY logPushMatrix(void) -{ - SIG( "glPushMatrix" ); - dllPushMatrix(); -} - -static void APIENTRY logPushName(GLuint name) -{ - SIG( "glPushName" ); - dllPushName( name ); -} - -static void APIENTRY logRasterPos2d(GLdouble x, GLdouble y) -{ - SIG ("glRasterPot2d" ); - dllRasterPos2d( x, y ); -} - -static void APIENTRY logRasterPos2dv(const GLdouble *v) -{ - SIG( "glRasterPos2dv" ); - dllRasterPos2dv( v ); -} - -static void APIENTRY logRasterPos2f(GLfloat x, GLfloat y) -{ - SIG( "glRasterPos2f" ); - dllRasterPos2f( x, y ); -} -static void APIENTRY logRasterPos2fv(const GLfloat *v) -{ - SIG( "glRasterPos2dv" ); - dllRasterPos2fv( v ); -} -static void APIENTRY logRasterPos2i(GLint x, GLint y) -{ - SIG( "glRasterPos2if" ); - dllRasterPos2i( x, y ); -} -static void APIENTRY logRasterPos2iv(const GLint *v) -{ - SIG( "glRasterPos2iv" ); - dllRasterPos2iv( v ); -} -static void APIENTRY logRasterPos2s(GLshort x, GLshort y) -{ - SIG( "glRasterPos2s" ); - dllRasterPos2s( x, y ); -} -static void APIENTRY logRasterPos2sv(const GLshort *v) -{ - SIG( "glRasterPos2sv" ); - dllRasterPos2sv( v ); -} -static void APIENTRY logRasterPos3d(GLdouble x, GLdouble y, GLdouble z) -{ - SIG( "glRasterPos3d" ); - dllRasterPos3d( x, y, z ); -} -static void APIENTRY logRasterPos3dv(const GLdouble *v) -{ - SIG( "glRasterPos3dv" ); - dllRasterPos3dv( v ); -} -static void APIENTRY logRasterPos3f(GLfloat x, GLfloat y, GLfloat z) -{ - SIG( "glRasterPos3f" ); - dllRasterPos3f( x, y, z ); -} -static void APIENTRY logRasterPos3fv(const GLfloat *v) -{ - SIG( "glRasterPos3fv" ); - dllRasterPos3fv( v ); -} -static void APIENTRY logRasterPos3i(GLint x, GLint y, GLint z) -{ - SIG( "glRasterPos3i" ); - dllRasterPos3i( x, y, z ); -} -static void APIENTRY logRasterPos3iv(const GLint *v) -{ - SIG( "glRasterPos3iv" ); - dllRasterPos3iv( v ); -} -static void APIENTRY logRasterPos3s(GLshort x, GLshort y, GLshort z) -{ - SIG( "glRasterPos3s" ); - dllRasterPos3s( x, y, z ); -} -static void APIENTRY logRasterPos3sv(const GLshort *v) -{ - SIG( "glRasterPos3sv" ); - dllRasterPos3sv( v ); -} -static void APIENTRY logRasterPos4d(GLdouble x, GLdouble y, GLdouble z, GLdouble w) -{ - SIG( "glRasterPos4d" ); - dllRasterPos4d( x, y, z, w ); -} -static void APIENTRY logRasterPos4dv(const GLdouble *v) -{ - SIG( "glRasterPos4dv" ); - dllRasterPos4dv( v ); -} -static void APIENTRY logRasterPos4f(GLfloat x, GLfloat y, GLfloat z, GLfloat w) -{ - SIG( "glRasterPos4f" ); - dllRasterPos4f( x, y, z, w ); -} -static void APIENTRY logRasterPos4fv(const GLfloat *v) -{ - SIG( "glRasterPos4fv" ); - dllRasterPos4fv( v ); -} -static void APIENTRY logRasterPos4i(GLint x, GLint y, GLint z, GLint w) -{ - SIG( "glRasterPos4i" ); - dllRasterPos4i( x, y, z, w ); -} -static void APIENTRY logRasterPos4iv(const GLint *v) -{ - SIG( "glRasterPos4iv" ); - dllRasterPos4iv( v ); -} -static void APIENTRY logRasterPos4s(GLshort x, GLshort y, GLshort z, GLshort w) -{ - SIG( "glRasterPos4s" ); - dllRasterPos4s( x, y, z, w ); -} -static void APIENTRY logRasterPos4sv(const GLshort *v) -{ - SIG( "glRasterPos4sv" ); - dllRasterPos4sv( v ); -} -static void APIENTRY logReadBuffer(GLenum mode) -{ - SIG( "glReadBuffer" ); - dllReadBuffer( mode ); -} -static void APIENTRY logReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels) -{ - SIG( "glReadPixels" ); - dllReadPixels( x, y, width, height, format, type, pixels ); -} - -static void APIENTRY logRectd(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2) -{ - SIG( "glRectd" ); - dllRectd( x1, y1, x2, y2 ); -} - -static void APIENTRY logRectdv(const GLdouble *v1, const GLdouble *v2) -{ - SIG( "glRectdv" ); - dllRectdv( v1, v2 ); -} - -static void APIENTRY logRectf(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2) -{ - SIG( "glRectf" ); - dllRectf( x1, y1, x2, y2 ); -} - -static void APIENTRY logRectfv(const GLfloat *v1, const GLfloat *v2) -{ - SIG( "glRectfv" ); - dllRectfv( v1, v2 ); -} -static void APIENTRY logRecti(GLint x1, GLint y1, GLint x2, GLint y2) -{ - SIG( "glRecti" ); - dllRecti( x1, y1, x2, y2 ); -} -static void APIENTRY logRectiv(const GLint *v1, const GLint *v2) -{ - SIG( "glRectiv" ); - dllRectiv( v1, v2 ); -} -static void APIENTRY logRects(GLshort x1, GLshort y1, GLshort x2, GLshort y2) -{ - SIG( "glRects" ); - dllRects( x1, y1, x2, y2 ); -} -static void APIENTRY logRectsv(const GLshort *v1, const GLshort *v2) -{ - SIG( "glRectsv" ); - dllRectsv( v1, v2 ); -} -static GLint APIENTRY logRenderMode(GLenum mode) -{ - SIG( "glRenderMode" ); - return dllRenderMode( mode ); -} -static void APIENTRY logRotated(GLdouble angle, GLdouble x, GLdouble y, GLdouble z) -{ - SIG( "glRotated" ); - dllRotated( angle, x, y, z ); -} - -static void APIENTRY logRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z) -{ - SIG( "glRotatef" ); - dllRotatef( angle, x, y, z ); -} - -static void APIENTRY logScaled(GLdouble x, GLdouble y, GLdouble z) -{ - SIG( "glScaled" ); - dllScaled( x, y, z ); -} - -static void APIENTRY logScalef(GLfloat x, GLfloat y, GLfloat z) -{ - SIG( "glScalef" ); - dllScalef( x, y, z ); -} - -static void APIENTRY logScissor(GLint x, GLint y, GLsizei width, GLsizei height) -{ - SIG( "glScissor" ); - dllScissor( x, y, width, height ); -} - -static void APIENTRY logSelectBuffer(GLsizei size, GLuint *buffer) -{ - SIG( "glSelectBuffer" ); - dllSelectBuffer( size, buffer ); -} - -static void APIENTRY logShadeModel(GLenum mode) -{ - SIG( "glShadeModel" ); - dllShadeModel( mode ); -} - -static void APIENTRY logStencilFunc(GLenum func, GLint ref, GLuint mask) -{ - SIG( "glStencilFunc" ); - dllStencilFunc( func, ref, mask ); -} - -static void APIENTRY logStencilMask(GLuint mask) -{ - SIG( "glStencilMask" ); - dllStencilMask( mask ); -} - -static void APIENTRY logStencilOp(GLenum fail, GLenum zfail, GLenum zpass) -{ - SIG( "glStencilOp" ); - dllStencilOp( fail, zfail, zpass ); -} - -static void APIENTRY logTexCoord1d(GLdouble s) -{ - SIG( "glTexCoord1d" ); - dllTexCoord1d( s ); -} - -static void APIENTRY logTexCoord1dv(const GLdouble *v) -{ - SIG( "glTexCoord1dv" ); - dllTexCoord1dv( v ); -} - -static void APIENTRY logTexCoord1f(GLfloat s) -{ - SIG( "glTexCoord1f" ); - dllTexCoord1f( s ); -} -static void APIENTRY logTexCoord1fv(const GLfloat *v) -{ - SIG( "glTexCoord1fv" ); - dllTexCoord1fv( v ); -} -static void APIENTRY logTexCoord1i(GLint s) -{ - SIG( "glTexCoord1i" ); - dllTexCoord1i( s ); -} -static void APIENTRY logTexCoord1iv(const GLint *v) -{ - SIG( "glTexCoord1iv" ); - dllTexCoord1iv( v ); -} -static void APIENTRY logTexCoord1s(GLshort s) -{ - SIG( "glTexCoord1s" ); - dllTexCoord1s( s ); -} -static void APIENTRY logTexCoord1sv(const GLshort *v) -{ - SIG( "glTexCoord1sv" ); - dllTexCoord1sv( v ); -} -static void APIENTRY logTexCoord2d(GLdouble s, GLdouble t) -{ - SIG( "glTexCoord2d" ); - dllTexCoord2d( s, t ); -} - -static void APIENTRY logTexCoord2dv(const GLdouble *v) -{ - SIG( "glTexCoord2dv" ); - dllTexCoord2dv( v ); -} -static void APIENTRY logTexCoord2f(GLfloat s, GLfloat t) -{ - SIG( "glTexCoord2f" ); - dllTexCoord2f( s, t ); -} -static void APIENTRY logTexCoord2fv(const GLfloat *v) -{ - SIG( "glTexCoord2fv" ); - dllTexCoord2fv( v ); -} -static void APIENTRY logTexCoord2i(GLint s, GLint t) -{ - SIG( "glTexCoord2i" ); - dllTexCoord2i( s, t ); -} -static void APIENTRY logTexCoord2iv(const GLint *v) -{ - SIG( "glTexCoord2iv" ); - dllTexCoord2iv( v ); -} -static void APIENTRY logTexCoord2s(GLshort s, GLshort t) -{ - SIG( "glTexCoord2s" ); - dllTexCoord2s( s, t ); -} -static void APIENTRY logTexCoord2sv(const GLshort *v) -{ - SIG( "glTexCoord2sv" ); - dllTexCoord2sv( v ); -} -static void APIENTRY logTexCoord3d(GLdouble s, GLdouble t, GLdouble r) -{ - SIG( "glTexCoord3d" ); - dllTexCoord3d( s, t, r ); -} -static void APIENTRY logTexCoord3dv(const GLdouble *v) -{ - SIG( "glTexCoord3dv" ); - dllTexCoord3dv( v ); -} -static void APIENTRY logTexCoord3f(GLfloat s, GLfloat t, GLfloat r) -{ - SIG( "glTexCoord3f" ); - dllTexCoord3f( s, t, r ); -} -static void APIENTRY logTexCoord3fv(const GLfloat *v) -{ - SIG( "glTexCoord3fv" ); - dllTexCoord3fv( v ); -} -static void APIENTRY logTexCoord3i(GLint s, GLint t, GLint r) -{ - SIG( "glTexCoord3i" ); - dllTexCoord3i( s, t, r ); -} -static void APIENTRY logTexCoord3iv(const GLint *v) -{ - SIG( "glTexCoord3iv" ); - dllTexCoord3iv( v ); -} -static void APIENTRY logTexCoord3s(GLshort s, GLshort t, GLshort r) -{ - SIG( "glTexCoord3s" ); - dllTexCoord3s( s, t, r ); -} -static void APIENTRY logTexCoord3sv(const GLshort *v) -{ - SIG( "glTexCoord3sv" ); - dllTexCoord3sv( v ); -} -static void APIENTRY logTexCoord4d(GLdouble s, GLdouble t, GLdouble r, GLdouble q) -{ - SIG( "glTexCoord4d" ); - dllTexCoord4d( s, t, r, q ); -} -static void APIENTRY logTexCoord4dv(const GLdouble *v) -{ - SIG( "glTexCoord4dv" ); - dllTexCoord4dv( v ); -} -static void APIENTRY logTexCoord4f(GLfloat s, GLfloat t, GLfloat r, GLfloat q) -{ - SIG( "glTexCoord4f" ); - dllTexCoord4f( s, t, r, q ); -} -static void APIENTRY logTexCoord4fv(const GLfloat *v) -{ - SIG( "glTexCoord4fv" ); - dllTexCoord4fv( v ); -} -static void APIENTRY logTexCoord4i(GLint s, GLint t, GLint r, GLint q) -{ - SIG( "glTexCoord4i" ); - dllTexCoord4i( s, t, r, q ); -} -static void APIENTRY logTexCoord4iv(const GLint *v) -{ - SIG( "glTexCoord4iv" ); - dllTexCoord4iv( v ); -} -static void APIENTRY logTexCoord4s(GLshort s, GLshort t, GLshort r, GLshort q) -{ - SIG( "glTexCoord4s" ); - dllTexCoord4s( s, t, r, q ); -} -static void APIENTRY logTexCoord4sv(const GLshort *v) -{ - SIG( "glTexCoord4sv" ); - dllTexCoord4sv( v ); -} -static void APIENTRY logTexCoordPointer(GLint size, GLenum type, GLsizei stride, const void *pointer) -{ - SIG( "glTexCoordPointer" ); - dllTexCoordPointer( size, type, stride, pointer ); -} - -static void APIENTRY logTexEnvf(GLenum target, GLenum pname, GLfloat param) -{ - fprintf( glw_state.log_fp, "glTexEnvf( 0x%x, 0x%x, %f )\n", target, pname, param ); - dllTexEnvf( target, pname, param ); -} - -static void APIENTRY logTexEnvfv(GLenum target, GLenum pname, const GLfloat *params) -{ - SIG( "glTexEnvfv" ); - dllTexEnvfv( target, pname, params ); -} - -static void APIENTRY logTexEnvi(GLenum target, GLenum pname, GLint param) -{ - fprintf( glw_state.log_fp, "glTexEnvi( 0x%x, 0x%x, 0x%x )\n", target, pname, param ); - dllTexEnvi( target, pname, param ); -} -static void APIENTRY logTexEnviv(GLenum target, GLenum pname, const GLint *params) -{ - SIG( "glTexEnviv" ); - dllTexEnviv( target, pname, params ); -} - -static void APIENTRY logTexGend(GLenum coord, GLenum pname, GLdouble param) -{ - SIG( "glTexGend" ); - dllTexGend( coord, pname, param ); -} - -static void APIENTRY logTexGendv(GLenum coord, GLenum pname, const GLdouble *params) -{ - SIG( "glTexGendv" ); - dllTexGendv( coord, pname, params ); -} - -static void APIENTRY logTexGenf(GLenum coord, GLenum pname, GLfloat param) -{ - SIG( "glTexGenf" ); - dllTexGenf( coord, pname, param ); -} -static void APIENTRY logTexGenfv(GLenum coord, GLenum pname, const GLfloat *params) -{ - SIG( "glTexGenfv" ); - dllTexGenfv( coord, pname, params ); -} -static void APIENTRY logTexGeni(GLenum coord, GLenum pname, GLint param) -{ - SIG( "glTexGeni" ); - dllTexGeni( coord, pname, param ); -} -static void APIENTRY logTexGeniv(GLenum coord, GLenum pname, const GLint *params) -{ - SIG( "glTexGeniv" ); - dllTexGeniv( coord, pname, params ); -} -static void APIENTRY logTexImage1D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels) -{ - SIG( "glTexImage1D" ); - dllTexImage1D( target, level, internalformat, width, border, format, type, pixels ); -} -static void APIENTRY logTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) -{ - SIG( "glTexImage2D" ); - dllTexImage2D( target, level, internalformat, width, height, border, format, type, pixels ); -} - -static void APIENTRY logTexParameterf(GLenum target, GLenum pname, GLfloat param) -{ - fprintf( glw_state.log_fp, "glTexParameterf( 0x%x, 0x%x, %f )\n", target, pname, param ); - dllTexParameterf( target, pname, param ); -} - -static void APIENTRY logTexParameterfv(GLenum target, GLenum pname, const GLfloat *params) -{ - SIG( "glTexParameterfv" ); - dllTexParameterfv( target, pname, params ); -} -static void APIENTRY logTexParameteri(GLenum target, GLenum pname, GLint param) -{ - fprintf( glw_state.log_fp, "glTexParameteri( 0x%x, 0x%x, 0x%x )\n", target, pname, param ); - dllTexParameteri( target, pname, param ); -} -static void APIENTRY logTexParameteriv(GLenum target, GLenum pname, const GLint *params) -{ - SIG( "glTexParameteriv" ); - dllTexParameteriv( target, pname, params ); -} -static void APIENTRY logTexSubImage1D(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels) -{ - SIG( "glTexSubImage1D" ); - dllTexSubImage1D( target, level, xoffset, width, format, type, pixels ); -} -static void APIENTRY logTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) -{ - SIG( "glTexSubImage2D" ); - dllTexSubImage2D( target, level, xoffset, yoffset, width, height, format, type, pixels ); -} -static void APIENTRY logTranslated(GLdouble x, GLdouble y, GLdouble z) -{ - SIG( "glTranslated" ); - dllTranslated( x, y, z ); -} - -static void APIENTRY logTranslatef(GLfloat x, GLfloat y, GLfloat z) -{ - SIG( "glTranslatef" ); - dllTranslatef( x, y, z ); -} - -static void APIENTRY logVertex2d(GLdouble x, GLdouble y) -{ - SIG( "glVertex2d" ); - dllVertex2d( x, y ); -} - -static void APIENTRY logVertex2dv(const GLdouble *v) -{ - SIG( "glVertex2dv" ); - dllVertex2dv( v ); -} -static void APIENTRY logVertex2f(GLfloat x, GLfloat y) -{ - SIG( "glVertex2f" ); - dllVertex2f( x, y ); -} -static void APIENTRY logVertex2fv(const GLfloat *v) -{ - SIG( "glVertex2fv" ); - dllVertex2fv( v ); -} -static void APIENTRY logVertex2i(GLint x, GLint y) -{ - SIG( "glVertex2i" ); - dllVertex2i( x, y ); -} -static void APIENTRY logVertex2iv(const GLint *v) -{ - SIG( "glVertex2iv" ); - dllVertex2iv( v ); -} -static void APIENTRY logVertex2s(GLshort x, GLshort y) -{ - SIG( "glVertex2s" ); - dllVertex2s( x, y ); -} -static void APIENTRY logVertex2sv(const GLshort *v) -{ - SIG( "glVertex2sv" ); - dllVertex2sv( v ); -} -static void APIENTRY logVertex3d(GLdouble x, GLdouble y, GLdouble z) -{ - SIG( "glVertex3d" ); - dllVertex3d( x, y, z ); -} -static void APIENTRY logVertex3dv(const GLdouble *v) -{ - SIG( "glVertex3dv" ); - dllVertex3dv( v ); -} -static void APIENTRY logVertex3f(GLfloat x, GLfloat y, GLfloat z) -{ - SIG( "glVertex3f" ); - dllVertex3f( x, y, z ); -} -static void APIENTRY logVertex3fv(const GLfloat *v) -{ - SIG( "glVertex3fv" ); - dllVertex3fv( v ); -} -static void APIENTRY logVertex3i(GLint x, GLint y, GLint z) -{ - SIG( "glVertex3i" ); - dllVertex3i( x, y, z ); -} -static void APIENTRY logVertex3iv(const GLint *v) -{ - SIG( "glVertex3iv" ); - dllVertex3iv( v ); -} -static void APIENTRY logVertex3s(GLshort x, GLshort y, GLshort z) -{ - SIG( "glVertex3s" ); - dllVertex3s( x, y, z ); -} -static void APIENTRY logVertex3sv(const GLshort *v) -{ - SIG( "glVertex3sv" ); - dllVertex3sv( v ); -} -static void APIENTRY logVertex4d(GLdouble x, GLdouble y, GLdouble z, GLdouble w) -{ - SIG( "glVertex4d" ); - dllVertex4d( x, y, z, w ); -} -static void APIENTRY logVertex4dv(const GLdouble *v) -{ - SIG( "glVertex4dv" ); - dllVertex4dv( v ); -} -static void APIENTRY logVertex4f(GLfloat x, GLfloat y, GLfloat z, GLfloat w) -{ - SIG( "glVertex4f" ); - dllVertex4f( x, y, z, w ); -} -static void APIENTRY logVertex4fv(const GLfloat *v) -{ - SIG( "glVertex4fv" ); - dllVertex4fv( v ); -} -static void APIENTRY logVertex4i(GLint x, GLint y, GLint z, GLint w) -{ - SIG( "glVertex4i" ); - dllVertex4i( x, y, z, w ); -} -static void APIENTRY logVertex4iv(const GLint *v) -{ - SIG( "glVertex4iv" ); - dllVertex4iv( v ); -} -static void APIENTRY logVertex4s(GLshort x, GLshort y, GLshort z, GLshort w) -{ - SIG( "glVertex4s" ); - dllVertex4s( x, y, z, w ); -} -static void APIENTRY logVertex4sv(const GLshort *v) -{ - SIG( "glVertex4sv" ); - dllVertex4sv( v ); -} -static void APIENTRY logVertexPointer(GLint size, GLenum type, GLsizei stride, const void *pointer) -{ - SIG( "glVertexPointer" ); - dllVertexPointer( size, type, stride, pointer ); -} -static void APIENTRY logViewport(GLint x, GLint y, GLsizei width, GLsizei height) -{ - SIG( "glViewport" ); - dllViewport( x, y, width, height ); -} - -/* -** QGL_Shutdown -** -** Unloads the specified DLL then nulls out all the proc pointers. -*/ -void QGL_Shutdown( void ) -{ - if ( glw_state.OpenGLLib ) - { - dlclose ( glw_state.OpenGLLib ); - glw_state.OpenGLLib = NULL; - } - - glw_state.OpenGLLib = NULL; - - qglAccum = NULL; - qglAlphaFunc = NULL; - qglAreTexturesResident = NULL; - qglArrayElement = NULL; - qglBegin = NULL; - qglBindTexture = NULL; - qglBitmap = NULL; - qglBlendFunc = NULL; - qglCallList = NULL; - qglCallLists = NULL; - qglClear = NULL; - qglClearAccum = NULL; - qglClearColor = NULL; - qglClearDepth = NULL; - qglClearIndex = NULL; - qglClearStencil = NULL; - qglClipPlane = NULL; - qglColor3b = NULL; - qglColor3bv = NULL; - qglColor3d = NULL; - qglColor3dv = NULL; - qglColor3f = NULL; - qglColor3fv = NULL; - qglColor3i = NULL; - qglColor3iv = NULL; - qglColor3s = NULL; - qglColor3sv = NULL; - qglColor3ub = NULL; - qglColor3ubv = NULL; - qglColor3ui = NULL; - qglColor3uiv = NULL; - qglColor3us = NULL; - qglColor3usv = NULL; - qglColor4b = NULL; - qglColor4bv = NULL; - qglColor4d = NULL; - qglColor4dv = NULL; - qglColor4f = NULL; - qglColor4fv = NULL; - qglColor4i = NULL; - qglColor4iv = NULL; - qglColor4s = NULL; - qglColor4sv = NULL; - qglColor4ub = NULL; - qglColor4ubv = NULL; - qglColor4ui = NULL; - qglColor4uiv = NULL; - qglColor4us = NULL; - qglColor4usv = NULL; - qglColorMask = NULL; - qglColorMaterial = NULL; - qglColorPointer = NULL; - qglCopyPixels = NULL; - qglCopyTexImage1D = NULL; - qglCopyTexImage2D = NULL; - qglCopyTexSubImage1D = NULL; - qglCopyTexSubImage2D = NULL; - qglCullFace = NULL; - qglDeleteLists = NULL; - qglDeleteTextures = NULL; - qglDepthFunc = NULL; - qglDepthMask = NULL; - qglDepthRange = NULL; - qglDisable = NULL; - qglDisableClientState = NULL; - qglDrawArrays = NULL; - qglDrawBuffer = NULL; - qglDrawElements = NULL; - qglDrawPixels = NULL; - qglEdgeFlag = NULL; - qglEdgeFlagPointer = NULL; - qglEdgeFlagv = NULL; - qglEnable = NULL; - qglEnableClientState = NULL; - qglEnd = NULL; - qglEndList = NULL; - qglEvalCoord1d = NULL; - qglEvalCoord1dv = NULL; - qglEvalCoord1f = NULL; - qglEvalCoord1fv = NULL; - qglEvalCoord2d = NULL; - qglEvalCoord2dv = NULL; - qglEvalCoord2f = NULL; - qglEvalCoord2fv = NULL; - qglEvalMesh1 = NULL; - qglEvalMesh2 = NULL; - qglEvalPoint1 = NULL; - qglEvalPoint2 = NULL; - qglFeedbackBuffer = NULL; - qglFinish = NULL; - qglFlush = NULL; - qglFogf = NULL; - qglFogfv = NULL; - qglFogi = NULL; - qglFogiv = NULL; - qglFrontFace = NULL; - qglFrustum = NULL; - qglGenLists = NULL; - qglGenTextures = NULL; - qglGetBooleanv = NULL; - qglGetClipPlane = NULL; - qglGetDoublev = NULL; - qglGetError = NULL; - qglGetFloatv = NULL; - qglGetIntegerv = NULL; - qglGetLightfv = NULL; - qglGetLightiv = NULL; - qglGetMapdv = NULL; - qglGetMapfv = NULL; - qglGetMapiv = NULL; - qglGetMaterialfv = NULL; - qglGetMaterialiv = NULL; - qglGetPixelMapfv = NULL; - qglGetPixelMapuiv = NULL; - qglGetPixelMapusv = NULL; - qglGetPointerv = NULL; - qglGetPolygonStipple = NULL; - qglGetString = NULL; - qglGetTexEnvfv = NULL; - qglGetTexEnviv = NULL; - qglGetTexGendv = NULL; - qglGetTexGenfv = NULL; - qglGetTexGeniv = NULL; - qglGetTexImage = NULL; - qglGetTexLevelParameterfv = NULL; - qglGetTexLevelParameteriv = NULL; - qglGetTexParameterfv = NULL; - qglGetTexParameteriv = NULL; - qglHint = NULL; - qglIndexMask = NULL; - qglIndexPointer = NULL; - qglIndexd = NULL; - qglIndexdv = NULL; - qglIndexf = NULL; - qglIndexfv = NULL; - qglIndexi = NULL; - qglIndexiv = NULL; - qglIndexs = NULL; - qglIndexsv = NULL; - qglIndexub = NULL; - qglIndexubv = NULL; - qglInitNames = NULL; - qglInterleavedArrays = NULL; - qglIsEnabled = NULL; - qglIsList = NULL; - qglIsTexture = NULL; - qglLightModelf = NULL; - qglLightModelfv = NULL; - qglLightModeli = NULL; - qglLightModeliv = NULL; - qglLightf = NULL; - qglLightfv = NULL; - qglLighti = NULL; - qglLightiv = NULL; - qglLineStipple = NULL; - qglLineWidth = NULL; - qglListBase = NULL; - qglLoadIdentity = NULL; - qglLoadMatrixd = NULL; - qglLoadMatrixf = NULL; - qglLoadName = NULL; - qglLogicOp = NULL; - qglMap1d = NULL; - qglMap1f = NULL; - qglMap2d = NULL; - qglMap2f = NULL; - qglMapGrid1d = NULL; - qglMapGrid1f = NULL; - qglMapGrid2d = NULL; - qglMapGrid2f = NULL; - qglMaterialf = NULL; - qglMaterialfv = NULL; - qglMateriali = NULL; - qglMaterialiv = NULL; - qglMatrixMode = NULL; - qglMultMatrixd = NULL; - qglMultMatrixf = NULL; - qglNewList = NULL; - qglNormal3b = NULL; - qglNormal3bv = NULL; - qglNormal3d = NULL; - qglNormal3dv = NULL; - qglNormal3f = NULL; - qglNormal3fv = NULL; - qglNormal3i = NULL; - qglNormal3iv = NULL; - qglNormal3s = NULL; - qglNormal3sv = NULL; - qglNormalPointer = NULL; - qglOrtho = NULL; - qglPassThrough = NULL; - qglPixelMapfv = NULL; - qglPixelMapuiv = NULL; - qglPixelMapusv = NULL; - qglPixelStoref = NULL; - qglPixelStorei = NULL; - qglPixelTransferf = NULL; - qglPixelTransferi = NULL; - qglPixelZoom = NULL; - qglPointSize = NULL; - qglPolygonMode = NULL; - qglPolygonOffset = NULL; - qglPolygonStipple = NULL; - qglPopAttrib = NULL; - qglPopClientAttrib = NULL; - qglPopMatrix = NULL; - qglPopName = NULL; - qglPrioritizeTextures = NULL; - qglPushAttrib = NULL; - qglPushClientAttrib = NULL; - qglPushMatrix = NULL; - qglPushName = NULL; - qglRasterPos2d = NULL; - qglRasterPos2dv = NULL; - qglRasterPos2f = NULL; - qglRasterPos2fv = NULL; - qglRasterPos2i = NULL; - qglRasterPos2iv = NULL; - qglRasterPos2s = NULL; - qglRasterPos2sv = NULL; - qglRasterPos3d = NULL; - qglRasterPos3dv = NULL; - qglRasterPos3f = NULL; - qglRasterPos3fv = NULL; - qglRasterPos3i = NULL; - qglRasterPos3iv = NULL; - qglRasterPos3s = NULL; - qglRasterPos3sv = NULL; - qglRasterPos4d = NULL; - qglRasterPos4dv = NULL; - qglRasterPos4f = NULL; - qglRasterPos4fv = NULL; - qglRasterPos4i = NULL; - qglRasterPos4iv = NULL; - qglRasterPos4s = NULL; - qglRasterPos4sv = NULL; - qglReadBuffer = NULL; - qglReadPixels = NULL; - qglRectd = NULL; - qglRectdv = NULL; - qglRectf = NULL; - qglRectfv = NULL; - qglRecti = NULL; - qglRectiv = NULL; - qglRects = NULL; - qglRectsv = NULL; - qglRenderMode = NULL; - qglRotated = NULL; - qglRotatef = NULL; - qglScaled = NULL; - qglScalef = NULL; - qglScissor = NULL; - qglSelectBuffer = NULL; - qglShadeModel = NULL; - qglStencilFunc = NULL; - qglStencilMask = NULL; - qglStencilOp = NULL; - qglTexCoord1d = NULL; - qglTexCoord1dv = NULL; - qglTexCoord1f = NULL; - qglTexCoord1fv = NULL; - qglTexCoord1i = NULL; - qglTexCoord1iv = NULL; - qglTexCoord1s = NULL; - qglTexCoord1sv = NULL; - qglTexCoord2d = NULL; - qglTexCoord2dv = NULL; - qglTexCoord2f = NULL; - qglTexCoord2fv = NULL; - qglTexCoord2i = NULL; - qglTexCoord2iv = NULL; - qglTexCoord2s = NULL; - qglTexCoord2sv = NULL; - qglTexCoord3d = NULL; - qglTexCoord3dv = NULL; - qglTexCoord3f = NULL; - qglTexCoord3fv = NULL; - qglTexCoord3i = NULL; - qglTexCoord3iv = NULL; - qglTexCoord3s = NULL; - qglTexCoord3sv = NULL; - qglTexCoord4d = NULL; - qglTexCoord4dv = NULL; - qglTexCoord4f = NULL; - qglTexCoord4fv = NULL; - qglTexCoord4i = NULL; - qglTexCoord4iv = NULL; - qglTexCoord4s = NULL; - qglTexCoord4sv = NULL; - qglTexCoordPointer = NULL; - qglTexEnvf = NULL; - qglTexEnvfv = NULL; - qglTexEnvi = NULL; - qglTexEnviv = NULL; - qglTexGend = NULL; - qglTexGendv = NULL; - qglTexGenf = NULL; - qglTexGenfv = NULL; - qglTexGeni = NULL; - qglTexGeniv = NULL; - qglTexImage1D = NULL; - qglTexImage2D = NULL; - qglTexParameterf = NULL; - qglTexParameterfv = NULL; - qglTexParameteri = NULL; - qglTexParameteriv = NULL; - qglTexSubImage1D = NULL; - qglTexSubImage2D = NULL; - qglTranslated = NULL; - qglTranslatef = NULL; - qglVertex2d = NULL; - qglVertex2dv = NULL; - qglVertex2f = NULL; - qglVertex2fv = NULL; - qglVertex2i = NULL; - qglVertex2iv = NULL; - qglVertex2s = NULL; - qglVertex2sv = NULL; - qglVertex3d = NULL; - qglVertex3dv = NULL; - qglVertex3f = NULL; - qglVertex3fv = NULL; - qglVertex3i = NULL; - qglVertex3iv = NULL; - qglVertex3s = NULL; - qglVertex3sv = NULL; - qglVertex4d = NULL; - qglVertex4dv = NULL; - qglVertex4f = NULL; - qglVertex4fv = NULL; - qglVertex4i = NULL; - qglVertex4iv = NULL; - qglVertex4s = NULL; - qglVertex4sv = NULL; - qglVertexPointer = NULL; - qglViewport = NULL; - - qfxMesaCreateContext = NULL; - qfxMesaCreateBestContext = NULL; - qfxMesaDestroyContext = NULL; - qfxMesaMakeCurrent = NULL; - qfxMesaGetCurrentContext = NULL; - qfxMesaSwapBuffers = NULL; - - qglXChooseVisual = NULL; - qglXCreateContext = NULL; - qglXDestroyContext = NULL; - qglXMakeCurrent = NULL; - qglXCopyContext = NULL; - qglXSwapBuffers = NULL; -} - -#define GPA( a ) dlsym( glw_state.OpenGLLib, a ) - -void *qwglGetProcAddress(char *symbol) -{ - if (glw_state.OpenGLLib) - return GPA ( symbol ); - return NULL; -} - -/* -** QGL_Init -** -** This is responsible for binding our qgl function pointers to -** the appropriate GL stuff. In Windows this means doing a -** LoadLibrary and a bunch of calls to GetProcAddress. On other -** operating systems we need to do the right thing, whatever that -** might be. -** -*/ - -qboolean QGL_Init( const char *dllname ) -{ -#if 0 //FIXME - // update 3Dfx gamma irrespective of underlying DLL - { - char envbuffer[1024]; - float g; - - g = 2.00 * ( 0.8 - ( vid_gamma->value - 0.5 ) ) + 1.0F; - Com_sprintf( envbuffer, sizeof(envbuffer), "SSTV2_GAMMA=%f", g ); - putenv( envbuffer ); - Com_sprintf( envbuffer, sizeof(envbuffer), "SST_GAMMA=%f", g ); - putenv( envbuffer ); - } -#endif - - if ( ( glw_state.OpenGLLib = dlopen( dllname, RTLD_LAZY ) ) == 0 ) - { - char fn[1024]; - FILE *fp; - extern uid_t saved_euid; // unix_main.c - -//fprintf(stdout, "uid=%d,euid=%d\n", getuid(), geteuid()); fflush(stdout); - - // if we are not setuid, try current directory - if (getuid() == saved_euid) { - getcwd(fn, sizeof(fn)); - Q_strcat(fn, sizeof(fn), "/"); - Q_strcat(fn, sizeof(fn), dllname); - - if ( ( glw_state.OpenGLLib = dlopen( fn, RTLD_LAZY ) ) == 0 ) { - ri.Printf(PRINT_ALL, "QGL_Init: Can't load %s from /etc/ld.so.conf or current dir: %s\n", dllname, dlerror()); - return qfalse; - } - } else { - ri.Printf(PRINT_ALL, "QGL_Init: Can't load %s from /etc/ld.so.conf: %s\n", dllname, dlerror()); - return qfalse; - } - } - - qglAccum = dllAccum = GPA( "glAccum" ); - qglAlphaFunc = dllAlphaFunc = GPA( "glAlphaFunc" ); - qglAreTexturesResident = dllAreTexturesResident = GPA( "glAreTexturesResident" ); - qglArrayElement = dllArrayElement = GPA( "glArrayElement" ); - qglBegin = dllBegin = GPA( "glBegin" ); - qglBindTexture = dllBindTexture = GPA( "glBindTexture" ); - qglBitmap = dllBitmap = GPA( "glBitmap" ); - qglBlendFunc = dllBlendFunc = GPA( "glBlendFunc" ); - qglCallList = dllCallList = GPA( "glCallList" ); - qglCallLists = dllCallLists = GPA( "glCallLists" ); - qglClear = dllClear = GPA( "glClear" ); - qglClearAccum = dllClearAccum = GPA( "glClearAccum" ); - qglClearColor = dllClearColor = GPA( "glClearColor" ); - qglClearDepth = dllClearDepth = GPA( "glClearDepth" ); - qglClearIndex = dllClearIndex = GPA( "glClearIndex" ); - qglClearStencil = dllClearStencil = GPA( "glClearStencil" ); - qglClipPlane = dllClipPlane = GPA( "glClipPlane" ); - qglColor3b = dllColor3b = GPA( "glColor3b" ); - qglColor3bv = dllColor3bv = GPA( "glColor3bv" ); - qglColor3d = dllColor3d = GPA( "glColor3d" ); - qglColor3dv = dllColor3dv = GPA( "glColor3dv" ); - qglColor3f = dllColor3f = GPA( "glColor3f" ); - qglColor3fv = dllColor3fv = GPA( "glColor3fv" ); - qglColor3i = dllColor3i = GPA( "glColor3i" ); - qglColor3iv = dllColor3iv = GPA( "glColor3iv" ); - qglColor3s = dllColor3s = GPA( "glColor3s" ); - qglColor3sv = dllColor3sv = GPA( "glColor3sv" ); - qglColor3ub = dllColor3ub = GPA( "glColor3ub" ); - qglColor3ubv = dllColor3ubv = GPA( "glColor3ubv" ); - qglColor3ui = dllColor3ui = GPA( "glColor3ui" ); - qglColor3uiv = dllColor3uiv = GPA( "glColor3uiv" ); - qglColor3us = dllColor3us = GPA( "glColor3us" ); - qglColor3usv = dllColor3usv = GPA( "glColor3usv" ); - qglColor4b = dllColor4b = GPA( "glColor4b" ); - qglColor4bv = dllColor4bv = GPA( "glColor4bv" ); - qglColor4d = dllColor4d = GPA( "glColor4d" ); - qglColor4dv = dllColor4dv = GPA( "glColor4dv" ); - qglColor4f = dllColor4f = GPA( "glColor4f" ); - qglColor4fv = dllColor4fv = GPA( "glColor4fv" ); - qglColor4i = dllColor4i = GPA( "glColor4i" ); - qglColor4iv = dllColor4iv = GPA( "glColor4iv" ); - qglColor4s = dllColor4s = GPA( "glColor4s" ); - qglColor4sv = dllColor4sv = GPA( "glColor4sv" ); - qglColor4ub = dllColor4ub = GPA( "glColor4ub" ); - qglColor4ubv = dllColor4ubv = GPA( "glColor4ubv" ); - qglColor4ui = dllColor4ui = GPA( "glColor4ui" ); - qglColor4uiv = dllColor4uiv = GPA( "glColor4uiv" ); - qglColor4us = dllColor4us = GPA( "glColor4us" ); - qglColor4usv = dllColor4usv = GPA( "glColor4usv" ); - qglColorMask = dllColorMask = GPA( "glColorMask" ); - qglColorMaterial = dllColorMaterial = GPA( "glColorMaterial" ); - qglColorPointer = dllColorPointer = GPA( "glColorPointer" ); - qglCopyPixels = dllCopyPixels = GPA( "glCopyPixels" ); - qglCopyTexImage1D = dllCopyTexImage1D = GPA( "glCopyTexImage1D" ); - qglCopyTexImage2D = dllCopyTexImage2D = GPA( "glCopyTexImage2D" ); - qglCopyTexSubImage1D = dllCopyTexSubImage1D = GPA( "glCopyTexSubImage1D" ); - qglCopyTexSubImage2D = dllCopyTexSubImage2D = GPA( "glCopyTexSubImage2D" ); - qglCullFace = dllCullFace = GPA( "glCullFace" ); - qglDeleteLists = dllDeleteLists = GPA( "glDeleteLists" ); - qglDeleteTextures = dllDeleteTextures = GPA( "glDeleteTextures" ); - qglDepthFunc = dllDepthFunc = GPA( "glDepthFunc" ); - qglDepthMask = dllDepthMask = GPA( "glDepthMask" ); - qglDepthRange = dllDepthRange = GPA( "glDepthRange" ); - qglDisable = dllDisable = GPA( "glDisable" ); - qglDisableClientState = dllDisableClientState = GPA( "glDisableClientState" ); - qglDrawArrays = dllDrawArrays = GPA( "glDrawArrays" ); - qglDrawBuffer = dllDrawBuffer = GPA( "glDrawBuffer" ); - qglDrawElements = dllDrawElements = GPA( "glDrawElements" ); - qglDrawPixels = dllDrawPixels = GPA( "glDrawPixels" ); - qglEdgeFlag = dllEdgeFlag = GPA( "glEdgeFlag" ); - qglEdgeFlagPointer = dllEdgeFlagPointer = GPA( "glEdgeFlagPointer" ); - qglEdgeFlagv = dllEdgeFlagv = GPA( "glEdgeFlagv" ); - qglEnable = dllEnable = GPA( "glEnable" ); - qglEnableClientState = dllEnableClientState = GPA( "glEnableClientState" ); - qglEnd = dllEnd = GPA( "glEnd" ); - qglEndList = dllEndList = GPA( "glEndList" ); - qglEvalCoord1d = dllEvalCoord1d = GPA( "glEvalCoord1d" ); - qglEvalCoord1dv = dllEvalCoord1dv = GPA( "glEvalCoord1dv" ); - qglEvalCoord1f = dllEvalCoord1f = GPA( "glEvalCoord1f" ); - qglEvalCoord1fv = dllEvalCoord1fv = GPA( "glEvalCoord1fv" ); - qglEvalCoord2d = dllEvalCoord2d = GPA( "glEvalCoord2d" ); - qglEvalCoord2dv = dllEvalCoord2dv = GPA( "glEvalCoord2dv" ); - qglEvalCoord2f = dllEvalCoord2f = GPA( "glEvalCoord2f" ); - qglEvalCoord2fv = dllEvalCoord2fv = GPA( "glEvalCoord2fv" ); - qglEvalMesh1 = dllEvalMesh1 = GPA( "glEvalMesh1" ); - qglEvalMesh2 = dllEvalMesh2 = GPA( "glEvalMesh2" ); - qglEvalPoint1 = dllEvalPoint1 = GPA( "glEvalPoint1" ); - qglEvalPoint2 = dllEvalPoint2 = GPA( "glEvalPoint2" ); - qglFeedbackBuffer = dllFeedbackBuffer = GPA( "glFeedbackBuffer" ); - qglFinish = dllFinish = GPA( "glFinish" ); - qglFlush = dllFlush = GPA( "glFlush" ); - qglFogf = dllFogf = GPA( "glFogf" ); - qglFogfv = dllFogfv = GPA( "glFogfv" ); - qglFogi = dllFogi = GPA( "glFogi" ); - qglFogiv = dllFogiv = GPA( "glFogiv" ); - qglFrontFace = dllFrontFace = GPA( "glFrontFace" ); - qglFrustum = dllFrustum = GPA( "glFrustum" ); - qglGenLists = dllGenLists = GPA( "glGenLists" ); - qglGenTextures = dllGenTextures = GPA( "glGenTextures" ); - qglGetBooleanv = dllGetBooleanv = GPA( "glGetBooleanv" ); - qglGetClipPlane = dllGetClipPlane = GPA( "glGetClipPlane" ); - qglGetDoublev = dllGetDoublev = GPA( "glGetDoublev" ); - qglGetError = dllGetError = GPA( "glGetError" ); - qglGetFloatv = dllGetFloatv = GPA( "glGetFloatv" ); - qglGetIntegerv = dllGetIntegerv = GPA( "glGetIntegerv" ); - qglGetLightfv = dllGetLightfv = GPA( "glGetLightfv" ); - qglGetLightiv = dllGetLightiv = GPA( "glGetLightiv" ); - qglGetMapdv = dllGetMapdv = GPA( "glGetMapdv" ); - qglGetMapfv = dllGetMapfv = GPA( "glGetMapfv" ); - qglGetMapiv = dllGetMapiv = GPA( "glGetMapiv" ); - qglGetMaterialfv = dllGetMaterialfv = GPA( "glGetMaterialfv" ); - qglGetMaterialiv = dllGetMaterialiv = GPA( "glGetMaterialiv" ); - qglGetPixelMapfv = dllGetPixelMapfv = GPA( "glGetPixelMapfv" ); - qglGetPixelMapuiv = dllGetPixelMapuiv = GPA( "glGetPixelMapuiv" ); - qglGetPixelMapusv = dllGetPixelMapusv = GPA( "glGetPixelMapusv" ); - qglGetPointerv = dllGetPointerv = GPA( "glGetPointerv" ); - qglGetPolygonStipple = dllGetPolygonStipple = GPA( "glGetPolygonStipple" ); - qglGetString = dllGetString = GPA( "glGetString" ); - qglGetTexEnvfv = dllGetTexEnvfv = GPA( "glGetTexEnvfv" ); - qglGetTexEnviv = dllGetTexEnviv = GPA( "glGetTexEnviv" ); - qglGetTexGendv = dllGetTexGendv = GPA( "glGetTexGendv" ); - qglGetTexGenfv = dllGetTexGenfv = GPA( "glGetTexGenfv" ); - qglGetTexGeniv = dllGetTexGeniv = GPA( "glGetTexGeniv" ); - qglGetTexImage = dllGetTexImage = GPA( "glGetTexImage" ); - qglGetTexLevelParameterfv = dllGetTexLevelParameterfv = GPA( "glGetLevelParameterfv" ); - qglGetTexLevelParameteriv = dllGetTexLevelParameteriv = GPA( "glGetLevelParameteriv" ); - qglGetTexParameterfv = dllGetTexParameterfv = GPA( "glGetTexParameterfv" ); - qglGetTexParameteriv = dllGetTexParameteriv = GPA( "glGetTexParameteriv" ); - qglHint = dllHint = GPA( "glHint" ); - qglIndexMask = dllIndexMask = GPA( "glIndexMask" ); - qglIndexPointer = dllIndexPointer = GPA( "glIndexPointer" ); - qglIndexd = dllIndexd = GPA( "glIndexd" ); - qglIndexdv = dllIndexdv = GPA( "glIndexdv" ); - qglIndexf = dllIndexf = GPA( "glIndexf" ); - qglIndexfv = dllIndexfv = GPA( "glIndexfv" ); - qglIndexi = dllIndexi = GPA( "glIndexi" ); - qglIndexiv = dllIndexiv = GPA( "glIndexiv" ); - qglIndexs = dllIndexs = GPA( "glIndexs" ); - qglIndexsv = dllIndexsv = GPA( "glIndexsv" ); - qglIndexub = dllIndexub = GPA( "glIndexub" ); - qglIndexubv = dllIndexubv = GPA( "glIndexubv" ); - qglInitNames = dllInitNames = GPA( "glInitNames" ); - qglInterleavedArrays = dllInterleavedArrays = GPA( "glInterleavedArrays" ); - qglIsEnabled = dllIsEnabled = GPA( "glIsEnabled" ); - qglIsList = dllIsList = GPA( "glIsList" ); - qglIsTexture = dllIsTexture = GPA( "glIsTexture" ); - qglLightModelf = dllLightModelf = GPA( "glLightModelf" ); - qglLightModelfv = dllLightModelfv = GPA( "glLightModelfv" ); - qglLightModeli = dllLightModeli = GPA( "glLightModeli" ); - qglLightModeliv = dllLightModeliv = GPA( "glLightModeliv" ); - qglLightf = dllLightf = GPA( "glLightf" ); - qglLightfv = dllLightfv = GPA( "glLightfv" ); - qglLighti = dllLighti = GPA( "glLighti" ); - qglLightiv = dllLightiv = GPA( "glLightiv" ); - qglLineStipple = dllLineStipple = GPA( "glLineStipple" ); - qglLineWidth = dllLineWidth = GPA( "glLineWidth" ); - qglListBase = dllListBase = GPA( "glListBase" ); - qglLoadIdentity = dllLoadIdentity = GPA( "glLoadIdentity" ); - qglLoadMatrixd = dllLoadMatrixd = GPA( "glLoadMatrixd" ); - qglLoadMatrixf = dllLoadMatrixf = GPA( "glLoadMatrixf" ); - qglLoadName = dllLoadName = GPA( "glLoadName" ); - qglLogicOp = dllLogicOp = GPA( "glLogicOp" ); - qglMap1d = dllMap1d = GPA( "glMap1d" ); - qglMap1f = dllMap1f = GPA( "glMap1f" ); - qglMap2d = dllMap2d = GPA( "glMap2d" ); - qglMap2f = dllMap2f = GPA( "glMap2f" ); - qglMapGrid1d = dllMapGrid1d = GPA( "glMapGrid1d" ); - qglMapGrid1f = dllMapGrid1f = GPA( "glMapGrid1f" ); - qglMapGrid2d = dllMapGrid2d = GPA( "glMapGrid2d" ); - qglMapGrid2f = dllMapGrid2f = GPA( "glMapGrid2f" ); - qglMaterialf = dllMaterialf = GPA( "glMaterialf" ); - qglMaterialfv = dllMaterialfv = GPA( "glMaterialfv" ); - qglMateriali = dllMateriali = GPA( "glMateriali" ); - qglMaterialiv = dllMaterialiv = GPA( "glMaterialiv" ); - qglMatrixMode = dllMatrixMode = GPA( "glMatrixMode" ); - qglMultMatrixd = dllMultMatrixd = GPA( "glMultMatrixd" ); - qglMultMatrixf = dllMultMatrixf = GPA( "glMultMatrixf" ); - qglNewList = dllNewList = GPA( "glNewList" ); - qglNormal3b = dllNormal3b = GPA( "glNormal3b" ); - qglNormal3bv = dllNormal3bv = GPA( "glNormal3bv" ); - qglNormal3d = dllNormal3d = GPA( "glNormal3d" ); - qglNormal3dv = dllNormal3dv = GPA( "glNormal3dv" ); - qglNormal3f = dllNormal3f = GPA( "glNormal3f" ); - qglNormal3fv = dllNormal3fv = GPA( "glNormal3fv" ); - qglNormal3i = dllNormal3i = GPA( "glNormal3i" ); - qglNormal3iv = dllNormal3iv = GPA( "glNormal3iv" ); - qglNormal3s = dllNormal3s = GPA( "glNormal3s" ); - qglNormal3sv = dllNormal3sv = GPA( "glNormal3sv" ); - qglNormalPointer = dllNormalPointer = GPA( "glNormalPointer" ); - qglOrtho = dllOrtho = GPA( "glOrtho" ); - qglPassThrough = dllPassThrough = GPA( "glPassThrough" ); - qglPixelMapfv = dllPixelMapfv = GPA( "glPixelMapfv" ); - qglPixelMapuiv = dllPixelMapuiv = GPA( "glPixelMapuiv" ); - qglPixelMapusv = dllPixelMapusv = GPA( "glPixelMapusv" ); - qglPixelStoref = dllPixelStoref = GPA( "glPixelStoref" ); - qglPixelStorei = dllPixelStorei = GPA( "glPixelStorei" ); - qglPixelTransferf = dllPixelTransferf = GPA( "glPixelTransferf" ); - qglPixelTransferi = dllPixelTransferi = GPA( "glPixelTransferi" ); - qglPixelZoom = dllPixelZoom = GPA( "glPixelZoom" ); - qglPointSize = dllPointSize = GPA( "glPointSize" ); - qglPolygonMode = dllPolygonMode = GPA( "glPolygonMode" ); - qglPolygonOffset = dllPolygonOffset = GPA( "glPolygonOffset" ); - qglPolygonStipple = dllPolygonStipple = GPA( "glPolygonStipple" ); - qglPopAttrib = dllPopAttrib = GPA( "glPopAttrib" ); - qglPopClientAttrib = dllPopClientAttrib = GPA( "glPopClientAttrib" ); - qglPopMatrix = dllPopMatrix = GPA( "glPopMatrix" ); - qglPopName = dllPopName = GPA( "glPopName" ); - qglPrioritizeTextures = dllPrioritizeTextures = GPA( "glPrioritizeTextures" ); - qglPushAttrib = dllPushAttrib = GPA( "glPushAttrib" ); - qglPushClientAttrib = dllPushClientAttrib = GPA( "glPushClientAttrib" ); - qglPushMatrix = dllPushMatrix = GPA( "glPushMatrix" ); - qglPushName = dllPushName = GPA( "glPushName" ); - qglRasterPos2d = dllRasterPos2d = GPA( "glRasterPos2d" ); - qglRasterPos2dv = dllRasterPos2dv = GPA( "glRasterPos2dv" ); - qglRasterPos2f = dllRasterPos2f = GPA( "glRasterPos2f" ); - qglRasterPos2fv = dllRasterPos2fv = GPA( "glRasterPos2fv" ); - qglRasterPos2i = dllRasterPos2i = GPA( "glRasterPos2i" ); - qglRasterPos2iv = dllRasterPos2iv = GPA( "glRasterPos2iv" ); - qglRasterPos2s = dllRasterPos2s = GPA( "glRasterPos2s" ); - qglRasterPos2sv = dllRasterPos2sv = GPA( "glRasterPos2sv" ); - qglRasterPos3d = dllRasterPos3d = GPA( "glRasterPos3d" ); - qglRasterPos3dv = dllRasterPos3dv = GPA( "glRasterPos3dv" ); - qglRasterPos3f = dllRasterPos3f = GPA( "glRasterPos3f" ); - qglRasterPos3fv = dllRasterPos3fv = GPA( "glRasterPos3fv" ); - qglRasterPos3i = dllRasterPos3i = GPA( "glRasterPos3i" ); - qglRasterPos3iv = dllRasterPos3iv = GPA( "glRasterPos3iv" ); - qglRasterPos3s = dllRasterPos3s = GPA( "glRasterPos3s" ); - qglRasterPos3sv = dllRasterPos3sv = GPA( "glRasterPos3sv" ); - qglRasterPos4d = dllRasterPos4d = GPA( "glRasterPos4d" ); - qglRasterPos4dv = dllRasterPos4dv = GPA( "glRasterPos4dv" ); - qglRasterPos4f = dllRasterPos4f = GPA( "glRasterPos4f" ); - qglRasterPos4fv = dllRasterPos4fv = GPA( "glRasterPos4fv" ); - qglRasterPos4i = dllRasterPos4i = GPA( "glRasterPos4i" ); - qglRasterPos4iv = dllRasterPos4iv = GPA( "glRasterPos4iv" ); - qglRasterPos4s = dllRasterPos4s = GPA( "glRasterPos4s" ); - qglRasterPos4sv = dllRasterPos4sv = GPA( "glRasterPos4sv" ); - qglReadBuffer = dllReadBuffer = GPA( "glReadBuffer" ); - qglReadPixels = dllReadPixels = GPA( "glReadPixels" ); - qglRectd = dllRectd = GPA( "glRectd" ); - qglRectdv = dllRectdv = GPA( "glRectdv" ); - qglRectf = dllRectf = GPA( "glRectf" ); - qglRectfv = dllRectfv = GPA( "glRectfv" ); - qglRecti = dllRecti = GPA( "glRecti" ); - qglRectiv = dllRectiv = GPA( "glRectiv" ); - qglRects = dllRects = GPA( "glRects" ); - qglRectsv = dllRectsv = GPA( "glRectsv" ); - qglRenderMode = dllRenderMode = GPA( "glRenderMode" ); - qglRotated = dllRotated = GPA( "glRotated" ); - qglRotatef = dllRotatef = GPA( "glRotatef" ); - qglScaled = dllScaled = GPA( "glScaled" ); - qglScalef = dllScalef = GPA( "glScalef" ); - qglScissor = dllScissor = GPA( "glScissor" ); - qglSelectBuffer = dllSelectBuffer = GPA( "glSelectBuffer" ); - qglShadeModel = dllShadeModel = GPA( "glShadeModel" ); - qglStencilFunc = dllStencilFunc = GPA( "glStencilFunc" ); - qglStencilMask = dllStencilMask = GPA( "glStencilMask" ); - qglStencilOp = dllStencilOp = GPA( "glStencilOp" ); - qglTexCoord1d = dllTexCoord1d = GPA( "glTexCoord1d" ); - qglTexCoord1dv = dllTexCoord1dv = GPA( "glTexCoord1dv" ); - qglTexCoord1f = dllTexCoord1f = GPA( "glTexCoord1f" ); - qglTexCoord1fv = dllTexCoord1fv = GPA( "glTexCoord1fv" ); - qglTexCoord1i = dllTexCoord1i = GPA( "glTexCoord1i" ); - qglTexCoord1iv = dllTexCoord1iv = GPA( "glTexCoord1iv" ); - qglTexCoord1s = dllTexCoord1s = GPA( "glTexCoord1s" ); - qglTexCoord1sv = dllTexCoord1sv = GPA( "glTexCoord1sv" ); - qglTexCoord2d = dllTexCoord2d = GPA( "glTexCoord2d" ); - qglTexCoord2dv = dllTexCoord2dv = GPA( "glTexCoord2dv" ); - qglTexCoord2f = dllTexCoord2f = GPA( "glTexCoord2f" ); - qglTexCoord2fv = dllTexCoord2fv = GPA( "glTexCoord2fv" ); - qglTexCoord2i = dllTexCoord2i = GPA( "glTexCoord2i" ); - qglTexCoord2iv = dllTexCoord2iv = GPA( "glTexCoord2iv" ); - qglTexCoord2s = dllTexCoord2s = GPA( "glTexCoord2s" ); - qglTexCoord2sv = dllTexCoord2sv = GPA( "glTexCoord2sv" ); - qglTexCoord3d = dllTexCoord3d = GPA( "glTexCoord3d" ); - qglTexCoord3dv = dllTexCoord3dv = GPA( "glTexCoord3dv" ); - qglTexCoord3f = dllTexCoord3f = GPA( "glTexCoord3f" ); - qglTexCoord3fv = dllTexCoord3fv = GPA( "glTexCoord3fv" ); - qglTexCoord3i = dllTexCoord3i = GPA( "glTexCoord3i" ); - qglTexCoord3iv = dllTexCoord3iv = GPA( "glTexCoord3iv" ); - qglTexCoord3s = dllTexCoord3s = GPA( "glTexCoord3s" ); - qglTexCoord3sv = dllTexCoord3sv = GPA( "glTexCoord3sv" ); - qglTexCoord4d = dllTexCoord4d = GPA( "glTexCoord4d" ); - qglTexCoord4dv = dllTexCoord4dv = GPA( "glTexCoord4dv" ); - qglTexCoord4f = dllTexCoord4f = GPA( "glTexCoord4f" ); - qglTexCoord4fv = dllTexCoord4fv = GPA( "glTexCoord4fv" ); - qglTexCoord4i = dllTexCoord4i = GPA( "glTexCoord4i" ); - qglTexCoord4iv = dllTexCoord4iv = GPA( "glTexCoord4iv" ); - qglTexCoord4s = dllTexCoord4s = GPA( "glTexCoord4s" ); - qglTexCoord4sv = dllTexCoord4sv = GPA( "glTexCoord4sv" ); - qglTexCoordPointer = dllTexCoordPointer = GPA( "glTexCoordPointer" ); - qglTexEnvf = dllTexEnvf = GPA( "glTexEnvf" ); - qglTexEnvfv = dllTexEnvfv = GPA( "glTexEnvfv" ); - qglTexEnvi = dllTexEnvi = GPA( "glTexEnvi" ); - qglTexEnviv = dllTexEnviv = GPA( "glTexEnviv" ); - qglTexGend = dllTexGend = GPA( "glTexGend" ); - qglTexGendv = dllTexGendv = GPA( "glTexGendv" ); - qglTexGenf = dllTexGenf = GPA( "glTexGenf" ); - qglTexGenfv = dllTexGenfv = GPA( "glTexGenfv" ); - qglTexGeni = dllTexGeni = GPA( "glTexGeni" ); - qglTexGeniv = dllTexGeniv = GPA( "glTexGeniv" ); - qglTexImage1D = dllTexImage1D = GPA( "glTexImage1D" ); - qglTexImage2D = dllTexImage2D = GPA( "glTexImage2D" ); - qglTexParameterf = dllTexParameterf = GPA( "glTexParameterf" ); - qglTexParameterfv = dllTexParameterfv = GPA( "glTexParameterfv" ); - qglTexParameteri = dllTexParameteri = GPA( "glTexParameteri" ); - qglTexParameteriv = dllTexParameteriv = GPA( "glTexParameteriv" ); - qglTexSubImage1D = dllTexSubImage1D = GPA( "glTexSubImage1D" ); - qglTexSubImage2D = dllTexSubImage2D = GPA( "glTexSubImage2D" ); - qglTranslated = dllTranslated = GPA( "glTranslated" ); - qglTranslatef = dllTranslatef = GPA( "glTranslatef" ); - qglVertex2d = dllVertex2d = GPA( "glVertex2d" ); - qglVertex2dv = dllVertex2dv = GPA( "glVertex2dv" ); - qglVertex2f = dllVertex2f = GPA( "glVertex2f" ); - qglVertex2fv = dllVertex2fv = GPA( "glVertex2fv" ); - qglVertex2i = dllVertex2i = GPA( "glVertex2i" ); - qglVertex2iv = dllVertex2iv = GPA( "glVertex2iv" ); - qglVertex2s = dllVertex2s = GPA( "glVertex2s" ); - qglVertex2sv = dllVertex2sv = GPA( "glVertex2sv" ); - qglVertex3d = dllVertex3d = GPA( "glVertex3d" ); - qglVertex3dv = dllVertex3dv = GPA( "glVertex3dv" ); - qglVertex3f = dllVertex3f = GPA( "glVertex3f" ); - qglVertex3fv = dllVertex3fv = GPA( "glVertex3fv" ); - qglVertex3i = dllVertex3i = GPA( "glVertex3i" ); - qglVertex3iv = dllVertex3iv = GPA( "glVertex3iv" ); - qglVertex3s = dllVertex3s = GPA( "glVertex3s" ); - qglVertex3sv = dllVertex3sv = GPA( "glVertex3sv" ); - qglVertex4d = dllVertex4d = GPA( "glVertex4d" ); - qglVertex4dv = dllVertex4dv = GPA( "glVertex4dv" ); - qglVertex4f = dllVertex4f = GPA( "glVertex4f" ); - qglVertex4fv = dllVertex4fv = GPA( "glVertex4fv" ); - qglVertex4i = dllVertex4i = GPA( "glVertex4i" ); - qglVertex4iv = dllVertex4iv = GPA( "glVertex4iv" ); - qglVertex4s = dllVertex4s = GPA( "glVertex4s" ); - qglVertex4sv = dllVertex4sv = GPA( "glVertex4sv" ); - qglVertexPointer = dllVertexPointer = GPA( "glVertexPointer" ); - qglViewport = dllViewport = GPA( "glViewport" ); - - qfxMesaCreateContext = GPA("fxMesaCreateContext"); - qfxMesaCreateBestContext = GPA("fxMesaCreateBestContext"); - qfxMesaDestroyContext = GPA("fxMesaDestroyContext"); - qfxMesaMakeCurrent = GPA("fxMesaMakeCurrent"); - qfxMesaGetCurrentContext = GPA("fxMesaGetCurrentContext"); - qfxMesaSwapBuffers = GPA("fxMesaSwapBuffers"); - - qglXChooseVisual = GPA("glXChooseVisual"); - qglXCreateContext = GPA("glXCreateContext"); - qglXDestroyContext = GPA("glXDestroyContext"); - qglXMakeCurrent = GPA("glXMakeCurrent"); - qglXCopyContext = GPA("glXCopyContext"); - qglXSwapBuffers = GPA("glXSwapBuffers"); - - qglLockArraysEXT = 0; - qglUnlockArraysEXT = 0; - qglPointParameterfEXT = 0; - qglPointParameterfvEXT = 0; - qglColorTableEXT = 0; - qgl3DfxSetPaletteEXT = 0; - qglSelectTextureSGIS = 0; - qglMTexCoord2fSGIS = 0; - qglActiveTextureARB = 0; - qglClientActiveTextureARB = 0; - qglMultiTexCoord2fARB = 0; - - return qtrue; -} - -void QGL_EnableLogging( qboolean enable ) -{ - if ( enable ) - { - if ( !glw_state.log_fp ) - { - struct tm *newtime; - time_t aclock; - char buffer[1024]; - cvar_t *basedir; - - time( &aclock ); - newtime = localtime( &aclock ); - - asctime( newtime ); - - basedir = ri.Cvar_Get( "basedir", "", 0 ); - Com_sprintf( buffer, sizeof(buffer), "%s/gl.log", basedir->string ); - glw_state.log_fp = fopen( buffer, "wt" ); - - fprintf( glw_state.log_fp, "%s\n", asctime( newtime ) ); - } - - qglAccum = logAccum; - qglAlphaFunc = logAlphaFunc; - qglAreTexturesResident = logAreTexturesResident; - qglArrayElement = logArrayElement; - qglBegin = logBegin; - qglBindTexture = logBindTexture; - qglBitmap = logBitmap; - qglBlendFunc = logBlendFunc; - qglCallList = logCallList; - qglCallLists = logCallLists; - qglClear = logClear; - qglClearAccum = logClearAccum; - qglClearColor = logClearColor; - qglClearDepth = logClearDepth; - qglClearIndex = logClearIndex; - qglClearStencil = logClearStencil; - qglClipPlane = logClipPlane; - qglColor3b = logColor3b; - qglColor3bv = logColor3bv; - qglColor3d = logColor3d; - qglColor3dv = logColor3dv; - qglColor3f = logColor3f; - qglColor3fv = logColor3fv; - qglColor3i = logColor3i; - qglColor3iv = logColor3iv; - qglColor3s = logColor3s; - qglColor3sv = logColor3sv; - qglColor3ub = logColor3ub; - qglColor3ubv = logColor3ubv; - qglColor3ui = logColor3ui; - qglColor3uiv = logColor3uiv; - qglColor3us = logColor3us; - qglColor3usv = logColor3usv; - qglColor4b = logColor4b; - qglColor4bv = logColor4bv; - qglColor4d = logColor4d; - qglColor4dv = logColor4dv; - qglColor4f = logColor4f; - qglColor4fv = logColor4fv; - qglColor4i = logColor4i; - qglColor4iv = logColor4iv; - qglColor4s = logColor4s; - qglColor4sv = logColor4sv; - qglColor4ub = logColor4ub; - qglColor4ubv = logColor4ubv; - qglColor4ui = logColor4ui; - qglColor4uiv = logColor4uiv; - qglColor4us = logColor4us; - qglColor4usv = logColor4usv; - qglColorMask = logColorMask; - qglColorMaterial = logColorMaterial; - qglColorPointer = logColorPointer; - qglCopyPixels = logCopyPixels; - qglCopyTexImage1D = logCopyTexImage1D; - qglCopyTexImage2D = logCopyTexImage2D; - qglCopyTexSubImage1D = logCopyTexSubImage1D; - qglCopyTexSubImage2D = logCopyTexSubImage2D; - qglCullFace = logCullFace; - qglDeleteLists = logDeleteLists ; - qglDeleteTextures = logDeleteTextures ; - qglDepthFunc = logDepthFunc ; - qglDepthMask = logDepthMask ; - qglDepthRange = logDepthRange ; - qglDisable = logDisable ; - qglDisableClientState = logDisableClientState ; - qglDrawArrays = logDrawArrays ; - qglDrawBuffer = logDrawBuffer ; - qglDrawElements = logDrawElements ; - qglDrawPixels = logDrawPixels ; - qglEdgeFlag = logEdgeFlag ; - qglEdgeFlagPointer = logEdgeFlagPointer ; - qglEdgeFlagv = logEdgeFlagv ; - qglEnable = logEnable ; - qglEnableClientState = logEnableClientState ; - qglEnd = logEnd ; - qglEndList = logEndList ; - qglEvalCoord1d = logEvalCoord1d ; - qglEvalCoord1dv = logEvalCoord1dv ; - qglEvalCoord1f = logEvalCoord1f ; - qglEvalCoord1fv = logEvalCoord1fv ; - qglEvalCoord2d = logEvalCoord2d ; - qglEvalCoord2dv = logEvalCoord2dv ; - qglEvalCoord2f = logEvalCoord2f ; - qglEvalCoord2fv = logEvalCoord2fv ; - qglEvalMesh1 = logEvalMesh1 ; - qglEvalMesh2 = logEvalMesh2 ; - qglEvalPoint1 = logEvalPoint1 ; - qglEvalPoint2 = logEvalPoint2 ; - qglFeedbackBuffer = logFeedbackBuffer ; - qglFinish = logFinish ; - qglFlush = logFlush ; - qglFogf = logFogf ; - qglFogfv = logFogfv ; - qglFogi = logFogi ; - qglFogiv = logFogiv ; - qglFrontFace = logFrontFace ; - qglFrustum = logFrustum ; - qglGenLists = logGenLists ; - qglGenTextures = logGenTextures ; - qglGetBooleanv = logGetBooleanv ; - qglGetClipPlane = logGetClipPlane ; - qglGetDoublev = logGetDoublev ; - qglGetError = logGetError ; - qglGetFloatv = logGetFloatv ; - qglGetIntegerv = logGetIntegerv ; - qglGetLightfv = logGetLightfv ; - qglGetLightiv = logGetLightiv ; - qglGetMapdv = logGetMapdv ; - qglGetMapfv = logGetMapfv ; - qglGetMapiv = logGetMapiv ; - qglGetMaterialfv = logGetMaterialfv ; - qglGetMaterialiv = logGetMaterialiv ; - qglGetPixelMapfv = logGetPixelMapfv ; - qglGetPixelMapuiv = logGetPixelMapuiv ; - qglGetPixelMapusv = logGetPixelMapusv ; - qglGetPointerv = logGetPointerv ; - qglGetPolygonStipple = logGetPolygonStipple ; - qglGetString = logGetString ; - qglGetTexEnvfv = logGetTexEnvfv ; - qglGetTexEnviv = logGetTexEnviv ; - qglGetTexGendv = logGetTexGendv ; - qglGetTexGenfv = logGetTexGenfv ; - qglGetTexGeniv = logGetTexGeniv ; - qglGetTexImage = logGetTexImage ; - qglGetTexLevelParameterfv = logGetTexLevelParameterfv ; - qglGetTexLevelParameteriv = logGetTexLevelParameteriv ; - qglGetTexParameterfv = logGetTexParameterfv ; - qglGetTexParameteriv = logGetTexParameteriv ; - qglHint = logHint ; - qglIndexMask = logIndexMask ; - qglIndexPointer = logIndexPointer ; - qglIndexd = logIndexd ; - qglIndexdv = logIndexdv ; - qglIndexf = logIndexf ; - qglIndexfv = logIndexfv ; - qglIndexi = logIndexi ; - qglIndexiv = logIndexiv ; - qglIndexs = logIndexs ; - qglIndexsv = logIndexsv ; - qglIndexub = logIndexub ; - qglIndexubv = logIndexubv ; - qglInitNames = logInitNames ; - qglInterleavedArrays = logInterleavedArrays ; - qglIsEnabled = logIsEnabled ; - qglIsList = logIsList ; - qglIsTexture = logIsTexture ; - qglLightModelf = logLightModelf ; - qglLightModelfv = logLightModelfv ; - qglLightModeli = logLightModeli ; - qglLightModeliv = logLightModeliv ; - qglLightf = logLightf ; - qglLightfv = logLightfv ; - qglLighti = logLighti ; - qglLightiv = logLightiv ; - qglLineStipple = logLineStipple ; - qglLineWidth = logLineWidth ; - qglListBase = logListBase ; - qglLoadIdentity = logLoadIdentity ; - qglLoadMatrixd = logLoadMatrixd ; - qglLoadMatrixf = logLoadMatrixf ; - qglLoadName = logLoadName ; - qglLogicOp = logLogicOp ; - qglMap1d = logMap1d ; - qglMap1f = logMap1f ; - qglMap2d = logMap2d ; - qglMap2f = logMap2f ; - qglMapGrid1d = logMapGrid1d ; - qglMapGrid1f = logMapGrid1f ; - qglMapGrid2d = logMapGrid2d ; - qglMapGrid2f = logMapGrid2f ; - qglMaterialf = logMaterialf ; - qglMaterialfv = logMaterialfv ; - qglMateriali = logMateriali ; - qglMaterialiv = logMaterialiv ; - qglMatrixMode = logMatrixMode ; - qglMultMatrixd = logMultMatrixd ; - qglMultMatrixf = logMultMatrixf ; - qglNewList = logNewList ; - qglNormal3b = logNormal3b ; - qglNormal3bv = logNormal3bv ; - qglNormal3d = logNormal3d ; - qglNormal3dv = logNormal3dv ; - qglNormal3f = logNormal3f ; - qglNormal3fv = logNormal3fv ; - qglNormal3i = logNormal3i ; - qglNormal3iv = logNormal3iv ; - qglNormal3s = logNormal3s ; - qglNormal3sv = logNormal3sv ; - qglNormalPointer = logNormalPointer ; - qglOrtho = logOrtho ; - qglPassThrough = logPassThrough ; - qglPixelMapfv = logPixelMapfv ; - qglPixelMapuiv = logPixelMapuiv ; - qglPixelMapusv = logPixelMapusv ; - qglPixelStoref = logPixelStoref ; - qglPixelStorei = logPixelStorei ; - qglPixelTransferf = logPixelTransferf ; - qglPixelTransferi = logPixelTransferi ; - qglPixelZoom = logPixelZoom ; - qglPointSize = logPointSize ; - qglPolygonMode = logPolygonMode ; - qglPolygonOffset = logPolygonOffset ; - qglPolygonStipple = logPolygonStipple ; - qglPopAttrib = logPopAttrib ; - qglPopClientAttrib = logPopClientAttrib ; - qglPopMatrix = logPopMatrix ; - qglPopName = logPopName ; - qglPrioritizeTextures = logPrioritizeTextures ; - qglPushAttrib = logPushAttrib ; - qglPushClientAttrib = logPushClientAttrib ; - qglPushMatrix = logPushMatrix ; - qglPushName = logPushName ; - qglRasterPos2d = logRasterPos2d ; - qglRasterPos2dv = logRasterPos2dv ; - qglRasterPos2f = logRasterPos2f ; - qglRasterPos2fv = logRasterPos2fv ; - qglRasterPos2i = logRasterPos2i ; - qglRasterPos2iv = logRasterPos2iv ; - qglRasterPos2s = logRasterPos2s ; - qglRasterPos2sv = logRasterPos2sv ; - qglRasterPos3d = logRasterPos3d ; - qglRasterPos3dv = logRasterPos3dv ; - qglRasterPos3f = logRasterPos3f ; - qglRasterPos3fv = logRasterPos3fv ; - qglRasterPos3i = logRasterPos3i ; - qglRasterPos3iv = logRasterPos3iv ; - qglRasterPos3s = logRasterPos3s ; - qglRasterPos3sv = logRasterPos3sv ; - qglRasterPos4d = logRasterPos4d ; - qglRasterPos4dv = logRasterPos4dv ; - qglRasterPos4f = logRasterPos4f ; - qglRasterPos4fv = logRasterPos4fv ; - qglRasterPos4i = logRasterPos4i ; - qglRasterPos4iv = logRasterPos4iv ; - qglRasterPos4s = logRasterPos4s ; - qglRasterPos4sv = logRasterPos4sv ; - qglReadBuffer = logReadBuffer ; - qglReadPixels = logReadPixels ; - qglRectd = logRectd ; - qglRectdv = logRectdv ; - qglRectf = logRectf ; - qglRectfv = logRectfv ; - qglRecti = logRecti ; - qglRectiv = logRectiv ; - qglRects = logRects ; - qglRectsv = logRectsv ; - qglRenderMode = logRenderMode ; - qglRotated = logRotated ; - qglRotatef = logRotatef ; - qglScaled = logScaled ; - qglScalef = logScalef ; - qglScissor = logScissor ; - qglSelectBuffer = logSelectBuffer ; - qglShadeModel = logShadeModel ; - qglStencilFunc = logStencilFunc ; - qglStencilMask = logStencilMask ; - qglStencilOp = logStencilOp ; - qglTexCoord1d = logTexCoord1d ; - qglTexCoord1dv = logTexCoord1dv ; - qglTexCoord1f = logTexCoord1f ; - qglTexCoord1fv = logTexCoord1fv ; - qglTexCoord1i = logTexCoord1i ; - qglTexCoord1iv = logTexCoord1iv ; - qglTexCoord1s = logTexCoord1s ; - qglTexCoord1sv = logTexCoord1sv ; - qglTexCoord2d = logTexCoord2d ; - qglTexCoord2dv = logTexCoord2dv ; - qglTexCoord2f = logTexCoord2f ; - qglTexCoord2fv = logTexCoord2fv ; - qglTexCoord2i = logTexCoord2i ; - qglTexCoord2iv = logTexCoord2iv ; - qglTexCoord2s = logTexCoord2s ; - qglTexCoord2sv = logTexCoord2sv ; - qglTexCoord3d = logTexCoord3d ; - qglTexCoord3dv = logTexCoord3dv ; - qglTexCoord3f = logTexCoord3f ; - qglTexCoord3fv = logTexCoord3fv ; - qglTexCoord3i = logTexCoord3i ; - qglTexCoord3iv = logTexCoord3iv ; - qglTexCoord3s = logTexCoord3s ; - qglTexCoord3sv = logTexCoord3sv ; - qglTexCoord4d = logTexCoord4d ; - qglTexCoord4dv = logTexCoord4dv ; - qglTexCoord4f = logTexCoord4f ; - qglTexCoord4fv = logTexCoord4fv ; - qglTexCoord4i = logTexCoord4i ; - qglTexCoord4iv = logTexCoord4iv ; - qglTexCoord4s = logTexCoord4s ; - qglTexCoord4sv = logTexCoord4sv ; - qglTexCoordPointer = logTexCoordPointer ; - qglTexEnvf = logTexEnvf ; - qglTexEnvfv = logTexEnvfv ; - qglTexEnvi = logTexEnvi ; - qglTexEnviv = logTexEnviv ; - qglTexGend = logTexGend ; - qglTexGendv = logTexGendv ; - qglTexGenf = logTexGenf ; - qglTexGenfv = logTexGenfv ; - qglTexGeni = logTexGeni ; - qglTexGeniv = logTexGeniv ; - qglTexImage1D = logTexImage1D ; - qglTexImage2D = logTexImage2D ; - qglTexParameterf = logTexParameterf ; - qglTexParameterfv = logTexParameterfv ; - qglTexParameteri = logTexParameteri ; - qglTexParameteriv = logTexParameteriv ; - qglTexSubImage1D = logTexSubImage1D ; - qglTexSubImage2D = logTexSubImage2D ; - qglTranslated = logTranslated ; - qglTranslatef = logTranslatef ; - qglVertex2d = logVertex2d ; - qglVertex2dv = logVertex2dv ; - qglVertex2f = logVertex2f ; - qglVertex2fv = logVertex2fv ; - qglVertex2i = logVertex2i ; - qglVertex2iv = logVertex2iv ; - qglVertex2s = logVertex2s ; - qglVertex2sv = logVertex2sv ; - qglVertex3d = logVertex3d ; - qglVertex3dv = logVertex3dv ; - qglVertex3f = logVertex3f ; - qglVertex3fv = logVertex3fv ; - qglVertex3i = logVertex3i ; - qglVertex3iv = logVertex3iv ; - qglVertex3s = logVertex3s ; - qglVertex3sv = logVertex3sv ; - qglVertex4d = logVertex4d ; - qglVertex4dv = logVertex4dv ; - qglVertex4f = logVertex4f ; - qglVertex4fv = logVertex4fv ; - qglVertex4i = logVertex4i ; - qglVertex4iv = logVertex4iv ; - qglVertex4s = logVertex4s ; - qglVertex4sv = logVertex4sv ; - qglVertexPointer = logVertexPointer ; - qglViewport = logViewport ; - } - else - { - qglAccum = dllAccum; - qglAlphaFunc = dllAlphaFunc; - qglAreTexturesResident = dllAreTexturesResident; - qglArrayElement = dllArrayElement; - qglBegin = dllBegin; - qglBindTexture = dllBindTexture; - qglBitmap = dllBitmap; - qglBlendFunc = dllBlendFunc; - qglCallList = dllCallList; - qglCallLists = dllCallLists; - qglClear = dllClear; - qglClearAccum = dllClearAccum; - qglClearColor = dllClearColor; - qglClearDepth = dllClearDepth; - qglClearIndex = dllClearIndex; - qglClearStencil = dllClearStencil; - qglClipPlane = dllClipPlane; - qglColor3b = dllColor3b; - qglColor3bv = dllColor3bv; - qglColor3d = dllColor3d; - qglColor3dv = dllColor3dv; - qglColor3f = dllColor3f; - qglColor3fv = dllColor3fv; - qglColor3i = dllColor3i; - qglColor3iv = dllColor3iv; - qglColor3s = dllColor3s; - qglColor3sv = dllColor3sv; - qglColor3ub = dllColor3ub; - qglColor3ubv = dllColor3ubv; - qglColor3ui = dllColor3ui; - qglColor3uiv = dllColor3uiv; - qglColor3us = dllColor3us; - qglColor3usv = dllColor3usv; - qglColor4b = dllColor4b; - qglColor4bv = dllColor4bv; - qglColor4d = dllColor4d; - qglColor4dv = dllColor4dv; - qglColor4f = dllColor4f; - qglColor4fv = dllColor4fv; - qglColor4i = dllColor4i; - qglColor4iv = dllColor4iv; - qglColor4s = dllColor4s; - qglColor4sv = dllColor4sv; - qglColor4ub = dllColor4ub; - qglColor4ubv = dllColor4ubv; - qglColor4ui = dllColor4ui; - qglColor4uiv = dllColor4uiv; - qglColor4us = dllColor4us; - qglColor4usv = dllColor4usv; - qglColorMask = dllColorMask; - qglColorMaterial = dllColorMaterial; - qglColorPointer = dllColorPointer; - qglCopyPixels = dllCopyPixels; - qglCopyTexImage1D = dllCopyTexImage1D; - qglCopyTexImage2D = dllCopyTexImage2D; - qglCopyTexSubImage1D = dllCopyTexSubImage1D; - qglCopyTexSubImage2D = dllCopyTexSubImage2D; - qglCullFace = dllCullFace; - qglDeleteLists = dllDeleteLists ; - qglDeleteTextures = dllDeleteTextures ; - qglDepthFunc = dllDepthFunc ; - qglDepthMask = dllDepthMask ; - qglDepthRange = dllDepthRange ; - qglDisable = dllDisable ; - qglDisableClientState = dllDisableClientState ; - qglDrawArrays = dllDrawArrays ; - qglDrawBuffer = dllDrawBuffer ; - qglDrawElements = dllDrawElements ; - qglDrawPixels = dllDrawPixels ; - qglEdgeFlag = dllEdgeFlag ; - qglEdgeFlagPointer = dllEdgeFlagPointer ; - qglEdgeFlagv = dllEdgeFlagv ; - qglEnable = dllEnable ; - qglEnableClientState = dllEnableClientState ; - qglEnd = dllEnd ; - qglEndList = dllEndList ; - qglEvalCoord1d = dllEvalCoord1d ; - qglEvalCoord1dv = dllEvalCoord1dv ; - qglEvalCoord1f = dllEvalCoord1f ; - qglEvalCoord1fv = dllEvalCoord1fv ; - qglEvalCoord2d = dllEvalCoord2d ; - qglEvalCoord2dv = dllEvalCoord2dv ; - qglEvalCoord2f = dllEvalCoord2f ; - qglEvalCoord2fv = dllEvalCoord2fv ; - qglEvalMesh1 = dllEvalMesh1 ; - qglEvalMesh2 = dllEvalMesh2 ; - qglEvalPoint1 = dllEvalPoint1 ; - qglEvalPoint2 = dllEvalPoint2 ; - qglFeedbackBuffer = dllFeedbackBuffer ; - qglFinish = dllFinish ; - qglFlush = dllFlush ; - qglFogf = dllFogf ; - qglFogfv = dllFogfv ; - qglFogi = dllFogi ; - qglFogiv = dllFogiv ; - qglFrontFace = dllFrontFace ; - qglFrustum = dllFrustum ; - qglGenLists = dllGenLists ; - qglGenTextures = dllGenTextures ; - qglGetBooleanv = dllGetBooleanv ; - qglGetClipPlane = dllGetClipPlane ; - qglGetDoublev = dllGetDoublev ; - qglGetError = dllGetError ; - qglGetFloatv = dllGetFloatv ; - qglGetIntegerv = dllGetIntegerv ; - qglGetLightfv = dllGetLightfv ; - qglGetLightiv = dllGetLightiv ; - qglGetMapdv = dllGetMapdv ; - qglGetMapfv = dllGetMapfv ; - qglGetMapiv = dllGetMapiv ; - qglGetMaterialfv = dllGetMaterialfv ; - qglGetMaterialiv = dllGetMaterialiv ; - qglGetPixelMapfv = dllGetPixelMapfv ; - qglGetPixelMapuiv = dllGetPixelMapuiv ; - qglGetPixelMapusv = dllGetPixelMapusv ; - qglGetPointerv = dllGetPointerv ; - qglGetPolygonStipple = dllGetPolygonStipple ; - qglGetString = dllGetString ; - qglGetTexEnvfv = dllGetTexEnvfv ; - qglGetTexEnviv = dllGetTexEnviv ; - qglGetTexGendv = dllGetTexGendv ; - qglGetTexGenfv = dllGetTexGenfv ; - qglGetTexGeniv = dllGetTexGeniv ; - qglGetTexImage = dllGetTexImage ; - qglGetTexLevelParameterfv = dllGetTexLevelParameterfv ; - qglGetTexLevelParameteriv = dllGetTexLevelParameteriv ; - qglGetTexParameterfv = dllGetTexParameterfv ; - qglGetTexParameteriv = dllGetTexParameteriv ; - qglHint = dllHint ; - qglIndexMask = dllIndexMask ; - qglIndexPointer = dllIndexPointer ; - qglIndexd = dllIndexd ; - qglIndexdv = dllIndexdv ; - qglIndexf = dllIndexf ; - qglIndexfv = dllIndexfv ; - qglIndexi = dllIndexi ; - qglIndexiv = dllIndexiv ; - qglIndexs = dllIndexs ; - qglIndexsv = dllIndexsv ; - qglIndexub = dllIndexub ; - qglIndexubv = dllIndexubv ; - qglInitNames = dllInitNames ; - qglInterleavedArrays = dllInterleavedArrays ; - qglIsEnabled = dllIsEnabled ; - qglIsList = dllIsList ; - qglIsTexture = dllIsTexture ; - qglLightModelf = dllLightModelf ; - qglLightModelfv = dllLightModelfv ; - qglLightModeli = dllLightModeli ; - qglLightModeliv = dllLightModeliv ; - qglLightf = dllLightf ; - qglLightfv = dllLightfv ; - qglLighti = dllLighti ; - qglLightiv = dllLightiv ; - qglLineStipple = dllLineStipple ; - qglLineWidth = dllLineWidth ; - qglListBase = dllListBase ; - qglLoadIdentity = dllLoadIdentity ; - qglLoadMatrixd = dllLoadMatrixd ; - qglLoadMatrixf = dllLoadMatrixf ; - qglLoadName = dllLoadName ; - qglLogicOp = dllLogicOp ; - qglMap1d = dllMap1d ; - qglMap1f = dllMap1f ; - qglMap2d = dllMap2d ; - qglMap2f = dllMap2f ; - qglMapGrid1d = dllMapGrid1d ; - qglMapGrid1f = dllMapGrid1f ; - qglMapGrid2d = dllMapGrid2d ; - qglMapGrid2f = dllMapGrid2f ; - qglMaterialf = dllMaterialf ; - qglMaterialfv = dllMaterialfv ; - qglMateriali = dllMateriali ; - qglMaterialiv = dllMaterialiv ; - qglMatrixMode = dllMatrixMode ; - qglMultMatrixd = dllMultMatrixd ; - qglMultMatrixf = dllMultMatrixf ; - qglNewList = dllNewList ; - qglNormal3b = dllNormal3b ; - qglNormal3bv = dllNormal3bv ; - qglNormal3d = dllNormal3d ; - qglNormal3dv = dllNormal3dv ; - qglNormal3f = dllNormal3f ; - qglNormal3fv = dllNormal3fv ; - qglNormal3i = dllNormal3i ; - qglNormal3iv = dllNormal3iv ; - qglNormal3s = dllNormal3s ; - qglNormal3sv = dllNormal3sv ; - qglNormalPointer = dllNormalPointer ; - qglOrtho = dllOrtho ; - qglPassThrough = dllPassThrough ; - qglPixelMapfv = dllPixelMapfv ; - qglPixelMapuiv = dllPixelMapuiv ; - qglPixelMapusv = dllPixelMapusv ; - qglPixelStoref = dllPixelStoref ; - qglPixelStorei = dllPixelStorei ; - qglPixelTransferf = dllPixelTransferf ; - qglPixelTransferi = dllPixelTransferi ; - qglPixelZoom = dllPixelZoom ; - qglPointSize = dllPointSize ; - qglPolygonMode = dllPolygonMode ; - qglPolygonOffset = dllPolygonOffset ; - qglPolygonStipple = dllPolygonStipple ; - qglPopAttrib = dllPopAttrib ; - qglPopClientAttrib = dllPopClientAttrib ; - qglPopMatrix = dllPopMatrix ; - qglPopName = dllPopName ; - qglPrioritizeTextures = dllPrioritizeTextures ; - qglPushAttrib = dllPushAttrib ; - qglPushClientAttrib = dllPushClientAttrib ; - qglPushMatrix = dllPushMatrix ; - qglPushName = dllPushName ; - qglRasterPos2d = dllRasterPos2d ; - qglRasterPos2dv = dllRasterPos2dv ; - qglRasterPos2f = dllRasterPos2f ; - qglRasterPos2fv = dllRasterPos2fv ; - qglRasterPos2i = dllRasterPos2i ; - qglRasterPos2iv = dllRasterPos2iv ; - qglRasterPos2s = dllRasterPos2s ; - qglRasterPos2sv = dllRasterPos2sv ; - qglRasterPos3d = dllRasterPos3d ; - qglRasterPos3dv = dllRasterPos3dv ; - qglRasterPos3f = dllRasterPos3f ; - qglRasterPos3fv = dllRasterPos3fv ; - qglRasterPos3i = dllRasterPos3i ; - qglRasterPos3iv = dllRasterPos3iv ; - qglRasterPos3s = dllRasterPos3s ; - qglRasterPos3sv = dllRasterPos3sv ; - qglRasterPos4d = dllRasterPos4d ; - qglRasterPos4dv = dllRasterPos4dv ; - qglRasterPos4f = dllRasterPos4f ; - qglRasterPos4fv = dllRasterPos4fv ; - qglRasterPos4i = dllRasterPos4i ; - qglRasterPos4iv = dllRasterPos4iv ; - qglRasterPos4s = dllRasterPos4s ; - qglRasterPos4sv = dllRasterPos4sv ; - qglReadBuffer = dllReadBuffer ; - qglReadPixels = dllReadPixels ; - qglRectd = dllRectd ; - qglRectdv = dllRectdv ; - qglRectf = dllRectf ; - qglRectfv = dllRectfv ; - qglRecti = dllRecti ; - qglRectiv = dllRectiv ; - qglRects = dllRects ; - qglRectsv = dllRectsv ; - qglRenderMode = dllRenderMode ; - qglRotated = dllRotated ; - qglRotatef = dllRotatef ; - qglScaled = dllScaled ; - qglScalef = dllScalef ; - qglScissor = dllScissor ; - qglSelectBuffer = dllSelectBuffer ; - qglShadeModel = dllShadeModel ; - qglStencilFunc = dllStencilFunc ; - qglStencilMask = dllStencilMask ; - qglStencilOp = dllStencilOp ; - qglTexCoord1d = dllTexCoord1d ; - qglTexCoord1dv = dllTexCoord1dv ; - qglTexCoord1f = dllTexCoord1f ; - qglTexCoord1fv = dllTexCoord1fv ; - qglTexCoord1i = dllTexCoord1i ; - qglTexCoord1iv = dllTexCoord1iv ; - qglTexCoord1s = dllTexCoord1s ; - qglTexCoord1sv = dllTexCoord1sv ; - qglTexCoord2d = dllTexCoord2d ; - qglTexCoord2dv = dllTexCoord2dv ; - qglTexCoord2f = dllTexCoord2f ; - qglTexCoord2fv = dllTexCoord2fv ; - qglTexCoord2i = dllTexCoord2i ; - qglTexCoord2iv = dllTexCoord2iv ; - qglTexCoord2s = dllTexCoord2s ; - qglTexCoord2sv = dllTexCoord2sv ; - qglTexCoord3d = dllTexCoord3d ; - qglTexCoord3dv = dllTexCoord3dv ; - qglTexCoord3f = dllTexCoord3f ; - qglTexCoord3fv = dllTexCoord3fv ; - qglTexCoord3i = dllTexCoord3i ; - qglTexCoord3iv = dllTexCoord3iv ; - qglTexCoord3s = dllTexCoord3s ; - qglTexCoord3sv = dllTexCoord3sv ; - qglTexCoord4d = dllTexCoord4d ; - qglTexCoord4dv = dllTexCoord4dv ; - qglTexCoord4f = dllTexCoord4f ; - qglTexCoord4fv = dllTexCoord4fv ; - qglTexCoord4i = dllTexCoord4i ; - qglTexCoord4iv = dllTexCoord4iv ; - qglTexCoord4s = dllTexCoord4s ; - qglTexCoord4sv = dllTexCoord4sv ; - qglTexCoordPointer = dllTexCoordPointer ; - qglTexEnvf = dllTexEnvf ; - qglTexEnvfv = dllTexEnvfv ; - qglTexEnvi = dllTexEnvi ; - qglTexEnviv = dllTexEnviv ; - qglTexGend = dllTexGend ; - qglTexGendv = dllTexGendv ; - qglTexGenf = dllTexGenf ; - qglTexGenfv = dllTexGenfv ; - qglTexGeni = dllTexGeni ; - qglTexGeniv = dllTexGeniv ; - qglTexImage1D = dllTexImage1D ; - qglTexImage2D = dllTexImage2D ; - qglTexParameterf = dllTexParameterf ; - qglTexParameterfv = dllTexParameterfv ; - qglTexParameteri = dllTexParameteri ; - qglTexParameteriv = dllTexParameteriv ; - qglTexSubImage1D = dllTexSubImage1D ; - qglTexSubImage2D = dllTexSubImage2D ; - qglTranslated = dllTranslated ; - qglTranslatef = dllTranslatef ; - qglVertex2d = dllVertex2d ; - qglVertex2dv = dllVertex2dv ; - qglVertex2f = dllVertex2f ; - qglVertex2fv = dllVertex2fv ; - qglVertex2i = dllVertex2i ; - qglVertex2iv = dllVertex2iv ; - qglVertex2s = dllVertex2s ; - qglVertex2sv = dllVertex2sv ; - qglVertex3d = dllVertex3d ; - qglVertex3dv = dllVertex3dv ; - qglVertex3f = dllVertex3f ; - qglVertex3fv = dllVertex3fv ; - qglVertex3i = dllVertex3i ; - qglVertex3iv = dllVertex3iv ; - qglVertex3s = dllVertex3s ; - qglVertex3sv = dllVertex3sv ; - qglVertex4d = dllVertex4d ; - qglVertex4dv = dllVertex4dv ; - qglVertex4f = dllVertex4f ; - qglVertex4fv = dllVertex4fv ; - qglVertex4i = dllVertex4i ; - qglVertex4iv = dllVertex4iv ; - qglVertex4s = dllVertex4s ; - qglVertex4sv = dllVertex4sv ; - qglVertexPointer = dllVertexPointer ; - qglViewport = dllViewport ; - } -} - - -void GLimp_LogNewFrame( void ) -{ - fprintf( glw_state.log_fp, "*** R_BeginFrame ***\n" ); -} - - diff --git a/code/unix/linux_snd.c b/code/unix/linux_snd.c deleted file mode 100644 index 77c816a..0000000 --- a/code/unix/linux_snd.c +++ /dev/null @@ -1,244 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../client/snd_local.h" - -int audio_fd; -int snd_inited=0; - -cvar_t *sndbits; -cvar_t *sndspeed; -cvar_t *sndchannels; - -cvar_t *snddevice; - -/* Some devices may work only with 48000 */ -static int tryrates[] = { 22050, 11025, 44100, 48000, 8000 }; - -qboolean SNDDMA_Init(void) -{ - int rc; - int fmt; - int tmp; - int i; - char *s; - struct audio_buf_info info; - int caps; - extern uid_t saved_euid; - - if (snd_inited) - return; - - if (!snddevice) { - sndbits = Cvar_Get("sndbits", "16", CVAR_ARCHIVE); - sndspeed = Cvar_Get("sndspeed", "0", CVAR_ARCHIVE); - sndchannels = Cvar_Get("sndchannels", "2", CVAR_ARCHIVE); - snddevice = Cvar_Get("snddevice", "/dev/dsp", CVAR_ARCHIVE); - } - -// open /dev/dsp, confirm capability to mmap, and get size of dma buffer - if (!audio_fd) { - seteuid(saved_euid); - - audio_fd = open(snddevice->string, O_RDWR); - - seteuid(getuid()); - - if (audio_fd < 0) { - perror(snddevice->string); - Com_Printf("Could not open %s\n", snddevice->string); - return 0; - } - } - -#if 0 - /* Not applicable here */ - rc = ioctl(audio_fd, SNDCTL_DSP_RESET, 0); - if (rc < 0) { - perror(snddevice->string); - Com_Printf("Could not reset %s\n", snddevice->string); - close(audio_fd); - - return 0; - } -#endif - - if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &caps) == -1) { - perror(snddevice->string); - Com_Printf("Sound driver too old\n"); - close(audio_fd); - return 0; - } - - if (!(caps & DSP_CAP_TRIGGER) || !(caps & DSP_CAP_MMAP)) { - Com_Printf("Sorry but your soundcard can't do this\n"); - close(audio_fd); - return 0; - } - - -/* SNDCTL_DSP_GETOSPACE moved to be called later */ - -// set sample bits & speed - dma.samplebits = (int)sndbits->value; - if (dma.samplebits != 16 && dma.samplebits != 8) { - ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &fmt); - if (fmt & AFMT_S16_LE) - dma.samplebits = 16; - else if (fmt & AFMT_U8) - dma.samplebits = 8; - } - - dma.speed = (int)sndspeed->value; - if (!dma.speed) { - for (i=0 ; ivalue; - if (dma.channels < 1 || dma.channels > 2) - dma.channels = 2; - -/* mmap() call moved forward */ - - tmp = 0; - if (dma.channels == 2) - tmp = 1; - rc = ioctl(audio_fd, SNDCTL_DSP_STEREO, &tmp); - if (rc < 0) { - perror(snddevice->string); - Com_Printf("Could not set %s to stereo=%d", snddevice->string, dma.channels); - close(audio_fd); - return 0; - } - - if (tmp) - dma.channels = 2; - else - dma.channels = 1; - - rc = ioctl(audio_fd, SNDCTL_DSP_SPEED, &dma.speed); - if (rc < 0) { - perror(snddevice->string); - Com_Printf("Could not set %s speed to %d", snddevice->string, dma.speed); - close(audio_fd); - return 0; - } - - if (dma.samplebits == 16) { - rc = AFMT_S16_LE; - rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc); - if (rc < 0) { - perror(snddevice->string); - Com_Printf("Could not support 16-bit data. Try 8-bit.\n"); - close(audio_fd); - return 0; - } - } else if (dma.samplebits == 8) { - rc = AFMT_U8; - rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc); - if (rc < 0) { - perror(snddevice->string); - Com_Printf("Could not support 8-bit data.\n"); - close(audio_fd); - return 0; - } - } else { - perror(snddevice->string); - Com_Printf("%d-bit sound not supported.", dma.samplebits); - close(audio_fd); - return 0; - } - - if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info)==-1) { - perror("GETOSPACE"); - Com_Printf("Um, can't do GETOSPACE?\n"); - close(audio_fd); - return 0; - } - - dma.samples = info.fragstotal * info.fragsize / (dma.samplebits/8); - dma.submission_chunk = 1; - -// memory map the dma buffer - - if (!dma.buffer) - dma.buffer = (unsigned char *) mmap(NULL, info.fragstotal - * info.fragsize, PROT_WRITE, MAP_FILE|MAP_SHARED, audio_fd, 0); - - if (!dma.buffer) { - perror(snddevice->string); - Com_Printf("Could not mmap %s\n", snddevice->string); - close(audio_fd); - return 0; - } - -// toggle the trigger & start her up - - tmp = 0; - rc = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp); - if (rc < 0) { - perror(snddevice->string); - Com_Printf("Could not toggle.\n"); - close(audio_fd); - return 0; - } - - tmp = PCM_ENABLE_OUTPUT; - rc = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp); - if (rc < 0) { - perror(snddevice->string); - Com_Printf("Could not toggle.\n"); - close(audio_fd); - - return 0; - } - - snd_inited = 1; - return 1; -} - -int SNDDMA_GetDMAPos(void) -{ - struct count_info count; - - if (!snd_inited) return 0; - - if (ioctl(audio_fd, SNDCTL_DSP_GETOPTR, &count) == -1) { - perror(snddevice->string); - Com_Printf("Uh, sound dead.\n"); - close(audio_fd); - snd_inited = 0; - return 0; - } - return count.ptr / (dma.samplebits / 8); -} - -void SNDDMA_Shutdown(void) -{ -} - -/* -============== -SNDDMA_Submit - -Send sound to device if buffer isn't really the dma buffer -=============== -*/ -void SNDDMA_Submit(void) -{ -} - -void SNDDMA_BeginPainting (void) -{ -} diff --git a/code/unix/matha.s b/code/unix/matha.s deleted file mode 100644 index 73174ee..0000000 --- a/code/unix/matha.s +++ /dev/null @@ -1,402 +0,0 @@ -// -// math.s -// x86 assembly-language math routines. - -#define GLQUAKE 1 // don't include unneeded defs -#include "qasm.h" - - -#if id386 - - .data - - .align 4 -Ljmptab: .long Lcase0, Lcase1, Lcase2, Lcase3 - .long Lcase4, Lcase5, Lcase6, Lcase7 - - .text - -// TODO: rounding needed? -// stack parameter offset -#define val 4 - -.globl C(Invert24To16) -C(Invert24To16): - - movl val(%esp),%ecx - movl $0x100,%edx // 0x10000000000 as dividend - cmpl %edx,%ecx - jle LOutOfRange - - subl %eax,%eax - divl %ecx - - ret - -LOutOfRange: - movl $0xFFFFFFFF,%eax - ret - -#if 0 - -#define in 4 -#define out 8 - - .align 2 -.globl C(TransformVector) -C(TransformVector): - movl in(%esp),%eax - movl out(%esp),%edx - - flds (%eax) // in[0] - fmuls C(vright) // in[0]*vright[0] - flds (%eax) // in[0] | in[0]*vright[0] - fmuls C(vup) // in[0]*vup[0] | in[0]*vright[0] - flds (%eax) // in[0] | in[0]*vup[0] | in[0]*vright[0] - fmuls C(vpn) // in[0]*vpn[0] | in[0]*vup[0] | in[0]*vright[0] - - flds 4(%eax) // in[1] | ... - fmuls C(vright)+4 // in[1]*vright[1] | ... - flds 4(%eax) // in[1] | in[1]*vright[1] | ... - fmuls C(vup)+4 // in[1]*vup[1] | in[1]*vright[1] | ... - flds 4(%eax) // in[1] | in[1]*vup[1] | in[1]*vright[1] | ... - fmuls C(vpn)+4 // in[1]*vpn[1] | in[1]*vup[1] | in[1]*vright[1] | ... - fxch %st(2) // in[1]*vright[1] | in[1]*vup[1] | in[1]*vpn[1] | ... - - faddp %st(0),%st(5) // in[1]*vup[1] | in[1]*vpn[1] | ... - faddp %st(0),%st(3) // in[1]*vpn[1] | ... - faddp %st(0),%st(1) // vpn_accum | vup_accum | vright_accum - - flds 8(%eax) // in[2] | ... - fmuls C(vright)+8 // in[2]*vright[2] | ... - flds 8(%eax) // in[2] | in[2]*vright[2] | ... - fmuls C(vup)+8 // in[2]*vup[2] | in[2]*vright[2] | ... - flds 8(%eax) // in[2] | in[2]*vup[2] | in[2]*vright[2] | ... - fmuls C(vpn)+8 // in[2]*vpn[2] | in[2]*vup[2] | in[2]*vright[2] | ... - fxch %st(2) // in[2]*vright[2] | in[2]*vup[2] | in[2]*vpn[2] | ... - - faddp %st(0),%st(5) // in[2]*vup[2] | in[2]*vpn[2] | ... - faddp %st(0),%st(3) // in[2]*vpn[2] | ... - faddp %st(0),%st(1) // vpn_accum | vup_accum | vright_accum - - fstps 8(%edx) // out[2] - fstps 4(%edx) // out[1] - fstps (%edx) // out[0] - - ret - -#endif - -#define EMINS 4+4 -#define EMAXS 4+8 -#define P 4+12 - - .align 2 -.globl C(BoxOnPlaneSide) -C(BoxOnPlaneSide): - pushl %ebx - - movl P(%esp),%edx - movl EMINS(%esp),%ecx - xorl %eax,%eax - movl EMAXS(%esp),%ebx - movb pl_signbits(%edx),%al - cmpb $8,%al - jge Lerror - flds pl_normal(%edx) // p->normal[0] - fld %st(0) // p->normal[0] | p->normal[0] - jmp Ljmptab(,%eax,4) - - -//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; -//dist2= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; -Lcase0: - fmuls (%ebx) // p->normal[0]*emaxs[0] | p->normal[0] - flds pl_normal+4(%edx) // p->normal[1] | p->normal[0]*emaxs[0] | - // p->normal[0] - fxch %st(2) // p->normal[0] | p->normal[0]*emaxs[0] | - // p->normal[1] - fmuls (%ecx) // p->normal[0]*emins[0] | - // p->normal[0]*emaxs[0] | p->normal[1] - fxch %st(2) // p->normal[1] | p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - fld %st(0) // p->normal[1] | p->normal[1] | - // p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - fmuls 4(%ebx) // p->normal[1]*emaxs[1] | p->normal[1] | - // p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - flds pl_normal+8(%edx) // p->normal[2] | p->normal[1]*emaxs[1] | - // p->normal[1] | p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - fxch %st(2) // p->normal[1] | p->normal[1]*emaxs[1] | - // p->normal[2] | p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - fmuls 4(%ecx) // p->normal[1]*emins[1] | - // p->normal[1]*emaxs[1] | - // p->normal[2] | p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - fxch %st(2) // p->normal[2] | p->normal[1]*emaxs[1] | - // p->normal[1]*emins[1] | - // p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - fld %st(0) // p->normal[2] | p->normal[2] | - // p->normal[1]*emaxs[1] | - // p->normal[1]*emins[1] | - // p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - fmuls 8(%ebx) // p->normal[2]*emaxs[2] | - // p->normal[2] | - // p->normal[1]*emaxs[1] | - // p->normal[1]*emins[1] | - // p->normal[0]*emaxs[0] | - // p->normal[0]*emins[0] - fxch %st(5) // p->normal[0]*emins[0] | - // p->normal[2] | - // p->normal[1]*emaxs[1] | - // p->normal[1]*emins[1] | - // p->normal[0]*emaxs[0] | - // p->normal[2]*emaxs[2] - faddp %st(0),%st(3) //p->normal[2] | - // p->normal[1]*emaxs[1] | - // p->normal[1]*emins[1]+p->normal[0]*emins[0]| - // p->normal[0]*emaxs[0] | - // p->normal[2]*emaxs[2] - fmuls 8(%ecx) //p->normal[2]*emins[2] | - // p->normal[1]*emaxs[1] | - // p->normal[1]*emins[1]+p->normal[0]*emins[0]| - // p->normal[0]*emaxs[0] | - // p->normal[2]*emaxs[2] - fxch %st(1) //p->normal[1]*emaxs[1] | - // p->normal[2]*emins[2] | - // p->normal[1]*emins[1]+p->normal[0]*emins[0]| - // p->normal[0]*emaxs[0] | - // p->normal[2]*emaxs[2] - faddp %st(0),%st(3) //p->normal[2]*emins[2] | - // p->normal[1]*emins[1]+p->normal[0]*emins[0]| - // p->normal[0]*emaxs[0]+p->normal[1]*emaxs[1]| - // p->normal[2]*emaxs[2] - fxch %st(3) //p->normal[2]*emaxs[2] + - // p->normal[1]*emins[1]+p->normal[0]*emins[0]| - // p->normal[0]*emaxs[0]+p->normal[1]*emaxs[1]| - // p->normal[2]*emins[2] - faddp %st(0),%st(2) //p->normal[1]*emins[1]+p->normal[0]*emins[0]| - // dist1 | p->normal[2]*emins[2] - - jmp LSetSides - -//dist1= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; -//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; -Lcase1: - fmuls (%ecx) // emins[0] - flds pl_normal+4(%edx) - fxch %st(2) - fmuls (%ebx) // emaxs[0] - fxch %st(2) - fld %st(0) - fmuls 4(%ebx) // emaxs[1] - flds pl_normal+8(%edx) - fxch %st(2) - fmuls 4(%ecx) // emins[1] - fxch %st(2) - fld %st(0) - fmuls 8(%ebx) // emaxs[2] - fxch %st(5) - faddp %st(0),%st(3) - fmuls 8(%ecx) // emins[2] - fxch %st(1) - faddp %st(0),%st(3) - fxch %st(3) - faddp %st(0),%st(2) - - jmp LSetSides - -//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; -//dist2= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; -Lcase2: - fmuls (%ebx) // emaxs[0] - flds pl_normal+4(%edx) - fxch %st(2) - fmuls (%ecx) // emins[0] - fxch %st(2) - fld %st(0) - fmuls 4(%ecx) // emins[1] - flds pl_normal+8(%edx) - fxch %st(2) - fmuls 4(%ebx) // emaxs[1] - fxch %st(2) - fld %st(0) - fmuls 8(%ebx) // emaxs[2] - fxch %st(5) - faddp %st(0),%st(3) - fmuls 8(%ecx) // emins[2] - fxch %st(1) - faddp %st(0),%st(3) - fxch %st(3) - faddp %st(0),%st(2) - - jmp LSetSides - -//dist1= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; -//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; -Lcase3: - fmuls (%ecx) // emins[0] - flds pl_normal+4(%edx) - fxch %st(2) - fmuls (%ebx) // emaxs[0] - fxch %st(2) - fld %st(0) - fmuls 4(%ecx) // emins[1] - flds pl_normal+8(%edx) - fxch %st(2) - fmuls 4(%ebx) // emaxs[1] - fxch %st(2) - fld %st(0) - fmuls 8(%ebx) // emaxs[2] - fxch %st(5) - faddp %st(0),%st(3) - fmuls 8(%ecx) // emins[2] - fxch %st(1) - faddp %st(0),%st(3) - fxch %st(3) - faddp %st(0),%st(2) - - jmp LSetSides - -//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; -//dist2= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; -Lcase4: - fmuls (%ebx) // emaxs[0] - flds pl_normal+4(%edx) - fxch %st(2) - fmuls (%ecx) // emins[0] - fxch %st(2) - fld %st(0) - fmuls 4(%ebx) // emaxs[1] - flds pl_normal+8(%edx) - fxch %st(2) - fmuls 4(%ecx) // emins[1] - fxch %st(2) - fld %st(0) - fmuls 8(%ecx) // emins[2] - fxch %st(5) - faddp %st(0),%st(3) - fmuls 8(%ebx) // emaxs[2] - fxch %st(1) - faddp %st(0),%st(3) - fxch %st(3) - faddp %st(0),%st(2) - - jmp LSetSides - -//dist1= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; -//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; -Lcase5: - fmuls (%ecx) // emins[0] - flds pl_normal+4(%edx) - fxch %st(2) - fmuls (%ebx) // emaxs[0] - fxch %st(2) - fld %st(0) - fmuls 4(%ebx) // emaxs[1] - flds pl_normal+8(%edx) - fxch %st(2) - fmuls 4(%ecx) // emins[1] - fxch %st(2) - fld %st(0) - fmuls 8(%ecx) // emins[2] - fxch %st(5) - faddp %st(0),%st(3) - fmuls 8(%ebx) // emaxs[2] - fxch %st(1) - faddp %st(0),%st(3) - fxch %st(3) - faddp %st(0),%st(2) - - jmp LSetSides - -//dist1= p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; -//dist2= p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; -Lcase6: - fmuls (%ebx) // emaxs[0] - flds pl_normal+4(%edx) - fxch %st(2) - fmuls (%ecx) // emins[0] - fxch %st(2) - fld %st(0) - fmuls 4(%ecx) // emins[1] - flds pl_normal+8(%edx) - fxch %st(2) - fmuls 4(%ebx) // emaxs[1] - fxch %st(2) - fld %st(0) - fmuls 8(%ecx) // emins[2] - fxch %st(5) - faddp %st(0),%st(3) - fmuls 8(%ebx) // emaxs[2] - fxch %st(1) - faddp %st(0),%st(3) - fxch %st(3) - faddp %st(0),%st(2) - - jmp LSetSides - -//dist1= p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; -//dist2= p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; -Lcase7: - fmuls (%ecx) // emins[0] - flds pl_normal+4(%edx) - fxch %st(2) - fmuls (%ebx) // emaxs[0] - fxch %st(2) - fld %st(0) - fmuls 4(%ecx) // emins[1] - flds pl_normal+8(%edx) - fxch %st(2) - fmuls 4(%ebx) // emaxs[1] - fxch %st(2) - fld %st(0) - fmuls 8(%ecx) // emins[2] - fxch %st(5) - faddp %st(0),%st(3) - fmuls 8(%ebx) // emaxs[2] - fxch %st(1) - faddp %st(0),%st(3) - fxch %st(3) - faddp %st(0),%st(2) - -LSetSides: - -// sides = 0; -// if (dist1 >= p->dist) -// sides = 1; -// if (dist2 < p->dist) -// sides |= 2; - - faddp %st(0),%st(2) // dist1 | dist2 - fcomps pl_dist(%edx) - xorl %ecx,%ecx - fnstsw %ax - fcomps pl_dist(%edx) - andb $1,%ah - xorb $1,%ah - addb %ah,%cl - - fnstsw %ax - andb $1,%ah - addb %ah,%ah - addb %ah,%cl - -// return sides; - - popl %ebx - movl %ecx,%eax // return status - - ret - - -Lerror: - movl 1, %eax - ret - -#endif // id386 diff --git a/code/unix/q3test.spec.sh b/code/unix/q3test.spec.sh deleted file mode 100644 index a50e1f6..0000000 --- a/code/unix/q3test.spec.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/sh -# Generate Quake3 test -# $1 is version -# $2 is release -# $3 is arch -# $4 is install dir (assumed to be in /var/tmp) -cat < -URL: http://www.idsoftware.com/ -Source: q3test-%{version}.tar.gz -Group: Games -Copyright: Restricted -Icon: quake3.gif -BuildRoot: /var/tmp/%{name}-%{version} -Summary: Q3Test for Linux - -%description - -%install - -%files - -%attr(644,root,root) $4/README.EULA -%attr(644,root,root) $4/README.Q3Test -%attr(755,root,root) $4/linuxquake3 -%attr(755,root,root) $4/cgamei386.so -%attr(755,root,root) $4/qagamei386.so -%attr(755,root,root) $4/libMesaVoodooGL.so.3.1 -%attr(644,root,root) $4/demoq3/pak0.pk3 - -EOF - diff --git a/code/unix/qasm.h b/code/unix/qasm.h deleted file mode 100644 index c31b6b8..0000000 --- a/code/unix/qasm.h +++ /dev/null @@ -1,459 +0,0 @@ -#ifndef __ASM_I386__ -#define __ASM_I386__ - -#ifdef ELF -#define C(label) label -#else -#define C(label) _##label -#endif - - -//#define GLQUAKE 1 - -#if defined(_WIN32) && !defined(WINDED) - -#if defined(_M_IX86) -#define __i386__ 1 -#endif - -#endif - -#ifdef __i386__ -#define id386 1 -#else -#define id386 0 -#endif - -// !!! must be kept the same as in d_iface.h !!! -#define TRANSPARENT_COLOR 255 - -#ifndef GLQUAKE - .extern C(d_zistepu) - .extern C(d_pzbuffer) - .extern C(d_zistepv) - .extern C(d_zrowbytes) - .extern C(d_ziorigin) - .extern C(r_turb_s) - .extern C(r_turb_t) - .extern C(r_turb_pdest) - .extern C(r_turb_spancount) - .extern C(r_turb_turb) - .extern C(r_turb_pbase) - .extern C(r_turb_sstep) - .extern C(r_turb_tstep) - .extern C(r_bmodelactive) - .extern C(d_sdivzstepu) - .extern C(d_tdivzstepu) - .extern C(d_sdivzstepv) - .extern C(d_tdivzstepv) - .extern C(d_sdivzorigin) - .extern C(d_tdivzorigin) - .extern C(sadjust) - .extern C(tadjust) - .extern C(bbextents) - .extern C(bbextentt) - .extern C(cacheblock) - .extern C(d_viewbuffer) - .extern C(cachewidth) - .extern C(d_pzbuffer) - .extern C(d_zrowbytes) - .extern C(d_zwidth) - .extern C(d_scantable) - .extern C(r_lightptr) - .extern C(r_numvblocks) - .extern C(prowdestbase) - .extern C(pbasesource) - .extern C(r_lightwidth) - .extern C(lightright) - .extern C(lightrightstep) - .extern C(lightdeltastep) - .extern C(lightdelta) - .extern C(lightright) - .extern C(lightdelta) - .extern C(sourcetstep) - .extern C(surfrowbytes) - .extern C(lightrightstep) - .extern C(lightdeltastep) - .extern C(r_sourcemax) - .extern C(r_stepback) - .extern C(colormap) - .extern C(blocksize) - .extern C(sourcesstep) - .extern C(lightleft) - .extern C(blockdivshift) - .extern C(blockdivmask) - .extern C(lightleftstep) - .extern C(r_origin) - .extern C(r_ppn) - .extern C(r_pup) - .extern C(r_pright) - .extern C(ycenter) - .extern C(xcenter) - .extern C(d_vrectbottom_particle) - .extern C(d_vrectright_particle) - .extern C(d_vrecty) - .extern C(d_vrectx) - .extern C(d_pix_shift) - .extern C(d_pix_min) - .extern C(d_pix_max) - .extern C(d_y_aspect_shift) - .extern C(screenwidth) - .extern C(r_leftclipped) - .extern C(r_leftenter) - .extern C(r_rightclipped) - .extern C(r_rightenter) - .extern C(modelorg) - .extern C(xscale) - .extern C(r_refdef) - .extern C(yscale) - .extern C(r_leftexit) - .extern C(r_rightexit) - .extern C(r_lastvertvalid) - .extern C(cacheoffset) - .extern C(newedges) - .extern C(removeedges) - .extern C(r_pedge) - .extern C(r_framecount) - .extern C(r_u1) - .extern C(r_emitted) - .extern C(edge_p) - .extern C(surface_p) - .extern C(surfaces) - .extern C(r_lzi1) - .extern C(r_v1) - .extern C(r_ceilv1) - .extern C(r_nearzi) - .extern C(r_nearzionly) - .extern C(edge_aftertail) - .extern C(edge_tail) - .extern C(current_iv) - .extern C(edge_head_u_shift20) - .extern C(span_p) - .extern C(edge_head) - .extern C(fv) - .extern C(edge_tail_u_shift20) - .extern C(r_apverts) - .extern C(r_anumverts) - .extern C(aliastransform) - .extern C(r_avertexnormals) - .extern C(r_plightvec) - .extern C(r_ambientlight) - .extern C(r_shadelight) - .extern C(aliasxcenter) - .extern C(aliasycenter) - .extern C(a_sstepxfrac) - .extern C(r_affinetridesc) - .extern C(acolormap) - .extern C(d_pcolormap) - .extern C(r_affinetridesc) - .extern C(d_sfrac) - .extern C(d_ptex) - .extern C(d_pedgespanpackage) - .extern C(d_tfrac) - .extern C(d_light) - .extern C(d_zi) - .extern C(d_pdest) - .extern C(d_pz) - .extern C(d_aspancount) - .extern C(erroradjustup) - .extern C(errorterm) - .extern C(d_xdenom) - .extern C(r_p0) - .extern C(r_p1) - .extern C(r_p2) - .extern C(a_tstepxfrac) - .extern C(r_sstepx) - .extern C(r_tstepx) - .extern C(a_ststepxwhole) - .extern C(zspantable) - .extern C(skintable) - .extern C(r_zistepx) - .extern C(erroradjustdown) - .extern C(d_countextrastep) - .extern C(ubasestep) - .extern C(a_ststepxwhole) - .extern C(a_tstepxfrac) - .extern C(r_lstepx) - .extern C(a_spans) - .extern C(erroradjustdown) - .extern C(d_pdestextrastep) - .extern C(d_pzextrastep) - .extern C(d_sfracextrastep) - .extern C(d_ptexextrastep) - .extern C(d_countextrastep) - .extern C(d_tfracextrastep) - .extern C(d_lightextrastep) - .extern C(d_ziextrastep) - .extern C(d_pdestbasestep) - .extern C(d_pzbasestep) - .extern C(d_sfracbasestep) - .extern C(d_ptexbasestep) - .extern C(ubasestep) - .extern C(d_tfracbasestep) - .extern C(d_lightbasestep) - .extern C(d_zibasestep) - .extern C(zspantable) - .extern C(r_lstepy) - .extern C(r_sstepy) - .extern C(r_tstepy) - .extern C(r_zistepy) - .extern C(D_PolysetSetEdgeTable) - .extern C(D_RasterizeAliasPolySmooth) - - .extern float_point5 - .extern Float2ToThe31nd - .extern izistep - .extern izi - .extern FloatMinus2ToThe31nd - .extern float_1 - .extern float_particle_z_clip - .extern float_minus_1 - .extern float_0 - .extern fp_16 - .extern fp_64k - .extern fp_1m - .extern fp_1m_minus_1 - .extern fp_8 - .extern entryvec_table - .extern advancetable - .extern sstep - .extern tstep - .extern pspantemp - .extern counttemp - .extern jumptemp - .extern reciprocal_table - .extern DP_Count - .extern DP_u - .extern DP_v - .extern DP_32768 - .extern DP_Color - .extern DP_Pix - .extern DP_EntryTable - .extern pbase - .extern s - .extern t - .extern sfracf - .extern tfracf - .extern snext - .extern tnext - .extern spancountminus1 - .extern zi16stepu - .extern sdivz16stepu - .extern tdivz16stepu - .extern zi8stepu - .extern sdivz8stepu - .extern tdivz8stepu - .extern reciprocal_table_16 - .extern entryvec_table_16 - .extern ceil_cw - .extern single_cw - .extern fp_64kx64k - .extern pz - .extern spr8entryvec_table -#endif - - .extern C(snd_scaletable) - .extern C(paintbuffer) - .extern C(snd_linear_count) - .extern C(snd_p) - .extern C(snd_vol) - .extern C(snd_out) - .extern C(vright) - .extern C(vup) - .extern C(vpn) - .extern C(BOPS_Error) - -// -// !!! note that this file must match the corresponding C structures at all -// times !!! -// - -// plane_t structure -// !!! if this is changed, it must be changed in model.h too !!! -// !!! if the size of this is changed, the array lookup in SV_HullPointContents -// must be changed too !!! -#define pl_normal 0 -#define pl_dist 12 -#define pl_type 16 -#define pl_signbits 17 -#define pl_pad 18 -#define pl_size 20 - -// hull_t structure -// !!! if this is changed, it must be changed in model.h too !!! -#define hu_clipnodes 0 -#define hu_planes 4 -#define hu_firstclipnode 8 -#define hu_lastclipnode 12 -#define hu_clip_mins 16 -#define hu_clip_maxs 28 -#define hu_size 40 - -// dnode_t structure -// !!! if this is changed, it must be changed in bspfile.h too !!! -#define nd_planenum 0 -#define nd_children 4 -#define nd_mins 8 -#define nd_maxs 20 -#define nd_firstface 32 -#define nd_numfaces 36 -#define nd_size 40 - -// sfxcache_t structure -// !!! if this is changed, it much be changed in sound.h too !!! -#define sfxc_length 0 -#define sfxc_loopstart 4 -#define sfxc_speed 8 -#define sfxc_width 12 -#define sfxc_stereo 16 -#define sfxc_data 20 - -// channel_t structure -// !!! if this is changed, it much be changed in sound.h too !!! -#define ch_sfx 0 -#define ch_leftvol 4 -#define ch_rightvol 8 -#define ch_end 12 -#define ch_pos 16 -#define ch_looping 20 -#define ch_entnum 24 -#define ch_entchannel 28 -#define ch_origin 32 -#define ch_dist_mult 44 -#define ch_master_vol 48 -#define ch_size 52 - -// portable_samplepair_t structure -// !!! if this is changed, it much be changed in sound.h too !!! -#define psp_left 0 -#define psp_right 4 -#define psp_size 8 - - -// -// !!! note that this file must match the corresponding C structures at all -// times !!! -// - -// !!! if this is changed, it must be changed in r_local.h too !!! -#define NEAR_CLIP 0.01 - -// !!! if this is changed, it must be changed in r_local.h too !!! -#define CYCLE 128 - -// espan_t structure -// !!! if this is changed, it must be changed in r_shared.h too !!! -#define espan_t_u 0 -#define espan_t_v 4 -#define espan_t_count 8 -#define espan_t_pnext 12 -#define espan_t_size 16 - -// sspan_t structure -// !!! if this is changed, it must be changed in d_local.h too !!! -#define sspan_t_u 0 -#define sspan_t_v 4 -#define sspan_t_count 8 -#define sspan_t_size 12 - -// spanpackage_t structure -// !!! if this is changed, it must be changed in d_polyset.c too !!! -#define spanpackage_t_pdest 0 -#define spanpackage_t_pz 4 -#define spanpackage_t_count 8 -#define spanpackage_t_ptex 12 -#define spanpackage_t_sfrac 16 -#define spanpackage_t_tfrac 20 -#define spanpackage_t_light 24 -#define spanpackage_t_zi 28 -#define spanpackage_t_size 32 - -// edge_t structure -// !!! if this is changed, it must be changed in r_shared.h too !!! -#define et_u 0 -#define et_u_step 4 -#define et_prev 8 -#define et_next 12 -#define et_surfs 16 -#define et_nextremove 20 -#define et_nearzi 24 -#define et_owner 28 -#define et_size 32 - -// surf_t structure -// !!! if this is changed, it must be changed in r_shared.h too !!! -#define SURF_T_SHIFT 6 -#define st_next 0 -#define st_prev 4 -#define st_spans 8 -#define st_key 12 -#define st_last_u 16 -#define st_spanstate 20 -#define st_flags 24 -#define st_data 28 -#define st_entity 32 -#define st_nearzi 36 -#define st_insubmodel 40 -#define st_d_ziorigin 44 -#define st_d_zistepu 48 -#define st_d_zistepv 52 -#define st_pad 56 -#define st_size 64 - -// clipplane_t structure -// !!! if this is changed, it must be changed in r_local.h too !!! -#define cp_normal 0 -#define cp_dist 12 -#define cp_next 16 -#define cp_leftedge 20 -#define cp_rightedge 21 -#define cp_reserved 22 -#define cp_size 24 - -// medge_t structure -// !!! if this is changed, it must be changed in model.h too !!! -#define me_v 0 -#define me_cachededgeoffset 4 -#define me_size 8 - -// mvertex_t structure -// !!! if this is changed, it must be changed in model.h too !!! -#define mv_position 0 -#define mv_size 12 - -// refdef_t structure -// !!! if this is changed, it must be changed in render.h too !!! -#define rd_vrect 0 -#define rd_aliasvrect 20 -#define rd_vrectright 40 -#define rd_vrectbottom 44 -#define rd_aliasvrectright 48 -#define rd_aliasvrectbottom 52 -#define rd_vrectrightedge 56 -#define rd_fvrectx 60 -#define rd_fvrecty 64 -#define rd_fvrectx_adj 68 -#define rd_fvrecty_adj 72 -#define rd_vrect_x_adj_shift20 76 -#define rd_vrectright_adj_shift20 80 -#define rd_fvrectright_adj 84 -#define rd_fvrectbottom_adj 88 -#define rd_fvrectright 92 -#define rd_fvrectbottom 96 -#define rd_horizontalFieldOfView 100 -#define rd_xOrigin 104 -#define rd_yOrigin 108 -#define rd_vieworg 112 -#define rd_viewangles 124 -#define rd_ambientlight 136 -#define rd_size 140 - -// mtriangle_t structure -// !!! if this is changed, it must be changed in model.h too !!! -#define mtri_facesfront 0 -#define mtri_vertindex 4 -#define mtri_size 16 // !!! if this changes, array indexing in !!! - // !!! d_polysa.s must be changed to match !!! -#define mtri_shift 4 - -#endif diff --git a/code/unix/quake3.gif b/code/unix/quake3.gif deleted file mode 100644 index 0e7a01c..0000000 Binary files a/code/unix/quake3.gif and /dev/null differ diff --git a/code/unix/snd_mixa.s b/code/unix/snd_mixa.s deleted file mode 100644 index d6c2945..0000000 --- a/code/unix/snd_mixa.s +++ /dev/null @@ -1,197 +0,0 @@ -// -// snd_mixa.s -// x86 assembly-language sound code -// - -#include "qasm.h" - -#if id386 - - .text - -#if 0 -//---------------------------------------------------------------------- -// 8-bit sound-mixing code -//---------------------------------------------------------------------- - -#define ch 4+16 -#define sc 8+16 -#define count 12+16 - -.globl C(S_PaintChannelFrom8) -C(S_PaintChannelFrom8): - pushl %esi // preserve register variables - pushl %edi - pushl %ebx - pushl %ebp - -// int data; -// short *lscale, *rscale; -// unsigned char *sfx; -// int i; - - movl ch(%esp),%ebx - movl sc(%esp),%esi - -// if (ch->leftvol > 255) -// ch->leftvol = 255; -// if (ch->rightvol > 255) -// ch->rightvol = 255; - movl ch_leftvol(%ebx),%eax - movl ch_rightvol(%ebx),%edx - cmpl $255,%eax - jna LLeftSet - movl $255,%eax -LLeftSet: - cmpl $255,%edx - jna LRightSet - movl $255,%edx -LRightSet: - -// lscale = snd_scaletable[ch->leftvol >> 3]; -// rscale = snd_scaletable[ch->rightvol >> 3]; -// sfx = (signed char *)sc->data + ch->pos; -// ch->pos += count; - andl $0xF8,%eax - addl $20,%esi - movl (%esi),%esi - andl $0xF8,%edx - movl ch_pos(%ebx),%edi - movl count(%esp),%ecx - addl %edi,%esi - shll $7,%eax - addl %ecx,%edi - shll $7,%edx - movl %edi,ch_pos(%ebx) - addl $(C(snd_scaletable)),%eax - addl $(C(snd_scaletable)),%edx - subl %ebx,%ebx - movb -1(%esi,%ecx,1),%bl - - testl $1,%ecx - jz LMix8Loop - - movl (%eax,%ebx,4),%edi - movl (%edx,%ebx,4),%ebp - addl C(paintbuffer)+psp_left-psp_size(,%ecx,psp_size),%edi - addl C(paintbuffer)+psp_right-psp_size(,%ecx,psp_size),%ebp - movl %edi,C(paintbuffer)+psp_left-psp_size(,%ecx,psp_size) - movl %ebp,C(paintbuffer)+psp_right-psp_size(,%ecx,psp_size) - movb -2(%esi,%ecx,1),%bl - - decl %ecx - jz LDone - -// for (i=0 ; i>8; -// if (val > 0x7fff) -// snd_out[i] = 0x7fff; -// else if (val < (short)0x8000) -// snd_out[i] = (short)0x8000; -// else -// snd_out[i] = val; - movl -8(%ebx,%ecx,4),%eax - sarl $8,%eax - cmpl $0x7FFF,%eax - jg LClampHigh - cmpl $0xFFFF8000,%eax - jnl LClampDone - movl $0xFFFF8000,%eax - jmp LClampDone -LClampHigh: - movl $0x7FFF,%eax -LClampDone: - -// val = (snd_p[i+1]*snd_vol)>>8; -// if (val > 0x7fff) -// snd_out[i+1] = 0x7fff; -// else if (val < (short)0x8000) -// snd_out[i+1] = (short)0x8000; -// else -// snd_out[i+1] = val; - movl -4(%ebx,%ecx,4),%edx - sarl $8,%edx - cmpl $0x7FFF,%edx - jg LClampHigh2 - cmpl $0xFFFF8000,%edx - jnl LClampDone2 - movl $0xFFFF8000,%edx - jmp LClampDone2 -LClampHigh2: - movl $0x7FFF,%edx -LClampDone2: - shll $16,%edx - andl $0xFFFF,%eax - orl %eax,%edx - movl %edx,-4(%edi,%ecx,2) - -// } - subl $2,%ecx - jnz LWLBLoopTop - -// snd_p += snd_linear_count; - - popl %ebx - popl %edi - - ret - - -#endif // id386 - diff --git a/code/unix/sys_dosa.s b/code/unix/sys_dosa.s deleted file mode 100644 index cc29312..0000000 --- a/code/unix/sys_dosa.s +++ /dev/null @@ -1,94 +0,0 @@ -// -// sys_dosa.s -// x86 assembly-language DOS-dependent routines. - -#include "qasm.h" - - - .data - - .align 4 -fpenv: - .long 0, 0, 0, 0, 0, 0, 0, 0 - - .text - -.globl C(MaskExceptions) -C(MaskExceptions): - fnstenv fpenv - orl $0x3F,fpenv - fldenv fpenv - - ret - -#if 0 -.globl C(unmaskexceptions) -C(unmaskexceptions): - fnstenv fpenv - andl $0xFFFFFFE0,fpenv - fldenv fpenv - - ret -#endif - - .data - - .align 4 -.globl ceil_cw, single_cw, full_cw, cw, pushed_cw -ceil_cw: .long 0 -single_cw: .long 0 -full_cw: .long 0 -cw: .long 0 -pushed_cw: .long 0 - - .text - -.globl C(Sys_LowFPPrecision) -C(Sys_LowFPPrecision): - fldcw single_cw - - ret - -.globl C(Sys_HighFPPrecision) -C(Sys_HighFPPrecision): - fldcw full_cw - - ret - -.globl C(Sys_PushFPCW_SetHigh) -C(Sys_PushFPCW_SetHigh): - fnstcw pushed_cw - fldcw full_cw - - ret - -.globl C(Sys_PopFPCW) -C(Sys_PopFPCW): - fldcw pushed_cw - - ret - -.globl C(Sys_SetFPCW) -C(Sys_SetFPCW): - fnstcw cw - movl cw,%eax -#if id386 - andb $0xF0,%ah - orb $0x03,%ah // round mode, 64-bit precision -#endif - movl %eax,full_cw - -#if id386 - andb $0xF0,%ah - orb $0x0C,%ah // chop mode, single precision -#endif - movl %eax,single_cw - -#if id386 - andb $0xF0,%ah - orb $0x08,%ah // ceil mode, single precision -#endif - movl %eax,ceil_cw - - ret - diff --git a/code/unix/ui_video.c b/code/unix/ui_video.c deleted file mode 100644 index e1a3cce..0000000 --- a/code/unix/ui_video.c +++ /dev/null @@ -1,702 +0,0 @@ -#include "../client/client.h" -#include "../client/ui_local.h" - -extern void UI_ForceMenuOff( void ); - -static const char *s_driver_names[] = -{ - "[default OpenGL]", - "[Voodoo OpenGL]", - "[Custom ]", - 0 -}; - -static const char *s_drivers[] = -{ - OPENGL_DRIVER_NAME, - _3DFX_DRIVER_NAME, - "", - 0 -}; - -/* -==================================================================== - -MENU INTERACTION - -==================================================================== -*/ -static menuframework_s s_menu; - -static menulist_s s_graphics_options_list; -static menulist_s s_mode_list; -static menulist_s s_driver_list; -static menuslider_s s_tq_slider; -static menulist_s s_fs_box; -static menulist_s s_lighting_box; -static menulist_s s_allow_extensions_box; -static menulist_s s_texturebits_box; -static menulist_s s_colordepth_list; -static menulist_s s_geometry_box; -static menulist_s s_filter_box; -static menuaction_s s_driverinfo_action; -static menuaction_s s_apply_action; -static menuaction_s s_defaults_action; - -typedef struct -{ - int mode; - qboolean fullscreen; - int tq; - int lighting; - int colordepth; - int texturebits; - int geometry; - int filter; - int driver; - qboolean extensions; -} InitialVideoOptions_s; - -static InitialVideoOptions_s s_ivo; - -static InitialVideoOptions_s s_ivo_templates[] = -{ - { - 4, qtrue, 2, 0, 2, 2, 1, 1, 0, qtrue // JDC: this was tq 3 - }, - { - 3, qtrue, 2, 0, 0, 0, 1, 0, 0, qtrue - }, - { - 2, qtrue, 1, 0, 1, 0, 0, 0, 0, qtrue - }, - { - 1, qtrue, 1, 1, 1, 0, 0, 0, 0, qtrue - }, - { - 3, qtrue, 1, 0, 0, 0, 1, 0, 0, qtrue - } -}; - -#define NUM_IVO_TEMPLATES ( sizeof( s_ivo_templates ) / sizeof( s_ivo_templates[0] ) ) - -static void DrvInfo_MenuDraw( void ); -static const char * DrvInfo_MenuKey( int key ); - -static void GetInitialVideoVars( void ) -{ - s_ivo.colordepth = s_colordepth_list.curvalue; - s_ivo.driver = s_driver_list.curvalue; - s_ivo.mode = s_mode_list.curvalue; - s_ivo.fullscreen = s_fs_box.curvalue; - s_ivo.extensions = s_allow_extensions_box.curvalue; - s_ivo.tq = s_tq_slider.curvalue; - s_ivo.lighting = s_lighting_box.curvalue; - s_ivo.geometry = s_geometry_box.curvalue; - s_ivo.filter = s_filter_box.curvalue; - s_ivo.texturebits = s_texturebits_box.curvalue; -} - -static void CheckConfigVsTemplates( void ) -{ - int i; - - for ( i = 0; i < NUM_IVO_TEMPLATES; i++ ) - { - if ( s_driver_list.curvalue != 1 ) - if ( s_ivo_templates[i].colordepth != s_colordepth_list.curvalue ) - continue; -#if 0 - if ( s_ivo_templates[i].driver != s_driver_list.curvalue ) - continue; -#endif - if ( s_ivo_templates[i].mode != s_mode_list.curvalue ) - continue; - if ( s_driver_list.curvalue != 1 ) - if ( s_ivo_templates[i].fullscreen != s_fs_box.curvalue ) - continue; - if ( s_ivo_templates[i].tq != s_tq_slider.curvalue ) - continue; - if ( s_ivo_templates[i].lighting != s_lighting_box.curvalue ) - continue; - if ( s_ivo_templates[i].geometry != s_geometry_box.curvalue ) - continue; - if ( s_ivo_templates[i].filter != s_filter_box.curvalue ) - continue; -// if ( s_ivo_templates[i].texturebits != s_texturebits_box.curvalue ) -// continue; - s_graphics_options_list.curvalue = i; - return; - } - s_graphics_options_list.curvalue = 4; -} - -static void UpdateMenuItemValues( void ) -{ - if ( s_driver_list.curvalue == 1 ) - { - s_fs_box.curvalue = 1; - s_fs_box.generic.flags = QMF_GRAYED; - s_colordepth_list.curvalue = 1; - } - else - { - s_fs_box.generic.flags = 0; - } - - if ( s_fs_box.curvalue == 0 || s_driver_list.curvalue == 1 ) - { - s_colordepth_list.curvalue = 0; - s_colordepth_list.generic.flags = QMF_GRAYED; - } - else - { - s_colordepth_list.generic.flags = 0; - } - - if ( s_allow_extensions_box.curvalue == 0 ) - { - if ( s_texturebits_box.curvalue == 0 ) - { - s_texturebits_box.curvalue = 1; - } - } - - s_apply_action.generic.flags = QMF_GRAYED; - - if ( s_ivo.mode != s_mode_list.curvalue ) - { - s_apply_action.generic.flags = QMF_BLINK; - } - if ( s_ivo.fullscreen != s_fs_box.curvalue ) - { - s_apply_action.generic.flags = QMF_BLINK; - } - if ( s_ivo.extensions != s_allow_extensions_box.curvalue ) - { - s_apply_action.generic.flags = QMF_BLINK; - } - if ( s_ivo.tq != s_tq_slider.curvalue ) - { - s_apply_action.generic.flags = QMF_BLINK; - } - if ( s_ivo.lighting != s_lighting_box.curvalue ) - { - s_apply_action.generic.flags = QMF_BLINK; - } - if ( s_ivo.colordepth != s_colordepth_list.curvalue ) - { - s_apply_action.generic.flags = QMF_BLINK; - } - if ( s_ivo.driver != s_driver_list.curvalue ) - { - s_apply_action.generic.flags = QMF_BLINK; - } - if ( s_ivo.texturebits != s_texturebits_box.curvalue ) - { - s_apply_action.generic.flags = QMF_BLINK; - } - if ( s_ivo.geometry != s_geometry_box.curvalue ) - { - s_apply_action.generic.flags = QMF_BLINK; - } - if ( s_ivo.filter != s_filter_box.curvalue ) - { - s_apply_action.generic.flags = QMF_BLINK; - } - - CheckConfigVsTemplates(); -} - -static void SetMenuItemValues( void ) -{ - s_mode_list.curvalue = Cvar_VariableValue( "r_mode" ); - s_fs_box.curvalue = Cvar_VariableValue("r_fullscreen"); - s_allow_extensions_box.curvalue = Cvar_VariableValue("r_allowExtensions"); - s_tq_slider.curvalue = 3-Cvar_VariableValue( "r_picmip"); - if ( s_tq_slider.curvalue < 0 ) - { - s_tq_slider.curvalue = 0; - } - else if ( s_tq_slider.curvalue > 3 ) - { - s_tq_slider.curvalue = 3; - } - - s_lighting_box.curvalue = Cvar_VariableValue( "r_vertexLight" ) != 0; - switch ( ( int ) Cvar_VariableValue( "r_texturebits" ) ) - { - case 0: - default: - s_texturebits_box.curvalue = 0; - break; - case 16: - s_texturebits_box.curvalue = 1; - break; - case 32: - s_texturebits_box.curvalue = 2; - break; - } - - if ( !Q_stricmp( Cvar_VariableString( "r_textureMode" ), "GL_LINEAR_MIPMAP_NEAREST" ) ) - { - s_filter_box.curvalue = 0; - } - else - { - s_filter_box.curvalue = 1; - } - - if ( Cvar_VariableValue( "r_subdivisions" ) == 999 || - Cvar_VariableValue( "r_lodBias" ) > 0 ) - { - s_geometry_box.curvalue = 0; - } - else - { - s_geometry_box.curvalue = 1; - } - - switch ( ( int ) Cvar_VariableValue( "r_colorbits" ) ) - { - default: - case 0: - s_colordepth_list.curvalue = 0; - break; - case 16: - s_colordepth_list.curvalue = 1; - break; - case 32: - s_colordepth_list.curvalue = 2; - break; - } - - if ( s_fs_box.curvalue == 0 ) - { - s_colordepth_list.curvalue = 0; - } - if ( s_driver_list.curvalue == 1 ) - { - s_colordepth_list.curvalue = 1; - } -} - -static void FullscreenCallback( void *s ) -{ -} - -static void ModeCallback( void *s ) -{ - // clamp 3dfx video modes - if ( s_driver_list.curvalue == 1 ) - { - if ( s_mode_list.curvalue < 2 ) - { - s_mode_list.curvalue = 2; - } - else if ( s_mode_list.curvalue > 6 ) - { - s_mode_list.curvalue = 6; - } - } -} - -static void GraphicsOptionsCallback( void *s ) -{ - InitialVideoOptions_s *ivo = &s_ivo_templates[s_graphics_options_list.curvalue]; - - s_mode_list.curvalue = ivo->mode; - s_tq_slider.curvalue = ivo->tq; - s_lighting_box.curvalue = ivo->lighting; - s_colordepth_list.curvalue = ivo->colordepth; - s_texturebits_box.curvalue = ivo->texturebits; - s_geometry_box.curvalue = ivo->geometry; - s_filter_box.curvalue = ivo->filter; - s_fs_box.curvalue = ivo->fullscreen; -} - -static void TextureDetailCallback( void *s ) -{ -} - -static void TextureQualityCallback( void *s ) -{ -} - -static void ExtensionsCallback( void *s ) -{ -} - -static void ColorDepthCallback( void *s ) -{ -} - -static void DriverInfoCallback( void *s ) -{ - UI_PushMenu( DrvInfo_MenuDraw, DrvInfo_MenuKey ); -} - -static void LightingCallback( void * s ) -{ -} - -static void ApplyChanges( void *unused ) -{ - switch ( s_texturebits_box.curvalue ) - { - case 0: - Cvar_SetValue( "r_texturebits", 0 ); - Cvar_SetValue( "r_ext_compress_textures", 1 ); - break; - case 1: - Cvar_SetValue( "r_texturebits", 16 ); - Cvar_SetValue( "r_ext_compress_textures", 0 ); - break; - case 2: - Cvar_SetValue( "r_texturebits", 32 ); - Cvar_SetValue( "r_ext_compress_textures", 0 ); - break; - } - Cvar_SetValue( "r_picmip", 3 - s_tq_slider.curvalue ); - Cvar_SetValue( "r_allowExtensions", s_allow_extensions_box.curvalue ); - Cvar_SetValue( "r_mode", s_mode_list.curvalue ); - Cvar_SetValue( "r_fullscreen", s_fs_box.curvalue ); - if (*s_drivers[s_driver_list.curvalue] ) - Cvar_Set( "r_glDriver", ( char * ) s_drivers[s_driver_list.curvalue] ); - switch ( s_colordepth_list.curvalue ) - { - case 0: - Cvar_SetValue( "r_colorbits", 0 ); - Cvar_SetValue( "r_depthbits", 0 ); - Cvar_SetValue( "r_stencilbits", 0 ); - break; - case 1: - Cvar_SetValue( "r_colorbits", 16 ); - Cvar_SetValue( "r_depthbits", 16 ); - Cvar_SetValue( "r_stencilbits", 0 ); - break; - case 2: - Cvar_SetValue( "r_colorbits", 32 ); - Cvar_SetValue( "r_depthbits", 24 ); - break; - } - Cvar_SetValue( "r_vertexLight", s_lighting_box.curvalue ); - - if ( s_geometry_box.curvalue ) - { - Cvar_SetValue( "r_lodBias", 0 ); - Cvar_SetValue( "r_subdivisions", 4 ); - } - else - { - Cvar_SetValue( "r_lodBias", 1 ); - Cvar_SetValue( "r_subdivisions", 999 ); - } - - if ( s_filter_box.curvalue ) - { - Cvar_Set( "r_textureMode", "GL_LINEAR_MIPMAP_LINEAR" ); - } - else - { - Cvar_Set( "r_textureMode", "GL_LINEAR_MIPMAP_NEAREST" ); - } - - UI_ForceMenuOff(); - - CL_Vid_Restart_f(); - - VID_MenuInit(); - -// s_fs_box.curvalue = Cvar_VariableValue( "r_fullscreen" ); -} - -/* -** VID_MenuInit -*/ -void VID_MenuInit( void ) -{ - static const char *tq_names[] = - { - "compressed", - "16-bit", - "32-bit", - 0 - }; - - static const char *s_graphics_options_names[] = - { - "high quality", - "normal", - "fast", - "fastest", - "custom", - 0 - }; - - static const char *lighting_names[] = - { - "lightmap", - "vertex", - 0 - }; - - static const char *colordepth_names[] = - { - "default", - "16-bit", - "32-bit", - 0 - }; - - static const char *resolutions[] = - { - "[320 240 ]", - "[400 300 ]", - "[512 384 ]", - "[640 480 ]", - "[800 600 ]", - "[960 720 ]", - "[1024 768 ]", - "[1152 864 ]", - "[1280 960 ]", - "[1600 1200]", - "[2048 1536]", - "[856 480 W]", - 0 - }; - static const char *filter_names[] = - { - "bilinear", - "trilinear", - 0 - }; - static const char *quality_names[] = - { - "low", - "high", - 0 - }; - static const char *enabled_names[] = - { - "disabled", - "enabled", - 0 - }; - int y = 0; - int i; - char *p; - - s_menu.x = SCREEN_WIDTH * 0.50; - s_menu.nitems = 0; - s_menu.wrapAround = qtrue; - - s_graphics_options_list.generic.type = MTYPE_SPINCONTROL; - s_graphics_options_list.generic.name = "graphics mode"; - s_graphics_options_list.generic.x = 0; - s_graphics_options_list.generic.y = y; - s_graphics_options_list.generic.callback = GraphicsOptionsCallback; - s_graphics_options_list.itemnames = s_graphics_options_names; - - s_driver_list.generic.type = MTYPE_SPINCONTROL; - s_driver_list.generic.name = "driver"; - s_driver_list.generic.x = 0; - s_driver_list.generic.y = y += 18; - - p = Cvar_VariableString( "r_glDriver" ); - for (i = 0; s_drivers[i]; i++) { - if (strcmp(s_drivers[i], p) == 0) - break; - } - if (!s_drivers[i]) - i--; // go back one, to default 'custom' - s_driver_list.curvalue = i; - - s_driver_list.itemnames = s_driver_names; - - // references/modifies "r_allowExtensions" - s_allow_extensions_box.generic.type = MTYPE_SPINCONTROL; - s_allow_extensions_box.generic.x = 0; - s_allow_extensions_box.generic.y = y += 18; - s_allow_extensions_box.generic.name = "OpenGL extensions"; - s_allow_extensions_box.generic.callback = ExtensionsCallback; - s_allow_extensions_box.itemnames = enabled_names; - - // references/modifies "r_mode" - s_mode_list.generic.type = MTYPE_SPINCONTROL; - s_mode_list.generic.name = "video mode"; - s_mode_list.generic.x = 0; - s_mode_list.generic.y = y += 36; - s_mode_list.itemnames = resolutions; - s_mode_list.generic.callback = ModeCallback; - - // references "r_colorbits" - s_colordepth_list.generic.type = MTYPE_SPINCONTROL; - s_colordepth_list.generic.name = "color depth"; - s_colordepth_list.generic.x = 0; - s_colordepth_list.generic.y = y += 18; - s_colordepth_list.itemnames = colordepth_names; - s_colordepth_list.generic.callback = ColorDepthCallback; - - // references/modifies "r_fullscreen" - s_fs_box.generic.type = MTYPE_RADIOBUTTON; - s_fs_box.generic.x = 0; - s_fs_box.generic.y = y += 18; - s_fs_box.generic.name = "fullscreen"; - s_fs_box.generic.callback = FullscreenCallback; - - // references/modifies "r_vertexLight" - s_lighting_box.generic.type = MTYPE_SPINCONTROL; - s_lighting_box.generic.x = 0; - s_lighting_box.generic.y = y += 18; - s_lighting_box.generic.name = "lighting"; - s_lighting_box.itemnames = lighting_names; - s_lighting_box.generic.callback = LightingCallback; - - // references/modifies "r_lodBias" & "subdivisions" - s_geometry_box.generic.type = MTYPE_SPINCONTROL; - s_geometry_box.generic.x = 0; - s_geometry_box.generic.y = y += 18; - s_geometry_box.generic.name = "geometric detail"; - s_geometry_box.itemnames = quality_names; - - // references/modifies "r_picmip" - s_tq_slider.generic.type = MTYPE_SLIDER; - s_tq_slider.generic.x = 0; - s_tq_slider.generic.y = y += 18; - s_tq_slider.generic.name = "texture detail"; - s_tq_slider.generic.callback = TextureDetailCallback; - s_tq_slider.minvalue = 0; - s_tq_slider.maxvalue = 3; - - // references/modifies "r_textureBits" - s_texturebits_box.generic.type = MTYPE_SPINCONTROL; - s_texturebits_box.generic.x = 0; - s_texturebits_box.generic.y = y += 18; - s_texturebits_box.generic.name = "texture quality"; - s_texturebits_box.generic.callback = TextureQualityCallback; - s_texturebits_box.itemnames = tq_names; - - // references/modifies "r_textureMode" - s_filter_box.generic.type = MTYPE_SPINCONTROL; - s_filter_box.generic.x = 0; - s_filter_box.generic.y = y += 18; - s_filter_box.generic.name = "texture filter"; - s_filter_box.itemnames = filter_names; - - s_driverinfo_action.generic.type = MTYPE_ACTION; - s_driverinfo_action.generic.name = "driver information"; - s_driverinfo_action.generic.x = 0; - s_driverinfo_action.generic.y = y += 36; - s_driverinfo_action.generic.callback = DriverInfoCallback; - - s_apply_action.generic.type = MTYPE_ACTION; - s_apply_action.generic.name = "apply"; - s_apply_action.generic.x = 0; - s_apply_action.generic.y = y += 36; - s_apply_action.generic.callback = ApplyChanges; - s_apply_action.generic.flags = QMF_GRAYED; - - SetMenuItemValues(); - GetInitialVideoVars(); - - Menu_AddItem( &s_menu, ( void * ) &s_graphics_options_list ); - Menu_AddItem( &s_menu, ( void * ) &s_driver_list ); - Menu_AddItem( &s_menu, ( void * ) &s_allow_extensions_box ); - Menu_AddItem( &s_menu, ( void * ) &s_mode_list ); - Menu_AddItem( &s_menu, ( void * ) &s_colordepth_list ); - Menu_AddItem( &s_menu, ( void * ) &s_fs_box ); - Menu_AddItem( &s_menu, ( void * ) &s_lighting_box ); - Menu_AddItem( &s_menu, ( void * ) &s_geometry_box ); - Menu_AddItem( &s_menu, ( void * ) &s_tq_slider ); - Menu_AddItem( &s_menu, ( void * ) &s_texturebits_box ); - Menu_AddItem( &s_menu, ( void * ) &s_filter_box ); - - Menu_AddItem( &s_menu, ( void * ) &s_driverinfo_action ); - Menu_AddItem( &s_menu, ( void * ) &s_apply_action ); - - Menu_Center( &s_menu ); - s_menu.y -= 6; -} - -/* -================ -VID_MenuDraw -================ -*/ -void VID_MenuDraw (void) -{ - UpdateMenuItemValues(); - Menu_AdjustCursor( &s_menu, 1 ); - Menu_Draw( &s_menu ); -} - -/* -================ -VID_MenuKey -================ -*/ -const char *VID_MenuKey( int key ) -{ - menuframework_s *m = &s_menu; - static const char *sound = "sound/misc/menu1.wav"; - - if ( key == K_ENTER ) - { - if ( !Menu_SelectItem( m ) ) - ApplyChanges( NULL ); - return NULL; - } - return Default_MenuKey( m, key ); - -} - -static void DrvInfo_MenuDraw( void ) -{ - float labelColor[] = { 0, 1.0, 0, 1.0 }; - float textColor[] = { 1, 1, 1, 1 }; - int i = 14; - char extensionsString[1024], *eptr = extensionsString; - - SCR_DrawBigStringColor( BIGCHAR_WIDTH * 4, BIGCHAR_HEIGHT * 3, "VENDOR:", labelColor ); - SCR_DrawBigStringColor( BIGCHAR_WIDTH * 4, BIGCHAR_HEIGHT * 4, Cvar_VariableString( "gl_vendor" ), textColor ); - SCR_DrawBigStringColor( BIGCHAR_WIDTH * 4, BIGCHAR_HEIGHT * 5.5, "VERSION:", labelColor ); - SCR_DrawBigStringColor( BIGCHAR_WIDTH * 4, BIGCHAR_HEIGHT * 6.5, Cvar_VariableString( "gl_version" ), textColor ); - SCR_DrawBigStringColor( BIGCHAR_WIDTH * 4, BIGCHAR_HEIGHT * 8, "RENDERER:", labelColor ); - SCR_DrawBigStringColor( BIGCHAR_WIDTH * 4, BIGCHAR_HEIGHT * 9, Cvar_VariableString( "gl_renderer" ), textColor ); - SCR_DrawBigStringColor( BIGCHAR_WIDTH * 4, BIGCHAR_HEIGHT * 10.5, "PIXELFORMAT:", labelColor ); - SCR_DrawBigStringColor( BIGCHAR_WIDTH * 4, BIGCHAR_HEIGHT * 11.5, Cvar_VariableString( "gl_pixelformat" ), textColor ); - - SCR_DrawBigStringColor( BIGCHAR_WIDTH * 4, BIGCHAR_HEIGHT * 13, "EXTENSIONS:", labelColor ); - strcpy( extensionsString, Cvar_VariableString( "gl_extensions" ) ); - while ( i < 25 && *eptr ) - { - while ( *eptr ) - { - char buf[2] = " "; - int j = BIGCHAR_WIDTH * 6; - - while ( *eptr && *eptr != ' ' ) - { - buf[0] = *eptr; - SCR_DrawBigStringColor( j, i * BIGCHAR_HEIGHT, buf, textColor ); - j += BIGCHAR_WIDTH; - eptr++; - } - - i++; - - while ( *eptr && *eptr == ' ' ) - eptr++; - } - } -} - -static const char * DrvInfo_MenuKey( int key ) -{ - if ( key == K_ESCAPE ) - UI_PopMenu(); - return NULL; -} - - diff --git a/code/unix/unix_glw.h b/code/unix/unix_glw.h deleted file mode 100644 index 41a02f1..0000000 --- a/code/unix/unix_glw.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef __linux__ -#error You shouldnt be including this file on non-Linux platforms -#endif - -#ifndef __GLW_LINUX_H__ -#define __GLW_LINUX_H__ - -typedef struct -{ - void *OpenGLLib; // instance of OpenGL library - - FILE *log_fp; -} glwstate_t; - -extern glwstate_t glw_state; - -#endif diff --git a/code/unix/unix_main.c b/code/unix/unix_main.c deleted file mode 100644 index d2d415f..0000000 --- a/code/unix/unix_main.c +++ /dev/null @@ -1,809 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "../game/q_shared.h" -#include "../qcommon/qcommon.h" -#include "../renderer/tr_public.h" - -cvar_t *nostdout; - -// Structure containing functions exported from refresh DLL -refexport_t re; - -unsigned sys_frame_time; - -uid_t saved_euid; -qboolean stdin_active = qtrue; - -// ======================================================================= -// General routines -// ======================================================================= - -void Sys_BeginProfiling( void ) { -} - -/* -================= -Sys_In_Restart_f - -Restart the input subsystem -================= -*/ -void Sys_In_Restart_f( void ) -{ - IN_Shutdown(); - IN_Init(); -} - -void Sys_ConsoleOutput (char *string) -{ - if (nostdout && nostdout->value) - return; - - fputs(string, stdout); -} - -void Sys_Printf (char *fmt, ...) -{ - va_list argptr; - char text[1024]; - unsigned char *p; - - va_start (argptr,fmt); - vsprintf (text,fmt,argptr); - va_end (argptr); - - if (strlen(text) > sizeof(text)) - Sys_Error("memory overwrite in Sys_Printf"); - - if (nostdout && nostdout->value) - return; - - for (p = (unsigned char *)text; *p; p++) { - *p &= 0x7f; - if ((*p > 128 || *p < 32) && *p != 10 && *p != 13 && *p != 9) - printf("[%02x]", *p); - else - putc(*p, stdout); - } -} - -void Sys_Quit (void) -{ - CL_Shutdown (); - fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY); - _exit(0); -} - -void Sys_Init(void) -{ - Cmd_AddCommand ("in_restart", Sys_In_Restart_f); - -#if id386 - Sys_SetFPCW(); -#endif - -#if defined __linux__ -#if defined __i386__ - Cvar_Set( "arch", "linux i386" ); -#elif defined __alpha__ - Cvar_Set( "arch", "linux alpha" ); -#elif defined __sparc__ - Cvar_Set( "arch", "linux sparc" ); -#else - Cvar_Set( "arch", "linux unknown" ); -#endif -#elif defined __sun__ -#if defined __i386__ - Cvar_Set( "arch", "solaris x86" ); -#elif defined __sparc__ - Cvar_Set( "arch", "solaris sparc" ); -#else - Cvar_Set( "arch", "solaris unknown" ); -#endif -#elif defined __sgi__ -#if defined __mips__ - Cvar_Set( "arch", "sgi mips" ); -#else - Cvar_Set( "arch", "sgi unknown" ); -#endif -#else - Cvar_Set( "arch", "unknown" ); -#endif - - IN_Init(); - -} - -void Sys_Error( const char *error, ...) -{ - va_list argptr; - char string[1024]; - -// change stdin to non blocking - fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY); - - CL_Shutdown (); - - va_start (argptr,error); - vsprintf (string,error,argptr); - va_end (argptr); - fprintf(stderr, "Error: %s\n", string); - - _exit (1); - -} - -void Sys_Warn (char *warning, ...) -{ - va_list argptr; - char string[1024]; - - va_start (argptr,warning); - vsprintf (string,warning,argptr); - va_end (argptr); - fprintf(stderr, "Warning: %s", string); -} - -/* -============ -Sys_FileTime - -returns -1 if not present -============ -*/ -int Sys_FileTime (char *path) -{ - struct stat buf; - - if (stat (path,&buf) == -1) - return -1; - - return buf.st_mtime; -} - -void floating_point_exception_handler(int whatever) -{ -// Sys_Warn("floating point exception\n"); - signal(SIGFPE, floating_point_exception_handler); -} - -char *Sys_ConsoleInput(void) -{ - static char text[256]; - int len; - fd_set fdset; - struct timeval timeout; - - if (!com_dedicated || !com_dedicated->value) - return NULL; - - if (!stdin_active) - return NULL; - - FD_ZERO(&fdset); - FD_SET(0, &fdset); // stdin - timeout.tv_sec = 0; - timeout.tv_usec = 0; - if (select (1, &fdset, NULL, NULL, &timeout) == -1 || !FD_ISSET(0, &fdset)) - return NULL; - - len = read (0, text, sizeof(text)); - if (len == 0) { // eof! - stdin_active = qfalse; - return NULL; - } - - if (len < 1) - return NULL; - text[len-1] = 0; // rip off the /n and terminate - - return text; -} - -/*****************************************************************************/ - -static void *game_library; - -#ifdef __i386__ - const char *gamename = "qagamei386.so"; -#elif defined __alpha__ - const char *gamename = "qagameaxp.so"; -#elif defined __mips__ - const char *gamename = "qagamemips.so"; -#else -#error Unknown arch -#endif - -/* -================= -Sys_UnloadGame -================= -*/ -void Sys_UnloadGame (void) -{ - Com_Printf("------ Unloading %s ------\n", gamename); - if (game_library) - dlclose (game_library); - game_library = NULL; -} - -/* -================= -Sys_GetGameAPI - -Loads the game dll -================= -*/ -void *Sys_GetGameAPI (void *parms) -{ - void *(*GetGameAPI) (void *); - - char name[MAX_OSPATH]; - char curpath[MAX_OSPATH]; - char *path; - if (game_library) - Com_Error (ERR_FATAL, "Sys_GetGameAPI without Sys_UnloadingGame"); - - // check the current debug directory first for development purposes - getcwd(curpath, sizeof(curpath)); - - Com_Printf("------- Loading %s -------\n", gamename); - Com_sprintf (name, sizeof(name), "%s/%s", curpath, gamename); - - game_library = dlopen (name, RTLD_LAZY ); - if (game_library) - Com_DPrintf ("LoadLibrary (%s)\n",name); - else { - Com_Printf( "LoadLibrary(\"%s\") failed\n", name); - Com_Printf( "...reason: '%s'\n", dlerror() ); - Com_Error( ERR_FATAL, "Couldn't load game" ); - } - - GetGameAPI = (void *)dlsym (game_library, "GetGameAPI"); - if (!GetGameAPI) - { - Sys_UnloadGame (); - return NULL; - } - - return GetGameAPI (parms); -} - -/*****************************************************************************/ - -static void *cgame_library; - -/* -================= -Sys_UnloadGame -================= -*/ -void Sys_UnloadCGame (void) -{ - if (cgame_library) - dlclose (cgame_library); - cgame_library = NULL; -} - -/* -================= -Sys_GetGameAPI - -Loads the game dll -================= -*/ -void *Sys_GetCGameAPI (void) -{ - void *(*api) (void); - - char name[MAX_OSPATH]; - char curpath[MAX_OSPATH]; -#ifdef __i386__ - const char *cgamename = "cgamei386.so"; -#elif defined __alpha__ - const char *cgamename = "cgameaxp.so"; -#elif defined __mips__ - const char *cgamename = "cgamemips.so"; -#else -#error Unknown arch -#endif - - Sys_UnloadCGame(); - - getcwd(curpath, sizeof(curpath)); - - Com_Printf("------- Loading %s -------\n", cgamename); - - sprintf (name, "%s/%s", curpath, cgamename); - cgame_library = dlopen (name, RTLD_LAZY ); - if (!cgame_library) - { - Com_Printf ("LoadLibrary (%s)\n",name); - Com_Error( ERR_FATAL, "Couldn't load cgame: %s", dlerror() ); - } - - api = (void *)dlsym (cgame_library, "GetCGameAPI"); - if (!api) - { - Com_Error( ERR_FATAL, "dlsym() failed on GetCGameAPI" ); - } - - return api(); -} - -/*****************************************************************************/ - -static void *ui_library; - -/* -================= -Sys_UnloadUI -================= -*/ -void Sys_UnloadUI(void) -{ - if (ui_library) - dlclose (ui_library); - ui_library = NULL; -} - -/* -================= -Sys_GetUIAPI - -Loads the ui dll -================= -*/ -void *Sys_GetUIAPI (void) -{ - void *api; - - char name[MAX_OSPATH]; - char curpath[MAX_OSPATH]; -#ifdef __i386__ - const char *uiname = "uii386.so"; -#elif defined __alpha__ - const char *uiname = "uiaxp.so"; -#elif defined __mips__ - const char *uiname = "uimips.so"; -#else -#error Unknown arch -#endif - - Sys_UnloadUI(); - - getcwd(curpath, sizeof(curpath)); - - Com_Printf("------- Loading %s -------\n", uiname); - - sprintf (name, "%s/%s", curpath, uiname); - ui_library = dlopen (name, RTLD_LAZY ); - if (!ui_library) - { - Com_Printf ("LoadLibrary (%s)\n",name); - Com_Error( ERR_FATAL, "Couldn't load ui: %s", dlerror() ); - } - - api = (void *)dlsym (ui_library, "GetUIAPI"); - if (!api) - { - Com_Error( ERR_FATAL, "dlsym() failed on GetUIAPI" ); - } - - return api; -} - -/*****************************************************************************/ - -void *Sys_GetRefAPI (void *parms) -{ - return (void *)GetRefAPI(REF_API_VERSION, parms); -} - -/* -======================================================================== - -BACKGROUND FILE STREAMING - -======================================================================== -*/ - -typedef struct { -#if 0 - HANDLE threadHandle; - int threadId; - CRITICAL_SECTION crit; -#endif - FILE *file; - byte *buffer; - qboolean eof; - int bufferSize; - int streamPosition; // next byte to be returned by Sys_StreamRead - int threadPosition; // next byte to be read from file -} streamState_t; - -streamState_t stream; - -/* -=============== -Sys_StreamThread - -A thread will be sitting in this loop forever -================ -*/ -void Sys_StreamThread( void ) -{ - int buffer; - int count; - int readCount; - int bufferPoint; - int r; - -//Loop here -// EnterCriticalSection (&stream.crit); - - // if there is any space left in the buffer, fill it up - if ( !stream.eof ) { - count = stream.bufferSize - (stream.threadPosition - stream.streamPosition); - if ( count ) { - bufferPoint = stream.threadPosition % stream.bufferSize; - buffer = stream.bufferSize - bufferPoint; - readCount = buffer < count ? buffer : count; - r = fread( stream.buffer + bufferPoint, 1, readCount, stream.file ); - stream.threadPosition += r; - - if ( r != readCount ) - stream.eof = qtrue; - } - } - -// LeaveCriticalSection (&stream.crit); -} - -/* -=============== -Sys_InitStreamThread - -================ -*/ -void Sys_InitStreamThread( void ) -{ -} - -/* -=============== -Sys_ShutdownStreamThread - -================ -*/ -void Sys_ShutdownStreamThread( void ) -{ -} - - -/* -=============== -Sys_BeginStreamedFile - -================ -*/ -void Sys_BeginStreamedFile( fileHandle_t f, int readAhead ) -{ - if ( stream.file ) { - Com_Error( ERR_FATAL, "Sys_BeginStreamedFile: unclosed stream"); - } - - stream.file = f; - stream.buffer = Z_Malloc( readAhead ); - stream.bufferSize = readAhead; - stream.streamPosition = 0; - stream.threadPosition = 0; - stream.eof = qfalse; - - // let the thread start running -// LeaveCriticalSection( &stream.crit ); -} - -/* -=============== -Sys_EndStreamedFile - -================ -*/ -void Sys_EndStreamedFile( FILE *f ) -{ - if ( f != stream.file ) { - Com_Error( ERR_FATAL, "Sys_EndStreamedFile: wrong file"); - } - // don't leave critical section until another stream is started -// EnterCriticalSection( &stream.crit ); - - stream.file = NULL; - Z_Free( stream.buffer ); -} - - -/* -=============== -Sys_StreamedRead - -================ -*/ -int Sys_StreamedRead( void *buffer, int size, int count, FILE *f ) -{ - int available; - int remaining; - int sleepCount; - int copy; - int bufferCount; - int bufferPoint; - byte *dest; - - dest = (byte *)buffer; - remaining = size * count; - - if ( remaining <= 0 ) { - Com_Error( ERR_FATAL, "Streamed read with non-positive size" ); - } - - sleepCount = 0; - while ( remaining > 0 ) { - available = stream.threadPosition - stream.streamPosition; - if ( !available ) { - if (stream.eof) - break; - Sys_StreamThread(); - continue; - } - - bufferPoint = stream.streamPosition % stream.bufferSize; - bufferCount = stream.bufferSize - bufferPoint; - - copy = available < bufferCount ? available : bufferCount; - if ( copy > remaining ) { - copy = remaining; - } - memcpy( dest, stream.buffer + bufferPoint, copy ); - stream.streamPosition += copy; - dest += copy; - remaining -= copy; - } - - return (count * size - remaining) / size; -} - -/* -=============== -Sys_StreamSeek - -================ -*/ -void Sys_StreamSeek( FILE *f, int offset, int origin ) { - - // halt the thread -// EnterCriticalSection( &stream.crit ); - - // clear to that point - fseek( f, offset, origin ); - stream.streamPosition = 0; - stream.threadPosition = 0; - stream.eof = qfalse; - - // let the thread start running at the new position -// LeaveCriticalSection( &stream.crit ); -} - - -/* -======================================================================== - -EVENT LOOP - -======================================================================== -*/ - -#define MAX_QUED_EVENTS 64 -#define MASK_QUED_EVENTS ( MAX_QUED_EVENTS - 1 ) - -sysEvent_t eventQue[MAX_QUED_EVENTS]; -int eventHead, eventTail; -byte sys_packetReceived[MAX_MSGLEN]; - -/* -================ -Sys_QueEvent - -A time of 0 will get the current time -Ptr should either be null, or point to a block of data that can -be freed by the game later. -================ -*/ -void Sys_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ) { - sysEvent_t *ev; - - ev = &eventQue[ eventHead & MASK_QUED_EVENTS ]; - eventHead++; - - if ( time == 0 ) { - time = Sys_Milliseconds(); - } - - ev->evTime = time; - ev->evType = type; - ev->evValue = value; - ev->evValue2 = value2; - ev->evPtrLength = ptrLength; - ev->evPtr = ptr; -} - -/* -================ -Sys_GetEvent - -================ -*/ -sysEvent_t Sys_GetEvent( void ) { - sysEvent_t ev; - char *s; - msg_t netmsg; - netadr_t adr; - - // return if we have data - if ( eventHead > eventTail ) { - eventTail++; - return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ]; - } - - // pump the message loop - // in vga this calls KBD_Update, under X, it calls GetEvent - Sys_SendKeyEvents (); - - // check for console commands - s = Sys_ConsoleInput(); - if ( s ) { - char *b; - int len; - - len = strlen( s ) + 1; - b = malloc( len ); - strcpy( b, s ); - Sys_QueEvent( 0, SE_CONSOLE, 0, 0, len, b ); - } - - // check for other input devices - IN_Frame(); - - // check for network packets - MSG_Init( &netmsg, sys_packetReceived, sizeof( sys_packetReceived ) ); - if ( Sys_GetPacket ( &adr, &netmsg ) ) { - netadr_t *buf; - int len; - - // copy out to a seperate buffer for qeueing - len = sizeof( netadr_t ) + netmsg.cursize; - buf = malloc( len ); - *buf = adr; - memcpy( buf+1, netmsg.data, netmsg.cursize ); - Sys_QueEvent( 0, SE_PACKET, 0, 0, len, buf ); - } - - // return if we have data - if ( eventHead > eventTail ) { - eventTail++; - return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ]; - } - - // create an empty event to return - - memset( &ev, 0, sizeof( ev ) ); - ev.evTime = Sys_Milliseconds(); - - return ev; -} - -/*****************************************************************************/ - -void Sys_AppActivate (void) -{ -} - -char *Sys_GetClipboardData(void) -{ - return NULL; -} - -void Sys_Print( const char *msg ) -{ - fputs(msg, stderr); -} - -int main (int argc, char **argv) -{ - int oldtime, newtime; - int len, i; - char *cmdline; - void SetProgramPath(char *path); - - // go back to real user for config loads - saved_euid = geteuid(); - seteuid(getuid()); - - SetProgramPath(argv[0]); - - // merge the command line, this is kinda silly - for (len = 1, i = 1; i < argc; i++) - len += strlen(argv[i]) + 1; - cmdline = malloc(len); - *cmdline = 0; - for (i = 1; i < argc; i++) { - if (i > 1) - strcat(cmdline, " "); - strcat(cmdline, argv[i]); - } - Com_Init(cmdline); - NET_Init(); - - fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY); - - nostdout = Cvar_Get("nostdout", "0", 0); - if (!nostdout->value) { - fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY); -// printf ("Linux Quake -- Version %0.3f\n", LINUX_VERSION); - } - - while (1) - { - // set low precision every frame, because some system calls - // reset it arbitrarily - Sys_LowFPPrecision (); - - Com_Frame (); - } -} - -#if 0 -/* -================ -Sys_MakeCodeWriteable -================ -*/ -void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) -{ - - int r; - unsigned long addr; - int psize = getpagesize(); - - addr = (startaddr & ~(psize-1)) - psize; - -// fprintf(stderr, "writable code %lx(%lx)-%lx, length=%lx\n", startaddr, -// addr, startaddr+length, length); - - r = mprotect((char*)addr, length + startaddr - addr + psize, 7); - - if (r < 0) - Sys_Error("Protection change failed\n"); - -} - -#endif diff --git a/code/unix/unix_net.c b/code/unix/unix_net.c deleted file mode 100644 index 72806f2..0000000 --- a/code/unix/unix_net.c +++ /dev/null @@ -1,443 +0,0 @@ -// unix_net.c - -#include "../game/q_shared.h" -#include "../qcommon/qcommon.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef NeXT -#include -#endif - -static cvar_t *noudp; - -netadr_t net_local_adr; - -int ip_socket; -int ipx_socket; - -#define MAX_IPS 16 -static int numIP; -static byte localIP[MAX_IPS][4]; - -int NET_Socket (char *net_interface, int port); -char *NET_ErrorString (void); - -//============================================================================= - -void NetadrToSockadr (netadr_t *a, struct sockaddr_in *s) -{ - memset (s, 0, sizeof(*s)); - - if (a->type == NA_BROADCAST) - { - s->sin_family = AF_INET; - - s->sin_port = a->port; - *(int *)&s->sin_addr = -1; - } - else if (a->type == NA_IP) - { - s->sin_family = AF_INET; - - *(int *)&s->sin_addr = *(int *)&a->ip; - s->sin_port = a->port; - } -} - -void SockadrToNetadr (struct sockaddr_in *s, netadr_t *a) -{ - *(int *)&a->ip = *(int *)&s->sin_addr; - a->port = s->sin_port; - a->type = NA_IP; -} - -char *NET_BaseAdrToString (netadr_t a) -{ - static char s[64]; - - Com_sprintf (s, sizeof(s), "%i.%i.%i.%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3]); - - return s; -} - -/* -============= -Sys_StringToAdr - -idnewt -192.246.40.70 -============= -*/ -qboolean Sys_StringToSockaddr (const char *s, struct sockaddr *sadr) -{ - struct hostent *h; - char *colon; - - memset (sadr, 0, sizeof(*sadr)); - ((struct sockaddr_in *)sadr)->sin_family = AF_INET; - - ((struct sockaddr_in *)sadr)->sin_port = 0; - - if ( s[0] >= '0' && s[0] <= '9') - { - *(int *)&((struct sockaddr_in *)sadr)->sin_addr = inet_addr(s); - } - else - { - if (! (h = gethostbyname(s)) ) - return qfalse; - *(int *)&((struct sockaddr_in *)sadr)->sin_addr = *(int *)h->h_addr_list[0]; - } - - return qtrue; -} - -/* -============= -Sys_StringToAdr - -localhost -idnewt -idnewt:28000 -192.246.40.70 -192.246.40.70:28000 -============= -*/ -qboolean Sys_StringToAdr (const char *s, netadr_t *a) -{ - struct sockaddr_in sadr; - - if (!Sys_StringToSockaddr (s, (struct sockaddr *)&sadr)) - return qfalse; - - SockadrToNetadr (&sadr, a); - - return qtrue; -} - - -//============================================================================= - -qboolean Sys_GetPacket (netadr_t *net_from, msg_t *net_message) -{ - int ret; - struct sockaddr_in from; - int fromlen; - int net_socket; - int protocol; - int err; - - for (protocol = 0 ; protocol < 2 ; protocol++) - { - if (protocol == 0) - net_socket = ip_socket; - else - net_socket = ipx_socket; - - if (!net_socket) - continue; - - fromlen = sizeof(from); - ret = recvfrom (net_socket, net_message->data, net_message->maxsize - , 0, (struct sockaddr *)&from, &fromlen); - - SockadrToNetadr (&from, net_from); - - if (ret == -1) - { - err = errno; - - if (err == EWOULDBLOCK || err == ECONNREFUSED) - continue; - Com_Printf ("NET_GetPacket: %s from %s\n", NET_ErrorString(), - NET_AdrToString(*net_from)); - continue; - } - - if (ret == net_message->maxsize) - { - Com_Printf ("Oversize packet from %s\n", NET_AdrToString (*net_from)); - continue; - } - - net_message->cursize = ret; - return qtrue; - } - - return qfalse; -} - -//============================================================================= - -void Sys_SendPacket( int length, const void *data, netadr_t to ) -{ - int ret; - struct sockaddr_in addr; - int net_socket; - - if (to.type == NA_BROADCAST) - { - net_socket = ip_socket; - } - else if (to.type == NA_IP) - { - net_socket = ip_socket; - } - else if (to.type == NA_IPX) - { - net_socket = ipx_socket; - } - else if (to.type == NA_BROADCAST_IPX) - { - net_socket = ipx_socket; - } - else { - Com_Error (ERR_FATAL, "NET_SendPacket: bad address type"); - return; - } - - if (!net_socket) - return; - - NetadrToSockadr (&to, &addr); - - ret = sendto (net_socket, data, length, 0, (struct sockaddr *)&addr, sizeof(addr) ); - if (ret == -1) - { - Com_Printf ("NET_SendPacket ERROR: %s to %s\n", NET_ErrorString(), - NET_AdrToString (to)); - } -} - - -//============================================================================= - -/* -================== -Sys_IsLANAddress - -LAN clients will have their rate var ignored -================== -*/ -qboolean Sys_IsLANAddress (netadr_t adr) { - int i; - if ( adr.type == NA_LOOPBACK ) { - return qtrue; - } - - // FIXME: ipx? - - if ( adr.type == NA_IP ) { - for ( i = 0 ; i < numIP ; i++ ) { - // assuming a class C network, which may not be smart... - if ( adr.ip[0] == localIP[i][0] - && adr.ip[1] == localIP[i][1] - && adr.ip[2] == localIP[i][2] ) { - return qtrue; - } - } - } - - return qfalse; -} - -/* -===================== -NET_GetLocalAddress -===================== -*/ -void NET_GetLocalAddress( void ) { - char hostname[256]; - struct hostent *hostInfo; - int error; - char *p; - int ip; - int n; - - if ( gethostname( hostname, 256 ) == -1 ) { - return; - } - - hostInfo = gethostbyname( hostname ); - if ( !hostInfo ) { - return; - } - - Com_Printf( "Hostname: %s\n", hostInfo->h_name ); - n = 0; - while( ( p = hostInfo->h_aliases[n++] ) != NULL ) { - Com_Printf( "Alias: %s\n", p ); - } - - if ( hostInfo->h_addrtype != AF_INET ) { - return; - } - - numIP = 0; - while( ( p = hostInfo->h_addr_list[numIP++] ) != NULL && numIP < MAX_IPS ) { - ip = ntohl( *(int *)p ); - localIP[ numIP ][0] = p[0]; - localIP[ numIP ][1] = p[1]; - localIP[ numIP ][2] = p[2]; - localIP[ numIP ][3] = p[3]; - Com_Printf( "IP: %i.%i.%i.%i\n", ( ip >> 24 ) & 0xff, ( ip >> 16 ) & 0xff, ( ip >> 8 ) & 0xff, ip & 0xff ); - } -} - -/* -==================== -NET_OpenIP -==================== -*/ -void NET_OpenIP (void) -{ - cvar_t *ip; - int port; - int i; - - ip = Cvar_Get ("net_ip", "localhost", 0); - - port = Cvar_Get("net_port", va("%i", PORT_SERVER), 0)->value; - - for ( i = 0 ; i < 10 ; i++ ) { - ip_socket = NET_IPSocket (ip->string, port + i); - if ( ip_socket ) { - Cvar_SetValue( "net_port", port + i ); - NET_GetLocalAddress(); - return; - } - } - Com_Error (ERR_FATAL, "Couldn't allocate IP port"); -} - - -/* -==================== -NET_Init -==================== -*/ -void NET_Init (void) -{ - noudp = Cvar_Get ("net_noudp", "0", 0); - // open sockets - if (! noudp->value) { - NET_OpenIP (); - } -} - - -/* -==================== -NET_Socket -==================== -*/ -int NET_IPSocket (char *net_interface, int port) -{ - int newsocket; - struct sockaddr_in address; - qboolean _qtrue = qtrue; - int i = 1; - - if ( net_interface ) { - Com_Printf("Opening IP socket: %s:%i\n", net_interface, port ); - } else { - Com_Printf("Opening IP socket: localhost:%i\n", port ); - } - - if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) - { - Com_Printf ("ERROR: UDP_OpenSocket: socket: %s", NET_ErrorString()); - return 0; - } - - // make it non-blocking - if (ioctl (newsocket, FIONBIO, &_qtrue) == -1) - { - Com_Printf ("ERROR: UDP_OpenSocket: ioctl FIONBIO:%s\n", NET_ErrorString()); - return 0; - } - - // make it broadcast capable - if (setsockopt(newsocket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) == -1) - { - Com_Printf ("ERROR: UDP_OpenSocket: setsockopt SO_BROADCAST:%s\n", NET_ErrorString()); - return 0; - } - - if (!net_interface || !net_interface[0] || !stricmp(net_interface, "localhost")) - address.sin_addr.s_addr = INADDR_ANY; - else - Sys_StringToSockaddr (net_interface, (struct sockaddr *)&address); - - if (port == PORT_ANY) - address.sin_port = 0; - else - address.sin_port = htons((short)port); - - address.sin_family = AF_INET; - - if( bind (newsocket, (void *)&address, sizeof(address)) == -1) - { - Com_Printf ("ERROR: UDP_OpenSocket: bind: %s\n", NET_ErrorString()); - close (newsocket); - return 0; - } - - return newsocket; -} - -/* -==================== -NET_Shutdown -==================== -*/ -void NET_Shutdown (void) -{ - if (ip_socket) { - close(ip_socket); - ip_socket = 0; - } -} - - -/* -==================== -NET_ErrorString -==================== -*/ -char *NET_ErrorString (void) -{ - int code; - - code = errno; - return strerror (code); -} - -// sleeps msec or until net socket is ready -void NET_Sleep(int msec) -{ - struct timeval timeout; - fd_set fdset; - extern qboolean stdin_active; - - if (!ip_socket || !com_dedicated->integer) - return; // we're not a server, just run full speed - - FD_ZERO(&fdset); - if (stdin_active) - FD_SET(0, &fdset); // stdin is processed too - FD_SET(ip_socket, &fdset); // network socket - timeout.tv_sec = msec/1000; - timeout.tv_usec = (msec%1000)*1000; - select(ip_socket+1, &fdset, NULL, NULL, &timeout); -} - diff --git a/code/unix/unix_shared.c b/code/unix/unix_shared.c deleted file mode 100644 index 558d7d9..0000000 --- a/code/unix/unix_shared.c +++ /dev/null @@ -1,369 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../game/q_shared.h" -#include "../qcommon/qcommon.h" - -//=============================================================================== - -// Used to determine CD Path -static char programpath[MAX_OSPATH]; - -/* -================ -Sys_Milliseconds -================ -*/ -int curtime; -int sys_timeBase; -int Sys_Milliseconds (void) -{ - struct timeval tp; - struct timezone tzp; - - gettimeofday(&tp, &tzp); - - if (!sys_timeBase) - { - sys_timeBase = tp.tv_sec; - return tp.tv_usec/1000; - } - - curtime = (tp.tv_sec - sys_timeBase)*1000 + tp.tv_usec/1000; - - return curtime; -} - -void Sys_Mkdir( const char *path ) -{ - mkdir (path, 0777); -} - -char *strlwr (char *s) -{ - while (*s) { - *s = tolower(*s); - s++; - } -} - -//============================================ - -/* Like glob_match, but match PATTERN against any final segment of TEXT. */ -static int glob_match_after_star(char *pattern, char *text) -{ - register char *p = pattern, *t = text; - register char c, c1; - - while ((c = *p++) == '?' || c == '*') - if (c == '?' && *t++ == '\0') - return 0; - - if (c == '\0') - return 1; - - if (c == '\\') - c1 = *p; - else - c1 = c; - - while (1) { - if ((c == '[' || *t == c1) && glob_match(p - 1, t)) - return 1; - if (*t++ == '\0') - return 0; - } -} - -/* Return nonzero if PATTERN has any special globbing chars in it. */ -static int glob_pattern_p(char *pattern) -{ - register char *p = pattern; - register char c; - int open = 0; - - while ((c = *p++) != '\0') - switch (c) { - case '?': - case '*': - return 1; - - case '[': /* Only accept an open brace if there is a close */ - open++; /* brace to match it. Bracket expressions must be */ - continue; /* complete, according to Posix.2 */ - case ']': - if (open) - return 1; - continue; - - case '\\': - if (*p++ == '\0') - return 0; - } - - return 0; -} - -/* Match the pattern PATTERN against the string TEXT; - return 1 if it matches, 0 otherwise. - - A match means the entire string TEXT is used up in matching. - - In the pattern string, `*' matches any sequence of characters, - `?' matches any character, [SET] matches any character in the specified set, - [!SET] matches any character not in the specified set. - - A set is composed of characters or ranges; a range looks like - character hyphen character (as in 0-9 or A-Z). - [0-9a-zA-Z_] is the set of characters allowed in C identifiers. - Any other character in the pattern must be matched exactly. - - To suppress the special syntactic significance of any of `[]*?!-\', - and match the character exactly, precede it with a `\'. -*/ - -int glob_match(char *pattern, char *text) -{ - register char *p = pattern, *t = text; - register char c; - - while ((c = *p++) != '\0') - switch (c) { - case '?': - if (*t == '\0') - return 0; - else - ++t; - break; - - case '\\': - if (*p++ != *t++) - return 0; - break; - - case '*': - return glob_match_after_star(p, t); - - case '[': - { - register char c1 = *t++; - int invert; - - if (!c1) - return (0); - - invert = ((*p == '!') || (*p == '^')); - if (invert) - p++; - - c = *p++; - while (1) { - register char cstart = c, cend = c; - - if (c == '\\') { - cstart = *p++; - cend = cstart; - } - if (c == '\0') - return 0; - - c = *p++; - if (c == '-' && *p != ']') { - cend = *p++; - if (cend == '\\') - cend = *p++; - if (cend == '\0') - return 0; - c = *p++; - } - if (c1 >= cstart && c1 <= cend) - goto match; - if (c == ']') - break; - } - if (!invert) - return 0; - break; - - match: - /* Skip the rest of the [...] construct that already matched. */ - while (c != ']') { - if (c == '\0') - return 0; - c = *p++; - if (c == '\0') - return 0; - else if (c == '\\') - ++p; - } - if (invert) - return 0; - break; - } - - default: - if (c != *t++) - return 0; - } - - return *t == '\0'; -} - -//============================================ - -#define MAX_FOUND_FILES 0x1000 - -char **Sys_ListFiles( const char *directory, const char *extension, int *numfiles, qboolean wantsubs ) -{ - struct dirent *d; - char *p; - DIR *fdir; - qboolean dironly = wantsubs; - char search[MAX_OSPATH]; - int nfiles; - char **listCopy; - char *list[MAX_FOUND_FILES]; - int flag; - int i; - struct stat st; - - int extLen; - - if ( !extension) - extension = ""; - - if ( extension[0] == '/' && extension[1] == 0 ) { - extension = ""; - dironly = qtrue; - } - - extLen = strlen( extension ); - - // search - nfiles = 0; - - if ((fdir = opendir(directory)) == NULL) { - *numfiles = 0; - return NULL; - } - - while ((d = readdir(fdir)) != NULL) { - Com_sprintf(search, sizeof(search), "%s/%s", directory, d->d_name); - if (stat(search, &st) == -1) - continue; - if ((dironly && !(st.st_mode & S_IFDIR)) || - (!dironly && (st.st_mode & S_IFDIR))) - continue; - - if (*extension) { - if ( strlen( d->d_name ) < strlen( extension ) || - Q_stricmp( - d->d_name + strlen( d->d_name ) - strlen( extension ), - extension ) ) { - continue; // didn't match - } - } - - if ( nfiles == MAX_FOUND_FILES - 1 ) - break; - list[ nfiles ] = CopyString( d->d_name ); - nfiles++; - } - - list[ nfiles ] = 0; - - closedir(fdir); - - // return a copy of the list - *numfiles = nfiles; - - if ( !nfiles ) { - return NULL; - } - - listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) ); - for ( i = 0 ; i < nfiles ; i++ ) { - listCopy[i] = list[i]; - } - listCopy[i] = NULL; - - return listCopy; -} - -void Sys_FreeFileList( char **list ) { - int i; - - if ( !list ) { - return; - } - - for ( i = 0 ; list[i] ; i++ ) { - Z_Free( list[i] ); - } - - Z_Free( list ); -} - -char *Sys_Cwd( void ) -{ - static char cwd[MAX_OSPATH]; - - getcwd( cwd, sizeof( cwd ) - 1 ); - cwd[MAX_OSPATH-1] = 0; - - return cwd; -} - -void SetProgramPath(char *path) -{ - char *p; - - Q_strncpyz(programpath, path, sizeof(programpath)); - if ((p = strrchr(programpath, '/')) != NULL) - *p = 0; // remove program name, leave only path -} - -char *Sys_DefaultCDPath(void) -{ - if (*programpath) - return programpath; - else - return Sys_Cwd(); -} - -char *Sys_DefaultBasePath(void) -{ - char *p; - static char basepath[MAX_OSPATH]; - int e; - - if ((p = getenv("HOME")) != NULL) { - Q_strncpyz(basepath, p, sizeof(basepath)); - Q_strcat(basepath, sizeof(basepath), "/.q3a"); - if (mkdir(basepath, 0777)) { - if (errno != EEXIST) - Sys_Error("Unable to create directory \"%s\", error is %s(%d)\n", basepath, strerror(errno), errno); - } - return basepath; - } - return ""; // assume current dir -} - -//============================================ - -int Sys_GetProcessorId( void ) -{ - return CPUID_GENERIC; -} - -void Sys_ShowConsole( int visLevel, qboolean quitOnClose ) -{ -} - - diff --git a/code/unix/vssver.scc b/code/unix/vssver.scc deleted file mode 100644 index d8a9f55..0000000 Binary files a/code/unix/vssver.scc and /dev/null differ diff --git a/code/vssver.scc b/code/vssver.scc deleted file mode 100644 index 43c6185..0000000 Binary files a/code/vssver.scc and /dev/null differ diff --git a/code/win32/background.bmp b/code/win32/background.bmp deleted file mode 100644 index 88b3dab..0000000 Binary files a/code/win32/background.bmp and /dev/null differ diff --git a/code/win32/bink.h b/code/win32/bink.h deleted file mode 100644 index 4a4b273..0000000 --- a/code/win32/bink.h +++ /dev/null @@ -1,620 +0,0 @@ -#ifndef BINKH -#define BINKH - -#define BINKVERSION "1.0p" -#define BINKDATE "2000-06-26" - -#ifndef __RADRES__ - -#include "rad.h" - -RADDEFSTART - -typedef struct BINK PTR4* HBINK; - -typedef s32 (RADLINK PTR4* BINKIOOPEN) (struct BINKIO PTR4* Bnkio, const char PTR4 *name, u32 flags); -typedef u32 (RADLINK PTR4* BINKIOREADHEADER) (struct BINKIO PTR4* Bnkio, s32 Offset, void PTR4* Dest,u32 Size); -typedef u32 (RADLINK PTR4* BINKIOREADFRAME) (struct BINKIO PTR4* Bnkio, u32 Framenum,s32 origofs,void PTR4* dest,u32 size); -typedef u32 (RADLINK PTR4* BINKIOGETBUFFERSIZE)(struct BINKIO PTR4* Bnkio, u32 Size); -typedef void (RADLINK PTR4* BINKIOSETINFO) (struct BINKIO PTR4* Bnkio, void PTR4* Buf,u32 Size,u32 FileSize,u32 simulate); -typedef u32 (RADLINK PTR4* BINKIOIDLE) (struct BINKIO PTR4* Bnkio); -typedef void (RADLINK PTR4* BINKIOCLOSE) (struct BINKIO PTR4* Bnkio); - -typedef struct BINKIO { - BINKIOREADHEADER ReadHeader; - BINKIOREADFRAME ReadFrame; - BINKIOGETBUFFERSIZE GetBufferSize; - BINKIOSETINFO SetInfo; - BINKIOIDLE Idle; - BINKIOCLOSE Close; - HBINK bink; - volatile u32 ReadError; - volatile u32 DoingARead; - volatile u32 BytesRead; - volatile u32 Working; - volatile u32 TotalTime; - volatile u32 ForegroundTime; - volatile u32 IdleTime; - volatile u32 ThreadTime; - volatile u32 BufSize; - volatile u32 BufHighUsed; - volatile u32 CurBufSize; - volatile u32 CurBufUsed; - volatile u8 iodata[128]; -} BINKIO; - -typedef s32 (RADLINK PTR4* BINKSNDOPEN) (struct BINKSND PTR4* BnkSnd, u32 freq, s32 bits, s32 chans, u32 flags, HBINK bink); -typedef s32 (RADLINK PTR4* BINKSNDREADY) (struct BINKSND PTR4* BnkSnd); -typedef s32 (RADLINK PTR4* BINKSNDLOCK) (struct BINKSND PTR4* BnkSnd, u8 PTR4* PTR4* addr, u32 PTR4* len); -typedef s32 (RADLINK PTR4* BINKSNDUNLOCK) (struct BINKSND PTR4* BnkSnd, u32 filled); -typedef void (RADLINK PTR4* BINKSNDVOLUME) (struct BINKSND PTR4* BnkSnd, s32 volume); -typedef void (RADLINK PTR4* BINKSNDPAN) (struct BINKSND PTR4* BnkSnd, s32 pan); -typedef s32 (RADLINK PTR4* BINKSNDONOFF) (struct BINKSND PTR4* BnkSnd, s32 status); -typedef s32 (RADLINK PTR4* BINKSNDPAUSE) (struct BINKSND PTR4* BnkSnd, s32 status); -typedef void (RADLINK PTR4* BINKSNDCLOSE) (struct BINKSND PTR4* BnkSnd); - -typedef BINKSNDOPEN (RADLINK PTR4* BINKSNDSYSOPEN) (u32 param); - -typedef struct BINKSND { - BINKSNDREADY Ready; - BINKSNDLOCK Lock; - BINKSNDUNLOCK Unlock; - BINKSNDVOLUME Volume; - BINKSNDPAN Pan; - BINKSNDPAUSE Pause; - BINKSNDONOFF SetOnOff; - BINKSNDCLOSE Close; - u32 BestSizeIn16; - u32 SoundDroppedOut; - s32 OnOff; - u32 Latency; - u32 freq; - s32 bits,chans; - u8 snddata[128]; -} BINKSND; - -typedef struct BINKRECT { - s32 Left,Top,Width,Height; -} BINKRECT; - -#define BINKMAXDIRTYRECTS 8 - -typedef struct BUNDLEPOINTERS { - void* typeptr; - void* type16ptr; - void* colorptr; - void* bits2ptr; - void* motionXptr; - void* motionYptr; - void* dctptr; - void* mdctptr; - void* patptr; -} BUNDLEPOINTERS; - - -typedef struct BINK { - u32 Width; // Width (1 based, 640 for example) - u32 Height; // Height (1 based, 480 for example) - u32 Frames; // Number of frames (1 based, 100 = 100 frames) - u32 FrameNum; // Frame to *be* displayed (1 based) - u32 LastFrameNum; // Last frame decompressed or skipped (1 based) - - u32 FrameRate; // Frame Rate Numerator - u32 FrameRateDiv; // Frame Rate Divisor (frame rate=numerator/divisor) - - u32 ReadError; // Non-zero if a read error has ocurred - u32 OpenFlags; // flags used on open - u32 BinkType; // Bink flags - - u32 Size; // size of file - u32 FrameSize; // The current frame's size in bytes - u32 SndSize; // The current frame sound tracks' size in bytes - - BINKRECT FrameRects[BINKMAXDIRTYRECTS];// Dirty rects from BinkGetRects - s32 NumRects; - - u32 PlaneNum; // which set of planes is current - void PTR4* YPlane[2]; // pointer to the uncompressed Y (Cr and Cr follow) - void PTR4* APlane[2]; // decompressed alpha plane (if present) - u32 YWidth; // widths and heights of the video planes - u32 YHeight; - u32 UVWidth; - u32 UVHeight; - - void PTR4* MaskPlane; // pointer to the mask plane (Ywidth/16*Yheight/16) - u32 MaskPitch; // Mask Pitch - u32 MaskLength; // total length of the mask plane - - u32 LargestFrameSize; // Largest frame size - u32 InternalFrames; // how many frames were potentially compressed - - s32 NumTracks; // how many tracks - - u32 Highest1SecRate; // Highest 1 sec data rate - u32 Highest1SecFrame; // Highest 1 sec data rate starting frame - - s32 Paused; // is the bink movie paused? - - u32 BackgroundThread; // handle to background thread - - // everything below is for internal Bink use - - void PTR4* compframe; // compressed frame data - void PTR4* preloadptr; // preloaded compressed frame data - u32* frameoffsets; // offsets of each of the frames - - BINKIO bio; // IO structure - u8 PTR4* ioptr; // io buffer ptr - u32 iosize; // io buffer size - u32 decompwidth; // width not include scaling - u32 decompheight; // height not include scaling - - s32 trackindex; // track index - u32 PTR4* tracksizes; // largest single frame of track - u32 PTR4* tracktypes; // type of each sound track - s32 PTR4* trackIDs; // external track numbers - - u32 numrects; // number of rects from BinkGetRects - - u32 playedframes; // how many frames have we played - u32 firstframetime; // very first frame start - u32 startframetime; // start frame start - u32 startblittime; // start of blit period - u32 startsynctime; // start of synched time - u32 startsyncframe; // frame of startsynctime - u32 twoframestime; // two frames worth of time - u32 entireframetime; // entire frame time - - u32 slowestframetime; // slowest frame in ms - u32 slowestframe; // slowest frame number - u32 slowest2frametime; // second slowest frame in ms - u32 slowest2frame; // second slowest frame - - u32 soundon; // sound turned on? - u32 videoon; // video turned on? - - u32 totalmem; // total memory used - u32 timevdecomp; // total time decompressing video - u32 timeadecomp; // total time decompressing audio - u32 timeblit; // total time blitting - u32 timeopen; // total open time - - u32 fileframerate; // frame rate originally in the file - u32 fileframeratediv; - - volatile u32 threadcontrol; // controls the background reading thread - - u32 runtimeframes; // max frames for runtime analysis - u32 runtimemoveamt; // bytes to move each frame - u32 PTR4* rtframetimes; // start times for runtime frames - u32 PTR4* rtadecomptimes; // decompress times for runtime frames - u32 PTR4* rtvdecomptimes; // decompress times for runtime frames - u32 PTR4* rtblittimes; // blit times for runtime frames - u32 PTR4* rtreadtimes; // read times for runtime frames - u32 PTR4* rtidlereadtimes; // idle read times for runtime frames - u32 PTR4* rtthreadreadtimes;// thread read times for runtime frames - - u32 lastblitflags; // flags used on last blit - u32 lastdecompframe; // last frame number decompressed - - u32 sndbufsize; // sound buffer size - u8 PTR4* sndbuf; // sound buffer - u8 PTR4* sndend; // end of the sound buffer - u8 PTR4* sndwritepos; // current write position - u8 PTR4* sndreadpos; // current read position - u32 sndcomp; // sound compression handle - u32 sndamt; // amount of sound currently in the buffer - volatile u32 sndreenter; // re-entrancy check on the sound - u32 sndconvert8; // convert back to 8-bit sound at runtime - BINKSND bsnd; // SND structure - u32 skippedlastblit; // skipped last frame? - u32 skippedblits; // how many blits were skipped - u32 soundskips; // number of sound stops - u32 sndendframe; // frame number that the sound ends on - u32 sndprime; // amount of data to prime the playahead - u32 sndpad; // padded this much audio - - BUNDLEPOINTERS bunp; // pointers to internal temporary memory -} BINK; - - -typedef struct BINKSUMMARY { - u32 Width; // Width of frames - u32 Height; // Height of frames - u32 TotalTime; // total time (ms) - u32 FileFrameRate; // frame rate - u32 FileFrameRateDiv; // frame rate divisor - u32 FrameRate; // frame rate - u32 FrameRateDiv; // frame rate divisor - u32 TotalOpenTime; // Time to open and prepare for decompression - u32 TotalFrames; // Total Frames - u32 TotalPlayedFrames; // Total Frames played - u32 SkippedFrames; // Total number of skipped frames - u32 SkippedBlits; // Total number of skipped blits - u32 SoundSkips; // Total number of sound skips - u32 TotalBlitTime; // Total time spent blitting - u32 TotalReadTime; // Total time spent reading - u32 TotalVideoDecompTime; // Total time spent decompressing video - u32 TotalAudioDecompTime; // Total time spent decompressing audio - u32 TotalIdleReadTime; // Total time spent reading while idle - u32 TotalBackReadTime; // Total time spent reading in background - u32 TotalReadSpeed; // Total io speed (bytes/second) - u32 SlowestFrameTime; // Slowest single frame time (ms) - u32 Slowest2FrameTime; // Second slowest single frame time (ms) - u32 SlowestFrameNum; // Slowest single frame number - u32 Slowest2FrameNum; // Second slowest single frame number - u32 AverageDataRate; // Average data rate of the movie - u32 AverageFrameSize; // Average size of the frame - u32 HighestMemAmount; // Highest amount of memory allocated - u32 TotalIOMemory; // Total extra memory allocated - u32 HighestIOUsed; // Highest extra memory actually used - u32 Highest1SecRate; // Highest 1 second rate - u32 Highest1SecFrame; // Highest 1 second start frame -} BINKSUMMARY; - - -typedef struct BINKREALTIME { - u32 FrameNum; // Current frame number - u32 FrameRate; // frame rate - u32 FrameRateDiv; // frame rate divisor - u32 Frames; // frames in this sample period - u32 FramesTime; // time is ms for these frames - u32 FramesVideoDecompTime; // time decompressing these frames - u32 FramesAudioDecompTime; // time decompressing these frames - u32 FramesReadTime; // time reading these frames - u32 FramesIdleReadTime; // time reading these frames at idle - u32 FramesThreadReadTime; // time reading these frames in background - u32 FramesBlitTime; // time blitting these frames - u32 ReadBufferSize; // size of read buffer - u32 ReadBufferUsed; // amount of read buffer currently used - u32 FramesDataRate; // data rate for these frames -} BINKREALTIME; - -#define BINKMARKER1 'fKIB' -#define BINKMARKER2 'gKIB' // new Bink files use this tag -#define BINKMARKER3 'hKIB' // newer Bink files use this tag -#define BINKMARKER4 'iKIB' // even newer Bink files use this tag - -typedef struct BINKHDR { - u32 Marker; // Bink marker - u32 Size; // size of the file-8 - u32 Frames; // Number of frames (1 based, 100 = 100 frames) - u32 LargestFrameSize; // Size in bytes of largest frame - u32 InternalFrames; // Number of internal frames - - u32 Width; // Width (1 based, 640 for example) - u32 Height; // Height (1 based, 480 for example) - u32 FrameRate; // frame rate - u32 FrameRateDiv; // frame rate divisor (framerate/frameratediv=fps) - - u32 Flags; // height compression options - u32 NumTracks; // number of tracks -} BINKHDR; - - -//======================================================================= -#define BINKFRAMERATE 0x00001000L // Override fr (call BinkFrameRate first) -#define BINKPRELOADALL 0x00002000L // Preload the entire animation -#define BINKSNDTRACK 0x00004000L // Set the track number to play -#define BINKOLDFRAMEFORMAT 0x00008000L // using the old Bink frame format (internal use only) -#define BINKRBINVERT 0x00010000L // use reversed R and B planes (internal use only) -#define BINKGRAYSCALE 0x00020000L // Force Bink to use grayscale -#define BINKNOMMX 0x00040000L // Don't use MMX -#define BINKNOSKIP 0x00080000L // Don't skip frames if falling behind -#define BINKALPHA 0x00100000L // Decompress alpha plane (if present) -#define BINKNOFILLIOBUF 0x00200000L // Fill the IO buffer in SmackOpen -#define BINKSIMULATE 0x00400000L // Simulate the speed (call BinkSim first) -#define BINKFILEHANDLE 0x00800000L // Use when passing in a file handle -#define BINKIOSIZE 0x01000000L // Set an io size (call BinkIOSize first) -#define BINKIOPROCESSOR 0x02000000L // Set an io processor (call BinkIO first) -#define BINKFROMMEMORY 0x04000000L // Use when passing in a pointer to the file -#define BINKNOTHREADEDIO 0x08000000L // Don't use a background thread for IO - -#define BINKSURFACEFAST 0x00000000L -#define BINKSURFACESLOW 0x08000000L -#define BINKSURFACEDIRECT 0x04000000L - -#define BINKCOPYALL 0x80000000L // copy all pixels (not just changed) -#define BINKCOPY2XH 0x10000000L // Force doubling height scaling -#define BINKCOPY2XHI 0x20000000L // Force interleaving height scaling -#define BINKCOPY2XW 0x30000000L // copy the width zoomed by two -#define BINKCOPY2XWH 0x40000000L // copy the width and height zoomed by two -#define BINKCOPY2XWHI 0x50000000L // copy the width and height zoomed by two -#define BINKCOPY1XI 0x60000000L // copy the width and height zoomed by two -#define BINKCOPYNOSCALING 0x70000000L // Force scaling off - -//#define BINKALPHA 0x00100000L // Decompress alpha plane (if present) -//#define BINKNOSKIP 0x00080000L // don't skip the blit if behind in sound -//#define BINKNOMMX 0x00040000L // Don't skip frames if falling behind -//#define BINKGRAYSCALE 0x00020000L // force Bink to use grayscale -//#define BINKRBINVERT 0x00010000L // use reversed R and B planes - -#define BINKSURFACE8P 0 -#define BINKSURFACE24 1 -#define BINKSURFACE24R 2 -#define BINKSURFACE32 3 -#define BINKSURFACE32R 4 -#define BINKSURFACE32A 5 -#define BINKSURFACE32RA 6 -#define BINKSURFACE4444 7 -#define BINKSURFACE5551 8 -#define BINKSURFACE555 9 -#define BINKSURFACE565 10 -#define BINKSURFACE655 11 -#define BINKSURFACE664 12 -#define BINKSURFACEYUY2 13 -#define BINKSURFACEUYVY 14 -#define BINKSURFACEYV12 15 -#define BINKSURFACEMASK 15 - -#define BINKGOTOQUICK 1 - -#define BINKGETKEYPREVIOUS 0 -#define BINKGETKEYNEXT 1 -#define BINKGETKEYCLOSEST 2 -#define BINKGETKEYNOTEQUAL 128 - -//======================================================================= - -#ifdef __RADMAC__ - #include - - #pragma export on - - RADEXPFUNC HBINK RADEXPLINK BinkMacOpen(FSSpec* fsp,u32 flags); -#endif - -RADEXPFUNC void PTR4* RADEXPLINK BinkLogoAddress(void); - -RADEXPFUNC void RADEXPLINK BinkSetError(const char PTR4* err); -RADEXPFUNC char PTR4* RADEXPLINK BinkGetError(void); - -RADEXPFUNC HBINK RADEXPLINK BinkOpen(const char PTR4* name,u32 flags); - -RADEXPFUNC s32 RADEXPLINK BinkDoFrame(HBINK bnk); -RADEXPFUNC void RADEXPLINK BinkNextFrame(HBINK bnk); -RADEXPFUNC s32 RADEXPLINK BinkWait(HBINK bnk); -RADEXPFUNC void RADEXPLINK BinkClose(HBINK bnk); -RADEXPFUNC s32 RADEXPLINK BinkPause(HBINK bnk,s32 pause); -RADEXPFUNC s32 RADEXPLINK BinkCopyToBuffer(HBINK bnk,void* dest,s32 destpitch,u32 destheight,u32 destx,u32 desty,u32 flags); -RADEXPFUNC s32 RADEXPLINK BinkGetRects(HBINK bnk,u32 flags); -RADEXPFUNC void RADEXPLINK BinkGoto(HBINK bnk,u32 frame,s32 flags); // use 1 for the first frame -RADEXPFUNC u32 RADEXPLINK BinkGetKeyFrame(HBINK bnk,u32 frame,s32 flags); - -RADEXPFUNC s32 RADEXPLINK BinkSetVideoOnOff(HBINK bnk,s32 onoff); -RADEXPFUNC s32 RADEXPLINK BinkSetSoundOnOff(HBINK bnk,s32 onoff); -RADEXPFUNC void RADEXPLINK BinkSetVolume(HBINK bnk,s32 volume); -RADEXPFUNC void RADEXPLINK BinkSetPan(HBINK bnk,s32 pan); -RADEXPFUNC void RADEXPLINK BinkService(HBINK bink); - -typedef struct BINKTRACK PTR4* HBINKTRACK; - -typedef struct BINKTRACK -{ - u32 Frequency; - u32 Bits; - u32 Channels; - u32 MaxSize; - - HBINK bink; - u32 sndcomp; - s32 trackindex; -} BINKTRACK; - - -RADEXPFUNC HBINKTRACK RADEXPLINK BinkOpenTrack(HBINK bnk,u32 trackindex); -RADEXPFUNC void RADEXPLINK BinkCloseTrack(HBINKTRACK bnkt); -RADEXPFUNC u32 RADEXPLINK BinkGetTrackData(HBINKTRACK bnkt,void PTR4* dest); - -RADEXPFUNC u32 RADEXPLINK BinkGetTrackType(HBINK bnk,u32 trackindex); -RADEXPFUNC u32 RADEXPLINK BinkGetTrackMaxSize(HBINK bnk,u32 trackindex); -RADEXPFUNC u32 RADEXPLINK BinkGetTrackID(HBINK bnk,u32 trackindex); -RADEXPFUNC u32 RADEXPLINK BinkGetTrackLargest(HBINK bnk,u32 trackindex); - -RADEXPFUNC void RADEXPLINK BinkGetSummary(HBINK bnk,BINKSUMMARY PTR4* sum); -RADEXPFUNC void RADEXPLINK BinkGetRealtime(HBINK bink,BINKREALTIME PTR4* run,u32 frames); - -#define BINKNOSOUND 0xffffffff - -RADEXPFUNC void RADEXPLINK BinkSetSoundTrack(u32 track); -RADEXPFUNC void RADEXPLINK BinkSetIO(BINKIOOPEN io); -RADEXPFUNC void RADEXPLINK BinkSetFrameRate(u32 forcerate,u32 forceratediv); -RADEXPFUNC void RADEXPLINK BinkSetSimulate(u32 sim); -RADEXPFUNC void RADEXPLINK BinkSetIOSize(u32 iosize); - -RADEXPFUNC s32 RADEXPLINK BinkSetSoundSystem(BINKSNDSYSOPEN open, u32 param); - -#ifdef __RADWIN__ - - RADEXPFUNC BINKSNDOPEN RADEXPLINK BinkOpenDirectSound(u32 param); // don't call directly - #define BinkSoundUseDirectSound(lpDS) BinkSetSoundSystem(BinkOpenDirectSound,(u32)lpDS) - - RADEXPFUNC BINKSNDOPEN RADEXPLINK BinkOpenWaveOut(u32 param); // don't call directly - #define BinkSoundUseWaveOut() BinkSetSoundSystem(BinkOpenWaveOut,0) - - #define INCLUDE_MMSYSTEM_H - #include "windows.h" - #include "windowsx.h" - - #ifdef __RADNT__ // to combat WIN32_LEAN_AND_MEAN - #include "mmsystem.h" - #endif - -#endif - - -#ifndef __RADMAC__ - - RADEXPFUNC BINKSNDOPEN RADEXPLINK BinkOpenMiles(u32 param); // don't call directly - #define BinkSoundUseMiles(hdigdriver) BinkSetSoundSystem(BinkOpenMiles,(u32)hdigdriver) - -#endif - - -#ifdef __RADMAC__ - - RADEXPFUNC BINKSNDOPEN RADEXPLINK BinkOpenSoundManager(u32 param); // don't call directly - #define BinkSoundUseSoundManager() BinkSetSoundSystem(BinkOpenSoundManager,0) - -#endif - - -// The BinkBuffer API isn't currently implemented on DOS -#if !defined(__RADDOS__) - -//========================================================================= -typedef struct BINKBUFFER * HBINKBUFFER; - -#define BINKBUFFERSTRETCHXINT 0x80000000 -#define BINKBUFFERSTRETCHX 0x40000000 -#define BINKBUFFERSHRINKXINT 0x20000000 -#define BINKBUFFERSHRINKX 0x10000000 -#define BINKBUFFERSTRETCHYINT 0x08000000 -#define BINKBUFFERSTRETCHY 0x04000000 -#define BINKBUFFERSHRINKYINT 0x02000000 -#define BINKBUFFERSHRINKY 0x01000000 -#define BINKBUFFERSCALES 0xff000000 -#define BINKBUFFERRESOLUTION 0x00800000 - -#ifdef __RADMAC__ - -#include -#include -#include - -typedef struct BINKBUFFER { - u32 Width; - u32 Height; - u32 WindowWidth; - u32 WindowHeight; - u32 SurfaceType; - void* Buffer; - s32 BufferPitch; - u32 ScreenWidth; - u32 ScreenHeight; - u32 ScreenDepth; - u32 ScaleFlags; - - s32 destx,desty; - s32 wndx,wndy; - u32 wnd; - - s32 noclipping; - u32 type; - s32 issoftcur; - u32 cursorcount; - -} BINKBUFFER; - - -#define BINKBUFFERAUTO 0 -#define BINKBUFFERDIRECT 1 -#define BINKBUFFERGWORLD 2 -#define BINKBUFFERTYPEMASK 31 - -RADEXPFUNC HBINKBUFFER RADEXPLINK BinkBufferOpen( WindowPtr wnd, u32 width, u32 height, u32 bufferflags); -RADEXPFUNC s32 RADEXPLINK BinkGDSurfaceType( GDHandle gd ); -RADEXPFUNC s32 RADEXPLINK BinkIsSoftwareCursor(GDHandle gd); -RADEXPFUNC s32 RADEXPLINK BinkCheckCursor(WindowPtr wp,s32 x,s32 y,s32 w,s32 h); - -#else - -typedef struct BINKBUFFER { - u32 Width; - u32 Height; - u32 WindowWidth; - u32 WindowHeight; - u32 SurfaceType; - void* Buffer; - s32 BufferPitch; - s32 ClientOffsetX; - s32 ClientOffsetY; - u32 ScreenWidth; - u32 ScreenHeight; - u32 ScreenDepth; - u32 ExtraWindowWidth; - u32 ExtraWindowHeight; - u32 ScaleFlags; - u32 StretchWidth; - u32 StretchHeight; - - s32 surface; - void* ddsurface; - void* ddclipper; - s32 destx,desty; - s32 wndx,wndy; - u32 wnd; - s32 ddoverlay; - s32 ddoffscreen; - s32 lastovershow; - - s32 issoftcur; - u32 cursorcount; - void* buffertop; - u32 type; - s32 noclipping; - - s32 loadeddd; - s32 loadedwin; - - void* dibh; - void* dibbuffer; - s32 dibpitch; - void* dibinfo; - u32 dibdc; - u32 diboldbitmap; - -} BINKBUFFER; - - -#define BINKBUFFERAUTO 0 -#define BINKBUFFERPRIMARY 1 -#define BINKBUFFERDIBSECTION 2 -#define BINKBUFFERYV12OVERLAY 3 -#define BINKBUFFERYUY2OVERLAY 4 -#define BINKBUFFERUYVYOVERLAY 5 -#define BINKBUFFERYV12OFFSCREEN 6 -#define BINKBUFFERYUY2OFFSCREEN 7 -#define BINKBUFFERUYVYOFFSCREEN 8 -#define BINKBUFFERRGBOFFSCREENVIDEO 9 -#define BINKBUFFERRGBOFFSCREENSYSTEM 10 -#define BINKBUFFERLAST 10 -#define BINKBUFFERTYPEMASK 31 - -RADEXPFUNC HBINKBUFFER RADEXPLINK BinkBufferOpen( HWND wnd, u32 width, u32 height, u32 bufferflags); -RADEXPFUNC s32 RADEXPLINK BinkBufferSetHWND( HBINKBUFFER buf, HWND newwnd); -RADEXPFUNC s32 RADEXPLINK BinkDDSurfaceType(void PTR4* lpDDS); -RADEXPFUNC s32 RADEXPLINK BinkIsSoftwareCursor(void PTR4* lpDDSP,HCURSOR cur); -RADEXPFUNC s32 RADEXPLINK BinkCheckCursor(HWND wnd,s32 x,s32 y,s32 w,s32 h); -RADEXPFUNC s32 RADEXPLINK BinkBufferSetDirectDraw(void PTR4* lpDirectDraw, void PTR4* lpPrimary); - -#endif - -RADEXPFUNC void RADEXPLINK BinkBufferClose( HBINKBUFFER buf); -RADEXPFUNC s32 RADEXPLINK BinkBufferLock( HBINKBUFFER buf); -RADEXPFUNC s32 RADEXPLINK BinkBufferUnlock( HBINKBUFFER buf); -RADEXPFUNC void RADEXPLINK BinkBufferSetResolution( s32 w, s32 h, s32 bits); -RADEXPFUNC void RADEXPLINK BinkBufferCheckWinPos( HBINKBUFFER buf, s32 PTR4* NewWindowX, s32 PTR4* NewWindowY); -RADEXPFUNC s32 RADEXPLINK BinkBufferSetOffset( HBINKBUFFER buf, s32 destx, s32 desty); -RADEXPFUNC void RADEXPLINK BinkBufferBlit( HBINKBUFFER buf, BINKRECT PTR4* rects, u32 numrects ); -RADEXPFUNC s32 RADEXPLINK BinkBufferSetScale( HBINKBUFFER buf, u32 w, u32 h); -RADEXPFUNC char PTR4* RADEXPLINK BinkBufferGetDescription( HBINKBUFFER buf); -RADEXPFUNC char PTR4* RADEXPLINK BinkBufferGetError(); -RADEXPFUNC s32 RADEXPLINK BinkBufferClear(HBINKBUFFER buf, u32 RGB); - -RADEXPFUNC void RADEXPLINK BinkRestoreCursor(s32 checkcount); - -#endif - -#ifdef __RADMAC__ - -#pragma export off - -#endif - -RADDEFEND - -#endif - -#endif - diff --git a/code/win32/binkw32.lib b/code/win32/binkw32.lib deleted file mode 100644 index 75369a3..0000000 Binary files a/code/win32/binkw32.lib and /dev/null differ diff --git a/code/win32/clear.bmp b/code/win32/clear.bmp deleted file mode 100644 index 1451638..0000000 Binary files a/code/win32/clear.bmp and /dev/null differ diff --git a/code/win32/feelit/FFC10.dll b/code/win32/feelit/FFC10.dll deleted file mode 100644 index 0536f23..0000000 Binary files a/code/win32/feelit/FFC10.dll and /dev/null differ diff --git a/code/win32/feelit/FFC10d.dll b/code/win32/feelit/FFC10d.dll deleted file mode 100644 index 1f546c2..0000000 Binary files a/code/win32/feelit/FFC10d.dll and /dev/null differ diff --git a/code/win32/feelit/ffc10.lib b/code/win32/feelit/ffc10.lib deleted file mode 100644 index 54ca7f4..0000000 Binary files a/code/win32/feelit/ffc10.lib and /dev/null differ diff --git a/code/win32/feelit/ffc10d.lib b/code/win32/feelit/ffc10d.lib deleted file mode 100644 index 786d23d..0000000 Binary files a/code/win32/feelit/ffc10d.lib and /dev/null differ diff --git a/code/win32/feelit/vssver.scc b/code/win32/feelit/vssver.scc deleted file mode 100644 index 3cf99a6..0000000 Binary files a/code/win32/feelit/vssver.scc and /dev/null differ diff --git a/code/win32/rad.h b/code/win32/rad.h deleted file mode 100644 index e669360..0000000 --- a/code/win32/rad.h +++ /dev/null @@ -1,962 +0,0 @@ -#ifndef __RAD__ -#define __RAD__ - -#define RADCOPYRIGHT "Copyright (C) 1994-2000, RAD Game Tools, Inc." - -#ifndef __RADRES__ - -// __RAD16__ means 16 bit code (Win16) -// __RAD32__ means 32 bit code (DOS, Win386, Win32s, Mac) - -// __RADDOS__ means DOS code (16 or 32 bit) -// __RADWIN__ means Windows code (Win16, Win386, Win32s) -// __RADWINEXT__ means Windows 386 extender (Win386) -// __RADNT__ means Win32s code -// __RADMAC__ means Macintosh - -// __RADX86__ means Intel x86 -// __RADMMX__ means Intel x86 MMX instructions are allowed -// __RAD68K__ means 68K -// __RADPPC__ means PowerPC - -// __RADLITTLEENDIAN__ means processor is little-endian (x86) -// __RADBIGENDIAN__ means processor is big-endian (680x0, PPC) - -// __RADALLOWINLINES__ means this compiler allows inline function declarations -// use RADINLINE for the appropriate keyword - - -#if (defined(__MWERKS__) && !defined(__INTEL__)) || defined(__MRC__) || defined(THINK_C) || defined(powerc) || defined(macintosh) || defined(__powerc) - - #define __RADMAC__ - #if defined(powerc) || defined(__powerc) - #define __RADPPC__ - #else - #define __RAD68K__ - #endif - - #define __RAD32__ - - #define __RADBIGENDIAN__ - - #if defined(__MWERKS__) - #if (defined(__cplusplus) || ! __option(only_std_keywords)) - #define __RADALLOWINLINES__ - #define RADINLINE inline - #endif - #elif defined(__MRC__) - #if defined(__cplusplus) - #define __RADALLOWINLINES__ - #define RADINLINE inline - #endif - #endif - -#else - - #define __RADX86__ - #define __RADMMX__ - - #ifdef __MWERKS__ - #define _WIN32 - #endif - - #ifdef __DOS__ - #define __RADDOS__ - #endif - - #ifdef __386__ - #define __RAD32__ - #endif - - #ifdef _Windows //For Borland - #ifdef __WIN32__ - #define WIN32 - #else - #define __WINDOWS__ - #endif - #endif - - #ifdef _WINDOWS //For MS - #ifndef _WIN32 - #define __WINDOWS__ - #endif - #endif - - #ifdef _WIN32 - #define __RADWIN__ - #define __RADNT__ - #define __RAD32__ - #else - #ifdef __NT__ - #define __RADWIN__ - #define __RADNT__ - #define __RAD32__ - #else - #ifdef __WINDOWS_386__ - #define __RADWIN__ - #define __RADWINEXT__ - #define __RAD32__ - #else - #ifdef __WINDOWS__ - #define __RADWIN__ - #define __RAD16__ - #else - #ifdef WIN32 - #define __RADWIN__ - #define __RADNT__ - #define __RAD32__ - #endif - #endif - #endif - #endif - #endif - - #define __RADLITTLEENDIAN__ - - // TODO - make sure these are set correctly for non-Mac versions - #define __RADALLOWINLINES__ - #define RADINLINE __inline - -#endif - -#ifndef __RADALLOWINLINES__ - #define RADINLINE -#endif - -#if (!defined(__RADDOS__) && !defined(__RADWIN__) && !defined(__RADMAC__)) - #error RAD.H did not detect your platform. Define __DOS__, __WINDOWS__, WIN32, macintosh, or powerc. -#endif - -#ifdef __RADMAC__ - - // this define is for CodeWarrior 11's stupid new libs (even though - // we don't use longlong's). - - #define __MSL_LONGLONG_SUPPORT__ - - #define RADLINK - #define RADEXPLINK - - #ifdef __CFM68K__ - #ifdef __RADINDLL__ - #define RADEXPFUNC RADDEFFUNC __declspec(export) - #else - #define RADEXPFUNC RADDEFFUNC __declspec(import) - #endif - #else - #define RADEXPFUNC RADDEFFUNC - #endif - #define RADASMLINK - -#else - - #ifdef __RADNT__ - #ifndef _WIN32 - #define _WIN32 - #endif - #ifndef WIN32 - #define WIN32 - #endif - #endif - - #ifdef __RADWIN__ - #ifdef __RAD32__ - #ifdef __RADNT__ - - #define RADLINK __stdcall - #define RADEXPLINK __stdcall - - #ifdef __RADINEXE__ - #define RADEXPFUNC RADDEFFUNC - #else - #ifndef __RADINDLL__ - #define RADEXPFUNC RADDEFFUNC __declspec(dllimport) - #ifdef __BORLANDC__ - #if __BORLANDC__<=0x460 - #undef RADEXPFUNC - #define RADEXPFUNC RADDEFFUNC - #endif - #endif - #else - #define RADEXPFUNC RADDEFFUNC __declspec(dllexport) - #endif - #endif - #else - #define RADLINK __pascal - #define RADEXPLINK __far __pascal - #define RADEXPFUNC RADDEFFUNC - #endif - #else - #define RADLINK __pascal - #define RADEXPLINK __far __pascal __export - #define RADEXPFUNC RADDEFFUNC - #endif - #else - #define RADLINK __pascal - #define RADEXPLINK __pascal - #define RADEXPFUNC RADDEFFUNC - #endif - - #define RADASMLINK __cdecl - -#endif - -#ifdef __RADWIN__ - #ifndef _WINDOWS - #define _WINDOWS - #endif -#endif - -#ifdef __cplusplus - #define RADDEFFUNC extern "C" - #define RADDEFSTART extern "C" { - #define RADDEFEND } -#else - #define RADDEFFUNC - #define RADDEFSTART - #define RADDEFEND -#endif - - -RADDEFSTART - -#define s8 signed char -#define u8 unsigned char -#define u32 unsigned long -#define s32 signed long -#define f32 float -#define f64 double - -#if defined(__MWERKS__) || defined(__MRC__) -#define u64 unsigned long long -#define s64 signed long long -#else -#define u64 unsigned __int64 -#define s64 signed __int64 -#endif - -/* 32 bit implementations */ - -#ifdef __RAD32__ - #define PTR4 - - #define u16 unsigned short - #define s16 signed short - - #ifdef __RADMAC__ - - #include - #include - #include - #include - #ifdef __MRC__ - #include "intrinsics.h" - #endif - - void radconv32a(void* p, u32 n); - - u32 radloadu32(u32 a); - - u32 radloadu32ptr(u32* p); - - #define radstrcpy strcpy - - #define radstrcat strcat - - #define radmemcpy(dest,source,size) BlockMoveData((Ptr)(source),(Ptr)(dest),size) - - #define radmemcpydb(dest,source,size) BlockMoveData((Ptr)(source),(Ptr)(dest),size) - - #define radmemcmp memcmp - - #define radmemset memset - - #define radstrlen strlen - - #define radstrchr strchr - - #define radtoupper toupper - - #define radstru32(s) ((u32)atol(s)) - - //s8 radstricmp(const void* s1,const void* s2); - - #define radstrcmp strcmp - - //char* radstrupr(void* s1); - - //char* radstrlwr(void* s1); - - u32 radsqr(u32 a); - - u32 mult64anddiv(u32 m1,u32 m2,u32 d); - - s32 radabs(s32 ab); - - #define radabs32 radabs - - //char* radstpcpy(void* dest,const void* source); - - //char* radstpcpyrs(void* dest,const void* source); - - void radmemset16(void* dest,u16 value,u32 size); - - //void radmemset32(void* dest,u32 value,u32 size); - - #define BreakPoint() DebugStr("\pBreakPoint() was called") - - //u8 radinp(u16 p); - - //void radoutp(u16 p,u8 v); - - //u32 RADsqrt(u32 sq); - - u32 RADCycleTimerAvail(void); - - void RADCycleTimerStartAddr(u32* addr); - - u32 RADCycleTimerDeltaAddr(u32* addr); - - void RADCycleTimerStartAddr64(u64* addr); - - void RADCycleTimerDeltaAddr64(u64* addr); - - #define RADCycleTimerStart(var) RADCycleTimerStartAddr(&var) - - #define RADCycleTimerDelta(var) RADCycleTimerDeltaAddr(&var) - - #define RADCycleTimerStart64(var) RADCycleTimerStartAddr64(&var) - - #define RADCycleTimerDelta64(var) RADCycleTimerDeltaAddr64(&var) - - - #ifdef __RAD68K__ - #pragma parameter radconv32a(__A0,__D0) - void radconv32a(void* p,u32 n) ={0x4A80,0x600C,0x2210,0xE059,0x4841,0xE059,0x20C1,0x5380,0x6EF2}; - // tst.l d0 bra.s @loope @loop: move.l (a0),d1 ror.w #8,d1 swap d1 ror.w #8,d1 move.l d1,(a0)+ sub.l #1,d0 bgt.s @loop @loope: - #endif - - #ifdef __RADALLOWINLINES__ - #if defined __RADPPC__ && defined(__MWERKS__) && (__MWERKS__ >= 0x2301) && 0 - u32 RADINLINE radloadu32(register u32 x) { - register u32 t1, t2; - asm { // x = aa bb cc dd - rlwinm t1,x,24,0,23 // t1 = dd aa bb 00 - rlwinm t2,x,8,24,31 // t2 = 00 00 00 aa - rlwimi t1,x,8, 8,15 // t1 = dd cc bb 00 - or x,t1,t2 // x = dd cc bb aa - } - return x; - } - #else - u32 RADINLINE radloadu32(register u32 x) { - return (((x << 24) & 0xFF000000) | - ((x << 8) & 0x00FF0000) | - ((x >> 8) & 0x0000FF00) | - ((x >> 24) & 0x000000FF)); - } - #endif - #endif - - #if defined(__RADPPC__) && (defined(__MWERKS__) || defined(__MRC__)) - #define radloadu32ptr(p) (u32) __lwbrx((p),0) - #else - #define radloadu32ptr(p) radloadu32(*(u32*)(p)); - #endif - - #ifdef __RADALLOWINLINES__ - u32 RADINLINE radsqr(u32 a) { return(a*a); } - #endif - - #ifdef __RAD68K__ - #pragma parameter __D0 mult64anddiv(__D0,__D1,__D2) - u32 mult64anddiv(u32 m1,u32 m2,u32 d) ={0x4C01,0x0C01,0x4C42,0x0C01}; - // muls.l d1,d1:d0 divs.l d2,d1:d0 - #endif - - #if defined(__RADPPC__) && (defined(__MWERKS__) || defined(__MRC__)) - #define radabs(ab) __abs((s32)(ab)) - #elif defined(__RADALLOWINLINES__) - s32 RADINLINE radabs(s32 ab) { return (ab < 0) ? -ab : ab; } - #endif - - #else - - #define radconv32a(p,n) ((void)0) - - #define radloadu32(a) ((u32)(a)) - - #define radloadu32ptr(p) *((u32*)(p)) - - #ifdef __WATCOMC__ - - u32 radsqr(s32 a); - #pragma aux radsqr = "mul eax" parm [eax] modify [EDX eax]; - - u32 mult64anddiv(u32 m1,u32 m2,u32 d); - #pragma aux mult64anddiv = "mul ecx" "div ebx" parm [eax] [ecx] [ebx] modify [EDX eax]; - - s32 radabs(s32 ab); - #pragma aux radabs = "test eax,eax" "jge skip" "neg eax" "skip:" parm [eax]; - - #define radabs32 radabs - - u32 DOSOut(const char* str); - #pragma aux DOSOut = "cld" "mov ecx,0xffffffff" "xor eax,eax" "mov edx,edi" "repne scasb" "not ecx" "dec ecx" "mov ebx,1" "mov ah,0x40" "int 0x21" parm [EDI] modify [EAX EBX ECX EDX EDI] value [ecx]; - - void DOSOutNum(const char* str,u32 len); - #pragma aux DOSOutNum = "mov ah,0x40" "mov ebx,1" "int 0x21" parm [edx] [ecx] modify [eax ebx]; - - u32 ErrOut(const char* str); - #pragma aux ErrOut = "cld" "mov ecx,0xffffffff" "xor eax,eax" "mov edx,edi" "repne scasb" "not ecx" "dec ecx" "xor ebx,ebx" "mov ah,0x40" "int 0x21" parm [EDI] modify [EAX EBX ECX EDX EDI] value [ecx]; - - void ErrOutNum(const char* str,u32 len); - #pragma aux ErrOutNum = "mov ah,0x40" "xor ebx,ebx" "int 0x21" parm [edx] [ecx] modify [eax ebx]; - - void radmemset16(void* dest,u16 value,u32 size); - #pragma aux radmemset16 = "cld" "mov bx,ax" "shl eax,16" "mov ax,bx" "mov bl,cl" "shr ecx,1" "rep stosd" "mov cl,bl" "and cl,1" "rep stosw" parm [EDI] [EAX] [ECX] modify [EAX EDX EBX ECX EDI]; - - void radmemset(void* dest,u8 value,u32 size); - #pragma aux radmemset = "cld" "mov ah,al" "mov bx,ax" "shl eax,16" "mov ax,bx" "mov bl,cl" "shr ecx,2" "and bl,3" "rep stosd" "mov cl,bl" "rep stosb" parm [EDI] [AL] [ECX] modify [EAX EDX EBX ECX EDI]; - - void radmemset32(void* dest,u32 value,u32 size); - #pragma aux radmemset32 = "cld" "rep stosd" parm [EDI] [EAX] [ECX] modify [EAX EDX EBX ECX EDI]; - - void radmemcpy(void* dest,const void* source,u32 size); - #pragma aux radmemcpy = "cld" "mov bl,cl" "shr ecx,2" "rep movsd" "mov cl,bl" "and cl,3" "rep movsb" parm [EDI] [ESI] [ECX] modify [EBX ECX EDI ESI]; - - void __far *radfmemcpy(void __far* dest,const void __far* source,u32 size); - #pragma aux radfmemcpy = "cld" "push es" "push ds" "mov es,cx" "mov ds,dx" "mov ecx,eax" "shr ecx,2" "rep movsd" "mov cl,al" "and cl,3" "rep movsb" "pop ds" "pop es" parm [CX EDI] [DX ESI] [EAX] modify [ECX EDI ESI] value [CX EDI]; - - void radmemcpydb(void* dest,const void* source,u32 size); //Destination bigger - #pragma aux radmemcpydb = "std" "mov bl,cl" "lea esi,[esi+ecx-4]" "lea edi,[edi+ecx-4]" "shr ecx,2" "rep movsd" "and bl,3" "jz dne" "add esi,3" "add edi,3" "mov cl,bl" "rep movsb" "dne:" "cld" parm [EDI] [ESI] [ECX] modify [EBX ECX EDI ESI]; - - char* radstrcpy(void* dest,const void* source); - #pragma aux radstrcpy = "cld" "mov edx,edi" "lp:" "mov al,[esi]" "inc esi" "mov [edi],al" "inc edi" "cmp al,0" "jne lp" parm [EDI] [ESI] modify [EAX EDX EDI ESI] value [EDX]; - - char __far* radfstrcpy(void __far* dest,const void __far* source); - #pragma aux radfstrcpy = "cld" "push es" "push ds" "mov es,cx" "mov ds,dx" "mov edx,edi" "lp:" "lodsb" "stosb" "test al,0xff" "jnz lp" "pop ds" "pop es" parm [CX EDI] [DX ESI] modify [EAX EDX EDI ESI] value [CX EDX]; - - char* radstpcpy(void* dest,const void* source); - #pragma aux radstpcpy = "cld" "lp:" "mov al,[esi]" "inc esi" "mov [edi],al" "inc edi" "cmp al,0" "jne lp" "dec edi" parm [EDI] [ESI] modify [EAX EDI ESI] value [EDI]; - - char* radstpcpyrs(void* dest,const void* source); - #pragma aux radstpcpyrs = "cld" "lp:" "mov al,[esi]" "inc esi" "mov [edi],al" "inc edi" "cmp al,0" "jne lp" "dec esi" parm [EDI] [ESI] modify [EAX EDI ESI] value [ESI]; - - u32 radstrlen(const void* dest); - #pragma aux radstrlen = "cld" "mov ecx,0xffffffff" "xor eax,eax" "repne scasb" "not ecx" "dec ecx" parm [EDI] modify [EAX ECX EDI] value [ECX]; - - char* radstrcat(void* dest,const void* source); - #pragma aux radstrcat = "cld" "mov ecx,0xffffffff" "mov edx,edi" "xor eax,eax" "repne scasb" "dec edi" "lp:" "lodsb" "stosb" "test al,0xff" "jnz lp" \ - parm [EDI] [ESI] modify [EAX ECX EDI ESI] value [EDX]; - - char* radstrchr(const void* dest,char chr); - #pragma aux radstrchr = "cld" "lp:" "lodsb" "cmp al,dl" "je fnd" "cmp al,0" "jnz lp" "mov esi,1" "fnd:" "dec esi" parm [ESI] [DL] modify [EAX ESI] value [esi]; - - s8 radmemcmp(const void* s1,const void* s2,u32 len); - #pragma aux radmemcmp = "cld" "rep cmpsb" "setne al" "jbe end" "neg al" "end:" parm [EDI] [ESI] [ECX] modify [ECX EDI ESI]; - - s8 radstrcmp(const void* s1,const void* s2); - #pragma aux radstrcmp = "lp:" "mov al,[esi]" "mov ah,[edi]" "cmp al,ah" "jne set" "cmp al,0" "je set" "inc esi" "inc edi" "jmp lp" "set:" "setne al" "jbe end" "neg al" "end:" \ - parm [EDI] [ESI] modify [EAX EDI ESI]; - - s8 radstricmp(const void* s1,const void* s2); - #pragma aux radstricmp = "lp:" "mov al,[esi]" "mov ah,[edi]" "cmp al,'a'" "jb c1" "cmp al,'z'" "ja c1" "sub al,32" "c1:" "cmp ah,'a'" "jb c2" "cmp ah,'z'" "ja c2" "sub ah,32" "c2:" "cmp al,ah" "jne set" "cmp al,0" "je set" \ - "inc esi" "inc edi" "jmp lp" "set:" "setne al" "jbe end" "neg al" "end:" \ - parm [EDI] [ESI] modify [EAX EDI ESI]; - - s8 radstrnicmp(const void* s1,const void* s2,u32 len); - #pragma aux radstrnicmp = "lp:" "mov al,[esi]" "mov ah,[edi]" "cmp al,'a'" "jb c1" "cmp al,'z'" "ja c1" "sub al,32" "c1:" "cmp ah,'a'" "jb c2" "cmp ah,'z'" "ja c2" "sub ah,32" "c2:" "cmp al,ah" "jne set" "cmp al,0" "je set" \ - "dec ecx" "jz set" "inc esi" "inc edi" "jmp lp" "set:" "setne al" "jbe end" "neg al" "end:" \ - parm [EDI] [ESI] [ECX] modify [EAX ECX EDI ESI]; - - char* radstrupr(void* s1); - #pragma aux radstrupr = "mov ecx,edi" "lp:" "mov al,[edi]" "cmp al,'a'" "jb c1" "cmp al,'z'" "ja c1" "sub [edi],32" "c1:" "inc edi" "cmp al,0" "jne lp" parm [EDI] modify [EAX EDI] value [ecx]; - - char* radstrlwr(void* s1); - #pragma aux radstrlwr = "mov ecx,edi" "lp:" "mov al,[edi]" "cmp al,'A'" "jb c1" "cmp al,'Z'" "ja c1" "add [edi],32" "c1:" "inc edi" "cmp al,0" "jne lp" parm [EDI] modify [EAX EDI] value [ecx]; - - u32 radstru32(const void* dest); - #pragma aux radstru32 = "cld" "xor ecx,ecx" "xor ebx,ebx" "xor edi,edi" "lodsb" "cmp al,45" "jne skip2" "mov edi,1" "jmp skip" "lp:" "mov eax,10" "mul ecx" "lea ecx,[eax+ebx]" \ - "skip:" "lodsb" "skip2:" "cmp al,0x39" "ja dne" "cmp al,0x30" "jb dne" "mov bl,al" "sub bl,0x30" "jmp lp" "dne:" "test edi,1" "jz pos" "neg ecx" "pos:" \ - parm [ESI] modify [EAX EBX EDX EDI ESI] value [ecx]; - - u16 GetDS(); - #pragma aux GetDS = "mov ax,ds" value [ax]; - - #ifdef __RADWINEXT__ - - #define _16To32(ptr16) ((void*)(((GetSelectorBase((u16)(((u32)(ptr16))>>16))+((u16)(u32)(ptr16)))-GetSelectorBase(GetDS())))) - - #endif - - #ifndef __RADWIN__ - #define int86 int386 - #define int86x int386x - #endif - - #define u32regs x - #define u16regs w - - #else - - #define radstrcpy strcpy - #define radstrcat strcat - #define radmemcpy memcpy - #define radmemcpydb memmove - #define radmemcmp memcmp - #define radmemset memset - #define radstrlen strlen - #define radstrchr strchr - #define radtoupper toupper - #define radstru32(s) ((u32)atol(s)) - #define radstricmp _stricmp - #define radstrcmp strcmp - #define radstrupr _strupr - #define radstrlwr _strlwr - #define BreakPoint() __asm {int 3} - #define DOSOut(str) - - #ifdef _MSC_VER - - #pragma warning( disable : 4035) - - typedef char* RADPCHAR; - - u32 __inline radsqr(u32 m) { - __asm { - mov eax,[m] - mul eax - } - } - - u32 __inline mult64anddiv(u32 m1,u32 m2, u32 d) { - __asm { - mov eax,[m1] - mov ecx,[m2] - mul ecx - mov ecx,[d] - div ecx - } - } - - s32 __inline radabs(s32 ab) { - __asm { - mov eax,[ab] - test eax,eax - jge skip - neg eax - skip: - } - } - - u8 __inline radinp(u16 p) { - __asm { - mov dx,[p] - in al,dx - } - } - - void __inline radoutp(u16 p,u8 v) { - __asm { - mov dx,[p] - mov al,[v] - out dx,al - } - } - - RADPCHAR __inline radstpcpy(char* p1, char* p2) { - __asm { - mov edx,[p1] - mov ecx,[p2] - cld - lp: - mov al,[ecx] - inc ecx - mov [edx],al - inc edx - cmp al,0 - jne lp - dec edx - mov eax,edx - } - } - - RADPCHAR __inline radstpcpyrs(char* p1, char* p2) { - __asm { - mov edx,[p1] - mov ecx,[p2] - cld - lp: - mov al,[ecx] - inc ecx - mov [edx],al - inc edx - cmp al,0 - jne lp - dec ecx - mov eax,ecx - } - } - - void __inline radmemset16(void* dest,u16 value,u32 sizeb) { - __asm { - mov edi,[dest] - mov ax,[value] - mov ecx,[sizeb] - shl eax,16 - cld - mov ax,[value] - mov bl,cl - shr ecx,1 - rep stosd - mov cl,bl - and cl,1 - rep stosw - } - } - - void __inline radmemset32(void* dest,u32 value,u32 sizeb) { - __asm { - mov edi,[dest] - mov eax,[value] - mov ecx,[sizeb] - cld - rep stosd - } - } - - u32 __inline __stdcall RADsqrt(u32 sq) { - __asm { - fild dword ptr [sq] - fsqrt - fistp word ptr [sq] - movzx eax,word ptr [sq] - } - } - - u32 __inline RADCycleTimerAvail(void) - { - u32 rdtscavail=(u32)-1; - __try - { - __asm - { -#ifdef __MWERKS__ - rdtsc -#else -#if _MSC_VER<=1100 - __emit 0xf - __emit 0x31 -#else - rdtsc -#endif -#endif - } - rdtscavail=1; - } - __except (1) - { - rdtscavail=(u32)-1; - } - return rdtscavail; - } - - void __inline RADCycleTimerStartAddr(u32* addr) - { - __asm { - mov ecx,[addr] -#ifdef __MWERKS__ - rdtsc -#else -#if _MSC_VER<=1100 - __emit 0xf - __emit 0x31 -#else - rdtsc -#endif -#endif - mov [ecx],eax - } - } - - u32 __inline RADCycleTimerDeltaAddr(u32* addr) - { - __asm { -#ifdef __MWERKS__ - rdtsc -#else -#if _MSC_VER<=1100 - __emit 0xf - __emit 0x31 -#else - rdtsc -#endif -#endif - mov ecx,[addr] - mov edx,eax - sub eax,[ecx] - mov [ecx],eax - } - } - - void __inline RADCycleTimerStartAddr64(u64* addr) - { - __asm { - mov ecx,[addr] -#ifdef __MWERKS__ - rdtsc -#else -#if _MSC_VER<=1100 - __emit 0xf - __emit 0x31 -#else - rdtsc -#endif -#endif - mov [ecx],eax - mov [ecx+4],edx - } - } - - void __inline RADCycleTimerDeltaAddr64(u64* addr) - { - __asm { -#ifdef __MWERKS__ - rdtsc -#else -#if _MSC_VER<=1100 - __emit 0xf - __emit 0x31 -#else - rdtsc -#endif -#endif - mov ecx,[addr] - sub eax,[ecx] - sbb edx,[ecx+4] - mov [ecx],eax - mov [ecx+4],edx - } - } - - #define RADCycleTimerStart(var) RADCycleTimerStartAddr(&var) - #define RADCycleTimerDelta(var) RADCycleTimerDeltaAddr(&var) - - #define RADCycleTimerStart64(var) RADCycleTimerStartAddr64(&var) - #define RADCycleTimerDelta64(var) RADCycleTimerDeltaAddr64(&var) - - #pragma warning( default : 4035) - - #endif - - #endif - - #endif - -#else - - #define PTR4 __far - - #define u16 unsigned int - #define s16 signed int - - #ifdef __WATCOMC__ - - u32 radsqr(s32 a); - #pragma aux radsqr = "shl edx,16" "mov dx,ax" "mov eax,edx" "xor edx,edx" "mul eax" "shld edx,eax,16" parm [dx ax] modify [DX ax] value [dx ax]; - - s16 radabs(s16 ab); - #pragma aux radabs = "test ax,ax" "jge skip" "neg ax" "skip:" parm [ax] value [ax]; - - s32 radabs32(s32 ab); - #pragma aux radabs32 = "test dx,dx" "jge skip" "neg dx" "neg ax" "sbb dx,0" "skip:" parm [dx ax] value [dx ax]; - - u32 DOSOut(const char far* dest); - #pragma aux DOSOut = "cld" "and edi,0xffff" "mov dx,di" "mov ecx,0xffffffff" "xor eax,eax" 0x67 "repne scasb" "not ecx" "dec ecx" "mov bx,1" "push ds" "push es" "pop ds" "mov ah,0x40" "int 0x21" "pop ds" "movzx eax,cx" "shr ecx,16" \ - parm [ES DI] modify [AX BX CX DX DI ES] value [CX AX]; - - void DOSOutNum(const char far* str,u16 len); - #pragma aux DOSOutNum = "push ds" "mov ds,cx" "mov cx,bx" "mov ah,0x40" "mov bx,1" "int 0x21" "pop ds" parm [cx dx] [bx] modify [ax bx cx]; - - u32 ErrOut(const char far* dest); - #pragma aux ErrOut = "cld" "and edi,0xffff" "mov dx,di" "mov ecx,0xffffffff" "xor eax,eax" 0x67 "repne scasb" "not ecx" "dec ecx" "xor bx,bx" "push ds" "push es" "pop ds" "mov ah,0x40" "int 0x21" "pop ds" "movzx eax,cx" "shr ecx,16" \ - parm [ES DI] modify [AX BX CX DX DI ES] value [CX AX]; - - void ErrOutNum(const char far* str,u16 len); - #pragma aux ErrOutNum = "push ds" "mov ds,cx" "mov cx,bx" "mov ah,0x40" "xor bx,bx" "int 0x21" "pop ds" parm [cx dx] [bx] modify [ax bx cx]; - - void radmemset(void far *dest,u8 value,u32 size); - #pragma aux radmemset = "cld" "and edi,0ffffh" "shl ecx,16" "mov cx,bx" "mov ah,al" "mov bx,ax" "shl eax,16" "mov ax,bx" "mov bl,cl" "shr ecx,2" 0x67 "rep stosd" "mov cl,bl" "and cl,3" "rep stosb" parm [ES DI] [AL] [CX BX]; - - void radmemset16(void far* dest,u16 value,u32 size); - #pragma aux radmemset16 = "cld" "and edi,0ffffh" "shl ecx,16" "mov cx,bx" "mov bx,ax" "shl eax,16" "mov ax,bx" "mov bl,cl" "shr ecx,1" "rep stosd" "mov cl,bl" "and cl,1" "rep stosw" parm [ES DI] [AX] [CX BX]; - - void radmemcpy(void far* dest,const void far* source,u32 size); - #pragma aux radmemcpy = "cld" "push ds" "mov ds,dx" "and esi,0ffffh" "and edi,0ffffh" "shl ecx,16" "mov cx,bx" "shr ecx,2" 0x67 "rep movsd" "mov cl,bl" "and cl,3" "rep movsb" "pop ds" parm [ES DI] [DX SI] [CX BX] modify [CX SI DI ES]; - - s8 radmemcmp(const void far* s1,const void far* s2,u32 len); - #pragma aux radmemcmp = "cld" "push ds" "mov ds,dx" "shl ecx,16" "mov cx,bx" "rep cmpsb" "setne al" "jbe end" "neg al" "end:" "pop ds" parm [ES DI] [DX SI] [CX BX] modify [CX SI DI ES]; - - char far* radstrcpy(void far* dest,const void far* source); - #pragma aux radstrcpy = "cld" "push ds" "mov ds,dx" "and esi,0xffff" "and edi,0xffff" "mov dx,di" "lp:" "lodsb" "stosb" "test al,0xff" "jnz lp" "pop ds" parm [ES DI] [DX SI] modify [AX DX DI SI ES] value [es dx]; - - char far* radstpcpy(void far* dest,const void far* source); - #pragma aux radstpcpy = "cld" "push ds" "mov ds,dx" "and esi,0xffff" "and edi,0xffff" "lp:" "lodsb" "stosb" "test al,0xff" "jnz lp" "dec di" "pop ds" parm [ES DI] [DX SI] modify [DI SI ES] value [es di]; - - u32 radstrlen(const void far* dest); - #pragma aux radstrlen = "cld" "and edi,0xffff" "mov ecx,0xffffffff" "xor eax,eax" 0x67 "repne scasb" "not ecx" "dec ecx" "movzx eax,cx" "shr ecx,16" parm [ES DI] modify [AX CX DI ES] value [CX AX]; - - char far* radstrcat(void far* dest,const void far* source); - #pragma aux radstrcat = "cld" "and edi,0xffff" "mov ecx,0xffffffff" "and esi,0xffff" "push ds" "mov ds,dx" "mov dx,di" "xor eax,eax" 0x67 "repne scasb" "dec edi" "lp:" "lodsb" "stosb" "test al,0xff" "jnz lp" "pop ds" \ - parm [ES DI] [DX SI] modify [AX CX DI SI ES] value [es dx]; - - char far* radstrchr(const void far* dest,char chr); - #pragma aux radstrchr = "cld" "lp:" 0x26 "lodsb" "cmp al,dl" "je fnd" "cmp al,0" "jnz lp" "xor ax,ax" "mov es,ax" "mov si,1" "fnd:" "dec si" parm [ES SI] [DL] modify [AX SI ES] value [es si]; - - s8 radstricmp(const void far* s1,const void far* s2); - #pragma aux radstricmp = "and edi,0xffff" "push ds" "mov ds,dx" "and esi,0xffff" "lp:" "mov al,[esi]" "mov ah,[edi]" "cmp al,'a'" "jb c1" "cmp al,'z'" "ja c1" "sub al,32" "c1:" \ - "cmp ah,'a'" "jb c2" "cmp ah,'z'" "ja c2" "sub ah,32" "c2:" "cmp al,ah" "jne set" "cmp al,0" "je set" \ - "inc esi" "inc edi" "jmp lp" "set:" "setne al" "jbe end" "neg al" "end:" "pop ds" \ - parm [ES DI] [DX SI] modify [AX DI SI]; - - u32 radstru32(const void far* dest); - #pragma aux radstru32 = "cld" "xor ecx,ecx" "xor ebx,ebx" "xor edi,edi" 0x26 "lodsb" "cmp al,45" "jne skip2" "mov edi,1" "jmp skip" "lp:" "mov eax,10" "mul ecx" "lea ecx,[eax+ebx]" \ - "skip:" 0x26 "lodsb" "skip2:" "cmp al,0x39" "ja dne" "cmp al,0x30" "jb dne" "mov bl,al" "sub bl,0x30" "jmp lp" "dne:" "test edi,1" "jz pos" "neg ecx" "pos:" \ - "movzx eax,cx" "shr ecx,16" parm [ES SI] modify [AX BX DX DI SI] value [cx ax]; - - u32 mult64anddiv(u32 m1,u32 m2,u32 d); - #pragma aux mult64anddiv = "shl ecx,16" "mov cx,ax" "shrd eax,edx,16" "mov ax,si" "mul ecx" "shl edi,16" "mov di,bx" "div edi" "shld edx,eax,16" "and edx,0xffff" "and eax,0xffff" parm [cx ax] [dx si] [di bx] \ - modify [ax bx cx dx si di] value [dx ax]; - - #endif - -#endif - -RADDEFEND - -#define u32neg1 ((u32)(s32)-1) -#define RAD_align(var) var; u8 junk##var[4-(sizeof(var)&3)]; -#define RAD_align_after(var) u8 junk##var[4-(sizeof(var)&3)]={0}; -#define RAD_align_init(var,val) var=val; u8 junk##var[4-(sizeof(var)&3)]={0}; -#define RAD_align_array(var,num) var[num]; u8 junk##var[4-(sizeof(var)&3)]; -#define RAD_align_string(var,str) char var[]=str; u8 junk##var[4-(sizeof(var)&3)]={0}; - - -typedef void PTR4* (RADLINK PTR4* RADMEMALLOC) (u32 bytes); -typedef void (RADLINK PTR4* RADMEMFREE) (void PTR4* ptr); - -#ifdef __RADMAC__ - #pragma export on -#endif -RADEXPFUNC void RADEXPLINK RADSetMemory(RADMEMALLOC a,RADMEMFREE f); -#ifdef __RADMAC__ - #pragma export off -#endif - -RADEXPFUNC void PTR4* RADEXPLINK radmalloc(u32 numbytes); -RADEXPFUNC void RADEXPLINK radfree(void PTR4* ptr); - -#ifdef __RADDOS__ - - RADDEFSTART - extern void* RADTimerSetupAddr; - extern void* RADTimerReadAddr; - extern void* RADTimerDoneAddr; - RADDEFEND - - typedef void RADEXPLINK (*RADTimerSetupType)(void); - typedef u32 RADEXPLINK (*RADTimerReadType)(void); - typedef void RADEXPLINK (*RADTimerDoneType)(void); - - #define RADTimerSetup() ((RADTimerSetupType)(RADTimerSetupAddr))() - #define RADTimerRead() ((RADTimerReadType)(RADTimerReadAddr))() - #define RADTimerDone() ((RADTimerDoneType)(RADTimerDoneAddr))() - -#else - - #define RADTimerSetup() - #define RADTimerDone() - - #if (defined(__RAD16__) || defined(__RADWINEXT__)) - - #define RADTimerRead timeGetTime - - #else - - RADEXPFUNC u32 RADEXPLINK RADTimerRead(void); - - #endif - -#endif - - -#ifdef __WATCOMC__ - - char bkbhit(); - #pragma aux bkbhit = "mov ah,1" "int 0x16" "lahf" "shr eax,14" "and eax,1" "xor al,1" ; - - char bgetch(); - #pragma aux bgetch = "xor ah,ah" "int 0x16" "test al,0xff" "jnz done" "mov al,ah" "or al,0x80" "done:" modify [AX]; - - void BreakPoint(); - #pragma aux BreakPoint = "int 3"; - - u8 radinp(u16 p); - #pragma aux radinp = "in al,dx" parm [DX]; - - u8 radtoupper(u8 p); - #pragma aux radtoupper = "cmp al,'a'" "jb c1" "cmp al,'z'" "ja c1" "sub al,32" "c1:" parm [al] value [al]; - - void radoutp(u16 p,u8 v); - #pragma aux radoutp = "out dx,al" parm [DX] [AL]; - -#else - -// for multi-processor machines - -#ifdef __RADNT__ - #define LockedIncrement(var) __asm { lock inc [var] } - #define LockedDecrement(var) __asm { lock dec [var] } - void __inline LockedIncrementFunc(void PTR4* var) { - __asm { - mov eax,[var] - lock inc [eax] - } - } - - void __inline LockedDecrementFunc(void PTR4* var) { - __asm { - mov eax,[var] - lock dec [eax] - } - } - -#else - - #ifdef __RADMAC__ - - #define LockedIncrement(var) {++(var);} - #define LockedDecrement(var) {--(var);} - - #define LockedIncrementFunc(ptr) {++(*((u32*)(ptr)));} - #define LockedDecrementFunc(ptr) {--(*((u32*)(ptr)));} - - #else - - #define LockedIncrement(var) __asm { inc [var] } - #define LockedDecrement(var) __asm { dec [var] } - void __inline LockedIncrementFunc(void PTR4* var) { __asm { mov eax,[var] - inc [eax] } } - void __inline LockedDecrementFunc(void PTR4* var) { __asm { mov eax,[var] - dec [eax] } } - #endif - -#endif - -#endif - -#endif - -#endif - diff --git a/code/win32/vssver.scc b/code/win32/vssver.scc deleted file mode 100644 index c121f66..0000000 Binary files a/code/win32/vssver.scc and /dev/null differ diff --git a/code/win32/win_glimp.cpp b/code/win32/win_glimp.cpp index 500af6e..434c301 100644 --- a/code/win32/win_glimp.cpp +++ b/code/win32/win_glimp.cpp @@ -735,25 +735,21 @@ static rserr_t GLW_SetMode( int mode, "display can be initialized with the current\n" "desktop display depth. Select 'OK' to try\n" "anyway. Select 'Cancel' to try a fullscreen\n" - "mode instead. (e.g. if you have a 3Dfx Voodoo,\n" - "Voodoo-2, or Voodoo Rush 3D accelerator installed)"; + "mode instead."; const char *psErrorTitle_German = "Falsche Desktop-Farbtiefe"; const char *psErrorBody_German = "Es ist unwahrscheinlich, dass bei der momentanen\n" "Desktop-Farbiefe ein Fenstermodus initialisiert\n" "werden kann. Mit 'OK' versuchen Sie es dennoch,\n" "mit 'Abbrechen' wechselt das Spiel in den\n" - "Vollbildmodus (wenn Sie z. B. eine 3Dfx Voodoo 1\n" - "oder 2 sowie Voodoo Rush 3D-Karte besitzen)"; + "Vollbildmodus."; const char *psErrorTitle_French = "Basse Intensité De la Couleur DeskTop"; const char *psErrorBody_French = "Il est fortement peu probable qu'un correct windowed\n" "l'affichage peut être initialisé avec la profondeur\n" "de bureau actuelle d'affichage. Choisissez 'OK'\n" "pour essayer de toute façon. Choisissez 'ANNUL'\n" - "pour essayer a fullscreen le mode à la place\n" - "(par exemple si vous faites installer un 3Dfx Voodoo,\n" - "Voodoo-2, ou accélérateur de Voodoo Rush 3D)"; + "pour essayer a fullscreen le mode à la place."; const char *psHeadText = psErrorTitle_English; const char *psBodyText = psErrorBody_English; @@ -1221,7 +1217,7 @@ static void GLW_InitExtensions( void ) // GL_ATI_pn_triangles qglPNTrianglesiATI = NULL; r_ATI_NPATCH_available = ri.Cvar_Get( "r_ATI_NPATCH_available", "0",CVAR_ROM ); - if ( strstr( glConfig.extensions_string, "GL_ATI_pn_triangles" ) ) +/* if ( strstr( glConfig.extensions_string, "GL_ATI_pn_triangles" ) ) { ri.Cvar_Set( "r_ATI_NPATCH_available", "1" ); if ( r_ati_pn_triangles->integer ) @@ -1241,6 +1237,7 @@ static void GLW_InitExtensions( void ) { ri.Printf( PRINT_ALL, "...GL_ATI_pn_triangles not found\n" ); } +*/ #endif // _NPATCH } diff --git a/code/win32/win_shared.cpp b/code/win32/win_shared.cpp index 44d01ac..b2bbc20 100644 --- a/code/win32/win_shared.cpp +++ b/code/win32/win_shared.cpp @@ -14,16 +14,11 @@ Sys_Milliseconds ================ */ -int sys_timeBase; int Sys_Milliseconds (void) { + static int sys_timeBase = timeGetTime(); int sys_curtime; - static qboolean initialized = qfalse; - if (!initialized) { - sys_timeBase = timeGetTime(); - initialized = qtrue; - } sys_curtime = timeGetTime() - sys_timeBase; return sys_curtime; diff --git a/code/win32/win_wndproc.cpp b/code/win32/win_wndproc.cpp index ccf12d5..ac09dac 100644 --- a/code/win32/win_wndproc.cpp +++ b/code/win32/win_wndproc.cpp @@ -210,6 +210,32 @@ static byte s_scantokey_italian[128] = 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // 7 }; +/* +// returns true if key should be ignored because NUMLOCK is on and therefore KP arrows should be numbers only... +// +qboolean SwallowBadNumLockedKPKey( int iKey ) +{ + switch (iKey) + { + case K_KP_HOME: + case K_KP_UPARROW: + case K_KP_PGUP: + case K_KP_LEFTARROW: + case K_KP_RIGHTARROW: + case K_KP_END: + case K_KP_DOWNARROW: + case K_KP_PGDN: + + if (GetKeyState(VK_NUMLOCK) & 0x8001) + { + return qtrue; + } + } + + return qfalse; +} +*/ + /* ======= MapKey diff --git a/code/win32/winquake.res b/code/win32/winquake.res new file mode 100644 index 0000000..9ef8365 Binary files /dev/null and b/code/win32/winquake.res differ diff --git a/ui/menudef.h b/ui/menudef.h index 5c3b0a4..56e6574 100644 --- a/ui/menudef.h +++ b/ui/menudef.h @@ -12,14 +12,15 @@ #define ITEM_TYPE_CHECKBOX 3 // check box #define ITEM_TYPE_EDITFIELD 4 // editable text, associated with a cvar #define ITEM_TYPE_COMBO 5 // drop down list -#define ITEM_TYPE_LISTBOX 6 // scrollable list -#define ITEM_TYPE_MODEL 7 // model -#define ITEM_TYPE_OWNERDRAW 8 // owner draw, name specs what it is -#define ITEM_TYPE_NUMERICFIELD 9 // editable text, associated with a cvar -#define ITEM_TYPE_SLIDER 10 // mouse speed, volume, etc. -#define ITEM_TYPE_YESNO 11 // yes no cvar setting -#define ITEM_TYPE_MULTI 12 // multiple list setting, enumerated -#define ITEM_TYPE_BIND 13 // multiple list setting, enumerated +#define ITEM_TYPE_LISTBOX 6 // scrollable list +#define ITEM_TYPE_MODEL 7 // model +#define ITEM_TYPE_OWNERDRAW 8 // owner draw, name specs what it is +#define ITEM_TYPE_NUMERICFIELD 9 // editable text, associated with a cvar +#define ITEM_TYPE_SLIDER 10 // mouse speed, volume, etc. +#define ITEM_TYPE_YESNO 11 // yes no cvar setting +#define ITEM_TYPE_MULTI 12 // multiple list setting, enumerated +#define ITEM_TYPE_BIND 13 // multiple list setting, enumerated +#define ITEM_TYPE_TEXTSCROLL 14 // scrolls text #define ITEM_ALIGN_LEFT 0 // left alignment #define ITEM_ALIGN_CENTER 1 // center alignment diff --git a/ui/vssver.scc b/ui/vssver.scc deleted file mode 100644 index c146db8..0000000 Binary files a/ui/vssver.scc and /dev/null differ diff --git a/utils/Assimilate/ASEFile.cpp b/utils/Assimilate/ASEFile.cpp new file mode 100644 index 0000000..3d78af6 --- /dev/null +++ b/utils/Assimilate/ASEFile.cpp @@ -0,0 +1,331 @@ +// ASEFile.cpp + +#include "Module.h" +#include "Tokenizer.h" +#include "AlertErrHandler.h" +#include "ASEFile.h" + +keywordArray_t CASEFile::s_symbols[] = +{ + "*", TK_ASE_ASTERISK, + "{", TK_ASE_OBRACE, + "}", TK_ASE_CBRACE, + ":", TK_ASE_COLON, + NULL, TK_EOF, +}; + +keywordArray_t CASEFile::s_keywords[] = +{ + "GEOMOBJECT", TK_GEOMOBJECT, + "SCENE", TK_SCENE, + "MATERIAL_LIST", TK_MATERIAL_LIST, + "COMMENT", TK_ASE_COMMENT, + "3DSMAX_ASCIIEXPORT", TK_3DSMAX_ASCIIEXPORT, + NULL, TK_EOF, +}; + +keywordArray_t CASEFile::s_sceneKeywords[] = +{ + NULL, TK_EOF, +}; + +keywordArray_t CASEFile::s_materialKeywords[] = +{ + NULL, TK_EOF, +}; + +keywordArray_t CASEFile::s_mapKeywords[] = +{ + NULL, TK_EOF, +}; + +keywordArray_t CASEFile::s_objectKeywords[] = +{ + NULL, TK_EOF, +}; + +keywordArray_t CASEFile::s_nodeKeywords[] = +{ + NULL, TK_EOF, +}; + +keywordArray_t CASEFile::s_meshKeywords[] = +{ + NULL, TK_EOF, +}; + +keywordArray_t CASEFile::s_vertexKeywords[] = +{ + NULL, TK_EOF, +}; + +keywordArray_t CASEFile::s_faceKeywords[] = +{ + NULL, TK_EOF, +}; + +keywordArray_t CASEFile::s_faceOptionKeywords[] = +{ + NULL, TK_EOF, +}; + +keywordArray_t CASEFile::s_tvertKeywords[] = +{ + NULL, TK_EOF, +}; + +keywordArray_t CASEFile::s_tfaceKeywords[] = +{ + NULL, TK_EOF, +}; + +keywordArray_t CASEFile::s_animationKeywords[] = +{ + NULL, TK_EOF, +}; + +keywordArray_t CASEFile::s_contorlPosBezierKeywords[] = +{ + NULL, TK_EOF, +}; + +keywordArray_t CASEFile::s_controlRotTCBKeywords[] = +{ + NULL, TK_EOF, +}; + +keywordArray_t CASEFile::s_controlScaleBezierKeywords[] = +{ + NULL, TK_EOF, +}; + +keywordArray_t CASEFile::s_labelKeywords[] = +{ + NULL, TK_EOF, +}; + +CASEFile::CASEFile() +{ +} + +CASEFile::~CASEFile() +{ +} + +CASEFile* CASEFile::Create(LPCTSTR filename) +{ + CASEFile* retval = new CASEFile(); + retval->Init(filename); + return retval; +} + +void CASEFile::Delete() +{ + if (m_file != NULL) + { + free(m_file); + m_file = NULL; + } + if (m_comment != NULL) + { + free(m_comment); + m_comment = NULL; + } + delete this; +} + +void CASEFile::Init(LPCTSTR filename) +{ + if ((filename != NULL) && (strlen(filename) > 0)) + { + m_file = (char*)malloc(strlen(filename) + 1); + strcpy(m_file, filename); + } + else + { + m_file = NULL; + } + m_export = 0; + m_comment = NULL; +} + +void CASEFile::Parse() +{ + if (m_file == NULL) + { + return; + } + CAlertErrHandler errhandler; + CTokenizer* tokenizer = CTokenizer::Create(TKF_USES_EOL | TKF_NUMERICIDENTIFIERSTART); + tokenizer->SetErrHandler(&errhandler); + tokenizer->SetKeywords(s_keywords); + tokenizer->SetSymbols(s_symbols); + tokenizer->AddParseFile(m_file, 16 * 1024); + int tokType = TK_UNDEFINED; + while(tokType != TK_EOF) + { + CToken* curToken = tokenizer->GetToken(); + if (curToken->GetType() == TK_EOF) + { + curToken->Delete(); + tokType = TK_EOF; + break; + } + if (curToken->GetType() == TK_EOL) + { + curToken->Delete(); + continue; + } + if (curToken->GetType() != TK_ASE_ASTERISK) + { + tokenizer->Error(TKERR_UNEXPECTED_TOKEN); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + continue; + } + curToken->Delete(); + curToken = tokenizer->GetToken(); + tokType = curToken->GetType(); + curToken->Delete(); + switch(tokType) + { + case TK_EOF: + break; + case TK_GEOMOBJECT: + ParseGeomObject(tokenizer); + break; + case TK_SCENE: + ParseScene(tokenizer); + break; + case TK_MATERIAL_LIST: + ParseMaterialList(tokenizer); + break; + case TK_ASE_COMMENT: + ParseComment(tokenizer); + break; + case TK_3DSMAX_ASCIIEXPORT: + ParseAsciiExport(tokenizer); + break; + default: + tokenizer->Error(TKERR_UNEXPECTED_TOKEN); + tokenizer->GetToEndOfLine()->Delete(); + break; + } + } +} + +void CASEFile::ParseAsciiExport(CTokenizer* tokenizer) +{ + CToken* token = tokenizer->GetToken(); + if (token->GetType() != TK_INT) + { + tokenizer->Error(token->GetStringValue(), TKERR_UNEXPECTED_TOKEN); + token->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + m_export = token->GetIntValue(); + token->Delete(); +} + +void CASEFile::ParseComment(CTokenizer* tokenizer) +{ + CToken* token = tokenizer->GetToken(); + if (token->GetType() != TK_STRING) + { + tokenizer->Error(token->GetStringValue(), TKERR_UNEXPECTED_TOKEN); + token->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + m_comment = (char*)malloc(strlen(token->GetStringValue()) + 1); + strcpy(m_comment, token->GetStringValue()); + token->Delete(); +} + +void CASEFile::ParseMaterialList(CTokenizer* tokenizer) +{ +} + +void CASEFile::ParseScene(CTokenizer* tokenizer) +{ + CToken* token = tokenizer->GetToken(s_sceneKeywords, 0, TKF_USES_EOL); + if (token->GetType() != TK_ASE_OBRACE) + { + tokenizer->Error(token->GetStringValue(), TKERR_UNEXPECTED_TOKEN); + token->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + token->Delete(); + + bool inScene = true; + while(inScene) + { + token = tokenizer->GetToken(s_sceneKeywords, 0, 0); + switch(token->GetType()) + { + case TK_EOF: + inScene = false; + tokenizer->Error("End of File", TKERR_UNEXPECTED_TOKEN); + token->Delete(); + break; + case TK_ASE_CBRACE: + token->Delete(); + inScene = false; + break; + case TK_EOL: + token->Delete(); + break; + case TK_ASE_ASTERISK: + token->Delete(); + token = tokenizer->GetToken(s_sceneKeywords, 0, 0); + switch (token->GetType()) + { + case TK_SCENE_FILENAME: + token->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + break; + case TK_SCENE_FIRSTFRAME: + token->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + break; + case TK_SCENE_LASTFRAME: + token->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + break; + case TK_SCENE_FRAMESPEED: + token->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + break; + case TK_SCENE_TICKSPERFRAME: + token->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + break; + case TK_SCENE_BACKGROUND_STATIC: + token->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + break; + case TK_SCENE_AMBIENT_STATIC: + token->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + break; + default: + tokenizer->Error(token->GetStringValue(), TKERR_UNEXPECTED_TOKEN); + token->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + break; + } + default: + tokenizer->Error(token->GetStringValue(), TKERR_UNEXPECTED_TOKEN); + token->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + } + } +} + +void CASEFile::ParseGeomObject(CTokenizer* tokenizer) +{ +} + + diff --git a/utils/Assimilate/ASEFile.h b/utils/Assimilate/ASEFile.h new file mode 100644 index 0000000..2bee929 --- /dev/null +++ b/utils/Assimilate/ASEFile.h @@ -0,0 +1,150 @@ +// ASEFile.h + +enum +{ + TK_ASE_COLON = TK_USERDEF, + TK_ASE_OBRACE, + TK_ASE_CBRACE, + TK_ASE_ASTERISK, + TK_MESH, + TK_TIMEVALUE, + TK_MESH_NUMVERTEX, + TK_MESH_NUMFACES, + TK_MESH_VERTEX_LIST, + TK_MESH_VERTEX, + TK_MESH_FACE_LIST, + TK_MESH_FACE, + TK_MESH_NUMTVERTEX, + TK_MESH_TVERTLIST, + TK_MESH_TVERT, + TK_MESH_NUMTVFACES, + TK_MESH_TFACELIST, + TK_MESH_TFACE, + TK_MESH_ANIMATION, + TK_WIREFRAME_COLOR, + TK_CONTROL_BEZIER_SCALE_KEY, + TK_CONTROL_SCALE_BEZIER, + TK_CONTROL_ROT_TCB, + TK_CONTROL_TCB_ROT_KEY, + TK_CONTROL_POS_BEZIER, + TK_CONTROL_BEZIER_POS_KEY, + TK_NODE_NAME, + TK_TM_ANIMATION, + TK_PROP_RECVSHADOW, + TK_PROP_CASTSHADOW, + TK_PROP_MOTIONBLUR, + TK_GEOMOBJECT, + TK_NODE_PARENT, + TK_NODE_TM, + TK_INHERIT_POS, + TK_INHERIT_ROT, + TK_INHERIT_SCL, + TK_TM_ROW0, + TK_TM_ROW1, + TK_TM_ROW2, + TK_TM_ROW3, + TK_TM_POS, + TK_TM_ROTAXIS, + TK_TM_ROTANGLE, + TK_TM_SCALE, + TK_TM_SCALEAXIS, + TK_TM_SCALEAXISANG, + TK_3DSMAX_ASCIIEXPORT, + TK_ASE_COMMENT, + TK_SCENE, + TK_SCENE_FILENAME, + TK_SCENE_FIRSTFRAME, + TK_SCENE_LASTFRAME, + TK_SCENE_FRAMESPEED, + TK_SCENE_TICKSPERFRAME, + TK_SCENE_BACKGROUND_STATIC, + TK_SCENE_AMBIENT_STATIC, + TK_MATERIAL_LIST, + TK_MATERIAL_COUNT, + TK_MATERIAL, + TK_MATERIAL_NAME, + TK_MATERIAL_CLASS, + TK_MATERIAL_AMBIENT, + TK_MATERIAL_DIFFUSE, + TK_MATREIAL_SPECULAR, + TK_MATERIAL_SHINE, + TK_MATERIAL_SHINESTRENGTH, + TK_MATERIAL_TRANSPARENCY, + TK_MATERIAL_WIRESIZE, + TK_MATERIAL_SHADING, + TK_MATERIAL_XP_FALLOFF, + TK_MATERIAL_SELFILLUM, + TK_MATERIAL_FALLOFF, + TK_MATERIAL_SOFTEN, + TK_MATERIAL_XP_TYPE, + TK_MAP_DIFFUSE, + TK_MAP_NAME, + TK_MAP_CLASS, + TK_MAP_SUBNO, + TK_MAP_AMOUNT, + TK_BITMAP, + TK_MAP_TYPE, + TK_UVW_U_OFFSET, + TK_UVW_V_OFFSET, + TK_UVW_U_TILING, + TK_UVW_V_TILING, + TK_UVW_ANGLE, + TK_UVW_BLUR, + TK_UVW_BLUR_OFFSET, + TK_UVW_NOUSE_AMT, + TK_UVW_NOISE_SIZE, + TK_UVW_NOISE_LEVEL, + TK_UVW_NOISE_PHASE, + TK_BITMAP_FILTER, + TK_MESH_SMOOTHING, + TK_MESH_MTLID, + TK_LABEL_A, + TK_LABEL_B, + TK_LABEL_C, + TK_LABEL_AB, + TK_LABEL_BC, + TK_LABEL_CA, +}; + +class CASEFile +{ +public: + CASEFile(); + ~CASEFile(); + static CASEFile* Create(LPCTSTR filename); + void Delete(); + + void Parse(); + +protected: + void Init(LPCTSTR filename); + + void ParseAsciiExport(CTokenizer* tokenizer); + void ParseComment(CTokenizer* tokenizer); + void ParseMaterialList(CTokenizer* tokenizer); + void ParseScene(CTokenizer* tokenizer); + void ParseGeomObject(CTokenizer* tokenizer); + + char* m_file; + int m_export; + char* m_comment; + + static keywordArray_t s_symbols[]; + static keywordArray_t s_keywords[]; + static keywordArray_t s_sceneKeywords[]; + static keywordArray_t s_materialKeywords[]; + static keywordArray_t s_mapKeywords[]; + static keywordArray_t s_objectKeywords[]; + static keywordArray_t s_nodeKeywords[]; + static keywordArray_t s_meshKeywords[]; + static keywordArray_t s_vertexKeywords[]; + static keywordArray_t s_faceKeywords[]; + static keywordArray_t s_faceOptionKeywords[]; + static keywordArray_t s_tvertKeywords[]; + static keywordArray_t s_tfaceKeywords[]; + static keywordArray_t s_animationKeywords[]; + static keywordArray_t s_contorlPosBezierKeywords[]; + static keywordArray_t s_controlRotTCBKeywords[]; + static keywordArray_t s_controlScaleBezierKeywords[]; + static keywordArray_t s_labelKeywords[]; +}; diff --git a/utils/Assimilate/AlertErrHandler.cpp b/utils/Assimilate/AlertErrHandler.cpp new file mode 100644 index 0000000..1c94b4e --- /dev/null +++ b/utils/Assimilate/AlertErrHandler.cpp @@ -0,0 +1,14 @@ +// AlertErrHandler.cpp + +#include +#include "Module.h" +#include "AlertErrHandler.h" + +bool gbParseError = false; + +void CAlertErrHandler::Error(int theError, LPCTSTR errString) +{ + gbParseError = true; + AfxMessageBox(errString); +} + diff --git a/utils/Assimilate/AlertErrHandler.h b/utils/Assimilate/AlertErrHandler.h new file mode 100644 index 0000000..bd5b018 --- /dev/null +++ b/utils/Assimilate/AlertErrHandler.h @@ -0,0 +1,11 @@ +// AlertErrHandler.h + +class CAlertErrHandler : public CErrHandler +{ +public: + CAlertErrHandler() {Init();}; + + virtual void Error(int theError, LPCTSTR errString); +}; + +extern bool gbParseError; diff --git a/utils/Assimilate/AnimPicker.cpp b/utils/Assimilate/AnimPicker.cpp new file mode 100644 index 0000000..53b4338 --- /dev/null +++ b/utils/Assimilate/AnimPicker.cpp @@ -0,0 +1,257 @@ +// AnimPicker.cpp : implementation file +// + +#include "stdafx.h" +//#include "assimilate.h" +#include "includes.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CAnimPicker dialog +static bool gbFilterOutUsed = false; + +CAnimPicker::CAnimPicker(char *psReturnString, + CWnd* pParent /*=NULL*/) + : CDialog(CAnimPicker::IDD, pParent) +{ + //{{AFX_DATA_INIT(CAnimPicker) + m_bFilterOutUsed = gbFilterOutUsed; + //}}AFX_DATA_INIT + + m_psReturnString= psReturnString; + *m_psReturnString = 0; +} + + +void CAnimPicker::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CAnimPicker) + DDX_Check(pDX, IDC_CHECK_FILTEROUTUSED, m_bFilterOutUsed); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CAnimPicker, CDialog) + //{{AFX_MSG_MAP(CAnimPicker) + ON_LBN_DBLCLK(IDC_LIST_BOTH, OnDblclkListBoth) + ON_LBN_DBLCLK(IDC_LIST_LEGS, OnDblclkListLegs) + ON_LBN_DBLCLK(IDC_LIST_TORSO, OnDblclkListTorso) + ON_LBN_SELCHANGE(IDC_LIST_LEGS, OnSelchangeListLegs) + ON_LBN_SELCHANGE(IDC_LIST_TORSO, OnSelchangeListTorso) + ON_LBN_SELCHANGE(IDC_LIST_BOTH, OnSelchangeListBoth) + ON_BN_CLICKED(IDC_CHECK_FILTEROUTUSED, OnCheckFilteroutused) + ON_WM_DESTROY() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CAnimPicker message handlers + +const char *sEnumUsedString = "* "; +const char *sEnumSeperatorString_Prefix = "====== "; // (the spaces keep these unique/comparable) +const char *sEnumSeperatorString_Suffix = " ======"; // + + +void CAnimPicker::FillListBoxes() +{ + // fill in the enum list boxes... + // + CListBox* boxLegs = (CListBox*)GetDlgItem(IDC_LIST_LEGS); + CListBox* boxTorso= (CListBox*)GetDlgItem(IDC_LIST_TORSO); + CListBox* boxBoth = (CListBox*)GetDlgItem(IDC_LIST_BOTH); + boxLegs->ResetContent(); + boxTorso->ResetContent(); + boxBoth->ResetContent(); + + CModel* theModel = ghAssimilateView->GetDocument()->GetCurrentUserSelectedModel(); + ASSERT(theModel); + if (theModel) + { + for (int i=0; ; i++) + { + LPCSTR p = ((CAssimilateApp*)AfxGetApp())->GetEnumEntry(i); + if (!p) + break; + + CString string = p; + + if (theModel->AnimEnumInUse(p)) + { + if (m_bFilterOutUsed) + continue; + string.Insert(0,sEnumUsedString); + } + + if (IsEnumSeperator(p)) + { + string = StripSeperatorStart(p); + string.Insert(0,sEnumSeperatorString_Prefix); + string+=sEnumSeperatorString_Suffix; + } + + CListBox* listBoxPtr = NULL; + switch (GetEnumTypeFromString(p)) // note (p), *not* (string) + { + case ET_BOTH: listBoxPtr = boxBoth; break; + case ET_TORSO: listBoxPtr = boxTorso; break; + case ET_LEGS: listBoxPtr = boxLegs; break; + default: ASSERT(0); break; + } + if (listBoxPtr) + { + // keep an index to the original enum for comment-diving reasons... + // + int iIndex = listBoxPtr->InsertString(-1,string); + listBoxPtr->SetItemData(iIndex,(DWORD)p); + } + } + } +} + + +BOOL CAnimPicker::OnInitDialog() +{ + CDialog::OnInitDialog(); + + FillListBoxes(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + + +void CAnimPicker::OnDblclkListBoth() +{ +// not needed now... +// +// CListBox* boxBoth = (CListBox*)GetDlgItem(IDC_LIST_BOTH); +// boxBoth->GetText(boxBoth->GetCurSel(),m_psReturnString); + + if (ReturnStringIsValid()) + { + EndDialog( IDOK ); + } +} + +void CAnimPicker::OnDblclkListLegs() +{ +// not needed now... +// +// CListBox* boxLegs = (CListBox*)GetDlgItem(IDC_LIST_LEGS); +// boxLegs->GetText(boxLegs->GetCurSel(),m_psReturnString); + + if (ReturnStringIsValid()) + { + EndDialog( IDOK ); + } +} + +void CAnimPicker::OnDblclkListTorso() +{ +// not needed now... +// +// CListBox* boxTorso = (CListBox*)GetDlgItem(IDC_LIST_TORSO); +// boxTorso->GetText(boxTorso->GetCurSel(),m_psReturnString); + + if (ReturnStringIsValid()) + { + EndDialog( IDOK ); + } +} + + +bool CAnimPicker::ReturnStringIsValid() +{ + if (!strlen(m_psReturnString) || + !strnicmp(m_psReturnString,sEnumUsedString, strlen(sEnumUsedString)) || + !strnicmp(m_psReturnString,sEnumSeperatorString_Prefix, strlen(sEnumSeperatorString_Prefix)) + ) + { + return false; + } + + return true; +} + +// updates the return string for if needed later (so ENTER key can work), handles onscreen Comments. +// +void CAnimPicker::HandleSelectionChange(CListBox* listbox) +{ + if (listbox->GetCurSel() != LB_ERR) + { + CString selectedString; + listbox->GetText(listbox->GetCurSel(),selectedString); + + LPCSTR lpOriginalEnumName = (LPCSTR) listbox->GetItemData(listbox->GetCurSel()); + + LPCSTR psComment = lpOriginalEnumName?((CAssimilateApp*)AfxGetApp())->GetEnumComment(lpOriginalEnumName):NULL; + + GetDlgItem(IDC_STATIC_COMMENT)->SetWindowText(psComment?va("Comment: %s",psComment):""); + + strcpy(m_psReturnString,selectedString); + + if (!ReturnStringIsValid()) + { + strcpy(m_psReturnString,""); + } + } +} + +void CAnimPicker::OnSelchangeListLegs() +{ + CListBox* listbox = (CListBox*)GetDlgItem(IDC_LIST_LEGS); + + HandleSelectionChange(listbox); +} + +void CAnimPicker::OnSelchangeListTorso() +{ + CListBox* listbox = (CListBox*)GetDlgItem(IDC_LIST_TORSO); + + HandleSelectionChange(listbox); +} + +void CAnimPicker::OnSelchangeListBoth() +{ + CListBox* listbox = (CListBox*)GetDlgItem(IDC_LIST_BOTH); + + HandleSelectionChange(listbox); +} + + +BOOL CAnimPicker::PreTranslateMessage(MSG* pMsg) +{ + int i = VK_RETURN; + + if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN) + { + if (ReturnStringIsValid()) + { + EndDialog( IDOK ); + } + return 1; // disable the RETURN key + } + + return CDialog::PreTranslateMessage(pMsg); +} + + +void CAnimPicker::OnCheckFilteroutused() +{ + UpdateData(DIALOG_TO_DATA); + FillListBoxes(); +} + +void CAnimPicker::OnDestroy() +{ + CDialog::OnDestroy(); + + gbFilterOutUsed = !!m_bFilterOutUsed; +} + diff --git a/utils/Assimilate/AnimPicker.h b/utils/Assimilate/AnimPicker.h new file mode 100644 index 0000000..acdebe0 --- /dev/null +++ b/utils/Assimilate/AnimPicker.h @@ -0,0 +1,62 @@ +#if !defined(AFX_ANIMPICKER_H__A3E6F523_45D1_11D3_8A22_00500424438B__INCLUDED_) +#define AFX_ANIMPICKER_H__A3E6F523_45D1_11D3_8A22_00500424438B__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// AnimPicker.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CAnimPicker dialog + +class CAnimPicker : public CDialog +{ +// Construction +public: + CAnimPicker(char *psReturnString, CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CAnimPicker) + enum { IDD = IDD_ANIMPICKER }; + BOOL m_bFilterOutUsed; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CAnimPicker) + public: + virtual BOOL PreTranslateMessage(MSG* pMsg); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + char* m_psReturnString; + bool ReturnStringIsValid(); + void HandleSelectionChange(CListBox* listbox); + + // Generated message map functions + //{{AFX_MSG(CAnimPicker) + afx_msg void OnDblclkListBoth(); + afx_msg void OnDblclkListLegs(); + afx_msg void OnDblclkListTorso(); + virtual BOOL OnInitDialog(); + afx_msg void OnSelchangeListLegs(); + afx_msg void OnSelchangeListTorso(); + afx_msg void OnSelchangeListBoth(); + afx_msg void OnCheckFilteroutused(); + afx_msg void OnDestroy(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + + void FillListBoxes(); +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_ANIMPICKER_H__A3E6F523_45D1_11D3_8A22_00500424438B__INCLUDED_) diff --git a/utils/Assimilate/Assimilate.cpp b/utils/Assimilate/Assimilate.cpp new file mode 100644 index 0000000..eb903ae --- /dev/null +++ b/utils/Assimilate/Assimilate.cpp @@ -0,0 +1,705 @@ +// Assimilate.cpp : Defines the class behaviors for the application. +// + +#include "stdafx.h" +#include "Includes.h" +#include "sourcesafe.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +bool gbViewAnimEnums = false; +bool gbViewFrameDetails = false; +bool gbViewFrameDetails_Additional = false; + +///////////////////////////////////////////////////////////////////////////// +// CAssimilateApp + +BEGIN_MESSAGE_MAP(CAssimilateApp, CWinApp) + //{{AFX_MSG_MAP(CAssimilateApp) + ON_COMMAND(ID_APP_ABOUT, OnAppAbout) + ON_COMMAND(IDM_PROPERTIES, OnProperties) + //}}AFX_MSG_MAP + // Standard file based document commands + ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew) + ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen) + // Standard print setup command + ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup) +END_MESSAGE_MAP() + +keywordArray_t CAssimilateApp::s_Symbols[] = +{ + "{", TK_OBRACE, + "}", TK_CBRACE, + ",", TK_COMMA, + ";", TK_SEMICOLON, + NULL, TK_EOF, +}; + +keywordArray_t CAssimilateApp::s_Keywords[] = +{ + "enum", TK_ENUM, + NULL, TK_EOF, +}; + +///////////////////////////////////////////////////////////////////////////// +// CAssimilateApp construction + +CAssimilateApp::CAssimilateApp() +{ + // TODO: add construction code here, + // Place all significant initialization in InitInstance +} + +///////////////////////////////////////////////////////////////////////////// +// The one and only CAssimilateApp object + +CAssimilateApp theApp; + +const TCHAR CAssimilateApp::c_prefSection[] = "Preferences"; +const TCHAR CAssimilateApp::c_enumFilename[] = "Enum Filename"; +const TCHAR CAssimilateApp::c_buffersize[] = "Buffer size"; +const TCHAR CAssimilateApp::c_QDataFilename[] = "QData filename"; + +void CAssimilateApp::LoadRegistry() +{ + m_bMultiPlayerMode = !!GetProfileInt(c_prefSection, "m_bMultiPlayerMode", m_bMultiPlayerMode); + m_enumFilename = GetProfileString(c_prefSection, c_enumFilename, m_enumFilename); + m_buffersize = GetProfileInt (c_prefSection, c_buffersize, m_buffersize); + m_QDataFilename = GetProfileString(c_prefSection, c_QDataFilename, m_QDataFilename); + m_QuakeDir = GetProfileString(c_prefSection, "m_QuakeDir", m_QuakeDir); + gbViewAnimEnums = !!GetProfileInt(c_prefSection, "gbViewAnimEnums", gbViewAnimEnums); + gbViewFrameDetails = !!GetProfileInt(c_prefSection, "gbViewFrameDetails", gbViewFrameDetails); + gbViewFrameDetails_Additional = !!GetProfileInt(c_prefSection, "gbViewFrameDetails_Additional", gbViewFrameDetails_Additional); + +} + +void CAssimilateApp::SaveRegistry() +{ + WriteProfileInt(c_prefSection, "m_bMultiPlayerMode", m_bMultiPlayerMode); + WriteProfileString(c_prefSection, c_enumFilename, m_enumFilename); + WriteProfileInt (c_prefSection, c_buffersize, m_buffersize); + WriteProfileString(c_prefSection, c_QDataFilename, m_QDataFilename); + WriteProfileString(c_prefSection, "m_QuakeDir", m_QuakeDir); + WriteProfileInt(c_prefSection, "gbViewAnimEnums", gbViewAnimEnums); + WriteProfileInt(c_prefSection, "gbViewFrameDetails", gbViewFrameDetails); + WriteProfileInt(c_prefSection, "gbViewFrameDetails_Additional", gbViewFrameDetails_Additional); +} + +///////////////////////////////////////////////////////////////////////////// +// CAssimilateApp initialization + +BOOL CAssimilateApp::InitInstance() +{ + m_bMultiPlayerMode = bDEFAULT_MULTIPLAYER_MODE; + m_enumFilename = sDEFAULT_ENUM_FILENAME; + m_buffersize = dwDEFAULT_BUFFERSIZE; + m_QDataFilename = sDEFAULT_QDATA_LOCATION; + m_QuakeDir = sDEFAULT_QUAKEDIR; + + AfxEnableControlContainer(); + + // Standard initialization + // If you are not using these features and wish to reduce the size + // of your final executable, you should remove from the following + // the specific initialization routines you do not need. + +#ifdef _AFXDLL + Enable3dControls(); // Call this when using MFC in a shared DLL +#else + Enable3dControlsStatic(); // Call this when linking to MFC statically +#endif + + // Change the registry key under which our settings are stored. + // TODO: You should modify this string to be something appropriate + // such as the name of your company or organization. + SetRegistryKey(_T("Local AppWizard-Generated Applications")); + + LoadStdProfileSettings(12); // Load standard INI file options (including MRU) + LoadRegistry(); + // Register the application's document templates. Document templates + // serve as the connection between documents, frame windows and views. + + CSingleDocTemplate* pDocTemplate; + pDocTemplate = new CSingleDocTemplate( + IDR_MAINFRAME, + RUNTIME_CLASS(CAssimilateDoc), + RUNTIME_CLASS(CMainFrame), // main SDI frame window + RUNTIME_CLASS(CAssimilateView)); + AddDocTemplate(pDocTemplate); + + // Enable DDE Execute open + EnableShellOpen(); + RegisterShellFileTypes(TRUE); + + // Parse command line for standard shell commands, DDE, file open + CCommandLineInfo cmdInfo; + ParseCommandLine(cmdInfo); + + LoadEnumTable(m_enumFilename); + + CString strCommandLineFile = cmdInfo.m_strFileName; + cmdInfo.m_strFileName = ""; + cmdInfo.m_nShellCommand = CCommandLineInfo::FileNew; // :-) + + // Dispatch commands specified on the command line + if (!ProcessShellCommand(cmdInfo)) + return FALSE; + + // The one and only window has been initialized, so show and update it. + m_pMainWnd->ShowWindow(SW_SHOW); + m_pMainWnd->UpdateWindow(); + + // Enable drag/drop open + m_pMainWnd->DragAcceptFiles(); + + +//=============== + // + // a bit hacky but wtf for utils, right?... + // + SS_SetString_Ini ("\\\\RAVEND\\VSS_PROJECTS\\StarWars\\SRCSAFE.INI"); + SS_SetString_Project("$/base/"); +//=============== + + if (strCommandLineFile.GetLength()) + { + if (!OpenDocumentFile(strCommandLineFile)) + return FALSE; + } + + return TRUE; +} + +// this asks whether or not a name is present in the enum table, not whether or not it's really valid (just so I remember) +// +bool CAssimilateApp::ValidEnum(LPCTSTR name) +{ + for (EnumTable_t::iterator i = m_enumTable.begin(); i!= m_enumTable.end(); ++i) + { + if (!stricmp((*i).first,name)) + { + return true; + } + } + + return false; // return (m_enumTable.FindSymbol(name) != NULL); +} + +// return any comment associated with a given enum... (may be NULL) +// +LPCSTR CAssimilateApp::GetEnumComment(LPCSTR psEnum) +{ + for (EnumTable_t::iterator i = m_enumTable.begin(); i!= m_enumTable.end(); ++i) + { + if (!stricmp((*i).first,psEnum)) + { + LPCSTR p = (*i).second; + + if (strlen(p)) + return p; + return NULL; + } + } + + return NULL; +} + +bool CAssimilateApp::GetMultiPlayerMode() +{ + return m_bMultiPlayerMode; +} + +LPCTSTR CAssimilateApp::GetEnumFilename() +{ + return m_enumFilename; +} + +LPCTSTR CAssimilateApp::GetQDataFilename() +{ + return m_QDataFilename; +} + +LPCTSTR CAssimilateApp::GetQuakeDir() +{ + return m_QuakeDir; +} + +DWORD CAssimilateApp::GetBufferSize() +{ + return m_buffersize; +} + +int CAssimilateApp::GetEnumTableEntries() +{ + return m_enumTable.size(); +} +LPCSTR CAssimilateApp::GetEnumEntry(int iIndex) +{ + for (EnumTable_t::iterator i = m_enumTable.begin(); i!= m_enumTable.end(); ++i) + { + if (!iIndex--) + { + return (*i).first; + } + } + + return NULL; +} + +bool CAssimilateApp::SetMultiPlayerMode(bool bMultiPlayerMode) +{ + if (bMultiPlayerMode != m_bMultiPlayerMode) + { + m_bMultiPlayerMode = bMultiPlayerMode; + return true; + } + return false; +} + +bool CAssimilateApp::SetEnumFilename(LPCTSTR filename) +{ + CString name = filename; + name.MakeLower(); + name.Replace('\\', '/'); + if (name.Compare(m_enumFilename) == 0) + { + return false; + } + m_enumFilename = name; + LoadEnumTable(m_enumFilename); + return true; +} + +bool CAssimilateApp::SetQuakeDir(LPCTSTR psQuakeDir) +{ + CString name = psQuakeDir; + name.MakeLower(); + name.Replace('\\','/'); + if (name.Compare(m_QuakeDir) == 0) + { + return false; + } + m_QuakeDir = name; + return true; +} + +bool CAssimilateApp::SetQDataFilename(LPCTSTR filename) +{ + CString name = filename; + name.MakeLower(); + name.Replace('\\', '/'); + if (name.Compare(m_QDataFilename) == 0) + { + return false; + } + m_QDataFilename = name; + return true; +} + +bool CAssimilateApp::SetBufferSize(DWORD buffersize) +{ + if (buffersize != m_buffersize) + { + m_buffersize = buffersize; + return true; + } + return false; +} + +void CAssimilateApp::LoadEnumTable(LPCTSTR filename) +{ + int iIndex_Both = 0, + iIndex_Torso = 0, + iIndex_Legs = 0; + + m_enumTable.clear(); + CTokenizer* tokenizer = CTokenizer::Create(); + tokenizer->AddParseFile(filename); + tokenizer->SetKeywords(s_Keywords); + tokenizer->SetSymbols(s_Symbols); + CToken* curToken = tokenizer->GetToken(TKF_NODIRECTIVES|TKF_IGNOREDIRECTIVES); + + bool bFinished = false; + while(curToken->GetType() != TK_EOF && !bFinished) + { + switch (curToken->GetType()) + { + case TK_ENUM: + while((curToken->GetType() != TK_EOF) && (curToken->GetType() != TK_OBRACE)) + { + curToken->Delete(); + curToken = tokenizer->GetToken(NULL, TKF_NODIRECTIVES|TKF_IGNOREDIRECTIVES|TKF_USES_EOL|TKF_COMMENTTOKENS, 0); + } + while ((curToken->GetType() != TK_EOF) && (curToken->GetType() != TK_CBRACE)) + { + if (curToken->GetType() == TK_IDENTIFIER) + { + CString curEnumName = curToken->GetStringValue(); // useful later + + curToken->Delete(); + curToken = tokenizer->GetToken(NULL, TKF_NODIRECTIVES|TKF_IGNOREDIRECTIVES|TKF_USES_EOL|TKF_COMMENTTOKENS, 0); + + // only add the kind of symbols we know about... + // + if (GetEnumTypeFromString(curEnumName) != ET_INVALID) + { + if (!ValidEnum(curEnumName)) // ie if not already in the table... + { + // now skip past any crud like " = 0," and check for comments... + // + CString Comment; + + while ( (curToken->GetType() != TK_EOF) && + (curToken->GetType() != TK_EOL) && + (curToken->GetType() != TK_CBRACE) + ) + { + if (curToken->GetType() == TK_COMMENT) + { + LPCSTR psComment = curToken->GetStringValue(); + + // only read comment like "//#"... + // + if (psComment[0]=='#') + { + psComment++; + while (isspace(*psComment)) psComment++; + + // see if there's anything left after skipping whitespace... + // + if (strlen(psComment)) + { + Comment = psComment; + } + } + } + + curToken->Delete(); + curToken = tokenizer->GetToken(NULL, TKF_NODIRECTIVES|TKF_IGNOREDIRECTIVES|TKF_USES_EOL|TKF_COMMENTTOKENS, 0); + } + + pair pr(curEnumName,Comment); + m_enumTable.push_back(pr); + } + } + } + else + { + // is this one of the new seperator comments? + // + if (curToken->GetType() == TK_COMMENT) + { + LPCSTR psComment = curToken->GetStringValue(); + + // only count comments beginning "//#"... + // + if (psComment[0] == '#') + { + psComment++; + while (isspace(*psComment)) psComment++; + + CString curEnumName = psComment; // but this will include potential comments, so... + CString comment; + + int loc = curEnumName.Find("#"); + if (loc>=0) + { + comment = curEnumName.Mid(loc+1); + curEnumName = curEnumName.Left(loc); + comment.TrimLeft(); + } + curEnumName.TrimRight(); // lose trailing whitespace + comment.TrimRight(); + + // the first part of the comment (a pseudo-enum) must be a valid type... + // + if (GetEnumTypeFromString(curEnumName) != ET_INVALID) + { + if (!ValidEnum(curEnumName)) // ie if not already in the table... + { + pair pr(curEnumName,comment); + m_enumTable.push_back(pr); + } + } + } + } + + curToken->Delete(); + curToken = tokenizer->GetToken(NULL, TKF_NODIRECTIVES|TKF_IGNOREDIRECTIVES|TKF_USES_EOL|TKF_COMMENTTOKENS, 0); + } + } + bFinished = true; + break; + default: + break; + } + if (curToken->GetType() != TK_EOF) + { + curToken->Delete(); + curToken = tokenizer->GetToken(TKF_NODIRECTIVES|TKF_IGNOREDIRECTIVES); + } + } + if (curToken->GetType() != TK_EOF) + { + curToken->Delete(); + } + tokenizer->Delete(); +} + + +///////////////////////////////////////////////////////////////////////////// +// CAboutDlg dialog used for App About + +class CAboutDlg : public CDialog +{ +public: + CAboutDlg(); + +// Dialog Data + //{{AFX_DATA(CAboutDlg) + enum { IDD = IDD_ABOUTBOX }; + //}}AFX_DATA + + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CAboutDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + //{{AFX_MSG(CAboutDlg) + // No message handlers + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) +{ + //{{AFX_DATA_INIT(CAboutDlg) + //}}AFX_DATA_INIT +} + +void CAboutDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CAboutDlg) + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) + //{{AFX_MSG_MAP(CAboutDlg) + // No message handlers + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +// App command to run the dialog +void CAssimilateApp::OnAppAbout() +{ + CAboutDlg aboutDlg; + aboutDlg.DoModal(); +} + +///////////////////////////////////////////////////////////////////////////// +// CAssimilateApp message handlers + + +int CAssimilateApp::ExitInstance() +{ + m_enumTable.clear(); + SaveRegistry(); + + return CWinApp::ExitInstance(); +} + +void CAssimilateApp::OnProperties() +{ + CString strOldEnumFilename(m_enumFilename); + + bool dirty = false; + CPropertySheet* propSheet = new CPropertySheet("Assimilate"); + CAssimilatePropPage* propPage = new CAssimilatePropPage(); + propPage->m_soilFlag = &dirty; + propSheet->AddPage(propPage); + + propSheet->DoModal(); + + delete propPage; + delete propSheet; + + if (dirty) + { + SaveRegistry(); + + if (strOldEnumFilename.CompareNoCase(m_enumFilename) // if enum table name changed... + && + ghAssimilateView->GetDocument()->GetNumModels() // ...and any models loaded + ) + { + //if (GetYesNo("Enum table changed, do you want to re-enumerate?\n\n( You should nearly always answer YES here )")) + ghAssimilateView->GetDocument()->Resequence(); + + PlayerMode_e ePlayerMode = ghAssimilateView->GetSingleOrMultiPlayerMode(); + + if (ePlayerMode == eMODE_SINGLE) + { + InfoBox("Since you're now using the single player enum table but you weren't previously, be warned that certain sequences may be sorted slightly differently.\n\nTHIS WILL STILL WORK FINE, but may affect (eg) WinDiff, or cause non-alphabetical sort order which could make things harder to find\n\n\nIf this bothers you, do a NEW (without saving) and reload this model now"); + } + else + if (ePlayerMode == eMODE_MULTI) + { + InfoBox("Attention! Multiplayer sequence order is fixed, but switching to the multiplayer settings from different settings can result in badly-sorted sequence order.\n\nYou should now do a NEW (without saving) and reload this model"); + } + } + } +} + +///////////////////////////////////////////////////////////////////////////// +// CAssimilatePropPage property page + +IMPLEMENT_DYNCREATE(CAssimilatePropPage, CPropertyPage) + +CAssimilatePropPage::CAssimilatePropPage() : CPropertyPage(CAssimilatePropPage::IDD) +{ + //{{AFX_DATA_INIT(CAssimilatePropPage) + m_buffsize = 0; + m_enumfilename = _T(""); + m_qdata = _T(""); + m_csQuakeDir = _T(""); + m_bMultiPlayer = FALSE; + //}}AFX_DATA_INIT +} + +CAssimilatePropPage::~CAssimilatePropPage() +{ +} + +void CAssimilatePropPage::DoDataExchange(CDataExchange* pDX) +{ + CPropertyPage::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CAssimilatePropPage) + DDX_Text(pDX, IDC_BUFFSIZE, m_buffsize); + DDX_Text(pDX, IDC_ENUM, m_enumfilename); + DDX_Text(pDX, IDC_QDATA, m_qdata); + DDX_Text(pDX, IDC_QUAKEDIR, m_csQuakeDir); + DDX_Check(pDX, IDC_CHECK_MULTIPLAYER, m_bMultiPlayer); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CAssimilatePropPage, CPropertyPage) + //{{AFX_MSG_MAP(CAssimilatePropPage) + ON_BN_CLICKED(IDC_ENUM_BROWSE, OnEnumBrowse) + ON_BN_CLICKED(IDC_QDATA_BROWSE, OnQdataBrowse) + ON_BN_CLICKED(IDC_BUTTON_DEFAULTS, OnButtonDefaults) + ON_BN_CLICKED(IDC_BUTTON_DEFAULTS_MULTI, OnButtonDefaultsMulti) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CAssimilatePropPage message handlers + +void CAssimilatePropPage::OnEnumBrowse() +{ + // TODO: Add your control notification handler code here + CFileDialog theDialog(true, ".h", NULL, OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST, _T("Enum File (*.h)|*.h|All Files|*.*||"), NULL); + int result = theDialog.DoModal(); + if (result != IDOK) + { + return; + } + m_enumfilename = theDialog.GetPathName(); + UpdateData(FALSE); +} + +void CAssimilatePropPage::OnQdataBrowse() +{ + // TODO: Add your control notification handler code here + CFileDialog theDialog(true, ".exe", NULL, OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST, _T("QData File (*.exe)|*.exe|All Files|*.*||"), NULL); + int result = theDialog.DoModal(); + if (result != IDOK) + { + return; + } + m_qdata = theDialog.GetPathName(); + UpdateData(FALSE); +} + +void CAssimilatePropPage::OnOK() +{ + // TODO: Add your specialized code here and/or call the base class + CAssimilateApp* app = (CAssimilateApp*)AfxGetApp(); + *m_soilFlag = !! + ( + app->SetEnumFilename(m_enumfilename) | // note single OR rather than double, else not all execute + app->SetQDataFilename(m_qdata) | + app->SetBufferSize(m_buffsize) | + app->SetQuakeDir(m_csQuakeDir) | + app->SetMultiPlayerMode(!!m_bMultiPlayer) + ); + + CPropertyPage::OnOK(); +} + +BOOL CAssimilatePropPage::OnInitDialog() +{ + CPropertyPage::OnInitDialog(); + + // TODO: Add extra initialization here + CAssimilateApp* app = (CAssimilateApp*)AfxGetApp(); + m_bMultiPlayer = app->GetMultiPlayerMode(); + m_enumfilename = app->GetEnumFilename(); + m_qdata = app->GetQDataFilename(); + m_buffsize = app->GetBufferSize(); + m_csQuakeDir = app->GetQuakeDir(); + + UpdateData(FALSE); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void CAssimilatePropPage::OnButtonDefaults() +{ + UpdateData(true); // dialog -> vars + + if (m_enumfilename.IsEmpty() || + m_qdata.IsEmpty() || + m_csQuakeDir.IsEmpty() || + GetYesNo("Override all fields with single player defaults,\n\nAre you Sure?") + ) + { + m_bMultiPlayer = false; + m_enumfilename = sDEFAULT_ENUM_FILENAME; + m_qdata = sDEFAULT_QDATA_LOCATION; + m_buffsize = dwDEFAULT_BUFFERSIZE; + m_csQuakeDir = sDEFAULT_QUAKEDIR; + + UpdateData(false); // vars -> dialog + } +} + + +void CAssimilatePropPage::OnButtonDefaultsMulti() +{ + UpdateData(true); // dialog -> vars + + if (m_enumfilename.IsEmpty() || + m_qdata.IsEmpty() || + m_csQuakeDir.IsEmpty() || + GetYesNo("Override all fields with multi player defaults,\n\nAre you Sure?") + ) + { + m_bMultiPlayer = true; + m_enumfilename = sDEFAULT_ENUM_FILENAME_MULTI; + m_qdata = sDEFAULT_QDATA_LOCATION; + m_buffsize = dwDEFAULT_BUFFERSIZE; + m_csQuakeDir = sDEFAULT_QUAKEDIR; + + UpdateData(false); // vars -> dialog + } +} diff --git a/utils/Assimilate/Assimilate.dsp b/utils/Assimilate/Assimilate.dsp new file mode 100644 index 0000000..80c69ee --- /dev/null +++ b/utils/Assimilate/Assimilate.dsp @@ -0,0 +1,380 @@ +# Microsoft Developer Studio Project File - Name="Assimilate" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=Assimilate - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Assimilate.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Assimilate.mak" CFG="Assimilate - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Assimilate - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "Assimilate - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName ""$/Utils/Assimilate", ORNAAAAA" +# PROP Scc_LocalPath "." +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Assimilate - Win32 Release" + +# PROP BASE Use_MFC 6 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 5 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /G6 /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FR /Yu"stdafx.h" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:windows /machine:I386 +# ADD LINK32 /nologo /stack:0xf42400 /subsystem:windows /machine:I386 +# SUBTRACT LINK32 /debug + +!ELSEIF "$(CFG)" == "Assimilate - Win32 Debug" + +# PROP BASE Use_MFC 6 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 6 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /FR /Yu"stdafx.h" /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" +# ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /stack:0xf42400 /subsystem:windows /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "Assimilate - Win32 Release" +# Name "Assimilate - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\AlertErrHandler.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\AnimPicker.cpp +# End Source File +# Begin Source File + +SOURCE=.\ASEFile.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\Assimilate.cpp +# End Source File +# Begin Source File + +SOURCE=.\Assimilate.rc +# End Source File +# Begin Source File + +SOURCE=.\AssimilateDoc.cpp +# End Source File +# Begin Source File + +SOURCE=.\AssimilateView.cpp +# End Source File +# Begin Source File + +SOURCE=.\BuildAll.cpp +# End Source File +# Begin Source File + +SOURCE=..\Common\cmdlib.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\Comment.cpp +# End Source File +# Begin Source File + +SOURCE=.\gla.cpp +# End Source File +# Begin Source File + +SOURCE=.\MainFrm.cpp +# End Source File +# Begin Source File + +SOURCE=.\matcomp.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\Common\mathlib.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\matrix4.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\Model.cpp +# End Source File +# Begin Source File + +SOURCE=.\Module.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\oddbits.cpp +# End Source File +# Begin Source File + +SOURCE=.\Sequences.cpp +# End Source File +# Begin Source File + +SOURCE=.\sourcesafe.cpp +# End Source File +# Begin Source File + +SOURCE=.\StdAfx.cpp +# ADD CPP /Yc"stdafx.h" +# End Source File +# Begin Source File + +SOURCE=.\Tokenizer.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\TxtFile.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\vect3.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\xsiimp.cpp +# End Source File +# Begin Source File + +SOURCE=.\xsilib.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\YesNoYesAllNoAll.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\AlertErrHandler.h +# End Source File +# Begin Source File + +SOURCE=.\AnimPicker.h +# End Source File +# Begin Source File + +SOURCE=.\ASEFile.h +# End Source File +# Begin Source File + +SOURCE=.\Assimilate.h +# End Source File +# Begin Source File + +SOURCE=.\AssimilateDoc.h +# End Source File +# Begin Source File + +SOURCE=.\AssimilateView.h +# End Source File +# Begin Source File + +SOURCE=.\BuildAll.h +# End Source File +# Begin Source File + +SOURCE=..\common\cmdlib.h +# End Source File +# Begin Source File + +SOURCE=.\Comment.h +# End Source File +# Begin Source File + +SOURCE=.\gla.h +# End Source File +# Begin Source File + +SOURCE=.\includes.h +# End Source File +# Begin Source File + +SOURCE=.\MainFrm.h +# End Source File +# Begin Source File + +SOURCE=.\matcomp.h +# End Source File +# Begin Source File + +SOURCE=..\common\mathlib.h +# End Source File +# Begin Source File + +SOURCE=.\matrix4.h +# End Source File +# Begin Source File + +SOURCE=.\mdx_format.h +# End Source File +# Begin Source File + +SOURCE=.\Model.h +# End Source File +# Begin Source File + +SOURCE=.\Module.h +# End Source File +# Begin Source File + +SOURCE=.\oddbits.h +# End Source File +# Begin Source File + +SOURCE=..\common\polyset.h +# End Source File +# Begin Source File + +SOURCE=.\Resource.h +# End Source File +# Begin Source File + +SOURCE=.\Sequences.h +# End Source File +# Begin Source File + +SOURCE=.\sourcesafe.h +# End Source File +# Begin Source File + +SOURCE=.\ssauterr.h +# End Source File +# Begin Source File + +SOURCE=.\ssauto.h +# End Source File +# Begin Source File + +SOURCE=.\StdAfx.h +# End Source File +# Begin Source File + +SOURCE=.\Tokenizer.h +# End Source File +# Begin Source File + +SOURCE=.\TxtFile.h +# End Source File +# Begin Source File + +SOURCE=.\vect3.h +# End Source File +# Begin Source File + +SOURCE=.\xsiimp.h +# End Source File +# Begin Source File + +SOURCE=.\xsilib.h +# End Source File +# Begin Source File + +SOURCE=.\YesNoYesAllNoAll.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\res\Assimilate.ico +# End Source File +# Begin Source File + +SOURCE=.\res\Assimilate.rc2 +# End Source File +# Begin Source File + +SOURCE=.\res\AssimilateDoc.ico +# End Source File +# Begin Source File + +SOURCE=.\res\Toolbar.bmp +# End Source File +# Begin Source File + +SOURCE=.\res\treeimag.bmp +# End Source File +# End Group +# Begin Source File + +SOURCE=.\Assimilate.reg +# End Source File +# Begin Source File + +SOURCE=.\ReadMe.txt +# End Source File +# End Target +# End Project diff --git a/utils/Assimilate/Assimilate.dsw b/utils/Assimilate/Assimilate.dsw new file mode 100644 index 0000000..2bc1a4f --- /dev/null +++ b/utils/Assimilate/Assimilate.dsw @@ -0,0 +1,37 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "Assimilate"=.\Assimilate.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/Utils/Assimilate", ORNAAAAA + . + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ + begin source code control + "$/Utils/Assimilate", ORNAAAAA + . + end source code control +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/utils/Assimilate/Assimilate.h b/utils/Assimilate/Assimilate.h new file mode 100644 index 0000000..973bf1f --- /dev/null +++ b/utils/Assimilate/Assimilate.h @@ -0,0 +1,151 @@ +// Assimilate.h : main header file for the ASSIMILATE application +// + +#if !defined(AFX_ASSIMILATE_H__2CCA5544_2AD3_11D3_82E0_0000C0366FF2__INCLUDED_) +#define AFX_ASSIMILATE_H__2CCA5544_2AD3_11D3_82E0_0000C0366FF2__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#ifndef __AFXWIN_H__ + #error include 'stdafx.h' before including this file for PCH +#endif + +// not incredibly important, just used for guessing string lengths for display/output neatness... +// +#define APPROX_LONGEST_ASE_NAME 20 +extern bool gbViewAnimEnums; +extern bool gbViewFrameDetails; +extern bool gbViewFrameDetails_Additional; + +///////////////////////////////////////////////////////////////////////////// +// CAssimilateApp: +// See Assimilate.cpp for the implementation of this class +// +enum +{ + TK_ENUM = TK_USERDEF, + TK_COMMA, + TK_OBRACE, + TK_CBRACE, + TK_SEMICOLON, +}; + +// 1st CString is enum key, (eg) "BOTH_PAIN1", 2nd is comment (body text only, no comment chars) +// +typedef list< pair > EnumTable_t; + +class CAssimilateApp : public CWinApp +{ +public: + CAssimilateApp(); + + LPCSTR GetEnumComment(LPCSTR psEnum); + bool ValidEnum(LPCTSTR name); + + bool GetMultiPlayerMode(); + LPCTSTR GetEnumFilename(); + LPCTSTR GetQDataFilename(); + DWORD GetBufferSize(); + LPCTSTR GetQuakeDir(); + + bool SetMultiPlayerMode(bool bMultiPlayerMode); + bool SetEnumFilename(LPCTSTR filename); + bool SetQDataFilename(LPCTSTR filename); + bool SetBufferSize(DWORD buffersize); + bool SetQuakeDir(LPCTSTR psQuakeDir); + + int GetEnumTableEntries(); + LPCSTR GetEnumEntry(int iIndex); + +protected: + void LoadEnumTable(LPCTSTR filename); + void LoadRegistry(); + void SaveRegistry(); + + EnumTable_t m_enumTable; + static keywordArray_t s_Symbols[]; + static keywordArray_t s_Keywords[]; + + bool m_bMultiPlayerMode; + CString m_enumFilename; + DWORD m_buffersize; + CString m_QDataFilename; + CString m_QuakeDir; + + static const TCHAR c_prefSection[]; + static const TCHAR c_enumFilename[]; + static const TCHAR c_buffersize[]; + static const TCHAR c_QDataFilename[]; + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CAssimilateApp) + public: + virtual BOOL InitInstance(); + virtual int ExitInstance(); + //}}AFX_VIRTUAL + +// Implementation + //{{AFX_MSG(CAssimilateApp) + afx_msg void OnAppAbout(); + afx_msg void OnProperties(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + + +///////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// CAssimilatePropPage dialog + +class CAssimilatePropPage : public CPropertyPage +{ + DECLARE_DYNCREATE(CAssimilatePropPage) + +// Construction +public: + CAssimilatePropPage(); + ~CAssimilatePropPage(); + + bool* m_soilFlag; + +// Dialog Data + //{{AFX_DATA(CAssimilatePropPage) + enum { IDD = IDD_PP_PROPERTIES }; + DWORD m_buffsize; + CString m_enumfilename; + CString m_qdata; + CString m_csQuakeDir; + BOOL m_bMultiPlayer; + //}}AFX_DATA + + +// Overrides + // ClassWizard generate virtual function overrides + //{{AFX_VIRTUAL(CAssimilatePropPage) + public: + virtual void OnOK(); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + // Generated message map functions + //{{AFX_MSG(CAssimilatePropPage) + afx_msg void OnEnumBrowse(); + afx_msg void OnQdataBrowse(); + virtual BOOL OnInitDialog(); + afx_msg void OnButtonDefaults(); + afx_msg void OnButtonDefaultsMulti(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + +}; +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_ASSIMILATE_H__2CCA5544_2AD3_11D3_82E0_0000C0366FF2__INCLUDED_) diff --git a/utils/Assimilate/Assimilate.ico b/utils/Assimilate/Assimilate.ico new file mode 100644 index 0000000..7eef0bc Binary files /dev/null and b/utils/Assimilate/Assimilate.ico differ diff --git a/utils/Assimilate/Assimilate.rc b/utils/Assimilate/Assimilate.rc new file mode 100644 index 0000000..e277889 --- /dev/null +++ b/utils/Assimilate/Assimilate.rc @@ -0,0 +1,690 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "#define _AFX_NO_SPLITTER_RESOURCES\r\n" + "#define _AFX_NO_OLE_RESOURCES\r\n" + "#define _AFX_NO_TRACKER_RESOURCES\r\n" + "#define _AFX_NO_PROPERTY_RESOURCES\r\n" + "\r\n" + "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n" + "#ifdef _WIN32\r\n" + "LANGUAGE 9, 1\r\n" + "#pragma code_page(1252)\r\n" + "#endif //_WIN32\r\n" + "#include ""res\\Assimilate.rc2"" // non-Microsoft Visual C++ edited resources\r\n" + "#include ""afxres.rc"" // Standard components\r\n" + "#include ""afxprint.rc"" // printing/print preview resources\r\n" + "#endif\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDR_MAINFRAME ICON DISCARDABLE "res\\Assimilate.ico" +IDR_ASSIMITYPE ICON DISCARDABLE "res\\AssimilateDoc.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDR_MAINFRAME BITMAP MOVEABLE PURE "res\\Toolbar.bmp" +IDB_TREEIMAGES BITMAP DISCARDABLE "res\\treeimag.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Toolbar +// + +IDR_MAINFRAME TOOLBAR DISCARDABLE 23, 21 +BEGIN + BUTTON ID_FILE_NEW + BUTTON ID_FILE_OPEN + BUTTON ID_FILE_SAVE + SEPARATOR + BUTTON ID_EDIT_CUT + BUTTON ID_EDIT_COPY + BUTTON ID_EDIT_PASTE + SEPARATOR + BUTTON ID_FILE_PRINT + SEPARATOR + BUTTON ID_APP_ABOUT + BUTTON IDM_RESEQUENCE + BUTTON IDM_BUILD + BUTTON ID_EDIT_BUILDALL + BUTTON IDM_EDIT_BUILDDEPENDANT + BUTTON IDM_VALIDATE + BUTTON IDM_CARWASH + BUTTON ID_EDIT_LAUNCHMODVIEWONCURRENT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MAINFRAME MENU PRELOAD DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&New\tCtrl+N", ID_FILE_NEW + MENUITEM "&Open...\tCtrl+O", ID_FILE_OPEN + MENUITEM "&Save\tCtrl+S", ID_FILE_SAVE + MENUITEM "Save &As...", ID_FILE_SAVE_AS + MENUITEM SEPARATOR + MENUITEM "Add Files...", IDM_ADDFILES + MENUITEM "Write Config Data...", IDM_EXTERNAL + MENUITEM SEPARATOR + MENUITEM "&Print...\tCtrl+P", ID_FILE_PRINT + MENUITEM "Print Pre&view", ID_FILE_PRINT_PREVIEW + MENUITEM "P&rint Setup...", ID_FILE_PRINT_SETUP + MENUITEM SEPARATOR + MENUITEM "Recent File", ID_FILE_MRU_FILE1, GRAYED + MENUITEM SEPARATOR + MENUITEM "E&xit", ID_APP_EXIT + END + POPUP "&Edit" + BEGIN + MENUITEM "&Undo\tCtrl+Z", ID_EDIT_UNDO + MENUITEM SEPARATOR + MENUITEM "Cu&t\tCtrl+X", ID_EDIT_CUT + MENUITEM "&Copy\tCtrl+C", ID_EDIT_COPY + MENUITEM "&Paste\tCtrl+V", ID_EDIT_PASTE + MENUITEM SEPARATOR + MENUITEM "Resequence", IDM_RESEQUENCE + MENUITEM "Build Model", IDM_BUILD + MENUITEM "Validate", IDM_VALIDATE + MENUITEM "View current model via ModView", + ID_EDIT_LAUNCHMODVIEWONCURRENT + + MENUITEM SEPARATOR + MENUITEM "Validate "" *.CAR /s "" ( aka .CAR WASH :-)", + IDM_CARWASH + MENUITEM SEPARATOR + MENUITEM "Build every CAR file in the project.... ""Hurrrrr.....Big Jobs!""", + ID_EDIT_BUILDALL + MENUITEM "Build dependant models", IDM_EDIT_BUILDDEPENDANT + MENUITEM SEPARATOR + MENUITEM "Preferences...", IDM_PROPERTIES + END + POPUP "&View" + BEGIN + MENUITEM "&Toolbar", ID_VIEW_TOOLBAR + MENUITEM "&Status Bar", ID_VIEW_STATUS_BAR + MENUITEM "Anim Enums", ID_VIEW_ANIMENUMS + MENUITEM "Frame Details", ID_VIEW_FRAMEDETAILS + MENUITEM "Frame Details On Additional Sequences", + ID_VIEW_FRAMEDETAILSONADDITIONALSEQUENCES + + END + POPUP "&Help" + BEGIN + MENUITEM "&About Assimilate...", ID_APP_ABOUT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE PURE +BEGIN + "N", ID_FILE_NEW, VIRTKEY, CONTROL + "O", ID_FILE_OPEN, VIRTKEY, CONTROL + "S", ID_FILE_SAVE, VIRTKEY, CONTROL + "P", ID_FILE_PRINT, VIRTKEY, CONTROL + "Z", ID_EDIT_UNDO, VIRTKEY, CONTROL + "X", ID_EDIT_CUT, VIRTKEY, CONTROL + "C", ID_EDIT_COPY, VIRTKEY, CONTROL + "V", ID_EDIT_PASTE, VIRTKEY, CONTROL + VK_BACK, ID_EDIT_UNDO, VIRTKEY, ALT + VK_DELETE, ID_EDIT_CUT, VIRTKEY, SHIFT + VK_INSERT, ID_EDIT_COPY, VIRTKEY, CONTROL + VK_INSERT, ID_EDIT_PASTE, VIRTKEY, SHIFT + VK_F6, ID_NEXT_PANE, VIRTKEY + VK_F6, ID_PREV_PANE, VIRTKEY, SHIFT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUTBOX DIALOG DISCARDABLE 0, 0, 235, 55 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About Assimilate" +FONT 8, "MS Sans Serif" +BEGIN + ICON IDR_MAINFRAME,IDC_STATIC,11,17,20,20 + LTEXT "Assimilate Version 2.1",IDC_STATIC,40,10,119,8, + SS_NOPREFIX + LTEXT "Copyright (C) 1999 Raven Software",IDC_STATIC,39,24,119, + 10 + DEFPUSHBUTTON "OK",IDOK,178,7,50,14,WS_GROUP + LTEXT "Written by M.Crowns, S.Cork",IDC_STATIC,39,38,121,8 +END + +IDD_PP_MODEL DIALOG DISCARDABLE 0, 0, 309, 359 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Model" +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "Build Parameters ",IDC_STATIC,7,6,295,346 + CONTROL "Skew 90 (only use this if you REALLY need to, i.e. leave it alone!)", + IDC_CHECK_SKEW90,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,17,18,222,10 + CONTROL "Smooth all surfaces",IDC_CHECK_SMOOTH_ALL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,17,28,77,10 + GROUPBOX "Skeleton",IDC_STATIC,17,75,277,74 + EDITTEXT IDC_EDIT_SKELPATH,23,127,263,14,ES_AUTOHSCROLL | + WS_GROUP + LTEXT "Path, eg ""models/players/interrogator/interrogator"" ( no extension )", + IDC_STATIC_SKELPATH,24,116,239,11 + GROUPBOX "Origin - Adjust",IDC_STATIC,17,155,277,31 + EDITTEXT IDC_EDIT_ORIGINX,31,165,52,14,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_ORIGINY,99,165,55,14,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_ORIGINZ,172,165,49,14,ES_AUTOHSCROLL + LTEXT "X",IDC_STATIC,23,167,8,8 + LTEXT "Y",IDC_STATIC,91,167,8,8 + LTEXT "Z",IDC_STATIC,163,167,8,8 + EDITTEXT IDC_EDIT_SCALE,182,83,41,14,ES_AUTOHSCROLL + LTEXT "Scale (default = 1.0)",IDC_STATIC,226,85,64,8 + CONTROL "Make EF1-type .skin file",IDC_CHECK_MAKESKIN,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,17,49,91,10 + CONTROL "Remove duplicate verts from mesh during compile", + IDC_CHECK_LOSEDUPVERTS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,17,39,172,10 + CONTROL "Makes it's own skeleton",IDC_CHECK_MAKESKEL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,25,87,92,10 + GROUPBOX "PCJ List",IDC_PCJ_STATIC,17,191,277,151 + LISTBOX IDC_LIST_PCJ,24,204,263,109,LBS_SORT | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_EDIT_PCJ,23,319,130,14,ES_AUTOHSCROLL + PUSHBUTTON "<---- Add to PCJ list",IDC_BUTTON_PCJ,157,319,68,14 + PUSHBUTTON "Delete PCJ entry",IDC_BUTTON_DELPCJ,226,319,61,14 + CONTROL "Keep ""motion"" bone",IDC_CHECK_KEEPMOTIONBONE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,17,60,87,10 + CONTROL "Uses ( older ) pre-quaternian animation-compression", + IDC_CHECK_PREQUAT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 25,101,194,10 +END + +IDD_PP_SEQUENCE DIALOG DISCARDABLE 0, 0, 542, 175 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Sequence" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Start Frame",IDC_STATIC,78,10,37,8 + LTEXT "Frame Count",IDC_STATIC,124,10,41,8 + LTEXT "Loop Frame",IDC_STATIC,173,10,38,8 + LTEXT "Frame Speed",IDC_STATIC,218,10,43,8 + LTEXT "Animation Enum",IDC_STATIC,369,10,53,8 + LTEXT "( -1 = none )",IDC_STATIC,173,19,39,8 + LTEXT "Master",IDC_STATIC,17,33,22,8 + EDITTEXT IDC_STARTFRAME,75,30,42,14,ES_AUTOHSCROLL + EDITTEXT IDC_FRAMECOUNT,123,30,42,14,ES_AUTOHSCROLL + EDITTEXT IDC_LOOPFRAME,171,30,42,14,ES_AUTOHSCROLL + EDITTEXT IDC_FRAMESPEED,219,30,42,14,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_ANIMATIONENUM,333,30,120,14,ES_AUTOHSCROLL | + ES_READONLY + PUSHBUTTON "Choose",IDC_BUTTON_CHOOSEANIMATIONENUM,455,31,36,13 + PUSHBUTTON "Clear",IDC_BUTTON_CLEARANIMATIONENUM,491,31,36,13 + LTEXT "Additional #1",IDC_STATIC,8,59,42,8 + EDITTEXT IDC_STARTFRAME2,75,57,42,14,ES_AUTOHSCROLL + EDITTEXT IDC_FRAMECOUNT2,123,57,42,14,ES_AUTOHSCROLL + EDITTEXT IDC_LOOPFRAME2,171,57,42,14,ES_AUTOHSCROLL + EDITTEXT IDC_FRAMESPEED2,219,57,42,14,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_ANIMATIONENUM2,333,57,120,14,ES_AUTOHSCROLL | + ES_READONLY + PUSHBUTTON "Choose",IDC_BUTTON_CHOOSEANIMATIONENUM2,455,57,36,13 + PUSHBUTTON "Clear",IDC_BUTTON_CLEARANIMATIONENUM2,491,57,36,13 + LTEXT "Additional #2",IDC_STATIC,8,77,42,8 + EDITTEXT IDC_STARTFRAME3,75,75,42,14,ES_AUTOHSCROLL + EDITTEXT IDC_FRAMECOUNT3,123,75,42,14,ES_AUTOHSCROLL + EDITTEXT IDC_LOOPFRAME3,171,75,42,14,ES_AUTOHSCROLL + EDITTEXT IDC_FRAMESPEED3,219,75,42,14,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_ANIMATIONENUM3,333,75,120,14,ES_AUTOHSCROLL | + ES_READONLY + PUSHBUTTON "Choose",IDC_BUTTON_CHOOSEANIMATIONENUM3,455,76,36,13 + PUSHBUTTON "Clear",IDC_BUTTON_CLEARANIMATIONENUM3,491,76,36,13 + LTEXT "Additional #3",IDC_STATIC,8,95,42,8 + EDITTEXT IDC_STARTFRAME4,75,93,42,14,ES_AUTOHSCROLL + EDITTEXT IDC_FRAMECOUNT4,123,93,42,14,ES_AUTOHSCROLL + EDITTEXT IDC_LOOPFRAME4,171,93,42,14,ES_AUTOHSCROLL + EDITTEXT IDC_FRAMESPEED4,219,93,42,14,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_ANIMATIONENUM4,333,93,120,14,ES_AUTOHSCROLL | + ES_READONLY + PUSHBUTTON "Choose",IDC_BUTTON_CHOOSEANIMATIONENUM4,455,94,36,13 + PUSHBUTTON "Clear",IDC_BUTTON_CLEARANIMATIONENUM4,491,94,36,13 + LTEXT "Additional #4",IDC_STATIC,8,112,42,8 + EDITTEXT IDC_STARTFRAME5,75,111,42,14,ES_AUTOHSCROLL + EDITTEXT IDC_FRAMECOUNT5,123,111,42,14,ES_AUTOHSCROLL + EDITTEXT IDC_LOOPFRAME5,171,111,42,14,ES_AUTOHSCROLL + EDITTEXT IDC_FRAMESPEED5,219,111,42,14,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_ANIMATIONENUM5,333,111,120,14,ES_AUTOHSCROLL | + ES_READONLY + PUSHBUTTON "Choose",IDC_BUTTON_CHOOSEANIMATIONENUM5,455,111,36,13 + PUSHBUTTON "Clear",IDC_BUTTON_CLEARANIMATIONENUM5,491,111,36,13 + LTEXT "File Path:",IDC_STATIC,7,142,36,8 + EDITTEXT IDC_PATH,75,140,186,14,ES_AUTOHSCROLL + CONTROL "GenLoopFrame",IDC_CHECK_GENLOOPFRAME,"Button", + BS_AUTOCHECKBOX | BS_LEFT | BS_TOP | BS_MULTILINE | + WS_TABSTOP,266,32,64,10 +END + +IDD_PP_PROPERTIES DIALOG DISCARDABLE 0, 0, 235, 159 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Preferences" +FONT 8, "MS Sans Serif" +BEGIN + RTEXT "Enum file:",IDC_STATIC,18,7,32,8 + EDITTEXT IDC_ENUM,53,7,160,12,ES_AUTOHSCROLL + PUSHBUTTON "...",IDC_ENUM_BROWSE,213,7,15,11 + RTEXT "Buffer size:",IDC_STATIC,18,22,32,8 + EDITTEXT IDC_BUFFSIZE,53,22,32,12,ES_AUTOHSCROLL + RTEXT "Compiler:",IDC_STATIC,18,36,32,8 + EDITTEXT IDC_QDATA,53,37,160,12,ES_AUTOHSCROLL + PUSHBUTTON "...",IDC_QDATA_BROWSE,213,37,15,11 + EDITTEXT IDC_QUAKEDIR,53,75,160,12,ES_AUTOHSCROLL + LTEXT "Quake Dir: eg: Q:\\quake\\baseq3\\ ( note trailing '\\' )", + IDC_STATIC,7,65,219,8 + PUSHBUTTON "Default all fields for StarWars Single Player", + IDC_BUTTON_DEFAULTS,12,110,211,14 + PUSHBUTTON "Default all fields for StarWars Multi Player (subject to change)", + IDC_BUTTON_DEFAULTS_MULTI,12,129,211,14 + CONTROL "MultiPlayer",IDC_CHECK_MULTIPLAYER,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,10,94,51,10 + LTEXT "( Ignore this dumb field, I'll lose it later )", + IDC_STATIC,87,23,121,8 +END + +IDD_ANIMPICKER DIALOG DISCARDABLE 0, 0, 549, 423 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Animation Picker" +FONT 8, "MS Sans Serif" +BEGIN + PUSHBUTTON "Cancel",IDCANCEL,479,380,50,14 + LISTBOX IDC_LIST_LEGS,20,23,160,352,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + LTEXT "LEGS_xxxx",IDC_STATIC,82,11,37,8 + LISTBOX IDC_LIST_TORSO,193,23,160,352,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + LTEXT "TORSO_xxxx",IDC_STATIC,248,11,44,8 + LISTBOX IDC_LIST_BOTH,370,23,160,352,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + LTEXT "BOTH_xxxx",IDC_STATIC,429,11,38,8 + LTEXT "( Double-click an entry to select. Entries beginning with ( * ) are already in use )", + IDC_STATIC,154,381,247,8 + LTEXT "",IDC_STATIC_COMMENT,22,398,444,8 + CONTROL "Filter-out used",IDC_CHECK_FILTEROUTUSED,"Button", + BS_AUTOCHECKBOX | BS_PUSHLIKE | WS_TABSTOP,415,380,59,14 +END + +IDD_DIALOG_YESNOYESNOALL DIALOG DISCARDABLE 0, 0, 308, 114 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Decision needed..." +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "Yes",IDOK,31,81,50,14 + PUSHBUTTON "No",IDCANCEL,93,81,50,14 + PUSHBUTTON "Yes To All",IDYESTOALL,157,81,50,14 + PUSHBUTTON "No To All",IDNOTOALL,221,81,50,14 + CTEXT "static",IDC_STATIC1,16,10,277,9,SS_CENTERIMAGE + CTEXT "static",IDC_STATIC2,16,20,277,9,SS_CENTERIMAGE + CTEXT "static",IDC_STATIC3,16,30,277,9,SS_CENTERIMAGE + CTEXT "static",IDC_STATIC4,16,40,277,9,SS_CENTERIMAGE + CTEXT "static",IDC_STATIC5,16,50,277,9,SS_CENTERIMAGE + CTEXT "static",IDC_STATIC6,16,60,277,9,SS_CENTERIMAGE +END + +IDD_BUILD_ALL DIALOG DISCARDABLE 0, 0, 186, 87 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Build-Models" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,27,66,50,14 + PUSHBUTTON "Cancel",IDCANCEL,106,66,50,14 + EDITTEXT IDC_EDIT_BUILDPATH,15,25,154,14,ES_AUTOHSCROLL + LTEXT "Game path to start at: ( eg ""models/players"" )", + IDC_STATIC,17,15,149,8 + CONTROL "Pre-Validate all CAR files before build", + IDC_CHECK_PREVALIDATECARS,"Button",BS_AUTOCHECKBOX | + BS_MULTILINE | WS_TABSTOP,15,46,155,14 +END + + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "\0" + VALUE "CompanyName", "Raven Software\0" + VALUE "FileDescription", "Helps assimilate .ase and .xsi files into ST:Voy config files\0" + VALUE "FileVersion", "2.0\0" + VALUE "InternalName", "Assimilate\0" + VALUE "LegalCopyright", "Copyright (C) 1999\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "Assimilate.EXE\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "Assimilate Application\0" + VALUE "ProductVersion", "2.0\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_ABOUTBOX, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 228 + TOPMARGIN, 7 + BOTTOMMARGIN, 48 + END + + IDD_PP_MODEL, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 302 + TOPMARGIN, 6 + BOTTOMMARGIN, 352 + END + + IDD_PP_SEQUENCE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 535 + TOPMARGIN, 7 + BOTTOMMARGIN, 168 + END + + IDD_PP_PROPERTIES, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 228 + TOPMARGIN, 7 + BOTTOMMARGIN, 152 + END + + IDD_ANIMPICKER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 542 + TOPMARGIN, 7 + BOTTOMMARGIN, 416 + END + + IDD_DIALOG_YESNOYESNOALL, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 301 + TOPMARGIN, 7 + BOTTOMMARGIN, 107 + END + + IDD_BUILD_ALL, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 80 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + IDR_MAINFRAME "Assimilate\n\nAssimilate\nAssimilate Files (*.car)\n.car\nAssimilate.Document\nAssimilate Document" +END + +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + AFX_IDS_APP_TITLE "Assimilate" + AFX_IDS_IDLEMESSAGE "Ready" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_INDICATOR_EXT "EXT" + ID_INDICATOR_CAPS "CAP" + ID_INDICATOR_NUM "NUM" + ID_INDICATOR_SCRL "SCRL" + ID_INDICATOR_OVR "OVR" + ID_INDICATOR_REC "REC" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_NEW "Create a new document\nNew" + ID_FILE_OPEN "Open an existing document\nOpen" + ID_FILE_CLOSE "Close the active document\nClose" + ID_FILE_SAVE "Save the active document\nSave" + ID_FILE_SAVE_AS "Save the active document with a new name\nSave As" + ID_FILE_PAGE_SETUP "Change the printing options\nPage Setup" + ID_FILE_PRINT_SETUP "Change the printer and printing options\nPrint Setup" + ID_FILE_PRINT "Print the active document\nPrint" + ID_FILE_PRINT_PREVIEW "Display full pages\nPrint Preview" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_APP_ABOUT "Display program information, version number and copyright\nAbout" + ID_APP_EXIT "Quit the application; prompts to save documents\nExit" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_MRU_FILE1 "Open this document" + ID_FILE_MRU_FILE2 "Open this document" + ID_FILE_MRU_FILE3 "Open this document" + ID_FILE_MRU_FILE4 "Open this document" + ID_FILE_MRU_FILE5 "Open this document" + ID_FILE_MRU_FILE6 "Open this document" + ID_FILE_MRU_FILE7 "Open this document" + ID_FILE_MRU_FILE8 "Open this document" + ID_FILE_MRU_FILE9 "Open this document" + ID_FILE_MRU_FILE10 "Open this document" + ID_FILE_MRU_FILE11 "Open this document" + ID_FILE_MRU_FILE12 "Open this document" + ID_FILE_MRU_FILE13 "Open this document" + ID_FILE_MRU_FILE14 "Open this document" + ID_FILE_MRU_FILE15 "Open this document" + ID_FILE_MRU_FILE16 "Open this document" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_NEXT_PANE "Switch to the next window pane\nNext Pane" + ID_PREV_PANE "Switch back to the previous window pane\nPrevious Pane" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_WINDOW_SPLIT "Split the active window into panes\nSplit" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_EDIT_CLEAR "Erase the selection\nErase" + ID_EDIT_CLEAR_ALL "Erase everything\nErase All" + ID_EDIT_COPY "Copy the selection and put it on the Clipboard\nCopy" + ID_EDIT_CUT "Cut the selection and put it on the Clipboard\nCut" + ID_EDIT_FIND "Find the specified text\nFind" + ID_EDIT_PASTE "Insert Clipboard contents\nPaste" + ID_EDIT_REPEAT "Repeat the last action\nRepeat" + ID_EDIT_REPLACE "Replace specific text with different text\nReplace" + ID_EDIT_SELECT_ALL "Select the entire document\nSelect All" + ID_EDIT_UNDO "Undo the last action\nUndo" + ID_EDIT_REDO "Redo the previously undone action\nRedo" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_VIEW_TOOLBAR "Show or hide the toolbar\nToggle ToolBar" + ID_VIEW_STATUS_BAR "Show or hide the status bar\nToggle StatusBar" +END + +STRINGTABLE DISCARDABLE +BEGIN + AFX_IDS_SCSIZE "Change the window size" + AFX_IDS_SCMOVE "Change the window position" + AFX_IDS_SCMINIMIZE "Reduce the window to an icon" + AFX_IDS_SCMAXIMIZE "Enlarge the window to full size" + AFX_IDS_SCNEXTWINDOW "Switch to the next document window" + AFX_IDS_SCPREVWINDOW "Switch to the previous document window" + AFX_IDS_SCCLOSE "Close the active window and prompts to save the documents" +END + +STRINGTABLE DISCARDABLE +BEGIN + AFX_IDS_SCRESTORE "Restore the window to normal size" + AFX_IDS_SCTASKLIST "Activate Task List" +END + +STRINGTABLE DISCARDABLE +BEGIN + AFX_IDS_PREVIEW_CLOSE "Close print preview mode\nCancel Preview" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDM_ADDFILES "Add files" + IDM_EXTERNAL "Creates the external data file" + IDM_RESEQUENCE "Resequences model\nResequence model" + IDM_PROPERTIES "Alter Properties" + ID_VIEW_ANIMENUMS "Toggles viewing of animation enums in treeview" + ID_VIEW_FRAMEDETAILS "Various frame info stuff, may well be removed after app finished" + IDM_BUILD "Performs a build on all models within this QDT/CAR file\nBuild" + IDM_VALIDATE "Validate: Checks for existance of all model files\nValidate" + IDM_CARWASH "Validates every .CAR file in baseq down (big job)\nValidate all ( CAR-wash )" + ID_EDIT_BUILDALL "Builds everything in the current single or multiplayer dir\nBuild all models" + IDM_EDIT_BUILDDEPENDANT "Build all models who are dependant on the skeleton produced by this CAR file\nBuild model + all dependants" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_VIEW_FRAMEDETAILSONADDITIONALSEQUENCES + "Displays frame details on additional sequences, as opposed to primary" + ID_BUTTON_RESEQUENCE "Resequence" + ID_EDIT_LAUNCHMODVIEWONCURRENT + "View using ModView\nView disk-version via ModView" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#define _AFX_NO_SPLITTER_RESOURCES +#define _AFX_NO_OLE_RESOURCES +#define _AFX_NO_TRACKER_RESOURCES +#define _AFX_NO_PROPERTY_RESOURCES + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE 9, 1 +#pragma code_page(1252) +#endif //_WIN32 +#include "res\Assimilate.rc2" // non-Microsoft Visual C++ edited resources +#include "afxres.rc" // Standard components +#include "afxprint.rc" // printing/print preview resources +#endif + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/utils/Assimilate/AssimilateDoc.cpp b/utils/Assimilate/AssimilateDoc.cpp new file mode 100644 index 0000000..611b730 --- /dev/null +++ b/utils/Assimilate/AssimilateDoc.cpp @@ -0,0 +1,3367 @@ +// AssimilateDoc.cpp : implementation of the CAssimilateDoc class +// + +#include "stdafx.h" +#include "Includes.h" +#include "BuildAll.h" +#include "sourcesafe.h" +#include "gla.h" // just for string stuff + +#include +using namespace std; + +#define sSAVEINFOSTRINGCHECK "(SaveInfo):" // so comment reader can stop these damn things accumulating + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +int giLODLevelOverride = 0; // MUST default to 0 + +void SS_DisposingOfCurrent(LPCSTR psFileName, bool bDirty); +static bool FileUsesGLAReference(LPCSTR psFilename, LPCSTR psGLAReference); + +keywordArray_t CAssimilateDoc::s_Symbols[] = +{ + "\\", TK_BACKSLASH, + "/", TK_SLASH, + ".", TK_DOT, + "_", TK_UNDERSCORE, + "-", TK_DASH, + "$", TK_DOLLAR, + NULL, TK_EOF, +}; + +keywordArray_t CAssimilateDoc::s_Keywords[] = +{ + "aseanimgrabinit", TK_AS_GRABINIT, + "scale", TK_AS_SCALE, + "keepmotion", TK_AS_KEEPMOTION, + "pcj", TK_AS_PCJ, + "aseanimgrab", TK_AS_GRAB, + "aseanimgrab_gla", TK_AS_GRAB_GLA, + "aseanimgrabfinalize", TK_AS_GRABFINALIZE, + "aseanimconvert", TK_AS_CONVERT, + "aseanimconvertmdx", TK_AS_CONVERTMDX, + "aseanimconvertmdx_noask", TK_AS_CONVERTMDX_NOASK, + NULL, TK_EOF, +}; + +keywordArray_t CAssimilateDoc::s_grabKeywords[] = +{ + "frames", TK_AS_FRAMES, + "fill", TK_AS_FILL, + "sound", TK_AS_SOUND, + "action", TK_AS_ACTION, + "enum", TK_AS_ENUM, + "loop", TK_AS_LOOP, + "qdskipstart", TK_AS_QDSKIPSTART, // useful so qdata can quickly skip extra stuff without having to know the syntax of what to skip + "qdskipstop", TK_AS_QDSKIPSTOP, + "additional", TK_AS_ADDITIONAL, + "prequat", TK_AS_PREQUAT, + "framespeed", TK_AS_FRAMESPEED, // retro hack because original format only supported frame speeds on additional sequences, not masters + "genloopframe", TK_AS_GENLOOPFRAME, + NULL, TK_EOF, +}; + +keywordArray_t CAssimilateDoc::s_convertKeywords[] = +{ + "playerparms", TK_AS_PLAYERPARMS, + "origin", TK_AS_ORIGIN, + "smooth", TK_AS_SMOOTH, + "losedupverts", TK_AS_LOSEDUPVERTS, + "makeskin", TK_AS_MAKESKIN, + "ignorebasedeviations", TK_AS_IGNOREBASEDEVIATIONS, // temporary! + "skew90", TK_AS_SKEW90, + "noskew90", TK_AS_NOSKEW90, + "skel", TK_AS_SKEL, + "makeskel", TK_AS_MAKESKEL, + NULL, TK_EOF, +}; + +LPCTSTR CAssimilateDoc::GetKeyword(int token, int table) +{ + if ((table == TABLE_ANY) || (table == TABLE_QDT)) + { + int i = 0; + while(s_Keywords[i].m_tokenvalue != TK_EOF) + { + if (s_Keywords[i].m_tokenvalue == token) + { + return s_Keywords[i].m_keyword; + } + i++; + } + } + if ((table == TABLE_ANY) || (table == TABLE_GRAB)) + { + int i = 0; + while(s_grabKeywords[i].m_tokenvalue != TK_EOF) + { + if (s_grabKeywords[i].m_tokenvalue == token) + { + return s_grabKeywords[i].m_keyword; + } + i++; + } + } + if ((table == TABLE_ANY) || (table == TABLE_CONVERT)) + { + int i = 0; + while(s_convertKeywords[i].m_tokenvalue != TK_EOF) + { + if (s_convertKeywords[i].m_tokenvalue == token) + { + return s_convertKeywords[i].m_keyword; + } + i++; + } + } + return NULL; +} + +///////////////////////////////////////////////////////////////////////////// +// CAssimilateDoc + +IMPLEMENT_DYNCREATE(CAssimilateDoc, CDocument) + +BEGIN_MESSAGE_MAP(CAssimilateDoc, CDocument) + //{{AFX_MSG_MAP(CAssimilateDoc) + ON_COMMAND(IDM_ADDFILES, OnAddfiles) + ON_COMMAND(IDM_EXTERNAL, OnExternal) + ON_COMMAND(IDM_RESEQUENCE, OnResequence) + ON_COMMAND(IDM_BUILD, OnBuild) + ON_COMMAND(IDM_BUILD_MULTILOD, OnBuildMultiLOD) + ON_COMMAND(IDM_VALIDATE, OnValidate) + ON_COMMAND(IDM_CARWASH, OnCarWash) + ON_COMMAND(IDM_VALIDATE_MULTILOD, OnValidateMultiLOD) + ON_COMMAND(ID_VIEW_ANIMENUMS, OnViewAnimEnums) + ON_UPDATE_COMMAND_UI(ID_VIEW_ANIMENUMS, OnUpdateViewAnimEnums) + ON_COMMAND(ID_VIEW_FRAMEDETAILS, OnViewFrameDetails) + ON_UPDATE_COMMAND_UI(ID_VIEW_FRAMEDETAILS, OnUpdateViewFrameDetails) + ON_UPDATE_COMMAND_UI(IDM_RESEQUENCE, OnUpdateResequence) + ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave) + ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_AS, OnUpdateFileSaveAs) + ON_UPDATE_COMMAND_UI(IDM_EXTERNAL, OnUpdateExternal) + ON_UPDATE_COMMAND_UI(IDM_VALIDATE, OnUpdateValidate) + ON_UPDATE_COMMAND_UI(IDM_BUILD, OnUpdateBuild) + ON_COMMAND(ID_EDIT_BUILDALL, OnEditBuildall) + ON_COMMAND(IDM_EDIT_BUILDDEPENDANT, OnEditBuildDependant) + ON_COMMAND(ID_VIEW_FRAMEDETAILSONADDITIONALSEQUENCES, OnViewFramedetailsonadditionalsequences) + ON_UPDATE_COMMAND_UI(ID_VIEW_FRAMEDETAILSONADDITIONALSEQUENCES, OnUpdateViewFramedetailsonadditionalsequences) + ON_UPDATE_COMMAND_UI(IDM_EDIT_BUILDDEPENDANT, OnUpdateEditBuilddependant) + ON_COMMAND(ID_EDIT_LAUNCHMODVIEWONCURRENT, OnEditLaunchmodviewoncurrent) + ON_UPDATE_COMMAND_UI(ID_EDIT_LAUNCHMODVIEWONCURRENT, OnUpdateEditLaunchmodviewoncurrent) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CAssimilateDoc construction/destruction + +CAssimilateDoc::CAssimilateDoc() +{ + // TODO: add one-time construction code here + Init(); +} + +CAssimilateDoc::~CAssimilateDoc() +{ +} + +BOOL CAssimilateDoc::OnNewDocument() +{ + SS_DisposingOfCurrent(m_strPathName, !!IsModified()); + + if (!CDocument::OnNewDocument()) + return FALSE; + + // TODO: add reinitialization code here + // (SDI documents will reuse this document) + + SetTitle("Untitled"); // for some reason MFC doesn't do this from time to time + + return TRUE; +} + +void CAssimilateDoc::Init() +{ + m_comments = NULL; + m_modelList = NULL; + m_curModel = NULL; + m_lastModel = NULL; +} + +///////////////////////////////////////////////////////////////////////////// +// CAssimilateDoc serialization + +CModel* CAssimilateDoc::AddModel() +{ + CModel* thisModel = CModel::Create(m_comments); + m_comments = NULL; + if (m_modelList == NULL) + { + m_modelList = thisModel; + } + else + { + CModel* curModel = m_modelList; + while (curModel->GetNext() != NULL) + { + curModel = curModel->GetNext(); + } + curModel->SetNext(thisModel); + } + m_curModel = thisModel; + + return m_curModel; +} + +// remember to account for Mike's m_lastModel and move it down if necessary (until I can throw it away)... +// +void CAssimilateDoc::DeleteModel(CModel *deleteModel) +{ + // linklist is only 1-way, so we need to find the stage previous to this (if any)... + // + CModel* prevModel = NULL; + CModel* scanModel = GetFirstModel(); + + while (scanModel && scanModel != deleteModel) + { + prevModel = scanModel; + scanModel = scanModel->GetNext(); + } + if (scanModel == deleteModel) + { + // we found it, so was this the first model in the list? + // + if (prevModel) + { + prevModel->SetNext(scanModel->GetNext()); // ...no + } + else + { + m_modelList = scanModel->GetNext(); // ...yes + } + scanModel->Delete(); + } + + // fixme: ditch this whenever possible + // keep Mike's var up to date... + // + scanModel = GetFirstModel(); + while(scanModel && scanModel->GetNext()) + { + scanModel = scanModel->GetNext(); + } + m_lastModel = scanModel; +} + +void CAssimilateDoc::EndModel() +{ + m_lastModel = m_curModel; + m_curModel = NULL; +} + +// XSI or GLA anim grab... +// +void CAssimilateDoc::ParseGrab(CTokenizer* tokenizer, int iGrabType) +{ + if (m_curModel == NULL) + { + tokenizer->Error("Grab without an active model"); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + CToken* curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL, 0); + if (curToken->GetType() != TK_IDENTIFIER) + { + tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + CString path = curToken->GetStringValue(); + curToken->Delete(); + + while(curToken != NULL) + { + curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL | TKF_SPACETOKENS, 0); + switch(curToken->GetType()) + { + case TK_SLASH: + case TK_BACKSLASH: + path += "/"; + curToken->Delete(); + curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL | TKF_SPACETOKENS, 0); + + // hack for "8472" as in models/players/8472/blah.car. Arrggh!!!!!!!!!!!!!!!!!! + // + if (curToken->GetType() == TK_INT) + { + path += curToken->GetStringValue(); + curToken->Delete(); + break; + } + + if (curToken->GetType() != TK_IDENTIFIER) + { + tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + path += curToken->GetStringValue(); + curToken->Delete(); + break; + case TK_UNDERSCORE: + case TK_DASH: + path += curToken->GetStringValue(); + curToken->Delete(); + curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL | TKF_SPACETOKENS, 0); + if (curToken->GetType() != TK_IDENTIFIER) + { + tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + path += curToken->GetStringValue(); + curToken->Delete(); + break; + case TK_DOT: + path += curToken->GetStringValue(); + curToken->Delete(); + curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL | TKF_SPACETOKENS, 0); + if (curToken->GetType() != TK_IDENTIFIER) + { + tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + path += curToken->GetStringValue(); + curToken->Delete(); + curToken = NULL; + break; + case TK_SPACE: + curToken->Delete(); + curToken = NULL; + break; + case TK_EOL: + tokenizer->PutBackToken(curToken); + curToken = NULL; + break; + default: + tokenizer->PutBackToken(curToken); + curToken = NULL; + break; + } + } + + CString enumname; + int fill = -1; + int loop = 0; + CString sound; + CString action; + int startFrame = 0; + int targetFrame = 0; + int framecount = -1; + int framespeed = iDEFAULTSEQFRAMESPEED; + bool bFrameSpeedFound = false; + int iFrameSpeedFromHeader; + // + int iStartFrames[MAX_ADDITIONAL_SEQUENCES]={0}; + int iFrameCounts[MAX_ADDITIONAL_SEQUENCES]={0}; + int iLoopFrames [MAX_ADDITIONAL_SEQUENCES]={0}; + int iFrameSpeeds[MAX_ADDITIONAL_SEQUENCES]={0}; + CString csEnums [MAX_ADDITIONAL_SEQUENCES]; + int iAdditionalSeqNum = 0; + bool bGenLoopFrame = false; + + bool bSomeParamsFound = false; + + curToken = tokenizer->GetToken(TKF_USES_EOL); + switch (iGrabType) + { + case TK_AS_GRAB: + { + while (curToken->GetType() == TK_DASH) + { + bSomeParamsFound = true; + curToken->Delete(); + curToken = tokenizer->GetToken(s_grabKeywords, TKF_USES_EOL, 0); + switch (curToken->GetType()) + { + case TK_AS_FRAMES: + curToken->Delete(); + + curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0); + if (curToken->GetType() != TK_INTEGER) + { + tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + startFrame = curToken->GetIntValue(); + curToken->Delete(); + + curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0); + if (curToken->GetType() != TK_INTEGER) + { + tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + targetFrame = curToken->GetIntValue(); + curToken->Delete(); + + curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0); + if (curToken->GetType() != TK_INTEGER) + { + tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + framecount = curToken->GetIntValue(); + break; + case TK_AS_FILL: + curToken->Delete(); + + curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0); + if (curToken->GetType() != TK_INTEGER) + { + tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + fill = curToken->GetIntValue(); + break; + case TK_AS_ENUM: + curToken->Delete(); + + curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0); + if (curToken->GetType() != TK_IDENTIFIER) + { + tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + enumname = curToken->GetStringValue(); + break; + case TK_AS_SOUND: + curToken->Delete(); + + curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0); + if (curToken->GetType() != TK_IDENTIFIER) + { + tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + sound = curToken->GetStringValue(); + break; + case TK_AS_ACTION: + curToken->Delete(); + + curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0); + if (curToken->GetType() != TK_IDENTIFIER) + { + tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + action = curToken->GetStringValue(); + break; + case TK_AS_LOOP: + curToken->Delete(); + + curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0); + if (curToken->GetType() != TK_INTEGER) + { + tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + loop = curToken->GetIntValue(); + break; + + case TK_AS_QDSKIPSTART: + case TK_AS_QDSKIPSTOP: + //curToken->Delete(); // don't do this, the whole thing relies on case statements leaving one current token for the outside loop to delete + break; + + case TK_AS_GENLOOPFRAME: + bGenLoopFrame = true; + break; + + case TK_AS_FRAMESPEED: // this is still read in for compatibility, but gets overwritten lower down + curToken->Delete(); + + curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0); + if (curToken->GetType() != TK_INTEGER) + { + tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + framespeed = curToken->GetIntValue(); + bFrameSpeedFound = true; + break; + + case TK_AS_PREQUAT: + m_curModel->SetPreQuat(true); + break; + case TK_AS_ADDITIONAL: + curToken->Delete(); + + if (iAdditionalSeqNum == MAX_ADDITIONAL_SEQUENCES) + { + tokenizer->Error(TKERR_USERERROR, va("Trying to define > %d additional sequences for this master",MAX_ADDITIONAL_SEQUENCES)); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + + // startframe... + // + curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0); + if (curToken->GetType() != TK_INTEGER) + { + tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + iStartFrames[iAdditionalSeqNum] = curToken->GetIntValue(); + + // framecount... + // + curToken->Delete(); + curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0); + if (curToken->GetType() != TK_INTEGER) + { + tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + iFrameCounts[iAdditionalSeqNum] = curToken->GetIntValue(); + + // loopframe... + // + curToken->Delete(); + curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0); + if (curToken->GetType() != TK_INTEGER) + { + tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + iLoopFrames[iAdditionalSeqNum] = curToken->GetIntValue(); + + // framespeed... + // + curToken->Delete(); + curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0); + if (curToken->GetType() != TK_INTEGER) + { + tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + iFrameSpeeds[iAdditionalSeqNum] = curToken->GetIntValue(); + + // enum... + // + curToken->Delete(); + curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0); + if (curToken->GetType() != TK_IDENTIFIER) + { + tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + csEnums[iAdditionalSeqNum] = curToken->GetStringValue(); + + iAdditionalSeqNum++; + break; + + default: + tokenizer->Error(TKERR_UNEXPECTED_TOKEN, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + curToken->Delete(); + curToken = tokenizer->GetToken(s_grabKeywords, TKF_USES_EOL, 0); + } + } + break; + + case TK_AS_GRAB_GLA: + { + // no additional params permitted for this type currently... + // + } + break; + } + + path.MakeLower(); + // + // if no extension, assume ".xsi"... (or ".gla" now) + // + if (!(path.GetAt(path.GetLength()-4) == '.')) + { + path += (iGrabType == TK_AS_GRAB)?".xsi":".gla"; + } + + if (curToken->GetType() != TK_EOL) + { + tokenizer->Error(TKERR_UNEXPECTED_TOKEN, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + + // ignore any user params about speed and frame counts, and just re-grab them from the XSI file... + // +// if (bSomeParamsFound) +// { +// } +// else + { + // at this point, it must be one of the paramless entries in a .CAR file, so we need to + // provide the values for: startFrame, targetFrame, framecount + // + // read in values from the actual file, in case we need to use them... + // + CString nameASE = ((CAssimilateApp*)AfxGetApp())->GetQuakeDir(); + nameASE+= path; + int iStartFrame, iFrameCount; + ReadASEHeader( nameASE, iStartFrame, iFrameCount, iFrameSpeedFromHeader, (iGrabType == TK_AS_GRAB_GLA) ); + +// if (strstr(nameASE,"death16")) +// { +// int z=1; +// } + + startFrame = 0; // always + targetFrame= 0; // any old shite value + framecount = iFrameCount; + + if (iGrabType != TK_AS_GRAB_GLA) + { + if (!bFrameSpeedFound) + { + framespeed = iFrameSpeedFromHeader; + } + } + } + + curToken->Delete(); + + CSequence* sequence = CSequence::_Create(bGenLoopFrame,(iGrabType == TK_AS_GRAB_GLA), path, startFrame, targetFrame, framecount, framespeed, iFrameSpeedFromHeader); + m_curModel->AddSequence(sequence); + sequence->AddComment(m_curModel->ExtractComments()); + sequence->DeriveName(); + if (enumname.IsEmpty()) + { + sequence->SetEnum(sequence->GetName()); + } + else + { + sequence->SetEnum(enumname); + } + sequence->SetFill(fill); + sequence->SetSound(sound); + sequence->SetAction(action); + sequence->SetValidEnum(((CAssimilateApp*)AfxGetApp())->ValidEnum(sequence->GetEnum())); + sequence->SetLoopFrame(loop); + + for (int i=0; iAdditionalSeqs[i]->SetStartFrame(iStartFrames[i]); + sequence->AdditionalSeqs[i]->SetFrameCount(iFrameCounts[i]); + sequence->AdditionalSeqs[i]->SetFrameSpeed(iFrameSpeeds[i]); + sequence->AdditionalSeqs[i]->SetLoopFrame (iLoopFrames [i]); + sequence->AdditionalSeqs[i]->SetEnum (csEnums [i]); + sequence->AdditionalSeqs[i]->SetValidEnum (((CAssimilateApp*)AfxGetApp())->ValidEnum(sequence->AdditionalSeqs[i]->GetEnum())); + } +} + + +// return = success. if false ret, return from caller because of error +// +bool Tokenizer_ReadPath(CString& path, CTokenizer* &tokenizer, CToken* &curToken ) +{ + curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL, 0); + if (curToken->GetType() != TK_IDENTIFIER) + { + tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return false; + } + path += curToken->GetStringValue(); + curToken->Delete(); + + while(curToken != NULL) + { + curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL | (path.IsEmpty()?0:TKF_SPACETOKENS), 0); + switch(curToken->GetType()) + { + case TK_SLASH: + case TK_BACKSLASH: + path += "/"; + curToken->Delete(); + curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL | TKF_SPACETOKENS, 0); + + // hack for "8472" as in models/players/8472/blah.car. Arrggh!!!!!!!!!!!!!!!!!! + // + if (curToken->GetType() == TK_INT) + { + path += curToken->GetStringValue(); + curToken->Delete(); + break; + } + + if (curToken->GetType() != TK_IDENTIFIER) + { + tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return false; + } + path += curToken->GetStringValue(); + curToken->Delete(); + break; + case TK_UNDERSCORE: + case TK_DASH: + path += curToken->GetStringValue(); + curToken->Delete(); + curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL | TKF_SPACETOKENS, 0); + if (curToken->GetType() != TK_IDENTIFIER) + { + tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return false; + } + path += curToken->GetStringValue(); + curToken->Delete(); + break; + case TK_DOT: + path += curToken->GetStringValue(); + curToken->Delete(); + curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL | TKF_SPACETOKENS, 0); + if (curToken->GetType() != TK_IDENTIFIER) + { + tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return false; + } + path += curToken->GetStringValue(); + curToken->Delete(); + curToken = NULL; + break; + case TK_SPACE: + case TK_EOL: + curToken->Delete(); + curToken = NULL; + break; + default: + tokenizer->PutBackToken(curToken); + curToken = NULL; + break; + } + } + + return true; +} + +void CAssimilateDoc::ParseConvert(CTokenizer* tokenizer, int iTokenType) +{ + if (m_lastModel == NULL) + { + tokenizer->Error("Convert without an internal model"); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + CToken* curToken = NULL; + CString path; + + if (!Tokenizer_ReadPath(path, tokenizer, curToken )) + return; +/* + while(curToken != NULL) + { + curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL | TKF_SPACETOKENS, 0); + switch(curToken->GetType()) + { + case TK_SLASH: + case TK_BACKSLASH: + path += "/"; + curToken->Delete(); + curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL | TKF_SPACETOKENS, 0); + + // hack for "8472" as in models/players/8472/blah.car. Arrggh!!!!!!!!!!!!!!!!!! + // + if (curToken->GetType() == TK_INT) + { + path += curToken->GetStringValue(); + curToken->Delete(); + break; + } + + if (curToken->GetType() != TK_IDENTIFIER) + { + tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + path += curToken->GetStringValue(); + curToken->Delete(); + break; + case TK_UNDERSCORE: + case TK_DASH: + path += curToken->GetStringValue(); + curToken->Delete(); + curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL | TKF_SPACETOKENS, 0); + if (curToken->GetType() != TK_IDENTIFIER) + { + tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + path += curToken->GetStringValue(); + curToken->Delete(); + break; + case TK_DOT: + path += curToken->GetStringValue(); + curToken->Delete(); + curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL | TKF_SPACETOKENS, 0); + if (curToken->GetType() != TK_IDENTIFIER) + { + tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + path += curToken->GetStringValue(); + curToken->Delete(); + curToken = NULL; + break; + case TK_SPACE: + case TK_EOL: + curToken->Delete(); + curToken = NULL; + break; + default: + tokenizer->PutBackToken(curToken); + curToken = NULL; + break; + } + } +*/ + int originx = 0; // important to default to 0! + int originy = 0; // + int originz = 0; // + int parm1 = 0; + int parm2 = 0; + int parm3 = 0; + int parm4 = 0; + + curToken = tokenizer->GetToken(); + while(curToken->GetType() == TK_DASH) + { + curToken->Delete(); + curToken = tokenizer->GetToken(s_convertKeywords, 0, 0); + switch(curToken->GetType()) + { + case TK_AS_ORIGIN: + curToken->Delete(); + curToken = tokenizer->GetToken(); + if (curToken->GetType() != TK_INTEGER) + { + tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + originx = curToken->GetIntValue(); + curToken->Delete(); + curToken = tokenizer->GetToken(); + if (curToken->GetType() != TK_INTEGER) + { + tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + originy = curToken->GetIntValue(); + curToken->Delete(); + curToken = tokenizer->GetToken(); + if (curToken->GetType() != TK_INTEGER) + { + tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + originz = curToken->GetIntValue(); + curToken->Delete(); + curToken = tokenizer->GetToken(); + break; + case TK_AS_PLAYERPARMS: + curToken->Delete(); + curToken = tokenizer->GetToken(); + if (curToken->GetType() != TK_INTEGER) + { + tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + parm1 = curToken->GetIntValue(); + curToken->Delete(); +/* this param no longer exists... + curToken = tokenizer->GetToken(); + if (curToken->GetType() != TK_INTEGER) + { + tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + parm2 = curToken->GetIntValue(); + curToken->Delete(); +*/ + curToken = tokenizer->GetToken(); + if (curToken->GetType() != TK_INTEGER) + { + tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + parm3 = curToken->GetIntValue(); + curToken->Delete(); + +/* this param no longer exists... + curToken = tokenizer->GetToken(); + if (curToken->GetType() != TK_INTEGER) + { + tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + parm4 = curToken->GetIntValue(); + curToken->Delete(); +*/ parm4 = 1; + + curToken = tokenizer->GetToken(); + break; + case TK_AS_SMOOTH: + curToken->Delete(); + m_lastModel->SetSmooth(true); + curToken = tokenizer->GetToken(); + break; + case TK_AS_LOSEDUPVERTS: + curToken->Delete(); + m_lastModel->SetLoseDupVerts(true); + curToken = tokenizer->GetToken(); + break; + case TK_AS_MAKESKIN: + curToken->Delete(); + m_lastModel->SetMakeSkin(true); + curToken = tokenizer->GetToken(); + break; + case TK_AS_IGNOREBASEDEVIATIONS: + curToken->Delete(); + m_lastModel->SetIgnoreBaseDeviations(true); + curToken = tokenizer->GetToken(); + break; + case TK_AS_SKEW90: + curToken->Delete(); + m_lastModel->SetSkew90(true); + curToken = tokenizer->GetToken(); + break; + case TK_AS_NOSKEW90: + curToken->Delete(); + m_lastModel->SetNoSkew90(true); + curToken = tokenizer->GetToken(); + break; +/* + case TK_AS_SKEL: + { + CString strSkelPath; + curToken->Delete(); + + if (!Tokenizer_ReadPath(strSkelPath, tokenizer, curToken )) + { + return; + } + m_lastModel->SetSkelPath(strSkelPath); + m_lastModel->SetMakeSkelPath(""); + curToken = tokenizer->GetToken(); + } + break; +*/ + case TK_AS_MAKESKEL: + { + CString strMakeSkelPath; + curToken->Delete(); + + if (!Tokenizer_ReadPath(strMakeSkelPath, tokenizer, curToken)) + { + return; + } + m_lastModel->SetMakeSkelPath(strMakeSkelPath); +// m_lastModel->SetSkelPath(""); + curToken = tokenizer->GetToken(); + } + break; + default: + tokenizer->Error(TKERR_UNEXPECTED_TOKEN, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + } + tokenizer->PutBackToken(curToken); + path.MakeLower(); + m_lastModel->DeriveName(path); + m_lastModel->SetParms(parm1, parm2, parm3, parm4); + m_lastModel->SetOrigin(originx, originy, originz); + m_lastModel->SetConvertType(iTokenType); +} + +void CAssimilateDoc::AddComment(LPCTSTR comment) +{ + // some code to stop those damn timestamps accumulating... + // + if (!strnicmp(comment,sSAVEINFOSTRINGCHECK,strlen(sSAVEINFOSTRINGCHECK))) + { + return; + } + CComment* thisComment = CComment::Create(comment); + if (m_curModel != NULL) + { + m_curModel->AddComment(thisComment); + return; + } + if (m_comments == NULL) + { + m_comments = thisComment; + } + else + { + CComment* curComment = m_comments; + while (curComment->GetNext() != NULL) + { + curComment = curComment->GetNext(); + } + curComment->SetNext(thisComment); + } +} + +/////////////////////////////////////////////// + +#define MAX_FOUND_FILES 0x1000 +#define MAX_OSPATH MAX_PATH +#include +#include + +char **Sys_ListFiles( const char *directory, const char *extension, int *numfiles ) { + char search[MAX_OSPATH]; + int nfiles; + char **listCopy; + char *list[MAX_FOUND_FILES]; + struct _finddata_t findinfo; + int findhandle; + int flag; + int i; + + if ( !extension) { + extension = ""; + } + + if ( extension[0] == '/' && extension[1] == 0 ) { + extension = ""; + flag = 0; + } else { + flag = _A_SUBDIR; + } + + sprintf( search, "%s\\*%s", directory, extension ); + + // search + nfiles = 0; + + findhandle = _findfirst (search, &findinfo); + if (findhandle == -1) { + *numfiles = 0; + return NULL; + } + + do { + if ( flag ^ ( findinfo.attrib & _A_SUBDIR ) ) { + if ( nfiles == MAX_FOUND_FILES - 1 ) { + break; + } + list[ nfiles ] = strdup( strlwr(findinfo.name) ); + nfiles++; + } + } while ( _findnext (findhandle, &findinfo) != -1 ); + + list[ nfiles ] = 0; + + _findclose (findhandle); + + // return a copy of the list + *numfiles = nfiles; + + if ( !nfiles ) { + return NULL; + } + + listCopy = (char **) malloc( ( nfiles + 1 ) * sizeof( *listCopy ) ); + for ( i = 0 ; i < nfiles ; i++ ) { + listCopy[i] = list[i]; + } + listCopy[i] = NULL; + + return listCopy; +} + +void Sys_FreeFileList( char **_list ) { + int i; + + if ( !_list ) { + return; + } + + for ( i = 0 ; _list[i] ; i++ ) { + free( _list[i] ); + } + + free( _list ); +} + +////////////////////////////////////////// + +CString strSkippedFiles; +CString strSkippedDirs; +CString strCARsFound; +int iCARsFound = 0; + +void AlphaSortCARs(void) +{ + typedef set SortedStrings_t; + SortedStrings_t SortedStrings; + + for (int i=0; i=0) + { + SortedStrings.insert(SortedStrings.end(), (LPCSTR)(strThisFile.Left(iLoc))); + strCARsFound= strCARsFound.Mid(iLoc+1); + } + } + + // clear files-found string out, and re-enter from sorted set... + // + strCARsFound = ""; + + for (SortedStrings_t::iterator it = SortedStrings.begin(); it != SortedStrings.end(); ++it) + { + strCARsFound += (*it).c_str(); + strCARsFound += "\n"; + } +} + +void R_CheckCARs( LPCSTR psDir, int iScanDepth, LPCSTR psGLAReferenceItShouldInclude ) +{ + ((CMainFrame*)AfxGetMainWnd())->StatusMessage(va("(%d .CAR files found so far) Scanning Dir: %s",iCARsFound,psDir)); + + // ignore any dir with "test" in it... + // + if (//strstr(psDir,"\\test") + //|| + strstr(psDir,"\\backup") + || + strstr(psDir,"\\ignore_") + ) + { + strSkippedDirs += psDir; + strSkippedDirs += "\n"; + return; + } + + char **sysFiles, **dirFiles;//, *args[5]; + int numSysFiles, i, /*len,*/ numdirs; + char altname[MAX_OSPATH]; +// char command1[MAX_OSPATH]; +// char command2[MAX_OSPATH]; + + dirFiles = Sys_ListFiles(psDir, "/", &numdirs); + if (numdirs > 2) + { +// if (!iScanDepth) // recursion limiter, to avoid scanning backup subdirs within model subdirs + { + for (i=2;iGetFilePath()); +} + +void CAssimilateDoc::Parse(LPCSTR psFilename) +{ + gbParseError = false; + CAlertErrHandler errhandler; + CTokenizer* tokenizer = CTokenizer::Create(TKF_NOCASEKEYWORDS | TKF_COMMENTTOKENS); + tokenizer->SetErrHandler(&errhandler); + tokenizer->SetSymbols(s_Symbols); + tokenizer->SetKeywords(s_Keywords); + tokenizer->AddParseFile(psFilename); + + extern bool gbSkipXSIRead_QuestionAsked; + extern bool gbSkipXSIRead; + gbSkipXSIRead_QuestionAsked = false; // opening a new file so reset our question + gbSkipXSIRead = false; + + int tokType = TK_UNDEFINED; + while(tokType != TK_EOF) + { + CToken* curToken = tokenizer->GetToken(); + tokType = curToken->GetType(); + switch(tokType) + { + case TK_EOF: + curToken->Delete(); + break; + case TK_DOLLAR: + curToken->Delete(); + curToken = tokenizer->GetToken(); + switch(curToken->GetType()) + { + case TK_AS_GRABINIT: + curToken->Delete(); + AddModel(); + break; + case TK_AS_SCALE: + curToken->Delete(); + curToken = tokenizer->GetToken(); + if (curToken->GetType() != TK_FLOAT && curToken->GetType() != TK_INT) + { + tokenizer->Error(TKERR_EXPECTED_FLOAT, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + m_curModel->SetScale((curToken->GetType() == TK_FLOAT)?curToken->GetFloatValue():curToken->GetIntValue()); + curToken->Delete(); + break; + case TK_AS_KEEPMOTION: + curToken->Delete(); + m_curModel->SetKeepMotion(true); + break; + case TK_AS_PCJ: + curToken->Delete(); + curToken = tokenizer->GetToken(); + if (curToken->GetType() != TK_IDENTIFIER && curToken->GetType() != TK_DOLLAR) // eg: '$pcj pelvis' or '$pcj $flatten' + { + tokenizer->Error(TKERR_EXPECTED_STRING, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + if (curToken->GetType() == TK_DOLLAR) // read string after '$' char + { + curToken->Delete(); + curToken = tokenizer->GetToken(); + if (curToken->GetType() != TK_IDENTIFIER) + { + tokenizer->Error(TKERR_EXPECTED_STRING, curToken->GetStringValue()); + curToken->Delete(); + tokenizer->GetToEndOfLine()->Delete(); + return; + } + + m_curModel->PCJList_AddEntry(va("$%s",curToken->GetStringValue())); + } + else + { + m_curModel->PCJList_AddEntry(curToken->GetStringValue()); + } + curToken->Delete(); + break; + case TK_AS_GRAB: + curToken->Delete(); + ParseGrab(tokenizer,TK_AS_GRAB); + break; + case TK_AS_GRAB_GLA: + curToken->Delete(); + ParseGrab(tokenizer,TK_AS_GRAB_GLA); + break; + case TK_AS_GRABFINALIZE: + curToken->Delete(); + EndModel(); + break; + case TK_AS_CONVERT: + curToken->Delete(); + ParseConvert(tokenizer,TK_AS_CONVERT); + break; + case TK_AS_CONVERTMDX: + curToken->Delete(); + ParseConvert(tokenizer,TK_AS_CONVERTMDX); + break; + case TK_AS_CONVERTMDX_NOASK: + curToken->Delete(); + ParseConvert(tokenizer,TK_AS_CONVERTMDX_NOASK); + break; + case TK_COMMENT: + AddComment(curToken->GetStringValue()); + curToken->Delete(); + break; + default: + tokenizer->Error(TKERR_UNEXPECTED_TOKEN); + curToken->Delete(); + break; + } + break; + case TK_COMMENT: + AddComment(curToken->GetStringValue()); + curToken->Delete(); + break; + default: + tokenizer->Error(TKERR_UNEXPECTED_TOKEN); + curToken->Delete(); + break; + } + } + tokenizer->Delete(); + UpdateAllViews(NULL, AS_NEWFILE, NULL); + Resequence(); +} + +void CAssimilateDoc::Write(CFile* file) +{ + CTxtFile* outfile = CTxtFile::Create(file); + +/* // write out time/date stamp... + // + CString commentLine; + CTime time = CTime::GetCurrentTime(); + commentLine.Format("// %s %s updated %s", sSAVEINFOSTRINGCHECK, file->GetFileName(), time.Format("%H:%M %A, %B %d, %Y")); + outfile->Writeln(commentLine); +*/ + CModel* curModel = m_modelList; + while(curModel != NULL) + { + curModel->Write(outfile); + curModel = curModel->GetNext(); + } + CComment* curComment = m_comments; + while(curComment != NULL) + { + curComment->Write(outfile); + curComment = curComment->GetNext(); + } + outfile->Delete(); +} + +void CAssimilateDoc::Serialize(CArchive& ar) +{ + if (ar.IsStoring()) + { + // TODO: add storing code here + Write(ar.GetFile()); + } + else + { + // TODO: add loading code here + Parse(ar.GetFile()); + } +} + +///////////////////////////////////////////////////////////////////////////// +// CAssimilateDoc diagnostics + +#ifdef _DEBUG +void CAssimilateDoc::AssertValid() const +{ + CDocument::AssertValid(); +} + +void CAssimilateDoc::Dump(CDumpContext& dc) const +{ + CDocument::Dump(dc); +} +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// CAssimilateDoc commands + +void CAssimilateDoc::DeleteContents() +{ + // TODO: Add your specialized code here and/or call the base class + UpdateAllViews(NULL, AS_DELETECONTENTS, NULL); + + while(m_comments != NULL) + { + CComment* curComment = m_comments; + m_comments = curComment->GetNext(); + curComment->Delete(); + } + while(m_modelList != NULL) + { + CModel* curModel = m_modelList; + m_modelList = curModel->GetNext(); + curModel->Delete(); + } + m_curModel = NULL; + m_lastModel = NULL; + + gbReportMissingASEs = true; + giFixUpdatedASEFrameCounts = YES; + + CDocument::DeleteContents(); +} + +CModel* CAssimilateDoc::GetFirstModel() +{ + return m_modelList; +} + +int CAssimilateDoc::GetNumModels() +{ + int iCount = 0; + CModel *theModel = m_modelList; + + while (theModel) + { + iCount++; + theModel = theModel->GetNext(); + } + + return iCount; +} + +void CAssimilateDoc::OnAddfiles() +{ + // TODO: Add your command handler code here + CFileDialog theDialog(true, ".xsi", NULL, OFN_EXPLORER | OFN_ALLOWMULTISELECT | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST, + _T("Anim Files (*.ase)(*.xsi)(*.gla)|*.ase;*.xsi;*.gla|All Files|*.*||"), NULL); + +//////////// +// Model files (*.MDR)(*.MD3)|*.md?| +//////////// + char filenamebuffer[16384]; + filenamebuffer[0] = '\0'; + theDialog.m_ofn.lpstrFile = filenamebuffer; + theDialog.m_ofn.nMaxFile = sizeof(filenamebuffer); + + CString + strInitialDir = ((CAssimilateApp*)AfxGetApp())->GetQuakeDir(); + strInitialDir+= "models/players"; + strInitialDir.Replace("/","\\"); + + theDialog.m_ofn.lpstrInitialDir = strInitialDir; + + int result = theDialog.DoModal(); + if (result != IDOK) + { + return; + } + + CWaitCursor waitcursor; + + POSITION pos = theDialog.GetStartPosition(); + while(pos != NULL) + { + CString thisfile = theDialog.GetNextPathName(pos); +/* int loc = thisfile.Find(':'); + if (loc > 0) + { + thisfile = thisfile.Right(thisfile.GetLength() - loc - 1); + } +*/ + Filename_RemoveBASEQ(thisfile); + AddFile(thisfile); + } + SetModifiedFlag(); + UpdateAllViews(NULL, AS_NEWFILE, NULL); + Resequence(); // must be AFTER UpdateAllViews +} + +void CAssimilateDoc::AddFile(LPCTSTR name) +{ + CString strTemp(name); + strTemp.MakeLower(); + if (!strstr(strTemp,"root.xsi") || GetYesNo("You're trying to add \"root.xsi\", which is inherent, you should only do this if you're making a model that has no seperate anim files\n\nProceed?")) + { + // update, ignore any files with a "_1" (2,3, etc) just before the suffix (this skips LOD files)... + // +// int iNameLen = strlen(name); + + // (also, only check files of at least namelen "_?.ase") + // +// if ( iNameLen>6 && name[iNameLen-6]=='_' && isdigit(name[iNameLen-5]) ) + { + // this is a LOD filename, so ignore it... + } +// else + { + CModel *curModel = GetCurrentUserSelectedModel(); + + if (!curModel) + { + curModel = AddModel(); + + CString path = name; + path.MakeLower(); + path.Replace('\\', '/'); + int loc = path.ReverseFind('.'); + if (loc > -1) + { + path = path.Left(loc); + } + loc = path.ReverseFind('/'); + path = path.Left(loc); + path = path + "/root"; + curModel->DeriveName(path); + } + + // check that we don't already have this file... + // + if (!curModel->ContainsFile(name)) + { + curModel->AddSequence(CSequence::CreateFromFile(name, curModel->ExtractComments())); + } + } + } +} + +void CAssimilateDoc::OnExternal() +{ + bool bCFGWritten = false; + + if (WriteCFGFiles(true,bCFGWritten)) + { + CString strReport; + + if (bCFGWritten) + { + if (((CAssimilateApp*)AfxGetApp())->GetMultiPlayerMode()) + { + strReport = "\n\n( CFG file written for MULTI-PLAYER format )"; + } + else + { + strReport = "\n\n( CFG file written for SINGLE-PLAYER format )"; + } + } + else + { + strReport = "\n\n( CFG not needed, not written )"; + } + + InfoBox(strReport); + } +} + +// called both from the menu, and now from the Build() member... +// +bool CAssimilateDoc::WriteCFGFiles(bool bPromptForNames, bool &bCFGWritten) +{ + bCFGWritten = false; + + CModel* curModel = m_modelList; + while(curModel != NULL) + { + bool bThisCFGWritten = false; + if (!curModel->WriteExternal(bPromptForNames,bThisCFGWritten)) + { + return false; + } + if (bThisCFGWritten) + bCFGWritten = true; + + curModel = curModel->GetNext(); + } + + return true; +} + +void CAssimilateDoc::Resequence() +{ + CModel* curModel = m_modelList; + while(curModel != NULL) + { + curModel->Resequence(true); + curModel = curModel->GetNext(); + } + + SetModifiedFlag(); + UpdateAllViews(NULL, AS_FILESUPDATED, NULL); // needed +} + +void CAssimilateDoc::OnResequence() +{ + extern bool gbSkipXSIRead_QuestionAsked; + extern bool gbSkipXSIRead; + gbSkipXSIRead_QuestionAsked = false; // make it happen again + gbSkipXSIRead = false; + Resequence(); +} + +void CAssimilateDoc::OnViewAnimEnums() +{ + gbViewAnimEnums = !gbViewAnimEnums; + UpdateAllViews(NULL, AS_FILESUPDATED, NULL); +} + +void CAssimilateDoc::OnUpdateViewAnimEnums(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(gbViewAnimEnums); +} + +void CAssimilateDoc::OnViewFrameDetails() +{ + gbViewFrameDetails = !gbViewFrameDetails; + UpdateAllViews(NULL, AS_FILESUPDATED, NULL); +} + +void CAssimilateDoc::OnUpdateViewFrameDetails(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(gbViewFrameDetails); +} + + +// 1) save qdt, 2) run qdata on it, 3) if success, save .cfg file... +// +bool CAssimilateDoc::Build(bool bAllowedToShowSuccessBox, int iLODLevel, bool bSkipSave) // damn this stupid Serialize() crap +{ + bool bSuccess = false; + + if (Validate()) // notepad will have been launched with a textfile of errors at this point if faulty + { + // seems valid, so save the QDT... + // + giLODLevelOverride = iLODLevel; + + if (!bSkipSave) + { + OnFileSave(); + } + + CString csQDataLocation = ((CAssimilateApp*)AfxGetApp())->GetQDataFilename(); + + // hack-city!!!!!!!!!! + // + CModel* curModel = ghAssimilateView->GetDocument()->GetCurrentUserSelectedModel(); + if (curModel->GetPreQuat()) + { + csQDataLocation.MakeLower(); + csQDataLocation.Replace("carcass","carcass_prequat"); + } + + CString params = csQDataLocation; + + if (!bSkipSave) + { + params += " -keypress"; + } + params += " -silent"; + params += " "; + params += m_strPathName; // += (eg) {"Q:\quake\baseq3\models\players\ste_assimilate_test\ste_testaa.qdt"} + Filename_AccountForLOD(params, giLODLevelOverride); + + PROCESS_INFORMATION pi; + STARTUPINFO startupinfo; + startupinfo.cb = sizeof(startupinfo); + startupinfo.lpReserved = NULL; + startupinfo.lpDesktop = NULL; + startupinfo.lpTitle = NULL; + startupinfo.dwFlags = 0; + startupinfo.cbReserved2 = 0; + startupinfo.lpReserved2 = NULL; + + params.Replace("/","\\"); + csQDataLocation.Replace("/","\\"); + + LPTSTR paramsPass = params.GetBuffer(params.GetLength() + 1); + + StartWait(); // ++++++++++++++++++++++++ + + if (CreateProcess(csQDataLocation, paramsPass, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &startupinfo, &pi)) + { + WaitForSingleObject(pi.hProcess, INFINITE); + EndWait(); // ------------------------ + DWORD result; + GetExitCodeProcess(pi.hProcess,&result); + if (result) + { + char error[64]; + sprintf(error,"Process returned error: %d",result); + MessageBox(NULL,error,"Build Failed",MB_OK|MB_ICONERROR); + } + CloseHandle(pi.hProcess); + + if (result==0) + { + // QData was run successfully at this point, so write the CFG file(s)... + // + if (iLODLevel) // only LOD 0 writes out the CFG file + { + bSuccess = true; + } + else + { + bool bCFGWritten = false; + if (WriteCFGFiles(false,bCFGWritten)) // false = no name prompt, derive automatically from model name + { + // success (on a plate)... + // + if (bAllowedToShowSuccessBox) + { + CString strReport("Everything seemed to go ok\n\nCAR"); + + if (bCFGWritten) + { + strReport += " and CFG files written"; + if (((CAssimilateApp*)AfxGetApp())->GetMultiPlayerMode()) + { + strReport += "\n\n\n\n( CFG file written for MULTI-PLAYER format )"; + } + else + { + strReport += "\n\n\n\n( CFG file written for SINGLE-PLAYER format )"; + } + } + else + { + strReport += " file written"; + strReport += "\n\n\n\n( CFG file not written for GLA-referencing model )"; + } + + InfoBox(strReport); + } + bSuccess = true; + } + } + } + } + else + { + EndWait(); // ------------------------ + MessageBox(NULL,"Could not spawn process.","Build Failed",MB_OK|MB_ICONERROR); + } + params.ReleaseBuffer(); + } + + giLODLevelOverride = 0; + + return bSuccess; +} + +void CAssimilateDoc::OnBuildMultiLOD() +{ + int iErrors = 0; + + // to save time, I'll run all the validates first, and only if they're all ok will I go on to the build + // (which incidentally does a harmless re-validate again) + // + for (int i=0; i< 1+EXTRA_LOD_LEVELS; i++) + { + iErrors += Validate(false, i)?0:1; + } + + // go ahead if all clear... + // + // (I'll write them in reverse-LOD order so the last one written is the standard one. This should hopefully avoid + // any problems with the current document potentially becoming "(name)_3.qdt" from then on to MFC) + // + if (!iErrors) + { + for (int i=EXTRA_LOD_LEVELS; i>=0; i--) + { + Build( !i, // bool bAllowedToShowSuccessBox (ie only on last one) + i, // int iLODLevel + false // bool bSkipSave + ); + } + } +} + + + +// 1) save qdt, 2) run qdata on it, 3) if success, save .cfg file... +// +void CAssimilateDoc::OnBuild() +{ + Build( true, // bool bAllowedToShowSuccessBox, + 0, // int iLODLevel + false // bool bSkipSave + ); +} + + +void CAssimilateDoc::ClearModelUserSelectionBools() +{ + CModel* curModel = m_modelList; + + while (curModel) + { + curModel->SetUserSelectionBool(false); + curModel = curModel->GetNext(); + } +} + +// if there's only one model loaded, return that, if none, return NULL, else if >1, return selected one, else NULL +// +CModel* CAssimilateDoc::GetCurrentUserSelectedModel() +{ + CModel *curModel = m_modelList; + + if (!curModel || !curModel->GetNext()) + { + return curModel; // 0 or 1 models total loaded + } + + // more than one loaded, so find the selected one and return that... + // + while (curModel) + { + if (curModel->GetUserSelectionBool()) + { + return curModel; + } + + curModel = curModel->GetNext(); + } + + return NULL; // multiple loaded, but none selected +} + + +static bool FileUsesGLAReference(LPCSTR psFilename, LPCSTR psGLAReference) +{ + bool bReturn = false; + + FILE *fHandle = fopen(psFilename,"rt"); + if (fHandle) + { + int iLen = filesize(fHandle); + if (iLen>0) + { + char *psText = (char*) malloc(iLen+1); + if (psText) + { + fread(psText,1,iLen,fHandle); + psText[iLen]='\0'; + + strlwr(psText); + + // this is a simple test that could be made more precise, but for now... + // + if ( (strstr(psText,"aseanimgrab_gla") || strstr(psText,"makeskel")) + && + strstr(psText,psGLAReference) + ) + { + bReturn = true; + } + + free(psText); + } + } + fclose(fHandle); + } + + return bReturn; +} + + + +#define sASSUMEPATH "w:\\game\\base" +bool gbCarWash_YesToXSIScan; +bool gbCarWash_DoingScan = false; // MUST default to this +bool gbQueryGoAhead = true; // MUST defualt to this +LPCSTR gpsCARWashDirOverride = NULL;// MUST default to this +CString strCarWashErrors; +bool gbCarwashErrorsOccured; +CString strMustContainThisGLA; // MUST be blank, else name of GLA to be present to be considered +void CAssimilateDoc::OnCarWashActual() +{ + gbCarwashErrorsOccured = false; + CString strStartDir = gpsCARWashDirOverride?gpsCARWashDirOverride:((CAssimilateApp*)AfxGetApp())->GetQuakeDir(); + strStartDir.Replace("/","\\"); + + if (!strStartDir.GetLength()) + { + ErrorBox("Quake path not known at this point. Prefs not setup?"); + gbCarwashErrorsOccured = true; + return; +// if (!GetYesNo("Quake path not known at this point because you've not loaded anything yet\n\nShould I assume " "\"" sASSUMEPATH "\"" "?")) +// return; +// +// strStartDir = sASSUMEPATH; + } + + // (this app was written so that GetQuakeDir() returns a path with a trailing slash, not nice normally, but here...) + // +// if (strStartDir.GetAt(strStartDir.GetLength()-1)=='\\') +// strStartDir = strStartDir.Left(strStartDir.GetLength()-1); + if (gpsCARWashDirOverride == NULL) + { + strStartDir += "models";//\\players"; + } + + if (gbQueryGoAhead) + { + if (!GetYesNo(va("About to scan: \"%s\\*.CAR /s\"\n\n"/*"This can take a LONG time, "*/"Proceed?",strStartDir))) + return; + } + + gbCarWash_YesToXSIScan = GetYesNo("Full XSI scan? ( \"NO\" will skip XSI reads, but v3.0 files are quick to read anyway now )"); + + CWaitCursor wait; + + strCARsFound.Empty(); + iCARsFound = 0; + strSkippedDirs.Empty(); + strSkippedFiles.Empty(); + + R_CheckCARs( strStartDir, 0, strMustContainThisGLA); //bool bBuildListOnly + AlphaSortCARs(); // not important to alpha-sort here (during car-wash), just looks nicer + ((CMainFrame*)AfxGetMainWnd())->StatusMessage("Ready"); + + // ok, now ready to begin pass 2... + // + CString strReport; + if (!iCARsFound) + { + ASSERT(0); + strReport = "No suitable .CAR files found for processing!\n\n"; + + if (!strSkippedDirs.IsEmpty()) + { + strReport+= "Skipped Dirs:\n\n"; + strReport+= strSkippedDirs; + } + + if (!strSkippedFiles.IsEmpty()) + { + strReport+= "\n\nSkipped Files:\n\n"; + strReport+= strSkippedFiles; + } + ErrorBox(strReport); + gbCarwashErrorsOccured = true; + } + else + { + //---------------- + gbCarWash_DoingScan = true; + strCarWashErrors.Empty(); + //---------------- + + CString strTotalErrors; + + strReport = "Processed files:\n\n"; + for (int i=0; i=0) + { + strThisFile = strThisFile.Left(iLoc); + strCARsFound= strCARsFound.Mid(iLoc+1); + + strReport += strThisFile + "\n"; + + ((CMainFrame*)AfxGetMainWnd())->StatusMessage(va("Scanning File %d/%d: %s",i+1,iCARsFound,(LPCSTR)strThisFile)); + + if (1)//strMustContainThisGLA.IsEmpty() || FileUsesGLAReference(strThisFile, strMustContainThisGLA)) + { + OnNewDocument(); + Parse(strThisFile); + if (gbParseError) + { + strTotalErrors += va("\nParse error in CAR file \"%s\"\n",(LPCSTR)strThisFile); + } + else + { + OnValidate(); + + if (!strCarWashErrors.IsEmpty()) + { + // "something is wrong..." :-) + // + strTotalErrors += va("\nError in file \"%s\":\n\n%s\n",(LPCSTR)strThisFile,(LPCSTR)strCarWashErrors); + } + strCarWashErrors.Empty(); + } + } + } + else + { + ASSERT(0); + strThisFile.Insert(0,"I fucked up, the following line didn't seem to have a CR: (tell me! -Ste)\n\n"); + ErrorBox(strThisFile); + } + } + + //---------------- + gbCarWash_DoingScan = false; + //---------------- + + + OnNewDocument(); // trash whatever was loaded last + +// strReport = "Processed files:\n\n"; +// strReport+= strCARsFound; + strReport+= "\n\nSkipped Dirs:\n\n"; + strReport+= strSkippedDirs; + strReport+= "\n\nSkipped Files:\n\n"; + strReport+= strSkippedFiles; + + if (strTotalErrors.IsEmpty()) + { + strReport.Insert(0,"(No additional errors found)\n\n"); + } + else + { + strReport+= "\n\nAdditional errors will now be sent to Notepad!..."; + gbCarwashErrorsOccured = true; + } + + if (gbQueryGoAhead || gbCarwashErrorsOccured) + { + InfoBox(strReport); + } + + if (!strTotalErrors.IsEmpty()) + { + strTotalErrors.Insert(0, "The following errors occured during CARWash...\n\n"); + SendToNotePad(strTotalErrors, "carwash_errors.txt"); + } + } + +//#define sASSUMEPATH "q:\\quake\\baseq3" +// strTGAsWithRedundantAlpha.Insert(0,"The following files are defined as 32-bit (ie with alpha), but the alpha channel is blank (ie all 255)...\n\n"); +// SendToNotePad(strTGAsWithRedundantAlpha, "TGAs_With_Redundant_Alpha.txt"); + + ((CMainFrame*)AfxGetMainWnd())->StatusMessage("Ready"); +} + +void CAssimilateDoc::OnCarWash() +{ + strMustContainThisGLA.Empty(); + OnCarWashActual(); +} + + +// creates as temp file, then spawns notepad with it... +// +bool SendToNotePad(LPCSTR psWhatever, LPCSTR psLocalFileName) +{ + bool bReturn = false; + + LPCSTR psOutputFileName = va("%s\\%s",scGetTempPath(),psLocalFileName); + + FILE *handle = fopen(psOutputFileName,"wt"); + if (handle) + { + fprintf(handle,psWhatever); + fclose(handle); + + char sExecString[MAX_PATH]; + + sprintf(sExecString,"notepad %s",psOutputFileName); + + if (WinExec(sExecString, // LPCSTR lpCmdLine, // address of command line + SW_SHOWNORMAL // UINT uCmdShow // window style for new application + ) + >31 // don't ask me, Windoze just uses >31 as OK in this call. + ) + { + // ok... + // + bReturn = true; + } + else + { + ErrorBox("Unable to locate/run NOTEPAD on this machine!\n\n(let me know about this -Ste)"); + } + } + else + { + ErrorBox(va("Unable to create file \"%s\" for notepad to use!",psOutputFileName)); + } + + return bReturn; +} + + +// AFX OnXxxx calls need to be void return, but the validate needs to ret a bool for elsewhere, so... +// +void CAssimilateDoc::OnValidate() +{ + Validate(!gbCarWash_DoingScan); +} + +void CAssimilateDoc::OnValidateMultiLOD() +{ + int iErrors = 0; + for (int i=0; i< 1+EXTRA_LOD_LEVELS; i++) + { + iErrors += Validate(false, i)?0:1; + } + + if (!iErrors) + { + InfoBox(va("Everything seems OK\n\n(Original + %d LOD levels checked)",EXTRA_LOD_LEVELS)); + } +} + + +// checks for things that would stop the build process, such as missing ASE files, invalid loopframes, bad anim enums, etc, +// and writes all the faults to a text file that it displays via launching notepad +// +bool CAssimilateDoc::Validate(bool bInfoBoxAllowed, int iLODLevel) +{ + OnResequence(); + + int iNumModels = ghAssimilateView->GetDocument()->GetNumModels(); + CModel* curModel = ghAssimilateView->GetDocument()->GetCurrentUserSelectedModel(); + bool bValidateAll = false; + int iFaults = 0; + + StartWait(); + + if (iNumModels) + { + CString sOutputTextFile; + + sOutputTextFile.Format("%s\\validation_faults%s.txt",scGetTempPath(),iLODLevel?va("_LOD%d",iLODLevel):""); + + FILE *hFile = fopen(sOutputTextFile,"wt"); + + if (hFile) + { + if (iNumModels>1) + { + if (!curModel) // >1 models, but none selected, so validate all + { + bValidateAll = true; + } + else + { + // >1 models, 1 selected, ask if we should do all... + // + bValidateAll = GetYesNo(va("Validate ALL models?\n\n( NO = model \"%s\" only )",curModel->GetName())); + } + } + + if (bValidateAll) + { + curModel = ghAssimilateView->GetDocument()->GetFirstModel(); + } + + if (iLODLevel) + { + fprintf(hFile,"(LOD Level %d)\n",iLODLevel); + } + + while (curModel) + { + fprintf(hFile,"\nModel: \"%s\"\n\n",curModel->GetName()); + + int iThisModelFaults = iFaults; + + if ( ( curModel->GetMakeSkelPath() && strlen(curModel->GetMakeSkelPath()) ) +// || +// ( curModel->GetSkelPath() && strlen(curModel->GetSkelPath()) ) + ) + { + // ... then all is fine + } + else + { + // this is an error, UNLESS you have a GLA sequence... + // + if (!curModel->HasGLA()) + { + //fprintf(hFile, "Model must have either a 'skel' or 'makeskel' path\n ( Double-click on the model name in the treeview to edit )\n"); + fprintf(hFile, "Model must have a 'makeskel' path\n ( Double-click on the top tree item's name (should be a folder), then click \"Makes it's own skeleton\" in the dialog )\n"); + iFaults++; + } + } + + // + // validate all sequences within this model... + // + int iGLACount = 0; + int iXSICount = 0; + CSequence* curSequence = curModel->GetFirstSequence(); + while (curSequence) + { + // we'll need to check these counts after checking all sequences... + // + if (curSequence->IsGLA()) + { + iGLACount++; + } + else + { + iXSICount++; + } + + #define SEQREPORT CString temp;temp.Format("Sequence \"%s\":",curSequence->GetName());while (temp.GetLength()<35)temp+=" "; + + // check 1, does the ASE file exist? (actually this is talking about XSIs/GLAs, but WTF... + // + CString nameASE = ((CAssimilateApp*)AfxGetApp())->GetQuakeDir(); + nameASE+= curSequence->GetPath(); + + Filename_AccountForLOD(nameASE, iLODLevel); + + const int iNameSpacing=35; + + if (!FileExists(nameASE)) + { + if (!gbCarWash_DoingScan) // because this will only duplicate reports otherwise + { + SEQREPORT; + temp += va(" ASE/XSI/GLA file not found: \"%s\"\n",nameASE); + fprintf(hFile,temp); + iFaults++; + } + } + + // new check, if this is an LOD ASE it must have the same framecount as the base (non-LOD) version... + // + if (iLODLevel) + { + // read this file's framecount... + // + int iStartFrame, iFrameCount, iFrameSpeed; + curSequence->ReadASEHeader( nameASE, iStartFrame, iFrameCount, iFrameSpeed); + + // read basefile's framecount... + // + CString baseASEname = ((CAssimilateApp*)AfxGetApp())->GetQuakeDir(); + baseASEname+= curSequence->GetPath(); + + int iStartFrameBASE, iFrameCountBASE, iFrameSpeedBASE; + curSequence->ReadASEHeader( baseASEname, iStartFrameBASE, iFrameCountBASE, iFrameSpeedBASE); + + // same?... + // + if (iFrameCount != iFrameCountBASE) + { + SEQREPORT; + temp += va(" (SERIOUS ERROR) base ASE has %d frames, but this LOD version has %d frames!\n",iFrameCountBASE,iFrameCount); + fprintf(hFile,temp); + iFaults++; + } + } + + // check 2, is the loopframe higher than the framecount of that sequence?... + // + if (curSequence->GetLoopFrame() >= curSequence->GetFrameCount()) + { + SEQREPORT; + temp += va(" loopframe %d is illegal, max = %d\n",curSequence->GetLoopFrame(),curSequence->GetFrameCount()-1); + fprintf(hFile,temp); + iFaults++; + } + + if (!curModel->IsGhoul2()) + { + // check 3, is the enum valid?... + // + if (curSequence->GetEnumType() == ET_INVALID) + { + SEQREPORT; + temp += va(" invalid animation enum \"%s\"\n",curSequence->GetEnum()); + fprintf(hFile,temp); + iFaults++; + } + } + + int iEnumUsageCount = curModel->AnimEnumInUse(curSequence->GetEnum()); + if (iEnumUsageCount>1) + { + SEQREPORT; + temp += va(" animation enum \"%s\" is used %d times\n",curSequence->GetEnum(),iEnumUsageCount); + fprintf(hFile,temp); + iFaults++; + } + + // a whole bunch of checks for the additional sequences... + // + if (!curSequence->IsGLA()) + { + for (int i=0; iAdditionalSeqs[i]; + + #define ADDITIONALSEQREPORT CString temp;temp.Format("Sequence \"%s\": (Additional: \"%s\"):",curSequence->GetName(),additionalSeq->GetEnum());while (temp.GetLength()<60)temp+=" "; + + if (additionalSeq->AdditionalSequenceIsValid()) + { + // check for duplicate enum names... + // + int iEnumUsageCount = curModel->AnimEnumInUse(additionalSeq->GetEnum()); + if (iEnumUsageCount>1) + { + ADDITIONALSEQREPORT; + temp += va(" animation enum \"%s\" is used %d times\n",additionalSeq->GetEnum(),iEnumUsageCount); + fprintf(hFile,temp); + iFaults++; + } + + // additional sequences must actually have some frames... + // + if (additionalSeq->GetFrameCount()<=0) + { + ADDITIONALSEQREPORT; + temp += va(" a frame count of %d is illegal (min = 1)\n",additionalSeq->GetFrameCount()); + fprintf(hFile,temp); + iFaults++; + } + else + { + // the start/count range of this additional seq can't exceed it's master... + // + if (additionalSeq->GetStartFrame() + additionalSeq->GetFrameCount() > curSequence->GetFrameCount()) + { + ADDITIONALSEQREPORT; + temp += va(" illegal start/count range of %d..%d exceeds master range of %d..%d\n", + additionalSeq->GetStartFrame(), + additionalSeq->GetStartFrame() + additionalSeq->GetFrameCount(), + curSequence->GetStartFrame(), + curSequence->GetStartFrame() + curSequence->GetFrameCount() + ); + fprintf(hFile,temp); + iFaults++; + } + else + { + // loopframe of an additional seq must be within its own seq framecount... + // + if (additionalSeq->GetLoopFrame() >= additionalSeq->GetFrameCount()) + { + ADDITIONALSEQREPORT; + temp += va(" loopframe %d is illegal, max is %d\n",additionalSeq->GetLoopFrame(),additionalSeq->GetFrameCount()-1); + fprintf(hFile,temp); + iFaults++; + } + } + } + } + else + { + // is this additional sequence invalid because of being just empty or being bad?... + // + if (strlen(additionalSeq->GetEnum())) + { + // it's a bad sequence (probably because of its enum being deleted from anims.h since it was saved) + // + ADDITIONALSEQREPORT; + temp += va(" this animation enum no longer exists in \"%s\"\n",sDEFAULT_ENUM_FILENAME); + fprintf(hFile,temp); + iFaults++; + } + } + } + } + curSequence = curSequence->GetNext(); + } + + // special GLA/XSI checks... + // + { + if (iGLACount>1) + { + fprintf(hFile, "Model has more than one GLA file specified\n"); + iFaults++; + } + + if (iGLACount && iXSICount) + { + fprintf(hFile, "Model has both GLA and XSI files specified. Pick one method or the other\n"); + iFaults++; + } + + if (iGLACount && (curModel->GetMakeSkelPath() && strlen(curModel->GetMakeSkelPath())) ) + { + fprintf(hFile, "Model has both a GLA sequence and a '-makeskel' path, this is meaningless\n"); + iFaults++; + } + + /* + if (iGLACount && (curModel->GetSkelPath() && strlen(curModel->GetSkelPath())) ) + { + fprintf(hFile, "Model has both a GLA sequence and a '-skel' path, you should probably blank out the 'skel' path\n"); + iFaults++; + } + */ + } + + if (iThisModelFaults == iFaults) + { + fprintf(hFile,"(ok)\n"); // just to be nice if reporting on >1 model... + } + else + { + fprintf(hFile,"\n(%d faults)\n",iFaults-iThisModelFaults); + } + + curModel = curModel->GetNext(); + if (!bValidateAll) + break; + + }// while (curModel) + + fclose(hFile); + + if (iFaults) + { + // now run notepad.exe on the file we've just created... + // + CString sExecString; + + sExecString.Format("notepad %s",sOutputTextFile); + + if (WinExec(sExecString, // LPCSTR lpCmdLine, // address of command line + SW_SHOWNORMAL // UINT uCmdShow // window style for new application + ) + >31 // don't ask me, Windoze just uses >31 as OK in this call. + ) + { + // ok. + } + else + { + ErrorBox("Unable to locate/run NOTEPAD on this machine!\n\n(let me know about this -Ste)"); + } + } + else + { + if (bInfoBoxAllowed) + { + InfoBox("Everything ok\n\n( All files exist, enums exist, and frames seem to be valid ranges )"); + } + } + }// if (hFile) + else + { + ErrorBox(va("Arrgh! Unable to create file '%s'!\n\n(let me know about this -Ste)",sOutputTextFile)); + } + + }// if (iNumModels) + + EndWait(); + + return !iFaults; + +} + + +void CAssimilateDoc::OnUpdateResequence(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(!!GetNumModels()); +} + +void CAssimilateDoc::OnUpdateFileSave(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(!!GetNumModels()); +} + +void CAssimilateDoc::OnUpdateFileSaveAs(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(!!GetNumModels()); +} + +void CAssimilateDoc::OnUpdateExternal(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(!!GetNumModels()); +} + +void CAssimilateDoc::OnUpdateValidate(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(!!GetNumModels()); +} + +void CAssimilateDoc::OnUpdateBuild(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(!!GetNumModels()); +} + + + +BOOL CAssimilateDoc::DoFileSave() +{ + // sourcesafe stuff + { + /////////////////////////////////////////// + // + // some stuff so I can leave the code below untouched... + // + LPCSTR filename = (LPCSTR) m_strPathName; + #define Sys_Printf(blah) StatusText(blah) + // + /////////////////////////////////////////// + // + // check it out first, if necessary... + // + if ( SS_FunctionsAvailable() ) + { + if ( SS_IsUnderSourceControl( filename ) ) + { + if ( SS_IsCheckedOut( filename )) + { + if ( !SS_IsCheckedOutByMe( filename )) + { + CString strCheckOuts; + int iCount; + + if (SS_ListCheckOuts( filename, strCheckOuts, iCount )) + { + ErrorBox( va("File \"%s\" is checked out by:\n\n%s\n... so you can't save over it...\n\n... so you can't compile...\n\nTough luck matey!....(bwahahahaha!!!!!)",filename,(LPCSTR) strCheckOuts)); + return false; + } + } + else + { + Sys_Printf ("(You own this file under SourceSafe)\n"); + } + } + else + { + if ( GetYesNo( va("The file \"%s\"\n\n...needs to be checked out so I can save over it\n\nProceed? ('No' will abort the save)",filename) )) + { + if (SS_CheckOut( filename )) + { + Sys_Printf ("(File checked out ok)\n"); + } + else + { + ASSERT(0); // I want to know if this ever happens + Sys_Printf ("(Error during file checkout, aborting save\n"); + return false; + } + } + else + { + Sys_Printf ("(Checkout cancelled, aborting save\n"); + return false; + } + } + } + else + { + Sys_Printf ("(This file is not under SourceSafe control)\n"); + } + } + + // now do seperate check for files that are still write-protected... + // + DWORD dw = GetFileAttributes( filename ); + + if (dw != 0xFFFFFFFF && ( dw & FILE_ATTRIBUTE_READONLY )) + { + // hmmm, still write protected... + // + if (SS_SetupOk()) + { + if (GetYesNo( va("The file \"%s\" is write-protected, but probably not because of SourceSafe, just as a safety thing.\n\n(Tell me if you believe this is wrong -Ste)\n\nDo you want me to un-writeprotect it so you can save over it? ('No' will abort the save)",filename ))) + { + if ( !SetFileAttributes( filename, dw&~FILE_ATTRIBUTE_READONLY) ) + { + ErrorBox("Failed to remove write protect, aborting..."); + return false; + } + } + else + { + Sys_Printf ("(File was not write-enabled, aborting save)"); + return false; + } + } + else + { + ErrorBox( va("The file \"%s\" is write-protected, but you don't appear to have SourceSafe set up properly on this machine, so I can't tell if the file is protected or just not checked out to you.\n\nIf you really want to edit this you'll have to write-enable it yourself (which I'm deliberately not offering to do for you here )",filename)); + } + } + } + + BOOL b = CDocument::DoFileSave(); + + if (b == TRUE) + { + // sourcesafe + LPCSTR filename = (LPCSTR) m_strPathName; + #define Sys_Printf(blah) StatusText(blah) + if ( SS_FunctionsAvailable() ) + { + if ( SS_IsUnderSourceControl( filename )) + { + if ( SS_IsCheckedOutByMe( filename )) + { +// if ( SS_CheckIn( filename )) +// { +// Sys_Printf("(Checked in ok)\n"); +// } +// else +// { +// Sys_Printf("Error during CheckIn\n"); +// } + } + else + { + ErrorBox( va("You do not have file \"%s\" checked out",filename)); + } + } + else + { + // new bit, if it wasn't under SourceSafe, then ask if they want to add it... + // + if (GetYesNo(va("File \"%s\" is not under SourceSafe control, add to database?",filename))) + { + if ( SS_Add( filename )) + { + Sys_Printf("(File was added to SourceSafe Ok)\n"); + + // check it out as well... + // + if (SS_CheckOut( filename )) + { + Sys_Printf ("(File checked out ok)\n"); + } + else + { + ASSERT(0); // I want to know if this ever happens + Sys_Printf ("( Error during file checkout! )\n"); + } + } + else + { + ErrorBox( va("Error adding file \"%s\" to SourceSafe",filename)); + } + } + } + } + } + + StatusText(NULL); + + return b; +} + +BOOL CAssimilateDoc::OnSaveDocument(LPCTSTR lpszPathName) +{ + CString strFileName = lpszPathName; + + Filename_AccountForLOD(strFileName, giLODLevelOverride); // this is actually junk now, should lose all this LODoverride stuff + + return CDocument::OnSaveDocument(strFileName); +} + + +// remember these two from session to session, maybe write to registry sometime?... +// +bool gbPreValidate = true; +CString strInitialBuildPath = "models/players"; +void CAssimilateDoc::OnEditBuildDependant() +{ + CModel* curModel = m_modelList; + if (curModel) + { + CString strStartDir = ((CAssimilateApp*)AfxGetApp())->GetQuakeDir(); + + if (!strStartDir.GetLength()) + { + // should never happen... + // + ErrorBox("Base path not known at this point. Prefs not setup?"); + return; + } + + LPCSTR psCurrentGLAName = curModel->GetMakeSkelPath(); + + if (psCurrentGLAName) + { + char sCurrentGLAName[1024]; + strcpy(sCurrentGLAName,psCurrentGLAName); + strMustContainThisGLA = sCurrentGLAName; + CBuildAll dlgBuildAll(strInitialBuildPath, gbPreValidate); + + if (dlgBuildAll.DoModal() == IDOK) + { + dlgBuildAll.GetData(strInitialBuildPath, gbPreValidate); + + strStartDir += strInitialBuildPath; + strStartDir.Replace("/","\\"); + while (strStartDir.Replace("\\\\","\\")){} + + if (gbPreValidate) + { + gbQueryGoAhead = false; + gpsCARWashDirOverride = (LPCSTR) strStartDir; + + OnCarWashActual(); + gbQueryGoAhead = true; + gpsCARWashDirOverride = NULL; + + if (gbCarwashErrorsOccured) + { + InfoBox("Build-All aborted because of errors"); + return; + } + } + + ((CMainFrame*)AfxGetMainWnd())->StatusMessage("Validated Ok, building..."); + + //////////////////////// largely block-pasted from CarWash.... :-) + + CWaitCursor wait; + + strCARsFound.Empty(); + iCARsFound = 0; + strSkippedDirs.Empty(); + strSkippedFiles.Empty(); + + // build up a list... + // + R_CheckCARs( strStartDir, 0, strMustContainThisGLA); //bool bBuildListOnly + AlphaSortCARs(); // important to do them in alpha-order during build, because of "_humanoid" - type dirs. + ((CMainFrame*)AfxGetMainWnd())->StatusMessage("Ready"); + + // ok, now ready to begin pass 2... + // + CString strReport; + if (!iCARsFound) + { + ASSERT(0); + strReport = "No suitable .CAR files found for processing!\n\n"; + + if (!strSkippedDirs.IsEmpty()) + { + strReport+= "Skipped Dirs:\n\n"; + strReport+= strSkippedDirs; + } + + if (!strSkippedFiles.IsEmpty()) + { + strReport+= "\n\nSkipped Files:\n\n"; + strReport+= strSkippedFiles; + } + ErrorBox(strReport); + } + else + { + //---------------- + gbCarWash_DoingScan = true; + strCarWashErrors.Empty(); + //---------------- + + CString strTotalErrors; + + strReport = "Processed files:\n\n"; + for (int i=0; i=0) + { + strThisFile = strThisFile.Left(iLoc); + strCARsFound= strCARsFound.Mid(iLoc+1); + + strReport += strThisFile + "\n"; + + ((CMainFrame*)AfxGetMainWnd())->StatusMessage(va("Scanning File %d/%d: %s",i+1,iCARsFound,(LPCSTR)strThisFile)); + + if (1)//FileUsesGLAReference(strThisFile, sCurrentGLAName)) + { + OnNewDocument(); + if (OnOpenDocument_Actual(strThisFile, false)) + { + if (gbParseError) + { + strTotalErrors += va("\nParse error in file \"%s\"\n",(LPCSTR)strThisFile); + break; + } + else + { + m_strPathName = strThisFile; // fucking stupid MFC doesn't set this!!!!!!!!!!!!! + bool bSuccess = Build( false, // bool bAllowedToShowSuccessBox, + 0, // int iLODLevel + true // bool bSkipSave + ); + + if (!strCarWashErrors.IsEmpty()) + { + // "something is wrong..." :-) + // + strTotalErrors += va("\nError in file \"%s\":\n\n%s\n",strThisFile,strCarWashErrors); + } + strCarWashErrors.Empty(); + + if (!bSuccess) + break; + } + } + else + { + strTotalErrors += va("\nUnable to open file \"%s\"\n",(LPCSTR)strThisFile); + break; + } + } + else + { + // this CAR file doesn't use the current GLA name, so ignore it + } + } + else + { + ASSERT(0); + strThisFile.Insert(0,"I fucked up, the following line didn't seem to have a CR: (tell me! -Ste)\n\n"); + ErrorBox(strThisFile); + } + } + + //---------------- + gbCarWash_DoingScan = false; + //---------------- + + + OnNewDocument(); // trash whatever was loaded last + + // strReport = "Processed files:\n\n"; + // strReport+= strCARsFound; + strReport+= "\n\nSkipped Dirs:\n\n"; + strReport+= strSkippedDirs; + strReport+= "\n\nSkipped Files:\n\n"; + strReport+= strSkippedFiles; + + if (strTotalErrors.IsEmpty()) + { + strReport.Insert(0,"(No additional errors found)\n\n"); + } + else + { + strReport+= "\n\nAdditional errors will now be sent to Notepad!..."; + } + InfoBox(strReport); + + if (!strTotalErrors.IsEmpty()) + { + strTotalErrors.Insert(0, "The following errors occured during build...\n\n"); + SendToNotePad(strTotalErrors, "build_errors.txt"); + } + } + } + } + else + { + ErrorBox("Currently loaded model does not make a skeleton, so there are no dependants\n\n\nDUH!!"); + } + } + else + { + ErrorBox("No model loaded to build dependants of!\n\n\nDUH!!"); + } + + ((CMainFrame*)AfxGetMainWnd())->StatusMessage("Ready"); +} + +void CAssimilateDoc::OnEditBuildall() +{ +// if (!GetYesNo("Safety Feature: Do you really want to rebuild every CAR file in the whole game?")) +// return; + + // validity-check... + // + CString strStartDir = ((CAssimilateApp*)AfxGetApp())->GetQuakeDir(); + + if (!strStartDir.GetLength()) + { + ErrorBox("Base path not known at this point. Prefs not setup?"); + return; + } + + CBuildAll dlgBuildAll(strInitialBuildPath, gbPreValidate); + + if (dlgBuildAll.DoModal() == IDOK) + { + dlgBuildAll.GetData(strInitialBuildPath, gbPreValidate); + + strStartDir += strInitialBuildPath; + strStartDir.Replace("/","\\"); + while (strStartDir.Replace("\\\\","\\")){} + + if (gbPreValidate) + { + gbQueryGoAhead = false; + gpsCARWashDirOverride = (LPCSTR) strStartDir; + OnCarWash(); + gbQueryGoAhead = true; + gpsCARWashDirOverride = NULL; + + if (gbCarwashErrorsOccured) + { + InfoBox("Build-All aborted because of errors"); + return; + } + } + + ((CMainFrame*)AfxGetMainWnd())->StatusMessage("Validated Ok, building..."); + + //////////////////////// largely block-pasted from CarWash.... :-) + + CWaitCursor wait; + + strCARsFound.Empty(); + iCARsFound = 0; + strSkippedDirs.Empty(); + strSkippedFiles.Empty(); + + // build up a list... + // + R_CheckCARs( strStartDir, 0, "" ); //bool bBuildListOnly + AlphaSortCARs(); // important to do them in alpha-order during build, because of "_humanoid" - type dirs. + ((CMainFrame*)AfxGetMainWnd())->StatusMessage("Ready"); + + // ok, now ready to begin pass 2... + // + CString strReport; + if (!iCARsFound) + { + ASSERT(0); + strReport = "No suitable .CAR files found for processing!\n\n"; + + if (!strSkippedDirs.IsEmpty()) + { + strReport+= "Skipped Dirs:\n\n"; + strReport+= strSkippedDirs; + } + + if (!strSkippedFiles.IsEmpty()) + { + strReport+= "\n\nSkipped Files:\n\n"; + strReport+= strSkippedFiles; + } + ErrorBox(strReport); + } + else + { + //---------------- + gbCarWash_DoingScan = true; + strCarWashErrors.Empty(); + //---------------- + + CString strTotalErrors; + + strReport = "Processed files:\n\n"; + for (int i=0; i=0) + { + strThisFile = strThisFile.Left(iLoc); + strCARsFound= strCARsFound.Mid(iLoc+1); + + strReport += strThisFile + "\n"; + + ((CMainFrame*)AfxGetMainWnd())->StatusMessage(va("Scanning File %d/%d: %s",i+1,iCARsFound,(LPCSTR)strThisFile)); + + OnNewDocument(); + if (OnOpenDocument_Actual(strThisFile, false)) + { + if (gbParseError) + { + strTotalErrors += va("\nParse error in file \"%s\"\n",(LPCSTR)strThisFile); + break; + } + else + { + m_strPathName = strThisFile; // fucking stupid MFC doesn't set this!!!!!!!!!!!!! + bool bSuccess = Build( false, // bool bAllowedToShowSuccessBox, + 0, // int iLODLevel + true // bool bSkipSave + ); + + if (!strCarWashErrors.IsEmpty()) + { + // "something is wrong..." :-) + // + strTotalErrors += va("\nError in file \"%s\":\n\n%s\n",strThisFile,strCarWashErrors); + } + strCarWashErrors.Empty(); + + if (!bSuccess) + break; + } + } + else + { + strTotalErrors += va("\nUnable to open file \"%s\"\n",(LPCSTR)strThisFile); + break; + } + } + else + { + ASSERT(0); + strThisFile.Insert(0,"I fucked up, the following line didn't seem to have a CR: (tell me! -Ste)\n\n"); + ErrorBox(strThisFile); + } + } + + //---------------- + gbCarWash_DoingScan = false; + //---------------- + + + OnNewDocument(); // trash whatever was loaded last + + // strReport = "Processed files:\n\n"; + // strReport+= strCARsFound; + strReport+= "\n\nSkipped Dirs:\n\n"; + strReport+= strSkippedDirs; + strReport+= "\n\nSkipped Files:\n\n"; + strReport+= strSkippedFiles; + + if (strTotalErrors.IsEmpty()) + { + strReport.Insert(0,"(No additional errors found)\n\n"); + } + else + { + strReport+= "\n\nAdditional errors will now be sent to Notepad!..."; + } + InfoBox(strReport); + + if (!strTotalErrors.IsEmpty()) + { + strTotalErrors.Insert(0, "The following errors occured during build...\n\n"); + SendToNotePad(strTotalErrors, "build_errors.txt"); + } + } + } + + ((CMainFrame*)AfxGetMainWnd())->StatusMessage("Ready"); +} + + + +void SS_DisposingOfCurrent(LPCSTR psFileName, bool bDirty) +{ + if (psFileName[0]) + { + LPCSTR filename = psFileName; // compile laziness + #undef Sys_Printf + #define Sys_Printf(blah) + if ( SS_FunctionsAvailable() ) + { + if ( SS_IsUnderSourceControl( filename ) ) + { + if ( SS_IsCheckedOutByMe( filename )) + { + if (bDirty) + { + // if 'need_save' then the user has clicked ok-to-lose-changes, so... + // + if ( GetYesNo( va("Since you've decided to lose changes on the file:\n\n\"%s\"\n\n...do you want to Undo Checkout as well?",filename))) + { + if (SS_UndoCheckOut( filename )) + { + Sys_Printf ("(Undo Checkout performed on map)\n"); + } + else + { + ErrorBox("Undo Checkout failed!\n"); + } + } + } + else + { + // if !'need_save' here then the user has saved this out already, so prompt for check in... + // + if ( GetYesNo( va("Since you've finished with the file:\n\n\"%s\"\n\n...do you want to do a Check In?",filename))) + { + if ( SS_CheckIn( filename )) + { + //Sys_Printf ("(CheckIn performed on map)\n"); + } + else + { + ErrorBox("CheckIn failed!\n"); + } + } + } + } + } + } + } +} + + + +BOOL CAssimilateDoc::OnOpenDocument_Actual(LPCTSTR lpszPathName, bool bCheckOut) +{ + SS_DisposingOfCurrent(m_strPathName, !!IsModified()); + + if (bCheckOut) + { + // checkout the new file? + LPCSTR filename = lpszPathName; // compile-laziness :-) + if ( SS_FunctionsAvailable() ) + { + if ( SS_IsUnderSourceControl( filename ) ) + { + if ( SS_IsCheckedOut( filename )) + { + if ( !SS_IsCheckedOutByMe( filename )) + { + CString strCheckOuts; + int iCount; + + if (SS_ListCheckOuts( filename, strCheckOuts, iCount )) + { + if (!GetYesNo( va("Warning: File \"%s\" is checked out by:\n\n%s\n... Continue loading? ",filename,(LPCSTR) strCheckOuts))) + { + return FALSE; + } + } + } + else + { + //InfoBox ("(You own this file under SourceSafe)\n"); + } + } + else + { + if ( GetYesNo( va("The file \"%s\"\n\n...is under SourceSafe control, check it out now?",filename) )) + { + if (SS_CheckOut( filename )) + { + //InfoBox ("(File checked out ok)\n"); + } + else + { + WarningBox( va("( Problem encountered during check out of file \"%s\" )",filename) ); + } + } + } + } + else + { + //InfoBox ("(This file is not under SourceSafe control)\n"); + } + } + } + + if (!CDocument::OnOpenDocument(lpszPathName)) + return FALSE; + + return TRUE; +} + + +BOOL CAssimilateDoc::OnOpenDocument(LPCTSTR lpszPathName) +{ + return OnOpenDocument_Actual(lpszPathName, true) ; +} + +void CAssimilateDoc::OnCloseDocument() +{ + SS_DisposingOfCurrent(m_strPathName, !!IsModified()); + + CDocument::OnCloseDocument(); +} + +void CAssimilateDoc::OnViewFramedetailsonadditionalsequences() +{ + gbViewFrameDetails_Additional = !gbViewFrameDetails_Additional; + UpdateAllViews(NULL, AS_FILESUPDATED, NULL); +} + +void CAssimilateDoc::OnUpdateViewFramedetailsonadditionalsequences(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(gbViewFrameDetails_Additional); +} + +void CAssimilateDoc::OnUpdateEditBuilddependant(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(!!GetNumModels()); +} + + + +bool RunApp(LPCSTR psAppCommand) +{ + CString strExec = psAppCommand; // eg "start q:\\bin_nt\\behaved.exe"; + strExec.Replace("/","\\"); // otherwise it only works under NT... + + char sBatchFilename[512]; + + GetTempPath(sizeof(sBatchFilename), sBatchFilename); + strcat(sBatchFilename,"~temp.bat"); + + FILE *handle = fopen(sBatchFilename,"wt"); + fprintf(handle,strExec); + fprintf(handle,"\n"); + fclose(handle); + + STARTUPINFO startupinfo; + PROCESS_INFORMATION ProcessInformation; + + GetStartupInfo (&startupinfo); + + BOOL ret = CreateProcess(sBatchFilename, + //batpath, // pointer to name of executable module + NULL, // pointer to command line string + NULL, // pointer to process security attributes + NULL, // pointer to thread security attributes + FALSE, // handle inheritance flag + 0 /*DETACHED_PROCESS*/, // creation flags + NULL, // pointer to new environment block + NULL, // pointer to current directory name + &startupinfo, // pointer to STARTUPINFO + &ProcessInformation // pointer to PROCESS_INFORMATION + ); +// remove(sBatchFilename); // if you do this, the CreateProcess fails, presumably it needs it for a few seconds + + return !!ret; +} + + +void CAssimilateDoc::OnEditLaunchmodviewoncurrent() +{ + char sExecString[MAX_PATH]; + + sprintf(sExecString,"start %s.glm",Filename_WithoutExt(m_strPathName)); + + if (RunApp(sExecString)) + { + // ok... + // + } + else + { + ErrorBox(va("CreateProcess() call \"%s\" failed!\n\n(let me know about this -Ste)",sExecString)); + } +} + +void CAssimilateDoc::OnUpdateEditLaunchmodviewoncurrent(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(!!GetNumModels()); +} diff --git a/utils/Assimilate/AssimilateDoc.h b/utils/Assimilate/AssimilateDoc.h new file mode 100644 index 0000000..73fe46b --- /dev/null +++ b/utils/Assimilate/AssimilateDoc.h @@ -0,0 +1,184 @@ +// AssimilateDoc.h : interface of the CAssimilateDoc class +// +///////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_ASSIMILATEDOC_H__2CCA554A_2AD3_11D3_82E0_0000C0366FF2__INCLUDED_) +#define AFX_ASSIMILATEDOC_H__2CCA554A_2AD3_11D3_82E0_0000C0366FF2__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +enum +{ + TK_AS_GRABINIT = TK_USERDEF, + TK_AS_GRAB, + TK_AS_GRAB_GLA, + TK_AS_FILL, + TK_AS_ACTION, + TK_AS_SOUND, + TK_AS_ENUM, + TK_AS_GRABFINALIZE, + TK_AS_CONVERT, + TK_AS_CONVERTMDX, + TK_AS_CONVERTMDX_NOASK, + TK_AS_FRAMES, + TK_AS_PLAYERPARMS, + TK_AS_SCALE, + TK_AS_PCJ, + TK_AS_KEEPMOTION, + TK_AS_ORIGIN, + TK_AS_SMOOTH, + TK_AS_LOSEDUPVERTS, + TK_AS_MAKESKIN, + TK_AS_IGNOREBASEDEVIATIONS, + TK_AS_SKEW90, + TK_AS_NOSKEW90, + TK_AS_SKEL, + TK_AS_MAKESKEL, + TK_AS_LOOP, + TK_AS_ADDITIONAL, + TK_AS_PREQUAT, + TK_AS_FRAMESPEED, + TK_AS_QDSKIPSTART, // useful so qdata can quickly skip extra stuff without having to know the syntax of what to skip + TK_AS_QDSKIPSTOP, + TK_AS_GENLOOPFRAME, + TK_DOLLAR, + TK_UNDERSCORE, + TK_DASH, + TK_DOT, + TK_SLASH, + TK_BACKSLASH, +}; + +enum +{ + AS_NOTHING, + AS_NEWFILE, + AS_DELETECONTENTS, + AS_FILESUPDATED, +}; + +enum +{ + TABLE_ANY, + TABLE_QDT, + TABLE_GRAB, + TABLE_CONVERT, +}; + +class CAssimilateDoc : public CDocument +{ +protected: // create from serialization only + CAssimilateDoc(); + DECLARE_DYNCREATE(CAssimilateDoc) + +// Attributes +public: + static LPCTSTR GetKeyword(int token, int table = 0); + +// Operations +public: + + int GetNumModels(); + CModel* GetFirstModel(); + void Resequence(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CAssimilateDoc) + public: + virtual BOOL OnNewDocument(); + virtual void Serialize(CArchive& ar); + virtual void DeleteContents(); + virtual BOOL DoFileSave(); + virtual BOOL OnSaveDocument(LPCTSTR lpszPathName); + virtual BOOL OnOpenDocument(LPCTSTR lpszPathName); + virtual BOOL OnOpenDocument_Actual(LPCTSTR lpszPathName, bool bCheckOut) ; + virtual void OnCloseDocument(); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CAssimilateDoc(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + + void ClearModelUserSelectionBools(); + CModel *GetCurrentUserSelectedModel(); + void DeleteModel(CModel *deleteModel); + +protected: + void Init(); + + void Parse(CFile* file); + void Parse(LPCSTR psFilename); + void Write(CFile* file); + + void AddFile(LPCTSTR name); + CModel* AddModel(); + void EndModel(); + void ParseGrab(CTokenizer* tokenizer, int iGrabType); + void ParseConvert(CTokenizer* tokenizer, int iTokenType); + void AddComment(LPCTSTR comment); + bool Validate(bool bInfoBoxAllowed = false, int iLODLevel = 0); + bool Build(bool bAllowedToShowSuccessBox, int iLODLevel, bool bSkipSave); + bool WriteCFGFiles(bool bPromptForNames, bool &bCFGWritten); + + CComment* m_comments; + CModel* m_modelList; + CModel* m_curModel; // fixme: update this in either usage or label to say only Mike's code uses it during loading, not elsewhere + CModel* m_lastModel; + + static keywordArray_t s_Symbols[]; + static keywordArray_t s_Keywords[]; + static keywordArray_t s_grabKeywords[]; + static keywordArray_t s_convertKeywords[]; + +// Generated message map functions +protected: + //{{AFX_MSG(CAssimilateDoc) + afx_msg void OnAddfiles(); + afx_msg void OnExternal(); + afx_msg void OnResequence(); + afx_msg void OnBuild(); + afx_msg void OnBuildMultiLOD(); + afx_msg void OnValidate(); + afx_msg void OnCarWash(); + afx_msg void OnCarWashActual(); + afx_msg void OnValidateMultiLOD(); + afx_msg void OnViewAnimEnums(); + afx_msg void OnUpdateViewAnimEnums(CCmdUI* pCmdUI); + afx_msg void OnViewFrameDetails(); + afx_msg void OnUpdateViewFrameDetails(CCmdUI* pCmdUI); + afx_msg void OnUpdateResequence(CCmdUI* pCmdUI); + afx_msg void OnUpdateFileSave(CCmdUI* pCmdUI); + afx_msg void OnUpdateFileSaveAs(CCmdUI* pCmdUI); + afx_msg void OnUpdateExternal(CCmdUI* pCmdUI); + afx_msg void OnUpdateValidate(CCmdUI* pCmdUI); + afx_msg void OnUpdateBuild(CCmdUI* pCmdUI); + afx_msg void OnEditBuildall(); + afx_msg void OnEditBuildDependant(); + afx_msg void OnViewFramedetailsonadditionalsequences(); + afx_msg void OnUpdateViewFramedetailsonadditionalsequences(CCmdUI* pCmdUI); + afx_msg void OnUpdateEditBuilddependant(CCmdUI* pCmdUI); + afx_msg void OnEditLaunchmodviewoncurrent(); + afx_msg void OnUpdateEditLaunchmodviewoncurrent(CCmdUI* pCmdUI); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +extern bool gbCarWash_YesToXSIScan; +extern bool gbCarWash_DoingScan; +extern CString strCarWashErrors; +bool SendToNotePad(LPCSTR psWhatever, LPCSTR psLocalFileName); + + +#endif // !defined(AFX_ASSIMILATEDOC_H__2CCA554A_2AD3_11D3_82E0_0000C0366FF2__INCLUDED_) diff --git a/utils/Assimilate/AssimilateView.cpp b/utils/Assimilate/AssimilateView.cpp new file mode 100644 index 0000000..db5fa9a --- /dev/null +++ b/utils/Assimilate/AssimilateView.cpp @@ -0,0 +1,617 @@ +// AssimilateView.cpp : implementation of the CAssimilateView class +// + +#include "stdafx.h" +#include "Includes.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +CAssimilateView* ghAssimilateView = NULL; + +///////////////////////////////////////////////////////////////////////////// +// CAssimilateView + +IMPLEMENT_DYNCREATE(CAssimilateView, CTreeView) + +BEGIN_MESSAGE_MAP(CAssimilateView, CTreeView) + //{{AFX_MSG_MAP(CAssimilateView) + ON_NOTIFY_REFLECT(NM_DBLCLK, OnDblclk) + ON_WM_DESTROY() + ON_NOTIFY_REFLECT(TVN_ITEMEXPANDED, OnItemexpanded) + ON_WM_RBUTTONDBLCLK() + ON_WM_KEYDOWN() + ON_NOTIFY_REFLECT(TVN_SELCHANGED, OnSelchanged) + //}}AFX_MSG_MAP + // Standard printing commands + ON_COMMAND(ID_FILE_PRINT, CTreeView::OnFilePrint) + ON_COMMAND(ID_FILE_PRINT_DIRECT, CTreeView::OnFilePrint) + ON_COMMAND(ID_FILE_PRINT_PREVIEW, CTreeView::OnFilePrintPreview) +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CAssimilateView construction/destruction + +CAssimilateView::CAssimilateView() +{ + ghAssimilateView = this; +} + +CAssimilateView::~CAssimilateView() +{ +} + +BOOL CAssimilateView::PreCreateWindow(CREATESTRUCT& cs) +{ + // TODO: Modify the Window class or styles here by modifying + // the CREATESTRUCT cs + + if (!CTreeView::PreCreateWindow(cs)) + { + return FALSE; + } + cs.style |= TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS | TVS_SHOWSELALWAYS/* | TVS_EDITLABELS*/; + return TRUE; +} + +///////////////////////////////////////////////////////////////////////////// +// CAssimilateView drawing + +void CAssimilateView::OnDraw(CDC* pDC) +{ + CAssimilateDoc* pDoc = GetDocument(); + ASSERT_VALID(pDoc); + // TODO: add draw code for native data here +} + +///////////////////////////////////////////////////////////////////////////// +// CAssimilateView printing + +BOOL CAssimilateView::OnPreparePrinting(CPrintInfo* pInfo) +{ + // default preparation + return DoPreparePrinting(pInfo); +} + +void CAssimilateView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) +{ + // TODO: add extra initialization before printing +} + +void CAssimilateView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) +{ + // TODO: add cleanup after printing +} + +///////////////////////////////////////////////////////////////////////////// +// CAssimilateView diagnostics + +#ifdef _DEBUG +void CAssimilateView::AssertValid() const +{ + CTreeView::AssertValid(); +} + +void CAssimilateView::Dump(CDumpContext& dc) const +{ + CTreeView::Dump(dc); +} + +CAssimilateDoc* CAssimilateView::GetDocument() // non-debug version is inline +{ + ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CAssimilateDoc))); + return (CAssimilateDoc*)m_pDocument; +} +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// CAssimilateView message handlers + +void CAssimilateView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) +{ + // TODO: Add your specialized code here and/or call the base class + CTreeCtrl& tree = GetTreeCtrl(); + + switch(lHint) + { + case AS_DELETECONTENTS: + tree.DeleteAllItems(); + break; + case AS_NEWFILE: + // findme: hmmm, not sure about this. Adding files zaps the tree? Would look odd to user... + tree.DeleteAllItems(); + BuildTree(); + UpdateTree(); + break; + case AS_FILESUPDATED: + UpdateTree(); // sorts tree according to enum names, sorts model according to new tree, re-draws tree + break; + default: + break; + } + + SetFirstModelTitleAndMode(); +} + + + +// in practise, the Assimilate app will only ever use one sort type, but wtf... +// +typedef enum +{ + TS_BY_ENUMTYPE = 0, +} TREESORTTYPE; + +// +// tree item compare callback, +// +// returns (to windows):- (so do NOT change these defines) +// +#define ITEM1_BEFORE_ITEM2 -1 +#define ITEM1_MATCHES_ITEM2 0 +#define ITEM1_AFTER_ITEM2 1 +// +int CALLBACK TreeCompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) +{ + CSequence* seq1 = (CSequence*) lParam1; + CSequence* seq2 = (CSequence*) lParam2; + + switch (lParamSort) + { + case TS_BY_ENUMTYPE: + { + // enumtypes are enum'd lower-numbers-more-important... + // + if (seq1->GetEnumType() < seq2->GetEnumType()) + { + return ITEM1_BEFORE_ITEM2; + } + + if (seq1->GetEnumType() > seq2->GetEnumType()) + { + return ITEM1_AFTER_ITEM2; + } + + // must be same type, so sort alphabetically... + // (strcmp almost returns correct values, except the results are signed anynums, not explicitly -1/0/1) + // + return ITEM1_MATCHES_ITEM2; +/* + int i = strcmp(seq1->GetEnum(),seq2->GetEnum()); + + if (i<0) + { + return ITEM1_BEFORE_ITEM2; + } + + if (i>0) + { + return ITEM1_AFTER_ITEM2; + } + + return ITEM1_MATCHES_ITEM2; +*/ + } + break; + } + + ASSERT(0); + return ITEM1_MATCHES_ITEM2; +} + +bool TreeSort(CTreeCtrl& tree, TREESORTTYPE ts) +{ + HTREEITEM hTreeItem_Model = tree.GetRootItem(); // "klingon" + + while (hTreeItem_Model) + { + TVSORTCB sortParams; + + sortParams.hParent = hTreeItem_Model; + sortParams.lpfnCompare = TreeCompareFunc; + sortParams.lParam = ts; + + tree.SortChildrenCB(&sortParams); + + hTreeItem_Model = tree.GetNextSiblingItem(hTreeItem_Model); + } + + return true; +} + +// returns true if any delete has taken place (sequence or model)... +// +bool CAssimilateView::DeleteCurrentItem(bool bNoQueryForSequenceDelete) +{ +// OutputDebugString(va("DeleteCurrentItem %s\n",bNoQueryForSequenceDelete?"CTRL":"")); + + // ok, what *is* the current item? + // + CTreeCtrl& tree = GetTreeCtrl(); + HTREEITEM theItem = tree.GetSelectedItem(); + if (theItem == NULL) + { + return false; + } + + // ok, so it's either a model or a sequence... + // + CModel* curModel = NULL; + if (tree.GetParentItem(theItem) == NULL) + { + curModel = (CModel*)tree.GetItemData(theItem); + if (curModel != NULL) + { + if (GetYesNo(va("Delete whole model \"%s\"?",curModel->GetName()))) + { + tree.DeleteItem(theItem); + GetDocument()->DeleteModel(curModel); + GetDocument()->SetModifiedFlag(); + return true; + } + } + } + else + { + curModel = (CModel*)tree.GetItemData(tree.GetParentItem(theItem)); + CSequence* curSequence = (CSequence*)tree.GetItemData(theItem); + if (curSequence != NULL) + { + // ask them if they're sure, unless the ctrl key is also pressed... + // + if (bNoQueryForSequenceDelete || GetYesNo(va("Delete sequence \"%s\"?",curSequence->GetName()))) + { + tree.DeleteItem(theItem); + curModel->DeleteSequence(curSequence); + GetDocument()->SetModifiedFlag(); + return true; + } + } + } + + return false; +} + +// also re-orders model(s) sequences to match tree order... +// +void CAssimilateView::SortTree() +{ + TreeSort(GetTreeCtrl(),TS_BY_ENUMTYPE); + + // now update models to reflect their positions within the tree struct... + // + HTREEITEM hTreeItem_Model = GetTreeCtrl().GetRootItem(); // "klingon" + + while (hTreeItem_Model) + { + CModel* curModel = (CModel*) GetTreeCtrl().GetItemData(hTreeItem_Model); +// OutputDebugString(va("item = '%s'\n",(LPCSTR) GetTreeCtrl().GetItemText(hTreeItem_Model))); + + int iSequenceNum = 0; + + HTREEITEM hTreeItem_Sequence = GetTreeCtrl().GetChildItem(hTreeItem_Model); + + while (hTreeItem_Sequence) + { + CSequence* curSequence = (CSequence*) GetTreeCtrl().GetItemData(hTreeItem_Sequence); + + curSequence->m_iSequenceNumber = iSequenceNum++; + + hTreeItem_Sequence = GetTreeCtrl().GetNextSiblingItem(hTreeItem_Sequence); + } + + curModel->ReOrderSequences(); + + hTreeItem_Model = GetTreeCtrl().GetNextSiblingItem(hTreeItem_Model); + } + + // sod it, too much work to check if re-ordering actually took place. Let's assume it did... :-) + // + GetDocument()->SetModifiedFlag(); +} + +// sorts tree according to enum names, sorts model according to new tree, re-draws tree +// (re-draw = changes icons & text according to enum-validity and prefs) +// +void CAssimilateView::UpdateTree() +{ + SortTree(); // sorts tree according to enum names, sorts model according to new tree + + HTREEITEM hTreeItem_Model = GetTreeCtrl().GetRootItem(); // "klingon" + + while (hTreeItem_Model) + { + CModel* curModel = (CModel*) GetTreeCtrl().GetItemData(hTreeItem_Model); +// OutputDebugString(va("item = '%s'\n",(LPCSTR) GetTreeCtrl().GetItemText(hTreeItem_Model))); + + HTREEITEM hTreeItem_Sequence = GetTreeCtrl().GetChildItem(hTreeItem_Model); + + while (hTreeItem_Sequence) + { + CSequence* curSequence = (CSequence*) GetTreeCtrl().GetItemData(hTreeItem_Sequence); + +// OutputDebugString(va("item = '%s'\n",(LPCSTR) GetTreeCtrl().GetItemText(hTreeItem_Sequence))); + +// gQuakeHelperTreeViewhandle->GetTreeCtrl().SetItemState(hTreeItem, bMatch?TVIS_SELECTED:0, TVIS_SELECTED); + + // update this tree item accordingly... + // + CDC *pDC = GetDC(); + GetTreeCtrl().SetItemText(hTreeItem_Sequence, curSequence->GetDisplayNameForTree(curModel,gbViewAnimEnums,gbViewFrameDetails,gbViewFrameDetails_Additional,pDC)); + ReleaseDC(pDC); + int iIcon = curSequence->GetDisplayIconForTree(curModel); + GetTreeCtrl().SetItemImage(hTreeItem_Sequence, iIcon, iIcon); + hTreeItem_Sequence = GetTreeCtrl().GetNextSiblingItem(hTreeItem_Sequence); + } + + hTreeItem_Model = GetTreeCtrl().GetNextSiblingItem(hTreeItem_Model); + } +} + + +PlayerMode_e CAssimilateView::GetSingleOrMultiPlayerMode() +{ + PlayerMode_e ePlayerMode = eMODE_BAD; + + LPCSTR psEnumFilename = ((CAssimilateApp*)AfxGetApp())->GetEnumFilename(); + + if (psEnumFilename && !stricmp(psEnumFilename,sDEFAULT_ENUM_FILENAME)) + ePlayerMode = eMODE_SINGLE; + else + if (psEnumFilename && !stricmp(psEnumFilename,sDEFAULT_ENUM_FILENAME_MULTI)) + ePlayerMode = eMODE_MULTI; + + return ePlayerMode; +} + +void CAssimilateView::SetFirstModelTitleAndMode() +{ + PlayerMode_e ePlayerMode = GetSingleOrMultiPlayerMode(); + + CTreeCtrl& tree = GetTreeCtrl(); + CModel* curModel = GetDocument()->GetFirstModel(); + if (curModel) + { + HTREEITEM hTreeItem = tree.GetRootItem(); + tree.SetItemText(hTreeItem, va("%s: ( %s )",curModel->GetName(), (ePlayerMode == eMODE_SINGLE)?"Single-Player Mode":(ePlayerMode == eMODE_MULTI)?"Multi-Player Mode":"Unknown Mode - Prefs not setup?")); + } +} + +void CAssimilateView::BuildTree() +{ + CTreeCtrl& tree = GetTreeCtrl(); + + CModel* curModel = GetDocument()->GetFirstModel(); + while(curModel != NULL) + { + HTREEITEM item = tree.InsertItem(curModel->GetName()); + tree.SetItemData(item, DWORD(curModel)); + tree.SetItemImage(item, ObjID_Folder, ObjID_Folder); + CSequence* curSequence = curModel->GetFirstSequence(); + while(curSequence != NULL) + { + CDC *pDC = GetDC(); + HTREEITEM seqItem = tree.InsertItem(curSequence->GetDisplayNameForTree(curModel,gbViewAnimEnums,gbViewFrameDetails,gbViewFrameDetails_Additional,pDC), item); + ReleaseDC(pDC); + tree.SetItemData(seqItem, DWORD(curSequence)); +/* if (curSequence->ValidEnum()) + { + tree.SetItemImage(seqItem, ObjID_Sequence, ObjID_Sequence); + } + else + { + tree.SetItemImage(seqItem, ObjID_MissingEnum, ObjID_MissingEnum); + }*/ + int iIcon = curSequence->GetDisplayIconForTree(curModel); + tree.SetItemImage(seqItem, iIcon, iIcon); + + curSequence = curSequence->GetNext(); + } + curModel = curModel->GetNext(); + } + + SetFirstModelTitleAndMode(); +} + +void CAssimilateView::OnDblclk(NMHDR* pNMHDR, LRESULT* pResult) +{ +// OutputDebugString("double\n"); + + *pResult = 0; + + CTreeCtrl& tree = GetTreeCtrl(); + HTREEITEM theItem = tree.GetSelectedItem(); + if (theItem == NULL) + { + return; + } + if (tree.GetParentItem(theItem) == NULL) + { + CModel* theModel = (CModel*)tree.GetItemData(theItem); + if (theModel != NULL) + { + if (theModel->DoProperties()) + { + GetDocument()->SetModifiedFlag(); + } + *pResult = 1; + } + } + else + { + CSequence* theSequence = (CSequence*)tree.GetItemData(theItem); + if (theSequence != NULL) + { + if (theSequence->DoProperties()) + { + GetDocument()->SetModifiedFlag(); + } + *pResult = 1; + } + } +} + + +void CAssimilateView::OnInitialUpdate() +{ + CTreeView::OnInitialUpdate(); + + // TODO: Add your specialized code here and/or call the base class + CImageList *pimagelist; + pimagelist = new CImageList(); + pimagelist->Create(IDB_TREEIMAGES, 16, FALSE, 0x00ffffff); + CTreeCtrl& theTree = GetTreeCtrl(); + CImageList* oldImageList; + oldImageList = theTree.GetImageList(TVSIL_NORMAL); + if (oldImageList != NULL) + delete(oldImageList); + theTree.SetImageList(pimagelist, TVSIL_NORMAL); +} + +void CAssimilateView::OnDestroy() +{ + CTreeCtrl& theTree = GetTreeCtrl(); + CImageList *pimagelist; + pimagelist = theTree.GetImageList(TVSIL_NORMAL); + if (pimagelist != NULL) + { + pimagelist->DeleteImageList(); + delete pimagelist; + } + CTreeView::OnDestroy(); + + // TODO: Add your message handler code here + +} + +void CAssimilateView::OnItemexpanded(NMHDR* pNMHDR, LRESULT* pResult) +{ + NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; + // TODO: Add your control notification handler code here + CTreeCtrl& theTree = GetTreeCtrl(); + HTREEITEM curSelection = pNMTreeView->itemNew.hItem; + if (theTree.GetParentItem(curSelection) == NULL) + { + int expandState = theTree.GetItemState(curSelection, TVIS_EXPANDED); + if (theTree.GetItemState(curSelection, TVIS_EXPANDED) & TVIS_EXPANDED) + { + theTree.SetItemImage(curSelection, ObjID_OpenFolder, ObjID_OpenFolder); + } + else + { + theTree.SetItemImage(curSelection, ObjID_Folder, ObjID_Folder); + } + } + *pResult = 0; +} + +void CAssimilateView::OnRButtonDblClk(UINT nFlags, CPoint point) +{ + // TODO: Add your message handler code here and/or call default + CTreeCtrl& tree = GetTreeCtrl(); + HTREEITEM theItem = tree.GetSelectedItem(); + if (theItem == NULL) + { + CTreeView::OnRButtonDblClk(nFlags, point); + return; //don't have an item, don't go on + } + if (tree.GetParentItem(theItem) == NULL) + { + CModel* theModel = (CModel*)tree.GetItemData(theItem); + if (theModel != NULL) + { + if (theModel->DoProperties()) + { + GetDocument()->SetModifiedFlag(); + } + } + } + else + { + /* // fixme: update this stuff some other time + CSequence* theSequence = (CSequence*)tree.GetItemData(theItem); + if (theSequence != NULL) + { + if (theSequence->Parse()) + { + GetDocument()->SetModifiedFlag(); + } + } + */ + } +} + + + + +void CAssimilateView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + switch (nChar) + { + case VK_DELETE: + { + if (DeleteCurrentItem(!!(GetAsyncKeyState(VK_CONTROL)))) // NoQueryForSequenceDelete if CTRL pressed + { + UpdateTree(); // sorts tree according to enum names, sorts model according to new tree, re-draws tree + } + } + break; + } + + CTreeView::OnKeyDown(nChar, nRepCnt, nFlags); +} + + + +// currently tells the document which model is the one the user considers current (ie which one is blue in tree) +// +void CAssimilateView::OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult) +{ +// OutputDebugString("OnSelchanged TOP\n"); + GetDocument()->ClearModelUserSelectionBools(); + + NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; + HTREEITEM theItem = pNMTreeView->itemNew.hItem; + + if (!theItem) + { + return; + } + + CTreeCtrl& tree = GetTreeCtrl(); + + CModel *theModel = NULL; + if (tree.GetParentItem(theItem) == NULL) + { + theModel = (CModel*)tree.GetItemData(theItem); + ASSERT(theModel); + if (theModel) + { + theModel->SetUserSelectionBool(); + } + } + else + { + theModel = (CModel*)tree.GetItemData(tree.GetParentItem(theItem)); + ASSERT(theModel); + if (theModel) + { + theModel->SetUserSelectionBool(); + } + } + +// OutputDebugString("OnSelchanged\n"); +// if (theModel) +// { +// OutputDebugString(va("Model = %s\n",theModel->GetName())); +// } +} + + + + + + diff --git a/utils/Assimilate/AssimilateView.h b/utils/Assimilate/AssimilateView.h new file mode 100644 index 0000000..7b1c30f --- /dev/null +++ b/utils/Assimilate/AssimilateView.h @@ -0,0 +1,109 @@ +// AssimilateView.h : interface of the CAssimilateView class +// +///////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_ASSIMILATEVIEW_H__2CCA554C_2AD3_11D3_82E0_0000C0366FF2__INCLUDED_) +#define AFX_ASSIMILATEVIEW_H__2CCA554C_2AD3_11D3_82E0_0000C0366FF2__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +enum +{ + ObjID_Folder, + ObjID_Unused1, + ObjID_Unused2, + ObjID_Unused3, + ObjID_Unused4, + ObjID_Sequence, + ObjID_ENUMINVALID, + ObjID_OpenFolder, + ObjID_Unused6, + ObjID_ENUMBOTH, + ObjID_ENUMTORSO, + ObjID_ENUMLEGS, + ObjID_ENUMG2, + ObjID_ENUMG2INVALID, + ObjID_ENUMG2GLA, + ObjID_ENUMG2GLAINVALID +}; + +typedef enum +{ + eMODE_BAD=0, + eMODE_SINGLE, + eMODE_MULTI +} PlayerMode_e; + +class CAssimilateView : public CTreeView +{ +protected: // create from serialization only + CAssimilateView(); + DECLARE_DYNCREATE(CAssimilateView) + +// Attributes +public: + CAssimilateDoc* GetDocument(); + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CAssimilateView) + public: + virtual void OnDraw(CDC* pDC); // overridden to draw this view + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + virtual void OnInitialUpdate(); + protected: + virtual BOOL OnPreparePrinting(CPrintInfo* pInfo); + virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo); + virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo); + virtual void OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CAssimilateView(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + + CModel *GetCurrentModel(); + PlayerMode_e GetSingleOrMultiPlayerMode(); + +protected: + void BuildTree(); + void UpdateTree(); + void SortTree(); + bool DeleteCurrentItem(bool bNoQueryForSequenceDelete = false); + void SetFirstModelTitleAndMode(); + +// Generated message map functions +protected: + //{{AFX_MSG(CAssimilateView) + afx_msg void OnDblclk(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnDestroy(); + afx_msg void OnItemexpanded(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnRButtonDblClk(UINT nFlags, CPoint point); + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +#ifndef _DEBUG // debug version in AssimilateView.cpp +inline CAssimilateDoc* CAssimilateView::GetDocument() + { return (CAssimilateDoc*)m_pDocument; } +#endif + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +extern CAssimilateView* ghAssimilateView; + +#endif // !defined(AFX_ASSIMILATEVIEW_H__2CCA554C_2AD3_11D3_82E0_0000C0366FF2__INCLUDED_) diff --git a/utils/Assimilate/BuildAll.cpp b/utils/Assimilate/BuildAll.cpp new file mode 100644 index 0000000..a9d9c65 --- /dev/null +++ b/utils/Assimilate/BuildAll.cpp @@ -0,0 +1,67 @@ +// BuildAll.cpp : implementation file +// + +#include "stdafx.h" +#include "Includes.h" +//#include "assimilate.h" +#include "BuildAll.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CBuildAll dialog + + +CBuildAll::CBuildAll(CString strPath, bool bPreValidate, + CWnd* pParent /*=NULL*/) + : CDialog(CBuildAll::IDD, pParent) +{ + //{{AFX_DATA_INIT(CBuildAll) + m_bPreValidateCARs = bPreValidate; + m_strBuildPath = strPath; + //}}AFX_DATA_INIT +} + + +void CBuildAll::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CBuildAll) + DDX_Check(pDX, IDC_CHECK_PREVALIDATECARS, m_bPreValidateCARs); + DDX_Text(pDX, IDC_EDIT_BUILDPATH, m_strBuildPath); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CBuildAll, CDialog) + //{{AFX_MSG_MAP(CBuildAll) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CBuildAll message handlers + +void CBuildAll::OnCancel() +{ + // TODO: Add extra cleanup here + + CDialog::OnCancel(); +} + +void CBuildAll::OnOK() +{ + // TODO: Add extra validation here + + CDialog::OnOK(); +} + +void CBuildAll::GetData(CString& strPath, bool& bPreValidate) +{ + strPath = m_strBuildPath; + bPreValidate= !!m_bPreValidateCARs; +} + diff --git a/utils/Assimilate/BuildAll.h b/utils/Assimilate/BuildAll.h new file mode 100644 index 0000000..29be287 --- /dev/null +++ b/utils/Assimilate/BuildAll.h @@ -0,0 +1,51 @@ +#if !defined(AFX_BUILDALL_H__46894350_463F_4AC6_B84D_5378E3D50E7E__INCLUDED_) +#define AFX_BUILDALL_H__46894350_463F_4AC6_B84D_5378E3D50E7E__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// BuildAll.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CBuildAll dialog + +class CBuildAll : public CDialog +{ +// Construction +public: + CBuildAll(CString strPath, bool bPreValidate, + CWnd* pParent = NULL); // standard constructor + + void GetData(CString& strPath, bool& bPreValidate); + +// Dialog Data + //{{AFX_DATA(CBuildAll) + enum { IDD = IDD_BUILD_ALL }; + BOOL m_bPreValidateCARs; + CString m_strBuildPath; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CBuildAll) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CBuildAll) + virtual void OnCancel(); + virtual void OnOK(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_BUILDALL_H__46894350_463F_4AC6_B84D_5378E3D50E7E__INCLUDED_) diff --git a/utils/Assimilate/Comment.cpp b/utils/Assimilate/Comment.cpp new file mode 100644 index 0000000..2b3bab6 --- /dev/null +++ b/utils/Assimilate/Comment.cpp @@ -0,0 +1,76 @@ +// Comment.cpp + +#include "StdAfx.h" +#include "Includes.h" + +CComment::CComment() +{ +} + +CComment::~CComment() +{ +} + +void CComment::Delete() +{ + if (m_comment != NULL) + { + free(m_comment); + m_comment = NULL; + } + delete this; +} + +CComment* CComment::Create(LPCTSTR comment) +{ + CComment* retval = new CComment(); + retval->Init(comment); + return retval; +} + +void CComment::SetComment(LPCTSTR comment) +{ + if (m_comment != NULL) + { + free(m_comment); + } + if (comment == NULL) + { + m_comment = NULL; + } + else + { + m_comment = (char*)malloc(strlen(comment) + 1); + strcpy(m_comment, comment); + } +} + +LPCTSTR CComment::GetComment() +{ + return m_comment; +} + +void CComment::SetNext(CComment* next) +{ + m_next = next; +} + +CComment* CComment::GetNext() +{ + return m_next; +} + +void CComment::Init(LPCTSTR comment) +{ + m_next = NULL; + m_comment = NULL; + SetComment(comment); +} + +void CComment::Write(CTxtFile* file) +{ + if (m_comment != NULL) + { + file->WriteComment(m_comment); + } +} diff --git a/utils/Assimilate/Comment.h b/utils/Assimilate/Comment.h new file mode 100644 index 0000000..cd044b5 --- /dev/null +++ b/utils/Assimilate/Comment.h @@ -0,0 +1,26 @@ +// Comment.h + +class CComment +{ +public: + CComment(); + virtual ~CComment(); + + virtual void Delete(); + static CComment* Create(LPCTSTR comment = NULL); + + void Write(CTxtFile* file); + + void SetComment(LPCTSTR comment); + LPCTSTR GetComment(); + + void SetNext(CComment* next); + CComment* GetNext(); + +protected: + void Init(LPCTSTR comment); + + char* m_comment; + CComment* m_next; +}; + diff --git a/utils/Assimilate/MainFrm.cpp b/utils/Assimilate/MainFrm.cpp new file mode 100644 index 0000000..3506848 --- /dev/null +++ b/utils/Assimilate/MainFrm.cpp @@ -0,0 +1,119 @@ +// MainFrm.cpp : implementation of the CMainFrame class +// + +#include "stdafx.h" +#include "Includes.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame + +IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd) + +BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) + //{{AFX_MSG_MAP(CMainFrame) + ON_WM_CREATE() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +static UINT indicators[] = +{ + ID_SEPARATOR, // status line indicator + ID_INDICATOR_CAPS, + ID_INDICATOR_NUM, + ID_INDICATOR_SCRL, +}; + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame construction/destruction + +CMainFrame::CMainFrame() +{ + // TODO: add member initialization code here + +} + +CMainFrame::~CMainFrame() +{ +} + +int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CFrameWnd::OnCreate(lpCreateStruct) == -1) + return -1; + + if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP + | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) || + !m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) + { + TRACE0("Failed to create toolbar\n"); + return -1; // fail to create + } + + if (!m_wndStatusBar.Create(this) || + !m_wndStatusBar.SetIndicators(indicators, + sizeof(indicators)/sizeof(UINT))) + { + TRACE0("Failed to create status bar\n"); + return -1; // fail to create + } + + // TODO: Delete these three lines if you don't want the toolbar to + // be dockable + m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); + EnableDocking(CBRS_ALIGN_ANY); + DockControlBar(&m_wndToolBar); + + return 0; +} + +BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) +{ + if( !CFrameWnd::PreCreateWindow(cs) ) + return FALSE; + // TODO: Modify the Window class or styles here by modifying + // the CREATESTRUCT cs + + return TRUE; +} + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame diagnostics + +#ifdef _DEBUG +void CMainFrame::AssertValid() const +{ + CFrameWnd::AssertValid(); +} + +void CMainFrame::Dump(CDumpContext& dc) const +{ + CFrameWnd::Dump(dc); +} + +#endif //_DEBUG + +void CMainFrame::StatusMessage(LPCTSTR message) +{ + if (this) + { + m_wndStatusBar.SetWindowText(message); + } +} + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame message handlers + + +void StatusText(LPCSTR psText) +{ + ((CMainFrame*)AfxGetMainWnd())->StatusMessage( (psText && psText[0]) ? psText : "Ready"); +} + + + diff --git a/utils/Assimilate/MainFrm.h b/utils/Assimilate/MainFrm.h new file mode 100644 index 0000000..3f9faff --- /dev/null +++ b/utils/Assimilate/MainFrm.h @@ -0,0 +1,63 @@ +// MainFrm.h : interface of the CMainFrame class +// +///////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_MAINFRM_H__2CCA5548_2AD3_11D3_82E0_0000C0366FF2__INCLUDED_) +#define AFX_MAINFRM_H__2CCA5548_2AD3_11D3_82E0_0000C0366FF2__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +class CMainFrame : public CFrameWnd +{ + +protected: // create from serialization only + CMainFrame(); + DECLARE_DYNCREATE(CMainFrame) + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CMainFrame) + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CMainFrame(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + void StatusMessage(LPCTSTR message); + +protected: // control bar embedded members + CStatusBar m_wndStatusBar; + CToolBar m_wndToolBar; + +// Generated message map functions +protected: + //{{AFX_MSG(CMainFrame) + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnEditBuilddependant(); + afx_msg void OnUpdateEditBuilddependant(CCmdUI* pCmdUI); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + + +void StatusText(LPCSTR psText); + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_MAINFRM_H__2CCA5548_2AD3_11D3_82E0_0000C0366FF2__INCLUDED_) + diff --git a/utils/Assimilate/Model.cpp b/utils/Assimilate/Model.cpp new file mode 100644 index 0000000..7139c5a --- /dev/null +++ b/utils/Assimilate/Model.cpp @@ -0,0 +1,1652 @@ +// Model.cpp + +#include "StdAfx.h" +#include "Includes.h" +#include + +#define sANIMATION_CFG_NAME "animation.cfg" +#define sANIMATION_PRE_NAME "animation.pre" + +bool gbReportMissingASEs = true; +int giFixUpdatedASEFrameCounts = YES; + + +CModel::CModel() +{ + m_bCurrentUserSelection = false; +} + +CModel::~CModel() +{ +} + +void CModel::Delete() +{ + while(m_comments != NULL) + { + CComment* curComment = m_comments; + m_comments = curComment->GetNext(); + curComment->Delete(); + } + while(m_sequences != NULL) + { + CSequence* curSequence = m_sequences; + m_sequences = curSequence->GetNext(); + curSequence->Delete(); + } + if (m_name != NULL) + { + free(m_name); + m_name = NULL; + } + if (m_path != NULL) + { + free(m_path); + m_path = NULL; + } + if (m_psSkelPath != NULL) + { + free(m_psSkelPath); + m_psSkelPath = NULL; + } + if (m_psMakeSkelPath != NULL) + { + free(m_psMakeSkelPath); + m_psMakeSkelPath = NULL; + } + m_curSequence = NULL; + + PCJList_Clear(); // not really necessary, but useful reminder + + delete this; +} + +CModel* CModel::Create(CComment* comments) +{ + CModel* retval = new CModel(); + retval->Init(comments); + return retval; +} + +bool CModel::DoProperties() +{ + bool dirty = false; + +// if (IsGhoul2()) +// { +// InfoBox("This properties page is for params not relevant for Ghoul2 models"); +// } +// else + { + CPropertySheet* propSheet = new CPropertySheet(m_name); + + CModelPropPage* propPage = new CModelPropPage(); + propPage->m_model = this; + propPage->m_soilFlag = &dirty; + propSheet->AddPage(propPage); + + for (int i=0; iAddPCJEntry(PCJList_GetEntry(i)); + } + + propSheet->DoModal(); + + delete propPage; + delete propSheet; + } + + return dirty; +} + +void CModel::SetNext(CModel* next) +{ + m_next= next; +} + +CModel* CModel::GetNext() +{ + return m_next; +} + +bool CModel::ContainsFile(LPCSTR psFilename) +{ + if (m_sequences == NULL) + { + return false; + } + else + { + CSequence* curSequence = m_sequences; + while(curSequence) + { + if (stricmp(curSequence->GetPath(),psFilename)==0) + return true; + curSequence = curSequence->GetNext(); + } + } + + return false; +} + +void CModel::AddSequence(CSequence* sequence) +{ + if (m_sequences == NULL) + { + m_sequences = sequence; + } + else + { + CSequence* curSequence = m_sequences; + while(curSequence->GetNext() != NULL) + { + curSequence = curSequence->GetNext(); + } + curSequence->SetNext(sequence); + } +} + +void CModel::DeleteSequence(CSequence* deleteSequence) +{ + // linklist is only 1-way, so we need to find the stage previous to this (if any)... + // + CSequence* prevSequence = NULL; + CSequence* scanSequence = GetFirstSequence(); + + while (scanSequence && scanSequence != deleteSequence) + { + prevSequence = scanSequence; + scanSequence = scanSequence->GetNext(); + } + if (scanSequence == deleteSequence) + { + // we found it, so was this the first sequence in the list? + // + if (prevSequence) + { + prevSequence->SetNext(scanSequence->GetNext()); // ...no + } + else + { + m_sequences = scanSequence->GetNext(); // ...yes + } + scanSequence->Delete(); + } +} + + + +// func for qsort to callback, returns: +// +// <0 elem1 less than elem2 +// 0 elem1 equivalent to elem2 +// >0 elem1 greater than elem2 +// +int ModelSequenceCompareFunc( const void *arg1, const void *arg2 ) +{ + CSequence *seq1 = (CSequence *) *(CSequence **)arg1; + CSequence *seq2 = (CSequence *) *(CSequence **)arg2; + + return (seq1->m_iSequenceNumber - seq2->m_iSequenceNumber); + +/* if (seq1->m_iSequenceNumber < seq2->m_iSequenceNumber) + return -1; + + if (seq1->m_iSequenceNumber > seq2->m_iSequenceNumber) + return 1; + + return 0; + */ +} + +// change the sequences around in the model till each one is in the position specified by it's member: m_iSequenceNumber +// +void CModel::ReOrderSequences() +{ + typedef vector sequences_t; sequences_t sequences; + + // add sequences to list... + // + CSequence *curSequence = m_sequences; + while (curSequence) + { + sequences.push_back(curSequence); + curSequence = curSequence->GetNext(); + } + + // re-order sequences... + // + qsort( (void *)&sequences[0], (size_t)(sequences.size()), sizeof(CSequence *), ModelSequenceCompareFunc ); + + // now rebuild links... + // + int iTotMasterSequences = GetTotMasterSequences(); // this needs to be eval'd here, you can't do it in the for-next below + m_sequences = NULL; + for (int i=0; iSetNext(NULL); + + AddSequence(curSequence); + } + + Resequence(); +} + +// a niceness feature so the popup anim enum dialog picker can ask which enums are already in use... +// +// (now updated to return a int instead of a bool, therefore can be used to check for duplicates) +// +int CModel::AnimEnumInUse(LPCSTR psAnimEnum) +{ + int iCount = 0; + if (strlen(psAnimEnum)) // added for G2 models, which don't necessarily use enums yet + { + CSequence *curSequence = GetFirstSequence(); + + while (curSequence) + { + if (!strcmp(psAnimEnum,curSequence->GetEnum())) + { + iCount++;// return true; + } + + // new bit, ask the additional sequences as well... + // + for (int i=0; iAdditionalSeqs[i]->GetEnum())) + { + iCount++;// return true; + } + } + + curSequence = curSequence->GetNext(); + } + } + return iCount;//false; +} + +CSequence* CModel::GetFirstSequence() +{ + return m_sequences; +} + +int CModel::GetTotMasterSequences() +{ + int tot = 0; + CSequence* curSequence = m_sequences; + while(curSequence != NULL) + { + tot++; + curSequence = curSequence->GetNext(); + } + return tot; +} + +int CModel::GetTotSequences() +{ + int tot = 0; + CSequence* curSequence = m_sequences; + while(curSequence != NULL) + { + tot++; + + for (int i=0; iAdditionalSeqs[i]->AdditionalSequenceIsValid()) + tot++; + } + + curSequence = curSequence->GetNext(); + } + return tot; +} + +void CModel::Resequence(bool bReScanASEFiles /* = false */) +{ + CWaitCursor wait; + CRect Rect; +// ((CAssimilateApp*)AfxGetApp())->m_pMainWnd->GetWindowRect(&Rect); +// CPoint Point = Rect.CenterPoint(); + + CProgressCtrl *pProgress = NULL; + + if (bReScanASEFiles && ((CAssimilateApp*)AfxGetApp())->m_pMainWnd) + { + pProgress = new CProgressCtrl; + bool bOK = !!pProgress->Create( WS_CHILD|WS_VISIBLE|PBS_SMOOTH, // DWORD dwStyle, + CRect(100,100,200,200), // const RECT& rect, + ((CAssimilateApp*)AfxGetApp())->m_pMainWnd, // CWnd* pParentWnd, + 1 // UINT nID + ); + if (!bOK) + { + delete pProgress; + pProgress = NULL; + } + } + + int iTotMasterSequences = GetTotMasterSequences(); + if (pProgress) + { + pProgress->SetRange(0,iTotMasterSequences); + } + int iSequenceNumber=0; + + int curFrame = 0; + CSequence* curSequence = m_sequences; + while(curSequence != NULL) + { + if (pProgress) + { + pProgress->SetPos(iSequenceNumber++); +// pProgress->SetWindowText(va("Sequence %d/%d (%s)",iSequenceNumber,iTotMasterSequences,curSequence->GetPath())); + wait.Restore(); + } + + // mark current enums as valid or not... + // + curSequence->SetValidEnum(((CAssimilateApp*)AfxGetApp())->ValidEnum(curSequence->GetEnum())); +//-------- + for (int _i=0; _iAdditionalSeqs[_i]; + + additionalSeq->SetValidEnum(((CAssimilateApp*)AfxGetApp())->ValidEnum(additionalSeq->GetEnum())); + } +//--------- + + if ( bReScanASEFiles ) + { + // new code, first of all check for changed framecounts (ie updated ASE file), and update sequence if nec... + // + CString nameASE = ((CAssimilateApp*)AfxGetApp())->GetQuakeDir(); + nameASE+= curSequence->GetPath(); + + if (!FileExists(nameASE)) + { + if (gbCarWash_DoingScan) + { + strCarWashErrors += va("Model file missing: \"%s\"\n",nameASE); + } + else + { + if ( gbReportMissingASEs ) + { + gbReportMissingASEs = GetYesNo(va("Model file missing: \"%s\"\n\nContinue recieving this message?",nameASE)); + } + } + } + else + { + int iStartFrame, iFrameCount, iFrameSpeed; + + iFrameCount = curSequence->GetFrameCount(); // default it in case we skip an XSI read + iFrameSpeed = curSequence->GetFrameSpeed(); // default it in case we cache this file + + curSequence->ReadASEHeader( nameASE, iStartFrame, iFrameCount, iFrameSpeed, true); // true = can skip XSI read + + if ( iFrameCount != curSequence->GetFrameCount() ) + { + if (gbCarWash_DoingScan) + { + strCarWashErrors += va("file: \"%s\" has a framecount of %d, but .CAR file says %d\n",nameASE,iFrameCount,curSequence->GetFrameCount()); + } + else + { + // don't mention it if the current count is zero, it's probably a new anim we've just added... + // + if ( curSequence->GetFrameCount() ) + { + if (giFixUpdatedASEFrameCounts == YES || giFixUpdatedASEFrameCounts == NO) + { + CYesNoYesAllNoAll query( va("Model file: \"%s\"",nameASE), + "", + va("... has a framecount of %d instead of %d as the QDT/CAR file says",iFrameCount, curSequence->GetFrameCount()), + "", + "", + "Do you want me to fix this?" + ); + giFixUpdatedASEFrameCounts = query.DoModal(); + //gbReportUpdatedASEFrameCounts = GetYesNo(va("Model file: \"%s\"\n\n... has a framecount of %d instead of %d as the QDT/CAR file says (so I'll update it).\n\nContinue recieving this message?",nameASE, iFrameCount, curSequence->GetFrameCount())); + } + } + + // update the sequence?... + // + if (giFixUpdatedASEFrameCounts == YES || giFixUpdatedASEFrameCounts == YES_ALL + || !curSequence->GetFrameCount() // update: I think this should be here? + ) + { + curSequence->SetFrameCount( iFrameCount ); + } + } + } + } + + // findmeste: this no longer seems to do anything under JK2, presumablt EF1-only? +#if 0 + // now try to do any auto-associate between the ASE filename base and the existing enums, + // so if we find (eg) /...../...../CROUCH.ASE and we have BOTH_CROUCH then auto-set the enum to BOTH_CROUCH + // + CString stringASEName = nameASE; + Filename_BaseOnly(stringASEName); // now = (eg) "falldeath" or "injured" etc + + for (int i=0; ; i++) + { + LPCSTR p = ((CAssimilateApp*)AfxGetApp())->GetEnumEntry(i); + + if (!p) // EOS? + break; + + CString stringEnum = p; + + // note, I could check stuff like "IsEnumSeperator(LPCSTR lpString)" on

, but you'd never + // have one of those enums assigned to a sequence anyway. + + char *psEnumPosAfterUnderScore = strchr(stringEnum,'_'); + if (psEnumPosAfterUnderScore++) // check it, and skip to next char + { + // does this enum match the ASE name? + // + if ( !stricmp( psEnumPosAfterUnderScore, stringASEName ) ) + { + // ok, we've found a good candidate, so set it... (no need for query-prev code, but I wanted to) + // + if ( strcmp( curSequence->GetEnum(), stringEnum)) + { +// InfoBox( va("(temp notify box)\n\nEnum auto-assign of \"%s\" to ASE \"%s\" (prev enum was \"%s\")",stringEnum,nameASE,curSequence->GetEnum())); + curSequence->SetEnum(stringEnum); + } + } + } + else + { + // this should never happen... + // + if (gbCarWash_DoingScan) + { + strCarWashErrors += va("found an anim enum with no underscore: \"%s\"\n",stringEnum); + } + else + { + ASSERT(0); + ErrorBox(va("Error! Somehow I found an anim enum with no underscore: \"%s\"",stringEnum)); + } + } + } +#endif + } + + // More bollox for Gummelt... :-) + // now do the other freaky trick (you'd better be grateful for all this Mike!!! ), which is: + // + // If you find the substring DEATH in this (master) sequence's enum, then ensure that the first *additional* + // sequence of it is set to be the corresponding DEAD enum, but using the last frame only (and non-looping) + // + // (... or something...) + // + { // keep scope local for neatness + + if ( strstr (curSequence->GetEnum(), "DEATH") ) + { + // scan this sequence's additional sequences for a DEAD of the same basic type... + // + CString stringEnumDEAD = curSequence->GetEnum(); + + ASSERT(!IsEnumSeperator(stringEnumDEAD)); + + stringEnumDEAD.Replace("DEATH","DEAD"); + + // 1st, is there even a corresponding DEAD enum in the global enum table that we can look for... + // + CString stringEnum; + bool bEnumFound = false; + for (int iEnumEntry=0; !bEnumFound; iEnumEntry++) + { + LPCSTR p = ((CAssimilateApp*)AfxGetApp())->GetEnumEntry(iEnumEntry); + + if (!p) // EOS? + break; + + stringEnum = p; + + // note, I could check stuff like "IsEnumSeperator(LPCSTR lpString)" on

, but you'd never + // have one of those enums assigned to a sequence anyway. + + // does this enum match the one we've built? + // + if ( !stricmp( stringEnum, stringEnumDEAD ) ) + { + bEnumFound = true; + } + } + + if ( bEnumFound ) + { + // ok, there *is* one of these, so let's scan this sequence's additional sequences to see if we've + // got it... + // + CSequence *additionalSeq; // outside FOR scope + for (int i=0; iAdditionalSeqs[i]; + + if (additionalSeq->AdditionalSequenceIsValid()) + { + if (!strcmp(additionalSeq->GetEnum(),stringEnum)) + { + break; // we've found one! + } + } + } + + // if we didn't find one, NULL the ptr + // + if ( i == MAX_ADDITIONAL_SEQUENCES) + { + additionalSeq = NULL; + } + + // did we find one? (or did it have the wrong info in?) + // + if ( additionalSeq == NULL // didn't find one + || additionalSeq->GetFrameCount()!=1 + || additionalSeq->GetLoopFrame() !=-1 + || additionalSeq->GetStartFrame()!= curSequence->GetFrameCount()-1 + || additionalSeq->GetFrameSpeed()!= curSequence->GetFrameSpeed() + ) + { + // find a slot to add this new sequence to, or use the faulty one... + // + if (additionalSeq == NULL) + { + for (int i=0; iAdditionalSeqs[i]; + + if (!additionalSeq->AdditionalSequenceIsValid()) + { + break; // found an unused slot + } + } + } + + // so have we got a slot to work with? + // + if ( additionalSeq == NULL ) + { + if (gbCarWash_DoingScan) + { + strCarWashErrors += va( "Fuck!!!: I need an 'additional sequence' slot free in the entry: \"%s\" to generate a DEAD seq, but there isn't one spare. Edit this yourself later.\n",curSequence->GetPath()); + } + else + { + ErrorBox( va( "Fuck!!!\n\nI need an 'additional sequence' slot free in the ASE:\n\n\"%s\"\n\n... to generate a DEAD seq, but there isn't one spare. Edit this yourself later.",curSequence->GetPath())); + } + } + else + { + additionalSeq->SetStartFrame( curSequence->GetFrameCount()-1 ); + additionalSeq->SetFrameCount( 1 ); + additionalSeq->SetLoopFrame (-1 ); + additionalSeq->SetFrameSpeed( curSequence->GetFrameSpeed() ); + additionalSeq->SetEnum ( stringEnumDEAD ); + } + } + } + } + } + + curSequence->SetTargetFrame(curFrame + curSequence->GetStartFrame()); // slightly more legal than just (curFrame) + + // update: now set any additional sequences within it... + // + for (int i=0; iAdditionalSeqs[i]->SetTargetFrame(curFrame + curSequence->AdditionalSeqs[i]->GetStartFrame()); + } + + curFrame += curSequence->GetFrameCount(); + curFrame += curSequence->GetGenLoopFrame()?1:0; // findme: is this right? I hate this system + curSequence = curSequence->GetNext(); + } + m_totFrames = curFrame; + + ghAssimilateView->GetDocument()->SetModifiedFlag(); +// (ghAssimilateView->GetDocument()->IsModified())?OutputDebugString("modified\n"):OutputDebugString("same\n"); + + if (pProgress) + { + delete pProgress; + pProgress = 0; + } +} + +void CModel::AddComment(CComment* comment) +{ + if (m_curSequence != NULL) + { + m_curSequence->AddComment(comment); + return; + } + if (m_comments == NULL) + { + m_comments = comment; + } + else + { + CComment* curComment = m_comments; + while (curComment->GetNext() != NULL) + { + curComment = curComment->GetNext(); + } + curComment->SetNext(comment); + } +} + +CComment* CModel::GetFirstComment() +{ + return m_comments; +} + +CComment* CModel::ExtractComments() +{ + CComment* retval = m_comments; + m_comments = NULL; + return retval; +} + +void CModel::SetName(LPCTSTR name) +{ + if (m_name != NULL) + { + free(m_name); + } + if (name == NULL) + { + m_name = NULL; + } + else + { + m_name = (char*)malloc(strlen(name) + 1); + strcpy(m_name, name); + } +} + +LPCTSTR CModel::GetName() +{ + return m_name; +} + +void CModel::SetPath(LPCTSTR name) +{ + if (m_path != NULL) + { + free(m_path); + } + if (name == NULL) + { + m_path = NULL; + } + else + { + m_path = (char*)malloc(strlen(name) + 1); + strcpy(m_path, name); + } +} + +LPCTSTR CModel::GetPath() +{ + return m_path; +} + +void CModel::DeriveName(LPCTSTR fromname) +{ + SetPath(fromname); + if (fromname == NULL) + { + if (m_name != NULL) + { + free(m_name); + m_name = NULL; + } + return; + } + CString name = fromname; + int loc = name.ReverseFind('/'); + if (loc > -1) + { + name = name.Left(loc); + } + loc = name.ReverseFind('/'); + if (loc > -1) + { + name = name.Right(name.GetLength() - loc - 1); + } + SetName(name); +} + +void CModel::Init(CComment* comments) +{ + m_comments = comments; + m_next = NULL; + m_sequences = NULL; + m_curSequence = NULL; + m_name = NULL; + m_path = NULL; + m_psSkelPath = NULL; + m_psMakeSkelPath = NULL; + SetOrigin(0, 0, 0); + SetParms(-1, -1, 0, 1); + m_iType = TK_AS_CONVERTMDX_NOASK; + m_bSmooth = false; + m_bLoseDupVerts = false; + m_bMakeSkin = false; + m_fScale = 1.0f; + m_bIgnoreBaseDeviations = false; + m_bSkew90 = false; + m_bNoSkew90 = false; + m_bKeepMotion = false; + m_bPreQuat = false; + + PCJList_Clear(); +} + +void CModel::SetConvertType(int iType) +{ + m_iType = iType; +} + +int CModel::GetConvertType(void) +{ + return m_iType; +} + +bool CModel::IsGhoul2(void) +{ + if (GetConvertType() == TK_AS_CONVERTMDX || + GetConvertType() == TK_AS_CONVERTMDX_NOASK + ) + { + return true; + } + + return false; +} + + +void CModel::SetMakeSkin(bool bMakeSkin) +{ + m_bMakeSkin = bMakeSkin; +} + +bool CModel::GetMakeSkin(void) +{ + return m_bMakeSkin; +} + +void CModel::SetSmooth(bool bSmooth) +{ + m_bSmooth = bSmooth; +} + +bool CModel::GetSmooth(void) +{ + return m_bSmooth; +} + +void CModel::SetKeepMotion(bool bKeepMotion) +{ + m_bKeepMotion = bKeepMotion; +} + +bool CModel::GetKeepMotion(void) +{ + return m_bKeepMotion; +} + +void CModel::SetLoseDupVerts(bool bLoseDupVerts) +{ + m_bLoseDupVerts = bLoseDupVerts; +} + +bool CModel::GetLoseDupVerts(void) +{ + return m_bLoseDupVerts; +} + +void CModel::SetScale(float fScale) +{ + m_fScale = fScale; +} + +float CModel::GetScale(void) +{ + return m_fScale; +} + + +bool CModel::GetPreQuat(void) +{ + return m_bPreQuat; +} + +void CModel::SetPreQuat(bool bPreQuat) +{ + m_bPreQuat = bPreQuat; +} + + +void CModel::PCJList_Clear() +{ + m_vPCJList.clear(); +} + +void CModel::PCJList_AddEntry(LPCSTR psEntry) +{ + m_vPCJList.push_back(psEntry); +} + +int CModel::PCJList_GetEntries() +{ + return m_vPCJList.size(); +} + +LPCSTR CModel::PCJList_GetEntry(int iIndex) +{ + if (iIndex < PCJList_GetEntries()) + { + return m_vPCJList[iIndex].c_str(); + } + + assert( iIndex < PCJList_GetEntries() ); + return ""; +} + + +// temporary!!!!!!! +void CModel::SetIgnoreBaseDeviations(bool bIgnore) +{ + m_bIgnoreBaseDeviations = bIgnore; +} + +bool CModel::GetIgnoreBaseDeviations(void) +{ + return m_bIgnoreBaseDeviations; +} + +void CModel::SetSkew90(bool bSkew90) +{ + m_bSkew90 = bSkew90; +} + +bool CModel::GetSkew90(void) +{ + return m_bSkew90; +} + +void CModel::SetNoSkew90(bool bNoSkew90) +{ + m_bNoSkew90 = bNoSkew90; +} + +bool CModel::GetNoSkew90(void) +{ + return m_bNoSkew90; +} + +/* +void CModel::SetSkelPath(LPCSTR psPath) +{ + if (m_psSkelPath != NULL) + { + free(m_psSkelPath); + } + + if (psPath == NULL) + { + m_psSkelPath = NULL; + } + else + { + m_psSkelPath = (char*) malloc (strlen(psPath)+1); + strcpy(m_psSkelPath, psPath); + } +} + +LPCSTR CModel::GetSkelPath(void) +{ + return m_psSkelPath; // warning, may be NULL or blank +} +*/ +void CModel::SetMakeSkelPath(LPCSTR psPath) +{ + if (m_psMakeSkelPath != NULL) + { + free(m_psMakeSkelPath); + } + + if (psPath == NULL) + { + m_psMakeSkelPath = NULL; + } + else + { + m_psMakeSkelPath = (char*) malloc (strlen(psPath)+1); + strcpy(m_psMakeSkelPath, psPath); + strlwr(m_psMakeSkelPath); + } +} + +LPCSTR CModel::GetMakeSkelPath(void) +{ + return m_psMakeSkelPath; // warning, may be NULL or blank +} + + +void CModel::SetOrigin(int x, int y, int z) +{ + SetOriginX(x); + SetOriginY(y); + SetOriginZ(z); +} + +void CModel::SetOriginX(int x) +{ + m_originx = x; +} + +void CModel::SetOriginY(int y) +{ + m_originy = y; +} + +void CModel::SetOriginZ(int z) +{ + m_originz = z; +} + +int CModel::GetOriginX() +{ + return m_originx; +} + +int CModel::GetOriginY() +{ + return m_originy; +} + +int CModel::GetOriginZ() +{ + return m_originz; +} + +void CModel::SetParms(int skipStart, int skipEnd, int totFrames, int headFrames) +{ + SetTotFrames(totFrames); +} + +void CModel::SetTotFrames(int value) +{ + m_totFrames = value; +} + +int CModel::GetTotFrames() +{ + return m_totFrames; +} + +void CModel::SetUserSelectionBool(bool bSelected) +{ + m_bCurrentUserSelection = bSelected; +} + +bool CModel::GetUserSelectionBool() +{ + return m_bCurrentUserSelection; +} + +bool CModel::WriteExternal(bool bPromptForNames, bool& bCFGWritten) +{ + bCFGWritten = false; + + CString filename; + if (bPromptForNames) + { + //XXXXXXXXXXXXXX +// CFileDialog dialog(FALSE, ".cfg", /*m_name*/"D:\\Source\\StarTrek\\Code-DM\\baseef\\models\\players2\\imperial\\animation_new.cfg", OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "Config Data Files (*.cfg)|*.cfg|All Files (*.*)|*.*||", NULL); + + CString strInitialPrompt(ghAssimilateView->GetDocument()->GetPathName()); + Filename_RemoveFilename(strInitialPrompt); + strInitialPrompt.Replace("/","\\"); + strInitialPrompt += "\\"; + strInitialPrompt += sANIMATION_CFG_NAME; + + CFileDialog dialog(FALSE, ".cfg", strInitialPrompt, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "Config Data Files (*.cfg)|*.cfg|All Files (*.*)|*.*||", NULL); + if (dialog.DoModal() != IDOK) + { + return false; + } + filename = dialog.GetPathName(); // eg. {"Q:\quake\baseq3\models\players\ste_assimilate_test\ste_assimilate_test.cfg"} + } + else + { + filename = ((CAssimilateApp*)AfxGetApp())->GetQuakeDir(); + filename+= GetPath(); + filename.MakeLower(); + filename.Replace('\\', '/'); + int loc = filename.Find(m_name);//"/root"); + if (loc>=0) + { + filename = filename.Left(loc+strlen(m_name)); + } + // dup the dirname to use as the model name... (eg "/.../.../klingon" becomes "/.../.../klingon/klingon" + //loc = filename.ReverseFind('/'); + //filename += filename.Mid(loc); + //filename += ".cfg"; + filename += "/"; filename += sANIMATION_CFG_NAME; + } + CTxtFile* file = CTxtFile::Create(filename); + if (file == NULL || !file->IsValid()) + { + ErrorBox(va("Error creating file \"%s\"!",filename)); + return false; + } + + // new bit, check for the existance of an animation.pre file, which means export this in Q3 format (rather than trek) + // + CString strQ3FormatCheckName(filename); + Filename_RemoveFilename(strQ3FormatCheckName); + strQ3FormatCheckName += "\\"; + strQ3FormatCheckName += sANIMATION_PRE_NAME; + strQ3FormatCheckName.Replace("/","\\"); + + bool bExportFormatIsQuake3Multiplayer = //FileExists(strQ3FormatCheckName); + ((CAssimilateApp*)AfxGetApp())->GetMultiPlayerMode(); + + CString strPrePend; + if (bExportFormatIsQuake3Multiplayer) + { + // multi-player format, check for optional animation.pre file... + // + FILE *fhPRE = fopen(strQ3FormatCheckName, "rt"); + + if (fhPRE) + { + // + // read all the lines in this file and just write them straight to the output file... + // + char sLine[16384]; + char *psLine; + CString strTrimmed; + + while ((psLine = fgets( sLine, sizeof(sLine), fhPRE ))!=NULL) + { + strTrimmed = psLine; + strTrimmed.Replace("\n",""); + strTrimmed.TrimRight(); + strTrimmed.TrimLeft(); + + file->Writeln(strTrimmed); + } + + if (ferror(fhPRE)) + { + ErrorBox(va("Error during reading of file \"%s\"!\n\n( this shouldn't happen )",(LPCSTR)strQ3FormatCheckName)); + } + + fclose(fhPRE); + } + + file->Writeln(""); + file->Writeln("//"); + file->Writeln("// Format: targetFrame, frameCount, loopFrame, frameSpeed"); + file->Writeln("//"); + } + else + { + // single-player format... + // + CString commentLine; + CTime time = CTime::GetCurrentTime(); + commentLine.Format("// %s %d frames; %d sequences; updated %s", filename, m_totFrames, GetTotSequences(), time.Format("%H:%M %A, %B %d, %Y")); + file->Writeln(commentLine); + // + // the Writeln functions I have to call don't handle "\n" chars properly because of being opened in binary mode + // (sigh), so I have to explicitly call the Writeln() functions to output CRs... :-( + // + file->Writeln("//"); + file->Writeln("// Format: enum, targetFrame, frameCount, loopFrame, frameSpeed"); + file->Writeln("//"); + } + + CSequence* curSequence = m_sequences; + while(curSequence != NULL) + { + curSequence->WriteExternal(this, file, bExportFormatIsQuake3Multiplayer); + curSequence = curSequence->GetNext(); + } + file->Delete(); + + if (HasGLA()) + { + unlink(filename); // zap it, since it's meaningless here (only has one seq/enum: the whole GLA) + } + else + { + bCFGWritten = true; + } + + return true; +} + +bool CModel::HasGLA() +{ + CSequence* curSequence = GetFirstSequence(); + while (curSequence) + { + if (curSequence->IsGLA()) + { + return true; + } + curSequence = curSequence->GetNext(); + } + + return false; +} + +// returns NULL else name string +// +LPCSTR CModel::GLAName(void) +{ + CSequence* curSequence = GetFirstSequence(); + while (curSequence) + { + if (curSequence->IsGLA()) + { + return curSequence->GetName(); + } + curSequence = curSequence->GetNext(); + } + + return NULL; +} + + +// should only be called once model is known to be in a sorted state or results are meaningless. +// +// either param can be NULL if not interested in them... +// +void CModel::GetMasterEnumBoundaryFrameNumbers(int *piFirstFrameAfterBOTH, int *piFirstFrameAfterTORSO) +{ + ENUMTYPE prevET = ET_INVALID; + int iFirstFrameAfterBOTH = 0; + int iFirstFrameAfterTORSO= 0; + + CSequence* curSequence = m_sequences; + while(curSequence != NULL) + { + ENUMTYPE thisET = curSequence->GetEnumType(); + + // update any frame markers first... + // + if (prevET == ET_BOTH && thisET != ET_BOTH) + { + iFirstFrameAfterBOTH = curSequence->GetTargetFrame(); + iFirstFrameAfterTORSO= curSequence->GetTargetFrame(); // set this as well in case there are no TORSOs at all + } + + if (prevET == ET_TORSO && thisET != ET_TORSO) + { + iFirstFrameAfterTORSO= curSequence->GetTargetFrame(); + } + + prevET = thisET; + + curSequence = curSequence->GetNext(); + } + // bug fix, if there are no leg frames at all, then we need to check if the ...AfterTORSO marker needs moving... + // + if (prevET == ET_BOTH) + { + iFirstFrameAfterBOTH = GetTotFrames(); + iFirstFrameAfterTORSO= GetTotFrames(); + } + if (prevET == ET_TORSO) + { + iFirstFrameAfterTORSO = GetTotFrames(); + } + + if (piFirstFrameAfterBOTH) + { + *piFirstFrameAfterBOTH = iFirstFrameAfterBOTH; + } + + if (piFirstFrameAfterTORSO) + { + *piFirstFrameAfterTORSO= iFirstFrameAfterTORSO; + } +} + +void CModel::Write(CTxtFile* file) +{ + file->Write("$"); + file->Writeln(CAssimilateDoc::GetKeyword(TK_AS_GRABINIT, TABLE_QDT)); + + if (!HasGLA()) + { + if (GetScale() != 1.0f) + { + file->Write("$"); + file->Write(CAssimilateDoc::GetKeyword(TK_AS_SCALE, TABLE_QDT)," "); + file->Writeln(va("%g",GetScale())); + } + + if (GetKeepMotion()) + { + file->Write("$"); + file->Writeln(CAssimilateDoc::GetKeyword(TK_AS_KEEPMOTION, TABLE_QDT)); + } + + for (int iPCJ=0; iPCJ < PCJList_GetEntries(); iPCJ++) + { + file->Write("$"); + file->Write(CAssimilateDoc::GetKeyword(TK_AS_PCJ, TABLE_QDT)," "); + file->Writeln( PCJList_GetEntry(iPCJ) ); + } + } + + CComment* curComment = m_comments; + while(curComment != NULL) + { + curComment->Write(file); + curComment = curComment->GetNext(); + } + + bool bFirstSeqWritten = false; + CSequence* curSequence = m_sequences; + while(curSequence != NULL) + { + curSequence->Write(file, !bFirstSeqWritten && GetPreQuat()); + curSequence = curSequence->GetNext(); + + bFirstSeqWritten = true; + } + + file->Writeln("$", CAssimilateDoc::GetKeyword(TK_AS_GRABFINALIZE, TABLE_QDT)); + + if (m_path != NULL) + { + file->Write("$", CAssimilateDoc::GetKeyword(GetConvertType(), TABLE_QDT)); + CString path = m_path; + + int loc = path.Find("/base"); + if (loc > -1) + { + path = path.Right(path.GetLength() - loc - 5); + loc = path.Find("/"); + path = path.Right(path.GetLength() - loc - 1); + } + if (!path.GetLength()) // check that some dopey artist hasn't use the name "base" on the right hand side + { + path = m_path; + } + + file->Write(" ", path, " "); + + // params stuff... + // + if (IsGhoul2()) + { + if (GetMakeSkin()) + { + file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_MAKESKIN, TABLE_CONVERT), " "); + } + if (GetSmooth()) + { + file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_SMOOTH, TABLE_CONVERT), " "); + } + if (GetLoseDupVerts()) + { + file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_LOSEDUPVERTS, TABLE_CONVERT), " "); + } + if (GetIgnoreBaseDeviations()) + { + file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_IGNOREBASEDEVIATIONS, TABLE_CONVERT), " "); + } + if (GetSkew90()) + { + file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_SKEW90, TABLE_CONVERT), " "); + } + if (GetNoSkew90()) + { + file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_NOSKEW90, TABLE_CONVERT), " "); + } + /* + if (GetSkelPath() && strlen(GetSkelPath())) + { + file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_SKEL, TABLE_CONVERT), " "); + file->Write(GetSkelPath(), " "); + } + */ + if (GetMakeSkelPath() && strlen(GetMakeSkelPath())) + { + file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_MAKESKEL, TABLE_CONVERT), " "); + file->Write(GetMakeSkelPath(), " "); + } + + // params below not used for ghoul2 + } + else + { + if (giLODLevelOverride) + { + file->Write(va("-lod %d ",giLODLevelOverride)); + } + //xxxxxxxxxxxxxxxxxxxxxxxxxxx + + file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_PLAYERPARMS, TABLE_CONVERT), " "); + file->Write(0);//m_skipStart+1); // ignore these, but don't want to update parser and have invalid prev files + file->Space(); + // file->Write(m_skipEnd); // this param no longer used + // file->Space(); + file->Write(0);//m_skipEnd);//max upper frames); + file->Space(); + // file->Write(m_headFrames); // this param no longer used + // file->Space(); + + } + + //xxxxxxxxxxxxxxxxxxxxxx + + if (m_originx || m_originy || m_originz) + { + file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_ORIGIN, TABLE_CONVERT), " "); + file->Write(m_originx); + file->Space(); + file->Write(m_originy); + file->Space(); + file->Write(m_originz); + } + + file->Writeln(); + } +} + + +///////////////////////////////////////////////////////////////////////////// +// CModelPropPage property page + +IMPLEMENT_DYNCREATE(CModelPropPage, CPropertyPage) + +CModelPropPage::CModelPropPage() : CPropertyPage(CModelPropPage::IDD) +{ + //{{AFX_DATA_INIT(CModelPropPage) + m_bSkew90 = FALSE; + m_bSmooth = FALSE; + m_strSkelPath = _T(""); + m_iOriginX = 0; + m_iOriginY = 0; + m_iOriginZ = 0; + m_fScale = 0.0f; + m_bMakeSkin = FALSE; + m_bLoseDupVerts = FALSE; + m_bMakeSkel = FALSE; + m_strNewPCJ = _T(""); + m_bKeepMotion = FALSE; + m_bPreQuat = FALSE; + //}}AFX_DATA_INIT + + m_PCJList.clear(); +} + +CModelPropPage::~CModelPropPage() +{ +} + +void CModelPropPage::DoDataExchange(CDataExchange* pDX) +{ + CPropertyPage::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CModelPropPage) + DDX_Check(pDX, IDC_CHECK_SKEW90, m_bSkew90); + DDX_Check(pDX, IDC_CHECK_SMOOTH_ALL, m_bSmooth); + DDX_Text(pDX, IDC_EDIT_SKELPATH, m_strSkelPath); + DDX_Text(pDX, IDC_EDIT_ORIGINX, m_iOriginX); + DDX_Text(pDX, IDC_EDIT_ORIGINY, m_iOriginY); + DDX_Text(pDX, IDC_EDIT_ORIGINZ, m_iOriginZ); + DDX_Text(pDX, IDC_EDIT_SCALE, m_fScale); + DDX_Check(pDX, IDC_CHECK_MAKESKIN, m_bMakeSkin); + DDX_Check(pDX, IDC_CHECK_LOSEDUPVERTS, m_bLoseDupVerts); + DDX_Check(pDX, IDC_CHECK_MAKESKEL, m_bMakeSkel); + DDX_Text(pDX, IDC_EDIT_PCJ, m_strNewPCJ); + DDX_Check(pDX, IDC_CHECK_KEEPMOTIONBONE, m_bKeepMotion); + DDX_Check(pDX, IDC_CHECK_PREQUAT, m_bPreQuat); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CModelPropPage, CPropertyPage) + //{{AFX_MSG_MAP(CModelPropPage) + ON_BN_CLICKED(IDC_CHECK_MAKESKEL, OnCheckMakeskel) + ON_BN_CLICKED(IDC_BUTTON_DELPCJ, OnButtonDelpcj) + ON_BN_CLICKED(IDC_BUTTON_PCJ, OnButtonPcj) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CModelPropPage message handlers + +void CModelPropPage::OnOK() +{ + if (!m_bMakeSkel && !m_strSkelPath.IsEmpty()) + { + // actually, this SHOULD just cancel the OnOK and stay in the dialog (according to the windoze docs, + // but of course being microsoft, it doesn't bloody work, and just cancels, so I adapted the message... + // + if (!GetYesNo("Warning, you have a make-skeleton path entered, but 'makeskel' is OFF, this will lose that path info when this dialog closes.\n\nProceed? ('NO' is the same as clicking 'CANCEL')")) + return; + } +/* +// these are all irrelevant now... +// + m_model->SetHeadFrames(m_headFrames); + m_model->SetSkipEnd(m_skipEnd); + m_model->SetSkipStart(m_skipStart); +*/ + m_model->SetOriginX(m_iOriginX); + m_model->SetOriginY(m_iOriginY); + m_model->SetOriginZ(m_iOriginZ); + + m_model->SetSkew90(!!m_bSkew90); + m_model->SetSmooth(!!m_bSmooth); + m_model->SetLoseDupVerts(!!m_bLoseDupVerts); + m_model->SetMakeSkin(!!m_bMakeSkin); +// m_model->SetSkelPath(m_bMakeSkel?(LPCSTR)m_strSkelPath:""); + m_model->SetMakeSkelPath(m_bMakeSkel?(LPCSTR)m_strSkelPath:""); + + m_model->SetScale(m_fScale); + m_model->SetKeepMotion(!!m_bKeepMotion); + m_model->SetPreQuat(!!m_bPreQuat); + + m_model->PCJList_Clear(); + for (int i=0; iPCJList_AddEntry(GetPCJEntry(i)); + } + + + *m_soilFlag = true; + + CPropertyPage::OnOK(); +} + +void CModelPropPage::PopulatePCJList(void) +{ + CListBox *pListBox = (CListBox *) GetDlgItem(IDC_LIST_PCJ); + if (pListBox) + { + pListBox->ResetContent(); + + for (int i = 0; iInsertString(-1, (LPCSTR) m_PCJList[i]); + } + } +} + +BOOL CModelPropPage::OnInitDialog() +{ + CPropertyPage::OnInitDialog(); +/* +// these are all irrelevant now... +// + m_headFrames = m_model->GetHeadFrames(); + m_skipEnd = m_model->GetSkipEnd(); + m_skipStart = m_model->GetSkipStart(); + m_totFrames.Format("%d", m_model->GetTotFrames()); + m_totSequences.Format("%d", m_model->GetTotSequences()); +*/ + m_iOriginX = m_model->GetOriginX(); + m_iOriginY = m_model->GetOriginY(); + m_iOriginZ = m_model->GetOriginZ(); + + m_bSkew90 = m_model->GetSkew90(); + m_bSmooth = m_model->GetSmooth(); + m_bLoseDupVerts = m_model->GetLoseDupVerts(); + m_bMakeSkin = m_model->GetMakeSkin(); + m_strSkelPath = m_model->GetMakeSkelPath(); + if (m_strSkelPath.IsEmpty()) + { +// m_strSkelPath = m_model->GetSkelPath(); + } + m_bMakeSkel = (m_model->GetMakeSkelPath() && strlen(m_model->GetMakeSkelPath()))?TRUE:FALSE; + + m_fScale = m_model->GetScale(); + m_bKeepMotion = m_model->GetKeepMotion(); + m_bPreQuat = m_model->GetPreQuat(); + + PopulatePCJList(); + + UpdateData(DATA_TO_DIALOG); + + HandleItemGreying(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + + +void CModelPropPage::DelPCJEntry(int iIndex) +{ + if (iIndex < m_PCJList.size()) + { + m_PCJList.erase(m_PCJList.begin() + iIndex); + } +} + +void CModelPropPage::AddPCJEntry(LPCSTR psPCJName) +{ + CString strTemp(psPCJName); + strTemp.Replace(" ",""); + strTemp.Replace("\t",""); + + m_PCJList.push_back(strTemp); +} + +int CModelPropPage::GetPCJEntries(void) +{ + return m_PCJList.size(); +} + +LPCSTR CModelPropPage::GetPCJEntry(int iIndex) +{ + if (iIndex < m_PCJList.size()) + { + return (LPCSTR) m_PCJList[iIndex]; + } + + assert(0); + return NULL; +} + +void CModelPropPage::HandleItemGreying(void) +{ + UpdateData(DIALOG_TO_DATA); + GetDlgItem(IDC_EDIT_SCALE)->EnableWindow(m_bMakeSkel); + GetDlgItem(IDC_EDIT_SKELPATH)->EnableWindow(m_bMakeSkel); + GetDlgItem(IDC_STATIC_SKELPATH)->EnableWindow(m_bMakeSkel); + + GetDlgItem(IDC_PCJ_STATIC)->EnableWindow(m_bMakeSkel); + GetDlgItem(IDC_LIST_PCJ)->EnableWindow(m_bMakeSkel); + GetDlgItem(IDC_EDIT_PCJ)->EnableWindow(m_bMakeSkel); + GetDlgItem(IDC_BUTTON_PCJ)->EnableWindow(m_bMakeSkel); + GetDlgItem(IDC_BUTTON_DELPCJ)->EnableWindow(m_bMakeSkel); + GetDlgItem(IDC_CHECK_KEEPMOTIONBONE)->EnableWindow(m_bMakeSkel); + + Invalidate(); +} + +void CModelPropPage::OnCheckMakeskel() +{ + UpdateData(DIALOG_TO_DATA); + + // first time we turn it on, we should make up a reasonable default name... + // + if (m_bMakeSkel && m_strSkelPath.IsEmpty()) + { + // basically I'm just going to use the dir name as the GLA name base as well... + // + CString strSuggestedPath(m_model->GetPath()); // eg. "models/players/blah/root" + int iLoc = strSuggestedPath.ReverseFind('/'); + if (iLoc>=0) + { + strSuggestedPath = strSuggestedPath.Left(iLoc); // eg. "models/players/blah" + + iLoc = strSuggestedPath.ReverseFind('/'); + if (iLoc >= 0) + { + CString strDir(strSuggestedPath.Mid(iLoc+1)); // eg. "blah" + + strSuggestedPath += "/"; + strSuggestedPath += strDir; // eg. "models/players/blah/blah" + + m_strSkelPath = strSuggestedPath; + + UpdateData(DATA_TO_DIALOG); + } + } + } + + HandleItemGreying(); +} + +void CModelPropPage::OnButtonPcj() +{ + UpdateData(DIALOG_TO_DATA); + + if (!m_strNewPCJ.IsEmpty()) + { + AddPCJEntry(m_strNewPCJ); + PopulatePCJList(); + + UpdateData(DIALOG_TO_DATA); + m_strNewPCJ = ""; + UpdateData(DATA_TO_DIALOG); + } +} + +void CModelPropPage::OnButtonDelpcj() +{ + CListBox *pListBox = (CListBox *) GetDlgItem(IDC_LIST_PCJ); + if (pListBox) + { + int iCurSel = pListBox->GetCurSel(); + if (iCurSel != LB_ERR) + { + DelPCJEntry(iCurSel); + PopulatePCJList(); + } + } +} diff --git a/utils/Assimilate/Model.h b/utils/Assimilate/Model.h new file mode 100644 index 0000000..6c253f9 --- /dev/null +++ b/utils/Assimilate/Model.h @@ -0,0 +1,203 @@ +// Model.h + + +#pragma warning( disable : 4786 ) // identifier was truncated + +#include +#include +#include +#include +using namespace std; + + +class CModel +{ +public: + CModel(); + virtual ~CModel(); + + virtual void Delete(); + static CModel* Create(CComment* comments = NULL); + + void Write(CTxtFile* file); + bool WriteExternal(bool bPromptForNames, bool& bCFGWritten); + bool HasGLA(void); + LPCSTR GLAName(void); + + void SetNext(CModel* next); + CModel* GetNext(); + + bool DoProperties(); + + void AddComment(CComment* comment); + CComment* GetFirstComment(); + CComment* ExtractComments(); + + bool ContainsFile(LPCSTR psFilename); + int AnimEnumInUse(LPCSTR psAnimEnum); + void AddSequence(CSequence* sequence); + void DeleteSequence(CSequence* deleteSequence); + CSequence* GetFirstSequence(); + void Resequence(bool bReScanASEFiles = false); + int GetTotSequences(); + int GetTotMasterSequences(); + void ReOrderSequences(); + void GetMasterEnumBoundaryFrameNumbers(int *piFirstFrameAfterBOTH, int *piFirstFrameAfterTORSO); + + LPCTSTR GetName(); + void SetName(LPCTSTR name); + void DeriveName(LPCTSTR fromname); + void SetPath(LPCTSTR path); + LPCTSTR GetPath(); + + void SetOrigin(int x, int y, int z); + void SetOriginX(int x); + void SetOriginY(int y); + void SetOriginZ(int z); + int GetOriginX(); + int GetOriginY(); + int GetOriginZ(); + + void SetParms(int skipStart, int skipEnd, int totFrames, int headFrames); + void SetTotFrames(int value); + int GetTotFrames(); + + void SetUserSelectionBool(bool bSelected = true); + bool GetUserSelectionBool(); + + void SetConvertType(int iType); + int GetConvertType(void); + bool IsGhoul2(void); + + void SetKeepMotion(bool bKeepMotion); + bool GetKeepMotion(void); + void SetSmooth(bool bSmooth); + void SetLoseDupVerts(bool bLoseDupVerts); + void SetMakeSkin(bool bMakeSkin); + void SetIgnoreBaseDeviations(bool bIgnore); + void SetSkew90(bool bSkew90); + void SetNoSkew90(bool bNoSkew90); +// void SetSkelPath(LPCSTR psPath); + void SetMakeSkelPath(LPCSTR psPath); + bool GetSmooth(void); + bool GetLoseDupVerts(void); + bool GetMakeSkin(void); + bool GetIgnoreBaseDeviations(void); + bool GetSkew90(void); + bool GetNoSkew90(void); +// LPCSTR GetSkelPath(void); + LPCSTR GetMakeSkelPath(void); + void SetScale(float fScale); + float GetScale(void); + bool GetPreQuat(void); + void SetPreQuat(bool bPreQuat); + + void PCJList_Clear(); + void PCJList_AddEntry(LPCSTR psEntry); + int PCJList_GetEntries(); + LPCSTR PCJList_GetEntry(int iIndex); + +protected: + void Init(CComment* comments); + + CModel* m_next; + CComment* m_comments; + char* m_name; + char* m_path; + CSequence* m_sequences; + CSequence* m_curSequence; + + int m_totFrames; + int m_headFrames; + int m_originx; + int m_originy; + int m_originz; + + bool m_bCurrentUserSelection; + + int m_iType; // eg TK_AS_CONVERT, TK_AS_CONVERTMDX, TK_AS_CONVERTMDX_NOASK, + bool m_bSmooth; + bool m_bLoseDupVerts; + bool m_bKeepMotion; + bool m_bMakeSkin; + float m_fScale; + bool m_bIgnoreBaseDeviations; + bool m_bSkew90; + bool m_bNoSkew90; + char* m_psSkelPath; + char* m_psMakeSkelPath; + vector m_vPCJList; + bool m_bPreQuat; +}; +///////////////////////////////////////////////////////////////////////////// +// CModelPropPage dialog + +class CModelPropPage : public CPropertyPage +{ + DECLARE_DYNCREATE(CModelPropPage) + +// Construction +public: + CModelPropPage(); + ~CModelPropPage(); + + CModel* m_model; + bool* m_soilFlag; + +// Dialog Data + //{{AFX_DATA(CModelPropPage) + enum { IDD = IDD_PP_MODEL }; + BOOL m_bSkew90; + BOOL m_bSmooth; + CString m_strSkelPath; + int m_iOriginX; + int m_iOriginY; + int m_iOriginZ; + float m_fScale; + BOOL m_bMakeSkin; + BOOL m_bLoseDupVerts; + BOOL m_bMakeSkel; + CString m_strNewPCJ; + BOOL m_bKeepMotion; + BOOL m_bPreQuat; + //}}AFX_DATA + +public: + void AddPCJEntry(LPCSTR psPCJName); + void DelPCJEntry(int iIndex); + + int GetPCJEntries(void); + LPCSTR GetPCJEntry(int iIndex); + + +protected: + vector m_PCJList; + + +// Overrides + // ClassWizard generate virtual function overrides + //{{AFX_VIRTUAL(CModelPropPage) + public: + virtual void OnOK(); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + // Generated message map functions + //{{AFX_MSG(CModelPropPage) + virtual BOOL OnInitDialog(); + afx_msg void OnCheckMakeskel(); + afx_msg void OnButtonDelpcj(); + afx_msg void OnButtonPcj(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + + void HandleItemGreying(void); + void PopulatePCJList(void); +}; + +extern bool gbReportMissingASEs; +extern int giFixUpdatedASEFrameCounts; + diff --git a/utils/Assimilate/Module.cpp b/utils/Assimilate/Module.cpp new file mode 100644 index 0000000..c9ea9e6 --- /dev/null +++ b/utils/Assimilate/Module.cpp @@ -0,0 +1,106 @@ +// CModule.cpp + +#include "Module.h" + +// CMoudle + +CModule::CModule() +{ + m_errHandler = NULL; + m_lastError = 0; + m_lastErrMsg = NULL; +} + +CModule::~CModule() +{ + if (m_errHandler != NULL) + { + m_errHandler->SetModule(NULL); + } + if (m_lastErrMsg != NULL) + { + free(m_lastErrMsg); + m_lastErrMsg = NULL; + } +} + +CErrHandler* CModule::InstallErrHandler(CErrHandler* errHandler) +{ + CErrHandler* retval = m_errHandler; + m_errHandler = errHandler; + return retval; +} + +void CModule::SetErrHandler(CErrHandler* errHandler) +{ + errHandler->SetModule(this); +} + +void CModule::ReportError(int theError, LPCTSTR errString) +{ + m_lastError = theError; + if (m_lastErrMsg != NULL) + { + free(m_lastErrMsg); + m_lastErrMsg = NULL; + } + if (errString != NULL) + { + m_lastErrMsg = (char*)malloc(strlen(errString) + 1); + strcpy(m_lastErrMsg, errString); + } + if (m_errHandler != NULL) + { + m_errHandler->Error(theError, errString); + } +} + +int CModule::GetLastError(BOOL reset) +{ + int retval = m_lastError; + if (reset) + { + if (m_lastErrMsg != NULL) + { + free(m_lastErrMsg); + m_lastErrMsg = NULL; + } + m_lastError = 0; + } + return retval; +} + +char* CModule::GetLastErrorMessage() +{ + return m_lastErrMsg; +} + +// CErrHandler + +CErrHandler::CErrHandler() +{ + Init(); +} + +void CErrHandler::Init() +{ + m_module = NULL; + m_oldHandler = NULL; +} + +CErrHandler::~CErrHandler() +{ + if (m_module != NULL) + { + m_module->InstallErrHandler(m_oldHandler); + } +} + +void CErrHandler::SetModule(CModule* module) +{ + m_module = module; + if (module != NULL) + { + m_oldHandler = m_module->InstallErrHandler(this); + } +} diff --git a/utils/Assimilate/Module.h b/utils/Assimilate/Module.h new file mode 100644 index 0000000..03771b2 --- /dev/null +++ b/utils/Assimilate/Module.h @@ -0,0 +1,46 @@ +// CModule.h +#ifndef USES_MODULES +#define USES_MODULES + +#include + +class CModule; + +class CErrHandler +{ +public: + CErrHandler(); + virtual ~CErrHandler(); + + void SetModule(CModule* module); + virtual void Error(int theError, LPCTSTR errString) {}; + +protected: + virtual void Init(); + + CModule* m_module; + CErrHandler* m_oldHandler; +}; + +class CModule +{ +public: + CModule(); + virtual ~CModule(); + + void SetErrHandler(CErrHandler* errHandler); + void ReportError(int theError, LPCTSTR errString); + int GetLastError(BOOL reset = TRUE); + char* GetLastErrorMessage(); + + friend class CErrHandler; + +protected: + CErrHandler* InstallErrHandler(CErrHandler* errHandler); + + CErrHandler* m_errHandler; + int m_lastError; + char* m_lastErrMsg; +}; + +#endif // USES_MODULES \ No newline at end of file diff --git a/utils/Assimilate/Resource.h b/utils/Assimilate/Resource.h new file mode 100644 index 0000000..94e386a --- /dev/null +++ b/utils/Assimilate/Resource.h @@ -0,0 +1,130 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by Assimilate.rc +// +#define IDYESTOALL 3 +#define IDNOTOALL 4 +#define IDD_ABOUTBOX 100 +#define IDD_PP_MODEL 107 +#define IDD_PP_SEQUENCE 108 +#define IDD_PP_PROPERTIES 109 +#define IDR_MAINFRAME 128 +#define IDR_ASSIMITYPE 129 +#define IDD_ANIMPICKER 131 +#define IDD_DIALOG_YESNOYESNOALL 134 +#define IDD_BUILD_ALL 135 +#define IDB_TREEIMAGES 155 +#define IDC_SKIPSTART 1000 +#define IDC_SKIPEND 1001 +#define IDC_TOTFRAMES 1002 +#define IDC_HEADFRAMES 1003 +#define IDC_ORIGINX 1004 +#define IDC_ORIGINY 1005 +#define IDC_ORIGINZ 1006 +#define IDC_TOTSEQUENCES 1007 +#define IDC_STARTFRAME 1008 +#define IDC_FRAMECOUNT 1009 +#define IDC_FRAMESPEED 1010 +#define IDC_TARGETFRAME 1011 +#define IDC_STARTFRAME2 1011 +#define IDC_PATH 1012 +#define IDC_ENUM 1013 +#define IDC_LOOPFRAME 1013 +#define IDC_ENUM_BROWSE 1014 +#define IDC_FRAMECOUNT2 1014 +#define IDC_BUFFSIZE 1015 +#define IDC_FRAMESPEED2 1015 +#define IDC_QDATA 1016 +#define IDC_LOOPFRAME2 1016 +#define IDC_QDATA_BROWSE 1017 +#define IDC_STARTFRAME3 1017 +#define IDC_QUAKEDIR 1018 +#define IDC_FRAMECOUNT3 1018 +#define IDC_FRAMESPEED3 1019 +#define IDC_LOOPFRAME3 1020 +#define IDC_STARTFRAME4 1021 +#define IDC_FRAMECOUNT4 1022 +#define IDC_FRAMESPEED4 1023 +#define IDC_LOOPFRAME4 1024 +#define IDC_STARTFRAME5 1025 +#define IDC_FRAMECOUNT5 1026 +#define IDC_FRAMESPEED5 1027 +#define IDC_LOOPFRAME5 1028 +#define IDC_BUTTON_CHOOSEANIMATIONENUM 1030 +#define IDC_BUTTON_CHOOSEANIMATIONENUM2 1031 +#define IDC_BUTTON_CHOOSEANIMATIONENUM3 1032 +#define IDC_BUTTON_CHOOSEANIMATIONENUM4 1033 +#define IDC_BUTTON_CHOOSEANIMATIONENUM5 1034 +#define IDC_LIST_LEGS 1037 +#define IDC_LIST_TORSO 1038 +#define IDC_LIST_BOTH 1039 +#define IDC_EDIT_ANIMATIONENUM 1049 +#define IDC_STATIC_COMMENT 1050 +#define IDC_EDIT_ANIMATIONENUM2 1050 +#define IDC_BUTTON_DEFAULTS 1051 +#define IDC_EDIT_ANIMATIONENUM3 1051 +#define IDC_EDIT_ANIMATIONENUM4 1052 +#define IDC_BUTTON_DEFAULTS_MULTI 1052 +#define IDC_EDIT_ANIMATIONENUM5 1053 +#define IDC_BUTTON_CLEARANIMATIONENUM 1058 +#define IDC_BUTTON_CLEARANIMATIONENUM2 1059 +#define IDC_STATIC1 1059 +#define IDC_BUTTON_CLEARANIMATIONENUM3 1060 +#define IDC_STATIC2 1060 +#define IDC_CHECK_MULTIPLAYER 1060 +#define IDC_BUTTON_CLEARANIMATIONENUM4 1061 +#define IDC_STATIC3 1061 +#define IDC_CHECK_SMOOTH_ALL 1061 +#define IDC_BUTTON_CLEARANIMATIONENUM5 1062 +#define IDC_STATIC4 1062 +#define IDC_STATIC5 1063 +#define IDC_STATIC6 1064 +#define IDC_CHECK_SKEW90 1064 +#define IDC_EDIT_SKELPATH 1065 +#define IDC_EDIT_ORIGINX 1066 +#define IDC_EDIT_ORIGINY 1067 +#define IDC_EDIT_BUILDPATH 1067 +#define IDC_EDIT_ORIGINZ 1068 +#define IDC_CHECK_PREVALIDATECARS 1068 +#define IDC_CHECK_MAKESKIN 1069 +#define IDC_EDIT_SCALE 1070 +#define IDC_CHECK_GENLOOPFRAME 1071 +#define IDC_CHECK_LOSEDUPVERTS 1071 +#define IDC_CHECK_KEEPMOTIONBONE 1072 +#define IDC_CHECK_MAKESKEL 1073 +#define IDC_STATIC_SKELPATH 1074 +#define IDC_CHECK_FILTEROUTUSED 1075 +#define IDC_CHECK_PREQUAT 1075 +#define IDC_LIST_PCJ 1076 +#define IDC_EDIT_PCJ 1077 +#define IDC_BUTTON_PCJ 1078 +#define IDC_BUTTON_DELPCJ 1079 +#define IDC_PCJ_STATIC 1080 +#define IDM_ADDFILES 32771 +#define IDM_EXTERNAL 32772 +#define IDM_RESEQUENCE 32773 +#define IDM_PROPERTIES 32774 +#define ID_VIEW_ANIMENUMS 32775 +#define ID_VIEW_FRAMEDETAILS 32776 +#define IDM_BUILD 32777 +#define IDM_VALIDATE 32778 +#define IDM_BUILD_MULTILOD 32779 +#define IDM_VALIDATE_MULTILOD 32780 +#define IDM_CARWASH 32781 +#define ID_EDIT_BUILDALL 32782 +#define IDM_EDIT_BUILDDEPENDANT 32783 +#define ID_VIEW_FRAMEDETAILSONADDITIONALSEQUENCES 32784 +#define ID_BUTTON_RESEQUENCE 32785 +#define ID_EDIT_LAUNCHMODVIEWONCURRENT 32787 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_3D_CONTROLS 1 +#define _APS_NEXT_RESOURCE_VALUE 136 +#define _APS_NEXT_COMMAND_VALUE 32789 +#define _APS_NEXT_CONTROL_VALUE 1082 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/utils/Assimilate/Sequences.cpp b/utils/Assimilate/Sequences.cpp new file mode 100644 index 0000000..0292ca7 --- /dev/null +++ b/utils/Assimilate/Sequences.cpp @@ -0,0 +1,1792 @@ +// Sequences.cpp + +#include "Stdafx.h" +#include "Includes.h" +#include "gla.h" +#include "matrix4.h" +#include "xsiimp.h" + + +#define ONKILLFOCUS UpdateData(DIALOG_TO_DATA) + +keywordArray_t CSequence_s_Symbols[] = +{ + "*", TK_ASTERISK, + NULL, TK_EOF, +}; + +keywordArray_t CSequence_s_Keywords[] = +{ + "SCENE_FRAMESPEED", TK_ASE_FRAMESPEED, + "SCENE_FIRSTFRAME", TK_ASE_FIRSTFRAME, + "SCENE_LASTFRAME", TK_ASE_LASTFRAME, + NULL, TK_EOF, +}; + +CSequence::CSequence() +{ +} + +CSequence::~CSequence() +{ +} + +void CSequence::Delete() +{ + while(m_comments != NULL) + { + CComment* curComment = m_comments; + m_comments = curComment->GetNext(); + curComment->Delete(); + } + if (m_path != NULL) + { + free(m_path); + m_path = NULL; + } + if (m_name != NULL) + { + free(m_name); + m_name = NULL; + } + if (m_action != NULL) + { + free(m_action); + m_action = NULL; + } + if (m_sound != NULL) + { + free(m_sound); + m_sound = NULL; + } + // now a cstring +// if (m_enum != NULL) +// { +// free(m_enum); +// m_enum = NULL; +// } + + for (int i=0; iDelete(); + AdditionalSeqs[i] = NULL; + } + } + + delete this; +} + +CSequence* CSequence::CreateFromFile(LPCTSTR path, CComment* comments) +{ + CSequence* retval = new CSequence(); + CString name = path; + name.Replace('\\', '/'); + name.MakeLower(); + retval->_Init(false,!!(strstr(path,".gla")),name, 0, 0, 0, iDEFAULTSEQFRAMESPEED,iDEFAULTSEQFRAMESPEED,comments); // (noticed) this fills in certain fields wrongly until the next 2 lines correct them + retval->DeriveName(); + retval->ReadHeader(); + // + // at this point the m_path member is wrong because it contains the full path including "\quake\...", so... + // + CString newPath = retval->GetPath(); + Filename_RemoveBASEQ(newPath); + retval->SetPath(newPath); + return retval; +} + +void CSequence::ReadHeader() +{ + int iStartFrame, iFrameCount, iFrameSpeed; + + CString nameASE = ((CAssimilateApp*)AfxGetApp())->GetQuakeDir(); + nameASE+= GetPath(); + + ReadASEHeader( nameASE, iStartFrame, iFrameCount, iFrameSpeed); + + SetStartFrame( iStartFrame ); + SetFrameCount( iFrameCount ); + SetFrameSpeed( iFrameSpeed ); // iDEFAULTSEQFRAMESPEED + SetFrameSpeedFromHeader( iFrameSpeed ); +} + + +void ReadXSIHeader(LPCSTR psFilename, int &iStartFrame, int &iFrameCount, int &iFrameSpeed) +{ + OutputDebugString(va("ReadXSIHeader(): %s\n",psFilename)); + + // let's try the fast way of reading first, by scanning directly for the header value if it's a 3.0 file... + // + { + bool bGotInfo = false; + FILE *fp = fopen(psFilename,"ra"); + if (fp) + { + fseek(fp,0,SEEK_END); + int iLen = ftell(fp); + if (iLen>1024)//2048) + iLen=1024;//2048; // findmeste + char *psFileBin = new char[iLen+1]; + assert(psFileBin); + psFileBin[iLen]='\0'; + fseek(fp,0,SEEK_SET); + if (fread(psFileBin,1,iLen,fp) == (size_t)iLen) + { + // I'm not sure how to tie this in with Gil's rather-complex XSI reader, so do this seperately... + // + { + /* Field I want to read looks like this: + + SI_Scene testdeath { + "FRAMES", + 1.000000, + 62.000000, + 30.000000, + } + + */ + + char *psSearch = strstr(psFileBin,"SI_Scene"); + + if (psSearch) + { + while (psSearch != (psFileBin+iLen) && *psSearch != '{') // '}' don't bugger-up brace-matching + { + psSearch++; + } + if (*psSearch++=='{') // '}' don't bugger-up brace-matching + { + char sScanBuffer[2048]={0}; + + for (int i=0; i FrameCountAndSpeed_t; +typedef map ASECachedInfo_t; +ASECachedInfo_t ASECachedInfo; +// +// this actually reads XSI or GLA headers... historical mutation strikes again... +// +static void ReadASEHeader_Actual(LPCSTR psFilename, int &iStartFrame, int &iFrameCount, int &iFrameSpeed, bool bReadingGLA, bool bCanSkipXSIRead /* = false */) +{ + // since the XSI loader is so damn slow and flakey I'm going to have to cache the info to avoid re-reading... + // + + // do we have it in the cache?... + // + if (strstr(psFilename,".xsi") || strstr(psFilename,".XSI")) + { + ASECachedInfo_t::iterator iter = ASECachedInfo.find(psFilename); + if (iter != ASECachedInfo.end()) + { + iStartFrame = 0; + iFrameCount = (*iter).second.first; + iFrameSpeed = (*iter).second.second; + return; + } + } + + // is it a GLA file?... + // + if (bReadingGLA) + { + char sTemp[1024]; + strcpy(sTemp,psFilename); + if (!(strstr(psFilename,".gla") || strstr(psFilename,".GLA"))) + { + strcat(sTemp,".gla"); + } + + iStartFrame = 0; + iFrameCount = GLA_ReadHeader(sTemp); + iFrameSpeed = 20; // any old value for GLA file + return; + } + + + // it's not in the cache, but we may be able to avoid having to read it under some circumstances... + // + bool bXSIShouldBeRead = true; + + if (gbCarWash_DoingScan) + { + bCanSkipXSIRead = false; // stop it asking the question + bXSIShouldBeRead= gbCarWash_YesToXSIScan; + } + + if ( (strstr(psFilename,".xsi") || strstr(psFilename,".XSI")) + && bCanSkipXSIRead + ) + { + if (!gbSkipXSIRead && !gbSkipXSIRead_QuestionAsked) + { + gbSkipXSIRead_QuestionAsked = true; + gbSkipXSIRead = !GetYesNo(va("Model file: \"%s\"\n\n... is an XSI, and they can be damn slow to read in\n\nDo you want to scan all the XSIs?",psFilename)); + } + + bXSIShouldBeRead = !gbSkipXSIRead; + } + + if (strstr(psFilename,".xsi") || strstr(psFilename,".XSI")) + { + if (bXSIShouldBeRead) + { + ReadXSIHeader(psFilename, iStartFrame, iFrameCount, iFrameSpeed); + + if (iFrameCount!=0) + { + // cache it for future... + // + ASECachedInfo[psFilename] = FrameCountAndSpeed_t(iFrameCount,iFrameSpeed); + } + } + return; + } + + // it must be an ASE file then instead.... + // + CTokenizer* tokenizer = CTokenizer::Create(); + tokenizer->AddParseFile(psFilename, ((CAssimilateApp*)AfxGetApp())->GetBufferSize()); + tokenizer->SetSymbols(CSequence_s_Symbols); + tokenizer->SetKeywords(CSequence_s_Keywords); + + CToken* curToken = tokenizer->GetToken(); + while(curToken != NULL) + { + switch (curToken->GetType()) + { + case TK_EOF: + curToken->Delete(); + curToken = NULL; + break; + case TK_ASTERISK: + curToken->Delete(); + curToken = tokenizer->GetToken(); + switch(curToken->GetType()) + { + case TK_ASE_FIRSTFRAME: + curToken->Delete(); + curToken = tokenizer->GetToken(); + if (curToken->GetType() == TK_INTEGER) + { + iStartFrame = curToken->GetIntValue(); + curToken->Delete(); + curToken = tokenizer->GetToken(); + } + break; + case TK_ASE_LASTFRAME: + curToken->Delete(); + curToken = tokenizer->GetToken(); + if (curToken->GetType() == TK_INTEGER) + { + iFrameCount = curToken->GetIntValue() + 1; + curToken->Delete(); + curToken = NULL; // tells outer loop to finish + } + break; + case TK_ASE_FRAMESPEED: + curToken->Delete(); + curToken = tokenizer->GetToken(); + if (curToken->GetType() == TK_INTEGER) + { + iFrameSpeed = curToken->GetIntValue(); + curToken->Delete(); + curToken = tokenizer->GetToken(); + } + break; + case TK_EOF: + curToken->Delete(); + curToken = NULL; + break; + default: + curToken->Delete(); + curToken = tokenizer->GetToken(); + break; + } + break; + default: + curToken->Delete(); + curToken = tokenizer->GetToken(); + break; + } + } + tokenizer->Delete(); + + iFrameCount -= iStartFrame; + iStartFrame = 0; +} + +void ReadASEHeader(LPCSTR psFilename, int &iStartFrame, int &iFrameCount, int &iFrameSpeed, bool bReadingGLA, bool bCanSkipXSIRead /* = false */) +{ + StatusText(va("Reading Header: \"%s\"",psFilename)); + ReadASEHeader_Actual(psFilename, iStartFrame, iFrameCount, iFrameSpeed, bReadingGLA, bCanSkipXSIRead); + StatusText(NULL); +} + +// this does NOT update any member vars, so can be called from outside this class, it just reads member vars to +// find out what tables to pass to tokenizer... +// +void CSequence::ReadASEHeader(LPCSTR psFilename, int &iStartFrame, int &iFrameCount, int &iFrameSpeed, bool bCanSkipXSIRead /* = false */) +{ + ::ReadASEHeader(psFilename, iStartFrame, iFrameCount, iFrameSpeed, m_bIsGLA, bCanSkipXSIRead); +} + +bool CSequence::Parse() +{ + CASEFile* aseFile = CASEFile::Create(m_path); + aseFile->Parse(); + aseFile->Delete(); + return true; +} + +CSequence* CSequence::_Create(bool bGenLoopFrame, bool bIsGLA, LPCTSTR path, int startFrame, int targetFrame, int frameCount, int frameSpeed, int frameSpeedFromHeader, CComment* comments) +{ + CSequence* retval = new CSequence(); + retval->_Init(bGenLoopFrame, bIsGLA, path, startFrame, targetFrame, frameCount, frameSpeed, frameSpeedFromHeader, comments); + return retval; +} + +bool CSequence::DoProperties() +{ + bool dirty = false; + + if (IsGLA()) + { + InfoBox("You can't edit the properties of a GLA file"); + } + else + { + CPropertySheet* propSheet = new CPropertySheet(m_name); + + CSequencePropPage* propPage = new CSequencePropPage(); + propPage->m_sequence = this; + propPage->m_soilFlag = &dirty; + propSheet->AddPage(propPage); + + propSheet->DoModal(); + + delete propPage; + delete propSheet; + } + + return dirty; +} + +CSequence* CSequence::GetNext() +{ + return m_next; +} + +void CSequence::SetNext(CSequence* next) +{ + m_next = next; +} + +bool CSequence::ValidEnum() +{ + return m_validEnum; +} + +void CSequence::SetValidEnum(bool value) +{ + m_validEnum = value; +} + +LPCTSTR CSequence::GetPath() +{ + return m_path; +} + +void CSequence::SetPath(LPCTSTR path) +{ + if (m_path != NULL) + { + free(m_path); + } + if (path == NULL) + { + m_path = NULL; + } + else + { + m_path = (char*)malloc(strlen(path) + 1); + strcpy(m_path, path); + } +} + +int CSequence::GetStartFrame() +{ + return m_startFrame; +} + +void CSequence::SetStartFrame(int frame) +{ + m_startFrame = frame; +} + +int CSequence::GetTargetFrame() +{ + return m_targetFrame; +} + +void CSequence::SetTargetFrame(int frame) +{ + m_targetFrame = frame; +} + +int CSequence::GetFrameCount() +{ + return m_frameCount; +} + +void CSequence::SetFrameCount(int count) +{ + m_frameCount = count; +} + +int CSequence::GetFrameSpeedFromHeader() +{ + return m_iFrameSpeedFromHeader; +} + +void CSequence::SetFrameSpeedFromHeader(int speed) +{ + m_iFrameSpeedFromHeader = speed; +} + +int CSequence::GetFrameSpeed() +{ + return m_frameSpeed; +} + +void CSequence::SetFrameSpeed(int speed) +{ + m_frameSpeed = speed; +} + +int CSequence::GetLoopFrame() +{ + return m_loopFrame; +} + +void CSequence::SetLoopFrame(int loop) +{ + m_loopFrame = loop; +} + +bool CSequence::GetGenLoopFrame(void) +{ + return m_bGenLoopFrame; +} + +void CSequence::SetGenLoopFrame(bool bGenLoopFrame) +{ + m_bGenLoopFrame = bGenLoopFrame; +} + + +int CSequence::GetFill() +{ + return m_fill; +} + +void CSequence::SetFill(int value) +{ + m_fill = value; +} + +// this is now recursive, so watch what you're doing... +// +void CSequence::_Init(bool bGenLoopFrame, bool bIsGLA, LPCTSTR path, int startFrame, int targetFrame, int frameCount, int frameSpeed, int frameSpeedFromHeader, CComment* comments) +{ + static bool bHere = false; + + m_path = NULL; + m_next = NULL; + m_name = NULL; + m_fill = -1; + // another Jake thing, not sure if this'll stay in when valid StarWars enums are available +#if 0 + m_enum = "";//NULL; +#else + m_enum = path; + if (!m_enum.IsEmpty()) + { + Filename_BaseOnly(m_enum); + m_enum.MakeUpper(); + } +#endif + m_sound = NULL; + m_action = NULL; + m_comments = comments; + m_validEnum = false; + m_loopFrame = -1; + m_bIsGLA = bIsGLA; + m_bGenLoopFrame = bGenLoopFrame; + + SetPath(path); + SetStartFrame(startFrame); + SetTargetFrame(targetFrame); + SetFrameCount(frameCount); + SetFrameSpeed(frameSpeed); + SetFrameSpeedFromHeader(frameSpeedFromHeader); + + if (bHere) + { + for (int i=0; i_Init(false, false,NULL, 0, 0, 0, iDEFAULTSEQFRAMESPEED, iDEFAULTSEQFRAMESPEED, NULL); + } + + bHere = false; + } +} + +void CSequence::SetName(LPCTSTR name) +{ + if (m_name != NULL) + { + free(m_name); + } + if ((name == NULL) || (strlen(name) < 1)) + { + m_name = NULL; + } + else + { + m_name = (char*)malloc(strlen(name) + 1); + strcpy(m_name, name); + } +} + + + +ENUMTYPE GetEnumTypeFromString(LPCSTR lpString) +{ + if (lpString) + { + if (!strnicmp(lpString,"BOTH_",5)) + { + return ET_BOTH; + } + + if (!strnicmp(lpString,"TORSO_",6)) + { + return ET_TORSO; + } + + if (!strnicmp(lpString,"LEGS_",5)) + { + return ET_LEGS; + } + + if (!strnicmp(lpString,"FACE_",5)) + { + return ET_FACE; + } + } + + return ET_INVALID; +} + +// some enums are actually only seperators, the way to check is if the char straight after the 1st '_' is valid... +// +bool IsEnumSeperator(LPCSTR lpString) +{ + while (*lpString && *lpString!='_') lpString++; + + if (*lpString++) + { + if (*lpString) + { + if (!(isalpha(*lpString) || isdigit(*lpString))) + return true; + } + } + + return false; +} + +// converts (eg) "BOTH_ THE DEATH ANIMS" to "THE DEATH ANIMS"... +// +LPCSTR StripSeperatorStart(LPCSTR lpString) +{ + if (!IsEnumSeperator(lpString)) + return lpString; + + while (*lpString && *lpString != '_') lpString++; + if (*lpString) + { + lpString++; + while (*lpString && isspace(*lpString)) lpString++; + } + + return lpString; +} + + + +// this is so that part of it can be called for a dialog box string that's not actually in the m_enum var... +// +ENUMTYPE CSequence::GetEnumTypeFromString(LPCSTR lpString) +{ + if (!lpString) + { + return ET_INVALID; + } + + if (!strlen(lpString)) + { + return ET_INVALID; // special bit so blank strings don't assert in this one enumtype-return function + } + + ENUMTYPE et = ::GetEnumTypeFromString(lpString); + + // it has to be valid here, so check we haven't forgotten some code... + // + ASSERT(et!=ET_INVALID); + + return et; +} + +ENUMTYPE CSequence::GetEnumType() +{ + // this check done first to cope with sequences named (eg) "TORSO_" but not having an entry in anims.h + // to match this (which a strcmp won't detect). This shouldn't really be necessary since the menu system only + // allows you to pick valid ones from anims.h, but it copes with people editing ascii files by hand (or alerts + // people to entries being subsequently deleted from anims.h after use) + // + if (ValidEnum()) + { + return GetEnumTypeFromString(GetEnum()); + } + + return ET_INVALID; +} + +int CSequence::GetDisplayIconForTree(CModel *pModel) +{ + if (pModel->IsGhoul2()) + { + if ( IsGLA() ) + { + return ObjID_ENUMG2GLA; + } + else + { + if (GetEnumType() == ET_INVALID) + { + return ObjID_ENUMINVALID; + } + else + { + return ObjID_ENUMG2; + } + } + } + + switch (GetEnumType()) + { + case ET_INVALID: return ObjID_ENUMINVALID; + case ET_BOTH: return ObjID_ENUMBOTH; + case ET_TORSO: return ObjID_ENUMTORSO; + case ET_LEGS: return ObjID_ENUMLEGS; + } + + ASSERT(0); + return ObjID_ENUMINVALID; +} + + +// not the fastest of routines, but I only call it occasionally... +// +LPCSTR CSequence::GetDisplayNameForTree(CModel* pModel, bool bIncludeAnimEnum, bool bIncludeFrameDetails, bool bViewFrameDetails_Additional, CDC* pDC) +{ + if (pModel->IsGhoul2()) + { +// bIncludeAnimEnum = false; // :-) + } + + +#define ACCOUNTFORWIDTH(width) \ + iWidthSoFar+=width; \ + while (1) \ + { \ + size = pDC->GetOutputTextExtent(string); \ + if (size.cx>=iWidthSoFar) \ + break; \ + string+=" "; \ + } + + static CString string; + int iWidthSoFar=0; + CSize size; + + string.Empty(); + + if (bIncludeFrameDetails) + { + CString temp; + temp.Format("( Frames: Target %4d, Count %4d%sLoop %4d, Speed %4d )",GetTargetFrame(),GetFrameCount(),GetGenLoopFrame()?"+1, ":", ",GetLoopFrame(),GetFrameSpeed()); + + string += temp; + + ACCOUNTFORWIDTH(400); + } + + string += GetName(); + ACCOUNTFORWIDTH(250); + + if (bIncludeAnimEnum && strlen(GetEnum())) //ValidEnum()) + { + CString _enum; + + if (ValidEnum()) + { + _enum.Format("( %s",GetEnum()); + } + else + { + _enum.Format("( (BAD: %s)",GetEnum()); + } + string += _enum; + + for (int i=0; iAdditionalSequenceIsValid()) + { + string += ", "; // spaces seem pretty small in default font + string += AdditionalSeqs[i]->GetEnum(); + + if (bViewFrameDetails_Additional) + { + CString temp; + temp.Format("(T:%d C:%d L:%d S:%d)", + AdditionalSeqs[i]->GetTargetFrame(), + AdditionalSeqs[i]->GetFrameCount(), + AdditionalSeqs[i]->GetLoopFrame(), + AdditionalSeqs[i]->GetFrameSpeed() + ); + + string += temp; + } + } + else + { + // either empty string, or a bad enum... (because of anims.h being edited) + // + if (strlen(AdditionalSeqs[i]->GetEnum())) + { + string += ", "; + string += va("(BAD: %s)",AdditionalSeqs[i]->GetEnum()); + } + } + } + + string += " )"; + + ACCOUNTFORWIDTH(200); // this is flawed now because of additional-seqs, so keep this string as the last one + } + + return string; +} + +LPCTSTR CSequence::GetName() +{ + return m_name; +} + +void CSequence::DeriveName() +{ + if (m_path == NULL) + { + if (m_name != NULL) + { + free(m_name); + m_name = NULL; + } + return; + } + CString name = m_path; + int loc = name.ReverseFind('.'); + if (loc > -1) + { + name = name.Left(loc); + } + loc = name.ReverseFind('/'); + if (loc > -1) + { + name = name.Right(name.GetLength() - loc - 1); + } + name.MakeUpper(); + SetName(name); +} + +void CSequence::SetSound(LPCTSTR name) +{ + if (m_sound != NULL) + { + free(m_sound); + } + if ((name == NULL) || (strlen(name) < 1)) + { + m_sound = NULL; + } + else + { + m_sound = (char*)malloc(strlen(name) + 1); + strcpy(m_sound, name); + } +} + +LPCTSTR CSequence::GetSound() +{ + return m_sound; +} + +void CSequence::SetAction(LPCTSTR name) +{ + if (m_action != NULL) + { + free(m_action); + } + if ((name == NULL) || (strlen(name) < 1)) + { + m_action = NULL; + } + else + { + m_action = (char*)malloc(strlen(name) + 1); + strcpy(m_action, name); + } +} + +bool CSequence::IsGLA() +{ + return m_bIsGLA; +} + +LPCTSTR CSequence::GetAction() +{ + return m_action; +} + +void CSequence::SetEnum(LPCTSTR name) +{ + /* + if (m_enum != NULL) + { + free(m_enum); + } + if ((name == NULL) || (strlen(name) < 1)) + { + m_enum = NULL; + } + else + { + m_enum = (char*)malloc(strlen(name) + 1); + strcpy(m_enum, name); + } + */ + m_enum = name; + + SetValidEnum(((CAssimilateApp*)AfxGetApp())->ValidEnum(m_enum)); +} + +LPCTSTR CSequence::GetEnum() +{ + return m_enum; +} + +void CSequence::AddComment(CComment* comment) +{ + if (m_comments == NULL) + { + m_comments = comment; + } + else + { + CComment* curComment = m_comments; + while (curComment->GetNext() != NULL) + { + curComment = curComment->GetNext(); + } + curComment->SetNext(comment); + } +} + +// this should only be called on sequence that you know are Additional ones... +// +bool CSequence::AdditionalSequenceIsValid() +{ + return (GetEnumType()!=ET_INVALID); +} + +// should only really be called on master sequences, though harmless otherwise... +// +int CSequence::GetValidAdditionalSequences() +{ + int iCount = 0; + + for (int i=0; iAdditionalSequenceIsValid()) + { + iCount++; + } + } + } + + return iCount; +} + +CComment* CSequence::GetFirstComment() +{ + return m_comments; +} + +void CSequence::Write(CTxtFile* file, bool bPreQuat) +{ + CComment* curComment = m_comments; + while(curComment != NULL) + { + curComment->Write(file); + curComment = curComment->GetNext(); + } + + file->Write("$", CAssimilateDoc::GetKeyword(IsGLA()?TK_AS_GRAB_GLA:TK_AS_GRAB, TABLE_QDT), " "); + CString path = m_path; + int loc = path.Find("/base"); + if (loc > -1) + { + path = path.Right(path.GetLength() - loc - 5); + loc = path.Find("/"); + path = path.Right(path.GetLength() - loc - 1); + } + if (!path.GetLength()) // check that some dopey artist hasn't use the name "base" on the right hand side + { + path = m_path; + } + + Filename_AccountForLOD(path, giLODLevelOverride); + file->Write(path); +/* + if (m_frameCount > -1) + { + file->Space(); + file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_FRAMES, TABLE_GRAB), " "); + file->Write(m_startFrame); + file->Space(); + file->Write(m_targetFrame); + file->Space(); + file->Write(m_frameCount); + } +*/ + +// if (strstr(path,"death16")) +// { +// int z=1; +// } + + if (!IsGLA()) + { + if (m_fill > -1) + { + file->Space(); + file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_FILL, TABLE_GRAB), " "); + // file->Space(); + file->Write(m_fill); + } + if (m_loopFrame != 0) + { + file->Space(); + file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_LOOP, TABLE_GRAB), " "); + // file->Space(); + file->Write(m_loopFrame); + } + // if (m_enum && (strcmp(m_name, m_enum) != 0)) + if (m_enum.GetLength() && (strcmp(m_name, m_enum) != 0)) + { + file->Space(); + file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_ENUM, TABLE_GRAB), " "); + // file->Space(); + file->Write(m_enum); + } + if (m_sound != NULL) + { + file->Space(); + file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_SOUND, TABLE_GRAB), " "); + // file->Space(); + file->Write(m_sound); + } + if (m_action != NULL) + { + file->Space(); + file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_ACTION, TABLE_GRAB), " "); + // file->Space(); + file->Write(m_action); + } + + if (m_bGenLoopFrame) + { + file->Space(); + file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_GENLOOPFRAME, TABLE_GRAB), " "); + } + + if (m_frameSpeed != m_iFrameSpeedFromHeader || + GetValidAdditionalSequences() || + bPreQuat + ) + { + // write out start marker (and stop-marker at bottom) so qdata can skip this more easily... + // + file->Space(); + file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_QDSKIPSTART, TABLE_GRAB)); + + if (m_frameSpeed != m_iFrameSpeedFromHeader) + { + file->Space(); + file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_FRAMESPEED, TABLE_GRAB), " "); + file->Write(m_frameSpeed); + } + + for (int i=0; iAdditionalSequenceIsValid()) + { + file->Space(); + file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_ADDITIONAL, TABLE_GRAB), " "); + file->Write(additionalSeq->GetStartFrame()); + file->Space(); + file->Write(additionalSeq->GetFrameCount()); + file->Space(); + file->Write(additionalSeq->GetLoopFrame()); + file->Space(); + file->Write(additionalSeq->GetFrameSpeed()); + file->Space(); + file->Write(additionalSeq->GetEnum()); + } + } + + // this is an unpleasant abuse, but needed to retro-hack into old versions of carcass... + // + if (bPreQuat) + { + file->Space(); + file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_PREQUAT, TABLE_GRAB)); + } + + // any other stuff in future (sound events etc)... + // + // ... + + // write end marker (so QData knows to start reading again)... + // + file->Space(); + file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_QDSKIPSTOP, TABLE_GRAB)); + } + } + + file->Writeln(); +} + +// this writes the CFG file... (it's also now recursive, albeit with changing 'this' ptrs, so be aware of that) +// +void CSequence::WriteExternal(CModel *pModel, CTxtFile* file, bool bMultiPlayerFormat) +{ + // this now keeps the spacing nicer for cutting and pasting into source... + // + CString sOutputEnum = m_enum; + if (sOutputEnum.IsEmpty()) + { + sOutputEnum = "???"; + } + + if (bMultiPlayerFormat) + { + // Format: targetFrame, frameCount, loopFrame, frameSpeed + //0 47 0 10 //BOTH_DEATH1 + + file->Write(m_targetFrame); + file->Write("\t"); + file->Write(m_frameCount); + file->Write("\t"); + if (m_loopFrame == -1) + file->Write(0); + else + file->Write(m_frameCount-m_loopFrame); + file->Write("\t"); + file->Write(m_frameSpeed); + file->Write("\t"); + + if (!m_validEnum) + { + file->Write("// fix me - invalid enum -"); + } + else + { + file->Write("//"); + file->Write(sOutputEnum); + file->Writeln(); + } + } + else + { + while(sOutputEnum.GetLength()<20) sOutputEnum+=" "; + + file->Write(sOutputEnum, "\t"); + + // file->Write(m_startFrame); + // file->Write("\t"); + + // special code, if this is a LEGS type enum then we need to adjust the target frame to 'lose' the chunk + // of frames occupied by the TORSO types... + // + int iTargetFrame = m_targetFrame; + file->Write(iTargetFrame); + file->Write("\t"); + file->Write(m_frameCount); + file->Write("\t"); + file->Write(m_loopFrame); + file->Write("\t"); + file->Write(m_frameSpeed); + if (!m_validEnum && !pModel->IsGhoul2()) + { + file->Write("\t// fix me - invalid enum -"); + } + if (m_sound != NULL) + { + file->Write("\t// sound - "); + } + if (m_action != NULL) + { + file->Write ("\t// action - "); + } + if (m_fill > -1) + { + file->Write("\t// fill = "); + file->Write(m_fill); + } + file->Writeln(); + } + + // now for a bit of recursion, write out any additional sequences that this sequence owns... + // + for (int i=0; iAdditionalSequenceIsValid()) + { + seq->WriteExternal(pModel, file, bMultiPlayerFormat); + } + } + } +} +///////////////////////////////////////////////////////////////////////////// +// CSequencePropPage property page + +IMPLEMENT_DYNCREATE(CSequencePropPage, CPropertyPage) + +CSequencePropPage::CSequencePropPage() : CPropertyPage(CSequencePropPage::IDD) +{ + //{{AFX_DATA_INIT(CSequencePropPage) + m_frameCount = 0; + m_frameSpeed = 0; + m_path = _T(""); + m_startFrame = 0; + m_iLoopFrame = 0; + m_AnimationEnum = _T(""); + m_AnimationEnum2 = _T(""); + m_AnimationEnum3 = _T(""); + m_AnimationEnum4 = _T(""); + m_AnimationEnum5 = _T(""); + m_frameCount2 = 0; + m_frameCount3 = 0; + m_frameCount4 = 0; + m_frameCount5 = 0; + m_frameSpeed2 = 0; + m_frameSpeed3 = 0; + m_frameSpeed4 = 0; + m_frameSpeed5 = 0; + m_iLoopFrame2 = 0; + m_iLoopFrame3 = 0; + m_iLoopFrame4 = 0; + m_iLoopFrame5 = 0; + m_startFrame2 = 0; + m_startFrame3 = 0; + m_startFrame4 = 0; + m_startFrame5 = 0; + m_bGenLoopFrame = FALSE; + //}}AFX_DATA_INIT +} + +CSequencePropPage::~CSequencePropPage() +{ +} + +void CSequencePropPage::DoDataExchange(CDataExchange* pDX) +{ + CPropertyPage::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CSequencePropPage) + DDX_Text(pDX, IDC_FRAMECOUNT, m_frameCount); + DDX_Text(pDX, IDC_FRAMESPEED, m_frameSpeed); + DDX_Text(pDX, IDC_PATH, m_path); + DDX_Text(pDX, IDC_STARTFRAME, m_startFrame); + DDX_Text(pDX, IDC_LOOPFRAME, m_iLoopFrame); + DDX_Text(pDX, IDC_EDIT_ANIMATIONENUM, m_AnimationEnum); + DDX_Text(pDX, IDC_EDIT_ANIMATIONENUM2, m_AnimationEnum2); + DDX_Text(pDX, IDC_EDIT_ANIMATIONENUM3, m_AnimationEnum3); + DDX_Text(pDX, IDC_EDIT_ANIMATIONENUM4, m_AnimationEnum4); + DDX_Text(pDX, IDC_EDIT_ANIMATIONENUM5, m_AnimationEnum5); + DDX_Text(pDX, IDC_FRAMECOUNT2, m_frameCount2); + DDX_Text(pDX, IDC_FRAMECOUNT3, m_frameCount3); + DDX_Text(pDX, IDC_FRAMECOUNT4, m_frameCount4); + DDX_Text(pDX, IDC_FRAMECOUNT5, m_frameCount5); + DDX_Text(pDX, IDC_FRAMESPEED2, m_frameSpeed2); + DDX_Text(pDX, IDC_FRAMESPEED3, m_frameSpeed3); + DDX_Text(pDX, IDC_FRAMESPEED4, m_frameSpeed4); + DDX_Text(pDX, IDC_FRAMESPEED5, m_frameSpeed5); + DDX_Text(pDX, IDC_LOOPFRAME2, m_iLoopFrame2); + DDX_Text(pDX, IDC_LOOPFRAME3, m_iLoopFrame3); + DDX_Text(pDX, IDC_LOOPFRAME4, m_iLoopFrame4); + DDX_Text(pDX, IDC_LOOPFRAME5, m_iLoopFrame5); + DDX_Text(pDX, IDC_STARTFRAME2, m_startFrame2); + DDX_Text(pDX, IDC_STARTFRAME3, m_startFrame3); + DDX_Text(pDX, IDC_STARTFRAME4, m_startFrame4); + DDX_Text(pDX, IDC_STARTFRAME5, m_startFrame5); + DDX_Check(pDX, IDC_CHECK_GENLOOPFRAME, m_bGenLoopFrame); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CSequencePropPage, CPropertyPage) + //{{AFX_MSG_MAP(CSequencePropPage) + ON_BN_CLICKED(IDC_BUTTON_CHOOSEANIMATIONENUM, OnButtonChooseanimationenum) + ON_BN_CLICKED(IDC_BUTTON_CHOOSEANIMATIONENUM2, OnButtonChooseanimationenum2) + ON_BN_CLICKED(IDC_BUTTON_CHOOSEANIMATIONENUM3, OnButtonChooseanimationenum3) + ON_BN_CLICKED(IDC_BUTTON_CHOOSEANIMATIONENUM4, OnButtonChooseanimationenum4) + ON_BN_CLICKED(IDC_BUTTON_CHOOSEANIMATIONENUM5, OnButtonChooseanimationenum5) + ON_BN_CLICKED(IDC_BUTTON_CLEARANIMATIONENUM, OnButtonClearanimationenum) + ON_BN_CLICKED(IDC_BUTTON_CLEARANIMATIONENUM2, OnButtonClearanimationenum2) + ON_BN_CLICKED(IDC_BUTTON_CLEARANIMATIONENUM3, OnButtonClearanimationenum3) + ON_BN_CLICKED(IDC_BUTTON_CLEARANIMATIONENUM4, OnButtonClearanimationenum4) + ON_BN_CLICKED(IDC_BUTTON_CLEARANIMATIONENUM5, OnButtonClearanimationenum5) + ON_EN_KILLFOCUS(IDC_STARTFRAME, OnKillfocusStartframe) + ON_EN_KILLFOCUS(IDC_STARTFRAME2, OnKillfocusStartframe2) + ON_EN_KILLFOCUS(IDC_STARTFRAME3, OnKillfocusStartframe3) + ON_EN_KILLFOCUS(IDC_STARTFRAME4, OnKillfocusStartframe4) + ON_EN_KILLFOCUS(IDC_STARTFRAME5, OnKillfocusStartframe5) + ON_EN_KILLFOCUS(IDC_LOOPFRAME, OnKillfocusLoopframe) + ON_EN_KILLFOCUS(IDC_LOOPFRAME2, OnKillfocusLoopframe2) + ON_EN_KILLFOCUS(IDC_LOOPFRAME3, OnKillfocusLoopframe3) + ON_EN_KILLFOCUS(IDC_LOOPFRAME4, OnKillfocusLoopframe4) + ON_EN_KILLFOCUS(IDC_LOOPFRAME5, OnKillfocusLoopframe5) + ON_EN_KILLFOCUS(IDC_FRAMESPEED5, OnKillfocusFramespeed5) + ON_EN_KILLFOCUS(IDC_FRAMESPEED4, OnKillfocusFramespeed4) + ON_EN_KILLFOCUS(IDC_FRAMESPEED3, OnKillfocusFramespeed3) + ON_EN_KILLFOCUS(IDC_FRAMESPEED2, OnKillfocusFramespeed2) + ON_EN_KILLFOCUS(IDC_FRAMESPEED, OnKillfocusFramespeed) + ON_EN_KILLFOCUS(IDC_FRAMECOUNT5, OnKillfocusFramecount5) + ON_EN_KILLFOCUS(IDC_FRAMECOUNT4, OnKillfocusFramecount4) + ON_EN_KILLFOCUS(IDC_FRAMECOUNT3, OnKillfocusFramecount3) + ON_EN_KILLFOCUS(IDC_FRAMECOUNT2, OnKillfocusFramecount2) + ON_EN_KILLFOCUS(IDC_FRAMECOUNT, OnKillfocusFramecount) + ON_EN_KILLFOCUS(IDC_EDIT_ANIMATIONENUM5, OnKillfocusEditAnimationenum5) + ON_EN_KILLFOCUS(IDC_EDIT_ANIMATIONENUM4, OnKillfocusEditAnimationenum4) + ON_EN_KILLFOCUS(IDC_EDIT_ANIMATIONENUM3, OnKillfocusEditAnimationenum3) + ON_EN_KILLFOCUS(IDC_EDIT_ANIMATIONENUM2, OnKillfocusEditAnimationenum2) + ON_EN_KILLFOCUS(IDC_EDIT_ANIMATIONENUM, OnKillfocusEditAnimationenum) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CSequencePropPage message handlers + +void CSequencePropPage::OnButtonChooseanimationenum() +{ + char sReturnString[200]; // OTT + + if (UpdateData(DIALOG_TO_DATA)) + { + CAnimPicker animDialog(&sReturnString[0]); + if (animDialog.DoModal()==IDOK) + { + SetModified(true); // enable the apply button + m_AnimationEnum = sReturnString; + UpdateData(DATA_TO_DIALOG); + HandleAllItemsGraying(); + } + } +} + +// deliberately does not do enum... +// +#define DEFAULT_FROM_MASTER(number) \ + if (m_AnimationEnum ## number.IsEmpty()) \ + { \ + m_frameCount ## number = m_frameCount; \ + m_frameSpeed ## number = m_frameSpeed; \ + m_iLoopFrame ## number = m_iLoopFrame; \ + m_startFrame ## number = m_startFrame; \ + } + +void CSequencePropPage::OnButtonChooseanimationenum2() +{ + char sReturnString[200]; // OTT + + if (UpdateData(DIALOG_TO_DATA)) + { + CAnimPicker animDialog(&sReturnString[0]); + if (animDialog.DoModal()==IDOK) + { + SetModified(true); // enable the apply button + DEFAULT_FROM_MASTER(2); + m_AnimationEnum2 = sReturnString; + UpdateData(DATA_TO_DIALOG); + HandleAllItemsGraying(); + } + } +} + +void CSequencePropPage::OnButtonChooseanimationenum3() +{ + char sReturnString[200]; // OTT + + if (UpdateData(DIALOG_TO_DATA)) + { + CAnimPicker animDialog(&sReturnString[0]); + if (animDialog.DoModal()==IDOK) + { + SetModified(true); // enable the apply button + DEFAULT_FROM_MASTER(3); + m_AnimationEnum3 = sReturnString; + UpdateData(DATA_TO_DIALOG); + HandleAllItemsGraying(); + } + } +} + +void CSequencePropPage::OnButtonChooseanimationenum4() +{ + char sReturnString[200]; // OTT + + if (UpdateData(DIALOG_TO_DATA)) + { + CAnimPicker animDialog(&sReturnString[0]); + if (animDialog.DoModal()==IDOK) + { + SetModified(true); // enable the apply button + DEFAULT_FROM_MASTER(4); + m_AnimationEnum4 = sReturnString; + UpdateData(DATA_TO_DIALOG); + HandleAllItemsGraying(); + } + } +} + +void CSequencePropPage::OnButtonChooseanimationenum5() +{ + char sReturnString[200]; // OTT + + if (UpdateData(DIALOG_TO_DATA)) + { + CAnimPicker animDialog(&sReturnString[0]); + if (animDialog.DoModal()==IDOK) + { + SetModified(true); // enable the apply button + DEFAULT_FROM_MASTER(5); + m_AnimationEnum5 = sReturnString; + UpdateData(DATA_TO_DIALOG); + HandleAllItemsGraying(); + } + } +} + +void CSequencePropPage::OnButtonClearanimationenum() +{ + if (UpdateData(DIALOG_TO_DATA)) + { + SetModified(true); // enable the apply button + m_AnimationEnum = ""; + UpdateData(DATA_TO_DIALOG); + HandleAllItemsGraying(); + } +} + +void CSequencePropPage::OnButtonClearanimationenum2() +{ + if (UpdateData(DIALOG_TO_DATA)) + { + SetModified(true); // enable the apply button + m_AnimationEnum2 = ""; + UpdateData(DATA_TO_DIALOG); + HandleAllItemsGraying(); + } +} + +void CSequencePropPage::OnButtonClearanimationenum3() +{ + if (UpdateData(DIALOG_TO_DATA)) + { + SetModified(true); // enable the apply button + m_AnimationEnum3 = ""; + UpdateData(DATA_TO_DIALOG); + HandleAllItemsGraying(); + } +} + +void CSequencePropPage::OnButtonClearanimationenum4() +{ + if (UpdateData(DIALOG_TO_DATA)) + { + SetModified(true); // enable the apply button + m_AnimationEnum4 = ""; + UpdateData(DATA_TO_DIALOG); + HandleAllItemsGraying(); + } +} + +void CSequencePropPage::OnButtonClearanimationenum5() +{ + if (UpdateData(DIALOG_TO_DATA)) + { + SetModified(true); // enable the apply button + m_AnimationEnum5 = ""; + UpdateData(DATA_TO_DIALOG); + HandleAllItemsGraying(); + } +} + + + +void CSequencePropPage::OnCancel() +{ + UpdateData(DATA_TO_DIALOG); // stops a nasty bug in MFC whereby having crap data (eg 'a' in an int-box) gets you stuck in a loop if you ESC from a prop page + CPropertyPage::OnCancel(); +} + +void CSequencePropPage::OnKillfocusStartframe() +{ + ONKILLFOCUS; +} + +void CSequencePropPage::OnKillfocusStartframe2() +{ + ONKILLFOCUS; +} + +void CSequencePropPage::OnKillfocusStartframe3() +{ + ONKILLFOCUS; +} + +void CSequencePropPage::OnKillfocusStartframe4() +{ + ONKILLFOCUS; +} + +void CSequencePropPage::OnKillfocusStartframe5() +{ + ONKILLFOCUS; +} + +void CSequencePropPage::OnKillfocusLoopframe() +{ + ONKILLFOCUS; +} + +void CSequencePropPage::OnKillfocusLoopframe2() +{ + ONKILLFOCUS; +} + +void CSequencePropPage::OnKillfocusLoopframe3() +{ + ONKILLFOCUS; +} + +void CSequencePropPage::OnKillfocusLoopframe4() +{ + ONKILLFOCUS; +} + +void CSequencePropPage::OnKillfocusLoopframe5() +{ + ONKILLFOCUS; +} + +void CSequencePropPage::OnKillfocusFramespeed5() +{ + ONKILLFOCUS; +} + +void CSequencePropPage::OnKillfocusFramespeed4() +{ + ONKILLFOCUS; +} + +void CSequencePropPage::OnKillfocusFramespeed3() +{ + ONKILLFOCUS; +} + +void CSequencePropPage::OnKillfocusFramespeed2() +{ + ONKILLFOCUS; +} + +void CSequencePropPage::OnKillfocusFramespeed() +{ + ONKILLFOCUS; +} + +void CSequencePropPage::OnKillfocusFramecount5() +{ + ONKILLFOCUS; +} + +void CSequencePropPage::OnKillfocusFramecount4() +{ + ONKILLFOCUS; +} + +void CSequencePropPage::OnKillfocusFramecount3() +{ + ONKILLFOCUS; +} + +void CSequencePropPage::OnKillfocusFramecount2() +{ + ONKILLFOCUS; +} + +void CSequencePropPage::OnKillfocusFramecount() +{ + ONKILLFOCUS; +} + +void CSequencePropPage::OnKillfocusEditAnimationenum5() +{ + ONKILLFOCUS; +} + +void CSequencePropPage::OnKillfocusEditAnimationenum4() +{ + ONKILLFOCUS; +} + +void CSequencePropPage::OnKillfocusEditAnimationenum3() +{ + ONKILLFOCUS; +} + +void CSequencePropPage::OnKillfocusEditAnimationenum2() +{ + ONKILLFOCUS; +} + +void CSequencePropPage::OnKillfocusEditAnimationenum() +{ + ONKILLFOCUS; +} + +// this is called by the OK and/or Apply buttons... +// +void CSequencePropPage::OkOrApply() +{ + m_sequence->SetFrameCount(m_frameCount); + m_sequence->SetFrameSpeed(m_frameSpeed); + m_sequence->SetStartFrame(m_startFrame); + m_sequence->SetLoopFrame(m_iLoopFrame); + m_sequence->SetEnum(m_AnimationEnum); + m_sequence->SetGenLoopFrame(!!m_bGenLoopFrame); + +#define OK_ADDITIONAL(number) \ + m_sequence->AdditionalSeqs[number-2]->SetFrameCount (m_frameCount ## number); \ + m_sequence->AdditionalSeqs[number-2]->SetFrameSpeed (m_frameSpeed ## number); \ + m_sequence->AdditionalSeqs[number-2]->SetStartFrame (m_startFrame ## number); \ + m_sequence->AdditionalSeqs[number-2]->SetLoopFrame (m_iLoopFrame ## number); \ + m_sequence->AdditionalSeqs[number-2]->SetEnum (m_AnimationEnum ## number); + + OK_ADDITIONAL(2); + OK_ADDITIONAL(3); + OK_ADDITIONAL(4); + OK_ADDITIONAL(5); + + // ensure I don't forget any future expansions by putting the next index in the sequence in an err-check... + // + #if !(6 == (MAX_ADDITIONAL_SEQUENCES+2)) + #error Need more OK_ code... + #endif + + m_sequence->SetPath(m_path); + *m_soilFlag = true; +} + +void CSequencePropPage::OnOK() +{ + OkOrApply(); + + // damn stupid C++... + // + if (ghAssimilateView) + { + CAssimilateDoc* pDoc = ghAssimilateView->GetDocument(); + if (pDoc) + { + pDoc->UpdateAllViews(NULL, AS_FILESUPDATED, NULL); + } + } + + CPropertyPage::OnOK(); +} + +BOOL CSequencePropPage::OnInitDialog() +{ + CPropertyPage::OnInitDialog(); + + // TODO: Add extra initialization here + m_frameCount = m_sequence->GetFrameCount(); + m_frameSpeed = m_sequence->GetFrameSpeed(); + m_startFrame = m_sequence->GetStartFrame(); + m_iLoopFrame = m_sequence->GetLoopFrame(); + m_AnimationEnum = m_sequence->GetEnum(); + m_bGenLoopFrame = m_sequence->GetGenLoopFrame(); + +#define INIT_ADDITIONAL(number) \ + m_frameCount ## number = m_sequence->AdditionalSeqs[number-2]->GetFrameCount(); \ + m_frameSpeed ## number = m_sequence->AdditionalSeqs[number-2]->GetFrameSpeed(); \ + m_startFrame ## number = m_sequence->AdditionalSeqs[number-2]->GetStartFrame(); \ + m_iLoopFrame ## number = m_sequence->AdditionalSeqs[number-2]->GetLoopFrame(); \ + m_AnimationEnum ## number = m_sequence->AdditionalSeqs[number-2]->GetEnum(); + + INIT_ADDITIONAL(2); + INIT_ADDITIONAL(3); + INIT_ADDITIONAL(4); + INIT_ADDITIONAL(5); + + // ensure I don't forget any future expansions by putting the next index in the sequence in an err-check... + // + #if !(6 == (MAX_ADDITIONAL_SEQUENCES+2)) + #error Need more INIT_ code... + #endif + + m_path = m_sequence->GetPath(); + + UpdateData(DATA_TO_DIALOG); + + HandleAllItemsGraying(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +BOOL CSequencePropPage::OnApply() +{ + OkOrApply(); + return CPropertyPage::OnApply(); +} + + +void CSequencePropPage::HandleAdditionalEditBoxesGraying() +{ + bool bOnOff; + + // note check runs off the dialog box strings, NOT the standard m_sequence member (because the data isn't set till OK) + // +#define GRAY_ADDITIONAL(number) \ + bOnOff = !(m_sequence->GetEnumTypeFromString(m_AnimationEnum ## number) == ET_INVALID);\ + GetDlgItem(IDC_STARTFRAME ## number)->EnableWindow(bOnOff); \ + GetDlgItem(IDC_FRAMECOUNT ## number)->EnableWindow(bOnOff); \ + GetDlgItem(IDC_LOOPFRAME ## number)->EnableWindow(bOnOff); \ + GetDlgItem(IDC_FRAMESPEED ## number)->EnableWindow(bOnOff); + + GRAY_ADDITIONAL(2); + GRAY_ADDITIONAL(3); + GRAY_ADDITIONAL(4); + GRAY_ADDITIONAL(5); + + // ensure I don't forget any future expansions by putting the next index in the sequence in an err-check... + // + #if !(6 == (MAX_ADDITIONAL_SEQUENCES+2)) + #error Need more gray-handler code... + #endif +} + +void CSequencePropPage::HandleAllItemsGraying() +{ + if (UpdateData(DIALOG_TO_DATA)) + { + HandleAdditionalEditBoxesGraying(); + } +} + diff --git a/utils/Assimilate/Sequences.h b/utils/Assimilate/Sequences.h new file mode 100644 index 0000000..55e26f3 --- /dev/null +++ b/utils/Assimilate/Sequences.h @@ -0,0 +1,245 @@ +// Sequences.h + +#define MAX_ADDITIONAL_SEQUENCES 4 +#define iDEFAULTSEQFRAMESPEED 20 + +class CModel; + +enum +{ + TK_ASTERISK = TK_USERDEF, + TK_ASE_FIRSTFRAME, + TK_ASE_LASTFRAME, + TK_ASE_FRAMESPEED, +}; + + +// note that these are also in order of importance for the treeview sorting +// +typedef enum +{ + ET_BOTH = 0, + ET_TORSO, + ET_LEGS, + ET_FACE, + ET_INVALID, + +} ENUMTYPE; + +// + +class CSequence +{ +public: + CSequence(); + virtual ~CSequence(); + + virtual void Delete(); + static CSequence* _Create(bool bGenLoopFrame, bool bIsGLA, LPCTSTR path = NULL, int startFrame = 0, int targetFrame = 0, int frameCount = 0, int frameSpeed = iDEFAULTSEQFRAMESPEED, int frameSpeedFromHeader = iDEFAULTSEQFRAMESPEED, CComment* comments = NULL); + static CSequence* CreateFromFile(LPCTSTR path, CComment* comments); + + void Write(CTxtFile* file, bool bPreQuat); + void WriteExternal(CModel *pModel, CTxtFile* file, bool bMultiPlayerFormat); + void ReadHeader(); + void ReadASEHeader(LPCSTR psFilename, int &iStartFrame, int &iFrameCount, int &iFrameSpeed, bool bCanSkipXSIRead = false); + void ReadXSIHeader(LPCSTR psFilename, int &iStartFrame, int &iFrameCount, int &iFrameSpeed); + bool Parse(); + + CSequence* GetNext(); + void SetNext(CSequence* next); + + bool DoProperties(); + + LPCTSTR GetPath(); + void SetPath(LPCTSTR path); + + bool ValidEnum(); + void SetValidEnum(bool value = true); + + int GetStartFrame(); + void SetStartFrame(int frame); + int GetTargetFrame(); + void SetTargetFrame(int frame); + int GetFrameCount(); + void SetFrameCount(int count); + int GetFrameSpeed(); + void SetFrameSpeed(int speed); + int GetFrameSpeedFromHeader(); + void SetFrameSpeedFromHeader(int speed); + int GetLoopFrame(); + void SetLoopFrame(int loop); + bool GetGenLoopFrame(void); + void SetGenLoopFrame(bool bGenLoopFrame); + + void AddComment(CComment* comment); + CComment* GetFirstComment(); + + int GetDisplayIconForTree(CModel* pModel); + LPCSTR GetDisplayNameForTree(CModel* pModel, bool bIncludeAnimEnum, bool bIncludeFrameDetails, bool bViewFrameDetails_Additional, CDC* pDC); + LPCTSTR GetName(); + void SetName(LPCTSTR name); + void DeriveName(); + + int GetFill(); + void SetFill(int value); + + ENUMTYPE GetEnumTypeFromString(LPCSTR lpString); + ENUMTYPE GetEnumType(); + LPCTSTR GetEnum(); + void SetEnum(LPCTSTR name); + + LPCTSTR GetSound(); + void SetSound(LPCTSTR name); + + LPCTSTR GetAction(); + void SetAction(LPCTSTR name); + + bool IsGLA(); + + int m_iSequenceNumber; // temp-use during tree-sort/model-resequencing + + // this isn't a brilliant wy of doing things, but enables retro-fitting of extra data fairly painlessly... + CSequence* AdditionalSeqs[MAX_ADDITIONAL_SEQUENCES]; + int GetValidAdditionalSequences(); + bool AdditionalSequenceIsValid(); + +protected: + void _Init(bool bGenLoopFrame, bool bIsGLA, LPCTSTR path, int startFrame, int targetFrame, int frameCount, int frameSpeed, int frameSpeedFromheader, CComment* comments); + + CSequence* m_next; + CComment* m_comments; + char* m_path; + char* m_name; + CString m_enum; + char* m_sound; + char* m_action; + int m_fill; + int m_startFrame; + int m_targetFrame; + int m_frameCount; + int m_frameSpeed; + int m_iFrameSpeedFromHeader; + int m_loopFrame; + bool m_bIsGLA; + bool m_bGenLoopFrame; + + bool m_validEnum; + +// static keywordArray_t s_Symbols[]; +// static keywordArray_t s_Keywords[]; +}; + + + +///////////////////////////////////////////////////////////////////////////// +// CSequencePropPage dialog + +class CSequencePropPage : public CPropertyPage +{ + DECLARE_DYNCREATE(CSequencePropPage) + +// Construction +public: + CSequencePropPage(); + ~CSequencePropPage(); + + CSequence* m_sequence; + bool* m_soilFlag; + +// Dialog Data + //{{AFX_DATA(CSequencePropPage) + enum { IDD = IDD_PP_SEQUENCE }; + int m_frameCount; + int m_frameSpeed; + CString m_path; + int m_startFrame; + int m_iLoopFrame; + CString m_AnimationEnum; + CString m_AnimationEnum2; + CString m_AnimationEnum3; + CString m_AnimationEnum4; + CString m_AnimationEnum5; + int m_frameCount2; + int m_frameCount3; + int m_frameCount4; + int m_frameCount5; + int m_frameSpeed2; + int m_frameSpeed3; + int m_frameSpeed4; + int m_frameSpeed5; + int m_iLoopFrame2; + int m_iLoopFrame3; + int m_iLoopFrame4; + int m_iLoopFrame5; + int m_startFrame2; + int m_startFrame3; + int m_startFrame4; + int m_startFrame5; + BOOL m_bGenLoopFrame; + //}}AFX_DATA + + + void OkOrApply(); +// Overrides + // ClassWizard generate virtual function overrides + //{{AFX_VIRTUAL(CSequencePropPage) + public: + virtual void OnOK(); + virtual BOOL OnApply(); + virtual void OnCancel(); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + void HandleAdditionalEditBoxesGraying(); + void HandleAllItemsGraying(); + // Generated message map functions + //{{AFX_MSG(CSequencePropPage) + virtual BOOL OnInitDialog(); + afx_msg void OnButtonChooseanimationenum(); + afx_msg void OnButtonChooseanimationenum2(); + afx_msg void OnButtonChooseanimationenum3(); + afx_msg void OnButtonChooseanimationenum4(); + afx_msg void OnButtonChooseanimationenum5(); + afx_msg void OnButtonClearanimationenum(); + afx_msg void OnButtonClearanimationenum2(); + afx_msg void OnButtonClearanimationenum3(); + afx_msg void OnButtonClearanimationenum4(); + afx_msg void OnButtonClearanimationenum5(); + afx_msg void OnKillfocusStartframe(); + afx_msg void OnKillfocusStartframe2(); + afx_msg void OnKillfocusStartframe3(); + afx_msg void OnKillfocusStartframe4(); + afx_msg void OnKillfocusStartframe5(); + afx_msg void OnKillfocusLoopframe(); + afx_msg void OnKillfocusLoopframe2(); + afx_msg void OnKillfocusLoopframe3(); + afx_msg void OnKillfocusLoopframe4(); + afx_msg void OnKillfocusLoopframe5(); + afx_msg void OnKillfocusFramespeed5(); + afx_msg void OnKillfocusFramespeed4(); + afx_msg void OnKillfocusFramespeed3(); + afx_msg void OnKillfocusFramespeed2(); + afx_msg void OnKillfocusFramespeed(); + afx_msg void OnKillfocusFramecount5(); + afx_msg void OnKillfocusFramecount4(); + afx_msg void OnKillfocusFramecount3(); + afx_msg void OnKillfocusFramecount2(); + afx_msg void OnKillfocusFramecount(); + afx_msg void OnKillfocusEditAnimationenum5(); + afx_msg void OnKillfocusEditAnimationenum4(); + afx_msg void OnKillfocusEditAnimationenum3(); + afx_msg void OnKillfocusEditAnimationenum2(); + afx_msg void OnKillfocusEditAnimationenum(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + +}; + +ENUMTYPE GetEnumTypeFromString(LPCSTR lpString); +bool IsEnumSeperator(LPCSTR lpString); +LPCSTR StripSeperatorStart(LPCSTR lpString); +void ReadASEHeader(LPCSTR psFilename, int &iStartFrame, int &iFrameCount, int &iFrameSpeed, bool bReadingGLA, bool bCanSkipXSIRead = false); + diff --git a/utils/Assimilate/StdAfx.cpp b/utils/Assimilate/StdAfx.cpp new file mode 100644 index 0000000..5c3df3b --- /dev/null +++ b/utils/Assimilate/StdAfx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// Assimilate.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + + + diff --git a/utils/Assimilate/StdAfx.h b/utils/Assimilate/StdAfx.h new file mode 100644 index 0000000..a0c42e5 --- /dev/null +++ b/utils/Assimilate/StdAfx.h @@ -0,0 +1,25 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#if !defined(AFX_STDAFX_H__2CCA5546_2AD3_11D3_82E0_0000C0366FF2__INCLUDED_) +#define AFX_STDAFX_H__2CCA5546_2AD3_11D3_82E0_0000C0366FF2__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers + +#include // MFC core and standard components +#include // MFC extensions +#include // MFC Automation classes +#include // MFC support for Internet Explorer 4 Common Controls +#include // MFC support for Windows Common Controls +#include + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__2CCA5546_2AD3_11D3_82E0_0000C0366FF2__INCLUDED_) diff --git a/utils/Assimilate/Tokenizer.cpp b/utils/Assimilate/Tokenizer.cpp new file mode 100644 index 0000000..db91e4e --- /dev/null +++ b/utils/Assimilate/Tokenizer.cpp @@ -0,0 +1,3346 @@ +// Tokenizer.cpp +#ifndef NOT_USING_MODULES +// !!! if you are not using modules, read BELOW !!! +#include "Module.h" // if you are not using modules, + // create an empty Module.h in your + // project -- use of modules allows + // the error handler to be overridden + // with a custom CErrHandler +#endif +#include "Tokenizer.h" + + +enum +{ + DIR_INCLUDE = TK_USERDEF, + DIR_IFDEF, + DIR_IFNDEF, + DIR_ENDIF, + DIR_ELSE, + DIR_DEFINE, + DIR_UNDEFINE, +}; + +keywordArray_t CTokenizer::directiveKeywords[] = +{ + "include", DIR_INCLUDE, + "ifdef", DIR_IFDEF, + "ifndef", DIR_IFNDEF, + "endif", DIR_ENDIF, + "else", DIR_ELSE, + "define", DIR_DEFINE, + "undefine", DIR_UNDEFINE, + "", TK_EOF, +}; + +keywordArray_t CTokenizer::errorMessages[] = +{ + "No Error", TKERR_NONE, + "Unknown Error", TKERR_UNKNOWN, + "Buffer creation failed", TKERR_BUFFERCREATE, + "Unrecognized symbol", TKERR_UNRECOGNIZEDSYMBOL, + "Duplicate symbol", TKERR_DUPLICATESYMBOL, + "String length exceeded", TKERR_STRINGLENGTHEXCEEDED, + "Identifier length exceeded", TKERR_IDENTIFIERLENGTHEXCEEDED, + "Expected integer", TKERR_EXPECTED_INTEGER, + "Expected identifier", TKERR_EXPECTED_IDENTIFIER, + "Expected string", TKERR_EXPECTED_STRING, + "Expected char", TKERR_EXPECTED_CHAR, + "Expected float", TKERR_EXPECTED_FLOAT, + "Unexpected token", TKERR_UNEXPECTED_TOKEN, + "Invalid directive", TKERR_INVALID_DIRECTIVE, + "Include file not found", TKERR_INCLUDE_FILE_NOTFOUND, + "Unmatched directive", TKERR_UNMATCHED_DIRECTIVE, + "Unexpected End of File", TKERR_UNEXPECTED_EOF, + "", TKERR_USERERROR, +}; + +// +// CSymbol +// + +CSymbol::CSymbol() +{ +} + +CSymbol::~CSymbol() +{ +} + +CSymbol* CSymbol::Create(LPCTSTR symbolName) +{ + CSymbol* retval = new CSymbol(); + retval->Init(symbolName); + return retval; +} + +LPCTSTR CSymbol::GetName() +{ + if (m_symbolName == NULL) + { + return ""; + } + return m_symbolName; +} + +void CSymbol::Init(LPCTSTR symbolName) +{ + if ((symbolName == NULL) || (strlen(symbolName) < 1)) + { + m_symbolName = NULL; + return; + } + m_symbolName = (char*)malloc(strlen(symbolName) + 1); +// ASSERT(m_symbolName); + strcpy(m_symbolName, symbolName); +} + +void CSymbol::Delete() +{ + if (m_symbolName != NULL) + { + free(m_symbolName); + m_symbolName = NULL; + } + delete this; +} + +// +// CDirectiveSymbol +// + +CDirectiveSymbol::CDirectiveSymbol() +{ +} + +CDirectiveSymbol::~CDirectiveSymbol() +{ +} + +CDirectiveSymbol* CDirectiveSymbol::Create(LPCTSTR symbolName) +{ + CDirectiveSymbol* retval = new CDirectiveSymbol(); + retval->Init(symbolName); + return retval; +} + +void CDirectiveSymbol::Init(LPCTSTR symbolName) +{ + CSymbol::Init(symbolName); + m_value = NULL; +} + +void CDirectiveSymbol::Delete() +{ + if (m_value != NULL) + { + free(m_value); + m_value = NULL; + } + CSymbol::Delete(); +} + +void CDirectiveSymbol::SetValue(LPCTSTR value) +{ + if (m_value != NULL) + { + free(m_value); + m_value = NULL; + } + if ((value == NULL) || (strlen(value) < 1)) + { + return; + } + m_value = (char*)malloc(strlen(value) + 1); + strcpy(m_value, value); +} + +LPCTSTR CDirectiveSymbol::GetValue() +{ + return m_value; +} + +// +// CIntSymbol +// + +CIntSymbol::CIntSymbol() +{ +} + +CIntSymbol* CIntSymbol::Create(LPCTSTR symbolName, int value) +{ + CIntSymbol* retval = new CIntSymbol(); + retval->Init(symbolName, value); + return retval; +} + +void CIntSymbol::Delete() +{ + CSymbol::Delete(); +} + +void CIntSymbol::Init(LPCTSTR symbolName, int value) +{ + CSymbol::Init(symbolName); + m_value = value; +} + +int CIntSymbol::GetValue() +{ + return m_value; +} + +// +// CSymbolTable +// + +CSymbolTable::CSymbolTable() +{ + Init(); +} + +CSymbolTable::~CSymbolTable() +{ +} + +CSymbolTable* CSymbolTable::Create() +{ + CSymbolTable* retval = new CSymbolTable(); + retval->Init(); + return retval; +} + +void CSymbolTable::Init() +{ +} + +void CSymbolTable::DiscardSymbols() +{ + for (symbolmap_t::iterator isymbol = m_symbols.begin(); isymbol != m_symbols.end(); isymbol++) + { + (*isymbol).second->Delete(); + } + m_symbols.erase(m_symbols.begin(), m_symbols.end()); +} + +void CSymbolTable::Delete() +{ + DiscardSymbols(); + delete this; +} + +void CSymbolTable::Iterate(void function(CSymbol*, DWORD, DWORD), DWORD parm1, DWORD parm2) +{ + for (symbolmap_t::iterator isymbol = m_symbols.begin(); isymbol != m_symbols.end(); isymbol++) + { + function((*isymbol).second, parm1, parm2); + } +} + +int CSymbolTable::GetNumEntries() +{ + return m_symbols.size(); +} + +LPCSTR CSymbolTable::GetEntry(int iIndex) +{ + if (iIndexGetName(); + + symbolmap_t::iterator iter = m_symbols.find(name); + if (iter != m_symbols.end()) + { + return false; + } + m_symbols.insert(symbolmap_t::value_type(name, theSymbol)); + return true; +} + +CSymbol* CSymbolTable::FindSymbol(LPCTSTR symbolName) +{ + if (symbolName) + { + symbolmap_t::iterator iter = m_symbols.find(symbolName); + if (iter != m_symbols.end()) + { + return (*iter).second; + } + } + return NULL; +} + +CSymbol* CSymbolTable::ExtractSymbol(LPCTSTR symbolName) +{ + symbolmap_t::iterator iter = m_symbols.find(symbolName); + if (iter != m_symbols.end()) + { + CSymbol* retval = (*iter).second; + m_symbols.erase(iter); + return retval; + } + return NULL; +} + +void CSymbolTable::RemoveSymbol(LPCTSTR symbolName) +{ + m_symbols.erase(symbolName); +} + +// +// CParseStream +// + +CParseStream::CParseStream() +{ +} + +CParseStream::~CParseStream() +{ +} + +CParseStream* CParseStream::Create() +{ + return NULL; +} + +void CParseStream::Delete() +{ + delete this; +} + +bool CParseStream::Init() +{ + m_next = NULL; + m_original = false; + return true; +} + +bool CParseStream::NextChar(byte& theByte) +{ + return false; +} + +long CParseStream::GetRemainingSize() +{ + return 0; +} + +bool CParseStream::CanEndOfFile() +{ + return false; +} + +CParseStream* CParseStream::GetNext() +{ + return m_next; +} + +void CParseStream::SetNext(CParseStream* next) +{ + m_next = next; +} + +int CParseStream::GetCurLine() +{ + return 0; +} + +void CParseStream::GetCurFilename(char** theBuff) +{ + *theBuff = NULL; +} + +bool CParseStream::IsThisDefinition(void* theDefinition) +{ + return false; +} + +// +// CParsePutBack +// + +CParsePutBack::CParsePutBack() +{ +} + +CParsePutBack::~CParsePutBack() +{ +} + +CParsePutBack* CParsePutBack::Create(byte theByte, CParseStream* parent) +{ + CParsePutBack* curParsePutBack = new CParsePutBack(); + curParsePutBack->Init(theByte, parent); + return curParsePutBack; +} + +void CParsePutBack::Delete() +{ + delete this; +} + +bool CParsePutBack::NextChar(byte& theByte) +{ + if (m_consumed) + { + return false; + } + theByte = m_byte; + m_consumed = true; + return true; +} + +void CParsePutBack::Init(byte theByte, CParseStream* parent) +{ + CParseStream::Init(); + m_consumed = false; + m_byte = theByte; + m_curLine = 0; + m_parent = parent; + if (m_parent != NULL) + { + m_curLine = m_parent->GetCurLine(); + } +} + +long CParsePutBack::GetRemainingSize() +{ + if (m_consumed) + { + return 0; + } + else + { + return 1; + } +} + +int CParsePutBack::GetCurLine() +{ + return m_curLine; +} + +void CParsePutBack::GetCurFilename(char** theBuff) +{ + if (m_parent == NULL) + { + *theBuff = NULL; + return; + } + m_parent->GetCurFilename(theBuff); +} + +// +// CParseFile +// + +CParseFile::CParseFile() +{ +} + +CParseFile::~CParseFile() +{ +} + +CParseFile* CParseFile::Create() +{ + CParseFile* theParseFile = new CParseFile(); + + if ( !theParseFile->Init() ) + { + delete theParseFile; + return NULL; + } + + return theParseFile; +} + +CParseFile* CParseFile::Create(LPCTSTR filename, CTokenizer* tokenizer, DWORD buffsize) +{ + CParseFile* theParseFile = new CParseFile(); + + if ( theParseFile->Init(filename, tokenizer, buffsize) ) + return theParseFile; + + theParseFile->Delete(); + return NULL; +} + +void CParseFile::Delete() +{ + if (m_buff != NULL) + { + free(m_buff); + m_buff = NULL; + } + if (m_ownsFile && (m_fileHandle != NULL)) + { + CloseHandle(m_fileHandle); + m_fileHandle = NULL; + } + if (m_fileName != NULL) + { + free(m_fileName); + m_fileName = NULL; + } + delete this; +} + +bool CParseFile::Init() +{ + m_fileHandle = NULL; + m_buff = NULL; + m_ownsFile = false; + m_curByte = NULL; + m_curLine = 1; + m_fileName = NULL; + m_usesBuffer = false; + m_buffsize = 0; + m_buffByte = 0; + bool retval = CParseStream::Init(); + m_original = true; + return retval; +} + +DWORD CParseFile::GetFileSize() +{ + DWORD dwCur = SetFilePointer(m_fileHandle, 0L, NULL, FILE_CURRENT); + DWORD dwLen = SetFilePointer(m_fileHandle, 0, NULL, FILE_END); + SetFilePointer(m_fileHandle, dwCur, NULL, FILE_BEGIN); + return dwLen; +} + +bool CParseFile::CanEndOfFile() +{ + return true; +} + +void CParseFile::Read(void* buff, UINT buffsize) +{ + DWORD bytesRead; + ReadFile(m_fileHandle, buff, buffsize, &bytesRead, NULL); +} + +bool CParseFile::Init(LPCTSTR filename, CTokenizer* tokenizer, DWORD buffsize) +{ + CParseStream::Init(); + m_fileName = (char*)malloc(strlen(filename) + 1); + strcpy(m_fileName, filename); + + DWORD dwAccess = GENERIC_READ; + DWORD dwShareMode = FILE_SHARE_WRITE | FILE_SHARE_READ; + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = 0; + DWORD dwCreateFlag = OPEN_EXISTING; + + //need to get the game path to complete filename + m_fileHandle = CreateFile(filename, dwAccess, dwShareMode, &sa, dwCreateFlag, FILE_ATTRIBUTE_NORMAL, NULL); + + if (m_fileHandle == (HANDLE)-1) + { + tokenizer->Error(TKERR_INCLUDE_FILE_NOTFOUND, filename); + // + // if you don't do this, you get a memory leak when the Init() code below orphans this handle + // (malloc'd at function top), I know it'll always be NZ, but I just prefer handling memptrs legally...-slc + // + if (m_fileName) + { + free(m_fileName); + m_fileName = NULL; // I know this gets zeroed in Init(), but I really hate leaving bad ptrs! + } + Init(); + + return false; + } + + m_filesize = GetFileSize(); + if (buffsize > 0) + { + m_usesBuffer = true; + if (buffsize > m_filesize) + { + buffsize = m_filesize; + } + } + else + { + buffsize = m_filesize; + } + m_buff = (byte*)malloc(buffsize); + if (m_buff == NULL) + { + tokenizer->Error(TKERR_BUFFERCREATE); + Init(); + return false; + } + Read(m_buff, buffsize); + m_buffsize = buffsize; + m_curByte = 0; + m_buffByte = 0; + m_curPos = 1; + m_ownsFile = true; + m_curLine = 1; + m_original = true; + return true; +} + +long CParseFile::GetRemainingSize() +{ + return m_filesize - m_curByte; +} + +bool CParseFile::NextChar(byte& theByte) +{ + if (m_curByte >= m_filesize) + { + return false; + } + if (m_buff[m_buffByte] == '\n') + { + m_curLine += 1; + m_curPos = 1; + } + else + { + m_curPos++; + } + theByte = m_buff[m_buffByte++]; + m_curByte++; + if (m_buffByte >= m_buffsize) + { + if (m_buffsize > GetRemainingSize()) + { + m_buffsize = GetRemainingSize(); + } + Read(m_buff, m_buffsize); + m_buffByte = 0; + } + return true; +} + +int CParseFile::GetCurLine() +{ + return m_curLine; +} + +void CParseFile::GetCurFilename(char** theBuff) +{ + *theBuff = NULL; + if (m_fileName != NULL) + { + *theBuff = (char*)malloc(strlen(m_fileName) + 1); + strcpy(*theBuff, m_fileName); + } +} + +// +// CParseMemory +// + +CParseMemory::CParseMemory() +{ +} + +CParseMemory::~CParseMemory() +{ +} + +CParseMemory* CParseMemory::Create(byte* data, long datasize, bool original) +{ + CParseMemory* curParse = new CParseMemory(); + curParse->Init(data, datasize, original); + return curParse; +} + +void CParseMemory::Delete() +{ + delete this; +} + +bool CParseMemory::CanEndOfFile() +{ + return true; +} + +bool CParseMemory::NextChar(byte& theByte) +{ + if (m_offset < m_datasize) + { + if (m_data[m_offset] == '\n') + { + m_curLine += 1; + m_curPos = 1; + } + else + { + m_curPos++; + } + theByte = m_data[m_offset++]; + return true; + } + else + { + return false; + } +} + +void CParseMemory::Init(byte* data, long datasize, bool original) +{ + m_data = data; + m_curLine = 1; + m_curPos = 1; + m_offset = 0; + m_datasize = datasize; + m_original = original; +} + +long CParseMemory::GetRemainingSize() +{ + return m_datasize - m_offset; +} + +int CParseMemory::GetCurLine() +{ + return m_curLine; +} + +void CParseMemory::GetCurFilename(char** theBuff) +{ + *theBuff = NULL; +} + +// +// CParseBlock +// + +CParseBlock::CParseBlock() +{ +} + +CParseBlock::~CParseBlock() +{ +} + +CParseBlock* CParseBlock::Create(byte* data, long datasize) +{ + CParseBlock* curParse = new CParseBlock(); + curParse->Init(data, datasize); + return curParse; +} + +void CParseBlock::Delete() +{ + if (m_data != NULL) + { + free(m_data); + m_data = NULL; + } + delete this; +} + +bool CParseBlock::CanEndOfFile() +{ + return false; +} + +void CParseBlock::Init(byte* data, long datasize) +{ + m_data = (byte*)malloc(datasize); + memcpy(m_data, data, datasize); + m_curLine = 1; + m_curPos = 1; + m_offset = 0; + m_datasize = datasize; + m_original = false; +} + +// +// CParseToken +// + +CParseToken::CParseToken() +{ +} + +CParseToken::~CParseToken() +{ +} + +CParseToken* CParseToken::Create(CToken* token) +{ + CParseToken* curParse = new CParseToken(); + curParse->Init(token); + return curParse; +} + +void CParseToken::Delete() +{ + if (m_data != NULL) + { + free(m_data); + m_data = NULL; + } + delete this; +} + +bool CParseToken::NextChar(byte& theByte) +{ + if (m_offset < m_datasize) + { + if (m_data[m_offset] == '\n') + { + m_curLine += 1; + m_curPos = 1; + } + else + { + m_curPos++; + } + theByte = m_data[m_offset++]; + return true; + } + else + { + return false; + } +} + +void CParseToken::Init(CToken* token) +{ + LPCTSTR tokenString = token->GetStringValue(); + m_datasize = strlen(tokenString); + if (m_datasize > 0) + { + m_data = (byte*) malloc(m_datasize); + memcpy(m_data, tokenString, m_datasize); + } + else + { + m_data = NULL; + } + m_curLine = 1; + m_curPos = 1; + m_offset = 0; + m_original = false; + token->Delete(); +} + +long CParseToken::GetRemainingSize() +{ + return m_datasize - m_offset; +} + +int CParseToken::GetCurLine() +{ + return m_curLine; +} + +void CParseToken::GetCurFilename(char** theBuff) +{ + *theBuff = NULL; +} + +// +// CParseDefine +// + +CParseDefine::CParseDefine() +{ +} + +CParseDefine::~CParseDefine() +{ +} + +CParseDefine* CParseDefine::Create(CDirectiveSymbol* definesymbol) +{ + CParseDefine* retval = new CParseDefine(); + retval->Init(definesymbol); + return retval; +} + +bool CParseDefine::CanEndOfFile() +{ + return false; +} + +void CParseDefine::Delete() +{ + CParseMemory::Delete(); +} + +void CParseDefine::Init(CDirectiveSymbol* definesymbol) +{ + CParseMemory::Init((byte*)definesymbol->GetValue(), strlen(definesymbol->GetValue())); + m_defineSymbol = definesymbol; +} + +bool CParseDefine::IsThisDefinition(void* theDefinition) +{ + return (CDirectiveSymbol*)theDefinition == m_defineSymbol; +} + +// +// CToken +// + +CToken::CToken() +{ +} + +CToken::~CToken() +{ +} + +CToken* CToken::Create() +{ + CToken* theToken; + if (m_avail == NULL) + { + theToken = new CToken(); + } + else + { + theToken = m_avail; + m_avail = theToken->GetNext(); + } + theToken->Init(); + return theToken; +} + +void CToken::Delete() +{ + if (m_string != NULL) + { + free(m_string); + m_string = NULL; + } + MakeAvail(); +} + +void CToken::Init() +{ + m_next = NULL; + m_string = NULL; +} + +void CToken::SetNext(CToken* theToken) +{ + m_next = theToken; +} + +CToken* CToken::GetNext() +{ + return m_next; +} + +int CToken::GetType() +{ + return TK_EOF; +} + +int CToken::GetIntValue() +{ + return 0; +} + +LPCTSTR CToken::GetStringValue() +{ + if (m_string == NULL) + { + return ""; + } + return m_string; +} + +float CToken::GetFloatValue() +{ + return 0.0; +} + +// +// CCharToken +// + +CCharToken::CCharToken() +{ +} + +CCharToken::~CCharToken() +{ +} + +CCharToken* CCharToken::Create(byte theByte) +{ + CCharToken* theToken; + if (m_avail == NULL) + { + theToken = new CCharToken(); + } + else + { + theToken = m_avail; + m_avail = (CCharToken*)theToken->GetNext(); + } + theToken->Init(theByte); + return theToken; +} + +void CCharToken::Delete() +{ + CToken::Delete(); +} + +void CCharToken::Init(byte theByte) +{ + CToken::Init(); + char charString[10]; + switch(theByte) + { + case '\0': + strcpy(charString, "\\0"); + break; + case '\n': + strcpy(charString, "\\n"); + break; + case '\\': + strcpy(charString, "\\\\"); + break; + case '\'': + strcpy(charString, "\\'"); + break; + case '\?': + strcpy(charString, "\\?"); + break; + case '\a': + strcpy(charString, "\\a"); + break; + case '\b': + strcpy(charString, "\\b"); + break; + case '\f': + strcpy(charString, "\\f"); + break; + case '\r': + strcpy(charString, "\\r"); + break; + case '\t': + strcpy(charString, "\\t"); + break; + case '\v': + strcpy(charString, "\\v"); + break; + default: + charString[0] = (char)theByte; + charString[1] = '\0'; + break; + } + m_string = (char*)malloc(strlen(charString) + 1); + strcpy(m_string, charString); +} + +int CCharToken::GetType() +{ + return TK_CHAR; +} + +// +// CStringToken +// + +CStringToken::CStringToken() +{ +} + +CStringToken::~CStringToken() +{ +} + +CStringToken* CStringToken::Create(LPCTSTR theString) +{ + CStringToken* theToken; + if (m_avail == NULL) + { + theToken = new CStringToken(); + } + else + { + theToken = m_avail; + m_avail = (CStringToken*)theToken->GetNext(); + } + theToken->Init(theString); + return theToken; +} + +void CStringToken::Delete() +{ + CToken::Delete(); +} + +void CStringToken::Init(LPCTSTR theString) +{ + CToken::Init(); + m_string = (char*)malloc(strlen(theString) + 1); +// ASSERT(m_string); + strcpy(m_string, theString); +} + +int CStringToken::GetType() +{ + return TK_STRING; +} + +// +// CIntToken +// + +CIntToken::CIntToken() +{ +} + +CIntToken::~CIntToken() +{ +} + +CIntToken* CIntToken::Create(long value) +{ + CIntToken* theToken; + if (m_avail == NULL) + { + theToken = new CIntToken(); + } + else + { + theToken = m_avail; + m_avail = (CIntToken*)theToken->GetNext(); + } + theToken->Init(value); + return theToken; +} + +void CIntToken::Delete() +{ + CToken::Delete(); +} + +void CIntToken::Init(long value) +{ + CToken::Init(); + m_value = value; +} + +int CIntToken::GetType() +{ + return TK_INT; +} + +int CIntToken::GetIntValue() +{ + return m_value; +} + +float CIntToken::GetFloatValue() +{ + return (float)m_value; +} + +LPCTSTR CIntToken::GetStringValue() +{ + if (m_string != NULL) + { + free(m_string); + m_string = NULL; + } + char temp[128]; + sprintf(temp, "%d", m_value); + m_string = (char*)malloc(strlen(temp) + 1); + strcpy(m_string, temp); + return m_string; +} + +// +// CFloatToken +// + +CFloatToken::CFloatToken() +{ +} + +CFloatToken::~CFloatToken() +{ +} + +CFloatToken* CFloatToken::Create(float value) +{ + CFloatToken* theToken; + if (m_avail == NULL) + { + theToken = new CFloatToken(); + } + else + { + theToken = m_avail; + m_avail = (CFloatToken*)theToken->GetNext(); + } + theToken->Init(value); + return theToken; +} + +void CFloatToken::Delete() +{ + CToken::Delete(); +} + +void CFloatToken::Init(float value) +{ + CToken::Init(); + m_value = value; +} + +int CFloatToken::GetType() +{ + return TK_FLOAT; +} + +float CFloatToken::GetFloatValue() +{ + return m_value; +} + +LPCTSTR CFloatToken::GetStringValue() +{ + if (m_string != NULL) + { + free(m_string); + m_string = NULL; + } + char temp[128]; + sprintf(temp, "%g", m_value); + m_string = (char*)malloc(strlen(temp) + 1); + strcpy(m_string, temp); + return m_string; +} + +// +// CIdentifierToken +// + +CIdentifierToken::CIdentifierToken() +{ +} + +CIdentifierToken::~CIdentifierToken() +{ +} + +CIdentifierToken* CIdentifierToken::Create(LPCTSTR name) +{ + CIdentifierToken* theToken; + if (m_avail == NULL) + { + theToken = new CIdentifierToken(); + } + else + { + theToken = m_avail; + m_avail = (CIdentifierToken*)theToken->GetNext(); + } + theToken->Init(name); + return theToken; +} + +void CIdentifierToken::Delete() +{ + CToken::Delete(); +} + +void CIdentifierToken::Init(LPCTSTR name) +{ + CToken::Init(); + m_string = (char*)malloc(strlen(name) + 1); +// ASSERT(m_string); + strcpy(m_string, name); +} + +int CIdentifierToken::GetType() +{ + return TK_IDENTIFIER; +} + +// +// CCommentToken +// + +CCommentToken::CCommentToken() +{ +} + +CCommentToken::~CCommentToken() +{ +} + +CCommentToken* CCommentToken::Create(LPCTSTR name) +{ + CCommentToken* theToken; + if (m_avail == NULL) + { + theToken = new CCommentToken(); + } + else + { + theToken = m_avail; + m_avail = (CCommentToken*)theToken->GetNext(); + } + theToken->Init(name); + return theToken; +} + +void CCommentToken::Delete() +{ + CToken::Delete(); +} + +void CCommentToken::Init(LPCTSTR name) +{ + CToken::Init(); + m_string = (char*)malloc(strlen(name) + 1); +// ASSERT(m_string); + strcpy(m_string, name); +} + +int CCommentToken::GetType() +{ + return TK_COMMENT; +} + +// +// CSpaceToken +// + +CSpaceToken::CSpaceToken() +{ +} + +CSpaceToken::~CSpaceToken() +{ +} + +CSpaceToken* CSpaceToken::Create(LPCTSTR name) +{ + CSpaceToken* theToken; + if (m_avail == NULL) + { + theToken = new CSpaceToken(); + } + else + { + theToken = m_avail; + m_avail = (CSpaceToken*)theToken->GetNext(); + } + theToken->Init(name); + return theToken; +} + +void CSpaceToken::Delete() +{ + CToken::Delete(); +} + +void CSpaceToken::Init(LPCTSTR name) +{ + CToken::Init(); + m_string = (char*)malloc(strlen(name) + 1); +// ASSERT(m_string); + strcpy(m_string, name); +} + +int CSpaceToken::GetType() +{ + return TK_SPACE; +} + +// +// CUserToken +// + +CUserToken::CUserToken() +{ +} + +CUserToken::~CUserToken() +{ +} + +CUserToken* CUserToken::Create(int value, LPCTSTR string) +{ + CUserToken* theToken; + if (m_avail == NULL) + { + theToken = new CUserToken(); + } + else + { + theToken = m_avail; + m_avail = (CUserToken*)theToken->GetNext(); + } + theToken->Init(value, string); + return theToken; +} + +void CUserToken::Delete() +{ + CToken::Delete(); +} + +void CUserToken::Init(int value, LPCTSTR string) +{ + CToken::Init(); + m_value = value; + m_string = (char*)malloc(strlen(string) + 1); + strcpy(m_string, string); +} + +int CUserToken::GetType() +{ + return m_value; +} + +// +// CUndefinedToken +// + +CUndefinedToken::CUndefinedToken() +{ +} + +CUndefinedToken::~CUndefinedToken() +{ +} + +CUndefinedToken* CUndefinedToken::Create(LPCTSTR string) +{ + CUndefinedToken* theToken; + if (m_avail == NULL) + { + theToken = new CUndefinedToken(); + } + else + { + theToken = m_avail; + m_avail = (CUndefinedToken*)theToken->GetNext(); + } + theToken->Init(string); + return theToken; +} + +void CUndefinedToken::Delete() +{ + CToken::Delete(); +} + +void CUndefinedToken::Init(LPCTSTR string) +{ + CToken::Init(); + m_string = (char*)malloc(strlen(string) + 1); + strcpy(m_string, string); +} + +int CUndefinedToken::GetType() +{ + return TK_UNDEFINED; +} + +// +// CTokenizerState +// + +CTokenizerState::CTokenizerState() +{ +} + +CTokenizerState::~CTokenizerState() +{ +} + +CTokenizerState* CTokenizerState::Create(bool skip) +{ + CTokenizerState* retval = new CTokenizerState(); + retval->Init(skip); + return retval; +} + +void CTokenizerState::Init(bool skip) +{ + m_next = NULL; + m_skip = skip; + m_elseHit = false; +} + +void CTokenizerState::Delete() +{ + delete this; +} + +CTokenizerState* CTokenizerState::GetNext() +{ + return m_next; +} + +bool CTokenizerState::ProcessElse() +{ + if (!m_elseHit) + { + m_elseHit = true; + m_skip = !m_skip; + } + return m_elseHit; +} + +void CTokenizerState::SetNext(CTokenizerState* next) +{ + m_next = next; +} + +bool CTokenizerState::Skipping() +{ + return m_skip; +} + +// +// CTokenizerHolderState +// + +CTokenizerHolderState::CTokenizerHolderState() +{ +} + +CTokenizerHolderState::~CTokenizerHolderState() +{ +} + +CTokenizerHolderState* CTokenizerHolderState::Create() +{ + CTokenizerHolderState* retval = new CTokenizerHolderState(); + retval->Init(); + return retval; +} + +void CTokenizerHolderState::Init() +{ + CTokenizerState::Init(true); +} + +void CTokenizerHolderState::Delete() +{ + delete this; +} + +bool CTokenizerHolderState::ProcessElse() +{ + if (!m_elseHit) + { + m_elseHit = true; + } + return m_elseHit; +} + +// +// CKeywordTable +// + +CKeywordTable::CKeywordTable(CTokenizer* tokenizer, keywordArray_t* keywords) +{ + m_tokenizer = tokenizer; + m_holdKeywords = tokenizer->SetKeywords(keywords); +} + +CKeywordTable::~CKeywordTable() +{ + m_tokenizer->SetKeywords(m_holdKeywords); +} + +// +// CTokenizer +// + +CTokenizer::CTokenizer() +{ +} + +CTokenizer::~CTokenizer() +{ +} + +CTokenizer* CTokenizer::Create(UINT dwFlags) +{ + CTokenizer* theTokenizer = new CTokenizer(); + theTokenizer->Init(dwFlags); + return theTokenizer; +} + +void CTokenizer::Delete() +{ + while (m_curParseStream != NULL) + { + CParseStream* curStream = m_curParseStream; + m_curParseStream = curStream->GetNext(); + curStream->Delete(); + } + if (m_symbolLookup != NULL) + { + m_symbolLookup->Delete(); + m_symbolLookup = NULL; + } + while (m_nextToken != NULL) + { + CToken* curToken = m_nextToken; + m_nextToken = curToken->GetNext(); + curToken->Delete(); + } + while (m_state != NULL) + { + Error(TKERR_UNMATCHED_DIRECTIVE); + CTokenizerState* curState = m_state; + m_state = curState->GetNext(); + curState->Delete(); + } + +/* if (m_lastErrMsg != NULL) + { + free(m_lastErrMsg); + m_lastErrMsg = NULL; + }*/ + CToken::DeleteAll(); + CCharToken::DeleteAll(); + CIntToken::DeleteAll(); + CFloatToken::DeleteAll(); + CStringToken::DeleteAll(); + CSpaceToken::DeleteAll(); + CUndefinedToken::DeleteAll(); + CUserToken::DeleteAll(); + CIdentifierToken::DeleteAll(); + CCommentToken::DeleteAll(); + delete this; +} + +void CTokenizer::Error(int theError) +{ + char errString[128]; + char lookupstring[128]; + int i = 0; + while ((errorMessages[i].m_tokenvalue != TKERR_USERERROR) && (errorMessages[i].m_tokenvalue != theError)) + { + i++; + } + if ((errorMessages[i].m_tokenvalue == TKERR_USERERROR) && (m_errors != NULL)) + { + i = 0; + while ((m_errors[i].m_tokenvalue != TK_EOF) && (m_errors[i].m_tokenvalue != theError)) + { + i++; + } + strcpy(lookupstring, m_errors[i].m_keyword); + } + else + { + strcpy(lookupstring, errorMessages[i].m_keyword); + } + + // If anyone objects to me altering this, say so... -Ste +// sprintf(errString, "Error -- %d, %s", theError, lookupstring); + sprintf(errString, "Error -- %d, \"%s\" (line %d)", theError, lookupstring, GetCurLine()); + Error(errString, theError); +} + +void CTokenizer::Error(int theError, LPCTSTR errString) +{ + char errstring[128]; + char lookupstring[128]; + int i = 0; + while ((errorMessages[i].m_tokenvalue != TKERR_USERERROR) && (errorMessages[i].m_tokenvalue != theError)) + { + i++; + } + if ((errorMessages[i].m_tokenvalue == TKERR_USERERROR) && (m_errors != NULL)) + { + i = 0; + while ((m_errors[i].m_tokenvalue != TK_EOF) && (m_errors[i].m_tokenvalue != theError)) + { + i++; + } + strcpy(lookupstring, m_errors[i].m_keyword); + } + else + { + strcpy(lookupstring, errorMessages[i].m_keyword); + } + + // If anyone objects to me altering this, say so... -Ste +// sprintf(errstring, "Error -- %d, %s - %s", theError, lookupstring, errString); + _snprintf(errstring, sizeof(errstring), "Error -- %d, %s - \"%s\" (line %d)", theError, lookupstring, errString, GetCurLine()); + Error(errstring, theError); +} + +void CTokenizer::Error(LPCTSTR errString, int theError) +{ + if (m_errorProc != NULL) + { + m_errorProc(errString); + } +#ifdef USES_MODULES + else + { + ReportError(theError, errString); + } +#endif +} + +bool CTokenizer::AddParseFile(LPCTSTR filename, DWORD buffsize) +{ + CParseStream* newStream = CParseFile::Create(filename, this, buffsize); + + if ( newStream != NULL ) + { + newStream->SetNext(m_curParseStream); + m_curParseStream = newStream; + return true; + } + + return false; +} + +void CTokenizer::AddParseStream(byte* data, long datasize) +{ + CParseStream* newStream = CParseMemory::Create(data, datasize, true); + newStream->SetNext(m_curParseStream); + m_curParseStream = newStream; +} + +long CTokenizer::GetRemainingSize() +{ + long retval = 0; + CParseStream* curStream = m_curParseStream; + while (curStream != NULL) + { + retval += curStream->GetRemainingSize(); + curStream = curStream->GetNext(); + } + return retval; +} + +LPCTSTR CTokenizer::LookupToken(int tokenID, keywordArray_t* theTable) +{ + if (theTable == NULL) + { + theTable = m_keywords; + } + if (theTable == NULL) + { + return NULL; + } + + int i = 0; + while (theTable[i].m_tokenvalue != TK_EOF) + { + if (theTable[i].m_tokenvalue == tokenID) + { + return theTable[i].m_keyword; + } + i++; + } + return NULL; +} + +void CTokenizer::PutBackToken(CToken* theToken, bool commented, LPCTSTR addedChars) +{ + if (commented) + { + CParseToken* newStream = CParseToken::Create(theToken); + newStream->SetNext(m_curParseStream); + m_curParseStream = newStream; + + if (addedChars != NULL) + { + CParsePutBack* spacer = CParsePutBack::Create(' ', newStream); + spacer->SetNext(m_curParseStream); + m_curParseStream = spacer; + + CParseBlock* newBlock = CParseBlock::Create((byte*)addedChars, strlen(addedChars)); + newBlock->SetNext(m_curParseStream); + m_curParseStream = newBlock; + } + + char temp[] = "// * "; + CParseBlock* newBlock = CParseBlock::Create((byte*)temp, strlen(temp)); + newBlock->SetNext(m_curParseStream); + m_curParseStream = newBlock; + return; + } + + switch(theToken->GetType()) + { + case TK_INT: + case TK_EOF: + case TK_UNDEFINED: + case TK_FLOAT: + case TK_CHAR: + case TK_STRING: + case TK_EOL: + case TK_COMMENT: + theToken->SetNext(m_nextToken); + m_nextToken = theToken; + break; + default: + CParseToken* newStream = CParseToken::Create(theToken); + newStream->SetNext(m_curParseStream); + m_curParseStream = newStream; + break; + } + + if (addedChars != NULL) + { + CParseBlock* newBlock = CParseBlock::Create((byte*)addedChars, strlen(addedChars)); + newBlock->SetNext(m_curParseStream); + m_curParseStream = newBlock; + } +} + +CToken* CTokenizer::GetToken(keywordArray_t* keywords, UINT onFlags, UINT offFlags) +{ + keywordArray_t* holdKeywords = SetKeywords(keywords); + CToken* retval = GetToken(onFlags, offFlags); + SetKeywords(holdKeywords); + return retval; +} + +CToken* CTokenizer::GetToken(UINT onFlags, UINT offFlags) +{ + UINT holdFlags = m_flags; + + m_flags |= onFlags; + m_flags &= (~offFlags); + CToken* theToken = NULL; + while (theToken == NULL) + { + theToken = FetchToken(); + if (theToken == NULL) + { + continue; + } + if (theToken->GetType() == TK_EOF) + { + break; + } + if (m_state != NULL) + { + if (m_state->Skipping()) + { + theToken->Delete(); + theToken = NULL; + } + } + } + + m_flags = holdFlags; + return theToken; +} + +void CTokenizer::GetToEndOfFile() +{ + m_parsingToEnd = true; + byte theByte; + while(m_parsingToEnd) + { + if (!NextChar(theByte)) + { + return; + } + } +} + +CToken* CTokenizer::GetToEndOfLine(int tokenType) +{ + char theString[MAX_STRING_LENGTH]; + theString[0] = ' '; + while (isspace(theString[0])) + { + if (!NextChar((byte&)theString[0])) + { + return NULL; + } + if ((theString[0] == '\n') || (theString[0] == '\r')) + { + PutBackChar(theString[0]); + theString[0] = ' '; + break; + } + } + for (int i = 1; ; i++) + { + char *read; + + if (i < MAX_STRING_LENGTH) + read = &theString[i]; + else + read = &theString[MAX_STRING_LENGTH-1]; //just use the last char + + if (NextChar((byte&)*read)) + { + if ((*read != '\n') && (*read != '\r')) + { + continue; + } + PutBackChar(*read); + } + + //got to end of line + *read = '\0'; + + switch(tokenType) + { + case TK_COMMENT: + // + // strip trailing whitespace... + // + while (strlen(theString) && isspace(theString[strlen(theString)-1])) + { + theString[strlen(theString)-1] = 0; + } + if (i >= MAX_STRING_LENGTH) + Error(TKERR_STRINGLENGTHEXCEEDED); + return CCommentToken::Create(theString); + case TK_IDENTIFIER: + default: + if (i >= MAX_STRING_LENGTH) + Error(TKERR_IDENTIFIERLENGTHEXCEEDED); + return CIdentifierToken::Create(theString); + } + } +} + +void CTokenizer::SkipToLineEnd() +{ + byte theByte; + while(NextChar(theByte)) + { + if (theByte == '\n') + { + break; + } + } +} + +CToken* CTokenizer::FetchToken() +{ + if (m_nextToken != NULL) + { + CToken* curToken = m_nextToken; + m_nextToken = curToken->GetNext(); + curToken->SetNext(NULL); + return curToken; + } + byte theByte; + CToken* theToken = NULL; + + while (true) + { + if (!NextChar(theByte)) + { + if ((m_curParseStream != NULL) && ((TKF_RETURN_EOF & m_flags) != 0)) + { + CParseStream* curParseStream = m_curParseStream; + m_curParseStream = curParseStream->GetNext(); + curParseStream->Delete(); + } + return CToken::Create(); + } + if ((theByte == ' ') && (m_flags & TKF_SPACETOKENS)) + { + return CSpaceToken::Create(" "); + } + if (theByte <= ' ') + { + if ((theByte == '\n') && ((TKF_USES_EOL & m_flags) != 0)) + { + return CUserToken::Create(TK_EOL, "-EOLN-"); + } + continue; + } + switch(theByte) + { + case '#': + if ((m_flags & TKF_IGNOREDIRECTIVES) == 0) + { + theToken = HandleDirective(); + } + else + { + if ((m_flags & TKF_NODIRECTIVES) != 0) + { + return HandleSymbol('#'); + } + SkipToLineEnd(); + } + break; + case '/': + theToken = HandleSlash(); + break; + case '"': + theToken = HandleString(); + break; + case '\'': + theToken = HandleQuote(); + break; + default: + if (((theByte >= 'a') && (theByte <= 'z')) + || ((theByte >= 'A') && (theByte <= 'Z')) + || ((theByte == '_') && ((m_flags & TKF_NOUNDERSCOREINIDENTIFIER) == 0))) + { + theToken = HandleIdentifier(theByte); + } + else if (((m_flags & TKF_RETURNINTSASIDENTIFIERS) != 0) && (theByte >= '0') && (theByte <= '9')) + { + theToken = HandleIdentifier(theByte); + } + else if (((m_flags & TKF_NUMERICIDENTIFIERSTART) != 0) && (theByte >= '0') && (theByte <= '9')) + { + theToken = HandleIdentifier(theByte); + } + else if (((theByte >= '0') && (theByte <= '9')) || (theByte == '-')) + { + theToken = HandleNumeric(theByte); + } + else if (theByte == '.') + { + theToken = HandleDecimal(); + } + else if (theByte <= ' ') + { + break; + } + else + { + theToken = HandleSymbol(theByte); + } + } + if (theToken != NULL) + { + return theToken; + } + } +} + +bool CTokenizer::NextChar(byte& theByte) +{ + while (m_curParseStream != NULL) + { + if (m_curParseStream->NextChar(theByte)) + { + return true; + } + bool endoffile = m_curParseStream->CanEndOfFile(); + if (endoffile && ((TKF_RETURN_EOF & m_flags) != 0)) + { + return false; + } + CParseStream* curParseStream = m_curParseStream; + m_curParseStream = curParseStream->GetNext(); + curParseStream->Delete(); + if (m_parsingToEnd && endoffile) + { + break; + } + } + if (m_parsingToEnd) + { + theByte = TK_EOF; + m_parsingToEnd = false; + return true; + } + return false; +} + +bool CTokenizer::RequireToken(int tokenType) +{ + CToken* theToken = GetToken(); + bool retValue = theToken->GetType() == tokenType; + theToken->Delete(); + return retValue; +} + +void CTokenizer::ScanUntilToken(int tokenType) +{ + CToken* curToken; + int tokenValue = TK_UNDEFINED; + while (tokenValue != tokenType) + { + curToken = GetToken(); + tokenValue = curToken->GetType(); + if (tokenValue == TK_EOF) + { + PutBackToken(curToken); + break; + } + if (tokenValue == tokenType) + { + PutBackToken(curToken); + } + else + { + curToken->Delete(); + } + } +} + +void CTokenizer::Init(UINT dwFlags) +{ + m_symbolLookup = NULL; + m_nextToken = NULL; + m_curParseStream = NULL; + m_keywords = NULL; + m_symbols = NULL; + m_errors = NULL; + m_state = NULL; + m_flags = dwFlags; + m_errorProc = NULL; + m_parsingToEnd = false; +} + +void CTokenizer::SetErrorProc(LPTokenizerErrorProc errorProc) +{ + m_errorProc = errorProc; +} + +void CTokenizer::SetAdditionalErrors(keywordArray_t* theErrors) +{ + m_errors = theErrors; +} + +keywordArray_t* CTokenizer::SetKeywords(keywordArray_t* theKeywords) +{ + keywordArray_t* retval = m_keywords; + m_keywords = theKeywords; + return retval; +} + +void CTokenizer::SetSymbols(keywordArray_t* theSymbols) +{ + m_symbols = theSymbols; + if (m_symbolLookup != NULL) + { + m_symbolLookup->Delete(); + m_symbolLookup = NULL; + } + int i = 0; + if (theSymbols == NULL) + { + return; + } + while(theSymbols[i].m_tokenvalue != TK_EOF) + { + InsertSymbol(theSymbols[i].m_keyword, theSymbols[i].m_tokenvalue); + i++; + } +} + +void CTokenizer::InsertSymbol(LPCTSTR theSymbol, int theValue) +{ + CSymbolLookup** curHead = &m_symbolLookup; + CSymbolLookup* curParent = NULL; + CSymbolLookup* curLookup = NULL; + + for (UINT i = 0; i < strlen(theSymbol); i++) + { + bool found = false; + curLookup = *curHead; + while (curLookup != NULL) + { + if (curLookup->GetByte() == theSymbol[i]) + { + found = true; + break; + } + curLookup = curLookup->GetNext(); + } + if (!found) + { + curLookup = CSymbolLookup::Create(theSymbol[i]); + curLookup->SetParent(curParent); + curLookup->SetNext(*curHead); + *curHead = curLookup; + } + curHead = curLookup->GetChildAddress(); + curParent = curLookup; + } + if (curLookup->GetValue() != -1) + { + Error(TKERR_DUPLICATESYMBOL); + } + curLookup->SetValue(theValue); +} + +CToken* CTokenizer::HandleString() +{ + char theString[MAX_STRING_LENGTH]; + for (int i = 0; i < MAX_STRING_LENGTH; i++) + { + if (!NextChar((byte&)theString[i])) + { + return NULL; + } + if (theString[i] == '"') + { + theString[i] = '\0'; + return CStringToken::Create(theString); + } + if (theString[i] == '\\') + { + theString[i] = Escapement(); + } + } + Error(TKERR_STRINGLENGTHEXCEEDED); + return NULL; +} + +void CTokenizer::GetCurFilename(char** filename) +{ + if (m_curParseStream == NULL) + { + *filename = (char*)malloc(1); + *filename[0] = '\0'; + return; + } + m_curParseStream->GetCurFilename(filename); +} + +int CTokenizer::GetCurLine() +{ + if (m_curParseStream == NULL) + { + return 0; + } + + return m_curParseStream->GetCurLine(); +} + +void CTokenizer::PutBackChar(byte theByte) +{ + CParseStream* newStream = CParsePutBack::Create(theByte, m_curParseStream); + newStream->SetNext(m_curParseStream); + m_curParseStream = newStream; +} + +byte CTokenizer::Escapement() +{ + byte theByte; + if (NextChar(theByte)) + { + if (m_flags & TKF_NO_ESC_XLAT) + { + PutBackChar(theByte); + } + else + { + switch(theByte) + { + case 'n': + return '\n'; + case '\\': + return '\\'; + case '\'': + return '\''; + case '?': + return '\?'; + case 'a': + return '\a'; + case 'b': + return '\b'; + case 'f': + return '\f'; + case 'r': + return '\r'; + case 't': + return '\t'; + case 'v': + return '\v'; + case '0': + return '\0'; + // support for octal or hex sequences?? \000 or \xhhh + default: + PutBackChar(theByte); + } + } + } + return '\\'; +} + +bool CTokenizer::AddDefineSymbol(CDirectiveSymbol* definesymbol) +{ + CParseStream* curStream = m_curParseStream; + while(curStream != NULL) + { + if (curStream->IsThisDefinition(definesymbol)) + { + return false; + } + curStream = curStream->GetNext(); + } + CParseStream* newStream = CParseDefine::Create(definesymbol); + newStream->SetNext(m_curParseStream); + m_curParseStream = newStream; + return true; +} + +CToken* CTokenizer::TokenFromName(LPCTSTR name) +{ + CDirectiveSymbol* defineSymbol = (CDirectiveSymbol*)m_defines.FindSymbol(name); + if (defineSymbol != NULL) + { + if (AddDefineSymbol(defineSymbol)) + { + return FetchToken(); + } + } + if ((m_keywords != NULL) && ((m_flags & TKF_IGNOREKEYWORDS) == 0)) + { + int i = 0; + if ((m_flags & TKF_NOCASEKEYWORDS) == 0) + { + while (m_keywords[i].m_tokenvalue != TK_EOF) + { + if (strcmp(m_keywords[i].m_keyword, name) == 0) + { + return CUserToken::Create(m_keywords[i].m_tokenvalue, name); + } + i++; + } + } + else + { + while (m_keywords[i].m_tokenvalue != TK_EOF) + { + if (stricmp(m_keywords[i].m_keyword, name) == 0) + { + return CUserToken::Create(m_keywords[i].m_tokenvalue, name); + } + i++; + } + } + } + return CIdentifierToken::Create(name); +} + +int CTokenizer::DirectiveFromName(LPCTSTR name) +{ + if (directiveKeywords != NULL) + { + int i = 0; + while (directiveKeywords[i].m_tokenvalue != TK_EOF) + { + if (strcmp(directiveKeywords[i].m_keyword, name) == 0) + { + return directiveKeywords[i].m_tokenvalue; + } + i++; + } + } + return -1; +} + +CToken* CTokenizer::HandleIdentifier(byte theByte) +{ + char theString[MAX_IDENTIFIER_LENGTH]; + theString[0] = theByte; + bool numeric = (theByte >= '0') && (theByte <= '9'); + for (int i = 1; i < MAX_IDENTIFIER_LENGTH; i++) + { + if (NextChar((byte&)theString[i])) + { + if (((theString[i] != '_') || ((m_flags & TKF_NOUNDERSCOREINIDENTIFIER) == 0)) && + ((theString[i] != '-') || ((m_flags & TKF_NODASHINIDENTIFIER) == 0))) + { + if ((theString[i] >= '0') && (theString[i] <= '9')) + { + continue; + } + else + if (((theString[i] >= 'A') && (theString[i] <= 'Z')) + || ((theString[i] >= 'a') && (theString[i] <= 'z')) + || (theString[i] == '_') || (theString[i] == '-')) + { + numeric = false; + continue; + } + } + if ((numeric) && ((m_flags & TKF_RETURNINTSASIDENTIFIERS) == 0)) + { + theByte = theString[i]; + theString[i] = '\0'; + if (theByte == '.') + { + long value = atoi(theString); + return HandleFloat(false, value); + } + PutBackChar(theByte); + i--; + theByte = theString[i]; + theString[i] = '\0'; + long value = atoi(theString); + return HandleNumeric(theByte, value); + } + PutBackChar(theString[i]); + } + theString[i] = '\0'; + return TokenFromName(theString); + } + Error(TKERR_IDENTIFIERLENGTHEXCEEDED); + return NULL; +} + +CToken* CTokenizer::HandleSlash() +{ + byte theByte; + if (!NextChar(theByte)) + { + return NULL; + } + if (theByte == '/') + { + if (m_flags & (TKF_COMMENTTOKENS|TKF_COMMENTTOKENSSLASHSLASHONLY)) + { + return GetToEndOfLine(TK_COMMENT); + } + SkipToLineEnd(); + return NULL; + } + if (theByte == '*') + { + if ( (m_flags & (TKF_COMMENTTOKENS|TKF_COMMENTTOKENSSLASHSLASHONLY)) == TKF_COMMENTTOKENS ) + { + char theString[MAX_IDENTIFIER_LENGTH + 1]; + memset(theString,0,sizeof(theString)); // needed later + theString[0] = ' '; + while (isspace(theString[0])) + { + if (!NextChar((byte&)theString[0])) + { + return NULL; + } + } + for (int i = 1; i < MAX_IDENTIFIER_LENGTH; i++) + { + if (NextChar((byte&)theString[i])) + { + if (theString[i] != '*') + { + continue; + } + i++; + if (NextChar((byte&)theString[i])) + { + if (theString[i] == '/') + { + i--; + theString[i] = '\0'; + // + // strip trailing whitespace... + // + while (strlen(theString) && isspace(theString[strlen(theString)-1])) + { + theString[strlen(theString)-1] = 0; + } + + return CCommentToken::Create(theString); + } + } + } + } + Error(TKERR_IDENTIFIERLENGTHEXCEEDED); + // + // we should actually keep reading until we've finished scanning the /**/ comment completely, even though + // we're not returning it, or we end up returning stuff inside this comment as individual tokens later on + // + while (NextChar(theByte)) + { + // note slightly awkward check to see if the leading star was the last char read above (unlikely but possible) + // + if (theByte != '*' && theString[MAX_IDENTIFIER_LENGTH-1]!= '*') + { + theString[MAX_IDENTIFIER_LENGTH-1]=0; + continue; + } + if (NextChar(theByte)) + { + if (theByte == '/') + { + break; + } + } + else + { + break; + } + } + + return NULL; + } + while(NextChar(theByte)) + { + while(theByte == '*') + { + if (!NextChar(theByte)) + { + break; + } + if (theByte == '/') + { + return NULL; + } + } + } + return NULL; + } + PutBackChar(theByte); + return HandleSymbol('/'); +} + +CToken* CTokenizer::HandleNumeric(byte theByte, long value) +{ + bool thesign = theByte == '-'; + if (thesign) + { + if (!NextChar(theByte)) + { + return HandleSymbol('-'); + } + if (theByte == '.') + { + return HandleDecimal(thesign); + } + if ((theByte < '0') || (theByte > '9')) + { + PutBackChar(theByte); + return HandleSymbol('-'); + } + } + if (theByte == 'O') + { + return HandleOctal(thesign); + } + + bool digithit = (value != 0); + + if ((theByte == '0') && (value == 0)) + { + digithit = true; + if (!NextChar(theByte)) + { + return CIntToken::Create(0); + } + if ((theByte == 'x') || (theByte == 'X')) + { + return HandleHex(thesign); + } + } + + while((theByte >= '0') && (theByte <= '9')) + { + value = (value * 10) + (theByte - '0'); + if (!NextChar(theByte)) + { + if (thesign) + { + value = -value; + } + return CIntToken::Create(value); + } + digithit = true; + } + if (theByte == '.') + { + if (digithit) + { + return HandleFloat(thesign, value); + } + if (thesign) + { + PutBackChar(theByte); + theByte = '-'; + } + return HandleSymbol(theByte); + } + PutBackChar(theByte); + if (thesign) + { + value = -value; + } + return CIntToken::Create(value); +} + +CToken* CTokenizer::HandleOctal(bool thesign) +{ + byte theByte; + int value = 0; + + if (!NextChar(theByte)) + { + return CIntToken::Create(value); + } + while(true) + { + if((theByte >= '0') && (theByte <='7')) + { + value = (value * 8) + (theByte - '0'); + } + else + { + PutBackChar(theByte); + if (thesign) + { + value = -value; + } + return CIntToken::Create(value); + } + if (!NextChar(theByte)) + { + if (thesign) + { + value = -value; + } + return CIntToken::Create(value); + } + } +} + +CToken* CTokenizer::HandleHex(bool thesign) +{ + int value = 0; + + while (true) + { + byte theByte; + if (!NextChar(theByte)) + { + if (thesign) + { + value = -value; + } + return CIntToken::Create(value); + } + switch (theByte) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + theByte = theByte - '0'; + break; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + theByte = theByte - 'A' + 10; + break; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + theByte = theByte - 'a' + 10; + break; + default: + PutBackChar(theByte); + if (thesign) + { + value = -value; + } + return CIntToken::Create(value); + } + value = (value * 16) + theByte; + } +} + +CToken* CTokenizer::HandleDecimal(bool thesign) +{ + byte theByte; + if (!NextChar(theByte)) + { + if (thesign) + { + PutBackChar('.'); + return HandleSymbol('-'); + } + HandleSymbol('.'); + } + PutBackChar(theByte); + if ((theByte <= '9') && (theByte >= '0')) + { + return HandleFloat(thesign); + } + if (thesign) + { + PutBackChar('.'); + theByte = '-'; + } + else + { + theByte = '.'; + } + return HandleSymbol(theByte); +} + +CToken* CTokenizer::HandleFloat(bool thesign, long value) +{ + float lower = 1.0; + float newValue = (float)value; + byte theByte; + while(NextChar(theByte)) + { + if ((theByte >= '0') && (theByte <= '9')) + { + lower = lower / 10; + newValue = newValue + ((theByte - '0') * lower); + continue; + } + PutBackChar(theByte); + break; + } + if (thesign) + { + newValue = -newValue; + } + return CFloatToken::Create(newValue); +} + +CToken* CTokenizer::HandleQuote() +{ + byte theByte; + if (!NextChar(theByte)) + { + Error(TKERR_EXPECTED_CHAR); + return NULL; + } + if (theByte == '\\') + { + theByte = Escapement(); + } + byte dummy; + if (!NextChar(dummy)) + { + Error(TKERR_EXPECTED_CHAR); + return NULL; + } + if (dummy != '\'') + { + PutBackChar(dummy); + PutBackChar(theByte); + Error(TKERR_EXPECTED_CHAR); + return NULL; + } + return CCharToken::Create(theByte); +} + +void CTokenizer::SetFlags(UINT flags) +{ + m_flags = flags; +} + +UINT CTokenizer::GetFlags() +{ + return m_flags; +} + +CToken* CTokenizer::HandleSymbol(byte theByte) +{ + char symbolString[128]; + int curStrLen = 0; + symbolString[0] = '\0'; + bool consumed = false; + + CSymbolLookup* curLookup; + if ((m_flags & TKF_RAWSYMBOLSONLY) == 0) + { + curLookup = m_symbolLookup; + } + else + { + curLookup = NULL; + } + CSymbolLookup* lastLookup = NULL; + while(curLookup != NULL) + { + if (curLookup->GetByte() == theByte) + { + symbolString[curStrLen++] = theByte; + symbolString[curStrLen] = '\0'; + lastLookup = curLookup; + consumed = true; + if (curLookup->GetChild() == NULL) + { + break; + } + if (!NextChar(theByte)) + { + PutBackToken(CToken::Create()); + break; + } + consumed = false; + curLookup = curLookup->GetChild(); + continue; + } + curLookup = curLookup->GetNext(); + } + if ((!consumed) && (lastLookup != NULL)) + { + PutBackChar(theByte); + } + while ((lastLookup != NULL) && (lastLookup->GetValue() == -1)) + { + curStrLen--; + symbolString[curStrLen] = '\0'; + // symbolString = symbolString.Left(symbolString.GetLength() - 1); + if (lastLookup->GetParent() == NULL) + { + if ((m_flags & TKF_WANTUNDEFINED) == 0) + { + Error(TKERR_UNRECOGNIZEDSYMBOL); + } + } + else + { + PutBackChar(lastLookup->GetByte()); + } + lastLookup = lastLookup->GetParent(); + } + if (lastLookup == NULL) + { + if ((m_flags & TKF_WANTUNDEFINED) == 0) + { + return NULL; + } + curStrLen = 0; + symbolString[curStrLen++] = char(theByte); + symbolString[curStrLen] = '\0'; + if ((m_flags & TKF_WIDEUNDEFINEDSYMBOLS) != 0) + { + while (true) + { + if (!NextChar(theByte)) + { + PutBackToken(CToken::Create()); + break; + } + if (theByte == ' ') + { + break; + } + if (((theByte >= 'a') && (theByte <= 'z')) || ((theByte >= 'A') && (theByte <= 'Z')) || + ((theByte >= '0') && (theByte <= '9'))) + { + PutBackChar(theByte); + break; + } + if (theByte < ' ') + { + if ((theByte == '\n') && ((TKF_USES_EOL & m_flags) != 0)) + { + PutBackToken(CUserToken::Create(TK_EOL, "-EOLN-")); + break; + } + continue; + } + symbolString[curStrLen++] = theByte; + symbolString[curStrLen] = '\0'; + } + } + return CUndefinedToken::Create(symbolString); + } + return CUserToken::Create(lastLookup->GetValue(), symbolString); +} + +CToken* CTokenizer::HandleDirective() +{ + int tokenValue = 0; + CToken* theToken = FetchToken(); + if (theToken->GetType() == TK_EOF) + { + return theToken; + } + if (theToken->GetType() != TK_IDENTIFIER) + { + Error(TKERR_INVALID_DIRECTIVE); + theToken->Delete(); + SkipToLineEnd(); + return NULL; + } + + CDirectiveSymbol* curSymbol; + CTokenizerState* state; + int theDirective = DirectiveFromName(theToken->GetStringValue()); + theToken->Delete(); + byte theByte; + switch(theDirective) + { + case DIR_INCLUDE: + if ((m_state != NULL) && (m_state->Skipping())) + { + break; + } + theToken = GetToken(); + if (theToken->GetType() != TK_STRING) + { + Error(TKERR_INCLUDE_FILE_NOTFOUND); + theToken->Delete(); + SkipToLineEnd(); + break; + } + AddParseFile(theToken->GetStringValue()); + theToken->Delete(); + break; + case DIR_IFDEF: + if ((m_state != NULL) && (m_state->Skipping())) + { + state = CTokenizerHolderState::Create(); + state->SetNext(m_state); + m_state = state; + break; + } + theToken = GetToken(); + if (theToken->GetType() != TK_IDENTIFIER) + { + Error(TKERR_EXPECTED_IDENTIFIER); + theToken->Delete(); + SkipToLineEnd(); + break; + } + state = CTokenizerState::Create(m_defines.FindSymbol(theToken->GetStringValue()) == NULL); + theToken->Delete(); + state->SetNext(m_state); + m_state = state; + break; + case DIR_IFNDEF: + if ((m_state != NULL) && (m_state->Skipping())) + { + state = CTokenizerHolderState::Create(); + state->SetNext(m_state); + m_state = state; + break; + } + theToken = GetToken(); + if (theToken->GetType() != TK_IDENTIFIER) + { + Error(TKERR_EXPECTED_IDENTIFIER); + theToken->Delete(); + SkipToLineEnd(); + break; + } + state = CTokenizerState::Create(m_defines.FindSymbol(theToken->GetStringValue()) != NULL); + theToken->Delete(); + state->SetNext(m_state); + m_state = state; + break; + case DIR_ENDIF: + if (m_state == NULL) + { + Error(TKERR_UNMATCHED_DIRECTIVE); + break; + } + state = m_state; + m_state = state->GetNext(); + state->Delete(); + break; + case DIR_ELSE: + if (m_state == NULL) + { + Error(TKERR_UNMATCHED_DIRECTIVE); + break; + } + if (!m_state->ProcessElse()) + { + Error(TKERR_UNMATCHED_DIRECTIVE); + break; + } + break; + case DIR_DEFINE: + if ((m_state != NULL) && (m_state->Skipping())) + { + break; + } + theToken = GetToken(); + if (theToken->GetType() != TK_IDENTIFIER) + { + Error(TKERR_EXPECTED_IDENTIFIER); + theToken->Delete(); + SkipToLineEnd(); + break; + } + // blind add - should check first for value changes + { + curSymbol = CDirectiveSymbol::Create(theToken->GetStringValue()); + char temp[128]; + int tempsize = 0; + while(NextChar(theByte)) + { + if (theByte == '\n') + { + break; + } + temp[tempsize++] = char(theByte); + } + temp[tempsize] = '\0'; + curSymbol->SetValue(temp); + if (!m_defines.AddSymbol(curSymbol)) + { + curSymbol->Delete(); + } + } + theToken->Delete(); + break; + case DIR_UNDEFINE: + if ((m_state != NULL) && (m_state->Skipping())) + { + break; + } + theToken = GetToken(); + if (theToken->GetType() != TK_IDENTIFIER) + { + Error(TKERR_EXPECTED_IDENTIFIER); + theToken->Delete(); + SkipToLineEnd(); + break; + } + m_defines.RemoveSymbol(theToken->GetStringValue()); + theToken->Delete(); + break; + default: + Error(TKERR_INVALID_DIRECTIVE); + SkipToLineEnd(); + break; + } + return NULL; +} + +COLORREF CTokenizer::ParseRGB() +{ + CToken* theToken = GetToken(); + if (theToken->GetType() != TK_INT) + { + Error(TKERR_EXPECTED_INTEGER); + theToken->Delete(); + return RGB(0, 0, 0); + } + int red = theToken->GetIntValue(); + theToken->Delete(); + theToken = GetToken(); + if (theToken->GetType() != TK_INT) + { + Error(TKERR_EXPECTED_INTEGER); + theToken->Delete(); + return RGB(0, 0, 0); + } + int green = theToken->GetIntValue(); + theToken->Delete(); + theToken = GetToken(); + if (theToken->GetType() != TK_INT) + { + Error(TKERR_EXPECTED_INTEGER); + theToken->Delete(); + return RGB(0, 0, 0); + } + int blue = theToken->GetIntValue(); + theToken->Delete(); + return RGB(red, green, blue); +} + +// +// CSymbolLookup +// + +CSymbolLookup::CSymbolLookup() +{ +} + +CSymbolLookup::~CSymbolLookup() +{ +} + +CSymbolLookup* CSymbolLookup::Create(byte theByte) +{ + CSymbolLookup* curLookup = new CSymbolLookup(); + curLookup->Init(theByte); + return curLookup; +} + +void CSymbolLookup::Delete() +{ + if (m_sibling != NULL) + { + m_sibling->Delete(); + m_sibling = NULL; + } + if (m_child != NULL) + { + m_child->Delete(); + m_child = NULL; + } + delete this; +} + +void CSymbolLookup::Init(byte theByte) +{ + m_parent = NULL; + m_child = NULL; + m_sibling = NULL; + m_value = -1; + m_byte = theByte; +} + +CSymbolLookup* CSymbolLookup::GetNext() +{ + return m_sibling; +} + +void CSymbolLookup::SetNext(CSymbolLookup* next) +{ + m_sibling = next; +} + +void CSymbolLookup::SetParent(CSymbolLookup* parent) +{ + m_parent = parent; +} + +void CSymbolLookup::SetValue(int value) +{ + m_value = value; +} + +int CSymbolLookup::GetValue() +{ + return m_value; +} + +byte CSymbolLookup::GetByte() +{ + return m_byte; +} + +CSymbolLookup** CSymbolLookup::GetChildAddress() +{ + return &m_child; +} + +CSymbolLookup* CSymbolLookup::GetChild() +{ + return m_child; +} + +CSymbolLookup* CSymbolLookup::GetParent() +{ + return m_parent; +} + +void CToken::DeleteAll() +{ + while(m_avail != NULL) + { + CToken* cur = m_avail; + m_avail = cur->GetNext(); + delete(cur); + } +} + +CToken* CToken::m_avail = NULL; + +void CToken::MakeAvail() +{ + SetNext(m_avail); + m_avail = this; +} + +void CCharToken::DeleteAll() +{ + while(m_avail != NULL) + { + CToken* cur = m_avail; + m_avail = (CCharToken*)cur->GetNext(); + delete(cur); + } +} + +CCharToken* CCharToken::m_avail = NULL; + +void CCharToken::MakeAvail() +{ + SetNext(m_avail); + m_avail = this; +} + +void CIntToken::DeleteAll() +{ + while(m_avail != NULL) + { + CToken* cur = m_avail; + m_avail = (CIntToken*)cur->GetNext(); + delete(cur); + } +} + +CIntToken* CIntToken::m_avail = NULL; + +void CIntToken::MakeAvail() +{ + SetNext(m_avail); + m_avail = this; +} + +void CFloatToken::DeleteAll() +{ + while(m_avail != NULL) + { + CToken* cur = m_avail; + m_avail = (CFloatToken*)cur->GetNext(); + delete(cur); + } +} + +CFloatToken* CFloatToken::m_avail = NULL; + +void CFloatToken::MakeAvail() +{ + SetNext(m_avail); + m_avail = this; +} + +void CStringToken::DeleteAll() +{ + while(m_avail != NULL) + { + CToken* cur = m_avail; + m_avail = (CStringToken*)cur->GetNext(); + delete(cur); + } +} + +CStringToken* CStringToken::m_avail = NULL; + +void CStringToken::MakeAvail() +{ + SetNext(m_avail); + m_avail = this; +} + +void CSpaceToken::DeleteAll() +{ + while(m_avail != NULL) + { + CToken* cur = m_avail; + m_avail = (CSpaceToken*)cur->GetNext(); + delete(cur); + } +} + +CSpaceToken* CSpaceToken::m_avail = NULL; + +void CSpaceToken::MakeAvail() +{ + SetNext(m_avail); + m_avail = this; +} + +void CUndefinedToken::DeleteAll() +{ + while(m_avail != NULL) + { + CToken* cur = m_avail; + m_avail = (CUndefinedToken*)cur->GetNext(); + delete(cur); + } +} + +CUndefinedToken* CUndefinedToken::m_avail = NULL; + +void CUndefinedToken::MakeAvail() +{ + SetNext(m_avail); + m_avail = this; +} + +void CUserToken::DeleteAll() +{ + while(m_avail != NULL) + { + CToken* cur = m_avail; + m_avail = (CUserToken*)cur->GetNext(); + delete(cur); + } +} + +CUserToken* CUserToken::m_avail = NULL; + +void CUserToken::MakeAvail() +{ + SetNext(m_avail); + m_avail = this; +} + +void CIdentifierToken::DeleteAll() +{ + while(m_avail != NULL) + { + CToken* cur = m_avail; + m_avail = (CIdentifierToken*)cur->GetNext(); + delete(cur); + } +} + +CIdentifierToken* CIdentifierToken::m_avail = NULL; + +void CIdentifierToken::MakeAvail() +{ + SetNext(m_avail); + m_avail = this; +} + +void CCommentToken::DeleteAll() +{ + while(m_avail != NULL) + { + CToken* cur = m_avail; + m_avail = (CCommentToken*)cur->GetNext(); + delete(cur); + } +} + +CCommentToken* CCommentToken::m_avail = NULL; + +void CCommentToken::MakeAvail() +{ + SetNext(m_avail); + m_avail = this; +} diff --git a/utils/Assimilate/Tokenizer.h b/utils/Assimilate/Tokenizer.h new file mode 100644 index 0000000..507b0fe --- /dev/null +++ b/utils/Assimilate/Tokenizer.h @@ -0,0 +1,674 @@ +// Tokenizer.h +// + +#ifndef __TOKENIZER_H +#define __TOKENIZER_H + +#pragma warning( disable : 4786 ) // identifier was truncated + +#include +#include +#include +using namespace std; + +#include + +typedef unsigned char byte; +typedef unsigned short word; + +//#define MAX_STRING_LENGTH 256 +#define MAX_STRING_LENGTH 512 +#define MAX_IDENTIFIER_LENGTH 128 + +#define TKF_IGNOREDIRECTIVES 0x00000001 // skip over lines starting with # +#define TKF_USES_EOL 0x00000002 // generate end of line tokens +#define TKF_NODIRECTIVES 0x00000004 // don't treat # in any special way +#define TKF_WANTUNDEFINED 0x00000008 // if token not found in symbols create undefined token +#define TKF_WIDEUNDEFINEDSYMBOLS 0x00000010 // when undefined token encountered, accumulate until space +#define TKF_RAWSYMBOLSONLY 0x00000020 +#define TKF_NUMERICIDENTIFIERSTART 0x00000040 +#define TKF_IGNOREKEYWORDS 0x00000080 +#define TKF_NOCASEKEYWORDS 0x00000100 +#define TKF_NOUNDERSCOREINIDENTIFIER 0x00000200 +#define TKF_NODASHINIDENTIFIER 0x00000400 +#define TKF_COMMENTTOKENS 0x00000800 +#define TKF_SPACETOKENS 0x00001000 +#define TKF_RETURNINTSASIDENTIFIERS 0x00002000 +#define TKF_COMMENTTOKENSSLASHSLASHONLY 0x00004000 // like TKF_COMMENTTOKENS, but only does "//", not "/**/" types +#define TKF_RETURN_EOF 0x00008000 +#define TKF_NO_ESC_XLAT 0x00010000 // turns off escape-sequence translation during string read (eg leaves "\n" as 2 literal chars rather than as a single CR) + +enum +{ + TKERR_NONE, + TKERR_UNKNOWN, + TKERR_BUFFERCREATE, + TKERR_UNRECOGNIZEDSYMBOL, + TKERR_DUPLICATESYMBOL, + TKERR_STRINGLENGTHEXCEEDED, + TKERR_IDENTIFIERLENGTHEXCEEDED, + TKERR_EXPECTED_INTEGER, + TKERR_EXPECTED_IDENTIFIER, + TKERR_EXPECTED_STRING, + TKERR_EXPECTED_CHAR, + TKERR_EXPECTED_FLOAT, + TKERR_UNEXPECTED_TOKEN, + TKERR_INVALID_DIRECTIVE, + TKERR_INCLUDE_FILE_NOTFOUND, + TKERR_UNMATCHED_DIRECTIVE, + TKERR_UNEXPECTED_EOF, + TKERR_USERERROR, +}; + +enum +{ + TK_EOF = -1, + TK_UNDEFINED, + TK_COMMENT, + TK_EOL, + TK_CHAR, + TK_STRING, + TK_INT, + TK_INTEGER = TK_INT, + TK_FLOAT, + TK_IDENTIFIER, + TK_SPACE, + TK_USERDEF, +}; + +typedef struct +{ + char* m_keyword; + int m_tokenvalue; +} keywordArray_t; + +class lessstr +{ +public: + bool operator()(LPCTSTR str1, LPCTSTR str2) const {return (strcmp(str1, str2) < 0);}; +}; + +class CParseStream +{ +public: + CParseStream(); + virtual ~CParseStream(); + static CParseStream* Create(); + virtual void Delete(); + virtual bool NextChar(byte& theByte); + virtual int GetCurLine(); + virtual void GetCurFilename(char** theBuff); + virtual long GetRemainingSize(); + + CParseStream* GetNext(); + void SetNext(CParseStream* next); + virtual bool CanEndOfFile(); + + virtual bool IsThisDefinition(void* theDefinition); + +protected: + virtual bool Init(); + + CParseStream* m_next; + bool m_original; +}; + +class CToken +{ +public: + CToken(); + virtual ~CToken(); + static CToken* Create(); + virtual void Delete(); + static void DeleteAll(); + + virtual int GetType(); + CToken* GetNext(); + void SetNext(CToken* theToken); + virtual int GetIntValue(); + virtual LPCTSTR GetStringValue(); + virtual float GetFloatValue(); + +protected: + virtual void Init(); + virtual void MakeAvail(); + + char* m_string; + CToken* m_next; + + static CToken* m_avail; +}; + +class CCharToken : public CToken +{ +public: + CCharToken(); + virtual ~CCharToken(); + static CCharToken* Create(byte theByte); + virtual void Delete(); + static void DeleteAll(); + + virtual int GetType(); + +protected: + virtual void Init(byte theByte); + virtual void MakeAvail(); + + static CCharToken* m_avail; +}; + +class CStringToken : public CToken +{ +public: + CStringToken(); + virtual ~CStringToken(); + static CStringToken* Create(LPCTSTR theString); + virtual void Delete(); + static void DeleteAll(); + + virtual int GetType(); + +protected: + virtual void Init(LPCTSTR theString); + virtual void MakeAvail(); + + static CStringToken* m_avail; +}; + +class CIntToken : public CToken +{ +public: + CIntToken(); + virtual ~CIntToken(); + static CIntToken* Create(long value); + virtual void Delete(); + static void DeleteAll(); + + virtual int GetType(); + virtual float GetFloatValue(); + virtual int GetIntValue(); + virtual LPCTSTR GetStringValue(); + +protected: + virtual void Init(long value); + virtual void MakeAvail(); + + long m_value; + + static CIntToken* m_avail; +}; + +class CFloatToken : public CToken +{ +public: + CFloatToken(); + virtual ~CFloatToken(); + static CFloatToken* Create(float value); + virtual void Delete(); + static void DeleteAll(); + + virtual int GetType(); + virtual float GetFloatValue(); + virtual LPCTSTR GetStringValue(); + +protected: + virtual void Init(float value); + virtual void MakeAvail(); + + float m_value; + + static CFloatToken* m_avail; +}; + +class CIdentifierToken : public CToken +{ +public: + CIdentifierToken(); + virtual ~CIdentifierToken(); + static CIdentifierToken* Create(LPCTSTR name); + virtual void Delete(); + static void DeleteAll(); + + virtual int GetType(); + +protected: + virtual void Init(LPCTSTR name); + virtual void MakeAvail(); + + static CIdentifierToken* m_avail; +}; + +class CCommentToken : public CToken +{ +public: + CCommentToken(); + virtual ~CCommentToken(); + static CCommentToken* Create(LPCTSTR name); + virtual void Delete(); + static void DeleteAll(); + + virtual int GetType(); + +protected: + virtual void Init(LPCTSTR name); + virtual void MakeAvail(); + + static CCommentToken* m_avail; +}; + +class CSpaceToken : public CToken +{ +public: + CSpaceToken(); + virtual ~CSpaceToken(); + static CSpaceToken* Create(LPCTSTR name); + virtual void Delete(); + static void DeleteAll(); + + virtual int GetType(); + +protected: + virtual void Init(LPCTSTR name); + virtual void MakeAvail(); + + static CSpaceToken* m_avail; +}; + +class CUserToken : public CToken +{ +public: + CUserToken(); + virtual ~CUserToken(); + static CUserToken* Create(int value, LPCTSTR string); + virtual void Delete(); + static void DeleteAll(); + + virtual int GetType(); + +protected: + virtual void Init(int value, LPCTSTR string); + virtual void MakeAvail(); + + int m_value; + + static CUserToken* m_avail; +}; + +class CUndefinedToken : public CToken +{ +public: + CUndefinedToken(); + virtual ~CUndefinedToken(); + static CUndefinedToken* Create(LPCTSTR string); + virtual void Delete(); + static void DeleteAll(); + + virtual int GetType(); + +protected: + virtual void Init(LPCTSTR string); + virtual void MakeAvail(); + + static CUndefinedToken* m_avail; +}; + +class CSymbol +{ +public: + CSymbol(); + virtual ~CSymbol(); + static CSymbol* Create(LPCTSTR symbolName); + virtual void Delete(); + + LPCTSTR GetName(); + +protected: + virtual void Init(LPCTSTR symbolName); + + char* m_symbolName; +}; + +typedef map symbolmap_t; + +class CDirectiveSymbol : public CSymbol +{ +public: + CDirectiveSymbol(); + virtual ~CDirectiveSymbol(); + static CDirectiveSymbol* Create(LPCTSTR symbolName); + virtual void Delete(); + + void SetValue(LPCTSTR value); + LPCTSTR GetValue(); + +protected: + virtual void Init(LPCTSTR symbolName); + + char* m_value; +}; + +class CIntSymbol : public CSymbol +{ +public: + CIntSymbol(); + static CIntSymbol* Create(LPCTSTR symbolName, int value); + virtual void Delete(); + + int GetValue(); + +protected: + virtual void Init(LPCTSTR symbolName, int value); + + int m_value; +}; + +class CSymbolTable +{ +public: + CSymbolTable(); + virtual ~CSymbolTable(); + static CSymbolTable* Create(); + void Delete(); + + bool AddSymbol(CSymbol* theSymbol); + CSymbol* FindSymbol(LPCTSTR symbolName); + CSymbol* ExtractSymbol(LPCTSTR symbolName); + void RemoveSymbol(LPCTSTR symbolName); + void DiscardSymbols(); + void Iterate(void function(CSymbol*, DWORD, DWORD), DWORD parm1, DWORD parm2); + int GetNumEntries(); + LPCSTR GetEntry(int iIndex); + +protected: + void Init(); + symbolmap_t m_symbols; +}; + +class CSymbolLookup +{ +public: + CSymbolLookup(); + virtual ~CSymbolLookup(); + static CSymbolLookup* Create(byte theByte); + virtual void Delete(); + CSymbolLookup* GetNext(); + void SetNext(CSymbolLookup* next); + void SetParent(CSymbolLookup* parent); + CSymbolLookup* GetParent(); + CSymbolLookup** GetChildAddress(); + CSymbolLookup* GetChild(); + void SetValue(int value); + int GetValue(); + byte GetByte(); + +protected: + void Init(byte theByte); + + CSymbolLookup* m_child; + CSymbolLookup* m_sibling; + CSymbolLookup* m_parent; + int m_value; + byte m_byte; +}; + +class CTokenizerState +{ +public: + CTokenizerState(); + virtual ~CTokenizerState(); + static CTokenizerState* Create(bool skip); + virtual void Delete(); + CTokenizerState* GetNext(); + void SetNext(CTokenizerState* next); + virtual bool ProcessElse(); + bool Skipping(); + +protected: + void Init(bool skip); + + bool m_skip; + bool m_elseHit; + CTokenizerState* m_next; +}; + +class CTokenizerHolderState : public CTokenizerState +{ +public: + CTokenizerHolderState(); + virtual ~CTokenizerHolderState(); + static CTokenizerHolderState* Create(); + virtual void Delete(); + virtual bool ProcessElse(); + +protected: + void Init(); +}; + +typedef void (*LPTokenizerErrorProc)(LPCTSTR errString); + +#ifdef USES_MODULES +class CTokenizer : public CModule +#else +class CTokenizer +#endif +{ +public: + CTokenizer(); + virtual ~CTokenizer(); + static CTokenizer* Create(UINT dwFlags = 0); + virtual void Delete(); + virtual void Error(int theError); + virtual void Error(int theError, LPCTSTR errString); + virtual void Error(LPCTSTR errString, int theError = TKERR_UNKNOWN); + + CToken* GetToken(UINT onFlags = 0, UINT offFlags = 0); + CToken* GetToken(keywordArray_t* keywords, UINT onFlags, UINT offFlags); + void PutBackToken(CToken* theToken, bool commented = false, LPCTSTR addedChars = NULL); + bool RequireToken(int tokenType); + void ScanUntilToken(int tokenType); + void SkipToLineEnd(); + CToken* GetToEndOfLine(int tokenType = TK_IDENTIFIER); + void GetToEndOfFile(); + + keywordArray_t* SetKeywords(keywordArray_t* theKeywords); + void SetSymbols(keywordArray_t* theSymbols); + void SetAdditionalErrors(keywordArray_t* theErrors); + void SetErrorProc(LPTokenizerErrorProc errorProc); + void AddParseStream(byte* data, long datasize); + bool AddParseFile(LPCTSTR filename, DWORD buffsize = 0); + COLORREF ParseRGB(); + long GetRemainingSize(); + + UINT GetFlags(); + void SetFlags(UINT flags); + + void GetCurFilename(char** filename); + int GetCurLine(); + + LPCTSTR LookupToken(int tokenID, keywordArray_t* theTable = NULL); + +protected: + void SetError(int theError, LPCTSTR errString); + virtual void Init(UINT dwFlags = 0); + CToken* FetchToken(); + bool AddDefineSymbol(CDirectiveSymbol* definesymbol); + bool NextChar(byte& theByte); + byte Escapement(); + void InsertSymbol(LPCTSTR theSymbol, int theValue); + void PutBackChar(byte theByte); + CToken* TokenFromName(LPCTSTR name); + CToken* HandleDirective(); + CToken* HandleSlash(); + CToken* HandleString(); + CToken* HandleQuote(); + CToken* HandleIdentifier(byte theByte); + CToken* HandleNumeric(byte theByte, long value = 0); + CToken* HandleFloat(bool thesign = false, long value = 0); + CToken* HandleDecimal(bool thesign = false); + CToken* HandleSymbol(byte theByte); + CToken* HandleHex(bool thesize); + CToken* HandleOctal(bool thesize); + int DirectiveFromName(LPCTSTR name); + + CParseStream* m_curParseStream; + keywordArray_t* m_keywords; + keywordArray_t* m_symbols; + keywordArray_t* m_errors; + CSymbolLookup* m_symbolLookup; + CToken* m_nextToken; + CSymbolTable m_defines; + CTokenizerState* m_state; + UINT m_flags; + LPTokenizerErrorProc m_errorProc; + bool m_parsingToEnd; + + static keywordArray_t errorMessages[]; + static keywordArray_t directiveKeywords[]; +}; + +class CKeywordTable +{ +public: + CKeywordTable(CTokenizer* tokenizer, keywordArray_t* keywords); + virtual ~CKeywordTable(); + +protected: + CTokenizer* m_tokenizer; + keywordArray_t* m_holdKeywords; +}; + +class CParsePutBack : public CParseStream +{ +public: + CParsePutBack(); + virtual ~CParsePutBack(); + static CParsePutBack* Create(byte theByte, CParseStream* parent); + virtual void Delete(); + virtual bool NextChar(byte& theByte); + virtual int GetCurLine(); + virtual void GetCurFilename(char** theBuff); + virtual long GetRemainingSize(); + +protected: + virtual void Init(byte theByte, CParseStream* parent); + + byte m_byte; + bool m_consumed; + int m_curLine; + CParseStream* m_parent; +}; + +class CParseMemory : public CParseStream +{ +public: + CParseMemory(); + virtual ~CParseMemory(); + static CParseMemory* Create(byte* data, long datasize, bool original = false); + virtual void Delete(); + virtual bool NextChar(byte& theByte); + virtual int GetCurLine(); + virtual void GetCurFilename(char** theBuff); + virtual long GetRemainingSize(); + virtual bool CanEndOfFile(); + + +protected: + virtual void Init(byte* data, long datasize, bool original = false); + + byte* m_data; + int m_curLine; + long m_curPos; + long m_datasize; + long m_offset; +}; + +class CParseBlock : public CParseMemory +{ +public: + CParseBlock(); + virtual ~CParseBlock(); + static CParseBlock* Create(byte* data, long datasize); + virtual void Delete(); + virtual bool CanEndOfFile(); + +protected: + virtual void Init(byte* data, long datasize); +}; + +class CParseToken : public CParseStream +{ +public: + CParseToken(); + virtual ~CParseToken(); + static CParseToken* Create(CToken* token); + virtual void Delete(); + virtual bool NextChar(byte& theByte); + virtual int GetCurLine(); + virtual void GetCurFilename(char** theBuff); + virtual long GetRemainingSize(); + +protected: + virtual void Init(CToken* token); + + byte* m_data; + int m_curLine; + long m_curPos; + long m_datasize; + long m_offset; +}; + +class CParseDefine : public CParseMemory +{ +public: + CParseDefine(); + virtual ~CParseDefine(); + static CParseDefine* Create(CDirectiveSymbol* definesymbol); + virtual void Delete(); + virtual bool IsThisDefinition(void* theDefinition); + virtual bool CanEndOfFile(); + +protected: + virtual void Init(CDirectiveSymbol* definesymbol); + + CDirectiveSymbol* m_defineSymbol; +}; + +class CParseFile : public CParseStream +{ +public: + CParseFile(); + virtual ~CParseFile(); + static CParseFile* Create(); + static CParseFile* Create(LPCTSTR filename, CTokenizer* tokenizer, DWORD buffsize = 0); +// static CParseFile* Create(CFile* file, CTokenizer* tokenizer); + virtual void Delete(); + virtual int GetCurLine(); + virtual void GetCurFilename(char** theBuff); + virtual long GetRemainingSize(); + virtual bool CanEndOfFile(); + + virtual bool NextChar(byte& theByte); + +protected: + virtual bool Init(); + virtual bool Init(LPCTSTR filename, CTokenizer* tokenizer, DWORD buffsize); +// virtual void Init(CFile* file, CTokenizer* tokenizer); + DWORD GetFileSize(); + void Read(void* buff, UINT buffsize); + +// CFile* m_file; + HANDLE m_fileHandle; + char* m_fileName; + int m_curLine; + int m_curPos; + byte* m_buff; + DWORD m_curByte; + DWORD m_buffByte; + DWORD m_filesize; + DWORD m_buffsize; + bool m_ownsFile; + bool m_usesBuffer; +}; + + +#endif//__TOKENIZER_H \ No newline at end of file diff --git a/utils/Assimilate/TxtFile.cpp b/utils/Assimilate/TxtFile.cpp new file mode 100644 index 0000000..05b280a --- /dev/null +++ b/utils/Assimilate/TxtFile.cpp @@ -0,0 +1,197 @@ +// TxtFile.cpp + +#include +#include "TxtFile.h" + +CTxtFile::CTxtFile() +{ +} + +CTxtFile::~CTxtFile() +{ +} + +CTxtFile* CTxtFile::Create(CFile* file) +{ + CTxtFile* newFile = new CTxtFile(); + newFile->Init(file); + return newFile; +} + +CTxtFile* CTxtFile::Create(LPCTSTR filename) +{ + CTxtFile* newFile = new CTxtFile(); + newFile->Init(filename); + return newFile; +} + +void CTxtFile::Delete() +{ + if (m_ownsFile && (m_file != NULL)) + { + delete m_file; + m_file = NULL; + } + delete this; +} + +void CTxtFile::Init(LPCTSTR filename) +{ + try + { + m_file = new CFile(filename, CFile::modeCreate | CFile::modeReadWrite | CFile::shareExclusive); + m_ownsFile = true; + } + catch(CException* e) + { + m_file = NULL; + e->Delete(); + } +} + +void CTxtFile::Init(CFile* file) +{ + m_file = file; + m_ownsFile = false; +} + +void CTxtFile::Write(LPCTSTR string) +{ + ASSERT(string); + m_file->Write(string, strlen(string)); +} + +void CTxtFile::Writeln(LPCTSTR string1, LPCTSTR string2, LPCTSTR string3) +{ + Write(string1); + Write(string2); + Writeln(string3); +} + +void CTxtFile::Writeln(LPCTSTR string1, LPCTSTR string2) +{ + Write(string1); + Writeln(string2); +} + +void CTxtFile::Write(LPCTSTR string1, LPCTSTR string2, LPCTSTR string3) +{ + Write(string1); + Write(string2); + Write(string3); +} + +void CTxtFile::Write(LPCTSTR string1, LPCTSTR string2) +{ + Write(string1); + Write(string2); +} + +void CTxtFile::Writeln(LPCTSTR string) +{ + Write(string); + Writeln(); +} + +void CTxtFile::Writeln() +{ + Write("\r\n"); +} + +void CTxtFile::Write(int value) +{ + CString temp; + temp.Format("%d", value); + Write(temp); +} + +void CTxtFile::Write(float value, int fractSize) +{ + CString temp; + switch (fractSize) + { + case 1: + temp.Format("%.1f", value); + break; + case 2: + temp.Format("%.2f", value); + break; + case 3: + temp.Format("%.3f", value); + break; + case 4: + temp.Format("%.4f", value); + break; + case 5: + temp.Format("%.5f", value); + break; + case 6: + temp.Format("%.6f", value); + break; + default: + temp.Format("%g", value); + break; + } + Write(temp); +} + +void CTxtFile::WriteString(LPCTSTR string) +{ + CString temp; + temp.Format("\"%s\"", string); + Write(temp); +} + +// there is a slight problem with writing comments at the moment to do with spurious 0x0D bytes occasionally +// being introduced. This may be due to designers sometimes using text editors that use CR-only line ends +// rather than CR/LF pairs, and the tokenizer reader not stripping them. I could update the tokenizer source +// member "GetStringValue()" but that's shadowed in a frightening number of programs, and I don't want to break +// something miles away, so I'll just check for them here. Basically, you should never get 2 0x0D bytes next +// to each other, so each time I find one a pair I'll remove the first one. +// +void CTxtFile::WriteComment(LPCTSTR string, int indent) +{ + const char sFind [3]={0x0D,0x0D,0}; // a bit horrible really, but needs to be exact bytes + const char sReplace[2]={0x0D,0}; + CString temp = string; + + while (temp.Replace(sFind,sReplace)); // after this we're down to 1 0x0D max + + int loc = temp.Find('\n'); + if (loc == -1) + { + // now we know there's no 0x0D/0x0A pairs then any remaining 0x0D bytes are orphans and should be disposed + // of (don't you just love it when text files are read in as binary?) + // + while (temp.Replace(sReplace,NULL)); + Space(indent); + Write("//", temp); + Writeln(); + return; + } + Space(indent); + Writeln("/* "); + while (loc > -1) + { + CString left = temp.Left(loc); + temp = temp.Right(temp.GetLength() - loc - 1); + Space(indent); + Writeln(left); + loc = temp.Find('\n'); + } + Space(indent); + Writeln(temp); + Space(indent); + Writeln("*/"); +} + +void CTxtFile::Space(int value) +{ + char* temp; + temp = (char*)malloc(value + 1); + ASSERT(temp); + memset(temp, ' ', value); + temp[value] = '\0'; + Write(temp); + free(temp); +} diff --git a/utils/Assimilate/TxtFile.h b/utils/Assimilate/TxtFile.h new file mode 100644 index 0000000..c2896b8 --- /dev/null +++ b/utils/Assimilate/TxtFile.h @@ -0,0 +1,33 @@ +// TxtFile.h + +class CTxtFile +{ +public: + CTxtFile(); + ~CTxtFile(); + static CTxtFile* Create(CFile* file); + static CTxtFile* Create(LPCTSTR filename); + void Delete(); + + void WriteComment(LPCTSTR string, int indent = 0); + void Write(LPCTSTR string); + void Write(LPCTSTR string1, LPCTSTR string2); + void Write(LPCTSTR string1, LPCTSTR string2, LPCTSTR string3); + void Writeln(); + void Writeln(LPCTSTR string); + void Writeln(LPCTSTR string1, LPCTSTR string2); + void Writeln(LPCTSTR string1, LPCTSTR string2, LPCTSTR string3); + void Write(int value); + void Write(float value, int fractSize = -1); + void WriteString(LPCTSTR string); + void Space(int value = 1); + CFile* GetFile() {return m_file;}; + int IsValid(void) {return !!m_file;}; + +private: + void Init(CFile* file); + void Init(LPCTSTR filename); + + CFile* m_file; + bool m_ownsFile; +}; \ No newline at end of file diff --git a/utils/Assimilate/YesNoYesAllNoAll.cpp b/utils/Assimilate/YesNoYesAllNoAll.cpp new file mode 100644 index 0000000..41a5940 --- /dev/null +++ b/utils/Assimilate/YesNoYesAllNoAll.cpp @@ -0,0 +1,100 @@ +// YesNoYesAllNoAll.cpp : implementation file +// + +// Ok, ok, I kjnow this is a stupid name for a file, I didn't notice that a class name I was +// filling in in a classwizard dialog was also adding to a filename field, and I couldn't be +// bothered trying to undo it all afterwards, ok? :-/ + +#include "stdafx.h" +//#include "assimilate.h" +#include "includes.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CYesNoYesAllNoAll dialog + + +// this is really really awful, but if I make on large static text box the damn thing won't wrap text, no matter what +// flagfs are set in the RC, so I'll do it this way for now (another MS classic...) +// +CYesNoYesAllNoAll::CYesNoYesAllNoAll( LPCSTR psLine1, + LPCSTR psLine2, + LPCSTR psLine3, + LPCSTR psLine4, + LPCSTR psLine5, + LPCSTR psLine6, + CWnd* pParent /*=NULL*/) + : CDialog(CYesNoYesAllNoAll::IDD, pParent) +{ + //{{AFX_DATA_INIT(CYesNoYesAllNoAll) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + + m_psLine1 = psLine1; + m_psLine2 = psLine2; + m_psLine3 = psLine3; + m_psLine4 = psLine4; + m_psLine5 = psLine5; + m_psLine6 = psLine6; +} + + +void CYesNoYesAllNoAll::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CYesNoYesAllNoAll) + // NOTE: the ClassWizard will add DDX and DDV calls here + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CYesNoYesAllNoAll, CDialog) + //{{AFX_MSG_MAP(CYesNoYesAllNoAll) + ON_BN_CLICKED(IDNOTOALL, OnNotoall) + ON_BN_CLICKED(IDYESTOALL, OnYestoall) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CYesNoYesAllNoAll message handlers + +void CYesNoYesAllNoAll::OnNotoall() +{ + EndDialog(NO_ALL); +} + +void CYesNoYesAllNoAll::OnYestoall() +{ + EndDialog(YES_ALL); +} + +void CYesNoYesAllNoAll::OnOK() +{ + EndDialog(YES); +} + +void CYesNoYesAllNoAll::OnCancel() +{ + EndDialog(NO); +} + + +BOOL CYesNoYesAllNoAll::OnInitDialog() +{ + CDialog::OnInitDialog(); + + GetDlgItem(IDC_STATIC1)->SetWindowText(m_psLine1?m_psLine1:""); + GetDlgItem(IDC_STATIC2)->SetWindowText(m_psLine2?m_psLine2:""); + GetDlgItem(IDC_STATIC3)->SetWindowText(m_psLine3?m_psLine3:""); + GetDlgItem(IDC_STATIC4)->SetWindowText(m_psLine4?m_psLine4:""); + GetDlgItem(IDC_STATIC5)->SetWindowText(m_psLine5?m_psLine5:""); + GetDlgItem(IDC_STATIC6)->SetWindowText(m_psLine6?m_psLine6:""); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/utils/Assimilate/YesNoYesAllNoAll.h b/utils/Assimilate/YesNoYesAllNoAll.h new file mode 100644 index 0000000..bd0eace --- /dev/null +++ b/utils/Assimilate/YesNoYesAllNoAll.h @@ -0,0 +1,63 @@ +#if !defined(AFX_YESNOYESALLNOALL_H__FCBCF4B4_7CCF_11D3_8A35_00500424438B__INCLUDED_) +#define AFX_YESNOYESALLNOALL_H__FCBCF4B4_7CCF_11D3_8A35_00500424438B__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// YesNoYesAllNoAll.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CYesNoYesAllNoAll dialog + +class CYesNoYesAllNoAll : public CDialog +{ +// Construction +public: + CYesNoYesAllNoAll( LPCSTR psLine1 = NULL, // this is awful, see constructor body for comment + LPCSTR psLine2 = NULL, + LPCSTR psLine3 = NULL, + LPCSTR psLine4 = NULL, + LPCSTR psLine5 = NULL, + LPCSTR psLine6 = NULL, + CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CYesNoYesAllNoAll) + enum { IDD = IDD_DIALOG_YESNOYESNOALL }; + // NOTE: the ClassWizard will add data members here + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CYesNoYesAllNoAll) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + LPCSTR m_psLine1; + LPCSTR m_psLine2; + LPCSTR m_psLine3; + LPCSTR m_psLine4; + LPCSTR m_psLine5; + LPCSTR m_psLine6; + + // Generated message map functions + //{{AFX_MSG(CYesNoYesAllNoAll) + afx_msg void OnNotoall(); + afx_msg void OnYestoall(); + virtual void OnOK(); + virtual void OnCancel(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_YESNOYESALLNOALL_H__FCBCF4B4_7CCF_11D3_8A35_00500424438B__INCLUDED_) diff --git a/utils/Assimilate/anims.h b/utils/Assimilate/anims.h new file mode 100644 index 0000000..117e02b --- /dev/null +++ b/utils/Assimilate/anims.h @@ -0,0 +1,121 @@ +// playerAnimations +typedef enum +{ + BOTH_DEATH1 = 0, //# special test comment 1 on BOTH_DEATH1 + BOTH_DEATH2, + BOTH_DEATH3, + BOTH_DEATH4, + BOTH_DEATH5, + BOTH_DEATHFORWARD, + BOTH_DEATHBACKWARD, + BOTH_DEATHFORWARD2, //# special test comment 2 on BOTH_DEATHFORWARD2 + BOTH_DEATHBACK2, + BOTH_WRITHINGDEATH,//if on back + BOTH_STUMBLEDEATH, + BOTH_FALLDEATH, + //# special test comment 3 on + BOTH_DEAD1, + BOTH_DEAD2, + BOTH_DEAD3, // next 2 just to check with and without whitespace, but empty otherwise. Will be deleted soon + BOTH_DEAD4, //# + BOTH_DEAD5, //# + BOTH_DEADFORWARD, + BOTH_DEADBACKWARD, + BOTH_DEADFORWARD2, + BOTH_DEADBACK2, + BOTH_WRITHINGDEAD, + BOTH_STUMBLEDEAD, + BOTH_FALLDEAD, + + TORSO_DROP, + TORSO_RAISE, + TORSO_ATTACK, + TORSO_STAND2, + TORSO_ATTACK2, + TORSO_GESTURE, + TORSO_STAND, + + LEGS_IDLE, + LEGS_IDLECR, + + LEGS_WALKCR, //#test1 + LEGS_WALK, //# hi there + LEGS_RUN, + LEGS_BACK, + LEGS_SWIM, + + LEGS_JUMP, + LEGS_LAND, + + LEGS_JUMPB, + LEGS_LANDB, + + LEGS_TURN, + COMMON_ANIMS_LAST, + +//extra anims + BOTH_PAIN1 = COMMON_ANIMS_LAST, + BOTH_PAIN2, + BOTH_PAIN3, + + TORSO_RUN, + TORSO_CONSOLE, + TORSO_CONSOLE2, + TORSO_CROUCH, + TORSO_WALKCR, + TORSO_IDLECR, + TORSO_WALK, + TORSO_PAIN, + TORSO_ASSIMILATE, + TORSO_PLUGIN, + TORSO_PLUGOUT, + TORSO_JUMP, + TORSO_INAIR, + TORSO_LAND, + TORSO_SITTING, + TORSO_WREADY1, + TORSO_WREADY2, + TORSO_TRICORDER, + TORSO_MEDICORDER, + TORSO_INJURED, + TORSO_WRITHING, + TORSO_CRAWLBACK, + TORSO_PAIN2WRITHE, + TORSO_CONSOLE1IDLE, + TORSO_CONSOLE1RIGHT, + TORSO_CONSOLE1LEFT, + TORSO_SITTING1, + TORSO_SITTING2, + TORSO_SITTING3, + TORSO_BENT, + TORSO_GUARD_LOOKAROUND, + TORSO_GUARD_IDLE, + TORSO_WIDLE, + + LEGS_ATTACK, + LEGS_ATTACK2, + LEGS_CONSOLE, + LEGS_CONSOLE2, + LEGS_CROUCH, + LEGS_PAIN, + LEGS_ASSIMILATE, + LEGS_PLUGIN, + LEGS_PLUGOUT, + LEGS_INAIR, + LEGS_SITTING, + LEGS_TRICORDER, + LEGS_MEDICORDER, + LEGS_INJURED, + LEGS_WRITHING, + LEGS_CRAWLBACK, + LEGS_PAIN2WRITHE, + LEGS_CONSOLE1IDLE, + LEGS_CONSOLE1RIGHT, + LEGS_CONSOLE1LEFT, + LEGS_SITTING1, + LEGS_SITTING2, + LEGS_SITTING3, + LEGS_SITPOSE, + LEGS_GUARD_LOOKAROUND, + LEGS_GUARD_IDLE, +} animNumber_t; \ No newline at end of file diff --git a/utils/Assimilate/gla.cpp b/utils/Assimilate/gla.cpp new file mode 100644 index 0000000..7cabd1f --- /dev/null +++ b/utils/Assimilate/gla.cpp @@ -0,0 +1,176 @@ +// Filename:- gla.cpp +// +#include "Stdafx.h" + +typedef float vec_t; +typedef vec_t vec2_t[2]; +typedef vec_t vec3_t[3]; +typedef vec_t vec4_t[4]; + +#include "Includes.h" +#include "mdx_format.h" +// +#include "gla.h" + + +// returns actual filename only, no path +// +char *Filename_WithoutPath(LPCSTR psFilename) +{ + static char sString[MAX_PATH]; +/* + LPCSTR p = strrchr(psFilename,'\\'); + + if (!p++) + { + p = strrchr(psFilename,'/'); + if (!p++) + p=psFilename; + } + + strcpy(sString,p); +*/ + + LPCSTR psCopyPos = psFilename; + + while (*psFilename) + { + if (*psFilename == '/' || *psFilename == '\\') + psCopyPos = psFilename+1; + psFilename++; + } + + strcpy(sString,psCopyPos); + + return sString; + +} + + +// returns (eg) "\dir\name" for "\dir\name.bmp" +// +char *Filename_WithoutExt(LPCSTR psFilename) +{ + static char sString[MAX_PATH]; + + strcpy(sString,psFilename); + + char *p = strrchr(sString,'.'); + char *p2= strrchr(sString,'\\'); + char *p3= strrchr(sString,'/'); + + // special check, make sure the first suffix we found from the end wasn't just a directory suffix (eg on a path'd filename with no extension anyway) + // + if (p && + (p2==0 || (p2 && p>p2)) && + (p3==0 || (p3 && p>p3)) + ) + *p=0; + + return sString; + +} + + +// loses anything after the path (if any), (eg) "\dir\name.bmp" becomes "\dir" +// +char *Filename_PathOnly(LPCSTR psFilename) +{ + static char sString[MAX_PATH]; + + strcpy(sString,psFilename); + +// for (int i=0; ip2)?p1:p2; + if (p) + *p=0; + + return sString; + +} + + +// returns filename's extension only (including '.'), else returns original string if no '.' in it... +// +char *Filename_ExtOnly(LPCSTR psFilename) +{ + static char sString[MAX_PATH]; + LPCSTR p = strrchr(psFilename,'.'); + + if (!p) + p=psFilename; + + strcpy(sString,p); + + return sString; + +} + + + + +int GLA_ReadHeader(LPCSTR psFilename) +{ + LPCSTR psGameDir=""; // already full-pathed in this app.... + // the messy shit, block-copied from elsewhere to load this... + // + LPCSTR psFullFilename = va("%s%s.gla",psGameDir,Filename_WithoutExt(psFilename)); + FILE *fp=fopen( psFullFilename,"rb"); // "ra" -stefind + if (!fp){ + ErrorBox(va("GLA file '%s' not found!\n",psFullFilename)); + return 0; + } + + // sod it, I'm only interested in reading the header... + // + +// fseek(fp,0,SEEK_END); + int len=sizeof(mdxaHeader_t);//////ftell(fp); + char *filebin=new char[len]; +// fseek(fp,0,SEEK_SET); + unsigned int uiFileBytesRead; + if ( (uiFileBytesRead = fread(filebin,1,len,fp)) != (size_t) len ) + { + fclose(fp); + ErrorBox(va("Read error in GLA file '%s', trying to read %d bytes and got %d bytes\n",psFullFilename,len,uiFileBytesRead)); + delete (filebin); + return 0; + } + fclose(fp); + + + // ok, we've got it, now start doing something useful... + // + mdxaHeader_t *pGLAHeader = (mdxaHeader_t *) filebin; + if (pGLAHeader->ident != MDXA_IDENT) + { + ErrorBox(va("Error, GLA header ident is %d, expecting %d!\n", pGLAHeader->ident, MDXA_IDENT)); + delete(filebin); + return 0; + } + if (pGLAHeader->version != MDXA_VERSION && pGLAHeader->version != MDXA_VERSION_QUAT) + { + ErrorBox(va("Error: GLA header version is %d, expecting %d or %d!\n", pGLAHeader->version, MDXA_VERSION, MDXA_VERSION_QUAT)); + delete(filebin); + return 0; + } + + int iFrames = pGLAHeader->numFrames; + + delete (filebin); + + return iFrames; +} + + + + +//////////////// eof /////////////// + diff --git a/utils/Assimilate/gla.h b/utils/Assimilate/gla.h new file mode 100644 index 0000000..ff00dd2 --- /dev/null +++ b/utils/Assimilate/gla.h @@ -0,0 +1,26 @@ +// Filename:- gla.h +// + +#ifndef GLA_H +#define GLA_H + +int GLA_ReadHeader(LPCSTR psFilename); + + +// other junk that has to go somewhere... +// + +// returns actual filename only, no path +// +char *Filename_WithoutPath(LPCSTR psFilename); +char *Filename_WithoutExt(LPCSTR psFilename); +char *Filename_PathOnly(LPCSTR psFilename); +char *Filename_ExtOnly(LPCSTR psFilename); + + +#endif // #ifndef GLA_H + + +//////////////////// eof /////////////////// + + diff --git a/utils/Assimilate/includes.h b/utils/Assimilate/includes.h new file mode 100644 index 0000000..fadcd4c --- /dev/null +++ b/utils/Assimilate/includes.h @@ -0,0 +1,50 @@ +// includes.h + +#include "resource.h" // main symbols + +#pragma warning( disable : 4786 ) // identifier was truncated +#include +using namespace std; + +#include "Module.h" +#include "AlertErrHandler.h" +#include "Tokenizer.h" +#include "TxtFile.h" +#include "ASEFile.h" + +#include "Comment.h" +#include "Sequences.h" +#include "Model.h" + +#include "Assimilate.h" + +#include "MainFrm.h" +#include "AssimilateDoc.h" +#include "AssimilateView.h" +#include "animpicker.h" +#include "oddbits.h" +#include "YesNoYesAllNoAll.h" + +#define bDEFAULT_MULTIPLAYER_MODE false +#define sDEFAULT_ENUM_FILENAME "w:/bin/gamesource/anims.h" // "q:/bin_nt/SourceForBehavEd/anims.h" //"d:/source/startrek/utils4/assimilate/anims.h" +#define sDEFAULT_ENUM_FILENAME_MULTI "w:/bin/gamesource/anims.h" //"d:/source/startrek/utils4/assimilate/anims.h" +#define sDEFAULT_QDATA_LOCATION "k:\\util\\carcass.exe" // "q:/bin_nt/q3data.exe" +#define sDEFAULT_QUAKEDIR "w:/Game/base/" // "q:/quake/baseEF/" +#define dwDEFAULT_BUFFERSIZE 4096 // 1024 (having this is pretty gay) + +#define DATA_TO_DIALOG FALSE +#define DIALOG_TO_DATA TRUE + +#define EXTRA_LOD_LEVELS 2 // ie 2 in addition to normal LOD + +extern int giLODLevelOverride; + +#define YES 0 +#define NO 1 +#define YES_ALL 2 +#define NO_ALL 3 + +#ifndef MAX_QPATH +#define MAX_QPATH 64 +#endif + diff --git a/utils/Assimilate/matcomp.c b/utils/Assimilate/matcomp.c new file mode 100644 index 0000000..b4267ad --- /dev/null +++ b/utils/Assimilate/matcomp.c @@ -0,0 +1,237 @@ +#include "MatComp.h" + +#include +#include +#include +#include + +#define MC_MASK_X ((1<<(MC_BITS_X))-1) +#define MC_MASK_Y ((1<<(MC_BITS_Y))-1) +#define MC_MASK_Z ((1<<(MC_BITS_Z))-1) +#define MC_MASK_VECT ((1<<(MC_BITS_VECT))-1) + +#define MC_N_VECT (1<<(MC_BITS_VECT)) + +#define MC_POS_X (0) +#define MC_SHIFT_X (0) + +#define MC_POS_Y ((((MC_BITS_X))/8)) +#define MC_SHIFT_Y ((((MC_BITS_X)%8))) + +#define MC_POS_Z ((((MC_BITS_X+MC_BITS_Y))/8)) +#define MC_SHIFT_Z ((((MC_BITS_X+MC_BITS_Y)%8))) + +#define MC_POS_V1 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z))/8)) +#define MC_SHIFT_V1 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z)%8))) + +#define MC_POS_V2 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT))/8)) +#define MC_SHIFT_V2 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT)%8))) + +#define MC_POS_V3 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*2))/8)) +#define MC_SHIFT_V3 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*2)%8))) + +#define MAXBIT 30 +#define MAXDIM 6 + +void sobseq(int n, int *x) +{ + int j,k,l; + unsigned int i,im,ipp; + static unsigned int in,ix[MAXDIM+1],*iu[MAXBIT+1]; + static unsigned int mdeg[MAXDIM+1]={0,1,2,3,3,4,4}; + static unsigned int ip[MAXDIM+1]={0,0,1,1,2,1,4}; + static unsigned int iv[MAXDIM*MAXBIT+1]={ + 0,1,1,1,1,1,1,3,1,3,3,1,1,5,7,7,3,3,5,15,11,5,15,13,9}; + + if (n < 0) { + for (j=1,k=0;j<=MAXBIT;j++,k+=MAXDIM) iu[j] = &iv[k]; + for (k=1;k<=MAXDIM;k++) { + for (j=1;j<=(int)mdeg[k];j++) iu[j][k] <<= (MAXBIT-j); + for (j=mdeg[k]+1;j<=MAXBIT;j++) { + ipp=ip[k]; + i=iu[j-mdeg[k]][k]; + i ^= (i >> mdeg[k]); + for (l=mdeg[k]-1;l>=1;l--) { + if (ipp & 1) i ^= iu[j-l][k]; + ipp >>= 1; + } + iu[j][k]=i; + } + } + in=0; + } else { + im=in; + for (j=1;j<=MAXBIT;j++) { + if (!(im & 1)) break; + im >>= 1; + } + assert(j <= MAXBIT); + im=(j-1)*MAXDIM; + for (k=1;k<=n;k++) { + ix[k] ^= iv[im+k]; + x[k-1]=(int)(ix[k]>>14)-32768; + } + in++; + } +} + +static float MC_Norms[MC_N_VECT][3]; +static int MC_Inited=0; + +void MC_Init() +{ + int x[3]; + int sq,num=0; + int tnum=0; + float d; + MC_Inited=1; + sobseq(-3,0); + while (num17000*17000) + continue; + d=1.0f/(float)sqrt((float)sq); + MC_Norms[num][0]=d*((float)(x[0])); + MC_Norms[num][1]=d*((float)(x[1])); + MC_Norms[num][2]=d*((float)(x[2])); + num++; + } +#if 0 + printf("%d trys to get %d vals\n",tnum,num); + { + int i,j; + float mn=5.0f,mx; + for (i=0;imx) + mx=d; + } + } + if (mxmx) + { + mx=d; + mxat=i; + } + } + return mxat; +} + +void MC_Compress(const float mat[3][4],unsigned char * comp) +{ + int i,val; + if (!MC_Inited) + MC_Init(); + for (i=0;i=(1<=(1<=(1<>=MC_SHIFT_X; + uval&=MC_MASK_X; + val=(int)uval; + val-=1<<(MC_BITS_X-1); + mat[0][3]=((float)(val))*MC_SCALE_X; + + uval=*(unsigned int *)(comp+MC_POS_Y); + uval>>=MC_SHIFT_Y; + uval&=MC_MASK_Y; + val=(int)uval; + val-=1<<(MC_BITS_Y-1); + mat[1][3]=((float)(val))*MC_SCALE_Y; + + uval=*(unsigned int *)(comp+MC_POS_Z); + uval>>=MC_SHIFT_Z; + uval&=MC_MASK_Z; + val=(int)uval; + val-=1<<(MC_BITS_Z-1); + mat[2][3]=((float)(val))*MC_SCALE_Z; + + uval=*(unsigned int *)(comp+MC_POS_V1); + uval>>=MC_SHIFT_V1; + uval&=MC_MASK_VECT; + mat[0][0]=MC_Norms[uval][0]; + mat[0][1]=MC_Norms[uval][1]; + mat[0][2]=MC_Norms[uval][2]; + + uval=*(unsigned int *)(comp+MC_POS_V2); + uval>>=MC_SHIFT_V2; + uval&=MC_MASK_VECT; + mat[1][0]=MC_Norms[uval][0]; + mat[1][1]=MC_Norms[uval][1]; + mat[1][2]=MC_Norms[uval][2]; + + uval=*(unsigned int *)(comp+MC_POS_V3); + uval>>=MC_SHIFT_V3; + uval&=MC_MASK_VECT; + mat[2][0]=MC_Norms[uval][0]; + mat[2][1]=MC_Norms[uval][1]; + mat[2][2]=MC_Norms[uval][2]; + +} + diff --git a/utils/Assimilate/matcomp.h b/utils/Assimilate/matcomp.h new file mode 100644 index 0000000..bf49278 --- /dev/null +++ b/utils/Assimilate/matcomp.h @@ -0,0 +1,29 @@ +#ifndef __MATCOMP__ +#define __MATCOMP__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define MC_BITS_X (15) +#define MC_BITS_Y (15) +#define MC_BITS_Z (16) +#define MC_BITS_VECT (14) + +#define MC_SCALE_X (1.0f/32) +#define MC_SCALE_Y (1.0f/32) +#define MC_SCALE_Z (1.0f/64) + + +// currently 11 +#define MC_COMP_BYTES (((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*3)+7)/8) + +void MC_Compress(const float mat[3][4],unsigned char * comp); +void MC_UnCompress(float mat[3][4],const unsigned char * comp); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/utils/Assimilate/matrix4.cpp b/utils/Assimilate/matrix4.cpp new file mode 100644 index 0000000..c248206 --- /dev/null +++ b/utils/Assimilate/matrix4.cpp @@ -0,0 +1,1226 @@ +#include "matrix4.h" + +extern const float Pi=3.1415926535f; +extern const float Half_Pi=Pi/2.0; + + +#define MAX_MATRIX (12) + +#define MATRIX_DEBUG 0 + +void BackSub(float a[][MAX_MATRIX],int *indx,float *b,int sz) +{ + int i,j,ii=-1,ip; + float sum; + for (i=0;i=0) + { + for (j=ii;j=0;i--) + { + sum=b[i]; + for (j=i+1;jbig) + big=temp; + if (big=big) + { + big=dum; + imax=i; + } + } + if (j!=imax) + { + for (k=0;k=0) + { + for (j=ii;j=0;i--) + { + sum=b[i]; + for (j=i+1;jbig) + big=temp; + if (big=big) + { + big=dum; + imax=i; + } + } + if (j!=imax) + { + for (k=0;kMATRIX_TOL) + return 0; + } + else + { + if (fabs(m[i][j])>MATRIX_TOL) + return 0; + } + } + } + flags=MATFLAG_IDENTITY; + return flags; +} + + +void Matrix4::Identity() +{ + int i; + float *f; + for (f=m[0],i=0;i<16;i++,f++) + *f=0; + for (i=0;i<4;i++) + m[i][i]=1.0;; + flags=MATFLAG_IDENTITY; +} + +float Matrix4::MaxAbsElement() +{ + if (flags&MATFLAG_IDENTITY) + { + assert(IntegrityCheck()); + return 1.0f; + } + int i; + float best=-1,*f; + for (f=m[0],i=0;i<16;i++,f++) + { + if (fabs(*f)>best) + best=fabs(*f); + } + return best; +} + +void Matrix4::Zero() +{ + int i; + float *f; + for (f=m[0],i=0;i<16;i++,f++) + *f=0; + flags=0; +} + +float Matrix4::Det() +{ + if (flags&MATFLAG_IDENTITY) + { + assert(IntegrityCheck()); + return 1.0f; + } + float d; + int i,indx[4]; + Matrix4 lu=*this; + if (!LUDecomposition4(lu.m,indx,&d)) + assert(0); + for (i=0;i<4;i++) + d*=lu.m[i][i]; + return d; +} + + +void Matrix4::Translate(const float tx,const float ty,const float tz) +{ + int i; + float *f; + for (f=m[0],i=0;i<12;i++,f++) + *f=0; + for (i=0;i<4;i++) + m[i][i]=1.0;; + m[3][0]=tx; + m[3][1]=ty; + m[3][2]=tz; + CalcFlags(); +} + +void Matrix4::Translate(const Vect3 &t) +{ + int i; + float *f; + for (f=m[0],i=0;i<12;i++,f++) + *f=0; + for (i=0;i<4;i++) + m[i][i]=1.0;; + m[3][0]=t[0]; + m[3][1]=t[1]; + m[3][2]=t[2]; + CalcFlags(); +} + +void Matrix4::Rotate(int axis,const float theta) +{ + int i; + float *f,s,c; + for (f=m[0],i=0;i<16;i++,f++) + *f=0; + for (i=0;i<4;i++) + m[i][i]=1.0; + s=sin(theta); + c=cos(theta); + switch(axis) + { + case 0: + m[1][1]=c; + m[1][2]=-s; + m[2][1]=s; + m[2][2]=c; + break; + case 1: + m[0][0]=c; + m[0][2]=s; + m[2][0]=-s; + m[2][2]=c; + break; + case 2: + m[0][0]=c; + m[0][1]=-s; + m[1][0]=s; + m[1][1]=c; + break; + } + CalcFlags(); +} + +void Matrix4::Rotate(const float rx,const float ry,const float rz) +{ + Matrix4 x,y,z,t; + x.Rotate(0,rx); + y.Rotate(1,ry); + z.Rotate(2,rz); + t.Concat(y,z); + Concat(x,t); +} + +void Matrix4::Rotate(const Vect3 v) +{ + Matrix4 x,y,z,t; + + x.Rotate(0, v[0]); + y.Rotate(1, v[1]); + z.Rotate(2, v[2]); + + t.Concat(y, z); + Concat(x, t); +} + +void Matrix4::Scale(const float sx,const float sy,const float sz) +{ + int i; + float *f; + for (f=m[0],i=0;i<16;i++,f++) + *f=0; + assert(fabs(sx)>1E-10); + assert(fabs(sy)>1E-10); + assert(fabs(sz)>1E-10); + m[0][0]=sx; + m[1][1]=sy; + m[2][2]=sz; + m[3][3]=1.0; + CalcFlags(); +} + +void Matrix4::Scale(const float sx) +{ + int i; + float *f; + for (f=m[0],i=0;i<16;i++,f++) + *f=0; + assert(fabs(sx)>1E-10); + m[0][0]=sx; + m[1][1]=sx; + m[2][2]=sx; + m[3][3]=1.0; + CalcFlags(); +} + +void Matrix4::Interp(const Matrix4 &m1,float s1,const Matrix4 &m2,float s2) +{ + if ((m1.flags&MATFLAG_IDENTITY)&&(m2.flags&MATFLAG_IDENTITY)) + { + assert(m1.IntegrityCheck()); + assert(m2.IntegrityCheck()); + Identity(); + return; + } + int i; + const float *f1; + const float *f2; + float *fd; + for (f1=m1.m[0],f2=m2.m[0],fd=m[0],i=0;i<16;i++,f1++,f2++,fd++) + *fd=s1*(*f1)+s2*(*f2); + flags=0; +} + +void Matrix4::Interp(const Matrix4 &m1,const Matrix4 &m2,float s1) +{ + if ((m1.flags&MATFLAG_IDENTITY)&&(m2.flags&MATFLAG_IDENTITY)) + { + assert(m1.IntegrityCheck()); + assert(m2.IntegrityCheck()); + Identity(); + return; + } + int i; + const float *f1; + const float *f2; + float *fd; + for (f1=m1.m[0],f2=m2.m[0],fd=m[0],i=0;i<16;i++,f1++,f2++,fd++) + *fd=s1*((*f1)-(*f2))+(*f2); + flags=0; +} + + +bool Matrix4::operator== (const Matrix4& t) const +{ + if ((flags&MATFLAG_IDENTITY)&&(t.flags&MATFLAG_IDENTITY)) + { + assert(IntegrityCheck()); + assert(t.IntegrityCheck()); + return true; + } + int i,j; + for (i=0;i<4;i++) + { + for (j=0;j<3;j++) + { + if (fabs(m[i][j]-t.m[i][j])>1E-4f) + return false; + } + } + return true; +} + +void Matrix4::Concat(const Matrix4 &m1,const Matrix4 &m2) +{ + if (m1.flags&MATFLAG_IDENTITY) + { + assert(m1.IntegrityCheck()); + *this=m2; + } + else if (m2.flags&MATFLAG_IDENTITY) + { + assert(m2.IntegrityCheck()); + *this=m1; + } + else + { + + assert(this!=&m1&&this!=&m2); + /* + int i,j,k; + float acc=0; + for (i=0;i<4;i++) + { + for (j=0;j<4;j++) + { + for(k=0,acc=0.;k<4;k++) + acc+=m1.m[i][k]*m2.m[k][j]; + m[i][j]=acc; + } + } + */ + m2.HXFormPointND(m[0],m1.m[0]); + m2.HXFormPointND(m[1],m1.m[1]); + m2.HXFormPointND(m[2],m1.m[2]); + m2.HXFormPointND(m[3],m1.m[3]); + flags=0; + } +} + +void Matrix4::XFormPoint(Vect3 &dest,const Vect3 &src) const +{ + if (flags&MATFLAG_IDENTITY) + { + assert(IntegrityCheck()); + dest=src; + return; + } +/* + dest[0]=m[0][0]*src[0]+m[1][0]*src[1]+m[2][0]*src[2]+m[3][0]; + dest[1]=m[0][0]*src[0]+m[1][0]*src[1]+m[2][0]*src[2]+m[3][1]; + dest[2]=m[3][2]; +*/ +/* + int i,j; + dest[0]=m[3][0]; + dest[1]=m[3][1]; + dest[2]=m[3][2]; + for (i=0;i<3;i++) + for (j=0;j<3;j++) + dest[i]+=m[j][i]*src[j]; +*/ + const float *s=&src.x(); + float *d=&dest.x(); + const float *mat=&m[0][0]; + __asm + { + mov ebx,[s] + mov eax,[mat] + mov ecx,[d] + fld [DWORD PTR ebx+8] // z + fld [DWORD PTR ebx+4] // y z + fld [DWORD PTR ebx] // x y z + + fld [DWORD PTR eax] // m00 x y z 1/m33+m23z+m13y+xm03 + fmul st,st(1) // m00x x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+4] // m01 m00x x y z 1/m33+m23z+m13y+xm03 + fmul st,st(2) // m01x m00x x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+8] // m02 m01x m00x x y z 1/m33+m23z+m13y+xm03 + fmulp st(3),st // m01x m00x m02x y z 1/m33+m23z+m13y+xm03 + fxch st(1) // m00x m01x m02x y z 1/m33+m23z+m13y+xm03 + fxch st(2) // m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+16] // m10 m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fmul st,st(4) // m10y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+20] // m11 m10y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + + + fmul st,st(5) // m11y m10y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fxch st(1) // m10y m11y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + faddp st(4),st // m11y m02x m01x m00x+m10y y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+24] // m12 m11y m02x m01x m00x+m10y y z 1/m33+m23z+m13y+xm03 + fmulp st(5),st // m11y m02x m01x m00x+m10y m12y z 1/m33+m23z+m13y+xm03 + + faddp st(2),st // m02x m01x+m11y m00x+m10y m12y z 1/m33+m23z+m13y+xm03 + faddp st(3),st // m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + + fld [DWORD PTR eax+32] // m20 m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + fmul st,st(4) // m20z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+36] // m21 m20z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + + fmul st,st(5) // m21z m20z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + fxch st(1) // m20z m21z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + faddp st(3),st // m21z m01x+m11y m00x+m10y+m20z m12y+m02x z 1/m33+m23z+m13y+xm03 + + fld [DWORD PTR eax+40] // m22 m21z m01x+m11y m00x+m10y+m20z m12y+m02x z 1/m33+m23z+m13y+xm03 + fmulp st(5),st // m21z m01x+m11y m00x+m10y+m20z m12y+m02x m22z 1/m33+m23z+m13y+xm03 + faddp st(1),st // m21z+m01x+m11y m00x+m10y+m20z m12y+m02x m22z 1/m33+m23z+m13y+xm03 + fxch st(3) // m22z m00x+m10y+m20z m12y+m02x m21z+m01x+m11y 1/m33+m23z+m13y+xm03 + faddp st(2),st // m00x+m10y+m20z m22z+m12y+m02x m21z+m01x+m11y 1/m33+m23z+m13y+xm03 + + + fadd [DWORD PTR eax+48] // m00x+m10y+m20z+m30 m22z+m12y+m02x m21z+m01x+m11y 1/m33+m23z+m13y+xm03 + fxch st(2) // m21z+m01x+m11y m22z+m12y+m02x m00x+m10y+m20z+m30 1/m33+m23z+m13y+xm03 + fadd [DWORD PTR eax+52] // m21z+m01x+m11y+m31 m22z+m12y+m02x m00x+m10y+m20z+m30 1/m33+m23z+m13y+xm03 + fxch st(1) // m22z+m12y+m02x m21z+m01x+m11y+m31 m00x+m10y+m20z+m30 1/m33+m23z+m13y+xm03 + fadd [DWORD PTR eax+56] // m22z+m12y+m02x+m32 m21z+m01x+m11y+m31 m00x+m10y+m20z+m30 1/m33+m23z+m13y+xm03 + + fxch st(2) // m00x+m10y+m20z+m30 m21z+m01x+m11y+m31 m22z+m12y+m02x+m32 1/m33+m23z+m13y+xm03 + fstp [DWORD PTR ecx] + fstp [DWORD PTR ecx+4] + fstp [DWORD PTR ecx+8] + } +} + +void Matrix4::XFormVect(Vect3 &dest,const Vect3 &src) const +{ + if (flags&MATFLAG_IDENTITY) + { + dest=src; + return; + } + int i,j; + dest=0.; + for (i=0;i<3;i++) + for (j=0;j<3;j++) + dest[i]+=m[j][i]*src[j]; +} + +void Matrix4::XFormVectTranspose(Vect3 &dest,const Vect3 &src) const +{ + if (flags&MATFLAG_IDENTITY) + { + assert(IntegrityCheck()); + dest=src; + return; + } +/* + int i,j; + dest=0.; + for (i=0;i<3;i++) + for (j=0;j<3;j++) + dest[i]+=m[i][j]*src[j]; +*/ + const float *s=&src.x(); + float *d=&dest.x(); + const float *mat=&m[0][0]; + __asm + { + mov ebx,[s] + mov eax,[mat] + mov ecx,[d] + fld [DWORD PTR ebx+8] // z + fld [DWORD PTR ebx+4] // y z + fld [DWORD PTR ebx] // x y z + + fld [DWORD PTR eax] // m00 x y z 1/m33+m23z+m13y+xm03 + fmul st,st(1) // m00x x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+16] // m01 m00x x y z 1/m33+m23z+m13y+xm03 + fmul st,st(2) // m01x m00x x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+32] // m02 m01x m00x x y z 1/m33+m23z+m13y+xm03 + fmulp st(3),st // m01x m00x m02x y z 1/m33+m23z+m13y+xm03 + fxch st(1) // m00x m01x m02x y z 1/m33+m23z+m13y+xm03 + fxch st(2) // m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+4] // m10 m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fmul st,st(4) // m10y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+20] // m11 m10y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + + + fmul st,st(5) // m11y m10y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fxch st(1) // m10y m11y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + faddp st(4),st // m11y m02x m01x m00x+m10y y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+36] // m12 m11y m02x m01x m00x+m10y y z 1/m33+m23z+m13y+xm03 + fmulp st(5),st // m11y m02x m01x m00x+m10y m12y z 1/m33+m23z+m13y+xm03 + + faddp st(2),st // m02x m01x+m11y m00x+m10y m12y z 1/m33+m23z+m13y+xm03 + faddp st(3),st // m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + + fld [DWORD PTR eax+8] // m20 m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + fmul st,st(4) // m20z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+24] // m21 m20z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + + fmul st,st(5) // m21z m20z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + fxch st(1) // m20z m21z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + faddp st(3),st // m21z m01x+m11y m00x+m10y+m20z m12y+m02x z 1/m33+m23z+m13y+xm03 + + fld [DWORD PTR eax+40] // m22 m21z m01x+m11y m00x+m10y+m20z m12y+m02x z 1/m33+m23z+m13y+xm03 + fmulp st(5),st // m21z m01x+m11y m00x+m10y+m20z m12y+m02x m22z 1/m33+m23z+m13y+xm03 + faddp st(1),st // m21z+m01x+m11y m00x+m10y+m20z m12y+m02x m22z 1/m33+m23z+m13y+xm03 + fxch st(3) // m22z m00x+m10y+m20z m12y+m02x m21z+m01x+m11y 1/m33+m23z+m13y+xm03 + faddp st(2),st // m00x+m10y+m20z m22z+m12y+m02x m21z+m01x+m11y 1/m33+m23z+m13y+xm03 + fstp [DWORD PTR ecx] + fstp [DWORD PTR ecx+8] + fstp [DWORD PTR ecx+4] + } +} + +#pragma warning( disable : 4725) //instruction may be inaccurate on some Pentiums + +float Matrix4::HXFormPoint(Vect3 &dest,const Vect3 &src) const +{ +/* + int i,j; + float h; + h=m[3][3]; + h+=m[0][3]*src[0]; + h+=m[1][3]*src[1]; + h+=m[2][3]*src[2]; + h=1.0f/h; + for (i=0;i<3;i++) + { + dest[i]=m[3][i]; + for (j=0;j<3;j++) + dest[i]+=m[j][i]*src[j]; + dest[i]*=h; + } +// return h; +*/ + static const float one=1.0f; + const float *s=&src.x(); + float *d=&dest.x(); + const float *mat=&m[0][0]; + float tret; + __asm + { + mov ebx,[s] + mov eax,[mat] + mov ecx,[d] + fld [DWORD PTR ebx] // x + fld [DWORD PTR eax+12] // m03 x + fmul st,st(1) // xm03 x + fld [DWORD PTR ebx+4] // y xm03 x + fld [DWORD PTR eax+28] // m13 y xm03 x + fmul st,st(1) // m13y y xm03 x + + fld [DWORD PTR ebx+8] // z m13y y xm03 x + fld [DWORD PTR eax+44] // m23 z m13y y xm03 x + fmul st,st(1) // m23z z m13y y xm03 x + fxch st(2) // m13y z m23z y xm03 x + faddp st(4),st // z m23z y m13y+xm03 x + fxch st(1) // m23z z y m13y+xm03 x + faddp st(3),st // z y m23z+m13y+xm03 x + fxch st(2) // m23z+m13y+xm03 y z x + + fadd [DWORD PTR eax+60] // m33+m23z+m13y+xm03 y z x + fdivr [one] // 1/m33+m23z+m13y+xm03 y z x + fxch st(3) // x y z 1/m33+m23z+m13y+xm03 + mov edx,[DWORD PTR eax] + mov edx,[DWORD PTR ecx] + mov edx,[DWORD PTR ecx+8] + + + + fld [DWORD PTR eax] // m00 x y z 1/m33+m23z+m13y+xm03 + fmul st,st(1) // m00x x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+4] // m01 m00x x y z 1/m33+m23z+m13y+xm03 + fmul st,st(2) // m01x m00x x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+8] // m02 m01x m00x x y z 1/m33+m23z+m13y+xm03 + fmulp st(3),st // m01x m00x m02x y z 1/m33+m23z+m13y+xm03 + fxch st(1) // m00x m01x m02x y z 1/m33+m23z+m13y+xm03 + fxch st(2) // m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+16] // m10 m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fmul st,st(4) // m10y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+20] // m11 m10y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + + + fmul st,st(5) // m11y m10y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fxch st(1) // m10y m11y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + faddp st(4),st // m11y m02x m01x m00x+m10y y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+24] // m12 m11y m02x m01x m00x+m10y y z 1/m33+m23z+m13y+xm03 + fmulp st(5),st // m11y m02x m01x m00x+m10y m12y z 1/m33+m23z+m13y+xm03 + + faddp st(2),st // m02x m01x+m11y m00x+m10y m12y z 1/m33+m23z+m13y+xm03 + faddp st(3),st // m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + + fld [DWORD PTR eax+32] // m20 m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + fmul st,st(4) // m20z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+36] // m21 m20z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + + fmul st,st(5) // m21z m20z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + fxch st(1) // m20z m21z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + faddp st(3),st // m21z m01x+m11y m00x+m10y+m20z m12y+m02x z 1/m33+m23z+m13y+xm03 + + fld [DWORD PTR eax+40] // m22 m21z m01x+m11y m00x+m10y+m20z m12y+m02x z 1/m33+m23z+m13y+xm03 + fmulp st(5),st // m21z m01x+m11y m00x+m10y+m20z m12y+m02x m22z 1/m33+m23z+m13y+xm03 + faddp st(1),st // m21z+m01x+m11y m00x+m10y+m20z m12y+m02x m22z 1/m33+m23z+m13y+xm03 + fxch st(3) // m22z m00x+m10y+m20z m12y+m02x m21z+m01x+m11y 1/m33+m23z+m13y+xm03 + faddp st(2),st // m00x+m10y+m20z m22z+m12y+m02x m21z+m01x+m11y 1/m33+m23z+m13y+xm03 + + + fadd [DWORD PTR eax+48] // m00x+m10y+m20z+m30 m22z+m12y+m02x m21z+m01x+m11y 1/m33+m23z+m13y+xm03 + fxch st(2) // m21z+m01x+m11y m22z+m12y+m02x m00x+m10y+m20z+m30 1/m33+m23z+m13y+xm03 + fadd [DWORD PTR eax+52] // m21z+m01x+m11y+m31 m22z+m12y+m02x m00x+m10y+m20z+m30 1/m33+m23z+m13y+xm03 + fxch st(1) // m22z+m12y+m02x m21z+m01x+m11y+m31 m00x+m10y+m20z+m30 1/m33+m23z+m13y+xm03 + fadd [DWORD PTR eax+56] // m22z+m12y+m02x+m32 m21z+m01x+m11y+m31 m00x+m10y+m20z+m30 1/m33+m23z+m13y+xm03 + + fxch st(2) // m00x+m10y+m20z+m30 m21z+m01x+m11y+m31 m22z+m12y+m02x+m32 1/m33+m23z+m13y+xm03 + fmul st,st(3) // sx m21z+m01x+m11y+m31 m22z+m12y+m02x+m32 1/m33+m23z+m13y+xm03 + fxch st(1) // m21z+m01x+m11y+m31 sx m22z+m12y+m02x+m32 1/m33+m23z+m13y+xm03 + fmul st,st(3) // sy sx m22z+m12y+m02x+m32 1/m33+m23z+m13y+xm03 + fxch st(2) // m22z+m12y+m02x+m32 sx sy 1/m33+m23z+m13y+xm03 + fmul st,st(3) // sz sx sy rhw + fstp [DWORD PTR ecx+8] + fstp [DWORD PTR ecx] + fstp [DWORD PTR ecx+4] + fstp [tret] + } + return tret; +} +#pragma warning( default: 4725) //instruction may be inaccurate on some Pentiums + +void Matrix4::HXFormPointND(float *d,const float *s) const +{ +/* + int i,j; + float h; + h=m[3][3]; + h+=m[0][3]*src[0]; + h+=m[1][3]*src[1]; + h+=m[2][3]*src[2]; + h=1.0f/h; + for (i=0;i<3;i++) + { + dest[i]=m[3][i]; + for (j=0;j<3;j++) + dest[i]+=m[j][i]*src[j]; + dest[i]*=h; + } +// return h; +*/ + const float *mat=&m[0][0]; + __asm + { + mov ebx,[s] + mov eax,[mat] + mov ecx,[d] + fld [DWORD PTR ebx] // x + fld [DWORD PTR eax+12] // m03 x + fmul st,st(1) // xm03 x + fld [DWORD PTR ebx+4] // y xm03 x + fld [DWORD PTR eax+28] // m13 y xm03 x + fmul st,st(1) // m13y y xm03 x + + fld [DWORD PTR ebx+8] // z m13y y xm03 x + fld [DWORD PTR eax+44] // m23 z m13y y xm03 x + fmul st,st(1) // m23z z m13y y xm03 x + fxch st(2) // m13y z m23z y xm03 x + faddp st(4),st // z m23z y m13y+xm03 x + fxch st(1) // m23z z y m13y+xm03 x + faddp st(3),st // z y m23z+m13y+xm03 x + fxch st(2) // m23z+m13y+xm03 y z x + fld [DWORD PTR ebx+12] // m33+m23z+m13y+xm03 y z x + fmul [DWORD PTR eax+60] // m33+m23z+m13y+xm03 y z x + faddp st(1),st + fxch st(3) // x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax] // m00 x y z 1/m33+m23z+m13y+xm03 + fmul st,st(1) // m00x x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+4] // m01 m00x x y z 1/m33+m23z+m13y+xm03 + fmul st,st(2) // m01x m00x x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+8] // m02 m01x m00x x y z 1/m33+m23z+m13y+xm03 + fmulp st(3),st // m01x m00x m02x y z 1/m33+m23z+m13y+xm03 + fxch st(1) // m00x m01x m02x y z 1/m33+m23z+m13y+xm03 + fxch st(2) // m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+16] // m10 m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fmul st,st(4) // m10y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+20] // m11 m10y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + + + fmul st,st(5) // m11y m10y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fxch st(1) // m10y m11y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + faddp st(4),st // m11y m02x m01x m00x+m10y y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+24] // m12 m11y m02x m01x m00x+m10y y z 1/m33+m23z+m13y+xm03 + fmulp st(5),st // m11y m02x m01x m00x+m10y m12y z 1/m33+m23z+m13y+xm03 + + faddp st(2),st // m02x m01x+m11y m00x+m10y m12y z 1/m33+m23z+m13y+xm03 + faddp st(3),st // m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + + fld [DWORD PTR eax+32] // m20 m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + fmul st,st(4) // m20z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+36] // m21 m20z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + + fmul st,st(5) // m21z m20z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + fxch st(1) // m20z m21z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + faddp st(3),st // m21z m01x+m11y m00x+m10y+m20z m12y+m02x z 1/m33+m23z+m13y+xm03 + + fld [DWORD PTR eax+40] // m22 m21z m01x+m11y m00x+m10y+m20z m12y+m02x z 1/m33+m23z+m13y+xm03 + fmulp st(5),st // m21z m01x+m11y m00x+m10y+m20z m12y+m02x m22z 1/m33+m23z+m13y+xm03 + faddp st(1),st // m21z+m01x+m11y m00x+m10y+m20z m12y+m02x m22z 1/m33+m23z+m13y+xm03 + fxch st(3) // m22z m00x+m10y+m20z m12y+m02x m21z+m01x+m11y 1/m33+m23z+m13y+xm03 + faddp st(2),st // m00x+m10y+m20z m22z+m12y+m02x m21z+m01x+m11y 1/m33+m23z+m13y+xm03 + + +// fadd [DWORD PTR eax+48] // m00x+m10y+m20z+m30 m22z+m12y+m02x m21z+m01x+m11y 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+48] + fmul [DWORD PTR ebx+12] + faddp st(1),st + fxch st(2) // m21z+m01x+m11y m22z+m12y+m02x m00x+m10y+m20z+m30 1/m33+m23z+m13y+xm03 + + fld [DWORD PTR eax+52] + fmul [DWORD PTR ebx+12] + faddp st(1),st + fxch st(1) // m22z+m12y+m02x m21z+m01x+m11y+m31 m00x+m10y+m20z+m30 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+56] + fmul [DWORD PTR ebx+12] + faddp st(1),st + + fstp [DWORD PTR ecx+8] + fstp [DWORD PTR ecx+4] + fstp [DWORD PTR ecx] + fstp [DWORD PTR ecx+12] + } +} + + + +void Matrix4::From3x4(const float mat[3][4]) +{ + for (int i=0; i<4; i++) + { + for (int j=0; j<3; j++) + { + m[i][j]=mat[j][i]; + } + } + + m[0][3] = 0.0f; + m[1][3] = 0.0f; + m[2][3] = 0.0f; + m[3][3] = 1.0f; + + CalcFlags(); +} + +void Matrix4::To3x4(float mat[3][4]) const +{ + for (int i=0; i<4; i++) + { + for (int j=0; j<3; j++) + { + mat[j][i] = m[i][j]; + } + } +} + +void Matrix4::SetRow(int i,const Vect3 &t) +{ + m[i][0]=t[0]; + m[i][1]=t[1]; + m[i][2]=t[2]; + flags=0; +} + +void Matrix4::SetColumn(int i,const Vect3 &t) +{ + m[0][i]=t[0]; + m[1][i]=t[1]; + m[2][i]=t[2]; + flags=0; +} + +void Matrix4::SetRow(int i) +{ + m[i][0]=0; + m[i][1]=0; + m[i][2]=0; + flags=0; +} + +void Matrix4::SetColumn(int i) +{ + m[0][i]=0; + m[1][i]=0; + m[2][i]=0; + flags=0; +} + +void Matrix4::SetFromDouble(double *d) +{ + int i; + float *f; + for (f=m[0],i=0;i<16;i++,f++,d++) + *f=(float)*d; + flags=0; +} + + +void Matrix4::MultiplyColumn(int i,float f) +{ + m[0][i]*=f; + m[1][i]*=f; + m[2][i]*=f; + flags=0; +} + +float Matrix4::GetColumnLen(int i) +{ + float d; + d=m[0][i]*m[0][i]+m[1][i]*m[1][i]+m[2][i]*m[2][i]; + assert(d>EPS_MATRIX); + return sqrt(d); +} + +void Matrix4::Transpose() +{ + int i,j; + float swap; + for (i=0;i<4;i++) + { + for (j=i+1;j<4;j++) + { + swap=m[i][j]; + m[i][j]=m[j][i]; + m[j][i]=swap; + } + } +} + diff --git a/utils/Assimilate/matrix4.h b/utils/Assimilate/matrix4.h new file mode 100644 index 0000000..121ed2e --- /dev/null +++ b/utils/Assimilate/matrix4.h @@ -0,0 +1,79 @@ +#if !defined(MATRIX4_INC) +#define MATRIX4_INC + +#include "vect3.h" +#include +#include + +#pragma warning( disable : 4244) +extern const float Pi; +extern const float Half_Pi; + + +#define MATFLAG_IDENTITY (1) +class Matrix4 +{ + float m[4][4]; + int flags; +public: + Matrix4() {flags=0;} + Matrix4(const Matrix4 &o); + float* operator[](int i) {return m[i];flags=0;} + const float* operator[](int i) const {return m[i];} + void SetElem(int r,int c,float v) {m[r][c]=v;flags=0;} + const float &Elem(int r,int c) const {return m[r][c];} + void SetFromMem(const float *mem) {memcpy(m,mem,sizeof(float)*16);CalcFlags();} + void GetFromMem(float *mem) const {memcpy(mem,m,sizeof(float)*16);} + void Identity(); + void Zero(); + int CalcFlags(); + int GetFlags() const {return flags;} + bool IntegrityCheck() const; + void Translate(const float tx,const float ty,const float tz); + void Translate(const Vect3 &t); + void Rotate(int axis,const float theta); + void Rotate(const float rx,const float ry,const float rz); + void Rotate(const Vect3 v); + void Scale(const float sx,const float sy,const float sz); + void Scale(const float sx); + void Concat(const Matrix4 &m1,const Matrix4 &m2); + void Interp(const Matrix4 &m1,float s1,const Matrix4 &m2,float s2); + void Interp(const Matrix4 &m1,const Matrix4 &m2,float s1); + void XFormPoint(Vect3 &dest,const Vect3 &src) const; + float HXFormPoint(Vect3 &dest,const Vect3 &src) const; + void HXFormPointND(float *dest,const float *src) const; + void XFormVect(Vect3 &dest,const Vect3 &src) const; + void XFormVectTranspose(Vect3 &dest,const Vect3 &src) const; + void SetRow(int i,const Vect3 &t); + void SetColumn(int i,const Vect3 &t); + void SetRow(int i); + void SetColumn(int i); + void SetFromDouble(double *d); + void MultiplyColumn(int i,float f); + float GetColumnLen(int i); + void Inverse(const Matrix4 &old); + void OrthoNormalInverse(const Matrix4 &old); + void FindFromPoints(const Vect3 base1[4],const Vect3 base2[4]); + void MakeEquiscalar(); + float Det(); + float MaxAbsElement(); + void GetRow(int r,Vect3 &v) const {v.x()=m[r][0];v.y()=m[r][1];v.z()=m[r][2];} + void GetColumn(int r,Vect3 &v) const {v.x()=m[0][r];v.y()=m[1][r];v.z()=m[2][r];} + void Transpose(); + bool operator== (const Matrix4& t) const; + void From3x4(const float mat[3][4]); + void To3x4(float mat[3][4]) const; +}; + + +int GetTextureSystem( + const Vect3 &p0,const Vect3 &uv0, //vertex 0 + const Vect3 &p1,const Vect3 &uv1, //vertex 1 + const Vect3 &p2,const Vect3 &uv2, //vertex 2 + const Vect3 &n, // normal vector for triangle + Vect3 &M, // returned gradient wrt u + Vect3 &N, // returned gradient wrt v + Vect3 &P); // returned location in world space where u=v=0 + + +#endif \ No newline at end of file diff --git a/utils/Assimilate/mdx_format.h b/utils/Assimilate/mdx_format.h new file mode 100644 index 0000000..e8239d0 --- /dev/null +++ b/utils/Assimilate/mdx_format.h @@ -0,0 +1,338 @@ +// Filename:- mdx_format.h +// +// DO NOT UPDATE THIS FILE IN ANY WAY WHATSOEVER WITHOUT TELLING ME (-Ste), +// BECAUSE THE MASTER COPY IS IN A DIFFERENT SOURCESAFE DATABASE AND WILL +// JUST GET PASTED OVER THIS ONE WHENEVER I CHANGE IT. +// +// +// +// MDX file format (typically uses file extension GLX for mesh, and GLA for anim/skeleton file) +// +// Notes: +// +// - All offset fields are relative to the address of the structure they occur in +// - So far, the only external symbol needed is MAX_QPATH, plus the typedefs for vec3_t, vec2_t etc + +#ifndef MDX_FORMAT_H +#define MDX_FORMAT_H + + +#define MDXM_IDENT (('M'<<24)+('G'<<16)+('L'<<8)+'2') +#define MDXA_IDENT (('A'<<24)+('G'<<16)+('L'<<8)+'2') + +#define MAX_TAGNAME_BYTES 32 // matches MDR, can be changed if nec. + +// +// normal version numbers... +// +#define MDXM_VERSION 4 +#define MDXA_VERSION 4 +#define MDXA_VERSION_QUAT 5 + +// (Note that since there is now a "_info.txt" file written out by carcass any changes made in here that +// introduce new data should also be reflected in the info-output) + +// 32 bit-flags for ghoul2 bone properties... (all undefined fields will be blank) +// +#define G2BONEFLAG_ALWAYSXFORM 0x00000001 + +// same thing but for surfaces... (Carcass will only generate 1st 2 flags, others are ingame +// +#define G2SURFACEFLAG_ISBOLT 0x00000001 +#define G2SURFACEFLAG_OFF 0x00000002 // saves strcmp()ing for "_off" in surface names +#define G2SURFACEFLAG_SPARE0 0x00000004 // future-expansion fields, saves invalidating models if we add more +#define G2SURFACEFLAG_SPARE1 0x00000008 // +#define G2SURFACEFLAG_SPARE2 0x00000010 // +#define G2SURFACEFLAG_SPARE3 0x00000020 // +#define G2SURFACEFLAG_SPARE4 0x00000040 // +#define G2SURFACEFLAG_SPARE5 0x00000080 // +// +#define G2SURFACEFLAG_NODESCENDANTS 0x00000100 // ingame-stuff, never generated by Carcass.... +#define G2SURFACEFLAG_GENERATED 0x00000200 // + + + +// triangle side-ordering stuff for tags... +// +#define iG2_TRISIDE_MIDDLE 1 +#define iG2_TRISIDE_LONGEST 0 +#define iG2_TRISIDE_SHORTEST 2 + + +#define MAX_G2_BONEREFS_PER_SURFACE 28 // currently only enforced when compiling Q3/G2 models, not xmen view-only tests +#define sDEFAULT_GLA_NAME "*default" // used when making special simple ghoul2 models, usually from MD3 files + + +//////////////////////////////////// +// +// these structs are defined here purely because of structure dependancy order... +// +typedef struct { + int boneIndex; // these are indexes into the surface boneReferences, not the global bone index + float boneWeight; // not the global per-frame bone list +} mdxmWeight_t; + + +#ifdef __cplusplus +struct mdxaCompBone_t +#else +typedef struct +#endif +{ + unsigned char Comp[24]; // MC_COMP_BYTES is in MatComp.h, but don't want to couple + + // I'm defining this '<' operator so this struct can be used as an STL key... + // + #ifdef __cplusplus + bool operator < (const mdxaCompBone_t& _X) const {return (memcmp(Comp,_X.Comp,sizeof(Comp))<0);} + #endif +} +#ifndef __cplusplus +mdxaCompBone_t +#endif +; + +#ifdef __cplusplus +struct mdxaCompQuatBone_t +#else +typedef struct +#endif +{ + unsigned char Comp[14]; + + // I'm defining this '<' operator so this struct can be used as an STL key... + // + #ifdef __cplusplus + bool operator < (const mdxaCompQuatBone_t& _X) const {return (memcmp(Comp,_X.Comp,sizeof(Comp))<0);} + #endif +} +#ifndef __cplusplus +mdxaCompQuatBone_t +#endif +; + + +#ifndef MDXABONEDEF +typedef struct { + float matrix[3][4]; +} mdxaBone_t; +#endif + +//////////////////////////////////// + + + + + + +// mdxHeader_t - this contains the header for the file, with sanity checking and version checking, plus number of lod's to be expected +// +typedef struct { + // + // ( first 3 fields are same format as MD3/MDR so we can apply easy model-format-type checks ) + // + int ident; // "IDP3" = MD3, "RDM5" = MDR, "2LGM"(GL2 Mesh) = MDX (cruddy char order I know, but I'm following what was there in other versions) + int version; // 1,2,3 etc as per format revision + char name[MAX_QPATH]; // model name (eg "models/players/marine.glm") // note: extension supplied + char animName[MAX_QPATH];// name of animation file this mesh requires // note: extension missing + int animIndex; // filled in by game (carcass defaults it to 0) + + int numBones; // (for ingame version-checks only, ensure we don't ref more bones than skel file has) + + int numLODs; + int ofsLODs; + + int numSurfaces; // now that surfaces are drawn hierarchically, we have same # per LOD + int ofsSurfHierarchy; + + int ofsEnd; // EOF, which of course gives overall file size +} mdxmHeader_t; + + +// for each surface (doesn't actually need a struct for this, just makes source clearer) +// { + typedef struct + { + int offsets[1]; // variable sized (mdxmHeader_t->numSurfaces), each offset points to a mdxmSurfHierarchy_t below + } mdxmHierarchyOffsets_t; +// } + +// for each surface... (mdxmHeader_t->numSurfaces) +// { + // mdxmSurfHierarchy_t - contains hierarchical info for surfaces... + + typedef struct { + char name[MAX_QPATH]; + unsigned int flags; + char shader[MAX_QPATH]; + int shaderIndex; // for in-game use (carcass defaults to 0) + int parentIndex; // this points to the index in the file of the parent surface. -1 if null/root + int numChildren; // number of surfaces which are children of this one + int childIndexes[1]; // [mdxmSurfHierarch_t->numChildren] (variable sized) + } mdxmSurfHierarchy_t; // struct size = (int)( &((mdxmSurfHierarch_t *)0)->childIndexes[ mdxmSurfHierarch_t->numChildren ] ); +// } + + +// for each LOD... (mdxmHeader_t->numLODs) +// { + // mdxLOD_t - this contains the header for this LOD. Contains num of surfaces, offset to surfaces and offset to next LOD. Surfaces are shader sorted, so each surface = 1 shader + + typedef struct { + // (used to contain numSurface/ofsSurfaces fields, but these are same per LOD level now) + // + int ofsEnd; // offset to next LOD + } mdxmLOD_t; + + + typedef struct { // added in GLM version 3 for ingame use at Jake's request + int offsets[1]; // variable sized (mdxmHeader_t->numSurfaces), each offset points to surfaces below + } mdxmLODSurfOffset_t; + + + // for each surface... (mdxmHeader_t->numSurfaces) + // { + // mdxSurface_t - reuse of header format containing surface name, number of bones, offset to poly data and number of polys, offset to vertex information, and number of verts. NOTE offsets are relative to this header. + + typedef struct { + int ident; // this one field at least should be kept, since the game-engine may switch-case (but currently=0 in carcass) + + int thisSurfaceIndex; // 0...mdxmHeader_t->numSurfaces-1 (because of how ingame renderer works) + + int ofsHeader; // this will be a negative number, pointing back to main header + + int numVerts; + int ofsVerts; + + int numTriangles; + int ofsTriangles; + + int maxVertBoneWeights; // ... per vertex for hardware to reference. This number subtract the vert->numWeights gives # pad weights (which software will ignore) + + // Bone references are a set of ints representing all the bones + // present in any vertex weights for this surface. This is + // needed because a model may have surfaces that need to be + // drawn at different sort times, and we don't want to have + // to re-interpolate all the bones for each surface. + // + int numBoneReferences; + int ofsBoneReferences; + + int ofsEnd; // next surface follows + + } mdxmSurface_t; + + + // for each triangle... (mdxmSurface_t->numTriangles) + // { + // mdxTriangle_t - contains indexes into verts. One struct entry per poly. + + typedef struct { + int indexes[3]; + } mdxmTriangle_t; + // } + + + // for each vert... (mdxmSurface_t->numVerts) + // { + // mdxVertex_t - this is an array with number of verts from the surface definition as its bounds. It contains normal info, texture coors and number of weightings for this bone + + typedef struct { + vec3_t normal; + vec3_t vertCoords; + vec2_t texCoords; + int numWeights; // remember, this is for software counts, look at mdxmSurface_t->numActualWeights for skipping purposes to account for padded weights + mdxmWeight_t weights[1]; // variable sized + } mdxmVertex_t; + + // } vert + + // } surface +// } LOD + + + +//---------------------------------------------------------------------------- +// seperate file here for animation data... +// + + +// mdxaHeader_t - this contains the header for the file, with sanity checking and version checking, plus number of lod's to be expected +// +typedef struct { + // + // ( first 3 fields are same format as MD3/MDR so we can apply easy model-format-type checks ) + // + int ident; // "IDP3" = MD3, "RDM5" = MDR, "2LGA"(GL2 Anim) = MDXA + int version; // 1,2,3 etc as per format revision + // + char name[MAX_QPATH]; // GLA name (eg "skeletons/marine") // note: extension missing + float fScale; // will be zero if build before this field was defined, else scale it was built with + + // frames and bones are shared by all levels of detail + // + int numFrames; + int ofsFrames; + int numBones; // (no offset to these since they're inside the frames array) + int ofsCompBonePool; // offset to global compressed-bone pool that all frames use + int ofsSkel; // offset to mdxaSkel_t info + + int ofsEnd; // EOF, which of course gives overall file size + +} mdxaHeader_t; + + +// for each bone... (doesn't actually need a struct for this, just makes source clearer) +// { + typedef struct + { + int offsets[1]; // variable sized (mdxaHeader_t->numBones), each offset points to an mdxaSkel_t below + } mdxaSkelOffsets_t; +// } + + + +// for each bone... (mdxaHeader_t->numBones) +// { + // mdxaSkel_t - contains hierarchical info only... + + typedef struct { + char name[MAX_QPATH]; // name of bone + unsigned int flags; + int parent; // index of bone that is parent to this one, -1 = NULL/root + mdxaBone_t BasePoseMat; // base pose + mdxaBone_t BasePoseMatInv; // inverse, to save run-time calc + int numChildren; // number of children bones + int children[1]; // [mdxaSkel_t->numChildren] (variable sized) + } mdxaSkel_t; // struct size = (int)( &((mdxaSkel_t *)0)->children[ mdxaSkel_t->numChildren ] ); +// } + + + +// for each frame... (mdxaHeader_t->numFrames) +// { + // mdxaFrame_t - which contains the header for the bones for this surface, plus the actual bone matrices themselves + + typedef struct { + // (used to contain frame bounds info etc as well, doesn't now) + // + int boneIndexes[1]; // [numBones] ... into compressed bone pool + } mdxaFrame_t; // struct size = (int)( &((mdxaFrame_t *)0)->bones[ mdxaHeader_t->numBones ] ); + +// } + + +// Compressed-bone pool that all frames use (mdxaHeader_t->ofsCompBonePool) (defined at end because size unknown until end) +// for each bone in pool (unknown number, no actual total stored at the moment)... +// { + // mdxaCompBone_t (defined at file top because of struct dependancy) +// } + +//--------------------------------------------------------------------------- + + +#endif // #ifndef MDX_FORMAT_H + +//////////////////////// eof /////////////////////// + + + diff --git a/utils/Assimilate/oddbits.cpp b/utils/Assimilate/oddbits.cpp new file mode 100644 index 0000000..9314a78 --- /dev/null +++ b/utils/Assimilate/oddbits.cpp @@ -0,0 +1,225 @@ +// Filename:- oddbits.cpp +// + +#include "stdafx.h" +#include "includes.h" + + +char *va(char *format, ...) +{ + static char strings[16][1024]; + va_list argptr; + + static int i=0; + + i = ++i&15; + + va_start (argptr, format); + vsprintf (strings[i], format,argptr); + va_end (argptr); + + return strings[i]; +} + + +// these MUST all be MB_TASKMODAL boxes now!! +// +void ErrorBox(const char *sString) +{ + MessageBox( NULL, sString, "Error", MB_OK|MB_ICONERROR|MB_TASKMODAL ); +} +void InfoBox(const char *sString) +{ + MessageBox( NULL, sString, "Info", MB_OK|MB_ICONINFORMATION|MB_TASKMODAL ); +} +void WarningBox(const char *sString) +{ + MessageBox( NULL, sString, "Warning", MB_OK|MB_ICONWARNING|MB_TASKMODAL ); +} + +bool FileExists (LPCSTR psFilename) +{ + FILE *handle = fopen(psFilename, "r"); + if (!handle) + { + return false; + } + fclose (handle); + return true; +} + + + +// returns a path to somewhere writeable, without trailing backslash... +// +// (for extra speed now, only evaluates it on the first call, change this if you like) +// +char *scGetTempPath(void) +{ + static char sBuffer[MAX_PATH]; + DWORD dwReturnedSize; + static int i=0; + + if (!i++) + { + dwReturnedSize = GetTempPath(sizeof(sBuffer),sBuffer); + + if (dwReturnedSize>sizeof(sBuffer)) + { + // temp path too long to return, so forget it... + // + strcpy(sBuffer,"c:"); // "c:\\"); // should be writeable + } + + // strip any trailing backslash... + // + if (sBuffer[strlen(sBuffer)-1]=='\\') + sBuffer[strlen(sBuffer)-1]='\0'; + }// if (!i++) + + return sBuffer; + +}// char *scGetTempPath(void) + + +// "psInitialLoadName" param can be "" if not bothered +char *InputLoadFileName(char *psInitialLoadName, char *psCaption, const char *psInitialDir, char *psFilter) +{ + static char sName[MAX_PATH]; + + CFileDialog FileDlg(TRUE, NULL, NULL, OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST, psFilter, AfxGetMainWnd()); + + FileDlg.m_ofn.lpstrInitialDir=psInitialDir; + FileDlg.m_ofn.lpstrTitle=psCaption; + strcpy(sName,psInitialLoadName); + FileDlg.m_ofn.lpstrFile=sName; + + if (FileDlg.DoModal() == IDOK) + return sName; + + return NULL; + +}// char *InputLoadFileName(char *psInitialLoadName, char *psCaption, char *psInitialDir, char *psFilter) + + +long filesize(FILE *handle) +{ + long curpos, length; + + curpos = ftell(handle); + fseek(handle, 0L, SEEK_END); + length = ftell(handle); + fseek(handle, curpos, SEEK_SET); + + return length; +} + + +// returns -1 for error +int scLoadFile (LPCSTR psPathedFilename, void **bufferptr, bool bBinaryMode /* = true */) +{ + FILE *f; + int length; + void *buffer; + + f = fopen(psPathedFilename,bBinaryMode?"rb":"rt"); + if (f) + { + length = filesize(f); + buffer = malloc (length+1); + ((char *)buffer)[length] = 0; + int lread = fread (buffer,1,length, f); + fclose (f); + + if (lread==length) + { + *bufferptr = buffer; + return length; + } + free(buffer); + } + + ErrorBox(va("Error reading file %s!",psPathedFilename)); + return -1; +} + + +// takes (eg) "q:\quake\baseq3\textures\borg\name.tga" +// +// and produces "textures/borg/name.tga" +// +void Filename_RemoveBASEQ(CString &string) +{ + string.Replace("\\","/"); + string.MakeLower(); + + int loc = string.Find("/game"); + if (loc >=0 ) + { + loc = string.Find("/",loc+1); + if (loc >=0) + { + // now pointing at "baseq3", "demoq3", whatever... + loc = string.Find("/", loc+1); + + if (loc >= 0) + { + // now pointing at local filename... + // + string = string.Mid(loc+1); + } + } + } +} + + +// takes (eg) "textures/borg/name.tga" +// +// and produces "textures/borg" +// +void Filename_RemoveFilename(CString &string) +{ + string.Replace("\\","/"); + + int loc = string.ReverseFind('/'); + if (loc >= 0) + { + string = string.Left(loc); + } +} + + +// takes (eg) "( longpath )/textures/borg/name.xxx" // N.B. I assume there's an extension on the input string +// +// and produces "name" +// +void Filename_BaseOnly(CString &string) +{ + string.Replace("\\","/"); + + int loc = string.GetLength()-4; + if (string[loc] == '.') + { + string = string.Left(loc); // "( longpath )/textures/borg/name" + loc = string.ReverseFind('/'); + if (loc >= 0) + { + string = string.Mid(loc+1); + } + } +} + +void Filename_AccountForLOD(CString &string, int iLODLevel) +{ + if (iLODLevel) + { + int loc = string.ReverseFind('.'); + if (loc>0) + { + string.Insert( loc, va("_%d",iLODLevel)); + } + } +} + +///////////////////// eof /////////////////// + diff --git a/utils/Assimilate/oddbits.h b/utils/Assimilate/oddbits.h new file mode 100644 index 0000000..c725396 --- /dev/null +++ b/utils/Assimilate/oddbits.h @@ -0,0 +1,37 @@ +// Filename:- oddbits.h +// + +#ifndef ODDBITS_H +#define ODDBITS_H + + +char *va(char *format, ...); +bool FileExists (LPCSTR psFilename); + +void ErrorBox(const char *sString); +void InfoBox(const char *sString); +void WarningBox(const char *sString); +// +// (Afx message boxes appear to be MB_TASKMODAL anyway, so no need to specify) +// +#define GetYesNo(psQuery) (!!(AfxMessageBox(psQuery,MB_YESNO|MB_ICONWARNING)==IDYES)) + + +char *scGetTempPath(void); +char *InputLoadFileName(char *psInitialLoadName, char *psCaption, const char *psInitialDir, char *psFilter); +long filesize(FILE *handle); +int scLoadFile (LPCSTR psPathedFilename, void **bufferptr, bool bBinaryMode = true ); +void Filename_RemoveBASEQ(CString &string); +void Filename_RemoveFilename(CString &string); +void Filename_BaseOnly(CString &string); +void Filename_AccountForLOD(CString &string, int iLODLevel); + +#define StartWait() HCURSOR hcurSave = SetCursor(::LoadCursor(NULL, IDC_WAIT)) +#define EndWait() SetCursor(hcurSave) + + +#endif // #ifndef ODDBITS_H + +/////////////////// eof //////////////////// + + diff --git a/utils/Assimilate/res/Assimilate.ico b/utils/Assimilate/res/Assimilate.ico new file mode 100644 index 0000000..ecd4216 Binary files /dev/null and b/utils/Assimilate/res/Assimilate.ico differ diff --git a/utils/Assimilate/res/AssimilateDoc.ico b/utils/Assimilate/res/AssimilateDoc.ico new file mode 100644 index 0000000..2a1f1ae Binary files /dev/null and b/utils/Assimilate/res/AssimilateDoc.ico differ diff --git a/utils/Assimilate/sourcesafe.cpp b/utils/Assimilate/sourcesafe.cpp new file mode 100644 index 0000000..16e82b4 --- /dev/null +++ b/utils/Assimilate/sourcesafe.cpp @@ -0,0 +1,1151 @@ +// Filename:- sourcesafe.cpp +// +// (read the sourcesafe.h notes for extra info) +// +#include "stdafx.h" +#include "Includes.h" + +// Microsoft's GUID stuff never seems to work properly, so... +// +#define DEFINE_GUID_THAT_WORKS(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } + +#include +//#include "oddbits.h" +#include "ssauto.h" +#include "ssauterr.h" + +#include "sourcesafe.h" + +// SourceSafe can sometimes take a LONG time to return from calls to it, so in order to let users know that your app +// hasn't crashed elsewhere it's a good idea to fill in these macros with something appropriate to your own code... +// +int iSourceSafeReferenceCount = 0; +void SourceSafe_Enter(LPCSTR psString) +{ +// if (!iSourceSafeReferenceCount++) + { + CString str = psString; + str.Insert(0,"SourceSafe: "); + StatusText(va("%s\n",str)); + } +} + +void SourceSafe_Leave(void) +{ +// if (!--iSourceSafeReferenceCount) + { + //((CMainFrame*)AfxGetMainWnd())->StatusMessage + StatusText("Ready\n"); + } +} + +#define _SS_ENTER(string) SourceSafe_Enter(string) //((CMainFrame*)AfxGetMainWnd())->StatusMessage("(Waiting for SourceSafe...)"); +#define _SS_LEAVE() SourceSafe_Leave() //((CMainFrame*)AfxGetMainWnd())->StatusMessage("Ready"); + +// Coding note, generally the functions beginning "SS_" are called externally, and the ones beginning "_" are internal. +// +// Incidentally, the docs say: +// +// ----- +// Be sure to link with the following libraries: +// user32.lib uuid.lib oleaut32.lib ole32.lib +// ----- +// +//... but this appears not to be needed. I suspect stdafx.h may have something to do with that, so I'll leave +// that comment there in case this is ever used with standard windows code only +// + + + +// replace these 3 with hacks to fit this module to BehavEd... +// +CString g_cstrSourceSafeINI;// = sEF1_SS_INI; +CString g_cstrSourceSafeProject;// = sEF1_SS_PROJECT; +bool g_bUseSourceSafe = false; //true; + + +void SS_SetString_Ini(LPCSTR psArg) +{ + if (g_bUseSourceSafe && g_cstrSourceSafeINI.CompareNoCase(psArg)) + { + SS_Shutdown_OnceOnly(); + } + + g_cstrSourceSafeINI = psArg; + g_bUseSourceSafe = (!g_cstrSourceSafeINI.IsEmpty() && !g_cstrSourceSafeProject.IsEmpty()); +} + +void SS_SetString_Project(LPCSTR psArg) +{ + if (g_bUseSourceSafe && g_cstrSourceSafeProject.CompareNoCase(psArg)) + { + SS_Shutdown_OnceOnly(); + } + + g_cstrSourceSafeProject = psArg; + g_bUseSourceSafe = (!g_cstrSourceSafeINI.IsEmpty() && !g_cstrSourceSafeProject.IsEmpty()); +} + +// remember to do a SysFreeString() on the returned value when you've finished with it! +// +BSTR StringToBSTR(LPCSTR string) +{ + OLECHAR* svalue = NULL; + BSTR bstrValue = NULL; + int iLen; + bool bFree_svalue = false; + + if( (iLen = MultiByteToWideChar(CP_ACP, 0, string, -1, svalue, 0 )) != 1) + { + svalue = new OLECHAR[iLen]; + bFree_svalue = true; + if( MultiByteToWideChar(CP_ACP, 0, string, -1, svalue, iLen ) == 0 ) + { + ErrorBox( va("StringToBSTR: Error converting \"%s\" to BSTR", string)); + } + } + else + { + svalue = L""; + } + + bstrValue = SysAllocString(svalue); + + if (svalue && bFree_svalue) + delete svalue; + + return bstrValue; +} + + +// thanks for making us jump through yet more hoops, Microsoft... +// +// (actually there is a "legal" way to do this, in a similar way to above, but I can be arsed looking it up) +// +LPCSTR BSTRToString(BSTR pStrOLE_MyName) +{ + static CString str; + str.Empty(); + + WORD *pw = (WORD *) pStrOLE_MyName; + + char c; + do + { + c = LOBYTE(*pw++); + str+=va("%c",c); // hahahaha! + } + while (c); + + return (LPCSTR) str; +} + + +IClassFactory *g_pClf = NULL; +IVSSDatabase *g_pVdb = NULL; +BSTR bstrPath = NULL; +BSTR bstrUName = NULL; +BSTR bstrUPass = NULL; +BSTR bstrProject= NULL; +// +bool gbDatabaseHeldOpen = false; + +bool _OpenDatabase(bool bQuietErrors = false); // spot the retrofit +bool _OpenDatabase(bool bQuietErrors) +{ + if (gbDatabaseHeldOpen) + return true; + + static bool bFirstTime = false; + if (!bFirstTime) + { + bFirstTime = true; + SS_Startup_OnceOnly(); + } + + _SS_ENTER("Opening..."); + + bool bReturn = false; + CLSID clsid; + + bstrPath = StringToBSTR ( (LPCSTR) g_cstrSourceSafeINI ); // eg "\\\\RAVEND\\VSS\\SRCSAFE.INI" + bstrUName = SysAllocString(L""); // user name (eg) "scork" or "guest", but best left blank for auto. + bstrUPass = SysAllocString(L""); // user password, again leave blank + bstrProject = StringToBSTR ( (LPCSTR) g_cstrSourceSafeProject ); // eg. "$/StarTrek/BaseQ3/Maps" + + CoInitialize(0); + if(S_OK == CLSIDFromProgID(L"SourceSafe", &clsid )) + { + if(S_OK == CoGetClassObject( clsid, CLSCTX_ALL, NULL, IID_IClassFactory, (void**)&g_pClf )) + { + if(S_OK == g_pClf->CreateInstance( NULL, IID_IVSSDatabase, (void **) &g_pVdb )) + { + if(S_OK == g_pVdb->Open(bstrPath, bstrUName, bstrUPass)) + { + if (S_OK == g_pVdb->put_CurrentProject(bstrProject)) + { + gbDatabaseHeldOpen = true; + bReturn = true; + } + else + { + if (!bQuietErrors) + ErrorBox( va("SourceSafe: Failed to set current project to \"%s\"",g_cstrSourceSafeProject)); + } + } + else + { + if (!bQuietErrors) + ErrorBox( va("SourceSafe: Failed during open of INI file \"%s\"",g_cstrSourceSafeINI)); + } + } + else + { + if (!bQuietErrors) + ErrorBox( "SourceSafe: Failed to create a database instance"); + } + } + else + { + if (!bQuietErrors) + ErrorBox( "SourceSafe: Failed to get ClassFactory interface"); + } + } + else + { + if (!bQuietErrors) + ErrorBox( "SourceSafe: Failed to get CLSID (GUID)"); + } + + _SS_LEAVE(); + + return bReturn; +} + +// this should be called regardless of whether or not the _OpenDatabase function succeeded because it could +// have got part way through and only error'd later, so this frees up what it needs to +// +void _CloseDatabase() +{ + if (gbDatabaseHeldOpen) + return; + + _SS_ENTER("Closeing..."); + + if (g_pVdb) + { + g_pVdb->Release(); + g_pVdb = NULL; + } + + if (g_pClf) + { + g_pClf->Release(); + g_pClf = NULL; + } + + CoUninitialize(); + + // no need to check for NULL ptr for this function... + // + SysFreeString(bstrPath); bstrPath = NULL; + SysFreeString(bstrUName); bstrUName = NULL; + SysFreeString(bstrUPass); bstrUPass = NULL; + SysFreeString(bstrProject); bstrProject = NULL; + + _SS_LEAVE(); +} + + + +// there isn't really any totally legal way to do this because Radiant doesn't have projects with files added to +// sourcesafe through menus etc in the same way as (eg) VC, so I'll just do a bit of a kludge for now. This should +// be updated for any other projects/files you want to handle yourself... +// +// currently I'm expecting files like: +// +// "Q:\\quake\\baseq3\\scripts\\borg.shader" +// +// .. which I convert by matching the "baseq3" part to produce: +// +// "$/StarTrek/BaseQ3/Shaders/borg3.shader" +// +// returns NULL on fail. +// + +// BEHAVED NOTE: +// +// scripts pathnames are in usually in the form : "Q:\\quake\\baseq3\\real_scripts\\oz\\myborg.txt" +// sourcesafe pathnames are in the form : "$/StarTrek/real_scripts" downwards +// +// ... so I just need to search for the common part of the path ("real_scripts")... +// +LPCSTR FilenameToProjectName(LPCSTR path, bool bQuiet = false); +LPCSTR FilenameToProjectName(LPCSTR path, bool bQuiet) +{ + static CString strPath; + strPath = path; // do NOT join to above line (or it only happens first time) + + strPath.Replace("\\","/"); + + int loc = strPath.Find("/models/"); + if (loc>=0) + { + strPath = strPath.Mid(loc+1); // reduce to "models/players/blah/blah.car" + } + else + { + if (!bQuiet) + { + ErrorBox( va("SourceSafe: FilenameToProjectName(): Failed to convert \"%s\" to SS path",path)); + } + return NULL; + } + + strPath.Insert(0,va("%s",(LPCSTR) g_cstrSourceSafeProject)); + + return (LPCSTR) strPath; +} + +// return is how many versions were listed, AFAIK it should always be at least 1 (ie "created") for any valid SS item +// +int _ListVersions( IVSSDatabase* db, LPCSTR psPathSS, CString &strOutput ) +{ + _SS_ENTER("ListVersions..."); + +#define MAX_VERSIONS_TO_LIST 40 // otherwise these things get fucking *huge*! + int iVersions = 0; + BSTR bstrval; + char lpbuf[200]; + char lpbuf2[200]; + + IVSSItem *vssi; + IVSSVersion *vers; + IVSSVersions *vx; + LPUNKNOWN lpunk; + IEnumVARIANT *ppvobj; + VARIANT st; + BSTR bstrValue; + int x; + ULONG fetched; + long lvnum; + + HRESULT hr; + + strOutput = ""; + + bstrValue = StringToBSTR(psPathSS); + + if( S_OK == db->get_VSSItem(bstrValue, FALSE, &vssi) ) + { + if( S_OK == vssi->get_Versions( 0l, &vx ) ) + { + if( S_OK == vx->_NewEnum(&lpunk) ) + { + if(!FAILED(lpunk->QueryInterface(IID_IEnumVARIANT, (void**)&ppvobj))) + { + vssi->get_Spec( &bstrval ); + x = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)bstrval, -1, + lpbuf, sizeof(lpbuf), NULL, NULL ); + +// strOutput = va("History of: %s\n", lpbuf ); +// OutputDebugString(va("History of: %s\n", lpbuf )); +// strOutput+= "ACTION USER NAME VERSION NUMBER\n"; +// OutputDebugString("ACTION USER NAME VERSION NUMBER\n"); + + do + { + ppvobj->Next( 1UL, &st, &fetched ); + if( fetched != 0 ) + { + if(!FAILED(hr = st.punkVal->QueryInterface(IID_IVSSVersion,(void**)&vers))) + { + vers->get_Action( &bstrval ); + WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)bstrval, -1, + lpbuf, sizeof(lpbuf), NULL, NULL ); + vers->get_Username( &bstrval ); + WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)bstrval, -1, + lpbuf2, sizeof( lpbuf2 ), NULL, NULL ); + vers->get_VersionNumber( &lvnum ); +// OutputDebugString(va("%s %s %ld\n", lpbuf, lpbuf2, lvnum )); + + // version numbers can go a bit weird, in that global labels have different version + // numbers, but since it's all presented in a backwards order I'd have to do a whole + // bunch of messy stuff putting them into arrays then scanning them from last to first + // to check version numbers were sequential, then zap any that weren't, so in the end + // I'm just going to to ignore version numbers for labels... + // + if (!strnicmp(lpbuf,"labeled",7)) + { + strOutput += va("%s by user %s\n", lpbuf, lpbuf2, lvnum ); + } + else + { + strOutput += va("%s by user %s (Ver. %ld)\n", lpbuf, lpbuf2, lvnum ); + } + + iVersions++; + + vers->Release(); + } + else + { + ErrorBox( va("SourceSafe: _ListVersions(): Failed in QueryInterface(IID_IVSSVersion) for file \"$s\"",psPathSS )); + } + st.punkVal->Release(); + } + } while( fetched != 0 + #ifdef MAX_VERSIONS_TO_LIST + && iVersions < MAX_VERSIONS_TO_LIST + #endif + ); + ppvobj->Release(); + } + else + { + ErrorBox( va("SourceSafe: _ListVersions(): Failed in QueryInterface(IID_IEnumVARIANT) for file \"$s\"",psPathSS )); + } + lpunk->Release(); + } + else + { + ErrorBox( va("SourceSafe: _ListVersions(): Failed in _NewEnum() for file \"$s\"",psPathSS )); + } + vx->Release(); + } + else + { + ErrorBox( va("SourceSafe: _ListVersions(): Failed in get_Versions() for file \"$s\"",psPathSS )); + } + vssi->Release(); + } + else + { + ErrorBox( va("SourceSafe: _ListVersions(): Failed in get_VSSItem() for file \"$s\"",psPathSS )); + } + SysFreeString(bstrValue); + + #ifdef MAX_VERSIONS_TO_LIST + if (iVersions >= MAX_VERSIONS_TO_LIST) + { + strOutput += va("\n\n(History list capped to %d entries)",MAX_VERSIONS_TO_LIST); + } + #endif + + _SS_LEAVE(); + + return iVersions; +} + + +// int return is number of users who have this file checked out, unless param 'psNameToCheckForMatch' is NZ, +// in which case return is a bool as to whether or not the name in question (usually our name) has the file checked out... +// +// ( output list is CR-delineated ) +// +// return is -1 for error, else checkout count, else 1 for true if doing a me-match via last param +// +int _ListCheckOuts( IVSSDatabase* db, LPCSTR psPathSS, CString &strOutput, LPCSTR psNameToCheckForMatch ) +{ + _SS_ENTER("ListCheckOuts..."); + + int iCheckOuts = -1; + BSTR bstrval; + char lpbuf[200]; + char lpbuf2[200]; + + IVSSItem *vssi; + IVSSVersion *vers; + IVSSCheckouts *vx; + LPUNKNOWN lpunk; + IEnumVARIANT *ppvobj; + VARIANT st; + BSTR bstrValue; + int x; + ULONG fetched; + + HRESULT hr; + + strOutput = ""; + + bstrValue = StringToBSTR( psPathSS ); + + if( S_OK == db->get_VSSItem( bstrValue, FALSE, &vssi )) + { + if( S_OK == vssi->get_Checkouts( &vx ) ) + { + if( S_OK == vx->_NewEnum(&lpunk) ) + { + if(!FAILED(lpunk->QueryInterface(IID_IEnumVARIANT, (void**)&ppvobj))) + { + vssi->get_Spec( &bstrval ); + x = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)bstrval, -1, lpbuf, sizeof(lpbuf), NULL, NULL ); + +// OutputDebugString(va("Checkout status of: %s\n", lpbuf )); +// strOutput = va("Checkout status of: %s\n", lpbuf )); + + do + { + ppvobj->Next( 1UL, &st, &fetched ); + if( fetched != 0 ) + { + if(!FAILED(hr = st.punkVal->QueryInterface(IID_IVSSCheckout,(void**)&vers))) + { +// vers->get_Action( &bstrval ); +// WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)bstrval, -1, +// lpbuf, sizeof(lpbuf), NULL, NULL ); + vers->get_Username( &bstrval ); + WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)bstrval, -1, + lpbuf2, sizeof( lpbuf2 ), NULL, NULL ); +// vers->get_VersionNumber( &lvnum ); + +// OutputDebugString(va("%s %s %ld\n", lpbuf, lpbuf2, lvnum )); + //OutputDebugString(va("%s\n", lpbuf2)); + strOutput += va("%s\n", lpbuf2); + + if ( psNameToCheckForMatch ) + { + if (!stricmp(lpbuf2,psNameToCheckForMatch) ) // is stricmp too paranoid? + { + iCheckOuts = 1; // hardwire: checked out by me = true + } + } + else + { + if (iCheckOuts == -1) // first time - change error-flag to valid count? + { + iCheckOuts = 1; + } + else + { + iCheckOuts++; + } + } + + vers->Release(); + } + else + { + ErrorBox( va("SourceSafe: _ListCheckOuts(): Failed in QueryInterface(IID_IVSSCheckout) for file \"$s\"",psPathSS )); + } + st.punkVal->Release(); + } + } while( fetched != 0 ); + ppvobj->Release(); + } + else + { + ErrorBox( va("SourceSafe: _ListCheckOuts(): Failed in QueryInterface(IID_IEnumVARIANT) for file \"$s\"",psPathSS )); + } + lpunk->Release(); + } + else + { + ErrorBox( va("SourceSafe: _ListCheckOuts(): Failed in _NewEnum() for file \"$s\"",psPathSS )); + } + vx->Release(); + } + else + { + ErrorBox( va("SourceSafe: _ListCheckOuts(): Failed in get_Checkouts() for file \"$s\"",psPathSS )); + } + vssi->Release(); + } + else + { + ErrorBox( va("SourceSafe: _ListCheckOuts(): Failed in get_VSSItem() for file \"$s\"",psPathSS )); + } + SysFreeString(bstrValue); + + _SS_LEAVE(); + + return iCheckOuts; +} + + +bool _IsCheckedOut( IVSSDatabase* db, LPCSTR psPathSS ) +{ + _SS_ENTER("Is checked out?..."); + + IVSSItem* vssi; + bool bReturn = false; + BSTR bstrValue = StringToBSTR( psPathSS ); + + if( S_OK == db->get_VSSItem( bstrValue, FALSE, &vssi ) ) + { + long lStatus; + if( S_OK == vssi->get_IsCheckedOut( &lStatus ) ) + { + bReturn = !!lStatus; + } + else + { + ErrorBox( va("SourceSafe: Error during _IsCheckedOut() for file \"%s\"",psPathSS )); + } + vssi->Release(); + } + else + { + ErrorBox( va( "SourceSafe: _IsCheckedOut(): Error during get_VSSItem() for file \"%s\"",psPathSS )); + } + + SysFreeString( bstrValue ); + + _SS_LEAVE(); + + return bReturn; +} + +bool _CheckOut( IVSSDatabase* db, LPCSTR psPathSS, LPCSTR psPathDisk ) +{ + _SS_ENTER("Check Out..."); + + IVSSItem* vssi; + bool bReturn = false; + BSTR bstrValue = StringToBSTR ( psPathSS ); + BSTR bsComment = SysAllocString( L"" ); + + if( S_OK == db->get_VSSItem( bstrValue, FALSE, &vssi ) ) + { + BSTR bsLocal = StringToBSTR(psPathDisk); + + if( S_OK == vssi->Checkout( bsComment, bsLocal, 0 )) + { + bReturn = true; + } + else + { + ErrorBox( va( "SourceSafe: Error during _CheckOut() for file \"%s\"",psPathSS )); + } + + SysFreeString( bsLocal ); + + vssi->Release(); + } + else + { + ErrorBox( va( "SourceSafe: _CheckOut(): Error during get_VSSItem() for file \"%s\"",psPathSS )); + } + + SysFreeString( bsComment ); + SysFreeString( bstrValue ); + + _SS_LEAVE(); + + return bReturn; +} + +bool _Add( IVSSDatabase* db, LPCSTR psPathSS, LPCSTR psPathDisk ) +{ + _SS_ENTER("Add..."); + + IVSSItem *vssi,*vssi2; + bool bReturn = false; + BSTR bstrValue = StringToBSTR ( psPathSS ); + BSTR bsComment = SysAllocString( L"" ); + + CString strPathRelativeToProject = psPathSS; + strPathRelativeToProject.Replace("/","\\"); + Filename_RemoveFilename(strPathRelativeToProject); + strPathRelativeToProject.Replace("\\","/"); + + // ( eg: "$/StarTrek/BaseQ3/real_scripts/ste" ) + + BSTR bstrProject = StringToBSTR(strPathRelativeToProject); + + // ensure SS path exists... + // + CString strBuiltUpProject; + CString strSourceProject(strPathRelativeToProject); + while (!strSourceProject.IsEmpty()) + { + CString strProjectPathSoFar(strBuiltUpProject); + CString strNewSubProj; + int iLoc = strSourceProject.Find("/"); + if (iLoc >=0) + { + strNewSubProj = strSourceProject.Left(iLoc); + } + else + { + strNewSubProj = strSourceProject; + strSourceProject = ""; + } + strBuiltUpProject += strBuiltUpProject.IsEmpty() ? "" : "/"; + strBuiltUpProject += strNewSubProj; + strSourceProject = strSourceProject.Mid(iLoc+1); + + if (strBuiltUpProject != "$") // get_VSSItem() doesn't work on root, and it'll always be there anyway + { + BSTR bstrTempProjectPath = StringToBSTR(strBuiltUpProject); + { + if( S_OK != db->get_VSSItem( bstrTempProjectPath, FALSE, &vssi ) ) + { + // need to build this new part of the path... + // + BSTR bstrProjectPathSoFar = StringToBSTR(strProjectPathSoFar); + { + if( S_OK == db->get_VSSItem( bstrProjectPathSoFar, FALSE, &vssi ) ) + { + BSTR bstrNewSubProj = StringToBSTR(strNewSubProj); + { + if (S_OK == vssi->NewSubproject(bstrNewSubProj, bsComment, &vssi2)) + { + // yay! + } + else + { + ErrorBox(va("SS_Add(): Error creating subproject \"%s\" in \"%s\"!",(LPCSTR) strNewSubProj, (LPCSTR) strProjectPathSoFar)); + break; + } + } + SysFreeString( bstrNewSubProj ); + } + } + SysFreeString( bstrProjectPathSoFar ); + } + } + SysFreeString( bstrTempProjectPath ); + } + } + + if( S_OK == db->get_VSSItem( bstrProject, FALSE, &vssi ) ) + { + BSTR bsLocal = StringToBSTR( psPathDisk ); + + // if( S_OK == vssi->Checkin ( bsComment, bsLocal, 0 )) + if( S_OK == vssi->Add ( bsLocal, bsComment, 0, &vssi2)) + { + vssi2->Release(); + bReturn = true; + } + else + { + ErrorBox( va( "SourceSafe: Error during _Add() for file \"%s\"",psPathSS )); + } + + SysFreeString( bsLocal ); + + vssi->Release(); + } + else + { + ErrorBox( va( "SourceSafe: _CheckIn(): Error during get_VSSItem() for file \"%s\"",psPathSS )); + } + + SysFreeString( bsComment ); + SysFreeString( bstrValue ); + + _SS_LEAVE(); + + return bReturn; +} + + +bool _CheckIn( IVSSDatabase* db, LPCSTR psPathSS, LPCSTR psPathDisk ) +{ + _SS_ENTER("Check In..."); + + IVSSItem* vssi; + bool bReturn = false; + BSTR bstrValue = StringToBSTR ( psPathSS ); + BSTR bsComment = SysAllocString( L"" ); + + if( S_OK == db->get_VSSItem( bstrValue, FALSE, &vssi ) ) + { + BSTR bsLocal = StringToBSTR( psPathDisk ); + + if( S_OK == vssi->Checkin ( bsComment, bsLocal, 0 )) + { + bReturn = true; + } + else + { + ErrorBox( va( "SourceSafe: Error during _CheckIn() for file \"%s\"",psPathSS )); + } + + SysFreeString( bsLocal ); + + vssi->Release(); + } + else + { + ErrorBox( va( "SourceSafe: _CheckIn(): Error during get_VSSItem() for file \"%s\"",psPathSS )); + } + + SysFreeString( bsComment ); + SysFreeString( bstrValue ); + + _SS_LEAVE(); + + return bReturn; +} + + + +bool _UndoCheckOut( IVSSDatabase* db, LPCSTR psPathSS ) +{ + _SS_ENTER("Undo Check out..."); + + IVSSItem* vssi; + bool bReturn = false; + BSTR bstrValue = StringToBSTR ( psPathSS ); + BSTR bsComment = SysAllocString( L"" ); + + if( S_OK == db->get_VSSItem(bstrValue, FALSE, &vssi ) ) + { + BSTR bsLocal = SysAllocString( L"" ); // this doesn't seem to need a path to the disk file, presumably it works it out + + if( S_OK == vssi->UndoCheckout( bsLocal, 0 )) + { + bReturn = true; + } + else + { + ErrorBox( va("SourceSafe: Error during _UndoCheckOut() for file \"%s\"",psPathSS)); + } + + SysFreeString(bsLocal); + + vssi->Release(); + } + else + { + ErrorBox( va( "SourceSafe: _UndoCheckOut(): Error during get_VSSItem() for file \"%s\"",psPathSS )); + } + + SysFreeString( bsComment ); + SysFreeString( bstrValue ); + + _SS_LEAVE(); + + return bReturn; +} + + + +// these routines called externally... +// + + +bool SS_IsUnderSourceControl( LPCSTR psPathedFilename ) +{ + CWaitCursor waitcursor; + bool bReturn = false; + + if ( SS_FunctionsAvailable() ) + { + LPCSTR psSSName = FilenameToProjectName(psPathedFilename, true); // bQuiet + + if (psSSName ) + { + if (_OpenDatabase() ) + { + _SS_ENTER("Checking if item is under control"); + + IVSSItem* vssi; + BSTR bstrValue = StringToBSTR ( psSSName ); + + if( S_OK == g_pVdb->get_VSSItem( bstrValue, FALSE, &vssi ) ) + { + vssi->Release(); + bReturn = true; + } + else + { + // item doesn't appear to be under SS control + } + + SysFreeString( bstrValue ); + + _SS_LEAVE(); + } + _CloseDatabase(); + } + } + + return bReturn; +} + +bool SS_Add(LPCSTR psPathedFilename) +{ + CWaitCursor waitcursor; + + bool bReturn = false; + + if ( SS_FunctionsAvailable() ) + { + LPCSTR psSSName = FilenameToProjectName(psPathedFilename); + + if ( psSSName ) + { + if ( _OpenDatabase() ) + { + if (_Add( g_pVdb, psSSName, psPathedFilename ) ) + { + bReturn = true; + } + } + _CloseDatabase(); + } + } + + return bReturn; +} + +bool SS_CheckIn(LPCSTR psPathedFilename) +{ + CWaitCursor waitcursor; + + bool bReturn = false; + + if ( SS_FunctionsAvailable() ) + { + LPCSTR psSSName = FilenameToProjectName(psPathedFilename); + + if ( psSSName ) + { + if ( _OpenDatabase() ) + { + if ( _CheckIn( g_pVdb, psSSName, psPathedFilename ) ) + { + bReturn = true; + } + } + _CloseDatabase(); + } + } + + return bReturn; +} + +bool SS_CheckOut( LPCSTR psPathedFilename ) +{ + CWaitCursor waitcursor; + bool bReturn = false; + + if ( SS_FunctionsAvailable() ) + { + LPCSTR psSSName = FilenameToProjectName(psPathedFilename); + + if ( psSSName ) + { + if ( _OpenDatabase() ) + { + if ( _CheckOut( g_pVdb, psSSName, psPathedFilename ) ) + { + bReturn = true; + } + } + _CloseDatabase(); + } + } + + return bReturn; +} + +bool SS_UndoCheckOut( LPCSTR psPathedFilename ) +{ + CWaitCursor waitcursor; + bool bReturn = false; + + if ( SS_FunctionsAvailable() ) + { + LPCSTR psSSName = FilenameToProjectName(psPathedFilename); + + if ( psSSName ) + { + if ( _OpenDatabase() ) + { + if ( _UndoCheckOut( g_pVdb, psSSName ) ) + { + bReturn = true; + } + } + _CloseDatabase(); + } + } + + return bReturn; +} + +bool SS_IsCheckedOut( LPCSTR psPathedFilename ) +{ + CWaitCursor waitcursor; + bool bReturn = false; + + ASSERT( SS_FunctionsAvailable() ); // you shouldn't really be calling this otherwise + + if ( SS_FunctionsAvailable() ) + { + LPCSTR psSSName = FilenameToProjectName(psPathedFilename); + + if ( psSSName ) + { + if ( _OpenDatabase() ) + { + if ( _IsCheckedOut( g_pVdb, psSSName ) ) + { + bReturn = true; + } + } + _CloseDatabase(); + } + } + + return bReturn; +} + +// similar to above, but we're only interested if it's checked out by us... +// +// (actually after I wrote this I found a more legal way to do it, but this works...) +// +bool SS_IsCheckedOutByMe( LPCSTR psPathedFilename ) +{ + CWaitCursor waitcursor; + bool bReturn = false; + + ASSERT( SS_FunctionsAvailable() ); // you shouldn't really be calling this otherwise + + if ( SS_FunctionsAvailable() ) + { + LPCSTR psSSName = FilenameToProjectName( psPathedFilename ); + + if ( psSSName ) + { + if ( _OpenDatabase() ) + { + _SS_ENTER("Is checked out by me?"); + + CString junk; + + BSTR pStrOLE_MyName; + if (S_OK == g_pVdb->get_UserName(&pStrOLE_MyName)) + { + int iCount = _ListCheckOuts( g_pVdb, psSSName, junk, BSTRToString( pStrOLE_MyName) ); + + if ( iCount == 1) + { + bReturn = true; + } + } + else + { + ErrorBox( "SourceSafe: SS_IsCheckedOutByMe(): Failed in get_UserName()" ); + } + + _SS_LEAVE(); + } + _CloseDatabase(); + } + } + + return bReturn; +} + +bool SS_ListVersions( LPCSTR psPathedFilename, CString &strOutput ) +{ + CWaitCursor waitcursor; + bool bReturn = false; + + if ( SS_FunctionsAvailable() ) + { + LPCSTR psSSName = FilenameToProjectName( psPathedFilename ); + + if ( psSSName ) + { + if ( _OpenDatabase() ) + { + if ( _ListVersions( g_pVdb, psSSName, strOutput )) + { + bReturn = true; + } + } + _CloseDatabase(); + } + } + + return bReturn; +} + +// if false (error) return, don't rely on a valid iCount! +// +bool SS_ListCheckOuts( LPCSTR psPathedFilename, CString &strOutput, int &iCount ) +{ + CWaitCursor waitcursor; + bool bReturn = false; + + ASSERT( SS_FunctionsAvailable() ); // you shouldn't really be calling this otherwise + + if ( SS_FunctionsAvailable() ) + { + LPCSTR psSSName = FilenameToProjectName( psPathedFilename ); + + if ( psSSName ) + { + if ( _OpenDatabase() ) + { + iCount = _ListCheckOuts( g_pVdb, psSSName, strOutput, NULL ); + + if (iCount != -1) + { + bReturn = true; // strictly speaking this may not be true, but if (later: hmmmm, what was i going to write here? ) + } + } + _CloseDatabase(); + } + } + + return bReturn; +} + + +bool SS_FunctionsAvailable( void ) +{ + bool bReturn = false; + + if ( g_bUseSourceSafe ) + { + if (_OpenDatabase(true)) // bool bQuietErrors + { + bReturn = true; + } + _CloseDatabase(); + } + + return bReturn; +} + +// this is a pretty tacky, but for now... :-) +// +bool SS_SetupOk( void ) +{ + CWaitCursor waitcursor; + bool g_bUseSourceSafe_PREV = g_bUseSourceSafe; + g_bUseSourceSafe = true; + + bool bReturn = SS_FunctionsAvailable(); + + g_bUseSourceSafe = g_bUseSourceSafe_PREV; + + return bReturn; +} + + + + +void SS_Startup_OnceOnly(void) +{ + // nothing at the moment... +} + +// this is now also called when switching databases +void SS_Shutdown_OnceOnly(void) +{ + gbDatabaseHeldOpen = false; + _CloseDatabase(); +} + + +/////////////////// eof ///////////////// + diff --git a/utils/Assimilate/sourcesafe.h b/utils/Assimilate/sourcesafe.h new file mode 100644 index 0000000..0fbe94a --- /dev/null +++ b/utils/Assimilate/sourcesafe.h @@ -0,0 +1,63 @@ +// Filename:- sourcesafe.h +// + +// any questions: bug me -Ste. + +#ifndef SOURCSAFE_H +#define SOURCSAFE_H + + +//#define sEF1_SS_INI "\\\\RAVEND\\VSS\\SRCSAFE.INI" +//#define sEF1_SS_PROJECT "$/StarTrek/BaseQ3/Maps" // note no slash on the end! + +//#define sCHC_SS_INI "\\\\Ravend\\vss_projects\\StarWars\\SRCSAFE.INI" +//#define sCHC_SS_PROJECT "$/base/maps/work" // note no slash on the end! + +/* +extern CString g_cstrSourceSafeINI; // these default to sensible values for Trek, but can be changed +extern CString g_cstrSourceSafeProject; // +extern bool g_bUseSourceSafe; // only external for prefs setting, use "SS_FunctionsAvailable()" for query +*/ +void SS_SetString_Ini(LPCSTR psArg); +void SS_SetString_Project(LPCSTR psArg); + +// Usage notes: +// +// By choice, you should check "SS_FunctionsAvailable()" yourself before calling any of the functions below. +// They do check them internally (so no SS code is called if you've turned it off because of a bad connection etc) +// but since these all return bools it can be misleading as to whether (eg) SS code is disabled/missing, or the answer +// from the call was false. Likewise you should check if the item is under sourcesafe control before calling the other +// operations, else you'll get an error box if you try and check out an item not in the SS database, rather than it +// quietly ignoring the request if you make it part of you fopen() handler. Hokay? +// +// Note that because I don't have any flag documentation I don't allow multiple checkouts (because I don't know how to...) +// but you should do one of the function calls below to check that no-one else has the item checked out before attempting +// to check it out yourself, otherwise you'll just get an error box saying that he checkout failed, which is far less +// helpful. +// +// Likewise, if you do a check-in where nothing's changed, it won't show up in the history list because I don't know how +// to set the flag to say always-show-new-checkin-even-no-differences. +// +bool SS_FunctionsAvailable ( void ); +bool SS_SetupOk ( void ); // similar to above, but doesn't care if functions are user-disabled (note that all other functions DO care, so this is only useful in rare places) +bool SS_IsUnderSourceControl( LPCSTR psPathedFilename ); // call this before deciding whether or not to call any others +bool SS_Add ( LPCSTR psPathedFilename ); +bool SS_CheckIn ( LPCSTR psPathedFilename ); +bool SS_CheckOut ( LPCSTR psPathedFilename ); +bool SS_UndoCheckOut ( LPCSTR psPathedFilename ); +bool SS_IsCheckedOut ( LPCSTR psPathedFilename ); +bool SS_IsCheckedOutByMe ( LPCSTR psPathedFilename ); +bool SS_ListVersions ( LPCSTR psPathedFilename, CString &strOutput ); // do whatever you want with the string +bool SS_ListCheckOuts ( LPCSTR psPathedFilename, CString &strOutput, int &iCount ); +// +void SS_Startup_OnceOnly (void); +void SS_Shutdown_OnceOnly (void); +// +void SS_LoadFromRegistry(void); +void SS_SaveToRegistry(void); + +#endif // #ifndef SOURCSAFE_H + + +//////////////// eof //////////////// + diff --git a/utils/Assimilate/ssauterr.h b/utils/Assimilate/ssauterr.h new file mode 100644 index 0000000..7324500 --- /dev/null +++ b/utils/Assimilate/ssauterr.h @@ -0,0 +1,145 @@ +// Filename:- ssauterr.h + +#ifndef SSAUTERR_H +#define SSAUTERR_H + +//***************************************************************************** +// ssauterr.h +// +// +// Copyright (c) 1995 by Microsoft Corporation, All Rights Reserved +//***************************************************************************** + +#define MAKEHR(iStat) MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, (USHORT) (iStat)) + +#define ESS_CORRUPT MAKEHR(-10600) // File %s may be corrupt +#define ESS_DT_BADDATESTR MAKEHR(-10159) // Invalid date string: "%s" +#define ESS_DT_INVALID MAKEHR(-10161) // Invalid time or date string +#define ESS_NOMORE_HANDLES MAKEHR(-10164) // Too many file handles open. +#define ESS_FILE_ACCESSDENIED MAKEHR(-10165) // Access to file "%s" denied +#define ESS_FILE_BADDRIVE MAKEHR(-10166) // Invalid drive: %s +#define ESS_FILE_BADHANDLE MAKEHR(-10167) // Invalid handle. +#define ESS_FILE_BADNAME MAKEHR(-10168) // Invalid filename: "%s" +#define ESS_FILE_BADPARAM MAKEHR(-10170) // Invalid access code (bad parameter) +#define ESS_FILE_BADPATH MAKEHR(-10171) // Invalid DOS path: %s +#define ESS_FILE_CURRENTDIR MAKEHR(-10172) // Folder %s is in use +#define ESS_FILE_DISKFULL MAKEHR(-10173) // Disk full +#define ESS_FILE_EXISTS MAKEHR(-10175) // File "%s" already exists +#define ESS_FILE_LOCKED MAKEHR(-10176) // File "%s" is locked +#define ESS_FILE_NOTFOUND MAKEHR(-10178) // File "%s" not found +#define ESS_FILE_READ MAKEHR(-10180) // Error reading from file +#define ESS_FILE_SHARE MAKEHR(-10181) // File %s is already open +#define ESS_FILE_TOOMANY MAKEHR(-10182) // Too many file handles open +#define ESS_FILE_VOLNOTSAME MAKEHR(-10183) // Cannot rename to another volume +#define ESS_FILE_WRITE MAKEHR(-10184) // Error writing to file +#define ESS_INI_BADBOOL MAKEHR(-10200) // Initialization variable "%s" must be set to "Yes" or "No" +#define ESS_INI_BADLINE MAKEHR(-10201) // Invalid syntax on line %d of file %s +#define ESS_INI_BADNUMBER MAKEHR(-10202) // Initialization variable ""%s"" set to invalid number +#define ESS_INI_BADPATH MAKEHR(-10203) // Initialization variable ""%s"" set to invalid path +#define ESS_INI_BADVALUE MAKEHR(-10205) // Initialization variable ""%s"" set to invalid value +#define ESS_INI_NOSUCHVAR MAKEHR(-10206) // Cannot find initialization variable "%s" +#define ESS_INI_NUMRANGE MAKEHR(-10207) // Initialization variable "%s" must be between %d and %d +#define ESS_INI_TOO_MANY_ENV MAKEHR(-10208) // Too many SS.INI environment strings +#define ESS_LOCK_TIMEOUT MAKEHR(-10266) // Timeout locking file: %s +#define ESS_MEM_NOMEMORY MAKEHR(-10270) // Out of memory +#define ESS_NO_TWEAK_CHKDOUT MAKEHR(-10625) // You cannot modify the properties of a file that is checked out. +#define ESS_NOMERGE_BIN_NODELTA MAKEHR(-10279) // You cannot perform a merge on a binary file, or a file that stores latest version only. +#define ESS_NOMULTI_BINARY MAKEHR(-10280) // Cannot check out %s. It is binary and is already checked out. +#define ESS_NOMULTI_NODELTA MAKEHR(-10281) // %s stores only the latest version and is already checked out. +#define ESS_OS_NOT_EXE MAKEHR(-10285) // Error executing: %s +#define ESS_SS_ADDPRJASSOCFILE MAKEHR(-10626) // %s is a SourceSafe configuration file and cannot be added. +#define ESS_SS_ADMIN_LOCKOUT MAKEHR(-10456) // The SourceSafe database has been locked by the Administrator. +#define ESS_SS_BADRENAME MAKEHR(-10402) // Unable to rename %s to %s. +#define ESS_SS_CANT_FIND_SSINI MAKEHR(-10403) // Cannot find SS.INI file for user %s +#define ESS_SS_CHECKED_OUT MAKEHR(-10405) // File %s is currently checked out by %s +#define ESS_SS_CHECKED_OUT_YOU MAKEHR(-10406) // You currently have file %s checked out +#define ESS_SS_CHECKOUT_OLD MAKEHR(-10408) // Cannot check out an old version of a file +#define ESS_SS_CHKOUT_USER MAKEHR(-10413) // File %s is currently checked out by %s +#define ESS_SS_CONFLICTS MAKEHR(-10415) // An automatic merge has occurred and there are conflicts.\nEdit %s to resolve them. +#define ESS_SS_DEL_ROOT MAKEHR(-10418) // Cannot delete the root project +#define ESS_SS_DEL_SHARED MAKEHR(-10419) // A deleted link to %s already exists +#define ESS_SS_FILE_NOTFOUND MAKEHR(-10421) // File ""%s"" not found +#define ESS_SS_HISTOPEN MAKEHR(-10404) // A history operation is already in progress +#define ESS_SS_INSUFRIGHTS MAKEHR(-10423) // You do not have access rights to %s +#define ESS_SS_LATERCHKEDOUT MAKEHR(-10426) // A more recent version is checked out +#define ESS_SS_LOCALRW MAKEHR(-10427) // A writable copy of %s already exists +#define ESS_SS_MOVE_CHANGENAME MAKEHR(-10428) // Move does not change the name of a project +#define ESS_SS_MOVE_NOPARENT MAKEHR(-10429) // Project %s does not exist +#define ESS_SS_MOVE_ROOT MAKEHR(-10430) // Cannot move the root project +#define ESS_SS_MUST_USE_VERS MAKEHR(-10431) // Cannot roll back to the most recent version of %s +#define ESS_SS_NOCOMMANCESTOR MAKEHR(-10432) // Files have no common ancestor +#define ESS_SS_NOCONFLICTS2 MAKEHR(-10434) // %s has been merged with no conflicts. +#define ESS_SS_NODOLLAR MAKEHR(-10435) // File %s is invalid. Files may not begin with $. +#define ESS_SS_NOT_CHKEDOUT MAKEHR(-10436) // File %s is not checked out +#define ESS_SS_NOT_SHARED MAKEHR(-10437) // File %s is not shared by any other projects +#define ESS_SS_NOTSEPARATED MAKEHR(-10438) // Files are not branched +#define ESS_SS_OPEN_LOGGIN MAKEHR(-10457) // Unable to open user login file %s. +#define ESS_SS_PATHTOOLONG MAKEHR(-10439) // Path %s too long +#define ESS_SS_RENAME_MOVE MAKEHR(-10442) // Rename does not move an item to another project +#define ESS_SS_RENAME_ROOT MAKEHR(-10443) // Cannot Rename the root project +#define ESS_SS_ROLLBACK_NOTOLD MAKEHR(-10447) // Cannot Rollback to the most recent version of %s +#define ESS_SS_SHARE_ANCESTOR MAKEHR(-10449) // A project cannot be shared under a descendant. +#define ESS_SS_SHARED MAKEHR(-10450) // File %s is already shared by this project +#define ESS_SSPEC_SYNTAX MAKEHR(-10515) // Invalid SourceSafe syntax: "%s" +#define ESS_UM_BAD_CHAR MAKEHR(-10550) // Bad username syntax: "%s" +#define ESS_UM_BAD_PASSWORD MAKEHR(-10551) // Invalid password +#define ESS_UM_BADVERSION MAKEHR(-10552) // Incompatible database version +#define ESS_UM_DEL_ADMIN MAKEHR(-10553) // Cannot delete the Admin user +#define ESS_UM_PERM_DENIED MAKEHR(-10554) // Permission denied +#define ESS_UM_RENAME_ADMIN MAKEHR(-10555) // Can not rename the Admin user +#define ESS_UM_TOO_LONG MAKEHR(-10556) // Username too long +#define ESS_UM_USER_EXISTS MAKEHR(-10557) // User "%s" already exists +#define ESS_UM_USER_NOT_FOUND MAKEHR(-10558) // User "%s" not found +#define ESS_URL_BADPATH MAKEHR(-10192) // The URL for project %s was not set properly. +#define ESS_VS_CHECKED_OUT MAKEHR(-10601) // File %s checked out +#define ESS_VS_CHILD_NOT_FOUND MAKEHR(-10602) // Subproject or file not found +#define ESS_VS_COLLISION MAKEHR(-10603) // Collision accessing database, please try again. +#define ESS_VS_EXCLUSIVE_CHECKED_OUT MAKEHR(-10614) // File %s is exclusively checked out. +#define ESS_VS_ITEMEXISTS MAKEHR(-10604) // An item with the name %s already exists +#define ESS_VS_LONGNAME MAKEHR(-10605) // %s is an invalid %s name +#define ESS_VS_MOVE_CYCLE MAKEHR(-10606) // You can not move a project under itself +#define ESS_VS_NO_DELTA MAKEHR(-10607) // File %s does not retain old versions of itself +#define ESS_VS_NOT_CHECKED_OUT MAKEHR(-10608) // File %s cannot be checked into this project +#define ESS_VS_NOT_FOUND MAKEHR(-10609) // File or project not found +#define ESS_VS_PARENT_NOT_FOUND MAKEHR(-10610) // Parent not found +#define ESS_VS_VERS_NOT_FOUND MAKEHR(-10615) // Version not found +#define ESS_VS_WANT_FILE MAKEHR(-10616) // This command only works on files. +#define ESS_VS_WANT_PRJ MAKEHR(-10617) // This command only works on projects. +#define ESS_URL_BUFOVERFLOW MAKEHR(-10194) // A link in %s was ignored because it was longer than SourceSafe can understand +#define ESS_URL_CANTCHECKHTML MAKEHR(-10193) // An error occurred while trying to check hyperlinks for %s +#define ESS_SS_ADDINFAILED MAKEHR(-10440) // Error loading SourceSafe add-in: %s +#define ESS_CANCEL MAKEHR(-32766) +#define ESS_LOADSTRING_FAILED MAKEHR(-10999) // Error loading resource string + +// SourceSafe questions answered affirmatively. +// +// A deleted copy of this %s file already exists in this project.\nDo you want to recover the existing file? +// Folder %s not found, create? +// Have any conflicts in %s been properly resolved? +// File %s is currently checked out by %s.\nProceed anyway? +// File %s was checked out to folder %s.\nProceed in %s? +// File %s is checked out to project %s, and you are in %s.\nProceed anyway? +// File %s is currently checked out by %s. Delete anyway? +// You currently have file %s checked out. Delete anyway? +// An item named %s was already deleted from this project.\nPurge the old item and delete this one now? +// This version of %s already has a label: overwrite? +// The label %s is already used. Remove the old label? +// %s has been merged with no conflicts.\nCheck in now? +// Redo the automatic merge? +// Delete local file: %s? +// %s is already checked out, continue? +// File %s has been destroyed, and cannot be rebuilt.\nContinue anyway? +// Project $%s has been destroyed, and cannot be rebuilt.\nContinue anyway? +// $%s was moved out of this project, and cannot be rebuilt.\nContinue anyway? +// %s has changed. Undo check out and lose changes? +// +// SourceSafe questions answered in the negative. +// +// A deleted file of the same name already exists in this SourceSafe project.\nDo you want to recover the deleted file instead of adding your local %s? +// %s is writable, replace? +// %s is checked out, replace? + + +#endif // #ifndef SSAUTERR_H + +////////////////////////////// eof //////////////////////////////// diff --git a/utils/Assimilate/ssauto.h b/utils/Assimilate/ssauto.h new file mode 100644 index 0000000..7645f3f --- /dev/null +++ b/utils/Assimilate/ssauto.h @@ -0,0 +1,608 @@ +// Filename:- ssauto.h +// + +/* This header file machine-generated by mktyplib.exe */ +/* Interface to type library: SourceSafeTypeLib */ + +#ifndef _SourceSafeTypeLib_H_ +#define _SourceSafeTypeLib_H_ + +DEFINE_GUID_THAT_WORKS(LIBID_SourceSafeTypeLib,0x783CD4E0L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F); +#ifndef BEGIN_INTERFACE +#define BEGIN_INTERFACE +#endif + +typedef enum _VSSFlags { + VSSFLAG_USERRONO = 1, + VSSFLAG_USERROYES = 2, + VSSFLAG_TIMENOW = 4, + VSSFLAG_TIMEMOD = 8, + VSSFLAG_TIMEUPD = 12, + VSSFLAG_EOLCR = 16, + VSSFLAG_EOLLF = 32, + VSSFLAG_EOLCRLF = 48, + VSSFLAG_REPASK = 64, + VSSFLAG_REPREPLACE = 128, + VSSFLAG_REPSKIP = 192, + VSSFLAG_REPMERGE = 256, + VSSFLAG_CMPFULL = 512, + VSSFLAG_CMPTIME = 1024, + VSSFLAG_CMPCHKSUM = 1536, + VSSFLAG_CMPFAIL = 2048, + VSSFLAG_RECURSNO = 4096, + VSSFLAG_RECURSYES = 8192, + VSSFLAG_FORCEDIRNO = 16384, + VSSFLAG_FORCEDIRYES = 32768, + VSSFLAG_KEEPNO = 65536, + VSSFLAG_KEEPYES = 131072, + VSSFLAG_DELNO = 262144, + VSSFLAG_DELYES = 524288, + VSSFLAG_DELNOREPLACE = 786432, + VSSFLAG_BINTEST = 1048576, + VSSFLAG_BINBINARY = 2097152, + VSSFLAG_BINTEXT = 3145728, + VSSFLAG_DELTAYES = 4194304, + VSSFLAG_DELTANO = 8388608, + VSSFLAG_UPDASK = 16777216, + VSSFLAG_UPDUPDATE = 33554432, + VSSFLAG_UPDUNCH = 50331648, + VSSFLAG_GETYES = 67108864, + VSSFLAG_GETNO = 134217728, + VSSFLAG_CHKEXCLUSIVEYES = 268435456, + VSSFLAG_CHKEXCLUSIVENO = 536870912, + VSSFLAG_HISTIGNOREFILES = 1073741824 +} VSSFlags; + +typedef enum _VSSFileStatus { + VSSFILE_NOTCHECKEDOUT = 0, + VSSFILE_CHECKEDOUT = 1, + VSSFILE_CHECKEDOUT_ME = 2 +} VSSFileStatus; + +typedef enum _VSSItemType { + VSSITEM_PROJECT = 0, + VSSITEM_FILE = 1 +} VSSItemType; + +interface IVSSItems; + +interface IVSSVersions; + +interface IVSSVersion; + +interface IVSSCheckouts; + +interface IVSSCheckout; + +DEFINE_GUID_THAT_WORKS(IID_IVSSItem,0x783CD4E1L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F); + +/* Definition of interface: IVSSItem */ +#undef INTERFACE +#define INTERFACE IVSSItem + +DECLARE_INTERFACE_(IVSSItem, IDispatch) +{ +BEGIN_INTERFACE +#ifndef NO_BASEINTERFACE_FUNCS + + /* IUnknown methods */ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + /* IDispatch methods */ + STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE; + + STDMETHOD(GetTypeInfo)( + THIS_ + UINT itinfo, + LCID lcid, + ITypeInfo FAR* FAR* pptinfo) PURE; + + STDMETHOD(GetIDsOfNames)( + THIS_ + REFIID riid, + OLECHAR FAR* FAR* rgszNames, + UINT cNames, + LCID lcid, + DISPID FAR* rgdispid) PURE; + + STDMETHOD(Invoke)( + THIS_ + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS FAR* pdispparams, + VARIANT FAR* pvarResult, + EXCEPINFO FAR* pexcepinfo, + UINT FAR* puArgErr) PURE; +#endif + + /* IVSSItem methods */ + STDMETHOD(get_Spec)(THIS_ BSTR FAR* pSpec) PURE; + STDMETHOD(get_Binary)(THIS_ VARIANT_BOOL FAR* pbBinary) PURE; + STDMETHOD(put_Binary)(THIS_ VARIANT_BOOL bBinary) PURE; + STDMETHOD(get_Deleted)(THIS_ VARIANT_BOOL FAR* pbDeleted) PURE; + STDMETHOD(put_Deleted)(THIS_ VARIANT_BOOL bDeleted) PURE; + STDMETHOD(get_Type)(THIS_ int FAR* piType) PURE; + STDMETHOD(get_LocalSpec)(THIS_ BSTR FAR* pLocal) PURE; + STDMETHOD(put_LocalSpec)(THIS_ BSTR Local) PURE; + STDMETHOD(get_Name)(THIS_ BSTR FAR* pName) PURE; + STDMETHOD(put_Name)(THIS_ BSTR Name) PURE; + STDMETHOD(get_Parent)(THIS_ IVSSItem FAR* FAR* ppIParent) PURE; + STDMETHOD(get_VersionNumber)(THIS_ long FAR* piVersion) PURE; + STDMETHOD(get_Items)(THIS_ VARIANT_BOOL IncludeDeleted, IVSSItems FAR* FAR* ppIItems) PURE; + STDMETHOD(Get)(THIS_ BSTR FAR* Local, long iFlags) PURE; + STDMETHOD(Checkout)(THIS_ BSTR Comment, BSTR Local, long iFlags) PURE; + STDMETHOD(Checkin)(THIS_ BSTR Comment, BSTR Local, long iFlags) PURE; + STDMETHOD(UndoCheckout)(THIS_ BSTR Local, long iFlags) PURE; + STDMETHOD(get_IsCheckedOut)(THIS_ long FAR* piStatus) PURE; + STDMETHOD(get_Checkouts)(THIS_ IVSSCheckouts FAR* FAR* ppICheckouts) PURE; + STDMETHOD(get_IsDifferent)(THIS_ BSTR Local, VARIANT_BOOL FAR* pbDifferent) PURE; + STDMETHOD(Add)(THIS_ BSTR Local, BSTR Comment, long iFlags, IVSSItem FAR* FAR* ppIItem) PURE; + STDMETHOD(NewSubproject)(THIS_ BSTR Name, BSTR Comment, IVSSItem FAR* FAR* ppIItem) PURE; + STDMETHOD(Share)(THIS_ IVSSItem FAR* pIItem, BSTR Comment, long iFlags) PURE; + STDMETHOD(Destroy)(THIS) PURE; + STDMETHOD(Move)(THIS_ IVSSItem FAR* pINewParent) PURE; + STDMETHOD(Label)(THIS_ BSTR Label, BSTR Comment) PURE; + STDMETHOD(get_Versions)(THIS_ long iFlags, IVSSVersions FAR* FAR* pIVersions) PURE; + STDMETHOD(get_Version)(THIS_ VARIANT Version, IVSSItem FAR* FAR* ppIItem) PURE; +}; + +DEFINE_GUID_THAT_WORKS(IID_IVSSVersions,0x783CD4E7L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F); + +/* Definition of interface: IVSSVersions */ +#undef INTERFACE +#define INTERFACE IVSSVersions + +DECLARE_INTERFACE_(IVSSVersions, IDispatch) +{ +BEGIN_INTERFACE +#ifndef NO_BASEINTERFACE_FUNCS + + /* IUnknown methods */ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + /* IDispatch methods */ + STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE; + + STDMETHOD(GetTypeInfo)( + THIS_ + UINT itinfo, + LCID lcid, + ITypeInfo FAR* FAR* pptinfo) PURE; + + STDMETHOD(GetIDsOfNames)( + THIS_ + REFIID riid, + OLECHAR FAR* FAR* rgszNames, + UINT cNames, + LCID lcid, + DISPID FAR* rgdispid) PURE; + + STDMETHOD(Invoke)( + THIS_ + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS FAR* pdispparams, + VARIANT FAR* pvarResult, + EXCEPINFO FAR* pexcepinfo, + UINT FAR* puArgErr) PURE; +#endif + + /* IVSSVersions methods */ + STDMETHOD(_NewEnum)(THIS_ IUnknown * FAR* ppIEnum) PURE; +}; + +//DEFINE_GUID_THAT_WORKS(IID_IVSSVersion,0x783CD4E8L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F);// original (and wrong) MS version, this equates as "IID_IVSSVersionOld", so fails during history enumeration -ste +DEFINE_GUID_THAT_WORKS(IID_IVSSVersion,0x2A0DE0E9L,0x2E9F,0x11d0,0x92,0x36,0x00,0xAA,0x00,0xA1,0xEB,0x95); // made from line below, from aauto.odl +// uuid(2A0DE0E9- 2E9F- 11d0- 92 36- 00 AA 00 A1 EB 95), // + +/* Definition of interface: IVSSVersion */ +#undef INTERFACE +#define INTERFACE IVSSVersion + +DECLARE_INTERFACE_(IVSSVersion, IDispatch) +{ +BEGIN_INTERFACE +#ifndef NO_BASEINTERFACE_FUNCS + + /* IUnknown methods */ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + /* IDispatch methods */ + STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE; + + STDMETHOD(GetTypeInfo)( + THIS_ + UINT itinfo, + LCID lcid, + ITypeInfo FAR* FAR* pptinfo) PURE; + + STDMETHOD(GetIDsOfNames)( + THIS_ + REFIID riid, + OLECHAR FAR* FAR* rgszNames, + UINT cNames, + LCID lcid, + DISPID FAR* rgdispid) PURE; + + STDMETHOD(Invoke)( + THIS_ + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS FAR* pdispparams, + VARIANT FAR* pvarResult, + EXCEPINFO FAR* pexcepinfo, + UINT FAR* puArgErr) PURE; +#endif + + /* IVSSVersion methods */ + STDMETHOD(get_Username)(THIS_ BSTR FAR* pUsername) PURE; + STDMETHOD(get_VersionNumber)(THIS_ long FAR* piVersion) PURE; + STDMETHOD(get_Action)(THIS_ BSTR FAR* pAction) PURE; + STDMETHOD(get_Date)(THIS_ DATE FAR* pDate) PURE; + STDMETHOD(get_Comment)(THIS_ BSTR FAR* pComment) PURE; + STDMETHOD(get_Label)(THIS_ BSTR FAR* pLabel) PURE; + STDMETHOD(get_VSSItem)(THIS_ IVSSItem FAR* FAR* ppIItem) PURE; +}; + +DEFINE_GUID_THAT_WORKS(IID_IVSSItems,0x783CD4E5L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F); + +/* Definition of interface: IVSSItems */ +#undef INTERFACE +#define INTERFACE IVSSItems + +DECLARE_INTERFACE_(IVSSItems, IDispatch) +{ +BEGIN_INTERFACE +#ifndef NO_BASEINTERFACE_FUNCS + + /* IUnknown methods */ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + /* IDispatch methods */ + STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE; + + STDMETHOD(GetTypeInfo)( + THIS_ + UINT itinfo, + LCID lcid, + ITypeInfo FAR* FAR* pptinfo) PURE; + + STDMETHOD(GetIDsOfNames)( + THIS_ + REFIID riid, + OLECHAR FAR* FAR* rgszNames, + UINT cNames, + LCID lcid, + DISPID FAR* rgdispid) PURE; + + STDMETHOD(Invoke)( + THIS_ + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS FAR* pdispparams, + VARIANT FAR* pvarResult, + EXCEPINFO FAR* pexcepinfo, + UINT FAR* puArgErr) PURE; +#endif + + /* IVSSItems methods */ + STDMETHOD(get_Count)(THIS_ long FAR* piCount) PURE; + STDMETHOD(get_Item)(THIS_ VARIANT sItem, IVSSItem FAR* FAR* ppIItem) PURE; + STDMETHOD(_NewEnum)(THIS_ IUnknown * FAR* ppIEnum) PURE; +}; + +DEFINE_GUID_THAT_WORKS(IID_IVSSCheckouts,0x8903A770L,0xF55F,0x11CF,0x92,0x27,0x00,0xAA,0x00,0xA1,0xEB,0x95); + +/* Definition of interface: IVSSCheckouts */ +#undef INTERFACE +#define INTERFACE IVSSCheckouts + +DECLARE_INTERFACE_(IVSSCheckouts, IDispatch) +{ +BEGIN_INTERFACE +#ifndef NO_BASEINTERFACE_FUNCS + + /* IUnknown methods */ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + /* IDispatch methods */ + STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE; + + STDMETHOD(GetTypeInfo)( + THIS_ + UINT itinfo, + LCID lcid, + ITypeInfo FAR* FAR* pptinfo) PURE; + + STDMETHOD(GetIDsOfNames)( + THIS_ + REFIID riid, + OLECHAR FAR* FAR* rgszNames, + UINT cNames, + LCID lcid, + DISPID FAR* rgdispid) PURE; + + STDMETHOD(Invoke)( + THIS_ + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS FAR* pdispparams, + VARIANT FAR* pvarResult, + EXCEPINFO FAR* pexcepinfo, + UINT FAR* puArgErr) PURE; +#endif + + /* IVSSCheckouts methods */ + STDMETHOD(get_Count)(THIS_ long FAR* piCount) PURE; + STDMETHOD(get_Item)(THIS_ VARIANT sItem, IVSSCheckout FAR* FAR* ppICheckout) PURE; + STDMETHOD(_NewEnum)(THIS_ IUnknown * FAR* ppIEnum) PURE; +}; + +DEFINE_GUID_THAT_WORKS(IID_IVSSCheckout,0x783CD4E6L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F); + +/* Definition of interface: IVSSCheckout */ +#undef INTERFACE +#define INTERFACE IVSSCheckout + +DECLARE_INTERFACE_(IVSSCheckout, IDispatch) +{ +BEGIN_INTERFACE +#ifndef NO_BASEINTERFACE_FUNCS + + /* IUnknown methods */ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + /* IDispatch methods */ + STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE; + + STDMETHOD(GetTypeInfo)( + THIS_ + UINT itinfo, + LCID lcid, + ITypeInfo FAR* FAR* pptinfo) PURE; + + STDMETHOD(GetIDsOfNames)( + THIS_ + REFIID riid, + OLECHAR FAR* FAR* rgszNames, + UINT cNames, + LCID lcid, + DISPID FAR* rgdispid) PURE; + + STDMETHOD(Invoke)( + THIS_ + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS FAR* pdispparams, + VARIANT FAR* pvarResult, + EXCEPINFO FAR* pexcepinfo, + UINT FAR* puArgErr) PURE; +#endif + + /* IVSSCheckout methods */ + STDMETHOD(get_Username)(THIS_ BSTR FAR* pUsername) PURE; + STDMETHOD(get_Date)(THIS_ DATE FAR* pDate) PURE; + STDMETHOD(get_LocalSpec)(THIS_ BSTR FAR* pLocal) PURE; + STDMETHOD(get_Machine)(THIS_ BSTR FAR* pMachine) PURE; + STDMETHOD(get_Project)(THIS_ BSTR FAR* pProject) PURE; + STDMETHOD(get_Comment)(THIS_ BSTR FAR* pComment) PURE; + STDMETHOD(get_VersionNumber)(THIS_ long FAR* piVersion) PURE; +}; + +DEFINE_GUID_THAT_WORKS(IID_IVSSDatabase,0x783CD4E2L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F); + +/* Definition of interface: IVSSDatabase */ +#undef INTERFACE +#define INTERFACE IVSSDatabase + +DECLARE_INTERFACE_(IVSSDatabase, IDispatch) +{ +BEGIN_INTERFACE +#ifndef NO_BASEINTERFACE_FUNCS + + /* IUnknown methods */ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + /* IDispatch methods */ + STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE; + + STDMETHOD(GetTypeInfo)( + THIS_ + UINT itinfo, + LCID lcid, + ITypeInfo FAR* FAR* pptinfo) PURE; + + STDMETHOD(GetIDsOfNames)( + THIS_ + REFIID riid, + OLECHAR FAR* FAR* rgszNames, + UINT cNames, + LCID lcid, + DISPID FAR* rgdispid) PURE; + + STDMETHOD(Invoke)( + THIS_ + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS FAR* pdispparams, + VARIANT FAR* pvarResult, + EXCEPINFO FAR* pexcepinfo, + UINT FAR* puArgErr) PURE; +#endif + + /* IVSSDatabase methods */ + STDMETHOD(Open)(THIS_ BSTR SrcSafeIni, BSTR Username, BSTR Password) PURE; + STDMETHOD(get_SrcSafeIni)(THIS_ BSTR FAR* pSrcSafeIni) PURE; + STDMETHOD(get_DatabaseName)(THIS_ BSTR FAR* pDatabaseName) PURE; + STDMETHOD(get_UserName)(THIS_ BSTR FAR* pUsername) PURE; + STDMETHOD(get_CurrentProject)(THIS_ BSTR FAR* pPrj) PURE; + STDMETHOD(put_CurrentProject)(THIS_ BSTR Prj) PURE; + STDMETHOD(get_VSSItem)(THIS_ BSTR Spec, VARIANT_BOOL Deleted, IVSSItem FAR* FAR* ppIVSSItem) PURE; +}; + +DEFINE_GUID_THAT_WORKS(CLSID_VSSItem,0x783CD4E3L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F); + +#ifdef __cplusplus +class VSSItem; +#endif + +DEFINE_GUID_THAT_WORKS(CLSID_VSSVersion,0x783CD4ECL,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F); + +#ifdef __cplusplus +class VSSVersion; +#endif + +DEFINE_GUID_THAT_WORKS(CLSID_VSSCheckout,0x2A0DE0E0L,0x2E9F,0x11D0,0x92,0x36,0x00,0xAA,0x00,0xA1,0xEB,0x95); + +#ifdef __cplusplus +class VSSCheckout; +#endif + +DEFINE_GUID_THAT_WORKS(CLSID_VSSDatabase,0x783CD4E4L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F); + +#ifdef __cplusplus +class VSSDatabase; +#endif + +DEFINE_GUID_THAT_WORKS(IID_IVSSEvents,0x783CD4E9L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F); + +/* Definition of interface: IVSSEvents */ +#undef INTERFACE +#define INTERFACE IVSSEvents + +DECLARE_INTERFACE_(IVSSEvents, IUnknown) +{ +BEGIN_INTERFACE +#ifndef NO_BASEINTERFACE_FUNCS + + /* IUnknown methods */ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; +#endif + + /* IVSSEvents methods */ + STDMETHOD(BeforeAdd)(THIS_ IVSSItem FAR* pIPrj, BSTR Local, VARIANT_BOOL FAR* pbContinue) PURE; + STDMETHOD(AfterAdd)(THIS_ IVSSItem FAR* pIItem, BSTR Local) PURE; + STDMETHOD(BeforeCheckout)(THIS_ IVSSItem FAR* pIItem, BSTR Local, VARIANT_BOOL FAR* pbContinue) PURE; + STDMETHOD(AfterCheckout)(THIS_ IVSSItem FAR* pIItem, BSTR Local) PURE; + STDMETHOD(BeforeCheckin)(THIS_ IVSSItem FAR* pIItem, BSTR Local, VARIANT_BOOL FAR* pbContinue) PURE; + STDMETHOD(AfterCheckin)(THIS_ IVSSItem FAR* pIItem, BSTR Local) PURE; + STDMETHOD(BeforeUndoCheckout)(THIS_ IVSSItem FAR* pIItem, BSTR Local, VARIANT_BOOL FAR* pbContinue) PURE; + STDMETHOD(AfterUndoCheckout)(THIS_ IVSSItem FAR* pIItem, BSTR Local) PURE; + STDMETHOD(BeforeRename)(THIS_ IVSSItem FAR* pIItem, BSTR NewName, VARIANT_BOOL FAR* pbContinue) PURE; + STDMETHOD(AfterRename)(THIS_ IVSSItem FAR* pIItem, BSTR OldName) PURE; + STDMETHOD(BeforeBranch)(THIS_ IVSSItem FAR* pIItem, VARIANT_BOOL FAR* pbContinue) PURE; + STDMETHOD(AfterBranch)(THIS_ IVSSItem FAR* pIItem) PURE; + STDMETHOD(BeforeEvent)(THIS_ long iEvent, IVSSItem FAR* pIItem, BSTR Str, VARIANT var, VARIANT_BOOL FAR* pbContinue) PURE; + STDMETHOD(AfterEvent)(THIS_ long iEvent, IVSSItem FAR* pIItem, BSTR Str, VARIANT var) PURE; +}; + +DEFINE_GUID_THAT_WORKS(IID_IVSS,0x783CD4EBL,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F); + +/* Definition of interface: IVSS */ +#undef INTERFACE +#define INTERFACE IVSS + +DECLARE_INTERFACE_(IVSS, IDispatch) +{ +BEGIN_INTERFACE +#ifndef NO_BASEINTERFACE_FUNCS + + /* IUnknown methods */ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + /* IDispatch methods */ + STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE; + + STDMETHOD(GetTypeInfo)( + THIS_ + UINT itinfo, + LCID lcid, + ITypeInfo FAR* FAR* pptinfo) PURE; + + STDMETHOD(GetIDsOfNames)( + THIS_ + REFIID riid, + OLECHAR FAR* FAR* rgszNames, + UINT cNames, + LCID lcid, + DISPID FAR* rgdispid) PURE; + + STDMETHOD(Invoke)( + THIS_ + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS FAR* pdispparams, + VARIANT FAR* pvarResult, + EXCEPINFO FAR* pexcepinfo, + UINT FAR* puArgErr) PURE; +#endif + + /* IVSS methods */ + STDMETHOD(get_VSSDatabase)(THIS_ IVSSDatabase FAR* FAR* ppIVSSDatabase) PURE; +}; + +DEFINE_GUID_THAT_WORKS(IID_IVSSEventHandler,0x783CD4EAL,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F); + +/* Definition of interface: IVSSEventHandler */ +#undef INTERFACE +#define INTERFACE IVSSEventHandler + +DECLARE_INTERFACE_(IVSSEventHandler, IUnknown) +{ +BEGIN_INTERFACE +#ifndef NO_BASEINTERFACE_FUNCS + + /* IUnknown methods */ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; +#endif + + /* IVSSEventHandler methods */ + STDMETHOD(Init)(THIS_ IVSS FAR* pIVSS) PURE; +}; + +DEFINE_GUID_THAT_WORKS(CLSID_VSSApp,0x2A0DE0E1L,0x2E9F,0x11D0,0x92,0x36,0x00,0xAA,0x00,0xA1,0xEB,0x95); + +#ifdef __cplusplus +class VSSApp; +#endif + +#endif //#ifndef _SourceSafeTypeLib_H_ + + +///////////////// eof ////////////////// diff --git a/utils/Assimilate/vect3.cpp b/utils/Assimilate/vect3.cpp new file mode 100644 index 0000000..001de9b --- /dev/null +++ b/utils/Assimilate/vect3.cpp @@ -0,0 +1,108 @@ +#include "vect3.h" + + +const Vect3 Vect3X(1.f,0.f,0.f); +const Vect3 Vect3Y(0.f,1.f,0.f); +const Vect3 Vect3Z(0.f,0.f,1.f); +const Vect3 Vect3negX(-1.f,0.f,0.f); +const Vect3 Vect3negY(0.f,-1.f,0.f); +const Vect3 Vect3negZ(0.f,0.f,-1.f); +const Vect3 Vect3Zero(0.f,0.f,0.f); + +const Vect3 &Vect3::operator/= (const float d) +{ + float inv=1.f/d; + return (*this)*=inv; +} + +void Vect3::Cross(const Vect3& p) +{ + Vect3 t=*this; + v[0]=t.v[1]*p.v[2]-t.v[2]*p.v[1]; + v[1]=t.v[2]*p.v[0]-t.v[0]*p.v[2]; + v[2]=t.v[0]*p.v[1]-t.v[1]*p.v[0]; +} + +void Vect3::NegCross(const Vect3& p) +{ + Vect3 t=*this; + v[0]=p.v[1]*t.v[2]-p.v[2]*t.v[1]; + v[1]=p.v[2]*t.v[0]-p.v[0]*t.v[2]; + v[2]=p.v[0]*t.v[1]-p.v[1]*t.v[0]; +} + +float Vect3::Dist(const Vect3& p) const +{ + Vect3 t=*this; + t-=p; + return t.Len(); +} + +float Vect3::Dist2(const Vect3& p) const +{ + Vect3 t=*this; + t-=p; + return t^t; +} + +void Vect3::Perp() +{ + float rlen,tlen; + Vect3 r,t; + r=*this; + r.Cross(Vect3X); + rlen=r.Len(); + t=*this; + t.Cross(Vect3Y); + tlen=t.Len(); + if (tlen>rlen) + { + r=t; + rlen=tlen; + } + t=*this; + t.Cross(Vect3Z); + tlen=t.Len(); + if (tlen>rlen) + { + r=t; + rlen=tlen; + } + *this=r; +} + +void Vect3::Min(const Vect3& p) +{ + if (p.v[0]v[0]) + v[0]=p.v[0]; + if (p.v[1]>v[1]) + v[1]=p.v[1]; + if (p.v[2]>v[2]) + v[2]=p.v[2]; +} + +float Vect3::MaxElement() const +{ + return v[MaxElementIndex()]; +} + +int Vect3::MaxElementIndex() const +{ + if (fabs(v[0])>fabs(v[1])&&fabs(v[0])>fabs(v[2])) + return 0; + if (fabs(v[1])>fabs(v[2])) + return 1; + return 2; +} + + diff --git a/utils/Assimilate/vect3.h b/utils/Assimilate/vect3.h new file mode 100644 index 0000000..aa40451 --- /dev/null +++ b/utils/Assimilate/vect3.h @@ -0,0 +1,92 @@ +#if !defined(VECT3_INC) +#define VECT3_INC + +#include +#include + +class Vect3 +{ + float v[3]; +public: + Vect3(const float val) {v[0]=val;v[1]=val;v[2]=val;} + Vect3() {}//never put anything in here! too slow} + Vect3(const float x,const float y,const float z) {v[0]=x;v[1]=y;v[2]=z;} + Vect3(const Vect3& t) {v[0]=t.v[0];v[1]=t.v[1];v[2]=t.v[2];} + Vect3(const float *t) {v[0]=t[0];v[1]=t[1];v[2]=t[2];} + float& operator[](int i) {return v[i];} + float& x() {return v[0];} + float& y() {return v[1];} + float& z() {return v[2];} + const float& operator[](int i) const {return v[i];} + const float& x() const {return v[0];} + const float& y() const {return v[1];} + const float& z() const {return v[2];} + void Set(const float x,const float y,const float z) {v[0]=x;v[1]=y;v[2]=z;} + + float Len() const {return (float)sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);} + float Len2() const {return v[0]*v[0]+v[1]*v[1]+v[2]*v[2];} + void Norm() {(*this)/=this->Len();} + bool ZeroNorm() {float d=this->Len();if (d>1E-10) {(*this)/=d;return true;} (*this)=0.0f; return false;} + void SafeNorm() {assert(this->Len()>1E-10);(*this)/=this->Len();} + + const Vect3 &operator= (const float d) {v[0]=d;v[1]=d;v[2]=d; return *this; } + const Vect3 &operator= (const Vect3& t) {v[0]=t.v[0];v[1]=t.v[1];v[2]=t.v[2]; return *this; } + + const Vect3 &operator+= (const float d) {v[0]+=d;v[1]+=d;v[2]+=d; return *this; } + const Vect3 &operator+= (const Vect3& t) {v[0]+=t.v[0];v[1]+=t.v[1];v[2]+=t.v[2]; return *this; } + + const Vect3 &operator-= (const float d) {v[0]-=d;v[1]-=d;v[2]-=d; return *this; } + const Vect3 &operator-= (const Vect3& t) {v[0]-=t.v[0];v[1]-=t.v[1];v[2]-=t.v[2]; return *this; } + + const Vect3 &operator*= (const float d) {v[0]*=d;v[1]*=d;v[2]*=d; return *this; } + const Vect3 &operator*= (const Vect3& t) {v[0]*=t.v[0];v[1]*=t.v[1];v[2]*=t.v[2]; return *this; } + + const Vect3 &operator/= (const float d); + const Vect3 &operator/= (const Vect3& t) {v[0]/=t.v[0];v[1]/=t.v[1];v[2]/=t.v[2]; return *this; } + + float operator^ (const Vect3& t) const {return v[0]*t.v[0]+v[1]*t.v[1]+v[2]*t.v[2];} + + float Dist(const Vect3&) const; + float Dist2(const Vect3&) const; + void Cross(const Vect3&); + void NegCross(const Vect3&); + void Perp(); + void Min(const Vect3&); + void Max(const Vect3&); + float MaxElement() const; + int MaxElementIndex() const; + + void Interp(const Vect3 &v1,const Vect3 &v2,float t) {*this=v1;*this-=v2;*this*=t;*this+=v2;} +// bool operator== (const Vect3& t) const {return v[0]==t.v[0]&&v[1]==t.v[1]&&v[2]==t.v[2];} + bool operator== (const Vect3& t) const {return fabs(v[0]-t.v[0])<.001f&&fabs(v[1]-t.v[1])<.001f&&fabs(v[2]-t.v[2])<.001f;} + bool operator< (const Vect3& t) const {assert(0);return false;} + bool operator!= (const Vect3& t) const {return !(v[0]==t.v[0]&&v[1]==t.v[1]&&v[2]==t.v[2]);} + bool operator> (const Vect3& t) const {assert(0);return false;} + + inline Vect3 operator +(const Vect3 &rhs) const { return Vect3(v[0]+rhs.v[0], v[1]+rhs.v[1], v[2]+rhs.v[2]); } + inline Vect3 operator -(const Vect3 &rhs) const { return Vect3(v[0]-rhs.v[0], v[1]-rhs.v[1], v[2]-rhs.v[2]); } + inline Vect3 operator *(const Vect3 &rhs) const { return Vect3(v[0]*rhs.v[0], v[1]*rhs.v[1], v[2]*rhs.v[2]); } + inline Vect3 operator *(const float scalar) const { return Vect3(v[0]*scalar, v[1]*scalar, v[2]*scalar); } + inline friend Vect3 operator *(const float scalar, const Vect3 &rhs); + inline Vect3 operator /(const Vect3 &rhs) const { return Vect3(v[0]/rhs.v[0], v[1]/rhs.v[1], v[2]/rhs.v[2]); } + inline Vect3 operator /(const float scalar) const { return Vect3(v[0]/scalar, v[1]/scalar, v[2]/scalar); } +}; + +inline Vect3 operator *(const float scalar, const Vect3 &rhs) +{ + return Vect3(scalar*rhs.v[0], scalar*rhs.v[1], scalar*rhs.v[2]); +} + + +extern const Vect3 Vect3X; +extern const Vect3 Vect3Y; +extern const Vect3 Vect3Z; +extern const Vect3 Vect3negX; +extern const Vect3 Vect3negY; +extern const Vect3 Vect3negZ; +extern const Vect3 Vect3Zero; + + + + +#endif diff --git a/utils/Assimilate/xsiimp.cpp b/utils/Assimilate/xsiimp.cpp new file mode 100644 index 0000000..323c516 --- /dev/null +++ b/utils/Assimilate/xsiimp.cpp @@ -0,0 +1,901 @@ +/********************************************************************** + *< + FILE: aiimp.cpp + + DESCRIPTION: .AI file import module + + CREATED BY: Tom Hudson + + HISTORY: created 28 June 1996 + + *> Copyright (c) 1996, All Rights Reserved. + **********************************************************************/ + +#include "Stdafx.h" +#include "Includes.h" + + +/* +#include +#include +#include +#include +#include +#include +*/ +#include "matrix4.h" +#include "xsiimp.h" + + +#define M_PI ( 3.14159265359f) + + +void +split_fn(char *path,char *file,char *pf) + { + int ix,jx,bs_loc,fn_loc; + if(strlen(pf)==0) { + if(path) *path=0; + if(file) *file=0; + return; + } + bs_loc=strlen(pf); + for(ix=bs_loc-1; ix>=0; --ix) { + if(pf[ix]=='\\') { + bs_loc=ix; + fn_loc=ix+1; + goto do_split; + } + if(pf[ix]==':') { + bs_loc=ix+1; + fn_loc=ix+1; + goto do_split; + } + } + bs_loc= -1; + fn_loc=0; + + do_split: + if(file) + strcpy(file,&pf[fn_loc]); + if(path) { + if(bs_loc>0) { + for(jx=0; jxPrint(ident+1); + sib=sib->sibling; + } + + PrintIdent(ident); + OutputDebugString("}\n"); + } + } +}; + +#define PDEB (0) + +char *ReadToken(char *at,char *end,int &len,char **out) +{ + char *ret=0; + len=0; + while (at'||*at=='{'||*at=='<'||*at=='\"')) + { + len++; + at++; + } + } + *out=at; +#if 0 + static char mess[10000]; + static char str[10000]; + mystrncpy(str,ret,len); + sprintf(mess,"Token |%s|\n",str); + OutputDebugString(mess); +#endif + return ret; +} + + +TxtNode *ReadANode(char *at,char *end,char **atout) +{ + TxtNode *ret=0; + int len; + char *retat; + char *start; +//OutputDebugString("***"); + start=ReadToken(at,end,len,&retat); + at=retat; + *atout=at; + if (!start) + { +//OutputDebugString("Eof\n"); + return 0; + } + if (*start=='}') + { +//OutputDebugString("End}\n"); + return 0; + } + if (*start=='{') + { +//OutputDebugString("Start{\n"); + ret=new TxtNode; + TxtNode **last=&ret->child; + while (1) + { + TxtNode *t=ReadANode(at,end,&retat); + at=retat; + if (!t) + break; + *last=t; + last=&t->sibling; + } + } + else + { + ret=new TxtNode; + ret->text=start; + ret->length=len; + } + *atout=at; + return ret; +} + +struct SceneNodes +{ + char name[100]; + SceneNodes *parent; + Matrix4 mat; + Matrix4 BaseMat; + Matrix4 *animat; + int nanimat; +}; + +static SceneNodes sNodes[5000]; +static nsNodes=0; + + +int FindNodes(SceneNodes *parent,TxtNode *t) +{ + static char tmp[1000]; + if (t->child) + { + TxtNode *sib=t->child; + while (sib) + { + if (sib->text&&!mystrncmp("Frame",sib->text,sib->length)) + { + SceneNodes tmpNode; + Matrix4 tm,tmBase; + tm.Identity(); + sib=sib->sibling; + if (!sib) + return 1; + assert(sib->length<100); + if (!strncmp("frm-",sib->text,4)) + mystrncpy(tmpNode.name,sib->text+4,sib->length-4); + else + mystrncpy(tmpNode.name,sib->text,sib->length); +//OutputDebugString(tmpNode.name); +//OutputDebugString("\n"); + + sib=sib->sibling; + if (!sib) + return 2; + { + TxtNode *FrameLook=sib; + TxtNode *sub=sib->child; + if (!sub) + return 3; + if (!sub->text) + return 4; + if (mystrncmp("FrameTransformMatrix",sub->text,sub->length)) + return 5; + sub=sub->sibling; + { + TxtNode *xf=sub->child; + int r,c; +#if 0 + OutputDebugString("matrix\n"); +#endif + for (r=0;r<4;r++) + { + int rr=r; + float sign=1.0f; + if (r==1) + rr=2; + if (r==2) + { + sign=-1.0f; + rr=1; + } + for (c=0;c<4;c++) + { + if (!xf||!xf->text) + return 6; + if (c==0) + tm[rr][0]=sign*(float)myatof(xf->text,xf->length); + else if (c==1) + tm[rr][2]=sign*(float)myatof(xf->text,xf->length); + else if (c==2) + tm[rr][1]=-sign*(float)myatof(xf->text,xf->length); + xf=xf->sibling; + } + } +#if 0 + for (r=0;r<4;r++) + { + sprintf(tmp,"%6f %6f %6f\n",tm[r][0],tm[r][1],tm[r][2]); + OutputDebugString(tmp); + } +#endif + tm.CalcFlags(); + } + tmBase=tm; + sub=sub->sibling; + while (sub) + { + if (sub->text&&!mystrncmp("SI_FrameBasePoseMatrix",sub->text,sub->length)) + { + sub=sub->sibling; + { + TxtNode *xf=sub->child; + int r,c; +#if 0 + OutputDebugString("pose matrix\n"); +#endif + for (r=0;r<4;r++) + { + int rr=r; + float sign=1.0f; + if (r==1) + rr=2; + if (r==2) + { + sign=-1.0f; + rr=1; + } + for (c=0;c<4;c++) + { + if (!xf||!xf->text) + return 6; + if (c==0) + tmBase[rr][0]=sign*(float)myatof(xf->text,xf->length); + else if (c==1) + tmBase[rr][2]=sign*(float)myatof(xf->text,xf->length); + else if (c==2) + tmBase[rr][1]=-sign*(float)myatof(xf->text,xf->length); + xf=xf->sibling; + } + } + tmBase.CalcFlags(); +#if 0 + for (r=0;r<4;r++) + { + sprintf(tmp,"%6f %6f %6f\n",tmBase[r][0],tmBase[r][1],tmBase[r][2]); + OutputDebugString(tmp); + } +#endif + } + } + sub=sub->sibling; + } + tmpNode.parent=parent; + tmpNode.mat=tm; + Matrix4 par,tmat; + if (parent) + tmat.Concat(tmBase,parent->BaseMat); + else + tmat=tmBase; + tmpNode.BaseMat=tmat; + SceneNodes *newparent=sNodes+nsNodes; + tmpNode.animat=0; + tmpNode.nanimat=0; + sNodes[nsNodes++]=tmpNode; + int err=FindNodes(newparent,FrameLook); + if (err) + return err; + } + } + sib=sib->sibling; + } + } + return 0; +} + +static int maxframe=-10000; +static int minframe=10000; +static int startframe=10000; +static int endframe=-10000; +static int CacheNodes[10000]; +static int nCacheNodes=0; +static char CacheName[100]; + +static int maxmatframe; +static Matrix4 mats[1000]; +static Vect3 scales[1000]; + +int ProcNKeys(TxtNode *t,int& foundkeys) +{ + if (t->child) + { + TxtNode *sib=t->child; + if (!sib->text) + return 207; + int tp=myatoi(sib->text,sib->length); + sib=sib->sibling; + if (!sib||!sib->text) + return 208; + int num=myatoi(sib->text,sib->length); + sib=sib->sibling; + int i; + int lastframe=startframe-1; + TxtNode *s=sib; + for (i=0;itext) + return 110; + int frame=myatoi(s->text,s->length); + if (frame>33000) + frame=frame-65536; + if (frameendframe) + endframe=frame; + if (frame-startframe>maxmatframe) + maxmatframe=frame-startframe; + s=s->sibling; + if (!s||!s->text) + return 212; + int junk=myatoi(s->text,s->length); + if (junk!=3) + return 213; + s=s->sibling; + if (!s||!s->text) + return 216; + float x=(float)myatof(s->text,s->length); + s=s->sibling; + if (!s||!s->text) + return 216; + float y=(float)myatof(s->text,s->length); + s=s->sibling; + if (!s||!s->text) + return 216; + float z=(float)myatof(s->text,s->length); + s=s->sibling; + int k; + if (tp==1) //scale + { + Vect3 v=Vect3(x,z,y); + for (k=lastframe+1;k<=frame;k++) + scales[k-startframe]=v; + foundkeys|=4; + } + else if (tp==2) //translation + { + Vect3 v(x,-z,y); + for (k=lastframe+1;k<=frame;k++) + { + mats[k-startframe].SetRow(3,v); + mats[k-startframe].CalcFlags(); + } + foundkeys|=1; + } + else if (tp==3) //rotation + { + Matrix4 tmp; + tmp.Rotate(-x*M_PI/180.0f,-y*M_PI/180.0f,-z*M_PI/180.0f); + Vect3 t,tt; + tmp.GetRow(2,t); + t*=-1.0f; + tmp.GetRow(1,tt); + tmp.SetRow(2,tt); + tmp.SetRow(1,t); + for (k=0;k<3;k++) + { + float tt=-tmp[k][2]; + tmp[k][2]=tmp[k][1]; + tmp[k][1]=tt; + } + for (k=lastframe+1;k<=frame;k++) + { + int l; + for (l=0;l<3;l++) + { + tmp.GetRow(l,t); + mats[k-startframe].SetRow(l,t); + mats[k-startframe].CalcFlags(); + } + } + foundkeys|=2; + } + lastframe=frame; + } + } + return 0; +} + +int FindPKeys(TxtNode *t) +{ + if (t->child) + { + TxtNode *sib=t->child; + if (!sib) + return 200; + char framename[100]; + { + if (!sib->child) + return 201; + if (!sib->child->text) + return 202; + if (!strncmp("frm-",sib->child->text,4)) + mystrncpy(framename,sib->child->text+4,sib->child->length-4); + else + { + if (!strncmp("SCENE",sib->child->text,4)) + return 0; + mystrncpy(framename,sib->child->text,sib->child->length); + } + sib=sib->sibling; + } + int foundkeys=0; + maxmatframe=0; + while (sib) + { + if (sib->text&&!mystrncmp("SI_AnimationKey",sib->text,sib->length)) + { + if (!sib->sibling) + return 103; + sib=sib->sibling; + int err=ProcNKeys(sib,foundkeys); + if (err) + return err; + } + sib=sib->sibling; + } +// if (foundkeys) + { + if (maxmatframe>maxframe) + maxframe=maxmatframe; + int i; + for (i=0;inanimat) + par=sNodes[i].parent->animat[j]; + else + par=sNodes[i].parent->BaseMat; + tmat.Concat(xf,par); + } + else + tmat=xf; + sNodes[i].animat[j]=tmat; + +#if 0 + par.Inverse(sNodes[i].BaseMat); + tmat.Concat(par,sNodes[i].animat[j]); + char tmp[1000]; + int r; + sprintf(tmp,"final frame %d %s\n",j,sNodes[i].name); + OutputDebugString(tmp); + for (r=0;r<4;r++) + { + sprintf(tmp,"%6f %6f %6f\n",tmat[r][0],tmat[r][1],tmat[r][2]); + OutputDebugString(tmp); + } + sprintf(tmp,"baseinv frame %d %s\n",j,sNodes[i].name); + OutputDebugString(tmp); + for (r=0;r<4;r++) + { + sprintf(tmp,"%6f %6f %6f\n",par[r][0],par[r][1],par[r][2]); + OutputDebugString(tmp); + } + sprintf(tmp,"bone frame %d %s\n",j,sNodes[i].name); + OutputDebugString(tmp); + for (r=0;r<4;r++) + { + sprintf(tmp,"%6f %6f %6f\n",sNodes[i].animat[j][r][0],sNodes[i].animat[j][r][1],sNodes[i].animat[j][r][2]); + OutputDebugString(tmp); + } +#endif + } + } + } + } + } + return 0; +} + +int FindSets(TxtNode *t) +{ + if (t->child) + { + TxtNode *sib=t->child; + while (sib) + { + if (sib->text&&!mystrncmp("Animation",sib->text,sib->length)) + { + if (!sib->sibling) + return 101; + sib=sib->sibling; + if (!sib->sibling) + return 102; + sib=sib->sibling; + int err=FindPKeys(sib); + if (err) + return err; + } + sib=sib->sibling; + } + } + return 0; +} + +int FindAnims(TxtNode *t) +{ + int i; + for (i=0;ichild) + { + TxtNode *sib=t->child; + while (sib) + { + if (sib->text&&!mystrncmp("AnimationSet",sib->text,sib->length)) + { + if (!sib->sibling) + return 100; + sib=sib->sibling; + int err=FindSets(sib); + if (err) + return err; + } + sib=sib->sibling; + } + } + return 0; +} + +/* +** returns the number of frames loaded +*/ +int XSI_LoadFile(const char *filename) +{ + char *filebin=0; + TxtNode *root; + + FILE *fp=fopen(filename,"ra"); + if (!fp){ + InfoBox(va("File not found: \"%s\"",filename)); + return 0; + } + fseek(fp,0,SEEK_END); + int len=ftell(fp); + filebin=new char[len]; + fseek(fp,0,SEEK_SET); + if (fread(filebin,1,len,fp)!=(size_t)len) + { + fclose(fp); + InfoBox(va("Bad XSI file\n",filename)); + delete(filebin); + return 0; + } + fclose(fp); + + char *at=filebin; + char *end=filebin+len; + root=new TxtNode; + TxtNode **last=&root->child; + char *retat; + while (1) + { + TxtNode *t=ReadANode(at,end,&retat); + if (!t) + { + at=end; + break; + } + at=retat; + *last=t; + last=&t->sibling; + } + if (!root->child) + { + printf("Bad XSI file\n"); + delete(filebin); + delete root; + return 0; + } +#if PDEB + root->Print(0); +#endif + + nsNodes=0; + maxframe=-10000; + minframe=10000; + startframe=10000; + endframe=-10000; + CacheName[0]=0; + int fn=FindNodes(0,root); + if (!fn) + { + fn=FindAnims(root); + } + + delete(filebin); + delete root; + + if (fn==2000) + { + printf("XSI import failed, Missing Keys\n",fn); + return 0; + } + if (fn) + { + printf("XSI import failed, code = %d",fn); + return 0; + } + return maxframe+1; +} + +int XSI_GetNumFrames() +{ + if (maxframe+1>0) + return maxframe+1; + return 0; +} + +int XSI_FindBone(char *bonename) +{ + int j; + for (j=0;j=0&&bone=0&&frame=0&&bone=0&&frame=0&&bone +#include +#include + + +#define MAX_ASE_MATERIALS 32 +#define MAX_ASE_OBJECTS 64 + 32 +#define MAX_ASE_ANIMATIONS 32 + +#define MAX_ASE_BONE_FRAMES (1280) +#define MAX_BONES (150) +#define MAX_BONES_VERT (8) + + +#define VERBOSE( x ) { if ( ase.verbose ) { printf x ; } } + +typedef struct +{ + int bone; + float wt; +} aseWeight_t; + +typedef struct +{ + float x, y, z; + float xBase, yBase, zBase; + float nx, ny, nz; + float s, t; + aseWeight_t Weights[MAX_BONES_VERT]; +} aseVertex_t; + +typedef struct +{ + float s, t; +} aseTVertex_t; + +typedef int aseFace_t[3]; + +typedef struct +{ + int numFaces; + int numVertexes; + int numTVertexes; + + int timeValue; + + aseVertex_t *vertexes; + aseTVertex_t *tvertexes; + aseFace_t *faces, *tfaces; + + int currentFace, currentVertex; + + int BoneRemap[MAX_BONES]; + int numBones; + int curBone,curBoneVertex; +} aseMesh_t; + +typedef struct +{ + char name[128]; +} aseMaterial_t; + +typedef struct +{ + char name[128]; + float scale; +} aseBone_t; + +/* +** contains the animate sequence of a single surface +** using a single material +*/ +typedef struct +{ + char name[128]; + + int materialRef; + + aseMesh_t anim; + +} aseGeomObject_t; + +typedef struct +{ + int numMaterials; + aseMaterial_t materials[MAX_ASE_MATERIALS]; + aseGeomObject_t objects[MAX_ASE_OBJECTS]; + + char *buffer; + char *curpos; + int len; + + int currentObject; + qboolean verbose; + qboolean grabAnims; + + int numBones; + int numBoneFrames; + aseBone_t bones[MAX_BONES]; + +} ase_t; + + +static char s_token[1024]; +static ase_t ase; +static ase_t aseGrab; + +static void ASE_Process( int type ); +static void ASE_FreeGeomObject( int ndx ); + +/* +** ASE_Load +*/ +void ASE_Load( const char *filename, qboolean verbose, qboolean grabAnims, int type ) +{ + FILE *fp = fopen( filename, "rb" ); + + if ( !fp ) + Error( "File not found '%s'", filename ); + + memset( &ase, 0, sizeof( ase ) ); + + ase.verbose = verbose; + ase.grabAnims = grabAnims; + ase.len = Q_filelength( fp ); + + ase.curpos = ase.buffer = malloc( ase.len ); + assert (ase.buffer != NULL); + + printf( "Processing '%s'\n", filename ); + + if ( fread( ase.buffer, ase.len, 1, fp ) != 1 ) + { + fclose( fp ); + Error( "fread() != 1 for '%s'", filename ); + } + + fclose( fp ); + + ASE_Process(type); + free (ase.buffer); +} + +/* +** ASE_Free +*/ +void ASE_Free( void ) +{ + int i; + + for ( i = 0; i < ase.currentObject; i++ ) + { + ASE_FreeGeomObject( i ); + } +} + +/* +** ASE_GetNumSurfaces +*/ +int ASE_GetNumSurfaces( void ) +{ + return ase.currentObject; +} + +/* +** ASE_GetSurfaceName +*/ +const char *ASE_GetSurfaceName( int which ) +{ + aseGeomObject_t *pObject = &ase.objects[which]; + + if ( !pObject->anim.numBones ) + return ""; + + return pObject->name; +} + +/* +** ASE_GetSurfaceAnimation +** +** Returns an animation (sequence of polysets) +*/ +polyset_t *ASE_GetSurfaceAnimation( int which, int *pNumFrames, int skipFrameStart, int skipFrameEnd, int maxFrames ) +{ + aseGeomObject_t *pObject = &ase.objects[which]; + aseMesh_t *pMesh = &pObject->anim; + polyset_t *psets; + int numFramesInAnimation; + int numFramesToKeep; + int f; + int t; + + numFramesInAnimation = 1; + + numFramesToKeep = numFramesInAnimation; + + *pNumFrames = numFramesToKeep; + + psets = calloc( sizeof( polyset_t ) * numFramesToKeep, 1 ); + + f=0; + + + strcpy( psets[f].name, pObject->name ); + strcpy( psets[f].materialname, ase.materials[pObject->materialRef].name ); + + psets[f].triangles = calloc( sizeof( triangle_t ) * pObject->anim.numFaces, 1 ); + psets[f].numtriangles = pObject->anim.numFaces; + + for ( t = 0; t < pObject->anim.numFaces; t++ ) + { + int k; + + for ( k = 0; k < 3; k++ ) + { + psets[f].triangles[t].verts[k][0] = pMesh->vertexes[pMesh->faces[t][k]].x; + psets[f].triangles[t].verts[k][1] = pMesh->vertexes[pMesh->faces[t][k]].y; + psets[f].triangles[t].verts[k][2] = pMesh->vertexes[pMesh->faces[t][k]].z; + + if ( pMesh->tvertexes && pMesh->tfaces ) + { + psets[f].triangles[t].texcoords[k][0] = pMesh->tvertexes[pMesh->tfaces[t][k]].s; + psets[f].triangles[t].texcoords[k][1] = pMesh->tvertexes[pMesh->tfaces[t][k]].t; + } + + } + } + return psets; +} + +static void ASE_FreeGeomObject( int ndx ) +{ + aseGeomObject_t *pObject; + pObject = &ase.objects[ndx]; + + if ( pObject->anim.vertexes ) + { + free( pObject->anim.vertexes ); + } + if ( pObject->anim.tvertexes ) + { + free( pObject->anim.tvertexes ); + } + if ( pObject->anim.faces ) + { + free( pObject->anim.faces ); + } + if ( pObject->anim.tfaces ) + { + free( pObject->anim.tfaces ); + } + + memset( pObject, 0, sizeof( *pObject ) ); +} + +static aseMesh_t *ASE_GetCurrentMesh( void ) +{ + aseGeomObject_t *pObject; + + if ( ase.currentObject >= MAX_ASE_OBJECTS ) + { + Error( "Too many GEOMOBJECTs" ); + return 0; // never called + } + + pObject = &ase.objects[ase.currentObject]; + + return &pObject->anim; +} + +static int CharIsTokenDelimiter( int ch ) +{ + if ( ch <= 32 ) + return 1; + return 0; +} + +static int ASE_GetToken( qboolean restOfLine ) +{ + int i = 0; + + if ( ase.buffer == 0 ) + return 0; + + if ( ( ase.curpos - ase.buffer ) == ase.len ) + return 0; + + // skip over crap + while ( ( ( ase.curpos - ase.buffer ) < ase.len ) && + ( *ase.curpos <= 32 ) ) + { + ase.curpos++; + } + + while ( ( ase.curpos - ase.buffer ) < ase.len ) + { + s_token[i] = *ase.curpos; + + ase.curpos++; + i++; + + if ( ( CharIsTokenDelimiter( s_token[i-1] ) && !restOfLine ) || + ( ( s_token[i-1] == '\n' ) || ( s_token[i-1] == '\r' ) ) ) + { + s_token[i-1] = 0; + break; + } + } + + s_token[i] = 0; + + return 1; +} + +static void ASE_ParseBracedBlock( void (*parser)( const char *token ) ) +{ + int indent = 0; + + while ( ASE_GetToken( qfalse ) ) + { + if ( !strcmp( s_token, "{" ) ) + { + indent++; + } + else if ( !strcmp( s_token, "}" ) ) + { + --indent; + if ( indent == 0 ) + break; + else if ( indent < 0 ) + Error( "Unexpected '}'" ); + } + else + { + if ( parser ) + parser( s_token ); + } + } +} + +static void ASE_SkipEnclosingBraces( void ) +{ + int indent = 0; + + while ( ASE_GetToken( qfalse ) ) + { + if ( !strcmp( s_token, "{" ) ) + { + indent++; + } + else if ( !strcmp( s_token, "}" ) ) + { + indent--; + if ( indent == 0 ) + break; + else if ( indent < 0 ) + Error( "Unexpected '}'" ); + } + } +} + +static void ASE_SkipRestOfLine( void ) +{ + ASE_GetToken( qtrue ); +} + +static void ASE_KeyMAP_DIFFUSE( const char *token ) +{ + char buffer[1024]; + int i = 0; + + if ( !strcmp( token, "*BITMAP" ) ) + { + ASE_GetToken( qfalse ); + + strcpy( buffer, s_token + 1 ); + if ( strchr( buffer, '"' ) ) + *strchr( buffer, '"' ) = 0; + + while ( buffer[i] ) + { + if ( buffer[i] == '\\' ) + buffer[i] = '/'; + i++; + } + + _strlwr(buffer); + if ( strstr( buffer, gamedir + 2 ) ) + { + strcpy( ase.materials[ase.numMaterials].name, strstr( buffer, gamedir + 2 ) + strlen( gamedir ) - 2 ); + } + else + { + sprintf( ase.materials[ase.numMaterials].name, "(not converted: '%s')", buffer ); + printf( "WARNING: illegal material name '%s'\n", buffer ); + } + } + else + { + } +} + +static void ASE_KeyMATERIAL( const char *token ) +{ + if ( !strcmp( token, "*MAP_DIFFUSE" ) ) + { + ASE_ParseBracedBlock( ASE_KeyMAP_DIFFUSE ); + } + else + { + } +} + +static void ASE_KeyMATERIAL_LIST( const char *token ) +{ + if ( !strcmp( token, "*MATERIAL_COUNT" ) ) + { + ASE_GetToken( qfalse ); + VERBOSE( ( "..num materials: %s\n", s_token ) ); + if ( atoi( s_token ) > MAX_ASE_MATERIALS ) + { + Error( "Too many materials!" ); + } + ase.numMaterials = 0; + } + else if ( !strcmp( token, "*MATERIAL" ) ) + { + VERBOSE( ( "..material %d ", ase.numMaterials ) ); + ASE_ParseBracedBlock( ASE_KeyMATERIAL ); + ase.numMaterials++; + } +} + +static void ASE_KeyMESH_VERTEX_LIST( const char *token ) +{ + aseMesh_t *pMesh = ASE_GetCurrentMesh(); + + if ( !strcmp( token, "*MESH_VERTEX" ) ) + { + ASE_GetToken( qfalse ); // skip number + +#if 0 //this is handled with matrices later + ASE_GetToken( qfalse ); + pMesh->vertexes[pMesh->currentVertex].y = atof( s_token ); + + ASE_GetToken( qfalse ); + pMesh->vertexes[pMesh->currentVertex].x = -atof( s_token ); + + ASE_GetToken( qfalse ); + pMesh->vertexes[pMesh->currentVertex].z = atof( s_token ); +#else + ASE_GetToken( qfalse ); + pMesh->vertexes[pMesh->currentVertex].x = atof( s_token ); + + ASE_GetToken( qfalse ); + pMesh->vertexes[pMesh->currentVertex].y = atof( s_token ); + + ASE_GetToken( qfalse ); + pMesh->vertexes[pMesh->currentVertex].z = atof( s_token ); +#endif + + pMesh->currentVertex++; + + if ( pMesh->currentVertex > pMesh->numVertexes ) + { + Error( "pMesh->currentVertex >= pMesh->numVertexes" ); + } + } + else + { + Error( "Unknown token '%s' while parsing MESH_VERTEX_LIST", token ); + } +} + +static void ASE_KeyMESH_FACE_LIST( const char *token ) +{ + aseMesh_t *pMesh = ASE_GetCurrentMesh(); + + if ( !strcmp( token, "*MESH_FACE" ) ) + { + ASE_GetToken( qfalse ); // skip face number + + ASE_GetToken( qfalse ); // skip label + ASE_GetToken( qfalse ); // first vertex + pMesh->faces[pMesh->currentFace][0] = atoi( s_token ); + + ASE_GetToken( qfalse ); // skip label + ASE_GetToken( qfalse ); // second vertex + pMesh->faces[pMesh->currentFace][2] = atoi( s_token ); + + ASE_GetToken( qfalse ); // skip label + ASE_GetToken( qfalse ); // third vertex + pMesh->faces[pMesh->currentFace][1] = atoi( s_token ); + + ASE_GetToken( qtrue ); + +/* + if ( ( p = strstr( s_token, "*MESH_MTLID" ) ) != 0 ) + { + p += strlen( "*MESH_MTLID" ) + 1; + mtlID = atoi( p ); + } + else + { + Error( "No *MESH_MTLID found for face!" ); + } +*/ + + pMesh->currentFace++; + } + else + { + Error( "Unknown token '%s' while parsing MESH_FACE_LIST", token ); + } +} + +static void ASE_KeyTFACE_LIST( const char *token ) +{ + aseMesh_t *pMesh = ASE_GetCurrentMesh(); + + if ( !strcmp( token, "*MESH_TFACE" ) ) + { + int a, b, c; + + ASE_GetToken( qfalse ); + + ASE_GetToken( qfalse ); + a = atoi( s_token ); + ASE_GetToken( qfalse ); + c = atoi( s_token ); + ASE_GetToken( qfalse ); + b = atoi( s_token ); + + pMesh->tfaces[pMesh->currentFace][0] = a; + pMesh->tfaces[pMesh->currentFace][1] = b; + pMesh->tfaces[pMesh->currentFace][2] = c; + + pMesh->currentFace++; + } + else + { + Error( "Unknown token '%s' in MESH_TFACE", token ); + } +} + +static void ASE_KeyMESH_TVERTLIST( const char *token ) +{ + aseMesh_t *pMesh = ASE_GetCurrentMesh(); + + if ( !strcmp( token, "*MESH_TVERT" ) ) + { + char u[80], v[80], w[80]; + + ASE_GetToken( qfalse ); + + ASE_GetToken( qfalse ); + strcpy( u, s_token ); + + ASE_GetToken( qfalse ); + strcpy( v, s_token ); + + ASE_GetToken( qfalse ); + strcpy( w, s_token ); + + pMesh->tvertexes[pMesh->currentVertex].s = atof( u ); + pMesh->tvertexes[pMesh->currentVertex].t = 1.0f - atof( v ); + + pMesh->currentVertex++; + + if ( pMesh->currentVertex > pMesh->numTVertexes ) + { + Error( "pMesh->currentVertex > pMesh->numTVertexes" ); + } + } + else + { + Error( "Unknown token '%s' while parsing MESH_TVERTLIST" ); + } +} + +static void ASE_KeyMESH( const char *token ) +{ + aseMesh_t *pMesh = ASE_GetCurrentMesh(); + + if ( !strcmp( token, "*TIMEVALUE" ) ) + { + ASE_GetToken( qfalse ); + + pMesh->timeValue = atoi( s_token ); + VERBOSE( ( ".....timevalue: %d\n", pMesh->timeValue ) ); + } + else if ( !strcmp( token, "*MESH_NUMVERTEX" ) ) + { + ASE_GetToken( qfalse ); + + pMesh->numVertexes = atoi( s_token ); + VERBOSE( ( ".....TIMEVALUE: %d\n", pMesh->timeValue ) ); + VERBOSE( ( ".....num vertexes: %d\n", pMesh->numVertexes ) ); + } + else if ( !strcmp( token, "*MESH_NUMFACES" ) ) + { + ASE_GetToken( qfalse ); + + pMesh->numFaces = atoi( s_token ); + VERBOSE( ( ".....num faces: %d\n", pMesh->numFaces ) ); + } + else if ( !strcmp( token, "*MESH_NUMTVFACES" ) ) + { + ASE_GetToken( qfalse ); + + if ( atoi( s_token ) != pMesh->numFaces ) + { + Error( "MESH_NUMTVFACES != MESH_NUMFACES" ); + } + } + else if ( !strcmp( token, "*MESH_NUMTVERTEX" ) ) + { + ASE_GetToken( qfalse ); + + pMesh->numTVertexes = atoi( s_token ); + VERBOSE( ( ".....num tvertexes: %d\n", pMesh->numTVertexes ) ); + } + else if ( !strcmp( token, "*MESH_VERTEX_LIST" ) ) + { + pMesh->vertexes = calloc( sizeof( aseVertex_t ) * pMesh->numVertexes, 1 ); + pMesh->currentVertex = 0; + VERBOSE( ( ".....parsing MESH_VERTEX_LIST\n" ) ); + ASE_ParseBracedBlock( ASE_KeyMESH_VERTEX_LIST ); + } + else if ( !strcmp( token, "*MESH_TVERTLIST" ) ) + { + pMesh->currentVertex = 0; + pMesh->tvertexes = calloc( sizeof( aseTVertex_t ) * pMesh->numTVertexes, 1 ); + VERBOSE( ( ".....parsing MESH_TVERTLIST\n" ) ); + ASE_ParseBracedBlock( ASE_KeyMESH_TVERTLIST ); + } + else if ( !strcmp( token, "*MESH_FACE_LIST" ) ) + { + pMesh->faces = calloc( sizeof( aseFace_t ) * pMesh->numFaces, 1 ); + pMesh->currentFace = 0; + VERBOSE( ( ".....parsing MESH_FACE_LIST\n" ) ); + ASE_ParseBracedBlock( ASE_KeyMESH_FACE_LIST ); + } + else if ( !strcmp( token, "*MESH_TFACELIST" ) ) + { + pMesh->tfaces = calloc( sizeof( aseFace_t ) * pMesh->numFaces, 1 ); + pMesh->currentFace = 0; + VERBOSE( ( ".....parsing MESH_TFACE_LIST\n" ) ); + ASE_ParseBracedBlock( ASE_KeyTFACE_LIST ); + } + else if ( !strcmp( token, "*MESH_NORMALS" ) ) + { + ASE_ParseBracedBlock( 0 ); + } +} + +static void ASE_KeyMESH_BONE_LIST( const char *token ) +{ + int i; + char buffer[1024]; + aseMesh_t *pMesh = ASE_GetCurrentMesh(); + + if ( !strcmp( token, "*MESH_BONE_NAME" ) ) + { + ASE_GetToken( qfalse ); + + ASE_GetToken( qfalse ); + + if (*s_token=='"') + strcpy( buffer, s_token + 1 ); + else + strcpy( buffer, s_token ); + if ( strchr( buffer, '"' ) ) + *strchr( buffer, '"' ) = 0; + for (i=0;i=ase.numBones) + { + strcpy(ase.bones[i].name,buffer); + ase.numBones++; + } + pMesh->BoneRemap[pMesh->curBone]=i; + pMesh->curBone++; + } + else + { + Error( "Unknown token '%s' in MESH_BONE_LIST", token ); + } +} + +static void ASE_KeyMESH_BONE_VERTEX_LIST( const char *token ) +{ + int i,b; + aseMesh_t *pMesh = ASE_GetCurrentMesh(); + + if ( !strcmp( token, "*MESH_BONE_VERTEX" ) ) + { + ASE_GetToken( qfalse ); // skip number + + ASE_GetToken( qfalse ); + pMesh->vertexes[pMesh->curBoneVertex].xBase = atof( s_token ); + + ASE_GetToken( qfalse ); + pMesh->vertexes[pMesh->curBoneVertex].yBase = atof( s_token ); + + ASE_GetToken( qfalse ); + pMesh->vertexes[pMesh->curBoneVertex].zBase = atof( s_token ); + + for (i=0;i<8;i++) + { + ASE_GetToken( qfalse ); + b=atoi( s_token ); + if (b>=0) + { + assert(bcurBone); + b=pMesh->BoneRemap[b]; + } + pMesh->vertexes[pMesh->curBoneVertex].Weights[i].bone = b; + assert(pMesh->vertexes[pMesh->curBoneVertex].Weights[i].bonevertexes[pMesh->curBoneVertex].Weights[i].wt = atof( s_token ); + } + + pMesh->curBoneVertex++; + + if ( pMesh->curBoneVertex > pMesh->numVertexes ) + { + Error( "pMesh->curBoneVertex >= pMesh->numVertexes" ); + } + } + else + { + Error( "Unknown token '%s' in MESH_BONE_VERTEX_LIST", token ); + } +} + +static void ASE_KeyMESH_WEIGHTS( const char *token ) +{ + aseMesh_t *pMesh = ASE_GetCurrentMesh(); + + if ( !strcmp( token, "*MESH_NUMBONE" ) ) + { + ASE_GetToken( qfalse ); + + pMesh->numBones = atoi( s_token ); + } + else if ( !strcmp( token, "*MESH_NUMVERTEX" ) ) + { + ASE_GetToken( qfalse ); + + if (pMesh->numVertexes != atoi( s_token )) + Error( "MESH_WEIGHTS NumVerts(%s) doesn't match mesh %s (%d)).", s_token, ase.objects[ase.currentObject].name, pMesh->numVertexes ); + } + else if ( !strcmp( token, "*MESH_BONE_LIST" ) ) + { + pMesh->curBone=0; + ASE_ParseBracedBlock( ASE_KeyMESH_BONE_LIST ); + } + else if ( !strcmp( token, "*MESH_BONE_VERTEX_LIST" ) ) + { + pMesh->curBoneVertex=0; + ASE_ParseBracedBlock( ASE_KeyMESH_BONE_VERTEX_LIST ); + } +} + + +static void ASE_KeyGEOMOBJECT( const char *token ) +{ + if ( !strcmp( token, "*NODE_NAME" ) ) + { + char *name = ase.objects[ase.currentObject].name; + + ASE_GetToken( qtrue ); + VERBOSE( ( " %s\n", s_token ) ); + strcpy( ase.objects[ase.currentObject].name, s_token + 1 ); + if ( strchr( ase.objects[ase.currentObject].name, '"' ) ) + *strchr( ase.objects[ase.currentObject].name, '"' ) = 0; + + if ( strstr( name, "tag" ) == name ) + { + while ( strchr( name, '_' ) != strrchr( name, '_' ) ) + { + *strrchr( name, '_' ) = 0; + } + while ( strrchr( name, ' ' ) ) + { + *strrchr( name, ' ' ) = 0; + } + } + } + else if ( !strcmp( token, "*NODE_PARENT" ) ) + { + ASE_SkipRestOfLine(); + } + // ignore unused data blocks + else if ( !strcmp( token, "*NODE_TM" ) || + !strcmp( token, "*TM_ANIMATION" ) ) + { + ASE_ParseBracedBlock( 0 ); + } + // ignore regular meshes that aren't part of animation + else if ( !strcmp( token, "*MESH" )) + { + ASE_ParseBracedBlock( ASE_KeyMESH ); + } + else if ( !strcmp( token, "*MESH_WEIGHTS" )) + { + ASE_ParseBracedBlock( ASE_KeyMESH_WEIGHTS ); + } + // according to spec these are obsolete + else if ( !strcmp( token, "*MATERIAL_REF" ) ) + { + ASE_GetToken( qfalse ); + + ase.objects[ase.currentObject].materialRef = atoi( s_token ); + } + // loads a sequence of animation frames + else if ( !strcmp( token, "*MESH_ANIMATION" ) ) + { + ASE_SkipEnclosingBraces(); + } + // skip unused info + else if ( !strcmp( token, "*PROP_MOTIONBLUR" ) || + !strcmp( token, "*PROP_CASTSHADOW" ) || + !strcmp( token, "*PROP_RECVSHADOW" ) ) + { + ASE_SkipRestOfLine(); + } +} + +static void CollapseObjects( void ) +{ +} + +/* +** ASE_Process +*/ +static void ASE_Process( int type ) +{ + while ( ASE_GetToken( qfalse ) ) + { + if ( !strcmp( s_token, "*3DSMAX_ASCIIEXPORT" ) || + !strcmp( s_token, "*COMMENT" ) ) + { + ASE_SkipRestOfLine(); + } + else if ( !strcmp( s_token, "*SCENE" ) ) + ASE_SkipEnclosingBraces(); + else if ( !strcmp( s_token, "*MATERIAL_LIST" ) ) + { + VERBOSE( ("MATERIAL_LIST\n") ); + + ASE_ParseBracedBlock( ASE_KeyMATERIAL_LIST ); + } + else if ( !strcmp( s_token, "*GEOMOBJECT" ) ) + { + VERBOSE( ("GEOMOBJECT" ) ); + + ASE_ParseBracedBlock( ASE_KeyGEOMOBJECT ); + + if (!ase.objects[ase.currentObject].anim.numFaces) //we didn't get any faces of animation! + { + printf( "WARNING: ASE_Process no triangles grabbed for %s!\n", ase.objects[ase.currentObject].name); + } + _strlwr(ase.objects[ase.currentObject].name); + if ( strstr( ase.objects[ase.currentObject].name, "Bip" ) || + strstr( ase.objects[ase.currentObject].name, "ignore_" ) ) + { + VERBOSE( ( "(discarding BIP/ignore object)\n" ) ); + ASE_FreeGeomObject( ase.currentObject ); + } + else if ( (type /*== TYPE_PLAYER*/) && ( strstr( ase.objects[ase.currentObject].name, "h_" ) != ase.objects[ase.currentObject].name ) && + ( strstr( ase.objects[ase.currentObject].name, "l_" ) != ase.objects[ase.currentObject].name ) && + ( strstr( ase.objects[ase.currentObject].name, "u_" ) != ase.objects[ase.currentObject].name ) && + ( strstr( ase.objects[ase.currentObject].name, "tag" ) != ase.objects[ase.currentObject].name ) && + ase.grabAnims ) + { + VERBOSE( ( "(ignoring improperly labeled object '%s')\n", ase.objects[ase.currentObject].name ) ); + ASE_FreeGeomObject( ase.currentObject ); + } + else + { + if ( ++ase.currentObject == MAX_ASE_OBJECTS ) + { + Error( "Too many GEOMOBJECTs" ); + } + } + } + else if ( s_token[0] ) + { + printf( "Unknown token '%s'\n", s_token ); + } + } + + if ( !ase.currentObject ) + Error( "No animation data!" ); + +// CollapseObjects(); +} diff --git a/utils/Assimilate/xsilib.h b/utils/Assimilate/xsilib.h new file mode 100644 index 0000000..dbc3f33 --- /dev/null +++ b/utils/Assimilate/xsilib.h @@ -0,0 +1,18 @@ +#include "..\common\cmdlib.h" +#include "..\common\mathlib.h" +#include "..\common\polyset.h" + +typedef enum +{ + FRAMECOPY_NONE = 0, + FRAMECOPY_1TO1, + FRAMECOPY_FILL +} framecopy_type; + + +void ASE_Load( const char *filename, qboolean verbose, qboolean meshanims, int type ); +int ASE_GetNumSurfaces( void ); +const char *ASE_GetSurfaceName( int ndx ); +void ASE_Free( void ); +polyset_t *ASE_GetSurfaceAnimation( int which, int *pNumFrames, int skipFrameStart, int skipFrameEnd, int maxFrames ); +void ASE_PutToSM(int lod,char *names); diff --git a/utils/Ibize/BlockStream.cpp b/utils/Ibize/BlockStream.cpp new file mode 100644 index 0000000..b91a9b8 --- /dev/null +++ b/utils/Ibize/BlockStream.cpp @@ -0,0 +1,675 @@ +// Interpreted Block Stream Functions +// +// -- jweier + +#pragma warning(disable : 4100) //unref formal parm +#pragma warning(disable : 4710) //member not inlined + +#include +#include "BlockStream.h" + +/* +=================================================================================================== + + CBlockMember + +=================================================================================================== +*/ + +CBlockMember::CBlockMember( void ) +{ + m_id = -1; + m_size = -1; + m_data = NULL; +} + +CBlockMember::~CBlockMember( void ) +{ + Free(); +} + +/* +------------------------- +Free +------------------------- +*/ + +void CBlockMember::Free( void ) +{ + if ( m_data != NULL ) + { + free ( m_data ); + m_data = NULL; + + m_id = m_size = -1; + } +} + +/* +------------------------- +GetInfo +------------------------- +*/ + +void CBlockMember::GetInfo( int *id, int *size, void **data ) +{ + *id = m_id; + *size = m_size; + *data = m_data; +} + +/* +------------------------- +SetData overloads +------------------------- +*/ + +void CBlockMember::SetData( const char *data ) +{ + WriteDataPointer( data, strlen(data)+1 ); +} + +void CBlockMember::SetData( vector_t data ) +{ + WriteDataPointer( data, 3 ); +} + +void CBlockMember::SetData( void *data, int size ) +{ + if ( m_data ) + free( m_data ); + + m_data = malloc( size ); + memcpy( m_data, data, size ); + m_size = size; +} + +// Member I/O functions + +/* +------------------------- +ReadMember +------------------------- +*/ + +int CBlockMember::ReadMember( char **stream, long *streamPos ) +{ + m_id = *(int *) (*stream + *streamPos); + *streamPos += sizeof( int ); + + m_size = *(long *) (*stream + *streamPos); + *streamPos += sizeof( long ); + + m_data = malloc( m_size ); + memset( m_data, 0, m_size ); + + memcpy( m_data, (*stream + *streamPos), m_size ); + *streamPos += m_size; + + return true; +} + +/* +------------------------- +WriteMember +------------------------- +*/ + +int CBlockMember::WriteMember( FILE *m_fileHandle ) +{ + fwrite( &m_id, sizeof(m_id), 1, m_fileHandle ); + fwrite( &m_size, sizeof(m_size), 1, m_fileHandle ); + fwrite( m_data, m_size, 1, m_fileHandle ); + + return true; +} + +/* +------------------------- +Duplicate +------------------------- +*/ + +CBlockMember *CBlockMember::Duplicate( void ) +{ + CBlockMember *newblock = new CBlockMember; + + if ( newblock == NULL ) + return NULL; + + newblock->SetData( m_data, m_size ); + newblock->SetSize( m_size ); + newblock->SetID( m_id ); + + return newblock; +} + +/* +=================================================================================================== + + CBlock + +=================================================================================================== +*/ + +CBlock::CBlock( void ) +{ + m_numMembers = 0; + m_flags = 0; + m_id = 0; +} + +CBlock::~CBlock( void ) +{ + Free(); +} + +/* +------------------------- +Init +------------------------- +*/ + +int CBlock::Init( void ) +{ + m_numMembers = 0; + m_flags = 0; + m_id = 0; + + return true; +} + +/* +------------------------- +Create +------------------------- +*/ + +int CBlock::Create( int block_id ) +{ + Init(); + + m_id = block_id; + + return true; +} + +/* +------------------------- +Free +------------------------- +*/ + +int CBlock::Free( void ) +{ + int numMembers = GetNumMembers(); + CBlockMember *bMember; + + while ( numMembers-- ) + { + bMember = GetMember( numMembers ); + + if (!bMember) + return false; + + delete bMember; + } + + m_members.clear(); //List of all CBlockMembers owned by this list + m_numMembers = 0; + + return true; +} + +// Write overloads + +/* +------------------------- +Write +------------------------- +*/ + +int CBlock::Write( int member_id, const char *member_data ) +{ + CBlockMember *bMember = new CBlockMember; + + bMember->SetID( member_id ); + + bMember->SetData( member_data ); + bMember->SetSize( strlen(member_data) + 1 ); + + AddMember( bMember ); + + return true; +} + +int CBlock::Write( int member_id, vector_t member_data ) +{ + CBlockMember *bMember; + + bMember = new CBlockMember; + + bMember->SetID( member_id ); + bMember->SetData( member_data ); + bMember->SetSize( sizeof(vector_t) ); + + AddMember( bMember ); + + return true; +} + +int CBlock::Write( int member_id, float member_data ) +{ + CBlockMember *bMember = new CBlockMember; + + bMember->SetID( member_id ); + bMember->WriteData( member_data ); + bMember->SetSize( sizeof(member_data) ); + + AddMember( bMember ); + + return true; +} + +int CBlock::Write( int member_id, int member_data ) +{ + CBlockMember *bMember = new CBlockMember; + + bMember->SetID( member_id ); + bMember->WriteData( member_data ); + bMember->SetSize( sizeof(member_data) ); + + AddMember( bMember ); + + return true; +} + + +int CBlock::Write( CBlockMember *bMember ) +{ +// findme: this is wrong: bMember->SetSize( sizeof(bMember->GetData()) ); + + AddMember( bMember ); + + return true; +} + +// Member list functions + +/* +------------------------- +AddMember +------------------------- +*/ + +int CBlock::AddMember( CBlockMember *member ) +{ + m_members.insert( m_members.end(), member ); + m_numMembers++; + + return true; +} + +/* +------------------------- +GetMember +------------------------- +*/ + +CBlockMember *CBlock::GetMember( int memberNum ) +{ + if ( memberNum > m_numMembers-1 ) + return false; + + return m_members[ memberNum ]; +} + +/* +------------------------- +GetMemberData +------------------------- +*/ + +void *CBlock::GetMemberData( int memberNum ) +{ + if ( memberNum > m_numMembers-1 ) + return NULL; + + return (void *) ((GetMember( memberNum ))->GetData()); +} + +/* +------------------------- +Duplicate +------------------------- +*/ + +CBlock *CBlock::Duplicate( void ) +{ + blockMember_v::iterator mi; + CBlock *newblock; + + newblock = new CBlock; + + if ( newblock == NULL ) + return false; + + newblock->Create( m_id ); + + //Duplicate entire block and return the cc + for ( mi = m_members.begin(); mi != m_members.end(); mi++ ) + { + newblock->AddMember( (*mi)->Duplicate() ); + } + + return newblock; +} + +/* +=================================================================================================== + + CBlockStream + +=================================================================================================== +*/ + +CBlockStream::CBlockStream( void ) +{ + m_stream = NULL; + m_streamPos = 0; +} + +CBlockStream::~CBlockStream( void ) +{ +} + +/* +------------------------- +GetChar +------------------------- +*/ + +char CBlockStream::GetChar( void ) +{ + char data; + + data = *(char*) (m_stream + m_streamPos); + m_streamPos += sizeof( data ); + + return data; +} + +/* +------------------------- +GetUnsignedInteger +------------------------- +*/ + +unsigned CBlockStream::GetUnsignedInteger( void ) +{ + unsigned data; + + data = *(unsigned *) (m_stream + m_streamPos); + m_streamPos += sizeof( data ); + + return data; +} + +/* +------------------------- +GetInteger +------------------------- +*/ + +int CBlockStream::GetInteger( void ) +{ + int data; + + data = *(int *) (m_stream + m_streamPos); + m_streamPos += sizeof( data ); + + return data; +} + +/* +------------------------- +GetLong +------------------------- +*/ + +long CBlockStream::GetLong( void ) +{ + long data; + + data = *(long *) (m_stream + m_streamPos); + m_streamPos += sizeof( data ); + + return data; +} + +/* +------------------------- +GetFloat +------------------------- +*/ + +float CBlockStream::GetFloat( void ) +{ + float data; + + data = *(float *) (m_stream + m_streamPos); + m_streamPos += sizeof( data ); + + return data; +} + +// Extension stripping utility + +/* +------------------------- +StripExtension +------------------------- +*/ + +void CBlockStream::StripExtension( const char *in, char *out ) +{ + int i = strlen(in); + + while ( (in[i] != '.') && (i >= 0) ) + i--; + + if ( i < 0 ) + { + strcpy(out, in); + return; + } + + strncpy(out, in, i); +} + +/* +------------------------- +Free +------------------------- +*/ + +int CBlockStream::Free( void ) +{ + //NOTENOTE: It is assumed that the user will free the passed memory block (m_stream) immediately after the run call + // That's why this doesn't free the memory, it only clears its internal pointer + + m_stream = NULL; + m_streamPos = 0; + + return true; +} + +/* +------------------------- +Create +------------------------- +*/ + +int CBlockStream::Create( char *filename ) +{ + char newName[MAX_FILENAME_LENGTH], *id_header = IBI_HEADER_ID; + float version = IBI_VERSION; + + //Clear the temp string + memset(newName, 0, sizeof(newName)); + + //Strip the extension and add the BLOCK_EXT extension + strcpy((char *) m_fileName, filename); + StripExtension( (char *) m_fileName, (char *) &newName ); + strcat((char *) newName, IBI_EXT); + + //Recover that as the active filename + strcpy(m_fileName, newName); + + if ( ((m_fileHandle = fopen(m_fileName, "wb")) == NULL) ) + { + return false; + } + + fwrite( id_header, 1, sizeof(id_header), m_fileHandle ); + fwrite( &version, 1, sizeof(version), m_fileHandle ); + + return true; +} + +/* +------------------------- +Init +------------------------- +*/ + +int CBlockStream::Init( void ) +{ + m_fileHandle = NULL; + memset(m_fileName, 0, sizeof(m_fileName)); + + m_stream = NULL; + m_streamPos = 0; + + return true; +} + +// Block I/O functions + +/* +------------------------- +WriteBlock +------------------------- +*/ + +int CBlockStream::WriteBlock( CBlock *block ) +{ + CBlockMember *bMember; + int id = block->GetBlockID(); + int numMembers = block->GetNumMembers(); + unsigned char flags = block->GetFlags(); + + fwrite ( &id, sizeof(id), 1, m_fileHandle ); + fwrite ( &numMembers, sizeof(numMembers), 1, m_fileHandle ); + fwrite ( &flags, sizeof( flags ), 1, m_fileHandle ); + + for ( int i = 0; i < numMembers; i++ ) + { + bMember = block->GetMember( i ); + bMember->WriteMember( m_fileHandle ); + } + + block->Free(); + + return true; +} + +/* +------------------------- +BlockAvailable +------------------------- +*/ + +int CBlockStream::BlockAvailable( void ) +{ + if ( m_streamPos >= m_fileSize ) + return false; + + return true; +} + +/* +------------------------- +ReadBlock +------------------------- +*/ + +int CBlockStream::ReadBlock( CBlock *get ) +{ + CBlockMember *bMember; + int b_id, numMembers; + unsigned char flags; + + if (!BlockAvailable()) + return false; + + b_id = GetInteger(); + numMembers = GetInteger(); + flags = (unsigned char) GetChar(); + + if (numMembers < 0) + return false; + + get->Create( b_id ); + get->SetFlags( flags ); + + while ( numMembers-- > 0) + { + bMember = new CBlockMember; + bMember->ReadMember( &m_stream, &m_streamPos ); + get->AddMember( bMember ); + } + + return true; +} + +/* +------------------------- +Open +------------------------- +*/ + +int CBlockStream::Open( char *buffer, long size ) +{ + char id_header[sizeof(IBI_HEADER_ID)]; + float version; + + Init(); + + m_fileSize = size; + + m_stream = buffer; + + for ( int i = 0; i < sizeof( id_header ); i++ ) + { + id_header[i] = GetChar(); + } + + version = GetFloat(); + + //Check for valid header + if ( strcmp( id_header, IBI_HEADER_ID ) ) + { + Free(); + return false; + } + + //Check for valid version + if ( version != IBI_VERSION ) + { + Free(); + return false; + } + + return true; +} diff --git a/utils/Ibize/IBIze.dsp b/utils/Ibize/IBIze.dsp new file mode 100644 index 0000000..ebd8003 --- /dev/null +++ b/utils/Ibize/IBIze.dsp @@ -0,0 +1,125 @@ +# Microsoft Developer Studio Project File - Name="IBIze" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=IBIze - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "IBIze.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "IBIze.mak" CFG="IBIze - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "IBIze - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "IBIze - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName ""$/Utils/IBIze", YIOAAAAA" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "IBIze - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "NOT_USING_MODULES" /YX /FD /I /startrekCPP/icarus" /I /icarus" /I /icarus" /I /icarus" " " " " /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "IBIze - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\startrek\icarus" /D "_DEBUG" /D "__DS__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "NOT_USING_MODULES" /FR /YX /FD /I /icarus" /I /icarus" /I /icarus" /GZ " " " /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "IBIze - Win32 Release" +# Name "IBIze - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\BlockStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\IBIze.cpp +# End Source File +# Begin Source File + +SOURCE=.\Interpreter.cpp +# End Source File +# Begin Source File + +SOURCE=.\Tokenizer.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\BlockStream.h +# End Source File +# Begin Source File + +SOURCE=.\Interpreter.h +# End Source File +# Begin Source File + +SOURCE=.\Tokenizer.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/utils/Ibize/IBIze.dsw b/utils/Ibize/IBIze.dsw new file mode 100644 index 0000000..f9bde47 --- /dev/null +++ b/utils/Ibize/IBIze.dsw @@ -0,0 +1,37 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "IBIze"=.\IBIze.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/Utils/IBIze", YIOAAAAA + . + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ + begin source code control + "$/Utils/IBIze", YIOAAAAA + . + end source code control +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/code/icarus/Interpreter.cpp b/utils/Ibize/Interpreter.cpp similarity index 100% rename from code/icarus/Interpreter.cpp rename to utils/Ibize/Interpreter.cpp diff --git a/code/icarus/Tokenizer.cpp b/utils/Ibize/Tokenizer.cpp similarity index 100% rename from code/icarus/Tokenizer.cpp rename to utils/Ibize/Tokenizer.cpp diff --git a/utils/Ibize/blockstream.h b/utils/Ibize/blockstream.h new file mode 100644 index 0000000..ea11272 --- /dev/null +++ b/utils/Ibize/blockstream.h @@ -0,0 +1,181 @@ +// BlockStream.h + +#ifndef __INTERPRETED_BLOCK_STREAM__ +#define __INTERPRETED_BLOCK_STREAM__ + +#pragma warning(disable : 4786) //identifier was truncated + +#include + +#include +#include +using namespace std; + +#define IBI_EXT ".IBI" //(I)nterpreted (B)lock (I)nstructions +#define IBI_HEADER_ID "IBI" + +const float IBI_VERSION = 1.57f; +const int MAX_FILENAME_LENGTH = 1024; + +typedef float vector_t[3]; + +enum +{ + POP_FRONT, + POP_BACK, + PUSH_FRONT, + PUSH_BACK +}; + +// Templates + +// CBlockMember + +class CBlockMember +{ +public: + + CBlockMember(); + ~CBlockMember(); + + void Free( void ); + + int WriteMember ( FILE * ); //Writes the member's data, in block format, to FILE * + int ReadMember( char **, long * ); //Reads the member's data, in block format, from FILE * + + void SetID( int id ) { m_id = id; } //Set the ID member variable + void SetSize( int size ) { m_size = size; } //Set the size member variable + + void GetInfo( int *, int *, void **); + + //SetData overloads + void SetData( const char * ); + void SetData( vector_t ); + void SetData( void *data, int size ); + + int GetID( void ) const { return m_id; } //Get ID member variables + void *GetData( void ) const { return m_data; } //Get data member variable + int GetSize( void ) const { return m_size; } //Get size member variable + + CBlockMember *Duplicate( void ); + + template WriteData(T &data) + { + if ( m_data ) + free( m_data ); + + m_data = malloc( sizeof(T) ); + *((T *) m_data) = data; + m_size = sizeof(T); + } + + template WriteDataPointer(const T *data, int num) + { + if ( m_data ) + free( m_data ); + + m_data = malloc( num*sizeof(T) ); + memcpy( m_data, data, num*sizeof(T) ); + m_size = num*sizeof(T); + } + +protected: + + int m_id; //ID of the value contained in data + int m_size; //Size of the data member variable + void *m_data; //Data for this member +}; + +//CBlock + +class CBlock +{ + typedef vector< CBlockMember * > blockMember_v; + +public: + + CBlock(); + ~CBlock(); + + int Init( void ); + + int Create( int ); + int Free(); + + //Write Overloads + + int Write( int, vector_t ); + int Write( int, float ); + int Write( int, const char * ); + int Write( int, int ); + int Write( CBlockMember * ); + + //Member push / pop functions + + int AddMember( CBlockMember * ); + CBlockMember *GetMember( int memberNum ); + + void *GetMemberData( int memberNum ); + + CBlock *Duplicate( void ); + + int GetBlockID( void ) const { return m_id; } //Get the ID for the block + int GetNumMembers( void ) const { return m_numMembers; } //Get the number of member in the block's list + + void SetFlags( unsigned char flags ) { m_flags = flags; } + void SetFlag( unsigned char flag ) { m_flags |= flag; } + + int HasFlag( unsigned char flag ) const { return ( m_flags & flag ); } + unsigned char GetFlags( void ) const { return m_flags; } + +protected: + + blockMember_v m_members; //List of all CBlockMembers owned by this list + int m_numMembers; //Number of members in m_members + int m_id; //ID of the block + unsigned char m_flags; +}; + +// CBlockStream + +class CBlockStream +{ +public: + + CBlockStream(); + ~CBlockStream(); + + int Init( void ); + + int Create( char * ); + int Free( void ); + + // Stream I/O functions + + int BlockAvailable( void ); + + int WriteBlock( CBlock * ); //Write the block out + int ReadBlock( CBlock * ); //Read the block in + + int Open( char *, long ); //Open a stream for reading / writing + +protected: + + unsigned GetUnsignedInteger( void ); + int GetInteger( void ); + + char GetChar( void ); + long GetLong( void ); + float GetFloat( void ); + + void StripExtension( const char *, char * ); //Utility function to strip away file extensions + + long m_fileSize; //Size of the file + FILE *m_fileHandle; //Global file handle of current I/O source + char m_fileName[MAX_FILENAME_LENGTH]; //Name of the current file + + char *m_stream; //Stream of data to be parsed + long m_streamPos; +}; + +#endif //__INTERPRETED_BLOCK_STREAM__ \ No newline at end of file diff --git a/utils/Ibize/ibize.cpp b/utils/Ibize/ibize.cpp new file mode 100644 index 0000000..43bc5cb --- /dev/null +++ b/utils/Ibize/ibize.cpp @@ -0,0 +1,196 @@ +// ICARUS: IBIze Interpreter +// +// -- jweier + +#include //For getch() +#include //For _findXXX + +#include "Tokenizer.h" +#include "BlockStream.h" +#include "Interpreter.h" + +#define IBIZE_VERSION 1.6f +#define SCRIPT_EXTENSION ".ICR" + +CInterpreter Interpreter; +CTokenizer Tokenizer; +CBlockStream BlockStream; + +/* +------------------------- +NULL_Error +------------------------- +*/ + +void NULL_Error( LPCSTR *error_msg ) +{ +} + +/* +------------------------- +InterpretFile +------------------------- +*/ + +// note different return type now, rather than true/false bool it returns CBlock# +1 of the bad command (if any), +// else 0 for all ok. Also returns -ve block numbers to indicate last block that was ok if an error occured between +// blocks (eg unexpected float at command identifier position) +// +int InterpretFile( char *filename ) +{ + printf("Parsing '%s'...\n\n", filename); + + //Create a block stream + if ( BlockStream.Create( filename ) == false ) + { + printf("ERROR: Unable to create file \"%s\"!\n", filename ); + return 1; + } + + //Create the Interpreted Block Instruction file + // + // (if error, return) + // + int iErrorBlock = Interpreter.Interpret( &Tokenizer, &BlockStream, filename ); + if (iErrorBlock!=0) + { + return iErrorBlock; + } + + BlockStream.Free(); + + return iErrorBlock; //true; +} + +/* +------------------------- +main +------------------------- +*/ + +int main(int argc, char* argv[]) +{ + struct _finddata_t fileinfo; + bool error_pause = false; + char *filename, error_msg[MAX_STRING_LENGTH], newfilename[MAX_FILENAME_LENGTH]; + int handle; + + //Init the tokenizer and pass the symbols we require + Tokenizer.Create( 0 ); + Tokenizer.SetSymbols( (keywordArray_t *) Interpreter.GetSymbols() ); + Tokenizer.SetErrorProc( (void (__cdecl *)(const char *)) NULL_Error ); + + //Init the block streaming class + BlockStream.Init(); + + //No script arguments, print the banner + if (argc < 2) + { + printf("\n\nIBIze v%1.2f -- jweier\n", IBIZE_VERSION ); + printf("ICARUS v%1.2f\n", ICARUS_VERSION ); + printf("Copyright (c) 1999, Raven Software\n"); + printf("------------------------------\n"); + printf("\nIBIze [script1.txt] [script2.txt] [script3.txt] ...\n\n"); + + return 0; + } + + int iErrorBlock = 0; + + //Interpret all files passed on the command line + for (int i=1; i variable_m; +typedef vector < variable_t * > variable_v; + +//CInterpreter + +class CInterpreter +{ +public: + + CInterpreter(); + ~CInterpreter(); + + int Interpret( CTokenizer *, CBlockStream *, char *filename=NULL ); //Main interpretation function + + int Match( int ); //Looks ahead to the next token to try and match it to the passed token, consumes token on success + int LookAhead( int ); //Looks ahead without consuming on success + + int FindSymbol( const char *, keywordArray_t * ); //Searches the symbol table for the given name. Returns the ID if found + + int GetAffect( void ); //Handles the affect() function + int GetWait( void ); //Handles the wait() function + int GetSet( void ); //Handles the set() function + int GetBroadcast( void ); //Handles the broadcast() function + int GetLoop( void ); //Handles the loop() function + int GetPrint( void ); //Handles the print() function + int GetUse( void ); //Handles the use() function + int GetFlush( void ); //Handles the flush() function + int GetRun( void ); //Handles the run() function + int GetKill( void ); //Handles the kill() function + int GetRemove( void ); //Handles the remove() function + int GetCamera( void ); //Handles the camera() function + int GetIf( void ); //Handles the if() conditional statement + int GetSound( void ); //Handles the sound() function + int GetMove( void ); //Handles the move() function + int GetRotate( void ); //Handles the rotate() function + int GetRem( void ); //Handles the rem() function + int GetTask( void ); + int GetDo( void ); + int GetElse( void ); + int GetDeclare( void ); + int GetFree( void ); + int GetDoWait( void ); + int GetSignal( void ); + int GetWaitSignal( void ); + int GetPlay( void ); + + int GetRandom( CBlock *block ); + int GetGet( CBlock *block ); //Heh + int GetTag( CBlock *block ); //Handles the tag() identifier + int GetVector( CBlock *block ); + + int GetNextType( void ); + + int GetType( char *get ); + + int GetAny( CBlock *block ); + int GetEvaluator( CBlock *block ); + int GetString( CBlock *); //Attempts to match and retrieve the value of a string token + int GetIdentifier( CBlock *get ); //Attempts to match and retrieve the value of an identifier token + int GetInteger( CBlock * ); //Attempts to match and retrieve the value of a int token + int GetFloat( CBlock * ); //Attempts to match and retrieve the value of a float token + int GetVariable( int type ); + + int GetID ( char * ); //Attempts to match and interpret an identifier + + keywordArray_t *GetSymbols( void ) { return (keywordArray_t *) &m_symbolKeywords; } //Returns the interpreter's symbol table + keywordArray_t *GetIDs( void ) { return (keywordArray_t *) &m_IDKeywords; } //Returns the interpreter's ID table + keywordArray_t *GetTypes( void ) { return (keywordArray_t *) &m_typeKeywords; } //Returns the interpreter's type table + +protected: + + void InitVars( void ); + void FreeVars( void ); + + variable_t *AddVar( const char *name, int type ); + variable_t *FindVar( const char *name ); + + const char *GetTokenName( int ); //Returns the name of a token + int Error( char *, ... ); //Prints an error message + + int MatchTag( void ); //Attempts to match to a tag identifier + int MatchGet( void ); //Attempts to match to a get identifier + int MatchRandom( void ); //Attempts to match to a random identifier + + CTokenizer *m_tokenizer; //Pointer to the tokenizer + CBlockStream *m_blockStream; //Pointer to the block stream + + variable_v m_vars; + variable_m m_varMap; + + string m_sCurrentLine; // used in IBIze error reporting for more clarity + string m_sCurrentFile; // full-pathed name of .TXT file (needed because of above, which affects parsestreams) + int m_iCurrentLine; // also needed now because of 'm_sCurrentLine' + int m_iBadCBlockNumber; // used for final app return code (NZ = err) + + static keywordArray_t m_symbolKeywords[]; //Symbols + static keywordArray_t m_IDKeywords[]; //Identifiers + static keywordArray_t m_typeKeywords[]; //Types + static keywordArray_t m_conditionalKeywords[]; //Conditional +}; + +#endif //__INTERPRETER__ \ No newline at end of file diff --git a/utils/Ibize/md4.c b/utils/Ibize/md4.c new file mode 100644 index 0000000..37de7f7 --- /dev/null +++ b/utils/Ibize/md4.c @@ -0,0 +1,277 @@ +/* GLOBAL.H - RSAREF types and constants */ + +#include + +/* POINTER defines a generic pointer type */ +typedef unsigned char *POINTER; + +/* UINT2 defines a two byte word */ +typedef unsigned short int UINT2; + +/* UINT4 defines a four byte word */ +typedef unsigned long int UINT4; + + +/* MD4.H - header file for MD4C.C */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. + +All rights reserved. + +License to copy and use this software is granted provided that it is identified as the “RSA Data Security, Inc. MD4 Message-Digest Algorithm” in all material mentioning or referencing this software or this function. +License is also granted to make and use derivative works provided that such works are identified as “derived from the RSA Data Security, Inc. MD4 Message-Digest Algorithm” in all material mentioning or referencing the derived work. +RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided “as is” without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this documentation and/or software. */ + +/* MD4 context. */ +typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD4_CTX; + +void MD4Init (MD4_CTX *); +void MD4Update (MD4_CTX *, unsigned char *, unsigned int); +void MD4Final (unsigned char [16], MD4_CTX *); + + + +/* MD4C.C - RSA Data Security, Inc., MD4 message-digest algorithm */ +/* Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved. + +License to copy and use this software is granted provided that it is identified as the +RSA Data Security, Inc. MD4 Message-Digest Algorithm + in all material mentioning or referencing this software or this function. +License is also granted to make and use derivative works provided that such works are identified as +derived from the RSA Data Security, Inc. MD4 Message-Digest Algorithm +in all material mentioning or referencing the derived work. +RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided +as is without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this documentation and/or software. */ + +/* Constants for MD4Transform routine. */ +#define S11 3 +#define S12 7 +#define S13 11 +#define S14 19 +#define S21 3 +#define S22 5 +#define S23 9 +#define S24 13 +#define S31 3 +#define S32 9 +#define S33 11 +#define S34 15 + +static void MD4Transform (UINT4 [4], unsigned char [64]); +static void Encode (unsigned char *, UINT4 *, unsigned int); +static void Decode (UINT4 *, unsigned char *, unsigned int); +static void MD4_memcpy (POINTER, POINTER, unsigned int); +static void MD4_memset (POINTER, int, unsigned int); + +static unsigned char PADDING[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 +}; + +/* F, G and H are basic MD4 functions. */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) + +/* ROTATE_LEFT rotates x left n bits. */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG and HH are transformations for rounds 1, 2 and 3 */ +/* Rotation is separate from addition to prevent recomputation */ +#define FF(a, b, c, d, x, s) {(a) += F ((b), (c), (d)) + (x); (a) = ROTATE_LEFT ((a), (s));} + +#define GG(a, b, c, d, x, s) {(a) += G ((b), (c), (d)) + (x) + (UINT4)0x5a827999; (a) = ROTATE_LEFT ((a), (s));} + +#define HH(a, b, c, d, x, s) {(a) += H ((b), (c), (d)) + (x) + (UINT4)0x6ed9eba1; (a) = \ +ROTATE_LEFT ((a), (s)); } + + +/* MD4 initialization. Begins an MD4 operation, writing a new context. */ +void MD4Init (MD4_CTX *context) +{ + context->count[0] = context->count[1] = 0; + +/* Load magic initialization constants.*/ +context->state[0] = 0x67452301; +context->state[1] = 0xefcdab89; +context->state[2] = 0x98badcfe; +context->state[3] = 0x10325476; +} + +/* MD4 block update operation. Continues an MD4 message-digest operation, processing another message block, and updating the context. */ +void MD4Update (MD4_CTX *context, unsigned char *input, unsigned int inputLen) +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3))< ((UINT4)inputLen << 3)) + context->count[1]++; + + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible.*/ + if (inputLen >= partLen) + { + memcpy((POINTER)&context->buffer[index], (POINTER)input, partLen); + MD4Transform (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD4Transform (context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + memcpy ((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i); +} + + +/* MD4 finalization. Ends an MD4 message-digest operation, writing the the message digest and zeroizing the context. */ +void MD4Final (unsigned char digest[16], MD4_CTX *context) +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64.*/ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD4Update (context, PADDING, padLen); + + /* Append length (before padding) */ + MD4Update (context, bits, 8); + + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information.*/ + memset ((POINTER)context, 0, sizeof (*context)); +} + + +/* MD4 basic transformation. Transforms state based on block. */ +static void MD4Transform (UINT4 state[4], unsigned char block[64]) +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + +/* Round 1 */ +FF (a, b, c, d, x[ 0], S11); /* 1 */ +FF (d, a, b, c, x[ 1], S12); /* 2 */ +FF (c, d, a, b, x[ 2], S13); /* 3 */ +FF (b, c, d, a, x[ 3], S14); /* 4 */ +FF (a, b, c, d, x[ 4], S11); /* 5 */ +FF (d, a, b, c, x[ 5], S12); /* 6 */ +FF (c, d, a, b, x[ 6], S13); /* 7 */ +FF (b, c, d, a, x[ 7], S14); /* 8 */ +FF (a, b, c, d, x[ 8], S11); /* 9 */ +FF (d, a, b, c, x[ 9], S12); /* 10 */ +FF (c, d, a, b, x[10], S13); /* 11 */ +FF (b, c, d, a, x[11], S14); /* 12 */ +FF (a, b, c, d, x[12], S11); /* 13 */ +FF (d, a, b, c, x[13], S12); /* 14 */ +FF (c, d, a, b, x[14], S13); /* 15 */ +FF (b, c, d, a, x[15], S14); /* 16 */ + +/* Round 2 */ +GG (a, b, c, d, x[ 0], S21); /* 17 */ +GG (d, a, b, c, x[ 4], S22); /* 18 */ +GG (c, d, a, b, x[ 8], S23); /* 19 */ +GG (b, c, d, a, x[12], S24); /* 20 */ +GG (a, b, c, d, x[ 1], S21); /* 21 */ +GG (d, a, b, c, x[ 5], S22); /* 22 */ +GG (c, d, a, b, x[ 9], S23); /* 23 */ +GG (b, c, d, a, x[13], S24); /* 24 */ +GG (a, b, c, d, x[ 2], S21); /* 25 */ +GG (d, a, b, c, x[ 6], S22); /* 26 */ +GG (c, d, a, b, x[10], S23); /* 27 */ +GG (b, c, d, a, x[14], S24); /* 28 */ +GG (a, b, c, d, x[ 3], S21); /* 29 */ +GG (d, a, b, c, x[ 7], S22); /* 30 */ +GG (c, d, a, b, x[11], S23); /* 31 */ +GG (b, c, d, a, x[15], S24); /* 32 */ + +/* Round 3 */ +HH (a, b, c, d, x[ 0], S31); /* 33 */ +HH (d, a, b, c, x[ 8], S32); /* 34 */ +HH (c, d, a, b, x[ 4], S33); /* 35 */ +HH (b, c, d, a, x[12], S34); /* 36 */ +HH (a, b, c, d, x[ 2], S31); /* 37 */ +HH (d, a, b, c, x[10], S32); /* 38 */ +HH (c, d, a, b, x[ 6], S33); /* 39 */ +HH (b, c, d, a, x[14], S34); /* 40 */ +HH (a, b, c, d, x[ 1], S31); /* 41 */ +HH (d, a, b, c, x[ 9], S32); /* 42 */ +HH (c, d, a, b, x[ 5], S33); /* 43 */ +HH (b, c, d, a, x[13], S34); /* 44 */ +HH (a, b, c, d, x[ 3], S31); /* 45 */ +HH (d, a, b, c, x[11], S32); /* 46 */ +HH (c, d, a, b, x[ 7], S33); /* 47 */ +HH (b, c, d, a, x[15], S34); /* 48 */ + +state[0] += a; +state[1] += b; +state[2] += c; +state[3] += d; + + /* Zeroize sensitive information.*/ + memset ((POINTER)x, 0, sizeof (x)); +} + + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is a multiple of 4. */ +static void Encode (unsigned char *output, UINT4 *input, unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is a multiple of 4. */ +static void Decode (UINT4 *output, unsigned char *input, unsigned int len) +{ +unsigned int i, j; + +for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} + +//=================================================================== + +unsigned Com_BlockChecksum (void *buffer, int length) +{ + int digest[4]; + unsigned val; + MD4_CTX ctx; + + MD4Init (&ctx); + MD4Update (&ctx, (unsigned char *)buffer, length); + MD4Final ( (unsigned char *)digest, &ctx); + + val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3]; + + return val; +} diff --git a/utils/Ibize/tokenizer.h b/utils/Ibize/tokenizer.h new file mode 100644 index 0000000..40ab834 --- /dev/null +++ b/utils/Ibize/tokenizer.h @@ -0,0 +1,597 @@ +// Tokenizer.h +// + +#ifndef __TOKENIZER_H +#define __TOKENIZER_H + +#pragma warning( disable : 4786 ) // identifier was truncated + +#include +#include +#include +using namespace std; + +#include + +typedef unsigned char byte; +typedef unsigned short word; + +#define MAX_STRING_LENGTH 256 +#define MAX_IDENTIFIER_LENGTH 128 + +#define TKF_IGNOREDIRECTIVES 0x00000001 // skip over lines starting with # +#define TKF_USES_EOL 0x00000002 // generate end of line tokens +#define TKF_NODIRECTIVES 0x00000004 // don't treat # in any special way +#define TKF_WANTUNDEFINED 0x00000008 // if token not found in symbols create undefined token +#define TKF_WIDEUNDEFINEDSYMBOLS 0x00000010 // when undefined token encountered, accumulate until space +#define TKF_RAWSYMBOLSONLY 0x00000020 +#define TKF_NUMERICIDENTIFIERSTART 0x00000040 +#define TKF_IGNOREKEYWORDS 0x00000080 +#define TKF_NOCASEKEYWORDS 0x00000100 +#define TKF_NOUNDERSCOREINIDENTIFIER 0x00000200 +#define TKF_NODASHINIDENTIFIER 0x00000400 +#define TKF_COMMENTTOKENS 0x00000800 + +enum +{ + TKERR_NONE, + TKERR_UNKNOWN, + TKERR_BUFFERCREATE, + TKERR_UNRECOGNIZEDSYMBOL, + TKERR_DUPLICATESYMBOL, + TKERR_STRINGLENGTHEXCEEDED, + TKERR_IDENTIFIERLENGTHEXCEEDED, + TKERR_EXPECTED_INTEGER, + TKERR_EXPECTED_IDENTIFIER, + TKERR_EXPECTED_STRING, + TKERR_EXPECTED_CHAR, + TKERR_EXPECTED_FLOAT, + TKERR_UNEXPECTED_TOKEN, + TKERR_INVALID_DIRECTIVE, + TKERR_INCLUDE_FILE_NOTFOUND, + TKERR_UNMATCHED_DIRECTIVE, + TKERR_USERERROR, +}; + +enum +{ + TK_EOF = -1, + TK_UNDEFINED, + TK_COMMENT, + TK_EOL, + TK_CHAR, + TK_STRING, + TK_INT, + TK_INTEGER = TK_INT, + TK_FLOAT, + TK_IDENTIFIER, + TK_USERDEF, +}; + +typedef struct +{ + char* m_keyword; + int m_tokenvalue; +} keywordArray_t; + +class lessstr +{ +public: + bool operator()(LPCTSTR str1, LPCTSTR str2) const {return (strcmp(str1, str2) < 0);}; +}; + +class CParseStream +{ +public: + CParseStream(); + ~CParseStream(); + static CParseStream* Create(); + virtual void Delete(); + virtual bool NextChar(byte& theByte); + virtual int GetCurLine(); + virtual void GetCurFilename(char** theBuff); + virtual long GetRemainingSize(); + + CParseStream* GetNext(); + void SetNext(CParseStream* next); + + virtual bool IsThisDefinition(void* theDefinition); + +protected: + virtual bool Init(); + + CParseStream* m_next; +}; + +class CToken +{ +public: + CToken(); + ~CToken(); + static CToken* Create(); + virtual void Delete(); + + virtual int GetType(); + CToken* GetNext(); + void SetNext(CToken* theToken); + virtual int GetIntValue(); + virtual LPCTSTR GetStringValue(); + virtual float GetFloatValue(); + +protected: + virtual void Init(); + + char* m_string; + CToken* m_next; +}; + +class CCharToken : public CToken +{ +public: + CCharToken(); + ~CCharToken(); + static CCharToken* Create(byte theByte); + virtual void Delete(); + + virtual int GetType(); + +protected: + virtual void Init(byte theByte); +}; + +class CStringToken : public CToken +{ +public: + CStringToken(); + ~CStringToken(); + static CStringToken* Create(LPCTSTR theString); + virtual void Delete(); + + virtual int GetType(); + +protected: + virtual void Init(LPCTSTR theString); +}; + +class CIntToken : public CToken +{ +public: + CIntToken(); + ~CIntToken(); + static CIntToken* Create(long value); + virtual void Delete(); + + virtual int GetType(); + virtual float GetFloatValue(); + virtual int GetIntValue(); + virtual LPCTSTR GetStringValue(); + +protected: + virtual void Init(long value); + + long m_value; +}; + +class CFloatToken : public CToken +{ +public: + CFloatToken(); + ~CFloatToken(); + static CFloatToken* Create(float value); + virtual void Delete(); + + virtual int GetType(); + virtual float GetFloatValue(); + virtual LPCTSTR GetStringValue(); + +protected: + virtual void Init(float value); + + float m_value; +}; + +class CIdentifierToken : public CToken +{ +public: + CIdentifierToken(); + ~CIdentifierToken(); + static CIdentifierToken* Create(LPCTSTR name); + virtual void Delete(); + + virtual int GetType(); + +protected: + virtual void Init(LPCTSTR name); +}; + +class CCommentToken : public CToken +{ +public: + CCommentToken(); + ~CCommentToken(); + static CCommentToken* Create(LPCTSTR name); + virtual void Delete(); + + virtual int GetType(); + +protected: + virtual void Init(LPCTSTR name); +}; + +class CUserToken : public CToken +{ +public: + CUserToken(); + ~CUserToken(); + static CUserToken* Create(int value, LPCTSTR string); + virtual void Delete(); + + virtual int GetType(); + +protected: + virtual void Init(int value, LPCTSTR string); + + int m_value; +}; + +class CUndefinedToken : public CToken +{ +public: + CUndefinedToken(); + ~CUndefinedToken(); + static CUndefinedToken* Create(LPCTSTR string); + virtual void Delete(); + + virtual int GetType(); + +protected: + virtual void Init(LPCTSTR string); +}; + +class CSymbol +{ +public: + CSymbol(); + virtual ~CSymbol(); + static CSymbol* Create(LPCTSTR symbolName); + virtual void Delete(); + + LPCTSTR GetName(); + +protected: + virtual void Init(LPCTSTR symbolName); + + char* m_symbolName; +}; + +typedef map symbolmap_t; + +class CDirectiveSymbol : public CSymbol +{ +public: + CDirectiveSymbol(); + ~CDirectiveSymbol(); + static CDirectiveSymbol* Create(LPCTSTR symbolName); + virtual void Delete(); + + void SetValue(LPCTSTR value); + LPCTSTR GetValue(); + +protected: + virtual void Init(LPCTSTR symbolName); + + char* m_value; +}; + +class CIntSymbol : public CSymbol +{ +public: + CIntSymbol(); + static CIntSymbol* Create(LPCTSTR symbolName, int value); + virtual void Delete(); + + int GetValue(); + +protected: + virtual void Init(LPCTSTR symbolName, int value); + + int m_value; +}; + +class CSymbolTable +{ +public: + CSymbolTable(); + ~CSymbolTable(); + static CSymbolTable* Create(); + void Delete(); + + bool AddSymbol(CSymbol* theSymbol); + CSymbol* FindSymbol(LPCTSTR symbolName); + CSymbol* ExtractSymbol(LPCTSTR symbolName); + void RemoveSymbol(LPCTSTR symbolName); + void DiscardSymbols(); + +protected: + void Init(); + symbolmap_t m_symbols; +}; + +class CSymbolLookup +{ +public: + CSymbolLookup(); + ~CSymbolLookup(); + static CSymbolLookup* Create(byte theByte); + virtual void Delete(); + CSymbolLookup* GetNext(); + void SetNext(CSymbolLookup* next); + void SetParent(CSymbolLookup* parent); + CSymbolLookup* GetParent(); + CSymbolLookup** GetChildAddress(); + CSymbolLookup* GetChild(); + void SetValue(int value); + int GetValue(); + byte GetByte(); + +protected: + void Init(byte theByte); + + CSymbolLookup* m_child; + CSymbolLookup* m_sibling; + CSymbolLookup* m_parent; + int m_value; + byte m_byte; +}; + +class CTokenizerState +{ +public: + CTokenizerState(); + ~CTokenizerState(); + static CTokenizerState* Create(bool skip); + virtual void Delete(); + CTokenizerState* GetNext(); + void SetNext(CTokenizerState* next); + virtual bool ProcessElse(); + bool Skipping(); + +protected: + void Init(bool skip); + + bool m_skip; + bool m_elseHit; + CTokenizerState* m_next; +}; + +class CTokenizerHolderState : public CTokenizerState +{ +public: + CTokenizerHolderState(); + ~CTokenizerHolderState(); + static CTokenizerHolderState* Create(); + virtual void Delete(); + virtual bool ProcessElse(); + +protected: + void Init(); +}; + +typedef void (*LPTokenizerErrorProc)(LPCTSTR errString); + +#ifdef USES_MODULES +class CTokenizer : public CModule +#else +class CTokenizer +#endif +{ +public: + CTokenizer(); + ~CTokenizer(); + static CTokenizer* Create(UINT dwFlags = 0); + virtual void Delete(); + virtual void Error(int theError); + virtual void Error(int theError, LPCTSTR errString); + virtual void Error(LPCTSTR errString, int theError = TKERR_UNKNOWN); + + CToken* GetToken(UINT onFlags = 0, UINT offFlags = 0); + CToken* GetToken(keywordArray_t* keywords, UINT onFlags, UINT offFlags); + void PutBackToken(CToken* theToken, bool commented = false, LPCTSTR addedChars = NULL, bool bIgnoreThisTokenType = false); + bool RequireToken(int tokenType); + void ScanUntilToken(int tokenType); + void SkipToLineEnd(); + CToken* GetToEndOfLine(int tokenType = TK_IDENTIFIER); + + keywordArray_t* SetKeywords(keywordArray_t* theKeywords); + void SetSymbols(keywordArray_t* theSymbols); + void SetAdditionalErrors(keywordArray_t* theErrors); + void SetErrorProc(LPTokenizerErrorProc errorProc); + void AddParseStream(byte* data, long datasize); + bool AddParseFile(LPCTSTR filename); + COLORREF ParseRGB(); + long GetRemainingSize(); + + UINT GetFlags(); + void SetFlags(UINT flags); + + void GetCurFilename(char** filename); + int GetCurLine(); + + LPCTSTR LookupToken(int tokenID, keywordArray_t* theTable = NULL); + +protected: + void SetError(int theError, LPCTSTR errString); + virtual void Init(UINT dwFlags = 0); + CToken* FetchToken(); + bool AddDefineSymbol(CDirectiveSymbol* definesymbol); + bool NextChar(byte& theByte); + byte Escapement(); + void InsertSymbol(LPCTSTR theSymbol, int theValue); + void PutBackChar(byte theByte, int curLine = 0, LPCTSTR filename = NULL); + CToken* TokenFromName(LPCTSTR name); + CToken* HandleDirective(); + CToken* HandleSlash(); + CToken* HandleString(); + CToken* HandleQuote(); + CToken* HandleIdentifier(byte theByte); + CToken* HandleNumeric(byte theByte); + CToken* HandleFloat(bool thesign = false, long value = 0); + CToken* HandleDecimal(bool thesign = false); + CToken* HandleSymbol(byte theByte); + CToken* HandleHex(bool thesize); + CToken* HandleOctal(bool thesize); + int DirectiveFromName(LPCTSTR name); + + CParseStream* m_curParseStream; + keywordArray_t* m_keywords; + keywordArray_t* m_symbols; + keywordArray_t* m_errors; + CSymbolLookup* m_symbolLookup; + CToken* m_nextToken; + CSymbolTable m_defines; + CTokenizerState* m_state; + UINT m_flags; + LPTokenizerErrorProc m_errorProc; + + static keywordArray_t errorMessages[]; + static keywordArray_t directiveKeywords[]; +}; + +class CKeywordTable +{ +public: + CKeywordTable(CTokenizer* tokenizer, keywordArray_t* keywords); + ~CKeywordTable(); + +protected: + CTokenizer* m_tokenizer; + keywordArray_t* m_holdKeywords; +}; + +class CParsePutBack : public CParseStream +{ +public: + CParsePutBack(); + ~CParsePutBack(); + static CParsePutBack* Create(byte theByte, int curLine, LPCTSTR filename); + virtual void Delete(); + virtual bool NextChar(byte& theByte); + virtual int GetCurLine(); + virtual void GetCurFilename(char** theBuff); + virtual long GetRemainingSize(); + +protected: + virtual void Init(byte theByte, int curLine, LPCTSTR filename); + + byte m_byte; + bool m_consumed; + int m_curLine; + char* m_curFile; +}; + +class CParseMemory : public CParseStream +{ +public: + CParseMemory(); + ~CParseMemory(); + static CParseMemory* Create(byte* data, long datasize); + virtual void Delete(); + virtual bool NextChar(byte& theByte); + virtual int GetCurLine(); + virtual void GetCurFilename(char** theBuff); + virtual long GetRemainingSize(); + +protected: + virtual void Init(byte* data, long datasize); + + byte* m_data; + int m_curLine; + long m_curPos; + long m_datasize; + long m_offset; +}; + +class CParseBlock : public CParseMemory +{ +public: + CParseBlock(); + ~CParseBlock(); + static CParseBlock* Create(byte* data, long datasize); + virtual void Delete(); + +protected: + virtual void Init(byte* data, long datasize); +}; + +class CParseToken : public CParseStream +{ +public: + CParseToken(); + ~CParseToken(); + static CParseToken* Create(CToken* token); + virtual void Delete(); + virtual bool NextChar(byte& theByte); + virtual int GetCurLine(); + virtual void GetCurFilename(char** theBuff); + virtual long GetRemainingSize(); + +protected: + virtual void Init(CToken* token); + + byte* m_data; + int m_curLine; + long m_curPos; + long m_datasize; + long m_offset; +}; + +class CParseDefine : public CParseMemory +{ +public: + CParseDefine(); + ~CParseDefine(); + static CParseDefine* Create(CDirectiveSymbol* definesymbol); + virtual void Delete(); + virtual bool IsThisDefinition(void* theDefinition); + +protected: + virtual void Init(CDirectiveSymbol* definesymbol); + + CDirectiveSymbol* m_defineSymbol; +}; + +class CParseFile : public CParseStream +{ +public: + CParseFile(); + ~CParseFile(); + static CParseFile* Create(); + static CParseFile* Create(LPCTSTR filename, CTokenizer* tokenizer); +// static CParseFile* Create(CFile* file, CTokenizer* tokenizer); + virtual void Delete(); + virtual int GetCurLine(); + virtual void GetCurFilename(char** theBuff); + virtual long GetRemainingSize(); + + virtual bool NextChar(byte& theByte); + +protected: + virtual bool Init(); + virtual bool Init(LPCTSTR filename, CTokenizer* tokenizer); +// virtual void Init(CFile* file, CTokenizer* tokenizer); + DWORD GetFileSize(); + void Read(void* buff, UINT buffsize); + +// CFile* m_file; + HANDLE m_fileHandle; + char* m_fileName; + int m_curLine; + int m_curPos; + byte* m_buff; + DWORD m_curByte; + DWORD m_filesize; + bool m_ownsFile; +}; + + +#endif//__TOKENIZER_H \ No newline at end of file diff --git a/utils/Libs/cmdlib.h b/utils/Libs/cmdlib.h new file mode 100644 index 0000000..c5bb7c6 --- /dev/null +++ b/utils/Libs/cmdlib.h @@ -0,0 +1,76 @@ +// +// start of shared cmdlib stuff +// + +#ifndef __CMDLIB__ +#define __CMDLIB__ + +#include +#include +#include +#include +#include +#include +#include + +#ifndef __BYTEBOOL__ +#define __BYTEBOOL__ + +#ifndef __cplusplus + typedef enum {false, true} boolean; +#else + typedef unsigned char boolean; +#endif + + +typedef unsigned char byte; +//typedef unsigned char byte; +#endif + +FILE *SafeOpenWrite (const char *filename); +FILE *SafeOpenRead (const char *filename); +void SafeRead (FILE *f, void *buffer, int count); +void SafeWrite (FILE *f, void *buffer, int count); +int LoadFile (const char *filename, void **bufferptr); +int LoadFileNoCrash (const char *filename, void **bufferptr); +void SaveFile (const char *filename, void *buffer, int count); +void DefaultExtension (char *path, char *extension); +void DefaultPath (char *path, char *basepath); +void StripFilename (char *path); +void StripExtension (char *path); +void ExtractFilePath (const char *path, char *dest); +void ExtractFileName (const char *path, char *dest); +void ExtractFileBase (const char *path, char *dest); +void ExtractFileExtension (const char *path, char *dest); +short BigShort (short l); +short LittleShort (short l); +int BigLong (int l); +int LittleLong (int l); +float BigFloat (float l); +float LittleFloat (float l); +void *qmalloc (size_t size); +void* qblockmalloc(size_t nSize); + + + +// error and printf functions +typedef void (PFN_ERR)(const char *pFormat, ...); +typedef void (PFN_PRINTF)(const char *pFormat, ...); +typedef void (PFN_ERR_NUM)(int nNum, const char *pFormat, ...); +typedef void (PFN_PRINTF_NUM)(int nNum, const char *pFormat, ...); + +void Error(const char *pFormat, ...); +void Printf(const char *pFormat, ...); +void ErrorNum(int n, const char *pFormat, ...); +void PrintfNum(int n, const char *pFormat, ...); + +void SetErrorHandler(PFN_ERR pe); +void SetPrintfHandler(PFN_PRINTF pe); +void SetErrorHandlerNum(PFN_ERR_NUM pe); +void SetPrintfHandlerNum(PFN_PRINTF_NUM pe); +void ConvertDOSToUnixName( char *dst, const char *src ); +char* StrDup(char* pStr); +char* StrDup(const char* pStr); + + +#endif \ No newline at end of file diff --git a/utils/Libs/cmdlib/cmdlib.cpp b/utils/Libs/cmdlib/cmdlib.cpp new file mode 100644 index 0000000..0e21164 --- /dev/null +++ b/utils/Libs/cmdlib/cmdlib.cpp @@ -0,0 +1,550 @@ +// +// start of shared cmdlib stuff +// + + +#include "cmdlib.h" +#include "windows.h" + +#define PATHSEPERATOR '/' + +// rad additions +// 11.29.99 +PFN_ERR *g_pfnError = NULL; +PFN_PRINTF *g_pfnPrintf = NULL; +PFN_ERR_NUM *g_pfnErrorNum = NULL; +PFN_PRINTF_NUM *g_pfnPrintfNum = NULL; + + +void Error(const char *pFormat, ...) +{ + if (g_pfnError) + { + va_list arg_ptr; + va_start(arg_ptr, pFormat); + g_pfnError(pFormat, arg_ptr); + va_end(arg_ptr); + } +} + +void Printf(const char *pFormat, ...) +{ + if (g_pfnPrintf) + { + va_list arg_ptr; + va_start(arg_ptr, pFormat); + g_pfnPrintf(pFormat, arg_ptr); + va_end(arg_ptr); + } +} + +void ErrorNum(int nErr, const char *pFormat, ...) +{ + if (g_pfnErrorNum) + { + va_list arg_ptr; + va_start(arg_ptr, pFormat); + g_pfnErrorNum(nErr, pFormat, arg_ptr); + va_end(arg_ptr); + } +} + +void PrintfNum(int nErr, const char *pFormat, ...) +{ + if (g_pfnPrintfNum) + { + va_list arg_ptr; + va_start(arg_ptr, pFormat); + g_pfnPrintfNum(nErr, pFormat, arg_ptr); + va_end(arg_ptr); + } +} + + + +void SetErrorHandler(PFN_ERR pe) +{ + g_pfnError = pe; +} + +void SetPrintfHandler(PFN_PRINTF pe) +{ + g_pfnPrintf = pe; +} + +void SetErrorHandlerNum(PFN_ERR_NUM pe) +{ + g_pfnErrorNum = pe; +} + +void SetPrintfHandler(PFN_PRINTF_NUM pe) +{ + g_pfnPrintfNum = pe; +} + + + +// rad end + +#define MEM_BLOCKSIZE 4096 +void* qblockmalloc(size_t nSize) +{ + void *b; + // round up to threshold + int nAllocSize = nSize % MEM_BLOCKSIZE; + if ( nAllocSize > 0) + { + nSize += MEM_BLOCKSIZE - nAllocSize; + } + b = malloc(nSize + 1); + memset (b, 0, nSize); + return b; +} + +void* qmalloc (size_t nSize) +{ + void *b; + b = malloc(nSize + 1); + memset (b, 0, nSize); + return b; +} + +/* +================ +Q_filelength +================ +*/ +int Q_filelength (FILE *f) +{ + int pos; + int end; + + pos = ftell (f); + fseek (f, 0, SEEK_END); + end = ftell (f); + fseek (f, pos, SEEK_SET); + + return end; +} + + +// FIXME: need error handler +FILE *SafeOpenWrite (const char *filename) +{ + FILE *f; + + f = fopen(filename, "wb"); + + if (!f) + { + Error ("Error opening %s: %s",filename,strerror(errno)); + } + + return f; +} + +FILE *SafeOpenRead (const char *filename) +{ + FILE *f; + + f = fopen(filename, "rb"); + + if (!f) + { + Error ("Error opening %s: %s",filename,strerror(errno)); + } + + return f; +} + + +void SafeRead (FILE *f, void *buffer, int count) +{ + if ( (int)fread (buffer, 1, count, f) != count) + Error ("File read failure"); +} + + +void SafeWrite (FILE *f, void *buffer, int count) +{ + if ( (int)fwrite (buffer, 1, count, f) != count) + Error ("File read failure"); +} + + + +/* +============== +LoadFile +============== +*/ +int LoadFile (const char *filename, void **bufferptr) +{ + FILE *f; + int length; + void *buffer; + + *bufferptr = NULL; + + if (filename == NULL || strlen(filename) == 0) + { + return -1; + } + + f = fopen (filename, "rb"); + if (!f) + { + return -1; + } + length = Q_filelength (f); + buffer = qblockmalloc (length+1); + ((char *)buffer)[length] = 0; + SafeRead (f, buffer, length); + fclose (f); + + *bufferptr = buffer; + return length; +} + + +/* +============== +LoadFileNoCrash + +returns -1 length if not present +============== +*/ +int LoadFileNoCrash (const char *filename, void **bufferptr) +{ + FILE *f; + int length; + void *buffer; + + f = fopen (filename, "rb"); + if (!f) + return -1; + length = Q_filelength (f); + buffer = qmalloc (length+1); + ((char *)buffer)[length] = 0; + SafeRead (f, buffer, length); + fclose (f); + + *bufferptr = buffer; + return length; +} + + +/* +============== +SaveFile +============== +*/ +void SaveFile (const char *filename, void *buffer, int count) +{ + FILE *f; + + f = SafeOpenWrite (filename); + SafeWrite (f, buffer, count); + fclose (f); +} + + + +void DefaultExtension (char *path, char *extension) +{ + char *src; +// +// if path doesn't have a .EXT, append extension +// (extension should include the .) +// + src = path + strlen(path) - 1; + + while (*src != PATHSEPERATOR && src != path) + { + if (*src == '.') + return; // it has an extension + src--; + } + + strcat (path, extension); +} + + +void DefaultPath (char *path, char *basepath) +{ + char temp[128]; + + if (path[0] == PATHSEPERATOR) + return; // absolute path location + strcpy (temp,path); + strcpy (path,basepath); + strcat (path,temp); +} + + +void StripFilename (char *path) +{ + int length; + + length = strlen(path)-1; + while (length > 0 && path[length] != PATHSEPERATOR) + length--; + path[length] = 0; +} + +void StripExtension (char *path) +{ + int length; + + length = strlen(path)-1; + while (length > 0 && path[length] != '.') + { + length--; + if (path[length] == '/') + return; // no extension + } + if (length) + path[length] = 0; +} + + +/* +==================== +Extract file parts +==================== +*/ +void ExtractFilePath (const char *path, char *dest) +{ + const char *src; + + src = path + strlen(path) - 1; + +// +// back up until a \ or the start +// + while (src != path && *(src-1) != PATHSEPERATOR) + src--; + + memcpy (dest, path, src-path); + dest[src-path] = 0; +} + +void ExtractFileName (const char *path, char *dest) +{ + const char *src; + + src = path + strlen(path) - 1; + +// +// back up until a \ or the start +// + while (src != path && *(src-1) != '/' + && *(src-1) != '\\' ) + src--; + + while (*src) + { + *dest++ = *src++; + } + *dest = 0; +} + +void ExtractFileBase (const char *path, char *dest) +{ + const char *src; + + src = path + strlen(path) - 1; + +// +// back up until a \ or the start +// + while (src != path && *(src-1) != '/' + && *(src-1) != '\\' ) + src--; + + while (*src && *src != '.') + { + *dest++ = *src++; + } + *dest = 0; +} + +void ExtractFileExtension (const char *path, char *dest) +{ + const char *src; + + src = path + strlen(path) - 1; + +// +// back up until a . or the start +// + while (src != path && *(src-1) != '.') + src--; + if (src == path) + { + *dest = 0; // no extension + return; + } + + strcpy (dest,src); +} + + +void ConvertDOSToUnixName( char *dst, const char *src ) +{ + while ( *src ) + { + if ( *src == '\\' ) + *dst = '/'; + else + *dst = *src; + dst++; src++; + } + *dst = 0; +} + + +char* StrDup(char* pStr) +{ + if (pStr) + { + return strcpy(new char[strlen(pStr)+1], pStr); + } + return NULL; +} + +char* StrDup(const char* pStr) +{ + if (pStr) + { + return strcpy(new char[strlen(pStr)+1], pStr); + } + return NULL; +} + + +/* +============================================================================ + + BYTE ORDER FUNCTIONS + +============================================================================ +*/ + +#ifdef _SGI_SOURCE +#define __BIG_ENDIAN__ +#endif + +#ifdef __BIG_ENDIAN__ + +short LittleShort (short l) +{ + byte b1,b2; + + b1 = l&255; + b2 = (l>>8)&255; + + return (b1<<8) + b2; +} + +short BigShort (short l) +{ + return l; +} + + +int LittleLong (int l) +{ + byte b1,b2,b3,b4; + + b1 = l&255; + b2 = (l>>8)&255; + b3 = (l>>16)&255; + b4 = (l>>24)&255; + + return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; +} + +int BigLong (int l) +{ + return l; +} + + +float LittleFloat (float l) +{ + union {byte b[4]; float f;} in, out; + + in.f = l; + out.b[0] = in.b[3]; + out.b[1] = in.b[2]; + out.b[2] = in.b[1]; + out.b[3] = in.b[0]; + + return out.f; +} + +float BigFloat (float l) +{ + return l; +} + + +#else + + +short BigShort (short l) +{ + byte b1,b2; + + b1 = l&255; + b2 = (l>>8)&255; + + return (b1<<8) + b2; +} + +short LittleShort (short l) +{ + return l; +} + + +int BigLong (int l) +{ + byte b1,b2,b3,b4; + + b1 = l&255; + b2 = (l>>8)&255; + b3 = (l>>16)&255; + b4 = (l>>24)&255; + + return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; +} + +int LittleLong (int l) +{ + return l; +} + +float BigFloat (float l) +{ + union {byte b[4]; float f;} in, out; + + in.f = l; + out.b[0] = in.b[3]; + out.b[1] = in.b[2]; + out.b[2] = in.b[1]; + out.b[3] = in.b[0]; + + return out.f; +} + +float LittleFloat (float l) +{ + return l; +} + + + +#endif + diff --git a/utils/Libs/cmdlib/cmdlib.dsp b/utils/Libs/cmdlib/cmdlib.dsp new file mode 100644 index 0000000..9f24bd5 --- /dev/null +++ b/utils/Libs/cmdlib/cmdlib.dsp @@ -0,0 +1,100 @@ +# Microsoft Developer Studio Project File - Name="cmdlib" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=cmdlib - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "cmdlib.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "cmdlib.mak" CFG="cmdlib - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "cmdlib - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "cmdlib - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName ""$/Utils/Libs/cmdlib", SHNAAAAA" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "cmdlib - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GR /GX /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"..\cmdlib.lib" + +!ELSEIF "$(CFG)" == "cmdlib - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GR /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"..\cmdlibd.lib" + +!ENDIF + +# Begin Target + +# Name "cmdlib - Win32 Release" +# Name "cmdlib - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\cmdlib.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\cmdlib.h +# End Source File +# End Group +# End Target +# End Project diff --git a/utils/Libs/cmdlib/cmdlib.dsw b/utils/Libs/cmdlib/cmdlib.dsw new file mode 100644 index 0000000..e796b89 --- /dev/null +++ b/utils/Libs/cmdlib/cmdlib.dsw @@ -0,0 +1,37 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "cmdlib"=.\cmdlib.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/Utils/Libs/cmdlib", SHNAAAAA + . + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ + begin source code control + "$/Utils/Libs/cmdlib", SHNAAAAA + . + end source code control +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/CODE-mp/cgame/cg_media.h b/utils/Libs/cmdlib/str.h similarity index 100% rename from CODE-mp/cgame/cg_media.h rename to utils/Libs/cmdlib/str.h diff --git a/utils/Libs/jpeg6/jchuff.h b/utils/Libs/jpeg6/jchuff.h new file mode 100644 index 0000000..0a81d54 --- /dev/null +++ b/utils/Libs/jpeg6/jchuff.h @@ -0,0 +1,34 @@ +/* + * jchuff.h + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains declarations for Huffman entropy encoding routines + * that are shared between the sequential encoder (jchuff.c) and the + * progressive encoder (jcphuff.c). No other modules need to see these. + */ + +/* Derived data constructed for each Huffman table */ + +typedef struct { + unsigned int ehufco[256]; /* code for each symbol */ + char ehufsi[256]; /* length of code for each symbol */ + /* If no code has been allocated for a symbol S, ehufsi[S] contains 0 */ +} c_derived_tbl; + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_make_c_derived_tbl jMkCDerived +#define jpeg_gen_optimal_table jGenOptTbl +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + +/* Expand a Huffman table definition into the derived format */ +EXTERN void jpeg_make_c_derived_tbl JPP((j_compress_ptr cinfo, + JHUFF_TBL * htbl, c_derived_tbl ** pdtbl)); + +/* Generate an optimal table definition given the specified counts */ +EXTERN void jpeg_gen_optimal_table JPP((j_compress_ptr cinfo, + JHUFF_TBL * htbl, long freq[])); diff --git a/utils/Libs/jpeg6/jcomapi.cpp b/utils/Libs/jpeg6/jcomapi.cpp new file mode 100644 index 0000000..8f417c0 --- /dev/null +++ b/utils/Libs/jpeg6/jcomapi.cpp @@ -0,0 +1,94 @@ +/* + * jcomapi.c + * + * Copyright (C) 1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains application interface routines that are used for both + * compression and decompression. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * Abort processing of a JPEG compression or decompression operation, + * but don't destroy the object itself. + * + * For this, we merely clean up all the nonpermanent memory pools. + * Note that temp files (virtual arrays) are not allowed to belong to + * the permanent pool, so we will be able to close all temp files here. + * Closing a data source or destination, if necessary, is the application's + * responsibility. + */ + +GLOBAL void +jpeg_abort (j_common_ptr cinfo) +{ + int pool; + + /* Releasing pools in reverse order might help avoid fragmentation + * with some (brain-damaged) malloc libraries. + */ + for (pool = JPOOL_NUMPOOLS-1; pool > JPOOL_PERMANENT; pool--) { + (*cinfo->mem->free_pool) (cinfo, pool); + } + + /* Reset overall state for possible reuse of object */ + cinfo->global_state = (cinfo->is_decompressor ? DSTATE_START : CSTATE_START); +} + + +/* + * Destruction of a JPEG object. + * + * Everything gets deallocated except the master jpeg_compress_struct itself + * and the error manager struct. Both of these are supplied by the application + * and must be freed, if necessary, by the application. (Often they are on + * the stack and so don't need to be freed anyway.) + * Closing a data source or destination, if necessary, is the application's + * responsibility. + */ + +GLOBAL void +jpeg_destroy (j_common_ptr cinfo) +{ + /* We need only tell the memory manager to release everything. */ + /* NB: mem pointer is NULL if memory mgr failed to initialize. */ + if (cinfo->mem != NULL) + (*cinfo->mem->self_destruct) (cinfo); + cinfo->mem = NULL; /* be safe if jpeg_destroy is called twice */ + cinfo->global_state = 0; /* mark it destroyed */ +} + + +/* + * Convenience routines for allocating quantization and Huffman tables. + * (Would jutils.c be a more reasonable place to put these?) + */ + +GLOBAL JQUANT_TBL * +jpeg_alloc_quant_table (j_common_ptr cinfo) +{ + JQUANT_TBL *tbl; + + tbl = (JQUANT_TBL *) + (*cinfo->mem->alloc_small) (cinfo, JPOOL_PERMANENT, SIZEOF(JQUANT_TBL)); + tbl->sent_table = FALSE; /* make sure this is false in any new table */ + return tbl; +} + + +GLOBAL JHUFF_TBL * +jpeg_alloc_huff_table (j_common_ptr cinfo) +{ + JHUFF_TBL *tbl; + + tbl = (JHUFF_TBL *) + (*cinfo->mem->alloc_small) (cinfo, JPOOL_PERMANENT, SIZEOF(JHUFF_TBL)); + tbl->sent_table = FALSE; /* make sure this is false in any new table */ + return tbl; +} diff --git a/utils/Libs/jpeg6/jconfig.h b/utils/Libs/jpeg6/jconfig.h new file mode 100644 index 0000000..187ecfc --- /dev/null +++ b/utils/Libs/jpeg6/jconfig.h @@ -0,0 +1,41 @@ +/* jconfig.wat --- jconfig.h for Watcom C/C++ on MS-DOS or OS/2. */ +/* see jconfig.doc for explanations */ + +#define HAVE_PROTOTYPES +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT +/* #define void char */ +/* #define const */ +#define CHAR_IS_UNSIGNED +#define HAVE_STDDEF_H +#define HAVE_STDLIB_H +#undef NEED_BSD_STRINGS +#undef NEED_SYS_TYPES_H +#undef NEED_FAR_POINTERS /* Watcom uses flat 32-bit addressing */ +#undef NEED_SHORT_EXTERNAL_NAMES +#undef INCOMPLETE_TYPES_BROKEN + +#define JDCT_DEFAULT JDCT_FLOAT +#define JDCT_FASTEST JDCT_FLOAT + +#ifdef JPEG_INTERNALS + +#undef RIGHT_SHIFT_IS_UNSIGNED + +#endif /* JPEG_INTERNALS */ + +#ifdef JPEG_CJPEG_DJPEG + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#define TARGA_SUPPORTED /* Targa image file format */ + +#undef TWO_FILE_COMMANDLINE /* optional */ +#define USE_SETMODE /* Needed to make one-file style work in Watcom */ +#undef NEED_SIGNAL_CATCHER /* Define this if you use jmemname.c */ +#undef DONT_USE_B_MODE +#undef PROGRESS_REPORT /* optional */ + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/utils/Libs/jpeg6/jdapimin.cpp b/utils/Libs/jpeg6/jdapimin.cpp new file mode 100644 index 0000000..1bae6a2 --- /dev/null +++ b/utils/Libs/jpeg6/jdapimin.cpp @@ -0,0 +1,400 @@ +/* + * jdapimin.c + * + * Copyright (C) 1994-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains application interface code for the decompression half + * of the JPEG library. These are the "minimum" API routines that may be + * needed in either the normal full-decompression case or the + * transcoding-only case. + * + * Most of the routines intended to be called directly by an application + * are in this file or in jdapistd.c. But also see jcomapi.c for routines + * shared by compression and decompression, and jdtrans.c for the transcoding + * case. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * Initialization of a JPEG decompression object. + * The error manager must already be set up (in case memory manager fails). + */ + +GLOBAL void +jpeg_create_decompress (j_decompress_ptr cinfo) +{ + int i; + + /* For debugging purposes, zero the whole master structure. + * But error manager pointer is already there, so save and restore it. + */ + { + struct jpeg_error_mgr * err = cinfo->err; + i = sizeof(struct jpeg_decompress_struct); + i = SIZEOF(struct jpeg_decompress_struct); + MEMZERO(cinfo, SIZEOF(struct jpeg_decompress_struct)); + cinfo->err = err; + } + cinfo->is_decompressor = TRUE; + + /* Initialize a memory manager instance for this object */ + jinit_memory_mgr((j_common_ptr) cinfo); + + /* Zero out pointers to permanent structures. */ + cinfo->progress = NULL; + cinfo->src = NULL; + + for (i = 0; i < NUM_QUANT_TBLS; i++) + cinfo->quant_tbl_ptrs[i] = NULL; + + for (i = 0; i < NUM_HUFF_TBLS; i++) { + cinfo->dc_huff_tbl_ptrs[i] = NULL; + cinfo->ac_huff_tbl_ptrs[i] = NULL; + } + + /* Initialize marker processor so application can override methods + * for COM, APPn markers before calling jpeg_read_header. + */ + jinit_marker_reader(cinfo); + + /* And initialize the overall input controller. */ + jinit_input_controller(cinfo); + + /* OK, I'm ready */ + cinfo->global_state = DSTATE_START; +} + + +/* + * Destruction of a JPEG decompression object + */ + +GLOBAL void +jpeg_destroy_decompress (j_decompress_ptr cinfo) +{ + jpeg_destroy((j_common_ptr) cinfo); /* use common routine */ +} + + +/* + * Abort processing of a JPEG decompression operation, + * but don't destroy the object itself. + */ + +GLOBAL void +jpeg_abort_decompress (j_decompress_ptr cinfo) +{ + jpeg_abort((j_common_ptr) cinfo); /* use common routine */ +} + + +/* + * Install a special processing method for COM or APPn markers. + */ + +GLOBAL void +jpeg_set_marker_processor (j_decompress_ptr cinfo, int marker_code, + jpeg_marker_parser_method routine) +{ + if (marker_code == JPEG_COM) + cinfo->marker->process_COM = routine; + else if (marker_code >= JPEG_APP0 && marker_code <= JPEG_APP0+15) + cinfo->marker->process_APPn[marker_code-JPEG_APP0] = routine; + else + ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, marker_code); +} + + +/* + * Set default decompression parameters. + */ + +LOCAL void +default_decompress_parms (j_decompress_ptr cinfo) +{ + /* Guess the input colorspace, and set output colorspace accordingly. */ + /* (Wish JPEG committee had provided a real way to specify this...) */ + /* Note application may override our guesses. */ + switch (cinfo->num_components) { + case 1: + cinfo->jpeg_color_space = JCS_GRAYSCALE; + cinfo->out_color_space = JCS_GRAYSCALE; + break; + + case 3: + if (cinfo->saw_JFIF_marker) { + cinfo->jpeg_color_space = JCS_YCbCr; /* JFIF implies YCbCr */ + } else if (cinfo->saw_Adobe_marker) { + switch (cinfo->Adobe_transform) { + case 0: + cinfo->jpeg_color_space = JCS_RGB; + break; + case 1: + cinfo->jpeg_color_space = JCS_YCbCr; + break; + default: + WARNMS1(cinfo, JWRN_ADOBE_XFORM, cinfo->Adobe_transform); + cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */ + break; + } + } else { + /* Saw no special markers, try to guess from the component IDs */ + int cid0 = cinfo->comp_info[0].component_id; + int cid1 = cinfo->comp_info[1].component_id; + int cid2 = cinfo->comp_info[2].component_id; + + if (cid0 == 1 && cid1 == 2 && cid2 == 3) + cinfo->jpeg_color_space = JCS_YCbCr; /* assume JFIF w/out marker */ + else if (cid0 == 82 && cid1 == 71 && cid2 == 66) + cinfo->jpeg_color_space = JCS_RGB; /* ASCII 'R', 'G', 'B' */ + else { + TRACEMS3(cinfo, 1, JTRC_UNKNOWN_IDS, cid0, cid1, cid2); + cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */ + } + } + /* Always guess RGB is proper output colorspace. */ + cinfo->out_color_space = JCS_RGB; + break; + + case 4: + if (cinfo->saw_Adobe_marker) { + switch (cinfo->Adobe_transform) { + case 0: + cinfo->jpeg_color_space = JCS_CMYK; + break; + case 2: + cinfo->jpeg_color_space = JCS_YCCK; + break; + default: + WARNMS1(cinfo, JWRN_ADOBE_XFORM, cinfo->Adobe_transform); + cinfo->jpeg_color_space = JCS_YCCK; /* assume it's YCCK */ + break; + } + } else { + /* No special markers, assume straight CMYK. */ + cinfo->jpeg_color_space = JCS_CMYK; + } + cinfo->out_color_space = JCS_CMYK; + break; + + default: + cinfo->jpeg_color_space = JCS_UNKNOWN; + cinfo->out_color_space = JCS_UNKNOWN; + break; + } + + /* Set defaults for other decompression parameters. */ + cinfo->scale_num = 1; /* 1:1 scaling */ + cinfo->scale_denom = 1; + cinfo->output_gamma = 1.0; + cinfo->buffered_image = FALSE; + cinfo->raw_data_out = FALSE; + cinfo->dct_method = JDCT_DEFAULT; + cinfo->do_fancy_upsampling = TRUE; + cinfo->do_block_smoothing = TRUE; + cinfo->quantize_colors = FALSE; + /* We set these in case application only sets quantize_colors. */ + cinfo->dither_mode = JDITHER_FS; +#ifdef QUANT_2PASS_SUPPORTED + cinfo->two_pass_quantize = TRUE; +#else + cinfo->two_pass_quantize = FALSE; +#endif + cinfo->desired_number_of_colors = 256; + cinfo->colormap = NULL; + /* Initialize for no mode change in buffered-image mode. */ + cinfo->enable_1pass_quant = FALSE; + cinfo->enable_external_quant = FALSE; + cinfo->enable_2pass_quant = FALSE; +} + + +/* + * Decompression startup: read start of JPEG datastream to see what's there. + * Need only initialize JPEG object and supply a data source before calling. + * + * This routine will read as far as the first SOS marker (ie, actual start of + * compressed data), and will save all tables and parameters in the JPEG + * object. It will also initialize the decompression parameters to default + * values, and finally return JPEG_HEADER_OK. On return, the application may + * adjust the decompression parameters and then call jpeg_start_decompress. + * (Or, if the application only wanted to determine the image parameters, + * the data need not be decompressed. In that case, call jpeg_abort or + * jpeg_destroy to release any temporary space.) + * If an abbreviated (tables only) datastream is presented, the routine will + * return JPEG_HEADER_TABLES_ONLY upon reaching EOI. The application may then + * re-use the JPEG object to read the abbreviated image datastream(s). + * It is unnecessary (but OK) to call jpeg_abort in this case. + * The JPEG_SUSPENDED return code only occurs if the data source module + * requests suspension of the decompressor. In this case the application + * should load more source data and then re-call jpeg_read_header to resume + * processing. + * If a non-suspending data source is used and require_image is TRUE, then the + * return code need not be inspected since only JPEG_HEADER_OK is possible. + * + * This routine is now just a front end to jpeg_consume_input, with some + * extra error checking. + */ + +GLOBAL int +jpeg_read_header (j_decompress_ptr cinfo, boolean require_image) +{ + int retcode; + + if (cinfo->global_state != DSTATE_START && + cinfo->global_state != DSTATE_INHEADER) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + retcode = jpeg_consume_input(cinfo); + + switch (retcode) { + case JPEG_REACHED_SOS: + retcode = JPEG_HEADER_OK; + break; + case JPEG_REACHED_EOI: + if (require_image) /* Complain if application wanted an image */ + ERREXIT(cinfo, JERR_NO_IMAGE); + /* Reset to start state; it would be safer to require the application to + * call jpeg_abort, but we can't change it now for compatibility reasons. + * A side effect is to free any temporary memory (there shouldn't be any). + */ + jpeg_abort((j_common_ptr) cinfo); /* sets state = DSTATE_START */ + retcode = JPEG_HEADER_TABLES_ONLY; + break; + case JPEG_SUSPENDED: + /* no work */ + break; + } + + return retcode; +} + + +/* + * Consume data in advance of what the decompressor requires. + * This can be called at any time once the decompressor object has + * been created and a data source has been set up. + * + * This routine is essentially a state machine that handles a couple + * of critical state-transition actions, namely initial setup and + * transition from header scanning to ready-for-start_decompress. + * All the actual input is done via the input controller's consume_input + * method. + */ + +GLOBAL int +jpeg_consume_input (j_decompress_ptr cinfo) +{ + int retcode = JPEG_SUSPENDED; + + /* NB: every possible DSTATE value should be listed in this switch */ + switch (cinfo->global_state) { + case DSTATE_START: + /* Start-of-datastream actions: reset appropriate modules */ + (*cinfo->inputctl->reset_input_controller) (cinfo); + /* Initialize application's data source module */ + (*cinfo->src->init_source) (cinfo); + cinfo->global_state = DSTATE_INHEADER; + /*FALLTHROUGH*/ + case DSTATE_INHEADER: + retcode = (*cinfo->inputctl->consume_input) (cinfo); + if (retcode == JPEG_REACHED_SOS) { /* Found SOS, prepare to decompress */ + /* Set up default parameters based on header data */ + default_decompress_parms(cinfo); + /* Set global state: ready for start_decompress */ + cinfo->global_state = DSTATE_READY; + } + break; + case DSTATE_READY: + /* Can't advance past first SOS until start_decompress is called */ + retcode = JPEG_REACHED_SOS; + break; + case DSTATE_PRELOAD: + case DSTATE_PRESCAN: + case DSTATE_SCANNING: + case DSTATE_RAW_OK: + case DSTATE_BUFIMAGE: + case DSTATE_BUFPOST: + case DSTATE_STOPPING: + retcode = (*cinfo->inputctl->consume_input) (cinfo); + break; + default: + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + } + return retcode; +} + + +/* + * Have we finished reading the input file? + */ + +GLOBAL boolean +jpeg_input_complete (j_decompress_ptr cinfo) +{ + /* Check for valid jpeg object */ + if (cinfo->global_state < DSTATE_START || + cinfo->global_state > DSTATE_STOPPING) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + return cinfo->inputctl->eoi_reached; +} + + +/* + * Is there more than one scan? + */ + +GLOBAL boolean +jpeg_has_multiple_scans (j_decompress_ptr cinfo) +{ + /* Only valid after jpeg_read_header completes */ + if (cinfo->global_state < DSTATE_READY || + cinfo->global_state > DSTATE_STOPPING) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + return cinfo->inputctl->has_multiple_scans; +} + + +/* + * Finish JPEG decompression. + * + * This will normally just verify the file trailer and release temp storage. + * + * Returns FALSE if suspended. The return value need be inspected only if + * a suspending data source is used. + */ + +GLOBAL boolean +jpeg_finish_decompress (j_decompress_ptr cinfo) +{ + if ((cinfo->global_state == DSTATE_SCANNING || + cinfo->global_state == DSTATE_RAW_OK) && ! cinfo->buffered_image) { + /* Terminate final pass of non-buffered mode */ + if (cinfo->output_scanline < cinfo->output_height) + ERREXIT(cinfo, JERR_TOO_LITTLE_DATA); + (*cinfo->master->finish_output_pass) (cinfo); + cinfo->global_state = DSTATE_STOPPING; + } else if (cinfo->global_state == DSTATE_BUFIMAGE) { + /* Finishing after a buffered-image operation */ + cinfo->global_state = DSTATE_STOPPING; + } else if (cinfo->global_state != DSTATE_STOPPING) { + /* STOPPING = repeat call after a suspension, anything else is error */ + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + } + /* Read until EOI */ + while (! cinfo->inputctl->eoi_reached) { + if ((*cinfo->inputctl->consume_input) (cinfo) == JPEG_SUSPENDED) + return FALSE; /* Suspend, come back later */ + } + /* Do final cleanup */ + (*cinfo->src->term_source) (cinfo); + /* We can use jpeg_abort to release memory and reset global_state */ + jpeg_abort((j_common_ptr) cinfo); + return TRUE; +} diff --git a/utils/Libs/jpeg6/jdapistd.cpp b/utils/Libs/jpeg6/jdapistd.cpp new file mode 100644 index 0000000..7781e16 --- /dev/null +++ b/utils/Libs/jpeg6/jdapistd.cpp @@ -0,0 +1,275 @@ +/* + * jdapistd.c + * + * Copyright (C) 1994-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains application interface code for the decompression half + * of the JPEG library. These are the "standard" API routines that are + * used in the normal full-decompression case. They are not used by a + * transcoding-only application. Note that if an application links in + * jpeg_start_decompress, it will end up linking in the entire decompressor. + * We thus must separate this file from jdapimin.c to avoid linking the + * whole decompression library into a transcoder. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Forward declarations */ +LOCAL boolean output_pass_setup JPP((j_decompress_ptr cinfo)); + + +/* + * Decompression initialization. + * jpeg_read_header must be completed before calling this. + * + * If a multipass operating mode was selected, this will do all but the + * last pass, and thus may take a great deal of time. + * + * Returns FALSE if suspended. The return value need be inspected only if + * a suspending data source is used. + */ + +GLOBAL boolean +jpeg_start_decompress (j_decompress_ptr cinfo) +{ + if (cinfo->global_state == DSTATE_READY) { + /* First call: initialize master control, select active modules */ + jinit_master_decompress(cinfo); + if (cinfo->buffered_image) { + /* No more work here; expecting jpeg_start_output next */ + cinfo->global_state = DSTATE_BUFIMAGE; + return TRUE; + } + cinfo->global_state = DSTATE_PRELOAD; + } + if (cinfo->global_state == DSTATE_PRELOAD) { + /* If file has multiple scans, absorb them all into the coef buffer */ + if (cinfo->inputctl->has_multiple_scans) { +#ifdef D_MULTISCAN_FILES_SUPPORTED + for (;;) { + int retcode; + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + /* Absorb some more input */ + retcode = (*cinfo->inputctl->consume_input) (cinfo); + if (retcode == JPEG_SUSPENDED) + return FALSE; + if (retcode == JPEG_REACHED_EOI) + break; + /* Advance progress counter if appropriate */ + if (cinfo->progress != NULL && + (retcode == JPEG_ROW_COMPLETED || retcode == JPEG_REACHED_SOS)) { + if (++cinfo->progress->pass_counter >= cinfo->progress->pass_limit) { + /* jdmaster underestimated number of scans; ratchet up one scan */ + cinfo->progress->pass_limit += (long) cinfo->total_iMCU_rows; + } + } + } +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + } + cinfo->output_scan_number = cinfo->input_scan_number; + } else if (cinfo->global_state != DSTATE_PRESCAN) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + /* Perform any dummy output passes, and set up for the final pass */ + return output_pass_setup(cinfo); +} + + +/* + * Set up for an output pass, and perform any dummy pass(es) needed. + * Common subroutine for jpeg_start_decompress and jpeg_start_output. + * Entry: global_state = DSTATE_PRESCAN only if previously suspended. + * Exit: If done, returns TRUE and sets global_state for proper output mode. + * If suspended, returns FALSE and sets global_state = DSTATE_PRESCAN. + */ + +LOCAL boolean +output_pass_setup (j_decompress_ptr cinfo) +{ + if (cinfo->global_state != DSTATE_PRESCAN) { + /* First call: do pass setup */ + (*cinfo->master->prepare_for_output_pass) (cinfo); + cinfo->output_scanline = 0; + cinfo->global_state = DSTATE_PRESCAN; + } + /* Loop over any required dummy passes */ + while (cinfo->master->is_dummy_pass) { +#ifdef QUANT_2PASS_SUPPORTED + /* Crank through the dummy pass */ + while (cinfo->output_scanline < cinfo->output_height) { + JDIMENSION last_scanline; + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->output_scanline; + cinfo->progress->pass_limit = (long) cinfo->output_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + /* Process some data */ + last_scanline = cinfo->output_scanline; + (*cinfo->main->process_data) (cinfo, (JSAMPARRAY) NULL, + &cinfo->output_scanline, (JDIMENSION) 0); + if (cinfo->output_scanline == last_scanline) + return FALSE; /* No progress made, must suspend */ + } + /* Finish up dummy pass, and set up for another one */ + (*cinfo->master->finish_output_pass) (cinfo); + (*cinfo->master->prepare_for_output_pass) (cinfo); + cinfo->output_scanline = 0; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif /* QUANT_2PASS_SUPPORTED */ + } + /* Ready for application to drive output pass through + * jpeg_read_scanlines or jpeg_read_raw_data. + */ + cinfo->global_state = cinfo->raw_data_out ? DSTATE_RAW_OK : DSTATE_SCANNING; + return TRUE; +} + + +/* + * Read some scanlines of data from the JPEG decompressor. + * + * The return value will be the number of lines actually read. + * This may be less than the number requested in several cases, + * including bottom of image, data source suspension, and operating + * modes that emit multiple scanlines at a time. + * + * Note: we warn about excess calls to jpeg_read_scanlines() since + * this likely signals an application programmer error. However, + * an oversize buffer (max_lines > scanlines remaining) is not an error. + */ + +GLOBAL JDIMENSION +jpeg_read_scanlines (j_decompress_ptr cinfo, JSAMPARRAY scanlines, + JDIMENSION max_lines) +{ + JDIMENSION row_ctr; + + if (cinfo->global_state != DSTATE_SCANNING) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + if (cinfo->output_scanline >= cinfo->output_height) { + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + return 0; + } + + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->output_scanline; + cinfo->progress->pass_limit = (long) cinfo->output_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + + /* Process some data */ + row_ctr = 0; + (*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, max_lines); + cinfo->output_scanline += row_ctr; + return row_ctr; +} + + +/* + * Alternate entry point to read raw data. + * Processes exactly one iMCU row per call, unless suspended. + */ + +GLOBAL JDIMENSION +jpeg_read_raw_data (j_decompress_ptr cinfo, JSAMPIMAGE data, + JDIMENSION max_lines) +{ + JDIMENSION lines_per_iMCU_row; + + if (cinfo->global_state != DSTATE_RAW_OK) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + if (cinfo->output_scanline >= cinfo->output_height) { + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + return 0; + } + + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->output_scanline; + cinfo->progress->pass_limit = (long) cinfo->output_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + + /* Verify that at least one iMCU row can be returned. */ + lines_per_iMCU_row = cinfo->max_v_samp_factor * cinfo->min_DCT_scaled_size; + if (max_lines < lines_per_iMCU_row) + ERREXIT(cinfo, JERR_BUFFER_SIZE); + + /* Decompress directly into user's buffer. */ + if (! (*cinfo->coef->decompress_data) (cinfo, data)) + return 0; /* suspension forced, can do nothing more */ + + /* OK, we processed one iMCU row. */ + cinfo->output_scanline += lines_per_iMCU_row; + return lines_per_iMCU_row; +} + + +/* Additional entry points for buffered-image mode. */ + +#ifdef D_MULTISCAN_FILES_SUPPORTED + +/* + * Initialize for an output pass in buffered-image mode. + */ + +GLOBAL boolean +jpeg_start_output (j_decompress_ptr cinfo, int scan_number) +{ + if (cinfo->global_state != DSTATE_BUFIMAGE && + cinfo->global_state != DSTATE_PRESCAN) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + /* Limit scan number to valid range */ + if (scan_number <= 0) + scan_number = 1; + if (cinfo->inputctl->eoi_reached && + scan_number > cinfo->input_scan_number) + scan_number = cinfo->input_scan_number; + cinfo->output_scan_number = scan_number; + /* Perform any dummy output passes, and set up for the real pass */ + return output_pass_setup(cinfo); +} + + +/* + * Finish up after an output pass in buffered-image mode. + * + * Returns FALSE if suspended. The return value need be inspected only if + * a suspending data source is used. + */ + +GLOBAL boolean +jpeg_finish_output (j_decompress_ptr cinfo) +{ + if ((cinfo->global_state == DSTATE_SCANNING || + cinfo->global_state == DSTATE_RAW_OK) && cinfo->buffered_image) { + /* Terminate this pass. */ + /* We do not require the whole pass to have been completed. */ + (*cinfo->master->finish_output_pass) (cinfo); + cinfo->global_state = DSTATE_BUFPOST; + } else if (cinfo->global_state != DSTATE_BUFPOST) { + /* BUFPOST = repeat call after a suspension, anything else is error */ + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + } + /* Read markers looking for SOS or EOI */ + while (cinfo->input_scan_number <= cinfo->output_scan_number && + ! cinfo->inputctl->eoi_reached) { + if ((*cinfo->inputctl->consume_input) (cinfo) == JPEG_SUSPENDED) + return FALSE; /* Suspend, come back later */ + } + cinfo->global_state = DSTATE_BUFIMAGE; + return TRUE; +} + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ diff --git a/utils/Libs/jpeg6/jdatasrc.cpp b/utils/Libs/jpeg6/jdatasrc.cpp new file mode 100644 index 0000000..5c1696d --- /dev/null +++ b/utils/Libs/jpeg6/jdatasrc.cpp @@ -0,0 +1,204 @@ +/* + * jdatasrc.c + * + * Copyright (C) 1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains decompression data source routines for the case of + * reading JPEG data from a file (or any stdio stream). While these routines + * are sufficient for most applications, some will want to use a different + * source manager. + * IMPORTANT: we assume that fread() will correctly transcribe an array of + * JOCTETs from 8-bit-wide elements on external storage. If char is wider + * than 8 bits on your machine, you may need to do some tweaking. + */ + + +/* this is not a core library module, so it doesn't define JPEG_INTERNALS */ +#include "jinclude.h" +#include "jpeglib.h" +#include "jerror.h" + + +/* Expanded data source object for stdio input */ + +typedef struct { + struct jpeg_source_mgr pub; /* public fields */ + + unsigned char *infile; /* source stream */ + JOCTET * buffer; /* start of buffer */ + boolean start_of_file; /* have we gotten any data yet? */ +} my_source_mgr; + +typedef my_source_mgr * my_src_ptr; + +#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */ + + +/* + * Initialize source --- called by jpeg_read_header + * before any data is actually read. + */ + +METHODDEF void +init_source (j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + /* We reset the empty-input-file flag for each image, + * but we don't clear the input buffer. + * This is correct behavior for reading a series of images from one source. + */ + src->start_of_file = TRUE; +} + + +/* + * Fill the input buffer --- called whenever buffer is emptied. + * + * In typical applications, this should read fresh data into the buffer + * (ignoring the current state of next_input_byte & bytes_in_buffer), + * reset the pointer & count to the start of the buffer, and return TRUE + * indicating that the buffer has been reloaded. It is not necessary to + * fill the buffer entirely, only to obtain at least one more byte. + * + * There is no such thing as an EOF return. If the end of the file has been + * reached, the routine has a choice of ERREXIT() or inserting fake data into + * the buffer. In most cases, generating a warning message and inserting a + * fake EOI marker is the best course of action --- this will allow the + * decompressor to output however much of the image is there. However, + * the resulting error message is misleading if the real problem is an empty + * input file, so we handle that case specially. + * + * In applications that need to be able to suspend compression due to input + * not being available yet, a FALSE return indicates that no more data can be + * obtained right now, but more may be forthcoming later. In this situation, + * the decompressor will return to its caller (with an indication of the + * number of scanlines it has read, if any). The application should resume + * decompression after it has loaded more data into the input buffer. Note + * that there are substantial restrictions on the use of suspension --- see + * the documentation. + * + * When suspending, the decompressor will back up to a convenient restart point + * (typically the start of the current MCU). next_input_byte & bytes_in_buffer + * indicate where the restart point will be if the current call returns FALSE. + * Data beyond this point must be rescanned after resumption, so move it to + * the front of the buffer rather than discarding it. + */ + +METHODDEF boolean +fill_input_buffer (j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + memcpy( src->buffer, src->infile, INPUT_BUF_SIZE ); + + src->infile += INPUT_BUF_SIZE; + + src->pub.next_input_byte = src->buffer; + src->pub.bytes_in_buffer = INPUT_BUF_SIZE; + src->start_of_file = FALSE; + + return TRUE; +} + + +/* + * Skip data --- used to skip over a potentially large amount of + * uninteresting data (such as an APPn marker). + * + * Writers of suspendable-input applications must note that skip_input_data + * is not granted the right to give a suspension return. If the skip extends + * beyond the data currently in the buffer, the buffer can be marked empty so + * that the next read will cause a fill_input_buffer call that can suspend. + * Arranging for additional bytes to be discarded before reloading the input + * buffer is the application writer's problem. + */ + +METHODDEF void +skip_input_data (j_decompress_ptr cinfo, long num_bytes) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + /* Just a dumb implementation for now. Could use fseek() except + * it doesn't work on pipes. Not clear that being smart is worth + * any trouble anyway --- large skips are infrequent. + */ + if (num_bytes > 0) { + while (num_bytes > (long) src->pub.bytes_in_buffer) { + num_bytes -= (long) src->pub.bytes_in_buffer; + (void) fill_input_buffer(cinfo); + /* note we assume that fill_input_buffer will never return FALSE, + * so suspension need not be handled. + */ + } + src->pub.next_input_byte += (size_t) num_bytes; + src->pub.bytes_in_buffer -= (size_t) num_bytes; + } +} + + +/* + * An additional method that can be provided by data source modules is the + * resync_to_restart method for error recovery in the presence of RST markers. + * For the moment, this source module just uses the default resync method + * provided by the JPEG library. That method assumes that no backtracking + * is possible. + */ + + +/* + * Terminate source --- called by jpeg_finish_decompress + * after all data has been read. Often a no-op. + * + * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding + * application must deal with any cleanup that should happen even + * for error exit. + */ + +METHODDEF void +term_source (j_decompress_ptr cinfo) +{ + /* no work necessary here */ +} + + +/* + * Prepare for input from a stdio stream. + * The caller must have already opened the stream, and is responsible + * for closing it after finishing decompression. + */ + +GLOBAL void +jpeg_stdio_src (j_decompress_ptr cinfo, unsigned char *infile) +{ + my_src_ptr src; + + /* The source object and input buffer are made permanent so that a series + * of JPEG images can be read from the same file by calling jpeg_stdio_src + * only before the first one. (If we discarded the buffer at the end of + * one image, we'd likely lose the start of the next one.) + * This makes it unsafe to use this manager and a different source + * manager serially with the same JPEG object. Caveat programmer. + */ + if (cinfo->src == NULL) { /* first time for this JPEG object? */ + cinfo->src = (struct jpeg_source_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_source_mgr)); + src = (my_src_ptr) cinfo->src; + src->buffer = (JOCTET *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + INPUT_BUF_SIZE * SIZEOF(JOCTET)); + } + + src = (my_src_ptr) cinfo->src; + src->pub.init_source = init_source; + src->pub.fill_input_buffer = fill_input_buffer; + src->pub.skip_input_data = skip_input_data; + src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ + src->pub.term_source = term_source; + src->infile = infile; + src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ + src->pub.next_input_byte = NULL; /* until buffer loaded */ +} diff --git a/utils/Libs/jpeg6/jdcoefct.cpp b/utils/Libs/jpeg6/jdcoefct.cpp new file mode 100644 index 0000000..2b20c07 --- /dev/null +++ b/utils/Libs/jpeg6/jdcoefct.cpp @@ -0,0 +1,725 @@ +/* + * jdcoefct.c + * + * Copyright (C) 1994-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the coefficient buffer controller for decompression. + * This controller is the top level of the JPEG decompressor proper. + * The coefficient buffer lies between entropy decoding and inverse-DCT steps. + * + * In buffered-image mode, this controller is the interface between + * input-oriented processing and output-oriented processing. + * Also, the input side (only) is used when reading a file for transcoding. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + +/* Block smoothing is only applicable for progressive JPEG, so: */ +#ifndef D_PROGRESSIVE_SUPPORTED +#undef BLOCK_SMOOTHING_SUPPORTED +#endif + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_d_coef_controller pub; /* public fields */ + + /* These variables keep track of the current location of the input side. */ + /* cinfo->input_iMCU_row is also used for this. */ + JDIMENSION MCU_ctr; /* counts MCUs processed in current row */ + int MCU_vert_offset; /* counts MCU rows within iMCU row */ + int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + /* The output side's location is represented by cinfo->output_iMCU_row. */ + + /* In single-pass modes, it's sufficient to buffer just one MCU. + * We allocate a workspace of D_MAX_BLOCKS_IN_MCU coefficient blocks, + * and let the entropy decoder write into that workspace each time. + * (On 80x86, the workspace is FAR even though it's not really very big; + * this is to keep the module interfaces unchanged when a large coefficient + * buffer is necessary.) + * In multi-pass modes, this array points to the current MCU's blocks + * within the virtual arrays; it is used only by the input side. + */ + JBLOCKROW MCU_buffer[D_MAX_BLOCKS_IN_MCU]; + +#ifdef D_MULTISCAN_FILES_SUPPORTED + /* In multi-pass modes, we need a virtual block array for each component. */ + jvirt_barray_ptr whole_image[MAX_COMPONENTS]; +#endif + +#ifdef BLOCK_SMOOTHING_SUPPORTED + /* When doing block smoothing, we latch coefficient Al values here */ + int * coef_bits_latch; +#define SAVED_COEFS 6 /* we save coef_bits[0..5] */ +#endif +} my_coef_controller; + +typedef my_coef_controller * my_coef_ptr; + +/* Forward declarations */ +METHODDEF int decompress_onepass + JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); +#ifdef D_MULTISCAN_FILES_SUPPORTED +METHODDEF int decompress_data + JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); +#endif +#ifdef BLOCK_SMOOTHING_SUPPORTED +LOCAL boolean smoothing_ok JPP((j_decompress_ptr cinfo)); +METHODDEF int decompress_smooth_data + JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); +#endif + + +LOCAL void +start_iMCU_row (j_decompress_ptr cinfo) +/* Reset within-iMCU-row counters for a new row (input side) */ +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + /* In an interleaved scan, an MCU row is the same as an iMCU row. + * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. + * But at the bottom of the image, process only what's left. + */ + if (cinfo->comps_in_scan > 1) { + coef->MCU_rows_per_iMCU_row = 1; + } else { + if (cinfo->input_iMCU_row < (cinfo->total_iMCU_rows-1)) + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; + else + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; + } + + coef->MCU_ctr = 0; + coef->MCU_vert_offset = 0; +} + + +/* + * Initialize for an input processing pass. + */ + +METHODDEF void +start_input_pass (j_decompress_ptr cinfo) +{ + cinfo->input_iMCU_row = 0; + start_iMCU_row(cinfo); +} + + +/* + * Initialize for an output processing pass. + */ + +METHODDEF void +start_output_pass (j_decompress_ptr cinfo) +{ +#ifdef BLOCK_SMOOTHING_SUPPORTED + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + /* If multipass, check to see whether to use block smoothing on this pass */ + if (coef->pub.coef_arrays != NULL) { + if (cinfo->do_block_smoothing && smoothing_ok(cinfo)) + coef->pub.decompress_data = decompress_smooth_data; + else + coef->pub.decompress_data = decompress_data; + } +#endif + cinfo->output_iMCU_row = 0; +} + + +/* + * Decompress and return some data in the single-pass case. + * Always attempts to emit one fully interleaved MCU row ("iMCU" row). + * Input and output must run in lockstep since we have only a one-MCU buffer. + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + * + * NB: output_buf contains a plane for each component in image. + * For single pass, this is the same as the components in the scan. + */ + +METHODDEF int +decompress_onepass (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + int blkn, ci, xindex, yindex, yoffset, useful_width; + JSAMPARRAY output_ptr; + JDIMENSION start_col, output_col; + jpeg_component_info *compptr; + inverse_DCT_method_ptr inverse_DCT; + + /* Loop to process as much as one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->MCU_ctr; MCU_col_num <= last_MCU_col; + MCU_col_num++) { + /* Try to fetch an MCU. Entropy decoder expects buffer to be zeroed. */ + jzero_far((void FAR *) coef->MCU_buffer[0], + (size_t) (cinfo->blocks_in_MCU * SIZEOF(JBLOCK))); + if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->MCU_ctr = MCU_col_num; + return JPEG_SUSPENDED; + } + /* Determine where data should go in output_buf and do the IDCT thing. + * We skip dummy blocks at the right and bottom edges (but blkn gets + * incremented past them!). Note the inner loop relies on having + * allocated the MCU_buffer[] blocks sequentially. + */ + blkn = 0; /* index of current DCT block within MCU */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Don't bother to IDCT an uninteresting component. */ + if (! compptr->component_needed) { + blkn += compptr->MCU_blocks; + continue; + } + inverse_DCT = cinfo->idct->inverse_DCT[compptr->component_index]; + useful_width = (MCU_col_num < last_MCU_col) ? compptr->MCU_width + : compptr->last_col_width; + output_ptr = output_buf[ci] + yoffset * compptr->DCT_scaled_size; + start_col = MCU_col_num * compptr->MCU_sample_width; + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + if (cinfo->input_iMCU_row < last_iMCU_row || + yoffset+yindex < compptr->last_row_height) { + output_col = start_col; + for (xindex = 0; xindex < useful_width; xindex++) { + (*inverse_DCT) (cinfo, compptr, + (JCOEFPTR) coef->MCU_buffer[blkn+xindex], + output_ptr, output_col); + output_col += compptr->DCT_scaled_size; + } + } + blkn += compptr->MCU_width; + output_ptr += compptr->DCT_scaled_size; + } + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->MCU_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + cinfo->output_iMCU_row++; + if (++(cinfo->input_iMCU_row) < cinfo->total_iMCU_rows) { + start_iMCU_row(cinfo); + return JPEG_ROW_COMPLETED; + } + /* Completed the scan */ + (*cinfo->inputctl->finish_input_pass) (cinfo); + return JPEG_SCAN_COMPLETED; +} + + +/* + * Dummy consume-input routine for single-pass operation. + */ + +METHODDEF int +dummy_consume_data (j_decompress_ptr cinfo) +{ + return JPEG_SUSPENDED; /* Always indicate nothing was done */ +} + + +#ifdef D_MULTISCAN_FILES_SUPPORTED + +/* + * Consume input data and store it in the full-image coefficient buffer. + * We read as much as one fully interleaved MCU row ("iMCU" row) per call, + * ie, v_samp_factor block rows for each component in the scan. + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + */ + +METHODDEF int +consume_data (j_decompress_ptr cinfo) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + int blkn, ci, xindex, yindex, yoffset; + JDIMENSION start_col; + JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN]; + JBLOCKROW buffer_ptr; + jpeg_component_info *compptr; + + /* Align the virtual buffers for the components used in this scan. */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + buffer[ci] = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[compptr->component_index], + cinfo->input_iMCU_row * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, TRUE); + /* Note: entropy decoder expects buffer to be zeroed, + * but this is handled automatically by the memory manager + * because we requested a pre-zeroed array. + */ + } + + /* Loop to process one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->MCU_ctr; MCU_col_num < cinfo->MCUs_per_row; + MCU_col_num++) { + /* Construct list of pointers to DCT blocks belonging to this MCU */ + blkn = 0; /* index of current DCT block within MCU */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + start_col = MCU_col_num * compptr->MCU_width; + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + buffer_ptr = buffer[ci][yindex+yoffset] + start_col; + for (xindex = 0; xindex < compptr->MCU_width; xindex++) { + coef->MCU_buffer[blkn++] = buffer_ptr++; + } + } + } + /* Try to fetch the MCU. */ + if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->MCU_ctr = MCU_col_num; + return JPEG_SUSPENDED; + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->MCU_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + if (++(cinfo->input_iMCU_row) < cinfo->total_iMCU_rows) { + start_iMCU_row(cinfo); + return JPEG_ROW_COMPLETED; + } + /* Completed the scan */ + (*cinfo->inputctl->finish_input_pass) (cinfo); + return JPEG_SCAN_COMPLETED; +} + + +/* + * Decompress and return some data in the multi-pass case. + * Always attempts to emit one fully interleaved MCU row ("iMCU" row). + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + * + * NB: output_buf contains a plane for each component in image. + */ + +METHODDEF int +decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + JDIMENSION block_num; + int ci, block_row, block_rows; + JBLOCKARRAY buffer; + JBLOCKROW buffer_ptr; + JSAMPARRAY output_ptr; + JDIMENSION output_col; + jpeg_component_info *compptr; + inverse_DCT_method_ptr inverse_DCT; + + /* Force some input to be done if we are getting ahead of the input. */ + while (cinfo->input_scan_number < cinfo->output_scan_number || + (cinfo->input_scan_number == cinfo->output_scan_number && + cinfo->input_iMCU_row <= cinfo->output_iMCU_row)) { + if ((*cinfo->inputctl->consume_input)(cinfo) == JPEG_SUSPENDED) + return JPEG_SUSPENDED; + } + + /* OK, output from the virtual arrays. */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Don't bother to IDCT an uninteresting component. */ + if (! compptr->component_needed) + continue; + /* Align the virtual buffer for this component. */ + buffer = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[ci], + cinfo->output_iMCU_row * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + /* Count non-dummy DCT block rows in this iMCU row. */ + if (cinfo->output_iMCU_row < last_iMCU_row) + block_rows = compptr->v_samp_factor; + else { + /* NB: can't use last_row_height here; it is input-side-dependent! */ + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (block_rows == 0) block_rows = compptr->v_samp_factor; + } + inverse_DCT = cinfo->idct->inverse_DCT[ci]; + output_ptr = output_buf[ci]; + /* Loop over all DCT blocks to be processed. */ + for (block_row = 0; block_row < block_rows; block_row++) { + buffer_ptr = buffer[block_row]; + output_col = 0; + for (block_num = 0; block_num < compptr->width_in_blocks; block_num++) { + (*inverse_DCT) (cinfo, compptr, (JCOEFPTR) buffer_ptr, + output_ptr, output_col); + buffer_ptr++; + output_col += compptr->DCT_scaled_size; + } + output_ptr += compptr->DCT_scaled_size; + } + } + + if (++(cinfo->output_iMCU_row) < cinfo->total_iMCU_rows) + return JPEG_ROW_COMPLETED; + return JPEG_SCAN_COMPLETED; +} + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + + +#ifdef BLOCK_SMOOTHING_SUPPORTED + +/* + * This code applies interblock smoothing as described by section K.8 + * of the JPEG standard: the first 5 AC coefficients are estimated from + * the DC values of a DCT block and its 8 neighboring blocks. + * We apply smoothing only for progressive JPEG decoding, and only if + * the coefficients it can estimate are not yet known to full precision. + */ + +/* + * Determine whether block smoothing is applicable and safe. + * We also latch the current states of the coef_bits[] entries for the + * AC coefficients; otherwise, if the input side of the decompressor + * advances into a new scan, we might think the coefficients are known + * more accurately than they really are. + */ + +LOCAL boolean +smoothing_ok (j_decompress_ptr cinfo) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + boolean smoothing_useful = FALSE; + int ci, coefi; + jpeg_component_info *compptr; + JQUANT_TBL * qtable; + int * coef_bits; + int * coef_bits_latch; + + if (! cinfo->progressive_mode || cinfo->coef_bits == NULL) + return FALSE; + + /* Allocate latch area if not already done */ + if (coef->coef_bits_latch == NULL) + coef->coef_bits_latch = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->num_components * + (SAVED_COEFS * SIZEOF(int))); + coef_bits_latch = coef->coef_bits_latch; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* All components' quantization values must already be latched. */ + if ((qtable = compptr->quant_table) == NULL) + return FALSE; + /* Verify DC & first 5 AC quantizers are nonzero to avoid zero-divide. */ + for (coefi = 0; coefi <= 5; coefi++) { + if (qtable->quantval[coefi] == 0) + return FALSE; + } + /* DC values must be at least partly known for all components. */ + coef_bits = cinfo->coef_bits[ci]; + if (coef_bits[0] < 0) + return FALSE; + /* Block smoothing is helpful if some AC coefficients remain inaccurate. */ + for (coefi = 1; coefi <= 5; coefi++) { + coef_bits_latch[coefi] = coef_bits[coefi]; + if (coef_bits[coefi] != 0) + smoothing_useful = TRUE; + } + coef_bits_latch += SAVED_COEFS; + } + + return smoothing_useful; +} + + +/* + * Variant of decompress_data for use when doing block smoothing. + */ + +METHODDEF int +decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + JDIMENSION block_num, last_block_column; + int ci, block_row, block_rows, access_rows; + JBLOCKARRAY buffer; + JBLOCKROW buffer_ptr, prev_block_row, next_block_row; + JSAMPARRAY output_ptr; + JDIMENSION output_col; + jpeg_component_info *compptr; + inverse_DCT_method_ptr inverse_DCT; + boolean first_row, last_row; + JBLOCK workspace; + int *coef_bits; + JQUANT_TBL *quanttbl; + INT32 Q00,Q01,Q02,Q10,Q11,Q20, num; + int DC1,DC2,DC3,DC4,DC5,DC6,DC7,DC8,DC9; + int Al, pred; + + /* Force some input to be done if we are getting ahead of the input. */ + while (cinfo->input_scan_number <= cinfo->output_scan_number && + ! cinfo->inputctl->eoi_reached) { + if (cinfo->input_scan_number == cinfo->output_scan_number) { + /* If input is working on current scan, we ordinarily want it to + * have completed the current row. But if input scan is DC, + * we want it to keep one row ahead so that next block row's DC + * values are up to date. + */ + JDIMENSION delta = (cinfo->Ss == 0) ? 1 : 0; + if (cinfo->input_iMCU_row > cinfo->output_iMCU_row+delta) + break; + } + if ((*cinfo->inputctl->consume_input)(cinfo) == JPEG_SUSPENDED) + return JPEG_SUSPENDED; + } + + /* OK, output from the virtual arrays. */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Don't bother to IDCT an uninteresting component. */ + if (! compptr->component_needed) + continue; + /* Count non-dummy DCT block rows in this iMCU row. */ + if (cinfo->output_iMCU_row < last_iMCU_row) { + block_rows = compptr->v_samp_factor; + access_rows = block_rows * 2; /* this and next iMCU row */ + last_row = FALSE; + } else { + /* NB: can't use last_row_height here; it is input-side-dependent! */ + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (block_rows == 0) block_rows = compptr->v_samp_factor; + access_rows = block_rows; /* this iMCU row only */ + last_row = TRUE; + } + /* Align the virtual buffer for this component. */ + if (cinfo->output_iMCU_row > 0) { + access_rows += compptr->v_samp_factor; /* prior iMCU row too */ + buffer = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[ci], + (cinfo->output_iMCU_row - 1) * compptr->v_samp_factor, + (JDIMENSION) access_rows, FALSE); + buffer += compptr->v_samp_factor; /* point to current iMCU row */ + first_row = FALSE; + } else { + buffer = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[ci], + (JDIMENSION) 0, (JDIMENSION) access_rows, FALSE); + first_row = TRUE; + } + /* Fetch component-dependent info */ + coef_bits = coef->coef_bits_latch + (ci * SAVED_COEFS); + quanttbl = compptr->quant_table; + Q00 = quanttbl->quantval[0]; + Q01 = quanttbl->quantval[1]; + Q10 = quanttbl->quantval[2]; + Q20 = quanttbl->quantval[3]; + Q11 = quanttbl->quantval[4]; + Q02 = quanttbl->quantval[5]; + inverse_DCT = cinfo->idct->inverse_DCT[ci]; + output_ptr = output_buf[ci]; + /* Loop over all DCT blocks to be processed. */ + for (block_row = 0; block_row < block_rows; block_row++) { + buffer_ptr = buffer[block_row]; + if (first_row && block_row == 0) + prev_block_row = buffer_ptr; + else + prev_block_row = buffer[block_row-1]; + if (last_row && block_row == block_rows-1) + next_block_row = buffer_ptr; + else + next_block_row = buffer[block_row+1]; + /* We fetch the surrounding DC values using a sliding-register approach. + * Initialize all nine here so as to do the right thing on narrow pics. + */ + DC1 = DC2 = DC3 = (int) prev_block_row[0][0]; + DC4 = DC5 = DC6 = (int) buffer_ptr[0][0]; + DC7 = DC8 = DC9 = (int) next_block_row[0][0]; + output_col = 0; + last_block_column = compptr->width_in_blocks - 1; + for (block_num = 0; block_num <= last_block_column; block_num++) { + /* Fetch current DCT block into workspace so we can modify it. */ + jcopy_block_row(buffer_ptr, (JBLOCKROW) workspace, (JDIMENSION) 1); + /* Update DC values */ + if (block_num < last_block_column) { + DC3 = (int) prev_block_row[1][0]; + DC6 = (int) buffer_ptr[1][0]; + DC9 = (int) next_block_row[1][0]; + } + /* Compute coefficient estimates per K.8. + * An estimate is applied only if coefficient is still zero, + * and is not known to be fully accurate. + */ + /* AC01 */ + if ((Al=coef_bits[1]) != 0 && workspace[1] == 0) { + num = 36 * Q00 * (DC4 - DC6); + if (num >= 0) { + pred = (int) (((Q01<<7) + num) / (Q01<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + pred = (int) (((Q10<<7) + num) / (Q10<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + pred = (int) (((Q20<<7) + num) / (Q20<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + pred = (int) (((Q11<<7) + num) / (Q11<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + pred = (int) (((Q02<<7) + num) / (Q02<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<DCT_scaled_size; + } + output_ptr += compptr->DCT_scaled_size; + } + } + + if (++(cinfo->output_iMCU_row) < cinfo->total_iMCU_rows) + return JPEG_ROW_COMPLETED; + return JPEG_SCAN_COMPLETED; +} + +#endif /* BLOCK_SMOOTHING_SUPPORTED */ + + +/* + * Initialize coefficient buffer controller. + */ + +GLOBAL void +jinit_d_coef_controller (j_decompress_ptr cinfo, boolean need_full_buffer) +{ + my_coef_ptr coef; + + coef = (my_coef_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_coef_controller)); + cinfo->coef = (struct jpeg_d_coef_controller *) coef; + coef->pub.start_input_pass = start_input_pass; + coef->pub.start_output_pass = start_output_pass; +#ifdef BLOCK_SMOOTHING_SUPPORTED + coef->coef_bits_latch = NULL; +#endif + + /* Create the coefficient buffer. */ + if (need_full_buffer) { +#ifdef D_MULTISCAN_FILES_SUPPORTED + /* Allocate a full-image virtual array for each component, */ + /* padded to a multiple of samp_factor DCT blocks in each direction. */ + /* Note we ask for a pre-zeroed array. */ + int ci, access_rows; + jpeg_component_info *compptr; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + access_rows = compptr->v_samp_factor; +#ifdef BLOCK_SMOOTHING_SUPPORTED + /* If block smoothing could be used, need a bigger window */ + if (cinfo->progressive_mode) + access_rows *= 3; +#endif + coef->whole_image[ci] = (*cinfo->mem->request_virt_barray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, TRUE, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + (long) compptr->v_samp_factor), + (JDIMENSION) access_rows); + } + coef->pub.consume_data = consume_data; + coef->pub.decompress_data = decompress_data; + coef->pub.coef_arrays = coef->whole_image; /* link to virtual arrays */ +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + /* We only need a single-MCU buffer. */ + JBLOCKROW buffer; + int i; + + buffer = (JBLOCKROW) + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + D_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + for (i = 0; i < D_MAX_BLOCKS_IN_MCU; i++) { + coef->MCU_buffer[i] = buffer + i; + } + coef->pub.consume_data = dummy_consume_data; + coef->pub.decompress_data = decompress_onepass; + coef->pub.coef_arrays = NULL; /* flag for no virtual arrays */ + } +} diff --git a/utils/Libs/jpeg6/jdcolor.cpp b/utils/Libs/jpeg6/jdcolor.cpp new file mode 100644 index 0000000..d360b53 --- /dev/null +++ b/utils/Libs/jpeg6/jdcolor.cpp @@ -0,0 +1,367 @@ +/* + * jdcolor.c + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains output colorspace conversion routines. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Private subobject */ + +typedef struct { + struct jpeg_color_deconverter pub; /* public fields */ + + /* Private state for YCC->RGB conversion */ + int * Cr_r_tab; /* => table for Cr to R conversion */ + int * Cb_b_tab; /* => table for Cb to B conversion */ + INT32 * Cr_g_tab; /* => table for Cr to G conversion */ + INT32 * Cb_g_tab; /* => table for Cb to G conversion */ +} my_color_deconverter; + +typedef my_color_deconverter * my_cconvert_ptr; + + +/**************** YCbCr -> RGB conversion: most common case **************/ + +/* + * YCbCr is defined per CCIR 601-1, except that Cb and Cr are + * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. + * The conversion equations to be implemented are therefore + * R = Y + 1.40200 * Cr + * G = Y - 0.34414 * Cb - 0.71414 * Cr + * B = Y + 1.77200 * Cb + * where Cb and Cr represent the incoming values less CENTERJSAMPLE. + * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) + * + * To avoid floating-point arithmetic, we represent the fractional constants + * as integers scaled up by 2^16 (about 4 digits precision); we have to divide + * the products by 2^16, with appropriate rounding, to get the correct answer. + * Notice that Y, being an integral input, does not contribute any fraction + * so it need not participate in the rounding. + * + * For even more speed, we avoid doing any multiplications in the inner loop + * by precalculating the constants times Cb and Cr for all possible values. + * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table); + * for 12-bit samples it is still acceptable. It's not very reasonable for + * 16-bit samples, but if you want lossless storage you shouldn't be changing + * colorspace anyway. + * The Cr=>R and Cb=>B values can be rounded to integers in advance; the + * values for the G calculation are left scaled up, since we must add them + * together before rounding. + */ + +#define SCALEBITS 16 /* speediest right-shift on some machines */ +#define ONE_HALF ((INT32) 1 << (SCALEBITS-1)) +#define FIX(x) ((INT32) ((x) * (1L<RGB colorspace conversion. + */ + +LOCAL void +build_ycc_rgb_table (j_decompress_ptr cinfo) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + int i; + INT32 x; + SHIFT_TEMPS + + cconvert->Cr_r_tab = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(int)); + cconvert->Cb_b_tab = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(int)); + cconvert->Cr_g_tab = (INT32 *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(INT32)); + cconvert->Cb_g_tab = (INT32 *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(INT32)); + + for (i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) { + /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ + /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ + /* Cr=>R value is nearest int to 1.40200 * x */ + cconvert->Cr_r_tab[i] = (int) + RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS); + /* Cb=>B value is nearest int to 1.77200 * x */ + cconvert->Cb_b_tab[i] = (int) + RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS); + /* Cr=>G value is scaled-up -0.71414 * x */ + cconvert->Cr_g_tab[i] = (- FIX(0.71414)) * x; + /* Cb=>G value is scaled-up -0.34414 * x */ + /* We also add in ONE_HALF so that need not do it in inner loop */ + cconvert->Cb_g_tab[i] = (- FIX(0.34414)) * x + ONE_HALF; + } +} + + +/* + * Convert some rows of samples to the output colorspace. + * + * Note that we change from noninterleaved, one-plane-per-component format + * to interleaved-pixel format. The output buffer is therefore three times + * as wide as the input buffer. + * A starting row offset is provided only for the input buffer. The caller + * can easily adjust the passed output_buf value to accommodate any row + * offset required on that side. + */ + +METHODDEF void +ycc_rgb_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int y, cb, cr; + register JSAMPROW outptr; + register JSAMPROW inptr0, inptr1, inptr2; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->output_width; + /* copy these pointers into registers if possible */ + register JSAMPLE * range_limit = cinfo->sample_range_limit; + register int * Crrtab = cconvert->Cr_r_tab; + register int * Cbbtab = cconvert->Cb_b_tab; + register INT32 * Crgtab = cconvert->Cr_g_tab; + register INT32 * Cbgtab = cconvert->Cb_g_tab; + SHIFT_TEMPS + + while (--num_rows >= 0) { + inptr0 = input_buf[0][input_row]; + inptr1 = input_buf[1][input_row]; + inptr2 = input_buf[2][input_row]; + input_row++; + outptr = *output_buf++; + for (col = 0; col < num_cols; col++) { + y = GETJSAMPLE(inptr0[col]); + cb = GETJSAMPLE(inptr1[col]); + cr = GETJSAMPLE(inptr2[col]); + /* Range-limiting is essential due to noise introduced by DCT losses. */ + outptr[RGB_RED] = range_limit[y + Crrtab[cr]]; + outptr[RGB_GREEN] = range_limit[y + + ((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], + SCALEBITS))]; + outptr[RGB_BLUE] = range_limit[y + Cbbtab[cb]]; + outptr += RGB_PIXELSIZE; + } + } +} + + +/**************** Cases other than YCbCr -> RGB **************/ + + +/* + * Color conversion for no colorspace change: just copy the data, + * converting from separate-planes to interleaved representation. + */ + +METHODDEF void +null_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + register JSAMPROW inptr, outptr; + register JDIMENSION count; + register int num_components = cinfo->num_components; + JDIMENSION num_cols = cinfo->output_width; + int ci; + + while (--num_rows >= 0) { + for (ci = 0; ci < num_components; ci++) { + inptr = input_buf[ci][input_row]; + outptr = output_buf[0] + ci; + for (count = num_cols; count > 0; count--) { + *outptr = *inptr++; /* needn't bother with GETJSAMPLE() here */ + outptr += num_components; + } + } + input_row++; + output_buf++; + } +} + + +/* + * Color conversion for grayscale: just copy the data. + * This also works for YCbCr -> grayscale conversion, in which + * we just copy the Y (luminance) component and ignore chrominance. + */ + +METHODDEF void +grayscale_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + jcopy_sample_rows(input_buf[0], (int) input_row, output_buf, 0, + num_rows, cinfo->output_width); +} + + +/* + * Adobe-style YCCK->CMYK conversion. + * We convert YCbCr to R=1-C, G=1-M, and B=1-Y using the same + * conversion as above, while passing K (black) unchanged. + * We assume build_ycc_rgb_table has been called. + */ + +METHODDEF void +ycck_cmyk_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int y, cb, cr; + register JSAMPROW outptr; + register JSAMPROW inptr0, inptr1, inptr2, inptr3; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->output_width; + /* copy these pointers into registers if possible */ + register JSAMPLE * range_limit = cinfo->sample_range_limit; + register int * Crrtab = cconvert->Cr_r_tab; + register int * Cbbtab = cconvert->Cb_b_tab; + register INT32 * Crgtab = cconvert->Cr_g_tab; + register INT32 * Cbgtab = cconvert->Cb_g_tab; + SHIFT_TEMPS + + while (--num_rows >= 0) { + inptr0 = input_buf[0][input_row]; + inptr1 = input_buf[1][input_row]; + inptr2 = input_buf[2][input_row]; + inptr3 = input_buf[3][input_row]; + input_row++; + outptr = *output_buf++; + for (col = 0; col < num_cols; col++) { + y = GETJSAMPLE(inptr0[col]); + cb = GETJSAMPLE(inptr1[col]); + cr = GETJSAMPLE(inptr2[col]); + /* Range-limiting is essential due to noise introduced by DCT losses. */ + outptr[0] = range_limit[MAXJSAMPLE - (y + Crrtab[cr])]; /* red */ + outptr[1] = range_limit[MAXJSAMPLE - (y + /* green */ + ((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], + SCALEBITS)))]; + outptr[2] = range_limit[MAXJSAMPLE - (y + Cbbtab[cb])]; /* blue */ + /* K passes through unchanged */ + outptr[3] = inptr3[col]; /* don't need GETJSAMPLE here */ + outptr += 4; + } + } +} + + +/* + * Empty method for start_pass. + */ + +METHODDEF void +start_pass_dcolor (j_decompress_ptr cinfo) +{ + /* no work needed */ +} + + +/* + * Module initialization routine for output colorspace conversion. + */ + +GLOBAL void +jinit_color_deconverter (j_decompress_ptr cinfo) +{ + my_cconvert_ptr cconvert; + int ci; + + cconvert = (my_cconvert_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_color_deconverter)); + cinfo->cconvert = (struct jpeg_color_deconverter *) cconvert; + cconvert->pub.start_pass = start_pass_dcolor; + + /* Make sure num_components agrees with jpeg_color_space */ + switch (cinfo->jpeg_color_space) { + case JCS_GRAYSCALE: + if (cinfo->num_components != 1) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + break; + + case JCS_RGB: + case JCS_YCbCr: + if (cinfo->num_components != 3) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + break; + + case JCS_CMYK: + case JCS_YCCK: + if (cinfo->num_components != 4) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + break; + + default: /* JCS_UNKNOWN can be anything */ + if (cinfo->num_components < 1) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + break; + } + + /* Set out_color_components and conversion method based on requested space. + * Also clear the component_needed flags for any unused components, + * so that earlier pipeline stages can avoid useless computation. + */ + + switch (cinfo->out_color_space) { + case JCS_GRAYSCALE: + cinfo->out_color_components = 1; + if (cinfo->jpeg_color_space == JCS_GRAYSCALE || + cinfo->jpeg_color_space == JCS_YCbCr) { + cconvert->pub.color_convert = grayscale_convert; + /* For color->grayscale conversion, only the Y (0) component is needed */ + for (ci = 1; ci < cinfo->num_components; ci++) + cinfo->comp_info[ci].component_needed = FALSE; + } else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_RGB: + cinfo->out_color_components = RGB_PIXELSIZE; + if (cinfo->jpeg_color_space == JCS_YCbCr) { + cconvert->pub.color_convert = ycc_rgb_convert; + build_ycc_rgb_table(cinfo); + } else if (cinfo->jpeg_color_space == JCS_RGB && RGB_PIXELSIZE == 3) { + cconvert->pub.color_convert = null_convert; + } else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_CMYK: + cinfo->out_color_components = 4; + if (cinfo->jpeg_color_space == JCS_YCCK) { + cconvert->pub.color_convert = ycck_cmyk_convert; + build_ycc_rgb_table(cinfo); + } else if (cinfo->jpeg_color_space == JCS_CMYK) { + cconvert->pub.color_convert = null_convert; + } else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + default: + /* Permit null conversion to same output space */ + if (cinfo->out_color_space == cinfo->jpeg_color_space) { + cinfo->out_color_components = cinfo->num_components; + cconvert->pub.color_convert = null_convert; + } else /* unsupported non-null conversion */ + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + } + + if (cinfo->quantize_colors) + cinfo->output_components = 1; /* single colormapped output component */ + else + cinfo->output_components = cinfo->out_color_components; +} diff --git a/utils/Libs/jpeg6/jdct.h b/utils/Libs/jpeg6/jdct.h new file mode 100644 index 0000000..1d66d4f --- /dev/null +++ b/utils/Libs/jpeg6/jdct.h @@ -0,0 +1,176 @@ +/* + * jdct.h + * + * Copyright (C) 1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This include file contains common declarations for the forward and + * inverse DCT modules. These declarations are private to the DCT managers + * (jcdctmgr.c, jddctmgr.c) and the individual DCT algorithms. + * The individual DCT algorithms are kept in separate files to ease + * machine-dependent tuning (e.g., assembly coding). + */ + + +/* + * A forward DCT routine is given a pointer to a work area of type DCTELEM[]; + * the DCT is to be performed in-place in that buffer. Type DCTELEM is int + * for 8-bit samples, INT32 for 12-bit samples. (NOTE: Floating-point DCT + * implementations use an array of type FAST_FLOAT, instead.) + * The DCT inputs are expected to be signed (range +-CENTERJSAMPLE). + * The DCT outputs are returned scaled up by a factor of 8; they therefore + * have a range of +-8K for 8-bit data, +-128K for 12-bit data. This + * convention improves accuracy in integer implementations and saves some + * work in floating-point ones. + * Quantization of the output coefficients is done by jcdctmgr.c. + */ + +#if BITS_IN_JSAMPLE == 8 +typedef int DCTELEM; /* 16 or 32 bits is fine */ +#else +typedef INT32 DCTELEM; /* must have 32 bits */ +#endif + +typedef JMETHOD(void, forward_DCT_method_ptr, (DCTELEM * data)); +typedef JMETHOD(void, float_DCT_method_ptr, (FAST_FLOAT * data)); + + +/* + * An inverse DCT routine is given a pointer to the input JBLOCK and a pointer + * to an output sample array. The routine must dequantize the input data as + * well as perform the IDCT; for dequantization, it uses the multiplier table + * pointed to by compptr->dct_table. The output data is to be placed into the + * sample array starting at a specified column. (Any row offset needed will + * be applied to the array pointer before it is passed to the IDCT code.) + * Note that the number of samples emitted by the IDCT routine is + * DCT_scaled_size * DCT_scaled_size. + */ + +/* typedef inverse_DCT_method_ptr is declared in jpegint.h */ + +/* + * Each IDCT routine has its own ideas about the best dct_table element type. + */ + +typedef MULTIPLIER ISLOW_MULT_TYPE; /* short or int, whichever is faster */ +#if BITS_IN_JSAMPLE == 8 +typedef MULTIPLIER IFAST_MULT_TYPE; /* 16 bits is OK, use short if faster */ +#define IFAST_SCALE_BITS 2 /* fractional bits in scale factors */ +#else +typedef INT32 IFAST_MULT_TYPE; /* need 32 bits for scaled quantizers */ +#define IFAST_SCALE_BITS 13 /* fractional bits in scale factors */ +#endif +typedef FAST_FLOAT FLOAT_MULT_TYPE; /* preferred floating type */ + + +/* + * Each IDCT routine is responsible for range-limiting its results and + * converting them to unsigned form (0..MAXJSAMPLE). The raw outputs could + * be quite far out of range if the input data is corrupt, so a bulletproof + * range-limiting step is required. We use a mask-and-table-lookup method + * to do the combined operations quickly. See the comments with + * prepare_range_limit_table (in jdmaster.c) for more info. + */ + +#define IDCT_range_limit(cinfo) ((cinfo)->sample_range_limit + CENTERJSAMPLE) + +#define RANGE_MASK (MAXJSAMPLE * 4 + 3) /* 2 bits wider than legal samples */ + + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_fdct_islow jFDislow +#define jpeg_fdct_ifast jFDifast +#define jpeg_fdct_float jFDfloat +#define jpeg_idct_islow jRDislow +#define jpeg_idct_ifast jRDifast +#define jpeg_idct_float jRDfloat +#define jpeg_idct_4x4 jRD4x4 +#define jpeg_idct_2x2 jRD2x2 +#define jpeg_idct_1x1 jRD1x1 +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + +/* Extern declarations for the forward and inverse DCT routines. */ + +EXTERN void jpeg_fdct_islow JPP((DCTELEM * data)); +EXTERN void jpeg_fdct_ifast JPP((DCTELEM * data)); +EXTERN void jpeg_fdct_float JPP((FAST_FLOAT * data)); + +EXTERN void jpeg_idct_islow + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN void jpeg_idct_ifast + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN void jpeg_idct_float + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN void jpeg_idct_4x4 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN void jpeg_idct_2x2 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN void jpeg_idct_1x1 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); + + +/* + * Macros for handling fixed-point arithmetic; these are used by many + * but not all of the DCT/IDCT modules. + * + * All values are expected to be of type INT32. + * Fractional constants are scaled left by CONST_BITS bits. + * CONST_BITS is defined within each module using these macros, + * and may differ from one module to the next. + */ + +#define ONE ((INT32) 1) +#define CONST_SCALE (ONE << CONST_BITS) + +/* Convert a positive real constant to an integer scaled by CONST_SCALE. + * Caution: some C compilers fail to reduce "FIX(constant)" at compile time, + * thus causing a lot of useless floating-point operations at run time. + */ + +#define FIX(x) ((INT32) ((x) * CONST_SCALE + 0.5)) + +/* Descale and correctly round an INT32 value that's scaled by N bits. + * We assume RIGHT_SHIFT rounds towards minus infinity, so adding + * the fudge factor is correct for either sign of X. + */ + +#define DESCALE(x,n) RIGHT_SHIFT((x) + (ONE << ((n)-1)), n) + +/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. + * This macro is used only when the two inputs will actually be no more than + * 16 bits wide, so that a 16x16->32 bit multiply can be used instead of a + * full 32x32 multiply. This provides a useful speedup on many machines. + * Unfortunately there is no way to specify a 16x16->32 multiply portably + * in C, but some C compilers will do the right thing if you provide the + * correct combination of casts. + */ + +#ifdef SHORTxSHORT_32 /* may work if 'int' is 32 bits */ +#define MULTIPLY16C16(var,const) (((INT16) (var)) * ((INT16) (const))) +#endif +#ifdef SHORTxLCONST_32 /* known to work with Microsoft C 6.0 */ +#define MULTIPLY16C16(var,const) (((INT16) (var)) * ((INT32) (const))) +#endif + +#ifndef MULTIPLY16C16 /* default definition */ +#define MULTIPLY16C16(var,const) ((var) * (const)) +#endif + +/* Same except both inputs are variables. */ + +#ifdef SHORTxSHORT_32 /* may work if 'int' is 32 bits */ +#define MULTIPLY16V16(var1,var2) (((INT16) (var1)) * ((INT16) (var2))) +#endif + +#ifndef MULTIPLY16V16 /* default definition */ +#define MULTIPLY16V16(var1,var2) ((var1) * (var2)) +#endif diff --git a/utils/Libs/jpeg6/jddctmgr.cpp b/utils/Libs/jpeg6/jddctmgr.cpp new file mode 100644 index 0000000..ba01fc6 --- /dev/null +++ b/utils/Libs/jpeg6/jddctmgr.cpp @@ -0,0 +1,270 @@ +/* + * jddctmgr.c + * + * Copyright (C) 1994-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the inverse-DCT management logic. + * This code selects a particular IDCT implementation to be used, + * and it performs related housekeeping chores. No code in this file + * is executed per IDCT step, only during output pass setup. + * + * Note that the IDCT routines are responsible for performing coefficient + * dequantization as well as the IDCT proper. This module sets up the + * dequantization multiplier table needed by the IDCT routine. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + + +/* + * The decompressor input side (jdinput.c) saves away the appropriate + * quantization table for each component at the start of the first scan + * involving that component. (This is necessary in order to correctly + * decode files that reuse Q-table slots.) + * When we are ready to make an output pass, the saved Q-table is converted + * to a multiplier table that will actually be used by the IDCT routine. + * The multiplier table contents are IDCT-method-dependent. To support + * application changes in IDCT method between scans, we can remake the + * multiplier tables if necessary. + * In buffered-image mode, the first output pass may occur before any data + * has been seen for some components, and thus before their Q-tables have + * been saved away. To handle this case, multiplier tables are preset + * to zeroes; the result of the IDCT will be a neutral gray level. + */ + + +/* Private subobject for this module */ + +typedef struct { + struct jpeg_inverse_dct pub; /* public fields */ + + /* This array contains the IDCT method code that each multiplier table + * is currently set up for, or -1 if it's not yet set up. + * The actual multiplier tables are pointed to by dct_table in the + * per-component comp_info structures. + */ + int cur_method[MAX_COMPONENTS]; +} my_idct_controller; + +typedef my_idct_controller * my_idct_ptr; + + +/* Allocated multiplier tables: big enough for any supported variant */ + +typedef union { + ISLOW_MULT_TYPE islow_array[DCTSIZE2]; +#ifdef DCT_IFAST_SUPPORTED + IFAST_MULT_TYPE ifast_array[DCTSIZE2]; +#endif +#ifdef DCT_FLOAT_SUPPORTED + FLOAT_MULT_TYPE float_array[DCTSIZE2]; +#endif +} multiplier_table; + + +/* The current scaled-IDCT routines require ISLOW-style multiplier tables, + * so be sure to compile that code if either ISLOW or SCALING is requested. + */ +#ifdef DCT_ISLOW_SUPPORTED +#define PROVIDE_ISLOW_TABLES +#else +#ifdef IDCT_SCALING_SUPPORTED +#define PROVIDE_ISLOW_TABLES +#endif +#endif + + +/* + * Prepare for an output pass. + * Here we select the proper IDCT routine for each component and build + * a matching multiplier table. + */ + +METHODDEF void +start_pass (j_decompress_ptr cinfo) +{ + my_idct_ptr idct = (my_idct_ptr) cinfo->idct; + int ci, i; + jpeg_component_info *compptr; + int method = 0; + inverse_DCT_method_ptr method_ptr = NULL; + JQUANT_TBL * qtbl; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Select the proper IDCT routine for this component's scaling */ + switch (compptr->DCT_scaled_size) { +#ifdef IDCT_SCALING_SUPPORTED + case 1: + method_ptr = jpeg_idct_1x1; + method = JDCT_ISLOW; /* jidctred uses islow-style table */ + break; + case 2: + method_ptr = jpeg_idct_2x2; + method = JDCT_ISLOW; /* jidctred uses islow-style table */ + break; + case 4: + method_ptr = jpeg_idct_4x4; + method = JDCT_ISLOW; /* jidctred uses islow-style table */ + break; +#endif + case DCTSIZE: + switch (cinfo->dct_method) { +#ifdef DCT_ISLOW_SUPPORTED + case JDCT_ISLOW: + method_ptr = jpeg_idct_islow; + method = JDCT_ISLOW; + break; +#endif +#ifdef DCT_IFAST_SUPPORTED + case JDCT_IFAST: + method_ptr = jpeg_idct_ifast; + method = JDCT_IFAST; + break; +#endif +#ifdef DCT_FLOAT_SUPPORTED + case JDCT_FLOAT: + method_ptr = jpeg_idct_float; + method = JDCT_FLOAT; + break; +#endif + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + break; + } + break; + default: + ERREXIT1(cinfo, JERR_BAD_DCTSIZE, compptr->DCT_scaled_size); + break; + } + idct->pub.inverse_DCT[ci] = method_ptr; + /* Create multiplier table from quant table. + * However, we can skip this if the component is uninteresting + * or if we already built the table. Also, if no quant table + * has yet been saved for the component, we leave the + * multiplier table all-zero; we'll be reading zeroes from the + * coefficient controller's buffer anyway. + */ + if (! compptr->component_needed || idct->cur_method[ci] == method) + continue; + qtbl = compptr->quant_table; + if (qtbl == NULL) /* happens if no data yet for component */ + continue; + idct->cur_method[ci] = method; + switch (method) { +#ifdef PROVIDE_ISLOW_TABLES + case JDCT_ISLOW: + { + /* For LL&M IDCT method, multipliers are equal to raw quantization + * coefficients, but are stored in natural order as ints. + */ + ISLOW_MULT_TYPE * ismtbl = (ISLOW_MULT_TYPE *) compptr->dct_table; + for (i = 0; i < DCTSIZE2; i++) { + ismtbl[i] = (ISLOW_MULT_TYPE) qtbl->quantval[jpeg_zigzag_order[i]]; + } + } + break; +#endif +#ifdef DCT_IFAST_SUPPORTED + case JDCT_IFAST: + { + /* For AA&N IDCT method, multipliers are equal to quantization + * coefficients scaled by scalefactor[row]*scalefactor[col], where + * scalefactor[0] = 1 + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + * For integer operation, the multiplier table is to be scaled by + * IFAST_SCALE_BITS. The multipliers are stored in natural order. + */ + IFAST_MULT_TYPE * ifmtbl = (IFAST_MULT_TYPE *) compptr->dct_table; +#define CONST_BITS 14 + static const INT16 aanscales[DCTSIZE2] = { + /* precomputed values scaled up by 14 bits */ + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270, + 21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906, + 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552, + 8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446, + 4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247 + }; + SHIFT_TEMPS + + for (i = 0; i < DCTSIZE2; i++) { + ifmtbl[i] = (IFAST_MULT_TYPE) + DESCALE(MULTIPLY16V16((INT32) qtbl->quantval[jpeg_zigzag_order[i]], + (INT32) aanscales[i]), + CONST_BITS-IFAST_SCALE_BITS); + } + } + break; +#endif +#ifdef DCT_FLOAT_SUPPORTED + case JDCT_FLOAT: + { + /* For float AA&N IDCT method, multipliers are equal to quantization + * coefficients scaled by scalefactor[row]*scalefactor[col], where + * scalefactor[0] = 1 + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + * The multipliers are stored in natural order. + */ + FLOAT_MULT_TYPE * fmtbl = (FLOAT_MULT_TYPE *) compptr->dct_table; + int row, col; + static const double aanscalefactor[DCTSIZE] = { + 1.0, 1.387039845, 1.306562965, 1.175875602, + 1.0, 0.785694958, 0.541196100, 0.275899379 + }; + + i = 0; + for (row = 0; row < DCTSIZE; row++) { + for (col = 0; col < DCTSIZE; col++) { + fmtbl[i] = (FLOAT_MULT_TYPE) + ((double) qtbl->quantval[jpeg_zigzag_order[i]] * + aanscalefactor[row] * aanscalefactor[col]); + i++; + } + } + } + break; +#endif + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + break; + } + } +} + + +/* + * Initialize IDCT manager. + */ + +GLOBAL void +jinit_inverse_dct (j_decompress_ptr cinfo) +{ + my_idct_ptr idct; + int ci; + jpeg_component_info *compptr; + + idct = (my_idct_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_idct_controller)); + cinfo->idct = (struct jpeg_inverse_dct *) idct; + idct->pub.start_pass = start_pass; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Allocate and pre-zero a multiplier table for each component */ + compptr->dct_table = + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(multiplier_table)); + MEMZERO(compptr->dct_table, SIZEOF(multiplier_table)); + /* Mark multiplier table not yet set up for any method */ + idct->cur_method[ci] = -1; + } +} diff --git a/utils/Libs/jpeg6/jdhuff.cpp b/utils/Libs/jpeg6/jdhuff.cpp new file mode 100644 index 0000000..db42772 --- /dev/null +++ b/utils/Libs/jpeg6/jdhuff.cpp @@ -0,0 +1,574 @@ +/* + * jdhuff.c + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains Huffman entropy decoding routines. + * + * Much of the complexity here has to do with supporting input suspension. + * If the data source module demands suspension, we want to be able to back + * up to the start of the current MCU. To do this, we copy state variables + * into local working storage, and update them back to the permanent + * storage only upon successful completion of an MCU. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdhuff.h" /* Declarations shared with jdphuff.c */ + + +/* + * Expanded entropy decoder object for Huffman decoding. + * + * The savable_state subrecord contains fields that change within an MCU, + * but must not be updated permanently until we complete the MCU. + */ + +typedef struct { + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ +} savable_state; + +/* This macro is to work around compilers with missing or broken + * structure assignment. You'll need to fix this code if you have + * such a compiler and you change MAX_COMPS_IN_SCAN. + */ + +#ifndef NO_STRUCT_ASSIGN +#define ASSIGN_STATE(dest,src) ((dest) = (src)) +#else +#if MAX_COMPS_IN_SCAN == 4 +#define ASSIGN_STATE(dest,src) \ + ((dest).last_dc_val[0] = (src).last_dc_val[0], \ + (dest).last_dc_val[1] = (src).last_dc_val[1], \ + (dest).last_dc_val[2] = (src).last_dc_val[2], \ + (dest).last_dc_val[3] = (src).last_dc_val[3]) +#endif +#endif + + +typedef struct { + struct jpeg_entropy_decoder pub; /* public fields */ + + /* These fields are loaded into local variables at start of each MCU. + * In case of suspension, we exit WITHOUT updating them. + */ + bitread_perm_state bitstate; /* Bit buffer at start of MCU */ + savable_state saved; /* Other state at start of MCU */ + + /* These fields are NOT loaded into local working state. */ + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + + /* Pointers to derived tables (these workspaces have image lifespan) */ + d_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS]; + d_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS]; +} huff_entropy_decoder; + +typedef huff_entropy_decoder * huff_entropy_ptr; + + +/* + * Initialize for a Huffman-compressed scan. + */ + +METHODDEF void +start_pass_huff_decoder (j_decompress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci, dctbl, actbl; + jpeg_component_info * compptr; + + /* Check that the scan parameters Ss, Se, Ah/Al are OK for sequential JPEG. + * This ought to be an error condition, but we make it a warning because + * there are some baseline files out there with all zeroes in these bytes. + */ + if (cinfo->Ss != 0 || cinfo->Se != DCTSIZE2-1 || + cinfo->Ah != 0 || cinfo->Al != 0) + WARNMS(cinfo, JWRN_NOT_SEQUENTIAL); + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + dctbl = compptr->dc_tbl_no; + actbl = compptr->ac_tbl_no; + /* Make sure requested tables are present */ + if (dctbl < 0 || dctbl >= NUM_HUFF_TBLS || + cinfo->dc_huff_tbl_ptrs[dctbl] == NULL) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, dctbl); + if (actbl < 0 || actbl >= NUM_HUFF_TBLS || + cinfo->ac_huff_tbl_ptrs[actbl] == NULL) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, actbl); + /* Compute derived values for Huffman tables */ + /* We may do this more than once for a table, but it's not expensive */ + jpeg_make_d_derived_tbl(cinfo, cinfo->dc_huff_tbl_ptrs[dctbl], + & entropy->dc_derived_tbls[dctbl]); + jpeg_make_d_derived_tbl(cinfo, cinfo->ac_huff_tbl_ptrs[actbl], + & entropy->ac_derived_tbls[actbl]); + /* Initialize DC predictions to 0 */ + entropy->saved.last_dc_val[ci] = 0; + } + + /* Initialize bitread state variables */ + entropy->bitstate.bits_left = 0; + entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ + entropy->bitstate.printed_eod = FALSE; + + /* Initialize restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; +} + + +/* + * Compute the derived values for a Huffman table. + * Note this is also used by jdphuff.c. + */ + +GLOBAL void +jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, JHUFF_TBL * htbl, + d_derived_tbl ** pdtbl) +{ + d_derived_tbl *dtbl; + int p, i, l, si; + int lookbits, ctr; + char huffsize[257]; + unsigned int huffcode[257]; + unsigned int code; + + /* Allocate a workspace if we haven't already done so. */ + if (*pdtbl == NULL) + *pdtbl = (d_derived_tbl *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(d_derived_tbl)); + dtbl = *pdtbl; + dtbl->pub = htbl; /* fill in back link */ + + /* Figure C.1: make table of Huffman code length for each symbol */ + /* Note that this is in code-length order. */ + + p = 0; + for (l = 1; l <= 16; l++) { + for (i = 1; i <= (int) htbl->bits[l]; i++) + huffsize[p++] = (char) l; + } + huffsize[p] = 0; + + /* Figure C.2: generate the codes themselves */ + /* Note that this is in code-length order. */ + + code = 0; + si = huffsize[0]; + p = 0; + while (huffsize[p]) { + while (((int) huffsize[p]) == si) { + huffcode[p++] = code; + code++; + } + code <<= 1; + si++; + } + + /* Figure F.15: generate decoding tables for bit-sequential decoding */ + + p = 0; + for (l = 1; l <= 16; l++) { + if (htbl->bits[l]) { + dtbl->valptr[l] = p; /* huffval[] index of 1st symbol of code length l */ + dtbl->mincode[l] = huffcode[p]; /* minimum code of length l */ + p += htbl->bits[l]; + dtbl->maxcode[l] = huffcode[p-1]; /* maximum code of length l */ + } else { + dtbl->maxcode[l] = -1; /* -1 if no codes of this length */ + } + } + dtbl->maxcode[17] = 0xFFFFFL; /* ensures jpeg_huff_decode terminates */ + + /* Compute lookahead tables to speed up decoding. + * First we set all the table entries to 0, indicating "too long"; + * then we iterate through the Huffman codes that are short enough and + * fill in all the entries that correspond to bit sequences starting + * with that code. + */ + + MEMZERO(dtbl->look_nbits, SIZEOF(dtbl->look_nbits)); + + p = 0; + for (l = 1; l <= HUFF_LOOKAHEAD; l++) { + for (i = 1; i <= (int) htbl->bits[l]; i++, p++) { + /* l = current code's length, p = its index in huffcode[] & huffval[]. */ + /* Generate left-justified code followed by all possible bit sequences */ + lookbits = huffcode[p] << (HUFF_LOOKAHEAD-l); + for (ctr = 1 << (HUFF_LOOKAHEAD-l); ctr > 0; ctr--) { + dtbl->look_nbits[lookbits] = l; + dtbl->look_sym[lookbits] = htbl->huffval[p]; + lookbits++; + } + } + } +} + + +/* + * Out-of-line code for bit fetching (shared with jdphuff.c). + * See jdhuff.h for info about usage. + * Note: current values of get_buffer and bits_left are passed as parameters, + * but are returned in the corresponding fields of the state struct. + * + * On most machines MIN_GET_BITS should be 25 to allow the full 32-bit width + * of get_buffer to be used. (On machines with wider words, an even larger + * buffer could be used.) However, on some machines 32-bit shifts are + * quite slow and take time proportional to the number of places shifted. + * (This is true with most PC compilers, for instance.) In this case it may + * be a win to set MIN_GET_BITS to the minimum value of 15. This reduces the + * average shift distance at the cost of more calls to jpeg_fill_bit_buffer. + */ + +#ifdef SLOW_SHIFT_32 +#define MIN_GET_BITS 15 /* minimum allowable value */ +#else +#define MIN_GET_BITS (BIT_BUF_SIZE-7) +#endif + + +GLOBAL boolean +jpeg_fill_bit_buffer (bitread_working_state * state, + register bit_buf_type get_buffer, register int bits_left, + int nbits) +/* Load up the bit buffer to a depth of at least nbits */ +{ + /* Copy heavily used state fields into locals (hopefully registers) */ + register const JOCTET * next_input_byte = state->next_input_byte; + register size_t bytes_in_buffer = state->bytes_in_buffer; + register int c; + + /* Attempt to load at least MIN_GET_BITS bits into get_buffer. */ + /* (It is assumed that no request will be for more than that many bits.) */ + + while (bits_left < MIN_GET_BITS) { + /* Attempt to read a byte */ + if (state->unread_marker != 0) + goto no_more_data; /* can't advance past a marker */ + + if (bytes_in_buffer == 0) { + if (! (*state->cinfo->src->fill_input_buffer) (state->cinfo)) + return FALSE; + next_input_byte = state->cinfo->src->next_input_byte; + bytes_in_buffer = state->cinfo->src->bytes_in_buffer; + } + bytes_in_buffer--; + c = GETJOCTET(*next_input_byte++); + + /* If it's 0xFF, check and discard stuffed zero byte */ + if (c == 0xFF) { + do { + if (bytes_in_buffer == 0) { + if (! (*state->cinfo->src->fill_input_buffer) (state->cinfo)) + return FALSE; + next_input_byte = state->cinfo->src->next_input_byte; + bytes_in_buffer = state->cinfo->src->bytes_in_buffer; + } + bytes_in_buffer--; + c = GETJOCTET(*next_input_byte++); + } while (c == 0xFF); + + if (c == 0) { + /* Found FF/00, which represents an FF data byte */ + c = 0xFF; + } else { + /* Oops, it's actually a marker indicating end of compressed data. */ + /* Better put it back for use later */ + state->unread_marker = c; + + no_more_data: + /* There should be enough bits still left in the data segment; */ + /* if so, just break out of the outer while loop. */ + if (bits_left >= nbits) + break; + /* Uh-oh. Report corrupted data to user and stuff zeroes into + * the data stream, so that we can produce some kind of image. + * Note that this code will be repeated for each byte demanded + * for the rest of the segment. We use a nonvolatile flag to ensure + * that only one warning message appears. + */ + if (! *(state->printed_eod_ptr)) { + WARNMS(state->cinfo, JWRN_HIT_MARKER); + *(state->printed_eod_ptr) = TRUE; + } + c = 0; /* insert a zero byte into bit buffer */ + } + } + + /* OK, load c into get_buffer */ + get_buffer = (get_buffer << 8) | c; + bits_left += 8; + } + + /* Unload the local registers */ + state->next_input_byte = next_input_byte; + state->bytes_in_buffer = bytes_in_buffer; + state->get_buffer = get_buffer; + state->bits_left = bits_left; + + return TRUE; +} + + +/* + * Out-of-line code for Huffman code decoding. + * See jdhuff.h for info about usage. + */ + +GLOBAL int +jpeg_huff_decode (bitread_working_state * state, + register bit_buf_type get_buffer, register int bits_left, + d_derived_tbl * htbl, int min_bits) +{ + register int l = min_bits; + register INT32 code; + + /* HUFF_DECODE has determined that the code is at least min_bits */ + /* bits long, so fetch that many bits in one swoop. */ + + CHECK_BIT_BUFFER(*state, l, return -1); + code = GET_BITS(l); + + /* Collect the rest of the Huffman code one bit at a time. */ + /* This is per Figure F.16 in the JPEG spec. */ + + while (code > htbl->maxcode[l]) { + code <<= 1; + CHECK_BIT_BUFFER(*state, 1, return -1); + code |= GET_BITS(1); + l++; + } + + /* Unload the local registers */ + state->get_buffer = get_buffer; + state->bits_left = bits_left; + + /* With garbage input we may reach the sentinel value l = 17. */ + + if (l > 16) { + WARNMS(state->cinfo, JWRN_HUFF_BAD_CODE); + return 0; /* fake a zero as the safest result */ + } + + return htbl->pub->huffval[ htbl->valptr[l] + + ((int) (code - htbl->mincode[l])) ]; +} + + +/* + * Figure F.12: extend sign bit. + * On some machines, a shift and add will be faster than a table lookup. + */ + +#ifdef AVOID_TABLES + +#define HUFF_EXTEND(x,s) ((x) < (1<<((s)-1)) ? (x) + (((-1)<<(s)) + 1) : (x)) + +#else + +#define HUFF_EXTEND(x,s) ((x) < extend_test[s] ? (x) + extend_offset[s] : (x)) + +static const int extend_test[16] = /* entry n is 2**(n-1) */ + { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, + 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 }; + +static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */ + { 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1, + ((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1, + ((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1, + ((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 }; + +#endif /* AVOID_TABLES */ + + +/* + * Check for a restart marker & resynchronize decoder. + * Returns FALSE if must suspend. + */ + +LOCAL boolean +process_restart (j_decompress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci; + + /* Throw away any unused bits remaining in bit buffer; */ + /* include any full bytes in next_marker's count of discarded bytes */ + cinfo->marker->discarded_bytes += entropy->bitstate.bits_left / 8; + entropy->bitstate.bits_left = 0; + + /* Advance past the RSTn marker */ + if (! (*cinfo->marker->read_restart_marker) (cinfo)) + return FALSE; + + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) + entropy->saved.last_dc_val[ci] = 0; + + /* Reset restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; + + /* Next segment can get another out-of-data warning */ + entropy->bitstate.printed_eod = FALSE; + + return TRUE; +} + + +/* + * Decode and return one MCU's worth of Huffman-compressed coefficients. + * The coefficients are reordered from zigzag order into natural array order, + * but are not dequantized. + * + * The i'th block of the MCU is stored into the block pointed to by + * MCU_data[i]. WE ASSUME THIS AREA HAS BEEN ZEROED BY THE CALLER. + * (Wholesale zeroing is usually a little faster than retail...) + * + * Returns FALSE if data source requested suspension. In that case no + * changes have been made to permanent state. (Exception: some output + * coefficients may already have been assigned. This is harmless for + * this module, since we'll just re-assign them on the next call.) + */ + +METHODDEF boolean +decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + register int s, k, r; + int blkn, ci; + JBLOCKROW block; + BITREAD_STATE_VARS; + savable_state state; + d_derived_tbl * dctbl; + d_derived_tbl * actbl; + jpeg_component_info * compptr; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restart(cinfo)) + return FALSE; + } + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(state, entropy->saved); + + /* Outer loop handles each block in the MCU */ + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + block = MCU_data[blkn]; + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + dctbl = entropy->dc_derived_tbls[compptr->dc_tbl_no]; + actbl = entropy->ac_derived_tbls[compptr->ac_tbl_no]; + + /* Decode a single block's worth of coefficients */ + + /* Section F.2.2.1: decode the DC coefficient difference */ + HUFF_DECODE(s, br_state, dctbl, return FALSE, label1); + if (s) { + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + } + + /* Shortcut if component's values are not interesting */ + if (! compptr->component_needed) + goto skip_ACs; + + /* Convert DC difference to actual value, update last_dc_val */ + s += state.last_dc_val[ci]; + state.last_dc_val[ci] = s; + /* Output the DC coefficient (assumes jpeg_natural_order[0] = 0) */ + (*block)[0] = (JCOEF) s; + + /* Do we need to decode the AC coefficients for this component? */ + if (compptr->DCT_scaled_size > 1) { + + /* Section F.2.2.2: decode the AC coefficients */ + /* Since zeroes are skipped, output area must be cleared beforehand */ + for (k = 1; k < DCTSIZE2; k++) { + HUFF_DECODE(s, br_state, actbl, return FALSE, label2); + + r = s >> 4; + s &= 15; + + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + /* Output coefficient in natural (dezigzagged) order. + * Note: the extra entries in jpeg_natural_order[] will save us + * if k >= DCTSIZE2, which could happen if the data is corrupted. + */ + (*block)[jpeg_natural_order[k]] = (JCOEF) s; + } else { + if (r != 15) + break; + k += 15; + } + } + + } else { +skip_ACs: + + /* Section F.2.2.2: decode the AC coefficients */ + /* In this path we just discard the values */ + for (k = 1; k < DCTSIZE2; k++) { + HUFF_DECODE(s, br_state, actbl, return FALSE, label3); + + r = s >> 4; + s &= 15; + + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + DROP_BITS(s); + } else { + if (r != 15) + break; + k += 15; + } + } + + } + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(entropy->saved, state); + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; +} + + +/* + * Module initialization routine for Huffman entropy decoding. + */ + +GLOBAL void +jinit_huff_decoder (j_decompress_ptr cinfo) +{ + huff_entropy_ptr entropy; + int i; + + entropy = (huff_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(huff_entropy_decoder)); + cinfo->entropy = (struct jpeg_entropy_decoder *) entropy; + entropy->pub.start_pass = start_pass_huff_decoder; + entropy->pub.decode_mcu = decode_mcu; + + /* Mark tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL; + } +} diff --git a/utils/Libs/jpeg6/jdhuff.h b/utils/Libs/jpeg6/jdhuff.h new file mode 100644 index 0000000..65f3054 --- /dev/null +++ b/utils/Libs/jpeg6/jdhuff.h @@ -0,0 +1,202 @@ +/* + * jdhuff.h + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains declarations for Huffman entropy decoding routines + * that are shared between the sequential decoder (jdhuff.c) and the + * progressive decoder (jdphuff.c). No other modules need to see these. + */ + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_make_d_derived_tbl jMkDDerived +#define jpeg_fill_bit_buffer jFilBitBuf +#define jpeg_huff_decode jHufDecode +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Derived data constructed for each Huffman table */ + +#define HUFF_LOOKAHEAD 8 /* # of bits of lookahead */ + +typedef struct { + /* Basic tables: (element [0] of each array is unused) */ + INT32 mincode[17]; /* smallest code of length k */ + INT32 maxcode[18]; /* largest code of length k (-1 if none) */ + /* (maxcode[17] is a sentinel to ensure jpeg_huff_decode terminates) */ + int valptr[17]; /* huffval[] index of 1st symbol of length k */ + + /* Link to public Huffman table (needed only in jpeg_huff_decode) */ + JHUFF_TBL *pub; + + /* Lookahead tables: indexed by the next HUFF_LOOKAHEAD bits of + * the input data stream. If the next Huffman code is no more + * than HUFF_LOOKAHEAD bits long, we can obtain its length and + * the corresponding symbol directly from these tables. + */ + int look_nbits[1< 32 bits on your machine, and shifting/masking longs is + * reasonably fast, making bit_buf_type be long and setting BIT_BUF_SIZE + * appropriately should be a win. Unfortunately we can't do this with + * something like #define BIT_BUF_SIZE (sizeof(bit_buf_type)*8) + * because not all machines measure sizeof in 8-bit bytes. + */ + +typedef struct { /* Bitreading state saved across MCUs */ + bit_buf_type get_buffer; /* current bit-extraction buffer */ + int bits_left; /* # of unused bits in it */ + boolean printed_eod; /* flag to suppress multiple warning msgs */ +} bitread_perm_state; + +typedef struct { /* Bitreading working state within an MCU */ + /* current data source state */ + const JOCTET * next_input_byte; /* => next byte to read from source */ + size_t bytes_in_buffer; /* # of bytes remaining in source buffer */ + int unread_marker; /* nonzero if we have hit a marker */ + /* bit input buffer --- note these values are kept in register variables, + * not in this struct, inside the inner loops. + */ + bit_buf_type get_buffer; /* current bit-extraction buffer */ + int bits_left; /* # of unused bits in it */ + /* pointers needed by jpeg_fill_bit_buffer */ + j_decompress_ptr cinfo; /* back link to decompress master record */ + boolean * printed_eod_ptr; /* => flag in permanent state */ +} bitread_working_state; + +/* Macros to declare and load/save bitread local variables. */ +#define BITREAD_STATE_VARS \ + register bit_buf_type get_buffer; \ + register int bits_left; \ + bitread_working_state br_state + +#define BITREAD_LOAD_STATE(cinfop,permstate) \ + br_state.cinfo = cinfop; \ + br_state.next_input_byte = cinfop->src->next_input_byte; \ + br_state.bytes_in_buffer = cinfop->src->bytes_in_buffer; \ + br_state.unread_marker = cinfop->unread_marker; \ + get_buffer = permstate.get_buffer; \ + bits_left = permstate.bits_left; \ + br_state.printed_eod_ptr = & permstate.printed_eod + +#define BITREAD_SAVE_STATE(cinfop,permstate) \ + cinfop->src->next_input_byte = br_state.next_input_byte; \ + cinfop->src->bytes_in_buffer = br_state.bytes_in_buffer; \ + cinfop->unread_marker = br_state.unread_marker; \ + permstate.get_buffer = get_buffer; \ + permstate.bits_left = bits_left + +/* + * These macros provide the in-line portion of bit fetching. + * Use CHECK_BIT_BUFFER to ensure there are N bits in get_buffer + * before using GET_BITS, PEEK_BITS, or DROP_BITS. + * The variables get_buffer and bits_left are assumed to be locals, + * but the state struct might not be (jpeg_huff_decode needs this). + * CHECK_BIT_BUFFER(state,n,action); + * Ensure there are N bits in get_buffer; if suspend, take action. + * val = GET_BITS(n); + * Fetch next N bits. + * val = PEEK_BITS(n); + * Fetch next N bits without removing them from the buffer. + * DROP_BITS(n); + * Discard next N bits. + * The value N should be a simple variable, not an expression, because it + * is evaluated multiple times. + */ + +#define CHECK_BIT_BUFFER(state,nbits,action) \ + { if (bits_left < (nbits)) { \ + if (! jpeg_fill_bit_buffer(&(state),get_buffer,bits_left,nbits)) \ + { action; } \ + get_buffer = (state).get_buffer; bits_left = (state).bits_left; } } + +#define GET_BITS(nbits) \ + (((int) (get_buffer >> (bits_left -= (nbits)))) & ((1<<(nbits))-1)) + +#define PEEK_BITS(nbits) \ + (((int) (get_buffer >> (bits_left - (nbits)))) & ((1<<(nbits))-1)) + +#define DROP_BITS(nbits) \ + (bits_left -= (nbits)) + +/* Load up the bit buffer to a depth of at least nbits */ +EXTERN boolean jpeg_fill_bit_buffer JPP((bitread_working_state * state, + register bit_buf_type get_buffer, register int bits_left, + int nbits)); + + +/* + * Code for extracting next Huffman-coded symbol from input bit stream. + * Again, this is time-critical and we make the main paths be macros. + * + * We use a lookahead table to process codes of up to HUFF_LOOKAHEAD bits + * without looping. Usually, more than 95% of the Huffman codes will be 8 + * or fewer bits long. The few overlength codes are handled with a loop, + * which need not be inline code. + * + * Notes about the HUFF_DECODE macro: + * 1. Near the end of the data segment, we may fail to get enough bits + * for a lookahead. In that case, we do it the hard way. + * 2. If the lookahead table contains no entry, the next code must be + * more than HUFF_LOOKAHEAD bits long. + * 3. jpeg_huff_decode returns -1 if forced to suspend. + */ + +#define HUFF_DECODE(result,state,htbl,failaction,slowlabel) \ +{ register int nb, look; \ + if (bits_left < HUFF_LOOKAHEAD) { \ + if (! jpeg_fill_bit_buffer(&state,get_buffer,bits_left, 0)) {failaction;} \ + get_buffer = state.get_buffer; bits_left = state.bits_left; \ + if (bits_left < HUFF_LOOKAHEAD) { \ + nb = 1; goto slowlabel; \ + } \ + } \ + look = PEEK_BITS(HUFF_LOOKAHEAD); \ + if ((nb = htbl->look_nbits[look]) != 0) { \ + DROP_BITS(nb); \ + result = htbl->look_sym[look]; \ + } else { \ + nb = HUFF_LOOKAHEAD+1; \ +slowlabel: \ + if ((result=jpeg_huff_decode(&state,get_buffer,bits_left,htbl,nb)) < 0) \ + { failaction; } \ + get_buffer = state.get_buffer; bits_left = state.bits_left; \ + } \ +} + +/* Out-of-line case for Huffman code fetching */ +EXTERN int jpeg_huff_decode JPP((bitread_working_state * state, + register bit_buf_type get_buffer, register int bits_left, + d_derived_tbl * htbl, int min_bits)); diff --git a/utils/Libs/jpeg6/jdinput.cpp b/utils/Libs/jpeg6/jdinput.cpp new file mode 100644 index 0000000..5b4774f --- /dev/null +++ b/utils/Libs/jpeg6/jdinput.cpp @@ -0,0 +1,381 @@ +/* + * jdinput.c + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains input control logic for the JPEG decompressor. + * These routines are concerned with controlling the decompressor's input + * processing (marker reading and coefficient decoding). The actual input + * reading is done in jdmarker.c, jdhuff.c, and jdphuff.c. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Private state */ + +typedef struct { + struct jpeg_input_controller pub; /* public fields */ + + boolean inheaders; /* TRUE until first SOS is reached */ +} my_input_controller; + +typedef my_input_controller * my_inputctl_ptr; + + +/* Forward declarations */ +METHODDEF int consume_markers JPP((j_decompress_ptr cinfo)); + + +/* + * Routines to calculate various quantities related to the size of the image. + */ + +LOCAL void +initial_setup (j_decompress_ptr cinfo) +/* Called once, when first SOS marker is reached */ +{ + int ci; + jpeg_component_info *compptr; + + /* Make sure image isn't bigger than I can handle */ + if ((long) cinfo->image_height > (long) JPEG_MAX_DIMENSION || + (long) cinfo->image_width > (long) JPEG_MAX_DIMENSION) + ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) JPEG_MAX_DIMENSION); + + /* For now, precision must match compiled-in value... */ + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + + /* Check that number of components won't exceed internal array sizes */ + if (cinfo->num_components > MAX_COMPONENTS) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, + MAX_COMPONENTS); + + /* Compute maximum sampling factors; check factor validity */ + cinfo->max_h_samp_factor = 1; + cinfo->max_v_samp_factor = 1; + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + if (compptr->h_samp_factor<=0 || compptr->h_samp_factor>MAX_SAMP_FACTOR || + compptr->v_samp_factor<=0 || compptr->v_samp_factor>MAX_SAMP_FACTOR) + ERREXIT(cinfo, JERR_BAD_SAMPLING); + cinfo->max_h_samp_factor = MAX(cinfo->max_h_samp_factor, + compptr->h_samp_factor); + cinfo->max_v_samp_factor = MAX(cinfo->max_v_samp_factor, + compptr->v_samp_factor); + } + + /* We initialize DCT_scaled_size and min_DCT_scaled_size to DCTSIZE. + * In the full decompressor, this will be overridden by jdmaster.c; + * but in the transcoder, jdmaster.c is not used, so we must do it here. + */ + cinfo->min_DCT_scaled_size = DCTSIZE; + + /* Compute dimensions of components */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + compptr->DCT_scaled_size = DCTSIZE; + /* Size in DCT blocks */ + compptr->width_in_blocks = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, + (long) (cinfo->max_h_samp_factor * DCTSIZE)); + compptr->height_in_blocks = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, + (long) (cinfo->max_v_samp_factor * DCTSIZE)); + /* downsampled_width and downsampled_height will also be overridden by + * jdmaster.c if we are doing full decompression. The transcoder library + * doesn't use these values, but the calling application might. + */ + /* Size in samples */ + compptr->downsampled_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, + (long) cinfo->max_h_samp_factor); + compptr->downsampled_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, + (long) cinfo->max_v_samp_factor); + /* Mark component needed, until color conversion says otherwise */ + compptr->component_needed = TRUE; + /* Mark no quantization table yet saved for component */ + compptr->quant_table = NULL; + } + + /* Compute number of fully interleaved MCU rows. */ + cinfo->total_iMCU_rows = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, + (long) (cinfo->max_v_samp_factor*DCTSIZE)); + + /* Decide whether file contains multiple scans */ + if (cinfo->comps_in_scan < cinfo->num_components || cinfo->progressive_mode) + cinfo->inputctl->has_multiple_scans = TRUE; + else + cinfo->inputctl->has_multiple_scans = FALSE; +} + + +LOCAL void +per_scan_setup (j_decompress_ptr cinfo) +/* Do computations that are needed before processing a JPEG scan */ +/* cinfo->comps_in_scan and cinfo->cur_comp_info[] were set from SOS marker */ +{ + int ci, mcublks, tmp; + jpeg_component_info *compptr; + + if (cinfo->comps_in_scan == 1) { + + /* Noninterleaved (single-component) scan */ + compptr = cinfo->cur_comp_info[0]; + + /* Overall image size in MCUs */ + cinfo->MCUs_per_row = compptr->width_in_blocks; + cinfo->MCU_rows_in_scan = compptr->height_in_blocks; + + /* For noninterleaved scan, always one block per MCU */ + compptr->MCU_width = 1; + compptr->MCU_height = 1; + compptr->MCU_blocks = 1; + compptr->MCU_sample_width = compptr->DCT_scaled_size; + compptr->last_col_width = 1; + /* For noninterleaved scans, it is convenient to define last_row_height + * as the number of block rows present in the last iMCU row. + */ + tmp = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (tmp == 0) tmp = compptr->v_samp_factor; + compptr->last_row_height = tmp; + + /* Prepare array describing MCU composition */ + cinfo->blocks_in_MCU = 1; + cinfo->MCU_membership[0] = 0; + + } else { + + /* Interleaved (multi-component) scan */ + if (cinfo->comps_in_scan <= 0 || cinfo->comps_in_scan > MAX_COMPS_IN_SCAN) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->comps_in_scan, + MAX_COMPS_IN_SCAN); + + /* Overall image size in MCUs */ + cinfo->MCUs_per_row = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, + (long) (cinfo->max_h_samp_factor*DCTSIZE)); + cinfo->MCU_rows_in_scan = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, + (long) (cinfo->max_v_samp_factor*DCTSIZE)); + + cinfo->blocks_in_MCU = 0; + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Sampling factors give # of blocks of component in each MCU */ + compptr->MCU_width = compptr->h_samp_factor; + compptr->MCU_height = compptr->v_samp_factor; + compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height; + compptr->MCU_sample_width = compptr->MCU_width * compptr->DCT_scaled_size; + /* Figure number of non-dummy blocks in last MCU column & row */ + tmp = (int) (compptr->width_in_blocks % compptr->MCU_width); + if (tmp == 0) tmp = compptr->MCU_width; + compptr->last_col_width = tmp; + tmp = (int) (compptr->height_in_blocks % compptr->MCU_height); + if (tmp == 0) tmp = compptr->MCU_height; + compptr->last_row_height = tmp; + /* Prepare array describing MCU composition */ + mcublks = compptr->MCU_blocks; + if (cinfo->blocks_in_MCU + mcublks > D_MAX_BLOCKS_IN_MCU) + ERREXIT(cinfo, JERR_BAD_MCU_SIZE); + while (mcublks-- > 0) { + cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci; + } + } + + } +} + + +/* + * Save away a copy of the Q-table referenced by each component present + * in the current scan, unless already saved during a prior scan. + * + * In a multiple-scan JPEG file, the encoder could assign different components + * the same Q-table slot number, but change table definitions between scans + * so that each component uses a different Q-table. (The IJG encoder is not + * currently capable of doing this, but other encoders might.) Since we want + * to be able to dequantize all the components at the end of the file, this + * means that we have to save away the table actually used for each component. + * We do this by copying the table at the start of the first scan containing + * the component. + * The JPEG spec prohibits the encoder from changing the contents of a Q-table + * slot between scans of a component using that slot. If the encoder does so + * anyway, this decoder will simply use the Q-table values that were current + * at the start of the first scan for the component. + * + * The decompressor output side looks only at the saved quant tables, + * not at the current Q-table slots. + */ + +LOCAL void +latch_quant_tables (j_decompress_ptr cinfo) +{ + int ci, qtblno; + jpeg_component_info *compptr; + JQUANT_TBL * qtbl; + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* No work if we already saved Q-table for this component */ + if (compptr->quant_table != NULL) + continue; + /* Make sure specified quantization table is present */ + qtblno = compptr->quant_tbl_no; + if (qtblno < 0 || qtblno >= NUM_QUANT_TBLS || + cinfo->quant_tbl_ptrs[qtblno] == NULL) + ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, qtblno); + /* OK, save away the quantization table */ + qtbl = (JQUANT_TBL *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(JQUANT_TBL)); + MEMCOPY(qtbl, cinfo->quant_tbl_ptrs[qtblno], SIZEOF(JQUANT_TBL)); + compptr->quant_table = qtbl; + } +} + + +/* + * Initialize the input modules to read a scan of compressed data. + * The first call to this is done by jdmaster.c after initializing + * the entire decompressor (during jpeg_start_decompress). + * Subsequent calls come from consume_markers, below. + */ + +METHODDEF void +start_input_pass (j_decompress_ptr cinfo) +{ + per_scan_setup(cinfo); + latch_quant_tables(cinfo); + (*cinfo->entropy->start_pass) (cinfo); + (*cinfo->coef->start_input_pass) (cinfo); + cinfo->inputctl->consume_input = cinfo->coef->consume_data; +} + + +/* + * Finish up after inputting a compressed-data scan. + * This is called by the coefficient controller after it's read all + * the expected data of the scan. + */ + +METHODDEF void +finish_input_pass (j_decompress_ptr cinfo) +{ + cinfo->inputctl->consume_input = consume_markers; +} + + +/* + * Read JPEG markers before, between, or after compressed-data scans. + * Change state as necessary when a new scan is reached. + * Return value is JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + * + * The consume_input method pointer points either here or to the + * coefficient controller's consume_data routine, depending on whether + * we are reading a compressed data segment or inter-segment markers. + */ + +METHODDEF int +consume_markers (j_decompress_ptr cinfo) +{ + my_inputctl_ptr inputctl = (my_inputctl_ptr) cinfo->inputctl; + int val; + + if (inputctl->pub.eoi_reached) /* After hitting EOI, read no further */ + return JPEG_REACHED_EOI; + + val = (*cinfo->marker->read_markers) (cinfo); + + switch (val) { + case JPEG_REACHED_SOS: /* Found SOS */ + if (inputctl->inheaders) { /* 1st SOS */ + initial_setup(cinfo); + inputctl->inheaders = FALSE; + /* Note: start_input_pass must be called by jdmaster.c + * before any more input can be consumed. jdapi.c is + * responsible for enforcing this sequencing. + */ + } else { /* 2nd or later SOS marker */ + if (! inputctl->pub.has_multiple_scans) + ERREXIT(cinfo, JERR_EOI_EXPECTED); /* Oops, I wasn't expecting this! */ + start_input_pass(cinfo); + } + break; + case JPEG_REACHED_EOI: /* Found EOI */ + inputctl->pub.eoi_reached = TRUE; + if (inputctl->inheaders) { /* Tables-only datastream, apparently */ + if (cinfo->marker->saw_SOF) + ERREXIT(cinfo, JERR_SOF_NO_SOS); + } else { + /* Prevent infinite loop in coef ctlr's decompress_data routine + * if user set output_scan_number larger than number of scans. + */ + if (cinfo->output_scan_number > cinfo->input_scan_number) + cinfo->output_scan_number = cinfo->input_scan_number; + } + break; + case JPEG_SUSPENDED: + break; + } + + return val; +} + + +/* + * Reset state to begin a fresh datastream. + */ + +METHODDEF void +reset_input_controller (j_decompress_ptr cinfo) +{ + my_inputctl_ptr inputctl = (my_inputctl_ptr) cinfo->inputctl; + + inputctl->pub.consume_input = consume_markers; + inputctl->pub.has_multiple_scans = FALSE; /* "unknown" would be better */ + inputctl->pub.eoi_reached = FALSE; + inputctl->inheaders = TRUE; + /* Reset other modules */ + (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo); + (*cinfo->marker->reset_marker_reader) (cinfo); + /* Reset progression state -- would be cleaner if entropy decoder did this */ + cinfo->coef_bits = NULL; +} + + +/* + * Initialize the input controller module. + * This is called only once, when the decompression object is created. + */ + +GLOBAL void +jinit_input_controller (j_decompress_ptr cinfo) +{ + my_inputctl_ptr inputctl; + + /* Create subobject in permanent pool */ + inputctl = (my_inputctl_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_input_controller)); + cinfo->inputctl = (struct jpeg_input_controller *) inputctl; + /* Initialize method pointers */ + inputctl->pub.consume_input = consume_markers; + inputctl->pub.reset_input_controller = reset_input_controller; + inputctl->pub.start_input_pass = start_input_pass; + inputctl->pub.finish_input_pass = finish_input_pass; + /* Initialize state: can't use reset_input_controller since we don't + * want to try to reset other modules yet. + */ + inputctl->pub.has_multiple_scans = FALSE; /* "unknown" would be better */ + inputctl->pub.eoi_reached = FALSE; + inputctl->inheaders = TRUE; +} diff --git a/utils/Libs/jpeg6/jdmainct.cpp b/utils/Libs/jpeg6/jdmainct.cpp new file mode 100644 index 0000000..c4e0d54 --- /dev/null +++ b/utils/Libs/jpeg6/jdmainct.cpp @@ -0,0 +1,512 @@ +/* + * jdmainct.c + * + * Copyright (C) 1994-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the main buffer controller for decompression. + * The main buffer lies between the JPEG decompressor proper and the + * post-processor; it holds downsampled data in the JPEG colorspace. + * + * Note that this code is bypassed in raw-data mode, since the application + * supplies the equivalent of the main buffer in that case. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * In the current system design, the main buffer need never be a full-image + * buffer; any full-height buffers will be found inside the coefficient or + * postprocessing controllers. Nonetheless, the main controller is not + * trivial. Its responsibility is to provide context rows for upsampling/ + * rescaling, and doing this in an efficient fashion is a bit tricky. + * + * Postprocessor input data is counted in "row groups". A row group + * is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) + * sample rows of each component. (We require DCT_scaled_size values to be + * chosen such that these numbers are integers. In practice DCT_scaled_size + * values will likely be powers of two, so we actually have the stronger + * condition that DCT_scaled_size / min_DCT_scaled_size is an integer.) + * Upsampling will typically produce max_v_samp_factor pixel rows from each + * row group (times any additional scale factor that the upsampler is + * applying). + * + * The coefficient controller will deliver data to us one iMCU row at a time; + * each iMCU row contains v_samp_factor * DCT_scaled_size sample rows, or + * exactly min_DCT_scaled_size row groups. (This amount of data corresponds + * to one row of MCUs when the image is fully interleaved.) Note that the + * number of sample rows varies across components, but the number of row + * groups does not. Some garbage sample rows may be included in the last iMCU + * row at the bottom of the image. + * + * Depending on the vertical scaling algorithm used, the upsampler may need + * access to the sample row(s) above and below its current input row group. + * The upsampler is required to set need_context_rows TRUE at global selection + * time if so. When need_context_rows is FALSE, this controller can simply + * obtain one iMCU row at a time from the coefficient controller and dole it + * out as row groups to the postprocessor. + * + * When need_context_rows is TRUE, this controller guarantees that the buffer + * passed to postprocessing contains at least one row group's worth of samples + * above and below the row group(s) being processed. Note that the context + * rows "above" the first passed row group appear at negative row offsets in + * the passed buffer. At the top and bottom of the image, the required + * context rows are manufactured by duplicating the first or last real sample + * row; this avoids having special cases in the upsampling inner loops. + * + * The amount of context is fixed at one row group just because that's a + * convenient number for this controller to work with. The existing + * upsamplers really only need one sample row of context. An upsampler + * supporting arbitrary output rescaling might wish for more than one row + * group of context when shrinking the image; tough, we don't handle that. + * (This is justified by the assumption that downsizing will be handled mostly + * by adjusting the DCT_scaled_size values, so that the actual scale factor at + * the upsample step needn't be much less than one.) + * + * To provide the desired context, we have to retain the last two row groups + * of one iMCU row while reading in the next iMCU row. (The last row group + * can't be processed until we have another row group for its below-context, + * and so we have to save the next-to-last group too for its above-context.) + * We could do this most simply by copying data around in our buffer, but + * that'd be very slow. We can avoid copying any data by creating a rather + * strange pointer structure. Here's how it works. We allocate a workspace + * consisting of M+2 row groups (where M = min_DCT_scaled_size is the number + * of row groups per iMCU row). We create two sets of redundant pointers to + * the workspace. Labeling the physical row groups 0 to M+1, the synthesized + * pointer lists look like this: + * M+1 M-1 + * master pointer --> 0 master pointer --> 0 + * 1 1 + * ... ... + * M-3 M-3 + * M-2 M + * M-1 M+1 + * M M-2 + * M+1 M-1 + * 0 0 + * We read alternate iMCU rows using each master pointer; thus the last two + * row groups of the previous iMCU row remain un-overwritten in the workspace. + * The pointer lists are set up so that the required context rows appear to + * be adjacent to the proper places when we pass the pointer lists to the + * upsampler. + * + * The above pictures describe the normal state of the pointer lists. + * At top and bottom of the image, we diddle the pointer lists to duplicate + * the first or last sample row as necessary (this is cheaper than copying + * sample rows around). + * + * This scheme breaks down if M < 2, ie, min_DCT_scaled_size is 1. In that + * situation each iMCU row provides only one row group so the buffering logic + * must be different (eg, we must read two iMCU rows before we can emit the + * first row group). For now, we simply do not support providing context + * rows when min_DCT_scaled_size is 1. That combination seems unlikely to + * be worth providing --- if someone wants a 1/8th-size preview, they probably + * want it quick and dirty, so a context-free upsampler is sufficient. + */ + + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_d_main_controller pub; /* public fields */ + + /* Pointer to allocated workspace (M or M+2 row groups). */ + JSAMPARRAY buffer[MAX_COMPONENTS]; + + boolean buffer_full; /* Have we gotten an iMCU row from decoder? */ + JDIMENSION rowgroup_ctr; /* counts row groups output to postprocessor */ + + /* Remaining fields are only used in the context case. */ + + /* These are the master pointers to the funny-order pointer lists. */ + JSAMPIMAGE xbuffer[2]; /* pointers to weird pointer lists */ + + int whichptr; /* indicates which pointer set is now in use */ + int context_state; /* process_data state machine status */ + JDIMENSION rowgroups_avail; /* row groups available to postprocessor */ + JDIMENSION iMCU_row_ctr; /* counts iMCU rows to detect image top/bot */ +} my_main_controller; + +typedef my_main_controller * my_main_ptr; + +/* context_state values: */ +#define CTX_PREPARE_FOR_IMCU 0 /* need to prepare for MCU row */ +#define CTX_PROCESS_IMCU 1 /* feeding iMCU to postprocessor */ +#define CTX_POSTPONED_ROW 2 /* feeding postponed row group */ + + +/* Forward declarations */ +METHODDEF void process_data_simple_main + JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)); +METHODDEF void process_data_context_main + JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)); +#ifdef QUANT_2PASS_SUPPORTED +METHODDEF void process_data_crank_post + JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)); +#endif + + +LOCAL void +alloc_funny_pointers (j_decompress_ptr cinfo) +/* Allocate space for the funny pointer lists. + * This is done only once, not once per pass. + */ +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + int ci, rgroup; + int M = cinfo->min_DCT_scaled_size; + jpeg_component_info *compptr; + JSAMPARRAY xbuf; + + /* Get top-level space for component array pointers. + * We alloc both arrays with one call to save a few cycles. + */ + main->xbuffer[0] = (JSAMPIMAGE) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->num_components * 2 * SIZEOF(JSAMPARRAY)); + main->xbuffer[1] = main->xbuffer[0] + cinfo->num_components; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + /* Get space for pointer lists --- M+4 row groups in each list. + * We alloc both pointer lists with one call to save a few cycles. + */ + xbuf = (JSAMPARRAY) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + 2 * (rgroup * (M + 4)) * SIZEOF(JSAMPROW)); + xbuf += rgroup; /* want one row group at negative offsets */ + main->xbuffer[0][ci] = xbuf; + xbuf += rgroup * (M + 4); + main->xbuffer[1][ci] = xbuf; + } +} + + +LOCAL void +make_funny_pointers (j_decompress_ptr cinfo) +/* Create the funny pointer lists discussed in the comments above. + * The actual workspace is already allocated (in main->buffer), + * and the space for the pointer lists is allocated too. + * This routine just fills in the curiously ordered lists. + * This will be repeated at the beginning of each pass. + */ +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + int ci, i, rgroup; + int M = cinfo->min_DCT_scaled_size; + jpeg_component_info *compptr; + JSAMPARRAY buf, xbuf0, xbuf1; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + xbuf0 = main->xbuffer[0][ci]; + xbuf1 = main->xbuffer[1][ci]; + /* First copy the workspace pointers as-is */ + buf = main->buffer[ci]; + for (i = 0; i < rgroup * (M + 2); i++) { + xbuf0[i] = xbuf1[i] = buf[i]; + } + /* In the second list, put the last four row groups in swapped order */ + for (i = 0; i < rgroup * 2; i++) { + xbuf1[rgroup*(M-2) + i] = buf[rgroup*M + i]; + xbuf1[rgroup*M + i] = buf[rgroup*(M-2) + i]; + } + /* The wraparound pointers at top and bottom will be filled later + * (see set_wraparound_pointers, below). Initially we want the "above" + * pointers to duplicate the first actual data line. This only needs + * to happen in xbuffer[0]. + */ + for (i = 0; i < rgroup; i++) { + xbuf0[i - rgroup] = xbuf0[0]; + } + } +} + + +LOCAL void +set_wraparound_pointers (j_decompress_ptr cinfo) +/* Set up the "wraparound" pointers at top and bottom of the pointer lists. + * This changes the pointer list state from top-of-image to the normal state. + */ +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + int ci, i, rgroup; + int M = cinfo->min_DCT_scaled_size; + jpeg_component_info *compptr; + JSAMPARRAY xbuf0, xbuf1; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + xbuf0 = main->xbuffer[0][ci]; + xbuf1 = main->xbuffer[1][ci]; + for (i = 0; i < rgroup; i++) { + xbuf0[i - rgroup] = xbuf0[rgroup*(M+1) + i]; + xbuf1[i - rgroup] = xbuf1[rgroup*(M+1) + i]; + xbuf0[rgroup*(M+2) + i] = xbuf0[i]; + xbuf1[rgroup*(M+2) + i] = xbuf1[i]; + } + } +} + + +LOCAL void +set_bottom_pointers (j_decompress_ptr cinfo) +/* Change the pointer lists to duplicate the last sample row at the bottom + * of the image. whichptr indicates which xbuffer holds the final iMCU row. + * Also sets rowgroups_avail to indicate number of nondummy row groups in row. + */ +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + int ci, i, rgroup, iMCUheight, rows_left; + jpeg_component_info *compptr; + JSAMPARRAY xbuf; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Count sample rows in one iMCU row and in one row group */ + iMCUheight = compptr->v_samp_factor * compptr->DCT_scaled_size; + rgroup = iMCUheight / cinfo->min_DCT_scaled_size; + /* Count nondummy sample rows remaining for this component */ + rows_left = (int) (compptr->downsampled_height % (JDIMENSION) iMCUheight); + if (rows_left == 0) rows_left = iMCUheight; + /* Count nondummy row groups. Should get same answer for each component, + * so we need only do it once. + */ + if (ci == 0) { + main->rowgroups_avail = (JDIMENSION) ((rows_left-1) / rgroup + 1); + } + /* Duplicate the last real sample row rgroup*2 times; this pads out the + * last partial rowgroup and ensures at least one full rowgroup of context. + */ + xbuf = main->xbuffer[main->whichptr][ci]; + for (i = 0; i < rgroup * 2; i++) { + xbuf[rows_left + i] = xbuf[rows_left-1]; + } + } +} + + +/* + * Initialize for a processing pass. + */ + +METHODDEF void +start_pass_main (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + + switch (pass_mode) { + case JBUF_PASS_THRU: + if (cinfo->upsample->need_context_rows) { + main->pub.process_data = process_data_context_main; + make_funny_pointers(cinfo); /* Create the xbuffer[] lists */ + main->whichptr = 0; /* Read first iMCU row into xbuffer[0] */ + main->context_state = CTX_PREPARE_FOR_IMCU; + main->iMCU_row_ctr = 0; + } else { + /* Simple case with no context needed */ + main->pub.process_data = process_data_simple_main; + } + main->buffer_full = FALSE; /* Mark buffer empty */ + main->rowgroup_ctr = 0; + break; +#ifdef QUANT_2PASS_SUPPORTED + case JBUF_CRANK_DEST: + /* For last pass of 2-pass quantization, just crank the postprocessor */ + main->pub.process_data = process_data_crank_post; + break; +#endif + default: + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + break; + } +} + + +/* + * Process some data. + * This handles the simple case where no context is required. + */ + +METHODDEF void +process_data_simple_main (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + JDIMENSION rowgroups_avail; + + /* Read input data if we haven't filled the main buffer yet */ + if (! main->buffer_full) { + if (! (*cinfo->coef->decompress_data) (cinfo, main->buffer)) + return; /* suspension forced, can do nothing more */ + main->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ + } + + /* There are always min_DCT_scaled_size row groups in an iMCU row. */ + rowgroups_avail = (JDIMENSION) cinfo->min_DCT_scaled_size; + /* Note: at the bottom of the image, we may pass extra garbage row groups + * to the postprocessor. The postprocessor has to check for bottom + * of image anyway (at row resolution), so no point in us doing it too. + */ + + /* Feed the postprocessor */ + (*cinfo->post->post_process_data) (cinfo, main->buffer, + &main->rowgroup_ctr, rowgroups_avail, + output_buf, out_row_ctr, out_rows_avail); + + /* Has postprocessor consumed all the data yet? If so, mark buffer empty */ + if (main->rowgroup_ctr >= rowgroups_avail) { + main->buffer_full = FALSE; + main->rowgroup_ctr = 0; + } +} + + +/* + * Process some data. + * This handles the case where context rows must be provided. + */ + +METHODDEF void +process_data_context_main (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + + /* Read input data if we haven't filled the main buffer yet */ + if (! main->buffer_full) { + if (! (*cinfo->coef->decompress_data) (cinfo, + main->xbuffer[main->whichptr])) + return; /* suspension forced, can do nothing more */ + main->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ + main->iMCU_row_ctr++; /* count rows received */ + } + + /* Postprocessor typically will not swallow all the input data it is handed + * in one call (due to filling the output buffer first). Must be prepared + * to exit and restart. This switch lets us keep track of how far we got. + * Note that each case falls through to the next on successful completion. + */ + switch (main->context_state) { + case CTX_POSTPONED_ROW: + /* Call postprocessor using previously set pointers for postponed row */ + (*cinfo->post->post_process_data) (cinfo, main->xbuffer[main->whichptr], + &main->rowgroup_ctr, main->rowgroups_avail, + output_buf, out_row_ctr, out_rows_avail); + if (main->rowgroup_ctr < main->rowgroups_avail) + return; /* Need to suspend */ + main->context_state = CTX_PREPARE_FOR_IMCU; + if (*out_row_ctr >= out_rows_avail) + return; /* Postprocessor exactly filled output buf */ + /*FALLTHROUGH*/ + case CTX_PREPARE_FOR_IMCU: + /* Prepare to process first M-1 row groups of this iMCU row */ + main->rowgroup_ctr = 0; + main->rowgroups_avail = (JDIMENSION) (cinfo->min_DCT_scaled_size - 1); + /* Check for bottom of image: if so, tweak pointers to "duplicate" + * the last sample row, and adjust rowgroups_avail to ignore padding rows. + */ + if (main->iMCU_row_ctr == cinfo->total_iMCU_rows) + set_bottom_pointers(cinfo); + main->context_state = CTX_PROCESS_IMCU; + /*FALLTHROUGH*/ + case CTX_PROCESS_IMCU: + /* Call postprocessor using previously set pointers */ + (*cinfo->post->post_process_data) (cinfo, main->xbuffer[main->whichptr], + &main->rowgroup_ctr, main->rowgroups_avail, + output_buf, out_row_ctr, out_rows_avail); + if (main->rowgroup_ctr < main->rowgroups_avail) + return; /* Need to suspend */ + /* After the first iMCU, change wraparound pointers to normal state */ + if (main->iMCU_row_ctr == 1) + set_wraparound_pointers(cinfo); + /* Prepare to load new iMCU row using other xbuffer list */ + main->whichptr ^= 1; /* 0=>1 or 1=>0 */ + main->buffer_full = FALSE; + /* Still need to process last row group of this iMCU row, */ + /* which is saved at index M+1 of the other xbuffer */ + main->rowgroup_ctr = (JDIMENSION) (cinfo->min_DCT_scaled_size + 1); + main->rowgroups_avail = (JDIMENSION) (cinfo->min_DCT_scaled_size + 2); + main->context_state = CTX_POSTPONED_ROW; + } +} + + +/* + * Process some data. + * Final pass of two-pass quantization: just call the postprocessor. + * Source data will be the postprocessor controller's internal buffer. + */ + +#ifdef QUANT_2PASS_SUPPORTED + +METHODDEF void +process_data_crank_post (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + (*cinfo->post->post_process_data) (cinfo, (JSAMPIMAGE) NULL, + (JDIMENSION *) NULL, (JDIMENSION) 0, + output_buf, out_row_ctr, out_rows_avail); +} + +#endif /* QUANT_2PASS_SUPPORTED */ + + +/* + * Initialize main buffer controller. + */ + +GLOBAL void +jinit_d_main_controller (j_decompress_ptr cinfo, boolean need_full_buffer) +{ + my_main_ptr main; + int ci, rgroup, ngroups; + jpeg_component_info *compptr; + + main = (my_main_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_main_controller)); + cinfo->main = (struct jpeg_d_main_controller *) main; + main->pub.start_pass = start_pass_main; + + if (need_full_buffer) /* shouldn't happen */ + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + /* Allocate the workspace. + * ngroups is the number of row groups we need. + */ + if (cinfo->upsample->need_context_rows) { + if (cinfo->min_DCT_scaled_size < 2) /* unsupported, see comments above */ + ERREXIT(cinfo, JERR_NOTIMPL); + alloc_funny_pointers(cinfo); /* Alloc space for xbuffer[] lists */ + ngroups = cinfo->min_DCT_scaled_size + 2; + } else { + ngroups = cinfo->min_DCT_scaled_size; + } + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + main->buffer[ci] = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + compptr->width_in_blocks * compptr->DCT_scaled_size, + (JDIMENSION) (rgroup * ngroups)); + } +} diff --git a/utils/Libs/jpeg6/jdmarker.cpp b/utils/Libs/jpeg6/jdmarker.cpp new file mode 100644 index 0000000..6c3c519 --- /dev/null +++ b/utils/Libs/jpeg6/jdmarker.cpp @@ -0,0 +1,1052 @@ +/* + * jdmarker.c + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to decode JPEG datastream markers. + * Most of the complexity arises from our desire to support input + * suspension: if not all of the data for a marker is available, + * we must exit back to the application. On resumption, we reprocess + * the marker. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +typedef enum { /* JPEG marker codes */ + M_SOF0 = 0xc0, + M_SOF1 = 0xc1, + M_SOF2 = 0xc2, + M_SOF3 = 0xc3, + + M_SOF5 = 0xc5, + M_SOF6 = 0xc6, + M_SOF7 = 0xc7, + + M_JPG = 0xc8, + M_SOF9 = 0xc9, + M_SOF10 = 0xca, + M_SOF11 = 0xcb, + + M_SOF13 = 0xcd, + M_SOF14 = 0xce, + M_SOF15 = 0xcf, + + M_DHT = 0xc4, + + M_DAC = 0xcc, + + M_RST0 = 0xd0, + M_RST1 = 0xd1, + M_RST2 = 0xd2, + M_RST3 = 0xd3, + M_RST4 = 0xd4, + M_RST5 = 0xd5, + M_RST6 = 0xd6, + M_RST7 = 0xd7, + + M_SOI = 0xd8, + M_EOI = 0xd9, + M_SOS = 0xda, + M_DQT = 0xdb, + M_DNL = 0xdc, + M_DRI = 0xdd, + M_DHP = 0xde, + M_EXP = 0xdf, + + M_APP0 = 0xe0, + M_APP1 = 0xe1, + M_APP2 = 0xe2, + M_APP3 = 0xe3, + M_APP4 = 0xe4, + M_APP5 = 0xe5, + M_APP6 = 0xe6, + M_APP7 = 0xe7, + M_APP8 = 0xe8, + M_APP9 = 0xe9, + M_APP10 = 0xea, + M_APP11 = 0xeb, + M_APP12 = 0xec, + M_APP13 = 0xed, + M_APP14 = 0xee, + M_APP15 = 0xef, + + M_JPG0 = 0xf0, + M_JPG13 = 0xfd, + M_COM = 0xfe, + + M_TEM = 0x01, + + M_ERROR = 0x100 +} JPEG_MARKER; + + +/* + * Macros for fetching data from the data source module. + * + * At all times, cinfo->src->next_input_byte and ->bytes_in_buffer reflect + * the current restart point; we update them only when we have reached a + * suitable place to restart if a suspension occurs. + */ + +/* Declare and initialize local copies of input pointer/count */ +#define INPUT_VARS(cinfo) \ + struct jpeg_source_mgr * datasrc = (cinfo)->src; \ + const JOCTET * next_input_byte = datasrc->next_input_byte; \ + size_t bytes_in_buffer = datasrc->bytes_in_buffer + +/* Unload the local copies --- do this only at a restart boundary */ +#define INPUT_SYNC(cinfo) \ + ( datasrc->next_input_byte = next_input_byte, \ + datasrc->bytes_in_buffer = bytes_in_buffer ) + +/* Reload the local copies --- seldom used except in MAKE_BYTE_AVAIL */ +#define INPUT_RELOAD(cinfo) \ + ( next_input_byte = datasrc->next_input_byte, \ + bytes_in_buffer = datasrc->bytes_in_buffer ) + +/* Internal macro for INPUT_BYTE and INPUT_2BYTES: make a byte available. + * Note we do *not* do INPUT_SYNC before calling fill_input_buffer, + * but we must reload the local copies after a successful fill. + */ +#define MAKE_BYTE_AVAIL(cinfo,action) \ + if (bytes_in_buffer == 0) { \ + if (! (*datasrc->fill_input_buffer) (cinfo)) \ + { action; } \ + INPUT_RELOAD(cinfo); \ + } \ + bytes_in_buffer-- + +/* Read a byte into variable V. + * If must suspend, take the specified action (typically "return FALSE"). + */ +#define INPUT_BYTE(cinfo,V,action) \ + MAKESTMT( MAKE_BYTE_AVAIL(cinfo,action); \ + V = GETJOCTET(*next_input_byte++); ) + +/* As above, but read two bytes interpreted as an unsigned 16-bit integer. + * V should be declared unsigned int or perhaps INT32. + */ +#define INPUT_2BYTES(cinfo,V,action) \ + MAKESTMT( MAKE_BYTE_AVAIL(cinfo,action); \ + V = ((unsigned int) GETJOCTET(*next_input_byte++)) << 8; \ + MAKE_BYTE_AVAIL(cinfo,action); \ + V += GETJOCTET(*next_input_byte++); ) + + +/* + * Routines to process JPEG markers. + * + * Entry condition: JPEG marker itself has been read and its code saved + * in cinfo->unread_marker; input restart point is just after the marker. + * + * Exit: if return TRUE, have read and processed any parameters, and have + * updated the restart point to point after the parameters. + * If return FALSE, was forced to suspend before reaching end of + * marker parameters; restart point has not been moved. Same routine + * will be called again after application supplies more input data. + * + * This approach to suspension assumes that all of a marker's parameters can + * fit into a single input bufferload. This should hold for "normal" + * markers. Some COM/APPn markers might have large parameter segments, + * but we use skip_input_data to get past those, and thereby put the problem + * on the source manager's shoulders. + * + * Note that we don't bother to avoid duplicate trace messages if a + * suspension occurs within marker parameters. Other side effects + * require more care. + */ + + +LOCAL boolean +get_soi (j_decompress_ptr cinfo) +/* Process an SOI marker */ +{ + int i; + + TRACEMS(cinfo, 1, JTRC_SOI); + + if (cinfo->marker->saw_SOI) + ERREXIT(cinfo, JERR_SOI_DUPLICATE); + + /* Reset all parameters that are defined to be reset by SOI */ + + for (i = 0; i < NUM_ARITH_TBLS; i++) { + cinfo->arith_dc_L[i] = 0; + cinfo->arith_dc_U[i] = 1; + cinfo->arith_ac_K[i] = 5; + } + cinfo->restart_interval = 0; + + /* Set initial assumptions for colorspace etc */ + + cinfo->jpeg_color_space = JCS_UNKNOWN; + cinfo->CCIR601_sampling = FALSE; /* Assume non-CCIR sampling??? */ + + cinfo->saw_JFIF_marker = FALSE; + cinfo->density_unit = 0; /* set default JFIF APP0 values */ + cinfo->X_density = 1; + cinfo->Y_density = 1; + cinfo->saw_Adobe_marker = FALSE; + cinfo->Adobe_transform = 0; + + cinfo->marker->saw_SOI = TRUE; + + return TRUE; +} + + +LOCAL boolean +get_sof (j_decompress_ptr cinfo, boolean is_prog, boolean is_arith) +/* Process a SOFn marker */ +{ + INT32 length; + int c, ci; + jpeg_component_info * compptr; + INPUT_VARS(cinfo); + + cinfo->progressive_mode = is_prog; + cinfo->arith_code = is_arith; + + INPUT_2BYTES(cinfo, length, return FALSE); + + INPUT_BYTE(cinfo, cinfo->data_precision, return FALSE); + INPUT_2BYTES(cinfo, cinfo->image_height, return FALSE); + INPUT_2BYTES(cinfo, cinfo->image_width, return FALSE); + INPUT_BYTE(cinfo, cinfo->num_components, return FALSE); + + length -= 8; + + TRACEMS4(cinfo, 1, JTRC_SOF, cinfo->unread_marker, + (int) cinfo->image_width, (int) cinfo->image_height, + cinfo->num_components); + + if (cinfo->marker->saw_SOF) + ERREXIT(cinfo, JERR_SOF_DUPLICATE); + + /* We don't support files in which the image height is initially specified */ + /* as 0 and is later redefined by DNL. As long as we have to check that, */ + /* might as well have a general sanity check. */ + if (cinfo->image_height <= 0 || cinfo->image_width <= 0 + || cinfo->num_components <= 0) + ERREXIT(cinfo, JERR_EMPTY_IMAGE); + + if (length != (cinfo->num_components * 3)) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + if (cinfo->comp_info == NULL) /* do only once, even if suspend */ + cinfo->comp_info = (jpeg_component_info *) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->num_components * SIZEOF(jpeg_component_info)); + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + compptr->component_index = ci; + INPUT_BYTE(cinfo, compptr->component_id, return FALSE); + INPUT_BYTE(cinfo, c, return FALSE); + compptr->h_samp_factor = (c >> 4) & 15; + compptr->v_samp_factor = (c ) & 15; + INPUT_BYTE(cinfo, compptr->quant_tbl_no, return FALSE); + + TRACEMS4(cinfo, 1, JTRC_SOF_COMPONENT, + compptr->component_id, compptr->h_samp_factor, + compptr->v_samp_factor, compptr->quant_tbl_no); + } + + cinfo->marker->saw_SOF = TRUE; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL boolean +get_sos (j_decompress_ptr cinfo) +/* Process a SOS marker */ +{ + INT32 length; + int i, ci, n, c, cc; + jpeg_component_info * compptr; + INPUT_VARS(cinfo); + + if (! cinfo->marker->saw_SOF) + ERREXIT(cinfo, JERR_SOS_NO_SOF); + + INPUT_2BYTES(cinfo, length, return FALSE); + + INPUT_BYTE(cinfo, n, return FALSE); /* Number of components */ + + if (length != (n * 2 + 6) || n < 1 || n > MAX_COMPS_IN_SCAN) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + TRACEMS1(cinfo, 1, JTRC_SOS, n); + + cinfo->comps_in_scan = n; + + /* Collect the component-spec parameters */ + + for (i = 0; i < n; i++) { + INPUT_BYTE(cinfo, cc, return FALSE); + INPUT_BYTE(cinfo, c, return FALSE); + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + if (cc == compptr->component_id) + goto id_found; + } + + ERREXIT1(cinfo, JERR_BAD_COMPONENT_ID, cc); + + id_found: + + cinfo->cur_comp_info[i] = compptr; + compptr->dc_tbl_no = (c >> 4) & 15; + compptr->ac_tbl_no = (c ) & 15; + + TRACEMS3(cinfo, 1, JTRC_SOS_COMPONENT, cc, + compptr->dc_tbl_no, compptr->ac_tbl_no); + } + + /* Collect the additional scan parameters Ss, Se, Ah/Al. */ + INPUT_BYTE(cinfo, c, return FALSE); + cinfo->Ss = c; + INPUT_BYTE(cinfo, c, return FALSE); + cinfo->Se = c; + INPUT_BYTE(cinfo, c, return FALSE); + cinfo->Ah = (c >> 4) & 15; + cinfo->Al = (c ) & 15; + + TRACEMS4(cinfo, 1, JTRC_SOS_PARAMS, cinfo->Ss, cinfo->Se, + cinfo->Ah, cinfo->Al); + + /* Prepare to scan data & restart markers */ + cinfo->marker->next_restart_num = 0; + + /* Count another SOS marker */ + cinfo->input_scan_number++; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +METHODDEF boolean +get_app0 (j_decompress_ptr cinfo) +/* Process an APP0 marker */ +{ +#define JFIF_LEN 14 + INT32 length; + UINT8 b[JFIF_LEN]; + int buffp; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + /* See if a JFIF APP0 marker is present */ + + if (length >= JFIF_LEN) { + for (buffp = 0; buffp < JFIF_LEN; buffp++) + INPUT_BYTE(cinfo, b[buffp], return FALSE); + length -= JFIF_LEN; + + if (b[0]==0x4A && b[1]==0x46 && b[2]==0x49 && b[3]==0x46 && b[4]==0) { + /* Found JFIF APP0 marker: check version */ + /* Major version must be 1, anything else signals an incompatible change. + * We used to treat this as an error, but now it's a nonfatal warning, + * because some bozo at Hijaak couldn't read the spec. + * Minor version should be 0..2, but process anyway if newer. + */ + if (b[5] != 1) + WARNMS2(cinfo, JWRN_JFIF_MAJOR, b[5], b[6]); + else if (b[6] > 2) + TRACEMS2(cinfo, 1, JTRC_JFIF_MINOR, b[5], b[6]); + /* Save info */ + cinfo->saw_JFIF_marker = TRUE; + cinfo->density_unit = b[7]; + cinfo->X_density = (b[8] << 8) + b[9]; + cinfo->Y_density = (b[10] << 8) + b[11]; + TRACEMS3(cinfo, 1, JTRC_JFIF, + cinfo->X_density, cinfo->Y_density, cinfo->density_unit); + if (b[12] | b[13]) + TRACEMS2(cinfo, 1, JTRC_JFIF_THUMBNAIL, b[12], b[13]); + if (length != ((INT32) b[12] * (INT32) b[13] * (INT32) 3)) + TRACEMS1(cinfo, 1, JTRC_JFIF_BADTHUMBNAILSIZE, (int) length); + } else { + /* Start of APP0 does not match "JFIF" */ + TRACEMS1(cinfo, 1, JTRC_APP0, (int) length + JFIF_LEN); + } + } else { + /* Too short to be JFIF marker */ + TRACEMS1(cinfo, 1, JTRC_APP0, (int) length); + } + + INPUT_SYNC(cinfo); + if (length > 0) /* skip any remaining data -- could be lots */ + (*cinfo->src->skip_input_data) (cinfo, (long) length); + + return TRUE; +} + + +METHODDEF boolean +get_app14 (j_decompress_ptr cinfo) +/* Process an APP14 marker */ +{ +#define ADOBE_LEN 12 + INT32 length; + UINT8 b[ADOBE_LEN]; + int buffp; + unsigned int version, flags0, flags1, transform; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + /* See if an Adobe APP14 marker is present */ + + if (length >= ADOBE_LEN) { + for (buffp = 0; buffp < ADOBE_LEN; buffp++) + INPUT_BYTE(cinfo, b[buffp], return FALSE); + length -= ADOBE_LEN; + + if (b[0]==0x41 && b[1]==0x64 && b[2]==0x6F && b[3]==0x62 && b[4]==0x65) { + /* Found Adobe APP14 marker */ + version = (b[5] << 8) + b[6]; + flags0 = (b[7] << 8) + b[8]; + flags1 = (b[9] << 8) + b[10]; + transform = b[11]; + TRACEMS4(cinfo, 1, JTRC_ADOBE, version, flags0, flags1, transform); + cinfo->saw_Adobe_marker = TRUE; + cinfo->Adobe_transform = (UINT8) transform; + } else { + /* Start of APP14 does not match "Adobe" */ + TRACEMS1(cinfo, 1, JTRC_APP14, (int) length + ADOBE_LEN); + } + } else { + /* Too short to be Adobe marker */ + TRACEMS1(cinfo, 1, JTRC_APP14, (int) length); + } + + INPUT_SYNC(cinfo); + if (length > 0) /* skip any remaining data -- could be lots */ + (*cinfo->src->skip_input_data) (cinfo, (long) length); + + return TRUE; +} + + +LOCAL boolean +get_dac (j_decompress_ptr cinfo) +/* Process a DAC marker */ +{ + INT32 length; + int index, val; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + while (length > 0) { + INPUT_BYTE(cinfo, index, return FALSE); + INPUT_BYTE(cinfo, val, return FALSE); + + length -= 2; + + TRACEMS2(cinfo, 1, JTRC_DAC, index, val); + + if (index < 0 || index >= (2*NUM_ARITH_TBLS)) + ERREXIT1(cinfo, JERR_DAC_INDEX, index); + + if (index >= NUM_ARITH_TBLS) { /* define AC table */ + cinfo->arith_ac_K[index-NUM_ARITH_TBLS] = (UINT8) val; + } else { /* define DC table */ + cinfo->arith_dc_L[index] = (UINT8) (val & 0x0F); + cinfo->arith_dc_U[index] = (UINT8) (val >> 4); + if (cinfo->arith_dc_L[index] > cinfo->arith_dc_U[index]) + ERREXIT1(cinfo, JERR_DAC_VALUE, val); + } + } + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL boolean +get_dht (j_decompress_ptr cinfo) +/* Process a DHT marker */ +{ + INT32 length; + UINT8 bits[17]; + UINT8 huffval[256]; + int i, index, count; + JHUFF_TBL **htblptr; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + while (length > 0) { + INPUT_BYTE(cinfo, index, return FALSE); + + TRACEMS1(cinfo, 1, JTRC_DHT, index); + + bits[0] = 0; + count = 0; + for (i = 1; i <= 16; i++) { + INPUT_BYTE(cinfo, bits[i], return FALSE); + count += bits[i]; + } + + length -= 1 + 16; + + TRACEMS8(cinfo, 2, JTRC_HUFFBITS, + bits[1], bits[2], bits[3], bits[4], + bits[5], bits[6], bits[7], bits[8]); + TRACEMS8(cinfo, 2, JTRC_HUFFBITS, + bits[9], bits[10], bits[11], bits[12], + bits[13], bits[14], bits[15], bits[16]); + + if (count > 256 || ((INT32) count) > length) + ERREXIT(cinfo, JERR_DHT_COUNTS); + + for (i = 0; i < count; i++) + INPUT_BYTE(cinfo, huffval[i], return FALSE); + + length -= count; + + if (index & 0x10) { /* AC table definition */ + index -= 0x10; + htblptr = &cinfo->ac_huff_tbl_ptrs[index]; + } else { /* DC table definition */ + htblptr = &cinfo->dc_huff_tbl_ptrs[index]; + } + + if (index < 0 || index >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_DHT_INDEX, index); + + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + + MEMCOPY((*htblptr)->bits, bits, SIZEOF((*htblptr)->bits)); + MEMCOPY((*htblptr)->huffval, huffval, SIZEOF((*htblptr)->huffval)); + } + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL boolean +get_dqt (j_decompress_ptr cinfo) +/* Process a DQT marker */ +{ + INT32 length; + int n, i, prec; + unsigned int tmp; + JQUANT_TBL *quant_ptr; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + while (length > 0) { + INPUT_BYTE(cinfo, n, return FALSE); + prec = n >> 4; + n &= 0x0F; + + TRACEMS2(cinfo, 1, JTRC_DQT, n, prec); + + if (n >= NUM_QUANT_TBLS) + ERREXIT1(cinfo, JERR_DQT_INDEX, n); + + if (cinfo->quant_tbl_ptrs[n] == NULL) + cinfo->quant_tbl_ptrs[n] = jpeg_alloc_quant_table((j_common_ptr) cinfo); + quant_ptr = cinfo->quant_tbl_ptrs[n]; + + for (i = 0; i < DCTSIZE2; i++) { + if (prec) + INPUT_2BYTES(cinfo, tmp, return FALSE); + else + INPUT_BYTE(cinfo, tmp, return FALSE); + quant_ptr->quantval[i] = (UINT16) tmp; + } + + for (i = 0; i < DCTSIZE2; i += 8) { + TRACEMS8(cinfo, 2, JTRC_QUANTVALS, + quant_ptr->quantval[i ], quant_ptr->quantval[i+1], + quant_ptr->quantval[i+2], quant_ptr->quantval[i+3], + quant_ptr->quantval[i+4], quant_ptr->quantval[i+5], + quant_ptr->quantval[i+6], quant_ptr->quantval[i+7]); + } + + length -= DCTSIZE2+1; + if (prec) length -= DCTSIZE2; + } + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL boolean +get_dri (j_decompress_ptr cinfo) +/* Process a DRI marker */ +{ + INT32 length; + unsigned int tmp; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + + if (length != 4) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + INPUT_2BYTES(cinfo, tmp, return FALSE); + + TRACEMS1(cinfo, 1, JTRC_DRI, tmp); + + cinfo->restart_interval = tmp; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +METHODDEF boolean +skip_variable (j_decompress_ptr cinfo) +/* Skip over an unknown or uninteresting variable-length marker */ +{ + INT32 length; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + + TRACEMS2(cinfo, 1, JTRC_MISC_MARKER, cinfo->unread_marker, (int) length); + + INPUT_SYNC(cinfo); /* do before skip_input_data */ + (*cinfo->src->skip_input_data) (cinfo, (long) length - 2L); + + return TRUE; +} + + +/* + * Find the next JPEG marker, save it in cinfo->unread_marker. + * Returns FALSE if had to suspend before reaching a marker; + * in that case cinfo->unread_marker is unchanged. + * + * Note that the result might not be a valid marker code, + * but it will never be 0 or FF. + */ + +LOCAL boolean +next_marker (j_decompress_ptr cinfo) +{ + int c; + INPUT_VARS(cinfo); + + for (;;) { + INPUT_BYTE(cinfo, c, return FALSE); + /* Skip any non-FF bytes. + * This may look a bit inefficient, but it will not occur in a valid file. + * We sync after each discarded byte so that a suspending data source + * can discard the byte from its buffer. + */ + while (c != 0xFF) { + cinfo->marker->discarded_bytes++; + INPUT_SYNC(cinfo); + INPUT_BYTE(cinfo, c, return FALSE); + } + /* This loop swallows any duplicate FF bytes. Extra FFs are legal as + * pad bytes, so don't count them in discarded_bytes. We assume there + * will not be so many consecutive FF bytes as to overflow a suspending + * data source's input buffer. + */ + do { + INPUT_BYTE(cinfo, c, return FALSE); + } while (c == 0xFF); + if (c != 0) + break; /* found a valid marker, exit loop */ + /* Reach here if we found a stuffed-zero data sequence (FF/00). + * Discard it and loop back to try again. + */ + cinfo->marker->discarded_bytes += 2; + INPUT_SYNC(cinfo); + } + + if (cinfo->marker->discarded_bytes != 0) { + WARNMS2(cinfo, JWRN_EXTRANEOUS_DATA, cinfo->marker->discarded_bytes, c); + cinfo->marker->discarded_bytes = 0; + } + + cinfo->unread_marker = c; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL boolean +first_marker (j_decompress_ptr cinfo) +/* Like next_marker, but used to obtain the initial SOI marker. */ +/* For this marker, we do not allow preceding garbage or fill; otherwise, + * we might well scan an entire input file before realizing it ain't JPEG. + * If an application wants to process non-JFIF files, it must seek to the + * SOI before calling the JPEG library. + */ +{ + int c, c2; + INPUT_VARS(cinfo); + + INPUT_BYTE(cinfo, c, return FALSE); + INPUT_BYTE(cinfo, c2, return FALSE); + if (c != 0xFF || c2 != (int) M_SOI) + ERREXIT2(cinfo, JERR_NO_SOI, c, c2); + + cinfo->unread_marker = c2; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +/* + * Read markers until SOS or EOI. + * + * Returns same codes as are defined for jpeg_consume_input: + * JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + */ + +METHODDEF int +read_markers (j_decompress_ptr cinfo) +{ + /* Outer loop repeats once for each marker. */ + for (;;) { + /* Collect the marker proper, unless we already did. */ + /* NB: first_marker() enforces the requirement that SOI appear first. */ + if (cinfo->unread_marker == 0) { + if (! cinfo->marker->saw_SOI) { + if (! first_marker(cinfo)) + return JPEG_SUSPENDED; + } else { + if (! next_marker(cinfo)) + return JPEG_SUSPENDED; + } + } + /* At this point cinfo->unread_marker contains the marker code and the + * input point is just past the marker proper, but before any parameters. + * A suspension will cause us to return with this state still true. + */ + switch (cinfo->unread_marker) { + case M_SOI: + if (! get_soi(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_SOF0: /* Baseline */ + case M_SOF1: /* Extended sequential, Huffman */ + if (! get_sof(cinfo, FALSE, FALSE)) + return JPEG_SUSPENDED; + break; + + case M_SOF2: /* Progressive, Huffman */ + if (! get_sof(cinfo, TRUE, FALSE)) + return JPEG_SUSPENDED; + break; + + case M_SOF9: /* Extended sequential, arithmetic */ + if (! get_sof(cinfo, FALSE, TRUE)) + return JPEG_SUSPENDED; + break; + + case M_SOF10: /* Progressive, arithmetic */ + if (! get_sof(cinfo, TRUE, TRUE)) + return JPEG_SUSPENDED; + break; + + /* Currently unsupported SOFn types */ + case M_SOF3: /* Lossless, Huffman */ + case M_SOF5: /* Differential sequential, Huffman */ + case M_SOF6: /* Differential progressive, Huffman */ + case M_SOF7: /* Differential lossless, Huffman */ + case M_JPG: /* Reserved for JPEG extensions */ + case M_SOF11: /* Lossless, arithmetic */ + case M_SOF13: /* Differential sequential, arithmetic */ + case M_SOF14: /* Differential progressive, arithmetic */ + case M_SOF15: /* Differential lossless, arithmetic */ + ERREXIT1(cinfo, JERR_SOF_UNSUPPORTED, cinfo->unread_marker); + break; + + case M_SOS: + if (! get_sos(cinfo)) + return JPEG_SUSPENDED; + cinfo->unread_marker = 0; /* processed the marker */ + return JPEG_REACHED_SOS; + + case M_EOI: + TRACEMS(cinfo, 1, JTRC_EOI); + cinfo->unread_marker = 0; /* processed the marker */ + return JPEG_REACHED_EOI; + + case M_DAC: + if (! get_dac(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_DHT: + if (! get_dht(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_DQT: + if (! get_dqt(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_DRI: + if (! get_dri(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_APP0: + case M_APP1: + case M_APP2: + case M_APP3: + case M_APP4: + case M_APP5: + case M_APP6: + case M_APP7: + case M_APP8: + case M_APP9: + case M_APP10: + case M_APP11: + case M_APP12: + case M_APP13: + case M_APP14: + case M_APP15: + if (! (*cinfo->marker->process_APPn[cinfo->unread_marker - (int) M_APP0]) (cinfo)) + return JPEG_SUSPENDED; + break; + + case M_COM: + if (! (*cinfo->marker->process_COM) (cinfo)) + return JPEG_SUSPENDED; + break; + + case M_RST0: /* these are all parameterless */ + case M_RST1: + case M_RST2: + case M_RST3: + case M_RST4: + case M_RST5: + case M_RST6: + case M_RST7: + case M_TEM: + TRACEMS1(cinfo, 1, JTRC_PARMLESS_MARKER, cinfo->unread_marker); + break; + + case M_DNL: /* Ignore DNL ... perhaps the wrong thing */ + if (! skip_variable(cinfo)) + return JPEG_SUSPENDED; + break; + + default: /* must be DHP, EXP, JPGn, or RESn */ + /* For now, we treat the reserved markers as fatal errors since they are + * likely to be used to signal incompatible JPEG Part 3 extensions. + * Once the JPEG 3 version-number marker is well defined, this code + * ought to change! + */ + ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, cinfo->unread_marker); + break; + } + /* Successfully processed marker, so reset state variable */ + cinfo->unread_marker = 0; + } /* end loop */ +} + + +/* + * Read a restart marker, which is expected to appear next in the datastream; + * if the marker is not there, take appropriate recovery action. + * Returns FALSE if suspension is required. + * + * This is called by the entropy decoder after it has read an appropriate + * number of MCUs. cinfo->unread_marker may be nonzero if the entropy decoder + * has already read a marker from the data source. Under normal conditions + * cinfo->unread_marker will be reset to 0 before returning; if not reset, + * it holds a marker which the decoder will be unable to read past. + */ + +METHODDEF boolean +read_restart_marker (j_decompress_ptr cinfo) +{ + /* Obtain a marker unless we already did. */ + /* Note that next_marker will complain if it skips any data. */ + if (cinfo->unread_marker == 0) { + if (! next_marker(cinfo)) + return FALSE; + } + + if (cinfo->unread_marker == + ((int) M_RST0 + cinfo->marker->next_restart_num)) { + /* Normal case --- swallow the marker and let entropy decoder continue */ + TRACEMS1(cinfo, 2, JTRC_RST, cinfo->marker->next_restart_num); + cinfo->unread_marker = 0; + } else { + /* Uh-oh, the restart markers have been messed up. */ + /* Let the data source manager determine how to resync. */ + if (! (*cinfo->src->resync_to_restart) (cinfo, + cinfo->marker->next_restart_num)) + return FALSE; + } + + /* Update next-restart state */ + cinfo->marker->next_restart_num = (cinfo->marker->next_restart_num + 1) & 7; + + return TRUE; +} + + +/* + * This is the default resync_to_restart method for data source managers + * to use if they don't have any better approach. Some data source managers + * may be able to back up, or may have additional knowledge about the data + * which permits a more intelligent recovery strategy; such managers would + * presumably supply their own resync method. + * + * read_restart_marker calls resync_to_restart if it finds a marker other than + * the restart marker it was expecting. (This code is *not* used unless + * a nonzero restart interval has been declared.) cinfo->unread_marker is + * the marker code actually found (might be anything, except 0 or FF). + * The desired restart marker number (0..7) is passed as a parameter. + * This routine is supposed to apply whatever error recovery strategy seems + * appropriate in order to position the input stream to the next data segment. + * Note that cinfo->unread_marker is treated as a marker appearing before + * the current data-source input point; usually it should be reset to zero + * before returning. + * Returns FALSE if suspension is required. + * + * This implementation is substantially constrained by wanting to treat the + * input as a data stream; this means we can't back up. Therefore, we have + * only the following actions to work with: + * 1. Simply discard the marker and let the entropy decoder resume at next + * byte of file. + * 2. Read forward until we find another marker, discarding intervening + * data. (In theory we could look ahead within the current bufferload, + * without having to discard data if we don't find the desired marker. + * This idea is not implemented here, in part because it makes behavior + * dependent on buffer size and chance buffer-boundary positions.) + * 3. Leave the marker unread (by failing to zero cinfo->unread_marker). + * This will cause the entropy decoder to process an empty data segment, + * inserting dummy zeroes, and then we will reprocess the marker. + * + * #2 is appropriate if we think the desired marker lies ahead, while #3 is + * appropriate if the found marker is a future restart marker (indicating + * that we have missed the desired restart marker, probably because it got + * corrupted). + * We apply #2 or #3 if the found marker is a restart marker no more than + * two counts behind or ahead of the expected one. We also apply #2 if the + * found marker is not a legal JPEG marker code (it's certainly bogus data). + * If the found marker is a restart marker more than 2 counts away, we do #1 + * (too much risk that the marker is erroneous; with luck we will be able to + * resync at some future point). + * For any valid non-restart JPEG marker, we apply #3. This keeps us from + * overrunning the end of a scan. An implementation limited to single-scan + * files might find it better to apply #2 for markers other than EOI, since + * any other marker would have to be bogus data in that case. + */ + +GLOBAL boolean +jpeg_resync_to_restart (j_decompress_ptr cinfo, int desired) +{ + int marker = cinfo->unread_marker; + int action = 1; + + /* Always put up a warning. */ + WARNMS2(cinfo, JWRN_MUST_RESYNC, marker, desired); + + /* Outer loop handles repeated decision after scanning forward. */ + for (;;) { + if (marker < (int) M_SOF0) + action = 2; /* invalid marker */ + else if (marker < (int) M_RST0 || marker > (int) M_RST7) + action = 3; /* valid non-restart marker */ + else { + if (marker == ((int) M_RST0 + ((desired+1) & 7)) || + marker == ((int) M_RST0 + ((desired+2) & 7))) + action = 3; /* one of the next two expected restarts */ + else if (marker == ((int) M_RST0 + ((desired-1) & 7)) || + marker == ((int) M_RST0 + ((desired-2) & 7))) + action = 2; /* a prior restart, so advance */ + else + action = 1; /* desired restart or too far away */ + } + TRACEMS2(cinfo, 4, JTRC_RECOVERY_ACTION, marker, action); + switch (action) { + case 1: + /* Discard marker and let entropy decoder resume processing. */ + cinfo->unread_marker = 0; + return TRUE; + case 2: + /* Scan to the next marker, and repeat the decision loop. */ + if (! next_marker(cinfo)) + return FALSE; + marker = cinfo->unread_marker; + break; + case 3: + /* Return without advancing past this marker. */ + /* Entropy decoder will be forced to process an empty segment. */ + return TRUE; + } + } /* end loop */ +} + + +/* + * Reset marker processing state to begin a fresh datastream. + */ + +METHODDEF void +reset_marker_reader (j_decompress_ptr cinfo) +{ + cinfo->comp_info = NULL; /* until allocated by get_sof */ + cinfo->input_scan_number = 0; /* no SOS seen yet */ + cinfo->unread_marker = 0; /* no pending marker */ + cinfo->marker->saw_SOI = FALSE; /* set internal state too */ + cinfo->marker->saw_SOF = FALSE; + cinfo->marker->discarded_bytes = 0; +} + + +/* + * Initialize the marker reader module. + * This is called only once, when the decompression object is created. + */ + +GLOBAL void +jinit_marker_reader (j_decompress_ptr cinfo) +{ + int i; + + /* Create subobject in permanent pool */ + cinfo->marker = (struct jpeg_marker_reader *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(struct jpeg_marker_reader)); + /* Initialize method pointers */ + cinfo->marker->reset_marker_reader = reset_marker_reader; + cinfo->marker->read_markers = read_markers; + cinfo->marker->read_restart_marker = read_restart_marker; + cinfo->marker->process_COM = skip_variable; + for (i = 0; i < 16; i++) + cinfo->marker->process_APPn[i] = skip_variable; + cinfo->marker->process_APPn[0] = get_app0; + cinfo->marker->process_APPn[14] = get_app14; + /* Reset marker processing state */ + reset_marker_reader(cinfo); +} diff --git a/utils/Libs/jpeg6/jdmaster.cpp b/utils/Libs/jpeg6/jdmaster.cpp new file mode 100644 index 0000000..64f730f --- /dev/null +++ b/utils/Libs/jpeg6/jdmaster.cpp @@ -0,0 +1,557 @@ +/* + * jdmaster.c + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains master control logic for the JPEG decompressor. + * These routines are concerned with selecting the modules to be executed + * and with determining the number of passes and the work to be done in each + * pass. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Private state */ + +typedef struct { + struct jpeg_decomp_master pub; /* public fields */ + + int pass_number; /* # of passes completed */ + + boolean using_merged_upsample; /* TRUE if using merged upsample/cconvert */ + + /* Saved references to initialized quantizer modules, + * in case we need to switch modes. + */ + struct jpeg_color_quantizer * quantizer_1pass; + struct jpeg_color_quantizer * quantizer_2pass; +} my_decomp_master; + +typedef my_decomp_master * my_master_ptr; + + +/* + * Determine whether merged upsample/color conversion should be used. + * CRUCIAL: this must match the actual capabilities of jdmerge.c! + */ + +LOCAL boolean +use_merged_upsample (j_decompress_ptr cinfo) +{ +#ifdef UPSAMPLE_MERGING_SUPPORTED + /* Merging is the equivalent of plain box-filter upsampling */ + if (cinfo->do_fancy_upsampling || cinfo->CCIR601_sampling) + return FALSE; + /* jdmerge.c only supports YCC=>RGB color conversion */ + if (cinfo->jpeg_color_space != JCS_YCbCr || cinfo->num_components != 3 || + cinfo->out_color_space != JCS_RGB || + cinfo->out_color_components != RGB_PIXELSIZE) + return FALSE; + /* and it only handles 2h1v or 2h2v sampling ratios */ + if (cinfo->comp_info[0].h_samp_factor != 2 || + cinfo->comp_info[1].h_samp_factor != 1 || + cinfo->comp_info[2].h_samp_factor != 1 || + cinfo->comp_info[0].v_samp_factor > 2 || + cinfo->comp_info[1].v_samp_factor != 1 || + cinfo->comp_info[2].v_samp_factor != 1) + return FALSE; + /* furthermore, it doesn't work if we've scaled the IDCTs differently */ + if (cinfo->comp_info[0].DCT_scaled_size != cinfo->min_DCT_scaled_size || + cinfo->comp_info[1].DCT_scaled_size != cinfo->min_DCT_scaled_size || + cinfo->comp_info[2].DCT_scaled_size != cinfo->min_DCT_scaled_size) + return FALSE; + /* ??? also need to test for upsample-time rescaling, when & if supported */ + return TRUE; /* by golly, it'll work... */ +#else + return FALSE; +#endif +} + + +/* + * Compute output image dimensions and related values. + * NOTE: this is exported for possible use by application. + * Hence it mustn't do anything that can't be done twice. + * Also note that it may be called before the master module is initialized! + */ + +GLOBAL void +jpeg_calc_output_dimensions (j_decompress_ptr cinfo) +/* Do computations that are needed before master selection phase */ +{ +#if 0 // JDC: commented out to remove warning + int ci; + jpeg_component_info *compptr; +#endif + + /* Prevent application from calling me at wrong times */ + if (cinfo->global_state != DSTATE_READY) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + +#ifdef IDCT_SCALING_SUPPORTED + + /* Compute actual output image dimensions and DCT scaling choices. */ + if (cinfo->scale_num * 8 <= cinfo->scale_denom) { + /* Provide 1/8 scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 8L); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 8L); + cinfo->min_DCT_scaled_size = 1; + } else if (cinfo->scale_num * 4 <= cinfo->scale_denom) { + /* Provide 1/4 scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 4L); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 4L); + cinfo->min_DCT_scaled_size = 2; + } else if (cinfo->scale_num * 2 <= cinfo->scale_denom) { + /* Provide 1/2 scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 2L); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 2L); + cinfo->min_DCT_scaled_size = 4; + } else { + /* Provide 1/1 scaling */ + cinfo->output_width = cinfo->image_width; + cinfo->output_height = cinfo->image_height; + cinfo->min_DCT_scaled_size = DCTSIZE; + } + /* In selecting the actual DCT scaling for each component, we try to + * scale up the chroma components via IDCT scaling rather than upsampling. + * This saves time if the upsampler gets to use 1:1 scaling. + * Note this code assumes that the supported DCT scalings are powers of 2. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + int ssize = cinfo->min_DCT_scaled_size; + while (ssize < DCTSIZE && + (compptr->h_samp_factor * ssize * 2 <= + cinfo->max_h_samp_factor * cinfo->min_DCT_scaled_size) && + (compptr->v_samp_factor * ssize * 2 <= + cinfo->max_v_samp_factor * cinfo->min_DCT_scaled_size)) { + ssize = ssize * 2; + } + compptr->DCT_scaled_size = ssize; + } + + /* Recompute downsampled dimensions of components; + * application needs to know these if using raw downsampled data. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Size in samples, after IDCT scaling */ + compptr->downsampled_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * + (long) (compptr->h_samp_factor * compptr->DCT_scaled_size), + (long) (cinfo->max_h_samp_factor * DCTSIZE)); + compptr->downsampled_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * + (long) (compptr->v_samp_factor * compptr->DCT_scaled_size), + (long) (cinfo->max_v_samp_factor * DCTSIZE)); + } + +#else /* !IDCT_SCALING_SUPPORTED */ + + /* Hardwire it to "no scaling" */ + cinfo->output_width = cinfo->image_width; + cinfo->output_height = cinfo->image_height; + /* jdinput.c has already initialized DCT_scaled_size to DCTSIZE, + * and has computed unscaled downsampled_width and downsampled_height. + */ + +#endif /* IDCT_SCALING_SUPPORTED */ + + /* Report number of components in selected colorspace. */ + /* Probably this should be in the color conversion module... */ + switch (cinfo->out_color_space) { + case JCS_GRAYSCALE: + cinfo->out_color_components = 1; + break; + case JCS_RGB: +#if RGB_PIXELSIZE != 3 + cinfo->out_color_components = RGB_PIXELSIZE; + break; +#endif /* else share code with YCbCr */ + case JCS_YCbCr: + cinfo->out_color_components = 3; + break; + case JCS_CMYK: + case JCS_YCCK: + cinfo->out_color_components = 4; + break; + default: /* else must be same colorspace as in file */ + cinfo->out_color_components = cinfo->num_components; + break; + } + cinfo->output_components = (cinfo->quantize_colors ? 1 : + cinfo->out_color_components); + + /* See if upsampler will want to emit more than one row at a time */ + if (use_merged_upsample(cinfo)) + cinfo->rec_outbuf_height = cinfo->max_v_samp_factor; + else + cinfo->rec_outbuf_height = 1; +} + + +/* + * Several decompression processes need to range-limit values to the range + * 0..MAXJSAMPLE; the input value may fall somewhat outside this range + * due to noise introduced by quantization, roundoff error, etc. These + * processes are inner loops and need to be as fast as possible. On most + * machines, particularly CPUs with pipelines or instruction prefetch, + * a (subscript-check-less) C table lookup + * x = sample_range_limit[x]; + * is faster than explicit tests + * if (x < 0) x = 0; + * else if (x > MAXJSAMPLE) x = MAXJSAMPLE; + * These processes all use a common table prepared by the routine below. + * + * For most steps we can mathematically guarantee that the initial value + * of x is within MAXJSAMPLE+1 of the legal range, so a table running from + * -(MAXJSAMPLE+1) to 2*MAXJSAMPLE+1 is sufficient. But for the initial + * limiting step (just after the IDCT), a wildly out-of-range value is + * possible if the input data is corrupt. To avoid any chance of indexing + * off the end of memory and getting a bad-pointer trap, we perform the + * post-IDCT limiting thus: + * x = range_limit[x & MASK]; + * where MASK is 2 bits wider than legal sample data, ie 10 bits for 8-bit + * samples. Under normal circumstances this is more than enough range and + * a correct output will be generated; with bogus input data the mask will + * cause wraparound, and we will safely generate a bogus-but-in-range output. + * For the post-IDCT step, we want to convert the data from signed to unsigned + * representation by adding CENTERJSAMPLE at the same time that we limit it. + * So the post-IDCT limiting table ends up looking like this: + * CENTERJSAMPLE,CENTERJSAMPLE+1,...,MAXJSAMPLE, + * MAXJSAMPLE (repeat 2*(MAXJSAMPLE+1)-CENTERJSAMPLE times), + * 0 (repeat 2*(MAXJSAMPLE+1)-CENTERJSAMPLE times), + * 0,1,...,CENTERJSAMPLE-1 + * Negative inputs select values from the upper half of the table after + * masking. + * + * We can save some space by overlapping the start of the post-IDCT table + * with the simpler range limiting table. The post-IDCT table begins at + * sample_range_limit + CENTERJSAMPLE. + * + * Note that the table is allocated in near data space on PCs; it's small + * enough and used often enough to justify this. + */ + +LOCAL void +prepare_range_limit_table (j_decompress_ptr cinfo) +/* Allocate and fill in the sample_range_limit table */ +{ + JSAMPLE * table; + int i; + + table = (JSAMPLE *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (5 * (MAXJSAMPLE+1) + CENTERJSAMPLE) * SIZEOF(JSAMPLE)); + table += (MAXJSAMPLE+1); /* allow negative subscripts of simple table */ + cinfo->sample_range_limit = table; + /* First segment of "simple" table: limit[x] = 0 for x < 0 */ + MEMZERO(table - (MAXJSAMPLE+1), (MAXJSAMPLE+1) * SIZEOF(JSAMPLE)); + /* Main part of "simple" table: limit[x] = x */ + for (i = 0; i <= MAXJSAMPLE; i++) + table[i] = (JSAMPLE) i; + table += CENTERJSAMPLE; /* Point to where post-IDCT table starts */ + /* End of simple table, rest of first half of post-IDCT table */ + for (i = CENTERJSAMPLE; i < 2*(MAXJSAMPLE+1); i++) + table[i] = MAXJSAMPLE; + /* Second half of post-IDCT table */ + MEMZERO(table + (2 * (MAXJSAMPLE+1)), + (2 * (MAXJSAMPLE+1) - CENTERJSAMPLE) * SIZEOF(JSAMPLE)); + MEMCOPY(table + (4 * (MAXJSAMPLE+1) - CENTERJSAMPLE), + cinfo->sample_range_limit, CENTERJSAMPLE * SIZEOF(JSAMPLE)); +} + + +/* + * Master selection of decompression modules. + * This is done once at jpeg_start_decompress time. We determine + * which modules will be used and give them appropriate initialization calls. + * We also initialize the decompressor input side to begin consuming data. + * + * Since jpeg_read_header has finished, we know what is in the SOF + * and (first) SOS markers. We also have all the application parameter + * settings. + */ + +LOCAL void +master_selection (j_decompress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + boolean use_c_buffer; + long samplesperrow; + JDIMENSION jd_samplesperrow; + + /* Initialize dimensions and other stuff */ + jpeg_calc_output_dimensions(cinfo); + prepare_range_limit_table(cinfo); + + /* Width of an output scanline must be representable as JDIMENSION. */ + samplesperrow = (long) cinfo->output_width * (long) cinfo->out_color_components; + jd_samplesperrow = (JDIMENSION) samplesperrow; + if ((long) jd_samplesperrow != samplesperrow) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + + /* Initialize my private state */ + master->pass_number = 0; + master->using_merged_upsample = use_merged_upsample(cinfo); + + /* Color quantizer selection */ + master->quantizer_1pass = NULL; + master->quantizer_2pass = NULL; + /* No mode changes if not using buffered-image mode. */ + if (! cinfo->quantize_colors || ! cinfo->buffered_image) { + cinfo->enable_1pass_quant = FALSE; + cinfo->enable_external_quant = FALSE; + cinfo->enable_2pass_quant = FALSE; + } + if (cinfo->quantize_colors) { + if (cinfo->raw_data_out) + ERREXIT(cinfo, JERR_NOTIMPL); + /* 2-pass quantizer only works in 3-component color space. */ + if (cinfo->out_color_components != 3) { + cinfo->enable_1pass_quant = TRUE; + cinfo->enable_external_quant = FALSE; + cinfo->enable_2pass_quant = FALSE; + cinfo->colormap = NULL; + } else if (cinfo->colormap != NULL) { + cinfo->enable_external_quant = TRUE; + } else if (cinfo->two_pass_quantize) { + cinfo->enable_2pass_quant = TRUE; + } else { + cinfo->enable_1pass_quant = TRUE; + } + + if (cinfo->enable_1pass_quant) { +#ifdef QUANT_1PASS_SUPPORTED + jinit_1pass_quantizer(cinfo); + master->quantizer_1pass = cinfo->cquantize; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } + + /* We use the 2-pass code to map to external colormaps. */ + if (cinfo->enable_2pass_quant || cinfo->enable_external_quant) { +#ifdef QUANT_2PASS_SUPPORTED + jinit_2pass_quantizer(cinfo); + master->quantizer_2pass = cinfo->cquantize; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } + /* If both quantizers are initialized, the 2-pass one is left active; + * this is necessary for starting with quantization to an external map. + */ + } + + /* Post-processing: in particular, color conversion first */ + if (! cinfo->raw_data_out) { + if (master->using_merged_upsample) { +#ifdef UPSAMPLE_MERGING_SUPPORTED + jinit_merged_upsampler(cinfo); /* does color conversion too */ +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + jinit_color_deconverter(cinfo); + jinit_upsampler(cinfo); + } + jinit_d_post_controller(cinfo, cinfo->enable_2pass_quant); + } + /* Inverse DCT */ + jinit_inverse_dct(cinfo); + /* Entropy decoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef D_PROGRESSIVE_SUPPORTED + jinit_phuff_decoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_decoder(cinfo); + } + + /* Initialize principal buffer controllers. */ + use_c_buffer = cinfo->inputctl->has_multiple_scans || cinfo->buffered_image; + jinit_d_coef_controller(cinfo, use_c_buffer); + + if (! cinfo->raw_data_out) + jinit_d_main_controller(cinfo, FALSE /* never need full buffer here */); + + /* We can now tell the memory manager to allocate virtual arrays. */ + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + /* Initialize input side of decompressor to consume first scan. */ + (*cinfo->inputctl->start_input_pass) (cinfo); + +#ifdef D_MULTISCAN_FILES_SUPPORTED + /* If jpeg_start_decompress will read the whole file, initialize + * progress monitoring appropriately. The input step is counted + * as one pass. + */ + if (cinfo->progress != NULL && ! cinfo->buffered_image && + cinfo->inputctl->has_multiple_scans) { + int nscans; + /* Estimate number of scans to set pass_limit. */ + if (cinfo->progressive_mode) { + /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ + nscans = 2 + 3 * cinfo->num_components; + } else { + /* For a nonprogressive multiscan file, estimate 1 scan per component. */ + nscans = cinfo->num_components; + } + cinfo->progress->pass_counter = 0L; + cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows * nscans; + cinfo->progress->completed_passes = 0; + cinfo->progress->total_passes = (cinfo->enable_2pass_quant ? 3 : 2); + /* Count the input pass as done */ + master->pass_number++; + } +#endif /* D_MULTISCAN_FILES_SUPPORTED */ +} + + +/* + * Per-pass setup. + * This is called at the beginning of each output pass. We determine which + * modules will be active during this pass and give them appropriate + * start_pass calls. We also set is_dummy_pass to indicate whether this + * is a "real" output pass or a dummy pass for color quantization. + * (In the latter case, jdapi.c will crank the pass to completion.) + */ + +METHODDEF void +prepare_for_output_pass (j_decompress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + if (master->pub.is_dummy_pass) { +#ifdef QUANT_2PASS_SUPPORTED + /* Final pass of 2-pass quantization */ + master->pub.is_dummy_pass = FALSE; + (*cinfo->cquantize->start_pass) (cinfo, FALSE); + (*cinfo->post->start_pass) (cinfo, JBUF_CRANK_DEST); + (*cinfo->main->start_pass) (cinfo, JBUF_CRANK_DEST); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif /* QUANT_2PASS_SUPPORTED */ + } else { + if (cinfo->quantize_colors && cinfo->colormap == NULL) { + /* Select new quantization method */ + if (cinfo->two_pass_quantize && cinfo->enable_2pass_quant) { + cinfo->cquantize = master->quantizer_2pass; + master->pub.is_dummy_pass = TRUE; + } else if (cinfo->enable_1pass_quant) { + cinfo->cquantize = master->quantizer_1pass; + } else { + ERREXIT(cinfo, JERR_MODE_CHANGE); + } + } + (*cinfo->idct->start_pass) (cinfo); + (*cinfo->coef->start_output_pass) (cinfo); + if (! cinfo->raw_data_out) { + if (! master->using_merged_upsample) + (*cinfo->cconvert->start_pass) (cinfo); + (*cinfo->upsample->start_pass) (cinfo); + if (cinfo->quantize_colors) + (*cinfo->cquantize->start_pass) (cinfo, master->pub.is_dummy_pass); + (*cinfo->post->start_pass) (cinfo, + (master->pub.is_dummy_pass ? JBUF_SAVE_AND_PASS : JBUF_PASS_THRU)); + (*cinfo->main->start_pass) (cinfo, JBUF_PASS_THRU); + } + } + + /* Set up progress monitor's pass info if present */ + if (cinfo->progress != NULL) { + cinfo->progress->completed_passes = master->pass_number; + cinfo->progress->total_passes = master->pass_number + + (master->pub.is_dummy_pass ? 2 : 1); + /* In buffered-image mode, we assume one more output pass if EOI not + * yet reached, but no more passes if EOI has been reached. + */ + if (cinfo->buffered_image && ! cinfo->inputctl->eoi_reached) { + cinfo->progress->total_passes += (cinfo->enable_2pass_quant ? 2 : 1); + } + } +} + + +/* + * Finish up at end of an output pass. + */ + +METHODDEF void +finish_output_pass (j_decompress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + if (cinfo->quantize_colors) + (*cinfo->cquantize->finish_pass) (cinfo); + master->pass_number++; +} + + +#ifdef D_MULTISCAN_FILES_SUPPORTED + +/* + * Switch to a new external colormap between output passes. + */ + +GLOBAL void +jpeg_new_colormap (j_decompress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + /* Prevent application from calling me at wrong times */ + if (cinfo->global_state != DSTATE_BUFIMAGE) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + if (cinfo->quantize_colors && cinfo->enable_external_quant && + cinfo->colormap != NULL) { + /* Select 2-pass quantizer for external colormap use */ + cinfo->cquantize = master->quantizer_2pass; + /* Notify quantizer of colormap change */ + (*cinfo->cquantize->new_color_map) (cinfo); + master->pub.is_dummy_pass = FALSE; /* just in case */ + } else + ERREXIT(cinfo, JERR_MODE_CHANGE); +} + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + + +/* + * Initialize master decompression control and select active modules. + * This is performed at the start of jpeg_start_decompress. + */ + +GLOBAL void +jinit_master_decompress (j_decompress_ptr cinfo) +{ + my_master_ptr master; + + master = (my_master_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_decomp_master)); + cinfo->master = (struct jpeg_decomp_master *) master; + master->pub.prepare_for_output_pass = prepare_for_output_pass; + master->pub.finish_output_pass = finish_output_pass; + + master->pub.is_dummy_pass = FALSE; + + master_selection(cinfo); +} diff --git a/utils/Libs/jpeg6/jdpostct.cpp b/utils/Libs/jpeg6/jdpostct.cpp new file mode 100644 index 0000000..e3283b0 --- /dev/null +++ b/utils/Libs/jpeg6/jdpostct.cpp @@ -0,0 +1,290 @@ +/* + * jdpostct.c + * + * Copyright (C) 1994-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the decompression postprocessing controller. + * This controller manages the upsampling, color conversion, and color + * quantization/reduction steps; specifically, it controls the buffering + * between upsample/color conversion and color quantization/reduction. + * + * If no color quantization/reduction is required, then this module has no + * work to do, and it just hands off to the upsample/color conversion code. + * An integrated upsample/convert/quantize process would replace this module + * entirely. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_d_post_controller pub; /* public fields */ + + /* Color quantization source buffer: this holds output data from + * the upsample/color conversion step to be passed to the quantizer. + * For two-pass color quantization, we need a full-image buffer; + * for one-pass operation, a strip buffer is sufficient. + */ + jvirt_sarray_ptr whole_image; /* virtual array, or NULL if one-pass */ + JSAMPARRAY buffer; /* strip buffer, or current strip of virtual */ + JDIMENSION strip_height; /* buffer size in rows */ + /* for two-pass mode only: */ + JDIMENSION starting_row; /* row # of first row in current strip */ + JDIMENSION next_row; /* index of next row to fill/empty in strip */ +} my_post_controller; + +typedef my_post_controller * my_post_ptr; + + +/* Forward declarations */ +METHODDEF void post_process_1pass + JPP((j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +#ifdef QUANT_2PASS_SUPPORTED +METHODDEF void post_process_prepass + JPP((j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +METHODDEF void post_process_2pass + JPP((j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +#endif + + +/* + * Initialize for a processing pass. + */ + +METHODDEF void +start_pass_dpost (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_post_ptr post = (my_post_ptr) cinfo->post; + + switch (pass_mode) { + case JBUF_PASS_THRU: + if (cinfo->quantize_colors) { + /* Single-pass processing with color quantization. */ + post->pub.post_process_data = post_process_1pass; + /* We could be doing buffered-image output before starting a 2-pass + * color quantization; in that case, jinit_d_post_controller did not + * allocate a strip buffer. Use the virtual-array buffer as workspace. + */ + if (post->buffer == NULL) { + post->buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, post->whole_image, + (JDIMENSION) 0, post->strip_height, TRUE); + } + } else { + /* For single-pass processing without color quantization, + * I have no work to do; just call the upsampler directly. + */ + post->pub.post_process_data = cinfo->upsample->upsample; + } + break; +#ifdef QUANT_2PASS_SUPPORTED + case JBUF_SAVE_AND_PASS: + /* First pass of 2-pass quantization */ + if (post->whole_image == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + post->pub.post_process_data = post_process_prepass; + break; + case JBUF_CRANK_DEST: + /* Second pass of 2-pass quantization */ + if (post->whole_image == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + post->pub.post_process_data = post_process_2pass; + break; +#endif /* QUANT_2PASS_SUPPORTED */ + default: + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + break; + } + post->starting_row = post->next_row = 0; +} + + +/* + * Process some data in the one-pass (strip buffer) case. + * This is used for color precision reduction as well as one-pass quantization. + */ + +METHODDEF void +post_process_1pass (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_post_ptr post = (my_post_ptr) cinfo->post; + JDIMENSION num_rows, max_rows; + + /* Fill the buffer, but not more than what we can dump out in one go. */ + /* Note we rely on the upsampler to detect bottom of image. */ + max_rows = out_rows_avail - *out_row_ctr; + if (max_rows > post->strip_height) + max_rows = post->strip_height; + num_rows = 0; + (*cinfo->upsample->upsample) (cinfo, + input_buf, in_row_group_ctr, in_row_groups_avail, + post->buffer, &num_rows, max_rows); + /* Quantize and emit data. */ + (*cinfo->cquantize->color_quantize) (cinfo, + post->buffer, output_buf + *out_row_ctr, (int) num_rows); + *out_row_ctr += num_rows; +} + + +#ifdef QUANT_2PASS_SUPPORTED + +/* + * Process some data in the first pass of 2-pass quantization. + */ + +METHODDEF void +post_process_prepass (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_post_ptr post = (my_post_ptr) cinfo->post; + JDIMENSION old_next_row, num_rows; + + /* Reposition virtual buffer if at start of strip. */ + if (post->next_row == 0) { + post->buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, post->whole_image, + post->starting_row, post->strip_height, TRUE); + } + + /* Upsample some data (up to a strip height's worth). */ + old_next_row = post->next_row; + (*cinfo->upsample->upsample) (cinfo, + input_buf, in_row_group_ctr, in_row_groups_avail, + post->buffer, &post->next_row, post->strip_height); + + /* Allow quantizer to scan new data. No data is emitted, */ + /* but we advance out_row_ctr so outer loop can tell when we're done. */ + if (post->next_row > old_next_row) { + num_rows = post->next_row - old_next_row; + (*cinfo->cquantize->color_quantize) (cinfo, post->buffer + old_next_row, + (JSAMPARRAY) NULL, (int) num_rows); + *out_row_ctr += num_rows; + } + + /* Advance if we filled the strip. */ + if (post->next_row >= post->strip_height) { + post->starting_row += post->strip_height; + post->next_row = 0; + } +} + + +/* + * Process some data in the second pass of 2-pass quantization. + */ + +METHODDEF void +post_process_2pass (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_post_ptr post = (my_post_ptr) cinfo->post; + JDIMENSION num_rows, max_rows; + + /* Reposition virtual buffer if at start of strip. */ + if (post->next_row == 0) { + post->buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, post->whole_image, + post->starting_row, post->strip_height, FALSE); + } + + /* Determine number of rows to emit. */ + num_rows = post->strip_height - post->next_row; /* available in strip */ + max_rows = out_rows_avail - *out_row_ctr; /* available in output area */ + if (num_rows > max_rows) + num_rows = max_rows; + /* We have to check bottom of image here, can't depend on upsampler. */ + max_rows = cinfo->output_height - post->starting_row; + if (num_rows > max_rows) + num_rows = max_rows; + + /* Quantize and emit data. */ + (*cinfo->cquantize->color_quantize) (cinfo, + post->buffer + post->next_row, output_buf + *out_row_ctr, + (int) num_rows); + *out_row_ctr += num_rows; + + /* Advance if we filled the strip. */ + post->next_row += num_rows; + if (post->next_row >= post->strip_height) { + post->starting_row += post->strip_height; + post->next_row = 0; + } +} + +#endif /* QUANT_2PASS_SUPPORTED */ + + +/* + * Initialize postprocessing controller. + */ + +GLOBAL void +jinit_d_post_controller (j_decompress_ptr cinfo, boolean need_full_buffer) +{ + my_post_ptr post; + + post = (my_post_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_post_controller)); + cinfo->post = (struct jpeg_d_post_controller *) post; + post->pub.start_pass = start_pass_dpost; + post->whole_image = NULL; /* flag for no virtual arrays */ + post->buffer = NULL; /* flag for no strip buffer */ + + /* Create the quantization buffer, if needed */ + if (cinfo->quantize_colors) { + /* The buffer strip height is max_v_samp_factor, which is typically + * an efficient number of rows for upsampling to return. + * (In the presence of output rescaling, we might want to be smarter?) + */ + post->strip_height = (JDIMENSION) cinfo->max_v_samp_factor; + if (need_full_buffer) { + /* Two-pass color quantization: need full-image storage. */ + /* We round up the number of rows to a multiple of the strip height. */ +#ifdef QUANT_2PASS_SUPPORTED + post->whole_image = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + cinfo->output_width * cinfo->out_color_components, + (JDIMENSION) jround_up((long) cinfo->output_height, + (long) post->strip_height), + post->strip_height); +#else + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); +#endif /* QUANT_2PASS_SUPPORTED */ + } else { + /* One-pass color quantization: just make a strip buffer. */ + post->buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->output_width * cinfo->out_color_components, + post->strip_height); + } + } +} diff --git a/utils/Libs/jpeg6/jdsample.cpp b/utils/Libs/jpeg6/jdsample.cpp new file mode 100644 index 0000000..bc171f4 --- /dev/null +++ b/utils/Libs/jpeg6/jdsample.cpp @@ -0,0 +1,478 @@ +/* + * jdsample.c + * + * Copyright (C) 1991-1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains upsampling routines. + * + * Upsampling input data is counted in "row groups". A row group + * is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) + * sample rows of each component. Upsampling will normally produce + * max_v_samp_factor pixel rows from each row group (but this could vary + * if the upsampler is applying a scale factor of its own). + * + * An excellent reference for image resampling is + * Digital Image Warping, George Wolberg, 1990. + * Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Pointer to routine to upsample a single component */ +typedef JMETHOD(void, upsample1_ptr, + (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr)); + +/* Private subobject */ + +typedef struct { + struct jpeg_upsampler pub; /* public fields */ + + /* Color conversion buffer. When using separate upsampling and color + * conversion steps, this buffer holds one upsampled row group until it + * has been color converted and output. + * Note: we do not allocate any storage for component(s) which are full-size, + * ie do not need rescaling. The corresponding entry of color_buf[] is + * simply set to point to the input data array, thereby avoiding copying. + */ + JSAMPARRAY color_buf[MAX_COMPONENTS]; + + /* Per-component upsampling method pointers */ + upsample1_ptr methods[MAX_COMPONENTS]; + + int next_row_out; /* counts rows emitted from color_buf */ + JDIMENSION rows_to_go; /* counts rows remaining in image */ + + /* Height of an input row group for each component. */ + int rowgroup_height[MAX_COMPONENTS]; + + /* These arrays save pixel expansion factors so that int_expand need not + * recompute them each time. They are unused for other upsampling methods. + */ + UINT8 h_expand[MAX_COMPONENTS]; + UINT8 v_expand[MAX_COMPONENTS]; +} my_upsampler; + +typedef my_upsampler * my_upsample_ptr; + + +/* + * Initialize for an upsampling pass. + */ + +METHODDEF void +start_pass_upsample (j_decompress_ptr cinfo) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + /* Mark the conversion buffer empty */ + upsample->next_row_out = cinfo->max_v_samp_factor; + /* Initialize total-height counter for detecting bottom of image */ + upsample->rows_to_go = cinfo->output_height; +} + + +/* + * Control routine to do upsampling (and color conversion). + * + * In this version we upsample each component independently. + * We upsample one row group into the conversion buffer, then apply + * color conversion a row at a time. + */ + +METHODDEF void +sep_upsample (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + int ci; + jpeg_component_info * compptr; + JDIMENSION num_rows; + + /* Fill the conversion buffer, if it's empty */ + if (upsample->next_row_out >= cinfo->max_v_samp_factor) { + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Invoke per-component upsample method. Notice we pass a POINTER + * to color_buf[ci], so that fullsize_upsample can change it. + */ + (*upsample->methods[ci]) (cinfo, compptr, + input_buf[ci] + (*in_row_group_ctr * upsample->rowgroup_height[ci]), + upsample->color_buf + ci); + } + upsample->next_row_out = 0; + } + + /* Color-convert and emit rows */ + + /* How many we have in the buffer: */ + num_rows = (JDIMENSION) (cinfo->max_v_samp_factor - upsample->next_row_out); + /* Not more than the distance to the end of the image. Need this test + * in case the image height is not a multiple of max_v_samp_factor: + */ + if (num_rows > upsample->rows_to_go) + num_rows = upsample->rows_to_go; + /* And not more than what the client can accept: */ + out_rows_avail -= *out_row_ctr; + if (num_rows > out_rows_avail) + num_rows = out_rows_avail; + + (*cinfo->cconvert->color_convert) (cinfo, upsample->color_buf, + (JDIMENSION) upsample->next_row_out, + output_buf + *out_row_ctr, + (int) num_rows); + + /* Adjust counts */ + *out_row_ctr += num_rows; + upsample->rows_to_go -= num_rows; + upsample->next_row_out += num_rows; + /* When the buffer is emptied, declare this input row group consumed */ + if (upsample->next_row_out >= cinfo->max_v_samp_factor) + (*in_row_group_ctr)++; +} + + +/* + * These are the routines invoked by sep_upsample to upsample pixel values + * of a single component. One row group is processed per call. + */ + + +/* + * For full-size components, we just make color_buf[ci] point at the + * input buffer, and thus avoid copying any data. Note that this is + * safe only because sep_upsample doesn't declare the input row group + * "consumed" until we are done color converting and emitting it. + */ + +METHODDEF void +fullsize_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + *output_data_ptr = input_data; +} + + +/* + * This is a no-op version used for "uninteresting" components. + * These components will not be referenced by color conversion. + */ + +METHODDEF void +noop_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + *output_data_ptr = NULL; /* safety check */ +} + + +/* + * This version handles any integral sampling ratios. + * This is not used for typical JPEG files, so it need not be fast. + * Nor, for that matter, is it particularly accurate: the algorithm is + * simple replication of the input pixel onto the corresponding output + * pixels. The hi-falutin sampling literature refers to this as a + * "box filter". A box filter tends to introduce visible artifacts, + * so if you are actually going to use 3:1 or 4:1 sampling ratios + * you would be well advised to improve this code. + */ + +METHODDEF void +int_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr, outptr; + register JSAMPLE invalue; + register int h; + JSAMPROW outend; + int h_expand, v_expand; + int inrow, outrow; + + h_expand = upsample->h_expand[compptr->component_index]; + v_expand = upsample->v_expand[compptr->component_index]; + + inrow = outrow = 0; + while (outrow < cinfo->max_v_samp_factor) { + /* Generate one output row with proper horizontal expansion */ + inptr = input_data[inrow]; + outptr = output_data[outrow]; + outend = outptr + cinfo->output_width; + while (outptr < outend) { + invalue = *inptr++; /* don't need GETJSAMPLE() here */ + for (h = h_expand; h > 0; h--) { + *outptr++ = invalue; + } + } + /* Generate any additional output rows by duplicating the first one */ + if (v_expand > 1) { + jcopy_sample_rows(output_data, outrow, output_data, outrow+1, + v_expand-1, cinfo->output_width); + } + inrow++; + outrow += v_expand; + } +} + + +/* + * Fast processing for the common case of 2:1 horizontal and 1:1 vertical. + * It's still a box filter. + */ + +METHODDEF void +h2v1_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr, outptr; + register JSAMPLE invalue; + JSAMPROW outend; + int inrow; + + for (inrow = 0; inrow < cinfo->max_v_samp_factor; inrow++) { + inptr = input_data[inrow]; + outptr = output_data[inrow]; + outend = outptr + cinfo->output_width; + while (outptr < outend) { + invalue = *inptr++; /* don't need GETJSAMPLE() here */ + *outptr++ = invalue; + *outptr++ = invalue; + } + } +} + + +/* + * Fast processing for the common case of 2:1 horizontal and 2:1 vertical. + * It's still a box filter. + */ + +METHODDEF void +h2v2_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr, outptr; + register JSAMPLE invalue; + JSAMPROW outend; + int inrow, outrow; + + inrow = outrow = 0; + while (outrow < cinfo->max_v_samp_factor) { + inptr = input_data[inrow]; + outptr = output_data[outrow]; + outend = outptr + cinfo->output_width; + while (outptr < outend) { + invalue = *inptr++; /* don't need GETJSAMPLE() here */ + *outptr++ = invalue; + *outptr++ = invalue; + } + jcopy_sample_rows(output_data, outrow, output_data, outrow+1, + 1, cinfo->output_width); + inrow++; + outrow += 2; + } +} + + +/* + * Fancy processing for the common case of 2:1 horizontal and 1:1 vertical. + * + * The upsampling algorithm is linear interpolation between pixel centers, + * also known as a "triangle filter". This is a good compromise between + * speed and visual quality. The centers of the output pixels are 1/4 and 3/4 + * of the way between input pixel centers. + * + * A note about the "bias" calculations: when rounding fractional values to + * integer, we do not want to always round 0.5 up to the next integer. + * If we did that, we'd introduce a noticeable bias towards larger values. + * Instead, this code is arranged so that 0.5 will be rounded up or down at + * alternate pixel locations (a simple ordered dither pattern). + */ + +METHODDEF void +h2v1_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr, outptr; + register int invalue; + register JDIMENSION colctr; + int inrow; + + for (inrow = 0; inrow < cinfo->max_v_samp_factor; inrow++) { + inptr = input_data[inrow]; + outptr = output_data[inrow]; + /* Special case for first column */ + invalue = GETJSAMPLE(*inptr++); + *outptr++ = (JSAMPLE) invalue; + *outptr++ = (JSAMPLE) ((invalue * 3 + GETJSAMPLE(*inptr) + 2) >> 2); + + for (colctr = compptr->downsampled_width - 2; colctr > 0; colctr--) { + /* General case: 3/4 * nearer pixel + 1/4 * further pixel */ + invalue = GETJSAMPLE(*inptr++) * 3; + *outptr++ = (JSAMPLE) ((invalue + GETJSAMPLE(inptr[-2]) + 1) >> 2); + *outptr++ = (JSAMPLE) ((invalue + GETJSAMPLE(*inptr) + 2) >> 2); + } + + /* Special case for last column */ + invalue = GETJSAMPLE(*inptr); + *outptr++ = (JSAMPLE) ((invalue * 3 + GETJSAMPLE(inptr[-1]) + 1) >> 2); + *outptr++ = (JSAMPLE) invalue; + } +} + + +/* + * Fancy processing for the common case of 2:1 horizontal and 2:1 vertical. + * Again a triangle filter; see comments for h2v1 case, above. + * + * It is OK for us to reference the adjacent input rows because we demanded + * context from the main buffer controller (see initialization code). + */ + +METHODDEF void +h2v2_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr0, inptr1, outptr; +#if BITS_IN_JSAMPLE == 8 + register int thiscolsum, lastcolsum, nextcolsum; +#else + register INT32 thiscolsum, lastcolsum, nextcolsum; +#endif + register JDIMENSION colctr; + int inrow, outrow, v; + + inrow = outrow = 0; + while (outrow < cinfo->max_v_samp_factor) { + for (v = 0; v < 2; v++) { + /* inptr0 points to nearest input row, inptr1 points to next nearest */ + inptr0 = input_data[inrow]; + if (v == 0) /* next nearest is row above */ + inptr1 = input_data[inrow-1]; + else /* next nearest is row below */ + inptr1 = input_data[inrow+1]; + outptr = output_data[outrow++]; + + /* Special case for first column */ + thiscolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++); + nextcolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++); + *outptr++ = (JSAMPLE) ((thiscolsum * 4 + 8) >> 4); + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + nextcolsum + 7) >> 4); + lastcolsum = thiscolsum; thiscolsum = nextcolsum; + + for (colctr = compptr->downsampled_width - 2; colctr > 0; colctr--) { + /* General case: 3/4 * nearer pixel + 1/4 * further pixel in each */ + /* dimension, thus 9/16, 3/16, 3/16, 1/16 overall */ + nextcolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++); + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + lastcolsum + 8) >> 4); + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + nextcolsum + 7) >> 4); + lastcolsum = thiscolsum; thiscolsum = nextcolsum; + } + + /* Special case for last column */ + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + lastcolsum + 8) >> 4); + *outptr++ = (JSAMPLE) ((thiscolsum * 4 + 7) >> 4); + } + inrow++; + } +} + + +/* + * Module initialization routine for upsampling. + */ + +GLOBAL void +jinit_upsampler (j_decompress_ptr cinfo) +{ + my_upsample_ptr upsample; + int ci; + jpeg_component_info * compptr; + boolean need_buffer, do_fancy; + int h_in_group, v_in_group, h_out_group, v_out_group; + + upsample = (my_upsample_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_upsampler)); + cinfo->upsample = (struct jpeg_upsampler *) upsample; + upsample->pub.start_pass = start_pass_upsample; + upsample->pub.upsample = sep_upsample; + upsample->pub.need_context_rows = FALSE; /* until we find out differently */ + + if (cinfo->CCIR601_sampling) /* this isn't supported */ + ERREXIT(cinfo, JERR_CCIR601_NOTIMPL); + + /* jdmainct.c doesn't support context rows when min_DCT_scaled_size = 1, + * so don't ask for it. + */ + do_fancy = cinfo->do_fancy_upsampling && cinfo->min_DCT_scaled_size > 1; + + /* Verify we can handle the sampling factors, select per-component methods, + * and create storage as needed. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Compute size of an "input group" after IDCT scaling. This many samples + * are to be converted to max_h_samp_factor * max_v_samp_factor pixels. + */ + h_in_group = (compptr->h_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; + v_in_group = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; + h_out_group = cinfo->max_h_samp_factor; + v_out_group = cinfo->max_v_samp_factor; + upsample->rowgroup_height[ci] = v_in_group; /* save for use later */ + need_buffer = TRUE; + if (! compptr->component_needed) { + /* Don't bother to upsample an uninteresting component. */ + upsample->methods[ci] = noop_upsample; + need_buffer = FALSE; + } else if (h_in_group == h_out_group && v_in_group == v_out_group) { + /* Fullsize components can be processed without any work. */ + upsample->methods[ci] = fullsize_upsample; + need_buffer = FALSE; + } else if (h_in_group * 2 == h_out_group && + v_in_group == v_out_group) { + /* Special cases for 2h1v upsampling */ + if (do_fancy && compptr->downsampled_width > 2) + upsample->methods[ci] = h2v1_fancy_upsample; + else + upsample->methods[ci] = h2v1_upsample; + } else if (h_in_group * 2 == h_out_group && + v_in_group * 2 == v_out_group) { + /* Special cases for 2h2v upsampling */ + if (do_fancy && compptr->downsampled_width > 2) { + upsample->methods[ci] = h2v2_fancy_upsample; + upsample->pub.need_context_rows = TRUE; + } else + upsample->methods[ci] = h2v2_upsample; + } else if ((h_out_group % h_in_group) == 0 && + (v_out_group % v_in_group) == 0) { + /* Generic integral-factors upsampling method */ + upsample->methods[ci] = int_upsample; + upsample->h_expand[ci] = (UINT8) (h_out_group / h_in_group); + upsample->v_expand[ci] = (UINT8) (v_out_group / v_in_group); + } else + ERREXIT(cinfo, JERR_FRACT_SAMPLE_NOTIMPL); + if (need_buffer) { + upsample->color_buf[ci] = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) jround_up((long) cinfo->output_width, + (long) cinfo->max_h_samp_factor), + (JDIMENSION) cinfo->max_v_samp_factor); + } + } +} diff --git a/utils/Libs/jpeg6/jdtrans.cpp b/utils/Libs/jpeg6/jdtrans.cpp new file mode 100644 index 0000000..eb873e0 --- /dev/null +++ b/utils/Libs/jpeg6/jdtrans.cpp @@ -0,0 +1,122 @@ +/* + * jdtrans.c + * + * Copyright (C) 1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains library routines for transcoding decompression, + * that is, reading raw DCT coefficient arrays from an input JPEG file. + * The routines in jdapimin.c will also be needed by a transcoder. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Forward declarations */ +LOCAL void transdecode_master_selection JPP((j_decompress_ptr cinfo)); + + +/* + * Read the coefficient arrays from a JPEG file. + * jpeg_read_header must be completed before calling this. + * + * The entire image is read into a set of virtual coefficient-block arrays, + * one per component. The return value is a pointer to the array of + * virtual-array descriptors. These can be manipulated directly via the + * JPEG memory manager, or handed off to jpeg_write_coefficients(). + * To release the memory occupied by the virtual arrays, call + * jpeg_finish_decompress() when done with the data. + * + * Returns NULL if suspended. This case need be checked only if + * a suspending data source is used. + */ + +GLOBAL jvirt_barray_ptr * +jpeg_read_coefficients (j_decompress_ptr cinfo) +{ + if (cinfo->global_state == DSTATE_READY) { + /* First call: initialize active modules */ + transdecode_master_selection(cinfo); + cinfo->global_state = DSTATE_RDCOEFS; + } else if (cinfo->global_state != DSTATE_RDCOEFS) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + /* Absorb whole file into the coef buffer */ + for (;;) { + int retcode; + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + /* Absorb some more input */ + retcode = (*cinfo->inputctl->consume_input) (cinfo); + if (retcode == JPEG_SUSPENDED) + return NULL; + if (retcode == JPEG_REACHED_EOI) + break; + /* Advance progress counter if appropriate */ + if (cinfo->progress != NULL && + (retcode == JPEG_ROW_COMPLETED || retcode == JPEG_REACHED_SOS)) { + if (++cinfo->progress->pass_counter >= cinfo->progress->pass_limit) { + /* startup underestimated number of scans; ratchet up one scan */ + cinfo->progress->pass_limit += (long) cinfo->total_iMCU_rows; + } + } + } + /* Set state so that jpeg_finish_decompress does the right thing */ + cinfo->global_state = DSTATE_STOPPING; + return cinfo->coef->coef_arrays; +} + + +/* + * Master selection of decompression modules for transcoding. + * This substitutes for jdmaster.c's initialization of the full decompressor. + */ + +LOCAL void +transdecode_master_selection (j_decompress_ptr cinfo) +{ + /* Entropy decoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef D_PROGRESSIVE_SUPPORTED + jinit_phuff_decoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_decoder(cinfo); + } + + /* Always get a full-image coefficient buffer. */ + jinit_d_coef_controller(cinfo, TRUE); + + /* We can now tell the memory manager to allocate virtual arrays. */ + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + /* Initialize input side of decompressor to consume first scan. */ + (*cinfo->inputctl->start_input_pass) (cinfo); + + /* Initialize progress monitoring. */ + if (cinfo->progress != NULL) { + int nscans; + /* Estimate number of scans to set pass_limit. */ + if (cinfo->progressive_mode) { + /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ + nscans = 2 + 3 * cinfo->num_components; + } else if (cinfo->inputctl->has_multiple_scans) { + /* For a nonprogressive multiscan file, estimate 1 scan per component. */ + nscans = cinfo->num_components; + } else { + nscans = 1; + } + cinfo->progress->pass_counter = 0L; + cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows * nscans; + cinfo->progress->completed_passes = 0; + cinfo->progress->total_passes = 1; + } +} diff --git a/utils/Libs/jpeg6/jerror.cpp b/utils/Libs/jpeg6/jerror.cpp new file mode 100644 index 0000000..09027d8 --- /dev/null +++ b/utils/Libs/jpeg6/jerror.cpp @@ -0,0 +1,231 @@ +/* + * jerror.c + * + * Copyright (C) 1991-1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains simple error-reporting and trace-message routines. + * These are suitable for Unix-like systems and others where writing to + * stderr is the right thing to do. Many applications will want to replace + * some or all of these routines. + * + * These routines are used by both the compression and decompression code. + */ + +/* this is not a core library module, so it doesn't define JPEG_INTERNALS */ +#include "jinclude.h" +#include "jpeglib.h" +#include "jversion.h" +#include "jerror.h" + +#ifndef EXIT_FAILURE /* define exit() codes if not provided */ +#define EXIT_FAILURE 1 +#endif + + +/* + * Create the message string table. + * We do this from the master message list in jerror.h by re-reading + * jerror.h with a suitable definition for macro JMESSAGE. + * The message table is made an external symbol just in case any applications + * want to refer to it directly. + */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_std_message_table jMsgTable +#endif + +#define JMESSAGE(code,string) string , + +const char * const jpeg_std_message_table[] = { +#include "jerror.h" + NULL +}; + + +/* + * Error exit handler: must not return to caller. + * + * Applications may override this if they want to get control back after + * an error. Typically one would longjmp somewhere instead of exiting. + * The setjmp buffer can be made a private field within an expanded error + * handler object. Note that the info needed to generate an error message + * is stored in the error object, so you can generate the message now or + * later, at your convenience. + * You should make sure that the JPEG object is cleaned up (with jpeg_abort + * or jpeg_destroy) at some point. + */ + +METHODDEF void +error_exit (j_common_ptr cinfo) +{ + char buffer[JMSG_LENGTH_MAX]; + + /* Create the message */ + (*cinfo->err->format_message) (cinfo, buffer); + + /* Let the memory manager delete any temp files before we die */ + jpeg_destroy(cinfo); + + // FIXME: need to get this setup with an error handler + //Error("%s\n", buffer ); +} + + +/* + * Actual output of an error or trace message. + * Applications may override this method to send JPEG messages somewhere + * other than stderr. + */ + +METHODDEF void +output_message (j_common_ptr cinfo) +{ + char buffer[JMSG_LENGTH_MAX]; + + /* Create the message */ + (*cinfo->err->format_message) (cinfo, buffer); + + /* Send it to stderr, adding a newline */ + printf("%s\n", buffer); +} + + +/* + * Decide whether to emit a trace or warning message. + * msg_level is one of: + * -1: recoverable corrupt-data warning, may want to abort. + * 0: important advisory messages (always display to user). + * 1: first level of tracing detail. + * 2,3,...: successively more detailed tracing messages. + * An application might override this method if it wanted to abort on warnings + * or change the policy about which messages to display. + */ + +METHODDEF void +emit_message (j_common_ptr cinfo, int msg_level) +{ + struct jpeg_error_mgr * err = cinfo->err; + + if (msg_level < 0) { + /* It's a warning message. Since corrupt files may generate many warnings, + * the policy implemented here is to show only the first warning, + * unless trace_level >= 3. + */ + if (err->num_warnings == 0 || err->trace_level >= 3) + (*err->output_message) (cinfo); + /* Always count warnings in num_warnings. */ + err->num_warnings++; + } else { + /* It's a trace message. Show it if trace_level >= msg_level. */ + if (err->trace_level >= msg_level) + (*err->output_message) (cinfo); + } +} + + +/* + * Format a message string for the most recent JPEG error or message. + * The message is stored into buffer, which should be at least JMSG_LENGTH_MAX + * characters. Note that no '\n' character is added to the string. + * Few applications should need to override this method. + */ + +METHODDEF void +format_message (j_common_ptr cinfo, char * buffer) +{ + struct jpeg_error_mgr * err = cinfo->err; + int msg_code = err->msg_code; + const char * msgtext = NULL; + const char * msgptr; + char ch; + boolean isstring; + + /* Look up message string in proper table */ + if (msg_code > 0 && msg_code <= err->last_jpeg_message) { + msgtext = err->jpeg_message_table[msg_code]; + } else if (err->addon_message_table != NULL && + msg_code >= err->first_addon_message && + msg_code <= err->last_addon_message) { + msgtext = err->addon_message_table[msg_code - err->first_addon_message]; + } + + /* Defend against bogus message number */ + if (msgtext == NULL) { + err->msg_parm.i[0] = msg_code; + msgtext = err->jpeg_message_table[0]; + } + + /* Check for string parameter, as indicated by %s in the message text */ + isstring = FALSE; + msgptr = msgtext; + while ((ch = *msgptr++) != '\0') { + if (ch == '%') { + if (*msgptr == 's') isstring = TRUE; + break; + } + } + + /* Format the message into the passed buffer */ + if (isstring) + sprintf(buffer, msgtext, err->msg_parm.s); + else + sprintf(buffer, msgtext, + err->msg_parm.i[0], err->msg_parm.i[1], + err->msg_parm.i[2], err->msg_parm.i[3], + err->msg_parm.i[4], err->msg_parm.i[5], + err->msg_parm.i[6], err->msg_parm.i[7]); +} + + +/* + * Reset error state variables at start of a new image. + * This is called during compression startup to reset trace/error + * processing to default state, without losing any application-specific + * method pointers. An application might possibly want to override + * this method if it has additional error processing state. + */ + +METHODDEF void +reset_error_mgr (j_common_ptr cinfo) +{ + cinfo->err->num_warnings = 0; + /* trace_level is not reset since it is an application-supplied parameter */ + cinfo->err->msg_code = 0; /* may be useful as a flag for "no error" */ +} + + +/* + * Fill in the standard error-handling methods in a jpeg_error_mgr object. + * Typical call is: + * struct jpeg_compress_struct cinfo; + * struct jpeg_error_mgr err; + * + * cinfo.err = jpeg_std_error(&err); + * after which the application may override some of the methods. + */ + +GLOBAL struct jpeg_error_mgr * +jpeg_std_error (struct jpeg_error_mgr * err) +{ + err->error_exit = error_exit; + err->emit_message = emit_message; + err->output_message = output_message; + err->format_message = format_message; + err->reset_error_mgr = reset_error_mgr; + + err->trace_level = 0; /* default = no tracing */ + err->num_warnings = 0; /* no warnings emitted yet */ + err->msg_code = 0; /* may be useful as a flag for "no error" */ + + /* Initialize message table pointers */ + err->jpeg_message_table = jpeg_std_message_table; + err->last_jpeg_message = (int) JMSG_LASTMSGCODE - 1; + + err->addon_message_table = NULL; + err->first_addon_message = 0; /* for safety */ + err->last_addon_message = 0; + + return err; +} diff --git a/utils/Libs/jpeg6/jerror.h b/utils/Libs/jpeg6/jerror.h new file mode 100644 index 0000000..0ffb8b4 --- /dev/null +++ b/utils/Libs/jpeg6/jerror.h @@ -0,0 +1,273 @@ +/* + * jerror.h + * + * Copyright (C) 1994-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the error and message codes for the JPEG library. + * Edit this file to add new codes, or to translate the message strings to + * some other language. + * A set of error-reporting macros are defined too. Some applications using + * the JPEG library may wish to include this file to get the error codes + * and/or the macros. + */ + +/* + * To define the enum list of message codes, include this file without + * defining macro JMESSAGE. To create a message string table, include it + * again with a suitable JMESSAGE definition (see jerror.c for an example). + */ +#ifndef JMESSAGE +#ifndef JERROR_H +/* First time through, define the enum list */ +#define JMAKE_ENUM_LIST +#else +/* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */ +#define JMESSAGE(code,string) +#endif /* JERROR_H */ +#endif /* JMESSAGE */ + +#ifdef JMAKE_ENUM_LIST + +typedef enum { + +#define JMESSAGE(code,string) code , + +#endif /* JMAKE_ENUM_LIST */ + +JMESSAGE(JMSG_NOMESSAGE, "Bogus message code %d") /* Must be first entry! */ + +/* For maintenance convenience, list is alphabetical by message code name */ +JMESSAGE(JERR_ARITH_NOTIMPL, + "Sorry, there are legal restrictions on arithmetic coding") +JMESSAGE(JERR_BAD_ALIGN_TYPE, "ALIGN_TYPE is wrong, please fix") +JMESSAGE(JERR_BAD_ALLOC_CHUNK, "MAX_ALLOC_CHUNK is wrong, please fix") +JMESSAGE(JERR_BAD_BUFFER_MODE, "Bogus buffer control mode") +JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS") +JMESSAGE(JERR_BAD_DCTSIZE, "IDCT output block size %d not supported") +JMESSAGE(JERR_BAD_IN_COLORSPACE, "Bogus input colorspace") +JMESSAGE(JERR_BAD_J_COLORSPACE, "Bogus JPEG colorspace") +JMESSAGE(JERR_BAD_LENGTH, "Bogus marker length") +JMESSAGE(JERR_BAD_MCU_SIZE, "Sampling factors too large for interleaved scan") +JMESSAGE(JERR_BAD_POOL_ID, "Invalid memory pool code %d") +JMESSAGE(JERR_BAD_PRECISION, "Unsupported JPEG data precision %d") +JMESSAGE(JERR_BAD_PROGRESSION, + "Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d") +JMESSAGE(JERR_BAD_PROG_SCRIPT, + "Invalid progressive parameters at scan script entry %d") +JMESSAGE(JERR_BAD_SAMPLING, "Bogus sampling factors") +JMESSAGE(JERR_BAD_SCAN_SCRIPT, "Invalid scan script at entry %d") +JMESSAGE(JERR_BAD_STATE, "Improper call to JPEG library in state %d") +JMESSAGE(JERR_BAD_VIRTUAL_ACCESS, "Bogus virtual array access") +JMESSAGE(JERR_BUFFER_SIZE, "Buffer passed to JPEG library is too small") +JMESSAGE(JERR_CANT_SUSPEND, "Suspension not allowed here") +JMESSAGE(JERR_CCIR601_NOTIMPL, "CCIR601 sampling not implemented yet") +JMESSAGE(JERR_COMPONENT_COUNT, "Too many color components: %d, max %d") +JMESSAGE(JERR_CONVERSION_NOTIMPL, "Unsupported color conversion request") +JMESSAGE(JERR_DAC_INDEX, "Bogus DAC index %d") +JMESSAGE(JERR_DAC_VALUE, "Bogus DAC value 0x%x") +JMESSAGE(JERR_DHT_COUNTS, "Bogus DHT counts") +JMESSAGE(JERR_DHT_INDEX, "Bogus DHT index %d") +JMESSAGE(JERR_DQT_INDEX, "Bogus DQT index %d") +JMESSAGE(JERR_EMPTY_IMAGE, "Empty JPEG image (DNL not supported)") +JMESSAGE(JERR_EMS_READ, "Read from EMS failed") +JMESSAGE(JERR_EMS_WRITE, "Write to EMS failed") +JMESSAGE(JERR_EOI_EXPECTED, "Didn't expect more than one scan") +JMESSAGE(JERR_FILE_READ, "Input file read error") +JMESSAGE(JERR_FILE_WRITE, "Output file write error --- out of disk space?") +JMESSAGE(JERR_FRACT_SAMPLE_NOTIMPL, "Fractional sampling not implemented yet") +JMESSAGE(JERR_HUFF_CLEN_OVERFLOW, "Huffman code size table overflow") +JMESSAGE(JERR_HUFF_MISSING_CODE, "Missing Huffman code table entry") +JMESSAGE(JERR_IMAGE_TOO_BIG, "Maximum supported image dimension is %u pixels") +JMESSAGE(JERR_INPUT_EMPTY, "Empty input file") +JMESSAGE(JERR_INPUT_EOF, "Premature end of input file") +JMESSAGE(JERR_MISMATCHED_QUANT_TABLE, + "Cannot transcode due to multiple use of quantization table %d") +JMESSAGE(JERR_MISSING_DATA, "Scan script does not transmit all data") +JMESSAGE(JERR_MODE_CHANGE, "Invalid color quantization mode change") +JMESSAGE(JERR_NOTIMPL, "Not implemented yet") +JMESSAGE(JERR_NOT_COMPILED, "Requested feature was omitted at compile time") +JMESSAGE(JERR_NO_BACKING_STORE, "Backing store not supported") +JMESSAGE(JERR_NO_HUFF_TABLE, "Huffman table 0x%02x was not defined") +JMESSAGE(JERR_NO_IMAGE, "JPEG datastream contains no image") +JMESSAGE(JERR_NO_QUANT_TABLE, "Quantization table 0x%02x was not defined") +JMESSAGE(JERR_NO_SOI, "Not a JPEG file: starts with 0x%02x 0x%02x") +JMESSAGE(JERR_OUT_OF_MEMORY, "Insufficient memory (case %d)") +JMESSAGE(JERR_QUANT_COMPONENTS, + "Cannot quantize more than %d color components") +JMESSAGE(JERR_QUANT_FEW_COLORS, "Cannot quantize to fewer than %d colors") +JMESSAGE(JERR_QUANT_MANY_COLORS, "Cannot quantize to more than %d colors") +JMESSAGE(JERR_SOF_DUPLICATE, "Invalid JPEG file structure: two SOF markers") +JMESSAGE(JERR_SOF_NO_SOS, "Invalid JPEG file structure: missing SOS marker") +JMESSAGE(JERR_SOF_UNSUPPORTED, "Unsupported JPEG process: SOF type 0x%02x") +JMESSAGE(JERR_SOI_DUPLICATE, "Invalid JPEG file structure: two SOI markers") +JMESSAGE(JERR_SOS_NO_SOF, "Invalid JPEG file structure: SOS before SOF") +JMESSAGE(JERR_TFILE_CREATE, "Failed to create temporary file %s") +JMESSAGE(JERR_TFILE_READ, "Read failed on temporary file") +JMESSAGE(JERR_TFILE_SEEK, "Seek failed on temporary file") +JMESSAGE(JERR_TFILE_WRITE, + "Write failed on temporary file --- out of disk space?") +JMESSAGE(JERR_TOO_LITTLE_DATA, "Application transferred too few scanlines") +JMESSAGE(JERR_UNKNOWN_MARKER, "Unsupported marker type 0x%02x") +JMESSAGE(JERR_VIRTUAL_BUG, "Virtual array controller messed up") +JMESSAGE(JERR_WIDTH_OVERFLOW, "Image too wide for this implementation") +JMESSAGE(JERR_XMS_READ, "Read from XMS failed") +JMESSAGE(JERR_XMS_WRITE, "Write to XMS failed") +JMESSAGE(JMSG_COPYRIGHT, JCOPYRIGHT) +JMESSAGE(JMSG_VERSION, JVERSION) +JMESSAGE(JTRC_16BIT_TABLES, + "Caution: quantization tables are too coarse for baseline JPEG") +JMESSAGE(JTRC_ADOBE, + "Adobe APP14 marker: version %d, flags 0x%04x 0x%04x, transform %d") +JMESSAGE(JTRC_APP0, "Unknown APP0 marker (not JFIF), length %u") +JMESSAGE(JTRC_APP14, "Unknown APP14 marker (not Adobe), length %u") +JMESSAGE(JTRC_DAC, "Define Arithmetic Table 0x%02x: 0x%02x") +JMESSAGE(JTRC_DHT, "Define Huffman Table 0x%02x") +JMESSAGE(JTRC_DQT, "Define Quantization Table %d precision %d") +JMESSAGE(JTRC_DRI, "Define Restart Interval %u") +JMESSAGE(JTRC_EMS_CLOSE, "Freed EMS handle %u") +JMESSAGE(JTRC_EMS_OPEN, "Obtained EMS handle %u") +JMESSAGE(JTRC_EOI, "End Of Image") +JMESSAGE(JTRC_HUFFBITS, " %3d %3d %3d %3d %3d %3d %3d %3d") +JMESSAGE(JTRC_JFIF, "JFIF APP0 marker, density %dx%d %d") +JMESSAGE(JTRC_JFIF_BADTHUMBNAILSIZE, + "Warning: thumbnail image size does not match data length %u") +JMESSAGE(JTRC_JFIF_MINOR, "Unknown JFIF minor revision number %d.%02d") +JMESSAGE(JTRC_JFIF_THUMBNAIL, " with %d x %d thumbnail image") +JMESSAGE(JTRC_MISC_MARKER, "Skipping marker 0x%02x, length %u") +JMESSAGE(JTRC_PARMLESS_MARKER, "Unexpected marker 0x%02x") +JMESSAGE(JTRC_QUANTVALS, " %4u %4u %4u %4u %4u %4u %4u %4u") +JMESSAGE(JTRC_QUANT_3_NCOLORS, "Quantizing to %d = %d*%d*%d colors") +JMESSAGE(JTRC_QUANT_NCOLORS, "Quantizing to %d colors") +JMESSAGE(JTRC_QUANT_SELECTED, "Selected %d colors for quantization") +JMESSAGE(JTRC_RECOVERY_ACTION, "At marker 0x%02x, recovery action %d") +JMESSAGE(JTRC_RST, "RST%d") +JMESSAGE(JTRC_SMOOTH_NOTIMPL, + "Smoothing not supported with nonstandard sampling ratios") +JMESSAGE(JTRC_SOF, "Start Of Frame 0x%02x: width=%u, height=%u, components=%d") +JMESSAGE(JTRC_SOF_COMPONENT, " Component %d: %dhx%dv q=%d") +JMESSAGE(JTRC_SOI, "Start of Image") +JMESSAGE(JTRC_SOS, "Start Of Scan: %d components") +JMESSAGE(JTRC_SOS_COMPONENT, " Component %d: dc=%d ac=%d") +JMESSAGE(JTRC_SOS_PARAMS, " Ss=%d, Se=%d, Ah=%d, Al=%d") +JMESSAGE(JTRC_TFILE_CLOSE, "Closed temporary file %s") +JMESSAGE(JTRC_TFILE_OPEN, "Opened temporary file %s") +JMESSAGE(JTRC_UNKNOWN_IDS, + "Unrecognized component IDs %d %d %d, assuming YCbCr") +JMESSAGE(JTRC_XMS_CLOSE, "Freed XMS handle %u") +JMESSAGE(JTRC_XMS_OPEN, "Obtained XMS handle %u") +JMESSAGE(JWRN_ADOBE_XFORM, "Unknown Adobe color transform code %d") +JMESSAGE(JWRN_BOGUS_PROGRESSION, + "Inconsistent progression sequence for component %d coefficient %d") +JMESSAGE(JWRN_EXTRANEOUS_DATA, + "Corrupt JPEG data: %u extraneous bytes before marker 0x%02x") +JMESSAGE(JWRN_HIT_MARKER, "Corrupt JPEG data: premature end of data segment") +JMESSAGE(JWRN_HUFF_BAD_CODE, "Corrupt JPEG data: bad Huffman code") +JMESSAGE(JWRN_JFIF_MAJOR, "Warning: unknown JFIF revision number %d.%02d") +JMESSAGE(JWRN_JPEG_EOF, "Premature end of JPEG file") +JMESSAGE(JWRN_MUST_RESYNC, + "Corrupt JPEG data: found marker 0x%02x instead of RST%d") +JMESSAGE(JWRN_NOT_SEQUENTIAL, "Invalid SOS parameters for sequential JPEG") +JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") + +#ifdef JMAKE_ENUM_LIST + + JMSG_LASTMSGCODE +} J_MESSAGE_CODE; + +#undef JMAKE_ENUM_LIST +#endif /* JMAKE_ENUM_LIST */ + +/* Zap JMESSAGE macro so that future re-inclusions do nothing by default */ +#undef JMESSAGE + + +#ifndef JERROR_H +#define JERROR_H + +/* Macros to simplify using the error and trace message stuff */ +/* The first parameter is either type of cinfo pointer */ + +/* Fatal errors (print message and exit) */ +#define ERREXIT(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT3(cinfo,code,p1,p2,p3) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT4(cinfo,code,p1,p2,p3,p4) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (cinfo)->err->msg_parm.i[3] = (p4), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXITS(cinfo,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) + +#define MAKESTMT(stuff) do { stuff } while (0) + +/* Nonfatal errors (we can keep going, but the data is probably corrupt) */ +#define WARNMS(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) + +/* Informational/debugging messages */ +#define TRACEMS(cinfo,lvl,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS1(cinfo,lvl,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS2(cinfo,lvl,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS3(cinfo,lvl,code,p1,p2,p3) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS4(cinfo,lvl,code,p1,p2,p3,p4) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS8(cinfo,lvl,code,p1,p2,p3,p4,p5,p6,p7,p8) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); _mp[5] = (p6); _mp[6] = (p7); _mp[7] = (p8); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMSS(cinfo,lvl,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) + +#endif /* JERROR_H */ diff --git a/utils/Libs/jpeg6/jfdctflt.cpp b/utils/Libs/jpeg6/jfdctflt.cpp new file mode 100644 index 0000000..1509b88 --- /dev/null +++ b/utils/Libs/jpeg6/jfdctflt.cpp @@ -0,0 +1,168 @@ +/* + * jfdctflt.c + * + * Copyright (C) 1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a floating-point implementation of the + * forward DCT (Discrete Cosine Transform). + * + * This implementation should be more accurate than either of the integer + * DCT implementations. However, it may not give the same results on all + * machines because of differences in roundoff behavior. Speed will depend + * on the hardware's floating point capacity. + * + * A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT + * on each column. Direct algorithms are also available, but they are + * much more complex and seem not to be any faster when reduced to code. + * + * This implementation is based on Arai, Agui, and Nakajima's algorithm for + * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in + * Japanese, but the algorithm is described in the Pennebaker & Mitchell + * JPEG textbook (see REFERENCES section in file README). The following code + * is based directly on figure 4-8 in P&M. + * While an 8-point DCT cannot be done in less than 11 multiplies, it is + * possible to arrange the computation so that many of the multiplies are + * simple scalings of the final outputs. These multiplies can then be + * folded into the multiplications or divisions by the JPEG quantization + * table entries. The AA&N method leaves only 5 multiplies and 29 adds + * to be done in the DCT itself. + * The primary disadvantage of this method is that with a fixed-point + * implementation, accuracy is lost due to imprecise representation of the + * scaled quantization values. However, that problem does not arise if + * we use floating point arithmetic. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + +#ifdef DCT_FLOAT_SUPPORTED + + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + + +/* + * Perform the forward DCT on one block of samples. + */ + +GLOBAL void +jpeg_fdct_float (FAST_FLOAT * data) +{ + FAST_FLOAT tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + FAST_FLOAT tmp10, tmp11, tmp12, tmp13; + FAST_FLOAT z1, z2, z3, z4, z5, z11, z13; + FAST_FLOAT *dataptr; + int ctr; + + /* Pass 1: process rows. */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[0] + dataptr[7]; + tmp7 = dataptr[0] - dataptr[7]; + tmp1 = dataptr[1] + dataptr[6]; + tmp6 = dataptr[1] - dataptr[6]; + tmp2 = dataptr[2] + dataptr[5]; + tmp5 = dataptr[2] - dataptr[5]; + tmp3 = dataptr[3] + dataptr[4]; + tmp4 = dataptr[3] - dataptr[4]; + + /* Even part */ + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[0] = tmp10 + tmp11; /* phase 3 */ + dataptr[4] = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * ((FAST_FLOAT) 0.707106781); /* c4 */ + dataptr[2] = tmp13 + z1; /* phase 5 */ + dataptr[6] = tmp13 - z1; + + /* Odd part */ + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + z5 = (tmp10 - tmp12) * ((FAST_FLOAT) 0.382683433); /* c6 */ + z2 = ((FAST_FLOAT) 0.541196100) * tmp10 + z5; /* c2-c6 */ + z4 = ((FAST_FLOAT) 1.306562965) * tmp12 + z5; /* c2+c6 */ + z3 = tmp11 * ((FAST_FLOAT) 0.707106781); /* c4 */ + + z11 = tmp7 + z3; /* phase 5 */ + z13 = tmp7 - z3; + + dataptr[5] = z13 + z2; /* phase 6 */ + dataptr[3] = z13 - z2; + dataptr[1] = z11 + z4; + dataptr[7] = z11 - z4; + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*7]; + tmp7 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*7]; + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*6]; + tmp6 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*6]; + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*5]; + tmp5 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*5]; + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*4]; + tmp4 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*4]; + + /* Even part */ + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[DCTSIZE*0] = tmp10 + tmp11; /* phase 3 */ + dataptr[DCTSIZE*4] = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * ((FAST_FLOAT) 0.707106781); /* c4 */ + dataptr[DCTSIZE*2] = tmp13 + z1; /* phase 5 */ + dataptr[DCTSIZE*6] = tmp13 - z1; + + /* Odd part */ + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + z5 = (tmp10 - tmp12) * ((FAST_FLOAT) 0.382683433); /* c6 */ + z2 = ((FAST_FLOAT) 0.541196100) * tmp10 + z5; /* c2-c6 */ + z4 = ((FAST_FLOAT) 1.306562965) * tmp12 + z5; /* c2+c6 */ + z3 = tmp11 * ((FAST_FLOAT) 0.707106781); /* c4 */ + + z11 = tmp7 + z3; /* phase 5 */ + z13 = tmp7 - z3; + + dataptr[DCTSIZE*5] = z13 + z2; /* phase 6 */ + dataptr[DCTSIZE*3] = z13 - z2; + dataptr[DCTSIZE*1] = z11 + z4; + dataptr[DCTSIZE*7] = z11 - z4; + + dataptr++; /* advance pointer to next column */ + } +} + +#endif /* DCT_FLOAT_SUPPORTED */ diff --git a/utils/Libs/jpeg6/jidctflt.cpp b/utils/Libs/jpeg6/jidctflt.cpp new file mode 100644 index 0000000..2e25c44 --- /dev/null +++ b/utils/Libs/jpeg6/jidctflt.cpp @@ -0,0 +1,241 @@ +/* + * jidctflt.c + * + * Copyright (C) 1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a floating-point implementation of the + * inverse DCT (Discrete Cosine Transform). In the IJG code, this routine + * must also perform dequantization of the input coefficients. + * + * This implementation should be more accurate than either of the integer + * IDCT implementations. However, it may not give the same results on all + * machines because of differences in roundoff behavior. Speed will depend + * on the hardware's floating point capacity. + * + * A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT + * on each row (or vice versa, but it's more convenient to emit a row at + * a time). Direct algorithms are also available, but they are much more + * complex and seem not to be any faster when reduced to code. + * + * This implementation is based on Arai, Agui, and Nakajima's algorithm for + * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in + * Japanese, but the algorithm is described in the Pennebaker & Mitchell + * JPEG textbook (see REFERENCES section in file README). The following code + * is based directly on figure 4-8 in P&M. + * While an 8-point DCT cannot be done in less than 11 multiplies, it is + * possible to arrange the computation so that many of the multiplies are + * simple scalings of the final outputs. These multiplies can then be + * folded into the multiplications or divisions by the JPEG quantization + * table entries. The AA&N method leaves only 5 multiplies and 29 adds + * to be done in the DCT itself. + * The primary disadvantage of this method is that with a fixed-point + * implementation, accuracy is lost due to imprecise representation of the + * scaled quantization values. However, that problem does not arise if + * we use floating point arithmetic. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + +#ifdef DCT_FLOAT_SUPPORTED + + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + + +/* Dequantize a coefficient by multiplying it by the multiplier-table + * entry; produce a float result. + */ + +#define DEQUANTIZE(coef,quantval) (((FAST_FLOAT) (coef)) * (quantval)) + + +/* + * Perform dequantization and inverse DCT on one block of coefficients. + */ + +GLOBAL void +jpeg_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + FAST_FLOAT tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + FAST_FLOAT tmp10, tmp11, tmp12, tmp13; + FAST_FLOAT z5, z10, z11, z12, z13; + JCOEFPTR inptr; + FLOAT_MULT_TYPE * quantptr; + FAST_FLOAT * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + FAST_FLOAT workspace[DCTSIZE2]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (FLOAT_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = DCTSIZE; ctr > 0; ctr--) { + /* Due to quantization, we will usually find that many of the input + * coefficients are zero, especially the AC terms. We can exploit this + * by short-circuiting the IDCT calculation for any column in which all + * the AC terms are zero. In that case each output is equal to the + * DC coefficient (with scale factor as needed). + * With typical images and quantization tables, half or more of the + * column DCT calculations can be simplified this way. + */ + + if ((inptr[DCTSIZE*1] | inptr[DCTSIZE*2] | inptr[DCTSIZE*3] | + inptr[DCTSIZE*4] | inptr[DCTSIZE*5] | inptr[DCTSIZE*6] | + inptr[DCTSIZE*7]) == 0) { + /* AC terms all zero */ + FAST_FLOAT dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + + wsptr[DCTSIZE*0] = dcval; + wsptr[DCTSIZE*1] = dcval; + wsptr[DCTSIZE*2] = dcval; + wsptr[DCTSIZE*3] = dcval; + wsptr[DCTSIZE*4] = dcval; + wsptr[DCTSIZE*5] = dcval; + wsptr[DCTSIZE*6] = dcval; + wsptr[DCTSIZE*7] = dcval; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + continue; + } + + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + tmp3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + tmp10 = tmp0 + tmp2; /* phase 3 */ + tmp11 = tmp0 - tmp2; + + tmp13 = tmp1 + tmp3; /* phases 5-3 */ + tmp12 = (tmp1 - tmp3) * ((FAST_FLOAT) 1.414213562) - tmp13; /* 2*c4 */ + + tmp0 = tmp10 + tmp13; /* phase 2 */ + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + tmp4 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + tmp5 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + tmp6 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + tmp7 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + z13 = tmp6 + tmp5; /* phase 6 */ + z10 = tmp6 - tmp5; + z11 = tmp4 + tmp7; + z12 = tmp4 - tmp7; + + tmp7 = z11 + z13; /* phase 5 */ + tmp11 = (z11 - z13) * ((FAST_FLOAT) 1.414213562); /* 2*c4 */ + + z5 = (z10 + z12) * ((FAST_FLOAT) 1.847759065); /* 2*c2 */ + tmp10 = ((FAST_FLOAT) 1.082392200) * z12 - z5; /* 2*(c2-c6) */ + tmp12 = ((FAST_FLOAT) -2.613125930) * z10 + z5; /* -2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; /* phase 2 */ + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + wsptr[DCTSIZE*0] = tmp0 + tmp7; + wsptr[DCTSIZE*7] = tmp0 - tmp7; + wsptr[DCTSIZE*1] = tmp1 + tmp6; + wsptr[DCTSIZE*6] = tmp1 - tmp6; + wsptr[DCTSIZE*2] = tmp2 + tmp5; + wsptr[DCTSIZE*5] = tmp2 - tmp5; + wsptr[DCTSIZE*4] = tmp3 + tmp4; + wsptr[DCTSIZE*3] = tmp3 - tmp4; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + } + + /* Pass 2: process rows from work array, store into output array. */ + /* Note that we must descale the results by a factor of 8 == 2**3. */ + + wsptr = workspace; + for (ctr = 0; ctr < DCTSIZE; ctr++) { + outptr = output_buf[ctr] + output_col; + /* Rows of zeroes can be exploited in the same way as we did with columns. + * However, the column calculation has created many nonzero AC terms, so + * the simplification applies less often (typically 5% to 10% of the time). + * And testing floats for zero is relatively expensive, so we don't bother. + */ + + /* Even part */ + + tmp10 = wsptr[0] + wsptr[4]; + tmp11 = wsptr[0] - wsptr[4]; + + tmp13 = wsptr[2] + wsptr[6]; + tmp12 = (wsptr[2] - wsptr[6]) * ((FAST_FLOAT) 1.414213562) - tmp13; + + tmp0 = tmp10 + tmp13; + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + z13 = wsptr[5] + wsptr[3]; + z10 = wsptr[5] - wsptr[3]; + z11 = wsptr[1] + wsptr[7]; + z12 = wsptr[1] - wsptr[7]; + + tmp7 = z11 + z13; + tmp11 = (z11 - z13) * ((FAST_FLOAT) 1.414213562); + + z5 = (z10 + z12) * ((FAST_FLOAT) 1.847759065); /* 2*c2 */ + tmp10 = ((FAST_FLOAT) 1.082392200) * z12 - z5; /* 2*(c2-c6) */ + tmp12 = ((FAST_FLOAT) -2.613125930) * z10 + z5; /* -2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + /* Final output stage: scale down by a factor of 8 and range-limit */ + + outptr[0] = range_limit[(int) DESCALE((INT32) (tmp0 + tmp7), 3) + & RANGE_MASK]; + outptr[7] = range_limit[(int) DESCALE((INT32) (tmp0 - tmp7), 3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) DESCALE((INT32) (tmp1 + tmp6), 3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) DESCALE((INT32) (tmp1 - tmp6), 3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) DESCALE((INT32) (tmp2 + tmp5), 3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) DESCALE((INT32) (tmp2 - tmp5), 3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) DESCALE((INT32) (tmp3 + tmp4), 3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) DESCALE((INT32) (tmp3 - tmp4), 3) + & RANGE_MASK]; + + wsptr += DCTSIZE; /* advance pointer to next row */ + } +} + +#endif /* DCT_FLOAT_SUPPORTED */ diff --git a/utils/Libs/jpeg6/jinclude.h b/utils/Libs/jpeg6/jinclude.h new file mode 100644 index 0000000..5ff60fe --- /dev/null +++ b/utils/Libs/jpeg6/jinclude.h @@ -0,0 +1,91 @@ +/* + * jinclude.h + * + * Copyright (C) 1991-1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file exists to provide a single place to fix any problems with + * including the wrong system include files. (Common problems are taken + * care of by the standard jconfig symbols, but on really weird systems + * you may have to edit this file.) + * + * NOTE: this file is NOT intended to be included by applications using the + * JPEG library. Most applications need only include jpeglib.h. + */ + + +/* Include auto-config file to find out which system include files we need. */ + +#include "jconfig.h" /* auto configuration options */ +#define JCONFIG_INCLUDED /* so that jpeglib.h doesn't do it again */ + +/* + * We need the NULL macro and size_t typedef. + * On an ANSI-conforming system it is sufficient to include . + * Otherwise, we get them from or ; we may have to + * pull in as well. + * Note that the core JPEG library does not require ; + * only the default error handler and data source/destination modules do. + * But we must pull it in because of the references to FILE in jpeglib.h. + * You can remove those references if you want to compile without . + */ + +#ifdef HAVE_STDDEF_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef NEED_SYS_TYPES_H +#include +#endif + +#include + +/* + * We need memory copying and zeroing functions, plus strncpy(). + * ANSI and System V implementations declare these in . + * BSD doesn't have the mem() functions, but it does have bcopy()/bzero(). + * Some systems may declare memset and memcpy in . + * + * NOTE: we assume the size parameters to these functions are of type size_t. + * Change the casts in these macros if not! + */ + +#ifdef NEED_BSD_STRINGS + +#include +#define MEMZERO(target,size) bzero((void *)(target), (size_t)(size)) +#define MEMCOPY(dest,src,size) bcopy((const void *)(src), (void *)(dest), (size_t)(size)) + +#else /* not BSD, assume ANSI/SysV string lib */ + +#include +#define MEMZERO(target,size) memset((void *)(target), 0, (size_t)(size)) +#define MEMCOPY(dest,src,size) memcpy((void *)(dest), (const void *)(src), (size_t)(size)) + +#endif + +/* + * In ANSI C, and indeed any rational implementation, size_t is also the + * type returned by sizeof(). However, it seems there are some irrational + * implementations out there, in which sizeof() returns an int even though + * size_t is defined as long or unsigned long. To ensure consistent results + * we always use this SIZEOF() macro in place of using sizeof() directly. + */ + +#define SIZEOF(object) ((size_t) sizeof(object)) + +/* + * The modules that use fread() and fwrite() always invoke them through + * these macros. On some systems you may need to twiddle the argument casts. + * CAUTION: argument order is different from underlying functions! + */ + +#define JFREAD(file,buf,sizeofbuf) \ + ((size_t) fread((void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) +#define JFWRITE(file,buf,sizeofbuf) \ + ((size_t) fwrite((const void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) diff --git a/utils/Libs/jpeg6/jmemmgr.cpp b/utils/Libs/jpeg6/jmemmgr.cpp new file mode 100644 index 0000000..61045f9 --- /dev/null +++ b/utils/Libs/jpeg6/jmemmgr.cpp @@ -0,0 +1,1115 @@ +/* + * jmemmgr.c + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the JPEG system-independent memory management + * routines. This code is usable across a wide variety of machines; most + * of the system dependencies have been isolated in a separate file. + * The major functions provided here are: + * * pool-based allocation and freeing of memory; + * * policy decisions about how to divide available memory among the + * virtual arrays; + * * control logic for swapping virtual arrays between main memory and + * backing storage. + * The separate system-dependent file provides the actual backing-storage + * access code, and it contains the policy decision about how much total + * main memory to use. + * This file is system-dependent in the sense that some of its functions + * are unnecessary in some systems. For example, if there is enough virtual + * memory so that backing storage will never be used, much of the virtual + * array control logic could be removed. (Of course, if you have that much + * memory then you shouldn't care about a little bit of unused code...) + */ + +#define JPEG_INTERNALS +#define AM_MEMORY_MANAGER /* we define jvirt_Xarray_control structs */ +#include "jinclude.h" +#include "jpeglib.h" +#include "jmemsys.h" /* import the system-dependent declarations */ + +#ifndef NO_GETENV +#ifndef HAVE_STDLIB_H /* should declare getenv() */ +extern char * getenv JPP((const char * name)); +#endif +#endif + + +/* + * Some important notes: + * The allocation routines provided here must never return NULL. + * They should exit to error_exit if unsuccessful. + * + * It's not a good idea to try to merge the sarray and barray routines, + * even though they are textually almost the same, because samples are + * usually stored as bytes while coefficients are shorts or ints. Thus, + * in machines where byte pointers have a different representation from + * word pointers, the resulting machine code could not be the same. + */ + + +/* + * Many machines require storage alignment: longs must start on 4-byte + * boundaries, doubles on 8-byte boundaries, etc. On such machines, malloc() + * always returns pointers that are multiples of the worst-case alignment + * requirement, and we had better do so too. + * There isn't any really portable way to determine the worst-case alignment + * requirement. This module assumes that the alignment requirement is + * multiples of sizeof(ALIGN_TYPE). + * By default, we define ALIGN_TYPE as double. This is necessary on some + * workstations (where doubles really do need 8-byte alignment) and will work + * fine on nearly everything. If your machine has lesser alignment needs, + * you can save a few bytes by making ALIGN_TYPE smaller. + * The only place I know of where this will NOT work is certain Macintosh + * 680x0 compilers that define double as a 10-byte IEEE extended float. + * Doing 10-byte alignment is counterproductive because longwords won't be + * aligned well. Put "#define ALIGN_TYPE long" in jconfig.h if you have + * such a compiler. + */ + +#ifndef ALIGN_TYPE /* so can override from jconfig.h */ +#define ALIGN_TYPE double +#endif + + +/* + * We allocate objects from "pools", where each pool is gotten with a single + * request to jpeg_get_small() or jpeg_get_large(). There is no per-object + * overhead within a pool, except for alignment padding. Each pool has a + * header with a link to the next pool of the same class. + * Small and large pool headers are identical except that the latter's + * link pointer must be FAR on 80x86 machines. + * Notice that the "real" header fields are union'ed with a dummy ALIGN_TYPE + * field. This forces the compiler to make SIZEOF(small_pool_hdr) a multiple + * of the alignment requirement of ALIGN_TYPE. + */ + +typedef union small_pool_struct * small_pool_ptr; + +typedef union small_pool_struct { + struct { + small_pool_ptr next; /* next in list of pools */ + size_t bytes_used; /* how many bytes already used within pool */ + size_t bytes_left; /* bytes still available in this pool */ + } hdr; + ALIGN_TYPE dummy; /* included in union to ensure alignment */ +} small_pool_hdr; + +typedef union large_pool_struct FAR * large_pool_ptr; + +typedef union large_pool_struct { + struct { + large_pool_ptr next; /* next in list of pools */ + size_t bytes_used; /* how many bytes already used within pool */ + size_t bytes_left; /* bytes still available in this pool */ + } hdr; + ALIGN_TYPE dummy; /* included in union to ensure alignment */ +} large_pool_hdr; + + +/* + * Here is the full definition of a memory manager object. + */ + +typedef struct { + struct jpeg_memory_mgr pub; /* public fields */ + + /* Each pool identifier (lifetime class) names a linked list of pools. */ + small_pool_ptr small_list[JPOOL_NUMPOOLS]; + large_pool_ptr large_list[JPOOL_NUMPOOLS]; + + /* Since we only have one lifetime class of virtual arrays, only one + * linked list is necessary (for each datatype). Note that the virtual + * array control blocks being linked together are actually stored somewhere + * in the small-pool list. + */ + jvirt_sarray_ptr virt_sarray_list; + jvirt_barray_ptr virt_barray_list; + + /* This counts total space obtained from jpeg_get_small/large */ + long total_space_allocated; + + /* alloc_sarray and alloc_barray set this value for use by virtual + * array routines. + */ + JDIMENSION last_rowsperchunk; /* from most recent alloc_sarray/barray */ +} my_memory_mgr; + +typedef my_memory_mgr * my_mem_ptr; + + +/* + * The control blocks for virtual arrays. + * Note that these blocks are allocated in the "small" pool area. + * System-dependent info for the associated backing store (if any) is hidden + * inside the backing_store_info struct. + */ + +struct jvirt_sarray_control { + JSAMPARRAY mem_buffer; /* => the in-memory buffer */ + JDIMENSION rows_in_array; /* total virtual array height */ + JDIMENSION samplesperrow; /* width of array (and of memory buffer) */ + JDIMENSION maxaccess; /* max rows accessed by access_virt_sarray */ + JDIMENSION rows_in_mem; /* height of memory buffer */ + JDIMENSION rowsperchunk; /* allocation chunk size in mem_buffer */ + JDIMENSION cur_start_row; /* first logical row # in the buffer */ + JDIMENSION first_undef_row; /* row # of first uninitialized row */ + boolean pre_zero; /* pre-zero mode requested? */ + boolean dirty; /* do current buffer contents need written? */ + boolean b_s_open; /* is backing-store data valid? */ + jvirt_sarray_ptr next; /* link to next virtual sarray control block */ + backing_store_info b_s_info; /* System-dependent control info */ +}; + +struct jvirt_barray_control { + JBLOCKARRAY mem_buffer; /* => the in-memory buffer */ + JDIMENSION rows_in_array; /* total virtual array height */ + JDIMENSION blocksperrow; /* width of array (and of memory buffer) */ + JDIMENSION maxaccess; /* max rows accessed by access_virt_barray */ + JDIMENSION rows_in_mem; /* height of memory buffer */ + JDIMENSION rowsperchunk; /* allocation chunk size in mem_buffer */ + JDIMENSION cur_start_row; /* first logical row # in the buffer */ + JDIMENSION first_undef_row; /* row # of first uninitialized row */ + boolean pre_zero; /* pre-zero mode requested? */ + boolean dirty; /* do current buffer contents need written? */ + boolean b_s_open; /* is backing-store data valid? */ + jvirt_barray_ptr next; /* link to next virtual barray control block */ + backing_store_info b_s_info; /* System-dependent control info */ +}; + + +#ifdef MEM_STATS /* optional extra stuff for statistics */ + +LOCAL void +print_mem_stats (j_common_ptr cinfo, int pool_id) +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + small_pool_ptr shdr_ptr; + large_pool_ptr lhdr_ptr; + + /* Since this is only a debugging stub, we can cheat a little by using + * fprintf directly rather than going through the trace message code. + * This is helpful because message parm array can't handle longs. + */ + fprintf(stderr, "Freeing pool %d, total space = %ld\n", + pool_id, mem->total_space_allocated); + + for (lhdr_ptr = mem->large_list[pool_id]; lhdr_ptr != NULL; + lhdr_ptr = lhdr_ptr->hdr.next) { + fprintf(stderr, " Large chunk used %ld\n", + (long) lhdr_ptr->hdr.bytes_used); + } + + for (shdr_ptr = mem->small_list[pool_id]; shdr_ptr != NULL; + shdr_ptr = shdr_ptr->hdr.next) { + fprintf(stderr, " Small chunk used %ld free %ld\n", + (long) shdr_ptr->hdr.bytes_used, + (long) shdr_ptr->hdr.bytes_left); + } +} + +#endif /* MEM_STATS */ + + +LOCAL void +out_of_memory (j_common_ptr cinfo, int which) +/* Report an out-of-memory error and stop execution */ +/* If we compiled MEM_STATS support, report alloc requests before dying */ +{ +#ifdef MEM_STATS + cinfo->err->trace_level = 2; /* force self_destruct to report stats */ +#endif + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, which); +} + + +/* + * Allocation of "small" objects. + * + * For these, we use pooled storage. When a new pool must be created, + * we try to get enough space for the current request plus a "slop" factor, + * where the slop will be the amount of leftover space in the new pool. + * The speed vs. space tradeoff is largely determined by the slop values. + * A different slop value is provided for each pool class (lifetime), + * and we also distinguish the first pool of a class from later ones. + * NOTE: the values given work fairly well on both 16- and 32-bit-int + * machines, but may be too small if longs are 64 bits or more. + */ + +static const size_t first_pool_slop[JPOOL_NUMPOOLS] = +{ + 1600, /* first PERMANENT pool */ + 16000 /* first IMAGE pool */ +}; + +static const size_t extra_pool_slop[JPOOL_NUMPOOLS] = +{ + 0, /* additional PERMANENT pools */ + 5000 /* additional IMAGE pools */ +}; + +#define MIN_SLOP 50 /* greater than 0 to avoid futile looping */ + + +METHODDEF void * +alloc_small (j_common_ptr cinfo, int pool_id, size_t sizeofobject) +/* Allocate a "small" object */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + small_pool_ptr hdr_ptr, prev_hdr_ptr; + char * data_ptr; + size_t odd_bytes, min_request, slop; + + /* Check for unsatisfiable request (do now to ensure no overflow below) */ + if (sizeofobject > (size_t) (MAX_ALLOC_CHUNK-SIZEOF(small_pool_hdr))) + out_of_memory(cinfo, 1); /* request exceeds malloc's ability */ + + /* Round up the requested size to a multiple of SIZEOF(ALIGN_TYPE) */ + odd_bytes = sizeofobject % SIZEOF(ALIGN_TYPE); + if (odd_bytes > 0) + sizeofobject += SIZEOF(ALIGN_TYPE) - odd_bytes; + + /* See if space is available in any existing pool */ + if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + prev_hdr_ptr = NULL; + hdr_ptr = mem->small_list[pool_id]; + while (hdr_ptr != NULL) { + if (hdr_ptr->hdr.bytes_left >= sizeofobject) + break; /* found pool with enough space */ + prev_hdr_ptr = hdr_ptr; + hdr_ptr = hdr_ptr->hdr.next; + } + + /* Time to make a new pool? */ + if (hdr_ptr == NULL) { + /* min_request is what we need now, slop is what will be leftover */ + min_request = sizeofobject + SIZEOF(small_pool_hdr); + if (prev_hdr_ptr == NULL) /* first pool in class? */ + slop = first_pool_slop[pool_id]; + else + slop = extra_pool_slop[pool_id]; + /* Don't ask for more than MAX_ALLOC_CHUNK */ + if (slop > (size_t) (MAX_ALLOC_CHUNK-min_request)) + slop = (size_t) (MAX_ALLOC_CHUNK-min_request); + /* Try to get space, if fail reduce slop and try again */ + for (;;) { + hdr_ptr = (small_pool_ptr) jpeg_get_small(cinfo, min_request + slop); + if (hdr_ptr != NULL) + break; + slop /= 2; + if (slop < MIN_SLOP) /* give up when it gets real small */ + out_of_memory(cinfo, 2); /* jpeg_get_small failed */ + } + mem->total_space_allocated += min_request + slop; + /* Success, initialize the new pool header and add to end of list */ + hdr_ptr->hdr.next = NULL; + hdr_ptr->hdr.bytes_used = 0; + hdr_ptr->hdr.bytes_left = sizeofobject + slop; + if (prev_hdr_ptr == NULL) /* first pool in class? */ + mem->small_list[pool_id] = hdr_ptr; + else + prev_hdr_ptr->hdr.next = hdr_ptr; + } + + /* OK, allocate the object from the current pool */ + data_ptr = (char *) (hdr_ptr + 1); /* point to first data byte in pool */ + data_ptr += hdr_ptr->hdr.bytes_used; /* point to place for object */ + hdr_ptr->hdr.bytes_used += sizeofobject; + hdr_ptr->hdr.bytes_left -= sizeofobject; + + return (void *) data_ptr; +} + + +/* + * Allocation of "large" objects. + * + * The external semantics of these are the same as "small" objects, + * except that FAR pointers are used on 80x86. However the pool + * management heuristics are quite different. We assume that each + * request is large enough that it may as well be passed directly to + * jpeg_get_large; the pool management just links everything together + * so that we can free it all on demand. + * Note: the major use of "large" objects is in JSAMPARRAY and JBLOCKARRAY + * structures. The routines that create these structures (see below) + * deliberately bunch rows together to ensure a large request size. + */ + +METHODDEF void FAR * +alloc_large (j_common_ptr cinfo, int pool_id, size_t sizeofobject) +/* Allocate a "large" object */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + large_pool_ptr hdr_ptr; + size_t odd_bytes; + + /* Check for unsatisfiable request (do now to ensure no overflow below) */ + if (sizeofobject > (size_t) (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr))) + out_of_memory(cinfo, 3); /* request exceeds malloc's ability */ + + /* Round up the requested size to a multiple of SIZEOF(ALIGN_TYPE) */ + odd_bytes = sizeofobject % SIZEOF(ALIGN_TYPE); + if (odd_bytes > 0) + sizeofobject += SIZEOF(ALIGN_TYPE) - odd_bytes; + + /* Always make a new pool */ + if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + hdr_ptr = (large_pool_ptr) jpeg_get_large(cinfo, sizeofobject + + SIZEOF(large_pool_hdr)); + if (hdr_ptr == NULL) + out_of_memory(cinfo, 4); /* jpeg_get_large failed */ + mem->total_space_allocated += sizeofobject + SIZEOF(large_pool_hdr); + + /* Success, initialize the new pool header and add to list */ + hdr_ptr->hdr.next = mem->large_list[pool_id]; + /* We maintain space counts in each pool header for statistical purposes, + * even though they are not needed for allocation. + */ + hdr_ptr->hdr.bytes_used = sizeofobject; + hdr_ptr->hdr.bytes_left = 0; + mem->large_list[pool_id] = hdr_ptr; + + return (void FAR *) (hdr_ptr + 1); /* point to first data byte in pool */ +} + + +/* + * Creation of 2-D sample arrays. + * The pointers are in near heap, the samples themselves in FAR heap. + * + * To minimize allocation overhead and to allow I/O of large contiguous + * blocks, we allocate the sample rows in groups of as many rows as possible + * without exceeding MAX_ALLOC_CHUNK total bytes per allocation request. + * NB: the virtual array control routines, later in this file, know about + * this chunking of rows. The rowsperchunk value is left in the mem manager + * object so that it can be saved away if this sarray is the workspace for + * a virtual array. + */ + +METHODDEF JSAMPARRAY +alloc_sarray (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, JDIMENSION numrows) +/* Allocate a 2-D sample array */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + JSAMPARRAY result; + JSAMPROW workspace; + JDIMENSION rowsperchunk, currow, i; + long ltemp; + + /* Calculate max # of rows allowed in one allocation chunk */ + ltemp = (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr)) / + ((long) samplesperrow * SIZEOF(JSAMPLE)); + if (ltemp <= 0) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + if (ltemp < (long) numrows) + rowsperchunk = (JDIMENSION) ltemp; + else + rowsperchunk = numrows; + mem->last_rowsperchunk = rowsperchunk; + + /* Get space for row pointers (small object) */ + result = (JSAMPARRAY) alloc_small(cinfo, pool_id, + (size_t) (numrows * SIZEOF(JSAMPROW))); + + /* Get the rows themselves (large objects) */ + currow = 0; + while (currow < numrows) { + rowsperchunk = MIN(rowsperchunk, numrows - currow); + workspace = (JSAMPROW) alloc_large(cinfo, pool_id, + (size_t) ((size_t) rowsperchunk * (size_t) samplesperrow + * SIZEOF(JSAMPLE))); + for (i = rowsperchunk; i > 0; i--) { + result[currow++] = workspace; + workspace += samplesperrow; + } + } + + return result; +} + + +/* + * Creation of 2-D coefficient-block arrays. + * This is essentially the same as the code for sample arrays, above. + */ + +METHODDEF JBLOCKARRAY +alloc_barray (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, JDIMENSION numrows) +/* Allocate a 2-D coefficient-block array */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + JBLOCKARRAY result; + JBLOCKROW workspace; + JDIMENSION rowsperchunk, currow, i; + long ltemp; + + /* Calculate max # of rows allowed in one allocation chunk */ + ltemp = (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr)) / + ((long) blocksperrow * SIZEOF(JBLOCK)); + if (ltemp <= 0) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + if (ltemp < (long) numrows) + rowsperchunk = (JDIMENSION) ltemp; + else + rowsperchunk = numrows; + mem->last_rowsperchunk = rowsperchunk; + + /* Get space for row pointers (small object) */ + result = (JBLOCKARRAY) alloc_small(cinfo, pool_id, + (size_t) (numrows * SIZEOF(JBLOCKROW))); + + /* Get the rows themselves (large objects) */ + currow = 0; + while (currow < numrows) { + rowsperchunk = MIN(rowsperchunk, numrows - currow); + workspace = (JBLOCKROW) alloc_large(cinfo, pool_id, + (size_t) ((size_t) rowsperchunk * (size_t) blocksperrow + * SIZEOF(JBLOCK))); + for (i = rowsperchunk; i > 0; i--) { + result[currow++] = workspace; + workspace += blocksperrow; + } + } + + return result; +} + + +/* + * About virtual array management: + * + * The above "normal" array routines are only used to allocate strip buffers + * (as wide as the image, but just a few rows high). Full-image-sized buffers + * are handled as "virtual" arrays. The array is still accessed a strip at a + * time, but the memory manager must save the whole array for repeated + * accesses. The intended implementation is that there is a strip buffer in + * memory (as high as is possible given the desired memory limit), plus a + * backing file that holds the rest of the array. + * + * The request_virt_array routines are told the total size of the image and + * the maximum number of rows that will be accessed at once. The in-memory + * buffer must be at least as large as the maxaccess value. + * + * The request routines create control blocks but not the in-memory buffers. + * That is postponed until realize_virt_arrays is called. At that time the + * total amount of space needed is known (approximately, anyway), so free + * memory can be divided up fairly. + * + * The access_virt_array routines are responsible for making a specific strip + * area accessible (after reading or writing the backing file, if necessary). + * Note that the access routines are told whether the caller intends to modify + * the accessed strip; during a read-only pass this saves having to rewrite + * data to disk. The access routines are also responsible for pre-zeroing + * any newly accessed rows, if pre-zeroing was requested. + * + * In current usage, the access requests are usually for nonoverlapping + * strips; that is, successive access start_row numbers differ by exactly + * num_rows = maxaccess. This means we can get good performance with simple + * buffer dump/reload logic, by making the in-memory buffer be a multiple + * of the access height; then there will never be accesses across bufferload + * boundaries. The code will still work with overlapping access requests, + * but it doesn't handle bufferload overlaps very efficiently. + */ + + +METHODDEF jvirt_sarray_ptr +request_virt_sarray (j_common_ptr cinfo, int pool_id, boolean pre_zero, + JDIMENSION samplesperrow, JDIMENSION numrows, + JDIMENSION maxaccess) +/* Request a virtual 2-D sample array */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + jvirt_sarray_ptr result; + + /* Only IMAGE-lifetime virtual arrays are currently supported */ + if (pool_id != JPOOL_IMAGE) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + /* get control block */ + result = (jvirt_sarray_ptr) alloc_small(cinfo, pool_id, + SIZEOF(struct jvirt_sarray_control)); + + result->mem_buffer = NULL; /* marks array not yet realized */ + result->rows_in_array = numrows; + result->samplesperrow = samplesperrow; + result->maxaccess = maxaccess; + result->pre_zero = pre_zero; + result->b_s_open = FALSE; /* no associated backing-store object */ + result->next = mem->virt_sarray_list; /* add to list of virtual arrays */ + mem->virt_sarray_list = result; + + return result; +} + + +METHODDEF jvirt_barray_ptr +request_virt_barray (j_common_ptr cinfo, int pool_id, boolean pre_zero, + JDIMENSION blocksperrow, JDIMENSION numrows, + JDIMENSION maxaccess) +/* Request a virtual 2-D coefficient-block array */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + jvirt_barray_ptr result; + + /* Only IMAGE-lifetime virtual arrays are currently supported */ + if (pool_id != JPOOL_IMAGE) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + /* get control block */ + result = (jvirt_barray_ptr) alloc_small(cinfo, pool_id, + SIZEOF(struct jvirt_barray_control)); + + result->mem_buffer = NULL; /* marks array not yet realized */ + result->rows_in_array = numrows; + result->blocksperrow = blocksperrow; + result->maxaccess = maxaccess; + result->pre_zero = pre_zero; + result->b_s_open = FALSE; /* no associated backing-store object */ + result->next = mem->virt_barray_list; /* add to list of virtual arrays */ + mem->virt_barray_list = result; + + return result; +} + + +METHODDEF void +realize_virt_arrays (j_common_ptr cinfo) +/* Allocate the in-memory buffers for any unrealized virtual arrays */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + long space_per_minheight, maximum_space, avail_mem; + long minheights, max_minheights; + jvirt_sarray_ptr sptr; + jvirt_barray_ptr bptr; + + /* Compute the minimum space needed (maxaccess rows in each buffer) + * and the maximum space needed (full image height in each buffer). + * These may be of use to the system-dependent jpeg_mem_available routine. + */ + space_per_minheight = 0; + maximum_space = 0; + for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { + if (sptr->mem_buffer == NULL) { /* if not realized yet */ + space_per_minheight += (long) sptr->maxaccess * + (long) sptr->samplesperrow * SIZEOF(JSAMPLE); + maximum_space += (long) sptr->rows_in_array * + (long) sptr->samplesperrow * SIZEOF(JSAMPLE); + } + } + for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { + if (bptr->mem_buffer == NULL) { /* if not realized yet */ + space_per_minheight += (long) bptr->maxaccess * + (long) bptr->blocksperrow * SIZEOF(JBLOCK); + maximum_space += (long) bptr->rows_in_array * + (long) bptr->blocksperrow * SIZEOF(JBLOCK); + } + } + + if (space_per_minheight <= 0) + return; /* no unrealized arrays, no work */ + + /* Determine amount of memory to actually use; this is system-dependent. */ + avail_mem = jpeg_mem_available(cinfo, space_per_minheight, maximum_space, + mem->total_space_allocated); + + /* If the maximum space needed is available, make all the buffers full + * height; otherwise parcel it out with the same number of minheights + * in each buffer. + */ + if (avail_mem >= maximum_space) + max_minheights = 1000000000L; + else { + max_minheights = avail_mem / space_per_minheight; + /* If there doesn't seem to be enough space, try to get the minimum + * anyway. This allows a "stub" implementation of jpeg_mem_available(). + */ + if (max_minheights <= 0) + max_minheights = 1; + } + + /* Allocate the in-memory buffers and initialize backing store as needed. */ + + for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { + if (sptr->mem_buffer == NULL) { /* if not realized yet */ + minheights = ((long) sptr->rows_in_array - 1L) / sptr->maxaccess + 1L; + if (minheights <= max_minheights) { + /* This buffer fits in memory */ + sptr->rows_in_mem = sptr->rows_in_array; + } else { + /* It doesn't fit in memory, create backing store. */ + sptr->rows_in_mem = (JDIMENSION) (max_minheights * sptr->maxaccess); + jpeg_open_backing_store(cinfo, & sptr->b_s_info, + (long) sptr->rows_in_array * + (long) sptr->samplesperrow * + (long) SIZEOF(JSAMPLE)); + sptr->b_s_open = TRUE; + } + sptr->mem_buffer = alloc_sarray(cinfo, JPOOL_IMAGE, + sptr->samplesperrow, sptr->rows_in_mem); + sptr->rowsperchunk = mem->last_rowsperchunk; + sptr->cur_start_row = 0; + sptr->first_undef_row = 0; + sptr->dirty = FALSE; + } + } + + for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { + if (bptr->mem_buffer == NULL) { /* if not realized yet */ + minheights = ((long) bptr->rows_in_array - 1L) / bptr->maxaccess + 1L; + if (minheights <= max_minheights) { + /* This buffer fits in memory */ + bptr->rows_in_mem = bptr->rows_in_array; + } else { + /* It doesn't fit in memory, create backing store. */ + bptr->rows_in_mem = (JDIMENSION) (max_minheights * bptr->maxaccess); + jpeg_open_backing_store(cinfo, & bptr->b_s_info, + (long) bptr->rows_in_array * + (long) bptr->blocksperrow * + (long) SIZEOF(JBLOCK)); + bptr->b_s_open = TRUE; + } + bptr->mem_buffer = alloc_barray(cinfo, JPOOL_IMAGE, + bptr->blocksperrow, bptr->rows_in_mem); + bptr->rowsperchunk = mem->last_rowsperchunk; + bptr->cur_start_row = 0; + bptr->first_undef_row = 0; + bptr->dirty = FALSE; + } + } +} + + +LOCAL void +do_sarray_io (j_common_ptr cinfo, jvirt_sarray_ptr ptr, boolean writing) +/* Do backing store read or write of a virtual sample array */ +{ + long bytesperrow, file_offset, byte_count, rows, thisrow, i; + + bytesperrow = (long) ptr->samplesperrow * SIZEOF(JSAMPLE); + file_offset = ptr->cur_start_row * bytesperrow; + /* Loop to read or write each allocation chunk in mem_buffer */ + for (i = 0; i < (long) ptr->rows_in_mem; i += ptr->rowsperchunk) { + /* One chunk, but check for short chunk at end of buffer */ + rows = MIN((long) ptr->rowsperchunk, (long) ptr->rows_in_mem - i); + /* Transfer no more than is currently defined */ + thisrow = (long) ptr->cur_start_row + i; + rows = MIN(rows, (long) ptr->first_undef_row - thisrow); + /* Transfer no more than fits in file */ + rows = MIN(rows, (long) ptr->rows_in_array - thisrow); + if (rows <= 0) /* this chunk might be past end of file! */ + break; + byte_count = rows * bytesperrow; + if (writing) + (*ptr->b_s_info.write_backing_store) (cinfo, & ptr->b_s_info, + (void FAR *) ptr->mem_buffer[i], + file_offset, byte_count); + else + (*ptr->b_s_info.read_backing_store) (cinfo, & ptr->b_s_info, + (void FAR *) ptr->mem_buffer[i], + file_offset, byte_count); + file_offset += byte_count; + } +} + + +LOCAL void +do_barray_io (j_common_ptr cinfo, jvirt_barray_ptr ptr, boolean writing) +/* Do backing store read or write of a virtual coefficient-block array */ +{ + long bytesperrow, file_offset, byte_count, rows, thisrow, i; + + bytesperrow = (long) ptr->blocksperrow * SIZEOF(JBLOCK); + file_offset = ptr->cur_start_row * bytesperrow; + /* Loop to read or write each allocation chunk in mem_buffer */ + for (i = 0; i < (long) ptr->rows_in_mem; i += ptr->rowsperchunk) { + /* One chunk, but check for short chunk at end of buffer */ + rows = MIN((long) ptr->rowsperchunk, (long) ptr->rows_in_mem - i); + /* Transfer no more than is currently defined */ + thisrow = (long) ptr->cur_start_row + i; + rows = MIN(rows, (long) ptr->first_undef_row - thisrow); + /* Transfer no more than fits in file */ + rows = MIN(rows, (long) ptr->rows_in_array - thisrow); + if (rows <= 0) /* this chunk might be past end of file! */ + break; + byte_count = rows * bytesperrow; + if (writing) + (*ptr->b_s_info.write_backing_store) (cinfo, & ptr->b_s_info, + (void FAR *) ptr->mem_buffer[i], + file_offset, byte_count); + else + (*ptr->b_s_info.read_backing_store) (cinfo, & ptr->b_s_info, + (void FAR *) ptr->mem_buffer[i], + file_offset, byte_count); + file_offset += byte_count; + } +} + + +METHODDEF JSAMPARRAY +access_virt_sarray (j_common_ptr cinfo, jvirt_sarray_ptr ptr, + JDIMENSION start_row, JDIMENSION num_rows, + boolean writable) +/* Access the part of a virtual sample array starting at start_row */ +/* and extending for num_rows rows. writable is true if */ +/* caller intends to modify the accessed area. */ +{ + JDIMENSION end_row = start_row + num_rows; + JDIMENSION undef_row; + + /* debugging check */ + if (end_row > ptr->rows_in_array || num_rows > ptr->maxaccess || + ptr->mem_buffer == NULL) + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + /* Make the desired part of the virtual array accessible */ + if (start_row < ptr->cur_start_row || + end_row > ptr->cur_start_row+ptr->rows_in_mem) { + if (! ptr->b_s_open) + ERREXIT(cinfo, JERR_VIRTUAL_BUG); + /* Flush old buffer contents if necessary */ + if (ptr->dirty) { + do_sarray_io(cinfo, ptr, TRUE); + ptr->dirty = FALSE; + } + /* Decide what part of virtual array to access. + * Algorithm: if target address > current window, assume forward scan, + * load starting at target address. If target address < current window, + * assume backward scan, load so that target area is top of window. + * Note that when switching from forward write to forward read, will have + * start_row = 0, so the limiting case applies and we load from 0 anyway. + */ + if (start_row > ptr->cur_start_row) { + ptr->cur_start_row = start_row; + } else { + /* use long arithmetic here to avoid overflow & unsigned problems */ + long ltemp; + + ltemp = (long) end_row - (long) ptr->rows_in_mem; + if (ltemp < 0) + ltemp = 0; /* don't fall off front end of file */ + ptr->cur_start_row = (JDIMENSION) ltemp; + } + /* Read in the selected part of the array. + * During the initial write pass, we will do no actual read + * because the selected part is all undefined. + */ + do_sarray_io(cinfo, ptr, FALSE); + } + /* Ensure the accessed part of the array is defined; prezero if needed. + * To improve locality of access, we only prezero the part of the array + * that the caller is about to access, not the entire in-memory array. + */ + if (ptr->first_undef_row < end_row) { + if (ptr->first_undef_row < start_row) { + if (writable) /* writer skipped over a section of array */ + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + undef_row = start_row; /* but reader is allowed to read ahead */ + } else { + undef_row = ptr->first_undef_row; + } + if (writable) + ptr->first_undef_row = end_row; + if (ptr->pre_zero) { + size_t bytesperrow = (size_t) ptr->samplesperrow * SIZEOF(JSAMPLE); + undef_row -= ptr->cur_start_row; /* make indexes relative to buffer */ + end_row -= ptr->cur_start_row; + while (undef_row < end_row) { + jzero_far((void FAR *) ptr->mem_buffer[undef_row], bytesperrow); + undef_row++; + } + } else { + if (! writable) /* reader looking at undefined data */ + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + } + } + /* Flag the buffer dirty if caller will write in it */ + if (writable) + ptr->dirty = TRUE; + /* Return address of proper part of the buffer */ + return ptr->mem_buffer + (start_row - ptr->cur_start_row); +} + + +METHODDEF JBLOCKARRAY +access_virt_barray (j_common_ptr cinfo, jvirt_barray_ptr ptr, + JDIMENSION start_row, JDIMENSION num_rows, + boolean writable) +/* Access the part of a virtual block array starting at start_row */ +/* and extending for num_rows rows. writable is true if */ +/* caller intends to modify the accessed area. */ +{ + JDIMENSION end_row = start_row + num_rows; + JDIMENSION undef_row; + + /* debugging check */ + if (end_row > ptr->rows_in_array || num_rows > ptr->maxaccess || + ptr->mem_buffer == NULL) + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + /* Make the desired part of the virtual array accessible */ + if (start_row < ptr->cur_start_row || + end_row > ptr->cur_start_row+ptr->rows_in_mem) { + if (! ptr->b_s_open) + ERREXIT(cinfo, JERR_VIRTUAL_BUG); + /* Flush old buffer contents if necessary */ + if (ptr->dirty) { + do_barray_io(cinfo, ptr, TRUE); + ptr->dirty = FALSE; + } + /* Decide what part of virtual array to access. + * Algorithm: if target address > current window, assume forward scan, + * load starting at target address. If target address < current window, + * assume backward scan, load so that target area is top of window. + * Note that when switching from forward write to forward read, will have + * start_row = 0, so the limiting case applies and we load from 0 anyway. + */ + if (start_row > ptr->cur_start_row) { + ptr->cur_start_row = start_row; + } else { + /* use long arithmetic here to avoid overflow & unsigned problems */ + long ltemp; + + ltemp = (long) end_row - (long) ptr->rows_in_mem; + if (ltemp < 0) + ltemp = 0; /* don't fall off front end of file */ + ptr->cur_start_row = (JDIMENSION) ltemp; + } + /* Read in the selected part of the array. + * During the initial write pass, we will do no actual read + * because the selected part is all undefined. + */ + do_barray_io(cinfo, ptr, FALSE); + } + /* Ensure the accessed part of the array is defined; prezero if needed. + * To improve locality of access, we only prezero the part of the array + * that the caller is about to access, not the entire in-memory array. + */ + if (ptr->first_undef_row < end_row) { + if (ptr->first_undef_row < start_row) { + if (writable) /* writer skipped over a section of array */ + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + undef_row = start_row; /* but reader is allowed to read ahead */ + } else { + undef_row = ptr->first_undef_row; + } + if (writable) + ptr->first_undef_row = end_row; + if (ptr->pre_zero) { + size_t bytesperrow = (size_t) ptr->blocksperrow * SIZEOF(JBLOCK); + undef_row -= ptr->cur_start_row; /* make indexes relative to buffer */ + end_row -= ptr->cur_start_row; + while (undef_row < end_row) { + jzero_far((void FAR *) ptr->mem_buffer[undef_row], bytesperrow); + undef_row++; + } + } else { + if (! writable) /* reader looking at undefined data */ + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + } + } + /* Flag the buffer dirty if caller will write in it */ + if (writable) + ptr->dirty = TRUE; + /* Return address of proper part of the buffer */ + return ptr->mem_buffer + (start_row - ptr->cur_start_row); +} + + +/* + * Release all objects belonging to a specified pool. + */ + +METHODDEF void +free_pool (j_common_ptr cinfo, int pool_id) +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + small_pool_ptr shdr_ptr; + large_pool_ptr lhdr_ptr; + size_t space_freed; + + if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + +#ifdef MEM_STATS + if (cinfo->err->trace_level > 1) + print_mem_stats(cinfo, pool_id); /* print pool's memory usage statistics */ +#endif + + /* If freeing IMAGE pool, close any virtual arrays first */ + if (pool_id == JPOOL_IMAGE) { + jvirt_sarray_ptr sptr; + jvirt_barray_ptr bptr; + + for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { + if (sptr->b_s_open) { /* there may be no backing store */ + sptr->b_s_open = FALSE; /* prevent recursive close if error */ + (*sptr->b_s_info.close_backing_store) (cinfo, & sptr->b_s_info); + } + } + mem->virt_sarray_list = NULL; + for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { + if (bptr->b_s_open) { /* there may be no backing store */ + bptr->b_s_open = FALSE; /* prevent recursive close if error */ + (*bptr->b_s_info.close_backing_store) (cinfo, & bptr->b_s_info); + } + } + mem->virt_barray_list = NULL; + } + + /* Release large objects */ + lhdr_ptr = mem->large_list[pool_id]; + mem->large_list[pool_id] = NULL; + + while (lhdr_ptr != NULL) { + large_pool_ptr next_lhdr_ptr = lhdr_ptr->hdr.next; + space_freed = lhdr_ptr->hdr.bytes_used + + lhdr_ptr->hdr.bytes_left + + SIZEOF(large_pool_hdr); + jpeg_free_large(cinfo, (void FAR *) lhdr_ptr, space_freed); + mem->total_space_allocated -= space_freed; + lhdr_ptr = next_lhdr_ptr; + } + + /* Release small objects */ + shdr_ptr = mem->small_list[pool_id]; + mem->small_list[pool_id] = NULL; + + while (shdr_ptr != NULL) { + small_pool_ptr next_shdr_ptr = shdr_ptr->hdr.next; + space_freed = shdr_ptr->hdr.bytes_used + + shdr_ptr->hdr.bytes_left + + SIZEOF(small_pool_hdr); + jpeg_free_small(cinfo, (void *) shdr_ptr, space_freed); + mem->total_space_allocated -= space_freed; + shdr_ptr = next_shdr_ptr; + } +} + + +/* + * Close up shop entirely. + * Note that this cannot be called unless cinfo->mem is non-NULL. + */ + +METHODDEF void +self_destruct (j_common_ptr cinfo) +{ + int pool; + + /* Close all backing store, release all memory. + * Releasing pools in reverse order might help avoid fragmentation + * with some (brain-damaged) malloc libraries. + */ + for (pool = JPOOL_NUMPOOLS-1; pool >= JPOOL_PERMANENT; pool--) { + free_pool(cinfo, pool); + } + + /* Release the memory manager control block too. */ + jpeg_free_small(cinfo, (void *) cinfo->mem, SIZEOF(my_memory_mgr)); + cinfo->mem = NULL; /* ensures I will be called only once */ + + jpeg_mem_term(cinfo); /* system-dependent cleanup */ +} + + +/* + * Memory manager initialization. + * When this is called, only the error manager pointer is valid in cinfo! + */ + +GLOBAL void +jinit_memory_mgr (j_common_ptr cinfo) +{ + my_mem_ptr mem; + long max_to_use; + int pool; + size_t test_mac; + + cinfo->mem = NULL; /* for safety if init fails */ + + /* Check for configuration errors. + * SIZEOF(ALIGN_TYPE) should be a power of 2; otherwise, it probably + * doesn't reflect any real hardware alignment requirement. + * The test is a little tricky: for X>0, X and X-1 have no one-bits + * in common if and only if X is a power of 2, ie has only one one-bit. + * Some compilers may give an "unreachable code" warning here; ignore it. + */ + if ((SIZEOF(ALIGN_TYPE) & (SIZEOF(ALIGN_TYPE)-1)) != 0) + ERREXIT(cinfo, JERR_BAD_ALIGN_TYPE); + /* MAX_ALLOC_CHUNK must be representable as type size_t, and must be + * a multiple of SIZEOF(ALIGN_TYPE). + * Again, an "unreachable code" warning may be ignored here. + * But a "constant too large" warning means you need to fix MAX_ALLOC_CHUNK. + */ + test_mac = (size_t) MAX_ALLOC_CHUNK; + if ((long) test_mac != MAX_ALLOC_CHUNK || + (MAX_ALLOC_CHUNK % SIZEOF(ALIGN_TYPE)) != 0) + ERREXIT(cinfo, JERR_BAD_ALLOC_CHUNK); + + max_to_use = jpeg_mem_init(cinfo); /* system-dependent initialization */ + + /* Attempt to allocate memory manager's control block */ + mem = (my_mem_ptr) jpeg_get_small(cinfo, SIZEOF(my_memory_mgr)); + + if (mem == NULL) { + jpeg_mem_term(cinfo); /* system-dependent cleanup */ + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 0); + } + + /* OK, fill in the method pointers */ + mem->pub.alloc_small = alloc_small; + mem->pub.alloc_large = alloc_large; + mem->pub.alloc_sarray = alloc_sarray; + mem->pub.alloc_barray = alloc_barray; + mem->pub.request_virt_sarray = request_virt_sarray; + mem->pub.request_virt_barray = request_virt_barray; + mem->pub.realize_virt_arrays = realize_virt_arrays; + mem->pub.access_virt_sarray = access_virt_sarray; + mem->pub.access_virt_barray = access_virt_barray; + mem->pub.free_pool = free_pool; + mem->pub.self_destruct = self_destruct; + + /* Initialize working state */ + mem->pub.max_memory_to_use = max_to_use; + + for (pool = JPOOL_NUMPOOLS-1; pool >= JPOOL_PERMANENT; pool--) { + mem->small_list[pool] = NULL; + mem->large_list[pool] = NULL; + } + mem->virt_sarray_list = NULL; + mem->virt_barray_list = NULL; + + mem->total_space_allocated = SIZEOF(my_memory_mgr); + + /* Declare ourselves open for business */ + cinfo->mem = & mem->pub; + + /* Check for an environment variable JPEGMEM; if found, override the + * default max_memory setting from jpeg_mem_init. Note that the + * surrounding application may again override this value. + * If your system doesn't support getenv(), define NO_GETENV to disable + * this feature. + */ +#ifndef NO_GETENV + { char * memenv; + + if ((memenv = getenv("JPEGMEM")) != NULL) { + char ch = 'x'; + + if (sscanf(memenv, "%ld%c", &max_to_use, &ch) > 0) { + if (ch == 'm' || ch == 'M') + max_to_use *= 1000L; + mem->pub.max_memory_to_use = max_to_use * 1000L; + } + } + } +#endif + +} diff --git a/utils/Libs/jpeg6/jmemnobs.cpp b/utils/Libs/jpeg6/jmemnobs.cpp new file mode 100644 index 0000000..e63e5e4 --- /dev/null +++ b/utils/Libs/jpeg6/jmemnobs.cpp @@ -0,0 +1,103 @@ +/* + * jmemnobs.c + * + * Copyright (C) 1992-1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file provides a really simple implementation of the system- + * dependent portion of the JPEG memory manager. This implementation + * assumes that no backing-store files are needed: all required space + * can be obtained from ri.Malloc(). + * This is very portable in the sense that it'll compile on almost anything, + * but you'd better have lots of main memory (or virtual memory) if you want + * to process big images. + * Note that the max_memory_to_use option is ignored by this implementation. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jmemsys.h" /* import the system-dependent declarations */ + +/* + * Memory allocation and ri.Freeing are controlled by the regular library + * routines ri.Malloc() and ri.Free(). + */ + +GLOBAL void * +jpeg_get_small (j_common_ptr cinfo, size_t sizeofobject) +{ + return (void *) malloc(sizeofobject); +} + +GLOBAL void +jpeg_free_small (j_common_ptr cinfo, void * object, size_t sizeofobject) +{ + free(object); +} + + +/* + * "Large" objects are treated the same as "small" ones. + * NB: although we include FAR keywords in the routine declarations, + * this file won't actually work in 80x86 small/medium model; at least, + * you probably won't be able to process useful-size images in only 64KB. + */ + +GLOBAL void FAR * +jpeg_get_large (j_common_ptr cinfo, size_t sizeofobject) +{ + return (void FAR *) malloc(sizeofobject); +} + +GLOBAL void +jpeg_free_large (j_common_ptr cinfo, void FAR * object, size_t sizeofobject) +{ + free(object); +} + + +/* + * This routine computes the total memory space available for allocation. + * Here we always say, "we got all you want bud!" + */ + +GLOBAL long +jpeg_mem_available (j_common_ptr cinfo, long min_bytes_needed, + long max_bytes_needed, long already_allocated) +{ + return max_bytes_needed; +} + + +/* + * Backing store (temporary file) management. + * Since jpeg_mem_available always promised the moon, + * this should never be called and we can just error out. + */ + +GLOBAL void +jpeg_open_backing_store (j_common_ptr cinfo, backing_store_ptr info, + long total_bytes_needed) +{ + ERREXIT(cinfo, JERR_NO_BACKING_STORE); +} + + +/* + * These routines take care of any system-dependent initialization and + * cleanup required. Here, there isn't any. + */ + +GLOBAL long +jpeg_mem_init (j_common_ptr cinfo) +{ + return 0; /* just set max_memory_to_use to 0 */ +} + +GLOBAL void +jpeg_mem_term (j_common_ptr cinfo) +{ + /* no work */ +} diff --git a/utils/Libs/jpeg6/jmemsys.h b/utils/Libs/jpeg6/jmemsys.h new file mode 100644 index 0000000..0c7d7c1 --- /dev/null +++ b/utils/Libs/jpeg6/jmemsys.h @@ -0,0 +1,182 @@ +/* + * jmemsys.h + * + * Copyright (C) 1992-1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This include file defines the interface between the system-independent + * and system-dependent portions of the JPEG memory manager. No other + * modules need include it. (The system-independent portion is jmemmgr.c; + * there are several different versions of the system-dependent portion.) + * + * This file works as-is for the system-dependent memory managers supplied + * in the IJG distribution. You may need to modify it if you write a + * custom memory manager. If system-dependent changes are needed in + * this file, the best method is to #ifdef them based on a configuration + * symbol supplied in jconfig.h, as we have done with USE_MSDOS_MEMMGR. + */ + + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_get_small jGetSmall +#define jpeg_free_small jFreeSmall +#define jpeg_get_large jGetLarge +#define jpeg_free_large jFreeLarge +#define jpeg_mem_available jMemAvail +#define jpeg_open_backing_store jOpenBackStore +#define jpeg_mem_init jMemInit +#define jpeg_mem_term jMemTerm +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* + * These two functions are used to allocate and release small chunks of + * memory. (Typically the total amount requested through jpeg_get_small is + * no more than 20K or so; this will be requested in chunks of a few K each.) + * Behavior should be the same as for the standard library functions malloc + * and free; in particular, jpeg_get_small must return NULL on failure. + * On most systems, these ARE malloc and free. jpeg_free_small is passed the + * size of the object being freed, just in case it's needed. + * On an 80x86 machine using small-data memory model, these manage near heap. + */ + +EXTERN void * jpeg_get_small JPP((j_common_ptr cinfo, size_t sizeofobject)); +EXTERN void jpeg_free_small JPP((j_common_ptr cinfo, void * object, + size_t sizeofobject)); + +/* + * These two functions are used to allocate and release large chunks of + * memory (up to the total free space designated by jpeg_mem_available). + * The interface is the same as above, except that on an 80x86 machine, + * far pointers are used. On most other machines these are identical to + * the jpeg_get/free_small routines; but we keep them separate anyway, + * in case a different allocation strategy is desirable for large chunks. + */ + +EXTERN void FAR * jpeg_get_large JPP((j_common_ptr cinfo,size_t sizeofobject)); +EXTERN void jpeg_free_large JPP((j_common_ptr cinfo, void FAR * object, + size_t sizeofobject)); + +/* + * The macro MAX_ALLOC_CHUNK designates the maximum number of bytes that may + * be requested in a single call to jpeg_get_large (and jpeg_get_small for that + * matter, but that case should never come into play). This macro is needed + * to model the 64Kb-segment-size limit of far addressing on 80x86 machines. + * On those machines, we expect that jconfig.h will provide a proper value. + * On machines with 32-bit flat address spaces, any large constant may be used. + * + * NB: jmemmgr.c expects that MAX_ALLOC_CHUNK will be representable as type + * size_t and will be a multiple of sizeof(align_type). + */ + +#ifndef MAX_ALLOC_CHUNK /* may be overridden in jconfig.h */ +#define MAX_ALLOC_CHUNK 1000000000L +#endif + +/* + * This routine computes the total space still available for allocation by + * jpeg_get_large. If more space than this is needed, backing store will be + * used. NOTE: any memory already allocated must not be counted. + * + * There is a minimum space requirement, corresponding to the minimum + * feasible buffer sizes; jmemmgr.c will request that much space even if + * jpeg_mem_available returns zero. The maximum space needed, enough to hold + * all working storage in memory, is also passed in case it is useful. + * Finally, the total space already allocated is passed. If no better + * method is available, cinfo->mem->max_memory_to_use - already_allocated + * is often a suitable calculation. + * + * It is OK for jpeg_mem_available to underestimate the space available + * (that'll just lead to more backing-store access than is really necessary). + * However, an overestimate will lead to failure. Hence it's wise to subtract + * a slop factor from the true available space. 5% should be enough. + * + * On machines with lots of virtual memory, any large constant may be returned. + * Conversely, zero may be returned to always use the minimum amount of memory. + */ + +EXTERN long jpeg_mem_available JPP((j_common_ptr cinfo, + long min_bytes_needed, + long max_bytes_needed, + long already_allocated)); + + +/* + * This structure holds whatever state is needed to access a single + * backing-store object. The read/write/close method pointers are called + * by jmemmgr.c to manipulate the backing-store object; all other fields + * are private to the system-dependent backing store routines. + */ + +#define TEMP_NAME_LENGTH 64 /* max length of a temporary file's name */ + +#ifdef USE_MSDOS_MEMMGR /* DOS-specific junk */ + +typedef unsigned short XMSH; /* type of extended-memory handles */ +typedef unsigned short EMSH; /* type of expanded-memory handles */ + +typedef union { + short file_handle; /* DOS file handle if it's a temp file */ + XMSH xms_handle; /* handle if it's a chunk of XMS */ + EMSH ems_handle; /* handle if it's a chunk of EMS */ +} handle_union; + +#endif /* USE_MSDOS_MEMMGR */ + +typedef struct backing_store_struct * backing_store_ptr; + +typedef struct backing_store_struct { + /* Methods for reading/writing/closing this backing-store object */ + JMETHOD(void, read_backing_store, (j_common_ptr cinfo, + backing_store_ptr info, + void FAR * buffer_address, + long file_offset, long byte_count)); + JMETHOD(void, write_backing_store, (j_common_ptr cinfo, + backing_store_ptr info, + void FAR * buffer_address, + long file_offset, long byte_count)); + JMETHOD(void, close_backing_store, (j_common_ptr cinfo, + backing_store_ptr info)); + + /* Private fields for system-dependent backing-store management */ +#ifdef USE_MSDOS_MEMMGR + /* For the MS-DOS manager (jmemdos.c), we need: */ + handle_union handle; /* reference to backing-store storage object */ + char temp_name[TEMP_NAME_LENGTH]; /* name if it's a file */ +#else + /* For a typical implementation with temp files, we need: */ + FILE * temp_file; /* stdio reference to temp file */ + char temp_name[TEMP_NAME_LENGTH]; /* name of temp file */ +#endif +} backing_store_info; + +/* + * Initial opening of a backing-store object. This must fill in the + * read/write/close pointers in the object. The read/write routines + * may take an error exit if the specified maximum file size is exceeded. + * (If jpeg_mem_available always returns a large value, this routine can + * just take an error exit.) + */ + +EXTERN void jpeg_open_backing_store JPP((j_common_ptr cinfo, + backing_store_ptr info, + long total_bytes_needed)); + + +/* + * These routines take care of any system-dependent initialization and + * cleanup required. jpeg_mem_init will be called before anything is + * allocated (and, therefore, nothing in cinfo is of use except the error + * manager pointer). It should return a suitable default value for + * max_memory_to_use; this may subsequently be overridden by the surrounding + * application. (Note that max_memory_to_use is only important if + * jpeg_mem_available chooses to consult it ... no one else will.) + * jpeg_mem_term may assume that all requested memory has been freed and that + * all opened backing-store objects have been closed. + */ + +EXTERN long jpeg_mem_init JPP((j_common_ptr cinfo)); +EXTERN void jpeg_mem_term JPP((j_common_ptr cinfo)); diff --git a/utils/Libs/jpeg6/jmorecfg.h b/utils/Libs/jpeg6/jmorecfg.h new file mode 100644 index 0000000..d451399 --- /dev/null +++ b/utils/Libs/jpeg6/jmorecfg.h @@ -0,0 +1,346 @@ +/* + * jmorecfg.h + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains additional configuration options that customize the + * JPEG software for special applications or support machine-dependent + * optimizations. Most users will not need to touch this file. + */ + + +/* + * Define BITS_IN_JSAMPLE as either + * 8 for 8-bit sample values (the usual setting) + * 12 for 12-bit sample values + * Only 8 and 12 are legal data precisions for lossy JPEG according to the + * JPEG standard, and the IJG code does not support anything else! + * We do not support run-time selection of data precision, sorry. + */ + +#define BITS_IN_JSAMPLE 8 /* use 8 or 12 */ + + +/* + * Maximum number of components (color channels) allowed in JPEG image. + * To meet the letter of the JPEG spec, set this to 255. However, darn + * few applications need more than 4 channels (maybe 5 for CMYK + alpha + * mask). We recommend 10 as a reasonable compromise; use 4 if you are + * really short on memory. (Each allowed component costs a hundred or so + * bytes of storage, whether actually used in an image or not.) + */ + +#define MAX_COMPONENTS 10 /* maximum number of image components */ + + +/* + * Basic data types. + * You may need to change these if you have a machine with unusual data + * type sizes; for example, "char" not 8 bits, "short" not 16 bits, + * or "long" not 32 bits. We don't care whether "int" is 16 or 32 bits, + * but it had better be at least 16. + */ + +/* Representation of a single sample (pixel element value). + * We frequently allocate large arrays of these, so it's important to keep + * them small. But if you have memory to burn and access to char or short + * arrays is very slow on your hardware, you might want to change these. + */ + +#if BITS_IN_JSAMPLE == 8 +/* JSAMPLE should be the smallest type that will hold the values 0..255. + * You can use a signed char by having GETJSAMPLE mask it with 0xFF. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JSAMPLE; +#ifdef CHAR_IS_UNSIGNED +#define GETJSAMPLE(value) ((int) (value)) +#else +#define GETJSAMPLE(value) ((int) (value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + +#define MAXJSAMPLE 255 +#define CENTERJSAMPLE 128 + +#endif /* BITS_IN_JSAMPLE == 8 */ + + +#if BITS_IN_JSAMPLE == 12 +/* JSAMPLE should be the smallest type that will hold the values 0..4095. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 4095 +#define CENTERJSAMPLE 2048 + +#endif /* BITS_IN_JSAMPLE == 12 */ + + +/* Representation of a DCT frequency coefficient. + * This should be a signed value of at least 16 bits; "short" is usually OK. + * Again, we allocate large arrays of these, but you can change to int + * if you have memory to burn and "short" is really slow. + */ + +typedef short JCOEF; + + +/* Compressed datastreams are represented as arrays of JOCTET. + * These must be EXACTLY 8 bits wide, at least once they are written to + * external storage. Note that when using the stdio data source/destination + * managers, this is also the data type passed to fread/fwrite. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JOCTET; +#define GETJOCTET(value) (value) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JOCTET; +#ifdef CHAR_IS_UNSIGNED +#define GETJOCTET(value) (value) +#else +#define GETJOCTET(value) ((value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + + +/* These typedefs are used for various table entries and so forth. + * They must be at least as wide as specified; but making them too big + * won't cost a huge amount of memory, so we don't provide special + * extraction code like we did for JSAMPLE. (In other words, these + * typedefs live at a different point on the speed/space tradeoff curve.) + */ + +/* UINT8 must hold at least the values 0..255. */ + +#ifdef HAVE_UNSIGNED_CHAR +typedef unsigned char UINT8; +#else /* not HAVE_UNSIGNED_CHAR */ +#ifdef CHAR_IS_UNSIGNED +typedef char UINT8; +#else /* not CHAR_IS_UNSIGNED */ +typedef short UINT8; +#endif /* CHAR_IS_UNSIGNED */ +#endif /* HAVE_UNSIGNED_CHAR */ + +/* UINT16 must hold at least the values 0..65535. */ + +#ifdef HAVE_UNSIGNED_SHORT +typedef unsigned short UINT16; +#else /* not HAVE_UNSIGNED_SHORT */ +typedef unsigned int UINT16; +#endif /* HAVE_UNSIGNED_SHORT */ + +/* INT16 must hold at least the values -32768..32767. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT16 */ +typedef short INT16; +#endif + +/* INT32 must hold at least signed 32-bit values. */ + +//#ifndef XMD_H /* X11/xmd.h correctly defines INT32 */ +//typedef long INT32; +//#endif + +/* Datatype used for image dimensions. The JPEG standard only supports + * images up to 64K*64K due to 16-bit fields in SOF markers. Therefore + * "unsigned int" is sufficient on all machines. However, if you need to + * handle larger images and you don't mind deviating from the spec, you + * can change this datatype. + */ + +typedef unsigned int JDIMENSION; + +#define JPEG_MAX_DIMENSION 65500L /* a tad under 64K to prevent overflows */ + + +/* These defines are used in all function definitions and extern declarations. + * You could modify them if you need to change function linkage conventions. + * Another application is to make all functions global for use with debuggers + * or code profilers that require it. + */ + +#define METHODDEF static /* a function called through method pointers */ +#define LOCAL static /* a function used only in its module */ +#define GLOBAL /* a function referenced thru EXTERNs */ +#define EXTERN extern /* a reference to a GLOBAL function */ + + +/* Here is the pseudo-keyword for declaring pointers that must be "far" + * on 80x86 machines. Most of the specialized coding for 80x86 is handled + * by just saying "FAR *" where such a pointer is needed. In a few places + * explicit coding is needed; see uses of the NEED_FAR_POINTERS symbol. + */ + +#ifdef NEED_FAR_POINTERS +#undef FAR +#define FAR far +#else +#undef FAR +#define FAR +#endif + + +/* + * On a few systems, type boolean and/or its values FALSE, TRUE may appear + * in standard header files. Or you may have conflicts with application- + * specific header files that you want to include together with these files. + * Defining HAVE_BOOLEAN before including jpeglib.h should make it work. + */ + +//#ifndef HAVE_BOOLEAN +//typedef int boolean; +//#endif +#ifndef FALSE /* in case these macros already exist */ +#define FALSE 0 /* values of boolean */ +#endif +#ifndef TRUE +#define TRUE 1 +#endif + + +/* + * The remaining options affect code selection within the JPEG library, + * but they don't need to be visible to most applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS or JPEG_INTERNAL_OPTIONS has been defined. + */ + +#ifdef JPEG_INTERNALS +#define JPEG_INTERNAL_OPTIONS +#endif + +#ifdef JPEG_INTERNAL_OPTIONS + + +/* + * These defines indicate whether to include various optional functions. + * Undefining some of these symbols will produce a smaller but less capable + * library. Note that you can leave certain source files out of the + * compilation/linking process if you've #undef'd the corresponding symbols. + * (You may HAVE to do that if your compiler doesn't like null source files.) + */ + +/* Arithmetic coding is unsupported for legal reasons. Complaints to IBM. */ + +/* Capability options common to encoder and decoder: */ + +#undef DCT_ISLOW_SUPPORTED /* slow but accurate integer algorithm */ +#undef DCT_IFAST_SUPPORTED /* faster, less accurate integer method */ +#define DCT_FLOAT_SUPPORTED /* floating-point: accurate, fast on fast HW */ + +/* Encoder capability options: */ + +#undef C_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define C_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define C_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define ENTROPY_OPT_SUPPORTED /* Optimization of entropy coding parms? */ +/* Note: if you selected 12-bit data precision, it is dangerous to turn off + * ENTROPY_OPT_SUPPORTED. The standard Huffman tables are only good for 8-bit + * precision, so jchuff.c normally uses entropy optimization to compute + * usable tables for higher precision. If you don't want to do optimization, + * you'll have to supply different default Huffman tables. + * The exact same statements apply for progressive JPEG: the default tables + * don't work for progressive mode. (This may get fixed, however.) + */ +#define INPUT_SMOOTHING_SUPPORTED /* Input image smoothing option? */ + +/* Decoder capability options: */ + +#undef D_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#undef D_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#undef D_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#undef BLOCK_SMOOTHING_SUPPORTED /* Block smoothing? (Progressive only) */ +#undef IDCT_SCALING_SUPPORTED /* Output rescaling via IDCT? */ +#undef UPSAMPLE_SCALING_SUPPORTED /* Output rescaling at upsample stage? */ +#undef UPSAMPLE_MERGING_SUPPORTED /* Fast path for sloppy upsampling? */ +#undef QUANT_1PASS_SUPPORTED /* 1-pass color quantization? */ +#undef QUANT_2PASS_SUPPORTED /* 2-pass color quantization? */ + +/* more capability options later, no doubt */ + + +/* + * Ordering of RGB data in scanlines passed to or from the application. + * If your application wants to deal with data in the order B,G,R, just + * change these macros. You can also deal with formats such as R,G,B,X + * (one extra byte per pixel) by changing RGB_PIXELSIZE. Note that changing + * the offsets will also change the order in which colormap data is organized. + * RESTRICTIONS: + * 1. The sample applications cjpeg,djpeg do NOT support modified RGB formats. + * 2. These macros only affect RGB<=>YCbCr color conversion, so they are not + * useful if you are using JPEG color spaces other than YCbCr or grayscale. + * 3. The color quantizer modules will not behave desirably if RGB_PIXELSIZE + * is not 3 (they don't understand about dummy color components!). So you + * can't use color quantization if you change that value. + */ + +#define RGB_RED 0 /* Offset of Red in an RGB scanline element */ +#define RGB_GREEN 1 /* Offset of Green */ +#define RGB_BLUE 2 /* Offset of Blue */ +#define RGB_PIXELSIZE 4 /* JSAMPLEs per RGB scanline element */ + + +/* Definitions for speed-related optimizations. */ + + +/* If your compiler supports inline functions, define INLINE + * as the inline keyword; otherwise define it as empty. + */ + +#ifndef INLINE +#ifdef __GNUC__ /* for instance, GNU C knows about inline */ +#define INLINE __inline__ +#endif +#ifndef INLINE +#define INLINE /* default is to define it as empty */ +#endif +#endif + + +/* On some machines (notably 68000 series) "int" is 32 bits, but multiplying + * two 16-bit shorts is faster than multiplying two ints. Define MULTIPLIER + * as short on such a machine. MULTIPLIER must be at least 16 bits wide. + */ + +#ifndef MULTIPLIER +#define MULTIPLIER int /* type for fastest integer multiply */ +#endif + + +/* FAST_FLOAT should be either float or double, whichever is done faster + * by your compiler. (Note that this type is only used in the floating point + * DCT routines, so it only matters if you've defined DCT_FLOAT_SUPPORTED.) + * Typically, float is faster in ANSI C compilers, while double is faster in + * pre-ANSI compilers (because they insist on converting to double anyway). + * The code below therefore chooses float if we have ANSI-style prototypes. + */ + +#ifndef FAST_FLOAT +#ifdef HAVE_PROTOTYPES +#define FAST_FLOAT float +#else +#define FAST_FLOAT double +#endif +#endif + +#endif /* JPEG_INTERNAL_OPTIONS */ diff --git a/utils/Libs/jpeg6/jpeg6.dsp b/utils/Libs/jpeg6/jpeg6.dsp new file mode 100644 index 0000000..5c7cf47 --- /dev/null +++ b/utils/Libs/jpeg6/jpeg6.dsp @@ -0,0 +1,224 @@ +# Microsoft Developer Studio Project File - Name="jpeg6" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=jpeg6 - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "jpeg6.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "jpeg6.mak" CFG="jpeg6 - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "jpeg6 - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "jpeg6 - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName ""$/Utils/Libs/jpeg6", DINAAAAA" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "jpeg6 - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GR /GX /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"..\jpeg6.lib" + +!ELSEIF "$(CFG)" == "jpeg6 - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GR /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"..\jpeg6d.lib" + +!ENDIF + +# Begin Target + +# Name "jpeg6 - Win32 Release" +# Name "jpeg6 - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\jcomapi.cpp +# End Source File +# Begin Source File + +SOURCE=.\jdapimin.cpp +# End Source File +# Begin Source File + +SOURCE=.\jdapistd.cpp +# End Source File +# Begin Source File + +SOURCE=.\jdatasrc.cpp +# End Source File +# Begin Source File + +SOURCE=.\jdcoefct.cpp +# End Source File +# Begin Source File + +SOURCE=.\jdcolor.cpp +# End Source File +# Begin Source File + +SOURCE=.\jddctmgr.cpp +# End Source File +# Begin Source File + +SOURCE=.\jdhuff.cpp +# End Source File +# Begin Source File + +SOURCE=.\jdinput.cpp +# End Source File +# Begin Source File + +SOURCE=.\jdmainct.cpp +# End Source File +# Begin Source File + +SOURCE=.\jdmarker.cpp +# End Source File +# Begin Source File + +SOURCE=.\jdmaster.cpp +# End Source File +# Begin Source File + +SOURCE=.\jdpostct.cpp +# End Source File +# Begin Source File + +SOURCE=.\jdsample.cpp +# End Source File +# Begin Source File + +SOURCE=.\jdtrans.cpp +# End Source File +# Begin Source File + +SOURCE=.\jerror.cpp +# End Source File +# Begin Source File + +SOURCE=.\jfdctflt.cpp +# End Source File +# Begin Source File + +SOURCE=.\jidctflt.cpp +# End Source File +# Begin Source File + +SOURCE=.\jmemmgr.cpp +# End Source File +# Begin Source File + +SOURCE=.\jmemnobs.cpp +# End Source File +# Begin Source File + +SOURCE=.\jpgload.cpp +# End Source File +# Begin Source File + +SOURCE=.\jutils.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\jchuff.h +# End Source File +# Begin Source File + +SOURCE=.\jconfig.h +# End Source File +# Begin Source File + +SOURCE=.\jdct.h +# End Source File +# Begin Source File + +SOURCE=.\jdhuff.h +# End Source File +# Begin Source File + +SOURCE=.\jerror.h +# End Source File +# Begin Source File + +SOURCE=.\jinclude.h +# End Source File +# Begin Source File + +SOURCE=.\jmemsys.h +# End Source File +# Begin Source File + +SOURCE=.\jmorecfg.h +# End Source File +# Begin Source File + +SOURCE=.\jpegint.h +# End Source File +# Begin Source File + +SOURCE=..\jpeglib.h +# End Source File +# Begin Source File + +SOURCE=.\jversion.h +# End Source File +# End Group +# End Target +# End Project diff --git a/utils/Libs/jpeg6/jpeg6.dsw b/utils/Libs/jpeg6/jpeg6.dsw new file mode 100644 index 0000000..19cf6c8 --- /dev/null +++ b/utils/Libs/jpeg6/jpeg6.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "jpeg6"=.\jpeg6.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/utils/Libs/jpeg6/jpegint.h b/utils/Libs/jpeg6/jpegint.h new file mode 100644 index 0000000..b3b6a6d --- /dev/null +++ b/utils/Libs/jpeg6/jpegint.h @@ -0,0 +1,388 @@ +/* + * jpegint.h + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file provides common declarations for the various JPEG modules. + * These declarations are considered internal to the JPEG library; most + * applications using the library shouldn't need to include this file. + */ + + +/* Declarations for both compression & decompression */ + +typedef enum { /* Operating modes for buffer controllers */ + JBUF_PASS_THRU, /* Plain stripwise operation */ + /* Remaining modes require a full-image buffer to have been created */ + JBUF_SAVE_SOURCE, /* Run source subobject only, save output */ + JBUF_CRANK_DEST, /* Run dest subobject only, using saved data */ + JBUF_SAVE_AND_PASS /* Run both subobjects, save output */ +} J_BUF_MODE; + +/* Values of global_state field (jdapi.c has some dependencies on ordering!) */ +#define CSTATE_START 100 /* after create_compress */ +#define CSTATE_SCANNING 101 /* start_compress done, write_scanlines OK */ +#define CSTATE_RAW_OK 102 /* start_compress done, write_raw_data OK */ +#define CSTATE_WRCOEFS 103 /* jpeg_write_coefficients done */ +#define DSTATE_START 200 /* after create_decompress */ +#define DSTATE_INHEADER 201 /* reading header markers, no SOS yet */ +#define DSTATE_READY 202 /* found SOS, ready for start_decompress */ +#define DSTATE_PRELOAD 203 /* reading multiscan file in start_decompress*/ +#define DSTATE_PRESCAN 204 /* performing dummy pass for 2-pass quant */ +#define DSTATE_SCANNING 205 /* start_decompress done, read_scanlines OK */ +#define DSTATE_RAW_OK 206 /* start_decompress done, read_raw_data OK */ +#define DSTATE_BUFIMAGE 207 /* expecting jpeg_start_output */ +#define DSTATE_BUFPOST 208 /* looking for SOS/EOI in jpeg_finish_output */ +#define DSTATE_RDCOEFS 209 /* reading file in jpeg_read_coefficients */ +#define DSTATE_STOPPING 210 /* looking for EOI in jpeg_finish_decompress */ + + +/* Declarations for compression modules */ + +/* Master control module */ +struct jpeg_comp_master { + JMETHOD(void, prepare_for_pass, (j_compress_ptr cinfo)); + JMETHOD(void, pass_startup, (j_compress_ptr cinfo)); + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean call_pass_startup; /* True if pass_startup must be called */ + boolean is_last_pass; /* True during last pass */ +}; + +/* Main buffer control (downsampled-data buffer) */ +struct jpeg_c_main_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, process_data, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail)); +}; + +/* Compression preprocessing (downsampling input buffer control) */ +struct jpeg_c_prep_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, pre_process_data, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, + JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail, + JSAMPIMAGE output_buf, + JDIMENSION *out_row_group_ctr, + JDIMENSION out_row_groups_avail)); +}; + +/* Coefficient buffer control */ +struct jpeg_c_coef_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(boolean, compress_data, (j_compress_ptr cinfo, + JSAMPIMAGE input_buf)); +}; + +/* Colorspace conversion */ +struct jpeg_color_converter { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + JMETHOD(void, color_convert, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +}; + +/* Downsampling */ +struct jpeg_downsampler { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + JMETHOD(void, downsample, (j_compress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_index, + JSAMPIMAGE output_buf, + JDIMENSION out_row_group_index)); + + boolean need_context_rows; /* TRUE if need rows above & below */ +}; + +/* Forward DCT (also controls coefficient quantization) */ +struct jpeg_forward_dct { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + /* perhaps this should be an array??? */ + JMETHOD(void, forward_DCT, (j_compress_ptr cinfo, + jpeg_component_info * compptr, + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + JDIMENSION start_row, JDIMENSION start_col, + JDIMENSION num_blocks)); +}; + +/* Entropy encoding */ +struct jpeg_entropy_encoder { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, boolean gather_statistics)); + JMETHOD(boolean, encode_mcu, (j_compress_ptr cinfo, JBLOCKROW *MCU_data)); + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); +}; + +/* Marker writing */ +struct jpeg_marker_writer { + /* write_any_marker is exported for use by applications */ + /* Probably only COM and APPn markers should be written */ + JMETHOD(void, write_any_marker, (j_compress_ptr cinfo, int marker, + const JOCTET *dataptr, unsigned int datalen)); + JMETHOD(void, write_file_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_frame_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_scan_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_file_trailer, (j_compress_ptr cinfo)); + JMETHOD(void, write_tables_only, (j_compress_ptr cinfo)); +}; + + +/* Declarations for decompression modules */ + +/* Master control module */ +struct jpeg_decomp_master { + JMETHOD(void, prepare_for_output_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, finish_output_pass, (j_decompress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean is_dummy_pass; /* True during 1st pass for 2-pass quant */ +}; + +/* Input control module */ +struct jpeg_input_controller { + JMETHOD(int, consume_input, (j_decompress_ptr cinfo)); + JMETHOD(void, reset_input_controller, (j_decompress_ptr cinfo)); + JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, finish_input_pass, (j_decompress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean has_multiple_scans; /* True if file has multiple scans */ + boolean eoi_reached; /* True when EOI has been consumed */ +}; + +/* Main buffer control (downsampled-data buffer) */ +struct jpeg_d_main_controller { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, process_data, (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +}; + +/* Coefficient buffer control */ +struct jpeg_d_coef_controller { + JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); + JMETHOD(int, consume_data, (j_decompress_ptr cinfo)); + JMETHOD(void, start_output_pass, (j_decompress_ptr cinfo)); + JMETHOD(int, decompress_data, (j_decompress_ptr cinfo, + JSAMPIMAGE output_buf)); + /* Pointer to array of coefficient virtual arrays, or NULL if none */ + jvirt_barray_ptr *coef_arrays; +}; + +/* Decompression postprocessing (color quantization buffer control) */ +struct jpeg_d_post_controller { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, post_process_data, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +}; + +/* Marker reading & parsing */ +struct jpeg_marker_reader { + JMETHOD(void, reset_marker_reader, (j_decompress_ptr cinfo)); + /* Read markers until SOS or EOI. + * Returns same codes as are defined for jpeg_consume_input: + * JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + */ + JMETHOD(int, read_markers, (j_decompress_ptr cinfo)); + /* Read a restart marker --- exported for use by entropy decoder only */ + jpeg_marker_parser_method read_restart_marker; + /* Application-overridable marker processing methods */ + jpeg_marker_parser_method process_COM; + jpeg_marker_parser_method process_APPn[16]; + + /* State of marker reader --- nominally internal, but applications + * supplying COM or APPn handlers might like to know the state. + */ + boolean saw_SOI; /* found SOI? */ + boolean saw_SOF; /* found SOF? */ + int next_restart_num; /* next restart number expected (0-7) */ + unsigned int discarded_bytes; /* # of bytes skipped looking for a marker */ +}; + +/* Entropy decoding */ +struct jpeg_entropy_decoder { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(boolean, decode_mcu, (j_decompress_ptr cinfo, + JBLOCKROW *MCU_data)); +}; + +/* Inverse DCT (also performs dequantization) */ +typedef JMETHOD(void, inverse_DCT_method_ptr, + (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col)); + +struct jpeg_inverse_dct { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + /* It is useful to allow each component to have a separate IDCT method. */ + inverse_DCT_method_ptr inverse_DCT[MAX_COMPONENTS]; +}; + +/* Upsampling (note that upsampler must also call color converter) */ +struct jpeg_upsampler { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, upsample, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); + + boolean need_context_rows; /* TRUE if need rows above & below */ +}; + +/* Colorspace conversion */ +struct jpeg_color_deconverter { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, color_convert, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows)); +}; + +/* Color quantization or color precision reduction */ +struct jpeg_color_quantizer { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, boolean is_pre_scan)); + JMETHOD(void, color_quantize, (j_decompress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPARRAY output_buf, + int num_rows)); + JMETHOD(void, finish_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, new_color_map, (j_decompress_ptr cinfo)); +}; + + +/* Miscellaneous useful macros */ + +#undef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#undef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + + +/* We assume that right shift corresponds to signed division by 2 with + * rounding towards minus infinity. This is correct for typical "arithmetic + * shift" instructions that shift in copies of the sign bit. But some + * C compilers implement >> with an unsigned shift. For these machines you + * must define RIGHT_SHIFT_IS_UNSIGNED. + * RIGHT_SHIFT provides a proper signed right shift of an INT32 quantity. + * It is only applied with constant shift counts. SHIFT_TEMPS must be + * included in the variables of any routine using RIGHT_SHIFT. + */ + +#ifdef RIGHT_SHIFT_IS_UNSIGNED +#define SHIFT_TEMPS INT32 shift_temp; +#define RIGHT_SHIFT(x,shft) \ + ((shift_temp = (x)) < 0 ? \ + (shift_temp >> (shft)) | ((~((INT32) 0)) << (32-(shft))) : \ + (shift_temp >> (shft))) +#else +#define SHIFT_TEMPS +#define RIGHT_SHIFT(x,shft) ((x) >> (shft)) +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jinit_compress_master jICompress +#define jinit_c_master_control jICMaster +#define jinit_c_main_controller jICMainC +#define jinit_c_prep_controller jICPrepC +#define jinit_c_coef_controller jICCoefC +#define jinit_color_converter jICColor +#define jinit_downsampler jIDownsampler +#define jinit_forward_dct jIFDCT +#define jinit_huff_encoder jIHEncoder +#define jinit_phuff_encoder jIPHEncoder +#define jinit_marker_writer jIMWriter +#define jinit_master_decompress jIDMaster +#define jinit_d_main_controller jIDMainC +#define jinit_d_coef_controller jIDCoefC +#define jinit_d_post_controller jIDPostC +#define jinit_input_controller jIInCtlr +#define jinit_marker_reader jIMReader +#define jinit_huff_decoder jIHDecoder +#define jinit_phuff_decoder jIPHDecoder +#define jinit_inverse_dct jIIDCT +#define jinit_upsampler jIUpsampler +#define jinit_color_deconverter jIDColor +#define jinit_1pass_quantizer jI1Quant +#define jinit_2pass_quantizer jI2Quant +#define jinit_merged_upsampler jIMUpsampler +#define jinit_memory_mgr jIMemMgr +#define jdiv_round_up jDivRound +#define jround_up jRound +#define jcopy_sample_rows jCopySamples +#define jcopy_block_row jCopyBlocks +#define jzero_far jZeroFar +#define jpeg_zigzag_order jZIGTable +#define jpeg_natural_order jZAGTable +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Compression module initialization routines */ +EXTERN void jinit_compress_master JPP((j_compress_ptr cinfo)); +EXTERN void jinit_c_master_control JPP((j_compress_ptr cinfo, + boolean transcode_only)); +EXTERN void jinit_c_main_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN void jinit_c_prep_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN void jinit_c_coef_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN void jinit_color_converter JPP((j_compress_ptr cinfo)); +EXTERN void jinit_downsampler JPP((j_compress_ptr cinfo)); +EXTERN void jinit_forward_dct JPP((j_compress_ptr cinfo)); +EXTERN void jinit_huff_encoder JPP((j_compress_ptr cinfo)); +EXTERN void jinit_phuff_encoder JPP((j_compress_ptr cinfo)); +EXTERN void jinit_marker_writer JPP((j_compress_ptr cinfo)); +/* Decompression module initialization routines */ +EXTERN void jinit_master_decompress JPP((j_decompress_ptr cinfo)); +EXTERN void jinit_d_main_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN void jinit_d_coef_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN void jinit_d_post_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN void jinit_input_controller JPP((j_decompress_ptr cinfo)); +EXTERN void jinit_marker_reader JPP((j_decompress_ptr cinfo)); +EXTERN void jinit_huff_decoder JPP((j_decompress_ptr cinfo)); +EXTERN void jinit_phuff_decoder JPP((j_decompress_ptr cinfo)); +EXTERN void jinit_inverse_dct JPP((j_decompress_ptr cinfo)); +EXTERN void jinit_upsampler JPP((j_decompress_ptr cinfo)); +EXTERN void jinit_color_deconverter JPP((j_decompress_ptr cinfo)); +EXTERN void jinit_1pass_quantizer JPP((j_decompress_ptr cinfo)); +EXTERN void jinit_2pass_quantizer JPP((j_decompress_ptr cinfo)); +EXTERN void jinit_merged_upsampler JPP((j_decompress_ptr cinfo)); +/* Memory manager initialization */ +EXTERN void jinit_memory_mgr JPP((j_common_ptr cinfo)); + +/* Utility routines in jutils.c */ +EXTERN long jdiv_round_up JPP((long a, long b)); +EXTERN long jround_up JPP((long a, long b)); +EXTERN void jcopy_sample_rows JPP((JSAMPARRAY input_array, int source_row, + JSAMPARRAY output_array, int dest_row, + int num_rows, JDIMENSION num_cols)); +EXTERN void jcopy_block_row JPP((JBLOCKROW input_row, JBLOCKROW output_row, + JDIMENSION num_blocks)); +EXTERN void jzero_far JPP((void FAR * target, size_t bytestozero)); +/* Constant tables in jutils.c */ +extern const int jpeg_zigzag_order[]; /* natural coef order to zigzag order */ +extern const int jpeg_natural_order[]; /* zigzag coef order to natural order */ + +/* Suppress undefined-structure complaints if necessary. */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef AM_MEMORY_MANAGER /* only jmemmgr.c defines these */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +#endif +#endif /* INCOMPLETE_TYPES_BROKEN */ diff --git a/utils/Libs/jpeg6/jpgload.cpp b/utils/Libs/jpeg6/jpgload.cpp new file mode 100644 index 0000000..1006b99 --- /dev/null +++ b/utils/Libs/jpeg6/jpgload.cpp @@ -0,0 +1,142 @@ + + +#include "jpeglib.h" +#include + +GLOBAL void LoadJPGBuff(unsigned char *fbuffer, unsigned char **pic, int *width, int *height ) +{ + /* This struct contains the JPEG decompression parameters and pointers to + * working space (which is allocated as needed by the JPEG library). + */ + struct jpeg_decompress_struct cinfo; + /* We use our private extension JPEG error handler. + * Note that this struct must live as long as the main JPEG parameter + * struct, to avoid dangling-pointer problems. + */ + /* This struct represents a JPEG error handler. It is declared separately + * because applications often want to supply a specialized error handler + * (see the second half of this file for an example). But here we just + * take the easy way out and use the standard error handler, which will + * print a message on stderr and call exit() if compression fails. + * Note that this struct must live as long as the main JPEG parameter + * struct, to avoid dangling-pointer problems. + */ + + struct jpeg_error_mgr jerr; + /* More stuff */ + JSAMPARRAY buffer; /* Output row buffer */ + int row_stride; /* physical row width in output buffer */ + unsigned char *out; + byte *bbuf; + int nSize; + + /* Step 1: allocate and initialize JPEG decompression object */ + + /* We have to set up the error handler first, in case the initialization + * step fails. (Unlikely, but it could happen if you are out of memory.) + * This routine fills in the contents of struct jerr, and returns jerr's + * address which we place into the link field in cinfo. + */ + cinfo.err = jpeg_std_error(&jerr); + + /* Now we can initialize the JPEG decompression object. */ + jpeg_create_decompress(&cinfo); + + /* Step 2: specify data source (eg, a file) */ + + jpeg_stdio_src(&cinfo, fbuffer); + + /* Step 3: read file parameters with jpeg_read_header() */ + + (void) jpeg_read_header(&cinfo, TRUE); + /* We can ignore the return value from jpeg_read_header since + * (a) suspension is not possible with the stdio data source, and + * (b) we passed TRUE to reject a tables-only JPEG file as an error. + * See libjpeg.doc for more info. + */ + + /* Step 4: set parameters for decompression */ + + /* In this example, we don't need to change any of the defaults set by + * jpeg_read_header(), so we do nothing here. + */ + + /* Step 5: Start decompressor */ + + (void) jpeg_start_decompress(&cinfo); + /* We can ignore the return value since suspension is not possible + * with the stdio data source. + */ + + /* We may need to do some setup of our own at this point before reading + * the data. After jpeg_start_decompress() we have the correct scaled + * output image dimensions available, as well as the output colormap + * if we asked for color quantization. + * In this example, we need to make an output work buffer of the right size. + */ + /* JSAMPLEs per row in output buffer */ + row_stride = cinfo.output_width * cinfo.output_components; + + nSize = cinfo.output_width*cinfo.output_height*cinfo.output_components; + out = reinterpret_cast(malloc(nSize+1)); + memset(out, 0, nSize+1); + + *pic = out; + *width = cinfo.output_width; + *height = cinfo.output_height; + + /* Step 6: while (scan lines remain to be read) */ + /* jpeg_read_scanlines(...); */ + + /* Here we use the library's state variable cinfo.output_scanline as the + * loop counter, so that we don't have to keep track ourselves. + */ + while (cinfo.output_scanline < cinfo.output_height) { + /* jpeg_read_scanlines expects an array of pointers to scanlines. + * Here the array is only one element long, but you could ask for + * more than one scanline at a time if that's more convenient. + */ + bbuf = ((out+(row_stride*cinfo.output_scanline))); + buffer = &bbuf; + (void) jpeg_read_scanlines(&cinfo, buffer, 1); + } + + // clear all the alphas to 255 + { + int i, j; + byte *buf; + + buf = *pic; + + j = cinfo.output_width * cinfo.output_height * 4; + for ( i = 3 ; i < j ; i+=4 ) { + buf[i] = 255; + } + } + + /* Step 7: Finish decompression */ + + (void) jpeg_finish_decompress(&cinfo); + /* We can ignore the return value since suspension is not possible + * with the stdio data source. + */ + + /* Step 8: Release JPEG decompression object */ + + /* This is an important step since it will release a good deal of memory. */ + jpeg_destroy_decompress(&cinfo); + + /* After finish_decompress, we can close the input file. + * Here we postpone it until after no more JPEG errors are possible, + * so as to simplify the setjmp error logic above. (Actually, I don't + * think that jpeg_destroy can do an error exit, but why assume anything...) + */ + //free (fbuffer); + + /* At this point you may want to check to see whether any corrupt-data + * warnings occurred (test whether jerr.pub.num_warnings is nonzero). + */ + + /* And we're done! */ +} + diff --git a/utils/Libs/jpeg6/jutils.cpp b/utils/Libs/jpeg6/jutils.cpp new file mode 100644 index 0000000..8e8dc13 --- /dev/null +++ b/utils/Libs/jpeg6/jutils.cpp @@ -0,0 +1,175 @@ +/* + * jutils.c + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains tables and miscellaneous utility routines needed + * for both compression and decompression. + * Note we prefix all global names with "j" to minimize conflicts with + * a surrounding application. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * jpeg_zigzag_order[i] is the zigzag-order position of the i'th element + * of a DCT block read in natural order (left to right, top to bottom). + */ + +const int jpeg_zigzag_order[DCTSIZE2] = { + 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63 +}; + +/* + * jpeg_natural_order[i] is the natural-order position of the i'th element + * of zigzag order. + * + * When reading corrupted data, the Huffman decoders could attempt + * to reference an entry beyond the end of this array (if the decoded + * zero run length reaches past the end of the block). To prevent + * wild stores without adding an inner-loop test, we put some extra + * "63"s after the real entries. This will cause the extra coefficient + * to be stored in location 63 of the block, not somewhere random. + * The worst case would be a run-length of 15, which means we need 16 + * fake entries. + */ + +const int jpeg_natural_order[DCTSIZE2+16] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + 63, 63, 63, 63, 63, 63, 63, 63, /* extra entries for safety in decoder */ + 63, 63, 63, 63, 63, 63, 63, 63 +}; + + +/* + * Arithmetic utilities + */ + +GLOBAL long +jdiv_round_up (long a, long b) +/* Compute a/b rounded up to next integer, ie, ceil(a/b) */ +/* Assumes a >= 0, b > 0 */ +{ + return (a + b - 1L) / b; +} + + +GLOBAL long +jround_up (long a, long b) +/* Compute a rounded up to next multiple of b, ie, ceil(a/b)*b */ +/* Assumes a >= 0, b > 0 */ +{ + a += b - 1L; + return a - (a % b); +} + + +/* On normal machines we can apply MEMCOPY() and MEMZERO() to sample arrays + * and coefficient-block arrays. This won't work on 80x86 because the arrays + * are FAR and we're assuming a small-pointer memory model. However, some + * DOS compilers provide far-pointer versions of memcpy() and memset() even + * in the small-model libraries. These will be used if USE_FMEM is defined. + * Otherwise, the routines below do it the hard way. (The performance cost + * is not all that great, because these routines aren't very heavily used.) + */ + +#ifndef NEED_FAR_POINTERS /* normal case, same as regular macros */ +#define FMEMCOPY(dest,src,size) MEMCOPY(dest,src,size) +#define FMEMZERO(target,size) MEMZERO(target,size) +#else /* 80x86 case, define if we can */ +#ifdef USE_FMEM +#define FMEMCOPY(dest,src,size) _fmemcpy((void FAR *)(dest), (const void FAR *)(src), (size_t)(size)) +#define FMEMZERO(target,size) _fmemset((void FAR *)(target), 0, (size_t)(size)) +#endif +#endif + + +GLOBAL void +jcopy_sample_rows (JSAMPARRAY input_array, int source_row, + JSAMPARRAY output_array, int dest_row, + int num_rows, JDIMENSION num_cols) +/* Copy some rows of samples from one place to another. + * num_rows rows are copied from input_array[source_row++] + * to output_array[dest_row++]; these areas may overlap for duplication. + * The source and destination arrays must be at least as wide as num_cols. + */ +{ + register JSAMPROW inptr, outptr; +#ifdef FMEMCOPY + register size_t count = (size_t) (num_cols * SIZEOF(JSAMPLE)); +#else + register JDIMENSION count; +#endif + register int row; + + input_array += source_row; + output_array += dest_row; + + for (row = num_rows; row > 0; row--) { + inptr = *input_array++; + outptr = *output_array++; +#ifdef FMEMCOPY + FMEMCOPY(outptr, inptr, count); +#else + for (count = num_cols; count > 0; count--) + *outptr++ = *inptr++; /* needn't bother with GETJSAMPLE() here */ +#endif + } +} + + +GLOBAL void +jcopy_block_row (JBLOCKROW input_row, JBLOCKROW output_row, + JDIMENSION num_blocks) +/* Copy a row of coefficient blocks from one place to another. */ +{ +#ifdef FMEMCOPY + FMEMCOPY(output_row, input_row, num_blocks * (DCTSIZE2 * SIZEOF(JCOEF))); +#else + register JCOEFPTR inptr, outptr; + register long count; + + inptr = (JCOEFPTR) input_row; + outptr = (JCOEFPTR) output_row; + for (count = (long) num_blocks * DCTSIZE2; count > 0; count--) { + *outptr++ = *inptr++; + } +#endif +} + + +GLOBAL void +jzero_far (void FAR * target, size_t bytestozero) +/* Zero out a chunk of FAR memory. */ +/* This might be sample-array data, block-array data, or alloc_large data. */ +{ +#ifdef FMEMZERO + FMEMZERO(target, bytestozero); +#else + register char FAR * ptr = (char FAR *) target; + register size_t count; + + for (count = bytestozero; count > 0; count--) { + *ptr++ = 0; + } +#endif +} diff --git a/utils/Libs/jpeg6/jversion.h b/utils/Libs/jpeg6/jversion.h new file mode 100644 index 0000000..02083ac --- /dev/null +++ b/utils/Libs/jpeg6/jversion.h @@ -0,0 +1,14 @@ +/* + * jversion.h + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains software version identification. + */ + + +#define JVERSION "6 2-Aug-95" + +#define JCOPYRIGHT "Copyright (C) 1995, Thomas G. Lane" diff --git a/utils/Libs/jpeglib.h b/utils/Libs/jpeglib.h new file mode 100644 index 0000000..81ca1b4 --- /dev/null +++ b/utils/Libs/jpeglib.h @@ -0,0 +1,1087 @@ +/* + * jpeglib.h + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the application interface for the JPEG library. + * Most applications using the library need only include this file, + * and perhaps jerror.h if they want to know the exact error codes. + */ + +#ifndef JPEGLIB_H +#define JPEGLIB_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + + +#ifdef __MACOS__ + +// JDC: stuff to make mac version compile +#define boolean qboolean +#define register +#define INT32 int + +#endif + +// rad additions +// 11.29.99 + +//#include "cmdlib.h" +#ifdef _WIN32 +#include "windows.h" +#include "stdio.h" +#endif + +#ifndef INT32 +#define INT32 int +#endif + +extern void LoadJPGBuff(unsigned char *fbuffer, unsigned char **pic, int *width, int *height ); +// rad end + + +/* + * First we include the configuration files that record how this + * installation of the JPEG library is set up. jconfig.h can be + * generated automatically for many systems. jmorecfg.h contains + * manual configuration options that most people need not worry about. + */ + +#ifndef JCONFIG_INCLUDED /* in case jinclude.h already did */ +#include "jpeg6/jconfig.h" /* widely used configuration options */ +#endif +#include "jpeg6/jmorecfg.h" /* seldom changed options */ + + +/* Version ID for the JPEG library. + * Might be useful for tests like "#if JPEG_LIB_VERSION >= 60". + */ + +#define JPEG_LIB_VERSION 60 /* Version 6 */ + + +/* Various constants determining the sizes of things. + * All of these are specified by the JPEG standard, so don't change them + * if you want to be compatible. + */ + +#define DCTSIZE 8 /* The basic DCT block is 8x8 samples */ +#define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ +#define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ +#define NUM_HUFF_TBLS 4 /* Huffman tables are numbered 0..3 */ +#define NUM_ARITH_TBLS 16 /* Arith-coding tables are numbered 0..15 */ +#define MAX_COMPS_IN_SCAN 4 /* JPEG limit on # of components in one scan */ +#define MAX_SAMP_FACTOR 4 /* JPEG limit on sampling factors */ +/* Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; + * the PostScript DCT filter can emit files with many more than 10 blocks/MCU. + * If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU + * to handle it. We even let you do this from the jconfig.h file. However, + * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe + * sometimes emits noncompliant files doesn't mean you should too. + */ +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on blocks per MCU */ +#ifndef D_MAX_BLOCKS_IN_MCU +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on blocks per MCU */ +#endif + + +/* This macro is used to declare a "method", that is, a function pointer. + * We want to supply prototype parameters if the compiler can cope. + * Note that the arglist parameter must be parenthesized! + */ + +#ifdef HAVE_PROTOTYPES +#define JMETHOD(type,methodname,arglist) type (*methodname) arglist +#else +#define JMETHOD(type,methodname,arglist) type (*methodname) () +#endif + + +/* Data structures for images (arrays of samples and of DCT coefficients). + * On 80x86 machines, the image arrays are too big for near pointers, + * but the pointer arrays can fit in near memory. + */ + +typedef JSAMPLE FAR *JSAMPROW; /* ptr to one image row of pixel samples. */ +typedef JSAMPROW *JSAMPARRAY; /* ptr to some rows (a 2-D sample array) */ +typedef JSAMPARRAY *JSAMPIMAGE; /* a 3-D sample array: top index is color */ + +typedef JCOEF JBLOCK[DCTSIZE2]; /* one block of coefficients */ +typedef JBLOCK FAR *JBLOCKROW; /* pointer to one row of coefficient blocks */ +typedef JBLOCKROW *JBLOCKARRAY; /* a 2-D array of coefficient blocks */ +typedef JBLOCKARRAY *JBLOCKIMAGE; /* a 3-D array of coefficient blocks */ + +typedef JCOEF FAR *JCOEFPTR; /* useful in a couple of places */ + + +/* Types for JPEG compression parameters and working tables. */ + + +/* DCT coefficient quantization tables. */ + +typedef struct { + /* This field directly represents the contents of a JPEG DQT marker. + * Note: the values are always given in zigzag order. + */ + UINT16 quantval[DCTSIZE2]; /* quantization step for each coefficient */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JQUANT_TBL; + + +/* Huffman coding tables. */ + +typedef struct { + /* These two fields directly represent the contents of a JPEG DHT marker */ + UINT8 bits[17]; /* bits[k] = # of symbols with codes of */ + /* length k bits; bits[0] is unused */ + UINT8 huffval[256]; /* The symbols, in order of incr code length */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JHUFF_TBL; + + +/* Basic info about one component (color channel). */ + +typedef struct { + /* These values are fixed over the whole image. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOF marker. */ + int component_id; /* identifier for this component (0..255) */ + int component_index; /* its index in SOF or cinfo->comp_info[] */ + int h_samp_factor; /* horizontal sampling factor (1..4) */ + int v_samp_factor; /* vertical sampling factor (1..4) */ + int quant_tbl_no; /* quantization table selector (0..3) */ + /* These values may vary between scans. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOS marker. */ + /* The decompressor output side may not use these variables. */ + int dc_tbl_no; /* DC entropy table selector (0..3) */ + int ac_tbl_no; /* AC entropy table selector (0..3) */ + + /* Remaining fields should be treated as private by applications. */ + + /* These values are computed during compression or decompression startup: */ + /* Component's size in DCT blocks. + * Any dummy blocks added to complete an MCU are not counted; therefore + * these values do not depend on whether a scan is interleaved or not. + */ + JDIMENSION width_in_blocks; + JDIMENSION height_in_blocks; + /* Size of a DCT block in samples. Always DCTSIZE for compression. + * For decompression this is the size of the output from one DCT block, + * reflecting any scaling we choose to apply during the IDCT step. + * Values of 1,2,4,8 are likely to be supported. Note that different + * components may receive different IDCT scalings. + */ + int DCT_scaled_size; + /* The downsampled dimensions are the component's actual, unpadded number + * of samples at the main buffer (preprocessing/compression interface), thus + * downsampled_width = ceil(image_width * Hi/Hmax) + * and similarly for height. For decompression, IDCT scaling is included, so + * downsampled_width = ceil(image_width * Hi/Hmax * DCT_scaled_size/DCTSIZE) + */ + JDIMENSION downsampled_width; /* actual width in samples */ + JDIMENSION downsampled_height; /* actual height in samples */ + /* This flag is used only for decompression. In cases where some of the + * components will be ignored (eg grayscale output from YCbCr image), + * we can skip most computations for the unused components. + */ + boolean component_needed; /* do we need the value of this component? */ + + /* These values are computed before starting a scan of the component. */ + /* The decompressor output side may not use these variables. */ + int MCU_width; /* number of blocks per MCU, horizontally */ + int MCU_height; /* number of blocks per MCU, vertically */ + int MCU_blocks; /* MCU_width * MCU_height */ + int MCU_sample_width; /* MCU width in samples, MCU_width*DCT_scaled_size */ + int last_col_width; /* # of non-dummy blocks across in last MCU */ + int last_row_height; /* # of non-dummy blocks down in last MCU */ + + /* Saved quantization table for component; NULL if none yet saved. + * See jdinput.c comments about the need for this information. + * This field is not currently used by the compressor. + */ + JQUANT_TBL * quant_table; + + /* Private per-component storage for DCT or IDCT subsystem. */ + void * dct_table; +} jpeg_component_info; + + +/* The script for encoding a multiple-scan file is an array of these: */ + +typedef struct { + int comps_in_scan; /* number of components encoded in this scan */ + int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ + int Ss, Se; /* progressive JPEG spectral selection parms */ + int Ah, Al; /* progressive JPEG successive approx. parms */ +} jpeg_scan_info; + + +/* Known color spaces. */ + +typedef enum { + JCS_UNKNOWN, /* error/unspecified */ + JCS_GRAYSCALE, /* monochrome */ + JCS_RGB, /* red/green/blue */ + JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */ + JCS_CMYK, /* C/M/Y/K */ + JCS_YCCK /* Y/Cb/Cr/K */ +} J_COLOR_SPACE; + +/* DCT/IDCT algorithm options. */ + +typedef enum { + JDCT_ISLOW, /* slow but accurate integer algorithm */ + JDCT_IFAST, /* faster, less accurate integer method */ + JDCT_FLOAT /* floating-point: accurate, fast on fast HW */ +} J_DCT_METHOD; + +#ifndef JDCT_DEFAULT /* may be overridden in jconfig.h */ +#define JDCT_DEFAULT JDCT_ISLOW +#endif +#ifndef JDCT_FASTEST /* may be overridden in jconfig.h */ +#define JDCT_FASTEST JDCT_IFAST +#endif + +/* Dithering options for decompression. */ + +typedef enum { + JDITHER_NONE, /* no dithering */ + JDITHER_ORDERED, /* simple ordered dither */ + JDITHER_FS /* Floyd-Steinberg error diffusion dither */ +} J_DITHER_MODE; + + +/* Common fields between JPEG compression and decompression master structs. */ + +#define jpeg_common_fields \ + struct jpeg_error_mgr * err; /* Error handler module */\ + struct jpeg_memory_mgr * mem; /* Memory manager module */\ + struct jpeg_progress_mgr * progress; /* Progress monitor, or NULL if none */\ + boolean is_decompressor; /* so common code can tell which is which */\ + int global_state /* for checking call sequence validity */ + +/* Routines that are to be used by both halves of the library are declared + * to receive a pointer to this structure. There are no actual instances of + * jpeg_common_struct, only of jpeg_compress_struct and jpeg_decompress_struct. + */ +struct jpeg_common_struct { + jpeg_common_fields; /* Fields common to both master struct types */ + /* Additional fields follow in an actual jpeg_compress_struct or + * jpeg_decompress_struct. All three structs must agree on these + * initial fields! (This would be a lot cleaner in C++.) + */ +}; + +typedef struct jpeg_common_struct * j_common_ptr; +typedef struct jpeg_compress_struct * j_compress_ptr; +typedef struct jpeg_decompress_struct * j_decompress_ptr; + + +/* Master record for a compression instance */ + +struct jpeg_compress_struct { + jpeg_common_fields; /* Fields shared with jpeg_decompress_struct */ + + /* Destination for compressed data */ + struct jpeg_destination_mgr * dest; + + /* Description of source image --- these fields must be filled in by + * outer application before starting compression. in_color_space must + * be correct before you can even call jpeg_set_defaults(). + */ + + JDIMENSION image_width; /* input image width */ + JDIMENSION image_height; /* input image height */ + int input_components; /* # of color components in input image */ + J_COLOR_SPACE in_color_space; /* colorspace of input image */ + + double input_gamma; /* image gamma of input image */ + + /* Compression parameters --- these fields must be set before calling + * jpeg_start_compress(). We recommend calling jpeg_set_defaults() to + * initialize everything to reasonable defaults, then changing anything + * the application specifically wants to change. That way you won't get + * burnt when new parameters are added. Also note that there are several + * helper routines to simplify changing parameters. + */ + + int data_precision; /* bits of precision in image data */ + + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + int num_scans; /* # of entries in scan_info array */ + const jpeg_scan_info * scan_info; /* script for multi-scan file, or NULL */ + /* The default value of scan_info is NULL, which causes a single-scan + * sequential JPEG file to be emitted. To create a multi-scan file, + * set num_scans and scan_info to point to an array of scan definitions. + */ + + boolean raw_data_in; /* TRUE=caller supplies downsampled data */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + boolean optimize_coding; /* TRUE=optimize entropy encoding parms */ + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + int smoothing_factor; /* 1..100, or 0 for no input smoothing */ + J_DCT_METHOD dct_method; /* DCT algorithm selector */ + + /* The restart interval can be specified in absolute MCUs by setting + * restart_interval, or in MCU rows by setting restart_in_rows + * (in which case the correct restart_interval will be figured + * for each scan). + */ + unsigned int restart_interval; /* MCUs per restart, or 0 for no restart */ + int restart_in_rows; /* if > 0, MCU rows per restart interval */ + + /* Parameters controlling emission of special markers. */ + + boolean write_JFIF_header; /* should a JFIF marker be written? */ + /* These three values are not used by the JPEG code, merely copied */ + /* into the JFIF APP0 marker. density_unit can be 0 for unknown, */ + /* 1 for dots/inch, or 2 for dots/cm. Note that the pixel aspect */ + /* ratio is defined by X_density/Y_density even when density_unit=0. */ + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean write_Adobe_marker; /* should an Adobe marker be written? */ + + /* State variable: index of next scanline to be written to + * jpeg_write_scanlines(). Application may use this to control its + * processing loop, e.g., "while (next_scanline < image_height)". + */ + + JDIMENSION next_scanline; /* 0 .. image_height-1 */ + + /* Remaining fields are known throughout compressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during compression startup + */ + boolean progressive_mode; /* TRUE if scan script uses progressive mode */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coef ctlr */ + /* The coefficient controller receives data in units of MCU rows as defined + * for fully interleaved scans (whether the JPEG file is interleaved or not). + * There are v_samp_factor * DCTSIZE sample rows of each component in an + * "iMCU" (interleaved MCU) row. + */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[C_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* + * Links to compression subobjects (methods and private variables of modules) + */ + struct jpeg_comp_master * master; + struct jpeg_c_main_controller * main; + struct jpeg_c_prep_controller * prep; + struct jpeg_c_coef_controller * coef; + struct jpeg_marker_writer * marker; + struct jpeg_color_converter * cconvert; + struct jpeg_downsampler * downsample; + struct jpeg_forward_dct * fdct; + struct jpeg_entropy_encoder * entropy; +}; + + +/* Master record for a decompression instance */ + +struct jpeg_decompress_struct { + jpeg_common_fields; /* Fields shared with jpeg_compress_struct */ + + /* Source of compressed data */ + struct jpeg_source_mgr * src; + + /* Basic description of image --- filled in by jpeg_read_header(). */ + /* Application may inspect these values to decide how to process image. */ + + JDIMENSION image_width; /* nominal image width (from SOF marker) */ + JDIMENSION image_height; /* nominal image height */ + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + /* Decompression processing parameters --- these fields must be set before + * calling jpeg_start_decompress(). Note that jpeg_read_header() initializes + * them to default values. + */ + + J_COLOR_SPACE out_color_space; /* colorspace for output */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + double output_gamma; /* image gamma wanted in output */ + + boolean buffered_image; /* TRUE=multiple output passes */ + boolean raw_data_out; /* TRUE=downsampled data wanted */ + + J_DCT_METHOD dct_method; /* IDCT algorithm selector */ + boolean do_fancy_upsampling; /* TRUE=apply fancy upsampling */ + boolean do_block_smoothing; /* TRUE=apply interblock smoothing */ + + boolean quantize_colors; /* TRUE=colormapped output wanted */ + /* the following are ignored if not quantize_colors: */ + J_DITHER_MODE dither_mode; /* type of color dithering to use */ + boolean two_pass_quantize; /* TRUE=use two-pass color quantization */ + int desired_number_of_colors; /* max # colors to use in created colormap */ + /* these are significant only in buffered-image mode: */ + boolean enable_1pass_quant; /* enable future use of 1-pass quantizer */ + boolean enable_external_quant;/* enable future use of external colormap */ + boolean enable_2pass_quant; /* enable future use of 2-pass quantizer */ + + /* Description of actual output image that will be returned to application. + * These fields are computed by jpeg_start_decompress(). + * You can also use jpeg_calc_output_dimensions() to determine these values + * in advance of calling jpeg_start_decompress(). + */ + + JDIMENSION output_width; /* scaled image width */ + JDIMENSION output_height; /* scaled image height */ + int out_color_components; /* # of color components in out_color_space */ + int output_components; /* # of color components returned */ + /* output_components is 1 (a colormap index) when quantizing colors; + * otherwise it equals out_color_components. + */ + int rec_outbuf_height; /* min recommended height of scanline buffer */ + /* If the buffer passed to jpeg_read_scanlines() is less than this many rows + * high, space and time will be wasted due to unnecessary data copying. + * Usually rec_outbuf_height will be 1 or 2, at most 4. + */ + + /* When quantizing colors, the output colormap is described by these fields. + * The application can supply a colormap by setting colormap non-NULL before + * calling jpeg_start_decompress; otherwise a colormap is created during + * jpeg_start_decompress or jpeg_start_output. + * The map has out_color_components rows and actual_number_of_colors columns. + */ + int actual_number_of_colors; /* number of entries in use */ + JSAMPARRAY colormap; /* The color map as a 2-D pixel array */ + + /* State variables: these variables indicate the progress of decompression. + * The application may examine these but must not modify them. + */ + + /* Row index of next scanline to be read from jpeg_read_scanlines(). + * Application may use this to control its processing loop, e.g., + * "while (output_scanline < output_height)". + */ + JDIMENSION output_scanline; /* 0 .. output_height-1 */ + + /* Current input scan number and number of iMCU rows completed in scan. + * These indicate the progress of the decompressor input side. + */ + int input_scan_number; /* Number of SOS markers seen so far */ + JDIMENSION input_iMCU_row; /* Number of iMCU rows completed */ + + /* The "output scan number" is the notional scan being displayed by the + * output side. The decompressor will not allow output scan/row number + * to get ahead of input scan/row, but it can fall arbitrarily far behind. + */ + int output_scan_number; /* Nominal scan number being displayed */ + JDIMENSION output_iMCU_row; /* Number of iMCU rows read */ + + /* Current progression status. coef_bits[c][i] indicates the precision + * with which component c's DCT coefficient i (in zigzag order) is known. + * It is -1 when no data has yet been received, otherwise it is the point + * transform (shift) value for the most recent scan of the coefficient + * (thus, 0 at completion of the progression). + * This pointer is NULL when reading a non-progressive file. + */ + int (*coef_bits)[DCTSIZE2]; /* -1 or current Al value for each coef */ + + /* Internal JPEG parameters --- the application usually need not look at + * these fields. Note that the decompressor output side may not use + * any parameters that can change between scans. + */ + + /* Quantization and Huffman tables are carried forward across input + * datastreams when processing abbreviated JPEG datastreams. + */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + /* These parameters are never carried across datastreams, since they + * are given in SOF/SOS markers or defined to be reset by SOI. + */ + + int data_precision; /* bits of precision in image data */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + boolean progressive_mode; /* TRUE if SOFn specifies progressive mode */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + unsigned int restart_interval; /* MCUs per restart interval, or 0 for no restart */ + + /* These fields record data obtained from optional markers recognized by + * the JPEG library. + */ + boolean saw_JFIF_marker; /* TRUE iff a JFIF APP0 marker was found */ + /* Data copied from JFIF marker: */ + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean saw_Adobe_marker; /* TRUE iff an Adobe APP14 marker was found */ + UINT8 Adobe_transform; /* Color transform code from Adobe marker */ + + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + + /* Remaining fields are known throughout decompressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during decompression startup + */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_scaled_size; /* smallest DCT_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ + /* The coefficient controller's input and output progress is measured in + * units of "iMCU" (interleaved MCU) rows. These are the same as MCU rows + * in fully interleaved JPEG scans, but are used whether the scan is + * interleaved or not. We define an iMCU row as v_samp_factor DCT block + * rows of each component. Therefore, the IDCT output contains + * v_samp_factor*DCT_scaled_size sample rows of a component per iMCU row. + */ + + JSAMPLE * sample_range_limit; /* table for fast range-limiting */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + * Note that the decompressor output side must not use these fields. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[D_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* This field is shared between entropy decoder and marker parser. + * It is either zero or the code of a JPEG marker that has been + * read from the data source, but has not yet been processed. + */ + int unread_marker; + + /* + * Links to decompression subobjects (methods, private variables of modules) + */ + struct jpeg_decomp_master * master; + struct jpeg_d_main_controller * main; + struct jpeg_d_coef_controller * coef; + struct jpeg_d_post_controller * post; + struct jpeg_input_controller * inputctl; + struct jpeg_marker_reader * marker; + struct jpeg_entropy_decoder * entropy; + struct jpeg_inverse_dct * idct; + struct jpeg_upsampler * upsample; + struct jpeg_color_deconverter * cconvert; + struct jpeg_color_quantizer * cquantize; +}; + + +/* "Object" declarations for JPEG modules that may be supplied or called + * directly by the surrounding application. + * As with all objects in the JPEG library, these structs only define the + * publicly visible methods and state variables of a module. Additional + * private fields may exist after the public ones. + */ + + +/* Error handler object */ + +struct jpeg_error_mgr { + /* Error exit handler: does not return to caller */ + JMETHOD(void, error_exit, (j_common_ptr cinfo)); + /* Conditionally emit a trace or warning message */ + JMETHOD(void, emit_message, (j_common_ptr cinfo, int msg_level)); + /* Routine that actually outputs a trace or error message */ + JMETHOD(void, output_message, (j_common_ptr cinfo)); + /* Format a message string for the most recent JPEG error or message */ + JMETHOD(void, format_message, (j_common_ptr cinfo, char * buffer)); +#define JMSG_LENGTH_MAX 200 /* recommended size of format_message buffer */ + /* Reset error state variables at start of a new image */ + JMETHOD(void, reset_error_mgr, (j_common_ptr cinfo)); + + /* The message ID code and any parameters are saved here. + * A message can have one string parameter or up to 8 int parameters. + */ + int msg_code; +#define JMSG_STR_PARM_MAX 80 + union { + int i[8]; + char s[JMSG_STR_PARM_MAX]; + } msg_parm; + + /* Standard state variables for error facility */ + + int trace_level; /* max msg_level that will be displayed */ + + /* For recoverable corrupt-data errors, we emit a warning message, + * but keep going unless emit_message chooses to abort. emit_message + * should count warnings in num_warnings. The surrounding application + * can check for bad data by seeing if num_warnings is nonzero at the + * end of processing. + */ + long num_warnings; /* number of corrupt-data warnings */ + + /* These fields point to the table(s) of error message strings. + * An application can change the table pointer to switch to a different + * message list (typically, to change the language in which errors are + * reported). Some applications may wish to add additional error codes + * that will be handled by the JPEG library error mechanism; the second + * table pointer is used for this purpose. + * + * First table includes all errors generated by JPEG library itself. + * Error code 0 is reserved for a "no such error string" message. + */ + const char * const * jpeg_message_table; /* Library errors */ + int last_jpeg_message; /* Table contains strings 0..last_jpeg_message */ + /* Second table can be added by application (see cjpeg/djpeg for example). + * It contains strings numbered first_addon_message..last_addon_message. + */ + const char * const * addon_message_table; /* Non-library errors */ + int first_addon_message; /* code for first string in addon table */ + int last_addon_message; /* code for last string in addon table */ +}; + + +/* Progress monitor object */ + +struct jpeg_progress_mgr { + JMETHOD(void, progress_monitor, (j_common_ptr cinfo)); + + long pass_counter; /* work units completed in this pass */ + long pass_limit; /* total number of work units in this pass */ + int completed_passes; /* passes completed so far */ + int total_passes; /* total number of passes expected */ +}; + + +/* Data destination object for compression */ + +struct jpeg_destination_mgr { + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + + JMETHOD(void, init_destination, (j_compress_ptr cinfo)); + JMETHOD(boolean, empty_output_buffer, (j_compress_ptr cinfo)); + JMETHOD(void, term_destination, (j_compress_ptr cinfo)); +}; + + +/* Data source object for decompression */ + +struct jpeg_source_mgr { + const JOCTET * next_input_byte; /* => next byte to read from buffer */ + size_t bytes_in_buffer; /* # of bytes remaining in buffer */ + + JMETHOD(void, init_source, (j_decompress_ptr cinfo)); + JMETHOD(boolean, fill_input_buffer, (j_decompress_ptr cinfo)); + JMETHOD(void, skip_input_data, (j_decompress_ptr cinfo, long num_bytes)); + JMETHOD(boolean, resync_to_restart, (j_decompress_ptr cinfo, int desired)); + JMETHOD(void, term_source, (j_decompress_ptr cinfo)); +}; + + +/* Memory manager object. + * Allocates "small" objects (a few K total), "large" objects (tens of K), + * and "really big" objects (virtual arrays with backing store if needed). + * The memory manager does not allow individual objects to be freed; rather, + * each created object is assigned to a pool, and whole pools can be freed + * at once. This is faster and more convenient than remembering exactly what + * to free, especially where malloc()/free() are not too speedy. + * NB: alloc routines never return NULL. They exit to error_exit if not + * successful. + */ + +#define JPOOL_PERMANENT 0 /* lasts until master record is destroyed */ +#define JPOOL_IMAGE 1 /* lasts until done with image/datastream */ +#define JPOOL_NUMPOOLS 2 + +typedef struct jvirt_sarray_control * jvirt_sarray_ptr; +typedef struct jvirt_barray_control * jvirt_barray_ptr; + + +struct jpeg_memory_mgr { + /* Method pointers */ + JMETHOD(void *, alloc_small, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(void FAR *, alloc_large, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(JSAMPARRAY, alloc_sarray, (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, + JDIMENSION numrows)); + JMETHOD(JBLOCKARRAY, alloc_barray, (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, + JDIMENSION numrows)); + JMETHOD(jvirt_sarray_ptr, request_virt_sarray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION samplesperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(jvirt_barray_ptr, request_virt_barray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION blocksperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(void, realize_virt_arrays, (j_common_ptr cinfo)); + JMETHOD(JSAMPARRAY, access_virt_sarray, (j_common_ptr cinfo, + jvirt_sarray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(JBLOCKARRAY, access_virt_barray, (j_common_ptr cinfo, + jvirt_barray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(void, free_pool, (j_common_ptr cinfo, int pool_id)); + JMETHOD(void, self_destruct, (j_common_ptr cinfo)); + + /* Limit on memory allocation for this JPEG object. (Note that this is + * merely advisory, not a guaranteed maximum; it only affects the space + * used for virtual-array buffers.) May be changed by outer application + * after creating the JPEG object. + */ + long max_memory_to_use; +}; + + +/* Routine signature for application-supplied marker processing methods. + * Need not pass marker code since it is stored in cinfo->unread_marker. + */ +typedef JMETHOD(boolean, jpeg_marker_parser_method, (j_decompress_ptr cinfo)); + + +/* Declarations for routines called by application. + * The JPP macro hides prototype parameters from compilers that can't cope. + * Note JPP requires double parentheses. + */ + +#ifdef HAVE_PROTOTYPES +#define JPP(arglist) arglist +#else +#define JPP(arglist) () +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. + * We shorten external names to be unique in the first six letters, which + * is good enough for all known systems. + * (If your compiler itself needs names to be unique in less than 15 + * characters, you are out of luck. Get a better compiler.) + */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_std_error jStdError +#define jpeg_create_compress jCreaCompress +#define jpeg_create_decompress jCreaDecompress +#define jpeg_destroy_compress jDestCompress +#define jpeg_destroy_decompress jDestDecompress +#define jpeg_stdio_dest jStdDest +#define jpeg_stdio_src jStdSrc +#define jpeg_set_defaults jSetDefaults +#define jpeg_set_colorspace jSetColorspace +#define jpeg_default_colorspace jDefColorspace +#define jpeg_set_quality jSetQuality +#define jpeg_set_linear_quality jSetLQuality +#define jpeg_add_quant_table jAddQuantTable +#define jpeg_quality_scaling jQualityScaling +#define jpeg_simple_progression jSimProgress +#define jpeg_suppress_tables jSuppressTables +#define jpeg_alloc_quant_table jAlcQTable +#define jpeg_alloc_huff_table jAlcHTable +#define jpeg_start_compress jStrtCompress +#define jpeg_write_scanlines jWrtScanlines +#define jpeg_finish_compress jFinCompress +#define jpeg_write_raw_data jWrtRawData +#define jpeg_write_marker jWrtMarker +#define jpeg_write_tables jWrtTables +#define jpeg_read_header jReadHeader +#define jpeg_start_decompress jStrtDecompress +#define jpeg_read_scanlines jReadScanlines +#define jpeg_finish_decompress jFinDecompress +#define jpeg_read_raw_data jReadRawData +#define jpeg_has_multiple_scans jHasMultScn +#define jpeg_start_output jStrtOutput +#define jpeg_finish_output jFinOutput +#define jpeg_input_complete jInComplete +#define jpeg_new_colormap jNewCMap +#define jpeg_consume_input jConsumeInput +#define jpeg_calc_output_dimensions jCalcDimensions +#define jpeg_set_marker_processor jSetMarker +#define jpeg_read_coefficients jReadCoefs +#define jpeg_write_coefficients jWrtCoefs +#define jpeg_copy_critical_parameters jCopyCrit +#define jpeg_abort_compress jAbrtCompress +#define jpeg_abort_decompress jAbrtDecompress +#define jpeg_abort jAbort +#define jpeg_destroy jDestroy +#define jpeg_resync_to_restart jResyncRestart +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Default error-management setup */ +EXTERN struct jpeg_error_mgr *jpeg_std_error JPP((struct jpeg_error_mgr *err)); + +/* Initialization and destruction of JPEG compression objects */ +/* NB: you must set up the error-manager BEFORE calling jpeg_create_xxx */ +EXTERN void jpeg_create_compress JPP((j_compress_ptr cinfo)); +EXTERN void jpeg_create_decompress JPP((j_decompress_ptr cinfo)); +EXTERN void jpeg_destroy_compress JPP((j_compress_ptr cinfo)); +EXTERN void jpeg_destroy_decompress JPP((j_decompress_ptr cinfo)); + +/* Standard data source and destination managers: stdio streams. */ +/* Caller is responsible for opening the file before and closing after. */ +EXTERN void jpeg_stdio_dest JPP((j_compress_ptr cinfo, FILE * outfile)); +EXTERN void jpeg_stdio_src JPP((j_decompress_ptr cinfo, unsigned char *infile)); + +/* Default parameter setup for compression */ +EXTERN void jpeg_set_defaults JPP((j_compress_ptr cinfo)); +/* Compression parameter setup aids */ +EXTERN void jpeg_set_colorspace JPP((j_compress_ptr cinfo, + J_COLOR_SPACE colorspace)); +EXTERN void jpeg_default_colorspace JPP((j_compress_ptr cinfo)); +EXTERN void jpeg_set_quality JPP((j_compress_ptr cinfo, int quality, + boolean force_baseline)); +EXTERN void jpeg_set_linear_quality JPP((j_compress_ptr cinfo, + int scale_factor, + boolean force_baseline)); +EXTERN void jpeg_add_quant_table JPP((j_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, + boolean force_baseline)); +EXTERN int jpeg_quality_scaling JPP((int quality)); +EXTERN void jpeg_simple_progression JPP((j_compress_ptr cinfo)); +EXTERN void jpeg_suppress_tables JPP((j_compress_ptr cinfo, + boolean suppress)); +EXTERN JQUANT_TBL * jpeg_alloc_quant_table JPP((j_common_ptr cinfo)); +EXTERN JHUFF_TBL * jpeg_alloc_huff_table JPP((j_common_ptr cinfo)); + +/* Main entry points for compression */ +EXTERN void jpeg_start_compress JPP((j_compress_ptr cinfo, + boolean write_all_tables)); +EXTERN JDIMENSION jpeg_write_scanlines JPP((j_compress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION num_lines)); +EXTERN void jpeg_finish_compress JPP((j_compress_ptr cinfo)); + +/* Replaces jpeg_write_scanlines when writing raw downsampled data. */ +EXTERN JDIMENSION jpeg_write_raw_data JPP((j_compress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION num_lines)); + +/* Write a special marker. See libjpeg.doc concerning safe usage. */ +EXTERN void jpeg_write_marker JPP((j_compress_ptr cinfo, int marker, + const JOCTET *dataptr, unsigned int datalen)); + +/* Alternate compression function: just write an abbreviated table file */ +EXTERN void jpeg_write_tables JPP((j_compress_ptr cinfo)); + +/* Decompression startup: read start of JPEG datastream to see what's there */ +EXTERN int jpeg_read_header JPP((j_decompress_ptr cinfo, + boolean require_image)); +/* Return value is one of: */ +#define JPEG_SUSPENDED 0 /* Suspended due to lack of input data */ +#define JPEG_HEADER_OK 1 /* Found valid image datastream */ +#define JPEG_HEADER_TABLES_ONLY 2 /* Found valid table-specs-only datastream */ +/* If you pass require_image = TRUE (normal case), you need not check for + * a TABLES_ONLY return code; an abbreviated file will cause an error exit. + * JPEG_SUSPENDED is only possible if you use a data source module that can + * give a suspension return (the stdio source module doesn't). + */ + +/* Main entry points for decompression */ +EXTERN boolean jpeg_start_decompress JPP((j_decompress_ptr cinfo)); +EXTERN JDIMENSION jpeg_read_scanlines JPP((j_decompress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION max_lines)); +EXTERN boolean jpeg_finish_decompress JPP((j_decompress_ptr cinfo)); + +/* Replaces jpeg_read_scanlines when reading raw downsampled data. */ +EXTERN JDIMENSION jpeg_read_raw_data JPP((j_decompress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION max_lines)); + +/* Additional entry points for buffered-image mode. */ +EXTERN boolean jpeg_has_multiple_scans JPP((j_decompress_ptr cinfo)); +EXTERN boolean jpeg_start_output JPP((j_decompress_ptr cinfo, + int scan_number)); +EXTERN boolean jpeg_finish_output JPP((j_decompress_ptr cinfo)); +EXTERN boolean jpeg_input_complete JPP((j_decompress_ptr cinfo)); +EXTERN void jpeg_new_colormap JPP((j_decompress_ptr cinfo)); +EXTERN int jpeg_consume_input JPP((j_decompress_ptr cinfo)); +/* Return value is one of: */ +/* #define JPEG_SUSPENDED 0 Suspended due to lack of input data */ +#define JPEG_REACHED_SOS 1 /* Reached start of new scan */ +#define JPEG_REACHED_EOI 2 /* Reached end of image */ +#define JPEG_ROW_COMPLETED 3 /* Completed one iMCU row */ +#define JPEG_SCAN_COMPLETED 4 /* Completed last iMCU row of a scan */ + +/* Precalculate output dimensions for current decompression parameters. */ +EXTERN void jpeg_calc_output_dimensions JPP((j_decompress_ptr cinfo)); + +/* Install a special processing method for COM or APPn markers. */ +EXTERN void jpeg_set_marker_processor JPP((j_decompress_ptr cinfo, + int marker_code, + jpeg_marker_parser_method routine)); + +/* Read or write raw DCT coefficients --- useful for lossless transcoding. */ +EXTERN jvirt_barray_ptr * jpeg_read_coefficients JPP((j_decompress_ptr cinfo)); +EXTERN void jpeg_write_coefficients JPP((j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays)); +EXTERN void jpeg_copy_critical_parameters JPP((j_decompress_ptr srcinfo, + j_compress_ptr dstinfo)); + +/* If you choose to abort compression or decompression before completing + * jpeg_finish_(de)compress, then you need to clean up to release memory, + * temporary files, etc. You can just call jpeg_destroy_(de)compress + * if you're done with the JPEG object, but if you want to clean it up and + * reuse it, call this: + */ +EXTERN void jpeg_abort_compress JPP((j_compress_ptr cinfo)); +EXTERN void jpeg_abort_decompress JPP((j_decompress_ptr cinfo)); + +/* Generic versions of jpeg_abort and jpeg_destroy that work on either + * flavor of JPEG object. These may be more convenient in some places. + */ +EXTERN void jpeg_abort JPP((j_common_ptr cinfo)); +EXTERN void jpeg_destroy JPP((j_common_ptr cinfo)); + +/* Default restart-marker-resync procedure for use by data source modules */ +EXTERN boolean jpeg_resync_to_restart JPP((j_decompress_ptr cinfo, + int desired)); + + +/* These marker codes are exported since applications and data source modules + * are likely to want to use them. + */ + +#define JPEG_RST0 0xD0 /* RST0 marker code */ +#define JPEG_EOI 0xD9 /* EOI marker code */ +#define JPEG_APP0 0xE0 /* APP0 marker code */ +#define JPEG_COM 0xFE /* COM marker code */ + + +/* If we have a brain-damaged compiler that emits warnings (or worse, errors) + * for structure definitions that are never filled in, keep it quiet by + * supplying dummy definitions for the various substructures. + */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef JPEG_INTERNALS /* will be defined in jpegint.h */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +struct jpeg_comp_master { long dummy; }; +struct jpeg_c_main_controller { long dummy; }; +struct jpeg_c_prep_controller { long dummy; }; +struct jpeg_c_coef_controller { long dummy; }; +struct jpeg_marker_writer { long dummy; }; +struct jpeg_color_converter { long dummy; }; +struct jpeg_downsampler { long dummy; }; +struct jpeg_forward_dct { long dummy; }; +struct jpeg_entropy_encoder { long dummy; }; +struct jpeg_decomp_master { long dummy; }; +struct jpeg_d_main_controller { long dummy; }; +struct jpeg_d_coef_controller { long dummy; }; +struct jpeg_d_post_controller { long dummy; }; +struct jpeg_input_controller { long dummy; }; +struct jpeg_marker_reader { long dummy; }; +struct jpeg_entropy_decoder { long dummy; }; +struct jpeg_inverse_dct { long dummy; }; +struct jpeg_upsampler { long dummy; }; +struct jpeg_color_deconverter { long dummy; }; +struct jpeg_color_quantizer { long dummy; }; +#endif /* JPEG_INTERNALS */ +#endif /* INCOMPLETE_TYPES_BROKEN */ + + +/* + * The JPEG library modules define JPEG_INTERNALS before including this file. + * The internal structure declarations are read only when that is true. + * Applications using the library should not include jpegint.h, but may wish + * to include jerror.h. + */ + +#ifdef JPEG_INTERNALS +#include "jpegint.h" /* fetch private declarations */ +#include "jerror.h" /* fetch error codes too */ +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* JPEGLIB_H */ diff --git a/CODE-mp/Splines/Splines.dsp b/utils/Libs/pak/pak.dsp similarity index 55% rename from CODE-mp/Splines/Splines.dsp rename to utils/Libs/pak/pak.dsp index e8c2789..93de275 100644 --- a/CODE-mp/Splines/Splines.dsp +++ b/utils/Libs/pak/pak.dsp @@ -1,34 +1,34 @@ -# Microsoft Developer Studio Project File - Name="Splines" - Package Owner=<4> +# Microsoft Developer Studio Project File - Name="pak" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Static Library" 0x0104 -CFG=Splines - Win32 Debug +CFG=pak - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE -!MESSAGE NMAKE /f "Splines.mak". +!MESSAGE NMAKE /f "pak.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE -!MESSAGE NMAKE /f "Splines.mak" CFG="Splines - Win32 Debug" +!MESSAGE NMAKE /f "pak.mak" CFG="pak - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE -!MESSAGE "Splines - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "Splines - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE "pak - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "pak - Win32 Debug" (based on "Win32 (x86) Static Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName ""$/General/code/Splines", GAAAAAAA" +# PROP Scc_ProjName ""$/Utils/Libs/pak", UKNAAAAA" # PROP Scc_LocalPath "." CPP=cl.exe RSC=rc.exe -!IF "$(CFG)" == "Splines - Win32 Release" +!IF "$(CFG)" == "pak - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GR /GX /Od /I ".." /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe @@ -49,9 +49,9 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LIB32=link.exe -lib # ADD BASE LIB32 /nologo -# ADD LIB32 /nologo +# ADD LIB32 /nologo /out:"..\pak.lib" -!ELSEIF "$(CFG)" == "Splines - Win32 Debug" +!ELSEIF "$(CFG)" == "pak - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 @@ -64,7 +64,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GR /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GR /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe @@ -72,48 +72,24 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LIB32=link.exe -lib # ADD BASE LIB32 /nologo -# ADD LIB32 /nologo +# ADD LIB32 /nologo /out:"..\pakd.lib" !ENDIF # Begin Target -# Name "Splines - Win32 Release" -# Name "Splines - Win32 Debug" +# Name "pak - Win32 Release" +# Name "pak - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File -SOURCE=.\math_angles.cpp +SOURCE=.\pakstuff.cpp # End Source File # Begin Source File -SOURCE=.\math_matrix.cpp -# End Source File -# Begin Source File - -SOURCE=.\math_quaternion.cpp -# End Source File -# Begin Source File - -SOURCE=.\math_vector.cpp -# End Source File -# Begin Source File - -SOURCE=.\q_parse.cpp -# End Source File -# Begin Source File - -SOURCE=.\q_shared.cpp -# End Source File -# Begin Source File - -SOURCE=.\splines.cpp -# End Source File -# Begin Source File - -SOURCE=.\util_str.cpp +SOURCE=.\unzip.cpp # End Source File # End Group # Begin Group "Header Files" @@ -121,35 +97,15 @@ SOURCE=.\util_str.cpp # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File -SOURCE=.\math_angles.h +SOURCE=..\pakstuff.h # End Source File # Begin Source File -SOURCE=.\math_matrix.h +SOURCE=..\str.h # End Source File # Begin Source File -SOURCE=.\math_quaternion.h -# End Source File -# Begin Source File - -SOURCE=.\math_vector.h -# End Source File -# Begin Source File - -SOURCE=.\q_shared.h -# End Source File -# Begin Source File - -SOURCE=.\splines.h -# End Source File -# Begin Source File - -SOURCE=.\util_list.h -# End Source File -# Begin Source File - -SOURCE=.\util_str.h +SOURCE=.\unzip.h # End Source File # End Group # End Target diff --git a/utils/Libs/pak/pak.dsw b/utils/Libs/pak/pak.dsw new file mode 100644 index 0000000..3077863 --- /dev/null +++ b/utils/Libs/pak/pak.dsw @@ -0,0 +1,37 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "pak"=.\pak.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/Utils/Libs/pak", UKNAAAAA + . + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ + begin source code control + "$/Utils/Libs/pak", UKNAAAAA + . + end source code control +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/utils/Libs/pak/pakstuff.cpp b/utils/Libs/pak/pakstuff.cpp new file mode 100644 index 0000000..626ff0e --- /dev/null +++ b/utils/Libs/pak/pakstuff.cpp @@ -0,0 +1,1186 @@ + +#include +#include +#include +#include +#include +#include "io.h" +#include "pakstuff.h" +#include "unzip.h" +//#include "cmdlib.h" +#include "str.h" + +int m_nPAKIndex; +FILE* pakfile[16]; +struct PACKDirectory pakdir; +PACKDirPtr pakdirptr = &pakdir; +UInt16 dirsize; +boolean pakopen = false; +int f_type; +DIRECTORY *paktextures = NULL; +boolean HavePakColormap; +UInt32 PakColormapOffset; +UInt32 PakColormapSize; +DIRECTORY *dirhead = NULL; +boolean g_bPK3 = false; +char g_strBasePath[1024]; + +struct PK3FileInfo +{ + unzFile m_zFile; + char *m_pName; + unz_s m_zInfo; + long m_lSize; + ~PK3FileInfo() + { + delete []m_pName; + } + bool operator ==(const PK3FileInfo& rhs) const { return strcmp(m_pName, rhs.m_pName) == 0; } +}; + +#define __PATHSEPERATOR '/' + +#define LOG_PAKFAIL + +#ifdef LOG_PAKFAIL + +class LogFile +{ +public: + FILE *m_pFile; + LogFile(const char* pName) + { + m_pFile = fopen(pName, "w"); + } + ~LogFile() + { + if (m_pFile) + { + fclose(m_pFile); + } + } + void Log(const char *pFormat, ...) + { + va_list arg_ptr; + va_start(arg_ptr, pFormat); + fprintf(m_pFile, pFormat, arg_ptr); + va_end(arg_ptr); + } +}; + +LogFile g_LogFile("c:\\paklog.txt"); +#endif + +template class StrPtr : public Str +{ +protected: + T* m_pPtr; + StrPtr() + { + m_pPtr = NULL; + } + + StrPtr(const char *pStr, T *p) : Str(pStr) + { + m_pPtr = p; + } + + T* Ptr() + { + return m_pPtr; + } + + T& Ref() + { + return *m_pPtr; + } + + +}; +// PtrList +// a list of ptrs +// +template class PtrList +{ +protected: + T *m_pPtr; + PtrList *m_pNext; + +public: + + PtrList() + { + m_pNext = NULL; + m_pPtr = NULL; + } + + PtrList(T *ip) + { + m_pNext = NULL; + m_pPtr = ip; + } + + ~PtrList() + { + delete m_pPtr; + } + + PtrList* Next() + { + return m_pNext; + } + + void Add(T *ip) + { + PtrList *pl = this; + while (pl && pl->m_pNext) + { + pl = pl->Next(); + } + pl->m_pNext = new PtrList(ip); + } + + void Remove() + { + PtrList *p = m_pNext; + if (p) + { + while (p->m_pNext != this && p->m_pNext != NULL) + { + p = p->m_pNext; + } + if (p->m_pNext == this) + { + p->m_pNext = m_pNext; + } + } + } + + virtual PtrList* Find(T *ip) + { + PtrList *p = m_pNext; + while (p) + { + if (*p->m_pPtr == *ip) + { + return p; + } + p = p->m_pNext; + } + return NULL; + } + + // remove vp from the list + void Remove(T *ip) + { + PtrList *p = Find(ip); + if (p) + { + p->Remove(); + } + } + + T* Ptr() + { + return m_pPtr; + } + + T& Ref() + { + return *m_pPtr; + } + + void RemoveAll() + { + PtrList *p = m_pNext; + while (p) + { + PtrList *p2 = p; + p = p->m_pNext; + delete p2; + } + } +}; + + +typedef PtrList ZFileList; +typedef PtrList StrList; +typedef PtrList PK3List; + + +StrList g_PK3TexturePaths; +PK3List g_PK3Files; +ZFileList g_zFiles; +#define WORK_LEN 1024 +#define TEXTURE_PATH "textures" +#define PATH_SEPERATORS "/\\:\0" + + +char* __StrDup(char* pStr) +{ + if (pStr) + { + return strcpy(new char[strlen(pStr)+1], pStr); + } + return NULL; +} + +char* __StrDup(const char* pStr) +{ + if (pStr) + { + return strcpy(new char[strlen(pStr)+1], pStr); + } + return NULL; +} + +#define MEM_BLOCKSIZE 4096 +void* __qblockmalloc(size_t nSize) +{ + void *b; + // round up to threshold + int nAllocSize = nSize % MEM_BLOCKSIZE; + if ( nAllocSize > 0) + { + nSize += MEM_BLOCKSIZE - nAllocSize; + } + b = malloc(nSize + 1); + memset (b, 0, nSize); + return b; +} + +void* __qmalloc (size_t nSize) +{ + void *b; + b = malloc(nSize + 1); + memset (b, 0, nSize); + return b; +} + + +/* +==================== +Extract file parts +==================== +*/ +void __ExtractFilePath (const char *path, char *dest) +{ + const char *src; + + src = path + strlen(path) - 1; + +// +// back up until a \ or the start +// + while (src != path && *(src-1) != __PATHSEPERATOR) + src--; + + memcpy (dest, path, src-path); + dest[src-path] = 0; +} + +void __ExtractFileName (const char *path, char *dest) +{ + const char *src; + + src = path + strlen(path) - 1; + +// +// back up until a \ or the start +// + while (src != path && *(src-1) != '/' + && *(src-1) != '\\' ) + src--; + + while (*src) + { + *dest++ = *src++; + } + *dest = 0; +} + +void __ExtractFileBase (const char *path, char *dest) +{ + const char *src; + + src = path + strlen(path) - 1; + +// +// back up until a \ or the start +// + while (src != path && *(src-1) != '/' + && *(src-1) != '\\' ) + src--; + + while (*src && *src != '.') + { + *dest++ = *src++; + } + *dest = 0; +} + +void __ExtractFileExtension (const char *path, char *dest) +{ + const char *src; + + src = path + strlen(path) - 1; + +// +// back up until a . or the start +// + while (src != path && *(src-1) != '.') + src--; + if (src == path) + { + *dest = 0; // no extension + return; + } + + strcpy (dest, src-1);//keep the . +} + + +void __ConvertDOSToUnixName( char *dst, const char *src ) +{ + while ( *src ) + { + if ( *src == '\\' ) + *dst = '/'; + else + *dst = *src; + dst++; src++; + } + *dst = 0; +} + + + + + +void AddSlash(Str& str) +{ + int nLen = str.GetLength(); + if (nLen > 0) + { + if (str[nLen-1] != '\\' && str[nLen-1] != '/') + str += '\\'; + } +} + +void FindReplace(Str& strContents, const char* pTag, const char* pValue) +{ + if (strcmp(pTag, pValue) == 0) + return; + for (int nPos = strContents.Find(pTag); nPos >= 0; nPos = strContents.Find(pTag)) + { + int nRightLen = strContents.GetLength() - strlen(pTag) - nPos; + Str strLeft(strContents.Left(nPos)); + Str strRight(strContents.Right(nRightLen)); + strLeft += pValue; + strLeft += strRight; + strContents = strLeft; + } +} + + + + + +void ProgError(char *errstr, ...) +{ + va_list args; + + va_start(args, errstr); + printf("\nProgram Error: *** "); + vprintf(errstr, args); + printf(" ***\n"); + va_end(args); + exit(5); +} + +boolean ReadBytes(FILE *file, void *addr, UInt32 size) +{ + while (size > 0x8000) + { + if (fread(addr, 1, 0x8000, file) != 0x8000) + return false; + addr = (char *)addr + 0x8000; + size -= 0x8000; + } + if (fread(addr, 1, size, file) != size) + return false; + return true; +} +int ReadMagic(FILE *file) +{ + UInt8 buf[4]; + + if (ReadBytes(file, buf, 4) == FALSE) + return FTYPE_ERROR; + if (!strncmp(reinterpret_cast(&buf[0]), "IWAD", 4)) + return FTYPE_IWAD; + if (!strncmp(reinterpret_cast(&buf[0]), "PWAD", 4)) + return FTYPE_PWAD; + if (!strncmp(reinterpret_cast(&buf[0]), "PACK", 4)) + return FTYPE_PACK; + if (!strncmp(reinterpret_cast(&buf[0]), "WAD2", 4)) + return FTYPE_WAD2; + if (buf[0] == 0x17 && buf[1] == 0 && buf[2] == 0 && buf[3] == 0) + return FTYPE_BSP; + if (!strncmp(reinterpret_cast(&buf[0]), "IDPO", 4)) + return FTYPE_MODEL; + if (!strncmp(reinterpret_cast(&buf[0]), "IDSP", 4)) + return FTYPE_SPRITE; + if (!strncmp(reinterpret_cast(&buf[0]), "RIFF", 4)) + return FTYPE_WAV; + if (!strncmp(reinterpret_cast(&buf[0]), ".snd", 4)) + return FTYPE_AU; + if (buf[0] == 'P') + { + if (buf[1] == '1') + return FTYPE_PBM_ASC; + if (buf[1] == '2') + return FTYPE_PGM_ASC; + if (buf[1] == '3') + return FTYPE_PPM_ASC; + if (buf[1] == '4') + return FTYPE_PBM_RAW; + if (buf[1] == '5') + return FTYPE_PGM_RAW; + if (buf[1] == '6') + return FTYPE_PPM_RAW; + } + if (buf[0] == 'B' && buf[1] == 'M') + return FTYPE_BMP; + if (!strncmp(reinterpret_cast(&buf[0]), "GIF8", 4)) + return FTYPE_GIF; + if (buf[0] == 0x0a && buf[1] == 0x05 && buf[2] == 0x01 && buf[3] == 0x08) + return FTYPE_PCX; + return FTYPE_UNKNOWN; +} +FILE *OpenFileReadMagic(const char *filename, int *ftype_r) +{ + FILE *f; + + *ftype_r = FTYPE_ERROR; + if ((f = fopen(filename, "rb")) == NULL) + return NULL; + *ftype_r = ReadMagic(f); + if (*ftype_r == FTYPE_ERROR) + { + fclose(f); + return NULL; + } + return f; +} +boolean WriteBytes(FILE *file, void *addr, UInt32 size) +{ + while (size > 0x8000) + { + if (fwrite(addr, 1, 0x8000, file) != 0x8000) + return FALSE; + addr = (char *)addr + 0x8000; + size -= 0x8000; + } + if (fwrite(addr, 1, size, file) != size) + return FALSE; + return TRUE; +} +char *ConvertFilePath(char *filename) +{ + char *cp; + + if (filename == NULL) + ProgError("BUG: cannot convert a NULL pathname"); + for (cp = filename; *cp; cp++) + if (*cp == '/' || *cp == '\\') + { +#ifdef QEU_DOS + *cp = '\\'; +#else + *cp = '/'; +#endif + } + return filename; +} + +/* + * Read the PACK directory into memory. The optional offset to the + * start of the PACK file is given in "offset". The number of files in + * the directory is returned in *dirsize_r. + */ +PACKDirPtr ReadPACKDirectory(FILE *packfile, UInt32 offset, UInt16 *dirsize_r) +{ + PACKDirPtr dir; + UInt32 pos, size; + UInt16 max, i; + + *dirsize_r = 0; + if (packfile == NULL) + return NULL; + if ((fseek(packfile, offset, SEEK_SET) < 0) + || (ReadMagic(packfile) != FTYPE_PACK) + || (ReadInt32(packfile, &pos) == FALSE) + || (ReadInt32(packfile, &size) == FALSE) + || (size == 0L) + || (size / sizeof(struct PACKDirectory) > 65535L) + || (fseek(packfile, offset + pos, SEEK_SET) < 0)) + return NULL; + dir = (PACKDirPtr)__qmalloc(size); + max = (UInt16)(size / sizeof(struct PACKDirectory)); + for (i = 0; i < max; i++) + { + if (ReadBytes(packfile, &dir[i], sizeof(struct PACKDirectory)) == FALSE) + { + free(dir); + return NULL; + } + ConvertFilePath(dir[i].name); + dir[i].offset = SwapInt32(dir[i].offset); + dir[i].size = SwapInt32(dir[i].size); + } + *dirsize_r = max; + return dir; +} + +/* + * Print the contents of the PACK directory in "outf". + */ +void DumpPACKDirectory(FILE *outf, PACKDirPtr dir, UInt16 dirsize) +{ + UInt16 i; + UInt32 sum; + char buf[57]; + + if (outf == NULL || dir == NULL || dirsize == 0) + return; + fprintf(outf, "num offset size file name\n"); + fprintf(outf, " (hex) (dec)\n"); + sum = 0L; + for (i = 0; i < dirsize; i++) + { + if(!strnicmp(dir[i].name, "textures", 8)) + { + strncpy(buf, dir[i].name, 56); + buf[56] = '\0'; + fprintf(outf, "%3u 0x%08lx %6ld %s\n", + i, dir[i].offset, dir[i].size, buf); + sum += dir[i].size; + } + } + fprintf(outf, "\nTotal size for %3u entries: %7lu bytes.\n", dirsize, sum); + fprintf(outf, "Size of the PACK directory: %7lu bytes.\n", + (UInt32)dirsize * (UInt32)sizeof(struct PACKDirectory)); + fprintf(outf, "Total (header + data + dir): %7lu bytes.\n", + 12L + sum + (UInt32)dirsize * (UInt32)sizeof(struct PACKDirectory)); +} + +void ClearFileList(FILELIST **list) +{ + FILELIST *temp; + + while(*list) + { + temp = *list; + *list = (*list)->next; + free(temp); + } +} + +void ClearDirList(DIRLIST **list) +{ + DIRLIST *temp; + + while(*list) + { + temp = *list; + *list = (*list)->next; + free(temp); + } +} + +DIRECTORY *FindPakDir(DIRECTORY *dir, char *name) +{ + DIRECTORY *currentPtr; + + for(currentPtr = dir; currentPtr; currentPtr = currentPtr->next) + { + if(!stricmp(name, currentPtr->name)) + { + return currentPtr; + } + } + return NULL; +} + + +// LoadPK3FileList +// --------------- +// +// This gets passed a file mask which we want to remove as +// we are only interested in the directory name and any given +// extension. Only handles explicit filenames or *.something +// +boolean LoadPK3FileList(FILELIST **filelist, const char *pattern) +{ + char cSearch[WORK_LEN]; + __ConvertDOSToUnixName( cSearch, pattern ); + char cPath[WORK_LEN]; + char cExt[WORK_LEN]; + char cFile[WORK_LEN]; + char cWork[WORK_LEN]; + __ExtractFilePath(pattern, cPath); + __ExtractFileName(pattern, cFile); + __ExtractFileExtension(pattern, cExt); + const char *pCompare = (strnicmp(cFile, "*.", 2) == 0) ? cExt : cFile; + + PK3List *p = g_PK3Files.Next(); + while (p != NULL) + { + // qualify the path + PK3FileInfo *pKey = p->Ptr(); + if (strstr(pKey->m_pName, cPath) && strstr(pKey->m_pName, pCompare)) + { + __ExtractFileName(pKey->m_pName, cWork); + AddToFileListAlphabetized(filelist, cWork, 0, 0, false); + } + p = p->Next(); + } + return (*filelist) != NULL; +} + +boolean GetPackFileList(FILELIST **filelist, char *pattern) +{ + char *str1, *str2; + int i; + DIRECTORY *dummy = paktextures; + FILELIST *temp; + + if (!pakopen) + return false; + + if (g_bPK3) + { + return LoadPK3FileList(filelist, pattern); + } + + str1 = pattern; + + for(i = 0; pattern[i] != '\0'; i++) + { + if(pattern[i] == '\\') + pattern[i] = '/'; + } + + while(strchr(str1, '/')) + { + str2 = strchr(str1, '/'); + *str2++ = '\0'; + dummy = FindPakDir(dummy, str1); + if(!dummy) + return false; + str1 = str2; + } + for(temp = dummy->files; temp; temp=temp->next) + { + AddToFileListAlphabetized(filelist, temp->filename, temp->offset, 0, false); + } + return true; +} + +boolean GetPackTextureDirs(DIRLIST **dirlist) +{ + UInt16 i; + char buf[57]; + + if (!pakopen) + return 1; + + if (g_bPK3) + { + StrList *pl = g_PK3TexturePaths.Next(); + while (pl != NULL) + { + AddToDirListAlphabetized(dirlist, pl->Ref(), 0); + pl = pl->Next(); + } + return true; + } + + for (i = 0; i < dirsize; i++) + { + if(!strnicmp(pakdirptr[i].name, "textures", 8)) + { + strncpy(buf, &(pakdirptr[i].name[9]), 46); + if(strchr(buf, '\\')) + *strchr(buf, '\\') = '\0'; + else if(strchr(buf, '/')) + *strchr(buf, '/') = '\0'; + else + buf[56] = '\0'; + + if(strchr(buf, '.')) + continue; + + AddToDirListAlphabetized(dirlist, buf, 0); + } + } + return true; +} + +boolean AddToDirListAlphabetized(DIRLIST **list, char *dirname, int from) +{ + DIRLIST *currentPtr, *previousPtr, *newPtr; + + strlwr(dirname); + for(currentPtr = *list; currentPtr; currentPtr = currentPtr->next) + { + if(!stricmp(dirname, currentPtr->dirname)) + { + return false; + } + } + previousPtr = NULL; + currentPtr = *list; + + if((newPtr = (DIRLIST *)__qmalloc(sizeof(DIRLIST))) == NULL) + return false; + + strcpy(newPtr->dirname, dirname); + newPtr->from = from; + + while(currentPtr != NULL && stricmp(dirname, currentPtr->dirname) > 0) + { + previousPtr = currentPtr; + currentPtr = currentPtr->next; + } //End while + if(previousPtr == NULL) + { + newPtr->next = *list; + *list = newPtr; + } //End if + else + { + previousPtr->next = newPtr; + newPtr->next = currentPtr; + } //End else + return true; +} + +boolean AddToFileListAlphabetized(FILELIST **list, char *filename, UInt32 offset, UInt32 size, boolean dirs) +{ + FILELIST *currentPtr, *previousPtr, *newPtr; + + for(currentPtr = *list; currentPtr; currentPtr = currentPtr->next) + { + if(!stricmp(filename, currentPtr->filename)) + { + return false; + } + } + previousPtr = NULL; + currentPtr = *list; + + if((newPtr = (FILELIST *)__qmalloc(sizeof(FILELIST))) == NULL) + return false; + + strcpy(newPtr->filename, filename); + newPtr->offset = offset; + newPtr->size = size; + + while(currentPtr != NULL && stricmp(filename, currentPtr->filename) > 0) + { + previousPtr = currentPtr; + currentPtr = currentPtr->next; + } //End while + if(previousPtr == NULL) + { + newPtr->next = *list; + *list = newPtr; + } //End if + else + { + previousPtr->next = newPtr; + newPtr->next = currentPtr; + } //End else + return true; +} + +boolean PakLoadFile(const char *filename, void **bufferptr) +{ + FILELIST *p = NULL; + DIRECTORY *dummy; + void *buffer; + char *str1, *str2; + + if(!pakopen) + return false; + + Str str(filename); + __ConvertDOSToUnixName(str, str); + + dummy = paktextures; + str1 = str; + + while(strchr(str1, '/')) + { + str2 = strchr(str1, '/'); + *str2++ = '\0'; + dummy = FindPakDir(dummy, str1); + if(!dummy) + return false; + str1 = str2; + } + + // FIXME: add error handling routines + for(p = dummy->files; p; p = p->next) + { + if(!stricmp(str1, p->filename)) + { + if (fseek(pakfile[m_nPAKIndex], p->offset, SEEK_SET) < 0) + { + //Sys_Printf("Unexpected EOF in pakfile\n"); + return false; + } + if((buffer = __qmalloc(p->size+5)) == NULL) + //Error("Could not allocate memory"); + + if(fread(buffer, 1, p->size, pakfile[m_nPAKIndex]) != p->size) + { + //Sys_Printf("Error reading %s from pak\n", str1); + free(buffer); + return false; + } + *bufferptr = buffer; + return true; + } + } + return false; +} + +int PakLoadAnyFile(const char *filename, void **bufferptr) +{ + char cWork[WORK_LEN]; + if (g_bPK3) + { + PK3FileInfo *pInfo; + Str strKey; + // need to lookup the file without the base/texture path on it + Str strBase(g_strBasePath); + AddSlash(strBase); + __ConvertDOSToUnixName(cWork, strBase); + Str strFile(filename); + __ConvertDOSToUnixName(strFile, strFile); + strFile.MakeLower(); + strlwr(cWork); + FindReplace(strFile, cWork, ""); + + PK3FileInfo infoFind; + infoFind.m_pName = __StrDup(strFile.GetBuffer()); + PK3List *pList = g_PK3Files.Find(&infoFind); + if (pList) + { + pInfo = pList->Ptr(); + memcpy(pInfo->m_zFile, &pInfo->m_zInfo, sizeof(unz_s)); + if (unzOpenCurrentFile(pInfo->m_zFile) == UNZ_OK) + { + void *buffer = __qblockmalloc(pInfo->m_lSize+1); + int n = unzReadCurrentFile(pInfo->m_zFile , buffer, pInfo->m_lSize); + *bufferptr = buffer; + unzCloseCurrentFile(pInfo->m_zFile); + return n; + } + } +#ifdef LOG_PAKFAIL + sprintf(cWork, "PAK failed on %s\n", filename); + g_LogFile.Log(cWork); +#endif + return -1; + } + + for (int i = 0; i < dirsize; i++) + { + if(!stricmp(filename, pakdirptr[i].name)) + { + if (fseek(pakfile[m_nPAKIndex], pakdirptr[i].offset, SEEK_SET) >= 0) + { + void *buffer = __qmalloc (pakdirptr[i].size+1); + ((char *)buffer)[pakdirptr[i].size] = 0; + if (fread(buffer, 1, pakdirptr[i].size, pakfile[m_nPAKIndex]) == pakdirptr[i].size) + { + *bufferptr = buffer; + return pakdirptr[i].size; + } + } + } + } +#ifdef LOG_PAKFAIL + sprintf(cWork, "PAK failed on %s\n", filename); + g_LogFile.Log(cWork); +#endif + return -1; +} + + + +DIRECTORY *AddPakDir(DIRECTORY **dir, char *name) +{ + DIRECTORY *currentPtr, *previousPtr, *newPtr; + + for(currentPtr = *dir; currentPtr; currentPtr = currentPtr->next) + { + if(!stricmp(name, currentPtr->name)) + { + return currentPtr; + } + } + previousPtr = NULL; + currentPtr = *dir; + + if((newPtr = (DIRECTORY *)__qmalloc(sizeof(DIRECTORY))) == NULL) + return NULL; + + strcpy(newPtr->name, name); + newPtr->files = NULL; + + while(currentPtr != NULL && stricmp(name, currentPtr->name) > 0) + { + previousPtr = currentPtr; + currentPtr = currentPtr->next; + } + if(previousPtr == NULL) + { + newPtr->next = *dir; + *dir = newPtr; + } + else + { + previousPtr->next = newPtr; + newPtr->next = currentPtr; + } + return newPtr; +} + + +// OpenPK3 +// ------- +// Opens a PK3 ( or zip ) file and creates a list of filenames +// and zip info structures +// +boolean OpenPK3(const char *filename) +{ + char cFilename[WORK_LEN]; + char cName[WORK_LEN]; + char cWork[WORK_LEN]; + unz_file_info zInfo; + unzFile *zFile = new unzFile(unzOpen(filename)); + g_zFiles.Add(zFile); + if (zFile != NULL) + { + int nStatus = unzGoToFirstFile(*zFile); + while (nStatus == UNZ_OK) + { + cFilename[0] = '\0'; + unzGetCurrentFileInfo(*zFile, &zInfo, cFilename, WORK_LEN, NULL, 0, NULL, 0); + strlwr(cFilename); + __ConvertDOSToUnixName( cWork, cFilename); + if (strstr(cWork, ".") != NULL) + { + PK3FileInfo *pInfo = new PK3FileInfo(); + pInfo->m_pName = __StrDup(cWork); + memcpy(&pInfo->m_zInfo, (unz_s*)*zFile, sizeof(unz_s)); + pInfo->m_lSize = zInfo.uncompressed_size; + pInfo->m_zFile = *zFile; + g_PK3Files.Add(pInfo); + } + char *p = strstr(cFilename, TEXTURE_PATH); + if (p != NULL) + { + // FIXME: path differences per os ? + // catch solo directory entry + if (strlen(p) > strlen(TEXTURE_PATH) + 1) + { + // skip textures + path seperator + p += strlen(TEXTURE_PATH) + 1; + int nEnd = strcspn(p, PATH_SEPERATORS); + strncpy(cName, p, nEnd); + cName[nEnd] = '\0'; + + boolean bFound = false; + StrList *pl = g_PK3TexturePaths.Next(); + while (pl != NULL) + { + if (strcmpi(pl->Ref(), cName) == 0) + { + // already have this, continue + bFound = true; + break; + } + pl = pl->Next(); + } + if (!bFound) + { + g_PK3TexturePaths.Add(new Str(cName)); + } + } + } + nStatus = unzGoToNextFile(*zFile); + } + } + return (zFile != NULL); +} + +void closePK3(unzFile zf) +{ + unzClose(zf); +} + +void OpenPakFile(const char *filename) +{ + int i; + char *str1, *str2; + DIRECTORY *dummy; + + if(!pakopen) + paktextures = NULL; + + HavePakColormap = false; + + Str strTest(filename); + strTest.MakeLower(); + if (strTest.Find("pk3") >= 0 || strTest.Find("zip") >= 0) + { + pakopen = g_bPK3 = OpenPK3(filename); + return; + } + + + if((pakfile[m_nPAKIndex] = OpenFileReadMagic(filename, &f_type)) == NULL) + { + //FIXME: error routine + //Sys_Printf("ERROR: Could not open %s", filename); + return; + } + if(f_type != FTYPE_PACK) + { + //Sys_Printf("ERROR: %s is not a valid pack file", filename); + if(f_type != FTYPE_ERROR) + fclose(pakfile[m_nPAKIndex]); + return; + } + pakdirptr = ReadPACKDirectory(pakfile[m_nPAKIndex], 0, &dirsize); + if (pakdirptr == NULL) + { + //Sys_Printf("ERROR: Could not read pack directory", filename); + fclose(pakfile[m_nPAKIndex]); + return; + } + if (dirsize == 0) + { + fclose(pakfile[m_nPAKIndex]); + return; + } + for (i = 0; i < dirsize; i++) + { + if(!strnicmp("textures/", pakdirptr[i].name, 9)) + { + dummy = paktextures; + str1 = pakdirptr[i].name+9; + while(strchr(str1, '/')) + { + str2 = strchr(str1, '/'); + *str2++ = '\0'; + dummy = AddPakDir(dummy==paktextures?&paktextures:&dummy, str1); + str1 = str2; + } + + AddToFileListAlphabetized(&(dummy->files), str1, pakdirptr[i].offset, pakdirptr[i].size, true); + } + else if(!strnicmp("pics/colormap.pcx", pakdirptr[i].name, 17)) + { + HavePakColormap = true; + PakColormapOffset = pakdirptr[i].offset; + PakColormapSize = pakdirptr[i].size; + } + } + pakopen = true; + +} + +void ClearPaKDir(DIRECTORY **dir) +{ + DIRECTORY *d1 = *dir, *d2; + + while(d1) + { + ClearFileList(&(d1->files)); + d2 = d1; + d1 = d1->next; + free(d2); + } +} + +void CleanUpPakDirs() +{ + ClearPaKDir(&paktextures); + paktextures = NULL; + dirhead = NULL; + g_PK3TexturePaths.RemoveAll(); + g_PK3Files.RemoveAll(); +} + +void ClosePakFile(void) +{ + if(pakopen) + { + if (g_bPK3) + { + ZFileList *p = g_zFiles.Next(); + while (p != NULL) + { + unzFile uz = p->Ref(); + closePK3(uz); + p = p->Next(); + } + } + else + { + fclose(pakfile[m_nPAKIndex]); + } + } + pakopen = false; + CleanUpPakDirs(); +} + + +void WINAPI InitPakFile(const char * pBasePath, const char *pName) +{ + m_nPAKIndex = 0; + pakopen = false; + paktextures = NULL; + strcpy(g_strBasePath, pBasePath); + if (pName == NULL) + { + char cWork[WORK_LEN]; + Str strPath(pBasePath); + AddSlash(strPath); + strPath += "*.pk3"; + bool bGo = true; + struct _finddata_t fileinfo; + int handle = _findfirst (strPath, &fileinfo); + if (handle != -1) + { + do + { + sprintf(cWork, "%s\\%s", pBasePath, fileinfo.name); + OpenPakFile(cWork); + } while (_findnext( handle, &fileinfo ) != -1); + _findclose (handle); + } + } + else + { + OpenPakFile(pName); + } +} + diff --git a/utils/Libs/pak/unzip.cpp b/utils/Libs/pak/unzip.cpp new file mode 100644 index 0000000..3d07d9b --- /dev/null +++ b/utils/Libs/pak/unzip.cpp @@ -0,0 +1,4546 @@ +/***************************************************************************** + * name: unzip.c + * + * desc: IO on .zip files using portions of zlib + * + * $Archive: /StarTrek/Utils4/Libs/pak/unzip.cpp $ + * $Author: Jscott $ + * $Revision: 2 $ + * $Modtime: 11/01/00 9:37 $ + * $Date: 24/05/00 13:45 $ + * + *****************************************************************************/ + +#include +#include +#include +#include "unzip.h" +//#include "cmdlib.h" + +/* unzip.h -- IO for uncompress .zip files using zlib + Version 0.15 beta, Mar 19th, 1998, + + Copyright (C) 1998 Gilles Vollant + + This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g + WinZip, InfoZip tools and compatible. + Encryption and multi volume ZipFile (span) are not supported. + Old compressions used by old PKZip 1.x are not supported + + THIS IS AN ALPHA VERSION. AT THIS STAGE OF DEVELOPPEMENT, SOMES API OR STRUCTURE + CAN CHANGE IN FUTURE VERSION !! + I WAIT FEEDBACK at mail info@winimage.com + Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution + + Condition of use and distribution are the same than zlib : + + 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. + + +*/ +/* for more info about .ZIP format, see + ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip + PkWare has also a specification at : + ftp://ftp.pkware.com/probdesc.zip */ + +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.1.3, July 9th, 1998 + + Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler + + 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. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-1998 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: unzip.cpp,v 1.1.1.3 2000/01/11 16:37:27 ttimo Exp $ */ + +#ifndef _ZCONF_H +#define _ZCONF_H + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +#define OF(args) args +#endif + +typedef unsigned char Byte; /* 8 bits */ +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ +typedef Byte *voidp; + +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#endif /* _ZCONF_H */ + +#define ZLIB_VERSION "1.1.3" + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +/* Allowed flush values; see deflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 +/* Possible values of the data_type field */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +const char * zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +int deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +int deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + the compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + 0.1% larger than avail_in plus 12 bytes. If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so (that is, total_in bytes). + + deflate() may update data_type if it can make a good guess about + the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). +*/ + + +int deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +int inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +int inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may some + introduce some output latency (reading input without producing any output) + except when forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much + output as possible to the output buffer. The flushing behavior of inflate is + not specified for values of the flush parameter other than Z_SYNC_FLUSH + and Z_FINISH, but the current implementation actually flushes as much output + as possible anyway. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster routine + may be used for the single inflate() call. + + If a preset dictionary is needed at this point (see inflateSetDictionary + below), inflate sets strm-adler to the adler32 checksum of the + dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise + it sets strm->adler to the adler32 checksum of all output produced + so (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or + an error code as described below. At the end of the stream, inflate() + checks that its computed adler32 checksum is equal to that saved by the + compressor and returns Z_STREAM_END only if the checksum is correct. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect + adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent + (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if no progress is possible or if there was not + enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR + case, the application may then call inflateSync to look for a good + compression block. +*/ + + +int inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +int deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match). Filtered data consists mostly of small values with a + somewhat random distribution. In this case, the compression algorithm is + tuned to compress them better. The effect of Z_FILTERED is to force more + Huffman coding and less string matching; it is somewhat intermediate + between Z_DEFAULT and Z_HUFFMAN_ONLY. The strategy parameter only affects + the compression ratio but not the correctness of the compressed output even + if it is not set appropriately. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +int deflateSetDictionary OF((z_streamp strm, + const Byte *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. + + Upon return of this function, strm->adler is set to the Adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The Adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +int deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +int deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +int deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +/* +int inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. If a compressed stream with a larger window size is given as + input, inflate() will return with the error code Z_DATA_ERROR instead of + trying to allocate a larger window. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a negative + memLevel). msg is set to null if there is no error message. inflateInit2 + does not perform any decompression apart from reading the zlib header if + present: this will be done by inflate(). (So next_in and avail_in may be + modified, but next_out and avail_out are unchanged.) +*/ + +int inflateSetDictionary OF((z_streamp strm, + const Byte *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate + if this call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the Adler32 value returned by this call of + inflate. The compressor and decompressor must use exactly the same + dictionary (see deflateSetDictionary). + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect Adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +int inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +int inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +int compress OF((Byte *dest, uLong *destLen, + const Byte *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least 0.1% larger than + sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +int compress2 OF((Byte *dest, uLong *destLen, + const Byte *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +int uncompress OF((Byte *dest, uLong *destLen, + const Byte *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ + + +typedef voidp gzFile; + +gzFile gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h". (See the description + of deflateInit2 for more information about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +gzFile gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +int gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +int gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +int gzwrite OF((gzFile file, + const voidp buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +int gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). +*/ + +int gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +char * gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +int gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +int gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +int gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +long gzseek OF((gzFile file, + long offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +int gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +long gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +int gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +int gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +const char * gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +uLong adler32 OF((uLong adler, const Byte *buf, uInt len)); + +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +uLong crc32 OF((uLong crc, const Byte *buf, uInt len)); +/* + Update a running crc with the bytes buf[0..len-1] and return the updated + crc. If buf is NULL, this function returns the required initial value + for the crc. Pre- and post-conditioning (one's complement) is performed + within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +// private stuff to not include cmdlib.h +/* +============================================================================ + + BYTE ORDER FUNCTIONS + +============================================================================ +*/ + +#ifdef _SGI_SOURCE +#define __BIG_ENDIAN__ +#endif + +#ifdef __BIG_ENDIAN__ + +short __LittleShort (short l) +{ + byte b1,b2; + + b1 = l&255; + b2 = (l>>8)&255; + + return (b1<<8) + b2; +} + +short __BigShort (short l) +{ + return l; +} + + +int __LittleLong (int l) +{ + byte b1,b2,b3,b4; + + b1 = l&255; + b2 = (l>>8)&255; + b3 = (l>>16)&255; + b4 = (l>>24)&255; + + return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; +} + +int __BigLong (int l) +{ + return l; +} + + +float __LittleFloat (float l) +{ + union {byte b[4]; float f;} in, out; + + in.f = l; + out.b[0] = in.b[3]; + out.b[1] = in.b[2]; + out.b[2] = in.b[1]; + out.b[3] = in.b[0]; + + return out.f; +} + +float __BigFloat (float l) +{ + return l; +} + + +#else + + +short __BigShort (short l) +{ + byte b1,b2; + + b1 = l&255; + b2 = (l>>8)&255; + + return (b1<<8) + b2; +} + +short __LittleShort (short l) +{ + return l; +} + + +int __BigLong (int l) +{ + byte b1,b2,b3,b4; + + b1 = l&255; + b2 = (l>>8)&255; + b3 = (l>>16)&255; + b4 = (l>>24)&255; + + return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; +} + +int __LittleLong (int l) +{ + return l; +} + +float __BigFloat (float l) +{ + union {byte b[4]; float f;} in, out; + + in.f = l; + out.b[0] = in.b[3]; + out.b[1] = in.b[2]; + out.b[2] = in.b[1]; + out.b[3] = in.b[0]; + + return out.f; +} + +float __LittleFloat (float l) +{ + return l; +} + + + +#endif + + + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +int deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +int inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +int deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +int inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) + + +const char * zError OF((int err)); +int inflateSyncPoint OF((z_streamp z)); +const uLong * get_crc_table OF((void)); + +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; + +extern const char *z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + + /* Common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#ifdef HAVE_STRERROR + extern char *strerror OF((int)); +# define zstrerror(errnum) strerror(errnum) +#else +# define zstrerror(errnum) "" +#endif + +#define zmemcpy memcpy +#define zmemcmp memcmp +#define zmemzero(dest, len) memset(dest, 0, len) + +/* Diagnostic functions */ +#ifdef _ZIP_DEBUG_ + int z_verbose = 0; +# define Assert(cond,msg) assert(cond); + //{if(!(cond)) Sys_Error(msg);} +# define Trace(x) {if (z_verbose>=0) Sys_Error x ;} +# define Tracev(x) {if (z_verbose>0) Sys_Error x ;} +# define Tracevv(x) {if (z_verbose>1) Sys_Error x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) Sys_Error x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) Sys_Error x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +typedef uLong (*check_func) OF((uLong check, const Byte *buf, uInt len)); +voidp zcalloc OF((voidp opaque, unsigned items, unsigned size)); +void zcfree OF((voidp opaque, voidp ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidp)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + + +#if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) && \ + !defined(CASESENSITIVITYDEFAULT_NO) +#define CASESENSITIVITYDEFAULT_NO +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (65536) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ + +/* +static int unzlocal_getByte(FILE *fin,int *pi) +{ + unsigned char c; + int err = fread(&c, 1, 1, fin); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ferror(fin)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} +*/ + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +static int unzlocal_getShort (FILE* fin, uLong *pX) +{ + short v; + + fread( &v, sizeof(v), 1, fin ); + + *pX = __LittleShort( v); + return UNZ_OK; + +/* + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +*/ +} + +static int unzlocal_getLong (FILE *fin, uLong *pX) +{ + int v; + + fread( &v, sizeof(v), 1, fin ); + + *pX = __LittleLong( v); + return UNZ_OK; + +/* + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +*/ +} + + +/* My own strcmpi / strcasecmp */ +static int strcmpcasenosensitive_internal (const char* fileName1,const char* fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int unzStringFileNameCompare (const char* fileName1,const char* fileName2,int iCaseSensitivity) +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#define BUFREADCOMMENT (0x400) + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +static uLong unzlocal_SearchCentralDir(FILE *fin) +{ + unsigned char* buf; + uLong uSizeFile; + uLong uBackRead; + uLong uMaxBack=0xffff; /* maximum size of global comment */ + uLong uPosFound=0; + + if (fseek(fin,0,SEEK_END) != 0) + return 0; + + + uSizeFile = ftell( fin ); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)malloc(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (fseek(fin,uReadPos,SEEK_SET)!=0) + break; + + if (fread(buf,(uInt)uReadSize,1,fin)!=1) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + free(buf); + return uPosFound; +} + +extern unzFile unzReOpen (const char* path, unzFile file) +{ + unz_s *s; + FILE * fin; + + fin=fopen(path,"rb"); + if (fin==NULL) + return NULL; + + s=(unz_s*)malloc(sizeof(unz_s)); + memcpy(s, (unz_s*)file, sizeof(unz_s)); + + s->file = fin; + return (unzFile)s; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib109.zip" or on an Unix computer + "zlib/zlib109.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +extern unzFile unzOpen (const char* path) +{ + unz_s us; + unz_s *s; + uLong central_pos,uL; + FILE * fin ; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + uLong number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + fin=fopen(path,"rb"); + if (fin==NULL) + return NULL; + + central_pos = unzlocal_SearchCentralDir(fin); + if (central_pos==0) + err=UNZ_ERRNO; + + if (fseek(fin,central_pos,SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unzlocal_getLong(fin,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unzlocal_getShort(fin,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unzlocal_getShort(fin,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unzlocal_getShort(fin,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir */ + if (unzlocal_getShort(fin,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unzlocal_getLong(fin,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unzlocal_getLong(fin,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* zipfile comment length */ + if (unzlocal_getShort(fin,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((central_pospfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + fclose(s->file); + free(s); + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int unzGetGlobalInfo (unzFile file,unz_global_info *pglobal_info) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +static void unzlocal_DosDateToTmuDate (uLong ulDosDate, tm_unz* ptm) +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +static int unzlocal_GetCurrentFileInfoInternal (unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize) +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (fseek(s->file,s->pos_in_central_dir+s->byte_before_the_zipfile,SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(s->file,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (fread(szFileName,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extrafile,lSeek,SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (fread(extraField,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentfile,lSeek,SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (fread(szComment,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int unzGetCurrentFileInfo ( unzFile file, unz_file_info *pfile_info, + char *szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char *szComment, uLong commentBufferSize) +{ + return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int unzGoToFirstFile (unzFile file) +{ + int err=UNZ_OK; + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int unzGoToNextFile (unzFile file) +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzipStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity) +{ + unz_s* s; + int err; + + + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + return err; +} + + +/* + Read the static header of the current zipfile + Check the coherency of the static header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in static header + (filename and size of extra field data) +*/ +static int unzlocal_CheckCurrentFileCoherencyHeader (unz_s* s, uInt* piSizeVar, + uLong *poffset_local_extrafield, + uInt *psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (fseek(s->file,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unzlocal_getShort(s->file,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(s->file,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(s->file,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int unzOpenCurrentFile (unzFile file) +{ + int err=UNZ_OK; + int Store; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; /* offset of the static extra field */ + uInt size_local_extrafield; /* size of the static extra field */ + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*) + malloc(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)malloc(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + free(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if ((s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + Store = s->cur_file_info.compression_method==0; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->file=s->file; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if (!Store) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidp)0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + + s->pfile_in_zip_read = pfile_in_zip_read_info; + return UNZ_OK; +} + + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int unzReadCurrentFile (unzFile file, void *buf, unsigned len) +{ + int err=UNZ_OK; + uInt iRead = 0; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->read_buffer == NULL)) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Byte*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if (len>pfile_in_zip_read_info->rest_read_uncompressed) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (s->cur_file_info.compressed_size == pfile_in_zip_read_info->rest_read_compressed) + if (fseek(pfile_in_zip_read_info->file, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile,SEEK_SET)!=0) + return UNZ_ERRNO; + if (fread(pfile_in_zip_read_info->read_buffer,uReadThis,1, + pfile_in_zip_read_info->file)!=1) + return UNZ_ERRNO; + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Byte*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if (pfile_in_zip_read_info->compression_method==0) + { + uInt uDoCopy,i ; + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;istream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { + uLong uTotalOutBefore,uTotalOutAfter; + const Byte *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern long unztell (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (long)pfile_in_zip_read_info->stream.total_out; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int unzeof (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the static-header version of the extra field (sometimes, there is + more info in the static-header version than in the central-header) + + if buf==NULL, it return the size of the static extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int unzGetLocalExtrafield (unzFile file,void *buf,unsigned len) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (fseek(pfile_in_zip_read_info->file, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield,SEEK_SET)!=0) + return UNZ_ERRNO; + + if (fread(buf,(uInt)size_to_read,1,pfile_in_zip_read_info->file)!=1) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzipOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + free(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + free(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int unzGetGlobalComment (unzFile file, char *szComment, uLong uSizeBuf) +{ + unz_s* s; + uLong uReadThis ; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (fseek(s->file,s->central_pos+22,SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (fread(szComment,(uInt)uReadThis,1,s->file)!=1) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-1998 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: unzip.cpp,v 1.1.1.3 2000/01/11 16:37:27 ttimo Exp $ */ + +#ifdef DYNAMIC_CRC_TABLE + +static int crc_table_empty = 1; +static uLong crc_table[256]; +static void make_crc_table OF((void)); + +/* + Generate a table for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The table is simply the CRC of all possible eight bit values. This is all + the information needed to generate CRC's on data a byte at a time for all + combinations of CRC register values and incoming bytes. +*/ +static void make_crc_table() +{ + uLong c; + int n, k; + uLong poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static const Byte p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* make exclusive-or pattern from polynomial (0xedb88320L) */ + poly = 0L; + for (n = 0; n < sizeof(p)/sizeof(Byte); n++) + poly |= 1L << (31 - p[n]); + + for (n = 0; n < 256; n++) + { + c = (uLong)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[n] = c; + } + crc_table_empty = 0; +} +#else +/* ======================================================================== + * Table of CRC-32's of all single-byte values (made by make_crc_table) + */ +static const uLong crc_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; +#endif + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const uLong * get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) make_crc_table(); +#endif + return (const uLong *)crc_table; +} + +/* ========================================================================= */ +#define DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); +#define DO2(buf) DO1(buf); DO1(buf); +#define DO4(buf) DO2(buf); DO2(buf); +#define DO8(buf) DO4(buf); DO4(buf); + +/* ========================================================================= */ +uLong crc32(uLong crc, const Byte *buf, uInt len) +{ + if (buf == Z_NULL) return 0L; +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif + crc = crc ^ 0xffffffffL; + while (len >= 8) + { + DO8(buf); + len -= 8; + } + if (len) do { + DO1(buf); + } while (--len); + return crc ^ 0xffffffffL; +} + +/* infblock.h -- header to use infblock.c + * Copyright (C) 1995-1998 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +struct inflate_blocks_state; +typedef struct inflate_blocks_state inflate_blocks_statef; + +extern inflate_blocks_statef * inflate_blocks_new OF(( + z_streamp z, + check_func c, /* check function */ + uInt w)); /* window size */ + +extern int inflate_blocks OF(( + inflate_blocks_statef *, + z_streamp , + int)); /* initial return code */ + +extern void inflate_blocks_reset OF(( + inflate_blocks_statef *, + z_streamp , + uLong *)); /* check value on output */ + +extern int inflate_blocks_free OF(( + inflate_blocks_statef *, + z_streamp)); + +extern void inflate_set_dictionary OF(( + inflate_blocks_statef *s, + const Byte *d, /* dictionary */ + uInt n)); /* dictionary length */ + +extern int inflate_blocks_sync_point OF(( + inflate_blocks_statef *s)); + +/* simplify the use of the inflate_huft type with some defines */ +#define exop word.what.Exop +#define bits word.what.Bits + +/* Table for deflate from PKZIP's appnote.txt. */ +static const uInt border[] = { /* Order of the bit length code lengths */ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-1998 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Huffman code lookup table entry--this entry is four bytes for machines + that have 16-bit pointers (e.g. PC's in the small or medium model). */ + +typedef struct inflate_huft_s inflate_huft; + +struct inflate_huft_s { + union { + struct { + Byte Exop; /* number of extra bits or operation */ + Byte Bits; /* number of bits in this code or subcode */ + } what; + uInt pad; /* pad structure to a power of 2 (4 bytes for */ + } word; /* 16-bit, 8 bytes for 32-bit int's) */ + uInt base; /* literal, length base, distance base, + or table offset */ +}; + +/* Maximum size of dynamic tree. The maximum found in a long but non- + exhaustive search was 1004 huft structures (850 for length/literals + and 154 for distances, the latter actually the result of an + exhaustive search). The actual maximum is not known, but the + value below is more than safe. */ +#define MANY 1440 + +extern int inflate_trees_bits OF(( + uInt *, /* 19 code lengths */ + uInt *, /* bits tree desired/actual depth */ + inflate_huft * *, /* bits tree result */ + inflate_huft *, /* space for trees */ + z_streamp)); /* for messages */ + +extern int inflate_trees_dynamic OF(( + uInt, /* number of literal/length codes */ + uInt, /* number of distance codes */ + uInt *, /* that many (total) code lengths */ + uInt *, /* literal desired/actual bit depth */ + uInt *, /* distance desired/actual bit depth */ + inflate_huft * *, /* literal/length tree result */ + inflate_huft * *, /* distance tree result */ + inflate_huft *, /* space for trees */ + z_streamp)); /* for messages */ + +extern int inflate_trees_fixed OF(( + uInt *, /* literal desired/actual bit depth */ + uInt *, /* distance desired/actual bit depth */ + inflate_huft * *, /* literal/length tree result */ + inflate_huft * *, /* distance tree result */ + z_streamp)); /* for memory allocation */ + + +/* infcodes.h -- header to use infcodes.c + * Copyright (C) 1995-1998 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +struct inflate_codes_state; +typedef struct inflate_codes_state inflate_codes_statef; + +extern inflate_codes_statef *inflate_codes_new OF(( + uInt, uInt, + inflate_huft *, inflate_huft *, + z_streamp )); + +extern int inflate_codes OF(( + inflate_blocks_statef *, + z_streamp , + int)); + +extern void inflate_codes_free OF(( + inflate_codes_statef *, + z_streamp )); + +/* infutil.h -- types and macros common to blocks and codes + * Copyright (C) 1995-1998 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +#ifndef _INFUTIL_H +#define _INFUTIL_H + +typedef enum { + TYPE, /* get type bits (3, including end bit) */ + LENS, /* get lengths for stored */ + STORED, /* processing stored block */ + TABLE, /* get table lengths */ + BTREE, /* get bit lengths tree for a dynamic block */ + DTREE, /* get length, distance trees for a dynamic block */ + CODES, /* processing fixed or dynamic block */ + DRY, /* output remaining window bytes */ + DONE, /* finished last block, done */ + BAD} /* got a data error--stuck here */ +inflate_block_mode; + +/* inflate blocks semi-private state */ +struct inflate_blocks_state { + + /* mode */ + inflate_block_mode mode; /* current inflate_block mode */ + + /* mode dependent information */ + union { + uInt left; /* if STORED, bytes left to copy */ + struct { + uInt table; /* table lengths (14 bits) */ + uInt index; /* index into blens (or border) */ + uInt *blens; /* bit lengths of codes */ + uInt bb; /* bit length tree depth */ + inflate_huft *tb; /* bit length decoding tree */ + } trees; /* if DTREE, decoding info for trees */ + struct { + inflate_codes_statef + *codes; + } decode; /* if CODES, current state */ + } sub; /* submode */ + uInt last; /* true if this block is the last block */ + + /* mode independent information */ + uInt bitk; /* bits in bit buffer */ + uLong bitb; /* bit buffer */ + inflate_huft *hufts; /* single malloc for tree space */ + Byte *window; /* sliding window */ + Byte *end; /* one byte after sliding window */ + Byte *read; /* window read pointer */ + Byte *write; /* window write pointer */ + check_func checkfn; /* check function */ + uLong check; /* check on output */ + +}; + + +/* defines for inflate input/output */ +/* update pointers and return */ +#define UPDBITS {s->bitb=b;s->bitk=k;} +#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;} +#define UPDOUT {s->write=q;} +#define UPDATE {UPDBITS UPDIN UPDOUT} +#define LEAVE {UPDATE return inflate_flush(s,z,r);} +/* get bytes and bits */ +#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} +#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} +#define NEXTBYTE (n--,*p++) +#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} +/* output bytes */ +#define WAVAIL (uInt)(qread?s->read-q-1:s->end-q) +#define LOADOUT {q=s->write;m=(uInt)WAVAIL;} +#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}} +#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} +#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} +#define OUTBYTE(a) {*q++=(Byte)(a);m--;} +/* load static pointers */ +#define LOAD {LOADIN LOADOUT} + +/* masks for lower bits (size given to avoid silly warnings with Visual C++) */ +extern uInt inflate_mask[17]; + +/* copy as much as possible from the sliding window to the output area */ +extern int inflate_flush OF(( + inflate_blocks_statef *, + z_streamp , + int)); + +#endif + + +/* + Notes beyond the 1.93a appnote.txt: + + 1. Distance pointers never point before the beginning of the output + stream. + 2. Distance pointers can point back across blocks, up to 32k away. + 3. There is an implied maximum of 7 bits for the bit length table and + 15 bits for the actual data. + 4. If only one code exists, then it is encoded using one bit. (Zero + would be more efficient, but perhaps a little confusing.) If two + codes exist, they are coded using one bit each (0 and 1). + 5. There is no way of sending zero distance codes--a dummy must be + sent if there are none. (History: a pre 2.0 version of PKZIP would + store blocks with no distance codes, but this was discovered to be + too harsh a criterion.) Valid only for 1.93a. 2.04c does allow + zero distance codes, which is sent as one code of zero bits in + length. + 6. There are up to 286 literal/length codes. Code 256 represents the + end-of-block. Note however that the static length tree defines + 288 codes just to fill out the Huffman codes. Codes 286 and 287 + cannot be used though, since there is no length base or extra bits + defined for them. Similarily, there are up to 30 distance codes. + However, static trees define 32 codes (all 5 bits) to fill out the + Huffman codes, but the last two had better not show up in the data. + 7. Unzip can check dynamic Huffman blocks for complete code sets. + The exception is that a single code would not be complete (see #4). + 8. The five bits following the block type is really the number of + literal codes sent minus 257. + 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits + (1+6+6). Therefore, to output three times the length, you output + three codes (1+1+1), whereas to output four times the same length, + you only need two codes (1+3). Hmm. + 10. In the tree reconstruction algorithm, Code = Code + Increment + only if BitLength(i) is not zero. (Pretty obvious.) + 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) + 12. Note: length code 284 can represent 227-258, but length code 285 + really is 258. The last length deserves its own, short code + since it gets used a lot in very redundant files. The length + 258 is special since 258 - 3 (the min match length) is 255. + 13. The literal/length and distance code bit lengths are read as a + single stream of lengths. It is possible (and advantageous) for + a repeat code (16, 17, or 18) to go across the boundary between + the two sets of lengths. + */ + + +void inflate_blocks_reset(inflate_blocks_statef *s, z_streamp z, uLong *c) +{ + if (c != Z_NULL) + *c = s->check; + if (s->mode == BTREE || s->mode == DTREE) + ZFREE(z, s->sub.trees.blens); + if (s->mode == CODES) + inflate_codes_free(s->sub.decode.codes, z); + s->mode = TYPE; + s->bitk = 0; + s->bitb = 0; + s->read = s->write = s->window; + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(0L, (const Byte *)Z_NULL, 0); + Tracev(("inflate: blocks reset\n")); +} + + +inflate_blocks_statef *inflate_blocks_new(z_streamp z, check_func c, uInt w) +{ + inflate_blocks_statef *s; + + if ((s = (inflate_blocks_statef *)ZALLOC + (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) + return s; + if ((s->hufts = + (inflate_huft *)ZALLOC(z, sizeof(inflate_huft), MANY)) == Z_NULL) + { + ZFREE(z, s); + return Z_NULL; + } + if ((s->window = (Byte *)ZALLOC(z, 1, w)) == Z_NULL) + { + ZFREE(z, s->hufts); + ZFREE(z, s); + return Z_NULL; + } + s->end = s->window + w; + s->checkfn = c; + s->mode = TYPE; + Tracev(("inflate: blocks allocated\n")); + inflate_blocks_reset(s, z, Z_NULL); + return s; +} + + +int inflate_blocks(inflate_blocks_statef *s, z_streamp z, int r) +{ + uInt t; /* temporary storage */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Byte *p; /* input data pointer */ + uInt n; /* bytes available there */ + Byte *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + + /* copy input/output information to locals (UPDATE macro restores) */ + LOAD + + /* process input based on current state */ + while (1) switch (s->mode) + { + case TYPE: + NEEDBITS(3) + t = (uInt)b & 7; + s->last = t & 1; + switch (t >> 1) + { + case 0: /* stored */ + Tracev(("inflate: stored block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + t = k & 7; /* go to byte boundary */ + DUMPBITS(t) + s->mode = LENS; /* get length of stored block */ + break; + case 1: /* fixed */ + Tracev(("inflate: fixed codes block%s\n", + s->last ? " (last)" : "")); + { + uInt bl, bd; + inflate_huft *tl, *td; + + inflate_trees_fixed(&bl, &bd, &tl, &td, z); + s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); + if (s->sub.decode.codes == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + } + DUMPBITS(3) + s->mode = CODES; + break; + case 2: /* dynamic */ + Tracev(("inflate: dynamic codes block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + s->mode = TABLE; + break; + case 3: /* illegal */ + DUMPBITS(3) + s->mode = BAD; + z->msg = (char*)"invalid block type"; + r = Z_DATA_ERROR; + LEAVE + } + break; + case LENS: + NEEDBITS(32) + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + s->mode = BAD; + z->msg = (char*)"invalid stored block lengths"; + r = Z_DATA_ERROR; + LEAVE + } + s->sub.left = (uInt)b & 0xffff; + b = k = 0; /* dump bits */ + Tracev(("inflate: stored length %u\n", s->sub.left)); + s->mode = s->sub.left ? STORED : (s->last ? DRY : TYPE); + break; + case STORED: + if (n == 0) + LEAVE + NEEDOUT + t = s->sub.left; + if (t > n) t = n; + if (t > m) t = m; + zmemcpy(q, p, t); + p += t; n -= t; + q += t; m -= t; + if ((s->sub.left -= t) != 0) + break; + Tracev(("inflate: stored end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + s->mode = s->last ? DRY : TYPE; + break; + case TABLE: + NEEDBITS(14) + s->sub.trees.table = t = (uInt)b & 0x3fff; +#ifndef PKZIP_BUG_WORKAROUND + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + s->mode = BAD; + z->msg = (char*)"too many length or distance symbols"; + r = Z_DATA_ERROR; + LEAVE + } +#endif + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if ((s->sub.trees.blens = (uInt*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + DUMPBITS(14) + s->sub.trees.index = 0; + Tracev(("inflate: table sizes ok\n")); + s->mode = BTREE; + case BTREE: + while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) + { + NEEDBITS(3) + s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; + DUMPBITS(3) + } + while (s->sub.trees.index < 19) + s->sub.trees.blens[border[s->sub.trees.index++]] = 0; + s->sub.trees.bb = 7; + t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, + &s->sub.trees.tb, s->hufts, z); + if (t != Z_OK) + { + ZFREE(z, s->sub.trees.blens); + r = t; + if (r == Z_DATA_ERROR) + s->mode = BAD; + LEAVE + } + s->sub.trees.index = 0; + Tracev(("inflate: bits tree ok\n")); + s->mode = DTREE; + case DTREE: + while (t = s->sub.trees.table, + s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) + { + inflate_huft *h; + uInt i, j, c; + + t = s->sub.trees.bb; + NEEDBITS(t) + h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); + t = h->bits; + c = h->base; + if (c < 16) + { + DUMPBITS(t) + s->sub.trees.blens[s->sub.trees.index++] = c; + } + else /* c == 16..18 */ + { + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + NEEDBITS(t + i) + DUMPBITS(t) + j += (uInt)b & inflate_mask[i]; + DUMPBITS(i) + i = s->sub.trees.index; + t = s->sub.trees.table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + ZFREE(z, s->sub.trees.blens); + s->mode = BAD; + z->msg = (char*)"invalid bit length repeat"; + r = Z_DATA_ERROR; + LEAVE + } + c = c == 16 ? s->sub.trees.blens[i - 1] : 0; + do { + s->sub.trees.blens[i++] = c; + } while (--j); + s->sub.trees.index = i; + } + } + s->sub.trees.tb = Z_NULL; + { + uInt bl, bd; + inflate_huft *tl, *td; + inflate_codes_statef *c; + + bl = 9; /* must be <= 9 for lookahead assumptions */ + bd = 6; /* must be <= 9 for lookahead assumptions */ + t = s->sub.trees.table; + t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), + s->sub.trees.blens, &bl, &bd, &tl, &td, + s->hufts, z); + ZFREE(z, s->sub.trees.blens); + if (t != Z_OK) + { + if (t == (uInt)Z_DATA_ERROR) + s->mode = BAD; + r = t; + LEAVE + } + Tracev(("inflate: trees ok\n")); + if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.decode.codes = c; + } + s->mode = CODES; + case CODES: + UPDATE + if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) + return inflate_flush(s, z, r); + r = Z_OK; + inflate_codes_free(s->sub.decode.codes, z); + LOAD + Tracev(("inflate: codes end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + if (!s->last) + { + s->mode = TYPE; + break; + } + s->mode = DRY; + case DRY: + FLUSH + if (s->read != s->write) + LEAVE + s->mode = DONE; + case DONE: + r = Z_STREAM_END; + LEAVE + case BAD: + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +int inflate_blocks_free(inflate_blocks_statef *s, z_streamp z) +{ + inflate_blocks_reset(s, z, Z_NULL); + ZFREE(z, s->window); + ZFREE(z, s->hufts); + ZFREE(z, s); + Tracev(("inflate: blocks freed\n")); + return Z_OK; +} + + +void inflate_set_dictionary(inflate_blocks_statef *s, const Byte *d, uInt n) +{ + zmemcpy(s->window, d, n); + s->read = s->write = s->window + n; +} + + +/* Returns true if inflate is currently at the end of a block generated + * by Z_SYNC_FLUSH or Z_FULL_FLUSH. + * IN assertion: s != Z_NULL + */ +int inflate_blocks_sync_point(inflate_blocks_statef *s) +{ + return s->mode == LENS; +} + + +/* And'ing with mask[n] masks the lower n bits */ +uInt inflate_mask[17] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + + +/* copy as much as possible from the sliding window to the output area */ +int inflate_flush(inflate_blocks_statef *s, z_streamp z, int r) +{ + uInt n; + Byte *p; + Byte *q; + + /* static copies of source and destination pointers */ + p = z->next_out; + q = s->read; + + /* compute number of bytes to copy as as end of window */ + n = (uInt)((q <= s->write ? s->write : s->end) - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + /* update counters */ + z->avail_out -= n; + z->total_out += n; + + /* update check information */ + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + /* copy as as end of window */ + zmemcpy(p, q, n); + p += n; + q += n; + + /* see if more to copy at beginning of window */ + if (q == s->end) + { + /* wrap pointers */ + q = s->window; + if (s->write == s->end) + s->write = s->window; + + /* compute bytes to copy */ + n = (uInt)(s->write - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + /* update counters */ + z->avail_out -= n; + z->total_out += n; + + /* update check information */ + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + /* copy */ + zmemcpy(p, q, n); + p += n; + q += n; + } + + /* update pointers */ + z->next_out = p; + s->read = q; + + /* done */ + return r; +} + +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-1998 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +const char inflate_copyright[] = + " inflate 1.1.3 Copyright 1995-1998 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* simplify the use of the inflate_huft type with some defines */ +#define exop word.what.Exop +#define bits word.what.Bits + + +static int huft_build OF(( + uInt *, /* code lengths in bits */ + uInt, /* number of codes */ + uInt, /* number of "simple" codes */ + const uInt *, /* list of base values for non-simple codes */ + const uInt *, /* list of extra bits for non-simple codes */ + inflate_huft **, /* result: starting table */ + uInt *, /* maximum lookup bits (returns actual) */ + inflate_huft *, /* space for trees */ + uInt *, /* hufts used in space */ + uInt * )); /* space for values */ + +/* Tables for deflate from PKZIP's appnote.txt. */ +static const uInt cplens[31] = { /* Copy lengths for literal codes 257..285 */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + /* see note #13 above about 258 */ +static const uInt cplext[31] = { /* Extra bits for literal codes 257..285 */ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; /* 112==invalid */ +static const uInt cpdist[30] = { /* Copy offsets for distance codes 0..29 */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +static const uInt cpdext[30] = { /* Extra bits for distance codes */ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + +/* + Huffman code decoding is performed using a multi-level table lookup. + The fastest way to decode is to simply build a lookup table whose + size is determined by the longest code. However, the time it takes + to build this table can also be a factor if the data being decoded + is not very long. The most common codes are necessarily the + shortest codes, so those codes dominate the decoding time, and hence + the speed. The idea is you can have a shorter table that decodes the + shorter, more probable codes, and then point to subsidiary tables for + the longer codes. The time it costs to decode the longer codes is + then traded against the time it takes to make longer tables. + + This results of this trade are in the variables lbits and dbits + below. lbits is the number of bits the first level table for literal/ + length codes can decode in one step, and dbits is the same thing for + the distance codes. Subsequent tables are also less than or equal to + those sizes. These values may be adjusted either when all of the + codes are shorter than that, in which case the longest code length in + bits is used, or when the shortest code is *longer* than the requested + table size, in which case the length of the shortest code in bits is + used. + + There are two different values for the two tables, since they code a + different number of possibilities each. The literal/length table + codes 286 possible values, or in a flat code, a little over eight + bits. The distance table codes 30 possible values, or a little less + than five bits, flat. The optimum values for speed end up being + about one bit more than those, so lbits is 8+1 and dbits is 5+1. + The optimum values may differ though from machine to machine, and + possibly even between compilers. Your mileage may vary. + */ + + +/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */ +#define BMAX 15 /* maximum bit length of any code */ + +static int huft_build(uInt *b, uInt n, uInt s, const uInt *d, const uInt *e, inflate_huft ** t, uInt *m, inflate_huft *hp, uInt *hn, uInt *v) +//uInt *b; /* code lengths in bits (all assumed <= BMAX) */ +//uInt n; /* number of codes (assumed <= 288) */ +//uInt s; /* number of simple-valued codes (0..s-1) */ +//const uInt *d; /* list of base values for non-simple codes */ +//const uInt *e; /* list of extra bits for non-simple codes */ +//inflate_huft ** t; /* result: starting table */ +//uInt *m; /* maximum lookup bits, returns actual */ +//inflate_huft *hp; /* space for trees */ +//uInt *hn; /* hufts used in space */ +//uInt *v; /* working area: values in order of bit length */ +/* Given a list of code lengths and a maximum table size, make a set of + tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + if the given code set is incomplete (the tables are still built in this + case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of + lengths), or Z_MEM_ERROR if not enough memory. */ +{ + + uInt a; /* counter for codes of length k */ + uInt c[BMAX+1]; /* bit length count table */ + uInt f; /* i repeats in table every f entries */ + int g; /* maximum code length */ + int h; /* table level */ + register uInt i; /* counter, current code */ + register uInt j; /* counter */ + register int k; /* number of bits in current code */ + int l; /* bits per table (returned in m) */ + uInt mask; /* (1 << w) - 1, to avoid cc -O bug on HP */ + register uInt *p; /* pointer into c[], b[], or v[] */ + inflate_huft *q; /* points to current table */ + struct inflate_huft_s r; /* table entry for structure assignment */ + inflate_huft *u[BMAX]; /* table stack */ + register int w; /* bits before this table == (l * h) */ + uInt x[BMAX+1]; /* bit offsets, then code stack */ + uInt *xp; /* pointer into x */ + int y; /* number of dummy codes added */ + uInt z; /* number of entries in current table */ + + + /* Generate counts for each bit length */ + p = c; +#define C0 *p++ = 0; +#define C2 C0 C0 C0 C0 +#define C4 C2 C2 C2 C2 + C4 /* clear c[]--assume BMAX+1 is 16 */ + p = b; i = n; + do { + c[*p++]++; /* assume all entries <= BMAX */ + } while (--i); + if (c[0] == n) /* null input--all zero length codes */ + { + *t = (inflate_huft *)Z_NULL; + *m = 0; + return Z_OK; + } + + + /* Find minimum and maximum length, bound *m by those */ + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; /* minimum code length */ + if ((uInt)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; /* maximum code length */ + if ((uInt)l > i) + l = i; + *m = l; + + + /* Adjust last length count to fill out codes, if needed */ + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return Z_DATA_ERROR; + if ((y -= c[i]) < 0) + return Z_DATA_ERROR; + c[i] += y; + + + /* Generate starting offsets into the value table for each length */ + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { /* note that i == g from above */ + *xp++ = (j += *p++); + } + + + /* Make a table of values in order of bit lengths */ + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + n = x[g]; /* set n to length of v */ + + + /* Generate the Huffman codes and for each, make the table entries */ + x[0] = i = 0; /* first Huffman code is zero */ + p = v; /* grab values in bit order */ + h = -1; /* no tables yet--level -1 */ + w = -l; /* bits decoded == (l * h) */ + u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */ + q = (inflate_huft *)Z_NULL; /* ditto */ + z = 0; /* ditto */ + + /* go through the bit lengths (k already is bits in shortest code) */ + for (; k <= g; k++) + { + a = c[k]; + while (a--) + { + /* here i is the Huffman code of length k bits for value *p */ + /* make tables up to required level */ + while (k > w + l) + { + h++; + w += l; /* previous table always l bits */ + + /* compute minimum size table less than or equal to l bits */ + z = g - w; + z = z > (uInt)l ? l : z; /* table size upper limit */ + if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ + { /* too few codes for k-w bit table */ + f -= a + 1; /* deduct codes from patterns left */ + xp = c + k; + if (j < z) + while (++j < z) /* try smaller tables up to z bits */ + { + if ((f <<= 1) <= *++xp) + break; /* enough codes to use up j bits */ + f -= *xp; /* else deduct codes from patterns */ + } + } + z = 1 << j; /* table entries for j-bit table */ + + /* allocate new table */ + if (*hn + z > MANY) /* (note: doesn't matter for fixed) */ + return Z_MEM_ERROR; /* not enough memory */ + u[h] = q = hp + *hn; + *hn += z; + + /* connect to last table, if there is one */ + if (h) + { + x[h] = i; /* save pattern for backing up */ + r.bits = (Byte)l; /* bits to dump before this table */ + r.exop = (Byte)j; /* bits in this table */ + j = i >> (w - l); + r.base = (uInt)(q - u[h-1] - j); /* offset to this table */ + u[h-1][j] = r; /* connect to last table */ + } + else + *t = q; /* first table is returned result */ + } + + /* set up table entry in r */ + r.bits = (Byte)(k - w); + if (p >= v + n) + r.exop = 128 + 64; /* out of values--invalid code */ + else if (*p < s) + { + r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */ + r.base = *p++; /* simple code is just the value */ + } + else + { + r.exop = (Byte)(e[*p - s] + 16 + 64);/* non-simple--look up in lists */ + r.base = d[*p++ - s]; + } + + /* fill code-like entries with r */ + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + /* backwards increment the k-bit code i */ + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + /* backup over finished tables */ + mask = (1 << w) - 1; /* needed on HP, cc -O bug */ + while ((i & mask) != x[h]) + { + h--; /* don't need to update q */ + w -= l; + mask = (1 << w) - 1; + } + } + } + + + /* Return Z_BUF_ERROR if we were given an incomplete table */ + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; +} + + +int inflate_trees_bits(uInt *c, uInt *bb, inflate_huft * *tb, inflate_huft *hp, z_streamp z) +//uInt *c; /* 19 code lengths */ +//uInt *bb; /* bits tree desired/actual depth */ +//inflate_huft * *tb; /* bits tree result */ +//inflate_huft *hp; /* space for trees */ +//z_streamp z; /* for messages */ +{ + int r; + uInt hn = 0; /* hufts used in space */ + uInt *v; /* work area for huft_build */ + + if ((v = (uInt*)ZALLOC(z, 19, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + r = huft_build(c, 19, 19, (uInt*)Z_NULL, (uInt*)Z_NULL, + tb, bb, hp, &hn, v); + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed dynamic bit lengths tree"; + else if (r == Z_BUF_ERROR || *bb == 0) + { + z->msg = (char*)"incomplete dynamic bit lengths tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; +} + + +int inflate_trees_dynamic(uInt nl, uInt nd, uInt *c, uInt *bl, uInt *bd, inflate_huft * *tl, inflate_huft * *td, inflate_huft *hp, z_streamp z) +//uInt nl; /* number of literal/length codes */ +//uInt nd; /* number of distance codes */ +//uInt *c; /* that many (total) code lengths */ +//uInt *bl; /* literal desired/actual bit depth */ +//uInt *bd; /* distance desired/actual bit depth */ +//inflate_huft * *tl; /* literal/length tree result */ +//inflate_huft * *td; /* distance tree result */ +//inflate_huft *hp; /* space for trees */ +//z_streamp z; /* for messages */ +{ + int r; + uInt hn = 0; /* hufts used in space */ + uInt *v; /* work area for huft_build */ + + /* allocate work area */ + if ((v = (uInt*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + + /* build literal/length tree */ + r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v); + if (r != Z_OK || *bl == 0) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed literal/length tree"; + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; + } + + /* build distance tree */ + r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v); + if (r != Z_OK || (*bd == 0 && nl > 257)) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed distance tree"; + else if (r == Z_BUF_ERROR) { +#ifdef PKZIP_BUG_WORKAROUND + r = Z_OK; + } +#else + z->msg = (char*)"incomplete distance tree"; + r = Z_DATA_ERROR; + } + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"empty distance tree with lengths"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; +#endif + } + + /* done */ + ZFREE(z, v); + return Z_OK; +} + +/* inffixed.h -- table for decoding fixed codes + * Generated automatically by the maketree.c program + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +static uInt fixed_bl = 9; +static uInt fixed_bd = 5; +static inflate_huft fixed_tl[] = { + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254}, + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255} + }; +static inflate_huft fixed_td[] = { + {{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097}, + {{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385}, + {{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193}, + {{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577}, + {{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145}, + {{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577}, + {{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289}, + {{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577} + }; + +int inflate_trees_fixed(uInt *bl, uInt *bd, inflate_huft * *tl, inflate_huft * *td, z_streamp z) +//uInt *bl; /* literal desired/actual bit depth */ +//uInt *bd; /* distance desired/actual bit depth */ +//inflate_huft * *tl; /* literal/length tree result */ +//inflate_huft * *td; /* distance tree result */ +//z_streamp z; /* for memory allocation */ +{ + *bl = fixed_bl; + *bd = fixed_bd; + *tl = fixed_tl; + *td = fixed_td; + return Z_OK; +} + +/* simplify the use of the inflate_huft type with some defines */ +#define exop word.what.Exop +#define bits word.what.Bits + +/* macros for bit input with no checking and for returning unused bytes */ +#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3;} + +/* Called with number of bytes left to write in window at least 258 + (the maximum string length) and number of input bytes available + at least ten. The ten bytes are six bytes for the longest length/ + distance pair plus four bytes for overloading the bit buffer. */ + +int inflate_fast(uInt bl, uInt bd, inflate_huft *tl, inflate_huft *td, inflate_blocks_statef *s, z_streamp z) +{ + inflate_huft *t; /* temporary pointer */ + uInt e; /* extra bits or operation */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Byte *p; /* input data pointer */ + uInt n; /* bytes available there */ + Byte *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + uInt ml; /* mask for literal/length tree */ + uInt md; /* mask for distance tree */ + uInt c; /* bytes to copy */ + uInt d; /* distance back to copy from */ + Byte *r; /* copy source pointer */ + + /* load input, output, bit values */ + LOAD + + /* initialize masks */ + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + /* do until not enough input or output space for fast loop */ + do { /* assume called with m >= 258 && n >= 10 */ + /* get literal/length code */ + GRABBITS(20) /* max bits for literal/length code */ + if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + continue; + } + do { + DUMPBITS(t->bits) + if (e & 16) + { + /* get extra bits for length */ + e &= 15; + c = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv(("inflate: * length %u\n", c)); + + /* decode distance base of block to copy */ + GRABBITS(15); /* max bits for distance code */ + e = (t = td + ((uInt)b & md))->exop; + do { + DUMPBITS(t->bits) + if (e & 16) + { + /* get extra bits to add to distance base */ + e &= 15; + GRABBITS(e) /* get extra bits (up to 13) */ + d = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv(("inflate: * distance %u\n", d)); + + /* do the copy */ + m -= c; + if ((uInt)(q - s->window) >= d) /* offset before dest */ + { /* just copy */ + r = q - d; + *q++ = *r++; c--; /* minimum count is three, */ + *q++ = *r++; c--; /* so unroll loop a little */ + } + else /* else offset after destination */ + { + e = d - (uInt)(q - s->window); /* bytes from offset to end */ + r = s->end - e; /* pointer to offset */ + if (c > e) /* if source crosses, */ + { + c -= e; /* copy to end of window */ + do { + *q++ = *r++; + } while (--e); + r = s->window; /* copy rest from start of window */ + } + } + do { /* copy all or what's left */ + *q++ = *r++; + } while (--c); + break; + } + else if ((e & 64) == 0) + { + t += t->base; + e = (t += ((uInt)b & inflate_mask[e]))->exop; + } + else + { + z->msg = (char*)"invalid distance code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + } while (1); + break; + } + if ((e & 64) == 0) + { + t += t->base; + if ((e = (t += ((uInt)b & inflate_mask[e]))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + break; + } + } + else if (e & 32) + { + Tracevv(("inflate: * end of block\n")); + UNGRAB + UPDATE + return Z_STREAM_END; + } + else + { + z->msg = (char*)"invalid literal/length code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + } while (1); + } while (m >= 258 && n >= 10); + + /* not enough input or output--restore pointers and return */ + UNGRAB + UPDATE + return Z_OK; +} + +/* infcodes.c -- process literals and length/distance pairs + * Copyright (C) 1995-1998 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* simplify the use of the inflate_huft type with some defines */ +#define exop word.what.Exop +#define bits word.what.Bits + +typedef enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ + START, /* x: set up for LEN */ + LEN, /* i: get length/literal/eob next */ + LENEXT, /* i: getting length extra (have base) */ + DIST, /* i: get distance next */ + DISTEXT, /* i: getting distance extra */ + COPY, /* o: copying bytes in window, waiting for space */ + LIT, /* o: got literal, waiting for output space */ + WASH, /* o: got eob, possibly still output waiting */ + END, /* x: got eob and all data flushed */ + BADCODE} /* x: got error */ +inflate_codes_mode; + +/* inflate codes private state */ +struct inflate_codes_state { + + /* mode */ + inflate_codes_mode mode; /* current inflate_codes mode */ + + /* mode dependent information */ + uInt len; + union { + struct { + inflate_huft *tree; /* pointer into tree */ + uInt need; /* bits needed */ + } code; /* if LEN or DIST, where in tree */ + uInt lit; /* if LIT, literal */ + struct { + uInt get; /* bits to get for extra */ + uInt dist; /* distance back to copy from */ + } copy; /* if EXT or COPY, where and how much */ + } sub; /* submode */ + + /* mode independent information */ + Byte lbits; /* ltree bits decoded per branch */ + Byte dbits; /* dtree bits decoder per branch */ + inflate_huft *ltree; /* literal/length/eob tree */ + inflate_huft *dtree; /* distance tree */ + +}; + + +inflate_codes_statef *inflate_codes_new(uInt bl, uInt bd, inflate_huft *tl, inflate_huft *td, z_streamp z) +{ + inflate_codes_statef *c; + + if ((c = (inflate_codes_statef *) + ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) + { + c->mode = START; + c->lbits = (Byte)bl; + c->dbits = (Byte)bd; + c->ltree = tl; + c->dtree = td; + Tracev(("inflate: codes new\n")); + } + return c; +} + + +int inflate_codes(inflate_blocks_statef *s, z_streamp z, int r) +{ + uInt j; /* temporary storage */ + inflate_huft *t; /* temporary pointer */ + uInt e; /* extra bits or operation */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Byte *p; /* input data pointer */ + uInt n; /* bytes available there */ + Byte *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + Byte *f; /* pointer to copy strings from */ + inflate_codes_statef *c = s->sub.decode.codes; /* codes state */ + + /* copy input/output information to locals (UPDATE macro restores) */ + LOAD + + /* process input and output based on current state */ + while (1) switch (c->mode) + { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ + case START: /* x: set up for LEN */ +#ifndef SLOW + if (m >= 258 && n >= 10) + { + UPDATE + r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); + LOAD + if (r != Z_OK) + { + c->mode = r == Z_STREAM_END ? WASH : BADCODE; + break; + } + } +#endif /* !SLOW */ + c->sub.code.need = c->lbits; + c->sub.code.tree = c->ltree; + c->mode = LEN; + case LEN: /* i: get length/literal/eob next */ + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e == 0) /* literal */ + { + c->sub.lit = t->base; + Tracevv((t->base >= 0x20 && t->base < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", t->base)); + c->mode = LIT; + break; + } + if (e & 16) /* length */ + { + c->sub.copy.get = e & 15; + c->len = t->base; + c->mode = LENEXT; + break; + } + if ((e & 64) == 0) /* next table */ + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + if (e & 32) /* end of block */ + { + Tracevv(("inflate: end of block\n")); + c->mode = WASH; + break; + } + c->mode = BADCODE; /* invalid code */ + z->msg = (char*)"invalid literal/length code"; + r = Z_DATA_ERROR; + LEAVE + case LENEXT: /* i: getting length extra (have base) */ + j = c->sub.copy.get; + NEEDBITS(j) + c->len += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + c->sub.code.need = c->dbits; + c->sub.code.tree = c->dtree; + Tracevv(("inflate: length %u\n", c->len)); + c->mode = DIST; + case DIST: /* i: get distance next */ + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e & 16) /* distance */ + { + c->sub.copy.get = e & 15; + c->sub.copy.dist = t->base; + c->mode = DISTEXT; + break; + } + if ((e & 64) == 0) /* next table */ + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + c->mode = BADCODE; /* invalid code */ + z->msg = (char*)"invalid distance code"; + r = Z_DATA_ERROR; + LEAVE + case DISTEXT: /* i: getting distance extra */ + j = c->sub.copy.get; + NEEDBITS(j) + c->sub.copy.dist += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + Tracevv(("inflate: distance %u\n", c->sub.copy.dist)); + c->mode = COPY; + case COPY: /* o: copying bytes in window, waiting for space */ +#ifndef __TURBOC__ /* Turbo C bug for following expression */ + f = (uInt)(q - s->window) < c->sub.copy.dist ? + s->end - (c->sub.copy.dist - (q - s->window)) : + q - c->sub.copy.dist; +#else + f = q - c->sub.copy.dist; + if ((uInt)(q - s->window) < c->sub.copy.dist) + f = s->end - (c->sub.copy.dist - (uInt)(q - s->window)); +#endif + while (c->len) + { + NEEDOUT + OUTBYTE(*f++) + if (f == s->end) + f = s->window; + c->len--; + } + c->mode = START; + break; + case LIT: /* o: got literal, waiting for output space */ + NEEDOUT + OUTBYTE(c->sub.lit) + c->mode = START; + break; + case WASH: /* o: got eob, possibly more output */ + if (k > 7) /* return unused byte, if any */ + { + Assert(k < 16, "inflate_codes grabbed too many bytes") + k -= 8; + n++; + p--; /* can always return one */ + } + FLUSH + if (s->read != s->write) + LEAVE + c->mode = END; + case END: + r = Z_STREAM_END; + LEAVE + case BADCODE: /* x: got error */ + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +#ifdef NEED_DUMMY_RETURN + return Z_STREAM_ERROR; /* Some dumb compilers complain without this */ +#endif +} + + +void inflate_codes_free(inflate_codes_statef *c, z_streamp z) +{ + ZFREE(z, c); + Tracev(("inflate: codes free\n")); +} + +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-1998 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#define BASE 65521L /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#undef DO1 +#undef DO2 +#undef DO4 +#undef DO8 + +#define DO1(buf,i) {s1 += buf[i]; s2 += s1;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* ========================================================================= */ +uLong adler32(uLong adler, const Byte *buf, uInt len) +{ + unsigned long s1 = adler & 0xffff; + unsigned long s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == Z_NULL) return 1L; + + while (len > 0) { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) { + DO16(buf); + buf += 16; + k -= 16; + } + if (k != 0) do { + s1 += *buf++; + s2 += s1; + } while (--k); + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; +} + +/* @(#) $Id: unzip.cpp,v 1.1.1.3 2000/01/11 16:37:27 ttimo Exp $ */ + +/* infblock.h -- header to use infblock.c + * Copyright (C) 1995-1998 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +extern inflate_blocks_statef * inflate_blocks_new OF(( + z_streamp z, + check_func c, /* check function */ + uInt w)); /* window size */ + +extern int inflate_blocks OF(( + inflate_blocks_statef *, + z_streamp , + int)); /* initial return code */ + +extern void inflate_blocks_reset OF(( + inflate_blocks_statef *, + z_streamp , + uLong *)); /* check value on output */ + +extern int inflate_blocks_free OF(( + inflate_blocks_statef *, + z_streamp)); + +extern void inflate_set_dictionary OF(( + inflate_blocks_statef *s, + const Byte *d, /* dictionary */ + uInt n)); /* dictionary length */ + +extern int inflate_blocks_sync_point OF(( + inflate_blocks_statef *s)); + +typedef enum { + imMETHOD, /* waiting for method byte */ + imFLAG, /* waiting for flag byte */ + imDICT4, /* four dictionary check bytes to go */ + imDICT3, /* three dictionary check bytes to go */ + imDICT2, /* two dictionary check bytes to go */ + imDICT1, /* one dictionary check byte to go */ + imDICT0, /* waiting for inflateSetDictionary */ + imBLOCKS, /* decompressing blocks */ + imCHECK4, /* four check bytes to go */ + imCHECK3, /* three check bytes to go */ + imCHECK2, /* two check bytes to go */ + imCHECK1, /* one check byte to go */ + imDONE, /* finished check, done */ + imBAD} /* got an error--stay here */ +inflate_mode; + +/* inflate private state */ +struct internal_state { + + /* mode */ + inflate_mode mode; /* current inflate mode */ + + /* mode dependent information */ + union { + uInt method; /* if FLAGS, method byte */ + struct { + uLong was; /* computed check value */ + uLong need; /* stream check value */ + } check; /* if CHECK, check values to compare */ + uInt marker; /* if BAD, inflateSync's marker bytes count */ + } sub; /* submode */ + + /* mode independent information */ + int nowrap; /* flag for no wrapper */ + uInt wbits; /* log2(window size) (8..15, defaults to 15) */ + inflate_blocks_statef + *blocks; /* current inflate_blocks state */ + +}; + + +int inflateReset(z_streamp z) +{ + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + z->total_in = z->total_out = 0; + z->msg = Z_NULL; + z->state->mode = z->state->nowrap ? imBLOCKS : imMETHOD; + inflate_blocks_reset(z->state->blocks, z, Z_NULL); + Tracev(("inflate: reset\n")); + return Z_OK; +} + + +int inflateEnd(z_streamp z) +{ + if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->blocks != Z_NULL) + inflate_blocks_free(z->state->blocks, z); + ZFREE(z, z->state); + z->state = Z_NULL; + Tracev(("inflate: end\n")); + return Z_OK; +} + + + +int inflateInit2_(z_streamp z, int w, const char *version, int stream_size) +{ + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != sizeof(z_stream)) + return Z_VERSION_ERROR; + + /* initialize state */ + if (z == Z_NULL) + return Z_STREAM_ERROR; + z->msg = Z_NULL; + if (z->zalloc == Z_NULL) + { + z->zalloc = (void *(*)(void *, unsigned, unsigned))zcalloc; + z->opaque = (voidp)0; + } + if (z->zfree == Z_NULL) z->zfree = (void (*)(void *, void *))zcfree; + if ((z->state = (struct internal_state *) + ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) + return Z_MEM_ERROR; + z->state->blocks = Z_NULL; + + /* handle undocumented nowrap option (no zlib header or check) */ + z->state->nowrap = 0; + if (w < 0) + { + w = - w; + z->state->nowrap = 1; + } + + /* set window size */ + if (w < 8 || w > 15) + { + inflateEnd(z); + return Z_STREAM_ERROR; + } + z->state->wbits = (uInt)w; + + /* create inflate_blocks state */ + if ((z->state->blocks = + inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w)) + == Z_NULL) + { + inflateEnd(z); + return Z_MEM_ERROR; + } + Tracev(("inflate: allocated\n")); + + /* reset state */ + inflateReset(z); + return Z_OK; +} + + +int inflateInit_(z_streamp z, const char *version, int stream_size) +{ + return inflateInit2_(z, DEF_WBITS, version, stream_size); +} + + +#define iNEEDBYTE {if(z->avail_in==0)return r;r=f;} +#define iNEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) + +int inflate(z_streamp z, int f) +{ + int r; + uInt b; + + if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL) + return Z_STREAM_ERROR; + f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; + r = Z_BUF_ERROR; + while (1) switch (z->state->mode) + { + case imMETHOD: + iNEEDBYTE + if (((z->state->sub.method = iNEXTBYTE) & 0xf) != Z_DEFLATED) + { + z->state->mode = imBAD; + z->msg = (char*)"unknown compression method"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + if ((z->state->sub.method >> 4) + 8 > z->state->wbits) + { + z->state->mode = imBAD; + z->msg = (char*)"invalid window size"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + z->state->mode = imFLAG; + case imFLAG: + iNEEDBYTE + b = iNEXTBYTE; + if (((z->state->sub.method << 8) + b) % 31) + { + z->state->mode = imBAD; + z->msg = (char*)"incorrect header check"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + Tracev(("inflate: zlib header ok\n")); + if (!(b & PRESET_DICT)) + { + z->state->mode = imBLOCKS; + break; + } + z->state->mode = imDICT4; + case imDICT4: + iNEEDBYTE + z->state->sub.check.need = (uLong)iNEXTBYTE << 24; + z->state->mode = imDICT3; + case imDICT3: + iNEEDBYTE + z->state->sub.check.need += (uLong)iNEXTBYTE << 16; + z->state->mode = imDICT2; + case imDICT2: + iNEEDBYTE + z->state->sub.check.need += (uLong)iNEXTBYTE << 8; + z->state->mode = imDICT1; + case imDICT1: + iNEEDBYTE + z->state->sub.check.need += (uLong)iNEXTBYTE; + z->adler = z->state->sub.check.need; + z->state->mode = imDICT0; + return Z_NEED_DICT; + case imDICT0: + z->state->mode = imBAD; + z->msg = (char*)"need dictionary"; + z->state->sub.marker = 0; /* can try inflateSync */ + return Z_STREAM_ERROR; + case imBLOCKS: + r = inflate_blocks(z->state->blocks, z, r); + if (r == Z_DATA_ERROR) + { + z->state->mode = imBAD; + z->state->sub.marker = 0; /* can try inflateSync */ + break; + } + if (r == Z_OK) + r = f; + if (r != Z_STREAM_END) + return r; + r = f; + inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); + if (z->state->nowrap) + { + z->state->mode = imDONE; + break; + } + z->state->mode = imCHECK4; + case imCHECK4: + iNEEDBYTE + z->state->sub.check.need = (uLong)iNEXTBYTE << 24; + z->state->mode = imCHECK3; + case imCHECK3: + iNEEDBYTE + z->state->sub.check.need += (uLong)iNEXTBYTE << 16; + z->state->mode = imCHECK2; + case imCHECK2: + iNEEDBYTE + z->state->sub.check.need += (uLong)iNEXTBYTE << 8; + z->state->mode = imCHECK1; + case imCHECK1: + iNEEDBYTE + z->state->sub.check.need += (uLong)iNEXTBYTE; + + if (z->state->sub.check.was != z->state->sub.check.need) + { + z->state->mode = imBAD; + z->msg = (char*)"incorrect data check"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + Tracev(("inflate: zlib check ok\n")); + z->state->mode = imDONE; + case imDONE: + return Z_STREAM_END; + case imBAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } +#ifdef NEED_DUMMY_RETURN + return Z_STREAM_ERROR; /* Some dumb compilers complain without this */ +#endif +} + + +int inflateSetDictionary(z_streamp z, const Byte *dictionary, uInt dictLength) +{ + uInt length = dictLength; + + if (z == Z_NULL || z->state == Z_NULL || z->state->mode != imDICT0) + return Z_STREAM_ERROR; + + if (adler32(1L, dictionary, dictLength) != z->adler) return Z_DATA_ERROR; + z->adler = 1L; + + if (length >= ((uInt)1<state->wbits)) + { + length = (1<state->wbits)-1; + dictionary += dictLength - length; + } + inflate_set_dictionary(z->state->blocks, dictionary, length); + z->state->mode = imBLOCKS; + return Z_OK; +} + + +int inflateSync(z_streamp z) +{ + uInt n; /* number of bytes to look at */ + Byte *p; /* pointer to bytes */ + uInt m; /* number of marker bytes found in a row */ + uLong r, w; /* temporaries to save total_in and total_out */ + + /* set up */ + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->mode != imBAD) + { + z->state->mode = imBAD; + z->state->sub.marker = 0; + } + if ((n = z->avail_in) == 0) + return Z_BUF_ERROR; + p = z->next_in; + m = z->state->sub.marker; + + /* search */ + while (n && m < 4) + { + static const Byte mark[4] = {0, 0, 0xff, 0xff}; + if (*p == mark[m]) + m++; + else if (*p) + m = 0; + else + m = 4 - m; + p++, n--; + } + + /* restore */ + z->total_in += p - z->next_in; + z->next_in = p; + z->avail_in = n; + z->state->sub.marker = m; + + /* return no joy or set up to restart on a new block */ + if (m != 4) + return Z_DATA_ERROR; + r = z->total_in; w = z->total_out; + inflateReset(z); + z->total_in = r; z->total_out = w; + z->state->mode = imBLOCKS; + return Z_OK; +} + + +/* Returns true if inflate is currently at the end of a block generated + * by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + * implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH + * but removes the length bytes of the resulting empty stored block. When + * decompressing, PPP checks that at the end of input packet, inflate is + * waiting for these length bytes. + */ +int inflateSyncPoint(z_streamp z) +{ + if (z == Z_NULL || z->state == Z_NULL || z->state->blocks == Z_NULL) + return Z_STREAM_ERROR; + return inflate_blocks_sync_point(z->state->blocks); +} + +voidp zcalloc (voidp opaque, unsigned items, unsigned size) +{ + if (opaque) items += size - size; /* make compiler happy */ + return (voidp)malloc(items*size); +} + +void zcfree (voidp opaque, voidp ptr) +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + diff --git a/utils/Libs/pak/unzip.h b/utils/Libs/pak/unzip.h new file mode 100644 index 0000000..d5c165d --- /dev/null +++ b/utils/Libs/pak/unzip.h @@ -0,0 +1,300 @@ + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef void* unzFile; +#endif + + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + unsigned int tm_sec; /* seconds after the minute - [0,59] */ + unsigned int tm_min; /* minutes after the hour - [0,59] */ + unsigned int tm_hour; /* hours since midnight - [0,23] */ + unsigned int tm_mday; /* day of the month - [1,31] */ + unsigned int tm_mon; /* months since January - [0,11] */ + unsigned int tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info_s +{ + unsigned long number_entry; /* total number of entries in the central dir on this disk */ + unsigned long size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info_s +{ + unsigned long version; /* version made by 2 unsigned chars */ + unsigned long version_needed; /* version needed to extract 2 unsigned chars */ + unsigned long flag; /* general purpose bit flag 2 unsigned chars */ + unsigned long compression_method; /* compression method 2 unsigned chars */ + unsigned long dosDate; /* last mod file date in Dos fmt 4 unsigned chars */ + unsigned long crc; /* crc-32 4 unsigned chars */ + unsigned long compressed_size; /* compressed size 4 unsigned chars */ + unsigned long uncompressed_size; /* uncompressed size 4 unsigned chars */ + unsigned long size_filename; /* filename length 2 unsigned chars */ + unsigned long size_file_extra; /* extra field length 2 unsigned chars */ + unsigned long size_file_comment; /* file comment length 2 unsigned chars */ + + unsigned long disk_num_start; /* disk number start 2 unsigned chars */ + unsigned long internal_fa; /* internal file attributes 2 unsigned chars */ + unsigned long external_fa; /* external file attributes 4 unsigned chars */ + + tm_unz tmu_date; +} unz_file_info; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info_internal_s +{ + unsigned long offset_curfile;/* relative offset of static header 4 unsigned chars */ +} unz_file_info_internal; + +typedef void* (*alloc_func) (void* opaque, unsigned int items, unsigned int size); +typedef void (*free_func) (void* opaque, void* address); + +struct internal_state; + +typedef struct z_stream_s { + unsigned char *next_in; /* next input unsigned char */ + unsigned int avail_in; /* number of unsigned chars available at next_in */ + unsigned long total_in; /* total nb of input unsigned chars read so */ + + unsigned char *next_out; /* next output unsigned char should be put there */ + unsigned int avail_out; /* remaining free space at next_out */ + unsigned long total_out; /* total nb of unsigned chars output so */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + unsigned char* opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: ascii or binary */ + unsigned long adler; /* adler32 value of the uncompressed data */ + unsigned long reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream *z_streamp; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + + unsigned long pos_in_zipfile; /* position in unsigned char on the zipfile, for fseek*/ + unsigned long stream_initialised; /* flag set if stream structure is initialised*/ + + unsigned long offset_local_extrafield;/* offset of the static extra field */ + unsigned int size_local_extrafield;/* size of the static extra field */ + unsigned long pos_local_extrafield; /* position in the static extra field in read*/ + + unsigned long crc32; /* crc32 of all data uncompressed */ + unsigned long crc32_wait; /* crc32 we must obtain after decompress all */ + unsigned long rest_read_compressed; /* number of unsigned char to be decompressed */ + unsigned long rest_read_uncompressed;/*number of unsigned char to be obtained after decomp*/ + FILE* file; /* io structore of the zipfile */ + unsigned long compression_method; /* compression method (0==store) */ + unsigned long byte_before_the_zipfile;/* unsigned char before the zipfile, (>0 for sfx)*/ +} file_in_zip_read_info_s; + + +/* unz_s contain internal information about the zipfile +*/ +typedef struct +{ + FILE* file; /* io structore of the zipfile */ + unz_global_info gi; /* public global information */ + unsigned long byte_before_the_zipfile;/* unsigned char before the zipfile, (>0 for sfx)*/ + unsigned long num_file; /* number of the current file in the zipfile*/ + unsigned long pos_in_central_dir; /* pos of the current file in the central dir*/ + unsigned long current_file_ok; /* flag about the usability of the current file*/ + unsigned long central_pos; /* position of the beginning of the central dir*/ + + unsigned long size_central_dir; /* size of the central directory */ + unsigned long offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info cur_file_info; /* public info about the current file in zip*/ + unz_file_info_internal cur_file_info_internal; /* private info about it*/ + file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ +} unz_s; + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +#define UNZ_CASESENSITIVE 1 +#define UNZ_NOTCASESENSITIVE 2 +#define UNZ_OSDEFAULTCASE 0 + +extern int unzStringFileNameCompare (const char* fileName1, const char* fileName2, int iCaseSensitivity); + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + +extern unzFile unzOpen (const char *path); +extern unzFile unzReOpen (const char* path, unzFile file); + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\zlib\\zlib111.zip" or on an Unix computer + "zlib/zlib111.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ + +extern int unzClose (unzFile file); + +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ + +extern int unzGetGlobalInfo (unzFile file, unz_global_info *pglobal_info); + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int unzGetGlobalComment (unzFile file, char *szComment, unsigned long uSizeBuf); + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of unsigned char copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int unzGoToFirstFile (unzFile file); + +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int unzGoToNextFile (unzFile file); + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity); + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +extern int unzGetCurrentFileInfo (unzFile file, unz_file_info *pfile_info, char *szFileName, unsigned long fileNameBufferSize, void *extraField, unsigned long extraFieldBufferSize, char *szComment, unsigned long commentBufferSize); + +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int unzOpenCurrentFile (unzFile file); + +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int unzCloseCurrentFile (unzFile file); + +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + + +extern int unzReadCurrentFile (unzFile file, void* buf, unsigned len); + +/* + Read unsigned chars from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of unsigned char copied if somes unsigned chars are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern long unztell(unzFile file); + +/* + Give the current position in uncompressed data +*/ + +extern int unzeof (unzFile file); + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int unzGetLocalExtrafield (unzFile file, void* buf, unsigned len); + +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of unsigned chars copied in buf, or (if <0) + the error code +*/ diff --git a/utils/Libs/pakstuff.h b/utils/Libs/pakstuff.h new file mode 100644 index 0000000..4fd9741 --- /dev/null +++ b/utils/Libs/pakstuff.h @@ -0,0 +1,120 @@ +#ifndef _PAKSTUFF_H_ +#define _PAKSTUFF_H_ + +#include +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef char Int8; +typedef short Int16; +typedef long Int32; +typedef unsigned char UInt8; +typedef unsigned short UInt16; +typedef unsigned long UInt32; +typedef float Float32; +typedef double Float64; +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define RANDOM(x) (random() % (x)) +#define RANDOMIZE() srand((int) time(NULL)) + +#define FTYPE_UNKNOWN 0 +#define FTYPE_IWAD 1 /* .wad "IWAD" */ +#define FTYPE_PWAD 2 /* .wad "PWAD" */ +#define FTYPE_PACK 3 /* .pak "PACK" */ +#define FTYPE_WAD2 4 /* .wad "WAD2" */ +#define FTYPE_BSP 10 /* .bsp (0x17 0x00 0x00 0x00) */ +#define FTYPE_MODEL 11 /* .mdl "IDPO" */ +#define FTYPE_SPRITE 12 /* .spr "IDSP" */ +#define FTYPE_WAV 20 /* .wav "RIFF" */ +#define FTYPE_AU 21 /* .au ".snd" */ +#define FTYPE_VOC 22 /* .voc ? */ +#define FTYPE_PBM_ASC 30 /* .pbm "P1" */ +#define FTYPE_PGM_ASC 31 /* .pgm "P2" */ +#define FTYPE_PPM_ASC 32 /* .ppm "P3" */ +#define FTYPE_PBM_RAW 33 /* .pbm "P4" */ +#define FTYPE_PGM_RAW 34 /* .pgm "P5" */ +#define FTYPE_PPM_RAW 35 /* .ppm "P6" */ +#define FTYPE_BMP 36 /* .bmp "BM" */ +#define FTYPE_GIF 37 /* .gif "GIF8" */ +#define FTYPE_PCX 38 /* .pcx (0x0a 0x05 0x01 0x08) */ +#define FTYPE_ERROR -1 + +#ifdef FAT_ENDIAN +Bool ReadInt16 (FILE *file, UInt16 huge *x); +Bool ReadInt32 (FILE *file, UInt32 huge *x); +Bool ReadFloat32 (FILE *file, Float32 huge *x); +Bool WriteInt16 (FILE *file, UInt16 huge *x); +Bool WriteInt32 (FILE *file, UInt32 huge *x); +Bool WriteFloat32 (FILE *file, Float32 huge *x); +UInt16 SwapInt16 (UInt16 x); +UInt32 SwapInt32 (UInt32 x); +Float32 SwapFloat32 (Float32 x); +#else +#define ReadInt16(f, p) ReadBytes((f), (p), 2L) +#define ReadInt32(f, p) ReadBytes((f), (p), 4L) +#define ReadFloat32(f, p) ReadBytes((f), (p), 4L) +#define WriteInt16(f, p) WriteBytes((f), (p), 2L) +#define WriteInt32(f, p) WriteBytes((f), (p), 4L) +#define WriteFloat32(f, p) WriteBytes((f), (p), 4L) +#define SwapInt16(x) (x) +#define SwapInt32(x) (x) +#define SwapFloat32(x) (x) +#endif /* FAT_ENDIAN */ + +#define FROMDISK -1 +struct PACKDirectory +{ + char name[56]; /* name of file */ + UInt32 offset; /* offset to start of data */ + UInt32 size; /* byte size of data */ +}; +typedef struct PACKDirectory *PACKDirPtr; + +typedef struct DirListStruct +{ + char dirname[1024]; + int from; + struct DirListStruct *next; +} DIRLIST; + +typedef struct FileListStruct +{ + char filename[1024]; + UInt32 offset; + UInt32 size; + struct FileListStruct *next; +} FILELIST; + +typedef struct DirStruct +{ + char name[1024]; + FILELIST *files; + struct DirStruct *next; +} DIRECTORY; + + +extern int m_nPAKIndex; +extern FILE* pakfile[16]; +extern boolean pakopen; +extern DIRECTORY *paktextures; + +void ClearFileList (FILELIST **); +void ClearDirList (DIRLIST **); +boolean GetPackFileList (FILELIST **, char *); +boolean GetPackTextureDirs (DIRLIST **); +boolean AddToDirListAlphabetized (DIRLIST **, char *, int); +boolean AddToFileListAlphabetized (FILELIST **t, char *, UInt32, UInt32, boolean); +boolean PakLoadFile (const char *, void **); +void OpenPakFile (const char *); +void ClosePakFile (void); +int PakLoadAnyFile(const char *filename, void **bufferptr); +void WINAPI InitPakFile(const char * pBasePath, const char *pName); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/utils/Libs/str.h b/utils/Libs/str.h new file mode 100644 index 0000000..cec63df --- /dev/null +++ b/utils/Libs/str.h @@ -0,0 +1,193 @@ +#ifndef __STR__ +#define __STR__ +// +// class Str +// loose replacement for CString from MFC +// +//#include "cmdlib.h" +#include + +char* __StrDup(char* pStr); +char* __StrDup(const char* pStr); + + + +static char *g_pStrWork = NULL; + +class Str +{ +protected: + bool m_bIgnoreCase; + char *m_pStr; + +public: + Str() + { + m_bIgnoreCase = true; + m_pStr = NULL; + } + + Str(char *p) + { + m_bIgnoreCase = true; + m_pStr = __StrDup(p); + } + + Str(const char *p) + { + m_bIgnoreCase = true; + m_pStr = __StrDup(p); + } + + void Deallocate() + { + delete []m_pStr; + m_pStr = NULL; + } + + void Allocate(int n) + { + Deallocate(); + m_pStr = new char[n]; + } + + const char* GetBuffer() + { + return m_pStr; + } + + void MakeEmpty() + { + Deallocate(); + m_pStr = __StrDup(""); + } + + ~Str() + { + Deallocate(); + delete []g_pStrWork; + g_pStrWork = NULL; + } + + void MakeLower() + { + if (m_pStr) + { + strlwr(m_pStr); + } + } + + int Find(const char *p) + { + char *pf = strstr(m_pStr, p); + return (pf) ? (pf - m_pStr) : -1; + } + + int GetLength() + { + return (m_pStr) ? strlen(m_pStr) : 0; + } + + const char* Left(int n) + { + delete []g_pStrWork; + if (n > 0) + { + g_pStrWork = new char[n+1]; + strncpy(g_pStrWork, m_pStr, n); + } + else + { + g_pStrWork = ""; + g_pStrWork = new char[1]; + g_pStrWork[0] = '\0'; + } + return g_pStrWork; + } + + const char* Right(int n) + { + delete []g_pStrWork; + if (n > 0) + { + g_pStrWork = new char[n+1]; + int nStart = GetLength() - n; + strncpy(g_pStrWork, &m_pStr[nStart], n); + g_pStrWork[n] = '\0'; + } + else + { + g_pStrWork = new char[1]; + g_pStrWork[0] = '\0'; + } + return g_pStrWork; + } + + + char& operator *() { return *m_pStr; } + char& operator *() const { return *const_cast(this)->m_pStr; } + operator void*() { return m_pStr; } + operator char*() { return m_pStr; } + operator const char*(){ return reinterpret_cast(m_pStr); } + operator unsigned char*() { return reinterpret_cast(m_pStr); } + operator const unsigned char*() { return reinterpret_cast(m_pStr); } + Str& operator =(const Str& rhs) + { + if (&rhs != this) + { + delete[] m_pStr; + m_pStr = __StrDup(rhs.m_pStr); + } + return *this; + } + + Str& operator =(const char* pStr) + { + if (m_pStr != pStr) + { + delete[] m_pStr; + m_pStr = __StrDup(pStr); + } + return *this; + } + + Str& operator +=(const char *pStr) + { + if (pStr) + { + if (m_pStr) + { + char *p = new char[strlen(m_pStr) + strlen(pStr) + 1]; + strcpy(p, m_pStr); + strcat(p, pStr); + delete m_pStr; + m_pStr = p; + } + else + { + m_pStr = __StrDup(pStr); + } + } + return *this; + } + + Str& operator +=(const char c) + { + return operator+=(&c); + } + + + bool operator ==(const Str& rhs) const { return (m_bIgnoreCase) ? stricmp(m_pStr, rhs.m_pStr) == 0 : strcmp(m_pStr, rhs.m_pStr) == 0; } + bool operator ==(char* pStr) const { return (m_bIgnoreCase) ? stricmp(m_pStr, pStr) == 0 : strcmp(m_pStr, pStr) == 0; } + bool operator ==(const char* pStr) const { return (m_bIgnoreCase) ? stricmp(m_pStr, pStr) == 0 : strcmp(m_pStr, pStr) == 0; } + bool operator !=(Str& rhs) const { return (m_bIgnoreCase) ? stricmp(m_pStr, rhs.m_pStr) != 0 : strcmp(m_pStr, rhs.m_pStr) != 0; } + bool operator !=(char* pStr) const { return (m_bIgnoreCase) ? stricmp(m_pStr, pStr) != 0 : strcmp(m_pStr, pStr) != 0; } + bool operator !=(const char* pStr) const { return (m_bIgnoreCase) ? stricmp(m_pStr, pStr) != 0 : strcmp(m_pStr, pStr) != 0; } + char& operator [](int nIndex) { return m_pStr[nIndex]; } + char& operator [](int nIndex) const { return m_pStr[nIndex]; } + +}; + + + +#endif \ No newline at end of file diff --git a/utils/Radiant/3dfxcamwnd.h b/utils/Radiant/3dfxcamwnd.h new file mode 100644 index 0000000..8c8ad7e --- /dev/null +++ b/utils/Radiant/3dfxcamwnd.h @@ -0,0 +1,97 @@ +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// +#define ANGLE_SPEED 300 +#define MOVE_SPEED 400 + +///////////////////////////////////////////////////////////////////////////// +// C3DFXCamWnd window +class CXYWnd; + +class C3DFXCamWnd : public CWnd +{ + DECLARE_DYNCREATE(C3DFXCamWnd); +// Construction +public: + C3DFXCamWnd(); + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(C3DFXCamWnd) + protected: + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + //}}AFX_VIRTUAL + +// Implementation +public: + CXYWnd* m_pXYFriend; + void SetXYFriend(CXYWnd* pWnd); + virtual ~C3DFXCamWnd(); + camera_t& Camera(){return m_Camera;}; + void Cam_MouseControl(float dtime); + void Cam_ChangeFloor(qboolean up); + +protected: + void Cam_Init(); + void Cam_BuildMatrix(); + void Cam_PositionDrag(); + void Cam_MouseDown(int x, int y, int buttons); + void Cam_MouseUp (int x, int y, int buttons); + void Cam_MouseMoved (int x, int y, int buttons); + void InitCull(); + qboolean CullBrush (brush_t *b); + void Cam_Draw(); + + + brush_t* m_TransBrushes[MAX_MAP_BRUSHES]; + bool m_bTransBrushIsGhost[MAX_MAP_BRUSHES]; // is this header used? no-one here uses 3dfx. Oh well.... + + int m_nNumTransBrushes; + camera_t m_Camera; + int m_nCambuttonstate; + CPoint m_ptButton; + CPoint m_ptCursor; + face_t* m_pSide_select; + vec3_t m_vCull1; + vec3_t m_vCull2; + int m_nCullv1[3]; + int m_nCullv2[3]; + + HDC m_hDC; + HGLRC m_hRC; + + // Generated message map functions +protected: + void OriginalMouseDown(UINT nFlags, CPoint point); + void OriginalMouseUp(UINT nFlags, CPoint point); + //{{AFX_MSG(C3DFXCamWnd) + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnPaint(); + afx_msg BOOL OnEraseBkgnd(CDC* pDC); + afx_msg void OnDestroy(); + afx_msg void OnClose(); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg void OnMButtonDown(UINT nFlags, CPoint point); + afx_msg void OnMButtonUp(UINT nFlags, CPoint point); + afx_msg void OnRButtonDown(UINT nFlags, CPoint point); + afx_msg void OnRButtonUp(UINT nFlags, CPoint point); + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnSize(UINT nType, int cx, int cy); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + diff --git a/utils/Radiant/GetString.cpp b/utils/Radiant/GetString.cpp new file mode 100644 index 0000000..377254a --- /dev/null +++ b/utils/Radiant/GetString.cpp @@ -0,0 +1,73 @@ +// GetString.cpp : implementation file +// + +#include "stdafx.h" +#include "radiant.h" +#include "GetString.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CGetString dialog + + +CGetString::CGetString(LPCSTR pPrompt, CString *pFeedback, CWnd* pParent /*=NULL*/) + : CDialog(CGetString::IDD, pParent) +{ + //{{AFX_DATA_INIT(CGetString) + m_strEditBox = _T(""); + //}}AFX_DATA_INIT + + m_pFeedback = pFeedback; + m_pPrompt = pPrompt; +} + + +void CGetString::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CGetString) + DDX_Text(pDX, IDC_EDIT1, m_strEditBox); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CGetString, CDialog) + //{{AFX_MSG_MAP(CGetString) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CGetString message handlers + +void CGetString::OnOK() +{ + UpdateData(DIALOG_TO_DATA); + + *m_pFeedback = m_strEditBox; + + CDialog::OnOK(); +} + +BOOL CGetString::OnInitDialog() +{ + CDialog::OnInitDialog(); + + // TODO: Add extra initialization here + + + GetDlgItem(IDC_PROMPT)->SetWindowText(m_pPrompt); +/* CWnd* theWnd = GetDlgItem(IDC_TREKPROPBUTTON_TOGGLEVIEW); + if (m_button.m_DrawType == gldt_STRETCHFIT) + { + theWnd->SetWindowText("Stretched"); + } +*/ + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/utils/Radiant/GetString.h b/utils/Radiant/GetString.h new file mode 100644 index 0000000..b714478 --- /dev/null +++ b/utils/Radiant/GetString.h @@ -0,0 +1,48 @@ +#if !defined(AFX_GETSTRING_H__062547B2_2CFC_4DA8_A379_65F83606D96A__INCLUDED_) +#define AFX_GETSTRING_H__062547B2_2CFC_4DA8_A379_65F83606D96A__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// GetString.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CGetString dialog + +class CGetString : public CDialog +{ +// Construction +public: + CGetString(LPCSTR pPrompt, CString *pFeedback, CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CGetString) + enum { IDD = IDD_DIALOG_GETSTRING }; + CString m_strEditBox; + //}}AFX_DATA + CString *m_pFeedback; + LPCSTR m_pPrompt; + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CGetString) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CGetString) + virtual void OnOK(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_GETSTRING_H__062547B2_2CFC_4DA8_A379_65F83606D96A__INCLUDED_) diff --git a/utils/Radiant/Quake3/cbrush.cpp b/utils/Radiant/Quake3/cbrush.cpp new file mode 100644 index 0000000..47ac71c --- /dev/null +++ b/utils/Radiant/Quake3/cbrush.cpp @@ -0,0 +1,1343 @@ +#include "stdafx.h" +#include "..\qe3.h" + +#define CBLOCK_SUBDIVISIONS 4 + +/* + + treat CONTENTS_DETAIL like the curve flags + + + get / set textures should not change any of the curve flags + setting a curved texture should set all of the curved faces + +*/ + +typedef float vec5_t[5]; + +typedef enum { + CS_FLAT, CS_CURVE, CS_SPLIT +} cubeSide_t; + +typedef struct { + int points[4]; + int neighbors[4]; + cubeSide_t type; + face_t *brushFace; +} cubeFace_t; + +typedef struct { + vec5_t points[8]; + cubeFace_t faces[6]; + qboolean negativeCurve; +} curveBlock_t; + +float STfromUXYZ[2][5]; +float STfromXYZ[2][4]; + +FILE *curveFile = NULL; +brush_t *bevelBrush = NULL; + +void Write1DMatrix (FILE *f, int x, float *m); +void Write2DMatrix (FILE *f, int y, int x, float *m); +void Write3DMatrix (FILE *f, int y, int x, int z, float *m); + + +bool g_bCamPaint = false; +const float fFullBright = 1.0; +const float fLowerLimit = .70; +const float fDec = .06; +void SetColor(face_t* f, float fColor[3]) +{ + return; + if (g_bCamPaint) + { + fColor[0] = f->d_color[0]; + fColor[1] = f->d_color[1]; + fColor[2] = f->d_color[2]; + qglColor3fv(fColor); + } +} + + +void DecColor(float fColor[3]) +{ + return; + if (g_bCamPaint) + { + fColor[0] -= fDec; + fColor[1] -= fDec ; + fColor[2] -= fDec; + for (int i = 0; i < 3; i++) + { + if (fColor[i] <= fLowerLimit) + fColor[i] = fFullBright; + } + qglColor3fv(fColor); + } +} + + + + +/* +=================== +CurveTextureMatrix +=================== +*/ +void CurveTextureMatrix (face_t *f, vec3_t top, vec3_t bottom, float STfromUXYZ[2][5]) { + texdef_t *td; + vec3_t dir; + int i, j; + float sinv, cosv; + float ang; + float temp[2][4]; + float ns, nt; + float max; + + td = &f->texdef; + if (!td->scale[0]) + td->scale[0] = 1; + if (!td->scale[1]) + td->scale[1] = 1; + + temp[0][0] = 64; // assume 64 wide + temp[0][1] = 0; + temp[0][2] = 0; + temp[0][3] = 0; + + // calculate the T vector + VectorSubtract (top, bottom, dir); + VectorNormalize (dir); + max = 0; + j = 0; + for (i = 0 ; i < 3 ; i++) { + if (fabs(dir[i]) > max) { + max = fabs(dir[i]); + j = i; + } + } + if (dir[j] > 0) { + VectorSubtract (vec3_origin, dir, dir); + } + + temp[1][0] = 0; + temp[1][1] = dir[0]; + temp[1][2] = dir[1]; + temp[1][3] = dir[2]; + + + // rotate axis + if (td->rotate == 0) + { sinv = 0 ; cosv = 1; } + else if (td->rotate == 90) + { sinv = 1 ; cosv = 0; } + else if (td->rotate == 180) + { sinv = 0 ; cosv = -1; } + else if (td->rotate == 270) + { sinv = -1 ; cosv = 0; } + else + { + ang = td->rotate / 180 * Q_PI; + sinv = sin(ang); + cosv = cos(ang); + } + + for (i=0 ; i<4 ; i++) { + ns = cosv * temp[0][i] - sinv * temp[1][i]; + nt = sinv * temp[0][i] + cosv * temp[1][i]; + STfromUXYZ[0][i] = ns; + STfromUXYZ[1][i] = nt; + } + + // scale + for (i=0 ; i<2 ; i++) + for (j=0 ; j<4 ; j++) + STfromUXYZ[i][j] /= td->scale[i]; + + // shift + STfromUXYZ[0][4] = td->shift[0]; + STfromUXYZ[1][4] = td->shift[1]; + + // scale by texture + for (i = 0 ; i < 5 ; i++) { + STfromUXYZ[0][i] /= f->d_texture->width; + } + + for (i = 0 ; i < 5 ; i++) { + STfromUXYZ[1][i] /= f->d_texture->height; + } + +} + + +/* +=================== +OppositeFace +=================== +*/ +int OppositeFace (curveBlock_t *cb, int face) { + int i; + qboolean neighbor[6]; + + memset (neighbor, 0, sizeof(neighbor)); + for (i = 0 ; i < 4 ; i++) { + neighbor[cb->faces[face].neighbors[i]] = true; + } + + for (i = 0 ; i < 6 ; i++) { + if (i == face) { + continue; + } + if (!neighbor[i]) { + return i; + } + } + return 0; // should never happen +} + + +/* +================ +BrushToCurveBlock +================ +*/ +curveBlock_t *BrushToCurveBlock(brush_t *b) { + curveBlock_t *cb; + face_t *f; + int i, j, k, l; + winding_t *w; + int side; + int point; + int p1, p2; + + cb = reinterpret_cast(malloc(sizeof(*cb))); + memset (cb, 0, sizeof(*cb)); + side = 0; + point = 0; + + // see if it is a negative curve + for (f = b->brush_faces ; f ; f = f->next) { + if (f->texdef.contents & CONTENTS_NEGATIVE_CURVE) { + cb->negativeCurve = true; + break; + } + } + + // find all the shared points + for (f = b->brush_faces ; f ; f = f->next) { + if (side >= 6) { + free(cb); + return NULL; // must be six sided + } + + if (f->texdef.flags & SURF_CURVE_FAKE) { + continue; + } + + cb->faces[side].brushFace = f; + if (f->texdef.flags & SURF_CURVE) { + cb->faces[side].type = CS_CURVE; + } else { + cb->faces[side].type = CS_FLAT; + } + + w = f->face_winding; + + if (!w ) { + continue; + } + + if (w->numpoints != 4) { + free(cb); + return NULL; // must be four sided + } + for (i = 0 ; i < w->numpoints ; i++) { + for (j = 0 ; j < point ; j++) { + if (fabs(w->points[i][0] - cb->points[j][0]) < 0.1 + && fabs(w->points[i][1] - cb->points[j][1]) < 0.1 + && fabs(w->points[i][2] - cb->points[j][2]) < 0.1 ) { + break; + } + } + if (j == 8) { + free(cb); + return NULL; // must have eight points + } + VectorCopy (w->points[i], cb->points[j]); + cb->faces[side].points[i] = j; + if (j == point) { + point++; + } + } + side++; + } + + // find all the neighbor relations + for (i = 0 ; i < 6 ; i++) { + for (j = 0 ; j < 4 ; j++) { + p1 = cb->faces[i].points[j]; + p2 = cb->faces[i].points[(j+1)&3]; + + // find the other face that matches this edge + for (k = 0 ; k < 6 ; k++) { + for (l = 0 ; l < 4 ; l++) { + if (cb->faces[k].points[l] == p2 && + cb->faces[k].points[(l+1)&3] == p1) { + cb->faces[i].neighbors[j] = k; + break; + } + } + if (l < 4) { + break; + } + } + if (k == 6) { + free(cb); + return NULL; // couldn't match neighbor + } + } + } + + return cb; +} + + + +//==================================================================== + + +/* +================ +DrawFullFace +================ +*/ +void DrawFullFace (curveBlock_t *cb, cubeFace_t *cf) { + int k; + float *next; + + if (bevelBrush) { + return; + } + + // set the texturing matrix + FaceTextureVectors (cf->brushFace, STfromXYZ); + + + if (curveFile) { + vec3_t vecs[4]; + + for (k = 0 ; k < 4 ; k++) { + VectorCopy (cb->points[cf->points[k]], vecs[k]); + } + + fprintf (curveFile, "FACE {\n"); + fprintf (curveFile, "4\n"); + fprintf (curveFile, "textures/%s\n", cf->brushFace->texdef.name); + Write2DMatrix (curveFile, 2, 4, (float *)STfromXYZ); + Write2DMatrix (curveFile, 4, 3, (float *)vecs); + fprintf (curveFile, "}\n"); + return; + } + + qglBindTexture (GL_TEXTURE_2D, cf->brushFace->d_texture->texture_number); + + float fColor[3]; + SetColor(cf->brushFace, fColor); + + + qglBegin (GL_POLYGON); + for (k = 0 ; k < 4 ; k++) { + next = cb->points[cf->points[k]]; + qglTexCoord2f (DotProduct(next, STfromXYZ[0]) + STfromXYZ[0][3], + DotProduct(next, STfromXYZ[1]) + STfromXYZ[1][3]); + qglVertex3fv (next); + DecColor(fColor); + } + qglEnd (); +} + + +/* +=============== +DrawCurveFan + +Draws a curve as part of a flat surface +=============== +*/ +void DrawCurveFan (face_t *cf, vec5_t opposite, vec5_t prev, vec5_t peak, vec5_t next) { + int i, k, l; + float coef[5][3]; + + // write it out + if (curveFile) { + vec5_t vecs[4]; + + for ( i = 0 ; i < 5 ; i++ ) { + vecs[0][i] = opposite[i]; + vecs[1][i] = prev[i]; + vecs[2][i] = peak[i]; + vecs[3][i] = next[i]; + } + fprintf (curveFile, "CURVEFAN {\n"); + fprintf (curveFile, "textures/%s\n", cf->texdef.name); + Write2DMatrix (curveFile, 4, 5, (float *)vecs); + fprintf (curveFile, "}\n"); + return; + } + + // calculate the coefficients + for (l = 0 ; l < 5 ; l++) { + float a, b, c; + + a = prev[l]; + b = peak[l]; + c = next[l]; + coef[l][0] = a; + coef[l][1] = 2 * b - 2 * a; + coef[l][2] = a - 2 * b + c; + } + + + // draw it + qglBindTexture (GL_TEXTURE_2D, cf->d_texture->texture_number); + + + float fColor[3]; + SetColor(cf, fColor); + + + qglBegin (GL_TRIANGLE_FAN); + + qglTexCoord2fv( opposite + 3 ); + qglVertex3fv( opposite ); + + for ( k = 0 ; k <= CBLOCK_SUBDIVISIONS ; k++ ) { + vec5_t curve; + float f; + + f = (float)k / CBLOCK_SUBDIVISIONS; + for ( l = 0 ; l < 5 ; l++ ) { + curve[l] = coef[l][2]*f*f + coef[l][1]*f + coef[l][0]; + } + + qglTexCoord2fv( curve + 3 ); + qglVertex3fv( curve ); + DecColor(fColor); + } + + qglEnd (); +} + +/* +=============== +DrawRuledSurface +=============== +*/ +void DrawRuledSurface (face_t *cf, float ctrl[2][3][5] ) { + int j, k, l; + vec5_t curve[2][CBLOCK_SUBDIVISIONS+1]; + //float u; + float *v; + + if (curveFile) { + fprintf (curveFile, "RULED {\n"); + fprintf (curveFile, "textures/%s\n", cf->texdef.name); + Write3DMatrix (curveFile, 2, 3, 5, (float *)ctrl); + fprintf (curveFile, "}\n"); + return; + } + + for (j = 0 ; j < 2 ; j++) { + for (l = 0 ; l < 5 ; l++) { + float a, b, c; + float qA, qB, qC; + float f; + int k; + + a = ctrl[j][0][l]; + b = ctrl[j][1][l]; + c = ctrl[j][2][l]; + qA = a - 2 * b + c; + qB = 2 * b - 2 * a; + qC = a; + + for (k = 0 ; k <= CBLOCK_SUBDIVISIONS ; k++) { + f = (float)k / CBLOCK_SUBDIVISIONS; + curve[j][k][l] = qA*f*f + qB*f + qC; + } + } + } + + if ( bevelBrush ) { + face_t *f; + + for (k = 0 ; k < CBLOCK_SUBDIVISIONS ; k++) { + f = Face_Clone( bevelBrush->brush_faces ); + f->texdef.flags |= SURF_CURVE_FAKE; + f->next = bevelBrush->brush_faces; + bevelBrush->brush_faces = f; + + VectorCopy( curve[0][k], f->planepts[0] ); + VectorCopy( curve[1][k], f->planepts[1] ); + VectorCopy( curve[0][k+1], f->planepts[2] ); + Brush_MakeFacePlane( f ); + } + return; + } + + + + qglBindTexture (GL_TEXTURE_2D, cf->d_texture->texture_number); + + float fColor[3]; + SetColor(cf, fColor); + + qglBegin (GL_QUAD_STRIP); + for (k = 0 ; k <= CBLOCK_SUBDIVISIONS ; k++) { + v = curve[0][k]; + qglTexCoord2fv( v + 3 ); + qglVertex3fv( v ); + + v = curve[1][k]; + qglTexCoord2fv( v + 3 ); + qglVertex3fv( v ); + } + + + qglEnd (); +} + + +/* +=============== +SamplePatch +=============== +*/ +void SamplePatch (float ctrl[3][3][5], float u, float v, float out[5]) { + float vCtrl[3][5]; + int vPoint; + int axis; + + // find the control points for the v coordinate + for (vPoint = 0 ; vPoint < 3 ; vPoint++) { + for (axis = 0 ; axis < 5 ; axis++) { + float a, b, c; + float qA, qB, qC; + + a = ctrl[0][vPoint][axis]; + b = ctrl[1][vPoint][axis]; + c = ctrl[2][vPoint][axis]; + qA = a - 2 * b + c; + qB = 2 * b - 2 * a; + qC = a; + + vCtrl[vPoint][axis] = qA * u * u + qB * u + qC; + } + } + + // interpolate the v value + for (axis = 0 ; axis < 5 ; axis++) { + float a, b, c; + float qA, qB, qC; + + a = vCtrl[0][axis]; + b = vCtrl[1][axis]; + c = vCtrl[2][axis]; + qA = a - 2 * b + c; + qB = 2 * b - 2 * a; + qC = a; + + out[axis] = qA * v * v + qB * v + qC; + } +} + +/* +=================== +DrawPatch +=================== +*/ +void DrawPatch (face_t *cf, float ctrl[3][3][5]) { + int i, j; + float u, v; + vec5_t verts[CBLOCK_SUBDIVISIONS+1][CBLOCK_SUBDIVISIONS+1]; + + if (curveFile) { + fprintf (curveFile, "PATCH {\n"); + fprintf (curveFile, "textures/%s\n", cf->texdef.name); + Write3DMatrix (curveFile, 3, 3, 5, (float *)ctrl); + fprintf (curveFile, "}\n"); + return; + } + + for (i = 0 ; i <= CBLOCK_SUBDIVISIONS ; i++) { + for (j = 0 ; j <= CBLOCK_SUBDIVISIONS ; j++) { + u = (float)i / CBLOCK_SUBDIVISIONS; + v = (float)j / CBLOCK_SUBDIVISIONS; + SamplePatch (ctrl, u, v, verts[i][j]); + } + } + + if ( bevelBrush ) { + face_t *f; + vec3_t v0, v1, v2; + vec3_t d1, d2, cross; + + for (i = 0 ; i < CBLOCK_SUBDIVISIONS ; i++) { + for (j = 0 ; j < CBLOCK_SUBDIVISIONS ; j++) { + VectorCopy( verts[i][j], v0 ); + VectorCopy( verts[i][j+1], v1 ); + VectorCopy( verts[i+1][j], v2 ); + + VectorSubtract( v0, v1, d1 ); + VectorSubtract( v2, v1, d2 ); + CrossProduct( d1, d2, cross ); + if ( VectorLength( cross ) == 0 ) { + continue; // degenerate + } + f = Face_Clone( bevelBrush->brush_faces ); + f->texdef.flags |= SURF_CURVE_FAKE; + VectorCopy( v0, f->planepts[0] ); + VectorCopy( v1, f->planepts[1] ); + VectorCopy( v2, f->planepts[2] ); + Brush_MakeFacePlane( f ); + f->next = bevelBrush->brush_faces; + bevelBrush->brush_faces = f; + } + } + return; + } + + + + qglBindTexture (GL_TEXTURE_2D, cf->d_texture->texture_number); + + float fColor[3]; + SetColor(cf, fColor); + + + for (i = 0 ; i < CBLOCK_SUBDIVISIONS ; i++) { + qglBegin (GL_QUAD_STRIP); + for (j = 0 ; j <= CBLOCK_SUBDIVISIONS ; j++) { + qglTexCoord2fv( verts[i+1][j] + 3 ); + qglVertex3fv( verts[i+1][j] ); + qglTexCoord2fv( verts[i][j] + 3 ); + qglVertex3fv( verts[i][j] ); + DecColor(fColor); + } + qglEnd (); + } +} + + +//==================================================================== + +/* +=================== +DrawCurveBlock +=================== +*/ +void DrawCurveBlock (curveBlock_t *cb) { + qboolean curveDrawn; + cubeFace_t *cf, *side; + int cfNum, j, k; + float *prev, *peak, *next, *opposite; + float *tTop, *tBottom; + float ctrl[3][3][5]; + + // the brush has only two or three curve sides, so draw it + curveDrawn = false; + + for (cfNum = 0 ; cfNum < 6 ; cfNum++) { + cf = &cb->faces[cfNum]; + if (cf->type == CS_SPLIT) { + // split sides are never drawn +// cf->type = CS_FLAT; + continue; + } + + if (cf->type == CS_FLAT) { +drawFlat: + // the neighbors of flat sides determine the outline + + // find the neighbor curve sode + for (j = 0 ; j < 4 ; j++) { + side = &cb->faces[cf->neighbors[j]]; + if (side->type == CS_CURVE) { + break; + } + } + + // make sure the first curve didn't start at the last slot + if (j == 0) { + if (cb->faces[cf->neighbors[3]].type == CS_CURVE) { + j = 3; + side = &cb->faces[cf->neighbors[j]]; + } + } + + if ( j == 4 || cb->faces[cf->neighbors[(j+1)&3]].type != CS_CURVE) { + // no double curves + if (!cb->negativeCurve) { + DrawFullFace (cb, cf); + } + continue; + } + + // set the texture coordinates for all control points + FaceTextureVectors (cf->brushFace, STfromXYZ); + for ( k = 0 ; k < 4 ; k++ ) { + float *v; + + v = cb->points[cf->points[k]]; + v[3] = DotProduct( v, STfromXYZ[0] ) + STfromXYZ[0][3]; + v[4] = DotProduct( v, STfromXYZ[1] ) + STfromXYZ[1][3]; + } + + // draw the curve first + if (cb->negativeCurve) { + next = cb->points[cf->points[j]]; + peak = cb->points[cf->points[(j+1)&3]]; + prev = cb->points[cf->points[(j+2)&3]]; + + // opposite corner first + DrawCurveFan (cf->brushFace, peak, prev, peak, next); + } else { + prev = cb->points[cf->points[j]]; + peak = cb->points[cf->points[(j+1)&3]]; + next = cb->points[cf->points[(j+2)&3]]; + + // opposite corner first + opposite = cb->points[cf->points[(j+3)&3]]; + DrawCurveFan (cf->brushFace, opposite, prev, peak, next); + } + continue; + } + + memset( ctrl, 0, sizeof(ctrl) ); + + if (cf->type != CS_CURVE) { + continue; + } + + if (cb->negativeCurve) { + FaceTextureVectors (cf->brushFace, STfromXYZ); + qglBindTexture (GL_TEXTURE_2D, cf->brushFace->d_texture->texture_number); + DrawFullFace (cb, cf); + } + + if (curveDrawn) { + continue; + } + curveDrawn = true; + + // find the neighboring curved side + for (j = 0 ; j < 4 ; j++) { + side = &cb->faces[cf->neighbors[j]]; + if (side->type == CS_CURVE) { + break; + } + } + + // make sure the first curve didn't start at the last slot + if (j == 0) { + if (cb->faces[cf->neighbors[3]].type == CS_CURVE) { + j = 3; + side = &cb->faces[cf->neighbors[j]]; + } + } + + // there should never be a curve without a neighboring + // curve unless a designer has manually messed it up + if (j == 4) { + goto drawFlat; + } + + // + // if three sides are curved, draw a sphere section + // + if (cb->faces[cf->neighbors[(j+1)&3]].type == CS_CURVE + || cb->faces[cf->neighbors[(j+3)&3]].type == CS_CURVE ){ + + VectorCopy (cb->points[cf->points[(j+3)&3]], ctrl[0][0]); + VectorCopy (cb->points[cf->points[(j+3)&3]], ctrl[1][0]); + VectorCopy (cb->points[cf->points[(j+3)&3]], ctrl[2][0]); + + VectorCopy (cb->points[cf->points[(j+0)&3]], ctrl[0][1]); + VectorCopy (cb->points[cf->points[(j+1)&3]], ctrl[1][1]); + VectorCopy (cb->points[cf->points[(j+2)&3]], ctrl[2][1]); + + for (k = 0 ; k < 4 ; k++) { + if (side->points[k] == cf->points[j]) { + break; + } + } + VectorCopy (cb->points[side->points[(k+1)&3]], ctrl[0][2]); + VectorCopy (cb->points[side->points[(k+2)&3]], ctrl[1][2]); + + side = &cb->faces[cf->neighbors[(j+1)&3]]; + for (k = 0 ; k < 4 ; k++) { + if (side->points[k] == cf->points[(j+2)&3]) { + break; + } + } + VectorCopy (cb->points[side->points[(k+3)&3]], ctrl[2][2]); + + if (cb->negativeCurve) { + vec3_t temp; + + for (j = 0 ; j < 3 ; j++) { + VectorCopy (ctrl[j][0], temp); + VectorCopy (ctrl[j][2], ctrl[j][0]); + VectorCopy (temp, ctrl[j][2]); + } + } + + // calculate texture coordinates for the control points + FaceTextureVectors (cf->brushFace, STfromXYZ); + for ( j = 0 ; j < 3 ; j++ ) { + for ( k = 0 ; k < 3 ; k++ ) { + ctrl[j][k][3] = DotProduct( ctrl[j][k], STfromXYZ[0] ) + + STfromUXYZ[0][3]; + ctrl[j][k][4] = DotProduct( ctrl[j][k], STfromXYZ[1] ) + + STfromUXYZ[1][3]; + } + } + // draw it + DrawPatch (cf->brushFace, ctrl); + continue; + } + + // + // the curve is a cylinder section + // + + // prev + VectorCopy (cb->points[cf->points[(j+2)&3]], ctrl[0][0]); + VectorCopy (cb->points[cf->points[(j+3)&3]], ctrl[1][0]); + + // peak + VectorCopy (cb->points[cf->points[(j+1)&3]], ctrl[0][1]); + VectorCopy (cb->points[cf->points[(j+0)&3]], ctrl[1][1]); + + // next + for (k = 0 ; k < 4 ; k++) { + if (side->points[k] == cf->points[j]) { + break; + } + } + VectorCopy (cb->points[side->points[(k+2)&3]], ctrl[0][2]); + VectorCopy (cb->points[side->points[(k+1)&3]], ctrl[1][2]); + + if (cb->negativeCurve) { + vec3_t temp; + + for (k = 0 ; k < 2 ; k++) { + VectorCopy (ctrl[k][0], temp); + VectorCopy (ctrl[k][2], ctrl[k][0]); + VectorCopy (temp, ctrl[k][2]); + } + } + + // find the edge opposite peak to use as the T texture axis + side = &cb->faces[cf->neighbors[(j+3)&3]]; + for (k = 0 ; k < 4 ; k++) { + if (side->points[k] == cf->points[j]) { + break; + } + } + tTop = cb->points[side->points[(k+2)&3]]; + + side = &cb->faces[cf->neighbors[(j+1)&3]]; + for (k = 0 ; k < 4 ; k++) { + if (side->points[k] == cf->points[(j+1)&3]) { + break; + } + } + tBottom = cb->points[side->points[(k+2)&3]]; + + // set the texturing matrix + CurveTextureMatrix (cf->brushFace, tTop, tBottom, STfromUXYZ); + // calculate texture coordinates at control points + for ( j = 0 ; j < 2 ; j++ ) { + for ( k = 0 ; k < 3 ; k++ ) { + ctrl[j][k][3] = DotProduct( ctrl[j][k], (STfromUXYZ[0]+1) ) + + STfromUXYZ[0][0] * (k / 2.0) + STfromUXYZ[0][4]; + ctrl[j][k][4] = DotProduct( ctrl[j][k], (STfromUXYZ[1]+1) ) + + STfromUXYZ[1][0] * (k / 2.0) + STfromUXYZ[1][4]; + } + } + DrawRuledSurface (cf->brushFace, ctrl); + } +} + +/* +=================== +SubdivideCurveBlock + +The curve block will be freed before return. +=================== +*/ +void SubdivideCurveBlock (curveBlock_t *cb) { + int i, j, k, l; + curveBlock_t *b1, *b2; + cubeFace_t *cf, *side; + int p1, p2; + + // if any curved side has curves on both + // sides, it must be subdivided + for (i = 0 ; i < 6 ; i++) { + cf = &cb->faces[i]; + if (cf->type != CS_CURVE) { + continue; + } + + for (j = 0 ; j < 2 ; j++) { + if (cb->faces[cf->neighbors[j]].type == CS_CURVE + && cb->faces[cf->neighbors[j+2]].type == CS_CURVE) { + break; + } + } + if (j == 2) { + continue; + } + + // subdivide it + b1 = reinterpret_cast(malloc(sizeof(*b1))); + *b1 = *cb; + b2 = reinterpret_cast(malloc(sizeof(*b2))); + *b2 = *cb; + + b1->faces[cf->neighbors[j+2]].type = CS_SPLIT; + b2->faces[cf->neighbors[j]].type = CS_SPLIT; + + p1 = cf->points[(j+3)&3]; + p2 = cf->points[j]; + for (k = 0 ; k < 3 ; k++) { + b1->points[p1][k] = b2->points[p2][k] = + 0.5*(cb->points[p1][k] + cb->points[p2][k]); + } + + side = &cb->faces[cf->neighbors[(j+3)&3]]; + for (l = 0 ; l < 4 ; l++) { + if (side->points[l] == p2 && side->points[(l+1)&3] == p1) { + break; + } + } + + p2 = side->points[(l+3)&3]; + p1 = side->points[(l+2)&3]; + for (k = 0 ; k < 3 ; k++) { + b1->points[p1][k] = b2->points[p2][k] = + 0.5*(cb->points[p1][k] + cb->points[p2][k]); + } + + + + p1 = cf->points[(j+2)&3]; + p2 = cf->points[(j+1)&3]; + for (k = 0 ; k < 3 ; k++) { + b1->points[p1][k] = b2->points[p2][k] = + 0.5*(cb->points[p1][k] + cb->points[p2][k]); + } + + + side = &cb->faces[cf->neighbors[(j+1)&3]]; + for (l = 0 ; l < 4 ; l++) { + if (side->points[l] == p1 && side->points[(l+1)&3] == p2) { + break; + } + } + + p2 = side->points[(l+2)&3]; + p1 = side->points[(l+3)&3]; + for (k = 0 ; k < 3 ; k++) { + b1->points[p1][k] = b2->points[p2][k] = + 0.5*(cb->points[p1][k] + cb->points[p2][k]); + } + + + free (cb); + SubdivideCurveBlock (b1); + SubdivideCurveBlock (b2); + return; + } + + // the cb is in canonical form + DrawCurveBlock (cb); + free (cb); +} + + + +/* +=============== +Curve_Invert +=============== +*/ +void Curve_Invert (void) { + brush_t *b; + face_t *f; + qboolean curve, negative; + + if (!QE_SingleBrush()) + return; + + b = selected_brushes.next; + + curve = false; + negative = false; + for (f = b->brush_faces ; f ; f = f->next) { + if (f->texdef.contents & CONTENTS_NEGATIVE_CURVE) { + negative = true; + } + if (f->texdef.flags & SURF_CURVE) { + curve = true; + } + } + if (!curve) { + return; + } + + negative ^= 1; + for (f = b->brush_faces ; f ; f = f->next) { + if (negative) { + f->texdef.contents |= CONTENTS_NEGATIVE_CURVE; + } else { + f->texdef.contents &= ~CONTENTS_NEGATIVE_CURVE; + } + } + + Curve_BuildPoints (b); + Sys_UpdateWindows (W_ALL); +} + +/* +=================== +Curve_MakeCurvedBrush +=================== +*/ +void Curve_MakeCurvedBrush (qboolean negative, qboolean top, qboolean bottom, + qboolean s1, qboolean s2, qboolean s3, qboolean s4) { + brush_t *b; + curveBlock_t *cb; + cubeFace_t *cf; + int i; + float best; + int bestFace; + int opposite; + face_t *f; + + if (!QE_SingleBrush()) + return; + + b = selected_brushes.next; + cb = BrushToCurveBlock(b); + if (!cb) { + return; + } + + // set as detail and clear all curve flags + for (f = b->brush_faces ; f ; f = f->next ) { + f->texdef.flags &= ~SURF_KEEP; + f->texdef.contents |= CONTENTS_DETAIL; + if (negative) { + f->texdef.contents |= CONTENTS_NEGATIVE_CURVE; + } else { + f->texdef.contents &= ~CONTENTS_NEGATIVE_CURVE; + } + } + + // find the top face + best = MIN_WORLD_COORD; + bestFace = 0; + + for (i = 0 ; i < 6 ; i++) { + if (cb->faces[i].brushFace->plane.normal[2] > best) { + best = cb->faces[i].brushFace->plane.normal[2]; + bestFace = i; + } + } + + cf = &cb->faces[bestFace]; + if (top) { + cf->brushFace->texdef.flags |= SURF_CURVE; + } + if (bottom) { + opposite = OppositeFace(cb, bestFace); + cb->faces[opposite].brushFace->texdef.flags |= SURF_CURVE; + } + if (s1) { + cb->faces[cf->neighbors[0]].brushFace->texdef.flags |= SURF_CURVE; + } + if (s2) { + cb->faces[cf->neighbors[1]].brushFace->texdef.flags |= SURF_CURVE; + } + if (s3) { + cb->faces[cf->neighbors[2]].brushFace->texdef.flags |= SURF_CURVE; + } + if (s4) { + cb->faces[cf->neighbors[3]].brushFace->texdef.flags |= SURF_CURVE; + } + + Curve_BuildPoints (b); + Sys_UpdateWindows (W_ALL); + +} + + +/* +================ +Curve_AddFakePlanes + +Call before saving the map to generate the extra clipping planes +the game uses for collision testing +================ +*/ +void Curve_AddFakePlanes( brush_t *b ) { + curveBlock_t *cb; + + // generate CURVE_FAKE faces + bevelBrush = b; + + cb = BrushToCurveBlock(b); + if ( !cb ) { + b->curveBrush = false; + bevelBrush = NULL; + return; + } + SubdivideCurveBlock (cb); + + bevelBrush = NULL; +} + + +/* +================ +Curve_StripFakePlanes + +Strips out any fakeplanes +================ +*/ +void Curve_StripFakePlanes( brush_t *b ) { + face_t *f; + face_t **ptr; //, **next; + + // remove any CURVE_FAKE faces + for ( ptr = &b->brush_faces ; *ptr ; ) { + f = *ptr; + if ( f->texdef.flags & SURF_CURVE_FAKE ) { + *ptr = f->next; + Face_Free( f ); + } else { + ptr = &f->next; + } + } +} + + +/* +================ +Curve_BuildPoints + +================ +*/ +void Curve_BuildPoints (brush_t *b) { + face_t *f; + + b->curveBrush = false; + for (f=b->brush_faces ; f ; f=f->next) { + if (f->texdef.flags & SURF_CURVE) { + b->curveBrush = true; + break; + } + } + if (!b->curveBrush) { + return; + } + + // FIXME: build display list here? +} + +//================================================================= + + +/* +=============== +Curve_CameraDraw +=============== +*/ +void Curve_CameraDraw (brush_t *b) { + curveBlock_t *cb; + + cb = BrushToCurveBlock(b); + if (!cb) { + b->curveBrush = false; + return; + } + + g_bCamPaint = true; + qglColor3f (1,1,1); + SubdivideCurveBlock (cb); +} + + +/* +=============== +Curve_XYDraw +=============== +*/ +void Curve_XYDraw (brush_t *b) { + curveBlock_t *cb; + + cb = BrushToCurveBlock(b); + if (!cb) { + b->curveBrush = false; + return; + } + + g_bCamPaint = false; + qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE); + SubdivideCurveBlock (cb); + qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL); +} + +//================================================================ + + +void Write1DMatrix (FILE *f, int x, float *m) { + int i; + + fprintf (f, "( "); + for (i = 0 ; i < x ; i++) { + if (m[i] == (int)m[i] ) { + fprintf (f, "%i ", (int)m[i]); + } else { + fprintf (f, "%f ", m[i]); + } + } + fprintf (f, ")"); +} + +void Write2DMatrix (FILE *f, int y, int x, float *m) { + int i; + + fprintf (f, "( "); + for (i = 0 ; i < y ; i++) { + Write1DMatrix (f, x, m + i*x); + fprintf (f, " "); + } + fprintf (f, ")\n"); +} + + +void Write3DMatrix (FILE *f, int z, int y, int x, float *m) { + int i; + + fprintf (f, "(\n"); + for (i = 0 ; i < z ; i++) { + Write2DMatrix (f, y, x, m + i*(x*y) ); + } + fprintf (f, ")\n"); +} + +void Write1DMatrix (CMemFile *f, int x, float *m) { + int i; + + MemFile_fprintf (f, "( "); + for (i = 0 ; i < x ; i++) { + if (m[i] == (int)m[i] ) { + MemFile_fprintf (f, "%i ", (int)m[i]); + } else { + MemFile_fprintf (f, "%f ", m[i]); + } + } + MemFile_fprintf (f, ")"); +} + +void Write2DMatrix (CMemFile *f, int y, int x, float *m) { + int i; + + MemFile_fprintf (f, "( "); + for (i = 0 ; i < y ; i++) { + Write1DMatrix (f, x, m + i*x); + MemFile_fprintf (f, " "); + } + MemFile_fprintf (f, ")\n"); +} + + +void Write3DMatrix (CMemFile *f, int z, int y, int x, float *m) { + int i; + + MemFile_fprintf (f, "(\n"); + for (i = 0 ; i < z ; i++) { + Write2DMatrix (f, y, x, m + i*(x*y) ); + } + MemFile_fprintf (f, ")\n"); +} + + + +/* +=============== +Curve_WriteFile +=============== +*/ +void Curve_WriteFile (char *name) { + char curveName[1024]; + brush_t *b; + curveBlock_t *cb; + time_t ltime; + + strcpy(curveName, name); + StripExtension (curveName); + strcat (curveName, ".bnd"); + curveFile = fopen(curveName, "w"); + if (!curveFile) { + return; + } + + time(<ime); + fprintf (curveFile, "// %s saved on %s\n", name, ctime(<ime) ); + + for (b=world_entity->brushes.onext ; b != &world_entity->brushes + ; b=b->onext) { + if (!b->curveBrush) { + continue; // only write curve brushes + } + + cb = BrushToCurveBlock(b); + SubdivideCurveBlock (cb); + + } + fclose (curveFile); + + curveFile = NULL; +} + + +#if 0 +void Curve_BevelBrush( brush_t *b ) { + curveBlock_t *cb; + face_t *f; + + // make a copy without any curve flags, but keeping the negative flag + bevelBrush = Brush_Clone( b ); + for (f = bevelBrush->brush_faces ; f ; f = f->next) { + f->texdef.flags &= ~SURF_CURVE; + } + bevelBrush->curveBrush = false; + + cb = BrushToCurveBlock(b); + SubdivideCurveBlock (cb); + + Brush_Build( bevelBrush ); + Brush_AddToList( bevelBrush, &active_brushes ); + Entity_LinkBrush( b->owner, bevelBrush ); + + Sys_UpdateWindows (W_ALL); + + bevelBrush = NULL; +} +#endif diff --git a/utils/Radiant/Quake3/pmesh.cpp b/utils/Radiant/Quake3/pmesh.cpp new file mode 100644 index 0000000..b815bca --- /dev/null +++ b/utils/Radiant/Quake3/pmesh.cpp @@ -0,0 +1,5015 @@ +// +// Preliminary patch stuff +// +// + + +#include "stdafx.h" +#include "..\qe3.h" +#include "..\DialogInfo.h" +#include "..\CapDialog.h" +#include "..\groupnames.h" + +// externs +extern void MemFile_fprintf(CMemFile* pMemFile, const char* pText, ...); +extern face_t *Face_Alloc( void ); +void _Write3DMatrix (FILE *f, int y, int x, int z, float *m); +void _Write3DMatrix (CMemFile *f, int y, int x, int z, float *m); + + + +#define CBLOCK_SUBDIVISIONS 6 + + +#define MIN_PATCH_WIDTH 3 +#define MIN_PATCH_HEIGHT 3 + +#define MAX_PATCH_WIDTH 16 +#define MAX_PATCH_HEIGHT 16 + +// patch type info +// type in lower 16 bits, flags in upper +// endcaps directly follow this patch in the list + +// types +#define PATCH_GENERIC 0x00000000 // generic flat patch +#define PATCH_CYLINDER 0x00000001 // cylinder +#define PATCH_BEVEL 0x00000002 // bevel +#define PATCH_ENDCAP 0x00000004 // endcap +#define PATCH_HEMISPHERE 0x00000008 // hemisphere +#define PATCH_CONE 0x00000010 // cone + +// behaviour styles +#define PATCH_CAP 0x00001000 // flat patch applied as a cap +#define PATCH_SEAM 0x00002000 // flat patch applied as a seam +#define PATCH_THICK 0x00004000 // patch applied as a thick portion + +// styles +#define PATCH_BEZIER 0x00000000 // default bezier +#define PATCH_BSPLINE 0x10000000 // bspline + +#define PATCH_TYPEMASK 0x00000fff // +#define PATCH_BTYPEMASK 0x0000f000 // +#define PATCH_STYLEMASK 0xffff0000 // + +typedef struct { + vec3_t xyz; + float st[2]; + float lightmap[2]; + vec3_t normal; +} drawVert_t; + + + +typedef struct { + int width, height; // in control points, not patches + int contents, flags, value, type; + qtexture_t *d_texture; + //--float ctrl[MAX_PATCH_WIDTH][MAX_PATCH_HEIGHT][5]; + drawVert_t ctrl[MAX_PATCH_WIDTH][MAX_PATCH_HEIGHT]; + brush_t *pSymbiot; + bool bSelected; + bool bOverlay; + bool bDirty; + int nListID; +} patchMesh_t; + + +// FIXME: this needs to be dynamic +#define MAX_PATCH_MESHES (4096 + 1024) +patchMesh_t patchMeshes[MAX_PATCH_MESHES]; +int numPatchMeshes = 0; + +// used for a save spot +patchMesh_t patchSave; + +// Tracks the selected patch for point manipulation/update. FIXME: Need to revert back to a generalized +// brush approach +//--int g_nSelectedPatch = -1; + +// HACK: for tracking which view generated the click +// as we dont want to deselect a point on a same point +// click if it is from a different view +int g_nPatchClickedView = -1; +bool g_bSameView = false; + + +// globals +bool g_bPatchShowBounds = true; +bool g_bPatchWireFrame = false; +bool g_bPatchWeld = true; +bool g_bPatchDrillDown = true; +bool g_bPatchInsertMode = false; +bool g_bPatchBendMode = false; +int g_nPatchBendState = -1; +int g_nPatchInsertState = -1; +int g_nBendOriginIndex = 0; +vec3_t g_vBendOrigin; + +bool g_bPatchAxisOnRow = true; +int g_nPatchAxisIndex = 0; +bool g_bPatchLowerEdge = true; + +// BEND states +enum +{ + BEND_SELECT_ROTATION = 0, + BEND_SELECT_ORIGIN, + BEND_SELECT_EDGE, + BEND_BENDIT, + BEND_STATE_COUNT +}; + +const char *g_pBendStateMsg[] = +{ + "Use TAB to cycle through available bend axis. Press ENTER when the desired one is highlighted.", + "Use TAB to cycle through available rotation axis. This will LOCK around that point. You may also use Shift + Middle Click to select an arbitrary point. Press ENTER when the desired one is highlighted", + "Use TAB to choose which side to bend. Press ENTER when the desired one is highlighted.", + "Use the MOUSE to bend the patch. It uses the same ui rules as Free Rotation. Press ENTER to accept the bend, press ESC to abandon it and exit Bend mode", + "" +}; + +// INSERT states +enum +{ + INSERT_SELECT_EDGE = 0, + INSERT_STATE_COUNT +}; + +const char* g_pInsertStateMsg[] = +{ + "Use TAB to cycle through available rows/columns for insertion/deletion. Press INS to insert at the highlight, DEL to remove the pair" +}; + + +float *g_InversePoints[1024]; + +const float fFullBright = 1.0; +const float fLowerLimit = .50; +const float fDec = .05; +void _SetColor(face_t* f, float fColor[3]) +{ + return; + fColor[0] = f->d_color[0]; + fColor[1] = f->d_color[1]; + fColor[2] = f->d_color[2]; + qglColor3fv(fColor); +} + + +void _DecColor(float fColor[3]) +{ + return; + fColor[0] -= fDec; + fColor[1] -= fDec ; + fColor[2] -= fDec; + for (int i = 0; i < 3; i++) + { + if (fColor[i] <= fLowerLimit) + { + fColor[0] = fFullBright; + fColor[1] = fFullBright; + fColor[2] = fFullBright; + break; + } + } + qglColor3fv(fColor); +} + +#define VectorClear(x) {x[0] = x[1] = x[2] = 0;} +vec_t __VectorNormalize (vec3_t in, vec3_t out) +{ + vec_t length, ilength; + + length = sqrt (in[0]*in[0] + in[1]*in[1] + in[2]*in[2]); + if (length == 0) + { + VectorClear (out); + return 0; + } + + ilength = 1.0/length; + out[0] = in[0]*ilength; + out[1] = in[1]*ilength; + out[2] = in[2]*ilength; + + return length; +} + + +void Patch_SetType(patchMesh_t *p, int nType) +{ + p->type = (p->type & PATCH_STYLEMASK) | nType; +} + +void Patch_SetStyle(patchMesh_t *p, int nStyle) +{ + p->type = (p->type & PATCH_TYPEMASK) | nStyle; +} + + + + +/* +=============== +InterpolateInteriorPoints +=============== +*/ +void InterpolateInteriorPoints( patchMesh_t *p ) +{ + int i, j, k; + int next, prev; + + for ( i = 0 ; i < p->width ; i += 2 ) + { + + next = ( i == p->width - 1 ) ? 1 : ( i + 1 ) % p->width; + prev = ( i == 0 ) ? p->width - 2 : i - 1; + +#if 0 + if ( i == 0 ) + { + next = ( i + 1 ) % p->width; + prev = p->width - 2; // joined wrap case + } + else if ( i == p->width - 1 ) + { + next = 1; + prev = i - 1; + } + else + { + next = ( i + 1 ) % p->width; + prev = i - 1; + } +#endif + + for ( j = 0 ; j < p->height ; j++ ) + { + for ( k = 0 ; k < 3 ; k++ ) + { + p->ctrl[i][j].xyz[k] = ( p->ctrl[next][j].xyz[k] + p->ctrl[prev][j].xyz[k] ) * 0.5; + } + } + } +} + +/* +================= +MakeMeshNormals + +================= +*/ +int neighbors[8][2] = { + {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1} +}; + +void Patch_MeshNormals(patchMesh_t& in ) +{ + int i, j, k, dist; + vec3_t normal; + vec3_t sum; + int count; + vec3_t base; + vec3_t delta; + int x, y; + drawVert_t *dv; + vec3_t around[8], temp; + qboolean good[8]; + qboolean wrapWidth, wrapHeight; + float len; + + wrapWidth = false; + for ( i = 0 ; i < in.height ; i++ ) + { + VectorSubtract( in.ctrl[i*in.width]->xyz, + in.ctrl[i*in.width+in.width-1]->xyz, delta ); + len = VectorLength( delta ); + if ( len > 1.0 ) + { + break; + } + } + if ( i == in.height ) + { + wrapWidth = true; + } + + wrapHeight = false; + for ( i = 0 ; i < in.width ; i++ ) + { + VectorSubtract( in.ctrl[i]->xyz, + in.ctrl[i + (in.height-1)*in.width]->xyz, delta ); + len = VectorLength( delta ); + if ( len > 1.0 ) + { + break; + } + } + if ( i == in.width) + { + wrapHeight = true; + } + + + for ( i = 0 ; i < in.width ; i++ ) + { + for ( j = 0 ; j < in.height ; j++ ) + { + count = 0; + //--dv = reinterpret_cast(in.ctrl[j*in.width+i]); + dv = &in.ctrl[i][j]; + VectorCopy( dv->xyz, base ); + for ( k = 0 ; k < 8 ; k++ ) + { + VectorClear( around[k] ); + good[k] = false; + + for ( dist = 1 ; dist <= 3 ; dist++ ) + { + x = i + neighbors[k][0] * dist; + y = j + neighbors[k][1] * dist; + if ( wrapWidth ) + { + if ( x < 0 ) + { + x = in.width - 1 + x; + } + else if ( x >= in.width ) + { + x = 1 + x - in.width; + } + } + if ( wrapHeight ) + { + if ( y < 0 ) + { + y = in.height - 1 + y; + } + else if ( y >= in.height ) + { + y = 1 + y - in.height; + } + } + + if ( x < 0 || x >= in.width || y < 0 || y >= in.height ) + { + break; // edge of patch + } + //--VectorSubtract( in.ctrl[y*in.width+x]->xyz, base, temp ); + VectorSubtract( in.ctrl[x][y].xyz, base, temp ); + if ( __VectorNormalize( temp, temp ) == 0 ) + { + continue; // degenerate edge, get more dist + } + else + { + good[k] = true; + VectorCopy( temp, around[k] ); + break; // good edge + } + } + } + + VectorClear( sum ); + for ( k = 0 ; k < 8 ; k++ ) + { + if ( !good[k] || !good[(k+1)&7] ) + { + continue; // didn't get two points + } + CrossProduct( around[(k+1)&7], around[k], normal ); + if ( __VectorNormalize( normal, normal ) == 0 ) + { + continue; + } + VectorAdd( normal, sum, sum ); + count++; + } + if ( count == 0 ) + { + //printf("bad normal\n"); + count = 1; + //continue; + } + __VectorNormalize( sum, dv->normal ); + } + } +} + + + + +/* +================== +Patch_CalcBounds +================== +*/ +void Patch_CalcBounds(patchMesh_t *p, vec3_t& vMin, vec3_t& vMax) +{ + vMin[0] = vMin[1] = vMin[2] = MAX_WORLD_COORD; + vMax[0] = vMax[1] = vMax[2] = MIN_WORLD_COORD; + + p->bDirty = true; + for (int w = 0; w < p->width; w++) + { + for (int h = 0; h < p->height; h++) + { + for (int j = 0; j < 3; j++) + { + float f = p->ctrl[w][h].xyz[j]; + if (f < vMin[j]) + vMin[j] = f; + if (f > vMax[j]) + vMax[j] = f; + } + } + } +} + +/* +================== +Brush_RebuildBrush +================== +*/ +void Brush_RebuildBrush(brush_t *b, vec3_t vMins, vec3_t vMaxs) +{ + // + // Total hack job + // Rebuilds a brush + int i, j; + face_t *f, *next; + vec3_t pts[4][2]; + texdef_t texdef; + // free faces + + for (j = 0; j < 3; j++) + { + if ((int)vMins[j] == (int)vMaxs[j]) + { + vMins[j] -= 4; + vMaxs[j] += 4; + } + } + + + for (f=b->brush_faces ; f ; f=next) + { + next = f->next; + if (f) + texdef = f->texdef; + Face_Free( f ); + } + + b->brush_faces = NULL; + + // left the last face so we can use its texdef + + for (i=0 ; i<3 ; i++) + if (vMaxs[i] < vMins[i]) + Error ("Brush_RebuildBrush: backwards"); + + pts[0][0][0] = vMins[0]; + pts[0][0][1] = vMins[1]; + + pts[1][0][0] = vMins[0]; + pts[1][0][1] = vMaxs[1]; + + pts[2][0][0] = vMaxs[0]; + pts[2][0][1] = vMaxs[1]; + + pts[3][0][0] = vMaxs[0]; + pts[3][0][1] = vMins[1]; + + for (i=0 ; i<4 ; i++) + { + pts[i][0][2] = vMins[2]; + pts[i][1][0] = pts[i][0][0]; + pts[i][1][1] = pts[i][0][1]; + pts[i][1][2] = vMaxs[2]; + } + + for (i=0 ; i<4 ; i++) + { + f = Face_Alloc(); + f->texdef = texdef; + f->texdef.flags &= ~SURF_KEEP; + f->texdef.contents &= ~CONTENTS_KEEP; + f->texdef.flags |= SURF_PATCH; + f->next = b->brush_faces; + b->brush_faces = f; + j = (i+1)%4; + + VectorCopy (pts[j][1], f->planepts[0]); + VectorCopy (pts[i][1], f->planepts[1]); + VectorCopy (pts[i][0], f->planepts[2]); + } + + f = Face_Alloc(); + f->texdef = texdef; + f->texdef.flags &= ~SURF_KEEP; + f->texdef.contents &= ~CONTENTS_KEEP; + f->texdef.flags |= SURF_PATCH; + f->next = b->brush_faces; + b->brush_faces = f; + + VectorCopy (pts[0][1], f->planepts[0]); + VectorCopy (pts[1][1], f->planepts[1]); + VectorCopy (pts[2][1], f->planepts[2]); + + f = Face_Alloc(); + f->texdef = texdef; + f->texdef.flags &= ~SURF_KEEP; + f->texdef.contents &= ~CONTENTS_KEEP; + f->texdef.flags |= SURF_PATCH; + f->next = b->brush_faces; + b->brush_faces = f; + + VectorCopy (pts[2][0], f->planepts[0]); + VectorCopy (pts[1][0], f->planepts[1]); + VectorCopy (pts[0][0], f->planepts[2]); + + Brush_Build(b); +} + +void Patch_Rebuild(patchMesh_t *p) +{ + vec3_t vMin, vMax; + Patch_CalcBounds(p, vMin, vMax); + Brush_RebuildBrush(p->pSymbiot, vMin, vMax); + p->bDirty = true; +} + + + +/* +================== +AddBrushForPatch +================== + adds a patch brush and ties it to this patch id +*/ +brush_t* AddBrushForPatch(int n, bool bLinkToWorld = true) +{ + // find the farthest points in x,y,z + vec3_t vMin, vMax; + Patch_CalcBounds(&patchMeshes[n], vMin, vMax); + + for (int j = 0; j < 3; j++) + { + if (vMin[j] == vMax[j]) + { + vMin[j] -= 4; + vMax[j] += 4; + } + } + + brush_t *b = Brush_Create(vMin, vMax, &g_qeglobals.d_texturewin.texdef); + face_t *f; + for (f=b->brush_faces ; f ; f=f->next) + { + f->texdef.flags |= SURF_PATCH; + } + + // FIXME: this entire type of linkage needs to be fixed + b->nPatchID = n; + patchMeshes[n].pSymbiot = b; + patchMeshes[n].bSelected = false; + patchMeshes[n].bOverlay = false; + patchMeshes[n].bDirty = true; + patchMeshes[n].nListID = -1; + + if (bLinkToWorld) + { + Brush_AddToList (b, &active_brushes); + Entity_LinkBrush (world_entity, b); + Brush_Build(b); + } + + return b; +} + +void Patch_SetPointIntensities(int n) +{ +#if 0 + patchMesh_t *p = patchMeshes[n]; + for (int i = 0; i < p->width; i++) + { + for (int j = 0; j < p->height; j++) + { + + } + } +#endif +} + +// very approximate widths and heights + +/* +================== +Patch_Width +================== +*/ +float Patch_Width(patchMesh_t *p) +{ + float f = 0; + for (int i = 0; i < p->width-1; i++) + { + vec3_t vTemp; + VectorSubtract(p->ctrl[i][0].xyz, p->ctrl[i+1][0].xyz, vTemp); + f += VectorLength(vTemp); + } + return f; +} + +float Patch_WidthDistanceTo(patchMesh_t *p, int j) +{ + float f = 0; + for (int i = 0; i < j; i++) + { + vec3_t vTemp; + VectorSubtract(p->ctrl[i][0].xyz, p->ctrl[i+1][0].xyz, vTemp); + f += VectorLength(vTemp); + } + return f; +} + + + +/* +================== +Patch_Height +================== +*/ +float Patch_Height(patchMesh_t *p) +{ + float f = 0; + for (int i = 0; i < p->height-1; i++) + { + vec3_t vTemp; + VectorSubtract(p->ctrl[0][i].xyz, p->ctrl[0][i+1].xyz, vTemp); + f += VectorLength(vTemp); + } + return f; +} + +float Patch_HeightDistanceTo(patchMesh_t *p, int j) +{ + float f = 0; + for (int i = 0; i < j; i++) + { + vec3_t vTemp; + VectorSubtract(p->ctrl[0][i].xyz, p->ctrl[0][i+1].xyz, vTemp); + f += VectorLength(vTemp); + } + return f; +} + + + +/* +================== +Patch_Naturalize +================== +texture = TotalTexture * LengthToThisControlPoint / TotalControlPointLength + +dist( this control point to first control point ) / dist ( last control pt to first) +*/ +void Patch_Naturalize(patchMesh_t *p) +{ + int nWidth = (g_PrefsDlg.m_bHiColorTextures == TRUE) ? p->d_texture->width * fTEXTURE_SCALE : p->d_texture->width; + int nHeight = (g_PrefsDlg.m_bHiColorTextures == TRUE) ? p->d_texture->height * fTEXTURE_SCALE : p->d_texture->height; + float fPWidth = Patch_Width(p); + float fPHeight = Patch_Height(p); + float xAccum = 0; + for ( int i = 0 ; i < p->width ; i++ ) + { + float yAccum = 0; + for ( int j = 0 ; j < p->height ; j++ ) + { + p->ctrl[i][j].st[0] = (fPWidth / nWidth) * xAccum / fPWidth; + p->ctrl[i][j].st[1] = (fPHeight / nHeight) * yAccum / fPHeight; + yAccum = Patch_HeightDistanceTo(p,j+1); + //p->ctrl[i][j][3] = (fPWidth / nWidth) * (float)i / (p->width - 1); + //p->ctrl[i][j][4] = (fPHeight/ nHeight) * (float)j / (p->height - 1); + } + xAccum = Patch_WidthDistanceTo(p,i+1); + } + p->bDirty = true; +} + +/* + if (bIBevel) + { + VectorCopy(p->ctrl[1][0], p->ctrl[1][1]); + } + + if (bIEndcap) + { + VectorCopy(p->ctrl[3][0], p->ctrl[4][1]); + VectorCopy(p->ctrl[2][0], p->ctrl[3][1]); + VectorCopy(p->ctrl[2][0], p->ctrl[2][1]); + VectorCopy(p->ctrl[2][0], p->ctrl[1][1]); + VectorCopy(p->ctrl[1][0], p->ctrl[0][1]); + VectorCopy(p->ctrl[1][0], p->ctrl[0][2]); + VectorCopy(p->ctrl[1][0], p->ctrl[1][2]); + VectorCopy(p->ctrl[2][0], p->ctrl[2][2]); + VectorCopy(p->ctrl[3][0], p->ctrl[3][2]); + VectorCopy(p->ctrl[3][0], p->ctrl[4][2]); + } +*/ + +int Index3By[][2] = +{ + {0,0}, + {1,0}, + {2,0}, + {2,1}, + {2,2}, + {1,2}, + {0,2}, + {0,1}, + {0,0}, + {0,0}, + {0,0}, + {0,0}, + {0,0}, + {0,0}, + {0,0} +}; + +int Index5By[][2] = +{ + {0,0}, + {1,0}, + {2,0}, + {3,0}, + {4,0}, + {4,1}, + {4,2}, + {4,3}, + {4,4}, + {3,4}, + {2,4}, + {1,4}, + {0,4}, + {0,3}, + {0,2}, + {0,1} +}; + + + +int Interior3By[][2] = +{ + {1,1} +}; + +int Interior5By[][2] = +{ + {1,1}, + {2,1}, + {3,1}, + {1,2}, + {2,2}, + {3,2}, + {1,3}, + {2,3}, + {3,3} +}; + +int Interior3ByCount = sizeof(Interior3By) / sizeof(int[2]); +int Interior5ByCount = sizeof(Interior5By) / sizeof(int[2]); + +face_t* Patch_GetAxisFace(patchMesh_t *p) +{ + face_t *f = NULL; + vec3_t vTemp; + brush_t *b = p->pSymbiot; + + for (f = b->brush_faces ; f ; f = f->next) + { + VectorSubtract(f->face_winding->points[1], f->face_winding->points[0], vTemp); + int nScore = 0; + + // default edge faces on caps are 8 high so + // as soon as we hit one that is bigger it should be on the right axis + for (int j = 0; j < 3; j++) + { + if (vTemp[j] > 8) + nScore++; + } + + if (nScore > 0) + break; + } + + if (f == NULL) + f = b->brush_faces; + return f; +} + +int g_nFaceCycle = 0; + +face_t* nextFace(patchMesh_t *p) +{ + brush_t *b = p->pSymbiot; + face_t *f = NULL; + int n = 0; + for (f = b->brush_faces ; f && n <= g_nFaceCycle; f = f->next) + { + n++; + } + g_nFaceCycle++; + if (g_nFaceCycle > 5) + { + g_nFaceCycle =0; + f = b->brush_faces; + } + + return f; +} + + +extern void EmitTextureCoordinates ( float *xyzst, qtexture_t *q, face_t *f); +void Patch_CapTexture(patchMesh_t *p, bool bFaceCycle = false) +{ + Patch_MeshNormals(*p); + face_t *f = (bFaceCycle) ? nextFace(p) : Patch_GetAxisFace(p); + vec3_t vSave; + VectorCopy(f->plane.normal, vSave); + + float fRotate = f->texdef.rotate; + f->texdef.rotate = 0; + float fScale[2]; + fScale[0] = f->texdef.scale[0]; + fScale[1] = f->texdef.scale[1]; + f->texdef.scale[0] = fTEXTURE_SCALE; + f->texdef.scale[1] = fTEXTURE_SCALE; + float fShift[2]; + fShift[0] = f->texdef.shift[0]; + fShift[1] = f->texdef.shift[1]; + f->texdef.shift[0] = 0; + f->texdef.shift[1] = 0; + + for (int i = 0; i < p->width; i++) + { + for (int j = 0; j < p->height; j++) + { + //--VectorCopy(p->ctrl[0][0].normal, f->plane.normal); + EmitTextureCoordinates( p->ctrl[i][j].xyz, f->d_texture, f); + } + } + VectorCopy(vSave, f->plane.normal); + f->texdef.rotate = fRotate; + f->texdef.scale[0] = fScale[0]; + f->texdef.scale[1] = fScale[1]; + f->texdef.shift[0] = fShift[0]; + f->texdef.shift[1] = fShift[1]; + p->bDirty = true; +} + +void FillPatch(patchMesh_t *p, vec3_t v) +{ + for (int i = 0; i < p->width; i++) + { + for (int j = 0; j < p->height; j++) + { + VectorCopy(v, p->ctrl[i][j].xyz); + } + } +} + +brush_t* Cap(patchMesh_t *pParent, bool bByColumn, bool bFirst) +{ + brush_t *b; + patchMesh_t *p; + vec3_t vMin, vMax; + int i, j; + + bool bSmall = true; + // make a generic patch + if (pParent->width <= 9) + { + b = Patch_GenericMesh(3, 3, 2, false); + } + else + { + b = Patch_GenericMesh(5, 5, 2, false); + bSmall = false; + } + + if (!b) + { + Sys_Printf("Unable to cap. Make sure you ungroup before re-capping."); + return NULL; + } + + p = &patchMeshes[b->nPatchID]; + p->type |= PATCH_CAP; + + vMin[0] = vMin[1] = vMin[2] = MAX_WORLD_COORD; + vMax[0] = vMax[1] = vMax[2] = MIN_WORLD_COORD; + + // we seam the column edge, FIXME: this might need to be able to seem either edge + // + int nSize = (bByColumn) ? pParent->width : pParent->height; + int nIndex = (bFirst) ? 0 : (bByColumn) ? pParent->height-1 : pParent->width-1; + + FillPatch(p, pParent->ctrl[0][nIndex].xyz); + + for (i = 0; i < nSize; i++) + { + if (bByColumn) + { + if (bSmall) + { + VectorCopy(pParent->ctrl[i][nIndex].xyz, p->ctrl[Index3By[i][0]][Index3By[i][1]].xyz); + } + else + { + VectorCopy(pParent->ctrl[i][nIndex].xyz, p->ctrl[Index5By[i][0]][Index5By[i][1]].xyz); + } + } + else + { + if (bSmall) + { + VectorCopy(pParent->ctrl[nIndex][i].xyz, p->ctrl[Index3By[i][0]][Index3By[i][1]].xyz); + } + else + { + VectorCopy(pParent->ctrl[nIndex][i].xyz, p->ctrl[Index5By[i][0]][Index5By[i][1]].xyz); + } + } + + for (j = 0; j < 3; j++) + { + float f = (bSmall) ? p->ctrl[Index3By[i][0]][Index3By[i][1]].xyz[j] : p->ctrl[Index5By[i][0]][Index5By[i][1]].xyz[j]; + if (f < vMin[j]) + vMin[j] = f; + if (f > vMax[j]) + vMax[j] = f; + } + } + + vec3_t vTemp; + for (j = 0; j < 3; j++) + { + vTemp[j] = vMin[j] + abs((vMax[j] - vMin[j]) * 0.5); + } + + int nCount = (bSmall) ? Interior3ByCount : Interior5ByCount; + for (j = 0; j < nCount; j++) + { + if (bSmall) + { + VectorCopy(vTemp, p->ctrl[Interior3By[j][0]][Interior3By[j][1]].xyz); + } + else + { + VectorCopy(vTemp, p->ctrl[Interior5By[j][0]][Interior5By[j][1]].xyz); + } + } + + if (bFirst) + { + drawVert_t vertTemp; + for (i = 0; i < p->width; i++) + { + for (j = 0; j < p->height / 2; j++) + { + memcpy(&vertTemp, &p->ctrl[i][p->height - 1- j], sizeof (drawVert_t)); + memcpy(&p->ctrl[i][p->height - 1 - j], &p->ctrl[i][j], sizeof(drawVert_t)); + memcpy(&p->ctrl[i][j], &vertTemp, sizeof(drawVert_t)); + } + } + } + + Patch_Rebuild(p); + Patch_CapTexture(p); + return p->pSymbiot; +} + +brush_t* CapSpecial(patchMesh_t *pParent, int nType, bool bFirst) +{ + brush_t *b; + patchMesh_t *p; + vec3_t vMin, vMax, vTemp; + int i, j; + + if (nType == CCapDialog::IENDCAP) + b = Patch_GenericMesh(5, 3, 2, false); + else + b = Patch_GenericMesh(3, 3, 2, false); + + if (!b) + { + Sys_Printf("Unable to cap. Make sure you ungroup before re-capping."); + return NULL; + } + + p = &patchMeshes[b->nPatchID]; + p->type |= PATCH_CAP; + + vMin[0] = vMin[1] = vMin[2] = MAX_WORLD_COORD; + vMax[0] = vMax[1] = vMax[2] = MIN_WORLD_COORD; + + int nSize = pParent->width; + int nIndex = (bFirst) ? 0 : pParent->height-1; + + // parent bounds are used for some things + Patch_CalcBounds(pParent, vMin, vMax); + + for (j = 0; j < 3; j++) + { + vTemp[j] = vMin[j] + abs((vMax[j] - vMin[j]) * 0.5); + } + + if (nType == CCapDialog::IBEVEL) + { + VectorCopy(pParent->ctrl[0][nIndex].xyz, p->ctrl[0][0].xyz); + VectorCopy(pParent->ctrl[2][nIndex].xyz, p->ctrl[0][2].xyz); + VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[0][1].xyz); + VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[2][2].xyz); + VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[1][0].xyz); + VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[1][1].xyz); + VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[1][2].xyz); + VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[2][0].xyz); + VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[2][1].xyz); + } + else if (nType == CCapDialog::BEVEL) + { + vec3_t p1, p2, p3, p4, temp, dir; + + VectorCopy(pParent->ctrl[0][nIndex].xyz, p3); + VectorCopy(pParent->ctrl[1][nIndex].xyz, p1); + VectorCopy(pParent->ctrl[2][nIndex].xyz, p2); + + VectorSubtract(p3, p2, dir); + VectorNormalize(dir); + VectorSubtract(p1, p2, temp); + vec_t dist = _DotProduct(temp, dir); + + VectorScale(dir, dist, temp); + + VectorAdd(p2, temp, temp); + + VectorSubtract(temp, p1, temp); + VectorScale(temp, 2, temp); + VectorAdd(p1, temp, p4); + + VectorCopy(p4, p->ctrl[0][0].xyz); + VectorCopy(p4, p->ctrl[1][0].xyz); + VectorCopy(p4, p->ctrl[0][1].xyz); + VectorCopy(p4, p->ctrl[1][1].xyz); + VectorCopy(p4, p->ctrl[0][2].xyz); + VectorCopy(p4, p->ctrl[1][2].xyz); + VectorCopy(p3, p->ctrl[2][0].xyz); + VectorCopy(p1, p->ctrl[2][1].xyz); + VectorCopy(p2, p->ctrl[2][2].xyz); + + } + else if (nType == CCapDialog::ENDCAP) + { + VectorAdd(pParent->ctrl[4][nIndex].xyz, pParent->ctrl[0][nIndex].xyz, vTemp); + VectorScale(vTemp, 0.5, vTemp); + VectorCopy(pParent->ctrl[0][nIndex].xyz, p->ctrl[0][0].xyz); + VectorCopy(vTemp, p->ctrl[1][0].xyz); + VectorCopy(pParent->ctrl[4][nIndex].xyz, p->ctrl[2][0].xyz); + + VectorCopy(pParent->ctrl[2][nIndex].xyz, p->ctrl[0][2].xyz); + VectorCopy(pParent->ctrl[2][nIndex].xyz, p->ctrl[1][2].xyz); + VectorCopy(pParent->ctrl[2][nIndex].xyz, p->ctrl[2][2].xyz); + VectorCopy(pParent->ctrl[2][nIndex].xyz, p->ctrl[1][1].xyz); + + VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[0][1].xyz); + VectorCopy(pParent->ctrl[3][nIndex].xyz, p->ctrl[2][1].xyz); + } + else + { + VectorCopy(pParent->ctrl[0][nIndex].xyz, p->ctrl[0][0].xyz); + VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[1][0].xyz); + VectorCopy(pParent->ctrl[2][nIndex].xyz, p->ctrl[2][0].xyz); + VectorCopy(pParent->ctrl[3][nIndex].xyz, p->ctrl[3][0].xyz); + VectorCopy(pParent->ctrl[4][nIndex].xyz, p->ctrl[4][0].xyz); + + VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[0][1].xyz); + VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[1][1].xyz); + VectorCopy(pParent->ctrl[2][nIndex].xyz, p->ctrl[2][1].xyz); + VectorCopy(pParent->ctrl[3][nIndex].xyz, p->ctrl[3][1].xyz); + VectorCopy(pParent->ctrl[3][nIndex].xyz, p->ctrl[4][1].xyz); + + VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[0][2].xyz); + VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[1][2].xyz); + VectorCopy(pParent->ctrl[2][nIndex].xyz, p->ctrl[2][2].xyz); + VectorCopy(pParent->ctrl[3][nIndex].xyz, p->ctrl[3][2].xyz); + VectorCopy(pParent->ctrl[3][nIndex].xyz, p->ctrl[4][2].xyz); + } + + + bool bEndCap = (nType == CCapDialog::ENDCAP || nType == CCapDialog::IENDCAP); + if ((!bFirst && !bEndCap) || (bFirst && bEndCap)) + { + drawVert_t vertTemp; + for (i = 0; i < p->width; i++) + { + for (j = 0; j < p->height / 2; j++) + { + memcpy(&vertTemp, &p->ctrl[i][p->height - 1- j], sizeof (drawVert_t)); + memcpy(&p->ctrl[i][p->height - 1 - j], &p->ctrl[i][j], sizeof(drawVert_t)); + memcpy(&p->ctrl[i][j], &vertTemp, sizeof(drawVert_t)); + } + } + } + + //--Patch_CalcBounds(p, vMin, vMax); + //--Brush_RebuildBrush(p->pSymbiot, vMin, vMax); + Patch_Rebuild(p); + Patch_CapTexture(p); + return p->pSymbiot; +} + + +void Patch_CapCurrent(bool bInvertedBevel, bool bInvertedEndcap) +{ + patchMesh_t *pParent; + brush_t *b[4]; + b[0] = b[1] = b[2] = b[3] = NULL; + int nIndex = 0; + for (brush_t *pb = selected_brushes.next ; pb != NULL && pb != &selected_brushes ; pb = pb->next) + { + if (pb->patchBrush) + { + pParent = &patchMeshes[pb->nPatchID]; + // decide which if any ends we are going to cap + // if any of these compares hit, it is a closed patch and as such + // the generic capping will work.. if we do not find a closed edge + // then we need to ask which kind of cap to add + if (VectorCompare(pParent->ctrl[0][0].xyz, pParent->ctrl[pParent->width-1][0].xyz)) + { + b[nIndex++] = Cap(pParent, true, false); + } + if (VectorCompare(pParent->ctrl[0][pParent->height-1].xyz, pParent->ctrl[pParent->width-1][pParent->height-1].xyz)) + { + b[nIndex++] = Cap(pParent, true, true); + } + if (VectorCompare(pParent->ctrl[0][0].xyz, pParent->ctrl[0][pParent->height-1].xyz)) + { + b[nIndex++] = Cap(pParent, false, false); + } + if (VectorCompare(pParent->ctrl[pParent->width-1][0].xyz, pParent->ctrl[pParent->width-1][pParent->height-1].xyz)) + { + b[nIndex++] = Cap(pParent, false, true); + } + } + } + + // if we did not cap anything with the above tests + if (nIndex == 0) + { + CCapDialog dlg; + if (dlg.DoModal() == IDOK) + { + b[nIndex++] = CapSpecial(pParent, dlg.getCapType(), false); + b[nIndex++] = CapSpecial(pParent, dlg.getCapType(), true); + } + } + + if (nIndex > 0) + { + while (nIndex > 0) + { + nIndex--; + if (b[nIndex]) + { + Select_Brush(b[nIndex]); + } + } + eclass_t *pecNew = Eclass_ForName("func_group", false); + if (pecNew) + { + entity_t *e = Entity_Create(pecNew); + SetKeyValue(e, "type", "patchCapped"); + } + } +} + + +//FIXME: Table drive all this crap +// +void GenerateEndCaps(brush_t *brushParent, bool bBevel, bool bEndcap, bool bInverted) +{ + brush_t *b, *b2; + patchMesh_t *p, *p2, *pParent; + vec3_t vTemp, vMin, vMax; + int i, j; + + pParent = &patchMeshes[brushParent->nPatchID]; + + Patch_CalcBounds(pParent, vMin, vMax); + // basically generate two endcaps, place them, and link the three brushes with a func_group + + if (pParent->width > 9) + b = Patch_GenericMesh(5, 3, 2, false); + else + b = Patch_GenericMesh(3, 3, 2, false); + p = &patchMeshes[b->nPatchID]; + + vMin[0] = vMin[1] = vMin[2] = MAX_WORLD_COORD; + vMax[0] = vMax[1] = vMax[2] = MIN_WORLD_COORD; + + for (i = 0; i < pParent->width; i++) + { + VectorCopy(pParent->ctrl[i][0].xyz, p->ctrl[Index3By[i][0]][Index3By[i][1]].xyz); + for (j = 0; j < 3; j++) + { + if (pParent->ctrl[i][0].xyz[j] < vMin[j]) + vMin[j] = pParent->ctrl[i][0].xyz[j]; + if (pParent->ctrl[i][0].xyz[j] > vMax[j]) + vMax[j] = pParent->ctrl[i][0].xyz[j]; + } + } + + for (j = 0; j < 3; j++) + { + vTemp[j] = vMin[j] + abs((vMax[j] - vMin[j]) * 0.5); + } + + for (i = 0; i < Interior3ByCount; i++) + { + VectorCopy(vTemp, p->ctrl[Interior3By[i][0]][Interior3By[i][1]].xyz); + } + + Patch_CalcBounds(p, vMin, vMax); + Brush_RebuildBrush(p->pSymbiot, vMin, vMax); + Select_Brush(p->pSymbiot); + return; + + bool bCreated = false; + + if (bInverted) + { + if (bBevel) + { + b = Patch_GenericMesh(3, 3, 2, false); + p = &patchMeshes[b->nPatchID]; + VectorCopy(p->ctrl[2][2].xyz, p->ctrl[1][2].xyz); + VectorCopy(p->ctrl[2][2].xyz, p->ctrl[2][1].xyz); + VectorCopy(p->ctrl[2][2].xyz, p->ctrl[0][1].xyz); + VectorCopy(p->ctrl[2][2].xyz, p->ctrl[1][0].xyz); + VectorCopy(p->ctrl[2][2].xyz, p->ctrl[1][1].xyz); + VectorCopy(p->ctrl[2][0].xyz, p->ctrl[0][0].xyz); + + b2 = Patch_GenericMesh(3, 3, 2, false); + p2 = &patchMeshes[b2->nPatchID]; + VectorCopy(p2->ctrl[2][2].xyz, p2->ctrl[1][2].xyz); + VectorCopy(p2->ctrl[2][2].xyz, p2->ctrl[2][1].xyz); + VectorCopy(p2->ctrl[2][2].xyz, p2->ctrl[0][1].xyz); + VectorCopy(p2->ctrl[2][2].xyz, p2->ctrl[1][0].xyz); + VectorCopy(p2->ctrl[2][2].xyz, p2->ctrl[1][1].xyz); + VectorCopy(p2->ctrl[2][0].xyz, p2->ctrl[0][0].xyz); + + + bCreated = true; + + } + else if (bEndcap) + { + b = Patch_GenericMesh(5, 5, 2, false); + p = &patchMeshes[b->nPatchID]; + VectorCopy(p->ctrl[4][4].xyz, p->ctrl[4][3].xyz); + VectorCopy(p->ctrl[0][4].xyz, p->ctrl[1][4].xyz); + VectorCopy(p->ctrl[0][4].xyz, p->ctrl[2][4].xyz); + VectorCopy(p->ctrl[0][4].xyz, p->ctrl[3][4].xyz); + + VectorCopy(p->ctrl[4][0].xyz, p->ctrl[4][1].xyz); + VectorCopy(p->ctrl[0][0].xyz, p->ctrl[1][0].xyz); + VectorCopy(p->ctrl[0][0].xyz, p->ctrl[2][0].xyz); + VectorCopy(p->ctrl[0][0].xyz, p->ctrl[3][0].xyz); + + for (i = 1; i < 4; i++) + { + for (j = 0; j < 4; j++) + { + VectorCopy(p->ctrl[4][i].xyz, p->ctrl[j][i].xyz); + } + } + + + b2 = Patch_GenericMesh(5, 5, 2, false); + p2 = &patchMeshes[b2->nPatchID]; + VectorCopy(p2->ctrl[4][4].xyz, p2->ctrl[4][3].xyz); + VectorCopy(p2->ctrl[0][4].xyz, p2->ctrl[1][4].xyz); + VectorCopy(p2->ctrl[0][4].xyz, p2->ctrl[2][4].xyz); + VectorCopy(p2->ctrl[0][4].xyz, p2->ctrl[3][4].xyz); + + VectorCopy(p2->ctrl[4][0].xyz, p2->ctrl[4][1].xyz); + VectorCopy(p2->ctrl[0][0].xyz, p2->ctrl[1][0].xyz); + VectorCopy(p2->ctrl[0][0].xyz, p2->ctrl[2][0].xyz); + VectorCopy(p2->ctrl[0][0].xyz, p2->ctrl[3][0].xyz); + + for (i = 1; i < 4; i++) + { + for (j = 0; j < 4; j++) + { + VectorCopy(p2->ctrl[4][i].xyz, p2->ctrl[j][i].xyz); + } + } + + + bCreated = true; + } + } + else + { + if (bBevel) + { + b = Patch_GenericMesh(3, 3, 2, false); + p = &patchMeshes[b->nPatchID]; + VectorCopy(p->ctrl[2][0].xyz, p->ctrl[2][1].xyz); + VectorCopy(p->ctrl[0][0].xyz, p->ctrl[1][0].xyz); + VectorCopy(p->ctrl[0][0].xyz, p->ctrl[2][0].xyz); + + b2 = Patch_GenericMesh(3, 3, 2, false); + p2 = &patchMeshes[b2->nPatchID]; + VectorCopy(p2->ctrl[2][0].xyz, p2->ctrl[2][1].xyz); + VectorCopy(p2->ctrl[0][0].xyz, p2->ctrl[1][0].xyz); + VectorCopy(p2->ctrl[0][0].xyz, p2->ctrl[2][0].xyz); + bCreated = true; + } + else if (bEndcap) + { + b = Patch_GenericMesh(5, 5, 2, false); + p = &patchMeshes[b->nPatchID]; + VectorCopy(p->ctrl[0][0].xyz, p->ctrl[1][0].xyz); + VectorCopy(p->ctrl[0][0].xyz, p->ctrl[2][0].xyz); + VectorCopy(p->ctrl[0][0].xyz, p->ctrl[3][0].xyz); + VectorCopy(p->ctrl[4][0].xyz, p->ctrl[4][1].xyz); + VectorCopy(p->ctrl[0][0].xyz, p->ctrl[4][0].xyz); + + VectorCopy(p->ctrl[0][4].xyz, p->ctrl[1][4].xyz); + VectorCopy(p->ctrl[0][4].xyz, p->ctrl[2][4].xyz); + VectorCopy(p->ctrl[0][4].xyz, p->ctrl[3][4].xyz); + VectorCopy(p->ctrl[4][4].xyz, p->ctrl[4][3].xyz); + VectorCopy(p->ctrl[0][4].xyz, p->ctrl[4][4].xyz); + + b2 = Patch_GenericMesh(5, 5, 2, false); + p2 = &patchMeshes[b2->nPatchID]; + VectorCopy(p2->ctrl[0][0].xyz, p2->ctrl[1][0].xyz); + VectorCopy(p2->ctrl[0][0].xyz, p2->ctrl[2][0].xyz); + VectorCopy(p2->ctrl[0][0].xyz, p2->ctrl[3][0].xyz); + VectorCopy(p2->ctrl[4][0].xyz, p2->ctrl[4][1].xyz); + VectorCopy(p2->ctrl[0][0].xyz, p2->ctrl[4][0].xyz); + + VectorCopy(p2->ctrl[0][4].xyz, p2->ctrl[1][4].xyz); + VectorCopy(p2->ctrl[0][4].xyz, p2->ctrl[2][4].xyz); + VectorCopy(p2->ctrl[0][4].xyz, p2->ctrl[3][4].xyz); + VectorCopy(p2->ctrl[4][4].xyz, p2->ctrl[4][3].xyz); + VectorCopy(p2->ctrl[0][4].xyz, p2->ctrl[4][4].xyz); + bCreated = true; + } + else + { + b = Patch_GenericMesh(3, 3, 2, false); + p = &patchMeshes[b->nPatchID]; + + VectorCopy(p->ctrl[0][1].xyz, vTemp); + VectorCopy(p->ctrl[0][2].xyz, p->ctrl[0][1].xyz) + VectorCopy(p->ctrl[1][2].xyz, p->ctrl[0][2].xyz) + VectorCopy(p->ctrl[2][2].xyz, p->ctrl[1][2].xyz) + VectorCopy(p->ctrl[2][1].xyz, p->ctrl[2][2].xyz) + VectorCopy(p->ctrl[2][0].xyz, p->ctrl[2][1].xyz) + VectorCopy(p->ctrl[1][0].xyz, p->ctrl[2][0].xyz) + VectorCopy(p->ctrl[0][0].xyz, p->ctrl[1][0].xyz) + VectorCopy(vTemp, p->ctrl[0][0].xyz) + + b2 = Patch_GenericMesh(3, 3, 2, false); + p2 = &patchMeshes[b2->nPatchID]; + VectorCopy(p2->ctrl[0][1].xyz, vTemp); + VectorCopy(p2->ctrl[0][2].xyz, p2->ctrl[0][1].xyz) + VectorCopy(p2->ctrl[1][2].xyz, p2->ctrl[0][2].xyz) + VectorCopy(p2->ctrl[2][2].xyz, p2->ctrl[1][2].xyz) + VectorCopy(p2->ctrl[2][1].xyz, p2->ctrl[2][2].xyz) + VectorCopy(p2->ctrl[2][0].xyz, p2->ctrl[2][1].xyz) + VectorCopy(p2->ctrl[1][0].xyz, p2->ctrl[2][0].xyz) + VectorCopy(p2->ctrl[0][0].xyz, p2->ctrl[1][0].xyz) + VectorCopy(vTemp, p2->ctrl[0][0].xyz) + bCreated = true; + } + } + + if (bCreated) + { + drawVert_t vertTemp; + for (i = 0; i < p->width; i++) + { + for (j = 0; j < p->height; j++) + { + p->ctrl[i][j].xyz[2] = vMin[2]; + p2->ctrl[i][j].xyz[2] = vMax[2]; + } + + for (j = 0; j < p->height / 2; j++) + { + memcpy(&vertTemp, &p->ctrl[i][p->height - 1- j], sizeof (drawVert_t)); + memcpy(&p->ctrl[i][p->height - 1 - j], &p->ctrl[i][j], sizeof(drawVert_t)); + memcpy(&p->ctrl[i][j], &vertTemp, sizeof(drawVert_t)); + } + + } + //Select_Delete(); + + Patch_CalcBounds(p, vMin, vMax); + Brush_RebuildBrush(p->pSymbiot, vMin, vMax); + Patch_CalcBounds(p2, vMin, vMax); + Brush_RebuildBrush(p2->pSymbiot, vMin, vMax); + Select_Brush(p->pSymbiot); + Select_Brush(p2->pSymbiot); + } + else + { + Select_Delete(); + } + //Select_Brush(brushParent); + +} + + +/* +=============== +BrushToPatchMesh +=============== +*/ +void Patch_BrushToMesh(bool bCone, bool bBevel, bool bEndcap, bool bSquare, int nHeight) +{ + brush_t *b; + patchMesh_t *p; + int i,j; + + if (!QE_SingleBrush()) + return; + + b = selected_brushes.next; + + p = &patchMeshes[numPatchMeshes]; + + p->d_texture = b->brush_faces->d_texture; + + p->height = nHeight; + + p->type = PATCH_CYLINDER; + if (bBevel) + { + p->type = PATCH_BEVEL; + p->width = 3; + int nStep = (b->maxs[2] - b->mins[2]) / (p->height-1); + int nStart = b->mins[2]; + for (i = 0; i < p->height; i++) + { + p->ctrl[0][i].xyz[0] = b->mins[0]; + p->ctrl[0][i].xyz[1] = b->mins[1]; + p->ctrl[0][i].xyz[2] = nStart; + + p->ctrl[1][i].xyz[0] = b->maxs[0]; + p->ctrl[1][i].xyz[1] = b->mins[1]; + p->ctrl[1][i].xyz[2] = nStart; + + p->ctrl[2][i].xyz[0] = b->maxs[0]; + p->ctrl[2][i].xyz[1] = b->maxs[1]; + p->ctrl[2][i].xyz[2] = nStart; + nStart += nStep; + } + } + else if (bEndcap) + { + p->type = PATCH_ENDCAP; + p->width = 5; + int nStep = (b->maxs[2] - b->mins[2]) / (p->height-1); + int nStart = b->mins[2]; + for (i = 0; i < p->height; i++) + { + p->ctrl[0][i].xyz[0] = b->mins[0]; + p->ctrl[0][i].xyz[1] = b->mins[1]; + p->ctrl[0][i].xyz[2] = nStart; + + p->ctrl[1][i].xyz[0] = b->mins[0]; + p->ctrl[1][i].xyz[1] = b->maxs[1]; + p->ctrl[1][i].xyz[2] = nStart; + + p->ctrl[2][i].xyz[0] = b->mins[0] + ((b->maxs[0] - b->mins[0]) * 0.5); + p->ctrl[2][i].xyz[1] = b->maxs[1]; + p->ctrl[2][i].xyz[2] = nStart; + + p->ctrl[3][i].xyz[0] = b->maxs[0]; + p->ctrl[3][i].xyz[1] = b->maxs[1]; + p->ctrl[3][i].xyz[2] = nStart; + + p->ctrl[4][i].xyz[0] = b->maxs[0]; + p->ctrl[4][i].xyz[1] = b->mins[1]; + p->ctrl[4][i].xyz[2] = nStart; + nStart += nStep; + } + } + else + { + p->width = 9; + p->ctrl[1][0].xyz[0] = b->mins[0]; + p->ctrl[1][0].xyz[1] = b->mins[1]; + + p->ctrl[3][0].xyz[0] = b->maxs[0]; + p->ctrl[3][0].xyz[1] = b->mins[1]; + + p->ctrl[5][0].xyz[0] = b->maxs[0]; + p->ctrl[5][0].xyz[1] = b->maxs[1]; + + p->ctrl[7][0].xyz[0] = b->mins[0]; + p->ctrl[7][0].xyz[1] = b->maxs[1]; + + for ( i = 1 ; i < p->width - 1 ; i += 2 ) + { + + p->ctrl[i][0].xyz[2] = b->mins[2]; + + VectorCopy( p->ctrl[i][0].xyz, p->ctrl[i][2].xyz ); + + p->ctrl[i][2].xyz[2] = b->maxs[2]; + + p->ctrl[i][1].xyz[0] = ( p->ctrl[i][0].xyz[0] + p->ctrl[i][2].xyz[0] ) * 0.5; + p->ctrl[i][1].xyz[1] = ( p->ctrl[i][0].xyz[1] + p->ctrl[i][2].xyz[1] ) * 0.5; + p->ctrl[i][1].xyz[2] = ( p->ctrl[i][0].xyz[2] + p->ctrl[i][2].xyz[2] ) * 0.5; + } + InterpolateInteriorPoints( p ); + + if (bSquare) + { + for (i = 0; i < p->width-1; i ++) + { + for (j = 0; j < p->height; j++) + { + VectorCopy(p->ctrl[i+1][j].xyz, p->ctrl[i][j].xyz); + } + } + for (j = 0; j < p->height; j++) + { + VectorCopy(p->ctrl[0][j].xyz, p->ctrl[8][j].xyz); + } + } + } + + + Patch_Naturalize(p); + + if (bCone) + { + p->type = PATCH_CONE; + float xc = (b->maxs[0] + b->mins[0]) * 0.5; + float yc = (b->maxs[1] + b->mins[1]) * 0.5; + + for ( i = 0 ; i < p->width ; i ++) + { + p->ctrl[i][2].xyz[0] = xc; + p->ctrl[i][2].xyz[1] = yc; + } + } +/* + if (bEndcap || bBevel) + { + if (bInverted) + { + for ( i = 0 ; i < p->height ; i ++) + { + if (bBevel) + { + VectorCopy(p->ctrl[7][i], p->ctrl[0][i]); + VectorCopy(p->ctrl[7][i], p->ctrl[8][i]); + VectorCopy(p->ctrl[3][i], p->ctrl[2][i]); + VectorCopy(p->ctrl[5][i], p->ctrl[1][i]); + VectorCopy(p->ctrl[5][i], p->ctrl[4][i]); + VectorCopy(p->ctrl[5][i], p->ctrl[6][i]); + } + else + { + VectorCopy(p->ctrl[4][i], p->ctrl[8][i]); + VectorCopy(p->ctrl[1][i], p->ctrl[0][i]); + VectorCopy(p->ctrl[1][i], p->ctrl[10][i]); + VectorCopy(p->ctrl[3][i], p->ctrl[2][i]); + VectorCopy(p->ctrl[5][i], p->ctrl[4][i]); + VectorCopy(p->ctrl[7][i], p->ctrl[6][i]); + VectorCopy(p->ctrl[5][i], p->ctrl[7][i]); + VectorCopy(p->ctrl[3][i], p->ctrl[9][i]); + } + } + } + else + { + for ( i = 0 ; i < p->height ; i ++) + { + VectorCopy(p->ctrl[1][i], p->ctrl[2][i]); + VectorCopy(p->ctrl[7][i], p->ctrl[6][i]); + if (bBevel) + { + VectorCopy(p->ctrl[5][i], p->ctrl[4][i]); + } + } + } + } +*/ + + b = AddBrushForPatch(numPatchMeshes); + + numPatchMeshes++; + +#if 1 + Select_Delete(); + Select_Brush(b); +#else + if (!bCone) + { + Select_Delete(); + Select_Brush(b); + GenerateEndCaps(b, bBevel, bEndcap, bInverted); + eclass_t *pecNew = Eclass_ForName("func_group", false); + if (pecNew) + { + Entity_Create(pecNew); + } + } + else + { + Select_Delete(); + Select_Brush(b); + } +#endif + +} + +/* +================== +Patch_GenericMesh +================== +*/ +brush_t* Patch_GenericMesh(int nWidth, int nHeight, int nOrientation, bool bDeleteSource) +{ + int i,j; + + if (nHeight < 3 || nHeight > 15 || nWidth < 3 || nWidth > 15) + { + Sys_Printf("Invalid patch width or height.\n"); + return NULL; + } + + patchMesh_t* p = &patchMeshes[numPatchMeshes]; + p->d_texture = Texture_ForName(g_qeglobals.d_texturewin.texdef.name); + + p->width = nWidth; + p->height = nHeight; + p->type = PATCH_GENERIC; + + int nFirst = 0; + int nSecond = 1; + if (nOrientation == 0) + { + nFirst = 1; + nSecond = 2; + } + else if (nOrientation == 1) + { + nSecond = 2; + } + + if (!QE_SingleBrush()) + return NULL; + + brush_t *b = selected_brushes.next; + + int xStep = b->mins[nFirst]; + float xAdj = abs((b->maxs[nFirst] - b->mins[nFirst]) / (nWidth - 1)); + float yAdj = abs((b->maxs[nSecond] - b->mins[nSecond]) / (nHeight - 1)); + + for (i = 0; i < nWidth; i++) + { + int yStep = b->mins[nSecond]; + for (j = 0; j < nHeight; j++) + { + p->ctrl[i][j].xyz[nFirst] = xStep; + p->ctrl[i][j].xyz[nSecond] = yStep; + p->ctrl[i][j].xyz[nOrientation] = 0; + yStep += yAdj; + } + xStep += xAdj; + } + + Patch_Naturalize(p); + + b = AddBrushForPatch(numPatchMeshes); + if (bDeleteSource) + { + Select_Delete(); + Select_Brush(b); + } + + numPatchMeshes++; + return b; + //g_qeglobals.d_select_mode = sel_curvepoint; +} + +/* +================== +PointInMoveList +================== +*/ +int PointInMoveList(float *pf) +{ + for (int i = 0; i < g_qeglobals.d_num_move_points; i++) + { + if (pf == &g_qeglobals.d_move_points[i][0]) + return i; + } + return -1; +} + +/* +================== +PointValueInMoveList +================== +*/ +int PointValueInMoveList(vec3_t v) +{ + for (int i = 0; i < g_qeglobals.d_num_move_points; i++) + { + if (VectorCompare(v, g_qeglobals.d_move_points[i])) + return i; + } + return -1; +} + + +/* +================== +RemovePointFromMoveList +================== +*/ +void RemovePointFromMoveList(vec3_t v) +{ + int n; + while ( (n = PointValueInMoveList(v)) >= 0) + { + for (int i = n; i < g_qeglobals.d_num_move_points-1; i++) + { + g_qeglobals.d_move_points[i] = g_qeglobals.d_move_points[i+1]; + } + g_qeglobals.d_num_move_points--; + } +} + +/* +================== +ColumnSelected +================== +*/ +bool ColumnSelected(patchMesh_t* p, int nCol) +{ + for (int i = 0; i < p->height; i++) + { + if (PointInMoveList(p->ctrl[nCol][i].xyz) == -1) + return false; + } + return true; +} + +/* +================== +AddPoint +================== +*/ +void AddPoint(patchMesh_t* p, vec3_t v, bool bWeldOrDrill = true) +{ + int nDim1 = (g_pParentWnd->ActiveXY()->GetViewType() == YZ) ? 1 : 0; + int nDim2 = (g_pParentWnd->ActiveXY()->GetViewType() == XY) ? 1 : 2; + g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = v; + if ((g_bPatchWeld || g_bPatchDrillDown) && bWeldOrDrill) + { + for ( int i = 0 ; i < p->width ; i++ ) + { + for ( int j = 0 ; j < p->height ; j++ ) + { + if (g_bPatchWeld) + { + if ( VectorCompare(v, p->ctrl[i][j].xyz) + && PointInMoveList(p->ctrl[i][j].xyz) == -1) + { + g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = p->ctrl[i][j].xyz; + continue; + } + } + if (g_bPatchDrillDown && g_nPatchClickedView != W_CAMERA) + { + if ( (fabs(v[nDim1] - p->ctrl[i][j].xyz[nDim1]) <= EQUAL_EPSILON) + &&(fabs(v[nDim2] - p->ctrl[i][j].xyz[nDim2]) <= EQUAL_EPSILON)) + { + if (PointInMoveList(p->ctrl[i][j].xyz) == -1) + { + g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = p->ctrl[i][j].xyz; + continue; + } + } +#if 0 + int l = 0; + for ( int k = 0; k < 2; k++ ) + { + if (fabs(v[k] - p->ctrl[i][j].xyz[k]) > EQUAL_EPSILON) + continue; + l++; + } + if (l >= 2 && PointInMoveList(p->ctrl[i][j].xyz) == -1) + { + g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = p->ctrl[i][j].xyz; + continue; + } +#endif + } + } + } + } +#if 0 + if (g_qeglobals.d_num_move_points == 1) + { + // single point selected + // FIXME: the two loops can probably be reduced to one + for ( int i = 0 ; i < p->width ; i++ ) + { + for ( int j = 0 ; j < p->height ; j++ ) + { + int n = PointInMoveList(v); + if (n >= 0) + { + if (((i & 0x01) && (j & 0x01)) == 0) + { + // put any sibling fixed points + // into the inverse list + int p1, p2, p3, p4; + p1 = i + 2; + p2 = i - 2; + p3 = j + 2; + p4 = j - 2; + if (p1 < p->width) + { + + } + if (p2 >= 0) + { + } + if (p3 < p->height) + { + } + if (p4 >= 0) + { + } + } + } + } + } + } +#endif +} + +/* +================== +SelectRow +================== +*/ +void SelectRow(patchMesh_t* p, int nRow, bool bMulti) +{ + if (!bMulti) + g_qeglobals.d_num_move_points = 0; + for (int i = 0; i < p->width; i++) + { + AddPoint(p, p->ctrl[i][nRow].xyz, false); + } + //Sys_Printf("Selected Row %d\n", nRow); +} + +/* +================== +SelectColumn +================== +*/ +void SelectColumn(patchMesh_t* p, int nCol, bool bMulti) +{ + if (!bMulti) + g_qeglobals.d_num_move_points = 0; + for (int i = 0; i < p->height; i++) + { + AddPoint(p, p->ctrl[nCol][i].xyz, false); + } + //Sys_Printf("Selected Col %d\n", nCol); +} + + +/* +================== +AddPatchMovePoint +================== +*/ +void AddPatchMovePoint(vec3_t v, bool bMulti, bool bFull) +{ + if (!g_bSameView && !bMulti && !bFull) + { + g_bSameView = true; + return; + } + + for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next) + { + if (pb->patchBrush) + { + patchMesh_t* p = &patchMeshes[pb->nPatchID]; + for ( int i = 0 ; i < p->width ; i++ ) + { + for ( int j = 0 ; j < p->height ; j++ ) + { + if (VectorCompare(v, p->ctrl[i][j].xyz)) + { + if (PointInMoveList(p->ctrl[i][j].xyz) == -1) + { + if (bFull) // if we want the full row/col this is on + { + SelectColumn(p, i, bMulti); + } + else + { + if (!bMulti) + g_qeglobals.d_num_move_points = 0; + AddPoint(p, p->ctrl[i][j].xyz); + //Sys_Printf("Selected col:row %d:%d\n", i, j); + } + //--if (!bMulti) + return; + } + else + { + if (bFull) + { + if (ColumnSelected(p, i)) + { + SelectRow(p, j, bMulti); + } + else + { + SelectColumn(p, i, bMulti); + } + return; + } + if (g_bSameView) + { + RemovePointFromMoveList(v); + return; + } + } + } + } + } + } + } +} + +/* +================== +Patch_UpdateSelected +================== +*/ +void Patch_UpdateSelected(vec3_t vMove) +{ + int i, j; + for (i=0 ; i < g_qeglobals.d_num_move_points ; i++) + { + VectorAdd (g_qeglobals.d_move_points[i], vMove, g_qeglobals.d_move_points[i]); + if (g_qeglobals.d_num_move_points == 1) + { + } + } + + //--patchMesh_t* p = &patchMeshes[g_nSelectedPatch]; + for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next) + { + if (pb->patchBrush) + { + patchMesh_t* p = &patchMeshes[pb->nPatchID]; + + + g_qeglobals.d_numpoints = 0; + for (i = 0 ; i < p->width ; i++ ) + { + for ( j = 0 ; j < p->height ; j++ ) + { + VectorCopy (p->ctrl[i][j].xyz, g_qeglobals.d_points[g_qeglobals.d_numpoints]); + g_qeglobals.d_numpoints++; + } + } + + vec3_t vMin, vMax; + Patch_CalcBounds(p, vMin, vMax); + Brush_RebuildBrush(p->pSymbiot, vMin, vMax); + } + } + //Brush_Free(p->pSymbiot); + //Select_Brush(AddBrushForPatch(g_nSelectedPatch)); +} + + + +/* +=============== +SampleSinglePatch +=============== +*/ +void SampleSinglePatch (float ctrl[3][3][5], float u, float v, float out[5]) { + float vCtrl[3][5]; + int vPoint; + int axis; + + // find the control points for the v coordinate + for (vPoint = 0 ; vPoint < 3 ; vPoint++) { + for (axis = 0 ; axis < 5 ; axis++) { + float a, b, c; + float qA, qB, qC; + + a = ctrl[0][vPoint][axis]; + b = ctrl[1][vPoint][axis]; + c = ctrl[2][vPoint][axis]; + qA = a - 2 * b + c; + qB = 2 * b - 2 * a; + qC = a; + + vCtrl[vPoint][axis] = qA * u * u + qB * u + qC; + } + } + + // interpolate the v value + for (axis = 0 ; axis < 5 ; axis++) { + float a, b, c; + float qA, qB, qC; + + a = vCtrl[0][axis]; + b = vCtrl[1][axis]; + c = vCtrl[2][axis]; + qA = a - 2 * b + c; + qB = 2 * b - 2 * a; + qC = a; + + out[axis] = qA * v * v + qB * v + qC; + } +} + +/* +=================== +DrawSinglePatch +=================== +*/ +void DrawSinglePatch (float ctrl[3][3][5], bool bPoints) +{ + int i, j; + float u, v; + float verts[CBLOCK_SUBDIVISIONS+1][CBLOCK_SUBDIVISIONS+1][5]; + + for (i = 0 ; i <= CBLOCK_SUBDIVISIONS ; i++) { + for (j = 0 ; j <= CBLOCK_SUBDIVISIONS ; j++) { + u = (float)i / CBLOCK_SUBDIVISIONS; + v = (float)j / CBLOCK_SUBDIVISIONS; + SampleSinglePatch (ctrl, u, v, verts[i][j]); + } + } + + for (i = 0 ; i < CBLOCK_SUBDIVISIONS ; i++) { + qglBegin (GL_QUAD_STRIP); + for (j = 0 ; j <= CBLOCK_SUBDIVISIONS ; j++) { + qglTexCoord2fv( verts[i+1][j] + 3 ); + qglVertex3fv( verts[i+1][j] ); + qglTexCoord2fv( verts[i][j] + 3 ); + qglVertex3fv( verts[i][j] ); + } + qglEnd (); + } + +} + +/* +================= +DrawPatchMesh +================= +*/ +//FIXME: this routine needs to be reorganized.. should be about 1/4 the size and complexity +void DrawPatchMesh( patchMesh_t *pm, bool bPoints, bool bShade = false ) { + int i, j, k, l; + float ctrl[3][3][5]; + + bool bOverlay = pm->bOverlay; + + //--float fColor[3]; + //--if (bShade) + //--{ + //-- face_t *f = pm->pSymbiot->brush_faces; + //-- _SetColor(f, fColor); + //--} + if (g_PrefsDlg.m_bDisplayLists) + { + if (pm->bDirty || pm->nListID <= 0) + { + if (pm->nListID <= 0) + pm->nListID = qglGenLists(1); + if (pm->nListID > 0) + { + qglNewList(pm->nListID, GL_COMPILE_AND_EXECUTE); + } + Patch_MeshNormals(*pm); + for ( i = 0 ; i + 2 < pm->width ; i += 2 ) + { + for ( j = 0 ; j + 2 < pm->height ; j += 2 ) + { + for ( k = 0 ; k < 3 ; k++ ) + { + for ( l = 0 ; l < 3 ; l++ ) + { + ctrl[k][l][0] = pm->ctrl[ i + k ][ j + l ].xyz[ 0 ]; + ctrl[k][l][1] = pm->ctrl[ i + k ][ j + l ].xyz[ 1 ]; + ctrl[k][l][2] = pm->ctrl[ i + k ][ j + l ].xyz[ 2 ]; + ctrl[k][l][3] = pm->ctrl[ i + k ][ j + l ].xyz[ 3 ]; + ctrl[k][l][4] = pm->ctrl[ i + k ][ j + l ].xyz[ 4 ]; + } + } + //--if (bShade) + //--{ + //-- _DecColor(fColor); + //--} + //--qglColor3f(pm->ctrl[i][j].lightmap[0],pm->ctrl[i][j].lightmap[0],pm->ctrl[i][j].lightmap[0]); + + DrawSinglePatch( ctrl, bPoints ); + } + } + + if (pm->nListID > 0) + { + qglEndList(); + pm->bDirty = false; + } + } + else + { + qglCallList(pm->nListID); + } + } + else + { + for ( i = 0 ; i + 2 < pm->width ; i += 2 ) + { + for ( j = 0 ; j + 2 < pm->height ; j += 2 ) + { + for ( k = 0 ; k < 3 ; k++ ) + { + for ( l = 0 ; l < 3 ; l++ ) + { + ctrl[k][l][0] = pm->ctrl[ i + k ][ j + l ].xyz[ 0 ]; + ctrl[k][l][1] = pm->ctrl[ i + k ][ j + l ].xyz[ 1 ]; + ctrl[k][l][2] = pm->ctrl[ i + k ][ j + l ].xyz[ 2 ]; + ctrl[k][l][3] = pm->ctrl[ i + k ][ j + l ].xyz[ 3 ]; + ctrl[k][l][4] = pm->ctrl[ i + k ][ j + l ].xyz[ 4 ]; + } + } + //--if (bShade) + //--{ + //-- _DecColor(fColor); + //--} + DrawSinglePatch( ctrl, bPoints ); + } + } + } + + vec3_t* pSelectedPoints[256]; + int nIndex = 0; + + qglPushAttrib(GL_CURRENT_BIT); + //--qglDisable(GL_BLEND); + //--qglDisable (GL_DEPTH_TEST); + //--qglDisable (GL_TEXTURE_2D); + + // FIXME: this bend painting code needs to be rolled up significantly as it is a cluster fuck right now + if (bPoints && (g_qeglobals.d_select_mode == sel_curvepoint || g_qeglobals.d_select_mode == sel_area || g_bPatchBendMode || g_bPatchInsertMode)) + { + bOverlay = false; + + // bending or inserting + if (g_bPatchBendMode || g_bPatchInsertMode) + { + qglPointSize(6); + if (g_bPatchAxisOnRow) + { + qglColor3f(1, 0, 1); + qglBegin(GL_POINTS); + for (i = 0; i < pm->width; i++) + { + qglVertex3fv(reinterpret_cast(&pm->ctrl[i][g_nPatchAxisIndex].xyz)); + } + qglEnd(); + + // could do all of this in one loop but it was pretty messy + if (g_bPatchInsertMode) + { + qglColor3f(0, 0, 1); + qglBegin(GL_POINTS); + for (i = 0; i < pm->width; i++) + { + qglVertex3fv(reinterpret_cast(&pm->ctrl[i][g_nPatchAxisIndex].xyz)); + qglVertex3fv(reinterpret_cast(&pm->ctrl[i][g_nPatchAxisIndex+1].xyz)); + } + qglEnd(); + } + else + { + if (g_nPatchBendState == BEND_SELECT_EDGE || g_nPatchBendState == BEND_BENDIT || g_nPatchBendState == BEND_SELECT_ORIGIN) + { + qglColor3f(0, 0, 1); + qglBegin(GL_POINTS); + if (g_nPatchBendState == BEND_SELECT_ORIGIN) + { + qglVertex3fv(g_vBendOrigin); + } + else + { + for (i = 0; i < pm->width; i++) + { + if (g_bPatchLowerEdge) + { + for (j = 0; j < g_nPatchAxisIndex; j++) + qglVertex3fv(reinterpret_cast(&pm->ctrl[i][j].xyz)); + } + else + { + for (j = pm->height-1; j > g_nPatchAxisIndex; j--) + qglVertex3fv(reinterpret_cast(&pm->ctrl[i][j].xyz)); + } + } + } + qglEnd(); + } + } + } + else + { + qglColor3f(1, 0, 1); + qglBegin(GL_POINTS); + for (i = 0; i < pm->height; i++) + { + qglVertex3fv(reinterpret_cast(&pm->ctrl[g_nPatchAxisIndex][i].xyz)); + } + qglEnd(); + + // could do all of this in one loop but it was pretty messy + if (g_bPatchInsertMode) + { + qglColor3f(0, 0, 1); + qglBegin(GL_POINTS); + for (i = 0; i < pm->height; i++) + { + qglVertex3fv(reinterpret_cast(&pm->ctrl[g_nPatchAxisIndex][i].xyz)); + qglVertex3fv(reinterpret_cast(&pm->ctrl[g_nPatchAxisIndex+1][i].xyz)); + } + qglEnd(); + } + else + { + if (g_nPatchBendState == BEND_SELECT_EDGE || g_nPatchBendState == BEND_BENDIT || g_nPatchBendState == BEND_SELECT_ORIGIN) + { + qglColor3f(0, 0, 1); + qglBegin(GL_POINTS); + for (i = 0; i < pm->height; i++) + { + if (g_nPatchBendState == BEND_SELECT_ORIGIN) + { + qglVertex3fv(reinterpret_cast(&pm->ctrl[g_nBendOriginIndex][i].xyz)); + } + else + { + if (g_bPatchLowerEdge) + { + for (j = 0; j < g_nPatchAxisIndex; j++) + qglVertex3fv(reinterpret_cast(&pm->ctrl[j][i].xyz)); + } + else + { + for (j = pm->width-1; j > g_nPatchAxisIndex; j--) + qglVertex3fv(reinterpret_cast(&pm->ctrl[j][i].xyz)); + } + } + } + qglEnd(); + } + } + } + } + else // just painting the grid for selection + { + qglPointSize(6); + for ( i = 0 ; i < pm->width ; i++ ) + { + for ( j = 0 ; j < pm->height ; j++ ) + { + qglBegin(GL_POINTS); + // FIXME: need to not do loop lookups inside here + int n = PointValueInMoveList(pm->ctrl[i][j].xyz); + if (n >= 0) + { + pSelectedPoints[nIndex++] = reinterpret_cast(&pm->ctrl[i][j].xyz); + } + + if (i & 0x01 || j & 0x01) + qglColor3f(1, 0, 1); + else + qglColor3f(0, 1, 0); + qglVertex3fv(pm->ctrl[i][j].xyz); + qglEnd(); + } + } + } + if (nIndex > 0) + { + qglBegin(GL_POINTS); + qglColor3f(0, 0, 1); + while (nIndex-- > 0) + { + qglVertex3fv(*pSelectedPoints[nIndex]); + } + qglEnd(); + } + } + + if (bOverlay) + { + qglPointSize(6); + qglColor3f(0.5, 0.5, 0.5); + for ( i = 0 ; i < pm->width ; i++ ) + { + qglBegin(GL_POINTS); + for ( j = 0 ; j < pm->height ; j++ ) + { + qglVertex3fv(pm->ctrl[i][j].xyz); + } + qglEnd(); + } + } + //--qglEnable (GL_TEXTURE_2D); + //--qglEnable (GL_DEPTH_TEST); + + qglPopAttrib(); + +} + +/* OLD, NOT USED +================= +DrawPatchesXY +================= +*/ +void DrawPatchesXY( void ) { + int i; + patchMesh_t *pm; + + qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE); + + for ( i = 0 ; i < numPatchMeshes ; i++ ) + { + pm = &patchMeshes[i]; + + if (pm->bSelected) + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES]); + else + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES]); + + DrawPatchMesh( pm , pm->bSelected ); + } + + qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL); +} + +/* +================== +Patch_DrawXY +================== +*/ +void Patch_DrawXY(int n, bool bIsGhost) +{ + patchMesh_t *pm = &patchMeshes[n]; + qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE); + if (pm->bSelected) + { + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES]); + //qglDisable (GL_LINE_STIPPLE); + //qglLineWidth (1); + } + else + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES]); + + if (bIsGhost) + { + qglColor4fv(v4GhostColor); + qglLineStipple( 8, 0xAAAA); + qglEnable(GL_LINE_STIPPLE); + qglLineWidth(2); + } + + DrawPatchMesh( pm , pm->bSelected ); + qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + + if (bIsGhost) + { + qglDisable(GL_LINE_STIPPLE); + qglLineWidth(1); + } + + if (pm->bSelected) + { + //qglLineWidth (2); + //qglEnable (GL_LINE_STIPPLE); + } +} + + + +#if 0 +/* OLD, NOT USED +================= +DrawPatchesCamera +================= +*/ +void DrawPatchesCamera( void ) { + int i; + patchMesh_t *pm; + + qglColor3f (1,1,1); + + qglDisable( GL_CULL_FACE ); + + + if (g_bPatchWireFrame) + { + qglPushAttrib(GL_ALL_ATTRIB_BITS); + qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE); + qglDisable(GL_TEXTURE_2D); + } + + for ( i = 0 ; i < numPatchMeshes ; i++ ) + { + pm = &patchMeshes[i]; + qglBindTexture (GL_TEXTURE_2D, pm->d_texture->texture_number); + DrawPatchMesh( pm , i == g_nSelectedPatch, true ); + } + + if (g_bPatchWireFrame) + { + qglPopAttrib(); + } + qglEnable( GL_CULL_FACE ); +} +#endif +/* +================== +Patch_DrawCam +================== +*/ +void Patch_DrawCam(int n, bool bIsGhost) +{ + patchMesh_t *pm = &patchMeshes[n]; + qglColor3f (1,1,1); + qglPushAttrib(GL_ALL_ATTRIB_BITS); + + if (g_bPatchWireFrame) + { + qglDisable( GL_CULL_FACE ); + qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE); + qglDisable(GL_TEXTURE_2D); + + if (bIsGhost) + { + //qglColor4fv(v4GhostColor); + //qglLineStipple( 8, 0xAAAA); + //qglEnable(GL_LINE_STIPPLE); + int z=1; + } + + DrawPatchMesh( pm , pm->bSelected, true ); + + if (bIsGhost) + { + } + + qglEnable( GL_CULL_FACE ); + } + else + { + qglEnable( GL_CULL_FACE ); + qglCullFace(GL_FRONT); + qglBindTexture (GL_TEXTURE_2D, pm->d_texture->texture_number); + + if (pm->d_texture->bFromShader && pm->d_texture->fTrans < 1.0) + { + //qglEnable ( GL_BLEND ); + //qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + qglColor4f(pm->d_texture->color[0], pm->d_texture->color[1], pm->d_texture->color[2], pm->d_texture->fTrans); + } + + if (bIsGhost) + { + qglEnable ( GL_BLEND ); + qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + qglColor4fv(v4GhostColor); + } + + DrawPatchMesh( pm , pm->bSelected, true ); + + qglCullFace(GL_BACK); + //qglDisable(GL_TEXTURE_2D); + qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE); + + if (!bIsGhost) + { + qglDisable ( GL_BLEND ); + } + + DrawPatchMesh( pm , pm->bSelected, true ); + + if (bIsGhost) + { + qglDisable ( GL_BLEND ); + qglColor3f(1,1,1); + } + } + +#if 0 // this paints normal indicators on the ctrl points + //--qglDisable (GL_DEPTH_TEST); + qglDisable (GL_TEXTURE_2D); + qglColor3f (1,1,1); + + for (int i = 0; i < pm->width; i++) + { + for (int j = 0; j < pm->height; j++) + { + vec3_t temp; + qglBegin (GL_LINES); + qglVertex3fv (pm->ctrl[i][j].xyz); + VectorMA (pm->ctrl[i][j].xyz, 8, pm->ctrl[i][j].normal, temp); + qglVertex3fv (temp); + qglEnd (); + } + } + qglEnable (GL_TEXTURE_2D); + //--qglEnable (GL_DEPTH_TEST); +#endif + + + qglPopAttrib(); +} + + + + +void ConvexHullForSection( float section[2][4][7] ) { +} + +void BrushesForSection( float section[2][4][7] ) { +} + +// +//================ +//Patch_BuildPoints +//================ +void Patch_BuildPoints (brush_t *b) +{ + face_t *f; + b->patchBrush = false; + for (f=b->brush_faces ; f ; f=f->next) + { + if (f->texdef.flags & SURF_PATCH) + { + b->patchBrush = true; + //vec3_t vMin, vMax; + //Patch_CalcBounds(&patchMeshes[b->nPatchID], vMin, vMax); + //VectorCopy(vMin, b->mins); + //VectorCopy(vMax, b->maxs); + break; + } + } +} + + + +// +//=============== +//Patch_WriteFile +//=============== +// +#if 0 +void Patch_WriteFile (char *name) +{ + char patchName[1024]; + time_t ltime; + strcpy(patchName, name); + StripExtension (patchName); + strcat (patchName, ".patch"); + + FILE *file = fopen(patchName, "w"); + if (file) + { + time(<ime); + fprintf (file, "// %s saved on %s\n", name, ctime(<ime) ); + + for (int i = 0; i < numPatchMeshes; i++) + { + fprintf(file, "{\n"); + fprintf(file, " %s\n", patchMeshes[i].d_texture->name); + fprintf(file, " ( %i %i %i ) \n", patchMeshes[i].width, patchMeshes[i].height, patchMeshes[i].negative); + for (int w = 0; w < patchMeshes[i].width; w++) + { + for (int h = 0; h < patchMeshes[i].height; h++) + { + fprintf(file, " ( "); + for (int k = 0; k < 5; k++) + { + float f = patchMeshes[i].ctrl[w][h][k]; + if (f == int(f)) + fprintf(file, "%i ", (int)f); + else + fprintf(file, "%f ", f); + } + fprintf(file, ")\n"); + } + } + fprintf(file, "}\n"); + } + fclose(file); + } +} +#endif +/* +================== +Patch_ReadBuffer +================== +*/ +void Patch_ReadBuffer(char* pBuff, bool bSelect) +{ + StartTokenParsing (pBuff); + while (GetToken(true)) + { + if (strcmp(token, "{")) + break; + + // texture def + GetToken(true); + patchMeshes[numPatchMeshes].d_texture = Texture_ForName(token); + + GetToken(true); + if (strcmp(token, "(")) + break; + + // width, height, flags (currently only negative) + GetToken(false); + patchMeshes[numPatchMeshes].width = atoi(token); + + GetToken(false); + patchMeshes[numPatchMeshes].height = atoi(token); + + GetToken(false); + //patchMeshes[numPatchMeshes].negative = atoi(token); + + GetToken(false); + if (strcmp(token, ")")) + break; + + //FIXME: need error bad file handling + for (int w = 0; w < patchMeshes[numPatchMeshes].width; w++) + { + for (int h = 0; h < patchMeshes[numPatchMeshes].height; h++) + { + GetToken(true); + // assumptions made here as i dont want to error out + for (int k = 0; k < 3; k++) + { + GetToken(false); + patchMeshes[numPatchMeshes].ctrl[w][h].xyz[k] = atof(token); + } + for (k = 0; k < 2; k++) + { + GetToken(false); + patchMeshes[numPatchMeshes].ctrl[w][h].st[k] = atof(token); + } + GetToken(false); + } + } + GetToken(true); + if (strcmp(token, "}")) + break; + + brush_t *b = AddBrushForPatch(numPatchMeshes); + if (bSelect) + Select_Brush(b); + + numPatchMeshes++; + } +} + +/* +================== +Patch_ReadFile +================== +*/ +void Patch_ReadFile (const char *name) +{ + char *pBuff; + char patchName[1024]; + strcpy(patchName, name); + StripExtension (patchName); + strcat (patchName, ".patch"); + + if (LoadFile (patchName, (void **)&pBuff) != -1) + { + if (g_pParentWnd->MessageBox("Found a .patch file associated with this map. Do you want it loaded?", "Load old .patch", MB_YESNO) == IDYES) + { + //FIXME: need to clean up old patches + numPatchMeshes = 0; + Patch_ReadBuffer(pBuff); + } + } +} +/* +================== +Patch_Move +================== +*/ +void Patch_Move(int n, const vec3_t vMove, bool bRebuild) +{ + patchMeshes[n].bDirty = true; + for (int w = 0; w < patchMeshes[n].width; w++) + { + for (int h = 0; h < patchMeshes[n].height; h++) + { + VectorAdd(patchMeshes[n].ctrl[w][h].xyz, vMove, patchMeshes[n].ctrl[w][h].xyz); + } + } + if (bRebuild) + { + vec3_t vMin, vMax; + Patch_CalcBounds(&patchMeshes[n], vMin, vMax); + //Brush_RebuildBrush(patchMeshes[n].pSymbiot, vMin, vMax); + } +} + +/* +================== +Patch_ApplyMatrix +================== +*/ +void Patch_ApplyMatrix(int n, const vec3_t vOrigin, const vec3_t vMatrix[3]) +{ + vec3_t vTemp; + patchMesh_t* p = &patchMeshes[n]; + + for (int w = 0; w < p->width; w++) + { + for (int h = 0; h < p->height; h++) + { + if ( (g_qeglobals.d_select_mode == sel_curvepoint || g_bPatchBendMode) + && PointInMoveList(p->ctrl[w][h].xyz) == -1) + continue; + VectorSubtract (p->ctrl[w][h].xyz, vOrigin, vTemp); + for (int j = 0; j < 3; j++) + p->ctrl[w][h].xyz[j] = DotProduct(vTemp, vMatrix[j]) + vOrigin[j]; + } + } + vec3_t vMin, vMax; + Patch_CalcBounds(p, vMin, vMax); + Brush_RebuildBrush(p->pSymbiot, vMin, vMax); +} + +/* +================== +Patch_EditPatch +================== +*/ +void Patch_EditPatch() +{ + //--patchMesh_t* p = &patchMeshes[n]; + g_qeglobals.d_numpoints = 0; + g_qeglobals.d_num_move_points = 0; + + for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next) + { + if (pb->patchBrush) + { + patchMesh_t* p = &patchMeshes[pb->nPatchID]; + for ( int i = 0 ; i < p->width ; i++ ) + { + for ( int j = 0 ; j < p->height ; j++ ) + { + VectorCopy (p->ctrl[i][j].xyz, g_qeglobals.d_points[g_qeglobals.d_numpoints]); + g_qeglobals.d_numpoints++; + } + } + } + } + g_qeglobals.d_select_mode = sel_curvepoint; + //--g_nSelectedPatch = n; +} + + + +/* +================== +Patch_Deselect +================== +*/ +//FIXME: need all sorts of asserts throughout a lot of this crap +void Patch_Deselect() +{ + //--g_nSelectedPatch = -1; + g_qeglobals.d_select_mode = sel_brush; + for (int i = 0; i < numPatchMeshes; i++) + patchMeshes[i].bSelected = false; + + if (g_bPatchBendMode) + Patch_BendToggle(); + if (g_bPatchInsertMode) + Patch_InsDelToggle(); +} + + +/* +================== +Patch_Select +================== +*/ +void Patch_Select(int n) +{ + // maintained for point manip.. which i need to fix as this + // is pf error prone + //--g_nSelectedPatch = n; + patchMeshes[n].bSelected = true; +} + + +/* +================== +Patch_Deselect +================== +*/ +void Patch_Deselect(int n) +{ + patchMeshes[n].bSelected = false; +} + + +/* +================== +Patch_Delete +================== +*/ +void Patch_Delete(int n) +{ + // bump the array down + for (int i = n; i < numPatchMeshes; i++) + { + patchMeshes[i].pSymbiot->nPatchID--; + patchMeshes[i] = patchMeshes[i+1]; + } + numPatchMeshes--; +} + + +/* +================== +Patch_Scale +================== +*/ +void Patch_Scale(int n, const vec3_t vOrigin, const vec3_t vAmt, bool bRebuild) +{ + patchMesh_t* p = &patchMeshes[n]; + + for (int w = 0; w < p->width; w++) + { + for (int h = 0; h < p->height; h++) + { + if (g_qeglobals.d_select_mode == sel_curvepoint && PointInMoveList(p->ctrl[w][h].xyz) == -1) + continue; + for (int i=0 ; i<3 ; i++) + { + p->ctrl[w][h].xyz[i] -= vOrigin[i]; + p->ctrl[w][h].xyz[i] *= vAmt[i]; + p->ctrl[w][h].xyz[i] += vOrigin[i]; + } + } + } + if (bRebuild) + { + vec3_t vMin, vMax; + Patch_CalcBounds(p, vMin, vMax); + Brush_RebuildBrush(p->pSymbiot, vMin, vMax); + } +} + + +/* +================== +Patch_Cleanup +================== +*/ +void Patch_Cleanup() +{ + //--g_nSelectedPatch = -1; + numPatchMeshes = 0; +} + + + +/* +================== +Patch_SetView +================== +*/ +void Patch_SetView(int n) +{ + g_bSameView = (n == g_nPatchClickedView); + g_nPatchClickedView = n; +} + + +/* +================== +Patch_SetTexture +================== +*/ +// FIXME: need array validation throughout +void Patch_SetTexture(int n, texdef_t *tex_def) +{ + patchMeshes[n].d_texture = Texture_ForName(tex_def->name); +} + + +/* +================== +Patch_DragScale +================== +*/ +bool Patch_DragScale(int n, vec3_t vAmt, vec3_t vMove) +{ + vec3_t vMin, vMax, vScale, vTemp, vMid; + int i; + + patchMesh_t* p = &patchMeshes[n]; + + Patch_CalcBounds(p, vMin, vMax); + + VectorSubtract(vMax, vMin, vTemp); + + // if we are scaling in the same dimension the patch has no depth + for (i = 0; i < 3; i ++) + { + if (vTemp[i] == 0 && vMove[i] != 0) + { + //Patch_Move(n, vMove, true); + return false; + } + } + + for (i=0 ; i<3 ; i++) + vMid[i] = (vMin[i] + ((vMax[i] - vMin[i]) / 2)); + + for (i = 0; i < 3; i++) + { + if (vAmt[i] != 0) + { + vScale[i] = 1.0 + vAmt[i] / vTemp[i]; + } + else + { + vScale[i] = 1.0; + } + } + + Patch_Scale(n, vMid, vScale, false); + + VectorSubtract(vMax, vMin, vTemp); + + Patch_CalcBounds(p, vMin, vMax); + + VectorSubtract(vMax, vMin, vMid); + + VectorSubtract(vMid, vTemp, vTemp); + + VectorScale(vTemp, 0.5, vTemp); + + // abs of both should always be equal + if (!VectorCompare(vMove, vAmt)) + { + for (i = 0; i < 3; i++) + { + if (vMove[i] != vAmt[i]) + vTemp[i] = -(vTemp[i]); + } + } + + Patch_Move(n, vTemp); + return true; +} + + +/* +================== +Patch_WriteFile +================== +*/ +#if 0 +void Patch_WriteFile (CMemFile* pMemFile) +{ + for (int i = 0; i < numPatchMeshes; i++) + { + if (patchMeshes[i].bSelected) + { + MemFile_fprintf(pMemFile, "{\n"); + MemFile_fprintf(pMemFile, " %s\n", patchMeshes[i].d_texture->name); + MemFile_fprintf(pMemFile, " ( %i %i %i ) \n", patchMeshes[i].width, patchMeshes[i].height, patchMeshes[i].negative); + for (int w = 0; w < patchMeshes[i].width; w++) + { + for (int h = 0; h < patchMeshes[i].height; h++) + { + MemFile_fprintf(pMemFile, " ( "); + for (int k = 0; k < 5; k++) + { + float f = patchMeshes[i].ctrl[w][h][k]; + if (f == int(f)) + MemFile_fprintf(pMemFile, "%i ", (int)f); + else + MemFile_fprintf(pMemFile, "%f ", f); + } + MemFile_fprintf(pMemFile, ")\n"); + } + } + MemFile_fprintf(pMemFile, "}\n"); + } + } +} +#endif + +/* +================== +Patch_AddRow +================== +*/ +void Patch_AddRow(int n) +{ + vec3_t vMin, vMax, vTemp; + int i, j; + + patchMesh_t *p = &patchMeshes[n]; + + if (p->height+2 < MAX_PATCH_HEIGHT) + { + Patch_CalcBounds(p, vMin, vMax); + VectorSubtract(vMax, vMin, vTemp); + for (i = 0; i < 3; i++) + { + vTemp[i] /= p->height + 2; + } + + for (j = 0; j < p->width; j++) + { + VectorCopy(p->ctrl[j][p->height].xyz, p->ctrl[j][p->height+1].xyz); + VectorCopy(p->ctrl[j][p->height].xyz, p->ctrl[j][p->height+2].xyz); + p->height += 2; + i = 1; + while (i < p->height) + { + VectorAdd(p->ctrl[j][i].xyz, vTemp, p->ctrl[j][i].xyz); + i++; + } + } + + Patch_CalcBounds(p, vMin, vMax); + Brush_RebuildBrush(p->pSymbiot, vMin, vMax); + } +} + +/* +================== +Patch_InsertColumn +================== +*/ +void Patch_InsertColumn(int n, bool bAdd) +{ + patchMesh_t *p = &patchMeshes[n]; + int h, w, i, j; + vec3_t vTemp; + + if (p->width + 2 >= MAX_PATCH_WIDTH) + return; + + if (bAdd) // add column? + { + for (h = 0; h < p->height; h++) + { + j = p->width-1; + VectorSubtract(p->ctrl[j][h].xyz, p->ctrl[j-1][h].xyz, vTemp); + for (i = 0; i < 3; i++) + vTemp[i] /= 3; + + memcpy(&p->ctrl[j+2][h],&p->ctrl[j][h], sizeof(drawVert_t)); + memcpy(&p->ctrl[j][h],&p->ctrl[j-1][h], sizeof(drawVert_t)); + + VectorAdd(p->ctrl[j][h].xyz, vTemp, p->ctrl[j][h].xyz); + memcpy(&p->ctrl[j+1][h], &p->ctrl[j][h], sizeof(drawVert_t)); + VectorAdd(p->ctrl[j+1][h].xyz, vTemp, p->ctrl[j+1][h].xyz); + } + } + else + { + for (h = 0; h < p->height; h++) + { + w = p->width-1; + while (w >= 0) + { + memcpy(&p->ctrl[w+2][h],&p->ctrl[w][h], sizeof(drawVert_t)); + w--; + } + VectorSubtract(p->ctrl[1][h].xyz, p->ctrl[0][h].xyz, vTemp); + for (i = 0; i < 3; i++) + vTemp[i] /= 3; + VectorCopy(p->ctrl[0][h].xyz, p->ctrl[1][h].xyz); + VectorAdd(p->ctrl[1][h].xyz, vTemp, p->ctrl[1][h].xyz); + VectorCopy(p->ctrl[1][h].xyz, p->ctrl[2][h].xyz); + VectorAdd(p->ctrl[2][h].xyz, vTemp, p->ctrl[2][h].xyz); + } + } + p->width += 2; +} + + +/* +================== +Patch_InsertRow +================== +*/ +void Patch_InsertRow(int n, bool bAdd) +{ + patchMesh_t *p = &patchMeshes[n]; + int h, w, i, j; + vec3_t vTemp; + + if (p->height + 2 >= MAX_PATCH_HEIGHT) + return; + + if (bAdd) // add column? + { + for (w = 0; w < p->width; w++) + { + j = p->height-1; + VectorSubtract(p->ctrl[w][j].xyz, p->ctrl[w][j-1].xyz, vTemp); + for (i = 0; i < 3; i++) + vTemp[i] /= 3; + + memcpy(&p->ctrl[w][j+2],&p->ctrl[w][j], sizeof(drawVert_t)); + memcpy(&p->ctrl[w][j],&p->ctrl[w][j-1], sizeof(drawVert_t)); + + VectorAdd(p->ctrl[w][j].xyz, vTemp, p->ctrl[w][j].xyz); + memcpy(&p->ctrl[w][j+1], &p->ctrl[w][j], sizeof(drawVert_t)); + VectorAdd(p->ctrl[w][j+1].xyz, vTemp, p->ctrl[w][j+1].xyz); + } + } + else + { + for (w = 0; w < p->width; w++) + { + h = p->height-1; + while (h >= 0) + { + memcpy(&p->ctrl[w][h+2],&p->ctrl[w][h], sizeof(drawVert_t)); + h--; + } + VectorSubtract(p->ctrl[w][1].xyz, p->ctrl[w][0].xyz, vTemp); + for (i = 0; i < 3; i++) + vTemp[i] /= 3; + VectorCopy(p->ctrl[w][0].xyz, p->ctrl[w][1].xyz); + VectorAdd(p->ctrl[w][1].xyz, vTemp, p->ctrl[w][1].xyz); + VectorCopy(p->ctrl[w][1].xyz, p->ctrl[w][2].xyz); + VectorAdd(p->ctrl[w][2].xyz, vTemp, p->ctrl[w][2].xyz); + } + } + p->height += 2; +} + + +/* +================== +Patch_RemoveRow +================== +*/ +void Patch_RemoveRow(int n, bool bFirst) +{ + patchMesh_t *p = &patchMeshes[n]; + + if (p->height <= MIN_PATCH_HEIGHT) + return; + + p->height -= 2; + + if (bFirst) + { + for (int w = 0; w < p->width; w++) + { + for (int h = 0; h < p->height; h++) + { + memcpy(&p->ctrl[w][h], &p->ctrl[w][h+2], sizeof(drawVert_t)); + } + } + } +} + + +/* +================== +Patch_RemoveColumn +================== +*/ +void Patch_RemoveColumn(int n, bool bFirst) +{ + patchMesh_t *p = &patchMeshes[n]; + + if (p->width <= MIN_PATCH_WIDTH) + return; + + p->width -= 2; + + if (bFirst) + { + for (int h = 0; h < p->height; h++) + { + for (int w = 0; w < p->width; w++) + { + memcpy(&p->ctrl[w][h], &p->ctrl[w+2][h], sizeof(drawVert_t)); + } + } + } +} + + +/* +================== +Patch_AdjustColumns +================== +*/ +void Patch_AdjustColumns(int n, int nCols) +{ + vec3_t vTemp, vTemp2; + int i, w, h; + patchMesh_t *p = &patchMeshes[n]; + + if (nCols & 0x01 || p->width + nCols < 3 || p->width + nCols > MAX_PATCH_WIDTH) + return; + + // add in column adjustment + p->width += nCols; + + for (h = 0; h < p->height; h++) + { + // for each column, we need to evenly disperse p->width number + // of points across the old bounds + + // calc total distance to interpolate + VectorSubtract(p->ctrl[p->width - 1 - nCols][h].xyz, p->ctrl[0][h].xyz, vTemp); + + // amount per cycle + for (i = 0; i < 3; i ++) + { + vTemp2[i] = vTemp[i] / (p->width - 1); + } + + // move along + for (w = 0; w < p->width-1; w++) + { + VectorAdd(p->ctrl[w][h].xyz, vTemp2, p->ctrl[w+1][h].xyz); + } + + } + for ( w = 0 ; w < p->width ; w++ ) + { + for ( h = 0 ; h < p->height ; h++ ) + { + p->ctrl[w][h].st[0] = 4 * (float)w / (p->width - 1); + p->ctrl[w][h].st[1] = 4 * (float)h / (p->height - 1); + } + } +} + + +/* +================== +Patch_AdjustRows +================== +*/ +void Patch_AdjustRows(int n, int nRows) +{ + vec3_t vTemp, vTemp2; + int i, w, h; + patchMesh_t *p = &patchMeshes[n]; + + if (nRows & 0x01 || p->height + nRows < 3 || p->height + nRows > MAX_PATCH_HEIGHT) + return; + + // add in column adjustment + p->height += nRows; + + for (w = 0; w < p->width; w++) + { + // for each row, we need to evenly disperse p->height number + // of points across the old bounds + + // calc total distance to interpolate + VectorSubtract(p->ctrl[w][p->height - 1 - nRows].xyz, p->ctrl[w][0].xyz, vTemp); + + //vTemp[0] = vTemp[1] = vTemp[2] = 0; + //for (h = 0; h < p->height - nRows; h ++) + //{ + // VectorAdd(vTemp, p->ctrl[w][h], vTemp); + //} + + // amount per cycle + for (i = 0; i < 3; i ++) + { + vTemp2[i] = vTemp[i] / (p->height - 1); + } + + // move along + for (h = 0; h < p->height-1; h++) + { + VectorAdd(p->ctrl[w][h].xyz, vTemp2, p->ctrl[w][h+1].xyz); + } + + } + for ( w = 0 ; w < p->width ; w++ ) + { + for ( h = 0 ; h < p->height ; h++ ) + { + p->ctrl[w][h].st[0] = 4 * (float)w / (p->width - 1); + p->ctrl[w][h].st[1] = 4 * (float)h / (p->height - 1); + } + } +} + + +void Patch_DisperseRows() +{ + vec3_t vTemp, vTemp2; + int i, w, h; + + + for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next) + { + if (pb->patchBrush) + { + patchMesh_t *p = &patchMeshes[pb->nPatchID]; + Patch_Rebuild(p); + for (w = 0; w < p->width; w++) + { + // for each row, we need to evenly disperse p->height number + // of points across the old bounds + + // calc total distance to interpolate + VectorSubtract(p->ctrl[w][p->height - 1].xyz, p->ctrl[w][0].xyz, vTemp); + + //vTemp[0] = vTemp[1] = vTemp[2] = 0; + //for (h = 0; h < p->height - nRows; h ++) + //{ + // VectorAdd(vTemp, p->ctrl[w][h], vTemp); + //} + + // amount per cycle + for (i = 0; i < 3; i ++) + { + vTemp2[i] = vTemp[i] / (p->height - 1); + } + + // move along + for (h = 0; h < p->height-1; h++) + { + VectorAdd(p->ctrl[w][h].xyz, vTemp2, p->ctrl[w][h+1].xyz); + } + Patch_Naturalize(p); + + } + } + } + +} + +/* +================== +Patch_AdjustColumns +================== +*/ +void Patch_DisperseColumns() +{ + vec3_t vTemp, vTemp2; + int i, w, h; + + for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next) + { + if (pb->patchBrush) + { + patchMesh_t *p = &patchMeshes[pb->nPatchID]; + Patch_Rebuild(p); + + for (h = 0; h < p->height; h++) + { + // for each column, we need to evenly disperse p->width number + // of points across the old bounds + + // calc total distance to interpolate + VectorSubtract(p->ctrl[p->width - 1][h].xyz, p->ctrl[0][h].xyz, vTemp); + + // amount per cycle + for (i = 0; i < 3; i ++) + { + vTemp2[i] = vTemp[i] / (p->width - 1); + } + + // move along + for (w = 0; w < p->width-1; w++) + { + VectorAdd(p->ctrl[w][h].xyz, vTemp2, p->ctrl[w+1][h].xyz); + } + + } + Patch_Naturalize(p); + } + } +} + + + +/* +================== +Patch_AdjustSelected +================== +*/ +void Patch_AdjustSelected(bool bInsert, bool bColumn, bool bFlag) +{ + bool bUpdate = false; + for (brush_t* pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next) + { + if (pb->patchBrush) + { + if (bInsert) + { + if (bColumn) + { + Patch_InsertColumn(pb->nPatchID, bFlag); + } + else + { + Patch_InsertRow(pb->nPatchID, bFlag); + } + } + else + { + if (bColumn) + { + Patch_RemoveColumn(pb->nPatchID, bFlag); + } + else + { + Patch_RemoveRow(pb->nPatchID, bFlag); + } + } + bUpdate = true; + vec3_t vMin, vMax; + patchMesh_t *p = &patchMeshes[pb->nPatchID]; + Patch_CalcBounds(p, vMin, vMax); + Brush_RebuildBrush(p->pSymbiot, vMin, vMax); + } + } + if (bUpdate) + { + Sys_UpdateWindows(W_ALL); + } +} + + +/* +================== +Patch_AdjustSelectedRowCols +================== +*/ +void Patch_AdjustSelectedRowCols(int nRows, int nCols) +{ + for (brush_t* pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next) + { + if (pb->patchBrush) + { + Patch_InsertColumn(pb->nPatchID, false); + if (nRows != 0) + { + Patch_AdjustRows(pb->nPatchID, nRows); + } + + if (nCols != 0) + { + Patch_AdjustColumns(pb->nPatchID, nCols); + } + } + } +} + + +// parses a patch +brush_t* Patch_ParseOld() +{ + GetToken(true); + if (strcmp(token, "{")) + return NULL; + + // texture def + GetToken(true); + patchMeshes[numPatchMeshes].d_texture = Texture_ForName(token); + + GetToken(true); + if (strcmp(token, "(")) + return NULL; + + // width, height, flags (currently only negative) + GetToken(false); + patchMeshes[numPatchMeshes].width = atoi(token); + + GetToken(false); + patchMeshes[numPatchMeshes].height = atoi(token); + + GetToken(false); + //patchMeshes[numPatchMeshes].negative = atoi(token); + + GetToken(false); + if (strcmp(token, ")")) + return NULL; + + //FIXME: need error bad file handling + for (int w = 0; w < patchMeshes[numPatchMeshes].width; w++) + { + for (int h = 0; h < patchMeshes[numPatchMeshes].height; h++) + { + GetToken(true); + // assumptions made here as i dont want to error out + for (int k = 0; k < 3; k++) + { + GetToken(false); + patchMeshes[numPatchMeshes].ctrl[w][h].xyz[k] = atof(token); + } + for (k = 0; k < 2; k++) + { + GetToken(false); + patchMeshes[numPatchMeshes].ctrl[w][h].st[k] = atof(token); + } + GetToken(false); + } + } + + GetToken(true); + if (strcmp(token, "}")) + return NULL; + + + brush_t *b = AddBrushForPatch(numPatchMeshes, false); + numPatchMeshes++; + + return b; +} + +void Parse1DMatrix(int x, float* p) +{ + GetToken(true); // ( + for (int i = 0; i < x; i++) + { + GetToken(false); + p[i] = atof(token); + } + GetToken(true); // ) +} + +void Parse2DMatrix(int y, int x, float* p) +{ + GetToken(true); // ( + for (int i = 0; i < y; i++) + { + Parse1DMatrix(x, p + i*x); + } + GetToken(true); // ) +} + +void Parse3DMatrix(int z, int y, int x, float* p) +{ + GetToken(true); // ( + for (int i = 0; i < z; i++) + { + Parse2DMatrix(y, x, p + i*(x*MAX_PATCH_HEIGHT)); + } + GetToken(true); // ) +} + +// parses a patch +brush_t* Patch_Parse(bool bOld) +{ + //--if (bOld) + //--{ + //-- return Patch_ParseOld(); + //--} + + if (numPatchMeshes == MAX_PATCH_MESHES) + { //ran out of patches, doh! + Error ("Patch_Parse: numPatchMeshes reached!"); + } + GetToken(true); + + if (strcmp(token, "{")) + return NULL; + + // texture def + GetToken(true); + + // band-aid + if (strcmp(token, "(")) + { + patchMeshes[numPatchMeshes].d_texture = Texture_ForName(token); + GetToken(true); + } + else + { + patchMeshes[numPatchMeshes].d_texture = notexture; + Sys_Printf("Warning: Patch read with no texture, using notexture... \n"); + } + + if (strcmp(token, "(")) + return NULL; + + // width, height, contents, flags, value, {type} + GetToken(false); + patchMeshes[numPatchMeshes].width = atoi(token); + + GetToken(false); + patchMeshes[numPatchMeshes].height = atoi(token); + + GetToken(false); + patchMeshes[numPatchMeshes].contents = atoi(token); + + GetToken(false); + patchMeshes[numPatchMeshes].flags = atoi(token); + + GetToken(false); + patchMeshes[numPatchMeshes].value = atoi(token); + + if (!bOld) + { + GetToken(false); + patchMeshes[numPatchMeshes].type = atoi(token); + } + + GetToken(false); + if (strcmp(token, ")")) + return NULL; + + float ctrl[MAX_PATCH_WIDTH][MAX_PATCH_HEIGHT][5]; + Parse3DMatrix(patchMeshes[numPatchMeshes].width, patchMeshes[numPatchMeshes].height, 5, reinterpret_cast(&ctrl)); + + int w, h; + patchMesh_t *p = &patchMeshes[numPatchMeshes]; + + for (w = 0; w < p->width; w++) + { + for (h = 0; h < p->height; h++) + { + p->ctrl[w][h].xyz[0] = ctrl[w][h][0]; + p->ctrl[w][h].xyz[1] = ctrl[w][h][1]; + p->ctrl[w][h].xyz[2] = ctrl[w][h][2]; + p->ctrl[w][h].st[0] = ctrl[w][h][3]; + p->ctrl[w][h].st[1] = ctrl[w][h][4]; + } + } + + GetToken(true); + if (strcmp(token, "}")) + return NULL; + + brush_t *b = AddBrushForPatch(numPatchMeshes, false); + numPatchMeshes++; + + return b; +} + + +/* +================== +Patch_Write +================== +*/ +void Patch_Write (int n, CMemFile *file) +{ + patchMesh_t *p = &patchMeshes[n]; + //--MemFile_fprintf(file, " {\n patchDef3\n {\n"); + MemFile_fprintf(file, " {\n patchDef2\n {\n"); + MemFile_fprintf(file, " %s\n",p->d_texture->name); + //--MemFile_fprintf(file, " ( %i %i %i %i %i %i ) \n", p->width, p->height, p->contents, p->flags, p->value, p->type); + MemFile_fprintf(file, " ( %i %i %i %i %i ) \n", p->width, p->height, p->contents, p->flags, p->value); + + float ctrl[MAX_PATCH_WIDTH][MAX_PATCH_HEIGHT][5]; + + int w, h; + for (w = 0; w < p->width; w++) + { + for (h = 0; h < p->height; h++) + { + ctrl[w][h][0] = p->ctrl[w][h].xyz[0]; + ctrl[w][h][1] = p->ctrl[w][h].xyz[1]; + ctrl[w][h][2] = p->ctrl[w][h].xyz[2]; + ctrl[w][h][3] = p->ctrl[w][h].st[0]; + ctrl[w][h][4] = p->ctrl[w][h].st[1]; + } + } + + _Write3DMatrix(file, p->width, p->height, 5, reinterpret_cast(&ctrl)); +#if 0 + for (int w = 0; w < p->width; w++) + { + for (int h = 0; h < p->height; h++) + { + MemFile_fprintf(file, " ( "); + for (int k = 0; k < 5; k++) + { + float f = p->ctrl[w][h][k]; + if (f == int(f)) + { + MemFile_fprintf(file, "%i ", (int)f); + } + else + { + MemFile_fprintf(file, "%f ", f); + } + } + MemFile_fprintf(file, ")\n"); + } + } +#endif + MemFile_fprintf(file, " }\n }\n"); +} + +void Patch_Write (int n, FILE *file) +{ + patchMesh_t *p = &patchMeshes[n]; + //--fprintf(file, " {\n patchDef3\n {\n"); + fprintf(file, " {\n patchDef2\n {\n"); + fprintf(file, " %s\n",p->d_texture->name); + //--fprintf(file, " ( %i %i %i %i %i %i ) \n", p->width, p->height, p->contents, p->flags, p->value, p->type); + fprintf(file, " ( %i %i %i %i %i ) \n", p->width, p->height, p->contents, p->flags, p->value); + + float ctrl[MAX_PATCH_WIDTH][MAX_PATCH_HEIGHT][5]; + + int w, h; + for (w = 0; w < p->width; w++) + { + for (h = 0; h < p->height; h++) + { + ctrl[w][h][0] = p->ctrl[w][h].xyz[0]; + ctrl[w][h][1] = p->ctrl[w][h].xyz[1]; + ctrl[w][h][2] = p->ctrl[w][h].xyz[2]; + ctrl[w][h][3] = p->ctrl[w][h].st[0]; + ctrl[w][h][4] = p->ctrl[w][h].st[1]; + } + } + + _Write3DMatrix(file, p->width, p->height, 5, reinterpret_cast(&ctrl)); +#if 0 + for (int w = 0; w < p->width; w++) + { + for (int h = 0; h < p->height; h++) + { + fprintf(file, " ( "); + for (int k = 0; k < 5; k++) + { + float f = p->ctrl[w][h][k]; + if (f == int(f)) + { + fprintf(file, "%i ", (int)f); + } + else + { + fprintf(file, "%f ", f); + } + } + fprintf(file, ")\n"); + } + } +#endif + fprintf(file, " }\n }\n"); +} + + +/* +================== +Patch_RotateTexture +================== +*/ +void Patch_RotateTexture(int n, float fAngle) +{ + patchMesh_t *p = &patchMeshes[n]; + vec3_t vMin, vMax; + Patch_CalcBounds(p, vMin, vMax); + p->bDirty = true; + for (int w = 0; w < p->width; w++) + { + for (int h = 0; h < p->height; h++) + { + if (g_qeglobals.d_select_mode == sel_curvepoint && PointInMoveList(p->ctrl[w][h].xyz) == -1) + continue; + + float x = p->ctrl[w][h].st[0]; + float y = p->ctrl[w][h].st[1]; + p->ctrl[w][h].st[0] = x * cos(fAngle * Q_PI / 180) - y * sin(fAngle * Q_PI / 180); + p->ctrl[w][h].st[1] = y * cos(fAngle * Q_PI / 180) + x * sin(fAngle * Q_PI / 180); + } + } +} + + +/* +================== +Patch_ScaleTexture +================== +*/ +void Patch_ScaleTexture(int n, float fx, float fy, bool bFixup) +{ + // FIXME: + // this hack turns scales into 1.1 or 0.9 + if (bFixup) + { + fx = (fx == 0) ? 1.0 : (fx > 0) ? 0.9 : 1.10; + fy = (fy == 0) ? 1.0 : (fy > 0) ? 0.9 : 1.10; + } + else + { + if (fx == 0) + fx = 1.0; + if (fy == 0) + fy = 1.0; + } + + patchMesh_t *p = &patchMeshes[n]; + for (int w = 0; w < p->width; w++) + { + for (int h = 0; h < p->height; h++) + { + if (g_qeglobals.d_select_mode == sel_curvepoint && PointInMoveList(p->ctrl[w][h].xyz) == -1) + continue; + + p->ctrl[w][h].st[0] *= fx; + p->ctrl[w][h].st[1] *= fy; + } + } + p->bDirty = true; +} + + +/* +================== +Patch_ShiftTexture +================== +*/ +void Patch_ShiftTexture(int n, float fx, float fy) +{ + //if (fx) + // fx = (fx > 0) ? 0.1 : -0.1; + //if (fy) + // fy = (fy > 0) ? 0.1 : -0.1; + + fx = (abs(fx) >= 1) ? fx / 10 : fx; + fy = (abs(fy) >= 1) ? fy / 10 : fy; + + patchMesh_t *p = &patchMeshes[n]; + for (int w = 0; w < p->width; w++) + { + for (int h = 0; h < p->height; h++) + { + if (g_qeglobals.d_select_mode == sel_curvepoint && PointInMoveList(p->ctrl[w][h].xyz) == -1) + continue; + + p->ctrl[w][h].st[0] += fx; + p->ctrl[w][h].st[1] += fy; + } + } + p->bDirty = true; +} + +void patchInvert(patchMesh_t *p) +{ + drawVert_t vertTemp; + p->bDirty = true; + for ( int i = 0 ; i < p->width ; i++ ) + { + for (int j = 0; j < p->height / 2; j++) + { + memcpy(&vertTemp, &p->ctrl[i][p->height - 1- j], sizeof (drawVert_t)); + memcpy(&p->ctrl[i][p->height - 1 - j], &p->ctrl[i][j], sizeof(drawVert_t)); + memcpy(&p->ctrl[i][j], &vertTemp, sizeof(drawVert_t)); + } + } +} + +/* +================== +Patch_ToggleInverted +================== +*/ +void Patch_ToggleInverted() +{ + bool bUpdate = false; + + for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next) + { + if (pb->patchBrush) + { + bUpdate = true; + patchInvert(&patchMeshes[pb->nPatchID]); + } + } + + if (bUpdate) + { + Sys_UpdateWindows(W_ALL); + } +} + +/* +================== +Patch_ToggleInverted +================== +*/ +void Patch_InvertTexture(bool bY) +{ + bool bUpdate = false; + + float fTemp[2]; + for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next) + { + if (pb->patchBrush) + { + bUpdate = true; + patchMesh_t *p = &patchMeshes[pb->nPatchID]; + p->bDirty = true; + if (bY) + { + for ( int i = 0 ; i < p->height ; i++ ) + { + for (int j = 0; j < p->width / 2; j++) + { + memcpy(fTemp, &p->ctrl[p->width - 1- j][i].st[0], sizeof (float[2])); + memcpy(&p->ctrl[p->width - 1- j][i].st[0], &p->ctrl[j][i].st[0], sizeof(float[2])); + memcpy(&p->ctrl[j][i].st[0], fTemp, sizeof(float[2])); + } + } + } + else + { + for ( int i = 0 ; i < p->width ; i++ ) + { + for (int j = 0; j < p->height / 2; j++) + { + memcpy(fTemp, &p->ctrl[i][p->height - 1- j].st[0], sizeof (float[2])); + memcpy(&p->ctrl[i][p->height - 1 - j].st[0], &p->ctrl[i][j].st[0], sizeof(float[2])); + memcpy(&p->ctrl[i][j].st[0], fTemp, sizeof(float[2])); + } + } + } + } + } + + if (bUpdate) + { + Sys_UpdateWindows(W_ALL); + } +} + + + + +/* +================== +Patch_Save +================== + Saves patch ctrl info (originally to deal with a + cancel in the surface dialog +*/ +void Patch_Save(int n) +{ + patchMesh_t *p = &patchMeshes[n]; + patchSave.width = p->width; + patchSave.height = p->height; + memcpy(patchSave.ctrl, p->ctrl, sizeof(p->ctrl)); +} + + +/* +================== +Patch_Restore +================== +*/ +void Patch_Restore(int n) +{ + patchMesh_t *p = &patchMeshes[n]; + p->width = patchSave.width; + p->height = patchSave.height; + memcpy(p->ctrl, patchSave.ctrl, sizeof(p->ctrl)); +} + +void Patch_ResetTexturing(float fx, float fy) +{ + for (brush_t* pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next) + { + if (pb->patchBrush) + { + patchMesh_t *p = &patchMeshes[pb->nPatchID]; + p->bDirty = true; + for ( int i = 0 ; i < p->width ; i++ ) + { + for ( int j = 0 ; j < p->height ; j++ ) + { + p->ctrl[i][j].st[0] = fx * (float)i / (p->width - 1); + p->ctrl[i][j].st[1] = fy * (float)j / (p->height - 1); + } + } + } + } +} + + +void Patch_FitTexturing() +{ + for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next) + { + if (pb->patchBrush) + { + patchMesh_t *p = &patchMeshes[pb->nPatchID]; + p->bDirty = true; + for ( int i = 0 ; i < p->width ; i++ ) + { + for ( int j = 0 ; j < p->height ; j++ ) + { + p->ctrl[i][j].st[0] = 1 * (float)i / (p->width - 1); + p->ctrl[i][j].st[1] = 1 * (float)j / (p->height - 1); + } + } + } + } +} + + +void Patch_SetTextureInfo(texdef_t *pt) +{ + for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next) + { + if (pb->patchBrush) + { + if (pt->rotate) + Patch_RotateTexture(pb->nPatchID, pt->rotate); + + if (pt->shift[0] || pt->shift[1]) + Patch_ShiftTexture(pb->nPatchID, pt->shift[0], pt->shift[1]); + + if (pt->scale[0] || pt->scale[1]) + Patch_ScaleTexture(pb->nPatchID, pt->scale[0], pt->scale[1], false); + + patchMesh_t *p = &patchMeshes[pb->nPatchID]; + p->contents = pt->contents; + p->flags = pt->flags; + p->value = pt->value; + } + } +} + +bool OnlyPatchesSelected() +{ + if (selected_face || selected_brushes.next == &selected_brushes) + return false; + for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next) + { + if (!pb->patchBrush) + { + return false; + } + } + return true; +} + +bool AnyPatchesSelected() +{ + if (selected_face || selected_brushes.next == &selected_brushes) + return false; + for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next) + { + if (pb->patchBrush) + { + return true; + } + } + return false; +} + +void Patch_BendToggle() +{ + if (g_bPatchBendMode) + { + g_bPatchBendMode = false; + HideInfoDialog(); + g_pParentWnd->UpdatePatchToolbarButtons() ; + return; + } + + brush_t* b = selected_brushes.next; + + if (!QE_SingleBrush() || !b->patchBrush) + { + Sys_Printf("Must bend a single patch"); + return; + } + + Patch_Save(b->nPatchID); + g_bPatchBendMode = true; + g_nPatchBendState = BEND_SELECT_ROTATION; + g_bPatchAxisOnRow = true; + g_nPatchAxisIndex = 1; + ShowInfoDialog(g_pBendStateMsg[BEND_SELECT_ROTATION]); +} + +void Patch_BendHandleTAB() +{ + if (!g_bPatchBendMode) + { + return; + } + + brush_t* b = selected_brushes.next; + if (!QE_SingleBrush() || !b->patchBrush) + { + Patch_BendToggle(); + Sys_Printf("No patch to bend!"); + return; + } + + patchMesh_t *p = &patchMeshes[b->nPatchID]; + + bool bShift = (GetKeyState(VK_SHIFT) & 0x8000); + + if (g_nPatchBendState == BEND_SELECT_ROTATION) + { + // only able to deal with odd numbered rows/cols + g_nPatchAxisIndex += (bShift) ? -2 : 2; + if (g_bPatchAxisOnRow) + { + if ((bShift) ? g_nPatchAxisIndex <= 0 : g_nPatchAxisIndex >= p->height) + { + g_bPatchAxisOnRow = false; + g_nPatchAxisIndex = (bShift) ? p->width-1 : 1; + } + } + else + { + if ((bShift) ? g_nPatchAxisIndex <= 0 : g_nPatchAxisIndex >= p->width) + { + g_bPatchAxisOnRow = true; + g_nPatchAxisIndex = (bShift) ? p->height-1 : 1; + } + } + } + else + if (g_nPatchBendState == BEND_SELECT_ORIGIN) + { + g_nBendOriginIndex += (bShift) ? -1 : 1; + if (g_bPatchAxisOnRow) + { + if (bShift) + { + if (g_nBendOriginIndex < 0) + g_nBendOriginIndex = p->width-1; + } + else + { + if (g_nBendOriginIndex > p->width-1) + g_nBendOriginIndex = 0; + } + VectorCopy(p->ctrl[g_nBendOriginIndex][g_nPatchAxisIndex].xyz, g_vBendOrigin); + } + else + { + if (bShift) + { + if (g_nBendOriginIndex < 0) + g_nBendOriginIndex = p->height-1; + } + else + { + if (g_nBendOriginIndex > p->height-1) + g_nBendOriginIndex = 0; + } + VectorCopy(p->ctrl[g_nPatchAxisIndex][g_nBendOriginIndex].xyz, g_vBendOrigin); + } + } + else + if (g_nPatchBendState == BEND_SELECT_EDGE) + { + g_bPatchLowerEdge ^= 1; + } + Sys_UpdateWindows(W_ALL); +} + +void Patch_BendHandleENTER() +{ + if (!g_bPatchBendMode) + { + return; + } + + if (g_nPatchBendState < BEND_BENDIT) + { + g_nPatchBendState++; + ShowInfoDialog(g_pBendStateMsg[g_nPatchBendState]); + if (g_nPatchBendState == BEND_SELECT_ORIGIN) + { + g_vBendOrigin[0] = g_vBendOrigin[1] = g_vBendOrigin[2] = 0; + g_nBendOriginIndex = 0; + Patch_BendHandleTAB(); + } + else + if (g_nPatchBendState == BEND_SELECT_EDGE) + { + g_bPatchLowerEdge = true; + } + else + if (g_nPatchBendState == BEND_BENDIT) + { + // basically we go into rotation mode, set the axis to the center of the + } + } + else + { + // done + Patch_BendToggle(); + } + Sys_UpdateWindows(W_ALL); + +} + + +void Patch_BendHandleESC() +{ + if (!g_bPatchBendMode) + { + return; + } + Patch_BendToggle(); + brush_t* b = selected_brushes.next; + if (QE_SingleBrush() && b->patchBrush) + { + Patch_Restore(b->nPatchID); + } + Sys_UpdateWindows(W_ALL); +} + +void Patch_SetBendRotateOrigin(patchMesh_t *p) +{ +#if 1 + int nType = g_pParentWnd->ActiveXY()->GetViewType(); + int nDim3 = (nType == XY) ? 2 : (nType == YZ) ? 0 : 1; + + g_vBendOrigin[nDim3] = 0; + VectorCopy(g_vBendOrigin, g_pParentWnd->ActiveXY()->RotateOrigin()); + return; +#else + int nDim1 = (g_pParentWnd->ActiveXY()->GetViewType() == YZ) ? 1 : 0; + int nDim2 = (g_pParentWnd->ActiveXY()->GetViewType() == XY) ? 1 : 2; + + float fxLo, fyLo, fxHi, fyHi; + fxLo = fyLo = MAX_WORLD_COORD; + fxHi = fyHi = MIN_WORLD_COORD; + + if (g_bPatchAxisOnRow) + { + for (int i = 0; i < p->width; i++) + { + if (p->ctrl[i][g_nPatchAxisIndex].xyz[nDim1] < fxLo) + fxLo = p->ctrl[i][g_nPatchAxisIndex].xyz[nDim1]; + + if (p->ctrl[i][g_nPatchAxisIndex].xyz[nDim1] > fxHi) + fxHi = p->ctrl[i][g_nPatchAxisIndex].xyz[nDim1]; + + if (p->ctrl[i][g_nPatchAxisIndex].xyz[nDim2] < fyLo) + fyLo = p->ctrl[i][g_nPatchAxisIndex].xyz[nDim2]; + + if (p->ctrl[i][g_nPatchAxisIndex].xyz[nDim2] > fyHi) + fyHi = p->ctrl[i][g_nPatchAxisIndex].xyz[nDim2]; + } + } + else + { + for (int i = 0; i < p->height; i++) + { + if (p->ctrl[g_nPatchAxisIndex][i].xyz[nDim1] < fxLo) + fxLo = p->ctrl[g_nPatchAxisIndex][i].xyz[nDim1]; + + if (p->ctrl[g_nPatchAxisIndex][i].xyz[nDim1] > fxHi) + fxHi = p->ctrl[g_nPatchAxisIndex][i].xyz[nDim1]; + + if (p->ctrl[g_nPatchAxisIndex][i].xyz[nDim2] < fyLo) + fyLo = p->ctrl[g_nPatchAxisIndex][i].xyz[nDim2]; + + if (p->ctrl[g_nPatchAxisIndex][i].xyz[nDim2] > fyHi) + fyHi = p->ctrl[g_nPatchAxisIndex][i].xyz[nDim2]; + } + } + + g_pParentWnd->ActiveXY()->RotateOrigin()[0] = g_pParentWnd->ActiveXY()->RotateOrigin()[1] = g_pParentWnd->ActiveXY()->RotateOrigin()[2] = 0.0; + g_pParentWnd->ActiveXY()->RotateOrigin()[nDim1] = (fxLo + fxHi) * 0.5; + g_pParentWnd->ActiveXY()->RotateOrigin()[nDim2] = (fyLo + fyHi) * 0.5; +#endif +} + +// also sets the rotational origin +void Patch_SelectBendAxis() +{ + brush_t* b = selected_brushes.next; + if (!QE_SingleBrush() || !b->patchBrush) + { + // should not ever happen + Patch_BendToggle(); + return; + } + + patchMesh_t *p = &patchMeshes[b->nPatchID]; + if (g_bPatchAxisOnRow) + { + SelectRow(p, g_nPatchAxisIndex, false); + } + else + { + SelectColumn(p, g_nPatchAxisIndex, false); + } + + //FIXME: this only needs to be set once... + Patch_SetBendRotateOrigin(p); + +} + +void Patch_SelectBendNormal() +{ + brush_t* b = selected_brushes.next; + if (!QE_SingleBrush() || !b->patchBrush) + { + // should not ever happen + Patch_BendToggle(); + return; + } + + patchMesh_t *p = &patchMeshes[b->nPatchID]; + + g_qeglobals.d_num_move_points = 0; + if (g_bPatchAxisOnRow) + { + if (g_bPatchLowerEdge) + { + for (int j = 0; j < g_nPatchAxisIndex; j++) + SelectRow(p, j, true); + } + else + { + for (int j = p->height-1; j > g_nPatchAxisIndex; j--) + SelectRow(p, j, true); + } + } + else + { + if (g_bPatchLowerEdge) + { + for (int j = 0; j < g_nPatchAxisIndex; j++) + SelectColumn(p, j, true); + } + else + { + for (int j = p->width-1; j > g_nPatchAxisIndex; j--) + SelectColumn(p, j, true); + } + } + Patch_SetBendRotateOrigin(p); +} + + + +void Patch_InsDelToggle() +{ + if (g_bPatchInsertMode) + { + g_bPatchInsertMode = false; + HideInfoDialog(); + g_pParentWnd->UpdatePatchToolbarButtons() ; + return; + } + + brush_t* b = selected_brushes.next; + + if (!QE_SingleBrush() || !b->patchBrush) + { + Sys_Printf("Must work with a single patch"); + return; + } + + Patch_Save(b->nPatchID); + g_bPatchInsertMode = true; + g_nPatchInsertState = INSERT_SELECT_EDGE; + g_bPatchAxisOnRow = true; + g_nPatchAxisIndex = 0; + ShowInfoDialog(g_pInsertStateMsg[INSERT_SELECT_EDGE]); + +} + +void Patch_InsDelESC() +{ + if (!g_bPatchInsertMode) + { + return; + } + Patch_InsDelToggle(); + Sys_UpdateWindows(W_ALL); +} + + +void Patch_InsDelHandleENTER() +{ +} + +void Patch_InsDelHandleTAB() +{ + if (!g_bPatchInsertMode) + { + Patch_InsDelToggle(); + return; + } + + brush_t* b = selected_brushes.next; + if (!QE_SingleBrush() || !b->patchBrush) + { + Patch_BendToggle(); + Sys_Printf("No patch to bend!"); + return; + } + + patchMesh_t *p = &patchMeshes[b->nPatchID]; + + // only able to deal with odd numbered rows/cols + g_nPatchAxisIndex += 2; + if (g_bPatchAxisOnRow) + { + if (g_nPatchAxisIndex >= p->height-1) + { + g_bPatchAxisOnRow = false; + g_nPatchAxisIndex = 0; + } + } + else + { + if (g_nPatchAxisIndex >= p->width-1) + { + g_bPatchAxisOnRow = true; + g_nPatchAxisIndex = 0; + } + } + Sys_UpdateWindows(W_ALL); +} + + +void _Write1DMatrix (FILE *f, int x, float *m) { + int i; + + fprintf (f, "( "); + for (i = 0 ; i < x ; i++) { + if (m[i] == (int)m[i] ) { + fprintf (f, "%i ", (int)m[i]); + } else { + fprintf (f, "%f ", m[i]); + } + } + fprintf (f, ")"); +} + +void _Write2DMatrix (FILE *f, int y, int x, float *m) { + int i; + + fprintf (f, "( "); + for (i = 0 ; i < y ; i++) { + _Write1DMatrix (f, x, m + i*x); + fprintf (f, " "); + } + fprintf (f, ")\n"); +} + + +void _Write3DMatrix (FILE *f, int z, int y, int x, float *m) { + int i; + + fprintf (f, "(\n"); + for (i = 0 ; i < z ; i++) { + _Write2DMatrix (f, y, x, m + i*(x*MAX_PATCH_HEIGHT) ); + } + fprintf (f, ")\n"); +} + +void _Write1DMatrix (CMemFile *f, int x, float *m) { + int i; + + MemFile_fprintf (f, "( "); + for (i = 0 ; i < x ; i++) { + if (m[i] == (int)m[i] ) { + MemFile_fprintf (f, "%i ", (int)m[i]); + } else { + MemFile_fprintf (f, "%f ", m[i]); + } + } + MemFile_fprintf (f, ")"); +} + +void _Write2DMatrix (CMemFile *f, int y, int x, float *m) { + int i; + + MemFile_fprintf (f, "( "); + for (i = 0 ; i < y ; i++) { + _Write1DMatrix (f, x, m + i*x); + MemFile_fprintf (f, " "); + } + MemFile_fprintf (f, ")\n"); +} + + +void _Write3DMatrix (CMemFile *f, int z, int y, int x, float *m) { + int i; + + MemFile_fprintf (f, "(\n"); + for (i = 0 ; i < z ; i++) { + _Write2DMatrix (f, y, x, m + i*(x*MAX_PATCH_HEIGHT) ); + } + MemFile_fprintf (f, ")\n"); +} + + +void Patch_NaturalizeSelected(bool bCap, bool bCycleCap) +{ + for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next) + { + if (pb->patchBrush) + { + if (bCap) + Patch_CapTexture(&patchMeshes[pb->nPatchID], bCycleCap); + else + Patch_Naturalize(&patchMeshes[pb->nPatchID]); + } + } +} + +bool within(vec3_t vTest, vec3_t vTL, vec3_t vBR) +{ + int nDim1 = (g_pParentWnd->ActiveXY()->GetViewType() == YZ) ? 1 : 0; + int nDim2 = (g_pParentWnd->ActiveXY()->GetViewType() == XY) ? 1 : 2; + if ((vTest[nDim1] > vTL[nDim1] && vTest[nDim1] < vBR[nDim1]) || + (vTest[nDim1] < vTL[nDim1] && vTest[nDim1] > vBR[nDim1])) + { + if ((vTest[nDim2] > vTL[nDim2] && vTest[nDim2] < vBR[nDim2]) || + (vTest[nDim2] < vTL[nDim2] && vTest[nDim2] > vBR[nDim2])) + return true; + } + return false; +} + + +void Patch_SelectAreaPoints() +{ + g_qeglobals.d_num_move_points = 0; + g_nPatchClickedView = -1; + + for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next) + { + if (pb->patchBrush) + { + patchMesh_t *p = &patchMeshes[pb->nPatchID]; + for (int i = 0; i < p->width; i++) + { + for (int j = 0; j < p->height; j++) + { + if (within(p->ctrl[i][j].xyz, g_qeglobals.d_vAreaTL, g_qeglobals.d_vAreaBR)) + { + g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = p->ctrl[i][j].xyz; + } + } + } + } + } +} + +const char* Patch_GetTextureName() +{ + brush_t* b = selected_brushes.next; + if (b->patchBrush) + { + patchMesh_t *p = &patchMeshes[b->nPatchID]; + if (p->d_texture->name) + return p->d_texture->name; + } + return ""; +} + +patchMesh_t* Patch_Duplicate(patchMesh_t *pFrom) +{ + patchMesh_t* p = &patchMeshes[numPatchMeshes]; + memcpy(p, pFrom , sizeof(patchMesh_t)); + p->bSelected = false; + p->bDirty = true; + p->bOverlay = false; + p->nListID = -1; + AddBrushForPatch(numPatchMeshes); + numPatchMeshes++; + return p; +} + + +void Patch_Thicken(int nAmount, bool bSeam) +{ + int i, j, h, w; + brush_t *b; + patchMesh_t *pSeam; + vec3_t vMin, vMax; + brush_t* brushes[5]; + brushes[0] = brushes[1] = brushes[2] = brushes[3] = brushes[4] = NULL; + int nIndex = 0; + + nAmount = -nAmount; + + for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next) + { + if (pb->patchBrush) + { + patchMesh_t *p = &patchMeshes[pb->nPatchID]; + Patch_MeshNormals(*p); + patchMesh_t *pNew = Patch_Duplicate(p); + for (i = 0; i < p->width; i++) + { + for (j = 0; j < p->height; j++) + { + VectorMA (p->ctrl[i][j].xyz, nAmount, p->ctrl[i][j].normal, pNew->ctrl[i][j].xyz); + } + } + + Patch_Rebuild(pNew); + pNew->type |= PATCH_THICK; + brushes[nIndex++] = pNew->pSymbiot; + + if (bSeam) + { + + // FIXME: this should detect if any edges of the patch are closed and act appropriately + // + if (!(p->type & PATCH_CYLINDER)) + { + b = Patch_GenericMesh(3, p->height, 2, false); + pSeam = &patchMeshes[b->nPatchID]; + pSeam->type |= PATCH_SEAM; + for (i = 0; i < p->height; i++) + { + VectorCopy(p->ctrl[0][i].xyz, pSeam->ctrl[0][i].xyz); + VectorCopy(pNew->ctrl[0][i].xyz, pSeam->ctrl[2][i].xyz); + VectorAdd(pSeam->ctrl[0][i].xyz, pSeam->ctrl[2][i].xyz, pSeam->ctrl[1][i].xyz); + VectorScale(pSeam->ctrl[1][i].xyz, 0.5, pSeam->ctrl[1][i].xyz); + } + + + Patch_CalcBounds(pSeam, vMin, vMax); + Brush_RebuildBrush(pSeam->pSymbiot, vMin, vMax); + //--Patch_CapTexture(pSeam); + Patch_Naturalize(pSeam); + patchInvert(pSeam); + brushes[nIndex++] = b; + + w = p->width - 1; + b = Patch_GenericMesh(3, p->height, 2, false); + pSeam = &patchMeshes[b->nPatchID]; + pSeam->type |= PATCH_SEAM; + for (i = 0; i < p->height; i++) + { + VectorCopy(p->ctrl[w][i].xyz, pSeam->ctrl[0][i].xyz); + VectorCopy(pNew->ctrl[w][i].xyz, pSeam->ctrl[2][i].xyz); + VectorAdd(pSeam->ctrl[0][i].xyz, pSeam->ctrl[2][i].xyz, pSeam->ctrl[1][i].xyz); + VectorScale(pSeam->ctrl[1][i].xyz, 0.5, pSeam->ctrl[1][i].xyz); + } + Patch_CalcBounds(pSeam, vMin, vMax); + Brush_RebuildBrush(pSeam->pSymbiot, vMin, vMax); + //--Patch_CapTexture(pSeam); + Patch_Naturalize(pSeam); + brushes[nIndex++] = b; + } + + //--{ + // otherwise we will add one per end + b = Patch_GenericMesh(p->width, 3, 2, false); + pSeam = &patchMeshes[b->nPatchID]; + pSeam->type |= PATCH_SEAM; + for (i = 0; i < p->width; i++) + { + VectorCopy(p->ctrl[i][0].xyz, pSeam->ctrl[i][0].xyz); + VectorCopy(pNew->ctrl[i][0].xyz, pSeam->ctrl[i][2].xyz); + VectorAdd(pSeam->ctrl[i][0].xyz, pSeam->ctrl[i][2].xyz, pSeam->ctrl[i][1].xyz); + VectorScale(pSeam->ctrl[i][1].xyz, 0.5, pSeam->ctrl[i][1].xyz); + } + + + Patch_CalcBounds(pSeam, vMin, vMax); + Brush_RebuildBrush(pSeam->pSymbiot, vMin, vMax); + //--Patch_CapTexture(pSeam); + Patch_Naturalize(pSeam); + patchInvert(pSeam); + brushes[nIndex++] = b; + + h = p->height - 1; + b = Patch_GenericMesh(p->width, 3, 2, false); + pSeam = &patchMeshes[b->nPatchID]; + pSeam->type |= PATCH_SEAM; + for (i = 0; i < p->width; i++) + { + VectorCopy(p->ctrl[i][h].xyz, pSeam->ctrl[i][0].xyz); + VectorCopy(pNew->ctrl[i][h].xyz, pSeam->ctrl[i][2].xyz); + VectorAdd(pSeam->ctrl[i][0].xyz, pSeam->ctrl[i][2].xyz, pSeam->ctrl[i][1].xyz); + VectorScale(pSeam->ctrl[i][1].xyz, 0.5, pSeam->ctrl[i][1].xyz); + } + Patch_CalcBounds(pSeam, vMin, vMax); + Brush_RebuildBrush(pSeam->pSymbiot, vMin, vMax); + //--Patch_CapTexture(pSeam); + Patch_Naturalize(pSeam); + brushes[nIndex++] = b; + + while (nIndex > 0) + { + nIndex--; + if (brushes[nIndex]) + { + Select_Brush(brushes[nIndex]); + } + } + eclass_t *pecNew = Eclass_ForName("func_group", false); + if (pecNew) + { + entity_t *e = Entity_Create(pecNew); + SetKeyValue(e, "type", "patchThick"); + } + + + //--} + } + patchInvert(pNew); + } + } +} + + +/* +lets get another list together as far as necessities.. + +*snapping stuff to the grid (i will only snap movements by the mouse to the grid.. snapping the rotational bend stuff will fubar everything) + +capping bevels/endcaps + +hot keys + +texture fix for caps + +clear clipboard + +*region fix + +*surface dialog + +*/ + +void Patch_SetOverlays() +{ + for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next) + { + if (pb->patchBrush) + { + patchMeshes[pb->nPatchID].bOverlay = true; + } + } +} + + + +void Patch_ClearOverlays() +{ + for ( int i = 0 ; i < numPatchMeshes ; i++ ) + { + patchMeshes[i].bOverlay = false; + } +} + +void Patch_Transpose() +{ + int i, j, l, w; + drawVert_t dv; + for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next) + { + if (pb->patchBrush) + { + patchMesh_t *p = &patchMeshes[pb->nPatchID]; + + patchMesh_t ptemp; + memcpy(&ptemp, &p, sizeof(patchMesh_t)); + + l = p->width > p->height ? p->width : p->height; + for ( i = 0 ; i < l ; i++ ) + { + for ( j = i+1 ; j < l ; j++ ) + { + memcpy(&dv, &p->ctrl[i][j], sizeof(drawVert_t)); + memcpy(&p->ctrl[i][j], &p->ctrl[j][i], sizeof(drawVert_t)); + memcpy(&p->ctrl[j][i], &dv, sizeof(drawVert_t)); + } + } + w = p->width; + p->width = p->height; + p->height = w; + patchInvert(p); + Patch_Rebuild(p); + } + } +} + + + +void Select_SnapToGrid() +{ + int i,j, k; + for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next) + { + if (pb->patchBrush) + { + patchMesh_t *p = &patchMeshes[pb->nPatchID]; +#if 0 + float ctrl[MAX_PATCH_WIDTH][MAX_PATCH_HEIGHT][5]; + memcpy(ctrl, p->ctrl, sizeof(p->ctrl)); + i = p->width; + p->width = p->height; + p->height = i; + for (i = 0; i < p->width; i++) + { + int l = p->height-1; + for (j = 0; j < p->height; j++) + { + for (k = 0; k < 5; k++) + { + p->ctrl[i][l][k] = ctrl[j][i][k]; + } + l--; + } + } +#else + for (i = 0; i < p->width; i++) + { + for (j = 0; j < p->height; j++) + { + for (k = 0; k < 3; k++) + { + p->ctrl[i][j].xyz[k] = floor(p->ctrl[i][j].xyz[k] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + } + } + } +#endif + vec3_t vMin, vMax; + Patch_CalcBounds(p, vMin, vMax); + Brush_RebuildBrush(p->pSymbiot, vMin, vMax); + } + else + { + Brush_SnapToGrid(pb); + } + } +} + +// there's already a function called Patch_GetTextureName, but that assumes you want it from the selected list, +// rather than using one you supply, which is daft. Hence this hacky name... +// +const char *Patch_FromBrush_GetTextureName(brush_t* pb) +{ + if (pb->patchBrush) + { + patchMesh_t *p = &patchMeshes[pb->nPatchID]; + if (p->d_texture->name) + return p->d_texture->name; + } + return ""; +} + +// return is just whether a replace occured or not (for friendly stat-print later)... +// +bool Patch_FindReplaceTexture(brush_t *pb, const char *pFind, const char *pReplace, bool bForce) +{ + if (pb->patchBrush) + { + patchMesh_t *p = &patchMeshes[pb->nPatchID]; + if (bForce || strcmpi(p->d_texture->name, pFind) == 0) + { + p->d_texture = Texture_ForName(pReplace); + //strcpy(p->d_texture->name, pReplace); + return true; + } + } + return false; +} + +void Patch_ReplaceQTexture(brush_t *pb, qtexture_t *pOld, qtexture_t *pNew) +{ + if (pb->patchBrush) + { + patchMesh_t *p = &patchMeshes[pb->nPatchID]; + if (p->d_texture == pOld) + { + p->d_texture = pNew; + } + } +} + +void Patch_MakeNonSolid(brush_t* pb) +{ +/* + if (pb->patchBrush) + { + patchMesh_t *p = &patchMeshes[pb->nPatchID]; + + brush_t* pb2 = p->pSymbiot; + + for (face_t*f=pb->brush_faces ; f ; f=f->next) + f->texdef.flags |= SURF_NONSOLID; + } +*/ +} + +void Patch_MakeSolid(brush_t* pb) +{ +/* + if (pb->patchBrush) + { + patchMesh_t *p = &patchMeshes[pb->nPatchID]; + + brush_t* pb2 = p->pSymbiot; + + for (face_t*f=pb->brush_faces ; f ; f=f->next) + f->texdef.flags &= ~SURF_NONSOLID; + } +*/ +} + +//Real nitpicky, but could you make CTRL-S save the current map with the current name? (ie: File/Save) +/* +Feature addition. +When reading in textures, please check for the presence of a file called "textures.link" or something, which contains one line such as; + +g:\quake3\baseq3\textures\common + + So that, when I'm reading in, lets say, my \eerie directory, it goes through and adds my textures to the palette, along with everything in common. + + Don't forget to add "Finer texture alignment" to the list. I'd like to be able to move in 0.1 increments using the Shift-Arrow Keys. + + No. Sometimes textures are drawn the wrong way on patches. We'd like the ability to flip a texture. Like the way X/Y scale -1 used to worked. + + 1) Easier way of deleting rows, columns +2) Fine tuning of textures on patches (X/Y shifts other than with the surface dialog) +2) Patch matrix transposition + + 1) Actually, bump texture flipping on patches to the top of the list of things to do. +2) When you select a patch, and hit S, it should read in the selected patch texture. Should not work if you multiselect patches and hit S +3) Brandon has a wierd anomoly. He fine-tunes a patch with caps. It looks fine when the patch is selected, but as soon as he escapes out, it reverts to it's pre-tuned state. When he selects the patch again, it looks tuned + + +*1) Flipping textures on patches +*2) When you select a patch, and hit S, it should read in the selected patch texture. Should not work if you multiselect patches and hit S +3) Easier way of deleting rows columns +*4) Thick Curves +5) Patch matrix transposition +6) Inverted cylinder capping +*7) bugs +*8) curve speed + + Have a new feature request. "Compute Bounding Box" for mapobjects (md3 files). This would be used for misc_mapobject (essentially, drop in 3DS Max models into our maps) + + Ok, Feature Request. Load and draw MD3's in the Camera view with proper bounding boxes. This should be off misc_model + + Feature Addition: View/Hide Hint Brushes -- This should be a specific case. +*/ \ No newline at end of file diff --git a/utils/Radiant/Radiant.dsp b/utils/Radiant/Radiant.dsp new file mode 100644 index 0000000..bdbbff3 --- /dev/null +++ b/utils/Radiant/Radiant.dsp @@ -0,0 +1,916 @@ +# Microsoft Developer Studio Project File - Name="Radiant" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=Radiant - Win32 Q3Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Radiant.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Radiant.mak" CFG="Radiant - Win32 Q3Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Radiant - Win32 Q3Release" (based on "Win32 (x86) Application") +!MESSAGE "Radiant - Win32 Q3Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName ""$/Utils/Radiant", GKOAAAAA" +# PROP Scc_LocalPath "." +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Radiant - Win32 Q3Release" + +# PROP BASE Use_MFC 5 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Radiant1" +# PROP BASE Intermediate_Dir "Radiant1" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 5 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Q3Release" +# PROP Intermediate_Dir "Q3Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GR /GX /Zd /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /G6 /MT /W3 /GR /GX /Zi /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "QUAKE3" /Fr /Yu"stdafx.h" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 opengl32.lib glu32.lib /nologo /subsystem:windows /machine:I386 /nodefaultlib:"libc.lib msvcrtd.lib" +# SUBTRACT BASE LINK32 /debug +# ADD LINK32 winmm.lib ..\libs\pak.lib /nologo /subsystem:windows /machine:I386 /nodefaultlib:"libc.lib msvcrtd.lib" /out:"Q3Release/chcRadiant.exe" +# SUBTRACT LINK32 /incremental:yes /debug + +!ELSEIF "$(CFG)" == "Radiant - Win32 Q3Debug" + +# PROP BASE Use_MFC 5 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Radiant2" +# PROP BASE Intermediate_Dir "Radiant2" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 5 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Q3Debug" +# PROP Intermediate_Dir "Q3Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GR /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /G6 /MTd /W3 /GR /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "QUAKE3" /FR /Yu"stdafx.h" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 opengl32.lib glu32.lib /nologo /subsystem:windows /debug /machine:I386 /nodefaultlib:"libc.lib msvcrtd.lib" /pdbtype:sept +# SUBTRACT BASE LINK32 /nodefaultlib +# ADD LINK32 winmm.lib ..\libs\pakd.lib /nologo /subsystem:windows /debug /machine:I386 /nodefaultlib:"libc.lib msvcrtd.lib" /out:"Q3Debug/chcRadiant.exe" /pdbtype:sept +# SUBTRACT LINK32 /nodefaultlib + +!ENDIF + +# Begin Target + +# Name "Radiant - Win32 Q3Release" +# Name "Radiant - Win32 Q3Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Group "QE4 Files" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Bmp.cpp +# End Source File +# Begin Source File + +SOURCE=.\brush.cpp +# End Source File +# Begin Source File + +SOURCE=.\cmdlib.cpp +# End Source File +# Begin Source File + +SOURCE=.\csg.cpp +# End Source File +# Begin Source File + +SOURCE=.\drag.cpp +# End Source File +# Begin Source File + +SOURCE=.\eclass.cpp +# End Source File +# Begin Source File + +SOURCE=.\entity.cpp +# End Source File +# Begin Source File + +SOURCE=.\lbmlib.cpp +# End Source File +# Begin Source File + +SOURCE=.\map.cpp +# End Source File +# Begin Source File + +SOURCE=.\mathlib.cpp +# End Source File +# Begin Source File + +SOURCE=.\mru.cpp +# End Source File +# Begin Source File + +SOURCE=.\parse.cpp +# End Source File +# Begin Source File + +SOURCE=.\points.cpp +# End Source File +# Begin Source File + +SOURCE=.\qe3.cpp +# End Source File +# Begin Source File + +SOURCE=.\select.cpp +# End Source File +# Begin Source File + +SOURCE=.\vertsel.cpp +# End Source File +# Begin Source File + +SOURCE=.\win_dlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\win_ent.cpp +# End Source File +# Begin Source File + +SOURCE=.\win_main.cpp +# End Source File +# Begin Source File + +SOURCE=.\win_qe3.cpp +# End Source File +# Begin Source File + +SOURCE=.\z.cpp +# End Source File +# End Group +# Begin Group "From others" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\fnmatch.cpp +# End Source File +# Begin Source File + +SOURCE=.\inc.cpp +# End Source File +# End Group +# Begin Group "Quake 3" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Quake3\cbrush.cpp +# End Source File +# Begin Source File + +SOURCE=.\Quake3\pmesh.cpp +# End Source File +# End Group +# Begin Group "JPEG Source" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=".\jpeg-6\jcomapi.c" +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jdapimin.c" +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jdapistd.c" +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jdatasrc.c" +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jdcoefct.c" +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jdcolor.c" +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jddctmgr.c" +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jdhuff.c" +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jdinput.c" +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jdmainct.c" +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jdmarker.c" +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jdmaster.c" +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jdpostct.c" +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jdsample.c" +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jdtrans.c" +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jerror.c" +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jidctflt.c" +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jmemmgr.c" +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jmemnobs.c" +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jutils.c" +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# End Group +# Begin Group "JPEG Headers" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=".\jpeg-6\jconfig.h" +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jdct.h" +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jdhuff.h" +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jerror.h" +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jinclude.h" +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jmemsys.h" +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jmorecfg.h" +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jpegint.h" +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jpeglib.h" +# End Source File +# Begin Source File + +SOURCE=".\jpeg-6\jversion.h" +# End Source File +# End Group +# Begin Source File + +SOURCE=.\autocaulk.cpp +# End Source File +# Begin Source File + +SOURCE=.\BrushScript.cpp +# End Source File +# Begin Source File + +SOURCE=.\BSInput.cpp +# End Source File +# Begin Source File + +SOURCE=.\CamWnd.cpp +# End Source File +# Begin Source File + +SOURCE=.\CapDialog.cpp +# End Source File +# Begin Source File + +SOURCE=.\cbrushstub.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CommandsDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\DialogInfo.cpp +# End Source File +# Begin Source File + +SOURCE=.\DialogTextures.cpp +# End Source File +# Begin Source File + +SOURCE=.\DialogThick.cpp +# End Source File +# Begin Source File + +SOURCE=.\EditWnd.cpp +# End Source File +# Begin Source File + +SOURCE=.\EntityListDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\EntKeyFindReplace.cpp +# End Source File +# Begin Source File + +SOURCE=.\FindTextureDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\GetString.cpp +# End Source File +# Begin Source File + +SOURCE=.\groupnames.cpp +# End Source File +# Begin Source File + +SOURCE=.\JPGFile.cpp +# End Source File +# Begin Source File + +SOURCE=.\JPGFile.h +# End Source File +# Begin Source File + +SOURCE=.\LstToolBar.cpp +# End Source File +# Begin Source File + +SOURCE=.\MainFrm.cpp +# End Source File +# Begin Source File + +SOURCE=.\MapInfo.cpp +# End Source File +# Begin Source File + +SOURCE=.\NewProjDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\oddbits.cpp +# End Source File +# Begin Source File + +SOURCE=.\PatchDensityDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\PlugIn.cpp +# End Source File +# Begin Source File + +SOURCE=.\PlugInManager.cpp +# End Source File +# Begin Source File + +SOURCE=.\PrefsDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\qgl_win.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=.\radbsp.cpp +# End Source File +# Begin Source File + +SOURCE=.\RADEditWnd.cpp +# End Source File +# Begin Source File + +SOURCE=.\Radiant.cpp +# End Source File +# Begin Source File + +SOURCE=.\RotateDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\ScaleDialog.cpp +# End Source File +# Begin Source File + +SOURCE=.\ScriptDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\ShaderInfo.cpp +# End Source File +# Begin Source File + +SOURCE=.\sourcesafe.cpp +# End Source File +# Begin Source File + +SOURCE=.\SourceSafeSettings.cpp +# End Source File +# Begin Source File + +SOURCE=.\StdAfx.cpp +# ADD CPP /Yc"stdafx.h" +# End Source File +# Begin Source File + +SOURCE=.\SurfaceDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\TexEdit.cpp +# End Source File +# Begin Source File + +SOURCE=.\TextureBar.cpp +# End Source File +# Begin Source File + +SOURCE=.\TextureLayout.cpp +# End Source File +# Begin Source File + +SOURCE=.\TexWnd.cpp +# End Source File +# Begin Source File + +SOURCE=.\XYWnd.cpp +# End Source File +# Begin Source File + +SOURCE=.\zclip.cpp +# End Source File +# Begin Source File + +SOURCE=.\ZWnd.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\3DFXCamWnd.h +# End Source File +# Begin Source File + +SOURCE=.\autocaulk.h +# End Source File +# Begin Source File + +SOURCE=.\BMP.H +# End Source File +# Begin Source File + +SOURCE=.\BRUSH.H +# End Source File +# Begin Source File + +SOURCE=.\BSInput.h +# End Source File +# Begin Source File + +SOURCE=.\CAMERA.H +# End Source File +# Begin Source File + +SOURCE=.\CamWnd.h +# End Source File +# Begin Source File + +SOURCE=.\CapDialog.h +# End Source File +# Begin Source File + +SOURCE=.\ChildFrm.h +# End Source File +# Begin Source File + +SOURCE=.\CMDLIB.H +# End Source File +# Begin Source File + +SOURCE=.\CommandsDlg.h +# End Source File +# Begin Source File + +SOURCE=.\DialogInfo.h +# End Source File +# Begin Source File + +SOURCE=.\DialogTextures.h +# End Source File +# Begin Source File + +SOURCE=.\DialogThick.h +# End Source File +# Begin Source File + +SOURCE=.\EditWnd.h +# End Source File +# Begin Source File + +SOURCE=.\ENTITY.H +# End Source File +# Begin Source File + +SOURCE=.\EntityListDlg.h +# End Source File +# Begin Source File + +SOURCE=.\ENTITYW.H +# End Source File +# Begin Source File + +SOURCE=.\EntKeyFindReplace.h +# End Source File +# Begin Source File + +SOURCE=.\FindTextureDlg.h +# End Source File +# Begin Source File + +SOURCE=.\FNMATCH.H +# End Source File +# Begin Source File + +SOURCE=.\GetString.h +# End Source File +# Begin Source File + +SOURCE=.\groupnames.h +# End Source File +# Begin Source File + +SOURCE=.\INC.H +# End Source File +# Begin Source File + +SOURCE=.\LBMLIB.H +# End Source File +# Begin Source File + +SOURCE=.\LstToolBar.h +# End Source File +# Begin Source File + +SOURCE=.\MainFrm.h +# End Source File +# Begin Source File + +SOURCE=.\MAP.H +# End Source File +# Begin Source File + +SOURCE=.\MapInfo.h +# End Source File +# Begin Source File + +SOURCE=.\MATHLIB.H +# End Source File +# Begin Source File + +SOURCE=.\MRU.H +# End Source File +# Begin Source File + +SOURCE=.\NewProjDlg.h +# End Source File +# Begin Source File + +SOURCE=.\oddbits.h +# End Source File +# Begin Source File + +SOURCE=..\Libs\pakstuff.h +# End Source File +# Begin Source File + +SOURCE=.\PARSE.H +# End Source File +# Begin Source File + +SOURCE=.\PatchDensityDlg.h +# End Source File +# Begin Source File + +SOURCE=.\PlugIn.h +# End Source File +# Begin Source File + +SOURCE=.\PlugInManager.h +# End Source File +# Begin Source File + +SOURCE=.\PrefsDlg.h +# End Source File +# Begin Source File + +SOURCE=.\qe3.h +# End Source File +# Begin Source File + +SOURCE=.\QEDEFS.H +# End Source File +# Begin Source File + +SOURCE=.\qerplugin.h +# End Source File +# Begin Source File + +SOURCE=.\QERTYPES.H +# End Source File +# Begin Source File + +SOURCE=.\QFILES.H +# End Source File +# Begin Source File + +SOURCE=.\QGL.H +# End Source File +# Begin Source File + +SOURCE=.\RADEditWnd.h +# End Source File +# Begin Source File + +SOURCE=.\Radiant.h +# End Source File +# Begin Source File + +SOURCE=.\RadiantDoc.h +# End Source File +# Begin Source File + +SOURCE=.\RadiantView.h +# End Source File +# Begin Source File + +SOURCE=.\Resource.h +# End Source File +# Begin Source File + +SOURCE=.\RotateDlg.h +# End Source File +# Begin Source File + +SOURCE=.\ScaleDialog.h +# End Source File +# Begin Source File + +SOURCE=.\ScriptDlg.h +# End Source File +# Begin Source File + +SOURCE=.\SELECT.H +# End Source File +# Begin Source File + +SOURCE=.\ShaderInfo.h +# End Source File +# Begin Source File + +SOURCE=.\sourcesafe.h +# End Source File +# Begin Source File + +SOURCE=.\SourceSafeSettings.h +# End Source File +# Begin Source File + +SOURCE=.\ssauterr.h +# End Source File +# Begin Source File + +SOURCE=.\ssauto.h +# End Source File +# Begin Source File + +SOURCE=.\StdAfx.h +# End Source File +# Begin Source File + +SOURCE=.\SurfaceDlg.h +# End Source File +# Begin Source File + +SOURCE=.\TexEdit.h +# End Source File +# Begin Source File + +SOURCE=.\TextureBar.h +# End Source File +# Begin Source File + +SOURCE=.\TextureLayout.h +# End Source File +# Begin Source File + +SOURCE=.\Textures.h +# End Source File +# Begin Source File + +SOURCE=.\TexWnd.h +# End Source File +# Begin Source File + +SOURCE=.\XY.H +# End Source File +# Begin Source File + +SOURCE=.\XYWnd.h +# End Source File +# Begin Source File + +SOURCE=.\Z.H +# End Source File +# Begin Source File + +SOURCE=.\zclip.h +# End Source File +# Begin Source File + +SOURCE=.\ZWnd.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\res\bevel.bmp +# End Source File +# Begin Source File + +SOURCE=.\res\bitmap2.bmp +# End Source File +# Begin Source File + +SOURCE=.\res\bmp00001.bmp +# End Source File +# Begin Source File + +SOURCE=.\res\bmp00002.bmp +# End Source File +# Begin Source File + +SOURCE=.\res\endcap.bmp +# End Source File +# Begin Source File + +SOURCE=.\res\ibevel.bmp +# End Source File +# Begin Source File + +SOURCE=.\res\iendcap.bmp +# End Source File +# Begin Source File + +SOURCE=.\res\logo_sm3dfx.bmp +# End Source File +# Begin Source File + +SOURCE=.\res\q.bmp +# End Source File +# Begin Source File + +SOURCE=.\res\Radiant.ico +# End Source File +# Begin Source File + +SOURCE=.\Radiant.rc +# End Source File +# Begin Source File + +SOURCE=.\res\Radiant.rc2 +# End Source File +# Begin Source File + +SOURCE=.\res\RadiantDoc.ico +# End Source File +# Begin Source File + +SOURCE=.\res\Toolbar.bmp +# End Source File +# Begin Source File + +SOURCE=.\res\toolbar1.bmp +# End Source File +# Begin Source File + +SOURCE=.\res\toolbar2.bmp +# End Source File +# Begin Source File + +SOURCE=.\res\viewdefa.bmp +# End Source File +# Begin Source File + +SOURCE=.\res\viewoppo.bmp +# End Source File +# End Group +# Begin Source File + +SOURCE=.\ReadMe.txt +# End Source File +# End Target +# End Project diff --git a/utils/Radiant/Radiant.dsw b/utils/Radiant/Radiant.dsw new file mode 100644 index 0000000..4cbf8ce --- /dev/null +++ b/utils/Radiant/Radiant.dsw @@ -0,0 +1,56 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "Radiant"=.\Radiant.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/Utils/Radiant", GKOAAAAA + . + end source code control +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name pak + End Project Dependency +}}} + +############################################################################### + +Project: "pak"=..\Libs\pak\pak.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/Utils/Libs/pak", UKNAAAAA + ..\libs\pak + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ + begin source code control + "$/Utils/Radiant", GKOAAAAA + . + end source code control +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/CODE-mp/cgame/cg_playeranimate.c b/utils/Radiant/SoundNameDlg.cpp similarity index 100% rename from CODE-mp/cgame/cg_playeranimate.c rename to utils/Radiant/SoundNameDlg.cpp diff --git a/utils/Radiant/autocaulk.cpp b/utils/Radiant/autocaulk.cpp new file mode 100644 index 0000000..16cdf6a --- /dev/null +++ b/utils/Radiant/autocaulk.cpp @@ -0,0 +1,323 @@ +// Filename:- autocaulk.cpp +// +#include "stdafx.h" +#include "qe3.h" +#include "oddbits.h" +// +#include "autocaulk.h" + + +#pragma warning( disable : 4786) +#include +using namespace std; +#pragma warning( disable : 4786) + + +// for some tedious reason, I can't include "brush.h" here, so... +// +extern void ClearBounds (vec3_t mins, vec3_t maxs); +extern void AddPointToBounds (const vec3_t v, vec3_t mins, vec3_t maxs); + +static void FloorBounds(vec3_t mins, vec3_t maxs) +{ + for (int i=0 ; i<3 ; i++) + { + mins[i] = floor(mins[i] + 0.5); + maxs[i] = floor(maxs[i] + 0.5); + } +} + +/* +typedef struct +{ + vec3_t v3Mins; + vec3_t v3Maxs; + vec3_t v3TL,v3TR,v3BL,v3BR; + vec3_t v3NormalisedRotationVector; + vec3_t v3NormalisedElevationVector; + +} SquaredFace_t; + +// make an artificial square face that encompasses all the points in the winding... +// +static void WindingToSquaredFace( SquaredFace_t *pSquareFaceDest, winding_t *pWinding) +{ +// memset(pSquareFaceDest,0,sizeof(*pSquareFaceDest); + + ClearBounds(pSquareFaceDest->v3Mins, pSquareFaceDest->v3Maxs); + + for (int i=0; inumpoints ; i++) + { + AddPointToBounds( pWinding->points[i], pSquareFaceDest->v3Mins, pSquareFaceDest->v3Maxs ); + } + + VectorCopy(pSquareFaceDest->v3Mins, pSquareFaceDest->v3BL); + VectorCopy(pSquareFaceDest->v3Maxs, pSquareFaceDest->v3TR); + + VectorCopy(pSquareFaceDest->v3Mins, pSquareFaceDest->v3TL); + pSquareFaceDest->v3TL[2] = pSquareFaceDest->v3Maxs[2]; + VectorCopy(pSquareFaceDest->v3Maxs, pSquareFaceDest->v3BR); + pSquareFaceDest->v3BR[2] = pSquareFaceDest->v3Mins[2]; + + VectorSubtract( pSquareFaceDest->v3TR, pSquareFaceDest->v3TL, pSquareFaceDest->v3NormalisedRotationVector); + VectorNormalize(pSquareFaceDest->v3NormalisedRotationVector); + + VectorSubtract( pSquareFaceDest->v3TL, pSquareFaceDest->v3BL, pSquareFaceDest->v3NormalisedElevationVector); + VectorNormalize(pSquareFaceDest->v3NormalisedElevationVector); +} +*/ + +static LPCSTR vtos(vec3_t v3) +{ + return va("%.3ff,%.3f,%.3f",v3[0],v3[1],v3[2]); +} +list < pair < face_t*, brush_t*> > FacesToCaulk; +void Select_AutoCaulk(bool bMakeDetail) +{ + Sys_Printf ("Caulking...\n"); + + FacesToCaulk.clear(); + + int iSystemBrushesSkipped = 0; + + brush_t *next; + for (brush_t *pSelectedBrush = selected_brushes.next ; pSelectedBrush != &selected_brushes ; pSelectedBrush = next) + { + next = pSelectedBrush->next; + + if (pSelectedBrush->owner->eclass->fixedsize) + continue; // apparently this means it's a model, so skip it... + + // new check, we can't caulk a brush that has any "system/" faces... + // + bool bSystemFacePresent = false; + for (face_t *pSelectedFace = pSelectedBrush->brush_faces; pSelectedFace; pSelectedFace = pSelectedFace->next) + { + if (!strnicmp(pSelectedFace->d_texture->name,"system/",7)) + { + bSystemFacePresent = true; + break; + } + } + if (bSystemFacePresent) + { + iSystemBrushesSkipped++; + continue; // verboten to caulk this. + } + + for (int iBrushListToScan = 0; iBrushListToScan<2; iBrushListToScan++) + { + brush_t *snext; + for (brush_t *pScannedBrush = (iBrushListToScan?active_brushes.next:selected_brushes.next); pScannedBrush != (iBrushListToScan?&active_brushes:&selected_brushes) ; pScannedBrush = snext) + { + snext = pScannedBrush->next; + + if ( pScannedBrush == pSelectedBrush) + continue; + + if (pScannedBrush->owner->eclass->fixedsize || pScannedBrush->patchBrush || pScannedBrush->hiddenBrush) + continue; + + if (FilterBrush(pScannedBrush)) + continue; + + //face_t *pFace = pScannedBrush->brush_faces; + if (pScannedBrush->brush_faces->d_texture->bFromShader && (pScannedBrush->brush_faces->d_texture->nShaderFlags & QER_NOCARVE)) + continue; + + // basic-reject first to see if brushes can even possibly touch (coplanar counts as touching) + // + for (int i=0 ; i<3 ; i++) + { + if (pSelectedBrush->mins[i] > pScannedBrush->maxs[i] || + pSelectedBrush->maxs[i] < pScannedBrush->mins[i]) + { + break; + } + } + if (i != 3) + continue; // can't be touching + + // ok, now for the clever stuff, we need to detect only those faces that are both coplanar and smaller + // or equal to the face they're coplanar with... + // + for (pSelectedFace = pSelectedBrush->brush_faces; pSelectedFace; pSelectedFace = pSelectedFace->next) + { + winding_t *pSelectedWinding = pSelectedFace->face_winding; + + if (!pSelectedWinding) + continue; // freed face, probably won't happen here, but who knows with this program? + + // SquaredFace_t SelectedSquaredFace; + // WindingToSquaredFace( &SelectedSquaredFace, pSelectedWinding); + + for (face_t *pScannedFace = pScannedBrush->brush_faces; pScannedFace; pScannedFace = pScannedFace->next) + { + // don't even try caulking against a system face, because these are often transparent and will leave holes + // + if (!strnicmp(pScannedFace->d_texture->name,"system/",7)) + continue; + + // and don't try caulking against something inherently transparent... + // + if (pScannedFace->d_texture->nShaderFlags & QER_TRANS) + continue; + + winding_t *pScannedWinding = pScannedFace->face_winding; + + if (!pScannedWinding) + continue; // freed face, probably won't happen here, but who knows with this program? + + // SquaredFace_t ScannedSquaredFace; + // WindingToSquaredFace( &ScannedSquaredFace, pScannedWinding); + + /* if (VectorCompare(ScannedSquaredFace.v3NormalisedRotationVector, SelectedSquaredFace.v3NormalisedRotationVector) + && + VectorCompare(ScannedSquaredFace.v3NormalisedElevationVector, SelectedSquaredFace.v3NormalisedElevationVector) + ) + */ + { + // brush faces are in parallel planes to each other, so check that their normals + // are opposite, by adding them together and testing for zero... + // (if normals are opposite, then faces can be against/touching each other?) + // + vec3_t v3ZeroTest; + static vec3_t v3Zero={0,0,0}; + VectorAdd(pSelectedFace->plane.normal,pScannedFace->plane.normal,v3ZeroTest); + if (VectorCompare(v3ZeroTest,v3Zero)) + { + // planes are facing each other... + // + // coplanar? (this is some maths of Gil's, which I don't even pretend to understand) + // + float fTotalDist = 0; + for (int _i=0; _i<3; _i++) + { + fTotalDist += fabs( DotProduct(pSelectedFace->plane.normal,pSelectedWinding->points[0]) + - + DotProduct(pSelectedFace->plane.normal,pScannedWinding->points[i]) + ); + } + //OutputDebugString(va("Dist = %g\n",fTotalDist)); + + if (fTotalDist > 0.01) + continue; + + // every point in the selected face must be within (or equal to) the bounds of the + // scanned face... + // + // work out the bounds first... + // + vec3_t v3ScannedBoundsMins, v3ScannedBoundsMaxs; + ClearBounds (v3ScannedBoundsMins, v3ScannedBoundsMaxs); + for (int iPoint=0; iPointnumpoints; iPoint++) + { + AddPointToBounds( pScannedWinding->points[iPoint], v3ScannedBoundsMins, v3ScannedBoundsMaxs); + } + // floor 'em... (or .001 differences mess things up... + // + FloorBounds(v3ScannedBoundsMins, v3ScannedBoundsMaxs); + + + // now check points from selected face... + // + bool bWithin = true; + for (iPoint=0; iPoint < pSelectedWinding->numpoints; iPoint++) + { + for (int iXYZ=0; iXYZ<3; iXYZ++) + { + float f = floor(pSelectedWinding->points[iPoint][iXYZ] + 0.5); + if (! + ( + f >= v3ScannedBoundsMins[iXYZ] + && + f <= v3ScannedBoundsMaxs[iXYZ] + ) + ) + { + bWithin = false; + } + } + } + + if (bWithin) + { + FacesToCaulk.push_back( pair(pSelectedFace,pSelectedBrush)); + + /* + OutputDebugString("Pair:\n"); + OutputDebugString(vtos(pSelectedFace->plane.normal)); + OutputDebugString("\n"); + OutputDebugString(vtos(pScannedFace->plane.normal)); + OutputDebugString("\n"); + + int z=1; + */ + } + } + } + } + } + } + } + } + + + // apply caulk... + // + int iFacesCaulked = 0; + if (FacesToCaulk.size()) + { + LPCSTR psCaulkName = "system/caulk"; + qtexture_t *pCaulk = Texture_ForName(psCaulkName); + + if (pCaulk) + { + // + // and call some other junk that Radiant wants so so we can use it later... + // + texdef_t tex; + memset (&tex, 0, sizeof(tex)); + tex.scale[0] = 1; + tex.scale[1] = 1; + tex.flags = pCaulk->flags; + tex.value = pCaulk->value; + tex.contents = pCaulk->contents; + strcpy (tex.name, pCaulk->name); + + //Texture_SetTexture (&tex); + + for (list< pair < face_t*, brush_t*> >::iterator it = FacesToCaulk.begin(); it!= FacesToCaulk.end(); ++it) + { + face_t *pFace = (*it).first; + brush_t*pBrush= (*it).second; + + memcpy(&pFace->texdef,&tex,sizeof(tex)); + + Face_FitTexture(pFace, 1, 1); // this doesn't work here for some reason... duh. + Brush_Build(pBrush); + + iFacesCaulked++; + } + } + else + { + Sys_Printf(" Unable to locate caulk texture at: \"%s\"!\n",psCaulkName); + } + } + + Sys_Printf("( %d faces caulked )\n",iFacesCaulked); + + if (iSystemBrushesSkipped) + { + Sys_Printf("( %d system-faced brushes skipped )\n",iSystemBrushesSkipped); + } + + if (bMakeDetail) + { + Select_MakeDetail( false ); + } + + Sys_UpdateWindows (W_ALL); +} + diff --git a/utils/Radiant/autocaulk.h b/utils/Radiant/autocaulk.h new file mode 100644 index 0000000..e84cf24 --- /dev/null +++ b/utils/Radiant/autocaulk.h @@ -0,0 +1,14 @@ +// Filename:- autocaulk.h +// + +#ifndef AUTOCAULK_H +#define AUTOCAULK_H + + +void Select_AutoCaulk(bool bMakeDetail); + + +#endif // AUTOCAULK_H + +///////////////// eof ////////////// + diff --git a/utils/Radiant/bmp.cpp b/utils/Radiant/bmp.cpp new file mode 100644 index 0000000..fa48d1c --- /dev/null +++ b/utils/Radiant/bmp.cpp @@ -0,0 +1,396 @@ + +#include "stdafx.h" +#include +#include +#include + +#include "bmp.h" + + +void Error(char *fmt, ...); + + +static int GetColorCount(int nbits) +{ + int ncolors = 0; + + if (nbits < 24) + { + ncolors = 1 << nbits; + } + + return(ncolors); +} + + +static void BMPLineNone(FILE *f, char *sline, int pixbytes, int width) +{ + int nbytes, i, k, j; + + switch (pixbytes) + { + case 1 : + nbytes = (width + 3) / 4; + nbytes *= 4; + + fread(sline, width, 1, f); + nbytes -= width; + + while (nbytes-- > 0) fgetc(f); + return; + + case 3 : + nbytes = ((width * 3) + 3) / 4; + nbytes *= 4; + + fread(sline, width, 3, f); + nbytes -= width * 3; + + while (nbytes-- > 0) fgetc(f); + + // reorder bgr to rgb + for (i = 0, j = 0; i < width; i++, j += 3) + { + k = sline[j]; + sline[j] = sline[j+2]; + sline[j+2] = k; + } + + return; + } + + Error("BMPLineNone failed."); +} + + +static void BMPLineRLE8(FILE *f, char *sline, int pixbytes, int width) +{ + Error("RLE8 not yet supported."); +} + + +static void BMPLineRLE4(FILE *f, char *sline, int pixbytes, int width) +{ + Error("RLE4 not yet supported."); +} + + +static void BMPLine(FILE *f, char *scanline, int pixbytes, int width, int rle) +{ + switch (rle) + { + case xBI_NONE : BMPLineNone(f, scanline, pixbytes, width); return; + case xBI_RLE8 : BMPLineRLE8(f, scanline, pixbytes, width); return; + case xBI_RLE4 : BMPLineRLE4(f, scanline, pixbytes, width); return; + } + + Error("Unknown compression type."); +} + + + +static void PrintHeader(binfo_t *b) +{ + printf("biSize : %ld\n", b->biSize); + printf("biWidth : %ld\n", b->biWidth); + printf("biHeight : %ld\n", b->biHeight); + printf("biPlanes : %d\n", b->biPlanes); + printf("biBitCount : %d\n", b->biBitCount); + printf("biCompression : %ld\n", b->biCompression); + printf("biSizeImage : %ld\n", b->biSizeImage); + printf("biXPelsPerMeter: %ld\n", b->biXPelsPerMeter); + printf("biYPelsPerMeter: %ld\n", b->biYPelsPerMeter); + printf("biClrUsed : %ld\n", b->biClrUsed); + printf("biClrImportant : %ld\n", b->biClrImportant); +} + + +void LoadBMP(char *filename, bitmap_t *bit) +{ + FILE *f; + bmphd_t bhd; + binfo_t info; + int pxlsize = 1; + int rowbytes, i, pixbytes; + char *scanline; + + // open file + if ((f = fopen(filename, "rb")) == NULL) + { + Error("Unable to open %s.", filename); + } + + // read in bitmap header + if (fread(&bhd, sizeof(bhd), 1, f) != 1) + { + fclose(f); + Error("Unable to read in bitmap header."); + } + + // make sure we have a valid bitmap file + if (bhd.bfType != BMP_SIGNATURE_WORD) + { + fclose(f); + Error("Invalid BMP file: %s", filename); + } + + // load in info header + if (fread(&info, sizeof(info), 1, f) != 1) + { + fclose(f); + Error("Unable to read bitmap info header."); + } + + // make sure this is an info type of bitmap + if (info.biSize != sizeof(binfo_t)) + { + fclose(f); + Error("We only support the info bitmap type."); + } + + // PrintHeader(&info); + + bit->bpp = info.biBitCount; + bit->width = info.biWidth; + bit->height = info.biHeight; + bit->data = NULL; + bit->palette = NULL; + + //currently we only read in 8 and 24 bit bmp files + if (info.biBitCount == 8) pixbytes = 1; + else if (info.biBitCount == 24) pixbytes = 3; + else + { + Error("BPP %d not supported.", info.biBitCount); + } + + // if this is an eight bit image load palette + if (pixbytes == 1) + { + drgb_t q; + + bit->palette = reinterpret_cast(malloc(sizeof(rgb_t) * 256)); + + for (i = 0; i < 256; i++) + { + if (fread(&q, sizeof(drgb_t), 1, f) != 1) + { + fclose(f); free(bit->palette); + Error("Unable to read palette."); + } + + bit->palette[i].r = q.red; + bit->palette[i].g = q.green; + bit->palette[i].b = q.blue; + } + } + + // position to start of bitmap + fseek(f, bhd.bfOffBits, SEEK_SET); + + // create scanline to read data into + rowbytes = ((info.biWidth * pixbytes) + 3) / 4; + rowbytes *= 4; + + scanline = reinterpret_cast(malloc(rowbytes)); + + // alloc space for new bitmap + bit->data = reinterpret_cast(malloc(info.biWidth * pixbytes * info.biHeight)); + + // read in image + for (i = 0; i < info.biHeight; i++) + { + BMPLine(f, scanline, pixbytes, info.biWidth, info.biCompression); + + // store line + memcpy(&bit->data[info.biWidth * pixbytes * (info.biHeight - i - 1)], scanline, info.biWidth * pixbytes); + } + + free(scanline); + fclose(f); +} + + + +static void BMPEncodeLine(FILE *f, unsigned char *data, int npxls, int pixbytes) +{ + int nbytes, i, j, k; + + switch (pixbytes) + { + case 1 : + nbytes = (npxls + 3) / 4; + nbytes *= 4; + + fwrite(data, npxls, 1, f); + nbytes -= npxls; + + while (nbytes-- > 0) fputc(0, f); + return; + + case 3 : + // reorder rgb to bgr + for (i = 0, j = 0; i < npxls; i++, j+= 3) + { + k = data[j]; + data[j] = data[j + 2]; + data[j + 2] = k; + } + + nbytes = ((npxls * 3) + 3) / 4; + nbytes *= 4; + + fwrite(data, npxls, 3, f); + nbytes -= npxls * 3; + + while (nbytes-- > 0) fputc(0, f); + return; + } + + Error("BMPEncodeLine Failed."); +} + + + +void WriteBMP(char *filename, bitmap_t *bit) +{ + FILE *f; + bmphd_t header; + binfo_t info; + drgb_t q; // palette that gets written + long bmofs; + int w, h, i; + int pixbytes; + + if (bit->bpp == 8) pixbytes = 1; + else if (bit->bpp == 24) pixbytes = 3; + + else + { + Error("BPP %d not supported.", bit->bpp); + } + + + if ((f = fopen(filename, "wb")) == NULL) + { + Error("Unable to open %s.", filename); + } + + // write out an empty header as a place holder + if (fwrite(&header, sizeof(header), 1, f) != 1) + { + Error("Unable to fwrite."); + } + + // init and write info header + info.biSize = sizeof(binfo_t); + info.biWidth = bit->width; + info.biHeight = bit->height; + info.biPlanes = 1; + info.biBitCount = bit->bpp; + info.biCompression = xBI_NONE; + info.biSizeImage = bit->width * bit->height; + info.biXPelsPerMeter = 0; + info.biYPelsPerMeter = 0; + info.biClrUsed = 256; + info.biClrImportant = 256; + + if (fwrite(&info, sizeof(binfo_t), 1, f) != 1) + { + Error("fwrite failed."); + } + + // write out palette if we need to + if (bit->bpp == 8) + { + for (i = 0; i < 256; i++) + { + q.red = bit->palette[i].r; + q.green = bit->palette[i].g; + q.blue = bit->palette[i].b; + + fwrite(&q, sizeof(q), 1, f); + } + } + + // save offset to start of bitmap + bmofs = ftell(f); + + // output bitmap + w = bit->width; + h = bit->height; + + for (i = h - 1; i >= 0; i--) + { + BMPEncodeLine(f, &bit->data[w * pixbytes * i], w, pixbytes); + } + + // update and rewrite file header + header.bfType = BMP_SIGNATURE_WORD; + header.bfSize = ftell(f); + header.bfOffBits = bmofs; + + fseek(f, 0L, SEEK_SET); + fwrite(&header, sizeof(header), 1, f); + + fclose(f); +} + + +void NewBMP(int width, int height, int bpp, bitmap_t *bit) +{ + int pixbytes; + + if (bpp == 8) pixbytes = 1; + else if (bpp == 24) pixbytes = 3; + + else + { + Error("NewBMP: 8 or 24 bit only."); + } + + bit->bpp = bpp; + bit->width = width; + bit->height = height; + + bit->data = reinterpret_cast(malloc(width * height * pixbytes)); + + if (bit->data == NULL) + { + Error("NewBMP: malloc failed."); + } + + // see if we need to create a palette + if (pixbytes == 1) + { + bit->palette = (rgb_t *) malloc(768); + + if (bit->palette == NULL) + { + Error("NewBMP: unable to malloc palette."); + } + } + else + { + bit->palette = NULL; + } +} + + + +void FreeBMP(bitmap_t *bitmap) +{ + if (bitmap->palette) + { + free(bitmap->palette); + bitmap->palette = NULL; + } + + if (bitmap->data) + { + free(bitmap->data); + bitmap->data = NULL; + } +} + + diff --git a/utils/Radiant/bmp.h b/utils/Radiant/bmp.h new file mode 100644 index 0000000..21daabf --- /dev/null +++ b/utils/Radiant/bmp.h @@ -0,0 +1,80 @@ +#ifndef _BMP_H +#define _BMP_H + +#define xBI_NONE 0 +#define xBI_RGB 0 +#define xBI_RLE4 2 +#define xBI_RLE8 1 + +#define BMP_SIGNATURE_WORD 0x4d42 + +#pragma pack(1) + + + +typedef struct { + unsigned short bfType; // signature - 'BM' + unsigned long bfSize; // file size in bytes + unsigned short bfReserved1; // 0 + unsigned short bfReserved2; // 0 + unsigned long bfOffBits; // offset to bitmap +} bmphd_t; + + + +typedef struct { + unsigned long biSize; // size of this struct + long biWidth; // bmap width in pixels + long biHeight; // bmap height in pixels + unsigned short biPlanes; // num planes - always 1 + unsigned short biBitCount; // bits perpixel + unsigned long biCompression; // compression flag + unsigned long biSizeImage; // image size in bytes + long biXPelsPerMeter; // horz resolution + long biYPelsPerMeter; // vert resolution + unsigned long biClrUsed; // 0 -> color table size + unsigned long biClrImportant; // important color count +} binfo_t; + + +typedef struct { + unsigned char blue; + unsigned char green; + unsigned char red; + unsigned char reserved; +} drgb_t; + + +// quake expects its palette to be bgr +// this is totally backwards but what can you do +typedef struct { + unsigned char r; + unsigned char g; + unsigned char b; +} rgb_t; + + +typedef struct { + unsigned char b; + unsigned char g; + unsigned char r; +} bgr_t; + + +typedef struct { + int bpp; // bits per pixel + int width; + int height; + unsigned char *data; + rgb_t *palette; +} bitmap_t; + + +void LoadBMP(char *filename, bitmap_t *bit); +void FreeBMP(bitmap_t *bitmap); +void WriteBMP(char *filename, bitmap_t *bit); +void NewBMP(int width, int height, int bpp, bitmap_t *bit); + + + +#endif diff --git a/utils/Radiant/brush.cpp b/utils/Radiant/brush.cpp new file mode 100644 index 0000000..66869ab --- /dev/null +++ b/utils/Radiant/brush.cpp @@ -0,0 +1,3530 @@ + + +#include "stdafx.h" +#include +#include "qe3.h" +#include "oddbits.h" +#include "groupnames.h" + +#define MAX_POINTS_ON_WINDING 64 + +face_t *Face_Alloc( void ); + +winding_t *NewWinding (int points); +void FreeWinding (winding_t *w); +winding_t *Winding_Clone( winding_t *w ); +winding_t *ClipWinding (winding_t *in, plane_t *split, qboolean keepon); + +qboolean qbShowFaces = false; + +void PrintWinding (winding_t *w) +{ + int i; + + printf ("-------------\n"); + for (i=0 ; inumpoints ; i++) + printf ("(%5.2f, %5.2f, %5.2f)\n", w->points[i][0] + , w->points[i][1], w->points[i][2]); +} + +void PrintPlane (plane_t *p) +{ + printf ("(%5.2f, %5.2f, %5.2f) : %5.2f\n", p->normal[0], p->normal[1], + p->normal[2], p->dist); +} + +void PrintVector (vec3_t v) +{ + printf ("(%5.2f, %5.2f, %5.2f)\n", v[0], v[1], v[2]); +} + + +face_t *Face_Clone (face_t *f) +{ + face_t *n; + + n = Face_Alloc(); + n->texdef = f->texdef; + memcpy (n->planepts, f->planepts, sizeof(n->planepts)); + + // all other fields are derived, and will be set by Brush_Build + return n; +} + +//============================================================================ + + + + + +/* +================== +NewWinding +================== +*/ +winding_t *NewWinding (int points) +{ + winding_t *w; + int size; + + if (points > MAX_POINTS_ON_WINDING) + Error ("NewWinding: %i points", points); + + size = (int)((winding_t *)0)->points[points]; + w = (winding_t*) malloc (size); + memset (w, 0, size); + w->maxpoints = points; + + return w; +} + + +void FreeWinding (winding_t *w) +{ + free (w); +} + + +/* +================== +Winding_Clone +================== +*/ +winding_t *Winding_Clone(winding_t *w) +{ + int size; + winding_t *c; + + size = (int)((winding_t *)0)->points[w->numpoints]; + c = (winding_t*)qmalloc (size); + memcpy (c, w, size); + return c; +} + + +/* +================== +ClipWinding + +Clips the winding to the plane, returning the new winding on the positive side +Frees the input winding. +If keepon is true, an exactly on-plane winding will be saved, otherwise +it will be clipped away. +================== +*/ +winding_t *ClipWinding (winding_t *in, plane_t *split, qboolean keepon) +{ + vec_t dists[MAX_POINTS_ON_WINDING]; + int sides[MAX_POINTS_ON_WINDING]; + int counts[3]; + vec_t dot; + int i, j; + vec_t *p1, *p2; + vec3_t mid; + winding_t *neww; + int maxpts; + + counts[0] = counts[1] = counts[2] = 0; + +// determine sides for each point + for (i=0 ; inumpoints ; i++) + { + dot = DotProduct (in->points[i], split->normal); + dot -= split->dist; + dists[i] = dot; + if (dot > ON_EPSILON) + sides[i] = SIDE_FRONT; + else if (dot < -ON_EPSILON) + sides[i] = SIDE_BACK; + else + { + sides[i] = SIDE_ON; + } + counts[sides[i]]++; + } + sides[i] = sides[0]; + dists[i] = dists[0]; + + if (keepon && !counts[0] && !counts[1]) + return in; + + if (!counts[0]) + { + FreeWinding (in); + return NULL; + } + if (!counts[1]) + return in; + + maxpts = in->numpoints+4; // can't use counts[0]+2 because + // of fp grouping errors + neww = NewWinding (maxpts); + + for (i=0 ; inumpoints ; i++) + { + p1 = in->points[i]; + + if (sides[i] == SIDE_ON) + { + VectorCopy (p1, neww->points[neww->numpoints]); + neww->numpoints++; + continue; + } + + if (sides[i] == SIDE_FRONT) + { + VectorCopy (p1, neww->points[neww->numpoints]); + neww->numpoints++; + } + + if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) + continue; + + // generate a split point + p2 = in->points[(i+1)%in->numpoints]; + + dot = dists[i] / (dists[i]-dists[i+1]); + for (j=0 ; j<3 ; j++) + { // avoid round off error when possible + if (split->normal[j] == 1) + mid[j] = split->dist; + else if (split->normal[j] == -1) + mid[j] = -split->dist; + else + mid[j] = p1[j] + dot*(p2[j]-p1[j]); + } + + VectorCopy (mid, neww->points[neww->numpoints]); + neww->numpoints++; + } + + if (neww->numpoints > maxpts) + Error ("ClipWinding: points exceeded estimate"); + +// free the original winding + FreeWinding (in); + + return neww; +} + + + +/* +============================================================================= + + TEXTURE COORDINATES + +============================================================================= +*/ + + +/* +================== +textureAxisFromPlane +================== +*/ +vec3_t baseaxis[18] = +{ +{0,0,1}, {1,0,0}, {0,-1,0}, // floor +{0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling +{1,0,0}, {0,1,0}, {0,0,-1}, // west wall +{-1,0,0}, {0,1,0}, {0,0,-1}, // east wall +{0,1,0}, {1,0,0}, {0,0,-1}, // south wall +{0,-1,0}, {1,0,0}, {0,0,-1} // north wall +}; + +void TextureAxisFromPlane(plane_t *pln, vec3_t xv, vec3_t yv) +{ + int bestaxis; + float dot,best; + int i; + + best = 0; + bestaxis = 0; + + for (i=0 ; i<6 ; i++) + { + dot = DotProduct (pln->normal, baseaxis[i*3]); + if (dot > (best + 0.0001)) + { + best = dot; + bestaxis = i; + } + } + + VectorCopy (baseaxis[bestaxis*3+1], xv); + VectorCopy (baseaxis[bestaxis*3+2], yv); +} + + + +float lightaxis[3] = {0.6, 0.8, 1.0}; +/* +================ +SetShadeForPlane + +Light different planes differently to +improve recognition +================ +*/ +float SetShadeForPlane (plane_t *p) +{ + int i; + float f; + + // axial plane + for (i=0 ; i<3 ; i++) + if (fabs(p->normal[i]) > 0.9) + { + f = lightaxis[i]; + return f; + } + + // between two axial planes + for (i=0 ; i<3 ; i++) + if (fabs(p->normal[i]) < 0.1) + { + f = (lightaxis[(i+1)%3] + lightaxis[(i+2)%3])/2; + return f; + } + + // other + f= (lightaxis[0] + lightaxis[1] + lightaxis[2]) / 3; + return f; +} + +vec3_t vecs[2]; +float shift[2]; + +/* +================ +SetFaceColor +================ +*/ +void SetFaceColor (brush_t *b, face_t *f, float fCurveColor) +{ + float shade; + qtexture_t *q; + + q = f->d_texture; + + // set shading for face + shade = SetShadeForPlane (&f->plane); + if (g_pParentWnd->GetCamera()->Camera().draw_mode == cd_texture && !b->owner->eclass->fixedsize) + { + //if (b->curveBrush) + // shade = fCurveColor; + f->d_color[0] = + f->d_color[1] = + f->d_color[2] = shade; + } + else + { + f->d_color[0] = shade*q->color[0]; + f->d_color[1] = shade*q->color[1]; + f->d_color[2] = shade*q->color[2]; + } +} + +/* +================ +FaceTextureVectors +================ +*/ +void FaceTextureVectors (face_t *f, float STfromXYZ[2][4]) +{ + vec3_t pvecs[2]; + int sv, tv; + float ang, sinv, cosv; + float ns, nt; + int i,j; + qtexture_t *q; + texdef_t *td; + + td = &f->texdef; + q = f->d_texture; + + memset (STfromXYZ, 0, 8*sizeof(float)); + + if (!td->scale[0]) + td->scale[0] = (g_PrefsDlg.m_bHiColorTextures) ? fTEXTURE_SCALE : 1; + if (!td->scale[1]) + td->scale[1] = (g_PrefsDlg.m_bHiColorTextures) ? fTEXTURE_SCALE : 1; + + // get natural texture axis + TextureAxisFromPlane(&f->plane, pvecs[0], pvecs[1]); + + // rotate axis + if (td->rotate == 0) + { sinv = 0 ; cosv = 1; } + else if (td->rotate == 90) + { sinv = 1 ; cosv = 0; } + else if (td->rotate == 180) + { sinv = 0 ; cosv = -1; } + else if (td->rotate == 270) + { sinv = -1 ; cosv = 0; } + else + { + ang = td->rotate / 180 * Q_PI; + sinv = sin(ang); + cosv = cos(ang); + } + + if (pvecs[0][0]) + sv = 0; + else if (pvecs[0][1]) + sv = 1; + else + sv = 2; + + if (pvecs[1][0]) + tv = 0; + else if (pvecs[1][1]) + tv = 1; + else + tv = 2; + + for (i=0 ; i<2 ; i++) { + ns = cosv * pvecs[i][sv] - sinv * pvecs[i][tv]; + nt = sinv * pvecs[i][sv] + cosv * pvecs[i][tv]; + STfromXYZ[i][sv] = ns; + STfromXYZ[i][tv] = nt; + } + + // scale + for (i=0 ; i<2 ; i++) + for (j=0 ; j<3 ; j++) + STfromXYZ[i][j] = STfromXYZ[i][j] / td->scale[i]; + + // shift + STfromXYZ[0][3] = td->shift[0]; + STfromXYZ[1][3] = td->shift[1]; + + for (j=0 ; j<4 ; j++) { + STfromXYZ[0][j] /= q->width; + STfromXYZ[1][j] /= q->height; + } +} + + + +void EmitTextureCoordinates ( float *xyzst, qtexture_t *q, face_t *f) +{ + float STfromXYZ[2][4]; + + FaceTextureVectors (f, STfromXYZ); + xyzst[3] = DotProduct (xyzst, STfromXYZ[0]) + STfromXYZ[0][3]; + xyzst[4] = DotProduct (xyzst, STfromXYZ[1]) + STfromXYZ[1][3]; + +} + + +//========================================================================== + + +/* +================= +BasePolyForPlane +================= +*/ +winding_t *BasePolyForPlane (plane_t *p) +{ + int i, x; + vec_t max, v; + vec3_t org, vright, vup; + winding_t *w; + +// find the major axis + + max = MIN_WORLD_COORD; + x = -1; + for (i=0 ; i<3; i++) + { + v = fabs(p->normal[i]); + if (v > max) + { + x = i; + max = v; + } + } + if (x==-1) + Error ("BasePolyForPlane: no axis found"); + + VectorCopy (vec3_origin, vup); + switch (x) + { + case 0: + case 1: + vup[2] = 1; + break; + case 2: + vup[0] = 1; + break; + } + + + v = DotProduct (vup, p->normal); + VectorMA (vup, -v, p->normal, vup); + VectorNormalize (vup); + + VectorScale (p->normal, p->dist, org); + + CrossProduct (vup, p->normal, vright); + + VectorScale (vup, MAX_WORLD_COORD, vup); + VectorScale (vright, MAX_WORLD_COORD, vright); + +// project a really big axis aligned box onto the plane + w = NewWinding (4); + + VectorSubtract (org, vright, w->points[0]); + VectorAdd (w->points[0], vup, w->points[0]); + + VectorAdd (org, vright, w->points[1]); + VectorAdd (w->points[1], vup, w->points[1]); + + VectorAdd (org, vright, w->points[2]); + VectorSubtract (w->points[2], vup, w->points[2]); + + VectorSubtract (org, vright, w->points[3]); + VectorSubtract (w->points[3], vup, w->points[3]); + + w->numpoints = 4; + + return w; +} + +void Brush_MakeFacePlane (face_t *f) { + int j; + vec3_t t1, t2, t3; + +// convert to a vector / dist plane + for (j=0 ; j<3 ; j++) + { + t1[j] = f->planepts[0][j] - f->planepts[1][j]; + t2[j] = f->planepts[2][j] - f->planepts[1][j]; + t3[j] = f->planepts[1][j]; + } + + CrossProduct(t1,t2, f->plane.normal); + if (VectorCompare (f->plane.normal, vec3_origin)) + printf ("WARNING: brush plane with no normal\n"); + VectorNormalize (f->plane.normal); + f->plane.dist = DotProduct (t3, f->plane.normal); +} + +void Brush_MakeFacePlanes (brush_t *b) +{ + face_t *f; + + for (f=b->brush_faces ; f ; f=f->next) + { + Brush_MakeFacePlane (f); + } +} + + +void DrawBrushEntityName (brush_t *b) +{ + char *name; + //float a, s, c; + //vec3_t mid; + //int i; + + if (!b->owner) + return; // during contruction + + if (b->owner == world_entity) + return; + + if (b != b->owner->brushes.onext) + return; // not key brush + +// MERGEME +#if 0 + if (!(g_qeglobals.d_savedinfo.exclude & EXCLUDE_ANGLES)) + { + // draw the angle pointer + a = FloatForKey (b->owner, "angle"); + if (a) + { + s = sin (a/180*Q_PI); + c = cos (a/180*Q_PI); + for (i=0 ; i<3 ; i++) + mid[i] = (b->mins[i] + b->maxs[i])*0.5; + + qglBegin (GL_LINE_STRIP); + qglVertex3fv (mid); + mid[0] += c*8; + mid[1] += s*8; + mid[2] += s*8; + qglVertex3fv (mid); + mid[0] -= c*4; + mid[1] -= s*4; + mid[2] -= s*4; + mid[0] -= s*4; + mid[1] += c*4; + mid[2] += c*4; + qglVertex3fv (mid); + mid[0] += c*4; + mid[1] += s*4; + mid[2] += s*4; + mid[0] += s*4; + mid[1] -= c*4; + mid[2] -= c*4; + qglVertex3fv (mid); + mid[0] -= c*4; + mid[1] -= s*4; + mid[2] -= s*4; + mid[0] += s*4; + mid[1] -= c*4; + mid[2] -= c*4; + qglVertex3fv (mid); + qglEnd (); + } + } +#endif + + + // slightly odd code for SHOW_GROUPNAMES I know... + // + if (g_qeglobals.d_savedinfo.show_names) + { + name = ValueForKey (b->owner, "classname"); + + if (g_qeglobals.d_savedinfo.exclude & SHOW_GROUPNAMES) + { + char *psGroupName = ValueForKey (b->owner, sKEYFIELD_GROUPNAME); + + if (strlen(psGroupName)) + { + name = va("%s ( %s )",name,psGroupName); + } + } + + qglRasterPos3f (b->mins[0]+4, b->mins[1]+4, b->mins[2]+4); + qglCallLists (strlen(name), GL_UNSIGNED_BYTE, name); + +#ifdef QUAKE3 + if (strnicmp(name,"waypoint",8)==0) + { + name = ValueForKey (b->owner, "targetname"); + if (name && strlen(name)) // just being safe.... + { + CString name2; + name2.Format("( %s )",name); + qglRasterPos3f (b->mins[0]+4, b->mins[1]+4-10, b->mins[2]+4); + qglCallLists (strlen(name2), GL_UNSIGNED_BYTE, name2); + } + } +#endif + } + else + { + // (this is deliberately not subservient to the above bool) + // + if (g_qeglobals.d_savedinfo.exclude & SHOW_GROUPNAMES) + { + name = ValueForKey (b->owner, sKEYFIELD_GROUPNAME); + + if (strlen(name)) + { + name = va("( Group: \"%s\" )",name); + + qglRasterPos3f (b->mins[0]+4, b->mins[1]+4, b->mins[2]+4); + qglCallLists (strlen(name), GL_UNSIGNED_BYTE, name); + } + } + } +} + +/* +================= +MakeFaceWinding + +returns the visible polygon on a face +================= +*/ +winding_t *MakeFaceWinding (brush_t *b, face_t *face) +{ + winding_t *w; + face_t *clip; + plane_t plane; + qboolean past; + + // get a poly that covers an effectively infinite area + w = BasePolyForPlane (&face->plane); + + // chop the poly by all of the other faces + past = false; + for (clip = b->brush_faces ; clip && w ; clip=clip->next) + { + if (clip == face) + { + past = true; + continue; + } + if (DotProduct (face->plane.normal, clip->plane.normal) > 0.999 + && fabs(face->plane.dist - clip->plane.dist) < 0.01 ) + { // identical plane, use the later one + if (past) + { + free (w); + return NULL; + } + continue; + } + + // flip the plane, because we want to keep the back side + VectorSubtract (vec3_origin,clip->plane.normal, plane.normal); + plane.dist = -clip->plane.dist; + + w = ClipWinding (w, &plane, false); + if (!w) + return w; + } + + if (w->numpoints < 3) + { + free(w); + w = NULL; + } + + if (!w) + printf ("unused plane\n"); + + return w; +} + + +void Brush_SnapPlanepts (brush_t *b) +{ + int i, j; + face_t *f; + + if (g_PrefsDlg.m_bNoClamp) + return; + + for (f=b->brush_faces ; f; f=f->next) + for (i=0 ; i<3 ; i++) + for (j=0 ; j<3 ; j++) + f->planepts[i][j] = floor (f->planepts[i][j] + 0.5); +} + +/* +** Brush_Build +** +** Builds a brush rendering data and also sets the min/max bounds +*/ +#define ZERO_EPSILON 0.001 +void Brush_Build( brush_t *b, bool bSnap, bool bMarkMap ) +{ +// int order; +// face_t *face; +// winding_t *w; + + + /* + ** build the windings and generate the bounding box + */ + Brush_BuildWindings(b, bSnap); + + Patch_BuildPoints (b); +/* + b->alphaBrush = false; + for (face_t *f=b->brush_faces ; f; f=f->next) + { + if (f->texdef.flags & SURF_ALPHA) + { + b->alphaBrush = true; + break; + } + } +*/ + + /* + ** move the points and edges if in select mode + */ + if (g_qeglobals.d_select_mode == sel_vertex || g_qeglobals.d_select_mode == sel_edge) + SetupVertexSelection (); + + if (bMarkMap) + { + Sys_MarkMapModified(); + } +} + +/* +================= +Brush_Parse + +The brush is NOT linked to any list +================= +*/ +brush_t *Brush_Parse (void) +{ + brush_t *b; + face_t *f; + int i,j; + + g_qeglobals.d_parsed_brushes++; + b = (brush_t*)qmalloc(sizeof(brush_t)); + + do + { + if (!GetToken (true)) + break; + if (!strcmp (token, "}") ) + break; + + if (strcmpi(token, "patchDef2") == 0 || strcmpi(token, "patchDef3") == 0) + { + free (b); + + // double string compare but will go away soon + b = Patch_Parse(strcmpi(token, "patchDef2") == 0); + if (b == NULL) + { + Warning ("parsing patch/brush"); + return NULL; + } + else + { + continue; + } + // handle inline patch + } + else + { + f = Face_Alloc(); + + // add the brush to the end of the chain, so + // loading and saving a map doesn't reverse the order + + f->next = NULL; + if (!b->brush_faces) + { + b->brush_faces = f; + } + else + { + face_t *scan; + for (scan=b->brush_faces ; scan->next ; scan=scan->next) + ; + scan->next = f; + } + + // read the three point plane definition + for (i=0 ; i<3 ; i++) + { + if (i != 0) + GetToken (true); + if (strcmp (token, "(") ) + { + Warning ("parsing brush"); + return NULL; + } + + for (j=0 ; j<3 ; j++) + { + GetToken (false); + f->planepts[i][j] = atof(token); + } + + GetToken (false); + if (strcmp (token, ")") ) + { + Warning ("parsing brush"); + return NULL; + } + } + } + + // read the texturedef + GetToken (false); + strcpy(f->texdef.name, token); + GetToken (false); + f->texdef.shift[0] = atoi(token); + GetToken (false); + f->texdef.shift[1] = atoi(token); + GetToken (false); + f->texdef.rotate = atoi(token); + GetToken (false); + f->texdef.scale[0] = atof(token); + GetToken (false); + f->texdef.scale[1] = atof(token); + + + // the flags and value field aren't necessarily present (but all new maps saved in this version *do* write them) + f->d_texture = Texture_ForName( f->texdef.name ); + f->texdef.flags = f->d_texture->flags; + f->texdef.value = f->d_texture->value; + f->texdef.contents = f->d_texture->contents; + + if (TokenAvailable ()) + { + GetToken (false); + f->texdef.contents = atoi(token); + GetToken (false); + f->texdef.flags = atoi(token); + GetToken (false); + f->texdef.value = atoi(token); + } + +#ifdef SOF + // optional fields here depending on flags... + // + if(f->texdef.flags & 0x400) // tall wall, read lighting values + { + if (TokenAvailable()) + { + GetToken (false); + f->texdef.lighting[0] = atof(token); // red + GetToken (false); + f->texdef.lighting[1] = atof(token); // green + GetToken (false); + f->texdef.lighting[2] = atof(token); // blue + GetToken (false); + f->texdef.lighting[3] = atof(token); // alpha + } + else + { + Error ("Brush_Parse: Expecting tall-wall lighting values, but none found!"); + } + } +#endif + + } while (1); + + + return b; +} + +/* +================= +Brush_Write +================= +*/ +void Brush_Write (brush_t *b, FILE *f) +{ + face_t *fa; + char *pname; + int i; + + if (b->patchBrush) + { + Patch_Write(b->nPatchID, f); + return; + } + fprintf (f, "{\n"); + for (fa=b->brush_faces ; fa ; fa=fa->next) + { + if (g_PrefsDlg.m_bNoClamp) + { + for (i=0 ; i<3 ; i++) + { + fprintf(f, "( "); + for (int j = 0; j < 3; j++) + { + if (fa->planepts[i][j] == static_cast(fa->planepts[i][j])) + fprintf(f, "%i ", static_cast(fa->planepts[i][j])); + else + fprintf(f, "%f ", fa->planepts[i][j]); + } + fprintf(f, ") "); + } + } + else + { + for (i=0 ; i<3 ; i++) + { + fprintf (f, "( %i %i %i ) ", (int)fa->planepts[i][0] , (int)fa->planepts[i][1], (int)fa->planepts[i][2]); + } + } + pname = fa->texdef.name; + if (pname[0] == 0) + pname = "unnamed"; + + fprintf (f, "%s %i %i %i ", +#ifdef QUAKE3 + strlwr +#endif + (pname), + (int)fa->texdef.shift[0], (int)fa->texdef.shift[1], + (int)fa->texdef.rotate); + + if (fa->texdef.scale[0] == (int)fa->texdef.scale[0]) + fprintf (f, "%i ", (int)fa->texdef.scale[0]); + else + fprintf (f, "%f ", (float)fa->texdef.scale[0]); + if (fa->texdef.scale[1] == (int)fa->texdef.scale[1]) + fprintf (f, "%i", (int)fa->texdef.scale[1]); + else + fprintf (f, "%f", (float)fa->texdef.scale[1]); + + // only output flags and value if not default + // KPRadiant (and any tga setup always needs to write them unless the build tools read the ini file +#if 1 + //if (!(g_pParentWnd->GetPlugInMgr().GetTextureInfo() && g_pParentWnd->GetPlugInMgr().GetTextureInfo()->m_bHalfLife)) + //{ + fprintf (f, " %i %i %i", fa->texdef.contents, fa->texdef.flags, fa->texdef.value); + //} +#else + bool bFlagsOut = false; + if (!fa->d_texture || fa->texdef.value != fa->d_texture->value + || fa->texdef.flags != fa->d_texture->flags + || fa->texdef.contents != fa->d_texture->contents) + { + fprintf (f, " %i %i %i", fa->texdef.contents, fa->texdef.flags, fa->texdef.value); + bFlagsOut = true; + } +#endif + +#ifdef SOF + if(fa->texdef.flags & 0x400) // tall wall, write lighting values + { + fprintf (f, " %f %f %f %f", fa->texdef.lighting[0], fa->texdef.lighting[1], fa->texdef.lighting[2], fa->texdef.lighting[3]); + } +#endif + + + fprintf (f, "\n"); + } + fprintf (f, "}\n"); + +} + + + +/* +================= +Brush_Write to a CMemFile* +================= +*/ +void Brush_Write (brush_t *b, CMemFile *pMemFile) +{ + face_t *fa; + char *pname; + int i; + + if (b->patchBrush) + { + Patch_Write(b->nPatchID, pMemFile); + return; + } + + MemFile_fprintf (pMemFile, "{\n"); + for (fa=b->brush_faces ; fa ; fa=fa->next) + { + if (g_PrefsDlg.m_bNoClamp) + { + for (i=0 ; i<3 ; i++) + { + MemFile_fprintf (pMemFile, "( "); + for (int j = 0; j < 3; j++) + { + if (fa->planepts[i][j] == static_cast(fa->planepts[i][j])) + MemFile_fprintf (pMemFile, "%i ", static_cast(fa->planepts[i][j])); + else + MemFile_fprintf (pMemFile, "%f ", fa->planepts[i][j]); + } + MemFile_fprintf (pMemFile, ") "); + } + } + else + { + for (i=0 ; i<3 ; i++) + { + MemFile_fprintf (pMemFile, "( %i %i %i ) ", (int)fa->planepts[i][0] + , (int)fa->planepts[i][1], (int)fa->planepts[i][2]); + } + } + pname = fa->texdef.name; + if (pname[0] == 0) + pname = "unnamed"; + + MemFile_fprintf (pMemFile, "%s %i %i %i ", pname, + (int)fa->texdef.shift[0], (int)fa->texdef.shift[1], + (int)fa->texdef.rotate); + + if (fa->texdef.scale[0] == (int)fa->texdef.scale[0]) + MemFile_fprintf (pMemFile, "%i ", (int)fa->texdef.scale[0]); + else + MemFile_fprintf (pMemFile, "%f ", (float)fa->texdef.scale[0]); + if (fa->texdef.scale[1] == (int)fa->texdef.scale[1]) + MemFile_fprintf (pMemFile, "%i", (int)fa->texdef.scale[1]); + else + MemFile_fprintf (pMemFile, "%f", (float)fa->texdef.scale[1]); + + // only output flags and value if not default + bool bFlagsOut = false; + if (fa->texdef.value != fa->d_texture->value + || fa->texdef.flags != fa->d_texture->flags + || fa->texdef.contents != fa->d_texture->contents) + { + MemFile_fprintf (pMemFile, " %i %i %i", fa->texdef.contents, fa->texdef.flags, fa->texdef.value); + bFlagsOut = true; + } + +#ifdef SOF + if(fa->texdef.flags & 0x400) // tall wall, write lighting values + { + MemFile_fprintf (pMemFile, " %f %f %f %f", fa->texdef.lighting[0], fa->texdef.lighting[1], fa->texdef.lighting[2], fa->texdef.lighting[3]); + } +#endif + + + MemFile_fprintf (pMemFile, "\n"); + } + MemFile_fprintf (pMemFile, "}\n"); +} + +/* +============= +Brush_Create + +Create non-textured blocks for entities +The brush is NOT linked to any list +============= +*/ +brush_t *Brush_Create (vec3_t mins, vec3_t maxs, texdef_t *texdef) +{ + int i, j; + vec3_t pts[4][2]; + face_t *f; + brush_t *b; + + for (i=0 ; i<3 ; i++) + if (maxs[i] < mins[i]) + Error ("Brush_InitSolid: backwards"); + + b = (brush_t*)qmalloc (sizeof(brush_t)); + + pts[0][0][0] = mins[0]; + pts[0][0][1] = mins[1]; + + pts[1][0][0] = mins[0]; + pts[1][0][1] = maxs[1]; + + pts[2][0][0] = maxs[0]; + pts[2][0][1] = maxs[1]; + + pts[3][0][0] = maxs[0]; + pts[3][0][1] = mins[1]; + + for (i=0 ; i<4 ; i++) + { + pts[i][0][2] = mins[2]; + pts[i][1][0] = pts[i][0][0]; + pts[i][1][1] = pts[i][0][1]; + pts[i][1][2] = maxs[2]; + } + + + for (i=0 ; i<4 ; i++) + { + f = Face_Alloc(); + f->texdef = *texdef; + f->texdef.flags &= ~SURF_KEEP; + f->texdef.contents &= ~CONTENTS_KEEP; + f->next = b->brush_faces; + b->brush_faces = f; + j = (i+1)%4; + + VectorCopy (pts[j][1], f->planepts[0]); + VectorCopy (pts[i][1], f->planepts[1]); + VectorCopy (pts[i][0], f->planepts[2]); + } + + f = Face_Alloc(); + f->texdef = *texdef; + f->texdef.flags &= ~SURF_KEEP; + f->texdef.contents &= ~CONTENTS_KEEP; + f->next = b->brush_faces; + b->brush_faces = f; + + VectorCopy (pts[0][1], f->planepts[0]); + VectorCopy (pts[1][1], f->planepts[1]); + VectorCopy (pts[2][1], f->planepts[2]); + + f = Face_Alloc(); + f->texdef = *texdef; + f->texdef.flags &= ~SURF_KEEP; + f->texdef.contents &= ~CONTENTS_KEEP; + f->next = b->brush_faces; + b->brush_faces = f; + + VectorCopy (pts[2][0], f->planepts[0]); + VectorCopy (pts[1][0], f->planepts[1]); + VectorCopy (pts[0][0], f->planepts[2]); + + return b; +} + +/* +============= +Brush_CreatePyramid + +Create non-textured pyramid for light entities +The brush is NOT linked to any list +============= +*/ +brush_t *Brush_CreatePyramid (vec3_t mins, vec3_t maxs, texdef_t *texdef) +{ + return Brush_Create(mins, maxs, texdef); + + for (int i=0 ; i<3 ; i++) + if (maxs[i] < mins[i]) + Error ("Brush_InitSolid: backwards"); + + brush_t* b = (brush_t*)qmalloc (sizeof(brush_t)); + + vec3_t corners[4]; + + float fMid = Q_rint(mins[2] + (Q_rint((maxs[2] - mins[2]) / 2))); + + corners[0][0] = mins[0]; + corners[0][1] = mins[1]; + corners[0][2] = fMid; + + corners[1][0] = mins[0]; + corners[1][1] = maxs[1]; + corners[1][2] = fMid; + + corners[2][0] = maxs[0]; + corners[2][1] = maxs[1]; + corners[2][2] = fMid; + + corners[3][0] = maxs[0]; + corners[3][1] = mins[1]; + corners[3][2] = fMid; + + vec3_t top, bottom; + + top[0] = Q_rint(mins[0] + ((maxs[0] - mins[0]) / 2)); + top[1] = Q_rint(mins[1] + ((maxs[1] - mins[1]) / 2)); + top[2] = Q_rint(maxs[2]); + + VectorCopy(top, bottom); + bottom[2] = mins[2]; + + // sides + for (i = 0; i < 4; i++) + { + face_t* f = Face_Alloc(); + f->texdef = *texdef; + f->texdef.flags &= ~SURF_KEEP; + f->texdef.contents &= ~CONTENTS_KEEP; + f->next = b->brush_faces; + b->brush_faces = f; + int j = (i+1)%4; + + VectorCopy (top, f->planepts[0]); + VectorCopy (corners[i], f->planepts[1]); + VectorCopy(corners[j], f->planepts[2]); + + f = Face_Alloc(); + f->texdef = *texdef; + f->texdef.flags &= ~SURF_KEEP; + f->texdef.contents &= ~CONTENTS_KEEP; + f->next = b->brush_faces; + b->brush_faces = f; + + VectorCopy (bottom, f->planepts[2]); + VectorCopy (corners[i], f->planepts[1]); + VectorCopy(corners[j], f->planepts[0]); + + } + + return b; +} + + + + +/* +============= +Brush_MakeSided + +Makes the current brushhave the given number of 2d sides +============= +*/ +void Brush_MakeSided (int sides) +{ + int i; + vec3_t mins, maxs; + brush_t *b; + texdef_t *texdef; + face_t *f; + vec3_t mid; + float width; + float sv, cv; + + if (sides < 3) + { + Sys_Status ("Bad sides number", 0); + return; + } + + if (!QE_SingleBrush ()) + { + Sys_Status ("Must have a single brush selected", 0 ); + return; + } + + b = selected_brushes.next; + VectorCopy (b->mins, mins); + VectorCopy (b->maxs, maxs); + texdef = &g_qeglobals.d_texturewin.texdef; + + Brush_Free (b); + + // find center of brush + width = 8; + for (i=0 ; i<2 ; i++) + { + mid[i] = (maxs[i] + mins[i])*0.5; + if (maxs[i] - mins[i] > width) + width = maxs[i] - mins[i]; + } + width /= 2; + + b = (brush_t*)qmalloc (sizeof(brush_t)); + + // create top face + f = Face_Alloc(); + f->texdef = *texdef; + f->next = b->brush_faces; + b->brush_faces = f; + + f->planepts[2][0] = mins[0];f->planepts[2][1] = mins[1];f->planepts[2][2] = maxs[2]; + f->planepts[1][0] = maxs[0];f->planepts[1][1] = mins[1];f->planepts[1][2] = maxs[2]; + f->planepts[0][0] = maxs[0];f->planepts[0][1] = maxs[1];f->planepts[0][2] = maxs[2]; + + // create bottom face + f = Face_Alloc(); + f->texdef = *texdef; + f->next = b->brush_faces; + b->brush_faces = f; + +f->planepts[0][0] = mins[0];f->planepts[0][1] = mins[1];f->planepts[0][2] = mins[2]; +f->planepts[1][0] = maxs[0];f->planepts[1][1] = mins[1];f->planepts[1][2] = mins[2]; +f->planepts[2][0] = maxs[0];f->planepts[2][1] = maxs[1];f->planepts[2][2] = mins[2]; + + for (i=0 ; itexdef = *texdef; + f->next = b->brush_faces; + b->brush_faces = f; + + sv = sin (i*3.14159265*2/sides); + cv = cos (i*3.14159265*2/sides); + + f->planepts[0][0] = floor(mid[0]+width*cv+0.5); + f->planepts[0][1] = floor(mid[1]+width*sv+0.5); + f->planepts[0][2] = mins[2]; + + f->planepts[1][0] = f->planepts[0][0]; + f->planepts[1][1] = f->planepts[0][1]; + f->planepts[1][2] = maxs[2]; + + f->planepts[2][0] = floor(f->planepts[0][0] - width*sv + 0.5); + f->planepts[2][1] = floor(f->planepts[0][1] + width*cv + 0.5); + f->planepts[2][2] = maxs[2]; + + } + + Brush_AddToList (b, &selected_brushes); + + Entity_LinkBrush (world_entity, b); + + Brush_Build( b ); + + Sys_UpdateWindows (W_ALL); +} + + +/* +============= +Brush_Free + +Frees the brush with all of its faces and display list. +Unlinks the brush from whichever chain it is in. +Decrements the owner entity's brushcount. +Removes owner entity if this was the last brush +unless owner is the world. +============= +*/ +void Brush_Free (brush_t *b) +{ + face_t *f, *next; + + // free faces + for (f=b->brush_faces ; f ; f=next) + { + next = f->next; + Face_Free( f ); + } + + // unlink from active/selected list + if (b->next) + Brush_RemoveFromList (b); + + // unlink from entity list + if (b->onext) + Entity_UnlinkBrush (b); + + free (b); +} + + +/* +============ +Brush_Clone + +Does NOT add the new brush to any lists +============ +*/ +brush_t *Brush_Clone (brush_t *b) +{ + brush_t *n; + face_t *f, *nf; + + n = (brush_t*)qmalloc(sizeof(brush_t)); + n->owner = b->owner; + for (f=b->brush_faces ; f ; f=f->next) + { + nf = Face_Clone( f ); + nf->next = n->brush_faces; + n->brush_faces = nf; + } + return n; +} + +/* +============== +Brush_Ray + +Itersects a ray with a brush +Returns the face hit and the distance along the ray the intersection occured at +Returns NULL and 0 if not hit at all +============== +*/ +face_t *Brush_Ray (vec3_t origin, vec3_t dir, brush_t *b, float *dist) +{ + face_t *f, *firstface; + vec3_t p1, p2; + double frac, d1, d2; + float scaleValue; + bool scale; + int i; + + VectorCopy (origin, p1); + // Need to clamp the p1 - p2 vector to compelety inside the valid range + // This is horrid.. =) + scale = true; + scaleValue = WORLD_SIZE; + + while(scale) + { + VectorMA(p1, scaleValue, dir, p2); + + scale = false; + for(i = 0; i < 3; i++) + { + if(p2[i] > MAX_WORLD_COORD) + { + scale = true; + } + if(p2[i] < MIN_WORLD_COORD) + { + scale = true; + } + } + scaleValue *= 0.8f; + } + scaleValue /= 0.8f; + VectorMA(p1, scaleValue, dir, p2); + // ..end of nastiness + + for (f=b->brush_faces ; f ; f=f->next) + { + d1 = (double) DotProduct (p1, f->plane.normal) - f->plane.dist; + d2 = (double) DotProduct (p2, f->plane.normal) - f->plane.dist; + if (d1 >= 0 && d2 >= 0) + { + *dist = 0; + return NULL; // ray is on front side of face + } + if (d1 <=0 && d2 <= 0) + continue; + // clip the ray to the plane + frac = d1 / (d1 - d2); + if (d1 > 0) + { + firstface = f; + for (i=0 ; i<3 ; i++) + p1[i] = (double)p1[i] + frac * ((double)p2[i] - (double)p1[i]); + } + else + { + for (i=0 ; i<3 ; i++) + p2[i] = (double)p1[i] + frac * ((double)p2[i] - (double)p1[i]); + } + } + + // find distance p1 is along dir + VectorSubtract (p1, origin, p1); + d1 = DotProduct (p1, dir); + + *dist = d1; + + return firstface; +} + +//PGM +face_t *Brush_Point (vec3_t origin, brush_t *b) +{ + face_t *f; + float d1; + + for (f=b->brush_faces ; f ; f=f->next) + { + d1 = DotProduct (origin, f->plane.normal) - f->plane.dist; + if (d1 > 0) + { + return NULL; // point is on front side of face + } + } + + return b->brush_faces; +} +//PGM + + +void Brush_AddToList (brush_t *b, brush_t *list, bool bAddToBackOfList /* = false */) +{ + if (b->next || b->prev) + Error ("Brush_AddToList: already linked"); + if (b->patchBrush && list == &selected_brushes) + { + Patch_Select(b->nPatchID); + } + + if (bAddToBackOfList) + { + // lists in radiant are circular, so no need to traverse to list end, just link to list-head previous... + // + b->prev = list->prev; + list->prev->next = b; + list->prev = b; + b->next = list; + } + else + { + b->next = list->next; + list->next->prev = b; + list->next = b; + b->prev = list; + } +} + +void Brush_RemoveFromList (brush_t *b) +{ + if (!b->next || !b->prev) + Error ("Brush_RemoveFromList: not linked"); + if (b->patchBrush) + { + Patch_Deselect(b->nPatchID); + } + b->next->prev = b->prev; + b->prev->next = b->next; + b->next = b->prev = NULL; +} + +/* +=============== +SetFaceTexdef + +Doesn't set the curve flags +=============== +*/ +void SetFaceTexdef (brush_t *b, face_t *f, texdef_t *texdef, bool bFitScale, bool bNoSystemTextureOverwrite) +{ + int oldFlags; + int oldContents; + face_t *tf; + + if (bNoSystemTextureOverwrite && !strnicmp(f->texdef.name,"system/",7)) + { + Sys_Printf("( System-shader protecion: Refusing to overwrite face using \"%s\" )\n",f->texdef.name); + return; + } + + oldFlags = f->texdef.flags; + oldContents = f->texdef.contents; + if (bFitScale) + { + f->texdef = *texdef; + // fit the scaling of the texture on the actual plane + vec3_t p1,p2,p3; // absolute coordinates + // compute absolute coordinates + ComputeAbsolute(f,p1,p2,p3); + // compute the scale + vec3_t vx,vy; + VectorSubtract(p2,p1,vx); + VectorNormalize(vx); + VectorSubtract(p3,p1,vy); + VectorNormalize(vy); + // assign scale + VectorScale(vx,texdef->scale[0],vx); + VectorScale(vy,texdef->scale[1],vy); + VectorAdd(p1,vx,p2); + VectorAdd(p1,vy,p3); + // compute back shift scale rot + AbsoluteToLocal(f->plane,f,p1,p2,p3); + } + else + f->texdef = *texdef; + f->texdef.flags = (f->texdef.flags & ~SURF_KEEP) | (oldFlags & SURF_KEEP); + f->texdef.contents = (f->texdef.contents & ~CONTENTS_KEEP) | (oldContents & CONTENTS_KEEP); + + // if this is a curve face, set all other curve faces to the same texdef + if (f->texdef.flags & SURF_CURVE) + { + for (tf = b->brush_faces ; tf ; tf = tf->next) + { + if (tf->texdef.flags & SURF_CURVE) + { + tf->texdef = f->texdef; + } + } + } +} + + +void Brush_SetTexture (brush_t *b, texdef_t *texdef, bool bFitScale/*=false*/, bool bNoSystemTextureOverwrite/*=false*/) +{ + for (face_t* f = b->brush_faces ; f ; f = f->next) + { + SetFaceTexdef (b, f, texdef, bFitScale, bNoSystemTextureOverwrite); + } + Brush_Build( b ); + if (b->patchBrush) + { + Patch_SetTexture(b->nPatchID, texdef); + } +} + + +qboolean ClipLineToFace (vec3_t p1, vec3_t p2, face_t *f) +{ + float d1, d2, fr; + int i; + float *v; + + d1 = DotProduct (p1, f->plane.normal) - f->plane.dist; + d2 = DotProduct (p2, f->plane.normal) - f->plane.dist; + + if (d1 >= 0 && d2 >= 0) + return false; // totally outside + if (d1 <= 0 && d2 <= 0) + return true; // totally inside + + fr = d1 / (d1 - d2); + + if (d1 > 0) + v = p1; + else + v = p2; + + for (i=0 ; i<3 ; i++) + v[i] = p1[i] + fr*(p2[i] - p1[i]); + + return true; +} + + +int AddPlanept (float *f) +{ + int i; + static overflow = 0; + + if (D_MAXPOINTS == g_qeglobals.d_num_move_points) //jfm: stop this overflow + { + overflow++; + Sys_Printf ("Trying to drag too many points!! %d\n",D_MAXPOINTS + overflow); + return 0; + } + overflow = 0; + + for (i=0 ; iowner->eclass->fixedsize) + return; + + c = 0; + for (i=0 ; i<3 ; i++) + c += AddPlanept (f->planepts[i]); + if (c == 0) + return; // allready completely added + + // select all points on this plane in all brushes the selection + for (b2=selected_brushes.next ; b2 != &selected_brushes ; b2 = b2->next) + { + if (b2 == b) + continue; + for (f2=b2->brush_faces ; f2 ; f2=f2->next) + { + for (i=0 ; i<3 ; i++) + if (fabs(DotProduct(f2->planepts[i], f->plane.normal) + -f->plane.dist) > ON_EPSILON) + break; + if (i==3) + { // move this face as well + Brush_SelectFaceForDragging (b2, f2, shear); + break; + } + } + } + + + // if shearing, take all the planes adjacent to + // selected faces and rotate their points so the + // edge clipped by a selcted face has two of the points + if (!shear) + return; + + for (f2=b->brush_faces ; f2 ; f2=f2->next) + { + if (f2 == f) + continue; + w = MakeFaceWinding (b, f2); + if (!w) + continue; + + // any points on f will become new control points + for (i=0 ; inumpoints ; i++) + { + d = DotProduct (w->points[i], f->plane.normal) + - f->plane.dist; + if (d > -ON_EPSILON && d < ON_EPSILON) + break; + } + + // + // if none of the points were on the plane, + // leave it alone + // + if (i != w->numpoints) + { + if (i == 0) + { // see if the first clockwise point was the + // last point on the winding + d = DotProduct (w->points[w->numpoints-1] + , f->plane.normal) - f->plane.dist; + if (d > -ON_EPSILON && d < ON_EPSILON) + i = w->numpoints - 1; + } + + AddPlanept (f2->planepts[0]); + + VectorCopy (w->points[i], f2->planepts[0]); + if (++i == w->numpoints) + i = 0; + + // see if the next point is also on the plane + d = DotProduct (w->points[i] + , f->plane.normal) - f->plane.dist; + if (d > -ON_EPSILON && d < ON_EPSILON) + AddPlanept (f2->planepts[1]); + + VectorCopy (w->points[i], f2->planepts[1]); + if (++i == w->numpoints) + i = 0; + + // the third point is never on the plane + + VectorCopy (w->points[i], f2->planepts[2]); + } + + free(w); + } +} + +/* +============== +Brush_SideSelect + +The mouse click did not hit the brush, so grab one or more side +planes for dragging +============== +*/ +void Brush_SideSelect (brush_t *b, vec3_t origin, vec3_t dir + , qboolean shear) +{ + face_t *f, *f2; + vec3_t p1, p2; + + //if (b->patchBrush) + // return; + //Patch_SideSelect(b->nPatchID, origin, dir); + for (f=b->brush_faces ; f ; f=f->next) + { + VectorCopy (origin, p1); + VectorMA (origin, WORLD_SIZE, dir, p2); + + for (f2=b->brush_faces ; f2 ; f2=f2->next) + { + if (f2 == f) + continue; + ClipLineToFace (p1, p2, f2); + } + + if (f2) + continue; + + if (VectorCompare (p1, origin)) + continue; + if (ClipLineToFace (p1, p2, f)) + continue; + + Brush_SelectFaceForDragging (b, f, shear); + } + + +} + +void Brush_BuildWindings( brush_t *b, bool bSnap ) +{ + winding_t *w; + face_t *face; + vec_t v; + + if (bSnap) + Brush_SnapPlanepts( b ); + + // clear the mins/maxs bounds + b->mins[0] = b->mins[1] = b->mins[2] = MAX_WORLD_COORD; + b->maxs[0] = b->maxs[1] = b->maxs[2] = MIN_WORLD_COORD; + + Brush_MakeFacePlanes (b); + + face = b->brush_faces; + + float fCurveColor = 1.0; + + for ( ; face ; face=face->next) + { + int i, j; + free(face->face_winding); + w = face->face_winding = MakeFaceWinding (b, face); + face->d_texture = Texture_ForName( face->texdef.name ); + + if (!w) + continue; + + for (i=0 ; inumpoints ; i++) + { + // add to bounding box + for (j=0 ; j<3 ; j++) + { + v = w->points[i][j]; + if (v > b->maxs[j]) + b->maxs[j] = v; + if (v < b->mins[j]) + b->mins[j] = v; + } + } + // setup s and t vectors, and set color + SetFaceColor (b, face, fCurveColor); + fCurveColor -= .10; + if (fCurveColor <= 0) + fCurveColor = 1.0; + //BeginTexturingFace( b, face, face->d_texture); + + for (i=0 ; inumpoints ; i++) + EmitTextureCoordinates( w->points[i], face->d_texture, face); + } +} + +/* +================== +Brush_RemoveEmptyFaces + +Frees any overconstraining faces +================== +*/ +void Brush_RemoveEmptyFaces ( brush_t *b ) +{ + face_t *f, *next; + + f = b->brush_faces; + b->brush_faces = NULL; + + for ( ; f ; f=next) + { + next = f->next; + if (!f->face_winding) + Face_Free (f); + else + { + f->next = b->brush_faces; + b->brush_faces = f; + } + + } +} + +void Brush_SnapToGrid(brush_t *pb) +{ + for (face_t *f = pb->brush_faces ; f; f = f->next) + { + for (int i = 0 ;i < 3 ;i++) + { + for (int j = 0 ;j < 3 ; j++) + { + f->planepts[i][j] = floor (f->planepts[i][j] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + } + } + } + Brush_Build(pb); +} + +void Brush_Rotate(brush_t *b, vec3_t vAngle, vec3_t vOrigin, bool bBuild) +{ + for (face_t* f=b->brush_faces ; f ; f=f->next) + { + for (int i=0 ; i<3 ; i++) + { + VectorRotate(f->planepts[i], vAngle, vOrigin, f->planepts[i]); + } + } + if (bBuild) + { + Brush_Build(b, false, false); + } +} + +void Brush_Scale2 (eclass_t *e,brush_t *b, float scale, vec3_t vOrigin, bool bBuild) +{ + vec3_t pts[4][2]; + int i,j; + face_t* f; + + pts[0][0][0] = e->mins[0]; + pts[0][0][1] = e->mins[1]; + + pts[1][0][0] = e->mins[0]; + pts[1][0][1] = e->maxs[1]; + + pts[2][0][0] = e->maxs[0]; + pts[2][0][1] = e->maxs[1]; + + pts[3][0][0] = e->maxs[0]; + pts[3][0][1] = e->mins[1]; + + for (i=0 ; i<4 ; i++) + { + pts[i][0][2] = e->mins[2]; + pts[i][1][0] = pts[i][0][0]; + pts[i][1][1] = pts[i][0][1]; + pts[i][1][2] = e->maxs[2]; + } + + f=b->brush_faces; + for (i=0;i<4; i++,f=f->next) + { + j = (i+1)%4; + VectorCopy (pts[j][1], f->planepts[0]); + VectorCopy (pts[i][1], f->planepts[1]); + VectorCopy (pts[i][0], f->planepts[2]); + } + + VectorCopy (pts[0][1], f->planepts[0]); + VectorCopy (pts[1][1], f->planepts[1]); + VectorCopy (pts[2][1], f->planepts[2]); + + f=f->next; + VectorCopy (pts[2][0], f->planepts[0]); + VectorCopy (pts[1][0], f->planepts[1]); + VectorCopy (pts[0][0], f->planepts[2]); + + + for (f=b->brush_faces ; f ; f=f->next) + { + for (int i=0 ; i<3 ; i++) + { + VectorScale(f->planepts[i], scale, f->planepts[i]); + VectorAdd(vOrigin,f->planepts[i],f->planepts[i]); + } + } + + if (bBuild) + { + Brush_Build(b, false, false); + } +} + + +// only designed for fixed size entity brushes +void Brush_Resize(brush_t *b, vec3_t vMin, vec3_t vMax) +{ + brush_t *b2 = Brush_Create(vMin, vMax, &b->brush_faces->texdef); + + face_t *next; + for (face_t *f=b->brush_faces ; f ; f=next) + { + next = f->next; + Face_Free( f ); + } + + b->brush_faces = b2->brush_faces; + + // unlink from active/selected list + if (b2->next) + Brush_RemoveFromList (b2); + free(b2); + Brush_Build(b, true); +} + + +eclass_t* HasModel(brush_t *b) +{ + vec3_t vMin, vMax; + vMin[0] = vMin[1] = vMin[2] = MAX_WORLD_COORD; + vMax[0] = vMax[1] = vMax[2] = MIN_WORLD_COORD; + + if (b->owner->md3Class != NULL) + { + return b->owner->md3Class; + } + + if (Eclass_hasModel(b->owner->eclass, vMin, vMax)) + { + return b->owner->eclass; + } + + eclass_t *e = NULL; + // FIXME: entity needs to track whether a cache hit failed and not ask again + if (strnicmp(b->owner->eclass->name, "misc_model",10) == 0) + { + char *pModel = ValueForKey(b->owner, "model"); + if (pModel != NULL && strlen(pModel) > 0) + { + e = GetCachedModel(b->owner, pModel, vMin, vMax); + if (e != NULL) + { + // we need to scale the brush to the proper size based on the model load + // recreate brush just like in load/save + + VectorAdd( vMin, b->owner->origin, vMin); + VectorAdd( vMax, b->owner->origin, vMax); + Brush_Resize(b, vMin, vMax); + +/* + // we need to scale the brush to the proper size based on the model load + vec3_t vTemp, vTemp2; + VectorSubtract(b->maxs, b->mins, vTemp); + VectorSubtract(vMax, vMin, vTemp2); + for (int i = 0; i < 3; i++) + { + if (vTemp[i] != 0) + { + vTemp2[i] /= vTemp[i]; + } + } + vec3_t vMid; + vMid[0] = vMid[1] = vMid[2] = 0.0; + for (int j=0 ; j<3 ; j++) + { + vMid[j] = (b->mins[j] + (vTemp[j] / 2)); + } + for (face_t* f=b->brush_faces ; f ; f=f->next) + { + for (int i=0 ; i<3 ; i++) + { + //scale + VectorSubtract(f->planepts[i], vMid, f->planepts[i]); + f->planepts[i][0] *= vTemp2[0]; + f->planepts[i][1] *= vTemp2[1]; + f->planepts[i][2] *= vTemp2[2]; + VectorAdd(f->planepts[i], vMid, f->planepts[i]); + } + } + //Brush_SnapToGrid(b); + Brush_Build(b, true); +*/ + b->bModelFailed = false; + } + else + { + b->bModelFailed = true; + } + } + } + return e; +} + + +static bool g_bInPaintedModel = false; +bool PaintedModel(brush_t *b, bool bOkToTexture, bool bIsGhost) +{ + if (g_bInPaintedModel) + { + return true; + } + + if (g_PrefsDlg.m_nEntityShowState == ENTITY_BOX) + { + return false; + } + else if (!IsBrushSelected(b) && (g_PrefsDlg.m_nEntityShowState & ENTITY_SELECTED_ONLY)) + { + return false; + } + + g_bInPaintedModel = true; + bool bReturn = false; + + eclass_t *pEclass = HasModel(b); + + if (pEclass) + { + qglPushAttrib(GL_ALL_ATTRIB_BITS); + entitymodel *model = pEclass->model; + + float a = FloatForKey (b->owner, "angle"); + +//============== +#ifdef QUAKE3 + bool bUseAnglesCode = false; + vec3_t v3Angles; + if (bOkToTexture) + { + vec3_t v3; + if (GetVectorForKey (b->owner, "angles", v3)) + { + // for some reason this program's own maths routines expect entity axis orders differently, so... + // + v3Angles[0] = v3[2]; + v3Angles[1] = v3[0]; + v3Angles[2] = v3[1]; + bUseAnglesCode = true; + } + } +#endif +//============== + + while (model != NULL) + { + if (bOkToTexture == false || g_PrefsDlg.m_nEntityShowState & ENTITY_WIREFRAME || model->nTextureBind == -1) // skinned + { + qglDisable( GL_CULL_FACE ); + qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE); + qglDisable(GL_TEXTURE_2D); + qglColor3fv(pEclass->color); + } + else + { + qglColor3f(1, 1, 1); + qglEnable(GL_TEXTURE_2D); + qglBindTexture( GL_TEXTURE_2D, model->nTextureBind ); + } + vec3_t v; + + int i,j; + VectorAdd(b->maxs, b->mins, v); // + VectorScale(v, 0.5, v); // + VectorCopy(b->owner->origin, v); // this is dumb, 3rd line just overwrites the other two -slc + + if (bIsGhost) + { + qglColor4fv(v4GhostColor); + qglLineStipple( 8, 0xAAAA); + qglEnable(GL_LINE_STIPPLE); + } + + + //for (i = 0; i < 3; i++) + //{ + // v[i] -= (pEclass->mins[i] - b->mins[i]); + //} + + //if (model->nModelPosition) + //{ + //v[2] = b->mins[2] - (pEclass->mins[2]); + //} + + float s, c; + if (a) + { + s = sin (a/180*Q_PI); + c = cos (a/180*Q_PI); + } + + qglBegin (GL_TRIANGLES); + + for (i = 0; i < model->nTriCount; i++) + { + for (j = 0; j < 3; j++) + { +//============== +#ifdef QUAKE3 + if (bUseAnglesCode) + { + vec3_t v3; + VectorSet(v3, model->pTriList[i].v[j][0] + v[0], + model->pTriList[i].v[j][1] + v[1], + model->pTriList[i].v[j][2] + v[2] + ); + VectorRotate(v3, v3Angles, b->owner->origin, v3); + + qglTexCoord2f (model->pTriList[i].st[j][0], model->pTriList[i].st[j][1]); + qglVertex3f(v3[0], v3[1], v3[2]); + } + else +#endif +//============== + { + float x = model->pTriList[i].v[j][0] + v[0]; + float y = model->pTriList[i].v[j][1] + v[1]; + if (a) + { + float x2 = (((x - v[0]) * c) - ((y - v[1]) * s)) + v[0]; + float y2 = (((x - v[0]) * s) + ((y - v[1]) * c)) + v[1]; + x = x2; + y = y2; + } + //qglTexCoord2f (pEclass->pTriList[i].st[j][0] / pEclass->nSkinWidth, pEclass->pTriList[i].st[j][1] / pEclass->nSkinHeight); + qglTexCoord2f (model->pTriList[i].st[j][0], model->pTriList[i].st[j][1]); + qglVertex3f(x, y, model->pTriList[i].v[j][2] + v[2]); + } + } + } + qglEnd(); + if (g_PrefsDlg.m_nEntityShowState & ENTITY_WIREFRAME) // skinned + { + qglEnable(GL_CULL_FACE ); + qglEnable(GL_TEXTURE_2D); + qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + } + else + { + qglDisable(GL_TEXTURE_2D); + } + + if (bIsGhost) + { + qglDisable(GL_LINE_STIPPLE); + } + + model = model->pNext; + } + + if (g_PrefsDlg.m_nEntityShowState & ENTITY_BOXED) + { + if (bIsGhost) + { + qglColor4fv(v4GhostColor); + qglLineStipple( 8, 0xAAAA); + qglEnable(GL_LINE_STIPPLE); + } + else + { + qglColor3fv(pEclass->color); + } + + + qglBegin(GL_LINE_LOOP); + qglVertex3f(b->mins[0],b->mins[1],b->mins[2]); + qglVertex3f(b->maxs[0],b->mins[1],b->mins[2]); + qglVertex3f(b->maxs[0],b->maxs[1],b->mins[2]); + qglVertex3f(b->mins[0],b->maxs[1],b->mins[2]); + qglEnd(); + + qglBegin(GL_LINE_LOOP); + qglVertex3f(b->mins[0],b->mins[1],b->maxs[2]); + qglVertex3f(b->maxs[0],b->mins[1],b->maxs[2]); + qglVertex3f(b->maxs[0],b->maxs[1],b->maxs[2]); + qglVertex3f(b->mins[0],b->maxs[1],b->maxs[2]); + qglEnd(); + + qglBegin(GL_LINES); + qglVertex3f(b->mins[0],b->mins[1],b->mins[2]); + qglVertex3f(b->mins[0],b->mins[1],b->maxs[2]); + qglVertex3f(b->mins[0],b->maxs[1],b->maxs[2]); + qglVertex3f(b->mins[0],b->maxs[1],b->mins[2]); + qglVertex3f(b->maxs[0],b->mins[1],b->mins[2]); + qglVertex3f(b->maxs[0],b->mins[1],b->maxs[2]); + qglVertex3f(b->maxs[0],b->maxs[1],b->maxs[2]); + qglVertex3f(b->maxs[0],b->maxs[1],b->mins[2]); + qglEnd(); + + if (bIsGhost) + { + qglDisable(GL_LINE_STIPPLE); + } + + } + qglPopAttrib(); + bReturn = true; + } + else + { + b->bModelFailed = true; + } + + g_bInPaintedModel = false; + return bReturn; +} + +void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up) +{ + float angle; + static float sr, sp, sy, cr, cp, cy; + // static to help MS compiler fp bugs + + angle = angles[YAW] * Q_PI / 180; + sy = sin(angle); + cy = cos(angle); + angle = angles[PITCH] * Q_PI / 180; + sp = sin(angle); + cp = cos(angle); + angle = angles[ROLL] * Q_PI / 180; + sr = sin(angle); + cr = cos(angle); + + if (forward) + { + forward[0] = cp*cy; + forward[1] = cp*sy; + forward[2] = -sp; + } + if (right) + { + right[0] = (-1*sr*sp*cy+-1*cr*-sy); + right[1] = (-1*sr*sp*sy+-1*cr*cy); + right[2] = -1*sr*cp; + } + if (up) + { + up[0] = (cr*sp*cy+-sr*-sy); + up[1] = (cr*sp*sy+-sr*cy); + up[2] = cr*cp; + } +} + +void FacingVectors (entity_t *e, vec3_t forward, vec3_t right, vec3_t up) +{ + int angleVal; + vec3_t angles; + + angleVal = IntForKey(e, "angle"); + if (angleVal == -1) // up + { + VectorSet(angles, 270, 0, 0); + } + else if(angleVal == -2) // down + { + VectorSet(angles, 90, 0, 0); + } + else + { + VectorSet(angles, 0, angleVal, 0); + } + + AngleVectors(angles, forward, right, up); +} + +void Brush_DrawFacingAngle (brush_t *b, entity_t *e) +{ + vec3_t forward, right, up; + vec3_t endpoint, tip1, tip2; + vec3_t start; + float dist; + + VectorAdd(e->brushes.onext->mins, e->brushes.onext->maxs, start); + VectorScale(start, 0.5, start); + dist = (b->maxs[0] - start[0]) * 2.5; + + FacingVectors (e, forward, right, up); + VectorMA (start, dist, forward, endpoint); + + dist = (b->maxs[0] - start[0]) * 0.5; + VectorMA (endpoint, -dist, forward, tip1); + VectorMA (tip1, -dist, up, tip1); + VectorMA (tip1, 2*dist, up, tip2); + + qglColor4f (1, 1, 1, 1); + qglLineWidth (4); + qglBegin (GL_LINES); + qglVertex3fv (start); + qglVertex3fv (endpoint); + qglVertex3fv (endpoint); + qglVertex3fv (tip1); + qglVertex3fv (endpoint); + qglVertex3fv (tip2); + qglEnd (); + qglLineWidth (1); +} + +void DrawLight(brush_t *b) +{ + vec3_t vTriColor; + bool bTriPaint = false; + + vTriColor[0] = vTriColor[2] = 1.0; + vTriColor[1] = 1.0; + bTriPaint = true; + CString strColor = ValueForKey(b->owner, "_color"); + if (strColor.GetLength() > 0) + { + float fR, fG, fB; + int n = sscanf(strColor,"%f %f %f", &fR, &fG, &fB); + if (n == 3) + { + vTriColor[0] = fR; + vTriColor[1] = fG; + vTriColor[2] = fB; + } + } + qglColor3f(vTriColor[0], vTriColor[1], vTriColor[2]); + + vec3_t vCorners[4]; + float fMid = b->mins[2] + (b->maxs[2] - b->mins[2]) / 2; + + vCorners[0][0] = b->mins[0]; + vCorners[0][1] = b->mins[1]; + vCorners[0][2] = fMid; + + vCorners[1][0] = b->mins[0]; + vCorners[1][1] = b->maxs[1]; + vCorners[1][2] = fMid; + + vCorners[2][0] = b->maxs[0]; + vCorners[2][1] = b->maxs[1]; + vCorners[2][2] = fMid; + + vCorners[3][0] = b->maxs[0]; + vCorners[3][1] = b->mins[1]; + vCorners[3][2] = fMid; + + vec3_t vTop, vBottom; + + vTop[0] = b->mins[0] + ((b->maxs[0] - b->mins[0]) / 2); + vTop[1] = b->mins[1] + ((b->maxs[1] - b->mins[1]) / 2); + vTop[2] = b->maxs[2]; + + VectorCopy(vTop, vBottom); + vBottom[2] = b->mins[2]; + + vec3_t vSave; + VectorCopy(vTriColor, vSave); + + qglBegin(GL_TRIANGLE_FAN); + qglVertex3fv(vTop); + for (int i = 0; i <= 3; i++) + { + vTriColor[0] *= 0.95; + vTriColor[1] *= 0.95; + vTriColor[2] *= 0.95; + qglColor3f(vTriColor[0], vTriColor[1], vTriColor[2]); + qglVertex3fv(vCorners[i]); + } + qglVertex3fv(vCorners[0]); + qglEnd(); + + VectorCopy(vSave, vTriColor); + vTriColor[0] *= 0.95; + vTriColor[1] *= 0.95; + vTriColor[2] *= 0.95; + + qglBegin(GL_TRIANGLE_FAN); + qglVertex3fv(vBottom); + qglVertex3fv(vCorners[0]); + for (i = 3; i >= 0; i--) + { + vTriColor[0] *= 0.95; + vTriColor[1] *= 0.95; + vTriColor[2] *= 0.95; + qglColor3f(vTriColor[0], vTriColor[1], vTriColor[2]); + qglVertex3fv(vCorners[i]); + } + qglEnd(); +} + + + +extern qboolean qbCheckShowFaces; +void Brush_Draw( brush_t *b, bool bIsGhost ) +{ + face_t *face; + int i, order; + qtexture_t *prev = 0; + winding_t *w; + + if (b->hiddenBrush) + { + return; + } + + + if (b->patchBrush) + { + Patch_DrawCam(b->nPatchID, bIsGhost); +// if (!g_bPatchShowBounds) + return; + } + + int nDrawMode = g_pParentWnd->GetCamera()->Camera().draw_mode; + + if (b->owner->eclass->fixedsize) + { + + /* MERGEME + if(!(g_qeglobals.d_savedinfo.exclude & EXCLUDE_ANGLES) && + !strnicmp(b->owner->eclass->name, "info_player", 11)) + { + Brush_DrawFacingAngle(b, b->owner); + } + */ + + if (g_PrefsDlg.m_bNewLightDraw && strcmpi(b->owner->eclass->name, "light") == 0) + { + DrawLight(b); + return; + } + if (nDrawMode == cd_texture || nDrawMode == cd_light) + qglDisable (GL_TEXTURE_2D); + + // if we are wireframing models + bool bp = (b->bModelFailed) ? false : PaintedModel(b, true, bIsGhost); + + if (nDrawMode == cd_texture || nDrawMode == cd_light) + qglEnable (GL_TEXTURE_2D); + + if (bp) + return; + } + + // guarantee the texture will be set first + prev = NULL; + for (face = b->brush_faces,order = 0 ; face ; face=face->next, order++) + { + w = face->face_winding; + if (!w) + continue; // freed face + +#if 0 + if (b->alphaBrush) + { + if (!(face->texdef.flags & SURF_ALPHA)) + continue; + //--qglPushAttrib(GL_ALL_ATTRIB_BITS); + qglDisable(GL_CULL_FACE); + //--qglTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + //--qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //--qglDisable(GL_DEPTH_TEST); + //--qglBlendFunc (GL_SRC_ALPHA, GL_DST_ALPHA); + //--qglEnable (GL_BLEND); + } +#endif + + if ((nDrawMode == cd_texture || nDrawMode == cd_light) && face->d_texture != prev) + { + // set the texture for this face + prev = face->d_texture; + qglBindTexture( GL_TEXTURE_2D, face->d_texture->texture_number ); + } + + + if (!b->patchBrush) + { + if (face->texdef.flags & SURF_TRANS33) + qglColor4f ( face->d_color[0], face->d_color[1], face->d_color[2], 0.33 ); + else if ( face->texdef.flags & SURF_TRANS66) + qglColor4f ( face->d_color[0], face->d_color[1], face->d_color[2], 0.66 ); + else + qglColor3fv( face->d_color ); + } + else + { + qglColor4f ( face->d_color[0], face->d_color[1], face->d_color[2], 0.13 ); + } + + // shader drawing stuff + if (face->d_texture->bFromShader) + { + // setup shader drawing + qglColor4f ( face->d_color[0], face->d_color[1], face->d_color[2], face->d_texture->fTrans ); + + } + + + if (bIsGhost) + { + qglColor4fv( v4GhostColor ); + qglDisable (GL_TEXTURE_2D); + } + + + // draw the polygon + qglBegin(GL_POLYGON); + if (nDrawMode == cd_light) + qglNormal3fv(face->plane.normal); + + for (i=0 ; inumpoints ; i++) + { + if (nDrawMode == cd_texture || nDrawMode == cd_light) + qglTexCoord2fv( &w->points[i][3] ); + qglVertex3fv(w->points[i]); + } + qglEnd(); + + // draw face lines over top of faces (artist/designer request)... + // drawing them as tris helps show how much overlap.waste there is because of diagonal lines + // + if (qbCheckShowFaces && qbShowFaces) + { + qglDisable (GL_TEXTURE_2D); + if (bIsGhost) + { + qglColor4fv( v4GhostColor ); + } + else + { + qglColor3f(255,255,255); + } + for (i=2 ; inumpoints ; i++) + { + qglBegin(GL_LINE_STRIP); + qglVertex3fv (w->points[0]); + qglVertex3fv (w->points[i-1]); + qglVertex3fv (w->points[i]); + qglVertex3fv (w->points[0]); + qglEnd (); + } + if (nDrawMode == cd_texture || nDrawMode == cd_light) + qglEnable (GL_TEXTURE_2D); + } + + if (bIsGhost) + { + qglEnable (GL_TEXTURE_2D); + } + + } + +#if 0 + if (b->alphaBrush) + { + //--qglPopAttrib(); + qglEnable(GL_CULL_FACE); + //--qglDisable (GL_BLEND); + //--qglTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + } +#endif + + if (b->owner->eclass->fixedsize && (nDrawMode == cd_texture || nDrawMode == cd_light)) + qglEnable (GL_TEXTURE_2D); + + qglBindTexture( GL_TEXTURE_2D, 0 ); +} + + + +void Face_Draw( face_t *f ) +{ + int i; + + if ( f->face_winding == 0 ) + return; + qglBegin( GL_POLYGON ); + for ( i = 0 ; i < f->face_winding->numpoints; i++) + qglVertex3fv( f->face_winding->points[i] ); + qglEnd(); +} + +bool gbSelectedLightBrushesAlwaysDrawRadii = false; +//#define DEGTORAD 0.01745329252 +//#define RADTODEG 57.295779513 +void Brush_DrawXY(brush_t *b, int nViewType, bool bIsGhost) +{ + face_t *face; + int order; + winding_t *w; + int i; + + if (b->hiddenBrush) + { + return; + } + + + if (b->patchBrush) + { + Patch_DrawXY(b->nPatchID, bIsGhost); + if (!g_bPatchShowBounds) + return; + } + + + if (b->owner->eclass->fixedsize) + { + if (g_PrefsDlg.m_bNewLightDraw && strcmpi(b->owner->eclass->name, "light") == 0) + { + vec3_t vCorners[4]; + float fMid = b->mins[2] + (b->maxs[2] - b->mins[2]) / 2; + + vCorners[0][0] = b->mins[0]; + vCorners[0][1] = b->mins[1]; + vCorners[0][2] = fMid; + + vCorners[1][0] = b->mins[0]; + vCorners[1][1] = b->maxs[1]; + vCorners[1][2] = fMid; + + vCorners[2][0] = b->maxs[0]; + vCorners[2][1] = b->maxs[1]; + vCorners[2][2] = fMid; + + vCorners[3][0] = b->maxs[0]; + vCorners[3][1] = b->mins[1]; + vCorners[3][2] = fMid; + + vec3_t vTop, vBottom; + + vTop[0] = b->mins[0] + ((b->maxs[0] - b->mins[0]) / 2); + vTop[1] = b->mins[1] + ((b->maxs[1] - b->mins[1]) / 2); + vTop[2] = b->maxs[2]; + + VectorCopy(vTop, vBottom); + vBottom[2] = b->mins[2]; + + qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE); + qglBegin(GL_TRIANGLE_FAN); + qglVertex3fv(vTop); + qglVertex3fv(vCorners[0]); + qglVertex3fv(vCorners[1]); + qglVertex3fv(vCorners[2]); + qglVertex3fv(vCorners[3]); + qglVertex3fv(vCorners[0]); + qglEnd(); + qglBegin(GL_TRIANGLE_FAN); + qglVertex3fv(vBottom); + qglVertex3fv(vCorners[0]); + qglVertex3fv(vCorners[3]); + qglVertex3fv(vCorners[2]); + qglVertex3fv(vCorners[1]); + qglVertex3fv(vCorners[0]); + qglEnd(); + DrawBrushEntityName (b); + + // now draw lighting radius stuff... + // + if (!(g_qeglobals.d_savedinfo.exclude & EXCLUDE_LIGHTS_RADII) || (gbSelectedLightBrushesAlwaysDrawRadii && IsBrushSelected(b)) ) + { + vec3_t v3Origin; + v3Origin[0] = b->mins[0] + (b->maxs[0] - b->mins[0]) / 2; + v3Origin[1] = b->mins[1] + (b->maxs[1] - b->mins[1]) / 2; + v3Origin[2] = b->mins[2] + (b->maxs[2] - b->mins[2]) / 2; + char *p = ValueForKey(b->owner, "light"); + if (p[0]==0) + { + p = "300"; + } + float f = atof(p); + float fLight = f; + float f45Len = 0.707; + + vec3_t v3TargetOrigin; + bool bDrawSpotlightArc = false; + LPCSTR psTargetName = ValueForKey(b->owner, "target"); + bool bIsSpotLight = !!psTargetName[0]; + if (bIsSpotLight) + { + char *p = ValueForKey(b->owner, "radius"); + if (p[0]==0) + { + p="64"; + } + f = atof(p); + + // find the origin of the target... + // + entity_t *e = FindEntity("targetname", psTargetName); + if (e) + { + v3TargetOrigin[0] = e->brushes.mins[0] + (e->brushes.maxs[0] - e->brushes.mins[0]) / 2; + v3TargetOrigin[1] = e->brushes.mins[1] + (e->brushes.maxs[1] - e->brushes.mins[1]) / 2; + v3TargetOrigin[2] = e->brushes.mins[2] + (e->brushes.maxs[2] - e->brushes.mins[2]) / 2; + bDrawSpotlightArc = true; + } + } + + for (int iPass=0; iPass<2; iPass++) + { + if (iPass) + { + float _f = f; + #define iFLAGS_LINEAR 0x01 + #define iFLAGS_NOINCIDENCE 0x02 + + if (bIsSpotLight) + { + if (bDrawSpotlightArc) + { + // I give up on this, it's beyond me + } + } + else + { + int iFlags = atoi( ValueForKey(b->owner,"spawnflags") ); + if (iFlags & iFLAGS_NOINCIDENCE) + { + f *= 0.9; + } + else + { + f/=4; + } + + p = ValueForKey(b->owner, "scale"); + if (p[0]==0) + { + p="1"; + } + float fScale = atof(p); + f *= fScale; + + // clamp inner dotted circle so it never exceeds outer... + // + if (f>_f) + f=_f; + } + qglLineStipple( 8, 0xAAAA); + qglEnable(GL_LINE_STIPPLE); + } + + qglBegin(GL_LINE_LOOP); + { + if (bIsSpotLight) + { + if (bDrawSpotlightArc) + { + // I give up on this, it's beyond me + /* + vec3_t v3Temp; + VectorSubtract(v3TargetOrigin,v3Origin,v3Temp); + float fAdjacent = VectorLength(v3Temp); + float fOpposite = f/2; + float f2 = atan2(fOpposite,fAdjacent); + f2 *= 180*Q_PI; // radtodeg + + float x, y, a; + + switch (g_pParentWnd->ActiveXY()->GetViewType()) + { + case XY: + + x = v3Origin[0]; + y = v3Origin[1]; + a = f2; + break; + + case XZ: + + x = v3Origin[0]; + y = v3Origin[2]; + a = f2; + break; + + case YZ: + + x = v3Origin[1]; + y = v3Origin[2]; + a = f2; + break; + } + + // findmeste + + qglVertex3f (x+48*cos(a+Q_PI/(360.0f/a)), y+48*sin(a+Q_PI/(360.0f/a)), 0); + qglVertex3f (x, y, 0); + qglVertex3f (x+48*cos(a-Q_PI/(360.0f/a)), y+48*sin(a-Q_PI/(360.0f/a)), 0); + qglEnd (); + */ + } + } + else + { + switch (g_pParentWnd->ActiveXY()->GetViewType()) + { + case XY: + + qglVertex3f( v3Origin[0]+(f*0), v3Origin[1]+(f*1), v3Origin[2] ); + qglVertex3f( v3Origin[0]+(f*f45Len), v3Origin[1]+(f*f45Len), v3Origin[2] ); + qglVertex3f( v3Origin[0]+(f*1), v3Origin[1]+(f*0), v3Origin[2] ); + qglVertex3f( v3Origin[0]+(f*f45Len), v3Origin[1]+(-f*f45Len), v3Origin[2] ); + qglVertex3f( v3Origin[0]+(f*0), v3Origin[1]+(-f*1), v3Origin[2] ); + qglVertex3f( v3Origin[0]+(-f*f45Len), v3Origin[1]+(-f*f45Len), v3Origin[2] ); + qglVertex3f( v3Origin[0]+(-f*1), v3Origin[1]+(f*0), v3Origin[2] ); + qglVertex3f( v3Origin[0]+(-f*f45Len), v3Origin[1]+(f*f45Len), v3Origin[2] ); + break; + + case XZ: + + qglVertex3f( v3Origin[0]+(f*0), v3Origin[1], v3Origin[2]+(f*1) ); + qglVertex3f( v3Origin[0]+(f*f45Len), v3Origin[1], v3Origin[2]+(f*f45Len) ); + qglVertex3f( v3Origin[0]+(f*1), v3Origin[1], v3Origin[2]+(f*0) ); + qglVertex3f( v3Origin[0]+(f*f45Len), v3Origin[1], v3Origin[2]+(-f*f45Len) ); + qglVertex3f( v3Origin[0]+(f*0), v3Origin[1], v3Origin[2]+(-f*1) ); + qglVertex3f( v3Origin[0]+(-f*f45Len), v3Origin[1], v3Origin[2]+(-f*f45Len) ); + qglVertex3f( v3Origin[0]+(-f*1), v3Origin[1], v3Origin[2]+(f*0) ); + qglVertex3f( v3Origin[0]+(-f*f45Len), v3Origin[1], v3Origin[2]+(f*f45Len) ); + break; + + case YZ: + + qglVertex3f( v3Origin[0], v3Origin[1]+(f*0), v3Origin[2]+(f*1) ); + qglVertex3f( v3Origin[0], v3Origin[1]+(f*f45Len), v3Origin[2]+(f*f45Len) ); + qglVertex3f( v3Origin[0], v3Origin[1]+(f*1), v3Origin[2]+(f*0) ); + qglVertex3f( v3Origin[0], v3Origin[1]+(f*f45Len), v3Origin[2]+(-f*f45Len) ); + qglVertex3f( v3Origin[0], v3Origin[1]+(f*0), v3Origin[2]+(-f*1) ); + qglVertex3f( v3Origin[0], v3Origin[1]+(-f*f45Len), v3Origin[2]+(-f*f45Len) ); + qglVertex3f( v3Origin[0], v3Origin[1]+(-f*1), v3Origin[2]+(f*0) ); + qglVertex3f( v3Origin[0], v3Origin[1]+(-f*f45Len), v3Origin[2]+(f*f45Len) ); + break; + } + } + } + qglEnd(); + } + qglDisable(GL_LINE_STIPPLE); + } + + return; + } + else if (strnicmp(b->owner->eclass->name, "misc_model",10) == 0) + { + if (PaintedModel(b, false, bIsGhost)) + return; + } + } + + for (face = b->brush_faces,order = 0 ; face ; face=face->next, order++) + { + // only draw polygons facing in a direction we care about + if (nViewType == XY) + { + if (face->plane.normal[2] <= 0) + continue; + } + else + { + if (nViewType == XZ) + { + if (face->plane.normal[1] <= 0) + continue; + } + else + { + if (face->plane.normal[0] <= 0) + continue; + } + } + + w = face->face_winding; + if (!w) + continue; + + //if (b->alphaBrush && !(face->texdef.flags & SURF_ALPHA)) + // continue; + + //xxxxxxxxxxxxxxxxxxxxxxxxxxxxx + // draw the polygon + qglBegin(GL_LINE_LOOP); + + for (i=0 ; inumpoints ; i++) + qglVertex3fv(w->points[i]); + + qglEnd(); + + + // draw angle arrows, but only if viewing from top down, and it's an ent brush with an "angle" field... + // + if (nViewType == XY && b->owner && (b->owner != world_entity) && strlen(ValueForKey(b->owner,"angle"))) + { + // draw directional arrow... + // + // (work out size first) + // + float fMaxX = MIN_WORLD_COORD; + float fMinX = MAX_WORLD_COORD; + float fMaxY = MIN_WORLD_COORD; + float fMinY = MAX_WORLD_COORD; + float fMaxZ = MIN_WORLD_COORD; + float fMinZ = MAX_WORLD_COORD; + + for (i=0 ; inumpoints ; i++) + { + fMaxX = max(fMaxX, w->points[i][0]); + fMinX = min(fMinX, w->points[i][0]); + + fMaxY = max(fMaxY, w->points[i][1]); + fMinY = min(fMinY, w->points[i][1]); + + fMaxZ = max(fMaxZ, w->points[i][2]); // Z stuff not really important, but useful for coords later so WTF? + fMinZ = min(fMinZ, w->points[i][2]); + } + + float fSmallest= min( (fMaxX-fMinX) , (fMaxY-fMinY) ); // do NOT use Z for smallest calc! + fSmallest*= 0.8f; + + vec3_t v3Centre = + { + fMinX + ((fMaxX-fMinX)/2), + fMinY + ((fMaxY-fMinY)/2), + fMinZ + ((fMaxZ-fMinZ)/2), + }; + + vec3_t v3Top; + VectorCopy(v3Centre,v3Top); + v3Top[1] += fSmallest/2; + + vec3_t v3LTArrowEdge = {v3Centre[0] - fSmallest/4, v3Centre[1] - fSmallest/4, v3Top[2]}; + vec3_t v3RTArrowEdge = {v3Centre[0] + fSmallest/4, v3Centre[1] - fSmallest/4, v3Top[2]}; + + vec3_t v3Angle = {0,0, FloatForKey (b->owner, "angle")-90}; + + VectorRotate(v3Top, v3Angle, v3Centre, v3Top); + VectorRotate(v3LTArrowEdge, v3Angle, v3Centre, v3LTArrowEdge); + VectorRotate(v3RTArrowEdge, v3Angle, v3Centre, v3RTArrowEdge); + + qglBegin(GL_LINE_LOOP); + + qglVertex3fv(v3Top); + qglVertex3fv(v3LTArrowEdge); + qglVertex3fv(v3Centre); + qglVertex3fv(v3RTArrowEdge); + + qglEnd(); + } + } + + DrawBrushEntityName (b); + +} + +face_t *Face_Alloc( void ) +{ + face_t *f = (face_t*)qmalloc( sizeof( *f ) ); + + return f; +} + +void Face_Free( face_t *f ) +{ + assert( f != 0 ); + + if ( f->face_winding ) + { + free( f->face_winding ); + f->face_winding = 0; + } + free( f ); +} + +void Clamp(float& f, int nClamp) +{ + float fFrac = f - static_cast(f); + f = static_cast(f) % nClamp; + f += fFrac; +} + +void Face_MoveTexture(face_t *f, vec3_t delta) +{ + vec3_t vX, vY; + TextureAxisFromPlane(&f->plane, vX, vY); + + vec3_t vDP, vShift; + vDP[0] = DotProduct(delta, vX); + vDP[1] = DotProduct(delta, vY); + + double fAngle = f->texdef.rotate / 180 * Q_PI; + double c = cos(fAngle); + double s = sin(fAngle); + + vShift[0] = vDP[0] * c - vDP[1] * s; + vShift[1] = vDP[0] * s + vDP[1] * c; + + if (!f->texdef.scale[0]) + f->texdef.scale[0] = 1; // fTEXTURE_SCALE? + if (!f->texdef.scale[1]) + f->texdef.scale[1] = 1; // fTEXTURE_SCALE? + + f->texdef.shift[0] -= vShift[0] / f->texdef.scale[0]; + f->texdef.shift[1] -= vShift[1] / f->texdef.scale[1]; + + // clamp the shifts + //f->texdef.shift[0] = static_cast(f->texdef.shift[0]) % f->d_texture->width; + //f->texdef.shift[1] = static_cast(f->texdef.shift[1]) % f->d_texture->height; + Clamp(f->texdef.shift[0], f->d_texture->width); + Clamp(f->texdef.shift[1], f->d_texture->height); + + +} + + +/* +============ +Brush_Move +============ +*/ +void Brush_Move (brush_t *b, const vec3_t move, bool bSnap) +{ + int i; + face_t *f; + + for (f=b->brush_faces ; f ; f=f->next) + { + vec3_t vTemp; + VectorCopy(move, vTemp); + + if (g_PrefsDlg.m_bTextureLock) + Face_MoveTexture(f, vTemp); + + for (i=0 ; i<3 ; i++) + VectorAdd (f->planepts[i], move, f->planepts[i]); + } + Brush_Build( b, bSnap ); + + + if (b->patchBrush) + { + Patch_Move(b->nPatchID, move); + } + + + // PGM - keep the origin vector up to date on fixed size entities. + if(b->owner->eclass->fixedsize) + { + VectorAdd(b->owner->origin, move, b->owner->origin); + //VectorAdd(b->maxs, b->mins, b->owner->origin); + //VectorScale(b->owner->origin, 0.5, b->owner->origin); + } +} + + + +void Brush_Print(brush_t* b) +{ + int nFace = 0; + for (face_t* f = b->brush_faces ; f ; f=f->next) + { + Sys_Printf("Face %i\n", nFace++); + Sys_Printf("%f %f %f\n", f->planepts[0][0], f->planepts[0][1], f->planepts[0][2]); + Sys_Printf("%f %f %f\n", f->planepts[1][0], f->planepts[1][1], f->planepts[1][2]); + Sys_Printf("%f %f %f\n", f->planepts[2][0], f->planepts[2][1], f->planepts[2][2]); + } +} + + + +/* +============= +Brush_MakeSided + +Makes the current brushhave the given number of 2d sides and turns it into a cone +============= +*/ +void Brush_MakeSidedCone(int sides) +{ + int i; + vec3_t mins, maxs; + brush_t *b; + texdef_t *texdef; + face_t *f; + vec3_t mid; + float width; + float sv, cv; + + if (sides < 3) + { + Sys_Status ("Bad sides number", 0); + return; + } + + if (!QE_SingleBrush ()) + { + Sys_Status ("Must have a single brush selected", 0 ); + return; + } + + b = selected_brushes.next; + VectorCopy (b->mins, mins); + VectorCopy (b->maxs, maxs); + texdef = &g_qeglobals.d_texturewin.texdef; + + Brush_Free (b); + + // find center of brush + width = 8; + for (i=0 ; i<2 ; i++) + { + mid[i] = (maxs[i] + mins[i])*0.5; + if (maxs[i] - mins[i] > width) + width = maxs[i] - mins[i]; + } + width /= 2; + + b = (brush_t*)qmalloc (sizeof(brush_t)); + + // create bottom face + f = Face_Alloc(); + f->texdef = *texdef; + f->next = b->brush_faces; + b->brush_faces = f; + + f->planepts[0][0] = mins[0];f->planepts[0][1] = mins[1];f->planepts[0][2] = mins[2]; + f->planepts[1][0] = maxs[0];f->planepts[1][1] = mins[1];f->planepts[1][2] = mins[2]; + f->planepts[2][0] = maxs[0];f->planepts[2][1] = maxs[1];f->planepts[2][2] = mins[2]; + + for (i=0 ; itexdef = *texdef; + f->next = b->brush_faces; + b->brush_faces = f; + + sv = sin (i*3.14159265*2/sides); + cv = cos (i*3.14159265*2/sides); + + + f->planepts[0][0] = floor(mid[0]+width*cv+0.5); + f->planepts[0][1] = floor(mid[1]+width*sv+0.5); + f->planepts[0][2] = mins[2]; + + f->planepts[1][0] = mid[0]; + f->planepts[1][1] = mid[1]; + f->planepts[1][2] = maxs[2]; + + f->planepts[2][0] = floor(f->planepts[0][0] - width * sv + 0.5); + f->planepts[2][1] = floor(f->planepts[0][1] + width * cv + 0.5); + f->planepts[2][2] = maxs[2]; + + } + + Brush_AddToList (b, &selected_brushes); + + Entity_LinkBrush (world_entity, b); + + Brush_Build( b ); + + Sys_UpdateWindows (W_ALL); +} + +/* +============= +Brush_MakeSided + +Makes the current brushhave the given number of 2d sides and turns it into a sphere +============= + +*/ +void Brush_MakeSidedSphere(int sides) +{ + int i,j; + vec3_t mins, maxs; + brush_t *b; + texdef_t *texdef; + face_t *f; + vec3_t mid; + + if (sides < 4) + { + Sys_Status ("Bad sides number", 0); + return; + } + + if (!QE_SingleBrush ()) + { + Sys_Status ("Must have a single brush selected", 0 ); + return; + } + + b = selected_brushes.next; + VectorCopy (b->mins, mins); + VectorCopy (b->maxs, maxs); + texdef = &g_qeglobals.d_texturewin.texdef; + + Brush_Free (b); + + // find center of brush + float radius = 8; + for (i=0 ; i<2 ; i++) + { + mid[i] = (maxs[i] + mins[i])*0.5; + if (maxs[i] - mins[i] > radius) + radius = maxs[i] - mins[i]; + } + radius /= 2; + + b = (brush_t*)qmalloc (sizeof(brush_t)); + + + float dt = float(2 * Q_PI / sides); + float dp = float(Q_PI / sides); + float t,p; + for(i=0; i <= sides-1; i++) + { + for(j=0;j <= sides-2; j++) + { + t = i * dt; + p = float(j * dp - Q_PI / 2); + + f = Face_Alloc(); + f->texdef = *texdef; + f->next = b->brush_faces; + b->brush_faces = f; + + VectorPolar(f->planepts[0], radius, t, p); + VectorPolar(f->planepts[1], radius, t, p + dp); + VectorPolar(f->planepts[2], radius, t + dt, p + dp); + + for (int k = 0; k < 3; k++) + VectorAdd(f->planepts[k], mid, f->planepts[k]); + } + } + + p = float((sides - 1) * dp - Q_PI / 2); + for(i = 0; i <= sides-1; i++) + { + t = i * dt; + + f = Face_Alloc(); + f->texdef = *texdef; + f->next = b->brush_faces; + b->brush_faces = f; + + VectorPolar(f->planepts[0], radius, t, p); + VectorPolar(f->planepts[1], radius, t + dt, p + dp); + VectorPolar(f->planepts[2], radius, t + dt, p); + + for (int k = 0; k < 3; k++) + VectorAdd(f->planepts[k], mid, f->planepts[k]); + } + + Brush_AddToList (b, &selected_brushes); + + Entity_LinkBrush (world_entity, b); + + Brush_Build( b ); + + Sys_UpdateWindows (W_ALL); +} + + + +void Brush_FitTexture( brush_t *b, int nHeight, int nWidth ) +{ + face_t *face; + + for (face = b->brush_faces ; face ; face=face->next) + { + Face_FitTexture( face, nHeight, nWidth ); + } +} + + +void ClearBounds (vec3_t mins, vec3_t maxs) +{ + mins[0] = mins[1] = mins[2] = MAX_WORLD_COORD; + maxs[0] = maxs[1] = maxs[2] = MIN_WORLD_COORD; +} + + +void AddPointToBounds (const vec3_t v, vec3_t mins, vec3_t maxs) +{ + int i; + vec_t val; + + for (i=0 ; i<3 ; i++) + { + val = v[i]; + if (val < mins[i]) + mins[i] = val; + if (val > maxs[i]) + maxs[i] = val; + } +} + + + +void Face_FitTexture( face_t * face, int nHeight, int nWidth ) +{ + winding_t *w; + vec3_t mins,maxs; + int i; + float width, height, temp; + float rot_width, rot_height; + float cosv,sinv,ang; + float min_t, min_s, max_t, max_s; + float s,t; + vec3_t vecs[2]; + vec3_t coords[4]; + texdef_t *td; + + if (nHeight < 1) + { + nHeight = 1; + } + if (nWidth < 1) + { + nWidth = 1; + } + + ClearBounds (mins, maxs); + + td = &face->texdef; + w = face->face_winding; + if (!w) + { + return; + } + for (i=0 ; inumpoints ; i++) + { + AddPointToBounds( w->points[i], mins, maxs ); + } + + //fitcode + // + // get the current angle + // + // hack, swap sides if 90 or 270 degrees, fixes a non-fit bug... + // + if (fabs(td->rotate - 90) < 0.001f || fabs(td->rotate - 270) < 0.001f) + { + int iTemp = nHeight; + nHeight = nWidth; + nWidth = iTemp; + } + + ang = td->rotate / 180 * Q_PI; + sinv = sin(ang); + cosv = cos(ang); + + // get natural texture axis + TextureAxisFromPlane(&face->plane, vecs[0], vecs[1]); + + min_s = DotProduct( mins, vecs[0] ); + min_t = DotProduct( mins, vecs[1] ); + max_s = DotProduct( maxs, vecs[0] ); + max_t = DotProduct( maxs, vecs[1] ); + width = max_s - min_s; + height = max_t - min_t; + coords[0][0] = min_s; + coords[0][1] = min_t; + coords[1][0] = max_s; + coords[1][1] = min_t; + coords[2][0] = min_s; + coords[2][1] = max_t; + coords[3][0] = max_s; + coords[3][1] = max_t; + min_s = min_t = MAX_WORLD_COORD; + max_s = max_t = MIN_WORLD_COORD; + for (i=0; i<4; i++) + { + s = cosv * coords[i][0] - sinv * coords[i][1]; + t = sinv * coords[i][0] + cosv * coords[i][1]; +//////////// + if (s > max_s) + { + max_s = s; + } + if (s < min_s) + { + min_s = s; + } + if (t < min_t) + { + min_t = t; + } + if (t > max_t) + { + max_t = t; + } + +//////////// + if (i&1) + { + if (s > max_s) + { + max_s = s; + } + } + else + { + if (s < min_s) + { + min_s = s; + } + if (i<2) + { + if (t < min_t) + { + min_t = t; + } + } + else + { + if (t > max_t) + { + max_t = t; + } + } + } + } + rot_width = (max_s - min_s); + rot_height = (max_t - min_t); + td->scale[0] = (rot_width/((float)(face->d_texture->width*nWidth))); + td->scale[1] = (rot_height/((float)(face->d_texture->height*nHeight))); + + td->shift[0] = min_s/td->scale[0]; + temp = (int)(td->shift[0] / (face->d_texture->width*nWidth)); + temp = (temp+1)*face->d_texture->width*nWidth; + td->shift[0] = (int)(temp - td->shift[0])%(face->d_texture->width*nWidth); + + td->shift[1] = min_t/td->scale[1]; + temp = (int)(td->shift[1] / (face->d_texture->height*nHeight)); + temp = (temp+1)*(face->d_texture->height*nHeight); + td->shift[1] = (int)(temp - td->shift[1])%(face->d_texture->height*nHeight); +} + + + diff --git a/utils/Radiant/brush.h b/utils/Radiant/brush.h new file mode 100644 index 0000000..abc8c82 --- /dev/null +++ b/utils/Radiant/brush.h @@ -0,0 +1,96 @@ + +// brush.h + +// all types moved to qertypes.h +//--typedef struct +//--{ +//-- int numpoints; +//-- int maxpoints; +//-- float points[8][5]; // variable sized +//--} winding_t; + + +// the normals on planes point OUT of the brush +//--#define MAXPOINTS 16 +//--typedef struct face_s +//--{ +//-- struct face_s *next; +//-- vec3_t planepts[3]; +//-- texdef_t texdef; +//-- plane_t plane; +//-- +//-- winding_t *face_winding; +//-- +//-- vec3_t d_color; +//-- qtexture_t *d_texture; +//-- +//--} face_t; +//-- +//--typedef struct { +//-- vec3_t xyz; +//-- float sideST[2]; +//-- float capST[2]; +//--} curveVertex_t; +//-- +//--typedef struct { +//-- curveVertex_t v[2]; +//--} sideVertex_t; +//-- +//--typedef struct brush_s +//--{ +//-- struct brush_s *prev, *next; // links in active/selected +//-- struct brush_s *oprev, *onext; // links in entity +//-- struct entity_s *owner; +//-- vec3_t mins, maxs; +//-- face_t *brush_faces; +//-- +//-- qboolean bModelFailed; +//-- // +//-- // curve brush extensions +//-- // all are derived from brush_faces +//-- qboolean curveBrush; +//-- qboolean patchBrush; +//-- int nPatchID; +//--} brush_t; + +void Brush_AddToList (brush_t *b, brush_t *list, bool bAddToBackOfList = false ); +void Brush_Build(brush_t *b, bool bSnap = true, bool bMarkMap = true); +void Brush_BuildWindings( brush_t *b, bool bSnap = true ); +brush_t *Brush_Clone (brush_t *b); +brush_t *Brush_Create (vec3_t mins, vec3_t maxs, texdef_t *texdef); +void Brush_Draw( brush_t *b, bool bIsGhost ); +void Brush_DrawXY(brush_t *b, int nViewType, bool bIsGhost = false); +void Brush_Free (brush_t *b); +void Brush_MakeSided (int sides); +void Brush_MakeSidedCone (int sides); +void Brush_Move (brush_t *b, const vec3_t move, bool bSnap = true); +brush_t *Brush_Parse (void); +face_t *Brush_Ray (vec3_t origin, vec3_t dir, brush_t *b, float *dist); +void Brush_RemoveFromList (brush_t *b); +void Brush_SelectFaceForDragging (brush_t *b, face_t *f, qboolean shear); +void Brush_SetTexture (brush_t *b, texdef_t *texdef, bool bFitScale = false, bool bNoSystemTextureOverwrite = false); +void Brush_SideSelect (brush_t *b, vec3_t origin, vec3_t dir, qboolean shear); +void Brush_Write (brush_t *b, FILE *f); +void Brush_RemoveEmptyFaces ( brush_t *b ); +void Brush_MakeFacePlane (face_t *f); +void Face_Free( face_t *f ); + +int AddPlanept (float *f); +face_t *Face_Clone (face_t *f); +void Face_Draw( face_t *face ); +winding_t *MakeFaceWinding (brush_t *b, face_t *face); +void FaceTextureVectors (face_t *f, float STfromXYZ[2][4]); +void SetFaceTexdef (brush_t *b, face_t *f, texdef_t *texdef, bool bFitScale/*=false*/, bool bNoSystemTextureOverwrite/*=false*/ ); +void Brush_Write (brush_t *b, CMemFile* pMemFile); +void Brush_SnapToGrid(brush_t *pb); +face_t *Face_Alloc( void ); +float SetShadeForPlane (plane_t *p); +void ClearBounds (vec3_t mins, vec3_t maxs); +void AddPointToBounds (const vec3_t v, vec3_t mins, vec3_t maxs); +void Brush_Rotate(brush_t *b, vec3_t vAngle, vec3_t vOrigin, bool bBuild = true); +void Brush_Scale2(eclass_t *e,brush_t *b, float scale, vec3_t vAngle, bool bBuild = true); +void Brush_FitTexture( brush_t *b, int nHeight, int nWidth ); +void Face_FitTexture( face_t * face, int nHeight, int nWidth ); + +extern qboolean qbShowFaces; + diff --git a/utils/Radiant/brushscript.cpp b/utils/Radiant/brushscript.cpp new file mode 100644 index 0000000..2c031f5 --- /dev/null +++ b/utils/Radiant/brushscript.cpp @@ -0,0 +1,651 @@ +// BrushScript stuff +// +#include "stdafx.h" +#include "qe3.h" +#include "BSInput.h" +#include "DialogInfo.h" + + +// +struct SVariableDef +{ + CString m_strName; + CString m_strInput; + float m_fValue; +}; + +struct SVecVariableDef +{ + CString m_strName; + CString m_strInput; + vec3_t m_vValue; +}; + + + +const int MAX_VARIABLES = 64; + +brush_t* g_pHold1 = NULL; +brush_t* g_pHold2 = NULL; +brush_t* g_pHold3 = NULL; +bool g_bRotateAroundSelection; +int g_nVariableCount; +int g_nVecVariableCount; +int g_nLoopCounter; +float g_fDefault = MAX_WORLD_COORD; +vec3_t g_vDefault; +bool g_bStartLoop; +char* g_pLooper; +bool g_bKeepGoing; + +SVariableDef g_Variables[MAX_VARIABLES]; +SVecVariableDef g_VecVariables[MAX_VARIABLES]; + +void InitForScriptRun() +{ + g_pHold1 = NULL; + g_pHold2 = NULL; + g_pHold3 = NULL; + g_bRotateAroundSelection = true; + g_nVariableCount = 0; + g_nVecVariableCount = 0; + g_nLoopCounter = 0; + g_bStartLoop = false; + g_pLooper = NULL; + g_bKeepGoing = true; +} + +void AddVariable(const char* pName, float fValue, const char* pInput = NULL) +{ + if (g_nVariableCount < MAX_VARIABLES) + { + g_Variables[g_nVariableCount].m_strName = pName; + g_Variables[g_nVariableCount].m_strName.MakeLower(); + g_Variables[g_nVariableCount].m_fValue = fValue; + if (pInput) + g_Variables[g_nVariableCount].m_strInput = pInput; + g_nVariableCount++; + } + else + g_pParentWnd->MessageBox("Maximum script variable limit reached!"); +} + +float VariableValue(const char* pName) +{ + CString strName = pName; + strName.MakeLower(); + for (int n = 0; n < g_nVariableCount; n++) + { + if (strName == g_Variables[n].m_strName) + return g_Variables[n].m_fValue; + } + //strName.Format("Reference to non-existant varirable %s", pName); + //g_pParentWnd->MessageBox(strName); + return g_fDefault; +} + +void SetVariableValue(const char* pName, float fValue) +{ + CString strName = pName; + strName.MakeLower(); + for (int n = 0; n < g_nVariableCount; n++) + { + if (strName == g_Variables[n].m_strName) + g_Variables[n].m_fValue = fValue; + } +} + + + +void AddVectorVariable(const char* pName, const char* pInput = NULL) +{ + if (g_nVecVariableCount < MAX_VARIABLES) + { + g_VecVariables[g_nVecVariableCount].m_strName = pName; + g_VecVariables[g_nVecVariableCount].m_strName.MakeLower(); + if (pInput) + g_VecVariables[g_nVariableCount].m_strInput = pInput; + g_nVecVariableCount++; + } + else + g_pParentWnd->MessageBox("Maximum script variable limit reached!"); +} + +void VectorVariableValue(const char* pName, vec3_t& v) +{ + CString strName = pName; + strName.MakeLower(); + for (int n = 0; n < g_nVecVariableCount; n++) + { + if (strName == g_VecVariables[n].m_strName) + { + VectorCopy(g_VecVariables[n].m_vValue, v); + return; + } + } + strName.Format("Reference to non-existant variable %s", pName); + g_pParentWnd->MessageBox(strName); +} + +void SetVectorVariableValue(const char* pName, vec3_t v) +{ + CString strName = pName; + strName.MakeLower(); + for (int n = 0; n < g_nVecVariableCount; n++) + { + if (strName == g_VecVariables[n].m_strName) + VectorCopy(v, g_VecVariables[n].m_vValue); + } +} + + + + + +// commands +// +// _CopySelected(nHoldPos) +// copies selected brush to hold spot 1, 2 or 3 +// +// _MoveSelected(x, y, z) +// moves selected brush by coords provided +// +// _RotateSelected(x, y, z) +// rotates selected brush by coords provided +// +// _MoveHold(nHoldPos, x, y, z) +// moves brush in hold pos by coords provided +// +// _RotateHold(nHoldPos, x, y, z) +// rotates brush in hold pos by coords provided +// +// _CopyToMap(nHoldPos) +// copies hold brush to map +// +// _CopyAndSelect(nHoldPos) +// copies hold brush to map and selects it +// +// _Input(VarName1, ... VarNamennn) +// inputs a list of values from the user +// + +typedef void (PFNScript)(char*&); + + +struct SBrushScript +{ + const char* m_pName; + PFNScript* m_pProc; +}; + + +const char* GetParam(char*& pBuffer) +{ + static CString strParam; + bool bStringMode = false; + + while (*pBuffer != NULL && isspace(*pBuffer)) // skip and whitespace + pBuffer++; + + if (*pBuffer == '(') // if it's an opening paren, skip it + pBuffer++; + + if (*pBuffer == '\"') // string ? + { + pBuffer++; + bStringMode = true; + } + + strParam = ""; + + if (bStringMode) + { + while (*pBuffer != NULL && *pBuffer != '\"') + strParam += *pBuffer++; + } + else + { + while (*pBuffer != NULL && *pBuffer != ' ' && *pBuffer != ')' && *pBuffer != ',') + strParam += *pBuffer++; + } + + if (*pBuffer != NULL) // skip last char + pBuffer++; + + if (strParam.GetLength() > 0) + { + if (strParam.GetAt(0) == '$') // ? variable name + { + float f = VariableValue(strParam); + if (f != g_fDefault) + strParam.Format("%f", f); + } + } + + return strParam; +} + +brush_t* CopyBrush(brush_t* p) +{ + brush_t* pCopy = Brush_Clone(p); + //Brush_AddToList (pCopy, &active_brushes); + //Entity_LinkBrush (world_entity, pCopy); + Brush_Build(pCopy, false); + return pCopy; +} + + +void CopySelected(char*& pBuffer) +{ + // expects one param + CString strParam = GetParam(pBuffer); + int n = atoi(strParam); + + brush_t* pCopy = NULL; + if (selected_brushes.next != &selected_brushes && + selected_brushes.next->next == &selected_brushes) + pCopy = selected_brushes.next; + + if (pCopy) + { + if (n == 1) + { + //if (g_pHold1) + //Brush_Free(g_pHold1); + g_pHold1 = CopyBrush(pCopy); + } + else if (n == 2) + { + //if (g_pHold2) + //Brush_Free(g_pHold2); + g_pHold2 = CopyBrush(pCopy); + } + else + { + //if (g_pHold3) + //Brush_Free(g_pHold3); + g_pHold3 = CopyBrush(pCopy); + } + } +} + +void MoveSelected(char*& pBuffer) +{ + vec3_t v; + CString strParam = GetParam(pBuffer); + v[0] = atof(strParam); + strParam = GetParam(pBuffer); + v[1] = atof(strParam); + strParam = GetParam(pBuffer); + v[2] = atof(strParam); + Select_Move(v, false); + Sys_UpdateWindows(W_ALL); +} + +void RotateSelected(char*& pBuffer) +{ + vec3_t v; + + if (g_bRotateAroundSelection) + { + Select_GetTrueMid(v); + VectorCopy(v, g_pParentWnd->ActiveXY()->RotateOrigin()); + } + + CString strParam = GetParam(pBuffer); + v[0] = atof(strParam); + strParam = GetParam(pBuffer); + v[1] = atof(strParam); + strParam = GetParam(pBuffer); + v[2] = atof(strParam); + for (int i = 0; i < 3; i++) + if (v[i] != 0.0) + Select_RotateAxis(i, v[i], false , true); + Sys_UpdateWindows(W_ALL); +} + +void MoveHold(char*& pBuffer) +{ + CString strParam = GetParam(pBuffer); + brush_t* pBrush = NULL; + int nHold = atoi(strParam); + if (nHold == 1) + pBrush = g_pHold1; + else if (nHold == 2) + pBrush = g_pHold2; + else + pBrush = g_pHold3; + + if (pBrush) + { + vec3_t v; + strParam = GetParam(pBuffer); + v[0] = atof(strParam); + strParam = GetParam(pBuffer); + v[1] = atof(strParam); + strParam = GetParam(pBuffer); + v[2] = atof(strParam); + Brush_Move (pBrush, v, false); + } +} + +void RotateHold(char*& pBuffer) +{ + CString strParam = GetParam(pBuffer); + brush_t* pBrush = NULL; + int nHold = atoi(strParam); + if (nHold == 1) + pBrush = g_pHold1; + else if (nHold == 2) + pBrush = g_pHold2; + else + pBrush = g_pHold3; + + if (pBrush) + { + vec3_t v; + strParam = GetParam(pBuffer); + v[0] = atof(strParam); + strParam = GetParam(pBuffer); + v[1] = atof(strParam); + strParam = GetParam(pBuffer); + v[2] = atof(strParam); + for (int i = 0; i < 3; i++) + if (v[i] != 0.0) + Select_RotateAxis(i, v[i]); + } +} + +void CopyToMap(char*& pBuffer) +{ + CString strParam = GetParam(pBuffer); + brush_t* pBrush = NULL; + int nHold = atoi(strParam); + if (nHold == 1) + pBrush = g_pHold1; + else if (nHold == 2) + pBrush = g_pHold2; + else + pBrush = g_pHold3; + + if (pBrush) + { + Brush_AddToList(pBrush, &active_brushes); + Entity_LinkBrush (world_entity, pBrush); + Brush_Build(pBrush, false); + Sys_UpdateWindows(W_ALL); + } +} + +void CopyAndSelect(char*& pBuffer) +{ + CString strParam = GetParam(pBuffer); + brush_t* pBrush = NULL; + int nHold = atoi(strParam); + if (nHold == 1) + pBrush = g_pHold1; + else if (nHold == 2) + pBrush = g_pHold2; + else + pBrush = g_pHold3; + + if (pBrush) + { + Select_Deselect(); + Brush_AddToList(pBrush, &active_brushes); + Entity_LinkBrush (world_entity, pBrush); + Brush_Build(pBrush, false); + Select_Brush(pBrush); + Sys_UpdateWindows(W_ALL); + } +} + +void Input(char*& pBuffer) +{ + CBSInput dlg; + bool bGo = false; + for (int n = 0; n < g_nVariableCount; n++) + { + if (g_Variables[n].m_strInput.GetLength() > 0) + { + bGo = true; + if (n < 5) + { + switch (n) + { + case 0 : dlg.m_strField1 = g_Variables[n].m_strInput; break; + case 1 : dlg.m_strField2 = g_Variables[n].m_strInput; break; + case 2 : dlg.m_strField3 = g_Variables[n].m_strInput; break; + case 3 : dlg.m_strField4 = g_Variables[n].m_strInput; break; + case 4 : dlg.m_strField5 = g_Variables[n].m_strInput; break; + } + } + } + } + if (bGo) + { + if (dlg.DoModal() == IDOK) + { + for (int n = 0; n < g_nVariableCount; n++) + { + if (g_Variables[n].m_strInput.GetLength() > 0) + { + if (n < 5) + { + switch (n) + { + case 0 : g_Variables[n].m_fValue = dlg.m_fField1; break; + case 1 : g_Variables[n].m_fValue = dlg.m_fField2; break; + case 2 : g_Variables[n].m_fValue = dlg.m_fField3; break; + case 3 : g_Variables[n].m_fValue = dlg.m_fField4; break; + case 4 : g_Variables[n].m_fValue = dlg.m_fField5; break; + } + } + } + } + } + else g_bKeepGoing = false; + } +} + +bool g_bWaiting; +void _3DPointDone(bool b, int n) +{ + g_bWaiting = false; +} + +void _3DPointInput(char*& pBuffer) +{ + CString strParam = GetParam(pBuffer); + CString strParam2 = GetParam(pBuffer); + ShowInfoDialog(strParam2); + AddVectorVariable(strParam, strParam2); + g_bWaiting = true; + AcquirePath(2, &_3DPointDone); + while (g_bWaiting) + { + MSG msg; + if (::PeekMessage( &msg, NULL, 0, 0, PM_REMOVE )) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + HideInfoDialog(); + SetVectorVariableValue(strParam, g_PathPoints[0]); +} + +void SetRotateOrigin(char*& pBuffer) +{ + vec3_t v; + CString strParam = GetParam(pBuffer); + VectorVariableValue(strParam, v); + VectorCopy(v, g_pParentWnd->ActiveXY()->RotateOrigin()); + g_bRotateAroundSelection = false; +} + +void InputVar(char*& pBuffer) +{ + CString strParam = GetParam(pBuffer); + CString strParam2 = GetParam(pBuffer); + AddVariable(strParam, 0.0, strParam2); +} + +void LoopCount(char*& pBuffer) +{ + CString strParam = GetParam(pBuffer); + g_nLoopCounter = atoi(strParam); + if (g_nLoopCounter == 0) + g_nLoopCounter = VariableValue(strParam); + if (g_nLoopCounter > 0) + g_pLooper = pBuffer; +} + +void LoopRun(char*& pBuffer) +{ + if (g_bStartLoop == true) + { + g_nLoopCounter--; + if (g_nLoopCounter == 0) + { + g_bStartLoop = false; + GetParam(pBuffer); + } + else + pBuffer = g_pLooper; + } + else + { + if (g_pLooper && g_nLoopCounter > 0) + { + g_bStartLoop = true; + pBuffer = g_pLooper; + } + else + { + GetParam(pBuffer); + } + } +} + + +void ConfirmMessage(char*& pBuffer) +{ + CString strParam = GetParam(pBuffer); + if (g_pParentWnd->MessageBox(strParam, "Script Info", MB_OKCANCEL) == IDCANCEL) + g_bKeepGoing = false; +} + +void Spherize(char*& pBuffer) +{ + g_bScreenUpdates = false; + for (int n = 0; n < 120; n += 36) + { + for (int i = 0; i < 360; i += 36) + { + Select_RotateAxis(0, i, false , true); + CSG_Subtract(); + } + Select_RotateAxis(2, n, false , true); + } + g_bScreenUpdates = true; +} + +void RunIt(char*& pBuffer); +SBrushScript g_ScriptCmds[] = +{ + {"_CopySelected", &CopySelected}, + {"_MoveSelected", &MoveSelected}, + {"_RotateSelected", &RotateSelected}, + {"_MoveHold", &MoveHold}, + {"_RotateHold", &RotateHold}, + {"_CopyToMap", &CopyToMap}, + {"_CopyAndSelect", &CopyAndSelect}, + {"_Input", &Input}, + {"_3DPointInput", &_3DPointInput}, + {"_SetRotateOrigin", &SetRotateOrigin}, + {"_InputVar", &InputVar}, + {"_LoopCount", &LoopCount}, + {"_LoopRun", &LoopRun}, + {"_ConfirmMessage", &ConfirmMessage}, + {"_Spherize", &Spherize}, + {"_RunScript", RunIt} +}; + +const int g_nScriptCmdCount = sizeof(g_ScriptCmds) / sizeof(SBrushScript); + +void RunScript(char* pBuffer) +{ + g_pHold1 = NULL; + g_pHold2 = NULL; + g_pHold3 = NULL; + + while (g_bKeepGoing && pBuffer && *pBuffer) + { + while (*pBuffer != NULL && *pBuffer != '_') + pBuffer++; + + char* pTemp = pBuffer; + int nLen = 0; + while (*pTemp != NULL && *pTemp != '(') + { + pTemp++; + nLen++; + } + if (*pBuffer != NULL) + { + bool bFound = false; + for (int i = 0; i < g_nScriptCmdCount; i++) + { + //if (strnicmp(g_ScriptCmds[i].m_pName, pBuffer, strlen(g_ScriptCmds[i].m_pName)) == 0) + if (strnicmp(g_ScriptCmds[i].m_pName, pBuffer, nLen) == 0) + { + pBuffer += strlen(g_ScriptCmds[i].m_pName); + g_ScriptCmds[i].m_pProc(pBuffer); + if (g_bStartLoop) + { + } + bFound = true; + break; + } + } + if (!bFound) + pBuffer++; + } + } +} + + +void RunScriptByName(char* pBuffer, bool bInit) +{ + if (bInit) + InitForScriptRun(); + char* pScript = new char[4096]; + CString strINI = g_strAppPath; + strINI += "\\scripts.ini"; + GetPrivateProfileSection(pBuffer, pScript, 16384, strINI); + CString strScript; + char* pWorkScript = pScript; + while (*pWorkScript != NULL) + { + strScript += pWorkScript; + pWorkScript += strlen(pWorkScript) + 1; + } + RunScript(strScript.GetBuffer(0)); +} + + +void RunIt(char*& pBuffer) +{ + brush_t* p1 = g_pHold1; + brush_t* p2 = g_pHold2; + brush_t* p3 = g_pHold3; + + CString strParam = GetParam(pBuffer); + RunScriptByName(strParam.GetBuffer(0), false); + + g_pHold3 = p3; + g_pHold2 = p2; + g_pHold1 = p1; +} + diff --git a/utils/Radiant/bsinput.cpp b/utils/Radiant/bsinput.cpp new file mode 100644 index 0000000..acb316b --- /dev/null +++ b/utils/Radiant/bsinput.cpp @@ -0,0 +1,94 @@ +// BSInput.cpp : implementation file +// + +#include "stdafx.h" +#include "Radiant.h" +#include "BSInput.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CBSInput dialog + + +CBSInput::CBSInput(CWnd* pParent /*=NULL*/) + : CDialog(CBSInput::IDD, pParent) +{ + //{{AFX_DATA_INIT(CBSInput) + m_fField1 = 0.0f; + m_fField2 = 0.0f; + m_fField3 = 0.0f; + m_fField4 = 0.0f; + m_fField5 = 0.0f; + m_strField1 = _T(""); + m_strField2 = _T(""); + m_strField3 = _T(""); + m_strField4 = _T(""); + m_strField5 = _T(""); + //}}AFX_DATA_INIT +} + + +void CBSInput::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CBSInput) + DDX_Text(pDX, IDC_EDIT_FIELD1, m_fField1); + DDX_Text(pDX, IDC_EDIT_FIELD2, m_fField2); + DDX_Text(pDX, IDC_EDIT_FIELD3, m_fField3); + DDX_Text(pDX, IDC_EDIT_FIELD4, m_fField4); + DDX_Text(pDX, IDC_EDIT_FIELD5, m_fField5); + DDX_Text(pDX, IDC_STATIC_FIELD1, m_strField1); + DDX_Text(pDX, IDC_STATIC_FIELD2, m_strField2); + DDX_Text(pDX, IDC_STATIC_FIELD3, m_strField3); + DDX_Text(pDX, IDC_STATIC_FIELD4, m_strField4); + DDX_Text(pDX, IDC_STATIC_FIELD5, m_strField5); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CBSInput, CDialog) + //{{AFX_MSG_MAP(CBSInput) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CBSInput message handlers + +BOOL CBSInput::OnInitDialog() +{ + CDialog::OnInitDialog(); + + if (m_strField1.GetLength() == 0) + { + GetDlgItem(IDC_EDIT_FIELD1)->ShowWindow(SW_HIDE); + GetDlgItem(IDC_STATIC_FIELD1)->ShowWindow(SW_HIDE); + } + if (m_strField2.GetLength() == 0) + { + GetDlgItem(IDC_EDIT_FIELD2)->ShowWindow(SW_HIDE); + GetDlgItem(IDC_STATIC_FIELD2)->ShowWindow(SW_HIDE); + } + if (m_strField3.GetLength() == 0) + { + GetDlgItem(IDC_EDIT_FIELD3)->ShowWindow(SW_HIDE); + GetDlgItem(IDC_STATIC_FIELD3)->ShowWindow(SW_HIDE); + } + if (m_strField4.GetLength() == 0) + { + GetDlgItem(IDC_EDIT_FIELD4)->ShowWindow(SW_HIDE); + GetDlgItem(IDC_STATIC_FIELD4)->ShowWindow(SW_HIDE); + } + if (m_strField5.GetLength() == 0) + { + GetDlgItem(IDC_EDIT_FIELD5)->ShowWindow(SW_HIDE); + GetDlgItem(IDC_STATIC_FIELD5)->ShowWindow(SW_HIDE); + } + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/utils/Radiant/bsinput.h b/utils/Radiant/bsinput.h new file mode 100644 index 0000000..67ef333 --- /dev/null +++ b/utils/Radiant/bsinput.h @@ -0,0 +1,55 @@ +#if !defined(AFX_BSINPUT_H__81DF2A32_A552_11D1_B58E_00AA00A410FC__INCLUDED_) +#define AFX_BSINPUT_H__81DF2A32_A552_11D1_B58E_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// BSInput.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CBSInput dialog + +class CBSInput : public CDialog +{ +// Construction +public: + CBSInput(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CBSInput) + enum { IDD = IDD_DIALOG_INPUT }; + float m_fField1; + float m_fField2; + float m_fField3; + float m_fField4; + float m_fField5; + CString m_strField1; + CString m_strField2; + CString m_strField3; + CString m_strField4; + CString m_strField5; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CBSInput) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CBSInput) + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_BSINPUT_H__81DF2A32_A552_11D1_B58E_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/camera.h b/utils/Radiant/camera.h new file mode 100644 index 0000000..6b85eb1 --- /dev/null +++ b/utils/Radiant/camera.h @@ -0,0 +1,30 @@ + +// window system independent camera view code + +typedef enum +{ + cd_wire, + cd_solid, + cd_texture, + cd_light, + cd_blend +} camera_draw_mode; + +typedef struct +{ + int width, height; + + qboolean timing; + + vec3_t origin; + vec3_t angles; + + camera_draw_mode draw_mode; + + vec3_t color; // background + + vec3_t forward, right, up; // move matrix + + vec3_t vup, vpn, vright; // view matrix +} camera_t; + diff --git a/utils/Radiant/camwnd.cpp b/utils/Radiant/camwnd.cpp new file mode 100644 index 0000000..7ec148c --- /dev/null +++ b/utils/Radiant/camwnd.cpp @@ -0,0 +1,1015 @@ +// CamWnd.cpp : implementation file +// + +#include "stdafx.h" +#include "Radiant.h" +#include "XYWnd.h" +#include "CamWnd.h" +#include "qe3.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +extern void DrawPathLines(); +qboolean qbCheckShowFaces = false; + +int g_nAngleSpeed = 300; +int g_nMoveSpeed = 400; + + +///////////////////////////////////////////////////////////////////////////// +// CCamWnd +IMPLEMENT_DYNCREATE(CCamWnd, CWnd); + +CCamWnd::CCamWnd() +{ + m_pXYFriend = NULL; + m_nNumTransBrushes = 0; + memset(&m_Camera, 0, sizeof(camera_t)); + m_pSide_select = NULL; + m_bClipMode = false; + Cam_Init(); +} + +CCamWnd::~CCamWnd() +{ +} + + +BEGIN_MESSAGE_MAP(CCamWnd, CWnd) + //{{AFX_MSG_MAP(CCamWnd) + ON_WM_KEYDOWN() + ON_WM_PAINT() + ON_WM_DESTROY() + ON_WM_CLOSE() + ON_WM_MOUSEMOVE() + ON_WM_LBUTTONDOWN() + ON_WM_LBUTTONUP() + ON_WM_MBUTTONDOWN() + ON_WM_MBUTTONUP() + ON_WM_RBUTTONDOWN() + ON_WM_RBUTTONUP() + ON_WM_CREATE() + ON_WM_SIZE() + ON_WM_NCCALCSIZE() + ON_WM_KILLFOCUS() + ON_WM_SETFOCUS() + ON_WM_KEYUP() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +LONG WINAPI CamWndProc ( + HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + RECT rect; + + GetClientRect(hWnd, &rect); + + switch (uMsg) + { + case WM_KILLFOCUS: + case WM_SETFOCUS: + SendMessage( hWnd, WM_NCACTIVATE, uMsg == WM_SETFOCUS, 0 ); + return 0; + + case WM_NCCALCSIZE:// don't let windows copy pixels + DefWindowProc (hWnd, uMsg, wParam, lParam); + return WVR_REDRAW; + + } + + return DefWindowProc( hWnd, uMsg, wParam, lParam ); +} + + +///////////////////////////////////////////////////////////////////////////// +// CCamWnd message handlers + +BOOL CCamWnd::PreCreateWindow(CREATESTRUCT& cs) +{ + WNDCLASS wc; + HINSTANCE hInstance = AfxGetInstanceHandle(); + if (::GetClassInfo(hInstance, CAMERA_WINDOW_CLASS, &wc) == FALSE) + { + // Register a new class + memset (&wc, 0, sizeof(wc)); + wc.style = CS_NOCLOSE | CS_OWNDC; + wc.lpszClassName = CAMERA_WINDOW_CLASS; + wc.hCursor = LoadCursor (NULL,IDC_ARROW); + wc.lpfnWndProc = CamWndProc; + if (AfxRegisterClass(&wc) == FALSE) + Error ("CCamWnd RegisterClass: failed"); + } + + cs.lpszClass = CAMERA_WINDOW_CLASS; + cs.lpszName = "CAM"; + if (cs.style != QE3_CHILDSTYLE) + cs.style = QE3_SPLITTER_STYLE; + + BOOL bResult = CWnd::PreCreateWindow(cs); + + // See if the class already exists and if not then we need + // to register our new window class. + return bResult; + +} + + +void CCamWnd::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + g_pParentWnd->HandleKey(nChar, nRepCnt, nFlags); +} + + +brush_t* g_pSplitList = NULL; + +void CCamWnd::OnPaint() +{ + CPaintDC dc(this); // device context for painting + bool bPaint = true; + if (!qwglMakeCurrent( dc.m_hDC, g_qeglobals.d_hglrcBase )) + { + Sys_Printf("ERROR: wglMakeCurrent failed..\n "); + Sys_Printf("Please restart QERadiant if the camera view is not working\n"); + } + else + { + QE_CheckOpenGLForErrors(); + g_pSplitList = NULL; + if (g_bClipMode) + { + if (g_Clip1.Set() && g_Clip2.Set()) + { + g_pSplitList = ( (g_pParentWnd->ActiveXY()->GetViewType() == XZ) ? !g_bSwitch : g_bSwitch) ? &g_brBackSplits : &g_brFrontSplits; + } + } + Cam_Draw (); + QE_CheckOpenGLForErrors(); + qwglSwapBuffers(dc.m_hDC); + } +} + + +void CCamWnd::SetXYFriend(CXYWnd * pWnd) +{ + m_pXYFriend = pWnd; +} + +void CCamWnd::OnDestroy() +{ + QEW_StopGL(GetSafeHwnd(), g_qeglobals.d_hglrcBase, g_qeglobals.d_hdcBase ); + CWnd::OnDestroy(); +} + +void CCamWnd::OnClose() +{ + CWnd::OnClose(); +} + +extern void Select_ShiftTexture(int x, int y); +extern void Select_RotateTexture(int amt); +extern void Select_ScaleTexture(int x, int y); +void CCamWnd::OnMouseMove(UINT nFlags, CPoint point) +{ + CRect r; + GetClientRect(r); + if (GetCapture() == this && (GetKeyState(VK_MENU) & 0x8000) && !((GetKeyState(VK_SHIFT) & 0x8000) || (GetKeyState(VK_CONTROL) & 0x8000))) + { + if (GetKeyState(VK_CONTROL) & 0x8000) + Select_RotateTexture(point.y - m_ptLastCursor.y); + else + if (GetKeyState(VK_SHIFT) & 0x8000) + Select_ScaleTexture(point.x - m_ptLastCursor.x, m_ptLastCursor.y - point.y); + else + Select_ShiftTexture(point.x - m_ptLastCursor.x, m_ptLastCursor.y - point.y); + } + else + { + Cam_MouseMoved(point.x, r.bottom - 1 - point.y, nFlags); + } + m_ptLastCursor = point; +} + +void CCamWnd::OnLButtonDown(UINT nFlags, CPoint point) +{ + m_ptLastCursor = point; + OriginalMouseDown(nFlags, point); +} + +void CCamWnd::OnLButtonUp(UINT nFlags, CPoint point) +{ + OriginalMouseUp(nFlags, point); +} + +void CCamWnd::OnMButtonDown(UINT nFlags, CPoint point) +{ + OriginalMouseDown(nFlags, point); +} + +void CCamWnd::OnMButtonUp(UINT nFlags, CPoint point) +{ + OriginalMouseUp(nFlags, point); +} + +void CCamWnd::OnRButtonDown(UINT nFlags, CPoint point) +{ + OriginalMouseDown(nFlags, point); +} + +void CCamWnd::OnRButtonUp(UINT nFlags, CPoint point) +{ + OriginalMouseUp(nFlags, point); +} + +int CCamWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CWnd::OnCreate(lpCreateStruct) == -1) + return -1; + + g_qeglobals.d_hdcBase = GetDC()->m_hDC; + QEW_SetupPixelFormat(g_qeglobals.d_hdcBase, true); + + if ((g_qeglobals.d_hglrcBase = qwglCreateContext(g_qeglobals.d_hdcBase)) == 0) + Error("wglCreateContext failed"); + + if (!qwglMakeCurrent(g_qeglobals.d_hdcBase, g_qeglobals.d_hglrcBase)) + Error ("wglMakeCurrent failed"); + + + // + // create GL font + // + HFONT hfont = ::CreateFont( + 10, // logical height of font + 7, // logical average character width + 0, // angle of escapement + 0, // base-line orientation angle + 0, // font weight + 0, // italic attribute flag + 0, // underline attribute flag + 0, // strikeout attribute flag + 0, // character set identifier + 0, // output precision + 0, // clipping precision + 0, // output quality + 0, // pitch and family + "" // pointer to typeface name string + ); + + if (!hfont) + Error( "couldn't create font" ); + + ::SelectObject(g_qeglobals.d_hdcBase, hfont); + + if ((g_qeglobals.d_font_list = qglGenLists (256)) == 0) + Error( "couldn't create font dlists" ); + + // create the bitmap display lists + // we're making images of glyphs 0 thru 255 + + if (g_PrefsDlg.m_bBuggyICD) + { + if ( !qwglUseFontBitmaps (g_qeglobals.d_hdcBase, 1, 255, g_qeglobals.d_font_list-1) ) + Error( "wglUseFontBitmaps faileD" ); + } + else + { + if ( !qwglUseFontBitmaps (g_qeglobals.d_hdcBase, 1, 255, g_qeglobals.d_font_list) ) + Error( "wglUseFontBitmaps faileD" ); + } + + // indicate start of glyph display lists + qglListBase (g_qeglobals.d_font_list); + + // report OpenGL information + Sys_Printf ("GL_VENDOR: %s\n", qglGetString (GL_VENDOR)); + Sys_Printf ("GL_RENDERER: %s\n", qglGetString (GL_RENDERER)); + Sys_Printf ("GL_VERSION: %s\n", qglGetString (GL_VERSION)); + Sys_Printf ("GL_EXTENSIONS: %s\n", qglGetString (GL_EXTENSIONS)); + + g_qeglobals.d_hwndCamera = GetSafeHwnd(); + + return 0; +} + +void CCamWnd::OriginalMouseUp(UINT nFlags, CPoint point) +{ + CRect r; + GetClientRect(r); + Cam_MouseUp(point.x, r.bottom - 1 - point.y, nFlags); + if (!(nFlags & (MK_LBUTTON|MK_RBUTTON|MK_MBUTTON))) + ReleaseCapture (); +} + +void CCamWnd::OriginalMouseDown(UINT nFlags, CPoint point) +{ + //if (GetTopWindow()->GetSafeHwnd() != GetSafeHwnd()) + // BringWindowToTop(); + CRect r; + GetClientRect(r); + SetFocus(); + SetCapture(); + //if (!(GetKeyState(VK_MENU) & 0x8000)) + Cam_MouseDown (point.x, r.bottom - 1 - point.y, nFlags); +} + +void CCamWnd::Cam_Init() +{ + //m_Camera.draw_mode = cd_texture; + m_Camera.timing = false; + m_Camera.origin[0] = 0; + m_Camera.origin[1] = 20; + m_Camera.origin[2] = 46; + m_Camera.color[0] = 0.3; + m_Camera.color[1] = 0.3; + m_Camera.color[2] = 0.3; +} + +void CCamWnd::Cam_BuildMatrix() +{ + float xa, ya; + float matrix[4][4]; + int i; + + xa = m_Camera.angles[0]/180*Q_PI; + ya = m_Camera.angles[1]/180*Q_PI; + + // the movement matrix is kept 2d + + m_Camera.forward[0] = cos(ya); + m_Camera.forward[1] = sin(ya); + m_Camera.right[0] = m_Camera.forward[1]; + m_Camera.right[1] = -m_Camera.forward[0]; + + qglGetFloatv (GL_PROJECTION_MATRIX, &matrix[0][0]); + + for (i=0 ; i<3 ; i++) + { + m_Camera.vright[i] = matrix[i][0]; + m_Camera.vup[i] = matrix[i][1]; + m_Camera.vpn[i] = matrix[i][2]; + } + + VectorNormalize (m_Camera.vright); + VectorNormalize (m_Camera.vup); + VectorNormalize (m_Camera.vpn); +} + + + +void CCamWnd::Cam_ChangeFloor (qboolean up) +{ + brush_t *b; + float d, bestd, current; + vec3_t start, dir; + + start[0] = m_Camera.origin[0]; + start[1] = m_Camera.origin[1]; + start[2] = MAX_WORLD_COORD/*8192*/; // !suspect!. plus the following hardwired numbers... + dir[0] = dir[1] = 0; + dir[2] = -1; + + current = MAX_WORLD_COORD/*8192*/ - (m_Camera.origin[2] - 48); + if (up) + bestd = 0; + else + bestd = WORLD_SIZE/*16384*/; + + for (b=active_brushes.next ; b != &active_brushes ; b=b->next) + { + if (!Brush_Ray (start, dir, b, &d)) + continue; + if (up && d < current && d > bestd) + bestd = d; + if (!up && d > current && d < bestd) + bestd = d; + } + + if (bestd == 0 || bestd == WORLD_SIZE/*16384*/) + return; + + m_Camera.origin[2] += current - bestd; + Sys_UpdateWindows (W_CAMERA|W_Z_OVERLAY|W_XY_OVERLAY); +} + + +void CCamWnd::Cam_PositionDrag() +{ + int x, y; + Sys_GetCursorPos (&x, &y); + if (x != m_ptCursor.x || y != m_ptCursor.y) + { + x -= m_ptCursor.x; + VectorMA (m_Camera.origin, x, m_Camera.vright, m_Camera.origin); + y -= m_ptCursor.y; + m_Camera.origin[2] -= y; + SetCursorPos(m_ptCursor.x, m_ptCursor.y); + Sys_UpdateWindows (W_CAMERA | W_XY_OVERLAY); + } +} + + +void CCamWnd::Cam_MouseControl (float dtime) +{ + int xl, xh; + int yl, yh; + float xf, yf; + if (g_PrefsDlg.m_nMouseButtons == 2) + { + if (m_nCambuttonstate != (MK_RBUTTON | MK_SHIFT)) + return; + } + else + { + if (m_nCambuttonstate != MK_RBUTTON) + return; + } + + xf = (float)(m_ptButton.x - m_Camera.width/2) / (m_Camera.width/2); + yf = (float)(m_ptButton.y - m_Camera.height/2) / (m_Camera.height/2); + + + xl = m_Camera.width/3; + xh = xl*2; + yl = m_Camera.height/3; + yh = yl*2; + + //Sys_Printf("xf-%f yf-%f xl-%i xh-i% yl-i% yh-i%\n",xf,yf,xl,xh,yl,yh); +#if 0 + // strafe + if (buttony < yl && (buttonx < xl || buttonx > xh)) + VectorMA (camera.origin, xf*dtime*g_nMoveSpeed, camera.right, camera.origin); + else +#endif + { + xf *= 1.0 - fabs(yf); + if (xf < 0) + { + xf += 0.1; + if (xf > 0) + xf = 0; + } + else + { + xf -= 0.1; + if (xf < 0) + xf = 0; + } + + VectorMA (m_Camera.origin, yf*dtime*g_nMoveSpeed, m_Camera.forward, m_Camera.origin); + m_Camera.angles[YAW] += xf*-dtime*g_nAngleSpeed; + } + +#if 0 + if (g_PrefsDlg.m_bQE4Painting) + { + MSG msg; + if (::PeekMessage( &msg, NULL, 0, 0, PM_REMOVE )) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +#endif + + int nUpdate = (g_PrefsDlg.m_bCamXYUpdate) ? (W_CAMERA | W_XY) : (W_CAMERA); + Sys_UpdateWindows (nUpdate); + g_pParentWnd->PostMessage(WM_TIMER, 0, 0); + +} + + + +void CCamWnd::Cam_MouseDown(int x, int y, int buttons) +{ + vec3_t dir; + float f, r, u; + int i; + + // + // calc ray direction + // + u = (float)(y - m_Camera.height/2) / (m_Camera.width/2); + r = (float)(x - m_Camera.width/2) / (m_Camera.width/2); + f = 1; + + for (i=0 ; i<3 ; i++) + dir[i] = m_Camera.vpn[i] * f + m_Camera.vright[i] * r + m_Camera.vup[i] * u; + VectorNormalize (dir); + + GetCursorPos(&m_ptCursor); + + m_nCambuttonstate = buttons; + m_ptButton.x = x; + m_ptButton.y = y; + + // LBUTTON = manipulate selection + // shift-LBUTTON = select + // middle button = grab texture + // ctrl-middle button = set entire brush to texture + // ctrl-shift-middle button = set single face to texture + int nMouseButton = g_PrefsDlg.m_nMouseButtons == 2 ? MK_RBUTTON : MK_MBUTTON; + if ((buttons == MK_LBUTTON) + || (buttons == (MK_LBUTTON | MK_SHIFT)) + || (buttons == (MK_LBUTTON | MK_CONTROL)) + || (buttons == (MK_LBUTTON | MK_CONTROL | MK_SHIFT)) + || (buttons == nMouseButton) + || (buttons == (nMouseButton|MK_SHIFT)) + || (buttons == (nMouseButton|MK_CONTROL)) + || (buttons == (nMouseButton|MK_SHIFT|MK_CONTROL))) + { + + if (g_PrefsDlg.m_nMouseButtons == 2 && (buttons == (MK_RBUTTON | MK_SHIFT))) + Cam_MouseControl (0.1); + else + { + // something global needs to track which window is responsible for stuff + Patch_SetView(W_CAMERA); + Drag_Begin (x, y, buttons, m_Camera.vright, m_Camera.vup, m_Camera.origin, dir); + } + return; + } + + if (buttons == MK_RBUTTON) + { + Cam_MouseControl (0.1); + return; + } +} + + +void CCamWnd::Cam_MouseUp (int x, int y, int buttons) +{ + m_nCambuttonstate = 0; + Drag_MouseUp (buttons); +} + + +void CCamWnd::Cam_MouseMoved (int x, int y, int buttons) +{ + m_nCambuttonstate = buttons; + if (!buttons) + return; + m_ptButton.x = x; + m_ptButton.y = y; + + if (buttons == (MK_RBUTTON|MK_CONTROL) ) + { + Cam_PositionDrag (); + Sys_UpdateWindows (W_XY|W_CAMERA|W_Z); + return; + } + + GetCursorPos(&m_ptCursor); + + if (buttons & (MK_LBUTTON | MK_MBUTTON) ) + { + Drag_MouseMoved (x, y, buttons); + Sys_UpdateWindows (W_XY|W_CAMERA|W_Z); + } +} + + +void CCamWnd::InitCull() +{ + int i; + + VectorSubtract (m_Camera.vpn, m_Camera.vright, m_vCull1); + VectorAdd (m_Camera.vpn, m_Camera.vright, m_vCull2); + + for (i=0 ; i<3 ; i++) + { + if (m_vCull1[i] > 0) + m_nCullv1[i] = 3+i; + else + m_nCullv1[i] = i; + if (m_vCull2[i] > 0) + m_nCullv2[i] = 3+i; + else + m_nCullv2[i] = i; + } +} + +qboolean CCamWnd::CullBrush (brush_t *b) +{ + int i; + vec3_t point; + float d; + + if (g_PrefsDlg.m_bCubicClipping) + { + float fLevel = g_PrefsDlg.m_nCubicScale * 64; + + point[0] = m_Camera.origin[0] - fLevel; + point[1] = m_Camera.origin[1] - fLevel; + point[2] = m_Camera.origin[2] - fLevel; + + for (i=0; i<3; i++) + if (b->mins[i] < point[i] && b->maxs[i] < point[i]) + return true; + + point[0] = m_Camera.origin[0] + fLevel; + point[1] = m_Camera.origin[1] + fLevel; + point[2] = m_Camera.origin[2] + fLevel; + + for (i=0; i<3; i++) + if (b->mins[i] > point[i] && b->maxs[i] > point[i]) + return true; + } + + + for (i=0 ; i<3 ; i++) + point[i] = b->mins[m_nCullv1[i]] - m_Camera.origin[i]; + + d = DotProduct (point, m_vCull1); + if (d < -1) + return true; + + for (i=0 ; i<3 ; i++) + point[i] = b->mins[m_nCullv2[i]] - m_Camera.origin[i]; + + d = DotProduct (point, m_vCull2); + if (d < -1) + return true; + + return false; +} + +#if 0 +void CCamWnd::DrawLightRadius(brush_t* pBrush) +{ + // if lighting + int nRadius = Brush_LightRadius(pBrush); + if (nRadius > 0) + { + Brush_SetLightColor(pBrush); + qglEnable (GL_BLEND); + qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE); + qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + qglDisable (GL_TEXTURE_2D); + + qglEnable(GL_TEXTURE_2D); + qglDisable(GL_BLEND); + qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + } +} +#endif + +/* +============== +Cam_Draw +============== +*/ + +void CCamWnd::Cam_Draw() +{ + brush_t *brush; + face_t *face; + float screenaspect; + float yfov; + double start, end; + int i; + + if (!active_brushes.next) + return; // not valid yet + + if (m_Camera.timing) + start = Sys_DoubleTime (); + + // + // clear + // + QE_CheckOpenGLForErrors(); + + qglViewport(0, 0, m_Camera.width, m_Camera.height); + qglScissor(0, 0, m_Camera.width, m_Camera.height); + qglClearColor (g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][0], + g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][1], + g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][2], 0); + qglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // + // set up viewpoint + // + qglMatrixMode(GL_PROJECTION); + qglLoadIdentity (); + + screenaspect = (float)m_Camera.width / m_Camera.height; + yfov = 2*atan((float)m_Camera.height / m_Camera.width)*180/Q_PI; + qgluPerspective (yfov, screenaspect, 2, MAX_WORLD_COORD/* 8192*/); + + qglRotatef (-90, 1, 0, 0); // put Z going up + qglRotatef (90, 0, 0, 1); // put Z going up + qglRotatef (m_Camera.angles[0], 0, 1, 0); + qglRotatef (-m_Camera.angles[1], 0, 0, 1); + qglTranslatef (-m_Camera.origin[0], -m_Camera.origin[1], -m_Camera.origin[2]); + + Cam_BuildMatrix (); + + InitCull (); + + // + // draw stuff + // + GLfloat lAmbient[] = {1.0, 1.0, 1.0, 1.0}; + + switch (m_Camera.draw_mode) + { + case cd_wire: + qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE); + qglDisable(GL_TEXTURE_2D); + qglDisable(GL_TEXTURE_1D); + qglDisable(GL_BLEND); + qglDisable(GL_DEPTH_TEST); + qglColor3f(1.0, 1.0, 1.0); +// qglEnable (GL_LINE_SMOOTH); + break; + + case cd_solid: + qglCullFace(GL_FRONT); + qglEnable(GL_CULL_FACE); + qglShadeModel (GL_FLAT); + qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + qglDisable(GL_TEXTURE_2D); + qglDisable(GL_BLEND); + qglEnable(GL_DEPTH_TEST); + qglDepthFunc (GL_LEQUAL); + break; + + case cd_texture: + qglCullFace(GL_FRONT); + qglEnable(GL_CULL_FACE); + qglShadeModel (GL_FLAT); + qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + qglEnable(GL_TEXTURE_2D); + qglTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + qglDisable(GL_BLEND); + qglEnable(GL_DEPTH_TEST); + qglDepthFunc (GL_LEQUAL); + break; + + case cd_blend: + qglCullFace(GL_FRONT); + qglEnable(GL_CULL_FACE); + qglShadeModel (GL_FLAT); + qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + qglEnable(GL_TEXTURE_2D); + qglTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + qglDisable(GL_DEPTH_TEST); + qglEnable (GL_BLEND); + qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + break; + } + + qglMatrixMode(GL_TEXTURE); + + m_nNumTransBrushes = 0; + + + qbCheckShowFaces = true; // tells brush drawer to check face-line drawer only within this loop + for (brush = active_brushes.next ; brush != &active_brushes ; brush=brush->next) + { + //DrawLightRadius(brush); + + if (CullBrush (brush)) + continue; + + int iFilter = FilterBrush(brush,true); + if (iFilter==1) // allow 0 or 2 + continue; + + if ( ((brush->brush_faces->texdef.flags & (SURF_TRANS33 | SURF_TRANS66)) || (brush->brush_faces->d_texture->bFromShader && brush->brush_faces->d_texture->fTrans != 1.0)) + || (iFilter==2) + ) + { + m_TransBrushes [ m_nNumTransBrushes ] = brush; + m_bTransBrushIsGhost[ m_nNumTransBrushes++] = (iFilter==2); + } + else + { +// TA-rem, so let's try it here as well + if (brush->patchBrush) + { + m_TransBrushes [ m_nNumTransBrushes ] = brush; + m_bTransBrushIsGhost [ m_nNumTransBrushes++ ] = (iFilter==2); + } + else + Brush_Draw(brush, (iFilter==2) ); + } + } + qbCheckShowFaces = false; + + // + //qglDepthMask ( 0 ); // Don't write to depth buffer + qglEnable ( GL_BLEND ); + qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + for ( i = 0; i < m_nNumTransBrushes; i++ ) + Brush_Draw (m_TransBrushes[i], m_bTransBrushIsGhost[i]); + + //qglDepthMask ( 1 ); // Ok, write now + + qglMatrixMode(GL_PROJECTION); + + // + // now draw selected brushes + // + + qglTranslatef (g_qeglobals.d_select_translate[0], g_qeglobals.d_select_translate[1], g_qeglobals.d_select_translate[2]); + qglMatrixMode(GL_TEXTURE); + + brush_t* pList = (g_bClipMode && g_pSplitList) ? g_pSplitList : &selected_brushes; + // draw normally + for (brush = pList->next ; brush != pList ; brush=brush->next) + { + //DrawLightRadius(brush); + //if (brush->patchBrush && g_qeglobals.d_select_mode == sel_curvepoint) + // continue; + + Brush_Draw(brush,false); + } + + // blend on top + qglMatrixMode(GL_PROJECTION); + + + qglColor4f(1.0, 0.0, 0.0, 0.3); + qglEnable (GL_BLEND); + qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + qglDisable (GL_TEXTURE_2D); + for (brush = pList->next ; brush != pList ; brush=brush->next) + { + if (brush->patchBrush && g_qeglobals.d_select_mode == sel_curvepoint) + continue; + + for (face=brush->brush_faces ; face ; face=face->next) + Face_Draw( face ); + } + + if (selected_face) + Face_Draw(selected_face); + + // non-zbuffered outline + + qglDisable (GL_BLEND); + qglDisable (GL_DEPTH_TEST); + qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE); + qglColor3f (1, 1, 1); + for (brush = pList->next ; brush != pList ; brush=brush->next) + { + if (brush->patchBrush && g_qeglobals.d_select_mode == sel_curvepoint) + continue; + + for (face=brush->brush_faces ; face ; face=face->next) + Face_Draw( face ); + } + + + // edge / vertex flags + + if (g_qeglobals.d_select_mode == sel_vertex) + { + qglPointSize (4); + qglColor3f (0,1,0); + qglBegin (GL_POINTS); + for (i=0 ; im_hDC, true); + if (!qwglMakeCurrent(g_qeglobals.d_hdcBase, g_qeglobals.d_hglrcBase)) + Error ("wglMakeCurrent failed"); + + return; + + long lStyle = ::GetWindowLong(GetSafeHwnd(), GWL_STYLE); + int nID = ::GetWindowLong(GetSafeHwnd(), GWL_ID); + CWnd* pParent = GetParent(); + CRect rctClient; + GetClientRect(rctClient); + DestroyWindow(); + Create(CAMERA_WINDOW_CLASS, "", lStyle, rctClient, pParent, nID); +} + +void CCamWnd::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + g_pParentWnd->HandleKey(nChar, nRepCnt, nFlags, false); +} diff --git a/utils/Radiant/camwnd.h b/utils/Radiant/camwnd.h new file mode 100644 index 0000000..3c0e280 --- /dev/null +++ b/utils/Radiant/camwnd.h @@ -0,0 +1,100 @@ +#if !defined(AFX_CAMWND_H__44B4BA03_781B_11D1_B53C_00AA00A410FC__INCLUDED_) +#define AFX_CAMWND_H__44B4BA03_781B_11D1_B53C_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// CamWnd.h : header file +// +///////////////////////////////////////////////////////////////////////////// +// CCamWnd window +class CXYWnd; + +class CCamWnd : public CWnd +{ + DECLARE_DYNCREATE(CCamWnd); +// Construction +public: + CCamWnd(); + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CCamWnd) + protected: + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + //}}AFX_VIRTUAL + +// Implementation +public: + void ReInitGL(); + void BenchMark(); + CXYWnd* m_pXYFriend; + void SetXYFriend(CXYWnd* pWnd); + virtual ~CCamWnd(); + camera_t& Camera(){return m_Camera;}; + void Cam_MouseControl(float dtime); + void Cam_ChangeFloor(qboolean up); + +protected: + void Cam_Init(); + void Cam_BuildMatrix(); + void Cam_PositionDrag(); + void Cam_MouseDown(int x, int y, int buttons); + void Cam_MouseUp (int x, int y, int buttons); + void Cam_MouseMoved (int x, int y, int buttons); + void InitCull(); + qboolean CullBrush (brush_t *b); + void Cam_Draw(); + + + brush_t* m_TransBrushes[MAX_MAP_BRUSHES]; + bool m_bTransBrushIsGhost[MAX_MAP_BRUSHES]; + + int m_nNumTransBrushes; + camera_t m_Camera; + int m_nCambuttonstate; + CPoint m_ptButton; + CPoint m_ptCursor; + CPoint m_ptLastCursor; + face_t* m_pSide_select; + vec3_t m_vCull1; + vec3_t m_vCull2; + int m_nCullv1[3]; + int m_nCullv2[3]; + bool m_bClipMode; + + // Generated message map functions +protected: + void OriginalMouseDown(UINT nFlags, CPoint point); + void OriginalMouseUp(UINT nFlags, CPoint point); + //{{AFX_MSG(CCamWnd) + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnPaint(); + afx_msg void OnDestroy(); + afx_msg void OnClose(); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg void OnMButtonDown(UINT nFlags, CPoint point); + afx_msg void OnMButtonUp(UINT nFlags, CPoint point); + afx_msg void OnRButtonDown(UINT nFlags, CPoint point); + afx_msg void OnRButtonUp(UINT nFlags, CPoint point); + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_CAMWND_H__44B4BA03_781B_11D1_B53C_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/capdialog.cpp b/utils/Radiant/capdialog.cpp new file mode 100644 index 0000000..fd6d884 --- /dev/null +++ b/utils/Radiant/capdialog.cpp @@ -0,0 +1,43 @@ +// CapDialog.cpp : implementation file +// + +#include "stdafx.h" +#include "Radiant.h" +#include "CapDialog.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CCapDialog dialog + + +CCapDialog::CCapDialog(CWnd* pParent /*=NULL*/) + : CDialog(CCapDialog::IDD, pParent) +{ + //{{AFX_DATA_INIT(CCapDialog) + m_nCap = 0; + //}}AFX_DATA_INIT +} + + +void CCapDialog::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CCapDialog) + DDX_Radio(pDX, IDC_RADIO_CAP, m_nCap); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CCapDialog, CDialog) + //{{AFX_MSG_MAP(CCapDialog) + // NOTE: the ClassWizard will add message map macros here + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CCapDialog message handlers diff --git a/utils/Radiant/capdialog.h b/utils/Radiant/capdialog.h new file mode 100644 index 0000000..0a0d9ea --- /dev/null +++ b/utils/Radiant/capdialog.h @@ -0,0 +1,48 @@ +#if !defined(AFX_CAPDIALOG_H__10637162_2BD2_11D2_B030_00AA00A410FC__INCLUDED_) +#define AFX_CAPDIALOG_H__10637162_2BD2_11D2_B030_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// CapDialog.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CCapDialog dialog + +class CCapDialog : public CDialog +{ +// Construction +public: + static enum {BEVEL = 0, ENDCAP, IBEVEL, IENDCAP}; + CCapDialog(CWnd* pParent = NULL); // standard constructor + + int getCapType() {return m_nCap;}; +// Dialog Data + //{{AFX_DATA(CCapDialog) + enum { IDD = IDD_DIALOG_CAP }; + int m_nCap; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CCapDialog) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CCapDialog) + // NOTE: the ClassWizard will add member functions here + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_CAPDIALOG_H__10637162_2BD2_11D2_B030_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/cbrushstub.cpp b/utils/Radiant/cbrushstub.cpp new file mode 100644 index 0000000..5a29ce3 --- /dev/null +++ b/utils/Radiant/cbrushstub.cpp @@ -0,0 +1,55 @@ +#include "stdafx.h" +#include "qe3.h" + +void Curve_Invert (void) +{ +} + +void Curve_MakeCurvedBrush (qboolean negative, qboolean top, qboolean bottom, + qboolean s1, qboolean s2, qboolean s3, qboolean s4) +{ +} + + +void Curve_CameraDraw (brush_t *b) +{ +} + +void Curve_XYDraw (brush_t *b) +{ +} + +void Curve_WriteFile (char *name) +{ +} + +void Curve_StripFakePlanes( brush_t *b ) +{ +} + +void Curve_AddFakePlanes( brush_t *b ) +{ +} + + +void Patch_BrushToMesh(){}; +void Patch_GenericMesh(int nWidth, int nHeight, int nOrientation){}; +void Patch_WriteFile (char *name){}; +void Patch_Move(int n, const vec3_t vMove){}; +void Patch_EditPatch(int n){}; +void Patch_Scale(int n, const vec3_t vOrigin, const vec3_t vAmt){}; +bool g_bShowPatchBounds; + +/* +bool g_bPatchWireFrame; +void Patch_ApplyMatrix(int n, const vec3_t vOrigin, const vec3_t vMatrix[3]){}; +void Patch_Deselect(){}; +void Patch_Select(int n){}; +void Patch_Cleanup(){}; +void Patch_Delete(int n){}; +void Patch_BuildPoints (brush_t *b){}; +void Curve_BuildPoints (brush_t *b) +{ +} +void Patch_ReadFile (char *name){}; +*/ \ No newline at end of file diff --git a/utils/Radiant/childfrm.h b/utils/Radiant/childfrm.h new file mode 100644 index 0000000..4c5bfd5 --- /dev/null +++ b/utils/Radiant/childfrm.h @@ -0,0 +1,52 @@ +// ChildFrm.h : interface of the CChildFrame class +// +///////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_CHILDFRM_H__330BBF0C_731C_11D1_B539_00AA00A410FC__INCLUDED_) +#define AFX_CHILDFRM_H__330BBF0C_731C_11D1_B539_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +class CChildFrame : public CMDIChildWnd +{ + DECLARE_DYNCREATE(CChildFrame) +public: + CChildFrame(); + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CChildFrame) + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CChildFrame(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +// Generated message map functions +protected: + //{{AFX_MSG(CChildFrame) + // NOTE - the ClassWizard will add and remove member functions here. + // DO NOT EDIT what you see in these blocks of generated code! + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_CHILDFRM_H__330BBF0C_731C_11D1_B539_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/cmdlib.cpp b/utils/Radiant/cmdlib.cpp new file mode 100644 index 0000000..2d20ad2 --- /dev/null +++ b/utils/Radiant/cmdlib.cpp @@ -0,0 +1,686 @@ +// cmdlib.c + +#include "stdafx.h" +#include "qe3.h" +#include "cmdlib.h" + +#define PATHSEPERATOR '/' + +char com_token[1024]; +qboolean com_eof; + +/* +================ +I_FloatTime +================ +*/ +double I_FloatTime (void) +{ + time_t t; + + time (&t); + + return t; +#if 0 +// more precise, less portable + struct timeval tp; + struct timezone tzp; + static int secbase; + + gettimeofday(&tp, &tzp); + + if (!secbase) + { + secbase = tp.tv_sec; + return tp.tv_usec/1000000.0; + } + + return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; +#endif +} + + +/* +============== +COM_Parse + +Parse a token out of a string +============== +*/ +char *COM_Parse (char *data) +{ + int c; + int len; + + len = 0; + com_token[0] = 0; + + if (!data) + return NULL; + +// skip whitespace +skipwhite: + while ( (c = *data) <= ' ') + { + if (c == 0) + { + com_eof = true; + return NULL; // end of file; + } + data++; + } + +// skip // comments + if (c=='/' && data[1] == '/') + { + while (*data && *data != '\n') + data++; + goto skipwhite; + } + + +// handle quoted strings specially + if (c == '\"') + { + data++; + do + { + c = *data++; + if (c=='\"') + { + com_token[len] = 0; + return data; + } + com_token[len] = c; + len++; + } while (1); + } + +// parse single characters + if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') + { + com_token[len] = c; + len++; + com_token[len] = 0; + return data+1; + } + +// parse a regular word + do + { + com_token[len] = c; + data++; + len++; + c = *data; + if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') + break; + } while (c>32); + + com_token[len] = 0; + return data; +} + + +int Q_strncasecmp (char *s1, char *s2, int n) +{ + int c1, c2; + + while (1) + { + c1 = *s1++; + c2 = *s2++; + + if (!n--) + return 0; // strings are equal until end point + + if (c1 != c2) + { + if (c1 >= 'a' && c1 <= 'z') + c1 -= ('a' - 'A'); + if (c2 >= 'a' && c2 <= 'z') + c2 -= ('a' - 'A'); + if (c1 != c2) + return -1; // strings not equal + } + if (!c1) + return 0; // strings are equal + } + + return -1; +} + +int Q_strcasecmp (char *s1, char *s2) +{ + return Q_strncasecmp (s1, s2, 99999); +} + + + +/* +============================================================================= + + MISC FUNCTIONS + +============================================================================= +*/ + + +int argc; +char *argv[MAX_NUM_ARGVS]; + +/* +============ +ParseCommandLine +============ +*/ +void ParseCommandLine (char *lpCmdLine) +{ + argc = 1; + argv[0] = "programname"; + + while (*lpCmdLine && (argc < MAX_NUM_ARGVS)) + { + while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126))) + lpCmdLine++; + + if (*lpCmdLine) + { + argv[argc] = lpCmdLine; + argc++; + + while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126))) + lpCmdLine++; + + if (*lpCmdLine) + { + *lpCmdLine = 0; + lpCmdLine++; + } + + } + } +} + + + +/* +================= +CheckParm + +Checks for the given parameter in the program's command line arguments +Returns the argument number (1 to argc-1) or 0 if not present +================= +*/ +int CheckParm (char *check) +{ + int i; + + for (i = 1;i 0 && path[length] != PATHSEPERATOR) + length--; + path[length] = 0; +} + +void StripExtension (char *path) +{ + int length; + + length = strlen(path)-1; + while (length > 0 && path[length] != '.') + { + length--; + if (path[length] == '/') + return; // no extension + } + if (length) + path[length] = 0; +} + + +/* +==================== +Extract file parts +==================== +*/ +void ExtractFilePath (char *path, char *dest) +{ + char *src; + + src = path + strlen(path) - 1; + +// +// back up until a \ or the start +// + while (src != path && *(src-1) != PATHSEPERATOR) + src--; + + memcpy (dest, path, src-path); + dest[src-path] = 0; +} + +void ExtractFileName (char *path, char *dest) +{ + char *src; + + src = path + strlen(path) - 1; + +// +// back up until a \ or the start +// + while (src != path && *(src-1) != '/' + && *(src-1) != '\\' ) + src--; + + while (*src) + { + *dest++ = *src++; + } + *dest = 0; +} + +void ExtractFileBase (char *path, char *dest) +{ + char *src; + + src = path + strlen(path) - 1; + +// +// back up until a \ or the start +// + while (src != path && *(src-1) != '/' + && *(src-1) != '\\' ) + src--; + + while (*src && *src != '.') + { + *dest++ = *src++; + } + *dest = 0; +} + +void ExtractFileExtension (char *path, char *dest) +{ + char *src; + + src = path + strlen(path) - 1; + +// +// back up until a . or the start +// + while (src != path && *(src-1) != '.') + src--; + if (src == path) + { + *dest = 0; // no extension + return; + } + + strcpy (dest,src); +} + + +/* +============== +ParseNum / ParseHex +============== +*/ +int ParseHex (char *hex) +{ + char *str; + int num; + + num = 0; + str = hex; + + while (*str) + { + num <<= 4; + if (*str >= '0' && *str <= '9') + num += *str-'0'; + else if (*str >= 'a' && *str <= 'f') + num += 10 + *str-'a'; + else if (*str >= 'A' && *str <= 'F') + num += 10 + *str-'A'; + else + Error ("Bad hex number: %s",hex); + str++; + } + + return num; +} + + +int ParseNum (char *str) +{ + if (str[0] == '$') + return ParseHex (str+1); + if (str[0] == '0' && str[1] == 'x') + return ParseHex (str+2); + return atol (str); +} + + + +/* +============================================================================ + + BYTE ORDER FUNCTIONS + +============================================================================ +*/ + +#ifdef _SGI_SOURCE +#define __BIG_ENDIAN__ +#endif + +#ifdef __BIG_ENDIAN__ + +short LittleShort (short l) +{ + byte b1,b2; + + b1 = l&255; + b2 = (l>>8)&255; + + return (b1<<8) + b2; +} + +short BigShort (short l) +{ + return l; +} + + +int LittleLong (int l) +{ + byte b1,b2,b3,b4; + + b1 = l&255; + b2 = (l>>8)&255; + b3 = (l>>16)&255; + b4 = (l>>24)&255; + + return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; +} + +int BigLong (int l) +{ + return l; +} + + +float LittleFloat (float l) +{ + union {byte b[4]; float f;} in, out; + + in.f = l; + out.b[0] = in.b[3]; + out.b[1] = in.b[2]; + out.b[2] = in.b[1]; + out.b[3] = in.b[0]; + + return out.f; +} + +float BigFloat (float l) +{ + return l; +} + + +#else + + +short BigShort (short l) +{ + byte b1,b2; + + b1 = l&255; + b2 = (l>>8)&255; + + return (b1<<8) + b2; +} + +short LittleShort (short l) +{ + return l; +} + + +int BigLong (int l) +{ + byte b1,b2,b3,b4; + + b1 = l&255; + b2 = (l>>8)&255; + b3 = (l>>16)&255; + b4 = (l>>24)&255; + + return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; +} + +int LittleLong (int l) +{ + return l; +} + +float BigFloat (float l) +{ + union {byte b[4]; float f;} in, out; + + in.f = l; + out.b[0] = in.b[3]; + out.b[1] = in.b[2]; + out.b[2] = in.b[1]; + out.b[3] = in.b[0]; + + return out.f; +} + +float LittleFloat (float l) +{ + return l; +} + + + +#endif + diff --git a/utils/Radiant/cmdlib.h b/utils/Radiant/cmdlib.h new file mode 100644 index 0000000..636f6f1 --- /dev/null +++ b/utils/Radiant/cmdlib.h @@ -0,0 +1,78 @@ +// cmdlib.h + +#ifndef __CMDLIB__ +#define __CMDLIB__ + +#include +#include +#include +#include +#include +#include +#include + +#ifndef __BYTEBOOL__ +#define __BYTEBOOL__ +typedef boolean qboolean; +//typedef unsigned char byte; +#endif + +// the dec offsetof macro doesn't work very well... +#define myoffsetof(type,identifier) ((size_t)&((type *)0)->identifier) + + +// set these before calling CheckParm +extern int myargc; +extern char **myargv; + +int Q_strncasecmp (char *s1, char *s2, int n); +int Q_strcasecmp (char *s1, char *s2); + +int Q_filelength (FILE *f); + +double I_FloatTime (void); + +void Error (char *error, ...); +void Warning (char *error, ...); +int CheckParm (char *check); +void ParseCommandLine (char *lpCmdLine); + +FILE *SafeOpenWrite (const char *filename); +FILE *SafeOpenRead (const char *filename); +void SafeRead (FILE *f, void *buffer, int count); +void SafeWrite (FILE *f, void *buffer, int count); + +int LoadFile (const char *filename, void **bufferptr); +int LoadFileNoCrash (const char *filename, void **bufferptr); +void SaveFile (const char *filename, void *buffer, int count); + +void DefaultExtension (char *path, char *extension); +void DefaultPath (char *path, char *basepath); +void StripFilename (char *path); +void StripExtension (char *path); + +void ExtractFilePath (char *path, char *dest); +void ExtractFileName (char *path, char *dest); +void ExtractFileBase (char *path, char *dest); +void ExtractFileExtension (char *path, char *dest); + +int ParseNum (char *str); + +short BigShort (short l); +short LittleShort (short l); +int BigLong (int l); +int LittleLong (int l); +float BigFloat (float l); +float LittleFloat (float l); + + +char *COM_Parse (char *data); + +extern char com_token[1024]; +extern qboolean com_eof; + +#define MAX_NUM_ARGVS 32 +extern int argc; +extern char *argv[MAX_NUM_ARGVS]; + +#endif diff --git a/utils/Radiant/commandsdlg.cpp b/utils/Radiant/commandsdlg.cpp new file mode 100644 index 0000000..f46b37e --- /dev/null +++ b/utils/Radiant/commandsdlg.cpp @@ -0,0 +1,75 @@ +// CommandsDlg.cpp : implementation file +// + +#include "stdafx.h" +#include "Radiant.h" +#include "CommandsDlg.h" +#include "MainFrm.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CCommandsDlg dialog + + +CCommandsDlg::CCommandsDlg(CWnd* pParent /*=NULL*/) + : CDialog(CCommandsDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CCommandsDlg) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void CCommandsDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CCommandsDlg) + DDX_Control(pDX, IDC_LIST_COMMANDS, m_lstCommands); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CCommandsDlg, CDialog) + //{{AFX_MSG_MAP(CCommandsDlg) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CCommandsDlg message handlers + +BOOL CCommandsDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + m_lstCommands.SetTabStops(96); + int nCount = g_nCommandCount; + for (int n = 0; n < nCount; n++) + { + CString strLine; + char c = g_Commands[n].m_nKey; + CString strKeys = c; + for (int k = 0; k < g_nKeyCount; k++) + { + if (g_Keys[k].m_nVKKey == g_Commands[n].m_nKey) + { + strKeys = g_Keys[k].m_strName; + break; + } + } + if (g_Commands[n].m_nModifiers & RAD_SHIFT) + strKeys += "+Shift"; + if (g_Commands[n].m_nModifiers & RAD_ALT) + strKeys += "+Alt"; + if (g_Commands[n].m_nModifiers & RAD_CONTROL) + strKeys += "+Control"; + strLine.Format("%s \t%s", g_Commands[n].m_strCommand, strKeys); + m_lstCommands.AddString(strLine); + } + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/utils/Radiant/commandsdlg.h b/utils/Radiant/commandsdlg.h new file mode 100644 index 0000000..26eb4d8 --- /dev/null +++ b/utils/Radiant/commandsdlg.h @@ -0,0 +1,46 @@ +#if !defined(AFX_COMMANDSDLG_H__C80F6E42_8531_11D1_B548_00AA00A410FC__INCLUDED_) +#define AFX_COMMANDSDLG_H__C80F6E42_8531_11D1_B548_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// CommandsDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CCommandsDlg dialog + +class CCommandsDlg : public CDialog +{ +// Construction +public: + CCommandsDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CCommandsDlg) + enum { IDD = IDD_DLG_COMMANDLIST }; + CListBox m_lstCommands; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CCommandsDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CCommandsDlg) + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_COMMANDSDLG_H__C80F6E42_8531_11D1_B548_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/csg.cpp b/utils/Radiant/csg.cpp new file mode 100644 index 0000000..9627de7 --- /dev/null +++ b/utils/Radiant/csg.cpp @@ -0,0 +1,161 @@ + +#include "stdafx.h" +#include "qe3.h" + +/* +============== +CSG_SplitBrushByFace + +The incoming brush is NOT freed. +The incoming face is NOT left referenced. +============== +*/ +void CSG_SplitBrushByFace (brush_t *in, face_t *f, brush_t **front, brush_t **back) +{ + brush_t *b; + face_t *nf; + vec3_t temp; + + b = Brush_Clone (in); + nf = Face_Clone (f); + + nf->texdef = b->brush_faces->texdef; + nf->next = b->brush_faces; + b->brush_faces = nf; + + Brush_Build( b ); + Brush_RemoveEmptyFaces ( b ); + if ( !b->brush_faces ) + { // completely clipped away + Brush_Free (b); + *back = NULL; + } + else + { + Entity_LinkBrush (in->owner, b); + *back = b; + } + + b = Brush_Clone (in); + nf = Face_Clone (f); + // swap the plane winding + VectorCopy (nf->planepts[0], temp); + VectorCopy (nf->planepts[1], nf->planepts[0]); + VectorCopy (temp, nf->planepts[1]); + + nf->texdef = b->brush_faces->texdef; + nf->next = b->brush_faces; + b->brush_faces = nf; + + Brush_Build( b ); + Brush_RemoveEmptyFaces ( b ); + if ( !b->brush_faces ) + { // completely clipped away + Brush_Free (b); + *front = NULL; + } + else + { + Entity_LinkBrush (in->owner, b); + *front = b; + } +} + +/* +============= +CSG_MakeHollow +============= +*/ +void CSG_MakeHollow (void) +{ + brush_t *b, *front, *back, *next; + face_t *f; + face_t split; + vec3_t move; + int i; + + for (b = selected_brushes.next ; b != &selected_brushes ; b=next) + { + next = b->next; + + if (b->owner->eclass->fixedsize || b->patchBrush || b->hiddenBrush) + continue; + + for (f = b->brush_faces ; f ; f=f->next) + { + split = *f; + VectorScale (f->plane.normal, g_qeglobals.d_gridsize, move); + for (i=0 ; i<3 ; i++) + VectorSubtract (split.planepts[i], move, split.planepts[i]); + + CSG_SplitBrushByFace (b, &split, &front, &back); + if (back) + Brush_Free (back); + if (front) + Brush_AddToList (front, &selected_brushes); + } + Brush_Free (b); + } + Sys_UpdateWindows (W_ALL); +} + +/* +============= +CSG_Subtract +============= +*/ +void CSG_Subtract (void) +{ + brush_t *b, *s, *frag, *front, *back, *next, *snext; + face_t *f; + int i; + + Sys_Printf ("Subtracting...\n"); + + for (b = selected_brushes.next ; b != &selected_brushes ; b=next) + { + next = b->next; + + if (b->owner->eclass->fixedsize) + continue; // can't use texture from a fixed entity, so don't subtract + + for (s=active_brushes.next ; s != &active_brushes ; s=snext) + { + snext = s->next; + + if (s->owner->eclass->fixedsize || s->patchBrush || s->hiddenBrush) + continue; + + if (FilterBrush (s)) + continue; + + //face_t *pFace = s->brush_faces; // not used + if (s->brush_faces->d_texture->bFromShader && (s->brush_faces->d_texture->nShaderFlags & QER_NOCARVE)) + { + continue; + } + + for (i=0 ; i<3 ; i++) + if (b->mins[i] >= s->maxs[i] - ON_EPSILON + || b->maxs[i] <= s->mins[i] + ON_EPSILON) + break; + if (i != 3) + continue; // definately don't touch + + frag = s; + for (f = b->brush_faces ; f && frag ; f=f->next) + { + CSG_SplitBrushByFace (frag, f, &front, &back); + Brush_Free (frag); + frag = back; + if (front) + Brush_AddToList (front, &active_brushes); + } + if (frag) + Brush_Free (frag); + } + } + + Sys_Printf ("done.\n"); + Sys_UpdateWindows (W_ALL); +} diff --git a/utils/Radiant/dialoginfo.cpp b/utils/Radiant/dialoginfo.cpp new file mode 100644 index 0000000..a1ba378 --- /dev/null +++ b/utils/Radiant/dialoginfo.cpp @@ -0,0 +1,73 @@ +// DialogInfo.cpp : implementation file +// + +#include "stdafx.h" +#include "Radiant.h" +#include "DialogInfo.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CDialogInfo dialog +CDialogInfo g_dlgInfo; + +void ShowInfoDialog(const char* pText) +{ + if (g_dlgInfo.GetSafeHwnd()) + { + g_dlgInfo.m_wndInfo.SetWindowText(pText); + g_dlgInfo.ShowWindow(SW_SHOW); + } + else + { + g_dlgInfo.Create(IDD_DLG_INFORMATION); + g_dlgInfo.m_wndInfo.SetWindowText(pText); + g_dlgInfo.ShowWindow(SW_SHOW); + } + g_pParentWnd->SetFocus(); +} + +void HideInfoDialog() +{ + if (g_dlgInfo.GetSafeHwnd()) + g_dlgInfo.ShowWindow(SW_HIDE); +} + + +CDialogInfo::CDialogInfo(CWnd* pParent /*=NULL*/) + : CDialog(CDialogInfo::IDD, pParent) +{ + //{{AFX_DATA_INIT(CDialogInfo) + //}}AFX_DATA_INIT +} + + +void CDialogInfo::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CDialogInfo) + DDX_Control(pDX, IDC_EDIT1, m_wndInfo); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CDialogInfo, CDialog) + //{{AFX_MSG_MAP(CDialogInfo) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CDialogInfo message handlers + +BOOL CDialogInfo::OnInitDialog() +{ + CDialog::OnInitDialog(); + // TODO: Add extra initialization here + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/utils/Radiant/dialoginfo.h b/utils/Radiant/dialoginfo.h new file mode 100644 index 0000000..f0a87e9 --- /dev/null +++ b/utils/Radiant/dialoginfo.h @@ -0,0 +1,48 @@ +#if !defined(AFX_DIALOGINFO_H__81DF2A33_A552_11D1_B58E_00AA00A410FC__INCLUDED_) +#define AFX_DIALOGINFO_H__81DF2A33_A552_11D1_B58E_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// DialogInfo.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CDialogInfo dialog +void HideInfoDialog(); +void ShowInfoDialog(const char* pText); + +class CDialogInfo : public CDialog +{ +// Construction +public: + CDialogInfo(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CDialogInfo) + enum { IDD = IDD_DLG_INFORMATION }; + CEdit m_wndInfo; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CDialogInfo) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CDialogInfo) + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_DIALOGINFO_H__81DF2A33_A552_11D1_B58E_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/dialogtextures.cpp b/utils/Radiant/dialogtextures.cpp new file mode 100644 index 0000000..df926c7 --- /dev/null +++ b/utils/Radiant/dialogtextures.cpp @@ -0,0 +1,68 @@ +// DialogTextures.cpp : implementation file +// + +#include "stdafx.h" +#include "Radiant.h" +#include "DialogTextures.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CDialogTextures dialog + + +CDialogTextures::CDialogTextures(CWnd* pParent /*=NULL*/) + : CDialog(CDialogTextures::IDD, pParent) +{ + //{{AFX_DATA_INIT(CDialogTextures) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void CDialogTextures::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CDialogTextures) + DDX_Control(pDX, IDC_LIST_TEXTURES, m_wndList); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CDialogTextures, CDialog) + //{{AFX_MSG_MAP(CDialogTextures) + ON_LBN_DBLCLK(IDC_LIST_TEXTURES, OnDblclkListTextures) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CDialogTextures message handlers + +void CDialogTextures::OnOK() +{ + m_nSelection = m_wndList.GetCurSel(); + CDialog::OnOK(); +} + +void CDialogTextures::OnDblclkListTextures() +{ + OnOK(); +} + +BOOL CDialogTextures::OnInitDialog() +{ + CDialog::OnInitDialog(); + CStringArray sa; + FillTextureMenu(&sa); + m_nSelection = -1; + for (int i = 0; i < sa.GetSize(); i ++) + { + m_wndList.AddString(sa.GetAt(i)); + } + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/utils/Radiant/dialogtextures.h b/utils/Radiant/dialogtextures.h new file mode 100644 index 0000000..fb392b9 --- /dev/null +++ b/utils/Radiant/dialogtextures.h @@ -0,0 +1,48 @@ +#if !defined(AFX_DIALOGTEXTURES_H__F3F3F984_E47E_11D1_B61B_00AA00A410FC__INCLUDED_) +#define AFX_DIALOGTEXTURES_H__F3F3F984_E47E_11D1_B61B_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// DialogTextures.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CDialogTextures dialog + +class CDialogTextures : public CDialog +{ +// Construction +public: + CDialogTextures(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CDialogTextures) + enum { IDD = IDD_DIALOG_TEXTURELIST }; + CListBox m_wndList; + //}}AFX_DATA + int m_nSelection; + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CDialogTextures) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + // Generated message map functions + //{{AFX_MSG(CDialogTextures) + virtual void OnOK(); + afx_msg void OnDblclkListTextures(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_DIALOGTEXTURES_H__F3F3F984_E47E_11D1_B61B_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/dialogthick.cpp b/utils/Radiant/dialogthick.cpp new file mode 100644 index 0000000..0c05cdc --- /dev/null +++ b/utils/Radiant/dialogthick.cpp @@ -0,0 +1,45 @@ +// DialogThick.cpp : implementation file +// + +#include "stdafx.h" +#include "Radiant.h" +#include "DialogThick.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CDialogThick dialog + + +CDialogThick::CDialogThick(CWnd* pParent /*=NULL*/) + : CDialog(CDialogThick::IDD, pParent) +{ + //{{AFX_DATA_INIT(CDialogThick) + m_bSeams = TRUE; + m_nAmount = 8; + //}}AFX_DATA_INIT +} + + +void CDialogThick::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CDialogThick) + DDX_Check(pDX, IDC_CHECK_SEAMS, m_bSeams); + DDX_Text(pDX, IDC_EDIT_AMOUNT, m_nAmount); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CDialogThick, CDialog) + //{{AFX_MSG_MAP(CDialogThick) + // NOTE: the ClassWizard will add message map macros here + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CDialogThick message handlers diff --git a/utils/Radiant/dialogthick.h b/utils/Radiant/dialogthick.h new file mode 100644 index 0000000..7d4cb30 --- /dev/null +++ b/utils/Radiant/dialogthick.h @@ -0,0 +1,47 @@ +#if !defined(AFX_DIALOGTHICK_H__59F46602_553D_11D2_B082_00AA00A410FC__INCLUDED_) +#define AFX_DIALOGTHICK_H__59F46602_553D_11D2_B082_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// DialogThick.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CDialogThick dialog + +class CDialogThick : public CDialog +{ +// Construction +public: + CDialogThick(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CDialogThick) + enum { IDD = IDD_DIALOG_THICKEN }; + BOOL m_bSeams; + int m_nAmount; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CDialogThick) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CDialogThick) + // NOTE: the ClassWizard will add member functions here + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_DIALOGTHICK_H__59F46602_553D_11D2_B082_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/drag.cpp b/utils/Radiant/drag.cpp new file mode 100644 index 0000000..1053978 --- /dev/null +++ b/utils/Radiant/drag.cpp @@ -0,0 +1,654 @@ +#include "stdafx.h" +#include "qe3.h" + +/* + + drag either multiple brushes, or select plane points from + a single brush. + +*/ + +qboolean drag_ok; +vec3_t drag_xvec; +vec3_t drag_yvec; + +static int buttonstate; +int pressx, pressy; +static vec3_t pressdelta; +static vec3_t vPressStart; +static int buttonx, buttony; + + +//int num_move_points; +//float *move_points[1024]; + +int lastx, lasty; + +qboolean drag_first; + + +void AxializeVector (vec3_t v) +{ + vec3_t a; + float o; + int i; + + if (!v[0] && !v[1]) + return; + if (!v[1] && !v[2]) + return; + if (!v[0] && !v[2]) + return; + + for (i=0 ; i<3 ; i++) + a[i] = fabs(v[i]); + if (a[0] > a[1] && a[0] > a[2]) + i = 0; + else if (a[1] > a[0] && a[1] > a[2]) + i = 1; + else + i = 2; + + o = v[i]; + VectorCopy (vec3_origin, v); + if (o<0) + v[i] = -1; + else + v[i] = 1; + +} + + +/* +=========== +Drag_Setup +=========== +*/ +void Drag_Setup (int x, int y, int buttons, + vec3_t xaxis, vec3_t yaxis, + vec3_t origin, vec3_t dir) +{ + trace_t t; + face_t *f; + + drag_first = true; + + VectorCopy (vec3_origin, pressdelta); + pressx = x; + pressy = y; + + VectorCopy (xaxis, drag_xvec); + AxializeVector (drag_xvec); + VectorCopy (yaxis, drag_yvec); + AxializeVector (drag_yvec); + + + extern void SelectCurvePointByRay (vec3_t org, vec3_t dir, int buttons); + if (g_qeglobals.d_select_mode == sel_curvepoint) + { + //if ((buttons == MK_LBUTTON)) + // g_qeglobals.d_num_move_points = 0; + + SelectCurvePointByRay (origin, dir, buttons); + + if (g_qeglobals.d_num_move_points || g_qeglobals.d_select_mode == sel_area) + { + drag_ok = true; + } + + Sys_UpdateWindows(W_ALL); + return; + + } + else + { + g_qeglobals.d_num_move_points = 0; + } + + if (selected_brushes.next == &selected_brushes) + { + Sys_Status("No selection to drag\n", 0); + return; + } + + + if (g_qeglobals.d_select_mode == sel_vertex) + { + SelectVertexByRay (origin, dir); + if (g_qeglobals.d_num_move_points) + { + drag_ok = true; + return; + } + } + + if (g_qeglobals.d_select_mode == sel_edge) + { + SelectEdgeByRay (origin, dir); + if (g_qeglobals.d_num_move_points) + { + drag_ok = true; + return; + } + } + + + // + // check for direct hit first + // + t = Test_Ray (origin, dir, true); + if (t.selected) + { + drag_ok = true; + + if (buttons == (MK_LBUTTON|MK_CONTROL) ) + { + Sys_Printf ("Shear dragging face\n"); + Brush_SelectFaceForDragging (t.brush, t.face, true); + } + else if (buttons == (MK_LBUTTON|MK_CONTROL|MK_SHIFT) ) + { + Sys_Printf ("Sticky dragging brush\n"); + for (f=t.brush->brush_faces ; f ; f=f->next) + Brush_SelectFaceForDragging (t.brush, f, false); + } + else + Sys_Printf ("Dragging entire selection\n"); + + return; + } + + if (g_qeglobals.d_select_mode == sel_vertex || g_qeglobals.d_select_mode == sel_edge) + return; + + // + // check for side hit + // + // multiple brushes selected? + if (selected_brushes.next->next != &selected_brushes) + { + // yes, special handling + bool bOK = (g_PrefsDlg.m_bALTEdge) ? (static_cast(::GetAsyncKeyState(VK_MENU))) : true; + if (bOK) + { + for (brush_t* pBrush = selected_brushes.next ; pBrush != &selected_brushes ; pBrush = pBrush->next) + { + if (buttons & MK_CONTROL) + Brush_SideSelect (pBrush, origin, dir, true); + else + Brush_SideSelect (pBrush, origin, dir, false); + } + } + else + { + Sys_Printf ("press ALT to drag multiple edges\n"); + return; + } + + } + else + { + // single select.. trying to drag fixed entities handle themselves and just move + if (buttons & MK_CONTROL) + Brush_SideSelect (selected_brushes.next, origin, dir, true); + else + Brush_SideSelect (selected_brushes.next, origin, dir, false); + } + Sys_Printf ("Side stretch\n"); + drag_ok = true; +} + +entity_t *peLink; + +void UpdateTarget(vec3_t origin, vec3_t dir) +{ + trace_t t; + entity_t *pe; + int i; + char sz[128]; + + t = Test_Ray (origin, dir, 0); + + if (!t.brush) + return; + + pe = t.brush->owner; + + if (pe == NULL) + return; + + // is this the first? + if (peLink != NULL) + { + + // Get the target id from out current target + // if there is no id, make one + + i = IntForKey(pe, "target"); + if (i <= 0) + { + i = GetUniqueTargetId(1); + sprintf(sz, "%d", i); + + SetKeyValue(pe, "target", sz); + } + + // set the target # into our src + + sprintf(sz, "%d", i); + SetKeyValue(peLink, "targetname", sz); + + Sys_UpdateWindows(W_ENTITY); + + } + + // promote the target to the src + + peLink = pe; + +} + +/* +=========== +Drag_Begin +=========== +*/ +void Drag_Begin (int x, int y, int buttons, + vec3_t xaxis, vec3_t yaxis, + vec3_t origin, vec3_t dir) +{ + trace_t t; + + drag_ok = false; + VectorCopy (vec3_origin, pressdelta); + VectorCopy (vec3_origin, vPressStart); + + drag_first = true; + peLink = NULL; + + // shift LBUTTON = select entire brush + if (buttons == (MK_LBUTTON | MK_SHIFT) && g_qeglobals.d_select_mode != sel_curvepoint) + { + int nFlag = (static_cast(::GetAsyncKeyState(VK_MENU))) ? SF_CYCLE : 0; + if (dir[0] == 0 || dir[1] == 0 || dir[2] == 0) // extremely low chance of this happening from camera + Select_Ray (origin, dir, nFlag | SF_ENTITIES_FIRST); // hack for XY + else + Select_Ray (origin, dir, nFlag); + return; + } + + // ctrl-shift LBUTTON = select single face + if (buttons == (MK_LBUTTON | MK_CONTROL | MK_SHIFT) && g_qeglobals.d_select_mode != sel_curvepoint) + { + Select_Deselect (); + Select_Ray (origin, dir, SF_SINGLEFACE); + return; + } + + // LBUTTON + all other modifiers = manipulate selection + if (buttons & MK_LBUTTON) + { + Drag_Setup (x, y, buttons, xaxis, yaxis, origin, dir); + return; + } + + int nMouseButton = g_PrefsDlg.m_nMouseButtons == 2 ? MK_RBUTTON : MK_MBUTTON; + // middle button = grab texture + if (buttons == nMouseButton) + { + t = Test_Ray (origin, dir, false); + if (t.face) + { + g_qeglobals.d_new_brush_bottom_z = t.brush->mins[2]; + g_qeglobals.d_new_brush_top_z = t.brush->maxs[2]; + Texture_SetTexture (&t.face->texdef); + UpdateSurfaceDialog(); + UpdatePatchInspector(); + } + else + Sys_Printf ("Did not select a texture\n"); + return; + } + + // ctrl-middle button = set entire brush to texture + if (buttons == (nMouseButton|MK_CONTROL) ) + { + t = Test_Ray (origin, dir, false); + if (t.brush) + { + if (t.brush->brush_faces->texdef.name[0] == '(') + Sys_Printf ("Can't change an entity texture\n"); + else + { + Brush_SetTexture (t.brush, &g_qeglobals.d_texturewin.texdef, false, false); + Sys_UpdateWindows (W_ALL); + } + } + else + Sys_Printf ("Didn't hit a btrush\n"); + return; + } + + // ctrl-shift-middle button = set single face to texture + if (buttons == (nMouseButton|MK_SHIFT|MK_CONTROL) ) + { + t = Test_Ray (origin, dir, false); + if (t.brush) + { + if (t.brush->brush_faces->texdef.name[0] == '(') + Sys_Printf ("Can't change an entity texture\n"); + else + { + SetFaceTexdef (t.brush, t.face, &g_qeglobals.d_texturewin.texdef, false, false); + Brush_Build( t.brush ); + Sys_UpdateWindows (W_ALL); + } + } + else + Sys_Printf ("Didn't hit a btrush\n"); + return; + } + + // shift-middle = (if light) + // set face texture info (err...whatever), + // else + // set brush to texture but preserve any system faces + // + if (buttons == (nMouseButton | MK_SHIFT)) + { + Sys_Printf("Set brush face texture info\n"); + t = Test_Ray (origin, dir, false); + if (t.brush) + { + if (t.brush->brush_faces->texdef.name[0] == '(') + { + if (strcmpi(t.brush->owner->eclass->name, "light") == 0) + { + CString strBuff; + qtexture_t* pTex = Texture_ForName(g_qeglobals.d_texturewin.texdef.name); + if (pTex) + { + vec3_t vColor; + VectorCopy(pTex->color, vColor); + + float fLargest = 0.0f; + for (int i = 0; i < 3; i++) + { + if (vColor[i] > fLargest) + fLargest = vColor[i]; + } + + if (fLargest == 0.0f) + { + vColor[0] = vColor[1] = vColor[2] = 1.0f; + } + else + { + float fScale = 1.0f / fLargest; + for (int i = 0; i < 3; i++) + { + vColor[i] *= fScale; + } + } + strBuff.Format("%f %f %f",pTex->color[0], pTex->color[1], pTex->color[2]); + SetKeyValue(t.brush->owner, "_color", strBuff.GetBuffer(0)); + Sys_UpdateWindows (W_ALL); + } + } + else + { + Sys_Printf ("Can't select an entity brush face\n"); + } + } + else + { + Brush_SetTexture (t.brush, &g_qeglobals.d_texturewin.texdef, false, true); + Sys_UpdateWindows (W_ALL); + } + } + else + Sys_Printf ("Didn't hit a brush\n"); + return; + } + +} + + +// +//=========== +//MoveSelection +//=========== +// +void MoveSelection (vec3_t move) +{ + int i; + brush_t *b; + CString strStatus; + vec3_t vTemp, vTemp2; + + if (!move[0] && !move[1] && !move[2]) + return; + + if (g_pParentWnd->ActiveXY()->RotateMode() || g_bPatchBendMode) + { + float fDeg = -move[2]; + float fAdj = move[2]; + int nAxis = 0; + if (g_pParentWnd->ActiveXY()->GetViewType() == XY) + { + fDeg = -move[1]; + fAdj = move[1]; + nAxis = 2; + } + else + if (g_pParentWnd->ActiveXY()->GetViewType() == XZ) + { + fDeg = move[2]; + fAdj = move[2]; + nAxis = 1; + } + else + nAxis = 0; + + g_pParentWnd->ActiveXY()->Rotation()[nAxis] += fAdj; + strStatus.Format("%s x:: %.1f y:: %.1f z:: %.1f", (g_bPatchBendMode) ? "Bend angle" : "Rotation", g_pParentWnd->ActiveXY()->Rotation()[0], g_pParentWnd->ActiveXY()->Rotation()[1], g_pParentWnd->ActiveXY()->Rotation()[2]); + g_pParentWnd->SetStatusText(2, strStatus); + + if (g_bPatchBendMode) + { + Patch_SelectBendNormal(); + Select_RotateAxis(nAxis, fDeg*2, false, true); + Patch_SelectBendAxis(); + Select_RotateAxis(nAxis, fDeg, false, true); + } + else + { + Select_RotateAxis(nAxis, fDeg, false, true); + } + return; + } + + if (g_pParentWnd->ActiveXY()->ScaleMode()) + { + vec3_t v; + v[0] = v[1] = v[2] = 1.0; + if (move[1] > 0) + { + v[0] = 1.1; + v[1] = 1.1; + v[2] = 1.1; + } + else + if (move[1] < 0) + { + v[0] = 0.9; + v[1] = 0.9; + v[2] = 0.9; + } + + Select_Scale((g_nScaleHow & SCALE_X) ? v[0] : 1.0, + (g_nScaleHow & SCALE_Y) ? v[1] : 1.0, + (g_nScaleHow & SCALE_Z) ? v[2] : 1.0); + Sys_UpdateWindows (W_ALL); + return; + } + + + vec3_t vDistance; + VectorSubtract(pressdelta, vPressStart, vDistance); + strStatus.Format("Distance x: %.1f y: %.1f z: %.1f", vDistance[0], vDistance[1], vDistance[2]); + g_pParentWnd->SetStatusText(3, strStatus); + + // + // dragging only a part of the selection + // + + // this is fairly crappy way to deal with curvepoint and area selection + // but it touches the smallest amount of code this way + // + if (g_qeglobals.d_num_move_points || g_qeglobals.d_select_mode == sel_area) + { + if (g_qeglobals.d_select_mode == sel_area) + { + VectorAdd(g_qeglobals.d_vAreaBR, move, g_qeglobals.d_vAreaBR); + return; + } + + if (g_qeglobals.d_select_mode == sel_curvepoint) + { + Patch_UpdateSelected(move); + return; + } + else + { + for (i=0 ; inext) + { + VectorCopy(b->maxs, vTemp); + VectorSubtract(vTemp, b->mins, vTemp); + Brush_Build( b ); + for (i=0 ; i<3 ; i++) + if (b->mins[i] > b->maxs[i] + || b->maxs[i] - b->mins[i] > WORLD_SIZE) + break; // dragged backwards or fucked up + if (i != 3) + break; + if (b->patchBrush) + { + VectorCopy(b->maxs, vTemp2); + VectorSubtract(vTemp2, b->mins, vTemp2); + VectorSubtract(vTemp2, vTemp, vTemp2); + if (!Patch_DragScale(b->nPatchID, vTemp2, move)) + { + b = NULL; + break; + } + } + } + + // if any of the brushes were crushed out of existance + // calcel the entire move + if (b != &selected_brushes) + { + Sys_Printf ("Brush dragged backwards, move canceled\n"); + for (i=0 ; inext) + Brush_Build( b ); + } + + } + else + { + // + // if there are lots of brushes selected, just translate instead + // of rebuilding the brushes + // + if (drag_yvec[2] == 0 && selected_brushes.next->next != &selected_brushes) + { + Select_Move (move); + //VectorAdd (g_qeglobals.d_select_translate, move, g_qeglobals.d_select_translate); + } + else + { + Select_Move (move); + } + } +} + +/* +=========== +Drag_MouseMoved +=========== +*/ +void Drag_MouseMoved (int x, int y, int buttons) +{ + vec3_t move, delta; + int i; + + if (!buttons) + { + drag_ok = false; + return; + } + if (!drag_ok) + return; + + // clear along one axis + if (buttons & MK_SHIFT) + { + drag_first = false; + if (abs(x-pressx) > abs(y-pressy)) + y = pressy; + else + x = pressx; + } + + + for (i=0 ; i<3 ; i++) + { + move[i] = drag_xvec[i]*(x - pressx) + drag_yvec[i]*(y - pressy); + move[i] = floor(move[i]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize; + } + + VectorSubtract (move, pressdelta, delta); + VectorCopy (move, pressdelta); + + MoveSelection (delta); + +} + +/* +=========== +Drag_MouseUp +=========== +*/ +void Drag_MouseUp (int nButtons) +{ + Sys_Status ("drag completed.", 0); + + if (g_qeglobals.d_select_mode == sel_area) + { + Patch_SelectAreaPoints(); + g_qeglobals.d_select_mode = sel_curvepoint; + Sys_UpdateWindows (W_ALL); + } + + if (g_qeglobals.d_select_translate[0] || g_qeglobals.d_select_translate[1] || g_qeglobals.d_select_translate[2]) + { + Select_Move (g_qeglobals.d_select_translate); + VectorCopy (vec3_origin, g_qeglobals.d_select_translate); + Sys_UpdateWindows (W_CAMERA); + } + + g_pParentWnd->SetStatusText(3, ""); + +} diff --git a/utils/Radiant/eclass.cpp b/utils/Radiant/eclass.cpp new file mode 100644 index 0000000..114a4ce --- /dev/null +++ b/utils/Radiant/eclass.cpp @@ -0,0 +1,947 @@ +#include "stdafx.h" +#include "qe3.h" +#include "io.h" +#include "inc.h" +#include "oddbits.h" +#include "..\libs\pakstuff.h" + +eclass_t *eclass = NULL; +eclass_t *eclass_bad = NULL; +char eclass_directory[1024]; + +// md3 cache for misc_models +eclass_t *g_md3Cache = NULL; + +/* + +the classname, color triple, and bounding box are parsed out of comments +A ? size means take the exact brush size. + +/*QUAKED (0 0 0) ? +/*QUAKED (0 0 0) (-8 -8 -8) (8 8 8) + +Flag names can follow the size description: + +/*QUAKED func_door (0 .5 .8) ? START_OPEN STONE_SOUND DOOR_DONT_LINK GOLD_KEY SILVER_KEY + +*/ + +void CleanEntityList(eclass_t *&pList) +{ + while (pList) + { + eclass_t* pTemp = pList->next; + + entitymodel *model = pList->model; + while (model != NULL) + { + delete []model->pTriList; + model = model->pNext; + } + + if (pList->modelpath) + free(pList->modelpath); + if (pList->skinpath) // PGM + free(pList->skinpath); // PGM + + free(pList->name); + free(pList->comments); + free(pList); + pList = pTemp; + } + + pList = NULL; + +} + + +void CleanUpEntities() +{ + CleanEntityList(eclass); + CleanEntityList(g_md3Cache); +/* + while (eclass) + { + eclass_t* pTemp = eclass->next; + delete []eclass->pTriList; + + if (eclass->modelpath) + free(eclass->modelpath); + if (eclass->skinpath) // PGM + free(eclass->skinpath); // PGM + + free(eclass->name); + free(eclass->comments); + free(eclass); + eclass = pTemp; + } + + eclass = NULL; +*/ + if (eclass_bad) + { + free(eclass_bad->name); + free(eclass_bad->comments); + free(eclass_bad); + eclass_bad = NULL; + } +} + +void ExtendBounds(vec3_t v, vec3_t &vMin, vec3_t &vMax) +{ + for (int i = 0 ;i < 3 ;i++) + { + vec_t f = v[i]; + + if (f < vMin[i]) + { + vMin[i] = f; + } + + if (f > vMax[i]) + { + vMax[i] = f; + } + } +} + + +CString strBadFiles; + +// FIXME: this code is a TOTAL clusterfuck +// +// update, returns NZ if model load occured +// +entity_t *gEntityToSetBoundsOf=0; +int LoadModel(const char *pLocation, eclass_t *e, vec3_t &vMin, vec3_t &vMax, entitymodel *&pModel, const char *pSkin) +{ + // this assumes a path only and uses tris.md2 + // for the model and skin.pcx for the skin + char cPath[1024]; + char cSkin[1024]; + char cFullLocation[1024]; + //struct _finddata_t fileinfo; + + if (strBadFiles.Find(va("%s\n",pLocation))>=0) + return 0; + + vMin[0] = vMin[1] = vMin[2] = MAX_WORLD_COORD; + vMax[0] = vMax[1] = vMax[2] = MIN_WORLD_COORD; + + bool bMD3 = false; + + strcpy( cFullLocation, pLocation ); + if (strstr(pLocation, ".md3")) + { + bMD3 = true; + } + else if (strstr(pLocation, ".md2")) + { + sprintf( cFullLocation, "%stris.md2", pLocation); + } + else + { + ErrorBox(va("Unhandled model type (only md3/md2 supported here):\n\n\"%s\"",pLocation)); + strBadFiles+=va("%s\n",pLocation); + return 0; + } + + sprintf( cPath, "%s/%s", ValueForKey(g_qeglobals.d_project_entity, "basepath"), cFullLocation); + + Sys_Printf("Loading model %s...", cPath); + unsigned char* p = NULL; + bool bOpen = (LoadFile(cPath, reinterpret_cast(&p)) > 0); + if (!bOpen) + { + Sys_Printf(" failed. Trying PAK file..."); +// sprintf (cPath, "%stris.md2", pLocation); + strcpy (cPath, cFullLocation); + bOpen = (PakLoadAnyFile(cPath, reinterpret_cast(&p)) > 0); + } + + if (!bOpen) + { +// if (GetYesNo("Ignore errors from this file for the rest of this edit session?")) + InfoBox(va("Unable to open the file \"%s\",\n\n" + "( Further errors from this file will be ignored during the rest of this edit session )",cPath) + ); + { + strBadFiles+=va("%s\n",pLocation); + } + } + + if (bOpen) + { + Sys_Printf(" successful.\n"); + + if (bMD3) + { + md3Header_t header; + md3Surface_t *pSurface; + header = *(md3Header_t *)p; + if (pSkin != NULL) + { + strcpy(cSkin, pSkin); + } + else + { + cSkin[0] = '\0'; + } + int n = header.numFrames; + pSurface = (md3Surface_t *) (p + header.ofsSurfaces); + for (int z = 0; z < header.numSurfaces; z++ ) + { + int nTris = pSurface->numTriangles; + + //unsigned char* pTris = reinterpret_cast(pSurface); + //pTris += pSurface->ofsTriangles; + + if (nTris > 0) + { + int nStart = 0; + if (pModel->pTriList == NULL) + { + pModel->nModelPosition = 0; + pModel->pTriList = new trimodel[nTris]; + pModel->nTriCount = nTris; + } + else + { + // already have one so we need to reallocate + int nNewCount = pModel->nTriCount + nTris; + trimodel* pNewModels = new trimodel[nNewCount]; + for (int i = 0; i < pModel->nTriCount; i++) + { + memcpy(&pNewModels[i], &pModel->pTriList[i], sizeof(trimodel)); + } + nStart = pModel->nTriCount; + pModel->nTriCount = nNewCount; + //nTris = nNewCount; + delete [] pModel->pTriList; + pModel->pTriList = pNewModels; + } + + md3Triangle_t *pTris = reinterpret_cast((reinterpret_cast(pSurface) + pSurface->ofsTriangles)); + md3XyzNormal_t *pXyz = reinterpret_cast((reinterpret_cast(pSurface) + pSurface->ofsXyzNormals)); + if (e->nFrame < pSurface->numFrames) + { + pXyz += (e->nFrame * pSurface->numVerts); + } + + md3St_t *pST = reinterpret_cast((reinterpret_cast(pSurface) + pSurface->ofsSt)); + + for (int i = 0; i < nTris; i++) + { + for (int k = 0; k < 3; k ++) + { + for (int j = 0; j < 3; j++) + { + //e->pTriList[i].v[k][j] = (f->verts[tri.index_xyz[k]].v[j] * f->scale[j] + f->translate[j]); + pModel->pTriList[nStart].v[k][j] = pXyz[pTris[i].indexes[k]].xyz[j] * MD3_XYZ_SCALE; + } + pModel->pTriList[nStart].st[k][0] = pST[pTris[i].indexes[k]].st[0]; + pModel->pTriList[nStart].st[k][1] = pST[pTris[i].indexes[k]].st[1]; + ExtendBounds (pModel->pTriList[nStart].v[k], vMin, vMax); + } + nStart++; + } + + } + + md3Shader_t *pShader = reinterpret_cast((reinterpret_cast(pSurface) + pSurface->ofsShaders)); +// sprintf (cPath, "%s/%s", ValueForKey(g_qeglobals.d_project_entity, "basepath"), pShader->name); +// strlwr(cPath); + pModel->nTextureBind = Texture_LoadSkin(/*cPath*/pShader->name, &pModel->nSkinWidth, &pModel->nSkinHeight, ValueForKey(g_qeglobals.d_project_entity, "basepath")); + pSurface = (md3Surface_t *) ((( char * ) pSurface) + pSurface->ofsEnd); + pModel->pNext = reinterpret_cast(qmalloc(sizeof(entitymodel_t))); + pModel = pModel->pNext; + } + } + else + { + + dmdl_t model; + daliasframe_t *f; + unsigned char* pTris = p; + dstvert_t *pST = NULL; + int nTris = 0; + + // grab model params + memcpy(&model, p, sizeof(dmdl_t)); + f = (daliasframe_t*)(p + model.ofs_frames); + pTris += model.ofs_tris; + pST = reinterpret_cast(p + model.ofs_st); + nTris = model.num_tris; + + if(pSkin) + { + strcpy (cSkin, pSkin); + if ((cSkin[strlen(cSkin)-1] == '\\') || (cSkin[strlen(cSkin)-1] == '/')) + strcat(cSkin, "skin.pcx\0"); + } + else + { + strcpy(cSkin, (char *)(p + model.ofs_skins)); + } + +// sprintf (cPath, "%s/%s", ValueForKey(g_qeglobals.d_project_entity, "basepath"), cSkin); +// strlwr(cPath); + pModel->nTextureBind = Texture_LoadSkin(/*cPath*/cSkin, &pModel->nSkinWidth, &pModel->nSkinHeight, ValueForKey(g_qeglobals.d_project_entity, "basepath")); + + int nStart = 0; + if (pModel->pTriList == NULL) + { + pModel->nModelPosition = 0; + pModel->pTriList = new trimodel[nTris]; + pModel->nTriCount = nTris; + } + else + { + // already have one so we need to reallocate + int nNewCount = pModel->nTriCount + nTris; + trimodel* pNewModels = new trimodel[nNewCount]; + for (int i = 0; i < pModel->nTriCount; i++) + { + memcpy(&pNewModels[i], &pModel->pTriList[i], sizeof(trimodel)); + } + nStart = pModel->nTriCount; + pModel->nTriCount = nNewCount; + nTris = nNewCount; + delete [] pModel->pTriList; + pModel->pTriList = pNewModels; + } + + for (int i = nStart; i < nTris; i++) + { + dtriangle_t tri; + memcpy(&tri, pTris, sizeof(dtriangle_t)); + for (int k = 0; k < 3; k ++) + { + for (int j = 0; j < 3; j++) + { + pModel->pTriList[i].v[k][j] = (f->verts[tri.index_xyz[k]].v[j] * f->scale[j] + f->translate[j]); + } + + pModel->pTriList[i].st[k][0] = pST[tri.index_st[k]].s / pModel->nSkinWidth; + pModel->pTriList[i].st[k][1] = pST[tri.index_st[k]].t / pModel->nSkinHeight;; + ExtendBounds (pModel->pTriList[i].v[k], vMin, vMax); + } + pTris += sizeof(dtriangle_t); + } + } + free(p); + } + else + { + Sys_Printf(" failed.\n"); + } + +#if 0 + if (pModel->pTriList != NULL && pModel->nTriCount > 0 && !bMD3) + { + if(fabs(vMin[2]) < ((vMax[2]-vMin[2]) / 10.0)) // > 90% above 0 point. + pModel->nModelPosition = 1; +// sprintf (cPath, "%s/%sskin.pcx", ValueForKey(g_qeglobals.d_project_entity, "basepath"), pLocation); + sprintf (cPath, "%s/%s", ValueForKey(g_qeglobals.d_project_entity, "basepath"), cSkin); + pModel->nTextureBind = Texture_LoadSkin(cPath, &pModel->nSkinWidth, &pModel->nSkinHeight); + if (pModel->nTextureBind == -1) + { +// sprintf (cPath, "%sskin.pcx", pLocation); + strcpy (cPath, cSkin); + pModel->nTextureBind = Texture_LoadSkin(cPath, &pModel->nSkinWidth, &pModel->nSkinHeight); + } + } +#endif + + return bOpen; +} + +// in QUAKE3 build this will remove the found command from the comment block as well.. +// +void setSpecialLoad(eclass_t *e, const char* pWhat, char*& p) +{ + CString str = e->comments; + int n = str.Find(pWhat); + if (n >= 0) + { + char* pText = e->comments + n + strlen(pWhat); + if (*pText == '\"') + pText++; + + str = ""; + while (*pText != '\"' && *pText != '\0') + { + str += *pText; + pText++; + } + + if (str.GetLength() > 0) + { + p = strdup(str); + //--LoadModel(str, e); +#ifdef QUAKE3 + CString strNewComment = e->comments; + strNewComment.Replace(va("%s\"%s\"",pWhat,str),""); // lose the command we've just processed from the comment + if (strNewComment[0]==0x0D) + strNewComment=strNewComment.Mid(1); // lose leading CR if nec + if (strNewComment[0]==0x0A) + strNewComment=strNewComment.Mid(1); // lose leading CR if nec + free(e->comments); + e->comments = (char *)qmalloc(strNewComment.GetLength()+1); + strcpy(e->comments,(LPCSTR)strNewComment); +#endif + + } + } +} + +char *debugname; + +eclass_t *Eclass_InitFromText (char *text) +{ + char *t; + int len; + int r, i; + char parms[256], *p; + eclass_t *e; + char color[128]; + + e = (eclass_t*)qmalloc(sizeof(*e)); + memset (e, 0, sizeof(*e)); + + text += strlen("/*QUAKED "); + +// grab the name + text = COM_Parse (text); + e->name = (char*)qmalloc (strlen(com_token)+1); + strcpy (e->name, com_token); + debugname = e->name; + +// grab the color, reformat as texture name + r = sscanf (text," (%f %f %f)", &e->color[0], &e->color[1], &e->color[2]); + if (r != 3) + return e; + sprintf (color, "(%f %f %f)", e->color[0], e->color[1], e->color[2]); + strcpy (e->texdef.name, color); + + while (*text != ')') + { + if (!*text) + return e; + text++; + } + text++; + +// get the size + text = COM_Parse (text); + if (com_token[0] == '(') + { // parse the size as two vectors + e->fixedsize = true; + r = sscanf (text,"%f %f %f) (%f %f %f)", &e->mins[0], &e->mins[1], &e->mins[2], + &e->maxs[0], &e->maxs[1], &e->maxs[2]); + if (r != 6) + return e; + + for (i=0 ; i<2 ; i++) + { + while (*text != ')') + { + if (!*text) + return e; + text++; + } + text++; + } + } + else + { // use the brushes + } + +// get the flags + + +// copy to the first /n + p = parms; + while (*text && *text != '\n') + *p++ = *text++; + *p = 0; + text++; + +// any remaining words are parm flags + + strcpy(e->flagnames[NOT_FLAGS_START+3], ""); // SOF will replace with deathmatch, Trek is user-defineable + p = parms; + for (i=0 ; iflagnames[i], com_token); + } + +#ifdef SOF + strcpy(e->flagnames[NOT_FLAGS_START+0], "!Easy"); + strcpy(e->flagnames[NOT_FLAGS_START+1], "!Medium"); + strcpy(e->flagnames[NOT_FLAGS_START+2], "!Hard"); + strcpy(e->flagnames[NOT_FLAGS_START+3], "!DeathMatch"); + strcpy(e->flagnames[NOT_FLAGS_START+4], "!Co-Op"); +#else + strcpy(e->flagnames[NOT_FLAGS_START+0], "!Easy"); + strcpy(e->flagnames[NOT_FLAGS_START+1], "!Medium"); + strcpy(e->flagnames[NOT_FLAGS_START+2], "!Hard"); +#endif + +// find the length until close comment + for (t=text ; t[0] && !(t[0]=='*' && t[1]=='/') ; t++) + ; + +// copy the comment block out + len = t-text; + e->comments = (char*)qmalloc (len+1); + memcpy (e->comments, text, len); +#if 0 + for (i=0 ; icomments[i] = '\r'; + else + e->comments[i] = text[i]; +#endif + e->comments[len] = 0; + + +// These appear to be directives used by Quake2, and neither Voyager nor SoF use them (but I'll leave 'em in) + + setSpecialLoad(e, "model=", e->modelpath); + setSpecialLoad(e, "skin=", e->skinpath); + char *pFrame = NULL; + setSpecialLoad(e, "frame=", pFrame); + if (pFrame != NULL) + { + e->nFrame = atoi(pFrame); + } + + if(!e->skinpath) + setSpecialLoad(e, "texture=", e->skinpath); + +// New Voyager directives... +// +#ifdef QUAKE3 + setSpecialLoad(e, "#MODELNAME=", e->psQuakEd_MODELNAME); +#endif + + return e; +} + +// formerly a qbool return, +// +// now updated so still NZ ret, but 1== hasModel, 2==hasModel because we've just loaded one +int Eclass_hasModel(eclass_t *e, vec3_t &vMin, vec3_t &vMax) +{ + int iReturn = 0; + + if (e->modelpath != NULL) + { + if (e->model == NULL) + { + e->model = reinterpret_cast(qmalloc(sizeof(entitymodel_t))); + } + char *pModelBuff = strdup(e->modelpath); + char *pSkinBuff = NULL; + if (e->skinpath) + { + pSkinBuff = strdup(e->skinpath); + } + + CStringList Models; + CStringList Skins; + char* pToken = strtok(pModelBuff, ";\0"); + while (pToken != NULL) + { + Models.AddTail(pToken); + pToken = strtok(NULL, ";\0"); + } + + if (pSkinBuff != NULL) + { + pToken = strtok(pSkinBuff, ";\0"); + while (pToken != NULL) + { + Skins.AddTail(pToken); + pToken = strtok(NULL, ";\0"); + } + } + + entitymodel *model = e->model; + for (int i = 0; i < Models.GetCount(); i++) + { + char *pSkin = NULL; + if (i < Skins.GetCount()) + { + pSkin = Skins.GetAt(Skins.FindIndex(i)).GetBuffer(0); + } + if (LoadModel(Models.GetAt(Models.FindIndex(i)), e, vMin, vMax, model, pSkin)) + { + iReturn = 2; + } + model->pNext = reinterpret_cast(qmalloc(sizeof(entitymodel_t))); + model = model->pNext; + } + + // at this point vMin and vMax contain the min max of the model + // which needs to be centered at origin 0, 0, 0 + VectorSnap(vMin); + VectorSnap(vMax); + vec3_t vTemp; + VectorAdd(vMin, vMax, vTemp); + VectorScale(vTemp, 0.5, vTemp); + model = e->model; +#if 0 + while (model != NULL) + { + for (i = 0; i < model->nTriCount; i++) + { + for (int j = 0; j < 3; j++) + { + VectorSubtract(model->pTriList[i].v[j], vTemp, model->pTriList[i].v[j]); + } + } + model = model->pNext; + } +#endif + free(pModelBuff); + free(e->modelpath); + e->modelpath = NULL; + + if(e->skinpath) + { + free(e->skinpath); + e->skinpath = NULL; + free(pSkinBuff); + } + + } + return ((iReturn == 2)?2:(e->model != NULL && e->model->nTriCount > 0)?1:0); // :-) +} + + +void EClass_InsertSortedList(eclass_t *&pList, eclass_t *e) +{ + eclass_t *s; + + if (!pList) + { + pList = e; + return; + } + + + s = pList; + if (stricmp (e->name, s->name) < 0) + { + e->next = s; + pList = e; + return; + } + + do + { + if (!s->next || stricmp (e->name, s->next->name) < 0) + { + e->next = s->next; + s->next = e; + return; + } + s=s->next; + } while (1); +} + +/* +================= +Eclass_InsertAlphabetized +================= +*/ +void Eclass_InsertAlphabetized (eclass_t *e) +{ +#if 1 + EClass_InsertSortedList(eclass, e); +#else + eclass_t *s; + + if (!eclass) + { + eclass = e; + return; + } + + + s = eclass; + if (stricmp (e->name, s->name) < 0) + { + e->next = s; + eclass = e; + return; + } + + do + { + if (!s->next || stricmp (e->name, s->next->name) < 0) + { + e->next = s->next; + s->next = e; + return; + } + s=s->next; + } while (1); +#endif +} + + +/* +================= +Eclass_ScanFile +================= +*/ + +//#ifdef BUILD_LIST +extern bool g_bBuildList; +CString strDefFile; +//#endif +void Eclass_ScanFile (char *filename) +{ + int size; + char *data; + eclass_t *e; + int i; + char temp[1024]; + + QE_ConvertDOSToUnixName( temp, filename ); + + Sys_Printf ("ScanFile: %s\n", temp); + + // BUG + size = LoadFile (filename, (void**)&data); + for (i=0 ; inext) + if (!strcmp (name, e->name)) + return e; + + // create a new class for it + if (has_brushes) + { + sprintf (init, "/*QUAKED %s (0 0.5 0) ?\nNot found in source.\n", name); + e = Eclass_InitFromText (init); + } + else + { + sprintf (init, "/*QUAKED %s (0 0.5 0) (-8 -8 -8) (8 8 8)\nNot found in source.\n", name); + e = Eclass_InitFromText (init); + } + + Eclass_InsertAlphabetized (e); + + return e; +} + + +// now called from function below... +// +eclass_t* GetCachedModel_ACTUAL(entity_t *pEntity, const char *pName, vec3_t &vMin, vec3_t &vMax) +{ + + eclass_t *e = NULL; + if (pName == NULL || strlen(pName) == 0) + { + return NULL; + } + + for (e = g_md3Cache; e ; e = e->next) + { + if (!strcmp (pName, e->name)) + { + pEntity->md3Class = e; + VectorCopy(e->mins, vMin); + VectorCopy(e->maxs, vMax); + return e; + } + } + + e = (eclass_t*)qmalloc(sizeof(*e)); + memset (e, 0, sizeof(*e)); + e->name = strdup(pName); + e->modelpath = strdup(pName); + e->skinpath = strdup(pName); + char *p = strstr(e->skinpath, ".md3"); + if (p != NULL) + { + p++; + strncpy(p, "tga", 3); + } + else + { + free(e->skinpath); + e->skinpath = NULL; + } + + if (stricmp(pEntity->eclass->name, "misc_model_breakable")) + { + e->color[0] = 0.95; + e->color[2] = 0.5; + } + else + { + e->color[0] = e->color[2] = 0.85; + } + + if (Eclass_hasModel(e, vMin, vMax)) + { + EClass_InsertSortedList(g_md3Cache, e); + VectorCopy(vMin, e->mins); + VectorCopy(vMax, e->maxs); + pEntity->md3Class = e; + return e; + } + + return NULL; +} + + +eclass_t* GetCachedModel(entity_t *pEntity, const char *pName, vec3_t &vMin, vec3_t &vMax) +{ + eclass_t* pEclass = GetCachedModel_ACTUAL(pEntity, pName, vMin, vMax); + + if (pEclass) + { +#ifdef QUAKE3 + if (gEntityToSetBoundsOf == pEntity) + { + if (strnicmp(gEntityToSetBoundsOf->eclass->name, "misc_model_",11) == 0) + { + // now record the vMin, vMax of the model this ent uses into itself so that by the time + // the game loads it can read the real model bounds at spawn time and overwrite the + // default of 16,16,16 that it would otherwise have + // + // ... unless those keys already exist (so they might be special hand-entered) + // + char *_p = ValueForKey(gEntityToSetBoundsOf, "mins"); + char *_p2= ValueForKey(gEntityToSetBoundsOf, "maxs"); + char *_p3= ValueForKey(gEntityToSetBoundsOf, sKEYFIELD_AUTOBOUND); + // + // if either key is missing then update both... (or if they do exist, but were only set by this code in the first place and therefore can be overwritten) + // + if ( ((strlen(_p) == 0) || (strlen(_p2) == 0)) || strlen(_p3) ) + { + SetKeyValue (gEntityToSetBoundsOf, sKEYFIELD_AUTOBOUND, "1"); + SetKeyValue (gEntityToSetBoundsOf, "mins", va("%i %i %i", (int)vMin[0], (int)vMin[1], (int)vMin[2])); + SetKeyValue (gEntityToSetBoundsOf, "maxs", va("%i %i %i", (int)vMax[0], (int)vMax[1], (int)vMax[2])); + } + + if (edit_entity == gEntityToSetBoundsOf) // oh jeez... + { + // SetKeyValuePairs(); + // g_pParentWnd->GetXYWnd()->SetFocus(); + } + } + gEntityToSetBoundsOf = NULL; + } +#endif + } + + return pEclass; +} + diff --git a/utils/Radiant/editwnd.cpp b/utils/Radiant/editwnd.cpp new file mode 100644 index 0000000..4c9a595 --- /dev/null +++ b/utils/Radiant/editwnd.cpp @@ -0,0 +1,40 @@ +// EditWnd.cpp : implementation file +// + +#include "stdafx.h" +#include "Radiant.h" +#include "EditWnd.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CEditWnd +IMPLEMENT_DYNCREATE(CEditWnd, CWnd); + +CEditWnd::CEditWnd() +{ +} + +CEditWnd::~CEditWnd() +{ +} + + +BEGIN_MESSAGE_MAP(CEditWnd, CEdit) + //{{AFX_MSG_MAP(CEditWnd) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CEditWnd message handlers + +BOOL CEditWnd::PreCreateWindow(CREATESTRUCT& cs) +{ + cs.style = WS_CHILD | WS_VSCROLL | ES_AUTOHSCROLL | ES_MULTILINE | WS_VISIBLE; + cs.lpszClass = "EDIT"; + return CEdit::PreCreateWindow(cs); +} diff --git a/utils/Radiant/editwnd.h b/utils/Radiant/editwnd.h new file mode 100644 index 0000000..e99fec7 --- /dev/null +++ b/utils/Radiant/editwnd.h @@ -0,0 +1,50 @@ +#if !defined(AFX_EDITWND_H__279AAE22_78C5_11D1_B53C_00AA00A410FC__INCLUDED_) +#define AFX_EDITWND_H__279AAE22_78C5_11D1_B53C_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// EditWnd.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CEditWnd window + +class CEditWnd : public CEdit +{ + DECLARE_DYNCREATE(CEditWnd); +// Construction +public: + CEditWnd(); + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CEditWnd) + protected: + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CEditWnd(); + + // Generated message map functions +protected: + //{{AFX_MSG(CEditWnd) + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_EDITWND_H__279AAE22_78C5_11D1_B53C_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/entity.cpp b/utils/Radiant/entity.cpp new file mode 100644 index 0000000..67fad6b --- /dev/null +++ b/utils/Radiant/entity.cpp @@ -0,0 +1,968 @@ +#include "stdafx.h" +#include "qe3.h" +#include "oddbits.h" +#include + +int GetNumKeys(entity_t *ent) +{ + int iCount = 0; + for (epair_t* ep=ent->epairs ; ep ; ep=ep->next) + { + iCount++; + } + + return iCount; +} + +char *GetKeyString(entity_t *ent, int iIndex) +{ + for (epair_t* ep=ent->epairs ; ep ; ep=ep->next) + { + if (!iIndex--) + return ep->key; + } + + assert(0); // why do I not find it surprising that this didn't compile until I added the header, ie assert never used? + return NULL; +} + +char *ValueForKey (entity_t *ent, LPCSTR key) +{ + epair_t *ep; + + for (ep=ent->epairs ; ep ; ep=ep->next) + if (!strcmp (ep->key, key) ) + return ep->value; + return ""; +} + +void TrackMD3Angles(entity_t *e, LPCSTR key, LPCSTR value) +{ + if (strcmpi(key, "angle") != 0) + { + return; + } + +#ifdef SOF + if (e->eclass->fixedsize && + ( + (strnicmp(e->eclass->name, "misc_", 5) == 0) || + (strnicmp(e->eclass->name, "light_", 6) == 0) || + (strnicmp(e->eclass->name, "m_", 2) == 0) || + (strnicmp(e->eclass->name, "item_weapon_", 12)== 0) || + (strnicmp(e->eclass->name, "item_ammo_", 10)== 0) + ) + ) +#else + if (e->eclass->fixedsize && strnicmp (e->eclass->name, "misc_model",10) == 0) +#endif + { + float a = FloatForKey (e, "angle"); + float b = atof(value); + if (a != b) + { + vec3_t vAngle; + vAngle[0] = vAngle[1] = 0; + vAngle[2] = -a; + Brush_Rotate(e->brushes.onext, vAngle, e->origin, true); + vAngle[2] = b; + Brush_Rotate(e->brushes.onext, vAngle, e->origin, true); + +#ifdef QUAKE3 + // + // auto assign new bounding box to model? + // + char *_p = ValueForKey(e, "mins"); + char *_p2= ValueForKey(e, "maxs"); + char *_p3= ValueForKey(e, sKEYFIELD_AUTOBOUND); + // + // if either key is missing then update both... (or if they do exist, but were only set by this code in the first place and therefore can be overwritten) + // + if ( ((strlen(_p) == 0) || (strlen(_p2) == 0)) || strlen(_p3) ) + { + SetKeyValue (e, sKEYFIELD_AUTOBOUND, "1"); + + vec3_t vMins,vMaxs; + + VectorSubtract(e->brushes.onext->mins, e->origin, vMins); + VectorSubtract(e->brushes.onext->maxs, e->origin, vMaxs); + + SetKeyValue (e, "mins", va("%i %i %i", (int)vMins[0], (int)vMins[1], (int)vMins[2])); + SetKeyValue (e, "maxs", va("%i %i %i", (int)vMaxs[0], (int)vMaxs[1], (int)vMaxs[2])); + } +#endif + + + } + } +} + +// Scale object bounding box +void TrackMD3Scale(entity_t *e, LPCSTR key, LPCSTR value) +{ + float oldscale,scale; + + if (strcmpi(key, "scale") != 0) + { + return; + } + + if (e->eclass->fixedsize && ((strnicmp(e->eclass->name, "misc_",5) == 0) || + (strnicmp(e->eclass->name, "light_",6) == 0))) + { + oldscale = FloatForKey (e, "scale"); + scale = atof(value); + if (oldscale != scale) // Value unchanged?????? + { + Brush_Scale2(e->eclass,e->brushes.onext, scale,e->origin,true); + + float a = FloatForKey (e, "angle"); + if (a) + { + // Re-rotate bbox + vec3_t vAngle; + vAngle[0] = vAngle[1] = 0; + vAngle[2] = a; + Brush_Rotate(e->brushes.onext, vAngle, e->origin, true); + } + } + } +} + + +// make sure you add any new tracking functions inside the IF, this is important! +// +void SetKeyValue (entity_t *ent, LPCSTR key, LPCSTR value, bool bDoTracking/*=true*/) +{ + epair_t *ep; + + if (ent == NULL) + return; + + if (!key || !key[0]) + return; + + if (ent == world_entity && strcmp(key,sKEYFIELD_GROUPNAME)==0) + return; // dumb to do this + + if (bDoTracking) + { + #ifdef SOF + TrackMD3Scale(ent, key, value); + #endif + + TrackMD3Angles(ent, key, value); + } + + for (ep=ent->epairs ; ep ; ep=ep->next) + { + if (!strcmp (ep->key, key) ) + { + free (ep->value); + ep->value = (char*)qmalloc(strlen(value)+1); + strcpy (ep->value, value); + return; + } + } + ep = (epair_t*)qmalloc (sizeof(*ep)); + ep->next = ent->epairs; + ent->epairs = ep; + ep->key = (char*)qmalloc(strlen(key)+1); + strcpy (ep->key, key); + ep->value = (char*)qmalloc(strlen(value)+1); + strcpy (ep->value, value); +} + +void DeleteKey (entity_t *ent, LPCSTR key) +{ + epair_t **ep, *next; + + ep = &ent->epairs; + while (*ep) + { + next = *ep; + if ( !strcmp (next->key, key) ) + { + *ep = next->next; + free(next->key); + free(next->value); + free(next); + return; + } + ep = &next->next; + } +} + + +float FloatForKey (entity_t *ent, LPCSTR key) +{ + char *k; + + k = ValueForKey (ent, key); + return atof(k); +} + +int IntForKey (entity_t *ent, LPCSTR key) +{ + char *k; + + k = ValueForKey (ent, key); + return atoi(k); +} + +// now returns success/fail bool... +// +bool GetVectorForKey (entity_t *ent, LPCSTR key, vec3_t vec) +{ + char *k; + + k = ValueForKey (ent, key); + int i = sscanf (k, "%f %f %f", &vec[0], &vec[1], &vec[2]); + + return !!strlen(k); +// return !!(i==3); // actually this would probably be more accurate, but I'll leave it as above for now +} + + +/* +=============== +Entity_Free + +Frees the entity and any brushes is has. +The entity is removed from the global entities list. +=============== +*/ +void Entity_Free (entity_t *e) +{ + epair_t *ep, *next; + + while (e->brushes.onext != &e->brushes) + Brush_Free (e->brushes.onext); + + if (e->next) + { + e->next->prev = e->prev; + e->prev->next = e->next; + } + + for (ep = e->epairs ; ep ; ep=next) + { + next = ep->next; + free (ep->key); + free (ep->value); + free (ep); + } + free (e); +} + +/* +================= +ParseEpair +================= +*/ +epair_t *ParseEpair (void) +{ + epair_t *e; + + e = (epair_t*)qmalloc (sizeof(*e)); + + e->key = (char*)qmalloc(strlen(token)+1); + strcpy (e->key, token); + + GetToken (false); + e->value = (char*)qmalloc(strlen(token)+1); + strcpy (e->value, token); + + return e; +} + +/* +================ +Entity_Parse + +If onlypairs is set, the classname info will not +be looked up, and the entity will not be added +to the global list. Used for parsing the project. +================ +*/ +entity_t *Entity_Parse (qboolean onlypairs, brush_t* pList) +{ + entity_t *ent; + eclass_t *e; + brush_t *b; + vec3_t mins, maxs; + epair_t *ep; + qboolean has_brushes; +#ifdef SOF + float scale; +#endif + + if (!GetToken (true)) + return NULL; + + if (strcmp (token, "{") ) + Error ("ParseEntity: { not found"); + + ent = (entity_t*)qmalloc (sizeof(*ent)); + ent->brushes.onext = ent->brushes.oprev = &ent->brushes; + + do + { + if (!GetToken (true)) + { + Warning ("ParseEntity: EOF without closing brace"); + return NULL; + } + if (!strcmp (token, "}") ) + break; + if (!strcmp (token, "{") ) + { + b = Brush_Parse (); + if (b != NULL) + { + b->owner = ent; + + // add to the end of the entity chain + b->onext = &ent->brushes; + b->oprev = ent->brushes.oprev; + ent->brushes.oprev->onext = b; + ent->brushes.oprev = b; + } + else + { + break; + } + + } + else + { + ep = ParseEpair (); + { + // update: the original code here may have been simple, but it meant that every map load/save + // the key/value pair fields were reversed in the save file, which messes up SourceSafe when it + // tries to delta the two versions during check-in... -slc +#if 0 + ep->next = ent->epairs; + ent->epairs = ep; +#else + // join this onto the END of the chain instead... + // + if (ent->epairs == NULL) // special case for if there isn't a chain yet... :-) + { + ep->next = ent->epairs; + ent->epairs = ep; + } + else + { + for (epair_t* ep2 = ent->epairs ; ep2 ; ep2=ep2->next) + { + if (ep2->next == NULL) + { + // found the end, so... + // + ep2->next = ep; + ep->next = NULL; + break; + } + } + } +#endif + } + } + } while (1); + + if (onlypairs) + return ent; + + if (ent->brushes.onext == &ent->brushes) + has_brushes = false; + else + has_brushes = true; + + GetVectorForKey (ent, "origin", ent->origin); + + e = Eclass_ForName (ValueForKey (ent, "classname"), has_brushes); + ent->eclass = e; + if (e->fixedsize) + { // fixed size entity + if (ent->brushes.onext != &ent->brushes) + { + printf ("Warning: Fixed size entity with brushes\n"); +#if 0 + while (ent->brushes.onext != &ent->brushes) + { // FIXME: this will free the entity and crash! + Brush_Free (b); + } +#endif + ent->brushes.next = ent->brushes.prev = &ent->brushes; + } + + // create a custom brush + VectorAdd (e->mins, ent->origin, mins); + VectorAdd (e->maxs, ent->origin, maxs); + + float a = 0; + if (strnicmp(e->name, "misc_model",10) == 0) + { + char* p = ValueForKey(ent, "model"); + if (p != NULL && strlen(p) > 0) + { + vec3_t vMin, vMax; + a = FloatForKey (ent, "angle"); + gEntityToSetBoundsOf = ent; + if (GetCachedModel(ent, p, vMin, vMax)) + { + // create a custom brush + VectorAdd (ent->md3Class->mins, ent->origin, mins); + VectorAdd (ent->md3Class->maxs, ent->origin, maxs); + } + } + } +#ifdef SOF + if (strnicmp(e->name, "misc_", 5) == 0 || + strnicmp(e->name, "light_", 6) == 0 || + strnicmp(e->name, "m_", 2) == 0 || + strnicmp(e->name, "item_weapon_", 12)== 0 || + strnicmp(e->name, "item_ammo_", 10)== 0 + ) + a = FloatForKey (ent, "angle"); +#endif + b = Brush_Create (mins, maxs, &e->texdef); +/////// + b->owner = ent; + + b->onext = ent->brushes.onext; + b->oprev = &ent->brushes; + ent->brushes.onext->oprev = b; + ent->brushes.onext = b; +/////// + Brush_Build(b); + +#ifdef SOF + scale = FloatForKey (ent, "scale"); + if (scale) + { + Brush_Scale2(e, b, scale, ent->origin,false); + } +#endif + +// if (a) +// { +// vec3_t vAngle; +// vAngle[0] = vAngle[1] = 0; +// vAngle[2] = a; +// Brush_Rotate(b, vAngle, ent->origin, false); +// } + +/* + b->owner = ent; + + b->onext = ent->brushes.onext; + b->oprev = &ent->brushes; + ent->brushes.onext->oprev = b; + ent->brushes.onext = b; +*/ + // do this AFTER doing the brush stuff just above, so don't join to the other "if (a)"... + // + if (a) + { + // pick any old value to rotate away to, then back from, to avoid 0/360 weirdness on key-compares + // + SetKeyValue(ent, "angle", "0", false); // false = no tracking, ie just set the angle and nothing else + SetKeyValue(ent, "angle", va("%g",a), true); // true = do tracking, ie actually do the brush rotate + } + } + else + { // brush entity + if (ent->brushes.next == &ent->brushes) + printf ("Warning: Brush entity with no brushes\n"); + } + + // add all the brushes to the main list + if (pList) + { + for (b=ent->brushes.onext ; b != &ent->brushes ; b=b->onext) + { + b->next = pList->next; + pList->next->prev = b; + b->prev = pList; + pList->next = b; + } + } + + return ent; +} + +void VectorMidpoint(vec3_t va, vec3_t vb, vec3_t& out) +{ + for (int i = 0; i < 3; i++) + out[i] = va[i] + ((vb[i] - va[i]) / 2); +} + + +/* +============ +Entity_Write +============ +*/ +void Entity_Write (entity_t *e, FILE *f, qboolean use_region) +{ + epair_t *ep; + brush_t *b; + vec3_t origin; + char text[128]; + int count; + + // if none of the entities brushes are in the region, + // don't write the entity at all + if (use_region) + { + // in region mode, save the camera position as playerstart + if ( !strcmp(ValueForKey (e, "classname"), "info_player_start") ) + { + // see if this ent is entirely within the region first... + // + bool bWithinRegion = true; + for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext) + if (Map_IsBrushFiltered(b)) + bWithinRegion = false; + + // if the player_start is NOT within the region, then use the camera position instead, else keep it... + // + if (!bWithinRegion || !GetYesNo("\"player_info_start\" found within region, keep it?\n\n( 'NO' will generate a new one from the current camera position )")) + { + fprintf (f, "{\n"); + fprintf (f, "\"classname\" \"info_player_start\"\n"); + fprintf (f, "\"origin\" \"%i %i %i\"\n", (int)g_pParentWnd->GetCamera()->Camera().origin[0], + (int)g_pParentWnd->GetCamera()->Camera().origin[1], (int)g_pParentWnd->GetCamera()->Camera().origin[2]); + fprintf (f, "\"angle\" \"%i\"\n", (int)g_pParentWnd->GetCamera()->Camera().angles[YAW]); + fprintf (f, "}\n"); + return; + } + } + + for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext) + if (!Map_IsBrushFiltered(b)) + break; // got one + + if (b == &e->brushes) + return; // nothing visible + } + + // if fixedsize, calculate a new origin based on the current + // brush position + if (e->eclass->fixedsize) + { +#ifdef SOF + // I know these 2 do the same thing, but if that VectorSubtract ever gets re-instated it'll break ours -slc + if (strnicmp(e->eclass->name, "misc_", 5) == 0 || + strnicmp(e->eclass->name, "light_", 6) == 0 || + strnicmp(e->eclass->name, "m_", 2) == 0 || + strnicmp(e->eclass->name, "item_weapon_", 12)== 0 || + strnicmp(e->eclass->name, "item_ammo_", 10)== 0 + ) + { + VectorCopy(e->origin, origin); + } +#else + if (strnicmp(e->eclass->name, "misc_model",10) == 0 && e->md3Class != NULL) + { + VectorCopy(e->origin, origin); + //VectorSubtract (e->brushes.onext->mins, e->md3Class->mins, origin); + } +#endif + else + { + VectorSubtract (e->brushes.onext->mins, e->eclass->mins, origin); + } + sprintf (text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); + SetKeyValue (e, "origin", text); + } + + fprintf (f, "{\n"); + for (ep = e->epairs ; ep ; ep=ep->next) + fprintf (f, "\"%s\" \"%s\"\n", ep->key, ep->value); + + if (!e->eclass->fixedsize) + { + count = 0; + for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext) + { + if (!use_region || !Map_IsBrushFiltered (b)) + { + fprintf (f, "// brush %i\n", count); + count++; + Brush_Write (b, f); + } + } + } + fprintf (f, "}\n"); +} + + + +qboolean IsBrushSelected(brush_t* bSel) +{ + for (brush_t* b = selected_brushes.next ;b != NULL && b != &selected_brushes; b = b->next) + { + if (b == bSel) + return true; + } + return false; +} + +// +//============ +//Entity_WriteSelected +//============ +// +void Entity_WriteSelected(entity_t *e, FILE *f) +{ + epair_t *ep; + brush_t *b; + vec3_t origin; + char text[128]; + int count; + + for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext) + if (IsBrushSelected(b)) + break; // got one + + if (b == &e->brushes) + return; // nothing selected + + // if fixedsize, calculate a new origin based on the current + // brush position + if (e->eclass->fixedsize) + { +#ifdef SOF + // I suspect these 2 will end up doing the same thing, but for now that VectorSubtract will break ours -slc + if (strnicmp(e->eclass->name, "misc_", 5) == 0 || + strnicmp(e->eclass->name, "light_", 6) == 0 || + strnicmp(e->eclass->name, "m_", 2) == 0 || + strnicmp(e->eclass->name, "item_weapon_", 12)== 0 || + strnicmp(e->eclass->name, "item_ammo_", 10)== 0 + ) + { + VectorCopy(e->origin, origin); + } +#else + if (strnicmp(e->eclass->name, "misc_model",10) == 0 && e->md3Class != NULL) + { + // Bugfix? I'm guessing this should have been the same as in Entity_Write, ie the copy version..... -slc + //VectorCopy(e->origin, origin); + VectorSubtract (e->brushes.onext->mins, e->md3Class->mins, origin); + } +#endif + else + { + VectorSubtract (e->brushes.onext->mins, e->eclass->mins, origin); + } + sprintf (text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); + SetKeyValue (e, "origin", text); + } + + fprintf (f, "{\n"); + for (ep = e->epairs ; ep ; ep=ep->next) + fprintf (f, "\"%s\" \"%s\"\n", ep->key, ep->value); + + if (!e->eclass->fixedsize) + { + count = 0; + for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext) + { + if (IsBrushSelected(b)) + { + fprintf (f, "// brush %i\n", count); + count++; + Brush_Write (b, f); + } + } + } + fprintf (f, "}\n"); +} + + +// +//============ +//Entity_WriteSelected to a CMemFile +//============ +// +void Entity_WriteSelected(entity_t *e, CMemFile* pMemFile) +{ + epair_t *ep; + brush_t *b; + vec3_t origin; + char text[128]; + int count; + + for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext) + if (IsBrushSelected(b)) + break; // got one + + if (b == &e->brushes) + return; // nothing selected + + // if fixedsize, calculate a new origin based on the current + // brush position + if (e->eclass->fixedsize) + { +#ifdef SOF + // I suspect these 2 will end up doing the same thing, but for now that VectorSubtract will break ours -slc + if (strnicmp(e->eclass->name, "misc_", 5) == 0 || + strnicmp(e->eclass->name, "light_", 6) == 0 || + strnicmp(e->eclass->name, "m_", 2) == 0 || + strnicmp(e->eclass->name, "item_weapon_", 12)== 0 || + strnicmp(e->eclass->name, "item_ammo_", 10)== 0 + ) + { + VectorCopy(e->origin, origin); + } +#else + if (strnicmp(e->eclass->name, "misc_model",10) == 0 && e->md3Class != NULL) + { + // Bugfix? I'm guessing this should have been the same as in Entity_Write, ie the copy version..... -slc + //VectorCopy(e->origin, origin); + VectorSubtract (e->brushes.onext->mins, e->md3Class->mins, origin); + } +#endif + else + { + VectorSubtract (e->brushes.onext->mins, e->eclass->mins, origin); + } + sprintf (text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); + SetKeyValue (e, "origin", text); + } + + MemFile_fprintf(pMemFile, "{\n"); + for (ep = e->epairs ; ep ; ep=ep->next) + MemFile_fprintf(pMemFile, "\"%s\" \"%s\"\n", ep->key, ep->value); + + if (!e->eclass->fixedsize) + { + count = 0; + for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext) + { + if (IsBrushSelected(b)) + { + MemFile_fprintf(pMemFile, "// brush %i\n", count); + count++; + Brush_Write (b, pMemFile); + } + } + } + MemFile_fprintf(pMemFile, "}\n"); +} + + + + +/* +============ +Entity_Create + +Creates a new entity out of the selected_brushes list. +If the entity class is fixed size, the brushes are only +used to find a midpoint. Otherwise, the brushes have +their ownershi[ transfered to the new entity. +============ +*/ +entity_t *Entity_Create (eclass_t *c) +{ + entity_t *e; + brush_t *b; + vec3_t mins, maxs; + int i; + + // check to make sure the brushes are ok + + for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + if (b->owner != world_entity) + { + Sys_Printf ("Entity NOT created, brushes not all from world\n"); + Sys_Beep (); + return NULL; + } + + // create it + + e = (entity_t*)qmalloc(sizeof(*e)); + e->brushes.onext = e->brushes.oprev = &e->brushes; + e->eclass = c; + SetKeyValue (e, "classname", c->name); + + // add the entity to the entity list + e->next = entities.next; + entities.next = e; + e->next->prev = e; + e->prev = &entities; + + if (c->fixedsize) + { + // + // just use the selection for positioning + // + b = selected_brushes.next; + for (i=0 ; i<3 ; i++) + e->origin[i] = b->mins[i] - c->mins[i]; + + // create a custom brush + VectorAdd (c->mins, e->origin, mins); + VectorAdd (c->maxs, e->origin, maxs); + + b = Brush_Create (mins, maxs, &c->texdef); + + Entity_LinkBrush (e, b); + + // delete the current selection + Select_Delete (); + + // select the new brush + b->next = b->prev = &selected_brushes; + selected_brushes.next = selected_brushes.prev = b; + + Brush_Build( b ); + } + else + { + // + // change the selected brushes over to the new entity + // + for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + { + Entity_UnlinkBrush (b); + Entity_LinkBrush (e, b); + Brush_Build( b ); // so the key brush gets a name + } + } + + Sys_UpdateWindows (W_ALL); + return e; +} + + +/* +=========== +Entity_LinkBrush +=========== +*/ +void Entity_LinkBrush (entity_t *e, brush_t *b) +{ + if (b->oprev || b->onext) + Error ("Entity_LinkBrush: Allready linked"); + b->owner = e; + + b->onext = e->brushes.onext; + b->oprev = &e->brushes; + e->brushes.onext->oprev = b; + e->brushes.onext = b; +} + +/* +=========== +Entity_UnlinkBrush +=========== +*/ +void Entity_UnlinkBrush (brush_t *b) +{ + //if (!b->owner || !b->onext || !b->oprev) + if (!b->onext || !b->oprev) + Error ("Entity_UnlinkBrush: Not currently linked"); + b->onext->oprev = b->oprev; + b->oprev->onext = b->onext; + b->onext = b->oprev = NULL; + b->owner = NULL; +} + + + +/* +=========== +Entity_Clone +=========== +*/ +entity_t *Entity_Clone (entity_t *e) +{ + entity_t *n; + epair_t *ep, *np; + + n = (entity_t*)qmalloc(sizeof(*n)); + n->brushes.onext = n->brushes.oprev = &n->brushes; + n->eclass = e->eclass; + + // add the entity to the entity list + n->next = entities.next; + entities.next = n; + n->next->prev = n; + n->prev = &entities; + + for (ep = e->epairs ; ep ; ep=ep->next) + { + np = (epair_t*)qmalloc(sizeof(*np)); + np->key = copystring(ep->key); + np->value = copystring(ep->value); + np->next = n->epairs; + n->epairs = np; + } + return n; +} + +int GetUniqueTargetId(int iHint) +{ + int iMin, iMax, i; + BOOL fFound; + entity_t *pe; + + fFound = FALSE; + pe = entities.next; + iMin = 0; + iMax = 0; + + for (; pe != NULL && pe != &entities ; pe = pe->next) + { + i = IntForKey(pe, "target"); + if (i) + { + iMin = min(i, iMin); + iMax = max(i, iMax); + if (i == iHint) + fFound = TRUE; + } + } + + if (fFound) + return iMax + 1; + else + return iHint; +} + +entity_t *FindEntity(const char *pszKey, const char *pszValue) +{ + entity_t *pe; + + pe = entities.next; + + for (; pe != NULL && pe != &entities ; pe = pe->next) + { + if (!strcmp(ValueForKey(pe, pszKey), pszValue)) + return pe; + } + + return NULL; +} + +entity_t *FindEntityInt(const char *pszKey, int iValue) +{ + entity_t *pe; + + pe = entities.next; + + for (; pe != NULL && pe != &entities ; pe = pe->next) + { + if (IntForKey(pe, pszKey) == iValue) + return pe; + } + + return NULL; +} + diff --git a/utils/Radiant/entity.h b/utils/Radiant/entity.h new file mode 100644 index 0000000..900f0f2 --- /dev/null +++ b/utils/Radiant/entity.h @@ -0,0 +1,102 @@ + +// entity.h + + +//--#define MAX_FLAGS 8 +//-- +//-- +//--typedef struct trimodel_t +//--{ +//-- vec3_t v[3]; +//-- float st[3][2]; +//--} trimodel; +//-- +//--typedef struct entitymodel_t +//--{ +//-- entitymodel_t *pNext; +//-- int nTriCount; +//-- trimodel *pTriList; +//-- int nTextureBind; +//-- int nSkinWidth; +//-- int nSkinHeight; +//-- int nModelPosition; +//--} entitymodel; +//-- +//-- +//--typedef struct eclass_s +//--{ +//-- struct eclass_s *next; +//-- char *name; +//-- qboolean fixedsize; +//-- qboolean unknown; // wasn't found in source +//-- vec3_t mins, maxs; +//-- vec3_t color; +//-- texdef_t texdef; +//-- char *comments; +//-- char flagnames[MAX_FLAGS][32]; +//-- +//--/* +//-- int nTriCount; +//-- trimodel *pTriList; +//-- int nTextureBind; +//-- int nSkinWidth, nSkinHeight; +//--*/ +//-- entitymodel *model; +//-- char *modelpath; +//-- char *skinpath; +//-- int nFrame; +//--} eclass_t; +//-- +//--extern eclass_t *eclass; + +void Eclass_InitForSourceDirectory (char *path); +eclass_t *Eclass_ForName (char *name, qboolean has_brushes); + +//=================================================== + + +typedef struct epair_s +{ + struct epair_s *next; + char *key; + char *value; +} epair_t; + +typedef struct entity_s +{ + struct entity_s *prev, *next; + brush_t brushes; // head/tail of list + vec3_t origin; + eclass_t *eclass; + epair_t *epairs; + eclass_t *md3Class; +} entity_t; + +int GetNumKeys(entity_t *ent); +char *GetKeyString(entity_t *ent, int iIndex); +char *ValueForKey (entity_t *ent, LPCSTR key); +void SetKeyValue (entity_t *ent, LPCSTR key, LPCSTR value, bool bDoTracking = true); +void DeleteKey (entity_t *ent, LPCSTR key); +float FloatForKey (entity_t *ent, LPCSTR key); +int IntForKey (entity_t *ent, LPCSTR key); +bool GetVectorForKey (entity_t *ent, LPCSTR key, vec3_t vec); + +void Entity_Free (entity_t *e); +entity_t *Entity_Parse (qboolean onlypairs, brush_t* pList = NULL); +void Entity_Write (entity_t *e, FILE *f, qboolean use_region); +void Entity_WriteSelected(entity_t *e, FILE *f); +void Entity_WriteSelected(entity_t *e, CMemFile*); +entity_t *Entity_Create (eclass_t *c); +entity_t *Entity_Clone (entity_t *e); + +void Entity_LinkBrush (entity_t *e, brush_t *b); +void Entity_UnlinkBrush (brush_t *b); +entity_t *FindEntity(const char *pszKey, const char *pszValue); +entity_t *FindEntityInt(const char *pszKey, int iValue); + +int GetUniqueTargetId(int iHint); +int Eclass_hasModel(eclass_t *e, vec3_t &vMin, vec3_t &vMax); +eclass_t* GetCachedModel(entity_t *pEntity, const char *pName, vec3_t &vMin, vec3_t &vMax); + +extern entity_t *gEntityToSetBoundsOf; + diff --git a/utils/Radiant/entitylistdlg.cpp b/utils/Radiant/entitylistdlg.cpp new file mode 100644 index 0000000..7e09fa3 --- /dev/null +++ b/utils/Radiant/entitylistdlg.cpp @@ -0,0 +1,131 @@ +// EntityListDlg.cpp : implementation file +// + +#include "stdafx.h" +#include "Radiant.h" +#include "EntityListDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CEntityListDlg dialog + + +CEntityListDlg::CEntityListDlg(CWnd* pParent /*=NULL*/) + : CDialog(CEntityListDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CEntityListDlg) + //}}AFX_DATA_INIT +} + + +void CEntityListDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CEntityListDlg) + DDX_Control(pDX, IDC_LIST_ENTITY, m_lstEntity); + DDX_Control(pDX, IDC_TREE_ENTITY, m_treeEntity); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CEntityListDlg, CDialog) + //{{AFX_MSG_MAP(CEntityListDlg) + ON_BN_CLICKED(IDSELECT, OnSelect) + ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_ENTITY, OnSelchangedTreeEntity) + ON_NOTIFY(NM_DBLCLK, IDC_TREE_ENTITY, OnDblclkTreeEntity) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CEntityListDlg message handlers + +void CEntityListDlg::OnSelect() +{ + HTREEITEM hItem = m_treeEntity.GetSelectedItem(); + if (hItem) + { + entity_t* pEntity = reinterpret_cast(m_treeEntity.GetItemData(hItem)); + if (pEntity) + { + Select_Deselect(); + Select_Brush (pEntity->brushes.onext); + } + } + Sys_UpdateWindows(W_ALL); +} + +BOOL CEntityListDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + CMapStringToPtr mapEntity; + + HTREEITEM hParent = m_treeEntity.InsertItem(world_entity->eclass->name); + HTREEITEM hChild = m_treeEntity.InsertItem(world_entity->eclass->name, hParent); + m_treeEntity.SetItemData(hChild, reinterpret_cast(world_entity)); + + for (entity_t* pEntity=entities.next ; pEntity != &entities ; pEntity=pEntity->next) + { + hParent = NULL; + if (mapEntity.Lookup(pEntity->eclass->name, reinterpret_cast(hParent)) == FALSE) + { + hParent = m_treeEntity.InsertItem(pEntity->eclass->name); + mapEntity.SetAt(pEntity->eclass->name, reinterpret_cast(hParent)); + } + hChild = m_treeEntity.InsertItem(pEntity->eclass->name, hParent); + m_treeEntity.SetItemData(hChild, reinterpret_cast(pEntity)); + } + + CRect rct; + m_lstEntity.GetClientRect(rct); + m_lstEntity.InsertColumn(0, "Key", LVCFMT_LEFT, rct.Width() / 2); + m_lstEntity.InsertColumn(1, "Value", LVCFMT_LEFT, rct.Width() / 2); + m_lstEntity.DeleteColumn(2); + UpdateData(FALSE); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void CEntityListDlg::OnSelchangedTreeEntity(NMHDR* pNMHDR, LRESULT* pResult) +{ + NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; + HTREEITEM hItem = m_treeEntity.GetSelectedItem(); + m_lstEntity.DeleteAllItems(); + if (hItem) + { + CString strList; + entity_t* pEntity = reinterpret_cast(m_treeEntity.GetItemData(hItem)); + if (pEntity) + { + for (epair_t* pEpair = pEntity->epairs ; pEpair ; pEpair = pEpair->next) + { + if (strlen(pEpair->key) > 8) + strList.Format("%s\t%s", pEpair->key, pEpair->value); + else + strList.Format("%s\t\t%s", pEpair->key, pEpair->value); + int nParent = m_lstEntity.InsertItem(0, pEpair->key); + m_lstEntity.SetItem(nParent, 1, LVIF_TEXT, pEpair->value, 0, 0, 0, reinterpret_cast(pEntity)); + + } + } + } + *pResult = 0; +} + +void CEntityListDlg::OnDblclkListInfo() +{ + // TODO: Add your control notification handler code here + +} + +void CEntityListDlg::OnDblclkTreeEntity(NMHDR* pNMHDR, LRESULT* pResult) +{ + OnSelect(); + *pResult = 0; +} diff --git a/utils/Radiant/entitylistdlg.h b/utils/Radiant/entitylistdlg.h new file mode 100644 index 0000000..2121db1 --- /dev/null +++ b/utils/Radiant/entitylistdlg.h @@ -0,0 +1,51 @@ +#if !defined(AFX_ENTITYLISTDLG_H__C241B9A3_819F_11D1_B548_00AA00A410FC__INCLUDED_) +#define AFX_ENTITYLISTDLG_H__C241B9A3_819F_11D1_B548_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// EntityListDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CEntityListDlg dialog + +class CEntityListDlg : public CDialog +{ +// Construction +public: + CEntityListDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CEntityListDlg) + enum { IDD = IDD_DLG_ENTITYLIST }; + CListCtrl m_lstEntity; + CTreeCtrl m_treeEntity; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CEntityListDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CEntityListDlg) + afx_msg void OnSelect(); + virtual BOOL OnInitDialog(); + afx_msg void OnSelchangedTreeEntity(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnDblclkListInfo(); + afx_msg void OnDblclkTreeEntity(NMHDR* pNMHDR, LRESULT* pResult); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_ENTITYLISTDLG_H__C241B9A3_819F_11D1_B548_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/entityw.h b/utils/Radiant/entityw.h new file mode 100644 index 0000000..b15eb7d --- /dev/null +++ b/utils/Radiant/entityw.h @@ -0,0 +1,60 @@ +// entity.h + +#define DlgXBorder 5 +#define DlgYBorder 5 + + +enum +{ + EntList = 0, + EntComment, + EntCheck1, + EntCheck2, + EntCheck3, + EntCheck4, + EntCheck5, + EntCheck6, + EntCheck7, + EntCheck8, + EntCheck9, + EntCheck10, + EntCheck11, + EntCheck12, +#ifdef SOF + EntCheck13, + EntCheck14, + EntCheck15, + EntCheck16, + EntCheck17, + EntCheck18, + EntCheck19, + EntCheck20, + EntCheck21, +#endif + EntProps, + EntDir0, + EntDir45, + EntDir90, + EntDir135, + EntDir180, + EntDir225, + EntDir270, + EntDir315, + EntDirUp, + EntDirDown, + EntDelProp, + EntKeyLabel, + EntKeyField, + EntValueLabel, + EntValueField, + EntColor, + EntAssignSounds, + EntAssignModels, + + EntLast, +}; + +extern HWND hwndEnt[EntLast]; + +extern int rgIds[EntLast]; + diff --git a/utils/Radiant/entkeyfindreplace.cpp b/utils/Radiant/entkeyfindreplace.cpp new file mode 100644 index 0000000..82f7b72 --- /dev/null +++ b/utils/Radiant/entkeyfindreplace.cpp @@ -0,0 +1,168 @@ +// EntKeyFindReplace.cpp : implementation file +// + +#include "stdafx.h" +#include "radiant.h" +#include "EntKeyFindReplace.h" +#include "oddbits.h" +/* +#include "stdafx.h" +#include "Radiant.h" +#include "ZWnd.h" +#include "qe3.h" +#include "zclip.h" +*/ + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CEntKeyFindReplace dialog + +CEntKeyFindReplace::CEntKeyFindReplace( CString* p_strFindKey, + CString* p_strFindValue, + CString* p_strReplaceKey, + CString* p_strReplaceValue, + bool* p_bWholeStringMatchOnly, + bool* p_bSelectAllMatchingEnts, + CWnd* pParent /*=NULL*/) + : CDialog(CEntKeyFindReplace::IDD, pParent) +{ + m_pStrFindKey = p_strFindKey; + m_pStrFindValue = p_strFindValue; + m_pStrReplaceKey = p_strReplaceKey; + m_pStrReplaceValue = p_strReplaceValue; + m_pbWholeStringMatchOnly = p_bWholeStringMatchOnly; + m_pbSelectAllMatchingEnts= p_bSelectAllMatchingEnts; + + //{{AFX_DATA_INIT(CEntKeyFindReplace) + m_strFindKey = *m_pStrFindKey; + m_strFindValue = *m_pStrFindValue; + m_strReplaceKey = *m_pStrReplaceKey; + m_strReplaceValue = *m_pStrReplaceValue; + m_bWholeStringMatchOnly = *m_pbWholeStringMatchOnly; + m_bSelectAllMatchingEnts = *m_pbSelectAllMatchingEnts; + //}}AFX_DATA_INIT +} + + +void CEntKeyFindReplace::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CEntKeyFindReplace) + DDX_Text(pDX, IDC_EDIT_FIND_KEY, m_strFindKey); + DDX_Text(pDX, IDC_EDIT_FIND_VALUE, m_strFindValue); + DDX_Text(pDX, IDC_EDIT_REPLACE_KEY, m_strReplaceKey); + DDX_Text(pDX, IDC_EDIT_REPLACE_VALUE, m_strReplaceValue); + DDX_Check(pDX, IDC_CHECK_FIND_WHOLESTRINGMATCHONLY, m_bWholeStringMatchOnly); + DDX_Check(pDX, IDC_CHECK_SELECTALLMATCHING, m_bSelectAllMatchingEnts); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CEntKeyFindReplace, CDialog) + //{{AFX_MSG_MAP(CEntKeyFindReplace) + ON_BN_CLICKED(IDREPLACE, OnReplace) + ON_BN_CLICKED(IDFIND, OnFind) + ON_BN_CLICKED(IDC_KEYCOPY, OnKeycopy) + ON_BN_CLICKED(IDC_VALUECOPY, OnValuecopy) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CEntKeyFindReplace message handlers + +void CEntKeyFindReplace::OnCancel() +{ + CDialog::OnCancel(); +} + +void CEntKeyFindReplace::OnReplace() +{ + // quick check, if no key value is specified then there's not much to do... + // + UpdateData(DIALOG_TO_DATA); + if (m_strFindKey.IsEmpty()) + { + ErrorBox("Empty FIND !\n\n(This is only permitted for FIND, not replace, for safety reasons)"); + } + else + { + if (!m_strFindValue.IsEmpty() || GetYesNo(va("Empty FIND means replace any existing ( & non-blank ) for \"%s\"\n\nProceed?",(LPCSTR)m_strFindKey))) + { + // another check, if they're trying to do a replace with a missing replace key, it'll just delete found keys... + // + if ((!m_strReplaceKey.IsEmpty() && !m_strReplaceValue.IsEmpty()) || GetYesNo(va("Empty REPLACE or fields will just delete all occurence of \"%s\"\n\nProceed?",m_strFindKey))) + { + if (GetYesNo("Sure?")) + { + CopyFields(); + EndDialog(ID_RET_REPLACE); + } + } + } + } +} + +void CEntKeyFindReplace::OnFind() +{ + // quick check, if no key value is specified then there's not much to do... + // + UpdateData(DIALOG_TO_DATA); + + if (m_strFindKey.IsEmpty() && m_strFindValue.IsEmpty()) + { + ErrorBox("Empty FIND fields!"); + } + else + { +// if (m_strFindKey.IsEmpty() && m_bSelectAllMatchingEnts) +// { +// if (GetYesNo("Warning! Having a blank FIND and ticking \"Select all matching ents\" can take a LONG time to do (and is probably a wrong choice anyway?)\n\nProceed?")) +// { +// CopyFields(); +// EndDialog(ID_RET_FIND); +// } +// } +// else + { + CopyFields(); + EndDialog(ID_RET_FIND); + } + } +} + +void CEntKeyFindReplace::CopyFields() +{ + UpdateData(DIALOG_TO_DATA); + + *m_pStrFindKey = m_strFindKey; + *m_pStrFindValue = m_strFindValue; + *m_pStrReplaceKey = m_strReplaceKey; + *m_pStrReplaceValue = m_strReplaceValue; + *m_pbWholeStringMatchOnly = m_bWholeStringMatchOnly; + *m_pbSelectAllMatchingEnts= m_bSelectAllMatchingEnts; +} + + +void CEntKeyFindReplace::OnKeycopy() +{ + UpdateData(DIALOG_TO_DATA); + + m_strReplaceKey = m_strFindKey; + + UpdateData(DATA_TO_DIALOG); +} + +void CEntKeyFindReplace::OnValuecopy() +{ + UpdateData(DIALOG_TO_DATA); + + m_strReplaceValue = m_strFindValue; + + UpdateData(DATA_TO_DIALOG); +} + diff --git a/utils/Radiant/entkeyfindreplace.h b/utils/Radiant/entkeyfindreplace.h new file mode 100644 index 0000000..b9b9e0f --- /dev/null +++ b/utils/Radiant/entkeyfindreplace.h @@ -0,0 +1,77 @@ +#if !defined(AFX_ENTKEYFINDREPLACE_H__1AE54C31_FC22_11D3_8A60_00500424438B__INCLUDED_) +#define AFX_ENTKEYFINDREPLACE_H__1AE54C31_FC22_11D3_8A60_00500424438B__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// EntKeyFindReplace.h : header file +// + + +// return vals for modal dialogue, any values will do that don't clash with the first 9 or so defined by IDOK etc +// +#define ID_RET_REPLACE 100 +#define ID_RET_FIND 101 + + +///////////////////////////////////////////////////////////////////////////// +// CEntKeyFindReplace dialog + +class CEntKeyFindReplace : public CDialog +{ +// Construction +public: + CEntKeyFindReplace(CString* p_strFindKey, + CString* p_strFindValue, + CString* p_strReplaceKey, + CString* p_strReplaceValue, + bool* p_bWholeStringMatchOnly, + bool* p_bSelectAllMatchingEnts, + CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CEntKeyFindReplace) + enum { IDD = IDD_ENTFINDREPLACE }; + CString m_strFindKey; + CString m_strFindValue; + CString m_strReplaceKey; + CString m_strReplaceValue; + BOOL m_bWholeStringMatchOnly; + BOOL m_bSelectAllMatchingEnts; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CEntKeyFindReplace) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CEntKeyFindReplace) + virtual void OnCancel(); + afx_msg void OnReplace(); + afx_msg void OnFind(); + afx_msg void OnKeycopy(); + afx_msg void OnValuecopy(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + + CString* m_pStrFindKey; + CString* m_pStrFindValue; + CString* m_pStrReplaceKey; + CString* m_pStrReplaceValue; + bool* m_pbWholeStringMatchOnly; + bool* m_pbSelectAllMatchingEnts; + + void CopyFields(); +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_ENTKEYFINDREPLACE_H__1AE54C31_FC22_11D3_8A60_00500424438B__INCLUDED_) diff --git a/utils/Radiant/findtexturedlg.cpp b/utils/Radiant/findtexturedlg.cpp new file mode 100644 index 0000000..1004d82 --- /dev/null +++ b/utils/Radiant/findtexturedlg.cpp @@ -0,0 +1,403 @@ +// FindTextureDlg.cpp : implementation file +// + +#include "stdafx.h" +#include "Radiant.h" +#include "FindTextureDlg.h" +#include "oddbits.h" + +#pragma warning(disable : 4786) // shut the fuck up about debug symbol name length +#include +using namespace std; + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + + +void PrintReplacementCount(int iReplacements, bool bSelectOnlyNoReplace) +{ + if (iReplacements) + { + if (bSelectOnlyNoReplace) + { + Sys_Printf(va("( Selected %d brushes with matching textures )\n",iReplacements)); + } + else + { + Sys_Printf(va("( Replaced %d search-matching texture usages )\n",iReplacements)); + } + } + else + { + Sys_Printf("( No search-matching textures usages found )\n"); + } +} + + +// Ideally I'd have put this above the original version it overrides, and added it to the H file etc, but the +// problem is that that'd mean making all other modules that included that header file STL_aware, and I just +// can't be bothered, besides this is the only place in the code that calls the other one in this fashion. +// +typedef pair FindReplaceStringPair_t; +typedef list FindReplaceList_t; +// +// return is simply a count of how many replacements took place, used for friendly stat printing... +// +int FindReplaceTextures(FindReplaceList_t &FindReplaceList, bool bSelected, bool bForce, bool bReScale, bool bSelectOnlyNoReplace) +{ + CWaitCursor wait; + + int iReplacedTextures = 0; + + int iLoopCount= FindReplaceList.size(); + int iLoopIter = 0; + + for (FindReplaceList_t::iterator it = FindReplaceList.begin(); it != FindReplaceList.end(); ++it, iLoopIter++) + { + CString strFind = (*it).first; + CString strReplace = (*it).second; + + Sys_Printf(va("Loop %d/%d, replacing \"%s\" with \"%s\"...",iLoopIter+1,iLoopCount, (LPCSTR)strFind, (LPCSTR)strReplace)); + + int iThisReplacedTextures = FindReplaceTextures(strFind, strReplace, bSelected, bForce, bReScale, bSelectOnlyNoReplace, + true, // bInhibitCameraUpdate + iLoopIter?true:false // bCalledDuringLoopAndNotFirstTime + ); + + Sys_Printf(va("%d found\n",iThisReplacedTextures)); + + iReplacedTextures += iThisReplacedTextures; + } + + if (iReplacedTextures) // :-) + { + Sys_UpdateWindows (W_CAMERA); // since we inhibited the one in the function we called + } + + return iReplacedTextures; +} + + + + +///////////////////////////////////////////////////////////////////////////// +// CFindTextureDlg dialog + +CFindTextureDlg g_TexFindDlg; +CFindTextureDlg& g_dlgFind = g_TexFindDlg; +static bool g_bFindActive = true; +static bool gbIsOpen = false; // needed, or the .isopen() member doesn't actually work + +void CFindTextureDlg::updateTextures(const char *p) +{ + if (isOpen()) + { + if (g_bFindActive) + { + setFindStr(p); + } + else + { + setReplaceStr(p); + } + } +} + + +CFindTextureDlg::CFindTextureDlg(CWnd* pParent /*=NULL*/) + : CDialog(CFindTextureDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CFindTextureDlg) + m_bSelectedOnly = FALSE; + m_strFind = _T(""); + m_strReplace = _T(""); + m_bForce = FALSE; + m_bLive = TRUE; + m_bReScale = TRUE; + m_bSelectOnlyNoReplace = FALSE; + //}}AFX_DATA_INIT +} + + +void CFindTextureDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CFindTextureDlg) + DDX_Check(pDX, IDC_CHECK_SELECTED, m_bSelectedOnly); + DDX_Text(pDX, IDC_EDIT_FIND, m_strFind); + DDX_Text(pDX, IDC_EDIT_REPLACE, m_strReplace); + DDX_Check(pDX, IDC_CHECK_FORCE, m_bForce); + DDX_Check(pDX, IDC_CHECK_LIVE, m_bLive); + DDX_Check(pDX, IDC_CHECK_RESCALE, m_bReScale); + DDX_Check(pDX, IDC_CHECK_SELECTONLY, m_bSelectOnlyNoReplace); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CFindTextureDlg, CDialog) + //{{AFX_MSG_MAP(CFindTextureDlg) + ON_BN_CLICKED(ID_BTN_APPLY, OnBtnApply) + ON_BN_CLICKED(IDC_BUTTON_BATCHFINDREPLACE, OnButtonBatchfindreplace) + ON_EN_SETFOCUS(IDC_EDIT_FIND, OnSetfocusEditFind) + ON_EN_SETFOCUS(IDC_EDIT_REPLACE, OnSetfocusEditReplace) + ON_WM_DESTROY() + ON_BN_CLICKED(IDC_CHECK_SELECTONLY, OnCheckSelectonly) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +LPCSTR scGetUserName(void); + +void CFindTextureDlg::OnBtnApply() +{ + SaveToRegistry(); + + if (m_bSelectOnlyNoReplace && !stricmp(scGetUserName(),"ebiessman")) + { + if (!GetYesNo("Are you *really* sure you want to do this, Eric?\n\nI mean, *really* sure?")) + return; + } + CWaitCursor wait; + int iReplacements = FindReplaceTextures(m_strFind, m_strReplace, m_bSelectedOnly, m_bForce, m_bReScale, m_bSelectOnlyNoReplace); + PrintReplacementCount(iReplacements, m_bSelectOnlyNoReplace); +} + +void CFindTextureDlg::OnOK() +{ + SaveToRegistry(); + + if (m_bSelectOnlyNoReplace && !stricmp(scGetUserName(),"ebiessman")) + { + if (!GetYesNo("Are you *really* sure you want to do this, Eric?\n\nI mean, *really* sure?")) + return; + } + + CWaitCursor wait; + int iReplacements = FindReplaceTextures(m_strFind, m_strReplace, m_bSelectedOnly, m_bForce, m_bReScale, m_bSelectOnlyNoReplace); + PrintReplacementCount(iReplacements, m_bSelectOnlyNoReplace); + + CDialog::OnOK(); + + gbIsOpen = false; +} + +void CFindTextureDlg::show() +{ + if (g_dlgFind.GetSafeHwnd() == NULL || IsWindow(g_dlgFind.GetSafeHwnd()) == FALSE) + { + g_dlgFind.Create(IDD_DIALOG_FINDREPLACE); + g_dlgFind.ShowWindow(SW_SHOW); + } + else + { + g_dlgFind.ShowWindow(SW_SHOW); + } + CRect rct; + LONG lSize = sizeof(rct); + if (LoadRegistryInfo("Radiant::TextureFindWindow", &rct, &lSize)) + g_dlgFind.SetWindowPos(NULL, rct.left, rct.top, 0,0, SWP_NOSIZE | SWP_SHOWWINDOW); + + g_dlgFind.LoadFromRegistry(); + + g_dlgFind.HandleItemGreying(); + + gbIsOpen = true; +} + + +bool CFindTextureDlg::isOpen() +{ + //return (g_dlgFind.GetSafeHwnd() == NULL || IsWindow(g_dlgFind.GetSafeHwnd()) == FALSE) ? false : true; + return gbIsOpen; +} + +void CFindTextureDlg::setFindStr(const char * p) +{ + g_dlgFind.UpdateData(TRUE); + if (g_dlgFind.m_bLive) + { + g_dlgFind.m_strFind = p; + g_dlgFind.UpdateData(FALSE); + } +} + +void CFindTextureDlg::setReplaceStr(const char * p) +{ + g_dlgFind.UpdateData(TRUE); + if (g_dlgFind.m_bLive) + { + g_dlgFind.m_strReplace = p; + g_dlgFind.UpdateData(FALSE); + } +} + + +void CFindTextureDlg::OnCancel() +{ + SaveToRegistry(); + CDialog::OnCancel(); + gbIsOpen = false; +} + +BOOL CFindTextureDlg::DestroyWindow() +{ + gbIsOpen = false; + return CDialog::DestroyWindow(); +} + +void CFindTextureDlg::OnButtonBatchfindreplace() +{ + UpdateData(DIALOG_TO_DATA); // setup member vars properly + + char sFileToLoad[MAX_PATH]={0}; + + LONG lSize = sizeof(sFileToLoad); + if (!LoadRegistryInfo("Radiant::TextureFindReplaceBatchTextFile", &sFileToLoad, &lSize)) + strcpy(sFileToLoad,va("%s\\%s",Filename_PathOnly(currentmap),"texturenames.txt")); + + char *psTextFilename = InputLoadFileName( Filename_WithoutPath(sFileToLoad), + "Name of text files for batch find/replace", + Filename_PathOnly(sFileToLoad), + "Text Files|*.txt;||"); + + if (psTextFilename) + { + // save for next time... + // + strcpy(sFileToLoad,psTextFilename); + SaveRegistryInfo("Radiant::TextureFindReplaceBatchTextFile", &sFileToLoad, sizeof(sFileToLoad)); + + + // now read this file in and build up a find/replace list to feed to the normal code... + // + FindReplaceList_t FindReplaceList; + + FILE *fhTextFile = fopen(sFileToLoad, "rt"); + + if (fhTextFile) + { + char sLine[16384]; // should be enough + char *psLine; + CString strLine; + + CString strFind; + CString strReplace; + + while ((psLine = fgets( sLine, sizeof(sLine), fhTextFile ))!=NULL) + { + strLine = strlwr(sLine); + strLine.Replace("\n",""); + strLine.TrimRight(); + strLine.TrimLeft(); + + if (!strLine.IsEmpty()) + { + int iLoc = strLine.FindOneOf(" \t"); + + if (iLoc >=0) + { + strFind = strLine.Left(iLoc); + strReplace = strLine.Mid(iLoc); + + strFind.TrimRight(); + strReplace.TrimLeft(); + + FindReplaceList.insert(FindReplaceList.end(), FindReplaceStringPair_t(strFind, strReplace)); + } + else + { + Sys_Printf(va("Syntax error in line \"%s\"! (need find and replace strings)\n",sFileToLoad)); + } + } + } + + // ( finished reading from the file ) + + if (ferror(fhTextFile)) + { + Sys_Printf(va("Error while reading file \"%s\"!\n",sFileToLoad)); + } + else + { + int iReplacements = FindReplaceTextures(FindReplaceList, m_bSelectedOnly, m_bForce, m_bReScale, m_bSelectOnlyNoReplace); + PrintReplacementCount(iReplacements, m_bSelectOnlyNoReplace); + } + + fclose(fhTextFile); + } + else + { + // shouldn't happen... + // + Sys_Printf(va("Error opening file \"%s\"!\n",sFileToLoad)); + } + } +} + + +void CFindTextureDlg::LoadFromRegistry() +{ + LONG + lSize = sizeof(this->m_bForce); + if (!LoadRegistryInfo("Radiant::TextureFindWindow_Force", &m_bForce, &lSize)) + m_bForce = false; + + lSize = sizeof(m_bLive); + if (!LoadRegistryInfo("Radiant::TextureFindWindow_Live", &m_bLive, &lSize)) + m_bLive = true; + + lSize = sizeof(m_bReScale); + if (!LoadRegistryInfo("Radiant::TextureFindWindow_ReScale", &m_bReScale, &lSize)) + m_bReScale = true; + + lSize = sizeof(m_bSelectOnlyNoReplace); + if (!LoadRegistryInfo("Radiant::TextureFindWindow_SelectOnlyNoReplace", &m_bSelectOnlyNoReplace, &lSize)) + m_bSelectOnlyNoReplace = false; + + UpdateData(DATA_TO_DIALOG); +} + +void CFindTextureDlg::SaveToRegistry(void) +{ + UpdateData(DIALOG_TO_DATA); + + CRect rct; + GetWindowRect(rct); + SaveRegistryInfo("Radiant::TextureFindWindow", &rct, sizeof(rct)); + + SaveRegistryInfo("Radiant::TextureFindWindow_Force", &m_bForce, sizeof(m_bForce)); + SaveRegistryInfo("Radiant::TextureFindWindow_Live", &m_bLive, sizeof(m_bLive)); + SaveRegistryInfo("Radiant::TextureFindWindow_ReScale", &m_bReScale, sizeof(m_bReScale)); + SaveRegistryInfo("Radiant::TextureFindWindow_SelectOnlyNoReplace", &m_bSelectOnlyNoReplace, sizeof(m_bSelectOnlyNoReplace)); +} + + +void CFindTextureDlg::OnSetfocusEditFind() +{ + g_bFindActive = true; +} + +void CFindTextureDlg::OnSetfocusEditReplace() +{ + g_bFindActive = false; +} + +void CFindTextureDlg::OnCheckSelectonly() +{ + HandleItemGreying(); +} + + +void CFindTextureDlg::HandleItemGreying() +{ + UpdateData(DIALOG_TO_DATA); + + bool bOnOff = !m_bSelectOnlyNoReplace; + + GetDlgItem(IDC_CHECK_RESCALE)->EnableWindow(bOnOff); + GetDlgItem(IDC_CHECK_SELECTED)->EnableWindow(bOnOff); + GetDlgItem(IDC_CHECK_FORCE)->EnableWindow(bOnOff); + GetDlgItem(IDC_CHECK_LIVE)->EnableWindow(bOnOff); +} diff --git a/utils/Radiant/findtexturedlg.h b/utils/Radiant/findtexturedlg.h new file mode 100644 index 0000000..94d95a1 --- /dev/null +++ b/utils/Radiant/findtexturedlg.h @@ -0,0 +1,71 @@ +#if !defined(AFX_FINDTEXTUREDLG_H__34B75D32_9F3A_11D1_B570_00AA00A410FC__INCLUDED_) +#define AFX_FINDTEXTUREDLG_H__34B75D32_9F3A_11D1_B570_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// FindTextureDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CFindTextureDlg dialog + +class CFindTextureDlg : public CDialog +{ +// Construction +public: + void SaveToRegistry(void); + void LoadFromRegistry(void); + + static void setReplaceStr(const char* p); + static void setFindStr(const char* p); + static bool isOpen(); + static void show(); + static void updateTextures(const char* p); + CFindTextureDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CFindTextureDlg) + enum { IDD = IDD_DIALOG_FINDREPLACE }; + BOOL m_bSelectedOnly; + CString m_strFind; + CString m_strReplace; + BOOL m_bForce; + BOOL m_bLive; + BOOL m_bReScale; + BOOL m_bSelectOnlyNoReplace; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CFindTextureDlg) + public: + virtual BOOL DestroyWindow(); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CFindTextureDlg) + afx_msg void OnBtnApply(); + virtual void OnOK(); + virtual void OnCancel(); + afx_msg void OnButtonBatchfindreplace(); + afx_msg void OnSetfocusEditFind(); + afx_msg void OnSetfocusEditReplace(); + afx_msg void OnCheckSelectonly(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + +protected: + void HandleItemGreying(); +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_FINDTEXTUREDLG_H__34B75D32_9F3A_11D1_B570_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/fnmatch.cpp b/utils/Radiant/fnmatch.cpp new file mode 100644 index 0000000..199a16b --- /dev/null +++ b/utils/Radiant/fnmatch.cpp @@ -0,0 +1,66 @@ +#include "stdafx.h" +#include + +#define Reg1 register +int match(char *mask, char *name) +{ + Reg1 unsigned char *m = (unsigned char *)mask, *n = (unsigned char *)name; + char *ma = mask, *na = name; + int wild = 0, q = 0; + + while (1) + { + if (*m == '*') + { + while (*m == '*') + m++; + wild = 1; + ma = (char *)m; + na = (char *)n; + } + + if (!*m) + { + if (!*n) + return 0; + for (m--; (m > (unsigned char *)mask) && (*m == '?'); m--) + ; + if ((*m == '*') && (m > (unsigned char *)mask) && + (m[-1] != '\\')) + return 0; + if (!wild) + return 1; + m = (unsigned char *)ma; + n = (unsigned char *)++na; + } + else if (!*n) + { + while(*m == '*') + m++; + return (*m != 0); + } + if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?'))) + { + m++; + q = 1; + } + else + q = 0; + + if ((tolower(*m) != tolower(*n)) && ((*m != '?') || q)) + { + if (!wild) + return 1; + m = (unsigned char *)ma; + n = (unsigned char *)++na; + } + else + { + if (*m) + m++; + if (*n) + n++; + } + } +} + diff --git a/utils/Radiant/fnmatch.h b/utils/Radiant/fnmatch.h new file mode 100644 index 0000000..99d09b6 --- /dev/null +++ b/utils/Radiant/fnmatch.h @@ -0,0 +1,6 @@ +#ifndef _FNMATCH_H +#define _FNMATCH_H + +int match(char *mask, char *name); + +#endif /* fnmatch.h */ diff --git a/utils/Radiant/groupnames.cpp b/utils/Radiant/groupnames.cpp new file mode 100644 index 0000000..9ecab98 --- /dev/null +++ b/utils/Radiant/groupnames.cpp @@ -0,0 +1,515 @@ +// Filename:- groupnames.cpp +// +#include "stdafx.h" +#include "Radiant.h" +#include "qe3.h" +#include "groupnames.h" +#include + +// I fucking hate this pragma shit + +#pragma warning(disable : 4786) // shut the fuck up about debug symbol name length +#include +#include +using namespace std; + +// all of the groupname equates in resource.h for this type have 100 entries, or 100+1 for the ones that have a menu +// item such as . Unfortunately resource.h isn't the world's greatest file to put comments in, since VC +// rewrites it when it feels like it, so fingers crossed no-one fucks with my code. +// +#define MAX_GROUPNAMES ((ID_SCRIPTPOPOP_GROUPNAMES_ID_END-ID_SCRIPTPOPOP_GROUPNAMES_ID_START)+1) + + +bool bGrouping_IsActive = true; // activates / deactivates all grouping at once +bool bGrouping_IsGhosted= false; // controls whether hidden groups are still displayed, albeit as ghosts, for placement + +vec3_t v3GhostColor = {0.8f,0.8f,0.8f}; +vec4_t v4GhostColor = {0.8f,0.8f,0.8f,0.4f}; + +// to avoid adding STL to this project I'll use key/pairs on fake ents to maintain simple string lists... :-) +// +// (note, that comment was from the EF1 codebase, but WTF, it works...) +// +entity_t fakeEnt1={0}; +entity_t fakeEnt2={0}; + +entity_t *fakeEnts[2]={&fakeEnt1,&fakeEnt2}; +int ifakeEntIndex=0; // this should only be toggled in one place!! + + +// returns: +// 0 for not-hidden +// 1 for hidden +// 2 for hidden, but ghosted +// +/*bool*/ int Grouping_EntIsHidden(entity_t *ent, bool bCalledFromDisplayCode) +{ + if (bGrouping_IsActive) + { + // does this ent have a group name?... + // + char *psGroupName = ValueForKey(ent, sKEYFIELD_GROUPNAME); + if (strlen(psGroupName)) + { + // is this groupname a hidden one? + // + char *psBoolValue = ValueForKey(fakeEnts[ifakeEntIndex], psGroupName); + + if (strlen(psBoolValue)) + { + bool bHidden = !atoi(psBoolValue); + + if (!bCalledFromDisplayCode || !bHidden) + return bHidden; + + return bGrouping_IsGhosted?2:1; + } + } + } + + return 0; +} + + +static void Entity_DeleteKeys(entity_t *ent) // I'd have thought there'd be one of these already, but... +{ + epair_t* pEPair = ent->epairs; + + while (pEPair) + { + epair_t* pNextEPair = pEPair->next; + free (pEPair->key); + free (pEPair->value); + free (pEPair); + pEPair = pNextEPair; + } + + ent->epairs = NULL; +} + +void Grouping_Shutdown(void) +{ + for (int i=0; inext) + { + char *psGroupName = ValueForKey(pEnt, sKEYFIELD_GROUPNAME); + + if (strlen(psGroupName)) + { + // make sure this groupname is added to the list... + // + // first, did this groupname key exist in the previous list? + // + char *psBoolValue = ValueForKey(prevEnt, psGroupName); + + if (!strlen(psBoolValue)) + { + // no prev bool found, so... + // + psBoolValue = "1"; // new groups defaulted to ON (of course). If not, an ent would vanish as soon as you entered a new groupname + } + + SetKeyValue(currEnt, psGroupName, psBoolValue); + } + } +} + + + +// I hadn't really wanted to do this, but... +// +int Grouping_GetListCount(void) +{ + Grouping_BeginListRead(); + + int iCount = 0; + bool bScrap; + + while (Grouping_NextListRead(bScrap)) iCount++; + + return (iCountepairs; + + while (pEPair && iIndex) + { + pEPair = pEPair->next; + + iIndex--; + } + + if (!iIndex && pEPair) + { + bOnOff = !!atoi(pEPair->value); + return pEPair->key; + } + + return NULL; +} + + + +void Grouping_ToggleGroupState(unsigned int iMenuIndexClickedOn) +{ + Grouping_BeginListRead(); + + while (1) + { + bool bOnOff; + char *psGroupName = Grouping_NextListRead(bOnOff); + + if (!psGroupName) + { + Sys_Printf("Error: unable to locate groupname index %d! tell me! (Ste)\n",iMenuIndexClickedOn); + break; // can't find it, err.... + } + + if (!iMenuIndexClickedOn--) + { + // this is the one, so toggle it in the current grouping list... + // + SetKeyValue(fakeEnts[ifakeEntIndex], psGroupName, !bOnOff?"1":"0"); + break; + } + } + + Sys_UpdateWindows(W_ALL); +} + + +// select all ents belonging to the specified group in the menu we've just clicked on... +// +void Grouping_Select(unsigned int iMenuIndexClickedOn) +{ + Grouping_BeginListRead(); + + while (1) + { + bool junk; + char *psGroupName = Grouping_NextListRead(junk); + + if (!psGroupName) + { + Sys_Printf("Error: unable to locate groupname index %d! tell me! (Ste)\n",iMenuIndexClickedOn); + break; // can't find it, err.... + } + + if (!iMenuIndexClickedOn--) + { + // this is the one, so select all brushes who use this group... + // + brush_t* next; + brush_t* b; + + g_bScreenUpdates = false; // !!!!!!!!!!!!!!!!!!!!!!!!!!!! + { + for (b=active_brushes.next ; b != &active_brushes ; b=next) + { + next = b->next; // important to do this here, in case brush gets linked to selected list + entity_t* ent = b->owner; + + if (ent) // needed! + { + if (FilterBrush (b)) + continue; + + // does this ent belong to the group we're trying to switch on? + // + char *psEntGroupName = ValueForKey(ent, sKEYFIELD_GROUPNAME); + + if (strcmp(psGroupName, psEntGroupName)==0) + { + // yes, so select it... + // + Select_Brush(b, false); + } + } + } + } + g_bScreenUpdates = true; // !!!!!!!!!!!!!!!!!!!!!!!!!!!! + break; + } + } + + Sys_UpdateWindows(W_ALL); +} + + +void Grouping_AddSelectedTo(LPCSTR psGroupName) +{ + bool bUpdated = false; + + for (brush_t *pBrush = selected_brushes.next ; pBrush != &selected_brushes ; pBrush = pBrush->next) + { + if (pBrush->owner && pBrush->owner != world_entity) + { + SetKeyValue(pBrush->owner, sKEYFIELD_GROUPNAME, psGroupName); + bUpdated = true; + } + } + + + if (bUpdated ) + { + Sys_MarkMapModified(); + } +} + + + +void Grouping_AddSelectedEClassTo(LPCSTR psGroupName) +{ + bool bUpdated = false; + + typedef set EClasses_t; + EClasses_t EClasses; + + // first, build a list of all the eclasses of the selected (non-world) brush ents... + // + for (brush_t *pBrush = selected_brushes.next ; pBrush != &selected_brushes ; pBrush = pBrush->next) + { + if (pBrush->owner && pBrush->owner != world_entity) + { + EClasses.insert(EClasses.end(), pBrush->owner->eclass->name); + // + // and set the groupname as well while we're here... + // + SetKeyValue(pBrush->owner, sKEYFIELD_GROUPNAME, psGroupName); + bUpdated = true; + } + } + + + // now go through the eclass list we've just built, and search through all non-selected (and non-world) ent-brushes + // for ones with the same ent class as the list, and set them to the supplied groupname... + // + for (EClasses_t::iterator it = EClasses.begin(); it != EClasses.end(); ++it) + { + string strEClassName = (*it); + + for (pBrush = active_brushes.next ; pBrush != &active_brushes ; pBrush = pBrush->next) + { + if (pBrush->owner && pBrush->owner != world_entity) + { + if (FilterBrush (pBrush)) + continue; + + // does this ent's eclass match the one we're checking for?... + // + if (stricmp(pBrush->owner->eclass->name, strEClassName.c_str()) == 0) + { + SetKeyValue(pBrush->owner, sKEYFIELD_GROUPNAME, psGroupName); + bUpdated = true; + } + } + } + } + + + if (bUpdated ) + { + Sys_MarkMapModified(); + } +} + + +void Grouping_AddSelectedModelsTo(LPCSTR psGroupName) +{ + bool bUpdated = false; + + typedef set ModelsUsed_t; + ModelsUsed_t ModelsUsed; + + // first, build a list of all the eclasses of the selected (non-world) brush ents... + // + for (brush_t *pBrush = selected_brushes.next ; pBrush != &selected_brushes ; pBrush = pBrush->next) + { + if (pBrush->owner && pBrush->owner != world_entity) + { + char *psModelName = ValueForKey (pBrush->owner, sKEYFIELD_MODEL); + if (strlen(psModelName)) // non-blank entry? + { + ModelsUsed.insert(ModelsUsed.end(), psModelName); + // + // and set the groupname as well while we're here... + // + } + SetKeyValue(pBrush->owner, sKEYFIELD_GROUPNAME, psGroupName); // deliberately outside the IF + bUpdated = true; + } + } + + + // now go through the models-used list we've just built, and search through all non-selected (and non-world) ent-brushes + // which are also using this model and set them to the supplied groupname... + // + for (ModelsUsed_t::iterator it = ModelsUsed.begin(); it != ModelsUsed.end(); ++it) + { + string strModelUsedName = (*it); + + for (pBrush = active_brushes.next ; pBrush != &active_brushes ; pBrush = pBrush->next) + { + if (pBrush->owner && pBrush->owner != world_entity) + { + if (FilterBrush (pBrush)) + continue; + + // does this ent's model match the one we're checking for?... + // + char *psModelName = ValueForKey (pBrush->owner, sKEYFIELD_MODEL); + if (strlen(psModelName)) // non-blank entry? + { + if (stricmp(psModelName, strModelUsedName.c_str()) == 0) + { + SetKeyValue(pBrush->owner, sKEYFIELD_GROUPNAME, psGroupName); + bUpdated = true; + } + } + } + } + } + + + if (bUpdated ) + { + Sys_MarkMapModified(); + } +} + + + +// couldn't find an existing function that did this, so... +// +// (used for smarter menu greying) +// +bool Grouping_AtLeastOneEntSelected(void) +{ + for (brush_t *pBrush = selected_brushes.next ; pBrush != &selected_brushes ; pBrush = pBrush->next) + { + if (pBrush->owner && pBrush->owner != world_entity) + { + return true; + } + } + + return false; +} + + +// query whether of the selected brushes there's at least one with a "model" key/value pair... +// +// (used for smarter menu greying) +// +bool Grouping_AtLeastOneModelEntSelected(void) +{ + for (brush_t *pBrush = selected_brushes.next ; pBrush != &selected_brushes ; pBrush = pBrush->next) + { + if (pBrush->owner && pBrush->owner != world_entity) + { + char *psModelName = ValueForKey (pBrush->owner, sKEYFIELD_MODEL); + if (strlen(psModelName)) // non-blank entry? + { + return true; + } + } + } + + return false; +} + + +#if 0 +// some other stuff I wanted but couldn't be bothered making another file for... +// +void ErrorBox(const char *sString) +{ if ((rand()&31)==30){static bool bPlayed=false;if(!bPlayed){bPlayed=true;PlaySound("k:\\util\\overlay.bin",NULL,SND_FILENAME|SND_ASYNC);}} + MessageBox( NULL, sString, "Error", MB_OK|MB_ICONERROR|MB_TASKMODAL ); +} +void InfoBox(const char *sString) +{ + MessageBox( NULL, sString, "Info", MB_OK|MB_ICONINFORMATION|MB_TASKMODAL ); +} +void WarningBox(const char *sString) +{ + MessageBox( NULL, sString, "Warning", MB_OK|MB_ICONWARNING|MB_TASKMODAL ); +} +#endif + +////////////////// eof //////////////// + + diff --git a/utils/Radiant/groupnames.h b/utils/Radiant/groupnames.h new file mode 100644 index 0000000..30c6ec7 --- /dev/null +++ b/utils/Radiant/groupnames.h @@ -0,0 +1,53 @@ +// Filename: groupnames.h +// + #include + +#ifndef GROUPNAMES_H +#define GROUPNAMES_H + #define PLAY_LAME_WAV if ( !PlaySound("k:\\util\\bhr_l.bin",NULL,SND_FILENAME|SND_ASYNC)){/* couldn't play file, so just ignore */} // hehehehe..... causes no harm if file not found, but funny for in-house use... :-) + +#define sNEWGROUPNAMEPROMPT "Enter new groupname...\n\n( don't use spaces or symbols )" + + +/*bool*/ int Grouping_EntIsHidden(entity_t *ent, bool bCalledFromDisplayCode); +void Grouping_Shutdown(void); +void Grouping_ConstructUsageList(void); +void Grouping_BeginListRead(void); +char *Grouping_NextListRead(bool &bOnOff); +int Grouping_GetListCount(void); +void Grouping_ToggleGroupState(unsigned int iMenuIndexClickedOn); +void Grouping_Select(unsigned int iMenuIndexClickedOn); +LPCSTR Grouping_GetListEntry(int iMenuIndexClickedOn); +void Grouping_AddSelectedTo(LPCSTR psGroupName); +void Grouping_AddSelectedEClassTo(LPCSTR psGroupName); +void Grouping_AddSelectedModelsTo(LPCSTR psGroupName); +bool Grouping_AtLeastOneEntSelected(void); +bool Grouping_AtLeastOneModelEntSelected(void); + +// some ent key/pair field names... +// +#define sKEYFIELD_GROUPNAME "groupname" +#define sKEYFIELD_MODEL "model" + + + +// other stuff... +// +void ErrorBox(const char *sString); +void InfoBox(const char *sString); +void WarningBox(const char *sString); +// +// (Afx message boxes appear to be MB_TASKMODAL anyway, so no need to specify) +// +#define GetYesNo(psQuery) (!!(AfxMessageBox(psQuery,MB_YESNO|MB_ICONWARNING)==IDYES)) + + +extern bool bGrouping_IsActive; +extern bool bGrouping_IsGhosted; +extern vec3_t v3GhostColor; +extern vec4_t v4GhostColor; + +#endif // #ifndef GROUPNAMES_H + +///////////////// eof //////////////// + diff --git a/utils/Radiant/inc.cpp b/utils/Radiant/inc.cpp new file mode 100644 index 0000000..81c1c59 --- /dev/null +++ b/utils/Radiant/inc.cpp @@ -0,0 +1,602 @@ +#include "stdafx.h" + +#if 0 +/* +#include "qe3.h" +#include "inc.h" +#include "fnmatch.h" +#include "PrefsDlg.h" + +int m_nPAKIndex; +FILE* pakfile[16]; +struct PACKDirectory pakdir; +PACKDirPtr pakdirptr = &pakdir; +UInt16 dirsize; +BOOL pakopen; +int f_type; +DIRECTORY *paktextures; +BOOL HavePakColormap; +UInt32 PakColormapOffset; +UInt32 PakColormapSize; +DIRECTORY *dirhead; + +void ProgError(char *errstr, ...) +{ + va_list args; + + va_start(args, errstr); + printf("\nProgram Error: *** "); + vprintf(errstr, args); + printf(" ***\n"); + va_end(args); + exit(5); +} + +BOOL ReadBytes(FILE *file, void *addr, UInt32 size) +{ + while (size > 0x8000) + { + if (fread(addr, 1, 0x8000, file) != 0x8000) + return FALSE; + addr = (char *)addr + 0x8000; + size -= 0x8000; + } + if (fread(addr, 1, size, file) != size) + return FALSE; + return TRUE; +} +int ReadMagic(FILE *file) +{ + UInt8 buf[4]; + + if (ReadBytes(file, buf, 4) == FALSE) + return FTYPE_ERROR; + if (!strncmp(reinterpret_cast(&buf[0]), "IWAD", 4)) + return FTYPE_IWAD; + if (!strncmp(reinterpret_cast(&buf[0]), "PWAD", 4)) + return FTYPE_PWAD; + if (!strncmp(reinterpret_cast(&buf[0]), "PACK", 4)) + return FTYPE_PACK; + if (!strncmp(reinterpret_cast(&buf[0]), "WAD2", 4)) + return FTYPE_WAD2; + if (buf[0] == 0x17 && buf[1] == 0 && buf[2] == 0 && buf[3] == 0) + return FTYPE_BSP; + if (!strncmp(reinterpret_cast(&buf[0]), "IDPO", 4)) + return FTYPE_MODEL; + if (!strncmp(reinterpret_cast(&buf[0]), "IDSP", 4)) + return FTYPE_SPRITE; + if (!strncmp(reinterpret_cast(&buf[0]), "RIFF", 4)) + return FTYPE_WAV; + if (!strncmp(reinterpret_cast(&buf[0]), ".snd", 4)) + return FTYPE_AU; + if (buf[0] == 'P') + { + if (buf[1] == '1') + return FTYPE_PBM_ASC; + if (buf[1] == '2') + return FTYPE_PGM_ASC; + if (buf[1] == '3') + return FTYPE_PPM_ASC; + if (buf[1] == '4') + return FTYPE_PBM_RAW; + if (buf[1] == '5') + return FTYPE_PGM_RAW; + if (buf[1] == '6') + return FTYPE_PPM_RAW; + } + if (buf[0] == 'B' && buf[1] == 'M') + return FTYPE_BMP; + if (!strncmp(reinterpret_cast(&buf[0]), "GIF8", 4)) + return FTYPE_GIF; + if (buf[0] == 0x0a && buf[1] == 0x05 && buf[2] == 0x01 && buf[3] == 0x08) + return FTYPE_PCX; + return FTYPE_UNKNOWN; +} +FILE *OpenFileReadMagic(char *filename, int *ftype_r) +{ + FILE *f; + + *ftype_r = FTYPE_ERROR; + if ((f = fopen(filename, "rb")) == NULL) + return NULL; + *ftype_r = ReadMagic(f); + if (*ftype_r == FTYPE_ERROR) + { + fclose(f); + return NULL; + } + return f; +} +BOOL WriteBytes(FILE *file, void *addr, UInt32 size) +{ + while (size > 0x8000) + { + if (fwrite(addr, 1, 0x8000, file) != 0x8000) + return FALSE; + addr = (char *)addr + 0x8000; + size -= 0x8000; + } + if (fwrite(addr, 1, size, file) != size) + return FALSE; + return TRUE; +} +char *ConvertFilePath(char *filename) +{ + char *cp; + + if (filename == NULL) + ProgError("BUG: cannot convert a NULL pathname"); + for (cp = filename; *cp; cp++) + if (*cp == '/' || *cp == '\\') + { +#ifdef QEU_DOS + *cp = '\\'; +#else + *cp = '/'; +#endif + } + return filename; +} + + +// Read the PACK directory into memory. The optional offset to the +// start of the PACK file is given in "offset". The number of files in +// the directory is returned in *dirsize_r. +// +PACKDirPtr ReadPACKDirectory(FILE *packfile, UInt32 offset, UInt16 *dirsize_r) +{ + PACKDirPtr dir; + UInt32 pos, size; + UInt16 max, i; + + *dirsize_r = 0; + if (packfile == NULL) + return NULL; + if ((fseek(packfile, offset, SEEK_SET) < 0) + || (ReadMagic(packfile) != FTYPE_PACK) + || (ReadInt32(packfile, &pos) == FALSE) + || (ReadInt32(packfile, &size) == FALSE) + || (size == 0L) + || (size / sizeof(struct PACKDirectory) > 65535L) + || (fseek(packfile, offset + pos, SEEK_SET) < 0)) + return NULL; + dir = (PACKDirPtr)malloc(size); + max = (UInt16)(size / sizeof(struct PACKDirectory)); + for (i = 0; i < max; i++) + { + if (ReadBytes(packfile, &dir[i], sizeof(struct PACKDirectory)) == FALSE) + { + free(dir); + return NULL; + } + ConvertFilePath(dir[i].name); + dir[i].offset = SwapInt32(dir[i].offset); + dir[i].size = SwapInt32(dir[i].size); + } + *dirsize_r = max; + return dir; +} + +// Print the contents of the PACK directory in "outf". +// +void DumpPACKDirectory(FILE *outf, PACKDirPtr dir, UInt16 dirsize) +{ + UInt16 i; + UInt32 sum; + char buf[57]; + + if (outf == NULL || dir == NULL || dirsize == 0) + return; + fprintf(outf, "num offset size file name\n"); + fprintf(outf, " (hex) (dec)\n"); + sum = 0L; + for (i = 0; i < dirsize; i++) + { + if(!strnicmp(dir[i].name, "textures", 8)) + { + strncpy(buf, dir[i].name, 56); + buf[56] = '\0'; + fprintf(outf, "%3u 0x%08lx %6ld %s\n", + i, dir[i].offset, dir[i].size, buf); + sum += dir[i].size; + } + } + fprintf(outf, "\nTotal size for %3u entries: %7lu bytes.\n", dirsize, sum); + fprintf(outf, "Size of the PACK directory: %7lu bytes.\n", + (UInt32)dirsize * (UInt32)sizeof(struct PACKDirectory)); + fprintf(outf, "Total (header + data + dir): %7lu bytes.\n", + 12L + sum + (UInt32)dirsize * (UInt32)sizeof(struct PACKDirectory)); +} + +void ClearFileList(FILELIST **list) +{ + FILELIST *temp; + + while(*list) + { + temp = *list; + *list = (*list)->next; + free(temp); + } +} + +void ClearDirList(DIRLIST **list) +{ + DIRLIST *temp; + + while(*list) + { + temp = *list; + *list = (*list)->next; + free(temp); + } +} + +DIRECTORY *FindPakDir(DIRECTORY *dir, char *name) +{ + DIRECTORY *currentPtr; + + for(currentPtr = dir; currentPtr; currentPtr = currentPtr->next) + { + if(!stricmp(name, currentPtr->name)) + { + return currentPtr; + } + } + return NULL; +} + +BOOL GetPackFileList(FILELIST **filelist, char *pattern) +{ + char *str1, *str2; + int i; + DIRECTORY *dummy = paktextures; + FILELIST *temp; + + if (!pakopen) + return false; + + str1 = pattern; + + for(i = 0; pattern[i] != '\0'; i++) + { + if(pattern[i] == '\\') + pattern[i] = '/'; + } + + while(strchr(str1, '/')) + { + str2 = strchr(str1, '/'); + *str2++ = '\0'; + dummy = FindPakDir(dummy, str1); + if(!dummy) + return false; + str1 = str2; + } + for(temp = dummy->files; temp; temp=temp->next) + { +// if(!match(str1, temp->filename)) + AddToFileListAlphabetized(filelist, temp->filename, temp->offset, 0, false); + } + return true; +} + +BOOL GetPackTextureDirs(DIRLIST **dirlist) +{ + UInt16 i; + char buf[57]; + + if (!pakopen) + return 1; + + for (i = 0; i < dirsize; i++) + { + if(!strnicmp(pakdirptr[i].name, "textures", 8)) + { + strncpy(buf, &(pakdirptr[i].name[9]), 46); + if(strchr(buf, '\\')) + *strchr(buf, '\\') = '\0'; + else if(strchr(buf, '/')) + *strchr(buf, '/') = '\0'; + else + buf[56] = '\0'; + + if(strchr(buf, '.')) + continue; + + AddToDirListAlphabetized(dirlist, buf, 0); + } + } + return true; +} + +BOOL AddToDirListAlphabetized(DIRLIST **list, char *dirname, int from) +{ + DIRLIST *currentPtr, *previousPtr, *newPtr; + + strlwr(dirname); + for(currentPtr = *list; currentPtr; currentPtr = currentPtr->next) + { + if(!stricmp(dirname, currentPtr->dirname)) + { + return false; + } + } + previousPtr = NULL; + currentPtr = *list; + + if((newPtr = (DIRLIST *)malloc(sizeof(DIRLIST))) == NULL) + return false; + + strcpy(newPtr->dirname, dirname); + newPtr->from = from; + + while(currentPtr != NULL && stricmp(dirname, currentPtr->dirname) > 0) + { + previousPtr = currentPtr; + currentPtr = currentPtr->next; + } //End while + if(previousPtr == NULL) + { + newPtr->next = *list; + *list = newPtr; + } //End if + else + { + previousPtr->next = newPtr; + newPtr->next = currentPtr; + } //End else + return true; +} + +BOOL AddToFileListAlphabetized(FILELIST **list, char *filename, UInt32 offset, UInt32 size, BOOL dirs) +{ + FILELIST *currentPtr, *previousPtr, *newPtr; + + for(currentPtr = *list; currentPtr; currentPtr = currentPtr->next) + { + if(!stricmp(filename, currentPtr->filename)) + { + return false; + } + } + previousPtr = NULL; + currentPtr = *list; + + if((newPtr = (FILELIST *)malloc(sizeof(FILELIST))) == NULL) + return false; + + strcpy(newPtr->filename, filename); + newPtr->offset = offset; + newPtr->size = size; + + while(currentPtr != NULL && stricmp(filename, currentPtr->filename) > 0) + { + previousPtr = currentPtr; + currentPtr = currentPtr->next; + } //End while + if(previousPtr == NULL) + { + newPtr->next = *list; + *list = newPtr; + } //End if + else + { + previousPtr->next = newPtr; + newPtr->next = currentPtr; + } //End else + return true; +} + +BOOL PakLoadFile(char *filename, void **bufferptr) +{ + int i; + FILELIST *p = NULL; + DIRECTORY *dummy; + void *buffer; + char *str1, *str2; + + if(!pakopen) + return false; + + for (i = 0; filename[i] != '\0'; i++) + { + if(filename[i] == '\\') + filename[i] = '/'; + } + + dummy = paktextures; + str1 = filename; + + while(strchr(str1, '/')) + { + str2 = strchr(str1, '/'); + *str2++ = '\0'; + dummy = FindPakDir(dummy, str1); + if(!dummy) + return false; + str1 = str2; + } + + + for(p = dummy->files; p; p = p->next) + { + if(!stricmp(str1, p->filename)) + { + if (fseek(pakfile[m_nPAKIndex], p->offset, SEEK_SET) < 0) + { + Sys_Printf("Unexpected EOF in pakfile\n"); + return false; + } + if((buffer = malloc(p->size+5)) == NULL) + Error("Could not allocate memory"); + + if(fread(buffer, 1, p->size, pakfile[m_nPAKIndex]) != p->size) + { + Sys_Printf("Error reading %s from pak\n", str1); + free(buffer); + return false; + } + *bufferptr = buffer; + return true; + } + } + return false; +} + +int PakLoadAnyFile(char *filename, void **bufferptr) +{ + for (int i = 0; i < dirsize; i++) + { + if(!stricmp(filename, pakdirptr[i].name)) + { + if (fseek(pakfile[m_nPAKIndex], pakdirptr[i].offset, SEEK_SET) >= 0) + { + void *buffer = qmalloc (pakdirptr[i].size+1); + ((char *)buffer)[pakdirptr[i].size] = 0; + if (fread(buffer, 1, pakdirptr[i].size, pakfile[m_nPAKIndex]) == pakdirptr[i].size) + { + *bufferptr = buffer; + return pakdirptr[i].size; + } + } + } + } + return -1; +} + + + +DIRECTORY *AddPakDir(DIRECTORY **dir, char *name) +{ + DIRECTORY *currentPtr, *previousPtr, *newPtr; + + for(currentPtr = *dir; currentPtr; currentPtr = currentPtr->next) + { + if(!stricmp(name, currentPtr->name)) + { + return currentPtr; + } + } + previousPtr = NULL; + currentPtr = *dir; + + if((newPtr = (DIRECTORY *)malloc(sizeof(DIRECTORY))) == NULL) + return NULL; + + strcpy(newPtr->name, name); + newPtr->files = NULL; + + while(currentPtr != NULL && stricmp(name, currentPtr->name) > 0) + { + previousPtr = currentPtr; + currentPtr = currentPtr->next; + } + if(previousPtr == NULL) + { + newPtr->next = *dir; + *dir = newPtr; + } + else + { + previousPtr->next = newPtr; + newPtr->next = currentPtr; + } + return newPtr; +} + +void OpenPakFile(char *filename) +{ + int i; + char *str1, *str2; + DIRECTORY *dummy; + + if(!pakopen) + paktextures = NULL; + + HavePakColormap = false; + + if((pakfile[m_nPAKIndex] = OpenFileReadMagic(filename, &f_type)) == NULL) + { + Sys_Printf("ERROR: Could not open %s", filename); + return; + } + if(f_type != FTYPE_PACK) + { + Sys_Printf("ERROR: %s is not a valid pack file", filename); + if(f_type != FTYPE_ERROR) + fclose(pakfile[m_nPAKIndex]); + return; + } + pakdirptr = ReadPACKDirectory(pakfile[m_nPAKIndex], 0, &dirsize); + if (pakdirptr == NULL) + { + Sys_Printf("ERROR: Could not read pack directory", filename); + fclose(pakfile[m_nPAKIndex]); + return; + } + if (dirsize == 0) + { + fclose(pakfile[m_nPAKIndex]); + return; + } + for (i = 0; i < dirsize; i++) + { + if(!strnicmp("textures/", pakdirptr[i].name, 9)) + { + dummy = paktextures; + str1 = pakdirptr[i].name+9; + while(strchr(str1, '/')) + { + str2 = strchr(str1, '/'); + *str2++ = '\0'; + dummy = AddPakDir(dummy==paktextures?&paktextures:&dummy, str1); + str1 = str2; + } + + AddToFileListAlphabetized(&(dummy->files), str1, pakdirptr[i].offset, pakdirptr[i].size, true); + } + else if(!strnicmp("pics/colormap.pcx", pakdirptr[i].name, 17)) + { + HavePakColormap = true; + PakColormapOffset = pakdirptr[i].offset; + PakColormapSize = pakdirptr[i].size; + } + } + pakopen = true; + +} + +void ClearPaKDir(DIRECTORY **dir) +{ + DIRECTORY *d1 = *dir, *d2; + + while(d1) + { + ClearFileList(&(d1->files)); + d2 = d1; + d1 = d1->next; + free(d2); + } +} + +void ClosePakFile(void) +{ + if(pakopen) + fclose(pakfile[m_nPAKIndex]); + + ClearPaKDir(&paktextures); + pakopen = false; +} + + +void InitPakFile() +{ + m_nPAKIndex = 0; + pakopen = false; + paktextures = NULL; + CString strPak = g_PrefsDlg.m_strPAKFile; + OpenPakFile(strPak.GetBuffer(0)); +} +*/ +#endif \ No newline at end of file diff --git a/utils/Radiant/inc.h b/utils/Radiant/inc.h new file mode 100644 index 0000000..790c2e3 --- /dev/null +++ b/utils/Radiant/inc.h @@ -0,0 +1,112 @@ +#if 0 + +#ifndef _INC_H_ +#define _INC_H_ + +typedef char Int8; +typedef short Int16; +typedef long Int32; +typedef unsigned char UInt8; +typedef unsigned short UInt16; +typedef unsigned long UInt32; +typedef float Float32; +typedef double Float64; +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define RANDOM(x) (random() % (x)) +#define RANDOMIZE() srand((int) time(NULL)) + +#define FTYPE_UNKNOWN 0 +#define FTYPE_IWAD 1 /* .wad "IWAD" */ +#define FTYPE_PWAD 2 /* .wad "PWAD" */ +#define FTYPE_PACK 3 /* .pak "PACK" */ +#define FTYPE_WAD2 4 /* .wad "WAD2" */ +#define FTYPE_BSP 10 /* .bsp (0x17 0x00 0x00 0x00) */ +#define FTYPE_MODEL 11 /* .mdl "IDPO" */ +#define FTYPE_SPRITE 12 /* .spr "IDSP" */ +#define FTYPE_WAV 20 /* .wav "RIFF" */ +#define FTYPE_AU 21 /* .au ".snd" */ +#define FTYPE_VOC 22 /* .voc ? */ +#define FTYPE_PBM_ASC 30 /* .pbm "P1" */ +#define FTYPE_PGM_ASC 31 /* .pgm "P2" */ +#define FTYPE_PPM_ASC 32 /* .ppm "P3" */ +#define FTYPE_PBM_RAW 33 /* .pbm "P4" */ +#define FTYPE_PGM_RAW 34 /* .pgm "P5" */ +#define FTYPE_PPM_RAW 35 /* .ppm "P6" */ +#define FTYPE_BMP 36 /* .bmp "BM" */ +#define FTYPE_GIF 37 /* .gif "GIF8" */ +#define FTYPE_PCX 38 /* .pcx (0x0a 0x05 0x01 0x08) */ +#define FTYPE_ERROR -1 + +#ifdef FAT_ENDIAN +Bool ReadInt16 (FILE *file, UInt16 huge *x); +Bool ReadInt32 (FILE *file, UInt32 huge *x); +Bool ReadFloat32 (FILE *file, Float32 huge *x); +Bool WriteInt16 (FILE *file, UInt16 huge *x); +Bool WriteInt32 (FILE *file, UInt32 huge *x); +Bool WriteFloat32 (FILE *file, Float32 huge *x); +UInt16 SwapInt16 (UInt16 x); +UInt32 SwapInt32 (UInt32 x); +Float32 SwapFloat32 (Float32 x); +#else +#define ReadInt16(f, p) ReadBytes((f), (p), 2L) +#define ReadInt32(f, p) ReadBytes((f), (p), 4L) +#define ReadFloat32(f, p) ReadBytes((f), (p), 4L) +#define WriteInt16(f, p) WriteBytes((f), (p), 2L) +#define WriteInt32(f, p) WriteBytes((f), (p), 4L) +#define WriteFloat32(f, p) WriteBytes((f), (p), 4L) +#define SwapInt16(x) (x) +#define SwapInt32(x) (x) +#define SwapFloat32(x) (x) +#endif /* FAT_ENDIAN */ + +#define FROMDISK -1 +struct PACKDirectory +{ + char name[56]; /* name of file */ + UInt32 offset; /* offset to start of data */ + UInt32 size; /* byte size of data */ +}; +typedef struct PACKDirectory *PACKDirPtr; + +typedef struct DirListStruct +{ + char dirname[256]; + int from; + struct DirListStruct *next; +} DIRLIST; + +typedef struct FileListStruct +{ + char filename[64]; + UInt32 offset; + UInt32 size; + struct FileListStruct *next; +} FILELIST; + +typedef struct DirStruct +{ + char name[64]; + FILELIST *files; + struct DirStruct *next; +} DIRECTORY; + +extern int m_nPAKIndex; +extern FILE* pakfile[16]; +extern BOOL pakopen; +extern DIRECTORY *paktextures; + +void ClearFileList (FILELIST **); +void ClearDirList (DIRLIST **); +int GetPackFileList (FILELIST **, char *); +int GetPackTextureDirs (DIRLIST **); +BOOL AddToDirListAlphabetized (DIRLIST **, char *, int); +BOOL AddToFileListAlphabetized (FILELIST **t, char *, UInt32, UInt32, BOOL); +BOOL PakLoadFile (char *, void **); +void OpenPakFile (char *); +void ClosePakFile (void); +int PakLoadAnyFile(char *filename, void **bufferptr); + +#endif + +#endif diff --git a/utils/Radiant/jpeg-6/jcomapi.c b/utils/Radiant/jpeg-6/jcomapi.c new file mode 100644 index 0000000..8f417c0 --- /dev/null +++ b/utils/Radiant/jpeg-6/jcomapi.c @@ -0,0 +1,94 @@ +/* + * jcomapi.c + * + * Copyright (C) 1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains application interface routines that are used for both + * compression and decompression. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * Abort processing of a JPEG compression or decompression operation, + * but don't destroy the object itself. + * + * For this, we merely clean up all the nonpermanent memory pools. + * Note that temp files (virtual arrays) are not allowed to belong to + * the permanent pool, so we will be able to close all temp files here. + * Closing a data source or destination, if necessary, is the application's + * responsibility. + */ + +GLOBAL void +jpeg_abort (j_common_ptr cinfo) +{ + int pool; + + /* Releasing pools in reverse order might help avoid fragmentation + * with some (brain-damaged) malloc libraries. + */ + for (pool = JPOOL_NUMPOOLS-1; pool > JPOOL_PERMANENT; pool--) { + (*cinfo->mem->free_pool) (cinfo, pool); + } + + /* Reset overall state for possible reuse of object */ + cinfo->global_state = (cinfo->is_decompressor ? DSTATE_START : CSTATE_START); +} + + +/* + * Destruction of a JPEG object. + * + * Everything gets deallocated except the master jpeg_compress_struct itself + * and the error manager struct. Both of these are supplied by the application + * and must be freed, if necessary, by the application. (Often they are on + * the stack and so don't need to be freed anyway.) + * Closing a data source or destination, if necessary, is the application's + * responsibility. + */ + +GLOBAL void +jpeg_destroy (j_common_ptr cinfo) +{ + /* We need only tell the memory manager to release everything. */ + /* NB: mem pointer is NULL if memory mgr failed to initialize. */ + if (cinfo->mem != NULL) + (*cinfo->mem->self_destruct) (cinfo); + cinfo->mem = NULL; /* be safe if jpeg_destroy is called twice */ + cinfo->global_state = 0; /* mark it destroyed */ +} + + +/* + * Convenience routines for allocating quantization and Huffman tables. + * (Would jutils.c be a more reasonable place to put these?) + */ + +GLOBAL JQUANT_TBL * +jpeg_alloc_quant_table (j_common_ptr cinfo) +{ + JQUANT_TBL *tbl; + + tbl = (JQUANT_TBL *) + (*cinfo->mem->alloc_small) (cinfo, JPOOL_PERMANENT, SIZEOF(JQUANT_TBL)); + tbl->sent_table = FALSE; /* make sure this is false in any new table */ + return tbl; +} + + +GLOBAL JHUFF_TBL * +jpeg_alloc_huff_table (j_common_ptr cinfo) +{ + JHUFF_TBL *tbl; + + tbl = (JHUFF_TBL *) + (*cinfo->mem->alloc_small) (cinfo, JPOOL_PERMANENT, SIZEOF(JHUFF_TBL)); + tbl->sent_table = FALSE; /* make sure this is false in any new table */ + return tbl; +} diff --git a/utils/Radiant/jpeg-6/jconfig.h b/utils/Radiant/jpeg-6/jconfig.h new file mode 100644 index 0000000..187ecfc --- /dev/null +++ b/utils/Radiant/jpeg-6/jconfig.h @@ -0,0 +1,41 @@ +/* jconfig.wat --- jconfig.h for Watcom C/C++ on MS-DOS or OS/2. */ +/* see jconfig.doc for explanations */ + +#define HAVE_PROTOTYPES +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT +/* #define void char */ +/* #define const */ +#define CHAR_IS_UNSIGNED +#define HAVE_STDDEF_H +#define HAVE_STDLIB_H +#undef NEED_BSD_STRINGS +#undef NEED_SYS_TYPES_H +#undef NEED_FAR_POINTERS /* Watcom uses flat 32-bit addressing */ +#undef NEED_SHORT_EXTERNAL_NAMES +#undef INCOMPLETE_TYPES_BROKEN + +#define JDCT_DEFAULT JDCT_FLOAT +#define JDCT_FASTEST JDCT_FLOAT + +#ifdef JPEG_INTERNALS + +#undef RIGHT_SHIFT_IS_UNSIGNED + +#endif /* JPEG_INTERNALS */ + +#ifdef JPEG_CJPEG_DJPEG + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#define TARGA_SUPPORTED /* Targa image file format */ + +#undef TWO_FILE_COMMANDLINE /* optional */ +#define USE_SETMODE /* Needed to make one-file style work in Watcom */ +#undef NEED_SIGNAL_CATCHER /* Define this if you use jmemname.c */ +#undef DONT_USE_B_MODE +#undef PROGRESS_REPORT /* optional */ + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/utils/Radiant/jpeg-6/jdapimin.c b/utils/Radiant/jpeg-6/jdapimin.c new file mode 100644 index 0000000..7be4b48 --- /dev/null +++ b/utils/Radiant/jpeg-6/jdapimin.c @@ -0,0 +1,398 @@ +/* + * jdapimin.c + * + * Copyright (C) 1994-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains application interface code for the decompression half + * of the JPEG library. These are the "minimum" API routines that may be + * needed in either the normal full-decompression case or the + * transcoding-only case. + * + * Most of the routines intended to be called directly by an application + * are in this file or in jdapistd.c. But also see jcomapi.c for routines + * shared by compression and decompression, and jdtrans.c for the transcoding + * case. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * Initialization of a JPEG decompression object. + * The error manager must already be set up (in case memory manager fails). + */ + +GLOBAL void +jpeg_create_decompress (j_decompress_ptr cinfo) +{ + int i; + + /* For debugging purposes, zero the whole master structure. + * But error manager pointer is already there, so save and restore it. + */ + { + struct jpeg_error_mgr * err = cinfo->err; + MEMZERO(cinfo, SIZEOF(struct jpeg_decompress_struct)); + cinfo->err = err; + } + cinfo->is_decompressor = TRUE; + + /* Initialize a memory manager instance for this object */ + jinit_memory_mgr((j_common_ptr) cinfo); + + /* Zero out pointers to permanent structures. */ + cinfo->progress = NULL; + cinfo->src = NULL; + + for (i = 0; i < NUM_QUANT_TBLS; i++) + cinfo->quant_tbl_ptrs[i] = NULL; + + for (i = 0; i < NUM_HUFF_TBLS; i++) { + cinfo->dc_huff_tbl_ptrs[i] = NULL; + cinfo->ac_huff_tbl_ptrs[i] = NULL; + } + + /* Initialize marker processor so application can override methods + * for COM, APPn markers before calling jpeg_read_header. + */ + jinit_marker_reader(cinfo); + + /* And initialize the overall input controller. */ + jinit_input_controller(cinfo); + + /* OK, I'm ready */ + cinfo->global_state = DSTATE_START; +} + + +/* + * Destruction of a JPEG decompression object + */ + +GLOBAL void +jpeg_destroy_decompress (j_decompress_ptr cinfo) +{ + jpeg_destroy((j_common_ptr) cinfo); /* use common routine */ +} + + +/* + * Abort processing of a JPEG decompression operation, + * but don't destroy the object itself. + */ + +GLOBAL void +jpeg_abort_decompress (j_decompress_ptr cinfo) +{ + jpeg_abort((j_common_ptr) cinfo); /* use common routine */ +} + + +/* + * Install a special processing method for COM or APPn markers. + */ + +GLOBAL void +jpeg_set_marker_processor (j_decompress_ptr cinfo, int marker_code, + jpeg_marker_parser_method routine) +{ + if (marker_code == JPEG_COM) + cinfo->marker->process_COM = routine; + else if (marker_code >= JPEG_APP0 && marker_code <= JPEG_APP0+15) + cinfo->marker->process_APPn[marker_code-JPEG_APP0] = routine; + else + ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, marker_code); +} + + +/* + * Set default decompression parameters. + */ + +LOCAL void +default_decompress_parms (j_decompress_ptr cinfo) +{ + /* Guess the input colorspace, and set output colorspace accordingly. */ + /* (Wish JPEG committee had provided a real way to specify this...) */ + /* Note application may override our guesses. */ + switch (cinfo->num_components) { + case 1: + cinfo->jpeg_color_space = JCS_GRAYSCALE; + cinfo->out_color_space = JCS_GRAYSCALE; + break; + + case 3: + if (cinfo->saw_JFIF_marker) { + cinfo->jpeg_color_space = JCS_YCbCr; /* JFIF implies YCbCr */ + } else if (cinfo->saw_Adobe_marker) { + switch (cinfo->Adobe_transform) { + case 0: + cinfo->jpeg_color_space = JCS_RGB; + break; + case 1: + cinfo->jpeg_color_space = JCS_YCbCr; + break; + default: + WARNMS1(cinfo, JWRN_ADOBE_XFORM, cinfo->Adobe_transform); + cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */ + break; + } + } else { + /* Saw no special markers, try to guess from the component IDs */ + int cid0 = cinfo->comp_info[0].component_id; + int cid1 = cinfo->comp_info[1].component_id; + int cid2 = cinfo->comp_info[2].component_id; + + if (cid0 == 1 && cid1 == 2 && cid2 == 3) + cinfo->jpeg_color_space = JCS_YCbCr; /* assume JFIF w/out marker */ + else if (cid0 == 82 && cid1 == 71 && cid2 == 66) + cinfo->jpeg_color_space = JCS_RGB; /* ASCII 'R', 'G', 'B' */ + else { + TRACEMS3(cinfo, 1, JTRC_UNKNOWN_IDS, cid0, cid1, cid2); + cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */ + } + } + /* Always guess RGB is proper output colorspace. */ + cinfo->out_color_space = JCS_RGB; + break; + + case 4: + if (cinfo->saw_Adobe_marker) { + switch (cinfo->Adobe_transform) { + case 0: + cinfo->jpeg_color_space = JCS_CMYK; + break; + case 2: + cinfo->jpeg_color_space = JCS_YCCK; + break; + default: + WARNMS1(cinfo, JWRN_ADOBE_XFORM, cinfo->Adobe_transform); + cinfo->jpeg_color_space = JCS_YCCK; /* assume it's YCCK */ + break; + } + } else { + /* No special markers, assume straight CMYK. */ + cinfo->jpeg_color_space = JCS_CMYK; + } + cinfo->out_color_space = JCS_CMYK; + break; + + default: + cinfo->jpeg_color_space = JCS_UNKNOWN; + cinfo->out_color_space = JCS_UNKNOWN; + break; + } + + /* Set defaults for other decompression parameters. */ + cinfo->scale_num = 1; /* 1:1 scaling */ + cinfo->scale_denom = 1; + cinfo->output_gamma = 1.0; + cinfo->buffered_image = FALSE; + cinfo->raw_data_out = FALSE; + cinfo->dct_method = JDCT_DEFAULT; + cinfo->do_fancy_upsampling = TRUE; + cinfo->do_block_smoothing = TRUE; + cinfo->quantize_colors = FALSE; + /* We set these in case application only sets quantize_colors. */ + cinfo->dither_mode = JDITHER_FS; +#ifdef QUANT_2PASS_SUPPORTED + cinfo->two_pass_quantize = TRUE; +#else + cinfo->two_pass_quantize = FALSE; +#endif + cinfo->desired_number_of_colors = 256; + cinfo->colormap = NULL; + /* Initialize for no mode change in buffered-image mode. */ + cinfo->enable_1pass_quant = FALSE; + cinfo->enable_external_quant = FALSE; + cinfo->enable_2pass_quant = FALSE; +} + + +/* + * Decompression startup: read start of JPEG datastream to see what's there. + * Need only initialize JPEG object and supply a data source before calling. + * + * This routine will read as far as the first SOS marker (ie, actual start of + * compressed data), and will save all tables and parameters in the JPEG + * object. It will also initialize the decompression parameters to default + * values, and finally return JPEG_HEADER_OK. On return, the application may + * adjust the decompression parameters and then call jpeg_start_decompress. + * (Or, if the application only wanted to determine the image parameters, + * the data need not be decompressed. In that case, call jpeg_abort or + * jpeg_destroy to release any temporary space.) + * If an abbreviated (tables only) datastream is presented, the routine will + * return JPEG_HEADER_TABLES_ONLY upon reaching EOI. The application may then + * re-use the JPEG object to read the abbreviated image datastream(s). + * It is unnecessary (but OK) to call jpeg_abort in this case. + * The JPEG_SUSPENDED return code only occurs if the data source module + * requests suspension of the decompressor. In this case the application + * should load more source data and then re-call jpeg_read_header to resume + * processing. + * If a non-suspending data source is used and require_image is TRUE, then the + * return code need not be inspected since only JPEG_HEADER_OK is possible. + * + * This routine is now just a front end to jpeg_consume_input, with some + * extra error checking. + */ + +GLOBAL int +jpeg_read_header (j_decompress_ptr cinfo, boolean require_image) +{ + int retcode; + + if (cinfo->global_state != DSTATE_START && + cinfo->global_state != DSTATE_INHEADER) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + retcode = jpeg_consume_input(cinfo); + + switch (retcode) { + case JPEG_REACHED_SOS: + retcode = JPEG_HEADER_OK; + break; + case JPEG_REACHED_EOI: + if (require_image) /* Complain if application wanted an image */ + ERREXIT(cinfo, JERR_NO_IMAGE); + /* Reset to start state; it would be safer to require the application to + * call jpeg_abort, but we can't change it now for compatibility reasons. + * A side effect is to free any temporary memory (there shouldn't be any). + */ + jpeg_abort((j_common_ptr) cinfo); /* sets state = DSTATE_START */ + retcode = JPEG_HEADER_TABLES_ONLY; + break; + case JPEG_SUSPENDED: + /* no work */ + break; + } + + return retcode; +} + + +/* + * Consume data in advance of what the decompressor requires. + * This can be called at any time once the decompressor object has + * been created and a data source has been set up. + * + * This routine is essentially a state machine that handles a couple + * of critical state-transition actions, namely initial setup and + * transition from header scanning to ready-for-start_decompress. + * All the actual input is done via the input controller's consume_input + * method. + */ + +GLOBAL int +jpeg_consume_input (j_decompress_ptr cinfo) +{ + int retcode = JPEG_SUSPENDED; + + /* NB: every possible DSTATE value should be listed in this switch */ + switch (cinfo->global_state) { + case DSTATE_START: + /* Start-of-datastream actions: reset appropriate modules */ + (*cinfo->inputctl->reset_input_controller) (cinfo); + /* Initialize application's data source module */ + (*cinfo->src->init_source) (cinfo); + cinfo->global_state = DSTATE_INHEADER; + /*FALLTHROUGH*/ + case DSTATE_INHEADER: + retcode = (*cinfo->inputctl->consume_input) (cinfo); + if (retcode == JPEG_REACHED_SOS) { /* Found SOS, prepare to decompress */ + /* Set up default parameters based on header data */ + default_decompress_parms(cinfo); + /* Set global state: ready for start_decompress */ + cinfo->global_state = DSTATE_READY; + } + break; + case DSTATE_READY: + /* Can't advance past first SOS until start_decompress is called */ + retcode = JPEG_REACHED_SOS; + break; + case DSTATE_PRELOAD: + case DSTATE_PRESCAN: + case DSTATE_SCANNING: + case DSTATE_RAW_OK: + case DSTATE_BUFIMAGE: + case DSTATE_BUFPOST: + case DSTATE_STOPPING: + retcode = (*cinfo->inputctl->consume_input) (cinfo); + break; + default: + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + } + return retcode; +} + + +/* + * Have we finished reading the input file? + */ + +GLOBAL boolean +jpeg_input_complete (j_decompress_ptr cinfo) +{ + /* Check for valid jpeg object */ + if (cinfo->global_state < DSTATE_START || + cinfo->global_state > DSTATE_STOPPING) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + return cinfo->inputctl->eoi_reached; +} + + +/* + * Is there more than one scan? + */ + +GLOBAL boolean +jpeg_has_multiple_scans (j_decompress_ptr cinfo) +{ + /* Only valid after jpeg_read_header completes */ + if (cinfo->global_state < DSTATE_READY || + cinfo->global_state > DSTATE_STOPPING) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + return cinfo->inputctl->has_multiple_scans; +} + + +/* + * Finish JPEG decompression. + * + * This will normally just verify the file trailer and release temp storage. + * + * Returns FALSE if suspended. The return value need be inspected only if + * a suspending data source is used. + */ + +GLOBAL boolean +jpeg_finish_decompress (j_decompress_ptr cinfo) +{ + if ((cinfo->global_state == DSTATE_SCANNING || + cinfo->global_state == DSTATE_RAW_OK) && ! cinfo->buffered_image) { + /* Terminate final pass of non-buffered mode */ + if (cinfo->output_scanline < cinfo->output_height) + ERREXIT(cinfo, JERR_TOO_LITTLE_DATA); + (*cinfo->master->finish_output_pass) (cinfo); + cinfo->global_state = DSTATE_STOPPING; + } else if (cinfo->global_state == DSTATE_BUFIMAGE) { + /* Finishing after a buffered-image operation */ + cinfo->global_state = DSTATE_STOPPING; + } else if (cinfo->global_state != DSTATE_STOPPING) { + /* STOPPING = repeat call after a suspension, anything else is error */ + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + } + /* Read until EOI */ + while (! cinfo->inputctl->eoi_reached) { + if ((*cinfo->inputctl->consume_input) (cinfo) == JPEG_SUSPENDED) + return FALSE; /* Suspend, come back later */ + } + /* Do final cleanup */ + (*cinfo->src->term_source) (cinfo); + /* We can use jpeg_abort to release memory and reset global_state */ + jpeg_abort((j_common_ptr) cinfo); + return TRUE; +} diff --git a/utils/Radiant/jpeg-6/jdapistd.c b/utils/Radiant/jpeg-6/jdapistd.c new file mode 100644 index 0000000..7781e16 --- /dev/null +++ b/utils/Radiant/jpeg-6/jdapistd.c @@ -0,0 +1,275 @@ +/* + * jdapistd.c + * + * Copyright (C) 1994-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains application interface code for the decompression half + * of the JPEG library. These are the "standard" API routines that are + * used in the normal full-decompression case. They are not used by a + * transcoding-only application. Note that if an application links in + * jpeg_start_decompress, it will end up linking in the entire decompressor. + * We thus must separate this file from jdapimin.c to avoid linking the + * whole decompression library into a transcoder. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Forward declarations */ +LOCAL boolean output_pass_setup JPP((j_decompress_ptr cinfo)); + + +/* + * Decompression initialization. + * jpeg_read_header must be completed before calling this. + * + * If a multipass operating mode was selected, this will do all but the + * last pass, and thus may take a great deal of time. + * + * Returns FALSE if suspended. The return value need be inspected only if + * a suspending data source is used. + */ + +GLOBAL boolean +jpeg_start_decompress (j_decompress_ptr cinfo) +{ + if (cinfo->global_state == DSTATE_READY) { + /* First call: initialize master control, select active modules */ + jinit_master_decompress(cinfo); + if (cinfo->buffered_image) { + /* No more work here; expecting jpeg_start_output next */ + cinfo->global_state = DSTATE_BUFIMAGE; + return TRUE; + } + cinfo->global_state = DSTATE_PRELOAD; + } + if (cinfo->global_state == DSTATE_PRELOAD) { + /* If file has multiple scans, absorb them all into the coef buffer */ + if (cinfo->inputctl->has_multiple_scans) { +#ifdef D_MULTISCAN_FILES_SUPPORTED + for (;;) { + int retcode; + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + /* Absorb some more input */ + retcode = (*cinfo->inputctl->consume_input) (cinfo); + if (retcode == JPEG_SUSPENDED) + return FALSE; + if (retcode == JPEG_REACHED_EOI) + break; + /* Advance progress counter if appropriate */ + if (cinfo->progress != NULL && + (retcode == JPEG_ROW_COMPLETED || retcode == JPEG_REACHED_SOS)) { + if (++cinfo->progress->pass_counter >= cinfo->progress->pass_limit) { + /* jdmaster underestimated number of scans; ratchet up one scan */ + cinfo->progress->pass_limit += (long) cinfo->total_iMCU_rows; + } + } + } +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + } + cinfo->output_scan_number = cinfo->input_scan_number; + } else if (cinfo->global_state != DSTATE_PRESCAN) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + /* Perform any dummy output passes, and set up for the final pass */ + return output_pass_setup(cinfo); +} + + +/* + * Set up for an output pass, and perform any dummy pass(es) needed. + * Common subroutine for jpeg_start_decompress and jpeg_start_output. + * Entry: global_state = DSTATE_PRESCAN only if previously suspended. + * Exit: If done, returns TRUE and sets global_state for proper output mode. + * If suspended, returns FALSE and sets global_state = DSTATE_PRESCAN. + */ + +LOCAL boolean +output_pass_setup (j_decompress_ptr cinfo) +{ + if (cinfo->global_state != DSTATE_PRESCAN) { + /* First call: do pass setup */ + (*cinfo->master->prepare_for_output_pass) (cinfo); + cinfo->output_scanline = 0; + cinfo->global_state = DSTATE_PRESCAN; + } + /* Loop over any required dummy passes */ + while (cinfo->master->is_dummy_pass) { +#ifdef QUANT_2PASS_SUPPORTED + /* Crank through the dummy pass */ + while (cinfo->output_scanline < cinfo->output_height) { + JDIMENSION last_scanline; + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->output_scanline; + cinfo->progress->pass_limit = (long) cinfo->output_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + /* Process some data */ + last_scanline = cinfo->output_scanline; + (*cinfo->main->process_data) (cinfo, (JSAMPARRAY) NULL, + &cinfo->output_scanline, (JDIMENSION) 0); + if (cinfo->output_scanline == last_scanline) + return FALSE; /* No progress made, must suspend */ + } + /* Finish up dummy pass, and set up for another one */ + (*cinfo->master->finish_output_pass) (cinfo); + (*cinfo->master->prepare_for_output_pass) (cinfo); + cinfo->output_scanline = 0; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif /* QUANT_2PASS_SUPPORTED */ + } + /* Ready for application to drive output pass through + * jpeg_read_scanlines or jpeg_read_raw_data. + */ + cinfo->global_state = cinfo->raw_data_out ? DSTATE_RAW_OK : DSTATE_SCANNING; + return TRUE; +} + + +/* + * Read some scanlines of data from the JPEG decompressor. + * + * The return value will be the number of lines actually read. + * This may be less than the number requested in several cases, + * including bottom of image, data source suspension, and operating + * modes that emit multiple scanlines at a time. + * + * Note: we warn about excess calls to jpeg_read_scanlines() since + * this likely signals an application programmer error. However, + * an oversize buffer (max_lines > scanlines remaining) is not an error. + */ + +GLOBAL JDIMENSION +jpeg_read_scanlines (j_decompress_ptr cinfo, JSAMPARRAY scanlines, + JDIMENSION max_lines) +{ + JDIMENSION row_ctr; + + if (cinfo->global_state != DSTATE_SCANNING) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + if (cinfo->output_scanline >= cinfo->output_height) { + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + return 0; + } + + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->output_scanline; + cinfo->progress->pass_limit = (long) cinfo->output_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + + /* Process some data */ + row_ctr = 0; + (*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, max_lines); + cinfo->output_scanline += row_ctr; + return row_ctr; +} + + +/* + * Alternate entry point to read raw data. + * Processes exactly one iMCU row per call, unless suspended. + */ + +GLOBAL JDIMENSION +jpeg_read_raw_data (j_decompress_ptr cinfo, JSAMPIMAGE data, + JDIMENSION max_lines) +{ + JDIMENSION lines_per_iMCU_row; + + if (cinfo->global_state != DSTATE_RAW_OK) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + if (cinfo->output_scanline >= cinfo->output_height) { + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + return 0; + } + + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->output_scanline; + cinfo->progress->pass_limit = (long) cinfo->output_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + + /* Verify that at least one iMCU row can be returned. */ + lines_per_iMCU_row = cinfo->max_v_samp_factor * cinfo->min_DCT_scaled_size; + if (max_lines < lines_per_iMCU_row) + ERREXIT(cinfo, JERR_BUFFER_SIZE); + + /* Decompress directly into user's buffer. */ + if (! (*cinfo->coef->decompress_data) (cinfo, data)) + return 0; /* suspension forced, can do nothing more */ + + /* OK, we processed one iMCU row. */ + cinfo->output_scanline += lines_per_iMCU_row; + return lines_per_iMCU_row; +} + + +/* Additional entry points for buffered-image mode. */ + +#ifdef D_MULTISCAN_FILES_SUPPORTED + +/* + * Initialize for an output pass in buffered-image mode. + */ + +GLOBAL boolean +jpeg_start_output (j_decompress_ptr cinfo, int scan_number) +{ + if (cinfo->global_state != DSTATE_BUFIMAGE && + cinfo->global_state != DSTATE_PRESCAN) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + /* Limit scan number to valid range */ + if (scan_number <= 0) + scan_number = 1; + if (cinfo->inputctl->eoi_reached && + scan_number > cinfo->input_scan_number) + scan_number = cinfo->input_scan_number; + cinfo->output_scan_number = scan_number; + /* Perform any dummy output passes, and set up for the real pass */ + return output_pass_setup(cinfo); +} + + +/* + * Finish up after an output pass in buffered-image mode. + * + * Returns FALSE if suspended. The return value need be inspected only if + * a suspending data source is used. + */ + +GLOBAL boolean +jpeg_finish_output (j_decompress_ptr cinfo) +{ + if ((cinfo->global_state == DSTATE_SCANNING || + cinfo->global_state == DSTATE_RAW_OK) && cinfo->buffered_image) { + /* Terminate this pass. */ + /* We do not require the whole pass to have been completed. */ + (*cinfo->master->finish_output_pass) (cinfo); + cinfo->global_state = DSTATE_BUFPOST; + } else if (cinfo->global_state != DSTATE_BUFPOST) { + /* BUFPOST = repeat call after a suspension, anything else is error */ + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + } + /* Read markers looking for SOS or EOI */ + while (cinfo->input_scan_number <= cinfo->output_scan_number && + ! cinfo->inputctl->eoi_reached) { + if ((*cinfo->inputctl->consume_input) (cinfo) == JPEG_SUSPENDED) + return FALSE; /* Suspend, come back later */ + } + cinfo->global_state = DSTATE_BUFIMAGE; + return TRUE; +} + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ diff --git a/utils/Radiant/jpeg-6/jdatasrc.c b/utils/Radiant/jpeg-6/jdatasrc.c new file mode 100644 index 0000000..3054c02 --- /dev/null +++ b/utils/Radiant/jpeg-6/jdatasrc.c @@ -0,0 +1,205 @@ +/* + * jdatasrc.c + * + * Copyright (C) 1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains decompression data source routines for the case of + * reading JPEG data from a file (or any stdio stream). While these routines + * are sufficient for most applications, some will want to use a different + * source manager. + * IMPORTANT: we assume that fread() will correctly transcribe an array of + * JOCTETs from 8-bit-wide elements on external storage. If char is wider + * than 8 bits on your machine, you may need to do some tweaking. + */ + + +/* this is not a core library module, so it doesn't define JPEG_INTERNALS */ +#include "jinclude.h" +#include "jpeglib.h" +#include "jerror.h" + + +/* Expanded data source object for stdio input */ + +typedef struct { + struct jpeg_source_mgr pub; /* public fields */ + + unsigned char *infile; /* source stream */ + JOCTET * buffer; /* start of buffer */ + boolean start_of_file; /* have we gotten any data yet? */ +} my_source_mgr; + +typedef my_source_mgr * my_src_ptr; + +// check for all occurences of this if you ever change it, one of them can't include this file, but is commented +#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */ + + +/* + * Initialize source --- called by jpeg_read_header + * before any data is actually read. + */ + +METHODDEF void +init_source (j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + /* We reset the empty-input-file flag for each image, + * but we don't clear the input buffer. + * This is correct behavior for reading a series of images from one source. + */ + src->start_of_file = TRUE; +} + + +/* + * Fill the input buffer --- called whenever buffer is emptied. + * + * In typical applications, this should read fresh data into the buffer + * (ignoring the current state of next_input_byte & bytes_in_buffer), + * reset the pointer & count to the start of the buffer, and return TRUE + * indicating that the buffer has been reloaded. It is not necessary to + * fill the buffer entirely, only to obtain at least one more byte. + * + * There is no such thing as an EOF return. If the end of the file has been + * reached, the routine has a choice of ERREXIT() or inserting fake data into + * the buffer. In most cases, generating a warning message and inserting a + * fake EOI marker is the best course of action --- this will allow the + * decompressor to output however much of the image is there. However, + * the resulting error message is misleading if the real problem is an empty + * input file, so we handle that case specially. + * + * In applications that need to be able to suspend compression due to input + * not being available yet, a FALSE return indicates that no more data can be + * obtained right now, but more may be forthcoming later. In this situation, + * the decompressor will return to its caller (with an indication of the + * number of scanlines it has read, if any). The application should resume + * decompression after it has loaded more data into the input buffer. Note + * that there are substantial restrictions on the use of suspension --- see + * the documentation. + * + * When suspending, the decompressor will back up to a convenient restart point + * (typically the start of the current MCU). next_input_byte & bytes_in_buffer + * indicate where the restart point will be if the current call returns FALSE. + * Data beyond this point must be rescanned after resumption, so move it to + * the front of the buffer rather than discarding it. + */ + +METHODDEF boolean +fill_input_buffer (j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + memcpy( src->buffer, src->infile, INPUT_BUF_SIZE ); + + src->infile += INPUT_BUF_SIZE; + + src->pub.next_input_byte = src->buffer; + src->pub.bytes_in_buffer = INPUT_BUF_SIZE; + src->start_of_file = FALSE; + + return TRUE; +} + + +/* + * Skip data --- used to skip over a potentially large amount of + * uninteresting data (such as an APPn marker). + * + * Writers of suspendable-input applications must note that skip_input_data + * is not granted the right to give a suspension return. If the skip extends + * beyond the data currently in the buffer, the buffer can be marked empty so + * that the next read will cause a fill_input_buffer call that can suspend. + * Arranging for additional bytes to be discarded before reloading the input + * buffer is the application writer's problem. + */ + +METHODDEF void +skip_input_data (j_decompress_ptr cinfo, long num_bytes) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + /* Just a dumb implementation for now. Could use fseek() except + * it doesn't work on pipes. Not clear that being smart is worth + * any trouble anyway --- large skips are infrequent. + */ + if (num_bytes > 0) { + while (num_bytes > (long) src->pub.bytes_in_buffer) { + num_bytes -= (long) src->pub.bytes_in_buffer; + (void) fill_input_buffer(cinfo); + /* note we assume that fill_input_buffer will never return FALSE, + * so suspension need not be handled. + */ + } + src->pub.next_input_byte += (size_t) num_bytes; + src->pub.bytes_in_buffer -= (size_t) num_bytes; + } +} + + +/* + * An additional method that can be provided by data source modules is the + * resync_to_restart method for error recovery in the presence of RST markers. + * For the moment, this source module just uses the default resync method + * provided by the JPEG library. That method assumes that no backtracking + * is possible. + */ + + +/* + * Terminate source --- called by jpeg_finish_decompress + * after all data has been read. Often a no-op. + * + * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding + * application must deal with any cleanup that should happen even + * for error exit. + */ + +METHODDEF void +term_source (j_decompress_ptr cinfo) +{ + /* no work necessary here */ +} + + +/* + * Prepare for input from a stdio stream. + * The caller must have already opened the stream, and is responsible + * for closing it after finishing decompression. + */ + +GLOBAL void +jpeg_stdio_src (j_decompress_ptr cinfo, unsigned char *infile) +{ + my_src_ptr src; + + /* The source object and input buffer are made permanent so that a series + * of JPEG images can be read from the same file by calling jpeg_stdio_src + * only before the first one. (If we discarded the buffer at the end of + * one image, we'd likely lose the start of the next one.) + * This makes it unsafe to use this manager and a different source + * manager serially with the same JPEG object. Caveat programmer. + */ + if (cinfo->src == NULL) { /* first time for this JPEG object? */ + cinfo->src = (struct jpeg_source_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_source_mgr)); + src = (my_src_ptr) cinfo->src; + src->buffer = (JOCTET *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + INPUT_BUF_SIZE * SIZEOF(JOCTET)); + } + + src = (my_src_ptr) cinfo->src; + src->pub.init_source = init_source; + src->pub.fill_input_buffer = fill_input_buffer; + src->pub.skip_input_data = skip_input_data; + src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ + src->pub.term_source = term_source; + src->infile = infile; + src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ + src->pub.next_input_byte = NULL; /* until buffer loaded */ +} diff --git a/utils/Radiant/jpeg-6/jdcoefct.c b/utils/Radiant/jpeg-6/jdcoefct.c new file mode 100644 index 0000000..2b20c07 --- /dev/null +++ b/utils/Radiant/jpeg-6/jdcoefct.c @@ -0,0 +1,725 @@ +/* + * jdcoefct.c + * + * Copyright (C) 1994-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the coefficient buffer controller for decompression. + * This controller is the top level of the JPEG decompressor proper. + * The coefficient buffer lies between entropy decoding and inverse-DCT steps. + * + * In buffered-image mode, this controller is the interface between + * input-oriented processing and output-oriented processing. + * Also, the input side (only) is used when reading a file for transcoding. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + +/* Block smoothing is only applicable for progressive JPEG, so: */ +#ifndef D_PROGRESSIVE_SUPPORTED +#undef BLOCK_SMOOTHING_SUPPORTED +#endif + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_d_coef_controller pub; /* public fields */ + + /* These variables keep track of the current location of the input side. */ + /* cinfo->input_iMCU_row is also used for this. */ + JDIMENSION MCU_ctr; /* counts MCUs processed in current row */ + int MCU_vert_offset; /* counts MCU rows within iMCU row */ + int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + /* The output side's location is represented by cinfo->output_iMCU_row. */ + + /* In single-pass modes, it's sufficient to buffer just one MCU. + * We allocate a workspace of D_MAX_BLOCKS_IN_MCU coefficient blocks, + * and let the entropy decoder write into that workspace each time. + * (On 80x86, the workspace is FAR even though it's not really very big; + * this is to keep the module interfaces unchanged when a large coefficient + * buffer is necessary.) + * In multi-pass modes, this array points to the current MCU's blocks + * within the virtual arrays; it is used only by the input side. + */ + JBLOCKROW MCU_buffer[D_MAX_BLOCKS_IN_MCU]; + +#ifdef D_MULTISCAN_FILES_SUPPORTED + /* In multi-pass modes, we need a virtual block array for each component. */ + jvirt_barray_ptr whole_image[MAX_COMPONENTS]; +#endif + +#ifdef BLOCK_SMOOTHING_SUPPORTED + /* When doing block smoothing, we latch coefficient Al values here */ + int * coef_bits_latch; +#define SAVED_COEFS 6 /* we save coef_bits[0..5] */ +#endif +} my_coef_controller; + +typedef my_coef_controller * my_coef_ptr; + +/* Forward declarations */ +METHODDEF int decompress_onepass + JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); +#ifdef D_MULTISCAN_FILES_SUPPORTED +METHODDEF int decompress_data + JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); +#endif +#ifdef BLOCK_SMOOTHING_SUPPORTED +LOCAL boolean smoothing_ok JPP((j_decompress_ptr cinfo)); +METHODDEF int decompress_smooth_data + JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); +#endif + + +LOCAL void +start_iMCU_row (j_decompress_ptr cinfo) +/* Reset within-iMCU-row counters for a new row (input side) */ +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + /* In an interleaved scan, an MCU row is the same as an iMCU row. + * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. + * But at the bottom of the image, process only what's left. + */ + if (cinfo->comps_in_scan > 1) { + coef->MCU_rows_per_iMCU_row = 1; + } else { + if (cinfo->input_iMCU_row < (cinfo->total_iMCU_rows-1)) + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; + else + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; + } + + coef->MCU_ctr = 0; + coef->MCU_vert_offset = 0; +} + + +/* + * Initialize for an input processing pass. + */ + +METHODDEF void +start_input_pass (j_decompress_ptr cinfo) +{ + cinfo->input_iMCU_row = 0; + start_iMCU_row(cinfo); +} + + +/* + * Initialize for an output processing pass. + */ + +METHODDEF void +start_output_pass (j_decompress_ptr cinfo) +{ +#ifdef BLOCK_SMOOTHING_SUPPORTED + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + /* If multipass, check to see whether to use block smoothing on this pass */ + if (coef->pub.coef_arrays != NULL) { + if (cinfo->do_block_smoothing && smoothing_ok(cinfo)) + coef->pub.decompress_data = decompress_smooth_data; + else + coef->pub.decompress_data = decompress_data; + } +#endif + cinfo->output_iMCU_row = 0; +} + + +/* + * Decompress and return some data in the single-pass case. + * Always attempts to emit one fully interleaved MCU row ("iMCU" row). + * Input and output must run in lockstep since we have only a one-MCU buffer. + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + * + * NB: output_buf contains a plane for each component in image. + * For single pass, this is the same as the components in the scan. + */ + +METHODDEF int +decompress_onepass (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + int blkn, ci, xindex, yindex, yoffset, useful_width; + JSAMPARRAY output_ptr; + JDIMENSION start_col, output_col; + jpeg_component_info *compptr; + inverse_DCT_method_ptr inverse_DCT; + + /* Loop to process as much as one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->MCU_ctr; MCU_col_num <= last_MCU_col; + MCU_col_num++) { + /* Try to fetch an MCU. Entropy decoder expects buffer to be zeroed. */ + jzero_far((void FAR *) coef->MCU_buffer[0], + (size_t) (cinfo->blocks_in_MCU * SIZEOF(JBLOCK))); + if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->MCU_ctr = MCU_col_num; + return JPEG_SUSPENDED; + } + /* Determine where data should go in output_buf and do the IDCT thing. + * We skip dummy blocks at the right and bottom edges (but blkn gets + * incremented past them!). Note the inner loop relies on having + * allocated the MCU_buffer[] blocks sequentially. + */ + blkn = 0; /* index of current DCT block within MCU */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Don't bother to IDCT an uninteresting component. */ + if (! compptr->component_needed) { + blkn += compptr->MCU_blocks; + continue; + } + inverse_DCT = cinfo->idct->inverse_DCT[compptr->component_index]; + useful_width = (MCU_col_num < last_MCU_col) ? compptr->MCU_width + : compptr->last_col_width; + output_ptr = output_buf[ci] + yoffset * compptr->DCT_scaled_size; + start_col = MCU_col_num * compptr->MCU_sample_width; + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + if (cinfo->input_iMCU_row < last_iMCU_row || + yoffset+yindex < compptr->last_row_height) { + output_col = start_col; + for (xindex = 0; xindex < useful_width; xindex++) { + (*inverse_DCT) (cinfo, compptr, + (JCOEFPTR) coef->MCU_buffer[blkn+xindex], + output_ptr, output_col); + output_col += compptr->DCT_scaled_size; + } + } + blkn += compptr->MCU_width; + output_ptr += compptr->DCT_scaled_size; + } + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->MCU_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + cinfo->output_iMCU_row++; + if (++(cinfo->input_iMCU_row) < cinfo->total_iMCU_rows) { + start_iMCU_row(cinfo); + return JPEG_ROW_COMPLETED; + } + /* Completed the scan */ + (*cinfo->inputctl->finish_input_pass) (cinfo); + return JPEG_SCAN_COMPLETED; +} + + +/* + * Dummy consume-input routine for single-pass operation. + */ + +METHODDEF int +dummy_consume_data (j_decompress_ptr cinfo) +{ + return JPEG_SUSPENDED; /* Always indicate nothing was done */ +} + + +#ifdef D_MULTISCAN_FILES_SUPPORTED + +/* + * Consume input data and store it in the full-image coefficient buffer. + * We read as much as one fully interleaved MCU row ("iMCU" row) per call, + * ie, v_samp_factor block rows for each component in the scan. + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + */ + +METHODDEF int +consume_data (j_decompress_ptr cinfo) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + int blkn, ci, xindex, yindex, yoffset; + JDIMENSION start_col; + JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN]; + JBLOCKROW buffer_ptr; + jpeg_component_info *compptr; + + /* Align the virtual buffers for the components used in this scan. */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + buffer[ci] = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[compptr->component_index], + cinfo->input_iMCU_row * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, TRUE); + /* Note: entropy decoder expects buffer to be zeroed, + * but this is handled automatically by the memory manager + * because we requested a pre-zeroed array. + */ + } + + /* Loop to process one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->MCU_ctr; MCU_col_num < cinfo->MCUs_per_row; + MCU_col_num++) { + /* Construct list of pointers to DCT blocks belonging to this MCU */ + blkn = 0; /* index of current DCT block within MCU */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + start_col = MCU_col_num * compptr->MCU_width; + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + buffer_ptr = buffer[ci][yindex+yoffset] + start_col; + for (xindex = 0; xindex < compptr->MCU_width; xindex++) { + coef->MCU_buffer[blkn++] = buffer_ptr++; + } + } + } + /* Try to fetch the MCU. */ + if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->MCU_ctr = MCU_col_num; + return JPEG_SUSPENDED; + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->MCU_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + if (++(cinfo->input_iMCU_row) < cinfo->total_iMCU_rows) { + start_iMCU_row(cinfo); + return JPEG_ROW_COMPLETED; + } + /* Completed the scan */ + (*cinfo->inputctl->finish_input_pass) (cinfo); + return JPEG_SCAN_COMPLETED; +} + + +/* + * Decompress and return some data in the multi-pass case. + * Always attempts to emit one fully interleaved MCU row ("iMCU" row). + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + * + * NB: output_buf contains a plane for each component in image. + */ + +METHODDEF int +decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + JDIMENSION block_num; + int ci, block_row, block_rows; + JBLOCKARRAY buffer; + JBLOCKROW buffer_ptr; + JSAMPARRAY output_ptr; + JDIMENSION output_col; + jpeg_component_info *compptr; + inverse_DCT_method_ptr inverse_DCT; + + /* Force some input to be done if we are getting ahead of the input. */ + while (cinfo->input_scan_number < cinfo->output_scan_number || + (cinfo->input_scan_number == cinfo->output_scan_number && + cinfo->input_iMCU_row <= cinfo->output_iMCU_row)) { + if ((*cinfo->inputctl->consume_input)(cinfo) == JPEG_SUSPENDED) + return JPEG_SUSPENDED; + } + + /* OK, output from the virtual arrays. */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Don't bother to IDCT an uninteresting component. */ + if (! compptr->component_needed) + continue; + /* Align the virtual buffer for this component. */ + buffer = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[ci], + cinfo->output_iMCU_row * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + /* Count non-dummy DCT block rows in this iMCU row. */ + if (cinfo->output_iMCU_row < last_iMCU_row) + block_rows = compptr->v_samp_factor; + else { + /* NB: can't use last_row_height here; it is input-side-dependent! */ + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (block_rows == 0) block_rows = compptr->v_samp_factor; + } + inverse_DCT = cinfo->idct->inverse_DCT[ci]; + output_ptr = output_buf[ci]; + /* Loop over all DCT blocks to be processed. */ + for (block_row = 0; block_row < block_rows; block_row++) { + buffer_ptr = buffer[block_row]; + output_col = 0; + for (block_num = 0; block_num < compptr->width_in_blocks; block_num++) { + (*inverse_DCT) (cinfo, compptr, (JCOEFPTR) buffer_ptr, + output_ptr, output_col); + buffer_ptr++; + output_col += compptr->DCT_scaled_size; + } + output_ptr += compptr->DCT_scaled_size; + } + } + + if (++(cinfo->output_iMCU_row) < cinfo->total_iMCU_rows) + return JPEG_ROW_COMPLETED; + return JPEG_SCAN_COMPLETED; +} + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + + +#ifdef BLOCK_SMOOTHING_SUPPORTED + +/* + * This code applies interblock smoothing as described by section K.8 + * of the JPEG standard: the first 5 AC coefficients are estimated from + * the DC values of a DCT block and its 8 neighboring blocks. + * We apply smoothing only for progressive JPEG decoding, and only if + * the coefficients it can estimate are not yet known to full precision. + */ + +/* + * Determine whether block smoothing is applicable and safe. + * We also latch the current states of the coef_bits[] entries for the + * AC coefficients; otherwise, if the input side of the decompressor + * advances into a new scan, we might think the coefficients are known + * more accurately than they really are. + */ + +LOCAL boolean +smoothing_ok (j_decompress_ptr cinfo) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + boolean smoothing_useful = FALSE; + int ci, coefi; + jpeg_component_info *compptr; + JQUANT_TBL * qtable; + int * coef_bits; + int * coef_bits_latch; + + if (! cinfo->progressive_mode || cinfo->coef_bits == NULL) + return FALSE; + + /* Allocate latch area if not already done */ + if (coef->coef_bits_latch == NULL) + coef->coef_bits_latch = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->num_components * + (SAVED_COEFS * SIZEOF(int))); + coef_bits_latch = coef->coef_bits_latch; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* All components' quantization values must already be latched. */ + if ((qtable = compptr->quant_table) == NULL) + return FALSE; + /* Verify DC & first 5 AC quantizers are nonzero to avoid zero-divide. */ + for (coefi = 0; coefi <= 5; coefi++) { + if (qtable->quantval[coefi] == 0) + return FALSE; + } + /* DC values must be at least partly known for all components. */ + coef_bits = cinfo->coef_bits[ci]; + if (coef_bits[0] < 0) + return FALSE; + /* Block smoothing is helpful if some AC coefficients remain inaccurate. */ + for (coefi = 1; coefi <= 5; coefi++) { + coef_bits_latch[coefi] = coef_bits[coefi]; + if (coef_bits[coefi] != 0) + smoothing_useful = TRUE; + } + coef_bits_latch += SAVED_COEFS; + } + + return smoothing_useful; +} + + +/* + * Variant of decompress_data for use when doing block smoothing. + */ + +METHODDEF int +decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + JDIMENSION block_num, last_block_column; + int ci, block_row, block_rows, access_rows; + JBLOCKARRAY buffer; + JBLOCKROW buffer_ptr, prev_block_row, next_block_row; + JSAMPARRAY output_ptr; + JDIMENSION output_col; + jpeg_component_info *compptr; + inverse_DCT_method_ptr inverse_DCT; + boolean first_row, last_row; + JBLOCK workspace; + int *coef_bits; + JQUANT_TBL *quanttbl; + INT32 Q00,Q01,Q02,Q10,Q11,Q20, num; + int DC1,DC2,DC3,DC4,DC5,DC6,DC7,DC8,DC9; + int Al, pred; + + /* Force some input to be done if we are getting ahead of the input. */ + while (cinfo->input_scan_number <= cinfo->output_scan_number && + ! cinfo->inputctl->eoi_reached) { + if (cinfo->input_scan_number == cinfo->output_scan_number) { + /* If input is working on current scan, we ordinarily want it to + * have completed the current row. But if input scan is DC, + * we want it to keep one row ahead so that next block row's DC + * values are up to date. + */ + JDIMENSION delta = (cinfo->Ss == 0) ? 1 : 0; + if (cinfo->input_iMCU_row > cinfo->output_iMCU_row+delta) + break; + } + if ((*cinfo->inputctl->consume_input)(cinfo) == JPEG_SUSPENDED) + return JPEG_SUSPENDED; + } + + /* OK, output from the virtual arrays. */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Don't bother to IDCT an uninteresting component. */ + if (! compptr->component_needed) + continue; + /* Count non-dummy DCT block rows in this iMCU row. */ + if (cinfo->output_iMCU_row < last_iMCU_row) { + block_rows = compptr->v_samp_factor; + access_rows = block_rows * 2; /* this and next iMCU row */ + last_row = FALSE; + } else { + /* NB: can't use last_row_height here; it is input-side-dependent! */ + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (block_rows == 0) block_rows = compptr->v_samp_factor; + access_rows = block_rows; /* this iMCU row only */ + last_row = TRUE; + } + /* Align the virtual buffer for this component. */ + if (cinfo->output_iMCU_row > 0) { + access_rows += compptr->v_samp_factor; /* prior iMCU row too */ + buffer = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[ci], + (cinfo->output_iMCU_row - 1) * compptr->v_samp_factor, + (JDIMENSION) access_rows, FALSE); + buffer += compptr->v_samp_factor; /* point to current iMCU row */ + first_row = FALSE; + } else { + buffer = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[ci], + (JDIMENSION) 0, (JDIMENSION) access_rows, FALSE); + first_row = TRUE; + } + /* Fetch component-dependent info */ + coef_bits = coef->coef_bits_latch + (ci * SAVED_COEFS); + quanttbl = compptr->quant_table; + Q00 = quanttbl->quantval[0]; + Q01 = quanttbl->quantval[1]; + Q10 = quanttbl->quantval[2]; + Q20 = quanttbl->quantval[3]; + Q11 = quanttbl->quantval[4]; + Q02 = quanttbl->quantval[5]; + inverse_DCT = cinfo->idct->inverse_DCT[ci]; + output_ptr = output_buf[ci]; + /* Loop over all DCT blocks to be processed. */ + for (block_row = 0; block_row < block_rows; block_row++) { + buffer_ptr = buffer[block_row]; + if (first_row && block_row == 0) + prev_block_row = buffer_ptr; + else + prev_block_row = buffer[block_row-1]; + if (last_row && block_row == block_rows-1) + next_block_row = buffer_ptr; + else + next_block_row = buffer[block_row+1]; + /* We fetch the surrounding DC values using a sliding-register approach. + * Initialize all nine here so as to do the right thing on narrow pics. + */ + DC1 = DC2 = DC3 = (int) prev_block_row[0][0]; + DC4 = DC5 = DC6 = (int) buffer_ptr[0][0]; + DC7 = DC8 = DC9 = (int) next_block_row[0][0]; + output_col = 0; + last_block_column = compptr->width_in_blocks - 1; + for (block_num = 0; block_num <= last_block_column; block_num++) { + /* Fetch current DCT block into workspace so we can modify it. */ + jcopy_block_row(buffer_ptr, (JBLOCKROW) workspace, (JDIMENSION) 1); + /* Update DC values */ + if (block_num < last_block_column) { + DC3 = (int) prev_block_row[1][0]; + DC6 = (int) buffer_ptr[1][0]; + DC9 = (int) next_block_row[1][0]; + } + /* Compute coefficient estimates per K.8. + * An estimate is applied only if coefficient is still zero, + * and is not known to be fully accurate. + */ + /* AC01 */ + if ((Al=coef_bits[1]) != 0 && workspace[1] == 0) { + num = 36 * Q00 * (DC4 - DC6); + if (num >= 0) { + pred = (int) (((Q01<<7) + num) / (Q01<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + pred = (int) (((Q10<<7) + num) / (Q10<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + pred = (int) (((Q20<<7) + num) / (Q20<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + pred = (int) (((Q11<<7) + num) / (Q11<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + pred = (int) (((Q02<<7) + num) / (Q02<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<DCT_scaled_size; + } + output_ptr += compptr->DCT_scaled_size; + } + } + + if (++(cinfo->output_iMCU_row) < cinfo->total_iMCU_rows) + return JPEG_ROW_COMPLETED; + return JPEG_SCAN_COMPLETED; +} + +#endif /* BLOCK_SMOOTHING_SUPPORTED */ + + +/* + * Initialize coefficient buffer controller. + */ + +GLOBAL void +jinit_d_coef_controller (j_decompress_ptr cinfo, boolean need_full_buffer) +{ + my_coef_ptr coef; + + coef = (my_coef_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_coef_controller)); + cinfo->coef = (struct jpeg_d_coef_controller *) coef; + coef->pub.start_input_pass = start_input_pass; + coef->pub.start_output_pass = start_output_pass; +#ifdef BLOCK_SMOOTHING_SUPPORTED + coef->coef_bits_latch = NULL; +#endif + + /* Create the coefficient buffer. */ + if (need_full_buffer) { +#ifdef D_MULTISCAN_FILES_SUPPORTED + /* Allocate a full-image virtual array for each component, */ + /* padded to a multiple of samp_factor DCT blocks in each direction. */ + /* Note we ask for a pre-zeroed array. */ + int ci, access_rows; + jpeg_component_info *compptr; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + access_rows = compptr->v_samp_factor; +#ifdef BLOCK_SMOOTHING_SUPPORTED + /* If block smoothing could be used, need a bigger window */ + if (cinfo->progressive_mode) + access_rows *= 3; +#endif + coef->whole_image[ci] = (*cinfo->mem->request_virt_barray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, TRUE, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + (long) compptr->v_samp_factor), + (JDIMENSION) access_rows); + } + coef->pub.consume_data = consume_data; + coef->pub.decompress_data = decompress_data; + coef->pub.coef_arrays = coef->whole_image; /* link to virtual arrays */ +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + /* We only need a single-MCU buffer. */ + JBLOCKROW buffer; + int i; + + buffer = (JBLOCKROW) + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + D_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + for (i = 0; i < D_MAX_BLOCKS_IN_MCU; i++) { + coef->MCU_buffer[i] = buffer + i; + } + coef->pub.consume_data = dummy_consume_data; + coef->pub.decompress_data = decompress_onepass; + coef->pub.coef_arrays = NULL; /* flag for no virtual arrays */ + } +} diff --git a/utils/Radiant/jpeg-6/jdcolor.c b/utils/Radiant/jpeg-6/jdcolor.c new file mode 100644 index 0000000..d360b53 --- /dev/null +++ b/utils/Radiant/jpeg-6/jdcolor.c @@ -0,0 +1,367 @@ +/* + * jdcolor.c + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains output colorspace conversion routines. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Private subobject */ + +typedef struct { + struct jpeg_color_deconverter pub; /* public fields */ + + /* Private state for YCC->RGB conversion */ + int * Cr_r_tab; /* => table for Cr to R conversion */ + int * Cb_b_tab; /* => table for Cb to B conversion */ + INT32 * Cr_g_tab; /* => table for Cr to G conversion */ + INT32 * Cb_g_tab; /* => table for Cb to G conversion */ +} my_color_deconverter; + +typedef my_color_deconverter * my_cconvert_ptr; + + +/**************** YCbCr -> RGB conversion: most common case **************/ + +/* + * YCbCr is defined per CCIR 601-1, except that Cb and Cr are + * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. + * The conversion equations to be implemented are therefore + * R = Y + 1.40200 * Cr + * G = Y - 0.34414 * Cb - 0.71414 * Cr + * B = Y + 1.77200 * Cb + * where Cb and Cr represent the incoming values less CENTERJSAMPLE. + * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) + * + * To avoid floating-point arithmetic, we represent the fractional constants + * as integers scaled up by 2^16 (about 4 digits precision); we have to divide + * the products by 2^16, with appropriate rounding, to get the correct answer. + * Notice that Y, being an integral input, does not contribute any fraction + * so it need not participate in the rounding. + * + * For even more speed, we avoid doing any multiplications in the inner loop + * by precalculating the constants times Cb and Cr for all possible values. + * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table); + * for 12-bit samples it is still acceptable. It's not very reasonable for + * 16-bit samples, but if you want lossless storage you shouldn't be changing + * colorspace anyway. + * The Cr=>R and Cb=>B values can be rounded to integers in advance; the + * values for the G calculation are left scaled up, since we must add them + * together before rounding. + */ + +#define SCALEBITS 16 /* speediest right-shift on some machines */ +#define ONE_HALF ((INT32) 1 << (SCALEBITS-1)) +#define FIX(x) ((INT32) ((x) * (1L<RGB colorspace conversion. + */ + +LOCAL void +build_ycc_rgb_table (j_decompress_ptr cinfo) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + int i; + INT32 x; + SHIFT_TEMPS + + cconvert->Cr_r_tab = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(int)); + cconvert->Cb_b_tab = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(int)); + cconvert->Cr_g_tab = (INT32 *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(INT32)); + cconvert->Cb_g_tab = (INT32 *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(INT32)); + + for (i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) { + /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ + /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ + /* Cr=>R value is nearest int to 1.40200 * x */ + cconvert->Cr_r_tab[i] = (int) + RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS); + /* Cb=>B value is nearest int to 1.77200 * x */ + cconvert->Cb_b_tab[i] = (int) + RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS); + /* Cr=>G value is scaled-up -0.71414 * x */ + cconvert->Cr_g_tab[i] = (- FIX(0.71414)) * x; + /* Cb=>G value is scaled-up -0.34414 * x */ + /* We also add in ONE_HALF so that need not do it in inner loop */ + cconvert->Cb_g_tab[i] = (- FIX(0.34414)) * x + ONE_HALF; + } +} + + +/* + * Convert some rows of samples to the output colorspace. + * + * Note that we change from noninterleaved, one-plane-per-component format + * to interleaved-pixel format. The output buffer is therefore three times + * as wide as the input buffer. + * A starting row offset is provided only for the input buffer. The caller + * can easily adjust the passed output_buf value to accommodate any row + * offset required on that side. + */ + +METHODDEF void +ycc_rgb_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int y, cb, cr; + register JSAMPROW outptr; + register JSAMPROW inptr0, inptr1, inptr2; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->output_width; + /* copy these pointers into registers if possible */ + register JSAMPLE * range_limit = cinfo->sample_range_limit; + register int * Crrtab = cconvert->Cr_r_tab; + register int * Cbbtab = cconvert->Cb_b_tab; + register INT32 * Crgtab = cconvert->Cr_g_tab; + register INT32 * Cbgtab = cconvert->Cb_g_tab; + SHIFT_TEMPS + + while (--num_rows >= 0) { + inptr0 = input_buf[0][input_row]; + inptr1 = input_buf[1][input_row]; + inptr2 = input_buf[2][input_row]; + input_row++; + outptr = *output_buf++; + for (col = 0; col < num_cols; col++) { + y = GETJSAMPLE(inptr0[col]); + cb = GETJSAMPLE(inptr1[col]); + cr = GETJSAMPLE(inptr2[col]); + /* Range-limiting is essential due to noise introduced by DCT losses. */ + outptr[RGB_RED] = range_limit[y + Crrtab[cr]]; + outptr[RGB_GREEN] = range_limit[y + + ((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], + SCALEBITS))]; + outptr[RGB_BLUE] = range_limit[y + Cbbtab[cb]]; + outptr += RGB_PIXELSIZE; + } + } +} + + +/**************** Cases other than YCbCr -> RGB **************/ + + +/* + * Color conversion for no colorspace change: just copy the data, + * converting from separate-planes to interleaved representation. + */ + +METHODDEF void +null_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + register JSAMPROW inptr, outptr; + register JDIMENSION count; + register int num_components = cinfo->num_components; + JDIMENSION num_cols = cinfo->output_width; + int ci; + + while (--num_rows >= 0) { + for (ci = 0; ci < num_components; ci++) { + inptr = input_buf[ci][input_row]; + outptr = output_buf[0] + ci; + for (count = num_cols; count > 0; count--) { + *outptr = *inptr++; /* needn't bother with GETJSAMPLE() here */ + outptr += num_components; + } + } + input_row++; + output_buf++; + } +} + + +/* + * Color conversion for grayscale: just copy the data. + * This also works for YCbCr -> grayscale conversion, in which + * we just copy the Y (luminance) component and ignore chrominance. + */ + +METHODDEF void +grayscale_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + jcopy_sample_rows(input_buf[0], (int) input_row, output_buf, 0, + num_rows, cinfo->output_width); +} + + +/* + * Adobe-style YCCK->CMYK conversion. + * We convert YCbCr to R=1-C, G=1-M, and B=1-Y using the same + * conversion as above, while passing K (black) unchanged. + * We assume build_ycc_rgb_table has been called. + */ + +METHODDEF void +ycck_cmyk_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int y, cb, cr; + register JSAMPROW outptr; + register JSAMPROW inptr0, inptr1, inptr2, inptr3; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->output_width; + /* copy these pointers into registers if possible */ + register JSAMPLE * range_limit = cinfo->sample_range_limit; + register int * Crrtab = cconvert->Cr_r_tab; + register int * Cbbtab = cconvert->Cb_b_tab; + register INT32 * Crgtab = cconvert->Cr_g_tab; + register INT32 * Cbgtab = cconvert->Cb_g_tab; + SHIFT_TEMPS + + while (--num_rows >= 0) { + inptr0 = input_buf[0][input_row]; + inptr1 = input_buf[1][input_row]; + inptr2 = input_buf[2][input_row]; + inptr3 = input_buf[3][input_row]; + input_row++; + outptr = *output_buf++; + for (col = 0; col < num_cols; col++) { + y = GETJSAMPLE(inptr0[col]); + cb = GETJSAMPLE(inptr1[col]); + cr = GETJSAMPLE(inptr2[col]); + /* Range-limiting is essential due to noise introduced by DCT losses. */ + outptr[0] = range_limit[MAXJSAMPLE - (y + Crrtab[cr])]; /* red */ + outptr[1] = range_limit[MAXJSAMPLE - (y + /* green */ + ((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], + SCALEBITS)))]; + outptr[2] = range_limit[MAXJSAMPLE - (y + Cbbtab[cb])]; /* blue */ + /* K passes through unchanged */ + outptr[3] = inptr3[col]; /* don't need GETJSAMPLE here */ + outptr += 4; + } + } +} + + +/* + * Empty method for start_pass. + */ + +METHODDEF void +start_pass_dcolor (j_decompress_ptr cinfo) +{ + /* no work needed */ +} + + +/* + * Module initialization routine for output colorspace conversion. + */ + +GLOBAL void +jinit_color_deconverter (j_decompress_ptr cinfo) +{ + my_cconvert_ptr cconvert; + int ci; + + cconvert = (my_cconvert_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_color_deconverter)); + cinfo->cconvert = (struct jpeg_color_deconverter *) cconvert; + cconvert->pub.start_pass = start_pass_dcolor; + + /* Make sure num_components agrees with jpeg_color_space */ + switch (cinfo->jpeg_color_space) { + case JCS_GRAYSCALE: + if (cinfo->num_components != 1) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + break; + + case JCS_RGB: + case JCS_YCbCr: + if (cinfo->num_components != 3) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + break; + + case JCS_CMYK: + case JCS_YCCK: + if (cinfo->num_components != 4) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + break; + + default: /* JCS_UNKNOWN can be anything */ + if (cinfo->num_components < 1) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + break; + } + + /* Set out_color_components and conversion method based on requested space. + * Also clear the component_needed flags for any unused components, + * so that earlier pipeline stages can avoid useless computation. + */ + + switch (cinfo->out_color_space) { + case JCS_GRAYSCALE: + cinfo->out_color_components = 1; + if (cinfo->jpeg_color_space == JCS_GRAYSCALE || + cinfo->jpeg_color_space == JCS_YCbCr) { + cconvert->pub.color_convert = grayscale_convert; + /* For color->grayscale conversion, only the Y (0) component is needed */ + for (ci = 1; ci < cinfo->num_components; ci++) + cinfo->comp_info[ci].component_needed = FALSE; + } else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_RGB: + cinfo->out_color_components = RGB_PIXELSIZE; + if (cinfo->jpeg_color_space == JCS_YCbCr) { + cconvert->pub.color_convert = ycc_rgb_convert; + build_ycc_rgb_table(cinfo); + } else if (cinfo->jpeg_color_space == JCS_RGB && RGB_PIXELSIZE == 3) { + cconvert->pub.color_convert = null_convert; + } else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_CMYK: + cinfo->out_color_components = 4; + if (cinfo->jpeg_color_space == JCS_YCCK) { + cconvert->pub.color_convert = ycck_cmyk_convert; + build_ycc_rgb_table(cinfo); + } else if (cinfo->jpeg_color_space == JCS_CMYK) { + cconvert->pub.color_convert = null_convert; + } else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + default: + /* Permit null conversion to same output space */ + if (cinfo->out_color_space == cinfo->jpeg_color_space) { + cinfo->out_color_components = cinfo->num_components; + cconvert->pub.color_convert = null_convert; + } else /* unsupported non-null conversion */ + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + } + + if (cinfo->quantize_colors) + cinfo->output_components = 1; /* single colormapped output component */ + else + cinfo->output_components = cinfo->out_color_components; +} diff --git a/utils/Radiant/jpeg-6/jdct.h b/utils/Radiant/jpeg-6/jdct.h new file mode 100644 index 0000000..1d66d4f --- /dev/null +++ b/utils/Radiant/jpeg-6/jdct.h @@ -0,0 +1,176 @@ +/* + * jdct.h + * + * Copyright (C) 1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This include file contains common declarations for the forward and + * inverse DCT modules. These declarations are private to the DCT managers + * (jcdctmgr.c, jddctmgr.c) and the individual DCT algorithms. + * The individual DCT algorithms are kept in separate files to ease + * machine-dependent tuning (e.g., assembly coding). + */ + + +/* + * A forward DCT routine is given a pointer to a work area of type DCTELEM[]; + * the DCT is to be performed in-place in that buffer. Type DCTELEM is int + * for 8-bit samples, INT32 for 12-bit samples. (NOTE: Floating-point DCT + * implementations use an array of type FAST_FLOAT, instead.) + * The DCT inputs are expected to be signed (range +-CENTERJSAMPLE). + * The DCT outputs are returned scaled up by a factor of 8; they therefore + * have a range of +-8K for 8-bit data, +-128K for 12-bit data. This + * convention improves accuracy in integer implementations and saves some + * work in floating-point ones. + * Quantization of the output coefficients is done by jcdctmgr.c. + */ + +#if BITS_IN_JSAMPLE == 8 +typedef int DCTELEM; /* 16 or 32 bits is fine */ +#else +typedef INT32 DCTELEM; /* must have 32 bits */ +#endif + +typedef JMETHOD(void, forward_DCT_method_ptr, (DCTELEM * data)); +typedef JMETHOD(void, float_DCT_method_ptr, (FAST_FLOAT * data)); + + +/* + * An inverse DCT routine is given a pointer to the input JBLOCK and a pointer + * to an output sample array. The routine must dequantize the input data as + * well as perform the IDCT; for dequantization, it uses the multiplier table + * pointed to by compptr->dct_table. The output data is to be placed into the + * sample array starting at a specified column. (Any row offset needed will + * be applied to the array pointer before it is passed to the IDCT code.) + * Note that the number of samples emitted by the IDCT routine is + * DCT_scaled_size * DCT_scaled_size. + */ + +/* typedef inverse_DCT_method_ptr is declared in jpegint.h */ + +/* + * Each IDCT routine has its own ideas about the best dct_table element type. + */ + +typedef MULTIPLIER ISLOW_MULT_TYPE; /* short or int, whichever is faster */ +#if BITS_IN_JSAMPLE == 8 +typedef MULTIPLIER IFAST_MULT_TYPE; /* 16 bits is OK, use short if faster */ +#define IFAST_SCALE_BITS 2 /* fractional bits in scale factors */ +#else +typedef INT32 IFAST_MULT_TYPE; /* need 32 bits for scaled quantizers */ +#define IFAST_SCALE_BITS 13 /* fractional bits in scale factors */ +#endif +typedef FAST_FLOAT FLOAT_MULT_TYPE; /* preferred floating type */ + + +/* + * Each IDCT routine is responsible for range-limiting its results and + * converting them to unsigned form (0..MAXJSAMPLE). The raw outputs could + * be quite far out of range if the input data is corrupt, so a bulletproof + * range-limiting step is required. We use a mask-and-table-lookup method + * to do the combined operations quickly. See the comments with + * prepare_range_limit_table (in jdmaster.c) for more info. + */ + +#define IDCT_range_limit(cinfo) ((cinfo)->sample_range_limit + CENTERJSAMPLE) + +#define RANGE_MASK (MAXJSAMPLE * 4 + 3) /* 2 bits wider than legal samples */ + + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_fdct_islow jFDislow +#define jpeg_fdct_ifast jFDifast +#define jpeg_fdct_float jFDfloat +#define jpeg_idct_islow jRDislow +#define jpeg_idct_ifast jRDifast +#define jpeg_idct_float jRDfloat +#define jpeg_idct_4x4 jRD4x4 +#define jpeg_idct_2x2 jRD2x2 +#define jpeg_idct_1x1 jRD1x1 +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + +/* Extern declarations for the forward and inverse DCT routines. */ + +EXTERN void jpeg_fdct_islow JPP((DCTELEM * data)); +EXTERN void jpeg_fdct_ifast JPP((DCTELEM * data)); +EXTERN void jpeg_fdct_float JPP((FAST_FLOAT * data)); + +EXTERN void jpeg_idct_islow + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN void jpeg_idct_ifast + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN void jpeg_idct_float + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN void jpeg_idct_4x4 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN void jpeg_idct_2x2 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN void jpeg_idct_1x1 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); + + +/* + * Macros for handling fixed-point arithmetic; these are used by many + * but not all of the DCT/IDCT modules. + * + * All values are expected to be of type INT32. + * Fractional constants are scaled left by CONST_BITS bits. + * CONST_BITS is defined within each module using these macros, + * and may differ from one module to the next. + */ + +#define ONE ((INT32) 1) +#define CONST_SCALE (ONE << CONST_BITS) + +/* Convert a positive real constant to an integer scaled by CONST_SCALE. + * Caution: some C compilers fail to reduce "FIX(constant)" at compile time, + * thus causing a lot of useless floating-point operations at run time. + */ + +#define FIX(x) ((INT32) ((x) * CONST_SCALE + 0.5)) + +/* Descale and correctly round an INT32 value that's scaled by N bits. + * We assume RIGHT_SHIFT rounds towards minus infinity, so adding + * the fudge factor is correct for either sign of X. + */ + +#define DESCALE(x,n) RIGHT_SHIFT((x) + (ONE << ((n)-1)), n) + +/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. + * This macro is used only when the two inputs will actually be no more than + * 16 bits wide, so that a 16x16->32 bit multiply can be used instead of a + * full 32x32 multiply. This provides a useful speedup on many machines. + * Unfortunately there is no way to specify a 16x16->32 multiply portably + * in C, but some C compilers will do the right thing if you provide the + * correct combination of casts. + */ + +#ifdef SHORTxSHORT_32 /* may work if 'int' is 32 bits */ +#define MULTIPLY16C16(var,const) (((INT16) (var)) * ((INT16) (const))) +#endif +#ifdef SHORTxLCONST_32 /* known to work with Microsoft C 6.0 */ +#define MULTIPLY16C16(var,const) (((INT16) (var)) * ((INT32) (const))) +#endif + +#ifndef MULTIPLY16C16 /* default definition */ +#define MULTIPLY16C16(var,const) ((var) * (const)) +#endif + +/* Same except both inputs are variables. */ + +#ifdef SHORTxSHORT_32 /* may work if 'int' is 32 bits */ +#define MULTIPLY16V16(var1,var2) (((INT16) (var1)) * ((INT16) (var2))) +#endif + +#ifndef MULTIPLY16V16 /* default definition */ +#define MULTIPLY16V16(var1,var2) ((var1) * (var2)) +#endif diff --git a/utils/Radiant/jpeg-6/jddctmgr.c b/utils/Radiant/jpeg-6/jddctmgr.c new file mode 100644 index 0000000..ba01fc6 --- /dev/null +++ b/utils/Radiant/jpeg-6/jddctmgr.c @@ -0,0 +1,270 @@ +/* + * jddctmgr.c + * + * Copyright (C) 1994-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the inverse-DCT management logic. + * This code selects a particular IDCT implementation to be used, + * and it performs related housekeeping chores. No code in this file + * is executed per IDCT step, only during output pass setup. + * + * Note that the IDCT routines are responsible for performing coefficient + * dequantization as well as the IDCT proper. This module sets up the + * dequantization multiplier table needed by the IDCT routine. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + + +/* + * The decompressor input side (jdinput.c) saves away the appropriate + * quantization table for each component at the start of the first scan + * involving that component. (This is necessary in order to correctly + * decode files that reuse Q-table slots.) + * When we are ready to make an output pass, the saved Q-table is converted + * to a multiplier table that will actually be used by the IDCT routine. + * The multiplier table contents are IDCT-method-dependent. To support + * application changes in IDCT method between scans, we can remake the + * multiplier tables if necessary. + * In buffered-image mode, the first output pass may occur before any data + * has been seen for some components, and thus before their Q-tables have + * been saved away. To handle this case, multiplier tables are preset + * to zeroes; the result of the IDCT will be a neutral gray level. + */ + + +/* Private subobject for this module */ + +typedef struct { + struct jpeg_inverse_dct pub; /* public fields */ + + /* This array contains the IDCT method code that each multiplier table + * is currently set up for, or -1 if it's not yet set up. + * The actual multiplier tables are pointed to by dct_table in the + * per-component comp_info structures. + */ + int cur_method[MAX_COMPONENTS]; +} my_idct_controller; + +typedef my_idct_controller * my_idct_ptr; + + +/* Allocated multiplier tables: big enough for any supported variant */ + +typedef union { + ISLOW_MULT_TYPE islow_array[DCTSIZE2]; +#ifdef DCT_IFAST_SUPPORTED + IFAST_MULT_TYPE ifast_array[DCTSIZE2]; +#endif +#ifdef DCT_FLOAT_SUPPORTED + FLOAT_MULT_TYPE float_array[DCTSIZE2]; +#endif +} multiplier_table; + + +/* The current scaled-IDCT routines require ISLOW-style multiplier tables, + * so be sure to compile that code if either ISLOW or SCALING is requested. + */ +#ifdef DCT_ISLOW_SUPPORTED +#define PROVIDE_ISLOW_TABLES +#else +#ifdef IDCT_SCALING_SUPPORTED +#define PROVIDE_ISLOW_TABLES +#endif +#endif + + +/* + * Prepare for an output pass. + * Here we select the proper IDCT routine for each component and build + * a matching multiplier table. + */ + +METHODDEF void +start_pass (j_decompress_ptr cinfo) +{ + my_idct_ptr idct = (my_idct_ptr) cinfo->idct; + int ci, i; + jpeg_component_info *compptr; + int method = 0; + inverse_DCT_method_ptr method_ptr = NULL; + JQUANT_TBL * qtbl; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Select the proper IDCT routine for this component's scaling */ + switch (compptr->DCT_scaled_size) { +#ifdef IDCT_SCALING_SUPPORTED + case 1: + method_ptr = jpeg_idct_1x1; + method = JDCT_ISLOW; /* jidctred uses islow-style table */ + break; + case 2: + method_ptr = jpeg_idct_2x2; + method = JDCT_ISLOW; /* jidctred uses islow-style table */ + break; + case 4: + method_ptr = jpeg_idct_4x4; + method = JDCT_ISLOW; /* jidctred uses islow-style table */ + break; +#endif + case DCTSIZE: + switch (cinfo->dct_method) { +#ifdef DCT_ISLOW_SUPPORTED + case JDCT_ISLOW: + method_ptr = jpeg_idct_islow; + method = JDCT_ISLOW; + break; +#endif +#ifdef DCT_IFAST_SUPPORTED + case JDCT_IFAST: + method_ptr = jpeg_idct_ifast; + method = JDCT_IFAST; + break; +#endif +#ifdef DCT_FLOAT_SUPPORTED + case JDCT_FLOAT: + method_ptr = jpeg_idct_float; + method = JDCT_FLOAT; + break; +#endif + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + break; + } + break; + default: + ERREXIT1(cinfo, JERR_BAD_DCTSIZE, compptr->DCT_scaled_size); + break; + } + idct->pub.inverse_DCT[ci] = method_ptr; + /* Create multiplier table from quant table. + * However, we can skip this if the component is uninteresting + * or if we already built the table. Also, if no quant table + * has yet been saved for the component, we leave the + * multiplier table all-zero; we'll be reading zeroes from the + * coefficient controller's buffer anyway. + */ + if (! compptr->component_needed || idct->cur_method[ci] == method) + continue; + qtbl = compptr->quant_table; + if (qtbl == NULL) /* happens if no data yet for component */ + continue; + idct->cur_method[ci] = method; + switch (method) { +#ifdef PROVIDE_ISLOW_TABLES + case JDCT_ISLOW: + { + /* For LL&M IDCT method, multipliers are equal to raw quantization + * coefficients, but are stored in natural order as ints. + */ + ISLOW_MULT_TYPE * ismtbl = (ISLOW_MULT_TYPE *) compptr->dct_table; + for (i = 0; i < DCTSIZE2; i++) { + ismtbl[i] = (ISLOW_MULT_TYPE) qtbl->quantval[jpeg_zigzag_order[i]]; + } + } + break; +#endif +#ifdef DCT_IFAST_SUPPORTED + case JDCT_IFAST: + { + /* For AA&N IDCT method, multipliers are equal to quantization + * coefficients scaled by scalefactor[row]*scalefactor[col], where + * scalefactor[0] = 1 + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + * For integer operation, the multiplier table is to be scaled by + * IFAST_SCALE_BITS. The multipliers are stored in natural order. + */ + IFAST_MULT_TYPE * ifmtbl = (IFAST_MULT_TYPE *) compptr->dct_table; +#define CONST_BITS 14 + static const INT16 aanscales[DCTSIZE2] = { + /* precomputed values scaled up by 14 bits */ + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270, + 21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906, + 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552, + 8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446, + 4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247 + }; + SHIFT_TEMPS + + for (i = 0; i < DCTSIZE2; i++) { + ifmtbl[i] = (IFAST_MULT_TYPE) + DESCALE(MULTIPLY16V16((INT32) qtbl->quantval[jpeg_zigzag_order[i]], + (INT32) aanscales[i]), + CONST_BITS-IFAST_SCALE_BITS); + } + } + break; +#endif +#ifdef DCT_FLOAT_SUPPORTED + case JDCT_FLOAT: + { + /* For float AA&N IDCT method, multipliers are equal to quantization + * coefficients scaled by scalefactor[row]*scalefactor[col], where + * scalefactor[0] = 1 + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + * The multipliers are stored in natural order. + */ + FLOAT_MULT_TYPE * fmtbl = (FLOAT_MULT_TYPE *) compptr->dct_table; + int row, col; + static const double aanscalefactor[DCTSIZE] = { + 1.0, 1.387039845, 1.306562965, 1.175875602, + 1.0, 0.785694958, 0.541196100, 0.275899379 + }; + + i = 0; + for (row = 0; row < DCTSIZE; row++) { + for (col = 0; col < DCTSIZE; col++) { + fmtbl[i] = (FLOAT_MULT_TYPE) + ((double) qtbl->quantval[jpeg_zigzag_order[i]] * + aanscalefactor[row] * aanscalefactor[col]); + i++; + } + } + } + break; +#endif + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + break; + } + } +} + + +/* + * Initialize IDCT manager. + */ + +GLOBAL void +jinit_inverse_dct (j_decompress_ptr cinfo) +{ + my_idct_ptr idct; + int ci; + jpeg_component_info *compptr; + + idct = (my_idct_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_idct_controller)); + cinfo->idct = (struct jpeg_inverse_dct *) idct; + idct->pub.start_pass = start_pass; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Allocate and pre-zero a multiplier table for each component */ + compptr->dct_table = + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(multiplier_table)); + MEMZERO(compptr->dct_table, SIZEOF(multiplier_table)); + /* Mark multiplier table not yet set up for any method */ + idct->cur_method[ci] = -1; + } +} diff --git a/utils/Radiant/jpeg-6/jdhuff.c b/utils/Radiant/jpeg-6/jdhuff.c new file mode 100644 index 0000000..db42772 --- /dev/null +++ b/utils/Radiant/jpeg-6/jdhuff.c @@ -0,0 +1,574 @@ +/* + * jdhuff.c + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains Huffman entropy decoding routines. + * + * Much of the complexity here has to do with supporting input suspension. + * If the data source module demands suspension, we want to be able to back + * up to the start of the current MCU. To do this, we copy state variables + * into local working storage, and update them back to the permanent + * storage only upon successful completion of an MCU. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdhuff.h" /* Declarations shared with jdphuff.c */ + + +/* + * Expanded entropy decoder object for Huffman decoding. + * + * The savable_state subrecord contains fields that change within an MCU, + * but must not be updated permanently until we complete the MCU. + */ + +typedef struct { + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ +} savable_state; + +/* This macro is to work around compilers with missing or broken + * structure assignment. You'll need to fix this code if you have + * such a compiler and you change MAX_COMPS_IN_SCAN. + */ + +#ifndef NO_STRUCT_ASSIGN +#define ASSIGN_STATE(dest,src) ((dest) = (src)) +#else +#if MAX_COMPS_IN_SCAN == 4 +#define ASSIGN_STATE(dest,src) \ + ((dest).last_dc_val[0] = (src).last_dc_val[0], \ + (dest).last_dc_val[1] = (src).last_dc_val[1], \ + (dest).last_dc_val[2] = (src).last_dc_val[2], \ + (dest).last_dc_val[3] = (src).last_dc_val[3]) +#endif +#endif + + +typedef struct { + struct jpeg_entropy_decoder pub; /* public fields */ + + /* These fields are loaded into local variables at start of each MCU. + * In case of suspension, we exit WITHOUT updating them. + */ + bitread_perm_state bitstate; /* Bit buffer at start of MCU */ + savable_state saved; /* Other state at start of MCU */ + + /* These fields are NOT loaded into local working state. */ + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + + /* Pointers to derived tables (these workspaces have image lifespan) */ + d_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS]; + d_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS]; +} huff_entropy_decoder; + +typedef huff_entropy_decoder * huff_entropy_ptr; + + +/* + * Initialize for a Huffman-compressed scan. + */ + +METHODDEF void +start_pass_huff_decoder (j_decompress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci, dctbl, actbl; + jpeg_component_info * compptr; + + /* Check that the scan parameters Ss, Se, Ah/Al are OK for sequential JPEG. + * This ought to be an error condition, but we make it a warning because + * there are some baseline files out there with all zeroes in these bytes. + */ + if (cinfo->Ss != 0 || cinfo->Se != DCTSIZE2-1 || + cinfo->Ah != 0 || cinfo->Al != 0) + WARNMS(cinfo, JWRN_NOT_SEQUENTIAL); + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + dctbl = compptr->dc_tbl_no; + actbl = compptr->ac_tbl_no; + /* Make sure requested tables are present */ + if (dctbl < 0 || dctbl >= NUM_HUFF_TBLS || + cinfo->dc_huff_tbl_ptrs[dctbl] == NULL) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, dctbl); + if (actbl < 0 || actbl >= NUM_HUFF_TBLS || + cinfo->ac_huff_tbl_ptrs[actbl] == NULL) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, actbl); + /* Compute derived values for Huffman tables */ + /* We may do this more than once for a table, but it's not expensive */ + jpeg_make_d_derived_tbl(cinfo, cinfo->dc_huff_tbl_ptrs[dctbl], + & entropy->dc_derived_tbls[dctbl]); + jpeg_make_d_derived_tbl(cinfo, cinfo->ac_huff_tbl_ptrs[actbl], + & entropy->ac_derived_tbls[actbl]); + /* Initialize DC predictions to 0 */ + entropy->saved.last_dc_val[ci] = 0; + } + + /* Initialize bitread state variables */ + entropy->bitstate.bits_left = 0; + entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ + entropy->bitstate.printed_eod = FALSE; + + /* Initialize restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; +} + + +/* + * Compute the derived values for a Huffman table. + * Note this is also used by jdphuff.c. + */ + +GLOBAL void +jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, JHUFF_TBL * htbl, + d_derived_tbl ** pdtbl) +{ + d_derived_tbl *dtbl; + int p, i, l, si; + int lookbits, ctr; + char huffsize[257]; + unsigned int huffcode[257]; + unsigned int code; + + /* Allocate a workspace if we haven't already done so. */ + if (*pdtbl == NULL) + *pdtbl = (d_derived_tbl *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(d_derived_tbl)); + dtbl = *pdtbl; + dtbl->pub = htbl; /* fill in back link */ + + /* Figure C.1: make table of Huffman code length for each symbol */ + /* Note that this is in code-length order. */ + + p = 0; + for (l = 1; l <= 16; l++) { + for (i = 1; i <= (int) htbl->bits[l]; i++) + huffsize[p++] = (char) l; + } + huffsize[p] = 0; + + /* Figure C.2: generate the codes themselves */ + /* Note that this is in code-length order. */ + + code = 0; + si = huffsize[0]; + p = 0; + while (huffsize[p]) { + while (((int) huffsize[p]) == si) { + huffcode[p++] = code; + code++; + } + code <<= 1; + si++; + } + + /* Figure F.15: generate decoding tables for bit-sequential decoding */ + + p = 0; + for (l = 1; l <= 16; l++) { + if (htbl->bits[l]) { + dtbl->valptr[l] = p; /* huffval[] index of 1st symbol of code length l */ + dtbl->mincode[l] = huffcode[p]; /* minimum code of length l */ + p += htbl->bits[l]; + dtbl->maxcode[l] = huffcode[p-1]; /* maximum code of length l */ + } else { + dtbl->maxcode[l] = -1; /* -1 if no codes of this length */ + } + } + dtbl->maxcode[17] = 0xFFFFFL; /* ensures jpeg_huff_decode terminates */ + + /* Compute lookahead tables to speed up decoding. + * First we set all the table entries to 0, indicating "too long"; + * then we iterate through the Huffman codes that are short enough and + * fill in all the entries that correspond to bit sequences starting + * with that code. + */ + + MEMZERO(dtbl->look_nbits, SIZEOF(dtbl->look_nbits)); + + p = 0; + for (l = 1; l <= HUFF_LOOKAHEAD; l++) { + for (i = 1; i <= (int) htbl->bits[l]; i++, p++) { + /* l = current code's length, p = its index in huffcode[] & huffval[]. */ + /* Generate left-justified code followed by all possible bit sequences */ + lookbits = huffcode[p] << (HUFF_LOOKAHEAD-l); + for (ctr = 1 << (HUFF_LOOKAHEAD-l); ctr > 0; ctr--) { + dtbl->look_nbits[lookbits] = l; + dtbl->look_sym[lookbits] = htbl->huffval[p]; + lookbits++; + } + } + } +} + + +/* + * Out-of-line code for bit fetching (shared with jdphuff.c). + * See jdhuff.h for info about usage. + * Note: current values of get_buffer and bits_left are passed as parameters, + * but are returned in the corresponding fields of the state struct. + * + * On most machines MIN_GET_BITS should be 25 to allow the full 32-bit width + * of get_buffer to be used. (On machines with wider words, an even larger + * buffer could be used.) However, on some machines 32-bit shifts are + * quite slow and take time proportional to the number of places shifted. + * (This is true with most PC compilers, for instance.) In this case it may + * be a win to set MIN_GET_BITS to the minimum value of 15. This reduces the + * average shift distance at the cost of more calls to jpeg_fill_bit_buffer. + */ + +#ifdef SLOW_SHIFT_32 +#define MIN_GET_BITS 15 /* minimum allowable value */ +#else +#define MIN_GET_BITS (BIT_BUF_SIZE-7) +#endif + + +GLOBAL boolean +jpeg_fill_bit_buffer (bitread_working_state * state, + register bit_buf_type get_buffer, register int bits_left, + int nbits) +/* Load up the bit buffer to a depth of at least nbits */ +{ + /* Copy heavily used state fields into locals (hopefully registers) */ + register const JOCTET * next_input_byte = state->next_input_byte; + register size_t bytes_in_buffer = state->bytes_in_buffer; + register int c; + + /* Attempt to load at least MIN_GET_BITS bits into get_buffer. */ + /* (It is assumed that no request will be for more than that many bits.) */ + + while (bits_left < MIN_GET_BITS) { + /* Attempt to read a byte */ + if (state->unread_marker != 0) + goto no_more_data; /* can't advance past a marker */ + + if (bytes_in_buffer == 0) { + if (! (*state->cinfo->src->fill_input_buffer) (state->cinfo)) + return FALSE; + next_input_byte = state->cinfo->src->next_input_byte; + bytes_in_buffer = state->cinfo->src->bytes_in_buffer; + } + bytes_in_buffer--; + c = GETJOCTET(*next_input_byte++); + + /* If it's 0xFF, check and discard stuffed zero byte */ + if (c == 0xFF) { + do { + if (bytes_in_buffer == 0) { + if (! (*state->cinfo->src->fill_input_buffer) (state->cinfo)) + return FALSE; + next_input_byte = state->cinfo->src->next_input_byte; + bytes_in_buffer = state->cinfo->src->bytes_in_buffer; + } + bytes_in_buffer--; + c = GETJOCTET(*next_input_byte++); + } while (c == 0xFF); + + if (c == 0) { + /* Found FF/00, which represents an FF data byte */ + c = 0xFF; + } else { + /* Oops, it's actually a marker indicating end of compressed data. */ + /* Better put it back for use later */ + state->unread_marker = c; + + no_more_data: + /* There should be enough bits still left in the data segment; */ + /* if so, just break out of the outer while loop. */ + if (bits_left >= nbits) + break; + /* Uh-oh. Report corrupted data to user and stuff zeroes into + * the data stream, so that we can produce some kind of image. + * Note that this code will be repeated for each byte demanded + * for the rest of the segment. We use a nonvolatile flag to ensure + * that only one warning message appears. + */ + if (! *(state->printed_eod_ptr)) { + WARNMS(state->cinfo, JWRN_HIT_MARKER); + *(state->printed_eod_ptr) = TRUE; + } + c = 0; /* insert a zero byte into bit buffer */ + } + } + + /* OK, load c into get_buffer */ + get_buffer = (get_buffer << 8) | c; + bits_left += 8; + } + + /* Unload the local registers */ + state->next_input_byte = next_input_byte; + state->bytes_in_buffer = bytes_in_buffer; + state->get_buffer = get_buffer; + state->bits_left = bits_left; + + return TRUE; +} + + +/* + * Out-of-line code for Huffman code decoding. + * See jdhuff.h for info about usage. + */ + +GLOBAL int +jpeg_huff_decode (bitread_working_state * state, + register bit_buf_type get_buffer, register int bits_left, + d_derived_tbl * htbl, int min_bits) +{ + register int l = min_bits; + register INT32 code; + + /* HUFF_DECODE has determined that the code is at least min_bits */ + /* bits long, so fetch that many bits in one swoop. */ + + CHECK_BIT_BUFFER(*state, l, return -1); + code = GET_BITS(l); + + /* Collect the rest of the Huffman code one bit at a time. */ + /* This is per Figure F.16 in the JPEG spec. */ + + while (code > htbl->maxcode[l]) { + code <<= 1; + CHECK_BIT_BUFFER(*state, 1, return -1); + code |= GET_BITS(1); + l++; + } + + /* Unload the local registers */ + state->get_buffer = get_buffer; + state->bits_left = bits_left; + + /* With garbage input we may reach the sentinel value l = 17. */ + + if (l > 16) { + WARNMS(state->cinfo, JWRN_HUFF_BAD_CODE); + return 0; /* fake a zero as the safest result */ + } + + return htbl->pub->huffval[ htbl->valptr[l] + + ((int) (code - htbl->mincode[l])) ]; +} + + +/* + * Figure F.12: extend sign bit. + * On some machines, a shift and add will be faster than a table lookup. + */ + +#ifdef AVOID_TABLES + +#define HUFF_EXTEND(x,s) ((x) < (1<<((s)-1)) ? (x) + (((-1)<<(s)) + 1) : (x)) + +#else + +#define HUFF_EXTEND(x,s) ((x) < extend_test[s] ? (x) + extend_offset[s] : (x)) + +static const int extend_test[16] = /* entry n is 2**(n-1) */ + { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, + 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 }; + +static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */ + { 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1, + ((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1, + ((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1, + ((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 }; + +#endif /* AVOID_TABLES */ + + +/* + * Check for a restart marker & resynchronize decoder. + * Returns FALSE if must suspend. + */ + +LOCAL boolean +process_restart (j_decompress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci; + + /* Throw away any unused bits remaining in bit buffer; */ + /* include any full bytes in next_marker's count of discarded bytes */ + cinfo->marker->discarded_bytes += entropy->bitstate.bits_left / 8; + entropy->bitstate.bits_left = 0; + + /* Advance past the RSTn marker */ + if (! (*cinfo->marker->read_restart_marker) (cinfo)) + return FALSE; + + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) + entropy->saved.last_dc_val[ci] = 0; + + /* Reset restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; + + /* Next segment can get another out-of-data warning */ + entropy->bitstate.printed_eod = FALSE; + + return TRUE; +} + + +/* + * Decode and return one MCU's worth of Huffman-compressed coefficients. + * The coefficients are reordered from zigzag order into natural array order, + * but are not dequantized. + * + * The i'th block of the MCU is stored into the block pointed to by + * MCU_data[i]. WE ASSUME THIS AREA HAS BEEN ZEROED BY THE CALLER. + * (Wholesale zeroing is usually a little faster than retail...) + * + * Returns FALSE if data source requested suspension. In that case no + * changes have been made to permanent state. (Exception: some output + * coefficients may already have been assigned. This is harmless for + * this module, since we'll just re-assign them on the next call.) + */ + +METHODDEF boolean +decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + register int s, k, r; + int blkn, ci; + JBLOCKROW block; + BITREAD_STATE_VARS; + savable_state state; + d_derived_tbl * dctbl; + d_derived_tbl * actbl; + jpeg_component_info * compptr; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restart(cinfo)) + return FALSE; + } + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(state, entropy->saved); + + /* Outer loop handles each block in the MCU */ + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + block = MCU_data[blkn]; + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + dctbl = entropy->dc_derived_tbls[compptr->dc_tbl_no]; + actbl = entropy->ac_derived_tbls[compptr->ac_tbl_no]; + + /* Decode a single block's worth of coefficients */ + + /* Section F.2.2.1: decode the DC coefficient difference */ + HUFF_DECODE(s, br_state, dctbl, return FALSE, label1); + if (s) { + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + } + + /* Shortcut if component's values are not interesting */ + if (! compptr->component_needed) + goto skip_ACs; + + /* Convert DC difference to actual value, update last_dc_val */ + s += state.last_dc_val[ci]; + state.last_dc_val[ci] = s; + /* Output the DC coefficient (assumes jpeg_natural_order[0] = 0) */ + (*block)[0] = (JCOEF) s; + + /* Do we need to decode the AC coefficients for this component? */ + if (compptr->DCT_scaled_size > 1) { + + /* Section F.2.2.2: decode the AC coefficients */ + /* Since zeroes are skipped, output area must be cleared beforehand */ + for (k = 1; k < DCTSIZE2; k++) { + HUFF_DECODE(s, br_state, actbl, return FALSE, label2); + + r = s >> 4; + s &= 15; + + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + /* Output coefficient in natural (dezigzagged) order. + * Note: the extra entries in jpeg_natural_order[] will save us + * if k >= DCTSIZE2, which could happen if the data is corrupted. + */ + (*block)[jpeg_natural_order[k]] = (JCOEF) s; + } else { + if (r != 15) + break; + k += 15; + } + } + + } else { +skip_ACs: + + /* Section F.2.2.2: decode the AC coefficients */ + /* In this path we just discard the values */ + for (k = 1; k < DCTSIZE2; k++) { + HUFF_DECODE(s, br_state, actbl, return FALSE, label3); + + r = s >> 4; + s &= 15; + + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + DROP_BITS(s); + } else { + if (r != 15) + break; + k += 15; + } + } + + } + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(entropy->saved, state); + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; +} + + +/* + * Module initialization routine for Huffman entropy decoding. + */ + +GLOBAL void +jinit_huff_decoder (j_decompress_ptr cinfo) +{ + huff_entropy_ptr entropy; + int i; + + entropy = (huff_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(huff_entropy_decoder)); + cinfo->entropy = (struct jpeg_entropy_decoder *) entropy; + entropy->pub.start_pass = start_pass_huff_decoder; + entropy->pub.decode_mcu = decode_mcu; + + /* Mark tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL; + } +} diff --git a/utils/Radiant/jpeg-6/jdhuff.h b/utils/Radiant/jpeg-6/jdhuff.h new file mode 100644 index 0000000..65f3054 --- /dev/null +++ b/utils/Radiant/jpeg-6/jdhuff.h @@ -0,0 +1,202 @@ +/* + * jdhuff.h + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains declarations for Huffman entropy decoding routines + * that are shared between the sequential decoder (jdhuff.c) and the + * progressive decoder (jdphuff.c). No other modules need to see these. + */ + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_make_d_derived_tbl jMkDDerived +#define jpeg_fill_bit_buffer jFilBitBuf +#define jpeg_huff_decode jHufDecode +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Derived data constructed for each Huffman table */ + +#define HUFF_LOOKAHEAD 8 /* # of bits of lookahead */ + +typedef struct { + /* Basic tables: (element [0] of each array is unused) */ + INT32 mincode[17]; /* smallest code of length k */ + INT32 maxcode[18]; /* largest code of length k (-1 if none) */ + /* (maxcode[17] is a sentinel to ensure jpeg_huff_decode terminates) */ + int valptr[17]; /* huffval[] index of 1st symbol of length k */ + + /* Link to public Huffman table (needed only in jpeg_huff_decode) */ + JHUFF_TBL *pub; + + /* Lookahead tables: indexed by the next HUFF_LOOKAHEAD bits of + * the input data stream. If the next Huffman code is no more + * than HUFF_LOOKAHEAD bits long, we can obtain its length and + * the corresponding symbol directly from these tables. + */ + int look_nbits[1< 32 bits on your machine, and shifting/masking longs is + * reasonably fast, making bit_buf_type be long and setting BIT_BUF_SIZE + * appropriately should be a win. Unfortunately we can't do this with + * something like #define BIT_BUF_SIZE (sizeof(bit_buf_type)*8) + * because not all machines measure sizeof in 8-bit bytes. + */ + +typedef struct { /* Bitreading state saved across MCUs */ + bit_buf_type get_buffer; /* current bit-extraction buffer */ + int bits_left; /* # of unused bits in it */ + boolean printed_eod; /* flag to suppress multiple warning msgs */ +} bitread_perm_state; + +typedef struct { /* Bitreading working state within an MCU */ + /* current data source state */ + const JOCTET * next_input_byte; /* => next byte to read from source */ + size_t bytes_in_buffer; /* # of bytes remaining in source buffer */ + int unread_marker; /* nonzero if we have hit a marker */ + /* bit input buffer --- note these values are kept in register variables, + * not in this struct, inside the inner loops. + */ + bit_buf_type get_buffer; /* current bit-extraction buffer */ + int bits_left; /* # of unused bits in it */ + /* pointers needed by jpeg_fill_bit_buffer */ + j_decompress_ptr cinfo; /* back link to decompress master record */ + boolean * printed_eod_ptr; /* => flag in permanent state */ +} bitread_working_state; + +/* Macros to declare and load/save bitread local variables. */ +#define BITREAD_STATE_VARS \ + register bit_buf_type get_buffer; \ + register int bits_left; \ + bitread_working_state br_state + +#define BITREAD_LOAD_STATE(cinfop,permstate) \ + br_state.cinfo = cinfop; \ + br_state.next_input_byte = cinfop->src->next_input_byte; \ + br_state.bytes_in_buffer = cinfop->src->bytes_in_buffer; \ + br_state.unread_marker = cinfop->unread_marker; \ + get_buffer = permstate.get_buffer; \ + bits_left = permstate.bits_left; \ + br_state.printed_eod_ptr = & permstate.printed_eod + +#define BITREAD_SAVE_STATE(cinfop,permstate) \ + cinfop->src->next_input_byte = br_state.next_input_byte; \ + cinfop->src->bytes_in_buffer = br_state.bytes_in_buffer; \ + cinfop->unread_marker = br_state.unread_marker; \ + permstate.get_buffer = get_buffer; \ + permstate.bits_left = bits_left + +/* + * These macros provide the in-line portion of bit fetching. + * Use CHECK_BIT_BUFFER to ensure there are N bits in get_buffer + * before using GET_BITS, PEEK_BITS, or DROP_BITS. + * The variables get_buffer and bits_left are assumed to be locals, + * but the state struct might not be (jpeg_huff_decode needs this). + * CHECK_BIT_BUFFER(state,n,action); + * Ensure there are N bits in get_buffer; if suspend, take action. + * val = GET_BITS(n); + * Fetch next N bits. + * val = PEEK_BITS(n); + * Fetch next N bits without removing them from the buffer. + * DROP_BITS(n); + * Discard next N bits. + * The value N should be a simple variable, not an expression, because it + * is evaluated multiple times. + */ + +#define CHECK_BIT_BUFFER(state,nbits,action) \ + { if (bits_left < (nbits)) { \ + if (! jpeg_fill_bit_buffer(&(state),get_buffer,bits_left,nbits)) \ + { action; } \ + get_buffer = (state).get_buffer; bits_left = (state).bits_left; } } + +#define GET_BITS(nbits) \ + (((int) (get_buffer >> (bits_left -= (nbits)))) & ((1<<(nbits))-1)) + +#define PEEK_BITS(nbits) \ + (((int) (get_buffer >> (bits_left - (nbits)))) & ((1<<(nbits))-1)) + +#define DROP_BITS(nbits) \ + (bits_left -= (nbits)) + +/* Load up the bit buffer to a depth of at least nbits */ +EXTERN boolean jpeg_fill_bit_buffer JPP((bitread_working_state * state, + register bit_buf_type get_buffer, register int bits_left, + int nbits)); + + +/* + * Code for extracting next Huffman-coded symbol from input bit stream. + * Again, this is time-critical and we make the main paths be macros. + * + * We use a lookahead table to process codes of up to HUFF_LOOKAHEAD bits + * without looping. Usually, more than 95% of the Huffman codes will be 8 + * or fewer bits long. The few overlength codes are handled with a loop, + * which need not be inline code. + * + * Notes about the HUFF_DECODE macro: + * 1. Near the end of the data segment, we may fail to get enough bits + * for a lookahead. In that case, we do it the hard way. + * 2. If the lookahead table contains no entry, the next code must be + * more than HUFF_LOOKAHEAD bits long. + * 3. jpeg_huff_decode returns -1 if forced to suspend. + */ + +#define HUFF_DECODE(result,state,htbl,failaction,slowlabel) \ +{ register int nb, look; \ + if (bits_left < HUFF_LOOKAHEAD) { \ + if (! jpeg_fill_bit_buffer(&state,get_buffer,bits_left, 0)) {failaction;} \ + get_buffer = state.get_buffer; bits_left = state.bits_left; \ + if (bits_left < HUFF_LOOKAHEAD) { \ + nb = 1; goto slowlabel; \ + } \ + } \ + look = PEEK_BITS(HUFF_LOOKAHEAD); \ + if ((nb = htbl->look_nbits[look]) != 0) { \ + DROP_BITS(nb); \ + result = htbl->look_sym[look]; \ + } else { \ + nb = HUFF_LOOKAHEAD+1; \ +slowlabel: \ + if ((result=jpeg_huff_decode(&state,get_buffer,bits_left,htbl,nb)) < 0) \ + { failaction; } \ + get_buffer = state.get_buffer; bits_left = state.bits_left; \ + } \ +} + +/* Out-of-line case for Huffman code fetching */ +EXTERN int jpeg_huff_decode JPP((bitread_working_state * state, + register bit_buf_type get_buffer, register int bits_left, + d_derived_tbl * htbl, int min_bits)); diff --git a/utils/Radiant/jpeg-6/jdinput.c b/utils/Radiant/jpeg-6/jdinput.c new file mode 100644 index 0000000..5b4774f --- /dev/null +++ b/utils/Radiant/jpeg-6/jdinput.c @@ -0,0 +1,381 @@ +/* + * jdinput.c + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains input control logic for the JPEG decompressor. + * These routines are concerned with controlling the decompressor's input + * processing (marker reading and coefficient decoding). The actual input + * reading is done in jdmarker.c, jdhuff.c, and jdphuff.c. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Private state */ + +typedef struct { + struct jpeg_input_controller pub; /* public fields */ + + boolean inheaders; /* TRUE until first SOS is reached */ +} my_input_controller; + +typedef my_input_controller * my_inputctl_ptr; + + +/* Forward declarations */ +METHODDEF int consume_markers JPP((j_decompress_ptr cinfo)); + + +/* + * Routines to calculate various quantities related to the size of the image. + */ + +LOCAL void +initial_setup (j_decompress_ptr cinfo) +/* Called once, when first SOS marker is reached */ +{ + int ci; + jpeg_component_info *compptr; + + /* Make sure image isn't bigger than I can handle */ + if ((long) cinfo->image_height > (long) JPEG_MAX_DIMENSION || + (long) cinfo->image_width > (long) JPEG_MAX_DIMENSION) + ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) JPEG_MAX_DIMENSION); + + /* For now, precision must match compiled-in value... */ + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + + /* Check that number of components won't exceed internal array sizes */ + if (cinfo->num_components > MAX_COMPONENTS) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, + MAX_COMPONENTS); + + /* Compute maximum sampling factors; check factor validity */ + cinfo->max_h_samp_factor = 1; + cinfo->max_v_samp_factor = 1; + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + if (compptr->h_samp_factor<=0 || compptr->h_samp_factor>MAX_SAMP_FACTOR || + compptr->v_samp_factor<=0 || compptr->v_samp_factor>MAX_SAMP_FACTOR) + ERREXIT(cinfo, JERR_BAD_SAMPLING); + cinfo->max_h_samp_factor = MAX(cinfo->max_h_samp_factor, + compptr->h_samp_factor); + cinfo->max_v_samp_factor = MAX(cinfo->max_v_samp_factor, + compptr->v_samp_factor); + } + + /* We initialize DCT_scaled_size and min_DCT_scaled_size to DCTSIZE. + * In the full decompressor, this will be overridden by jdmaster.c; + * but in the transcoder, jdmaster.c is not used, so we must do it here. + */ + cinfo->min_DCT_scaled_size = DCTSIZE; + + /* Compute dimensions of components */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + compptr->DCT_scaled_size = DCTSIZE; + /* Size in DCT blocks */ + compptr->width_in_blocks = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, + (long) (cinfo->max_h_samp_factor * DCTSIZE)); + compptr->height_in_blocks = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, + (long) (cinfo->max_v_samp_factor * DCTSIZE)); + /* downsampled_width and downsampled_height will also be overridden by + * jdmaster.c if we are doing full decompression. The transcoder library + * doesn't use these values, but the calling application might. + */ + /* Size in samples */ + compptr->downsampled_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, + (long) cinfo->max_h_samp_factor); + compptr->downsampled_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, + (long) cinfo->max_v_samp_factor); + /* Mark component needed, until color conversion says otherwise */ + compptr->component_needed = TRUE; + /* Mark no quantization table yet saved for component */ + compptr->quant_table = NULL; + } + + /* Compute number of fully interleaved MCU rows. */ + cinfo->total_iMCU_rows = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, + (long) (cinfo->max_v_samp_factor*DCTSIZE)); + + /* Decide whether file contains multiple scans */ + if (cinfo->comps_in_scan < cinfo->num_components || cinfo->progressive_mode) + cinfo->inputctl->has_multiple_scans = TRUE; + else + cinfo->inputctl->has_multiple_scans = FALSE; +} + + +LOCAL void +per_scan_setup (j_decompress_ptr cinfo) +/* Do computations that are needed before processing a JPEG scan */ +/* cinfo->comps_in_scan and cinfo->cur_comp_info[] were set from SOS marker */ +{ + int ci, mcublks, tmp; + jpeg_component_info *compptr; + + if (cinfo->comps_in_scan == 1) { + + /* Noninterleaved (single-component) scan */ + compptr = cinfo->cur_comp_info[0]; + + /* Overall image size in MCUs */ + cinfo->MCUs_per_row = compptr->width_in_blocks; + cinfo->MCU_rows_in_scan = compptr->height_in_blocks; + + /* For noninterleaved scan, always one block per MCU */ + compptr->MCU_width = 1; + compptr->MCU_height = 1; + compptr->MCU_blocks = 1; + compptr->MCU_sample_width = compptr->DCT_scaled_size; + compptr->last_col_width = 1; + /* For noninterleaved scans, it is convenient to define last_row_height + * as the number of block rows present in the last iMCU row. + */ + tmp = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (tmp == 0) tmp = compptr->v_samp_factor; + compptr->last_row_height = tmp; + + /* Prepare array describing MCU composition */ + cinfo->blocks_in_MCU = 1; + cinfo->MCU_membership[0] = 0; + + } else { + + /* Interleaved (multi-component) scan */ + if (cinfo->comps_in_scan <= 0 || cinfo->comps_in_scan > MAX_COMPS_IN_SCAN) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->comps_in_scan, + MAX_COMPS_IN_SCAN); + + /* Overall image size in MCUs */ + cinfo->MCUs_per_row = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, + (long) (cinfo->max_h_samp_factor*DCTSIZE)); + cinfo->MCU_rows_in_scan = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, + (long) (cinfo->max_v_samp_factor*DCTSIZE)); + + cinfo->blocks_in_MCU = 0; + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Sampling factors give # of blocks of component in each MCU */ + compptr->MCU_width = compptr->h_samp_factor; + compptr->MCU_height = compptr->v_samp_factor; + compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height; + compptr->MCU_sample_width = compptr->MCU_width * compptr->DCT_scaled_size; + /* Figure number of non-dummy blocks in last MCU column & row */ + tmp = (int) (compptr->width_in_blocks % compptr->MCU_width); + if (tmp == 0) tmp = compptr->MCU_width; + compptr->last_col_width = tmp; + tmp = (int) (compptr->height_in_blocks % compptr->MCU_height); + if (tmp == 0) tmp = compptr->MCU_height; + compptr->last_row_height = tmp; + /* Prepare array describing MCU composition */ + mcublks = compptr->MCU_blocks; + if (cinfo->blocks_in_MCU + mcublks > D_MAX_BLOCKS_IN_MCU) + ERREXIT(cinfo, JERR_BAD_MCU_SIZE); + while (mcublks-- > 0) { + cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci; + } + } + + } +} + + +/* + * Save away a copy of the Q-table referenced by each component present + * in the current scan, unless already saved during a prior scan. + * + * In a multiple-scan JPEG file, the encoder could assign different components + * the same Q-table slot number, but change table definitions between scans + * so that each component uses a different Q-table. (The IJG encoder is not + * currently capable of doing this, but other encoders might.) Since we want + * to be able to dequantize all the components at the end of the file, this + * means that we have to save away the table actually used for each component. + * We do this by copying the table at the start of the first scan containing + * the component. + * The JPEG spec prohibits the encoder from changing the contents of a Q-table + * slot between scans of a component using that slot. If the encoder does so + * anyway, this decoder will simply use the Q-table values that were current + * at the start of the first scan for the component. + * + * The decompressor output side looks only at the saved quant tables, + * not at the current Q-table slots. + */ + +LOCAL void +latch_quant_tables (j_decompress_ptr cinfo) +{ + int ci, qtblno; + jpeg_component_info *compptr; + JQUANT_TBL * qtbl; + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* No work if we already saved Q-table for this component */ + if (compptr->quant_table != NULL) + continue; + /* Make sure specified quantization table is present */ + qtblno = compptr->quant_tbl_no; + if (qtblno < 0 || qtblno >= NUM_QUANT_TBLS || + cinfo->quant_tbl_ptrs[qtblno] == NULL) + ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, qtblno); + /* OK, save away the quantization table */ + qtbl = (JQUANT_TBL *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(JQUANT_TBL)); + MEMCOPY(qtbl, cinfo->quant_tbl_ptrs[qtblno], SIZEOF(JQUANT_TBL)); + compptr->quant_table = qtbl; + } +} + + +/* + * Initialize the input modules to read a scan of compressed data. + * The first call to this is done by jdmaster.c after initializing + * the entire decompressor (during jpeg_start_decompress). + * Subsequent calls come from consume_markers, below. + */ + +METHODDEF void +start_input_pass (j_decompress_ptr cinfo) +{ + per_scan_setup(cinfo); + latch_quant_tables(cinfo); + (*cinfo->entropy->start_pass) (cinfo); + (*cinfo->coef->start_input_pass) (cinfo); + cinfo->inputctl->consume_input = cinfo->coef->consume_data; +} + + +/* + * Finish up after inputting a compressed-data scan. + * This is called by the coefficient controller after it's read all + * the expected data of the scan. + */ + +METHODDEF void +finish_input_pass (j_decompress_ptr cinfo) +{ + cinfo->inputctl->consume_input = consume_markers; +} + + +/* + * Read JPEG markers before, between, or after compressed-data scans. + * Change state as necessary when a new scan is reached. + * Return value is JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + * + * The consume_input method pointer points either here or to the + * coefficient controller's consume_data routine, depending on whether + * we are reading a compressed data segment or inter-segment markers. + */ + +METHODDEF int +consume_markers (j_decompress_ptr cinfo) +{ + my_inputctl_ptr inputctl = (my_inputctl_ptr) cinfo->inputctl; + int val; + + if (inputctl->pub.eoi_reached) /* After hitting EOI, read no further */ + return JPEG_REACHED_EOI; + + val = (*cinfo->marker->read_markers) (cinfo); + + switch (val) { + case JPEG_REACHED_SOS: /* Found SOS */ + if (inputctl->inheaders) { /* 1st SOS */ + initial_setup(cinfo); + inputctl->inheaders = FALSE; + /* Note: start_input_pass must be called by jdmaster.c + * before any more input can be consumed. jdapi.c is + * responsible for enforcing this sequencing. + */ + } else { /* 2nd or later SOS marker */ + if (! inputctl->pub.has_multiple_scans) + ERREXIT(cinfo, JERR_EOI_EXPECTED); /* Oops, I wasn't expecting this! */ + start_input_pass(cinfo); + } + break; + case JPEG_REACHED_EOI: /* Found EOI */ + inputctl->pub.eoi_reached = TRUE; + if (inputctl->inheaders) { /* Tables-only datastream, apparently */ + if (cinfo->marker->saw_SOF) + ERREXIT(cinfo, JERR_SOF_NO_SOS); + } else { + /* Prevent infinite loop in coef ctlr's decompress_data routine + * if user set output_scan_number larger than number of scans. + */ + if (cinfo->output_scan_number > cinfo->input_scan_number) + cinfo->output_scan_number = cinfo->input_scan_number; + } + break; + case JPEG_SUSPENDED: + break; + } + + return val; +} + + +/* + * Reset state to begin a fresh datastream. + */ + +METHODDEF void +reset_input_controller (j_decompress_ptr cinfo) +{ + my_inputctl_ptr inputctl = (my_inputctl_ptr) cinfo->inputctl; + + inputctl->pub.consume_input = consume_markers; + inputctl->pub.has_multiple_scans = FALSE; /* "unknown" would be better */ + inputctl->pub.eoi_reached = FALSE; + inputctl->inheaders = TRUE; + /* Reset other modules */ + (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo); + (*cinfo->marker->reset_marker_reader) (cinfo); + /* Reset progression state -- would be cleaner if entropy decoder did this */ + cinfo->coef_bits = NULL; +} + + +/* + * Initialize the input controller module. + * This is called only once, when the decompression object is created. + */ + +GLOBAL void +jinit_input_controller (j_decompress_ptr cinfo) +{ + my_inputctl_ptr inputctl; + + /* Create subobject in permanent pool */ + inputctl = (my_inputctl_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_input_controller)); + cinfo->inputctl = (struct jpeg_input_controller *) inputctl; + /* Initialize method pointers */ + inputctl->pub.consume_input = consume_markers; + inputctl->pub.reset_input_controller = reset_input_controller; + inputctl->pub.start_input_pass = start_input_pass; + inputctl->pub.finish_input_pass = finish_input_pass; + /* Initialize state: can't use reset_input_controller since we don't + * want to try to reset other modules yet. + */ + inputctl->pub.has_multiple_scans = FALSE; /* "unknown" would be better */ + inputctl->pub.eoi_reached = FALSE; + inputctl->inheaders = TRUE; +} diff --git a/utils/Radiant/jpeg-6/jdmainct.c b/utils/Radiant/jpeg-6/jdmainct.c new file mode 100644 index 0000000..c4e0d54 --- /dev/null +++ b/utils/Radiant/jpeg-6/jdmainct.c @@ -0,0 +1,512 @@ +/* + * jdmainct.c + * + * Copyright (C) 1994-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the main buffer controller for decompression. + * The main buffer lies between the JPEG decompressor proper and the + * post-processor; it holds downsampled data in the JPEG colorspace. + * + * Note that this code is bypassed in raw-data mode, since the application + * supplies the equivalent of the main buffer in that case. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * In the current system design, the main buffer need never be a full-image + * buffer; any full-height buffers will be found inside the coefficient or + * postprocessing controllers. Nonetheless, the main controller is not + * trivial. Its responsibility is to provide context rows for upsampling/ + * rescaling, and doing this in an efficient fashion is a bit tricky. + * + * Postprocessor input data is counted in "row groups". A row group + * is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) + * sample rows of each component. (We require DCT_scaled_size values to be + * chosen such that these numbers are integers. In practice DCT_scaled_size + * values will likely be powers of two, so we actually have the stronger + * condition that DCT_scaled_size / min_DCT_scaled_size is an integer.) + * Upsampling will typically produce max_v_samp_factor pixel rows from each + * row group (times any additional scale factor that the upsampler is + * applying). + * + * The coefficient controller will deliver data to us one iMCU row at a time; + * each iMCU row contains v_samp_factor * DCT_scaled_size sample rows, or + * exactly min_DCT_scaled_size row groups. (This amount of data corresponds + * to one row of MCUs when the image is fully interleaved.) Note that the + * number of sample rows varies across components, but the number of row + * groups does not. Some garbage sample rows may be included in the last iMCU + * row at the bottom of the image. + * + * Depending on the vertical scaling algorithm used, the upsampler may need + * access to the sample row(s) above and below its current input row group. + * The upsampler is required to set need_context_rows TRUE at global selection + * time if so. When need_context_rows is FALSE, this controller can simply + * obtain one iMCU row at a time from the coefficient controller and dole it + * out as row groups to the postprocessor. + * + * When need_context_rows is TRUE, this controller guarantees that the buffer + * passed to postprocessing contains at least one row group's worth of samples + * above and below the row group(s) being processed. Note that the context + * rows "above" the first passed row group appear at negative row offsets in + * the passed buffer. At the top and bottom of the image, the required + * context rows are manufactured by duplicating the first or last real sample + * row; this avoids having special cases in the upsampling inner loops. + * + * The amount of context is fixed at one row group just because that's a + * convenient number for this controller to work with. The existing + * upsamplers really only need one sample row of context. An upsampler + * supporting arbitrary output rescaling might wish for more than one row + * group of context when shrinking the image; tough, we don't handle that. + * (This is justified by the assumption that downsizing will be handled mostly + * by adjusting the DCT_scaled_size values, so that the actual scale factor at + * the upsample step needn't be much less than one.) + * + * To provide the desired context, we have to retain the last two row groups + * of one iMCU row while reading in the next iMCU row. (The last row group + * can't be processed until we have another row group for its below-context, + * and so we have to save the next-to-last group too for its above-context.) + * We could do this most simply by copying data around in our buffer, but + * that'd be very slow. We can avoid copying any data by creating a rather + * strange pointer structure. Here's how it works. We allocate a workspace + * consisting of M+2 row groups (where M = min_DCT_scaled_size is the number + * of row groups per iMCU row). We create two sets of redundant pointers to + * the workspace. Labeling the physical row groups 0 to M+1, the synthesized + * pointer lists look like this: + * M+1 M-1 + * master pointer --> 0 master pointer --> 0 + * 1 1 + * ... ... + * M-3 M-3 + * M-2 M + * M-1 M+1 + * M M-2 + * M+1 M-1 + * 0 0 + * We read alternate iMCU rows using each master pointer; thus the last two + * row groups of the previous iMCU row remain un-overwritten in the workspace. + * The pointer lists are set up so that the required context rows appear to + * be adjacent to the proper places when we pass the pointer lists to the + * upsampler. + * + * The above pictures describe the normal state of the pointer lists. + * At top and bottom of the image, we diddle the pointer lists to duplicate + * the first or last sample row as necessary (this is cheaper than copying + * sample rows around). + * + * This scheme breaks down if M < 2, ie, min_DCT_scaled_size is 1. In that + * situation each iMCU row provides only one row group so the buffering logic + * must be different (eg, we must read two iMCU rows before we can emit the + * first row group). For now, we simply do not support providing context + * rows when min_DCT_scaled_size is 1. That combination seems unlikely to + * be worth providing --- if someone wants a 1/8th-size preview, they probably + * want it quick and dirty, so a context-free upsampler is sufficient. + */ + + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_d_main_controller pub; /* public fields */ + + /* Pointer to allocated workspace (M or M+2 row groups). */ + JSAMPARRAY buffer[MAX_COMPONENTS]; + + boolean buffer_full; /* Have we gotten an iMCU row from decoder? */ + JDIMENSION rowgroup_ctr; /* counts row groups output to postprocessor */ + + /* Remaining fields are only used in the context case. */ + + /* These are the master pointers to the funny-order pointer lists. */ + JSAMPIMAGE xbuffer[2]; /* pointers to weird pointer lists */ + + int whichptr; /* indicates which pointer set is now in use */ + int context_state; /* process_data state machine status */ + JDIMENSION rowgroups_avail; /* row groups available to postprocessor */ + JDIMENSION iMCU_row_ctr; /* counts iMCU rows to detect image top/bot */ +} my_main_controller; + +typedef my_main_controller * my_main_ptr; + +/* context_state values: */ +#define CTX_PREPARE_FOR_IMCU 0 /* need to prepare for MCU row */ +#define CTX_PROCESS_IMCU 1 /* feeding iMCU to postprocessor */ +#define CTX_POSTPONED_ROW 2 /* feeding postponed row group */ + + +/* Forward declarations */ +METHODDEF void process_data_simple_main + JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)); +METHODDEF void process_data_context_main + JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)); +#ifdef QUANT_2PASS_SUPPORTED +METHODDEF void process_data_crank_post + JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)); +#endif + + +LOCAL void +alloc_funny_pointers (j_decompress_ptr cinfo) +/* Allocate space for the funny pointer lists. + * This is done only once, not once per pass. + */ +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + int ci, rgroup; + int M = cinfo->min_DCT_scaled_size; + jpeg_component_info *compptr; + JSAMPARRAY xbuf; + + /* Get top-level space for component array pointers. + * We alloc both arrays with one call to save a few cycles. + */ + main->xbuffer[0] = (JSAMPIMAGE) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->num_components * 2 * SIZEOF(JSAMPARRAY)); + main->xbuffer[1] = main->xbuffer[0] + cinfo->num_components; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + /* Get space for pointer lists --- M+4 row groups in each list. + * We alloc both pointer lists with one call to save a few cycles. + */ + xbuf = (JSAMPARRAY) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + 2 * (rgroup * (M + 4)) * SIZEOF(JSAMPROW)); + xbuf += rgroup; /* want one row group at negative offsets */ + main->xbuffer[0][ci] = xbuf; + xbuf += rgroup * (M + 4); + main->xbuffer[1][ci] = xbuf; + } +} + + +LOCAL void +make_funny_pointers (j_decompress_ptr cinfo) +/* Create the funny pointer lists discussed in the comments above. + * The actual workspace is already allocated (in main->buffer), + * and the space for the pointer lists is allocated too. + * This routine just fills in the curiously ordered lists. + * This will be repeated at the beginning of each pass. + */ +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + int ci, i, rgroup; + int M = cinfo->min_DCT_scaled_size; + jpeg_component_info *compptr; + JSAMPARRAY buf, xbuf0, xbuf1; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + xbuf0 = main->xbuffer[0][ci]; + xbuf1 = main->xbuffer[1][ci]; + /* First copy the workspace pointers as-is */ + buf = main->buffer[ci]; + for (i = 0; i < rgroup * (M + 2); i++) { + xbuf0[i] = xbuf1[i] = buf[i]; + } + /* In the second list, put the last four row groups in swapped order */ + for (i = 0; i < rgroup * 2; i++) { + xbuf1[rgroup*(M-2) + i] = buf[rgroup*M + i]; + xbuf1[rgroup*M + i] = buf[rgroup*(M-2) + i]; + } + /* The wraparound pointers at top and bottom will be filled later + * (see set_wraparound_pointers, below). Initially we want the "above" + * pointers to duplicate the first actual data line. This only needs + * to happen in xbuffer[0]. + */ + for (i = 0; i < rgroup; i++) { + xbuf0[i - rgroup] = xbuf0[0]; + } + } +} + + +LOCAL void +set_wraparound_pointers (j_decompress_ptr cinfo) +/* Set up the "wraparound" pointers at top and bottom of the pointer lists. + * This changes the pointer list state from top-of-image to the normal state. + */ +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + int ci, i, rgroup; + int M = cinfo->min_DCT_scaled_size; + jpeg_component_info *compptr; + JSAMPARRAY xbuf0, xbuf1; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + xbuf0 = main->xbuffer[0][ci]; + xbuf1 = main->xbuffer[1][ci]; + for (i = 0; i < rgroup; i++) { + xbuf0[i - rgroup] = xbuf0[rgroup*(M+1) + i]; + xbuf1[i - rgroup] = xbuf1[rgroup*(M+1) + i]; + xbuf0[rgroup*(M+2) + i] = xbuf0[i]; + xbuf1[rgroup*(M+2) + i] = xbuf1[i]; + } + } +} + + +LOCAL void +set_bottom_pointers (j_decompress_ptr cinfo) +/* Change the pointer lists to duplicate the last sample row at the bottom + * of the image. whichptr indicates which xbuffer holds the final iMCU row. + * Also sets rowgroups_avail to indicate number of nondummy row groups in row. + */ +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + int ci, i, rgroup, iMCUheight, rows_left; + jpeg_component_info *compptr; + JSAMPARRAY xbuf; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Count sample rows in one iMCU row and in one row group */ + iMCUheight = compptr->v_samp_factor * compptr->DCT_scaled_size; + rgroup = iMCUheight / cinfo->min_DCT_scaled_size; + /* Count nondummy sample rows remaining for this component */ + rows_left = (int) (compptr->downsampled_height % (JDIMENSION) iMCUheight); + if (rows_left == 0) rows_left = iMCUheight; + /* Count nondummy row groups. Should get same answer for each component, + * so we need only do it once. + */ + if (ci == 0) { + main->rowgroups_avail = (JDIMENSION) ((rows_left-1) / rgroup + 1); + } + /* Duplicate the last real sample row rgroup*2 times; this pads out the + * last partial rowgroup and ensures at least one full rowgroup of context. + */ + xbuf = main->xbuffer[main->whichptr][ci]; + for (i = 0; i < rgroup * 2; i++) { + xbuf[rows_left + i] = xbuf[rows_left-1]; + } + } +} + + +/* + * Initialize for a processing pass. + */ + +METHODDEF void +start_pass_main (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + + switch (pass_mode) { + case JBUF_PASS_THRU: + if (cinfo->upsample->need_context_rows) { + main->pub.process_data = process_data_context_main; + make_funny_pointers(cinfo); /* Create the xbuffer[] lists */ + main->whichptr = 0; /* Read first iMCU row into xbuffer[0] */ + main->context_state = CTX_PREPARE_FOR_IMCU; + main->iMCU_row_ctr = 0; + } else { + /* Simple case with no context needed */ + main->pub.process_data = process_data_simple_main; + } + main->buffer_full = FALSE; /* Mark buffer empty */ + main->rowgroup_ctr = 0; + break; +#ifdef QUANT_2PASS_SUPPORTED + case JBUF_CRANK_DEST: + /* For last pass of 2-pass quantization, just crank the postprocessor */ + main->pub.process_data = process_data_crank_post; + break; +#endif + default: + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + break; + } +} + + +/* + * Process some data. + * This handles the simple case where no context is required. + */ + +METHODDEF void +process_data_simple_main (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + JDIMENSION rowgroups_avail; + + /* Read input data if we haven't filled the main buffer yet */ + if (! main->buffer_full) { + if (! (*cinfo->coef->decompress_data) (cinfo, main->buffer)) + return; /* suspension forced, can do nothing more */ + main->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ + } + + /* There are always min_DCT_scaled_size row groups in an iMCU row. */ + rowgroups_avail = (JDIMENSION) cinfo->min_DCT_scaled_size; + /* Note: at the bottom of the image, we may pass extra garbage row groups + * to the postprocessor. The postprocessor has to check for bottom + * of image anyway (at row resolution), so no point in us doing it too. + */ + + /* Feed the postprocessor */ + (*cinfo->post->post_process_data) (cinfo, main->buffer, + &main->rowgroup_ctr, rowgroups_avail, + output_buf, out_row_ctr, out_rows_avail); + + /* Has postprocessor consumed all the data yet? If so, mark buffer empty */ + if (main->rowgroup_ctr >= rowgroups_avail) { + main->buffer_full = FALSE; + main->rowgroup_ctr = 0; + } +} + + +/* + * Process some data. + * This handles the case where context rows must be provided. + */ + +METHODDEF void +process_data_context_main (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + + /* Read input data if we haven't filled the main buffer yet */ + if (! main->buffer_full) { + if (! (*cinfo->coef->decompress_data) (cinfo, + main->xbuffer[main->whichptr])) + return; /* suspension forced, can do nothing more */ + main->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ + main->iMCU_row_ctr++; /* count rows received */ + } + + /* Postprocessor typically will not swallow all the input data it is handed + * in one call (due to filling the output buffer first). Must be prepared + * to exit and restart. This switch lets us keep track of how far we got. + * Note that each case falls through to the next on successful completion. + */ + switch (main->context_state) { + case CTX_POSTPONED_ROW: + /* Call postprocessor using previously set pointers for postponed row */ + (*cinfo->post->post_process_data) (cinfo, main->xbuffer[main->whichptr], + &main->rowgroup_ctr, main->rowgroups_avail, + output_buf, out_row_ctr, out_rows_avail); + if (main->rowgroup_ctr < main->rowgroups_avail) + return; /* Need to suspend */ + main->context_state = CTX_PREPARE_FOR_IMCU; + if (*out_row_ctr >= out_rows_avail) + return; /* Postprocessor exactly filled output buf */ + /*FALLTHROUGH*/ + case CTX_PREPARE_FOR_IMCU: + /* Prepare to process first M-1 row groups of this iMCU row */ + main->rowgroup_ctr = 0; + main->rowgroups_avail = (JDIMENSION) (cinfo->min_DCT_scaled_size - 1); + /* Check for bottom of image: if so, tweak pointers to "duplicate" + * the last sample row, and adjust rowgroups_avail to ignore padding rows. + */ + if (main->iMCU_row_ctr == cinfo->total_iMCU_rows) + set_bottom_pointers(cinfo); + main->context_state = CTX_PROCESS_IMCU; + /*FALLTHROUGH*/ + case CTX_PROCESS_IMCU: + /* Call postprocessor using previously set pointers */ + (*cinfo->post->post_process_data) (cinfo, main->xbuffer[main->whichptr], + &main->rowgroup_ctr, main->rowgroups_avail, + output_buf, out_row_ctr, out_rows_avail); + if (main->rowgroup_ctr < main->rowgroups_avail) + return; /* Need to suspend */ + /* After the first iMCU, change wraparound pointers to normal state */ + if (main->iMCU_row_ctr == 1) + set_wraparound_pointers(cinfo); + /* Prepare to load new iMCU row using other xbuffer list */ + main->whichptr ^= 1; /* 0=>1 or 1=>0 */ + main->buffer_full = FALSE; + /* Still need to process last row group of this iMCU row, */ + /* which is saved at index M+1 of the other xbuffer */ + main->rowgroup_ctr = (JDIMENSION) (cinfo->min_DCT_scaled_size + 1); + main->rowgroups_avail = (JDIMENSION) (cinfo->min_DCT_scaled_size + 2); + main->context_state = CTX_POSTPONED_ROW; + } +} + + +/* + * Process some data. + * Final pass of two-pass quantization: just call the postprocessor. + * Source data will be the postprocessor controller's internal buffer. + */ + +#ifdef QUANT_2PASS_SUPPORTED + +METHODDEF void +process_data_crank_post (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + (*cinfo->post->post_process_data) (cinfo, (JSAMPIMAGE) NULL, + (JDIMENSION *) NULL, (JDIMENSION) 0, + output_buf, out_row_ctr, out_rows_avail); +} + +#endif /* QUANT_2PASS_SUPPORTED */ + + +/* + * Initialize main buffer controller. + */ + +GLOBAL void +jinit_d_main_controller (j_decompress_ptr cinfo, boolean need_full_buffer) +{ + my_main_ptr main; + int ci, rgroup, ngroups; + jpeg_component_info *compptr; + + main = (my_main_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_main_controller)); + cinfo->main = (struct jpeg_d_main_controller *) main; + main->pub.start_pass = start_pass_main; + + if (need_full_buffer) /* shouldn't happen */ + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + /* Allocate the workspace. + * ngroups is the number of row groups we need. + */ + if (cinfo->upsample->need_context_rows) { + if (cinfo->min_DCT_scaled_size < 2) /* unsupported, see comments above */ + ERREXIT(cinfo, JERR_NOTIMPL); + alloc_funny_pointers(cinfo); /* Alloc space for xbuffer[] lists */ + ngroups = cinfo->min_DCT_scaled_size + 2; + } else { + ngroups = cinfo->min_DCT_scaled_size; + } + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + main->buffer[ci] = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + compptr->width_in_blocks * compptr->DCT_scaled_size, + (JDIMENSION) (rgroup * ngroups)); + } +} diff --git a/utils/Radiant/jpeg-6/jdmarker.c b/utils/Radiant/jpeg-6/jdmarker.c new file mode 100644 index 0000000..6c3c519 --- /dev/null +++ b/utils/Radiant/jpeg-6/jdmarker.c @@ -0,0 +1,1052 @@ +/* + * jdmarker.c + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to decode JPEG datastream markers. + * Most of the complexity arises from our desire to support input + * suspension: if not all of the data for a marker is available, + * we must exit back to the application. On resumption, we reprocess + * the marker. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +typedef enum { /* JPEG marker codes */ + M_SOF0 = 0xc0, + M_SOF1 = 0xc1, + M_SOF2 = 0xc2, + M_SOF3 = 0xc3, + + M_SOF5 = 0xc5, + M_SOF6 = 0xc6, + M_SOF7 = 0xc7, + + M_JPG = 0xc8, + M_SOF9 = 0xc9, + M_SOF10 = 0xca, + M_SOF11 = 0xcb, + + M_SOF13 = 0xcd, + M_SOF14 = 0xce, + M_SOF15 = 0xcf, + + M_DHT = 0xc4, + + M_DAC = 0xcc, + + M_RST0 = 0xd0, + M_RST1 = 0xd1, + M_RST2 = 0xd2, + M_RST3 = 0xd3, + M_RST4 = 0xd4, + M_RST5 = 0xd5, + M_RST6 = 0xd6, + M_RST7 = 0xd7, + + M_SOI = 0xd8, + M_EOI = 0xd9, + M_SOS = 0xda, + M_DQT = 0xdb, + M_DNL = 0xdc, + M_DRI = 0xdd, + M_DHP = 0xde, + M_EXP = 0xdf, + + M_APP0 = 0xe0, + M_APP1 = 0xe1, + M_APP2 = 0xe2, + M_APP3 = 0xe3, + M_APP4 = 0xe4, + M_APP5 = 0xe5, + M_APP6 = 0xe6, + M_APP7 = 0xe7, + M_APP8 = 0xe8, + M_APP9 = 0xe9, + M_APP10 = 0xea, + M_APP11 = 0xeb, + M_APP12 = 0xec, + M_APP13 = 0xed, + M_APP14 = 0xee, + M_APP15 = 0xef, + + M_JPG0 = 0xf0, + M_JPG13 = 0xfd, + M_COM = 0xfe, + + M_TEM = 0x01, + + M_ERROR = 0x100 +} JPEG_MARKER; + + +/* + * Macros for fetching data from the data source module. + * + * At all times, cinfo->src->next_input_byte and ->bytes_in_buffer reflect + * the current restart point; we update them only when we have reached a + * suitable place to restart if a suspension occurs. + */ + +/* Declare and initialize local copies of input pointer/count */ +#define INPUT_VARS(cinfo) \ + struct jpeg_source_mgr * datasrc = (cinfo)->src; \ + const JOCTET * next_input_byte = datasrc->next_input_byte; \ + size_t bytes_in_buffer = datasrc->bytes_in_buffer + +/* Unload the local copies --- do this only at a restart boundary */ +#define INPUT_SYNC(cinfo) \ + ( datasrc->next_input_byte = next_input_byte, \ + datasrc->bytes_in_buffer = bytes_in_buffer ) + +/* Reload the local copies --- seldom used except in MAKE_BYTE_AVAIL */ +#define INPUT_RELOAD(cinfo) \ + ( next_input_byte = datasrc->next_input_byte, \ + bytes_in_buffer = datasrc->bytes_in_buffer ) + +/* Internal macro for INPUT_BYTE and INPUT_2BYTES: make a byte available. + * Note we do *not* do INPUT_SYNC before calling fill_input_buffer, + * but we must reload the local copies after a successful fill. + */ +#define MAKE_BYTE_AVAIL(cinfo,action) \ + if (bytes_in_buffer == 0) { \ + if (! (*datasrc->fill_input_buffer) (cinfo)) \ + { action; } \ + INPUT_RELOAD(cinfo); \ + } \ + bytes_in_buffer-- + +/* Read a byte into variable V. + * If must suspend, take the specified action (typically "return FALSE"). + */ +#define INPUT_BYTE(cinfo,V,action) \ + MAKESTMT( MAKE_BYTE_AVAIL(cinfo,action); \ + V = GETJOCTET(*next_input_byte++); ) + +/* As above, but read two bytes interpreted as an unsigned 16-bit integer. + * V should be declared unsigned int or perhaps INT32. + */ +#define INPUT_2BYTES(cinfo,V,action) \ + MAKESTMT( MAKE_BYTE_AVAIL(cinfo,action); \ + V = ((unsigned int) GETJOCTET(*next_input_byte++)) << 8; \ + MAKE_BYTE_AVAIL(cinfo,action); \ + V += GETJOCTET(*next_input_byte++); ) + + +/* + * Routines to process JPEG markers. + * + * Entry condition: JPEG marker itself has been read and its code saved + * in cinfo->unread_marker; input restart point is just after the marker. + * + * Exit: if return TRUE, have read and processed any parameters, and have + * updated the restart point to point after the parameters. + * If return FALSE, was forced to suspend before reaching end of + * marker parameters; restart point has not been moved. Same routine + * will be called again after application supplies more input data. + * + * This approach to suspension assumes that all of a marker's parameters can + * fit into a single input bufferload. This should hold for "normal" + * markers. Some COM/APPn markers might have large parameter segments, + * but we use skip_input_data to get past those, and thereby put the problem + * on the source manager's shoulders. + * + * Note that we don't bother to avoid duplicate trace messages if a + * suspension occurs within marker parameters. Other side effects + * require more care. + */ + + +LOCAL boolean +get_soi (j_decompress_ptr cinfo) +/* Process an SOI marker */ +{ + int i; + + TRACEMS(cinfo, 1, JTRC_SOI); + + if (cinfo->marker->saw_SOI) + ERREXIT(cinfo, JERR_SOI_DUPLICATE); + + /* Reset all parameters that are defined to be reset by SOI */ + + for (i = 0; i < NUM_ARITH_TBLS; i++) { + cinfo->arith_dc_L[i] = 0; + cinfo->arith_dc_U[i] = 1; + cinfo->arith_ac_K[i] = 5; + } + cinfo->restart_interval = 0; + + /* Set initial assumptions for colorspace etc */ + + cinfo->jpeg_color_space = JCS_UNKNOWN; + cinfo->CCIR601_sampling = FALSE; /* Assume non-CCIR sampling??? */ + + cinfo->saw_JFIF_marker = FALSE; + cinfo->density_unit = 0; /* set default JFIF APP0 values */ + cinfo->X_density = 1; + cinfo->Y_density = 1; + cinfo->saw_Adobe_marker = FALSE; + cinfo->Adobe_transform = 0; + + cinfo->marker->saw_SOI = TRUE; + + return TRUE; +} + + +LOCAL boolean +get_sof (j_decompress_ptr cinfo, boolean is_prog, boolean is_arith) +/* Process a SOFn marker */ +{ + INT32 length; + int c, ci; + jpeg_component_info * compptr; + INPUT_VARS(cinfo); + + cinfo->progressive_mode = is_prog; + cinfo->arith_code = is_arith; + + INPUT_2BYTES(cinfo, length, return FALSE); + + INPUT_BYTE(cinfo, cinfo->data_precision, return FALSE); + INPUT_2BYTES(cinfo, cinfo->image_height, return FALSE); + INPUT_2BYTES(cinfo, cinfo->image_width, return FALSE); + INPUT_BYTE(cinfo, cinfo->num_components, return FALSE); + + length -= 8; + + TRACEMS4(cinfo, 1, JTRC_SOF, cinfo->unread_marker, + (int) cinfo->image_width, (int) cinfo->image_height, + cinfo->num_components); + + if (cinfo->marker->saw_SOF) + ERREXIT(cinfo, JERR_SOF_DUPLICATE); + + /* We don't support files in which the image height is initially specified */ + /* as 0 and is later redefined by DNL. As long as we have to check that, */ + /* might as well have a general sanity check. */ + if (cinfo->image_height <= 0 || cinfo->image_width <= 0 + || cinfo->num_components <= 0) + ERREXIT(cinfo, JERR_EMPTY_IMAGE); + + if (length != (cinfo->num_components * 3)) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + if (cinfo->comp_info == NULL) /* do only once, even if suspend */ + cinfo->comp_info = (jpeg_component_info *) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->num_components * SIZEOF(jpeg_component_info)); + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + compptr->component_index = ci; + INPUT_BYTE(cinfo, compptr->component_id, return FALSE); + INPUT_BYTE(cinfo, c, return FALSE); + compptr->h_samp_factor = (c >> 4) & 15; + compptr->v_samp_factor = (c ) & 15; + INPUT_BYTE(cinfo, compptr->quant_tbl_no, return FALSE); + + TRACEMS4(cinfo, 1, JTRC_SOF_COMPONENT, + compptr->component_id, compptr->h_samp_factor, + compptr->v_samp_factor, compptr->quant_tbl_no); + } + + cinfo->marker->saw_SOF = TRUE; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL boolean +get_sos (j_decompress_ptr cinfo) +/* Process a SOS marker */ +{ + INT32 length; + int i, ci, n, c, cc; + jpeg_component_info * compptr; + INPUT_VARS(cinfo); + + if (! cinfo->marker->saw_SOF) + ERREXIT(cinfo, JERR_SOS_NO_SOF); + + INPUT_2BYTES(cinfo, length, return FALSE); + + INPUT_BYTE(cinfo, n, return FALSE); /* Number of components */ + + if (length != (n * 2 + 6) || n < 1 || n > MAX_COMPS_IN_SCAN) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + TRACEMS1(cinfo, 1, JTRC_SOS, n); + + cinfo->comps_in_scan = n; + + /* Collect the component-spec parameters */ + + for (i = 0; i < n; i++) { + INPUT_BYTE(cinfo, cc, return FALSE); + INPUT_BYTE(cinfo, c, return FALSE); + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + if (cc == compptr->component_id) + goto id_found; + } + + ERREXIT1(cinfo, JERR_BAD_COMPONENT_ID, cc); + + id_found: + + cinfo->cur_comp_info[i] = compptr; + compptr->dc_tbl_no = (c >> 4) & 15; + compptr->ac_tbl_no = (c ) & 15; + + TRACEMS3(cinfo, 1, JTRC_SOS_COMPONENT, cc, + compptr->dc_tbl_no, compptr->ac_tbl_no); + } + + /* Collect the additional scan parameters Ss, Se, Ah/Al. */ + INPUT_BYTE(cinfo, c, return FALSE); + cinfo->Ss = c; + INPUT_BYTE(cinfo, c, return FALSE); + cinfo->Se = c; + INPUT_BYTE(cinfo, c, return FALSE); + cinfo->Ah = (c >> 4) & 15; + cinfo->Al = (c ) & 15; + + TRACEMS4(cinfo, 1, JTRC_SOS_PARAMS, cinfo->Ss, cinfo->Se, + cinfo->Ah, cinfo->Al); + + /* Prepare to scan data & restart markers */ + cinfo->marker->next_restart_num = 0; + + /* Count another SOS marker */ + cinfo->input_scan_number++; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +METHODDEF boolean +get_app0 (j_decompress_ptr cinfo) +/* Process an APP0 marker */ +{ +#define JFIF_LEN 14 + INT32 length; + UINT8 b[JFIF_LEN]; + int buffp; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + /* See if a JFIF APP0 marker is present */ + + if (length >= JFIF_LEN) { + for (buffp = 0; buffp < JFIF_LEN; buffp++) + INPUT_BYTE(cinfo, b[buffp], return FALSE); + length -= JFIF_LEN; + + if (b[0]==0x4A && b[1]==0x46 && b[2]==0x49 && b[3]==0x46 && b[4]==0) { + /* Found JFIF APP0 marker: check version */ + /* Major version must be 1, anything else signals an incompatible change. + * We used to treat this as an error, but now it's a nonfatal warning, + * because some bozo at Hijaak couldn't read the spec. + * Minor version should be 0..2, but process anyway if newer. + */ + if (b[5] != 1) + WARNMS2(cinfo, JWRN_JFIF_MAJOR, b[5], b[6]); + else if (b[6] > 2) + TRACEMS2(cinfo, 1, JTRC_JFIF_MINOR, b[5], b[6]); + /* Save info */ + cinfo->saw_JFIF_marker = TRUE; + cinfo->density_unit = b[7]; + cinfo->X_density = (b[8] << 8) + b[9]; + cinfo->Y_density = (b[10] << 8) + b[11]; + TRACEMS3(cinfo, 1, JTRC_JFIF, + cinfo->X_density, cinfo->Y_density, cinfo->density_unit); + if (b[12] | b[13]) + TRACEMS2(cinfo, 1, JTRC_JFIF_THUMBNAIL, b[12], b[13]); + if (length != ((INT32) b[12] * (INT32) b[13] * (INT32) 3)) + TRACEMS1(cinfo, 1, JTRC_JFIF_BADTHUMBNAILSIZE, (int) length); + } else { + /* Start of APP0 does not match "JFIF" */ + TRACEMS1(cinfo, 1, JTRC_APP0, (int) length + JFIF_LEN); + } + } else { + /* Too short to be JFIF marker */ + TRACEMS1(cinfo, 1, JTRC_APP0, (int) length); + } + + INPUT_SYNC(cinfo); + if (length > 0) /* skip any remaining data -- could be lots */ + (*cinfo->src->skip_input_data) (cinfo, (long) length); + + return TRUE; +} + + +METHODDEF boolean +get_app14 (j_decompress_ptr cinfo) +/* Process an APP14 marker */ +{ +#define ADOBE_LEN 12 + INT32 length; + UINT8 b[ADOBE_LEN]; + int buffp; + unsigned int version, flags0, flags1, transform; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + /* See if an Adobe APP14 marker is present */ + + if (length >= ADOBE_LEN) { + for (buffp = 0; buffp < ADOBE_LEN; buffp++) + INPUT_BYTE(cinfo, b[buffp], return FALSE); + length -= ADOBE_LEN; + + if (b[0]==0x41 && b[1]==0x64 && b[2]==0x6F && b[3]==0x62 && b[4]==0x65) { + /* Found Adobe APP14 marker */ + version = (b[5] << 8) + b[6]; + flags0 = (b[7] << 8) + b[8]; + flags1 = (b[9] << 8) + b[10]; + transform = b[11]; + TRACEMS4(cinfo, 1, JTRC_ADOBE, version, flags0, flags1, transform); + cinfo->saw_Adobe_marker = TRUE; + cinfo->Adobe_transform = (UINT8) transform; + } else { + /* Start of APP14 does not match "Adobe" */ + TRACEMS1(cinfo, 1, JTRC_APP14, (int) length + ADOBE_LEN); + } + } else { + /* Too short to be Adobe marker */ + TRACEMS1(cinfo, 1, JTRC_APP14, (int) length); + } + + INPUT_SYNC(cinfo); + if (length > 0) /* skip any remaining data -- could be lots */ + (*cinfo->src->skip_input_data) (cinfo, (long) length); + + return TRUE; +} + + +LOCAL boolean +get_dac (j_decompress_ptr cinfo) +/* Process a DAC marker */ +{ + INT32 length; + int index, val; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + while (length > 0) { + INPUT_BYTE(cinfo, index, return FALSE); + INPUT_BYTE(cinfo, val, return FALSE); + + length -= 2; + + TRACEMS2(cinfo, 1, JTRC_DAC, index, val); + + if (index < 0 || index >= (2*NUM_ARITH_TBLS)) + ERREXIT1(cinfo, JERR_DAC_INDEX, index); + + if (index >= NUM_ARITH_TBLS) { /* define AC table */ + cinfo->arith_ac_K[index-NUM_ARITH_TBLS] = (UINT8) val; + } else { /* define DC table */ + cinfo->arith_dc_L[index] = (UINT8) (val & 0x0F); + cinfo->arith_dc_U[index] = (UINT8) (val >> 4); + if (cinfo->arith_dc_L[index] > cinfo->arith_dc_U[index]) + ERREXIT1(cinfo, JERR_DAC_VALUE, val); + } + } + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL boolean +get_dht (j_decompress_ptr cinfo) +/* Process a DHT marker */ +{ + INT32 length; + UINT8 bits[17]; + UINT8 huffval[256]; + int i, index, count; + JHUFF_TBL **htblptr; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + while (length > 0) { + INPUT_BYTE(cinfo, index, return FALSE); + + TRACEMS1(cinfo, 1, JTRC_DHT, index); + + bits[0] = 0; + count = 0; + for (i = 1; i <= 16; i++) { + INPUT_BYTE(cinfo, bits[i], return FALSE); + count += bits[i]; + } + + length -= 1 + 16; + + TRACEMS8(cinfo, 2, JTRC_HUFFBITS, + bits[1], bits[2], bits[3], bits[4], + bits[5], bits[6], bits[7], bits[8]); + TRACEMS8(cinfo, 2, JTRC_HUFFBITS, + bits[9], bits[10], bits[11], bits[12], + bits[13], bits[14], bits[15], bits[16]); + + if (count > 256 || ((INT32) count) > length) + ERREXIT(cinfo, JERR_DHT_COUNTS); + + for (i = 0; i < count; i++) + INPUT_BYTE(cinfo, huffval[i], return FALSE); + + length -= count; + + if (index & 0x10) { /* AC table definition */ + index -= 0x10; + htblptr = &cinfo->ac_huff_tbl_ptrs[index]; + } else { /* DC table definition */ + htblptr = &cinfo->dc_huff_tbl_ptrs[index]; + } + + if (index < 0 || index >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_DHT_INDEX, index); + + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + + MEMCOPY((*htblptr)->bits, bits, SIZEOF((*htblptr)->bits)); + MEMCOPY((*htblptr)->huffval, huffval, SIZEOF((*htblptr)->huffval)); + } + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL boolean +get_dqt (j_decompress_ptr cinfo) +/* Process a DQT marker */ +{ + INT32 length; + int n, i, prec; + unsigned int tmp; + JQUANT_TBL *quant_ptr; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + while (length > 0) { + INPUT_BYTE(cinfo, n, return FALSE); + prec = n >> 4; + n &= 0x0F; + + TRACEMS2(cinfo, 1, JTRC_DQT, n, prec); + + if (n >= NUM_QUANT_TBLS) + ERREXIT1(cinfo, JERR_DQT_INDEX, n); + + if (cinfo->quant_tbl_ptrs[n] == NULL) + cinfo->quant_tbl_ptrs[n] = jpeg_alloc_quant_table((j_common_ptr) cinfo); + quant_ptr = cinfo->quant_tbl_ptrs[n]; + + for (i = 0; i < DCTSIZE2; i++) { + if (prec) + INPUT_2BYTES(cinfo, tmp, return FALSE); + else + INPUT_BYTE(cinfo, tmp, return FALSE); + quant_ptr->quantval[i] = (UINT16) tmp; + } + + for (i = 0; i < DCTSIZE2; i += 8) { + TRACEMS8(cinfo, 2, JTRC_QUANTVALS, + quant_ptr->quantval[i ], quant_ptr->quantval[i+1], + quant_ptr->quantval[i+2], quant_ptr->quantval[i+3], + quant_ptr->quantval[i+4], quant_ptr->quantval[i+5], + quant_ptr->quantval[i+6], quant_ptr->quantval[i+7]); + } + + length -= DCTSIZE2+1; + if (prec) length -= DCTSIZE2; + } + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL boolean +get_dri (j_decompress_ptr cinfo) +/* Process a DRI marker */ +{ + INT32 length; + unsigned int tmp; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + + if (length != 4) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + INPUT_2BYTES(cinfo, tmp, return FALSE); + + TRACEMS1(cinfo, 1, JTRC_DRI, tmp); + + cinfo->restart_interval = tmp; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +METHODDEF boolean +skip_variable (j_decompress_ptr cinfo) +/* Skip over an unknown or uninteresting variable-length marker */ +{ + INT32 length; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + + TRACEMS2(cinfo, 1, JTRC_MISC_MARKER, cinfo->unread_marker, (int) length); + + INPUT_SYNC(cinfo); /* do before skip_input_data */ + (*cinfo->src->skip_input_data) (cinfo, (long) length - 2L); + + return TRUE; +} + + +/* + * Find the next JPEG marker, save it in cinfo->unread_marker. + * Returns FALSE if had to suspend before reaching a marker; + * in that case cinfo->unread_marker is unchanged. + * + * Note that the result might not be a valid marker code, + * but it will never be 0 or FF. + */ + +LOCAL boolean +next_marker (j_decompress_ptr cinfo) +{ + int c; + INPUT_VARS(cinfo); + + for (;;) { + INPUT_BYTE(cinfo, c, return FALSE); + /* Skip any non-FF bytes. + * This may look a bit inefficient, but it will not occur in a valid file. + * We sync after each discarded byte so that a suspending data source + * can discard the byte from its buffer. + */ + while (c != 0xFF) { + cinfo->marker->discarded_bytes++; + INPUT_SYNC(cinfo); + INPUT_BYTE(cinfo, c, return FALSE); + } + /* This loop swallows any duplicate FF bytes. Extra FFs are legal as + * pad bytes, so don't count them in discarded_bytes. We assume there + * will not be so many consecutive FF bytes as to overflow a suspending + * data source's input buffer. + */ + do { + INPUT_BYTE(cinfo, c, return FALSE); + } while (c == 0xFF); + if (c != 0) + break; /* found a valid marker, exit loop */ + /* Reach here if we found a stuffed-zero data sequence (FF/00). + * Discard it and loop back to try again. + */ + cinfo->marker->discarded_bytes += 2; + INPUT_SYNC(cinfo); + } + + if (cinfo->marker->discarded_bytes != 0) { + WARNMS2(cinfo, JWRN_EXTRANEOUS_DATA, cinfo->marker->discarded_bytes, c); + cinfo->marker->discarded_bytes = 0; + } + + cinfo->unread_marker = c; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL boolean +first_marker (j_decompress_ptr cinfo) +/* Like next_marker, but used to obtain the initial SOI marker. */ +/* For this marker, we do not allow preceding garbage or fill; otherwise, + * we might well scan an entire input file before realizing it ain't JPEG. + * If an application wants to process non-JFIF files, it must seek to the + * SOI before calling the JPEG library. + */ +{ + int c, c2; + INPUT_VARS(cinfo); + + INPUT_BYTE(cinfo, c, return FALSE); + INPUT_BYTE(cinfo, c2, return FALSE); + if (c != 0xFF || c2 != (int) M_SOI) + ERREXIT2(cinfo, JERR_NO_SOI, c, c2); + + cinfo->unread_marker = c2; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +/* + * Read markers until SOS or EOI. + * + * Returns same codes as are defined for jpeg_consume_input: + * JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + */ + +METHODDEF int +read_markers (j_decompress_ptr cinfo) +{ + /* Outer loop repeats once for each marker. */ + for (;;) { + /* Collect the marker proper, unless we already did. */ + /* NB: first_marker() enforces the requirement that SOI appear first. */ + if (cinfo->unread_marker == 0) { + if (! cinfo->marker->saw_SOI) { + if (! first_marker(cinfo)) + return JPEG_SUSPENDED; + } else { + if (! next_marker(cinfo)) + return JPEG_SUSPENDED; + } + } + /* At this point cinfo->unread_marker contains the marker code and the + * input point is just past the marker proper, but before any parameters. + * A suspension will cause us to return with this state still true. + */ + switch (cinfo->unread_marker) { + case M_SOI: + if (! get_soi(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_SOF0: /* Baseline */ + case M_SOF1: /* Extended sequential, Huffman */ + if (! get_sof(cinfo, FALSE, FALSE)) + return JPEG_SUSPENDED; + break; + + case M_SOF2: /* Progressive, Huffman */ + if (! get_sof(cinfo, TRUE, FALSE)) + return JPEG_SUSPENDED; + break; + + case M_SOF9: /* Extended sequential, arithmetic */ + if (! get_sof(cinfo, FALSE, TRUE)) + return JPEG_SUSPENDED; + break; + + case M_SOF10: /* Progressive, arithmetic */ + if (! get_sof(cinfo, TRUE, TRUE)) + return JPEG_SUSPENDED; + break; + + /* Currently unsupported SOFn types */ + case M_SOF3: /* Lossless, Huffman */ + case M_SOF5: /* Differential sequential, Huffman */ + case M_SOF6: /* Differential progressive, Huffman */ + case M_SOF7: /* Differential lossless, Huffman */ + case M_JPG: /* Reserved for JPEG extensions */ + case M_SOF11: /* Lossless, arithmetic */ + case M_SOF13: /* Differential sequential, arithmetic */ + case M_SOF14: /* Differential progressive, arithmetic */ + case M_SOF15: /* Differential lossless, arithmetic */ + ERREXIT1(cinfo, JERR_SOF_UNSUPPORTED, cinfo->unread_marker); + break; + + case M_SOS: + if (! get_sos(cinfo)) + return JPEG_SUSPENDED; + cinfo->unread_marker = 0; /* processed the marker */ + return JPEG_REACHED_SOS; + + case M_EOI: + TRACEMS(cinfo, 1, JTRC_EOI); + cinfo->unread_marker = 0; /* processed the marker */ + return JPEG_REACHED_EOI; + + case M_DAC: + if (! get_dac(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_DHT: + if (! get_dht(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_DQT: + if (! get_dqt(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_DRI: + if (! get_dri(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_APP0: + case M_APP1: + case M_APP2: + case M_APP3: + case M_APP4: + case M_APP5: + case M_APP6: + case M_APP7: + case M_APP8: + case M_APP9: + case M_APP10: + case M_APP11: + case M_APP12: + case M_APP13: + case M_APP14: + case M_APP15: + if (! (*cinfo->marker->process_APPn[cinfo->unread_marker - (int) M_APP0]) (cinfo)) + return JPEG_SUSPENDED; + break; + + case M_COM: + if (! (*cinfo->marker->process_COM) (cinfo)) + return JPEG_SUSPENDED; + break; + + case M_RST0: /* these are all parameterless */ + case M_RST1: + case M_RST2: + case M_RST3: + case M_RST4: + case M_RST5: + case M_RST6: + case M_RST7: + case M_TEM: + TRACEMS1(cinfo, 1, JTRC_PARMLESS_MARKER, cinfo->unread_marker); + break; + + case M_DNL: /* Ignore DNL ... perhaps the wrong thing */ + if (! skip_variable(cinfo)) + return JPEG_SUSPENDED; + break; + + default: /* must be DHP, EXP, JPGn, or RESn */ + /* For now, we treat the reserved markers as fatal errors since they are + * likely to be used to signal incompatible JPEG Part 3 extensions. + * Once the JPEG 3 version-number marker is well defined, this code + * ought to change! + */ + ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, cinfo->unread_marker); + break; + } + /* Successfully processed marker, so reset state variable */ + cinfo->unread_marker = 0; + } /* end loop */ +} + + +/* + * Read a restart marker, which is expected to appear next in the datastream; + * if the marker is not there, take appropriate recovery action. + * Returns FALSE if suspension is required. + * + * This is called by the entropy decoder after it has read an appropriate + * number of MCUs. cinfo->unread_marker may be nonzero if the entropy decoder + * has already read a marker from the data source. Under normal conditions + * cinfo->unread_marker will be reset to 0 before returning; if not reset, + * it holds a marker which the decoder will be unable to read past. + */ + +METHODDEF boolean +read_restart_marker (j_decompress_ptr cinfo) +{ + /* Obtain a marker unless we already did. */ + /* Note that next_marker will complain if it skips any data. */ + if (cinfo->unread_marker == 0) { + if (! next_marker(cinfo)) + return FALSE; + } + + if (cinfo->unread_marker == + ((int) M_RST0 + cinfo->marker->next_restart_num)) { + /* Normal case --- swallow the marker and let entropy decoder continue */ + TRACEMS1(cinfo, 2, JTRC_RST, cinfo->marker->next_restart_num); + cinfo->unread_marker = 0; + } else { + /* Uh-oh, the restart markers have been messed up. */ + /* Let the data source manager determine how to resync. */ + if (! (*cinfo->src->resync_to_restart) (cinfo, + cinfo->marker->next_restart_num)) + return FALSE; + } + + /* Update next-restart state */ + cinfo->marker->next_restart_num = (cinfo->marker->next_restart_num + 1) & 7; + + return TRUE; +} + + +/* + * This is the default resync_to_restart method for data source managers + * to use if they don't have any better approach. Some data source managers + * may be able to back up, or may have additional knowledge about the data + * which permits a more intelligent recovery strategy; such managers would + * presumably supply their own resync method. + * + * read_restart_marker calls resync_to_restart if it finds a marker other than + * the restart marker it was expecting. (This code is *not* used unless + * a nonzero restart interval has been declared.) cinfo->unread_marker is + * the marker code actually found (might be anything, except 0 or FF). + * The desired restart marker number (0..7) is passed as a parameter. + * This routine is supposed to apply whatever error recovery strategy seems + * appropriate in order to position the input stream to the next data segment. + * Note that cinfo->unread_marker is treated as a marker appearing before + * the current data-source input point; usually it should be reset to zero + * before returning. + * Returns FALSE if suspension is required. + * + * This implementation is substantially constrained by wanting to treat the + * input as a data stream; this means we can't back up. Therefore, we have + * only the following actions to work with: + * 1. Simply discard the marker and let the entropy decoder resume at next + * byte of file. + * 2. Read forward until we find another marker, discarding intervening + * data. (In theory we could look ahead within the current bufferload, + * without having to discard data if we don't find the desired marker. + * This idea is not implemented here, in part because it makes behavior + * dependent on buffer size and chance buffer-boundary positions.) + * 3. Leave the marker unread (by failing to zero cinfo->unread_marker). + * This will cause the entropy decoder to process an empty data segment, + * inserting dummy zeroes, and then we will reprocess the marker. + * + * #2 is appropriate if we think the desired marker lies ahead, while #3 is + * appropriate if the found marker is a future restart marker (indicating + * that we have missed the desired restart marker, probably because it got + * corrupted). + * We apply #2 or #3 if the found marker is a restart marker no more than + * two counts behind or ahead of the expected one. We also apply #2 if the + * found marker is not a legal JPEG marker code (it's certainly bogus data). + * If the found marker is a restart marker more than 2 counts away, we do #1 + * (too much risk that the marker is erroneous; with luck we will be able to + * resync at some future point). + * For any valid non-restart JPEG marker, we apply #3. This keeps us from + * overrunning the end of a scan. An implementation limited to single-scan + * files might find it better to apply #2 for markers other than EOI, since + * any other marker would have to be bogus data in that case. + */ + +GLOBAL boolean +jpeg_resync_to_restart (j_decompress_ptr cinfo, int desired) +{ + int marker = cinfo->unread_marker; + int action = 1; + + /* Always put up a warning. */ + WARNMS2(cinfo, JWRN_MUST_RESYNC, marker, desired); + + /* Outer loop handles repeated decision after scanning forward. */ + for (;;) { + if (marker < (int) M_SOF0) + action = 2; /* invalid marker */ + else if (marker < (int) M_RST0 || marker > (int) M_RST7) + action = 3; /* valid non-restart marker */ + else { + if (marker == ((int) M_RST0 + ((desired+1) & 7)) || + marker == ((int) M_RST0 + ((desired+2) & 7))) + action = 3; /* one of the next two expected restarts */ + else if (marker == ((int) M_RST0 + ((desired-1) & 7)) || + marker == ((int) M_RST0 + ((desired-2) & 7))) + action = 2; /* a prior restart, so advance */ + else + action = 1; /* desired restart or too far away */ + } + TRACEMS2(cinfo, 4, JTRC_RECOVERY_ACTION, marker, action); + switch (action) { + case 1: + /* Discard marker and let entropy decoder resume processing. */ + cinfo->unread_marker = 0; + return TRUE; + case 2: + /* Scan to the next marker, and repeat the decision loop. */ + if (! next_marker(cinfo)) + return FALSE; + marker = cinfo->unread_marker; + break; + case 3: + /* Return without advancing past this marker. */ + /* Entropy decoder will be forced to process an empty segment. */ + return TRUE; + } + } /* end loop */ +} + + +/* + * Reset marker processing state to begin a fresh datastream. + */ + +METHODDEF void +reset_marker_reader (j_decompress_ptr cinfo) +{ + cinfo->comp_info = NULL; /* until allocated by get_sof */ + cinfo->input_scan_number = 0; /* no SOS seen yet */ + cinfo->unread_marker = 0; /* no pending marker */ + cinfo->marker->saw_SOI = FALSE; /* set internal state too */ + cinfo->marker->saw_SOF = FALSE; + cinfo->marker->discarded_bytes = 0; +} + + +/* + * Initialize the marker reader module. + * This is called only once, when the decompression object is created. + */ + +GLOBAL void +jinit_marker_reader (j_decompress_ptr cinfo) +{ + int i; + + /* Create subobject in permanent pool */ + cinfo->marker = (struct jpeg_marker_reader *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(struct jpeg_marker_reader)); + /* Initialize method pointers */ + cinfo->marker->reset_marker_reader = reset_marker_reader; + cinfo->marker->read_markers = read_markers; + cinfo->marker->read_restart_marker = read_restart_marker; + cinfo->marker->process_COM = skip_variable; + for (i = 0; i < 16; i++) + cinfo->marker->process_APPn[i] = skip_variable; + cinfo->marker->process_APPn[0] = get_app0; + cinfo->marker->process_APPn[14] = get_app14; + /* Reset marker processing state */ + reset_marker_reader(cinfo); +} diff --git a/utils/Radiant/jpeg-6/jdmaster.c b/utils/Radiant/jpeg-6/jdmaster.c new file mode 100644 index 0000000..64f730f --- /dev/null +++ b/utils/Radiant/jpeg-6/jdmaster.c @@ -0,0 +1,557 @@ +/* + * jdmaster.c + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains master control logic for the JPEG decompressor. + * These routines are concerned with selecting the modules to be executed + * and with determining the number of passes and the work to be done in each + * pass. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Private state */ + +typedef struct { + struct jpeg_decomp_master pub; /* public fields */ + + int pass_number; /* # of passes completed */ + + boolean using_merged_upsample; /* TRUE if using merged upsample/cconvert */ + + /* Saved references to initialized quantizer modules, + * in case we need to switch modes. + */ + struct jpeg_color_quantizer * quantizer_1pass; + struct jpeg_color_quantizer * quantizer_2pass; +} my_decomp_master; + +typedef my_decomp_master * my_master_ptr; + + +/* + * Determine whether merged upsample/color conversion should be used. + * CRUCIAL: this must match the actual capabilities of jdmerge.c! + */ + +LOCAL boolean +use_merged_upsample (j_decompress_ptr cinfo) +{ +#ifdef UPSAMPLE_MERGING_SUPPORTED + /* Merging is the equivalent of plain box-filter upsampling */ + if (cinfo->do_fancy_upsampling || cinfo->CCIR601_sampling) + return FALSE; + /* jdmerge.c only supports YCC=>RGB color conversion */ + if (cinfo->jpeg_color_space != JCS_YCbCr || cinfo->num_components != 3 || + cinfo->out_color_space != JCS_RGB || + cinfo->out_color_components != RGB_PIXELSIZE) + return FALSE; + /* and it only handles 2h1v or 2h2v sampling ratios */ + if (cinfo->comp_info[0].h_samp_factor != 2 || + cinfo->comp_info[1].h_samp_factor != 1 || + cinfo->comp_info[2].h_samp_factor != 1 || + cinfo->comp_info[0].v_samp_factor > 2 || + cinfo->comp_info[1].v_samp_factor != 1 || + cinfo->comp_info[2].v_samp_factor != 1) + return FALSE; + /* furthermore, it doesn't work if we've scaled the IDCTs differently */ + if (cinfo->comp_info[0].DCT_scaled_size != cinfo->min_DCT_scaled_size || + cinfo->comp_info[1].DCT_scaled_size != cinfo->min_DCT_scaled_size || + cinfo->comp_info[2].DCT_scaled_size != cinfo->min_DCT_scaled_size) + return FALSE; + /* ??? also need to test for upsample-time rescaling, when & if supported */ + return TRUE; /* by golly, it'll work... */ +#else + return FALSE; +#endif +} + + +/* + * Compute output image dimensions and related values. + * NOTE: this is exported for possible use by application. + * Hence it mustn't do anything that can't be done twice. + * Also note that it may be called before the master module is initialized! + */ + +GLOBAL void +jpeg_calc_output_dimensions (j_decompress_ptr cinfo) +/* Do computations that are needed before master selection phase */ +{ +#if 0 // JDC: commented out to remove warning + int ci; + jpeg_component_info *compptr; +#endif + + /* Prevent application from calling me at wrong times */ + if (cinfo->global_state != DSTATE_READY) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + +#ifdef IDCT_SCALING_SUPPORTED + + /* Compute actual output image dimensions and DCT scaling choices. */ + if (cinfo->scale_num * 8 <= cinfo->scale_denom) { + /* Provide 1/8 scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 8L); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 8L); + cinfo->min_DCT_scaled_size = 1; + } else if (cinfo->scale_num * 4 <= cinfo->scale_denom) { + /* Provide 1/4 scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 4L); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 4L); + cinfo->min_DCT_scaled_size = 2; + } else if (cinfo->scale_num * 2 <= cinfo->scale_denom) { + /* Provide 1/2 scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 2L); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 2L); + cinfo->min_DCT_scaled_size = 4; + } else { + /* Provide 1/1 scaling */ + cinfo->output_width = cinfo->image_width; + cinfo->output_height = cinfo->image_height; + cinfo->min_DCT_scaled_size = DCTSIZE; + } + /* In selecting the actual DCT scaling for each component, we try to + * scale up the chroma components via IDCT scaling rather than upsampling. + * This saves time if the upsampler gets to use 1:1 scaling. + * Note this code assumes that the supported DCT scalings are powers of 2. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + int ssize = cinfo->min_DCT_scaled_size; + while (ssize < DCTSIZE && + (compptr->h_samp_factor * ssize * 2 <= + cinfo->max_h_samp_factor * cinfo->min_DCT_scaled_size) && + (compptr->v_samp_factor * ssize * 2 <= + cinfo->max_v_samp_factor * cinfo->min_DCT_scaled_size)) { + ssize = ssize * 2; + } + compptr->DCT_scaled_size = ssize; + } + + /* Recompute downsampled dimensions of components; + * application needs to know these if using raw downsampled data. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Size in samples, after IDCT scaling */ + compptr->downsampled_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * + (long) (compptr->h_samp_factor * compptr->DCT_scaled_size), + (long) (cinfo->max_h_samp_factor * DCTSIZE)); + compptr->downsampled_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * + (long) (compptr->v_samp_factor * compptr->DCT_scaled_size), + (long) (cinfo->max_v_samp_factor * DCTSIZE)); + } + +#else /* !IDCT_SCALING_SUPPORTED */ + + /* Hardwire it to "no scaling" */ + cinfo->output_width = cinfo->image_width; + cinfo->output_height = cinfo->image_height; + /* jdinput.c has already initialized DCT_scaled_size to DCTSIZE, + * and has computed unscaled downsampled_width and downsampled_height. + */ + +#endif /* IDCT_SCALING_SUPPORTED */ + + /* Report number of components in selected colorspace. */ + /* Probably this should be in the color conversion module... */ + switch (cinfo->out_color_space) { + case JCS_GRAYSCALE: + cinfo->out_color_components = 1; + break; + case JCS_RGB: +#if RGB_PIXELSIZE != 3 + cinfo->out_color_components = RGB_PIXELSIZE; + break; +#endif /* else share code with YCbCr */ + case JCS_YCbCr: + cinfo->out_color_components = 3; + break; + case JCS_CMYK: + case JCS_YCCK: + cinfo->out_color_components = 4; + break; + default: /* else must be same colorspace as in file */ + cinfo->out_color_components = cinfo->num_components; + break; + } + cinfo->output_components = (cinfo->quantize_colors ? 1 : + cinfo->out_color_components); + + /* See if upsampler will want to emit more than one row at a time */ + if (use_merged_upsample(cinfo)) + cinfo->rec_outbuf_height = cinfo->max_v_samp_factor; + else + cinfo->rec_outbuf_height = 1; +} + + +/* + * Several decompression processes need to range-limit values to the range + * 0..MAXJSAMPLE; the input value may fall somewhat outside this range + * due to noise introduced by quantization, roundoff error, etc. These + * processes are inner loops and need to be as fast as possible. On most + * machines, particularly CPUs with pipelines or instruction prefetch, + * a (subscript-check-less) C table lookup + * x = sample_range_limit[x]; + * is faster than explicit tests + * if (x < 0) x = 0; + * else if (x > MAXJSAMPLE) x = MAXJSAMPLE; + * These processes all use a common table prepared by the routine below. + * + * For most steps we can mathematically guarantee that the initial value + * of x is within MAXJSAMPLE+1 of the legal range, so a table running from + * -(MAXJSAMPLE+1) to 2*MAXJSAMPLE+1 is sufficient. But for the initial + * limiting step (just after the IDCT), a wildly out-of-range value is + * possible if the input data is corrupt. To avoid any chance of indexing + * off the end of memory and getting a bad-pointer trap, we perform the + * post-IDCT limiting thus: + * x = range_limit[x & MASK]; + * where MASK is 2 bits wider than legal sample data, ie 10 bits for 8-bit + * samples. Under normal circumstances this is more than enough range and + * a correct output will be generated; with bogus input data the mask will + * cause wraparound, and we will safely generate a bogus-but-in-range output. + * For the post-IDCT step, we want to convert the data from signed to unsigned + * representation by adding CENTERJSAMPLE at the same time that we limit it. + * So the post-IDCT limiting table ends up looking like this: + * CENTERJSAMPLE,CENTERJSAMPLE+1,...,MAXJSAMPLE, + * MAXJSAMPLE (repeat 2*(MAXJSAMPLE+1)-CENTERJSAMPLE times), + * 0 (repeat 2*(MAXJSAMPLE+1)-CENTERJSAMPLE times), + * 0,1,...,CENTERJSAMPLE-1 + * Negative inputs select values from the upper half of the table after + * masking. + * + * We can save some space by overlapping the start of the post-IDCT table + * with the simpler range limiting table. The post-IDCT table begins at + * sample_range_limit + CENTERJSAMPLE. + * + * Note that the table is allocated in near data space on PCs; it's small + * enough and used often enough to justify this. + */ + +LOCAL void +prepare_range_limit_table (j_decompress_ptr cinfo) +/* Allocate and fill in the sample_range_limit table */ +{ + JSAMPLE * table; + int i; + + table = (JSAMPLE *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (5 * (MAXJSAMPLE+1) + CENTERJSAMPLE) * SIZEOF(JSAMPLE)); + table += (MAXJSAMPLE+1); /* allow negative subscripts of simple table */ + cinfo->sample_range_limit = table; + /* First segment of "simple" table: limit[x] = 0 for x < 0 */ + MEMZERO(table - (MAXJSAMPLE+1), (MAXJSAMPLE+1) * SIZEOF(JSAMPLE)); + /* Main part of "simple" table: limit[x] = x */ + for (i = 0; i <= MAXJSAMPLE; i++) + table[i] = (JSAMPLE) i; + table += CENTERJSAMPLE; /* Point to where post-IDCT table starts */ + /* End of simple table, rest of first half of post-IDCT table */ + for (i = CENTERJSAMPLE; i < 2*(MAXJSAMPLE+1); i++) + table[i] = MAXJSAMPLE; + /* Second half of post-IDCT table */ + MEMZERO(table + (2 * (MAXJSAMPLE+1)), + (2 * (MAXJSAMPLE+1) - CENTERJSAMPLE) * SIZEOF(JSAMPLE)); + MEMCOPY(table + (4 * (MAXJSAMPLE+1) - CENTERJSAMPLE), + cinfo->sample_range_limit, CENTERJSAMPLE * SIZEOF(JSAMPLE)); +} + + +/* + * Master selection of decompression modules. + * This is done once at jpeg_start_decompress time. We determine + * which modules will be used and give them appropriate initialization calls. + * We also initialize the decompressor input side to begin consuming data. + * + * Since jpeg_read_header has finished, we know what is in the SOF + * and (first) SOS markers. We also have all the application parameter + * settings. + */ + +LOCAL void +master_selection (j_decompress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + boolean use_c_buffer; + long samplesperrow; + JDIMENSION jd_samplesperrow; + + /* Initialize dimensions and other stuff */ + jpeg_calc_output_dimensions(cinfo); + prepare_range_limit_table(cinfo); + + /* Width of an output scanline must be representable as JDIMENSION. */ + samplesperrow = (long) cinfo->output_width * (long) cinfo->out_color_components; + jd_samplesperrow = (JDIMENSION) samplesperrow; + if ((long) jd_samplesperrow != samplesperrow) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + + /* Initialize my private state */ + master->pass_number = 0; + master->using_merged_upsample = use_merged_upsample(cinfo); + + /* Color quantizer selection */ + master->quantizer_1pass = NULL; + master->quantizer_2pass = NULL; + /* No mode changes if not using buffered-image mode. */ + if (! cinfo->quantize_colors || ! cinfo->buffered_image) { + cinfo->enable_1pass_quant = FALSE; + cinfo->enable_external_quant = FALSE; + cinfo->enable_2pass_quant = FALSE; + } + if (cinfo->quantize_colors) { + if (cinfo->raw_data_out) + ERREXIT(cinfo, JERR_NOTIMPL); + /* 2-pass quantizer only works in 3-component color space. */ + if (cinfo->out_color_components != 3) { + cinfo->enable_1pass_quant = TRUE; + cinfo->enable_external_quant = FALSE; + cinfo->enable_2pass_quant = FALSE; + cinfo->colormap = NULL; + } else if (cinfo->colormap != NULL) { + cinfo->enable_external_quant = TRUE; + } else if (cinfo->two_pass_quantize) { + cinfo->enable_2pass_quant = TRUE; + } else { + cinfo->enable_1pass_quant = TRUE; + } + + if (cinfo->enable_1pass_quant) { +#ifdef QUANT_1PASS_SUPPORTED + jinit_1pass_quantizer(cinfo); + master->quantizer_1pass = cinfo->cquantize; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } + + /* We use the 2-pass code to map to external colormaps. */ + if (cinfo->enable_2pass_quant || cinfo->enable_external_quant) { +#ifdef QUANT_2PASS_SUPPORTED + jinit_2pass_quantizer(cinfo); + master->quantizer_2pass = cinfo->cquantize; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } + /* If both quantizers are initialized, the 2-pass one is left active; + * this is necessary for starting with quantization to an external map. + */ + } + + /* Post-processing: in particular, color conversion first */ + if (! cinfo->raw_data_out) { + if (master->using_merged_upsample) { +#ifdef UPSAMPLE_MERGING_SUPPORTED + jinit_merged_upsampler(cinfo); /* does color conversion too */ +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + jinit_color_deconverter(cinfo); + jinit_upsampler(cinfo); + } + jinit_d_post_controller(cinfo, cinfo->enable_2pass_quant); + } + /* Inverse DCT */ + jinit_inverse_dct(cinfo); + /* Entropy decoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef D_PROGRESSIVE_SUPPORTED + jinit_phuff_decoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_decoder(cinfo); + } + + /* Initialize principal buffer controllers. */ + use_c_buffer = cinfo->inputctl->has_multiple_scans || cinfo->buffered_image; + jinit_d_coef_controller(cinfo, use_c_buffer); + + if (! cinfo->raw_data_out) + jinit_d_main_controller(cinfo, FALSE /* never need full buffer here */); + + /* We can now tell the memory manager to allocate virtual arrays. */ + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + /* Initialize input side of decompressor to consume first scan. */ + (*cinfo->inputctl->start_input_pass) (cinfo); + +#ifdef D_MULTISCAN_FILES_SUPPORTED + /* If jpeg_start_decompress will read the whole file, initialize + * progress monitoring appropriately. The input step is counted + * as one pass. + */ + if (cinfo->progress != NULL && ! cinfo->buffered_image && + cinfo->inputctl->has_multiple_scans) { + int nscans; + /* Estimate number of scans to set pass_limit. */ + if (cinfo->progressive_mode) { + /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ + nscans = 2 + 3 * cinfo->num_components; + } else { + /* For a nonprogressive multiscan file, estimate 1 scan per component. */ + nscans = cinfo->num_components; + } + cinfo->progress->pass_counter = 0L; + cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows * nscans; + cinfo->progress->completed_passes = 0; + cinfo->progress->total_passes = (cinfo->enable_2pass_quant ? 3 : 2); + /* Count the input pass as done */ + master->pass_number++; + } +#endif /* D_MULTISCAN_FILES_SUPPORTED */ +} + + +/* + * Per-pass setup. + * This is called at the beginning of each output pass. We determine which + * modules will be active during this pass and give them appropriate + * start_pass calls. We also set is_dummy_pass to indicate whether this + * is a "real" output pass or a dummy pass for color quantization. + * (In the latter case, jdapi.c will crank the pass to completion.) + */ + +METHODDEF void +prepare_for_output_pass (j_decompress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + if (master->pub.is_dummy_pass) { +#ifdef QUANT_2PASS_SUPPORTED + /* Final pass of 2-pass quantization */ + master->pub.is_dummy_pass = FALSE; + (*cinfo->cquantize->start_pass) (cinfo, FALSE); + (*cinfo->post->start_pass) (cinfo, JBUF_CRANK_DEST); + (*cinfo->main->start_pass) (cinfo, JBUF_CRANK_DEST); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif /* QUANT_2PASS_SUPPORTED */ + } else { + if (cinfo->quantize_colors && cinfo->colormap == NULL) { + /* Select new quantization method */ + if (cinfo->two_pass_quantize && cinfo->enable_2pass_quant) { + cinfo->cquantize = master->quantizer_2pass; + master->pub.is_dummy_pass = TRUE; + } else if (cinfo->enable_1pass_quant) { + cinfo->cquantize = master->quantizer_1pass; + } else { + ERREXIT(cinfo, JERR_MODE_CHANGE); + } + } + (*cinfo->idct->start_pass) (cinfo); + (*cinfo->coef->start_output_pass) (cinfo); + if (! cinfo->raw_data_out) { + if (! master->using_merged_upsample) + (*cinfo->cconvert->start_pass) (cinfo); + (*cinfo->upsample->start_pass) (cinfo); + if (cinfo->quantize_colors) + (*cinfo->cquantize->start_pass) (cinfo, master->pub.is_dummy_pass); + (*cinfo->post->start_pass) (cinfo, + (master->pub.is_dummy_pass ? JBUF_SAVE_AND_PASS : JBUF_PASS_THRU)); + (*cinfo->main->start_pass) (cinfo, JBUF_PASS_THRU); + } + } + + /* Set up progress monitor's pass info if present */ + if (cinfo->progress != NULL) { + cinfo->progress->completed_passes = master->pass_number; + cinfo->progress->total_passes = master->pass_number + + (master->pub.is_dummy_pass ? 2 : 1); + /* In buffered-image mode, we assume one more output pass if EOI not + * yet reached, but no more passes if EOI has been reached. + */ + if (cinfo->buffered_image && ! cinfo->inputctl->eoi_reached) { + cinfo->progress->total_passes += (cinfo->enable_2pass_quant ? 2 : 1); + } + } +} + + +/* + * Finish up at end of an output pass. + */ + +METHODDEF void +finish_output_pass (j_decompress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + if (cinfo->quantize_colors) + (*cinfo->cquantize->finish_pass) (cinfo); + master->pass_number++; +} + + +#ifdef D_MULTISCAN_FILES_SUPPORTED + +/* + * Switch to a new external colormap between output passes. + */ + +GLOBAL void +jpeg_new_colormap (j_decompress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + /* Prevent application from calling me at wrong times */ + if (cinfo->global_state != DSTATE_BUFIMAGE) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + if (cinfo->quantize_colors && cinfo->enable_external_quant && + cinfo->colormap != NULL) { + /* Select 2-pass quantizer for external colormap use */ + cinfo->cquantize = master->quantizer_2pass; + /* Notify quantizer of colormap change */ + (*cinfo->cquantize->new_color_map) (cinfo); + master->pub.is_dummy_pass = FALSE; /* just in case */ + } else + ERREXIT(cinfo, JERR_MODE_CHANGE); +} + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + + +/* + * Initialize master decompression control and select active modules. + * This is performed at the start of jpeg_start_decompress. + */ + +GLOBAL void +jinit_master_decompress (j_decompress_ptr cinfo) +{ + my_master_ptr master; + + master = (my_master_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_decomp_master)); + cinfo->master = (struct jpeg_decomp_master *) master; + master->pub.prepare_for_output_pass = prepare_for_output_pass; + master->pub.finish_output_pass = finish_output_pass; + + master->pub.is_dummy_pass = FALSE; + + master_selection(cinfo); +} diff --git a/utils/Radiant/jpeg-6/jdpostct.c b/utils/Radiant/jpeg-6/jdpostct.c new file mode 100644 index 0000000..e3283b0 --- /dev/null +++ b/utils/Radiant/jpeg-6/jdpostct.c @@ -0,0 +1,290 @@ +/* + * jdpostct.c + * + * Copyright (C) 1994-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the decompression postprocessing controller. + * This controller manages the upsampling, color conversion, and color + * quantization/reduction steps; specifically, it controls the buffering + * between upsample/color conversion and color quantization/reduction. + * + * If no color quantization/reduction is required, then this module has no + * work to do, and it just hands off to the upsample/color conversion code. + * An integrated upsample/convert/quantize process would replace this module + * entirely. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_d_post_controller pub; /* public fields */ + + /* Color quantization source buffer: this holds output data from + * the upsample/color conversion step to be passed to the quantizer. + * For two-pass color quantization, we need a full-image buffer; + * for one-pass operation, a strip buffer is sufficient. + */ + jvirt_sarray_ptr whole_image; /* virtual array, or NULL if one-pass */ + JSAMPARRAY buffer; /* strip buffer, or current strip of virtual */ + JDIMENSION strip_height; /* buffer size in rows */ + /* for two-pass mode only: */ + JDIMENSION starting_row; /* row # of first row in current strip */ + JDIMENSION next_row; /* index of next row to fill/empty in strip */ +} my_post_controller; + +typedef my_post_controller * my_post_ptr; + + +/* Forward declarations */ +METHODDEF void post_process_1pass + JPP((j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +#ifdef QUANT_2PASS_SUPPORTED +METHODDEF void post_process_prepass + JPP((j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +METHODDEF void post_process_2pass + JPP((j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +#endif + + +/* + * Initialize for a processing pass. + */ + +METHODDEF void +start_pass_dpost (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_post_ptr post = (my_post_ptr) cinfo->post; + + switch (pass_mode) { + case JBUF_PASS_THRU: + if (cinfo->quantize_colors) { + /* Single-pass processing with color quantization. */ + post->pub.post_process_data = post_process_1pass; + /* We could be doing buffered-image output before starting a 2-pass + * color quantization; in that case, jinit_d_post_controller did not + * allocate a strip buffer. Use the virtual-array buffer as workspace. + */ + if (post->buffer == NULL) { + post->buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, post->whole_image, + (JDIMENSION) 0, post->strip_height, TRUE); + } + } else { + /* For single-pass processing without color quantization, + * I have no work to do; just call the upsampler directly. + */ + post->pub.post_process_data = cinfo->upsample->upsample; + } + break; +#ifdef QUANT_2PASS_SUPPORTED + case JBUF_SAVE_AND_PASS: + /* First pass of 2-pass quantization */ + if (post->whole_image == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + post->pub.post_process_data = post_process_prepass; + break; + case JBUF_CRANK_DEST: + /* Second pass of 2-pass quantization */ + if (post->whole_image == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + post->pub.post_process_data = post_process_2pass; + break; +#endif /* QUANT_2PASS_SUPPORTED */ + default: + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + break; + } + post->starting_row = post->next_row = 0; +} + + +/* + * Process some data in the one-pass (strip buffer) case. + * This is used for color precision reduction as well as one-pass quantization. + */ + +METHODDEF void +post_process_1pass (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_post_ptr post = (my_post_ptr) cinfo->post; + JDIMENSION num_rows, max_rows; + + /* Fill the buffer, but not more than what we can dump out in one go. */ + /* Note we rely on the upsampler to detect bottom of image. */ + max_rows = out_rows_avail - *out_row_ctr; + if (max_rows > post->strip_height) + max_rows = post->strip_height; + num_rows = 0; + (*cinfo->upsample->upsample) (cinfo, + input_buf, in_row_group_ctr, in_row_groups_avail, + post->buffer, &num_rows, max_rows); + /* Quantize and emit data. */ + (*cinfo->cquantize->color_quantize) (cinfo, + post->buffer, output_buf + *out_row_ctr, (int) num_rows); + *out_row_ctr += num_rows; +} + + +#ifdef QUANT_2PASS_SUPPORTED + +/* + * Process some data in the first pass of 2-pass quantization. + */ + +METHODDEF void +post_process_prepass (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_post_ptr post = (my_post_ptr) cinfo->post; + JDIMENSION old_next_row, num_rows; + + /* Reposition virtual buffer if at start of strip. */ + if (post->next_row == 0) { + post->buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, post->whole_image, + post->starting_row, post->strip_height, TRUE); + } + + /* Upsample some data (up to a strip height's worth). */ + old_next_row = post->next_row; + (*cinfo->upsample->upsample) (cinfo, + input_buf, in_row_group_ctr, in_row_groups_avail, + post->buffer, &post->next_row, post->strip_height); + + /* Allow quantizer to scan new data. No data is emitted, */ + /* but we advance out_row_ctr so outer loop can tell when we're done. */ + if (post->next_row > old_next_row) { + num_rows = post->next_row - old_next_row; + (*cinfo->cquantize->color_quantize) (cinfo, post->buffer + old_next_row, + (JSAMPARRAY) NULL, (int) num_rows); + *out_row_ctr += num_rows; + } + + /* Advance if we filled the strip. */ + if (post->next_row >= post->strip_height) { + post->starting_row += post->strip_height; + post->next_row = 0; + } +} + + +/* + * Process some data in the second pass of 2-pass quantization. + */ + +METHODDEF void +post_process_2pass (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_post_ptr post = (my_post_ptr) cinfo->post; + JDIMENSION num_rows, max_rows; + + /* Reposition virtual buffer if at start of strip. */ + if (post->next_row == 0) { + post->buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, post->whole_image, + post->starting_row, post->strip_height, FALSE); + } + + /* Determine number of rows to emit. */ + num_rows = post->strip_height - post->next_row; /* available in strip */ + max_rows = out_rows_avail - *out_row_ctr; /* available in output area */ + if (num_rows > max_rows) + num_rows = max_rows; + /* We have to check bottom of image here, can't depend on upsampler. */ + max_rows = cinfo->output_height - post->starting_row; + if (num_rows > max_rows) + num_rows = max_rows; + + /* Quantize and emit data. */ + (*cinfo->cquantize->color_quantize) (cinfo, + post->buffer + post->next_row, output_buf + *out_row_ctr, + (int) num_rows); + *out_row_ctr += num_rows; + + /* Advance if we filled the strip. */ + post->next_row += num_rows; + if (post->next_row >= post->strip_height) { + post->starting_row += post->strip_height; + post->next_row = 0; + } +} + +#endif /* QUANT_2PASS_SUPPORTED */ + + +/* + * Initialize postprocessing controller. + */ + +GLOBAL void +jinit_d_post_controller (j_decompress_ptr cinfo, boolean need_full_buffer) +{ + my_post_ptr post; + + post = (my_post_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_post_controller)); + cinfo->post = (struct jpeg_d_post_controller *) post; + post->pub.start_pass = start_pass_dpost; + post->whole_image = NULL; /* flag for no virtual arrays */ + post->buffer = NULL; /* flag for no strip buffer */ + + /* Create the quantization buffer, if needed */ + if (cinfo->quantize_colors) { + /* The buffer strip height is max_v_samp_factor, which is typically + * an efficient number of rows for upsampling to return. + * (In the presence of output rescaling, we might want to be smarter?) + */ + post->strip_height = (JDIMENSION) cinfo->max_v_samp_factor; + if (need_full_buffer) { + /* Two-pass color quantization: need full-image storage. */ + /* We round up the number of rows to a multiple of the strip height. */ +#ifdef QUANT_2PASS_SUPPORTED + post->whole_image = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + cinfo->output_width * cinfo->out_color_components, + (JDIMENSION) jround_up((long) cinfo->output_height, + (long) post->strip_height), + post->strip_height); +#else + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); +#endif /* QUANT_2PASS_SUPPORTED */ + } else { + /* One-pass color quantization: just make a strip buffer. */ + post->buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->output_width * cinfo->out_color_components, + post->strip_height); + } + } +} diff --git a/utils/Radiant/jpeg-6/jdsample.c b/utils/Radiant/jpeg-6/jdsample.c new file mode 100644 index 0000000..bc171f4 --- /dev/null +++ b/utils/Radiant/jpeg-6/jdsample.c @@ -0,0 +1,478 @@ +/* + * jdsample.c + * + * Copyright (C) 1991-1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains upsampling routines. + * + * Upsampling input data is counted in "row groups". A row group + * is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) + * sample rows of each component. Upsampling will normally produce + * max_v_samp_factor pixel rows from each row group (but this could vary + * if the upsampler is applying a scale factor of its own). + * + * An excellent reference for image resampling is + * Digital Image Warping, George Wolberg, 1990. + * Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Pointer to routine to upsample a single component */ +typedef JMETHOD(void, upsample1_ptr, + (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr)); + +/* Private subobject */ + +typedef struct { + struct jpeg_upsampler pub; /* public fields */ + + /* Color conversion buffer. When using separate upsampling and color + * conversion steps, this buffer holds one upsampled row group until it + * has been color converted and output. + * Note: we do not allocate any storage for component(s) which are full-size, + * ie do not need rescaling. The corresponding entry of color_buf[] is + * simply set to point to the input data array, thereby avoiding copying. + */ + JSAMPARRAY color_buf[MAX_COMPONENTS]; + + /* Per-component upsampling method pointers */ + upsample1_ptr methods[MAX_COMPONENTS]; + + int next_row_out; /* counts rows emitted from color_buf */ + JDIMENSION rows_to_go; /* counts rows remaining in image */ + + /* Height of an input row group for each component. */ + int rowgroup_height[MAX_COMPONENTS]; + + /* These arrays save pixel expansion factors so that int_expand need not + * recompute them each time. They are unused for other upsampling methods. + */ + UINT8 h_expand[MAX_COMPONENTS]; + UINT8 v_expand[MAX_COMPONENTS]; +} my_upsampler; + +typedef my_upsampler * my_upsample_ptr; + + +/* + * Initialize for an upsampling pass. + */ + +METHODDEF void +start_pass_upsample (j_decompress_ptr cinfo) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + /* Mark the conversion buffer empty */ + upsample->next_row_out = cinfo->max_v_samp_factor; + /* Initialize total-height counter for detecting bottom of image */ + upsample->rows_to_go = cinfo->output_height; +} + + +/* + * Control routine to do upsampling (and color conversion). + * + * In this version we upsample each component independently. + * We upsample one row group into the conversion buffer, then apply + * color conversion a row at a time. + */ + +METHODDEF void +sep_upsample (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + int ci; + jpeg_component_info * compptr; + JDIMENSION num_rows; + + /* Fill the conversion buffer, if it's empty */ + if (upsample->next_row_out >= cinfo->max_v_samp_factor) { + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Invoke per-component upsample method. Notice we pass a POINTER + * to color_buf[ci], so that fullsize_upsample can change it. + */ + (*upsample->methods[ci]) (cinfo, compptr, + input_buf[ci] + (*in_row_group_ctr * upsample->rowgroup_height[ci]), + upsample->color_buf + ci); + } + upsample->next_row_out = 0; + } + + /* Color-convert and emit rows */ + + /* How many we have in the buffer: */ + num_rows = (JDIMENSION) (cinfo->max_v_samp_factor - upsample->next_row_out); + /* Not more than the distance to the end of the image. Need this test + * in case the image height is not a multiple of max_v_samp_factor: + */ + if (num_rows > upsample->rows_to_go) + num_rows = upsample->rows_to_go; + /* And not more than what the client can accept: */ + out_rows_avail -= *out_row_ctr; + if (num_rows > out_rows_avail) + num_rows = out_rows_avail; + + (*cinfo->cconvert->color_convert) (cinfo, upsample->color_buf, + (JDIMENSION) upsample->next_row_out, + output_buf + *out_row_ctr, + (int) num_rows); + + /* Adjust counts */ + *out_row_ctr += num_rows; + upsample->rows_to_go -= num_rows; + upsample->next_row_out += num_rows; + /* When the buffer is emptied, declare this input row group consumed */ + if (upsample->next_row_out >= cinfo->max_v_samp_factor) + (*in_row_group_ctr)++; +} + + +/* + * These are the routines invoked by sep_upsample to upsample pixel values + * of a single component. One row group is processed per call. + */ + + +/* + * For full-size components, we just make color_buf[ci] point at the + * input buffer, and thus avoid copying any data. Note that this is + * safe only because sep_upsample doesn't declare the input row group + * "consumed" until we are done color converting and emitting it. + */ + +METHODDEF void +fullsize_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + *output_data_ptr = input_data; +} + + +/* + * This is a no-op version used for "uninteresting" components. + * These components will not be referenced by color conversion. + */ + +METHODDEF void +noop_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + *output_data_ptr = NULL; /* safety check */ +} + + +/* + * This version handles any integral sampling ratios. + * This is not used for typical JPEG files, so it need not be fast. + * Nor, for that matter, is it particularly accurate: the algorithm is + * simple replication of the input pixel onto the corresponding output + * pixels. The hi-falutin sampling literature refers to this as a + * "box filter". A box filter tends to introduce visible artifacts, + * so if you are actually going to use 3:1 or 4:1 sampling ratios + * you would be well advised to improve this code. + */ + +METHODDEF void +int_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr, outptr; + register JSAMPLE invalue; + register int h; + JSAMPROW outend; + int h_expand, v_expand; + int inrow, outrow; + + h_expand = upsample->h_expand[compptr->component_index]; + v_expand = upsample->v_expand[compptr->component_index]; + + inrow = outrow = 0; + while (outrow < cinfo->max_v_samp_factor) { + /* Generate one output row with proper horizontal expansion */ + inptr = input_data[inrow]; + outptr = output_data[outrow]; + outend = outptr + cinfo->output_width; + while (outptr < outend) { + invalue = *inptr++; /* don't need GETJSAMPLE() here */ + for (h = h_expand; h > 0; h--) { + *outptr++ = invalue; + } + } + /* Generate any additional output rows by duplicating the first one */ + if (v_expand > 1) { + jcopy_sample_rows(output_data, outrow, output_data, outrow+1, + v_expand-1, cinfo->output_width); + } + inrow++; + outrow += v_expand; + } +} + + +/* + * Fast processing for the common case of 2:1 horizontal and 1:1 vertical. + * It's still a box filter. + */ + +METHODDEF void +h2v1_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr, outptr; + register JSAMPLE invalue; + JSAMPROW outend; + int inrow; + + for (inrow = 0; inrow < cinfo->max_v_samp_factor; inrow++) { + inptr = input_data[inrow]; + outptr = output_data[inrow]; + outend = outptr + cinfo->output_width; + while (outptr < outend) { + invalue = *inptr++; /* don't need GETJSAMPLE() here */ + *outptr++ = invalue; + *outptr++ = invalue; + } + } +} + + +/* + * Fast processing for the common case of 2:1 horizontal and 2:1 vertical. + * It's still a box filter. + */ + +METHODDEF void +h2v2_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr, outptr; + register JSAMPLE invalue; + JSAMPROW outend; + int inrow, outrow; + + inrow = outrow = 0; + while (outrow < cinfo->max_v_samp_factor) { + inptr = input_data[inrow]; + outptr = output_data[outrow]; + outend = outptr + cinfo->output_width; + while (outptr < outend) { + invalue = *inptr++; /* don't need GETJSAMPLE() here */ + *outptr++ = invalue; + *outptr++ = invalue; + } + jcopy_sample_rows(output_data, outrow, output_data, outrow+1, + 1, cinfo->output_width); + inrow++; + outrow += 2; + } +} + + +/* + * Fancy processing for the common case of 2:1 horizontal and 1:1 vertical. + * + * The upsampling algorithm is linear interpolation between pixel centers, + * also known as a "triangle filter". This is a good compromise between + * speed and visual quality. The centers of the output pixels are 1/4 and 3/4 + * of the way between input pixel centers. + * + * A note about the "bias" calculations: when rounding fractional values to + * integer, we do not want to always round 0.5 up to the next integer. + * If we did that, we'd introduce a noticeable bias towards larger values. + * Instead, this code is arranged so that 0.5 will be rounded up or down at + * alternate pixel locations (a simple ordered dither pattern). + */ + +METHODDEF void +h2v1_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr, outptr; + register int invalue; + register JDIMENSION colctr; + int inrow; + + for (inrow = 0; inrow < cinfo->max_v_samp_factor; inrow++) { + inptr = input_data[inrow]; + outptr = output_data[inrow]; + /* Special case for first column */ + invalue = GETJSAMPLE(*inptr++); + *outptr++ = (JSAMPLE) invalue; + *outptr++ = (JSAMPLE) ((invalue * 3 + GETJSAMPLE(*inptr) + 2) >> 2); + + for (colctr = compptr->downsampled_width - 2; colctr > 0; colctr--) { + /* General case: 3/4 * nearer pixel + 1/4 * further pixel */ + invalue = GETJSAMPLE(*inptr++) * 3; + *outptr++ = (JSAMPLE) ((invalue + GETJSAMPLE(inptr[-2]) + 1) >> 2); + *outptr++ = (JSAMPLE) ((invalue + GETJSAMPLE(*inptr) + 2) >> 2); + } + + /* Special case for last column */ + invalue = GETJSAMPLE(*inptr); + *outptr++ = (JSAMPLE) ((invalue * 3 + GETJSAMPLE(inptr[-1]) + 1) >> 2); + *outptr++ = (JSAMPLE) invalue; + } +} + + +/* + * Fancy processing for the common case of 2:1 horizontal and 2:1 vertical. + * Again a triangle filter; see comments for h2v1 case, above. + * + * It is OK for us to reference the adjacent input rows because we demanded + * context from the main buffer controller (see initialization code). + */ + +METHODDEF void +h2v2_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr0, inptr1, outptr; +#if BITS_IN_JSAMPLE == 8 + register int thiscolsum, lastcolsum, nextcolsum; +#else + register INT32 thiscolsum, lastcolsum, nextcolsum; +#endif + register JDIMENSION colctr; + int inrow, outrow, v; + + inrow = outrow = 0; + while (outrow < cinfo->max_v_samp_factor) { + for (v = 0; v < 2; v++) { + /* inptr0 points to nearest input row, inptr1 points to next nearest */ + inptr0 = input_data[inrow]; + if (v == 0) /* next nearest is row above */ + inptr1 = input_data[inrow-1]; + else /* next nearest is row below */ + inptr1 = input_data[inrow+1]; + outptr = output_data[outrow++]; + + /* Special case for first column */ + thiscolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++); + nextcolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++); + *outptr++ = (JSAMPLE) ((thiscolsum * 4 + 8) >> 4); + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + nextcolsum + 7) >> 4); + lastcolsum = thiscolsum; thiscolsum = nextcolsum; + + for (colctr = compptr->downsampled_width - 2; colctr > 0; colctr--) { + /* General case: 3/4 * nearer pixel + 1/4 * further pixel in each */ + /* dimension, thus 9/16, 3/16, 3/16, 1/16 overall */ + nextcolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++); + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + lastcolsum + 8) >> 4); + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + nextcolsum + 7) >> 4); + lastcolsum = thiscolsum; thiscolsum = nextcolsum; + } + + /* Special case for last column */ + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + lastcolsum + 8) >> 4); + *outptr++ = (JSAMPLE) ((thiscolsum * 4 + 7) >> 4); + } + inrow++; + } +} + + +/* + * Module initialization routine for upsampling. + */ + +GLOBAL void +jinit_upsampler (j_decompress_ptr cinfo) +{ + my_upsample_ptr upsample; + int ci; + jpeg_component_info * compptr; + boolean need_buffer, do_fancy; + int h_in_group, v_in_group, h_out_group, v_out_group; + + upsample = (my_upsample_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_upsampler)); + cinfo->upsample = (struct jpeg_upsampler *) upsample; + upsample->pub.start_pass = start_pass_upsample; + upsample->pub.upsample = sep_upsample; + upsample->pub.need_context_rows = FALSE; /* until we find out differently */ + + if (cinfo->CCIR601_sampling) /* this isn't supported */ + ERREXIT(cinfo, JERR_CCIR601_NOTIMPL); + + /* jdmainct.c doesn't support context rows when min_DCT_scaled_size = 1, + * so don't ask for it. + */ + do_fancy = cinfo->do_fancy_upsampling && cinfo->min_DCT_scaled_size > 1; + + /* Verify we can handle the sampling factors, select per-component methods, + * and create storage as needed. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Compute size of an "input group" after IDCT scaling. This many samples + * are to be converted to max_h_samp_factor * max_v_samp_factor pixels. + */ + h_in_group = (compptr->h_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; + v_in_group = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; + h_out_group = cinfo->max_h_samp_factor; + v_out_group = cinfo->max_v_samp_factor; + upsample->rowgroup_height[ci] = v_in_group; /* save for use later */ + need_buffer = TRUE; + if (! compptr->component_needed) { + /* Don't bother to upsample an uninteresting component. */ + upsample->methods[ci] = noop_upsample; + need_buffer = FALSE; + } else if (h_in_group == h_out_group && v_in_group == v_out_group) { + /* Fullsize components can be processed without any work. */ + upsample->methods[ci] = fullsize_upsample; + need_buffer = FALSE; + } else if (h_in_group * 2 == h_out_group && + v_in_group == v_out_group) { + /* Special cases for 2h1v upsampling */ + if (do_fancy && compptr->downsampled_width > 2) + upsample->methods[ci] = h2v1_fancy_upsample; + else + upsample->methods[ci] = h2v1_upsample; + } else if (h_in_group * 2 == h_out_group && + v_in_group * 2 == v_out_group) { + /* Special cases for 2h2v upsampling */ + if (do_fancy && compptr->downsampled_width > 2) { + upsample->methods[ci] = h2v2_fancy_upsample; + upsample->pub.need_context_rows = TRUE; + } else + upsample->methods[ci] = h2v2_upsample; + } else if ((h_out_group % h_in_group) == 0 && + (v_out_group % v_in_group) == 0) { + /* Generic integral-factors upsampling method */ + upsample->methods[ci] = int_upsample; + upsample->h_expand[ci] = (UINT8) (h_out_group / h_in_group); + upsample->v_expand[ci] = (UINT8) (v_out_group / v_in_group); + } else + ERREXIT(cinfo, JERR_FRACT_SAMPLE_NOTIMPL); + if (need_buffer) { + upsample->color_buf[ci] = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) jround_up((long) cinfo->output_width, + (long) cinfo->max_h_samp_factor), + (JDIMENSION) cinfo->max_v_samp_factor); + } + } +} diff --git a/utils/Radiant/jpeg-6/jdtrans.c b/utils/Radiant/jpeg-6/jdtrans.c new file mode 100644 index 0000000..eb873e0 --- /dev/null +++ b/utils/Radiant/jpeg-6/jdtrans.c @@ -0,0 +1,122 @@ +/* + * jdtrans.c + * + * Copyright (C) 1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains library routines for transcoding decompression, + * that is, reading raw DCT coefficient arrays from an input JPEG file. + * The routines in jdapimin.c will also be needed by a transcoder. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Forward declarations */ +LOCAL void transdecode_master_selection JPP((j_decompress_ptr cinfo)); + + +/* + * Read the coefficient arrays from a JPEG file. + * jpeg_read_header must be completed before calling this. + * + * The entire image is read into a set of virtual coefficient-block arrays, + * one per component. The return value is a pointer to the array of + * virtual-array descriptors. These can be manipulated directly via the + * JPEG memory manager, or handed off to jpeg_write_coefficients(). + * To release the memory occupied by the virtual arrays, call + * jpeg_finish_decompress() when done with the data. + * + * Returns NULL if suspended. This case need be checked only if + * a suspending data source is used. + */ + +GLOBAL jvirt_barray_ptr * +jpeg_read_coefficients (j_decompress_ptr cinfo) +{ + if (cinfo->global_state == DSTATE_READY) { + /* First call: initialize active modules */ + transdecode_master_selection(cinfo); + cinfo->global_state = DSTATE_RDCOEFS; + } else if (cinfo->global_state != DSTATE_RDCOEFS) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + /* Absorb whole file into the coef buffer */ + for (;;) { + int retcode; + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + /* Absorb some more input */ + retcode = (*cinfo->inputctl->consume_input) (cinfo); + if (retcode == JPEG_SUSPENDED) + return NULL; + if (retcode == JPEG_REACHED_EOI) + break; + /* Advance progress counter if appropriate */ + if (cinfo->progress != NULL && + (retcode == JPEG_ROW_COMPLETED || retcode == JPEG_REACHED_SOS)) { + if (++cinfo->progress->pass_counter >= cinfo->progress->pass_limit) { + /* startup underestimated number of scans; ratchet up one scan */ + cinfo->progress->pass_limit += (long) cinfo->total_iMCU_rows; + } + } + } + /* Set state so that jpeg_finish_decompress does the right thing */ + cinfo->global_state = DSTATE_STOPPING; + return cinfo->coef->coef_arrays; +} + + +/* + * Master selection of decompression modules for transcoding. + * This substitutes for jdmaster.c's initialization of the full decompressor. + */ + +LOCAL void +transdecode_master_selection (j_decompress_ptr cinfo) +{ + /* Entropy decoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef D_PROGRESSIVE_SUPPORTED + jinit_phuff_decoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_decoder(cinfo); + } + + /* Always get a full-image coefficient buffer. */ + jinit_d_coef_controller(cinfo, TRUE); + + /* We can now tell the memory manager to allocate virtual arrays. */ + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + /* Initialize input side of decompressor to consume first scan. */ + (*cinfo->inputctl->start_input_pass) (cinfo); + + /* Initialize progress monitoring. */ + if (cinfo->progress != NULL) { + int nscans; + /* Estimate number of scans to set pass_limit. */ + if (cinfo->progressive_mode) { + /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ + nscans = 2 + 3 * cinfo->num_components; + } else if (cinfo->inputctl->has_multiple_scans) { + /* For a nonprogressive multiscan file, estimate 1 scan per component. */ + nscans = cinfo->num_components; + } else { + nscans = 1; + } + cinfo->progress->pass_counter = 0L; + cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows * nscans; + cinfo->progress->completed_passes = 0; + cinfo->progress->total_passes = 1; + } +} diff --git a/utils/Radiant/jpeg-6/jerror.c b/utils/Radiant/jpeg-6/jerror.c new file mode 100644 index 0000000..cf866f4 --- /dev/null +++ b/utils/Radiant/jpeg-6/jerror.c @@ -0,0 +1,242 @@ +/* + * jerror.c + * + * Copyright (C) 1991-1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains simple error-reporting and trace-message routines. + * These are suitable for Unix-like systems and others where writing to + * stderr is the right thing to do. Many applications will want to replace + * some or all of these routines. + * + * These routines are used by both the compression and decompression code. + */ + +/* this is not a core library module, so it doesn't define JPEG_INTERNALS */ +#include "jinclude.h" +#include "jpeglib.h" +#include "jversion.h" +#include "jerror.h" + +// stefix: +#include "..\jpgfile.h" + +#ifndef EXIT_FAILURE /* define exit() codes if not provided */ +#define EXIT_FAILURE 1 +#endif + + +/* + * Create the message string table. + * We do this from the master message list in jerror.h by re-reading + * jerror.h with a suitable definition for macro JMESSAGE. + * The message table is made an external symbol just in case any applications + * want to refer to it directly. + */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_std_message_table jMsgTable +#endif + +#define JMESSAGE(code,string) string , + +const char * const jpeg_std_message_table[] = { +#include "jerror.h" + NULL +}; + + +/* + * Error exit handler: must not return to caller. + * + * Applications may override this if they want to get control back after + * an error. Typically one would longjmp somewhere instead of exiting. + * The setjmp buffer can be made a private field within an expanded error + * handler object. Note that the info needed to generate an error message + * is stored in the error object, so you can generate the message now or + * later, at your convenience. + * You should make sure that the JPEG object is cleaned up (with jpeg_abort + * or jpeg_destroy) at some point. + */ + +METHODDEF void +error_exit (j_common_ptr cinfo) +{ + char buffer[JMSG_LENGTH_MAX]; + + /* Create the message */ + (*cinfo->err->format_message) (cinfo, buffer); + + /* Let the memory manager delete any temp files before we die */ + jpeg_destroy(cinfo); + +// stefix +#if 0 + ri.Error( ERR_FATAL, "%s\n", buffer ); +#else + ERROR_STRING_NO_RETURN(buffer); +#endif +} + +/* + * Actual output of an error or trace message. + * Applications may override this method to send JPEG messages somewhere + * other than stderr. + */ + +METHODDEF void +output_message (j_common_ptr cinfo) +{ + char buffer[JMSG_LENGTH_MAX]; + + /* Create the message */ + (*cinfo->err->format_message) (cinfo, buffer); + +// stefix +#if 0 + /* Send it to stderr, adding a newline */ + ri.Printf(PRINT_ALL, "%s\n", buffer); +#else + MESSAGE_STRING(buffer); +#endif +} + + +/* + * Decide whether to emit a trace or warning message. + * msg_level is one of: + * -1: recoverable corrupt-data warning, may want to abort. + * 0: important advisory messages (always display to user). + * 1: first level of tracing detail. + * 2,3,...: successively more detailed tracing messages. + * An application might override this method if it wanted to abort on warnings + * or change the policy about which messages to display. + */ + +METHODDEF void +emit_message (j_common_ptr cinfo, int msg_level) +{ + struct jpeg_error_mgr * err = cinfo->err; + + if (msg_level < 0) { + /* It's a warning message. Since corrupt files may generate many warnings, + * the policy implemented here is to show only the first warning, + * unless trace_level >= 3. + */ + if (err->num_warnings == 0 || err->trace_level >= 3) + (*err->output_message) (cinfo); + /* Always count warnings in num_warnings. */ + err->num_warnings++; + } else { + /* It's a trace message. Show it if trace_level >= msg_level. */ + if (err->trace_level >= msg_level) + (*err->output_message) (cinfo); + } +} + + +/* + * Format a message string for the most recent JPEG error or message. + * The message is stored into buffer, which should be at least JMSG_LENGTH_MAX + * characters. Note that no '\n' character is added to the string. + * Few applications should need to override this method. + */ + +METHODDEF void +format_message (j_common_ptr cinfo, char * buffer) +{ + struct jpeg_error_mgr * err = cinfo->err; + int msg_code = err->msg_code; + const char * msgtext = NULL; + const char * msgptr; + char ch; + boolean isstring; + + /* Look up message string in proper table */ + if (msg_code > 0 && msg_code <= err->last_jpeg_message) { + msgtext = err->jpeg_message_table[msg_code]; + } else if (err->addon_message_table != NULL && + msg_code >= err->first_addon_message && + msg_code <= err->last_addon_message) { + msgtext = err->addon_message_table[msg_code - err->first_addon_message]; + } + + /* Defend against bogus message number */ + if (msgtext == NULL) { + err->msg_parm.i[0] = msg_code; + msgtext = err->jpeg_message_table[0]; + } + + /* Check for string parameter, as indicated by %s in the message text */ + isstring = FALSE; + msgptr = msgtext; + while ((ch = *msgptr++) != '\0') { + if (ch == '%') { + if (*msgptr == 's') isstring = TRUE; + break; + } + } + + /* Format the message into the passed buffer */ + if (isstring) + sprintf(buffer, msgtext, err->msg_parm.s); + else + sprintf(buffer, msgtext, + err->msg_parm.i[0], err->msg_parm.i[1], + err->msg_parm.i[2], err->msg_parm.i[3], + err->msg_parm.i[4], err->msg_parm.i[5], + err->msg_parm.i[6], err->msg_parm.i[7]); +} + + +/* + * Reset error state variables at start of a new image. + * This is called during compression startup to reset trace/error + * processing to default state, without losing any application-specific + * method pointers. An application might possibly want to override + * this method if it has additional error processing state. + */ + +METHODDEF void +reset_error_mgr (j_common_ptr cinfo) +{ + cinfo->err->num_warnings = 0; + /* trace_level is not reset since it is an application-supplied parameter */ + cinfo->err->msg_code = 0; /* may be useful as a flag for "no error" */ +} + + +/* + * Fill in the standard error-handling methods in a jpeg_error_mgr object. + * Typical call is: + * struct jpeg_compress_struct cinfo; + * struct jpeg_error_mgr err; + * + * cinfo.err = jpeg_std_error(&err); + * after which the application may override some of the methods. + */ + +GLOBAL struct jpeg_error_mgr * +jpeg_std_error (struct jpeg_error_mgr * err) +{ + err->error_exit = error_exit; + err->emit_message = emit_message; + err->output_message = output_message; + err->format_message = format_message; + err->reset_error_mgr = reset_error_mgr; + + err->trace_level = 0; /* default = no tracing */ + err->num_warnings = 0; /* no warnings emitted yet */ + err->msg_code = 0; /* may be useful as a flag for "no error" */ + + /* Initialize message table pointers */ + err->jpeg_message_table = jpeg_std_message_table; + err->last_jpeg_message = (int) JMSG_LASTMSGCODE - 1; + + err->addon_message_table = NULL; + err->first_addon_message = 0; /* for safety */ + err->last_addon_message = 0; + + return err; +} diff --git a/utils/Radiant/jpeg-6/jerror.h b/utils/Radiant/jpeg-6/jerror.h new file mode 100644 index 0000000..0ffb8b4 --- /dev/null +++ b/utils/Radiant/jpeg-6/jerror.h @@ -0,0 +1,273 @@ +/* + * jerror.h + * + * Copyright (C) 1994-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the error and message codes for the JPEG library. + * Edit this file to add new codes, or to translate the message strings to + * some other language. + * A set of error-reporting macros are defined too. Some applications using + * the JPEG library may wish to include this file to get the error codes + * and/or the macros. + */ + +/* + * To define the enum list of message codes, include this file without + * defining macro JMESSAGE. To create a message string table, include it + * again with a suitable JMESSAGE definition (see jerror.c for an example). + */ +#ifndef JMESSAGE +#ifndef JERROR_H +/* First time through, define the enum list */ +#define JMAKE_ENUM_LIST +#else +/* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */ +#define JMESSAGE(code,string) +#endif /* JERROR_H */ +#endif /* JMESSAGE */ + +#ifdef JMAKE_ENUM_LIST + +typedef enum { + +#define JMESSAGE(code,string) code , + +#endif /* JMAKE_ENUM_LIST */ + +JMESSAGE(JMSG_NOMESSAGE, "Bogus message code %d") /* Must be first entry! */ + +/* For maintenance convenience, list is alphabetical by message code name */ +JMESSAGE(JERR_ARITH_NOTIMPL, + "Sorry, there are legal restrictions on arithmetic coding") +JMESSAGE(JERR_BAD_ALIGN_TYPE, "ALIGN_TYPE is wrong, please fix") +JMESSAGE(JERR_BAD_ALLOC_CHUNK, "MAX_ALLOC_CHUNK is wrong, please fix") +JMESSAGE(JERR_BAD_BUFFER_MODE, "Bogus buffer control mode") +JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS") +JMESSAGE(JERR_BAD_DCTSIZE, "IDCT output block size %d not supported") +JMESSAGE(JERR_BAD_IN_COLORSPACE, "Bogus input colorspace") +JMESSAGE(JERR_BAD_J_COLORSPACE, "Bogus JPEG colorspace") +JMESSAGE(JERR_BAD_LENGTH, "Bogus marker length") +JMESSAGE(JERR_BAD_MCU_SIZE, "Sampling factors too large for interleaved scan") +JMESSAGE(JERR_BAD_POOL_ID, "Invalid memory pool code %d") +JMESSAGE(JERR_BAD_PRECISION, "Unsupported JPEG data precision %d") +JMESSAGE(JERR_BAD_PROGRESSION, + "Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d") +JMESSAGE(JERR_BAD_PROG_SCRIPT, + "Invalid progressive parameters at scan script entry %d") +JMESSAGE(JERR_BAD_SAMPLING, "Bogus sampling factors") +JMESSAGE(JERR_BAD_SCAN_SCRIPT, "Invalid scan script at entry %d") +JMESSAGE(JERR_BAD_STATE, "Improper call to JPEG library in state %d") +JMESSAGE(JERR_BAD_VIRTUAL_ACCESS, "Bogus virtual array access") +JMESSAGE(JERR_BUFFER_SIZE, "Buffer passed to JPEG library is too small") +JMESSAGE(JERR_CANT_SUSPEND, "Suspension not allowed here") +JMESSAGE(JERR_CCIR601_NOTIMPL, "CCIR601 sampling not implemented yet") +JMESSAGE(JERR_COMPONENT_COUNT, "Too many color components: %d, max %d") +JMESSAGE(JERR_CONVERSION_NOTIMPL, "Unsupported color conversion request") +JMESSAGE(JERR_DAC_INDEX, "Bogus DAC index %d") +JMESSAGE(JERR_DAC_VALUE, "Bogus DAC value 0x%x") +JMESSAGE(JERR_DHT_COUNTS, "Bogus DHT counts") +JMESSAGE(JERR_DHT_INDEX, "Bogus DHT index %d") +JMESSAGE(JERR_DQT_INDEX, "Bogus DQT index %d") +JMESSAGE(JERR_EMPTY_IMAGE, "Empty JPEG image (DNL not supported)") +JMESSAGE(JERR_EMS_READ, "Read from EMS failed") +JMESSAGE(JERR_EMS_WRITE, "Write to EMS failed") +JMESSAGE(JERR_EOI_EXPECTED, "Didn't expect more than one scan") +JMESSAGE(JERR_FILE_READ, "Input file read error") +JMESSAGE(JERR_FILE_WRITE, "Output file write error --- out of disk space?") +JMESSAGE(JERR_FRACT_SAMPLE_NOTIMPL, "Fractional sampling not implemented yet") +JMESSAGE(JERR_HUFF_CLEN_OVERFLOW, "Huffman code size table overflow") +JMESSAGE(JERR_HUFF_MISSING_CODE, "Missing Huffman code table entry") +JMESSAGE(JERR_IMAGE_TOO_BIG, "Maximum supported image dimension is %u pixels") +JMESSAGE(JERR_INPUT_EMPTY, "Empty input file") +JMESSAGE(JERR_INPUT_EOF, "Premature end of input file") +JMESSAGE(JERR_MISMATCHED_QUANT_TABLE, + "Cannot transcode due to multiple use of quantization table %d") +JMESSAGE(JERR_MISSING_DATA, "Scan script does not transmit all data") +JMESSAGE(JERR_MODE_CHANGE, "Invalid color quantization mode change") +JMESSAGE(JERR_NOTIMPL, "Not implemented yet") +JMESSAGE(JERR_NOT_COMPILED, "Requested feature was omitted at compile time") +JMESSAGE(JERR_NO_BACKING_STORE, "Backing store not supported") +JMESSAGE(JERR_NO_HUFF_TABLE, "Huffman table 0x%02x was not defined") +JMESSAGE(JERR_NO_IMAGE, "JPEG datastream contains no image") +JMESSAGE(JERR_NO_QUANT_TABLE, "Quantization table 0x%02x was not defined") +JMESSAGE(JERR_NO_SOI, "Not a JPEG file: starts with 0x%02x 0x%02x") +JMESSAGE(JERR_OUT_OF_MEMORY, "Insufficient memory (case %d)") +JMESSAGE(JERR_QUANT_COMPONENTS, + "Cannot quantize more than %d color components") +JMESSAGE(JERR_QUANT_FEW_COLORS, "Cannot quantize to fewer than %d colors") +JMESSAGE(JERR_QUANT_MANY_COLORS, "Cannot quantize to more than %d colors") +JMESSAGE(JERR_SOF_DUPLICATE, "Invalid JPEG file structure: two SOF markers") +JMESSAGE(JERR_SOF_NO_SOS, "Invalid JPEG file structure: missing SOS marker") +JMESSAGE(JERR_SOF_UNSUPPORTED, "Unsupported JPEG process: SOF type 0x%02x") +JMESSAGE(JERR_SOI_DUPLICATE, "Invalid JPEG file structure: two SOI markers") +JMESSAGE(JERR_SOS_NO_SOF, "Invalid JPEG file structure: SOS before SOF") +JMESSAGE(JERR_TFILE_CREATE, "Failed to create temporary file %s") +JMESSAGE(JERR_TFILE_READ, "Read failed on temporary file") +JMESSAGE(JERR_TFILE_SEEK, "Seek failed on temporary file") +JMESSAGE(JERR_TFILE_WRITE, + "Write failed on temporary file --- out of disk space?") +JMESSAGE(JERR_TOO_LITTLE_DATA, "Application transferred too few scanlines") +JMESSAGE(JERR_UNKNOWN_MARKER, "Unsupported marker type 0x%02x") +JMESSAGE(JERR_VIRTUAL_BUG, "Virtual array controller messed up") +JMESSAGE(JERR_WIDTH_OVERFLOW, "Image too wide for this implementation") +JMESSAGE(JERR_XMS_READ, "Read from XMS failed") +JMESSAGE(JERR_XMS_WRITE, "Write to XMS failed") +JMESSAGE(JMSG_COPYRIGHT, JCOPYRIGHT) +JMESSAGE(JMSG_VERSION, JVERSION) +JMESSAGE(JTRC_16BIT_TABLES, + "Caution: quantization tables are too coarse for baseline JPEG") +JMESSAGE(JTRC_ADOBE, + "Adobe APP14 marker: version %d, flags 0x%04x 0x%04x, transform %d") +JMESSAGE(JTRC_APP0, "Unknown APP0 marker (not JFIF), length %u") +JMESSAGE(JTRC_APP14, "Unknown APP14 marker (not Adobe), length %u") +JMESSAGE(JTRC_DAC, "Define Arithmetic Table 0x%02x: 0x%02x") +JMESSAGE(JTRC_DHT, "Define Huffman Table 0x%02x") +JMESSAGE(JTRC_DQT, "Define Quantization Table %d precision %d") +JMESSAGE(JTRC_DRI, "Define Restart Interval %u") +JMESSAGE(JTRC_EMS_CLOSE, "Freed EMS handle %u") +JMESSAGE(JTRC_EMS_OPEN, "Obtained EMS handle %u") +JMESSAGE(JTRC_EOI, "End Of Image") +JMESSAGE(JTRC_HUFFBITS, " %3d %3d %3d %3d %3d %3d %3d %3d") +JMESSAGE(JTRC_JFIF, "JFIF APP0 marker, density %dx%d %d") +JMESSAGE(JTRC_JFIF_BADTHUMBNAILSIZE, + "Warning: thumbnail image size does not match data length %u") +JMESSAGE(JTRC_JFIF_MINOR, "Unknown JFIF minor revision number %d.%02d") +JMESSAGE(JTRC_JFIF_THUMBNAIL, " with %d x %d thumbnail image") +JMESSAGE(JTRC_MISC_MARKER, "Skipping marker 0x%02x, length %u") +JMESSAGE(JTRC_PARMLESS_MARKER, "Unexpected marker 0x%02x") +JMESSAGE(JTRC_QUANTVALS, " %4u %4u %4u %4u %4u %4u %4u %4u") +JMESSAGE(JTRC_QUANT_3_NCOLORS, "Quantizing to %d = %d*%d*%d colors") +JMESSAGE(JTRC_QUANT_NCOLORS, "Quantizing to %d colors") +JMESSAGE(JTRC_QUANT_SELECTED, "Selected %d colors for quantization") +JMESSAGE(JTRC_RECOVERY_ACTION, "At marker 0x%02x, recovery action %d") +JMESSAGE(JTRC_RST, "RST%d") +JMESSAGE(JTRC_SMOOTH_NOTIMPL, + "Smoothing not supported with nonstandard sampling ratios") +JMESSAGE(JTRC_SOF, "Start Of Frame 0x%02x: width=%u, height=%u, components=%d") +JMESSAGE(JTRC_SOF_COMPONENT, " Component %d: %dhx%dv q=%d") +JMESSAGE(JTRC_SOI, "Start of Image") +JMESSAGE(JTRC_SOS, "Start Of Scan: %d components") +JMESSAGE(JTRC_SOS_COMPONENT, " Component %d: dc=%d ac=%d") +JMESSAGE(JTRC_SOS_PARAMS, " Ss=%d, Se=%d, Ah=%d, Al=%d") +JMESSAGE(JTRC_TFILE_CLOSE, "Closed temporary file %s") +JMESSAGE(JTRC_TFILE_OPEN, "Opened temporary file %s") +JMESSAGE(JTRC_UNKNOWN_IDS, + "Unrecognized component IDs %d %d %d, assuming YCbCr") +JMESSAGE(JTRC_XMS_CLOSE, "Freed XMS handle %u") +JMESSAGE(JTRC_XMS_OPEN, "Obtained XMS handle %u") +JMESSAGE(JWRN_ADOBE_XFORM, "Unknown Adobe color transform code %d") +JMESSAGE(JWRN_BOGUS_PROGRESSION, + "Inconsistent progression sequence for component %d coefficient %d") +JMESSAGE(JWRN_EXTRANEOUS_DATA, + "Corrupt JPEG data: %u extraneous bytes before marker 0x%02x") +JMESSAGE(JWRN_HIT_MARKER, "Corrupt JPEG data: premature end of data segment") +JMESSAGE(JWRN_HUFF_BAD_CODE, "Corrupt JPEG data: bad Huffman code") +JMESSAGE(JWRN_JFIF_MAJOR, "Warning: unknown JFIF revision number %d.%02d") +JMESSAGE(JWRN_JPEG_EOF, "Premature end of JPEG file") +JMESSAGE(JWRN_MUST_RESYNC, + "Corrupt JPEG data: found marker 0x%02x instead of RST%d") +JMESSAGE(JWRN_NOT_SEQUENTIAL, "Invalid SOS parameters for sequential JPEG") +JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") + +#ifdef JMAKE_ENUM_LIST + + JMSG_LASTMSGCODE +} J_MESSAGE_CODE; + +#undef JMAKE_ENUM_LIST +#endif /* JMAKE_ENUM_LIST */ + +/* Zap JMESSAGE macro so that future re-inclusions do nothing by default */ +#undef JMESSAGE + + +#ifndef JERROR_H +#define JERROR_H + +/* Macros to simplify using the error and trace message stuff */ +/* The first parameter is either type of cinfo pointer */ + +/* Fatal errors (print message and exit) */ +#define ERREXIT(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT3(cinfo,code,p1,p2,p3) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT4(cinfo,code,p1,p2,p3,p4) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (cinfo)->err->msg_parm.i[3] = (p4), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXITS(cinfo,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) + +#define MAKESTMT(stuff) do { stuff } while (0) + +/* Nonfatal errors (we can keep going, but the data is probably corrupt) */ +#define WARNMS(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) + +/* Informational/debugging messages */ +#define TRACEMS(cinfo,lvl,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS1(cinfo,lvl,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS2(cinfo,lvl,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS3(cinfo,lvl,code,p1,p2,p3) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS4(cinfo,lvl,code,p1,p2,p3,p4) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS8(cinfo,lvl,code,p1,p2,p3,p4,p5,p6,p7,p8) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); _mp[5] = (p6); _mp[6] = (p7); _mp[7] = (p8); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMSS(cinfo,lvl,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) + +#endif /* JERROR_H */ diff --git a/utils/Radiant/jpeg-6/jidctflt.c b/utils/Radiant/jpeg-6/jidctflt.c new file mode 100644 index 0000000..2e25c44 --- /dev/null +++ b/utils/Radiant/jpeg-6/jidctflt.c @@ -0,0 +1,241 @@ +/* + * jidctflt.c + * + * Copyright (C) 1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a floating-point implementation of the + * inverse DCT (Discrete Cosine Transform). In the IJG code, this routine + * must also perform dequantization of the input coefficients. + * + * This implementation should be more accurate than either of the integer + * IDCT implementations. However, it may not give the same results on all + * machines because of differences in roundoff behavior. Speed will depend + * on the hardware's floating point capacity. + * + * A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT + * on each row (or vice versa, but it's more convenient to emit a row at + * a time). Direct algorithms are also available, but they are much more + * complex and seem not to be any faster when reduced to code. + * + * This implementation is based on Arai, Agui, and Nakajima's algorithm for + * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in + * Japanese, but the algorithm is described in the Pennebaker & Mitchell + * JPEG textbook (see REFERENCES section in file README). The following code + * is based directly on figure 4-8 in P&M. + * While an 8-point DCT cannot be done in less than 11 multiplies, it is + * possible to arrange the computation so that many of the multiplies are + * simple scalings of the final outputs. These multiplies can then be + * folded into the multiplications or divisions by the JPEG quantization + * table entries. The AA&N method leaves only 5 multiplies and 29 adds + * to be done in the DCT itself. + * The primary disadvantage of this method is that with a fixed-point + * implementation, accuracy is lost due to imprecise representation of the + * scaled quantization values. However, that problem does not arise if + * we use floating point arithmetic. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + +#ifdef DCT_FLOAT_SUPPORTED + + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + + +/* Dequantize a coefficient by multiplying it by the multiplier-table + * entry; produce a float result. + */ + +#define DEQUANTIZE(coef,quantval) (((FAST_FLOAT) (coef)) * (quantval)) + + +/* + * Perform dequantization and inverse DCT on one block of coefficients. + */ + +GLOBAL void +jpeg_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + FAST_FLOAT tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + FAST_FLOAT tmp10, tmp11, tmp12, tmp13; + FAST_FLOAT z5, z10, z11, z12, z13; + JCOEFPTR inptr; + FLOAT_MULT_TYPE * quantptr; + FAST_FLOAT * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + FAST_FLOAT workspace[DCTSIZE2]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (FLOAT_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = DCTSIZE; ctr > 0; ctr--) { + /* Due to quantization, we will usually find that many of the input + * coefficients are zero, especially the AC terms. We can exploit this + * by short-circuiting the IDCT calculation for any column in which all + * the AC terms are zero. In that case each output is equal to the + * DC coefficient (with scale factor as needed). + * With typical images and quantization tables, half or more of the + * column DCT calculations can be simplified this way. + */ + + if ((inptr[DCTSIZE*1] | inptr[DCTSIZE*2] | inptr[DCTSIZE*3] | + inptr[DCTSIZE*4] | inptr[DCTSIZE*5] | inptr[DCTSIZE*6] | + inptr[DCTSIZE*7]) == 0) { + /* AC terms all zero */ + FAST_FLOAT dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + + wsptr[DCTSIZE*0] = dcval; + wsptr[DCTSIZE*1] = dcval; + wsptr[DCTSIZE*2] = dcval; + wsptr[DCTSIZE*3] = dcval; + wsptr[DCTSIZE*4] = dcval; + wsptr[DCTSIZE*5] = dcval; + wsptr[DCTSIZE*6] = dcval; + wsptr[DCTSIZE*7] = dcval; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + continue; + } + + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + tmp3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + tmp10 = tmp0 + tmp2; /* phase 3 */ + tmp11 = tmp0 - tmp2; + + tmp13 = tmp1 + tmp3; /* phases 5-3 */ + tmp12 = (tmp1 - tmp3) * ((FAST_FLOAT) 1.414213562) - tmp13; /* 2*c4 */ + + tmp0 = tmp10 + tmp13; /* phase 2 */ + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + tmp4 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + tmp5 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + tmp6 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + tmp7 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + z13 = tmp6 + tmp5; /* phase 6 */ + z10 = tmp6 - tmp5; + z11 = tmp4 + tmp7; + z12 = tmp4 - tmp7; + + tmp7 = z11 + z13; /* phase 5 */ + tmp11 = (z11 - z13) * ((FAST_FLOAT) 1.414213562); /* 2*c4 */ + + z5 = (z10 + z12) * ((FAST_FLOAT) 1.847759065); /* 2*c2 */ + tmp10 = ((FAST_FLOAT) 1.082392200) * z12 - z5; /* 2*(c2-c6) */ + tmp12 = ((FAST_FLOAT) -2.613125930) * z10 + z5; /* -2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; /* phase 2 */ + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + wsptr[DCTSIZE*0] = tmp0 + tmp7; + wsptr[DCTSIZE*7] = tmp0 - tmp7; + wsptr[DCTSIZE*1] = tmp1 + tmp6; + wsptr[DCTSIZE*6] = tmp1 - tmp6; + wsptr[DCTSIZE*2] = tmp2 + tmp5; + wsptr[DCTSIZE*5] = tmp2 - tmp5; + wsptr[DCTSIZE*4] = tmp3 + tmp4; + wsptr[DCTSIZE*3] = tmp3 - tmp4; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + } + + /* Pass 2: process rows from work array, store into output array. */ + /* Note that we must descale the results by a factor of 8 == 2**3. */ + + wsptr = workspace; + for (ctr = 0; ctr < DCTSIZE; ctr++) { + outptr = output_buf[ctr] + output_col; + /* Rows of zeroes can be exploited in the same way as we did with columns. + * However, the column calculation has created many nonzero AC terms, so + * the simplification applies less often (typically 5% to 10% of the time). + * And testing floats for zero is relatively expensive, so we don't bother. + */ + + /* Even part */ + + tmp10 = wsptr[0] + wsptr[4]; + tmp11 = wsptr[0] - wsptr[4]; + + tmp13 = wsptr[2] + wsptr[6]; + tmp12 = (wsptr[2] - wsptr[6]) * ((FAST_FLOAT) 1.414213562) - tmp13; + + tmp0 = tmp10 + tmp13; + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + z13 = wsptr[5] + wsptr[3]; + z10 = wsptr[5] - wsptr[3]; + z11 = wsptr[1] + wsptr[7]; + z12 = wsptr[1] - wsptr[7]; + + tmp7 = z11 + z13; + tmp11 = (z11 - z13) * ((FAST_FLOAT) 1.414213562); + + z5 = (z10 + z12) * ((FAST_FLOAT) 1.847759065); /* 2*c2 */ + tmp10 = ((FAST_FLOAT) 1.082392200) * z12 - z5; /* 2*(c2-c6) */ + tmp12 = ((FAST_FLOAT) -2.613125930) * z10 + z5; /* -2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + /* Final output stage: scale down by a factor of 8 and range-limit */ + + outptr[0] = range_limit[(int) DESCALE((INT32) (tmp0 + tmp7), 3) + & RANGE_MASK]; + outptr[7] = range_limit[(int) DESCALE((INT32) (tmp0 - tmp7), 3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) DESCALE((INT32) (tmp1 + tmp6), 3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) DESCALE((INT32) (tmp1 - tmp6), 3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) DESCALE((INT32) (tmp2 + tmp5), 3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) DESCALE((INT32) (tmp2 - tmp5), 3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) DESCALE((INT32) (tmp3 + tmp4), 3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) DESCALE((INT32) (tmp3 - tmp4), 3) + & RANGE_MASK]; + + wsptr += DCTSIZE; /* advance pointer to next row */ + } +} + +#endif /* DCT_FLOAT_SUPPORTED */ diff --git a/utils/Radiant/jpeg-6/jinclude.h b/utils/Radiant/jpeg-6/jinclude.h new file mode 100644 index 0000000..f6dac9a --- /dev/null +++ b/utils/Radiant/jpeg-6/jinclude.h @@ -0,0 +1,105 @@ +/* + * jinclude.h + * + * Copyright (C) 1991-1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file exists to provide a single place to fix any problems with + * including the wrong system include files. (Common problems are taken + * care of by the standard jconfig symbols, but on really weird systems + * you may have to edit this file.) + * + * NOTE: this file is NOT intended to be included by applications using the + * JPEG library. Most applications need only include jpeglib.h. + */ + + +#ifdef __cplusplus +extern "C" +{ +#endif + + + + +/* Include auto-config file to find out which system include files we need. */ + +#include "jconfig.h" /* auto configuration options */ +#define JCONFIG_INCLUDED /* so that jpeglib.h doesn't do it again */ + +/* + * We need the NULL macro and size_t typedef. + * On an ANSI-conforming system it is sufficient to include . + * Otherwise, we get them from or ; we may have to + * pull in as well. + * Note that the core JPEG library does not require ; + * only the default error handler and data source/destination modules do. + * But we must pull it in because of the references to FILE in jpeglib.h. + * You can remove those references if you want to compile without . + */ + +#ifdef HAVE_STDDEF_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef NEED_SYS_TYPES_H +#include +#endif + +#include + +/* + * We need memory copying and zeroing functions, plus strncpy(). + * ANSI and System V implementations declare these in . + * BSD doesn't have the mem() functions, but it does have bcopy()/bzero(). + * Some systems may declare memset and memcpy in . + * + * NOTE: we assume the size parameters to these functions are of type size_t. + * Change the casts in these macros if not! + */ + +#ifdef NEED_BSD_STRINGS + +#include +#define MEMZERO(target,size) bzero((void *)(target), (size_t)(size)) +#define MEMCOPY(dest,src,size) bcopy((const void *)(src), (void *)(dest), (size_t)(size)) + +#else /* not BSD, assume ANSI/SysV string lib */ + +#include +#define MEMZERO(target,size) memset((void *)(target), 0, (size_t)(size)) +#define MEMCOPY(dest,src,size) memcpy((void *)(dest), (const void *)(src), (size_t)(size)) + +#endif + +/* + * In ANSI C, and indeed any rational implementation, size_t is also the + * type returned by sizeof(). However, it seems there are some irrational + * implementations out there, in which sizeof() returns an int even though + * size_t is defined as long or unsigned long. To ensure consistent results + * we always use this SIZEOF() macro in place of using sizeof() directly. + */ + +#define SIZEOF(object) ((size_t) sizeof(object)) + +/* + * The modules that use fread() and fwrite() always invoke them through + * these macros. On some systems you may need to twiddle the argument casts. + * CAUTION: argument order is different from underlying functions! + */ + +#define JFREAD(file,buf,sizeofbuf) \ + ((size_t) fread((void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) +#define JFWRITE(file,buf,sizeofbuf) \ + ((size_t) fwrite((const void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) + + +#ifdef __cplusplus +}; +#endif + diff --git a/utils/Radiant/jpeg-6/jmemmgr.c b/utils/Radiant/jpeg-6/jmemmgr.c new file mode 100644 index 0000000..61045f9 --- /dev/null +++ b/utils/Radiant/jpeg-6/jmemmgr.c @@ -0,0 +1,1115 @@ +/* + * jmemmgr.c + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the JPEG system-independent memory management + * routines. This code is usable across a wide variety of machines; most + * of the system dependencies have been isolated in a separate file. + * The major functions provided here are: + * * pool-based allocation and freeing of memory; + * * policy decisions about how to divide available memory among the + * virtual arrays; + * * control logic for swapping virtual arrays between main memory and + * backing storage. + * The separate system-dependent file provides the actual backing-storage + * access code, and it contains the policy decision about how much total + * main memory to use. + * This file is system-dependent in the sense that some of its functions + * are unnecessary in some systems. For example, if there is enough virtual + * memory so that backing storage will never be used, much of the virtual + * array control logic could be removed. (Of course, if you have that much + * memory then you shouldn't care about a little bit of unused code...) + */ + +#define JPEG_INTERNALS +#define AM_MEMORY_MANAGER /* we define jvirt_Xarray_control structs */ +#include "jinclude.h" +#include "jpeglib.h" +#include "jmemsys.h" /* import the system-dependent declarations */ + +#ifndef NO_GETENV +#ifndef HAVE_STDLIB_H /* should declare getenv() */ +extern char * getenv JPP((const char * name)); +#endif +#endif + + +/* + * Some important notes: + * The allocation routines provided here must never return NULL. + * They should exit to error_exit if unsuccessful. + * + * It's not a good idea to try to merge the sarray and barray routines, + * even though they are textually almost the same, because samples are + * usually stored as bytes while coefficients are shorts or ints. Thus, + * in machines where byte pointers have a different representation from + * word pointers, the resulting machine code could not be the same. + */ + + +/* + * Many machines require storage alignment: longs must start on 4-byte + * boundaries, doubles on 8-byte boundaries, etc. On such machines, malloc() + * always returns pointers that are multiples of the worst-case alignment + * requirement, and we had better do so too. + * There isn't any really portable way to determine the worst-case alignment + * requirement. This module assumes that the alignment requirement is + * multiples of sizeof(ALIGN_TYPE). + * By default, we define ALIGN_TYPE as double. This is necessary on some + * workstations (where doubles really do need 8-byte alignment) and will work + * fine on nearly everything. If your machine has lesser alignment needs, + * you can save a few bytes by making ALIGN_TYPE smaller. + * The only place I know of where this will NOT work is certain Macintosh + * 680x0 compilers that define double as a 10-byte IEEE extended float. + * Doing 10-byte alignment is counterproductive because longwords won't be + * aligned well. Put "#define ALIGN_TYPE long" in jconfig.h if you have + * such a compiler. + */ + +#ifndef ALIGN_TYPE /* so can override from jconfig.h */ +#define ALIGN_TYPE double +#endif + + +/* + * We allocate objects from "pools", where each pool is gotten with a single + * request to jpeg_get_small() or jpeg_get_large(). There is no per-object + * overhead within a pool, except for alignment padding. Each pool has a + * header with a link to the next pool of the same class. + * Small and large pool headers are identical except that the latter's + * link pointer must be FAR on 80x86 machines. + * Notice that the "real" header fields are union'ed with a dummy ALIGN_TYPE + * field. This forces the compiler to make SIZEOF(small_pool_hdr) a multiple + * of the alignment requirement of ALIGN_TYPE. + */ + +typedef union small_pool_struct * small_pool_ptr; + +typedef union small_pool_struct { + struct { + small_pool_ptr next; /* next in list of pools */ + size_t bytes_used; /* how many bytes already used within pool */ + size_t bytes_left; /* bytes still available in this pool */ + } hdr; + ALIGN_TYPE dummy; /* included in union to ensure alignment */ +} small_pool_hdr; + +typedef union large_pool_struct FAR * large_pool_ptr; + +typedef union large_pool_struct { + struct { + large_pool_ptr next; /* next in list of pools */ + size_t bytes_used; /* how many bytes already used within pool */ + size_t bytes_left; /* bytes still available in this pool */ + } hdr; + ALIGN_TYPE dummy; /* included in union to ensure alignment */ +} large_pool_hdr; + + +/* + * Here is the full definition of a memory manager object. + */ + +typedef struct { + struct jpeg_memory_mgr pub; /* public fields */ + + /* Each pool identifier (lifetime class) names a linked list of pools. */ + small_pool_ptr small_list[JPOOL_NUMPOOLS]; + large_pool_ptr large_list[JPOOL_NUMPOOLS]; + + /* Since we only have one lifetime class of virtual arrays, only one + * linked list is necessary (for each datatype). Note that the virtual + * array control blocks being linked together are actually stored somewhere + * in the small-pool list. + */ + jvirt_sarray_ptr virt_sarray_list; + jvirt_barray_ptr virt_barray_list; + + /* This counts total space obtained from jpeg_get_small/large */ + long total_space_allocated; + + /* alloc_sarray and alloc_barray set this value for use by virtual + * array routines. + */ + JDIMENSION last_rowsperchunk; /* from most recent alloc_sarray/barray */ +} my_memory_mgr; + +typedef my_memory_mgr * my_mem_ptr; + + +/* + * The control blocks for virtual arrays. + * Note that these blocks are allocated in the "small" pool area. + * System-dependent info for the associated backing store (if any) is hidden + * inside the backing_store_info struct. + */ + +struct jvirt_sarray_control { + JSAMPARRAY mem_buffer; /* => the in-memory buffer */ + JDIMENSION rows_in_array; /* total virtual array height */ + JDIMENSION samplesperrow; /* width of array (and of memory buffer) */ + JDIMENSION maxaccess; /* max rows accessed by access_virt_sarray */ + JDIMENSION rows_in_mem; /* height of memory buffer */ + JDIMENSION rowsperchunk; /* allocation chunk size in mem_buffer */ + JDIMENSION cur_start_row; /* first logical row # in the buffer */ + JDIMENSION first_undef_row; /* row # of first uninitialized row */ + boolean pre_zero; /* pre-zero mode requested? */ + boolean dirty; /* do current buffer contents need written? */ + boolean b_s_open; /* is backing-store data valid? */ + jvirt_sarray_ptr next; /* link to next virtual sarray control block */ + backing_store_info b_s_info; /* System-dependent control info */ +}; + +struct jvirt_barray_control { + JBLOCKARRAY mem_buffer; /* => the in-memory buffer */ + JDIMENSION rows_in_array; /* total virtual array height */ + JDIMENSION blocksperrow; /* width of array (and of memory buffer) */ + JDIMENSION maxaccess; /* max rows accessed by access_virt_barray */ + JDIMENSION rows_in_mem; /* height of memory buffer */ + JDIMENSION rowsperchunk; /* allocation chunk size in mem_buffer */ + JDIMENSION cur_start_row; /* first logical row # in the buffer */ + JDIMENSION first_undef_row; /* row # of first uninitialized row */ + boolean pre_zero; /* pre-zero mode requested? */ + boolean dirty; /* do current buffer contents need written? */ + boolean b_s_open; /* is backing-store data valid? */ + jvirt_barray_ptr next; /* link to next virtual barray control block */ + backing_store_info b_s_info; /* System-dependent control info */ +}; + + +#ifdef MEM_STATS /* optional extra stuff for statistics */ + +LOCAL void +print_mem_stats (j_common_ptr cinfo, int pool_id) +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + small_pool_ptr shdr_ptr; + large_pool_ptr lhdr_ptr; + + /* Since this is only a debugging stub, we can cheat a little by using + * fprintf directly rather than going through the trace message code. + * This is helpful because message parm array can't handle longs. + */ + fprintf(stderr, "Freeing pool %d, total space = %ld\n", + pool_id, mem->total_space_allocated); + + for (lhdr_ptr = mem->large_list[pool_id]; lhdr_ptr != NULL; + lhdr_ptr = lhdr_ptr->hdr.next) { + fprintf(stderr, " Large chunk used %ld\n", + (long) lhdr_ptr->hdr.bytes_used); + } + + for (shdr_ptr = mem->small_list[pool_id]; shdr_ptr != NULL; + shdr_ptr = shdr_ptr->hdr.next) { + fprintf(stderr, " Small chunk used %ld free %ld\n", + (long) shdr_ptr->hdr.bytes_used, + (long) shdr_ptr->hdr.bytes_left); + } +} + +#endif /* MEM_STATS */ + + +LOCAL void +out_of_memory (j_common_ptr cinfo, int which) +/* Report an out-of-memory error and stop execution */ +/* If we compiled MEM_STATS support, report alloc requests before dying */ +{ +#ifdef MEM_STATS + cinfo->err->trace_level = 2; /* force self_destruct to report stats */ +#endif + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, which); +} + + +/* + * Allocation of "small" objects. + * + * For these, we use pooled storage. When a new pool must be created, + * we try to get enough space for the current request plus a "slop" factor, + * where the slop will be the amount of leftover space in the new pool. + * The speed vs. space tradeoff is largely determined by the slop values. + * A different slop value is provided for each pool class (lifetime), + * and we also distinguish the first pool of a class from later ones. + * NOTE: the values given work fairly well on both 16- and 32-bit-int + * machines, but may be too small if longs are 64 bits or more. + */ + +static const size_t first_pool_slop[JPOOL_NUMPOOLS] = +{ + 1600, /* first PERMANENT pool */ + 16000 /* first IMAGE pool */ +}; + +static const size_t extra_pool_slop[JPOOL_NUMPOOLS] = +{ + 0, /* additional PERMANENT pools */ + 5000 /* additional IMAGE pools */ +}; + +#define MIN_SLOP 50 /* greater than 0 to avoid futile looping */ + + +METHODDEF void * +alloc_small (j_common_ptr cinfo, int pool_id, size_t sizeofobject) +/* Allocate a "small" object */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + small_pool_ptr hdr_ptr, prev_hdr_ptr; + char * data_ptr; + size_t odd_bytes, min_request, slop; + + /* Check for unsatisfiable request (do now to ensure no overflow below) */ + if (sizeofobject > (size_t) (MAX_ALLOC_CHUNK-SIZEOF(small_pool_hdr))) + out_of_memory(cinfo, 1); /* request exceeds malloc's ability */ + + /* Round up the requested size to a multiple of SIZEOF(ALIGN_TYPE) */ + odd_bytes = sizeofobject % SIZEOF(ALIGN_TYPE); + if (odd_bytes > 0) + sizeofobject += SIZEOF(ALIGN_TYPE) - odd_bytes; + + /* See if space is available in any existing pool */ + if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + prev_hdr_ptr = NULL; + hdr_ptr = mem->small_list[pool_id]; + while (hdr_ptr != NULL) { + if (hdr_ptr->hdr.bytes_left >= sizeofobject) + break; /* found pool with enough space */ + prev_hdr_ptr = hdr_ptr; + hdr_ptr = hdr_ptr->hdr.next; + } + + /* Time to make a new pool? */ + if (hdr_ptr == NULL) { + /* min_request is what we need now, slop is what will be leftover */ + min_request = sizeofobject + SIZEOF(small_pool_hdr); + if (prev_hdr_ptr == NULL) /* first pool in class? */ + slop = first_pool_slop[pool_id]; + else + slop = extra_pool_slop[pool_id]; + /* Don't ask for more than MAX_ALLOC_CHUNK */ + if (slop > (size_t) (MAX_ALLOC_CHUNK-min_request)) + slop = (size_t) (MAX_ALLOC_CHUNK-min_request); + /* Try to get space, if fail reduce slop and try again */ + for (;;) { + hdr_ptr = (small_pool_ptr) jpeg_get_small(cinfo, min_request + slop); + if (hdr_ptr != NULL) + break; + slop /= 2; + if (slop < MIN_SLOP) /* give up when it gets real small */ + out_of_memory(cinfo, 2); /* jpeg_get_small failed */ + } + mem->total_space_allocated += min_request + slop; + /* Success, initialize the new pool header and add to end of list */ + hdr_ptr->hdr.next = NULL; + hdr_ptr->hdr.bytes_used = 0; + hdr_ptr->hdr.bytes_left = sizeofobject + slop; + if (prev_hdr_ptr == NULL) /* first pool in class? */ + mem->small_list[pool_id] = hdr_ptr; + else + prev_hdr_ptr->hdr.next = hdr_ptr; + } + + /* OK, allocate the object from the current pool */ + data_ptr = (char *) (hdr_ptr + 1); /* point to first data byte in pool */ + data_ptr += hdr_ptr->hdr.bytes_used; /* point to place for object */ + hdr_ptr->hdr.bytes_used += sizeofobject; + hdr_ptr->hdr.bytes_left -= sizeofobject; + + return (void *) data_ptr; +} + + +/* + * Allocation of "large" objects. + * + * The external semantics of these are the same as "small" objects, + * except that FAR pointers are used on 80x86. However the pool + * management heuristics are quite different. We assume that each + * request is large enough that it may as well be passed directly to + * jpeg_get_large; the pool management just links everything together + * so that we can free it all on demand. + * Note: the major use of "large" objects is in JSAMPARRAY and JBLOCKARRAY + * structures. The routines that create these structures (see below) + * deliberately bunch rows together to ensure a large request size. + */ + +METHODDEF void FAR * +alloc_large (j_common_ptr cinfo, int pool_id, size_t sizeofobject) +/* Allocate a "large" object */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + large_pool_ptr hdr_ptr; + size_t odd_bytes; + + /* Check for unsatisfiable request (do now to ensure no overflow below) */ + if (sizeofobject > (size_t) (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr))) + out_of_memory(cinfo, 3); /* request exceeds malloc's ability */ + + /* Round up the requested size to a multiple of SIZEOF(ALIGN_TYPE) */ + odd_bytes = sizeofobject % SIZEOF(ALIGN_TYPE); + if (odd_bytes > 0) + sizeofobject += SIZEOF(ALIGN_TYPE) - odd_bytes; + + /* Always make a new pool */ + if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + hdr_ptr = (large_pool_ptr) jpeg_get_large(cinfo, sizeofobject + + SIZEOF(large_pool_hdr)); + if (hdr_ptr == NULL) + out_of_memory(cinfo, 4); /* jpeg_get_large failed */ + mem->total_space_allocated += sizeofobject + SIZEOF(large_pool_hdr); + + /* Success, initialize the new pool header and add to list */ + hdr_ptr->hdr.next = mem->large_list[pool_id]; + /* We maintain space counts in each pool header for statistical purposes, + * even though they are not needed for allocation. + */ + hdr_ptr->hdr.bytes_used = sizeofobject; + hdr_ptr->hdr.bytes_left = 0; + mem->large_list[pool_id] = hdr_ptr; + + return (void FAR *) (hdr_ptr + 1); /* point to first data byte in pool */ +} + + +/* + * Creation of 2-D sample arrays. + * The pointers are in near heap, the samples themselves in FAR heap. + * + * To minimize allocation overhead and to allow I/O of large contiguous + * blocks, we allocate the sample rows in groups of as many rows as possible + * without exceeding MAX_ALLOC_CHUNK total bytes per allocation request. + * NB: the virtual array control routines, later in this file, know about + * this chunking of rows. The rowsperchunk value is left in the mem manager + * object so that it can be saved away if this sarray is the workspace for + * a virtual array. + */ + +METHODDEF JSAMPARRAY +alloc_sarray (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, JDIMENSION numrows) +/* Allocate a 2-D sample array */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + JSAMPARRAY result; + JSAMPROW workspace; + JDIMENSION rowsperchunk, currow, i; + long ltemp; + + /* Calculate max # of rows allowed in one allocation chunk */ + ltemp = (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr)) / + ((long) samplesperrow * SIZEOF(JSAMPLE)); + if (ltemp <= 0) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + if (ltemp < (long) numrows) + rowsperchunk = (JDIMENSION) ltemp; + else + rowsperchunk = numrows; + mem->last_rowsperchunk = rowsperchunk; + + /* Get space for row pointers (small object) */ + result = (JSAMPARRAY) alloc_small(cinfo, pool_id, + (size_t) (numrows * SIZEOF(JSAMPROW))); + + /* Get the rows themselves (large objects) */ + currow = 0; + while (currow < numrows) { + rowsperchunk = MIN(rowsperchunk, numrows - currow); + workspace = (JSAMPROW) alloc_large(cinfo, pool_id, + (size_t) ((size_t) rowsperchunk * (size_t) samplesperrow + * SIZEOF(JSAMPLE))); + for (i = rowsperchunk; i > 0; i--) { + result[currow++] = workspace; + workspace += samplesperrow; + } + } + + return result; +} + + +/* + * Creation of 2-D coefficient-block arrays. + * This is essentially the same as the code for sample arrays, above. + */ + +METHODDEF JBLOCKARRAY +alloc_barray (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, JDIMENSION numrows) +/* Allocate a 2-D coefficient-block array */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + JBLOCKARRAY result; + JBLOCKROW workspace; + JDIMENSION rowsperchunk, currow, i; + long ltemp; + + /* Calculate max # of rows allowed in one allocation chunk */ + ltemp = (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr)) / + ((long) blocksperrow * SIZEOF(JBLOCK)); + if (ltemp <= 0) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + if (ltemp < (long) numrows) + rowsperchunk = (JDIMENSION) ltemp; + else + rowsperchunk = numrows; + mem->last_rowsperchunk = rowsperchunk; + + /* Get space for row pointers (small object) */ + result = (JBLOCKARRAY) alloc_small(cinfo, pool_id, + (size_t) (numrows * SIZEOF(JBLOCKROW))); + + /* Get the rows themselves (large objects) */ + currow = 0; + while (currow < numrows) { + rowsperchunk = MIN(rowsperchunk, numrows - currow); + workspace = (JBLOCKROW) alloc_large(cinfo, pool_id, + (size_t) ((size_t) rowsperchunk * (size_t) blocksperrow + * SIZEOF(JBLOCK))); + for (i = rowsperchunk; i > 0; i--) { + result[currow++] = workspace; + workspace += blocksperrow; + } + } + + return result; +} + + +/* + * About virtual array management: + * + * The above "normal" array routines are only used to allocate strip buffers + * (as wide as the image, but just a few rows high). Full-image-sized buffers + * are handled as "virtual" arrays. The array is still accessed a strip at a + * time, but the memory manager must save the whole array for repeated + * accesses. The intended implementation is that there is a strip buffer in + * memory (as high as is possible given the desired memory limit), plus a + * backing file that holds the rest of the array. + * + * The request_virt_array routines are told the total size of the image and + * the maximum number of rows that will be accessed at once. The in-memory + * buffer must be at least as large as the maxaccess value. + * + * The request routines create control blocks but not the in-memory buffers. + * That is postponed until realize_virt_arrays is called. At that time the + * total amount of space needed is known (approximately, anyway), so free + * memory can be divided up fairly. + * + * The access_virt_array routines are responsible for making a specific strip + * area accessible (after reading or writing the backing file, if necessary). + * Note that the access routines are told whether the caller intends to modify + * the accessed strip; during a read-only pass this saves having to rewrite + * data to disk. The access routines are also responsible for pre-zeroing + * any newly accessed rows, if pre-zeroing was requested. + * + * In current usage, the access requests are usually for nonoverlapping + * strips; that is, successive access start_row numbers differ by exactly + * num_rows = maxaccess. This means we can get good performance with simple + * buffer dump/reload logic, by making the in-memory buffer be a multiple + * of the access height; then there will never be accesses across bufferload + * boundaries. The code will still work with overlapping access requests, + * but it doesn't handle bufferload overlaps very efficiently. + */ + + +METHODDEF jvirt_sarray_ptr +request_virt_sarray (j_common_ptr cinfo, int pool_id, boolean pre_zero, + JDIMENSION samplesperrow, JDIMENSION numrows, + JDIMENSION maxaccess) +/* Request a virtual 2-D sample array */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + jvirt_sarray_ptr result; + + /* Only IMAGE-lifetime virtual arrays are currently supported */ + if (pool_id != JPOOL_IMAGE) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + /* get control block */ + result = (jvirt_sarray_ptr) alloc_small(cinfo, pool_id, + SIZEOF(struct jvirt_sarray_control)); + + result->mem_buffer = NULL; /* marks array not yet realized */ + result->rows_in_array = numrows; + result->samplesperrow = samplesperrow; + result->maxaccess = maxaccess; + result->pre_zero = pre_zero; + result->b_s_open = FALSE; /* no associated backing-store object */ + result->next = mem->virt_sarray_list; /* add to list of virtual arrays */ + mem->virt_sarray_list = result; + + return result; +} + + +METHODDEF jvirt_barray_ptr +request_virt_barray (j_common_ptr cinfo, int pool_id, boolean pre_zero, + JDIMENSION blocksperrow, JDIMENSION numrows, + JDIMENSION maxaccess) +/* Request a virtual 2-D coefficient-block array */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + jvirt_barray_ptr result; + + /* Only IMAGE-lifetime virtual arrays are currently supported */ + if (pool_id != JPOOL_IMAGE) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + /* get control block */ + result = (jvirt_barray_ptr) alloc_small(cinfo, pool_id, + SIZEOF(struct jvirt_barray_control)); + + result->mem_buffer = NULL; /* marks array not yet realized */ + result->rows_in_array = numrows; + result->blocksperrow = blocksperrow; + result->maxaccess = maxaccess; + result->pre_zero = pre_zero; + result->b_s_open = FALSE; /* no associated backing-store object */ + result->next = mem->virt_barray_list; /* add to list of virtual arrays */ + mem->virt_barray_list = result; + + return result; +} + + +METHODDEF void +realize_virt_arrays (j_common_ptr cinfo) +/* Allocate the in-memory buffers for any unrealized virtual arrays */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + long space_per_minheight, maximum_space, avail_mem; + long minheights, max_minheights; + jvirt_sarray_ptr sptr; + jvirt_barray_ptr bptr; + + /* Compute the minimum space needed (maxaccess rows in each buffer) + * and the maximum space needed (full image height in each buffer). + * These may be of use to the system-dependent jpeg_mem_available routine. + */ + space_per_minheight = 0; + maximum_space = 0; + for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { + if (sptr->mem_buffer == NULL) { /* if not realized yet */ + space_per_minheight += (long) sptr->maxaccess * + (long) sptr->samplesperrow * SIZEOF(JSAMPLE); + maximum_space += (long) sptr->rows_in_array * + (long) sptr->samplesperrow * SIZEOF(JSAMPLE); + } + } + for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { + if (bptr->mem_buffer == NULL) { /* if not realized yet */ + space_per_minheight += (long) bptr->maxaccess * + (long) bptr->blocksperrow * SIZEOF(JBLOCK); + maximum_space += (long) bptr->rows_in_array * + (long) bptr->blocksperrow * SIZEOF(JBLOCK); + } + } + + if (space_per_minheight <= 0) + return; /* no unrealized arrays, no work */ + + /* Determine amount of memory to actually use; this is system-dependent. */ + avail_mem = jpeg_mem_available(cinfo, space_per_minheight, maximum_space, + mem->total_space_allocated); + + /* If the maximum space needed is available, make all the buffers full + * height; otherwise parcel it out with the same number of minheights + * in each buffer. + */ + if (avail_mem >= maximum_space) + max_minheights = 1000000000L; + else { + max_minheights = avail_mem / space_per_minheight; + /* If there doesn't seem to be enough space, try to get the minimum + * anyway. This allows a "stub" implementation of jpeg_mem_available(). + */ + if (max_minheights <= 0) + max_minheights = 1; + } + + /* Allocate the in-memory buffers and initialize backing store as needed. */ + + for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { + if (sptr->mem_buffer == NULL) { /* if not realized yet */ + minheights = ((long) sptr->rows_in_array - 1L) / sptr->maxaccess + 1L; + if (minheights <= max_minheights) { + /* This buffer fits in memory */ + sptr->rows_in_mem = sptr->rows_in_array; + } else { + /* It doesn't fit in memory, create backing store. */ + sptr->rows_in_mem = (JDIMENSION) (max_minheights * sptr->maxaccess); + jpeg_open_backing_store(cinfo, & sptr->b_s_info, + (long) sptr->rows_in_array * + (long) sptr->samplesperrow * + (long) SIZEOF(JSAMPLE)); + sptr->b_s_open = TRUE; + } + sptr->mem_buffer = alloc_sarray(cinfo, JPOOL_IMAGE, + sptr->samplesperrow, sptr->rows_in_mem); + sptr->rowsperchunk = mem->last_rowsperchunk; + sptr->cur_start_row = 0; + sptr->first_undef_row = 0; + sptr->dirty = FALSE; + } + } + + for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { + if (bptr->mem_buffer == NULL) { /* if not realized yet */ + minheights = ((long) bptr->rows_in_array - 1L) / bptr->maxaccess + 1L; + if (minheights <= max_minheights) { + /* This buffer fits in memory */ + bptr->rows_in_mem = bptr->rows_in_array; + } else { + /* It doesn't fit in memory, create backing store. */ + bptr->rows_in_mem = (JDIMENSION) (max_minheights * bptr->maxaccess); + jpeg_open_backing_store(cinfo, & bptr->b_s_info, + (long) bptr->rows_in_array * + (long) bptr->blocksperrow * + (long) SIZEOF(JBLOCK)); + bptr->b_s_open = TRUE; + } + bptr->mem_buffer = alloc_barray(cinfo, JPOOL_IMAGE, + bptr->blocksperrow, bptr->rows_in_mem); + bptr->rowsperchunk = mem->last_rowsperchunk; + bptr->cur_start_row = 0; + bptr->first_undef_row = 0; + bptr->dirty = FALSE; + } + } +} + + +LOCAL void +do_sarray_io (j_common_ptr cinfo, jvirt_sarray_ptr ptr, boolean writing) +/* Do backing store read or write of a virtual sample array */ +{ + long bytesperrow, file_offset, byte_count, rows, thisrow, i; + + bytesperrow = (long) ptr->samplesperrow * SIZEOF(JSAMPLE); + file_offset = ptr->cur_start_row * bytesperrow; + /* Loop to read or write each allocation chunk in mem_buffer */ + for (i = 0; i < (long) ptr->rows_in_mem; i += ptr->rowsperchunk) { + /* One chunk, but check for short chunk at end of buffer */ + rows = MIN((long) ptr->rowsperchunk, (long) ptr->rows_in_mem - i); + /* Transfer no more than is currently defined */ + thisrow = (long) ptr->cur_start_row + i; + rows = MIN(rows, (long) ptr->first_undef_row - thisrow); + /* Transfer no more than fits in file */ + rows = MIN(rows, (long) ptr->rows_in_array - thisrow); + if (rows <= 0) /* this chunk might be past end of file! */ + break; + byte_count = rows * bytesperrow; + if (writing) + (*ptr->b_s_info.write_backing_store) (cinfo, & ptr->b_s_info, + (void FAR *) ptr->mem_buffer[i], + file_offset, byte_count); + else + (*ptr->b_s_info.read_backing_store) (cinfo, & ptr->b_s_info, + (void FAR *) ptr->mem_buffer[i], + file_offset, byte_count); + file_offset += byte_count; + } +} + + +LOCAL void +do_barray_io (j_common_ptr cinfo, jvirt_barray_ptr ptr, boolean writing) +/* Do backing store read or write of a virtual coefficient-block array */ +{ + long bytesperrow, file_offset, byte_count, rows, thisrow, i; + + bytesperrow = (long) ptr->blocksperrow * SIZEOF(JBLOCK); + file_offset = ptr->cur_start_row * bytesperrow; + /* Loop to read or write each allocation chunk in mem_buffer */ + for (i = 0; i < (long) ptr->rows_in_mem; i += ptr->rowsperchunk) { + /* One chunk, but check for short chunk at end of buffer */ + rows = MIN((long) ptr->rowsperchunk, (long) ptr->rows_in_mem - i); + /* Transfer no more than is currently defined */ + thisrow = (long) ptr->cur_start_row + i; + rows = MIN(rows, (long) ptr->first_undef_row - thisrow); + /* Transfer no more than fits in file */ + rows = MIN(rows, (long) ptr->rows_in_array - thisrow); + if (rows <= 0) /* this chunk might be past end of file! */ + break; + byte_count = rows * bytesperrow; + if (writing) + (*ptr->b_s_info.write_backing_store) (cinfo, & ptr->b_s_info, + (void FAR *) ptr->mem_buffer[i], + file_offset, byte_count); + else + (*ptr->b_s_info.read_backing_store) (cinfo, & ptr->b_s_info, + (void FAR *) ptr->mem_buffer[i], + file_offset, byte_count); + file_offset += byte_count; + } +} + + +METHODDEF JSAMPARRAY +access_virt_sarray (j_common_ptr cinfo, jvirt_sarray_ptr ptr, + JDIMENSION start_row, JDIMENSION num_rows, + boolean writable) +/* Access the part of a virtual sample array starting at start_row */ +/* and extending for num_rows rows. writable is true if */ +/* caller intends to modify the accessed area. */ +{ + JDIMENSION end_row = start_row + num_rows; + JDIMENSION undef_row; + + /* debugging check */ + if (end_row > ptr->rows_in_array || num_rows > ptr->maxaccess || + ptr->mem_buffer == NULL) + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + /* Make the desired part of the virtual array accessible */ + if (start_row < ptr->cur_start_row || + end_row > ptr->cur_start_row+ptr->rows_in_mem) { + if (! ptr->b_s_open) + ERREXIT(cinfo, JERR_VIRTUAL_BUG); + /* Flush old buffer contents if necessary */ + if (ptr->dirty) { + do_sarray_io(cinfo, ptr, TRUE); + ptr->dirty = FALSE; + } + /* Decide what part of virtual array to access. + * Algorithm: if target address > current window, assume forward scan, + * load starting at target address. If target address < current window, + * assume backward scan, load so that target area is top of window. + * Note that when switching from forward write to forward read, will have + * start_row = 0, so the limiting case applies and we load from 0 anyway. + */ + if (start_row > ptr->cur_start_row) { + ptr->cur_start_row = start_row; + } else { + /* use long arithmetic here to avoid overflow & unsigned problems */ + long ltemp; + + ltemp = (long) end_row - (long) ptr->rows_in_mem; + if (ltemp < 0) + ltemp = 0; /* don't fall off front end of file */ + ptr->cur_start_row = (JDIMENSION) ltemp; + } + /* Read in the selected part of the array. + * During the initial write pass, we will do no actual read + * because the selected part is all undefined. + */ + do_sarray_io(cinfo, ptr, FALSE); + } + /* Ensure the accessed part of the array is defined; prezero if needed. + * To improve locality of access, we only prezero the part of the array + * that the caller is about to access, not the entire in-memory array. + */ + if (ptr->first_undef_row < end_row) { + if (ptr->first_undef_row < start_row) { + if (writable) /* writer skipped over a section of array */ + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + undef_row = start_row; /* but reader is allowed to read ahead */ + } else { + undef_row = ptr->first_undef_row; + } + if (writable) + ptr->first_undef_row = end_row; + if (ptr->pre_zero) { + size_t bytesperrow = (size_t) ptr->samplesperrow * SIZEOF(JSAMPLE); + undef_row -= ptr->cur_start_row; /* make indexes relative to buffer */ + end_row -= ptr->cur_start_row; + while (undef_row < end_row) { + jzero_far((void FAR *) ptr->mem_buffer[undef_row], bytesperrow); + undef_row++; + } + } else { + if (! writable) /* reader looking at undefined data */ + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + } + } + /* Flag the buffer dirty if caller will write in it */ + if (writable) + ptr->dirty = TRUE; + /* Return address of proper part of the buffer */ + return ptr->mem_buffer + (start_row - ptr->cur_start_row); +} + + +METHODDEF JBLOCKARRAY +access_virt_barray (j_common_ptr cinfo, jvirt_barray_ptr ptr, + JDIMENSION start_row, JDIMENSION num_rows, + boolean writable) +/* Access the part of a virtual block array starting at start_row */ +/* and extending for num_rows rows. writable is true if */ +/* caller intends to modify the accessed area. */ +{ + JDIMENSION end_row = start_row + num_rows; + JDIMENSION undef_row; + + /* debugging check */ + if (end_row > ptr->rows_in_array || num_rows > ptr->maxaccess || + ptr->mem_buffer == NULL) + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + /* Make the desired part of the virtual array accessible */ + if (start_row < ptr->cur_start_row || + end_row > ptr->cur_start_row+ptr->rows_in_mem) { + if (! ptr->b_s_open) + ERREXIT(cinfo, JERR_VIRTUAL_BUG); + /* Flush old buffer contents if necessary */ + if (ptr->dirty) { + do_barray_io(cinfo, ptr, TRUE); + ptr->dirty = FALSE; + } + /* Decide what part of virtual array to access. + * Algorithm: if target address > current window, assume forward scan, + * load starting at target address. If target address < current window, + * assume backward scan, load so that target area is top of window. + * Note that when switching from forward write to forward read, will have + * start_row = 0, so the limiting case applies and we load from 0 anyway. + */ + if (start_row > ptr->cur_start_row) { + ptr->cur_start_row = start_row; + } else { + /* use long arithmetic here to avoid overflow & unsigned problems */ + long ltemp; + + ltemp = (long) end_row - (long) ptr->rows_in_mem; + if (ltemp < 0) + ltemp = 0; /* don't fall off front end of file */ + ptr->cur_start_row = (JDIMENSION) ltemp; + } + /* Read in the selected part of the array. + * During the initial write pass, we will do no actual read + * because the selected part is all undefined. + */ + do_barray_io(cinfo, ptr, FALSE); + } + /* Ensure the accessed part of the array is defined; prezero if needed. + * To improve locality of access, we only prezero the part of the array + * that the caller is about to access, not the entire in-memory array. + */ + if (ptr->first_undef_row < end_row) { + if (ptr->first_undef_row < start_row) { + if (writable) /* writer skipped over a section of array */ + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + undef_row = start_row; /* but reader is allowed to read ahead */ + } else { + undef_row = ptr->first_undef_row; + } + if (writable) + ptr->first_undef_row = end_row; + if (ptr->pre_zero) { + size_t bytesperrow = (size_t) ptr->blocksperrow * SIZEOF(JBLOCK); + undef_row -= ptr->cur_start_row; /* make indexes relative to buffer */ + end_row -= ptr->cur_start_row; + while (undef_row < end_row) { + jzero_far((void FAR *) ptr->mem_buffer[undef_row], bytesperrow); + undef_row++; + } + } else { + if (! writable) /* reader looking at undefined data */ + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + } + } + /* Flag the buffer dirty if caller will write in it */ + if (writable) + ptr->dirty = TRUE; + /* Return address of proper part of the buffer */ + return ptr->mem_buffer + (start_row - ptr->cur_start_row); +} + + +/* + * Release all objects belonging to a specified pool. + */ + +METHODDEF void +free_pool (j_common_ptr cinfo, int pool_id) +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + small_pool_ptr shdr_ptr; + large_pool_ptr lhdr_ptr; + size_t space_freed; + + if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + +#ifdef MEM_STATS + if (cinfo->err->trace_level > 1) + print_mem_stats(cinfo, pool_id); /* print pool's memory usage statistics */ +#endif + + /* If freeing IMAGE pool, close any virtual arrays first */ + if (pool_id == JPOOL_IMAGE) { + jvirt_sarray_ptr sptr; + jvirt_barray_ptr bptr; + + for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { + if (sptr->b_s_open) { /* there may be no backing store */ + sptr->b_s_open = FALSE; /* prevent recursive close if error */ + (*sptr->b_s_info.close_backing_store) (cinfo, & sptr->b_s_info); + } + } + mem->virt_sarray_list = NULL; + for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { + if (bptr->b_s_open) { /* there may be no backing store */ + bptr->b_s_open = FALSE; /* prevent recursive close if error */ + (*bptr->b_s_info.close_backing_store) (cinfo, & bptr->b_s_info); + } + } + mem->virt_barray_list = NULL; + } + + /* Release large objects */ + lhdr_ptr = mem->large_list[pool_id]; + mem->large_list[pool_id] = NULL; + + while (lhdr_ptr != NULL) { + large_pool_ptr next_lhdr_ptr = lhdr_ptr->hdr.next; + space_freed = lhdr_ptr->hdr.bytes_used + + lhdr_ptr->hdr.bytes_left + + SIZEOF(large_pool_hdr); + jpeg_free_large(cinfo, (void FAR *) lhdr_ptr, space_freed); + mem->total_space_allocated -= space_freed; + lhdr_ptr = next_lhdr_ptr; + } + + /* Release small objects */ + shdr_ptr = mem->small_list[pool_id]; + mem->small_list[pool_id] = NULL; + + while (shdr_ptr != NULL) { + small_pool_ptr next_shdr_ptr = shdr_ptr->hdr.next; + space_freed = shdr_ptr->hdr.bytes_used + + shdr_ptr->hdr.bytes_left + + SIZEOF(small_pool_hdr); + jpeg_free_small(cinfo, (void *) shdr_ptr, space_freed); + mem->total_space_allocated -= space_freed; + shdr_ptr = next_shdr_ptr; + } +} + + +/* + * Close up shop entirely. + * Note that this cannot be called unless cinfo->mem is non-NULL. + */ + +METHODDEF void +self_destruct (j_common_ptr cinfo) +{ + int pool; + + /* Close all backing store, release all memory. + * Releasing pools in reverse order might help avoid fragmentation + * with some (brain-damaged) malloc libraries. + */ + for (pool = JPOOL_NUMPOOLS-1; pool >= JPOOL_PERMANENT; pool--) { + free_pool(cinfo, pool); + } + + /* Release the memory manager control block too. */ + jpeg_free_small(cinfo, (void *) cinfo->mem, SIZEOF(my_memory_mgr)); + cinfo->mem = NULL; /* ensures I will be called only once */ + + jpeg_mem_term(cinfo); /* system-dependent cleanup */ +} + + +/* + * Memory manager initialization. + * When this is called, only the error manager pointer is valid in cinfo! + */ + +GLOBAL void +jinit_memory_mgr (j_common_ptr cinfo) +{ + my_mem_ptr mem; + long max_to_use; + int pool; + size_t test_mac; + + cinfo->mem = NULL; /* for safety if init fails */ + + /* Check for configuration errors. + * SIZEOF(ALIGN_TYPE) should be a power of 2; otherwise, it probably + * doesn't reflect any real hardware alignment requirement. + * The test is a little tricky: for X>0, X and X-1 have no one-bits + * in common if and only if X is a power of 2, ie has only one one-bit. + * Some compilers may give an "unreachable code" warning here; ignore it. + */ + if ((SIZEOF(ALIGN_TYPE) & (SIZEOF(ALIGN_TYPE)-1)) != 0) + ERREXIT(cinfo, JERR_BAD_ALIGN_TYPE); + /* MAX_ALLOC_CHUNK must be representable as type size_t, and must be + * a multiple of SIZEOF(ALIGN_TYPE). + * Again, an "unreachable code" warning may be ignored here. + * But a "constant too large" warning means you need to fix MAX_ALLOC_CHUNK. + */ + test_mac = (size_t) MAX_ALLOC_CHUNK; + if ((long) test_mac != MAX_ALLOC_CHUNK || + (MAX_ALLOC_CHUNK % SIZEOF(ALIGN_TYPE)) != 0) + ERREXIT(cinfo, JERR_BAD_ALLOC_CHUNK); + + max_to_use = jpeg_mem_init(cinfo); /* system-dependent initialization */ + + /* Attempt to allocate memory manager's control block */ + mem = (my_mem_ptr) jpeg_get_small(cinfo, SIZEOF(my_memory_mgr)); + + if (mem == NULL) { + jpeg_mem_term(cinfo); /* system-dependent cleanup */ + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 0); + } + + /* OK, fill in the method pointers */ + mem->pub.alloc_small = alloc_small; + mem->pub.alloc_large = alloc_large; + mem->pub.alloc_sarray = alloc_sarray; + mem->pub.alloc_barray = alloc_barray; + mem->pub.request_virt_sarray = request_virt_sarray; + mem->pub.request_virt_barray = request_virt_barray; + mem->pub.realize_virt_arrays = realize_virt_arrays; + mem->pub.access_virt_sarray = access_virt_sarray; + mem->pub.access_virt_barray = access_virt_barray; + mem->pub.free_pool = free_pool; + mem->pub.self_destruct = self_destruct; + + /* Initialize working state */ + mem->pub.max_memory_to_use = max_to_use; + + for (pool = JPOOL_NUMPOOLS-1; pool >= JPOOL_PERMANENT; pool--) { + mem->small_list[pool] = NULL; + mem->large_list[pool] = NULL; + } + mem->virt_sarray_list = NULL; + mem->virt_barray_list = NULL; + + mem->total_space_allocated = SIZEOF(my_memory_mgr); + + /* Declare ourselves open for business */ + cinfo->mem = & mem->pub; + + /* Check for an environment variable JPEGMEM; if found, override the + * default max_memory setting from jpeg_mem_init. Note that the + * surrounding application may again override this value. + * If your system doesn't support getenv(), define NO_GETENV to disable + * this feature. + */ +#ifndef NO_GETENV + { char * memenv; + + if ((memenv = getenv("JPEGMEM")) != NULL) { + char ch = 'x'; + + if (sscanf(memenv, "%ld%c", &max_to_use, &ch) > 0) { + if (ch == 'm' || ch == 'M') + max_to_use *= 1000L; + mem->pub.max_memory_to_use = max_to_use * 1000L; + } + } + } +#endif + +} diff --git a/utils/Radiant/jpeg-6/jmemnobs.c b/utils/Radiant/jpeg-6/jmemnobs.c new file mode 100644 index 0000000..84602f2 --- /dev/null +++ b/utils/Radiant/jpeg-6/jmemnobs.c @@ -0,0 +1,123 @@ +/* + * jmemnobs.c + * + * Copyright (C) 1992-1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file provides a really simple implementation of the system- + * dependent portion of the JPEG memory manager. This implementation + * assumes that no backing-store files are needed: all required space + * can be obtained from ri.Malloc(). + * This is very portable in the sense that it'll compile on almost anything, + * but you'd better have lots of main memory (or virtual memory) if you want + * to process big images. + * Note that the max_memory_to_use option is ignored by this implementation. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jmemsys.h" /* import the system-dependent declarations */ + +/* + * Memory allocation and ri.Freeing are controlled by the regular library + * routines ri.Malloc() and ri.Free(). + */ + +GLOBAL void * +jpeg_get_small (j_common_ptr cinfo, size_t sizeofobject) +{ +// stefix +#if 0 + return (void *) ri.Malloc(sizeofobject); +#else + return (void *) malloc(sizeofobject); +#endif +} + +GLOBAL void +jpeg_free_small (j_common_ptr cinfo, void * object, size_t sizeofobject) +{ +// stefix +#if 0 + ri.Free(object); +#else + free(object); +#endif +} + + +/* + * "Large" objects are treated the same as "small" ones. + * NB: although we include FAR keywords in the routine declarations, + * this file won't actually work in 80x86 small/medium model; at least, + * you probably won't be able to process useful-size images in only 64KB. + */ + +GLOBAL void FAR * +jpeg_get_large (j_common_ptr cinfo, size_t sizeofobject) +{ +// stefix +#if 0 + return (void FAR *) ri.Malloc(sizeofobject); +#else + return (void FAR *) malloc(sizeofobject); +#endif +} + +GLOBAL void +jpeg_free_large (j_common_ptr cinfo, void FAR * object, size_t sizeofobject) +{ +// stefix +#if 0 + ri.Free(object); +#else + free(object); +#endif +} + + +/* + * This routine computes the total memory space available for allocation. + * Here we always say, "we got all you want bud!" + */ + +GLOBAL long +jpeg_mem_available (j_common_ptr cinfo, long min_bytes_needed, + long max_bytes_needed, long already_allocated) +{ + return max_bytes_needed; +} + + +/* + * Backing store (temporary file) management. + * Since jpeg_mem_available always promised the moon, + * this should never be called and we can just error out. + */ + +GLOBAL void +jpeg_open_backing_store (j_common_ptr cinfo, backing_store_ptr info, + long total_bytes_needed) +{ + ERREXIT(cinfo, JERR_NO_BACKING_STORE); +} + + +/* + * These routines take care of any system-dependent initialization and + * cleanup required. Here, there isn't any. + */ + +GLOBAL long +jpeg_mem_init (j_common_ptr cinfo) +{ + return 0; /* just set max_memory_to_use to 0 */ +} + +GLOBAL void +jpeg_mem_term (j_common_ptr cinfo) +{ + /* no work */ +} diff --git a/utils/Radiant/jpeg-6/jmemsys.h b/utils/Radiant/jpeg-6/jmemsys.h new file mode 100644 index 0000000..0c7d7c1 --- /dev/null +++ b/utils/Radiant/jpeg-6/jmemsys.h @@ -0,0 +1,182 @@ +/* + * jmemsys.h + * + * Copyright (C) 1992-1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This include file defines the interface between the system-independent + * and system-dependent portions of the JPEG memory manager. No other + * modules need include it. (The system-independent portion is jmemmgr.c; + * there are several different versions of the system-dependent portion.) + * + * This file works as-is for the system-dependent memory managers supplied + * in the IJG distribution. You may need to modify it if you write a + * custom memory manager. If system-dependent changes are needed in + * this file, the best method is to #ifdef them based on a configuration + * symbol supplied in jconfig.h, as we have done with USE_MSDOS_MEMMGR. + */ + + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_get_small jGetSmall +#define jpeg_free_small jFreeSmall +#define jpeg_get_large jGetLarge +#define jpeg_free_large jFreeLarge +#define jpeg_mem_available jMemAvail +#define jpeg_open_backing_store jOpenBackStore +#define jpeg_mem_init jMemInit +#define jpeg_mem_term jMemTerm +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* + * These two functions are used to allocate and release small chunks of + * memory. (Typically the total amount requested through jpeg_get_small is + * no more than 20K or so; this will be requested in chunks of a few K each.) + * Behavior should be the same as for the standard library functions malloc + * and free; in particular, jpeg_get_small must return NULL on failure. + * On most systems, these ARE malloc and free. jpeg_free_small is passed the + * size of the object being freed, just in case it's needed. + * On an 80x86 machine using small-data memory model, these manage near heap. + */ + +EXTERN void * jpeg_get_small JPP((j_common_ptr cinfo, size_t sizeofobject)); +EXTERN void jpeg_free_small JPP((j_common_ptr cinfo, void * object, + size_t sizeofobject)); + +/* + * These two functions are used to allocate and release large chunks of + * memory (up to the total free space designated by jpeg_mem_available). + * The interface is the same as above, except that on an 80x86 machine, + * far pointers are used. On most other machines these are identical to + * the jpeg_get/free_small routines; but we keep them separate anyway, + * in case a different allocation strategy is desirable for large chunks. + */ + +EXTERN void FAR * jpeg_get_large JPP((j_common_ptr cinfo,size_t sizeofobject)); +EXTERN void jpeg_free_large JPP((j_common_ptr cinfo, void FAR * object, + size_t sizeofobject)); + +/* + * The macro MAX_ALLOC_CHUNK designates the maximum number of bytes that may + * be requested in a single call to jpeg_get_large (and jpeg_get_small for that + * matter, but that case should never come into play). This macro is needed + * to model the 64Kb-segment-size limit of far addressing on 80x86 machines. + * On those machines, we expect that jconfig.h will provide a proper value. + * On machines with 32-bit flat address spaces, any large constant may be used. + * + * NB: jmemmgr.c expects that MAX_ALLOC_CHUNK will be representable as type + * size_t and will be a multiple of sizeof(align_type). + */ + +#ifndef MAX_ALLOC_CHUNK /* may be overridden in jconfig.h */ +#define MAX_ALLOC_CHUNK 1000000000L +#endif + +/* + * This routine computes the total space still available for allocation by + * jpeg_get_large. If more space than this is needed, backing store will be + * used. NOTE: any memory already allocated must not be counted. + * + * There is a minimum space requirement, corresponding to the minimum + * feasible buffer sizes; jmemmgr.c will request that much space even if + * jpeg_mem_available returns zero. The maximum space needed, enough to hold + * all working storage in memory, is also passed in case it is useful. + * Finally, the total space already allocated is passed. If no better + * method is available, cinfo->mem->max_memory_to_use - already_allocated + * is often a suitable calculation. + * + * It is OK for jpeg_mem_available to underestimate the space available + * (that'll just lead to more backing-store access than is really necessary). + * However, an overestimate will lead to failure. Hence it's wise to subtract + * a slop factor from the true available space. 5% should be enough. + * + * On machines with lots of virtual memory, any large constant may be returned. + * Conversely, zero may be returned to always use the minimum amount of memory. + */ + +EXTERN long jpeg_mem_available JPP((j_common_ptr cinfo, + long min_bytes_needed, + long max_bytes_needed, + long already_allocated)); + + +/* + * This structure holds whatever state is needed to access a single + * backing-store object. The read/write/close method pointers are called + * by jmemmgr.c to manipulate the backing-store object; all other fields + * are private to the system-dependent backing store routines. + */ + +#define TEMP_NAME_LENGTH 64 /* max length of a temporary file's name */ + +#ifdef USE_MSDOS_MEMMGR /* DOS-specific junk */ + +typedef unsigned short XMSH; /* type of extended-memory handles */ +typedef unsigned short EMSH; /* type of expanded-memory handles */ + +typedef union { + short file_handle; /* DOS file handle if it's a temp file */ + XMSH xms_handle; /* handle if it's a chunk of XMS */ + EMSH ems_handle; /* handle if it's a chunk of EMS */ +} handle_union; + +#endif /* USE_MSDOS_MEMMGR */ + +typedef struct backing_store_struct * backing_store_ptr; + +typedef struct backing_store_struct { + /* Methods for reading/writing/closing this backing-store object */ + JMETHOD(void, read_backing_store, (j_common_ptr cinfo, + backing_store_ptr info, + void FAR * buffer_address, + long file_offset, long byte_count)); + JMETHOD(void, write_backing_store, (j_common_ptr cinfo, + backing_store_ptr info, + void FAR * buffer_address, + long file_offset, long byte_count)); + JMETHOD(void, close_backing_store, (j_common_ptr cinfo, + backing_store_ptr info)); + + /* Private fields for system-dependent backing-store management */ +#ifdef USE_MSDOS_MEMMGR + /* For the MS-DOS manager (jmemdos.c), we need: */ + handle_union handle; /* reference to backing-store storage object */ + char temp_name[TEMP_NAME_LENGTH]; /* name if it's a file */ +#else + /* For a typical implementation with temp files, we need: */ + FILE * temp_file; /* stdio reference to temp file */ + char temp_name[TEMP_NAME_LENGTH]; /* name of temp file */ +#endif +} backing_store_info; + +/* + * Initial opening of a backing-store object. This must fill in the + * read/write/close pointers in the object. The read/write routines + * may take an error exit if the specified maximum file size is exceeded. + * (If jpeg_mem_available always returns a large value, this routine can + * just take an error exit.) + */ + +EXTERN void jpeg_open_backing_store JPP((j_common_ptr cinfo, + backing_store_ptr info, + long total_bytes_needed)); + + +/* + * These routines take care of any system-dependent initialization and + * cleanup required. jpeg_mem_init will be called before anything is + * allocated (and, therefore, nothing in cinfo is of use except the error + * manager pointer). It should return a suitable default value for + * max_memory_to_use; this may subsequently be overridden by the surrounding + * application. (Note that max_memory_to_use is only important if + * jpeg_mem_available chooses to consult it ... no one else will.) + * jpeg_mem_term may assume that all requested memory has been freed and that + * all opened backing-store objects have been closed. + */ + +EXTERN long jpeg_mem_init JPP((j_common_ptr cinfo)); +EXTERN void jpeg_mem_term JPP((j_common_ptr cinfo)); diff --git a/utils/Radiant/jpeg-6/jmorecfg.h b/utils/Radiant/jpeg-6/jmorecfg.h new file mode 100644 index 0000000..d451399 --- /dev/null +++ b/utils/Radiant/jpeg-6/jmorecfg.h @@ -0,0 +1,346 @@ +/* + * jmorecfg.h + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains additional configuration options that customize the + * JPEG software for special applications or support machine-dependent + * optimizations. Most users will not need to touch this file. + */ + + +/* + * Define BITS_IN_JSAMPLE as either + * 8 for 8-bit sample values (the usual setting) + * 12 for 12-bit sample values + * Only 8 and 12 are legal data precisions for lossy JPEG according to the + * JPEG standard, and the IJG code does not support anything else! + * We do not support run-time selection of data precision, sorry. + */ + +#define BITS_IN_JSAMPLE 8 /* use 8 or 12 */ + + +/* + * Maximum number of components (color channels) allowed in JPEG image. + * To meet the letter of the JPEG spec, set this to 255. However, darn + * few applications need more than 4 channels (maybe 5 for CMYK + alpha + * mask). We recommend 10 as a reasonable compromise; use 4 if you are + * really short on memory. (Each allowed component costs a hundred or so + * bytes of storage, whether actually used in an image or not.) + */ + +#define MAX_COMPONENTS 10 /* maximum number of image components */ + + +/* + * Basic data types. + * You may need to change these if you have a machine with unusual data + * type sizes; for example, "char" not 8 bits, "short" not 16 bits, + * or "long" not 32 bits. We don't care whether "int" is 16 or 32 bits, + * but it had better be at least 16. + */ + +/* Representation of a single sample (pixel element value). + * We frequently allocate large arrays of these, so it's important to keep + * them small. But if you have memory to burn and access to char or short + * arrays is very slow on your hardware, you might want to change these. + */ + +#if BITS_IN_JSAMPLE == 8 +/* JSAMPLE should be the smallest type that will hold the values 0..255. + * You can use a signed char by having GETJSAMPLE mask it with 0xFF. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JSAMPLE; +#ifdef CHAR_IS_UNSIGNED +#define GETJSAMPLE(value) ((int) (value)) +#else +#define GETJSAMPLE(value) ((int) (value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + +#define MAXJSAMPLE 255 +#define CENTERJSAMPLE 128 + +#endif /* BITS_IN_JSAMPLE == 8 */ + + +#if BITS_IN_JSAMPLE == 12 +/* JSAMPLE should be the smallest type that will hold the values 0..4095. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 4095 +#define CENTERJSAMPLE 2048 + +#endif /* BITS_IN_JSAMPLE == 12 */ + + +/* Representation of a DCT frequency coefficient. + * This should be a signed value of at least 16 bits; "short" is usually OK. + * Again, we allocate large arrays of these, but you can change to int + * if you have memory to burn and "short" is really slow. + */ + +typedef short JCOEF; + + +/* Compressed datastreams are represented as arrays of JOCTET. + * These must be EXACTLY 8 bits wide, at least once they are written to + * external storage. Note that when using the stdio data source/destination + * managers, this is also the data type passed to fread/fwrite. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JOCTET; +#define GETJOCTET(value) (value) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JOCTET; +#ifdef CHAR_IS_UNSIGNED +#define GETJOCTET(value) (value) +#else +#define GETJOCTET(value) ((value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + + +/* These typedefs are used for various table entries and so forth. + * They must be at least as wide as specified; but making them too big + * won't cost a huge amount of memory, so we don't provide special + * extraction code like we did for JSAMPLE. (In other words, these + * typedefs live at a different point on the speed/space tradeoff curve.) + */ + +/* UINT8 must hold at least the values 0..255. */ + +#ifdef HAVE_UNSIGNED_CHAR +typedef unsigned char UINT8; +#else /* not HAVE_UNSIGNED_CHAR */ +#ifdef CHAR_IS_UNSIGNED +typedef char UINT8; +#else /* not CHAR_IS_UNSIGNED */ +typedef short UINT8; +#endif /* CHAR_IS_UNSIGNED */ +#endif /* HAVE_UNSIGNED_CHAR */ + +/* UINT16 must hold at least the values 0..65535. */ + +#ifdef HAVE_UNSIGNED_SHORT +typedef unsigned short UINT16; +#else /* not HAVE_UNSIGNED_SHORT */ +typedef unsigned int UINT16; +#endif /* HAVE_UNSIGNED_SHORT */ + +/* INT16 must hold at least the values -32768..32767. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT16 */ +typedef short INT16; +#endif + +/* INT32 must hold at least signed 32-bit values. */ + +//#ifndef XMD_H /* X11/xmd.h correctly defines INT32 */ +//typedef long INT32; +//#endif + +/* Datatype used for image dimensions. The JPEG standard only supports + * images up to 64K*64K due to 16-bit fields in SOF markers. Therefore + * "unsigned int" is sufficient on all machines. However, if you need to + * handle larger images and you don't mind deviating from the spec, you + * can change this datatype. + */ + +typedef unsigned int JDIMENSION; + +#define JPEG_MAX_DIMENSION 65500L /* a tad under 64K to prevent overflows */ + + +/* These defines are used in all function definitions and extern declarations. + * You could modify them if you need to change function linkage conventions. + * Another application is to make all functions global for use with debuggers + * or code profilers that require it. + */ + +#define METHODDEF static /* a function called through method pointers */ +#define LOCAL static /* a function used only in its module */ +#define GLOBAL /* a function referenced thru EXTERNs */ +#define EXTERN extern /* a reference to a GLOBAL function */ + + +/* Here is the pseudo-keyword for declaring pointers that must be "far" + * on 80x86 machines. Most of the specialized coding for 80x86 is handled + * by just saying "FAR *" where such a pointer is needed. In a few places + * explicit coding is needed; see uses of the NEED_FAR_POINTERS symbol. + */ + +#ifdef NEED_FAR_POINTERS +#undef FAR +#define FAR far +#else +#undef FAR +#define FAR +#endif + + +/* + * On a few systems, type boolean and/or its values FALSE, TRUE may appear + * in standard header files. Or you may have conflicts with application- + * specific header files that you want to include together with these files. + * Defining HAVE_BOOLEAN before including jpeglib.h should make it work. + */ + +//#ifndef HAVE_BOOLEAN +//typedef int boolean; +//#endif +#ifndef FALSE /* in case these macros already exist */ +#define FALSE 0 /* values of boolean */ +#endif +#ifndef TRUE +#define TRUE 1 +#endif + + +/* + * The remaining options affect code selection within the JPEG library, + * but they don't need to be visible to most applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS or JPEG_INTERNAL_OPTIONS has been defined. + */ + +#ifdef JPEG_INTERNALS +#define JPEG_INTERNAL_OPTIONS +#endif + +#ifdef JPEG_INTERNAL_OPTIONS + + +/* + * These defines indicate whether to include various optional functions. + * Undefining some of these symbols will produce a smaller but less capable + * library. Note that you can leave certain source files out of the + * compilation/linking process if you've #undef'd the corresponding symbols. + * (You may HAVE to do that if your compiler doesn't like null source files.) + */ + +/* Arithmetic coding is unsupported for legal reasons. Complaints to IBM. */ + +/* Capability options common to encoder and decoder: */ + +#undef DCT_ISLOW_SUPPORTED /* slow but accurate integer algorithm */ +#undef DCT_IFAST_SUPPORTED /* faster, less accurate integer method */ +#define DCT_FLOAT_SUPPORTED /* floating-point: accurate, fast on fast HW */ + +/* Encoder capability options: */ + +#undef C_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define C_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define C_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define ENTROPY_OPT_SUPPORTED /* Optimization of entropy coding parms? */ +/* Note: if you selected 12-bit data precision, it is dangerous to turn off + * ENTROPY_OPT_SUPPORTED. The standard Huffman tables are only good for 8-bit + * precision, so jchuff.c normally uses entropy optimization to compute + * usable tables for higher precision. If you don't want to do optimization, + * you'll have to supply different default Huffman tables. + * The exact same statements apply for progressive JPEG: the default tables + * don't work for progressive mode. (This may get fixed, however.) + */ +#define INPUT_SMOOTHING_SUPPORTED /* Input image smoothing option? */ + +/* Decoder capability options: */ + +#undef D_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#undef D_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#undef D_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#undef BLOCK_SMOOTHING_SUPPORTED /* Block smoothing? (Progressive only) */ +#undef IDCT_SCALING_SUPPORTED /* Output rescaling via IDCT? */ +#undef UPSAMPLE_SCALING_SUPPORTED /* Output rescaling at upsample stage? */ +#undef UPSAMPLE_MERGING_SUPPORTED /* Fast path for sloppy upsampling? */ +#undef QUANT_1PASS_SUPPORTED /* 1-pass color quantization? */ +#undef QUANT_2PASS_SUPPORTED /* 2-pass color quantization? */ + +/* more capability options later, no doubt */ + + +/* + * Ordering of RGB data in scanlines passed to or from the application. + * If your application wants to deal with data in the order B,G,R, just + * change these macros. You can also deal with formats such as R,G,B,X + * (one extra byte per pixel) by changing RGB_PIXELSIZE. Note that changing + * the offsets will also change the order in which colormap data is organized. + * RESTRICTIONS: + * 1. The sample applications cjpeg,djpeg do NOT support modified RGB formats. + * 2. These macros only affect RGB<=>YCbCr color conversion, so they are not + * useful if you are using JPEG color spaces other than YCbCr or grayscale. + * 3. The color quantizer modules will not behave desirably if RGB_PIXELSIZE + * is not 3 (they don't understand about dummy color components!). So you + * can't use color quantization if you change that value. + */ + +#define RGB_RED 0 /* Offset of Red in an RGB scanline element */ +#define RGB_GREEN 1 /* Offset of Green */ +#define RGB_BLUE 2 /* Offset of Blue */ +#define RGB_PIXELSIZE 4 /* JSAMPLEs per RGB scanline element */ + + +/* Definitions for speed-related optimizations. */ + + +/* If your compiler supports inline functions, define INLINE + * as the inline keyword; otherwise define it as empty. + */ + +#ifndef INLINE +#ifdef __GNUC__ /* for instance, GNU C knows about inline */ +#define INLINE __inline__ +#endif +#ifndef INLINE +#define INLINE /* default is to define it as empty */ +#endif +#endif + + +/* On some machines (notably 68000 series) "int" is 32 bits, but multiplying + * two 16-bit shorts is faster than multiplying two ints. Define MULTIPLIER + * as short on such a machine. MULTIPLIER must be at least 16 bits wide. + */ + +#ifndef MULTIPLIER +#define MULTIPLIER int /* type for fastest integer multiply */ +#endif + + +/* FAST_FLOAT should be either float or double, whichever is done faster + * by your compiler. (Note that this type is only used in the floating point + * DCT routines, so it only matters if you've defined DCT_FLOAT_SUPPORTED.) + * Typically, float is faster in ANSI C compilers, while double is faster in + * pre-ANSI compilers (because they insist on converting to double anyway). + * The code below therefore chooses float if we have ANSI-style prototypes. + */ + +#ifndef FAST_FLOAT +#ifdef HAVE_PROTOTYPES +#define FAST_FLOAT float +#else +#define FAST_FLOAT double +#endif +#endif + +#endif /* JPEG_INTERNAL_OPTIONS */ diff --git a/utils/Radiant/jpeg-6/jpegint.h b/utils/Radiant/jpeg-6/jpegint.h new file mode 100644 index 0000000..b3b6a6d --- /dev/null +++ b/utils/Radiant/jpeg-6/jpegint.h @@ -0,0 +1,388 @@ +/* + * jpegint.h + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file provides common declarations for the various JPEG modules. + * These declarations are considered internal to the JPEG library; most + * applications using the library shouldn't need to include this file. + */ + + +/* Declarations for both compression & decompression */ + +typedef enum { /* Operating modes for buffer controllers */ + JBUF_PASS_THRU, /* Plain stripwise operation */ + /* Remaining modes require a full-image buffer to have been created */ + JBUF_SAVE_SOURCE, /* Run source subobject only, save output */ + JBUF_CRANK_DEST, /* Run dest subobject only, using saved data */ + JBUF_SAVE_AND_PASS /* Run both subobjects, save output */ +} J_BUF_MODE; + +/* Values of global_state field (jdapi.c has some dependencies on ordering!) */ +#define CSTATE_START 100 /* after create_compress */ +#define CSTATE_SCANNING 101 /* start_compress done, write_scanlines OK */ +#define CSTATE_RAW_OK 102 /* start_compress done, write_raw_data OK */ +#define CSTATE_WRCOEFS 103 /* jpeg_write_coefficients done */ +#define DSTATE_START 200 /* after create_decompress */ +#define DSTATE_INHEADER 201 /* reading header markers, no SOS yet */ +#define DSTATE_READY 202 /* found SOS, ready for start_decompress */ +#define DSTATE_PRELOAD 203 /* reading multiscan file in start_decompress*/ +#define DSTATE_PRESCAN 204 /* performing dummy pass for 2-pass quant */ +#define DSTATE_SCANNING 205 /* start_decompress done, read_scanlines OK */ +#define DSTATE_RAW_OK 206 /* start_decompress done, read_raw_data OK */ +#define DSTATE_BUFIMAGE 207 /* expecting jpeg_start_output */ +#define DSTATE_BUFPOST 208 /* looking for SOS/EOI in jpeg_finish_output */ +#define DSTATE_RDCOEFS 209 /* reading file in jpeg_read_coefficients */ +#define DSTATE_STOPPING 210 /* looking for EOI in jpeg_finish_decompress */ + + +/* Declarations for compression modules */ + +/* Master control module */ +struct jpeg_comp_master { + JMETHOD(void, prepare_for_pass, (j_compress_ptr cinfo)); + JMETHOD(void, pass_startup, (j_compress_ptr cinfo)); + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean call_pass_startup; /* True if pass_startup must be called */ + boolean is_last_pass; /* True during last pass */ +}; + +/* Main buffer control (downsampled-data buffer) */ +struct jpeg_c_main_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, process_data, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail)); +}; + +/* Compression preprocessing (downsampling input buffer control) */ +struct jpeg_c_prep_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, pre_process_data, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, + JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail, + JSAMPIMAGE output_buf, + JDIMENSION *out_row_group_ctr, + JDIMENSION out_row_groups_avail)); +}; + +/* Coefficient buffer control */ +struct jpeg_c_coef_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(boolean, compress_data, (j_compress_ptr cinfo, + JSAMPIMAGE input_buf)); +}; + +/* Colorspace conversion */ +struct jpeg_color_converter { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + JMETHOD(void, color_convert, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +}; + +/* Downsampling */ +struct jpeg_downsampler { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + JMETHOD(void, downsample, (j_compress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_index, + JSAMPIMAGE output_buf, + JDIMENSION out_row_group_index)); + + boolean need_context_rows; /* TRUE if need rows above & below */ +}; + +/* Forward DCT (also controls coefficient quantization) */ +struct jpeg_forward_dct { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + /* perhaps this should be an array??? */ + JMETHOD(void, forward_DCT, (j_compress_ptr cinfo, + jpeg_component_info * compptr, + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + JDIMENSION start_row, JDIMENSION start_col, + JDIMENSION num_blocks)); +}; + +/* Entropy encoding */ +struct jpeg_entropy_encoder { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, boolean gather_statistics)); + JMETHOD(boolean, encode_mcu, (j_compress_ptr cinfo, JBLOCKROW *MCU_data)); + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); +}; + +/* Marker writing */ +struct jpeg_marker_writer { + /* write_any_marker is exported for use by applications */ + /* Probably only COM and APPn markers should be written */ + JMETHOD(void, write_any_marker, (j_compress_ptr cinfo, int marker, + const JOCTET *dataptr, unsigned int datalen)); + JMETHOD(void, write_file_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_frame_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_scan_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_file_trailer, (j_compress_ptr cinfo)); + JMETHOD(void, write_tables_only, (j_compress_ptr cinfo)); +}; + + +/* Declarations for decompression modules */ + +/* Master control module */ +struct jpeg_decomp_master { + JMETHOD(void, prepare_for_output_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, finish_output_pass, (j_decompress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean is_dummy_pass; /* True during 1st pass for 2-pass quant */ +}; + +/* Input control module */ +struct jpeg_input_controller { + JMETHOD(int, consume_input, (j_decompress_ptr cinfo)); + JMETHOD(void, reset_input_controller, (j_decompress_ptr cinfo)); + JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, finish_input_pass, (j_decompress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean has_multiple_scans; /* True if file has multiple scans */ + boolean eoi_reached; /* True when EOI has been consumed */ +}; + +/* Main buffer control (downsampled-data buffer) */ +struct jpeg_d_main_controller { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, process_data, (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +}; + +/* Coefficient buffer control */ +struct jpeg_d_coef_controller { + JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); + JMETHOD(int, consume_data, (j_decompress_ptr cinfo)); + JMETHOD(void, start_output_pass, (j_decompress_ptr cinfo)); + JMETHOD(int, decompress_data, (j_decompress_ptr cinfo, + JSAMPIMAGE output_buf)); + /* Pointer to array of coefficient virtual arrays, or NULL if none */ + jvirt_barray_ptr *coef_arrays; +}; + +/* Decompression postprocessing (color quantization buffer control) */ +struct jpeg_d_post_controller { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, post_process_data, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +}; + +/* Marker reading & parsing */ +struct jpeg_marker_reader { + JMETHOD(void, reset_marker_reader, (j_decompress_ptr cinfo)); + /* Read markers until SOS or EOI. + * Returns same codes as are defined for jpeg_consume_input: + * JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + */ + JMETHOD(int, read_markers, (j_decompress_ptr cinfo)); + /* Read a restart marker --- exported for use by entropy decoder only */ + jpeg_marker_parser_method read_restart_marker; + /* Application-overridable marker processing methods */ + jpeg_marker_parser_method process_COM; + jpeg_marker_parser_method process_APPn[16]; + + /* State of marker reader --- nominally internal, but applications + * supplying COM or APPn handlers might like to know the state. + */ + boolean saw_SOI; /* found SOI? */ + boolean saw_SOF; /* found SOF? */ + int next_restart_num; /* next restart number expected (0-7) */ + unsigned int discarded_bytes; /* # of bytes skipped looking for a marker */ +}; + +/* Entropy decoding */ +struct jpeg_entropy_decoder { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(boolean, decode_mcu, (j_decompress_ptr cinfo, + JBLOCKROW *MCU_data)); +}; + +/* Inverse DCT (also performs dequantization) */ +typedef JMETHOD(void, inverse_DCT_method_ptr, + (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col)); + +struct jpeg_inverse_dct { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + /* It is useful to allow each component to have a separate IDCT method. */ + inverse_DCT_method_ptr inverse_DCT[MAX_COMPONENTS]; +}; + +/* Upsampling (note that upsampler must also call color converter) */ +struct jpeg_upsampler { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, upsample, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); + + boolean need_context_rows; /* TRUE if need rows above & below */ +}; + +/* Colorspace conversion */ +struct jpeg_color_deconverter { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, color_convert, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows)); +}; + +/* Color quantization or color precision reduction */ +struct jpeg_color_quantizer { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, boolean is_pre_scan)); + JMETHOD(void, color_quantize, (j_decompress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPARRAY output_buf, + int num_rows)); + JMETHOD(void, finish_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, new_color_map, (j_decompress_ptr cinfo)); +}; + + +/* Miscellaneous useful macros */ + +#undef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#undef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + + +/* We assume that right shift corresponds to signed division by 2 with + * rounding towards minus infinity. This is correct for typical "arithmetic + * shift" instructions that shift in copies of the sign bit. But some + * C compilers implement >> with an unsigned shift. For these machines you + * must define RIGHT_SHIFT_IS_UNSIGNED. + * RIGHT_SHIFT provides a proper signed right shift of an INT32 quantity. + * It is only applied with constant shift counts. SHIFT_TEMPS must be + * included in the variables of any routine using RIGHT_SHIFT. + */ + +#ifdef RIGHT_SHIFT_IS_UNSIGNED +#define SHIFT_TEMPS INT32 shift_temp; +#define RIGHT_SHIFT(x,shft) \ + ((shift_temp = (x)) < 0 ? \ + (shift_temp >> (shft)) | ((~((INT32) 0)) << (32-(shft))) : \ + (shift_temp >> (shft))) +#else +#define SHIFT_TEMPS +#define RIGHT_SHIFT(x,shft) ((x) >> (shft)) +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jinit_compress_master jICompress +#define jinit_c_master_control jICMaster +#define jinit_c_main_controller jICMainC +#define jinit_c_prep_controller jICPrepC +#define jinit_c_coef_controller jICCoefC +#define jinit_color_converter jICColor +#define jinit_downsampler jIDownsampler +#define jinit_forward_dct jIFDCT +#define jinit_huff_encoder jIHEncoder +#define jinit_phuff_encoder jIPHEncoder +#define jinit_marker_writer jIMWriter +#define jinit_master_decompress jIDMaster +#define jinit_d_main_controller jIDMainC +#define jinit_d_coef_controller jIDCoefC +#define jinit_d_post_controller jIDPostC +#define jinit_input_controller jIInCtlr +#define jinit_marker_reader jIMReader +#define jinit_huff_decoder jIHDecoder +#define jinit_phuff_decoder jIPHDecoder +#define jinit_inverse_dct jIIDCT +#define jinit_upsampler jIUpsampler +#define jinit_color_deconverter jIDColor +#define jinit_1pass_quantizer jI1Quant +#define jinit_2pass_quantizer jI2Quant +#define jinit_merged_upsampler jIMUpsampler +#define jinit_memory_mgr jIMemMgr +#define jdiv_round_up jDivRound +#define jround_up jRound +#define jcopy_sample_rows jCopySamples +#define jcopy_block_row jCopyBlocks +#define jzero_far jZeroFar +#define jpeg_zigzag_order jZIGTable +#define jpeg_natural_order jZAGTable +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Compression module initialization routines */ +EXTERN void jinit_compress_master JPP((j_compress_ptr cinfo)); +EXTERN void jinit_c_master_control JPP((j_compress_ptr cinfo, + boolean transcode_only)); +EXTERN void jinit_c_main_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN void jinit_c_prep_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN void jinit_c_coef_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN void jinit_color_converter JPP((j_compress_ptr cinfo)); +EXTERN void jinit_downsampler JPP((j_compress_ptr cinfo)); +EXTERN void jinit_forward_dct JPP((j_compress_ptr cinfo)); +EXTERN void jinit_huff_encoder JPP((j_compress_ptr cinfo)); +EXTERN void jinit_phuff_encoder JPP((j_compress_ptr cinfo)); +EXTERN void jinit_marker_writer JPP((j_compress_ptr cinfo)); +/* Decompression module initialization routines */ +EXTERN void jinit_master_decompress JPP((j_decompress_ptr cinfo)); +EXTERN void jinit_d_main_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN void jinit_d_coef_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN void jinit_d_post_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN void jinit_input_controller JPP((j_decompress_ptr cinfo)); +EXTERN void jinit_marker_reader JPP((j_decompress_ptr cinfo)); +EXTERN void jinit_huff_decoder JPP((j_decompress_ptr cinfo)); +EXTERN void jinit_phuff_decoder JPP((j_decompress_ptr cinfo)); +EXTERN void jinit_inverse_dct JPP((j_decompress_ptr cinfo)); +EXTERN void jinit_upsampler JPP((j_decompress_ptr cinfo)); +EXTERN void jinit_color_deconverter JPP((j_decompress_ptr cinfo)); +EXTERN void jinit_1pass_quantizer JPP((j_decompress_ptr cinfo)); +EXTERN void jinit_2pass_quantizer JPP((j_decompress_ptr cinfo)); +EXTERN void jinit_merged_upsampler JPP((j_decompress_ptr cinfo)); +/* Memory manager initialization */ +EXTERN void jinit_memory_mgr JPP((j_common_ptr cinfo)); + +/* Utility routines in jutils.c */ +EXTERN long jdiv_round_up JPP((long a, long b)); +EXTERN long jround_up JPP((long a, long b)); +EXTERN void jcopy_sample_rows JPP((JSAMPARRAY input_array, int source_row, + JSAMPARRAY output_array, int dest_row, + int num_rows, JDIMENSION num_cols)); +EXTERN void jcopy_block_row JPP((JBLOCKROW input_row, JBLOCKROW output_row, + JDIMENSION num_blocks)); +EXTERN void jzero_far JPP((void FAR * target, size_t bytestozero)); +/* Constant tables in jutils.c */ +extern const int jpeg_zigzag_order[]; /* natural coef order to zigzag order */ +extern const int jpeg_natural_order[]; /* zigzag coef order to natural order */ + +/* Suppress undefined-structure complaints if necessary. */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef AM_MEMORY_MANAGER /* only jmemmgr.c defines these */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +#endif +#endif /* INCOMPLETE_TYPES_BROKEN */ diff --git a/utils/Radiant/jpeg-6/jpeglib.h b/utils/Radiant/jpeg-6/jpeglib.h new file mode 100644 index 0000000..1555a64 --- /dev/null +++ b/utils/Radiant/jpeg-6/jpeglib.h @@ -0,0 +1,1078 @@ +/* + * jpeglib.h + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the application interface for the JPEG library. + * Most applications using the library need only include this file, + * and perhaps jerror.h if they want to know the exact error codes. + */ + +#ifndef JPEGLIB_H +#define JPEGLIB_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + + +//#include "..\renderer\tr_local.h" +// stefix +#ifndef boolean +typedef unsigned char boolean; +typedef int INT32;//, *PINT32; +#endif + +#ifdef __MACOS__ +// JDC: stuff to make mac version compile +#define boolean qboolean +#define register +#define INT32 int + +#endif + +/* + * First we include the configuration files that record how this + * installation of the JPEG library is set up. jconfig.h can be + * generated automatically for many systems. jmorecfg.h contains + * manual configuration options that most people need not worry about. + */ + +#ifndef JCONFIG_INCLUDED /* in case jinclude.h already did */ +#include "jconfig.h" /* widely used configuration options */ +#endif +#include "jmorecfg.h" /* seldom changed options */ + + +/* Version ID for the JPEG library. + * Might be useful for tests like "#if JPEG_LIB_VERSION >= 60". + */ + +#define JPEG_LIB_VERSION 60 /* Version 6 */ + + +/* Various constants determining the sizes of things. + * All of these are specified by the JPEG standard, so don't change them + * if you want to be compatible. + */ + +#define DCTSIZE 8 /* The basic DCT block is 8x8 samples */ +#define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ +#define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ +#define NUM_HUFF_TBLS 4 /* Huffman tables are numbered 0..3 */ +#define NUM_ARITH_TBLS 16 /* Arith-coding tables are numbered 0..15 */ +#define MAX_COMPS_IN_SCAN 4 /* JPEG limit on # of components in one scan */ +#define MAX_SAMP_FACTOR 4 /* JPEG limit on sampling factors */ +/* Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; + * the PostScript DCT filter can emit files with many more than 10 blocks/MCU. + * If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU + * to handle it. We even let you do this from the jconfig.h file. However, + * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe + * sometimes emits noncompliant files doesn't mean you should too. + */ +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on blocks per MCU */ +#ifndef D_MAX_BLOCKS_IN_MCU +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on blocks per MCU */ +#endif + + +/* This macro is used to declare a "method", that is, a function pointer. + * We want to supply prototype parameters if the compiler can cope. + * Note that the arglist parameter must be parenthesized! + */ + +#ifdef HAVE_PROTOTYPES +#define JMETHOD(type,methodname,arglist) type (*methodname) arglist +#else +#define JMETHOD(type,methodname,arglist) type (*methodname) () +#endif + + +/* Data structures for images (arrays of samples and of DCT coefficients). + * On 80x86 machines, the image arrays are too big for near pointers, + * but the pointer arrays can fit in near memory. + */ + +typedef JSAMPLE FAR *JSAMPROW; /* ptr to one image row of pixel samples. */ +typedef JSAMPROW *JSAMPARRAY; /* ptr to some rows (a 2-D sample array) */ +typedef JSAMPARRAY *JSAMPIMAGE; /* a 3-D sample array: top index is color */ + +typedef JCOEF JBLOCK[DCTSIZE2]; /* one block of coefficients */ +typedef JBLOCK FAR *JBLOCKROW; /* pointer to one row of coefficient blocks */ +typedef JBLOCKROW *JBLOCKARRAY; /* a 2-D array of coefficient blocks */ +typedef JBLOCKARRAY *JBLOCKIMAGE; /* a 3-D array of coefficient blocks */ + +typedef JCOEF FAR *JCOEFPTR; /* useful in a couple of places */ + + +/* Types for JPEG compression parameters and working tables. */ + + +/* DCT coefficient quantization tables. */ + +typedef struct { + /* This field directly represents the contents of a JPEG DQT marker. + * Note: the values are always given in zigzag order. + */ + UINT16 quantval[DCTSIZE2]; /* quantization step for each coefficient */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JQUANT_TBL; + + +/* Huffman coding tables. */ + +typedef struct { + /* These two fields directly represent the contents of a JPEG DHT marker */ + UINT8 bits[17]; /* bits[k] = # of symbols with codes of */ + /* length k bits; bits[0] is unused */ + UINT8 huffval[256]; /* The symbols, in order of incr code length */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JHUFF_TBL; + + +/* Basic info about one component (color channel). */ + +typedef struct { + /* These values are fixed over the whole image. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOF marker. */ + int component_id; /* identifier for this component (0..255) */ + int component_index; /* its index in SOF or cinfo->comp_info[] */ + int h_samp_factor; /* horizontal sampling factor (1..4) */ + int v_samp_factor; /* vertical sampling factor (1..4) */ + int quant_tbl_no; /* quantization table selector (0..3) */ + /* These values may vary between scans. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOS marker. */ + /* The decompressor output side may not use these variables. */ + int dc_tbl_no; /* DC entropy table selector (0..3) */ + int ac_tbl_no; /* AC entropy table selector (0..3) */ + + /* Remaining fields should be treated as private by applications. */ + + /* These values are computed during compression or decompression startup: */ + /* Component's size in DCT blocks. + * Any dummy blocks added to complete an MCU are not counted; therefore + * these values do not depend on whether a scan is interleaved or not. + */ + JDIMENSION width_in_blocks; + JDIMENSION height_in_blocks; + /* Size of a DCT block in samples. Always DCTSIZE for compression. + * For decompression this is the size of the output from one DCT block, + * reflecting any scaling we choose to apply during the IDCT step. + * Values of 1,2,4,8 are likely to be supported. Note that different + * components may receive different IDCT scalings. + */ + int DCT_scaled_size; + /* The downsampled dimensions are the component's actual, unpadded number + * of samples at the main buffer (preprocessing/compression interface), thus + * downsampled_width = ceil(image_width * Hi/Hmax) + * and similarly for height. For decompression, IDCT scaling is included, so + * downsampled_width = ceil(image_width * Hi/Hmax * DCT_scaled_size/DCTSIZE) + */ + JDIMENSION downsampled_width; /* actual width in samples */ + JDIMENSION downsampled_height; /* actual height in samples */ + /* This flag is used only for decompression. In cases where some of the + * components will be ignored (eg grayscale output from YCbCr image), + * we can skip most computations for the unused components. + */ + boolean component_needed; /* do we need the value of this component? */ + + /* These values are computed before starting a scan of the component. */ + /* The decompressor output side may not use these variables. */ + int MCU_width; /* number of blocks per MCU, horizontally */ + int MCU_height; /* number of blocks per MCU, vertically */ + int MCU_blocks; /* MCU_width * MCU_height */ + int MCU_sample_width; /* MCU width in samples, MCU_width*DCT_scaled_size */ + int last_col_width; /* # of non-dummy blocks across in last MCU */ + int last_row_height; /* # of non-dummy blocks down in last MCU */ + + /* Saved quantization table for component; NULL if none yet saved. + * See jdinput.c comments about the need for this information. + * This field is not currently used by the compressor. + */ + JQUANT_TBL * quant_table; + + /* Private per-component storage for DCT or IDCT subsystem. */ + void * dct_table; +} jpeg_component_info; + + +/* The script for encoding a multiple-scan file is an array of these: */ + +typedef struct { + int comps_in_scan; /* number of components encoded in this scan */ + int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ + int Ss, Se; /* progressive JPEG spectral selection parms */ + int Ah, Al; /* progressive JPEG successive approx. parms */ +} jpeg_scan_info; + + +/* Known color spaces. */ + +typedef enum { + JCS_UNKNOWN, /* error/unspecified */ + JCS_GRAYSCALE, /* monochrome */ + JCS_RGB, /* red/green/blue */ + JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */ + JCS_CMYK, /* C/M/Y/K */ + JCS_YCCK /* Y/Cb/Cr/K */ +} J_COLOR_SPACE; + +/* DCT/IDCT algorithm options. */ + +typedef enum { + JDCT_ISLOW, /* slow but accurate integer algorithm */ + JDCT_IFAST, /* faster, less accurate integer method */ + JDCT_FLOAT /* floating-point: accurate, fast on fast HW */ +} J_DCT_METHOD; + +#ifndef JDCT_DEFAULT /* may be overridden in jconfig.h */ +#define JDCT_DEFAULT JDCT_ISLOW +#endif +#ifndef JDCT_FASTEST /* may be overridden in jconfig.h */ +#define JDCT_FASTEST JDCT_IFAST +#endif + +/* Dithering options for decompression. */ + +typedef enum { + JDITHER_NONE, /* no dithering */ + JDITHER_ORDERED, /* simple ordered dither */ + JDITHER_FS /* Floyd-Steinberg error diffusion dither */ +} J_DITHER_MODE; + + +/* Common fields between JPEG compression and decompression master structs. */ + +#define jpeg_common_fields \ + struct jpeg_error_mgr * err; /* Error handler module */\ + struct jpeg_memory_mgr * mem; /* Memory manager module */\ + struct jpeg_progress_mgr * progress; /* Progress monitor, or NULL if none */\ + boolean is_decompressor; /* so common code can tell which is which */\ + int global_state /* for checking call sequence validity */ + +/* Routines that are to be used by both halves of the library are declared + * to receive a pointer to this structure. There are no actual instances of + * jpeg_common_struct, only of jpeg_compress_struct and jpeg_decompress_struct. + */ +struct jpeg_common_struct { + jpeg_common_fields; /* Fields common to both master struct types */ + /* Additional fields follow in an actual jpeg_compress_struct or + * jpeg_decompress_struct. All three structs must agree on these + * initial fields! (This would be a lot cleaner in C++.) + */ +}; + +typedef struct jpeg_common_struct * j_common_ptr; +typedef struct jpeg_compress_struct * j_compress_ptr; +typedef struct jpeg_decompress_struct * j_decompress_ptr; + + +/* Master record for a compression instance */ + +struct jpeg_compress_struct { + jpeg_common_fields; /* Fields shared with jpeg_decompress_struct */ + + /* Destination for compressed data */ + struct jpeg_destination_mgr * dest; + + /* Description of source image --- these fields must be filled in by + * outer application before starting compression. in_color_space must + * be correct before you can even call jpeg_set_defaults(). + */ + + JDIMENSION image_width; /* input image width */ + JDIMENSION image_height; /* input image height */ + int input_components; /* # of color components in input image */ + J_COLOR_SPACE in_color_space; /* colorspace of input image */ + + double input_gamma; /* image gamma of input image */ + + /* Compression parameters --- these fields must be set before calling + * jpeg_start_compress(). We recommend calling jpeg_set_defaults() to + * initialize everything to reasonable defaults, then changing anything + * the application specifically wants to change. That way you won't get + * burnt when new parameters are added. Also note that there are several + * helper routines to simplify changing parameters. + */ + + int data_precision; /* bits of precision in image data */ + + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + int num_scans; /* # of entries in scan_info array */ + const jpeg_scan_info * scan_info; /* script for multi-scan file, or NULL */ + /* The default value of scan_info is NULL, which causes a single-scan + * sequential JPEG file to be emitted. To create a multi-scan file, + * set num_scans and scan_info to point to an array of scan definitions. + */ + + boolean raw_data_in; /* TRUE=caller supplies downsampled data */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + boolean optimize_coding; /* TRUE=optimize entropy encoding parms */ + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + int smoothing_factor; /* 1..100, or 0 for no input smoothing */ + J_DCT_METHOD dct_method; /* DCT algorithm selector */ + + /* The restart interval can be specified in absolute MCUs by setting + * restart_interval, or in MCU rows by setting restart_in_rows + * (in which case the correct restart_interval will be figured + * for each scan). + */ + unsigned int restart_interval; /* MCUs per restart, or 0 for no restart */ + int restart_in_rows; /* if > 0, MCU rows per restart interval */ + + /* Parameters controlling emission of special markers. */ + + boolean write_JFIF_header; /* should a JFIF marker be written? */ + /* These three values are not used by the JPEG code, merely copied */ + /* into the JFIF APP0 marker. density_unit can be 0 for unknown, */ + /* 1 for dots/inch, or 2 for dots/cm. Note that the pixel aspect */ + /* ratio is defined by X_density/Y_density even when density_unit=0. */ + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean write_Adobe_marker; /* should an Adobe marker be written? */ + + /* State variable: index of next scanline to be written to + * jpeg_write_scanlines(). Application may use this to control its + * processing loop, e.g., "while (next_scanline < image_height)". + */ + + JDIMENSION next_scanline; /* 0 .. image_height-1 */ + + /* Remaining fields are known throughout compressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during compression startup + */ + boolean progressive_mode; /* TRUE if scan script uses progressive mode */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coef ctlr */ + /* The coefficient controller receives data in units of MCU rows as defined + * for fully interleaved scans (whether the JPEG file is interleaved or not). + * There are v_samp_factor * DCTSIZE sample rows of each component in an + * "iMCU" (interleaved MCU) row. + */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[C_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* + * Links to compression subobjects (methods and private variables of modules) + */ + struct jpeg_comp_master * master; + struct jpeg_c_main_controller * main; + struct jpeg_c_prep_controller * prep; + struct jpeg_c_coef_controller * coef; + struct jpeg_marker_writer * marker; + struct jpeg_color_converter * cconvert; + struct jpeg_downsampler * downsample; + struct jpeg_forward_dct * fdct; + struct jpeg_entropy_encoder * entropy; +}; + + +/* Master record for a decompression instance */ + +struct jpeg_decompress_struct { + jpeg_common_fields; /* Fields shared with jpeg_compress_struct */ + + /* Source of compressed data */ + struct jpeg_source_mgr * src; + + /* Basic description of image --- filled in by jpeg_read_header(). */ + /* Application may inspect these values to decide how to process image. */ + + JDIMENSION image_width; /* nominal image width (from SOF marker) */ + JDIMENSION image_height; /* nominal image height */ + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + /* Decompression processing parameters --- these fields must be set before + * calling jpeg_start_decompress(). Note that jpeg_read_header() initializes + * them to default values. + */ + + J_COLOR_SPACE out_color_space; /* colorspace for output */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + double output_gamma; /* image gamma wanted in output */ + + boolean buffered_image; /* TRUE=multiple output passes */ + boolean raw_data_out; /* TRUE=downsampled data wanted */ + + J_DCT_METHOD dct_method; /* IDCT algorithm selector */ + boolean do_fancy_upsampling; /* TRUE=apply fancy upsampling */ + boolean do_block_smoothing; /* TRUE=apply interblock smoothing */ + + boolean quantize_colors; /* TRUE=colormapped output wanted */ + /* the following are ignored if not quantize_colors: */ + J_DITHER_MODE dither_mode; /* type of color dithering to use */ + boolean two_pass_quantize; /* TRUE=use two-pass color quantization */ + int desired_number_of_colors; /* max # colors to use in created colormap */ + /* these are significant only in buffered-image mode: */ + boolean enable_1pass_quant; /* enable future use of 1-pass quantizer */ + boolean enable_external_quant;/* enable future use of external colormap */ + boolean enable_2pass_quant; /* enable future use of 2-pass quantizer */ + + /* Description of actual output image that will be returned to application. + * These fields are computed by jpeg_start_decompress(). + * You can also use jpeg_calc_output_dimensions() to determine these values + * in advance of calling jpeg_start_decompress(). + */ + + JDIMENSION output_width; /* scaled image width */ + JDIMENSION output_height; /* scaled image height */ + int out_color_components; /* # of color components in out_color_space */ + int output_components; /* # of color components returned */ + /* output_components is 1 (a colormap index) when quantizing colors; + * otherwise it equals out_color_components. + */ + int rec_outbuf_height; /* min recommended height of scanline buffer */ + /* If the buffer passed to jpeg_read_scanlines() is less than this many rows + * high, space and time will be wasted due to unnecessary data copying. + * Usually rec_outbuf_height will be 1 or 2, at most 4. + */ + + /* When quantizing colors, the output colormap is described by these fields. + * The application can supply a colormap by setting colormap non-NULL before + * calling jpeg_start_decompress; otherwise a colormap is created during + * jpeg_start_decompress or jpeg_start_output. + * The map has out_color_components rows and actual_number_of_colors columns. + */ + int actual_number_of_colors; /* number of entries in use */ + JSAMPARRAY colormap; /* The color map as a 2-D pixel array */ + + /* State variables: these variables indicate the progress of decompression. + * The application may examine these but must not modify them. + */ + + /* Row index of next scanline to be read from jpeg_read_scanlines(). + * Application may use this to control its processing loop, e.g., + * "while (output_scanline < output_height)". + */ + JDIMENSION output_scanline; /* 0 .. output_height-1 */ + + /* Current input scan number and number of iMCU rows completed in scan. + * These indicate the progress of the decompressor input side. + */ + int input_scan_number; /* Number of SOS markers seen so far */ + JDIMENSION input_iMCU_row; /* Number of iMCU rows completed */ + + /* The "output scan number" is the notional scan being displayed by the + * output side. The decompressor will not allow output scan/row number + * to get ahead of input scan/row, but it can fall arbitrarily far behind. + */ + int output_scan_number; /* Nominal scan number being displayed */ + JDIMENSION output_iMCU_row; /* Number of iMCU rows read */ + + /* Current progression status. coef_bits[c][i] indicates the precision + * with which component c's DCT coefficient i (in zigzag order) is known. + * It is -1 when no data has yet been received, otherwise it is the point + * transform (shift) value for the most recent scan of the coefficient + * (thus, 0 at completion of the progression). + * This pointer is NULL when reading a non-progressive file. + */ + int (*coef_bits)[DCTSIZE2]; /* -1 or current Al value for each coef */ + + /* Internal JPEG parameters --- the application usually need not look at + * these fields. Note that the decompressor output side may not use + * any parameters that can change between scans. + */ + + /* Quantization and Huffman tables are carried forward across input + * datastreams when processing abbreviated JPEG datastreams. + */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + /* These parameters are never carried across datastreams, since they + * are given in SOF/SOS markers or defined to be reset by SOI. + */ + + int data_precision; /* bits of precision in image data */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + boolean progressive_mode; /* TRUE if SOFn specifies progressive mode */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + unsigned int restart_interval; /* MCUs per restart interval, or 0 for no restart */ + + /* These fields record data obtained from optional markers recognized by + * the JPEG library. + */ + boolean saw_JFIF_marker; /* TRUE iff a JFIF APP0 marker was found */ + /* Data copied from JFIF marker: */ + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean saw_Adobe_marker; /* TRUE iff an Adobe APP14 marker was found */ + UINT8 Adobe_transform; /* Color transform code from Adobe marker */ + + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + + /* Remaining fields are known throughout decompressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during decompression startup + */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_scaled_size; /* smallest DCT_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ + /* The coefficient controller's input and output progress is measured in + * units of "iMCU" (interleaved MCU) rows. These are the same as MCU rows + * in fully interleaved JPEG scans, but are used whether the scan is + * interleaved or not. We define an iMCU row as v_samp_factor DCT block + * rows of each component. Therefore, the IDCT output contains + * v_samp_factor*DCT_scaled_size sample rows of a component per iMCU row. + */ + + JSAMPLE * sample_range_limit; /* table for fast range-limiting */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + * Note that the decompressor output side must not use these fields. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[D_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* This field is shared between entropy decoder and marker parser. + * It is either zero or the code of a JPEG marker that has been + * read from the data source, but has not yet been processed. + */ + int unread_marker; + + /* + * Links to decompression subobjects (methods, private variables of modules) + */ + struct jpeg_decomp_master * master; + struct jpeg_d_main_controller * main; + struct jpeg_d_coef_controller * coef; + struct jpeg_d_post_controller * post; + struct jpeg_input_controller * inputctl; + struct jpeg_marker_reader * marker; + struct jpeg_entropy_decoder * entropy; + struct jpeg_inverse_dct * idct; + struct jpeg_upsampler * upsample; + struct jpeg_color_deconverter * cconvert; + struct jpeg_color_quantizer * cquantize; +}; + + +/* "Object" declarations for JPEG modules that may be supplied or called + * directly by the surrounding application. + * As with all objects in the JPEG library, these structs only define the + * publicly visible methods and state variables of a module. Additional + * private fields may exist after the public ones. + */ + + +/* Error handler object */ + +struct jpeg_error_mgr { + /* Error exit handler: does not return to caller */ + JMETHOD(void, error_exit, (j_common_ptr cinfo)); + /* Conditionally emit a trace or warning message */ + JMETHOD(void, emit_message, (j_common_ptr cinfo, int msg_level)); + /* Routine that actually outputs a trace or error message */ + JMETHOD(void, output_message, (j_common_ptr cinfo)); + /* Format a message string for the most recent JPEG error or message */ + JMETHOD(void, format_message, (j_common_ptr cinfo, char * buffer)); +#define JMSG_LENGTH_MAX 200 /* recommended size of format_message buffer */ + /* Reset error state variables at start of a new image */ + JMETHOD(void, reset_error_mgr, (j_common_ptr cinfo)); + + /* The message ID code and any parameters are saved here. + * A message can have one string parameter or up to 8 int parameters. + */ + int msg_code; +#define JMSG_STR_PARM_MAX 80 + union { + int i[8]; + char s[JMSG_STR_PARM_MAX]; + } msg_parm; + + /* Standard state variables for error facility */ + + int trace_level; /* max msg_level that will be displayed */ + + /* For recoverable corrupt-data errors, we emit a warning message, + * but keep going unless emit_message chooses to abort. emit_message + * should count warnings in num_warnings. The surrounding application + * can check for bad data by seeing if num_warnings is nonzero at the + * end of processing. + */ + long num_warnings; /* number of corrupt-data warnings */ + + /* These fields point to the table(s) of error message strings. + * An application can change the table pointer to switch to a different + * message list (typically, to change the language in which errors are + * reported). Some applications may wish to add additional error codes + * that will be handled by the JPEG library error mechanism; the second + * table pointer is used for this purpose. + * + * First table includes all errors generated by JPEG library itself. + * Error code 0 is reserved for a "no such error string" message. + */ + const char * const * jpeg_message_table; /* Library errors */ + int last_jpeg_message; /* Table contains strings 0..last_jpeg_message */ + /* Second table can be added by application (see cjpeg/djpeg for example). + * It contains strings numbered first_addon_message..last_addon_message. + */ + const char * const * addon_message_table; /* Non-library errors */ + int first_addon_message; /* code for first string in addon table */ + int last_addon_message; /* code for last string in addon table */ +}; + + +/* Progress monitor object */ + +struct jpeg_progress_mgr { + JMETHOD(void, progress_monitor, (j_common_ptr cinfo)); + + long pass_counter; /* work units completed in this pass */ + long pass_limit; /* total number of work units in this pass */ + int completed_passes; /* passes completed so far */ + int total_passes; /* total number of passes expected */ +}; + + +/* Data destination object for compression */ + +struct jpeg_destination_mgr { + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + + JMETHOD(void, init_destination, (j_compress_ptr cinfo)); + JMETHOD(boolean, empty_output_buffer, (j_compress_ptr cinfo)); + JMETHOD(void, term_destination, (j_compress_ptr cinfo)); +}; + + +/* Data source object for decompression */ + +struct jpeg_source_mgr { + const JOCTET * next_input_byte; /* => next byte to read from buffer */ + size_t bytes_in_buffer; /* # of bytes remaining in buffer */ + + JMETHOD(void, init_source, (j_decompress_ptr cinfo)); + JMETHOD(boolean, fill_input_buffer, (j_decompress_ptr cinfo)); + JMETHOD(void, skip_input_data, (j_decompress_ptr cinfo, long num_bytes)); + JMETHOD(boolean, resync_to_restart, (j_decompress_ptr cinfo, int desired)); + JMETHOD(void, term_source, (j_decompress_ptr cinfo)); +}; + + +/* Memory manager object. + * Allocates "small" objects (a few K total), "large" objects (tens of K), + * and "really big" objects (virtual arrays with backing store if needed). + * The memory manager does not allow individual objects to be freed; rather, + * each created object is assigned to a pool, and whole pools can be freed + * at once. This is faster and more convenient than remembering exactly what + * to free, especially where malloc()/free() are not too speedy. + * NB: alloc routines never return NULL. They exit to error_exit if not + * successful. + */ + +#define JPOOL_PERMANENT 0 /* lasts until master record is destroyed */ +#define JPOOL_IMAGE 1 /* lasts until done with image/datastream */ +#define JPOOL_NUMPOOLS 2 + +typedef struct jvirt_sarray_control * jvirt_sarray_ptr; +typedef struct jvirt_barray_control * jvirt_barray_ptr; + + +struct jpeg_memory_mgr { + /* Method pointers */ + JMETHOD(void *, alloc_small, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(void FAR *, alloc_large, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(JSAMPARRAY, alloc_sarray, (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, + JDIMENSION numrows)); + JMETHOD(JBLOCKARRAY, alloc_barray, (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, + JDIMENSION numrows)); + JMETHOD(jvirt_sarray_ptr, request_virt_sarray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION samplesperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(jvirt_barray_ptr, request_virt_barray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION blocksperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(void, realize_virt_arrays, (j_common_ptr cinfo)); + JMETHOD(JSAMPARRAY, access_virt_sarray, (j_common_ptr cinfo, + jvirt_sarray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(JBLOCKARRAY, access_virt_barray, (j_common_ptr cinfo, + jvirt_barray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(void, free_pool, (j_common_ptr cinfo, int pool_id)); + JMETHOD(void, self_destruct, (j_common_ptr cinfo)); + + /* Limit on memory allocation for this JPEG object. (Note that this is + * merely advisory, not a guaranteed maximum; it only affects the space + * used for virtual-array buffers.) May be changed by outer application + * after creating the JPEG object. + */ + long max_memory_to_use; +}; + + +/* Routine signature for application-supplied marker processing methods. + * Need not pass marker code since it is stored in cinfo->unread_marker. + */ +typedef JMETHOD(boolean, jpeg_marker_parser_method, (j_decompress_ptr cinfo)); + + +/* Declarations for routines called by application. + * The JPP macro hides prototype parameters from compilers that can't cope. + * Note JPP requires double parentheses. + */ + +#ifdef HAVE_PROTOTYPES +#define JPP(arglist) arglist +#else +#define JPP(arglist) () +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. + * We shorten external names to be unique in the first six letters, which + * is good enough for all known systems. + * (If your compiler itself needs names to be unique in less than 15 + * characters, you are out of luck. Get a better compiler.) + */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_std_error jStdError +#define jpeg_create_compress jCreaCompress +#define jpeg_create_decompress jCreaDecompress +#define jpeg_destroy_compress jDestCompress +#define jpeg_destroy_decompress jDestDecompress +#define jpeg_stdio_dest jStdDest +#define jpeg_stdio_src jStdSrc +#define jpeg_set_defaults jSetDefaults +#define jpeg_set_colorspace jSetColorspace +#define jpeg_default_colorspace jDefColorspace +#define jpeg_set_quality jSetQuality +#define jpeg_set_linear_quality jSetLQuality +#define jpeg_add_quant_table jAddQuantTable +#define jpeg_quality_scaling jQualityScaling +#define jpeg_simple_progression jSimProgress +#define jpeg_suppress_tables jSuppressTables +#define jpeg_alloc_quant_table jAlcQTable +#define jpeg_alloc_huff_table jAlcHTable +#define jpeg_start_compress jStrtCompress +#define jpeg_write_scanlines jWrtScanlines +#define jpeg_finish_compress jFinCompress +#define jpeg_write_raw_data jWrtRawData +#define jpeg_write_marker jWrtMarker +#define jpeg_write_tables jWrtTables +#define jpeg_read_header jReadHeader +#define jpeg_start_decompress jStrtDecompress +#define jpeg_read_scanlines jReadScanlines +#define jpeg_finish_decompress jFinDecompress +#define jpeg_read_raw_data jReadRawData +#define jpeg_has_multiple_scans jHasMultScn +#define jpeg_start_output jStrtOutput +#define jpeg_finish_output jFinOutput +#define jpeg_input_complete jInComplete +#define jpeg_new_colormap jNewCMap +#define jpeg_consume_input jConsumeInput +#define jpeg_calc_output_dimensions jCalcDimensions +#define jpeg_set_marker_processor jSetMarker +#define jpeg_read_coefficients jReadCoefs +#define jpeg_write_coefficients jWrtCoefs +#define jpeg_copy_critical_parameters jCopyCrit +#define jpeg_abort_compress jAbrtCompress +#define jpeg_abort_decompress jAbrtDecompress +#define jpeg_abort jAbort +#define jpeg_destroy jDestroy +#define jpeg_resync_to_restart jResyncRestart +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Default error-management setup */ +EXTERN struct jpeg_error_mgr *jpeg_std_error JPP((struct jpeg_error_mgr *err)); + +/* Initialization and destruction of JPEG compression objects */ +/* NB: you must set up the error-manager BEFORE calling jpeg_create_xxx */ +EXTERN void jpeg_create_compress JPP((j_compress_ptr cinfo)); +EXTERN void jpeg_create_decompress JPP((j_decompress_ptr cinfo)); + +EXTERN void jpeg_destroy_decompress JPP((j_decompress_ptr cinfo)); + + + +/* Standard data source and destination managers: stdio streams. */ +/* Caller is responsible for opening the file before and closing after. */ +EXTERN void jpeg_stdio_dest JPP((j_compress_ptr cinfo, FILE * outfile)); +EXTERN void jpeg_stdio_src JPP((j_decompress_ptr cinfo, unsigned char *infile)); + +/* Default parameter setup for compression */ +EXTERN void jpeg_set_defaults JPP((j_compress_ptr cinfo)); +/* Compression parameter setup aids */ +EXTERN void jpeg_set_colorspace JPP((j_compress_ptr cinfo, + J_COLOR_SPACE colorspace)); +EXTERN void jpeg_default_colorspace JPP((j_compress_ptr cinfo)); +EXTERN void jpeg_set_quality JPP((j_compress_ptr cinfo, int quality, + boolean force_baseline)); +EXTERN void jpeg_set_linear_quality JPP((j_compress_ptr cinfo, + int scale_factor, + boolean force_baseline)); +EXTERN void jpeg_add_quant_table JPP((j_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, + boolean force_baseline)); +EXTERN int jpeg_quality_scaling JPP((int quality)); +EXTERN void jpeg_simple_progression JPP((j_compress_ptr cinfo)); +EXTERN void jpeg_suppress_tables JPP((j_compress_ptr cinfo, + boolean suppress)); +EXTERN JQUANT_TBL * jpeg_alloc_quant_table JPP((j_common_ptr cinfo)); +EXTERN JHUFF_TBL * jpeg_alloc_huff_table JPP((j_common_ptr cinfo)); + +/* Main entry points for compression */ +EXTERN void jpeg_start_compress JPP((j_compress_ptr cinfo, + boolean write_all_tables)); +EXTERN JDIMENSION jpeg_write_scanlines JPP((j_compress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION num_lines)); +EXTERN void jpeg_finish_compress JPP((j_compress_ptr cinfo)); + +/* Replaces jpeg_write_scanlines when writing raw downsampled data. */ +EXTERN JDIMENSION jpeg_write_raw_data JPP((j_compress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION num_lines)); + +/* Write a special marker. See libjpeg.doc concerning safe usage. */ +EXTERN void jpeg_write_marker JPP((j_compress_ptr cinfo, int marker, + const JOCTET *dataptr, unsigned int datalen)); + +/* Alternate compression function: just write an abbreviated table file */ +EXTERN void jpeg_write_tables JPP((j_compress_ptr cinfo)); + +/* Decompression startup: read start of JPEG datastream to see what's there */ +EXTERN int jpeg_read_header JPP((j_decompress_ptr cinfo, + boolean require_image)); +/* Return value is one of: */ +#define JPEG_SUSPENDED 0 /* Suspended due to lack of input data */ +#define JPEG_HEADER_OK 1 /* Found valid image datastream */ +#define JPEG_HEADER_TABLES_ONLY 2 /* Found valid table-specs-only datastream */ +/* If you pass require_image = TRUE (normal case), you need not check for + * a TABLES_ONLY return code; an abbreviated file will cause an error exit. + * JPEG_SUSPENDED is only possible if you use a data source module that can + * give a suspension return (the stdio source module doesn't). + */ + +/* Main entry points for decompression */ +EXTERN boolean jpeg_start_decompress JPP((j_decompress_ptr cinfo)); +EXTERN JDIMENSION jpeg_read_scanlines JPP((j_decompress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION max_lines)); +EXTERN boolean jpeg_finish_decompress JPP((j_decompress_ptr cinfo)); + +/* Replaces jpeg_read_scanlines when reading raw downsampled data. */ +EXTERN JDIMENSION jpeg_read_raw_data JPP((j_decompress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION max_lines)); + +/* Additional entry points for buffered-image mode. */ +EXTERN boolean jpeg_has_multiple_scans JPP((j_decompress_ptr cinfo)); +EXTERN boolean jpeg_start_output JPP((j_decompress_ptr cinfo, + int scan_number)); +EXTERN boolean jpeg_finish_output JPP((j_decompress_ptr cinfo)); +EXTERN boolean jpeg_input_complete JPP((j_decompress_ptr cinfo)); +EXTERN void jpeg_new_colormap JPP((j_decompress_ptr cinfo)); +EXTERN int jpeg_consume_input JPP((j_decompress_ptr cinfo)); +/* Return value is one of: */ +/* #define JPEG_SUSPENDED 0 Suspended due to lack of input data */ +#define JPEG_REACHED_SOS 1 /* Reached start of new scan */ +#define JPEG_REACHED_EOI 2 /* Reached end of image */ +#define JPEG_ROW_COMPLETED 3 /* Completed one iMCU row */ +#define JPEG_SCAN_COMPLETED 4 /* Completed last iMCU row of a scan */ + +/* Precalculate output dimensions for current decompression parameters. */ +EXTERN void jpeg_calc_output_dimensions JPP((j_decompress_ptr cinfo)); + +/* Install a special processing method for COM or APPn markers. */ +EXTERN void jpeg_set_marker_processor JPP((j_decompress_ptr cinfo, + int marker_code, + jpeg_marker_parser_method routine)); + +/* Read or write raw DCT coefficients --- useful for lossless transcoding. */ +EXTERN jvirt_barray_ptr * jpeg_read_coefficients JPP((j_decompress_ptr cinfo)); +EXTERN void jpeg_write_coefficients JPP((j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays)); +EXTERN void jpeg_copy_critical_parameters JPP((j_decompress_ptr srcinfo, + j_compress_ptr dstinfo)); + +/* If you choose to abort compression or decompression before completing + * jpeg_finish_(de)compress, then you need to clean up to release memory, + * temporary files, etc. You can just call jpeg_destroy_(de)compress + * if you're done with the JPEG object, but if you want to clean it up and + * reuse it, call this: + */ +EXTERN void jpeg_abort_compress JPP((j_compress_ptr cinfo)); +EXTERN void jpeg_abort_decompress JPP((j_decompress_ptr cinfo)); + +/* Generic versions of jpeg_abort and jpeg_destroy that work on either + * flavor of JPEG object. These may be more convenient in some places. + */ +EXTERN void jpeg_abort JPP((j_common_ptr cinfo)); +EXTERN void jpeg_destroy JPP((j_common_ptr cinfo)); + +/* Default restart-marker-resync procedure for use by data source modules */ +EXTERN boolean jpeg_resync_to_restart JPP((j_decompress_ptr cinfo, + int desired)); + + +/* These marker codes are exported since applications and data source modules + * are likely to want to use them. + */ + +#define JPEG_RST0 0xD0 /* RST0 marker code */ +#define JPEG_EOI 0xD9 /* EOI marker code */ +#define JPEG_APP0 0xE0 /* APP0 marker code */ +#define JPEG_COM 0xFE /* COM marker code */ + + +/* If we have a brain-damaged compiler that emits warnings (or worse, errors) + * for structure definitions that are never filled in, keep it quiet by + * supplying dummy definitions for the various substructures. + */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef JPEG_INTERNALS /* will be defined in jpegint.h */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +struct jpeg_comp_master { long dummy; }; +struct jpeg_c_main_controller { long dummy; }; +struct jpeg_c_prep_controller { long dummy; }; +struct jpeg_c_coef_controller { long dummy; }; +struct jpeg_marker_writer { long dummy; }; +struct jpeg_color_converter { long dummy; }; +struct jpeg_downsampler { long dummy; }; +struct jpeg_forward_dct { long dummy; }; +struct jpeg_entropy_encoder { long dummy; }; +struct jpeg_decomp_master { long dummy; }; +struct jpeg_d_main_controller { long dummy; }; +struct jpeg_d_coef_controller { long dummy; }; +struct jpeg_d_post_controller { long dummy; }; +struct jpeg_input_controller { long dummy; }; +struct jpeg_marker_reader { long dummy; }; +struct jpeg_entropy_decoder { long dummy; }; +struct jpeg_inverse_dct { long dummy; }; +struct jpeg_upsampler { long dummy; }; +struct jpeg_color_deconverter { long dummy; }; +struct jpeg_color_quantizer { long dummy; }; +#endif /* JPEG_INTERNALS */ +#endif /* INCOMPLETE_TYPES_BROKEN */ + + +/* + * The JPEG library modules define JPEG_INTERNALS before including this file. + * The internal structure declarations are read only when that is true. + * Applications using the library should not include jpegint.h, but may wish + * to include jerror.h. + */ + +#ifdef JPEG_INTERNALS +#include "jpegint.h" /* fetch private declarations */ +#include "jerror.h" /* fetch error codes too */ +#endif + +#ifdef __cplusplus +}; +#endif + +#endif /* JPEGLIB_H */ diff --git a/utils/Radiant/jpeg-6/jutils.c b/utils/Radiant/jpeg-6/jutils.c new file mode 100644 index 0000000..8e8dc13 --- /dev/null +++ b/utils/Radiant/jpeg-6/jutils.c @@ -0,0 +1,175 @@ +/* + * jutils.c + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains tables and miscellaneous utility routines needed + * for both compression and decompression. + * Note we prefix all global names with "j" to minimize conflicts with + * a surrounding application. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * jpeg_zigzag_order[i] is the zigzag-order position of the i'th element + * of a DCT block read in natural order (left to right, top to bottom). + */ + +const int jpeg_zigzag_order[DCTSIZE2] = { + 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63 +}; + +/* + * jpeg_natural_order[i] is the natural-order position of the i'th element + * of zigzag order. + * + * When reading corrupted data, the Huffman decoders could attempt + * to reference an entry beyond the end of this array (if the decoded + * zero run length reaches past the end of the block). To prevent + * wild stores without adding an inner-loop test, we put some extra + * "63"s after the real entries. This will cause the extra coefficient + * to be stored in location 63 of the block, not somewhere random. + * The worst case would be a run-length of 15, which means we need 16 + * fake entries. + */ + +const int jpeg_natural_order[DCTSIZE2+16] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + 63, 63, 63, 63, 63, 63, 63, 63, /* extra entries for safety in decoder */ + 63, 63, 63, 63, 63, 63, 63, 63 +}; + + +/* + * Arithmetic utilities + */ + +GLOBAL long +jdiv_round_up (long a, long b) +/* Compute a/b rounded up to next integer, ie, ceil(a/b) */ +/* Assumes a >= 0, b > 0 */ +{ + return (a + b - 1L) / b; +} + + +GLOBAL long +jround_up (long a, long b) +/* Compute a rounded up to next multiple of b, ie, ceil(a/b)*b */ +/* Assumes a >= 0, b > 0 */ +{ + a += b - 1L; + return a - (a % b); +} + + +/* On normal machines we can apply MEMCOPY() and MEMZERO() to sample arrays + * and coefficient-block arrays. This won't work on 80x86 because the arrays + * are FAR and we're assuming a small-pointer memory model. However, some + * DOS compilers provide far-pointer versions of memcpy() and memset() even + * in the small-model libraries. These will be used if USE_FMEM is defined. + * Otherwise, the routines below do it the hard way. (The performance cost + * is not all that great, because these routines aren't very heavily used.) + */ + +#ifndef NEED_FAR_POINTERS /* normal case, same as regular macros */ +#define FMEMCOPY(dest,src,size) MEMCOPY(dest,src,size) +#define FMEMZERO(target,size) MEMZERO(target,size) +#else /* 80x86 case, define if we can */ +#ifdef USE_FMEM +#define FMEMCOPY(dest,src,size) _fmemcpy((void FAR *)(dest), (const void FAR *)(src), (size_t)(size)) +#define FMEMZERO(target,size) _fmemset((void FAR *)(target), 0, (size_t)(size)) +#endif +#endif + + +GLOBAL void +jcopy_sample_rows (JSAMPARRAY input_array, int source_row, + JSAMPARRAY output_array, int dest_row, + int num_rows, JDIMENSION num_cols) +/* Copy some rows of samples from one place to another. + * num_rows rows are copied from input_array[source_row++] + * to output_array[dest_row++]; these areas may overlap for duplication. + * The source and destination arrays must be at least as wide as num_cols. + */ +{ + register JSAMPROW inptr, outptr; +#ifdef FMEMCOPY + register size_t count = (size_t) (num_cols * SIZEOF(JSAMPLE)); +#else + register JDIMENSION count; +#endif + register int row; + + input_array += source_row; + output_array += dest_row; + + for (row = num_rows; row > 0; row--) { + inptr = *input_array++; + outptr = *output_array++; +#ifdef FMEMCOPY + FMEMCOPY(outptr, inptr, count); +#else + for (count = num_cols; count > 0; count--) + *outptr++ = *inptr++; /* needn't bother with GETJSAMPLE() here */ +#endif + } +} + + +GLOBAL void +jcopy_block_row (JBLOCKROW input_row, JBLOCKROW output_row, + JDIMENSION num_blocks) +/* Copy a row of coefficient blocks from one place to another. */ +{ +#ifdef FMEMCOPY + FMEMCOPY(output_row, input_row, num_blocks * (DCTSIZE2 * SIZEOF(JCOEF))); +#else + register JCOEFPTR inptr, outptr; + register long count; + + inptr = (JCOEFPTR) input_row; + outptr = (JCOEFPTR) output_row; + for (count = (long) num_blocks * DCTSIZE2; count > 0; count--) { + *outptr++ = *inptr++; + } +#endif +} + + +GLOBAL void +jzero_far (void FAR * target, size_t bytestozero) +/* Zero out a chunk of FAR memory. */ +/* This might be sample-array data, block-array data, or alloc_large data. */ +{ +#ifdef FMEMZERO + FMEMZERO(target, bytestozero); +#else + register char FAR * ptr = (char FAR *) target; + register size_t count; + + for (count = bytestozero; count > 0; count--) { + *ptr++ = 0; + } +#endif +} diff --git a/utils/Radiant/jpeg-6/jversion.h b/utils/Radiant/jpeg-6/jversion.h new file mode 100644 index 0000000..02083ac --- /dev/null +++ b/utils/Radiant/jpeg-6/jversion.h @@ -0,0 +1,14 @@ +/* + * jversion.h + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains software version identification. + */ + + +#define JVERSION "6 2-Aug-95" + +#define JCOPYRIGHT "Copyright (C) 1995, Thomas G. Lane" diff --git a/utils/Radiant/jpgfile.cpp b/utils/Radiant/jpgfile.cpp new file mode 100644 index 0000000..bd71ccb --- /dev/null +++ b/utils/Radiant/jpgfile.cpp @@ -0,0 +1,234 @@ +// Filename:- JPGFile.cpp + +#include "stdafx.h" +#include "oddbits.h" +#include "jpgfile.h" +#include "..\libs\pakstuff.h" + +#include "jpeg-6\jpeglib.h" + +// bool return really, but needs an int to match header proto which is included by a C file, and MS C compiler +// doesn't like bool types +// +int LoadJPG( const char *filename, unsigned char **pic, int *width, int *height ) +{ + bool bReturn = true; + *pic = NULL; + + /* This struct contains the JPEG decompression parameters and pointers to + * working space (which is allocated as needed by the JPEG library). + */ + struct jpeg_decompress_struct cinfo; + /* We use our private extension JPEG error handler. + * Note that this struct must live as long as the main JPEG parameter + * struct, to avoid dangling-pointer problems. + */ + /* This struct represents a JPEG error handler. It is declared separately + * because applications often want to supply a specialized error handler + * (see the second half of this file for an example). But here we just + * take the easy way out and use the standard error handler, which will + * print a message on stderr and call exit() if compression fails. + * Note that this struct must live as long as the main JPEG parameter + * struct, to avoid dangling-pointer problems. + */ + struct jpeg_error_mgr jerr; + /* More stuff */ + JSAMPARRAY buffer; /* Output row buffer */ + int row_stride; /* physical row width in output buffer */ + unsigned char *out; + byte *fbuffer; + byte *bbuf; + + /* In this example we want to open the input file before doing anything else, + * so that the setjmp() error recovery below can assume the file is open. + * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that + * requires it in order to read binary files. + */ + +//stefix +#if 0 + ri.FS_ReadFile ( ( char * ) filename, (void **)&fbuffer); + if (!fbuffer) + return false; +#else + int iLoadedLen = LoadFile (filename, (void **)&fbuffer); + if (iLoadedLen == -1) + { + iLoadedLen = PakLoadAnyFile(filename, (void **)&fbuffer); + if (iLoadedLen == -1) + { + return false; + } + } +#endif + + + try + { + + /* Step 1: allocate and initialize JPEG decompression object */ + + /* We have to set up the error handler first, in case the initialization + * step fails. (Unlikely, but it could happen if you are out of memory.) + * This routine fills in the contents of struct jerr, and returns jerr's + * address which we place into the link field in cinfo. + */ + cinfo.err = jpeg_std_error(&jerr); + + /* Now we can initialize the JPEG decompression object. */ + jpeg_create_decompress(&cinfo); + + /* Step 2: specify data source (eg, a file) */ + + jpeg_stdio_src(&cinfo, fbuffer); + + /* Step 3: read file parameters with jpeg_read_header() */ + + (void) jpeg_read_header(&cinfo, TRUE); + /* We can ignore the return value from jpeg_read_header since + * (a) suspension is not possible with the stdio data source, and + * (b) we passed TRUE to reject a tables-only JPEG file as an error. + * See libjpeg.doc for more info. + */ + + /* Step 4: set parameters for decompression */ + + /* In this example, we don't need to change any of the defaults set by + * jpeg_read_header(), so we do nothing here. + */ + + /* Step 5: Start decompressor */ + + (void) jpeg_start_decompress(&cinfo); + /* We can ignore the return value since suspension is not possible + * with the stdio data source. + */ + + /* We may need to do some setup of our own at this point before reading + * the data. After jpeg_start_decompress() we have the correct scaled + * output image dimensions available, as well as the output colormap + * if we asked for color quantization. + * In this example, we need to make an output work buffer of the right size. + */ + /* JSAMPLEs per row in output buffer */ + row_stride = cinfo.output_width * cinfo.output_components; + + if (cinfo.output_components!=4 && cinfo.output_components!=1 ) + { + throw(va("JPG %s is unsupported color depth (%d)\n", filename, cinfo.output_components)); + } + out = (byte *)malloc(cinfo.output_width*cinfo.output_height*4); + + *pic = out; + *width = cinfo.output_width; + *height = cinfo.output_height; + + /* Step 6: while (scan lines remain to be read) */ + /* jpeg_read_scanlines(...); */ + + /* Here we use the library's state variable cinfo.output_scanline as the + * loop counter, so that we don't have to keep track ourselves. + */ + while (cinfo.output_scanline < cinfo.output_height) { + /* jpeg_read_scanlines expects an array of pointers to scanlines. + * Here the array is only one element long, but you could ask for + * more than one scanline at a time if that's more convenient. + */ + bbuf = ((out+(row_stride*cinfo.output_scanline))); + buffer = &bbuf; + (void) jpeg_read_scanlines(&cinfo, buffer, 1); + } + + // if we've just loaded a greyscale, then adjust it from 8-bit to 32bit by stretch-copying it over itself... + // (this also does the alpha stuff as well) + // + if (cinfo.output_components == 1) + { + byte *pbDest = (*pic + (cinfo.output_width * cinfo.output_height * 4))-1; + byte *pbSrc = (*pic + (cinfo.output_width * cinfo.output_height ))-1; + int iPixels = cinfo.output_width * cinfo.output_height; + + for (int i=0; i 0x80) + { + rept = (rept^0xff)+2; + b = *source++; + memset(unpacked,b,rept); + unpacked += rept; + } + else if (rept < 0x80) + { + rept++; + memcpy(unpacked,source,rept); + unpacked += rept; + source += rept; + } + else + rept = 0; // rept of 0x80 is NOP + + count += rept; + + } while (countbpwidth) + Error ("Decompression exceeded width!\n"); + + + return source; +} + + +/* +================= +LoadLBM +================= +*/ +void LoadLBM (char *filename, byte **picture, byte **palette) +{ + byte *LBMbuffer, *picbuffer, *cmapbuffer; + int y; + byte *LBM_P, *LBMEND_P; + byte *pic_p; + byte *body_p; + + int formtype,formlength; + int chunktype,chunklength; + +// qiet compiler warnings + picbuffer = NULL; + cmapbuffer = NULL; + +// +// load the LBM +// + LoadFile (filename, (void **)&LBMbuffer); + +// +// parse the LBM header +// + LBM_P = LBMbuffer; + if ( *(int *)LBMbuffer != LittleLong(FORMID) ) + Error ("No FORM ID at start of file!\n"); + + LBM_P += 4; + formlength = BigLong( *(int *)LBM_P ); + LBM_P += 4; + LBMEND_P = LBM_P + Align(formlength); + + formtype = LittleLong(*(int *)LBM_P); + + if (formtype != ILBMID && formtype != PBMID) + Error ("Unrecognized form type: %c%c%c%c\n", formtype&0xff + ,(formtype>>8)&0xff,(formtype>>16)&0xff,(formtype>>24)&0xff); + + LBM_P += 4; + +// +// parse chunks +// + + while (LBM_P < LBMEND_P) + { + chunktype = LBM_P[0] + (LBM_P[1]<<8) + (LBM_P[2]<<16) + (LBM_P[3]<<24); + LBM_P += 4; + chunklength = LBM_P[3] + (LBM_P[2]<<8) + (LBM_P[1]<<16) + (LBM_P[0]<<24); + LBM_P += 4; + + switch ( chunktype ) + { + case BMHDID: + memcpy (&bmhd,LBM_P,sizeof(bmhd)); + bmhd.w = BigShort(bmhd.w); + bmhd.h = BigShort(bmhd.h); + bmhd.x = BigShort(bmhd.x); + bmhd.y = BigShort(bmhd.y); + bmhd.pageWidth = BigShort(bmhd.pageWidth); + bmhd.pageHeight = BigShort(bmhd.pageHeight); + break; + + case CMAPID: + cmapbuffer = (unsigned char*)malloc (768); + memset (cmapbuffer, 0, 768); + memcpy (cmapbuffer, LBM_P, chunklength); + break; + + case BODYID: + body_p = LBM_P; + + pic_p = picbuffer = (unsigned char*)malloc (bmhd.w*bmhd.h); + if (formtype == PBMID) + { + // + // unpack PBM + // + for (y=0 ; ydata; + + pcx->xmin = LittleShort(pcx->xmin); + pcx->ymin = LittleShort(pcx->ymin); + pcx->xmax = LittleShort(pcx->xmax); + pcx->ymax = LittleShort(pcx->ymax); + pcx->hres = LittleShort(pcx->hres); + pcx->vres = LittleShort(pcx->vres); + pcx->bytes_per_line = LittleShort(pcx->bytes_per_line); + pcx->palette_type = LittleShort(pcx->palette_type); + + if (pcx->manufacturer != 0x0a + || pcx->version != 5 + || pcx->encoding != 1 + || pcx->bits_per_pixel != 8 + || pcx->xmax >= 640 + || pcx->ymax >= 480) + Error ("Bad pcx file %s", filename); + + if (palette) + { + *palette = (unsigned char*)malloc(768); + memcpy (*palette, (byte *)pcx + len - 768, 768); + } + + if (width) + *width = pcx->xmax+1; + if (height) + *height = pcx->ymax+1; + + if (!pic) + { + free(pcx); + return; + } + + out = (unsigned char*)malloc ( (pcx->ymax+1) * (pcx->xmax+1) ); + if (!out) + Error ("Skin_Cache: couldn't allocate"); + + *pic = out; + + pix = out; + + for (y=0 ; y<=pcx->ymax ; y++, pix += pcx->xmax+1) + { + for (x=0 ; x<=pcx->xmax ; ) + { + dataByte = *raw++; + + if((dataByte & 0xC0) == 0xC0) + { + runLength = dataByte & 0x3F; + dataByte = *raw++; + } + else + runLength = 1; + + while(runLength-- > 0) + pix[x++] = dataByte; + } + + } + + if ( raw - (byte *)pcx > len) + Error ("PCX file %s was malformed", filename); + + free (pcx); +} + +/* +============== +WritePCXfile +============== +*/ +void WritePCXfile (char *filename, byte *data, + int width, int height, byte *palette) +{ + int i, j, length; + pcx_t *pcx; + byte *pack; + + pcx = (pcx_t*)malloc (width*height*2+1000); + memset (pcx, 0, sizeof(*pcx)); + + pcx->manufacturer = 0x0a; // PCX id + pcx->version = 5; // 256 color + pcx->encoding = 1; // uncompressed + pcx->bits_per_pixel = 8; // 256 color + pcx->xmin = 0; + pcx->ymin = 0; + pcx->xmax = LittleShort((short)(width-1)); + pcx->ymax = LittleShort((short)(height-1)); + pcx->hres = LittleShort((short)width); + pcx->vres = LittleShort((short)height); + pcx->color_planes = 1; // chunky image + pcx->bytes_per_line = LittleShort((short)width); + pcx->palette_type = LittleShort(2); // not a grey scale + + // pack the image + pack = &pcx->data; + + for (i=0 ; i=0; row--) { + pixbuf = targa_rgba + row*columns*4; + for(column=0; column=0; row--) { + pixbuf = targa_rgba + row*columns*4; + for(column=0; column0) + row--; + else + goto breakOut; + pixbuf = targa_rgba + row*columns*4; + } + } + } + else { // non run-length packet + for(j=0;j0) + row--; + else + goto breakOut; + pixbuf = targa_rgba + row*columns*4; + } + } + } + } + breakOut:; + } + } + free(raw); +} + + +// tacky suff for time/date compares... +// +char gsFoundFilename[MAX_PATH]; +FILETIME gtFileTime; +bool gbFileTimeValid; +extern bool GetFileTime(LPCSTR psFileName, FILETIME &ft); +void LoadTGA (char *name, byte **pixels, int *width, int *height) +{ + // try a TGA first... + // + LoadTGA_Actual(name, pixels, width, height); + + if (*pixels) + { + strcpy(gsFoundFilename,name); + gbFileTimeValid = GetFileTime(gsFoundFilename, gtFileTime); + return; + } + +#ifdef QUAKE3 + // Trek version only: didn't find the TGA, so let's try a JPG instead... + // + char sJPGFilename[MAX_PATH]; + + strcpy(sJPGFilename,name); + strlwr(sJPGFilename); + char *p = strstr(sJPGFilename,".tga"); + if (p) + { + strcpy(p,".jpg"); + LoadJPG( sJPGFilename, pixels, width, height ); + if (*pixels) + { + strcpy(gsFoundFilename,sJPGFilename); + gbFileTimeValid = GetFileTime(gsFoundFilename, gtFileTime); + return; + } + } +#endif +} + + + diff --git a/utils/Radiant/lbmlib.h b/utils/Radiant/lbmlib.h new file mode 100644 index 0000000..02eb305 --- /dev/null +++ b/utils/Radiant/lbmlib.h @@ -0,0 +1,18 @@ +// piclib.h + + +void LoadLBM (char *filename, byte **picture, byte **palette); +void WriteLBMfile (char *filename, byte *data, int width, int height + , byte *palette); +void LoadPCX (char *filename, byte **picture, byte **palette, int *width, int *height); +void WritePCXfile (char *filename, byte *data, int width, int height + , byte *palette); + +// loads / saves either lbm or pcx, depending on extension +void Load256Image (char *name, byte **pixels, byte **palette, + int *width, int *height); +void Save256Image (char *name, byte *pixels, byte *palette, + int width, int height); + + +void LoadTGA (char *filename, byte **pixels, int *width, int *height); diff --git a/utils/Radiant/lsttoolbar.cpp b/utils/Radiant/lsttoolbar.cpp new file mode 100644 index 0000000..3c31015 --- /dev/null +++ b/utils/Radiant/lsttoolbar.cpp @@ -0,0 +1,38 @@ +// LstToolBar.cpp : implementation file +// + +#include "stdafx.h" +#include "Radiant.h" +#include "LstToolBar.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CLstToolBar + +CLstToolBar::CLstToolBar() +{ +} + +CLstToolBar::~CLstToolBar() +{ +} + + +BEGIN_MESSAGE_MAP(CLstToolBar, CToolBar) + //{{AFX_MSG_MAP(CLstToolBar) + ON_WM_PARENTNOTIFY() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CLstToolBar message handlers + +void CLstToolBar::OnParentNotify(UINT message, LPARAM lParam) +{ + CToolBar::OnParentNotify(message, lParam); +} diff --git a/utils/Radiant/lsttoolbar.h b/utils/Radiant/lsttoolbar.h new file mode 100644 index 0000000..9af327f --- /dev/null +++ b/utils/Radiant/lsttoolbar.h @@ -0,0 +1,48 @@ +#if !defined(AFX_LSTTOOLBAR_H__279AAE23_78C5_11D1_B53C_00AA00A410FC__INCLUDED_) +#define AFX_LSTTOOLBAR_H__279AAE23_78C5_11D1_B53C_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// LstToolBar.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CLstToolBar window + +class CLstToolBar : public CToolBar +{ +// Construction +public: + CLstToolBar(); + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CLstToolBar) + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CLstToolBar(); + + // Generated message map functions +protected: + //{{AFX_MSG(CLstToolBar) + afx_msg void OnParentNotify(UINT message, LPARAM lParam); + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_LSTTOOLBAR_H__279AAE23_78C5_11D1_B53C_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/mainfrm.cpp b/utils/Radiant/mainfrm.cpp new file mode 100644 index 0000000..6bfd2ca --- /dev/null +++ b/utils/Radiant/mainfrm.cpp @@ -0,0 +1,5460 @@ +// MainFrm.cpp : implementation of the CMainFrame class +// + +#include "stdafx.h" +#include "Radiant.h" +#include "qe3.h" +#include "ZWnd.h" +#include "CamWnd.h" +#include "TexWnd.h" +#include "EditWnd.h" +#include "entityw.h" +#include "PrefsDlg.h" +#include "MapInfo.h" +#include "MainFrm.h" +#include "RotateDlg.h" +#include "EntityListDlg.h" +#include "ScriptDlg.h" +#include "NewProjDlg.h" +#include "CommandsDlg.h" +#include "3DFXCamWnd.h" +#include "inc.h" +#include "ScaleDialog.h" +#include "FindTextureDlg.h" +#include "SurfaceDlg.h" +#include "shlobj.h" +#include "DialogTextures.h" +#include "PatchDensityDlg.h" +#include "DialogThick.h" +#include "oddbits.h" +#include "sourcesafe.h" +#include "sourcesafesettings.h" +#include "groupnames.h" +#include "entkeyfindreplace.h" +#include +#include "..\libs\pakstuff.h" +#include "autocaulk.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + + +// globals +CString g_strAppPath; // holds the full path of the executable +CEdit* g_pEdit = NULL; // used originally to make qe4 work with mfc.. still used +CMainFrame* g_pParentWnd = NULL; // used to precast to CMainFrame +CPrefsDlg g_Preferences; // global prefs instance +CPrefsDlg& g_PrefsDlg = g_Preferences; // reference used throughout +int g_nUpdateBits = 0; // window update flags +bool g_bScreenUpdates = true; // whether window painting is active, used in a few places + // to disable updates for speed reasons + // both of the above should be made members of CMainFrame + +bool g_bSnapToGrid = true; // early use, no longer in use, clamping pref will be used +CString g_strProject; // holds the active project filename +CString g_cstrBehavEdPath = sCHC_BEHAVED_PATH; + +#define MIN_ZOOM_SCALE 0.006f +#define MAX_ZOOM_SCALE 16.0f + + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame + +// command mapping stuff +// +// m_strCommand is the command string +// m_nKey is the windows VK_??? equivelant +// m_nModifiers are key states as follows +// bit +// 0 - shift +// 1 - alt +// 2 - control +// 4 - press only +// +#define SPEED_MOVE 32 +#define SPEED_TURN 22.5 + + +SCommandInfo g_Commands[] = +{ +// shift control brought to top to insure they are looked at first MLC + {"ShowClipBrushs", 'B', RAD_SHIFT + RAD_CONTROL, ID_VIEW_SHOWCLIP}, + {"ToggleCamera", 'C', RAD_SHIFT + RAD_CONTROL, ID_TOGGLECAMERA}, + {"ToggleDetailOnly", 'D', RAD_SHIFT + RAD_CONTROL, ID_VIEW_SHOW_DETAIL_ONLY}, + {"RedisperseCols", 'E', RAD_SHIFT + RAD_CONTROL, ID_CURVE_REDISPERSE_COLS}, + {"ShowFuncGroups", 'F', RAD_SHIFT + RAD_CONTROL, ID_VIEW_SHOWFUNCGROUPS}, + {"ShowLights", 'G', RAD_SHIFT + RAD_CONTROL, ID_VIEW_SHOWLIGHTS}, + {"ShowHintBrushs", 'H', RAD_SHIFT + RAD_CONTROL, ID_VIEW_SHOWHINT}, + {"InvertCurveTextureX", 'I', RAD_SHIFT + RAD_CONTROL, ID_CURVE_NEGATIVETEXTUREY}, + {"NextLeakSpot", 'K', RAD_SHIFT + RAD_CONTROL, ID_MISC_NEXTLEAKSPOT}, + {"PrevLeakSpot", 'L', RAD_SHIFT + RAD_CONTROL, ID_MISC_PREVIOUSLEAKSPOT}, + {"MakeDetail", 'M', RAD_SHIFT + RAD_CONTROL, ID_CURVE_MATRIX_TRANSPOSE}, + {"ShowEntities", 'N', RAD_SHIFT + RAD_CONTROL, ID_VIEW_SHOWENT}, + {"ToggleShowPatches", 'P', RAD_SHIFT + RAD_CONTROL, ID_CURVE_CYCLECAP}, + {"ToggleShowLightRadii", 'R', RAD_SHIFT + RAD_CONTROL, ID_VIEW_SHOWLIGHTRADII_KB}, + {"CycleCapTextures", 'T', RAD_SHIFT + RAD_CONTROL, ID_CURVE_CYCLECAP}, + {"ShowCurves", 'U', RAD_SHIFT + RAD_CONTROL, ID_VIEW_SHOWCURVES}, + {"ToggleView", 'V', RAD_SHIFT + RAD_CONTROL, ID_TOGGLEVIEW}, + {"ClearPatchOverlays", 'Y', RAD_SHIFT + RAD_CONTROL, ID_CURVE_OVERLAY_CLEAR}, + {"ToggleZ", 'Z', RAD_SHIFT + RAD_CONTROL, ID_TOGGLEZ}, + {"IncPatchColumn", VK_ADD, RAD_SHIFT + RAD_CONTROL, ID_CURVE_INSERTCOLUMN}, + {"DecPatchColumn", VK_SUBTRACT, RAD_SHIFT + RAD_CONTROL, ID_CURVE_DELETECOLUMN}, + {"MapHome", VK_HOME, RAD_SHIFT + RAD_CONTROL, ID_MAPHOME}, + + {"ToggleEasy", '1', RAD_SHIFT, ID_SHOW_EASY}, + {"ToggleMedium", '2', RAD_SHIFT, ID_SHOW_MEDIUM}, + {"ToggleHard", '3', RAD_SHIFT, ID_SHOW_HARD}, + {"AutoCaulk", 'A', RAD_SHIFT, ID_AUTOCAULK}, + {"DumpSelectedBrush", 'D', RAD_SHIFT, ID_SELECTION_PRINT}, + {"FaceFit", 'F', RAD_SHIFT, ID_FACEFIT}, + {"ConnectSelection", 'K', RAD_SHIFT, ID_SELECTION_CONNECTSMART}, + {"ShowHidden", 'H', RAD_SHIFT, ID_VIEW_HIDESHOW_SHOWHIDDEN}, + {"InvertCurveTextureY", 'I', RAD_SHIFT, ID_CURVE_NEGATIVETEXTUREX}, + {"ClearNonSolid", 'L', RAD_SHIFT, ID_CLEAR_NONSOLID}, + {"TogTexRotLock", 'R', RAD_SHIFT, ID_TOGGLE_ROTATELOCK}, + {"TogTexLock", 'T', RAD_SHIFT, ID_TOGGLE_LOCK}, + {"PasteNoTargetNameChange",'V', RAD_SHIFT, ID_EDIT_PASTEBRUSHNOTARGETNAMECHANGE}, + {"TextureFit", '5', RAD_SHIFT, ID_SELECTION_TEXTURE_FIT}, + {"Patch TAB", VK_TAB, RAD_SHIFT, ID_PATCH_TAB}, + {"SplitSelected", VK_RETURN, RAD_SHIFT, ID_SPLIT_SELECTED}, + {"TexDecrement", VK_SUBTRACT, RAD_SHIFT, ID_SELECTION_TEXTURE_DEC}, + {"TexIncrement", VK_ADD, RAD_SHIFT, ID_SELECTION_TEXTURE_INC}, + {"TexRotateClock", VK_NEXT, RAD_SHIFT, ID_SELECTION_TEXTURE_ROTATECLOCK}, + {"TexRotateCounter", VK_PRIOR, RAD_SHIFT, ID_SELECTION_TEXTURE_ROTATECOUNTER}, + {"TexShiftLeft", VK_LEFT, RAD_SHIFT, ID_SELECTION_TEXTURE_SHIFTLEFT}, + {"TexShiftRight", VK_RIGHT, RAD_SHIFT, ID_SELECTION_TEXTURE_SHIFTRIGHT}, + {"TexShiftUp", VK_UP, RAD_SHIFT, ID_SELECTION_TEXTURE_SHIFTUP}, + {"TexShiftDown", VK_DOWN, RAD_SHIFT, ID_SELECTION_TEXTURE_SHIFTDOWN}, + {"CubicClipZoomOut10", 219, RAD_SHIFT, ID_VIEW_CUBEOUT10}, + {"CubicClipZoomIn10", 221, RAD_SHIFT, ID_VIEW_CUBEIN10}, + + {"SelectNudgeDown", VK_DOWN, RAD_ALT, ID_SELECTION_SELECT_NUDGEDOWN}, + {"SelectNudgeLeft", VK_LEFT, RAD_ALT, ID_SELECTION_SELECT_NUDGELEFT}, + {"SelectNudgeRight", VK_RIGHT, RAD_ALT, ID_SELECTION_SELECT_NUDGERIGHT}, + {"SelectNudgeUp", VK_UP, RAD_ALT, ID_SELECTION_SELECT_NUDGEUP}, + + {"RegionSetToSelection", 'B', RAD_CONTROL, ID_REGION_SETSELECTION}, + {"Copy", 'C', RAD_CONTROL, ID_EDIT_COPYBRUSH}, + {"ShowDetail", 'D', RAD_CONTROL, ID_VIEW_SHOWDETAIL}, + {"RedisperseRows", 'E', RAD_CONTROL, ID_CURVE_REDISPERSE_ROWS}, + {"RegionOff", 'F', RAD_CONTROL, ID_REGION_OFF}, + {"SnapPatchToGrid", 'G', RAD_CONTROL, ID_SELECT_SNAPTOGRID}, + {"InvertCurve", 'I', RAD_CONTROL, ID_CURVE_NEGATIVE}, + {"ConnectSelection", 'K', RAD_CONTROL, ID_SELECTION_CONNECT}, + {"ShowNonSolid", 'L', RAD_CONTROL, ID_VIEW_SHOW_NONSOLID}, + {"MakeDetail", 'M', RAD_CONTROL, ID_SELECTION_MAKE_DETAIL}, + {"NaturalizePatch", 'N', RAD_CONTROL, ID_PATCH_NATURALIZE}, + {"FileOpen", 'O', RAD_CONTROL, ID_FILE_OPEN}, + {"ToggleShowPatches", 'P', RAD_CONTROL, ID_VIEW_SHOWCURVES}, + {"ToggleRealtime", 'R', RAD_CONTROL, ID_VIEW_CAMERAUPDATE}, + {"FileSave", 'S', RAD_CONTROL, ID_FILE_SAVE}, + {"ThickenPatch", 'T', RAD_CONTROL, ID_CURVE_THICKEN}, + {"MakeNonSolid", 'U', RAD_CONTROL, ID_MAKE_NONSOLID}, + {"Paste", 'V', RAD_CONTROL, ID_EDIT_PASTEBRUSH}, + {"WayPointToggle", 'W', RAD_CONTROL, ID_VIEW_SHOWWAYPOINTS}, + {"Exit", 'X', RAD_CONTROL, ID_FILE_EXIT}, + {"RegionSetXY", 'Y', RAD_CONTROL, ID_REGION_SETXY}, + {"Undo", 'Z', RAD_CONTROL, ID_EDIT_UNDO}, + {"Brush3Sided", '3', RAD_CONTROL, ID_BRUSH_3SIDED}, + {"Brush4Sided", '4', RAD_CONTROL, ID_BRUSH_4SIDED}, + {"Brush5Sided", '5', RAD_CONTROL, ID_BRUSH_5SIDED}, + {"Brush6Sided", '6', RAD_CONTROL, ID_BRUSH_6SIDED}, + {"Brush7Sided", '7', RAD_CONTROL, ID_BRUSH_7SIDED}, + {"Brush8Sided", '8', RAD_CONTROL, ID_BRUSH_8SIDED}, + {"Brush9Sided", '9', RAD_CONTROL, ID_BRUSH_9SIDED}, + {"IncPatchRow", VK_ADD, RAD_CONTROL, ID_CURVE_INSERTROW}, + {"DecPatchRow", VK_SUBTRACT, RAD_CONTROL, ID_CURVE_DELETEROW}, + {"NextView", VK_TAB, RAD_CONTROL, ID_VIEW_NEXTVIEW}, + {"FlipClip", VK_RETURN, RAD_CONTROL, ID_FLIP_CLIP}, + {"ZZoomOut", VK_INSERT, RAD_CONTROL, ID_VIEW_ZZOOMOUT}, + {"ZZoomIn", VK_DELETE, RAD_CONTROL, ID_VIEW_ZZOOMIN}, + {"TexScaleUp", VK_UP, RAD_CONTROL, ID_SELECTION_TEXTURE_SCALEUP}, + {"TexScaleDown", VK_DOWN, RAD_CONTROL, ID_SELECTION_TEXTURE_SCALEDOWN}, + {"TexScaleLeft", VK_LEFT, RAD_CONTROL, ID_SELECTION_TEXTURE_SCALELEFT}, + {"TexScaleRight", VK_RIGHT, RAD_CONTROL, ID_SELECTION_TEXTURE_SCALERIGHT}, + {"CubicClipZoomOut", 219, RAD_CONTROL, ID_VIEW_CUBEOUT}, + {"CubicClipZoomIn", 221, RAD_CONTROL, ID_VIEW_CUBEIN}, + {"ToggleCubicClip", '\\', RAD_CONTROL, ID_VIEW_CUBICCLIPPING}, + {"CloneSelectionNoTargetNameChange", + VK_SPACE, RAD_CONTROL, ID_SELECTION_CLONE_NOTARGETNAMECHANGE}, + {"FindOrReplaceEnt", VK_F3, RAD_CONTROL, ID_MISC_FINDENT}, + + {"ToggleSizePaint", 'Q', RAD_PRESS, ID_SELECTION_TOGGLESIZEPAINT}, + + {"FindOrReplaceNextEnt", VK_F3, 0, ID_MISC_FINDNEXTENT}, + {"CameraAngleUp", 'A', 0, ID_CAMERA_ANGLEUP}, + {"BendMode", 'B', 0, ID_PATCH_BEND}, + {"CameraDown", 'C', 0, ID_CAMERA_DOWN}, + {"CameraUp", 'D', 0, ID_CAMERA_UP}, + {"DragEdges", 'E', 0, ID_SELECTION_DRAGEDGES}, + {"HideSelected", 'H', 0, ID_VIEW_HIDESHOW_HIDESELECTED}, + {"EntityColor", 'K', 0, ID_MISC_SELECTENTITYCOLOR}, + {"EntityList", 'L', 0, ID_ENTITYLIST}, + {"MapInfo", 'M', 0, ID_MAPINFO}, + {"ViewEntityInfo", 'N', 0, ID_VIEW_ENTITY}, + {"ViewConsole", 'O', 0, ID_VIEW_CONSOLE}, + {"ToggleConsole", 'O', 0, ID_TOGGLECONSOLE}, + {"Preferences", 'P', 0, ID_PREFS}, + {"MouseRotate", 'R', 0, ID_SELECT_MOUSEROTATE}, + {"SurfaceInspector", 'S', 0, ID_TEXTURES_INSPECTOR}, + {"ViewTextures", 'T', 0, ID_VIEW_TEXTURE}, + {"DragVertices", 'V', 0, ID_SELECTION_DRAGVERTECIES}, + {"ToggleClipper", 'X', 0, ID_VIEW_CLIPPER}, + {"MakeOverlayPatch", 'Y', 0, ID_CURVE_OVERLAY_SET}, + {"CameraAngleDown", 'Z', 0, ID_CAMERA_ANGLEDOWN}, + {"ToggleGrid", '0', 0, ID_GRID_TOGGLE}, + {"SetGrid1", '1', 0, ID_GRID_1}, + {"SetGrid2", '2', 0, ID_GRID_2}, + {"SetGrid4", '3', 0, ID_GRID_4}, + {"SetGrid8", '4', 0, ID_GRID_8}, + {"SetGrid16", '5', 0, ID_GRID_16}, + {"SetGrid32", '6', 0, ID_GRID_32}, + {"SetGrid64", '7', 0, ID_GRID_64}, + {"Patch TAB", VK_TAB, 0, ID_PATCH_TAB}, + {"CameraForward", VK_UP, 0, ID_CAMERA_FORWARD}, + {"CameraBack", VK_DOWN, 0, ID_CAMERA_BACK}, + {"CameraLeft", VK_LEFT, 0, ID_CAMERA_LEFT}, + {"CameraRight", VK_RIGHT, 0, ID_CAMERA_RIGHT}, + {"CameraStrafeRight", VK_PERIOD, 0, ID_CAMERA_STRAFERIGHT}, + {"CameraStrafeLeft", VK_COMMA, 0, ID_CAMERA_STRAFELEFT}, + {"CloneSelection", VK_SPACE, 0, ID_SELECTION_CLONE}, + {"DeleteSelection", VK_BACK, 0, ID_SELECTION_DELETE}, + {"UnSelectSelection", VK_ESCAPE, 0, ID_SELECTION_DESELECT}, + {"CenterView", VK_END, 0, ID_VIEW_CENTER}, + {"ZoomOut", VK_INSERT, 0, ID_VIEW_ZOOMOUT}, + {"ZoomIn", VK_DELETE, 0, ID_VIEW_ZOOMIN}, + {"UpFloor", VK_PRIOR, 0, ID_VIEW_UPFLOOR}, + {"DownFloor", VK_NEXT, 0, ID_VIEW_DOWNFLOOR}, + {"ClipSelected", VK_RETURN, 0, ID_CLIP_SELECTED}, + {"MoveSelectionDOWN", VK_SUBTRACT, 0, ID_SELECTION_MOVEDOWN}, + {"MoveSelectionUP", VK_ADD, 0, ID_SELECTION_MOVEUP}, + {"GridDown", 219, 0, ID_GRID_PREV}, + {"GridUp", 221, 0, ID_GRID_NEXT}, + //{"ForceCameraWalk", 'Q', RAD_PRESS, ID_CAMERA_ACTIVE} +}; + +int g_nCommandCount = sizeof(g_Commands) / sizeof(SCommandInfo); + +SKeyInfo g_Keys[] = +{ + {"SPACE", VK_SPACE}, + {"BACKSPACE", VK_BACK}, + {"ESCAPE", VK_ESCAPE}, + {"END", VK_END}, + {"INSERT", VK_INSERT}, + {"DELETE", VK_DELETE}, + {"PAGEUP", VK_PRIOR}, + {"PAGEDOWN", VK_NEXT}, + {"UP", VK_UP}, + {"DOWN", VK_DOWN}, + {"LEFT", VK_LEFT}, + {"RIGHT", VK_RIGHT}, + {"F1", VK_F1}, + {"F2", VK_F2}, + {"F3", VK_F3}, + {"F4", VK_F4}, + {"F5", VK_F5}, + {"F6", VK_F6}, + {"F7", VK_F7}, + {"F8", VK_F8}, + {"F9", VK_F9}, + {"F10", VK_F10}, + {"F11", VK_F11}, + {"F12", VK_F12}, + {"TAB", VK_TAB}, + {"RETURN", VK_RETURN}, + {"COMMA", VK_COMMA}, + {"PERIOD", VK_PERIOD}, + {"PLUS", VK_ADD}, + {"MULTIPLY", VK_MULTIPLY}, + {"SUBTRACT", VK_SUBTRACT}, + {"NUMPAD0", VK_NUMPAD0}, + {"NUMPAD1", VK_NUMPAD1}, + {"NUMPAD2", VK_NUMPAD2}, + {"NUMPAD3", VK_NUMPAD3}, + {"NUMPAD4", VK_NUMPAD4}, + {"NUMPAD5", VK_NUMPAD5}, + {"NUMPAD6", VK_NUMPAD6}, + {"NUMPAD7", VK_NUMPAD7}, + {"NUMPAD8", VK_NUMPAD8}, + {"NUMPAD9", VK_NUMPAD9}, + {"[", 219}, + {"]", 221} +}; + +int g_nKeyCount = sizeof(g_Keys) / sizeof(SKeyInfo); + +const int CMD_TEXTUREWAD_END = CMD_TEXTUREWAD + 127; +const int CMD_BSPCOMMAND_END = CMD_BSPCOMMAND + 127; +const int IDMRU_END = IDMRU+NBMRUMENU; + +const int g_msgBSPDone = RegisterWindowMessage("_BSPDone"); +const int g_msgBSPStatus = RegisterWindowMessage("_BSPStatus"); + +IMPLEMENT_DYNAMIC(CMainFrame, CFrameWnd) + +BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) + //{{AFX_MSG_MAP(CMainFrame) + ON_WM_PARENTNOTIFY() + ON_WM_CREATE() + ON_WM_TIMER() + ON_WM_DESTROY() + ON_WM_CLOSE() + ON_WM_KEYDOWN() + ON_WM_SIZE() + ON_COMMAND(ID_VIEW_CAMERATOGGLE, ToggleCamera) + ON_COMMAND(ID_FILE_CLOSE, OnFileClose) + ON_COMMAND(ID_FILE_EXIT, OnFileExit) + ON_COMMAND(ID_FILE_LOADPROJECT, OnFileLoadproject) + ON_COMMAND(ID_FILE_NEW, OnFileNew) + ON_COMMAND(ID_FILE_OPEN, OnFileOpen) + ON_COMMAND(ID_FILE_POINTFILE, OnFilePointfile) + ON_COMMAND(ID_FILE_PRINT, OnFilePrint) + ON_COMMAND(ID_FILE_PRINT_PREVIEW, OnFilePrintPreview) + ON_COMMAND(ID_FILE_SAVE, OnFileSave) + ON_COMMAND(ID_FILE_SAVEAS, OnFileSaveas) + ON_COMMAND(ID_VIEW_100, OnView100) + ON_COMMAND(ID_VIEW_CENTER, OnViewCenter) + ON_COMMAND(ID_VIEW_CONSOLE, OnViewConsole) + ON_COMMAND(ID_VIEW_DOWNFLOOR, OnViewDownfloor) + ON_COMMAND(ID_VIEW_ENTITY, OnViewEntity) + ON_COMMAND(ID_VIEW_FRONT, OnViewFront) + ON_COMMAND(ID_VIEW_SHOWBLOCKS, OnViewShowblocks) + ON_COMMAND(ID_VIEW_SHOWCLIP, OnViewShowclip) + ON_COMMAND(ID_VIEW_SHOWCOORDINATES, OnViewShowcoordinates) + ON_COMMAND(ID_VIEW_SHOWDETAIL, OnViewShowdetail) + ON_COMMAND(ID_VIEW_SHOW_DETAIL_ONLY, OnViewShowDetailOnly) + ON_COMMAND(ID_SHOW_EASY, OnViewShowEasy) + ON_COMMAND(ID_SHOW_MEDIUM, OnViewShowMedium) + ON_COMMAND(ID_SHOW_HARD, OnViewShowHard) + ON_COMMAND(ID_VIEW_SHOW_NONSOLID, OnViewShowNonSolid) + ON_COMMAND(ID_MAKE_NONSOLID, OnMakeNonSolid) + ON_COMMAND(ID_CLEAR_NONSOLID, OnClearNonSolid) + ON_COMMAND(ID_VIEW_SHOWENT, OnViewShowent) + ON_COMMAND(ID_VIEW_SHOWLIGHTS, OnViewShowlights) + ON_COMMAND(ID_VIEW_SHOWNAMES, OnViewShownames) + ON_COMMAND(ID_VIEW_SHOWPATH, OnViewShowpath) + ON_COMMAND(ID_VIEW_SHOWWATER, OnViewShowwater) + ON_COMMAND(ID_VIEW_SHOWWORLD, OnViewShowworld) + ON_COMMAND(ID_VIEW_TEXTURE, OnViewTexture) + ON_COMMAND(ID_VIEW_UPFLOOR, OnViewUpfloor) + ON_COMMAND(ID_VIEW_XY, OnViewXy) + ON_COMMAND(ID_VIEW_Z100, OnViewZ100) + ON_COMMAND(ID_VIEW_ZOOMIN, OnViewZoomin) + ON_COMMAND(ID_VIEW_ZOOMOUT, OnViewZoomout) + ON_COMMAND(ID_VIEW_ZZOOMIN, OnViewZzoomin) + ON_COMMAND(ID_VIEW_ZZOOMOUT, OnViewZzoomout) + ON_COMMAND(ID_VIEW_SIDE, OnViewSide) + ON_COMMAND(ID_TEXTURES_SHOWINUSE, OnTexturesShowinuse) + ON_COMMAND(ID_TEXTURES_INSPECTOR, OnTexturesInspector) + ON_COMMAND(ID_MISC_BENCHMARK, OnMiscBenchmark) + ON_COMMAND(ID_MISC_FINDBRUSH, OnMiscFindbrush) + ON_COMMAND(ID_MISC_GAMMA, OnMiscGamma) + ON_COMMAND(ID_MISC_NEXTLEAKSPOT, OnMiscNextleakspot) + ON_COMMAND(ID_MISC_PREVIOUSLEAKSPOT, OnMiscPreviousleakspot) + ON_COMMAND(ID_MISC_PRINTXY, OnMiscPrintxy) + ON_COMMAND(ID_MISC_SELECTENTITYCOLOR, OnMiscSelectentitycolor) + ON_COMMAND(ID_TEXTUREBK, OnTexturebk) + ON_COMMAND(ID_COLORS_MAJOR, OnColorsMajor) + ON_COMMAND(ID_COLORS_MINOR, OnColorsMinor) + ON_COMMAND(ID_COLORS_XYBK, OnColorsXybk) + ON_COMMAND(ID_BRUSH_3SIDED, OnBrush3sided) + ON_COMMAND(ID_BRUSH_4SIDED, OnBrush4sided) + ON_COMMAND(ID_BRUSH_5SIDED, OnBrush5sided) + ON_COMMAND(ID_BRUSH_6SIDED, OnBrush6sided) + ON_COMMAND(ID_BRUSH_7SIDED, OnBrush7sided) + ON_COMMAND(ID_BRUSH_8SIDED, OnBrush8sided) + ON_COMMAND(ID_BRUSH_9SIDED, OnBrush9sided) + ON_COMMAND(ID_BRUSH_ARBITRARYSIDED, OnBrushArbitrarysided) + ON_COMMAND(ID_BRUSH_FLIPX, OnBrushFlipx) + ON_COMMAND(ID_BRUSH_FLIPY, OnBrushFlipy) + ON_COMMAND(ID_BRUSH_FLIPZ, OnBrushFlipz) + ON_COMMAND(ID_BRUSH_ROTATEX, OnBrushRotatex) + ON_COMMAND(ID_BRUSH_ROTATEY, OnBrushRotatey) + ON_COMMAND(ID_BRUSH_ROTATEZ, OnBrushRotatez) + ON_COMMAND(ID_REGION_OFF, OnRegionOff) + ON_COMMAND(ID_REGION_SETBRUSH, OnRegionSetbrush) + ON_COMMAND(ID_REGION_SETSELECTION, OnRegionSetselection) + ON_COMMAND(ID_REGION_SETTALLBRUSH, OnRegionSettallbrush) + ON_COMMAND(ID_REGION_SETXY, OnRegionSetxy) + ON_COMMAND(ID_SELECTION_ARBITRARYROTATION, OnSelectionArbitraryrotation) + ON_COMMAND(ID_SELECTION_CLONE, OnSelectionClone) + ON_COMMAND(ID_SELECTION_CLONE_NOTARGETNAMECHANGE, OnSelectionClone_NoTargetNameChange) + ON_COMMAND(ID_SELECTION_CONNECT, OnSelectionConnect) + ON_COMMAND(ID_SELECTION_CONNECTSMART, OnSelectionConnectSmart) + ON_COMMAND(ID_SELECTION_CSGSUBTRACT, OnSelectionCsgsubtract) + ON_COMMAND(ID_SELECTION_DELETE, OnSelectionDelete) + ON_COMMAND(ID_SELECTION_DESELECT, OnSelectionDeselect) + ON_COMMAND(ID_SELECTION_DRAGEDGES, OnSelectionDragedges) + ON_COMMAND(ID_SELECTION_DRAGVERTECIES, OnSelectionDragvertecies) + ON_COMMAND(ID_SELECTION_MAKE_DETAIL, OnSelectionMakeDetail) + ON_COMMAND(ID_SELECTION_MAKE_STRUCTURAL, OnSelectionMakeStructural) + ON_COMMAND(ID_SELECTION_HIDEWAYPOINTCHILDREN, OnSelectionHideWaypointChildren) + ON_COMMAND(ID_SELECTION_UNHIDEWAYPOINTCHILDREN, OnSelectionUnHideWaypointChildren) + ON_COMMAND(ID_SELECTION_UNHIDEALLWAYPOINTS, OnSelectionUnHideAllWaypoints) + ON_COMMAND(ID_SELECTION_MAKEHOLLOW, OnSelectionMakehollow) + ON_COMMAND(ID_SELECTION_SELECTCOMPLETETALL, OnSelectionSelectcompletetall) + ON_COMMAND(ID_SELECTION_SELECTINSIDE, OnSelectionSelectinside) + ON_COMMAND(ID_SELECTION_SELECTPARTIALTALL, OnSelectionSelectpartialtall) + ON_COMMAND(ID_SELECTION_SELECTTOUCHING, OnSelectionSelecttouching) + ON_COMMAND(ID_SELECTION_UNGROUPENTITY, OnSelectionUngroupentity) + ON_COMMAND(ID_TEXTURES_POPUP, OnTexturesPopup) + ON_COMMAND(ID_POPUP_SELECTION, OnPopupSelection) + ON_COMMAND(ID_VIEW_CHANGE, OnViewChange) + ON_COMMAND(ID_VIEW_CAMERAUPDATE, OnViewCameraupdate) + ON_UPDATE_COMMAND_UI(ID_VIEW_CAMERAUPDATE, OnUpdateViewCameraupdate) + ON_WM_SIZING() + ON_COMMAND(ID_HELP_ABOUT, OnHelpAbout) + ON_COMMAND(ID_VIEW_CLIPPER, OnViewClipper) + ON_COMMAND(ID_CAMERA_ANGLEDOWN, OnCameraAngledown) + ON_COMMAND(ID_CAMERA_ANGLEUP, OnCameraAngleup) + ON_COMMAND(ID_CAMERA_BACK, OnCameraBack) + ON_COMMAND(ID_CAMERA_DOWN, OnCameraDown) + ON_COMMAND(ID_CAMERA_FORWARD, OnCameraForward) + ON_COMMAND(ID_CAMERA_LEFT, OnCameraLeft) + ON_COMMAND(ID_CAMERA_RIGHT, OnCameraRight) + ON_COMMAND(ID_CAMERA_STRAFELEFT, OnCameraStrafeleft) + ON_COMMAND(ID_CAMERA_STRAFERIGHT, OnCameraStraferight) + ON_COMMAND(ID_CAMERA_UP, OnCameraUp) + ON_COMMAND(ID_GRID_TOGGLE, OnGridToggle) + ON_COMMAND(ID_ENTITYLIST, OnEntitylist) + ON_COMMAND(ID_MAPINFO, OnMapinfo) + ON_COMMAND(ID_PREFS, OnPrefs) + ON_COMMAND(ID_TOGGLECAMERA, OnTogglecamera) + ON_COMMAND(ID_TOGGLECONSOLE, OnToggleconsole) + ON_COMMAND(ID_TOGGLEVIEW, OnToggleview) + ON_COMMAND(ID_TOGGLEZ, OnTogglez) + ON_COMMAND(ID_TOGGLE_LOCK, OnToggleLock) + ON_COMMAND(ID_EDIT_MAPINFO, OnEditMapinfo) + ON_COMMAND(ID_EDIT_ENTITYINFO, OnEditEntityinfo) + ON_COMMAND(ID_BRUSH_SCRIPTS, OnBrushScripts) + ON_COMMAND(ID_VIEW_NEXTVIEW, OnViewNextview) + ON_COMMAND(ID_HELP_COMMANDLIST, OnHelpCommandlist) + ON_COMMAND(ID_FILE_NEWPROJECT, OnFileNewproject) + ON_WM_KEYUP() + ON_COMMAND(ID_FLIP_CLIP, OnFlipClip) + ON_COMMAND(ID_CLIP_SELECTED, OnClipSelected) + ON_COMMAND(ID_SPLIT_SELECTED, OnSplitSelected) + ON_COMMAND(ID_TOGGLEVIEW_XZ, OnToggleviewXz) + ON_COMMAND(ID_TOGGLEVIEW_YZ, OnToggleviewYz) + ON_COMMAND(ID_COLORS_BRUSH, OnColorsBrush) + ON_COMMAND(ID_COLORS_CLIPPER, OnColorsClipper) + ON_COMMAND(ID_COLORS_GRIDTEXT, OnColorsGridtext) + ON_COMMAND(ID_COLORS_SELECTEDBRUSH, OnColorsSelectedbrush) + ON_COMMAND(ID_COLORS_GRIDBLOCK, OnColorsGridblock) + ON_COMMAND(ID_COLORS_VIEWNAME, OnColorsViewname) + ON_COMMAND(ID_COLOR_SETORIGINAL, OnColorSetoriginal) + ON_COMMAND(ID_COLOR_SETQER, OnColorSetqer) + ON_COMMAND(ID_COLOR_SETBLACK, OnColorSetblack) + ON_COMMAND(ID_SNAPTOGRID, OnSnaptogrid) + ON_COMMAND(ID_SELECT_SCALE, OnSelectScale) + ON_COMMAND(ID_SELECT_MOUSEROTATE, OnSelectMouserotate) + ON_COMMAND(ID_EDIT_COPYBRUSH, OnEditCopybrush) + ON_COMMAND(ID_EDIT_PASTEBRUSH, OnEditPastebrush) + ON_COMMAND(ID_EDIT_PASTEBRUSHNOTARGETNAMECHANGE,OnEditPastebrushNoTargetnameChange) + ON_COMMAND(ID_EDIT_UNDO, OnEditUndo) + ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo) + ON_COMMAND(ID_SELECTION_TEXTURE_DEC, OnSelectionTextureDec) + ON_COMMAND(ID_SELECTION_TEXTURE_FIT, OnSelectionTextureFit) + ON_COMMAND(ID_SELECTION_TEXTURE_INC, OnSelectionTextureInc) + ON_COMMAND(ID_SELECTION_TEXTURE_ROTATECLOCK, OnSelectionTextureRotateclock) + ON_COMMAND(ID_SELECTION_TEXTURE_ROTATECOUNTER, OnSelectionTextureRotatecounter) + ON_COMMAND(ID_SELECTION_TEXTURE_SCALEDOWN, OnSelectionTextureScaledown) + ON_COMMAND(ID_SELECTION_TEXTURE_SCALEUP, OnSelectionTextureScaleup) + ON_COMMAND(ID_SELECTION_TEXTURE_SHIFTDOWN, OnSelectionTextureShiftdown) + ON_COMMAND(ID_SELECTION_TEXTURE_SHIFTLEFT, OnSelectionTextureShiftleft) + ON_COMMAND(ID_SELECTION_TEXTURE_SHIFTRIGHT, OnSelectionTextureShiftright) + ON_COMMAND(ID_SELECTION_TEXTURE_SHIFTUP, OnSelectionTextureShiftup) + ON_COMMAND(ID_GRID_NEXT, OnGridNext) + ON_COMMAND(ID_GRID_PREV, OnGridPrev) + ON_COMMAND(ID_SELECTION_TEXTURE_SCALELEFT, OnSelectionTextureScaleLeft) + ON_COMMAND(ID_SELECTION_TEXTURE_SCALERIGHT, OnSelectionTextureScaleRight) + ON_COMMAND(ID_TEXTURE_REPLACEALL, OnTextureReplaceall) + ON_COMMAND(ID_SCALELOCKX, OnScalelockx) + ON_COMMAND(ID_SCALELOCKY, OnScalelocky) + ON_COMMAND(ID_SCALELOCKZ, OnScalelockz) + ON_COMMAND(ID_SELECT_MOUSESCALE, OnSelectMousescale) + ON_COMMAND(ID_VIEW_CUBICCLIPPING, OnViewCubicclipping) + ON_COMMAND(ID_FILE_IMPORT, OnFileImport) + ON_COMMAND(ID_FILE_PROJECTSETTINGS, OnFileProjectsettings) + ON_UPDATE_COMMAND_UI(ID_FILE_IMPORT, OnUpdateFileImport) + ON_COMMAND(ID_VIEW_CUBEIN, OnViewCubein) + ON_COMMAND(ID_VIEW_CUBEOUT, OnViewCubeout) + ON_COMMAND(ID_VIEW_CUBEIN10, OnViewCubein10) + ON_COMMAND(ID_VIEW_CUBEOUT10, OnViewCubeout10) + ON_COMMAND(ID_FILE_SAVEREGION, OnFileSaveregion) + ON_UPDATE_COMMAND_UI(ID_FILE_SAVEREGION, OnUpdateFileSaveregion) + ON_COMMAND(ID_SELECTION_MOVEDOWN, OnSelectionMovedown) + ON_COMMAND(ID_SELECTION_MOVEUP, OnSelectionMoveup) + ON_COMMAND(ID_TOOLBAR_MAIN, OnToolbarMain) + ON_COMMAND(ID_TOOLBAR_TEXTURE, OnToolbarTexture) + ON_COMMAND(ID_SELECTION_PRINT, OnSelectionPrint) + ON_COMMAND(ID_SELECTION_TOGGLESIZEPAINT, OnSelectionTogglesizepaint) + ON_COMMAND(ID_BRUSH_MAKECONE, OnBrushMakecone) + ON_COMMAND(ID_TEXTURES_LOAD, OnTexturesLoad) + ON_COMMAND(ID_TOGGLE_ROTATELOCK, OnToggleRotatelock) + ON_COMMAND(ID_CURVE_BEVEL, OnCurveBevel) + ON_COMMAND(ID_CURVE_CYLINDER, OnCurveCylinder) + ON_COMMAND(ID_CURVE_EIGHTHSPHERE, OnCurveEighthsphere) + ON_COMMAND(ID_CURVE_ENDCAP, OnCurveEndcap) + ON_COMMAND(ID_CURVE_HEMISPHERE, OnCurveHemisphere) + ON_COMMAND(ID_CURVE_INVERTCURVE, OnCurveInvertcurve) + ON_COMMAND(ID_CURVE_QUARTER, OnCurveQuarter) + ON_COMMAND(ID_CURVE_SPHERE, OnCurveSphere) + ON_COMMAND(ID_FILE_IMPORTMAP, OnFileImportmap) + ON_COMMAND(ID_FILE_EXPORTMAP, OnFileExportmap) + ON_COMMAND(ID_EDIT_LOADPREFAB, OnEditLoadprefab) + ON_COMMAND(ID_VIEW_SHOWWAYPOINTS, OnViewShowWayPoints) + ON_COMMAND(ID_VIEW_SHOWWAYPOINTS_ONLY, OnViewShowWayPointsOnly) + ON_COMMAND(ID_VIEW_SHOWMISC_MODEL, OnViewShowMiscModels) + ON_COMMAND(ID_VIEW_SHOWMISC_MODEL_BREAKABLE, OnViewShowMiscModelBreakables) + ON_COMMAND(ID_VIEW_SHOWMISC_MODEL_XXXX, OnViewShowMiscModelXXXX) + ON_COMMAND(ID_VIEW_SHOWTRIGGER_XXXX, OnViewShowTriggerXXXX) + ON_COMMAND(ID_VIEW_SHOWTARGET_SPEAKER, OnViewShowTargetSpeakers) + ON_COMMAND(ID_VIEW_SHOWREF_TAGS, OnViewShowRefTags) + ON_COMMAND(ID_VIEW_SHOWCURVES, OnViewShowcurves) + ON_COMMAND(ID_SELECTION_SELECT_NUDGEDOWN, OnSelectionSelectNudgedown) + ON_COMMAND(ID_SELECTION_SELECT_NUDGELEFT, OnSelectionSelectNudgeleft) + ON_COMMAND(ID_SELECTION_SELECT_NUDGERIGHT, OnSelectionSelectNudgeright) + ON_COMMAND(ID_SELECTION_SELECT_NUDGEUP, OnSelectionSelectNudgeup) + ON_WM_SYSKEYDOWN() + ON_COMMAND(ID_TEXTURES_LOADLIST, OnTexturesLoadlist) + ON_COMMAND(ID_DONTSELECTCURVE, OnDontselectcurve) + ON_COMMAND(ID_CONVERTCURVES, OnConvertcurves) + ON_COMMAND(ID_VIEW_SHOWFACES, OnShowFaces) + ON_COMMAND(ID_DYNAMIC_LIGHTING, OnDynamicLighting) + ON_COMMAND(ID_CURVE_SIMPLEPATCHMESH, OnCurveSimplepatchmesh) + ON_COMMAND(ID_PATCH_SHOWBOUNDINGBOX, OnPatchToggleBox) + ON_COMMAND(ID_PATCH_WIREFRAME, OnPatchWireframe) + ON_COMMAND(ID_CURVE_PATCHCONE, OnCurvePatchcone) + ON_COMMAND(ID_CURVE_PATCHTUBE, OnCurvePatchtube) + ON_COMMAND(ID_PATCH_WELD, OnPatchWeld) + ON_COMMAND(ID_CURVE_PATCHBEVEL, OnCurvePatchbevel) + ON_COMMAND(ID_CURVE_PATCHENDCAP, OnCurvePatchendcap) + ON_COMMAND(ID_CURVE_PATCHINVERTEDBEVEL, OnCurvePatchinvertedbevel) + ON_COMMAND(ID_CURVE_PATCHINVERTEDENDCAP, OnCurvePatchinvertedendcap) + ON_COMMAND(ID_PATCH_DRILLDOWN, OnPatchDrilldown) + ON_COMMAND(ID_CURVE_INSERTCOLUMN, OnCurveInsertcolumn) + ON_COMMAND(ID_CURVE_INSERTROW, OnCurveInsertrow) + ON_COMMAND(ID_CURVE_DELETECOLUMN, OnCurveDeletecolumn) + ON_COMMAND(ID_MAPHOME, OnMapHome) + ON_COMMAND(ID_CURVE_DELETEROW, OnCurveDeleterow) + ON_COMMAND(ID_CURVE_INSERT_ADDCOLUMN, OnCurveInsertAddcolumn) + ON_COMMAND(ID_CURVE_INSERT_ADDROW, OnCurveInsertAddrow) + ON_COMMAND(ID_CURVE_INSERT_INSERTCOLUMN, OnCurveInsertInsertcolumn) + ON_COMMAND(ID_CURVE_INSERT_INSERTROW, OnCurveInsertInsertrow) + ON_COMMAND(ID_CURVE_NEGATIVE, OnCurveNegative) + ON_COMMAND(ID_CURVE_NEGATIVETEXTUREX, OnCurveNegativeTextureX) + ON_COMMAND(ID_CURVE_NEGATIVETEXTUREY, OnCurveNegativeTextureY) + ON_COMMAND(ID_CURVE_DELETE_FIRSTCOLUMN, OnCurveDeleteFirstcolumn) + ON_COMMAND(ID_CURVE_DELETE_FIRSTROW, OnCurveDeleteFirstrow) + ON_COMMAND(ID_CURVE_DELETE_LASTCOLUMN, OnCurveDeleteLastcolumn) + ON_COMMAND(ID_CURVE_DELETE_LASTROW, OnCurveDeleteLastrow) + ON_COMMAND(ID_PATCH_BEND, OnPatchBend) + ON_COMMAND(ID_PATCH_INSDEL, OnPatchInsdel) + ON_COMMAND(ID_PATCH_ENTER, OnPatchEnter) + ON_COMMAND(ID_PATCH_TAB, OnPatchTab) + ON_COMMAND(ID_CURVE_PATCHDENSETUBE, OnCurvePatchdensetube) + ON_COMMAND(ID_CURVE_PATCHVERYDENSETUBE, OnCurvePatchverydensetube) + ON_COMMAND(ID_CURVE_CAP, OnCurveCap) + ON_COMMAND(ID_CURVE_CAP_INVERTEDBEVEL, OnCurveCapInvertedbevel) + ON_COMMAND(ID_CURVE_CAP_INVERTEDENDCAP, OnCurveCapInvertedendcap) + ON_COMMAND(ID_CURVE_REDISPERSE_COLS, OnCurveRedisperseCols) + ON_COMMAND(ID_CURVE_REDISPERSE_ROWS, OnCurveRedisperseRows) + ON_COMMAND(ID_PATCH_NATURALIZE, OnPatchNaturalize) + ON_COMMAND(ID_SELECT_SNAPTOGRID, OnSnapToGrid) + ON_COMMAND(ID_CURVE_PATCHSQUARE, OnCurvePatchsquare) + ON_COMMAND(ID_TEXTURES_TEXTUREWINDOWSCALE_10, OnTexturesTexturewindowscale10) + ON_COMMAND(ID_TEXTURES_TEXTUREWINDOWSCALE_100, OnTexturesTexturewindowscale100) + ON_COMMAND(ID_TEXTURES_TEXTUREWINDOWSCALE_200, OnTexturesTexturewindowscale200) + ON_COMMAND(ID_TEXTURES_TEXTUREWINDOWSCALE_25, OnTexturesTexturewindowscale25) + ON_COMMAND(ID_TEXTURES_TEXTUREWINDOWSCALE_50, OnTexturesTexturewindowscale50) + ON_COMMAND(ID_TEXTURES_FLUSH, OnTexturesFlush) + ON_COMMAND(ID_CURVE_OVERLAY_CLEAR, OnCurveOverlayClear) + ON_COMMAND(ID_CURVE_OVERLAY_SET, OnCurveOverlaySet) + ON_COMMAND(ID_CURVE_THICKEN, OnCurveThicken) + ON_COMMAND(ID_CURVE_CYCLECAP, OnCurveCyclecap) + ON_COMMAND(ID_CURVE_MATRIX_TRANSPOSE, OnCurveMatrixTranspose) + ON_COMMAND(ID_TEXTURES_RELOADSHADERS, OnTexturesReloadshaders) + ON_COMMAND(ID_SHOW_ENTITIES, OnShowEntities) + ON_COMMAND(ID_VIEW_ENTITIESAS_BOUNDINGBOX, OnViewEntitiesasBoundingbox) + ON_COMMAND(ID_VIEW_ENTITIESAS_SELECTEDSKINNED, OnViewEntitiesasSelectedskinned) + ON_COMMAND(ID_VIEW_ENTITIESAS_SELECTEDWIREFRAME, OnViewEntitiesasSelectedwireframe) + ON_COMMAND(ID_VIEW_ENTITIESAS_SKINNED, OnViewEntitiesasSkinned) + ON_COMMAND(ID_VIEW_ENTITIESAS_SKINNEDANDBOXED, OnViewEntitiesasSkinnedandboxed) + ON_COMMAND(ID_VIEW_ENTITIESAS_WRITEFRAME, OnViewEntitiesasWriteframe) + ON_COMMAND(ID_PLUGINS_REFRESH, OnPluginsRefresh) + ON_COMMAND(ID_VIEW_SHOWHINT, OnViewShowhint) + ON_UPDATE_COMMAND_UI(ID_TEXTURES_SHOWINUSE, OnUpdateTexturesShowinuse) + ON_UPDATE_COMMAND_UI(ID_VIEW_SHOWFACES, OnUpdateMiscShowFaces) + ON_COMMAND(ID_SS_STATUS, OnSSStatus) + ON_COMMAND(ID_SS_ADD, OnSSAdd) + ON_COMMAND(ID_SS_CHECKIN, OnSSCheckIn) + ON_COMMAND(ID_SS_CHECKOUT, OnSSCheckOut) + ON_COMMAND(ID_SS_UNDOCHECKOUT, OnSSUndoCheckOut) + ON_COMMAND(ID_SS_HISTORY, OnSSHistory) + ON_COMMAND(ID_SS_CONFIGURE, OnSSConfigure) + ON_COMMAND(ID_MISC_FINDENT, OnFindEnt) + ON_COMMAND(ID_MISC_FINDNEXTENT, OnFindNextEnt) + ON_COMMAND(ID_FACEFIT,OnFaceFit) + ON_COMMAND(ID_VIEW_HIDESHOW_HIDESELECTED, OnViewHideshowHideselected) + ON_COMMAND(ID_VIEW_HIDESHOW_SHOWHIDDEN, OnViewHideshowShowhidden) + ON_COMMAND(ID_AUTOCAULK, OnAutocaulk) + ON_UPDATE_COMMAND_UI(ID_AUTOCAULK, OnUpdateAutocaulk) + ON_COMMAND(ID_VIEW_SHOWFUNCGROUPS, OnViewShowfuncgroups) + ON_COMMAND(ID_VIEW_SHOWLIGHTRADII, OnViewShowlightradii) + ON_COMMAND(ID_VIEW_SHOWLIGHTRADII_KB, OnViewShowlightradii_KB) + ON_COMMAND(ID_VIEW_SHOWCURVESONLY, OnViewShowcurvesonly) + //}}AFX_MSG_MAP + ON_COMMAND_RANGE(CMD_TEXTUREWAD, CMD_TEXTUREWAD_END, OnTextureWad) + ON_COMMAND_RANGE(CMD_BSPCOMMAND, CMD_BSPCOMMAND_END, OnBspCommand) + ON_COMMAND_RANGE(IDMRU, IDMRU_END, OnMru) + ON_COMMAND_RANGE(ID_VIEW_NEAREST, ID_TEXTURES_FLATSHADE, OnViewNearest) + ON_COMMAND_RANGE(ID_GRID_1, ID_GRID_64, OnGrid1) + ON_COMMAND_RANGE(ID_PLUGIN_START, ID_PLUGIN_END, OnPlugIn) + ON_REGISTERED_MESSAGE(g_msgBSPDone, OnBSPDone) + ON_REGISTERED_MESSAGE(g_msgBSPStatus, OnBSPStatus) + ON_MESSAGE(WM_DISPLAYCHANGE, OnDisplayChange) + +END_MESSAGE_MAP() + +static UINT indicators[] = +{ + ID_SEPARATOR, // status line indicator + ID_SEPARATOR, // status line indicator + ID_SEPARATOR, // status line indicator + ID_SEPARATOR, // status line indicator + ID_SEPARATOR, // status line indicator + ID_SEPARATOR, // status line indicator +}; + +void CMainFrame::OnDisplayChange(UINT wParam, long lParam) +{ + int n = wParam; +} + + +void CMainFrame::OnBSPStatus(UINT wParam, long lParam) +{ +} + +void CMainFrame::OnBSPDone(UINT wParam, long lParam) +{ + DLLBuildDone(); +} + + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame construction/destruction + +CMainFrame::CMainFrame() +{ + m_bDoLoop = false; + m_bSplittersOK = false; + g_pParentWnd = this; + m_pXYWnd = NULL; + m_pCamWnd = NULL; + m_pTexWnd = NULL; + m_pZWnd = NULL; + m_pEditWnd = NULL; + m_pYZWnd = NULL; + m_pXZWnd = NULL; + m_pActiveXY = NULL; + m_bCamPreview = true; +} + +CMainFrame::~CMainFrame() +{ +} + +void HandlePopup(CWnd* pWindow, unsigned int uId) +{ + // Get the current position of the mouse + CPoint ptMouse; + GetCursorPos(&ptMouse); + + // Load up a menu that has the options we are looking for in it + CMenu mnuPopup; + VERIFY(mnuPopup.LoadMenu(uId)); + mnuPopup.GetSubMenu(0)->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON | + TPM_RIGHTBUTTON, ptMouse.x, ptMouse.y,pWindow); + mnuPopup.DestroyMenu(); + + // Set focus back to window + pWindow->SetFocus(); +} + + +void CMainFrame::OnParentNotify(UINT message, LPARAM lParam) +{ +} + +void CMainFrame::SetButtonMenuStates() +{ + CMenu* pMenu = GetMenu(); + if (pMenu) + { + // by default all of these are checked because that's how they're defined in the menu editor + if ( !g_qeglobals.d_savedinfo.show_names ) + pMenu->CheckMenuItem(ID_VIEW_SHOWNAMES, MF_BYCOMMAND | MF_UNCHECKED); + if ( !g_qeglobals.d_savedinfo.show_coordinates ) + pMenu->CheckMenuItem(ID_VIEW_SHOWCOORDINATES, MF_BYCOMMAND | MF_UNCHECKED ); + if ( !g_qeglobals.show_blocks) + pMenu->CheckMenuItem(ID_VIEW_SHOWBLOCKS, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_LIGHTS ) + pMenu->CheckMenuItem(ID_VIEW_SHOWLIGHTS, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_ENT ) + pMenu->CheckMenuItem(ID_VIEW_SHOWENT, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_FUNC_GROUP ) + pMenu->CheckMenuItem(ID_VIEW_SHOWFUNCGROUPS, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_PATHS ) + pMenu->CheckMenuItem(ID_VIEW_SHOWPATH, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_WATER ) + pMenu->CheckMenuItem(ID_VIEW_SHOWWATER, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_WORLD ) + pMenu->CheckMenuItem(ID_VIEW_SHOWWORLD, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_CLIP ) + pMenu->CheckMenuItem(ID_VIEW_SHOWCLIP, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_HINT ) + pMenu->CheckMenuItem(ID_VIEW_SHOWHINT, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_DETAIL ) + pMenu->CheckMenuItem(ID_VIEW_SHOWDETAIL, MF_BYCOMMAND | MF_UNCHECKED ); + +#ifdef QUAKE3 + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_CURVES ) + pMenu->CheckMenuItem(ID_VIEW_SHOWCURVES, MF_BYCOMMAND | MF_UNCHECKED ); + g_qeglobals.d_savedinfo.exclude &= ~SHOW_CURVES_ONLY; // lose this option on re-read always!!!! + if ( !(g_qeglobals.d_savedinfo.exclude & SHOW_CURVES_ONLY) ) + pMenu->CheckMenuItem(ID_VIEW_SHOWCURVESONLY, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_WAYPOINTS ) + pMenu->CheckMenuItem(ID_VIEW_SHOWWAYPOINTS, MF_BYCOMMAND | MF_UNCHECKED ); + g_qeglobals.d_savedinfo.exclude &= ~SHOW_WAYPOINTS_ONLY; // lose this option on re-read always!!!! + if ( !(g_qeglobals.d_savedinfo.exclude & SHOW_WAYPOINTS_ONLY) ) + pMenu->CheckMenuItem(ID_VIEW_SHOWWAYPOINTS_ONLY, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_MISCMODELS ) + pMenu->CheckMenuItem(ID_VIEW_SHOWMISC_MODEL, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_MISCMODELBREAKABLES ) + pMenu->CheckMenuItem(ID_VIEW_SHOWMISC_MODEL_BREAKABLE, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_MISCMODELXXXX ) + pMenu->CheckMenuItem(ID_VIEW_SHOWMISC_MODEL_XXXX, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_TRIGGERXXXX ) + pMenu->CheckMenuItem(ID_VIEW_SHOWTRIGGER_XXXX, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_TARGETSPEAKERS ) + pMenu->CheckMenuItem(ID_VIEW_SHOWTARGET_SPEAKER, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_REFTAGS ) + pMenu->CheckMenuItem(ID_VIEW_SHOWREF_TAGS, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_NONSOLID ) + pMenu->CheckMenuItem(ID_VIEW_SHOW_NONSOLID, MF_BYCOMMAND | MF_UNCHECKED ); + + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_EASY ) + pMenu->CheckMenuItem(ID_SHOW_EASY, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_MEDIUM ) + pMenu->CheckMenuItem(ID_SHOW_MEDIUM, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_HARD ) + pMenu->CheckMenuItem(ID_SHOW_HARD, MF_BYCOMMAND | MF_UNCHECKED ); + g_qeglobals.d_savedinfo.exclude &= ~EXCLUDE_ALL_EXCEPT_DETAIL; // lose this option on re-read always!!!! + if ( !(g_qeglobals.d_savedinfo.exclude & EXCLUDE_ALL_EXCEPT_DETAIL) ) + pMenu->CheckMenuItem(ID_VIEW_SHOW_DETAIL_ONLY, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_LIGHTS_RADII ) + pMenu->CheckMenuItem(ID_VIEW_SHOWLIGHTRADII, MF_BYCOMMAND | MF_UNCHECKED ); +#endif + + pMenu->CheckMenuItem(ID_TOGGLE_LOCK, MF_BYCOMMAND | (g_PrefsDlg.m_bTextureLock) ? MF_CHECKED : MF_UNCHECKED); + pMenu->CheckMenuItem(ID_TOGGLE_ROTATELOCK, MF_BYCOMMAND | (g_PrefsDlg.m_bRotateLock) ? MF_CHECKED : MF_UNCHECKED); + pMenu->CheckMenuItem(ID_VIEW_CUBICCLIPPING, MF_BYCOMMAND | (g_PrefsDlg.m_bCubicClipping) ? MF_CHECKED : MF_UNCHECKED); + if (m_wndToolBar.GetSafeHwnd()) + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_VIEW_CUBICCLIPPING, (g_PrefsDlg.m_bCubicClipping) ? TRUE : FALSE); + + int n = g_PrefsDlg.m_nTextureScale; + int id; + switch (n) + { + case 10 : id = ID_TEXTURES_TEXTUREWINDOWSCALE_10; break; + case 25 : id = ID_TEXTURES_TEXTUREWINDOWSCALE_25; break; + case 50 : id = ID_TEXTURES_TEXTUREWINDOWSCALE_50; break; + case 200 : id = ID_TEXTURES_TEXTUREWINDOWSCALE_200; break; + default : id = ID_TEXTURES_TEXTUREWINDOWSCALE_100; break; + } + CheckTextureScale(id); + + + } + if (g_qeglobals.d_project_entity) + { + FillTextureMenu(); // redundant but i'll clean it up later.. yeah right.. + FillBSPMenu(); +#ifdef QUAKE3 + LoadMruInReg(g_qeglobals.d_lpMruMenu,"Software\\id\\Q3Trek_QuakeEd4\\MRU"); +#else + LoadMruInReg(g_qeglobals.d_lpMruMenu,"Software\\id\\QuakeEd4\\MRU"); +#endif + + PlaceMenuMRUItem(g_qeglobals.d_lpMruMenu,::GetSubMenu(::GetMenu(GetSafeHwnd()),0), ID_FILE_EXIT); + } +} + +int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + + //Init3Dfx(); + + char* pBuffer = g_strAppPath.GetBufferSetLength(_MAX_PATH + 1); + int nResult = ::GetModuleFileName(NULL, pBuffer, _MAX_PATH); + ASSERT(nResult != 0); + pBuffer[g_strAppPath.ReverseFind('\\') + 1] = '\0'; + g_strAppPath.ReleaseBuffer(); + + InitCommonControls (); + g_qeglobals.d_hInstance = AfxGetInstanceHandle(); + MFCCreate(AfxGetInstanceHandle()); + + //g_PrefsDlg.LoadPrefs(); + + if (CFrameWnd::OnCreate(lpCreateStruct) == -1) + return -1; + + UINT nStyle; + UINT nID = (g_PrefsDlg.m_bWideToolbar) ? IDR_TOOLBAR_ADVANCED : IDR_TOOLBAR1; + + if (!m_wndToolBar.Create(this) || + !m_wndToolBar.LoadToolBar(nID)) + { + TRACE0("Failed to create toolbar\n"); + return -1; // fail to create + } + + if (!m_wndStatusBar.Create(this) || + !m_wndStatusBar.SetIndicators(indicators, + sizeof(indicators)/sizeof(UINT))) + { + TRACE0("Failed to create status bar\n"); + return -1; // fail to create + } + +#if 0 + if (!m_wndScaleBar.Create(this) || + !m_wndScaleBar.LoadToolBar(IDR_TOOLBAR_SCALELOCK)) + { + TRACE0("Failed to create scaling toolbar\n"); + return -1; // fail to create + } +#endif + + // TODO: Remove this if you don't want tool tips or a resizeable toolbar + m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() | + CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC); + + // TODO: Delete these three lines if you don't want the toolbar to + // be dockable + m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); + EnableDocking(CBRS_ALIGN_ANY); + DockControlBar(&m_wndToolBar); + + //m_wndScaleBar.SetBarStyle(m_wndScaleBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC); + //m_wndScaleBar.EnableDocking(CBRS_ALIGN_ANY); + //m_wndScaleBar.ShowWindow(SW_HIDE); + + int nImage; + int nIndex = m_wndToolBar.CommandToIndex(ID_VIEW_CAMERATOGGLE); + if (nIndex >= 0) + { + m_wndToolBar.GetButtonInfo(nIndex, nID, nStyle, nImage); + m_wndToolBar.SetButtonInfo(nIndex, nID, TBBS_CHECKBOX, nImage); + m_wndToolBar.GetToolBarCtrl().CheckButton(nID); + } + m_bCamPreview = true; + + nIndex = m_wndToolBar.CommandToIndex(ID_VIEW_CUBICCLIPPING); + if (nIndex >= 0) + { + m_wndToolBar.GetButtonInfo(nIndex, nID, nStyle, nImage); + m_wndToolBar.SetButtonInfo(nIndex, nID, TBBS_CHECKBOX, nImage); + } + nIndex = m_wndToolBar.CommandToIndex(ID_VIEW_ENTITY); + if (nIndex >= 0) + { + m_wndToolBar.GetButtonInfo(nIndex, nID, nStyle, nImage); + m_wndToolBar.SetButtonInfo(nIndex, nID, TBBS_CHECKBOX, nImage); + } + nIndex = m_wndToolBar.CommandToIndex(ID_VIEW_CLIPPER); + if (nIndex >= 0) + { + m_wndToolBar.GetButtonInfo(nIndex, nID, nStyle, nImage); + m_wndToolBar.SetButtonInfo(nIndex, nID, TBBS_CHECKBOX, nImage); + } + nIndex = m_wndToolBar.CommandToIndex(ID_SELECT_MOUSEROTATE); + if (nIndex >= 0) + { + m_wndToolBar.GetButtonInfo(nIndex, nID, nStyle, nImage); + m_wndToolBar.SetButtonInfo(nIndex, nID, TBBS_CHECKBOX, nImage); + } + + nIndex = m_wndToolBar.CommandToIndex(ID_SELECT_MOUSESCALE); + if (nIndex >= 0) + { + m_wndToolBar.GetButtonInfo(nIndex, nID, nStyle, nImage); + m_wndToolBar.SetButtonInfo(nIndex, nID, TBBS_CHECKBOX, nImage); + } + + nIndex = m_wndToolBar.CommandToIndex(ID_SCALELOCKX); + if (nIndex >= 0) + { + m_wndToolBar.GetButtonInfo(nIndex, nID, nStyle, nImage); + m_wndToolBar.SetButtonInfo(nIndex, nID, TBBS_CHECKBOX, nImage); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SCALELOCKX, TRUE); + } + + nIndex = m_wndToolBar.CommandToIndex(ID_SCALELOCKY); + if (nIndex >= 0) + { + m_wndToolBar.GetButtonInfo(nIndex, nID, nStyle, nImage); + m_wndToolBar.SetButtonInfo(nIndex, nID, TBBS_CHECKBOX, nImage); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SCALELOCKY, TRUE); + } + + nIndex = m_wndToolBar.CommandToIndex(ID_SCALELOCKZ); + if (nIndex >= 0) + { + m_wndToolBar.GetButtonInfo(nIndex, nID, nStyle, nImage); + m_wndToolBar.SetButtonInfo(nIndex, nID, TBBS_CHECKBOX, nImage); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SCALELOCKZ, TRUE); + } + +#ifdef QUAKE3 + nIndex = m_wndToolBar.CommandToIndex(ID_DONTSELECTCURVE); + if (nIndex >= 0) + { + m_wndToolBar.GetButtonInfo(nIndex, nID, nStyle, nImage); + m_wndToolBar.SetButtonInfo(nIndex, nID, TBBS_CHECKBOX, nImage); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_DONTSELECTCURVE, (g_PrefsDlg.m_bSelectCurves) ? FALSE : TRUE); + } + nIndex = m_wndToolBar.CommandToIndex(ID_PATCH_SHOWBOUNDINGBOX); + if (nIndex >= 0) + { + m_wndToolBar.GetButtonInfo(nIndex, nID, nStyle, nImage); + m_wndToolBar.SetButtonInfo(nIndex, nID, TBBS_CHECKBOX, nImage); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_PATCH_SHOWBOUNDINGBOX, g_bPatchShowBounds); + } + nIndex = m_wndToolBar.CommandToIndex(ID_PATCH_WELD); + if (nIndex >= 0) + { + m_wndToolBar.GetButtonInfo(nIndex, nID, nStyle, nImage); + m_wndToolBar.SetButtonInfo(nIndex, nID, TBBS_CHECKBOX, nImage); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_PATCH_WELD, TRUE); + } + nIndex = m_wndToolBar.CommandToIndex(ID_PATCH_DRILLDOWN); + if (nIndex >= 0) + { + m_wndToolBar.GetButtonInfo(nIndex, nID, nStyle, nImage); + m_wndToolBar.SetButtonInfo(nIndex, nID, TBBS_CHECKBOX, nImage); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_PATCH_DRILLDOWN, TRUE); + } + nIndex = m_wndToolBar.CommandToIndex(ID_PATCH_BEND); + if (nIndex >= 0) + { + m_wndToolBar.GetButtonInfo(nIndex, nID, nStyle, nImage); + m_wndToolBar.SetButtonInfo(nIndex, nID, TBBS_CHECKBOX, nImage); + } + nIndex = m_wndToolBar.CommandToIndex(ID_PATCH_INSDEL); + if (nIndex >= 0) + { + m_wndToolBar.GetButtonInfo(nIndex, nID, nStyle, nImage); + m_wndToolBar.SetButtonInfo(nIndex, nID, TBBS_CHECKBOX, nImage); + } +#else + m_wndToolBar.GetToolBarCtrl().HideButton(ID_DONTSELECTCURVE); + m_wndToolBar.GetToolBarCtrl().HideButton(ID_PATCH_SHOWBOUNDINGBOX); + m_wndToolBar.GetToolBarCtrl().HideButton(ID_PATCH_WELD); + m_wndToolBar.GetToolBarCtrl().HideButton(ID_PATCH_WIREFRAME); +#endif + g_nScaleHow = (SCALE_X | SCALE_Y | SCALE_Z); + + +#ifdef QUAKE3 + g_pParentWnd->GetMenu()->DestroyMenu(); + CMenu* pMenu = new CMenu(); + pMenu->LoadMenu(IDR_MENU_QUAKE3); + g_pParentWnd->SetMenu(pMenu); +#else + CMenu* pMenu = GetMenu(); +#endif + + m_wndTextureBar.Create(this, IDD_TEXTUREBAR, CBRS_BOTTOM, 7433); + m_wndTextureBar.EnableDocking(CBRS_ALIGN_ANY); + DockControlBar(&m_wndTextureBar); + + g_qeglobals.d_lpMruMenu = CreateMruMenuDefault(); + + m_bAutoMenuEnable = FALSE; + + LoadCommandMap(); + + CFont* pFont = new CFont(); + pFont->CreatePointFont(g_PrefsDlg.m_nStatusSize * 10, "Arial"); + m_wndStatusBar.SetFont(pFont); + + if (g_PrefsDlg.m_bRunBefore == FALSE) + { + g_PrefsDlg.m_bRunBefore = TRUE; + g_PrefsDlg.SavePrefs(); + if (MessageBox("Would you like QERadiant to build and load a default project? If this is the first time you have run QERadiant or you are not familiar with editing QE4 project files directly, this is HIGHLY recommended", "Create a default project?", MB_YESNO) == IDYES) + { + OnFileNewproject(); + } + } + else + { + // hack that keeps SGI OpenGL from crashing on texture load with no map +#if 0 + if (g_PrefsDlg.m_bSGIOpenGL) + { + vec3_t vMin, vMax; + vMin[0] = vMin[1] = vMin[2] = 0; + vMax[0] = vMax[1] = vMax[2] = 8; + brush_t* pBrush = Brush_Create(vMin, vMax, &g_qeglobals.d_texturewin.texdef); + Entity_LinkBrush (world_entity, pBrush); + Brush_Build(pBrush); + Brush_AddToList (pBrush, &active_brushes); + Select_Brush(pBrush); + Sys_UpdateWindows(W_ALL); + PostMessage(WM_COMMAND, ID_SELECTION_DELETE, 0); + } +#endif + if (g_PrefsDlg.m_bLoadLastMap && g_PrefsDlg.m_strLastMap.GetLength() > 0) + Map_LoadFile(g_PrefsDlg.m_strLastMap.GetBuffer(0)); + } + + SetGridStatus(); + SetTexValStatus(); + SetButtonMenuStates(); + LoadBarState("RadiantToolBars"); + if (!g_PrefsDlg.m_bTextureBar) + ShowControlBar(&m_wndTextureBar, FALSE, TRUE); + else + ShowControlBar(&m_wndTextureBar, TRUE, TRUE); + + ShowControlBar(&m_wndToolBar, (m_wndToolBar.GetStyle() & WS_VISIBLE), TRUE); + + SetActiveXY(m_pXYWnd); + m_pXYWnd->SetFocus(); + + SS_LoadFromRegistry(); + + +//// + char sBuffer[256]; + LONG lSize = sizeof(sBuffer); + if (LoadRegistryInfo("Radiant::BEHAVED_PATH", &sBuffer, &lSize)) + g_cstrBehavEdPath = sBuffer; +//// + + PostMessage(WM_KEYDOWN, 'O', NULL); + OnPluginsRefresh(); + return 0; +} + +void CMainFrame::LoadCommandMap() +{ + CString strINI; + char* pBuff = new char[1024]; + if (g_PrefsDlg.m_strUserPath.GetLength() > 0) + strINI = g_PrefsDlg.m_strUserPath; + else + { + strINI = g_strAppPath; + strINI += "\\radiant.ini"; + } + + for (int i = 0; i < g_nCommandCount; i++) + { + int nLen = GetPrivateProfileString("Commands", g_Commands[i].m_strCommand, "", pBuff, 1024, strINI); + if (nLen > 0) + { + CString strBuff = pBuff; + strBuff.TrimLeft(); + strBuff.TrimRight(); + int nSpecial = strBuff.Find("+alt"); + if (nSpecial >= 0) + { + g_Commands[i].m_nModifiers |= RAD_ALT; + FindReplace(strBuff, "+alt", ""); + } + nSpecial = strBuff.Find("+ctrl"); + if (nSpecial >= 0) + { + g_Commands[i].m_nModifiers |= RAD_CONTROL; + FindReplace(strBuff, "+ctrl", ""); + } + nSpecial = strBuff.Find("+shift"); + if (nSpecial >= 0) + { + g_Commands[i].m_nModifiers |= RAD_SHIFT; + FindReplace(strBuff, "+shift", ""); + } + strBuff.TrimLeft(); + strBuff.TrimRight(); + strBuff.MakeUpper(); + if (nLen == 1) // most often case.. deal with first + { + g_Commands[i].m_nKey = __toascii(strBuff.GetAt(0)); + } + else // special key + { + for (int j = 0; j < g_nKeyCount; j++) + { + if (strBuff.CompareNoCase(g_Keys[j].m_strName) == 0) + { + g_Commands[i].m_nKey = g_Keys[j].m_nVKKey; + break; + } + } + } + } + } + delete []pBuff; +} + +BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) +{ + // TODO: Modify the Window class or styles here by modifying + // the CREATESTRUCT cs + return CFrameWnd::PreCreateWindow(cs); +} + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame diagnostics + +#ifdef _DEBUG +void CMainFrame::AssertValid() const +{ + CFrameWnd::AssertValid(); +} + +void CMainFrame::Dump(CDumpContext& dc) const +{ + CFrameWnd::Dump(dc); +} + +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame message handlers +void CMainFrame::CreateQEChildren() +{ + // the project file can be specified on the command line, + // or implicitly found in the scripts directory + bool bProjectLoaded = false; + if (AfxGetApp()->m_lpCmdLine && strlen(AfxGetApp()->m_lpCmdLine)) + { + ParseCommandLine (AfxGetApp()->m_lpCmdLine); + bProjectLoaded = QE_LoadProject(argv[1]); + } + else + { + if (g_PrefsDlg.m_bLoadLast && g_PrefsDlg.m_strLastProject.GetLength() > 0) + { + bProjectLoaded = QE_LoadProject(g_PrefsDlg.m_strLastProject.GetBuffer(0)); + } + if (!bProjectLoaded) + { + CString str = g_strAppPath; + AddSlash(str); + str += "../base/scripts/StarWars.qe4"; + char cWork[1024]; + char *pFile = NULL; + GetFullPathName(str, 1024, cWork, &pFile); + bProjectLoaded = QE_LoadProject(cWork); + } + if (!bProjectLoaded) + { + bProjectLoaded = QE_LoadProject("scripts/StarWars.qe4"); + } + } + + if (!bProjectLoaded) + { +#if 0 + // let's try the default project directory.. + char* pBuff = new char[1024]; + ::GetCurrentDirectory(1024, pBuff); + CString strDefProj = g_strAppPath; + AddSlash(strDefProj); + strDefProj += "defproj"; + if (::SetCurrentDirectory(strDefProj)) + { + bProjectLoaded = QE_LoadProject("scripts/StarWars.qe4"); + if (bProjectLoaded) + { + // setup auto load stuff for the default map + g_PrefsDlg.m_bLoadLast = TRUE; + AddSlash(strDefProj); + strDefProj += "maps\\defproj.map"; + g_PrefsDlg.m_strLastMap = strDefProj; + g_PrefsDlg.SavePrefs(); + } + } + else + { + ::SetCurrentDirectory(pBuff); + } + delete []pBuff; +#endif + + if (!bProjectLoaded) + { + Sys_Printf ("Using default.qe4. You may experience problems. See the readme.txt\n"); + CString strProj = g_strAppPath; + strProj += "\\default.qe4"; + bProjectLoaded = QE_LoadProject(strProj.GetBuffer(0)); + + if (!bProjectLoaded) + { + CFileDialog dlgFile(true, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "QERadiant Project files (*.qe4, *.prj)|*.qe4|*.prj||", this); + if (dlgFile.DoModal() == IDOK) + bProjectLoaded = QE_LoadProject(dlgFile.GetPathName().GetBuffer(0)); + } + } + } + + if (!bProjectLoaded) + Error("Unable to load project file. It was unavailable in the scripts path and the default could not be found"); + +// if (g_PrefsDlg.m_bPAK == TRUE) +// InitPakFile(); + + if (g_PrefsDlg.m_bPAK == TRUE) + { + // FIXME: pay attention to Q3 pref + //InitPakFile(ValueForKey(g_qeglobals.d_project_entity, "basepath"), g_PrefsDlg.m_strPAKFile); + InitPakFile(ValueForKey(g_qeglobals.d_project_entity, "basepath"), NULL); + } + + + QE_Init (); + + Sys_Printf ("Entering message loop\n"); + + m_bDoLoop = true; + SetTimer(QE_TIMER0, 1000, NULL); + +} + +BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam) +{ + return CFrameWnd::OnCommand(wParam, lParam); +} + +LRESULT CMainFrame::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam) +{ + RoutineProcessing(); + return CFrameWnd::DefWindowProc(message, wParam, lParam); +} + + +void CMainFrame::RoutineProcessing() +{ + if (m_bDoLoop) + { + double time = 0.0; + double oldtime = 0.0; + double delta= 0.0; + + CheckBspProcess (); + time = Sys_DoubleTime (); + delta = time - oldtime; + oldtime = time; + if (delta > 0.2) + delta = 0.2; + + // run time dependant behavior + if (m_pCamWnd) + m_pCamWnd->Cam_MouseControl(delta); + + if (g_PrefsDlg.m_bQE4Painting && g_nUpdateBits) + { + int nBits = g_nUpdateBits; // this is done to keep this routine from being + g_nUpdateBits = 0; // re-entered due to the paint process.. only + UpdateWindows(nBits); // happens in rare cases but causes a stack overflow + } + + } +} + +LRESULT CMainFrame::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) +{ + return CFrameWnd::WindowProc(message, wParam, lParam); +} + +bool MouseDown() +{ + if (::GetAsyncKeyState(VK_LBUTTON)) + return true; + if (::GetAsyncKeyState(VK_RBUTTON)) + return true; + if (::GetAsyncKeyState(VK_MBUTTON)) + return true; + return false; +} + + +void CMainFrame::OnTimer(UINT nIDEvent) +{ + if (!MouseDown()) + { + QE_CountBrushesAndUpdateStatusBar(); + QE_CheckAutoSave(); + } +} +struct SplitInfo +{ + int m_nMin; + int m_nCur; +}; + +bool LoadWindowPlacement(HWND hwnd, const char* pName) +{ + WINDOWPLACEMENT wp; + wp.length = sizeof(WINDOWPLACEMENT); + LONG lSize = sizeof(wp); + if (LoadRegistryInfo(pName, &wp, &lSize)) + { + ::SetWindowPlacement(hwnd, &wp); + return true; + } + return false; +} + +void SaveWindowPlacement(HWND hwnd, const char* pName) +{ + WINDOWPLACEMENT wp; + wp.length = sizeof(WINDOWPLACEMENT); + if (::GetWindowPlacement(hwnd, &wp)) + { + SaveRegistryInfo(pName, &wp, sizeof(wp)); + } +} + + +void CMainFrame::OnDestroy() +{ + KillTimer(QE_TIMER0); + + SS_SaveToRegistry(); + SS_Shutdown_OnceOnly(); + +//// + char sBuffer[256]; + strcpy(sBuffer,g_cstrBehavEdPath); + SaveRegistryInfo("Radiant::BEHAVED_PATH", &sBuffer, sizeof(sBuffer)); +//// + + SaveBarState("RadiantToolBars"); + + // FIXME + // original mru stuff needs replaced with mfc stuff +#ifdef QUAKE3 + SaveMruInReg(g_qeglobals.d_lpMruMenu,"Software\\id\\Q3Trek_QuakeEd4\\MRU"); +#else + SaveMruInReg(g_qeglobals.d_lpMruMenu,"Software\\id\\QuakeEd4\\MRU"); +#endif + + DeleteMruMenu(g_qeglobals.d_lpMruMenu); + + SaveWindowPlacement(GetSafeHwnd(), "Radiant::MainWindowPlace"); + //SaveWindowState(GetSafeHwnd(), "Radiant::MainWindow"); + //if (m_nCurrentStyle == QR_QE4) + //SaveWindowPlacement(g_qeglobals.d_hwndEntity, "EntityWindowPlace"); + + if (m_nCurrentStyle == 0 || m_nCurrentStyle == 3) + { + SaveWindowState(m_wndSplit.GetSafeHwnd(), "Radiant::Split"); + SaveWindowState(m_wndSplit2.GetSafeHwnd(), "Radiant::Split2"); + SaveWindowState(m_wndSplit3.GetSafeHwnd(), "Radiant::Split3"); + + SplitInfo spinfo; + m_wndSplit.GetRowInfo(0, spinfo.m_nCur, spinfo.m_nMin); + SaveRegistryInfo("Radiant::Split::Row_0", &spinfo, sizeof(spinfo)); + m_wndSplit.GetRowInfo(1, spinfo.m_nCur, spinfo.m_nMin); + SaveRegistryInfo("Radiant::Split::Row_1", &spinfo, sizeof(spinfo)); + + m_wndSplit2.GetColumnInfo(0, spinfo.m_nCur, spinfo.m_nMin); + SaveRegistryInfo("Radiant::Split2::Col_0", &spinfo, sizeof(spinfo)); + m_wndSplit2.GetColumnInfo(1, spinfo.m_nCur, spinfo.m_nMin); + SaveRegistryInfo("Radiant::Split2::Col_1", &spinfo, sizeof(spinfo)); + m_wndSplit2.GetColumnInfo(2, spinfo.m_nCur, spinfo.m_nMin); + SaveRegistryInfo("Radiant::Split2::Col_2", &spinfo, sizeof(spinfo)); + + m_wndSplit3.GetRowInfo(0, spinfo.m_nCur, spinfo.m_nMin); + SaveRegistryInfo("Radiant::Split3::Row_0", &spinfo, sizeof(spinfo)); + m_wndSplit3.GetRowInfo(1, spinfo.m_nCur, spinfo.m_nMin); + SaveRegistryInfo("Radiant::Split3::Row_1", &spinfo, sizeof(spinfo)); + } + else + { + SaveWindowPlacement(m_pXYWnd->GetSafeHwnd(), "xywindow"); + SaveWindowPlacement(m_pXZWnd->GetSafeHwnd(), "xzwindow"); + SaveWindowPlacement(m_pYZWnd->GetSafeHwnd(), "yzwindow"); + SaveWindowPlacement(m_pCamWnd->GetSafeHwnd(), "camerawindow"); + SaveWindowPlacement(m_pZWnd->GetSafeHwnd(), "zwindow"); + SaveWindowState(m_pTexWnd->GetSafeHwnd(), "texwindow"); + SaveWindowState(m_pEditWnd->GetSafeHwnd(), "editwindow"); + } + + if (m_pXYWnd->GetSafeHwnd()) + m_pXYWnd->SendMessage(WM_DESTROY, 0, 0); + delete m_pXYWnd; m_pXYWnd = NULL; + + if (m_pYZWnd->GetSafeHwnd()) + m_pYZWnd->SendMessage(WM_DESTROY, 0, 0); + delete m_pYZWnd; m_pYZWnd = NULL; + + if (m_pXZWnd->GetSafeHwnd()) + m_pXZWnd->SendMessage(WM_DESTROY, 0, 0); + delete m_pXZWnd; m_pXZWnd = NULL; + + if (m_pZWnd->GetSafeHwnd()) + m_pZWnd->SendMessage(WM_DESTROY, 0, 0); + delete m_pZWnd; m_pZWnd = NULL; + + if (m_pTexWnd->GetSafeHwnd()) + m_pTexWnd->SendMessage(WM_DESTROY, 0, 0); + delete m_pTexWnd; m_pTexWnd = NULL; + + if (m_pEditWnd->GetSafeHwnd()) + m_pEditWnd->SendMessage(WM_DESTROY, 0, 0); + delete m_pEditWnd; m_pEditWnd = NULL; + + if (m_pCamWnd->GetSafeHwnd()) + m_pCamWnd->SendMessage(WM_DESTROY, 0, 0); + delete m_pCamWnd;m_pCamWnd = NULL; + + SaveRegistryInfo("SavedInfo", &g_qeglobals.d_savedinfo, sizeof(g_qeglobals.d_savedinfo)); + + // this is tacky, but they only want a few extra things saved.... + // + SaveRegistryInfo("_g_bPatchShowBounds", &g_bPatchShowBounds, sizeof(g_bPatchShowBounds)); + SaveRegistryInfo("_g_PrefsDlg.m_bSelectCurves", &g_PrefsDlg.m_bSelectCurves, sizeof(g_PrefsDlg.m_bSelectCurves)); + + if (strcmpi(currentmap, "unnamed.map") != 0) + { + g_PrefsDlg.m_strLastMap = currentmap; + g_PrefsDlg.SavePrefs(); + } + CleanUpEntities(); + + while (active_brushes.next != &active_brushes) + Brush_Free (active_brushes.next); + while (selected_brushes.next != &selected_brushes) + Brush_Free (selected_brushes.next); + while (filtered_brushes.next != &filtered_brushes) + Brush_Free (filtered_brushes.next); + + while (entities.next != &entities) + Entity_Free (entities.next); + + epair_t* pEPair = g_qeglobals.d_project_entity->epairs; + while (pEPair) + { + epair_t* pNextEPair = pEPair->next; + free (pEPair->key); + free (pEPair->value); + free (pEPair); + pEPair = pNextEPair; + } + + Grouping_Shutdown(); + + entity_t* pEntity = g_qeglobals.d_project_entity->next; + while (pEntity != NULL && pEntity != g_qeglobals.d_project_entity) + { + entity_t* pNextEntity = pEntity->next; + Entity_Free(pEntity); + pEntity = pNextEntity; + } + + if (g_qeglobals.d_qtextures) + { + qtexture_t* pTex = g_qeglobals.d_qtextures->next; + while (pTex != NULL && pTex != g_qeglobals.d_qtextures) + { + qtexture_t* pNextTex = pTex->next; + free(pTex); + pTex = pNextTex; + } + } + + if (world_entity) + Entity_Free(world_entity); + + if (notexture) + free(notexture); + + //if (current_texture) + // free(current_texture); + ClosePakFile(); + + FreeShaders(); + + CFrameWnd::OnDestroy(); +} + +void CMainFrame::OnClose() +{ + if (ConfirmModified()) + { + extern void Map_Free (void); // unfortunately if I include map.h for this at the top I get other errors... + Select_Deselect(); // prevent query during Map_Free about copying selected brushes + Map_Free(); // this is now being called to give the user a chance to checkin/undocheckout the map, and it + // also cuts down on the memory leak mess toy get exiting in debugger mode + CFrameWnd::OnClose(); + } +} + +void CMainFrame::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + // run through our list to see if we have a handler for nChar + // + + for (int i = 0; i < g_nCommandCount; i++) + { + if (g_Commands[i].m_nKey == nChar) // find a match? + { + bool bGo = true; + if (g_Commands[i].m_nModifiers & RAD_PRESS) + { + int nModifiers = g_Commands[i].m_nModifiers & ~RAD_PRESS; + if (nModifiers) // are there modifiers present? + { + if (nModifiers & RAD_ALT) + if (!(GetKeyState(VK_MENU) & 0x8000)) + bGo = false; + if (nModifiers & RAD_CONTROL) + if (!(GetKeyState(VK_CONTROL) & 0x8000)) + bGo = false; + if (nModifiers & RAD_SHIFT) + if (!(GetKeyState(VK_SHIFT) & 0x8000)) + bGo = false; + } + else // no modifiers make sure none of those keys are pressed + { + if (GetKeyState(VK_MENU) & 0x8000) + bGo = false; + if (GetKeyState(VK_CONTROL) & 0x8000) + bGo = false; + if (GetKeyState(VK_SHIFT) & 0x8000) + bGo = false; + } + if (bGo) + { + SendMessage(WM_COMMAND, g_Commands[i].m_nCommand, 0); + break; + } + } + } + } +} + +bool CamOK(unsigned int nKey) +{ + if (nKey == VK_UP || nKey == VK_LEFT || nKey == VK_RIGHT || nKey == VK_DOWN) + { + if (::GetAsyncKeyState(nKey)) + return true; + else + return false; + } + return true; +} + + +void CMainFrame::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + //OnKeyDown(nChar, nRepCnt, nFlags); + if (nChar == VK_DOWN) + { + OnKeyDown(nChar, nRepCnt, nFlags); + } + CFrameWnd::OnSysKeyDown(nChar, nRepCnt, nFlags); +} + +void CMainFrame::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + for (int i = 0; i < g_nCommandCount; i++) + { + if (g_Commands[i].m_nKey == nChar) // find a match? + { + bool bGo = true; + if (g_Commands[i].m_nModifiers) // are there modifiers present? + { + if (g_Commands[i].m_nModifiers & RAD_ALT) + if (!(GetKeyState(VK_MENU) & 0x8000)) + bGo = false; + if (g_Commands[i].m_nModifiers & RAD_CONTROL) + if (!(GetKeyState(VK_CONTROL) & 0x8000)) + bGo = false; + if (g_Commands[i].m_nModifiers & RAD_SHIFT) + if (!(GetKeyState(VK_SHIFT) & 0x8000)) + bGo = false; + } + else // no modifiers make sure none of those keys are pressed + { + if (GetKeyState(VK_MENU) & 0x8000) + bGo = false; + if (GetKeyState(VK_CONTROL) & 0x8000) + bGo = false; + if (GetKeyState(VK_SHIFT) & 0x8000) + bGo = false; + } + if (bGo) + { + SendMessage(WM_COMMAND, g_Commands[i].m_nCommand, 0); + break; + } + } + } + + CFrameWnd::OnKeyDown(nChar, nRepCnt, nFlags); +} + +BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) +{ + g_qeglobals.d_hwndMain = GetSafeHwnd(); + g_qeglobals.d_hwndStatus = GetMessageBar()->GetSafeHwnd(); + + if (g_PrefsDlg.m_bRunBefore == FALSE) + { + MessageBox("In the following dialog, please make sure the Quake2 .exe information is correct.\nQERadiant will NOT run correctly without this information"); + g_PrefsDlg.DoModal(); + } + + m_nCurrentStyle = g_PrefsDlg.m_nView; + CreateEntityWindow(AfxGetInstanceHandle()); + if (!LoadWindowPlacement(GetSafeHwnd(), "Radiant::MainWindowPlace")) + LoadWindowState(GetSafeHwnd(), "Radiant::MainWindow"); + + //if (m_nCurrentStyle == QR_QE4) + // LoadWindowPlacement(g_qeglobals.d_hwndEntity, "EntityWindowPlace"); + + CRect rect(5,25, 100, 100); + + if (g_PrefsDlg.m_nView == 0 || g_PrefsDlg.m_nView == 3) + { + m_wndSplit.CreateStatic(this, 2, 1); + m_wndSplit2.CreateStatic(&m_wndSplit, 1, 3); + m_wndSplit3.CreateStatic(&m_wndSplit2, 2,1); + + m_wndSplit.CreateView(1,0,RUNTIME_CLASS(CEditWnd), CSize(25, 100), pContext); + g_pEdit = dynamic_cast(m_wndSplit.GetPane(1,0)); + if (g_pEdit) + g_qeglobals.d_hwndEdit = g_pEdit->GetSafeHwnd(); + + m_wndSplit3.CreateView(0,0,RUNTIME_CLASS(CCamWnd), CSize(25, 100), pContext); + m_pCamWnd = dynamic_cast(m_wndSplit3.GetPane(0,0)); + + m_wndSplit2.CreateView(0,1,RUNTIME_CLASS(CXYWnd), CSize(25, 100), pContext); + m_pXYWnd = dynamic_cast(m_wndSplit2.GetPane(0,1)); + m_pXYWnd->SetViewType(XY); + + m_pCamWnd->SetXYFriend(m_pXYWnd); + + m_wndSplit2.CreateView(0,2,RUNTIME_CLASS(CZWnd), CSize(25, 100), pContext); + m_pZWnd = dynamic_cast(m_wndSplit2.GetPane(0,2)); + + m_wndSplit3.CreateView(1,0,RUNTIME_CLASS(CTexWnd), CSize(25, 100), pContext); + m_pTexWnd = dynamic_cast(m_wndSplit3.GetPane(1,0)); + + CreateQEChildren(); + + if (g_PrefsDlg.m_nView == 0) + { + // the following bit switches the left and right views + CWnd* pRight = m_wndSplit2.GetPane(0,2); + long lRightID = ::GetWindowLong(pRight->GetSafeHwnd(), GWL_ID); + long lLeftID = ::GetWindowLong(m_wndSplit3.GetSafeHwnd(), GWL_ID); + ::SetWindowLong(pRight->GetSafeHwnd(), GWL_ID, lLeftID); + ::SetWindowLong(m_wndSplit3.GetSafeHwnd(), GWL_ID, lRightID); + } + + CRect rctParent; + GetClientRect(rctParent); + + m_wndSplit.SetRowInfo(0, rctParent.Height() * .85, 50); + m_wndSplit.SetRowInfo(1, rctParent.Height() * .15, 5); + + float fLeft = (g_PrefsDlg.m_nView == 0) ? .05 : .25; + float fRight = (g_PrefsDlg.m_nView == 0) ? .25 : .05; + int nMin1 = (g_PrefsDlg.m_nView == 0) ? 10 : 25; + int nMin2 = (nMin1 == 10) ? 25 : 10; + + m_wndSplit2.SetColumnInfo(0, rctParent.Width() * fLeft, nMin1); + m_wndSplit2.SetColumnInfo(1, rctParent.Width() * .70, 100); + m_wndSplit2.SetColumnInfo(2, rctParent.Width() * fRight, nMin2); + + m_wndSplit3.SetRowInfo(1, (rctParent.Height() * .85) * .40, 15); + m_wndSplit3.SetRowInfo(0, (rctParent.Height() * .85) * .60, 15); + + LoadWindowState(m_wndSplit.GetSafeHwnd(), "Radiant::Split"); + LoadWindowState(m_wndSplit2.GetSafeHwnd(), "Radiant::Split2"); + LoadWindowState(m_wndSplit3.GetSafeHwnd(), "Radiant::Split3"); + ::ShowWindow(g_qeglobals.d_hwndEntity, SW_HIDE); + + SplitInfo spinfo; + long lSize; + if (LoadRegistryInfo("Radiant::Split::Row_0", &spinfo, &lSize)) + m_wndSplit.SetRowInfo(0, spinfo.m_nCur, spinfo.m_nMin); + if (LoadRegistryInfo("Radiant::Split::Row_1", &spinfo, &lSize)) + m_wndSplit.SetRowInfo(1, spinfo.m_nCur, spinfo.m_nMin); + + if (LoadRegistryInfo("Radiant::Split2::Col_0", &spinfo, &lSize)) + m_wndSplit2.SetColumnInfo(0, spinfo.m_nCur, spinfo.m_nMin); + if (LoadRegistryInfo("Radiant::Split2::Col_1", &spinfo, &lSize)) + m_wndSplit2.SetColumnInfo(1, spinfo.m_nCur, spinfo.m_nMin); + if (LoadRegistryInfo("Radiant::Split2::Col_2", &spinfo, &lSize)) + m_wndSplit2.SetColumnInfo(2, spinfo.m_nCur, spinfo.m_nMin); + + if (LoadRegistryInfo("Radiant::Split3::Row_0", &spinfo, &lSize)) + m_wndSplit3.SetRowInfo(0, spinfo.m_nCur, spinfo.m_nMin); + if (LoadRegistryInfo("Radiant::Split3::Row_1", &spinfo, &lSize)) + m_wndSplit3.SetRowInfo(1, spinfo.m_nCur, spinfo.m_nMin); + + m_wndSplit.RecalcLayout(); + m_wndSplit2.RecalcLayout(); + m_wndSplit3.RecalcLayout(); + } + else if (g_PrefsDlg.m_nView == 1) + { + m_pCamWnd = new CCamWnd(); + m_pCamWnd->Create(CAMERA_WINDOW_CLASS, "", QE3_CHILDSTYLE, rect, this, 1234); + + m_pZWnd = new CZWnd(); + m_pZWnd->Create(Z_WINDOW_CLASS, "", QE3_CHILDSTYLE, rect, this, 1238); + + m_pXYWnd = new CXYWnd(); + m_pXYWnd->Create(XY_WINDOW_CLASS, "", QE3_CHILDSTYLE, rect, this, 1235); + m_pXYWnd->SetViewType(XY); + + m_pXZWnd = new CXYWnd(); + m_pXZWnd->Create(XY_WINDOW_CLASS, "", QE3_CHILDSTYLE, rect, this, 1236); + m_pXZWnd->SetViewType(XZ); + + m_pYZWnd = new CXYWnd(); + m_pYZWnd->Create(XY_WINDOW_CLASS, "", QE3_CHILDSTYLE, rect, this, 1237); + m_pYZWnd->SetViewType(YZ); + + m_pCamWnd->SetXYFriend(m_pXYWnd); + + m_pTexWnd = new CTexWnd(); + //m_pTexWnd->Create(TEXTURE_WINDOW_CLASS, "", QE3_CHILDSTYLE, rect, this, 1237); + m_pTexWnd->Create(TEXTURE_WINDOW_CLASS, "", QE3_SPLITTER_STYLE, rect, this, 1239); + ::SetParent(m_pTexWnd->GetSafeHwnd(), g_qeglobals.d_hwndEntity); + + //m_pEditWnd = new CRADEditWnd(); + //m_pEditWnd->Create(NULL, "Console", QE3_CHILDSTYLE, rect, this, 1238); + //g_pEdit = m_pEditWnd->GetEditWnd(); + //if (g_pEdit) + // g_qeglobals.d_hwndEdit = g_pEdit->GetSafeHwnd(); + + LoadWindowPlacement(m_pXYWnd->GetSafeHwnd(), "xywindow"); + LoadWindowPlacement(m_pXZWnd->GetSafeHwnd(), "xzwindow"); + LoadWindowPlacement(m_pYZWnd->GetSafeHwnd(), "yzwindow"); + LoadWindowPlacement(m_pCamWnd->GetSafeHwnd(), "camerawindow"); + LoadWindowPlacement(m_pZWnd->GetSafeHwnd(), "zwindow"); + if (!g_PrefsDlg.m_bXZVis) + m_pXZWnd->ShowWindow(SW_HIDE); + if (!g_PrefsDlg.m_bYZVis) + m_pYZWnd->ShowWindow(SW_HIDE); + if (!g_PrefsDlg.m_bZVis) + m_pZWnd->ShowWindow(SW_HIDE); + //LoadWindowPlacement(m_pTexWnd->GetSafeHwnd(), "texwindow"); + //LoadWindowPlacement(m_pEditWnd->GetSafeHwnd(), "editwindow"); + CreateQEChildren(); + } + else // 4 way + { + m_wndSplit.CreateStatic(this, 2, 2); + + m_wndSplit.CreateView(0,0,RUNTIME_CLASS(CCamWnd), CSize(25, 100), pContext); + m_pCamWnd = dynamic_cast(m_wndSplit.GetPane(0,0)); + + m_wndSplit.CreateView(0,1,RUNTIME_CLASS(CXYWnd), CSize(25, 100), pContext); + m_pXYWnd = dynamic_cast(m_wndSplit.GetPane(0,1)); + m_pXYWnd->SetViewType(XY); + + m_wndSplit.CreateView(1,0,RUNTIME_CLASS(CXYWnd), CSize(25, 100), pContext); + m_pYZWnd = dynamic_cast(m_wndSplit.GetPane(1,0)); + m_pYZWnd->SetViewType(YZ); + + m_wndSplit.CreateView(1,1,RUNTIME_CLASS(CXYWnd), CSize(25, 100), pContext); + m_pXZWnd = dynamic_cast(m_wndSplit.GetPane(1,1)); + m_pXZWnd->SetViewType(XZ); + + m_pCamWnd->SetXYFriend(m_pXYWnd); + + m_pTexWnd = new CTexWnd(); + m_pTexWnd->Create(TEXTURE_WINDOW_CLASS, "", QE3_SPLITTER_STYLE, rect, this, 1237); + ::SetParent(m_pTexWnd->GetSafeHwnd(), g_qeglobals.d_hwndEntity); + + m_pZWnd = new CZWnd(); + m_pZWnd->Create(Z_WINDOW_CLASS, "", QE3_CHILDSTYLE, rect, this, 1236); + m_pZWnd->ShowWindow(SW_HIDE); + + + //m_pEditWnd = new CRADEditWnd(); + //m_pEditWnd->Create(NULL, "Console", QE3_STYLE, rect, this, 1238); + //g_pEdit = m_pEditWnd->GetEditWnd(); + //if (g_pEdit) + // g_qeglobals.d_hwndEdit = g_pEdit->GetSafeHwnd(); + + LoadWindowState(m_pTexWnd->GetSafeHwnd(), "texwindow"); + LoadWindowState(m_pEditWnd->GetSafeHwnd(), "editwindow"); + ::ShowWindow(g_qeglobals.d_hwndEntity, SW_HIDE); + + CreateQEChildren(); + + CRect rctParent; + GetClientRect(rctParent); + + m_wndSplit.SetRowInfo(0, rctParent.Height() * .5, 50); + m_wndSplit.SetRowInfo(1, rctParent.Height() * .5, 50); + + m_wndSplit.SetColumnInfo(0, rctParent.Width() * .5, 50); + m_wndSplit.SetColumnInfo(1, rctParent.Width() * .5, 50); + + LoadWindowState(m_wndSplit.GetSafeHwnd(), "Radiant::SplitSPLIT"); + + m_wndSplit.RecalcLayout(); + } + + if (g_pEdit) + g_pEdit->SendMessage(WM_SETFONT, (WPARAM)::GetStockObject(DEFAULT_GUI_FONT), (LPARAM)TRUE); + + + if (m_pXYWnd) + m_pXYWnd->SetActive(true); + m_bSplittersOK = true; + Texture_SetMode(g_qeglobals.d_savedinfo.iTexMenu); + + return TRUE; +} + +CRect g_rctOld(0,0,0,0); +void CMainFrame::OnSize(UINT nType, int cx, int cy) +{ + CFrameWnd::OnSize(nType, cx, cy); + + CRect rctParent; + GetClientRect(rctParent); + + UINT nID; + UINT nStyle; + int nWidth; + if (m_wndStatusBar.GetSafeHwnd()) + { + m_wndStatusBar.GetPaneInfo(0, nID, nStyle, nWidth); + m_wndStatusBar.SetPaneInfo(0, nID, nStyle, rctParent.Width() * .19); + m_wndStatusBar.GetPaneInfo(1, nID, nStyle, nWidth); + m_wndStatusBar.SetPaneInfo(1, nID, nStyle, rctParent.Width() * .19); + m_wndStatusBar.GetPaneInfo(2, nID, nStyle, nWidth); + m_wndStatusBar.SetPaneInfo(2, nID, nStyle, rctParent.Width() * .19); + m_wndStatusBar.GetPaneInfo(3, nID, nStyle, nWidth); + m_wndStatusBar.SetPaneInfo(3, nID, nStyle, rctParent.Width() * .19); + m_wndStatusBar.GetPaneInfo(4, nID, nStyle, nWidth); + m_wndStatusBar.SetPaneInfo(4, nID, nStyle, rctParent.Width() * .13); + m_wndStatusBar.GetPaneInfo(5, nID, nStyle, nWidth); + m_wndStatusBar.SetPaneInfo(5, nID, nStyle, rctParent.Width() * .01); + } + + if (nType == SIZE_RESTORED && m_bSplittersOK && g_rctOld.Width() > 0) + { + if (m_nCurrentStyle == 0 || m_nCurrentStyle == 3) + { + SplitInfo spinfo; + m_wndSplit.GetRowInfo(0, spinfo.m_nCur, spinfo.m_nMin); + float fpc1 = (float)spinfo.m_nCur / g_rctOld.Height(); + m_wndSplit.GetRowInfo(1, spinfo.m_nCur, spinfo.m_nMin); + float fpc2 = (float)spinfo.m_nCur / g_rctOld.Height(); + m_wndSplit2.GetColumnInfo(0, spinfo.m_nCur, spinfo.m_nMin); + float fpc3 = (float)spinfo.m_nCur / g_rctOld.Width(); + m_wndSplit2.GetColumnInfo(1, spinfo.m_nCur, spinfo.m_nMin); + float fpc4 = (float)spinfo.m_nCur / g_rctOld.Width(); + m_wndSplit2.GetColumnInfo(2, spinfo.m_nCur, spinfo.m_nMin); + float fpc5 = (float)spinfo.m_nCur / g_rctOld.Width(); + m_wndSplit3.GetRowInfo(0, spinfo.m_nCur, spinfo.m_nMin); + float fpc6 = (float)spinfo.m_nCur / g_rctOld.Height(); + m_wndSplit3.GetRowInfo(1, spinfo.m_nCur, spinfo.m_nMin); + float fpc7 = (float)spinfo.m_nCur / g_rctOld.Height(); + + m_wndSplit.SetRowInfo(0, rctParent.Height() * fpc1, 100); + m_wndSplit.SetRowInfo(1, rctParent.Height() * fpc2, 25); + + int nMin1 = (m_nCurrentStyle == 0) ? 10 : 25; + int nMin2 = (nMin1 == 10) ? 25 : 10; + + m_wndSplit2.SetColumnInfo(0, rctParent.Width() * fpc3, nMin1); + m_wndSplit2.SetColumnInfo(1, rctParent.Width() * fpc4, 100); + m_wndSplit2.SetColumnInfo(2, rctParent.Width() * fpc5, nMin2); + + m_wndSplit3.SetRowInfo(0, rctParent.Height() * fpc6, 50); + m_wndSplit3.SetRowInfo(1, rctParent.Height() * fpc7, 50); + + m_wndSplit.RecalcLayout(); + m_wndSplit2.RecalcLayout(); + m_wndSplit3.RecalcLayout(); + } + } + + +} + + +void OpenDialog (void); +void SaveAsDialog (bool bRegion); +void Select_Ungroup (void); + +void CMainFrame::ToggleCamera() +{ + if (m_bCamPreview) + m_bCamPreview = false; + else + m_bCamPreview = true; +} + +void CMainFrame::OnFileClose() +{ + +} + +void CMainFrame::OnFileExit() +{ + // confirm-query removed because I put one in the OnClose member instead, this way I also catch it when people + // exit the program via clicking on the X button in window-frame top right, not just File/Exit + // + PostMessage (WM_CLOSE, 0, 0L); +} + +void CMainFrame::OnFileLoadproject() +{ + if (ConfirmModified()) + ProjectDialog (); +} + +void CMainFrame::OnFileNew() +{ + if (ConfirmModified()) + Map_New (); +} + +void CMainFrame::OnFileOpen() +{ + if (ConfirmModified()) + OpenDialog (); +} + +void CMainFrame::OnFilePointfile() +{ + if (g_qeglobals.d_pointfile_display_list) + Pointfile_Clear (); + else + Pointfile_Check (); +} + +void CMainFrame::OnFilePrint() +{ + +} + +void CMainFrame::OnFilePrintPreview() +{ + +} + +void CMainFrame::OnFileSave() +{ + if (!strcmp(currentmap, "unnamed.map")) + SaveAsDialog (false); + else + Map_SaveFile (currentmap, false); +} + +void CMainFrame::OnFileSaveas() +{ + SaveAsDialog(false); +} + +void CMainFrame::OnView100() +{ + if (m_pXYWnd) + m_pXYWnd->SetScale(1); + if (m_pXZWnd) + m_pXZWnd->SetScale(1); + if (m_pYZWnd) + m_pYZWnd->SetScale(1); + Sys_UpdateWindows (W_XY|W_XY_OVERLAY); +} + +void CMainFrame::OnViewCenter() +{ + m_pCamWnd->Camera().angles[ROLL] = m_pCamWnd->Camera().angles[PITCH] = 0; + m_pCamWnd->Camera().angles[YAW] = 22.5 * + floor( (m_pCamWnd->Camera().angles[YAW]+11)/22.5 ); + Sys_UpdateWindows (W_CAMERA | W_XY_OVERLAY); +} + +void CMainFrame::OnViewConsole() +{ + if (m_nCurrentStyle > 0 && m_nCurrentStyle < 3) // QE4 style + { + if (inspector_mode == W_CONSOLE && m_nCurrentStyle != QR_QE4) // are we in console mode already? + { + if (::IsWindowVisible(g_qeglobals.d_hwndEntity)) + ::ShowWindow(g_qeglobals.d_hwndEntity, SW_HIDE); + else + ::ShowWindow(g_qeglobals.d_hwndEntity, SW_NORMAL); + } + else + { + ::ShowWindow(g_qeglobals.d_hwndEntity, SW_NORMAL); + SetInspectorMode(W_CONSOLE); + } + } +} + +void CMainFrame::OnViewDownfloor() +{ + m_pCamWnd->Cam_ChangeFloor (false); +} + +void CMainFrame::OnViewEntity() +{ + if (m_nCurrentStyle == 0 || m_nCurrentStyle == 3) + { + if (::IsWindowVisible(g_qeglobals.d_hwndEntity)) + ::ShowWindow(g_qeglobals.d_hwndEntity, SW_HIDE); + else + { + ::ShowWindow(g_qeglobals.d_hwndEntity, SW_NORMAL); + SetInspectorMode(W_ENTITY); + } + } + else + { + if (inspector_mode == W_ENTITY && m_nCurrentStyle != QR_QE4) + { + if (::IsWindowVisible(g_qeglobals.d_hwndEntity)) + ::ShowWindow(g_qeglobals.d_hwndEntity, SW_HIDE); + else + ::ShowWindow(g_qeglobals.d_hwndEntity, SW_NORMAL); + } + else + { + ::ShowWindow(g_qeglobals.d_hwndEntity, SW_NORMAL); + SetInspectorMode(W_ENTITY); + } + } +} + +void CMainFrame::OnViewFront() +{ + if (m_nCurrentStyle != 2) + { + m_pXYWnd->SetViewType(YZ); + m_pXYWnd->PositionView(); + } + Sys_UpdateWindows (W_XY); +} + +void CMainFrame::OnMru(unsigned int nID) +{ + DoMru(GetSafeHwnd(),nID); +} + +void CMainFrame::OnViewNearest(unsigned int nID) +{ + Texture_SetMode(nID); +} + +void CMainFrame::OnTextureWad(unsigned int nID) +{ + Sys_BeginWait (); + Texture_ShowDirectory (nID); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnBspCommand(unsigned int nID) +{ + if (g_PrefsDlg.m_bSnapShots && stricmp(currentmap, "unnamed.map") != 0) + Map_Snapshot(); + + RunBsp (bsp_commands[LOWORD(nID-CMD_BSPCOMMAND)]); +} + + + +void CMainFrame::OnViewShowblocks() +{ + g_qeglobals.show_blocks ^= 1; + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWBLOCKS, MF_BYCOMMAND | (g_qeglobals.show_blocks ? MF_CHECKED : MF_UNCHECKED) ); + Sys_UpdateWindows (W_XY); +} + +void CMainFrame::OnViewShowclip() +{ + if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_CLIP ) & EXCLUDE_CLIP ) + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWCLIP, MF_BYCOMMAND | MF_UNCHECKED ); + else + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWCLIP, MF_BYCOMMAND | MF_CHECKED ); + Sys_UpdateWindows (W_XY|W_CAMERA); +} + +void CMainFrame::OnViewShowcoordinates() +{ + g_qeglobals.d_savedinfo.show_coordinates ^= 1; + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWCOORDINATES, MF_BYCOMMAND | (g_qeglobals.d_savedinfo.show_coordinates ? MF_CHECKED : MF_UNCHECKED) ); + Sys_UpdateWindows (W_XY); +} + +void CMainFrame::OnViewShowdetail() +{ + if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_DETAIL ) & EXCLUDE_DETAIL ) + { + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWDETAIL, MF_BYCOMMAND | MF_UNCHECKED ); + ::SetWindowText (g_qeglobals.d_hwndCamera, "Camera View (DETAIL EXCLUDED)"); + } + else + { + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWDETAIL, MF_BYCOMMAND | MF_CHECKED ); + ::SetWindowText (g_qeglobals.d_hwndCamera, "Camera View"); + } + Sys_UpdateWindows (W_XY|W_CAMERA); +} + +void CMainFrame::OnViewShowDetailOnly() +{ + bool Excluded = !!(g_qeglobals.d_savedinfo.exclude & EXCLUDE_ALL_EXCEPT_DETAIL); + + if ( !(( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_ALL_EXCEPT_DETAIL ) & EXCLUDE_ALL_EXCEPT_DETAIL) ) + CheckMenuItem( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOW_DETAIL_ONLY, MF_BYCOMMAND | MF_UNCHECKED); + else + CheckMenuItem( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOW_DETAIL_ONLY, MF_BYCOMMAND | MF_CHECKED); + Sys_UpdateWindows (W_XY|W_CAMERA); +} + + +void CMainFrame::OnViewShowent() +{ + if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_ENT ) & EXCLUDE_ENT ) + CheckMenuItem( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWENT, MF_BYCOMMAND | MF_UNCHECKED); + else + CheckMenuItem( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWENT, MF_BYCOMMAND | MF_CHECKED); + Sys_UpdateWindows (W_XY|W_CAMERA); +} + +void CMainFrame::OnViewShowlights() +{ + if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_LIGHTS ) & EXCLUDE_LIGHTS ) + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWLIGHTS, MF_BYCOMMAND | MF_UNCHECKED ); + else + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWLIGHTS, MF_BYCOMMAND | MF_CHECKED ); + Sys_UpdateWindows (W_XY|W_CAMERA); +} + +extern bool gbSelectedLightBrushesAlwaysDrawRadii; +void CMainFrame::OnViewShowlightradii() +{ + if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_LIGHTS_RADII ) & EXCLUDE_LIGHTS_RADII ) + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWLIGHTRADII, MF_BYCOMMAND | MF_UNCHECKED ); + else + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWLIGHTRADII, MF_BYCOMMAND | MF_CHECKED ); + + bool bRadiiOn = !(g_qeglobals.d_savedinfo.exclude & EXCLUDE_LIGHTS_RADII); + Sys_Printf("( Show light radii = %s %s)\n",(!bRadiiOn?"OFF":"ON"),(!bRadiiOn && gbSelectedLightBrushesAlwaysDrawRadii)?"( unless selected )":""); + Sys_UpdateWindows (W_XY|W_CAMERA); +} + +void CMainFrame::OnViewShowlightradii_KB() +{ + if ( !( g_qeglobals.d_savedinfo.exclude & EXCLUDE_LIGHTS_RADII )) + { + gbSelectedLightBrushesAlwaysDrawRadii = !gbSelectedLightBrushesAlwaysDrawRadii; + } + + OnViewShowlightradii(); +} + + +void CMainFrame::OnViewShownames() +{ + g_qeglobals.d_savedinfo.show_names = !g_qeglobals.d_savedinfo.show_names; + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWNAMES, MF_BYCOMMAND | (g_qeglobals.d_savedinfo.show_names ? MF_CHECKED : MF_UNCHECKED) ); + Map_BuildBrushData(); + Sys_UpdateWindows (W_XY); +} + +void CMainFrame::OnViewShowpath() +{ + if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_PATHS ) & EXCLUDE_PATHS ) + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWPATH, MF_BYCOMMAND | MF_UNCHECKED ); + else + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWPATH, MF_BYCOMMAND | MF_CHECKED ); + Sys_UpdateWindows (W_XY|W_CAMERA); +} + +void CMainFrame::OnViewShowwater() +{ + if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_WATER ) & EXCLUDE_WATER ) + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWWATER, MF_BYCOMMAND | MF_UNCHECKED ); + else + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWWATER, MF_BYCOMMAND | MF_CHECKED ); + Sys_UpdateWindows (W_XY|W_CAMERA); +} + +void CMainFrame::OnViewShowworld() +{ + if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_WORLD ) & EXCLUDE_WORLD ) + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWWORLD, MF_BYCOMMAND | MF_UNCHECKED ); + else + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWWORLD, MF_BYCOMMAND | MF_CHECKED ); + Sys_UpdateWindows (W_XY|W_CAMERA); +} + +void CMainFrame::OnViewTexture() +{ + if (m_nCurrentStyle > 0 && m_nCurrentStyle < 3) // QE4 style + { + if (inspector_mode == W_TEXTURE && m_nCurrentStyle != QR_QE4) // are we in console mode already? + { + if (::IsWindowVisible(g_qeglobals.d_hwndEntity)) + ::ShowWindow(g_qeglobals.d_hwndEntity, SW_HIDE); + else + ::ShowWindow(g_qeglobals.d_hwndEntity, SW_SHOW); + } + else + { + ::ShowWindow(g_qeglobals.d_hwndEntity, SW_SHOW); + SetInspectorMode(W_TEXTURE); + } + } +} + +void CMainFrame::OnViewUpfloor() +{ + m_pCamWnd->Cam_ChangeFloor (true); +} + +void CMainFrame::OnViewXy() +{ + if (m_nCurrentStyle != 2) + { + m_pXYWnd->SetViewType(XY); + m_pXYWnd->PositionView(); + } + Sys_UpdateWindows (W_XY); +} + +void CMainFrame::OnViewZ100() +{ + z.scale = 1; + Sys_UpdateWindows (W_Z|W_Z_OVERLAY); +} + +void CMainFrame::OnViewZoomin() +{ + if (m_pXYWnd && m_pXYWnd->Active()) + { + m_pXYWnd->SetScale(m_pXYWnd->Scale() * 5.0 / 4); + if (m_pXYWnd->Scale() > MAX_ZOOM_SCALE) + m_pXYWnd->SetScale(MAX_ZOOM_SCALE); + } + + if (m_pXZWnd && m_pXZWnd->Active()) + { + m_pXZWnd->SetScale(m_pXZWnd->Scale() * 5.0 / 4); + if (m_pXZWnd->Scale() > MAX_ZOOM_SCALE) + m_pXZWnd->SetScale(MAX_ZOOM_SCALE); + } + + if (m_pYZWnd && m_pYZWnd->Active()) + { + m_pYZWnd->SetScale(m_pYZWnd->Scale() * 5.0 / 4); + if (m_pYZWnd->Scale() > MAX_ZOOM_SCALE) + m_pYZWnd->SetScale(MAX_ZOOM_SCALE); + } + + + Sys_UpdateWindows (W_XY|W_XY_OVERLAY); +} + +void CMainFrame::OnViewZoomout() +{ + + if (m_pXYWnd && m_pXYWnd->Active()) + { + m_pXYWnd->SetScale(m_pXYWnd->Scale() * 4.0 / 5); + if (m_pXYWnd->Scale() < MIN_ZOOM_SCALE) + m_pXYWnd->SetScale(MIN_ZOOM_SCALE); + } + + if (m_pXZWnd && m_pXZWnd->Active()) + { + m_pXZWnd->SetScale(m_pXZWnd->Scale() * 4.0 / 5); + if (m_pXZWnd->Scale() < MIN_ZOOM_SCALE) + m_pXZWnd->SetScale(MIN_ZOOM_SCALE); + } + + if (m_pYZWnd && m_pYZWnd->Active()) + { + m_pYZWnd->SetScale(m_pYZWnd->Scale() * 4.0 / 5); + if (m_pYZWnd->Scale() < MIN_ZOOM_SCALE) + m_pYZWnd->SetScale(MIN_ZOOM_SCALE); + } + Sys_UpdateWindows (W_XY|W_XY_OVERLAY); +} + +void CMainFrame::OnViewZzoomin() +{ + z.scale *= 5.0/4; + if (z.scale > MAX_ZOOM_SCALE) + z.scale = MAX_ZOOM_SCALE; + Sys_UpdateWindows (W_Z|W_Z_OVERLAY); +} + +void CMainFrame::OnViewZzoomout() +{ + z.scale *= 4.0/5; + if (z.scale < MIN_ZOOM_SCALE) + z.scale = MIN_ZOOM_SCALE; + Sys_UpdateWindows (W_Z|W_Z_OVERLAY); +} + +void CMainFrame::OnViewSide() +{ + if (m_nCurrentStyle != 2) + { + m_pXYWnd->SetViewType(XZ); + m_pXYWnd->PositionView(); + } + Sys_UpdateWindows (W_XY); +} + +void CMainFrame::OnGrid1(unsigned int nID) +{ + HMENU hMenu = ::GetMenu(GetSafeHwnd()); + + CheckMenuItem(hMenu, ID_GRID_1, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_2, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_4, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_8, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_16, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_32, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_64, MF_BYCOMMAND | MF_UNCHECKED); + + switch (nID) + { + case ID_GRID_1: g_qeglobals.d_gridsize = 0; break; + case ID_GRID_2: g_qeglobals.d_gridsize = 1; break; + case ID_GRID_4: g_qeglobals.d_gridsize = 2; break; + case ID_GRID_8: g_qeglobals.d_gridsize = 3; break; + case ID_GRID_16: g_qeglobals.d_gridsize = 4; break; + case ID_GRID_32: g_qeglobals.d_gridsize = 5; break; + case ID_GRID_64: g_qeglobals.d_gridsize = 6; break; + } + g_qeglobals.d_gridsize = 1 << g_qeglobals.d_gridsize; + + if (g_PrefsDlg.m_bSnapTToGrid) + g_qeglobals.d_savedinfo.m_nTextureTweak = g_qeglobals.d_gridsize; + + SetGridStatus(); + CheckMenuItem(hMenu, nID, MF_BYCOMMAND | MF_CHECKED); + Sys_UpdateWindows (W_XY|W_Z); + +} + +void CMainFrame::OnTexturesShowinuse() +{ + Sys_BeginWait (); + Texture_ShowInuse (); + if (m_pTexWnd) + m_pTexWnd->RedrawWindow(); +} + +//from TexWnd.cpp +extern qboolean texture_showinuse; +void CMainFrame::OnUpdateTexturesShowinuse(CCmdUI* pCmdUI) +{ +// pCmdUI->SetCheck(texture_showinuse); // this is bollocks, this menu is a command, not a bool-toggler +} + +void CMainFrame::OnUpdateMiscShowFaces(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(qbShowFaces); +} + +void CMainFrame::OnTexturesInspector() +{ + DoSurface (); +} + +void CMainFrame::OnMiscBenchmark() +{ + m_pCamWnd->BenchMark(); +} + +void CMainFrame::OnMiscFindbrush() +{ + DoFind(); +} + +void CMainFrame::OnMiscGamma() +{ + DoGamma(); + MessageBox("You must restart QERadiant for Gamma settings to take place"); +} + +void CMainFrame::OnMiscNextleakspot() +{ + Pointfile_Next(); +} + +void CMainFrame::OnMiscPreviousleakspot() +{ + Pointfile_Prev(); +} + +void CMainFrame::OnMiscPrintxy() +{ + WXY_Print(); +} + +void CMainFrame::OnMiscSelectentitycolor() +{ + if (edit_entity) + { + CString strColor = ValueForKey(edit_entity, "_color"); + if (strColor.GetLength() > 0) + { + float fR, fG, fB; + int n = sscanf(strColor,"%f %f %f", &fR, &fG, &fB); + if (n == 3) + { + g_qeglobals.d_savedinfo.colors[COLOR_ENTITY][0] = fR; + g_qeglobals.d_savedinfo.colors[COLOR_ENTITY][1] = fG; + g_qeglobals.d_savedinfo.colors[COLOR_ENTITY][2] = fB; + } + } + + if (inspector_mode == W_ENTITY && (DoColor(COLOR_ENTITY))) + { + char buffer[100]; + sprintf(buffer, "%f %f %f", g_qeglobals.d_savedinfo.colors[COLOR_ENTITY][0], + g_qeglobals.d_savedinfo.colors[COLOR_ENTITY][1], + g_qeglobals.d_savedinfo.colors[COLOR_ENTITY][2]); + + ::SetWindowText( hwndEnt[EntValueField], buffer ); + ::SetWindowText( hwndEnt[EntKeyField], "_color" ); + AddProp(); +//DK - SOF change to get color to entity quickly + //--::SetWindowText( hwndEnt[EntValueField], buffer ); +//-- ::SetWindowText( hwndEnt[EntKeyField], "color" ); +//-- AddProp(); + } + Sys_UpdateWindows( W_ALL ); + } +} + +void CMainFrame::OnTexturebk() +{ + DoColor(COLOR_TEXTUREBACK); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnColorsMajor() +{ + DoColor(COLOR_GRIDMAJOR); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnColorsMinor() +{ + DoColor(COLOR_GRIDMINOR); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnColorsXybk() +{ + DoColor(COLOR_GRIDBACK); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnBrush3sided() +{ + Brush_MakeSided(3); +} + +void CMainFrame::OnBrush4sided() +{ + Brush_MakeSided(4); + +} + +void CMainFrame::OnBrush5sided() +{ + Brush_MakeSided(5); +} + +void CMainFrame::OnBrush6sided() +{ + Brush_MakeSided(6); +} + +void CMainFrame::OnBrush7sided() +{ + Brush_MakeSided(7); +} + +void CMainFrame::OnBrush8sided() +{ + Brush_MakeSided(8); +} + +void CMainFrame::OnBrush9sided() +{ + Brush_MakeSided(9); +} + +void CMainFrame::OnBrushArbitrarysided() +{ + DoSides(); +} + +void CMainFrame::OnBrushFlipx() +{ + Select_FlipAxis (0); // turns curves inside-out + OnCurveNegative(); // outside-in them again :-) +} + +void CMainFrame::OnBrushFlipy() +{ + Select_FlipAxis (1); // turns curves inside-out + OnCurveNegative(); // outside-in them again :-) +} + +void CMainFrame::OnBrushFlipz() +{ + Select_FlipAxis (2); // turns curves inside-out + OnCurveNegative(); // outside-in them again :-) +} + +void CMainFrame::OnBrushRotatex() +{ + Select_RotateAxis (0, 90); +} + +void CMainFrame::OnBrushRotatey() +{ + Select_RotateAxis (1, 90); +} + +void CMainFrame::OnBrushRotatez() +{ + Select_RotateAxis (2, 90); +} + +void CMainFrame::OnRegionOff() +{ + Map_RegionOff (); +} + +void CMainFrame::OnRegionSetbrush() +{ + Map_RegionBrush (); +} + +void CMainFrame::OnRegionSetselection() +{ + Map_RegionSelectedBrushes (); +} + +void CMainFrame::OnRegionSettallbrush() +{ + Map_RegionTallBrush (); +} + +void CMainFrame::OnRegionSetxy() +{ + Map_RegionXY (); +} + +void CMainFrame::OnSelectionArbitraryrotation() +{ + if (ActiveXY()) + ActiveXY()->UndoCopy(); + CRotateDlg dlg; + dlg.DoModal(); + //DoRotate (); +} + +bool gbInhibitDupTargetCorrection = false; // important default!!!!!!!!! +void CMainFrame::OnSelectionClone_NoTargetNameChange() +{ +#ifdef QUAKE3 // probably best not to alter the SoF version so near to project end + if (ActiveXY()) + ActiveXY()->UndoCopy(); + + gbInhibitDupTargetCorrection = true; + Select_Clone (); + gbInhibitDupTargetCorrection = false; +#endif +} + +void CMainFrame::OnSelectionClone() +{ + if (ActiveXY()) + ActiveXY()->UndoCopy(); + Select_Clone (); +} + +void CMainFrame::OnSelectionConnect() +{ + ConnectEntities(); +} + +void CMainFrame::OnSelectionConnectSmart() +{ + ConnectEntities(true); +} + +void CMainFrame::OnSelectionCsgsubtract() +{ + if (ActiveXY()) + ActiveXY()->UndoCopy(); + CSG_Subtract (); +} + +void CMainFrame::OnSelectionDelete() +{ + if (ActiveXY()) + ActiveXY()->UndoCopy(); + Select_Delete (); +} + +void CMainFrame::OnSelectionDeselect() +{ + if (!ByeByeSurfaceDialog()) + { + if (g_bClipMode) + OnViewClipper(); + else + if (g_bRotateMode) + OnSelectMouserotate(); + else + if (g_bScaleMode) + OnSelectMousescale(); + else + if (g_bPathMode) + { + if (ActiveXY()) + ActiveXY()->KillPathMode(); + } + else + { + if (g_qeglobals.d_select_mode == sel_curvepoint && g_qeglobals.d_num_move_points > 0) + { + g_qeglobals.d_num_move_points = 0; + Sys_UpdateWindows(W_ALL); + } + else + { + Select_Deselect (); + SetStatusText(2, " "); + } + } + } +} + +void CMainFrame::OnSelectionDragedges() +{ + if (g_qeglobals.d_select_mode == sel_edge) + { + g_qeglobals.d_select_mode = sel_brush; + Sys_UpdateWindows (W_ALL); + } + else + { + SetupVertexSelection (); + if (g_qeglobals.d_numpoints) + g_qeglobals.d_select_mode = sel_edge; + Sys_UpdateWindows (W_ALL); + } +} + +void CMainFrame::OnSelectionDragvertecies() +{ + if (g_qeglobals.d_select_mode == sel_vertex || g_qeglobals.d_select_mode == sel_curvepoint) + { + g_qeglobals.d_select_mode = sel_brush; + Sys_UpdateWindows (W_ALL); + } + else + { + //--if (QE_SingleBrush() && selected_brushes.next->patchBrush) + if (OnlyPatchesSelected()) + { + Patch_EditPatch(); + } + else if (!AnyPatchesSelected()) + { + SetupVertexSelection (); + if (g_qeglobals.d_numpoints) + g_qeglobals.d_select_mode = sel_vertex; + } + Sys_UpdateWindows (W_ALL); + } +} + +void CMainFrame::OnSelectionMakeDetail() +{ +// Select_MakeDetail (); + Select_AutoCaulk(true); // bMakeDetail +} + +void CMainFrame::OnSelectionMakeStructural() +{ + Select_MakeStructural (); +} + +void CMainFrame::OnMakeNonSolid() +{ + Select_MakeNonSolid(); +} +void CMainFrame::OnClearNonSolid() +{ + Select_ClearNonSolid(); +} + +void CMainFrame::OnSelectionHideWaypointChildren() +{ + Select_HideWaypointChildren(); +} +void CMainFrame::OnSelectionUnHideWaypointChildren() +{ + Select_UnHideWaypointChildren(); +} +void CMainFrame::OnSelectionUnHideAllWaypoints() +{ + Select_UnHideAllWaypoints(); +} + +void CMainFrame::OnSelectionMakehollow() +{ + if (ActiveXY()) + ActiveXY()->UndoCopy(); + CSG_MakeHollow (); +} + +void CMainFrame::OnSelectionSelectcompletetall() +{ + if (ActiveXY()) + ActiveXY()->UndoCopy(); + Select_CompleteTall (); +} + +void CMainFrame::OnSelectionSelectinside() +{ + Select_Inside (); +} + +void CMainFrame::OnSelectionSelectpartialtall() +{ + Select_PartialTall (); +} + +void CMainFrame::OnSelectionSelecttouching() +{ + Select_Touching (); +} + +void CMainFrame::OnSelectionUngroupentity() +{ + Select_Ungroup (); +} + +void CMainFrame::OnSelectionGroupNamesDisplay() +{ + g_qeglobals.d_savedinfo.exclude ^= SHOW_GROUPNAMES; + Sys_UpdateWindows (W_XY|W_CAMERA); +} + +void CMainFrame::OnSelectionGroupNamesActive() +{ + bGrouping_IsActive = !bGrouping_IsActive; + if (bGrouping_IsActive) + { + Select_Deselect(); // needed to prevent problems with things that shouldn't be selectable? + } + Sys_UpdateWindows (W_XY|W_CAMERA); +} + +void CMainFrame::OnSelectionGroupNamesGhosted() +{ + bGrouping_IsGhosted = !bGrouping_IsGhosted; + Sys_UpdateWindows (W_XY|W_CAMERA); +} + +void CMainFrame::OnTexturesPopup() +{ + HandlePopup(this, IDR_POPUP_TEXTURE); +} + +void CMainFrame::OnPopupSelection() +{ + HandlePopup(this, IDR_POPUP_SELECTION); +} + +void CMainFrame::OnViewChange() +{ + OnViewNextview(); + //HandlePopup(this, IDR_POPUP_VIEW); +} + +void CMainFrame::OnViewCameraupdate() +{ + Sys_UpdateWindows(W_CAMERA); +} + +void CMainFrame::OnUpdateViewCameraupdate(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(m_bCamPreview == false); +} + +void CMainFrame::OnSizing(UINT fwSide, LPRECT pRect) +{ + CFrameWnd::OnSizing(fwSide, pRect); + GetClientRect(g_rctOld); +} + +void CMainFrame::OnHelpAbout() +{ + DoAbout(); +} + +void CMainFrame::OnViewClipper() +{ + if (ActiveXY()) + { + if (ActiveXY()->ClipMode()) + { + ActiveXY()->SetClipMode(false); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_VIEW_CLIPPER, FALSE); + } + else + { + if (ActiveXY()->RotateMode()) + OnSelectMouserotate(); + ActiveXY()->SetClipMode(true); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_VIEW_CLIPPER); + } + } +} + +void CMainFrame::OnCameraAngledown() +{ + m_pCamWnd->Camera().angles[0] -= SPEED_TURN; + if (m_pCamWnd->Camera().angles[0] < -85) + m_pCamWnd->Camera().angles[0] = -85; + Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY); +} + +void CMainFrame::OnCameraAngleup() +{ + m_pCamWnd->Camera().angles[0] += SPEED_TURN; + if (m_pCamWnd->Camera().angles[0] > 85) + m_pCamWnd->Camera().angles[0] = 85; + Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY); +} + +void CMainFrame::OnCameraBack() +{ + VectorMA (m_pCamWnd->Camera().origin, -SPEED_MOVE, m_pCamWnd->Camera().forward, m_pCamWnd->Camera().origin); + int nUpdate = (g_PrefsDlg.m_bCamXYUpdate) ? (W_CAMERA | W_XY) : (W_CAMERA); + Sys_UpdateWindows (nUpdate); +} + +void CMainFrame::OnCameraDown() +{ + m_pCamWnd->Camera().origin[2] -= SPEED_MOVE; + int nUpdate = (g_PrefsDlg.m_bCamXYUpdate) ? (W_CAMERA | W_XY) : (W_CAMERA); + Sys_UpdateWindows (nUpdate); +} + +void CMainFrame::OnCameraForward() +{ + VectorMA (m_pCamWnd->Camera().origin, SPEED_MOVE, m_pCamWnd->Camera().forward, m_pCamWnd->Camera().origin); + int nUpdate = (g_PrefsDlg.m_bCamXYUpdate) ? (W_CAMERA | W_XY) : (W_CAMERA); + Sys_UpdateWindows (nUpdate); +} + +void CMainFrame::OnCameraLeft() +{ + m_pCamWnd->Camera().angles[1] += SPEED_TURN; + int nUpdate = (g_PrefsDlg.m_bCamXYUpdate) ? (W_CAMERA | W_XY) : (W_CAMERA); + Sys_UpdateWindows (nUpdate); +} + +void CMainFrame::OnCameraRight() +{ + m_pCamWnd->Camera().angles[1] -= SPEED_TURN; + int nUpdate = (g_PrefsDlg.m_bCamXYUpdate) ? (W_CAMERA | W_XY) : (W_CAMERA); + Sys_UpdateWindows (nUpdate); +} + +void CMainFrame::OnCameraStrafeleft() +{ + VectorMA (m_pCamWnd->Camera().origin, -SPEED_MOVE, m_pCamWnd->Camera().right, m_pCamWnd->Camera().origin); + int nUpdate = (g_PrefsDlg.m_bCamXYUpdate) ? (W_CAMERA | W_XY) : (W_CAMERA); + Sys_UpdateWindows (nUpdate); +} + +void CMainFrame::OnCameraStraferight() +{ + VectorMA (m_pCamWnd->Camera().origin, SPEED_MOVE, m_pCamWnd->Camera().right, m_pCamWnd->Camera().origin); + int nUpdate = (g_PrefsDlg.m_bCamXYUpdate) ? (W_CAMERA | W_XY) : (W_CAMERA); + Sys_UpdateWindows (nUpdate); +} + +void CMainFrame::OnCameraUp() +{ + m_pCamWnd->Camera().origin[2] += SPEED_MOVE; + int nUpdate = (g_PrefsDlg.m_bCamXYUpdate) ? (W_CAMERA | W_XY) : (W_CAMERA); + Sys_UpdateWindows (nUpdate); +} + +void CMainFrame::OnGridToggle() +{ + g_qeglobals.d_showgrid = !g_qeglobals.d_showgrid; + Sys_UpdateWindows (W_XY|W_Z); +} + +void CMainFrame::OnEntitylist() +{ +} + +void CMainFrame::OnMapinfo() +{ +} + +void CMainFrame::OnPrefs() +{ + int nView = g_PrefsDlg.m_nView; + BOOL bToolbar = g_PrefsDlg.m_bWideToolbar; + BOOL bTextureBar = g_PrefsDlg.m_bTextureBar; + BOOL bSGIOpenGL = g_PrefsDlg.m_bSGIOpenGL; + BOOL bBuggyICD = g_PrefsDlg.m_bBuggyICD; + BOOL bShaderTest = g_PrefsDlg.m_bShaderTest; + g_PrefsDlg.LoadPrefs(); + if (g_PrefsDlg.DoModal() == IDOK) + { + if (g_PrefsDlg.m_nView != nView || g_PrefsDlg.m_bWideToolbar != bToolbar + || g_PrefsDlg.m_bSGIOpenGL != bSGIOpenGL || g_PrefsDlg.m_bBuggyICD != bBuggyICD || bShaderTest != g_PrefsDlg.m_bShaderTest) + MessageBox("You need to restart QERadiant for the view changes to take place."); + if (m_pTexWnd) + m_pTexWnd->UpdatePrefs(); + if (bTextureBar != g_PrefsDlg.m_bTextureBar) + { + if (bTextureBar) // was turned on + ShowControlBar(&m_wndTextureBar, TRUE, TRUE); + else // was turned off + ShowControlBar(&m_wndTextureBar, FALSE, TRUE); + m_wndTextureBar.Invalidate(); + } + } +} + +// 0 = radiant styel +// 1 = qe4 style +void CMainFrame::SetWindowStyle(int nStyle) +{ +} + +void CMainFrame::OnTogglecamera() +{ + if (m_nCurrentStyle > 0 && m_nCurrentStyle < 3) // QE4 style + { + if (m_pCamWnd && m_pCamWnd->GetSafeHwnd()) + { + if (m_pCamWnd->IsWindowVisible()) + m_pCamWnd->ShowWindow(SW_HIDE); + else + m_pCamWnd->ShowWindow(SW_SHOW); + } + } +} + +void CMainFrame::OnToggleconsole() +{ + if (m_nCurrentStyle > 0 && m_nCurrentStyle < 3) // QE4 style + { + if (m_pEditWnd && m_pEditWnd->GetSafeHwnd()) + { + if (m_pEditWnd->IsWindowVisible()) + m_pEditWnd->ShowWindow(SW_HIDE); + else + m_pEditWnd->ShowWindow(SW_SHOW); + } + } +} + +void CMainFrame::OnToggleview() +{ + if (m_nCurrentStyle == 1) // QE4 style + { + if (m_pXYWnd && m_pXYWnd->GetSafeHwnd()) + { + if (m_pXYWnd->IsWindowVisible()) + m_pXYWnd->ShowWindow(SW_HIDE); + else + m_pXYWnd->ShowWindow(SW_SHOW); + } + } +} + +void CMainFrame::OnTogglez() +{ + if (m_nCurrentStyle == 1 || m_nCurrentStyle == 2) // QE4 style + { + if (m_pZWnd && m_pZWnd->GetSafeHwnd()) + { + if (m_pZWnd->IsWindowVisible()) + m_pZWnd->ShowWindow(SW_HIDE); + else + m_pZWnd->ShowWindow(SW_SHOW); + } + } +} + +void CMainFrame::OnToggleLock() +{ + g_PrefsDlg.m_bTextureLock = !g_PrefsDlg.m_bTextureLock; + CMenu* pMenu = GetMenu(); + if (pMenu) + pMenu->CheckMenuItem(ID_TOGGLE_LOCK, MF_BYCOMMAND | (g_PrefsDlg.m_bTextureLock) ? MF_CHECKED : MF_UNCHECKED); + g_PrefsDlg.SavePrefs(); + +// Sys_Printf(va("Texture Lock: %s\n",g_PrefsDlg.m_bTextureLock?"ON":"OFF")); + SetGridStatus(); +} + +void CMainFrame::OnEditMapinfo() +{ + CMapInfo dlg; + dlg.DoModal(); +} + +void CMainFrame::OnEditEntityinfo() +{ + CEntityListDlg dlg; + dlg.DoModal(); +} + + + +void CMainFrame::OnBrushScripts() +{ + CScriptDlg dlg; + dlg.DoModal(); +} + +void CMainFrame::OnViewNextview() +{ + if (m_nCurrentStyle != 2) + { + if (m_pXYWnd->GetViewType() == XY) + m_pXYWnd->SetViewType(XZ); + else + if (m_pXYWnd->GetViewType() == XZ) + m_pXYWnd->SetViewType(YZ); + else + m_pXYWnd->SetViewType(XY); + m_pXYWnd->PositionView(); + Sys_UpdateWindows (W_XY); + } +} + +void CMainFrame::OnHelpCommandlist() +{ + CCommandsDlg dlg; + dlg.DoModal(); +#if 0 + if (g_b3Dfx) + { + C3DFXCamWnd* pWnd = new C3DFXCamWnd(); + CRect rect(50,50,400, 400); + pWnd->Create(_3DFXCAMERA_WINDOW_CLASS, "", QE3_CHILDSTYLE, rect, this, 1234); + pWnd->ShowWindow(SW_SHOW); + } +#endif +} + +void CMainFrame::OnFileNewproject() +{ + CNewProjDlg dlg; + if (dlg.DoModal() == IDOK && dlg.m_strName.GetLength() > 0) + { + CString strQ2; + CString strQ2File; + ExtractPath_and_Filename(g_PrefsDlg.m_strQuake2, strQ2, strQ2File); + + + AddSlash(strQ2); + strQ2 += dlg.m_strName; + CString strProjToLoad; + CString strMapToLoad; + bool bGood = true; + if (::CreateDirectory(strQ2, NULL)) + { + CString strDir = strQ2; + strDir += "\\maps"; + if (::CreateDirectory(strDir, NULL)) + { + CString strSource = g_strAppPath; + AddSlash(strSource); + strSource += "projmap.dat"; + CString strDest = strDir; + AddSlash(strDest); + CString strName; + strName.Format("%s.map", dlg.m_strName); + strDest += strName; + strMapToLoad = strDest; + if (!::CopyFile(strSource, strDest, FALSE)) + bGood = false; + } + else bGood = false; + + strDir = strQ2; + strDir += "\\pics"; + if (::CreateDirectory(strDir, NULL)) + { + CString strSource = g_strAppPath; + AddSlash(strSource); + strSource += "colormap.pcx"; + CString strDest = strDir; + AddSlash(strDest); + strDest += "colormap.pcx"; + if (!::CopyFile(strSource, strDest, FALSE)) + bGood = false; + } + else bGood = false; + + strDir = strQ2; + strDir += "\\scripts"; + if (::CreateDirectory(strDir, NULL)) + { + CString strSource = g_strAppPath; + AddSlash(strSource); + strSource += "projqe4.dat"; + CString strDest = strDir; + AddSlash(strDest); + strDest += "quake.qe4"; + if (!::CopyFile(strSource, strDest, FALSE)) + bGood = false; + else + strProjToLoad = strDest; + } + else bGood = false; + if (bGood && strProjToLoad.GetLength() > 0) + { + if (QE_LoadProject(strProjToLoad.GetBuffer(0))) + { + if (strMapToLoad.GetLength() > 0) + Map_LoadFile(strMapToLoad.GetBuffer(0)); + } + } + } + else + { + CString strMsg; + strMsg.Format("Unable to create directory %s", strQ2); + MessageBox(strMsg); + } + + } +} + +void CMainFrame::UpdateStatusText() +{ + for (int n = 0; n < 6; n++) + { + if (m_strStatus[n].GetLength() >= 0 && m_wndStatusBar.GetSafeHwnd()) + m_wndStatusBar.SetPaneText(n, m_strStatus[n]); + } +} + +void CMainFrame::SetStatusText(int nPane, const char * pText) +{ + if (pText && nPane <= 5 && nPane > 0) + { + m_strStatus[nPane] = pText; + UpdateStatusText(); + } +} + +void CMainFrame::UpdateWindows(int nBits) +{ + + if (!g_bScreenUpdates) + return; + + if (nBits & (W_XY | W_XY_OVERLAY)) + { + if (m_pXYWnd) + m_pXYWnd->RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); + if (m_pXZWnd) + m_pXZWnd->RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); + if (m_pYZWnd) + m_pYZWnd->RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); + } + + if (nBits & W_CAMERA || ((nBits & W_CAMERA_IFON) && m_bCamPreview)) + { + if (m_pCamWnd) + { + m_pCamWnd->RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); + } + } + + if (nBits & (W_Z | W_Z_OVERLAY)) + { + if (m_pZWnd) + m_pZWnd->RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); + } + + if (nBits & W_TEXTURE) + { + if (m_pTexWnd) + m_pTexWnd->RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); + } +} + +void Sys_UpdateWindows (int nBits) +{ + if (g_PrefsDlg.m_bQE4Painting) + g_nUpdateBits |= nBits; + else + g_pParentWnd->UpdateWindows(nBits); +} + + + +void CMainFrame::OnFlipClip() +{ + if (m_pActiveXY) + m_pActiveXY->FlipClip(); +} + +void CMainFrame::OnClipSelected() +{ + if (m_pActiveXY && m_pActiveXY->ClipMode()) + { + m_pActiveXY->Clip(); + } + else + { + if (g_bPatchBendMode) + Patch_BendHandleENTER(); + else if (g_bPatchBendMode) + Patch_InsDelHandleENTER(); + } +} + +void CMainFrame::OnSplitSelected() +{ + if (m_pActiveXY) + m_pActiveXY->SplitClip(); +} + +CXYWnd* CMainFrame::ActiveXY() +{ + return m_pActiveXY; +} + + +void CMainFrame::OnToggleviewXz() +{ + if (m_nCurrentStyle == 1) // QE4 style + { + if (m_pXZWnd && m_pXZWnd->GetSafeHwnd()) + { + // get windowplacement doesn't actually save this so we will here + g_PrefsDlg.m_bXZVis = m_pXZWnd->IsWindowVisible(); + if (g_PrefsDlg.m_bXZVis) + m_pXZWnd->ShowWindow(SW_HIDE); + else + m_pXZWnd->ShowWindow(SW_SHOW); + g_PrefsDlg.m_bXZVis ^= 1; + g_PrefsDlg.SavePrefs(); + } + } +} + +void CMainFrame::OnToggleviewYz() +{ + if (m_nCurrentStyle == 1) // QE4 style + { + if (m_pYZWnd && m_pYZWnd->GetSafeHwnd()) + { + g_PrefsDlg.m_bYZVis = m_pYZWnd->IsWindowVisible(); + if (g_PrefsDlg.m_bYZVis) + m_pYZWnd->ShowWindow(SW_HIDE); + else + m_pYZWnd->ShowWindow(SW_SHOW); + g_PrefsDlg.m_bYZVis ^= 1; + g_PrefsDlg.SavePrefs(); + } + } +} + +void CMainFrame::OnColorsBrush() +{ + DoColor(COLOR_BRUSHES); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnColorsClipper() +{ + DoColor(COLOR_CLIPPER); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnColorsGridtext() +{ + DoColor(COLOR_GRIDTEXT); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnColorsSelectedbrush() +{ + DoColor(COLOR_SELBRUSHES); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnColorsGridblock() +{ + DoColor(COLOR_GRIDBLOCK); + Sys_UpdateWindows (W_ALL); +} + + + +void CMainFrame::OnColorsViewname() +{ + DoColor(COLOR_VIEWNAME); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnColorSetoriginal() +{ + for (int i=0 ; i<3 ; i++) + { + g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][i] = 0.25; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][i] = 1.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR][i] = 0.75; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR][i] = 0.5; + g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][i] = 0.25; + } + + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][0] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][2] = 1.0; + + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][0] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][2] = 0.0; + + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][0] = 1.0; + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][2] = 0.0; + + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][0] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][2] = 1.0; + + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][0] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][2] = 0.0; + + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][0] = 0.5; + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][2] = 0.75; + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnColorSetqer() +{ + for (int i=0 ; i<3 ; i++) + { + g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][i] = 0.25; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][i] = 1.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR][i] = 1.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR][i] = 0.5; + g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][i] = 0.25; + } + + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][0] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][2] = 1.0; + + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][0] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][2] = 0.0; + + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][0] = 1.0; + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][2] = 0.0; + + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][0] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][2] = 1.0; + + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][0] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][2] = 0.0; + + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][0] = 0.5; + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][2] = 0.75; + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnColorSetblack() +{ + for (int i=0 ; i<3 ; i++) + { + g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][i] = 0.25; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][i] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR][i] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][i] = 0.25; + } + + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR][0] = 0.3; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR][1] = 0.5; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR][2] = 0.5; + + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][0] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][2] = 1.0; + + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][0] = 1.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][1] = 1.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][2] = 1.0; + + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][0] = 1.0; + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][2] = 0.0; + + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][0] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][2] = 1.0; + + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][0] = 1.0; + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][1] = 1.0; + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][2] = 1.0; + + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][0] = 0.7; + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][1] = 0.7; + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][2] = 0.0; + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnSnaptogrid() +{ + g_bSnapToGrid ^= 1; + CMenu* pMenu = GetMenu(); + if (pMenu) + pMenu->CheckMenuItem(ID_SNAPTOGRID, MF_BYCOMMAND | (g_bSnapToGrid) ? MF_CHECKED : MF_UNCHECKED); +} + + +void CMainFrame::OnSelectScale() +{ + if (ActiveXY()) + ActiveXY()->UndoCopy(); + CScaleDialog dlg; + if (dlg.DoModal() == IDOK) + { + if (dlg.m_fX > 0 && dlg.m_fY > 0 && dlg.m_fZ > 0) + { + Select_Scale(dlg.m_fX, dlg.m_fY, dlg.m_fZ); + Sys_UpdateWindows (W_ALL); + } + else + Sys_Printf("Warning.. Tried to scale by a zero value."); + } +} + +void CMainFrame::OnSelectMouserotate() +{ + if (ActiveXY()) + { + if (ActiveXY()->ClipMode()) + OnViewClipper(); + if (ActiveXY()->RotateMode()) + { + ActiveXY()->SetRotateMode(false); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SELECT_MOUSEROTATE, FALSE); + Map_BuildBrushData(); + } + else + { + ActiveXY()->SetRotateMode(true); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SELECT_MOUSEROTATE); + } + } +} + +void CMainFrame::OnEditCopybrush() +{ + if (ActiveXY()) + ActiveXY()->Copy(); +} + +void CMainFrame::OnEditPastebrush() +{ + if (ActiveXY()) + ActiveXY()->Paste(); +} + +void CMainFrame::OnEditPastebrushNoTargetnameChange() // a Mike G request +{ + gbInhibitDupTargetCorrection = true; + + if (ActiveXY()) + ActiveXY()->Paste(); + + gbInhibitDupTargetCorrection = false; +} + +void CMainFrame::OnEditUndo() +{ + if (ActiveXY()) + ActiveXY()->Undo(); +} + +void CMainFrame::OnUpdateEditUndo(CCmdUI* pCmdUI) +{ + BOOL bEnable = false; + if (ActiveXY()) + bEnable = ActiveXY()->UndoAvailable(); + pCmdUI->Enable(bEnable); +} + +void CMainFrame::OnSelectionTextureDec() +{ + g_qeglobals.d_savedinfo.m_nTextureTweak--; + if (g_qeglobals.d_savedinfo.m_nTextureTweak == 0) + g_qeglobals.d_savedinfo.m_nTextureTweak--; + SetTexValStatus(); + Sys_Printf("Texture scaling set to %d\n",g_qeglobals.d_savedinfo.m_nTextureTweak); +} + +void CMainFrame::OnSelectionTextureFit() +{ + // TODO: Add your command handler code here + +} + +void CMainFrame::OnSelectionTextureInc() +{ + g_qeglobals.d_savedinfo.m_nTextureTweak++; + if (g_qeglobals.d_savedinfo.m_nTextureTweak == 0) + g_qeglobals.d_savedinfo.m_nTextureTweak++; + SetTexValStatus(); + Sys_Printf("Texture scaling set to %d\n",g_qeglobals.d_savedinfo.m_nTextureTweak); +} + +void CMainFrame::OnSelectionTextureRotateclock() +{ + Select_RotateTexture(abs(g_PrefsDlg.m_nRotation)); +} + +void CMainFrame::OnSelectionTextureRotatecounter() +{ + Select_RotateTexture(-abs(g_PrefsDlg.m_nRotation)); +} + +void CMainFrame::OnSelectionTextureScaledown() +{ + Select_ScaleTexture(0, -abs(g_qeglobals.d_savedinfo.m_nTextureTweak)); +} + +void CMainFrame::OnSelectionTextureScaleup() +{ + Select_ScaleTexture(0, abs(g_qeglobals.d_savedinfo.m_nTextureTweak)); +} + +void CMainFrame::OnSelectionTextureScaleLeft() +{ + Select_ScaleTexture(-abs(g_qeglobals.d_savedinfo.m_nTextureTweak),0); +} + +void CMainFrame::OnSelectionTextureScaleRight() +{ + Select_ScaleTexture(abs(g_qeglobals.d_savedinfo.m_nTextureTweak),0); +} + + + +void CMainFrame::OnSelectionTextureShiftdown() +{ + Select_ShiftTexture(0, -abs(g_qeglobals.d_savedinfo.m_nTextureTweak)); +} + +void CMainFrame::OnSelectionTextureShiftleft() +{ + Select_ShiftTexture(-abs(g_qeglobals.d_savedinfo.m_nTextureTweak), 0); +} + +void CMainFrame::OnSelectionTextureShiftright() +{ + Select_ShiftTexture(abs(g_qeglobals.d_savedinfo.m_nTextureTweak), 0); +} + +void CMainFrame::OnSelectionTextureShiftup() +{ + Select_ShiftTexture(0, abs(g_qeglobals.d_savedinfo.m_nTextureTweak)); +} + +void CMainFrame::OnGridNext() +{ + if (g_qeglobals.d_gridsize < 64) + { + g_qeglobals.d_gridsize = g_qeglobals.d_gridsize << 1; + Sys_UpdateWindows(W_XY | W_Z); + SetGridStatus(); + + HMENU hMenu = ::GetMenu(GetSafeHwnd()); + CheckMenuItem(hMenu, ID_GRID_1, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_2, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_4, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_8, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_16, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_32, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_64, MF_BYCOMMAND | MF_UNCHECKED); + + int nID; + switch (g_qeglobals.d_gridsize) + { + case 1: nID = ID_GRID_1; break; + case 2: nID = ID_GRID_2; break; + case 4: nID = ID_GRID_4; break; + case 8: nID = ID_GRID_8; break; + case 16: nID = ID_GRID_16; break; + case 32: nID = ID_GRID_32; break; + case 64: nID = ID_GRID_64; break; + } + CheckMenuItem(hMenu, nID, MF_BYCOMMAND | MF_CHECKED); + } +} + +void CMainFrame::OnGridPrev() +{ + if (g_qeglobals.d_gridsize > 1) + { + g_qeglobals.d_gridsize = g_qeglobals.d_gridsize >> 1; + Sys_UpdateWindows(W_XY | W_Z); + SetGridStatus(); + HMENU hMenu = ::GetMenu(GetSafeHwnd()); + CheckMenuItem(hMenu, ID_GRID_1, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_2, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_4, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_8, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_16, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_32, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_64, MF_BYCOMMAND | MF_UNCHECKED); + + int nID; + switch (g_qeglobals.d_gridsize) + { + case 1: nID = ID_GRID_1; break; + case 2: nID = ID_GRID_2; break; + case 4: nID = ID_GRID_4; break; + case 8: nID = ID_GRID_8; break; + case 16: nID = ID_GRID_16; break; + case 32: nID = ID_GRID_32; break; + case 64: nID = ID_GRID_64; break; + } + CheckMenuItem(hMenu, nID, MF_BYCOMMAND | MF_CHECKED); + } +} + + +void CMainFrame::SetGridStatus() +{ + CString strStatus; + char c1; + char c2; + c1 = (g_PrefsDlg.m_bTextureLock) ? 'M' : ' '; + c2 = (g_PrefsDlg.m_bRotateLock) ? 'R' : ' '; + strStatus.Format("G:%i T:%i R:%i C:%i L:%c%c", g_qeglobals.d_gridsize, g_qeglobals.d_savedinfo.m_nTextureTweak, g_PrefsDlg.m_nRotation, g_PrefsDlg.m_nCubicScale, c1, c2); + SetStatusText(4, strStatus); +} + + +void CMainFrame::SetTexValStatus() +{ + //CString strStatus; + //strStatus.Format("T: %i C: %i", g_nTextureTweak, g_nCubicScale); + //SetStatusText(5, strStatus.GetBuffer(0)); + SetGridStatus(); +} + +void CMainFrame::OnTextureReplaceall() +{ + CFindTextureDlg::show(); +} + + +void CMainFrame::OnScalelockx() +{ + if (g_nScaleHow & SCALE_X) + { + g_nScaleHow ^= SCALE_X; + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SCALELOCKX, FALSE); + } + else + { + g_nScaleHow |= SCALE_X; + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SCALELOCKX); + } +} + +void CMainFrame::OnScalelocky() +{ + if (g_nScaleHow & SCALE_Y) + { + g_nScaleHow ^= SCALE_Y; + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SCALELOCKY, FALSE); + } + else + { + g_nScaleHow |= SCALE_Y; + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SCALELOCKY); + } +} + +void CMainFrame::OnScalelockz() +{ + if (g_nScaleHow & SCALE_Z) + { + g_nScaleHow ^= SCALE_Z; + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SCALELOCKZ, FALSE); + } + else + { + g_nScaleHow |= SCALE_Z; + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SCALELOCKZ); + } +} + +void CMainFrame::OnSelectMousescale() +{ + if (ActiveXY()) + { + if (ActiveXY()->ClipMode()) + OnViewClipper(); + if (ActiveXY()->RotateMode()) + { + ActiveXY()->SetRotateMode(false); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SELECT_MOUSESCALE, FALSE); + } + if (ActiveXY()->ScaleMode()) + { + ActiveXY()->SetScaleMode(false); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SELECT_MOUSESCALE, FALSE); + } + else + { + ActiveXY()->SetScaleMode(true); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SELECT_MOUSESCALE); + } + } +} + +void CMainFrame::OnFileImport() +{ +} + +void CMainFrame::OnFileProjectsettings() +{ + DoProjectSettings(); +} + +void CMainFrame::OnUpdateFileImport(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(FALSE); +} + +#define CUBIC_MAX_LIMIT (WORLD_SIZE/64) + +void CMainFrame::OnViewCubein() +{ + g_PrefsDlg.m_nCubicScale--; + if (g_PrefsDlg.m_nCubicScale < 1) + g_PrefsDlg.m_nCubicScale = 1; + Sys_Printf("CubicScale: %d/%d\n",g_PrefsDlg.m_nCubicScale,CUBIC_MAX_LIMIT); + g_PrefsDlg.SavePrefs(); + Sys_UpdateWindows(W_CAMERA); + SetTexValStatus(); +} + +void CMainFrame::OnViewCubeout() +{ + g_PrefsDlg.m_nCubicScale++; + if (g_PrefsDlg.m_nCubicScale > CUBIC_MAX_LIMIT) + g_PrefsDlg.m_nCubicScale = CUBIC_MAX_LIMIT; + + Sys_Printf("CubicScale: %d/%d\n",g_PrefsDlg.m_nCubicScale,CUBIC_MAX_LIMIT); + g_PrefsDlg.SavePrefs(); + Sys_UpdateWindows(W_CAMERA); + SetTexValStatus(); +} + +void CMainFrame::OnViewCubein10() +{ + g_PrefsDlg.m_nCubicScale-=10; + if (g_PrefsDlg.m_nCubicScale < 1) + g_PrefsDlg.m_nCubicScale = 1; + Sys_Printf("CubicScale: %d/%d\n",g_PrefsDlg.m_nCubicScale,CUBIC_MAX_LIMIT); + g_PrefsDlg.SavePrefs(); + Sys_UpdateWindows(W_CAMERA); + SetTexValStatus(); +} + +void CMainFrame::OnViewCubeout10() +{ + g_PrefsDlg.m_nCubicScale+=10; + if (g_PrefsDlg.m_nCubicScale > CUBIC_MAX_LIMIT) + g_PrefsDlg.m_nCubicScale = CUBIC_MAX_LIMIT; + + Sys_Printf("CubicScale: %d/%d\n",g_PrefsDlg.m_nCubicScale,CUBIC_MAX_LIMIT); + g_PrefsDlg.SavePrefs(); + Sys_UpdateWindows(W_CAMERA); + SetTexValStatus(); +} + + +void CMainFrame::OnViewCubicclipping() +{ + g_PrefsDlg.m_bCubicClipping ^= 1; + CMenu* pMenu = GetMenu(); + if (pMenu) + pMenu->CheckMenuItem(ID_VIEW_CUBICCLIPPING, MF_BYCOMMAND | (g_PrefsDlg.m_bCubicClipping) ? MF_CHECKED : MF_UNCHECKED); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_VIEW_CUBICCLIPPING, (g_PrefsDlg.m_bCubicClipping) ? TRUE : FALSE); + g_PrefsDlg.SavePrefs(); + Map_BuildBrushData (); + Sys_UpdateWindows(W_CAMERA); +} + + +void CMainFrame::OnFileSaveregion() +{ + SaveAsDialog (true); +} + +void CMainFrame::OnUpdateFileSaveregion(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(static_cast(region_active)); +} + +void CMainFrame::OnSelectionMovedown() +{ + vec3_t vAmt; + vAmt[0] = vAmt[1] = 0.0; + vAmt[2] = -g_qeglobals.d_gridsize; + Select_Move (vAmt); + Sys_UpdateWindows(W_CAMERA | W_XY | W_Z); +} + +void CMainFrame::OnSelectionMoveup() +{ + vec3_t vAmt; + vAmt[0] = vAmt[1] = 0.0; + vAmt[2] = g_qeglobals.d_gridsize; + Select_Move (vAmt); + Sys_UpdateWindows(W_CAMERA | W_XY | W_Z); +} + +void CMainFrame::OnToolbarMain() +{ + +} + +void CMainFrame::OnToolbarTexture() +{ + +} + +void CMainFrame::OnSelectionPrint() +{ + for (brush_t* b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + Brush_Print(b); +} + +void CMainFrame::UpdateTextureBar() +{ + if (m_wndTextureBar.GetSafeHwnd()) + m_wndTextureBar.GetSurfaceAttributes(); +} + +bool g_bTABDown = false; +bool g_bOriginalFlag; +void CMainFrame::OnSelectionTogglesizepaint() +{ + if (::GetAsyncKeyState('Q')) + { + if (!g_bTABDown) + { + g_bTABDown = true; + g_bOriginalFlag = g_PrefsDlg.m_bSizePaint; + g_PrefsDlg.m_bSizePaint = !g_bOriginalFlag; + Sys_UpdateWindows(W_XY); + return; + } + } + else + { + g_bTABDown = false; + g_PrefsDlg.m_bSizePaint = g_bOriginalFlag; + Sys_UpdateWindows(W_XY); + return; + } +} + +void CMainFrame::OnBrushMakecone() +{ + DoSides(true); +} + + +void CMainFrame::OnTexturesLoad() +{ + BROWSEINFO bi; + CString strPath; + char* p = strPath.GetBuffer(MAX_PATH+1); + bi.hwndOwner = GetSafeHwnd(); + bi.pidlRoot = NULL; + bi.pszDisplayName = p; + bi.lpszTitle = "Load textures from path"; + bi.ulFlags = 0; + bi.lpfn = NULL; + bi.lParam = NULL; + bi.iImage = 0; + LPITEMIDLIST pidlBrowse; + pidlBrowse = SHBrowseForFolder(&bi); + if (pidlBrowse) + { + SHGetPathFromIDList(pidlBrowse, p); + strPath.ReleaseBuffer(); + AddSlash(strPath); + Texture_ShowDirectory(strPath.GetBuffer(0)); + } +} + +void CMainFrame::OnToggleRotatelock() +{ + g_PrefsDlg.m_bRotateLock ^= 1; + CMenu* pMenu = GetMenu(); + if (pMenu) + pMenu->CheckMenuItem(ID_TOGGLE_ROTATELOCK, MF_BYCOMMAND | (g_PrefsDlg.m_bRotateLock) ? MF_CHECKED : MF_UNCHECKED); + g_PrefsDlg.SavePrefs(); + +// Sys_Printf(va("Texture Rotation Lock: %s\n",g_PrefsDlg.m_bRotateLock?"ON":"OFF")); + SetGridStatus(); +} + + +void CMainFrame::OnCurveBevel() +{ + Curve_MakeCurvedBrush (false, false, false, false, false, true, true); +} + +void CMainFrame::OnCurveCylinder() +{ + Curve_MakeCurvedBrush (false, false, false, true, true, true, true); +} + +void CMainFrame::OnCurveEighthsphere() +{ + Curve_MakeCurvedBrush (false, true, false, true, true, false, false); +} + +void CMainFrame::OnCurveEndcap() +{ + Curve_MakeCurvedBrush (false, false, false, false, true, true, true); +} + +void CMainFrame::OnCurveHemisphere() +{ + Curve_MakeCurvedBrush (false, true, false, true, true, true, true); +} + +void CMainFrame::OnCurveInvertcurve() +{ + Curve_Invert (); +} + +void CMainFrame::OnCurveQuarter() +{ + Curve_MakeCurvedBrush (false, true, false, true, true, true, false); +} + +void CMainFrame::OnCurveSphere() +{ + Curve_MakeCurvedBrush (false, true, true, true, true, true, true); +} + +void CMainFrame::OnFileImportmap() +{ + CFileDialog dlgFile(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "Map files (*.map)|*.map||", this); + if (dlgFile.DoModal() == IDOK) + { + Map_ImportFile(dlgFile.GetPathName().GetBuffer(0)); + } +} + +void CMainFrame::OnFileExportmap() +{ + CFileDialog dlgFile(FALSE, "map", NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "Map files (*.map)|*.map||", this); + if (dlgFile.DoModal() == IDOK) + { + Map_SaveSelected(dlgFile.GetPathName().GetBuffer(0)); + } +} + +void CMainFrame::OnEditLoadprefab() +{ + char CurPath[1024]; + ::GetCurrentDirectory(1024, CurPath); + if (g_PrefsDlg.m_strPrefabPath.GetLength() > 0) + ::SetCurrentDirectory(g_PrefsDlg.m_strPrefabPath); + OnFileImportmap(); + ::SetCurrentDirectory(CurPath); +} + +void CMainFrame::OnViewShowcurves() +{ + if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_CURVES ) & EXCLUDE_CURVES ) + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWCURVES, MF_BYCOMMAND | MF_UNCHECKED ); + else + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWCURVES, MF_BYCOMMAND | MF_CHECKED ); + Sys_UpdateWindows (W_XY|W_CAMERA); +} + +void CMainFrame::OnViewShowWayPoints() +{ + if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_WAYPOINTS ) & EXCLUDE_WAYPOINTS ) + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWWAYPOINTS, MF_BYCOMMAND | MF_UNCHECKED ); + else + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWWAYPOINTS, MF_BYCOMMAND | MF_CHECKED ); + Sys_UpdateWindows (W_XY|W_CAMERA); +} + + +void CMainFrame::OnViewShowWayPointsOnly() +{ + if ( ( g_qeglobals.d_savedinfo.exclude ^= SHOW_WAYPOINTS_ONLY ) & SHOW_WAYPOINTS_ONLY ) + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWWAYPOINTS_ONLY, MF_BYCOMMAND | MF_CHECKED ); + else + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWWAYPOINTS_ONLY, MF_BYCOMMAND | MF_UNCHECKED ); + Sys_UpdateWindows (W_XY|W_CAMERA); +} + +void CMainFrame::OnViewShowMiscModels() +{ + if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_MISCMODELS ) & EXCLUDE_MISCMODELS ) + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWMISC_MODEL, MF_BYCOMMAND | MF_UNCHECKED ); + else + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWMISC_MODEL, MF_BYCOMMAND | MF_CHECKED ); + Sys_UpdateWindows (W_XY|W_CAMERA); +} + +void CMainFrame::OnViewShowMiscModelBreakables() +{ + if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_MISCMODELBREAKABLES ) & EXCLUDE_MISCMODELBREAKABLES ) + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWMISC_MODEL_BREAKABLE, MF_BYCOMMAND | MF_UNCHECKED ); + else + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWMISC_MODEL_BREAKABLE, MF_BYCOMMAND | MF_CHECKED ); + Sys_UpdateWindows (W_XY|W_CAMERA); +} + +void CMainFrame::OnViewShowMiscModelXXXX() +{ + if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_MISCMODELXXXX ) & EXCLUDE_MISCMODELXXXX ) + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWMISC_MODEL_XXXX, MF_BYCOMMAND | MF_UNCHECKED ); + else + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWMISC_MODEL_XXXX, MF_BYCOMMAND | MF_CHECKED ); + Sys_UpdateWindows (W_XY|W_CAMERA); +} + +void CMainFrame::OnViewShowTriggerXXXX() +{ + if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_TRIGGERXXXX ) & EXCLUDE_TRIGGERXXXX ) + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWTRIGGER_XXXX, MF_BYCOMMAND | MF_UNCHECKED ); + else + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWTRIGGER_XXXX, MF_BYCOMMAND | MF_CHECKED ); + Sys_UpdateWindows (W_XY|W_CAMERA); +} + +void CMainFrame::OnViewShowTargetSpeakers() +{ + if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_TARGETSPEAKERS ) & EXCLUDE_TARGETSPEAKERS ) + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWTARGET_SPEAKER, MF_BYCOMMAND | MF_UNCHECKED ); + else + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWTARGET_SPEAKER, MF_BYCOMMAND | MF_CHECKED ); + Sys_UpdateWindows (W_XY|W_CAMERA); +} + +void CMainFrame::OnViewShowRefTags() +{ + if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_REFTAGS ) & EXCLUDE_REFTAGS ) + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWREF_TAGS, MF_BYCOMMAND | MF_UNCHECKED ); + else + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWREF_TAGS, MF_BYCOMMAND | MF_CHECKED ); + Sys_UpdateWindows (W_XY|W_CAMERA); +} + +void CMainFrame::OnViewShowNonSolid() +{ + if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_NONSOLID ) & EXCLUDE_NONSOLID ) + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOW_NONSOLID, MF_BYCOMMAND | MF_UNCHECKED ); + else + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOW_NONSOLID, MF_BYCOMMAND | MF_CHECKED ); + + Sys_UpdateWindows (W_XY|W_CAMERA); +} + +void CMainFrame::OnViewShowEasy() +{ + if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_EASY ) & EXCLUDE_EASY ) + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_SHOW_EASY, MF_BYCOMMAND | MF_UNCHECKED ); + else + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_SHOW_EASY, MF_BYCOMMAND | MF_CHECKED ); + + Sys_UpdateWindows (W_XY|W_CAMERA); +} + +void CMainFrame::OnViewShowMedium() +{ + if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_MEDIUM ) & EXCLUDE_MEDIUM ) + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_SHOW_MEDIUM, MF_BYCOMMAND | MF_UNCHECKED ); + else + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_SHOW_MEDIUM, MF_BYCOMMAND | MF_CHECKED ); + + Sys_UpdateWindows (W_XY|W_CAMERA); +} + +void CMainFrame::OnViewShowHard() +{ + if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_HARD ) & EXCLUDE_HARD ) + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_SHOW_HARD, MF_BYCOMMAND | MF_UNCHECKED ); + else + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_SHOW_HARD, MF_BYCOMMAND | MF_CHECKED ); + + Sys_UpdateWindows (W_XY|W_CAMERA); +} + +void CMainFrame::OnSelectionSelectNudgedown() +{ + NudgeSelection(3, g_qeglobals.d_savedinfo.m_nTextureTweak); +} + +void CMainFrame::OnSelectionSelectNudgeleft() +{ + NudgeSelection(0, g_qeglobals.d_savedinfo.m_nTextureTweak); +} + +void CMainFrame::OnSelectionSelectNudgeright() +{ + NudgeSelection(2, g_qeglobals.d_savedinfo.m_nTextureTweak); +} + +void CMainFrame::OnSelectionSelectNudgeup() +{ + NudgeSelection(1, g_qeglobals.d_savedinfo.m_nTextureTweak); +} + +void CMainFrame::NudgeSelection(int nDirection, int nAmount) +{ + if (ActiveXY()->RotateMode()) + { + int nAxis = 0; + if (ActiveXY()->GetViewType() == XY) + { + nAxis = 2; + } + else + if (g_pParentWnd->ActiveXY()->GetViewType() == XZ) + { + nAxis = 1; + nAmount = -nAmount; + } + + if (nDirection == 2 || nDirection == 3) + { + nAmount = -nAmount; + } + + float fDeg = -nAmount; + float fAdj = nAmount; + + g_pParentWnd->ActiveXY()->Rotation()[nAxis] += fAdj; + CString strStatus; + strStatus.Format("Rotation x:: %.1f y:: %.1f z:: %.1f", g_pParentWnd->ActiveXY()->Rotation()[0], g_pParentWnd->ActiveXY()->Rotation()[1], g_pParentWnd->ActiveXY()->Rotation()[2]); + g_pParentWnd->SetStatusText(2, strStatus); + Select_RotateAxis(nAxis, fDeg, false, true); + Sys_UpdateWindows (W_ALL); + } + else + if (ActiveXY()->ScaleMode()) + { + if (nDirection == 0 || nDirection == 3) + { + nAmount = -nAmount; + } + vec3_t v; + v[0] = v[1] = v[2] = 1.0; + if (nAmount > 0) + { + v[0] = 1.1; + v[1] = 1.1; + v[2] = 1.1; + } + else + { + v[0] = 0.9; + v[1] = 0.9; + v[2] = 0.9; + } + + Select_Scale((g_nScaleHow & SCALE_X) ? v[0] : 1.0, + (g_nScaleHow & SCALE_Y) ? v[1] : 1.0, + (g_nScaleHow & SCALE_Z) ? v[2] : 1.0); + Sys_UpdateWindows (W_ALL); + } + else + { + // 0 - left, 1 - up, 2 - right, 3 - down + int nDim; + if (nDirection == 0) + { + nDim = ActiveXY()->GetViewType() == YZ ? 1 : 0; + nAmount = -nAmount; + } + else if (nDirection == 1) + { + nDim = ActiveXY()->GetViewType() == XY ? 1 : 2; + } + else if (nDirection == 2) + { + nDim = ActiveXY()->GetViewType() == YZ ? 1 : 0; + } + else + { + nDim = ActiveXY()->GetViewType() == XY ? 1 : 2; + nAmount = -nAmount; + } + Nudge(nDim, nAmount); + } +} + + +BOOL CMainFrame::PreTranslateMessage(MSG* pMsg) +{ + return CFrameWnd::PreTranslateMessage(pMsg); +} + +void CMainFrame::Nudge(int nDim, float fNudge) +{ + vec3_t vMove; + vMove[0] = vMove[1] = vMove[2] = 0; + vMove[nDim] = fNudge; + Select_Move(vMove, true); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnTexturesLoadlist() +{ + CDialogTextures dlg; + if (dlg.DoModal() == IDOK && dlg.m_nSelection >= 0) + { + Texture_ShowDirectory(dlg.m_nSelection + CMD_TEXTUREWAD); + } +} + +void CMainFrame::OnDontselectcurve() +{ + g_PrefsDlg.m_bSelectCurves ^= 1; + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_DONTSELECTCURVE, (g_PrefsDlg.m_bSelectCurves) ? FALSE : TRUE); +} + +void CMainFrame::OnShowFaces() +{ + qbShowFaces = !qbShowFaces; + Sys_UpdateWindows(W_CAMERA); +} + + + +CString strFindKey; +CString strFindValue; +CString strReplaceKey; +CString strReplaceValue; +bool gbWholeStringMatchOnly = true; +bool gbSelectAllMatchingEnts= false; +brush_t* gpPrevEntBrushFound = NULL; + +// all this because there's no ansi stristr(), sigh... +// +LPCSTR String_ToLower(LPCSTR psString) +{ + const int iBufferSize = 4096; + static char sString[8][iBufferSize]; + static int iIndex=0; + + if (strlen(psString)>=iBufferSize) + { + assert(0); + Sys_Printf("String_ToLower(): Warning, input string was %d bytes too large, performing strlwr() inline!\n",strlen(psString)-(iBufferSize-1)); + return strlwr(const_cast(psString)); + } + + iIndex=++iIndex&7; + + strcpy(sString[iIndex],psString); + strlwr(sString[iIndex]); + + return sString[iIndex]; +} + +bool FindNextBrush(brush_t* pPrevFoundBrush) // can be NULL for fresh search +{ + bool bFoundSomething = false; + entity_t *pLastFoundEnt; + brush_t *pLastFoundBrush; + + CWaitCursor waitcursor; + + Select_Deselect(true); // bool bDeSelectToListBack + + // see whether to start search from prev_brush->next by checking if prev_brush is still in the active list... + // + brush_t *pStartBrush = active_brushes.next; +/* + if (pPrevFoundBrush && !gbSelectAllMatchingEnts) + { + brush_t *pPrev = NULL; + for (brush_t* b = active_brushes.next ; b != &active_brushes ; b = b->next) + { + if (pPrev == pPrevFoundBrush && pPrevFoundBrush) + { + pStartBrush = b; + break; + } + pPrev = b; + } + } +*/ + // now do the search proper... + // + int iBrushesScanned = 0; + int iBrushesSelected=0; + int iEntsScanned = 0; + + brush_t* pNextBrush; + for (brush_t* b = pStartBrush; b != &active_brushes ; b = pNextBrush) + { + // setup the ptr before going any further (because selecting a brush down below moves it to a + // different link list), but we need to ensure that the next brush has a different ent-owner than the current + // one, or multi-brush ents will confuse the list process if they get selected (infinite loop badness)... + // + // pNextBrush = &active_brushes; // default to loop-stop condition + pNextBrush = b->next; + while (pNextBrush->owner == b->owner && pNextBrush!=&active_brushes) + { + pNextBrush = pNextBrush->next; + } + + iBrushesScanned++; + + // a simple progress bar so they don't think it's locked up on long searches... + // + static int iDotBodge=0; + if (!(++iDotBodge&15)) + Sys_Printf("."); // cut down on printing + + bool bMatch = false; + entity_t* ent = b->owner; + + if (ent && ent!= world_entity) // needed! + { + iEntsScanned++; + if (FilterBrush (b)) + continue; + + // only check the find-key if there was one specified... + // + if (!strFindKey.IsEmpty()) + { + char *psEntFoundValue = ValueForKey(ent, strFindKey); + + if (strlen(psEntFoundValue) + && + ( +// (stricmp(strFindValue, psEntFoundValue)==0) // found this exact key/value + ( + (gbWholeStringMatchOnly && stricmp(psEntFoundValue, strFindValue)==0) + || + (!gbWholeStringMatchOnly && strstr(String_ToLower(psEntFoundValue), String_ToLower(strFindValue))) + ) + || // or + (strFindValue.IsEmpty()) // any value for this key if blank value search specified + ) + ) + { + bMatch = true; + } + } + else + { + // no FIND key specified, so just scan all of them... + // + int iNumEntKeys = GetNumKeys(ent); + for (int i=0; i search specified then any found-value is ok + || + (gbWholeStringMatchOnly && stricmp(psEntFoundValue, strFindValue)==0) + || + (!gbWholeStringMatchOnly && strstr(String_ToLower(psEntFoundValue), String_ToLower(strFindValue))) + ) + { + if (!gbWholeStringMatchOnly && strstr(String_ToLower(psEntFoundValue), String_ToLower(strFindValue))) + { +// OutputDebugString(va("Matching because: psEntFoundValue '%s' & strFindValue '%s'\n",psEntFoundValue, strFindValue)); +// Sys_Printf("Matching because: psEntFoundValue '%s' & strFindValue '%s'\n",psEntFoundValue, strFindValue); + +// if (strstr(psEntFoundValue,"killsplat")) +// { +// DebugBreak(); +// } + } + bMatch = true; + break; + } + } + } + } + + if (bMatch) + { + bFoundSomething = true; + pLastFoundEnt = ent; + pLastFoundBrush = b; + iBrushesSelected++; + + g_bScreenUpdates = false; // !!!!!!!!!!!!!!!!!!!!!!!!!!!! + + Select_Brush(b); + + g_bScreenUpdates = true; // !!!!!!!!!!!!!!!!!!!!!!!!!!!! + + if (!gbSelectAllMatchingEnts) + break; + } + } + } + if (gbSelectAllMatchingEnts) + { + Sys_Printf("\nBrushes Selected: %d (Brushes Scanned %d, Ents Scanned %d)\n", iBrushesSelected, iBrushesScanned, iEntsScanned); + } + + if (bFoundSomething) + { + vec3_t v3Origin; + + if (pLastFoundEnt->origin[0] != 0.0f || pLastFoundEnt->origin[1] != 0.0f || pLastFoundEnt->origin[2] != 0.0f) + { + VectorCopy(pLastFoundEnt->origin,v3Origin); + } + else + { + // pLastFoundEnt's origin is zero, so use average point of brush mins maxs instead... + // + v3Origin[0] = (pLastFoundBrush->mins[0] + pLastFoundBrush->maxs[0])/2; + v3Origin[1] = (pLastFoundBrush->mins[1] + pLastFoundBrush->maxs[1])/2; + v3Origin[2] = (pLastFoundBrush->mins[2] + pLastFoundBrush->maxs[2])/2; + } + + // got one, jump the camera to it... + // + VectorCopy(v3Origin, g_pParentWnd->GetCamera()->Camera().origin); + g_pParentWnd->GetCamera()->Camera().origin[1] -= 32; // back off a touch to look at it + VectorSet (g_pParentWnd->GetCamera()->Camera().angles,0,90,0); + + // force main screen into XY camera mode (just in case)... + // + g_pParentWnd->SetActiveXY(g_pParentWnd->GetXYWnd()); + g_pParentWnd->GetXYWnd()->PositionView(); + + Sys_UpdateWindows (W_ALL); + // + // and record for next find request (F3)... + // + gpPrevEntBrushFound = pLastFoundBrush; + } + + return bFoundSomething; +} + +void CMainFrame::OnFindEnt() +{ + CEntKeyFindReplace FindReplace(&strFindKey, &strFindValue, &strReplaceKey, &strReplaceValue, &gbWholeStringMatchOnly, &gbSelectAllMatchingEnts); + + switch (FindReplace.DoModal()) + { + case ID_RET_REPLACE: + { + brush_t* next = NULL; + int iOccurences = 0; + for (brush_t* b = active_brushes.next ; b != &active_brushes ; b = next) + { + next = b->next; // important to do this here, in case brush gets linked to a different list + entity_t* ent = b->owner; + + if (ent) // needed! + { + if (FilterBrush (b)) + continue; + + char *psEntFoundValue = ValueForKey(ent, strFindKey); + + if (stricmp(strFindValue, psEntFoundValue)==0 || // found this exact key/value + (strlen(psEntFoundValue) && strFindValue.IsEmpty()) // or any value for this key if blank value search specified + ) + { + // found this search key/value, so delete it... + // + DeleteKey(ent,strFindKey); + // + // and replace with the new key/value (if specified)... + // + if (!strReplaceKey.IsEmpty() && !strReplaceValue.IsEmpty()) + { + SetKeyValue (ent, strReplaceKey, strReplaceValue); + } + iOccurences++; + } + } + } + if (iOccurences) + { + Sys_Printf("%d occurence(s) replaced\n",iOccurences); + } + else + { + Sys_Printf("Nothing found to replace\n"); + } + } + break; + case ID_RET_FIND: + { + gpPrevEntBrushFound = NULL; + FindNextBrush(NULL); + } + break; + } +} +void CMainFrame::OnFindNextEnt() +{ + // try it once, if it fails, try it again from top, and give up if still failed after that... + // + if (!FindNextBrush(gpPrevEntBrushFound)) + { + gpPrevEntBrushFound = NULL; + FindNextBrush(NULL); + } +} + +void CMainFrame::OnFaceFit() +{ + FaceFit(); +} + +void CMainFrame::OnViewHideshowHideselected() +{ + Select_Hide(); + Select_Deselect(); +} + +void CMainFrame::OnViewHideshowShowhidden() +{ + Select_ShowAllHidden(); +} + + +void CMainFrame::OnConvertcurves() +{ +#if 0 + Select_Deselect(); + for (brush_t* pb = active_brushes.next ; pb != &active_brushes ; pb = pb->next) + { + if (pb->curveBrush) + { + for (face_t* f = pb->brush_faces ; f ; f=f->next) + { + if (f->texdef.contents & CONTENTS_LADDER) + { + f->texdef.contents &= ~CONTENTS_LADDER; + f->texdef.contents |= CONTENTS_NEGATIVE_CURVE; + } + } + } + } + Map_BuildBrushData(); +#endif + +} + +void CMainFrame::OnDynamicLighting() +{ + CCamWnd* pCam = new CCamWnd(); + CRect rect(100, 100, 300, 300); + pCam->Create(CAMERA_WINDOW_CLASS, "", WS_OVERLAPPEDWINDOW, rect, GetDesktopWindow(), 12345); + pCam->ShowWindow(SW_SHOW); +} + + +void CMainFrame::OnCurveSimplepatchmesh() +{ + CPatchDensityDlg dlg; + dlg.DoModal(); +} + + +void CMainFrame::OnPatchToggleBox() +{ + g_bPatchShowBounds ^= 1; + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_PATCH_SHOWBOUNDINGBOX, (g_bPatchShowBounds) ? TRUE : FALSE); + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnPatchWireframe() +{ + g_bPatchWireFrame ^= 1; + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_PATCH_WIREFRAME, (g_bPatchWireFrame) ? TRUE : FALSE); + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnCurvePatchcone() +{ + Patch_BrushToMesh(true); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnCurvePatchtube() +{ + Patch_BrushToMesh(false); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnPatchWeld() +{ + g_bPatchWeld ^= 1; + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_PATCH_WELD, (g_bPatchWeld) ? TRUE : FALSE); + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnCurvePatchbevel() +{ + Patch_BrushToMesh(false, true, false); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnCurvePatchendcap() +{ + Patch_BrushToMesh(false, false, true); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnCurvePatchinvertedbevel() +{ + //Patch_BrushToMesh(false, true, false, true); + //Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnCurvePatchinvertedendcap() +{ + //Patch_BrushToMesh(false, false, true, true); + //Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnPatchDrilldown() +{ + g_bPatchDrillDown ^= 1; + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_PATCH_DRILLDOWN, (g_bPatchDrillDown) ? TRUE : FALSE); + Sys_UpdateWindows(W_ALL); +} + + +void CMainFrame::OnCurveInsertcolumn() +{ + //Patch_AdjustSelectedRowCols(0, 2); + Patch_AdjustSelected(true, true, true); + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnCurveInsertrow() +{ + //Patch_AdjustSelectedRowCols(2, 0); + Patch_AdjustSelected(true, false, true); + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnMapHome() +{ + if (m_pXYWnd) + { + m_pXYWnd->XY_Init(); + Sys_UpdateWindows(W_ALL); + } +} + +void CMainFrame::OnCurveDeletecolumn() +{ + Patch_AdjustSelected(false, true, true); + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnCurveDeleterow() +{ + Patch_AdjustSelected(false, false, true); + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnCurveInsertAddcolumn() +{ + Patch_AdjustSelected(true, true, true); + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnCurveInsertAddrow() +{ + Patch_AdjustSelected(true, false, true); + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnCurveInsertInsertcolumn() +{ + Patch_AdjustSelected(true, true, false); + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnCurveInsertInsertrow() +{ + Patch_AdjustSelected(true, false, false); + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnCurveNegative() +{ + Patch_ToggleInverted(); + //Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnCurveNegativeTextureX() +{ + Patch_InvertTexture(false); + //Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnCurveNegativeTextureY() +{ + Patch_InvertTexture(true); + //Sys_UpdateWindows(W_ALL); +} + + +void CMainFrame::OnCurveDeleteFirstcolumn() +{ + Patch_AdjustSelected(false, true, true); + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnCurveDeleteFirstrow() +{ + Patch_AdjustSelected(false, false, true); + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnCurveDeleteLastcolumn() +{ + Patch_AdjustSelected(false, true, false); + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnCurveDeleteLastrow() +{ + Patch_AdjustSelected(false, false, false); + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnPatchBend() +{ + Patch_BendToggle(); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_PATCH_BEND, (g_bPatchBendMode) ? TRUE : FALSE); + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnPatchInsdel() +{ + Patch_InsDelToggle(); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_PATCH_INSDEL, (g_bPatchInsertMode) ? TRUE : FALSE); + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnPatchEnter() +{ + +} + +void CMainFrame::OnPatchTab() +{ + if (g_bPatchBendMode) + Patch_BendHandleTAB(); + else if (g_bPatchInsertMode) + Patch_InsDelHandleTAB(); + else + { + // check to see if the selected brush is part of a func group + // if it is, deselect everything and reselect the next brush + // in the group + brush_t *b = selected_brushes.next; + entity_t * e; + if (b != &selected_brushes) + { + if (strcmpi(b->owner->eclass->name, "worldspawn") != 0) + { + e = b->owner; + Select_Deselect(); + for (brush_t * b2 = e->brushes.onext ; b2 != &e->brushes ; b2 = b2->onext) + { + if (b == b2) + { + b2 = b2->onext; + break; + } + } + if (b2 == &e->brushes) + b2 = b2->onext; + + Select_Brush(b2, false); + Sys_UpdateWindows(W_ALL); + } + } + } +} + +void CMainFrame::UpdatePatchToolbarButtons() +{ + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_PATCH_BEND, (g_bPatchBendMode) ? TRUE : FALSE); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_PATCH_INSDEL, (g_bPatchInsertMode) ? TRUE : FALSE); +} + +void CMainFrame::OnCurvePatchdensetube() +{ + Patch_BrushToMesh(false); + OnCurveInsertAddrow(); + OnCurveInsertInsertrow(); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnCurvePatchverydensetube() +{ + Patch_BrushToMesh(false); + OnCurveInsertAddrow(); + OnCurveInsertInsertrow(); + OnCurveInsertAddrow(); + OnCurveInsertInsertrow(); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnCurveCap() +{ + Patch_CapCurrent(); + Sys_UpdateWindows (W_ALL); +} + + +void CMainFrame::OnCurveCapInvertedbevel() +{ + Patch_CapCurrent(true); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnCurveCapInvertedendcap() +{ + Patch_CapCurrent(false, true); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnCurveRedisperseCols() +{ + Patch_DisperseColumns(); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnCurveRedisperseRows() +{ + Patch_DisperseRows(); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnPatchNaturalize() +{ + Patch_NaturalizeSelected(); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnSnapToGrid() +{ + Select_SnapToGrid(); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnCurvePatchsquare() +{ + Patch_BrushToMesh(false, false, false, true); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::CheckTextureScale(int id) +{ + CMenu* pMenu = GetMenu(); + if (pMenu) + { + pMenu->CheckMenuItem(ID_TEXTURES_TEXTUREWINDOWSCALE_10, MF_BYCOMMAND | MF_UNCHECKED); + pMenu->CheckMenuItem(ID_TEXTURES_TEXTUREWINDOWSCALE_25, MF_BYCOMMAND | MF_UNCHECKED); + pMenu->CheckMenuItem(ID_TEXTURES_TEXTUREWINDOWSCALE_50, MF_BYCOMMAND | MF_UNCHECKED); + pMenu->CheckMenuItem(ID_TEXTURES_TEXTUREWINDOWSCALE_100, MF_BYCOMMAND | MF_UNCHECKED); + pMenu->CheckMenuItem(ID_TEXTURES_TEXTUREWINDOWSCALE_200, MF_BYCOMMAND | MF_UNCHECKED); + pMenu->CheckMenuItem(id, MF_BYCOMMAND | MF_CHECKED); + } + g_PrefsDlg.SavePrefs(); + Texture_ResetPosition(); + Sys_UpdateWindows(W_TEXTURE); +} + +void CMainFrame::OnTexturesTexturewindowscale10() +{ + g_PrefsDlg.m_nTextureScale = 10; + CheckTextureScale(ID_TEXTURES_TEXTUREWINDOWSCALE_10); +} + +void CMainFrame::OnTexturesTexturewindowscale100() +{ + g_PrefsDlg.m_nTextureScale = 100; + CheckTextureScale(ID_TEXTURES_TEXTUREWINDOWSCALE_100); +} + +void CMainFrame::OnTexturesTexturewindowscale200() +{ + g_PrefsDlg.m_nTextureScale = 200; + CheckTextureScale(ID_TEXTURES_TEXTUREWINDOWSCALE_200); +} + +void CMainFrame::OnTexturesTexturewindowscale25() +{ + g_PrefsDlg.m_nTextureScale = 25; + CheckTextureScale(ID_TEXTURES_TEXTUREWINDOWSCALE_25); +} + +void CMainFrame::OnTexturesTexturewindowscale50() +{ + g_PrefsDlg.m_nTextureScale = 50; + CheckTextureScale(ID_TEXTURES_TEXTUREWINDOWSCALE_50); +} + + + +void CMainFrame::OnTexturesFlush() +{ + Texture_Flush(); + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnCurveOverlayClear() +{ + Patch_ClearOverlays(); + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnCurveOverlaySet() +{ + Patch_SetOverlays(); + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnCurveThicken() +{ + CDialogThick dlg; + if (dlg.DoModal() == IDOK) + { + Patch_Thicken(dlg.m_nAmount, dlg.m_bSeams); + Sys_UpdateWindows(W_ALL); + } +} + +void CMainFrame::OnCurveCyclecap() +{ + Patch_NaturalizeSelected(true, true); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnCurveMatrixTranspose() +{ + Patch_Transpose(); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnTexturesReloadshaders() +{ + CWaitCursor wait; + ReloadShaders(); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::SetEntityCheck() +{ + CMenu* pMenu = GetMenu(); + if (pMenu) + { + pMenu->CheckMenuItem(ID_VIEW_ENTITIESAS_BOUNDINGBOX, MF_BYCOMMAND | (g_PrefsDlg.m_nEntityShowState == ENTITY_BOX) ? MF_CHECKED : MF_UNCHECKED); + pMenu->CheckMenuItem(ID_VIEW_ENTITIESAS_WIREFRAME, MF_BYCOMMAND | (g_PrefsDlg.m_nEntityShowState == ENTITY_WIRE) ? MF_CHECKED : MF_UNCHECKED); + pMenu->CheckMenuItem(ID_VIEW_ENTITIESAS_SELECTEDWIREFRAME, MF_BYCOMMAND | (g_PrefsDlg.m_nEntityShowState == ENTITY_SELECTED) ? MF_CHECKED : MF_UNCHECKED); + pMenu->CheckMenuItem(ID_VIEW_ENTITIESAS_SELECTEDSKINNED, MF_BYCOMMAND | (g_PrefsDlg.m_nEntityShowState == ENTITY_SELECTED_SKIN) ? MF_CHECKED : MF_UNCHECKED); + pMenu->CheckMenuItem(ID_VIEW_ENTITIESAS_SKINNED, MF_BYCOMMAND | (g_PrefsDlg.m_nEntityShowState == ENTITY_SKINNED) ? MF_CHECKED : MF_UNCHECKED); + pMenu->CheckMenuItem(ID_VIEW_ENTITIESAS_SKINNEDANDBOXED, MF_BYCOMMAND | (g_PrefsDlg.m_nEntityShowState == ENTITY_SKINNED_BOXED) ? MF_CHECKED : MF_UNCHECKED); + } +} + + +void CMainFrame::OnShowEntities() +{ + HandlePopup(this, IDR_POPUP_ENTITY); +} + +void CMainFrame::OnViewEntitiesasBoundingbox() +{ + g_PrefsDlg.m_nEntityShowState = ENTITY_BOX; + SetEntityCheck(); + g_PrefsDlg.SavePrefs(); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnViewEntitiesasSelectedskinned() +{ + g_PrefsDlg.m_nEntityShowState = ENTITY_SELECTED_SKIN; + SetEntityCheck(); + g_PrefsDlg.SavePrefs(); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnViewEntitiesasSelectedwireframe() +{ + g_PrefsDlg.m_nEntityShowState = ENTITY_SELECTED; + SetEntityCheck(); + g_PrefsDlg.SavePrefs(); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnViewEntitiesasSkinned() +{ + g_PrefsDlg.m_nEntityShowState = ENTITY_SKINNED; + SetEntityCheck(); + g_PrefsDlg.SavePrefs(); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnViewEntitiesasSkinnedandboxed() +{ + g_PrefsDlg.m_nEntityShowState = ENTITY_SKINNED_BOXED; + SetEntityCheck(); + g_PrefsDlg.SavePrefs(); + Sys_UpdateWindows (W_ALL); +} + +void CMainFrame::OnViewEntitiesasWriteframe() +{ + g_PrefsDlg.m_nEntityShowState = ENTITY_WIRE; + SetEntityCheck(); + g_PrefsDlg.SavePrefs(); + Sys_UpdateWindows (W_ALL); +} + + +void CMainFrame::OnSSStatus() +{ + CString string; + + if ( SS_SetupOk() ) + { + bool bEnabled = SS_FunctionsAvailable(); + string += va("SourceSafe appears to be setup ok %s",bEnabled?"and is enabled":"but is disabled"); + + if (bEnabled) + { + LPSTR filename = currentmap; + + bool bUnderSourceControl = SS_IsUnderSourceControl( filename ); + + string += va("\n\n(Current file is \"%s\" %s)",filename,bUnderSourceControl?"and is under SourceSafe control":"but is not under SourceSafe control"); + + if ( bUnderSourceControl ) + { + if ( SS_IsCheckedOut( filename )) + { + CString strCheckOuts; + int iCount; + + if ( SS_ListCheckOuts( filename, strCheckOuts, iCount )) + { + if ( (iCount != 1) || !SS_IsCheckedOutByMe( filename )) + { + string += va("\n\nIt is checked out to:\n\n%s",(LPCSTR) strCheckOuts); + } + else + { + string += "\n\nYou have it checked out"; + } + } + } + else + { + string += "\n\nIt is not checked out to anyone"; + } + } + } + } + else + { + string += "This machine does not appear to have SourceSafe setup properly (if at all)"; + } + + InfoBox((LPCSTR) string); +} + +void CMainFrame::OnSSAdd() +{ + if ( SS_FunctionsAvailable() ) + { + //LPCSTR filename = GetCurrentFileName(); + LPSTR filename = currentmap; + + if ( !SS_IsUnderSourceControl( filename )) + { + if ( SS_Add( filename )) + { + Sys_Printf("(File was added to SourceSafe Ok)\n"); + + if (GetYesNo("Check this file out as well?")) + { + if ( SS_CheckOut( filename )) + { + Sys_Printf("(Checked out ok)\n"); + } + else + { + ErrorBox("Failed during CheckOut\n"); + } + } + } + else + { + ErrorBox( va("Error adding file \"%s\" to SourceSafe",filename)); + } + } + else + { + ErrorBox( va("File \"%s\" is already under SourceSafe control",filename)); + } + + } + else + { + ErrorBox("Function not available"); + } +} + + +void CMainFrame::OnSSCheckIn() +{ + if ( SS_FunctionsAvailable() ) + { + LPSTR filename = currentmap; + + if ( SS_IsUnderSourceControl( filename )) + { + if ( SS_IsCheckedOutByMe( filename )) + { + if ( SS_CheckIn( filename )) + { + Sys_Printf("(Checked in ok)\n"); + } + else + { + Sys_Printf("Error during CheckIn\n"); + } + } + else + { + ErrorBox( va("You do not have file \"%s\" checked out",filename)); + } + } + else + { + ErrorBox( va("File \"%s\" is not under SourceSafe control",filename)); + } + } + else + { + ErrorBox("Function not available"); + } +} + +void CMainFrame::OnSSCheckOut() +{ + if ( SS_FunctionsAvailable() ) + { + LPSTR filename = currentmap; + + if ( SS_IsUnderSourceControl( filename )) + { + if ( SS_IsCheckedOutByMe( filename )) + { + InfoBox( va("You already have file \"%s\" checked out",filename)); + } + else + { + if (SS_IsCheckedOut( filename )) + { + CString strCheckOuts; + int iCount; + + if ( SS_ListCheckOuts( filename, strCheckOuts, iCount )) + { + ErrorBox( va("File \"%s\" is already checked out by:\n\n%s",filename,(LPCSTR) strCheckOuts)); + } + } + else + { + if ( SS_CheckOut( filename )) + { + Sys_Printf("(Checked out ok)\n"); + } + else + { + Sys_Printf("Error during CheckOut\n"); + } + } + } + } + else + { + ErrorBox( va("File \"%s\" is not under SourceSafe control",filename)); + } + } + else + { + ErrorBox("Function not available"); + } +} + +void CMainFrame::OnSSUndoCheckOut() +{ + if ( SS_FunctionsAvailable() ) + { + LPSTR filename = currentmap; + + if ( SS_IsUnderSourceControl( filename )) + { + if ( SS_IsCheckedOutByMe( filename )) + { + if ( SS_UndoCheckOut( filename )) + { + Sys_Printf("(Undo CheckOut ok)\n"); + } + else + { + Sys_Printf("Error during Undo CheckOut\n"); + } + } + else + { + ErrorBox( va("You do not have file \"%s\" checked out",filename)); + } + } + else + { + ErrorBox( va("File \"%s\" is not under SourceSafe control",filename)); + } + } + else + { + ErrorBox("Function not available"); + } +} + +void CMainFrame::OnSSHistory() +{ + CString string; + + if ( SS_FunctionsAvailable() ) + { + LPSTR filename = currentmap; + + if ( SS_IsUnderSourceControl( filename ) ) + { + CString strHistory; + + if ( SS_ListVersions( filename, strHistory )) + { + string += va("History of \"%s\"...\n\n%s",filename,(LPCSTR)strHistory); + } + } + else + { + string += va("File \"%s\" doesn't appear to be under SourceSafe control",filename); + } + } + else + { + string += "This machine does not appear to have SourceSafe setup properly (if at all)"; + } + + InfoBox((LPCSTR) string); +} + +void CMainFrame::OnSSConfigure() +{ + CSourceSafeSettings dialog(&g_cstrSourceSafeINI, &g_cstrSourceSafeProject, &g_bUseSourceSafe, &g_cstrBehavEdPath); + dialog.DoModal(); +} + + +void CMainFrame::OnPluginsRefresh() +{ + CleanPlugInMenu(); + CString str(g_strAppPath); + AddSlash(str); + str += "plugins\\"; + m_PlugInMgr.Init(str); +} + +void CMainFrame::CleanPlugInMenu() +{ + m_nNextPlugInID = ID_PLUGIN_START; + CMenu* pMenu = GetMenu(); + //--pMenu->RemoveMenu(MENU_PLUGIN, MF_BYPOSITION); + //--pMenu->InsertMenu(MENU_PLUGIN, MF_BYPOSITION, 0, "Plugins"); + //--DrawMenuBar(); + CMenu* pSub = pMenu->GetSubMenu(MENU_PLUGIN); + if (pSub) + { + int n = pSub->GetMenuItemCount(); + for (int i = n; i > 1 ; i--) + { + pSub->RemoveMenu(i, MF_BYPOSITION); + } + } +} + +void CMainFrame::AddPlugInMenuItem(CPlugIn* pPlugIn) +{ + const char *menuText; //PGM + CMenu* pMenu = GetMenu(); + CMenu* pSub = pMenu->GetSubMenu(MENU_PLUGIN); + if (pSub) + { + CMenu* pChild = new CMenu(); + pChild->CreateMenu(); + int nCount = pPlugIn->getCommandCount(); + if (nCount > 0) + { + while (nCount > 0) + { + menuText = pPlugIn->getCommand(--nCount); + if (menuText != NULL && strlen(menuText) > 0) + { + if(!strcmp(menuText, "-")) + pChild->AppendMenu(MF_SEPARATOR, NULL); + else + pChild->AppendMenu(MF_STRING, m_nNextPlugInID, menuText); + pPlugIn->addMenuID(m_nNextPlugInID++); + } + } + pSub->AppendMenu(MF_POPUP, reinterpret_cast(pChild->GetSafeHmenu()), pPlugIn->getMenuName()); + } + } +} + +void CMainFrame::OnPlugIn(unsigned int nID) +{ + CMenu* pMenu = GetMenu(); + CString str; + pMenu->GetMenuString(nID, str, MF_BYCOMMAND); + m_PlugInMgr.Dispatch(nID, str); +} + +void CMainFrame::OnViewShowhint() +{ + if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_HINT ) & EXCLUDE_HINT ) + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWHINT, MF_BYCOMMAND | MF_UNCHECKED ); + else + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWHINT, MF_BYCOMMAND | MF_CHECKED ); + Sys_UpdateWindows (W_XY|W_CAMERA); +} + +void CMainFrame::OnAutocaulk() +{ + Select_AutoCaulk(false); // bMakeDetail +} + +void CMainFrame::OnUpdateAutocaulk(CCmdUI* pCmdUI) +{ + pCmdUI->Enable( selected_brushes.next != &selected_brushes); +} + +void CMainFrame::OnViewShowfuncgroups() +{ + if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_FUNC_GROUP ) & EXCLUDE_FUNC_GROUP ) + CheckMenuItem( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWFUNCGROUPS, MF_BYCOMMAND | MF_UNCHECKED); + else + CheckMenuItem( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWFUNCGROUPS, MF_BYCOMMAND | MF_CHECKED); + Sys_UpdateWindows (W_XY|W_CAMERA); +} + + +void CMainFrame::OnViewShowcurvesonly() +{ + if ( ( g_qeglobals.d_savedinfo.exclude ^= SHOW_CURVES_ONLY ) & SHOW_CURVES_ONLY ) + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWCURVESONLY, MF_BYCOMMAND | MF_CHECKED ); + else + CheckMenuItem ( ::GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWCURVESONLY, MF_BYCOMMAND | MF_UNCHECKED ); + Sys_UpdateWindows (W_XY|W_CAMERA); +} + diff --git a/utils/Radiant/mainfrm.h b/utils/Radiant/mainfrm.h new file mode 100644 index 0000000..e4085ee --- /dev/null +++ b/utils/Radiant/mainfrm.h @@ -0,0 +1,487 @@ +// MainFrm.h : interface of the CMainFrame class +// +///////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_MAINFRM_H__330BBF0A_731C_11D1_B539_00AA00A410FC__INCLUDED_) +#define AFX_MAINFRM_H__330BBF0A_731C_11D1_B539_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +#include "LstToolBar.h" +#include "XYWnd.h" +#include "TexWnd.h" +#include "ZWnd.h" +#include "CamWnd.h" +#include "RADEditWnd.h" +#include "TextureBar.h" +#include "PlugInManager.h" +#include "PlugIn.h" + +const int RAD_SHIFT = 0x01; +const int RAD_ALT = 0x02; +const int RAD_CONTROL = 0x04; +const int RAD_PRESS = 0x08; + +struct SCommandInfo +{ + char* m_strCommand; + unsigned int m_nKey; + unsigned int m_nModifiers; + unsigned int m_nCommand; +}; + +struct SKeyInfo +{ + char* m_strName; + unsigned int m_nVKKey; +}; + + + + +class CMainFrame : public CFrameWnd +{ + DECLARE_DYNAMIC(CMainFrame) +public: + CMainFrame(); + void HandleKey(UINT nChar, UINT nRepCnt, UINT nFlags, bool bDown = true) + { + if (bDown) + OnKeyDown(nChar, nRepCnt, nFlags); + else + OnKeyUp(nChar, nRepCnt, nFlags); + }; + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CMainFrame) + public: + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + virtual BOOL PreTranslateMessage(MSG* pMsg); + protected: + virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); + virtual LRESULT DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam); + virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam); + virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext); + //}}AFX_VIRTUAL + +// Implementation +public: + void UpdatePatchToolbarButtons(); + void NudgeSelection(int nDirection, int nAmount); + void UpdateTextureBar(); + void SetButtonMenuStates(); + void SetTexValStatus(); + void SetGridStatus(); + void RoutineProcessing(); + CXYWnd* ActiveXY(); + void UpdateWindows(int nBits); + void SetStatusText(int nPane, const char* pText); + void UpdateStatusText(); + void SetWindowStyle(int nStyle); + virtual ~CMainFrame(); + CXYWnd* GetXYWnd() {return m_pXYWnd;}; + CXYWnd* GetXZWnd() {return m_pXZWnd;}; + CXYWnd* GetYZWnd() {return m_pYZWnd;}; + CCamWnd* GetCamera() {return m_pCamWnd;}; + CTexWnd* GetTexWnd() {return m_pTexWnd;}; + CZWnd* GetZWnd() {return m_pZWnd;}; + void SetActiveXY(CXYWnd* p) + { + if (m_pActiveXY) + m_pActiveXY->SetActive(false); + + m_pActiveXY = p; + + if (m_pActiveXY) + m_pActiveXY->SetActive(true); + + }; + int CurrentStyle() { return m_nCurrentStyle; }; +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +protected: // control bar embedded members + CStatusBar m_wndStatusBar; + CLstToolBar m_wndToolBar; + CLstToolBar m_wndScaleBar; + CDialogBar m_wndHelpBar; + CTextureBar m_wndTextureBar; + CSplitterWnd m_wndSplit; + CSplitterWnd m_wndSplit2; + CSplitterWnd m_wndSplit3; + CXYWnd* m_pXYWnd; + CXYWnd* m_pYZWnd; + CXYWnd* m_pXZWnd; + CCamWnd* m_pCamWnd; + CTexWnd* m_pTexWnd; + CZWnd* m_pZWnd; + CRADEditWnd* m_pEditWnd; + int m_nCurrentStyle; + CString m_strStatus[15]; + CXYWnd* m_pActiveXY; + bool m_bCamPreview; + CPlugInManager m_PlugInMgr; + int m_nNextPlugInID; + +// Generated message map functions +protected: + bool m_bDoLoop; + bool m_bSplittersOK; + void CreateQEChildren(); + void LoadCommandMap(); + void SetEntityCheck(); + afx_msg void OnBSPStatus(UINT wParam, long lParam); + afx_msg void OnBSPDone(UINT wParam, long lParam); +public: + void Nudge(int nDim, float fNudge); + + CPlugInManager &GetPlugInMgr() {return m_PlugInMgr;}; + void AddPlugInMenuItem(CPlugIn* pPlugIn); + void CleanPlugInMenu(); + + // these are public so i can easily reflect messages + // from child windows.. + //{{AFX_MSG(CMainFrame) + afx_msg void OnParentNotify(UINT message, LPARAM lParam); + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnTimer(UINT nIDEvent); + afx_msg void OnDestroy(); + afx_msg void OnClose(); + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void ToggleCamera(); + afx_msg void OnFileClose(); + afx_msg void OnFileExit(); + afx_msg void OnFileLoadproject(); + afx_msg void OnFileNew(); + afx_msg void OnFileOpen(); + afx_msg void OnFilePointfile(); + afx_msg void OnFilePrint(); + afx_msg void OnFilePrintPreview(); + afx_msg void OnFileSave(); + afx_msg void OnFileSaveas(); + afx_msg void OnView100(); + afx_msg void OnViewCenter(); + afx_msg void OnViewConsole(); + afx_msg void OnViewDownfloor(); + afx_msg void OnViewEntity(); + afx_msg void OnViewFront(); + afx_msg void OnViewShowblocks(); + afx_msg void OnViewShowclip(); + afx_msg void OnViewShowcoordinates(); + afx_msg void OnViewShowdetail(); + afx_msg void OnViewShowDetailOnly(); + afx_msg void OnViewShowEasy(); + afx_msg void OnViewShowMedium(); + afx_msg void OnViewShowHard(); + afx_msg void OnViewShowNonSolid(); + afx_msg void OnMakeNonSolid(); + afx_msg void OnClearNonSolid(); + afx_msg void OnViewShowent(); + afx_msg void OnViewShowlights(); + afx_msg void OnViewShownames(); + afx_msg void OnViewShowpath(); + afx_msg void OnViewShowwater(); + afx_msg void OnViewShowworld(); + afx_msg void OnViewTexture(); + afx_msg void OnViewUpfloor(); + afx_msg void OnViewXy(); + afx_msg void OnViewZ100(); + afx_msg void OnViewZoomin(); + afx_msg void OnViewZoomout(); + afx_msg void OnViewZzoomin(); + afx_msg void OnViewZzoomout(); + afx_msg void OnViewSide(); + afx_msg void OnTexturesShowinuse(); + afx_msg void OnTexturesInspector(); + afx_msg void OnMiscBenchmark(); + afx_msg void OnMiscFindbrush(); + afx_msg void OnMiscGamma(); + afx_msg void OnMiscNextleakspot(); + afx_msg void OnMiscPreviousleakspot(); + afx_msg void OnMiscPrintxy(); + afx_msg void OnMiscSelectentitycolor(); + afx_msg void OnTexturebk(); + afx_msg void OnColorsMajor(); + afx_msg void OnColorsMinor(); + afx_msg void OnColorsXybk(); + afx_msg void OnBrush3sided(); + afx_msg void OnBrush4sided(); + afx_msg void OnBrush5sided(); + afx_msg void OnBrush6sided(); + afx_msg void OnBrush7sided(); + afx_msg void OnBrush8sided(); + afx_msg void OnBrush9sided(); + afx_msg void OnBrushArbitrarysided(); + afx_msg void OnBrushFlipx(); + afx_msg void OnBrushFlipy(); + afx_msg void OnBrushFlipz(); + afx_msg void OnBrushRotatex(); + afx_msg void OnBrushRotatey(); + afx_msg void OnBrushRotatez(); + afx_msg void OnRegionOff(); + afx_msg void OnRegionSetbrush(); + afx_msg void OnRegionSetselection(); + afx_msg void OnRegionSettallbrush(); + afx_msg void OnRegionSetxy(); + afx_msg void OnSelectionArbitraryrotation(); + afx_msg void OnSelectionClone(); + afx_msg void OnSelectionClone_NoTargetNameChange() ; + afx_msg void OnSelectionConnect(); + afx_msg void OnSelectionConnectSmart(); + afx_msg void OnSelectionCsgsubtract(); + afx_msg void OnSelectionDelete(); + afx_msg void OnSelectionDeselect(); + afx_msg void OnSelectionDragedges(); + afx_msg void OnSelectionDragvertecies(); + afx_msg void OnSelectionMakeDetail(); + afx_msg void OnSelectionMakeStructural(); + afx_msg void OnSelectionHideWaypointChildren(); + afx_msg void OnSelectionUnHideWaypointChildren(); + afx_msg void OnSelectionUnHideAllWaypoints(); + afx_msg void OnSelectionMakehollow(); + afx_msg void OnSelectionSelectcompletetall(); + afx_msg void OnSelectionSelectinside(); + afx_msg void OnSelectionSelectpartialtall(); + afx_msg void OnSelectionSelecttouching(); + afx_msg void OnSelectionUngroupentity(); + afx_msg void OnSelectionGroupNamesDisplay(); + afx_msg void OnSelectionGroupNamesActive(); + afx_msg void OnSelectionGroupNamesGhosted(); + afx_msg void OnTexturesPopup(); + afx_msg void OnPopupSelection(); + afx_msg void OnViewChange(); + afx_msg void OnViewCameraupdate(); + afx_msg void OnUpdateViewCameraupdate(CCmdUI* pCmdUI); + afx_msg void OnSizing(UINT fwSide, LPRECT pRect); + afx_msg void OnHelpAbout(); + afx_msg void OnViewClipper(); + afx_msg void OnCameraAngledown(); + afx_msg void OnCameraAngleup(); + afx_msg void OnCameraBack(); + afx_msg void OnCameraDown(); + afx_msg void OnCameraForward(); + afx_msg void OnCameraLeft(); + afx_msg void OnCameraRight(); + afx_msg void OnCameraStrafeleft(); + afx_msg void OnCameraStraferight(); + afx_msg void OnCameraUp(); + afx_msg void OnGridToggle(); + afx_msg void OnEntitylist(); + afx_msg void OnMapinfo(); + afx_msg void OnPrefs(); + afx_msg void OnTogglecamera(); + afx_msg void OnToggleconsole(); + afx_msg void OnToggleview(); + afx_msg void OnTogglez(); + afx_msg void OnToggleLock(); + afx_msg void OnEditMapinfo(); + afx_msg void OnEditEntityinfo(); + afx_msg void OnBrushScripts(); + afx_msg void OnViewNextview(); + afx_msg void OnHelpCommandlist(); + afx_msg void OnFileNewproject(); + afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnFlipClip(); + afx_msg void OnClipSelected(); + afx_msg void OnSplitSelected(); + afx_msg void OnToggleviewXz(); + afx_msg void OnToggleviewYz(); + afx_msg void OnColorsBrush(); + afx_msg void OnColorsClipper(); + afx_msg void OnColorsGridtext(); + afx_msg void OnColorsSelectedbrush(); + afx_msg void OnColorsGridblock(); + afx_msg void OnColorsViewname(); + afx_msg void OnColorSetoriginal(); + afx_msg void OnColorSetqer(); + afx_msg void OnColorSetblack(); + afx_msg void OnSnaptogrid(); + afx_msg void OnSelectScale(); + afx_msg void OnSelectMouserotate(); + afx_msg void OnEditCopybrush(); + afx_msg void OnEditPastebrush(); + afx_msg void OnEditPastebrushNoTargetnameChange(); + afx_msg void OnEditUndo(); + afx_msg void OnUpdateEditUndo(CCmdUI* pCmdUI); + afx_msg void OnSelectionTextureDec(); + afx_msg void OnSelectionTextureFit(); + afx_msg void OnSelectionTextureInc(); + afx_msg void OnSelectionTextureRotateclock(); + afx_msg void OnSelectionTextureRotatecounter(); + afx_msg void OnSelectionTextureScaledown(); + afx_msg void OnSelectionTextureScaleup(); + afx_msg void OnSelectionTextureShiftdown(); + afx_msg void OnSelectionTextureShiftleft(); + afx_msg void OnSelectionTextureShiftright(); + afx_msg void OnSelectionTextureShiftup(); + afx_msg void OnGridNext(); + afx_msg void OnGridPrev(); + afx_msg void OnSelectionTextureScaleLeft(); + afx_msg void OnSelectionTextureScaleRight(); + afx_msg void OnTextureReplaceall(); + afx_msg void OnScalelockx(); + afx_msg void OnScalelocky(); + afx_msg void OnScalelockz(); + afx_msg void OnSelectMousescale(); + afx_msg void OnViewCubicclipping(); + afx_msg void OnFileImport(); + afx_msg void OnFileProjectsettings(); + afx_msg void OnUpdateFileImport(CCmdUI* pCmdUI); + afx_msg void OnViewCubein(); + afx_msg void OnViewCubeout(); + afx_msg void OnViewCubein10(); + afx_msg void OnViewCubeout10(); + afx_msg void OnFileSaveregion(); + afx_msg void OnUpdateFileSaveregion(CCmdUI* pCmdUI); + afx_msg void OnSelectionMovedown(); + afx_msg void OnSelectionMoveup(); + afx_msg void OnToolbarMain(); + afx_msg void OnToolbarTexture(); + afx_msg void OnSelectionPrint(); + afx_msg void OnSelectionTogglesizepaint(); + afx_msg void OnBrushMakecone(); + afx_msg void OnTexturesLoad(); + afx_msg void OnToggleRotatelock(); + afx_msg void OnCurveBevel(); + afx_msg void OnCurveCylinder(); + afx_msg void OnCurveEighthsphere(); + afx_msg void OnCurveEndcap(); + afx_msg void OnCurveHemisphere(); + afx_msg void OnCurveInvertcurve(); + afx_msg void OnCurveQuarter(); + afx_msg void OnCurveSphere(); + afx_msg void OnFileImportmap(); + afx_msg void OnFileExportmap(); + afx_msg void OnEditLoadprefab(); + afx_msg void OnViewShowWayPoints(); + afx_msg void OnViewShowWayPointsOnly(); + afx_msg void OnViewShowMiscModels(); + afx_msg void OnViewShowMiscModelBreakables(); + afx_msg void OnViewShowMiscModelXXXX(); + afx_msg void OnViewShowTriggerXXXX(); + afx_msg void OnViewShowTargetSpeakers(); + afx_msg void OnViewShowRefTags(); + afx_msg void OnViewShowcurves(); + afx_msg void OnSelectionSelectNudgedown(); + afx_msg void OnSelectionSelectNudgeleft(); + afx_msg void OnSelectionSelectNudgeright(); + afx_msg void OnSelectionSelectNudgeup(); + afx_msg void OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnTexturesLoadlist(); + afx_msg void OnDontselectcurve(); + afx_msg void OnConvertcurves(); + afx_msg void OnShowFaces(); + afx_msg void OnDynamicLighting(); + afx_msg void OnCurveSimplepatchmesh(); + afx_msg void OnPatchToggleBox(); + afx_msg void OnPatchWireframe(); + afx_msg void OnCurvePatchcone(); + afx_msg void OnCurvePatchtube(); + afx_msg void OnPatchWeld(); + afx_msg void OnCurvePatchbevel(); + afx_msg void OnCurvePatchendcap(); + afx_msg void OnCurvePatchinvertedbevel(); + afx_msg void OnCurvePatchinvertedendcap(); + afx_msg void OnPatchDrilldown(); + afx_msg void OnCurveInsertcolumn(); + afx_msg void OnCurveInsertrow(); + afx_msg void OnCurveDeletecolumn(); + afx_msg void OnMapHome(); + afx_msg void OnCurveDeleterow(); + afx_msg void OnCurveInsertAddcolumn(); + afx_msg void OnCurveInsertAddrow(); + afx_msg void OnCurveInsertInsertcolumn(); + afx_msg void OnCurveInsertInsertrow(); + afx_msg void OnCurveNegative(); + afx_msg void OnCurveNegativeTextureX(); + afx_msg void OnCurveNegativeTextureY(); + afx_msg void OnCurveDeleteFirstcolumn(); + afx_msg void OnCurveDeleteFirstrow(); + afx_msg void OnCurveDeleteLastcolumn(); + afx_msg void OnCurveDeleteLastrow(); + afx_msg void OnPatchBend(); + afx_msg void OnPatchInsdel(); + afx_msg void OnPatchEnter(); + afx_msg void OnPatchTab(); + afx_msg void OnCurvePatchdensetube(); + afx_msg void OnCurvePatchverydensetube(); + afx_msg void OnCurveCap(); + afx_msg void OnCurveCapInvertedbevel(); + afx_msg void OnCurveCapInvertedendcap(); + afx_msg void OnCurveRedisperseCols(); + afx_msg void OnCurveRedisperseRows(); + afx_msg void OnPatchNaturalize(); + afx_msg void OnSnapToGrid(); + afx_msg void OnCurvePatchsquare(); + afx_msg void OnTexturesTexturewindowscale10(); + afx_msg void OnTexturesTexturewindowscale100(); + afx_msg void OnTexturesTexturewindowscale200(); + afx_msg void OnTexturesTexturewindowscale25(); + afx_msg void OnTexturesTexturewindowscale50(); + afx_msg void OnTexturesFlush(); + afx_msg void OnCurveOverlayClear(); + afx_msg void OnCurveOverlaySet(); + afx_msg void OnCurveThicken(); + afx_msg void OnCurveCyclecap(); + afx_msg void OnCurveMatrixTranspose(); + afx_msg void OnTexturesReloadshaders(); + afx_msg void OnShowEntities(); + afx_msg void OnViewEntitiesasBoundingbox(); + afx_msg void OnViewEntitiesasSelectedskinned(); + afx_msg void OnViewEntitiesasSelectedwireframe(); + afx_msg void OnViewEntitiesasSkinned(); + afx_msg void OnViewEntitiesasSkinnedandboxed(); + afx_msg void OnViewEntitiesasWriteframe(); + afx_msg void OnPluginsRefresh(); + afx_msg void OnViewShowhint(); + afx_msg void OnUpdateTexturesShowinuse(CCmdUI* pCmdUI); + afx_msg void OnUpdateMiscShowFaces(CCmdUI* pCmdUI); + afx_msg void OnSSStatus(); + afx_msg void OnSSAdd(); + afx_msg void OnSSCheckIn(); + afx_msg void OnSSCheckOut(); + afx_msg void OnSSUndoCheckOut(); + afx_msg void OnSSHistory(); + afx_msg void OnSSConfigure(); + afx_msg void OnFindEnt(); + afx_msg void OnFindNextEnt(); + afx_msg void OnFaceFit(); + afx_msg void OnViewHideshowHideselected(); + afx_msg void OnViewHideshowShowhidden(); + afx_msg void OnAutocaulk(); + afx_msg void OnUpdateAutocaulk(CCmdUI* pCmdUI); + afx_msg void OnViewShowfuncgroups(); + afx_msg void OnViewShowlightradii(); + afx_msg void OnViewShowlightradii_KB(); + afx_msg void OnViewShowcurvesonly(); + //}}AFX_MSG + afx_msg void OnMru(unsigned int nID); + afx_msg void OnViewNearest(unsigned int nID); + afx_msg void OnTextureWad(unsigned int nID); + afx_msg void OnBspCommand(unsigned int nID); + afx_msg void OnGrid1(unsigned int nID); + afx_msg void OnDisplayChange(WPARAM wp, LPARAM lp); + void CheckTextureScale(int id); + afx_msg void OnPlugIn(unsigned int nID); + + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_MAINFRM_H__330BBF0A_731C_11D1_B539_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/map.cpp b/utils/Radiant/map.cpp new file mode 100644 index 0000000..d4dc9e0 --- /dev/null +++ b/utils/Radiant/map.cpp @@ -0,0 +1,1673 @@ +// map.c + +#include "stdafx.h" +#include "qe3.h" +#include "PrefsDlg.h" +#include "oddbits.h" +#ifdef QUAKE3 +#include "sourcesafe.h" +#endif +#include "groupnames.h" + +qboolean modified; // for quit confirmation (0 = clean, 1 = unsaved, + // 2 = autosaved, but not regular saved) + +char currentmap[1024]; + +brush_t active_brushes; // brushes currently being displayed +brush_t selected_brushes; // highlighted +face_t *selected_face; +brush_t *selected_face_brush; +brush_t filtered_brushes; // brushes that have been filtered or regioned + +entity_t entities; // head/tail of doubly linked list + +entity_t *world_entity = NULL; + +void AddRegionBrushes (void); +void RemoveRegionBrushes (void); + + +/* +============================================================= + + Cross map selection saving + + this could fuck up if you have only part of a complex entity selected... +============================================================= +*/ + +brush_t between_brushes; +entity_t between_entities; + +bool g_bRestoreBetween = false; + +void Map_SaveBetween (void) +{ + + if (g_pParentWnd->ActiveXY()) + { + g_bRestoreBetween = true; + g_pParentWnd->ActiveXY()->Copy(); + } + return; + +#if 0 + + brush_t *b; + entity_t *e, *e2; + + between_brushes.next = selected_brushes.next; + between_brushes.prev = selected_brushes.prev; + between_brushes.next->prev = &between_brushes; + between_brushes.prev->next = &between_brushes; + + between_entities.next = between_entities.prev = &between_entities; + selected_brushes.next = selected_brushes.prev = &selected_brushes; + + for (b=between_brushes.next ; b != &between_brushes ; b=b->next) + { + e = b->owner; + if (e == world_entity) + b->owner = NULL; + else + { + for (e2=between_entities.next ; e2 != &between_entities ; e2=e2->next) + if (e2 == e) + goto next; // allready got the entity + // move the entity over + e->prev->next = e->next; + e->next->prev = e->prev; + e->next = between_entities.next; + e->prev = &between_entities; + e->next->prev = e; + e->prev->next = e; + } +next: ; + } +#endif +} + +void Map_RestoreBetween (void) +{ + if (g_pParentWnd->ActiveXY() && g_bRestoreBetween) + g_pParentWnd->ActiveXY()->Paste(); + return; + +#if 0 + entity_t *head, *tail; + brush_t *b; + + if (!between_brushes.next) + return; + + for (b=between_brushes.next ; b != &between_brushes ; b=b->next) + { + if (!b->owner) + { + b->owner = world_entity; + b->onext = world_entity->brushes.onext; + b->oprev = &world_entity->brushes; + b->onext->oprev = b; + b->oprev->onext = b; + } + } + + selected_brushes.next = between_brushes.next; + selected_brushes.prev = between_brushes.prev; + selected_brushes.next->prev = &selected_brushes; + selected_brushes.prev->next = &selected_brushes; + + head = between_entities.next; + tail = between_entities.prev; + + if (head != tail) + { + entities.prev->next = head; + head->prev = entities.prev; + tail->next = &entities; + entities.prev = tail; + } + + between_brushes.next = NULL; + between_entities.next = NULL; +#endif +} + +//============================================================================ + +bool CheckForTinyBrush(brush_t* b, int n, float fSize) +{ + bool bTiny = false; + for (int i=0 ; i<3 ; i++) + { + if (b->maxs[i] - b->mins[i] < fSize) + bTiny = true; + } + if (bTiny) + Sys_Printf("Possible problem brush (too small) #%i ", n); + return bTiny; +} + +void Map_BuildBrushData(void) +{ + brush_t *b, *next; + + if (active_brushes.next == NULL) + return; + + Sys_BeginWait (); // this could take a while + + int n = 0; + for (b=active_brushes.next ; b != NULL && b != &active_brushes ; b=next) + { + next = b->next; + Brush_Build( b ); + if (!b->brush_faces || (g_PrefsDlg.m_bCleanTiny && CheckForTinyBrush(b, n++, g_PrefsDlg.m_fTinySize))) + { + Brush_Free (b); + Sys_Printf ("Removed degenerate brush\n"); + } + } + Sys_EndWait(); +} + +entity_t *Map_FindClass (char *cname) +{ + entity_t *ent; + + for (ent = entities.next ; ent != &entities ; ent=ent->next) + { + if (!strcmp(cname, ValueForKey (ent, "classname"))) + return ent; + } + return NULL; +} + +/* +================ +Map_Free +================ +*/ +void Map_Free (void) +{ +#ifdef QUAKE3 + // I'm going to try and offer s helpful auto-checkin menu when the user does a map free + // + LPSTR filename = currentmap; + if ( world_entity && active_brushes.next && strcmp(filename,"unnamed.map") ) // apparently all this tells me that there's a valid map loaded + { + if ( SS_FunctionsAvailable() ) + { + if ( SS_IsUnderSourceControl( filename ) ) + { + if ( SS_IsCheckedOutByMe( filename )) + { + if (modified) + { + // if 'modified' flag is still set here then the user has clicked OK on "lose changes" OnNew, so... + // + if ( GetYesNo( va("Since you've decided to lose changes on the map:\n\n\"%s\"\n\n...do you want to Undo Checkout as well?",filename))) + { + if (SS_UndoCheckOut( filename )) + { + Sys_Printf ("(Undo Checkout performed on map)\n"); + } + else + { + Sys_Printf ("(Undo Checkout failed on map)\n"); + } + } + } + else + { + // if 'modified' is false here then the user has saved out a legit map, prompt for check in... + // + if ( GetYesNo( va("Since you've finished with the map:\n\n\"%s\"\n\n...do you want to do a Check In?",filename))) + { + if ( SS_CheckIn( filename )) + { + Sys_Printf ("(CheckIn performed on map)\n"); + } + else + { + Sys_Printf ("(CheckIn failed on map)\n"); + } + } + } + } + } + } + } +#endif + + if (selected_brushes.next && + (selected_brushes.next != &selected_brushes) ) + { + g_bRestoreBetween = false; + if (MessageBox(g_qeglobals.d_hwndMain, "Copy selection?", "", MB_YESNO) == IDYES) + Map_SaveBetween (); + } + + Grouping_Shutdown(); + + Texture_ClearInuse (); + Pointfile_Clear (); + strcpy (currentmap, "unnamed.map"); + Sys_SetTitle (currentmap); + g_qeglobals.d_num_entities = 0; + + if (!active_brushes.next) + { // first map + active_brushes.prev = active_brushes.next = &active_brushes; + selected_brushes.prev = selected_brushes.next = &selected_brushes; + filtered_brushes.prev = filtered_brushes.next = &filtered_brushes; + + entities.prev = entities.next = &entities; + } + else + { + while (active_brushes.next != &active_brushes) + Brush_Free (active_brushes.next); + while (selected_brushes.next != &selected_brushes) + Brush_Free (selected_brushes.next); + while (filtered_brushes.next != &filtered_brushes) + Brush_Free (filtered_brushes.next); + + while (entities.next != &entities) + Entity_Free (entities.next); + } + + if (world_entity) + Entity_Free(world_entity); + world_entity = NULL; + selected_face= NULL; // yet another crash-bug fix + + Patch_Cleanup(); +} + +/* +================ +Map_LoadFile +================ +*/ +void Map_LoadFile (const char *filename) +{ + char *buf; + entity_t *ent; + char temp[1024]; + + Sys_BeginWait (); + Select_Deselect(); + //SetInspectorMode(W_CONSOLE); + + Map_Free (); + + Sys_ClearPrintf (); + + QE_ConvertDOSToUnixName( temp, filename ); + Sys_Printf ("Map_LoadFile: %s\n", temp ); + + g_qeglobals.d_parsed_brushes = 0; + +#ifdef QUAKE3 + // checkout the new map? + if ( SS_FunctionsAvailable() ) + { + if ( SS_IsUnderSourceControl( filename ) ) + { + if ( SS_IsCheckedOut( filename )) + { + if ( !SS_IsCheckedOutByMe( filename )) + { + CString strCheckOuts; + int iCount; + + if (SS_ListCheckOuts( filename, strCheckOuts, iCount )) + { + if (!GetYesNo(( va("Warning: Map \"%s\" is checked out by:\n\n%s\n... so you will be unable to compile or save\n\n\nDo you still want to load this map? (NO will abort load)",filename,(LPCSTR) strCheckOuts)))) + { + Sys_Printf ("(map load aborted)\n"); + return; + } + } + } + else + { + Sys_Printf ("(You own this file under SourceSafe)\n"); + } + } + else + { + if ( GetYesNo( va("The map \"%s\"\n\n...is under SourceSafe control, check it out now?",filename) )) + { + if (SS_CheckOut( filename )) + { + Sys_Printf ("(Map checked out ok)\n"); + } + } + } + } + else + { + Sys_Printf ("(This map is not under SourceSafe control)\n"); + } + } +#endif + + strcpy (currentmap, filename); + + if (LoadFile (filename, (void **)&buf) != -1) + { + + StartTokenParsing (buf); + g_qeglobals.d_num_entities = 0; + + while (1) + { + ent = Entity_Parse (false, &active_brushes); + if (!ent) + break; + if (!strcmp(ValueForKey (ent, "classname"), "worldspawn")) + { + if (world_entity) + Sys_Printf ("WARNING: multiple worldspawn\n"); + world_entity = ent; + + } + else + { + // add the entity to the end of the entity list + ent->next = &entities; + ent->prev = entities.prev; + entities.prev->next = ent; + entities.prev = ent; + g_qeglobals.d_num_entities++; + } + +#if 0 + // special code to boost all (non-default) lights by 3.5. DO NOT LEAVE IN!!!! (may be useful again later) + if (ent != world_entity) + { + if (!strcmp(ValueForKey (ent, "classname"), "light")) + { + char *p = ValueForKey (ent, "light"); + if (strlen(p)) + { + float f = atof(p); + f *= 3.5f; + p = va("%g",f); + SetKeyValue(ent,"light",p); + } + } + } +#endif + + } + } + + free (buf); + + if (!world_entity) + { + Sys_Printf ("No worldspawn in map.\n"); + Map_New (); + return; + } + + Sys_Printf ("--- LoadMapFile ---\n"); + Sys_Printf ("%s\n", temp ); + + Sys_Printf ("%5i brushes\n", g_qeglobals.d_parsed_brushes ); + Sys_Printf ("%5i entities\n", g_qeglobals.d_num_entities); + + Map_RestoreBetween (); + + Sys_Printf ("Map_BuildAllDisplayLists\n"); + Map_BuildBrushData(); + + // + // move the view to a start position + // + ent = Map_FindClass ("info_player_start"); + if (!ent) + ent = Map_FindClass ("info_player_deathmatch"); + g_pParentWnd->GetCamera()->Camera().angles[PITCH] = 0; + if (ent) + { + GetVectorForKey (ent, "origin", g_pParentWnd->GetCamera()->Camera().origin); + GetVectorForKey (ent, "origin", g_pParentWnd->GetXYWnd()->GetOrigin()); + g_pParentWnd->GetCamera()->Camera().angles[YAW] = FloatForKey (ent, "angle"); + } + else + { + g_pParentWnd->GetCamera()->Camera().angles[YAW] = 0; + VectorCopy (vec3_origin, g_pParentWnd->GetCamera()->Camera().origin); + VectorCopy (vec3_origin, g_pParentWnd->GetXYWnd()->GetOrigin()); + } + + Sys_UpdateWindows (W_ALL); + + Patch_ReadFile (filename); + + Map_RegionOff (); + + + modified = false; + Sys_SetTitle (temp); + + Texture_ShowInuse (); + + Sys_EndWait(); + +} + +/* +=========== +Map_SaveFile +=========== +*/ +// now returns success/fail bool +bool Map_SaveFile (const char *filename, qboolean use_region ) +{ + entity_t *e, *next; + FILE *f; + char temp[1024]; + int count; + + if (filename == NULL || strlen(filename) == 0) + { + CFileDialog dlgSave(FALSE, "map", NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "Map Files (*.map)|*.map||", AfxGetMainWnd()); + if (dlgSave.DoModal() == IDOK) + filename = strdup(dlgSave.m_ofn.lpstrFile); + else + return false; + } + + Pointfile_Clear (); + QE_ConvertDOSToUnixName( temp, filename ); + + Sys_Printf ("Map_SaveFile: %s\n", filename); + +#ifdef QUAKE3 + if ( SS_FunctionsAvailable() ) + { + if ( SS_IsUnderSourceControl( filename ) ) + { + if ( SS_IsCheckedOut( filename )) + { + if ( !SS_IsCheckedOutByMe( filename )) + { + CString strCheckOuts; + int iCount; + + if (SS_ListCheckOuts( filename, strCheckOuts, iCount )) + { + ErrorBox( va("Map \"%s\" is checked out by:\n\n%s\n... so you can't save over it...\n\n... so you can't compile...\n\nTough luck matey!....(bwahahahaha!!!!!)",filename,(LPCSTR) strCheckOuts)); + return false; + } + } + else + { + Sys_Printf ("(You own this file under SourceSafe)\n"); + } + } + else + { + if ( GetYesNo( va("The map \"%s\"\n\n...needs to be checked out so I can save over it\n\nProceed? ('No' will abort the save)",filename) )) + { + if (SS_CheckOut( filename )) + { + Sys_Printf ("(Map checked out ok)\n"); + } + else + { + ASSERT(0); // I want to know if this ever happens + Sys_Printf ("(Error during map checkout, aborting save\n"); + return false; + } + } + else + { + Sys_Printf ("(Checkout cancelled, aborting save\n"); + return false; + } + } + } + else + { + Sys_Printf ("(This map is not under SourceSafe control)\n"); + } + } + + // now do seperate check for files that are still write-protected... + // + DWORD dw = GetFileAttributes( filename ); + + if (dw != 0xFFFFFFFF && ( dw & FILE_ATTRIBUTE_READONLY )) + { + // hmmm, still write protected... + // + if (SS_SetupOk()) + { + if (GetYesNo( va("The file \"%s\" is write-protected, but probably not because of SourceSafe, just as a safety thing.\n\n(Tell me if you believe this is wrong -Ste)\n\nDo you want me to un-writeprotect it so you can save over it? ('No' will abort the save)",filename ))) + { + if ( !SetFileAttributes( filename, dw&~FILE_ATTRIBUTE_READONLY) ) + { + ErrorBox("Failed to remove write protect, aborting..."); + return false; + } + } + else + { + Sys_Printf ("(Map was not write-enabled, aborting save)"); + return false; + } + } + else + { + ErrorBox( va("The file \"%s\" is write-protected, but you don't appear to have SourceSafe set up properly on this machine, so I can't tell if the file is protected or just not checked out to you.\n\nIf you really want to edit this you'll have to write-enable it yourself (which I'm deliberately not offering to do for you here )",filename)); + } + } +#endif + + if (!use_region) + { + char backup[1024]; + + // rename current to .bak + strcpy (backup, filename); + StripExtension (backup); + strcat (backup, ".bak"); + // + // just before we try and delete the backup, make sure it's not write protected (if it exists)... + // + DWORD dw = GetFileAttributes( backup ); + if (dw != 0xFFFFFFFF && ( dw & FILE_ATTRIBUTE_READONLY )) + SetFileAttributes( backup, dw&~FILE_ATTRIBUTE_READONLY); + _unlink (backup); + rename (filename, backup); + } + + f = fopen(filename, "w"); + + if (!f) + { + Sys_Printf ("ERROR!!!! Couldn't open %s for output\n", filename); + return false; + } + + if (use_region) + AddRegionBrushes (); + + // write world entity first + Entity_Write (world_entity, f, use_region); + + // then write all other ents + count = 1; + for (e=entities.next ; e != &entities ; e=next) + { + next = e->next; + if (e->brushes.onext == &e->brushes) + { + Entity_Free (e); // no brushes left, so remove it + } + else + { + fprintf (f, "// entity %i\n", count); + count++; + Entity_Write (e, f, use_region); + } + } + + fclose (f); + + if (use_region) + RemoveRegionBrushes (); + + Sys_Printf ("Saved.\n"); + modified = false; + + if ( !strstr( temp, "autosave" ) ) + Sys_SetTitle (temp); + + if (!use_region) + { + time_t timer; + FILE *f; + + time (&timer); + MessageBeep (MB_ICONEXCLAMATION); + f = fopen ("c:/tstamps.log", "a"); + if (f) + { + fprintf (f, "%s", filename); + //fprintf (f, "%4i : %35s : %s", g_qeglobals.d_workcount, filename, ctime(&timer)); + fclose (f); + g_qeglobals.d_workcount = 0; + } + fclose (f); + Sys_Status ("Saved.\n", 0); + } + + //Curve_WriteFile (filename); //.trinity + //Patch_WriteFile (filename); + + return true; +} + +/* +=========== +Map_New +=========== +*/ +void Map_New (void) +{ + Sys_Printf ("Map_New\n"); + Map_Free (); + + world_entity = (entity_s*)qmalloc(sizeof(*world_entity)); + world_entity->brushes.onext = + world_entity->brushes.oprev = &world_entity->brushes; + SetKeyValue (world_entity, "classname", "worldspawn"); + world_entity->eclass = Eclass_ForName ("worldspawn", true); + + g_pParentWnd->GetCamera()->Camera().angles[YAW] = 0; + g_pParentWnd->GetCamera()->Camera().angles[PITCH] = 0; + VectorCopy (vec3_origin, g_pParentWnd->GetCamera()->Camera().origin); + g_pParentWnd->GetCamera()->Camera().origin[2] = 48; + VectorCopy (vec3_origin, g_pParentWnd->GetXYWnd()->GetOrigin()); + + Map_RestoreBetween (); + + Sys_UpdateWindows (W_ALL); + modified = false; +} + + +/* +=========================================================== + + REGION + +=========================================================== +*/ +#if 0 +qboolean region_active; +vec3_t region_mins = {MIN_WORLD_COORD, MIN_WORLD_COORD, MIN_WORLD_COORD}; +vec3_t region_maxs = {MAX_WORLD_COORD, MAX_WORLD_COORD, MAX_WORLD_COORD}; + +brush_t *region_sides[6]; + +/* +=========== +AddRegionBrushes + +a regioned map will have temp walls put up at the region boundary +=========== +*/ +void AddRegionBrushes (void) +{ + vec3_t mins, maxs; + int i; + texdef_t td; + + if (!region_active) + return; + + memset (&td, 0, sizeof(td)); + strcpy (td.name, "REGION"); + + mins[0] = region_mins[0] - 16; + maxs[0] = region_mins[0] + 1; + mins[1] = region_mins[1] - 16; + maxs[1] = region_maxs[1] + 16; + mins[2] = -2048; + maxs[2] = 2048; + region_sides[0] = Brush_Create (mins, maxs, &td); + + mins[0] = region_maxs[0] - 1; + maxs[0] = region_maxs[0] + 16; + region_sides[1] = Brush_Create (mins, maxs, &td); + + mins[0] = region_mins[0] - 16; + maxs[0] = region_maxs[0] + 16; + mins[1] = region_mins[1] - 16; + maxs[1] = region_mins[1] + 1; + region_sides[2] = Brush_Create (mins, maxs, &td); + + mins[1] = region_maxs[1] - 1; + maxs[1] = region_maxs[1] + 16; + region_sides[3] = Brush_Create (mins, maxs, &td); + + mins[0] = region_mins[0]; + maxs[0] = region_maxs[0]; + mins[1] = region_mins[1]; + maxs[1] = region_maxs[1]; + mins[2] = -2048; + maxs[2] = -2048 + 16; + region_sides[4] = Brush_Create (mins, maxs, &td); + + mins[2] = 2048; + maxs[2] = 2048 + 16; + region_sides[5] = Brush_Create (mins, maxs, &td); + + for (i=0 ; i<6 ; i++) + { + Brush_AddToList (region_sides[i], &selected_brushes); + Entity_LinkBrush (world_entity, region_sides[i]); + Brush_Build(region_sides[i]); + } +} + +void RemoveRegionBrushes (void) +{ + int i; + + if (!region_active) + return; + for (i=0 ; i<6 ; i++) + Brush_Free (region_sides[i]); +} + +#endif/* +=========================================================== + + REGION + +=========================================================== +*/ + +qboolean region_active; +vec3_t region_mins = {MIN_WORLD_COORD, MIN_WORLD_COORD, MIN_WORLD_COORD}; +vec3_t region_maxs = {MAX_WORLD_COORD, MAX_WORLD_COORD, MAX_WORLD_COORD}; + +brush_t *region_sides[4]; + + +float g_fMaxZ,g_fMinZ; +static void FillInWorldZBounds(void) +{ + g_fMaxZ = -MAX_WORLD_COORD; // default as opposite-ends, so they'll get changed by anything + g_fMinZ = MAX_WORLD_COORD; + + for (brush_t *b = active_brushes.next ; b != &active_brushes ; b = b->next) + { + if (g_fMaxZ < b->maxs[2]) + g_fMaxZ = b->maxs[2]; + if (g_fMinZ > b->mins[2]) + g_fMinZ = b->mins[2]; + } +} + + +/* +=========== +AddRegionBrushes + +a regioned map will have temp walls put up at the region boundary +=========== +*/ +void AddRegionBrushes (void) +{ + vec3_t mins, maxs; + int i; + texdef_t td; + + if (!region_active) + return; + + FillInWorldZBounds(); + + memset (&td, 0, sizeof(td)); + strcpy (td.name, "REGION"); + + mins[0] = region_mins[0] - 16; + maxs[0] = region_mins[0] + 1; + mins[1] = region_mins[1] - 16; + maxs[1] = region_maxs[1] + 16; + mins[2] = g_fMinZ - 16; // -MAX_WORLD_COORD/2; + maxs[2] = g_fMaxZ + 16; // MAX_WORLD_COORD/2; + region_sides[0] = Brush_Create (mins, maxs, &td); + + mins[0] = region_maxs[0] - 1; + maxs[0] = region_maxs[0] + 16; + region_sides[1] = Brush_Create (mins, maxs, &td); + + mins[0] = region_mins[0] - 16; + maxs[0] = region_maxs[0] + 16; + mins[1] = region_mins[1] - 16; + maxs[1] = region_mins[1] + 1; + region_sides[2] = Brush_Create (mins, maxs, &td); + + mins[1] = region_maxs[1] - 1; + maxs[1] = region_maxs[1] + 16; + region_sides[3] = Brush_Create (mins, maxs, &td); + + for (i=0 ; i<4 ; i++) + { + Brush_AddToList (region_sides[i], &selected_brushes); + Entity_LinkBrush (world_entity, region_sides[i]); + Brush_Build( region_sides[i] ); + } +} + +void RemoveRegionBrushes (void) +{ + int i; + + if (!region_active) + return; + for (i=0 ; i<4 ; i++) + Brush_Free (region_sides[i]); +} + + +// new behaviour is to filter out stuff that's even partially outside the region, not just entirely +// ... (unless the owner ent has no origin) +// +qboolean Map_IsBrushFiltered (brush_t *b) +{ + bool bAllowOverlaps = true; + + if (b->owner) + { + if (strlen(ValueForKey(b->owner,"origin"))) + bAllowOverlaps = false; + } + + + int i; + + if (bAllowOverlaps) + { + for (i=0 ; i<3 ; i++) + { + if (b->mins[i] > region_maxs[i]) + return true; + if (b->maxs[i] < region_mins[i]) + return true; + } + } + else + { + for (i=0; i<3; i++) + { + if (b->maxs[i] > region_maxs[i]) + return true; + if (b->mins[i] < region_mins[i]) + return true; + } + } + + return false; +} + +/* +=========== +Map_RegionOff + +Other filtering options may still be on +=========== +*/ +void Map_RegionOff (void) +{ + brush_t *b, *next; + int i; + + region_active = false; + for (i=0 ; i<3 ; i++) + { + region_maxs[i] = MAX_WORLD_COORD; + region_mins[i] = MIN_WORLD_COORD; + } + + for (b=filtered_brushes.next ; b != &filtered_brushes ; b=next) + { + next = b->next; + if (Map_IsBrushFiltered (b)) + continue; // still filtered + Brush_RemoveFromList (b); + if (active_brushes.next == NULL || active_brushes.prev == NULL) + { + active_brushes.next = &active_brushes; + active_brushes.prev = &active_brushes; + } + Brush_AddToList (b, &active_brushes); + } + + Sys_UpdateWindows (W_ALL); +} + +void Map_ApplyRegion (void) +{ + brush_t *b, *next; + + region_active = true; + for (b=active_brushes.next ; b != &active_brushes ; b=next) + { + next = b->next; + if (!Map_IsBrushFiltered (b)) + continue; // still filtered + Brush_RemoveFromList (b); + Brush_AddToList (b, &filtered_brushes); + } + + Sys_UpdateWindows (W_ALL); +} + + +/* +======================== +Map_RegionSelectedBrushes +======================== +*/ +void Map_RegionSelectedBrushes (void) +{ + Map_RegionOff (); + + if (selected_brushes.next == &selected_brushes) // nothing selected + { + Sys_Printf("Tried to region with no selection...\n"); + return; + } + region_active = true; + Select_GetBounds (region_mins, region_maxs); + + // move the entire active_brushes list to filtered_brushes + filtered_brushes.next = active_brushes.next; + filtered_brushes.prev = active_brushes.prev; + filtered_brushes.next->prev = &filtered_brushes; + filtered_brushes.prev->next = &filtered_brushes; + + // move the entire selected_brushes list to active_brushes + active_brushes.next = selected_brushes.next; + active_brushes.prev = selected_brushes.prev; + active_brushes.next->prev = &active_brushes; + active_brushes.prev->next = &active_brushes; + + // clear selected_brushes + selected_brushes.next = selected_brushes.prev = &selected_brushes; + + Sys_UpdateWindows (W_ALL); +} + + +/* +=========== +Map_RegionXY +=========== +*/ +void Map_RegionXY (void) +{ + Map_RegionOff (); + + FillInWorldZBounds(); + + region_mins[0] = g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5 * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(); + region_maxs[0] = g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5 * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(); + region_mins[1] = g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5 * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(); + region_maxs[1] = g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5 * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(); + region_mins[2] = g_fMinZ - 64; // -MAX_WORLD_COORD; // +/- 64 is just for shits, rather than touching the surface + region_maxs[2] = g_fMaxZ + 64; // MAX_WORLD_COORD; + Map_ApplyRegion (); +} + +/* +=========== +Map_RegionTallBrush +=========== +*/ +void Map_RegionTallBrush (void) +{ + brush_t *b; + + if (!QE_SingleBrush ()) + return; + + b = selected_brushes.next; + + Map_RegionOff (); + + FillInWorldZBounds(); + + VectorCopy (b->mins, region_mins); + VectorCopy (b->maxs, region_maxs); + region_mins[2] = g_fMinZ - 64; // -MAX_WORLD_COORD; + region_maxs[2] = g_fMaxZ + 64; // MAX_WORLD_COORD; + + Select_Delete (); + Map_ApplyRegion (); +} +/* +=========== +Map_RegionBrush +=========== +*/ +void Map_RegionBrush (void) +{ + brush_t *b; + + if (!QE_SingleBrush ()) + return; + + b = selected_brushes.next; + + Map_RegionOff (); + + VectorCopy (b->mins, region_mins); + VectorCopy (b->maxs, region_maxs); + + Select_Delete (); + Map_ApplyRegion (); +} + + +/* +============= +Q_strncpyz + +Safe strncpy that ensures a trailing zero +============= +*/ +void Q_strncpyz( char *dest, const char *src, int destsize ) +{ + strncpy( dest, src, destsize-1 ); + dest[destsize-1] = 0; +} + +void Q3_UniqueTargetName(CString& rStr, char *targNameNum, int &iHighest, qboolean targNameOnly) +{ + char number[16]; + char targName[128]; + int len, numStart, i; + int maxtarg = 0; + + if (!targNameNum || !targNameNum[0]) + {//No intital value! + return; + } + + Q_strncpyz(targName, targNameNum, sizeof(targName)); + len = strlen(targName); + numStart = 0; + for(i = 0; i < len; i++) + { + if( atoi(&targName[i]) == 0 )//Not a number + { + numStart = i; + } + else + { + break; + } + } + + //Remember the name and number seperately + Q_strncpyz(number, &targName[numStart + 1], sizeof(number)); + //truncate the name + targName[numStart + 1] = 0; + len = numStart; + + numStart += 1; + + for (entity_t* e=entities.next ; e != &entities ; e=e->next) + { + char* tn; + + if(targNameOnly) + {//looking for new targetname only + tn = ValueForKey (e, "targetname"); + if (tn && tn[0]) + { + if(strncmp(tn, targName, len) == 0) + {//Same name + int targetnum = atoi(tn + numStart); + if (targetnum > maxtarg) + { + maxtarg = targetnum; + } + } + } + } + else + {//looking for new target + tn = ValueForKey (e, "target"); + if (tn && tn[0]) + { + if(strncmp(tn, targName, len) == 0) + {//Same name + int targetnum = atoi(tn+numStart); + if (targetnum > maxtarg) + { + maxtarg = targetnum; + } + } + } + + tn = ValueForKey (e, "target2"); + if (tn && tn[0]) + { + if(strncmp(tn, targName, len) == 0) + {//Same name + int targetnum = atoi(tn+numStart); + if (targetnum > maxtarg) + { + maxtarg = targetnum; + } + } + } + + tn = ValueForKey (e, "target3"); + if (tn && tn[0]) + { + if(strncmp(tn, targName, len) == 0) + {//Same name + int targetnum = atoi(tn+numStart); + if (targetnum > maxtarg) + { + maxtarg = targetnum; + } + } + } + + tn = ValueForKey (e, "target4"); + if (tn && tn[0]) + { + if(strncmp(tn, targName, len) == 0) + {//Same name + int targetnum = atoi(tn+numStart); + if (targetnum > maxtarg) + { + maxtarg = targetnum; + } + } + } + } + } + + if (maxtarg < iHighest) + { + maxtarg = iHighest++; + } + else + { + iHighest = maxtarg+1; + } + + + rStr.Format("%s%i", targName, maxtarg+1); +} + +void UniqueTargetName(CString& rStr, int &iHighest) +{ + // make a unique target value + int maxtarg = 0; + for (entity_t* e=entities.next ; e != &entities ; e=e->next) + { + char* tn = ValueForKey (e, "targetname"); + + if (tn && tn[0]) + { + int targetnum = atoi(tn+1); + if (targetnum > maxtarg) + maxtarg = targetnum; + } +#ifndef QUAKE3 + else +#endif + { + tn = ValueForKey (e, "target"); + if (tn && tn[0]) + { + int targetnum = atoi(tn+1); + if (targetnum > maxtarg) + maxtarg = targetnum; + } +#ifdef QUAKE3 + tn = ValueForKey (e, "target2"); + if (tn && tn[0]) + { + int targetnum = atoi(tn+1); + if (targetnum > maxtarg) + maxtarg = targetnum; + } + + tn = ValueForKey (e, "target3"); + if (tn && tn[0]) + { + int targetnum = atoi(tn+1); + if (targetnum > maxtarg) + maxtarg = targetnum; + } + + tn = ValueForKey (e, "target4"); + if (tn && tn[0]) + { + int targetnum = atoi(tn+1); + if (targetnum > maxtarg) + maxtarg = targetnum; + } +#endif + } + } +#ifdef QUAKE3 + if (maxtarg < iHighest) + { + maxtarg = iHighest++; + } + else + { + iHighest = maxtarg+1; + } +#endif + + + rStr.Format("t%i", maxtarg+1); +} + +// +//================ +//Map_ImportFile +//================ +// +#ifdef QUAKE3 +extern bool gbInhibitDupTargetCorrection; +#endif + +void Map_ImportBuffer (char* buf) +{ + entity_t* ent; + brush_t* b = NULL; + CPtrArray ptrs; + + Select_Deselect(); + + g_qeglobals.d_parsed_brushes = 0; + if (buf) + { + CMapStringToString mapStr; + StartTokenParsing (buf); + g_qeglobals.d_num_entities = 0; + +#ifdef QUAKE3 + entity_t *pPrevLastEnt = entities.prev; // record this so later on we know who was the last existing brush before the new ones +#endif + + while (1) + { + + // use the selected brushes list as it's handy + //ent = Entity_Parse (false, &selected_brushes); + ent = Entity_Parse (false, &active_brushes); + if (!ent) + break; + if (!strcmp(ValueForKey (ent, "classname"), "worldspawn")) + { + // world brushes need to be added to the current world entity + + b=ent->brushes.onext; + while (b && b != &ent->brushes) + { + brush_t* bNext = b->onext; + Entity_UnlinkBrush(b); + Entity_LinkBrush(world_entity, b); + ptrs.Add(b); + b = bNext; + } + } + else + { + // the following bit remaps conflicting target/targetname key/value pairs + + + CString strKey; + CString strTarget; + + int iHighest = 0; + + +#ifdef QUAKE3 + if (!gbInhibitDupTargetCorrection) // a special request by Oz to make it easier to cut/paste 15(?!?!)-piece doors +#endif + { + + CString str = ValueForKey(ent, "targetname"); + if (str.GetLength() > 0) + { + if (FindEntity("targetname", str.GetBuffer(0))) + { + if (!mapStr.Lookup(str, strKey)) + { + #ifdef QUAKE3 + Q3_UniqueTargetName(strKey, str.GetBuffer(0), iHighest, true); + #else + UniqueTargetName(strKey, iHighest); + #endif + mapStr.SetAt(str, strKey); + } + SetKeyValue(ent, "targetname", strKey.GetBuffer(0)); + } + } + + str = ValueForKey(ent, "target"); + if (str.GetLength() > 0) + { + if (FindEntity("target", str.GetBuffer(0))) + { + if (!mapStr.Lookup(str, strKey)) + { + #ifdef QUAKE3 + Q3_UniqueTargetName(strKey, str.GetBuffer(0), iHighest, false); + #else + UniqueTargetName(strKey, iHighest); + #endif + mapStr.SetAt(str, strKey); + } + strTarget = strKey; + SetKeyValue(ent, "target", strTarget.GetBuffer(0)); + } + } + + #ifdef QUAKE3 + str = ValueForKey(ent, "target2"); + if (str.GetLength() > 0) + { + if (FindEntity("target2", str.GetBuffer(0))) + { + if (!mapStr.Lookup(str, strKey)) + { + Q3_UniqueTargetName(strKey, str.GetBuffer(0), iHighest, false); + mapStr.SetAt(str, strKey); + } + strTarget = strKey; + SetKeyValue(ent, "target2", strTarget.GetBuffer(0)); + } + } + + str = ValueForKey(ent, "target3"); + if (str.GetLength() > 0) + { + if (FindEntity("target3", str.GetBuffer(0))) + { + if (!mapStr.Lookup(str, strKey)) + { + Q3_UniqueTargetName(strKey, str.GetBuffer(0), iHighest, false); + mapStr.SetAt(str, strKey); + } + strTarget = strKey; + SetKeyValue(ent, "target3", strTarget.GetBuffer(0)); + } + } + + str = ValueForKey(ent, "target4"); + if (str.GetLength() > 0) + { + if (FindEntity("target4", str.GetBuffer(0))) + { + if (!mapStr.Lookup(str, strKey)) + { + Q3_UniqueTargetName(strKey, str.GetBuffer(0), iHighest, false); + mapStr.SetAt(str, strKey); + } + strTarget = strKey; + SetKeyValue(ent, "target4", strTarget.GetBuffer(0)); + } + } + #endif + } + + // add the entity to the end of the entity list + ent->next = &entities; + ent->prev = entities.prev; + entities.prev->next = ent; + entities.prev = ent; + g_qeglobals.d_num_entities++; + + for (b=ent->brushes.onext ; b != &ent->brushes ; b=b->onext) + { + ptrs.Add(b); + } + + } + } + +#ifdef QUAKE3 + // now go through the new/cloned brushes and remove any target/targetnames that aren't aprt of our nice little new group + // + if (!gbInhibitDupTargetCorrection) // a special request by Oz to make it easier to cut/paste 15(?!?!)-piece doors + for ( entity_t *ent2 = entities.prev; ent2 != pPrevLastEnt; ent2 = ent2->prev ) + { + CString str; + /*//FIXME: Only do this if you're pasting in? + // if no-one is targeting this ent2, zap it's targetname... + // + str = ValueForKey(ent2, "targetname"); + if (str.GetLength() > 0) + { + if (!FindEntity("target", str.GetBuffer(0)) && + !FindEntity("target2", str.GetBuffer(0)) && + !FindEntity("target3", str.GetBuffer(0)) && + !FindEntity("target4", str.GetBuffer(0)) + ) + { + DeleteKey(ent2, "targetname"); + } + } + // if ent2 is targeting something that's not part of our new little group then lose the target info... + // + str = ValueForKey(ent2, "target"); + if (str.GetLength() > 0) + { + if (!FindEntity("targetname", str.GetBuffer(0))) + { + DeleteKey(ent2, "target"); + } + } + + str = ValueForKey(ent2, "target2"); + if (str.GetLength() > 0) + { + if (!FindEntity("targetname", str.GetBuffer(0))) + { + DeleteKey(ent2, "target2"); + } + } + + str = ValueForKey(ent2, "target3"); + if (str.GetLength() > 0) + { + if (!FindEntity("targetname", str.GetBuffer(0))) + { + DeleteKey(ent2, "target3"); + } + } + + str = ValueForKey(ent2, "target4"); + if (str.GetLength() > 0) + { + if (!FindEntity("targetname", str.GetBuffer(0))) + { + DeleteKey(ent2, "target4"); + } + } + */ + + // now shuffle up the target ptrs so no gaps... + // + bool bShuffleHappened; + do + { + bShuffleHappened = false; + + CString str2; + + if (str = ValueForKey(ent2, "target4")) + { + if (str.GetLength() > 0) + { + if (str2 = ValueForKey(ent2, "target3")) + { + if (str2.GetLength() == 0) + { + SetKeyValue(ent2, "target3", str.GetBuffer(0)); + bShuffleHappened = true; + DeleteKey(ent2,"target4"); + } + } + } + } + + if (str = ValueForKey(ent2, "target3")) + { + if (str.GetLength() > 0) + { + if (str2 = ValueForKey(ent2, "target2")) + { + if (str2.GetLength() == 0) + { + SetKeyValue(ent2, "target2", str.GetBuffer(0)); + bShuffleHappened = true; + DeleteKey(ent2,"target3"); + } + } + } + } + + if (str = ValueForKey(ent2, "target2")) + { + if (str.GetLength() > 0) + { + if (str2 = ValueForKey(ent2, "target")) + { + if (str2.GetLength() == 0) + { + SetKeyValue(ent2, "target", str.GetBuffer(0)); + bShuffleHappened = true; + DeleteKey(ent2,"target2"); + } + } + } + } + } + while (bShuffleHappened); + } +#endif + + } + + g_bScreenUpdates = false; //deep inside Select_Brush's call chain, it might call UpdateWindows + for (int i = 0; i < ptrs.GetSize(); i++) + { + Brush_Build(reinterpret_cast(ptrs[i]), true, false); + Select_Brush(reinterpret_cast(ptrs[i])); + } + g_bScreenUpdates = true; + + ptrs.RemoveAll(); + + Sys_UpdateWindows (W_ALL); + Sys_MarkMapModified(); + modified = true; +} + + +// +//================ +//Map_ImportFile +//================ +// +void Map_ImportFile (char *filename) +{ + char* buf; + char temp[1024]; + Sys_BeginWait (); + QE_ConvertDOSToUnixName( temp, filename ); + if (LoadFile (filename, (void **)&buf) != -1) + { + Map_ImportBuffer(buf); + free(buf); + Map_BuildBrushData(); + } + Sys_UpdateWindows (W_ALL); + modified = true; + Sys_EndWait(); +} + +// +//=========== +//Map_SaveSelected +//=========== +// +// Saves selected world brushes and whole entities with partial/full selections +// +void Map_SaveSelected(char* pFilename) +{ + entity_t *e, *next; + FILE *f; + char temp[1024]; + int count; + + QE_ConvertDOSToUnixName(temp, pFilename); + f = fopen(pFilename, "w"); + + if (!f) + { + Sys_Printf ("ERROR!!!! Couldn't open %s\n", pFilename); + return; + } + + // write world entity first + Entity_WriteSelected(world_entity, f); + + // then write all other ents + count = 1; + for (e=entities.next ; e != &entities ; e=next) + { + fprintf (f, "// entity %i\n", count); + count++; + Entity_WriteSelected(e, f); + next = e->next; + } + fclose (f); +} + + +// +//=========== +//Map_SaveSelected +//=========== +// +// Saves selected world brushes and whole entities with partial/full selections +// +void Map_SaveSelected(CMemFile* pMemFile, CMemFile* pPatchFile) +{ + entity_t *e, *next; + int count; + + // write world entity first + Entity_WriteSelected(world_entity, pMemFile); + + // then write all other ents + count = 1; + for (e=entities.next ; e != &entities ; e=next) + { + MemFile_fprintf(pMemFile, "// entity %i\n", count); + count++; + Entity_WriteSelected(e, pMemFile); + next = e->next; + } + + //if (pPatchFile) + // Patch_WriteFile(pPatchFile); +} + + +void MemFile_fprintf(CMemFile* pMemFile, const char* pText, ...) +{ + char Buffer[4096]; + va_list args; + va_start (args,pText); + vsprintf(Buffer, pText, args); + pMemFile->Write(Buffer, strlen(Buffer)); +} \ No newline at end of file diff --git a/utils/Radiant/map.h b/utils/Radiant/map.h new file mode 100644 index 0000000..0cddd5f --- /dev/null +++ b/utils/Radiant/map.h @@ -0,0 +1,34 @@ +// map.h -- the state of the current world that all views are displaying + +extern char currentmap[1024]; + +// head/tail of doubly linked lists +extern brush_t active_brushes; // brushes currently being displayed +extern brush_t selected_brushes; // highlighted +extern face_t *selected_face; +extern brush_t *selected_face_brush; +extern brush_t filtered_brushes; // brushes that have been filtered or regioned + +extern entity_t entities; +extern entity_t *world_entity; // the world entity is NOT included in + // the entities chain + +extern qboolean modified; // for quit confirmations + +extern vec3_t region_mins, region_maxs; +extern qboolean region_active; + +void Map_LoadFile (const char *filename); +bool Map_SaveFile (const char *filename, qboolean use_region); +void Map_New (void); +void Map_BuildBrushData(void); + +void Map_RegionOff (void); +void Map_RegionXY (void); +void Map_RegionTallBrush (void); +void Map_RegionBrush (void); +void Map_RegionSelectedBrushes (void); +qboolean Map_IsBrushFiltered (brush_t *b); + +void Map_SaveSelected(CMemFile* pMemFile, CMemFile* pPatchFile = NULL); +void Map_ImportBuffer (char* buf); diff --git a/utils/Radiant/mapinfo.cpp b/utils/Radiant/mapinfo.cpp new file mode 100644 index 0000000..ee56126 --- /dev/null +++ b/utils/Radiant/mapinfo.cpp @@ -0,0 +1,93 @@ +// MapInfo.cpp : implementation file +// + +#include "stdafx.h" +#include "Radiant.h" +#include "MapInfo.h" +#include "qe3.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CMapInfo dialog + + +CMapInfo::CMapInfo(CWnd* pParent /*=NULL*/) + : CDialog(CMapInfo::IDD, pParent) +{ + //{{AFX_DATA_INIT(CMapInfo) + m_nNet = 0; + m_nTotalBrushes = 0; + m_nTotalEntities = 0; + //}}AFX_DATA_INIT +} + + +void CMapInfo::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CMapInfo) + DDX_Control(pDX, IDC_LIST_ENTITIES, m_lstEntity); + DDX_Text(pDX, IDC_EDIT_NET, m_nNet); + DDX_Text(pDX, IDC_EDIT_TOTALBRUSHES, m_nTotalBrushes); + DDX_Text(pDX, IDC_EDIT_TOTALENTITIES, m_nTotalEntities); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CMapInfo, CDialog) + //{{AFX_MSG_MAP(CMapInfo) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CMapInfo message handlers + +BOOL CMapInfo::OnInitDialog() +{ + CDialog::OnInitDialog(); + + m_nTotalBrushes = 0; + m_nTotalEntities = 0; + m_nNet = 0; + for (brush_t* pBrush=active_brushes.next ; pBrush != &active_brushes ; pBrush=pBrush->next) + { + m_nTotalBrushes++; + if (pBrush->owner == world_entity) + m_nNet++; + } + + + CMapStringToPtr mapEntity; + + int nValue = 0; + for (entity_t* pEntity=entities.next ; pEntity != &entities ; pEntity=pEntity->next) + { + m_nTotalEntities++; + nValue = 0; + mapEntity.Lookup(pEntity->eclass->name, reinterpret_cast(nValue)); + nValue++ ; + mapEntity.SetAt(pEntity->eclass->name, reinterpret_cast(nValue)); + } + + m_lstEntity.ResetContent(); + m_lstEntity.SetTabStops(96); + CString strKey; + POSITION pos = mapEntity.GetStartPosition(); + while (pos) + { + mapEntity.GetNextAssoc(pos, strKey, reinterpret_cast(nValue)); + CString strList; + strList.Format("%s\t%i", strKey, nValue); + m_lstEntity.AddString(strList); + } + + UpdateData(FALSE); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/utils/Radiant/mapinfo.h b/utils/Radiant/mapinfo.h new file mode 100644 index 0000000..5c2621b --- /dev/null +++ b/utils/Radiant/mapinfo.h @@ -0,0 +1,49 @@ +#if !defined(AFX_MAPINFO_H__C241B9A2_819F_11D1_B548_00AA00A410FC__INCLUDED_) +#define AFX_MAPINFO_H__C241B9A2_819F_11D1_B548_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// MapInfo.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CMapInfo dialog + +class CMapInfo : public CDialog +{ +// Construction +public: + CMapInfo(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CMapInfo) + enum { IDD = IDD_DLG_MAPINFO }; + CListBox m_lstEntity; + int m_nNet; + int m_nTotalBrushes; + int m_nTotalEntities; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CMapInfo) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CMapInfo) + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_MAPINFO_H__C241B9A2_819F_11D1_B548_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/materials.cpp b/utils/Radiant/materials.cpp new file mode 100644 index 0000000..508fb92 --- /dev/null +++ b/utils/Radiant/materials.cpp @@ -0,0 +1,534 @@ +// Filename:- materials.cpp +// +// (File contains stuff imported from SoFED to fill in the materials dialogue box, only applies to SOF) +// +#include "stdafx.h" // including these for what would otherwise be blank under Q3 keeps .PCHs working better. +#include "Radiant.h" + + + +// Note: this file contains some stuff that's duplicated in other files (eg parse.cpp), but the code here has so +// many fiddly differences (and is only loaded to fill in one dialogue in the SOF compile-version of this editor +// that I've just kepy it all self-contained here, so it can be ripped out and disposed off later when the project +// is done. + + +#ifdef SOF // may as well hide entire file except for SOF mode +#include "materials.h" +#include "direct.h" + +//#include "cmdlib.h" +//#include "scriplib.h" + + +#define BASEDIRNAME "base" +#define PATHSEPERATOR '/' + + +/* +============================================================================= + + PARSING STUFF + +============================================================================= +*/ + +typedef struct +{ + char filename[1024]; + char *buffer,*script_p,*end_p; + int line; +} script_t; + +#define MAX_INCLUDES 8 +script_t scriptstack[MAX_INCLUDES]; +script_t *script; +int sof_scriptline; + +char sof_token[MAXTOKEN]; +qboolean endofscript; +qboolean tokenready; // only true if UnGetScriptToken was just called + + + + +// protos... +// +void AddScriptToStack (char *filename); +void LoadScriptFile (char *filename); +void ParseFromMemory (char *buffer, int size); +void UnGetScriptToken (void); +qboolean EndOfScript (qboolean crossline); +qboolean GetScriptToken (qboolean crossline); +qboolean ScriptTokenAvailable (void); +char *ExpandPath (char *path); + + + + +extern char sof_token[MAXTOKEN]; +extern int sof_scriptline; + +void StartTokenParsing (char *data); +qboolean GetToken (qboolean crossline); +void UngetToken (void); +qboolean TokenAvailable (void); + + + + + +void Q_getwd (char *out) +{ +#ifdef WIN32 + _getcwd (out, 256); + strcat (out, "\\"); +#else + getwd (out); + strcat (out, "/"); +#endif +} + + + + + +/* + +qdir will hold the path up to the quake directory, including the slash + + f:\quake\ + /raid/quake/ + +gamedir will hold qdir + the game directory (id1, id2, etc) + + */ + +char qdir[1024]; +char gamedir[1024]; + +void SetQdirFromPath (char *path) +{ + char temp[1024]; + char *c; + int len; + + if (!(path[0] == '/' || path[0] == '\\' || path[1] == ':')) + { // path is partial + Q_getwd (temp); + strcat (temp, path); + path = temp; + } + + // search for "quake2" in path +#if 1 + len = strlen(BASEDIRNAME); + + for (c=path+strlen(path)-1 ; c != path ; c--) + { + if (!Q_strncasecmp (c, BASEDIRNAME, len)) + { + strncpy (qdir, path, c-path); + Sys_Printf ("qdir: %s\n", qdir); + + while (*c) + { + if (*c == '/' || *c == '\\') + { + strncpy (gamedir, path, c+1-path); + Sys_Printf ("gamedir: %s\n", gamedir); + return; + } + + c++; + } + +// assert(0); + Error ("Gamedir at root in %s", path); + + return; + } + } + +// assert(0); + Error ("SetQdirFromPath: no '%s' in %s", BASEDIRNAME, path); +#else + len = strlen(BASEDIRNAME); + for (c=path+strlen(path)-1 ; c != path ; c--) + if (!Q_strncasecmp (c, BASEDIRNAME, len)) + { + strncpy (qdir, path, c+len+1-path); + qprintf ("qdir: %s\n", qdir); + c += len+1; + while (*c) + { + if (*c == '/' || *c == '\\') + { + strncpy (gamedir, path, c+1-path); + qprintf ("gamedir: %s\n", gamedir); + return; + } + c++; + } + Error ("No gamedir in %s", path); + return; + } + Error ("SetQdirFromPath: no '%s' in %s", BASEDIRNAME, path); +#endif +} + + + +materialtype_t defaultmaterialtypes[] = +{ + {"gravel", MATERIAL_GRAVEL}, + {"metal", MATERIAL_METAL}, + {"stone", MATERIAL_STONE}, + {"wood", MATERIAL_WOOD}, + {NULL, 0} +}; + +materialtype_t *materialtypes = NULL;// defaultmaterialtypes; + +void QFile_ReadMaterialTypes(char* filename) +{ + int i; + FILE *f; + + if (materialtypes != defaultmaterialtypes) + { + if (materialtypes) + free(materialtypes); + materialtypes = (materialtype_t*)malloc(256 * sizeof(materialtype_t)); + } + + f = fopen (filename, "rb"); + if (!f) + { + materialtypes = defaultmaterialtypes; + return; + } + fclose (f); + + LoadScriptFile(filename); + i = 0; + + do + { + GetScriptToken (true); + if (endofscript) + { + break; + } + if (strcmp(sof_token, "material") != 0) + { + while (ScriptTokenAvailable()) + { + GetScriptToken(false); + } + } + else + { + GetScriptToken(false); + materialtypes[i].name = (char*)malloc(strlen(sof_token) + 1); + strcpy(materialtypes[i].name, sof_token); + GetScriptToken (false); + materialtypes[i].value = atoi(sof_token); + } + } + while (i++ < 255); + + materialtypes[i].name = NULL; + materialtypes[i].value = 0; +} + + + +char *ExpandPath (char *path) +{ + static char full[1024]; + if (!qdir) + Error ("ExpandPath called without qdir set"); + if (path[0] == '/' || path[0] == '\\' || path[1] == ':') + return path; + sprintf (full, "%s%s", qdir, path); + return full; +} + + + +/* +============== +AddScriptToStack +============== +*/ +void AddScriptToStack (char *filename) +{ + int size; + + script++; + if (script == &scriptstack[MAX_INCLUDES]) + Error ("script file exceeded MAX_INCLUDES"); + strcpy (script->filename, ExpandPath (filename) ); + + size = LoadFile (script->filename, (void **)&script->buffer); + + printf ("entering %s\n", script->filename); + + script->line = 1; + + script->script_p = script->buffer; + script->end_p = script->buffer + size; +} + + +/* +============== +LoadScriptFile +============== +*/ +void LoadScriptFile (char *filename) +{ + script = scriptstack; + AddScriptToStack (filename); + + endofscript = false; + tokenready = false; +} + + +/* +============== +ParseFromMemory +============== +*/ +void ParseFromMemory (char *buffer, int size) +{ + script = scriptstack; + script++; + if (script == &scriptstack[MAX_INCLUDES]) + Error ("script file exceeded MAX_INCLUDES"); + strcpy (script->filename, "memory buffer" ); + + script->buffer = buffer; + script->line = 1; + script->script_p = script->buffer; + script->end_p = script->buffer + size; + + endofscript = false; + tokenready = false; +} + + +/* +============== +UnGetScriptToken + +Signals that the current sof_token was not used, and should be reported +for the next GetScriptToken. Note that + +GetScriptToken (true); +UnGetScriptToken (); +GetScriptToken (false); + +could cross a line boundary. +============== +*/ +void UnGetScriptToken (void) +{ + tokenready = true; +} + + +qboolean EndOfScript (qboolean crossline) +{ + if (!crossline) + Error ("Line %i is incomplete\n",sof_scriptline); + + if (!strcmp (script->filename, "memory buffer")) + { + endofscript = true; + return false; + } + + free (script->buffer); + if (script == scriptstack+1) + { + endofscript = true; + return false; + } + script--; + sof_scriptline = script->line; + printf ("returning to %s\n", script->filename); + return GetScriptToken (crossline); +} + +/* +============== +GetScriptToken +============== +*/ +qboolean GetScriptToken (qboolean crossline) +{ + char *token_p; + + if (tokenready) // is a sof_token allready waiting? + { + tokenready = false; + return true; + } + + if (script->script_p >= script->end_p) + return EndOfScript (crossline); + +// +// skip space +// +skipspace: + while (*script->script_p <= 32) + { + if (script->script_p >= script->end_p) + return EndOfScript (crossline); + if (*script->script_p++ == '\n') + { + if (!crossline) + Error ("Line %i is incomplete\n",sof_scriptline); + sof_scriptline = script->line++; + } + } + + if (script->script_p >= script->end_p) + return EndOfScript (crossline); + + // ; # // comments + if (*script->script_p == ';' || *script->script_p == '#' + || ( script->script_p[0] == '/' && script->script_p[1] == '/') ) + { + if (!crossline) + Error ("Line %i is incomplete\n",sof_scriptline); + while (*script->script_p++ != '\n') + if (script->script_p >= script->end_p) + return EndOfScript (crossline); + goto skipspace; + } + + // /* */ comments + if (script->script_p[0] == '/' && script->script_p[1] == '*') + { + if (!crossline) + Error ("Line %i is incomplete\n",sof_scriptline); + script->script_p+=2; + while (script->script_p[0] != '*' && script->script_p[1] != '/') + { + script->script_p++; + if (script->script_p >= script->end_p) + return EndOfScript (crossline); + } + script->script_p += 2; + goto skipspace; + } + +// +// copy sof_token +// + token_p = sof_token; + + if (*script->script_p == '"') + { + // quoted sof_token + script->script_p++; + while (*script->script_p != '"') + { + *token_p++ = *script->script_p++; + if (script->script_p == script->end_p) + break; + if (token_p == &sof_token[MAXTOKEN]) + Error ("Token too large on line %i\n",sof_scriptline); + } + script->script_p++; + } + else // regular sof_token + while ( *script->script_p > 32 && *script->script_p != ';') + { + *token_p++ = *script->script_p++; + if (script->script_p == script->end_p) + break; + if (token_p == &sof_token[MAXTOKEN]) + Error ("Token too large on line %i\n",sof_scriptline); + } + + *token_p = 0; + + if (!strcmp (sof_token, "$include")) + { + GetScriptToken (false); + AddScriptToStack (sof_token); + return GetScriptToken (crossline); + } + + return true; +} + + +/* +============== +ScriptTokenAvailable + +Returns true if there is another sof_token on the line +============== +*/ +qboolean ScriptTokenAvailable (void) +{ + char *search_p; + + search_p = script->script_p; + + if (search_p >= script->end_p) + return false; + + while ( *search_p <= 32) + { + if (*search_p == '\n') + return false; + search_p++; + if (search_p == script->end_p) + return false; + + } + + if (*search_p == ';') + return false; + + return true; +} + + + + + +char *ExpandArg (char *path) +{ + static char full[1024]; + + if (path[0] != '/' && path[0] != '\\' && path[1] != ':') + { + Q_getwd (full); + strcat (full, path); + } + else + strcpy (full, path); + return full; +} + + + + + + + +#endif // #ifdef SOF + +///////////////////// eof ////////////////////////// + + + diff --git a/utils/Radiant/materials.h b/utils/Radiant/materials.h new file mode 100644 index 0000000..b5c65a2 --- /dev/null +++ b/utils/Radiant/materials.h @@ -0,0 +1,33 @@ +// Filename:- materials.h +// +#ifndef MATERIALS_H +#define MATERIALS_H +#ifdef SOF + + +typedef struct +{ + char *name; + int value; +} materialtype_t; + +enum +{ + MATERIAL_GRAVEL, + MATERIAL_METAL, + MATERIAL_STONE, + MATERIAL_WOOD, +}; + +extern materialtype_t *materialtypes; + + +void QFile_ReadMaterialTypes(char* filename); +char *ExpandArg (char *path); + + +#endif // #ifdef SOF +#endif // #ifndef MATERIALS_H + +////////////////// eof ///////////////////// + diff --git a/utils/Radiant/mathlib.cpp b/utils/Radiant/mathlib.cpp new file mode 100644 index 0000000..0c7a810 --- /dev/null +++ b/utils/Radiant/mathlib.cpp @@ -0,0 +1,161 @@ +// mathlib.c -- math primitives + +#include "stdafx.h" +#include "cmdlib.h" +#include "mathlib.h" + +vec3_t vec3_origin = {0.0f,0.0f,0.0f}; + + +float VectorLength(vec3_t v) +{ + int i; + float length; + + length = 0.0f; + for (i=0 ; i< 3 ; i++) + length += v[i]*v[i]; + length = (float)sqrt (length); + + return length; +} + +qboolean VectorCompare (vec3_t v1, vec3_t v2) +{ + int i; + + for (i=0 ; i<3 ; i++) + if (fabs(v1[i]-v2[i]) > EQUAL_EPSILON) + return false; + + return true; +} + +vec_t Q_rint (vec_t in) +{ + if (g_PrefsDlg.m_bNoClamp) + return in; + else + return (float)floor (in + 0.5); +} + +void VectorMA (vec3_t va, float scale, vec3_t vb, vec3_t vc) +{ + vc[0] = va[0] + scale*vb[0]; + vc[1] = va[1] + scale*vb[1]; + vc[2] = va[2] + scale*vb[2]; +} + +void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross) +{ + cross[0] = v1[1]*v2[2] - v1[2]*v2[1]; + cross[1] = v1[2]*v2[0] - v1[0]*v2[2]; + cross[2] = v1[0]*v2[1] - v1[1]*v2[0]; +} + +vec_t _DotProduct (vec3_t v1, vec3_t v2) +{ + return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]; +} + +void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out) +{ + out[0] = va[0]-vb[0]; + out[1] = va[1]-vb[1]; + out[2] = va[2]-vb[2]; +} + +void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out) +{ + out[0] = va[0]+vb[0]; + out[1] = va[1]+vb[1]; + out[2] = va[2]+vb[2]; +} + +void _VectorCopy (vec3_t in, vec3_t out) +{ + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; +} + +vec_t VectorNormalize (vec3_t v) +{ + int i; + float length; + + length = 0.0f; + for (i=0 ; i< 3 ; i++) + length += v[i]*v[i]; + length = (float)sqrt (length); + if (length == 0) + return (vec_t)0; + + for (i=0 ; i< 3 ; i++) + v[i] /= length; + + return length; +} + +void VectorInverse (vec3_t v) +{ + v[0] = -v[0]; + v[1] = -v[1]; + v[2] = -v[2]; +} + +void VectorScale (vec3_t v, vec_t scale, vec3_t out) +{ + out[0] = v[0] * scale; + out[1] = v[1] * scale; + out[2] = v[2] * scale; +} + + +void VectorRotate (vec3_t vIn, vec3_t vRotation, vec3_t out) +{ + vec3_t vWork, va; + VectorCopy(vIn, va); + VectorCopy(va, vWork); + int nIndex[3][2]; + nIndex[0][0] = 1; nIndex[0][1] = 2; + nIndex[1][0] = 2; nIndex[1][1] = 0; + nIndex[2][0] = 0; nIndex[2][1] = 1; + + for (int i = 0; i < 3; i++) + { + if (vRotation[i] != 0) + { + double dAngle = vRotation[i] / 180 * Q_PI; + double c = cos(dAngle); + double s = sin(dAngle); + vWork[nIndex[i][0]] = va[nIndex[i][0]] * c - va[nIndex[i][1]] * s; + vWork[nIndex[i][1]] = va[nIndex[i][0]] * s + va[nIndex[i][1]] * c; + } + VectorCopy(vWork, va); + } + VectorCopy(vWork, out); +} + +void VectorRotate (vec3_t vIn, vec3_t vRotation, vec3_t vOrigin, vec3_t out) +{ + vec3_t vTemp, vTemp2; + VectorSubtract(vIn, vOrigin, vTemp); + VectorRotate(vTemp, vRotation, vTemp2); + VectorAdd(vTemp2, vOrigin, out); +} + +void VectorPolar(vec3_t v, float radius, float theta, float phi) +{ + v[0]=float(radius * cos(theta) * cos(phi)); + v[1]=float(radius * sin(theta) * cos(phi)); + v[2]=float(radius * sin(phi)); +} + +void VectorSnap(vec3_t v) +{ + for (int i = 0; i < 3; i++) + { + v[i] = floor (v[i] + 0.5); + } +} diff --git a/utils/Radiant/mathlib.h b/utils/Radiant/mathlib.h new file mode 100644 index 0000000..dc946e0 --- /dev/null +++ b/utils/Radiant/mathlib.h @@ -0,0 +1,47 @@ +#ifndef __MATHLIB__ +#define __MATHLIB__ + +// mathlib.h + +#include + +typedef float vec_t; +typedef vec_t vec3_t[3]; + +#define SIDE_FRONT 0 +#define SIDE_ON 2 +#define SIDE_BACK 1 +#define SIDE_CROSS -2 + +#define Q_PI 3.14159265358979323846 + +extern vec3_t vec3_origin; + +#define EQUAL_EPSILON 0.001 + +qboolean VectorCompare (vec3_t v1, vec3_t v2); + +#define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+x[2]*y[2]) +#define VectorSubtract(a,b,c) {c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];} +#define VectorAdd(a,b,c) {c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];} +#define VectorCopy(a,b) {b[0]=a[0];b[1]=a[1];b[2]=a[2];} +#define VectorSet(v, a, b, c) {v[0]=a;v[1]=b;v[2]=c;} + +vec_t Q_rint (vec_t in); +vec_t _DotProduct (vec3_t v1, vec3_t v2); +void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out); +void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out); +void _VectorCopy (vec3_t in, vec3_t out); + +float VectorLength(vec3_t v); + +void VectorMA (vec3_t va, float scale, vec3_t vb, vec3_t vc); + +void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross); +vec_t VectorNormalize (vec3_t v); +void VectorInverse (vec3_t v); +void VectorScale (vec3_t v, vec_t scale, vec3_t out); +void VectorPolar(vec3_t v, float radius, float theta, float phi); +void VectorSnap(vec3_t v); + +#endif diff --git a/utils/Radiant/mru.cpp b/utils/Radiant/mru.cpp new file mode 100644 index 0000000..9aaa798 --- /dev/null +++ b/utils/Radiant/mru.cpp @@ -0,0 +1,650 @@ +//************************************************************* +// File name: mru.c +// +// Description: +// +// Routines for MRU support +// +// Development Team: +// +// Gilles Vollant (100144.2636@compuserve.com) +// +//************************************************************* + +#include "stdafx.h" +#include +#include +#include + +#include "mru.h" +// CreateMruMenu : MRUMENU constructor +// wNbLruShowInit : nb of item showed in menu +// wNbLruMenuInit : nb of item stored in memory +// wMaxSizeLruItemInit : size max. of filename + + +//************************************************************* +// +// CreateMruMenu() +// +// Purpose: +// +// Allocate and Initialize an MRU and return a pointer on it +// +// +// Parameters: +// +// WORD wNbLruShowInit - Maximum number of item displayed on menu +// WORD wNbLruMenuInit - Maximum number of item stored in memory +// WORD wMaxSizeLruItemInit - Maximum size of an item (ie size of pathname) +// WORD wIdMruInit - ID of the first item in the menu (default:IDMRU) +// +// +// Return: (LPMRUMENU) +// +// Pointer on a MRUMENU structure, used by other function +// +// +// Comments: +// wNbLruShowInit <= wNbLruMenuInit +// +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* + +LPMRUMENU CreateMruMenu (WORD wNbLruShowInit, + WORD wNbLruMenuInit,WORD wMaxSizeLruItemInit,WORD wIdMruInit) +{ +LPMRUMENU lpMruMenu; + lpMruMenu = (LPMRUMENU)GlobalAllocPtr(GHND,sizeof(MRUMENU)); + + lpMruMenu->wNbItemFill = 0; + lpMruMenu->wNbLruMenu = wNbLruMenuInit; + lpMruMenu->wNbLruShow = wNbLruShowInit; + lpMruMenu->wIdMru = wIdMruInit; + lpMruMenu->wMaxSizeLruItem = wMaxSizeLruItemInit; + lpMruMenu->lpMRU = (LPSTR)GlobalAllocPtr(GHND, + lpMruMenu->wNbLruMenu*(UINT)lpMruMenu->wMaxSizeLruItem); + if (lpMruMenu->lpMRU == NULL) + { + GlobalFreePtr(lpMruMenu); + lpMruMenu = NULL; + } + return lpMruMenu; +} + +//************************************************************* +// +// CreateMruMenuDefault() +// +// Purpose: +// +// Allocate and Initialize an MRU and return a pointer on it +// Use default parameter +// +// +// Parameters: +// +// +// Return: (LPMRUMENU) +// +// Pointer on a MRUMENU structure, used by other function +// +// +// Comments: +// +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* + +LPMRUMENU CreateMruMenuDefault() +{ + return CreateMruMenu (NBMRUMENUSHOW,NBMRUMENU,MAXSIZEMRUITEM,IDMRU); +} + + +//************************************************************* +// +// DeleteMruMenu() +// +// Purpose: +// Destructor : +// Clean and free a MRUMENU structure +// +// Parameters: +// +// LPMRUMENU lpMruMenu - pointer on MRUMENU, allocated +// by CreateMruMenu() or CreateMruMenuDefault() +// +// +// Return: void +// +// +// Comments: +// +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* +void DeleteMruMenu(LPMRUMENU lpMruMenu) +{ + GlobalFreePtr(lpMruMenu->lpMRU); + GlobalFreePtr(lpMruMenu); +} + +//************************************************************* +// +// SetNbLruShow() +// +// Purpose: +// Change the maximum number of item displayed on menu +// +// Parameters: +// LPMRUMENU lpMruMenu - pointer on MRUMENU +// WORD wNbLruShowInit - Maximum number of item displayed on menu +// +// +// Return: void +// +// +// Comments: +// +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* +void SetNbLruShow (LPMRUMENU lpMruMenu,WORD wNbLruShowInit) +{ + lpMruMenu->wNbLruShow = min(wNbLruShowInit,lpMruMenu->wNbLruMenu); +} + +//************************************************************* +// +// SetMenuItem() +// +// Purpose: +// Set the filename of an item +// +// Parameters: +// LPMRUMENU lpMruMenu - pointer on MRUMENU +// WORD wItem - Number of Item to set, zero based +// LPSTR lpItem - String, contain the filename of the item +// +// +// Return: (BOOL) +// TRUE - Function run successfully +// FALSE - Function don't run successfully +// +// +// Comments: +// used when load .INI or reg database +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* +BOOL SetMenuItem (LPMRUMENU lpMruMenu,WORD wItem,LPSTR lpItem) +{ + if (wItem >= NBMRUMENU) + return FALSE; + _fstrncpy((lpMruMenu->lpMRU) + + ((lpMruMenu->wMaxSizeLruItem) * (UINT)wItem), + lpItem,lpMruMenu->wMaxSizeLruItem-1); + lpMruMenu->wNbItemFill = max(lpMruMenu->wNbItemFill,wItem+1); + return TRUE; +} + +//************************************************************* +// +// GetMenuItem() +// +// Purpose: +// Get the filename of an item +// +// Parameters: +// LPMRUMENU lpMruMenu - pointer on MRUMENU +// WORD wItem - Number of Item to set, zero based +// BOOL fIDMBased - TRUE : wItem is based on ID menu item +// FALSE : wItem is zero-based +// LPSTR lpItem - String where the filename of the item will be +// stored by GetMenuItem() +// UINT uiSize - Size of the lpItem buffer +// +// +// Return: (BOOL) +// TRUE - Function run successfully +// FALSE - Function don't run successfully +// +// +// Comments: +// Used for saving in .INI or reg database, or when user select +// an MRU in File menu +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* +BOOL GetMenuItem (LPMRUMENU lpMruMenu,WORD wItem, + BOOL fIDMBased,LPSTR lpItem,UINT uiSize) +{ + if (fIDMBased) + wItem -= (lpMruMenu->wIdMru + 1); + if (wItem >= lpMruMenu->wNbItemFill) + return FALSE; + _fstrncpy(lpItem,(lpMruMenu->lpMRU) + + ((lpMruMenu->wMaxSizeLruItem) * (UINT)(wItem)),uiSize); + *(lpItem+uiSize-1) = '\0'; + return TRUE; +} + +//************************************************************* +// +// AddNewItem() +// +// Purpose: +// Add an item at the begin of the list +// +// Parameters: +// LPMRUMENU lpMruMenu - pointer on MRUMENU +// LPSTR lpItem - String contain the filename to add +// +// Return: (BOOL) +// TRUE - Function run successfully +// FALSE - Function don't run successfully +// +// +// Comments: +// Used when used open a file (using File Open common +// dialog, Drag and drop or MRU) +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* +void AddNewItem (LPMRUMENU lpMruMenu,LPSTR lpItem) +{ +WORD i,j; + for (i=0;iwNbItemFill;i++) + if (lstrcmpi(lpItem,(lpMruMenu->lpMRU) + + ((lpMruMenu->wMaxSizeLruItem) * (UINT)i)) == 0) + { + // Shift the other items + for (j=i;j>0;j--) + lstrcpy((lpMruMenu->lpMRU) + (lpMruMenu->wMaxSizeLruItem * (UINT)j), + (lpMruMenu->lpMRU) + (lpMruMenu->wMaxSizeLruItem * (UINT)(j-1))); + _fstrncpy(lpMruMenu->lpMRU,lpItem,lpMruMenu->wMaxSizeLruItem-1); + return ; + } + lpMruMenu->wNbItemFill = min(lpMruMenu->wNbItemFill+1,lpMruMenu->wNbLruMenu); + for (i=lpMruMenu->wNbItemFill-1;i>0;i--) + lstrcpy(lpMruMenu->lpMRU + (lpMruMenu->wMaxSizeLruItem * (UINT)i), + lpMruMenu->lpMRU + (lpMruMenu->wMaxSizeLruItem * (UINT)(i-1))); + _fstrncpy(lpMruMenu->lpMRU,lpItem,lpMruMenu->wMaxSizeLruItem-1); +} + +//************************************************************* +// +// DelMenuItem() +// +// Purpose: +// Delete an item +// +// Parameters: +// LPMRUMENU lpMruMenu - pointer on MRUMENU +// WORD wItem - Number of Item to set, zero based +// BOOL fIDMBased - TRUE : wItem is based on ID menu item +// FALSE : wItem is zero-based +// +// Return: (BOOL) +// TRUE - Function run successfully +// FALSE - Function don't run successfully +// +// +// Comments: +// Used when used open a file, using MRU, and when an error +// occured (by example, when file was deleted) +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* +BOOL DelMenuItem(LPMRUMENU lpMruMenu,WORD wItem,BOOL fIDMBased) +{ +WORD i; + if (fIDMBased) + wItem -= (lpMruMenu->wIdMru + 1); + if (lpMruMenu->wNbItemFill <= wItem) + return FALSE; + lpMruMenu->wNbItemFill--; + for (i=wItem;iwNbItemFill;i++) + lstrcpy(lpMruMenu->lpMRU + (lpMruMenu->wMaxSizeLruItem * (UINT)i), + lpMruMenu->lpMRU + (lpMruMenu->wMaxSizeLruItem * (UINT)(i+1))); + return TRUE; +} + +//************************************************************* +// +// PlaceMenuMRUItem() +// +// Purpose: +// Add MRU at the end of a menu +// +// Parameters: +// LPMRUMENU lpMruMenu - pointer on MRUMENU +// HMENU hMenu - Handle of menu where MRU must be added +// UINT uiItem - Item of menu entry where MRU must be added +// +// Return: void +// +// +// Comments: +// Used MRU is modified, for refresh the File menu +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* +void PlaceMenuMRUItem(LPMRUMENU lpMruMenu,HMENU hMenu,UINT uiItem) +{ +int i; +WORD wNbShow; + if (hMenu == NULL) + return; + // remove old MRU in menu + for (i=0;i<=(int)(lpMruMenu->wNbLruMenu);i++) + RemoveMenu(hMenu,i+lpMruMenu->wIdMru,MF_BYCOMMAND); + + if (lpMruMenu->wNbItemFill == 0) + return; + + // If they are item, insert a separator before the files + InsertMenu(hMenu,uiItem,MF_SEPARATOR,lpMruMenu->wIdMru,NULL); + + wNbShow = min(lpMruMenu->wNbItemFill,lpMruMenu->wNbLruShow); + for (i=(int)wNbShow-1;i>=0;i--) + { + LPSTR lpTxt; + if (lpTxt = (LPSTR)GlobalAllocPtr(GHND,lpMruMenu->wMaxSizeLruItem + 20)) + { + wsprintf(lpTxt,"&%lu %s", + (DWORD)(i+1),lpMruMenu->lpMRU + (lpMruMenu->wMaxSizeLruItem*(UINT)i)); + InsertMenu(hMenu,(((WORD)i)!=(wNbShow-1)) ? (lpMruMenu->wIdMru+i+2) : lpMruMenu->wIdMru, + MF_STRING,lpMruMenu->wIdMru+i+1,lpTxt); + GlobalFreePtr(lpTxt); + } + } + +} + +/////////////////////////////////////////// + + + +//************************************************************* +// +// SaveMruInIni() +// +// Purpose: +// Save MRU in a private .INI +// +// Parameters: +// LPMRUMENU lpMruMenu - pointer on MRUMENU +// LPSTR lpszSection - Points to a null-terminated string containing +// the name of the section +// LPSTR lpszFile - Points to a null-terminated string that names +// the initialization file. +// +// Return: (BOOL) +// TRUE - Function run successfully +// FALSE - Function don't run successfully +// +// +// Comments: +// See WritePrivateProfileString API for more info on lpszSection and lpszFile +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* +BOOL SaveMruInIni(LPMRUMENU lpMruMenu,LPSTR lpszSection,LPSTR lpszFile) +{ +LPSTR lpTxt; +WORD i; + + lpTxt = (LPSTR)GlobalAllocPtr(GHND,lpMruMenu->wMaxSizeLruItem + 20); + if (lpTxt == NULL) + return FALSE; + + for (i=0;iwNbLruMenu;i++) + { + char szEntry[16]; + wsprintf(szEntry,"File%lu",(DWORD)i+1); + if (!GetMenuItem(lpMruMenu,i,FALSE,lpTxt,lpMruMenu->wMaxSizeLruItem + 10)) + *lpTxt = '\0'; + WritePrivateProfileString(lpszSection,szEntry,lpTxt,lpszFile); + } + GlobalFreePtr(lpTxt); + WritePrivateProfileString(NULL,NULL,NULL,lpszFile); // flush cache + return TRUE; +} + + +//************************************************************* +// +// LoadMruInIni() +// +// Purpose: +// Load MRU from a private .INI +// +// Parameters: +// LPMRUMENU lpMruMenu - pointer on MRUMENU +// LPSTR lpszSection - Points to a null-terminated string containing +// the name of the section +// LPSTR lpszFile - Points to a null-terminated string that names +// the initialization file. +// +// Return: (BOOL) +// TRUE - Function run successfully +// FALSE - Function don't run successfully +// +// +// Comments: +// See GetPrivateProfileString API for more info on lpszSection and lpszFile +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* +BOOL LoadMruInIni(LPMRUMENU lpMruMenu,LPSTR lpszSection,LPSTR lpszFile) +{ +LPSTR lpTxt; +WORD i; + lpTxt = (LPSTR)GlobalAllocPtr(GHND,lpMruMenu->wMaxSizeLruItem + 20); + if (lpTxt == NULL) + return FALSE; + + for (i=0;iwNbLruMenu;i++) + { + char szEntry[16]; + + wsprintf(szEntry,"File%lu",(DWORD)i+1); + GetPrivateProfileString(lpszSection,szEntry,"",lpTxt, + lpMruMenu->wMaxSizeLruItem + 10,lpszFile); + if (*lpTxt == '\0') + break; + SetMenuItem(lpMruMenu,i,lpTxt); + } + GlobalFreePtr(lpTxt); + return TRUE; +} + +#ifdef WIN32 + +BOOL IsWin395OrHigher(void) +{ + WORD wVer; + + wVer = LOWORD(GetVersion()); + wVer = (((WORD)LOBYTE(wVer)) << 8) | (WORD)HIBYTE(wVer); + + return (wVer >= 0x035F); // 5F = 95 dec +} + + +//************************************************************* +// +// SaveMruInReg() +// +// Purpose: +// Save MRU in the registry +// +// Parameters: +// LPMRUMENU lpMruMenu - pointer on MRUMENU +// LPSTR lpszKey - Points to a null-terminated string +// specifying the name of a key that +// this function opens or creates. +// +// Return: (BOOL) +// TRUE - Function run successfully +// FALSE - Function don't run successfully +// +// +// Comments: +// Win32 function designed for Windows NT and Windows 95 +// See RegCreateKeyEx API for more info on lpszKey +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* +BOOL SaveMruInReg(LPMRUMENU lpMruMenu,LPSTR lpszKey) +{ +LPSTR lpTxt; +WORD i; +HKEY hCurKey; +DWORD dwDisp; + + lpTxt = (LPSTR)GlobalAllocPtr(GHND,lpMruMenu->wMaxSizeLruItem + 20); + if (lpTxt == NULL) + return FALSE; + + RegCreateKeyEx(HKEY_CURRENT_USER,lpszKey,0,NULL, + REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&hCurKey,&dwDisp); + + for (i=0;iwNbLruMenu;i++) + { + char szEntry[16]; + wsprintf(szEntry,"File%lu",(DWORD)i+1); + if (!GetMenuItem(lpMruMenu,i,FALSE,lpTxt,lpMruMenu->wMaxSizeLruItem + 10)) + *lpTxt = '\0'; + RegSetValueEx(hCurKey,szEntry,0,REG_SZ,(unsigned char*)lpTxt,lstrlen(lpTxt)); + } + RegCloseKey(hCurKey); + GlobalFreePtr(lpTxt); + return TRUE; +} + +//************************************************************* +// +// LoadMruInReg() +// +// Purpose: +// Load MRU from the registry +// +// Parameters: +// LPMRUMENU lpMruMenu - pointer on MRUMENU +// LPSTR lpszKey - Points to a null-terminated string +// specifying the name of a key that +// this function opens or creates. +// +// Return: (BOOL) +// TRUE - Function run successfully +// FALSE - Function don't run successfully +// +// +// Comments: +// Win32 function designed for Windows NT and Windows 95 +// See RegOpenKeyEx API for more info on lpszKey +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* +BOOL LoadMruInReg(LPMRUMENU lpMruMenu,LPSTR lpszKey) +{ +LPSTR lpTxt; +WORD i; +HKEY hCurKey; +DWORD dwType; + lpTxt = (LPSTR)GlobalAllocPtr(GHND,lpMruMenu->wMaxSizeLruItem + 20); + if (lpTxt == NULL) + return FALSE; + + RegOpenKeyEx(HKEY_CURRENT_USER,lpszKey,0,KEY_READ,&hCurKey); + + + for (i=0;iwNbLruMenu;i++) + { + char szEntry[16]; + DWORD dwSizeBuf; + wsprintf(szEntry,"File%lu",(DWORD)i+1); + *lpTxt = '\0'; + dwSizeBuf = lpMruMenu->wMaxSizeLruItem + 10; + RegQueryValueEx(hCurKey,szEntry,NULL,&dwType,(LPBYTE)lpTxt,&dwSizeBuf); + *(lpTxt+dwSizeBuf)='\0'; + if (*lpTxt == '\0') + break; + SetMenuItem(lpMruMenu,i,lpTxt); + } + RegCloseKey(hCurKey); + GlobalFreePtr(lpTxt); + return TRUE; +} + + +//************************************************************* +// +// GetWin32Kind() +// +// Purpose: +// Get the Win32 platform +// +// Parameters: +// +// Return: (WIN32KIND) +// WINNT - Run under Windows NT +// WIN32S - Run under Windows 3.1x + Win32s +// WIN95ORGREATHER - Run under Windows 95 +// +// +// Comments: +// Win32 function designed for Windows NT and Windows 95 +// See RegOpenKeyEx API for more info on lpszKey +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* +WIN32KIND GetWin32Kind() +{ +BOOL IsWin395OrHigher(void); + + WORD wVer; + + if ((GetVersion() & 0x80000000) == 0) + return WINNT; + wVer = LOWORD(GetVersion()); + wVer = (((WORD)LOBYTE(wVer)) << 8) | (WORD)HIBYTE(wVer); + + if (wVer >= 0x035F) + return WIN95ORGREATHER; + else + return WIN32S; +} +#endif diff --git a/utils/Radiant/mru.h b/utils/Radiant/mru.h new file mode 100644 index 0000000..497e028 --- /dev/null +++ b/utils/Radiant/mru.h @@ -0,0 +1,82 @@ +//************************************************************* +// File name: mru.h +// +// Description: +// +// Header for MRU support +// +// Development Team: +// +// Gilles Vollant (100144.2636@compuserve.com) +// +//************************************************************* + +#ifndef __MRU_H__ +#define __MRU_H__ + +// although I've bumped the MRU count and this is a non-afx version, for safety etc I've kept the new limit +// to the same as the AFX one of "_AFX_MRU_MAX_COUNT" -Ste +// +#define NBMRUMENUSHOW 16 //6 // Default number of MRU showed in the menu File +#define NBMRUMENU 16 //9 // Default number of MRU stored +#define IDMRU 8000 // Default First ID of MRU +#ifdef OFS_MAXPATHNAME +#define MAXSIZEMRUITEM OFS_MAXPATHNAME +#else +#define MAXSIZEMRUITEM 128 // Default max size of an entry +#endif + +typedef struct +{ +WORD wNbItemFill; +WORD wNbLruShow; +WORD wNbLruMenu; +WORD wMaxSizeLruItem; +WORD wIdMru; +LPSTR lpMRU; +} MRUMENU; + +typedef MRUMENU FAR * LPMRUMENU; + +#ifdef __cplusplus +LPMRUMENU CreateMruMenu (WORD wNbLruShowInit=NBMRUMENUSHOW, + WORD wNbLruMenuInit=NBMRUMENU, + WORD wMaxSizeLruItemInit=MAXSIZEMRUITEM, + WORD wIdMruInit=IDMRU); +#else +LPMRUMENU CreateMruMenu (WORD wNbLruShowInit, + WORD wNbLruMenuInit, + WORD wMaxSizeLruItemInit, + WORD wIdMruInit); +#endif + +LPMRUMENU CreateMruMenuDefault(); +void DeleteMruMenu (LPMRUMENU lpMruMenu); + +void SetNbLruShow (LPMRUMENU lpMruMenu,WORD wNbLruShowInit); +BOOL SetMenuItem (LPMRUMENU lpMruMenu,WORD wItem, + LPSTR lpItem); +BOOL GetMenuItem (LPMRUMENU lpMruMenu,WORD wItem, + BOOL fIDMBased,LPSTR lpItem,UINT uiSize); +BOOL DelMenuItem (LPMRUMENU lpMruMenu,WORD wItem,BOOL fIDMBased); +void AddNewItem (LPMRUMENU lpMruMenu,LPSTR lpItem); +void PlaceMenuMRUItem(LPMRUMENU lpMruMenu,HMENU hMenu,UINT uiItem); + +BOOL SaveMruInIni (LPMRUMENU lpMruMenu,LPSTR lpszSection,LPSTR lpszFile); +BOOL LoadMruInIni (LPMRUMENU lpMruMenu,LPSTR lpszSection,LPSTR lpszFile); +#ifdef WIN32 +BOOL SaveMruInReg (LPMRUMENU lpMruMenu,LPSTR lpszKey); +BOOL LoadMruInReg (LPMRUMENU lpMruMenu,LPSTR lpszKey); + +typedef enum +{ +WIN32S, +WINNT, +WIN95ORGREATHER +} WIN32KIND; +WIN32KIND GetWin32Kind(); +#endif + + +////////////////////////////////////////////////////////////// +#endif diff --git a/utils/Radiant/newprojdlg.cpp b/utils/Radiant/newprojdlg.cpp new file mode 100644 index 0000000..268424d --- /dev/null +++ b/utils/Radiant/newprojdlg.cpp @@ -0,0 +1,43 @@ +// NewProjDlg.cpp : implementation file +// + +#include "stdafx.h" +#include "Radiant.h" +#include "NewProjDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CNewProjDlg dialog + + +CNewProjDlg::CNewProjDlg(CWnd* pParent /*=NULL*/) + : CDialog(CNewProjDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CNewProjDlg) + m_strName = _T(""); + //}}AFX_DATA_INIT +} + + +void CNewProjDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CNewProjDlg) + DDX_Text(pDX, IDC_EDIT_NAME, m_strName); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CNewProjDlg, CDialog) + //{{AFX_MSG_MAP(CNewProjDlg) + // NOTE: the ClassWizard will add message map macros here + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CNewProjDlg message handlers diff --git a/utils/Radiant/newprojdlg.h b/utils/Radiant/newprojdlg.h new file mode 100644 index 0000000..9b795da --- /dev/null +++ b/utils/Radiant/newprojdlg.h @@ -0,0 +1,46 @@ +#if !defined(AFX_NEWPROJDLG_H__1E2527A2_8447_11D1_B548_00AA00A410FC__INCLUDED_) +#define AFX_NEWPROJDLG_H__1E2527A2_8447_11D1_B548_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// NewProjDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CNewProjDlg dialog + +class CNewProjDlg : public CDialog +{ +// Construction +public: + CNewProjDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CNewProjDlg) + enum { IDD = IDD_DLG_NEWPROJECT }; + CString m_strName; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CNewProjDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CNewProjDlg) + // NOTE: the ClassWizard will add member functions here + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_NEWPROJDLG_H__1E2527A2_8447_11D1_B548_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/oddbits.cpp b/utils/Radiant/oddbits.cpp new file mode 100644 index 0000000..feb5bf8 --- /dev/null +++ b/utils/Radiant/oddbits.cpp @@ -0,0 +1,406 @@ +// Filename:- oddbits.cpp +// + +#include "stdafx.h" +#include "oddbits.h" +#include + +char *va(char *format, ...) +{ + va_list argptr; + static char string[8][16384]; + static int i=0; + + i=(++i)&7; + + va_start (argptr, format); + vsprintf (string[i], format,argptr); + va_end (argptr); + + return string[i]; +} + +void ErrorBox(const char *sString) +{ if ((rand()&31)==30){static bool bPlayed=false;if(!bPlayed){bPlayed=true;PlaySound("k:\\util\\overlay.bin",NULL,SND_FILENAME|SND_ASYNC);}} + MessageBox( NULL, sString, "Error", MB_OK|MB_ICONERROR|MB_TASKMODAL ); +} +void InfoBox(const char *sString) +{ + MessageBox( NULL, sString, "Info", MB_OK|MB_ICONINFORMATION|MB_TASKMODAL ); +} +void WarningBox(const char *sString) +{ + MessageBox( NULL, sString, "Warning", MB_OK|MB_ICONWARNING|MB_TASKMODAL ); +} + +bool FileExists (LPCSTR psFilename) +{ + FILE *handle = fopen(psFilename, "r"); + if (!handle) + { + return false; + } + fclose (handle); + return true; +} + + + +// returns a path to somewhere writeable, without trailing backslash... +// +// (for extra speed now, only evaluates it on the first call, change this if you like) +// +char *scGetTempPath(void) +{ + static char sBuffer[MAX_PATH]; + DWORD dwReturnedSize; + static int i=0; + + if (!i++) + { + dwReturnedSize = GetTempPath(sizeof(sBuffer),sBuffer); + + if (dwReturnedSize>sizeof(sBuffer)) + { + // temp path too long to return, so forget it... + // + strcpy(sBuffer,"c:"); // "c:\\"); // should be writeable + } + + // strip any trailing backslash... + // + if (sBuffer[strlen(sBuffer)-1]=='\\') + sBuffer[strlen(sBuffer)-1]='\0'; + }// if (!i++) + + return sBuffer; + +}// char *scGetTempPath(void) + + +// "psInitialLoadName" param can be "" if not bothered +char *InputLoadFileName(char *psInitialLoadName, char *psCaption, const char *psInitialDir, char *psFilter) +{ + static char sName[MAX_PATH]; + + CFileDialog FileDlg(TRUE, NULL, NULL, OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST, psFilter, AfxGetMainWnd()); + + FileDlg.m_ofn.lpstrInitialDir=psInitialDir; + FileDlg.m_ofn.lpstrTitle=psCaption; + strcpy(sName,psInitialLoadName); + FileDlg.m_ofn.lpstrFile=sName; + + if (FileDlg.DoModal() == IDOK) + return sName; + + return NULL; + +}// char *InputLoadFileName(char *psInitialLoadName, char *psCaption, char *psInitialDir, char *psFilter) + + +long filesize(FILE *handle) +{ + long curpos, length; + + curpos = ftell(handle); + fseek(handle, 0L, SEEK_END); + length = ftell(handle); + fseek(handle, curpos, SEEK_SET); + + return length; +} + +/* +// returns -1 for error +int LoadFile (char *psPathedFilename, void **bufferptr) +{ + FILE *f; + int length; + void *buffer; + + f = fopen(psPathedFilename,"rb"); + if (f) + { + length = filesize(f); + buffer = malloc (length+1); + ((char *)buffer)[length] = 0; + int lread = fread (buffer,1,length, f); + fclose (f); + + if (lread==length) + { + *bufferptr = buffer; + return length; + } + free(buffer); + } + + ErrorBox(va("Error reading file %s!",psPathedFilename)); + return -1; +} +*/ + + + + + +#ifndef SOF // if I don't have this crap in then it clashes with another version in materials.cpp. Groan. + +#define BASEDIRNAME "base" + + +/* + +qdir will hold the path up to the quake directory, including the slash + + f:\quake\ + /raid/quake/ + +gamedir will hold qdir + the game directory (id1, id2, etc) + + */ +//deleteme +char qdir[1024]; +char gamedir[1024]; // q:\quake\baseef + +void SetQdirFromPath( const char *path ) +{ + static bool bDone = false; + + if (!bDone) + { + bDone = true; + +// char temp[1024]; + const char *c; + const char *sep; + int len, count; + +// if (!(path[0] == '/' || path[0] == '\\' || path[1] == ':')) +// { // path is partial +// Q_getwd (temp); +// strcat (temp, path); +// path = temp; +// } + + _strlwr((char*)path); + + // search for "base" in path from the RIGHT hand side (and must have a [back]slash just before it) + + len = strlen(BASEDIRNAME); + for (c=path+strlen(path)-1 ; c != path ; c--) + { + int i; + + if (!strnicmp (c, BASEDIRNAME, len) + && + (*(c-1) == '/' || *(c-1) == '\\') // would be more efficient to do this first, but only checking after a strncasecmp ok ensures no invalid pointer-1 access + ) + { + sep = c + len; + count = 1; + while (*sep && *sep != '/' && *sep != '\\') + { + sep++; + count++; + } + strncpy (gamedir, path, c+len+count-path); + for ( i = 0; i < (int)strlen( gamedir ); i++ ) + { + if ( gamedir[i] == '\\' ) + gamedir[i] = '/'; + } + // qprintf ("gamedir: %s\n", gamedir); + + strncpy (qdir, path, c-path); + qdir[c-path] = 0; + for ( i = 0; i < (int)strlen( qdir ); i++ ) + { + if ( qdir[i] == '\\' ) + qdir[i] = '/'; + } + // qprintf ("qdir: %s\n", qdir); + + return; + } + } + // Error ("SetQdirFromPath: no '%s' in %s", BASEDIRNAME, path); + } +} + +// takes (eg) "q:\quake\baseq3\textures\borg\name.tga" +// +// and produces "textures/borg/name.tga" +// +void Filename_RemoveQUAKEBASE(CString &string) +{ + string.Replace("\\","/"); + string.MakeLower(); + +/* + int loc = string.Find("/quake"); + if (loc >=0 ) + { + loc = string.Find("/",loc+1); + if (loc >=0) + { + // now pointing at "baseq3", "demoq3", whatever... + loc = string.Find("/", loc+1); + + if (loc >= 0) + { + // now pointing at local filename... + // + string = string.Mid(loc+1); + } + } + } +*/ + + SetQdirFromPath( string ); + string.Replace( gamedir, "" ); +} +#endif + + +// takes (eg) "textures/borg/name.tga" +// +// and produces "textures/borg" +// +void Filename_RemoveFilename(CString &string) +{ + string.Replace("\\","/"); + + int loc = string.ReverseFind('/'); + if (loc >= 0) + { + string = string.Left(loc); + } +} + + +// takes (eg) "( longpath )/textures/borg/name.xxx" // N.B. I assume there's an extension on the input string +// +// and produces "name" +// +void Filename_BaseOnly(CString &string) +{ + string.Replace("\\","/"); + + int loc = string.GetLength()-4; + if (string[loc] == '.') + { + string = string.Left(loc); // "( longpath )/textures/borg/name" + loc = string.ReverseFind('/'); + if (loc >= 0) + { + string = string.Mid(loc+1); + } + } +} + + +// returns actual filename only, no path +// +char *Filename_WithoutPath(LPCSTR psFilename) +{ + static char sString[MAX_PATH]; +/* + LPCSTR p = strrchr(psFilename,'\\'); + + if (!p++) + { + p = strrchr(psFilename,'/'); + if (!p++) + p=psFilename; + } + + strcpy(sString,p); +*/ + + LPCSTR psCopyPos = psFilename; + + while (*psFilename) + { + if (*psFilename == '/' || *psFilename == '\\') + psCopyPos = psFilename+1; + psFilename++; + } + + strcpy(sString,psCopyPos); + + return sString; + +} + + +// returns (eg) "\dir\name" for "\dir\name.bmp" +// +char *Filename_WithoutExt(LPCSTR psFilename) +{ + static char sString[MAX_PATH]; + + strcpy(sString,psFilename); + + char *p = strrchr(sString,'.'); + char *p2= strrchr(sString,'\\'); + char *p3= strrchr(sString,'/'); + + // special check, make sure the first suffix we found from the end wasn't just a directory suffix (eg on a path'd filename with no extension anyway) + // + if (p && + (p2==0 || (p2 && p>p2)) && + (p3==0 || (p3 && p>p3)) + ) + *p=0; + + return sString; + +}// char *Filename_WithoutExt(char *psFilename) + + +// loses anything after the path (if any), (eg) "\dir\name.bmp" becomes "\dir" +// +char *Filename_PathOnly(LPCSTR psFilename) +{ + static char sString[MAX_PATH]; + + strcpy(sString,psFilename); + +// for (int i=0; ip2)?p1:p2; + if (p) + *p=0; + + return sString; + +}// char *Filename_WithoutExt(char *psFilename) + + +// returns filename's extension only, else returns original string if no '.' in it... +// +char *Filename_ExtOnly(LPCSTR psFilename) +{ + static char sString[MAX_PATH]; + LPCSTR p = strrchr(psFilename,'.'); + + if (!p) + p=psFilename; + + strcpy(sString,p); + + return sString; + +} + + + +///////////////////// eof /////////////////// + diff --git a/utils/Radiant/oddbits.h b/utils/Radiant/oddbits.h new file mode 100644 index 0000000..7f1e8e1 --- /dev/null +++ b/utils/Radiant/oddbits.h @@ -0,0 +1,40 @@ +// Filename:- oddbits.h +// + +#ifndef ODDBITS_H +#define ODDBITS_H + +char *va(char *format, ...); +bool FileExists (LPCSTR psFilename); + +void ErrorBox(const char *sString); +void InfoBox(const char *sString); +void WarningBox(const char *sString); +// +// (Afx message boxes appear to be MB_TASKMODAL anyway, so no need to specify) +// +#define GetYesNo(psQuery) (!!(AfxMessageBox(psQuery,MB_YESNO|MB_ICONWARNING)==IDYES)) + + +char *scGetTempPath(void); +char *InputLoadFileName(char *psInitialLoadName, char *psCaption, const char *psInitialDir, char *psFilter); +long filesize(FILE *handle); +//int LoadFile (char *psPathedFilename, void **bufferptr); +void Filename_RemoveQUAKEBASE(CString &string); +void Filename_RemoveFilename(CString &string); +void Filename_BaseOnly(CString &string); + +//#define StartWait() HCURSOR hcurSave = SetCursor(::LoadCursor(NULL, IDC_WAIT)) +//#define EndWait() SetCursor(hcurSave) + +char *Filename_WithoutPath(LPCSTR psFilename); +char *Filename_WithoutExt(LPCSTR psFilename); +char *Filename_PathOnly(LPCSTR psFilename); +char *Filename_ExtOnly(LPCSTR psFilename); + + +#endif // #ifndef ODDBITS_H + +/////////////////// eof //////////////////// + + diff --git a/utils/Radiant/parse.cpp b/utils/Radiant/parse.cpp new file mode 100644 index 0000000..9397bc3 --- /dev/null +++ b/utils/Radiant/parse.cpp @@ -0,0 +1,124 @@ +#include "stdafx.h" +#include "qe3.h" + +char token[MAXTOKEN]; +qboolean unget; +char *script_p; +int scriptline; + +void StartTokenParsing (char *data) +{ + scriptline = 1; + script_p = data; + unget = false; +} + +qboolean GetToken (qboolean crossline) +{ + char *token_p; + + if (unget) // is a token allready waiting? + return true; + +// +// skip space +// +skipspace: + while (*script_p <= 32) + { + if (!*script_p) + { + if (!crossline) + Sys_Printf("Warning: Line %i is incomplete [01]\n",scriptline); + return false; + } + if (*script_p++ == '\n') + { + if (!crossline) + Sys_Printf("Warning: Line %i is incomplete [02]\n",scriptline); + scriptline++; + } + } + + if (script_p[0] == '/' && script_p[1] == '/') // comment field + { + if (!crossline) + Sys_Printf("Warning: Line %i is incomplete [03]\n",scriptline); + while (*script_p++ != '\n') + if (!*script_p) + { + if (!crossline) + Sys_Printf("Warning: Line %i is incomplete [04]\n",scriptline); + return false; + } + goto skipspace; + } + +// +// copy token +// + token_p = token; + + if (*script_p == '"') + { + script_p++; + //if (*script_p == '"') // handle double quotes i suspect they are put in by other editors cccasionally + // script_p++; + while ( *script_p != '"' ) + { + if (!*script_p) + Error ("EOF inside quoted token"); + *token_p++ = *script_p++; + if (token_p == &token[MAXTOKEN]) + Error ("Token too large on line %i",scriptline); + } + script_p++; + //if (*script_p == '"') // handle double quotes i suspect they are put in by other editors cccasionally + // script_p++; + } + else while ( *script_p > 32 ) + { + *token_p++ = *script_p++; + if (token_p == &token[MAXTOKEN]) + Error ("Token too large on line %i",scriptline); + } + + *token_p = 0; + + return true; +} + +void UngetToken (void) +{ + unget = true; +} + + +/* +============== +TokenAvailable + +Returns true if there is another token on the line +============== +*/ +qboolean TokenAvailable (void) +{ + char *search_p; + + search_p = script_p; + + while ( *search_p <= 32) + { + if (*search_p == '\n') + return false; + if (*search_p == 0) + return false; + search_p++; + } + + if (*search_p == ';') + return false; + + return true; +} + diff --git a/utils/Radiant/parse.h b/utils/Radiant/parse.h new file mode 100644 index 0000000..7218604 --- /dev/null +++ b/utils/Radiant/parse.h @@ -0,0 +1,12 @@ +// parse.h -- text file parsing routines + +#define MAXTOKEN 1024 + +extern char token[MAXTOKEN]; +extern int scriptline; + +void StartTokenParsing (char *data); +qboolean GetToken (qboolean crossline); +void UngetToken (void); +qboolean TokenAvailable (void); + diff --git a/utils/Radiant/patchdensitydlg.cpp b/utils/Radiant/patchdensitydlg.cpp new file mode 100644 index 0000000..dba66f2 --- /dev/null +++ b/utils/Radiant/patchdensitydlg.cpp @@ -0,0 +1,68 @@ +// PatchDensityDlg.cpp : implementation file +// + +#include "stdafx.h" +#include "Radiant.h" +#include "PatchDensityDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CPatchDensityDlg dialog + + +CPatchDensityDlg::CPatchDensityDlg(CWnd* pParent /*=NULL*/) + : CDialog(CPatchDensityDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CPatchDensityDlg) + //}}AFX_DATA_INIT +} + + +void CPatchDensityDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CPatchDensityDlg) + DDX_Control(pDX, IDC_COMBO_WIDTH, m_wndWidth); + DDX_Control(pDX, IDC_COMBO_HEIGHT, m_wndHeight); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CPatchDensityDlg, CDialog) + //{{AFX_MSG_MAP(CPatchDensityDlg) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CPatchDensityDlg message handlers + +int g_nXLat[] = {3,5,7,9,11,13,15}; + +void CPatchDensityDlg::OnOK() +{ + int nWidth = m_wndWidth.GetCurSel(); + int nHeight = m_wndHeight.GetCurSel(); + + if (nWidth >= 0 && nWidth <= 6 && nHeight >= 0 && nHeight <= 6) + { + Patch_GenericMesh(g_nXLat[nWidth], g_nXLat[nHeight], g_pParentWnd->ActiveXY()->GetViewType()); + Sys_UpdateWindows(W_ALL); + } + + CDialog::OnOK(); +} + +BOOL CPatchDensityDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + m_wndWidth.SetCurSel(0); + m_wndHeight.SetCurSel(0); + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/utils/Radiant/patchdensitydlg.h b/utils/Radiant/patchdensitydlg.h new file mode 100644 index 0000000..d7870c7 --- /dev/null +++ b/utils/Radiant/patchdensitydlg.h @@ -0,0 +1,48 @@ +#if !defined(AFX_PATCHDENSITYDLG_H__509162A1_1023_11D2_AFFB_00AA00A410FC__INCLUDED_) +#define AFX_PATCHDENSITYDLG_H__509162A1_1023_11D2_AFFB_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// PatchDensityDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CPatchDensityDlg dialog + +class CPatchDensityDlg : public CDialog +{ +// Construction +public: + CPatchDensityDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CPatchDensityDlg) + enum { IDD = IDD_DIALOG_NEWPATCH }; + CComboBox m_wndWidth; + CComboBox m_wndHeight; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CPatchDensityDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CPatchDensityDlg) + virtual void OnOK(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_PATCHDENSITYDLG_H__509162A1_1023_11D2_AFFB_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/plugin.cpp b/utils/Radiant/plugin.cpp new file mode 100644 index 0000000..6edfeeb --- /dev/null +++ b/utils/Radiant/plugin.cpp @@ -0,0 +1,177 @@ +// PlugIn.cpp: implementation of the CPlugIn class. +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "Radiant.h" +#include "PlugIn.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CPlugIn::CPlugIn() +{ + m_hDLL = NULL; +} + +CPlugIn::~CPlugIn() +{ + if (m_hDLL != NULL) + free(); +} + + + +bool CPlugIn::load(const char *p) +{ + m_hDLL = ::LoadLibrary(p); + if (m_hDLL != NULL) + { + m_pfnInit = reinterpret_cast(::GetProcAddress(m_hDLL, QERPLUG_INIT)); + if (m_pfnInit != NULL) + { + m_strVersion = (*m_pfnInit)(AfxGetApp()->m_hInstance, g_pParentWnd->GetSafeHwnd()); + Sys_Printf("Loaded plugin > %s\n", m_strVersion); + + m_pfnGetName = reinterpret_cast(::GetProcAddress(m_hDLL, QERPLUG_GETNAME)); + if (m_pfnGetName != NULL) + { + m_strName = (*m_pfnGetName)(); + } + + m_pfnGetCommandList = reinterpret_cast(::GetProcAddress(m_hDLL, QERPLUG_GETCOMMANDLIST)); + if (m_pfnGetCommandList) + { + CString str = (*m_pfnGetCommandList)(); + char cTemp[1024]; + strcpy(cTemp, str); + char* token = strtok(cTemp, ",;"); + if (token && *token == ' ') + { + while (*token == ' ') + token++; + } + while (token != NULL) + { + m_CommandStrings.Add(token); + token = strtok(NULL, ",;"); + } + } + + m_pfnDispatch = reinterpret_cast(::GetProcAddress(m_hDLL, QERPLUG_DISPATCH)); + m_pfnGetFuncTable = reinterpret_cast(::GetProcAddress(m_hDLL, QERPLUG_GETFUNCTABLE)); + + m_pfnGetTextureInfo = reinterpret_cast(::GetProcAddress(m_hDLL, QERPLUG_GETTEXTUREINFO)); + m_pfnLoadTexture = reinterpret_cast(::GetProcAddress(m_hDLL, QERPLUG_LOADTEXTURE)); + + m_pfnGetSurfaceFlags = reinterpret_cast(::GetProcAddress(m_hDLL, QERPLUG_GETSURFACEFLAGS)); + + return (m_pfnDispatch != NULL && m_pfnGetFuncTable != NULL); + //--return true; + } + Sys_Printf("FAILED to Load plugin > %s\n", p); + } + free(); + return false; +} + +_QERTextureInfo* CPlugIn::getTextureInfo() +{ + if (m_pfnGetTextureInfo != NULL) + { + return reinterpret_cast<_QERTextureInfo*>((*m_pfnGetTextureInfo)()); + } + return NULL; +} + +void CPlugIn::loadTexture(LPCSTR pFilename) +{ + if (m_pfnLoadTexture != NULL) + { + (*m_pfnLoadTexture)(pFilename); + } +} + +LPVOID CPlugIn::getSurfaceFlags() +{ + if (m_pfnGetSurfaceFlags != NULL) + { + return reinterpret_cast((*m_pfnGetSurfaceFlags)()); + } + return NULL; +} + + + +void CPlugIn::free() +{ + if (m_hDLL != NULL) + ::FreeLibrary(m_hDLL); + m_hDLL = NULL; +} + +const char* CPlugIn::getVersionStr() +{ + return m_pfnGetName(); +} + + +const char* CPlugIn::getMenuName() +{ + return m_strName; +} + + +int CPlugIn::getCommandCount() +{ + return m_CommandStrings.GetSize(); +} + +const char* CPlugIn::getCommand(int n) +{ + return m_CommandStrings.GetAt(n); +} + + +void CPlugIn::dispatchCommand(const char* p, vec3_t vMin, vec3_t vMax, BOOL bSingleBrush) +{ + if (m_pfnDispatch) + { + (*m_pfnDispatch)(p, vMin, vMax, bSingleBrush); + } +} + +void CPlugIn::addMenuID(int n) +{ + m_CommandIDs.Add(n); +} + + + +bool CPlugIn::ownsCommandID(int n) +{ + for (int i = 0; i < m_CommandIDs.GetSize(); i++) + { + if (m_CommandIDs.GetAt(i) == n) + return true; + } + return false; +} + +void* CPlugIn::getFuncTable() +{ + if (m_pfnGetFuncTable) + { + return (*m_pfnGetFuncTable)(); + } + return NULL; +} + + diff --git a/utils/Radiant/plugin.h b/utils/Radiant/plugin.h new file mode 100644 index 0000000..67eed04 --- /dev/null +++ b/utils/Radiant/plugin.h @@ -0,0 +1,52 @@ +// PlugIn.h: interface for the CPlugIn class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_PLUGIN_H__B501A832_5755_11D2_B084_00AA00A410FC__INCLUDED_) +#define AFX_PLUGIN_H__B501A832_5755_11D2_B084_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +#include "qerplugin.h" + +class CPlugIn : public CObject +{ +private: + HMODULE m_hDLL; + PFN_QERPLUG_INIT m_pfnInit; + PFN_QERPLUG_GETNAME m_pfnGetName; + PFN_QERPLUG_GETCOMMANDLIST m_pfnGetCommandList; + PFN_QERPLUG_DISPATCH m_pfnDispatch; + PFN_QERPLUG_GETFUNCTABLE m_pfnGetFuncTable; + PFN_QERPLUG_GETTEXTUREINFO m_pfnGetTextureInfo; + PFN_QERPLUG_LOADTEXTURE m_pfnLoadTexture; + PFN_QERPLUG_GETSURFACEFLAGS m_pfnGetSurfaceFlags; + CWordArray m_CommandIDs; + CStringArray m_CommandStrings; + CString m_strName; + CString m_strVersion; + +public: + void* getFuncTable(); + bool ownsCommandID(int n); + void addMenuID(int n); + CPlugIn(); + virtual ~CPlugIn(); + bool load(const char *p); + void free(); + const char* getVersionStr(); + const char* getMenuName(); + int getCommandCount(); + const char* getCommand(int n); + void dispatchCommand(const char* p, vec3_t vMin, vec3_t vMax, BOOL bSingleBrush); + + _QERTextureInfo *getTextureInfo(); + void loadTexture(LPCSTR pFilename); + + LPVOID getSurfaceFlags(); + +}; + +#endif // !defined(AFX_PLUGIN_H__B501A832_5755_11D2_B084_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/pluginmanager.cpp b/utils/Radiant/pluginmanager.cpp new file mode 100644 index 0000000..e1a3a99 --- /dev/null +++ b/utils/Radiant/pluginmanager.cpp @@ -0,0 +1,808 @@ +// PlugInManager.cpp: implementation of the CPlugInManager class. +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "io.h" +#include "Radiant.h" +#include "PlugInManager.h" +#include "PlugIn.h" +#include "DialogInfo.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CPlugInManager::CPlugInManager() +{ + m_pTexturePlug = NULL; + m_pSurfaceListPlug = NULL; +} + +CPlugInManager::~CPlugInManager() +{ + Cleanup(); +} + +void CPlugInManager::Init(const char * pPath) +{ + Cleanup(); + + CString strPath(pPath); + strPath += "*.dll"; + + bool bGo = true; + + struct _finddata_t fileinfo; + int handle = _findfirst (strPath, &fileinfo); + if (handle != -1) + { + do + { + strPath.Format("%s\\%s", pPath, fileinfo.name); + CPlugIn *pPlug = new CPlugIn(); + if (pPlug->load(strPath)) + { + if(FillFuncTable(pPlug)) // PGM + { + m_PlugIns.Add(pPlug); + g_pParentWnd->AddPlugInMenuItem(pPlug); + + // if this thing handles textures + if (pPlug->getTextureInfo() != NULL) + { + this->m_pTexturePlug = pPlug; + + // if this is a wad style texture extension, have it load everything now + if (pPlug->getTextureInfo()->m_bWadStyle) + { + CString strPath = ValueForKey(g_qeglobals.d_project_entity, "texturepath"); + pPlug->loadTexture(strPath); + } + } + + if (pPlug->getSurfaceFlags() != NULL) + { + this->m_pSurfaceListPlug = pPlug; + } + } + else + { + delete pPlug; // PGM + } + } + else + { + delete pPlug; + } + } while (_findnext( handle, &fileinfo ) != -1); + _findclose (handle); + } + +} + +void CPlugInManager::Cleanup() +{ + int i; + for (i = 0; i < m_PlugIns.GetSize(); i++) + { + CPlugIn *plug = reinterpret_cast(m_PlugIns.GetAt(i)); + plug->free(); + delete plug; + } + m_PlugIns.RemoveAll(); + + for (i = 0; i < m_BrushHandles.GetSize(); i++) + { + brush_t *pb = reinterpret_cast(m_BrushHandles.GetAt(i)); + Brush_Free(pb); + } + m_BrushHandles.RemoveAll(); +} + +void CPlugInManager::Dispatch(int n, const char * p) +{ + for (int i = 0; i < m_PlugIns.GetSize(); i++) + { + CPlugIn *plug = reinterpret_cast(m_PlugIns.GetAt(i)); + if (plug->ownsCommandID(n)) + { + vec3_t vMin, vMax; + if (selected_brushes.next == &selected_brushes) + { + vMin[0] = vMin[1] = vMin[2] = 0; + VectorCopy(vMin, vMax); + } + else + { + Select_GetBounds (vMin, vMax); + } + plug->dispatchCommand(p, vMin, vMax, QE_SingleBrush(true)); // PGM -- added quiet + break; + } + } +} + + + + +void WINAPI QERApp_CreateBrush(vec3_t vMin, vec3_t vMax) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + brush_t* pBrush = Brush_Create(vMin, vMax, &g_qeglobals.d_texturewin.texdef); + Entity_LinkBrush (world_entity, pBrush); + Brush_Build(pBrush); + Brush_AddToList (pBrush, &active_brushes); + Select_Brush(pBrush); + Sys_UpdateWindows(W_ALL); +} + + +LPVOID CPlugInManager::CreateBrushHandle() +{ + brush_t *pb = reinterpret_cast(qmalloc (sizeof(brush_t))); + m_BrushHandles.Add(pb); + return (LPVOID)pb; +} + + +void CPlugInManager::DeleteBrushHandle(void * vp) +{ + CPtrArray* pHandles[3]; + pHandles[0] = &m_SelectedBrushHandles; + pHandles[1] = &m_ActiveBrushHandles; + pHandles[2] = &m_BrushHandles; + + for (int j = 0; j < 3; j++) + { + for (int i = 0; i < pHandles[j]->GetSize(); i++) + { + brush_t *pb = reinterpret_cast(pHandles[j]->GetAt(i)); + if (pb == reinterpret_cast(vp)) + { + if (j == 2) + { + // only remove it from the list if it is work area + // this allows the selected and active list indexes to remain constant + // throughout a session (i.e. between an allocate and release) + pHandles[j]->RemoveAt(i); + } + Brush_Free(pb); + Sys_MarkMapModified(); // PGM + return; + } + } + } +} + +void CPlugInManager::CommitBrushHandleToMap(void * vp) +{ + g_bScreenUpdates = false; //stop the madness + + for (int i = 0; i < m_BrushHandles.GetSize(); i++) + { + brush_t *pb = reinterpret_cast(m_BrushHandles.GetAt(i)); + if (pb == reinterpret_cast(vp)) + { + m_BrushHandles.RemoveAt(i); + Entity_LinkBrush (world_entity, pb); + Brush_Build(pb); + Brush_AddToList (pb, &active_brushes); + Select_Brush(pb); + } + } + g_bScreenUpdates = true; //resume the madness + Sys_UpdateWindows(W_ALL); +} + + +void CPlugInManager::AddFaceToBrushHandle(void * vp, vec3_t v1, vec3_t v2, vec3_t v3) +{ + brush_t *bp = FindBrushHandle(vp); + if (bp != NULL) + { + face_t *f = Face_Alloc(); + f->texdef = g_qeglobals.d_texturewin.texdef; + f->texdef.flags &= ~SURF_KEEP; + f->texdef.contents &= ~CONTENTS_KEEP; + f->next = bp->brush_faces; + bp->brush_faces = f; + VectorCopy (v1, f->planepts[0]); + VectorCopy (v2, f->planepts[1]); + VectorCopy (v3, f->planepts[2]); + } +} + +brush_t* CPlugInManager::FindBrushHandle(void * vp) +{ + CPtrArray* pHandles[3]; + pHandles[0] = &m_SelectedBrushHandles; + pHandles[1] = &m_ActiveBrushHandles; + pHandles[2] = &m_BrushHandles; + + for (int j = 0; j < 3; j++) + { + for (int i = 0; i < pHandles[j]->GetSize(); i++) + { + brush_t *pb = reinterpret_cast(pHandles[j]->GetAt(i)); + if (pb == reinterpret_cast(vp)) + { + return pb; + } + } + } + return NULL; +} + +LPVOID WINAPI QERApp_CreateBrushHandle() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return g_pParentWnd->GetPlugInMgr().CreateBrushHandle(); +} + +void WINAPI QERApp_DeleteBrushHandle(LPVOID vp) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + g_pParentWnd->GetPlugInMgr().DeleteBrushHandle(vp); +} + +void WINAPI QERApp_CommitBrushHandleToMap(LPVOID vp) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + g_pParentWnd->GetPlugInMgr().CommitBrushHandleToMap(vp); +} + +void WINAPI QERApp_AddFace(LPVOID vp, vec3_t v1, vec3_t v2, vec3_t v3) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + g_pParentWnd->GetPlugInMgr().AddFaceToBrushHandle(vp, v1, v2, v3); +} + +void WINAPI QERApp_DeleteSelection() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + Select_Delete(); +} + +void WINAPI QERApp_SysMsg(LPCSTR pMsg) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + CString str = pMsg; + Sys_Printf(str.GetBuffer(0)); +} + +void WINAPI QERApp_InfoMsg(LPCSTR pMsg) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + ShowInfoDialog(pMsg); +} + +void WINAPI QERApp_HideInfoMsg() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + HideInfoDialog(); +} + +//===== +//PGM +void WINAPI QERApp_PositionView(vec3_t v1, vec3_t v2) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + g_pParentWnd->ActiveXY()->SetOrigin(v1); + // FIXME - implement this! + Sys_UpdateWindows(W_ALL); +} +//PGM +//===== + +//FIXME: this AcquirePath stuff is pretty much a mess and needs cleaned up +bool g_bPlugWait = false; +bool g_bPlugOK = false; +int g_nPlugCount = 0; + +void _PlugDone(bool b, int n) +{ + g_bPlugWait = false; + g_bPlugOK = b; + g_nPlugCount = n; //++timo added number of entered points +} + +void WINAPI QERApp_GetPoints(int nMax, _QERPointData *pData, LPCSTR pMsg) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + ShowInfoDialog(pMsg); + g_bPlugWait = true; + g_bPlugOK = false; + g_nPlugCount = 0; //++timo replaced by number of points +// g_nPlugCount=nMax-1; + AcquirePath(nMax, &_PlugDone); + while (g_bPlugWait) + { + MSG msg; + if (::PeekMessage( &msg, NULL, 0, 0, PM_REMOVE )) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + HideInfoDialog(); + + pData->m_nCount = 0; + pData->m_pVectors = NULL; + + if (g_bPlugOK && g_nPlugCount > 0) + { + pData->m_nCount = g_nPlugCount; + pData->m_pVectors = reinterpret_cast(qmalloc(g_nPlugCount * sizeof(vec3_t))); + vec3_t *pOut = pData->m_pVectors; + for (int i = 0; i < g_nPlugCount; i++) + { + //++timo + /* + memcpy(pOut, &g_PathPoints[i], sizeof(vec3_t)); + pOut += sizeof(vec3_t); + */ + // CClipPoint g_PathPoints[256]; + memcpy(pOut, &g_PathPoints[i],sizeof(vec3_t)); + pOut++; + } + } +} + +void WINAPI QERApp_AddFaceData(LPVOID pv, _QERFaceData *pData) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + brush_t* pBrush = g_pParentWnd->GetPlugInMgr().FindBrushHandle(pv); + if (pBrush != NULL) + { + face_t *f = Face_Alloc(); + f->texdef = g_qeglobals.d_texturewin.texdef; + f->texdef.flags = pData->m_nFlags; + f->texdef.contents = pData->m_nContents; + f->texdef.value = pData->m_nValue; + f->texdef.rotate = pData->m_fRotate; + f->texdef.shift[0] = pData->m_fShift[0]; + f->texdef.shift[1] = pData->m_fShift[1]; + f->texdef.scale[0] = pData->m_fScale[0]; + f->texdef.scale[1] = pData->m_fScale[1]; + strcpy(f->texdef.name, pData->m_TextureName); + f->next = pBrush->brush_faces; + pBrush->brush_faces = f; + VectorCopy (pData->m_v1, f->planepts[0]); + VectorCopy (pData->m_v2, f->planepts[1]); + VectorCopy (pData->m_v3, f->planepts[2]); + Sys_MarkMapModified(); // PGM + } +} + +int WINAPI QERApp_GetFaceCount(LPVOID pv) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + int n = 0; + brush_t *pBrush = g_pParentWnd->GetPlugInMgr().FindBrushHandle(pv); + if (pBrush != NULL) + { + for (face_t *f = pBrush->brush_faces ; f; f = f->next) + { + n++; + } + } + return n; +} + +_QERFaceData* WINAPI QERApp_GetFaceData(LPVOID pv, int nFaceIndex) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + static _QERFaceData face; + int n = 0; + brush_t *pBrush = g_pParentWnd->GetPlugInMgr().FindBrushHandle(pv); + if (pBrush != NULL) + { + for (face_t *f = pBrush->brush_faces ; f; f = f->next) + { + if (n == nFaceIndex) + { + face.m_nContents = f->texdef.contents; + face.m_nFlags = f->texdef.flags; + face.m_fRotate = f->texdef.rotate; + face.m_fScale[0] = f->texdef.scale[0]; + face.m_fScale[1] = f->texdef.scale[1]; + face.m_fShift[0] = f->texdef.shift[0]; + face.m_fShift[1] = f->texdef.shift[1]; + face.m_nValue = f->texdef.value; + strcpy(face.m_TextureName, f->texdef.name); + VectorCopy(f->planepts[0], face.m_v1); + VectorCopy(f->planepts[1], face.m_v2); + VectorCopy(f->planepts[2], face.m_v3); + return &face; + } + n++; + } + } + return NULL; +} + +void WINAPI QERApp_SetFaceData(LPVOID pv, int nFaceIndex, _QERFaceData *pData) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + int n = 0; + brush_t *pBrush = g_pParentWnd->GetPlugInMgr().FindBrushHandle(pv); + if (pBrush != NULL) + { + for (face_t *f = pBrush->brush_faces ; f; f = f->next) + { + if (n == nFaceIndex) + { + f->texdef.flags = pData->m_nFlags; + f->texdef.contents = pData->m_nContents; + f->texdef.value = pData->m_nValue; + f->texdef.rotate = pData->m_fRotate; + f->texdef.shift[0] = pData->m_fShift[0]; + f->texdef.shift[1] = pData->m_fShift[1]; + f->texdef.scale[0] = pData->m_fScale[0]; + f->texdef.scale[1] = pData->m_fScale[1]; + strcpy(f->texdef.name, pData->m_TextureName); + VectorCopy(pData->m_v1, f->planepts[0]); + VectorCopy(pData->m_v2, f->planepts[1]); + VectorCopy(pData->m_v3, f->planepts[2]); + Sys_MarkMapModified(); // PGM + return; // PGM + } + n++; + } + } +} + +void WINAPI QERApp_DeleteFace(LPVOID pv, int nFaceIndex) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + int n = 0; + brush_t *pBrush = g_pParentWnd->GetPlugInMgr().FindBrushHandle(pv); + if (pBrush != NULL) + { + face_t *pPrev = pBrush->brush_faces; + for (face_t *f = pBrush->brush_faces; f; f = f->next) + { + if (n == nFaceIndex) + { + pPrev->next = f->next; + Face_Free (f); + Sys_MarkMapModified(); // PGM + return; + } + n++; + pPrev = f; + } + } +} + +//========== +//PGM +void WINAPI QERApp_BuildBrush (LPVOID pv) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + brush_t *pBrush = g_pParentWnd->GetPlugInMgr().FindBrushHandle(pv); + if (pBrush != NULL) + { + Brush_Build(pBrush); + Sys_UpdateWindows(W_ALL); + } +} + +void WINAPI QERApp_SelectBrush (LPVOID pv) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + brush_t *pBrush = g_pParentWnd->GetPlugInMgr().FindBrushHandle(pv); + if (pBrush != NULL) + { + Select_Brush(pBrush, false); + Sys_UpdateWindows(W_ALL); + } + +} + +void WINAPI QERApp_DeselectBrush (LPVOID pv) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + // FIXME - implement this! +} + +void WINAPI QERApp_ResetPlugins() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + g_pParentWnd->OnPluginsRefresh(); +} + +void WINAPI QERApp_DeselectAllBrushes () +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + Select_Deselect(); + Sys_UpdateWindows(W_ALL); +} +//PGM +//========== + +void WINAPI QERApp_TextureBrush(LPVOID pv, LPCSTR pName) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + brush_t *pBrush = g_pParentWnd->GetPlugInMgr().FindBrushHandle(pv); + if (pBrush != NULL) + { + for (face_t *f = pBrush->brush_faces ; f; f = f->next) + { + strcpy(f->texdef.name, pName); + } + Sys_MarkMapModified(); // PGM + } +} + +int WINAPI QERApp_SelectedBrushCount() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + int n = 0; + for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next) + { + n++; + } + return n; +} + +int WINAPI QERApp_ActiveBrushCount() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + int n = 0; + for (brush_t *pb = active_brushes.next ; pb != &active_brushes ; pb = pb->next) + { + n++; + } + return n; +} + +int WINAPI QERApp_AllocateSelectedBrushHandles() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + int n = 0; + for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next) + { + n++; + g_pParentWnd->GetPlugInMgr().GetSelectedHandles().Add(pb); + } + return n; +} + +int WINAPI QERApp_AllocateActiveBrushHandles() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + int n = 0; + for (brush_t *pb = active_brushes.next ; pb != &active_brushes ; pb = pb->next) + { + n++; + g_pParentWnd->GetPlugInMgr().GetActiveHandles().Add(pb); + } + return n; +} + +void WINAPI QERApp_ReleaseSelectedBrushHandles() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + g_pParentWnd->GetPlugInMgr().GetSelectedHandles().RemoveAll(); + Sys_UpdateWindows(W_ALL); +} + +void WINAPI QERApp_ReleaseActiveBrushHandles() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + g_pParentWnd->GetPlugInMgr().GetActiveHandles().RemoveAll(); + Sys_UpdateWindows(W_ALL); +} + +LPVOID WINAPI QERApp_GetActiveBrushHandle(int nIndex) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + if (nIndex < g_pParentWnd->GetPlugInMgr().GetActiveHandles().GetSize()) + { + return reinterpret_cast(g_pParentWnd->GetPlugInMgr().GetActiveHandles().GetAt(nIndex)); + } + return NULL; +} + +LPVOID WINAPI QERApp_GetSelectedBrushHandle(int nIndex) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + if (nIndex < g_pParentWnd->GetPlugInMgr().GetSelectedHandles().GetSize()) + { + return reinterpret_cast(g_pParentWnd->GetPlugInMgr().GetSelectedHandles().GetAt(nIndex)); + } + return NULL; +} + +int WINAPI QERApp_TextureCount() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + Texture_StartPos (); + int x, y; + int n = 0; + while (1) + { + qtexture_t *q = Texture_NextPos (&x, &y); + if (!q) + break; + n++; + } + return n; +} + +LPCSTR WINAPI QERApp_GetTexture(int nIndex) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + static char name[QER_MAX_NAMELEN]; + Texture_StartPos (); + int x, y; + int n = 0; + while (1) + { + qtexture_t *q = Texture_NextPos (&x, &y); + if (!q) + break; + if (n == nIndex) + { + strcpy(name, q->name); + return name; + } + n++; + } + return NULL; +} + +LPCSTR WINAPI QERApp_GetCurrentTexture() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return g_qeglobals.d_texturewin.texdef.name; +} + +void WINAPI QERApp_SetCurrentTexture(LPCSTR strName) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + texdef_t tex; + strcpy(tex.name, strName); + Texture_SetTexture(&tex); +} + +int WINAPI QERApp_GetEClassCount() +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + int n = 0; + for (eclass_t *e = eclass ; e ; e = e->next) + { + n++; + } + return n; +} + +LPCSTR WINAPI QERApp_GetEClass(int nIndex) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + int n = 0; + for (eclass_t *e = eclass ; e ; e = e->next) + { + if (n == nIndex) + { + return e->name; + } + } + return NULL; +} + +void WINAPI QERApp_LoadTextureRGBA(LPVOID vp) +{ + Texture_LoadFromPlugIn(vp); +} + + +_QERTextureInfo* CPlugInManager::GetTextureInfo() +{ + if (m_pTexturePlug != NULL) + { + return m_pTexturePlug->getTextureInfo(); + } + else + { + return NULL; + } +} + +LPVOID CPlugInManager::GetSurfaceFlags() +{ + if (m_pSurfaceListPlug != NULL) + { + return m_pSurfaceListPlug->getSurfaceFlags(); + } + else + { + return NULL; + } +} + +extern qtexture_t *g_pluginTexture; + +void CPlugInManager::LoadTexture(const char *pFilename) +{ + g_pluginTexture = NULL; + if (m_pTexturePlug != NULL) + { + m_pTexturePlug->loadTexture(pFilename); + } +} + + +int CPlugInManager::FillFuncTable(CPlugIn *pPlug) +{ + _QERFuncTable_1 *pTable = reinterpret_cast<_QERFuncTable_1*>(pPlug->getFuncTable()); + if (pTable != NULL) + { + if (pTable->m_fVersion != QER_PLUG_VERSION) + { + Sys_Printf("Radiant plugin manager was built with version %.2f, Plugin %s is version %.2f\n", QER_PLUG_VERSION, pPlug->getVersionStr(), pTable->m_fVersion); + } + if (pTable->m_fVersion >= QER_PLUG_VERSION_1) + { + pTable->m_pfnCreateBrush = &QERApp_CreateBrush; + pTable->m_pfnCreateBrushHandle = &QERApp_CreateBrushHandle; + pTable->m_pfnDeleteBrushHandle = &QERApp_DeleteBrushHandle; + pTable->m_pfnCommitBrushHandle = &QERApp_CommitBrushHandleToMap; + pTable->m_pfnAddFace = &QERApp_AddFace; + pTable->m_pfnAddFaceData = &QERApp_AddFaceData; + pTable->m_pfnGetFaceData = &QERApp_GetFaceData; + pTable->m_pfnGetFaceCount = &QERApp_GetFaceCount; + pTable->m_pfnSetFaceData = &QERApp_SetFaceData; + pTable->m_pfnDeleteFace = &QERApp_DeleteFace; + pTable->m_pfnTextureBrush = &QERApp_TextureBrush; + pTable->m_pfnBuildBrush = &QERApp_BuildBrush; // PGM + pTable->m_pfnSelectBrush = &QERApp_SelectBrush; // PGM + pTable->m_pfnDeselectBrush = &QERApp_DeselectBrush; // PGM + pTable->m_pfnDeselectAllBrushes = &QERApp_DeselectAllBrushes; // PGM + pTable->m_pfnDeleteSelection = &QERApp_DeleteSelection; + pTable->m_pfnGetPoints = &QERApp_GetPoints; + pTable->m_pfnSysMsg = &QERApp_SysMsg; + pTable->m_pfnInfoMsg = &QERApp_InfoMsg; + pTable->m_pfnHideInfoMsg = &QERApp_HideInfoMsg; + pTable->m_pfnPositionView = &QERApp_PositionView; // PGM + pTable->m_pfnSelectedBrushCount = &QERApp_SelectedBrushCount; + pTable->m_pfnAllocateSelectedBrushHandles = &QERApp_AllocateSelectedBrushHandles; + pTable->m_pfnReleaseSelectedBrushHandles = &QERApp_ReleaseSelectedBrushHandles; + pTable->m_pfnGetSelectedBrushHandle = &QERApp_GetSelectedBrushHandle; + pTable->m_pfnActiveBrushCount = &QERApp_ActiveBrushCount; + pTable->m_pfnAllocateActiveBrushHandles = &QERApp_AllocateActiveBrushHandles; + pTable->m_pfnReleaseActiveBrushHandles = &QERApp_ReleaseActiveBrushHandles; + pTable->m_pfnGetActiveBrushHandle = &QERApp_GetActiveBrushHandle; + pTable->m_pfnTextureCount = &QERApp_TextureCount; + pTable->m_pfnGetTexture = &QERApp_GetTexture; + pTable->m_pfnGetCurrentTexture = &QERApp_GetCurrentTexture; + pTable->m_pfnSetCurrentTexture = &QERApp_SetCurrentTexture; + pTable->m_pfnGetEClassCount = &QERApp_GetEClassCount; + pTable->m_pfnGetEClass = &QERApp_GetEClass; + pTable->m_pfnResetPlugins = &QERApp_ResetPlugins; + } + // end of v1.00 + if (pTable->m_fVersion >= QER_PLUG_VERSION) + { + // v1.50 starts + pTable->m_pfnLoadTextureRGBA = &QERApp_LoadTextureRGBA; + // end of v1.50 + } + + return true; + } + else + { + Sys_Printf("Unable to load %s because the function tables are not the same size\n", pPlug->getVersionStr()); + } + return false; +} diff --git a/utils/Radiant/pluginmanager.h b/utils/Radiant/pluginmanager.h new file mode 100644 index 0000000..c8b3abd --- /dev/null +++ b/utils/Radiant/pluginmanager.h @@ -0,0 +1,52 @@ +// PlugInManager.h: interface for the CPlugInManager class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_PLUGINMANAGER_H__CFB18412_55FE_11D2_B082_00AA00A410FC__INCLUDED_) +#define AFX_PLUGINMANAGER_H__CFB18412_55FE_11D2_B082_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +#include "plugin.h" + +class CPlugInManager +{ +private: + CObArray m_PlugIns; + CPtrArray m_BrushHandles; + CPtrArray m_SelectedBrushHandles; + CPtrArray m_ActiveBrushHandles; + + CPlugIn *m_pTexturePlug; + CPlugIn *m_pSurfaceListPlug; + +public: + CPtrArray& GetActiveHandles() {return m_ActiveBrushHandles; }; + CPtrArray& GetSelectedHandles() {return m_SelectedBrushHandles; }; + brush_t* FindBrushHandle(void *vp); + void AddFaceToBrushHandle(void *vp, vec3_t v1, vec3_t v2, vec3_t v3); + void CommitBrushHandleToMap(void *vp); + void DeleteBrushHandle(LPVOID vp); + LPVOID CreateBrushHandle(); + void Dispatch(int n, const char *p); + void Cleanup(); + void Init(const char* pPath); + CPlugInManager(); + virtual ~CPlugInManager(); + + // the texture manager front ends the single load + // addins (texture, model, map formats.. etc.) + _QERTextureInfo* GetTextureInfo(); + void LoadTexture(const char *pFilename); + + LPVOID GetSurfaceFlags(); + + + +protected: + int FillFuncTable(CPlugIn *pPlug); // PGM +}; + +#endif // !defined(AFX_PLUGINMANAGER_H__CFB18412_55FE_11D2_B082_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/points.cpp b/utils/Radiant/points.cpp new file mode 100644 index 0000000..dadc820 --- /dev/null +++ b/utils/Radiant/points.cpp @@ -0,0 +1,172 @@ +#include "stdafx.h" + +#include "qe3.h" + + +#define MAX_POINTFILE 8192 +static vec3_t s_pointvecs[MAX_POINTFILE]; +static int s_num_points, s_check_point; + +// returns NULL if all ok, else error message... +// +char *Pointfile_Delete(void) +{ + char *psReturn = NULL; + char name[1024]; + + strcpy (name, currentmap); + StripExtension (name); + strcat (name, ".lin"); + + if (remove(name)) + { + static char sTemp[1024]; + sprintf(sTemp,"Error deleting file \"%s\"",name); + if (errno == EACCES) // path specifies a read-only file + { + strcat(sTemp," because it's read-only."); + psReturn = &sTemp[0]; + } + else + if (errno != ENOENT) // not found, or path is a dir + { + // so anything else is probably... + // + strcat(sTemp," because it's probably still open or something?"); + psReturn = &sTemp[0]; + } + else + { + // (not found) harmless, so ignore + } + + if (psReturn) + { + strcat(sTemp,"\nYou should fix this, otherwise you'll keep getting erroneous leak reports!\n"); + } + } + + return psReturn; +} + +// advance camera to next point +void Pointfile_Next (void) +{ + vec3_t dir; + + if (s_check_point >= s_num_points-2) + { + Sys_Status ("End of pointfile", 0); + return; + } + s_check_point++; + VectorCopy (s_pointvecs[s_check_point], g_pParentWnd->GetCamera()->Camera().origin); + VectorCopy (s_pointvecs[s_check_point], g_pParentWnd->GetXYWnd()->GetOrigin()); + VectorSubtract (s_pointvecs[s_check_point+1], g_pParentWnd->GetCamera()->Camera().origin, dir); + VectorNormalize (dir); + g_pParentWnd->GetCamera()->Camera().angles[1] = atan2 (dir[1], dir[0])*180/3.14159; + g_pParentWnd->GetCamera()->Camera().angles[0] = asin (dir[2])*180/3.14159; + + Sys_UpdateWindows (W_ALL); +} + +// advance camera to previous point +void Pointfile_Prev (void) +{ + vec3_t dir; + + if ( s_check_point == 0) + { + Sys_Status ("Start of pointfile", 0); + return; + } + s_check_point--; + VectorCopy (s_pointvecs[s_check_point], g_pParentWnd->GetCamera()->Camera().origin); + VectorCopy (s_pointvecs[s_check_point], g_pParentWnd->GetXYWnd()->GetOrigin()); + VectorSubtract (s_pointvecs[s_check_point+1], g_pParentWnd->GetCamera()->Camera().origin, dir); + VectorNormalize (dir); + g_pParentWnd->GetCamera()->Camera().angles[1] = atan2 (dir[1], dir[0])*180/3.14159; + g_pParentWnd->GetCamera()->Camera().angles[0] = asin (dir[2])*180/3.14159; + + Sys_UpdateWindows (W_ALL); +} + +void Pointfile_Check (void) +{ + char name[1024]; + FILE *f; + vec3_t v; + + strcpy (name, currentmap); + StripExtension (name); + strcat (name, ".lin"); + + f = fopen (name, "r"); + if (!f) + return; + + Sys_Printf ("Reading pointfile %s\n", name); + + if (!g_qeglobals.d_pointfile_display_list) + g_qeglobals.d_pointfile_display_list = qglGenLists(1); + + s_num_points = 0; + qglNewList (g_qeglobals.d_pointfile_display_list, GL_COMPILE); + qglColor3f (1, 0, 0); + qglDisable(GL_TEXTURE_2D); + qglDisable(GL_TEXTURE_1D); + qglLineWidth (4); + qglBegin(GL_LINE_STRIP); + do + { + if (fscanf (f, "%f %f %f\n", &v[0], &v[1], &v[2]) != 3) + break; + if (s_num_points < MAX_POINTFILE) + { + VectorCopy (v, s_pointvecs[s_num_points]); + s_num_points++; + } + qglVertex3fv (v); + } while (1); + qglEnd(); + qglLineWidth (1); + qglEndList (); + + s_check_point = 0; + fclose (f); + + // don't jump the camera to the leak line, the designers hated this... +#if 0 + Pointfile_Next (); +#else + Sys_UpdateWindows (W_ALL); +#endif +} + +void Pointfile_Draw( void ) +{ + int i; + + qglColor3f( 1.0F, 0.0F, 0.0F ); + qglDisable(GL_TEXTURE_2D); + qglDisable(GL_TEXTURE_1D); + qglLineWidth (4); + qglBegin(GL_LINE_STRIP); + for ( i = 0; i < s_num_points; i++ ) + { + qglVertex3fv( s_pointvecs[i] ); + } + qglEnd(); + qglLineWidth( 1 ); +} + +void Pointfile_Clear (void) +{ + if (!g_qeglobals.d_pointfile_display_list) + return; + + qglDeleteLists (g_qeglobals.d_pointfile_display_list, 1); + g_qeglobals.d_pointfile_display_list = 0; + Sys_UpdateWindows (W_ALL); +} + diff --git a/utils/Radiant/prefsdlg.cpp b/utils/Radiant/prefsdlg.cpp new file mode 100644 index 0000000..503a2d1 --- /dev/null +++ b/utils/Radiant/prefsdlg.cpp @@ -0,0 +1,540 @@ +// PrefsDlg.cpp : implementation file +// + +#include "stdafx.h" +#include "PrefsDlg.h" +#include "shlobj.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#define PREF_SECTION "Prefs" +#define INTERNAL_SECTION "Internals" +#define MOUSE_KEY "MouseButtons" +#define WINDOW_KEY "QE4StyleWindows" +#define LAYOUT_KEY "WindowLayout" +#define Q2_KEY "Quake2Dir" +#define RUNQ2_KEY "RunQuake2Run" +#define TLOCK_KEY "TextureLock" +#define RLOCK_KEY "RotateLock" +#define LOADLAST_KEY "LoadLast" +#define LOADLASTMAP_KEY "LoadLastMap" +#define LASTPROJ_KEY "LastProject" +#define LASTMAP_KEY "LastMap" +#define RUN_KEY "RunBefore" +#define _3DFX_KEY "Use3Dfx" +#define FACE_KEY "NewFaceGrab" +#define BSP_KEY "InternalBSP" +#define RCLICK_KEY "NewRightClick" +#define VERTEX_KEY "NewVertex" +#define AUTOSAVE_KEY "Autosave" +#define AUTOSAVETIME_KEY "AutosaveMinutes" +#define PAK_KEY "UsePAK" +#define NEWAPPLY_KEY "ApplyDismissesSurface" +#define HACK_KEY "Gatewayescapehack" +#define TEXTURE_KEY "NewTextureWindowStuff" +#define TINYBRUSH_KEY "CleanTinyBrushes" +#define TINYSIZE_KEY "CleanTinyBrusheSize" +#define SNAPSHOT_KEY "Snapshots" +#define NUMSNAPSHOT_KEY "NumSnapshots" +#define PAKFILE_KEY "PAKFile" +#define STATUS_KEY "StatusPointSize" +#define MOVESPEED_KEY "MoveSpeed" +#define ANGLESPEED_KEY "AngleSpeed" +#define SETGAME_KEY "UseSetGame" +#define CAMXYUPDATE_KEY "CamXYUpdate" +#define LIGHTDRAW_KEY "NewLightStyle" +#define WHATGAME_KEY "WhichGame" +#define CUBICCLIP_KEY "CubicClipping" +#define CUBICSCALE_KEY "CubicScale" +#define ALTEDGE_KEY "ALTEdgeDrag" +#define TEXTUREBAR_KEY "UseTextureBar" +#define FACECOLORS_KEY "FaceColors" +#define QE4PAINT_KEY "QE4Paint" +#define SNAPT_KEY "SnapT" +#define XZVIS_KEY "XZVIS" +#define YZVIS_KEY "YZVIS" +#define ZVIS_KEY "ZVIS" +#define SIZEPAINT_KEY "SizePainting" +#define DLLENTITIES_KEY "DLLEntities" +#define WIDETOOLBAR_KEY "WideToolBar" +#define NOCLAMP_KEY "NoClamp" +#define PREFAB_KEY "PrefabPath" +#define USERINI_KEY "UserINIPath" +#define ROTATION_KEY "Rotation" +#define SGIOPENGL_KEY "SGIOpenGL" +#define BUGGYICD_KEY "BuggyICD" +#define HICOLOR_KEY "HiColorTextures" +#define CHASEMOUSE_KEY "ChaseMouse" +#define ENTITYSHOW_KEY "EntityShow" +#define TEXTURESCALE_KEY "TextureScale" +#define TEXTURESCROLLBAR_KEY "TextureScrollbar" +#define DISPLAYLISTS_KEY "UseDisplayLists" +#define NORMALIZECOLORS_KEY "NormalizeColors" +#define SHADERS_KEY "UseShaders" +#define SWITCHCLIP_KEY "SwitchClipKey" +#define SELWHOLEENTS_KEY "SelectWholeEntitiesKey" +#define TEXTURESUBSET_KEY "UseTextureSubsetLoading" +#define TEXTUREQUALITY_KEY "TextureQuality" +#define SHOWSHADERS_KEY "ShowShaders" +#define SHADERTEST_KEY "ShaderTest" + +#define MOUSE_DEF 1 +#define WINDOW_DEF 0 +#define Q2_DEF "c:\\quake2\\quake2.exe" +#define PAKFILE_DEF "c:\\quake2\\baseq2\\pak0.pak" +#define RUNQ2_DEF 0 +#define TLOCK_DEF 1 +#define LOADLAST_DEF 1 +#define RUN_DEF 0 + +///////////////////////////////////////////////////////////////////////////// +// CPrefsDlg dialog + + +CPrefsDlg::CPrefsDlg(CWnd* pParent /*=NULL*/) + : CDialog(CPrefsDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CPrefsDlg) + m_strQuake2 = _T("c:\\quake2\\"); + m_nMouse = 0; + m_nView = 0; + m_bLoadLast = FALSE; + m_bFace = FALSE; + m_bInternalBSP = FALSE; + m_bRightClick = FALSE; + m_bRunQuake = FALSE; + m_bSetGame = FALSE; + m_bVertex = FALSE; + m_bAutoSave = TRUE; + m_bNewApplyHandling = FALSE; + m_strAutoSave = _T("5"); + m_bPAK = FALSE; + m_bLoadLastMap = FALSE; + m_bTextureWindow = FALSE; + m_bSnapShots = FALSE; + m_fTinySize = 0.5; + m_bCleanTiny = FALSE; + m_strPAKFile = _T("c:\\quake2\\baseq2\\pak0.pak"); + m_nStatusSize = 10; + m_bCamXYUpdate = FALSE; + m_bNewLightDraw = FALSE; + m_strPrefabPath = _T(""); + m_nWhatGame = 0; + m_strWhatGame = _T("Quake3"); + m_bALTEdge = FALSE; + m_bTextureBar = FALSE; + m_bFaceColors = FALSE; + m_bQE4Painting = FALSE; + m_bSnapTToGrid = FALSE; + m_bXZVis = FALSE; + m_bYZVis = FALSE; + m_bZVis = FALSE; + m_bSizePaint = FALSE; + m_bDLLEntities = FALSE; + m_bWideToolbar = FALSE; + m_bNoClamp = FALSE; + m_strUserPath = _T(""); + m_nRotation = 0; + m_bSGIOpenGL = FALSE; + m_bBuggyICD = FALSE; + m_bHiColorTextures = TRUE; + m_bChaseMouse = FALSE; + m_bTextureScrollbar = TRUE; + m_bDisplayLists = TRUE; + m_bUseShaders = FALSE; + m_bShaderTest = TRUE; + m_numSnapShots = 0; + //}}AFX_DATA_INIT + //LoadPrefs(); + m_bSelectCurves = TRUE; + m_nEntityShowState = 0; + m_nTextureScale = 0; + m_bSwitchClip = FALSE; + m_bSelectWholeEntities = TRUE; + m_nTextureQuality = 3; + m_bShowShaders = TRUE; +} + + + + +void CPrefsDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CPrefsDlg) + DDX_Control(pDX, IDC_SLIDER_TEXTUREQUALITY, m_wndTexturequality); + DDX_Control(pDX, IDC_COMBO_WHATGAME, m_wndWhatGame); + DDX_Control(pDX, IDC_SLIDER_CAMSPEED, m_wndCamSpeed); + DDX_Control(pDX, IDC_SPIN_AUTOSAVE, m_wndSpin); + DDX_Text(pDX, IDC_EDIT_QUAKE2, m_strQuake2); + DDX_Radio(pDX, IDC_RADIO_MOUSE, m_nMouse); + DDX_Radio(pDX, IDC_RADIO_VIEWTYPE, m_nView); + DDX_Check(pDX, IDC_CHECK_LOADLAST, m_bLoadLast); + DDX_Check(pDX, IDC_CHECK_FACE, m_bFace); + DDX_Check(pDX, IDC_CHECK_INTERNALBSP, m_bInternalBSP); + DDX_Check(pDX, IDC_CHECK_RIGHTCLICK, m_bRightClick); + DDX_Check(pDX, IDC_CHECK_RUNQUAKE, m_bRunQuake); + DDX_Check(pDX, IDC_CHECK_SETGAME, m_bSetGame); + DDX_Check(pDX, IDC_CHECK_VERTEX, m_bVertex); + DDX_Check(pDX, IDC_CHECK_AUTOSAVE, m_bAutoSave); + DDX_Text(pDX, IDC_EDIT_AUTOSAVE, m_strAutoSave); + DDX_Check(pDX, IDC_CHECK_PAK, m_bPAK); + DDX_Check(pDX, IDC_CHECK_LOADLASTMAP, m_bLoadLastMap); + DDX_Check(pDX, IDC_CHECK_TEXTUREWINDOW, m_bTextureWindow); + DDX_Check(pDX, IDC_CHECK_SNAPSHOTS, m_bSnapShots); + DDX_Text(pDX, IDC_EDIT_PAKFILE, m_strPAKFile); + DDX_Text(pDX, IDC_EDIT_STATUSPOINTSIZE, m_nStatusSize); + DDX_Check(pDX, IDC_CHECK_CAMXYUPDATE, m_bCamXYUpdate); + DDX_Check(pDX, IDC_CHECK_LIGHTDRAW, m_bNewLightDraw); + DDX_Text(pDX, IDC_EDIT_PREFABPATH, m_strPrefabPath); + DDX_CBString(pDX, IDC_COMBO_WHATGAME, m_strWhatGame); + DDX_Check(pDX, IDC_CHECK_ALTDRAG, m_bALTEdge); + DDX_Check(pDX, IDC_CHECK_TEXTURETOOLBAR, m_bTextureBar); + DDX_Check(pDX, IDC_CHECK_FACECOLOR, m_bFaceColors); + DDX_Check(pDX, IDC_CHECK_QE4PAINTING, m_bQE4Painting); + DDX_Check(pDX, IDC_CHECK_SNAPT, m_bSnapTToGrid); + DDX_Check(pDX, IDC_CHECK_SIZEPAINT, m_bSizePaint); + DDX_Check(pDX, IDC_CHECK_DLLENTITIES, m_bDLLEntities); + DDX_Check(pDX, IDC_CHECK_WIDETOOLBAR, m_bWideToolbar); + DDX_Check(pDX, IDC_CHECK_NOCLAMP, m_bNoClamp); + DDX_Text(pDX, IDC_EDIT_USERPATH, m_strUserPath); + DDX_Text(pDX, IDC_EDIT_ROTATION, m_nRotation); + DDX_Check(pDX, IDC_CHECK_SGIOPENGL, m_bSGIOpenGL); + DDX_Check(pDX, IDC_CHECK_BUGGYICD, m_bBuggyICD); + DDX_Check(pDX, IDC_CHECK_HICOLOR, m_bHiColorTextures); + DDX_Check(pDX, IDC_CHECK_MOUSECHASE, m_bChaseMouse); + DDX_Check(pDX, IDC_CHECK_TEXTURESCROLLBAR, m_bTextureScrollbar); + DDX_Check(pDX, IDC_CHECK_DISPLAYLISTS, m_bDisplayLists); + DDX_Check(pDX, IDC_CHECK_USESHADERS, m_bUseShaders); + DDX_Check(pDX, IDC_CHECK_SHADERTEST, m_bShaderTest); + DDX_Text(pDX, IDC_EDIT_NUMSNAPSHOTS, m_numSnapShots); + DDV_MinMaxInt(pDX, m_numSnapShots, 0, 99); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CPrefsDlg, CDialog) + //{{AFX_MSG_MAP(CPrefsDlg) + ON_BN_CLICKED(IDC_BTN_BROWSE, OnBtnBrowse) + ON_BN_CLICKED(IDC_BTN_BROWSEPAK, OnBtnBrowsepak) + ON_BN_CLICKED(IDC_BTN_BROWSEPREFAB, OnBtnBrowseprefab) + ON_BN_CLICKED(IDC_BTN_BROWSEUSERINI, OnBtnBrowseuserini) + ON_CBN_SELCHANGE(IDC_COMBO_WHATGAME, OnSelchangeComboWhatgame) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CPrefsDlg message handlers + +void CPrefsDlg::OnBtnBrowse() +{ + UpdateData(TRUE); + CFileDialog dlg(true, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "Executables (*.exe)|*.exe||", this); + if (dlg.DoModal() == IDOK) + { + m_strQuake2 = dlg.GetPathName(); + UpdateData(FALSE); + } +} + +BOOL CPrefsDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + m_wndSpin.SetRange(1,60); + m_wndCamSpeed.SetRange(10, 800); + m_wndCamSpeed.SetPos(m_nMoveSpeed); + + this->m_wndTexturequality.SetRange(0, 3); + this->m_wndTexturequality.SetPos(m_nTextureQuality); + + m_wndWhatGame.AddString("Quake2"); + m_wndWhatGame.AddString("Quake3"); + + GetDlgItem(IDC_CHECK_HICOLOR)->EnableWindow(TRUE); + GetDlgItem(IDC_CHECK_NOCLAMP)->EnableWindow(TRUE); + + //GetDlgItem(IDC_CHECK_NOCLAMP)->EnableWindow(FALSE); + + m_wndWhatGame.SelectString(-1,m_strWhatGame); + if (strstr(m_strWhatGame, "Quake3") != NULL) + { + GetDlgItem(IDC_EDIT_PAKFILE)->EnableWindow(FALSE); + GetDlgItem(IDC_BTN_BROWSEPAK)->EnableWindow(FALSE); + GetDlgItem(IDC_CHECK_INTERNALBSP)->EnableWindow(FALSE); + } + else + { + GetDlgItem(IDC_EDIT_PAKFILE)->EnableWindow(TRUE); + GetDlgItem(IDC_BTN_BROWSEPAK)->EnableWindow(TRUE); + GetDlgItem(IDC_CHECK_INTERNALBSP)->EnableWindow(TRUE); + } + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void CPrefsDlg::OnOK() +{ + m_nMoveSpeed = m_wndCamSpeed.GetPos(); + m_nAngleSpeed = (float)m_nMoveSpeed * 0.50; + this->m_nTextureQuality = m_wndTexturequality.GetPos(); + SavePrefs(); + + if (g_pParentWnd) + g_pParentWnd->SetGridStatus(); + Sys_UpdateWindows(W_ALL); + CDialog::OnOK(); +} + +void CPrefsDlg::LoadPrefs() +{ + CString strBuff; + CString strPrefab = g_strAppPath; + AddSlash(strPrefab); + strPrefab += "Prefabs\\"; + + m_nMouse = AfxGetApp()->GetProfileInt(PREF_SECTION, MOUSE_KEY, MOUSE_DEF); + if (m_nMouse == 0) + m_nMouseButtons = 2; + else + m_nMouseButtons = 3; + + m_nView = AfxGetApp()->GetProfileInt(PREF_SECTION, WINDOW_KEY, WINDOW_DEF); + m_strQuake2 = AfxGetApp()->GetProfileString(PREF_SECTION, Q2_KEY, Q2_DEF); + m_bRunQuake = AfxGetApp()->GetProfileInt(PREF_SECTION, RUNQ2_KEY, RUNQ2_DEF); + m_bTextureLock = AfxGetApp()->GetProfileInt(PREF_SECTION, TLOCK_KEY, TLOCK_DEF); + m_bRotateLock = AfxGetApp()->GetProfileInt(PREF_SECTION, RLOCK_KEY, TLOCK_DEF); + m_strLastProject = AfxGetApp()->GetProfileString(PREF_SECTION, LASTPROJ_KEY, ""); + m_strLastMap = AfxGetApp()->GetProfileString(PREF_SECTION, LASTMAP_KEY, ""); + m_bLoadLast = AfxGetApp()->GetProfileInt(PREF_SECTION, LOADLAST_KEY, LOADLAST_DEF); + m_bRunBefore = AfxGetApp()->GetProfileInt(INTERNAL_SECTION, RUN_KEY, RUN_DEF); + //m_b3Dfx = AfxGetApp()->GetProfileInt(PREF_SECTION, _3DFX_KEY, 0); + m_bFace = AfxGetApp()->GetProfileInt(PREF_SECTION, FACE_KEY, 1); + m_bInternalBSP = AfxGetApp()->GetProfileInt(PREF_SECTION, BSP_KEY, 1); + m_bRightClick = AfxGetApp()->GetProfileInt(PREF_SECTION, RCLICK_KEY, 1); + m_bVertex = AfxGetApp()->GetProfileInt(PREF_SECTION, VERTEX_KEY, 1); + m_bAutoSave = AfxGetApp()->GetProfileInt(PREF_SECTION, AUTOSAVE_KEY, 1); + m_bPAK = AfxGetApp()->GetProfileInt(PREF_SECTION, PAK_KEY, 1); + m_bNewApplyHandling = AfxGetApp()->GetProfileInt(PREF_SECTION, NEWAPPLY_KEY, 0); + m_bLoadLastMap = AfxGetApp()->GetProfileInt(PREF_SECTION, LOADLASTMAP_KEY, 0); + m_bGatewayHack = AfxGetApp()->GetProfileInt(PREF_SECTION, HACK_KEY, 0); + m_bTextureWindow = AfxGetApp()->GetProfileInt(PREF_SECTION, TEXTURE_KEY, 1); + m_bCleanTiny = AfxGetApp()->GetProfileInt(PREF_SECTION, TINYBRUSH_KEY, 0); + strBuff = AfxGetApp()->GetProfileString(PREF_SECTION, TINYSIZE_KEY, "0.5"); + m_fTinySize = atof(strBuff); + m_nAutoSave = AfxGetApp()->GetProfileInt(PREF_SECTION, AUTOSAVETIME_KEY, 5); + m_strAutoSave.Format("%i", m_nAutoSave); + m_bSnapShots = AfxGetApp()->GetProfileInt(PREF_SECTION, SNAPSHOT_KEY, 1); + m_numSnapShots = AfxGetApp()->GetProfileInt(PREF_SECTION, NUMSNAPSHOT_KEY, 10); + m_strPAKFile = AfxGetApp()->GetProfileString(PREF_SECTION, PAKFILE_KEY, PAKFILE_DEF); + m_nStatusSize = AfxGetApp()->GetProfileInt(PREF_SECTION, STATUS_KEY, 10); + m_nMoveSpeed = AfxGetApp()->GetProfileInt(PREF_SECTION, MOVESPEED_KEY, 400); + m_nAngleSpeed = AfxGetApp()->GetProfileInt(PREF_SECTION, ANGLESPEED_KEY, 300); + m_bSetGame = AfxGetApp()->GetProfileInt(PREF_SECTION, SETGAME_KEY, 0); + m_bCamXYUpdate = AfxGetApp()->GetProfileInt(PREF_SECTION, CAMXYUPDATE_KEY, 1); + m_bNewLightDraw = AfxGetApp()->GetProfileInt(PREF_SECTION, LIGHTDRAW_KEY, 1); + m_bCubicClipping = AfxGetApp()->GetProfileInt(PREF_SECTION, CUBICCLIP_KEY, 1); + m_nCubicScale = AfxGetApp()->GetProfileInt(PREF_SECTION, CUBICSCALE_KEY, 13); + m_bALTEdge = AfxGetApp()->GetProfileInt(PREF_SECTION, ALTEDGE_KEY, 0); + m_bTextureBar = AfxGetApp()->GetProfileInt(PREF_SECTION, TEXTUREBAR_KEY, 0); + m_strWhatGame = AfxGetApp()->GetProfileString(PREF_SECTION, WHATGAME_KEY, "Quake3"); + m_bFaceColors = AfxGetApp()->GetProfileInt(PREF_SECTION, FACECOLORS_KEY, 0); + m_bQE4Painting = AfxGetApp()->GetProfileInt(PREF_SECTION, QE4PAINT_KEY, 1); + m_bSnapTToGrid = AfxGetApp()->GetProfileInt(PREF_SECTION, SNAPT_KEY, 0); + m_bXZVis = AfxGetApp()->GetProfileInt(PREF_SECTION, XZVIS_KEY, 0); + m_bYZVis = AfxGetApp()->GetProfileInt(PREF_SECTION, YZVIS_KEY, 0); + m_bZVis = AfxGetApp()->GetProfileInt(PREF_SECTION, ZVIS_KEY, 1); + m_bSizePaint = AfxGetApp()->GetProfileInt(PREF_SECTION, SIZEPAINT_KEY, 0); + m_bDLLEntities = AfxGetApp()->GetProfileInt(PREF_SECTION, DLLENTITIES_KEY, 0); + m_bWideToolbar = AfxGetApp()->GetProfileInt(PREF_SECTION, WIDETOOLBAR_KEY, 0); + m_bNoClamp = AfxGetApp()->GetProfileInt(PREF_SECTION, NOCLAMP_KEY, 0); + m_strPrefabPath = AfxGetApp()->GetProfileString(PREF_SECTION, PREFAB_KEY, strPrefab); + m_strUserPath = AfxGetApp()->GetProfileString(PREF_SECTION, USERINI_KEY, ""); + m_nRotation = AfxGetApp()->GetProfileInt(PREF_SECTION, ROTATION_KEY, 45); + m_bSGIOpenGL = AfxGetApp()->GetProfileInt(PREF_SECTION, SGIOPENGL_KEY, 0); + m_bBuggyICD = AfxGetApp()->GetProfileInt(PREF_SECTION, BUGGYICD_KEY, 0); + m_bHiColorTextures = AfxGetApp()->GetProfileInt(PREF_SECTION, HICOLOR_KEY, 1); + m_bChaseMouse = AfxGetApp()->GetProfileInt(PREF_SECTION, CHASEMOUSE_KEY, 1); + m_nEntityShowState = AfxGetApp()->GetProfileInt(PREF_SECTION, ENTITYSHOW_KEY, 0); + m_nTextureScale = AfxGetApp()->GetProfileInt(PREF_SECTION, TEXTURESCALE_KEY, 50); + m_bTextureScrollbar = AfxGetApp()->GetProfileInt(PREF_SECTION, TEXTURESCROLLBAR_KEY, TRUE); + m_bDisplayLists = AfxGetApp()->GetProfileInt(PREF_SECTION, DISPLAYLISTS_KEY, TRUE); + m_bUseShaders = AfxGetApp()->GetProfileInt(PREF_SECTION, SHADERS_KEY, FALSE); + m_bSwitchClip = AfxGetApp()->GetProfileInt(PREF_SECTION, SWITCHCLIP_KEY, TRUE); + m_bSelectWholeEntities = AfxGetApp()->GetProfileInt(PREF_SECTION, SELWHOLEENTS_KEY, TRUE); + m_nTextureQuality = AfxGetApp()->GetProfileInt(PREF_SECTION, TEXTUREQUALITY_KEY, 6); + m_bShowShaders = AfxGetApp()->GetProfileInt(PREF_SECTION, SHOWSHADERS_KEY, TRUE); + m_bShaderTest = AfxGetApp()->GetProfileInt(PREF_SECTION, SHADERTEST_KEY, TRUE); + + if (m_bRunBefore == FALSE) + { + SetGamePrefs(); + } +} + + +void CPrefsDlg::SavePrefs() +{ + if (GetSafeHwnd()) + UpdateData(TRUE); + AfxGetApp()->WriteProfileInt(PREF_SECTION, MOUSE_KEY, m_nMouse); + if (m_nMouse == 0) + m_nMouseButtons = 2; + else + m_nMouseButtons = 3; + AfxGetApp()->WriteProfileInt(PREF_SECTION, WINDOW_KEY, m_nView); + AfxGetApp()->WriteProfileString(PREF_SECTION, Q2_KEY, m_strQuake2); + AfxGetApp()->WriteProfileInt(PREF_SECTION, RUNQ2_KEY, m_bRunQuake); + AfxGetApp()->WriteProfileInt(PREF_SECTION, TLOCK_KEY, m_bTextureLock); + AfxGetApp()->WriteProfileInt(PREF_SECTION, RLOCK_KEY, m_bRotateLock); + AfxGetApp()->WriteProfileInt(PREF_SECTION, LOADLAST_KEY, m_bLoadLast); + AfxGetApp()->WriteProfileString(PREF_SECTION, LASTPROJ_KEY, m_strLastProject); + AfxGetApp()->WriteProfileString(PREF_SECTION, LASTMAP_KEY, m_strLastMap); + AfxGetApp()->WriteProfileInt(INTERNAL_SECTION, RUN_KEY, m_bRunBefore); + //AfxGetApp()->WriteProfileInt(PREF_SECTION, _3DFX_KEY, m_b3Dfx); + AfxGetApp()->WriteProfileInt(PREF_SECTION, FACE_KEY, m_bFace); + AfxGetApp()->WriteProfileInt(PREF_SECTION, BSP_KEY, m_bInternalBSP); + AfxGetApp()->WriteProfileInt(PREF_SECTION, RCLICK_KEY, m_bRightClick); + AfxGetApp()->WriteProfileInt(PREF_SECTION, VERTEX_KEY, m_bVertex); + AfxGetApp()->WriteProfileInt(PREF_SECTION, AUTOSAVE_KEY, m_bAutoSave); + AfxGetApp()->WriteProfileInt(PREF_SECTION, PAK_KEY, m_bPAK); + AfxGetApp()->WriteProfileInt(PREF_SECTION, LOADLASTMAP_KEY, m_bLoadLastMap); + AfxGetApp()->WriteProfileInt(PREF_SECTION, TEXTURE_KEY, m_bTextureWindow); + m_nAutoSave = atoi(m_strAutoSave); + AfxGetApp()->WriteProfileInt(PREF_SECTION, AUTOSAVETIME_KEY, m_nAutoSave); + AfxGetApp()->WriteProfileInt(PREF_SECTION, SNAPSHOT_KEY, m_bSnapShots); + AfxGetApp()->WriteProfileInt(PREF_SECTION, NUMSNAPSHOT_KEY, m_numSnapShots); + AfxGetApp()->WriteProfileString(PREF_SECTION, PAKFILE_KEY, m_strPAKFile); + AfxGetApp()->WriteProfileInt(PREF_SECTION, STATUS_KEY, m_nStatusSize); + AfxGetApp()->WriteProfileInt(PREF_SECTION, SETGAME_KEY, m_bSetGame); + AfxGetApp()->WriteProfileInt(PREF_SECTION, CAMXYUPDATE_KEY, m_bCamXYUpdate); + AfxGetApp()->WriteProfileInt(PREF_SECTION, LIGHTDRAW_KEY, m_bNewLightDraw); + AfxGetApp()->WriteProfileInt(PREF_SECTION, MOVESPEED_KEY, m_nMoveSpeed); + AfxGetApp()->WriteProfileInt(PREF_SECTION, ANGLESPEED_KEY, m_nAngleSpeed); + AfxGetApp()->WriteProfileInt(PREF_SECTION, CUBICCLIP_KEY, m_bCubicClipping); + AfxGetApp()->WriteProfileInt(PREF_SECTION, CUBICSCALE_KEY, m_nCubicScale); + AfxGetApp()->WriteProfileInt(PREF_SECTION, ALTEDGE_KEY, m_bALTEdge); + AfxGetApp()->WriteProfileInt(PREF_SECTION, TEXTUREBAR_KEY, m_bTextureBar); + AfxGetApp()->WriteProfileInt(PREF_SECTION, FACECOLORS_KEY, m_bFaceColors); + AfxGetApp()->WriteProfileString(PREF_SECTION, WHATGAME_KEY, m_strWhatGame); + AfxGetApp()->WriteProfileInt(PREF_SECTION, QE4PAINT_KEY, m_bQE4Painting); + AfxGetApp()->WriteProfileInt(PREF_SECTION, SNAPT_KEY, m_bSnapTToGrid); + AfxGetApp()->WriteProfileInt(PREF_SECTION, XZVIS_KEY, m_bXZVis); + AfxGetApp()->WriteProfileInt(PREF_SECTION, YZVIS_KEY, m_bYZVis); + AfxGetApp()->WriteProfileInt(PREF_SECTION, ZVIS_KEY, m_bZVis); + AfxGetApp()->WriteProfileInt(PREF_SECTION, SIZEPAINT_KEY, m_bSizePaint); + AfxGetApp()->WriteProfileInt(PREF_SECTION, DLLENTITIES_KEY, m_bDLLEntities); + AfxGetApp()->WriteProfileInt(PREF_SECTION, WIDETOOLBAR_KEY, m_bWideToolbar); + AfxGetApp()->WriteProfileInt(PREF_SECTION, NOCLAMP_KEY, m_bNoClamp); + AfxGetApp()->WriteProfileString(PREF_SECTION, PREFAB_KEY, m_strPrefabPath); + AfxGetApp()->WriteProfileString(PREF_SECTION, USERINI_KEY, m_strUserPath); + AfxGetApp()->WriteProfileInt(PREF_SECTION, ROTATION_KEY, m_nRotation); + AfxGetApp()->WriteProfileInt(PREF_SECTION, SGIOPENGL_KEY, m_bSGIOpenGL); + AfxGetApp()->WriteProfileInt(PREF_SECTION, BUGGYICD_KEY, m_bBuggyICD); + AfxGetApp()->WriteProfileInt(PREF_SECTION, HICOLOR_KEY, m_bHiColorTextures); + AfxGetApp()->WriteProfileInt(PREF_SECTION, CHASEMOUSE_KEY, m_bChaseMouse); + AfxGetApp()->WriteProfileInt(PREF_SECTION, ENTITYSHOW_KEY, m_nEntityShowState); + AfxGetApp()->WriteProfileInt(PREF_SECTION, TEXTURESCALE_KEY, m_nTextureScale); + AfxGetApp()->WriteProfileInt(PREF_SECTION, TEXTURESCROLLBAR_KEY, m_bTextureScrollbar); + AfxGetApp()->WriteProfileInt(PREF_SECTION, DISPLAYLISTS_KEY, m_bDisplayLists); + AfxGetApp()->WriteProfileInt(PREF_SECTION, SHADERS_KEY, m_bUseShaders); + AfxGetApp()->WriteProfileInt(PREF_SECTION, SWITCHCLIP_KEY, m_bSwitchClip); + AfxGetApp()->WriteProfileInt(PREF_SECTION, SELWHOLEENTS_KEY, m_bSelectWholeEntities); + AfxGetApp()->WriteProfileInt(PREF_SECTION, TEXTUREQUALITY_KEY, m_nTextureQuality); + AfxGetApp()->WriteProfileInt(PREF_SECTION, SHOWSHADERS_KEY, m_bShowShaders); + AfxGetApp()->WriteProfileInt(PREF_SECTION, SHADERTEST_KEY, m_bShaderTest); +} + + + +void CPrefsDlg::OnBtnBrowsepak() +{ + UpdateData(TRUE); + CFileDialog dlg(true, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "PAK files (*.pak)|*.pak||", this); + if (dlg.DoModal() == IDOK) + { + m_strPAKFile = dlg.GetPathName(); + UpdateData(FALSE); + } +} + +void CPrefsDlg::OnBtnBrowseprefab() +{ + UpdateData(TRUE); + BROWSEINFO bi; + CString strPath; + char* p = strPath.GetBuffer(MAX_PATH+1); + bi.hwndOwner = GetSafeHwnd(); + bi.pidlRoot = NULL; + bi.pszDisplayName = p; + bi.lpszTitle = "Load textures from path"; + bi.ulFlags = 0; + bi.lpfn = NULL; + bi.lParam = NULL; + bi.iImage = 0; + LPITEMIDLIST pidlBrowse; + pidlBrowse = SHBrowseForFolder(&bi); + if (pidlBrowse) + { + SHGetPathFromIDList(pidlBrowse, p); + strPath.ReleaseBuffer(); + AddSlash(strPath); + m_strPrefabPath = strPath; + UpdateData(FALSE); + } +} + +void CPrefsDlg::OnBtnBrowseuserini() +{ + UpdateData(TRUE); + CFileDialog dlg(true, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "INI files (*.ini)|*.ini||", this); + if (dlg.DoModal() == IDOK) + { + m_strUserPath = dlg.GetPathName(); + UpdateData(FALSE); + } +} + +void CPrefsDlg::OnSelchangeComboWhatgame() +{ + int n = m_wndWhatGame.GetCurSel(); + if (n >= 0) + { + m_wndWhatGame.GetLBText(n, m_strWhatGame); + } + SetGamePrefs(); +} + +void CPrefsDlg::SetGamePrefs() +{ + if (strstr(m_strWhatGame, "Quake3") != NULL) + { + m_bHiColorTextures = TRUE; + m_bWideToolbar = TRUE; + m_strPAKFile = "PK3 files are loaded from the baseq3 path"; + m_bInternalBSP = FALSE; + if (GetSafeHwnd()) + { + GetDlgItem(IDC_EDIT_PAKFILE)->EnableWindow(FALSE); + GetDlgItem(IDC_BTN_BROWSEPAK)->EnableWindow(FALSE); + GetDlgItem(IDC_CHECK_INTERNALBSP)->EnableWindow(FALSE); + } + } + else + { + m_bHiColorTextures = FALSE; + m_bWideToolbar = FALSE; + m_strPAKFile = PAKFILE_DEF; + if (GetSafeHwnd()) + { + GetDlgItem(IDC_EDIT_PAKFILE)->EnableWindow(TRUE); + GetDlgItem(IDC_BTN_BROWSEPAK)->EnableWindow(TRUE); + GetDlgItem(IDC_CHECK_INTERNALBSP)->EnableWindow(TRUE); + } + } + SavePrefs(); +} diff --git a/utils/Radiant/prefsdlg.h b/utils/Radiant/prefsdlg.h new file mode 100644 index 0000000..7b03ab8 --- /dev/null +++ b/utils/Radiant/prefsdlg.h @@ -0,0 +1,133 @@ +#if !defined(AFX_PREFSDLG_H__DC829122_812D_11D1_B548_00AA00A410FC__INCLUDED_) +#define AFX_PREFSDLG_H__DC829122_812D_11D1_B548_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// PrefsDlg.h : header file +// + +#include "resource.h" + +///////////////////////////////////////////////////////////////////////////// +// CPrefsDlg dialog + +#define MAX_TEXTURE_QUALITY 3 + +class CPrefsDlg : public CDialog +{ +// Construction +public: + // these mirror what goes in the combo box + enum {SHADER_NONE = 0, SHADER_COMMON, SHADER_ALL}; + void LoadPrefs(); + void SavePrefs(); + void SetGamePrefs(); + CPrefsDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CPrefsDlg) + enum { IDD = IDD_DLG_PREFS }; + CSliderCtrl m_wndTexturequality; + CComboBox m_wndWhatGame; + CSliderCtrl m_wndCamSpeed; + CSpinButtonCtrl m_wndSpin; + CString m_strQuake2; + int m_nMouse; + int m_nView; + BOOL m_bTextureLock; + BOOL m_bLoadLast; + BOOL m_bRunBefore; + CString m_strLastProject; + CString m_strLastMap; + BOOL m_bFace; + BOOL m_bInternalBSP; + BOOL m_bRightClick; + BOOL m_bRunQuake; + BOOL m_bSetGame; + BOOL m_bVertex; + BOOL m_bAutoSave; + BOOL m_bNewApplyHandling; + CString m_strAutoSave; + BOOL m_bPAK; + BOOL m_bLoadLastMap; + BOOL m_bGatewayHack; + BOOL m_bTextureWindow; + BOOL m_bSnapShots; + float m_fTinySize; + BOOL m_bCleanTiny; + CString m_strPAKFile; + int m_nStatusSize; + BOOL m_bCamXYUpdate; + BOOL m_bNewLightDraw; + CString m_strPrefabPath; + int m_nWhatGame; + CString m_strWhatGame; + BOOL m_bALTEdge; + BOOL m_bTextureBar; + BOOL m_bFaceColors; + BOOL m_bQE4Painting; + BOOL m_bSnapTToGrid; + BOOL m_bXZVis; + BOOL m_bYZVis; + BOOL m_bZVis; + BOOL m_bSizePaint; + BOOL m_bDLLEntities; + BOOL m_bRotateLock; + BOOL m_bWideToolbar; + BOOL m_bNoClamp; + CString m_strUserPath; + int m_nRotation; + BOOL m_bSGIOpenGL; + BOOL m_bBuggyICD; + BOOL m_bHiColorTextures; + BOOL m_bChaseMouse; + BOOL m_bTextureScrollbar; + BOOL m_bDisplayLists; + BOOL m_bUseShaders; + BOOL m_bShaderTest; + int m_numSnapShots; + //}}AFX_DATA + int m_nMouseButtons; + int m_nAngleSpeed; + int m_nMoveSpeed; + int m_nAutoSave; + bool m_bCubicClipping; + int m_nCubicScale; + BOOL m_bSelectCurves; + int m_nEntityShowState; + int m_nTextureScale; + BOOL m_bNormalizeColors; + BOOL m_bSwitchClip; + BOOL m_bSelectWholeEntities; + int m_nTextureQuality; + BOOL m_bShowShaders; + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CPrefsDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CPrefsDlg) + afx_msg void OnBtnBrowse(); + virtual BOOL OnInitDialog(); + virtual void OnOK(); + afx_msg void OnBtnBrowsepak(); + afx_msg void OnBtnBrowseprefab(); + afx_msg void OnBtnBrowseuserini(); + afx_msg void OnSelchangeComboWhatgame(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_PREFSDLG_H__DC829122_812D_11D1_B548_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/qe3.cpp b/utils/Radiant/qe3.cpp new file mode 100644 index 0000000..18f454c --- /dev/null +++ b/utils/Radiant/qe3.cpp @@ -0,0 +1,629 @@ +#include "stdafx.h" +#include "qe3.h" +#include "PrefsDlg.h" +#include +#include +#ifdef SOF +#include "materials.h" +#endif +#include "oddbits.h" + +QEGlobals_t g_qeglobals; + + + +void QE_CheckOpenGLForErrors(void) +{ + CString strMsg; + int i = qglGetError(); + if (i != GL_NO_ERROR) + { + if (i == GL_OUT_OF_MEMORY) + { + //strMsg.Format("OpenGL out of memory error %s\nDo you wish to save before exiting?", qgluErrorString((GLenum)i)); + if (MessageBox(g_qeglobals.d_hwndMain, strMsg, "QERadiant Error", MB_YESNO) == IDYES) + { + Map_SaveFile(NULL, false); + } + exit(1); + } + else + { + //strMsg.Format("Warning: OpenGL Error %s\n ", qgluErrorString((GLenum)i)); + Sys_Printf (strMsg.GetBuffer(0)); + } + } +} + + +char *ExpandReletivePath (char *const p) +{ + static char temp[1024]; + char *base; + + if (!p || !p[0]) + return NULL; + if (p[0] == '/' || p[0] == '\\') + return p; + + base = ValueForKey(g_qeglobals.d_project_entity, "basepath"); + sprintf (temp, "%s/%s", base, p); + return temp; +} + + + +void *qmalloc (size_t size) +{ + void *b; + b = malloc(size); + memset (b, 0, size); + return b; +} + +char *copystring (char *const s) +{ + char *b; + b = (char*)malloc(strlen(s)+1); + strcpy (b,s); + return b; +} + + +bool DoesFileExist(const char* const pBuff, long& lSize) +{ + CFile file; + if (file.Open(pBuff, CFile::modeRead | CFile::shareDenyNone)) + { + lSize += file.GetLength(); + file.Close(); + return true; + } + return false; +} + +bool RollDownFiles(const char *const fName, int nCount, int nTop) +{ + //delete the 0 file and move 1-nCount down + CString strOld, strNew; + int i; + + strOld.Format("%s.%i", fName, 0); + remove (strOld); + for (i=0;i lastNumToKeep) //the roll limit is full + { + nCount--; + RollDownFiles(strNewPath,lastNumToKeep, nCount); + strFile.Format("%s.%i", strNewPath, lastNumToKeep); //set it back one to fill in top pos + } + // strFile has the next available slot + Map_SaveFile(strFile, false); + Sys_SetTitle (currentmap); + /*if (lSize > 8 * 1024 * 1024) // total size of saves > 8 mb + { + Sys_Printf("The snapshot files in the [%s] directory total more than 4 megabytes. You might consider cleaning the directory up.", strOrgPath); + } + */ + } + else + { + CString strMsg; + strMsg.Format("Snapshot save failed.. unabled to create directory\n%s", strOrgPath); + g_pParentWnd->MessageBox(strMsg); + } +} +/* +=============== +QE_CheckAutoSave + +If five minutes have passed since making a change +and the map hasn't been saved, save it out. +=============== +*/ + + +void QE_CheckAutoSave( void ) +{ + static clock_t s_start; + clock_t now; + + now = clock(); + + if ( modified != 1 || !s_start) + { + s_start = now; + return; + } + + if ( now - s_start > ( CLOCKS_PER_SEC * 60 * g_PrefsDlg.m_nAutoSave)) + { + + if (g_PrefsDlg.m_bAutoSave) + { + // only snapshot if not working on a default map +/* if (g_PrefsDlg.m_bSnapShots && stricmp(currentmap, "unnamed.map") != 0) + { + Sys_Printf("Autosaving snapshot..."); + Sys_Printf("\n"); + Sys_Status ("Autosaving snapshot...",0); + Map_Snapshot(); + } + else +*/ + { + Sys_Printf("Autosaving ..."); + Sys_Printf("\n"); + Sys_Status ("Autosaving snapshot...",0); + Map_SaveFile (ValueForKey(g_qeglobals.d_project_entity, "autosave"), false); + } + + Sys_Status ("Autosaving...Saved.", 0 ); + modified = 2; + } + else + { + Sys_Printf ("Autosave skipped...\n"); + Sys_Status ("Autosave skipped...", 0 ); + } + s_start = now; + } +} + + +int +BuildShortPathName(const char* pPath, char* pBuffer, int nBufferLen) +{ + int nResult = GetShortPathName(pPath, pBuffer, nBufferLen); + if (nResult == 0) + strcpy(pBuffer, pPath); // Use long filename + return nResult; +} + + + + +/* +=========== +QE_LoadProject +=========== +*/ +qboolean QE_LoadProject (const char *const projectfile) +{ + char *data; + + + Sys_Printf ("QE_LoadProject (%s)\n", projectfile); + +#ifdef SOF + char path2[MAX_PATH]; + strcpy(path2, projectfile); + StripExtension(path2); + DefaultExtension(path2, ".mat"); + QFile_ReadMaterialTypes(ExpandArg(path2)); +#endif + + if ( LoadFileNoCrash (projectfile, (void **)&data) == -1) + return false; + + g_strProject = projectfile; + + CString strData = data; + free(data); + + CString strQ2Path = g_PrefsDlg.m_strQuake2; + CString strQ2File; + ExtractPath_and_Filename(g_PrefsDlg.m_strQuake2, strQ2Path, strQ2File); + AddSlash(strQ2Path); + + + char* pBuff = new char[1024]; + + BuildShortPathName(strQ2Path, pBuff, 1024); + FindReplace(strData, "__Q2PATH", pBuff); + BuildShortPathName(g_strAppPath, pBuff, 1024); + FindReplace(strData, "__QERPATH", pBuff); + + char* pFile; + if (GetFullPathName(projectfile, 1024, pBuff, &pFile)) + { + g_PrefsDlg.m_strLastProject = pBuff; + BuildShortPathName(g_PrefsDlg.m_strLastProject, pBuff, 1024); + g_PrefsDlg.m_strLastProject = pBuff; + g_PrefsDlg.SavePrefs(); + + ExtractPath_and_Filename(pBuff, strQ2Path, strQ2File); + int nLen = strQ2Path.GetLength(); + if (nLen > 0) + { + if (strQ2Path[nLen - 1] == '\\') + strQ2Path.SetAt(nLen-1,'\0'); + char* pBuffer = strQ2Path.GetBufferSetLength(_MAX_PATH + 1); + int n = strQ2Path.ReverseFind('\\'); + if (n >=0 ) + pBuffer[n + 1] = '\0'; + strQ2Path.ReleaseBuffer(); + FindReplace(strData, "__QEPROJPATH", strQ2Path); + } + } + + delete []pBuff; + + StartTokenParsing (strData.GetBuffer(0)); + g_qeglobals.d_project_entity = Entity_Parse (true); + if (!g_qeglobals.d_project_entity) + Error ("Couldn't parse %s", projectfile); + + int len; + + strQ2Path = ValueForKey(g_qeglobals.d_project_entity, "basepath"); + len = strQ2Path.GetLength() - 1; + if (strQ2Path[len] == '\\') { + strQ2Path.SetAt(len,0); + SetKeyValue(g_qeglobals.d_project_entity, "basepath",strQ2Path); //make sure there is no trailing slash + } + + strQ2Path = ValueForKey(g_qeglobals.d_project_entity, "remotebasepath"); + len = strQ2Path.GetLength() - 1; + if (strQ2Path[len] == '\\') { + strQ2Path.SetAt(len,0); + SetKeyValue(g_qeglobals.d_project_entity, "remotebasepath",strQ2Path); //make sure there is no trailing slash + } + + Eclass_InitForSourceDirectory (ValueForKey (g_qeglobals.d_project_entity, "entitypath")); + FillClassList(); // list in entity window + + Map_New(); + + + FillTextureMenu(); + FillBSPMenu(); + + return true; +} +#if 0 +qboolean QE_LoadProject (const char *projectfile) +{ + char *data; + char path2[MAX_PATH]; + + Sys_Printf ("QE_LoadProject (%s)\n", projectfile); + + strcpy(path2, projectfile); + StripExtension(path2); + DefaultExtension(path2, ".mat"); + QFile_ReadMaterialTypes(ExpandArg(path2)); + if ( LoadFileNoCrash (projectfile, (void *)&data) == -1) + return false; + StartTokenParsing (data); + g_qeglobals.d_project_entity = Entity_Parse (true); // parses project file and + // stores key value pairs in linked list g_qeglobals.d_project_entity.epairs, accessed + // using ValueforKey(g_qeglobals.d_project_entity, "keyname")); + if (!g_qeglobals.d_project_entity) + Error ("Couldn't parse %s", projectfile); + free (data); + + Eclass_InitForSourceDirectory (ValueForKey (g_qeglobals.d_project_entity, "entitypath")); + + FillClassList (); // list in entity window + + Map_New (); + + FillTextureMenu (); + FillBSPMenu (); + + return true; +} + +#endif + +/* +=========== +QE_SaveProject +=========== +*/ +//extern char *bsp_commands[256]; + +qboolean QE_SaveProject (const char* pProjectFile) +{ + //char filename[1024]; + FILE *fp; + epair_t *ep; + + //sprintf (filename, "%s\\%s.prj", g_projectdir, g_username); + + if (!(fp = fopen (pProjectFile, "w+"))) + Error ("Could not open project file!"); + + fprintf (fp, "{\n"); + for (ep = g_qeglobals.d_project_entity->epairs; ep; ep=ep->next) + fprintf (fp, "\"%s\" \"%s\"\n", ep->key, ep->value); + fprintf (fp, "}\n"); + + fclose (fp); + + return TRUE; +} + + + +/* +=========== +QE_KeyDown +=========== +*/ +#define SPEED_MOVE 32 +#define SPEED_TURN 22.5 + + +/* +=============== +ConnectEntities + +Sets target / targetname on the two entities selected +from the first selected to the secon +=============== +*/ +void ConnectEntities (bool bIncrementalTargetNames /* = false */ ) +{ + entity_t *e1, *e2, *e; + char *target, *tn; + int maxtarg, targetnum; + char newtarg[32]={0}; + + if (g_qeglobals.d_select_count != 2) + { + Sys_Status ("Must have two brushes selected.", 0); + Sys_Printf ("Must have two brushes selected.\n"); + Sys_Beep (); + return; + } + + e1 = g_qeglobals.d_select_order[0]->owner; + e2 = g_qeglobals.d_select_order[1]->owner; + + if (e1 == world_entity || e2 == world_entity) + { + Sys_Status ("Can't connect to the world.", 0); + Sys_Printf ("Can't connect to the world.\n"); + Sys_Beep (); + return; + } + + if (e1 == e2) + { + Sys_Status ("Brushes are from same entity.", 0); + Sys_Printf ("Brushes are from same entity.\n"); + Sys_Beep (); + return; + } + + static const char *pTargetNames[]= + { + "target", + "target2", + "target3", + "target4" + }; + const int iTargetNames = sizeof(pTargetNames)/sizeof(pTargetNames[0]); + int iTarget = 0; // leave as zero for "else" case below + + if (bIncrementalTargetNames) + { + for (iTarget=0; iTargetnext) + { + tn = ValueForKey (e, "targetname"); + if (tn && tn[0]) + { + targetnum = atoi(tn+1); + if (targetnum > maxtarg) + maxtarg = targetnum; + } + } + sprintf (newtarg, "t%i", maxtarg+1); + } + } + + SetKeyValue (e1, pTargetNames[iTarget], newtarg); + SetKeyValue (e2, "targetname", newtarg); + Sys_UpdateWindows (W_XY | W_CAMERA); + + Select_Deselect(); + Select_Brush (g_qeglobals.d_select_order[1]); +} + +qboolean QE_SingleBrush (const bool bQuiet) +{ + if ( (selected_brushes.next == &selected_brushes) + || (selected_brushes.next->next != &selected_brushes) ) + { + if (!bQuiet) + { + Sys_Printf ("Error: you must have a single brush selected\n"); + } + return false; + } + if (selected_brushes.next->owner->eclass->fixedsize) + { + if (!bQuiet) + { + Sys_Printf ("Error: you cannot manipulate fixed size entities\n"); + } + return false; + } + + return true; +} + +void QE_Init (void) +{ + /* + ** initialize variables + */ + g_qeglobals.d_gridsize = 8; + g_qeglobals.d_showgrid = true; + + /* + ** other stuff + */ + Texture_Init (true); + //Cam_Init (); + //XY_Init (); + Z_Init (); +} + +void QE_ConvertDOSToUnixName( char *dst, const char *src ) +{ + while ( *src ) + { + if ( *src == '\\' ) + *dst = '/'; + else + *dst = *src; + dst++; src++; + } + *dst = 0; +} + +int g_numbrushes, g_numentities; + +void QE_CountBrushesAndUpdateStatusBar( void ) +{ + static int s_lastbrushcount, s_lastentitycount; + static qboolean s_didonce; + + //entity_t *e; + brush_t *b, *next; + + g_numbrushes = 0; + g_numentities = 0; + + if ( active_brushes.next != NULL ) + { + for ( b = active_brushes.next ; b != NULL && b != &active_brushes ; b=next) + { + next = b->next; + if (b->brush_faces ) + { + if ( !b->owner->eclass->fixedsize) + g_numbrushes++; + else + g_numentities++; + } + } + } +/* + if ( entities.next != NULL ) + { + for ( e = entities.next ; e != &entities && g_numentities != MAX_MAP_ENTITIES ; e = e->next) + { + g_numentities++; + } + } +*/ + if ( ( ( g_numbrushes != s_lastbrushcount ) || ( g_numentities != s_lastentitycount ) ) || ( !s_didonce ) ) + { + Sys_UpdateStatusBar(); + + s_lastbrushcount = g_numbrushes; + s_lastentitycount = g_numentities; + s_didonce = true; + } +} + diff --git a/utils/Radiant/qe3.h b/utils/Radiant/qe3.h new file mode 100644 index 0000000..934d6f0 --- /dev/null +++ b/utils/Radiant/qe3.h @@ -0,0 +1,397 @@ +#ifndef __QE3_H__ +#define __QE3_H__ + +// disable data conversion warnings for gl +#pragma warning(disable : 4244) // MIPS +#pragma warning(disable : 4136) // X86 +#pragma warning(disable : 4051) // ALPHA + +#include + +extern "C" +{ +#include "qgl.h" +} + +#include +#include + +#include "qertypes.h" +#include "cmdlib.h" +#include "mathlib.h" +#include "parse.h" +#include "lbmlib.h" + +#include +#include "afxres.h" +#include "resource.h" + +#include "qedefs.h" + +#include "qfiles.h" + +#include "textures.h" +#include "brush.h" +#include "entity.h" +#include "map.h" +#include "select.h" + +#include "camera.h" +#include "xy.h" +#include "z.h" +#include "mru.h" + +typedef struct +{ + int p1, p2; + face_t *f1, *f2; +} pedge_t; + +typedef struct +{ + int iSize; + int iTexMenu; // nearest, linear, etc + float fGamma; // gamma for textures + char szProject[256]; // last project loaded + vec3_t colors[COLOR_LAST]; + qboolean show_names; + qboolean show_coordinates; + int exclude; + int m_nTextureTweak; +} SavedInfo_t; + +// +// system functions +// +void Sys_UpdateStatusBar( void ); +void Sys_UpdateWindows (int bits); +void Sys_Beep (void); +void Sys_ClearPrintf (void); +void Sys_Printf (char *text, ...); +double Sys_DoubleTime (void); +void Sys_GetCursorPos (int *x, int *y); +void Sys_SetCursorPos (int x, int y); +void Sys_SetTitle (char *text); +void Sys_BeginWait (void); +void Sys_EndWait (void); +void Sys_Status(const char *psz, int part); + +/* +** most of the QE globals are stored in this structure +*/ +#define D_MAXPOINTS 4096 +typedef struct +{ + qboolean d_showgrid; + int d_gridsize; + + int d_num_entities; + + entity_t *d_project_entity; + + float d_new_brush_bottom_z, + d_new_brush_top_z; + + HINSTANCE d_hInstance; + + HGLRC d_hglrcBase; + HDC d_hdcBase; + + HWND d_hwndMain; + HWND d_hwndCamera; + HWND d_hwndEdit; + HWND d_hwndEntity; + HWND d_hwndTexture; + HWND d_hwndXY; + HWND d_hwndZ; + HWND d_hwndStatus; + + vec3_t d_points[MAX_POINTS]; + int d_numpoints; + pedge_t d_edges[MAX_EDGES]; + int d_numedges; + + int d_num_move_points; + float *d_move_points[D_MAXPOINTS]; + + qtexture_t *d_qtextures; + + texturewin_t d_texturewin; + + int d_pointfile_display_list; + + xy_t d_xyOld; + + LPMRUMENU d_lpMruMenu; + + SavedInfo_t d_savedinfo; + + int d_workcount; + + // connect entities uses the last two brushes selected + int d_select_count; + brush_t *d_select_order[2]; + vec3_t d_select_translate; // for dragging w/o making new display lists + select_t d_select_mode; + + int d_font_list; + + int d_parsed_brushes; + + qboolean show_blocks; + + vec3_t d_vAreaTL; + vec3_t d_vAreaBR; + +} QEGlobals_t; + +void *qmalloc (size_t size); +char *copystring (char *s); +char *ExpandReletivePath (char *p); + +char *Pointfile_Delete(void); +void Pointfile_Check (void); +void Pointfile_Next (void); +void Pointfile_Prev (void); +void Pointfile_Clear (void); +void Pointfile_Draw( void ); +void Pointfile_Load( void ); + +// +// drag.c +// +void Drag_Begin (int x, int y, int buttons, + vec3_t xaxis, vec3_t yaxis, + vec3_t origin, vec3_t dir); +void Drag_MouseMoved (int x, int y, int buttons); +void Drag_MouseUp (int nButtons = 0); + +// +// csg.c +// +void CSG_MakeHollow (void); +void CSG_Subtract (void); + +// +// vertsel.c +// + +void SetupVertexSelection (void); +void SelectEdgeByRay (vec3_t org, vec3_t dir); +void SelectVertexByRay (vec3_t org, vec3_t dir); + +void ConnectEntities (bool bIncrementalTargetNames = false); + +extern int update_bits; + +extern int screen_width; +extern int screen_height; + +extern HANDLE bsp_process; + +char *TranslateString (char *buf); + +void ProjectDialog (void); + +void FillTextureMenu (CStringArray* pArray = NULL); +void FillBSPMenu (void); + +BOOL CALLBACK Win_Dialog ( + HWND hwndDlg, // handle to dialog box + UINT uMsg, // message + WPARAM wParam, // first message parameter + LPARAM lParam // second message parameter +); + + +// +// win_cam.c +// +void WCam_Create (HINSTANCE hInstance); + + +// +// win_xy.c +// +void WXY_Create (HINSTANCE hInstance); + +// +// win_z.c +// +void WZ_Create (HINSTANCE hInstance); + +// +// win_ent.c +// + + +// +// win_main.c +// +void Main_Create (HINSTANCE hInstance); +extern BOOL SaveWindowState(HWND hWnd, const char *pszName); +extern BOOL LoadWindowState(HWND hWnd, const char *pszName); + +extern BOOL SaveRegistryInfo(const char *pszName, void *pvBuf, long lSize); +extern BOOL LoadRegistryInfo(const char *pszName, void *pvBuf, long *plSize); + +// +// entityw.c +// +BOOL CreateEntityWindow(HINSTANCE hInstance); +void FillClassList (void); +BOOL UpdateEntitySel(eclass_t *pec); +void SetInspectorMode(int iType); +int DrawTexControls(HWND hWnd); +void SetSpawnFlags(void); +void GetSpawnFlags(void); +void SetKeyValuePairs(bool bClearMD3 = false); +extern void BuildGammaTable(float g); + + +// win_dlg.c + +void DoGamma(void); +void DoFind(void); +void DoRotate(void); +void DoSides(bool bCone = false); +void DoAbout(void); +void DoSurface(); + +/* +** QE function declarations +*/ +void QE_CheckAutoSave( void ); +void QE_ConvertDOSToUnixName( char *dst, const char *src ); +void QE_CountBrushesAndUpdateStatusBar( void ); +void QE_CheckOpenGLForErrors(void); +void QE_ExpandBspString (char *bspaction, char *out, char *mapname); +void QE_Init (void); +qboolean QE_KeyDown (int key, int nFlags = 0); +qboolean QE_LoadProject (const char *projectfile); +qboolean QE_SingleBrush (bool bQuiet = false); + + +// sys stuff +void Sys_MarkMapModified (void); + +/* +** QE Win32 function declarations +*/ +int QEW_SetupPixelFormat(HDC hDC, qboolean zbuffer ); +void QEW_StopGL( HWND hWnd, HGLRC hGLRC, HDC hDC ); + +/* +** extern declarations +*/ +extern QEGlobals_t g_qeglobals; + +enum VIEWTYPE {YZ, XZ, XY}; +qboolean IsBrushSelected(brush_t* bSel); + +// curve brushes + +void Curve_MakeCurvedBrush (qboolean negative, qboolean top, qboolean bottom, + qboolean s1, qboolean s2, qboolean s3, qboolean s4); + +void Curve_Invert (void); + +void Curve_AddFakePlanes( brush_t *B ); +void Curve_StripFakePlanes( brush_t *B ); +void Curve_BuildPoints (brush_t *b); +void Curve_XYDraw (brush_t *b); +void Curve_CameraDraw (brush_t *b); + +void Curve_WriteFile (char *name); + + +// patch stuff +brush_t* Patch_GenericMesh(int nWidth, int nHeight, int nOrientation = 2, bool bDeleteSource = true); +void Patch_ReadFile (const char *name); +void Patch_WriteFile (const char *name); +void Patch_BuildPoints (brush_t *b); +void Patch_Move(int n, const vec3_t vMove, bool bRebuild = false); +void Patch_ApplyMatrix(int n, const vec3_t vOrigin, const vec3_t vMatrix[3]); +void Patch_EditPatch(); +void Patch_Deselect(); +void Patch_Deselect(int n); +void Patch_Delete(int n); +void Patch_Select(int n); +void Patch_Scale(int n, const vec3_t vOrigin, const vec3_t vAmt, bool bRebuilt = true); +void Patch_Cleanup(); +void Patch_SetView(int n); +void Patch_SetTexture(int n, texdef_t *tex_def); +void Patch_BrushToMesh(bool bCone = false, bool bBevel = false, bool bEndcap = false, bool bSquare = false, int nHeight = 3); +bool Patch_DragScale(int n, vec3_t vAmt, vec3_t vMove); +void Patch_ReadBuffer(char* pBuff, bool bSelect = false); +void Patch_WriteFile (CMemFile* pMemFile); +void Patch_UpdateSelected(vec3_t vMove); +void Patch_AddRow(int n); +brush_t* Patch_Parse(bool bOld); +void Patch_Write (int n, FILE *f); +void Patch_Write (int n, CMemFile *file); +void Patch_AdjustColumns(int n, int nCols); +void Patch_AdjustRows(int n, int nRows); +void Patch_AdjustSelected(bool bInsert, bool bColumn, bool bFlag); +void Patch_RotateTexture(int n, float fAngle); +void Patch_ScaleTexture(int n, float fx, float fy, bool bFixup = true); +void Patch_ShiftTexture(int n, float fx, float fy); +void Patch_DrawCam(int n, bool bIsGhost); +void Patch_DrawXY(int n, bool bIsGhost); +void Patch_InsertColumn(int n, bool bAdd); +void Patch_InsertRow(int n, bool bAdd); +void Patch_RemoveRow(int n, bool bFirst); +void Patch_RemoveColumn(int n, bool bFirst); +void Patch_ToggleInverted(); +void Patch_Restore(int n); +void Patch_Save(int n); +void Patch_SetTextureInfo(texdef_t* pt); +void Patch_NaturalTexturing(); +void Patch_ResetTexturing(float fx, float fy); +void Patch_FitTexturing(); +void Patch_BendToggle(); +void Patch_StartInsDel(); +void Patch_BendHandleTAB(); +void Patch_BendHandleENTER(); +void Patch_SelectBendNormal(); +void Patch_SelectBendAxis(); +bool OnlyPatchesSelected(); +bool AnyPatchesSelected(); +void Patch_CapCurrent(bool bInvertedBevel = false, bool bInvertedEndcap = false); +void Patch_DisperseRows(); +void Patch_DisperseColumns(); +void Patch_NaturalizeSelected(bool bCap = false, bool bCycleCap = false); +void Patch_SelectAreaPoints(); +void Patch_InvertTexture(bool bY); +void Patch_InsDelToggle(); +void Patch_InsDelHandleTAB(); +void Patch_InsDelHandleENTER(); +void Patch_SetOverlays(); +void Patch_ClearOverlays(); +void Patch_Thicken(int nAmount, bool bSeam); +void Patch_Transpose(); +const char* Patch_GetTextureName(); +bool Patch_FindReplaceTexture(brush_t *pb, const char *pFind, const char *pReplace, bool bForce); +const char *Patch_FromBrush_GetTextureName(brush_t* pb); +void Patch_ReplaceQTexture(brush_t *pb, qtexture_t *pOld, qtexture_t *pNew); +void Patch_MakeNonSolid(brush_t* pb); +void Patch_MakeSolid(brush_t* pb); +void Select_SnapToGrid(); +extern bool g_bPatchShowBounds; +extern bool g_bPatchWireFrame; +extern bool g_bPatchWeld; +extern bool g_bPatchDrillDown; +extern bool g_bPatchInsertMode; +extern bool g_bPatchBendMode; +extern vec3_t g_vBendOrigin; + +#define UpdatePatchInspector() + +// some ent key/pair field names... +// +#define sKEYFIELD_AUTOBOUND "autobound" +#define sKEYFIELD_GROUPNAME "groupname" +#define sKEYFIELD_MODEL "model" +#define fTEXTURE_SCALE 0.25f + +#endif diff --git a/utils/Radiant/qedefs.h b/utils/Radiant/qedefs.h new file mode 100644 index 0000000..aceb552 --- /dev/null +++ b/utils/Radiant/qedefs.h @@ -0,0 +1,149 @@ +#ifndef __QEDEFS_H__ +#define __QEDEFS_H__ + +#define QE_VERSION 0x0501 + +#define QE3_STYLE (WS_OVERLAPPED | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_CHILD) +#define QE3_STYLE2 (WS_OVERLAPPED | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_MINIMIZEBOX | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU) +#define QE3_CHILDSTYLE (WS_OVERLAPPED | WS_MINIMIZEBOX | WS_THICKFRAME | WS_CAPTION | WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_MAXIMIZEBOX) + +#define QE3_SPLITTER_STYLE (WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS) + + + +#define QE_AUTOSAVE_INTERVAL 5 // number of minutes between autosaves + +#define _3DFXCAMERA_WINDOW_CLASS "Q3DFXCamera" +#define CAMERA_WINDOW_CLASS "QCamera" +#define XY_WINDOW_CLASS "QXY" +#define Z_WINDOW_CLASS "QZ" +#define ENT_WINDOW_CLASS "QENT" +#define TEXTURE_WINDOW_CLASS "QTEX" + +#define ZWIN_WIDTH 40 +#define CWIN_SIZE (0.4) + +#define MAX_EDGES 256 +#define MAX_POINTS 512 + +#define CMD_TEXTUREWAD 60000 +#define CMD_BSPCOMMAND 61000 + +#define PITCH 0 +#define YAW 1 +#define ROLL 2 + +#define QE_TIMER0 1 + +#define PLANE_X 0 +#define PLANE_Y 1 +#define PLANE_Z 2 +#define PLANE_ANYX 3 +#define PLANE_ANYY 4 +#define PLANE_ANYZ 5 + +#define ON_EPSILON 0.01 + +#define KEY_FORWARD 1 +#define KEY_BACK 2 +#define KEY_TURNLEFT 4 +#define KEY_TURNRIGHT 8 +#define KEY_LEFT 16 +#define KEY_RIGHT 32 +#define KEY_LOOKUP 64 +#define KEY_LOOKDOWN 128 +#define KEY_UP 256 +#define KEY_DOWN 512 + +// xy.c +#define EXCLUDE_LIGHTS 0x01 +#define EXCLUDE_ENT 0x02 +#define EXCLUDE_PATHS 0x04 +#define EXCLUDE_WATER 0x08 +#define EXCLUDE_WORLD 0x10 +#define EXCLUDE_CLIP 0x20 +#define EXCLUDE_DETAIL 0x40 +#define EXCLUDE_CURVES 0x80 +#define INCLUDE_EASY 0x100 +#define INCLUDE_NORMAL 0x200 +#define INCLUDE_HARD 0x400 +#define INCLUDE_DEATHMATCH 0x800 +#define EXCLUDE_HINT 0x1000 +#define EXCLUDE_WAYPOINTS 0x2000 +#define EXCLUDE_MISCMODELS 0x4000 +#define EXCLUDE_MISCMODELBREAKABLES 0x8000 +#define EXCLUDE_TARGETSPEAKERS 0x10000 +#define EXCLUDE_REFTAGS 0x20000 +#define EXCLUDE_MISCMODELXXXX 0x40000 +#define SHOW_GROUPNAMES 0x80000 // a bit misleading, this is used elsewhere than the show/hide menu, but I'm not sure of the side effects of adding to the QE globals structure (invalidate user prefs etc?) and I only need one bit, so... +#define SHOW_WAYPOINTS_ONLY 0x100000 // this IS used in view/show, but it's not a single exclude, nor an include in the normal sense +#define EXCLUDE_TRIGGERXXXX 0x200000 +#define EXCLUDE_NONSOLID 0x400000 +#define EXCLUDE_EASY 0x800000 +#define EXCLUDE_MEDIUM 0x1000000 +#define EXCLUDE_HARD 0x2000000 +#define EXCLUDE_ALL_EXCEPT_DETAIL 0x4000000 +#define EXCLUDE_FUNC_GROUP 0x8000000 +#define EXCLUDE_LIGHTS_RADII 0x10000000 +#define SHOW_CURVES_ONLY 0x20000000 + +// +// menu indexes for modifying menus +// +#define MENU_VIEW 2 +#define MENU_BSP 4 +#define MENU_TEXTURE 6 +#define MENU_PLUGIN 11 + + +// odd things not in windows header... +#define VK_COMMA 188 +#define VK_PERIOD 190 + +/* +** window bits +*/ +#define W_CAMERA 0x0001 +#define W_XY 0x0002 +#define W_XY_OVERLAY 0x0004 +#define W_Z 0x0008 +#define W_TEXTURE 0x0010 +#define W_Z_OVERLAY 0x0020 +#define W_CONSOLE 0x0040 +#define W_ENTITY 0x0080 +#define W_CAMERA_IFON 0x0100 +#define W_XZ 0x0200 //--| only used for patch vertex manip stuff +#define W_YZ 0x0400 //--| +#define W_ALL 0xFFFFFFFF + +#define COLOR_TEXTUREBACK 0 +#define COLOR_GRIDBACK 1 +#define COLOR_GRIDMINOR 2 +#define COLOR_GRIDMAJOR 3 +#define COLOR_CAMERABACK 4 +#define COLOR_ENTITY 5 +#define COLOR_GRIDBLOCK 6 +#define COLOR_GRIDTEXT 7 +#define COLOR_BRUSHES 8 +#define COLOR_SELBRUSHES 9 +#define COLOR_CLIPPER 10 +#define COLOR_VIEWNAME 11 +#define COLOR_LAST 12 + +// classes +#define ENTITY_WIREFRAME 0x00001 +#define ENTITY_SKIN_MODEL 0x00010 +#define ENTITY_SELECTED_ONLY 0x00100 +#define ENTITY_BOXED 0x01000 + +// menu settings +#define ENTITY_BOX 0x01000 +#define ENTITY_WIRE 0x00001 +#define ENTITY_SELECTED 0x00101 +#define ENTITY_SKINNED 0x00010 +#define ENTITY_SKINNED_BOXED 0x01010 +#define ENTITY_SELECTED_SKIN 0x00110 + + + +#endif diff --git a/utils/Radiant/qerplugin.h b/utils/Radiant/qerplugin.h new file mode 100644 index 0000000..2207521 --- /dev/null +++ b/utils/Radiant/qerplugin.h @@ -0,0 +1,406 @@ +// QERadiant PlugIns +// +// +#include +#include "qertypes.h" + +#define QER_PLUG_VERSION_1 1.00 +#define QER_PLUG_VERSION 1.50 + +#define QER_MAX_NAMELEN 1024 + +// the editor will look for plugins in two places, the plugins path +// under the application path, and the path under the basepath as defined +// in the project (.qe4) file. +// +// you can drop any number of new texture, model format DLL's in the standard plugin path +// but only one plugin that overrides map loading/saving, surface dialog, surface flags, etc.. +// should be used at one time.. if multiples are loaded then the last one loaded will be the +// active one +// +// type of services the plugin supplies, pass any combo of these flags +// it is assumed the plugin will have a matching function as defined below +// to correlate to the implied functionality +// +// FIXME: after specing this crap i went to a simpler model so this may disappear +#define QER_PLUG_GAME_TEXTURE 0x0001 // defines new texture format +#define QER_PLUG_GAME_MODEL 0x0002 // defines new model format +#define QER_PLUG_GAME_MAP 0x0004 // handles map load/save +#define QER_PLUG_GAME_SURFACEDLG 0x0008 // handles surface dialog +#define QER_PLUG_GAME_SURFACEFLAGS 0x0010 // renames surface/content names + +// basics +#define QERPLUG_INIT "QERPlug_Init" +#define QERPLUG_GETNAME "QERPlug_GetName" +#define QERPLUG_GETCOMMANDLIST "QERPlug_GetCommandList" +#define QERPLUG_DISPATCH "QERPlug_Dispatch" +#define QERPLUG_GETFUNCTABLE "QERPlug_GetFuncTable" + +// FIXME: not used, probably should remove +#define QERPLUG_GETSERVICETPE "QERPlug_GetServiceType" + +// game stuff +#define QERPLUG_GETTEXTUREINFO "QERPlug_GetTextureInfo" // gets a texture info structure +#define QERPLUG_LOADTEXTURE "QERPlug_LoadTexture" // loads a texture, will return an RGBA structure + // and any surface flags/contents for it +#define QERPLUG_GETTMODELINFO "QERPlug_GetModelInfo" // gets a model info structure +#define QERPLUG_LOADMODEL "QERPlug_LoadModel" // loads model data from a plugin +#define QERPLUG_DOSURFACEDLG "QERPlug_DoSurfaceDlg" // runs the surface dialog in a plugin + // this is going to get icky +#define QERPLUG_GETSURFACEFLAGS "QERPlug_GetSurfaceFlags" // gets a list of surface/content flag names from a plugin + +struct _QERTextureInfo +{ + char m_TextureExtension[QER_MAX_NAMELEN]; // the extension these textures have + bool m_bHiColor; // if textures are NOT high color, the default + // palette (as described inthe qe4 file will be used for gamma correction) + // if they are high color, gamma and shading are computed on the fly + // based on the rgba data + //--bool m_bIsShader; // will probably do q3 shaders this way when i merge + bool m_bWadStyle; // if this is true, the plugin will be presented with the texture path + // defined in the .qe4 file and is expected to preload all the textures + bool m_bHalfLife; // causes brushes to be saved/parsed without the surface contents/flags/value +}; + +struct _QERTextureLoad // returned by a plugin +{ + _QERTextureLoad() + { + memset(reinterpret_cast(this), 0, sizeof(_QERTextureLoad)); + }; + + ~_QERTextureLoad() + { + delete []m_pRGBA; + delete []m_pName; + }; + + void makeSpace(int nSize) + { + m_pRGBA = new unsigned char[nSize+1]; + }; + + void setName(const char* p) + { + m_pName = new char[strlen(p)+1]; + strcpy(m_pName, p); + }; + + + unsigned char *m_pRGBA; // rgba data (alpha channel is supported and drawn appropriately) + int m_nWidth; // width + int m_nHeight; // height + int m_nContents; // default contents + int m_nFlags; // "" flags + int m_nValue; // "" value + char *m_pName; // name to be referenced in map, build tools, etc. +}; + +struct _QERModelInfo +{ + char m_ModelExtension[QER_MAX_NAMELEN]; + bool m_bSkinned; + bool m_bMultipart; +}; + +struct _QERModelLoad +{ + // vertex and skin data +}; + + +// hook functions +// FIXME: none of the hook stuff works for v1.00 +#define QERPLUG_MAPLOAD "QERPlug_MapLoad" +#define QERPLUG_MAPSAVE "QERPlug_MapSave" + + +//========================================= +// plugin functions version 1.0 +typedef LPCSTR (WINAPI* PFN_QERPLUG_INIT)(HMODULE hApp, HWND hwndMain); +typedef LPCSTR (WINAPI* PFN_QERPLUG_GETNAME)(); +typedef LPCSTR (WINAPI* PFN_QERPLUG_GETCOMMANDLIST)(); +typedef void (WINAPI* PFN_QERPLUG_DISPATCH)(LPCSTR p, vec3_t vMin, vec3_t vMax, BOOL bSingleBrush); +typedef LPVOID (WINAPI* PFN_QERPLUG_GETFUNCTABLE)(); +typedef void (WINAPI* PFN_QERPLUG_MAPLOAD)(); +typedef void (WINAPI* PFN_QERPLUG_MAPSAVE)(); + +// editor defined plugin dispatches +// these are used to signal the completion after a 'Get' function is called in the editor +#define QERPLUG_DISPATCH_POINTDONE "PointDone" +#define QERPLUG_DISPATCH_BRUSHDONE "BrushDone" + +// v1.5 +// +// Texture loading +// returns a ptr to _QERTextureInfo +typedef LPVOID (WINAPI* PFN_QERPLUG_GETTEXTUREINFO)(); +// +// loads a texture by calling the texture load func in the editor (defined below) +// transparency (for water, fog, lava, etc.. ) can be emulated in the editor +// by passing in appropriate alpha data or by setting the appropriate surface flags +// expected by q2 (which the editor will use.. ) +typedef void (WINAPI* PFN_QERPLUG_LOADTEXTURE)(LPCSTR pFilename); + +// v1.6 +typedef LPVOID (WINAPI* PFN_QERPLUG_GETSURFACEFLAGS)(); + + + + +//========================================= +// editor functions + +// There are 3 potential brush handle lists +// 1. the list that contains brushes a plugin creates using CreateBrushHandle +// 2. the selected brush list (brushes the user has selected) +// 3. the active brush list (brushes in the map that are not selected) +// +// In general, the same things can be done to brush handles (face manip, delete brushhandle, etc.. ) in each +// list. There are a few exceptions. +// 1. You cannot commit a selected or active brush handle to the map. This is because it is already in the map. +// 2. You cannot bind brush handles from the selected or active brush list to an entity. As of v1.0 of the plugins +// the only way for a plugin to create entities is to create a brush handles (or a list of handles) and then bind +// them to an entity. This will commit the brush(s) and/or the entities to the map as well. +// +// To use the active or selected brush lists, you must first allocate them (which returns a count) and then +// release them when you are finish manipulating brushes in one of those lists. + + +// brush manipulation routines +#define QERAPP_CREATEBRUSH "QERApp_CreateBrush" +#define QERAPP_CREATEBRUSHHANDLE "QERApp_CreateBrushHandle" +#define QERAPP_DELETEBRUSHHANDLE "QERApp_DeleteBrushHandle" +#define QERAPP_COMMITBRUSHHANDLETOMAP "QERApp_CommitBrushHandleToMap" +#define QERAPP_BINDHANDLESTOENTITY "QERApp_BindHandlesToEntity" +#define QERAPP_ADDFACE "QERApp_AddFace" +#define QERAPP_ADDFACEDATA "QERApp_AddFaceData" +#define QERAPP_GETFACECOUNT "QERApp_GetFaceCount" +#define QERAPP_GETFACEDATA "QERApp_GetFaceData" +#define QERAPP_SETFACEDATA "QERApp_SetFaceData" +#define QERAPP_DELETEFACE "QERApp_DeleteFace" +#define QERAPP_TEXTUREBRUSH "QERApp_TextureBrush" +#define QERAPP_BUILDBRUSH "QERApp_BuildBrush" // PGM +#define QERAPP_SELECTEDBRUSHCOUNT "QERApp_SelectedBrushCount" +#define QERAPP_ALLOCATESELECTEDBRUSHHANDLES "QERApp_AllocateSelectedBrushHandles" +#define QERAPP_RELEASESELECTEDBRUSHHANDLES "QERApp_ReleaseSelectedBrushHandles" +#define QERAPP_GETSELECTEDBRUSHHANDLE "QERApp_GetSelectedBrushHandle" +#define QERAPP_ACTIVEBRUSHCOUNT "QERApp_ActiveBrushCount" +#define QERAPP_ALLOCATEACTIVEBRUSHHANDLES "QERApp_AllocateActiveBrushHandles" +#define QERAPP_RELEASEACTIVEBRUSHHANDLES "QERApp_ReleaseActiveBrushHandles" +#define QERAPP_GETACTIVEBRUSHHANDLE "QERApp_GetActiveBrushHandle" + +// texture stuff +#define QERAPP_TEXTURECOUNT "QERApp_TextureCount" +#define QERAPP_GETTEXTURE "QERApp_GetTexture" +#define QERAPP_GETCURRENTTEXTURE "QERApp_GetCurrentTexture" +#define QERAPP_SETCURRENTTEXTURE "QERApp_SetCurrentTexture" + +// selection +#define QERAPP_DELETESELECTION "QERApp_DeleteSelection" +#define QERAPP_SELECTBRUSH "QERApp_SelectBrush" // PGM +#define QERAPP_DESELECTBRUSH "QERApp_DeselectBrush" // PGM +#define QERAPP_DESELECTALLBRUSHES "QERApp_DeselectAllBrushes" // PGM + +// data gathering +#define QERAPP_GETPOINTS "QERApp_GetPoints" +#define QERAPP_SELECTBRUSHES "QERApp_GetBrushes" + +// entity class stuff +// the entity handling is very basic for 1.0 +#define QERAPP_GETECLASSCOUNT "QERApp_GetEClassCount" +#define QERAPP_GETECLASS "QERApp_GetEClass" + +// misc +#define QERAPP_SYSMSG "QERApp_SysMsg" +#define QERAPP_INFOMSG "QERApp_InfoMsg" +#define QERAPP_HIDEINFOMSG "QERApp_HideInfoMsg" +#define QERAPP_POSITIONVIEW "QERApp_PositionView" // PGM +#define QERAPP_RESET_PLUGINS "QERApp_ResetPlugins" + +// texture loading +#define QERAPP_LOADTEXTURERGBA "QERApp_LoadTextureRGBA" + +// FIXME: the following are not implemented yet +// hook registrations +#define QERAPP_REGISTER_MAPLOADFUNC "QERApp_Register_MapLoadFunc" +#define QERAPP_REGISTER_MAPSAVEFUNC "QERApp_Register_MapSaveFunc" + +// FIXME: the following are not implemented yet +#define QERAPP_REGISTER_PROJECTLOADFUNC "QERApp_Register_ProjectLoadFunc" +#define QERAPP_REGISTER_MOUSEHANDLER "QERApp_Register_MouseHandler" +#define QERAPP_REGISTER_KEYHANDLER "QERApp_Register_KeyHandler" + +// FIXME: new primtives do not work in v1.00 +// primitives are new types of things in the map +// for instance, the Q3 curves could have been done as +// primitives instead of being built in +// it will be a plugins responsibility to hook the map load and save funcs to load +// and/or save any additional data (like new primitives of some type) +// the editor will call each registered renderer during the rendering process to repaint +// any primitives the plugin owns +// each primitive object has a temporary sibling brush that lives in the map +// FIXME: go backwards on this a bit.. orient it more towards the temp brush mode as it will be cleaner +// basically a plugin will hook the map load and save and will add the primitives to the map.. this will +// produce a temporary 'primitive' brush and the appropriate renderer will be called as well as the +// edit handler (for edge drags, sizes, rotates, etc.. ) and the vertex maker will be called when vertex +// mode is attemped on the brush.. there will need to be a GetPrimitiveBounds callback in the edit handler +// so the brush can resize appropriately as needed.. this might be the plugins responsibility to set the +// sibling brushes size.. it will then be the plugins responsibility to hook map save to save the primitives +// as the editor will discard any temp primitive brushes.. (there probably needs to be some kind of sanity check +// here as far as keeping the brushes and the plugin in sync.. i suppose the edit handler can deal with all of that +// crap but it looks like a nice place for a mess) +#define QERAPP_REGISTER_PRIMITIVE "QERApp_Register_Primitive" +#define QERAPP_REGISTER_RENDERER "QERApp_Register_Renderer" +#define QERAPP_REGISTER_EDITHANDLER "QERApp_Register_EditHandler" +#define QERAPP_REGISTER_VERTEXMAKER "QERApp_Register_VertexMaker" +#define QERAPP_ADDPRIMITIVE "QERApp_AddPrimitive" + +#define QERAPP_GETENTITYCOUNT "QERApp_GetEntityCount" +#define QERAPP_GETENTITYHANDLE "QERApp_GetEntityHandle" +#define QERAPP_GETENTITYINFO "QERApp_GetEntityInfo" +#define QERAPP_GETKEYVALLIST "QERApp_GetKeyValList" +#define QERAPP_SETKEYVALLIST "QERApp_SetKeyValList" +#define QERAPP_ALLOCATEENTITYBRUSHHANDLES "QERApp_AllocateEntityBrushHandles" +#define QERAPP_RELEASEENTITYBRUSHHANDLES "QERApp_ReleaseEntityBrushHandles" +#define QERAPP_GETENTITYBRUSHHANDLE "QERApp_GetEntityBrushHandle" + + +struct _QERPointData +{ + int m_nCount; + vec3_t *m_pVectors; +}; + +struct _QERFaceData +{ + char m_TextureName[QER_MAX_NAMELEN]; + int m_nContents; + int m_nFlags; + int m_nValue; + float m_fShift[2]; + float m_fRotate; + float m_fScale[2]; + vec3_t m_v1, m_v2, m_v3; +}; + + +typedef void (WINAPI* PFN_QERAPP_CREATEBRUSH)(vec3_t vMin, vec3_t vMax); + +typedef LPVOID (WINAPI* PFN_QERAPP_CREATEBRUSHHANDLE)(); +typedef void (WINAPI* PFN_QERAPP_DELETEBRUSHHANDLE)(LPVOID pv); +typedef void (WINAPI* PFN_QERAPP_COMMITBRUSHHANDLETOMAP)(LPVOID pv); +typedef void (WINAPI* PFN_QERAPP_ADDFACE)(LPVOID pv, vec3_t v1, vec3_t v2, vec3_t v3); + +typedef void (WINAPI* PFN_QERAPP_ADDFACEDATA)(LPVOID pv, _QERFaceData *pData); +typedef int (WINAPI* PFN_QERAPP_GETFACECOUNT)(LPVOID pv); +typedef _QERFaceData* (WINAPI* PFN_QERAPP_GETFACEDATA)(LPVOID pv, int nFaceIndex); +typedef void (WINAPI* PFN_QERAPP_SETFACEDATA)(LPVOID pv, int nFaceIndex, _QERFaceData *pData); +typedef void (WINAPI* PFN_QERAPP_DELETEFACE)(LPVOID pv, int nFaceIndex); +typedef void (WINAPI* PFN_QERAPP_TEXTUREBRUSH)(LPVOID pv, LPCSTR pName); +typedef void (WINAPI* PFN_QERAPP_BUILDBRUSH)(LPVOID pv); // PGM +typedef void (WINAPI* PFN_QERAPP_SELECTBRUSH)(LPVOID pv); // PGM +typedef void (WINAPI* PFN_QERAPP_DESELECTBRUSH)(LPVOID pv); // PGM +typedef void (WINAPI* PFN_QERAPP_DESELECTALLBRUSHES)(); // PGM + +typedef void (WINAPI* PFN_QERAPP_DELETESELECTION)(); +typedef void (WINAPI* PFN_QERAPP_GETPOINTS)(int nMax, _QERPointData *pData, LPCSTR pMsg); +typedef void (WINAPI* PFN_QERAPP_SYSMSG)(LPCSTR pMsg); +typedef void (WINAPI* PFN_QERAPP_INFOMSG)(LPCSTR pMsg); +typedef void (WINAPI* PFN_QERAPP_HIDEINFOMSG)(); +typedef void (WINAPI* PFN_QERAPP_POSITIONVIEW)(vec3_t v1, vec3_t v2); //PGM + +typedef int (WINAPI* PFN_QERAPP_SELECTEDBRUSHCOUNT)(); +typedef int (WINAPI* PFN_QERAPP_ALLOCATESELECTEDBRUSHHANDLES)(); +typedef void (WINAPI* PFN_QERAPP_RELEASESELECTEDBRUSHHANDLES)(); +typedef LPVOID (WINAPI* PFN_QERAPP_GETSELECTEDBRUSHHANDLE)(int nIndex); + +typedef int (WINAPI* PFN_QERAPP_ACTIVEBRUSHCOUNT)(); +typedef int (WINAPI* PFN_QERAPP_ALLOCATEACTIVEBRUSHHANDLES)(); +typedef void (WINAPI* PFN_QERAPP_RELEASEACTIVEBRUSHHANDLES)(); +typedef LPVOID (WINAPI* PFN_QERAPP_GETACTIVEBRUSHHANDLE)(int nIndex); + +typedef int (WINAPI* PFN_QERAPP_TEXTURECOUNT)(); +typedef LPCSTR (WINAPI* PFN_QERAPP_GETTEXTURE)(int nIndex); +typedef LPCSTR (WINAPI* PFN_QERAPP_GETCURRENTTEXTURE)(); +typedef void (WINAPI* PFN_QERAPP_SETCURRENTTEXTURE)(LPCSTR pName); + +typedef void (WINAPI* PFN_QERAPP_REGISTERMAPLOAD)(LPVOID vp); +typedef void (WINAPI* PFN_QERAPP_REGISTERMAPSAVE)(LPVOID vp); + +typedef int (WINAPI* PFN_QERAPP_GETECLASSCOUNT)(); +typedef LPCSTR (WINAPI* PFN_QERAPP_GETECLASS)(int nIndex); + +typedef void (WINAPI* PFN_QERAPP_RESETPLUGINS)(); +//--typedef int (WINAPI* PFN_QERAPP_GETENTITYCOUNT)(); + + +// called by texture loaders for each texture loaded +typedef void (WINAPI* PFN_QERAPP_LOADTEXTURERGBA)(LPVOID vp); + +//--typedef LPCSTR (WINAPI* PFN_QERAPP_GETENTITY)(int nIndex); + + +// FIXME: +// add map format extensions +// add texture format handlers +// add surface dialog handler +// add model handler/displayer + +// v1 func table +// Plugins need to declare one of these and implement the getfunctable as described above +struct _QERFuncTable_1 +{ + float m_fVersion; + int m_nSize; + PFN_QERAPP_CREATEBRUSH m_pfnCreateBrush; + PFN_QERAPP_CREATEBRUSHHANDLE m_pfnCreateBrushHandle; + PFN_QERAPP_DELETEBRUSHHANDLE m_pfnDeleteBrushHandle; + PFN_QERAPP_COMMITBRUSHHANDLETOMAP m_pfnCommitBrushHandle; + PFN_QERAPP_ADDFACE m_pfnAddFace; + PFN_QERAPP_ADDFACEDATA m_pfnAddFaceData; + PFN_QERAPP_GETFACEDATA m_pfnGetFaceData; + PFN_QERAPP_GETFACECOUNT m_pfnGetFaceCount; + PFN_QERAPP_SETFACEDATA m_pfnSetFaceData; + PFN_QERAPP_DELETEFACE m_pfnDeleteFace; + PFN_QERAPP_TEXTUREBRUSH m_pfnTextureBrush; + PFN_QERAPP_BUILDBRUSH m_pfnBuildBrush; // PGM + PFN_QERAPP_SELECTBRUSH m_pfnSelectBrush; // PGM + PFN_QERAPP_DESELECTBRUSH m_pfnDeselectBrush; // PGM + PFN_QERAPP_DESELECTALLBRUSHES m_pfnDeselectAllBrushes; // PGM + + PFN_QERAPP_DELETESELECTION m_pfnDeleteSelection; + PFN_QERAPP_GETPOINTS m_pfnGetPoints; + PFN_QERAPP_SYSMSG m_pfnSysMsg; + PFN_QERAPP_INFOMSG m_pfnInfoMsg; + PFN_QERAPP_HIDEINFOMSG m_pfnHideInfoMsg; + PFN_QERAPP_POSITIONVIEW m_pfnPositionView; // PGM + + PFN_QERAPP_SELECTEDBRUSHCOUNT m_pfnSelectedBrushCount; + PFN_QERAPP_ALLOCATESELECTEDBRUSHHANDLES m_pfnAllocateSelectedBrushHandles; + PFN_QERAPP_RELEASESELECTEDBRUSHHANDLES m_pfnReleaseSelectedBrushHandles; + PFN_QERAPP_GETSELECTEDBRUSHHANDLE m_pfnGetSelectedBrushHandle; + + PFN_QERAPP_ACTIVEBRUSHCOUNT m_pfnActiveBrushCount; + PFN_QERAPP_ALLOCATEACTIVEBRUSHHANDLES m_pfnAllocateActiveBrushHandles; + PFN_QERAPP_RELEASEACTIVEBRUSHHANDLES m_pfnReleaseActiveBrushHandles; + PFN_QERAPP_GETACTIVEBRUSHHANDLE m_pfnGetActiveBrushHandle; + + PFN_QERAPP_TEXTURECOUNT m_pfnTextureCount; + PFN_QERAPP_GETTEXTURE m_pfnGetTexture; + PFN_QERAPP_GETCURRENTTEXTURE m_pfnGetCurrentTexture; + PFN_QERAPP_SETCURRENTTEXTURE m_pfnSetCurrentTexture; + + PFN_QERAPP_GETECLASSCOUNT m_pfnGetEClassCount; + PFN_QERAPP_GETECLASS m_pfnGetEClass; + PFN_QERAPP_RESETPLUGINS m_pfnResetPlugins; + // v1.00 ends here + // v1.50 starts here + PFN_QERAPP_LOADTEXTURERGBA m_pfnLoadTextureRGBA; + // v1.50 ends here +}; + + +//--typedef int (WINAPI* PFN_QERAPP_BRUSHCOUNT )(); +//--typedef brush_t* (WINAPI* PFN_QERAPP_GETBRUSH )(); +//--typedef void (WINAPI* PFN_QERAPP_DELETEBRUSH )(); diff --git a/utils/Radiant/qertypes.h b/utils/Radiant/qertypes.h new file mode 100644 index 0000000..bd8e2d4 --- /dev/null +++ b/utils/Radiant/qertypes.h @@ -0,0 +1,191 @@ +// qertypes.h +// +// common types +// merged from brush.h, etc. for plugin support +// +#ifndef _QERTYPE_H +#define _QERTYPE_H + +#ifndef __BYTEBOOL__ +#define __BYTEBOOL__ +typedef boolean qboolean; +//typedef unsigned char byte; +#endif + +#define MAXPOINTS 16 + +typedef float vec_t; +typedef vec_t vec3_t[3]; +typedef vec_t vec4_t[4]; + +typedef struct texdef_s +{ + char name[128]; + float shift[2]; + float rotate; + float scale[2]; + int contents; + int flags; + int value; + float lighting[4]; +} texdef_t; + +typedef struct +{ + int width, height; + int originy; + texdef_t texdef; + int m_nTotalHeight; +} texturewin_t; + +#define QER_TRANS 0x00000001 +#define QER_NOCARVE 0x00000002 + +typedef struct qtexture_s +{ + struct qtexture_s *next; + char name[64]; // includes partial directory and extension + + FILETIME ft; // these 3 are for smart-replace filetime code + char sFTName[MAX_PATH]; // NOT MAX_QPATH. testing first char for NZ = validity (qmalloc does memset() 0) + + int width, height; + int contents; + int flags; + int value; + int texture_number; // gl bind number + + char shadername[1024]; // old shader stuff + qboolean bFromShader; // created from a shader + float fTrans; // amount of transparency + int nShaderFlags; // qer_ shader flags + vec3_t color; // for flat shade mode + qboolean inuse; // true = is present on the level + + bool bForArchitecture; // false until any world stuff requests this texture (used for filtering model textures out of available shader list) + bool bPlaceHolder; // not a real texture yet, still needs loading for real + +} qtexture_t; + + +typedef struct +{ + int numpoints; + int maxpoints; + float points[8][5]; // variable sized +} winding_t; + +typedef struct +{ + vec3_t normal; + double dist; + int type; +} plane_t; + +typedef struct face_s +{ + struct face_s *next; + vec3_t planepts[3]; + texdef_t texdef; + plane_t plane; + + winding_t *face_winding; + + vec3_t d_color; + qtexture_t *d_texture; + +} face_t; + +typedef struct { + vec3_t xyz; + float sideST[2]; + float capST[2]; +} curveVertex_t; + +typedef struct { + curveVertex_t v[2]; +} sideVertex_t; + +typedef struct brush_s +{ + struct brush_s *prev, *next; // links in active/selected + struct brush_s *oprev, *onext; // links in entity + struct entity_s *owner; + vec3_t mins, maxs; + face_t *brush_faces; + + qboolean bModelFailed; + // + // curve brush extensions + // all are derived from brush_faces + qboolean curveBrush; + qboolean patchBrush; + qboolean hiddenBrush; + + int nPatchID; +} brush_t; + + +#ifdef SOF + #define MAX_FLAGS 21 + #define COLUMN_SIZE 7 + #define NOT_FLAGS_START 8 + #define NOT_FLAGS_SIZE 5 +#else + #define MAX_FLAGS 12 //8 8, plus 3 fixed, plus one extra instead of !deathmatch + #define COLUMN_SIZE 4 + #define NOT_FLAGS_START 8 //0 + #define NOT_FLAGS_SIZE 3 //0 +#endif + + +typedef struct trimodel_t +{ + vec3_t v[3]; + float st[3][2]; +} trimodel; + +typedef struct entitymodel_t +{ + entitymodel_t *pNext; + int nTriCount; + trimodel *pTriList; + int nTextureBind; + int nSkinWidth; + int nSkinHeight; + int nModelPosition; +} entitymodel; + + +typedef struct eclass_s +{ + struct eclass_s *next; + char *name; + qboolean fixedsize; + qboolean unknown; // wasn't found in source + vec3_t mins, maxs; + vec3_t color; + texdef_t texdef; + char *comments; + char flagnames[MAX_FLAGS][32]; + +/* + int nTriCount; + trimodel *pTriList; + int nTextureBind; + int nSkinWidth, nSkinHeight; +*/ + entitymodel *model; + char *modelpath; + char *skinpath; + int nFrame; + + // new fields that I auto-stuff in via new QUAKED-type directives -ste + // + char *psQuakEd_MODELNAME; + +} eclass_t; + +extern eclass_t *eclass; + +#endif \ No newline at end of file diff --git a/utils/Radiant/qfiles.h b/utils/Radiant/qfiles.h new file mode 100644 index 0000000..9ec1a8c --- /dev/null +++ b/utils/Radiant/qfiles.h @@ -0,0 +1,474 @@ + +// +// qfiles.h: quake file formats +// This file must be identical in the quake and utils directories +// + +/* +======================================================================== + +.MD2 triangle model file format + +======================================================================== +*/ + +#define IDALIASHEADER (('2'<<24)+('P'<<16)+('D'<<8)+'I') +#define ALIAS_VERSION 8 + +#define MAX_TRIANGLES 4096 +#define MAX_VERTS 2048 +#define MAX_FRAMES 512 +#define MAX_MD2SKINS 32 +#define MAX_SKINNAME 64 + +typedef struct +{ + short s; + short t; +} dstvert_t; + +typedef struct +{ + short index_xyz[3]; + short index_st[3]; +} dtriangle_t; + +typedef struct +{ + byte v[3]; // scaled byte to fit in frame mins/maxs + byte lightnormalindex; +} dtrivertx_t; + +typedef struct +{ + float scale[3]; // multiply byte verts by this + float translate[3]; // then add this + char name[16]; // frame name from grabbing + dtrivertx_t verts[1]; // variable sized +} daliasframe_t; + + +// the glcmd format: +// a positive integer starts a tristrip command, followed by that many +// vertex structures. +// a negative integer starts a trifan command, followed by -x vertexes +// a zero indicates the end of the command list. +// a vertex consists of a floating point s, a floating point t, +// and an integer vertex index. + + +typedef struct +{ + int ident; + int version; + + int skinwidth; + int skinheight; + int framesize; // byte size of each frame + + int num_skins; + int num_xyz; + int num_st; // greater than num_xyz for seams + int num_tris; + int num_glcmds; // dwords in strip/fan command list + int num_frames; + + int ofs_skins; // each skin is a MAX_SKINNAME string + int ofs_st; // byte offset from start for stverts + int ofs_tris; // offset for dtriangles + int ofs_frames; // offset for first frame + int ofs_glcmds; + int ofs_end; // end of file + +} dmdl_t; + +#define MD3_IDENT (('3'<<24)+('P'<<16)+('D'<<8)+'I') +#define MAX_QPATH 64 // max length of a quake game pathname +#define MD3_XYZ_SCALE (1.0/64) + +typedef struct { + int ident; + int version; + + char name[MAX_QPATH]; // model name + + int flags; + + int numFrames; + int numTags; + int numSurfaces; + + int numSkins; + + int ofsFrames; // offset for first frame + int ofsTags; // numFrames * numTags + int ofsSurfaces; // first surface, others follow + + int ofsEnd; // end of file +} md3Header_t; + +typedef struct { + int ident; // + + char name[MAX_QPATH]; // polyset name + + int flags; + int numFrames; // all surfaces in a model should have the same + + int numShaders; // all surfaces in a model should have the same + int numVerts; + + int numTriangles; + int ofsTriangles; + + int ofsShaders; // offset from start of md3Surface_t + int ofsSt; // texture coords are common for all frames + int ofsXyzNormals; // numVerts * numFrames + + int ofsEnd; // next surface follows + +} md3Surface_t; + +typedef struct { + char name[MAX_QPATH]; + int shaderIndex; // for in-game use +} md3Shader_t; + +typedef struct { + int indexes[3]; +} md3Triangle_t; + +typedef struct { + float st[2]; +} md3St_t; + +typedef struct { + short xyz[3]; + short normal; +} md3XyzNormal_t; + + +typedef struct +{ + float st[2]; + int nVertIndex; +} glst_t; + +typedef struct +{ + int nCount; + int ObjectIndex; + glst_t GlSt; +} gl_t; + +/* +======================================================================== + +.SP2 sprite file format + +======================================================================== +*/ + +#define IDSPRITEHEADER (('2'<<24)+('S'<<16)+('D'<<8)+'I') + // little-endian "IDS2" +#define SPRITE_VERSION 2 + +typedef struct +{ + int width, height; + int origin_x, origin_y; // raster coordinates inside pic + char name[MAX_SKINNAME]; // name of pcx file +} dsprframe_t; + +typedef struct { + int ident; + int version; + int numframes; + dsprframe_t frames[1]; // variable sized +} dsprite_t; + +/* +============================================================================== + + .WAL texture file format + +============================================================================== +*/ + + +#define MIPLEVELS 4 +typedef struct miptex_s +{ + char name[32]; + unsigned width, height; + unsigned offsets[MIPLEVELS]; // four mip maps stored + char animname[32]; // next frame in animation chain + int flags; + int contents; + int value; +} miptex_t; + + + +/* +============================================================================== + + .BSP file format + +============================================================================== +*/ + +#define IDBSPHEADER (('P'<<24)+('S'<<16)+('B'<<8)+'I') + // little-endian "IBSP" + +#define BSPVERSION 36 + + +// upper design bounds +// leaffaces, leafbrushes, planes, and verts are still bounded by +// 16 bit short limits +#define MAX_MAP_MODELS 1024 +#define MAX_MAP_BRUSHES 8192 +#define MAX_MAP_ENTITIES 2048 +#define MAX_MAP_ENTSTRING 0x20000 +#define MAX_MAP_TEXINFO 8192 + +#define MAX_MAP_PLANES 65536 +#define MAX_MAP_NODES 65536 +#define MAX_MAP_BRUSHSIDES 65536 +#define MAX_MAP_LEAFS 65536 +#define MAX_MAP_VERTS 65536 +#define MAX_MAP_FACES 65536 +#define MAX_MAP_LEAFFACES 65536 +#define MAX_MAP_LEAFBRUSHES 65536 +#define MAX_MAP_PORTALS 65536 +#define MAX_MAP_EDGES 128000 +#define MAX_MAP_SURFEDGES 256000 +#define MAX_MAP_LIGHTING 0x200000 +#define MAX_MAP_VISIBILITY 0x100000 + +// key / value pair sizes + +#define MAX_KEY 32 +#define MAX_VALUE 1024 + +//============================================================================= + +typedef struct +{ + int fileofs, filelen; +} lump_t; + +#define LUMP_ENTITIES 0 +#define LUMP_PLANES 1 +#define LUMP_VERTEXES 2 +#define LUMP_VISIBILITY 3 +#define LUMP_NODES 4 +#define LUMP_TEXINFO 5 +#define LUMP_FACES 6 +#define LUMP_LIGHTING 7 +#define LUMP_LEAFS 8 +#define LUMP_LEAFFACES 9 +#define LUMP_LEAFBRUSHES 10 +#define LUMP_EDGES 11 +#define LUMP_SURFEDGES 12 +#define LUMP_MODELS 13 +#define LUMP_BRUSHES 14 +#define LUMP_BRUSHSIDES 15 +#define LUMP_POP 16 + +#define HEADER_LUMPS 17 + +typedef struct +{ + int ident; + int version; + lump_t lumps[HEADER_LUMPS]; +} dheader_t; + +typedef struct +{ + float mins[3], maxs[3]; + float origin[3]; // for sounds or lights + int headnode; + int firstface, numfaces; // submodels just draw faces + // without walking the bsp tree +} dmodel_t; + + +typedef struct +{ + float point[3]; +} dvertex_t; + + +// 0-2 are axial planes +#define PLANE_X 0 +#define PLANE_Y 1 +#define PLANE_Z 2 + +// 3-5 are non-axial planes snapped to the nearest +#define PLANE_ANYX 3 +#define PLANE_ANYY 4 +#define PLANE_ANYZ 5 + +// planes (x&~1) and (x&~1)+1 are allways opposites + +typedef struct +{ + float normal[3]; + float dist; + int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate +} dplane_t; + + +// contents flags are seperate bits +// a given brush can contribute multiple content bits +// multiple brushes can be in a single leaf + +// lower bits are stronger, and will eat weaker brushes completely +#define CONTENTS_SOLID 1 // an eye is never valid in a solid +#define CONTENTS_WINDOW 2 // translucent, but not watery +#define CONTENTS_AUX 4 +#define CONTENTS_LAVA 8 +#define CONTENTS_SLIME 16 +#define CONTENTS_WATER 32 +#define CONTENTS_MIST 64 +#define LAST_VISIBLE_CONTENTS 64 + +// remaining contents are non-visible, and don't eat brushes +#define CONTENTS_PLAYERCLIP 0x10000 +#define CONTENTS_MONSTERCLIP 0x20000 + +// currents can be added to any other contents, and may be mixed +#define CONTENTS_CURRENT_0 0x40000 +#define CONTENTS_CURRENT_90 0x80000 +#define CONTENTS_CURRENT_180 0x100000 +#define CONTENTS_CURRENT_270 0x200000 +#define CONTENTS_CURRENT_UP 0x400000 +#define CONTENTS_CURRENT_DOWN 0x800000 + +#define CONTENTS_ORIGIN 0x1000000 // removed before bsping an entity + +#define CONTENTS_MONSTER 0x2000000 // should never be on a brush, only in game +#define CONTENTS_DEADMONSTER 0x4000000 // corpse +#define CONTENTS_DETAIL 0x8000000 // brushes to be added after vis leafs +#define CONTENTS_TRANSLUCENT 0x10000000 // auto set if any surface has trans +#define CONTENTS_LADDER 0x20000000 // ladder +#define CONTENTS_NEGATIVE_CURVE 0x40000000 // reverse inside / outside + +#define CONTENTS_KEEP (CONTENTS_DETAIL | CONTENTS_NEGATIVE_CURVE) + + +typedef struct +{ + int planenum; + int children[2]; // negative numbers are -(leafs+1), not nodes + short mins[3]; // for frustom culling + short maxs[3]; + unsigned short firstface; + unsigned short numfaces; // counting both sides +} dnode_t; + + +typedef struct texinfo_s +{ + float vecs[2][4]; // [s/t][xyz offset] + int flags; // miptex flags + overrides + int value; // light emission, etc + char texture[32]; // texture name (textures/*.wal) + int nexttexinfo; // for animations, -1 = end of chain +} texinfo_t; + + +#define SURF_LIGHT 0x1 // value will hold the light strength + +#define SURF_SLICK 0x2 // effects game physics + +#define SURF_SKY 0x4 // don't draw, but add to skybox +#define SURF_WARP 0x8 // turbulent water warp +#define SURF_TRANS33 0x10 +#define SURF_TRANS66 0x20 +#define SURF_FLOWING 0x40 // scroll towards angle +#define SURF_NODRAW 0x80 // don't bother referencing the texture + +#ifdef SOF +// top 8 bits of SURF_xxx flags are material type, next 2 bits down are lightmap mipscale value -slc +#endif + +// not very pleasant, but this stops SoF crashing because of sharing material bits with Q3's curved surface bits... +// +#ifdef SOF +#define SURF_PATCH 0 +#define SURF_CURVE_FAKE 0 +#define SURF_CURVE 0 +#define SURF_NONSOLID 0 +#else +#define SURF_NONSOLID 0x4000 +#define SURF_PATCH 0x20000000 +#define SURF_CURVE_FAKE 0x40000000 +#define SURF_CURVE 0x80000000 +#endif + +#define SURF_KEEP (SURF_CURVE | SURF_CURVE_FAKE | SURF_PATCH | SURF_NONSOLID) + + +// note that edge 0 is never used, because negative edge nums are used for +// counterclockwise use of the edge in a face +typedef struct +{ + unsigned short v[2]; // vertex numbers +} dedge_t; + +#define MAXLIGHTMAPS 4 +typedef struct +{ + unsigned short planenum; + short side; + + int firstedge; // we must support > 64k edges + short numedges; + short texinfo; + +// lighting info + byte styles[MAXLIGHTMAPS]; + int lightofs; // start of [numstyles*surfsize] samples +} dface_t; + +typedef struct +{ + int contents; // OR of all brushes (not needed?) + + int pvsofs; // -1 = no info + int phsofs; // -1 = no info + + short mins[3]; // for frustum culling + short maxs[3]; + + unsigned short firstleafface; + unsigned short numleaffaces; + + unsigned short firstleafbrush; + unsigned short numleafbrushes; +} dleaf_t; + +typedef struct +{ + unsigned short planenum; // facing out of the leaf + short texinfo; +} dbrushside_t; + +typedef struct +{ + int firstside; + int numsides; + int contents; +} dbrush_t; + +#define ANGLE_UP -1 +#define ANGLE_DOWN -2 + + +#define MAX_WORLD_COORD ( 64 * 1024 ) +#define MIN_WORLD_COORD ( -64 * 1024 ) +#define WORLD_SIZE ( MAX_WORLD_COORD - MIN_WORLD_COORD ) diff --git a/utils/Radiant/qgl.h b/utils/Radiant/qgl.h new file mode 100644 index 0000000..6930dc5 --- /dev/null +++ b/utils/Radiant/qgl.h @@ -0,0 +1,425 @@ +/* +** QGL.H +*/ + +#ifndef __QGL_H__ +#define __QGL_H__ + +#ifdef _WIN32 +# include +#endif + +#include +#include + +int QGL_Init( const char *dllname, const char* pGluName ); +void QGL_Shutdown(); + +#ifndef APIENTRY +# define APIENTRY +#endif + +extern void ( APIENTRY * qglAccum )(GLenum op, GLfloat value); +extern void ( APIENTRY * qglAlphaFunc )(GLenum func, GLclampf ref); +extern GLboolean ( APIENTRY * qglAreTexturesResident )(GLsizei n, const GLuint *textures, GLboolean *residences); +extern void ( APIENTRY * qglArrayElement )(GLint i); +extern void ( APIENTRY * qglBegin )(GLenum mode); +extern void ( APIENTRY * qglBindTexture )(GLenum target, GLuint texture); +extern void ( APIENTRY * qglBitmap )(GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap); +extern void ( APIENTRY * qglBlendFunc )(GLenum sfactor, GLenum dfactor); +extern void ( APIENTRY * qglCallList )(GLuint list); +extern void ( APIENTRY * qglCallLists )(GLsizei n, GLenum type, const GLvoid *lists); +extern void ( APIENTRY * qglClear )(GLbitfield mask); +extern void ( APIENTRY * qglClearAccum )(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +extern void ( APIENTRY * qglClearColor )(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +extern void ( APIENTRY * qglClearDepth )(GLclampd depth); +extern void ( APIENTRY * qglClearIndex )(GLfloat c); +extern void ( APIENTRY * qglClearStencil )(GLint s); +extern void ( APIENTRY * qglClipPlane )(GLenum plane, const GLdouble *equation); +extern void ( APIENTRY * qglColor3b )(GLbyte red, GLbyte green, GLbyte blue); +extern void ( APIENTRY * qglColor3bv )(const GLbyte *v); +extern void ( APIENTRY * qglColor3d )(GLdouble red, GLdouble green, GLdouble blue); +extern void ( APIENTRY * qglColor3dv )(const GLdouble *v); +extern void ( APIENTRY * qglColor3f )(GLfloat red, GLfloat green, GLfloat blue); +extern void ( APIENTRY * qglColor3fv )(const GLfloat *v); +extern void ( APIENTRY * qglColor3i )(GLint red, GLint green, GLint blue); +extern void ( APIENTRY * qglColor3iv )(const GLint *v); +extern void ( APIENTRY * qglColor3s )(GLshort red, GLshort green, GLshort blue); +extern void ( APIENTRY * qglColor3sv )(const GLshort *v); +extern void ( APIENTRY * qglColor3ub )(GLubyte red, GLubyte green, GLubyte blue); +extern void ( APIENTRY * qglColor3ubv )(const GLubyte *v); +extern void ( APIENTRY * qglColor3ui )(GLuint red, GLuint green, GLuint blue); +extern void ( APIENTRY * qglColor3uiv )(const GLuint *v); +extern void ( APIENTRY * qglColor3us )(GLushort red, GLushort green, GLushort blue); +extern void ( APIENTRY * qglColor3usv )(const GLushort *v); +extern void ( APIENTRY * qglColor4b )(GLbyte red, GLbyte green, GLbyte blue, GLbyte alpha); +extern void ( APIENTRY * qglColor4bv )(const GLbyte *v); +extern void ( APIENTRY * qglColor4d )(GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha); +extern void ( APIENTRY * qglColor4dv )(const GLdouble *v); +extern void ( APIENTRY * qglColor4f )(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +extern void ( APIENTRY * qglColor4fv )(const GLfloat *v); +extern void ( APIENTRY * qglColor4i )(GLint red, GLint green, GLint blue, GLint alpha); +extern void ( APIENTRY * qglColor4iv )(const GLint *v); +extern void ( APIENTRY * qglColor4s )(GLshort red, GLshort green, GLshort blue, GLshort alpha); +extern void ( APIENTRY * qglColor4sv )(const GLshort *v); +extern void ( APIENTRY * qglColor4ub )(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha); +extern void ( APIENTRY * qglColor4ubv )(const GLubyte *v); +extern void ( APIENTRY * qglColor4ui )(GLuint red, GLuint green, GLuint blue, GLuint alpha); +extern void ( APIENTRY * qglColor4uiv )(const GLuint *v); +extern void ( APIENTRY * qglColor4us )(GLushort red, GLushort green, GLushort blue, GLushort alpha); +extern void ( APIENTRY * qglColor4usv )(const GLushort *v); +extern void ( APIENTRY * qglColorMask )(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +extern void ( APIENTRY * qglColorMaterial )(GLenum face, GLenum mode); +extern void ( APIENTRY * qglColorPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +extern void ( APIENTRY * qglCopyPixels )(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type); +extern void ( APIENTRY * qglCopyTexImage1D )(GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLint border); +extern void ( APIENTRY * qglCopyTexImage2D )(GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +extern void ( APIENTRY * qglCopyTexSubImage1D )(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +extern void ( APIENTRY * qglCopyTexSubImage2D )(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +extern void ( APIENTRY * qglCullFace )(GLenum mode); +extern void ( APIENTRY * qglDeleteLists )(GLuint list, GLsizei range); +extern void ( APIENTRY * qglDeleteTextures )(GLsizei n, const GLuint *textures); +extern void ( APIENTRY * qglDepthFunc )(GLenum func); +extern void ( APIENTRY * qglDepthMask )(GLboolean flag); +extern void ( APIENTRY * qglDepthRange )(GLclampd zNear, GLclampd zFar); +extern void ( APIENTRY * qglDisable )(GLenum cap); +extern void ( APIENTRY * qglDisableClientState )(GLenum array); +extern void ( APIENTRY * qglDrawArrays )(GLenum mode, GLint first, GLsizei count); +extern void ( APIENTRY * qglDrawBuffer )(GLenum mode); +extern void ( APIENTRY * qglDrawElements )(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices); +extern void ( APIENTRY * qglDrawPixels )(GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +extern void ( APIENTRY * qglEdgeFlag )(GLboolean flag); +extern void ( APIENTRY * qglEdgeFlagPointer )(GLsizei stride, const GLvoid *pointer); +extern void ( APIENTRY * qglEdgeFlagv )(const GLboolean *flag); +extern void ( APIENTRY * qglEnable )(GLenum cap); +extern void ( APIENTRY * qglEnableClientState )(GLenum array); +extern void ( APIENTRY * qglEnd )(void); +extern void ( APIENTRY * qglEndList )(void); +extern void ( APIENTRY * qglEvalCoord1d )(GLdouble u); +extern void ( APIENTRY * qglEvalCoord1dv )(const GLdouble *u); +extern void ( APIENTRY * qglEvalCoord1f )(GLfloat u); +extern void ( APIENTRY * qglEvalCoord1fv )(const GLfloat *u); +extern void ( APIENTRY * qglEvalCoord2d )(GLdouble u, GLdouble v); +extern void ( APIENTRY * qglEvalCoord2dv )(const GLdouble *u); +extern void ( APIENTRY * qglEvalCoord2f )(GLfloat u, GLfloat v); +extern void ( APIENTRY * qglEvalCoord2fv )(const GLfloat *u); +extern void ( APIENTRY * qglEvalMesh1 )(GLenum mode, GLint i1, GLint i2); +extern void ( APIENTRY * qglEvalMesh2 )(GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2); +extern void ( APIENTRY * qglEvalPoint1 )(GLint i); +extern void ( APIENTRY * qglEvalPoint2 )(GLint i, GLint j); +extern void ( APIENTRY * qglFeedbackBuffer )(GLsizei size, GLenum type, GLfloat *buffer); +extern void ( APIENTRY * qglFinish )(void); +extern void ( APIENTRY * qglFlush )(void); +extern void ( APIENTRY * qglFogf )(GLenum pname, GLfloat param); +extern void ( APIENTRY * qglFogfv )(GLenum pname, const GLfloat *params); +extern void ( APIENTRY * qglFogi )(GLenum pname, GLint param); +extern void ( APIENTRY * qglFogiv )(GLenum pname, const GLint *params); +extern void ( APIENTRY * qglFrontFace )(GLenum mode); +extern void ( APIENTRY * qglFrustum )(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +extern GLuint ( APIENTRY * qglGenLists )(GLsizei range); +extern void ( APIENTRY * qglGenTextures )(GLsizei n, GLuint *textures); +extern void ( APIENTRY * qglGetBooleanv )(GLenum pname, GLboolean *params); +extern void ( APIENTRY * qglGetClipPlane )(GLenum plane, GLdouble *equation); +extern void ( APIENTRY * qglGetDoublev )(GLenum pname, GLdouble *params); +extern GLenum ( APIENTRY * qglGetError )(void); +extern void ( APIENTRY * qglGetFloatv )(GLenum pname, GLfloat *params); +extern void ( APIENTRY * qglGetIntegerv )(GLenum pname, GLint *params); +extern void ( APIENTRY * qglGetLightfv )(GLenum light, GLenum pname, GLfloat *params); +extern void ( APIENTRY * qglGetLightiv )(GLenum light, GLenum pname, GLint *params); +extern void ( APIENTRY * qglGetMapdv )(GLenum target, GLenum query, GLdouble *v); +extern void ( APIENTRY * qglGetMapfv )(GLenum target, GLenum query, GLfloat *v); +extern void ( APIENTRY * qglGetMapiv )(GLenum target, GLenum query, GLint *v); +extern void ( APIENTRY * qglGetMaterialfv )(GLenum face, GLenum pname, GLfloat *params); +extern void ( APIENTRY * qglGetMaterialiv )(GLenum face, GLenum pname, GLint *params); +extern void ( APIENTRY * qglGetPixelMapfv )(GLenum map, GLfloat *values); +extern void ( APIENTRY * qglGetPixelMapuiv )(GLenum map, GLuint *values); +extern void ( APIENTRY * qglGetPixelMapusv )(GLenum map, GLushort *values); +extern void ( APIENTRY * qglGetPointerv )(GLenum pname, GLvoid* *params); +extern void ( APIENTRY * qglGetPolygonStipple )(GLubyte *mask); +extern const GLubyte * ( APIENTRY * qglGetString )(GLenum name); +extern void ( APIENTRY * qglGetTexEnvfv )(GLenum target, GLenum pname, GLfloat *params); +extern void ( APIENTRY * qglGetTexEnviv )(GLenum target, GLenum pname, GLint *params); +extern void ( APIENTRY * qglGetTexGendv )(GLenum coord, GLenum pname, GLdouble *params); +extern void ( APIENTRY * qglGetTexGenfv )(GLenum coord, GLenum pname, GLfloat *params); +extern void ( APIENTRY * qglGetTexGeniv )(GLenum coord, GLenum pname, GLint *params); +extern void ( APIENTRY * qglGetTexImage )(GLenum target, GLint level, GLenum format, GLenum type, GLvoid *pixels); +extern void ( APIENTRY * qglGetTexLevelParameterfv )(GLenum target, GLint level, GLenum pname, GLfloat *params); +extern void ( APIENTRY * qglGetTexLevelParameteriv )(GLenum target, GLint level, GLenum pname, GLint *params); +extern void ( APIENTRY * qglGetTexParameterfv )(GLenum target, GLenum pname, GLfloat *params); +extern void ( APIENTRY * qglGetTexParameteriv )(GLenum target, GLenum pname, GLint *params); +extern void ( APIENTRY * qglHint )(GLenum target, GLenum mode); +extern void ( APIENTRY * qglIndexMask )(GLuint mask); +extern void ( APIENTRY * qglIndexPointer )(GLenum type, GLsizei stride, const GLvoid *pointer); +extern void ( APIENTRY * qglIndexd )(GLdouble c); +extern void ( APIENTRY * qglIndexdv )(const GLdouble *c); +extern void ( APIENTRY * qglIndexf )(GLfloat c); +extern void ( APIENTRY * qglIndexfv )(const GLfloat *c); +extern void ( APIENTRY * qglIndexi )(GLint c); +extern void ( APIENTRY * qglIndexiv )(const GLint *c); +extern void ( APIENTRY * qglIndexs )(GLshort c); +extern void ( APIENTRY * qglIndexsv )(const GLshort *c); +extern void ( APIENTRY * qglIndexub )(GLubyte c); +extern void ( APIENTRY * qglIndexubv )(const GLubyte *c); +extern void ( APIENTRY * qglInitNames )(void); +extern void ( APIENTRY * qglInterleavedArrays )(GLenum format, GLsizei stride, const GLvoid *pointer); +extern GLboolean ( APIENTRY * qglIsEnabled )(GLenum cap); +extern GLboolean ( APIENTRY * qglIsList )(GLuint list); +extern GLboolean ( APIENTRY * qglIsTexture )(GLuint texture); +extern void ( APIENTRY * qglLightModelf )(GLenum pname, GLfloat param); +extern void ( APIENTRY * qglLightModelfv )(GLenum pname, const GLfloat *params); +extern void ( APIENTRY * qglLightModeli )(GLenum pname, GLint param); +extern void ( APIENTRY * qglLightModeliv )(GLenum pname, const GLint *params); +extern void ( APIENTRY * qglLightf )(GLenum light, GLenum pname, GLfloat param); +extern void ( APIENTRY * qglLightfv )(GLenum light, GLenum pname, const GLfloat *params); +extern void ( APIENTRY * qglLighti )(GLenum light, GLenum pname, GLint param); +extern void ( APIENTRY * qglLightiv )(GLenum light, GLenum pname, const GLint *params); +extern void ( APIENTRY * qglLineStipple )(GLint factor, GLushort pattern); +extern void ( APIENTRY * qglLineWidth )(GLfloat width); +extern void ( APIENTRY * qglListBase )(GLuint base); +extern void ( APIENTRY * qglLoadIdentity )(void); +extern void ( APIENTRY * qglLoadMatrixd )(const GLdouble *m); +extern void ( APIENTRY * qglLoadMatrixf )(const GLfloat *m); +extern void ( APIENTRY * qglLoadName )(GLuint name); +extern void ( APIENTRY * qglLogicOp )(GLenum opcode); +extern void ( APIENTRY * qglMap1d )(GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points); +extern void ( APIENTRY * qglMap1f )(GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points); +extern void ( APIENTRY * qglMap2d )(GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points); +extern void ( APIENTRY * qglMap2f )(GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points); +extern void ( APIENTRY * qglMapGrid1d )(GLint un, GLdouble u1, GLdouble u2); +extern void ( APIENTRY * qglMapGrid1f )(GLint un, GLfloat u1, GLfloat u2); +extern void ( APIENTRY * qglMapGrid2d )(GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2); +extern void ( APIENTRY * qglMapGrid2f )(GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2); +extern void ( APIENTRY * qglMaterialf )(GLenum face, GLenum pname, GLfloat param); +extern void ( APIENTRY * qglMaterialfv )(GLenum face, GLenum pname, const GLfloat *params); +extern void ( APIENTRY * qglMateriali )(GLenum face, GLenum pname, GLint param); +extern void ( APIENTRY * qglMaterialiv )(GLenum face, GLenum pname, const GLint *params); +extern void ( APIENTRY * qglMatrixMode )(GLenum mode); +extern void ( APIENTRY * qglMultMatrixd )(const GLdouble *m); +extern void ( APIENTRY * qglMultMatrixf )(const GLfloat *m); +extern void ( APIENTRY * qglNewList )(GLuint list, GLenum mode); +extern void ( APIENTRY * qglNormal3b )(GLbyte nx, GLbyte ny, GLbyte nz); +extern void ( APIENTRY * qglNormal3bv )(const GLbyte *v); +extern void ( APIENTRY * qglNormal3d )(GLdouble nx, GLdouble ny, GLdouble nz); +extern void ( APIENTRY * qglNormal3dv )(const GLdouble *v); +extern void ( APIENTRY * qglNormal3f )(GLfloat nx, GLfloat ny, GLfloat nz); +extern void ( APIENTRY * qglNormal3fv )(const GLfloat *v); +extern void ( APIENTRY * qglNormal3i )(GLint nx, GLint ny, GLint nz); +extern void ( APIENTRY * qglNormal3iv )(const GLint *v); +extern void ( APIENTRY * qglNormal3s )(GLshort nx, GLshort ny, GLshort nz); +extern void ( APIENTRY * qglNormal3sv )(const GLshort *v); +extern void ( APIENTRY * qglNormalPointer )(GLenum type, GLsizei stride, const GLvoid *pointer); +extern void ( APIENTRY * qglOrtho )(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +extern void ( APIENTRY * qglPassThrough )(GLfloat token); +extern void ( APIENTRY * qglPixelMapfv )(GLenum map, GLsizei mapsize, const GLfloat *values); +extern void ( APIENTRY * qglPixelMapuiv )(GLenum map, GLsizei mapsize, const GLuint *values); +extern void ( APIENTRY * qglPixelMapusv )(GLenum map, GLsizei mapsize, const GLushort *values); +extern void ( APIENTRY * qglPixelStoref )(GLenum pname, GLfloat param); +extern void ( APIENTRY * qglPixelStorei )(GLenum pname, GLint param); +extern void ( APIENTRY * qglPixelTransferf )(GLenum pname, GLfloat param); +extern void ( APIENTRY * qglPixelTransferi )(GLenum pname, GLint param); +extern void ( APIENTRY * qglPixelZoom )(GLfloat xfactor, GLfloat yfactor); +extern void ( APIENTRY * qglPointSize )(GLfloat size); +extern void ( APIENTRY * qglPolygonMode )(GLenum face, GLenum mode); +extern void ( APIENTRY * qglPolygonOffset )(GLfloat factor, GLfloat units); +extern void ( APIENTRY * qglPolygonStipple )(const GLubyte *mask); +extern void ( APIENTRY * qglPopAttrib )(void); +extern void ( APIENTRY * qglPopClientAttrib )(void); +extern void ( APIENTRY * qglPopMatrix )(void); +extern void ( APIENTRY * qglPopName )(void); +extern void ( APIENTRY * qglPrioritizeTextures )(GLsizei n, const GLuint *textures, const GLclampf *priorities); +extern void ( APIENTRY * qglPushAttrib )(GLbitfield mask); +extern void ( APIENTRY * qglPushClientAttrib )(GLbitfield mask); +extern void ( APIENTRY * qglPushMatrix )(void); +extern void ( APIENTRY * qglPushName )(GLuint name); +extern void ( APIENTRY * qglRasterPos2d )(GLdouble x, GLdouble y); +extern void ( APIENTRY * qglRasterPos2dv )(const GLdouble *v); +extern void ( APIENTRY * qglRasterPos2f )(GLfloat x, GLfloat y); +extern void ( APIENTRY * qglRasterPos2fv )(const GLfloat *v); +extern void ( APIENTRY * qglRasterPos2i )(GLint x, GLint y); +extern void ( APIENTRY * qglRasterPos2iv )(const GLint *v); +extern void ( APIENTRY * qglRasterPos2s )(GLshort x, GLshort y); +extern void ( APIENTRY * qglRasterPos2sv )(const GLshort *v); +extern void ( APIENTRY * qglRasterPos3d )(GLdouble x, GLdouble y, GLdouble z); +extern void ( APIENTRY * qglRasterPos3dv )(const GLdouble *v); +extern void ( APIENTRY * qglRasterPos3f )(GLfloat x, GLfloat y, GLfloat z); +extern void ( APIENTRY * qglRasterPos3fv )(const GLfloat *v); +extern void ( APIENTRY * qglRasterPos3i )(GLint x, GLint y, GLint z); +extern void ( APIENTRY * qglRasterPos3iv )(const GLint *v); +extern void ( APIENTRY * qglRasterPos3s )(GLshort x, GLshort y, GLshort z); +extern void ( APIENTRY * qglRasterPos3sv )(const GLshort *v); +extern void ( APIENTRY * qglRasterPos4d )(GLdouble x, GLdouble y, GLdouble z, GLdouble w); +extern void ( APIENTRY * qglRasterPos4dv )(const GLdouble *v); +extern void ( APIENTRY * qglRasterPos4f )(GLfloat x, GLfloat y, GLfloat z, GLfloat w); +extern void ( APIENTRY * qglRasterPos4fv )(const GLfloat *v); +extern void ( APIENTRY * qglRasterPos4i )(GLint x, GLint y, GLint z, GLint w); +extern void ( APIENTRY * qglRasterPos4iv )(const GLint *v); +extern void ( APIENTRY * qglRasterPos4s )(GLshort x, GLshort y, GLshort z, GLshort w); +extern void ( APIENTRY * qglRasterPos4sv )(const GLshort *v); +extern void ( APIENTRY * qglReadBuffer )(GLenum mode); +extern void ( APIENTRY * qglReadPixels )(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels); +extern void ( APIENTRY * qglRectd )(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2); +extern void ( APIENTRY * qglRectdv )(const GLdouble *v1, const GLdouble *v2); +extern void ( APIENTRY * qglRectf )(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2); +extern void ( APIENTRY * qglRectfv )(const GLfloat *v1, const GLfloat *v2); +extern void ( APIENTRY * qglRecti )(GLint x1, GLint y1, GLint x2, GLint y2); +extern void ( APIENTRY * qglRectiv )(const GLint *v1, const GLint *v2); +extern void ( APIENTRY * qglRects )(GLshort x1, GLshort y1, GLshort x2, GLshort y2); +extern void ( APIENTRY * qglRectsv )(const GLshort *v1, const GLshort *v2); +extern GLint ( APIENTRY * qglRenderMode )(GLenum mode); +extern void ( APIENTRY * qglRotated )(GLdouble angle, GLdouble x, GLdouble y, GLdouble z); +extern void ( APIENTRY * qglRotatef )(GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +extern void ( APIENTRY * qglScaled )(GLdouble x, GLdouble y, GLdouble z); +extern void ( APIENTRY * qglScalef )(GLfloat x, GLfloat y, GLfloat z); +extern void ( APIENTRY * qglScissor )(GLint x, GLint y, GLsizei width, GLsizei height); +extern void ( APIENTRY * qglSelectBuffer )(GLsizei size, GLuint *buffer); +extern void ( APIENTRY * qglShadeModel )(GLenum mode); +extern void ( APIENTRY * qglStencilFunc )(GLenum func, GLint ref, GLuint mask); +extern void ( APIENTRY * qglStencilMask )(GLuint mask); +extern void ( APIENTRY * qglStencilOp )(GLenum fail, GLenum zfail, GLenum zpass); +extern void ( APIENTRY * qglTexCoord1d )(GLdouble s); +extern void ( APIENTRY * qglTexCoord1dv )(const GLdouble *v); +extern void ( APIENTRY * qglTexCoord1f )(GLfloat s); +extern void ( APIENTRY * qglTexCoord1fv )(const GLfloat *v); +extern void ( APIENTRY * qglTexCoord1i )(GLint s); +extern void ( APIENTRY * qglTexCoord1iv )(const GLint *v); +extern void ( APIENTRY * qglTexCoord1s )(GLshort s); +extern void ( APIENTRY * qglTexCoord1sv )(const GLshort *v); +extern void ( APIENTRY * qglTexCoord2d )(GLdouble s, GLdouble t); +extern void ( APIENTRY * qglTexCoord2dv )(const GLdouble *v); +extern void ( APIENTRY * qglTexCoord2f )(GLfloat s, GLfloat t); +extern void ( APIENTRY * qglTexCoord2fv )(const GLfloat *v); +extern void ( APIENTRY * qglTexCoord2i )(GLint s, GLint t); +extern void ( APIENTRY * qglTexCoord2iv )(const GLint *v); +extern void ( APIENTRY * qglTexCoord2s )(GLshort s, GLshort t); +extern void ( APIENTRY * qglTexCoord2sv )(const GLshort *v); +extern void ( APIENTRY * qglTexCoord3d )(GLdouble s, GLdouble t, GLdouble r); +extern void ( APIENTRY * qglTexCoord3dv )(const GLdouble *v); +extern void ( APIENTRY * qglTexCoord3f )(GLfloat s, GLfloat t, GLfloat r); +extern void ( APIENTRY * qglTexCoord3fv )(const GLfloat *v); +extern void ( APIENTRY * qglTexCoord3i )(GLint s, GLint t, GLint r); +extern void ( APIENTRY * qglTexCoord3iv )(const GLint *v); +extern void ( APIENTRY * qglTexCoord3s )(GLshort s, GLshort t, GLshort r); +extern void ( APIENTRY * qglTexCoord3sv )(const GLshort *v); +extern void ( APIENTRY * qglTexCoord4d )(GLdouble s, GLdouble t, GLdouble r, GLdouble q); +extern void ( APIENTRY * qglTexCoord4dv )(const GLdouble *v); +extern void ( APIENTRY * qglTexCoord4f )(GLfloat s, GLfloat t, GLfloat r, GLfloat q); +extern void ( APIENTRY * qglTexCoord4fv )(const GLfloat *v); +extern void ( APIENTRY * qglTexCoord4i )(GLint s, GLint t, GLint r, GLint q); +extern void ( APIENTRY * qglTexCoord4iv )(const GLint *v); +extern void ( APIENTRY * qglTexCoord4s )(GLshort s, GLshort t, GLshort r, GLshort q); +extern void ( APIENTRY * qglTexCoord4sv )(const GLshort *v); +extern void ( APIENTRY * qglTexCoordPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +extern void ( APIENTRY * qglTexEnvf )(GLenum target, GLenum pname, GLfloat param); +extern void ( APIENTRY * qglTexEnvfv )(GLenum target, GLenum pname, const GLfloat *params); +extern void ( APIENTRY * qglTexEnvi )(GLenum target, GLenum pname, GLint param); +extern void ( APIENTRY * qglTexEnviv )(GLenum target, GLenum pname, const GLint *params); +extern void ( APIENTRY * qglTexGend )(GLenum coord, GLenum pname, GLdouble param); +extern void ( APIENTRY * qglTexGendv )(GLenum coord, GLenum pname, const GLdouble *params); +extern void ( APIENTRY * qglTexGenf )(GLenum coord, GLenum pname, GLfloat param); +extern void ( APIENTRY * qglTexGenfv )(GLenum coord, GLenum pname, const GLfloat *params); +extern void ( APIENTRY * qglTexGeni )(GLenum coord, GLenum pname, GLint param); +extern void ( APIENTRY * qglTexGeniv )(GLenum coord, GLenum pname, const GLint *params); +extern void ( APIENTRY * qglTexImage1D )(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +extern void ( APIENTRY * qglTexImage2D )(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +extern void ( APIENTRY * qglTexParameterf )(GLenum target, GLenum pname, GLfloat param); +extern void ( APIENTRY * qglTexParameterfv )(GLenum target, GLenum pname, const GLfloat *params); +extern void ( APIENTRY * qglTexParameteri )(GLenum target, GLenum pname, GLint param); +extern void ( APIENTRY * qglTexParameteriv )(GLenum target, GLenum pname, const GLint *params); +extern void ( APIENTRY * qglTexSubImage1D )(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels); +extern void ( APIENTRY * qglTexSubImage2D )(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +extern void ( APIENTRY * qglTranslated )(GLdouble x, GLdouble y, GLdouble z); +extern void ( APIENTRY * qglTranslatef )(GLfloat x, GLfloat y, GLfloat z); +extern void ( APIENTRY * qglVertex2d )(GLdouble x, GLdouble y); +extern void ( APIENTRY * qglVertex2dv )(const GLdouble *v); +extern void ( APIENTRY * qglVertex2f )(GLfloat x, GLfloat y); +extern void ( APIENTRY * qglVertex2fv )(const GLfloat *v); +extern void ( APIENTRY * qglVertex2i )(GLint x, GLint y); +extern void ( APIENTRY * qglVertex2iv )(const GLint *v); +extern void ( APIENTRY * qglVertex2s )(GLshort x, GLshort y); +extern void ( APIENTRY * qglVertex2sv )(const GLshort *v); +extern void ( APIENTRY * qglVertex3d )(GLdouble x, GLdouble y, GLdouble z); +extern void ( APIENTRY * qglVertex3dv )(const GLdouble *v); +extern void ( APIENTRY * qglVertex3f )(GLfloat x, GLfloat y, GLfloat z); +extern void ( APIENTRY * qglVertex3fv )(const GLfloat *v); +extern void ( APIENTRY * qglVertex3i )(GLint x, GLint y, GLint z); +extern void ( APIENTRY * qglVertex3iv )(const GLint *v); +extern void ( APIENTRY * qglVertex3s )(GLshort x, GLshort y, GLshort z); +extern void ( APIENTRY * qglVertex3sv )(const GLshort *v); +extern void ( APIENTRY * qglVertex4d )(GLdouble x, GLdouble y, GLdouble z, GLdouble w); +extern void ( APIENTRY * qglVertex4dv )(const GLdouble *v); +extern void ( APIENTRY * qglVertex4f )(GLfloat x, GLfloat y, GLfloat z, GLfloat w); +extern void ( APIENTRY * qglVertex4fv )(const GLfloat *v); +extern void ( APIENTRY * qglVertex4i )(GLint x, GLint y, GLint z, GLint w); +extern void ( APIENTRY * qglVertex4iv )(const GLint *v); +extern void ( APIENTRY * qglVertex4s )(GLshort x, GLshort y, GLshort z, GLshort w); +extern void ( APIENTRY * qglVertex4sv )(const GLshort *v); +extern void ( APIENTRY * qglVertexPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +extern void ( APIENTRY * qglViewport )(GLint x, GLint y, GLsizei width, GLsizei height); + +extern void ( APIENTRY * qglPointParameterfEXT)( GLenum param, GLfloat value ); +extern void ( APIENTRY * qglPointParameterfvEXT)( GLenum param, const GLfloat *value ); +extern void ( APIENTRY * qglColorTableEXT)( int, int, int, int, int, const void * ); + +extern void ( APIENTRY * qglMTexCoord2fSGIS)( GLenum, GLfloat, GLfloat ); +extern void ( APIENTRY * qglSelectTextureSGIS)( GLenum ); + +#ifdef _WIN32 + +extern int ( WINAPI * qwglChoosePixelFormat )(HDC, CONST PIXELFORMATDESCRIPTOR *); +extern int ( WINAPI * qwglDescribePixelFormat) (HDC, int, UINT, LPPIXELFORMATDESCRIPTOR); +extern int ( WINAPI * qwglGetPixelFormat)(HDC); +extern BOOL ( WINAPI * qwglSetPixelFormat)(HDC, int, CONST PIXELFORMATDESCRIPTOR *); +extern BOOL ( WINAPI * qwglSwapBuffers)(HDC); + +extern BOOL ( WINAPI * qwglCopyContext)(HGLRC, HGLRC, UINT); +extern HGLRC ( WINAPI * qwglCreateContext)(HDC); +extern HGLRC ( WINAPI * qwglCreateLayerContext)(HDC, int); +extern BOOL ( WINAPI * qwglDeleteContext)(HGLRC); +extern HGLRC ( WINAPI * qwglGetCurrentContext)(VOID); +extern HDC ( WINAPI * qwglGetCurrentDC)(VOID); +extern PROC ( WINAPI * qwglGetProcAddress)(LPCSTR); +extern BOOL ( WINAPI * qwglMakeCurrent)(HDC, HGLRC); +extern BOOL ( WINAPI * qwglShareLists)(HGLRC, HGLRC); +extern BOOL ( WINAPI * qwglUseFontBitmaps)(HDC, DWORD, DWORD, DWORD); + +extern BOOL ( WINAPI * qwglUseFontOutlines)(HDC, DWORD, DWORD, DWORD, FLOAT, + FLOAT, int, LPGLYPHMETRICSFLOAT); + +extern BOOL ( WINAPI * qwglDescribeLayerPlane)(HDC, int, int, UINT, + LPLAYERPLANEDESCRIPTOR); +extern int ( WINAPI * qwglSetLayerPaletteEntries)(HDC, int, int, int, + CONST COLORREF *); +extern int ( WINAPI * qwglGetLayerPaletteEntries)(HDC, int, int, int, + COLORREF *); +extern BOOL ( WINAPI * qwglRealizeLayerPalette)(HDC, int, BOOL); +extern BOOL ( WINAPI * qwglSwapLayerBuffers)(HDC, UINT); + +extern BOOL ( WINAPI * qwglSwapIntervalEXT)( int interval ); + +extern BOOL ( WINAPI * qwglGetDeviceGammaRampEXT ) ( unsigned char *pRed, unsigned char *pGreen, unsigned char *pBlue ); +extern BOOL ( WINAPI * qwglSetDeviceGammaRampEXT ) ( const unsigned char *pRed, const unsigned char *pGreen, const unsigned char *pBlue ); + +#endif + +// glu stuff.. radiant only uses a couple +extern int (APIENTRY* qgluBuild2DMipmaps) (GLenum target, GLint components, GLint width, GLint height, GLenum format, GLenum type, const void *data); +extern void (APIENTRY* qgluPerspective) (GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar); + + +// end of glu stuff + + +/* +** extension constants +*/ +#define GL_POINT_SIZE_MIN_EXT 0x8126 +#define GL_POINT_SIZE_MAX_EXT 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_EXT 0x8128 +#define GL_DISTANCE_ATTENUATION_EXT 0x8129 + +#define GL_SHARED_TEXTURE_PALETTE_EXT 0x81FB + +#define GL_TEXTURE0_SGIS 0x835E +#define GL_TEXTURE1_SGIS 0x835F + +#endif diff --git a/utils/Radiant/qgl_win.c b/utils/Radiant/qgl_win.c new file mode 100644 index 0000000..c5f0c20 --- /dev/null +++ b/utils/Radiant/qgl_win.c @@ -0,0 +1,1195 @@ +/* +** QGL_WIN.C +** +** This file implements the operating system binding of GL to QGL function +** pointers. When doing a port of Quake2 you must implement the following +** two functions: +** +** QGL_Init() - loads libraries, assigns function pointers, etc. +** QGL_Shutdown() - unloads libraries, NULLs function pointers +*/ +#include "qgl.h" +#include + +HMODULE g_hGLDLL; +HMODULE g_hGLUDLL; + +# pragma warning (disable : 4113 4133 4047 ) + +int ( WINAPI * qwglChoosePixelFormat )(HDC, CONST PIXELFORMATDESCRIPTOR *); +int ( WINAPI * qwglDescribePixelFormat) (HDC, int, UINT, LPPIXELFORMATDESCRIPTOR); +int ( WINAPI * qwglGetPixelFormat)(HDC); +BOOL ( WINAPI * qwglSetPixelFormat)(HDC, int, CONST PIXELFORMATDESCRIPTOR *); +BOOL ( WINAPI * qwglSwapBuffers)(HDC); + +BOOL ( WINAPI * qwglCopyContext)(HGLRC, HGLRC, UINT); +HGLRC ( WINAPI * qwglCreateContext)(HDC); +HGLRC ( WINAPI * qwglCreateLayerContext)(HDC, int); +BOOL ( WINAPI * qwglDeleteContext)(HGLRC); +HGLRC ( WINAPI * qwglGetCurrentContext)(VOID); +HDC ( WINAPI * qwglGetCurrentDC)(VOID); +PROC ( WINAPI * qwglGetProcAddress)(LPCSTR); +BOOL ( WINAPI * qwglMakeCurrent)(HDC, HGLRC); +BOOL ( WINAPI * qwglShareLists)(HGLRC, HGLRC); +BOOL ( WINAPI * qwglUseFontBitmaps)(HDC, DWORD, DWORD, DWORD); + +BOOL ( WINAPI * qwglUseFontOutlines)(HDC, DWORD, DWORD, DWORD, FLOAT, + FLOAT, int, LPGLYPHMETRICSFLOAT); + +BOOL ( WINAPI * qwglDescribeLayerPlane)(HDC, int, int, UINT, + LPLAYERPLANEDESCRIPTOR); +int ( WINAPI * qwglSetLayerPaletteEntries)(HDC, int, int, int, + CONST COLORREF *); +int ( WINAPI * qwglGetLayerPaletteEntries)(HDC, int, int, int, + COLORREF *); +BOOL ( WINAPI * qwglRealizeLayerPalette)(HDC, int, BOOL); +BOOL ( WINAPI * qwglSwapLayerBuffers)(HDC, UINT); + +void ( APIENTRY * qglAccum )(GLenum op, GLfloat value); +void ( APIENTRY * qglAlphaFunc )(GLenum func, GLclampf ref); +GLboolean ( APIENTRY * qglAreTexturesResident )(GLsizei n, const GLuint *textures, GLboolean *residences); +void ( APIENTRY * qglArrayElement )(GLint i); +void ( APIENTRY * qglBegin )(GLenum mode); +void ( APIENTRY * qglBindTexture )(GLenum target, GLuint texture); +void ( APIENTRY * qglBitmap )(GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap); +void ( APIENTRY * qglBlendFunc )(GLenum sfactor, GLenum dfactor); +void ( APIENTRY * qglCallList )(GLuint list); +void ( APIENTRY * qglCallLists )(GLsizei n, GLenum type, const GLvoid *lists); +void ( APIENTRY * qglClear )(GLbitfield mask); +void ( APIENTRY * qglClearAccum )(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +void ( APIENTRY * qglClearColor )(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +void ( APIENTRY * qglClearDepth )(GLclampd depth); +void ( APIENTRY * qglClearIndex )(GLfloat c); +void ( APIENTRY * qglClearStencil )(GLint s); +void ( APIENTRY * qglClipPlane )(GLenum plane, const GLdouble *equation); +void ( APIENTRY * qglColor3b )(GLbyte red, GLbyte green, GLbyte blue); +void ( APIENTRY * qglColor3bv )(const GLbyte *v); +void ( APIENTRY * qglColor3d )(GLdouble red, GLdouble green, GLdouble blue); +void ( APIENTRY * qglColor3dv )(const GLdouble *v); +void ( APIENTRY * qglColor3f )(GLfloat red, GLfloat green, GLfloat blue); +void ( APIENTRY * qglColor3fv )(const GLfloat *v); +void ( APIENTRY * qglColor3i )(GLint red, GLint green, GLint blue); +void ( APIENTRY * qglColor3iv )(const GLint *v); +void ( APIENTRY * qglColor3s )(GLshort red, GLshort green, GLshort blue); +void ( APIENTRY * qglColor3sv )(const GLshort *v); +void ( APIENTRY * qglColor3ub )(GLubyte red, GLubyte green, GLubyte blue); +void ( APIENTRY * qglColor3ubv )(const GLubyte *v); +void ( APIENTRY * qglColor3ui )(GLuint red, GLuint green, GLuint blue); +void ( APIENTRY * qglColor3uiv )(const GLuint *v); +void ( APIENTRY * qglColor3us )(GLushort red, GLushort green, GLushort blue); +void ( APIENTRY * qglColor3usv )(const GLushort *v); +void ( APIENTRY * qglColor4b )(GLbyte red, GLbyte green, GLbyte blue, GLbyte alpha); +void ( APIENTRY * qglColor4bv )(const GLbyte *v); +void ( APIENTRY * qglColor4d )(GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha); +void ( APIENTRY * qglColor4dv )(const GLdouble *v); +void ( APIENTRY * qglColor4f )(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +void ( APIENTRY * qglColor4fv )(const GLfloat *v); +void ( APIENTRY * qglColor4i )(GLint red, GLint green, GLint blue, GLint alpha); +void ( APIENTRY * qglColor4iv )(const GLint *v); +void ( APIENTRY * qglColor4s )(GLshort red, GLshort green, GLshort blue, GLshort alpha); +void ( APIENTRY * qglColor4sv )(const GLshort *v); +void ( APIENTRY * qglColor4ub )(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha); +void ( APIENTRY * qglColor4ubv )(const GLubyte *v); +void ( APIENTRY * qglColor4ui )(GLuint red, GLuint green, GLuint blue, GLuint alpha); +void ( APIENTRY * qglColor4uiv )(const GLuint *v); +void ( APIENTRY * qglColor4us )(GLushort red, GLushort green, GLushort blue, GLushort alpha); +void ( APIENTRY * qglColor4usv )(const GLushort *v); +void ( APIENTRY * qglColorMask )(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +void ( APIENTRY * qglColorMaterial )(GLenum face, GLenum mode); +void ( APIENTRY * qglColorPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +void ( APIENTRY * qglCopyPixels )(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type); +void ( APIENTRY * qglCopyTexImage1D )(GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLint border); +void ( APIENTRY * qglCopyTexImage2D )(GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +void ( APIENTRY * qglCopyTexSubImage1D )(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +void ( APIENTRY * qglCopyTexSubImage2D )(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +void ( APIENTRY * qglCullFace )(GLenum mode); +void ( APIENTRY * qglDeleteLists )(GLuint list, GLsizei range); +void ( APIENTRY * qglDeleteTextures )(GLsizei n, const GLuint *textures); +void ( APIENTRY * qglDepthFunc )(GLenum func); +void ( APIENTRY * qglDepthMask )(GLboolean flag); +void ( APIENTRY * qglDepthRange )(GLclampd zNear, GLclampd zFar); +void ( APIENTRY * qglDisable )(GLenum cap); +void ( APIENTRY * qglDisableClientState )(GLenum array); +void ( APIENTRY * qglDrawArrays )(GLenum mode, GLint first, GLsizei count); +void ( APIENTRY * qglDrawBuffer )(GLenum mode); +void ( APIENTRY * qglDrawElements )(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices); +void ( APIENTRY * qglDrawPixels )(GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +void ( APIENTRY * qglEdgeFlag )(GLboolean flag); +void ( APIENTRY * qglEdgeFlagPointer )(GLsizei stride, const GLvoid *pointer); +void ( APIENTRY * qglEdgeFlagv )(const GLboolean *flag); +void ( APIENTRY * qglEnable )(GLenum cap); +void ( APIENTRY * qglEnableClientState )(GLenum array); +void ( APIENTRY * qglEnd )(void); +void ( APIENTRY * qglEndList )(void); +void ( APIENTRY * qglEvalCoord1d )(GLdouble u); +void ( APIENTRY * qglEvalCoord1dv )(const GLdouble *u); +void ( APIENTRY * qglEvalCoord1f )(GLfloat u); +void ( APIENTRY * qglEvalCoord1fv )(const GLfloat *u); +void ( APIENTRY * qglEvalCoord2d )(GLdouble u, GLdouble v); +void ( APIENTRY * qglEvalCoord2dv )(const GLdouble *u); +void ( APIENTRY * qglEvalCoord2f )(GLfloat u, GLfloat v); +void ( APIENTRY * qglEvalCoord2fv )(const GLfloat *u); +void ( APIENTRY * qglEvalMesh1 )(GLenum mode, GLint i1, GLint i2); +void ( APIENTRY * qglEvalMesh2 )(GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2); +void ( APIENTRY * qglEvalPoint1 )(GLint i); +void ( APIENTRY * qglEvalPoint2 )(GLint i, GLint j); +void ( APIENTRY * qglFeedbackBuffer )(GLsizei size, GLenum type, GLfloat *buffer); +void ( APIENTRY * qglFinish )(void); +void ( APIENTRY * qglFlush )(void); +void ( APIENTRY * qglFogf )(GLenum pname, GLfloat param); +void ( APIENTRY * qglFogfv )(GLenum pname, const GLfloat *params); +void ( APIENTRY * qglFogi )(GLenum pname, GLint param); +void ( APIENTRY * qglFogiv )(GLenum pname, const GLint *params); +void ( APIENTRY * qglFrontFace )(GLenum mode); +void ( APIENTRY * qglFrustum )(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +GLuint ( APIENTRY * qglGenLists )(GLsizei range); +void ( APIENTRY * qglGenTextures )(GLsizei n, GLuint *textures); +void ( APIENTRY * qglGetBooleanv )(GLenum pname, GLboolean *params); +void ( APIENTRY * qglGetClipPlane )(GLenum plane, GLdouble *equation); +void ( APIENTRY * qglGetDoublev )(GLenum pname, GLdouble *params); +GLenum ( APIENTRY * qglGetError )(void); +void ( APIENTRY * qglGetFloatv )(GLenum pname, GLfloat *params); +void ( APIENTRY * qglGetIntegerv )(GLenum pname, GLint *params); +void ( APIENTRY * qglGetLightfv )(GLenum light, GLenum pname, GLfloat *params); +void ( APIENTRY * qglGetLightiv )(GLenum light, GLenum pname, GLint *params); +void ( APIENTRY * qglGetMapdv )(GLenum target, GLenum query, GLdouble *v); +void ( APIENTRY * qglGetMapfv )(GLenum target, GLenum query, GLfloat *v); +void ( APIENTRY * qglGetMapiv )(GLenum target, GLenum query, GLint *v); +void ( APIENTRY * qglGetMaterialfv )(GLenum face, GLenum pname, GLfloat *params); +void ( APIENTRY * qglGetMaterialiv )(GLenum face, GLenum pname, GLint *params); +void ( APIENTRY * qglGetPixelMapfv )(GLenum map, GLfloat *values); +void ( APIENTRY * qglGetPixelMapuiv )(GLenum map, GLuint *values); +void ( APIENTRY * qglGetPixelMapusv )(GLenum map, GLushort *values); +void ( APIENTRY * qglGetPointerv )(GLenum pname, GLvoid* *params); +void ( APIENTRY * qglGetPolygonStipple )(GLubyte *mask); +const GLubyte * ( APIENTRY * qglGetString )(GLenum name); +void ( APIENTRY * qglGetTexEnvfv )(GLenum target, GLenum pname, GLfloat *params); +void ( APIENTRY * qglGetTexEnviv )(GLenum target, GLenum pname, GLint *params); +void ( APIENTRY * qglGetTexGendv )(GLenum coord, GLenum pname, GLdouble *params); +void ( APIENTRY * qglGetTexGenfv )(GLenum coord, GLenum pname, GLfloat *params); +void ( APIENTRY * qglGetTexGeniv )(GLenum coord, GLenum pname, GLint *params); +void ( APIENTRY * qglGetTexImage )(GLenum target, GLint level, GLenum format, GLenum type, GLvoid *pixels); +void ( APIENTRY * qglGetTexLevelParameterfv )(GLenum target, GLint level, GLenum pname, GLfloat *params); +void ( APIENTRY * qglGetTexLevelParameteriv )(GLenum target, GLint level, GLenum pname, GLint *params); +void ( APIENTRY * qglGetTexParameterfv )(GLenum target, GLenum pname, GLfloat *params); +void ( APIENTRY * qglGetTexParameteriv )(GLenum target, GLenum pname, GLint *params); +void ( APIENTRY * qglHint )(GLenum target, GLenum mode); +void ( APIENTRY * qglIndexMask )(GLuint mask); +void ( APIENTRY * qglIndexPointer )(GLenum type, GLsizei stride, const GLvoid *pointer); +void ( APIENTRY * qglIndexd )(GLdouble c); +void ( APIENTRY * qglIndexdv )(const GLdouble *c); +void ( APIENTRY * qglIndexf )(GLfloat c); +void ( APIENTRY * qglIndexfv )(const GLfloat *c); +void ( APIENTRY * qglIndexi )(GLint c); +void ( APIENTRY * qglIndexiv )(const GLint *c); +void ( APIENTRY * qglIndexs )(GLshort c); +void ( APIENTRY * qglIndexsv )(const GLshort *c); +void ( APIENTRY * qglIndexub )(GLubyte c); +void ( APIENTRY * qglIndexubv )(const GLubyte *c); +void ( APIENTRY * qglInitNames )(void); +void ( APIENTRY * qglInterleavedArrays )(GLenum format, GLsizei stride, const GLvoid *pointer); +GLboolean ( APIENTRY * qglIsEnabled )(GLenum cap); +GLboolean ( APIENTRY * qglIsList )(GLuint list); +GLboolean ( APIENTRY * qglIsTexture )(GLuint texture); +void ( APIENTRY * qglLightModelf )(GLenum pname, GLfloat param); +void ( APIENTRY * qglLightModelfv )(GLenum pname, const GLfloat *params); +void ( APIENTRY * qglLightModeli )(GLenum pname, GLint param); +void ( APIENTRY * qglLightModeliv )(GLenum pname, const GLint *params); +void ( APIENTRY * qglLightf )(GLenum light, GLenum pname, GLfloat param); +void ( APIENTRY * qglLightfv )(GLenum light, GLenum pname, const GLfloat *params); +void ( APIENTRY * qglLighti )(GLenum light, GLenum pname, GLint param); +void ( APIENTRY * qglLightiv )(GLenum light, GLenum pname, const GLint *params); +void ( APIENTRY * qglLineStipple )(GLint factor, GLushort pattern); +void ( APIENTRY * qglLineWidth )(GLfloat width); +void ( APIENTRY * qglListBase )(GLuint base); +void ( APIENTRY * qglLoadIdentity )(void); +void ( APIENTRY * qglLoadMatrixd )(const GLdouble *m); +void ( APIENTRY * qglLoadMatrixf )(const GLfloat *m); +void ( APIENTRY * qglLoadName )(GLuint name); +void ( APIENTRY * qglLogicOp )(GLenum opcode); +void ( APIENTRY * qglMap1d )(GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points); +void ( APIENTRY * qglMap1f )(GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points); +void ( APIENTRY * qglMap2d )(GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points); +void ( APIENTRY * qglMap2f )(GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points); +void ( APIENTRY * qglMapGrid1d )(GLint un, GLdouble u1, GLdouble u2); +void ( APIENTRY * qglMapGrid1f )(GLint un, GLfloat u1, GLfloat u2); +void ( APIENTRY * qglMapGrid2d )(GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2); +void ( APIENTRY * qglMapGrid2f )(GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2); +void ( APIENTRY * qglMaterialf )(GLenum face, GLenum pname, GLfloat param); +void ( APIENTRY * qglMaterialfv )(GLenum face, GLenum pname, const GLfloat *params); +void ( APIENTRY * qglMateriali )(GLenum face, GLenum pname, GLint param); +void ( APIENTRY * qglMaterialiv )(GLenum face, GLenum pname, const GLint *params); +void ( APIENTRY * qglMatrixMode )(GLenum mode); +void ( APIENTRY * qglMultMatrixd )(const GLdouble *m); +void ( APIENTRY * qglMultMatrixf )(const GLfloat *m); +void ( APIENTRY * qglNewList )(GLuint list, GLenum mode); +void ( APIENTRY * qglNormal3b )(GLbyte nx, GLbyte ny, GLbyte nz); +void ( APIENTRY * qglNormal3bv )(const GLbyte *v); +void ( APIENTRY * qglNormal3d )(GLdouble nx, GLdouble ny, GLdouble nz); +void ( APIENTRY * qglNormal3dv )(const GLdouble *v); +void ( APIENTRY * qglNormal3f )(GLfloat nx, GLfloat ny, GLfloat nz); +void ( APIENTRY * qglNormal3fv )(const GLfloat *v); +void ( APIENTRY * qglNormal3i )(GLint nx, GLint ny, GLint nz); +void ( APIENTRY * qglNormal3iv )(const GLint *v); +void ( APIENTRY * qglNormal3s )(GLshort nx, GLshort ny, GLshort nz); +void ( APIENTRY * qglNormal3sv )(const GLshort *v); +void ( APIENTRY * qglNormalPointer )(GLenum type, GLsizei stride, const GLvoid *pointer); +void ( APIENTRY * qglOrtho )(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +void ( APIENTRY * qglPassThrough )(GLfloat token); +void ( APIENTRY * qglPixelMapfv )(GLenum map, GLsizei mapsize, const GLfloat *values); +void ( APIENTRY * qglPixelMapuiv )(GLenum map, GLsizei mapsize, const GLuint *values); +void ( APIENTRY * qglPixelMapusv )(GLenum map, GLsizei mapsize, const GLushort *values); +void ( APIENTRY * qglPixelStoref )(GLenum pname, GLfloat param); +void ( APIENTRY * qglPixelStorei )(GLenum pname, GLint param); +void ( APIENTRY * qglPixelTransferf )(GLenum pname, GLfloat param); +void ( APIENTRY * qglPixelTransferi )(GLenum pname, GLint param); +void ( APIENTRY * qglPixelZoom )(GLfloat xfactor, GLfloat yfactor); +void ( APIENTRY * qglPointSize )(GLfloat size); +void ( APIENTRY * qglPolygonMode )(GLenum face, GLenum mode); +void ( APIENTRY * qglPolygonOffset )(GLfloat factor, GLfloat units); +void ( APIENTRY * qglPolygonStipple )(const GLubyte *mask); +void ( APIENTRY * qglPopAttrib )(void); +void ( APIENTRY * qglPopClientAttrib )(void); +void ( APIENTRY * qglPopMatrix )(void); +void ( APIENTRY * qglPopName )(void); +void ( APIENTRY * qglPrioritizeTextures )(GLsizei n, const GLuint *textures, const GLclampf *priorities); +void ( APIENTRY * qglPushAttrib )(GLbitfield mask); +void ( APIENTRY * qglPushClientAttrib )(GLbitfield mask); +void ( APIENTRY * qglPushMatrix )(void); +void ( APIENTRY * qglPushName )(GLuint name); +void ( APIENTRY * qglRasterPos2d )(GLdouble x, GLdouble y); +void ( APIENTRY * qglRasterPos2dv )(const GLdouble *v); +void ( APIENTRY * qglRasterPos2f )(GLfloat x, GLfloat y); +void ( APIENTRY * qglRasterPos2fv )(const GLfloat *v); +void ( APIENTRY * qglRasterPos2i )(GLint x, GLint y); +void ( APIENTRY * qglRasterPos2iv )(const GLint *v); +void ( APIENTRY * qglRasterPos2s )(GLshort x, GLshort y); +void ( APIENTRY * qglRasterPos2sv )(const GLshort *v); +void ( APIENTRY * qglRasterPos3d )(GLdouble x, GLdouble y, GLdouble z); +void ( APIENTRY * qglRasterPos3dv )(const GLdouble *v); +void ( APIENTRY * qglRasterPos3f )(GLfloat x, GLfloat y, GLfloat z); +void ( APIENTRY * qglRasterPos3fv )(const GLfloat *v); +void ( APIENTRY * qglRasterPos3i )(GLint x, GLint y, GLint z); +void ( APIENTRY * qglRasterPos3iv )(const GLint *v); +void ( APIENTRY * qglRasterPos3s )(GLshort x, GLshort y, GLshort z); +void ( APIENTRY * qglRasterPos3sv )(const GLshort *v); +void ( APIENTRY * qglRasterPos4d )(GLdouble x, GLdouble y, GLdouble z, GLdouble w); +void ( APIENTRY * qglRasterPos4dv )(const GLdouble *v); +void ( APIENTRY * qglRasterPos4f )(GLfloat x, GLfloat y, GLfloat z, GLfloat w); +void ( APIENTRY * qglRasterPos4fv )(const GLfloat *v); +void ( APIENTRY * qglRasterPos4i )(GLint x, GLint y, GLint z, GLint w); +void ( APIENTRY * qglRasterPos4iv )(const GLint *v); +void ( APIENTRY * qglRasterPos4s )(GLshort x, GLshort y, GLshort z, GLshort w); +void ( APIENTRY * qglRasterPos4sv )(const GLshort *v); +void ( APIENTRY * qglReadBuffer )(GLenum mode); +void ( APIENTRY * qglReadPixels )(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels); +void ( APIENTRY * qglRectd )(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2); +void ( APIENTRY * qglRectdv )(const GLdouble *v1, const GLdouble *v2); +void ( APIENTRY * qglRectf )(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2); +void ( APIENTRY * qglRectfv )(const GLfloat *v1, const GLfloat *v2); +void ( APIENTRY * qglRecti )(GLint x1, GLint y1, GLint x2, GLint y2); +void ( APIENTRY * qglRectiv )(const GLint *v1, const GLint *v2); +void ( APIENTRY * qglRects )(GLshort x1, GLshort y1, GLshort x2, GLshort y2); +void ( APIENTRY * qglRectsv )(const GLshort *v1, const GLshort *v2); +GLint ( APIENTRY * qglRenderMode )(GLenum mode); +void ( APIENTRY * qglRotated )(GLdouble angle, GLdouble x, GLdouble y, GLdouble z); +void ( APIENTRY * qglRotatef )(GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +void ( APIENTRY * qglScaled )(GLdouble x, GLdouble y, GLdouble z); +void ( APIENTRY * qglScalef )(GLfloat x, GLfloat y, GLfloat z); +void ( APIENTRY * qglScissor )(GLint x, GLint y, GLsizei width, GLsizei height); +void ( APIENTRY * qglSelectBuffer )(GLsizei size, GLuint *buffer); +void ( APIENTRY * qglShadeModel )(GLenum mode); +void ( APIENTRY * qglStencilFunc )(GLenum func, GLint ref, GLuint mask); +void ( APIENTRY * qglStencilMask )(GLuint mask); +void ( APIENTRY * qglStencilOp )(GLenum fail, GLenum zfail, GLenum zpass); +void ( APIENTRY * qglTexCoord1d )(GLdouble s); +void ( APIENTRY * qglTexCoord1dv )(const GLdouble *v); +void ( APIENTRY * qglTexCoord1f )(GLfloat s); +void ( APIENTRY * qglTexCoord1fv )(const GLfloat *v); +void ( APIENTRY * qglTexCoord1i )(GLint s); +void ( APIENTRY * qglTexCoord1iv )(const GLint *v); +void ( APIENTRY * qglTexCoord1s )(GLshort s); +void ( APIENTRY * qglTexCoord1sv )(const GLshort *v); +void ( APIENTRY * qglTexCoord2d )(GLdouble s, GLdouble t); +void ( APIENTRY * qglTexCoord2dv )(const GLdouble *v); +void ( APIENTRY * qglTexCoord2f )(GLfloat s, GLfloat t); +void ( APIENTRY * qglTexCoord2fv )(const GLfloat *v); +void ( APIENTRY * qglTexCoord2i )(GLint s, GLint t); +void ( APIENTRY * qglTexCoord2iv )(const GLint *v); +void ( APIENTRY * qglTexCoord2s )(GLshort s, GLshort t); +void ( APIENTRY * qglTexCoord2sv )(const GLshort *v); +void ( APIENTRY * qglTexCoord3d )(GLdouble s, GLdouble t, GLdouble r); +void ( APIENTRY * qglTexCoord3dv )(const GLdouble *v); +void ( APIENTRY * qglTexCoord3f )(GLfloat s, GLfloat t, GLfloat r); +void ( APIENTRY * qglTexCoord3fv )(const GLfloat *v); +void ( APIENTRY * qglTexCoord3i )(GLint s, GLint t, GLint r); +void ( APIENTRY * qglTexCoord3iv )(const GLint *v); +void ( APIENTRY * qglTexCoord3s )(GLshort s, GLshort t, GLshort r); +void ( APIENTRY * qglTexCoord3sv )(const GLshort *v); +void ( APIENTRY * qglTexCoord4d )(GLdouble s, GLdouble t, GLdouble r, GLdouble q); +void ( APIENTRY * qglTexCoord4dv )(const GLdouble *v); +void ( APIENTRY * qglTexCoord4f )(GLfloat s, GLfloat t, GLfloat r, GLfloat q); +void ( APIENTRY * qglTexCoord4fv )(const GLfloat *v); +void ( APIENTRY * qglTexCoord4i )(GLint s, GLint t, GLint r, GLint q); +void ( APIENTRY * qglTexCoord4iv )(const GLint *v); +void ( APIENTRY * qglTexCoord4s )(GLshort s, GLshort t, GLshort r, GLshort q); +void ( APIENTRY * qglTexCoord4sv )(const GLshort *v); +void ( APIENTRY * qglTexCoordPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +void ( APIENTRY * qglTexEnvf )(GLenum target, GLenum pname, GLfloat param); +void ( APIENTRY * qglTexEnvfv )(GLenum target, GLenum pname, const GLfloat *params); +void ( APIENTRY * qglTexEnvi )(GLenum target, GLenum pname, GLint param); +void ( APIENTRY * qglTexEnviv )(GLenum target, GLenum pname, const GLint *params); +void ( APIENTRY * qglTexGend )(GLenum coord, GLenum pname, GLdouble param); +void ( APIENTRY * qglTexGendv )(GLenum coord, GLenum pname, const GLdouble *params); +void ( APIENTRY * qglTexGenf )(GLenum coord, GLenum pname, GLfloat param); +void ( APIENTRY * qglTexGenfv )(GLenum coord, GLenum pname, const GLfloat *params); +void ( APIENTRY * qglTexGeni )(GLenum coord, GLenum pname, GLint param); +void ( APIENTRY * qglTexGeniv )(GLenum coord, GLenum pname, const GLint *params); +void ( APIENTRY * qglTexImage1D )(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +void ( APIENTRY * qglTexImage2D )(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +void ( APIENTRY * qglTexParameterf )(GLenum target, GLenum pname, GLfloat param); +void ( APIENTRY * qglTexParameterfv )(GLenum target, GLenum pname, const GLfloat *params); +void ( APIENTRY * qglTexParameteri )(GLenum target, GLenum pname, GLint param); +void ( APIENTRY * qglTexParameteriv )(GLenum target, GLenum pname, const GLint *params); +void ( APIENTRY * qglTexSubImage1D )(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels); +void ( APIENTRY * qglTexSubImage2D )(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +void ( APIENTRY * qglTranslated )(GLdouble x, GLdouble y, GLdouble z); +void ( APIENTRY * qglTranslatef )(GLfloat x, GLfloat y, GLfloat z); +void ( APIENTRY * qglVertex2d )(GLdouble x, GLdouble y); +void ( APIENTRY * qglVertex2dv )(const GLdouble *v); +void ( APIENTRY * qglVertex2f )(GLfloat x, GLfloat y); +void ( APIENTRY * qglVertex2fv )(const GLfloat *v); +void ( APIENTRY * qglVertex2i )(GLint x, GLint y); +void ( APIENTRY * qglVertex2iv )(const GLint *v); +void ( APIENTRY * qglVertex2s )(GLshort x, GLshort y); +void ( APIENTRY * qglVertex2sv )(const GLshort *v); +void ( APIENTRY * qglVertex3d )(GLdouble x, GLdouble y, GLdouble z); +void ( APIENTRY * qglVertex3dv )(const GLdouble *v); +void ( APIENTRY * qglVertex3f )(GLfloat x, GLfloat y, GLfloat z); +void ( APIENTRY * qglVertex3fv )(const GLfloat *v); +void ( APIENTRY * qglVertex3i )(GLint x, GLint y, GLint z); +void ( APIENTRY * qglVertex3iv )(const GLint *v); +void ( APIENTRY * qglVertex3s )(GLshort x, GLshort y, GLshort z); +void ( APIENTRY * qglVertex3sv )(const GLshort *v); +void ( APIENTRY * qglVertex4d )(GLdouble x, GLdouble y, GLdouble z, GLdouble w); +void ( APIENTRY * qglVertex4dv )(const GLdouble *v); +void ( APIENTRY * qglVertex4f )(GLfloat x, GLfloat y, GLfloat z, GLfloat w); +void ( APIENTRY * qglVertex4fv )(const GLfloat *v); +void ( APIENTRY * qglVertex4i )(GLint x, GLint y, GLint z, GLint w); +void ( APIENTRY * qglVertex4iv )(const GLint *v); +void ( APIENTRY * qglVertex4s )(GLshort x, GLshort y, GLshort z, GLshort w); +void ( APIENTRY * qglVertex4sv )(const GLshort *v); +void ( APIENTRY * qglVertexPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +void ( APIENTRY * qglViewport )(GLint x, GLint y, GLsizei width, GLsizei height); + +BOOL ( WINAPI * qwglSwapIntervalEXT)( int interval ); +BOOL ( WINAPI * qwglGetDeviceGammaRampEXT)( unsigned char *, unsigned char *, unsigned char * ); +BOOL ( WINAPI * qwglSetDeviceGammaRampEXT)( const unsigned char *, const unsigned char *, const unsigned char * ); +void ( APIENTRY * qglPointParameterfEXT)( GLenum param, GLfloat value ); +void ( APIENTRY * qglPointParameterfvEXT)( GLenum param, const GLfloat *value ); +void ( APIENTRY * qglColorTableEXT)( int, int, int, int, int, const void * ); +void ( APIENTRY * qglSelectTextureSGIS)( GLenum ); +void ( APIENTRY * qglMTexCoord2fSGIS)( GLenum, GLfloat, GLfloat ); + + +// glu stuff +void (APIENTRY * qgluPerspective) (GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar); +int (APIENTRY * qgluBuild2DMipmaps) (GLenum target, GLint components, GLint width, GLint height, GLenum format, GLenum type, const void *data); + + +/* +** QGL_Shutdown +** +** Unloads the specified DLL then nulls out all the proc pointers. +*/ +void QGL_Shutdown() +{ + if (g_hGLDLL) + { + FreeLibrary(g_hGLDLL); + g_hGLDLL = NULL; + } + if (g_hGLUDLL) + { + FreeLibrary(g_hGLUDLL); + g_hGLUDLL = NULL; + } + + + qglAccum = NULL; + qglAlphaFunc = NULL; + qglAreTexturesResident = NULL; + qglArrayElement = NULL; + qglBegin = NULL; + qglBindTexture = NULL; + qglBitmap = NULL; + qglBlendFunc = NULL; + qglCallList = NULL; + qglCallLists = NULL; + qglClear = NULL; + qglClearAccum = NULL; + qglClearColor = NULL; + qglClearDepth = NULL; + qglClearIndex = NULL; + qglClearStencil = NULL; + qglClipPlane = NULL; + qglColor3b = NULL; + qglColor3bv = NULL; + qglColor3d = NULL; + qglColor3dv = NULL; + qglColor3f = NULL; + qglColor3fv = NULL; + qglColor3i = NULL; + qglColor3iv = NULL; + qglColor3s = NULL; + qglColor3sv = NULL; + qglColor3ub = NULL; + qglColor3ubv = NULL; + qglColor3ui = NULL; + qglColor3uiv = NULL; + qglColor3us = NULL; + qglColor3usv = NULL; + qglColor4b = NULL; + qglColor4bv = NULL; + qglColor4d = NULL; + qglColor4dv = NULL; + qglColor4f = NULL; + qglColor4fv = NULL; + qglColor4i = NULL; + qglColor4iv = NULL; + qglColor4s = NULL; + qglColor4sv = NULL; + qglColor4ub = NULL; + qglColor4ubv = NULL; + qglColor4ui = NULL; + qglColor4uiv = NULL; + qglColor4us = NULL; + qglColor4usv = NULL; + qglColorMask = NULL; + qglColorMaterial = NULL; + qglColorPointer = NULL; + qglCopyPixels = NULL; + qglCopyTexImage1D = NULL; + qglCopyTexImage2D = NULL; + qglCopyTexSubImage1D = NULL; + qglCopyTexSubImage2D = NULL; + qglCullFace = NULL; + qglDeleteLists = NULL; + qglDeleteTextures = NULL; + qglDepthFunc = NULL; + qglDepthMask = NULL; + qglDepthRange = NULL; + qglDisable = NULL; + qglDisableClientState = NULL; + qglDrawArrays = NULL; + qglDrawBuffer = NULL; + qglDrawElements = NULL; + qglDrawPixels = NULL; + qglEdgeFlag = NULL; + qglEdgeFlagPointer = NULL; + qglEdgeFlagv = NULL; + qglEnable = NULL; + qglEnableClientState = NULL; + qglEnd = NULL; + qglEndList = NULL; + qglEvalCoord1d = NULL; + qglEvalCoord1dv = NULL; + qglEvalCoord1f = NULL; + qglEvalCoord1fv = NULL; + qglEvalCoord2d = NULL; + qglEvalCoord2dv = NULL; + qglEvalCoord2f = NULL; + qglEvalCoord2fv = NULL; + qglEvalMesh1 = NULL; + qglEvalMesh2 = NULL; + qglEvalPoint1 = NULL; + qglEvalPoint2 = NULL; + qglFeedbackBuffer = NULL; + qglFinish = NULL; + qglFlush = NULL; + qglFogf = NULL; + qglFogfv = NULL; + qglFogi = NULL; + qglFogiv = NULL; + qglFrontFace = NULL; + qglFrustum = NULL; + qglGenLists = NULL; + qglGenTextures = NULL; + qglGetBooleanv = NULL; + qglGetClipPlane = NULL; + qglGetDoublev = NULL; + qglGetError = NULL; + qglGetFloatv = NULL; + qglGetIntegerv = NULL; + qglGetLightfv = NULL; + qglGetLightiv = NULL; + qglGetMapdv = NULL; + qglGetMapfv = NULL; + qglGetMapiv = NULL; + qglGetMaterialfv = NULL; + qglGetMaterialiv = NULL; + qglGetPixelMapfv = NULL; + qglGetPixelMapuiv = NULL; + qglGetPixelMapusv = NULL; + qglGetPointerv = NULL; + qglGetPolygonStipple = NULL; + qglGetString = NULL; + qglGetTexEnvfv = NULL; + qglGetTexEnviv = NULL; + qglGetTexGendv = NULL; + qglGetTexGenfv = NULL; + qglGetTexGeniv = NULL; + qglGetTexImage = NULL; + qglGetTexLevelParameterfv = NULL; + qglGetTexLevelParameteriv = NULL; + qglGetTexParameterfv = NULL; + qglGetTexParameteriv = NULL; + qglHint = NULL; + qglIndexMask = NULL; + qglIndexPointer = NULL; + qglIndexd = NULL; + qglIndexdv = NULL; + qglIndexf = NULL; + qglIndexfv = NULL; + qglIndexi = NULL; + qglIndexiv = NULL; + qglIndexs = NULL; + qglIndexsv = NULL; + qglIndexub = NULL; + qglIndexubv = NULL; + qglInitNames = NULL; + qglInterleavedArrays = NULL; + qglIsEnabled = NULL; + qglIsList = NULL; + qglIsTexture = NULL; + qglLightModelf = NULL; + qglLightModelfv = NULL; + qglLightModeli = NULL; + qglLightModeliv = NULL; + qglLightf = NULL; + qglLightfv = NULL; + qglLighti = NULL; + qglLightiv = NULL; + qglLineStipple = NULL; + qglLineWidth = NULL; + qglListBase = NULL; + qglLoadIdentity = NULL; + qglLoadMatrixd = NULL; + qglLoadMatrixf = NULL; + qglLoadName = NULL; + qglLogicOp = NULL; + qglMap1d = NULL; + qglMap1f = NULL; + qglMap2d = NULL; + qglMap2f = NULL; + qglMapGrid1d = NULL; + qglMapGrid1f = NULL; + qglMapGrid2d = NULL; + qglMapGrid2f = NULL; + qglMaterialf = NULL; + qglMaterialfv = NULL; + qglMateriali = NULL; + qglMaterialiv = NULL; + qglMatrixMode = NULL; + qglMultMatrixd = NULL; + qglMultMatrixf = NULL; + qglNewList = NULL; + qglNormal3b = NULL; + qglNormal3bv = NULL; + qglNormal3d = NULL; + qglNormal3dv = NULL; + qglNormal3f = NULL; + qglNormal3fv = NULL; + qglNormal3i = NULL; + qglNormal3iv = NULL; + qglNormal3s = NULL; + qglNormal3sv = NULL; + qglNormalPointer = NULL; + qglOrtho = NULL; + qglPassThrough = NULL; + qglPixelMapfv = NULL; + qglPixelMapuiv = NULL; + qglPixelMapusv = NULL; + qglPixelStoref = NULL; + qglPixelStorei = NULL; + qglPixelTransferf = NULL; + qglPixelTransferi = NULL; + qglPixelZoom = NULL; + qglPointSize = NULL; + qglPolygonMode = NULL; + qglPolygonOffset = NULL; + qglPolygonStipple = NULL; + qglPopAttrib = NULL; + qglPopClientAttrib = NULL; + qglPopMatrix = NULL; + qglPopName = NULL; + qglPrioritizeTextures = NULL; + qglPushAttrib = NULL; + qglPushClientAttrib = NULL; + qglPushMatrix = NULL; + qglPushName = NULL; + qglRasterPos2d = NULL; + qglRasterPos2dv = NULL; + qglRasterPos2f = NULL; + qglRasterPos2fv = NULL; + qglRasterPos2i = NULL; + qglRasterPos2iv = NULL; + qglRasterPos2s = NULL; + qglRasterPos2sv = NULL; + qglRasterPos3d = NULL; + qglRasterPos3dv = NULL; + qglRasterPos3f = NULL; + qglRasterPos3fv = NULL; + qglRasterPos3i = NULL; + qglRasterPos3iv = NULL; + qglRasterPos3s = NULL; + qglRasterPos3sv = NULL; + qglRasterPos4d = NULL; + qglRasterPos4dv = NULL; + qglRasterPos4f = NULL; + qglRasterPos4fv = NULL; + qglRasterPos4i = NULL; + qglRasterPos4iv = NULL; + qglRasterPos4s = NULL; + qglRasterPos4sv = NULL; + qglReadBuffer = NULL; + qglReadPixels = NULL; + qglRectd = NULL; + qglRectdv = NULL; + qglRectf = NULL; + qglRectfv = NULL; + qglRecti = NULL; + qglRectiv = NULL; + qglRects = NULL; + qglRectsv = NULL; + qglRenderMode = NULL; + qglRotated = NULL; + qglRotatef = NULL; + qglScaled = NULL; + qglScalef = NULL; + qglScissor = NULL; + qglSelectBuffer = NULL; + qglShadeModel = NULL; + qglStencilFunc = NULL; + qglStencilMask = NULL; + qglStencilOp = NULL; + qglTexCoord1d = NULL; + qglTexCoord1dv = NULL; + qglTexCoord1f = NULL; + qglTexCoord1fv = NULL; + qglTexCoord1i = NULL; + qglTexCoord1iv = NULL; + qglTexCoord1s = NULL; + qglTexCoord1sv = NULL; + qglTexCoord2d = NULL; + qglTexCoord2dv = NULL; + qglTexCoord2f = NULL; + qglTexCoord2fv = NULL; + qglTexCoord2i = NULL; + qglTexCoord2iv = NULL; + qglTexCoord2s = NULL; + qglTexCoord2sv = NULL; + qglTexCoord3d = NULL; + qglTexCoord3dv = NULL; + qglTexCoord3f = NULL; + qglTexCoord3fv = NULL; + qglTexCoord3i = NULL; + qglTexCoord3iv = NULL; + qglTexCoord3s = NULL; + qglTexCoord3sv = NULL; + qglTexCoord4d = NULL; + qglTexCoord4dv = NULL; + qglTexCoord4f = NULL; + qglTexCoord4fv = NULL; + qglTexCoord4i = NULL; + qglTexCoord4iv = NULL; + qglTexCoord4s = NULL; + qglTexCoord4sv = NULL; + qglTexCoordPointer = NULL; + qglTexEnvf = NULL; + qglTexEnvfv = NULL; + qglTexEnvi = NULL; + qglTexEnviv = NULL; + qglTexGend = NULL; + qglTexGendv = NULL; + qglTexGenf = NULL; + qglTexGenfv = NULL; + qglTexGeni = NULL; + qglTexGeniv = NULL; + qglTexImage1D = NULL; + qglTexImage2D = NULL; + qglTexParameterf = NULL; + qglTexParameterfv = NULL; + qglTexParameteri = NULL; + qglTexParameteriv = NULL; + qglTexSubImage1D = NULL; + qglTexSubImage2D = NULL; + qglTranslated = NULL; + qglTranslatef = NULL; + qglVertex2d = NULL; + qglVertex2dv = NULL; + qglVertex2f = NULL; + qglVertex2fv = NULL; + qglVertex2i = NULL; + qglVertex2iv = NULL; + qglVertex2s = NULL; + qglVertex2sv = NULL; + qglVertex3d = NULL; + qglVertex3dv = NULL; + qglVertex3f = NULL; + qglVertex3fv = NULL; + qglVertex3i = NULL; + qglVertex3iv = NULL; + qglVertex3s = NULL; + qglVertex3sv = NULL; + qglVertex4d = NULL; + qglVertex4dv = NULL; + qglVertex4f = NULL; + qglVertex4fv = NULL; + qglVertex4i = NULL; + qglVertex4iv = NULL; + qglVertex4s = NULL; + qglVertex4sv = NULL; + qglVertexPointer = NULL; + qglViewport = NULL; + + qwglCopyContext = NULL; + qwglCreateContext = NULL; + qwglCreateLayerContext = NULL; + qwglDeleteContext = NULL; + qwglDescribeLayerPlane = NULL; + qwglGetCurrentContext = NULL; + qwglGetCurrentDC = NULL; + qwglGetLayerPaletteEntries = NULL; + qwglGetProcAddress = NULL; + qwglMakeCurrent = NULL; + qwglRealizeLayerPalette = NULL; + qwglSetLayerPaletteEntries = NULL; + qwglShareLists = NULL; + qwglSwapLayerBuffers = NULL; + qwglUseFontBitmaps = NULL; + qwglUseFontOutlines = NULL; + + qwglChoosePixelFormat = NULL; + qwglDescribePixelFormat = NULL; + qwglGetPixelFormat = NULL; + qwglSetPixelFormat = NULL; + qwglSwapBuffers = NULL; + + qwglSwapIntervalEXT = NULL; + + qwglGetDeviceGammaRampEXT = NULL; + qwglSetDeviceGammaRampEXT = NULL; + + qgluPerspective = NULL; + qgluBuild2DMipmaps = NULL; + +} + +# define GPA(h, a ) GetProcAddress( h, a ) + +/* +** QGL_Init +** +** This is responsible for binding our qgl function pointers to +** the appropriate GL stuff. In Windows this means doing a +** LoadLibrary and a bunch of calls to GetProcAddress. On other +** operating systems we need to do the right thing, whatever that +** might be. +** +*/ +int QGL_Init(const char *dllname, const char* pGluName ) +{ + + g_hGLDLL = LoadLibrary(dllname); + if (g_hGLDLL == NULL) + { + return 0; + } + + g_hGLUDLL = LoadLibrary(pGluName); + if (g_hGLUDLL == NULL) + { + FreeLibrary(g_hGLDLL); + g_hGLDLL = NULL; + return 0; + } + + + qgluPerspective = GPA(g_hGLUDLL, "gluPerspective"); + qgluBuild2DMipmaps = GPA(g_hGLUDLL, "gluBuild2DMipmaps"); + + qglAccum = GPA(g_hGLDLL, "glAccum" ); + qglAlphaFunc = GPA(g_hGLDLL, "glAlphaFunc" ); + qglAreTexturesResident = GPA(g_hGLDLL, "glAreTexturesResident" ); + qglArrayElement = GPA(g_hGLDLL, "glArrayElement" ); + qglBegin = GPA(g_hGLDLL, "glBegin" ); + qglBindTexture = GPA(g_hGLDLL, "glBindTexture" ); + qglBitmap = GPA(g_hGLDLL, "glBitmap" ); + qglBlendFunc = GPA(g_hGLDLL, "glBlendFunc" ); + qglCallList = GPA(g_hGLDLL, "glCallList" ); + qglCallLists = GPA(g_hGLDLL, "glCallLists" ); + qglClear = GPA(g_hGLDLL, "glClear" ); + qglClearAccum = GPA(g_hGLDLL, "glClearAccum" ); + qglClearColor = GPA(g_hGLDLL, "glClearColor" ); + qglClearDepth = GPA(g_hGLDLL, "glClearDepth" ); + qglClearIndex = GPA(g_hGLDLL, "glClearIndex" ); + qglClearStencil = GPA(g_hGLDLL, "glClearStencil" ); + qglClipPlane = GPA(g_hGLDLL, "glClipPlane" ); + qglColor3b = GPA(g_hGLDLL, "glColor3b" ); + qglColor3bv = GPA(g_hGLDLL, "glColor3bv" ); + qglColor3d = GPA(g_hGLDLL, "glColor3d" ); + qglColor3dv = GPA(g_hGLDLL, "glColor3dv" ); + qglColor3f = GPA(g_hGLDLL, "glColor3f" ); + qglColor3fv = GPA(g_hGLDLL, "glColor3fv" ); + qglColor3i = GPA(g_hGLDLL, "glColor3i" ); + qglColor3iv = GPA(g_hGLDLL, "glColor3iv" ); + qglColor3s = GPA(g_hGLDLL, "glColor3s" ); + qglColor3sv = GPA(g_hGLDLL, "glColor3sv" ); + qglColor3ub = GPA(g_hGLDLL, "glColor3ub" ); + qglColor3ubv = GPA(g_hGLDLL, "glColor3ubv" ); + qglColor3ui = GPA(g_hGLDLL, "glColor3ui" ); + qglColor3uiv = GPA(g_hGLDLL, "glColor3uiv" ); + qglColor3us = GPA(g_hGLDLL, "glColor3us" ); + qglColor3usv = GPA(g_hGLDLL, "glColor3usv" ); + qglColor4b = GPA(g_hGLDLL, "glColor4b" ); + qglColor4bv = GPA(g_hGLDLL, "glColor4bv" ); + qglColor4d = GPA(g_hGLDLL, "glColor4d" ); + qglColor4dv = GPA(g_hGLDLL, "glColor4dv" ); + qglColor4f = GPA(g_hGLDLL, "glColor4f" ); + qglColor4fv = GPA(g_hGLDLL, "glColor4fv" ); + qglColor4i = GPA(g_hGLDLL, "glColor4i" ); + qglColor4iv = GPA(g_hGLDLL, "glColor4iv" ); + qglColor4s = GPA(g_hGLDLL, "glColor4s" ); + qglColor4sv = GPA(g_hGLDLL, "glColor4sv" ); + qglColor4ub = GPA(g_hGLDLL, "glColor4ub" ); + qglColor4ubv = GPA(g_hGLDLL, "glColor4ubv" ); + qglColor4ui = GPA(g_hGLDLL, "glColor4ui" ); + qglColor4uiv = GPA(g_hGLDLL, "glColor4uiv" ); + qglColor4us = GPA(g_hGLDLL, "glColor4us" ); + qglColor4usv = GPA(g_hGLDLL, "glColor4usv" ); + qglColorMask = GPA(g_hGLDLL, "glColorMask" ); + qglColorMaterial = GPA(g_hGLDLL, "glColorMaterial" ); + qglColorPointer = GPA(g_hGLDLL, "glColorPointer" ); + qglCopyPixels = GPA(g_hGLDLL, "glCopyPixels" ); + qglCopyTexImage1D = GPA(g_hGLDLL, "glCopyTexImage1D" ); + qglCopyTexImage2D = GPA(g_hGLDLL, "glCopyTexImage2D" ); + qglCopyTexSubImage1D = GPA(g_hGLDLL, "glCopyTexSubImage1D" ); + qglCopyTexSubImage2D = GPA(g_hGLDLL, "glCopyTexSubImage2D" ); + qglCullFace = GPA(g_hGLDLL, "glCullFace" ); + qglDeleteLists = GPA(g_hGLDLL, "glDeleteLists" ); + qglDeleteTextures = GPA(g_hGLDLL, "glDeleteTextures" ); + qglDepthFunc = GPA(g_hGLDLL, "glDepthFunc" ); + qglDepthMask = GPA(g_hGLDLL, "glDepthMask" ); + qglDepthRange = GPA(g_hGLDLL, "glDepthRange" ); + qglDisable = GPA(g_hGLDLL, "glDisable" ); + qglDisableClientState = GPA(g_hGLDLL, "glDisableClientState" ); + qglDrawArrays = GPA(g_hGLDLL, "glDrawArrays" ); + qglDrawBuffer = GPA(g_hGLDLL, "glDrawBuffer" ); + qglDrawElements = GPA(g_hGLDLL, "glDrawElements" ); + qglDrawPixels = GPA(g_hGLDLL, "glDrawPixels" ); + qglEdgeFlag = GPA(g_hGLDLL, "glEdgeFlag" ); + qglEdgeFlagPointer = GPA(g_hGLDLL, "glEdgeFlagPointer" ); + qglEdgeFlagv = GPA(g_hGLDLL, "glEdgeFlagv" ); + qglEnable = GPA(g_hGLDLL, "glEnable" ); + qglEnableClientState = GPA(g_hGLDLL, "glEnableClientState" ); + qglEnd = GPA(g_hGLDLL, "glEnd" ); + qglEndList = GPA(g_hGLDLL, "glEndList" ); + qglEvalCoord1d = GPA(g_hGLDLL, "glEvalCoord1d" ); + qglEvalCoord1dv = GPA(g_hGLDLL, "glEvalCoord1dv" ); + qglEvalCoord1f = GPA(g_hGLDLL, "glEvalCoord1f" ); + qglEvalCoord1fv = GPA(g_hGLDLL, "glEvalCoord1fv" ); + qglEvalCoord2d = GPA(g_hGLDLL, "glEvalCoord2d" ); + qglEvalCoord2dv = GPA(g_hGLDLL, "glEvalCoord2dv" ); + qglEvalCoord2f = GPA(g_hGLDLL, "glEvalCoord2f" ); + qglEvalCoord2fv = GPA(g_hGLDLL, "glEvalCoord2fv" ); + qglEvalMesh1 = GPA(g_hGLDLL, "glEvalMesh1" ); + qglEvalMesh2 = GPA(g_hGLDLL, "glEvalMesh2" ); + qglEvalPoint1 = GPA(g_hGLDLL, "glEvalPoint1" ); + qglEvalPoint2 = GPA(g_hGLDLL, "glEvalPoint2" ); + qglFeedbackBuffer = GPA(g_hGLDLL, "glFeedbackBuffer" ); + qglFinish = GPA(g_hGLDLL, "glFinish" ); + qglFlush = GPA(g_hGLDLL, "glFlush" ); + qglFogf = GPA(g_hGLDLL, "glFogf" ); + qglFogfv = GPA(g_hGLDLL, "glFogfv" ); + qglFogi = GPA(g_hGLDLL, "glFogi" ); + qglFogiv = GPA(g_hGLDLL, "glFogiv" ); + qglFrontFace = GPA(g_hGLDLL, "glFrontFace" ); + qglFrustum = GPA(g_hGLDLL, "glFrustum" ); + qglGenLists = GPA(g_hGLDLL, "glGenLists" ); + qglGenTextures = GPA(g_hGLDLL, "glGenTextures" ); + qglGetBooleanv = GPA(g_hGLDLL, "glGetBooleanv" ); + qglGetClipPlane = GPA(g_hGLDLL, "glGetClipPlane" ); + qglGetDoublev = GPA(g_hGLDLL, "glGetDoublev" ); + qglGetError = GPA(g_hGLDLL, "glGetError" ); + qglGetFloatv = GPA(g_hGLDLL, "glGetFloatv" ); + qglGetIntegerv = GPA(g_hGLDLL, "glGetIntegerv" ); + qglGetLightfv = GPA(g_hGLDLL, "glGetLightfv" ); + qglGetLightiv = GPA(g_hGLDLL, "glGetLightiv" ); + qglGetMapdv = GPA(g_hGLDLL, "glGetMapdv" ); + qglGetMapfv = GPA(g_hGLDLL, "glGetMapfv" ); + qglGetMapiv = GPA(g_hGLDLL, "glGetMapiv" ); + qglGetMaterialfv = GPA(g_hGLDLL, "glGetMaterialfv" ); + qglGetMaterialiv = GPA(g_hGLDLL, "glGetMaterialiv" ); + qglGetPixelMapfv = GPA(g_hGLDLL, "glGetPixelMapfv" ); + qglGetPixelMapuiv = GPA(g_hGLDLL, "glGetPixelMapuiv" ); + qglGetPixelMapusv = GPA(g_hGLDLL, "glGetPixelMapusv" ); + qglGetPointerv = GPA(g_hGLDLL, "glGetPointerv" ); + qglGetPolygonStipple = GPA(g_hGLDLL, "glGetPolygonStipple" ); + qglGetString = GPA(g_hGLDLL, "glGetString" ); + qglGetTexEnvfv = GPA(g_hGLDLL, "glGetTexEnvfv" ); + qglGetTexEnviv = GPA(g_hGLDLL, "glGetTexEnviv" ); + qglGetTexGendv = GPA(g_hGLDLL, "glGetTexGendv" ); + qglGetTexGenfv = GPA(g_hGLDLL, "glGetTexGenfv" ); + qglGetTexGeniv = GPA(g_hGLDLL, "glGetTexGeniv" ); + qglGetTexImage = GPA(g_hGLDLL, "glGetTexImage" ); + qglGetTexLevelParameterfv = GPA(g_hGLDLL, "glGetLevelParameterfv" ); + qglGetTexLevelParameteriv = GPA(g_hGLDLL, "glGetLevelParameteriv" ); + qglGetTexParameterfv = GPA(g_hGLDLL, "glGetTexParameterfv" ); + qglGetTexParameteriv = GPA(g_hGLDLL, "glGetTexParameteriv" ); + qglHint = GPA(g_hGLDLL, "glHint" ); + qglIndexMask = GPA(g_hGLDLL, "glIndexMask" ); + qglIndexPointer = GPA(g_hGLDLL, "glIndexPointer" ); + qglIndexd = GPA(g_hGLDLL, "glIndexd" ); + qglIndexdv = GPA(g_hGLDLL, "glIndexdv" ); + qglIndexf = GPA(g_hGLDLL, "glIndexf" ); + qglIndexfv = GPA(g_hGLDLL, "glIndexfv" ); + qglIndexi = GPA(g_hGLDLL, "glIndexi" ); + qglIndexiv = GPA(g_hGLDLL, "glIndexiv" ); + qglIndexs = GPA(g_hGLDLL, "glIndexs" ); + qglIndexsv = GPA(g_hGLDLL, "glIndexsv" ); + qglIndexub = GPA(g_hGLDLL, "glIndexub" ); + qglIndexubv = GPA(g_hGLDLL, "glIndexubv" ); + qglInitNames = GPA(g_hGLDLL, "glInitNames" ); + qglInterleavedArrays = GPA(g_hGLDLL, "glInterleavedArrays" ); + qglIsEnabled = GPA(g_hGLDLL, "glIsEnabled" ); + qglIsList = GPA(g_hGLDLL, "glIsList" ); + qglIsTexture = GPA(g_hGLDLL, "glIsTexture" ); + qglLightModelf = GPA(g_hGLDLL, "glLightModelf" ); + qglLightModelfv = GPA(g_hGLDLL, "glLightModelfv" ); + qglLightModeli = GPA(g_hGLDLL, "glLightModeli" ); + qglLightModeliv = GPA(g_hGLDLL, "glLightModeliv" ); + qglLightf = GPA(g_hGLDLL, "glLightf" ); + qglLightfv = GPA(g_hGLDLL, "glLightfv" ); + qglLighti = GPA(g_hGLDLL, "glLighti" ); + qglLightiv = GPA(g_hGLDLL, "glLightiv" ); + qglLineStipple = GPA(g_hGLDLL, "glLineStipple" ); + qglLineWidth = GPA(g_hGLDLL, "glLineWidth" ); + qglListBase = GPA(g_hGLDLL, "glListBase" ); + qglLoadIdentity = GPA(g_hGLDLL, "glLoadIdentity" ); + qglLoadMatrixd = GPA(g_hGLDLL, "glLoadMatrixd" ); + qglLoadMatrixf = GPA(g_hGLDLL, "glLoadMatrixf" ); + qglLoadName = GPA(g_hGLDLL, "glLoadName" ); + qglLogicOp = GPA(g_hGLDLL, "glLogicOp" ); + qglMap1d = GPA(g_hGLDLL, "glMap1d" ); + qglMap1f = GPA(g_hGLDLL, "glMap1f" ); + qglMap2d = GPA(g_hGLDLL, "glMap2d" ); + qglMap2f = GPA(g_hGLDLL, "glMap2f" ); + qglMapGrid1d = GPA(g_hGLDLL, "glMapGrid1d" ); + qglMapGrid1f = GPA(g_hGLDLL, "glMapGrid1f" ); + qglMapGrid2d = GPA(g_hGLDLL, "glMapGrid2d" ); + qglMapGrid2f = GPA(g_hGLDLL, "glMapGrid2f" ); + qglMaterialf = GPA(g_hGLDLL, "glMaterialf" ); + qglMaterialfv = GPA(g_hGLDLL, "glMaterialfv" ); + qglMateriali = GPA(g_hGLDLL, "glMateriali" ); + qglMaterialiv = GPA(g_hGLDLL, "glMaterialiv" ); + qglMatrixMode = GPA(g_hGLDLL, "glMatrixMode" ); + qglMultMatrixd = GPA(g_hGLDLL, "glMultMatrixd" ); + qglMultMatrixf = GPA(g_hGLDLL, "glMultMatrixf" ); + qglNewList = GPA(g_hGLDLL, "glNewList" ); + qglNormal3b = GPA(g_hGLDLL, "glNormal3b" ); + qglNormal3bv = GPA(g_hGLDLL, "glNormal3bv" ); + qglNormal3d = GPA(g_hGLDLL, "glNormal3d" ); + qglNormal3dv = GPA(g_hGLDLL, "glNormal3dv" ); + qglNormal3f = GPA(g_hGLDLL, "glNormal3f" ); + qglNormal3fv = GPA(g_hGLDLL, "glNormal3fv" ); + qglNormal3i = GPA(g_hGLDLL, "glNormal3i" ); + qglNormal3iv = GPA(g_hGLDLL, "glNormal3iv" ); + qglNormal3s = GPA(g_hGLDLL, "glNormal3s" ); + qglNormal3sv = GPA(g_hGLDLL, "glNormal3sv" ); + qglNormalPointer = GPA(g_hGLDLL, "glNormalPointer" ); + qglOrtho = GPA(g_hGLDLL, "glOrtho" ); + qglPassThrough = GPA(g_hGLDLL, "glPassThrough" ); + qglPixelMapfv = GPA(g_hGLDLL, "glPixelMapfv" ); + qglPixelMapuiv = GPA(g_hGLDLL, "glPixelMapuiv" ); + qglPixelMapusv = GPA(g_hGLDLL, "glPixelMapusv" ); + qglPixelStoref = GPA(g_hGLDLL, "glPixelStoref" ); + qglPixelStorei = GPA(g_hGLDLL, "glPixelStorei" ); + qglPixelTransferf = GPA(g_hGLDLL, "glPixelTransferf" ); + qglPixelTransferi = GPA(g_hGLDLL, "glPixelTransferi" ); + qglPixelZoom = GPA(g_hGLDLL, "glPixelZoom" ); + qglPointSize = GPA(g_hGLDLL, "glPointSize" ); + qglPolygonMode = GPA(g_hGLDLL, "glPolygonMode" ); + qglPolygonOffset = GPA(g_hGLDLL, "glPolygonOffset" ); + qglPolygonStipple = GPA(g_hGLDLL, "glPolygonStipple" ); + qglPopAttrib = GPA(g_hGLDLL, "glPopAttrib" ); + qglPopClientAttrib = GPA(g_hGLDLL, "glPopClientAttrib" ); + qglPopMatrix = GPA(g_hGLDLL, "glPopMatrix" ); + qglPopName = GPA(g_hGLDLL, "glPopName" ); + qglPrioritizeTextures = GPA(g_hGLDLL, "glPrioritizeTextures" ); + qglPushAttrib = GPA(g_hGLDLL, "glPushAttrib" ); + qglPushClientAttrib = GPA(g_hGLDLL, "glPushClientAttrib" ); + qglPushMatrix = GPA(g_hGLDLL, "glPushMatrix" ); + qglPushName = GPA(g_hGLDLL, "glPushName" ); + qglRasterPos2d = GPA(g_hGLDLL, "glRasterPos2d" ); + qglRasterPos2dv = GPA(g_hGLDLL, "glRasterPos2dv" ); + qglRasterPos2f = GPA(g_hGLDLL, "glRasterPos2f" ); + qglRasterPos2fv = GPA(g_hGLDLL, "glRasterPos2fv" ); + qglRasterPos2i = GPA(g_hGLDLL, "glRasterPos2i" ); + qglRasterPos2iv = GPA(g_hGLDLL, "glRasterPos2iv" ); + qglRasterPos2s = GPA(g_hGLDLL, "glRasterPos2s" ); + qglRasterPos2sv = GPA(g_hGLDLL, "glRasterPos2sv" ); + qglRasterPos3d = GPA(g_hGLDLL, "glRasterPos3d" ); + qglRasterPos3dv = GPA(g_hGLDLL, "glRasterPos3dv" ); + qglRasterPos3f = GPA(g_hGLDLL, "glRasterPos3f" ); + qglRasterPos3fv = GPA(g_hGLDLL, "glRasterPos3fv" ); + qglRasterPos3i = GPA(g_hGLDLL, "glRasterPos3i" ); + qglRasterPos3iv = GPA(g_hGLDLL, "glRasterPos3iv" ); + qglRasterPos3s = GPA(g_hGLDLL, "glRasterPos3s" ); + qglRasterPos3sv = GPA(g_hGLDLL, "glRasterPos3sv" ); + qglRasterPos4d = GPA(g_hGLDLL, "glRasterPos4d" ); + qglRasterPos4dv = GPA(g_hGLDLL, "glRasterPos4dv" ); + qglRasterPos4f = GPA(g_hGLDLL, "glRasterPos4f" ); + qglRasterPos4fv = GPA(g_hGLDLL, "glRasterPos4fv" ); + qglRasterPos4i = GPA(g_hGLDLL, "glRasterPos4i" ); + qglRasterPos4iv = GPA(g_hGLDLL, "glRasterPos4iv" ); + qglRasterPos4s = GPA(g_hGLDLL, "glRasterPos4s" ); + qglRasterPos4sv = GPA(g_hGLDLL, "glRasterPos4sv" ); + qglReadBuffer = GPA(g_hGLDLL, "glReadBuffer" ); + qglReadPixels = GPA(g_hGLDLL, "glReadPixels" ); + qglRectd = GPA(g_hGLDLL, "glRectd" ); + qglRectdv = GPA(g_hGLDLL, "glRectdv" ); + qglRectf = GPA(g_hGLDLL, "glRectf" ); + qglRectfv = GPA(g_hGLDLL, "glRectfv" ); + qglRecti = GPA(g_hGLDLL, "glRecti" ); + qglRectiv = GPA(g_hGLDLL, "glRectiv" ); + qglRects = GPA(g_hGLDLL, "glRects" ); + qglRectsv = GPA(g_hGLDLL, "glRectsv" ); + qglRenderMode = GPA(g_hGLDLL, "glRenderMode" ); + qglRotated = GPA(g_hGLDLL, "glRotated" ); + qglRotatef = GPA(g_hGLDLL, "glRotatef" ); + qglScaled = GPA(g_hGLDLL, "glScaled" ); + qglScalef = GPA(g_hGLDLL, "glScalef" ); + qglScissor = GPA(g_hGLDLL, "glScissor" ); + qglSelectBuffer = GPA(g_hGLDLL, "glSelectBuffer" ); + qglShadeModel = GPA(g_hGLDLL, "glShadeModel" ); + qglStencilFunc = GPA(g_hGLDLL, "glStencilFunc" ); + qglStencilMask = GPA(g_hGLDLL, "glStencilMask" ); + qglStencilOp = GPA(g_hGLDLL, "glStencilOp" ); + qglTexCoord1d = GPA(g_hGLDLL, "glTexCoord1d" ); + qglTexCoord1dv = GPA(g_hGLDLL, "glTexCoord1dv" ); + qglTexCoord1f = GPA(g_hGLDLL, "glTexCoord1f" ); + qglTexCoord1fv = GPA(g_hGLDLL, "glTexCoord1fv" ); + qglTexCoord1i = GPA(g_hGLDLL, "glTexCoord1i" ); + qglTexCoord1iv = GPA(g_hGLDLL, "glTexCoord1iv" ); + qglTexCoord1s = GPA(g_hGLDLL, "glTexCoord1s" ); + qglTexCoord1sv = GPA(g_hGLDLL, "glTexCoord1sv" ); + qglTexCoord2d = GPA(g_hGLDLL, "glTexCoord2d" ); + qglTexCoord2dv = GPA(g_hGLDLL, "glTexCoord2dv" ); + qglTexCoord2f = GPA(g_hGLDLL, "glTexCoord2f" ); + qglTexCoord2fv = GPA(g_hGLDLL, "glTexCoord2fv" ); + qglTexCoord2i = GPA(g_hGLDLL, "glTexCoord2i" ); + qglTexCoord2iv = GPA(g_hGLDLL, "glTexCoord2iv" ); + qglTexCoord2s = GPA(g_hGLDLL, "glTexCoord2s" ); + qglTexCoord2sv = GPA(g_hGLDLL, "glTexCoord2sv" ); + qglTexCoord3d = GPA(g_hGLDLL, "glTexCoord3d" ); + qglTexCoord3dv = GPA(g_hGLDLL, "glTexCoord3dv" ); + qglTexCoord3f = GPA(g_hGLDLL, "glTexCoord3f" ); + qglTexCoord3fv = GPA(g_hGLDLL, "glTexCoord3fv" ); + qglTexCoord3i = GPA(g_hGLDLL, "glTexCoord3i" ); + qglTexCoord3iv = GPA(g_hGLDLL, "glTexCoord3iv" ); + qglTexCoord3s = GPA(g_hGLDLL, "glTexCoord3s" ); + qglTexCoord3sv = GPA(g_hGLDLL, "glTexCoord3sv" ); + qglTexCoord4d = GPA(g_hGLDLL, "glTexCoord4d" ); + qglTexCoord4dv = GPA(g_hGLDLL, "glTexCoord4dv" ); + qglTexCoord4f = GPA(g_hGLDLL, "glTexCoord4f" ); + qglTexCoord4fv = GPA(g_hGLDLL, "glTexCoord4fv" ); + qglTexCoord4i = GPA(g_hGLDLL, "glTexCoord4i" ); + qglTexCoord4iv = GPA(g_hGLDLL, "glTexCoord4iv" ); + qglTexCoord4s = GPA(g_hGLDLL, "glTexCoord4s" ); + qglTexCoord4sv = GPA(g_hGLDLL, "glTexCoord4sv" ); + qglTexCoordPointer = GPA(g_hGLDLL, "glTexCoordPointer" ); + qglTexEnvf = GPA(g_hGLDLL, "glTexEnvf" ); + qglTexEnvfv = GPA(g_hGLDLL, "glTexEnvfv" ); + qglTexEnvi = GPA(g_hGLDLL, "glTexEnvi" ); + qglTexEnviv = GPA(g_hGLDLL, "glTexEnviv" ); + qglTexGend = GPA(g_hGLDLL, "glTexGend" ); + qglTexGendv = GPA(g_hGLDLL, "glTexGendv" ); + qglTexGenf = GPA(g_hGLDLL, "glTexGenf" ); + qglTexGenfv = GPA(g_hGLDLL, "glTexGenfv" ); + qglTexGeni = GPA(g_hGLDLL, "glTexGeni" ); + qglTexGeniv = GPA(g_hGLDLL, "glTexGeniv" ); + qglTexImage1D = GPA(g_hGLDLL, "glTexImage1D" ); + qglTexImage2D = GPA(g_hGLDLL, "glTexImage2D" ); + qglTexParameterf = GPA(g_hGLDLL, "glTexParameterf" ); + qglTexParameterfv = GPA(g_hGLDLL, "glTexParameterfv" ); + qglTexParameteri = GPA(g_hGLDLL, "glTexParameteri" ); + qglTexParameteriv = GPA(g_hGLDLL, "glTexParameteriv" ); + qglTexSubImage1D = GPA(g_hGLDLL, "glTexSubImage1D" ); + qglTexSubImage2D = GPA(g_hGLDLL, "glTexSubImage2D" ); + qglTranslated = GPA(g_hGLDLL, "glTranslated" ); + qglTranslatef = GPA(g_hGLDLL, "glTranslatef" ); + qglVertex2d = GPA(g_hGLDLL, "glVertex2d" ); + qglVertex2dv = GPA(g_hGLDLL, "glVertex2dv" ); + qglVertex2f = GPA(g_hGLDLL, "glVertex2f" ); + qglVertex2fv = GPA(g_hGLDLL, "glVertex2fv" ); + qglVertex2i = GPA(g_hGLDLL, "glVertex2i" ); + qglVertex2iv = GPA(g_hGLDLL, "glVertex2iv" ); + qglVertex2s = GPA(g_hGLDLL, "glVertex2s" ); + qglVertex2sv = GPA(g_hGLDLL, "glVertex2sv" ); + qglVertex3d = GPA(g_hGLDLL, "glVertex3d" ); + qglVertex3dv = GPA(g_hGLDLL, "glVertex3dv" ); + qglVertex3f = GPA(g_hGLDLL, "glVertex3f" ); + qglVertex3fv = GPA(g_hGLDLL, "glVertex3fv" ); + qglVertex3i = GPA(g_hGLDLL, "glVertex3i" ); + qglVertex3iv = GPA(g_hGLDLL, "glVertex3iv" ); + qglVertex3s = GPA(g_hGLDLL, "glVertex3s" ); + qglVertex3sv = GPA(g_hGLDLL, "glVertex3sv" ); + qglVertex4d = GPA(g_hGLDLL, "glVertex4d" ); + qglVertex4dv = GPA(g_hGLDLL, "glVertex4dv" ); + qglVertex4f = GPA(g_hGLDLL, "glVertex4f" ); + qglVertex4fv = GPA(g_hGLDLL, "glVertex4fv" ); + qglVertex4i = GPA(g_hGLDLL, "glVertex4i" ); + qglVertex4iv = GPA(g_hGLDLL, "glVertex4iv" ); + qglVertex4s = GPA(g_hGLDLL, "glVertex4s" ); + qglVertex4sv = GPA(g_hGLDLL, "glVertex4sv" ); + qglVertexPointer = GPA(g_hGLDLL, "glVertexPointer" ); + qglViewport = GPA(g_hGLDLL, "glViewport" ); + + qwglCopyContext = GPA(g_hGLDLL, "wglCopyContext" ); + qwglCreateContext = GPA(g_hGLDLL, "wglCreateContext" ); + qwglCreateLayerContext = GPA(g_hGLDLL, "wglCreateLayerContext" ); + qwglDeleteContext = GPA(g_hGLDLL, "wglDeleteContext" ); + qwglDescribeLayerPlane = GPA(g_hGLDLL, "wglDescribeLayerPlane" ); + qwglGetCurrentContext = GPA(g_hGLDLL, "wglGetCurrentContext" ); + qwglGetCurrentDC = GPA(g_hGLDLL, "wglGetCurrentDC" ); + qwglGetLayerPaletteEntries = GPA(g_hGLDLL, "wglGetLayerPaletteEntries" ); + qwglGetProcAddress = GPA(g_hGLDLL, "wglGetProcAddress" ); + qwglMakeCurrent = GPA(g_hGLDLL, "wglMakeCurrent" ); + qwglRealizeLayerPalette = GPA(g_hGLDLL, "wglRealizeLayerPalette" ); + qwglSetLayerPaletteEntries = GPA(g_hGLDLL, "wglSetLayerPaletteEntries" ); + qwglShareLists = GPA(g_hGLDLL, "wglShareLists" ); + qwglSwapLayerBuffers = GPA(g_hGLDLL, "wglSwapLayerBuffers" ); + qwglUseFontBitmaps = GPA(g_hGLDLL, "wglUseFontBitmapsA" ); + qwglUseFontOutlines = GPA(g_hGLDLL, "wglUseFontOutlinesA" ); + + qwglChoosePixelFormat = GPA(g_hGLDLL, "wglChoosePixelFormat" ); + qwglDescribePixelFormat = GPA(g_hGLDLL, "wglDescribePixelFormat" ); + qwglGetPixelFormat = GPA(g_hGLDLL, "wglGetPixelFormat" ); + qwglSetPixelFormat = GPA(g_hGLDLL, "wglSetPixelFormat" ); + qwglSwapBuffers = GPA(g_hGLDLL, "wglSwapBuffers" ); + + qwglSwapIntervalEXT = 0; + qglPointParameterfEXT = 0; + qglPointParameterfvEXT = 0; + qglColorTableEXT = 0; + qglSelectTextureSGIS = 0; + qglMTexCoord2fSGIS = 0; + + return 1; +} + +#pragma warning (default : 4113 4133 4047 ) + + + diff --git a/utils/Radiant/radbsp.cpp b/utils/Radiant/radbsp.cpp new file mode 100644 index 0000000..80c2dc8 --- /dev/null +++ b/utils/Radiant/radbsp.cpp @@ -0,0 +1,56 @@ +//----------------------------------------------------------------------------- +// #includes follow... +//----------------------------------------------------------------------------- +// +#include "StdAfx.h" // For: Precompiled Headers +// +//----------------------------------------------------------------------------- +// #defines, consts, etc follow... +//----------------------------------------------------------------------------- +// +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +// +//----------------------------------------------------------------------------- +// Implementation follows... +//----------------------------------------------------------------------------- +// + +// msvc 5.xx does not do well optimizing this code +#pragma optimize("",off) + +HINSTANCE g_hQBSPDLL = NULL; + +typedef void (WINAPI* PFN_QBSP)(char*, HWND, const char*); + +void RunTools(char* pCommandLine, HWND hwnd, const char* pPAKFile) +{ + static PFN_QBSP lpfnQBSP = NULL; + if (g_hQBSPDLL == NULL) + g_hQBSPDLL = ::LoadLibrary("qbspdll.dll"); + else + { + ::FreeLibrary(g_hQBSPDLL); + g_hQBSPDLL = ::LoadLibrary("qbspdll.dll"); + lpfnQBSP = NULL; + } + if (g_hQBSPDLL != NULL) + { + if (lpfnQBSP == NULL && g_hQBSPDLL != NULL) + { + FARPROC pFunction = ::GetProcAddress(g_hQBSPDLL, "FullProcess"); + lpfnQBSP = reinterpret_cast(pFunction); + } + if (lpfnQBSP != NULL) + { + (*lpfnQBSP)(pCommandLine, hwnd, pPAKFile); + } + } +} + + +#pragma optimize("",on) + diff --git a/utils/Radiant/radeditwnd.cpp b/utils/Radiant/radeditwnd.cpp new file mode 100644 index 0000000..e212aa9 --- /dev/null +++ b/utils/Radiant/radeditwnd.cpp @@ -0,0 +1,54 @@ +// RADEditWnd.cpp : implementation file +// + +#include "stdafx.h" +#include "Radiant.h" +#include "RADEditWnd.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CRADEditWnd + +CRADEditWnd::CRADEditWnd() +{ +} + +CRADEditWnd::~CRADEditWnd() +{ +} + + +BEGIN_MESSAGE_MAP(CRADEditWnd, CWnd) + //{{AFX_MSG_MAP(CRADEditWnd) + ON_WM_CREATE() + ON_WM_SIZE() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +///////////////////////////////////////////////////////////////////////////// +// CRADEditWnd message handlers + +int CRADEditWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CWnd::OnCreate(lpCreateStruct) == -1) + return -1; + + CRect rctClient; + GetClientRect(rctClient); + m_wndEdit.Create(WS_CHILD | WS_VSCROLL | ES_AUTOHSCROLL | ES_MULTILINE | WS_VISIBLE, rctClient, this, 101); + return 0; +} + +void CRADEditWnd::OnSize(UINT nType, int cx, int cy) +{ + CWnd::OnSize(nType, cx, cy); + CRect rctClient; + GetClientRect(rctClient); + m_wndEdit.SetWindowPos(NULL, rctClient.left, rctClient.top, rctClient.Width(), rctClient.Height(), SWP_SHOWWINDOW); +} diff --git a/utils/Radiant/radeditwnd.h b/utils/Radiant/radeditwnd.h new file mode 100644 index 0000000..b4bee34 --- /dev/null +++ b/utils/Radiant/radeditwnd.h @@ -0,0 +1,52 @@ +#if !defined(AFX_RADEDITWND_H__DC829124_812D_11D1_B548_00AA00A410FC__INCLUDED_) +#define AFX_RADEDITWND_H__DC829124_812D_11D1_B548_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// RADEditWnd.h : header file +// + +#include "EditWnd.h" +///////////////////////////////////////////////////////////////////////////// +// CRADEditWnd window + +class CRADEditWnd : public CWnd +{ +// Construction +public: + CRADEditWnd(); + +// Attributes +public: + CEdit* GetEditWnd() {return dynamic_cast(&m_wndEdit); }; +protected: + CEditWnd m_wndEdit; +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CRADEditWnd) + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CRADEditWnd(); + + + // Generated message map functions +protected: + //{{AFX_MSG(CRADEditWnd) + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnSize(UINT nType, int cx, int cy); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_RADEDITWND_H__DC829124_812D_11D1_B548_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/radiant.cpp b/utils/Radiant/radiant.cpp new file mode 100644 index 0000000..223ebc0 --- /dev/null +++ b/utils/Radiant/radiant.cpp @@ -0,0 +1,172 @@ +// Radiant.cpp : Defines the class behaviors for the application. +// + +#include "stdafx.h" +#include "Radiant.h" + +#include "MainFrm.h" +#include "ChildFrm.h" +#include "RadiantDoc.h" +#include "RadiantView.h" +#include "PrefsDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CRadiantApp + +BEGIN_MESSAGE_MAP(CRadiantApp, CWinApp) + //{{AFX_MSG_MAP(CRadiantApp) + // NOTE - the ClassWizard will add and remove mapping macros here. + // DO NOT EDIT what you see in these blocks of generated code! + //}}AFX_MSG_MAP + // Standard file based document commands + ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew) + ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen) + // Standard print setup command + ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup) +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CRadiantApp construction + +CRadiantApp::CRadiantApp(LPCTSTR lpszAppName/*=NULL*/) // app name defaults to EXE name +:CWinApp(lpszAppName) +{ + // TODO: add construction code here, + // Place all significant initialization in InitInstance +} + +///////////////////////////////////////////////////////////////////////////// +// The one and only CRadiantApp object + +#ifdef QUAKE3 +CRadiantApp theApp("Q3Trek_Radiant"); +#else +CRadiantApp theApp; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CRadiantApp initialization + +HINSTANCE g_hOpenGL32 = NULL; +HINSTANCE g_hOpenGL = NULL; +bool g_bBuildList = false; + +BOOL CRadiantApp::InitInstance() +{ + //g_hOpenGL32 = ::LoadLibrary("opengl32.dll"); + // AfxEnableControlContainer(); + + // Standard initialization + // If you are not using these features and wish to reduce the size + // of your final executable, you should remove from the following + // the specific initialization routines you do not need. + //AfxEnableMemoryTracking(FALSE); + +#ifdef _AFXDLL + Enable3dControls(); // Call this when using MFC in a shared DLL +#else + Enable3dControlsStatic(); // Call this when linking to MFC statically +#endif + + // Change the registry key under which our settings are stored. + // You should modify this string to be something appropriate + // such as the name of your company or organization. + SetRegistryKey("QERadiant"); + + LoadStdProfileSettings(_AFX_MRU_MAX_COUNT); // Load standard INI file options (including MRU) + + // Register the application's document templates. Document templates + // serve as the connection between documents, frame windows and views. + +// CMultiDocTemplate* pDocTemplate; +// pDocTemplate = new CMultiDocTemplate( +// IDR_RADIANTYPE, +// RUNTIME_CLASS(CRadiantDoc), +// RUNTIME_CLASS(CMainFrame), // custom MDI child frame +// RUNTIME_CLASS(CRadiantView)); +// AddDocTemplate(pDocTemplate); + + // create main MDI Frame window + + g_PrefsDlg.LoadPrefs(); + + CString strOpenGL = (g_PrefsDlg.m_bSGIOpenGL) ? "opengl.dll" : "opengl32.dll"; + CString strGLU = (g_PrefsDlg.m_bSGIOpenGL) ? "glu.dll" : "glu32.dll"; + + if (!QGL_Init(strOpenGL, strGLU)) + { + g_PrefsDlg.m_bSGIOpenGL ^= 1; + strOpenGL = (g_PrefsDlg.m_bSGIOpenGL) ? "opengl.dll" : "opengl32.dll"; + strGLU = (g_PrefsDlg.m_bSGIOpenGL) ? "glu.dll" : "glu32.dll"; + if (!QGL_Init(strOpenGL, strGLU)) + { + AfxMessageBox("Failed to load OpenGL libraries. \"OPENGL32.DLL\" and \"OPENGL.DLL\" were tried"); + return FALSE; + } + g_PrefsDlg.SavePrefs(); + } + + + CString strTemp = m_lpCmdLine; + strTemp.MakeLower(); + if (strTemp.Find("builddefs") >= 0) + g_bBuildList = true; + + + + CMainFrame* pMainFrame = new CMainFrame; + if (!pMainFrame->LoadFrame(IDR_MAINFRAME)) + return FALSE; + + if (pMainFrame->m_hAccelTable) + { + ::DestroyAcceleratorTable(pMainFrame->m_hAccelTable); + pMainFrame->m_hAccelTable=NULL; + } + + pMainFrame->LoadAccelTable(MAKEINTRESOURCE(IDR_MINIACCEL)); + + + m_pMainWnd = pMainFrame; + + // Parse command line for standard shell commands, DDE, file open + CCommandLineInfo cmdInfo; + ParseCommandLine(cmdInfo); + + if (cmdInfo.m_strFileName.CompareNoCase("builddefs")==-1) + { + // Dispatch commands specified on the command line..... if command line was not "builddefs" +// if (!ProcessShellCommand(cmdInfo)) +// return FALSE; + } + + // The main window has been initialized, so show and update it. + pMainFrame->ShowWindow(m_nCmdShow); + pMainFrame->UpdateWindow(); + + return TRUE; +} + +///////////////////////////////////////////////////////////////////////////// +// CRadiantApp commands + +int CRadiantApp::ExitInstance() +{ + // TODO: Add your specialized code here and/or call the base class + //::FreeLibrary(g_hOpenGL32); + QGL_Shutdown(); + return CWinApp::ExitInstance(); +} + +BOOL CRadiantApp::OnIdle(LONG lCount) +{ + if (g_pParentWnd) + g_pParentWnd->RoutineProcessing(); + return CWinApp::OnIdle(lCount); +} diff --git a/utils/Radiant/radiant.h b/utils/Radiant/radiant.h new file mode 100644 index 0000000..4524e39 --- /dev/null +++ b/utils/Radiant/radiant.h @@ -0,0 +1,51 @@ +// Radiant.h : main header file for the RADIANT application +// + +#if !defined(AFX_RADIANT_H__330BBF06_731C_11D1_B539_00AA00A410FC__INCLUDED_) +#define AFX_RADIANT_H__330BBF06_731C_11D1_B539_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +#ifndef __AFXWIN_H__ + #error include 'stdafx.h' before including this file for PCH +#endif + +#include "resource.h" // main symbols + +///////////////////////////////////////////////////////////////////////////// +// CRadiantApp: +// See Radiant.cpp for the implementation of this class +// + +class CRadiantApp : public CWinApp +{ +public: + CRadiantApp(LPCTSTR lpszAppName = NULL); // app name defaults to EXE name + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CRadiantApp) + public: + virtual BOOL InitInstance(); + virtual int ExitInstance(); + virtual BOOL OnIdle(LONG lCount); + //}}AFX_VIRTUAL + +// Implementation + + //{{AFX_MSG(CRadiantApp) + // NOTE - the ClassWizard will add and remove member functions here. + // DO NOT EDIT what you see in these blocks of generated code ! + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_RADIANT_H__330BBF06_731C_11D1_B539_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/radiant.rc b/utils/Radiant/radiant.rc new file mode 100644 index 0000000..de3d426 --- /dev/null +++ b/utils/Radiant/radiant.rc @@ -0,0 +1,2634 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "#define _AFX_NO_SPLITTER_RESOURCES\r\n" + "#define _AFX_NO_OLE_RESOURCES\r\n" + "#define _AFX_NO_TRACKER_RESOURCES\r\n" + "#define _AFX_NO_PROPERTY_RESOURCES\r\n" + "\r\n" + "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n" + "#ifdef _WIN32\r\n" + "LANGUAGE 9, 1\r\n" + "#pragma code_page(1252)\r\n" + "#endif\r\n" + "#include ""res\\Radiant.rc2"" // non-Microsoft Visual C++ edited resources\r\n" + "#include ""afxres.rc"" // Standard components\r\n" + "#include ""afxprint.rc"" // printing/print preview resources\r\n" + "#endif\0" +END + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDR_MAINFRAME ICON DISCARDABLE "res\\Radiant.ico" +IDR_RADIANTYPE ICON DISCARDABLE "res\\RadiantDoc.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDR_MAINFRAME BITMAP MOVEABLE PURE "res\\Toolbar.bmp" +IDR_TOOLBAR1 BITMAP DISCARDABLE "res\\toolbar1.bmp" +IDB_BITMAP1 BITMAP DISCARDABLE "res\\q.bmp" +IDB_VIEWDEFAULT BITMAP DISCARDABLE "res\\bitmap2.bmp" +IDB_VIEWQE4 BITMAP DISCARDABLE "res\\viewdefa.bmp" +IDB_VIEW4WAY BITMAP DISCARDABLE "res\\viewoppo.bmp" +IDB_VIEWDEFAULT_Z BITMAP DISCARDABLE "res\\bmp00001.bmp" +IDB_3DFX BITMAP DISCARDABLE "res\\logo_sm3dfx.bmp" +IDR_TOOLBAR_SCALELOCK BITMAP DISCARDABLE "res\\toolbar2.bmp" +IDR_TOOLBAR_ADVANCED BITMAP DISCARDABLE "res\\bmp00002.bmp" +IDB_IENDCAP BITMAP DISCARDABLE "res\\iendcap.bmp" +IDB_ENDCAP BITMAP DISCARDABLE "res\\endcap.bmp" +IDB_BEVEL BITMAP DISCARDABLE "res\\bevel.bmp" +IDB_IBEVEL BITMAP DISCARDABLE "res\\ibevel.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Toolbar +// + +IDR_MAINFRAME TOOLBAR DISCARDABLE 16, 15 +BEGIN + BUTTON ID_FILE_NEW + BUTTON ID_FILE_OPEN + BUTTON ID_FILE_SAVE + SEPARATOR + BUTTON ID_EDIT_CUT + BUTTON ID_EDIT_COPY + BUTTON ID_EDIT_PASTE + SEPARATOR + BUTTON ID_FILE_PRINT + BUTTON ID_APP_ABOUT + BUTTON ID_BUTTON32777 + BUTTON ID_BUTTON32778 +END + +IDR_TOOLBAR1 TOOLBAR DISCARDABLE 16, 15 +BEGIN + BUTTON ID_FILE_OPEN + BUTTON ID_FILE_SAVE + SEPARATOR + BUTTON ID_BRUSH_FLIPX + BUTTON ID_BRUSH_ROTATEX + BUTTON ID_BRUSH_FLIPY + BUTTON ID_BRUSH_ROTATEY + BUTTON ID_BRUSH_FLIPZ + BUTTON ID_BRUSH_ROTATEZ + SEPARATOR + BUTTON ID_POPUP_SELECTION + SEPARATOR + BUTTON ID_SELECTION_CSGSUBTRACT + BUTTON ID_SELECTION_MAKEHOLLOW + SEPARATOR + BUTTON ID_VIEW_CHANGE + SEPARATOR + BUTTON ID_TEXTURES_POPUP + SEPARATOR + BUTTON ID_VIEW_CAMERATOGGLE + BUTTON ID_VIEW_CAMERAUPDATE + BUTTON ID_VIEW_CUBICCLIPPING + SEPARATOR + BUTTON ID_VIEW_ENTITY + SEPARATOR + BUTTON ID_VIEW_CLIPPER + SEPARATOR + BUTTON ID_SELECT_MOUSEROTATE + SEPARATOR + BUTTON ID_SELECT_MOUSESCALE + BUTTON ID_SCALELOCKX + BUTTON ID_SCALELOCKY + BUTTON ID_SCALELOCKZ +END + +IDR_TOOLBAR_SCALELOCK TOOLBAR DISCARDABLE 16, 15 +BEGIN + BUTTON ID_SCALELOCKX + BUTTON ID_SCALELOCKY + BUTTON ID_SCALELOCKZ +END + +IDR_TOOLBAR_ADVANCED TOOLBAR DISCARDABLE 16, 15 +BEGIN + BUTTON ID_FILE_OPEN + BUTTON ID_FILE_SAVE + SEPARATOR + BUTTON ID_BRUSH_FLIPX + BUTTON ID_BRUSH_ROTATEX + BUTTON ID_BRUSH_FLIPY + BUTTON ID_BRUSH_ROTATEY + BUTTON ID_BRUSH_FLIPZ + BUTTON ID_BRUSH_ROTATEZ + SEPARATOR + BUTTON ID_SELECTION_SELECTCOMPLETETALL + BUTTON ID_SELECTION_SELECTTOUCHING + BUTTON ID_SELECTION_SELECTPARTIALTALL + BUTTON ID_SELECTION_SELECTINSIDE + SEPARATOR + BUTTON ID_SELECTION_CSGSUBTRACT + BUTTON ID_SELECTION_MAKEHOLLOW + SEPARATOR + BUTTON ID_VIEW_CHANGE + SEPARATOR + BUTTON ID_TEXTURES_POPUP + SEPARATOR + BUTTON ID_VIEW_CUBICCLIPPING + SEPARATOR + BUTTON ID_VIEW_CLIPPER + SEPARATOR + BUTTON ID_SELECT_MOUSEROTATE + SEPARATOR + BUTTON ID_SELECT_MOUSESCALE + BUTTON ID_SCALELOCKX + BUTTON ID_SCALELOCKY + BUTTON ID_SCALELOCKZ + SEPARATOR + BUTTON ID_DONTSELECTCURVE + BUTTON ID_PATCH_SHOWBOUNDINGBOX + BUTTON ID_PATCH_WIREFRAME + SEPARATOR + BUTTON ID_PATCH_BEND + BUTTON ID_PATCH_INSDEL + SEPARATOR + BUTTON ID_CURVE_CAP + SEPARATOR + BUTTON ID_PATCH_WELD + BUTTON ID_PATCH_DRILLDOWN + SEPARATOR + BUTTON ID_SHOW_ENTITIES +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_RADIANTYPE MENU PRELOAD DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&New\tCtrl+N", ID_FILE_NEW + MENUITEM "&Open...\tCtrl+O", ID_FILE_OPEN + MENUITEM "&Close", ID_FILE_CLOSE + MENUITEM "&Save\tCtrl+S", ID_FILE_SAVE + MENUITEM "Save &As...", ID_FILE_SAVE_AS + MENUITEM SEPARATOR + MENUITEM "&Print...\tCtrl+P", ID_FILE_PRINT + MENUITEM "Print Pre&view", ID_FILE_PRINT_PREVIEW + MENUITEM "P&rint Setup...", ID_FILE_PRINT_SETUP + MENUITEM SEPARATOR + MENUITEM "Recent File", ID_FILE_MRU_FILE1, GRAYED + MENUITEM SEPARATOR + MENUITEM "E&xit", ID_APP_EXIT + END + POPUP "&Edit" + BEGIN + MENUITEM "&Undo\tCtrl+Z", ID_EDIT_UNDO + MENUITEM SEPARATOR + MENUITEM "Cu&t\tCtrl+X", ID_EDIT_CUT + MENUITEM "&Copy\tCtrl+C", ID_EDIT_COPY + MENUITEM "&Paste\tCtrl+V", ID_EDIT_PASTE + END + POPUP "&View" + BEGIN + MENUITEM "&Toolbar", ID_VIEW_TOOLBAR + MENUITEM "&Status Bar", ID_VIEW_STATUS_BAR + END + POPUP "&Window" + BEGIN + MENUITEM "&New Window", ID_WINDOW_NEW + MENUITEM "&Cascade", ID_WINDOW_CASCADE + MENUITEM "&Tile", ID_WINDOW_TILE_HORZ + MENUITEM "&Arrange Icons", ID_WINDOW_ARRANGE + END + POPUP "&Help" + BEGIN + MENUITEM "&About Radiant...", ID_APP_ABOUT + END +END + +IDR_MENU_QUAKE3 MENU DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&New map", ID_FILE_NEW + MENUITEM SEPARATOR + MENUITEM "&Open...", ID_FILE_OPEN + MENUITEM "&Load...", ID_FILE_IMPORTMAP + MENUITEM "&Save", ID_FILE_SAVE + MENUITEM "Save &as...", ID_FILE_SAVEAS + MENUITEM "Save s&elected...", ID_FILE_EXPORTMAP + MENUITEM SEPARATOR + MENUITEM "Save re&gion...", ID_FILE_SAVEREGION + MENUITEM SEPARATOR + MENUITEM "New p&roject...", ID_FILE_NEWPROJECT + MENUITEM "Load &project...", ID_FILE_LOADPROJECT + MENUITEM "Pro&ject settings...", ID_FILE_PROJECTSETTINGS + MENUITEM SEPARATOR + MENUITEM "&Pointfile...", ID_FILE_POINTFILE + MENUITEM SEPARATOR + MENUITEM "E&xit", ID_FILE_EXIT + END + POPUP "&Edit" + BEGIN + MENUITEM "Undo", ID_EDIT_UNDO + MENUITEM SEPARATOR + MENUITEM "&Copy brush", ID_EDIT_COPYBRUSH + MENUITEM "&Paste brush", ID_EDIT_PASTEBRUSH + MENUITEM SEPARATOR + MENUITEM "Map Info...", ID_EDIT_MAPINFO + MENUITEM "Entity Info...", ID_EDIT_ENTITYINFO + MENUITEM SEPARATOR + MENUITEM "Brush Scripts...", ID_BRUSH_SCRIPTS + MENUITEM SEPARATOR + MENUITEM "Load Pre&fab...", ID_EDIT_LOADPREFAB + MENUITEM SEPARATOR + MENUITEM "Preferences...", ID_PREFS + END + POPUP "&View" + BEGIN + POPUP "Toggle" + BEGIN + MENUITEM "Camera View", ID_TOGGLECAMERA + MENUITEM "Console View", ID_TOGGLECONSOLE + MENUITEM "Entity View", ID_VIEW_ENTITY + MENUITEM "XY (Top)", ID_TOGGLEVIEW + MENUITEM "YZ (Side)", ID_TOGGLEVIEW_YZ + MENUITEM "XZ (Front)", ID_TOGGLEVIEW_XZ + MENUITEM "Z View", ID_TOGGLEZ + END + MENUITEM SEPARATOR + MENUITEM "&Center\tEnd", ID_VIEW_CENTER + MENUITEM "&Up Floor\tPage Up", ID_VIEW_UPFLOOR + MENUITEM "&Down Floor\tPage Down", ID_VIEW_DOWNFLOOR + MENUITEM SEPARATOR + MENUITEM "&Next (XY, YZ, XY)\tCtrl-TAB", ID_VIEW_NEXTVIEW + POPUP "Layout" + BEGIN + MENUITEM "XY (Top)", ID_VIEW_XY + MENUITEM "YZ", ID_VIEW_SIDE + MENUITEM "XZ", ID_VIEW_FRONT + END + POPUP "Zoom" + BEGIN + MENUITEM "&XY 100%", ID_VIEW_100 + MENUITEM "XY Zoom &In\tDelete", ID_VIEW_ZOOMIN + MENUITEM "XY Zoom &Out\tInsert", ID_VIEW_ZOOMOUT + MENUITEM SEPARATOR + MENUITEM "&Z 100%", ID_VIEW_Z100 + MENUITEM "Z Zoo&m In\tctrl-Delete", ID_VIEW_ZZOOMIN + MENUITEM "Z Zoom O&ut\tctrl-Insert", ID_VIEW_ZZOOMOUT + MENUITEM SEPARATOR + MENUITEM "Cubic Clip Zoom In\tctrl-]", ID_VIEW_CUBEIN + MENUITEM "Cubic Clip Zoom Out\tctrl-[", ID_VIEW_CUBEOUT + END + MENUITEM SEPARATOR + POPUP "Show" + BEGIN + MENUITEM "Show &Names", ID_VIEW_SHOWNAMES + , CHECKED + MENUITEM "Show Blocks", ID_VIEW_SHOWBLOCKS + MENUITEM "Show C&oordinates", ID_VIEW_SHOWCOORDINATES + , CHECKED + MENUITEM "Show &Entities", ID_VIEW_SHOWENT, CHECKED + MENUITEM "Show Func_Groups", ID_VIEW_SHOWFUNCGROUPS + , CHECKED + MENUITEM "Show &Path", ID_VIEW_SHOWPATH + , CHECKED + MENUITEM "Show &Lights", ID_VIEW_SHOWLIGHTS + , CHECKED + MENUITEM "Show Light Radii", ID_VIEW_SHOWLIGHTRADII + , CHECKED + MENUITEM "Show &Water", ID_VIEW_SHOWWATER + , CHECKED + MENUITEM "Show Clip &Brush", ID_VIEW_SHOWCLIP + , CHECKED + MENUITEM "Show &Hint Brush", ID_VIEW_SHOWHINT + , CHECKED + MENUITEM "Show Wor&ld", ID_VIEW_SHOWWORLD + , CHECKED + MENUITEM "Show Detail\tctrl-D", ID_VIEW_SHOWDETAIL + , CHECKED + MENUITEM "Show Curves", ID_VIEW_SHOWCURVES + , CHECKED + MENUITEM "Show Curves ONLY !!!!!! (this overrides all other show types)", + ID_VIEW_SHOWCURVESONLY + , CHECKED + MENUITEM "Show WayPoints ( and ""point_combat"" )", + ID_VIEW_SHOWWAYPOINTS + , CHECKED + MENUITEM "Show Waypoints ONLY !!!!!! ( this overrides all other Show-Ent types )", + ID_VIEW_SHOWWAYPOINTS_ONLY + + MENUITEM "Show Misc_Models", ID_VIEW_SHOWMISC_MODEL + , CHECKED + MENUITEM "Show Misc_Model_Breakable", ID_VIEW_SHOWMISC_MODEL_BREAKABLE + , CHECKED + MENUITEM "Show Misc_Model_xxxx", ID_VIEW_SHOWMISC_MODEL_XXXX + , CHECKED + MENUITEM "Show Trigger_xxxx", ID_VIEW_SHOWTRIGGER_XXXX + , CHECKED + MENUITEM "Show Target_Speakers", ID_VIEW_SHOWTARGET_SPEAKER + , CHECKED + MENUITEM "Show Ref_Tags", ID_VIEW_SHOWREF_TAGS + , CHECKED + MENUITEM "Show Non-Solid", ID_VIEW_SHOW_NONSOLID + , CHECKED + MENUITEM "Show EASY", ID_SHOW_EASY, CHECKED + MENUITEM "Show MEDIUM", ID_SHOW_MEDIUM, CHECKED + MENUITEM "Show HARD", ID_SHOW_HARD, CHECKED + MENUITEM "Show detail ONLY!!!! ( overrides all other brush-show flags )", + ID_VIEW_SHOW_DETAIL_ONLY + , CHECKED + END + MENUITEM SEPARATOR + POPUP "Entities as" + BEGIN + MENUITEM "Bounding box", ID_VIEW_ENTITIESAS_BOUNDINGBOX + + MENUITEM "Writeframe", ID_VIEW_ENTITIESAS_WRITEFRAME + + MENUITEM "Selected Wireframe", ID_VIEW_ENTITIESAS_SELECTEDWIREFRAME + + MENUITEM "Selected Skinned", ID_VIEW_ENTITIESAS_SELECTEDSKINNED + + MENUITEM "Skinned", ID_VIEW_ENTITIESAS_SKINNED + + MENUITEM "Skinned and Boxed", ID_VIEW_ENTITIESAS_SKINNEDANDBOXED + + END + MENUITEM SEPARATOR + MENUITEM "Cubic Clipping", ID_VIEW_CUBICCLIPPING + , CHECKED + MENUITEM SEPARATOR + END + POPUP "&Selection" + BEGIN + POPUP "Drag" + BEGIN + MENUITEM "Drag &Edges", ID_SELECTION_DRAGEDGES + MENUITEM "Drag &Vertices", ID_SELECTION_DRAGVERTECIES + + END + MENUITEM SEPARATOR + MENUITEM "&Clone", ID_SELECTION_CLONE + MENUITEM "Deselect", ID_SELECTION_DESELECT + MENUITEM "&Delete", ID_SELECTION_DELETE + MENUITEM SEPARATOR + POPUP "Flip" + BEGIN + MENUITEM "Flip &X", ID_BRUSH_FLIPX + MENUITEM "Flip &Y", ID_BRUSH_FLIPY + MENUITEM "Flip &Z", ID_BRUSH_FLIPZ + END + MENUITEM SEPARATOR + POPUP "Rotate" + BEGIN + MENUITEM "Rotate X", ID_BRUSH_ROTATEX + MENUITEM "Rotate Y", ID_BRUSH_ROTATEY + MENUITEM "Rotate Z", ID_BRUSH_ROTATEZ + MENUITEM "Arbitrary rotation...", ID_SELECTION_ARBITRARYROTATION + + END + MENUITEM SEPARATOR + MENUITEM "Scale...", ID_SELECT_SCALE + POPUP "CSG" + BEGIN + MENUITEM "Make &Hollow", ID_SELECTION_MAKEHOLLOW + MENUITEM "CSG &Subtract", ID_SELECTION_CSGSUBTRACT + END + MENUITEM SEPARATOR + POPUP "Select" + BEGIN + MENUITEM "Select Complete &Tall", ID_SELECTION_SELECTCOMPLETETALL + + MENUITEM "Select T&ouching", ID_SELECTION_SELECTTOUCHING + + MENUITEM "Select &Partial Tall", ID_SELECTION_SELECTPARTIALTALL + + MENUITEM "Select &Inside", ID_SELECTION_SELECTINSIDE + + END + MENUITEM SEPARATOR + POPUP "Clipper" + BEGIN + MENUITEM "Toggle Clipper", ID_VIEW_CLIPPER + MENUITEM SEPARATOR + MENUITEM "Clip selection", ID_CLIP_SELECTED + MENUITEM "Split selectedion", ID_SPLIT_SELECTED + MENUITEM "Flip Clip orientation", ID_FLIP_CLIP + END + MENUITEM SEPARATOR + MENUITEM "Connect entities", ID_SELECTION_CONNECT + MENUITEM "Ungroup entity", ID_SELECTION_UNGROUPENTITY + MENUITEM SEPARATOR + MENUITEM "Make detail\tctrl-M", ID_SELECTION_MAKE_DETAIL + MENUITEM "Make structural", ID_SELECTION_MAKE_STRUCTURAL + MENUITEM "Make Non-Solid\tctrl-U", ID_MAKE_NONSOLID + MENUITEM "Make Solid\tshift-L", ID_CLEAR_NONSOLID + END + POPUP "&Bsp" + BEGIN + MENUITEM SEPARATOR + END + POPUP "&Grid" + BEGIN + MENUITEM "Grid1\t&1", ID_GRID_1 + MENUITEM "Grid2\t&2", ID_GRID_2 + MENUITEM "Grid4\t&3", ID_GRID_4 + MENUITEM "Grid8\t&4", ID_GRID_8, CHECKED + MENUITEM "Grid16\t&5", ID_GRID_16 + MENUITEM "Grid32\t&6", ID_GRID_32 + MENUITEM "Grid64\t&7", ID_GRID_64 + MENUITEM SEPARATOR + MENUITEM "Snap to grid", ID_SNAPTOGRID + , CHECKED, GRAYED + END + POPUP "&Textures" + BEGIN + MENUITEM "Show In &Use\tU", ID_TEXTURES_SHOWINUSE + MENUITEM "&Surface Inspector\tS", ID_TEXTURES_INSPECTOR + MENUITEM SEPARATOR + POPUP "Render Quality" + BEGIN + MENUITEM "&Wireframe", ID_TEXTURES_WIREFRAME + MENUITEM "&Flat shade", ID_TEXTURES_FLATSHADE + MENUITEM "&Nearest", ID_VIEW_NEAREST + MENUITEM "Nearest &Mipmap", ID_VIEW_NEARESTMIPMAP + MENUITEM "&Linear", ID_VIEW_LINEAR + MENUITEM "&Bilinear", ID_VIEW_BILINEAR + MENUITEM "B&ilinear Mipmap", ID_VIEW_BILINEARMIPMAP + MENUITEM "T&rilinear", ID_VIEW_TRILINEAR + END + MENUITEM SEPARATOR + MENUITEM "Find / Replace...", ID_TEXTURE_REPLACEALL + MENUITEM SEPARATOR + POPUP "Texture Lock" + BEGIN + MENUITEM "Moves", ID_TOGGLE_LOCK, CHECKED + MENUITEM "Rotations", ID_TOGGLE_ROTATELOCK + , CHECKED + END + MENUITEM SEPARATOR + POPUP "Load" + BEGIN + MENUITEM "Load...", ID_TEXTURES_LOAD + MENUITEM "Load from List.. ", ID_TEXTURES_LOADLIST + END + MENUITEM "Reload Shaders", ID_TEXTURES_RELOADSHADERS + MENUITEM SEPARATOR + MENUITEM "Flush", ID_TEXTURES_FLUSH + MENUITEM SEPARATOR + POPUP "Texture Window Scale" + BEGIN + MENUITEM "200%", ID_TEXTURES_TEXTUREWINDOWSCALE_200 + + MENUITEM "100%", ID_TEXTURES_TEXTUREWINDOWSCALE_100 + + MENUITEM "50%", ID_TEXTURES_TEXTUREWINDOWSCALE_50 + + MENUITEM "25%", ID_TEXTURES_TEXTUREWINDOWSCALE_25 + + MENUITEM "10%", ID_TEXTURES_TEXTUREWINDOWSCALE_10 + + END + MENUITEM SEPARATOR + END + POPUP "&Misc" + BEGIN + MENUITEM "&Benchmark", ID_MISC_BENCHMARK + POPUP "&Colors" + BEGIN + POPUP "Themes" + BEGIN + MENUITEM "QE4 Original", ID_COLOR_SETORIGINAL + MENUITEM "QERadiant Original", ID_COLOR_SETQER + MENUITEM "Black and Green", ID_COLOR_SETBLACK + END + MENUITEM SEPARATOR + MENUITEM "&Texture Background...", ID_TEXTUREBK + MENUITEM "Grid Background...", ID_COLORS_XYBK + MENUITEM "Grid Major...", ID_COLORS_MAJOR + MENUITEM "Grid Minor...", ID_COLORS_MINOR + MENUITEM "Grid Text...", ID_COLORS_GRIDTEXT + MENUITEM "Grid Block...", ID_COLORS_GRIDBLOCK + MENUITEM "Default Brush...", ID_COLORS_BRUSH + MENUITEM "Selected Brush...", ID_COLORS_SELECTEDBRUSH + MENUITEM "Clipper...", ID_COLORS_CLIPPER + MENUITEM "Active View name...", ID_COLORS_VIEWNAME + END + MENUITEM "&Gamma...", ID_MISC_GAMMA + MENUITEM "Find brush...", ID_MISC_FINDBRUSH + MENUITEM "Next leak spot\tctrl-shift-K", ID_MISC_NEXTLEAKSPOT + MENUITEM "Previous leak spot\tctrl-shift-L", + ID_MISC_PREVIOUSLEAKSPOT + MENUITEM "&Print XY View", ID_MISC_PRINTXY + MENUITEM "&Select Entity Color...\tK", ID_MISC_SELECTENTITYCOLOR + MENUITEM "Show Face Lines", ID_VIEW_SHOWFACES + MENUITEM "Find or Replace Entity\tctrl-F3", ID_MISC_FINDENT + MENUITEM "Auto-Caulk selected brushes", ID_AUTOCAULK + END + POPUP "&Region" + BEGIN + MENUITEM "&Off", ID_REGION_OFF + MENUITEM "&Set XY", ID_REGION_SETXY + MENUITEM "Set &Tall Brush", ID_REGION_SETTALLBRUSH + MENUITEM "Set &Brush", ID_REGION_SETBRUSH + MENUITEM "Set Se&lected Brushes", ID_REGION_SETSELECTION + END + POPUP "&Brush" + BEGIN + MENUITEM "3 sided\tctrl-3", ID_BRUSH_3SIDED + MENUITEM "4 sided\tctrl-4", ID_BRUSH_4SIDED + MENUITEM "5 sided\tctrl-5", ID_BRUSH_5SIDED + MENUITEM "6 sided\tctrl-6", ID_BRUSH_6SIDED + MENUITEM "7 sided\tctrl-7", ID_BRUSH_7SIDED + MENUITEM "8 sided\tctrl-8", ID_BRUSH_8SIDED + MENUITEM "9 sided\tctrl-9", ID_BRUSH_9SIDED + MENUITEM SEPARATOR + MENUITEM "Arbitrary sided...", ID_BRUSH_ARBITRARYSIDED + MENUITEM SEPARATOR + POPUP "Primitives" + BEGIN + MENUITEM "Cone...", ID_BRUSH_MAKECONE + MENUITEM "Sphere...", ID_BRUSH_PRIMITIVES_SPHERE + + MENUITEM "Torus...", ID_BRUSH_PRIMITIVES_TORUS + + END + END + POPUP "&Curve" + BEGIN + MENUITEM "Cylinder", ID_CURVE_PATCHTUBE + POPUP "More Cylinders" + BEGIN + MENUITEM "Dense Cylinder", ID_CURVE_PATCHDENSETUBE + MENUITEM "Very Dense Cylinder", ID_CURVE_PATCHVERYDENSETUBE + + MENUITEM "Square Cylinder", ID_CURVE_PATCHSQUARE + END + MENUITEM SEPARATOR + MENUITEM "End cap", ID_CURVE_PATCHENDCAP + MENUITEM "Bevel", ID_CURVE_PATCHBEVEL + MENUITEM SEPARATOR + MENUITEM "Cone", ID_CURVE_PATCHCONE + MENUITEM SEPARATOR + MENUITEM "Simple Patch Mesh...", ID_CURVE_SIMPLEPATCHMESH + MENUITEM SEPARATOR + POPUP "Insert" + BEGIN + MENUITEM "Insert (2) Columns", ID_CURVE_INSERT_INSERTCOLUMN + + MENUITEM "Add (2) Columns", ID_CURVE_INSERT_ADDCOLUMN + + MENUITEM SEPARATOR + MENUITEM "Insert (2) Rows", ID_CURVE_INSERT_INSERTROW + + MENUITEM "Add (2) Rows", ID_CURVE_INSERT_ADDROW + END + POPUP "Delete" + BEGIN + MENUITEM "First (2) Columns", ID_CURVE_DELETE_FIRSTCOLUMN + + MENUITEM "Last (2) Columns", ID_CURVE_DELETE_LASTCOLUMN + + MENUITEM SEPARATOR + MENUITEM "First (2) Rows", ID_CURVE_DELETE_FIRSTROW + MENUITEM "Last (2) Rows", ID_CURVE_DELETE_LASTROW + END + MENUITEM SEPARATOR + POPUP "Matrix" + BEGIN + MENUITEM "Invert", ID_CURVE_NEGATIVE + POPUP "Re-disperse" + BEGIN + MENUITEM "Cols", ID_CURVE_REDISPERSE_COLS + + MENUITEM "Rows", ID_CURVE_REDISPERSE_ROWS + + END + MENUITEM "Transpose", ID_CURVE_MATRIX_TRANSPOSE + + END + MENUITEM SEPARATOR + POPUP "Cap" + BEGIN + MENUITEM "Normal", ID_CURVE_CAP + MENUITEM "Inverted Bevel", ID_CURVE_CAP_INVERTEDBEVEL + + MENUITEM "Inverted Endcap", ID_CURVE_CAP_INVERTEDENDCAP + + MENUITEM SEPARATOR + MENUITEM "Cycle Cap Texture", ID_CURVE_CYCLECAP + END + MENUITEM SEPARATOR + POPUP "Overlay" + BEGIN + MENUITEM "Set", ID_CURVE_OVERLAY_SET + MENUITEM "Clear", ID_CURVE_OVERLAY_CLEAR + END + MENUITEM SEPARATOR + MENUITEM "Thicken...", ID_CURVE_THICKEN + END + POPUP "&Plugins" + BEGIN + MENUITEM "Refresh", ID_PLUGINS_REFRESH + MENUITEM SEPARATOR + END + POPUP "SourceSafe" + BEGIN + MENUITEM "Status", ID_SS_STATUS + MENUITEM SEPARATOR + MENUITEM "Add", ID_SS_ADD + MENUITEM "Check In", ID_SS_CHECKIN + MENUITEM "Check Out", ID_SS_CHECKOUT + MENUITEM "Undo Check Out", ID_SS_UNDOCHECKOUT + MENUITEM "History", ID_SS_HISTORY + MENUITEM SEPARATOR + MENUITEM "Configure", ID_SS_CONFIGURE + END + POPUP "&Help" + BEGIN + MENUITEM "Command list...", ID_HELP_COMMANDLIST + MENUITEM SEPARATOR + MENUITEM "&About...", ID_HELP_ABOUT + END +END + +IDR_POPUP_TEXTURE MENU DISCARDABLE +BEGIN + POPUP "Popup" + BEGIN + MENUITEM "&Wireframe", ID_TEXTURES_WIREFRAME + MENUITEM "&Flat shade", ID_TEXTURES_FLATSHADE + MENUITEM "&Nearest", ID_VIEW_NEAREST + MENUITEM "Nearest &Mipmap", ID_VIEW_NEARESTMIPMAP + MENUITEM "&Linear", ID_VIEW_LINEAR + MENUITEM "&Bilinear", ID_VIEW_BILINEAR + MENUITEM "B&ilinear Mipmap", ID_VIEW_BILINEARMIPMAP + MENUITEM "T&rilinear", ID_VIEW_TRILINEAR + END +END + +IDR_POPUP_SELECTION MENU DISCARDABLE +BEGIN + POPUP "Popup" + BEGIN + MENUITEM "Select Complete &Tall", ID_SELECTION_SELECTCOMPLETETALL + + MENUITEM "Select T&ouching", ID_SELECTION_SELECTTOUCHING + MENUITEM "Select &Partial Tall", ID_SELECTION_SELECTPARTIALTALL + + MENUITEM "Select &Inside", ID_SELECTION_SELECTINSIDE + END +END + +IDR_POPUP_VIEW MENU DISCARDABLE +BEGIN + POPUP "Popup" + BEGIN + MENUITEM "XY (Top)", ID_VIEW_XY + MENUITEM "XZ", ID_VIEW_SIDE + MENUITEM "YZ", ID_VIEW_FRONT + END +END + +IDR_MENU_DROP MENU DISCARDABLE +BEGIN + POPUP "Drop" + BEGIN + MENUITEM SEPARATOR + POPUP "Select" + BEGIN + MENUITEM "Select Complete &Tall", ID_SELECTION_SELECTCOMPLETETALL + + MENUITEM "Select T&ouching", ID_SELECTION_SELECTTOUCHING + + MENUITEM "Select &Partial Tall", ID_SELECTION_SELECTPARTIALTALL + + MENUITEM "Select &Inside", ID_SELECTION_SELECTINSIDE + + END + MENUITEM "Ungroup entity", ID_SELECTION_UNGROUPENTITY + MENUITEM "Make detail", ID_SELECTION_MAKE_DETAIL + MENUITEM "Make structural", ID_SELECTION_MAKE_STRUCTURAL + END +END + +IDR_MAINFRAME MENU DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&New map", ID_FILE_NEW + MENUITEM SEPARATOR + MENUITEM "&Open...", ID_FILE_OPEN + MENUITEM "&Load...", ID_FILE_IMPORTMAP + MENUITEM "&Save", ID_FILE_SAVE + MENUITEM "Save &as...", ID_FILE_SAVEAS + MENUITEM "Save s&elected...", ID_FILE_EXPORTMAP + MENUITEM SEPARATOR + MENUITEM "Save re&gion...", ID_FILE_SAVEREGION + MENUITEM SEPARATOR + MENUITEM "New p&roject...", ID_FILE_NEWPROJECT + MENUITEM "Load &project...", ID_FILE_LOADPROJECT + MENUITEM "Pro&ject settings...", ID_FILE_PROJECTSETTINGS + MENUITEM SEPARATOR + MENUITEM "&Pointfile...", ID_FILE_POINTFILE + MENUITEM SEPARATOR + MENUITEM "E&xit", ID_FILE_EXIT + END + POPUP "&Edit" + BEGIN + MENUITEM "Undo (no really :-)", ID_EDIT_UNDO + MENUITEM SEPARATOR + MENUITEM "&Copy brush", ID_EDIT_COPYBRUSH + MENUITEM "&Paste brush", ID_EDIT_PASTEBRUSH + MENUITEM SEPARATOR + MENUITEM "Map Info...", ID_EDIT_MAPINFO + MENUITEM "Entity Info...", ID_EDIT_ENTITYINFO + MENUITEM SEPARATOR + MENUITEM "Brush Scripts...", ID_BRUSH_SCRIPTS + MENUITEM SEPARATOR + MENUITEM "Load Pre&fab...", ID_EDIT_LOADPREFAB + MENUITEM SEPARATOR + MENUITEM "Preferences...", ID_PREFS + END + POPUP "&View" + BEGIN + POPUP "Toggle" + BEGIN + MENUITEM "Camera View", ID_TOGGLECAMERA + MENUITEM "Console View", ID_TOGGLECONSOLE + MENUITEM "Entity View", ID_VIEW_ENTITY + MENUITEM "XY (Top)", ID_TOGGLEVIEW + MENUITEM "YZ (Side)", ID_TOGGLEVIEW_YZ + MENUITEM "XZ (Front)", ID_TOGGLEVIEW_XZ + MENUITEM "Z View", ID_TOGGLEZ + END + MENUITEM SEPARATOR + MENUITEM "&Center\tEnd", ID_VIEW_CENTER + MENUITEM "&Up Floor\tPage Up", ID_VIEW_UPFLOOR + MENUITEM "&Down Floor\tPage Down", ID_VIEW_DOWNFLOOR + MENUITEM SEPARATOR + MENUITEM "&Next (XY, YZ, XY)\tCtrl-TAB", ID_VIEW_NEXTVIEW + POPUP "Layout" + BEGIN + MENUITEM "XY (Top)", ID_VIEW_XY + MENUITEM "YZ", ID_VIEW_SIDE + MENUITEM "XZ", ID_VIEW_FRONT + END + POPUP "Zoom" + BEGIN + MENUITEM "&XY 100%", ID_VIEW_100 + MENUITEM "XY Zoom &In\tDelete", ID_VIEW_ZOOMIN + MENUITEM "XY Zoom &Out\tInsert", ID_VIEW_ZOOMOUT + MENUITEM SEPARATOR + MENUITEM "&Z 100%", ID_VIEW_Z100 + MENUITEM "Z Zoo&m In\tctrl-Delete", ID_VIEW_ZZOOMIN + MENUITEM "Z Zoom O&ut\tctrl-Insert", ID_VIEW_ZZOOMOUT + MENUITEM SEPARATOR + MENUITEM "Cubic Clip Zoom In\tctrl-]", ID_VIEW_CUBEIN + MENUITEM "Cubic Clip Zoom Out\tctrl-[", ID_VIEW_CUBEOUT + END + MENUITEM SEPARATOR + POPUP "Show" + BEGIN + MENUITEM "Show &Names", ID_VIEW_SHOWNAMES + , CHECKED + MENUITEM "Show Blocks", ID_VIEW_SHOWBLOCKS + MENUITEM "Show C&oordinates", ID_VIEW_SHOWCOORDINATES + , CHECKED + MENUITEM "Show &Entities", ID_VIEW_SHOWENT, CHECKED + MENUITEM "Show &Path", ID_VIEW_SHOWPATH + , CHECKED + MENUITEM "Show &Lights", ID_VIEW_SHOWLIGHTS + , CHECKED + MENUITEM "Show &Water", ID_VIEW_SHOWWATER + , CHECKED + MENUITEM "Show Clip &Brush", ID_VIEW_SHOWCLIP + , CHECKED + MENUITEM "Show Wor&ld", ID_VIEW_SHOWWORLD + , CHECKED + MENUITEM "Show Detail\tctrl-D", ID_VIEW_SHOWDETAIL + , CHECKED + END + MENUITEM SEPARATOR + MENUITEM "Cubic Clipping", ID_VIEW_CUBICCLIPPING + , CHECKED + MENUITEM SEPARATOR + MENUITEM "Dynamic Lighting", ID_DYNAMIC_LIGHTING + END + POPUP "&Selection" + BEGIN + POPUP "Drag" + BEGIN + MENUITEM "Drag &Edges", ID_SELECTION_DRAGEDGES + MENUITEM "Drag &Vertices", ID_SELECTION_DRAGVERTECIES + + END + MENUITEM SEPARATOR + MENUITEM "&Clone", ID_SELECTION_CLONE + MENUITEM "Deselect", ID_SELECTION_DESELECT + MENUITEM "&Delete", ID_SELECTION_DELETE + MENUITEM SEPARATOR + POPUP "Flip" + BEGIN + MENUITEM "Flip &X", ID_BRUSH_FLIPX + MENUITEM "Flip &Y", ID_BRUSH_FLIPY + MENUITEM "Flip &Z", ID_BRUSH_FLIPZ + END + MENUITEM SEPARATOR + POPUP "Rotate" + BEGIN + MENUITEM "Rotate X", ID_BRUSH_ROTATEX + MENUITEM "Rotate Y", ID_BRUSH_ROTATEY + MENUITEM "Rotate Z", ID_BRUSH_ROTATEZ + MENUITEM "Arbitrary rotation...", ID_SELECTION_ARBITRARYROTATION + + END + MENUITEM SEPARATOR + MENUITEM "Scale...", ID_SELECT_SCALE + POPUP "CSG" + BEGIN + MENUITEM "Make &Hollow", ID_SELECTION_MAKEHOLLOW + MENUITEM "CSG &Subtract", ID_SELECTION_CSGSUBTRACT + END + MENUITEM SEPARATOR + POPUP "Select" + BEGIN + MENUITEM "Select Complete &Tall", ID_SELECTION_SELECTCOMPLETETALL + + MENUITEM "Select T&ouching", ID_SELECTION_SELECTTOUCHING + + MENUITEM "Select &Partial Tall", ID_SELECTION_SELECTPARTIALTALL + + MENUITEM "Select &Inside", ID_SELECTION_SELECTINSIDE + + MENUITEM "Nudge Left", ID_SELECTION_SELECT_NUDGELEFT + + MENUITEM "Nudge Right", ID_SELECTION_SELECT_NUDGERIGHT + + MENUITEM "Nudge Up", ID_SELECTION_SELECT_NUDGEUP + + MENUITEM "Nudge Down", ID_SELECTION_SELECT_NUDGEDOWN + + END + MENUITEM SEPARATOR + POPUP "Clipper" + BEGIN + MENUITEM "Toggle Clipper", ID_VIEW_CLIPPER + MENUITEM SEPARATOR + MENUITEM "Clip selection", ID_CLIP_SELECTED + MENUITEM "Split selectedion", ID_SPLIT_SELECTED + MENUITEM "Flip Clip orientation", ID_FLIP_CLIP + END + MENUITEM SEPARATOR + MENUITEM "Connect entities", ID_SELECTION_CONNECT + MENUITEM "Ungroup entity", ID_SELECTION_UNGROUPENTITY + MENUITEM "Make detail", ID_SELECTION_MAKE_DETAIL + MENUITEM "Make structural", ID_SELECTION_MAKE_STRUCTURAL + END + POPUP "&Bsp" + BEGIN + MENUITEM SEPARATOR + END + POPUP "&Grid" + BEGIN + MENUITEM "Grid1\t&1", ID_GRID_1 + MENUITEM "Grid2\t&2", ID_GRID_2 + MENUITEM "Grid4\t&3", ID_GRID_4 + MENUITEM "Grid8\t&4", ID_GRID_8, CHECKED + MENUITEM "Grid16\t&5", ID_GRID_16 + MENUITEM "Grid32\t&6", ID_GRID_32 + MENUITEM "Grid64\t&7", ID_GRID_64 + MENUITEM SEPARATOR + MENUITEM "Snap to grid", ID_SNAPTOGRID + , CHECKED, GRAYED + END + POPUP "&Textures" + BEGIN + MENUITEM "Show In &Use\tU", ID_TEXTURES_SHOWINUSE + MENUITEM "&Surface Inspector\tS", ID_TEXTURES_INSPECTOR + MENUITEM SEPARATOR + POPUP "Render Quality" + BEGIN + MENUITEM "&Wireframe", ID_TEXTURES_WIREFRAME + MENUITEM "&Flat shade", ID_TEXTURES_FLATSHADE + MENUITEM "&Nearest", ID_VIEW_NEAREST + MENUITEM "Nearest &Mipmap", ID_VIEW_NEARESTMIPMAP + MENUITEM "&Linear", ID_VIEW_LINEAR + MENUITEM "&Bilinear", ID_VIEW_BILINEAR + MENUITEM "B&ilinear Mipmap", ID_VIEW_BILINEARMIPMAP + MENUITEM "T&rilinear", ID_VIEW_TRILINEAR + END + MENUITEM SEPARATOR + MENUITEM "Find / Replace...", ID_TEXTURE_REPLACEALL + MENUITEM SEPARATOR + MENUITEM "Move Texture Lock", ID_TOGGLE_LOCK, CHECKED + MENUITEM "Rotation Texture Lock", ID_TOGGLE_ROTATELOCK + , CHECKED + MENUITEM SEPARATOR + MENUITEM "Load...", ID_TEXTURES_LOAD + MENUITEM "Load from List.. ", ID_TEXTURES_LOADLIST + MENUITEM SEPARATOR + END + POPUP "&Misc" + BEGIN + MENUITEM "&Benchmark", ID_MISC_BENCHMARK + POPUP "&Colors" + BEGIN + POPUP "Themes" + BEGIN + MENUITEM "QE4 Original", ID_COLOR_SETORIGINAL + MENUITEM "QERadiant Original", ID_COLOR_SETQER + MENUITEM "Black and Green", ID_COLOR_SETBLACK + END + MENUITEM SEPARATOR + MENUITEM "&Texture Background...", ID_TEXTUREBK + MENUITEM "Grid Background...", ID_COLORS_XYBK + MENUITEM "Grid Major...", ID_COLORS_MAJOR + MENUITEM "Grid Minor...", ID_COLORS_MINOR + MENUITEM "Grid Text...", ID_COLORS_GRIDTEXT + MENUITEM "Grid Block...", ID_COLORS_GRIDBLOCK + MENUITEM "Default Brush...", ID_COLORS_BRUSH + MENUITEM "Selected Brush...", ID_COLORS_SELECTEDBRUSH + MENUITEM "Clipper...", ID_COLORS_CLIPPER + MENUITEM "Active View name...", ID_COLORS_VIEWNAME + END + MENUITEM "&Gamma...", ID_MISC_GAMMA + MENUITEM "Find brush...", ID_MISC_FINDBRUSH + MENUITEM "Next leak spot\tctrl-l", ID_MISC_NEXTLEAKSPOT + MENUITEM "Previous leak spot\tctrl-p", ID_MISC_PREVIOUSLEAKSPOT + MENUITEM "&Print XY View", ID_MISC_PRINTXY + MENUITEM "&Select Entity Color...\tK", ID_MISC_SELECTENTITYCOLOR + MENUITEM "Convert old curves", ID_CONVERTCURVES + MENUITEM "Show Face Lines", ID_VIEW_SHOWFACES + MENUITEM "Find or Replace Entity\tctrl-F3", ID_MISC_FINDENT + END + POPUP "&Region" + BEGIN + MENUITEM "&Off", ID_REGION_OFF + MENUITEM "&Set XY", ID_REGION_SETXY + MENUITEM "Set &Tall Brush", ID_REGION_SETTALLBRUSH + MENUITEM "Set &Brush", ID_REGION_SETBRUSH + MENUITEM "Set Se&lected Brushes", ID_REGION_SETSELECTION + END + POPUP "&Brush" + BEGIN + MENUITEM "3 sided\tctrl-3", ID_BRUSH_3SIDED + MENUITEM "4 sided\tctrl-4", ID_BRUSH_4SIDED + MENUITEM "5 sided\tctrl-5", ID_BRUSH_5SIDED + MENUITEM "6 sided\tctrl-6", ID_BRUSH_6SIDED + MENUITEM "7 sided\tctrl-7", ID_BRUSH_7SIDED + MENUITEM "8 sided\tctrl-8", ID_BRUSH_8SIDED + MENUITEM "9 sided\tctrl-9", ID_BRUSH_9SIDED + MENUITEM SEPARATOR + MENUITEM "Arbitrary sided...", ID_BRUSH_ARBITRARYSIDED + MENUITEM SEPARATOR + POPUP "Primitives" + BEGIN + MENUITEM "Cone...", ID_BRUSH_MAKECONE + END + END + POPUP "&Help" + BEGIN + MENUITEM "Command list...", ID_HELP_COMMANDLIST + MENUITEM SEPARATOR + MENUITEM "&About...", ID_HELP_ABOUT + END +END + +IDR_POPUP_ENTITY MENU DISCARDABLE +BEGIN + POPUP "Popup" + BEGIN + MENUITEM "Bounding box", ID_VIEW_ENTITIESAS_BOUNDINGBOX + + MENUITEM "Wireframe", ID_VIEW_ENTITIESAS_WIREFRAME + MENUITEM "Selected Wireframe", ID_VIEW_ENTITIESAS_SELECTEDWIREFRAME + + MENUITEM "Selected Skinned", ID_VIEW_ENTITIESAS_SELECTEDSKINNED + + MENUITEM "Skinned", ID_VIEW_ENTITIESAS_SKINNED + MENUITEM "Skinned and Boxed", ID_VIEW_ENTITIESAS_SKINNEDANDBOXED + + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_ACCELERATOR1 ACCELERATORS DISCARDABLE +BEGIN + "3", ID_BRUSH_3SIDED, VIRTKEY, CONTROL, NOINVERT + "4", ID_BRUSH_4SIDED, VIRTKEY, CONTROL, NOINVERT + "5", ID_BRUSH_5SIDED, VIRTKEY, CONTROL, NOINVERT + "6", ID_BRUSH_6SIDED, VIRTKEY, CONTROL, NOINVERT + "7", ID_BRUSH_7SIDED, VIRTKEY, CONTROL, NOINVERT + "8", ID_BRUSH_8SIDED, VIRTKEY, CONTROL, NOINVERT + "9", ID_BRUSH_9SIDED, VIRTKEY, CONTROL, NOINVERT + "D", ID_VIEW_SHOWDETAIL, VIRTKEY, CONTROL, NOINVERT + "K", ID_SELECTION_CONNECT, VIRTKEY, CONTROL, NOINVERT + "M", ID_SELECTION_MAKE_DETAIL, VIRTKEY, CONTROL, NOINVERT + "O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT + "P", ID_MISC_PREVIOUSLEAKSPOT, VIRTKEY, CONTROL, NOINVERT + "S", ID_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT + VK_DELETE, ID_VIEW_ZZOOMIN, VIRTKEY, CONTROL, NOINVERT + VK_INSERT, ID_VIEW_ZZOOMOUT, VIRTKEY, CONTROL, NOINVERT + "X", ID_FILE_EXIT, VIRTKEY, CONTROL, NOINVERT +END + +IDR_MAINFRAME ACCELERATORS DISCARDABLE +BEGIN + "7", ID_BRUSH_7SIDED, VIRTKEY, CONTROL, NOINVERT + "8", ID_BRUSH_8SIDED, VIRTKEY, CONTROL, NOINVERT + "9", ID_BRUSH_9SIDED, VIRTKEY, CONTROL, NOINVERT + "D", ID_VIEW_SHOWDETAIL, VIRTKEY, CONTROL, NOINVERT + "K", ID_SELECTION_CONNECT, VIRTKEY, CONTROL, NOINVERT + "M", ID_SELECTION_MAKE_DETAIL, VIRTKEY, CONTROL, NOINVERT + "O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT + "P", ID_MISC_PREVIOUSLEAKSPOT, VIRTKEY, CONTROL, NOINVERT + "S", ID_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT + VK_DELETE, ID_VIEW_ZZOOMIN, VIRTKEY, CONTROL, NOINVERT + VK_DOWN, ID_SELECTION_SELECT_NUDGEDOWN, VIRTKEY, ALT, NOINVERT + VK_INSERT, ID_VIEW_ZZOOMOUT, VIRTKEY, CONTROL, NOINVERT + "X", ID_FILE_EXIT, VIRTKEY, CONTROL, NOINVERT +END + +IDR_ACCEL_SURFACE ACCELERATORS DISCARDABLE +BEGIN + VK_ESCAPE, ID_BYEBYE, VIRTKEY, NOINVERT +END + +IDR_MINIACCEL ACCELERATORS DISCARDABLE +BEGIN + VK_DOWN, ID_SELECTION_SELECT_NUDGEDOWN, VIRTKEY, ALT, NOINVERT + VK_LEFT, ID_SELECTION_SELECT_NUDGELEFT, VIRTKEY, ALT, NOINVERT + VK_RIGHT, ID_SELECTION_SELECT_NUDGERIGHT, VIRTKEY, ALT, NOINVERT + VK_UP, ID_SELECTION_SELECT_NUDGEUP, VIRTKEY, ALT, NOINVERT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_FINDTEXTURE DIALOG DISCARDABLE 0, 0, 129, 53 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Find Texture" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,10,30,50,14 + PUSHBUTTON "Cancel",IDCANCEL,70,30,50,14 + EDITTEXT IDC_EDIT1,10,10,110,14,ES_AUTOHSCROLL +END + +IDD_ENTITY DIALOGEX 0, 0, 236, 398 +STYLE DS_3DLOOK | WS_CLIPSIBLINGS | WS_CAPTION | WS_THICKFRAME +EXSTYLE WS_EX_TOOLWINDOW | WS_EX_CLIENTEDGE +CAPTION "Entity" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + LISTBOX IDC_E_LIST,5,5,180,94,LBS_SORT | LBS_NOINTEGRALHEIGHT | + LBS_WANTKEYBOARDINPUT | WS_VSCROLL | WS_TABSTOP, + WS_EX_CLIENTEDGE + EDITTEXT IDC_E_COMMENT,5,102,180,45,ES_MULTILINE | ES_READONLY | + WS_VSCROLL,WS_EX_CLIENTEDGE + PUSHBUTTON "135",IDC_E_135,5,308,15,15 + PUSHBUTTON "180",IDC_E_180,5,324,15,15 + PUSHBUTTON "225",IDC_E_225,5,339,15,15 + PUSHBUTTON "270",IDC_E_270,21,339,15,15 + PUSHBUTTON "90",IDC_E_90,21,308,15,15 + PUSHBUTTON "45",IDC_E_45,35,308,15,15 + PUSHBUTTON "360",IDC_E_0,35,324,15,15 + PUSHBUTTON "315",IDC_E_315,35,339,15,15 + PUSHBUTTON "Up",IDC_E_UP,60,313,15,15 + PUSHBUTTON "Dn",IDC_E_DOWN,60,329,15,15 + CONTROL "",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,9,151,50,8 + CONTROL "",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,9,161,50,8 + CONTROL "",IDC_CHECK3,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,9,171,50,8 + CONTROL "",IDC_CHECK4,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,9,181,50,8 + CONTROL "",IDC_CHECK5,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,9,191,50,8 + CONTROL "",IDC_CHECK6,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,9,201,50,8 + CONTROL "",IDC_CHECK7,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,9,211,50,8 + CONTROL "",IDC_CHECK8,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,59,151,50,8 + CONTROL "!Easy",IDC_CHECK9,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 59,161,50,8 + CONTROL "!Medium",IDC_CHECK10,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,59,171,50,8 + CONTROL "!Hard",IDC_CHECK11,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,59,181,50,10 + CONTROL "!DeathMatch",IDC_CHECK12,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,59,191,55,10 + LISTBOX IDC_E_PROPS,5,224,180,50,LBS_SORT | LBS_USETABSTOPS | + LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT | + WS_VSCROLL | WS_TABSTOP,WS_EX_CLIENTEDGE + PUSHBUTTON "Del Key/Pair",IDC_E_DELPROP,105,313,45,15 + EDITTEXT IDC_E_STATUS,83,330,95,30,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL + LTEXT "Key",IDC_STATIC_KEY,5,278,25,10 + LTEXT "Value",IDC_STATIC_VALUE,5,294,25,10 + EDITTEXT IDC_E_KEY_FIELD,40,277,145,12,ES_AUTOHSCROLL + EDITTEXT IDC_E_VALUE_FIELD,40,293,145,12,ES_AUTOHSCROLL + PUSHBUTTON "Hide",IDC_BTN_HIDE,141,369,37,13 + PUSHBUTTON "Sound...",IDC_BTN_ASSIGNSOUND,200,279,36,11 + PUSHBUTTON "Model...",IDC_BTN_ASSIGNMODEL,200,296,36,11 + CONTROL "!Co-Op",IDC_CHECK13,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,59,201,50,8 + CONTROL "",IDC_CHECK14,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,59,211,50,8 + CONTROL "",IDC_CHECK15,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,119,151,50,8 + CONTROL "",IDC_CHECK16,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,119,161,50,8 + CONTROL "",IDC_CHECK17,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,119,171,50,8 + CONTROL "",IDC_CHECK18,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,119,181,50,8 + CONTROL "",IDC_CHECK19,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,119,191,50,8 + CONTROL "",IDC_CHECK20,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,119,201,50,8 + CONTROL "",IDC_CHECK21,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,119,211,50,8 +END + +IDD_GAMMA DIALOGEX 0, 0, 135, 94 +STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU +CAPTION "Gamma" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + EDITTEXT IDC_G_EDIT,5,8,46,13,ES_AUTOHSCROLL,WS_EX_CLIENTEDGE + DEFPUSHBUTTON "OK",IDOK,91,5,39,14 + PUSHBUTTON "Cancel",IDCANCEL,91,23,39,14 + LTEXT "0.0 is brightest\n1.0 is darkest\n\nYou must restart for the settings to take effect", + IDC_STATIC,7,25,61,55 +END + +IDD_FINDBRUSH DIALOGEX 0, 0, 127, 76 +STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU +CAPTION "Find brush" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,5,55,50,14 + PUSHBUTTON "Cancel",IDCANCEL,65,55,50,14 + EDITTEXT IDC_FIND_ENTITY,80,15,46,13,ES_AUTOHSCROLL, + WS_EX_CLIENTEDGE + EDITTEXT IDC_FIND_BRUSH,80,30,46,13,ES_AUTOHSCROLL, + WS_EX_CLIENTEDGE + LTEXT "Entity number",IDC_STATIC,10,15,60,8 + LTEXT "Brush number",IDC_STATIC,10,30,65,8 +END + +IDD_ROTATE DIALOG DISCARDABLE 0, 0, 129, 65 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Arbitrary rotation" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_ROTX,23,3,29,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN1,"msctls_updown32",UDS_WRAP | + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,52,3,11,14 + EDITTEXT IDC_ROTY,23,21,29,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN2,"msctls_updown32",UDS_WRAP | + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,53,21,11,14 + EDITTEXT IDC_ROTZ,23,39,29,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN3,"msctls_updown32",UDS_WRAP | + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,53,39,11,14 + DEFPUSHBUTTON "OK",IDOK,82,3,39,14 + PUSHBUTTON "Cancel",IDCANCEL,82,19,39,14 + PUSHBUTTON "Apply",ID_APPLY,82,39,39,14 + RTEXT "X",IDC_STATIC,8,6,8,8 + RTEXT "Y",IDC_STATIC,8,24,8,8 + RTEXT "Z",IDC_STATIC,8,42,8,8 +END + +IDD_SIDES DIALOGEX 0, 0, 130, 47 +STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU +CAPTION "Arbitrrary sides" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + EDITTEXT IDC_SIDES,27,6,40,14,ES_AUTOHSCROLL,WS_EX_CLIENTEDGE + DEFPUSHBUTTON "OK",IDOK,85,4,41,14 + PUSHBUTTON "Cancel",IDCANCEL,85,22,41,14 + LTEXT "Sides:",IDC_STATIC,6,8,20,8 +END + +IDD_ABOUT DIALOG DISCARDABLE 0, 0, 274, 270 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About QERadiant" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,232,7,35,14 + CONTROL 127,IDC_STATIC,"Static",SS_BITMAP | SS_CENTERIMAGE | + SS_REALSIZEIMAGE | SS_SUNKEN | WS_BORDER,7,7,87,80 + GROUPBOX "OpenGL Properties",IDC_STATIC,5,93,265,50 + LTEXT "Vendor:\t\tWHOEVER",IDC_ABOUT_GLVENDOR,10,101,253,10 + LTEXT "Version:\t\t1.1",IDC_ABOUT_GLVERSION,10,111,249,10 + LTEXT "Renderer:\tWHATEVER",IDC_ABOUT_GLRENDERER,10,121,253,18 + LTEXT "WHATEVER",IDC_ABOUT_GLEXTENSIONS,10,158,255,95 + GROUPBOX "OpenGL Extensions",IDC_STATIC,5,146,265,116 + CONTROL "Q3Radiant 1.0 Beta build 147\nCopyright ©1998 Id Software, Inc.\n\nQ3Radiant is an unsupported\nproduct, however you may E-mail \nproblems to raduffy@gte.net. You \nmight get lucky", + IDC_STATIC,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,99,7, + 117,59 + PUSHBUTTON "Credits...",IDC_BUTTON_CREDITS,235,66,32,13,WS_DISABLED + LTEXT "This version modified 4/@ Raven Software by S.Cork, J.Monroe, M.Gummelt", + IDC_STATIC,99,71,134,16 +END + +IDD_SURFACE DIALOGEX 400, 100, 438, 237 +STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +CAPTION "Surface inspector" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + EDITTEXT IDC_TEXTURE,33,7,133,12,ES_AUTOHSCROLL + EDITTEXT IDC_HSHIFT,121,25,45,12,ES_AUTOHSCROLL + CONTROL "Spin2",IDC_SPIN_HSHIFT,"msctls_updown32",UDS_ALIGNRIGHT | + UDS_AUTOBUDDY | UDS_ARROWKEYS,155,25,11,14 + EDITTEXT IDC_VSHIFT,121,41,45,12,ES_AUTOHSCROLL + CONTROL "Spin2",IDC_SPIN_VSHIFT,"msctls_updown32",UDS_ALIGNRIGHT | + UDS_AUTOBUDDY | UDS_ARROWKEYS,155,41,11,14 + EDITTEXT IDC_HSCALE,121,58,45,12,ES_AUTOHSCROLL + CONTROL "Spin2",IDC_SPIN_HSCALE,"msctls_updown32",UDS_ALIGNRIGHT | + UDS_AUTOBUDDY | UDS_ARROWKEYS,155,58,11,14 + EDITTEXT IDC_VSCALE,121,75,45,12,ES_AUTOHSCROLL + CONTROL "Spin2",IDC_SPIN_VSCALE,"msctls_updown32",UDS_ALIGNRIGHT | + UDS_AUTOBUDDY | UDS_ARROWKEYS,155,75,11,14 + EDITTEXT IDC_ROTATE,121,92,45,12,ES_AUTOHSCROLL + CONTROL "Spin2",IDC_SPIN_ROTATE,"msctls_updown32",UDS_ALIGNRIGHT | + UDS_AUTOBUDDY | UDS_ARROWKEYS,155,91,11,14 + EDITTEXT IDC_VALUE,104,110,62,12,ES_AUTOHSCROLL + PUSHBUTTON "Reset",IDC_BTN_FACERESET,51,133,34,14 + PUSHBUTTON "Axial",IDC_BTN_AXIAL,13,149,34,14 + PUSHBUTTON "Fit",IDC_BTN_FACEFIT,51,149,34,14 + EDITTEXT IDC_EDIT_WIDTH,87,149,38,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin2",IDC_SPIN_WIDTH,"msctls_updown32",UDS_WRAP | + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS | UDS_NOTHOUSANDS,115,151,11,23 + EDITTEXT IDC_EDIT_HEIGHT,127,149,38,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin1",IDC_SPIN_HEIGHT,"msctls_updown32",UDS_WRAP | + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS | UDS_NOTHOUSANDS,153,148,11,25 + PUSHBUTTON "CAP",IDC_BTN_PATCHDETAILS,12,180,34,14 + PUSHBUTTON "Set...",IDC_BTN_PATCHRESET,51,180,34,14 + PUSHBUTTON "Natural",IDC_BTN_PATCHNATURAL,90,180,34,14 + PUSHBUTTON "Fit",IDC_BTN_PATCHFIT,129,180,34,14 + DEFPUSHBUTTON "OK",IDOK,19,211,40,14 + PUSHBUTTON "Apply",IDAPPLY,69,211,40,14 + PUSHBUTTON "Cancel",ID_BTN_CANCEL,117,211,40,14 + CONTROL "light",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,184,10,53,8 + CONTROL "slick",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,184,20,53,8 + CONTROL "sky",IDC_CHECK3,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,184,30,54,8 + CONTROL "warp",IDC_CHECK4,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,184,40,52,8 + CONTROL "trans33",IDC_CHECK5,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,184,50,54,8 + CONTROL "trans66",IDC_CHECK6,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,184,60,51,8 + CONTROL "flowing",IDC_CHECK7,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,184,70,54,8 + CONTROL "nodraw",IDC_CHECK8,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,184,80,51,8 + CONTROL "hint",IDC_CHECK9,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,184,90,53,8 + CONTROL "skip",IDC_CHECK10,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,184,100,55,8 + CONTROL "400",IDC_CHECK11,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,184,110,54,8 + CONTROL "800",IDC_CHECK12,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,184,120,55,8 + CONTROL "1000",IDC_CHECK13,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,184,130,54,8 + CONTROL "2000",IDC_CHECK14,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,184,140,56,8 + CONTROL "Non-Solid",IDC_CHECK15,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,183,151,54,8 + CONTROL "8000",IDC_CHECK16,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,184,160,53,8 + CONTROL "10000",IDC_CHECK17,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,242,10,48,8 + CONTROL "20000",IDC_CHECK18,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,242,20,70,8 + CONTROL "40000",IDC_CHECK19,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,242,30,41,8 + CONTROL "80000",IDC_CHECK20,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,242,40,41,8 + CONTROL "100000",IDC_CHECK21,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,242,50,41,8 + CONTROL "200000",IDC_CHECK22,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,242,60,41,8 + CONTROL "400000",IDC_CHECK23,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,242,70,41,8 + CONTROL "800000",IDC_CHECK24,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,242,80,41,8 + CONTROL "1000000",IDC_CHECK25,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,242,90,41,8 + CONTROL "2000000",IDC_CHECK26,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,242,100,41,8 + CONTROL "4000000",IDC_CHECK27,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,242,110,41,8 + CONTROL "8000000",IDC_CHECK28,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,242,120,41,8 + CONTROL "10000000",IDC_CHECK29,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,242,130,40,8 + CONTROL "20000000",IDC_CHECK30,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,242,140,45,8 + CONTROL "40000000",IDC_CHECK31,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,242,150,45,8 + CONTROL "80000000",IDC_CHECK32,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,242,160,45,8 + CONTROL "solid",IDC_CHECK33,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,330,9,41,8 + CONTROL "window",IDC_CHECK34,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,330,20,41,8 + CONTROL "aux",IDC_CHECK35,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,330,30,41,8 + CONTROL "lava",IDC_CHECK36,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,330,41,41,8 + CONTROL "slime",IDC_CHECK37,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,330,49,41,8 + CONTROL "water",IDC_CHECK38,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,330,60,41,8 + CONTROL "mist",IDC_CHECK39,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,330,70,41,8 + CONTROL "80",IDC_CHECK40,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,330,81,41,8 + CONTROL "100",IDC_CHECK41,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,330,89,41,8 + CONTROL "200",IDC_CHECK42,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,330,100,41,8 + CONTROL "400",IDC_CHECK43,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,330,110,41,8 + CONTROL "800",IDC_CHECK44,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,330,121,41,8 + CONTROL "1000",IDC_CHECK45,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,330,129,41,8 + CONTROL "2000",IDC_CHECK46,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,330,140,41,8 + CONTROL "4000",IDC_CHECK47,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,330,150,41,8 + CONTROL "8000",IDC_CHECK48,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,330,161,41,8 + CONTROL "playerclip",IDC_CHECK49,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,379,10,41,8 + CONTROL "monsterclip",IDC_CHECK50,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,379,20,50,8 + CONTROL "current_0",IDC_CHECK51,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,379,31,50,8 + CONTROL "current_90",IDC_CHECK52,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,379,41,50,8 + CONTROL "current_180",IDC_CHECK53,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,379,50,50,8 + CONTROL "current_270",IDC_CHECK54,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,379,60,50,8 + CONTROL "current_up",IDC_CHECK55,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,379,71,50,8 + CONTROL "current_dn",IDC_CHECK56,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,379,81,50,8 + CONTROL "origin",IDC_CHECK57,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,379,90,41,8 + CONTROL "monster",IDC_CHECK58,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,379,100,41,8 + CONTROL "corpse",IDC_CHECK59,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,379,111,41,8 + CONTROL "Detail",IDC_CHECK60,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,379,120,41,8 + CONTROL "translucent",IDC_CHECK61,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,379,130,50,8 + CONTROL "ladder",IDC_CHECK62,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,379,140,45,8 + CONTROL "40000000",IDC_CHECK63,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,379,151,45,8 + CONTROL "Non-Solid",IDC_CHECK64,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,379,163,45,8 + RTEXT "Vertical shift",IDC_STATIC,51,43,65,8 + RTEXT "Horizontal stretch",IDC_STATIC,51,60,65,8 + RTEXT "Vertical stretch",IDC_STATIC,51,77,65,8 + RTEXT "Rotate",IDC_STATIC,86,94,30,8 + RTEXT "Value",IDC_STATIC_VALUE,16,112,84,8 + LTEXT "Texture",IDC_STATIC,6,10,28,8 + RTEXT "Horizontal shift",IDC_STATIC,51,28,65,8 + GROUPBOX "Texturing",IDC_STATIC_PATCHTEXTURING,6,125,161,76 + LTEXT "Patch",IDC_STATIC_PATCH,13,168,20,8 + LTEXT "Width",IDC_STATIC,93,138,20,8 + LTEXT "Height",IDC_STATIC,128,138,22,8 + LTEXT "Brush",IDC_STATIC,13,136,19,8 +END + +IDD_DLG_PREFS DIALOG DISCARDABLE 0, 0, 386, 327 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "QERadiant Preferences" +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_COMBO_WHATGAME,13,18,157,76,CBS_DROPDOWN | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + CONTROL "Entities are DLL based",IDC_CHECK_DLLENTITIES,"Button", + BS_AUTOCHECKBOX | BS_TOP | BS_MULTILINE | NOT WS_VISIBLE | + WS_TABSTOP,291,190,88,12 + CONTROL "&2 button",IDC_RADIO_MOUSE,"Button",BS_AUTORADIOBUTTON | + WS_GROUP,188,17,42,10 + CONTROL "&3 button",IDC_RADIO2,"Button",BS_AUTORADIOBUTTON,235, + 17,42,10 + EDITTEXT IDC_EDIT_STATUSPOINTSIZE,282,244,20,12,ES_AUTOHSCROLL + CONTROL "Wide toolbar",IDC_CHECK_WIDETOOLBAR,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,193,135,56,10 + CONTROL "",IDC_RADIO_VIEWTYPE,"Button",BS_AUTORADIOBUTTON | + WS_GROUP,18,69,10,10 + CONTROL "",IDC_RADIO_VIEWTYPE2,"Button",BS_AUTORADIOBUTTON,45,69, + 11,10 + CONTROL "",IDC_RADIO_VIEWTYPE3,"Button",BS_AUTORADIOBUTTON,73,69, + 11,10 + CONTROL "",IDC_RADIO_VIEWTYPE4,"Button",BS_AUTORADIOBUTTON,99,69, + 11,10 + CONTROL "Slider1",IDC_SLIDER_CAMSPEED,"msctls_trackbar32", + WS_TABSTOP,128,57,92,11 + CONTROL "Update XY views during\nmouse drags", + IDC_CHECK_CAMXYUPDATE,"Button",BS_AUTOCHECKBOX | + BS_MULTILINE | WS_TABSTOP,130,82,91,18 + CONTROL "Right click to drop entities",IDC_CHECK_RIGHTCLICK, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,135,95,10 + CONTROL "Face selection",IDC_CHECK_FACE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,13,147,62,10 + CONTROL "QE4 update model",IDC_CHECK_QE4PAINTING,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,130,102,75,10 + CONTROL "Texture subset",IDC_CHECK_TEXTUREWINDOW,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,308,94,63,10 + CONTROL "Texture toolbar",IDC_CHECK_TEXTURETOOLBAR,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,236,94,63,10 + CONTROL "Snap T to Grid",IDC_CHECK_SNAPT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,114,147,62,10 + CONTROL "Light drawing",IDC_CHECK_LIGHTDRAW,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,193,147,58,10 + CONTROL "ALT + multi-drag",IDC_CHECK_ALTDRAG,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,114,135,68,10 + CONTROL "Paint sizing info",IDC_CHECK_SIZEPAINT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,193,158,65,10 + EDITTEXT IDC_EDIT_QUAKE2,13,189,229,12,ES_AUTOHSCROLL + PUSHBUTTON "...",IDC_BTN_BROWSE,248,190,16,11 + CONTROL "Use internal (DLL) QBSP....",IDC_CHECK_INTERNALBSP, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,208,102,10 + CONTROL "Use +setgame for run",IDC_CHECK_SETGAME,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,224,208,83,10 + CONTROL "Don't clamp plane points",IDC_CHECK_NOCLAMP,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,14,220,93,10 + CONTROL "Load last project on open",IDC_CHECK_LOADLAST,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,121,221,96,10 + CONTROL "Load last map on open",IDC_CHECK_LOADLASTMAP,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,224,220,88,10 + CONTROL "Run game after QBSP3...",IDC_CHECK_RUNQUAKE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,121,208,96,10 + CONTROL "Write face color info",IDC_CHECK_FACECOLOR,"Button", + BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,291,214,79, + 10 + CONTROL "Auto save every ",IDC_CHECK_AUTOSAVE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,14,232,66,10 + EDITTEXT IDC_EDIT_AUTOSAVE,86,232,27,12,ES_AUTOHSCROLL | + ES_NUMBER + CONTROL "Spin1",IDC_SPIN_AUTOSAVE,"msctls_updown32",UDS_WRAP | + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,110,231,11,14 + CONTROL "Snapshots, max",IDC_CHECK_SNAPSHOTS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,14,244,69,10 + CONTROL "Use PAK file:",IDC_CHECK_PAK,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,13,263,57,10 + EDITTEXT IDC_EDIT_PAKFILE,70,262,227,12,ES_AUTOHSCROLL + PUSHBUTTON "...",IDC_BTN_BROWSEPAK,303,263,16,11 + EDITTEXT IDC_EDIT_PREFABPATH,70,279,227,12,ES_AUTOHSCROLL + PUSHBUTTON "...",IDC_BTN_BROWSEPREFAB,303,280,16,11 + EDITTEXT IDC_EDIT_USERPATH,70,296,227,12,ES_AUTOHSCROLL + PUSHBUTTON "...",IDC_BTN_BROWSEUSERINI,303,297,16,11 + DEFPUSHBUTTON "OK",IDOK,341,7,38,14 + PUSHBUTTON "Cancel",IDCANCEL,341,24,38,14 + LTEXT "minutes",IDC_STATIC,114,233,25,8 + CONTROL "Vertex editing",IDC_CHECK_VERTEX,"Button", + BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | + WS_TABSTOP,291,203,83,10 + GROUPBOX "Mouse",IDC_STATIC,179,7,103,28 + GROUPBOX "Views / Rendering",IDC_STATIC,7,38,372,85 + GROUPBOX "Game path / Tool settings / Stuff that wouldn't fit anywhere else", + IDC_STATIC,7,177,372,141 + CONTROL 147,IDB_VIEWDEFAULT,"Static",SS_BITMAP,13,48,21,19 + CONTROL 148,IDB_VIEWDEFAULT2,"Static",SS_BITMAP,40,48,21,19 + CONTROL 149,IDB_VIEWDEFAULT3,"Static",SS_BITMAP,67,48,21,19 + GROUPBOX "New functionality:",IDC_STATIC,7,124,372,52 + CONTROL 150,IDB_VIEWDEFAULT_Z,"Static",SS_BITMAP,93,48,21,19 + LTEXT "Status point size:",IDC_STATIC,224,246,54,8 + LTEXT "slow",IDC_STATIC,131,69,15,8 + LTEXT "fast",IDC_STATIC,204,69,12,8 + GROUPBOX "Camera ",IDC_STATIC,126,47,100,72 + LTEXT "Prefab path:",IDC_STATIC,25,280,40,8 + GROUPBOX "Optimize interface for",IDC_STATIC,7,7,170,28 + LTEXT "User INI path:",IDC_STATIC,21,298,45,8 + LTEXT "Rotation inc:",IDC_STATIC,15,161,41,8 + EDITTEXT IDC_EDIT_ROTATION,58,159,24,12,ES_AUTOHSCROLL + CONTROL "Use SGI OpenGL",IDC_CHECK_SGIOPENGL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,15,81,71,10 + CONTROL "Buggy ICD",IDC_CHECK_BUGGYICD,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,15,92,50,10 + CONTROL "Hi Color Textures",IDC_CHECK_HICOLOR,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,281,135,70,10 + CONTROL "Mouse chaser",IDC_CHECK_MOUSECHASE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,114,158,68,10 + CONTROL "Texture scrollbar",IDC_CHECK_TEXTURESCROLLBAR,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,236,105,68,10 + CONTROL "OpenGL display lists",IDC_CHECK_DISPLAYLISTS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,15,103,79,10 + CONTROL "Use Shaders",IDC_CHECK_USESHADERS,"Button", + BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | + WS_TABSTOP,322,230,57,10 + GROUPBOX "Texturing",IDC_STATIC,231,47,141,72 + LTEXT "Quality",IDC_STATIC,237,57,22,8 + CONTROL "Slider1",IDC_SLIDER_TEXTUREQUALITY,"msctls_trackbar32", + TBS_AUTOTICKS | WS_TABSTOP,237,68,127,11 + LTEXT "Low",IDC_STATIC,239,81,14,8 + LTEXT "High",IDC_STATIC,347,82,16,8 + CONTROL "Shader Test",IDC_CHECK_SHADERTEST,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,280,161,55,10 + EDITTEXT IDC_EDIT_NUMSNAPSHOTS,86,243,27,12,ES_AUTOHSCROLL | + ES_NUMBER + CONTROL "Spin1",IDC_SPIN_AUTOSAVE2,"msctls_updown32",UDS_WRAP | + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,110,242,11,14 + LTEXT "files",IDC_STATIC,114,245,15,8 +END + +IDD_DLG_MAPINFO DIALOG DISCARDABLE 0, 0, 181, 183 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Map Info" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "Close",IDOK,138,7,36,14 + LTEXT "Total Brushes",IDC_STATIC,7,14,50,8 + EDITTEXT IDC_EDIT_TOTALBRUSHES,66,12,40,12,ES_RIGHT | + ES_AUTOHSCROLL | ES_NUMBER + LTEXT "Total Entities",IDC_STATIC,7,30,50,8 + EDITTEXT IDC_EDIT_TOTALENTITIES,66,28,40,12,ES_RIGHT | + ES_AUTOHSCROLL | ES_NUMBER + LTEXT "Net brush count\n(non entity)",IDC_STATIC,7,49,57,17 + EDITTEXT IDC_EDIT_NET,66,47,40,12,ES_RIGHT | ES_AUTOHSCROLL | + ES_NUMBER + LISTBOX IDC_LIST_ENTITIES,7,81,167,95,LBS_SORT | LBS_USETABSTOPS | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + LTEXT "Entity breakdown",IDC_STATIC,7,71,56,8 +END + +IDD_DLG_ENTITYLIST DIALOG DISCARDABLE 0, 0, 294, 226 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Entities" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "Close",IDOK,248,205,39,14 + CONTROL "Tree1",IDC_TREE_ENTITY,"SysTreeView32",TVS_HASBUTTONS | + TVS_HASLINES | TVS_LINESATROOT | WS_BORDER | WS_TABSTOP, + 7,7,148,212 + DEFPUSHBUTTON "Select",IDSELECT,161,205,39,14 + CONTROL "List2",IDC_LIST_ENTITY,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_EDITLABELS | LVS_NOSORTHEADER | + WS_BORDER | WS_TABSTOP,161,7,126,187 +END + +IDD_DLG_SCRIPTS DIALOG DISCARDABLE 0, 0, 212, 215 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Available Scripts" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "New...",ID_NEW,166,74,39,14,WS_DISABLED + PUSHBUTTON "Close",IDOK,168,194,37,14 + LISTBOX IDC_LIST_SCRIPTS,7,56,146,152,LBS_SORT | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "Edit...",ID_EDIT,166,90,39,14,WS_DISABLED + DEFPUSHBUTTON "Run",ID_RUN,166,56,39,14 + LTEXT "WARNING: BrushScripting is in a highly experimental state and is far from complete. If you attempt to use them it is VERY LIKELY that QERadiant will crash. Save your work before attempting to make use of any scripting features.", + IDC_STATIC,7,7,198,38 +END + +IDD_DLG_NEWPROJECT DIALOG DISCARDABLE 0, 0, 246, 74 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "New project" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_EDIT_NAME,57,28,116,12,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,189,7,50,14 + PUSHBUTTON "Cancel",IDCANCEL,189,24,50,14 + LTEXT "This will create a new directory beneath your Quake2 path based on the project name you give.", + IDC_STATIC,7,7,165,19 + LTEXT "Project name:",IDC_STATIC,7,30,44,8 + CONTROL "Include game dll files",IDC_CHECK1,"Button", + BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,7,49,81,10 +END + +IDD_DLG_COMMANDLIST DIALOG DISCARDABLE 0, 0, 283, 223 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Mapped Commands" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "Close",IDOK,241,202,35,14 + LISTBOX IDC_LIST_COMMANDS,7,7,221,209,LBS_SORT | LBS_USETABSTOPS | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP +END + +IDD_DIALOG_SCALE DIALOG DISCARDABLE 0, 0, 122, 74 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Scale" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,79,7,36,14 + PUSHBUTTON "Cancel",IDCANCEL,79,23,36,14 + LTEXT "X:",IDC_STATIC,7,15,8,8 + LTEXT "Y:",IDC_STATIC,7,32,8,8 + LTEXT "Z:",IDC_STATIC,7,49,8,8 + EDITTEXT IDC_EDIT_X,22,13,32,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_Y,22,30,32,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_Z,22,47,32,12,ES_AUTOHSCROLL +END + +IDD_DIALOG_FINDREPLACE DIALOGEX 0, 0, 222, 167 +STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +CAPTION "Find replace texture(s)" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + EDITTEXT IDC_EDIT_FIND,27,16,132,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_REPLACE,45,31,114,12,ES_AUTOHSCROLL + CONTROL "Replace within selected brushes only", + IDC_CHECK_SELECTED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 7,81,133,10 + CONTROL "Force replacement (ignore current texture name)", + IDC_CHECK_FORCE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7, + 93,167,10 + DEFPUSHBUTTON "OK",IDOK,175,7,40,14 + PUSHBUTTON "Close",IDCANCEL,175,135,40,14 + LTEXT "Find:",IDC_STATIC,7,18,16,8 + LTEXT "Replace:",IDC_STATIC,7,33,30,8 + DEFPUSHBUTTON "Apply",ID_BTN_APPLY,175,25,40,14 + CONTROL "Live updates from Texture/Camera Windows", + IDC_CHECK_LIVE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7, + 105,157,10 + PUSHBUTTON "Batch Find/Replace from text file", + IDC_BUTTON_BATCHFINDREPLACE,7,48,208,14 + CONTROL "Re-scale coords during change",IDC_CHECK_RESCALE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,69,115,10 + CONTROL "Select only, no replace (this overrides ALL above options)", + IDC_CHECK_SELECTONLY,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,117,200,10 +END + +IDD_DIALOG_STAIRS DIALOG DISCARDABLE 0, 0, 196, 143 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Stairs" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,147,7,42,14 + PUSHBUTTON "Cancel",IDCANCEL,136,24,42,14 + LTEXT "This creates a set of stairs using the selected brush as a template. You may optionally specify a rotation angle to be applied to each new step", + IDC_STATIC,7,7,120,37 + LTEXT "After pressing OK, left click to select a ""height"" point for the stairs. Enough stairs will be created to consume the hieght (Z) between the selected brush and the point you specify", + IDC_STATIC,7,46,116,53 + LTEXT "Rotation per step:",IDC_STATIC,7,104,57,8 + EDITTEXT IDC_EDIT_ROTATION,69,102,40,12,ES_AUTOHSCROLL +END + +IDD_DIALOG_INPUT DIALOG DISCARDABLE 0, 0, 171, 173 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "BrushScript Input" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,127,7,37,14 + PUSHBUTTON "Cancel",IDCANCEL,127,24,37,14 + LTEXT "Field1:",IDC_STATIC_FIELD1,7,13,96,8 + EDITTEXT IDC_EDIT_FIELD1,7,23,47,14,ES_AUTOHSCROLL + LTEXT "Field1:",IDC_STATIC_FIELD2,7,41,105,8 + EDITTEXT IDC_EDIT_FIELD2,7,51,47,14,ES_AUTOHSCROLL + LTEXT "Field1:",IDC_STATIC_FIELD3,7,71,115,8 + EDITTEXT IDC_EDIT_FIELD3,7,81,47,14,ES_AUTOHSCROLL + LTEXT "Field1:",IDC_STATIC_FIELD4,7,103,129,8 + EDITTEXT IDC_EDIT_FIELD4,7,113,47,14,ES_AUTOHSCROLL + LTEXT "Field1:",IDC_STATIC_FIELD5,7,133,135,8 + EDITTEXT IDC_EDIT_FIELD5,7,142,47,14,ES_AUTOHSCROLL +END + +IDD_DLG_INFORMATION DIALOG DISCARDABLE 0, 0, 186, 95 +STYLE WS_POPUP | WS_CAPTION +CAPTION "Information" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_EDIT1,7,7,172,81,ES_MULTILINE | WS_DISABLED | + WS_VSCROLL +END + +IDD_PROJECT DIALOGEX 250, 100, 341, 258 +STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU +CAPTION "Project Settings" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,290,10,45,13 + PUSHBUTTON "Cancel",IDCANCEL,290,26,45,13 + GROUPBOX "Project settings",IDC_STATIC,5,7,280,133 + EDITTEXT IDC_PRJBASEPATH,75,20,205,12,ES_AUTOHSCROLL + LTEXT "basepath",IDC_STATIC,42,22,30,8 + LTEXT "rshcmd",IDC_STATIC,48,62,24,8 + EDITTEXT IDC_PRJRSHCMD,75,60,205,12,ES_AUTOHSCROLL + LTEXT "remotebasepath",IDC_STATIC,20,82,52,8 + EDITTEXT IDC_PRJREMOTEBASE,75,80,205,12,ES_AUTOHSCROLL + LTEXT "entitypath",IDC_STATIC,40,102,32,8 + EDITTEXT IDC_PRJENTITYPATH,75,100,205,12,ES_AUTOHSCROLL + LTEXT "texturepath",IDC_STATIC,36,122,36,8 + EDITTEXT IDC_PRJTEXPATH,75,120,205,12,ES_AUTOHSCROLL + GROUPBOX "Menu commands",IDC_STATIC,5,146,280,105 + PUSHBUTTON "Add...",IDC_ADDCMD,290,150,45,13 + LISTBOX IDC_CMD_LIST,10,157,270,90,LBS_SORT | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP, + WS_EX_CLIENTEDGE + PUSHBUTTON "Remove",IDC_REMCMD,290,184,45,13 + LTEXT "mapspath",IDC_STATIC,40,42,32,8 + EDITTEXT IDC_PRJMAPSPATH,75,40,205,12,ES_AUTOHSCROLL + PUSHBUTTON "Change...",IDC_EDITCMD,290,167,45,13 +END + +IDD_ADDCMD DIALOG DISCARDABLE 300, 200, 312, 65 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Add command" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,116,44,37,13 + PUSHBUTTON "Cancel",IDCANCEL,159,44,37,13 + EDITTEXT IDC_CMDMENUTEXT,44,8,254,12,ES_AUTOHSCROLL + EDITTEXT IDC_CMDCOMMAND,44,26,254,12,ES_AUTOHSCROLL + LTEXT "Menu text",IDC_STATIC,5,10,32,8 + LTEXT "Command",IDC_STATIC,5,28,32,8 +END + +IDD_TEXTUREBAR DIALOG DISCARDABLE 0, 0, 313, 19 +STYLE WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_HSHIFT,33,4,32,12,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN_HSHIFT,"msctls_updown32",UDS_ALIGNRIGHT | + UDS_AUTOBUDDY | UDS_ARROWKEYS,56,3,10,14 + EDITTEXT IDC_VSHIFT,76,4,32,12,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN_VSHIFT,"msctls_updown32",UDS_ALIGNRIGHT | + UDS_AUTOBUDDY | UDS_ARROWKEYS,97,3,11,14 + EDITTEXT IDC_HSCALE,143,4,32,12,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN_HSCALE,"msctls_updown32",UDS_ALIGNRIGHT | + UDS_AUTOBUDDY | UDS_ARROWKEYS,164,3,11,14 + EDITTEXT IDC_VSCALE,188,4,32,12,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN_VSCALE,"msctls_updown32",UDS_ALIGNRIGHT | + UDS_AUTOBUDDY | UDS_ARROWKEYS,210,3,11,14 + EDITTEXT IDC_ROTATE,252,4,32,12,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN_ROTATE,"msctls_updown32",UDS_ALIGNRIGHT | + UDS_AUTOBUDDY | UDS_ARROWKEYS,272,3,11,14 + LTEXT "Shift H",IDC_STATIC,7,6,22,8 + LTEXT "V",IDC_STATIC,68,6,8,8 + LTEXT "Scale H",IDC_STATIC,112,6,26,8 + LTEXT "V",IDC_STATIC,180,6,8,8 + LTEXT "Rotate",IDC_STATIC,226,6,22,8 + DEFPUSHBUTTON "Button1",IDC_BTN_APPLYTEXTURESTUFF,278,2,11,8,NOT + WS_VISIBLE | NOT WS_TABSTOP + EDITTEXT IDC_EDIT_ROTATEAMT,287,4,20,12,ES_AUTOHSCROLL +END + +IDD_DIALOG_TEXTURELIST DIALOG DISCARDABLE 0, 0, 257, 279 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Textures" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "&Load",IDOK,205,7,45,14 + PUSHBUTTON "Close",IDCANCEL,205,23,45,14 + LISTBOX IDC_LIST_TEXTURES,7,7,189,265,LBS_SORT | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP +END + +IDD_DIALOG_NEWPATCH DIALOG DISCARDABLE 0, 0, 129, 58 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Patch density" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,90,7,32,14 + PUSHBUTTON "Cancel",IDCANCEL,90,24,32,14 + LTEXT "Width:",IDC_STATIC,9,9,22,8 + LTEXT "Height:",IDC_STATIC,7,27,24,8 + COMBOBOX IDC_COMBO_WIDTH,38,7,33,51,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_COMBO_HEIGHT,38,25,33,51,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP +END + +IDD_DIALOG_TEXTURELAYOUT DIALOG DISCARDABLE 0, 0, 186, 95 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Patch texture layout" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,141,7,38,14 + PUSHBUTTON "Cancel",IDCANCEL,141,24,38,14 + LTEXT "Texture will be fit across the patch based on the x and y values given. Values of 1x1 will ""fit"" the texture. 2x2 will repeat it twice, etc.", + IDC_STATIC,7,7,122,36 + LTEXT "Texture x:",IDC_STATIC,7,48,32,8 + EDITTEXT IDC_EDIT_X,42,46,30,12,ES_AUTOHSCROLL + LTEXT "Texture y:",IDC_STATIC,7,64,32,8 + EDITTEXT IDC_EDIT_Y,42,62,30,12,ES_AUTOHSCROLL +END + +IDD_DIALOG_CAP DIALOG DISCARDABLE 0, 0, 171, 84 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Cap" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,123,7,41,14 + PUSHBUTTON "Cancel",IDCANCEL,123,25,41,14 + CONTROL "Bevel",IDC_RADIO_CAP,"Button",BS_AUTORADIOBUTTON | + WS_GROUP,31,10,50,10 + CONTROL "Endcap",IDC_RADIO_CAP2,"Button",BS_AUTORADIOBUTTON,31, + 28,50,10 + CONTROL "Inverted Bevel",IDC_RADIO_CAP3,"Button", + BS_AUTORADIOBUTTON,31,47,68,10 + CONTROL "Inverted Endcap",IDC_RADIO_CAP4,"Button", + BS_AUTORADIOBUTTON,31,65,75,10 + CONTROL 177,IDC_STATIC,"Static",SS_BITMAP | SS_REALSIZEIMAGE | + SS_SUNKEN,7,7,18,15 + CONTROL 178,IDC_STATIC,"Static",SS_BITMAP | SS_REALSIZEIMAGE | + SS_SUNKEN,7,44,18,15 + CONTROL 176,IDC_STATIC,"Static",SS_BITMAP | SS_REALSIZEIMAGE | + SS_SUNKEN,7,26,18,15 + CONTROL 175,IDC_STATIC,"Static",SS_BITMAP | SS_REALSIZEIMAGE | + SS_SUNKEN,7,63,17,14 +END + +IDD_DIALOG_THICKEN DIALOG DISCARDABLE 0, 0, 204, 63 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Thicken Patch" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,158,7,39,14 + PUSHBUTTON "Cancel",IDCANCEL,158,24,39,14 + LTEXT "This produces a func_grouped set of patches that contains the original patch along with the 'thick' patch and an optional set of seam patches. ", + IDC_STATIC,7,7,145,33 + EDITTEXT IDC_EDIT_AMOUNT,38,42,26,12,ES_AUTOHSCROLL + LTEXT "Amount:",IDC_STATIC,7,44,27,8 + CONTROL "Seams",IDC_CHECK_SEAMS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,74,43,37,10 +END + +IDD_SOURCESAFE DIALOG DISCARDABLE 0, 0, 267, 207 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "SourceSafe settings ( & other stuff )" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,87,186,50,14 + PUSHBUTTON "Cancel",IDCANCEL,162,186,50,14 + EDITTEXT IDC_EDIT_SS_INIPATH,65,18,165,14,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_SS_PROJECTPATH,65,38,165,14,ES_AUTOHSCROLL + LTEXT "INI path:",IDC_STATIC,28,20,27,8 + LTEXT "Project path:",IDC_STATIC,16,40,46,8 + CONTROL "Use SS?",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | WS_TABSTOP,24,58,49,10 + PUSHBUTTON "Default everything to Voyager settings", + IDC_BUTTON_DEFAULTALL,65,118,165,23 + EDITTEXT IDC_EDIT_SS_BEHAVEDPATH,65,89,165,14,ES_AUTOHSCROLL + GROUPBOX "Other Stuff I added later that's actually smeg-all to do with SS:", + IDC_STATIC,6,75,255,37 + LTEXT "BehavEd path:",IDC_STATIC,11,93,48,8 + PUSHBUTTON "Default everything to StarWars settings", + IDC_BUTTON_DEFAULTALL2,65,149,165,23 +END + +IDD_ENTFINDREPLACE DIALOG DISCARDABLE 0, 0, 425, 129 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Entity key Find / Replace (F3 = find next)" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "Find",IDFIND,35,100,121,14 + PUSHBUTTON "Cancel",IDCANCEL,161,100,87,14 + EDITTEXT IDC_EDIT_FIND_KEY,35,21,121,14,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_FIND_VALUE,35,43,121,14,ES_AUTOHSCROLL + LTEXT "Key",IDC_STATIC,19,24,13,8 + LTEXT "Value",IDC_STATIC,14,45,19,8 + EDITTEXT IDC_EDIT_REPLACE_KEY,253,21,121,14,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_REPLACE_VALUE,253,43,121,14,ES_AUTOHSCROLL + LTEXT "Key",IDC_STATIC,237,24,13,8 + LTEXT "Value",IDC_STATIC,231,45,19,8 + GROUPBOX "FIND",IDC_STATIC,7,10,175,79,BS_CENTER + GROUPBOX "REPLACE",IDC_STATIC,225,10,173,79,BS_CENTER + DEFPUSHBUTTON "Replace",IDREPLACE,254,100,121,14 + PUSHBUTTON "->",IDC_KEYCOPY,190,21,29,14 + PUSHBUTTON "->",IDC_VALUECOPY,190,43,29,14 + CONTROL "Whole-string match only", + IDC_CHECK_FIND_WHOLESTRINGMATCHONLY,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,35,62,121,10 + CONTROL "Select all matching ents at once", + IDC_CHECK_SELECTALLMATCHING,"Button",BS_AUTOCHECKBOX | + BS_MULTILINE | WS_TABSTOP,35,74,116,9 +END + +IDD_DIALOG_GETSTRING DIALOG DISCARDABLE 0, 0, 186, 95 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Input needed..." +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,29,74,50,14 + PUSHBUTTON "Cancel",IDCANCEL,101,74,50,14 + LTEXT "Static",IDC_PROMPT,13,13,159,34 + EDITTEXT IDC_EDIT1,11,51,163,14,ES_AUTOHSCROLL +END + + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "\0" + VALUE "CompanyName", "\0" + VALUE "FileDescription", "Radiant MFC Application\0" + VALUE "FileVersion", "1, 0, 0, 1\0" + VALUE "InternalName", "Radiant\0" + VALUE "LegalCopyright", "Copyright (C) 1997\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "Radiant.EXE\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "Radiant Application\0" + VALUE "ProductVersion", "1, 0, 0, 1\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_ENTITY, DIALOG + BEGIN + RIGHTMARGIN, 234 + BOTTOMMARGIN, 354 + END + + IDD_GAMMA, DIALOG + BEGIN + RIGHTMARGIN, 127 + BOTTOMMARGIN, 76 + END + + IDD_SIDES, DIALOG + BEGIN + RIGHTMARGIN, 126 + BOTTOMMARGIN, 45 + END + + IDD_SURFACE, DIALOG + BEGIN + RIGHTMARGIN, 429 + BOTTOMMARGIN, 233 + END + + IDD_DLG_PREFS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 379 + TOPMARGIN, 7 + BOTTOMMARGIN, 320 + END + + IDD_DLG_MAPINFO, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 174 + TOPMARGIN, 7 + BOTTOMMARGIN, 176 + END + + IDD_DLG_ENTITYLIST, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 287 + TOPMARGIN, 7 + BOTTOMMARGIN, 219 + END + + IDD_DLG_SCRIPTS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 205 + TOPMARGIN, 7 + BOTTOMMARGIN, 208 + END + + IDD_DLG_NEWPROJECT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 239 + TOPMARGIN, 7 + BOTTOMMARGIN, 67 + END + + IDD_DLG_COMMANDLIST, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 276 + TOPMARGIN, 7 + BOTTOMMARGIN, 216 + END + + IDD_DIALOG_SCALE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 115 + TOPMARGIN, 7 + BOTTOMMARGIN, 67 + END + + IDD_DIALOG_FINDREPLACE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 215 + TOPMARGIN, 7 + BOTTOMMARGIN, 149 + END + + IDD_DIALOG_STAIRS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 189 + TOPMARGIN, 7 + BOTTOMMARGIN, 136 + END + + IDD_DIALOG_INPUT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 164 + TOPMARGIN, 7 + BOTTOMMARGIN, 166 + END + + IDD_DLG_INFORMATION, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 88 + END + + IDD_ADDCMD, DIALOG + BEGIN + BOTTOMMARGIN, 60 + END + + IDD_DIALOG_TEXTURELIST, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 250 + TOPMARGIN, 7 + BOTTOMMARGIN, 272 + END + + IDD_DIALOG_NEWPATCH, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 122 + TOPMARGIN, 7 + BOTTOMMARGIN, 51 + END + + IDD_DIALOG_TEXTURELAYOUT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 88 + END + + IDD_DIALOG_CAP, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 164 + TOPMARGIN, 7 + BOTTOMMARGIN, 77 + END + + IDD_DIALOG_THICKEN, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 197 + TOPMARGIN, 7 + BOTTOMMARGIN, 56 + END + + IDD_SOURCESAFE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 260 + TOPMARGIN, 7 + BOTTOMMARGIN, 200 + END + + IDD_ENTFINDREPLACE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 418 + TOPMARGIN, 7 + BOTTOMMARGIN, 122 + END + + IDD_DIALOG_GETSTRING, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 88 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog Info +// + +IDD_DIALOG_NEWPATCH DLGINIT +BEGIN + IDC_COMBO_WIDTH, 0x403, 2, 0 +0x0033, + IDC_COMBO_WIDTH, 0x403, 2, 0 +0x0035, + IDC_COMBO_WIDTH, 0x403, 2, 0 +0x0037, + IDC_COMBO_WIDTH, 0x403, 2, 0 +0x0039, + IDC_COMBO_WIDTH, 0x403, 3, 0 +0x3131, "\000" + IDC_COMBO_WIDTH, 0x403, 3, 0 +0x3331, "\000" + IDC_COMBO_WIDTH, 0x403, 3, 0 +0x3531, "\000" + IDC_COMBO_HEIGHT, 0x403, 2, 0 +0x0033, + IDC_COMBO_HEIGHT, 0x403, 2, 0 +0x0035, + IDC_COMBO_HEIGHT, 0x403, 2, 0 +0x0037, + IDC_COMBO_HEIGHT, 0x403, 2, 0 +0x0039, + IDC_COMBO_HEIGHT, 0x403, 3, 0 +0x3131, "\000" + IDC_COMBO_HEIGHT, 0x403, 3, 0 +0x3331, "\000" + IDC_COMBO_HEIGHT, 0x403, 3, 0 +0x3531, "\000" + 0 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + IDR_MAINFRAME "QERadiant" + IDR_RADIANTYPE "\nRadian\nRadian\n\n\nRadiant.Document\nRadian Document" +END + +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + AFX_IDS_APP_TITLE "Radiant" + AFX_IDS_IDLEMESSAGE "Ready" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_INDICATOR_EXT "EXT" + ID_INDICATOR_CAPS "CAP" + ID_INDICATOR_NUM "NUM" + ID_INDICATOR_SCRL "SCRL" + ID_INDICATOR_OVR "OVR" + ID_INDICATOR_REC "REC" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_NEW "Create a new document\nNew" + ID_FILE_OPEN "Open an existing map\nOpen" + ID_FILE_CLOSE "Close the active document\nClose" + ID_FILE_SAVE "Save the active map\nSave" + ID_FILE_SAVE_AS "Save the active document with a new name\nSave As" + ID_FILE_PAGE_SETUP "Change the printing options\nPage Setup" + ID_FILE_PRINT_SETUP "Change the printer and printing options\nPrint Setup" + ID_FILE_PRINT "Print the active document\nPrint" + ID_FILE_PRINT_PREVIEW "Display full pages\nPrint Preview" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_APP_ABOUT "Display program information, version number and copyright\nAbout" + ID_APP_EXIT "Quit the application; prompts to save documents\nExit" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_MRU_FILE1 "Open this document" + ID_FILE_MRU_FILE2 "Open this document" + ID_FILE_MRU_FILE3 "Open this document" + ID_FILE_MRU_FILE4 "Open this document" + ID_FILE_MRU_FILE5 "Open this document" + ID_FILE_MRU_FILE6 "Open this document" + ID_FILE_MRU_FILE7 "Open this document" + ID_FILE_MRU_FILE8 "Open this document" + ID_FILE_MRU_FILE9 "Open this document" + ID_FILE_MRU_FILE10 "Open this document" + ID_FILE_MRU_FILE11 "Open this document" + ID_FILE_MRU_FILE12 "Open this document" + ID_FILE_MRU_FILE13 "Open this document" + ID_FILE_MRU_FILE14 "Open this document" + ID_FILE_MRU_FILE15 "Open this document" + ID_FILE_MRU_FILE16 "Open this document" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_NEXT_PANE "Switch to the next window pane\nNext Pane" + ID_PREV_PANE "Switch back to the previous window pane\nPrevious Pane" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_WINDOW_NEW "Open another window for the active document\nNew Window" + ID_WINDOW_ARRANGE "Arrange icons at the bottom of the window\nArrange Icons" + ID_WINDOW_CASCADE "Arrange windows so they overlap\nCascade Windows" + ID_WINDOW_TILE_HORZ "Arrange windows as non-overlapping tiles\nTile Windows" + ID_WINDOW_TILE_VERT "Arrange windows as non-overlapping tiles\nTile Windows" + ID_WINDOW_SPLIT "Split the active window into panes\nSplit" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_EDIT_CLEAR "Erase the selection\nErase" + ID_EDIT_CLEAR_ALL "Erase everything\nErase All" + ID_EDIT_COPY "Copy the selection and put it on the Clipboard\nCopy" + ID_EDIT_CUT "Cut the selection and put it on the Clipboard\nCut" + ID_EDIT_FIND "Find the specified text\nFind" + ID_EDIT_PASTE "Insert Clipboard contents\nPaste" + ID_EDIT_REPEAT "Repeat the last action\nRepeat" + ID_EDIT_REPLACE "Replace specific text with different text\nReplace" + ID_EDIT_SELECT_ALL "Select the entire document\nSelect All" + ID_EDIT_UNDO "Undo the last action\nUndo" + ID_EDIT_REDO "Redo the previously undone action\nRedo" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_VIEW_TOOLBAR "Show or hide the toolbar\nToggle ToolBar" + ID_VIEW_STATUS_BAR "Show or hide the status bar\nToggle StatusBar" +END + +STRINGTABLE DISCARDABLE +BEGIN + AFX_IDS_SCSIZE "Change the window size" + AFX_IDS_SCMOVE "Change the window position" + AFX_IDS_SCMINIMIZE "Reduce the window to an icon" + AFX_IDS_SCMAXIMIZE "Enlarge the window to full size" + AFX_IDS_SCNEXTWINDOW "Switch to the next document window" + AFX_IDS_SCPREVWINDOW "Switch to the previous document window" + AFX_IDS_SCCLOSE "Close the active window and prompts to save the documents" +END + +STRINGTABLE DISCARDABLE +BEGIN + AFX_IDS_SCRESTORE "Restore the window to normal size" + AFX_IDS_SCTASKLIST "Activate Task List" + AFX_IDS_MDICHILD "Activate this window" +END + +STRINGTABLE DISCARDABLE +BEGIN + AFX_IDS_PREVIEW_CLOSE "Close print preview mode\nCancel Preview" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_VIEW_XY "View original layout (Top)" + ID_VIEW_SIDE "Side view" + ID_VIEW_FRONT "Front view" + ID_VIEW_CAMERATOGGLE "Toggle full time camera preview\nCamera preview" + ID_TEXTURES_POPUP "Texture view mode\nTexture view mode" + ID_POPUP_SELECTION "Selection\nSelection" + ID_VIEW_CHANGE "Change views\nChange views" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_VIEW_ENTITY "Toggle entity inspector\nEntity inspector" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_BRUSH_FLIPX "Flip selected brushes along x-axis\\x-axis Flip" + ID_BRUSH_FLIPY "Flip selected brushes along y-axis\ny-axis Flip" + ID_BRUSH_FLIPZ "Flip selected brushes along z-axis\nz-axis Flip" + ID_BRUSH_ROTATEX "Rotate selected brushes along x-axis\nx-axis Rotate" + ID_BRUSH_ROTATEY "Rotate selected brushes along y-axis\ny-axis Rotate" + ID_BRUSH_ROTATEZ "Rotate selected brushes along z-axis\nz-axis Rotate" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_SELECTION_SELECTCOMPLETETALL "Complete Tall\nComplete Tall" + ID_SELECTION_CSGSUBTRACT "CSG Subtract\nCSG Subtract" + ID_SELECTION_SELECTTOUCHING "Select Touching\nSelect Touching" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_SELECTION_MAKEHOLLOW "Hollow Selection\nHollow" + ID_SELECTION_SELECTPARTIALTALL "Select Partial Tall\nSelect Partial Tall" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_VIEW_CAMERAUPDATE "Update Camera\nUpdate Camera" + ID_VIEW_CLIPPER "Set clipper mode\nClipper" + ID_PREFS "Preferences\nPreferences" + ID_EDIT_MAPINFO "Entity Information\nEntity list" + ID_BRUSH_SCRIPTS "Define and run BrushScripts\nBrushScripts" + ID_HELP_COMMANDLIST "Provides a list of the currently bound commands" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_SELECT_MOUSEROTATE "Free rotation\nFree Rotation" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_TEXTURE_REPLACESELECTED + "Find and replace texture names in selected brushes/faces" + ID_TEXTURE_REPLACEALL "Find and replace texture names in all brushes/faces" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_SELECT_MOUSESCALE "Free Scaling\nFree Scaling" + ID_SCALELOCKX "Scale X\nScale X" + ID_SCALELOCKY "Scale Y\nScale Y" + ID_SCALELOCKZ "Scale Z\nScale Z" + ID_VIEW_CUBICCLIPPING "Cubic clip the camera view\nCubic clip the camera view" + ID_FILE_PROJECTSETTINGS "View and edit project attributes" + ID_VIEW_CUBEOUT "Zoom cubic clip out" + ID_VIEW_CUBEIN "Zoom cubic clip in" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_SAVEREGION "Save defined region" + ID_FILE_LOADREGION "Load saved region" + ID_TOOLBAR_MAIN "Standard toolbar" + ID_TOOLBAR_TEXTURE "Texture control toolbar" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_TEXTURES_LOAD "Load from a specific directory" + ID_CURVE_CYLINDER "Create a cylinder" + ID_FILE_IMPORTMAP "Load map, leaving current map intact" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_SELECTION_SELECTINSIDE "Select Inside\nSelect Inside" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_EXPORTMAP "Save selection into map file" + ID_EDIT_LOADPREFAB "Load .map from prefab path" + ID_VIEW_SHOWCURVES "Show Curved brushes" + ID_TEXTURES_LOADLIST "Loads from a list. (Good in case there are too many for the menu)" + ID_DONTSELECTCURVE "Don't select curved brushes\nDon't select curved brushes" + ID_CONVERTCURVES "Turns selected brush into a new test curve" + ID_PATCH_SHOWBOUNDINGBOX + "Show patch bounding box\nShow patch bounding box" + ID_CURVE_SIMPLEPATCHMESH "Builds a flat patch mesh, you specify the size" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_PATCH_WIREFRAME "Show patches as wireframes\nShow patches as wireframes" + ID_PATCH_WELD "Welds equal patch points during moves\nWelds equal patch points during moves" + ID_CURVE_PATCHTUBE "Create a cylinder" + ID_CURVE_PATCHENDCAP "Create an endcap" + ID_CURVE_PATCHBEVEL "Create a bevel" + ID_PATCH_DRILLDOWN "Selects drill down rows and columns\nSelects drill down rows and columns" + ID_CURVE_LOADPATCHFILE "Load corresponding .patch file\nLoad corresponding .patch file" + ID_CURVE_INSERTROW "Inserts a row in the selected patch(s)\nInserts a row in the selected patch(s)" + ID_CURVE_INSERTCOLUMN "Inserts a column in the selected patch(s)\nInserts a column in the selected patch(s)" + ID_CURVE_DELETEROW "Deletes a row in the current patch(s)\nDeletes a row in the current patch(s)" + ID_CURVE_DELETECOLUMN "Deletes a column in the current Patch(s)\nDeletes a column in the current Patch(s)" + ID_PATCH_INSDEL "Redisperse patch points\nRedisperse patch points" + ID_CURVE_INSERT_ADDCOLUMN + "Add a column to the end of the patch\nAdd a column to the end of the patch" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_CURVE_INSERT_INSERTCOLUMN + "Insert a column at the beginning of the patch\nInsert a column at the beginning of the patch" + ID_CURVE_INSERT_ADDROW "Add a row at the end of the patch\nAdd a row at the end of the patch" + ID_CURVE_INSERT_INSERTROW + "Insert a row at the beginning of the patch\nInsert a row at the beginning of the patch" + ID_CURVE_DELETE_FIRSTCOLUMN + "Delete the first (2) columns\nDelete the first (2) columns" + ID_CURVE_DELETE_LASTCOLUMN + "Delete the last (2) columns\nDelete the last (2) columns" + ID_CURVE_DELETE_FIRSTROW + "Delete the first (2) rows\nDelete the first (2) rows" + ID_CURVE_DELETE_LASTROW "Delete the last (2) rows\nDelete the last (2) rows" + ID_CURVE_NEGATIVE "Toggle negative flag\nToggle negative flag" + ID_PATCH_BEND "Patch Bend mode\nPatch Bend mode" + ID_CURVE_PATCHDENSETUBE "Create a dense tube" + ID_CURVE_PATCHVERYDENSETUBE "Create a very dense cylinder" + ID_CURVE_CAP "Put caps on the current patch\nPut caps on the current patch" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_CURVE_REDISPERSE_ROWS + "Re-disperse rows across the dimensions of the patch" + ID_CURVE_REDISPERSE_COLS + "Re-disperse columns across the dimensions of the patch" + ID_CURVE_PATCHSQUARE "Create a very square cylinder" + ID_TEXTURES_FLUSH "Flush texture palette" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_TEXTURES_RELOADSHADERS + "Reload Shaders and apply visual changes to current map" + ID_SHOW_ENTITIES "Show Entities as\nShow Entities as" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_SS_CHECKIN "Check In current map (if checked out)" + ID_SS_CHECKOUT "Check out current map (if not already)" + ID_SS_UNDOCHECKOUT "Undo checkout of current map (if checked out) and lose changes" + ID_SS_HISTORY "Show SourceSafe history of current map" + ID_SS_CONFIGURE "Change default SourceSafe config" + ID_SS_STATUS "Show overall SourceSafe status, and that of current map" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_VIEW_SHOWWAYPOINTS_ONLY + "Special option, disable everything else while active" + ID_VIEW_SHOWTRIGGER_XXXX + "Toggle showing of all trigger types (if Show Entities is on)" + ID_MISC_FINDENT "Invokes a dialog to enter search/replace criteria" + ID_SHOW_EASY "Show things that appear in EASY mode" + ID_SHOW_MEDIUM "Show things that appear in MEDIUM mode" + ID_SHOW_HARD "Show things that appear in HARD mode" + ID_SS_ADD "Add current map to SourceSafe" + ID_AUTOCAULK "Applies caulk texture to hidden-surfaces (as best I can, but not if they span multiple faces)" + ID_VIEW_SHOWFUNCGROUPS "toggle show func groups" + ID_VIEW_SHOWLIGHTRADII "Shows the radius of a light, inner dotted line = can read by, solid = max maths distance" + ID_VIEW_SHOWCURVESONLY "Show curves ONLY (special usage code for Eric B)" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#define _AFX_NO_SPLITTER_RESOURCES +#define _AFX_NO_OLE_RESOURCES +#define _AFX_NO_TRACKER_RESOURCES +#define _AFX_NO_PROPERTY_RESOURCES + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE 9, 1 +#pragma code_page(1252) +#endif +#include "res\Radiant.rc2" // non-Microsoft Visual C++ edited resources +#include "afxres.rc" // Standard components +#include "afxprint.rc" // printing/print preview resources +#endif +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/utils/Radiant/radiantdoc.h b/utils/Radiant/radiantdoc.h new file mode 100644 index 0000000..0aecee5 --- /dev/null +++ b/utils/Radiant/radiantdoc.h @@ -0,0 +1,57 @@ +// RadiantDoc.h : interface of the CRadiantDoc class +// +///////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_RADIANTDOC_H__330BBF0E_731C_11D1_B539_00AA00A410FC__INCLUDED_) +#define AFX_RADIANTDOC_H__330BBF0E_731C_11D1_B539_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + + +class CRadiantDoc : public CDocument +{ +protected: // create from serialization only + CRadiantDoc(); + DECLARE_DYNCREATE(CRadiantDoc) + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CRadiantDoc) + public: + virtual BOOL OnNewDocument(); + virtual void Serialize(CArchive& ar); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CRadiantDoc(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +protected: + +// Generated message map functions +protected: + //{{AFX_MSG(CRadiantDoc) + // NOTE - the ClassWizard will add and remove member functions here. + // DO NOT EDIT what you see in these blocks of generated code ! + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_RADIANTDOC_H__330BBF0E_731C_11D1_B539_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/radiantview.h b/utils/Radiant/radiantview.h new file mode 100644 index 0000000..42f54a8 --- /dev/null +++ b/utils/Radiant/radiantview.h @@ -0,0 +1,66 @@ +// RadiantView.h : interface of the CRadiantView class +// +///////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_RADIANTVIEW_H__330BBF10_731C_11D1_B539_00AA00A410FC__INCLUDED_) +#define AFX_RADIANTVIEW_H__330BBF10_731C_11D1_B539_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +class CRadiantView : public CView +{ +protected: // create from serialization only + CRadiantView(); + DECLARE_DYNCREATE(CRadiantView) + +// Attributes +public: + CRadiantDoc* GetDocument(); + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CRadiantView) + public: + virtual void OnDraw(CDC* pDC); // overridden to draw this view + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + protected: + virtual BOOL OnPreparePrinting(CPrintInfo* pInfo); + virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo); + virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CRadiantView(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +protected: + +// Generated message map functions +protected: + //{{AFX_MSG(CRadiantView) + // NOTE - the ClassWizard will add and remove member functions here. + // DO NOT EDIT what you see in these blocks of generated code ! + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +#ifndef _DEBUG // debug version in RadiantView.cpp +inline CRadiantDoc* CRadiantView::GetDocument() + { return (CRadiantDoc*)m_pDocument; } +#endif + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_RADIANTVIEW_H__330BBF10_731C_11D1_B539_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/res/radiant.ico b/utils/Radiant/res/radiant.ico new file mode 100644 index 0000000..a988838 Binary files /dev/null and b/utils/Radiant/res/radiant.ico differ diff --git a/utils/Radiant/res/radiantdoc.ico b/utils/Radiant/res/radiantdoc.ico new file mode 100644 index 0000000..2a1f1ae Binary files /dev/null and b/utils/Radiant/res/radiantdoc.ico differ diff --git a/utils/Radiant/resource.h b/utils/Radiant/resource.h new file mode 100644 index 0000000..7699afe --- /dev/null +++ b/utils/Radiant/resource.h @@ -0,0 +1,765 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by Radiant.rc +// +#define IDSELECT 2 +#define ID_APPLY 3 +#define IDREPLACE 3 +#define IDD_ABOUTBOX 100 +#define IDR_ACCELERATOR1 104 +#define IDD_CREATE_ENTITY 107 +#define IDD_EDIT_ENTITY 108 +#define IDR_TOOLBAR1 109 +#define IDD_FINDTEXTURE 111 +#define IDD_ENTITY 115 +#define IDR_E_MENU 116 +#define IDD_EDITPROP 117 +#define IDD_GAMMA 118 +#define IDD_FINDBRUSH 119 +#define IDI_ICON1 120 +#define IDD_ROTATE 121 +#define IDD_SIDES 122 +#define IDD_ABOUT 123 +#define IDB_BITMAP1 127 +#define IDD_SURFACE 129 +#define IDR_MAINFRAME 130 +#define IDR_RADIANTYPE 131 +#define IDAPPLY 132 +#define ID_BTN_APPLY 133 +#define IDC_CHECK_LIVE 134 +#define IDR_POPUP_TEXTURE 135 +#define IDC_CHECK_SELECTONLY 135 +#define IDR_POPUP_SELECTION 136 +#define IDR_POPUP_VIEW 137 +#define IDD_DLG_PREFS 139 +#define IDD_DLG_MAPINFO 141 +#define IDD_DLG_ENTITYLIST 142 +#define IDD_DLG_SCRIPTS 143 +#define IDD_DLG_NEWPROJECT 144 +#define IDD_DLG_COMMANDLIST 145 +#define IDR_MENU_DROP 146 +#define IDB_VIEWDEFAULT 147 +#define IDR_MENU_DAIKATANA 147 +#define IDB_VIEWQE4 148 +#define IDB_VIEWDEFAULT2 148 +#define IDR_MENU11 148 +#define IDR_MENU_QUAKE3 148 +#define IDB_VIEW4WAY 149 +#define IDB_VIEWDEFAULT3 149 +#define IDB_VIEWDEFAULT_Z 150 +#define IDB_3DFX 154 +#define IDR_ACCEL_SURFACE 155 +#define IDD_DIALOG_SCALE 156 +#define IDR_MINIACCEL 156 +#define IDD_DIALOG_FINDREPLACE 159 +#define IDD_DIALOG_STAIRS 160 +#define IDD_DIALOG_INPUT 161 +#define IDD_DLG_INFORMATION 162 +#define IDR_TOOLBAR_SCALELOCK 163 +#define IDR_TOOLBAR_ADVANCED 164 +#define IDD_PROJECT 165 +#define IDD_TEXTUREBAR 166 +#define IDD_ADDCMD 167 +#define IDD_DIALOG_TEXTURELIST 168 +#define IDD_DIALOG_NEWPATCH 169 +#define IDR_MENU1 170 +#define IDD_DIALOG_TEXTURELAYOUT 171 +#define IDR_POPUP_ENTITY 171 +#define IDD_DIALOG_CAP 173 +#define IDB_IENDCAP 175 +#define IDB_ENDCAP 176 +#define IDD_DIALOG_THICKEN 176 +#define IDB_BEVEL 177 +#define IDB_IBEVEL 178 +#define IDD_SOURCESAFE 180 +#define IDD_ENTFINDREPLACE 181 +#define IDD_DIALOG_GETSTRING 182 +#define IDC_BTN_HIDE 1000 +#define IDC_SPIN1 1001 +#define IDC_SPIN2 1002 +#define IDC_RADIO_MOUSE 1002 +#define IDC_SPIN3 1003 +#define IDC_RADIO2 1003 +#define IDC_RADIO_VIEWTYPE 1004 +#define IDC_RADIO_VIEWTYPE2 1005 +#define IDC_BTN_BROWSE 1006 +#define IDC_EDIT_QUAKE2 1007 +#define IDC_RADIO_VIEWTYPE3 1008 +#define IDC_LIST_ENTITIES 1009 +#define IDC_RADIO_VIEWTYPE4 1009 +#define IDC_EDIT_TOTALBRUSHES 1010 +#define IDC_EDIT_PAKFILE 1010 +#define IDC_RADIO_RUNQUAKE 1011 +#define IDC_BTN_BROWSEPAK 1011 +#define IDC_RADIO_RUNQUAKE2 1012 +#define IDC_EDIT_PREFABPATH 1012 +#define IDC_RADIO_RUNQUAKE3 1013 +#define IDC_BTN_BROWSEPREFAB 1013 +#define IDC_CHECK_LOADLAST 1014 +#define IDC_TREE_ENTITY 1015 +#define IDC_CHECK_AUTOSAVE 1015 +#define IDC_CHECK_LOADLASTMAP 1016 +#define IDC_LIST_ENTITY 1017 +#define IDC_EDIT_USERPATH 1017 +#define IDC_ENTITY_COMMENT 1018 +#define IDC_LIST_SCRIPTS 1018 +#define IDC_BTN_BROWSEUSERINI 1018 +#define ID_NEW 1019 +#define IDC_CHECK_USESHADERS 1019 +#define ID_EDIT 1020 +#define IDC_EDIT_NAME 1020 +#define IDC_VALUE 1021 +#define ID_RUN 1021 +#define IDC_LIST_COMMANDS 1021 +#define IDC_KEY 1022 +#define IDC_EDIT_COLOR 1022 +#define IDC_ENTITY_LIST 1023 +#define IDC_CHECK_FACE 1023 +#define IDC_PAIRS 1024 +#define IDC_CHECK_RIGHTCLICK 1024 +#define IDC_CHECK_VERTEX 1025 +#define IDC_CHECK1 1026 +#define IDC_CHECK_TEXTUREWINDOW 1026 +#define IDC_CHECK2 1027 +#define IDC_CHECK_TEXTURETOOLBAR 1027 +#define IDC_CHECK3 1028 +#define IDC_CHECK_LIGHTDRAW 1028 +#define IDC_CHECK4 1029 +#define IDC_CHECK_SNAPT 1029 +#define IDC_CHECK5 1030 +#define IDC_CHECK_3DFX 1030 +#define IDC_CHECK_TEXTURESCROLLBAR 1030 +#define IDC_CHECK6 1031 +#define IDC_CHECK_INTERNALBSP 1031 +#define IDC_CHECK7 1032 +#define IDC_CHECK_RUNQUAKE 1032 +#define IDC_CHECK8 1033 +#define IDC_CHECK_SETGAME 1033 +#define IDC_CHECK9 1034 +#define IDC_CHECK_3DFX2 1034 +#define IDC_CHECK_DISPLAYLISTS 1034 +#define IDC_CHECK10 1035 +#define IDC_EDIT_AUTOSAVE 1035 +#define IDC_CHECK11 1036 +#define IDC_CHECK_PAK 1036 +#define IDC_CHECK12 1037 +#define IDC_BUTTON1 1037 +#define IDC_BTN_APPLYTEXTURESTUFF 1037 +#define IDC_CHECK_QE4PAINTING 1037 +#define IDC_BUTTON_DEFAULTALL 1037 +#define IDC_KEYCOPY 1037 +#define IDC_BUTTON_BATCHFINDREPLACE 1037 +#define IDC_ANGLE90 1038 +#define IDC_CHECK13 1038 +#define IDC_SPIN_AUTOSAVE 1038 +#define IDC_BUTTON_DEFAULTALL2 1038 +#define IDC_ANGLE45 1039 +#define IDC_CHECK14 1039 +#define IDC_BUTTON_CREDITS 1039 +#define IDC_CHECK_SIZEPAINT 1039 +#define IDC_ANGLE135 1040 +#define IDC_CHECK15 1040 +#define IDC_EDIT_X 1040 +#define IDC_EDIT_NUMSNAPSHOTS 1040 +#define IDC_ANGLE225 1041 +#define IDC_CHECK16 1041 +#define IDC_CHECK_SNAPSHOTS 1041 +#define IDC_ANGLEUP 1042 +#define IDC_CHECK17 1042 +#define IDC_EDIT_FIND 1042 +#define IDC_SPIN_AUTOSAVE2 1042 +#define IDC_ANGLE180 1043 +#define IDC_CHECK18 1043 +#define IDC_EDIT_REPLACE 1043 +#define IDC_ANGLE315 1044 +#define IDC_CHECK19 1044 +#define IDC_CHECK_SELECTED 1044 +#define IDC_ANGLE0 1045 +#define IDC_CHECK20 1045 +#define IDC_CHECK_FORCE 1045 +#define IDC_ANGLE270 1046 +#define IDC_CHECK21 1046 +#define IDC_ANGLEDOWN 1047 +#define IDC_CHECK22 1047 +#define IDC_DELETEKEY 1048 +#define IDC_ADDPAIR 1048 +#define IDC_CHECK23 1048 +#define IDC_DELETEPAIR 1049 +#define IDC_CHECK24 1049 +#define IDC_CHECK25 1050 +#define IDC_SPAWN1 1051 +#define IDC_CHECK26 1051 +#define IDC_SPAWN2 1052 +#define IDC_CHECK27 1052 +#define IDC_SPAWN3 1053 +#define IDC_CHECK28 1053 +#define IDC_SPAWN4 1054 +#define IDC_CHECK29 1054 +#define IDC_SPAWN5 1055 +#define IDC_CHECK30 1055 +#define IDC_SPAWN6 1056 +#define IDC_CHECK31 1056 +#define IDC_SPAWN7 1057 +#define IDC_CHECK32 1057 +#define IDC_SPAWN8 1058 +#define IDC_CHECK33 1058 +#define IDC_EDIT1 1059 +#define IDC_CHECK34 1059 +#define IDC_ROTATE_BOX 1060 +#define IDC_ROTZ 1060 +#define IDC_CHECK35 1060 +#define IDC_EDIT_TOTALENTITIES 1060 +#define IDC_EDIT_Y 1060 +#define IDC_SCALE_BOX 1061 +#define IDC_ROTY 1061 +#define IDC_CHECK36 1061 +#define IDC_EDIT_NET 1061 +#define IDC_EDIT_Z 1061 +#define IDC_E_VALUE_FIELD 1062 +#define IDC_CHECK37 1062 +#define IDC_CHECK38 1063 +#define IDC_E_LIST 1064 +#define IDC_CHECK39 1064 +#define IDC_E_COMMENT 1065 +#define IDC_CHECK40 1065 +#define IDC_CHECK41 1066 +#define IDC_E_PROPS 1067 +#define IDC_CHECK42 1067 +#define IDC_E_135 1068 +#define IDC_CHECK43 1068 +#define IDC_E_180 1069 +#define IDC_CHECK44 1069 +#define IDC_E_225 1070 +#define IDC_CHECK45 1070 +#define IDC_E_270 1071 +#define IDC_CHECK46 1071 +#define IDC_E_90 1072 +#define IDC_CHECK47 1072 +#define IDC_E_45 1073 +#define IDC_CHECK48 1073 +#define IDC_E_0 1074 +#define IDC_CHECK49 1074 +#define IDC_E_315 1075 +#define IDC_CHECK50 1075 +#define IDC_E_UP 1076 +#define IDC_CHECK51 1076 +#define IDC_E_DOWN 1077 +#define IDC_CHECK52 1077 +#define IDC_CHECK53 1078 +#define IDC_CHECK54 1079 +#define IDC_E_ADDPROP 1080 +#define IDC_CHECK55 1080 +#define IDC_E_DELPROP 1081 +#define IDC_CHECK56 1081 +#define IDC_E_CREATE 1082 +#define IDC_CHECK57 1082 +#define IDC_E_STATUS 1083 +#define IDC_CHECK58 1083 +#define IDC_CHECK59 1084 +#define IDC_CHECK60 1085 +#define IDC_CHECK61 1086 +#define IDC_CHECK62 1087 +#define IDC_CHECK63 1088 +#define IDC_CHECK64 1089 +#define IDC_SHIFT_BOX 1090 +#define IDC_HSHIFT 1090 +#define IDC_VSHIFT 1091 +#define IDC_ROTATEV 1092 +#define ID_BTN_CANCEL 1092 +#define IDC_HSTRETCH 1092 +#define IDC_SCALEV 1093 +#define IDC_EDIT_STATUSPOINTSIZE 1093 +#define IDC_VSTRETCH 1093 +#define IDC_SCALEH 1094 +#define IDC_EDIT_ROTATION 1094 +#define IDC_SHIFTV 1095 +#define IDC_HSHIFTA 1095 +#define IDC_STATIC_FIELD1 1095 +#define IDC_SHIFTH 1096 +#define IDC_VSHIFTA 1097 +#define IDC_HSCALE 1098 +#define IDC_HSCALEA 1099 +#define IDC_VSCALE 1100 +#define IDC_EDIT_FIELD1 1100 +#define IDC_VSCALEA 1101 +#define IDC_STATIC_FIELD2 1101 +#define IDC_ROTATE 1102 +#define IDC_EDIT_FIELD2 1102 +#define IDC_SLIDER_CAMSPEED 1102 +#define IDC_G_EDIT 1103 +#define IDC_ROTATEA 1103 +#define IDC_STATIC_FIELD3 1103 +#define IDC_CHECK_CAMXYUPDATE 1103 +#define IDC_STATIC_KEY 1104 +#define IDC_FIND_BRUSH 1104 +#define IDC_EDIT_FIELD3 1104 +#define IDC_STATIC_VALUE 1105 +#define IDC_STATIC_FIELD4 1105 +#define IDC_E_KEY_FIELD 1106 +#define IDC_EDIT_FIELD4 1106 +#define IDC_FIND_ENTITY 1107 +#define IDC_STATIC_FIELD5 1107 +#define IDC_SIDES 1108 +#define IDC_EDIT_FIELD5 1108 +#define IDC_COMBO_WHATGAME 1108 +#define IDC_ROTX 1109 +#define IDC_BTN_COLOR 1109 +#define IDC_STATIC_COLOR 1110 +#define IDC_E_COLOR 1111 +#define IDC_ABOUT_GLVENDOR 1112 +#define IDC_ABOUT_GLVERSION 1113 +#define IDC_BTN_THECOLOR 1113 +#define IDC_ABOUT_GLRENDERER 1114 +#define IDC_STATIC_STUFF 1115 +#define IDC_TEXTURE 1116 +#define IDC_CHECK_ALTDRAG 1116 +#define IDC_PRJRSHCMD 1117 +#define IDC_SPIN_HSHIFT 1117 +#define IDC_CHECK_MOUSECHASE 1117 +#define IDC_PRJREMOTEBASE 1118 +#define IDC_SPIN_VSHIFT 1118 +#define IDC_CHECK_FACECOLOR 1118 +#define IDC_PRJENTITYPATH 1119 +#define IDC_SPIN_VSCALE 1119 +#define IDC_CHECK_NOCLAMP 1119 +#define IDC_PRJTEXPATH 1120 +#define IDC_SPIN_HSCALE 1120 +#define IDC_PRJAUTOSAVE 1121 +#define IDC_SPIN_ROTATE 1121 +#define IDC_ADDCMD 1122 +#define IDC_EDIT_ROTATEAMT 1122 +#define IDC_CMD_LIST 1123 +#define IDC_CHECK_DLLENTITIES 1123 +#define IDC_PRJBASEPATH 1124 +#define IDC_CHECK_WIDETOOLBAR 1124 +#define IDC_CMDCOMMAND 1125 +#define IDC_CHECK_BUGGYICD 1125 +#define IDC_CMDMENUTEXT 1126 +#define IDC_CHECK_SGIOPENGL 1126 +#define IDC_REMCMD 1127 +#define IDC_PRJINTERVAL 1128 +#define IDC_CHECK_HICOLOR 1128 +#define IDC_PRJMAPSPATH 1129 +#define IDC_CHECK_SHADERTEST 1129 +#define IDC_EDITCMD 1130 +#define IDC_ABOUT_GLEXTENSIONS 1131 +#define IDC_LIST_TEXTURES 1132 +#define IDC_COMBO_WIDTH 1136 +#define IDC_COMBO_HEIGHT 1137 +#define IDC_BTN_PATCHDETAILS 1137 +#define IDC_BTN_PATCHRESET 1138 +#define IDC_BTN_PATCHNATURAL 1139 +#define IDC_RADIO_CAP 1139 +#define IDC_BTN_PATCHFIT 1140 +#define IDC_RADIO_CAP2 1140 +#define IDC_CHECK_SEAMS 1140 +#define IDC_RADIO_CAP3 1141 +#define IDC_EDIT_AMOUNT 1141 +#define IDC_BTN_AXIAL 1141 +#define IDC_RADIO_CAP4 1142 +#define IDC_BTN_ASSIGNSOUND 1142 +#define IDC_BTN_FACEFIT 1142 +#define IDC_BTN_ASSIGNMODEL 1143 +#define IDC_BTN_FACERESET 1143 +#define IDC_SLIDER_TEXTUREQUALITY 1144 +#define IDC_TEXTURE_MATERIAL 1144 +#define IDC_LIGHT_RED 1146 +#define IDC_LIGHT_GREEN 1147 +#define IDC_LIGHT_BLUE 1148 +#define IDC_LIGHT_ALPHA 1149 +#define IDC_STATIC_LIGHTING 1152 +#define IDC_STATIC_MATERIAL 1153 +#define IDC_STATIC_PATCHTEXTURING 1154 +#define IDC_TEXTURE_LIGHTMIP 1155 +#define IDC_STATIC_LIGHTMIP 1156 +#define IDC_EDIT_WIDTH 1157 +#define IDC_EDIT_HEIGHT 1158 +#define IDC_SPIN_HEIGHT 1160 +#define IDC_SPIN_WIDTH 1161 +#define IDC_STATIC_PATCH 1162 +#define IDC_EDIT_SS_INIPATH 1163 +#define IDC_EDIT_SS_PROJECTPATH 1164 +#define IDC_EDIT_FIND_KEY 1164 +#define IDC_EDIT_SS_BEHAVEDPATH 1165 +#define IDC_EDIT_FIND_VALUE 1165 +#define IDC_EDIT_REPLACE_KEY 1166 +#define IDC_EDIT_REPLACE_VALUE 1167 +#define IDFIND 1168 +#define IDC_VALUECOPY 1169 +#define IDC_CHECK_FIND_WHOLESTRINGMATCHONLY 1170 +#define IDC_CHECK_SELECTALLMATCHING 1171 +#define IDC_CHECK_RESCALE 1172 +#define IDC_PROMPT 1173 +#define ID_ENTITY_START 22800 +#define ID_FILE_IMPORT 32771 +#define ID_VIEW_XY 32772 +#define ID_VIEW_SIDE 32773 +#define ID_VIEW_FRONT 32774 +#define ID_CAMERATOGGLE 32775 +#define ID_VIEW_CAMERATOGGLE 32776 +#define ID_BUTTON32777 32777 +#define ID_BUTTON32778 32778 +#define ID_TEXTURES_POPUP 32780 +#define ID_POPUP_SELECTION 32782 +#define ID_VIEW_CHANGE 32783 +#define ID_VIEW_CAMERAUPDATE 32784 +#define ID_VIEW_CLIPPER 32785 +#define ID_PREFS 32786 +#define ID_TOGGLE_LOCK 32787 +#define ID_EDIT_MAPINFO 32788 +#define ID_EDIT_ENTITYINFO 32789 +#define ID_BRUSH_SCRIPTS 32790 +#define ID_VIEW_NEXTVIEW 32791 +#define ID_HELP_COMMANDLIST 32792 +#define ID_FILE_NEWPROJECT 32793 +#define ID_MENUITEM32795 32795 +#define ID_SNAPTOGRID 32795 +#define ID_SPLIT_SELECTED 32823 +#define ID_CLIP_SELECTED 32824 +#define ID_FLIP_CLIP 32825 +#define ID_TOGGLEVIEW_YZ 32831 +#define ID_TOGGLEVIEW_XZ 32832 +#define ID_COLORS_GRIDTEXT 32833 +#define ID_COLORS_BRUSH 32834 +#define ID_COLORS_SELECTEDBRUSH 32835 +#define ID_COLORS_CLIPPER 32836 +#define ID_COLORS_GRIDBLOCK 32837 +#define ID_COLORS_VIEWNAME 32838 +#define ID_COLOR_SETORIGINAL 32839 +#define ID_COLOR_SETQER 32840 +#define ID_COLOR_SETBLACK 32841 +#define ID_BYEBYE 32842 +#define ID_SELECT_SCALE 32843 +#define ID_SELECT_MOUSEROTATE 32844 +#define ID_TEXTURE_REPLACESELECTED 32859 +#define ID_TEXTURE_REPLACEALL 32860 +#define ID_SELECT_MOUSESCALE 32866 +#define ID_SCALELOCKX 32867 +#define ID_SCALELOCKY 32868 +#define ID_SCALELOCKZ 32869 +#define ID_VIEW_CUBICCLIPPING 32870 +#define ID_FILE_PROJECTSETTINGS 32875 +#define ID_VIEW_CUBEOUT 32876 +#define ID_VIEW_CUBEIN 32877 +#define ID_NODES_LOADNODES 32878 +#define ID_NODES_SHOWNODES 32879 +#define ID_NODES_SHOWLINKS 32880 +#define ID_NODES_REMOVEALLNODES 32881 +#define ID_NODES_COUNTNODES 32882 +#define ID_NODES_GIVEMONEYTONELNO 32883 +#define ID_FILE_SAVEREGION 32887 +#define ID_FILE_LOADREGION 32888 +#define ID_SELECTION_MOVEDOWN 32890 +#define ID_TOOLBAR_MAIN 32891 +#define ID_SELECTION_MOVEUP 32892 +#define ID_TOOLBAR_TEXTURE 32892 +#define ID_BRUSH_MAKECONE 32896 +#define ID_TEXTURES_LOAD 32897 +#define ID_TOGGLE_ROTATELOCK 32898 +#define ID_CURVE_CYLINDER 32903 +#define ID_CURVE_ENDCAP 32904 +#define ID_CURVE_BEVEL 32905 +#define ID_CURVE_SPHERE 32906 +#define ID_CURVE_HEMISPHERE 32907 +#define ID_CURVE_QUARTER 32908 +#define ID_CURVE_EIGHTHSPHERE 32909 +#define ID_CURVE_INVERTCURVE 32910 +#define ID_FILE_IMPORTMAP 32911 +#define ID_FILE_EXPORTMAP 32912 +#define ID_EDIT_LOADPREFAB 32913 +#define ID_VIEW_SHOWCURVES 32914 +#define ID_SELECTION_SELECT_NUDGELEFT 32916 +#define ID_SELECTION_SELECT_NUDGERIGHT 32917 +#define ID_SELECTION_SELECT_NUDGEUP 32918 +#define ID_SELECTION_SELECT_NUDGEDOWN 32919 +#define ID_TEXTURES_LOADLIST 32920 +#define ID_DONTSELECTCURVE 32923 +#define ID_CONVERTCURVES 32924 +#define ID_DYNAMIC_LIGHTING 32925 +#define ID_PATCH_SHOWBOUNDINGBOX 32926 +#define ID_CURVE_SIMPLEPATCHMESH 32927 +#define ID_PATCH_WIREFRAME 32928 +#define ID_PATCH_WELD 32929 +#define ID_CURVE_PATCHTUBE 32930 +#define ID_CURVE_PATCHCONE 32931 +#define ID_CURVE_PATCHENDCAP 32932 +#define ID_CURVE_PATCHBEVEL 32933 +#define ID_CURVE_PATCHINVERTEDENDCAP 32934 +#define ID_CURVE_PATCHINVERTEDBEVEL 32935 +#define ID_PATCH_DRILLDOWN 32936 +#define ID_CURVE_LOADPATCHFILE 32937 +#define ID_CURVE_INSERTROW 32938 +#define ID_CURVE_INSERTCOLUMN 32939 +#define ID_CURVE_DELETEROW 32940 +#define ID_CURVE_DELETECOLUMN 32941 +#define ID_BUTTON32942 32942 +#define ID_PATCH_INSDEL 32942 +#define ID_CURVE_INSERT_ADDCOLUMN 32943 +#define ID_CURVE_INSERT_INSERTCOLUMN 32944 +#define ID_CURVE_INSERT_ADDROW 32945 +#define ID_CURVE_INSERT_INSERTROW 32946 +#define ID_CURVE_DELETE_FIRSTCOLUMN 32947 +#define ID_CURVE_DELETE_LASTCOLUMN 32948 +#define ID_CURVE_DELETE_FIRSTROW 32949 +#define ID_CURVE_DELETE_LASTROW 32950 +#define ID_CURVE_NEGATIVE 32951 +#define ID_PATCH_BEND 32952 +#define ID_CURVE_PATCHDENSETUBE 32955 +#define ID_CURVE_PATCHVERYDENSETUBE 32956 +#define ID_CURVE_CAP 32957 +#define ID_CURVE_CAP_INVERTEDBEVEL 32959 +#define ID_CURVE_CAP_INVERTEDENDCAP 32960 +#define ID_CURVE_REDISPERSE_ROWS 32961 +#define ID_CURVE_REDISPERSE_COLS 32962 +#define ID_PATCH_NATURALIZE 32963 +#define ID_CURVE_PATCHSQUARE 32964 +#define ID_BRUSH_PRIMITIVES_SPHERE 32965 +#define ID_BRUSH_PRIMITIVES_TORUS 32966 +#define ID_TEXTURES_TEXTUREWINDOWSCALE_200 32967 +#define ID_TEXTURES_TEXTUREWINDOWSCALE_100 32968 +#define ID_TEXTURES_TEXTUREWINDOWSCALE_50 32969 +#define ID_TEXTURES_TEXTUREWINDOWSCALE_25 32970 +#define ID_TEXTURES_TEXTUREWINDOWSCALE_10 32971 +#define ID_CURVE_NEGATIVETEXTUREX 32972 +#define ID_TEXTURES_FLUSH 32973 +#define ID_CURVE_OVERLAY_SET 32974 +#define ID_CURVE_OVERLAY_CLEAR 32975 +#define ID_CURVE_NEGATIVETEXTUREY 32976 +#define ID_CURVE_THICKEN 32977 +#define ID_CURVE_CYCLECAP 32978 +#define ID_CURVE_MATRIX_TRANSPOSE 32981 +#define ID_PLUGINS_REFRESH 32982 +#define ID_TEXTURES_RELOADSHADERS 32983 +#define ID_VIEW_ENTITIESAS_BOUNDINGBOX 32984 +#define ID_VIEW_ENTITIESAS_WRITEFRAME 32985 +#define ID_VIEW_ENTITIESAS_SELECTEDWIREFRAME 32986 +#define ID_VIEW_ENTITIESAS_SELECTEDSKINNED 32987 +#define ID_VIEW_ENTITIESAS_SKINNED 32988 +#define ID_VIEW_ENTITIESAS_SKINNEDANDBOXED 32989 +#define ID_SHOW_ENTITIES 32990 +#define ID_VIEW_ENTITIESAS_WIREFRAME 32991 +#define ID_VIEW_SHOWHINT 32993 +#define ID_VIEW_SHOWFACES 32994 +#define ID_VIEW_SHOWWAYPOINTS 32995 +#define ID_VIEW_SHOWMISC_MODEL 32996 +#define ID_VIEW_SHOWMISC_MODEL_BREAKABLE 32997 +#define ID_VIEW_SHOWTARGET_SPEAKER 32998 +#define ID_VIEW_SHOWREF_TAGS 32999 +#define ID_SS_CHECKIN 33000 +#define ID_SS_CHECKOUT 33001 +#define ID_SS_UNDOCHECKOUT 33002 +#define ID_SS_HISTORY 33003 +#define ID_SS_CONFIGURE 33004 +#define ID_SS_STATUS 33005 +#define ID_VIEW_SHOWMISC_MODEL_XXXX 33006 +#define ID_VIEW_SHOWWAYPOINTS_ONLY 33008 +#define ID_VIEW_SHOWTRIGGER_XXXX 33009 +#define ID_MISC_FINDENT 33010 +#define ID_MISC_FINDNEXTENT 33011 +#define ID_SHOW_EASY 33012 +#define ID_SHOW_MEDIUM 33013 +#define ID_SHOW_HARD 33014 +#define ID_SS_ADD 33015 +#define ID_AUTOCAULK 33016 +#define ID_VIEW_SHOW_DETAIL_ONLY 33017 +#define ID_VIEW_SHOW_CAULK 33018 +#define ID_VIEW_SHOWFUNCGROUPS 33019 +#define ID_VIEW_SHOWLIGHTRADII 33020 +#define ID_VIEW_SHOWLIGHTRADII_KB 33021 +#define ID_VIEW_SHOWCURVESONLY 33022 +#define ID_ENTITY_END 33500 +#define ID_PLUGIN_START 33800 +#define ID_PLUGIN_END 33999 +#define ID_FILE_EXIT 40002 +#define ID_FILE_SAVEAS 40004 +#define ID_VIEW_CENTER 40005 +#define ID_VIEW_UPFLOOR 40006 +#define ID_VIEW_DOWNFLOOR 40007 +#define ID_BRUSH_FLIPX 40008 +#define ID_BRUSH_FLIPY 40009 +#define ID_BRUSH_FLIPZ 40010 +#define ID_BRUSH_ROTATEX 40011 +#define ID_BRUSH_ROTATEY 40012 +#define ID_BRUSH_ROTATEZ 40013 +#define ID_BSP_FULLVIS 40016 +#define ID_BSP_FASTVIS 40017 +#define ID_BSP_NOVIS 40018 +#define ID_BSP_RELIGHT 40019 +#define ID_BSP_ENTITIES 40020 +#define ID_FILE_POINTFILE 40021 +#define ID_VIEW_100 40022 +#define ID_VIEW_75 40023 +#define ID_VIEW_50 40024 +#define ID_VIEW_25 40025 +#define ID_VIEW_12 40026 +#define ID_TEXTURES_SHOWALL 40033 +#define ID_TEXTURES_SHOWINUSE 40034 +#define ID_TEXTURES_TOGGLEVIEW 40037 +#define ID_SELECTION_CREATEENTITY 40039 +#define ID_SELECTION_EDITENTITY 40040 +#define ID_MISC_BENCHMARK 40041 +#define ID_REGION_OFF 40043 +#define ID_REGION_SETXY 40044 +#define ID_REGION_SETBRUSH 40045 +#define ID_SELECTION_MAKEHOLLOW 40046 +#define ID_SELECTION_SELECTPARTIALTALL 40047 +#define ID_SELECTION_SELECTCOMPLETETALL 40048 +#define ID_SELECTION_CSGSUBTRACT 40049 +#define ID_SELECTION_SELECTTOUCHING 40050 +#define ID_VIEW_NEAREST 40052 +#define ID_VIEW_NEARESTMIPMAP 40053 +#define ID_VIEW_LINEAR 40054 +#define ID_VIEW_BILINEAR 40055 +#define ID_VIEW_BILINEARMIPMAP 40056 +#define ID_VIEW_TRILINEAR 40057 +#define ID_TEXTURES_WIREFRAME 40058 +#define ID_TEXTURES_FLATSHADE 40059 +#define ID_VIEW_SHOWNAMES 40060 +#define ID_VIEW_ZOOMIN 40061 +#define ID_VIEW_ZOOMOUT 40062 +#define ID_VIEW_SHOWCOORDINATES 40063 +#define ID_VIEW_Z100 40064 +#define ID_VIEW_ZZOOMIN 40065 +#define ID_VIEW_ZZOOMOUT 40066 +#define ID_SELECTION_CLONE 40067 +#define ID_SELECTION_DESELECT 40068 +#define ID_SELECTION_DELETE 40069 +#define ID_BUTTON40068 40070 +#define ID_SELECTION_DRAGVERTECIES 40074 +#define ID_SELECTION_DRAGEDGES 40075 +#define ID_REGION_SETTALLBRUSH 40076 +#define ID_SELECTION_SELECTINSIDE 40092 +#define ID_PROJECT_RELEAD 40094 +#define ID_PROJECT_CHANGE 40095 +#define ID_MISC_GAMMA 40097 +#define ID_VIEW_SHOWENT 40098 +#define ID_VIEW_SHOWPATH 40099 +#define ID_VIEW_SHOWLIGHTS 40100 +#define ID_VIEW_SHOWCLIP 40101 +#define ID_VIEW_SHOWWATER 40102 +#define ID_VIEW_SHOWWORLD 40103 +#define ID_MISC_TEXTUREBACKGROUN 40104 +#define ID_TEXTUREBK 40105 +#define ID_COLORS_XYBK 40106 +#define ID_FILE_ABOUT 40107 +#define ID_VIEW_CONSOLE 40108 +#define ID_VIEW_ENTITY 40109 +#define ID_VIEW_TEXTURE 40110 +#define ID_COLORS_MAJOR 40111 +#define ID_COLORS_MINOR 40113 +#define ID_SELECTION_CONNECT 40114 +#define ID_FILE_LOADPROJECT 40115 +#define ID_MISC_FINDBRUSH 40116 +#define ID_MISC_NEXTLEAKSPOT 40117 +#define ID_MISC_PREVIOUSLEAKSPOT 40118 +#define ID_BRUSH_3SIDED 40119 +#define ID_BRUSH_4SIDED 40120 +#define ID_BRUSH_5SIDED 40121 +#define ID_BRUSH_6SIDED 40122 +#define ID_BRUSH_7SIDED 40123 +#define ID_BRUSH_8SIDED 40124 +#define ID_BRUSH_9SIDED 40125 +#define ID_SELECTION_ARBITRARYROTATION 40126 +#define ID_BRUSH_ARBITRARYSIDED 40127 +#define ID_SELECTION_UNGROUPENTITY 40130 +#define ID_MISC_SELECTENTITYCOLOR 40131 +#define ID_MISC_PRINTXY 40132 +#define ID_HELP_ABOUT 40134 +#define ID_EDIT_COPYBRUSH 40135 +#define ID_EDIT_PASTEBRUSH 40136 +#define ID_TEXTURES_INSPECTOR 40137 +#define ID_VIEW_SHOWDETAIL 40138 +#define ID_SELECTION_MAKE_DETAIL 40139 +#define ID_SELECTION_MAKE_STRUCTURAL 40140 +#define ID_REGION_SETSELECTION 40142 +#define ID_VIEW_SHOWBLOCKS 40143 +#define ID_GRID_1 40144 +#define ID_GRID_2 40145 +#define ID_GRID_4 40146 +#define ID_GRID_8 40147 +#define ID_GRID_16 40148 +#define ID_GRID_32 40149 +#define ID_GRID_64 40150 +#define ID_CAMERA_UP 40151 +#define ID_CAMERA_DOWN 40152 +#define ID_CAMERA_LEFT 40153 +#define ID_CAMERA_RIGHT 40155 +#define ID_CAMERA_FORWARD 40156 +#define ID_CAMERA_BACK 40157 +#define ID_CAMERA_ANGLEUP 40158 +#define ID_CAMERA_ANGLEDOWN 40159 +#define ID_CAMERA_STRAFELEFT 40160 +#define ID_CAMERA_STRAFERIGHT 40161 +#define ID_GRID_TOGGLE 40162 +#define ID_ENTITYLIST 40163 +#define ID_MAPINFO 40164 +#define ID_TOGGLECONSOLE 40165 +#define ID_TOGGLECAMERA 40166 +#define ID_TOGGLEZ 40167 +#define ID_TOGGLEVIEW 40168 +#define ID_SELECTION_TEXTURE_DEC 40169 +#define ID_SELECTION_TEXTURE_INC 40170 +#define ID_SELECTION_TEXTURE_FIT 40171 +#define ID_SELECTION_TEXTURE_ROTATECLOCK 40172 +#define ID_SELECTION_TEXTURE_ROTATECOUNTER 40173 +#define ID_SELECTION_TEXTURE_SCALEUP 40174 +#define ID_SELECTION_TEXTURE_SCALEDOWN 40175 +#define ID_SELECTION_TEXTURE_SHIFTLEFT 40176 +#define ID_SELECTION_TEXTURE_SHIFTRIGHT 40177 +#define ID_SELECTION_TEXTURE_SHIFTUP 40178 +#define ID_SELECTION_TEXTURE_SHIFTDOWN 40179 +#define ID_GRID_NEXT 40180 +#define ID_GRID_PREV 40181 +#define ID_SELECTION_TEXTURE_SCALELEFT 40182 +#define ID_SELECTION_TEXTURE_SCALERIGHT 40183 +#define ID_SELECTION_PRINT 40184 +#define ID_SELECTION_TOGGLESIZEPAINT 40185 +#define ID_PATCH_TAB 40186 +#define ID_PATCH_ENTER 40187 +#define ID_SELECT_SNAPTOGRID 40188 +#define ID_SELECTION_HIDEWAYPOINTCHILDREN 40189 +#define ID_SELECTION_UNHIDEWAYPOINTCHILDREN 40190 +#define ID_SELECTION_UNHIDEALLWAYPOINTS 40191 +#define ID_SCRIPTPOPUP_NOTEPAD_ID_START 40192 +#define ID_SCRIPTPOPUP_NOTEPAD_ID_END 40291 +#define ID_SCRIPTPOPUP_MSDEV_ID_START 40292 +#define ID_SCRIPTPOPUP_MSDEV_ID_END 40391 +#define ID_SCRIPTPOPUP_BEHAVED_ID_START 40392 +#define ID_SCRIPTPOPUP_BEHAVED_ID_END 40491 +#define ID_SCRIPTPOPOP_GROUPNAMES_ID_START 40492 +#define ID_SCRIPTPOPOP_GROUPNAMES_ID_END 40591 +#define ID_SELECTION_GROUPNAMES_DISPLAY 40592 +#define ID_SELECTION_GROUPNAMES_SELECT_ID_START 40593 +#define ID_SELECTION_GROUPNAMES_SELECT_ID_END 40692 +#define ID_SELECTION_CLONE_NOTARGETNAMECHANGE 40693 +#define ID_CLEAR_NONSOLID 40694 +#define ID_MAKE_NONSOLID 40695 +#define ID_VIEW_SHOW_NONSOLID 40696 +#define ID_EDIT_PASTEBRUSHNOTARGETNAMECHANGE 40697 +#define ID_SELECTION_GROUPNAMES_ADDSELECTEDTO_ID_START 40698 +#define ID_SELECTION_GROUPNAMES_ADDSELECTEDTO_ID_END 40798 +#define ID_SELECTION_GROUPNAMES_ADDSELECTEDECLASSTO_ID_START 40799 +#define ID_SELECTION_GROUPNAMES_ADDSELECTEDECLASSTO_ID_END 40899 +#define ID_SELECTION_GROUPNAMES_ADDSELECTEDMODELSTO_ID_START 40900 +#define ID_SELECTION_GROUPNAMES_ADDSELECTEDMODELSTO_ID_END 41000 +#define ID_SELECTION_GROUPNAMES_ACTIVE 41001 +#define ID_SELECTION_GROUPNAMES_GHOSTED 41002 +#define ID_FACEFIT 41003 +#define ID_MAPHOME 41004 +#define ID_VIEW_HIDESHOW_HIDESELECTED 41005 +#define ID_VIEW_HIDESHOW_SHOWHIDDEN 41006 +#define ID_VIEW_CUBEOUT10 41007 +#define ID_VIEW_CUBEIN10 41008 +#define ID_SELECTION_CONNECTSMART 41009 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_3D_CONTROLS 1 +#define _APS_NEXT_RESOURCE_VALUE 183 +#define _APS_NEXT_COMMAND_VALUE 33023 +#define _APS_NEXT_CONTROL_VALUE 1174 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/utils/Radiant/rotatedlg.cpp b/utils/Radiant/rotatedlg.cpp new file mode 100644 index 0000000..72ac64e --- /dev/null +++ b/utils/Radiant/rotatedlg.cpp @@ -0,0 +1,109 @@ +// RotateDlg.cpp : implementation file +// + +#include "stdafx.h" +#include "Radiant.h" +#include "RotateDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CRotateDlg dialog + + +CRotateDlg::CRotateDlg(CWnd* pParent /*=NULL*/) + : CDialog(CRotateDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CRotateDlg) + m_strX = _T(""); + m_strY = _T(""); + m_strZ = _T(""); + //}}AFX_DATA_INIT +} + + +void CRotateDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CRotateDlg) + DDX_Control(pDX, IDC_SPIN3, m_wndSpin3); + DDX_Control(pDX, IDC_SPIN2, m_wndSpin2); + DDX_Control(pDX, IDC_SPIN1, m_wndSpin1); + DDX_Text(pDX, IDC_ROTX, m_strX); + DDX_Text(pDX, IDC_ROTY, m_strY); + DDX_Text(pDX, IDC_ROTZ, m_strZ); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CRotateDlg, CDialog) + //{{AFX_MSG_MAP(CRotateDlg) + ON_BN_CLICKED(ID_APPLY, OnApply) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN1, OnDeltaposSpin1) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN2, OnDeltaposSpin2) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN3, OnDeltaposSpin3) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CRotateDlg message handlers + +void CRotateDlg::OnOK() +{ + OnApply(); + CDialog::OnOK(); +} + +void CRotateDlg::OnApply() +{ + UpdateData(TRUE); + float f = atof(m_strX); + if (f != 0.0) + Select_RotateAxis(0,f); + f = atof(m_strY); + if (f != 0.0) + Select_RotateAxis(1,f); + f = atof(m_strZ); + if (f != 0.0) + Select_RotateAxis(2,f); +} + +BOOL CRotateDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + m_wndSpin1.SetRange(0, 359); + m_wndSpin2.SetRange(0, 359); + m_wndSpin3.SetRange(0, 359); + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void CRotateDlg::OnDeltaposSpin1(NMHDR* pNMHDR, LRESULT* pResult) +{ + NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR; + Select_RotateAxis(0, pNMUpDown->iDelta); + *pResult = 0; +} + +void CRotateDlg::OnDeltaposSpin2(NMHDR* pNMHDR, LRESULT* pResult) +{ + NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR; + Select_RotateAxis(1, pNMUpDown->iDelta); + *pResult = 0; +} + +void CRotateDlg::OnDeltaposSpin3(NMHDR* pNMHDR, LRESULT* pResult) +{ + NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR; + Select_RotateAxis(2, pNMUpDown->iDelta); + *pResult = 0; +} + +void CRotateDlg::ApplyNoPaint() +{ + +} diff --git a/utils/Radiant/rotatedlg.h b/utils/Radiant/rotatedlg.h new file mode 100644 index 0000000..fdb5ade --- /dev/null +++ b/utils/Radiant/rotatedlg.h @@ -0,0 +1,57 @@ +#if !defined(AFX_ROTATEDLG_H__D4B79152_7A7E_11D1_B541_00AA00A410FC__INCLUDED_) +#define AFX_ROTATEDLG_H__D4B79152_7A7E_11D1_B541_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// RotateDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CRotateDlg dialog + +class CRotateDlg : public CDialog +{ +// Construction +public: + CRotateDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CRotateDlg) + enum { IDD = IDD_ROTATE }; + CSpinButtonCtrl m_wndSpin3; + CSpinButtonCtrl m_wndSpin2; + CSpinButtonCtrl m_wndSpin1; + CString m_strX; + CString m_strY; + CString m_strZ; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CRotateDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + void ApplyNoPaint(); + + // Generated message map functions + //{{AFX_MSG(CRotateDlg) + virtual void OnOK(); + afx_msg void OnApply(); + virtual BOOL OnInitDialog(); + afx_msg void OnDeltaposSpin1(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnDeltaposSpin2(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnDeltaposSpin3(NMHDR* pNMHDR, LRESULT* pResult); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_ROTATEDLG_H__D4B79152_7A7E_11D1_B541_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/scaledialog.cpp b/utils/Radiant/scaledialog.cpp new file mode 100644 index 0000000..ca58d2b --- /dev/null +++ b/utils/Radiant/scaledialog.cpp @@ -0,0 +1,47 @@ +// ScaleDialog.cpp : implementation file +// + +#include "stdafx.h" +#include "Radiant.h" +#include "ScaleDialog.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CScaleDialog dialog + + +CScaleDialog::CScaleDialog(CWnd* pParent /*=NULL*/) + : CDialog(CScaleDialog::IDD, pParent) +{ + //{{AFX_DATA_INIT(CScaleDialog) + m_fZ = 1.0f; + m_fX = 1.0f; + m_fY = 1.0f; + //}}AFX_DATA_INIT +} + + +void CScaleDialog::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CScaleDialog) + DDX_Text(pDX, IDC_EDIT_Z, m_fZ); + DDX_Text(pDX, IDC_EDIT_X, m_fX); + DDX_Text(pDX, IDC_EDIT_Y, m_fY); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CScaleDialog, CDialog) + //{{AFX_MSG_MAP(CScaleDialog) + // NOTE: the ClassWizard will add message map macros here + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CScaleDialog message handlers diff --git a/utils/Radiant/scaledialog.h b/utils/Radiant/scaledialog.h new file mode 100644 index 0000000..76b02c2 --- /dev/null +++ b/utils/Radiant/scaledialog.h @@ -0,0 +1,48 @@ +#if !defined(AFX_SCALEDIALOG_H__8A9B33B2_9922_11D1_B568_00AA00A410FC__INCLUDED_) +#define AFX_SCALEDIALOG_H__8A9B33B2_9922_11D1_B568_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// ScaleDialog.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CScaleDialog dialog + +class CScaleDialog : public CDialog +{ +// Construction +public: + CScaleDialog(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CScaleDialog) + enum { IDD = IDD_DIALOG_SCALE }; + float m_fZ; + float m_fX; + float m_fY; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CScaleDialog) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CScaleDialog) + // NOTE: the ClassWizard will add member functions here + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_SCALEDIALOG_H__8A9B33B2_9922_11D1_B568_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/scriptdlg.cpp b/utils/Radiant/scriptdlg.cpp new file mode 100644 index 0000000..b4f7e87 --- /dev/null +++ b/utils/Radiant/scriptdlg.cpp @@ -0,0 +1,82 @@ +// ScriptDlg.cpp : implementation file +// + +#include "stdafx.h" +#include "Radiant.h" +#include "ScriptDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CScriptDlg dialog + + +CScriptDlg::CScriptDlg(CWnd* pParent /*=NULL*/) + : CDialog(CScriptDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CScriptDlg) + m_strScript = _T(""); + //}}AFX_DATA_INIT +} + + +void CScriptDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CScriptDlg) + DDX_Control(pDX, IDC_LIST_SCRIPTS, m_lstScripts); + DDX_LBString(pDX, IDC_LIST_SCRIPTS, m_strScript); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CScriptDlg, CDialog) + //{{AFX_MSG_MAP(CScriptDlg) + ON_BN_CLICKED(ID_RUN, OnRun) + ON_LBN_DBLCLK(IDC_LIST_SCRIPTS, OnDblclkListScripts) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CScriptDlg message handlers + +void CScriptDlg::OnRun() +{ + UpdateData(TRUE); + EndDialog(IDOK); + RunScriptByName(m_strScript.GetBuffer(0), true); +} + +BOOL CScriptDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + char* pBuff = new char[16384]; + CString strINI = g_strAppPath; + strINI += "\\scripts.ini"; + int n = GetPrivateProfileSectionNames(pBuff, 16384, strINI); + + // CStringList list; + m_lstScripts.ResetContent(); + char* pWorkBuff = pBuff; + while (*pWorkBuff != NULL) + { + m_lstScripts.AddString(pWorkBuff); + pWorkBuff += strlen(pWorkBuff) + 1; + } + delete []pBuff; + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + + +void CScriptDlg::OnDblclkListScripts() +{ + UpdateData(TRUE); + EndDialog(IDOK); + RunScriptByName(m_strScript.GetBuffer(0), true); +} diff --git a/utils/Radiant/scriptdlg.h b/utils/Radiant/scriptdlg.h new file mode 100644 index 0000000..58c33b3 --- /dev/null +++ b/utils/Radiant/scriptdlg.h @@ -0,0 +1,49 @@ +#if !defined(AFX_SCRIPTDLG_H__C241B9A4_819F_11D1_B548_00AA00A410FC__INCLUDED_) +#define AFX_SCRIPTDLG_H__C241B9A4_819F_11D1_B548_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// ScriptDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CScriptDlg dialog + +class CScriptDlg : public CDialog +{ +// Construction +public: + CScriptDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CScriptDlg) + enum { IDD = IDD_DLG_SCRIPTS }; + CListBox m_lstScripts; + CString m_strScript; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CScriptDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CScriptDlg) + afx_msg void OnRun(); + virtual BOOL OnInitDialog(); + afx_msg void OnDblclkListScripts(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_SCRIPTDLG_H__C241B9A4_819F_11D1_B548_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/select.cpp b/utils/Radiant/select.cpp new file mode 100644 index 0000000..9c00e51 --- /dev/null +++ b/utils/Radiant/select.cpp @@ -0,0 +1,1696 @@ +// select.c +#include "stdafx.h" +#include "qe3.h" +#include "oddbits.h" + +// externs + +/* +=========== +Test_Ray +=========== +*/ +#define DIST_START MAX_WORLD_COORD +trace_t Test_Ray (vec3_t origin, vec3_t dir, int flags) +{ + brush_t *brush; + face_t *face; + float dist; + trace_t t; + + memset (&t, 0, sizeof(t)); + t.dist = DIST_START * sqrt(3) * 2; + + if (flags & SF_CYCLE) + { + CPtrArray array; + brush_t *pToSelect = (selected_brushes.next != &selected_brushes) ? selected_brushes.next : NULL; + Select_Deselect(); + + // go through active brushes and accumulate all "hit" brushes + for (brush = active_brushes.next ; brush != &active_brushes ; brush=brush->next) + { + //if ( (flags & SF_ENTITIES_FIRST) && brush->owner == world_entity) + // continue; + + if (FilterBrush (brush)) + continue; + + if (!g_PrefsDlg.m_bSelectCurves && (brush->curveBrush || brush->patchBrush)) + continue; + + //if (!g_bShowPatchBounds && brush->patchBrush) + // continue; + + face = Brush_Ray (origin, dir, brush, &dist); + if (face) + { + array.Add(brush); + } + } + + int nSize = array.GetSize(); + if (nSize > 0) + { + bool bFound = false; + for (int i = 0; i < nSize; i++) + { + brush_t *b = reinterpret_cast(array.GetAt(i)); + // did we hit the last one selected yet ? + if (b == pToSelect) + { + // yes we want to select the next one in the list + int n = (i > 0) ? i-1 : nSize-1; + pToSelect = reinterpret_cast(array.GetAt(n)); + bFound = true; + break; + } + } + if (!bFound) + pToSelect = reinterpret_cast(array.GetAt(0)); + } + if (pToSelect) + { + face = Brush_Ray (origin, dir, pToSelect, &dist); + t.dist = dist; + t.brush = pToSelect; + t.face = face; + t.selected = false; + return t; + } + } + + if (! (flags & SF_SELECTED_ONLY) ) + { + for (brush = active_brushes.next ; brush != &active_brushes ; brush=brush->next) + { + if ( (flags & SF_ENTITIES_FIRST) && brush->owner == world_entity) + continue; + + if (FilterBrush (brush)) + continue; + + if (!g_PrefsDlg.m_bSelectCurves && (brush->curveBrush || brush->patchBrush)) + continue; + + //if (!g_bShowPatchBounds && brush->patchBrush) + // continue; + + face = Brush_Ray (origin, dir, brush, &dist); + if (dist > 0 && dist < t.dist) + { + t.dist = dist; + t.brush = brush; + t.face = face; + t.selected = false; + } + } + } + + + for (brush = selected_brushes.next ; brush != &selected_brushes ; brush=brush->next) + { + if ( (flags & SF_ENTITIES_FIRST) && brush->owner == world_entity) + continue; + + if (FilterBrush (brush)) + continue; + + if (!g_PrefsDlg.m_bSelectCurves && brush->curveBrush) + continue; + + face = Brush_Ray (origin, dir, brush, &dist); + if (dist > 0 && dist < t.dist) + { + t.dist = dist; + t.brush = brush; + t.face = face; + t.selected = true; + } + } + + // if entites first, but didn't find any, check regular + + if ( (flags & SF_ENTITIES_FIRST) && t.brush == NULL) + return Test_Ray (origin, dir, flags - SF_ENTITIES_FIRST); + + return t; + +} + + +/* +============ +Select_Brush + +============ +*/ +void Select_Brush (brush_t *brush, bool bComplete /* = true */) +{ + brush_t *b; + entity_t *e; + + selected_face = NULL; + if (g_qeglobals.d_select_count < 2) + g_qeglobals.d_select_order[g_qeglobals.d_select_count] = brush; + g_qeglobals.d_select_count++; + + //if (brush->patchBrush) + // Patch_Select(brush->nPatchID); + + e = brush->owner; + if (e) + { + // select complete entity on first click + if (e != world_entity && bComplete == true) + { + for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + { + if (b->owner == e) + goto singleselect; + } + + for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext) + { + Brush_RemoveFromList (b); + Brush_AddToList (b, &selected_brushes); + } + } + else + { +singleselect: + Brush_RemoveFromList (brush); + Brush_AddToList (brush, &selected_brushes); + UpdateSurfaceDialog(); + UpdatePatchInspector(); + } + + if (e->eclass) + { + UpdateEntitySel(brush->owner->eclass); + } + } + + vec3_t vMin, vMax, vSize; + Select_GetBounds (vMin, vMax); + VectorSubtract(vMax, vMin, vSize); + CString strStatus; + strStatus.Format("Selection X:: %.1f Y:: %.1f Z:: %.1f", vSize[0], vSize[1], vSize[2]); + g_pParentWnd->SetStatusText(2, strStatus); +} + +/* +============ +Select_Ray + +If the origin is inside a brush, that brush will be ignored. +============ +*/ +void Select_Ray (vec3_t origin, vec3_t dir, int flags) +{ + trace_t t; + + t = Test_Ray (origin, dir, flags); + if (!t.brush) + return; + + if (flags == SF_SINGLEFACE) + { + selected_face = t.face; + selected_face_brush = t.brush; + Sys_UpdateWindows (W_ALL); + g_qeglobals.d_select_mode = sel_brush; + Texture_SetTexture (&t.face->texdef); + UpdateSurfaceDialog(); + return; + } + + // move the brush to the other list + + g_qeglobals.d_select_mode = sel_brush; + + if (t.selected) + { + Brush_RemoveFromList (t.brush); + Brush_AddToList (t.brush, &active_brushes); + } else + { + Select_Brush (t.brush, !(GetKeyState(VK_MENU) & 0x8000)); + } + + Sys_UpdateWindows (W_ALL); +} + + +void Select_Delete (void) +{ + brush_t *brush; + + selected_face = NULL; + g_qeglobals.d_select_mode = sel_brush; + + g_qeglobals.d_select_count = 0; + g_qeglobals.d_num_move_points = 0; + while (selected_brushes.next != &selected_brushes) + { + brush = selected_brushes.next; + if (brush->patchBrush) + Patch_Delete(brush->nPatchID); + Brush_Free (brush); + } + + // FIXME: remove any entities with no brushes + + Sys_MarkMapModified(); + Sys_UpdateWindows (W_ALL); +} + +void Select_Deselect (bool bDeSelectToListBack /*= false*/) // to avoid continuously re-finding same selected ents +{ + brush_t *b; + + Patch_Deselect(); + + g_qeglobals.d_workcount++; + g_qeglobals.d_select_count = 0; + g_qeglobals.d_num_move_points = 0; + b = selected_brushes.next; + + if (b == &selected_brushes) + { + if (selected_face) + { + selected_face = NULL; + } + Sys_UpdateWindows (W_ALL); + return; + } + + selected_face = NULL; + g_qeglobals.d_select_mode = sel_brush; + + // grab top / bottom height for new brushes + if (b->mins[2] < b->maxs[2]) + { + g_qeglobals.d_new_brush_bottom_z = b->mins[2]; + g_qeglobals.d_new_brush_top_z = b->maxs[2]; + } + + if (bDeSelectToListBack) + { + selected_brushes.next->prev = active_brushes.prev; + selected_brushes.prev->next = &active_brushes; + active_brushes.prev->next = selected_brushes.next; + active_brushes.prev = selected_brushes.prev; + } + else + { + selected_brushes.next->prev = &active_brushes; + selected_brushes.prev->next = active_brushes.next; + active_brushes.next->prev = selected_brushes.prev; + active_brushes.next = selected_brushes.next; + } + + selected_brushes.prev = selected_brushes.next = &selected_brushes; + + Sys_UpdateWindows (W_ALL); +} + + +/* +============ +Select_Move +============ +*/ +void Select_Move (vec3_t delta, bool bSnap) +{ + brush_t *b; + + +// actually move the selected brushes + for (b = selected_brushes.next ; b != &selected_brushes ; b=b->next) + Brush_Move (b, delta, bSnap); + + vec3_t vMin, vMax; + Select_GetBounds (vMin, vMax); + CString strStatus; + strStatus.Format("Origin X:: %.1f Y:: %.1f Z:: %.1f", vMin[0], vMax[1], vMax[2]); + g_pParentWnd->SetStatusText(2, strStatus); + +// Sys_UpdateWindows (W_ALL); +} + +/* +============ +Select_Clone + +Creates an exact duplicate of the selection in place, then moves +the selected brushes off of their old positions +============ +*/ +void Select_Clone (void) +{ +#if 1 + ASSERT(g_pParentWnd->ActiveXY()); + g_bScreenUpdates = false; + g_pParentWnd->ActiveXY()->Copy(); + g_pParentWnd->ActiveXY()->Paste(); + g_pParentWnd->NudgeSelection(2, g_qeglobals.d_gridsize); + g_pParentWnd->NudgeSelection(3, g_qeglobals.d_gridsize); + g_bScreenUpdates = true; + Sys_UpdateWindows(W_ALL); +#else +/* // commenting-out helps stop me wasting time updating code that's #if'd out (as has happened)... + + brush_t *b, *b2, *n, *next, *next2; + vec3_t delta; + entity_t *e; + + g_qeglobals.d_workcount++; + g_qeglobals.d_select_mode = sel_brush; + + delta[0] = g_qeglobals.d_gridsize; + delta[1] = g_qeglobals.d_gridsize; + delta[2] = 0; + + for (b=selected_brushes.next ; b != &selected_brushes ; b=next) + { + next = b->next; + // if the brush is a world brush, handle simply + if (b->owner == world_entity) + { + n = Brush_Clone (b); + Brush_AddToList (n, &active_brushes); + Entity_LinkBrush (world_entity, n); + Brush_Build( n ); + Brush_Move (b, delta); + continue; + } + + e = Entity_Clone (b->owner); + // clear the target / targetname + + DeleteKey (e, "target"); + DeleteKey (e, "targetname"); + + #ifdef QUAKE3 + DeleteKey (e, "target2"); + DeleteKey (e, "target3"); + DeleteKey (e, "target4"); + #endif + + // if the brush is a fixed size entity, create a new entity + if (b->owner->eclass->fixedsize) + { + n = Brush_Clone (b); + Brush_AddToList (n, &active_brushes); + Entity_LinkBrush (e, n); + Brush_Build( n ); + Brush_Move (b, delta); + continue; + } + + // brush is a complex entity, grab all the other ones now + + next = &selected_brushes; + + for ( b2 = b ; b2 != &selected_brushes ; b2=next2) + { + next2 = b2->next; + if (b2->owner != b->owner) + { + if (next == &selected_brushes) + next = b2; + continue; + } + + // move b2 to the start of selected_brushes, + // so it won't be hit again + Brush_RemoveFromList (b2); + Brush_AddToList (b2, &selected_brushes); + + n = Brush_Clone (b2); + Brush_AddToList (n, &active_brushes); + Entity_LinkBrush (e, n); + Brush_Build( n ); + Brush_Move (b2, delta, true); + } + + } +*/ +#endif + Sys_UpdateWindows (W_ALL); +} + + + +/* +============ +Select_SetTexture +bool bFitScale = compute scale on the plane and counteract plane->axial snapping +============ +*/ +void Select_SetTexture (texdef_t *texdef, bool bFitScale/*=false*/, bool bNoSystemTextureOverwrite/*=false*/) +{ + brush_t *b; + + if (selected_face) + { + SetFaceTexdef (selected_face_brush, selected_face, texdef, bFitScale, bNoSystemTextureOverwrite); + Brush_Build(selected_face_brush, bFitScale); + } + else + { + for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + if (!b->owner->eclass->fixedsize) + Brush_SetTexture (b, texdef, bFitScale, bNoSystemTextureOverwrite); + } + Sys_UpdateWindows (W_ALL); +} + + +void Select_FitTexture(int nHeight, int nWidth) +{ + brush_t *b; + + if(selected_brushes.next == &selected_brushes && selected_face == NULL) + return; + + for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + { + Brush_FitTexture(b, nHeight, nWidth); + Brush_Build(b); + } + + if (selected_face) + { + Face_FitTexture(selected_face, nHeight, nWidth); + Brush_Build(selected_face_brush); + } + + Sys_UpdateWindows (W_CAMERA); +} + + +void Select_Hide() +{ + for (brush_t* b=selected_brushes.next ; b && b != &selected_brushes ; b=b->next) + { + b->hiddenBrush = true; + } + Sys_UpdateWindows (W_ALL); +} + +void Select_ShowAllHidden() +{ + brush_t* b; + for (b=selected_brushes.next ; b && b != &selected_brushes ; b=b->next) + { + b->hiddenBrush = false; + } + for (b=active_brushes.next ; b && b != &active_brushes ; b=b->next) + { + b->hiddenBrush = false; + } + Sys_UpdateWindows (W_ALL); +} + + + +/* +================================================================ + + TRANSFORMATIONS + +================================================================ +*/ + +void Select_GetBounds (vec3_t mins, vec3_t maxs) +{ + brush_t *b; + int i; + + for (i=0 ; i<3 ; i++) + { + mins[i] = MAX_WORLD_COORD; + maxs[i] = MIN_WORLD_COORD; + } + + for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + for (i=0 ; i<3 ; i++) + { + if (b->mins[i] < mins[i]) + mins[i] = b->mins[i]; + if (b->maxs[i] > maxs[i]) + maxs[i] = b->maxs[i]; + } +} + + +void Select_GetTrueMid (vec3_t mid) +{ + vec3_t mins, maxs; + Select_GetBounds (mins, maxs); + + for (int i=0 ; i<3 ; i++) + mid[i] = (mins[i] + ((maxs[i] - mins[i]) / 2)); +} + + +void Select_GetMid (vec3_t mid) +{ + vec3_t mins, maxs; + int i; + + if (g_PrefsDlg.m_bNoClamp) + { + Select_GetTrueMid(mid); + return; + } + + Select_GetBounds (mins, maxs); + + for (i=0 ; i<3 ; i++) + mid[i] = g_qeglobals.d_gridsize*floor ( ( (mins[i] + maxs[i])*0.5 )/g_qeglobals.d_gridsize ); + +} + +vec3_t select_origin; +vec3_t select_matrix[3]; +qboolean select_fliporder; + +void Select_AplyMatrix (bool bSnap) +{ + brush_t *b; + face_t *f; + int i, j; + vec3_t temp; + + for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + { + for (f=b->brush_faces ; f ; f=f->next) + { + for (i=0 ; i<3 ; i++) + { + VectorSubtract (f->planepts[i], select_origin, temp); + for (j=0 ; j<3 ; j++) + f->planepts[i][j] = DotProduct(temp, select_matrix[j]) + select_origin[j]; + } + if (select_fliporder) + { + VectorCopy (f->planepts[0], temp); + VectorCopy (f->planepts[2], f->planepts[0]); + VectorCopy (temp, f->planepts[2]); + } + } + Brush_Build(b, bSnap); + if (b->patchBrush) + { + Patch_ApplyMatrix(b->nPatchID, select_origin, select_matrix); + } + } +} + +void ProjectOnPlane(vec3_t& normal,float dist,vec3_t& ez, vec3_t& p) +{ + if (fabs(ez[0]) == 1) + p[0] = (dist - normal[1] * p[1] - normal[2] * p[2]) / normal[0]; + else if (fabs(ez[1]) == 1) + p[1] = (dist - normal[0] * p[0] - normal[2] * p[2]) / normal[1]; + else + p[2] = (dist - normal[0] * p[0] - normal[1] * p[1]) / normal[2]; +} + +void Back(vec3_t& dir, vec3_t& p) +{ + if (fabs(dir[0]) == 1) + p[0] = 0; + else if (fabs(dir[1]) == 1) + p[1] = 0; + else p[2] = 0; +} + + + +// using scale[0] and scale[1] +void ComputeScale(vec3_t& rex, vec3_t& rey, vec3_t& p, face_t* f) +{ + float px = DotProduct(rex, p); + float py = DotProduct(rey, p); + px *= f->texdef.scale[0]; + py *= f->texdef.scale[1]; + vec3_t aux; + VectorCopy(rex, aux); + VectorScale(aux, px, aux); + VectorCopy(aux, p); + VectorCopy(rey, aux); + VectorScale(aux, py, aux); + VectorAdd(p, aux, p); +} + + + +void AbsoluteToLocal(plane_t normal2, face_t* f, vec3_t& p1, vec3_t& p2, vec3_t& p3) +{ + vec3_t ex,ey,ez; + // computing new local axis base + TextureAxisFromPlane(&normal2, ex, ey); + CrossProduct(ex, ey, ez); + + // projecting back on (ex,ey) + Back(ez,p1); + Back(ez,p2); + Back(ez,p3); + + vec3_t aux; + // rotation + VectorCopy(p2, aux); + VectorSubtract(aux, p1,aux); + + float x = DotProduct(aux,ex); + float y = DotProduct(aux,ey); + f->texdef.rotate = 180 * atan2(y,x) / Q_PI; + + vec3_t rex,rey; + // computing rotated local axis base + VectorCopy(ez, aux); + VectorScale(aux, f->texdef.rotate, aux); + VectorCopy(ex, rex); + VectorRotate(rex, aux, rex); + VectorCopy(ey, rey); + VectorRotate(rey, aux, rey); + + // scale + VectorCopy(p2, aux); + VectorSubtract(aux, p1, aux); + f->texdef.scale[0] = DotProduct(aux, rex); + VectorCopy(p3, aux); + VectorSubtract(aux, p1, aux); + f->texdef.scale[1] = DotProduct(aux, rey); + + // shift + // only using p1 + x = DotProduct(rex,p1); + y = DotProduct(rey,p1); + x /= f->texdef.scale[0]; + y /= f->texdef.scale[1]; + + VectorCopy(rex, p1); + VectorScale(p1, x, p1); + VectorCopy(rey, aux); + VectorScale(aux, y, aux); + VectorAdd(p1, aux, p1); + VectorCopy(ez, aux); + VectorScale(aux, -f->texdef.rotate, aux); + VectorRotate(p1, aux, p1); + f->texdef.shift[0] = -DotProduct(p1, ex); + f->texdef.shift[1] = -DotProduct(p1, ey); + + // stored rot is good considering local axis base + // change it if necessary + f->texdef.rotate = -f->texdef.rotate; + + Clamp(f->texdef.shift[0], f->d_texture->width); + Clamp(f->texdef.shift[1], f->d_texture->height); + Clamp(f->texdef.rotate, 360); + +} + + +void RotateFaceTexture(face_t* f, int nAxis, float fDeg) +{ + vec3_t p1,p2,p3, rota; + p1[0] = p1[1] = p1[2] = 0; + VectorCopy(p1, p2); + VectorCopy(p1, p3); + VectorCopy(p1, rota); + ComputeAbsolute(f, p1, p2, p3); + + + rota[nAxis] = fDeg; + VectorRotate(p1, rota, select_origin, p1); + VectorRotate(p2, rota, select_origin, p2); + VectorRotate(p3, rota, select_origin, p3); + + plane_t normal2; + vec3_t vNormal; + vNormal[0] = f->plane.normal[0]; + vNormal[1] = f->plane.normal[1]; + vNormal[2] = f->plane.normal[2]; + VectorRotate(vNormal, rota, vNormal); + normal2.normal[0] = vNormal[0]; + normal2.normal[1] = vNormal[1]; + normal2.normal[2] = vNormal[2]; + AbsoluteToLocal(normal2, f, p1, p2 ,p3); + +} + +void RotateTextures(int nAxis, float fDeg, vec3_t vOrigin) +{ + for (brush_t* b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + { + for (face_t* f=b->brush_faces ; f ; f=f->next) + { + RotateFaceTexture(f, nAxis, fDeg); + Brush_Build(b, false); + } + Brush_Build(b, false); + } +} + + +void Select_FlipAxis (int axis) +{ + int i; + + Select_GetMid (select_origin); + for (i=0 ; i<3 ; i++) + { + VectorCopy (vec3_origin, select_matrix[i]); + select_matrix[i][i] = 1; + } + select_matrix[axis][axis] = -1; + + select_fliporder = true; + Select_AplyMatrix (true); + Sys_UpdateWindows (W_ALL); +} + + +void Select_Scale(float x, float y, float z) +{ + Select_GetMid (select_origin); + for (brush_t* b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + { + for (face_t* f=b->brush_faces ; f ; f=f->next) + { + for (int i=0 ; i<3 ; i++) + { + f->planepts[i][0] -= select_origin[0]; + f->planepts[i][1] -= select_origin[1]; + f->planepts[i][2] -= select_origin[2]; + f->planepts[i][0] *= x; + //f->planepts[i][0] = floor(f->planepts[i][0] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + + f->planepts[i][1] *= y; + //f->planepts[i][1] = floor(f->planepts[i][1] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + + f->planepts[i][2] *= z; + //f->planepts[i][2] = floor(f->planepts[i][2] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + + f->planepts[i][0] += select_origin[0]; + f->planepts[i][1] += select_origin[1]; + f->planepts[i][2] += select_origin[2]; + } + } + Brush_Build(b, false); + if (b->patchBrush) + { + vec3_t v; + v[0] = x; + v[1] = y; + v[2] = z; + Patch_Scale(b->nPatchID, select_origin, v); + } + } +} + +void Select_RotateAxis (int axis, float deg, bool bPaint, bool bMouse) +{ + vec3_t temp; + int i, j; + vec_t c, s; + + if (deg == 0) + { + //Sys_Printf("0 deg\n"); + return; + } + + if (bMouse) + { + VectorCopy(g_pParentWnd->ActiveXY()->RotateOrigin(), select_origin); + } + else + { + Select_GetMid (select_origin); + } + + select_fliporder = false; + + if (deg == 90) + { + for (i=0 ; i<3 ; i++) + { + VectorCopy (vec3_origin, select_matrix[i]); + select_matrix[i][i] = 1; + } + i = (axis+1)%3; + j = (axis+2)%3; + VectorCopy (select_matrix[i], temp); + VectorCopy (select_matrix[j], select_matrix[i]); + VectorSubtract (vec3_origin, temp, select_matrix[j]); + } + else + { + deg = -deg; + if (deg == -180.0) + { + c = -1; + s = 0; + } + else if (deg == -270.0) + { + c = 0; + s = -1; + } + else + { + c = cos(deg * Q_PI / 180.0); + s = sin(deg * Q_PI / 180.0); + } + + for (i=0 ; i<3 ; i++) + { + VectorCopy (vec3_origin, select_matrix[i]); + select_matrix[i][i] = 1; + } + + switch (axis) + { + case 0: + select_matrix[1][1] = c; + select_matrix[1][2] = -s; + select_matrix[2][1] = s; + select_matrix[2][2] = c; + break; + case 1: + select_matrix[0][0] = c; + select_matrix[0][2] = s; + select_matrix[2][0] = -s; + select_matrix[2][2] = c; + break; + case 2: + select_matrix[0][0] = c; + select_matrix[0][1] = -s; + select_matrix[1][0] = s; + select_matrix[1][1] = c; + break; + } + } + + + if (g_PrefsDlg.m_bRotateLock) + RotateTextures(axis, deg, select_origin); + + Select_AplyMatrix(!bMouse); + + if (bPaint) + Sys_UpdateWindows (W_ALL); +} + +/* +================================================================ + +GROUP SELECTIONS + +================================================================ +*/ + +void Select_CompleteTall (void) +{ + brush_t *b, *next; + //int i; + vec3_t mins, maxs; + + if (!QE_SingleBrush ()) + return; + + g_qeglobals.d_select_mode = sel_brush; + + VectorCopy (selected_brushes.next->mins, mins); + VectorCopy (selected_brushes.next->maxs, maxs); + Select_Delete (); + + int nDim1 = (g_pParentWnd->ActiveXY()->GetViewType() == YZ) ? 1 : 0; + int nDim2 = (g_pParentWnd->ActiveXY()->GetViewType() == XY) ? 1 : 2; + + for (b=active_brushes.next ; b != &active_brushes ; b=next) + { + next = b->next; + + if ( (b->maxs[nDim1] > maxs[nDim1] || b->mins[nDim1] < mins[nDim1]) + || (b->maxs[nDim2] > maxs[nDim2] || b->mins[nDim2] < mins[nDim2]) ) + continue; + + if (FilterBrush (b)) + continue; + + Brush_RemoveFromList (b); + Brush_AddToList (b, &selected_brushes); +#if 0 + // old stuff + for (i=0 ; i<2 ; i++) + if (b->maxs[i] > maxs[i] || b->mins[i] < mins[i]) + break; + if (i == 2) + { + Brush_RemoveFromList (b); + Brush_AddToList (b, &selected_brushes); + } +#endif + } + Sys_UpdateWindows (W_ALL); +} + +void Select_PartialTall (void) +{ + brush_t *b, *next; + //int i; + vec3_t mins, maxs; + + if (!QE_SingleBrush ()) + return; + + g_qeglobals.d_select_mode = sel_brush; + + VectorCopy (selected_brushes.next->mins, mins); + VectorCopy (selected_brushes.next->maxs, maxs); + Select_Delete (); + + int nDim1 = (g_pParentWnd->ActiveXY()->GetViewType() == YZ) ? 1 : 0; + int nDim2 = (g_pParentWnd->ActiveXY()->GetViewType() == XY) ? 1 : 2; + + for (b=active_brushes.next ; b != &active_brushes ; b=next) + { + next = b->next; + + if ( (b->mins[nDim1] > maxs[nDim1] || b->maxs[nDim1] < mins[nDim1]) + || (b->mins[nDim2] > maxs[nDim2] || b->maxs[nDim2] < mins[nDim2]) ) + continue; + + if (FilterBrush (b)) + continue; + + Brush_RemoveFromList (b); + Brush_AddToList (b, &selected_brushes); + + +#if 0 +// old stuff + for (i=0 ; i<2 ; i++) + if (b->mins[i] > maxs[i] || b->maxs[i] < mins[i]) + break; + if (i == 2) + { + Brush_RemoveFromList (b); + Brush_AddToList (b, &selected_brushes); + } +#endif + } + Sys_UpdateWindows (W_ALL); +} + +void Select_Touching (void) +{ + brush_t *b, *next; + int i; + vec3_t mins, maxs; + + if (!QE_SingleBrush ()) + return; + + g_qeglobals.d_select_mode = sel_brush; + + VectorCopy (selected_brushes.next->mins, mins); + VectorCopy (selected_brushes.next->maxs, maxs); + + for (b=active_brushes.next ; b != &active_brushes ; b=next) + { + next = b->next; + + if (FilterBrush (b)) + continue; + + for (i=0 ; i<3 ; i++) + if (b->mins[i] > maxs[i]+1 || b->maxs[i] < mins[i]-1) + break; + + if (i == 3) + { + Brush_RemoveFromList (b); + Brush_AddToList (b, &selected_brushes); + } + } + Sys_UpdateWindows (W_ALL); +} + +void Select_Inside (void) +{ + brush_t *b, *next; + int i; + vec3_t mins, maxs; + + if (!QE_SingleBrush ()) + return; + + g_qeglobals.d_select_mode = sel_brush; + + VectorCopy (selected_brushes.next->mins, mins); + VectorCopy (selected_brushes.next->maxs, maxs); + Select_Delete (); + + for (b=active_brushes.next ; b != &active_brushes ; b=next) + { + next = b->next; + + if (FilterBrush (b)) + continue; + + for (i=0 ; i<3 ; i++) + if (b->maxs[i] > maxs[i] || b->mins[i] < mins[i]) + break; + if (i == 3) + { + Brush_RemoveFromList (b); + Brush_AddToList (b, &selected_brushes); + } + } + Sys_UpdateWindows (W_ALL); +} + +/* +============= +Select_Ungroup + +Turn the currently selected entity back into normal brushes +============= +*/ +void Select_Ungroup (void) +{ + int numselectedgroups; + entity_t *e; + brush_t *b, *sb; + + numselectedgroups = 0; + for (sb = selected_brushes.next; sb != &selected_brushes; sb = sb->next) + { + e = sb->owner; + + if (!e || e == world_entity || e->eclass->fixedsize) + { + continue; + } + + for (b = e->brushes.onext; b != &e->brushes; b = e->brushes.onext) + { + //Brush_RemoveFromList (b); + //Brush_AddToList (b, &active_brushes); + Entity_UnlinkBrush (b); + Entity_LinkBrush (world_entity, b); + Brush_Build( b ); + b->owner = world_entity; + } + Entity_Free (e); + numselectedgroups++; + } + + if (numselectedgroups <= 0) + { + Sys_Printf("No grouped entities selected.\n"); + return; + } + Sys_Printf("Ungrouped %d entit%s.\n", numselectedgroups, (numselectedgroups == 1)?"y":"ies"); + Sys_UpdateWindows (W_ALL); +} + +/* +==================== +Select_MakeStructural +==================== +*/ +void Select_MakeStructural (void) +{ + brush_t *b; + face_t *f; + + for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + for (f=b->brush_faces ; f ; f=f->next) + f->texdef.contents &= ~CONTENTS_DETAIL; + Select_Deselect (); + Sys_UpdateWindows (W_ALL); +} + +void Select_MakeDetail (bool bDeselectAfterwards /* = true */) +{ + brush_t *b; + face_t *f; + + for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + for (f=b->brush_faces ; f ; f=f->next) + f->texdef.contents |= CONTENTS_DETAIL; + + if (bDeselectAfterwards) + { + Select_Deselect (); + } + Sys_UpdateWindows (W_ALL); +} + +void Select_MakeNonSolid(void) +{ + brush_t *b; + face_t *f; + + for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + { + for (f=b->brush_faces ; f ; f=f->next) + f->texdef.flags |= SURF_NONSOLID; + + if (b->patchBrush) + { + Patch_MakeNonSolid(b); + } + } + Select_Deselect (); + Sys_UpdateWindows (W_ALL); +} +void Select_ClearNonSolid(void) +{ + brush_t *b; + face_t *f; + + for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + { + for (f=b->brush_faces ; f ; f=f->next) + f->texdef.flags &= ~SURF_NONSOLID; + + if (b->patchBrush) + { + Patch_MakeSolid(b); + } + } + + Select_Deselect (); + Sys_UpdateWindows (W_ALL); +} + + +int GetSelectedBrushesCount(void) +{ + int iCount = 0; + + for (brush_t *pb=selected_brushes.next ; pb != &selected_brushes ; pb=pb->next) + { + iCount++; + } + + return iCount; +} + +// returns either single selected brush or NULL +// +brush_t *GetTheSelectedBrush(bool bQuietError) +{ + if (GetSelectedBrushesCount()==1) + { + return selected_brushes.next; + } + + if (!bQuietError) + { + ErrorBox("This only works with 1 brush selected"); + } + return NULL; +} + + +void R_Entity_UpdateAllTargetWaypoints(entity_t *pEnt, entity_t *pOriginalEnt, bool bHide) +{ + if (pEnt) + { + if (strlen(ValueForKey(pEnt, sKEY_WAYPOINTHIDE_RECURSION_PROTECT))==0) + { + SetKeyValue(pEnt, sKEY_WAYPOINTHIDE_RECURSION_PROTECT, "1"); // note: actual value irrelevant, just so long as strlen()!=0 + + CString str; + entity_t *pDestEnt; + + str = ValueForKey(pEnt, "target"); + if (str.GetLength() > 0) + { + pDestEnt = FindEntity("targetname", str.GetBuffer(0)); + + R_Entity_UpdateAllTargetWaypoints(pDestEnt, pOriginalEnt, bHide); + } + + str = ValueForKey(pEnt, "target2"); + if (str.GetLength() > 0) + { + pDestEnt = FindEntity("targetname", str.GetBuffer(0)); + + R_Entity_UpdateAllTargetWaypoints(pDestEnt, pOriginalEnt, bHide); + } + + str = ValueForKey(pEnt, "target3"); + if (str.GetLength() > 0) + { + pDestEnt = FindEntity("targetname", str.GetBuffer(0)); + + R_Entity_UpdateAllTargetWaypoints(pDestEnt, pOriginalEnt, bHide); + } + + str = ValueForKey(pEnt, "target4"); + if (str.GetLength() > 0) + { + pDestEnt = FindEntity("targetname", str.GetBuffer(0)); + + R_Entity_UpdateAllTargetWaypoints(pDestEnt, pOriginalEnt, bHide); + } + + if (pEnt != pOriginalEnt) // because we don't want to hide/unhide ourselves, just the children + { + if (bHide) + { + SetKeyValue(pEnt, sKEY_HIDDENWAYPOINT, "1"); // note: actual value irrelevant, just so long as strlen()!=0 + } + else + { + DeleteKey(pEnt, sKEY_HIDDENWAYPOINT); + } + } + } + } +} + + +void Select_ClearAllRecursionProtection(void) +{ + for (entity_t* pEnt=entities.next ; pEnt != &entities ; pEnt=pEnt->next) + { + DeleteKey(pEnt, sKEY_WAYPOINTHIDE_RECURSION_PROTECT); + } +} + +void Select_HideWaypointChildren(void) +{ + brush_t *pb = GetTheSelectedBrush(); + + if (pb) + { + if (!strncmp(pb->owner->eclass->name, "waypoint", 8)) + { + Select_ClearAllRecursionProtection(); // probably not needed beforehand, but wtf? + R_Entity_UpdateAllTargetWaypoints(pb->owner, pb->owner, true); + Select_ClearAllRecursionProtection(); + Sys_UpdateWindows (W_XY|W_CAMERA); + } + else + { + ErrorBox("This only works with waypoint brushes"); + } + } +} + +void Select_UnHideWaypointChildren(void) +{ + brush_t *pb = GetTheSelectedBrush(); + + if (pb) + { + if (!strncmp(pb->owner->eclass->name, "waypoint", 8)) + { + Select_ClearAllRecursionProtection(); // probably not needed beforehand, but wtf? + R_Entity_UpdateAllTargetWaypoints(pb->owner, pb->owner, false); + Select_ClearAllRecursionProtection(); + Sys_UpdateWindows (W_XY|W_CAMERA); + } + else + { + ErrorBox("This only works with waypoint brushes"); + } + } +} + +void Select_UnHideAllWaypoints(void) +{ + for (entity_t* pEnt=entities.next ; pEnt != &entities ; pEnt=pEnt->next) + { + DeleteKey(pEnt, sKEY_HIDDENWAYPOINT); + } + + Sys_UpdateWindows (W_XY|W_CAMERA); +} + + +#ifdef QUAKE3 +// +// you can safely add up to 100 fields here without problems, all automatically. Any more, and you need to +// update the gap between ID_SCRIPTPOPUP_ID_START & ID_SCRIPTPOPUP_ID_END in resource.h -ste +// +static const char *ScriptRunFieldNames[]= +{ + "spawnscript", + "idlescript", + "touchscript", + "usescript", + "awakescript", + "angerscript", + "attackscript", + "victoryscript", + "painscript", + "fleescript", + "deathscript", + "delayscript", + "delayscripttime", + "blockedscript", + "affirmscript", + "negativescript", +}; + +int ScriptPopup_GetFieldsCount() +{ + return (sizeof(ScriptRunFieldNames) / sizeof(ScriptRunFieldNames[0])); +} + +LPCSTR ScriptPopup_GetField(int iIndex) +{ + ASSERT(iIndex=ScriptPopup_GetFieldsCount()) + return ""; // won't happen if called only from submenus, but jic... + + return ScriptRunFieldNames[iIndex]; +} + +// returns NULL if field doesn't exist +LPCSTR Ent_GetScriptNameForScriptRunField(entity_t *ent, LPCSTR psFieldName) +{ + LPCSTR psReturn = ValueForKey(ent, psFieldName); + + if (strlen(psReturn)) + return psReturn; + + return NULL; +} + +// returns NULL if field doesn't exist in the selected entity (used as bool check in menu greying) +// +LPCSTR ScriptPopup_GetScriptName(int iIndex) +{ + brush_t *pb = GetTheSelectedBrush(true); + + if (pb) + { + return Ent_GetScriptNameForScriptRunField(pb->owner, ScriptPopup_GetField(iIndex)); + } + + return NULL; +} + +bool ScriptPopup_Allowed() +{ + brush_t *pb = GetTheSelectedBrush(true); + + if (pb) + { + for (int i=0; iowner,ScriptRunFieldNames[i])) + { + return true; + } + } + } + + return false; +} +#endif + +void Select_ShiftTexture(int x, int y) +{ + brush_t *b; + face_t *f; + + if(selected_brushes.next == &selected_brushes && selected_face == NULL) + return; + + for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + { + for (f=b->brush_faces ; f ; f=f->next) + { + f->texdef.shift[0] += x; + f->texdef.shift[1] += y; + } + Brush_Build(b); + if (b->patchBrush) + Patch_ShiftTexture(b->nPatchID, x, y); + } + + if (selected_face) + { + selected_face->texdef.shift[0] += x; + selected_face->texdef.shift[1] += y; + Brush_Build(selected_face_brush); + } + + Sys_UpdateWindows (W_CAMERA); +} + +void Select_ScaleTexture(int x, int y) +{ + brush_t *b; + face_t *f; + + if(selected_brushes.next == &selected_brushes && selected_face == NULL) + { + return; + } + + for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + { + for (f=b->brush_faces ; f ; f=f->next) + { + f->texdef.scale[0] += x; + f->texdef.scale[1] += y; + } + Brush_Build(b); + if (b->patchBrush) + Patch_ScaleTexture(b->nPatchID, x, y); + } + + if (selected_face) + { + selected_face->texdef.scale[0] += x; + selected_face->texdef.scale[1] += y; + Brush_Build(selected_face_brush); + } + + Sys_UpdateWindows (W_CAMERA); +} + +void Select_RotateTexture(int amt) +{ + brush_t *b; + face_t *f; + + if(selected_brushes.next == &selected_brushes && selected_face == NULL) + return; + + for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + { + for (f=b->brush_faces ; f ; f=f->next) + { + f->texdef.rotate += amt; + f->texdef.rotate = static_cast(f->texdef.rotate) % 360; + } + Brush_Build(b); + if (b->patchBrush) + Patch_RotateTexture(b->nPatchID, amt); + } + + if (selected_face) + { + selected_face->texdef.rotate += amt; + selected_face->texdef.rotate = static_cast(selected_face->texdef.rotate) % 360; + Brush_Build(selected_face_brush); + } + + Sys_UpdateWindows (W_CAMERA); +} + +// return is simply a count of how many texture replacements took place, only used for friendly-stats on completion... +// +// the arg list for this is getting pretty gay now, the reason being that the various update-windows routines that +// radiant has don't work like windows ones, where paint messages are just issued then it returns instantly +// (and eliminates dup paint calls), radiant actually goes off and performs them, so I need to stop it doing that +// when calling this in the middle of a loop... +// +int FindReplaceTextures(const char* pFind, const char* pReplace, bool bSelected, bool bForce, bool bReScale, bool bSelectOnlyNoReplace, + bool bInhibitCameraUpdate /*= false*/, + bool bCalledDuringLoopAndNotFirstTime /*= false*/ // sigh, but saves an update-all-windows call + ) +{ + int iReplacedCount = 0; + + if (bSelectOnlyNoReplace) + { + bSelected = false; + bForce = false; + } + + const brush_t* const pList = (bSelected) ? &selected_brushes : &active_brushes; + if (!bSelected) + { + if (!bCalledDuringLoopAndNotFirstTime) + Select_Deselect(); + } + + const qtexture_t * const pReplaceTex = Texture_ForName(pReplace); + + + // count them first, so I can show progress.. + // + int iBrushCount = 0; + for (brush_t* pBrush = pList->next ; pBrush != pList; pBrush = pBrush->next) + { + iBrushCount++; + } + + Sys_Printf("\n"); + brush_t* pNextBrush = pList->next->prev; // doesn't matter what value assigned here really. Note sneaky workaround here because can't point at non-const item, so use next->prev + for (pBrush = pList->next ; pBrush != pList; pBrush = pNextBrush) + { + pNextBrush = pBrush->next; + + // just so they don't think it's locked up... + // + if ( !((iBrushCount--) & 15)) + { + Sys_Printf("."); + } + OutputDebugString(va("iBrushCount = %d\n",iBrushCount)); + + + if (pBrush->patchBrush) + { + if (bSelectOnlyNoReplace) + { + if (!stricmp(pFind,Patch_FromBrush_GetTextureName(pBrush))) + { + g_bScreenUpdates = false; // !!!!!!!!!!!!!!!!!!!!!!!!!!!! + Select_Brush(pBrush, false); + g_bScreenUpdates = true; // !!!!!!!!!!!!!!!!!!!!!!!!!!!! + continue; + } + } + else + { + iReplacedCount += (Patch_FindReplaceTexture(pBrush, pFind, pReplace, bForce))?1:0; + } + } + + for (face_t* pFace = pBrush->brush_faces; pFace; pFace = pFace->next) + { + if(bForce || strcmpi(pFace->texdef.name, pFind) == 0) + { + iReplacedCount++; + if (bSelectOnlyNoReplace) + { + g_bScreenUpdates = false; // !!!!!!!!!!!!!!!!!!!!!!!!!!!! + Select_Brush(pBrush, false); + g_bScreenUpdates = true; // !!!!!!!!!!!!!!!!!!!!!!!!!!!! + break; + } + else + { + if (bReScale) + { + if (pFace->d_texture == pReplaceTex) + {//not changing textures, so reset the scale instead + const float fXAspect = fTEXTURE_SCALE / (float) pFace->texdef.scale[0]; + const float fYAspect = fTEXTURE_SCALE / (float) pFace->texdef.scale[1]; + + pFace->texdef.scale[0] = fTEXTURE_SCALE; + pFace->texdef.scale[1] = fTEXTURE_SCALE; + + pFace->texdef.shift[0] /= fXAspect; + pFace->texdef.shift[1] /= fYAspect; + } + else + { + const float fXAspect = (float)(pFace->d_texture->width) / (float) pReplaceTex->width; + const float fYAspect = (float)(pFace->d_texture->height) / (float) pReplaceTex->height; + + pFace->texdef.scale[0] *= fXAspect; + pFace->texdef.scale[1] *= fYAspect; + + pFace->texdef.shift[0] /= fXAspect; + pFace->texdef.shift[1] /= fYAspect; + } + } + strcpy(pFace->texdef.name, pReplace); + pFace->d_texture = (qtexture_t *)pReplaceTex; + } + } + } + if (!bSelectOnlyNoReplace) + { + Brush_Build(pBrush); + } + } + Sys_Printf("\n"); + + if (!bInhibitCameraUpdate) + { + Sys_UpdateWindows (W_CAMERA); + } + + return iReplacedCount; +} + + +void ComputeAbsolute(face_t* f, vec3_t& p1, vec3_t& p2, vec3_t& p3) +{ + vec3_t ex,ey,ez; // local axis base + + // compute first local axis base + TextureAxisFromPlane(&f->plane, ex, ey); + CrossProduct(ex, ey, ez); + + vec3_t aux; + VectorCopy(ex, aux); + VectorScale(aux, -f->texdef.shift[0], aux); + VectorCopy(aux, p1); + VectorCopy(ey, aux); + VectorScale(aux, -f->texdef.shift[1], aux); + VectorAdd(p1, aux, p1); + VectorCopy(p1, p2); + VectorAdd(p2, ex, p2); + VectorCopy(p1, p3); + VectorAdd(p3, ey, p3); + VectorCopy(ez, aux); + VectorScale(aux, -f->texdef.rotate, aux); + VectorRotate(p1, aux, p1); + VectorRotate(p2, aux, p2); + VectorRotate(p3, aux, p3); + // computing rotated local axis base + vec3_t rex,rey; + VectorCopy(ex, rex); + VectorRotate(rex, aux, rex); + VectorCopy(ey, rey); + VectorRotate(rey, aux, rey); + + ComputeScale(rex,rey,p1,f); + ComputeScale(rex,rey,p2,f); + ComputeScale(rex,rey,p3,f); + + // project on normal plane + // along ez + // assumes plane normal is normalized + ProjectOnPlane(f->plane.normal,f->plane.dist,ez,p1); + ProjectOnPlane(f->plane.normal,f->plane.dist,ez,p2); + ProjectOnPlane(f->plane.normal,f->plane.dist,ez,p3); +}; + + diff --git a/utils/Radiant/select.h b/utils/Radiant/select.h new file mode 100644 index 0000000..b22610a --- /dev/null +++ b/utils/Radiant/select.h @@ -0,0 +1,69 @@ + +typedef enum +{ + sel_brush, + // sel_sticky_brush, + // sel_face, + sel_vertex, + sel_edge, + sel_singlevertex, + sel_curvepoint, + sel_area +} select_t; + +typedef struct +{ + brush_t *brush; + face_t *face; + float dist; + qboolean selected; +} trace_t; + + +#define SF_SELECTED_ONLY 0x01 +#define SF_ENTITIES_FIRST 0x02 +#define SF_SINGLEFACE 0x04 +#define SF_IGNORECURVES 0x08 +#define SF_IGNOREGROUPS 0x10 +#define SF_CYCLE 0x20 + + +trace_t Test_Ray (vec3_t origin, vec3_t dir, int flags); + +void Select_GetBounds (vec3_t mins, vec3_t maxs); +void Select_Brush (brush_t *b, bool bComplete = true); +void Select_Ray (vec3_t origin, vec3_t dir, int flags); +void Select_Delete (void); +void Select_Deselect (bool bDeSelectToListBack = false); +void Select_Clone (void); +void Select_Move (vec3_t delta, bool bSnap = true); +void Select_SetTexture (texdef_t *texdef, bool bFitScale = false, bool bNoSystemTextureOverwrite = false); +void Select_FitTexture(int nHeight = 1, int nWidth = 1); +void Select_FlipAxis (int axis); +void Select_RotateAxis (int axis, float deg, bool bPaint = true, bool bMouse = false); +void Select_CompleteTall (void); +void Select_PartialTall (void); +void Select_Touching (void); +void Select_Inside (void); +void Select_MakeStructural (void); +void Select_MakeDetail (bool bDeselectAfterwards = true); +void Select_MakeNonSolid(void); +void Select_ClearNonSolid(void); +void Select_HideWaypointChildren(void); +void Select_UnHideWaypointChildren(void); +void Select_UnHideAllWaypoints(void); +void Select_Hide(); +void Select_ShowAllHidden(); +brush_t *GetTheSelectedBrush(bool bQuietError = false); +void ComputeAbsolute(face_t* f, vec3_t& p1, vec3_t& p2, vec3_t& p3); +void AbsoluteToLocal(plane_t normal2, face_t* f, vec3_t& p1, vec3_t& p2, vec3_t& p3); + +#ifdef QUAKE3 +int ScriptPopup_GetFieldsCount(); +LPCSTR ScriptPopup_GetField(int iIndex); +LPCSTR ScriptPopup_GetScriptName(int iIndex); +bool ScriptPopup_Allowed(); +#endif + +#define sKEY_HIDDENWAYPOINT "hiddenwaypoint" +#define sKEY_WAYPOINTHIDE_RECURSION_PROTECT "recursed" diff --git a/utils/Radiant/shaderinfo.cpp b/utils/Radiant/shaderinfo.cpp new file mode 100644 index 0000000..5e37e3a --- /dev/null +++ b/utils/Radiant/shaderinfo.cpp @@ -0,0 +1,47 @@ +// ShaderInfo.cpp: implementation of the CShaderInfo class. +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "Radiant.h" +#include "ShaderInfo.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CShaderInfo::CShaderInfo() +{ + m_fTransValue = 1.0; + m_nFlags = 0; +} + +CShaderInfo::~CShaderInfo() +{ + +} + +void CShaderInfo::Parse(const char *pName) +{ + +} + +void CShaderInfo::setName(char *pName) +{ + //--char path[1024]; + //--strcpy(path, pName); + //--DefaultExtension(path, ".tga"); + m_strName = pName; + m_strName.MakeLower(); + if (m_strName.Find("textures") == 0) + { + CString s = m_strName.Right(m_strName.GetLength() - strlen("textures") - 1); + m_strName = s; + } +} diff --git a/utils/Radiant/shaderinfo.h b/utils/Radiant/shaderinfo.h new file mode 100644 index 0000000..a0e5e33 --- /dev/null +++ b/utils/Radiant/shaderinfo.h @@ -0,0 +1,33 @@ +// ShaderInfo.h: interface for the CShaderInfo class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_SHADERINFO_H__93B64600_A208_11D2_803D_0020AFEB881A__INCLUDED_) +#define AFX_SHADERINFO_H__93B64600_A208_11D2_803D_0020AFEB881A__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +class CShaderInfo +{ +public: + CString m_strName; + CString m_strShaderName; + CString m_strTextureName; + CStringList m_strEditorParams; + CStringList m_lstSurfaceParams; + float m_fTransValue; + int m_nFlags; + + + void Parse(const char *pName); + CShaderInfo(); + virtual ~CShaderInfo(); + void setName(char *pName); + + + +}; + +#endif // !defined(AFX_SHADERINFO_H__93B64600_A208_11D2_803D_0020AFEB881A__INCLUDED_) diff --git a/utils/Radiant/sourcesafe.cpp b/utils/Radiant/sourcesafe.cpp new file mode 100644 index 0000000..25f21a7 --- /dev/null +++ b/utils/Radiant/sourcesafe.cpp @@ -0,0 +1,1162 @@ +// Filename:- sourcesafe.cpp +// +// (read the sourcesafe.h notes for extra info) +// +#include "stdafx.h" + +// Microsoft's GUID stuff never seems to work properly, so... +// +#define DEFINE_GUID_THAT_WORKS(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } + +#include +#include "oddbits.h" +#include "ssauto.h" +#include "ssauterr.h" + +#include "sourcesafe.h" + + +// SourceSafe can sometimes take a LONG time to return from calls to it, so in order to let users know that your app +// hasn't crashed elsewhere it's a good idea to fill in these macros with something appropriate to your own code... +// +int iSourceSafeReferenceCount = 0; +void SourceSafe_Enter(LPCSTR psString) +{ +// if (!iSourceSafeReferenceCount++) + { + CString str = psString; + str.Insert(0,"SourceSafe: "); + Sys_Printf(va("%s\n",str)); + } +} + +void SourceSafe_Leave(void) +{ +// if (!--iSourceSafeReferenceCount) + { + //((CMainFrame*)AfxGetMainWnd())->StatusMessage + Sys_Printf("Ready\n"); + } +} + +#define _SS_ENTER(string) SourceSafe_Enter(string) //((CMainFrame*)AfxGetMainWnd())->StatusMessage("(Waiting for SourceSafe...)"); +#define _SS_LEAVE() SourceSafe_Leave() //((CMainFrame*)AfxGetMainWnd())->StatusMessage("Ready"); + +// Coding note, generally the functions beginning "SS_" are called externally, and the ones beginning "_" are internal. +// +// Incidentally, the docs say: +// +// ----- +// Be sure to link with the following libraries: +// user32.lib uuid.lib oleaut32.lib ole32.lib +// ----- +// +//... but this appears not to be needed. I suspect stdafx.h may have something to do with that, so I'll leave +// that comment there in case this is ever used with standard windows code only +// + + + +// replace these 3 with hacks to fit this module to BehavEd... +// +CString g_cstrSourceSafeINI = sEF1_SS_INI; +CString g_cstrSourceSafeProject = sEF1_SS_PROJECT; +BOOL g_bUseSourceSafe = true; + + +// remember to do a SysFreeString() on the returned value when you've finished with it! +// +BSTR StringToBSTR(LPCSTR string) +{ + OLECHAR* svalue = NULL; + BSTR bstrValue = NULL; + int iLen; + bool bFree_svalue = false; + + if( (iLen = MultiByteToWideChar(CP_ACP, 0, string, -1, svalue, 0 )) != 1) + { + svalue = new OLECHAR[iLen]; + bFree_svalue = true; + if( MultiByteToWideChar(CP_ACP, 0, string, -1, svalue, iLen ) == 0 ) + { + ErrorBox( va("StringToBSTR: Error converting \"%s\" to BSTR", string)); + } + } + else + { + svalue = L""; + } + + bstrValue = SysAllocString(svalue); + + if (svalue && bFree_svalue) + delete svalue; + + return bstrValue; +} + + +// thanks for making us jump through yet more hoops, Microsoft... +// +// (actually there is a "legal" way to do this, in a similar way to above, but I can be arsed looking it up) +// +LPCSTR BSTRToString(BSTR pStrOLE_MyName) +{ + static CString str; + str.Empty(); + + WORD *pw = (WORD *) pStrOLE_MyName; + + char c; + do + { + c = LOBYTE(*pw++); + str+=va("%c",c); // hahahaha! + } + while (c); + + return (LPCSTR) str; +} + + +IClassFactory *g_pClf = NULL; +IVSSDatabase *g_pVdb = NULL; +BSTR bstrPath = NULL; +BSTR bstrUName = NULL; +BSTR bstrUPass = NULL; +BSTR bstrProject= NULL; +// +bool gbDatabaseHeldOpen = false; + +bool _OpenDatabase(bool bQuietErrors = false); // spot the retrofit +bool _OpenDatabase(bool bQuietErrors) +{ + if (gbDatabaseHeldOpen) + return true; + + static bool bFirstTime = false; + if (!bFirstTime) + { + bFirstTime = true; + SS_Startup_OnceOnly(); + } + + _SS_ENTER("Opening..."); + + bool bReturn = false; + CLSID clsid; + + bstrPath = StringToBSTR ( (LPCSTR) g_cstrSourceSafeINI ); // eg "\\\\RAVEND\\VSS\\SRCSAFE.INI" + bstrUName = SysAllocString(L""); // user name (eg) "scork" or "guest", but best left blank for auto. + bstrUPass = SysAllocString(L""); // user password, again leave blank + bstrProject = StringToBSTR ( (LPCSTR) g_cstrSourceSafeProject ); // eg. "$/StarTrek/BaseQ3/Maps" + + CoInitialize(0); + if(S_OK == CLSIDFromProgID(L"SourceSafe", &clsid )) + { + if(S_OK == CoGetClassObject( clsid, CLSCTX_ALL, NULL, IID_IClassFactory, (void**)&g_pClf )) + { + if(S_OK == g_pClf->CreateInstance( NULL, IID_IVSSDatabase, (void **) &g_pVdb )) + { + if(S_OK == g_pVdb->Open(bstrPath, bstrUName, bstrUPass)) + { + if (S_OK == g_pVdb->put_CurrentProject(bstrProject)) + { + gbDatabaseHeldOpen = true; + bReturn = true; + } + else + { + if (!bQuietErrors) + ErrorBox( va("SourceSafe: Failed to set current project to \"%s\"",g_cstrSourceSafeProject)); + } + } + else + { + if (!bQuietErrors) + ErrorBox( va("SourceSafe: Failed during open of INI file \"%s\"",g_cstrSourceSafeINI)); + } + } + else + { + if (!bQuietErrors) + ErrorBox( "SourceSafe: Failed to create a database instance"); + } + } + else + { + if (!bQuietErrors) + ErrorBox( "SourceSafe: Failed to get ClassFactory interface"); + } + } + else + { + if (!bQuietErrors) + ErrorBox( "SourceSafe: Failed to get CLSID (GUID)"); + } + + _SS_LEAVE(); + + return bReturn; +} + +// this should be called regardless of whether or not the _OpenDatabase function succeeded because it could +// have got part way through and only error'd later, so this frees up what it needs to +// +void _CloseDatabase() +{ + if (gbDatabaseHeldOpen) + return; + + _SS_ENTER("Closeing..."); + + if (g_pVdb) + { + g_pVdb->Release(); + g_pVdb = NULL; + } + + if (g_pClf) + { + g_pClf->Release(); + g_pClf = NULL; + } + + CoUninitialize(); + + // no need to check for NULL ptr for this function... + // + SysFreeString(bstrPath); bstrPath = NULL; + SysFreeString(bstrUName); bstrUName = NULL; + SysFreeString(bstrUPass); bstrUPass = NULL; + SysFreeString(bstrProject); bstrProject = NULL; + + _SS_LEAVE(); +} + + + +// there isn't really any totally legal way to do this because Radiant doesn't have projects with files added to +// sourcesafe through menus etc in the same way as (eg) VC, so I'll just do a bit of a kludge for now. This should +// be updated for any other projects/files you want to handle yourself... +// +// currently I'm expecting files like: +// +// "Q:\\quake\\baseq3\\scripts\\borg.shader" +// +// .. which I convert by matching the "baseq3" part to produce: +// +// "$/StarTrek/BaseQ3/Shaders/borg3.shader" +// +// returns NULL on fail. +// + +// BEHAVED NOTE: +// +// scripts pathnames are in usually in the form : "Q:\\quake\\baseq3\\real_scripts\\oz\\myborg.txt" +// sourcesafe pathnames are in the form : "$/StarTrek/real_scripts" downwards +// +// ... so I just need to search for the common part of the path ("real_scripts")... +// +LPCSTR FilenameToProjectName(LPCSTR path, bool bQuiet = false); +LPCSTR FilenameToProjectName(LPCSTR path, bool bQuiet) +{ + static CString strPath; + strPath = path; // do NOT join to above line (or it only happens first time) + + strPath.Replace("\\","/"); + + int loc = strPath.Find("/maps/"); + if (loc>=0) + { + strPath = strPath.Mid(loc+6); // skip over "/maps/" + } + else + { + if (!bQuiet) + { + ErrorBox( va("SourceSafe: FilenameToProjectName(): Failed to convert \"%s\" to SS path",path)); + } + return NULL; + } + + strPath.Insert(0,va("%s/",(LPCSTR) g_cstrSourceSafeProject)); + + return (LPCSTR) strPath; +} + +// return is how many versions were listed, AFAIK it should always be at least 1 (ie "created") for any valid SS item +// +int _ListVersions( IVSSDatabase* db, LPCSTR psPathSS, CString &strOutput ) +{ + _SS_ENTER("ListVersions..."); + +#define MAX_VERSIONS_TO_LIST 40 // otherwise these things get fucking *huge*! + int iVersions = 0; + BSTR bstrval; + char lpbuf[200]; + char lpbuf2[200]; + + IVSSItem *vssi; + IVSSVersion *vers; + IVSSVersions *vx; + LPUNKNOWN lpunk; + IEnumVARIANT *ppvobj; + VARIANT st; + BSTR bstrValue; + int x; + ULONG fetched; + long lvnum; + + HRESULT hr; + + strOutput = ""; + + bstrValue = StringToBSTR(psPathSS); + + if( S_OK == db->get_VSSItem(bstrValue, FALSE, &vssi) ) + { + if( S_OK == vssi->get_Versions( 0l, &vx ) ) + { + if( S_OK == vx->_NewEnum(&lpunk) ) + { + if(!FAILED(lpunk->QueryInterface(IID_IEnumVARIANT, (void**)&ppvobj))) + { + vssi->get_Spec( &bstrval ); + x = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)bstrval, -1, + lpbuf, sizeof(lpbuf), NULL, NULL ); + +// strOutput = va("History of: %s\n", lpbuf ); +// OutputDebugString(va("History of: %s\n", lpbuf )); +// strOutput+= "ACTION USER NAME VERSION NUMBER\n"; +// OutputDebugString("ACTION USER NAME VERSION NUMBER\n"); + + do + { + ppvobj->Next( 1UL, &st, &fetched ); + if( fetched != 0 ) + { + if(!FAILED(hr = st.punkVal->QueryInterface(IID_IVSSVersion,(void**)&vers))) + { + vers->get_Action( &bstrval ); + WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)bstrval, -1, + lpbuf, sizeof(lpbuf), NULL, NULL ); + vers->get_Username( &bstrval ); + WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)bstrval, -1, + lpbuf2, sizeof( lpbuf2 ), NULL, NULL ); + vers->get_VersionNumber( &lvnum ); +// OutputDebugString(va("%s %s %ld\n", lpbuf, lpbuf2, lvnum )); + + // version numbers can go a bit weird, in that global labels have different version + // numbers, but since it's all presented in a backwards order I'd have to do a whole + // bunch of messy stuff putting them into arrays then scanning them from last to first + // to check version numbers were sequential, then zap any that weren't, so in the end + // I'm just going to to ignore version numbers for labels... + // + if (!strnicmp(lpbuf,"labeled",7)) + { + strOutput += va("%s by user %s\n", lpbuf, lpbuf2, lvnum ); + } + else + { + strOutput += va("%s by user %s (Ver. %ld)\n", lpbuf, lpbuf2, lvnum ); + } + + iVersions++; + + vers->Release(); + } + else + { + ErrorBox( va("SourceSafe: _ListVersions(): Failed in QueryInterface(IID_IVSSVersion) for file \"$s\"",psPathSS )); + } + st.punkVal->Release(); + } + } while( fetched != 0 + #ifdef MAX_VERSIONS_TO_LIST + && iVersions < MAX_VERSIONS_TO_LIST + #endif + ); + ppvobj->Release(); + } + else + { + ErrorBox( va("SourceSafe: _ListVersions(): Failed in QueryInterface(IID_IEnumVARIANT) for file \"$s\"",psPathSS )); + } + lpunk->Release(); + } + else + { + ErrorBox( va("SourceSafe: _ListVersions(): Failed in _NewEnum() for file \"$s\"",psPathSS )); + } + vx->Release(); + } + else + { + ErrorBox( va("SourceSafe: _ListVersions(): Failed in get_Versions() for file \"$s\"",psPathSS )); + } + vssi->Release(); + } + else + { + ErrorBox( va("SourceSafe: _ListVersions(): Failed in get_VSSItem() for file \"$s\"",psPathSS )); + } + SysFreeString(bstrValue); + + #ifdef MAX_VERSIONS_TO_LIST + if (iVersions >= MAX_VERSIONS_TO_LIST) + { + strOutput += va("\n\n(History list capped to %d entries)",MAX_VERSIONS_TO_LIST); + } + #endif + + _SS_LEAVE(); + + return iVersions; +} + + +// int return is number of users who have this file checked out, unless param 'psNameToCheckForMatch' is NZ, +// in which case return is a bool as to whether or not the name in question (usually our name) has the file checked out... +// +// ( output list is CR-delineated ) +// +// return is -1 for error, else checkout count, else 1 for true if doing a me-match via last param +// +int _ListCheckOuts( IVSSDatabase* db, LPCSTR psPathSS, CString &strOutput, LPCSTR psNameToCheckForMatch ) +{ + _SS_ENTER("ListCheckOuts..."); + + int iCheckOuts = -1; + BSTR bstrval; + char lpbuf[200]; + char lpbuf2[200]; + + IVSSItem *vssi; + IVSSVersion *vers; + IVSSCheckouts *vx; + LPUNKNOWN lpunk; + IEnumVARIANT *ppvobj; + VARIANT st; + BSTR bstrValue; + int x; + ULONG fetched; + + HRESULT hr; + + strOutput = ""; + + bstrValue = StringToBSTR( psPathSS ); + + if( S_OK == db->get_VSSItem( bstrValue, FALSE, &vssi )) + { + if( S_OK == vssi->get_Checkouts( &vx ) ) + { + if( S_OK == vx->_NewEnum(&lpunk) ) + { + if(!FAILED(lpunk->QueryInterface(IID_IEnumVARIANT, (void**)&ppvobj))) + { + vssi->get_Spec( &bstrval ); + x = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)bstrval, -1, lpbuf, sizeof(lpbuf), NULL, NULL ); + +// OutputDebugString(va("Checkout status of: %s\n", lpbuf )); +// strOutput = va("Checkout status of: %s\n", lpbuf )); + + do + { + ppvobj->Next( 1UL, &st, &fetched ); + if( fetched != 0 ) + { + if(!FAILED(hr = st.punkVal->QueryInterface(IID_IVSSCheckout,(void**)&vers))) + { +// vers->get_Action( &bstrval ); +// WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)bstrval, -1, +// lpbuf, sizeof(lpbuf), NULL, NULL ); + vers->get_Username( &bstrval ); + WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)bstrval, -1, + lpbuf2, sizeof( lpbuf2 ), NULL, NULL ); +// vers->get_VersionNumber( &lvnum ); + +// OutputDebugString(va("%s %s %ld\n", lpbuf, lpbuf2, lvnum )); + //OutputDebugString(va("%s\n", lpbuf2)); + strOutput += va("%s\n", lpbuf2); + + if ( psNameToCheckForMatch ) + { + if (!stricmp(lpbuf2,psNameToCheckForMatch) ) // is stricmp too paranoid? + { + iCheckOuts = 1; // hardwire: checked out by me = true + } + } + else + { + if (iCheckOuts == -1) // first time - change error-flag to valid count? + { + iCheckOuts = 1; + } + else + { + iCheckOuts++; + } + } + + vers->Release(); + } + else + { + ErrorBox( va("SourceSafe: _ListCheckOuts(): Failed in QueryInterface(IID_IVSSCheckout) for file \"$s\"",psPathSS )); + } + st.punkVal->Release(); + } + } while( fetched != 0 ); + ppvobj->Release(); + } + else + { + ErrorBox( va("SourceSafe: _ListCheckOuts(): Failed in QueryInterface(IID_IEnumVARIANT) for file \"$s\"",psPathSS )); + } + lpunk->Release(); + } + else + { + ErrorBox( va("SourceSafe: _ListCheckOuts(): Failed in _NewEnum() for file \"$s\"",psPathSS )); + } + vx->Release(); + } + else + { + ErrorBox( va("SourceSafe: _ListCheckOuts(): Failed in get_Checkouts() for file \"$s\"",psPathSS )); + } + vssi->Release(); + } + else + { + ErrorBox( va("SourceSafe: _ListCheckOuts(): Failed in get_VSSItem() for file \"$s\"",psPathSS )); + } + SysFreeString(bstrValue); + + _SS_LEAVE(); + + return iCheckOuts; +} + + +bool _IsCheckedOut( IVSSDatabase* db, LPCSTR psPathSS ) +{ + _SS_ENTER("Is checked out?..."); + + IVSSItem* vssi; + bool bReturn = false; + BSTR bstrValue = StringToBSTR( psPathSS ); + + if( S_OK == db->get_VSSItem( bstrValue, FALSE, &vssi ) ) + { + long lStatus; + if( S_OK == vssi->get_IsCheckedOut( &lStatus ) ) + { + bReturn = !!lStatus; + } + else + { + ErrorBox( va("SourceSafe: Error during _IsCheckedOut() for file \"%s\"",psPathSS )); + } + vssi->Release(); + } + else + { + ErrorBox( va( "SourceSafe: _IsCheckedOut(): Error during get_VSSItem() for file \"%s\"",psPathSS )); + } + + SysFreeString( bstrValue ); + + _SS_LEAVE(); + + return bReturn; +} + +bool _CheckOut( IVSSDatabase* db, LPCSTR psPathSS, LPCSTR psPathDisk ) +{ + _SS_ENTER("Check Out..."); + + IVSSItem* vssi; + bool bReturn = false; + BSTR bstrValue = StringToBSTR ( psPathSS ); + BSTR bsComment = SysAllocString( L"" ); + + if( S_OK == db->get_VSSItem( bstrValue, FALSE, &vssi ) ) + { + BSTR bsLocal = StringToBSTR(psPathDisk); + + if( S_OK == vssi->Checkout( bsComment, bsLocal, 0 )) + { + bReturn = true; + } + else + { + ErrorBox( va( "SourceSafe: Error during _CheckOut() for file \"%s\"",psPathSS )); + } + + SysFreeString( bsLocal ); + + vssi->Release(); + } + else + { + ErrorBox( va( "SourceSafe: _CheckOut(): Error during get_VSSItem() for file \"%s\"",psPathSS )); + } + + SysFreeString( bsComment ); + SysFreeString( bstrValue ); + + _SS_LEAVE(); + + return bReturn; +} + + +bool _Add( IVSSDatabase* db, LPCSTR psPathSS, LPCSTR psPathDisk ) +{ + _SS_ENTER("Add..."); + + IVSSItem *vssi,*vssi2; + bool bReturn = false; + BSTR bstrValue = StringToBSTR ( psPathSS ); + BSTR bsComment = SysAllocString( L"" ); + + CString strPathRelativeToProject = psPathSS; + strPathRelativeToProject.Replace("/","\\"); + Filename_RemoveFilename(strPathRelativeToProject); + strPathRelativeToProject.Replace("\\","/"); + + // ( eg: "$/StarTrek/BaseQ3/real_scripts/ste" ) + + BSTR bstrProject = StringToBSTR(strPathRelativeToProject); + + // ensure SS path exists... + // + CString strBuiltUpProject; + CString strSourceProject(strPathRelativeToProject); + while (!strSourceProject.IsEmpty()) + { + CString strProjectPathSoFar(strBuiltUpProject); + CString strNewSubProj; + int iLoc = strSourceProject.Find("/"); + if (iLoc >=0) + { + strNewSubProj = strSourceProject.Left(iLoc); + } + else + { + strNewSubProj = strSourceProject; + strSourceProject = ""; + } + strBuiltUpProject += strBuiltUpProject.IsEmpty() ? "" : "/"; + strBuiltUpProject += strNewSubProj; + strSourceProject = strSourceProject.Mid(iLoc+1); + + if (strBuiltUpProject != "$") // get_VSSItem() doesn't work on root, and it'll always be there anyway + { + BSTR bstrTempProjectPath = StringToBSTR(strBuiltUpProject); + { + if( S_OK != db->get_VSSItem( bstrTempProjectPath, FALSE, &vssi ) ) + { + // need to build this new part of the path... + // + BSTR bstrProjectPathSoFar = StringToBSTR(strProjectPathSoFar); + { + if( S_OK == db->get_VSSItem( bstrProjectPathSoFar, FALSE, &vssi ) ) + { + BSTR bstrNewSubProj = StringToBSTR(strNewSubProj); + { + if (S_OK == vssi->NewSubproject(bstrNewSubProj, bsComment, &vssi2)) + { + // yay! + } + else + { + ErrorBox(va("SS_Add(): Error creating subproject \"%s\" in \"%s\"!",(LPCSTR) strNewSubProj, (LPCSTR) strProjectPathSoFar)); + break; + } + } + SysFreeString( bstrNewSubProj ); + } + } + SysFreeString( bstrProjectPathSoFar ); + } + } + SysFreeString( bstrTempProjectPath ); + } + } + + if( S_OK == db->get_VSSItem( bstrProject, FALSE, &vssi ) ) + { + BSTR bsLocal = StringToBSTR( psPathDisk ); + + // if( S_OK == vssi->Checkin ( bsComment, bsLocal, 0 )) + if( S_OK == vssi->Add ( bsLocal, bsComment, 0, &vssi2)) + { + vssi2->Release(); + bReturn = true; + } + else + { + ErrorBox( va( "SourceSafe: Error during _Add() for file \"%s\"",psPathSS )); + } + + SysFreeString( bsLocal ); + + vssi->Release(); + } + else + { + ErrorBox( va( "SourceSafe: _CheckIn(): Error during get_VSSItem() for file \"%s\"",psPathSS )); + } + + SysFreeString( bsComment ); + SysFreeString( bstrValue ); + + _SS_LEAVE(); + + return bReturn; +} + + +bool _CheckIn( IVSSDatabase* db, LPCSTR psPathSS, LPCSTR psPathDisk ) +{ + _SS_ENTER("Check In..."); + + IVSSItem* vssi; + bool bReturn = false; + BSTR bstrValue = StringToBSTR ( psPathSS ); + BSTR bsComment = SysAllocString( L"" ); + + if( S_OK == db->get_VSSItem( bstrValue, FALSE, &vssi ) ) + { + BSTR bsLocal = StringToBSTR( psPathDisk ); + + if( S_OK == vssi->Checkin ( bsComment, bsLocal, 0 )) + { + bReturn = true; + } + else + { + ErrorBox( va( "SourceSafe: Error during _CheckIn() for file \"%s\"",psPathSS )); + } + + SysFreeString( bsLocal ); + + vssi->Release(); + } + else + { + ErrorBox( va( "SourceSafe: _CheckIn(): Error during get_VSSItem() for file \"%s\"",psPathSS )); + } + + SysFreeString( bsComment ); + SysFreeString( bstrValue ); + + _SS_LEAVE(); + + return bReturn; +} + + + +bool _UndoCheckOut( IVSSDatabase* db, LPCSTR psPathSS ) +{ + _SS_ENTER("Undo Check out..."); + + IVSSItem* vssi; + bool bReturn = false; + BSTR bstrValue = StringToBSTR ( psPathSS ); + BSTR bsComment = SysAllocString( L"" ); + + if( S_OK == db->get_VSSItem(bstrValue, FALSE, &vssi ) ) + { + BSTR bsLocal = SysAllocString( L"" ); // this doesn't seem to need a path to the disk file, presumably it works it out + + if( S_OK == vssi->UndoCheckout( bsLocal, 0 )) + { + bReturn = true; + } + else + { + ErrorBox( va("SourceSafe: Error during _UndoCheckOut() for file \"%s\"",psPathSS)); + } + + SysFreeString(bsLocal); + + vssi->Release(); + } + else + { + ErrorBox( va( "SourceSafe: _UndoCheckOut(): Error during get_VSSItem() for file \"%s\"",psPathSS )); + } + + SysFreeString( bsComment ); + SysFreeString( bstrValue ); + + _SS_LEAVE(); + + return bReturn; +} + + + +// these routines called externally... +// + + +bool SS_IsUnderSourceControl( LPCSTR psPathedFilename ) +{ + CWaitCursor waitcursor; + bool bReturn = false; + + if ( SS_FunctionsAvailable() ) + { + LPCSTR psSSName = FilenameToProjectName(psPathedFilename, true); // bQuiet + + if (psSSName ) + { + if (_OpenDatabase() ) + { + _SS_ENTER("Checking if item is under control"); + + IVSSItem* vssi; + BSTR bstrValue = StringToBSTR ( psSSName ); + + if( S_OK == g_pVdb->get_VSSItem( bstrValue, FALSE, &vssi ) ) + { + vssi->Release(); + bReturn = true; + } + else + { + // item doesn't appear to be under SS control + } + + SysFreeString( bstrValue ); + + _SS_LEAVE(); + } + _CloseDatabase(); + } + } + + return bReturn; +} + +bool SS_Add(LPCSTR psPathedFilename) +{ + CWaitCursor waitcursor; + + bool bReturn = false; + + if ( SS_FunctionsAvailable() ) + { + LPCSTR psSSName = FilenameToProjectName(psPathedFilename); + + if ( psSSName ) + { + if ( _OpenDatabase() ) + { + if (_Add( g_pVdb, psSSName, psPathedFilename ) ) + { + bReturn = true; + } + } + _CloseDatabase(); + } + } + + return bReturn; +} + +bool SS_CheckIn(LPCSTR psPathedFilename) +{ + CWaitCursor waitcursor; + + bool bReturn = false; + + if ( SS_FunctionsAvailable() ) + { + LPCSTR psSSName = FilenameToProjectName(psPathedFilename); + + if ( psSSName ) + { + if ( _OpenDatabase() ) + { + if ( _CheckIn( g_pVdb, psSSName, psPathedFilename ) ) + { + bReturn = true; + } + } + _CloseDatabase(); + } + } + + return bReturn; +} + +bool SS_CheckOut( LPCSTR psPathedFilename ) +{ + CWaitCursor waitcursor; + bool bReturn = false; + + if ( SS_FunctionsAvailable() ) + { + LPCSTR psSSName = FilenameToProjectName(psPathedFilename); + + if ( psSSName ) + { + if ( _OpenDatabase() ) + { + if ( _CheckOut( g_pVdb, psSSName, psPathedFilename ) ) + { + bReturn = true; + } + } + _CloseDatabase(); + } + } + + return bReturn; +} + +bool SS_UndoCheckOut( LPCSTR psPathedFilename ) +{ + CWaitCursor waitcursor; + bool bReturn = false; + + if ( SS_FunctionsAvailable() ) + { + LPCSTR psSSName = FilenameToProjectName(psPathedFilename); + + if ( psSSName ) + { + if ( _OpenDatabase() ) + { + if ( _UndoCheckOut( g_pVdb, psSSName ) ) + { + bReturn = true; + } + } + _CloseDatabase(); + } + } + + return bReturn; +} + +bool SS_IsCheckedOut( LPCSTR psPathedFilename ) +{ + CWaitCursor waitcursor; + bool bReturn = false; + + ASSERT( SS_FunctionsAvailable() ); // you shouldn't really be calling this otherwise + + if ( SS_FunctionsAvailable() ) + { + LPCSTR psSSName = FilenameToProjectName(psPathedFilename); + + if ( psSSName ) + { + if ( _OpenDatabase() ) + { + if ( _IsCheckedOut( g_pVdb, psSSName ) ) + { + bReturn = true; + } + } + _CloseDatabase(); + } + } + + return bReturn; +} + +// similar to above, but we're only interested if it's checked out by us... +// +// (actually after I wrote this I found a more legal way to do it, but this works...) +// +bool SS_IsCheckedOutByMe( LPCSTR psPathedFilename ) +{ + CWaitCursor waitcursor; + bool bReturn = false; + + ASSERT( SS_FunctionsAvailable() ); // you shouldn't really be calling this otherwise + + if ( SS_FunctionsAvailable() ) + { + LPCSTR psSSName = FilenameToProjectName( psPathedFilename ); + + if ( psSSName ) + { + if ( _OpenDatabase() ) + { + _SS_ENTER("Is checked out by me?"); + + CString junk; + + BSTR pStrOLE_MyName; + if (S_OK == g_pVdb->get_UserName(&pStrOLE_MyName)) + { + int iCount = _ListCheckOuts( g_pVdb, psSSName, junk, BSTRToString( pStrOLE_MyName) ); + + if ( iCount == 1) + { + bReturn = true; + } + } + else + { + ErrorBox( "SourceSafe: SS_IsCheckedOutByMe(): Failed in get_UserName()" ); + } + + _SS_LEAVE(); + } + _CloseDatabase(); + } + } + + return bReturn; +} + +bool SS_ListVersions( LPCSTR psPathedFilename, CString &strOutput ) +{ + CWaitCursor waitcursor; + bool bReturn = false; + + if ( SS_FunctionsAvailable() ) + { + LPCSTR psSSName = FilenameToProjectName( psPathedFilename ); + + if ( psSSName ) + { + if ( _OpenDatabase() ) + { + if ( _ListVersions( g_pVdb, psSSName, strOutput )) + { + bReturn = true; + } + } + _CloseDatabase(); + } + } + + return bReturn; +} + +// if false (error) return, don't rely on a valid iCount! +// +bool SS_ListCheckOuts( LPCSTR psPathedFilename, CString &strOutput, int &iCount ) +{ + CWaitCursor waitcursor; + bool bReturn = false; + + ASSERT( SS_FunctionsAvailable() ); // you shouldn't really be calling this otherwise + + if ( SS_FunctionsAvailable() ) + { + LPCSTR psSSName = FilenameToProjectName( psPathedFilename ); + + if ( psSSName ) + { + if ( _OpenDatabase() ) + { + iCount = _ListCheckOuts( g_pVdb, psSSName, strOutput, NULL ); + + if (iCount != -1) + { + bReturn = true; // strictly speaking this may not be true, but if (later: hmmmm, what was i going to write here? ) + } + } + _CloseDatabase(); + } + } + + return bReturn; +} + + +bool SS_FunctionsAvailable( void ) +{ + bool bReturn = false; + + if ( g_bUseSourceSafe ) + { + if (_OpenDatabase(true)) // bool bQuietErrors + { + bReturn = true; + } + _CloseDatabase(); + } + + return bReturn; +} + +// this is a pretty tacky, but for now... :-) +// +bool SS_SetupOk( void ) +{ + CWaitCursor waitcursor; + bool g_bUseSourceSafe_PREV = g_bUseSourceSafe; + g_bUseSourceSafe = true; + + bool bReturn = SS_FunctionsAvailable(); + + g_bUseSourceSafe = g_bUseSourceSafe_PREV; + + return bReturn; +} + + + + +void SS_Startup_OnceOnly(void) +{ + // nothing at the moment... +} + +void SS_Shutdown_OnceOnly(void) +{ + gbDatabaseHeldOpen = false; + _CloseDatabase(); +} + +void SS_LoadFromRegistry(void) +{ + char sBuffer[256]; + + LONG + lSize = sizeof(sBuffer); + if (LoadRegistryInfo("Radiant::SS_INI", &sBuffer, &lSize)) + g_cstrSourceSafeINI = sBuffer; + + lSize = sizeof(sBuffer); + if (LoadRegistryInfo("Radiant::SS_Project", &sBuffer, &lSize)) + g_cstrSourceSafeProject = sBuffer; + + lSize = sizeof(sBuffer); + if (LoadRegistryInfo("Radiant::SS_Use", &sBuffer, &lSize)) + g_bUseSourceSafe = !!strlen(sBuffer); // any length string = true +} + + +void SS_SaveToRegistry(void) +{ + char sBuffer[256]; + + strcpy(sBuffer,g_cstrSourceSafeINI); + SaveRegistryInfo("Radiant::SS_INI", &sBuffer, sizeof(sBuffer)); + + strcpy(sBuffer,g_cstrSourceSafeProject); + SaveRegistryInfo("Radiant::SS_Project", &sBuffer, sizeof(sBuffer)); + + strcpy(sBuffer,g_bUseSourceSafe?"true":""); // zero-len string for false! + SaveRegistryInfo("Radiant::SS_Use", &sBuffer, sizeof(sBuffer)); +} + + +/////////////////// eof ///////////////// + diff --git a/utils/Radiant/sourcesafe.h b/utils/Radiant/sourcesafe.h new file mode 100644 index 0000000..a7879af --- /dev/null +++ b/utils/Radiant/sourcesafe.h @@ -0,0 +1,59 @@ +// Filename:- sourcesafe.h +// + +// any questions: bug me -Ste. + +#ifndef SOURCSAFE_H +#define SOURCSAFE_H + + +#define sEF1_SS_INI "\\\\RAVEND\\VSS\\SRCSAFE.INI" +#define sEF1_SS_PROJECT "$/StarTrek/BaseQ3/Maps" // note no slash on the end! + +#define sCHC_SS_INI "\\\\Ravend\\vss_projects\\StarWars\\SRCSAFE.INI" +#define sCHC_SS_PROJECT "$/base/maps" // note no slash on the end! + +extern CString g_cstrSourceSafeINI; // these default to sensible values for Trek, but can be changed +extern CString g_cstrSourceSafeProject; // +extern BOOL g_bUseSourceSafe; // only external for prefs setting, use "SS_FunctionsAvailable()" for query + +// Usage notes: +// +// By choice, you should check "SS_FunctionsAvailable()" yourself before calling any of the functions below. +// They do check them internally (so no SS code is called if you've turned it off because of a bad connection etc) +// but since these all return bools it can be misleading as to whether (eg) SS code is disabled/missing, or the answer +// from the call was false. Likewise you should check if the item is under sourcesafe control before calling the other +// operations, else you'll get an error box if you try and check out an item not in the SS database, rather than it +// quietly ignoring the request if you make it part of you fopen() handler. Hokay? +// +// Note that because I don't have any flag documentation I don't allow multiple checkouts (because I don't know how to...) +// but you should do one of the function calls below to check that no-one else has the item checked out before attempting +// to check it out yourself, otherwise you'll just get an error box saying that he checkout failed, which is far less +// helpful. +// +// Likewise, if you do a check-in where nothing's changed, it won't show up in the history list because I don't know how +// to set the flag to say always-show-new-checkin-even-no-differences. +// +bool SS_FunctionsAvailable ( void ); +bool SS_SetupOk ( void ); // similar to above, but doesn't care if functions are user-disabled (note that all other functions DO care, so this is only useful in rare places) +bool SS_IsUnderSourceControl( LPCSTR psPathedFilename ); // call this before deciding whether or not to call any others +bool SS_Add ( LPCSTR psPathedFilename ); +bool SS_CheckIn ( LPCSTR psPathedFilename ); +bool SS_CheckOut ( LPCSTR psPathedFilename ); +bool SS_UndoCheckOut ( LPCSTR psPathedFilename ); +bool SS_IsCheckedOut ( LPCSTR psPathedFilename ); +bool SS_IsCheckedOutByMe ( LPCSTR psPathedFilename ); +bool SS_ListVersions ( LPCSTR psPathedFilename, CString &strOutput ); // do whatever you want with the string +bool SS_ListCheckOuts ( LPCSTR psPathedFilename, CString &strOutput, int &iCount ); +// +void SS_Startup_OnceOnly (void); +void SS_Shutdown_OnceOnly (void); +// +void SS_LoadFromRegistry(void); +void SS_SaveToRegistry(void); + +#endif // #ifndef SOURCSAFE_H + + +//////////////// eof //////////////// + diff --git a/utils/Radiant/sourcesafesettings.cpp b/utils/Radiant/sourcesafesettings.cpp new file mode 100644 index 0000000..1c81191 --- /dev/null +++ b/utils/Radiant/sourcesafesettings.cpp @@ -0,0 +1,97 @@ +// SourceSafeSettings.cpp : implementation file +// + +#include "stdafx.h" +#include "radiant.h" +#include "SourceSafeSettings.h" +#include "SourceSafe.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CSourceSafeSettings dialog + +CSourceSafeSettings::CSourceSafeSettings(CString* pcsINI, CString* pcsProject, BOOL *pbEnabled, CString* pcsBehavEd, CWnd* pParent /*=NULL*/) + : CDialog(CSourceSafeSettings::IDD, pParent) +{ + //{{AFX_DATA_INIT(CSourceSafeSettings) + m_bUseSS = FALSE; + m_sINIPath = _T(""); + m_sProjectPath = _T(""); + m_sBehavEdPath = _T(""); + //}}AFX_DATA_INIT + + m_bUseSS = *pbEnabled; + m_sINIPath = *pcsINI; + m_sProjectPath = *pcsProject; + m_sBehavEdPath = *pcsBehavEd; + + m_pcstrINIPath = pcsINI; + m_pcstrProjectPath = pcsProject; + m_pcstrBehavEdPath = pcsBehavEd; + m_pboolUseSS = pbEnabled; +} + + +void CSourceSafeSettings::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CSourceSafeSettings) + DDX_Check(pDX, IDC_CHECK1, m_bUseSS); + DDX_Text(pDX, IDC_EDIT_SS_INIPATH, m_sINIPath); + DDX_Text(pDX, IDC_EDIT_SS_PROJECTPATH, m_sProjectPath); + DDX_Text(pDX, IDC_EDIT_SS_BEHAVEDPATH, m_sBehavEdPath); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CSourceSafeSettings, CDialog) + //{{AFX_MSG_MAP(CSourceSafeSettings) + ON_BN_CLICKED(IDC_BUTTON_DEFAULTALL, OnButtonDefaultall) + ON_BN_CLICKED(IDC_BUTTON_DEFAULTALL2, OnButtonDefaultall2) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CSourceSafeSettings message handlers + +void CSourceSafeSettings::OnButtonDefaultall() +{ + m_bUseSS = true; + m_sINIPath = sEF1_SS_INI; + m_sProjectPath = sEF1_SS_PROJECT; + m_sBehavEdPath = sEF1_BEHAVED_PATH; + + UpdateData(DATA_TO_DIALOG); +} + +void CSourceSafeSettings::OnButtonDefaultall2() +{ + m_bUseSS = true; + m_sINIPath = sCHC_SS_INI; + m_sProjectPath = sCHC_SS_PROJECT; + m_sBehavEdPath = sCHC_BEHAVED_PATH; + + UpdateData(DATA_TO_DIALOG); +} + + +void CSourceSafeSettings::OnOK() +{ + UpdateData(DIALOG_TO_DATA); + + *m_pcstrINIPath = m_sINIPath; + *m_pcstrProjectPath = m_sProjectPath; + *m_pcstrBehavEdPath = m_sBehavEdPath; + *m_pboolUseSS = m_bUseSS; + + SS_Shutdown_OnceOnly(); + + CDialog::OnOK(); +} + + diff --git a/utils/Radiant/sourcesafesettings.h b/utils/Radiant/sourcesafesettings.h new file mode 100644 index 0000000..1dc4984 --- /dev/null +++ b/utils/Radiant/sourcesafesettings.h @@ -0,0 +1,56 @@ +#if !defined(AFX_SOURCESAFESETTINGS_H__DB2B9121_EAE1_11D3_8A5B_00500424438B__INCLUDED_) +#define AFX_SOURCESAFESETTINGS_H__DB2B9121_EAE1_11D3_8A5B_00500424438B__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// SourceSafeSettings.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CSourceSafeSettings dialog + +class CSourceSafeSettings : public CDialog +{ +// Construction +public: + CSourceSafeSettings(CString* pcsINI, CString* pcsProject, BOOL *pbEnabled, CString* pcsBehavEd, CWnd* pParent = NULL); + +// Dialog Data + //{{AFX_DATA(CSourceSafeSettings) + enum { IDD = IDD_SOURCESAFE }; + BOOL m_bUseSS; + CString m_sINIPath; + CString m_sProjectPath; + CString m_sBehavEdPath; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CSourceSafeSettings) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + CString* m_pcstrINIPath; + CString* m_pcstrProjectPath; + CString* m_pcstrBehavEdPath; + BOOL* m_pboolUseSS; + + // Generated message map functions + //{{AFX_MSG(CSourceSafeSettings) + afx_msg void OnButtonDefaultall(); + virtual void OnOK(); + afx_msg void OnButtonDefaultall2(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_SOURCESAFESETTINGS_H__DB2B9121_EAE1_11D3_8A5B_00500424438B__INCLUDED_) diff --git a/utils/Radiant/ssauterr.h b/utils/Radiant/ssauterr.h new file mode 100644 index 0000000..7324500 --- /dev/null +++ b/utils/Radiant/ssauterr.h @@ -0,0 +1,145 @@ +// Filename:- ssauterr.h + +#ifndef SSAUTERR_H +#define SSAUTERR_H + +//***************************************************************************** +// ssauterr.h +// +// +// Copyright (c) 1995 by Microsoft Corporation, All Rights Reserved +//***************************************************************************** + +#define MAKEHR(iStat) MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, (USHORT) (iStat)) + +#define ESS_CORRUPT MAKEHR(-10600) // File %s may be corrupt +#define ESS_DT_BADDATESTR MAKEHR(-10159) // Invalid date string: "%s" +#define ESS_DT_INVALID MAKEHR(-10161) // Invalid time or date string +#define ESS_NOMORE_HANDLES MAKEHR(-10164) // Too many file handles open. +#define ESS_FILE_ACCESSDENIED MAKEHR(-10165) // Access to file "%s" denied +#define ESS_FILE_BADDRIVE MAKEHR(-10166) // Invalid drive: %s +#define ESS_FILE_BADHANDLE MAKEHR(-10167) // Invalid handle. +#define ESS_FILE_BADNAME MAKEHR(-10168) // Invalid filename: "%s" +#define ESS_FILE_BADPARAM MAKEHR(-10170) // Invalid access code (bad parameter) +#define ESS_FILE_BADPATH MAKEHR(-10171) // Invalid DOS path: %s +#define ESS_FILE_CURRENTDIR MAKEHR(-10172) // Folder %s is in use +#define ESS_FILE_DISKFULL MAKEHR(-10173) // Disk full +#define ESS_FILE_EXISTS MAKEHR(-10175) // File "%s" already exists +#define ESS_FILE_LOCKED MAKEHR(-10176) // File "%s" is locked +#define ESS_FILE_NOTFOUND MAKEHR(-10178) // File "%s" not found +#define ESS_FILE_READ MAKEHR(-10180) // Error reading from file +#define ESS_FILE_SHARE MAKEHR(-10181) // File %s is already open +#define ESS_FILE_TOOMANY MAKEHR(-10182) // Too many file handles open +#define ESS_FILE_VOLNOTSAME MAKEHR(-10183) // Cannot rename to another volume +#define ESS_FILE_WRITE MAKEHR(-10184) // Error writing to file +#define ESS_INI_BADBOOL MAKEHR(-10200) // Initialization variable "%s" must be set to "Yes" or "No" +#define ESS_INI_BADLINE MAKEHR(-10201) // Invalid syntax on line %d of file %s +#define ESS_INI_BADNUMBER MAKEHR(-10202) // Initialization variable ""%s"" set to invalid number +#define ESS_INI_BADPATH MAKEHR(-10203) // Initialization variable ""%s"" set to invalid path +#define ESS_INI_BADVALUE MAKEHR(-10205) // Initialization variable ""%s"" set to invalid value +#define ESS_INI_NOSUCHVAR MAKEHR(-10206) // Cannot find initialization variable "%s" +#define ESS_INI_NUMRANGE MAKEHR(-10207) // Initialization variable "%s" must be between %d and %d +#define ESS_INI_TOO_MANY_ENV MAKEHR(-10208) // Too many SS.INI environment strings +#define ESS_LOCK_TIMEOUT MAKEHR(-10266) // Timeout locking file: %s +#define ESS_MEM_NOMEMORY MAKEHR(-10270) // Out of memory +#define ESS_NO_TWEAK_CHKDOUT MAKEHR(-10625) // You cannot modify the properties of a file that is checked out. +#define ESS_NOMERGE_BIN_NODELTA MAKEHR(-10279) // You cannot perform a merge on a binary file, or a file that stores latest version only. +#define ESS_NOMULTI_BINARY MAKEHR(-10280) // Cannot check out %s. It is binary and is already checked out. +#define ESS_NOMULTI_NODELTA MAKEHR(-10281) // %s stores only the latest version and is already checked out. +#define ESS_OS_NOT_EXE MAKEHR(-10285) // Error executing: %s +#define ESS_SS_ADDPRJASSOCFILE MAKEHR(-10626) // %s is a SourceSafe configuration file and cannot be added. +#define ESS_SS_ADMIN_LOCKOUT MAKEHR(-10456) // The SourceSafe database has been locked by the Administrator. +#define ESS_SS_BADRENAME MAKEHR(-10402) // Unable to rename %s to %s. +#define ESS_SS_CANT_FIND_SSINI MAKEHR(-10403) // Cannot find SS.INI file for user %s +#define ESS_SS_CHECKED_OUT MAKEHR(-10405) // File %s is currently checked out by %s +#define ESS_SS_CHECKED_OUT_YOU MAKEHR(-10406) // You currently have file %s checked out +#define ESS_SS_CHECKOUT_OLD MAKEHR(-10408) // Cannot check out an old version of a file +#define ESS_SS_CHKOUT_USER MAKEHR(-10413) // File %s is currently checked out by %s +#define ESS_SS_CONFLICTS MAKEHR(-10415) // An automatic merge has occurred and there are conflicts.\nEdit %s to resolve them. +#define ESS_SS_DEL_ROOT MAKEHR(-10418) // Cannot delete the root project +#define ESS_SS_DEL_SHARED MAKEHR(-10419) // A deleted link to %s already exists +#define ESS_SS_FILE_NOTFOUND MAKEHR(-10421) // File ""%s"" not found +#define ESS_SS_HISTOPEN MAKEHR(-10404) // A history operation is already in progress +#define ESS_SS_INSUFRIGHTS MAKEHR(-10423) // You do not have access rights to %s +#define ESS_SS_LATERCHKEDOUT MAKEHR(-10426) // A more recent version is checked out +#define ESS_SS_LOCALRW MAKEHR(-10427) // A writable copy of %s already exists +#define ESS_SS_MOVE_CHANGENAME MAKEHR(-10428) // Move does not change the name of a project +#define ESS_SS_MOVE_NOPARENT MAKEHR(-10429) // Project %s does not exist +#define ESS_SS_MOVE_ROOT MAKEHR(-10430) // Cannot move the root project +#define ESS_SS_MUST_USE_VERS MAKEHR(-10431) // Cannot roll back to the most recent version of %s +#define ESS_SS_NOCOMMANCESTOR MAKEHR(-10432) // Files have no common ancestor +#define ESS_SS_NOCONFLICTS2 MAKEHR(-10434) // %s has been merged with no conflicts. +#define ESS_SS_NODOLLAR MAKEHR(-10435) // File %s is invalid. Files may not begin with $. +#define ESS_SS_NOT_CHKEDOUT MAKEHR(-10436) // File %s is not checked out +#define ESS_SS_NOT_SHARED MAKEHR(-10437) // File %s is not shared by any other projects +#define ESS_SS_NOTSEPARATED MAKEHR(-10438) // Files are not branched +#define ESS_SS_OPEN_LOGGIN MAKEHR(-10457) // Unable to open user login file %s. +#define ESS_SS_PATHTOOLONG MAKEHR(-10439) // Path %s too long +#define ESS_SS_RENAME_MOVE MAKEHR(-10442) // Rename does not move an item to another project +#define ESS_SS_RENAME_ROOT MAKEHR(-10443) // Cannot Rename the root project +#define ESS_SS_ROLLBACK_NOTOLD MAKEHR(-10447) // Cannot Rollback to the most recent version of %s +#define ESS_SS_SHARE_ANCESTOR MAKEHR(-10449) // A project cannot be shared under a descendant. +#define ESS_SS_SHARED MAKEHR(-10450) // File %s is already shared by this project +#define ESS_SSPEC_SYNTAX MAKEHR(-10515) // Invalid SourceSafe syntax: "%s" +#define ESS_UM_BAD_CHAR MAKEHR(-10550) // Bad username syntax: "%s" +#define ESS_UM_BAD_PASSWORD MAKEHR(-10551) // Invalid password +#define ESS_UM_BADVERSION MAKEHR(-10552) // Incompatible database version +#define ESS_UM_DEL_ADMIN MAKEHR(-10553) // Cannot delete the Admin user +#define ESS_UM_PERM_DENIED MAKEHR(-10554) // Permission denied +#define ESS_UM_RENAME_ADMIN MAKEHR(-10555) // Can not rename the Admin user +#define ESS_UM_TOO_LONG MAKEHR(-10556) // Username too long +#define ESS_UM_USER_EXISTS MAKEHR(-10557) // User "%s" already exists +#define ESS_UM_USER_NOT_FOUND MAKEHR(-10558) // User "%s" not found +#define ESS_URL_BADPATH MAKEHR(-10192) // The URL for project %s was not set properly. +#define ESS_VS_CHECKED_OUT MAKEHR(-10601) // File %s checked out +#define ESS_VS_CHILD_NOT_FOUND MAKEHR(-10602) // Subproject or file not found +#define ESS_VS_COLLISION MAKEHR(-10603) // Collision accessing database, please try again. +#define ESS_VS_EXCLUSIVE_CHECKED_OUT MAKEHR(-10614) // File %s is exclusively checked out. +#define ESS_VS_ITEMEXISTS MAKEHR(-10604) // An item with the name %s already exists +#define ESS_VS_LONGNAME MAKEHR(-10605) // %s is an invalid %s name +#define ESS_VS_MOVE_CYCLE MAKEHR(-10606) // You can not move a project under itself +#define ESS_VS_NO_DELTA MAKEHR(-10607) // File %s does not retain old versions of itself +#define ESS_VS_NOT_CHECKED_OUT MAKEHR(-10608) // File %s cannot be checked into this project +#define ESS_VS_NOT_FOUND MAKEHR(-10609) // File or project not found +#define ESS_VS_PARENT_NOT_FOUND MAKEHR(-10610) // Parent not found +#define ESS_VS_VERS_NOT_FOUND MAKEHR(-10615) // Version not found +#define ESS_VS_WANT_FILE MAKEHR(-10616) // This command only works on files. +#define ESS_VS_WANT_PRJ MAKEHR(-10617) // This command only works on projects. +#define ESS_URL_BUFOVERFLOW MAKEHR(-10194) // A link in %s was ignored because it was longer than SourceSafe can understand +#define ESS_URL_CANTCHECKHTML MAKEHR(-10193) // An error occurred while trying to check hyperlinks for %s +#define ESS_SS_ADDINFAILED MAKEHR(-10440) // Error loading SourceSafe add-in: %s +#define ESS_CANCEL MAKEHR(-32766) +#define ESS_LOADSTRING_FAILED MAKEHR(-10999) // Error loading resource string + +// SourceSafe questions answered affirmatively. +// +// A deleted copy of this %s file already exists in this project.\nDo you want to recover the existing file? +// Folder %s not found, create? +// Have any conflicts in %s been properly resolved? +// File %s is currently checked out by %s.\nProceed anyway? +// File %s was checked out to folder %s.\nProceed in %s? +// File %s is checked out to project %s, and you are in %s.\nProceed anyway? +// File %s is currently checked out by %s. Delete anyway? +// You currently have file %s checked out. Delete anyway? +// An item named %s was already deleted from this project.\nPurge the old item and delete this one now? +// This version of %s already has a label: overwrite? +// The label %s is already used. Remove the old label? +// %s has been merged with no conflicts.\nCheck in now? +// Redo the automatic merge? +// Delete local file: %s? +// %s is already checked out, continue? +// File %s has been destroyed, and cannot be rebuilt.\nContinue anyway? +// Project $%s has been destroyed, and cannot be rebuilt.\nContinue anyway? +// $%s was moved out of this project, and cannot be rebuilt.\nContinue anyway? +// %s has changed. Undo check out and lose changes? +// +// SourceSafe questions answered in the negative. +// +// A deleted file of the same name already exists in this SourceSafe project.\nDo you want to recover the deleted file instead of adding your local %s? +// %s is writable, replace? +// %s is checked out, replace? + + +#endif // #ifndef SSAUTERR_H + +////////////////////////////// eof //////////////////////////////// diff --git a/utils/Radiant/ssauto.h b/utils/Radiant/ssauto.h new file mode 100644 index 0000000..7645f3f --- /dev/null +++ b/utils/Radiant/ssauto.h @@ -0,0 +1,608 @@ +// Filename:- ssauto.h +// + +/* This header file machine-generated by mktyplib.exe */ +/* Interface to type library: SourceSafeTypeLib */ + +#ifndef _SourceSafeTypeLib_H_ +#define _SourceSafeTypeLib_H_ + +DEFINE_GUID_THAT_WORKS(LIBID_SourceSafeTypeLib,0x783CD4E0L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F); +#ifndef BEGIN_INTERFACE +#define BEGIN_INTERFACE +#endif + +typedef enum _VSSFlags { + VSSFLAG_USERRONO = 1, + VSSFLAG_USERROYES = 2, + VSSFLAG_TIMENOW = 4, + VSSFLAG_TIMEMOD = 8, + VSSFLAG_TIMEUPD = 12, + VSSFLAG_EOLCR = 16, + VSSFLAG_EOLLF = 32, + VSSFLAG_EOLCRLF = 48, + VSSFLAG_REPASK = 64, + VSSFLAG_REPREPLACE = 128, + VSSFLAG_REPSKIP = 192, + VSSFLAG_REPMERGE = 256, + VSSFLAG_CMPFULL = 512, + VSSFLAG_CMPTIME = 1024, + VSSFLAG_CMPCHKSUM = 1536, + VSSFLAG_CMPFAIL = 2048, + VSSFLAG_RECURSNO = 4096, + VSSFLAG_RECURSYES = 8192, + VSSFLAG_FORCEDIRNO = 16384, + VSSFLAG_FORCEDIRYES = 32768, + VSSFLAG_KEEPNO = 65536, + VSSFLAG_KEEPYES = 131072, + VSSFLAG_DELNO = 262144, + VSSFLAG_DELYES = 524288, + VSSFLAG_DELNOREPLACE = 786432, + VSSFLAG_BINTEST = 1048576, + VSSFLAG_BINBINARY = 2097152, + VSSFLAG_BINTEXT = 3145728, + VSSFLAG_DELTAYES = 4194304, + VSSFLAG_DELTANO = 8388608, + VSSFLAG_UPDASK = 16777216, + VSSFLAG_UPDUPDATE = 33554432, + VSSFLAG_UPDUNCH = 50331648, + VSSFLAG_GETYES = 67108864, + VSSFLAG_GETNO = 134217728, + VSSFLAG_CHKEXCLUSIVEYES = 268435456, + VSSFLAG_CHKEXCLUSIVENO = 536870912, + VSSFLAG_HISTIGNOREFILES = 1073741824 +} VSSFlags; + +typedef enum _VSSFileStatus { + VSSFILE_NOTCHECKEDOUT = 0, + VSSFILE_CHECKEDOUT = 1, + VSSFILE_CHECKEDOUT_ME = 2 +} VSSFileStatus; + +typedef enum _VSSItemType { + VSSITEM_PROJECT = 0, + VSSITEM_FILE = 1 +} VSSItemType; + +interface IVSSItems; + +interface IVSSVersions; + +interface IVSSVersion; + +interface IVSSCheckouts; + +interface IVSSCheckout; + +DEFINE_GUID_THAT_WORKS(IID_IVSSItem,0x783CD4E1L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F); + +/* Definition of interface: IVSSItem */ +#undef INTERFACE +#define INTERFACE IVSSItem + +DECLARE_INTERFACE_(IVSSItem, IDispatch) +{ +BEGIN_INTERFACE +#ifndef NO_BASEINTERFACE_FUNCS + + /* IUnknown methods */ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + /* IDispatch methods */ + STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE; + + STDMETHOD(GetTypeInfo)( + THIS_ + UINT itinfo, + LCID lcid, + ITypeInfo FAR* FAR* pptinfo) PURE; + + STDMETHOD(GetIDsOfNames)( + THIS_ + REFIID riid, + OLECHAR FAR* FAR* rgszNames, + UINT cNames, + LCID lcid, + DISPID FAR* rgdispid) PURE; + + STDMETHOD(Invoke)( + THIS_ + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS FAR* pdispparams, + VARIANT FAR* pvarResult, + EXCEPINFO FAR* pexcepinfo, + UINT FAR* puArgErr) PURE; +#endif + + /* IVSSItem methods */ + STDMETHOD(get_Spec)(THIS_ BSTR FAR* pSpec) PURE; + STDMETHOD(get_Binary)(THIS_ VARIANT_BOOL FAR* pbBinary) PURE; + STDMETHOD(put_Binary)(THIS_ VARIANT_BOOL bBinary) PURE; + STDMETHOD(get_Deleted)(THIS_ VARIANT_BOOL FAR* pbDeleted) PURE; + STDMETHOD(put_Deleted)(THIS_ VARIANT_BOOL bDeleted) PURE; + STDMETHOD(get_Type)(THIS_ int FAR* piType) PURE; + STDMETHOD(get_LocalSpec)(THIS_ BSTR FAR* pLocal) PURE; + STDMETHOD(put_LocalSpec)(THIS_ BSTR Local) PURE; + STDMETHOD(get_Name)(THIS_ BSTR FAR* pName) PURE; + STDMETHOD(put_Name)(THIS_ BSTR Name) PURE; + STDMETHOD(get_Parent)(THIS_ IVSSItem FAR* FAR* ppIParent) PURE; + STDMETHOD(get_VersionNumber)(THIS_ long FAR* piVersion) PURE; + STDMETHOD(get_Items)(THIS_ VARIANT_BOOL IncludeDeleted, IVSSItems FAR* FAR* ppIItems) PURE; + STDMETHOD(Get)(THIS_ BSTR FAR* Local, long iFlags) PURE; + STDMETHOD(Checkout)(THIS_ BSTR Comment, BSTR Local, long iFlags) PURE; + STDMETHOD(Checkin)(THIS_ BSTR Comment, BSTR Local, long iFlags) PURE; + STDMETHOD(UndoCheckout)(THIS_ BSTR Local, long iFlags) PURE; + STDMETHOD(get_IsCheckedOut)(THIS_ long FAR* piStatus) PURE; + STDMETHOD(get_Checkouts)(THIS_ IVSSCheckouts FAR* FAR* ppICheckouts) PURE; + STDMETHOD(get_IsDifferent)(THIS_ BSTR Local, VARIANT_BOOL FAR* pbDifferent) PURE; + STDMETHOD(Add)(THIS_ BSTR Local, BSTR Comment, long iFlags, IVSSItem FAR* FAR* ppIItem) PURE; + STDMETHOD(NewSubproject)(THIS_ BSTR Name, BSTR Comment, IVSSItem FAR* FAR* ppIItem) PURE; + STDMETHOD(Share)(THIS_ IVSSItem FAR* pIItem, BSTR Comment, long iFlags) PURE; + STDMETHOD(Destroy)(THIS) PURE; + STDMETHOD(Move)(THIS_ IVSSItem FAR* pINewParent) PURE; + STDMETHOD(Label)(THIS_ BSTR Label, BSTR Comment) PURE; + STDMETHOD(get_Versions)(THIS_ long iFlags, IVSSVersions FAR* FAR* pIVersions) PURE; + STDMETHOD(get_Version)(THIS_ VARIANT Version, IVSSItem FAR* FAR* ppIItem) PURE; +}; + +DEFINE_GUID_THAT_WORKS(IID_IVSSVersions,0x783CD4E7L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F); + +/* Definition of interface: IVSSVersions */ +#undef INTERFACE +#define INTERFACE IVSSVersions + +DECLARE_INTERFACE_(IVSSVersions, IDispatch) +{ +BEGIN_INTERFACE +#ifndef NO_BASEINTERFACE_FUNCS + + /* IUnknown methods */ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + /* IDispatch methods */ + STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE; + + STDMETHOD(GetTypeInfo)( + THIS_ + UINT itinfo, + LCID lcid, + ITypeInfo FAR* FAR* pptinfo) PURE; + + STDMETHOD(GetIDsOfNames)( + THIS_ + REFIID riid, + OLECHAR FAR* FAR* rgszNames, + UINT cNames, + LCID lcid, + DISPID FAR* rgdispid) PURE; + + STDMETHOD(Invoke)( + THIS_ + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS FAR* pdispparams, + VARIANT FAR* pvarResult, + EXCEPINFO FAR* pexcepinfo, + UINT FAR* puArgErr) PURE; +#endif + + /* IVSSVersions methods */ + STDMETHOD(_NewEnum)(THIS_ IUnknown * FAR* ppIEnum) PURE; +}; + +//DEFINE_GUID_THAT_WORKS(IID_IVSSVersion,0x783CD4E8L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F);// original (and wrong) MS version, this equates as "IID_IVSSVersionOld", so fails during history enumeration -ste +DEFINE_GUID_THAT_WORKS(IID_IVSSVersion,0x2A0DE0E9L,0x2E9F,0x11d0,0x92,0x36,0x00,0xAA,0x00,0xA1,0xEB,0x95); // made from line below, from aauto.odl +// uuid(2A0DE0E9- 2E9F- 11d0- 92 36- 00 AA 00 A1 EB 95), // + +/* Definition of interface: IVSSVersion */ +#undef INTERFACE +#define INTERFACE IVSSVersion + +DECLARE_INTERFACE_(IVSSVersion, IDispatch) +{ +BEGIN_INTERFACE +#ifndef NO_BASEINTERFACE_FUNCS + + /* IUnknown methods */ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + /* IDispatch methods */ + STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE; + + STDMETHOD(GetTypeInfo)( + THIS_ + UINT itinfo, + LCID lcid, + ITypeInfo FAR* FAR* pptinfo) PURE; + + STDMETHOD(GetIDsOfNames)( + THIS_ + REFIID riid, + OLECHAR FAR* FAR* rgszNames, + UINT cNames, + LCID lcid, + DISPID FAR* rgdispid) PURE; + + STDMETHOD(Invoke)( + THIS_ + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS FAR* pdispparams, + VARIANT FAR* pvarResult, + EXCEPINFO FAR* pexcepinfo, + UINT FAR* puArgErr) PURE; +#endif + + /* IVSSVersion methods */ + STDMETHOD(get_Username)(THIS_ BSTR FAR* pUsername) PURE; + STDMETHOD(get_VersionNumber)(THIS_ long FAR* piVersion) PURE; + STDMETHOD(get_Action)(THIS_ BSTR FAR* pAction) PURE; + STDMETHOD(get_Date)(THIS_ DATE FAR* pDate) PURE; + STDMETHOD(get_Comment)(THIS_ BSTR FAR* pComment) PURE; + STDMETHOD(get_Label)(THIS_ BSTR FAR* pLabel) PURE; + STDMETHOD(get_VSSItem)(THIS_ IVSSItem FAR* FAR* ppIItem) PURE; +}; + +DEFINE_GUID_THAT_WORKS(IID_IVSSItems,0x783CD4E5L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F); + +/* Definition of interface: IVSSItems */ +#undef INTERFACE +#define INTERFACE IVSSItems + +DECLARE_INTERFACE_(IVSSItems, IDispatch) +{ +BEGIN_INTERFACE +#ifndef NO_BASEINTERFACE_FUNCS + + /* IUnknown methods */ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + /* IDispatch methods */ + STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE; + + STDMETHOD(GetTypeInfo)( + THIS_ + UINT itinfo, + LCID lcid, + ITypeInfo FAR* FAR* pptinfo) PURE; + + STDMETHOD(GetIDsOfNames)( + THIS_ + REFIID riid, + OLECHAR FAR* FAR* rgszNames, + UINT cNames, + LCID lcid, + DISPID FAR* rgdispid) PURE; + + STDMETHOD(Invoke)( + THIS_ + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS FAR* pdispparams, + VARIANT FAR* pvarResult, + EXCEPINFO FAR* pexcepinfo, + UINT FAR* puArgErr) PURE; +#endif + + /* IVSSItems methods */ + STDMETHOD(get_Count)(THIS_ long FAR* piCount) PURE; + STDMETHOD(get_Item)(THIS_ VARIANT sItem, IVSSItem FAR* FAR* ppIItem) PURE; + STDMETHOD(_NewEnum)(THIS_ IUnknown * FAR* ppIEnum) PURE; +}; + +DEFINE_GUID_THAT_WORKS(IID_IVSSCheckouts,0x8903A770L,0xF55F,0x11CF,0x92,0x27,0x00,0xAA,0x00,0xA1,0xEB,0x95); + +/* Definition of interface: IVSSCheckouts */ +#undef INTERFACE +#define INTERFACE IVSSCheckouts + +DECLARE_INTERFACE_(IVSSCheckouts, IDispatch) +{ +BEGIN_INTERFACE +#ifndef NO_BASEINTERFACE_FUNCS + + /* IUnknown methods */ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + /* IDispatch methods */ + STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE; + + STDMETHOD(GetTypeInfo)( + THIS_ + UINT itinfo, + LCID lcid, + ITypeInfo FAR* FAR* pptinfo) PURE; + + STDMETHOD(GetIDsOfNames)( + THIS_ + REFIID riid, + OLECHAR FAR* FAR* rgszNames, + UINT cNames, + LCID lcid, + DISPID FAR* rgdispid) PURE; + + STDMETHOD(Invoke)( + THIS_ + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS FAR* pdispparams, + VARIANT FAR* pvarResult, + EXCEPINFO FAR* pexcepinfo, + UINT FAR* puArgErr) PURE; +#endif + + /* IVSSCheckouts methods */ + STDMETHOD(get_Count)(THIS_ long FAR* piCount) PURE; + STDMETHOD(get_Item)(THIS_ VARIANT sItem, IVSSCheckout FAR* FAR* ppICheckout) PURE; + STDMETHOD(_NewEnum)(THIS_ IUnknown * FAR* ppIEnum) PURE; +}; + +DEFINE_GUID_THAT_WORKS(IID_IVSSCheckout,0x783CD4E6L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F); + +/* Definition of interface: IVSSCheckout */ +#undef INTERFACE +#define INTERFACE IVSSCheckout + +DECLARE_INTERFACE_(IVSSCheckout, IDispatch) +{ +BEGIN_INTERFACE +#ifndef NO_BASEINTERFACE_FUNCS + + /* IUnknown methods */ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + /* IDispatch methods */ + STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE; + + STDMETHOD(GetTypeInfo)( + THIS_ + UINT itinfo, + LCID lcid, + ITypeInfo FAR* FAR* pptinfo) PURE; + + STDMETHOD(GetIDsOfNames)( + THIS_ + REFIID riid, + OLECHAR FAR* FAR* rgszNames, + UINT cNames, + LCID lcid, + DISPID FAR* rgdispid) PURE; + + STDMETHOD(Invoke)( + THIS_ + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS FAR* pdispparams, + VARIANT FAR* pvarResult, + EXCEPINFO FAR* pexcepinfo, + UINT FAR* puArgErr) PURE; +#endif + + /* IVSSCheckout methods */ + STDMETHOD(get_Username)(THIS_ BSTR FAR* pUsername) PURE; + STDMETHOD(get_Date)(THIS_ DATE FAR* pDate) PURE; + STDMETHOD(get_LocalSpec)(THIS_ BSTR FAR* pLocal) PURE; + STDMETHOD(get_Machine)(THIS_ BSTR FAR* pMachine) PURE; + STDMETHOD(get_Project)(THIS_ BSTR FAR* pProject) PURE; + STDMETHOD(get_Comment)(THIS_ BSTR FAR* pComment) PURE; + STDMETHOD(get_VersionNumber)(THIS_ long FAR* piVersion) PURE; +}; + +DEFINE_GUID_THAT_WORKS(IID_IVSSDatabase,0x783CD4E2L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F); + +/* Definition of interface: IVSSDatabase */ +#undef INTERFACE +#define INTERFACE IVSSDatabase + +DECLARE_INTERFACE_(IVSSDatabase, IDispatch) +{ +BEGIN_INTERFACE +#ifndef NO_BASEINTERFACE_FUNCS + + /* IUnknown methods */ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + /* IDispatch methods */ + STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE; + + STDMETHOD(GetTypeInfo)( + THIS_ + UINT itinfo, + LCID lcid, + ITypeInfo FAR* FAR* pptinfo) PURE; + + STDMETHOD(GetIDsOfNames)( + THIS_ + REFIID riid, + OLECHAR FAR* FAR* rgszNames, + UINT cNames, + LCID lcid, + DISPID FAR* rgdispid) PURE; + + STDMETHOD(Invoke)( + THIS_ + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS FAR* pdispparams, + VARIANT FAR* pvarResult, + EXCEPINFO FAR* pexcepinfo, + UINT FAR* puArgErr) PURE; +#endif + + /* IVSSDatabase methods */ + STDMETHOD(Open)(THIS_ BSTR SrcSafeIni, BSTR Username, BSTR Password) PURE; + STDMETHOD(get_SrcSafeIni)(THIS_ BSTR FAR* pSrcSafeIni) PURE; + STDMETHOD(get_DatabaseName)(THIS_ BSTR FAR* pDatabaseName) PURE; + STDMETHOD(get_UserName)(THIS_ BSTR FAR* pUsername) PURE; + STDMETHOD(get_CurrentProject)(THIS_ BSTR FAR* pPrj) PURE; + STDMETHOD(put_CurrentProject)(THIS_ BSTR Prj) PURE; + STDMETHOD(get_VSSItem)(THIS_ BSTR Spec, VARIANT_BOOL Deleted, IVSSItem FAR* FAR* ppIVSSItem) PURE; +}; + +DEFINE_GUID_THAT_WORKS(CLSID_VSSItem,0x783CD4E3L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F); + +#ifdef __cplusplus +class VSSItem; +#endif + +DEFINE_GUID_THAT_WORKS(CLSID_VSSVersion,0x783CD4ECL,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F); + +#ifdef __cplusplus +class VSSVersion; +#endif + +DEFINE_GUID_THAT_WORKS(CLSID_VSSCheckout,0x2A0DE0E0L,0x2E9F,0x11D0,0x92,0x36,0x00,0xAA,0x00,0xA1,0xEB,0x95); + +#ifdef __cplusplus +class VSSCheckout; +#endif + +DEFINE_GUID_THAT_WORKS(CLSID_VSSDatabase,0x783CD4E4L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F); + +#ifdef __cplusplus +class VSSDatabase; +#endif + +DEFINE_GUID_THAT_WORKS(IID_IVSSEvents,0x783CD4E9L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F); + +/* Definition of interface: IVSSEvents */ +#undef INTERFACE +#define INTERFACE IVSSEvents + +DECLARE_INTERFACE_(IVSSEvents, IUnknown) +{ +BEGIN_INTERFACE +#ifndef NO_BASEINTERFACE_FUNCS + + /* IUnknown methods */ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; +#endif + + /* IVSSEvents methods */ + STDMETHOD(BeforeAdd)(THIS_ IVSSItem FAR* pIPrj, BSTR Local, VARIANT_BOOL FAR* pbContinue) PURE; + STDMETHOD(AfterAdd)(THIS_ IVSSItem FAR* pIItem, BSTR Local) PURE; + STDMETHOD(BeforeCheckout)(THIS_ IVSSItem FAR* pIItem, BSTR Local, VARIANT_BOOL FAR* pbContinue) PURE; + STDMETHOD(AfterCheckout)(THIS_ IVSSItem FAR* pIItem, BSTR Local) PURE; + STDMETHOD(BeforeCheckin)(THIS_ IVSSItem FAR* pIItem, BSTR Local, VARIANT_BOOL FAR* pbContinue) PURE; + STDMETHOD(AfterCheckin)(THIS_ IVSSItem FAR* pIItem, BSTR Local) PURE; + STDMETHOD(BeforeUndoCheckout)(THIS_ IVSSItem FAR* pIItem, BSTR Local, VARIANT_BOOL FAR* pbContinue) PURE; + STDMETHOD(AfterUndoCheckout)(THIS_ IVSSItem FAR* pIItem, BSTR Local) PURE; + STDMETHOD(BeforeRename)(THIS_ IVSSItem FAR* pIItem, BSTR NewName, VARIANT_BOOL FAR* pbContinue) PURE; + STDMETHOD(AfterRename)(THIS_ IVSSItem FAR* pIItem, BSTR OldName) PURE; + STDMETHOD(BeforeBranch)(THIS_ IVSSItem FAR* pIItem, VARIANT_BOOL FAR* pbContinue) PURE; + STDMETHOD(AfterBranch)(THIS_ IVSSItem FAR* pIItem) PURE; + STDMETHOD(BeforeEvent)(THIS_ long iEvent, IVSSItem FAR* pIItem, BSTR Str, VARIANT var, VARIANT_BOOL FAR* pbContinue) PURE; + STDMETHOD(AfterEvent)(THIS_ long iEvent, IVSSItem FAR* pIItem, BSTR Str, VARIANT var) PURE; +}; + +DEFINE_GUID_THAT_WORKS(IID_IVSS,0x783CD4EBL,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F); + +/* Definition of interface: IVSS */ +#undef INTERFACE +#define INTERFACE IVSS + +DECLARE_INTERFACE_(IVSS, IDispatch) +{ +BEGIN_INTERFACE +#ifndef NO_BASEINTERFACE_FUNCS + + /* IUnknown methods */ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + /* IDispatch methods */ + STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE; + + STDMETHOD(GetTypeInfo)( + THIS_ + UINT itinfo, + LCID lcid, + ITypeInfo FAR* FAR* pptinfo) PURE; + + STDMETHOD(GetIDsOfNames)( + THIS_ + REFIID riid, + OLECHAR FAR* FAR* rgszNames, + UINT cNames, + LCID lcid, + DISPID FAR* rgdispid) PURE; + + STDMETHOD(Invoke)( + THIS_ + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS FAR* pdispparams, + VARIANT FAR* pvarResult, + EXCEPINFO FAR* pexcepinfo, + UINT FAR* puArgErr) PURE; +#endif + + /* IVSS methods */ + STDMETHOD(get_VSSDatabase)(THIS_ IVSSDatabase FAR* FAR* ppIVSSDatabase) PURE; +}; + +DEFINE_GUID_THAT_WORKS(IID_IVSSEventHandler,0x783CD4EAL,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F); + +/* Definition of interface: IVSSEventHandler */ +#undef INTERFACE +#define INTERFACE IVSSEventHandler + +DECLARE_INTERFACE_(IVSSEventHandler, IUnknown) +{ +BEGIN_INTERFACE +#ifndef NO_BASEINTERFACE_FUNCS + + /* IUnknown methods */ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; +#endif + + /* IVSSEventHandler methods */ + STDMETHOD(Init)(THIS_ IVSS FAR* pIVSS) PURE; +}; + +DEFINE_GUID_THAT_WORKS(CLSID_VSSApp,0x2A0DE0E1L,0x2E9F,0x11D0,0x92,0x36,0x00,0xAA,0x00,0xA1,0xEB,0x95); + +#ifdef __cplusplus +class VSSApp; +#endif + +#endif //#ifndef _SourceSafeTypeLib_H_ + + +///////////////// eof ////////////////// diff --git a/utils/Radiant/stdafx.cpp b/utils/Radiant/stdafx.cpp new file mode 100644 index 0000000..8611ee9 --- /dev/null +++ b/utils/Radiant/stdafx.cpp @@ -0,0 +1,6 @@ +// stdafx.cpp : source file that includes just the standard includes +// Radiant.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + diff --git a/utils/Radiant/stdafx.h b/utils/Radiant/stdafx.h new file mode 100644 index 0000000..ad9364f --- /dev/null +++ b/utils/Radiant/stdafx.h @@ -0,0 +1,137 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#if !defined(AFX_STDAFX_H__330BBF08_731C_11D1_B539_00AA00A410FC__INCLUDED_) +#define AFX_STDAFX_H__330BBF08_731C_11D1_B539_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +#pragma warning(disable: 4305) // For: double to float +#pragma warning(disable: 4800) // For: performance warning on bool conversions + +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers + +#include // MFC core and standard components +#include // MFC extensions +#include // MFC OLE automation classes +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // MFC support for Windows Common Controls +#endif // _AFX_NO_AFXCMN_SUPPORT + + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__330BBF08_731C_11D1_B539_00AA00A410FC__INCLUDED_) + +#include "MainFrm.h" +#include "PrefsDlg.h" +#include "FindTextureDlg.h" + + +extern CMainFrame* g_pParentWnd; +extern CString g_strAppPath; +extern CPrefsDlg& g_PrefsDlg; +extern CFindTextureDlg& g_dlgFind; + +// layout styles +#define QR_SPLIT 0 +#define QR_QE4 1 +#define QR_4WAY 2 +#define QR_SPLITZ 3 + + +// externs +extern void AddSlash(CString&); +extern void DLLBuildDone(); +extern void CleanUpEntities(); +extern void MFCCreate(HINSTANCE); +extern BOOL Init3Dfx(); +extern void FindReplace(CString& strContents, const char* pTag, const char* pValue); +extern void CheckBspProcess(); +extern void QE_CountBrushesAndUpdateStatusBar(); +extern void QE_CheckAutoSave(); +extern qtexture_t *notexture; +extern qtexture_t *current_texture; +extern BOOL SaveWindowState(HWND hWnd, const char *pszName); +extern void InitPakFile(); +extern BOOL DoMru(HWND, WORD); +extern void RunBsp (char *command); +extern void Map_Snapshot(); +extern void WXY_Print(); +extern void AddProp( void ); +extern qboolean DoColor(int iIndex); +extern entity_t *edit_entity; +extern int inspector_mode; +extern bool g_bRotateMode; +extern bool g_bClipMode; +extern bool g_bScaleMode; +extern int g_nScaleHow; +extern bool g_bPathMode; +extern bool ByeByeSurfaceDialog(); +extern void RunScript(char* pBuffer); +extern bool ExtractPath_and_Filename(const char* pPath, CString& strPath, CString& strFilename); +extern HINSTANCE g_hOpenGL32; +extern void Select_Scale(float x, float y, float z); +extern void Select_RotateTexture(int amt); +extern void Select_ScaleTexture(int x, int y); +extern void Select_ShiftTexture(int x, int y); +extern int FindReplaceTextures(const char* pFind, const char* pReplace, bool bSelected, bool bForce, bool bReScale, bool bFindOnlyNoReplace, bool bInhibitCameraUpdate = false, bool bCalledDuringLoopAndNotFirstTime = false); +extern void DoProjectSettings(); +extern qboolean region_active; +extern void Brush_Print(brush_t* b); +extern void Texture_ShowDirectory (char* pPath, bool Linked = false); +extern void Map_ImportFile (char *filename); +extern void Map_SaveSelected(char* pFilename); +extern void UpdateSurfaceDialog(); +extern bool g_bNewFace; +extern void Select_GetTrueMid (vec3_t mid); +extern bool g_bSwitch; +extern brush_t g_brFrontSplits; +extern brush_t g_brBackSplits; +extern CClipPoint g_Clip1; +extern CClipPoint g_Clip2; +extern brush_t* g_pSplitList; +extern CClipPoint g_PathPoints[256]; +extern void AcquirePath(int nCount, PFNPathCallback* pFunc); +extern bool g_bScreenUpdates; +extern SCommandInfo g_Commands[]; +extern int g_nCommandCount; +extern SKeyInfo g_Keys[]; +extern int g_nKeyCount; +extern int inspector_mode; +extern char *bsp_commands[256]; +extern void RunScriptByName(char*, bool); +extern void DoNewColor(int* i1, int* i2, int* i3); +extern void UpdateSurfaceDialog(); +extern bool g_bSnapToGrid; +extern void CSG_SplitBrushByFace (brush_t *in, face_t *f, brush_t **front, brush_t **back); +extern void HandlePopup(CWnd* pWindow, unsigned int uId); +extern z_t z; +extern void Select_Scale(float x, float y, float z); +extern CString g_strProject; +extern void TextureAxisFromPlane(plane_t *pln, vec3_t xv, vec3_t yv); +extern void VectorRotate (vec3_t va, vec3_t vb, vec3_t out); +extern void VectorRotate (vec3_t vIn, vec3_t vRotation, vec3_t vOrigin, vec3_t out); +extern qboolean QE_SaveProject (const char* pProjectFile); +extern void NewBSP(char* pCommandLine, HWND); +extern void NewVIS(char* pCommandLine, HWND); +extern void NewRAD(char* pCommandLine, HWND); +extern void RunTools(char* pCommandLine, HWND, const char*); +extern void Clamp(float& f, int nClamp); +extern void MemFile_fprintf(CMemFile* pMemFile, const char* pText, ...); +extern void SaveWindowPlacement(HWND hwnd, const char* pName); +extern bool LoadWindowPlacement(HWND hwnd, const char* pName); +extern qboolean ConfirmModified (void); + +// stuff to help with MFC, since I can never remember which way round the bool is for CDialog::UpdateData()... +// +#define DATA_TO_DIALOG FALSE +#define DIALOG_TO_DATA TRUE + +#define sEF1_BEHAVED_PATH "k:\\util\\runbehavedlocal.bat" //"q:\\bin_nt\\BehavEd.exe" +#define sCHC_BEHAVED_PATH "k:\\util\\runbehavedlocal.bat" //"w:\\bin\\BehavEd.exe" diff --git a/utils/Radiant/surfacedlg.cpp b/utils/Radiant/surfacedlg.cpp new file mode 100644 index 0000000..6da3982 --- /dev/null +++ b/utils/Radiant/surfacedlg.cpp @@ -0,0 +1,1006 @@ +// SurfaceDlg.cpp : implementation file +// + +#include "stdafx.h" +#include "Radiant.h" +#include "SurfaceDlg.h" +#include "PrefsDlg.h" +#include "mainfrm.h" +#include "TextureLayout.h" +#ifdef SOF +#include "materials.h" +#endif +#include "oddbits.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CSurfaceDlg dialog + +CSurfaceDlg g_dlgSurface; + + +CSurfaceDlg::CSurfaceDlg(CWnd* pParent /*=NULL*/) + : CDialog(CSurfaceDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CSurfaceDlg) + m_nWidth = 1; + m_nHeight = 1; + //}}AFX_DATA_INIT +} + + +void CSurfaceDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CSurfaceDlg) + DDX_Control(pDX, IDC_SPIN_HEIGHT, m_wndHeight); + DDX_Control(pDX, IDC_SPIN_WIDTH, m_wndWidth); + DDX_Control(pDX, IDC_SPIN_VSHIFT, m_wndVShift); + DDX_Control(pDX, IDC_SPIN_VSCALE, m_wndVScale); + DDX_Control(pDX, IDC_SPIN_ROTATE, m_wndRotate); + DDX_Control(pDX, IDC_SPIN_HSHIFT, m_wndHShift); + DDX_Control(pDX, IDC_SPIN_HSCALE, m_wndHScale); + DDX_Text(pDX, IDC_EDIT_WIDTH, m_nWidth); + DDV_MinMaxInt(pDX, m_nWidth, 1, 5000); + DDX_Text(pDX, IDC_EDIT_HEIGHT, m_nHeight); + DDV_MinMaxInt(pDX, m_nHeight, 1, 5000); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CSurfaceDlg, CDialog) + //{{AFX_MSG_MAP(CSurfaceDlg) + ON_WM_HSCROLL() + ON_WM_KEYDOWN() + ON_WM_VSCROLL() + ON_BN_CLICKED(IDAPPLY, OnApply) + ON_WM_CLOSE() + ON_WM_DESTROY() + ON_BN_CLICKED(ID_BTN_CANCEL, OnBtnCancel) + ON_BN_CLICKED(IDC_BTN_COLOR, OnBtnColor) + ON_WM_CTLCOLOR() + ON_WM_CREATE() + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_HSHIFT, OnDeltaPosSpin) + ON_BN_CLICKED(IDC_BTN_PATCHDETAILS, OnBtnPatchdetails) + ON_BN_CLICKED(IDC_BTN_PATCHNATURAL, OnBtnPatchnatural) + ON_BN_CLICKED(IDC_BTN_PATCHRESET, OnBtnPatchreset) + ON_BN_CLICKED(IDC_BTN_PATCHFIT, OnBtnPatchfit) + ON_BN_CLICKED(IDC_BTN_AXIAL, OnBtnAxial) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_HSCALE, OnDeltaPosSpin) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_ROTATE, OnDeltaPosSpin) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_VSCALE, OnDeltaPosSpin) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_VSHIFT, OnDeltaPosSpin) + ON_BN_CLICKED(IDC_BTN_FACEFIT, OnBtnFacefit) + ON_BN_CLICKED(IDC_BTN_FACERESET, OnBtnFaceReset) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CSurfaceDlg message handlers + + +/* +=================================================== + + SURFACE INSPECTOR + +=================================================== +*/ + +texdef_t g_old_texdef; +texdef_t g_patch_texdef; +HWND g_surfwin = NULL; +qboolean g_changed_surface; + +int g_checkboxes[64] = { + IDC_CHECK1, IDC_CHECK2, IDC_CHECK3, IDC_CHECK4, + IDC_CHECK5, IDC_CHECK6, IDC_CHECK7, IDC_CHECK8, + IDC_CHECK9, IDC_CHECK10, IDC_CHECK11, IDC_CHECK12, + IDC_CHECK13, IDC_CHECK14, IDC_CHECK15, IDC_CHECK16, + IDC_CHECK17, IDC_CHECK18, IDC_CHECK19, IDC_CHECK20, + IDC_CHECK21, IDC_CHECK22, IDC_CHECK23, IDC_CHECK24, + IDC_CHECK25, IDC_CHECK26, IDC_CHECK27, IDC_CHECK28, + IDC_CHECK29, IDC_CHECK30, IDC_CHECK31, IDC_CHECK32, + + IDC_CHECK33, IDC_CHECK34, IDC_CHECK35, IDC_CHECK36, + IDC_CHECK37, IDC_CHECK38, IDC_CHECK39, IDC_CHECK40, + IDC_CHECK41, IDC_CHECK42, IDC_CHECK43, IDC_CHECK44, + IDC_CHECK45, IDC_CHECK46, IDC_CHECK47, IDC_CHECK48, + IDC_CHECK49, IDC_CHECK50, IDC_CHECK51, IDC_CHECK52, + IDC_CHECK53, IDC_CHECK54, IDC_CHECK55, IDC_CHECK56, + IDC_CHECK57, IDC_CHECK58, IDC_CHECK59, IDC_CHECK60, + IDC_CHECK61, IDC_CHECK62, IDC_CHECK63, IDC_CHECK64 + }; + +#ifdef SOF +char *psLightmipStrings[]= // don't add to these 4 unless you update all flag AND bits etc. Basically, leave it. -slc +{ + "default", + "low (eg 1/3....1/1)", // this spacing actually matches under final output font + "lower (eg 1/6....1/1)", + "single pixel", + NULL +}; +#endif + +/* +============== +SetTexMods + +Set the fields to the current texdef +=============== +*/ + +bool g_bNewFace = false; +bool g_bNewApplyHandling = false; +bool g_bGatewayhack = false; + +void CSurfaceDlg::SetTexMods() +{ + char sz[128]; + texdef_t *pt; + int i; + + if (!g_surfwin) + return; + + m_bPatchMode = false; + + if (OnlyPatchesSelected()) + { + pt = &g_qeglobals.d_texturewin.texdef; + if (QE_SingleBrush()) + { + strcpy(g_patch_texdef.name, Patch_GetTextureName()); + } + else + { + strcpy(g_patch_texdef.name, pt->name); + } + g_patch_texdef.contents = pt->contents; + g_patch_texdef.flags = pt->flags; + g_patch_texdef.value = pt->value; + pt = &g_patch_texdef; + m_bPatchMode = true; + } + else + { + if (g_bNewFace && selected_face) + pt = &selected_face->texdef; + else + pt = &g_qeglobals.d_texturewin.texdef; + +#ifdef SOF + // lightmip combo box... + // + i = (pt->flags >> 22) &3; + HWND comboBox = GetDlgItem(IDC_TEXTURE_LIGHTMIP)->GetSafeHwnd(); + ::SendMessage(comboBox, CB_RESETCONTENT, 0,0); + for (int j=0; psLightmipStrings[j]; j++) + { + ::SendMessage(comboBox, CB_ADDSTRING, 0, (LPARAM)psLightmipStrings[j]); + if (i == j) + { + ::SendMessage(comboBox, CB_SELECTSTRING, (WPARAM)(-1), (LPARAM)psLightmipStrings[j]); + } + } + + // materials combo box... + // + i = (pt->flags & 0x0FF000000) >> 24; + comboBox = GetDlgItem(IDC_TEXTURE_MATERIAL)->GetSafeHwnd(); + ::SendMessage(comboBox, CB_RESETCONTENT, 0,0); + for (materialtype_t *mat = materialtypes ; mat->name ; mat++) + { + ::SendMessage(comboBox, CB_ADDSTRING, 0, (LPARAM)mat->name); + if (i == mat->value) + { + ::SendMessage(comboBox, CB_SELECTSTRING, (WPARAM)(-1), (LPARAM)mat->name); + } + } +#endif + + } + + SendMessage (WM_SETREDRAW, 0, 0); + + ::SetWindowText(GetDlgItem(IDC_TEXTURE)->GetSafeHwnd(), pt->name); + if (m_bPatchMode) + sprintf(sz, "%4.3f", pt->shift[0]); + else + sprintf(sz, "%d", (int)pt->shift[0]); + ::SetWindowText(GetDlgItem(IDC_HSHIFT)->GetSafeHwnd(), sz); + + if (m_bPatchMode) + sprintf(sz, "%4.3f", pt->shift[1]); + else + sprintf(sz, "%d", (int)pt->shift[1]); + ::SetWindowText(GetDlgItem(IDC_VSHIFT)->GetSafeHwnd(), sz); + + sprintf(sz, m_bPatchMode ? "%4.6f" : "%4.6f", pt->scale[0]); + ::SetWindowText(GetDlgItem(IDC_HSCALE)->GetSafeHwnd(), sz); + + sprintf(sz, m_bPatchMode ? "%4.6f" : "%4.6f", pt->scale[1]); + ::SetWindowText(GetDlgItem(IDC_VSCALE)->GetSafeHwnd(), sz); + + sprintf(sz, "%d", (int)pt->rotate); + ::SetWindowText(GetDlgItem(IDC_ROTATE)->GetSafeHwnd(), sz); + + sprintf(sz, "%d", (int)pt->value); + ::SetWindowText(GetDlgItem(IDC_VALUE)->GetSafeHwnd(), sz); + +#ifdef SOF + sprintf(sz, "%f", pt->lighting[0]); + ::SetWindowText(GetDlgItem(IDC_LIGHT_RED)->GetSafeHwnd(), sz); + + sprintf(sz, "%f", pt->lighting[1]); + ::SetWindowText(GetDlgItem(IDC_LIGHT_GREEN)->GetSafeHwnd(), sz); + + sprintf(sz, "%f", pt->lighting[2]); + ::SetWindowText(GetDlgItem(IDC_LIGHT_BLUE)->GetSafeHwnd(), sz); + + sprintf(sz, "%f", pt->lighting[3]); + ::SetWindowText(GetDlgItem(IDC_LIGHT_ALPHA)->GetSafeHwnd(), sz); +#endif + +#ifdef SOF + for (i=0 ; i<22 ; i++) // top 8 bits are now material, next 2 are lightmip +#else + for (i=0 ; i<32 ; i++) +#endif + ::SendMessage(GetDlgItem(g_checkboxes[i])->GetSafeHwnd(), BM_SETCHECK, !!(pt->flags&(1<GetSafeHwnd(), BM_SETCHECK, !!(pt->contents&(1<GetSafeHwnd(), sz, 127); + pt->shift[0] = atof(sz); + + ::GetWindowText (GetDlgItem(IDC_VSHIFT)->GetSafeHwnd(), sz, 127); + pt->shift[1] = atof(sz); + + ::GetWindowText(GetDlgItem(IDC_HSCALE)->GetSafeHwnd(), sz, 127); + pt->scale[0] = atof(sz); + + ::GetWindowText(GetDlgItem(IDC_VSCALE)->GetSafeHwnd(), sz, 127); + pt->scale[1] = atof(sz); + + ::GetWindowText(GetDlgItem(IDC_ROTATE)->GetSafeHwnd(), sz, 127); + pt->rotate = atof(sz); + + ::GetWindowText(GetDlgItem(IDC_VALUE)->GetSafeHwnd(), sz, 127); + pt->value = atof(sz); + +#ifdef SOF // not 100% sure it'll ever get here in SOF mode - no patches? + ::GetWindowText(GetDlgItem(IDC_LIGHT_RED)->GetSafeHwnd(), sz, 127); + pt->lighting[0] = atof(sz); + + ::GetWindowText(GetDlgItem(IDC_LIGHT_GREEN)->GetSafeHwnd(), sz, 127); + pt->lighting[1] = atof(sz); + + ::GetWindowText(GetDlgItem(IDC_LIGHT_BLUE)->GetSafeHwnd(), sz, 127); + pt->lighting[2] = atof(sz); + + ::GetWindowText(GetDlgItem(IDC_LIGHT_ALPHA)->GetSafeHwnd(), sz, 127); + pt->lighting[3] = atof(sz); +#endif + + + pt->flags = 0; +#ifdef SOF + for (i=0 ; i<22 ; i++) // top 8 bits are now material, next 2 are lightmip +#else + for (i=0 ; i<32 ; i++) +#endif + { + b = ::SendMessage(GetDlgItem(g_checkboxes[i])->GetSafeHwnd(), BM_GETCHECK, 0, 0); + if (b != 1 && b != 0) + continue; + pt->flags |= b<contents = 0; + for (i=0 ; i<32 ; i++) + { + b = ::SendMessage(GetDlgItem(g_checkboxes[32+i])->GetSafeHwnd(), BM_GETCHECK, 0, 0); + if (b != 1 && b != 0) + continue; + pt->contents |= b<texdef; + else + pt = &g_qeglobals.d_texturewin.texdef; + } + + ::GetWindowText (GetDlgItem(IDC_TEXTURE)->GetSafeHwnd(), sz, 127); + sz[127]=0; + CString str(sz); + str.TrimLeft(); + str.TrimRight(); + strcpy(sz,(LPCSTR)str); + + strncpy (pt->name, sz, sizeof(pt->name)-1); + + if (pt->name[0] <= ' ') + { + strcpy (pt->name, "none"); + ::SetWindowText(GetDlgItem(IDC_TEXTURE)->GetSafeHwnd(), pt->name); + } + + ::GetWindowText (GetDlgItem(IDC_HSHIFT)->GetSafeHwnd(), sz, 127); + pt->shift[0] = atof(sz); + + ::GetWindowText (GetDlgItem(IDC_VSHIFT)->GetSafeHwnd(), sz, 127); + pt->shift[1] = atof(sz); + + ::GetWindowText(GetDlgItem(IDC_HSCALE)->GetSafeHwnd(), sz, 127); + pt->scale[0] = atof(sz); + + ::GetWindowText(GetDlgItem(IDC_VSCALE)->GetSafeHwnd(), sz, 127); + pt->scale[1] = atof(sz); + + ::GetWindowText(GetDlgItem(IDC_ROTATE)->GetSafeHwnd(), sz, 127); + pt->rotate = atof(sz); + + ::GetWindowText(GetDlgItem(IDC_VALUE)->GetSafeHwnd(), sz, 127); + pt->value = atof(sz); + + +#ifdef SOF + ::GetWindowText(GetDlgItem(IDC_LIGHT_RED)->GetSafeHwnd(), sz, 127); + pt->lighting[0] = atof(sz); + + ::GetWindowText(GetDlgItem(IDC_LIGHT_GREEN)->GetSafeHwnd(), sz, 127); + pt->lighting[1] = atof(sz); + + ::GetWindowText(GetDlgItem(IDC_LIGHT_BLUE)->GetSafeHwnd(), sz, 127); + pt->lighting[2] = atof(sz); + + ::GetWindowText(GetDlgItem(IDC_LIGHT_ALPHA)->GetSafeHwnd(), sz, 127); + pt->lighting[3] = atof(sz); +#endif + + + pt->flags = 0; +#ifdef SOF + for (i=0 ; i<22 ; i++) // top 8 bits are now material, next 2 are lightmip +#else + for (i=0 ; i<32 ; i++) +#endif + { + b = ::SendMessage(GetDlgItem(g_checkboxes[i])->GetSafeHwnd(), BM_GETCHECK, 0, 0); + if (b != 1 && b != 0) + continue; + pt->flags |= b<contents = 0; + for (i=0 ; i<32 ; i++) + { + b = ::SendMessage(GetDlgItem(g_checkboxes[32+i])->GetSafeHwnd(), BM_GETCHECK, 0, 0); + if (b != 1 && b != 0) + continue; + pt->contents |= b<GetSafeHwnd(); + ::GetWindowText(comboBox, sz, 127); + for (int j=0; psLightmipStrings[j]; j++) + { + if (!strcmp(psLightmipStrings[j], sz)) + { + // assumes SURF_LIGHTMIP is in bits 22,23 (top 8 bits (minus 2)) + pt->flags = (pt->flags & ~(0x03<<22)) | (j << 22); + break; + } + } + + // read bits from MATERIALS combo box... + comboBox = GetDlgItem(IDC_TEXTURE_MATERIAL)->GetSafeHwnd(); + ::GetWindowText(comboBox, sz, 127); + for (materialtype_t *mat=materialtypes ; mat->name ; mat++) + { + if (!strcmp(mat->name, sz)) + { + // assumes SURF_MATERIAL is in top 8 bits + pt->flags = (pt->flags & 0x0FFFFFF) | (mat->value << 24); + break; + } + } +#endif + + + g_changed_surface = true; + + Select_SetTexture(pt); + //if (m_bPatchMode) + //{ + // Patch_SetTextureInfo(pt); + //} + +} + +/* +================= +UpdateSpinners +================= +*/ + +void CSurfaceDlg::UpdateSpinners(bool bUp, int nID) +{ + texdef_t *pt; + texdef_t td; + + if (m_bPatchMode) + { + td.rotate = 0.0; + td.scale[0] = td.scale[1] = 0.0; + td.shift[0] = td.shift[1] = 0.0; + GrabPatchMods(); + + pt = &g_patch_texdef; + td.contents = pt->contents; + td.flags = pt->flags; + td.value = pt->value; + + if (nID == IDC_SPIN_ROTATE) + { + if (bUp) + td.rotate = pt->rotate; + else + td.rotate = -pt->rotate; + } + else if (nID == IDC_SPIN_HSCALE) + { + if (bUp) + td.scale[0] = 1-pt->scale[0]; + else + td.scale[0] = 1+pt->scale[0]; + } + else if (nID == IDC_SPIN_VSCALE) + { + if (bUp) + td.scale[1] = 1-pt->scale[1]; + else + td.scale[1] = 1+pt->scale[1]; + } + + else if (nID == IDC_SPIN_HSHIFT) + { + if (bUp) + td.shift[0] = pt->shift[0]; + else + td.shift[0] = -pt->shift[0]; + } + else if (nID == IDC_SPIN_VSHIFT) + { + if (bUp) + td.shift[1] = pt->shift[1]; + else + td.shift[1] = -pt->shift[1]; + } + pt = &g_qeglobals.d_texturewin.texdef; + Patch_SetTextureInfo(&td); + } + else + { + GetTexMods (); + if (g_bNewFace && selected_face) + pt = &selected_face->texdef; + else + pt = &g_qeglobals.d_texturewin.texdef; + + if (nID == IDC_SPIN_ROTATE) + { + if (bUp) + pt->rotate += 45; + else + pt->rotate -= 45; + + if (pt->rotate < 0) + pt->rotate += 360; + + if (pt->rotate >= 360) + pt->rotate -= 360; + } + else if (nID == IDC_SPIN_HSCALE) + { + if (bUp) + pt->scale[0] += 0.1; + else + pt->scale[0] -= 0.1; + } + else if (nID == IDC_SPIN_VSCALE) + { + if (bUp) + pt->scale[1] += 0.1; + else + pt->scale[1] -= 0.1; + } + + else if (nID == IDC_SPIN_HSHIFT) + { + if (bUp) + pt->shift[0] += 8; + else + pt->shift[0] -= 8; + } + else if (nID == IDC_SPIN_VSHIFT) + { + if (bUp) + pt->shift[1] += 8; + else + pt->shift[1] -= 8; + } + } + + SetTexMods(); + g_changed_surface = true; + Select_SetTexture(pt); +} + +void CSurfaceDlg::UpdateSpinners(int nScrollCode, int nPos, CScrollBar* pBar) +{ + texdef_t *pt; + + GetTexMods (); + if (g_bNewFace && selected_face) + pt = &selected_face->texdef; + else + pt = &g_qeglobals.d_texturewin.texdef; + + if ((nScrollCode != SB_LINEUP) && (nScrollCode != SB_LINEDOWN)) + return; + + if (pBar->GetSafeHwnd() == ::GetDlgItem(GetSafeHwnd(), IDC_ROTATEA)) + { + if (nScrollCode == SB_LINEUP) + pt->rotate += 45; + else + pt->rotate -= 45; + + if (pt->rotate < 0) + pt->rotate += 360; + + if (pt->rotate >= 360) + pt->rotate -= 360; + } + + else if (pBar->GetSafeHwnd() == ::GetDlgItem(GetSafeHwnd(), IDC_HSCALEA)) + { + if (nScrollCode == SB_LINEDOWN) + pt->scale[0] -= 0.1; + else + pt->scale[0] += 0.1; + } + + else if (pBar->GetSafeHwnd() == ::GetDlgItem(GetSafeHwnd(), IDC_VSCALEA)) + { + if (nScrollCode == SB_LINEUP) + pt->scale[1] += 0.1; + else + pt->scale[1] -= 0.1; + } + + else if (pBar->GetSafeHwnd() == ::GetDlgItem(GetSafeHwnd(), IDC_HSHIFTA)) + { + if (nScrollCode == SB_LINEDOWN) + pt->shift[0] -= 8; + else + pt->shift[0] += 8; + } + + else if (pBar->GetSafeHwnd() == ::GetDlgItem(GetSafeHwnd(), IDC_VSHIFTA)) + { + if (nScrollCode == SB_LINEUP) + pt->shift[1] += 8; + else + pt->shift[1] -= 8; + } + + SetTexMods(); + g_changed_surface = true; + Select_SetTexture(pt); +} + +void UpdateSurfaceDialog() +{ + if (g_surfwin) + g_dlgSurface.SetTexMods(); + g_pParentWnd->UpdateTextureBar(); +} + + + + +bool ByeByeSurfaceDialog(); + + + +void DoSurface (void) +{ + g_bNewFace = g_PrefsDlg.m_bFace; + g_bNewApplyHandling = g_PrefsDlg.m_bNewApplyHandling; + g_bGatewayhack = g_PrefsDlg.m_bGatewayHack; + // save current state for cancel + g_old_texdef = g_qeglobals.d_texturewin.texdef; + g_changed_surface = false; + + if (g_surfwin == NULL && g_dlgSurface.GetSafeHwnd() == NULL) + { + g_patch_texdef.scale[0] = 0.05; + g_patch_texdef.scale[1] = 0.05; + g_patch_texdef.shift[0] = 0.05; + g_patch_texdef.shift[1] = 0.05; + g_patch_texdef.rotate = 45; + + g_dlgSurface.Create(IDD_SURFACE); + CRect rct; + LONG lSize = sizeof(rct); + if (LoadRegistryInfo("Radiant::SurfaceWindow", &rct, &lSize)) + g_dlgSurface.SetWindowPos(NULL, rct.left, rct.top, 0,0, SWP_NOSIZE | SWP_SHOWWINDOW); + + Sys_UpdateWindows(W_ALL); + } + else + { + g_surfwin = g_dlgSurface.GetSafeHwnd(); + g_dlgSurface.SetTexMods (); + g_dlgSurface.ShowWindow(SW_SHOW); + } +} + +bool ByeByeSurfaceDialog() +{ + if (g_surfwin) + { + if (g_bGatewayhack) + PostMessage(g_surfwin, WM_COMMAND, IDAPPLY, 0); + else + PostMessage(g_surfwin, WM_COMMAND, IDCANCEL, 0); + return true; + } + else return false; +} + +BOOL CSurfaceDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + g_surfwin = GetSafeHwnd(); + SetTexMods (); + +// enable whichever one of these you think looks best... +// +//#define LOSE_MENU_ITEM(item) GetDlgItem(item)->EnableWindow(false); // grey the item +#define LOSE_MENU_ITEM(item) GetDlgItem(item)->DestroyWindow(); // remove it + + +#ifdef QUAKE3 + GetDlgItem(IDC_CHECK32)->SetWindowText("Curve"); + GetDlgItem(IDC_CHECK63)->SetWindowText("Inverted"); + RECT Rect,Rect2; + GetWindowRect(&Rect); + GetDlgItem(IDC_STATIC_PATCHTEXTURING)->GetWindowRect(&Rect2); + SetWindowPos(&wndTopMost, Rect.left, Rect.top, 2*(Rect2.left-Rect.left) + (Rect2.right-Rect2.left), (Rect.bottom-Rect.top), SWP_NOCOPYBITS|SWP_NOMOVE); +#endif + + m_wndHScale.SetRange(0, 1000); + m_wndVScale.SetRange(0, 1000); + m_wndHShift.SetRange(0, 1000); + m_wndVShift.SetRange(0, 1000); + m_wndRotate.SetRange(0, 1000); + m_wndWidth.SetRange(1, 5000); + m_wndHeight.SetRange(1, 5000); + + LPVOID lpv = g_pParentWnd->GetPlugInMgr().GetSurfaceFlags(); + if (lpv != NULL) + { + int i = 0; + char* p = reinterpret_cast(lpv); + char* pBuff = new char[strlen(p)+1]; + strcpy(pBuff, p); + char* pToken = strtok(pBuff, ";\0"); + while (pToken != NULL) + { + GetDlgItem(g_checkboxes[i++])->SetWindowText(pToken); + pToken = strtok(NULL, ";\0"); + } + } + + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void CSurfaceDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) +{ + CDialog::OnHScroll(nSBCode, nPos, pScrollBar); + UpdateSpinners(nSBCode, nPos, pScrollBar); + Sys_UpdateWindows(W_CAMERA); +} + +void CSurfaceDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + + CDialog::OnKeyDown(nChar, nRepCnt, nFlags); +} + +void CSurfaceDlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) +{ + CDialog::OnVScroll(nSBCode, nPos, pScrollBar); + UpdateSpinners(nSBCode, nPos, pScrollBar); + Sys_UpdateWindows(W_CAMERA); +} + +void CSurfaceDlg::OnApply() +{ + GetTexMods (); + Sys_UpdateWindows(W_CAMERA); + if (g_bNewApplyHandling) + OnOK(); +} + +void CSurfaceDlg::OnOK() +{ + GetTexMods(); + g_surfwin = NULL; + CDialog::OnOK(); + Sys_UpdateWindows(W_ALL); +} + +void CSurfaceDlg::OnClose() +{ + g_surfwin = NULL; + CDialog::OnClose(); +} + +void CSurfaceDlg::OnCancel() +{ + if (g_bGatewayhack) + OnOK(); + else + OnBtnCancel(); +} + +void CSurfaceDlg::OnDestroy() +{ + if (GetSafeHwnd()) + { + CRect rct; + GetWindowRect(rct); + SaveRegistryInfo("Radiant::SurfaceWindow", &rct, sizeof(rct)); + } + CDialog::OnDestroy(); + g_surfwin = NULL; + Sys_UpdateWindows(W_ALL); +} + +void CSurfaceDlg::OnBtnCancel() +{ + g_qeglobals.d_texturewin.texdef = g_old_texdef; + if (g_changed_surface) + Select_SetTexture(&g_qeglobals.d_texturewin.texdef); + g_surfwin = NULL; + DestroyWindow(); +} + +void CSurfaceDlg::OnBtnColor() +{ +} + +HBRUSH CSurfaceDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) +{ + HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); + return hbr; +} + +int CSurfaceDlg::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CDialog::OnCreate(lpCreateStruct) == -1) + return -1; + + return 0; +} + +BOOL CSurfaceDlg::PreCreateWindow(CREATESTRUCT& cs) +{ + // TODO: Add your specialized code here and/or call the base class + + return CDialog::PreCreateWindow(cs); +} + + +void CSurfaceDlg::OnDeltaPosSpin(NMHDR* pNMHDR, LRESULT* pResult) +{ + NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR; + UpdateSpinners((pNMUpDown->iDelta > 0), pNMUpDown->hdr.idFrom); + *pResult = 0; +} + + + +void CSurfaceDlg::OnBtnPatchdetails() +{ + Patch_NaturalizeSelected(true); + Sys_UpdateWindows(W_ALL); +} + +void CSurfaceDlg::OnBtnPatchnatural() +{ + Patch_NaturalizeSelected(); + Sys_UpdateWindows(W_ALL); +} + +void CSurfaceDlg::OnBtnPatchreset() +{ + CTextureLayout dlg; + if (dlg.DoModal() == IDOK) + { + Patch_ResetTexturing(dlg.m_fX, dlg.m_fY); + } + Sys_UpdateWindows(W_ALL); +} + +void CSurfaceDlg::OnBtnPatchfit() +{ + Patch_FitTexturing(); + Sys_UpdateWindows(W_ALL); +} + +void CSurfaceDlg::OnBtnAxial() +{ + Select_SetTexture (&g_qeglobals.d_texturewin.texdef, true); + g_changed_surface = true; + SetTexMods(); + Sys_UpdateWindows(W_ALL); +} + +void FaceFit_Actual(int iWidth, int iHeight) +{ +/* if (1)//selected_face == NULL) + { + brush_t *b; + for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + { + selected_face_brush = b; + for (face_t* pFace = b->brush_faces; pFace; pFace = pFace->next) + { + selected_face = pFace; + Select_FitTexture(m_nHeight, m_nWidth); + SetTexMods(); + } + } +// selected_face = NULL; + } +// else +*/ + Select_FitTexture(iHeight, iWidth); +} + + +// this is fucking crap. I can't use the ID version because their's doesn't work properly either, it seems to, but it +// has some bad side effects, so for now... +// +void CSurfaceDlg::OnBtnFacefit() +{ + UpdateData(TRUE); + + if (!selected_face) + { + ErrorBox("This only works when a face is selected, not a whole brush"); + return; + } + + FaceFit_Actual(m_nWidth, m_nHeight); + SetTexMods(); + + g_changed_surface = true; + Sys_UpdateWindows(W_ALL); +} + +// so we can call the functionality of the FIT button from a hotkey without the dialog being up... +// +void FaceFit(void) +{ + FaceFit_Actual(g_dlgSurface.m_nWidth, g_dlgSurface.m_nHeight); + g_dlgSurface.SetTexMods(); + Sys_UpdateWindows(W_ALL); +} + + +void CSurfaceDlg::OnBtnFaceReset() +{ + UpdateData(TRUE); + + if (!selected_face) + { + ErrorBox("This only works when a face is selected, not a whole brush"); + return; + } + +/* if (selected_face == NULL) + { + brush_t *b; + for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + { + selected_face_brush = b; + for (face_t* pFace = b->brush_faces; pFace; pFace = pFace->next) + { + selected_face = pFace; + + pFace->texdef.shift[0] = + pFace->texdef.shift[1] = 0.0f; + pFace->texdef.rotate = 0.0f; + pFace->texdef.scale[0] = + pFace->texdef.scale[1] = (g_PrefsDlg.m_bHiColorTextures) ? 0.5 : 1; + + SetTexMods(); + } + } +// selected_face = NULL; + } +// else +*/ + if (selected_face) + { + face_t* pFace = selected_face; + + pFace->texdef.shift[0] = + pFace->texdef.shift[1] = 0.0f; + pFace->texdef.rotate = 0.0f; + pFace->texdef.scale[0] = + pFace->texdef.scale[1] = (g_PrefsDlg.m_bHiColorTextures) ? fTEXTURE_SCALE : 1; + + SetTexMods(); + } + + g_changed_surface = true; + OnApply(); +} + diff --git a/utils/Radiant/surfacedlg.h b/utils/Radiant/surfacedlg.h new file mode 100644 index 0000000..577db92 --- /dev/null +++ b/utils/Radiant/surfacedlg.h @@ -0,0 +1,83 @@ +#if !defined(AFX_SURFACEDLG_H__D84E0C22_9EEA_11D1_B570_00AA00A410FC__INCLUDED_) +#define AFX_SURFACEDLG_H__D84E0C22_9EEA_11D1_B570_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// SurfaceDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CSurfaceDlg dialog + +class CSurfaceDlg : public CDialog +{ + bool m_bPatchMode; +// Construction +public: + CSurfaceDlg(CWnd* pParent = NULL); // standard constructor + void SetTexMods(); + void GetTexMods(); + void GrabPatchMods(); + +// Dialog Data + //{{AFX_DATA(CSurfaceDlg) + enum { IDD = IDD_SURFACE }; + CSpinButtonCtrl m_wndHeight; + CSpinButtonCtrl m_wndWidth; + CSpinButtonCtrl m_wndVShift; + CSpinButtonCtrl m_wndVScale; + CSpinButtonCtrl m_wndRotate; + CSpinButtonCtrl m_wndHShift; + CSpinButtonCtrl m_wndHScale; + int m_nWidth; + int m_nHeight; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CSurfaceDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + //}}AFX_VIRTUAL + +// Implementation +protected: + + void UpdateSpinners(int nScrollCode, int nPos, CScrollBar* pBar); + void UpdateSpinners(bool bUp, int nID); + // Generated message map functions + //{{AFX_MSG(CSurfaceDlg) + virtual BOOL OnInitDialog(); + afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + afx_msg void OnApply(); + virtual void OnOK(); + afx_msg void OnClose(); + virtual void OnCancel(); + afx_msg void OnDestroy(); + afx_msg void OnBtnCancel(); + afx_msg void OnBtnColor(); + afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor); + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnDeltaPosSpin(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnBtnPatchdetails(); + afx_msg void OnBtnPatchnatural(); + afx_msg void OnBtnPatchreset(); + afx_msg void OnBtnPatchfit(); + afx_msg void OnBtnAxial(); + afx_msg void OnBtnFacefit(); + afx_msg void OnBtnFaceReset(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +void FaceFit(void); + +#endif // !defined(AFX_SURFACEDLG_H__D84E0C22_9EEA_11D1_B570_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/texedit.cpp b/utils/Radiant/texedit.cpp new file mode 100644 index 0000000..80afd61 --- /dev/null +++ b/utils/Radiant/texedit.cpp @@ -0,0 +1,64 @@ +// TexEdit.cpp : implementation file +// + +#include "stdafx.h" +#include "Radiant.h" +#include "TexEdit.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CTexEdit + +CTexEdit::CTexEdit() +{ + m_pTexWnd = NULL; +} + +CTexEdit::~CTexEdit() +{ +} + + +BEGIN_MESSAGE_MAP(CTexEdit, CEdit) + //{{AFX_MSG_MAP(CTexEdit) + ON_WM_CTLCOLOR_REFLECT() + ON_CONTROL_REFLECT(EN_CHANGE, OnChange) + ON_WM_CREATE() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CTexEdit message handlers + +HBRUSH CTexEdit::CtlColor(CDC* pDC, UINT nCtlColor) +{ + if (nCtlColor == CTLCOLOR_EDIT) + { + pDC->SetBkColor(RGB(192,192,192)); + return (HBRUSH)GetStockObject(LTGRAY_BRUSH); + } + return NULL; +} + +void CTexEdit::OnChange() +{ + CString str; + GetWindowText(str); + if (m_pTexWnd) + m_pTexWnd->UpdateFilter(str); +} + +int CTexEdit::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CEdit::OnCreate(lpCreateStruct) == -1) + return -1; + + m_Font.CreatePointFont(100, "Arial"); + SetFont(&m_Font, FALSE); + return 0; +} diff --git a/utils/Radiant/texedit.h b/utils/Radiant/texedit.h new file mode 100644 index 0000000..b92fdfd --- /dev/null +++ b/utils/Radiant/texedit.h @@ -0,0 +1,55 @@ +#if !defined(AFX_TEXEDIT_H__913F6812_99CD_11D1_B568_00AA00A410FC__INCLUDED_) +#define AFX_TEXEDIT_H__913F6812_99CD_11D1_B568_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// TexEdit.h : header file +// +///////////////////////////////////////////////////////////////////////////// +// CTexEdit window + +class CTexWnd; + +class CTexEdit : public CEdit +{ +// Construction +public: + CTexEdit(); + +// Attributes +public: + +protected: + CTexWnd* m_pTexWnd; + CFont m_Font; +// Operations +public: + void SetTexWnd(CTexWnd* pTex) {m_pTexWnd = pTex;}; + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CTexEdit) + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CTexEdit(); + + // Generated message map functions +protected: + //{{AFX_MSG(CTexEdit) + afx_msg HBRUSH CtlColor(CDC* pDC, UINT nCtlColor); + afx_msg void OnChange(); + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_TEXEDIT_H__913F6812_99CD_11D1_B568_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/texturebar.cpp b/utils/Radiant/texturebar.cpp new file mode 100644 index 0000000..7dfb536 --- /dev/null +++ b/utils/Radiant/texturebar.cpp @@ -0,0 +1,177 @@ +// TextureBar.cpp : implementation file +// + +#include "stdafx.h" +#include "Radiant.h" +#include "TextureBar.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CTextureBar dialog + + +CTextureBar::CTextureBar() + : CDialogBar() +{ + //{{AFX_DATA_INIT(CTextureBar) + m_nHShift = 0; + m_nHScale = 0; + m_nRotate = 0; + m_nVShift = 0; + m_nVScale = 0; + m_nRotateAmt = 45; + //}}AFX_DATA_INIT +} + + +void CTextureBar::DoDataExchange(CDataExchange* pDX) +{ + CDialogBar::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CTextureBar) + DDX_Control(pDX, IDC_SPIN_ROTATE, m_spinRotate); + DDX_Control(pDX, IDC_SPIN_VSCALE, m_spinVScale); + DDX_Control(pDX, IDC_SPIN_VSHIFT, m_spinVShift); + DDX_Control(pDX, IDC_SPIN_HSCALE, m_spinHScale); + DDX_Control(pDX, IDC_SPIN_HSHIFT, m_spinHShift); + DDX_Text(pDX, IDC_HSHIFT, m_nHShift); + DDX_Text(pDX, IDC_HSCALE, m_nHScale); + DDX_Text(pDX, IDC_ROTATE, m_nRotate); + DDX_Text(pDX, IDC_VSHIFT, m_nVShift); + DDX_Text(pDX, IDC_VSCALE, m_nVScale); + DDX_Text(pDX, IDC_EDIT_ROTATEAMT, m_nRotateAmt); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CTextureBar, CDialogBar) + //{{AFX_MSG_MAP(CTextureBar) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_HSHIFT, OnDeltaposSpinHshift) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_VSHIFT, OnDeltaposSpinVshift) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_HSCALE, OnDeltaposSpinHScale) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_VSCALE, OnDeltaposSpinVScale) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_ROTATE, OnDeltaposSpinRotate) + ON_COMMAND(ID_SELECTION_PRINT, OnSelectionPrint) + ON_WM_CREATE() + ON_BN_CLICKED(IDC_BTN_APPLYTEXTURESTUFF, OnBtnApplytexturestuff) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CTextureBar message handlers + +void CTextureBar::OnDeltaposSpinHshift(NMHDR* pNMHDR, LRESULT* pResult) +{ + NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR; + *pResult = 0; + + if (pNMUpDown->iDelta < 0) + Select_ShiftTexture(abs(g_qeglobals.d_savedinfo.m_nTextureTweak), 0); + else + Select_ShiftTexture(-abs(g_qeglobals.d_savedinfo.m_nTextureTweak), 0); + GetSurfaceAttributes(); +} + +void CTextureBar::OnDeltaposSpinVshift(NMHDR* pNMHDR, LRESULT* pResult) +{ + NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR; + // TODO: Add your control notification handler code here + + *pResult = 0; + if (pNMUpDown->iDelta < 0) + Select_ShiftTexture(0, abs(g_qeglobals.d_savedinfo.m_nTextureTweak)); + else + Select_ShiftTexture(0, -abs(g_qeglobals.d_savedinfo.m_nTextureTweak)); + GetSurfaceAttributes(); +} + +void CTextureBar::OnDeltaposSpinHScale(NMHDR* pNMHDR, LRESULT* pResult) +{ + NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR; + // TODO: Add your control notification handler code here + + *pResult = 0; + if (pNMUpDown->iDelta < 0) + Select_ScaleTexture(abs(g_qeglobals.d_savedinfo.m_nTextureTweak),0); + else + Select_ScaleTexture(-abs(g_qeglobals.d_savedinfo.m_nTextureTweak),0); + GetSurfaceAttributes(); +} + +void CTextureBar::OnDeltaposSpinVScale(NMHDR* pNMHDR, LRESULT* pResult) +{ + NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR; + // TODO: Add your control notification handler code here + + *pResult = 0; + if (pNMUpDown->iDelta < 0) + Select_ScaleTexture(0, abs(g_qeglobals.d_savedinfo.m_nTextureTweak)); + else + Select_ScaleTexture(0, -abs(g_qeglobals.d_savedinfo.m_nTextureTweak)); + GetSurfaceAttributes(); +} + +void CTextureBar::OnDeltaposSpinRotate(NMHDR* pNMHDR, LRESULT* pResult) +{ + NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR; + *pResult = 0; + UpdateData(TRUE); + if (pNMUpDown->iDelta < 0) + Select_RotateTexture(abs(m_nRotateAmt)); + else + Select_RotateTexture(-abs(m_nRotateAmt)); + GetSurfaceAttributes(); +} + + +void CTextureBar::OnSelectionPrint() +{ + // TODO: Add your command handler code here + +} + +int CTextureBar::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CDialogBar::OnCreate(lpCreateStruct) == -1) + return -1; + return 0; +} + + +void CTextureBar::OnBtnApplytexturestuff() +{ + SetSurfaceAttributes(); +} + +void CTextureBar::GetSurfaceAttributes() +{ + texdef_t* pt = (selected_face) ? &selected_face->texdef : &g_qeglobals.d_texturewin.texdef; + if (pt) + { + m_nHShift = pt->shift[0]; + m_nVShift = pt->shift[1]; + m_nHScale = pt->scale[0]; + m_nVScale = pt->scale[1]; + m_nRotate = pt->rotate; + UpdateData(FALSE); + } +} + +void CTextureBar::SetSurfaceAttributes() +{ + if (selected_face) + { + texdef_t* pt = &selected_face->texdef; + UpdateData(TRUE); + pt->shift[0] = m_nHShift; + pt->shift[1] = m_nVShift; + pt->scale[0] = m_nHScale; + pt->scale[1] = m_nVScale; + pt->rotate = m_nRotate; + Sys_UpdateWindows(W_CAMERA); + } +} diff --git a/utils/Radiant/texturebar.h b/utils/Radiant/texturebar.h new file mode 100644 index 0000000..11c067f --- /dev/null +++ b/utils/Radiant/texturebar.h @@ -0,0 +1,64 @@ +#if !defined(AFX_TEXTUREBAR_H__86220273_B656_11D1_B59F_00AA00A410FC__INCLUDED_) +#define AFX_TEXTUREBAR_H__86220273_B656_11D1_B59F_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// TextureBar.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CTextureBar dialog + +class CTextureBar : public CDialogBar +{ +// Construction +public: + void GetSurfaceAttributes(); + void SetSurfaceAttributes(); + CTextureBar(); + +// Dialog Data + //{{AFX_DATA(CTextureBar) + enum { IDD = IDD_TEXTUREBAR }; + CSpinButtonCtrl m_spinRotate; + CSpinButtonCtrl m_spinVScale; + CSpinButtonCtrl m_spinVShift; + CSpinButtonCtrl m_spinHScale; + CSpinButtonCtrl m_spinHShift; + int m_nHShift; + int m_nHScale; + int m_nRotate; + int m_nVShift; + int m_nVScale; + int m_nRotateAmt; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CTextureBar) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + // Generated message map functions + //{{AFX_MSG(CTextureBar) + afx_msg void OnDeltaposSpinHshift(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnDeltaposSpinVshift(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnDeltaposSpinHScale(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnDeltaposSpinVScale(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnDeltaposSpinRotate(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnSelectionPrint(); + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnBtnApplytexturestuff(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_TEXTUREBAR_H__86220273_B656_11D1_B59F_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/texturelayout.cpp b/utils/Radiant/texturelayout.cpp new file mode 100644 index 0000000..91d23f8 --- /dev/null +++ b/utils/Radiant/texturelayout.cpp @@ -0,0 +1,59 @@ +// TextureLayout.cpp : implementation file +// + +#include "stdafx.h" +#include "Radiant.h" +#include "TextureLayout.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CTextureLayout dialog + + +CTextureLayout::CTextureLayout(CWnd* pParent /*=NULL*/) + : CDialog(CTextureLayout::IDD, pParent) +{ + //{{AFX_DATA_INIT(CTextureLayout) + m_fX = 4.0f; + m_fY = 4.0f; + //}}AFX_DATA_INIT +} + + +void CTextureLayout::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CTextureLayout) + DDX_Text(pDX, IDC_EDIT_X, m_fX); + DDX_Text(pDX, IDC_EDIT_Y, m_fY); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CTextureLayout, CDialog) + //{{AFX_MSG_MAP(CTextureLayout) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CTextureLayout message handlers + +void CTextureLayout::OnOK() +{ + CDialog::OnOK(); +} + +BOOL CTextureLayout::OnInitDialog() +{ + CDialog::OnInitDialog(); + + // TODO: Add extra initialization here + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/utils/Radiant/texturelayout.h b/utils/Radiant/texturelayout.h new file mode 100644 index 0000000..33d7582 --- /dev/null +++ b/utils/Radiant/texturelayout.h @@ -0,0 +1,48 @@ +#if !defined(AFX_TEXTURELAYOUT_H__5EA4C722_173F_11D2_B024_00AA00A410FC__INCLUDED_) +#define AFX_TEXTURELAYOUT_H__5EA4C722_173F_11D2_B024_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// TextureLayout.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CTextureLayout dialog + +class CTextureLayout : public CDialog +{ +// Construction +public: + CTextureLayout(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CTextureLayout) + enum { IDD = IDD_DIALOG_TEXTURELAYOUT }; + float m_fX; + float m_fY; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CTextureLayout) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CTextureLayout) + virtual void OnOK(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_TEXTURELAYOUT_H__5EA4C722_173F_11D2_B024_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/textures.h b/utils/Radiant/textures.h new file mode 100644 index 0000000..2157f75 --- /dev/null +++ b/utils/Radiant/textures.h @@ -0,0 +1,74 @@ + +//--typedef struct texdef_s +//--{ +//-- char name[32]; +//-- float shift[2]; +//-- float rotate; +//-- float scale[2]; +//-- int contents; +//-- int flags; +//-- int value; +//--} texdef_t; +//-- +//--typedef struct +//--{ +//-- int width, height; +//-- int originy; +//-- texdef_t texdef; +//-- int m_nTotalHeight; +//--} texturewin_t; +//-- +//--#define QER_TRANS 0x00000001 +//--#define QER_NOCARVE 0x00000002 +//-- +//--typedef struct qtexture_s +//--{ +//-- struct qtexture_s *next; +//-- char name[64]; // includes partial directory and extension +//-- int width, height; +//-- int contents; +//-- int flags; +//-- int value; +//-- int texture_number; // gl bind number +//-- +//-- char shadername[1024]; // old shader stuff +//-- qboolean bFromShader; // created from a shader +//-- float fTrans; // amount of transparency +//-- int nShaderFlags; // qer_ shader flags +//-- vec3_t color; // for flat shade mode +//-- qboolean inuse; // true = is present on the level +//--} qtexture_t; +//-- + +// a texturename of the form (0 0 0) will +// create a solid color texture + +void Texture_Init (bool bHardInit = true); +void Texture_Flush (bool bReload = false); +void Texture_ClearInuse (void); +void Texture_ShowInuse (void); +void Texture_ShowDirectory (int menunum, bool bLinked = false); + +typedef enum +{ + eReplace_NO, + eReplace_YES, + eReplace_TIMESTAMP + +} eReplace_t; + +qtexture_t *Texture_ForName (const char *name, bool bForArchitecture = true, const char *psBasePath = NULL, eReplace_t eReplace = eReplace_NO, bool bPlaceHolderLoad = false); + +void Texture_Init (void); +void Texture_SetTexture (texdef_t *texdef, bool bFitScale = false, bool bNoSystemTextureOverwrite = false); + +void Texture_SetMode(int iMenu); // GL_TEXTURE_NEAREST, etc.. +void Texture_ResetPosition(); + +void FreeShaders(); +void LoadShaders(); +void ReloadShaders(); +int Texture_LoadSkin(char *pName, int *pnWidth, int *pnHeight, const char *psBasePath); +void Texture_LoadFromPlugIn(LPVOID vp); +void Texture_StartPos (void); +qtexture_t *Texture_NextPos (int *x, int *y); diff --git a/utils/Radiant/texwnd.cpp b/utils/Radiant/texwnd.cpp new file mode 100644 index 0000000..ed64646 --- /dev/null +++ b/utils/Radiant/texwnd.cpp @@ -0,0 +1,3195 @@ +// TexWnd.cpp : implementation file +// + +#include "stdafx.h" +#include +#include "Radiant.h" +#include "TexWnd.h" +#include "qe3.h" +#include "io.h" +#include "inc.h" +#include "PrefsDlg.h" +#include "shaderinfo.h" +#include "oddbits.h" +#include "..\libs\pakstuff.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#define TYP_MIPTEX 68 +static unsigned tex_palette[256]; + +qtexture_t *notexture = NULL; +qtexture_t *g_pluginTexture = NULL; + +static qboolean nomips = false; + +#define FONT_HEIGHT 10 + +HGLRC s_hglrcTexture = NULL; +HDC s_hdcTexture = NULL; + +//int texture_mode = GL_NEAREST; +//int texture_mode = GL_NEAREST_MIPMAP_NEAREST; +//int texture_mode = GL_NEAREST_MIPMAP_LINEAR; +//int texture_mode = GL_LINEAR; +//int texture_mode = GL_LINEAR_MIPMAP_NEAREST; +int texture_mode = GL_LINEAR_MIPMAP_LINEAR; + +int texture_extension_number = 1; +int g_nCurrentTextureMenuName; + +int g_nTextureOffset = 0; + +// current active texture directory. if empty, show textures in use +char texture_directory[128]; // use if texture_showinuse is false +qboolean texture_showinuse; + +bool g_bFilterEnabled = false; +CString g_strFilter; + +// texture layout functions +qtexture_t *current_texture = NULL; +int current_x, current_y, current_row; + +int texture_nummenus; +#define MAX_TEXTUREDIRS 128 +char texture_menunames[MAX_TEXTUREDIRS][128]; + +qboolean g_dontuse = true; // set to true to load the texture but not flag as used + +void SelectTexture (int mx, int my, bool bShift, bool bFitScale = false, bool bNoSystemTextureOverwrite = false); + +void Texture_MouseDown (int x, int y, int buttons); +void Texture_MouseUp (int x, int y, int buttons); +void Texture_MouseMoved (int x, int y, int buttons); + +CPtrArray g_lstShaders; +CPtrArray g_lstDeferred; + +struct DeferredShaderLoad +{ + CString m_strImage; + CString m_strShader; + CString m_strPath; + + DeferredShaderLoad(const char *pImage, const char *pShader, const char *pPath) + { + m_strImage = pImage; + m_strShader = pShader; + m_strPath = pPath; + m_strPath.MakeLower(); + } + +}; + +void loadDeferredByName(const char *pName) +{ +} + +bool DeferredExists(const char *pName) +{ + for (qtexture_t *q=g_qeglobals.d_qtextures ; q ; q=q->next) + { + if (!strcmp(q->name, pName)) + { + return true; + } + } + return false; + +} + +bool ShaderQTextureExists(const char *pName) +{ + for (qtexture_t *q=g_qeglobals.d_qtextures ; q ; q=q->next) + { + if (!strcmp(q->name, pName)) + { + return true; + } + } + return false; + +} + + + +void LoadDeferred(const char *pPath) +{ + int nSize = g_lstDeferred.GetSize(); + for (int i = 0; i < nSize; i++) + { + DeferredShaderLoad *p = reinterpret_cast(g_lstDeferred.ElementAt(i)); + if (p != NULL) + { + if (p->m_strPath.CompareNoCase(pPath) == 0) + { + if (DeferredExists(p->m_strShader) == false) + { + qtexture_t *q = Texture_ForName(p->m_strImage.GetBuffer(0)); + if (q != NULL) + { + strcpy(q->name, p->m_strShader); + } + } + } + } + } +} + + +CShaderInfo* hasShader(const char *pName) +{ + int nSize = g_lstShaders.GetSize(); + for (int i = 0; i < nSize; i++) + { + CShaderInfo *pInfo = reinterpret_cast(g_lstShaders.ElementAt(i)); + if (pInfo != NULL) + { + if (pInfo->m_strName.CompareNoCase(pName) == 0) + { + return pInfo; + } + } + } + return NULL; +} + + +/* +struct ShaderInfo +{ + ShaderInfo(){ }; + ~ShaderInfo() { }; + + CString m_strName; + CStringList m_strEditorParams; + CStringList m_strSurfaceParams; + +}; +*/ + +// gets active texture extension +// +// FIXME: fix this to be generic from project file +// +int GetTextureExtensionCount() +{ + return 2; +} + +const char* GetTextureExtension(int nIndex) +{ + if ( nIndex == 0) + { + _QERTextureInfo *pInfo = g_pParentWnd->GetPlugInMgr().GetTextureInfo(); + const char *pTex = (pInfo != NULL) ? pInfo->m_TextureExtension : NULL; + return (pTex == NULL) ? (g_PrefsDlg.m_bHiColorTextures == FALSE) ? "wal" : "tga" : pTex; + } + // return jpg for 2nd extension + return "jpg"; +} + + + +/* +================= +CheckTexturePrefix + +Check the texture name for a prefix, if it exists, do nothing +if it does not exist, append it +================= +*/ +void CheckTexturePrefix (char *texname) +{ + char string[128]; + char *temp; + + // if the texture directory prefix is not in the texture name (i.e. imported file), add it + if (!strstr (texname, texture_menunames[g_nCurrentTextureMenuName])) + { + // if the texture name has an asterisk in it, remove it. + if (texname[0] == '*') + { + temp = texname + 1; + strcpy (texname, temp); + } + + sprintf (string, "%s%s", + texture_menunames[g_nCurrentTextureMenuName], + texname); + + temp = _strlwr (string); + strcpy (texname, temp); + } +} + + +//===================================================== + +void SortTextures(void) +{ + qtexture_t *q, *qtemp, *qhead, *qcur, *qprev; + + // standard insertion sort + // Take the first texture from the list and + // add it to our new list + if ( g_qeglobals.d_qtextures == NULL) + return; + + qhead = g_qeglobals.d_qtextures; + q = g_qeglobals.d_qtextures->next; + qhead->next = NULL; + + // while there are still things on the old + // list, keep adding them to the new list + while (q) + { + qtemp = q; + q = q->next; + + qprev = NULL; + qcur = qhead; + + while (qcur) + { + // Insert it here? + if (strcmp(qtemp->name, qcur->name) < 0) + { + qtemp->next = qcur; + if (qprev) + qprev->next = qtemp; + else + qhead = qtemp; + break; + } + + // Move on + + qprev = qcur; + qcur = qcur->next; + + + // is this one at the end? + + if (qcur == NULL) + { + qprev->next = qtemp; + qtemp->next = NULL; + } + } + + + } + + g_qeglobals.d_qtextures = qhead; +} + +//===================================================== + + +/* +============== +Texture_InitPalette +============== +*/ +void Texture_InitPalette (byte *pal) +{ + int r,g,b,v; + int i; + int inf; + byte gammatable[256]; + float gamma; + + gamma = g_qeglobals.d_savedinfo.fGamma; + + if (gamma == 1.0) + { + for (i=0 ; i<256 ; i++) + gammatable[i] = i; + } + else + { + for (i=0 ; i<256 ; i++) + { + inf = 255 * pow ( (i+0.5)/255.5 , gamma ) + 0.5; + if (inf < 0) + inf = 0; + if (inf > 255) + inf = 255; + gammatable[i] = inf; + } + } + + for (i=0 ; i<256 ; i++) + { + r = gammatable[pal[0]]; + g = gammatable[pal[1]]; + b = gammatable[pal[2]]; + pal += 3; + + v = (r<<24) + (g<<16) + (b<<8) + 255; + v = BigLong (v); + + tex_palette[i] = v; + } +} + +void SetTexParameters (void) +{ + qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, texture_mode ); + + switch ( texture_mode ) + { + case GL_NEAREST: + case GL_NEAREST_MIPMAP_NEAREST: + case GL_NEAREST_MIPMAP_LINEAR: + qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + break; + case GL_LINEAR: + case GL_LINEAR_MIPMAP_NEAREST: + case GL_LINEAR_MIPMAP_LINEAR: + qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + break; + } +} + +/* +============ +Texture_SetMode +============ +*/ +void Texture_SetMode(int iMenu) +{ + int i, iMode; + HMENU hMenu; + qboolean texturing = true; + + hMenu = GetMenu(g_qeglobals.d_hwndMain); + + switch(iMenu) { + case ID_VIEW_NEAREST: + iMode = GL_NEAREST; + break; + case ID_VIEW_NEARESTMIPMAP: + iMode = GL_NEAREST_MIPMAP_NEAREST; + break; + case ID_VIEW_LINEAR: + iMode = GL_NEAREST_MIPMAP_LINEAR; + break; + case ID_VIEW_BILINEAR: + iMode = GL_LINEAR; + break; + case ID_VIEW_BILINEARMIPMAP: + iMode = GL_LINEAR_MIPMAP_NEAREST; + break; + case ID_VIEW_TRILINEAR: + iMode = GL_LINEAR_MIPMAP_LINEAR; + break; + + case ID_TEXTURES_WIREFRAME: + iMode = 0; + texturing = false; + break; + + case ID_TEXTURES_FLATSHADE: + iMode = 0; + texturing = false; + break; + + } + + CheckMenuItem(hMenu, ID_VIEW_NEAREST, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_VIEW_NEARESTMIPMAP, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_VIEW_LINEAR, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_VIEW_BILINEARMIPMAP, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_VIEW_BILINEAR, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_VIEW_TRILINEAR, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_TEXTURES_WIREFRAME, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_TEXTURES_FLATSHADE, MF_BYCOMMAND | MF_UNCHECKED); + + CheckMenuItem(hMenu, iMenu, MF_BYCOMMAND | MF_CHECKED); + + g_qeglobals.d_savedinfo.iTexMenu = iMenu; + texture_mode = iMode; + + if (g_PrefsDlg.m_bSGIOpenGL) + { + if (s_hdcTexture && s_hglrcTexture) + { + //if (!qwglMakeCurrent(g_qeglobals.d_hdcBase, g_qeglobals.d_hglrcBase)) + if (!qwglMakeCurrent(s_hdcTexture, s_hglrcTexture)) + Error ("wglMakeCurrent in LoadTexture failed"); + } + else + return; + } + + if ( texturing ) + SetTexParameters (); + + if ( !texturing && iMenu == ID_TEXTURES_WIREFRAME) + { + g_pParentWnd->GetCamera()->Camera().draw_mode = cd_wire; + Map_BuildBrushData(); + Sys_UpdateWindows (W_ALL); + return; + + } else if ( !texturing && iMenu == ID_TEXTURES_FLATSHADE) { + + g_pParentWnd->GetCamera()->Camera().draw_mode = cd_solid; + Map_BuildBrushData(); + Sys_UpdateWindows (W_ALL); + return; + } + + for (i=1 ; iGetCamera()->Camera().draw_mode != cd_texture) + { + g_pParentWnd->GetCamera()->Camera().draw_mode = cd_texture; + Map_BuildBrushData(); + } + + Sys_UpdateWindows (W_ALL); +} + +/* +================ +R_MipMap + +Operates in place, quartering the size of the texture +================ +*/ +void R_MipMap (byte *in, int &width, int &height) +{ + int i, j; + byte *out; + int row; + + row = width * 4; + width >>= 1; + height >>= 1; + out = in; + for (i=0 ; i>2; + out[1] = (in[1] + in[5] + in[row+1] + in[row+5])>>2; + out[2] = (in[2] + in[6] + in[row+2] + in[row+6])>>2; + out[3] = (in[3] + in[7] + in[row+3] + in[row+7])>>2; + } + } +} + +/* +================= +Texture_LoadTexture +================= +*/ +//extern void Handle3DfxTexturing(qtexture_t* q, int nWidth, int nHeight, unsigned* pDest); +qtexture_t *Texture_LoadTexture (miptex_t *qtex) +{ + byte *source; + unsigned char *dest; + int width, height, i, count; + int total[3]; + qtexture_t *q; + + + width = LittleLong(qtex->width); + height = LittleLong(qtex->height); + + //if (g_PrefsDlg.m_bSGIOpenGL) + //{ + // if (width == 320) + // return NULL; + //} + + q = (qtexture_t*)qmalloc(sizeof(*q)); + + q->width = width; + q->height = height; + + q->flags = qtex->flags; + q->value = qtex->value; + q->contents = qtex->contents; + + dest = (unsigned char*)qmalloc (width*height*4); + + count = width*height; + source = (byte *)qtex + LittleLong(qtex->offsets[0]); + + // The dib is upside down so we want to copy it into + // the buffer bottom up. + + total[0] = total[1] = total[2] = 0; + for (i=0 ; icolor[0] = (float)total[0]/(count*255); + q->color[1] = (float)total[1]/(count*255); + q->color[2] = (float)total[2]/(count*255); + + q->texture_number = texture_extension_number++; + + if (g_PrefsDlg.m_bSGIOpenGL) + { + //if (!qwglMakeCurrent(g_qeglobals.d_hdcBase, g_qeglobals.d_hglrcBase)) + if (!qwglMakeCurrent(s_hdcTexture, s_hglrcTexture)) + Error ("wglMakeCurrent in LoadTexture failed"); + } + + qglBindTexture( GL_TEXTURE_2D, q->texture_number ); + + //Handle3DfxTexturing(q, width, height, dest); + + SetTexParameters (); + + int nCount = MAX_TEXTURE_QUALITY - g_PrefsDlg.m_nTextureQuality; + while (nCount-- > 0) + { + if (width > 16 && height > 16) + { + R_MipMap(dest, width, height); + } + else + { + break; + } + } + + if (g_PrefsDlg.m_bSGIOpenGL) + { + if (nomips) + { + qglTexImage2D(GL_TEXTURE_2D, 0, 3, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, dest); + } + else + qgluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height,GL_RGBA, GL_UNSIGNED_BYTE, dest); + } + else + { + if (nomips) + qglTexImage2D(GL_TEXTURE_2D, 0, 3, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, dest); + else + qgluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height,GL_RGBA, GL_UNSIGNED_BYTE, dest); + } + + free (dest); + + qglBindTexture( GL_TEXTURE_2D, 0 ); + + return q; +} + + + + +/* +================= +Texture_LoadTexture +================= +*/ +//extern void Handle3DfxTexturing(qtexture_t* q, int nWidth, int nHeight, unsigned* pDest); + +// (the TGA part of this function name appears to be irrelevant, the only criteria is that the pixels supplied are +// 32bpp, and in a normal sequential memory buffer...) +// +qtexture_t *Texture_LoadTGATexture (unsigned char* pPixels, int nWidth, int nHeight, char* pPath, int nFlags, int nContents, int nValue ) +{ + int i, j, inf; + byte gammatable[256]; + float fGamma = g_qeglobals.d_savedinfo.fGamma; + + + qtexture_t* q = (qtexture_t*)qmalloc(sizeof(*q)); + q->width = nWidth; + q->height = nHeight; + q->flags = nFlags; + q->value = nValue; + q->contents = nContents; + + + int nCount = nWidth * nHeight; + float total[3]; + total[0] = total[1] = total[2] = 0.0f; + + if (fGamma == 1.0) + { + for (i=0 ; i<256 ; i++) + gammatable[i] = i; + } + else + { + for (i=0 ; i<256 ; i++) + { + inf = 255 * pow ( (i+0.5)/255.5 , fGamma ) + 0.5; + if (inf < 0) + inf = 0; + if (inf > 255) + inf = 255; + gammatable[i] = inf; + } + } + + + // all targas are stored internally as 32bit so rgba = 4 bytes + for (i = 0 ; i < (nCount * 4) ; i += 4) + { + for (j = 0; j < 3; j++) + { + total[j] += (pPixels+i)[j]; + byte b = (pPixels+i)[j]; + (pPixels+i)[j] = gammatable[b]; + + } + } + + q->color[0] = total[0] / (nCount * 255); + q->color[1] = total[1] / (nCount * 255); + q->color[2] = total[2] / (nCount * 255); + + + q->texture_number = texture_extension_number++; + + if (g_PrefsDlg.m_bSGIOpenGL) + { + //if (!qwglMakeCurrent(g_qeglobals.d_hdcBase, g_qeglobals.d_hglrcBase)) + if (!qwglMakeCurrent(s_hdcTexture, s_hglrcTexture)) + Error ("wglMakeCurrent in LoadTexture failed"); + } + + qglBindTexture( GL_TEXTURE_2D, q->texture_number ); + + //Handle3DfxTexturing(q, width, height, dest); + + SetTexParameters(); + + nCount = MAX_TEXTURE_QUALITY - g_PrefsDlg.m_nTextureQuality; + while (nCount-- > 0) + { + if (nWidth > 16 && nHeight > 16) + { + R_MipMap(pPixels, nWidth, nHeight); + } + else + { + break; + } + } + + if (g_PrefsDlg.m_bSGIOpenGL) + { + if (nomips) + { + qglTexImage2D(GL_TEXTURE_2D, 0, 3, nWidth, nHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, pPixels); + } + else + qgluBuild2DMipmaps(GL_TEXTURE_2D, 3, nWidth, nHeight,GL_RGBA, GL_UNSIGNED_BYTE, pPixels); + } + else + { + if (nomips) + qglTexImage2D(GL_TEXTURE_2D, 0, 3, nWidth, nHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, pPixels); + else + qgluBuild2DMipmaps(GL_TEXTURE_2D, 3, nWidth, nHeight,GL_RGBA, GL_UNSIGNED_BYTE, pPixels); + } + + qglBindTexture( GL_TEXTURE_2D, 0 ); + + return q; +} + + +qtexture_t *Texture_LoadTGATexture (unsigned char* pPixels, int nWidth, int nHeight, char *pPath) +{ + CString strName; + CString strPath; + ExtractPath_and_Filename(pPath, strPath, strName); + AddSlash(strPath); + strPath += "textureinfo.ini"; + strName.MakeLower(); + StripExtension (strName.GetBuffer(0)); + strName.ReleaseBuffer(); + + int nFlags = GetPrivateProfileInt(strName, "Flags", 0, strPath); + int nValue = GetPrivateProfileInt(strName, "Value", 0, strPath); + int nContents = GetPrivateProfileInt(strName, "Contents", 0, strPath); + return Texture_LoadTGATexture(pPixels, nWidth, nHeight, pPath, nFlags, nValue, nContents); +} + + +void Texture_LoadFromPlugIn(LPVOID vp) +{ + _QERTextureLoad *pLoad = reinterpret_cast<_QERTextureLoad*>(vp); + if (pLoad != NULL) + { + qtexture_t *q; + q = Texture_LoadTGATexture(pLoad->m_pRGBA, pLoad->m_nWidth, pLoad->m_nHeight, NULL, pLoad->m_nFlags, pLoad->m_nContents, pLoad->m_nValue); + if (q != NULL) + { + // to save duplicate code (since one always ends up getting forgotten and out of sync) this is now done later by caller +// strcpy (q->name, pLoad->m_pName); +// StripExtension (q->name); +// if (!g_dontuse) +// q->inuse = true; +// q->next = g_qeglobals.d_qtextures; +// g_qeglobals.d_qtextures = q; + g_pluginTexture = q; + } + } +} + + +/* +=============== +Texture_CreateSolid + +Create a single pixel texture of the apropriate color +=============== +*/ +qtexture_t *Texture_CreateSolid (const char *name) +{ + byte data[4]; + qtexture_t *q; + + q = (qtexture_t*)qmalloc(sizeof(*q)); + + sscanf (name, "(%f %f %f)", &q->color[0], &q->color[1], &q->color[2]); + + data[0] = q->color[0]*255; + data[1] = q->color[1]*255; + data[2] = q->color[2]*255; + data[3] = 255; + + q->width = q->height = 1; + //q->width = q->height = 2; + q->texture_number = texture_extension_number++; + qglBindTexture( GL_TEXTURE_2D, q->texture_number ); + SetTexParameters (); + + if (g_PrefsDlg.m_bSGIOpenGL) + { + qglTexImage2D(GL_TEXTURE_2D, 0, 3, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + } + else + { + if (nomips) + qglTexImage2D(GL_TEXTURE_2D, 0, 3, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + else + qgluBuild2DMipmaps(GL_TEXTURE_2D, 3, 1, 1,GL_RGBA, GL_UNSIGNED_BYTE, data); + } + qglBindTexture( GL_TEXTURE_2D, 0 ); + + return q; +} + + +/* +================= +Texture_MakeNotexture +================= +*/ +void Texture_MakeNotexture (void) +{ + qtexture_t *q; + byte data[4][4]; + + + if (g_PrefsDlg.m_bSGIOpenGL) + { + if (s_hdcTexture && s_hglrcTexture) + { + //if (!qwglMakeCurrent(g_qeglobals.d_hdcBase, g_qeglobals.d_hglrcBase)) + if (!qwglMakeCurrent(s_hdcTexture, s_hglrcTexture)) + Error ("wglMakeCurrent in LoadTexture failed"); + } + else + return; + } + + notexture = q = (qtexture_t*)qmalloc(sizeof(*q)); + strcpy (q->name, "notexture"); + q->width = q->height = 64; + + memset (data, 0, sizeof(data)); + data[0][2] = data[3][2] = 255; + + q->color[0] = 0; + q->color[1] = 0; + q->color[2] = 0.5; + + q->texture_number = texture_extension_number++; + qglBindTexture( GL_TEXTURE_2D, q->texture_number ); + SetTexParameters (); + + if (nomips) + qglTexImage2D(GL_TEXTURE_2D, 0, 3, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + else + VERIFY(qgluBuild2DMipmaps(GL_TEXTURE_2D, 3, 2, 2,GL_RGBA, GL_UNSIGNED_BYTE, data) == 0); + + qglBindTexture( GL_TEXTURE_2D, 0 ); +} + +qtexture_t *Texture_MakePlaceHolder(void) +{ + qtexture_t *q; + byte data[4][4]; + + if (g_PrefsDlg.m_bSGIOpenGL) + { + if (s_hdcTexture && s_hglrcTexture) + { + //if (!qwglMakeCurrent(g_qeglobals.d_hdcBase, g_qeglobals.d_hglrcBase)) + if (!qwglMakeCurrent(s_hdcTexture, s_hglrcTexture)) + Error ("wglMakeCurrent in Texture_MakePlaceHolder failed"); + } + else + return NULL; + } + + q = (qtexture_t*)qmalloc(sizeof(*q)); + strcpy (q->name, "placeholder"); // overwritten on return + q->width = q->height = 64; + q->bPlaceHolder = true; + + memset (data, 0, sizeof(data)); + data[0][1] = data[3][1] = 255; // green and black squares + + q->color[0] = 0; + q->color[1] = 0; + q->color[2] = 0.5; + + q->texture_number = texture_extension_number++; + qglBindTexture( GL_TEXTURE_2D, q->texture_number ); + SetTexParameters (); + + if (nomips) + qglTexImage2D(GL_TEXTURE_2D, 0, 3, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + else + VERIFY(qgluBuild2DMipmaps(GL_TEXTURE_2D, 3, 2, 2,GL_RGBA, GL_UNSIGNED_BYTE, data) == 0); + + qglBindTexture( GL_TEXTURE_2D, 0 ); + + return q; +} + + + +void LoadShader(const char* pFilename, qtexture_t *q) +{ + char* pBuff = NULL; + CString strTexture; + int nSize = LoadFile(pFilename, reinterpret_cast(&pBuff)); + if (nSize == -1) + { + nSize = PakLoadAnyFile(pFilename, reinterpret_cast(&pBuff)); + } + if (nSize > 0) + { + StartTokenParsing(pBuff); + while (GetToken(true)) + { + // first token should be the path + name.. (from base) + CShaderInfo *pShader = new CShaderInfo(); + pShader->setName(token); + pShader->m_strShaderName = pFilename; + strTexture = token; + bool bGood = true; + float fTrans = 1.0; + GetToken(true); + if (strcmp(token, "{")) + { + bGood = false; + break; + } + else + { + // we need to read until we hit a balanced } + int nMatch = 1; + while (nMatch > 0 && GetToken(true)) + { + if (strcmp(token, "{") == 0) + { + nMatch++; + } + else if (strcmp(token, "}") == 0) + { + nMatch--; + } + else if (strcmpi(token, "qer_nocarve") == 0) + { + pShader->m_nFlags |= QER_NOCARVE; + } + else if (strcmpi(token, "qer_trans") == 0) + { + if (GetToken(true)) + { + fTrans = atof(token); + } + pShader->m_nFlags |= QER_TRANS; + } + else if (strcmpi(token, "qer_editorimage") == 0) + { + if (GetToken(true)) + { + char* pTex = copystring(token); + QE_ConvertDOSToUnixName( pTex, pTex ); + CString str = pTex; + free (pTex); + FindReplace(str, "textures/", ""); + FindReplace(str, ".tga", ""); + int nPos = str.Find('/'); + if (nPos == -1) + { + nPos = str.Find('\\'); + } + if (nPos >= 0) + { + if (g_PrefsDlg.m_bShaderTest) + { + pShader->m_strTextureName = str; + pShader->m_strTextureName.MakeLower(); //avoid problems with case + } + else + { + CString strPath = str.Left(nPos+1); + DeferredShaderLoad *deferred = new DeferredShaderLoad(str, pShader->m_strName, strPath); + g_lstDeferred.Add(deferred); + } + } + } + } + else if (strcmpi(token, "surfaceparm") == 0) + { + //--while (GetToken(false)) + //--{ + //-- + //--} + if (GetToken(true)) + { + // next token should be a surface parm + //--if (strcmpi(token, "trans") == 0) + //--{ + //-- fTrans = 0.33; + //--} + if (strcmpi(token, "fog") == 0) + { + if (fTrans == 1.0) // has not been explicitly set by qer_trans + { + fTrans = 0.35; + } + } + } + } + } + if (nMatch != 0) + { + bGood = false; + break; + } + } + //--if (bGood && q) + if (bGood) + { + pShader->m_fTransValue = fTrans; + g_lstShaders.Add(pShader); + + if (g_PrefsDlg.m_bShaderTest) + { +// new + if (pShader->m_strTextureName.GetLength() > 0) + { + if (!ShaderQTextureExists(pShader->m_strName)) + { + q = Texture_ForName ( pShader->m_strTextureName.GetBuffer(0), + true, // bool bForArchitecture + NULL, // const char *psBasePath + eReplace_NO, // eReplace_t eReplace + true // bool bPlaceHolderLoad + ); + if (q != NULL) + { + strcpy(q->shadername, pShader->m_strShaderName); + q->bFromShader = true; + q->fTrans = pShader->m_fTransValue; + q->nShaderFlags = pShader->m_nFlags; + strcpy(q->name, pShader->m_strName); + } + } + } + } +// end new + //--q->bFromShader = true; + //--q->fTrans = fTrans; + + //--// good texture here + //--//Sys_Printf("Test load texture %s\n", strTexture); + //--// FIXME.. this is a load of crap + //--strcpy(dirstring, strTexture); + //--QE_ConvertDOSToUnixName(dirstring, dirstring); + //--strTexture = dirstring; + //--FindReplace(strTexture, "textures/", ""); + //--qtexture_t *q = Texture_ForName (strTexture.GetBuffer(0)); + //--if (q != NULL) + //--{ + //-- q->bFromShader = true; + //-- q->fTrans = fTrans; + //--} + } + else + { + Sys_Printf("Error parsing shader at texture %s\n", strTexture); + } + + } + free (pBuff); + } + else + { + Sys_Printf("Unabled to read shader %s\n", pFilename); + } +} + + +extern bool DoesFileExist(const char* pBuff, long& lSize); +void SetNameShaderInfo(qtexture_t* q, const char* pPath, const char* pName) +{ + CShaderInfo *pInfo = hasShader(pName); + if (pInfo) + { + strcpy(q->shadername, pInfo->m_strShaderName); + q->bFromShader = true; + q->fTrans = pInfo->m_fTransValue; + q->nShaderFlags = pInfo->m_nFlags; + } + else + { + q->shadername[0] = 0; + } + strncpy (q->name, pName, sizeof(q->name) - 1); + StripExtension (q->name); +} + +void ReplaceQTexture(qtexture_t *pOld, qtexture_t *pNew) +{ + brush_t* pList = &active_brushes; + + for (brush_t* pBrush = pList->next ; pBrush != pList; pBrush = pBrush->next) + { + if (pBrush->patchBrush) + { + Patch_ReplaceQTexture(pBrush, pOld, pNew); + } + + for (face_t* pFace = pBrush->brush_faces; pFace; pFace = pFace->next) + { + if (pFace->d_texture == pOld) + { + pFace->d_texture = pNew; + } + } + //Brush_Build(pBrush); + } + +} + + +void Texture_Remove(qtexture_t *q) +{ + qtexture_t* pTex = g_qeglobals.d_qtextures->next; + if (q == g_qeglobals.d_qtextures) // it is the head + { + g_qeglobals.d_qtextures->next = q->next->next; + g_qeglobals.d_qtextures = q->next; + } + else + { + qtexture_t* pLast = g_qeglobals.d_qtextures; + while (pTex != NULL && pTex != g_qeglobals.d_qtextures) + { + if (pTex == q) + { + pLast->next = q->next; + break; + } + pLast = pTex; + pTex = pTex->next; + } + } + qglDeleteTextures(1, reinterpret_cast(&q->texture_number)); + free(q); + +} + + +bool GetFileTime(LPCSTR psFileName, FILETIME &ft) +{ + bool bSuccess = false; + HANDLE hFile = CreateFile( psFileName, // LPCTSTR lpFileName, // pointer to name of the file + GENERIC_READ, // DWORD dwDesiredAccess, // access (read-write) mode + FILE_SHARE_READ, // DWORD dwShareMode, // share mode + NULL, // LPSECURITY_ATTRIBUTES lpSecurityAttributes, // pointer to security attributes + OPEN_EXISTING, // DWORD dwCreationDisposition, // how to create + FILE_FLAG_SEQUENTIAL_SCAN,// DWORD dwFlagsAndAttributes, // file attributes + NULL // HANDLE hTemplateFile // handle to file with attributes to + ); + + if (hFile != INVALID_HANDLE_VALUE) + { + if (GetFileTime(hFile, // handle to file + NULL, // LPFILETIME lpCreationTime + NULL, // LPFILETIME lpLastAccessTime + &ft // LPFILETIME lpLastWriteTime + ) + ) + { + bSuccess = true; + } + + CloseHandle(hFile); + } + + return bSuccess; +} + +bool CurrentFileOutOfDate(FILETIME &ftCurrent, FILETIME &ftProposed) +{ + LONG l = CompareFileTime( &ftCurrent, &ftProposed ); + + return (l<0); +} + +static qtexture_t *Texture_ForName_Actual (const char *name, const char *psBasePath /* may be NULL */, qtexture_t *pRemove, bool bPlaceHolderLoad) +{ + byte *lump; + qtexture_t *q; + char filename[1024]; + + if (bPlaceHolderLoad) + { + Sys_Printf ("Placeholding %s...", name); + q = Texture_MakePlaceHolder(); + if (q == NULL) + { + Sys_Printf ("ERROR in SGI code!\n"); + return notexture; // only happens if Errorbox pops up using SGI OpenGL, so who cares? + } + SetNameShaderInfo(q, filename, name); + Sys_Printf ("ok\n"); + } + else + { + if (name[0] == '(') + { + q = Texture_CreateSolid (name); + strncpy (q->name, name, sizeof(q->name)-1); + } + else + { + // FIXME: this is a mess.. need to move consolidate stuff + // down to a single routine.. + // + // if plugins have a texture loader + // { + // + // } + // else + // + if (g_pParentWnd->GetPlugInMgr().GetTextureInfo() != NULL) + { + // rad: 12/19/98 + // if the plugin is not a wad style then we need to treat it normally + // otherwise return without trying to explicitly load the texture + // as it should have been loaded by the wad style plugin at init time + CString strTex = GetTextureExtension(0); + sprintf (filename, "%s\\%s.%s", (psBasePath?psBasePath:ValueForKey (g_qeglobals.d_project_entity, "texturepath")), name, strTex); + if (!g_pParentWnd->GetPlugInMgr().GetTextureInfo()->m_bWadStyle) + { + g_pParentWnd->GetPlugInMgr().LoadTexture(filename); + q = g_pluginTexture; + } + else + { + return notexture; + // wadstyle.. if we get here then we do not have it + } + } + else + { + if (g_PrefsDlg.m_bHiColorTextures == TRUE) + { + sprintf (filename, "%s/%s.tga", (psBasePath?psBasePath:ValueForKey (g_qeglobals.d_project_entity, "texturepath")), name); + if (strchr(/*file*/name,' ')) // only check 'name', not 'filename' since we don't care about the "q:\quake" part (which CAN have spaces) + { + ErrorBox( va("Dammit!\n\nThe texture name \"%s\" has a space in it\n\nFix this now!!!",name)); + } + else + { + Sys_Printf ("Loading %s...", name); + unsigned char* pPixels = NULL; + int nWidth; + int nHeight; + LoadTGA(filename, &pPixels, &nWidth, &nHeight); + if (pPixels) + { + q = Texture_LoadTGATexture(pPixels, nWidth, nHeight, NULL, 0, 0, 0); + + SetNameShaderInfo(q, filename, name); + Sys_Printf ("done.\n", name); + free(pPixels); + } + else + { + Sys_Printf ("failed. Trying Shader..."); + + //=========== findmeste2 + CShaderInfo *pInfo = hasShader(name); + if (pInfo) + { + sprintf (filename, "%s/%s.tga", (psBasePath?psBasePath:ValueForKey (g_qeglobals.d_project_entity, "texturepath")), pInfo->m_strTextureName); + LoadTGA(filename, &pPixels, &nWidth, &nHeight); + } + + if (pPixels) + { + q = Texture_LoadTGATexture(pPixels, nWidth, nHeight, NULL, 0, 0, 0); + + SetNameShaderInfo(q, filename, name); + free(pPixels); + + Sys_Printf (" success, shader: \"%s\"\n", q->shadername); + } + else + { + Sys_Printf (" failed. Using default. !! !! !!\n"); + q = notexture; + SetNameShaderInfo(q, filename, name); + Texture_MakeNotexture(); + } + } + } + } + else + { + // load the file + sprintf (filename, "%s/%s.wal", (psBasePath?psBasePath:ValueForKey (g_qeglobals.d_project_entity, "texturepath")), name); + Sys_Printf ("Loading %s...", name); + if (LoadFile (filename, (void**)&lump) == -1) + { + sprintf (filename, "%s.wal", name); + Sys_Printf("failed.. trying pak0.pak.."); + if(!PakLoadFile(filename, (void**)&lump)) + { + Sys_Printf (" load failed!\n"); + return notexture; + } + } + Sys_Printf("successful.\n"); + q = Texture_LoadTexture ((miptex_t *)lump); + free (lump); + strncpy (q->name, name, sizeof(q->name)-1); + StripExtension (q->name); + } + } + + if(!q) + return notexture; + strncpy (q->name, name, sizeof(q->name)-1); + StripExtension (q->name); + }// name[0] != '(' + } + + if(!q) // safety + return notexture; + + if (!g_dontuse) + q->inuse = true; + q->next = g_qeglobals.d_qtextures; + g_qeglobals.d_qtextures = q; + + if (pRemove != NULL) + { + Sys_Printf ("** Replacing %s with %s **\n", pRemove->name, q->name); + ReplaceQTexture(pRemove, q); + Texture_Remove(pRemove); + } + + return q; +} + + +extern char gsFoundFilename[MAX_PATH]; +extern FILETIME gtFileTime; +extern bool gbFileTimeValid; +//bool GetFileTime(LPCSTR psFileName, FILETIME &ft); +qtexture_t *Texture_ForName(const char *name, bool bForArchitecture /* = true */, const char *psBasePath /* = NULL */, eReplace_t eReplace /* = eReplace_NO */, bool bPlaceHolderLoad /* = false */ ) +{ + qtexture_t *q; + +#ifdef QUAKE3 + // this is really dirty, I'm going to write back into a constant char array (which is part of a CString buffer) + // purely because I know that changing the case won't overrun any internal buffers... :-) + // + char *__p = const_cast (name); + strlwr(__p); +#endif + + if (strlen(name) == 0) + return notexture; + + qtexture_t *pRemove = NULL; + + for (q=g_qeglobals.d_qtextures ; q ; q=q->next) + { + if (!stricmp(name, q->name)) + { + if (bPlaceHolderLoad) // don't worry about this until we try and load it seriously, this is just establishing the name slot + { + return q; + } + else + { + if (q->bPlaceHolder) + { + eReplace = eReplace_YES; // neat, huh? + } + } + + switch ( eReplace ) + { + case eReplace_NO: + + if (!g_dontuse) + q->inuse = true; + + q->bForArchitecture = bForArchitecture; + return q; + + case eReplace_YES: + + pRemove = q; + //Texture_Remove(q); + break; + + + case eReplace_TIMESTAMP: + + if (q->sFTName[0]) + { + FILETIME ftDiskVersion; + + if (GetFileTime(q->sFTName, ftDiskVersion)) + { + if (!CurrentFileOutOfDate(q->ft, ftDiskVersion)) + { + if (!g_dontuse) + q->inuse = true; + + q->bForArchitecture = bForArchitecture; + return q; + } + } + } + + pRemove = q; + break; + } + break; + } + } + + gbFileTimeValid = false; + + q = Texture_ForName_Actual(name, psBasePath, pRemove, bPlaceHolderLoad); + + if (gbFileTimeValid && gsFoundFilename[0]) + { + q->ft = gtFileTime; + strcpy(q->sFTName,gsFoundFilename); + } + else + { + q->sFTName[0] = '\0'; + } + + q->bForArchitecture = bForArchitecture; + return q; +} + +/* +=============== +Texture_ForName +=============== +*/ +/* +// appears not to be used now +qtexture_t *Texture_ForNamePath(char* name, char* pFullPath) +{ + byte *lump; + qtexture_t *q; + char filename[1024]; + + if (strlen(name) == 0) + return notexture; + + for (q=g_qeglobals.d_qtextures ; q ; q=q->next) + { + if (!strcmp(name, q->name)) + { + if (!g_dontuse) + q->inuse = true; + return q; + } + } + + if (name[0] == '(') + { + q = Texture_CreateSolid (name); + strncpy (q->name, name, sizeof(q->name)-1); + } + else + { + // load the file + if (g_PrefsDlg.m_bHiColorTextures == TRUE) + { + sprintf(filename, "%s%s", pFullPath, ".tga"); + Sys_Printf ("Loading %s...", name); + unsigned char* pPixels = NULL; + int nWidth; + int nHeight; + LoadTGA(filename, &pPixels, &nWidth, &nHeight); + if (pPixels) + { + q = Texture_LoadTGATexture(pPixels, nWidth, nHeight, NULL, 0, 0, 0); + } + else + { + return notexture; + } + free(pPixels); + } + else + { + sprintf(filename, "%s%s", pFullPath, ".wal"); + Sys_Printf ("Loading %s...", name); + if (LoadFile (filename, (void**)&lump) == -1) + { + Sys_Printf (" load failed!\n"); + return notexture; + } + Sys_Printf("successful.\n"); + q = Texture_LoadTexture ((miptex_t *)lump); + free (lump); + } + if (g_PrefsDlg.m_bSGIOpenGL) + { + if(!q) + return notexture; + } + strncpy (q->name, name, sizeof(q->name)-1); + StripExtension (q->name); + } + + if (!g_dontuse) + q->inuse = true; + q->next = g_qeglobals.d_qtextures; + g_qeglobals.d_qtextures = q; + + return q; +} +*/ + + +/* +================== +FillTextureMenu + +================== +*/ +void FillTextureMenu (CStringArray* pArray) +{ + HMENU hmenu; + int i; + struct _finddata_t fileinfo; + int handle; + char dirstring[1024]; + char *path; + DIRLIST *list = NULL, *temp; + + if (g_pParentWnd->GetPlugInMgr().GetTextureInfo() != NULL) + { + if (g_pParentWnd->GetPlugInMgr().GetTextureInfo()->m_bWadStyle) + return; + } + + hmenu = GetSubMenu (GetMenu(g_qeglobals.d_hwndMain), MENU_TEXTURE); + + // delete everything + for (i=0 ; inext) + { + AppendMenu (hmenu, MF_ENABLED|MF_STRING, CMD_TEXTUREWAD+texture_nummenus, (LPCTSTR)temp->dirname); + strcpy (texture_menunames[texture_nummenus], temp->dirname); + //--if (!g_PrefsDlg.m_bUseShaders) + //--{ + strcat (texture_menunames[texture_nummenus], "/"); + //--} + if (pArray) + pArray->Add(temp->dirname); + if (++texture_nummenus == MAX_TEXTUREDIRS) + break; + } + + ClearDirList(&list); + } + + +} + + +/* +================== +Texture_ClearInuse + +A new map is being loaded, so clear inuse markers +================== +*/ +void Texture_ClearInuse (void) +{ + qtexture_t *q; + + for (q=g_qeglobals.d_qtextures ; q ; q=q->next) + { + q->inuse = false; + } +} + + + +/* +============== +Texture_LoadPalette +============== +*/ +void Texture_LoadPalette (char *palette) +{ + char name[1024]; + byte *pal; // added for daikatana + + Texture_ClearInuse (); + + //load the palette + sprintf (name, "%s/textures/%scolormap.bmp", // added for daikatana + ValueForKey(g_qeglobals.d_project_entity, "basepath"), palette);// added for daikatana + + Load256Image (name, NULL, &pal, NULL, NULL); // added for daikatana + if (!pal) // added for daikatana + Error ("Couldn't load %s", name); // added for daikatana + Texture_InitPalette (pal); // added for daikatana + free (pal); // added for daikatana + + // create the fallback texture // added for daikatana + Texture_MakeNotexture (); // added for daikatana +} + +/* +============== +Texture_LoadPalette +============== +*/ +void Texture_LoadPaletteExplicit(char *palette) +{ + char name[1024]; + byte *pal; // added for daikatana + + Texture_ClearInuse (); + + //load the palette + sprintf (name, "%s%s",palette,"colormap.bmp"); + + Load256Image (name, NULL, &pal, NULL, NULL); // added for daikatana + if (!pal) // added for daikatana + Error ("Couldn't load %s", name); // added for daikatana + Texture_InitPalette (pal); // added for daikatana + free (pal); // added for daikatana + + // create the fallback texture // added for daikatana + Texture_MakeNotexture (); // added for daikatana +} + + +/* +============== +Texture_ShowDirectory +============== +*/ +void Texture_ShowDirectory (int menunum, bool bLinked) +{ + struct _finddata_t fileinfo; + int handle; + char name[1024]; + char dirstring[1024]; + char linkstring[1024]; + FILELIST *list = NULL, *temp; + CString strTemp; + + //Texture_Flush(false); + Select_Deselect(); + texture_showinuse = false; + strcpy (texture_directory, texture_menunames[menunum-CMD_TEXTUREWAD]); + + if (g_pParentWnd->GetPlugInMgr().GetTextureInfo() != NULL) + { + if (g_pParentWnd->GetPlugInMgr().GetTextureInfo()->m_bWadStyle) + return; + } + + // new + if (!g_PrefsDlg.m_bShaderTest) + { + g_dontuse = true; // needed because this next piece of code calls Texture_ForName() internally! -slc + LoadDeferred(texture_directory); + g_dontuse = false; + } + + if (g_PrefsDlg.m_bHiColorTextures == FALSE) + { + } + + g_qeglobals.d_texturewin.originy = 0; + + //--if (g_PrefsDlg.m_bUseShaders) + //--{ + //-- sprintf (dirstring, "%s/scripts/%s", ValueForKey (g_qeglobals.d_project_entity, "basepath"), texture_directory); + //-- Sys_Printf("loading textures from shader %s\n", dirstring); + //-- LoadShader(dirstring); + //--} + //--else + //--{ + Sys_Status("Loading textures\n", 0); + + // load all image files + + + sprintf (linkstring, "%s/textures/%stextureinfo.ini", ValueForKey (g_qeglobals.d_project_entity, "basepath"), texture_menunames[menunum-CMD_TEXTUREWAD]); + + for (int nExt = 0; nExt < GetTextureExtensionCount(); nExt++) + { + sprintf (dirstring, "%s/textures/%s*.%s", ValueForKey (g_qeglobals.d_project_entity, "basepath"), texture_menunames[menunum-CMD_TEXTUREWAD],GetTextureExtension(nExt)); + Sys_Printf ("Scanning %s\n", dirstring); + + handle = _findfirst (dirstring, &fileinfo); + + if (handle == -1) + { + sprintf(dirstring, "%s/%s*.%s", ValueForKey (g_qeglobals.d_project_entity, "texturepath"), texture_menunames[menunum-CMD_TEXTUREWAD],GetTextureExtension(nExt)); + + handle = _findfirst (dirstring, &fileinfo); + } + + + if (handle != -1) + { + do + { + sprintf (name, "%s%s", texture_directory, fileinfo.name); + AddToFileListAlphabetized(&list, name, FROMDISK, 0, false); + } while (_findnext( handle, &fileinfo ) != -1); + _findclose (handle); + } + else + {//if you don't put textures/ in here, it will also return other crap like, menu/common/*.tga + sprintf (dirstring, "textures/%s*.%s", texture_menunames[menunum-CMD_TEXTUREWAD],GetTextureExtension(nExt)); + if(!GetPackFileList(&list, dirstring)) { +// return; + } + } + } + + g_dontuse = true; + for(temp = list; temp; temp = temp->next) + { + if(temp->offset == -1) + sprintf(name, "%s", temp->filename); + else + sprintf(name, "%s%s", texture_menunames[menunum-CMD_TEXTUREWAD], temp->filename); + StripExtension (name); + strTemp = name; + strTemp.MakeLower(); + if ( strTemp.Find(".specular") >= 0 || + strTemp.Find(".glow") >= 0 || + strTemp.Find(".bump") >= 0 || + strTemp.Find(".diffuse") >= 0 || + strTemp.Find(".blend") >= 0 || + strTemp.Find(".alpha") >= 0 + ) + continue; + else + { + //if pref do replacing + //Texture_ForName (name, true); + //else + Texture_ForName (name, true, NULL, eReplace_TIMESTAMP); + + // any architecture placeholder textures still need loading?... + // + bool bPlaceHoldersStillExist = false; + for (qtexture_t *q=g_qeglobals.d_qtextures ; q ; q=q->next) + { + if (q->bForArchitecture && q->bPlaceHolder) + { + bPlaceHoldersStillExist = true; + break; + } + } + + if (bPlaceHoldersStillExist) + { + // new bit, because of placeholder loading, we should also force-load any other shaders that use this + // texture... + CString strTextureName(name); + CShaderInfo *pParentShader = hasShader(name); + if (pParentShader && !pParentShader->m_strTextureName.IsEmpty()) + { + strTextureName = pParentShader->m_strTextureName; + } + + // find all other shaders that also use this texture and load them as well... + // + int nSize = g_lstShaders.GetSize(); + for (int i = 0; i < nSize; i++) + { + CShaderInfo *pInfo = reinterpret_cast(g_lstShaders.ElementAt(i)); + if (pInfo != NULL && pInfo != pParentShader) + { + // does this shader same the same actual texture as the one we've just loaded?... + // + if (pInfo->m_strTextureName.CompareNoCase(strTextureName) == 0) + { + // yep, so ensure this one is loaded as well... + // + Texture_ForName(pInfo->m_strName, true, NULL, eReplace_TIMESTAMP); + } + } + } + } + } + } + + ClearFileList(&list); + //--} + + + g_dontuse = false; + + if (!bLinked) + { + + for (int k = 0; k < 10; k++) + { + sprintf(name, "Path%d", k); + if (GetPrivateProfileString("Include", name, "", dirstring, 1024, linkstring) > 0) + { + Texture_ShowDirectory(dirstring, true); + } + } + + SortTextures(); + + sprintf (name, "Textures: %s", texture_directory); + SetWindowText(g_qeglobals.d_hwndEntity, name); + + // select the first texture in the list + if (!g_qeglobals.d_texturewin.texdef.name[0]) + SelectTexture (16, g_qeglobals.d_texturewin.height -16, false); + } + + Sys_Printf ("Done\n", dirstring); +} + + +// this can be combined with the above, but per usual i am in a hurry +// +void Texture_ShowDirectory (char* pPath, bool bLinked) +{ + struct _finddata_t fileinfo; + int handle; + char name[1024]; + char dirstring[1024]; + char linkstring[1024]; + FILELIST *list = NULL, *temp; + + //Texture_Flush(false); + + texture_showinuse = false; + strcpy (texture_directory, pPath); + + if (g_PrefsDlg.m_bHiColorTextures == FALSE) + { + } + + g_qeglobals.d_texturewin.originy = 0; + Sys_Status("loading all textures\n", 0); + + // load all .wal files + for (int nExt = 0; nExt < GetTextureExtensionCount(); nExt++) + { + sprintf(dirstring, "%s*.%s", pPath,GetTextureExtension(nExt)); + + Sys_Printf ("Scanning %s\n", dirstring); + + handle = _findfirst (dirstring, &fileinfo); + + if (handle != -1) + { + do + { + sprintf (name, "%s%s", texture_directory, fileinfo.name); + AddToFileListAlphabetized(&list, name, FROMDISK, 0, false); + } while (_findnext( handle, &fileinfo ) != -1); + _findclose (handle); + } + else + { + //sprintf (dirstring, "%s*.wal", texture_menunames[menunum-CMD_TEXTUREWAD]); + //if(!GetPackFileList(&list, dirstring)) + // return; + } + } + + g_dontuse = true; + for(temp = list; temp; temp = temp->next) + { + if(temp->offset == -1) + sprintf(name, "%s", temp->filename); + else + sprintf(name, "%s%s", pPath, temp->filename); + StripExtension (name); + + int nLen = strlen(name)-1; + ASSERT(nLen > 0); + while (name[nLen] != '\\') + nLen--; + // found first one + nLen--; + ASSERT(nLen > 0); + while (name[nLen] != '\\') + nLen--; + ASSERT(nLen >= 0); + QE_ConvertDOSToUnixName(name, name); + Texture_ForName(&name[nLen+1]); + + } + + ClearFileList(&list); + + g_dontuse = false; + + + if (!bLinked) + { + + for (int k = 0; k < 10; k++) + { + sprintf(name, "Path%d", k); + if (GetPrivateProfileString("Include", name, "", dirstring, 1024, linkstring) > 0) + { + Texture_ShowDirectory(dirstring, true); + } + } + + SortTextures(); + + sprintf (name, "Textures: %s", texture_directory); + SetWindowText(g_qeglobals.d_hwndEntity, name); + + // select the first texture in the list + if (!g_qeglobals.d_texturewin.texdef.name[0]) + SelectTexture (16, g_qeglobals.d_texturewin.height -16, false); + } +} + + + +void Texture_ResetPosition() +{ + SelectTexture (16, g_qeglobals.d_texturewin.height -16, false); + g_qeglobals.d_texturewin.originy = 0; +} + + +/* +============== +Texture_ShowInuse +============== +*/ +void Texture_ShowInuse (void) +{ + face_t *f; + brush_t *b; + char name[1024]; + + texture_showinuse = true; + g_dontuse = false; + + g_qeglobals.d_texturewin.originy = 0; + + Texture_ClearInuse(); + Sys_Status("Selecting active textures\n", 0); + + for (b=active_brushes.next ; b != NULL && b != &active_brushes ; b=b->next) + { + const char *p = Patch_FromBrush_GetTextureName(b); + if (strlen(p)) + Texture_ForName (p); + + for (f=b->brush_faces ; f ; f=f->next) + Texture_ForName (f->texdef.name); + } + + for (b=selected_brushes.next ; b != NULL && b != &selected_brushes ; b=b->next) + { + const char *p = Patch_FromBrush_GetTextureName(b); + if (strlen(p)) + Texture_ForName (p); + + for (f=b->brush_faces ; f ; f=f->next) + Texture_ForName (f->texdef.name); + } + + SortTextures(); + //SetInspectorMode(W_TEXTURE); + Sys_UpdateWindows (W_TEXTURE); + + sprintf (name, "Textures: in use"); + SetWindowText(g_qeglobals.d_hwndEntity, name); + + // select the first texture in the list + if (!g_qeglobals.d_texturewin.texdef.name[0]) + { + SelectTexture (16, g_qeglobals.d_texturewin.height -16, false); + } +} + +/* +============================================================================ + +TEXTURE LAYOUT + +============================================================================ +*/ + +void Texture_StartPos (void) +{ + current_texture = g_qeglobals.d_qtextures; + current_x = 8; + current_y = -8; + current_row = 0; +} + +qtexture_t *Texture_NextPos (int *x, int *y) +{ + qtexture_t *q; + + while (1) + { + q = current_texture; + if (!q) + return q; + current_texture = current_texture->next; + if (q->name[0] == '(') // fake color texture + continue; + + if (!q->bForArchitecture) + continue; + + + if (g_bFilterEnabled) + { + CString strName = q->name; + int nPos = strName.Find('\\'); + if (nPos == -1) + nPos = strName.Find('/'); + if (nPos >= 0) + strName = strName.Right(strName.GetLength() - nPos - 1); + if (strnicmp(g_strFilter.GetBuffer(0), strName, g_strFilter.GetLength()) == 0) + break; + else + continue; + } + + if (q->bFromShader && g_PrefsDlg.m_bShowShaders == FALSE) + { + continue; + } + + if (q->inuse) + break; // always show in use + + if (!texture_showinuse && !strnicmp (q->name, texture_directory, strlen(texture_directory))) + break; + continue; + } + + int nWidth = (g_PrefsDlg.m_bHiColorTextures == TRUE) ? q->width * ((float)g_PrefsDlg.m_nTextureScale / 100) : q->width; + int nHeight = (g_PrefsDlg.m_bHiColorTextures == TRUE) ? q->height * ((float)g_PrefsDlg.m_nTextureScale / 100) : q->height; + if (current_x + nWidth > g_qeglobals.d_texturewin.width-8 && current_row) + { // go to the next row unless the texture is the first on the row + current_x = 8; + current_y -= current_row + FONT_HEIGHT + 4; + current_row = 0; + } + + *x = current_x; + *y = current_y; + + // Is our texture larger than the row? If so, grow the + // row height to match it + + if (current_row < nHeight) + current_row = nHeight; + + // never go less than 64, or the names get all crunched up + current_x += nWidth < 64 ? 64 : nWidth; + current_x += 8; + + return q; +} + +/* +============================================================================ + + MOUSE ACTIONS + +============================================================================ +*/ + +static int textures_cursorx, textures_cursory; + + +/* +============ +Texture_SetTexture + +============ +*/ +void Texture_SetTexture (texdef_t *texdef, bool bFitScale/*=false*/, bool bNoSystemTextureOverwrite/*=false*/) +{ + qtexture_t *q; + int x,y; + + if (texdef->name[0] == '(') + { + Sys_Status("Can't select an entity texture\n", 0); + return; + } + g_qeglobals.d_texturewin.texdef = *texdef; + g_qeglobals.d_texturewin.texdef.flags &= ~SURF_KEEP; + g_qeglobals.d_texturewin.texdef.contents &= ~CONTENTS_KEEP; + + Sys_UpdateWindows (W_TEXTURE); + + g_dlgFind.updateTextures(texdef->name); + + if (!g_dlgFind.isOpen())// && bSetSelection) + { + Select_SetTexture(texdef, bFitScale, bNoSystemTextureOverwrite); + } + +// scroll origin so the texture is completely on screen + Texture_StartPos (); + while (1) + { + q = Texture_NextPos (&x, &y); + if (!q) + break; + + int nWidth = (g_PrefsDlg.m_bHiColorTextures == TRUE) ? q->width * ((float)g_PrefsDlg.m_nTextureScale / 100) : q->width; + int nHeight = (g_PrefsDlg.m_bHiColorTextures == TRUE) ? q->height * ((float)g_PrefsDlg.m_nTextureScale / 100) : q->height; + if (!strcmpi(texdef->name, q->name)) + { + if (y > g_qeglobals.d_texturewin.originy) + { + g_qeglobals.d_texturewin.originy = y; + Sys_UpdateWindows (W_TEXTURE); + return; + } + + if (y-nHeight-2*FONT_HEIGHT < g_qeglobals.d_texturewin.originy-g_qeglobals.d_texturewin.height) + { + g_qeglobals.d_texturewin.originy = y-nHeight-2*FONT_HEIGHT+g_qeglobals.d_texturewin.height; + Sys_UpdateWindows (W_TEXTURE); + return; + } + + return; + } + } +} + + +/* +============== +SelectTexture + + By mouse click +============== +*/ +void SelectTexture (int mx, int my, bool bShift, bool bFitScale/*=false*/, bool bNoSystemTextureOverwrite/*=false*/) +{ + int x, y; + qtexture_t *q, *pPrev = NULL; + texdef_t tex; + + my += g_qeglobals.d_texturewin.originy-g_qeglobals.d_texturewin.height; + + Texture_StartPos (); + for (;;pPrev = q) + { + q = Texture_NextPos (&x, &y); + if (!q) + break; + int nWidth = (g_PrefsDlg.m_bHiColorTextures == TRUE) ? q->width * ((float)g_PrefsDlg.m_nTextureScale / 100) : q->width; + int nHeight = (g_PrefsDlg.m_bHiColorTextures == TRUE) ? q->height * ((float)g_PrefsDlg.m_nTextureScale / 100) : q->height; + if (mx > x && mx - x < nWidth + && my < y && y - my < nHeight + FONT_HEIGHT) + { + if (q->bPlaceHolder) + { + CWaitCursor wait; + + // this next bit will change the order of the link list by inserting us at the front, so we need to + // restore the list order for niceness... + // + qtexture_t *pNext = q->next; + + q = Texture_ForName(q->name, true, NULL, eReplace_YES, false); // q is now different (new malloc, linked to list start) + // + // move new q ptr back to old position in list... + // + if (pPrev) + { + assert(g_qeglobals.d_qtextures == q); + g_qeglobals.d_qtextures = q->next; + pPrev->next = q; + q->next = pNext; + } + else + { + // was at the head now, was at the head before, ergo nothing to do... + } + + Sys_UpdateWindows( W_TEXTURE); + return; + } + if (bShift) + { + if (g_PrefsDlg.m_bHiColorTextures && q->shadername[0] != 0) + { + CString s = "notepad "; + s += q->shadername; + WinExec(s, SW_SHOWNORMAL); + } + } + memset (&tex, 0, sizeof(tex)); + tex.scale[0] = (g_PrefsDlg.m_bHiColorTextures) ? fTEXTURE_SCALE : 1; + tex.scale[1] = (g_PrefsDlg.m_bHiColorTextures) ? fTEXTURE_SCALE : 1; + tex.flags = q->flags; + tex.value = q->value; + tex.contents = q->contents; + strcpy (tex.name, q->name); + Texture_SetTexture (&tex, bFitScale,bNoSystemTextureOverwrite); + CString strTex; + CString strName = q->name; + //int nPos = strName.Find('\\'); + //if (nPos == -1) + // nPos = strName.Find('/'); + //if (nPos >= 0) + // strName = strName.Right(strName.GetLength() - nPos - 1); + strTex.Format("%s W: %i H: %i", strName.GetBuffer(0), q->width, q->height); + g_pParentWnd->SetStatusText(3, strTex); + return; + } + } + + Sys_Status("Did not select a texture\n", 0); +} + +/* +============== +Texture_MouseDown +============== +*/ +void Texture_MouseDown (int x, int y, int buttons) +{ + Sys_GetCursorPos (&textures_cursorx, &textures_cursory); + + // lbutton = select texture + if (buttons == MK_LBUTTON || buttons == (MK_LBUTTON | MK_SHIFT) || buttons == (MK_LBUTTON | MK_CONTROL) || buttons == (MK_LBUTTON | MK_CONTROL | MK_SHIFT)) + { + // I now distinguish shift and ctrl from shift&ctrl... + // + bool bShift = (buttons & MK_SHIFT) && !(buttons & MK_CONTROL); + bool bFitScale = (buttons & MK_CONTROL) && !(buttons & MK_SHIFT); + bool bNoSystemTextureOverwrite = (buttons & MK_CONTROL) && (buttons & MK_SHIFT); + + SelectTexture (x, g_qeglobals.d_texturewin.height - 1 - y, bShift, bFitScale, bNoSystemTextureOverwrite); + UpdateSurfaceDialog(); + UpdatePatchInspector(); + } +} + +/* +============== +Texture_MouseUp +============== +*/ +void Texture_MouseUp (int x, int y, int buttons) +{ +} + +/* +============== +Texture_MouseMoved +============== +*/ +void Texture_MouseMoved (int x, int y, int buttons) +{ + int scale = 1; + + if ( buttons & MK_SHIFT ) + scale = 4; + + // rbutton = drag texture origin + if (buttons & MK_RBUTTON) + { + Sys_GetCursorPos (&x, &y); + if ( y != textures_cursory) + { + g_qeglobals.d_texturewin.originy += ( y-textures_cursory) * scale; + if (g_qeglobals.d_texturewin.originy > 0) + g_qeglobals.d_texturewin.originy = 0; + Sys_SetCursorPos (textures_cursorx, textures_cursory); + CWnd *pWnd = CWnd::FromHandle(g_qeglobals.d_hwndTexture); + if (g_PrefsDlg.m_bTextureScrollbar && pWnd != NULL) + { + pWnd->SetScrollPos(SB_VERT, abs(g_qeglobals.d_texturewin.originy)); + } + InvalidateRect(g_qeglobals.d_hwndTexture, NULL, false); + UpdateWindow (g_qeglobals.d_hwndTexture); + } + return; + } +} + + +/* +============================================================================ + +DRAWING + +============================================================================ +*/ + +int imax(int iFloor, int i) { if (i>iFloor) return iFloor; return i; } +HFONT ghFont = NULL; + +/* +============ +Texture_Draw2 +============ +*/ +void Texture_Draw2 (int width, int height) +{ + qtexture_t *q; + int x, y; + char *name; + + qglClearColor ( + g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][0], + g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][1], + g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][2], + 0); + qglViewport (0,0,width,height); + qglMatrixMode(GL_PROJECTION); + qglLoadIdentity (); + + qglClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + qglDisable (GL_DEPTH_TEST); + qglDisable(GL_BLEND); + qglOrtho (0, width, g_qeglobals.d_texturewin.originy-height, g_qeglobals.d_texturewin.originy, -100, 100); + qglEnable (GL_TEXTURE_2D); + + qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + g_qeglobals.d_texturewin.width = width; + g_qeglobals.d_texturewin.height = height; + Texture_StartPos (); + + while (1) + { + q = Texture_NextPos (&x, &y); + if (!q) + break; + + int nWidth = (g_PrefsDlg.m_bHiColorTextures == TRUE) ? q->width * ((float)g_PrefsDlg.m_nTextureScale / 100) : q->width; + int nHeight = (g_PrefsDlg.m_bHiColorTextures == TRUE) ? q->height * ((float)g_PrefsDlg.m_nTextureScale / 100) : q->height; + // Is this texture visible? + if ( (y-nHeight-FONT_HEIGHT < g_qeglobals.d_texturewin.originy) + && (y > g_qeglobals.d_texturewin.originy - height) ) + { + + // if in use, draw a background + if ((q->inuse && !texture_showinuse) || q->bFromShader) + { + qglLineWidth (1); + + if (q->bFromShader) + { + qglColor3f (1,1,1); + } + else + { + qglColor3f (0.5,1,0.5); + } + qglDisable (GL_TEXTURE_2D); + + qglBegin (GL_LINE_LOOP); + qglVertex2f (x-1,y+1-FONT_HEIGHT); + qglVertex2f (x-1,y-nHeight-1-FONT_HEIGHT); + qglVertex2f (x+1+nWidth,y-nHeight-1-FONT_HEIGHT); + qglVertex2f (x+1+nWidth,y+1-FONT_HEIGHT); + qglEnd (); + + qglEnable (GL_TEXTURE_2D); + } + + // Draw the texture + float fScale = (g_PrefsDlg.m_bHiColorTextures == TRUE) ? ((float)g_PrefsDlg.m_nTextureScale / 100) : 1.0; + + qglBindTexture( GL_TEXTURE_2D, q->texture_number ); + QE_CheckOpenGLForErrors(); + qglColor3f (1,1,1); + qglBegin (GL_QUADS); + qglTexCoord2f (0,0); + qglVertex2f (x,y-FONT_HEIGHT); + qglTexCoord2f (1,0); + qglVertex2f (x+nWidth,y-FONT_HEIGHT); + qglTexCoord2f (1,1); + qglVertex2f (x+nWidth,y-FONT_HEIGHT-nHeight); + qglTexCoord2f (0,1); + qglVertex2f (x,y-FONT_HEIGHT-nHeight); + qglEnd (); + + // draw the selection border + if (!strcmpi(g_qeglobals.d_texturewin.texdef.name, q->name)) + { + qglLineWidth (3); + qglColor3f (1,0,0); + qglDisable (GL_TEXTURE_2D); + + qglBegin (GL_LINE_LOOP); + qglVertex2f (x-4,y-FONT_HEIGHT+4); + qglVertex2f (x-4,y-FONT_HEIGHT-nHeight-4); + qglVertex2f (x+4+nWidth,y-FONT_HEIGHT-nHeight-4); + qglVertex2f (x+4+nWidth,y-FONT_HEIGHT+4); + qglEnd (); + + qglEnable (GL_TEXTURE_2D); + qglLineWidth (1); + } + + // draw the texture name + qglColor3f (0,0,0); + + qglRasterPos2f (x, y-FONT_HEIGHT+2); + + // don't draw the directory name + for (name = q->name ; *name && *name != '/' && *name != '\\' ; name++) + ; + if (!*name) + name = q->name; + else + name++; + + if (g_PrefsDlg.m_bHiColorTextures && q->shadername[0] != 0) + { + // slow as shit + CString s = "["; + s += name; + s += "]"; + qglCallLists (s.GetLength(), GL_UNSIGNED_BYTE, s.GetBuffer(0)); + } + else + { + qglCallLists (strlen(name), GL_UNSIGNED_BYTE, name); + } + } + } + + g_qeglobals.d_texturewin.m_nTotalHeight = abs(y) + 100; + // reset the current texture + qglBindTexture( GL_TEXTURE_2D, 0 ); + qglFinish(); +} + +#if 0 +void Texture_LoadColorMap() +{ + char name[1024]; + byte *pal; + + // load the palette + + sprintf (name, "%s/pics/colormap.pcx", ValueForKey (g_qeglobals.d_project_entity, "basepath")); + + Load256Image (name, NULL, &pal, NULL, NULL); + if (!pal) + { + // before dropping out, try to load it from the QERadiant directory + CString strFile = g_strAppPath; + AddSlash(strFile); + strFile += "colormap.pcx"; + Load256Image (strFile.GetBuffer(0), NULL, &pal, NULL, NULL); + if (!pal) + Error ("Couldn't load %s or %s", name, strFile); + } + else + { + Texture_InitPalette (pal); + free (pal); + } +} + + +// +//================== +//Texture_Init +//================== +// +void Texture_Init (bool bHardInit) +{ + Texture_LoadColorMap(); + + // create the fallback texture + Texture_MakeNotexture (); + g_qeglobals.d_qtextures = NULL; +} +#else + +void Texture_Init (bool bHardInit) +{ + char name[1024]; + byte *pal; + + if (g_PrefsDlg.m_bHiColorTextures == FALSE) + { + // load the palette + sprintf (name, "%s/pics/colormap.pcx", ValueForKey (g_qeglobals.d_project_entity, "basepath")); + + Load256Image (name, NULL, &pal, NULL, NULL); + if (!pal) + { + // before dropping out, try to load it from the QERadiant directory + CString strFile = g_strAppPath; + AddSlash(strFile); + strFile += "colormap.pcx"; + Load256Image (strFile.GetBuffer(0), NULL, &pal, NULL, NULL); + if (!pal) + Error ("Couldn't load %s or %s", name, strFile); + } + Texture_InitPalette (pal); + free (pal); + } + + // create the fallback texture + + if (bHardInit) + { + Texture_MakeNotexture(); + g_qeglobals.d_qtextures = NULL; + } + LoadShaders(); + +} + + +#endif + +/* +================== +Texture_Flush +================== +*/ +void Texture_Flush (bool bReload) +{ + if (!ConfirmModified()) + return; + + Map_New (); + CWaitCursor cursor; + CStringList strList; + Texture_Init(false); + + if (g_qeglobals.d_qtextures) + { + qtexture_t* pTex = g_qeglobals.d_qtextures->next; + while (pTex != NULL && pTex != g_qeglobals.d_qtextures) + { + qtexture_t* pNextTex = pTex->next; + if (bReload) + { + if (pTex->name[0] != '(') + { + strList.AddTail(pTex->name); + } + } + free(pTex); + pTex = pNextTex; + } + } + + GLuint* pGln = new GLuint[texture_extension_number-1]; + qglGenTextures(texture_extension_number-1, pGln); + QE_CheckOpenGLForErrors(); + qglDeleteTextures(texture_extension_number-1, pGln); + QE_CheckOpenGLForErrors(); + delete []pGln; + texture_extension_number = 1; + g_qeglobals.d_qtextures = NULL; + + if (bReload) + { + POSITION pos = strList.GetHeadPosition(); + while (pos) + { + CString strTex = strList.GetNext(pos); + Texture_ForName (strTex.GetBuffer(0)); + } + } +} + + + +///////////////////////////////////////////////////////////////////////////// +// CTexWnd +IMPLEMENT_DYNCREATE(CTexWnd, CWnd); + +CTexWnd::CTexWnd() +{ + m_bNeedRange = true; +} + +CTexWnd::~CTexWnd() +{ +} + + +BEGIN_MESSAGE_MAP(CTexWnd, CWnd) + //{{AFX_MSG_MAP(CTexWnd) + ON_WM_CREATE() + ON_WM_SIZE() + ON_WM_PARENTNOTIFY() + ON_WM_TIMER() + ON_WM_KEYDOWN() + ON_WM_KEYUP() + ON_WM_PAINT() + ON_WM_VSCROLL() + ON_COMMAND(ID_TEXTURES_FLUSH, OnTexturesFlush) + ON_BN_CLICKED(1200, OnShaderClick) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +///////////////////////////////////////////////////////////////////////////// +// CTexWnd message handlers + +/* +============ +WTexWndProc +============ +*/ +LONG WINAPI TexWndProc ( + HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + int xPos, yPos; + RECT rect; + + GetClientRect(hWnd, &rect); + + switch (uMsg) + { + case WM_CREATE: + s_hdcTexture = GetDC(hWnd); + QEW_SetupPixelFormat(s_hdcTexture, false); + + if ( ( s_hglrcTexture = qwglCreateContext( s_hdcTexture ) ) == 0 ) + Error( "wglCreateContext in WTex_WndProc failed" ); + + if (!qwglShareLists( g_qeglobals.d_hglrcBase, s_hglrcTexture ) ) + Error( "wglShareLists in WTex_WndProc failed" ); + + if (!qwglMakeCurrent( s_hdcTexture, s_hglrcTexture )) + Error ("wglMakeCurrent in WTex_WndProc failed"); + + g_qeglobals.d_hwndTexture = hWnd; + return 0; + + case WM_DESTROY: + //wglMakeCurrent( NULL, NULL ); + //wglDeleteContext( s_hglrcTexture ); + ReleaseDC( hWnd, s_hdcTexture ); + return 0; +#if 0 + case WM_PAINT: + { + PAINTSTRUCT ps; + + BeginPaint(hWnd, &ps); + + if ( !qwglMakeCurrent( s_hdcTexture, s_hglrcTexture ) ) + //if ( !wglMakeCurrent( ps.hdc, s_hglrcTexture ) ) + { + Sys_Printf("ERROR: wglMakeCurrent failed..\n "); + Sys_Printf("Please restart QERadiant if the Texture view is not working\n"); + } + else + { + Texture_Draw2 (rect.right-rect.left, rect.bottom-rect.top - g_nTextureOffset); + qwglSwapBuffers(s_hdcTexture); +// TRACE("Texture Paint\n"); + } + EndPaint(hWnd, &ps); + } + return 0; +#endif + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_LBUTTONDOWN: + SetCapture( g_qeglobals.d_hwndTexture ); + xPos = (short)LOWORD(lParam); // horizontal position of cursor + yPos = (short)HIWORD(lParam); // vertical position of cursor + + Texture_MouseDown (xPos, yPos - g_nTextureOffset, wParam); + return 0; + + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_LBUTTONUP: + xPos = (short)LOWORD(lParam); // horizontal position of cursor + yPos = (short)HIWORD(lParam); // vertical position of cursor + + Texture_MouseUp (xPos, yPos - g_nTextureOffset, wParam); + if (! (wParam & (MK_LBUTTON|MK_RBUTTON|MK_MBUTTON))) + ReleaseCapture (); + return 0; + + case WM_MOUSEMOVE: + xPos = (short)LOWORD(lParam); // horizontal position of cursor + yPos = (short)HIWORD(lParam); // vertical position of cursor + + Texture_MouseMoved (xPos, yPos - g_nTextureOffset, wParam); + return 0; + } + + return DefWindowProc (hWnd, uMsg, wParam, lParam); +} + + + +BOOL CTexWnd::PreCreateWindow(CREATESTRUCT& cs) +{ + WNDCLASS wc; + HINSTANCE hInstance = AfxGetInstanceHandle(); + if (::GetClassInfo(hInstance, TEXTURE_WINDOW_CLASS, &wc) == FALSE) + { + // Register a new class + memset (&wc, 0, sizeof(wc)); + wc.style = CS_NOCLOSE | CS_OWNDC; + wc.lpszClassName = TEXTURE_WINDOW_CLASS; + wc.hCursor = LoadCursor (NULL,IDC_ARROW); + wc.lpfnWndProc = TexWndProc; + if (AfxRegisterClass(&wc) == FALSE) + Error ("CZWnd RegisterClass: failed"); + } + + cs.lpszClass = TEXTURE_WINDOW_CLASS; + cs.lpszName = "TEX"; + if (cs.style != QE3_CHILDSTYLE && cs.style != QE3_STYLE) + cs.style = QE3_SPLITTER_STYLE; + + return CWnd::PreCreateWindow(cs); +} + +int CTexWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CWnd::OnCreate(lpCreateStruct) == -1) + return -1; + + CRect rctEdit(8, 5, 20, 20); + g_nTextureOffset = 0; + + if (g_PrefsDlg.m_bShaderTest) + { + m_wndShaders.Create("Show Shaders", WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, rctEdit, this, 1200); + m_wndShaders.ModifyStyleEx(0, WS_EX_CLIENTEDGE, 0); + m_wndShaders.SetCheck(g_PrefsDlg.m_bShowShaders); + g_nTextureOffset = 25; + } + + rctEdit.SetRect(8, g_nTextureOffset, 20, 20); + m_wndFilter.Create(WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | ES_LEFT, rctEdit, this, 1201); + m_wndFilter.ModifyStyleEx(0, WS_EX_CLIENTEDGE, 0); + m_wndFilter.SetTexWnd(this); + + g_nTextureOffset += 25; + if (!g_PrefsDlg.m_bTextureWindow) + { + m_wndFilter.ShowWindow(SW_HIDE); + g_nTextureOffset -= 25; + } + + ShowScrollBar(SB_VERT, g_PrefsDlg.m_bTextureScrollbar); + m_bNeedRange = true; + + return 0; +} + +void CTexWnd::OnSize(UINT nType, int cx, int cy) +{ + CWnd::OnSize(nType, cx, cy); + CRect rctClient; + GetClientRect(rctClient); + if (g_PrefsDlg.m_bShaderTest) + { + m_wndShaders.SetWindowPos(NULL, rctClient.left + 8, rctClient.top + 5, rctClient.right - 16, 20, 0); + } + m_wndFilter.SetWindowPos(NULL, rctClient.left + 8, rctClient.top + 25, rctClient.right - 16, 20, 0); + m_bNeedRange = true; +} + +void CTexWnd::OnShaderClick() +{ + g_PrefsDlg.m_bShowShaders = (m_wndShaders.GetCheck() != 0); + g_PrefsDlg.SavePrefs(); + RedrawWindow(); +} + +void CTexWnd::OnParentNotify(UINT message, LPARAM lParam) +{ + CWnd::OnParentNotify(message, lParam); +} + +int g_nLastLen = 0; +int g_nTimerHandle = -1; +char g_cLastChar; + +void CTexWnd::UpdateFilter(const char* pFilter) +{ + if (g_nTimerHandle > 0) + KillTimer(1); + g_bFilterEnabled = false; + if (pFilter) + { + g_strFilter = pFilter; + if (g_strFilter.GetLength() > 0) + { + g_bFilterEnabled = true; + if (g_pParentWnd->CurrentStyle() == QR_QE4 || g_pParentWnd->CurrentStyle() == QR_4WAY) + { + if (g_strFilter.GetLength() > g_nLastLen) + { + g_cLastChar = toupper(g_strFilter.GetAt(g_strFilter.GetLength()-1)); + if (g_cLastChar == 'N' || g_cLastChar == 'O') // one of the other popups + { + g_nTimerHandle = SetTimer(1, 800, NULL); // half second timer + } + } + } + } + g_nLastLen = g_strFilter.GetLength(); + SortTextures(); + } + Sys_UpdateWindows (W_TEXTURE); +} + +void CTexWnd::UpdatePrefs() +{ + if (!g_PrefsDlg.m_bTextureWindow) + { + m_wndFilter.ShowWindow(SW_HIDE); + g_nTextureOffset = 0; + } + else + { + m_wndFilter.ShowWindow(SW_SHOW); + g_nTextureOffset = 25; + } + ShowScrollBar(SB_VERT, g_PrefsDlg.m_bTextureScrollbar); + m_bNeedRange = true; + Invalidate(); + UpdateWindow(); +} + +void CTexWnd::FocusEdit() +{ + if (m_wndFilter.IsWindowVisible()) + m_wndFilter.SetFocus(); +} + +void CTexWnd::OnTimer(UINT nIDEvent) +{ + KillTimer(1); + g_nLastLen = 0; + g_nTimerHandle = -1; + ::SetFocus(g_qeglobals.d_hwndEntity); + ::PostMessage(g_qeglobals.d_hwndEntity, WM_CHAR, g_cLastChar, 0); +} + +void CTexWnd::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + g_pParentWnd->HandleKey(nChar, nRepCnt, nFlags); + //CWnd::OnKeyDown(nChar, nRepCnt, nFlags); +} + +void CTexWnd::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + g_pParentWnd->HandleKey(nChar, nRepCnt, nFlags, false); +} + +void CTexWnd::OnPaint() +{ + CPaintDC dc(this); // device context for painting + CRect rctClient; + GetClientRect(rctClient); + int nOld = g_qeglobals.d_texturewin.m_nTotalHeight; + if (!qwglMakeCurrent(s_hdcTexture, s_hglrcTexture)) + //if ( !qwglMakeCurrent(dc.m_hDC, s_hglrcTexture ) ) + { + Sys_Printf("ERROR: wglMakeCurrent failed..\n "); + Sys_Printf("Please restart QERadiant if the Texture view is not working\n"); + } + else + { + Texture_Draw2 (rctClient.right-rctClient.left, rctClient.bottom-rctClient.top - g_nTextureOffset); + qwglSwapBuffers(s_hdcTexture); +// TRACE("Texture Paint\n"); + } + if (g_PrefsDlg.m_bTextureScrollbar && (m_bNeedRange || g_qeglobals.d_texturewin.m_nTotalHeight != nOld)) + { + m_bNeedRange = false; + SetScrollRange(SB_VERT, 0, g_qeglobals.d_texturewin.m_nTotalHeight, TRUE); + } +} + +void CTexWnd::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) +{ + CWnd::OnVScroll(nSBCode, nPos, pScrollBar); + + int n = GetScrollPos(SB_VERT);; + switch (nSBCode) + { + case SB_LINEUP : + { + n = (n - 15 > 0) ? n - 15 : 0; + break; + } + case SB_LINEDOWN : + { + n = (n + 15 < g_qeglobals.d_texturewin.m_nTotalHeight) ? n + 15 : n; + break; + } + case SB_PAGEUP : + { + n = (n - g_qeglobals.d_texturewin.height > 0) ? n - g_qeglobals.d_texturewin.height : 0; + break; + } + case SB_PAGEDOWN : + { + n = (n + g_qeglobals.d_texturewin.height < g_qeglobals.d_texturewin.m_nTotalHeight) ? n + g_qeglobals.d_texturewin.height : n; + break; + } + case SB_THUMBPOSITION : + { + n = nPos; + break; + } + case SB_THUMBTRACK : + { + n = nPos; + break; + } + } + SetScrollPos(SB_VERT, n); + g_qeglobals.d_texturewin.originy = -((int)n); + Invalidate(); + UpdateWindow(); + //Sys_UpdateWindows(W_TEXTURE); +} + +/* +and are the caps new caps? anything done with older stuff will be fubar'd.. which brings up the point if you ever naturalize a cap, you cannot force it back to cap texturing.. i will add that too +*/ + +void CTexWnd::OnTexturesFlush() +{ + // TODO: Add your command handler code here + +} + +void LoadShaders() +{ + char dirstring[1024]; + char *path; + struct _finddata_t fileinfo; + int handle; + path = ValueForKey (g_qeglobals.d_project_entity, "basepath"); + sprintf (dirstring, "%s/shaders/*.shader", path); +/* + sprintf (dirstring, "%s/shaders/shaderlist.txt", path); + char *pBuff = NULL; + + int nLen = LoadFile(dirstring, reinterpret_cast(&pBuff)); + if (nLen == -1) + { + nLen = PakLoadAnyFile(dirstring, reinterpret_cast(&pBuff)); + } + if (nLen > 0) + { + CStringList lst; + StartTokenParsing(pBuff); + nLen = 0; + while (GetToken(true)) + { + // each token should be a shader filename + sprintf(dirstring, "%s/shaders/%s.shader", path, token); + lst.AddTail(dirstring); + nLen++; + } + POSITION pos = lst.GetHeadPosition(); + while (pos != NULL) + { + LoadShader(lst.GetAt(pos).GetBuffer(0), NULL); + lst.GetNext(pos); + } + free(pBuff); + } + else + { + Sys_Printf("Unable to load shaderlist.txt, shaders not loaded!"); + } +*/ + + handle = _findfirst (dirstring, &fileinfo); + if (handle != -1) + { + do + { + if ((fileinfo.attrib & _A_SUBDIR)) + continue; + sprintf(dirstring, "%s/shaders/%s", path, fileinfo.name); + LoadShader(dirstring, NULL); + } while (_findnext( handle, &fileinfo ) != -1); + + _findclose (handle); + } +} + +void FreeShaders() +{ + int nSize = g_lstShaders.GetSize(); + for (int i = 0; i < nSize; i++) + { + CShaderInfo *pInfo = reinterpret_cast(g_lstShaders.ElementAt(i)); + delete pInfo; + } + + nSize = g_lstDeferred.GetSize(); + for (i = 0; i < nSize; i++) + { + DeferredShaderLoad *p = reinterpret_cast(g_lstDeferred.ElementAt(i)); + delete p; + } + + g_lstShaders.RemoveAll(); + g_lstDeferred.RemoveAll(); +} + +void ReloadShaders() +{ + FreeShaders(); + LoadShaders(); + qtexture_t* pTex = g_qeglobals.d_qtextures; + while (pTex != NULL) + { + SetNameShaderInfo(pTex, NULL, pTex->name); + pTex = pTex->next; + } + +} + +int Texture_LoadSkin(char *pName, int *pnWidth, int *pnHeight, const char *psBasePath) +{ +#if 1 + qtexture_t *pTex = Texture_ForName(Filename_WithoutExt(pName), false, psBasePath); + *pnWidth = pTex->width; + *pnHeight= pTex->height; + + return pTex->texture_number; + +#else + byte *pic = NULL; + byte *pic32 = NULL; + byte *pal; + int nTex = -1; + + if (strstr(pName, ".pcx") != NULL) + { + LoadPCX(pName, &pic, &pal, pnWidth, pnHeight); + int c = (*pnWidth) * (*pnHeight); + pic32 = reinterpret_cast(qmalloc(4 * c )); + byte* out = pic32; + for (int i = 0 ; i < c ; i++) + { + int p = pic[i]; + out[0] = pal[p*3]; + out[1] = pal[p*3 + 1]; + out[2] = pal[p*3 + 2]; + out[3] = 255; + out += 4; + } + free(pic); + free(pal); + } + else if (strstr(pName, ".tga") != NULL) + { + unsigned char* pPixels = NULL; + LoadTGA(pName, &pPixels, pnWidth, pnHeight); + if (pPixels) + { + pic32 = reinterpret_cast(pPixels); + } + } + + if (pic32 != NULL) + { + nTex = texture_extension_number++; + if (g_PrefsDlg.m_bSGIOpenGL) + { + //if (!qwglMakeCurrent(g_qeglobals.d_hdcBase, g_qeglobals.d_hglrcBase)) + if (!qwglMakeCurrent(s_hdcTexture, s_hglrcTexture)) + Error ("wglMakeCurrent in LoadTexture failed"); + } + + qglBindTexture( GL_TEXTURE_2D, nTex); + SetTexParameters (); + + int nCount = MAX_TEXTURE_QUALITY - g_PrefsDlg.m_nTextureQuality; + while (nCount-- > 0) + { + if (*pnWidth > 16 && *pnHeight > 16) + { + R_MipMap(pic32, *pnWidth, *pnHeight); + } + else + { + break; + } + } + + if (g_PrefsDlg.m_bSGIOpenGL) + { + if (nomips) + { + qglTexImage2D(GL_TEXTURE_2D, 0, 3, *pnWidth, *pnHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, pic32); + } + else + qgluBuild2DMipmaps(GL_TEXTURE_2D, 3, *pnWidth, *pnHeight,GL_RGBA, GL_UNSIGNED_BYTE, pic32); + } + else + { + if (nomips) + qglTexImage2D(GL_TEXTURE_2D, 0, 3, *pnWidth, *pnHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, pic32); + else + qgluBuild2DMipmaps(GL_TEXTURE_2D, 3, *pnWidth, *pnHeight,GL_RGBA, GL_UNSIGNED_BYTE, pic32); + } + free (pic32); + qglBindTexture( GL_TEXTURE_2D, 0 ); + } + return nTex; +#endif +} + + diff --git a/utils/Radiant/texwnd.h b/utils/Radiant/texwnd.h new file mode 100644 index 0000000..339fca9 --- /dev/null +++ b/utils/Radiant/texwnd.h @@ -0,0 +1,67 @@ +#if !defined(AFX_TEXWND_H__44B4BA05_781B_11D1_B53C_00AA00A410FC__INCLUDED_) +#define AFX_TEXWND_H__44B4BA05_781B_11D1_B53C_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// TexWnd.h : header file +// +#include "texedit.h" + +///////////////////////////////////////////////////////////////////////////// +// CTexWnd window + +class CTexWnd : public CWnd +{ + DECLARE_DYNCREATE(CTexWnd); +// Construction +public: + CTexWnd(); + void UpdateFilter(const char* pFilter); + void UpdatePrefs(); + void FocusEdit(); + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CTexWnd) + protected: + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CTexWnd(); + +protected: + CTexEdit m_wndFilter; + CButton m_wndShaders; + bool m_bNeedRange; + // Generated message map functions +protected: + //{{AFX_MSG(CTexWnd) + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnParentNotify(UINT message, LPARAM lParam); + afx_msg void OnTimer(UINT nIDEvent); + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnPaint(); + afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + afx_msg void OnTexturesFlush(); + //}}AFX_MSG + afx_msg void OnShaderClick(); + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_TEXWND_H__44B4BA05_781B_11D1_B53C_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/tower.cpp b/utils/Radiant/tower.cpp new file mode 100644 index 0000000..e734f5c --- /dev/null +++ b/utils/Radiant/tower.cpp @@ -0,0 +1,324 @@ +/* + The tower command will take two brushes at different Z's and + create a brush between the two. For this to work, the low Z plane + of the upper brush and the high Z plane of the lower brush need to + have the same number of sides +*/ + +#include "stdafx.h" +#include "tower.h" + +//from Brush.cpp +face_t *Face_Alloc( void ); +winding_t *NewWinding (int points); + +void DoTower(void) +{ + brush_t* brush1 = NULL; + brush_t* brush2 = NULL; + brush_t* newBrush = NULL; + brush_t* tempBrush = NULL; + face_t* topFace = NULL; + face_t* bottomFace = NULL; + face_t* curFace; + face_t* faceList; + int zFlag = 0; + int i = 0; // loop counter + + if (g_qeglobals.d_select_count != 2) + { + Sys_Printf ("Error: you must have exactly 2 brushes selected\n"); + Sys_Beep (); + return; + } + brush1 = selected_brushes.next; + brush2 = selected_brushes.next->next; +// establish brush1 as the upper brush + if (brush2->maxs[2] > brush1->mins[2]) + { + tempBrush = brush1; + brush1 = brush2; + brush2 = tempBrush; + } +// test to insure brushes do not "overlap" in the Z direction + if (brush2->maxs[2] > brush1->mins[2]) + { + Sys_Printf ("Brushes are not separated in the Z direction!"); + Sys_Beep(); + return; + } +// find the bottom Z plane (topFace) in 1 and top Z plane in 2 (bottomFace) + topFace = brush1->brush_faces; + while (topFace != NULL) + { + zFlag = 0; + for (i = 0; i<3; i++) + { + if (abs(topFace->planepts[i][2] - brush1->mins[2]) < TOWER_EPSILON) + { + zFlag++; + } + } + if (zFlag == 3) + { + break; + } + else + { + topFace = topFace->next; + } + } + if (topFace == NULL) + { + Sys_Printf("Couldn't find flat bottom-face in top brush", 0); + Sys_Beep(); + return; + } + bottomFace = brush2->brush_faces; + while (bottomFace != NULL) + { + zFlag = 0; + for (i = 0; i<3; i++) + { + if (abs(bottomFace->planepts[i][2] - brush2->maxs[2]) < TOWER_EPSILON) + { + zFlag++; + } + } + if (zFlag == 3) + { + break; + } + else + { + bottomFace = bottomFace->next; + } + } + if (bottomFace == NULL) + { + Sys_Printf ("Couldn't find flat top-face in bottom brush", 0); + Sys_Beep(); + return; + } +// count vertices on top and bottom planes to make sure they are equal + if (topFace->face_winding->numpoints != bottomFace->face_winding->numpoints) + { + + Sys_Printf ("Top and Bottom faces don't have same #'s of vertices!", 0); + Sys_Beep(); + return; + } +// put top and bottom faces on brush +// reverse winding for top and bottom + faceList = Face_Alloc(); + for ( i = 0; i<3; i++) + { + VectorCopy(topFace->planepts[2-i],faceList->planepts[i]); + } + curFace = Face_Alloc(); + for ( i = 0; i < 3; i++) + { + VectorCopy(bottomFace->planepts[2-i],curFace->planepts[i]); + } + curFace->next = faceList; + faceList = curFace; + + curFace = MakePlaneList(topFace, bottomFace); + if (curFace == NULL) + { + Sys_Printf ("Couldn't make planes for tower!", 0); + Sys_Beep(); + return; + } + else + { + faceList->next->next = curFace; + } + + newBrush = (brush_t*)qmalloc(sizeof(brush_t)); + newBrush->brush_faces = faceList; + Select_Deselect(); + Brush_AddToList (newBrush, &selected_brushes); + + Entity_LinkBrush (world_entity, newBrush); + + Brush_Build(newBrush); +// UNDO_FinishBrushAdd("&Undo Tower"); + Sys_UpdateWindows(W_ALL); + return; + +} + +face_t* MakePlaneList(face_t* top, face_t* bottom) +{ + face_t* fList; + face_t* curFace; + face_t* newTop; + face_t* newBot; + int i; + int j; + int k; + vec3_t t1, t2, t3; + + fList = NULL; + newTop = Face_Alloc(); + CopyFace(top, newTop); + newBot = Face_Alloc(); + CopyFace(bottom, newBot); + WrapFaces(newTop, newBot); + for (i = 0; iface_winding->numpoints; i++) + { + if (i == (newTop->face_winding->numpoints - 1)) + { + j = 0; + } + else + { + j = i + 1; + } + + curFace = Face_Alloc(); + VectorCopy(newTop->face_winding->points[j],curFace->planepts[0]); + VectorCopy(newTop->face_winding->points[i],curFace->planepts[1]); + VectorCopy(newBot->face_winding->points[i],curFace->planepts[2]); + + for (k=0 ; k<3 ; k++) + { + t1[k] = curFace->planepts[0][k] - curFace->planepts[1][k]; + t2[k] = curFace->planepts[2][k] - curFace->planepts[1][k]; + t3[k] = curFace->planepts[1][k]; + } + + CrossProduct(t1,t2, curFace->plane.normal); + if (VectorCompare (curFace->plane.normal, vec3_origin)) + { + printf ("WARNING: brush plane with no normal\n"); + + } + VectorNormalize (curFace->plane.normal); + curFace->plane.dist = DotProduct (t3, curFace->plane.normal); + + curFace->next = fList; + fList = curFace; + } // for loop + return fList; +} + + +void WrapFaces( face_t* top, face_t* bottom) +{ + face_t* tempFace; + int i; + float maxX; + float maxY; + int pointFlag; + + tempFace = Face_Alloc(); + //wrap the top face points the other way + CopyFace(top, tempFace); + for ( i = 0; iface_winding->numpoints; i++) + { + VectorCopy(top->face_winding->points[top->face_winding->numpoints-1-i],tempFace->face_winding->points[i]); + } + CopyFace(tempFace,top); + // top and bottom are now wrapped with normals pointing upward + // now grab the point in top with most positive x (and y if there are more than one) + maxX = top->face_winding->points[0][0]; + maxY = top->face_winding->points[0][1]; + pointFlag = 0; + + for ( i = 1; iface_winding->numpoints; i++) + { + if ( maxX > top->face_winding->points[i][0] ) + { + continue; + } + else + { + if ( maxX == top->face_winding->points[i][0] ) + { + if (top->face_winding->points[i][1] > maxY) + { + maxY = top->face_winding->points[i][1]; + pointFlag = i; + } + } + else + { + maxX = top->face_winding->points[i][0]; + maxY = top->face_winding->points[i][1]; + pointFlag = i; + } + } + } +// now, starting at the point[pointflag] in top, write the sequence starting at [0] in tempFace + for ( i = 0; iface_winding->numpoints; i++) + { + if (pointFlag == top->face_winding->numpoints) + { + pointFlag = 0; + } + VectorCopy(top->face_winding->points[pointFlag], tempFace->face_winding->points[i]); + pointFlag++; + } + CopyFace(tempFace,top); +//repeat with bottom + CopyFace(bottom, tempFace); + maxX = bottom->face_winding->points[0][0]; + maxY = bottom->face_winding->points[0][1]; + pointFlag = 0; + + for ( i = 1; iface_winding->numpoints; i++) + { + if ( maxX > bottom->face_winding->points[i][0] ) + { + continue; + } + else + { + if ( maxX == bottom->face_winding->points[i][0] ) + { + if (bottom->face_winding->points[i][1] > maxY) + { + maxY = bottom->face_winding->points[i][1]; + pointFlag = i; + } + } + else + { + maxX = bottom->face_winding->points[i][0]; + maxY = bottom->face_winding->points[i][1]; + pointFlag = i; + } + } + } +// now, starting at the point[pointflag] in bottom, write the sequence starting at [0] in tempFace + for ( i = 0; iface_winding->numpoints; i++) + { + if (pointFlag == bottom->face_winding->numpoints) + { + pointFlag = 0; + } + VectorCopy(bottom->face_winding->points[pointFlag], tempFace->face_winding->points[i]); + pointFlag++; + } + CopyFace(tempFace,bottom); + Face_Free(tempFace); + return; +} + +void CopyFace( face_t* in, face_t* out) +{ + int i; + + out->face_winding = NewWinding(in->face_winding->numpoints); + out->face_winding->numpoints = in->face_winding->numpoints; + for (i = 0; iface_winding->numpoints; i++) + { + VectorCopy(in->face_winding->points[i],out->face_winding->points[i]); + } + for (i = 0; i<3; i++) + { + VectorCopy(in->planepts[i],out->planepts[i]); + } +} diff --git a/utils/Radiant/tower.h b/utils/Radiant/tower.h new file mode 100644 index 0000000..86584ad --- /dev/null +++ b/utils/Radiant/tower.h @@ -0,0 +1,10 @@ +// tower.h +// routines for tower.c + +#define TOWER_EPSILON 0.001f + + + void DoTower(void); + face_t* MakePlaneList(face_t* top, face_t* bottom); + void WrapFaces( face_t* top, face_t* bottom); + void CopyFace( face_t* in, face_t* out); diff --git a/utils/Radiant/vertsel.cpp b/utils/Radiant/vertsel.cpp new file mode 100644 index 0000000..cef20b2 --- /dev/null +++ b/utils/Radiant/vertsel.cpp @@ -0,0 +1,342 @@ + +#include "stdafx.h" +#include "qe3.h" + +#define NEWEDGESEL 1 + + +int FindPoint (vec3_t point) +{ + int i, j; + + for (i=0 ; i 0.1) + break; + if (j == 3) + return i; + } + + VectorCopy (point, g_qeglobals.d_points[g_qeglobals.d_numpoints]); + g_qeglobals.d_numpoints++; + + return g_qeglobals.d_numpoints-1; +} + +int FindEdge (int p1, int p2, face_t *f) +{ + int i; + + for (i=0 ; inumpoints ; i++) + pnum[i] = FindPoint (w->points[i]); + for (i=0 ; inumpoints ; i++) + FindEdge (pnum[i], pnum[(i+1)%w->numpoints], f); + + free (w); +} + +void SetupVertexSelection (void) +{ + face_t *f; + brush_t *b; + + g_qeglobals.d_numpoints = 0; + g_qeglobals.d_numedges = 0; + +#ifdef NEWEDGESEL + for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + { + for (f=b->brush_faces ; f ; f=f->next) + MakeFace (b,f); + } +#else + if (!QE_SingleBrush()) + return; + + b = selected_brushes.next; + for (f=b->brush_faces ; f ; f=f->next) + MakeFace (b,f); +#endif + +} + + +#ifdef NEWEDGESEL +void SelectFaceEdge (brush_t* b, face_t *f, int p1, int p2) +#else +void SelectFaceEdge (face_t *f, int p1, int p2) +#endif +{ + winding_t *w; + int i, j, k; + int pnum[128]; + +#ifdef NEWEDGESEL + w = MakeFaceWinding (b, f); +#else + w = MakeFaceWinding (selected_brushes.next, f); +#endif + if (!w) + return; + for (i=0 ; inumpoints ; i++) + pnum[i] = FindPoint (w->points[i]); + + for (i=0 ; inumpoints ; i++) + if (pnum[i] == p1 && pnum[(i+1)%w->numpoints] == p2) + { + VectorCopy (g_qeglobals.d_points[pnum[i]], f->planepts[0]); + VectorCopy (g_qeglobals.d_points[pnum[(i+1)%w->numpoints]], f->planepts[1]); + VectorCopy (g_qeglobals.d_points[pnum[(i+2)%w->numpoints]], f->planepts[2]); + for (j=0 ; j<3 ; j++) + { + for (k=0 ; k<3 ; k++) + { + f->planepts[j][k] = floor(f->planepts[j][k]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize; + } + } + + AddPlanept (f->planepts[0]); + AddPlanept (f->planepts[1]); + break; + } + + if (i == w->numpoints) + Sys_Printf ("SelectFaceEdge: failed\n"); + free (w); +} + + +void SelectVertex (int p1) +{ + brush_t *b; + winding_t *w; + int i, j, k; + face_t *f; + +#ifdef NEWEDGESEL + for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + { + for (f=b->brush_faces ; f ; f=f->next) + { + w = MakeFaceWinding (b, f); + if (!w) + continue; + for (i=0 ; inumpoints ; i++) + { + if (FindPoint (w->points[i]) == p1) + { + VectorCopy (w->points[(i+w->numpoints-1)%w->numpoints], f->planepts[0]); + VectorCopy (w->points[i], f->planepts[1]); + VectorCopy (w->points[(i+1)%w->numpoints], f->planepts[2]); + for (j=0 ; j<3 ; j++) + { + for (k=0 ; k<3 ; k++) + { + ;//f->planepts[j][k] = floor(f->planepts[j][k]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize; + } + } + + AddPlanept (f->planepts[1]); + //MessageBeep(-1); + + break; + } + } + free (w); + } + } +#else + b = selected_brushes.next; + for (f=b->brush_faces ; f ; f=f->next) + { + w = MakeFaceWinding (b, f); + if (!w) + continue; + for (i=0 ; inumpoints ; i++) + { + if (FindPoint (w->points[i]) == p1) + { + VectorCopy (w->points[(i+w->numpoints-1)%w->numpoints], f->planepts[0]); + VectorCopy (w->points[i], f->planepts[1]); + VectorCopy (w->points[(i+1)%w->numpoints], f->planepts[2]); + for (j=0 ; j<3 ; j++) + { + for (k=0 ; k<3 ; k++) + { + ;//f->planepts[j][k] = floor(f->planepts[j][k]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize; + } + } + + AddPlanept (f->planepts[1]); + //MessageBeep(-1); + + break; + } + } + free (w); + } +#endif +} + +void SelectEdgeByRay (vec3_t org, vec3_t dir) +{ + int i, j, besti; + float d, bestd; + vec3_t mid, temp; + pedge_t *e; + + // find the edge closest to the ray + besti = -1; + bestd = 8; + + for (i=0 ; inext) + { + SelectFaceEdge (b, e->f1, e->p1, e->p2); + SelectFaceEdge (b, e->f2, e->p2, e->p1); + } +#else + SelectFaceEdge (e->f1, e->p1, e->p2); + SelectFaceEdge (e->f2, e->p2, e->p1); +#endif + + +} + +void SelectVertexByRay (vec3_t org, vec3_t dir) +{ + int i, besti; + float d, bestd; + vec3_t temp; + + // find the point closest to the ray + besti = -1; + bestd = 8; + + for (i=0 ; iActiveXY()->AreaSelectOK()) + { + g_qeglobals.d_select_mode = sel_area; + VectorCopy(org, g_qeglobals.d_vAreaTL); + VectorCopy(org, g_qeglobals.d_vAreaBR); + } + return; + } + //Sys_Printf ("hit vertex\n"); + AddPatchMovePoint(g_qeglobals.d_points[besti], buttons & MK_CONTROL, buttons & MK_SHIFT); +} + + + diff --git a/utils/Radiant/win_dlg.cpp b/utils/Radiant/win_dlg.cpp new file mode 100644 index 0000000..92d1882 --- /dev/null +++ b/utils/Radiant/win_dlg.cpp @@ -0,0 +1,582 @@ +#include "stdafx.h" +#include "qe3.h" +#include "PrefsDlg.h" + +BOOL CALLBACK EditCommandDlgProc ( + HWND hwndDlg, // handle to dialog box + UINT uMsg, // message + WPARAM wParam, // first message parameter + LPARAM lParam // second message parameter + ) +{ + char key[1024]; + char value[1024]; + char *temp; + int index; + HWND hOwner; + + hOwner = GetParent (hwndDlg); + + switch (uMsg) + { + case WM_INITDIALOG: + index = SendDlgItemMessage (hOwner, IDC_CMD_LIST, LB_GETCURSEL, 0, 0); + if (index >= 0) + { + SendDlgItemMessage(hOwner, IDC_CMD_LIST, LB_GETTEXT, index, (LPARAM) (LPCTSTR) key); + temp = ValueForKey (g_qeglobals.d_project_entity, key); + strcpy (value, temp); + SetDlgItemText(hwndDlg, IDC_CMDMENUTEXT, key); + SetDlgItemText(hwndDlg, IDC_CMDCOMMAND, value); + } + return FALSE; + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + if (!GetDlgItemText(hwndDlg, IDC_CMDMENUTEXT, key, 64)) + { + Sys_Printf ("Command not added\n"); + return FALSE; + } + + if (!GetDlgItemText(hwndDlg, IDC_CMDCOMMAND, value, 64)) + { + Sys_Printf ("Command not added\n"); + return FALSE; + } + + //if (key[0] == 'b' && key[1] == 's' && key[2] == 'p') + //{ + SetKeyValue (g_qeglobals.d_project_entity, key, value); + FillBSPMenu (); + //} + //else + // Sys_Printf ("BSP commands must be preceded by \"bsp\""); + + EndDialog(hwndDlg, 1); + return TRUE; + + case IDCANCEL: + EndDialog(hwndDlg, 0); + return TRUE; + } + } + return FALSE; +} + +BOOL CALLBACK AddCommandDlgProc ( + HWND hwndDlg, // handle to dialog box + UINT uMsg, // message + WPARAM wParam, // first message parameter + LPARAM lParam // second message parameter + ) +{ + char key[64]; + char value[128]; + + switch (uMsg) + { + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + if (!GetDlgItemText(hwndDlg, IDC_CMDMENUTEXT, key, 64)) + { + Sys_Printf ("Command not added\n"); + return FALSE; + } + + if (!GetDlgItemText(hwndDlg, IDC_CMDCOMMAND, value, 64)) + { + Sys_Printf ("Command not added\n"); + return FALSE; + } + + if (key[0] == 'b' && key[1] == 's' && key[2] == 'p') + { + SetKeyValue (g_qeglobals.d_project_entity, key, value); + FillBSPMenu (); + } + else + Sys_Printf ("BSP commands must be preceded by \"bsp\""); + + EndDialog(hwndDlg, 1); + return TRUE; + + case IDCANCEL: + EndDialog(hwndDlg, 0); + return TRUE; + } + } + return FALSE; +} + +void UpdateBSPCommandList (HWND hwndDlg) +{ + int i; + epair_t *ep; + + SendDlgItemMessage(hwndDlg, IDC_CMD_LIST, LB_RESETCONTENT, 0 , 0); + + i = 0; + for (ep = g_qeglobals.d_project_entity->epairs ; ep ; ep=ep->next) + { + if (ep->key[0] == 'b' && ep->key[1] == 's' && ep->key[2] == 'p') + { + SendDlgItemMessage(hwndDlg, IDC_CMD_LIST, LB_ADDSTRING, i , (LPARAM) ep->key); + i++; + } + } +} + +BOOL CALLBACK ProjectDlgProc ( + HWND hwndDlg, // handle to dialog box + UINT uMsg, // message + WPARAM wParam, // first message parameter + LPARAM lParam // second message parameter + ) +{ + char key[64]; + char value[128]; + int index; + + switch (uMsg) + { + case WM_INITDIALOG: + SetDlgItemText(hwndDlg, IDC_PRJBASEPATH, ValueForKey (g_qeglobals.d_project_entity, "basepath")); + SetDlgItemText(hwndDlg, IDC_PRJMAPSPATH, ValueForKey (g_qeglobals.d_project_entity, "mapspath")); + SetDlgItemText(hwndDlg, IDC_PRJRSHCMD, ValueForKey (g_qeglobals.d_project_entity, "rshcmd")); + SetDlgItemText(hwndDlg, IDC_PRJREMOTEBASE, ValueForKey (g_qeglobals.d_project_entity, "remotebasepath")); + SetDlgItemText(hwndDlg, IDC_PRJENTITYPATH, ValueForKey (g_qeglobals.d_project_entity, "entitypath")); + SetDlgItemText(hwndDlg, IDC_PRJTEXPATH, ValueForKey (g_qeglobals.d_project_entity, "texturepath")); + + UpdateBSPCommandList (hwndDlg); + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_ADDCMD: +// DialogBox(g_qeglobals.d_hInstance, (char *)IDD_ADDCMD, g_qeglobals.d_hwndMain, AddCommandDlgProc); + DialogBox(g_qeglobals.d_hInstance, (char *)IDD_ADDCMD, hwndDlg, AddCommandDlgProc); + UpdateBSPCommandList (hwndDlg); + break; + + case IDC_EDITCMD: +// DialogBox(g_qeglobals.d_hInstance, (char *)IDD_ADDCMD, g_qeglobals.d_hwndMain, EditCommandDlgProc); + DialogBox(g_qeglobals.d_hInstance, (char *)IDD_ADDCMD, hwndDlg, EditCommandDlgProc); + UpdateBSPCommandList (hwndDlg); + break; + + case IDC_REMCMD: + index = SendDlgItemMessage (hwndDlg, IDC_CMD_LIST, LB_GETCURSEL, 0, 0); + SendDlgItemMessage(hwndDlg, IDC_CMD_LIST, LB_GETTEXT, index, (LPARAM) (LPCTSTR) key); + DeleteKey (g_qeglobals.d_project_entity, key); + Sys_Printf ("Selected %d\n", index); + UpdateBSPCommandList (hwndDlg); + break; + + case IDOK: + GetDlgItemText(hwndDlg, IDC_PRJBASEPATH, value, 128); + SetKeyValue (g_qeglobals.d_project_entity, "basepath", value); + GetDlgItemText(hwndDlg, IDC_PRJMAPSPATH, value, 128); + SetKeyValue (g_qeglobals.d_project_entity, "mapspath", value); + GetDlgItemText(hwndDlg, IDC_PRJRSHCMD, value, 128); + SetKeyValue (g_qeglobals.d_project_entity, "rshcmd", value); + GetDlgItemText(hwndDlg, IDC_PRJREMOTEBASE, value, 128); + SetKeyValue (g_qeglobals.d_project_entity, "remotebasepath", value); + GetDlgItemText(hwndDlg, IDC_PRJENTITYPATH, value, 128); + SetKeyValue (g_qeglobals.d_project_entity, "entitypath", value); + GetDlgItemText(hwndDlg, IDC_PRJTEXPATH, value, 128); + SetKeyValue (g_qeglobals.d_project_entity, "texturepath", value); + EndDialog(hwndDlg, 1); + QE_SaveProject(g_strProject); + return TRUE; + + case IDCANCEL: + EndDialog(hwndDlg, 0); + return TRUE; + } + } + return FALSE; +} + +void DoProjectSettings() +{ + DialogBox(g_qeglobals.d_hInstance, (char *)IDD_PROJECT, g_qeglobals.d_hwndMain, ProjectDlgProc); +} + + + +BOOL CALLBACK GammaDlgProc ( + HWND hwndDlg, // handle to dialog box + UINT uMsg, // message + WPARAM wParam, // first message parameter + LPARAM lParam // second message parameter + ) +{ + char sz[256]; + + switch (uMsg) + { + case WM_INITDIALOG: + sprintf(sz, "%1.1f", g_qeglobals.d_savedinfo.fGamma); + SetWindowText(GetDlgItem(hwndDlg, IDC_G_EDIT), sz); + return TRUE; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + + case IDOK: + GetWindowText(GetDlgItem(hwndDlg, IDC_G_EDIT), sz, 255); + g_qeglobals.d_savedinfo.fGamma = atof(sz); + EndDialog(hwndDlg, 1); + return TRUE; + + case IDCANCEL: + EndDialog(hwndDlg, 0); + return TRUE; + } + } + return FALSE; +} + + + +void DoGamma(void) +{ + char *psz, sz[256]; + if ( DialogBox(g_qeglobals.d_hInstance, (char *)IDD_GAMMA, g_qeglobals.d_hwndMain, GammaDlgProc)) + { + psz = ValueForKey(world_entity, "_wad"); + if (psz) + { + strcpy(sz, psz); + CWaitCursor cursor; + Texture_Flush(true); + Texture_ShowInuse(); + } + } +} + +//================================================ + + +void SelectBrush (int entitynum, int brushnum) +{ + entity_t *e; + brush_t *b; + int i; + + if (entitynum == 0) + e = world_entity; + else + { + e = entities.next; + while (--entitynum) + { + e=e->next; + if (e == &entities) + { + Sys_Status ("No such entity.", 0); + return; + } + } + } + + b = e->brushes.onext; + if (b == &e->brushes) + { + Sys_Status ("No such brush.", 0); + return; + } + while (brushnum--) + { + b=b->onext; + if (b == &e->brushes) + { + Sys_Status ("No such brush.", 0); + return; + } + } + + Brush_RemoveFromList (b); + Brush_AddToList (b, &selected_brushes); + + + Sys_UpdateWindows (W_ALL); + for (i=0 ; i<3 ; i++) + { + if (g_pParentWnd->GetXYWnd()) + g_pParentWnd->GetXYWnd()->GetOrigin()[i] = (b->mins[i] + b->maxs[i])/2; + + if (g_pParentWnd->GetXZWnd()) + g_pParentWnd->GetXZWnd()->GetOrigin()[i] = (b->mins[i] + b->maxs[i])/2; + + if (g_pParentWnd->GetYZWnd()) + g_pParentWnd->GetYZWnd()->GetOrigin()[i] = (b->mins[i] + b->maxs[i])/2; + } + + Sys_Status ("Selected.", 0); +} + +/* +================= +GetSelectionIndex +================= +*/ +void GetSelectionIndex (int *ent, int *brush) +{ + brush_t *b, *b2; + entity_t *entity; + + *ent = *brush = 0; + + b = selected_brushes.next; + if (b == &selected_brushes) + return; + + // find entity + if (b->owner != world_entity) + { + (*ent)++; + for (entity = entities.next ; entity != &entities + ; entity=entity->next, (*ent)++) + ; + } + + // find brush + for (b2=b->owner->brushes.onext + ; b2 != b && b2 != &b->owner->brushes + ; b2=b2->onext, (*brush)++) + ; +} + +BOOL CALLBACK FindBrushDlgProc ( + HWND hwndDlg, // handle to dialog box + UINT uMsg, // message + WPARAM wParam, // first message parameter + LPARAM lParam // second message parameter + ) +{ + char entstr[256]; + char brushstr[256]; + HWND h; + int ent, brush; + + switch (uMsg) + { + case WM_INITDIALOG: + // set entity and brush number + GetSelectionIndex (&ent, &brush); + sprintf (entstr, "%i", ent); + sprintf (brushstr, "%i", brush); + SetWindowText(GetDlgItem(hwndDlg, IDC_FIND_ENTITY), entstr); + SetWindowText(GetDlgItem(hwndDlg, IDC_FIND_BRUSH), brushstr); + + h = GetDlgItem(hwndDlg, IDC_FIND_ENTITY); + SetFocus (h); + return FALSE; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + GetWindowText(GetDlgItem(hwndDlg, IDC_FIND_ENTITY), entstr, 255); + GetWindowText(GetDlgItem(hwndDlg, IDC_FIND_BRUSH), brushstr, 255); + SelectBrush (atoi(entstr), atoi(brushstr)); + EndDialog(hwndDlg, 1); + return TRUE; + + case IDCANCEL: + EndDialog(hwndDlg, 0); + return TRUE; + } + } + return FALSE; +} + + + +void DoFind(void) +{ + DialogBox(g_qeglobals.d_hInstance, (char *)IDD_FINDBRUSH, g_qeglobals.d_hwndMain, FindBrushDlgProc); +} + +/* +=================================================== + + ARBITRARY ROTATE + +=================================================== +*/ + + +BOOL CALLBACK RotateDlgProc ( + HWND hwndDlg, // handle to dialog box + UINT uMsg, // message + WPARAM wParam, // first message parameter + LPARAM lParam // second message parameter + ) +{ + char str[256]; + HWND h; + float v; + + switch (uMsg) + { + case WM_INITDIALOG: + h = GetDlgItem(hwndDlg, IDC_FIND_ENTITY); + SetFocus (h); + return FALSE; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + + case IDOK: + GetWindowText(GetDlgItem(hwndDlg, IDC_ROTX), str, 255); + v = atof(str); + if (v) + Select_RotateAxis (0, v); + + GetWindowText(GetDlgItem(hwndDlg, IDC_ROTY), str, 255); + v = atof(str); + if (v) + Select_RotateAxis (1, v); + + GetWindowText(GetDlgItem(hwndDlg, IDC_ROTZ), str, 255); + v = atof(str); + if (v) + Select_RotateAxis (2, v); + + EndDialog(hwndDlg, 1); + return TRUE; + + case IDCANCEL: + EndDialog(hwndDlg, 0); + return TRUE; + } + } + + return FALSE; +} + + + +void DoRotate(void) +{ + DialogBox(g_qeglobals.d_hInstance, (char *)IDD_ROTATE, g_qeglobals.d_hwndMain, RotateDlgProc); +} + +/* +=================================================== + + ARBITRARY SIDES + +=================================================== +*/ + +bool g_bDoCone = false; +BOOL CALLBACK SidesDlgProc ( + HWND hwndDlg, // handle to dialog box + UINT uMsg, // message + WPARAM wParam, // first message parameter + LPARAM lParam // second message parameter + ) +{ + char str[256]; + HWND h; + + switch (uMsg) + { + case WM_INITDIALOG: + h = GetDlgItem(hwndDlg, IDC_SIDES); + SetFocus (h); + return FALSE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + + case IDOK: + GetWindowText(GetDlgItem(hwndDlg, IDC_SIDES), str, 255); + if (g_bDoCone) + Brush_MakeSidedCone(atoi(str)); + else + Brush_MakeSided (atoi(str)); + + EndDialog(hwndDlg, 1); + break; + + case IDCANCEL: + EndDialog(hwndDlg, 0); + break; + } + default: + return FALSE; + } +} + + + +void DoSides(bool bCone) +{ + g_bDoCone = bCone; + DialogBox(g_qeglobals.d_hInstance, (char *)IDD_SIDES, g_qeglobals.d_hwndMain, SidesDlgProc); +} + +//====================================================================== + +/* +=================== +DoAbout +=================== +*/ +BOOL CALLBACK AboutDlgProc( HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + char renderer[1024]; + char version[1024]; + char vendor[1024]; + char extensions[4096]; + + sprintf( renderer, "Renderer:\t%s", qglGetString( GL_RENDERER ) ); + sprintf( version, "Version:\t\t%s", qglGetString( GL_VERSION ) ); + sprintf( vendor, "Vendor:\t\t%s", qglGetString( GL_VENDOR ) ); + sprintf( extensions, "\n%s", qglGetString( GL_EXTENSIONS ) ); + + SetWindowText( GetDlgItem( hwndDlg, IDC_ABOUT_GLRENDERER ), renderer ); + SetWindowText( GetDlgItem( hwndDlg, IDC_ABOUT_GLVERSION ), version ); + SetWindowText( GetDlgItem( hwndDlg, IDC_ABOUT_GLVENDOR ), vendor ); + SetWindowText( GetDlgItem( hwndDlg, IDC_ABOUT_GLEXTENSIONS ), extensions ); + + } + return TRUE; + + case WM_CLOSE: + EndDialog( hwndDlg, 1 ); + return TRUE; + + case WM_COMMAND: + if ( LOWORD( wParam ) == IDOK ) + EndDialog(hwndDlg, 1); + return TRUE; + } + return FALSE; +} + +void DoAbout(void) +{ + DialogBox( g_qeglobals.d_hInstance, ( char * ) IDD_ABOUT, g_qeglobals.d_hwndMain, AboutDlgProc ); +} + + diff --git a/utils/Radiant/win_ent.cpp b/utils/Radiant/win_ent.cpp new file mode 100644 index 0000000..1e756a5 --- /dev/null +++ b/utils/Radiant/win_ent.cpp @@ -0,0 +1,1301 @@ +#include "stdafx.h" +#include "qe3.h" +#include "entityw.h" +#include "TexWnd.h" +#include "oddbits.h" + +int rgIds[EntLast] = { + IDC_E_LIST, + IDC_E_COMMENT, + IDC_CHECK1, + IDC_CHECK2, + IDC_CHECK3, + IDC_CHECK4, + IDC_CHECK5, + IDC_CHECK6, + IDC_CHECK7, + IDC_CHECK8, + IDC_CHECK9, + IDC_CHECK10, + IDC_CHECK11, + IDC_CHECK12, +#ifdef SOF + IDC_CHECK13, + IDC_CHECK14, + IDC_CHECK15, + IDC_CHECK16, + IDC_CHECK17, + IDC_CHECK18, + IDC_CHECK19, + IDC_CHECK20, + IDC_CHECK21, +#endif + IDC_E_PROPS, + IDC_E_0, + IDC_E_45, + IDC_E_90, + IDC_E_135, + IDC_E_180, + IDC_E_225, + IDC_E_270, + IDC_E_315, + IDC_E_UP, + IDC_E_DOWN, + IDC_E_DELPROP, + + IDC_STATIC_KEY, + IDC_E_KEY_FIELD, + IDC_STATIC_VALUE, + IDC_E_VALUE_FIELD, + + IDC_E_COLOR, + + IDC_BTN_ASSIGNSOUND, + IDC_BTN_ASSIGNMODEL + +}; + +HWND hwndEnt[EntLast]; + +int inspector_mode; // W_TEXTURE, W_ENTITY, or W_CONSOLE + +qboolean multiple_entities; + +entity_t *edit_entity; + + +BOOL CALLBACK EntityWndProc( + HWND hwndDlg, // handle to dialog box + UINT uMsg, // message + WPARAM wParam, // first message parameter + LPARAM lParam); // second message parameter + +void SizeEntityDlg(int iWidth, int iHeight); +void AddProp(); +void GetTexMods(void); + + +LRESULT (CALLBACK* OldFieldWindowProc) (HWND, UINT, WPARAM, LPARAM); +LRESULT (CALLBACK* OldEntityListWindowProc) (HWND, UINT, WPARAM, LPARAM); + +/* +========================= +FieldWndProc + +Just to handle tab and enter... +========================= +*/ +BOOL CALLBACK FieldWndProc( + HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + switch (uMsg) + { + case WM_CHAR: + if (LOWORD(wParam) == VK_TAB) + return FALSE; + if (LOWORD(wParam) == VK_RETURN) + return FALSE; + if (LOWORD(wParam) == VK_ESCAPE) + { + SetFocus (g_qeglobals.d_hwndCamera); + return FALSE; + } + break; + + case WM_KEYDOWN: + if (LOWORD(wParam) == VK_TAB) + { + if (hwnd == hwndEnt[EntKeyField]) + { + SendMessage (hwndEnt[EntValueField], WM_SETTEXT, 0, (long)""); + SetFocus (hwndEnt[EntValueField]); + } + else + SetFocus (hwndEnt[EntKeyField]); + } + if (LOWORD(wParam) == VK_RETURN) + { + if (hwnd == hwndEnt[EntKeyField]) + { + SendMessage (hwndEnt[EntValueField], WM_SETTEXT, 0, (long)""); + SetFocus (hwndEnt[EntValueField]); + } + else + { + AddProp (); + SetFocus (g_qeglobals.d_hwndCamera); + } + } + break; +// case WM_NCHITTEST: + case WM_LBUTTONDOWN: + SetFocus (hwnd); + break; + } + return CallWindowProc (OldFieldWindowProc, hwnd, uMsg, wParam, lParam); +} + + +/* +========================= +EntityListWndProc + +Just to handle enter... +========================= +*/ +BOOL CALLBACK EntityListWndProc( + HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + switch (uMsg) + { + case WM_KEYDOWN: + if (LOWORD(wParam) == VK_RETURN) + { + SendMessage ( g_qeglobals.d_hwndEntity, + WM_COMMAND, + (LBN_DBLCLK<<16) + IDC_E_LIST, + 0 ); + return 0; + } + break; + } + return CallWindowProc (OldEntityListWindowProc, hwnd, uMsg, wParam, lParam); +} + + +/* +================ +GetEntityControls + +Finds the controls from the dialog and +moves them to the window +================ +*/ +void GetEntityControls(HWND ghwndEntity) +{ + int i; + + for (i = 0; i < EntLast; i++) + { + if (i == EntList || i == EntProps || i == EntComment) + continue; + if (i == EntKeyField || i == EntValueField) + continue; + hwndEnt[i] = GetDlgItem(ghwndEntity, rgIds[i]); + if (hwndEnt[i]) + { + SetParent (hwndEnt[i], g_qeglobals.d_hwndEntity ); + SendMessage(hwndEnt[i], WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), (LPARAM)TRUE); + } + } + + + // SetParent apears to not modify some internal state + // on listboxes, so create it from scratch... + + hwndEnt[EntList] = CreateWindow ("listbox", NULL, + LBS_STANDARD | LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT + | WS_VSCROLL | WS_CHILD | WS_VISIBLE, + 5, 5, 180, 99, + g_qeglobals.d_hwndEntity, + (HMENU)IDC_E_LIST, + g_qeglobals.d_hInstance, + NULL); + if (!hwndEnt[EntList]) + Error ("CreateWindow failed"); + + hwndEnt[EntProps] = CreateWindow ("listbox", NULL, + LBS_STANDARD | LBS_NOINTEGRALHEIGHT | LBS_USETABSTOPS + | WS_VSCROLL | WS_CHILD | WS_VISIBLE, + 5, 100, 180, 99, + g_qeglobals.d_hwndEntity, + (HMENU)IDC_E_PROPS, + g_qeglobals.d_hInstance, + NULL); + if (!hwndEnt[EntProps]) + Error ("CreateWindow failed"); + + hwndEnt[EntComment] = CreateWindow ("edit", NULL, + ES_MULTILINE | ES_READONLY | WS_VSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER, + 5, 100, 180, 99, + g_qeglobals.d_hwndEntity, + (HMENU)IDC_E_COMMENT, + g_qeglobals.d_hInstance, + NULL); + if (!hwndEnt[EntComment]) + Error ("CreateWindow failed"); + + hwndEnt[EntKeyField] = CreateWindow ("edit", NULL, + WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, + 5, 100, 180, 99, + g_qeglobals.d_hwndEntity, + (HMENU)IDC_E_KEY_FIELD, + g_qeglobals.d_hInstance, + NULL); + if (!hwndEnt[EntKeyField]) + Error ("CreateWindow failed"); + + hwndEnt[EntValueField] = CreateWindow ("edit", NULL, + WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, + 5, 100, 180, 99, + g_qeglobals.d_hwndEntity, + (HMENU)IDC_E_VALUE_FIELD, + g_qeglobals.d_hInstance, + NULL); + if (!hwndEnt[EntValueField]) + Error ("CreateWindow failed"); + + + if (g_pParentWnd->CurrentStyle() > 0 && g_pParentWnd->CurrentStyle() < 3) + { + g_qeglobals.d_hwndEdit = CreateWindow ("edit", NULL, ES_MULTILINE | ES_READONLY | WS_VSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER, + 5, 100, 180, 99, g_qeglobals.d_hwndEntity, (HMENU)IDC_E_STATUS, + g_qeglobals.d_hInstance, NULL); + if (!g_qeglobals.d_hwndEdit) + Error ("CreateWindow failed"); + } + + SendMessage(hwndEnt[EntList], WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), (LPARAM)TRUE); + SendMessage(hwndEnt[EntProps], WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), (LPARAM)TRUE); + SendMessage(hwndEnt[EntComment], WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), (LPARAM)TRUE); + SendMessage(hwndEnt[EntKeyField], WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), (LPARAM)TRUE); + SendMessage(hwndEnt[EntValueField], WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), (LPARAM)TRUE); + + if (g_pParentWnd->CurrentStyle() > 0 && g_pParentWnd->CurrentStyle() < 3) + SendMessage(g_qeglobals.d_hwndEdit, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), (LPARAM)TRUE); +} + + + +/* +=============================================================== + +ENTITY WINDOW + +=============================================================== +*/ + + +void FillClassList (void) +{ + eclass_t *pec; + int iIndex; + + SendMessage(hwndEnt[EntList], LB_RESETCONTENT, 0 , 0); + + for (pec = eclass ; pec ; pec = pec->next) + { + iIndex = SendMessage(hwndEnt[EntList], LB_ADDSTRING, 0 , (LPARAM)pec->name); + SendMessage(hwndEnt[EntList], LB_SETITEMDATA, iIndex, (LPARAM)pec); + } + +} + + +/* +============== +WEnt_Create +============== +*/ +void WEnt_Create (HINSTANCE hInstance) +{ + WNDCLASS wc; + + /* Register the camera class */ + memset (&wc, 0, sizeof(wc)); + + wc.style = CS_NOCLOSE | CS_OWNDC; + wc.lpfnWndProc = (WNDPROC)EntityWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = 0; + wc.hCursor = LoadCursor (NULL,IDC_ARROW); + wc.hbrBackground = (HBRUSH)GetStockObject (LTGRAY_BRUSH); + wc.lpszMenuName = NULL; + wc.lpszClassName = ENT_WINDOW_CLASS; + + RegisterClass (&wc); + + int nStyle = (g_pParentWnd->CurrentStyle() == QR_QE4) ? QE3_STYLE : QE3_STYLE2; + g_qeglobals.d_hwndEntity = CreateWindow (ENT_WINDOW_CLASS , + "Entity", + nStyle, + 20, + 20, + 100, + 480, // size + + g_qeglobals.d_hwndMain, // parent + 0, // no menu + hInstance, + NULL); + + if (!g_qeglobals.d_hwndEntity ) + Error ("Couldn't create Entity window"); +} + +/* +============== +CreateEntityWindow +============== +*/ +BOOL CreateEntityWindow(HINSTANCE hInstance) +{ + HWND hwndEntityPalette; + + inspector_mode = W_ENTITY; + + WEnt_Create (hInstance); + + hwndEntityPalette = CreateDialog(hInstance, (char *)IDD_ENTITY, g_qeglobals.d_hwndMain, (DLGPROC)NULL); + if (!hwndEntityPalette) + Error ("CreateDialog failed"); + + GetEntityControls (hwndEntityPalette); + DestroyWindow (hwndEntityPalette); + + OldFieldWindowProc = (WNDPROC)GetWindowLong (hwndEnt[EntKeyField], GWL_WNDPROC); + SetWindowLong (hwndEnt[EntKeyField], GWL_WNDPROC, (long)FieldWndProc); + SetWindowLong (hwndEnt[EntValueField], GWL_WNDPROC, (long)FieldWndProc); + + OldEntityListWindowProc = (WNDPROC)GetWindowLong (hwndEnt[EntList], GWL_WNDPROC); + SetWindowLong (hwndEnt[EntList], GWL_WNDPROC, (long)EntityListWndProc); + + FillClassList (); + + + LoadWindowPlacement(g_qeglobals.d_hwndEntity, "EntityWindowPlace"); + ShowWindow (g_qeglobals.d_hwndEntity, SW_HIDE); + SetInspectorMode (W_CONSOLE); + + return TRUE; +} + +/* +============== +SetInspectorMode +============== +*/ +void SetInspectorMode(int iType) +{ + RECT rc; + HMENU hMenu = GetMenu( g_qeglobals.d_hwndMain ); + + if ((g_pParentWnd->CurrentStyle() == QR_SPLIT || g_pParentWnd->CurrentStyle() == QR_SPLITZ) && (iType == W_TEXTURE || iType == W_CONSOLE)) + return; + + + // Is the caller asking us to cycle to the next window? + + if (iType == -1) + { + if (inspector_mode == W_ENTITY) + iType = W_TEXTURE; + else if (inspector_mode == W_TEXTURE) + iType = W_CONSOLE; + else + iType = W_ENTITY; + } + + inspector_mode = iType; + switch(iType) + { + + case W_ENTITY: + SetWindowText(g_qeglobals.d_hwndEntity, "Entity"); + EnableMenuItem( hMenu, ID_MISC_SELECTENTITYCOLOR, MF_ENABLED | MF_BYCOMMAND ); + break; + + case W_TEXTURE: + SetWindowText(g_qeglobals.d_hwndEntity, "Textures"); + g_pParentWnd->GetTexWnd()->FocusEdit(); + EnableMenuItem( hMenu, ID_MISC_SELECTENTITYCOLOR, MF_GRAYED | MF_DISABLED | MF_BYCOMMAND ); + break; + + case W_CONSOLE: + SetWindowText(g_qeglobals.d_hwndEntity, "Console"); + EnableMenuItem( hMenu, ID_MISC_SELECTENTITYCOLOR, MF_GRAYED | MF_DISABLED | MF_BYCOMMAND ); + break; + + default: + break; + } + + GetWindowRect (g_qeglobals.d_hwndEntity, &rc); + SizeEntityDlg( rc.right - rc.left - 8, rc.bottom - rc.top - 32); + + +// InvalidateRect(entwindow, NULL, true); +// ShowWindow (entwindow, SW_SHOW); +// UpdateWindow (entwindow); + + HWND hFlag = (g_pParentWnd->CurrentStyle() == QR_QE4) ? HWND_TOP : HWND_TOPMOST; + SetWindowPos( g_qeglobals.d_hwndEntity, hFlag, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOSIZE | SWP_NOMOVE ); + RedrawWindow (g_qeglobals.d_hwndEntity, NULL, NULL, RDW_ERASE | RDW_INVALIDATE| RDW_ERASENOW | RDW_UPDATENOW | RDW_ALLCHILDREN); +} + + + + + +// SetKeyValuePairs +// +// Reset the key/value (aka property) listbox and fill it with the +// k/v pairs from the entity being edited. +// + +void SetKeyValuePairs (bool bClearMD3) +{ + epair_t *pep; + RECT rc; + char sz[4096]; + + if (edit_entity == NULL) + return; + + // set key/value pair list + + GetWindowRect(hwndEnt[EntProps], &rc); + SendMessage(hwndEnt[EntProps], LB_SETCOLUMNWIDTH, (rc.right - rc.left)/2, 0); + SendMessage(hwndEnt[EntProps], LB_RESETCONTENT, 0, 0); + + // Walk through list and add pairs + + for (pep = edit_entity->epairs ; pep ; pep = pep->next) + { + // if the key is less than 8 chars, add a tab for alignment + if (strlen(pep->key) > 8) + sprintf (sz, "%s\t%s", pep->key, pep->value); + else + sprintf (sz, "%s\t\t%s", pep->key, pep->value); + SendMessage(hwndEnt[EntProps], LB_ADDSTRING, 0, (LPARAM)sz); + } + + if (strnicmp(edit_entity->eclass->name, "misc_model",10) == 0) + { + // if this is a misc_model + // cache the md3 for display later + if (bClearMD3) + { + edit_entity->md3Class = NULL; + } + //char *pModel = ValueForKey(edit_entity, "model"); + +/* + if (pModel != NULL) + { + GetCachedModel(pModel, vMin, vMax); + } +*/ + } + Sys_UpdateWindows(W_CAMERA | W_XY); + +} + +// SetSpawnFlags +// +// Update the checkboxes to reflect the flag state of the entity +// +void SetSpawnFlags(void) +{ + int f; + int i; + int v; + + f = atoi(ValueForKey (edit_entity, "spawnflags")); + for (i=0 ; inext) + SetKeyValue(b->owner, "spawnflags", sz); + } + else + SetKeyValue (edit_entity, "spawnflags", sz); + SetKeyValuePairs (); +} + +// UpdateSel +// +// Update the listbox, checkboxes and k/v pairs to reflect the new selection +// + +BOOL UpdateSel(int iIndex, eclass_t *pec) +{ + int i; + brush_t *b; + + if (selected_brushes.next == &selected_brushes) + { + edit_entity = world_entity; + multiple_entities = false; + } + else + { + edit_entity = selected_brushes.next->owner; + for (b=selected_brushes.next->next ; b != &selected_brushes ; b=b->next) + { + if (b->owner != edit_entity) + { + multiple_entities = true; + break; + } + } + } + + if (iIndex != LB_ERR) + SendMessage(hwndEnt[EntList], LB_SETCURSEL, iIndex, 0); + + if (pec == NULL) + return TRUE; + + // Set up the description + + SendMessage(hwndEnt[EntComment], WM_SETTEXT, 0, + (LPARAM)TranslateString(pec->comments)); + + for (i=0 ; iflagnames[i] && pec->flagnames[i][0] != 0) + { + EnableWindow(hwnd, TRUE); + SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)pec->flagnames[i]); + } else { + + // disable check box + SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)" "); + EnableWindow(hwnd, FALSE); + } + } + + SetSpawnFlags(); + SetKeyValuePairs(); + return TRUE; +} + +BOOL UpdateEntitySel(eclass_t *pec) +{ + int iIndex; + + iIndex = (int)SendMessage(hwndEnt[EntList], LB_FINDSTRINGEXACT, + (WPARAM)-1, (LPARAM)pec->name); + + return UpdateSel(iIndex, pec); +} + +// CreateEntity +// +// Creates a new entity based on the currently selected brush and entity type. +// + +void CreateEntity(void) +{ + eclass_t *pecNew; + entity_t *petNew; + int i; + HWND hwnd; + char sz[1024]; + + // check to make sure we have a brush + + if (selected_brushes.next == &selected_brushes) + { + MessageBox(g_qeglobals.d_hwndMain, "You must have a selected brush to create an entity" + , "info", 0); + return; + } + + + // find out what type of entity we are trying to create + + hwnd = hwndEnt[EntList]; + + i = SendMessage(hwndEnt[EntList], LB_GETCURSEL, 0, 0); + + if (i < 0) + { + MessageBox(g_qeglobals.d_hwndMain, "You must have a selected class to create an entity" + , "info", 0); + return; + } + + SendMessage(hwnd, LB_GETTEXT, i, (LPARAM)sz); + + if (!stricmp(sz, "worldspawn")) + { + MessageBox(g_qeglobals.d_hwndMain, "Can't create an entity with worldspawn.", "info", 0); + return; + } + + pecNew = Eclass_ForName(sz, false); + + // create it + + petNew = Entity_Create(pecNew); + + if (petNew == NULL) + { + MessageBox(g_qeglobals.d_hwndMain, "Failed to create entity.", "info", 0); + return; + } + + if (selected_brushes.next == &selected_brushes) + edit_entity = world_entity; + else + edit_entity = selected_brushes.next->owner; + + SetKeyValuePairs(); + Select_Deselect (); + Select_Brush (edit_entity->brushes.onext); + Sys_UpdateWindows(W_ALL); + +} + + + +/* +=============== +AddProp + +=============== +*/ +void AddProp() +{ + char key[4096]; + char value[4096]; + + if (edit_entity == NULL) + return; + + // Get current selection text + + SendMessage(hwndEnt[EntKeyField], WM_GETTEXT, sizeof(key)-1, (LPARAM)key); + SendMessage(hwndEnt[EntValueField], WM_GETTEXT, sizeof(value)-1, (LPARAM)value); + + if (multiple_entities) + { + brush_t *b; + + for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + SetKeyValue(b->owner, key, value); + } + else + SetKeyValue(edit_entity, key, value); + + // refresh the prop listbox + + SetKeyValuePairs(); +} + +/* +=============== +DelProp + +=============== +*/ +void DelProp(void) +{ + char sz[4096]; + + if (edit_entity == NULL) + return; + + // Get current selection text + + SendMessage(hwndEnt[EntKeyField], WM_GETTEXT, sizeof(sz)-1, (LPARAM)sz); + + if (multiple_entities) + { + brush_t *b; + + for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + DeleteKey(b->owner, sz); + } + else + DeleteKey(edit_entity, sz); + + // refresh the prop listbox + + SetKeyValuePairs(); +} + +/* +=============== +EditProp + +=============== +*/ +void EditProp(void) +{ + int i; + HWND hwnd; + char sz[4096]; + char *val; + + if (edit_entity == NULL) + return; + + hwnd = hwndEnt[EntProps]; + + // Get current selection text + + i = SendMessage(hwnd, LB_GETCURSEL, 0, 0); + + if (i < 0) + return; + + SendMessage(hwnd, LB_GETTEXT, i, (LPARAM)sz); + + // strip it down to the key name + + for(i=0;sz[i] != '\t';i++) + ; + + sz[i] = '\0'; + + val = sz + i + 1; + if (*val == '\t') + val++; + + SendMessage(hwndEnt[EntKeyField], WM_SETTEXT, 0, (LPARAM)sz); + SendMessage(hwndEnt[EntValueField], WM_SETTEXT, 0, (LPARAM)val); +} + + +HDWP defer; +int col; +void MOVE(HWND e, int x, int y, int w, int h) +{ +// defer=DeferWindowPos(defer,e,HWND_TOP,col+(x),y,w,h,SWP_SHOWWINDOW); +// MoveWindow (e, col+x, y, w, h, FALSE); + SetWindowPos (e, HWND_TOP, col+x, y, w, h, + SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOZORDER); +} + + +/* +=============== +SizeEnitityDlg + +Positions all controls so that the active inspector +is displayed correctly and the inactive ones are +off the side +=============== +*/ +void SizeEntityDlg(int iWidth, int iHeight) +{ + int y, x, xCheck, yCheck; + int i, iRow; + int w, h; + + if (iWidth < 32 || iHeight < 32) + return; + + SendMessage( g_qeglobals.d_hwndEntity, WM_SETREDRAW, 0, 0); + + //========================================== + + // + // console + // + + if (inspector_mode == W_CONSOLE) + col = 0; + else + col = iWidth; + + if (g_pParentWnd->CurrentStyle() > 0 && g_pParentWnd->CurrentStyle() < 3) + MOVE(g_qeglobals.d_hwndEdit, DlgXBorder, DlgYBorder, iWidth - (2 * DlgXBorder), iHeight - (2 * DlgYBorder) ); + + //========================================== + + // + // texture controls + // + if (inspector_mode == W_TEXTURE) + col = 0; + else + col = iWidth; + + if (g_pParentWnd->CurrentStyle() > 0 && g_pParentWnd->CurrentStyle() < 3) + MOVE(g_qeglobals.d_hwndTexture, DlgXBorder, DlgYBorder, iWidth - (2 * DlgXBorder), iHeight - (2 * DlgYBorder) ); + + //========================================== + + // + // entity controls + // + if (inspector_mode == W_ENTITY) + col = 0; + else + col = iWidth; + + + // top half includes the entity list (2/3) and the + // comments (1/3) - 2 gaps, above and below. + + y = iHeight/2; + y -= 2 * DlgYBorder; + y = y / 3; + w = iWidth - (2 * DlgXBorder); + MOVE(hwndEnt[EntList], DlgXBorder, DlgYBorder, w, 2 * y); + + MOVE(hwndEnt[EntComment], DlgXBorder, 2 * DlgYBorder + 2 * y, w, y - (2 * DlgYBorder)); + + // bottom half includes flags (fixed), k/v pairs, + // and buttons (fixed). + + // xCheck = width of a single check box + // yCheck = distance from top of one check to the next + + xCheck = (iWidth - (2 * DlgXBorder)) / 3; + yCheck = 20; + + x = DlgXBorder; + + for (iRow = 0; iRow <= MAX_FLAGS; iRow += COLUMN_SIZE) + { + y = iHeight/2; + + for (i = 0; i < COLUMN_SIZE; i++) + { + MOVE(hwndEnt[EntCheck1 + i + iRow], + x, y, xCheck, yCheck); + y += yCheck; + } + + x += xCheck; + } + + // + // properties scroll box + // + y = iHeight/2 + COLUMN_SIZE * yCheck; + + w = iWidth - (2 * DlgXBorder); + h = (iHeight - (yCheck * 5 + 2 * DlgYBorder) ) - y; + + MOVE(hwndEnt[EntProps], DlgXBorder, y, w, h); + + y += h + DlgYBorder; + + // + // key / value fields + // + w = iWidth-(DlgXBorder+45); + MOVE(hwndEnt[EntKeyLabel], DlgXBorder, y, 40, yCheck); + MOVE(hwndEnt[EntKeyField], DlgXBorder+40, y, w, yCheck); + y += yCheck; + + MOVE(hwndEnt[EntValueLabel], DlgXBorder, y, 40, yCheck); + MOVE(hwndEnt[EntValueField], DlgXBorder+40, y, w, yCheck); + y += yCheck; + + // + // angle check boxes + // + y += 2; + i = y; + x = DlgXBorder; + + xCheck = yCheck*2; + + MOVE(hwndEnt[EntDir135], x, y, xCheck, yCheck); + y += yCheck; + + MOVE(hwndEnt[EntDir180], x, y, xCheck, yCheck); + y += yCheck; + + MOVE(hwndEnt[EntDir225], x, y, xCheck, yCheck); + + y = i; + x += xCheck; + + + MOVE(hwndEnt[EntDir90], x, y, xCheck, yCheck); + y += yCheck; + y += yCheck; + + MOVE(hwndEnt[EntDir270], x, y, xCheck, yCheck); + + y = i; + x += xCheck; + + + MOVE(hwndEnt[EntDir45], x, y, xCheck, yCheck); + y += yCheck; + + MOVE(hwndEnt[EntDir0], x, y, xCheck, yCheck); + y += yCheck; + + MOVE(hwndEnt[EntDir315], x, y, xCheck, yCheck); + + y = i + yCheck/2; + x += xCheck + xCheck/2; + + + MOVE(hwndEnt[EntDirUp], x, y, xCheck, yCheck); + y += yCheck; + + MOVE(hwndEnt[EntDirDown], x, y, xCheck, yCheck); + + y = i; + x += 1.5 * xCheck; + + MOVE(hwndEnt[EntDelProp], x, y, xCheck*2, yCheck); + y += yCheck + 4; + + MOVE(hwndEnt[EntAssignSounds], x, y, xCheck*2, yCheck); + y += yCheck; + MOVE(hwndEnt[EntAssignModels], x, y, xCheck*2, yCheck); + + SendMessage( g_qeglobals.d_hwndEntity, WM_SETREDRAW, 1, 0); +// InvalidateRect(entwindow, NULL, TRUE); +} + +void AssignSound() +{ + CString strBasePath = ValueForKey(g_qeglobals.d_project_entity, "basepath"); + AddSlash(strBasePath); + CString strPath = strBasePath; + strPath += "sound\\"; + + CFileDialog dlgFile(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "Sound files (*.wav,*.mp3)|*.wav;*.mp3||", g_pParentWnd); + dlgFile.m_ofn.lpstrInitialDir = strPath; + if (dlgFile.DoModal() == IDOK) + { + SendMessage(hwndEnt[EntKeyField], WM_SETTEXT, 0, (LPARAM)"noise"); + CString str = dlgFile.GetPathName().GetBuffer(0); + str.MakeLower(); + strBasePath.MakeLower(); + QE_ConvertDOSToUnixName(str.GetBuffer(0), str.GetBuffer(0)); + QE_ConvertDOSToUnixName(strBasePath.GetBuffer(0), strBasePath.GetBuffer(0)); + int n = str.Find(strBasePath); + if (n == 0) + { + str = str.Right(str.GetLength() - strBasePath.GetLength()); + } + + SendMessage(hwndEnt[EntValueField], WM_SETTEXT, 0, (LPARAM)str.GetBuffer(0)); + AddProp(); + g_pParentWnd->GetXYWnd()->SetFocus(); + } +} + +#ifdef QUAKE3 +CString strASSIGNMODEL; +#endif + +void AssignModel() +{ +#ifdef QUAKE3 + if (strASSIGNMODEL.GetLength()) + { + SendMessage(hwndEnt[EntKeyField], WM_SETTEXT, 0, (LPARAM)"model"); + SendMessage(hwndEnt[EntValueField], WM_SETTEXT, 0, (LPARAM) ((LPCSTR)strASSIGNMODEL));//.GetBuffer(0)); + strASSIGNMODEL=""; + gEntityToSetBoundsOf = edit_entity; + AddProp(); + edit_entity->md3Class = NULL; + g_pParentWnd->GetXYWnd()->SetFocus(); + +//----------- +#if 1 + // brush is currently built at 0 angle, now fake a rotate away and back to fill inthe mins/maxs fields... + // + SetKeyValue(edit_entity, "angle", "0", false); // tell it that it has no angle, and don't rebuild + SetFocus (g_qeglobals.d_hwndCamera); + SetKeyValuePairs (); + + SetKeyValue(edit_entity, "angle", "90", true); // true = rebuild brush, rotate to some other angle + SetFocus (g_qeglobals.d_hwndCamera); + SetKeyValuePairs (); + + + SetKeyValue(edit_entity, "angle", "0", true); // true = rebuild brush, rotate back to 0 + SetFocus (g_qeglobals.d_hwndCamera); + SetKeyValuePairs (); +#endif +//----------- + } + else +#endif + { + CString strBasePath = ValueForKey(g_qeglobals.d_project_entity, "basepath"); + AddSlash(strBasePath); + CString strPath = strBasePath; + strPath += "models\\mapobjects\\"; + + CFileDialog dlgFile(TRUE, NULL, NULL, OFN_OVERWRITEPROMPT, "Model files (*.md3)|*.md3||", g_pParentWnd); + dlgFile.m_ofn.lpstrInitialDir = strPath; + if (dlgFile.DoModal() == IDOK) + { + SendMessage(hwndEnt[EntKeyField], WM_SETTEXT, 0, (LPARAM)"model"); + CString str = dlgFile.GetPathName().GetBuffer(0); + str.MakeLower(); + strBasePath.MakeLower(); + QE_ConvertDOSToUnixName(str.GetBuffer(0), str.GetBuffer(0)); + QE_ConvertDOSToUnixName(strBasePath.GetBuffer(0), strBasePath.GetBuffer(0)); + int n = str.Find(strBasePath); + if (n == 0) + { + str = str.Right(str.GetLength() - strBasePath.GetLength()); + } + + SendMessage(hwndEnt[EntValueField], WM_SETTEXT, 0, (LPARAM)str.GetBuffer(0)); + gEntityToSetBoundsOf = edit_entity; + AddProp(); + edit_entity->md3Class = NULL; + g_pParentWnd->GetXYWnd()->SetFocus(); + } + } +} + + +/* +========================= +EntityWndProc +========================= +*/ +BOOL CALLBACK EntityWndProc( + HWND hwndDlg, // handle to dialog box + UINT uMsg, // message + WPARAM wParam, // first message parameter + LPARAM lParam) // second message parameter +{ + RECT rc; + + GetClientRect(hwndDlg, &rc); + + switch (uMsg) + { + + case WM_CHAR : + { + char c = toupper(LOWORD(wParam)); + if (c == 'N') + g_pParentWnd->PostMessage(WM_COMMAND, ID_VIEW_ENTITY, 0); + else + if (c == 'O') + g_pParentWnd->PostMessage(WM_COMMAND, ID_VIEW_CONSOLE, 0); + else + if (c == 'T') + g_pParentWnd->PostMessage(WM_COMMAND, ID_VIEW_TEXTURE, 0); + else + DefWindowProc (hwndDlg, uMsg, wParam, lParam); + break; + } + + case WM_SIZE: + + DefWindowProc (hwndDlg, uMsg, wParam, lParam); + break; + + case WM_DESTROY: + SaveWindowPlacement(g_qeglobals.d_hwndEntity, "EntityWindowPlace"); + DefWindowProc(hwndDlg, uMsg, wParam, lParam); + break; + + case WM_GETMINMAXINFO: + { + LPMINMAXINFO lpmmi; + + lpmmi = (LPMINMAXINFO) lParam; + lpmmi->ptMinTrackSize.x = 320; + lpmmi->ptMinTrackSize.y = 700; + } + return 0; + + case WM_WINDOWPOSCHANGING: + { + LPWINDOWPOS lpwp; + lpwp = (LPWINDOWPOS) lParam; + + DefWindowProc (hwndDlg, uMsg, wParam, lParam); + + lpwp->flags |= SWP_NOCOPYBITS; + SizeEntityDlg(lpwp->cx-8, lpwp->cy-32); + return 0; + + } + return 0; + + + case WM_COMMAND: + switch (LOWORD(wParam)) { + + case IDC_BTN_ASSIGNSOUND: + AssignSound(); + break; + + case IDC_BTN_ASSIGNMODEL: + AssignModel(); + break; + + case IDC_E_DELPROP: + DelProp(); + SetFocus (g_qeglobals.d_hwndCamera); + break; + + case IDC_E_0: + SetKeyValue (edit_entity, "angle", "360"); + SetFocus (g_qeglobals.d_hwndCamera); + SetKeyValuePairs (); + break; + case IDC_E_45: + SetKeyValue (edit_entity, "angle", "45"); + SetFocus (g_qeglobals.d_hwndCamera); + SetKeyValuePairs (); + break; + case IDC_E_90: + SetKeyValue (edit_entity, "angle", "90"); + SetFocus (g_qeglobals.d_hwndCamera); + SetKeyValuePairs (); + break; + case IDC_E_135: + SetKeyValue (edit_entity, "angle", "135"); + SetFocus (g_qeglobals.d_hwndCamera); + SetKeyValuePairs (); + break; + case IDC_E_180: + SetKeyValue (edit_entity, "angle", "180"); + SetFocus (g_qeglobals.d_hwndCamera); + SetKeyValuePairs (); + break; + case IDC_E_225: + SetKeyValue (edit_entity, "angle", "225"); + SetFocus (g_qeglobals.d_hwndCamera); + SetKeyValuePairs (); + break; + case IDC_E_270: + SetKeyValue (edit_entity, "angle", "270"); + SetFocus (g_qeglobals.d_hwndCamera); + SetKeyValuePairs (); + break; + case IDC_E_315: + SetKeyValue (edit_entity, "angle", "315"); + SetFocus (g_qeglobals.d_hwndCamera); + SetKeyValuePairs (); + break; + case IDC_E_UP: + SetKeyValue (edit_entity, "angle", "-1"); + SetFocus (g_qeglobals.d_hwndCamera); + SetKeyValuePairs (); + break; + case IDC_E_DOWN: + SetKeyValue (edit_entity, "angle", "-2"); + SetFocus (g_qeglobals.d_hwndCamera); + SetKeyValuePairs (); + break; + + case IDC_BTN_HIDE: + ::PostMessage(g_qeglobals.d_hwndMain, WM_COMMAND, ID_VIEW_CAMERATOGGLE, 0); + break; + + case IDC_CHECK1: + case IDC_CHECK2: + case IDC_CHECK3: + case IDC_CHECK4: + case IDC_CHECK5: + case IDC_CHECK6: + case IDC_CHECK7: + case IDC_CHECK8: + case IDC_CHECK9: + case IDC_CHECK10: + case IDC_CHECK11: + case IDC_CHECK12: + case IDC_CHECK13: + case IDC_CHECK14: + case IDC_CHECK15: + case IDC_CHECK16: + case IDC_CHECK17: + case IDC_CHECK18: + case IDC_CHECK19: + case IDC_CHECK20: + case IDC_CHECK21: + GetSpawnFlags(); + SetFocus (g_qeglobals.d_hwndCamera); + break; + + + case IDC_E_PROPS: + switch (HIWORD(wParam)) + { + case LBN_SELCHANGE: + + EditProp(); + return TRUE; + } + break; + + case IDC_E_LIST: + + switch (HIWORD(wParam)) { + + case LBN_SELCHANGE: + { + int iIndex; + eclass_t *pec; + + iIndex = SendMessage(hwndEnt[EntList], LB_GETCURSEL, 0, 0); + pec = (eclass_t *)SendMessage(hwndEnt[EntList], LB_GETITEMDATA, + iIndex, 0); + + UpdateSel(iIndex, pec); + + return TRUE; + break; + } + + case LBN_DBLCLK: + CreateEntity (); + SetFocus (g_qeglobals.d_hwndCamera); + break; + } + break; + + + default: + return DefWindowProc( hwndDlg, uMsg, wParam, lParam ); + } + + return 0; + } + + return DefWindowProc (hwndDlg, uMsg, wParam, lParam); +} diff --git a/utils/Radiant/win_main.cpp b/utils/Radiant/win_main.cpp new file mode 100644 index 0000000..70ca506 --- /dev/null +++ b/utils/Radiant/win_main.cpp @@ -0,0 +1,932 @@ +#include "stdafx.h" +#include "qe3.h" +#include +#include "mru.h" +#include "entityw.h" +#include "PrefsDlg.h" +#include "oddbits.h" + + +static HWND s_hwndToolbar; + +BOOL SaveRegistryInfo(const char *pszName, void *pvBuf, long lSize); +BOOL LoadRegistryInfo(const char *pszName, void *pvBuf, long *plSize); + +static HWND CreateMyStatusWindow(HINSTANCE hInst); +static HWND CreateToolBar(HINSTANCE hinst); + +extern void WXY_Print( void ); + +/* +============================================================================== + + MENU + +============================================================================== +*/ + +void OpenDialog (void); +void SaveAsDialog (bool bRegion); +qboolean ConfirmModified (void); +void Select_Ungroup (void); + +void QE_ExpandBspString (char *bspaction, char *out, char *mapname) +{ + char *in; + char src[1024]; + char rsh[1024]; + char base[256]; + + strcpy(src, mapname); + strlwr(src); + in = strstr(src, "maps/"); + if (!in) + { + in = strstr(src, "maps\\"); + } + if (in) + { + in += 5; + strcpy(base, in); + in = base; + while (*in) + { + if (*in == '\\') + { + *in = '/'; + } + in++; + } + } + else + { + ExtractFileName (mapname, base); + } + + sprintf (src, "%s/maps/%s", ValueForKey(g_qeglobals.d_project_entity, "remotebasepath"), base); + strcpy (rsh, ValueForKey(g_qeglobals.d_project_entity, "rshcmd")); + + in = ValueForKey( g_qeglobals.d_project_entity, bspaction ); + while (*in) + { + if (in[0] == '!') + { + strcpy (out, rsh); + out += strlen(rsh); + in++; + continue; + } + if (in[0] == '$') + { + strcpy (out, src); + out += strlen(src); + in++; + continue; + } + if (in[0] == '@') + { + *out++ = '"'; + in++; + continue; + } + *out++ = *in++; + } + *out = 0; +} + +void FindReplace(CString& strContents, const char* pTag, const char* pValue) +{ + if (strcmp(pTag, pValue) == 0) + return; + for (int nPos = strContents.Find(pTag); nPos >= 0; nPos = strContents.Find(pTag)) + { + int nRightLen = strContents.GetLength() - strlen(pTag) - nPos; + CString strLeft = strContents.Left(nPos); + CString strRight = strContents.Right(nRightLen); + strLeft += pValue; + strLeft += strRight; + strContents = strLeft; + } +} + + + +HWND g_hWnd = NULL; +HANDLE g_hToolThread = NULL; +CString g_strParams; + +UINT ToolThread(LPVOID pParam) +{ + char* p = reinterpret_cast(pParam); + if (g_PrefsDlg.m_bPAK) + RunTools(p, g_hWnd, g_PrefsDlg.m_strPAKFile); + else + RunTools(p, g_hWnd, ""); + g_hToolThread = NULL; + delete []p; + return 0; +} + +void ThreadTools(char* p) +{ + CWinThread* pThread = AfxBeginThread(ToolThread, reinterpret_cast(p)); + g_hToolThread = pThread->m_hThread; +} + + + +CTime g_tBegin; +void RunBsp (char *command) +{ + char sys[1024]; + char batpath[1024]; + char outputpath[1024]; + char temppath[512]; + char name[1024]; + FILE *hFile; + BOOL ret; + PROCESS_INFORMATION ProcessInformation; + STARTUPINFO startupinfo; + + g_hWnd = g_pParentWnd->GetSafeHwnd(); + SetInspectorMode(W_CONSOLE); + g_tBegin = CTime::GetCurrentTime(); + + + DWORD dwExitcode; + ret = GetExitCodeProcess (g_hToolThread, &dwExitcode); + if (dwExitcode != STILL_ACTIVE) + g_hToolThread = NULL; + + if (bsp_process || g_hToolThread) + { + Sys_Printf ("BSP is still going...\n"); + return; + } + + GetTempPath(512, temppath); + sprintf (outputpath, "%sjunk.txt", temppath); + + strcpy (name, currentmap); + if (region_active) + { + Map_SaveFile (name, false); + StripExtension (name); + strcat (name, ".reg"); + } + + if (Map_SaveFile (name, region_active)) + { + QE_ExpandBspString (command, sys, name); + + + CString strSys = sys; + FindReplace(strSys, "&&", "\r\n"); + strcpy(sys, strSys); + + if (g_PrefsDlg.m_bInternalBSP) + { + g_tBegin = CTime::GetCurrentTime(); + strSys.MakeLower(); + char* p = new char[strSys.GetLength()+1]; + strcpy(p, strSys.GetBuffer(0)); + ThreadTools(p); + } + else + { + Sys_ClearPrintf (); + Sys_Printf ("==================\nRunning bsp command...\n"); + Sys_Printf ("\n%s\n", sys); + + // + // write qe3bsp.bat + // + sprintf (batpath, "%sqe3bsp.bat", temppath); + hFile = fopen(batpath, "w"); + if (!hFile) + Error ("Can't write to %s", batpath); + fprintf (hFile, sys); + fclose (hFile); + + // + // write qe3bsp2.bat + // + sprintf (batpath, "%sqe3bsp2.bat", temppath); + hFile = fopen(batpath, "w"); + if (!hFile) + Error ("Can't write to %s", batpath); + fprintf (hFile, "%sqe3bsp.bat > %s", temppath, outputpath); + fclose (hFile); + + LPCSTR psDeleteError = Pointfile_Delete (); + if (psDeleteError) + { + if (!GetYesNo(va("%s\n\nContinue with BSP process anyway?",psDeleteError))) + return; + } + + GetStartupInfo (&startupinfo); + + ret = CreateProcess( + batpath, // pointer to name of executable module + NULL, // pointer to command line string + NULL, // pointer to process security attributes + NULL, // pointer to thread security attributes + FALSE, // handle inheritance flag + 0 /*DETACHED_PROCESS*/, // creation flags + NULL, // pointer to new environment block + NULL, // pointer to current directory name + &startupinfo, // pointer to STARTUPINFO + &ProcessInformation // pointer to PROCESS_INFORMATION + ); + + if (!ret) + Error ("CreateProcess failed"); + + bsp_process = ProcessInformation.hProcess; + + Sleep (100); // give the new process a chance to open it's window + + BringWindowToTop( g_qeglobals.d_hwndMain ); // pop us back on top + //SetFocus (g_qeglobals.d_hwndCamera); + } + } + else + { + InfoBox(va("Aborting BSP process because map \"%s\" couldn't be saved!",name)); + } +} + +void DLLBuildDone() +{ + g_hToolThread = NULL; + CTime tEnd = CTime::GetCurrentTime(); + CTimeSpan tElapsed = tEnd - g_tBegin; + CString strElapsed; + strElapsed.Format("Run time was %i hours, %i minutes and %i seconds", tElapsed.GetHours(), tElapsed.GetMinutes(), tElapsed.GetSeconds()); + Sys_Printf(strElapsed.GetBuffer(0)); + Pointfile_Check(); + + if (g_PrefsDlg.m_bRunQuake == TRUE) + { + char cCurDir[1024]; + GetCurrentDirectory(1024, cCurDir); + CString strExePath = g_PrefsDlg.m_strQuake2; + CString strOrgPath; + CString strOrgFile; + ExtractPath_and_Filename(currentmap, strOrgPath, strOrgFile); + if (g_PrefsDlg.m_bSetGame == TRUE) // run in place with set game.. don't copy map + { + CString strBasePath = ValueForKey(g_qeglobals.d_project_entity, "basepath"); + strExePath += " +set game "; + strExePath += strBasePath; + WinExec(strExePath, SW_SHOW); + } + else + { + CString strCopyPath = strExePath; + char* pBuffer = strCopyPath.GetBufferSetLength(_MAX_PATH + 1); + pBuffer[strCopyPath.ReverseFind('\\') + 1] = '\0'; + strCopyPath.ReleaseBuffer(); + SetCurrentDirectory(strCopyPath); + CString strOrgPath; + CString strOrgFile; + ExtractPath_and_Filename(currentmap, strOrgPath, strOrgFile); + AddSlash(strCopyPath); + FindReplace(strOrgFile, ".map", ".bsp"); + strCopyPath += "\\baseq2\\maps\\"; + strCopyPath += strOrgFile; + AddSlash(strOrgPath); + strOrgPath += strOrgFile; + bool bRun = (strOrgPath.CompareNoCase(strCopyPath) == 0); + if (!bRun) + bRun = (CopyFile(strOrgPath, strCopyPath, FALSE) == TRUE); + if (bRun) + { + FindReplace(strOrgFile, ".bsp", ""); + strExePath += " +map "; + strExePath += strOrgFile; + WinExec(strExePath, SW_SHOW); + } + } + SetCurrentDirectory(cCurDir); + } + +} + +/* +============= +DoColor + +============= +*/ + +class CMyColorDialog : public CColorDialog +{ + DECLARE_DYNCREATE(CMyColorDialog); + // Construction +public: + CMyColorDialog( COLORREF clrInit = 0, DWORD dwFlags = 0, CWnd* +pParentWnd = NULL ); + // Statics +protected: + enum { NCUSTCOLORS = 16 }; + static COLORREF c_CustColors[NCUSTCOLORS]; + static COLORREF c_LastCustColors[NCUSTCOLORS]; + static bool c_NeedToInitCustColors; +protected: + static void InitCustColors(); + static void SaveCustColors(); + // Dialog Data +protected: + //{{AFX_DATA(CMyColorDialog) + //}}AFX_DATA + // Overrides +protected: + // ClassWizard generate virtual function overrides + //{{AFX_VIRTUAL(CMyColorDialog) +public: + virtual int DoModal(); +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + // Implementation +protected: + // Generated message map functions + //{{AFX_MSG(CMyColorDialog) + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +IMPLEMENT_DYNCREATE(CMyColorDialog, CColorDialog) + +bool CMyColorDialog::c_NeedToInitCustColors = true; +COLORREF CMyColorDialog::c_CustColors[]; +COLORREF CMyColorDialog::c_LastCustColors[]; + +#define SECTION _T("Custom Colors") + +void CMyColorDialog::InitCustColors() { + for (int i = 0; i < NCUSTCOLORS; i++) { + CString entry; entry.Format("%d",i); + c_LastCustColors[i] = c_CustColors[i] = + ::AfxGetApp()->GetProfileInt(SECTION,entry,RGB(255,255,255)); + } + c_NeedToInitCustColors= false; +} + +void CMyColorDialog::SaveCustColors() { + for (int i = 0; i < NCUSTCOLORS; i++) { + if (c_LastCustColors[i] != c_CustColors[i]) { + CString entry; entry.Format("%d",i); + if (c_CustColors[i] == RGB(255,255,255)) { + ::AfxGetApp()->WriteProfileString(SECTION,entry,NULL); + } else { + ::AfxGetApp()->WriteProfileInt(SECTION, entry,c_CustColors[i]); + } + c_LastCustColors[i] = c_CustColors[i]; + } + } +} + +CMyColorDialog::CMyColorDialog( COLORREF clrInit, DWORD dwFlags, + CWnd* pParentWnd) : CColorDialog(clrInit,dwFlags,pParentWnd) +{ + //{{AFX_DATA_INIT(CMyColorDialog) + //}}AFX_DATA_INIT + if (c_NeedToInitCustColors) { + InitCustColors(); + } + m_cc.lpCustColors = c_CustColors; +} + +int CMyColorDialog::DoModal() { + int code = CColorDialog::DoModal(); + SaveCustColors(); + return code; +} + +void CMyColorDialog::DoDataExchange(CDataExchange* pDX) { + // overridden (calls this base class) + CColorDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CMyColorDialog) + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(CMyColorDialog, CColorDialog) +//{{AFX_MSG_MAP(CMyColorDialog) +//}}AFX_MSG_MAP +END_MESSAGE_MAP() + +void DoNewColor(int* i1, int* i2, int* i3) +{ + COLORREF cr = (*i1) + + ((*i2) <<8) + + ((*i3) <<16); + CMyColorDialog dlg(cr, CC_FULLOPEN | CC_RGBINIT); + if (dlg.DoModal() == IDOK) + { + *i1 = (dlg.m_cc.rgbResult & 255); + *i2 = ((dlg.m_cc.rgbResult >> 8) & 255); + *i3 = ((dlg.m_cc.rgbResult >> 16) & 255); + } + +} + + +qboolean DoColor(int iIndex) +{ + + COLORREF cr = (int)(g_qeglobals.d_savedinfo.colors[iIndex][0]*255) + + (((int)(g_qeglobals.d_savedinfo.colors[iIndex][1]*255))<<8) + + (((int)(g_qeglobals.d_savedinfo.colors[iIndex][2]*255))<<16); + CMyColorDialog dlg(cr, CC_FULLOPEN | CC_RGBINIT); + if (dlg.DoModal() == IDOK) + { + g_qeglobals.d_savedinfo.colors[iIndex][0] = (dlg.m_cc.rgbResult&255)/255.0; + g_qeglobals.d_savedinfo.colors[iIndex][1] = ((dlg.m_cc.rgbResult>>8)&255)/255.0; + g_qeglobals.d_savedinfo.colors[iIndex][2] = ((dlg.m_cc.rgbResult>>16)&255)/255.0; + + /* + ** scale colors so that at least one component is at 1.0F + ** if this is meant to select an entity color + */ + if ( iIndex == COLOR_ENTITY ) + { + float largest = 0.0F; + + if ( g_qeglobals.d_savedinfo.colors[iIndex][0] > largest ) + largest = g_qeglobals.d_savedinfo.colors[iIndex][0]; + if ( g_qeglobals.d_savedinfo.colors[iIndex][1] > largest ) + largest = g_qeglobals.d_savedinfo.colors[iIndex][1]; + if ( g_qeglobals.d_savedinfo.colors[iIndex][2] > largest ) + largest = g_qeglobals.d_savedinfo.colors[iIndex][2]; + + if ( largest == 0.0F ) + { + g_qeglobals.d_savedinfo.colors[iIndex][0] = 1.0F; + g_qeglobals.d_savedinfo.colors[iIndex][1] = 1.0F; + g_qeglobals.d_savedinfo.colors[iIndex][2] = 1.0F; + } + else + { + float scaler = 1.0F / largest; + + g_qeglobals.d_savedinfo.colors[iIndex][0] *= scaler; + g_qeglobals.d_savedinfo.colors[iIndex][1] *= scaler; + g_qeglobals.d_savedinfo.colors[iIndex][2] *= scaler; + } + } + + Sys_UpdateWindows (W_ALL); + return true; + } + else return false; + +} + + +/* Copied from MSDN */ + +BOOL DoMru(HWND hWnd,WORD wId) +{ + char szFileName[128]; + OFSTRUCT of; + BOOL fExist; + + if (!ConfirmModified()) + return false; + + GetMenuItem(g_qeglobals.d_lpMruMenu, wId, TRUE, szFileName, sizeof(szFileName)); + + // Test if the file exists. + + fExist = OpenFile(szFileName ,&of,OF_EXIST) != HFILE_ERROR; + + if (fExist) { + + // Place the file on the top of MRU. + AddNewItem(g_qeglobals.d_lpMruMenu,(LPSTR)szFileName); + + // Now perform opening this file !!! + Map_LoadFile (szFileName); + } + else + // Remove the file on MRU. + DelMenuItem(g_qeglobals.d_lpMruMenu,wId,TRUE); + + // Refresh the File menu. + PlaceMenuMRUItem(g_qeglobals.d_lpMruMenu,GetSubMenu(GetMenu(hWnd),0), + ID_FILE_EXIT); + + return fExist; +} + + + +/* +============== +Main_Create +============== +*/ + +void MFCCreate (HINSTANCE hInstance) +{ + HMENU hMenu = NULL; + int i = sizeof(g_qeglobals.d_savedinfo); + long l = i; + LoadRegistryInfo("SavedInfo", &g_qeglobals.d_savedinfo, &l); + + // this is tacky, but they only want a few extra things saved.... + // + l = sizeof(g_bPatchShowBounds); + LoadRegistryInfo("_g_bPatchShowBounds", &g_bPatchShowBounds, &l); + + l = sizeof(g_PrefsDlg.m_bSelectCurves); + LoadRegistryInfo("_g_PrefsDlg.m_bSelectCurves", &g_PrefsDlg.m_bSelectCurves,&l); + + int nOldSize = g_qeglobals.d_savedinfo.iSize; + if (g_qeglobals.d_savedinfo.iSize != sizeof(g_qeglobals.d_savedinfo)) + { + // fill in new defaults + g_qeglobals.d_savedinfo.iSize = sizeof(g_qeglobals.d_savedinfo); + g_qeglobals.d_savedinfo.fGamma = 1.0; + g_qeglobals.d_savedinfo.iTexMenu = ID_VIEW_NEAREST; + g_qeglobals.d_savedinfo.m_nTextureTweak = 1; + + //g_qeglobals.d_savedinfo.exclude = INCLUDE_EASY | INCLUDE_NORMAL | INCLUDE_HARD | INCLUDE_DEATHMATCH; + g_qeglobals.d_savedinfo.show_coordinates = true; + g_qeglobals.d_savedinfo.show_names = true; + + for (i=0 ; i<3 ; i++) + { + g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][i] = 0.25; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][i] = 1.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR][i] = 0.75; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR][i] = 0.5; + g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][i] = 0.25; + } + + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][0] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][2] = 1.0; + + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][0] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][2] = 0.0; + + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][0] = 1.0; + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][2] = 0.0; + + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][0] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][2] = 1.0; + + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][0] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][2] = 0.0; + + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][0] = 0.5; + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][2] = 0.75; + + + // old size was smaller, reload original prefs + if (nOldSize < sizeof(g_qeglobals.d_savedinfo)) + { + long l = nOldSize; + LoadRegistryInfo("SavedInfo", &g_qeglobals.d_savedinfo, &l); + } + + } + if ( ( hMenu = GetMenu( g_qeglobals.d_hwndMain ) ) != 0 ) + { + // by default all of these are checked because that's how they're defined in the menu editor + if ( !g_qeglobals.d_savedinfo.show_names ) + CheckMenuItem( hMenu, ID_VIEW_SHOWNAMES, MF_BYCOMMAND | MF_UNCHECKED ); + if ( !g_qeglobals.d_savedinfo.show_coordinates ) + CheckMenuItem( hMenu, ID_VIEW_SHOWCOORDINATES, MF_BYCOMMAND | MF_UNCHECKED ); + + // hmmm, if I breakpoint here it never reaches it in Trek/Q3 mode (normal mode as well?) + // + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_LIGHTS ) + CheckMenuItem( hMenu, ID_VIEW_SHOWLIGHTS, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_ENT ) + CheckMenuItem( hMenu, ID_VIEW_ENTITY, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_PATHS ) + CheckMenuItem( hMenu, ID_VIEW_SHOWPATH, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_WATER ) + CheckMenuItem( hMenu, ID_VIEW_SHOWWATER, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_WORLD ) + CheckMenuItem( hMenu, ID_VIEW_SHOWWORLD, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_CLIP ) + CheckMenuItem( hMenu, ID_VIEW_SHOWCLIP, MF_BYCOMMAND | MF_UNCHECKED ); + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_HINT ) + CheckMenuItem( hMenu, ID_VIEW_SHOWHINT, MF_BYCOMMAND | MF_UNCHECKED ); + } + +} + + +/* +============================================================= + +REGISTRY INFO + +============================================================= +*/ + +BOOL SaveRegistryInfo(const char *pszName, void *pvBuf, long lSize) +{ + LONG lres; + DWORD dwDisp; + HKEY hKeyId; + + lres = RegCreateKeyEx(HKEY_CURRENT_USER, + + #ifdef QUAKE3 + "Software\\QERadiant\\Q3Trek_QERadiant", + #else + "Software\\QERadiant\\QERadiant", + #endif + 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKeyId, &dwDisp); + + + if (lres != ERROR_SUCCESS) + return FALSE; + + lres = RegSetValueEx(hKeyId, pszName, 0, REG_BINARY, (unsigned char*)pvBuf, lSize); + + RegCloseKey(hKeyId); + + if (lres != ERROR_SUCCESS) + return FALSE; + + return TRUE; +} + +BOOL LoadRegistryInfo(const char *pszName, void *pvBuf, long *plSize) +{ + HKEY hKey; + long lres, lType, lSize; + + if (plSize == NULL) + plSize = &lSize; + + // enables people to keep seperate prefs for SoF and Trek versions of the editor... + // +#ifdef QUAKE3 + lres = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\QERadiant\\Q3Trek_QERadiant", 0, KEY_READ, &hKey); +#else + lres = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\QERadiant\\QERadiant", 0, KEY_READ, &hKey); +#endif + + if (lres != ERROR_SUCCESS) + return FALSE; + + lres = RegQueryValueEx(hKey, pszName, NULL, (unsigned long*)&lType, (unsigned char*)pvBuf, (unsigned long*)plSize); + + RegCloseKey(hKey); + + if (lres != ERROR_SUCCESS) + return FALSE; + + return TRUE; +} + +BOOL SaveWindowState(HWND hWnd, const char *pszName) +{ + RECT rc; + GetWindowRect(hWnd, &rc); + if (hWnd != g_qeglobals.d_hwndMain) // && g_pParentWnd->CurrentStyle() == QR_QE4) + { + if (::GetParent(hWnd) != g_qeglobals.d_hwndMain) + { + ::SetParent(hWnd, g_qeglobals.d_hwndMain); + } + MapWindowPoints(NULL, g_qeglobals.d_hwndMain, (POINT *)&rc, 2); + + } + BOOL b = SaveRegistryInfo(pszName, &rc, sizeof(rc)); + return b; +} + + +BOOL LoadWindowState(HWND hWnd, const char *pszName) +{ + RECT rc; + LONG lSize = sizeof(rc); + + if (LoadRegistryInfo(pszName, &rc, &lSize)) + { + if (rc.left < 0) + rc.left = 0; + if (rc.top < 0) + rc.top = 0; + if (rc.right < rc.left + 16) + rc.right = rc.left + 16; + if (rc.bottom < rc.top + 16) + rc.bottom = rc.top + 16; + + MoveWindow(hWnd, rc.left, rc.top, rc.right - rc.left, + rc.bottom - rc.top, FALSE); + return TRUE; + } + + return FALSE; +} + +/* +=============================================================== + + STATUS WINDOW + +=============================================================== +*/ + +void Sys_UpdateStatusBar( void ) +{ + extern int g_numbrushes, g_numentities; + + char numbrushbuffer[100]=""; + + sprintf( numbrushbuffer, "Brushes: %d Entities: %d", g_numbrushes, g_numentities ); + g_pParentWnd->SetStatusText(2, numbrushbuffer); + //Sys_Status( numbrushbuffer, 2 ); +} + +void Sys_Status(const char *psz, int part ) +{ + SendMessage(g_qeglobals.d_hwndStatus, SB_SETTEXT, part, (LPARAM)psz); +} + +static HWND CreateMyStatusWindow(HINSTANCE hInst) +{ + HWND hWnd; + int partsize[3] = { 300, 1100, -1 }; + + hWnd = CreateWindowEx( WS_EX_TOPMOST, // no extended styles + STATUSCLASSNAME, // status bar + "", // no text + WS_CHILD | WS_BORDER | WS_VISIBLE, // styles + -100, -100, 10, 10, // x, y, cx, cy + g_qeglobals.d_hwndMain, // parent window + (HMENU)100, // window ID + hInst, // instance + NULL); // window data + + SendMessage( hWnd, SB_SETPARTS, 3, ( long ) partsize ); + + return hWnd; +} + +//============================================================== + +#define NUMBUTTONS 15 +HWND CreateToolBar(HINSTANCE hinst) +{ + HWND hwndTB; + TBADDBITMAP tbab; + TBBUTTON tbb[NUMBUTTONS]; + + // Ensure that the common control DLL is loaded. + + InitCommonControls(); + + // Create a toolbar that the user can customize and that has a + // tooltip associated with it. + + hwndTB = CreateWindowEx(0, TOOLBARCLASSNAME, (LPSTR) NULL, + WS_CHILD | TBSTYLE_TOOLTIPS | CCS_ADJUSTABLE | WS_BORDER, + 0, 0, 0, 0, g_qeglobals.d_hwndMain, (HMENU) IDR_TOOLBAR1, hinst, NULL); + + // Send the TB_BUTTONSTRUCTSIZE message, which is required for + // backward compatibility. + + SendMessage(hwndTB, TB_BUTTONSTRUCTSIZE, (WPARAM) sizeof(TBBUTTON), 0); + + // Add the bitmap containing button images to the toolbar. + + tbab.hInst = hinst; + tbab.nID = IDR_TOOLBAR1; + SendMessage(hwndTB, TB_ADDBITMAP, (WPARAM)NUMBUTTONS, (WPARAM) &tbab); + + // Fill the TBBUTTON array with button information, and add the + // buttons to the toolbar. + + tbb[0].iBitmap = 0; + tbb[0].idCommand = ID_BRUSH_FLIPX; + tbb[0].fsState = TBSTATE_ENABLED; + tbb[0].fsStyle = TBSTYLE_BUTTON; + tbb[0].dwData = 0; + tbb[0].iString = 0; + + tbb[1].iBitmap = 2; + tbb[1].idCommand = ID_BRUSH_FLIPY; + tbb[1].fsState = TBSTATE_ENABLED; + tbb[1].fsStyle = TBSTYLE_BUTTON; + tbb[1].dwData = 0; + tbb[1].iString = 0; + + tbb[2].iBitmap = 4; + tbb[2].idCommand = ID_BRUSH_FLIPZ; + tbb[2].fsState = TBSTATE_ENABLED; + tbb[2].fsStyle = TBSTYLE_BUTTON; + tbb[2].dwData = 0; + tbb[2].iString = 0; + + tbb[3].iBitmap = 1; + tbb[3].idCommand = ID_BRUSH_ROTATEX; + tbb[3].fsState = TBSTATE_ENABLED; + tbb[3].fsStyle = TBSTYLE_BUTTON; + tbb[3].dwData = 0; + tbb[3].iString = 0; + + tbb[4].iBitmap = 3; + tbb[4].idCommand = ID_BRUSH_ROTATEY; + tbb[4].fsState = TBSTATE_ENABLED; + tbb[4].fsStyle = TBSTYLE_BUTTON; + tbb[4].dwData = 0; + tbb[4].iString = 0; + + tbb[5].iBitmap = 5; + tbb[5].idCommand = ID_BRUSH_ROTATEZ; + tbb[5].fsState = TBSTATE_ENABLED; + tbb[5].fsStyle = TBSTYLE_BUTTON; + tbb[5].dwData = 0; + tbb[5].iString = 0; + + tbb[6].iBitmap = 6; + tbb[6].idCommand = ID_SELECTION_SELECTCOMPLETETALL; + tbb[6].fsState = TBSTATE_ENABLED; + tbb[6].fsStyle = TBSTYLE_BUTTON; + tbb[6].dwData = 0; + tbb[6].iString = 0; + + tbb[7].iBitmap = 7; + tbb[7].idCommand = ID_SELECTION_SELECTTOUCHING; + tbb[7].fsState = TBSTATE_ENABLED; + tbb[7].fsStyle = TBSTYLE_BUTTON; + tbb[7].dwData = 0; + tbb[7].iString = 0; + + tbb[8].iBitmap = 8; + tbb[8].idCommand = ID_SELECTION_SELECTPARTIALTALL; + tbb[8].fsState = TBSTATE_ENABLED; + tbb[8].fsStyle = TBSTYLE_BUTTON; + tbb[8].dwData = 0; + tbb[8].iString = 0; + + + tbb[9].iBitmap = 9; + tbb[9].idCommand = ID_SELECTION_SELECTINSIDE; + tbb[9].fsState = TBSTATE_ENABLED; + tbb[9].fsStyle = TBSTYLE_BUTTON; + tbb[9].dwData = 0; + tbb[9].iString = 0; + + tbb[10].iBitmap = 10; + tbb[10].idCommand = ID_SELECTION_CSGSUBTRACT; + tbb[10].fsState = TBSTATE_ENABLED; + tbb[10].fsStyle = TBSTYLE_BUTTON; + tbb[10].dwData = 0; + tbb[10].iString = 0; + + + tbb[11].iBitmap = 11; + tbb[11].idCommand = ID_SELECTION_MAKEHOLLOW; + tbb[11].fsState = TBSTATE_ENABLED; + tbb[11].fsStyle = TBSTYLE_BUTTON; + tbb[11].dwData = 0; + tbb[11].iString = 0; + + tbb[12].iBitmap = 12; + tbb[12].idCommand = ID_TEXTURES_WIREFRAME; + tbb[12].fsState = TBSTATE_ENABLED; + tbb[12].fsStyle = TBSTYLE_BUTTON; + tbb[12].dwData = 0; + tbb[12].iString = 0; + + tbb[13].iBitmap = 13; + tbb[13].idCommand = ID_TEXTURES_FLATSHADE; + tbb[13].fsState = TBSTATE_ENABLED; + tbb[13].fsStyle = TBSTYLE_BUTTON; + tbb[13].dwData = 0; + tbb[13].iString = 0; + + tbb[14].iBitmap = 14; + tbb[14].idCommand = ID_VIEW_TRILINEAR; + tbb[14].fsState = TBSTATE_ENABLED; + tbb[14].fsStyle = TBSTYLE_BUTTON; + tbb[14].dwData = 0; + tbb[14].iString = 0; + + SendMessage(hwndTB, TB_ADDBUTTONS, (WPARAM)NUMBUTTONS, + (LPARAM) (LPTBBUTTON) &tbb); + + ShowWindow(hwndTB, SW_SHOW); + + return hwndTB; +} + diff --git a/utils/Radiant/win_qe3.cpp b/utils/Radiant/win_qe3.cpp new file mode 100644 index 0000000..2315f2f --- /dev/null +++ b/utils/Radiant/win_qe3.cpp @@ -0,0 +1,884 @@ +#include "stdafx.h" +#include "qe3.h" +#include "mru.h" +#include "PrefsDlg.h" +#include "oddbits.h" + +extern CEdit* g_pEdit; + +int screen_width; +int screen_height; +qboolean have_quit; + +int update_bits; + +HANDLE bsp_process; + +//=========================================== + +void Sys_MarkMapModified (void) +{ + char title[1024]; + + if (modified != 1) + { + modified = true; // mark the map as changed + sprintf (title, "%s *", currentmap); + + QE_ConvertDOSToUnixName( title, title ); + Sys_SetTitle (title); + } +} + + +void Sys_SetTitle (char *text) +{ + SetWindowText (g_qeglobals.d_hwndMain, text); +} + +HCURSOR waitcursor; + +void Sys_BeginWait (void) +{ + waitcursor = SetCursor (LoadCursor (NULL, IDC_WAIT)); +} + +void Sys_EndWait (void) +{ + if (waitcursor) + { + SetCursor (waitcursor); + waitcursor = NULL; + } +} + + +void Sys_GetCursorPos (int *x, int *y) +{ + POINT lpPoint; + + GetCursorPos (&lpPoint); + *x = lpPoint.x; + *y = lpPoint.y; +} + +void Sys_SetCursorPos (int x, int y) +{ + SetCursorPos (x, y); +} + + +void Sys_Beep (void) +{ + MessageBeep (MB_ICONASTERISK); +} + +char *TranslateString (char *buf) +{ + static char buf2[32768]; + int i, l; + char *out; + + l = strlen(buf); + out = buf2; + for (i=0 ; i SCROLLBACK_MAX_LINES) + { + // this doesn't work if the average number of chars per line is >86 (which exceeds 32k), so for now... +/* + char replaceText[5]; + + replaceText[0] = '\0'; + + SendMessage (g_qeglobals.d_hwndEdit, WM_SETREDRAW, (WPARAM)0, (LPARAM)0); + SendMessage (g_qeglobals.d_hwndEdit, EM_GETSEL, (WPARAM)&oldPosS, (LPARAM)&oldPosE); + SendMessage (g_qeglobals.d_hwndEdit, EM_SETSEL, 0, SCROLLBACK_DEL_CHARS); + SendMessage (g_qeglobals.d_hwndEdit, EM_REPLACESEL, (WPARAM)0, (LPARAM)replaceText); + SendMessage (g_qeglobals.d_hwndEdit, EM_SETSEL, oldPosS, oldPosE); + SendMessage (g_qeglobals.d_hwndEdit, WM_SETREDRAW, (WPARAM)1, (LPARAM)0); +*/ + Sys_ClearPrintf(); + SendMessage (g_qeglobals.d_hwndEdit, EM_REPLACESEL, 0, (LPARAM) "(Output buffer full: clearing it)\r\n"); + } +//PGM + + SendMessage (g_qeglobals.d_hwndEdit, EM_REPLACESEL, 0, (LPARAM)out); +#endif + +} + + +double Sys_DoubleTime (void) +{ + return clock()/ 1000.0; +} + +void PrintPixels (HDC hDC) +{ + int i; + PIXELFORMATDESCRIPTOR p[64]; + + printf ("### flags color layer\n"); + for (i=1 ; i<64 ; i++) + { + if (!DescribePixelFormat ( hDC, i, sizeof(p[0]), &p[i])) + break; + printf ("%3i %5i %5i %5i\n", i, + p[i].dwFlags, + p[i].cColorBits, + p[i].bReserved); + } + printf ("%i modes\n", i-1); +} + + +/* +** ChoosePFD +** +** Helper function that replaces ChoosePixelFormat. +*/ +#define MAX_PFDS 256 + +static int GLW_ChoosePFD( HDC hDC, PIXELFORMATDESCRIPTOR *pPFD ) +{ + PIXELFORMATDESCRIPTOR pfds[MAX_PFDS+1]; + int maxPFD = 0; + int i; + int bestMatch = 0; + + OutputDebugString( va("...GLW_ChoosePFD( %d, %d, %d )\n", ( int ) pPFD->cColorBits, ( int ) pPFD->cDepthBits, ( int ) pPFD->cStencilBits) ); + + // count number of PFDs + // + maxPFD = DescribePixelFormat( hDC, 1, sizeof( PIXELFORMATDESCRIPTOR ), &pfds[0] ); + + if ( maxPFD > MAX_PFDS ) + { + OutputDebugString( va( "...numPFDs > MAX_PFDS (%d > %d)\n", maxPFD, MAX_PFDS) ); + maxPFD = MAX_PFDS; + } + + OutputDebugString( va("...%d PFDs found\n", maxPFD - 1) ); + + FILE *handle = fopen("c:\\chcRadiant_GL_report.txt","wt"); + + fprintf(handle,"Total PFDs: %d\n\n",maxPFD); + + // grab information + for ( i = 1; i <= maxPFD; i++ ) + { + DescribePixelFormat( hDC, i, sizeof( PIXELFORMATDESCRIPTOR ), &pfds[i] ); + + fprintf(handle,"PFD %d/%d\n",i,maxPFD); + fprintf(handle,"=========\n"); + +#define FLAGDUMP(flag) if ( (pfds[i].dwFlags & flag ) != 0 ) fprintf(handle,"(flag: %s)\n",#flag); + + FLAGDUMP( PFD_DOUBLEBUFFER ); + FLAGDUMP( PFD_STEREO ); + FLAGDUMP( PFD_DRAW_TO_WINDOW ); + FLAGDUMP( PFD_DRAW_TO_BITMAP ); + FLAGDUMP( PFD_SUPPORT_GDI ); + FLAGDUMP( PFD_SUPPORT_OPENGL ); + FLAGDUMP( PFD_GENERIC_FORMAT ); + FLAGDUMP( PFD_NEED_PALETTE ); + FLAGDUMP( PFD_NEED_SYSTEM_PALETTE ); + FLAGDUMP( PFD_SWAP_EXCHANGE ); + FLAGDUMP( PFD_SWAP_COPY ); + FLAGDUMP( PFD_SWAP_LAYER_BUFFERS ); + FLAGDUMP( PFD_GENERIC_ACCELERATED ); + FLAGDUMP( PFD_SUPPORT_DIRECTDRAW ); + + if ( pfds[i].iPixelType == PFD_TYPE_RGBA ) + { +// fprintf(handle,"RGBA mode\n"); + } + else + { + fprintf(handle,"NOT RGBA mode!!!!!!!!!!!!\n"); + } + + fprintf(handle, "Colour bits: %d\n",pfds[i].cColorBits); + fprintf(handle, "Depth bits: %d\n",pfds[i].cDepthBits); + + fprintf(handle,"\n"); + } + + + // look for a best match + for ( i = 1; i <= maxPFD; i++ ) + { + fprintf(handle,"(bestMatch: %d)\n",bestMatch ); + + // + // make sure this has hardware acceleration + // + if ( ( pfds[i].dwFlags & PFD_GENERIC_FORMAT ) != 0 ) + { +// if ( !r_allowSoftwareGL->integer ) + { +// if ( r_verbose->integer ) + { + fprintf(handle,//OutputDebugString( + va ("...PFD %d rejected, software acceleration\n", i )); + } + continue; + } + } + + // verify pixel type + if ( pfds[i].iPixelType != PFD_TYPE_RGBA ) + { +// if ( r_verbose->integer ) + { + fprintf(handle,//OutputDebugString( + va("...PFD %d rejected, not RGBA\n", i) ); + } + continue; + } + + // verify proper flags + if ( ( ( pfds[i].dwFlags & pPFD->dwFlags ) & pPFD->dwFlags ) != pPFD->dwFlags ) + { +// if ( r_verbose->integer ) + { + fprintf(handle,//OutputDebugString( + va("...PFD %d rejected, improper flags (0x%x instead of 0x%x)\n", i, pfds[i].dwFlags, pPFD->dwFlags) ); + } + continue; + } + + // verify enough bits + if ( pfds[i].cDepthBits < 15 ) + { + fprintf(handle,va("...PFD %d rejected, depth bits only %d (<15)\n", i, pfds[i].cDepthBits) ); + continue; + } +/* if ( ( pfds[i].cStencilBits < 4 ) && ( pPFD->cStencilBits > 0 ) ) + { + continue; + } +*/ + // + // selection criteria (in order of priority): + // + // PFD_STEREO + // colorBits + // depthBits + // stencilBits + // + if ( bestMatch ) + { +/* + // check stereo + if ( ( pfds[i].dwFlags & PFD_STEREO ) && ( !( pfds[bestMatch].dwFlags & PFD_STEREO ) ) && ( pPFD->dwFlags & PFD_STEREO ) ) + { + bestMatch = i; + continue; + } + + if ( !( pfds[i].dwFlags & PFD_STEREO ) && ( pfds[bestMatch].dwFlags & PFD_STEREO ) && ( pPFD->dwFlags & PFD_STEREO ) ) + { + bestMatch = i; + continue; + } +*/ + // check color + if ( pfds[bestMatch].cColorBits != pPFD->cColorBits ) + { + // prefer perfect match + if ( pfds[i].cColorBits == pPFD->cColorBits ) + { + bestMatch = i; + continue; + } + // otherwise if this PFD has more bits than our best, use it + else if ( pfds[i].cColorBits > pfds[bestMatch].cColorBits ) + { + bestMatch = i; + continue; + } + } + + // check depth + if ( pfds[bestMatch].cDepthBits != pPFD->cDepthBits ) + { + // prefer perfect match + if ( pfds[i].cDepthBits == pPFD->cDepthBits ) + { + bestMatch = i; + continue; + } + // otherwise if this PFD has more bits than our best, use it + else if ( pfds[i].cDepthBits > pfds[bestMatch].cDepthBits ) + { + bestMatch = i; + continue; + } + } +/* + // check stencil + if ( pfds[bestMatch].cStencilBits != pPFD->cStencilBits ) + { + // prefer perfect match + if ( pfds[i].cStencilBits == pPFD->cStencilBits ) + { + bestMatch = i; + continue; + } + // otherwise if this PFD has more bits than our best, use it + else if ( ( pfds[i].cStencilBits > pfds[bestMatch].cStencilBits ) && + ( pPFD->cStencilBits > 0 ) ) + { + bestMatch = i; + continue; + } + } +*/ + } + else + { + bestMatch = i; + } + } + + fprintf(handle,"Bestmode: %d\n",bestMatch); + + if ( !bestMatch ) + { + fprintf(handle,"No decent mode found!\n"); + fclose(handle); + return 0; + } + + if ( ( pfds[bestMatch].dwFlags & PFD_GENERIC_FORMAT ) != 0 ) + { +// if ( !r_allowSoftwareGL->integer ) +// { +// ri.Printf( PRINT_ALL, "...no hardware acceleration found\n" ); +// return 0; +// } +// else + { + fprintf(handle,//OutputDebugString( + "...using software emulation\n" ); + } + } + else if ( pfds[bestMatch].dwFlags & PFD_GENERIC_ACCELERATED ) + { + fprintf(handle,//OutputDebugString( + "...MCD acceleration found\n" ); + } + else + { + fprintf(handle,//OutputDebugString( + "...hardware acceleration found\n" ); + } + + *pPFD = pfds[bestMatch]; + + fclose(handle); + + return bestMatch; +} + + +//========================================================================== + +void QEW_StopGL( HWND hWnd, HGLRC hGLRC, HDC hDC ) +{ + qwglMakeCurrent( NULL, NULL ); + qwglDeleteContext( hGLRC ); + ReleaseDC( hWnd, hDC ); +} + +int QEW_SetupPixelFormat(HDC hDC, qboolean zbuffer ) +{ + static PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd + 1, // version number + PFD_DRAW_TO_WINDOW | // support window + PFD_SUPPORT_OPENGL | // support OpenGL + PFD_DOUBLEBUFFER, // double buffered + PFD_TYPE_RGBA, // RGBA type + 24, // 24-bit color depth + 0, 0, 0, 0, 0, 0, // color bits ignored + 0, // no alpha buffer + 0, // shift bit ignored + 0, // no accumulation buffer + 0, 0, 0, 0, // accum bits ignored + 32, // depth bits + 0, // no stencil buffer + 0, // no auxiliary buffer + PFD_MAIN_PLANE, // main layer + 0, // reserved + 0, 0, 0 // layer masks ignored + }; // + int pixelformat = 0; + + zbuffer = true; + if ( !zbuffer ) + pfd.cDepthBits = 0; + + if (g_PrefsDlg.m_bSGIOpenGL) + { + if ( (pixelformat = qwglChoosePixelFormat(hDC, &pfd)) == 0 ) + { + printf("%d",GetLastError()); + Error ("ChoosePixelFormat failed"); + } + + if (!qwglSetPixelFormat(hDC, pixelformat, &pfd)) + Error ("SetPixelFormat failed"); + } + else + { + // try the pixel format chooser from the game first, then fall back to the normal one + // if the game-chooser couldn't find one (which only happens on shit cards with no 3d support) + // + if ( (pixelformat = GLW_ChoosePFD/*ChoosePixelFormat*/(hDC, &pfd)) == 0 + && + (pixelformat = ChoosePixelFormat(hDC, &pfd)) == 0 + ) + { + printf("%d",GetLastError()); + Error ("ChoosePixelFormat failed"); + } + + if (!SetPixelFormat(hDC, pixelformat, &pfd)) + Error ("SetPixelFormat failed"); + } + + return pixelformat; +} + +/* +================= +Error + +For abnormal program terminations +================= +*/ +void Error (char *error, ...) +{ + va_list argptr; + char text[1024]; + char text2[1024]; + int err; + + err = GetLastError (); + int i = qglGetError(); + + va_start (argptr,error); + vsprintf (text, error,argptr); + va_end (argptr); + + sprintf (text2, "%s\nGetLastError() = %i - %i\nAn unrecoverable error has occured. Would you like to edit Preferences before exiting QERadiant?", text, err, i); + + if (MessageBox(g_qeglobals.d_hwndMain, text2, "Error", MB_YESNO) == IDYES) + { + g_PrefsDlg.LoadPrefs(); + g_PrefsDlg.DoModal(); + } + + exit (1); +} + + +void Warning (char *error, ...) +{ + va_list argptr; + char text[1024]; + int err; + + err = GetLastError (); + int i = qglGetError(); + + va_start (argptr,error); + vsprintf (text, error,argptr); + va_end (argptr); + + Sys_Printf(text); +} + + +/* +====================================================================== + +FILE DIALOGS + +====================================================================== +*/ + +qboolean ConfirmModified (void) +{ + if (!modified) + return true; + + if (MessageBox (g_qeglobals.d_hwndMain, "This will lose changes to the map" + , "warning", MB_OKCANCEL) == IDCANCEL) + return false; + return true; +} + +static OPENFILENAME ofn; /* common dialog box structure */ +static char szDirName[MAX_PATH]; /* directory string */ +static char szFile[260]; /* filename string */ +static char szFileTitle[260]; /* file title string */ +static char szFilter[260] = /* filter string */ + "Map file (*.map, *.reg)\0*.map;*.reg\0\0"; +static char szProjectFilter[260] = /* filter string */ + "QERadiant project (*.qe4, *.prj)\0*.qe4;*.prj\0\0"; +static char chReplace; /* string separator for szFilter */ +static int i, cbString; /* integer count variables */ +static HANDLE hf; /* file handle */ + +void OpenDialog (void) +{ + /* + * Obtain the system directory name and + * store it in szDirName. + */ + + strcpy (szDirName, ValueForKey (g_qeglobals.d_project_entity, "mapspath") ); + if (strlen(szDirName) == 0) + { + strcpy (szDirName, ValueForKey (g_qeglobals.d_project_entity, "basepath") ); + strcat (szDirName, "\\maps"); + } + + /* Place the terminating null character in the szFile. */ + + szFile[0] = '\0'; + + /* Set the members of the OPENFILENAME structure. */ + + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = g_qeglobals.d_hwndCamera; + ofn.lpstrFilter = szFilter; + ofn.nFilterIndex = 1; + ofn.lpstrFile = szFile; + ofn.nMaxFile = sizeof(szFile); + ofn.lpstrFileTitle = szFileTitle; + ofn.nMaxFileTitle = sizeof(szFileTitle); + ofn.lpstrInitialDir = szDirName; + ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST | + OFN_FILEMUSTEXIST; + + /* Display the Open dialog box. */ + + if (!GetOpenFileName(&ofn)) + return; // canceled + + // Add the file in MRU. + //FIXME + AddNewItem( g_qeglobals.d_lpMruMenu, ofn.lpstrFile); + + // Refresh the File menu. + //FIXME + PlaceMenuMRUItem(g_qeglobals.d_lpMruMenu,GetSubMenu(GetMenu(g_qeglobals.d_hwndMain),0), + ID_FILE_EXIT); + + /* Open the file. */ + + Map_LoadFile (ofn.lpstrFile); +} + +void ProjectDialog (void) +{ + /* + * Obtain the system directory name and + * store it in szDirName. + */ + + strcpy (szDirName, ValueForKey(g_qeglobals.d_project_entity, "basepath") ); + strcat (szDirName, "\\scripts"); + + /* Place the terminating null character in the szFile. */ + + szFile[0] = '\0'; + + /* Set the members of the OPENFILENAME structure. */ + + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = g_qeglobals.d_hwndCamera; + ofn.lpstrFilter = szProjectFilter; + ofn.nFilterIndex = 1; + ofn.lpstrFile = szFile; + ofn.nMaxFile = sizeof(szFile); + ofn.lpstrFileTitle = szFileTitle; + ofn.nMaxFileTitle = sizeof(szFileTitle); + ofn.lpstrInitialDir = szDirName; + ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST | + OFN_FILEMUSTEXIST; + + /* Display the Open dialog box. */ + + if (!GetOpenFileName(&ofn)) + return; // canceled + + // Refresh the File menu. + PlaceMenuMRUItem(g_qeglobals.d_lpMruMenu,GetSubMenu(GetMenu(g_qeglobals.d_hwndMain),0), + ID_FILE_EXIT); + + /* Open the file. */ + if (!QE_LoadProject(ofn.lpstrFile)) + Error ("Couldn't load project file"); +} + + +extern void AddSlash(CString& strPath); +void SaveAsDialog (bool bRegion) +{ + strcpy (szDirName, ValueForKey (g_qeglobals.d_project_entity, "basepath") ); + CString strPath = szDirName; + AddSlash(strPath); + strPath += "maps"; + + /* Place the terminating null character in the szFile. */ + + szFile[0] = '\0'; + + /* Set the members of the OPENFILENAME structure. */ + + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = g_qeglobals.d_hwndCamera; + ofn.lpstrFilter = szFilter; + ofn.nFilterIndex = 1; + ofn.lpstrFile = szFile; + ofn.nMaxFile = sizeof(szFile); + ofn.lpstrFileTitle = szFileTitle; + ofn.nMaxFileTitle = sizeof(szFileTitle); + ofn.lpstrInitialDir = strPath; + ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST | + OFN_FILEMUSTEXIST | OFN_OVERWRITEPROMPT; + + /* Display the Open dialog box. */ + + if (!GetSaveFileName(&ofn)) + return; // canceled + + if (bRegion) + DefaultExtension (ofn.lpstrFile, ".reg"); + else + DefaultExtension (ofn.lpstrFile, ".map"); + + if (!bRegion) + { + strcpy (currentmap, ofn.lpstrFile); + AddNewItem(g_qeglobals.d_lpMruMenu, ofn.lpstrFile); + PlaceMenuMRUItem(g_qeglobals.d_lpMruMenu,GetSubMenu(GetMenu(g_qeglobals.d_hwndMain),0), ID_FILE_EXIT); + } + Map_SaveFile (ofn.lpstrFile, bRegion); // ignore region +} + +/* +======================================================= + +Menu modifications + +======================================================= +*/ + +/* +================== +FillBSPMenu + +================== +*/ +char *bsp_commands[256]; + +void FillBSPMenu (void) +{ + HMENU hmenu; + epair_t *ep; + int i; + static int count; + + hmenu = GetSubMenu (GetMenu(g_qeglobals.d_hwndMain), MENU_BSP); + + for (i=0 ; iepairs ; ep ; ep=ep->next) + { + if (ep->key[0] == 'b' && ep->key[1] == 's' && ep->key[2] == 'p') + { + bsp_commands[i] = ep->key; + AppendMenu (hmenu, MF_ENABLED|MF_STRING, + CMD_BSPCOMMAND+i, (LPCTSTR)ep->key); + i++; + } + } + count = i; +} + +//============================================== +void AddSlash(CString& strPath) +{ + if (strPath.GetLength() > 0) + { + if (strPath.GetAt(strPath.GetLength()-1) != '\\') + strPath += '\\'; + } +} + + +bool ExtractPath_and_Filename(const char* pPath, CString& strPath, CString& strFilename) +{ + CString strPathName = pPath; + int nSlash = strPathName.ReverseFind('\\'); + if (nSlash >= 0) + { + strPath = strPathName.Left(nSlash+1); + strFilename = strPathName.Right(strPathName.GetLength() - nSlash - 1); + } + else strFilename = pPath; + return true; +} + + +/* +=============== +CheckBspProcess + +See if the BSP is done yet +=============== +*/ +extern void FindReplace(CString& strContents, const char* pTag, const char* pValue); +extern CTime g_tBegin; + +void CheckBspProcess (void) +{ + char outputpath[1024]; + char temppath[512]; + DWORD exitcode; + char *out; + BOOL ret; + + if (!bsp_process) + return; + + ret = GetExitCodeProcess (bsp_process, &exitcode); + if (!ret) + Error ("GetExitCodeProcess failed"); + if (exitcode == STILL_ACTIVE) + return; + + bsp_process = 0; + + GetTempPath(512, temppath); + sprintf (outputpath, "%sjunk.txt", temppath); + + LoadFile (outputpath, (void **)&out); + Sys_Printf ("%s", out); + Sys_Printf ("\ncompleted.\n"); + free (out); + + CTime tEnd = CTime::GetCurrentTime(); + CTimeSpan tElapsed = tEnd - g_tBegin; + CString strElapsed; + strElapsed.Format("Run time was %i hours, %i minutes and %i seconds", tElapsed.GetHours(), tElapsed.GetMinutes(), tElapsed.GetSeconds()); + Sys_Printf(strElapsed.GetBuffer(0)); + + + Sys_Beep (); + Pointfile_Check(); + if (g_PrefsDlg.m_bRunQuake == TRUE) + { + char cCurDir[1024]; + GetCurrentDirectory(1024, cCurDir); + CString strExePath = g_PrefsDlg.m_strQuake2; + CString strOrgPath; + CString strOrgFile; + ExtractPath_and_Filename(currentmap, strOrgPath, strOrgFile); + if (g_PrefsDlg.m_bSetGame == TRUE) // run in place with set game.. don't copy map + { + CString strBasePath = ValueForKey(g_qeglobals.d_project_entity, "basepath"); + strExePath += " +set game "; + strExePath += strBasePath; + WinExec(strExePath, SW_SHOW); + } + else + { + CString strCopyPath = strExePath; + char* pBuffer = strCopyPath.GetBufferSetLength(_MAX_PATH + 1); + pBuffer[strCopyPath.ReverseFind('\\') + 1] = '\0'; + strCopyPath.ReleaseBuffer(); + SetCurrentDirectory(strCopyPath); + CString strOrgPath; + CString strOrgFile; + ExtractPath_and_Filename(currentmap, strOrgPath, strOrgFile); + AddSlash(strCopyPath); + FindReplace(strOrgFile, ".map", ".bsp"); + strCopyPath += "\\baseq2\\maps\\"; + strCopyPath += strOrgFile; + AddSlash(strOrgPath); + strOrgPath += strOrgFile; + bool bRun = (strOrgPath.CompareNoCase(strCopyPath) == 0); + if (!bRun) + bRun = (CopyFile(strOrgPath, strCopyPath, FALSE) == TRUE); + if (bRun) + { + FindReplace(strOrgFile, ".bsp", ""); + strExePath += " +map "; + strExePath += strOrgFile; + WinExec(strExePath, SW_SHOW); + } + } + SetCurrentDirectory(cCurDir); + } +} + +extern int cambuttonstate; + + diff --git a/utils/Radiant/xy.h b/utils/Radiant/xy.h new file mode 100644 index 0000000..f1b8bbe --- /dev/null +++ b/utils/Radiant/xy.h @@ -0,0 +1,20 @@ + +// window system independent camera view code + +typedef struct +{ + int width, height; + + qboolean timing; + + vec3_t origin; // at center of window + float scale; + + float topclip, bottomclip; + + qboolean d_dirty; +} xy_t; + + +/*BOOL*/ int FilterBrush(brush_t *pb, BOOL bCalledFromDisplayCode = false); + diff --git a/utils/Radiant/xywnd.cpp b/utils/Radiant/xywnd.cpp new file mode 100644 index 0000000..1b9b86c --- /dev/null +++ b/utils/Radiant/xywnd.cpp @@ -0,0 +1,4464 @@ +// XYWnd.cpp : implementation file +// +// QERadiant +// +// + +#include "stdafx.h" +#include "Radiant.h" +#include "XYWnd.h" +#include "qe3.h" +#include "PrefsDlg.h" +#include "DialogInfo.h" +#include "oddbits.h" +#include "groupnames.h" +#include "getstring.h" + + +#pragma warning(disable : 4786) // shut the fuck up about debug symbol name length +#include +using namespace std; +#pragma warning(disable : 4786) // shut the fuck up about debug symbol name length + + + + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#define PAGEFLIPS 2 + + +const char* g_pDimStrings[] = {"x:%.f", "y:%.f", "z:%.f"}; +const char* g_pOrgStrings[] = {"(x:%.f y:%.f)", "(x:%.f z:%.f)", "(y:%.f z:%.f)"}; +CString g_strDim; +CString g_strStatus; + +bool g_bScaleMode; +int g_nScaleHow; +bool g_bRotateMode; +bool g_bClipMode; +bool g_bRogueClipMode; +bool g_bSwitch; +CClipPoint g_Clip1; +CClipPoint g_Clip2; +CClipPoint g_Clip3; +CClipPoint* g_pMovingClip; +brush_t g_brFrontSplits; +brush_t g_brBackSplits; + +brush_t g_brClipboard; +brush_t g_brUndo; +entity_t g_enClipboard; + +vec3_t g_vRotateOrigin; +vec3_t g_vRotation; + +bool g_bPathMode; +CClipPoint g_PathPoints[256]; +CClipPoint* g_pMovingPath; +int g_nPathCount; +int g_nPathLimit; + +bool g_bSmartGo; + +bool g_bPointMode; +CClipPoint g_PointPoints[512]; +CClipPoint* g_pMovingPoint; +int g_nPointCount; +int g_nPointLimit; + + +const int XY_LEFT = 0x01; +const int XY_RIGHT = 0x02; +const int XY_UP = 0x04; +const int XY_DOWN = 0x08; + +PFNPathCallback* g_pPathFunc = NULL; + +void AcquirePath(int nCount, PFNPathCallback* pFunc) +{ + g_nPathCount = 0; + g_nPathLimit = nCount; + g_pPathFunc = pFunc; + g_bPathMode = true; +} + + +CPtrArray g_ptrMenus; + +CMemFile g_Clipboard(4096); +CMemFile g_PatchClipboard(4096); + +extern int pressx; +extern int pressy; + + +///////////////////////////////////////////////////////////////////////////// +// CXYWnd + +IMPLEMENT_DYNCREATE(CXYWnd, CWnd); + +CXYWnd::CXYWnd() +{ + g_brClipboard.next = &g_brClipboard; + g_brUndo.next = &g_brUndo; + g_bRotateMode = false; + g_nScaleHow = 0; + g_bRotateMode = false; + g_bClipMode = false; + g_bRogueClipMode = false; + g_bSwitch = true; + g_pMovingClip = NULL; + g_pMovingPath = NULL; + g_brFrontSplits.next = &g_brFrontSplits; + g_brBackSplits.next = &g_brBackSplits; + m_bActive = false; + //m_bTiming = true; + m_bTiming = false; + m_bRButtonDown = false; + m_nUpdateBits = W_XY; + g_bPathMode = false; + g_nPathCount = 0; + g_nPathLimit = 0; + m_nTimerID = -1; + XY_Init(); +} + +CXYWnd::~CXYWnd() +{ + int nSize = g_ptrMenus.GetSize(); + while (nSize > 0) + { + CMenu* pMenu = reinterpret_cast(g_ptrMenus.GetAt(nSize-1)); + ASSERT(pMenu); + pMenu->DestroyMenu(); + delete pMenu; + nSize--; + } + g_ptrMenus.RemoveAll(); + m_mnuDrop.DestroyMenu(); +} + + +BEGIN_MESSAGE_MAP(CXYWnd, CWnd) + //{{AFX_MSG_MAP(CXYWnd) + ON_WM_CREATE() + ON_WM_LBUTTONDOWN() + ON_WM_MBUTTONDOWN() + ON_WM_RBUTTONDOWN() + ON_WM_LBUTTONUP() + ON_WM_MBUTTONUP() + ON_WM_RBUTTONUP() + ON_WM_MOUSEMOVE() + ON_WM_PAINT() + ON_WM_KEYDOWN() + ON_WM_SIZE() + ON_WM_DESTROY() + ON_COMMAND(ID_SELECT_MOUSEROTATE, OnSelectMouserotate) + ON_WM_TIMER() + ON_WM_KEYUP() + ON_WM_NCCALCSIZE() + ON_WM_KILLFOCUS() + ON_WM_SETFOCUS() + ON_WM_CLOSE() + ON_COMMAND(ID_SELECTION_MAKE_DETAIL, CMainFrame::OnSelectionMakeDetail) + ON_COMMAND(ID_SELECTION_MAKE_STRUCTURAL, CMainFrame::OnSelectionMakeStructural) + ON_COMMAND(ID_SELECTION_HIDEWAYPOINTCHILDREN, CMainFrame::OnSelectionHideWaypointChildren) + ON_COMMAND(ID_SELECTION_UNHIDEWAYPOINTCHILDREN, CMainFrame::OnSelectionUnHideWaypointChildren) + ON_COMMAND(ID_SELECTION_UNHIDEALLWAYPOINTS, CMainFrame::OnSelectionUnHideAllWaypoints) + ON_COMMAND(ID_SELECTION_SELECTCOMPLETETALL, CMainFrame::OnSelectionSelectcompletetall) + ON_COMMAND(ID_SELECTION_SELECTINSIDE, CMainFrame::OnSelectionSelectinside) + ON_COMMAND(ID_SELECTION_SELECTPARTIALTALL, CMainFrame::OnSelectionSelectpartialtall) + ON_COMMAND(ID_SELECTION_SELECTTOUCHING, CMainFrame::OnSelectionSelecttouching) + ON_COMMAND(ID_SELECTION_UNGROUPENTITY, CMainFrame::OnSelectionUngroupentity) + ON_COMMAND(ID_SELECTION_GROUPNAMES_DISPLAY, CMainFrame::OnSelectionGroupNamesDisplay) + ON_COMMAND(ID_SELECTION_GROUPNAMES_ACTIVE, CMainFrame::OnSelectionGroupNamesActive) + ON_COMMAND(ID_SELECTION_GROUPNAMES_GHOSTED, CMainFrame::OnSelectionGroupNamesGhosted) + //}}AFX_MSG_MAP + ON_COMMAND_RANGE(ID_ENTITY_START, ID_ENTITY_END, OnEntityCreate) + ON_COMMAND_RANGE(ID_SCRIPTPOPUP_NOTEPAD_ID_START, ID_SCRIPTPOPUP_NOTEPAD_ID_END, OnScriptPopup_Notepad) + ON_COMMAND_RANGE(ID_SCRIPTPOPUP_MSDEV_ID_START, ID_SCRIPTPOPUP_MSDEV_ID_END, OnScriptPopup_MSDev) + ON_COMMAND_RANGE(ID_SCRIPTPOPUP_BEHAVED_ID_START, ID_SCRIPTPOPUP_BEHAVED_ID_END, OnScriptPopup_BehavEd) + ON_COMMAND_RANGE(ID_SCRIPTPOPOP_GROUPNAMES_ID_START, ID_SCRIPTPOPOP_GROUPNAMES_ID_END, OnPopup_GroupNamesShowHide) + ON_COMMAND_RANGE(ID_SELECTION_GROUPNAMES_SELECT_ID_START, ID_SELECTION_GROUPNAMES_SELECT_ID_END, OnPopup_GroupNamesSelect) + ON_COMMAND_RANGE(ID_SELECTION_GROUPNAMES_ADDSELECTEDTO_ID_START, ID_SELECTION_GROUPNAMES_ADDSELECTEDTO_ID_END, OnPopup_GroupNamesAddSelectedTo) + ON_COMMAND_RANGE(ID_SELECTION_GROUPNAMES_ADDSELECTEDECLASSTO_ID_START, ID_SELECTION_GROUPNAMES_ADDSELECTEDECLASSTO_ID_END, OnPopup_GroupNamesAddSelectedEClassTo) + ON_COMMAND_RANGE(ID_SELECTION_GROUPNAMES_ADDSELECTEDMODELSTO_ID_START, ID_SELECTION_GROUPNAMES_ADDSELECTEDMODELSTO_ID_END, OnPopup_GroupNamesAddSelectedModelsTo) +END_MESSAGE_MAP() + + + + + + +BOOL CXYWnd::GetStringFromClipboard(CString& String) +{ + if (OpenClipboard()) + { + HGLOBAL hXferBuffer; + if ((hXferBuffer = GetClipboardData(CF_TEXT))==NULL) + { + CloseClipboard(); + ErrorBox("Dammit, some sort of problem reading from the clipboard..."); + return FALSE; // hmmmm... Oh well. + } + + char *psClipBoardString = (char*) GlobalLock(hXferBuffer); + String = psClipBoardString; + GlobalUnlock(psClipBoardString); + + CloseClipboard(); + return TRUE; + } + + return FALSE; +} + +BOOL CXYWnd::SendStringToClipboard(LPCSTR psString) +{ + HGLOBAL hXferBuffer = GlobalAlloc((UINT)GMEM_MOVEABLE|GMEM_DDESHARE,(DWORD)strlen(psString)+1); + if (hXferBuffer) + { + char *psLockedDest = (char*) GlobalLock(hXferBuffer); + memcpy(psLockedDest,psString,strlen(psString)+1); + GlobalUnlock(psLockedDest); + + if (OpenClipboard()) + { + EmptyClipboard(); // empty it (all handles to NULL); + if((SetClipboardData((UINT)CF_TEXT,hXferBuffer))==NULL) + { + CloseClipboard(); + ErrorBox("Dammit, some sort of problem writing to the clipboard..."); + return FALSE; // hmmmm... Oh well. + } + CloseClipboard(); + return TRUE; + } + } + + ErrorBox(va("Dammit, I can't allocate %d bytes for some strange reason (reboot, then try again, else tell me - Ste)",strlen(psString)+1)); + return FALSE; +} + + +///////////////////////////////////////////////////////////////////////////// +// CXYWnd message handlers +LONG WINAPI XYWndProc(HWND, UINT, WPARAM, LPARAM); +BOOL CXYWnd::PreCreateWindow(CREATESTRUCT& cs) +{ + WNDCLASS wc; + HINSTANCE hInstance = AfxGetInstanceHandle(); + if (::GetClassInfo(hInstance, XY_WINDOW_CLASS, &wc) == FALSE) + { + // Register a new class + memset (&wc, 0, sizeof(wc)); + wc.style = CS_NOCLOSE | CS_OWNDC; + wc.lpszClassName = XY_WINDOW_CLASS; + wc.hCursor = NULL; //LoadCursor (NULL,IDC_ARROW); + wc.lpfnWndProc = ::DefWindowProc; + if (AfxRegisterClass(&wc) == FALSE) + Error ("CCamWnd RegisterClass: failed"); + } + + cs.lpszClass = XY_WINDOW_CLASS; + cs.lpszName = "VIEW"; + if (cs.style != QE3_CHILDSTYLE) + cs.style = QE3_SPLITTER_STYLE; + + return CWnd::PreCreateWindow(cs); +} + +HDC s_hdcXY; +HGLRC s_hglrcXY; + +static unsigned s_stipple[32] = +{ + 0xaaaaaaaa, 0x55555555,0xaaaaaaaa, 0x55555555, + 0xaaaaaaaa, 0x55555555,0xaaaaaaaa, 0x55555555, + 0xaaaaaaaa, 0x55555555,0xaaaaaaaa, 0x55555555, + 0xaaaaaaaa, 0x55555555,0xaaaaaaaa, 0x55555555, + 0xaaaaaaaa, 0x55555555,0xaaaaaaaa, 0x55555555, + 0xaaaaaaaa, 0x55555555,0xaaaaaaaa, 0x55555555, + 0xaaaaaaaa, 0x55555555,0xaaaaaaaa, 0x55555555, + 0xaaaaaaaa, 0x55555555,0xaaaaaaaa, 0x55555555, +}; + +/* +============ +WXY_WndProc +============ +*/ +LONG WINAPI XYWndProc ( + HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + switch (uMsg) + { + case WM_DESTROY: + QEW_StopGL( hWnd, s_hglrcXY, s_hdcXY ); + return 0; + + case WM_NCCALCSIZE:// don't let windows copy pixels + DefWindowProc (hWnd, uMsg, wParam, lParam); + return WVR_REDRAW; + + case WM_KILLFOCUS: + case WM_SETFOCUS: + SendMessage( hWnd, WM_NCACTIVATE, uMsg == WM_SETFOCUS, 0 ); + return 0; + + case WM_CLOSE: + DestroyWindow (hWnd); + return 0; + } + + return DefWindowProc (hWnd, uMsg, wParam, lParam); +} + + +static void WXY_InitPixelFormat( PIXELFORMATDESCRIPTOR *pPFD ) +{ + memset( pPFD, 0, sizeof( *pPFD ) ); + + pPFD->nSize = sizeof( PIXELFORMATDESCRIPTOR ); + pPFD->nVersion = 1; + pPFD->dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW; + pPFD->iPixelType = PFD_TYPE_RGBA; + pPFD->cColorBits = 24; + pPFD->cDepthBits = 32; + pPFD->iLayerType = PFD_MAIN_PLANE; + +} + +void WXY_Print( void ) +{ + DOCINFO di; + + PRINTDLG pd; + + /* + ** initialize the PRINTDLG struct and execute it + */ + memset( &pd, 0, sizeof( pd ) ); + pd.lStructSize = sizeof( pd ); + pd.hwndOwner = g_qeglobals.d_hwndXY; + pd.Flags = PD_RETURNDC; + pd.hInstance = 0; + if ( !PrintDlg( &pd ) || !pd.hDC ) + { + MessageBox( g_qeglobals.d_hwndMain, "Could not PrintDlg()", "QE4 Print Error", MB_OK | MB_ICONERROR ); + return; + } + + /* + ** StartDoc + */ + memset( &di, 0, sizeof( di ) ); + di.cbSize = sizeof( di ); + di.lpszDocName = "QE4"; + if ( StartDoc( pd.hDC, &di ) <= 0 ) + { + MessageBox( g_qeglobals.d_hwndMain, "Could not StartDoc()", "QE4 Print Error", MB_OK | MB_ICONERROR ); + return; + } + + /* + ** StartPage + */ + if ( StartPage( pd.hDC ) <= 0 ) + { + MessageBox( g_qeglobals.d_hwndMain, "Could not StartPage()", "QE4 Print Error", MB_OK | MB_ICONERROR ); + return; + } + + /* + ** read pixels from the XY window + */ + { + int bmwidth = 320, bmheight = 320; + int pwidth, pheight; + + RECT r; + + GetWindowRect( g_qeglobals.d_hwndXY, &r ); + + bmwidth = r.right - r.left; + bmheight = r.bottom - r.top; + + pwidth = GetDeviceCaps( pd.hDC, PHYSICALWIDTH ) - GetDeviceCaps( pd.hDC, PHYSICALOFFSETX ); + pheight = GetDeviceCaps( pd.hDC, PHYSICALHEIGHT ) - GetDeviceCaps( pd.hDC, PHYSICALOFFSETY ); + + StretchBlt( pd.hDC, + 0, 0, + pwidth, pheight, + s_hdcXY, + 0, 0, + bmwidth, bmheight, + SRCCOPY ); + } + + /* + ** EndPage and EndDoc + */ + if ( EndPage( pd.hDC ) <= 0 ) + { + MessageBox( g_qeglobals.d_hwndMain, "QE4 Print Error", "Could not EndPage()", MB_OK | MB_ICONERROR ); + return; + } + + if ( EndDoc( pd.hDC ) <= 0 ) + { + MessageBox( g_qeglobals.d_hwndMain, "QE4 Print Error", "Could not EndDoc()", MB_OK | MB_ICONERROR ); + return; + } +} + +int CXYWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CWnd::OnCreate(lpCreateStruct) == -1) + return -1; + + s_hdcXY = ::GetDC(GetSafeHwnd()); + QEW_SetupPixelFormat(s_hdcXY, false); + if ( ( s_hglrcXY = qwglCreateContext( s_hdcXY ) ) == 0 ) + Error( "wglCreateContext in WXY_WndProc failed" ); + + if (!qwglShareLists( g_qeglobals.d_hglrcBase, s_hglrcXY ) ) + Error( "wglShareLists in WXY_WndProc failed" ); + + if (!qwglMakeCurrent( s_hdcXY, s_hglrcXY )) + Error ("wglMakeCurrent failed"); + + qglPolygonStipple ((unsigned char *)s_stipple); + qglLineStipple (3, 0xaaaa); + g_qeglobals.d_hwndXY = GetSafeHwnd(); + return 0; +} + +float ptSum(vec3_t pt) +{ + return pt[0] + pt[1] + pt[2]; +} + +void CXYWnd::DropClipPoint(UINT nFlags, CPoint point) +{ + CRect rctZ; + GetClientRect(rctZ); + if (g_pMovingClip) + { + SetCapture(); + XY_ToGridPoint (point.x, rctZ.Height() - 1 - point.y , *g_pMovingClip); + } + else + { + vec3_t* pPt = NULL; + if (g_Clip1.Set() == false) + { + pPt = g_Clip1; + g_Clip1.Set(true); + g_Clip1.m_ptScreen = point; + } + else + if (g_Clip2.Set() == false) + { + pPt = g_Clip2; + g_Clip2.Set(true); + g_Clip2.m_ptScreen = point; + } + else + if (g_Clip3.Set() == false) + { + pPt = g_Clip3; + g_Clip3.Set(true); + g_Clip3.m_ptScreen = point; + } + else + { + RetainClipMode(true); + pPt = g_Clip1; + g_Clip1.Set(true); + g_Clip1.m_ptScreen = point; + } + XY_ToGridPoint (point.x, rctZ.Height() - 1 - point.y , *pPt); + } + Sys_UpdateWindows(XY | W_CAMERA_IFON); +} + + +void CXYWnd::DropPathPoint(UINT nFlags, CPoint point) +{ + CRect rctZ; + GetClientRect(rctZ); + if (g_pMovingPath) + { + SetCapture(); + XY_ToGridPoint (point.x, rctZ.Height() - 1 - point.y , *g_pMovingPath); + } + else + { + g_PathPoints[g_nPathCount].Set(true); + g_PathPoints[g_nPathCount].m_ptScreen = point; + XY_ToGridPoint(point.x, rctZ.Height() - 1 - point.y, g_PathPoints[g_nPathCount]); + g_nPathCount++; + if (g_nPathCount == g_nPathLimit) + { + if (g_pPathFunc) + g_pPathFunc(true, g_nPathCount); + g_nPathCount = 0; + g_bPathMode = false; + g_pPathFunc = NULL; + } + } + Sys_UpdateWindows(XY | W_CAMERA_IFON); +} + +void CXYWnd::AddPointPoint(UINT nFlags, vec3_t* pVec) +{ + g_PointPoints[g_nPointCount].Set(true); + //g_PointPoints[g_nPointCount].m_ptScreen = point; + _VectorCopy(*pVec, g_PointPoints[g_nPointCount]); + g_PointPoints[g_nPointCount].SetPointPtr(pVec); + g_nPointCount++; + Sys_UpdateWindows(XY | W_CAMERA_IFON); +} + + +void CXYWnd::OnLButtonDown(UINT nFlags, CPoint point) +{ + g_pParentWnd->SetActiveXY(this); + UndoCopy(); + if (ClipMode() && !RogueClipMode()) + { + DropClipPoint(nFlags, point); + } + else if (PathMode()) + { + DropPathPoint(nFlags, point); + } + else OriginalButtonDown(nFlags, point); +} + +void CXYWnd::OnMButtonDown(UINT nFlags, CPoint point) +{ + OriginalButtonDown(nFlags, point); +} + + +float Betwixt(float f1, float f2) +{ + if (f1 > f2) + return f2 + ((f1 - f2) / 2); + else + return f1 + ((f2 - f1) / 2); +} + +void CXYWnd::ProduceSplits(brush_t** pFront, brush_t** pBack) +{ + *pFront = NULL; + *pBack = NULL; + if (ClipMode()) + { + if (g_Clip1.Set() && g_Clip2.Set()) + { + face_t face; + VectorCopy(g_Clip1.m_ptClip,face.planepts[0]); + VectorCopy(g_Clip2.m_ptClip,face.planepts[1]); + VectorCopy(g_Clip3.m_ptClip,face.planepts[2]); + if (selected_brushes.next && (selected_brushes.next->next == &selected_brushes)) + { + if (g_Clip3.Set() == false) + { + if (m_nViewType == XY) + { + face.planepts[0][2] = selected_brushes.next->mins[2]; + face.planepts[1][2] = selected_brushes.next->mins[2]; + face.planepts[2][0] = Betwixt(g_Clip1.m_ptClip[0], g_Clip2.m_ptClip[0]); + face.planepts[2][1] = Betwixt(g_Clip1.m_ptClip[1], g_Clip2.m_ptClip[1]); + face.planepts[2][2] = selected_brushes.next->maxs[2]; + } + else if (m_nViewType == YZ) + { + face.planepts[0][0] = selected_brushes.next->mins[0]; + face.planepts[1][0] = selected_brushes.next->mins[0]; + face.planepts[2][1] = Betwixt(g_Clip1.m_ptClip[1], g_Clip2.m_ptClip[1]); + face.planepts[2][2] = Betwixt(g_Clip1.m_ptClip[2], g_Clip2.m_ptClip[2]); + face.planepts[2][0] = selected_brushes.next->maxs[0]; + } + else + { + face.planepts[0][1] = selected_brushes.next->mins[1]; + face.planepts[1][1] = selected_brushes.next->mins[1]; + face.planepts[2][0] = Betwixt(g_Clip1.m_ptClip[0], g_Clip2.m_ptClip[0]); + face.planepts[2][2] = Betwixt(g_Clip1.m_ptClip[2], g_Clip2.m_ptClip[2]); + face.planepts[2][1] = selected_brushes.next->maxs[1]; + } + } + + CSG_SplitBrushByFace (selected_brushes.next, &face, pFront, pBack); + } + + } + } +} + +void CleanList(brush_t* pList) +{ + brush_t* pBrush = pList->next; + while (pBrush != NULL && pBrush != pList) + { + brush_t* pNext = pBrush->next; + Brush_Free(pBrush); + pBrush = pNext; + } +} + +void CXYWnd::ProduceSplitLists() +{ + CleanList(&g_brFrontSplits); + CleanList(&g_brBackSplits); + g_brFrontSplits.next = &g_brFrontSplits; + g_brBackSplits.next = &g_brBackSplits; + brush_t* pBrush; + for (pBrush = selected_brushes.next ; pBrush != NULL && pBrush != &selected_brushes ; pBrush=pBrush->next) + { + brush_t* pFront = NULL; + brush_t* pBack = NULL; + if (ClipMode()) + { + if (g_Clip1.Set() && g_Clip2.Set()) + { + face_t face; + VectorCopy(g_Clip1.m_ptClip,face.planepts[0]); + VectorCopy(g_Clip2.m_ptClip,face.planepts[1]); + VectorCopy(g_Clip3.m_ptClip,face.planepts[2]); + if (g_Clip3.Set() == false) + { + if (g_pParentWnd->ActiveXY()->GetViewType() == XY) + { + face.planepts[0][2] = pBrush->mins[2]; + face.planepts[1][2] = pBrush->mins[2]; + face.planepts[2][0] = Betwixt(g_Clip1.m_ptClip[0], g_Clip2.m_ptClip[0]); + face.planepts[2][1] = Betwixt(g_Clip1.m_ptClip[1], g_Clip2.m_ptClip[1]); + face.planepts[2][2] = pBrush->maxs[2]; + } + else if (g_pParentWnd->ActiveXY()->GetViewType() == YZ) + { + face.planepts[0][0] = pBrush->mins[0]; + face.planepts[1][0] = pBrush->mins[0]; + face.planepts[2][1] = Betwixt(g_Clip1.m_ptClip[1], g_Clip2.m_ptClip[1]); + face.planepts[2][2] = Betwixt(g_Clip1.m_ptClip[2], g_Clip2.m_ptClip[2]); + face.planepts[2][0] = pBrush->maxs[0]; + } + else + { + face.planepts[0][1] = pBrush->mins[1]; + face.planepts[1][1] = pBrush->mins[1]; + face.planepts[2][0] = Betwixt(g_Clip1.m_ptClip[0], g_Clip2.m_ptClip[0]); + face.planepts[2][2] = Betwixt(g_Clip1.m_ptClip[2], g_Clip2.m_ptClip[2]); + face.planepts[2][1] = pBrush->maxs[1]; + } + } + CSG_SplitBrushByFace (pBrush, &face, &pFront, &pBack); + if (pBack) + Brush_AddToList(pBack, &g_brBackSplits); + if (pFront) + Brush_AddToList(pFront, &g_brFrontSplits); + } + } + } +} + +void Brush_CopyList (brush_t* pFrom, brush_t* pTo) +{ + brush_t* pBrush = pFrom->next; + while (pBrush != NULL && pBrush != pFrom) + { + brush_t* pNext = pBrush->next; + Brush_RemoveFromList(pBrush); + Brush_AddToList(pBrush, pTo); + pBrush = pNext; + } +} + +void CXYWnd::OnRButtonDown(UINT nFlags, CPoint point) +{ + g_pParentWnd->SetActiveXY(this); + m_ptDown = point; + m_bRButtonDown = true; + + if (g_PrefsDlg.m_nMouseButtons == 3) // 3 button mouse + { + if ((GetKeyState(VK_CONTROL) & 0x8000)) + { + if (ClipMode()) // already there? + DropClipPoint(nFlags, point); + else + { + SetClipMode(true); + g_bRogueClipMode = true; + DropClipPoint(nFlags, point); + } + return; + } + } + OriginalButtonDown(nFlags, point); +} + +void CXYWnd::OnLButtonUp(UINT nFlags, CPoint point) +{ + if (ClipMode()) + { + if (g_pMovingClip) + { + ReleaseCapture(); + g_pMovingClip = NULL; + } + } + OriginalButtonUp(nFlags, point); +} + +void CXYWnd::OnMButtonUp(UINT nFlags, CPoint point) +{ + OriginalButtonUp(nFlags, point); +} + +void CXYWnd::OnRButtonUp(UINT nFlags, CPoint point) +{ + m_bRButtonDown = false; + if (point == m_ptDown) // mouse didn't move + { + bool bGo = true; + if ((GetKeyState(VK_MENU) & 0x8000)) + bGo = false; + if ((GetKeyState(VK_CONTROL) & 0x8000)) + bGo = false; + if ((GetKeyState(VK_SHIFT) & 0x8000)) + bGo = false; + if (bGo) + HandleDrop(); + } + OriginalButtonUp(nFlags, point); +} + +void CXYWnd::OriginalButtonDown(UINT nFlags, CPoint point) +{ + CRect rctZ; + GetClientRect(rctZ); + SetWindowPos(&wndTop, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE); + if (::GetTopWindow( g_qeglobals.d_hwndMain ) != GetSafeHwnd()) + ::BringWindowToTop(GetSafeHwnd()); + SetFocus(); + SetCapture(); + XY_MouseDown (point.x, rctZ.Height() - 1 - point.y , nFlags); + m_nScrollFlags = nFlags; +} + +void CXYWnd::OriginalButtonUp(UINT nFlags, CPoint point) +{ + CRect rctZ; + GetClientRect(rctZ); + XY_MouseUp (point.x, rctZ.Height() - 1 - point.y , nFlags); + if (! (nFlags & (MK_LBUTTON|MK_RBUTTON|MK_MBUTTON))) + ReleaseCapture (); +} + + +float fDiff(float f1, float f2) +{ + if (f1 > f2) + return f1 - f2; + else + return f2 - f1; +} + + +void CXYWnd::OnMouseMove(UINT nFlags, CPoint point) +{ + vec3_t tdp; + m_ptDown.x = 0; + m_ptDown.y = 0; + + if ( g_PrefsDlg.m_bChaseMouse == TRUE && + (point.x < 0 || point.y < 0 || + point.x > m_nWidth || point.y > m_nHeight) && + GetCapture() == this) + { + float fAdjustment = (g_qeglobals.d_gridsize / 8 * 64) / m_fScale; + //m_ptDrag = point; + m_ptDragAdj.x = 0; + m_ptDragAdj.y = 0; + if (point.x < 0) + { + m_ptDragAdj.x = -fAdjustment; + } + else + if (point.x > m_nWidth) + { + m_ptDragAdj.x = fAdjustment; + } + if (point.y < 0) + { + m_ptDragAdj.y = -fAdjustment; + } + else + if (point.y > m_nHeight) + { + m_ptDragAdj.y = fAdjustment; + } + if (m_nTimerID == -1) + { + m_nTimerID = SetTimer(100, 50, NULL); + m_ptDrag = point; + m_ptDragTotal = 0; + } + return; + } + //else if (m_nTimerID != -1) + if (m_nTimerID != -1) + { + KillTimer(m_nTimerID); + pressx -= m_ptDragTotal.x; + pressy += m_ptDragTotal.y; + m_nTimerID = -1; + //return; + } + + bool bCrossHair = false; + if (!m_bRButtonDown) + { + tdp[0] = tdp[1] = tdp[2] = 0.0; + SnapToPoint (point.x, m_nHeight - 1 - point.y , tdp); + + g_strStatus.Format("x:: %.1f y:: %.1f z:: %.1f", tdp[0], tdp[1], tdp[2]); + g_pParentWnd->SetStatusText(1, g_strStatus); + + // i need to generalize the point code.. having 3 flavors pretty much sucks.. + // once the new curve stuff looks like it is going to stick i will + // rationalize this down to a single interface.. + if (PointMode()) + { + if (g_pMovingPoint && GetCapture() == this) + { + bCrossHair = true; + XY_ToGridPoint (point.x, m_nHeight - 1 - point.y , g_pMovingPoint->m_ptClip); + g_pMovingPoint->UpdatePointPtr(); + Sys_UpdateWindows(XY | W_CAMERA_IFON); + } + else + { + g_pMovingPoint = NULL; + int nDim1 = (m_nViewType == YZ) ? 1 : 0; + int nDim2 = (m_nViewType == XY) ? 1 : 2; + for (int n = 0; n < g_nPointCount; n++) + { + if ( fDiff(g_PointPoints[n].m_ptClip[nDim1], tdp[nDim1]) < 3 && + fDiff(g_PointPoints[n].m_ptClip[nDim2], tdp[nDim2]) < 3 ) + { + bCrossHair = true; + g_pMovingPoint = &g_PointPoints[n]; + } + } + } + } + else if (ClipMode()) + { + if (g_pMovingClip && GetCapture() == this) + { + bCrossHair = true; + XY_ToGridPoint (point.x, m_nHeight - 1 - point.y , g_pMovingClip->m_ptClip); + Sys_UpdateWindows(XY | W_CAMERA_IFON); + } + else + { + g_pMovingClip = NULL; + int nDim1 = (m_nViewType == YZ) ? 1 : 0; + int nDim2 = (m_nViewType == XY) ? 1 : 2; + if (g_Clip1.Set()) + { + if ( fDiff(g_Clip1.m_ptClip[nDim1], tdp[nDim1]) < 3 && + fDiff(g_Clip1.m_ptClip[nDim2], tdp[nDim2]) < 3 ) + { + bCrossHair = true; + g_pMovingClip = &g_Clip1; + } + } + if (g_Clip2.Set()) + { + if ( fDiff(g_Clip2.m_ptClip[nDim1], tdp[nDim1]) < 3 && + fDiff(g_Clip2.m_ptClip[nDim2], tdp[nDim2]) < 3 ) + { + bCrossHair = true; + g_pMovingClip = &g_Clip2; + } + } + if (g_Clip3.Set()) + { + if ( fDiff(g_Clip3.m_ptClip[nDim1], tdp[nDim1]) < 3 && + fDiff(g_Clip3.m_ptClip[nDim2], tdp[nDim2]) < 3 ) + { + bCrossHair = true; + g_pMovingClip = &g_Clip3; + } + } + } + if (bCrossHair == false) + XY_MouseMoved (point.x, m_nHeight - 1 - point.y , nFlags); + } + else if (PathMode()) + { + if (g_pMovingPath && GetCapture() == this) + { + bCrossHair = true; + XY_ToGridPoint (point.x, m_nHeight - 1 - point.y , g_pMovingPath->m_ptClip); + Sys_UpdateWindows(XY | W_CAMERA_IFON); + } + else + { + g_pMovingPath = NULL; + int nDim1 = (m_nViewType == YZ) ? 1 : 0; + int nDim2 = (m_nViewType == XY) ? 1 : 2; + for (int n = 0; n < g_nPathCount; n++) + { + if ( fDiff(g_PathPoints[n].m_ptClip[nDim1], tdp[nDim1]) < 3 && + fDiff(g_PathPoints[n].m_ptClip[nDim2], tdp[nDim2]) < 3 ) + { + bCrossHair = true; + g_pMovingPath = &g_PathPoints[n]; + } + } + } + } + else + { + XY_MouseMoved (point.x, m_nHeight - 1 - point.y , nFlags); + } + } + else + { + XY_MouseMoved (point.x, m_nHeight - 1 - point.y , nFlags); + } + if (bCrossHair) + SetCursor(::LoadCursor(NULL, IDC_CROSS)); + else + SetCursor(::LoadCursor(NULL, IDC_ARROW)); +} + +void CXYWnd::RetainClipMode(bool bMode) +{ + bool bSave = g_bRogueClipMode; + SetClipMode(bMode); + if (bMode == true) + g_bRogueClipMode = bSave; + else + g_bRogueClipMode = false; +} + +void CXYWnd::SetClipMode(bool bMode) +{ + g_bClipMode = bMode; + g_bRogueClipMode = false; + if (bMode) + { + g_Clip1.Reset(); + g_Clip2.Reset(); + g_Clip3.Reset(); + CleanList(&g_brFrontSplits); + CleanList(&g_brBackSplits); + g_brFrontSplits.next = &g_brFrontSplits; + g_brBackSplits.next = &g_brBackSplits; + } + else + { + if (g_pMovingClip) + { + ReleaseCapture(); + g_pMovingClip = NULL; + } + CleanList(&g_brFrontSplits); + CleanList(&g_brBackSplits); + g_brFrontSplits.next = &g_brFrontSplits; + g_brBackSplits.next = &g_brBackSplits; + Sys_UpdateWindows(XY | W_CAMERA_IFON); + } +} + +bool CXYWnd::ClipMode() +{ + return g_bClipMode; +} + +bool CXYWnd::RogueClipMode() +{ + return g_bRogueClipMode; +} + +bool CXYWnd::PathMode() +{ + return g_bPathMode; +} + + +bool CXYWnd::PointMode() +{ + return g_bPointMode; +} + +void CXYWnd::SetPointMode(bool b) +{ + g_bPointMode = b; + if (!b) + g_nPointCount = 0; +} + + +void CXYWnd::OnPaint() +{ + CPaintDC dc(this); // device context for painting + bool bPaint = true; + if (!qwglMakeCurrent(dc.m_hDC, s_hglrcXY)) + { + Sys_Printf("ERROR: wglMakeCurrent failed.. Error:%i\n",qglGetError()); + Sys_Printf("Please restart QERadiant if the Map view is not working\n"); + bPaint = false; + } + if (bPaint) + { + QE_CheckOpenGLForErrors(); + XY_Draw (); + QE_CheckOpenGLForErrors(); + if (ClipMode()) + { + if (m_nViewType != XY) + { + qglPushMatrix(); + if (m_nViewType == YZ) + qglRotatef (-90, 0, 1, 0); // put Z going up + qglRotatef (-90, 1, 0, 0); // put Z going up + } + qglPointSize (4); + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER]); + qglBegin (GL_POINTS); + if (g_Clip1.Set()) + qglVertex3fv (g_Clip1); + if (g_Clip2.Set()) + qglVertex3fv (g_Clip2); + if (g_Clip3.Set()) + qglVertex3fv (g_Clip3); + qglEnd (); + qglPointSize (1); + + CString strMsg; + if (g_Clip1.Set()) + { + qglRasterPos3f (g_Clip1.m_ptClip[0]+2, g_Clip1.m_ptClip[1]+2, g_Clip1.m_ptClip[2]+2); + strMsg = "1"; + //strMsg.Format("1 (%f, %f, %f)", g_Clip1[0], g_Clip1[1], g_Clip1[2]); + qglCallLists (strMsg.GetLength(), GL_UNSIGNED_BYTE, strMsg); + } + if (g_Clip2.Set()) + { + qglRasterPos3f (g_Clip2.m_ptClip[0]+2, g_Clip2.m_ptClip[1]+2, g_Clip2.m_ptClip[2]+2); + strMsg = "2"; + //strMsg.Format("2 (%f, %f, %f)", g_Clip2[0], g_Clip2[1], g_Clip2[2]); + qglCallLists (strMsg.GetLength(), GL_UNSIGNED_BYTE, strMsg); + } + if (g_Clip3.Set()) + { + qglRasterPos3f (g_Clip3.m_ptClip[0]+2, g_Clip3.m_ptClip[1]+2, g_Clip3.m_ptClip[2]+2); + strMsg = "3"; + //strMsg.Format("3 (%f, %f, %f)", g_Clip3[0], g_Clip3[1], g_Clip3[2]); + qglCallLists (strMsg.GetLength(), GL_UNSIGNED_BYTE, strMsg); + } + if (g_Clip1.Set() && g_Clip2.Set()) + { + ProduceSplitLists(); + brush_t* pBrush; + brush_t* pList = ( (m_nViewType == XZ) ? !g_bSwitch : g_bSwitch) ? &g_brBackSplits : &g_brFrontSplits; + for (pBrush = pList->next ; pBrush != NULL && pBrush != pList ; pBrush=pBrush->next) + { + qglColor3f (1,1,0); + face_t *face; + int order; + for (face = pBrush->brush_faces,order = 0 ; face ; face=face->next, order++) + { + winding_t* w = face->face_winding; + if (!w) + continue; + // draw the polygon + qglBegin(GL_LINE_LOOP); + for (int i=0 ; inumpoints ; i++) + qglVertex3fv(w->points[i]); + qglEnd(); + } + } + } + + if (m_nViewType != XY) + qglPopMatrix(); + } + + if (PathMode()) + { + if (m_nViewType != XY) + { + qglPushMatrix(); + if (m_nViewType == YZ) + qglRotatef (-90, 0, 1, 0); // put Z going up + qglRotatef (-90, 1, 0, 0); // put Z going up + } + qglPointSize (4); + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER]); + qglBegin (GL_POINTS); + + for (int n = 0; n < g_nPathCount; n++) + qglVertex3fv(g_PathPoints[n]); + qglEnd (); + qglPointSize (1); + + CString strMsg; + for (n = 0; n < g_nPathCount; n++) + { + qglRasterPos3f (g_PathPoints[n].m_ptClip[0]+2, g_PathPoints[n].m_ptClip[1]+2, g_PathPoints[n].m_ptClip[2]+2); + strMsg.Format("%i", n+1); + qglCallLists (strMsg.GetLength(), GL_UNSIGNED_BYTE, strMsg); + } + + if (m_nViewType != XY) + qglPopMatrix(); + } + + qwglSwapBuffers(dc.m_hDC); +// TRACE("XY Paint\n"); + } + } + +void CXYWnd::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + g_pParentWnd->HandleKey(nChar, nRepCnt, nFlags); +} + + + +void CreateEntityFromName(char* pName, brush_t* pBrush) +{ + eclass_t *pecNew; + entity_t *petNew; + if (stricmp(pName, "worldspawn") == 0) + { + MessageBox(g_qeglobals.d_hwndMain, "Can't create an entity with worldspawn.", "info", 0); + return; + } + + pecNew = Eclass_ForName(pName, false); + + // create it + petNew = Entity_Create(pecNew); + if (petNew == NULL) + { + if (!((selected_brushes.next == &selected_brushes)||(selected_brushes.next->next != &selected_brushes))) + { + brush_t* b = selected_brushes.next; + if (b->owner != world_entity && b->owner->eclass->fixedsize && pecNew->fixedsize) + { + vec3_t mins, maxs; + vec3_t origin; + for (int i=0 ; i<3 ; i++) + origin[i] = b->mins[i] - pecNew->mins[i]; + + VectorAdd (pecNew->mins, origin, mins); + VectorAdd (pecNew->maxs, origin, maxs); + brush_t* nb = Brush_Create (mins, maxs, &pecNew->texdef); + Entity_LinkBrush (b->owner, nb); + nb->owner->eclass = pecNew; + SetKeyValue (nb->owner, "classname", pName); + Brush_Free(b); + Brush_Build(nb); + Brush_AddToList (nb, &active_brushes); + Select_Brush(nb); + return; + } + } + MessageBox(g_qeglobals.d_hwndMain, "Failed to create entity.", "info", 0); + return; + } + + Select_Deselect (); + //entity_t* pEntity = world_entity; + //if (selected_brushes.next != &selected_brushes) + // pEntity = selected_brushes.next->owner; + Select_Brush (petNew->brushes.onext); + + if (strnicmp(pName, "misc_model",10) == 0) + { +#ifdef QUAKE3 + extern CString strASSIGNMODEL; + strASSIGNMODEL = (pecNew->psQuakEd_MODELNAME && strlen(pecNew->psQuakEd_MODELNAME))?pecNew->psQuakEd_MODELNAME:""; +#endif + SetInspectorMode(W_ENTITY); + PostMessage(g_qeglobals.d_hwndEntity, WM_COMMAND, IDC_BTN_ASSIGNMODEL, 0); + } + +} + + +brush_t* CreateEntityBrush(int x, int y, CXYWnd* pWnd) +{ + vec3_t mins, maxs; + int i; + float temp; + brush_t *n; + + pWnd->XY_ToGridPoint (x, y, mins); + x += 32; + y += 32; + pWnd->XY_ToGridPoint (x, y, maxs); + + int nDim = (pWnd->GetViewType() == XY) ? 2 : (pWnd->GetViewType() == YZ) ? 0 : 1; + mins[nDim] = g_qeglobals.d_gridsize * ((int)(g_qeglobals.d_new_brush_bottom_z/g_qeglobals.d_gridsize)); + maxs[nDim] = g_qeglobals.d_gridsize * ((int)(g_qeglobals.d_new_brush_top_z/g_qeglobals.d_gridsize)); + + if (maxs[nDim] <= mins[nDim]) + maxs[nDim] = mins[nDim] + g_qeglobals.d_gridsize; + + for (i=0 ; i<3 ; i++) + { + if (mins[i] == maxs[i]) + maxs[i] += 16; // don't create a degenerate brush + if (mins[i] > maxs[i]) + { + temp = mins[i]; + mins[i] = maxs[i]; + maxs[i] = temp; + } + } + + n = Brush_Create (mins, maxs, &g_qeglobals.d_texturewin.texdef); + if (!n) + return NULL; + + Brush_AddToList (n, &selected_brushes); + Entity_LinkBrush (world_entity, n); + Brush_Build( n ); + return n; +} + +void CreateRightClickEntity(CXYWnd* pWnd, int x, int y, char* pName) +{ + CRect rctZ; + pWnd->GetClientRect(rctZ); + brush_t* pBrush = (selected_brushes.next == &selected_brushes) ? CreateEntityBrush(x, rctZ.Height() - 1 - y, pWnd) : selected_brushes.next; + CreateEntityFromName(pName, pBrush); + //Select_Brush(pBrush); +} + +brush_t* CreateSmartBrush(vec3_t v) +{ + vec3_t mins, maxs; + int i; + brush_t *n; + + for (i=0 ; i<3 ; i++) + { + mins[i] = v[i] - 16; + maxs[i] = v[i] + 16; + } + + n = Brush_Create (mins, maxs, &g_qeglobals.d_texturewin.texdef); + if (!n) + return NULL; + + Brush_AddToList(n, &selected_brushes); + //Entity_LinkBrush(world_entity, n); + Brush_Build(n); + return n; +} + + + + +CString g_strSmartEntity; +int g_nSmartX; +int g_nSmartY; +bool g_bSmartWaiting; +void _SmartPointDone(bool b, int n) +{ + g_bSmartWaiting = false; +} + +void CreateSmartEntity(CXYWnd* pWnd, int x, int y, const char* pName) +{ + g_nSmartX = x; + g_nSmartY = y; + g_strSmartEntity = pName; + if (g_strSmartEntity.Find("Smart_Train") >= 0) + { + ShowInfoDialog("Select the path of the train by left clicking in XY, YZ and/or XZ views. You can move an already dropped point by grabbing and moving it. When you are finished, press ENTER to accept and create the entity and path(s), press ESC to abandon the creation"); + g_bPathMode = true; + g_nPathLimit = 0; + g_nPathCount = 0; + g_bSmartGo = true; + } + else + if (g_strSmartEntity.Find("Smart_Monster...") >= 0) + { + g_bPathMode = true; + g_nPathLimit = 0; + g_nPathCount = 0; + } + else + if (g_strSmartEntity.Find("Smart_Rotating") >= 0) + { + g_bSmartWaiting = true; + ShowInfoDialog("Left click to specify the rotation origin"); + AcquirePath(1, &_SmartPointDone); + while (g_bSmartWaiting) + { + MSG msg; + if (::PeekMessage( &msg, NULL, 0, 0, PM_REMOVE )) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + HideInfoDialog(); + CPtrArray array; + g_bScreenUpdates = false; + CreateRightClickEntity(g_pParentWnd->ActiveXY(), g_nSmartX, g_nSmartY, "func_rotating"); + array.Add(reinterpret_cast(selected_brushes.next)); + Select_Deselect(); + brush_t* pBrush = CreateSmartBrush(g_PathPoints[0]); + array.Add(pBrush); + Select_Deselect(); + Select_Brush(reinterpret_cast(array.GetAt(0))); + Select_Brush(reinterpret_cast(array.GetAt(1))); + ConnectEntities(); + g_bScreenUpdates = true; + } +} + + +void FinishSmartCreation() +{ + CPtrArray array; + HideInfoDialog(); + brush_t* pEntities = NULL; + if (g_strSmartEntity.Find("Smart_Train") >= 0) + { + g_bScreenUpdates = false; + CreateRightClickEntity(g_pParentWnd->ActiveXY(), g_nSmartX, g_nSmartY, "func_train"); + array.Add(reinterpret_cast(selected_brushes.next)); + for (int n = 0; n < g_nPathCount; n++) + { + Select_Deselect(); + CreateRightClickEntity(g_pParentWnd->ActiveXY(), g_PathPoints[n].m_ptScreen.x,g_PathPoints[n].m_ptScreen.y, "path_corner"); + array.Add(reinterpret_cast(selected_brushes.next)); + } + + for (n = 0; n < g_nPathCount; n++) + { + Select_Deselect(); + Select_Brush(reinterpret_cast(array.GetAt(n))); + Select_Brush(reinterpret_cast(array.GetAt(n+1))); + ConnectEntities(); + } + g_bScreenUpdates = true; + + } + g_nPathCount = 0; + g_bPathMode = false; + Sys_UpdateWindows(W_ALL); +} + +void CXYWnd::KillPathMode() +{ + g_bSmartGo = false; + g_bPathMode = false; + if (g_pPathFunc) + g_pPathFunc(false, g_nPathCount); + g_nPathCount = 0; + g_pPathFunc = NULL; + Sys_UpdateWindows(W_ALL); +} + +#ifdef QUAKE3 +void ViewScript(LPCSTR psAppCommand, LPCSTR psScriptnameBaseOnly) +{ + CString strExec = psAppCommand; // eg "start q:\\bin_nt\\behaved.exe"; + strExec+= " "; + strExec+= ValueForKey(g_qeglobals.d_project_entity, "basepath"); + strExec+= "\\scripts\\"; + strExec+= psScriptnameBaseOnly; //ScriptPopup_GetScriptName(nID - ID_SCRIPTPOPUP_BEHAVED_ID_START); + strExec+= ".txt"; + + strExec.Replace("/","\\"); // otherwise it only works under NT... + + char sBatchFilename[512]; + + GetTempPath(sizeof(sBatchFilename), sBatchFilename); + strcat(sBatchFilename,"~temp.bat"); + + FILE *handle = fopen(sBatchFilename,"wt"); + fprintf(handle,strExec); + fprintf(handle,"\n"); + fclose(handle); + + STARTUPINFO startupinfo; + PROCESS_INFORMATION ProcessInformation; + + GetStartupInfo (&startupinfo); + + BOOL ret = CreateProcess(sBatchFilename, + //batpath, // pointer to name of executable module + NULL, // pointer to command line string + NULL, // pointer to process security attributes + NULL, // pointer to thread security attributes + FALSE, // handle inheritance flag + 0 /*DETACHED_PROCESS*/, // creation flags + NULL, // pointer to new environment block + NULL, // pointer to current directory name + &startupinfo, // pointer to STARTUPINFO + &ProcessInformation // pointer to PROCESS_INFORMATION + ); +// remove(sBatchFilename); // if you do this, the CreateProcess fails, presumably it needs it for a few seconds +} +#endif + +void CXYWnd::OnScriptPopup_Notepad(unsigned int nID) +{ +#ifdef QUAKE3 + ViewScript("start notepad", ScriptPopup_GetScriptName(nID - ID_SCRIPTPOPUP_NOTEPAD_ID_START)); +#endif +} + +void CXYWnd::OnScriptPopup_MSDev(unsigned int nID) +{ +#ifdef QUAKE3 + ViewScript("start msdev.exe", ScriptPopup_GetScriptName(nID - ID_SCRIPTPOPUP_MSDEV_ID_START)); +#endif +} + +void CXYWnd::OnScriptPopup_BehavEd(unsigned int nID) +{ +#ifdef QUAKE3 + extern CString g_cstrBehavEdPath; + ViewScript(va("start %s",g_cstrBehavEdPath), ScriptPopup_GetScriptName(nID - ID_SCRIPTPOPUP_BEHAVED_ID_START)); +#endif +} + +void CXYWnd::OnPopup_GroupNamesShowHide(unsigned int nID) +{ + Grouping_ToggleGroupState(nID - ID_SCRIPTPOPOP_GROUPNAMES_ID_START); +} + +void CXYWnd::OnPopup_GroupNamesSelect(unsigned int nID) +{ + Grouping_Select(nID - ID_SELECTION_GROUPNAMES_SELECT_ID_START); +} + + + + +// returns NULL if CANCEL, else input string +// +LPCSTR GetString(LPCSTR psPrompt) +{ + static CString strReturn; + + CGetString Input(psPrompt,&strReturn); + if (Input.DoModal() == IDOK) + { + strReturn.TrimLeft(); + strReturn.TrimRight(); + + return (LPCSTR)strReturn; + } + + return NULL; +} + + +LPCSTR GetNewGroupName() +{ + LPCSTR psGroupName = GetString(sNEWGROUPNAMEPROMPT); + + if (psGroupName) + { + // check for whitespace, since I specifically told them in the prompt not to use any... + // + CString strTeststring(psGroupName); + if (strTeststring.FindOneOf(" \t")!= -1) + { + // I usually prefer being ruder for in-house, but since this'll get released... + // + PLAY_LAME_WAV; + ErrorBox("Sigh.... for those of you who can't read very well I'll say it again....\n\n\n" + "DON'T PUT SPACES IN THE NAME !!!, ( OR TABS EITHER !!! )\n\n\n\nHonestly, some people..."); + return NULL; + } + } + + return psGroupName; +} + + + +// 'nID' will be one higher than normal when entering here, since 0 is ... +// +void CXYWnd::OnPopup_GroupNamesAddSelectedTo(unsigned int nID) +{ + nID -= ID_SELECTION_GROUPNAMES_ADDSELECTEDTO_ID_START; + + LPCSTR psGroupToAddTo = NULL; + + if (nID==0) + { + // this is the "" option, so... + // + psGroupToAddTo = GetNewGroupName(); + } + else + { + psGroupToAddTo = Grouping_GetListEntry(nID-1); + } + + + if (psGroupToAddTo) + { + Grouping_AddSelectedTo(psGroupToAddTo); + Sys_UpdateWindows(W_ALL); + } +} + +void CXYWnd::OnPopup_GroupNamesAddSelectedEClassTo(unsigned int nID) +{ + nID -= ID_SELECTION_GROUPNAMES_ADDSELECTEDECLASSTO_ID_START; + + LPCSTR psGroupToAddTo = NULL; + + if (nID==0) + { + // this is the "" option, so... + // + psGroupToAddTo = GetNewGroupName(); + } + else + { + psGroupToAddTo = Grouping_GetListEntry(nID-1); + } + + + if (psGroupToAddTo) + { + Grouping_AddSelectedEClassTo(psGroupToAddTo); + Sys_UpdateWindows(W_ALL); + } +} + +void CXYWnd::OnPopup_GroupNamesAddSelectedModelsTo(unsigned int nID) +{ + nID -= ID_SELECTION_GROUPNAMES_ADDSELECTEDMODELSTO_ID_START; + + LPCSTR psGroupToAddTo = NULL; + + if (nID==0) + { + // this is the "" option, so... + // + psGroupToAddTo = GetNewGroupName(); + } + else + { + psGroupToAddTo = Grouping_GetListEntry(nID-1); + } + + + if (psGroupToAddTo) + { + Grouping_AddSelectedModelsTo(psGroupToAddTo); + Sys_UpdateWindows(W_ALL); + } +} + + +void CXYWnd::OnEntityCreate(unsigned int nID) +{ + if (m_mnuDrop.GetSafeHmenu()) + { + CString strItem; + m_mnuDrop.GetMenuString(nID, strItem, MF_BYCOMMAND); + + if (strItem.Find("Smart_") >= 0) + { + CreateSmartEntity(this, m_ptDown.x, m_ptDown.y, strItem); + } + else + { + CreateRightClickEntity(this, m_ptDown.x, m_ptDown.y, strItem.GetBuffer(0)); + } + + Sys_UpdateWindows(W_ALL); + //OnLButtonDown((MK_LBUTTON | MK_SHIFT), CPoint(m_ptDown.x+2, m_ptDown.y+2)); + } +} + + +void CXYWnd::HandleDrop() +{ + static unsigned int uiPopupMenu_Grouping; + static unsigned int uiPopupMenu_GroupingSelect; + static unsigned int uiPopupMenu_GroupingAddSelectedTo; + static unsigned int uiPopupMenu_GroupingAddSelectedEClassTo; + static unsigned int uiPopupMenu_GroupingAddSelectedModelsTo; + static unsigned int uiPopupMenu_Script_NOTEPAD; + static unsigned int uiPopupMenu_Script_MSDEV; + static unsigned int uiPopupMenu_Script_BEHAVED; + static CMenu* cmenuPopup_Grouping = NULL; + static CMenu* cmenuPopup_GroupingSelect = NULL; + static CMenu* cmenuPopup_GroupingAddSelectedTo = NULL; + static CMenu* cmenuPopup_GroupingAddSelectedEClassTo = NULL; + static CMenu* cmenuPopup_GroupingAddSelectedModelsTo = NULL; + static CMenu* cmenuPopup_Script_NOTEPAD = NULL; + static CMenu* cmenuPopup_Script_MSDEV = NULL; + static CMenu* cmenuPopup_Script_BEHAVED = NULL; + + if (g_PrefsDlg.m_bRightClick == false) + return; + if (!m_mnuDrop.GetSafeHmenu()) // first time, load it up + { + m_mnuDrop.CreatePopupMenu(); + int nID = ID_ENTITY_START; + + CMenu* pChild2 = new CMenu; + pChild2->CreateMenu(); + pChild2->AppendMenu(MF_STRING, ID_SELECTION_SELECTCOMPLETETALL, "Select Complete Tall"); + pChild2->AppendMenu(MF_STRING, ID_SELECTION_SELECTTOUCHING, "Select Touching"); + pChild2->AppendMenu(MF_STRING, ID_SELECTION_SELECTPARTIALTALL, "Select Partial Tall"); + pChild2->AppendMenu(MF_STRING, ID_SELECTION_SELECTINSIDE, "Select Inside"); + m_mnuDrop.AppendMenu(MF_POPUP, reinterpret_cast(pChild2->GetSafeHmenu()), "Select"); + m_mnuDrop.AppendMenu(MF_STRING, ID_SELECTION_UNGROUPENTITY, "Ungroup Entity"); + m_mnuDrop.AppendMenu(MF_STRING, ID_SELECTION_MAKE_DETAIL, "Make Detail"); + m_mnuDrop.AppendMenu(MF_STRING, ID_SELECTION_MAKE_STRUCTURAL, "Make Structural"); + + CMenu* pChild = NULL; + + {// my grouping stuff... + + m_mnuDrop.AppendMenu(MF_SEPARATOR, nID++, ""); + + // + // "Grouping: Active" + // + m_mnuDrop.AppendMenu(MF_STRING | (bGrouping_IsActive)?MF_CHECKED:MF_UNCHECKED, + ID_SELECTION_GROUPNAMES_ACTIVE, "Grouping: Active" + ); + + // + // "Grouping: Hidden = Ghosted" + // + m_mnuDrop.AppendMenu(MF_STRING | (bGrouping_IsGhosted)?MF_CHECKED:MF_UNCHECKED, + ID_SELECTION_GROUPNAMES_GHOSTED, "Grouping: Hidden = Ghosted" + ); + + // + // "Grouping: Display Names"... + // + m_mnuDrop.AppendMenu(MF_STRING | (g_qeglobals.d_savedinfo.exclude & SHOW_GROUPNAMES)?MF_CHECKED:MF_UNCHECKED, + ID_SELECTION_GROUPNAMES_DISPLAY, "Grouping: Display Names" + ); + + + m_mnuDrop.AppendMenu(MF_SEPARATOR, nID++, ""); + + + // + // "Grouping: Show/Hide"... + // + pChild = new CMenu; // LEAK? + pChild->CreateMenu(); + // + // for (_i=0; ; _i++) + // { + // this menu changes dynamically, so build it at the end of this function, not in the once-only part! + // pChild->AppendMenu(MF_STRING, ID_SCRIPTPOPUP_GROUPNAMES_ID_START, "test string only"); + // } + uiPopupMenu_Grouping = reinterpret_cast(pChild->GetSafeHmenu()); + m_mnuDrop.AppendMenu(MF_POPUP, uiPopupMenu_Grouping, "Grouping: Show/Hide"); + cmenuPopup_Grouping = pChild; + + + // "Grouping: Select"... + // + pChild = new CMenu; // LEAK? + pChild->CreateMenu(); + // + // for (_i=0; ; _i++) + // { + // this menu changes dynamically, so build it at the end of this function, not in the once-only part! + // pChild->AppendMenu(MF_STRING, ID_SELECTION_GROUPNAMES_SELECT_ID_START, "test string only"); + // } + uiPopupMenu_GroupingSelect = reinterpret_cast(pChild->GetSafeHmenu()); + m_mnuDrop.AppendMenu(MF_POPUP, uiPopupMenu_GroupingSelect, "Grouping: Select"); + cmenuPopup_GroupingSelect = pChild; + + + // kinda nicer with a seperator, to space out the "Grouping: Add functions"... + // +// m_mnuDrop.AppendMenu(MF_SEPARATOR, nID++, ""); // ... no it isn't + + + // "Grouping: Add to group"... + // + pChild = new CMenu; // LEAK? + pChild->CreateMenu(); + // + // for (_i=0; ; _i++) + // { + // this menu changes dynamically, so build it at the end of this function, not in the once-only part! + // pChild->AppendMenu(MF_STRING, ID_SELECTION_GROUPNAMES_ADDSELECTEDTO_ID_START, "test string only"); + // } + uiPopupMenu_GroupingAddSelectedTo = reinterpret_cast(pChild->GetSafeHmenu()); + m_mnuDrop.AppendMenu(MF_POPUP, uiPopupMenu_GroupingAddSelectedTo, "Grouping: Add to group"); + cmenuPopup_GroupingAddSelectedTo = pChild; + + + + // "Grouping: Add ALL ents of eclass to group"... + // + pChild = new CMenu; // LEAK? + pChild->CreateMenu(); + // + // for (_i=0; ; _i++) + // { + // this menu changes dynamically, so build it at the end of this function, not in the once-only part! + // pChild->AppendMenu(MF_STRING, ID_SELECTION_GROUPNAMES_ADDSELECTEDECLASSTO_ID_START, "test string only"); + // } + uiPopupMenu_GroupingAddSelectedEClassTo = reinterpret_cast(pChild->GetSafeHmenu()); + m_mnuDrop.AppendMenu(MF_POPUP, uiPopupMenu_GroupingAddSelectedEClassTo, "Grouping: Add ALL ents of eclass to group"); + cmenuPopup_GroupingAddSelectedEClassTo = pChild; + + + + // "Grouping: Add ALL ents using models to group"... + // + pChild = new CMenu; // LEAK? + pChild->CreateMenu(); + // + // for (_i=0; ; _i++) + // { + // this menu changes dynamically, so build it at the end of this function, not in the once-only part! + // pChild->AppendMenu(MF_STRING, ID_SELECTION_GROUPNAMES_ADDSELECTEDMODELSTO_ID_START, "test string only"); + // } + uiPopupMenu_GroupingAddSelectedModelsTo = reinterpret_cast(pChild->GetSafeHmenu()); + m_mnuDrop.AppendMenu(MF_POPUP, uiPopupMenu_GroupingAddSelectedModelsTo, "Grouping: Add ALL ents using models to group"); + cmenuPopup_GroupingAddSelectedModelsTo = pChild; + + + //m_mnuDrop.AppendMenu(MF_SEPARATOR, nID++, ""); + } + +#ifdef QUAKE3 + m_mnuDrop.AppendMenu(MF_SEPARATOR, nID++, ""); + m_mnuDrop.AppendMenu(MF_STRING, ID_SELECTION_HIDEWAYPOINTCHILDREN, "Hide Waypoint Children"); + m_mnuDrop.AppendMenu(MF_STRING, ID_SELECTION_UNHIDEWAYPOINTCHILDREN, "UnHide Waypoint Children"); + m_mnuDrop.AppendMenu(MF_STRING, ID_SELECTION_UNHIDEALLWAYPOINTS, "UnHide All Waypoints"); + m_mnuDrop.AppendMenu(MF_SEPARATOR, nID++, ""); + + // "send script to..." + // + // NOTEPAD... + // + pChild = new CMenu; + pChild->CreateMenu(); + for (int _i=0; _iAppendMenu(MF_STRING, ID_SCRIPTPOPUP_NOTEPAD_ID_START + _i, ScriptPopup_GetField(_i)); + } + uiPopupMenu_Script_NOTEPAD = reinterpret_cast(pChild->GetSafeHmenu()); + m_mnuDrop.AppendMenu(MF_POPUP, uiPopupMenu_Script_NOTEPAD, "Send Script to Notepad"); + cmenuPopup_Script_NOTEPAD = pChild; + // + // MSDEV... + // + pChild = new CMenu; + pChild->CreateMenu(); + for (_i=0; _iAppendMenu(MF_STRING, ID_SCRIPTPOPUP_MSDEV_ID_START + _i, ScriptPopup_GetField(_i)); + } + uiPopupMenu_Script_MSDEV = reinterpret_cast(pChild->GetSafeHmenu()); + m_mnuDrop.AppendMenu(MF_POPUP, uiPopupMenu_Script_MSDEV, "Send Script to MSDev"); + cmenuPopup_Script_MSDEV = pChild; + // + // BEHAVED... + // + pChild = new CMenu; + pChild->CreateMenu(); + for (_i=0; _iAppendMenu(MF_STRING, ID_SCRIPTPOPUP_BEHAVED_ID_START + _i, ScriptPopup_GetField(_i)); + } + uiPopupMenu_Script_BEHAVED = reinterpret_cast(pChild->GetSafeHmenu()); + m_mnuDrop.AppendMenu(MF_POPUP, uiPopupMenu_Script_BEHAVED, "Send Script to BehavEd"); + cmenuPopup_Script_BEHAVED = pChild; + + pChild = NULL; +#endif + + m_mnuDrop.AppendMenu(MF_SEPARATOR, nID++, ""); + + pChild = new CMenu; + pChild->CreateMenu(); + pChild->AppendMenu(MF_STRING, nID++, "Smart_Train"); + //pChild->AppendMenu(MF_STRING, nID++, "Smart_Rotating"); + m_mnuDrop.AppendMenu(MF_POPUP, reinterpret_cast(pChild->GetSafeHmenu()), "Smart Entities"); + m_mnuDrop.AppendMenu(MF_SEPARATOR, nID++, ""); + + pChild = NULL; + eclass_t *e; + CString strActive; + CString strLast; + CString strName; + for (e=eclass ; e ; e=e->next) + { + strLast = strName; + strName = e->name; + int n_ = strName.Find("_"); + if (n_ > 0) + { + CString strLeft = strName.Left(n_); + CString strRight = strName.Right(strName.GetLength() - n_ - 1); + if (strLeft == strActive) // this is a child + { + ASSERT(pChild); + pChild->AppendMenu(MF_STRING, nID++, strName); + } + else + { + if (pChild) + { + m_mnuDrop.AppendMenu(MF_POPUP, reinterpret_cast(pChild->GetSafeHmenu()), strActive); + g_ptrMenus.Add(pChild); + //pChild->DestroyMenu(); + //delete pChild; + pChild = NULL; + } + strActive = strLeft; + pChild = new CMenu; + pChild->CreateMenu(); + pChild->AppendMenu(MF_STRING, nID++, strName); + } + } + else + { + if (pChild) + { + m_mnuDrop.AppendMenu(MF_POPUP, reinterpret_cast(pChild->GetSafeHmenu()), strActive); + g_ptrMenus.Add(pChild); + //pChild->DestroyMenu(); + //delete pChild; + pChild = NULL; + } + strActive = ""; + m_mnuDrop.AppendMenu(MF_STRING, nID++, strName); + } + } + } + + // the menu is created at this point, now do some selective enabling/greying for niceness... + // +#ifdef QUAKE3 + // + // send script to NOTEPAD... + // + m_mnuDrop.EnableMenuItem(uiPopupMenu_Script_NOTEPAD, MF_BYCOMMAND | ScriptPopup_Allowed()?MF_ENABLED:MF_GRAYED) ; + for (int _i=0; _iEnableMenuItem(ID_SCRIPTPOPUP_NOTEPAD_ID_START + _i, MF_BYCOMMAND | (ScriptPopup_GetScriptName(_i)?MF_ENABLED:MF_GRAYED)); + } + // + // send script to MSDEV... + // + m_mnuDrop.EnableMenuItem(uiPopupMenu_Script_MSDEV, MF_BYCOMMAND | ScriptPopup_Allowed()?MF_ENABLED:MF_GRAYED) ; + for (_i=0; _iEnableMenuItem(ID_SCRIPTPOPUP_MSDEV_ID_START + _i, MF_BYCOMMAND | (ScriptPopup_GetScriptName(_i)?MF_ENABLED:MF_GRAYED)); + } + // + // send script to BEHAVED... + // + m_mnuDrop.EnableMenuItem(uiPopupMenu_Script_BEHAVED, MF_BYCOMMAND | ScriptPopup_Allowed()?MF_ENABLED:MF_GRAYED) ; + for (_i=0; _iEnableMenuItem(ID_SCRIPTPOPUP_BEHAVED_ID_START + _i, MF_BYCOMMAND | (ScriptPopup_GetScriptName(_i)?MF_ENABLED:MF_GRAYED)); + } +#endif + + + + // menu is created at this point, now do any selective menu-populating and/or greying/checking etc... + + {// fill in some special popup menus to do with grouping... + + Grouping_ConstructUsageList(); // MUST occur before any other menu groupname function calls + int iGroupListCount = Grouping_GetListCount(); + + + // + // "Grouping: Active" + // + m_mnuDrop.CheckMenuItem(ID_SELECTION_GROUPNAMES_ACTIVE, MF_BYCOMMAND | (bGrouping_IsActive)?MF_CHECKED:MF_UNCHECKED); + + // + // "Grouping: Hidden = Ghosted" + // + m_mnuDrop.CheckMenuItem (ID_SELECTION_GROUPNAMES_GHOSTED, MF_BYCOMMAND | (bGrouping_IsGhosted)?MF_CHECKED:MF_UNCHECKED); + m_mnuDrop.EnableMenuItem(ID_SELECTION_GROUPNAMES_GHOSTED, MF_BYCOMMAND | (bGrouping_IsActive )?MF_ENABLED:MF_GRAYED); + + + // + // "Grouping: shownames"... + // + m_mnuDrop.CheckMenuItem (ID_SELECTION_GROUPNAMES_DISPLAY, MF_BYCOMMAND | (g_qeglobals.d_savedinfo.exclude & SHOW_GROUPNAMES)?MF_CHECKED:MF_UNCHECKED); + m_mnuDrop.EnableMenuItem(ID_SELECTION_GROUPNAMES_DISPLAY, MF_BYCOMMAND | (bGrouping_IsActive)?MF_ENABLED:MF_GRAYED); + + // + // "Grouping show/hide"... + // + { + // empty previous entries first, then re-build the menu... + // + while (cmenuPopup_Grouping->DeleteMenu(0, MF_BYPOSITION)); // + // + // re-build... + // + m_mnuDrop.EnableMenuItem(uiPopupMenu_Grouping, MF_BYCOMMAND | (iGroupListCount && bGrouping_IsActive)?MF_ENABLED:MF_GRAYED); + Grouping_BeginListRead(); + for (int _i=0; _iAppendMenu(MF_STRING | (bOnOff?MF_CHECKED:MF_UNCHECKED), + ID_SCRIPTPOPOP_GROUPNAMES_ID_START + _i, + psText + ); + } + } + + // + // "Grouping select"... + // + { + // empty previous entries first, then re-build the menu... + // + while (cmenuPopup_GroupingSelect->DeleteMenu(0, MF_BYPOSITION)); // + // + // re-build... + // + m_mnuDrop.EnableMenuItem(uiPopupMenu_GroupingSelect, MF_BYCOMMAND | (iGroupListCount && bGrouping_IsActive)?MF_ENABLED:MF_GRAYED); + Grouping_BeginListRead(); + for (int _i=0; _iAppendMenu(MF_STRING, ID_SELECTION_GROUPNAMES_SELECT_ID_START + _i, psText); + } + } + + // + // "Grouping: Add to group"... + // + { + // empty previous entries first, then re-build the menu... + // + while (cmenuPopup_GroupingAddSelectedTo->DeleteMenu(0, MF_BYPOSITION)); // + // + // re-build... + // + // (this items is only enabled if at least one brush is selected) + m_mnuDrop.EnableMenuItem(uiPopupMenu_GroupingAddSelectedTo, MF_BYCOMMAND | (bGrouping_IsActive && Grouping_AtLeastOneEntSelected())?MF_ENABLED:MF_GRAYED); + + // item at top... + // + cmenuPopup_GroupingAddSelectedTo->AppendMenu(MF_STRING, ID_SELECTION_GROUPNAMES_ADDSELECTEDTO_ID_START, ""); + // + // now add existing groups... + // + Grouping_BeginListRead(); + for (int _i=0; _iAppendMenu(MF_STRING, ID_SELECTION_GROUPNAMES_ADDSELECTEDTO_ID_START + _i + 1/* +1 to account for "" */, psText); + } + } + + // "Grouping: Add ALL ents of eclass to group"... + // + { + // empty previous entries first, then re-build the menu... + // + while (cmenuPopup_GroupingAddSelectedEClassTo->DeleteMenu(0, MF_BYPOSITION)); // + // + // re-build... + // + // (this items is only enabled if at least one brush is selected) + m_mnuDrop.EnableMenuItem(uiPopupMenu_GroupingAddSelectedEClassTo, MF_BYCOMMAND | (bGrouping_IsActive && Grouping_AtLeastOneEntSelected())?MF_ENABLED:MF_GRAYED); + + // item at top... + // + cmenuPopup_GroupingAddSelectedEClassTo->AppendMenu(MF_STRING, ID_SELECTION_GROUPNAMES_ADDSELECTEDECLASSTO_ID_START, ""); + // + // now add existing groups... + // + Grouping_BeginListRead(); + for (int _i=0; _iAppendMenu(MF_STRING, ID_SELECTION_GROUPNAMES_ADDSELECTEDECLASSTO_ID_START + _i + 1/* +1 to account for "" */, psText); + } + } + + + // "Grouping: Add ALL ents using models to group"... + // + { + // empty previous entries first, then re-build the menu... + // + while (cmenuPopup_GroupingAddSelectedModelsTo->DeleteMenu(0, MF_BYPOSITION)); // + // + // re-build... + // + // (this items is only enabled if at least one brush is selected) + m_mnuDrop.EnableMenuItem(uiPopupMenu_GroupingAddSelectedModelsTo, MF_BYCOMMAND | (bGrouping_IsActive && Grouping_AtLeastOneModelEntSelected())?MF_ENABLED:MF_GRAYED); + + // item at top... + // + cmenuPopup_GroupingAddSelectedModelsTo->AppendMenu(MF_STRING, ID_SELECTION_GROUPNAMES_ADDSELECTEDMODELSTO_ID_START, ""); + // + // now add existing groups... + // + Grouping_BeginListRead(); + for (int _i=0; _iAppendMenu(MF_STRING, ID_SELECTION_GROUPNAMES_ADDSELECTEDMODELSTO_ID_START + _i + 1/* +1 to account for "" */, psText); + } + } + } + + + CPoint ptMouse; + GetCursorPos(&ptMouse); + m_mnuDrop.TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON, ptMouse.x, ptMouse.y, this); +} + +// this is also now called by CMainFrame::OnMapHome +// +void CXYWnd::XY_Init() +{ + m_vOrigin[0] = 0; + m_vOrigin[1] = 20; + m_vOrigin[2] = 46; + m_fScale = 1; +} + +void CXYWnd::SnapToPoint (int x, int y, vec3_t point) +{ + if (g_bSnapToGrid) + XY_ToPoint(x, y, point); + else + XY_ToPoint(x, y, point); + //XY_ToPoint(x, y, point); +} + + +void CXYWnd::XY_ToPoint (int x, int y, vec3_t point) +{ + float fx = x; + float fy = y; + float fw = m_nWidth; + float fh = m_nHeight; + if (m_nViewType == XY) + { + point[0] = m_vOrigin[0] + (fx - fw / 2) / m_fScale; + point[1] = m_vOrigin[1] + (fy - fh / 2) / m_fScale; + //point[2] = 0; + } + else if (m_nViewType == YZ) + { + ////point[0] = 0; + //point[1] = m_vOrigin[0] + (fx - fw / 2) / m_fScale; + //point[2] = m_vOrigin[1] + (fy - fh / 2 ) / m_fScale; + point[1] = m_vOrigin[1] + (fx - fw / 2) / m_fScale; + point[2] = m_vOrigin[2] + (fy - fh / 2 ) / m_fScale; + } + else + { + //point[0] = m_vOrigin[0] + (fx - fw / 2) / m_fScale; + ////point[1] = 0; + //point[2] = m_vOrigin[1] + (fy - fh / 2) / m_fScale; + point[0] = m_vOrigin[0] + (fx - fw / 2) / m_fScale; + //point[1] = 0; + point[2] = m_vOrigin[2] + (fy - fh / 2) / m_fScale; + } +} + + +void CXYWnd::XY_ToGridPoint (int x, int y, vec3_t point) +{ + if (m_nViewType == XY) + { + point[0] = m_vOrigin[0] + (x - m_nWidth / 2) / m_fScale; + point[1] = m_vOrigin[1] + (y - m_nHeight / 2) / m_fScale; + //point[2] = 0; + point[0] = floor(point[0] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + point[1] = floor(point[1] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + } + else if (m_nViewType == YZ) + { + //point[0] = 0; + //point[1] = m_vOrigin[0] + (x - m_nWidth / 2) / m_fScale; + //point[2] = m_vOrigin[1] + (y - m_nHeight / 2) / m_fScale; + point[1] = m_vOrigin[1] + (x - m_nWidth / 2) / m_fScale; + point[2] = m_vOrigin[2] + (y - m_nHeight / 2) / m_fScale; + point[1] = floor(point[1] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + point[2] = floor(point[2] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + } + else + { + //point[1] = 0; + //point[0] = m_vOrigin[0] + (x - m_nWidth / 2) / m_fScale; + //point[2] = m_vOrigin[1] + (y - m_nHeight / 2) / m_fScale; + point[0] = m_vOrigin[0] + (x - m_nWidth / 2) / m_fScale; + point[2] = m_vOrigin[2] + (y - m_nHeight / 2) / m_fScale; + point[0] = floor(point[0] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + point[2] = floor(point[2] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + } +} + + +void CXYWnd::XY_MouseDown (int x, int y, int buttons) +{ + + vec3_t point; + vec3_t origin, dir, right, up; + + m_nButtonstate = buttons; + m_nPressx = x; + m_nPressy = y; + VectorCopy (vec3_origin, m_vPressdelta); + + XY_ToPoint (x, y, point); + + VectorCopy (point, origin); + + dir[0] = 0; dir[1] = 0; dir[2] = 0; + if (m_nViewType == XY) + { + origin[2] = MAX_WORLD_COORD; + dir[2] = -1; + right[0] = 1 / m_fScale; + right[1] = 0; + right[2] = 0; + up[0] = 0; + up[1] = 1 / m_fScale; + up[2] = 0; + } + else if (m_nViewType == YZ) + { + origin[0] = MAX_WORLD_COORD; + dir[0] = -1; + right[1] = 1 / m_fScale; + right[2] = 0; + right[0] = 0; + up[0] = 0; + up[2] = 1 / m_fScale; + up[1] = 0; + } + else + { + origin[1] = MAX_WORLD_COORD; + dir[1] = -1; + right[0] = 1 / m_fScale; + right[2] = 0; + right[1] = 0; + up[0] = 0; + up[2] = 1 / m_fScale; + up[1] = 0; + } + + + m_bPress_selection = (selected_brushes.next != &selected_brushes); + + GetCursorPos(&m_ptCursor); + //Sys_GetCursorPos (&m_ptCursor.x, &m_ptCursor.y); + + // lbutton = manipulate selection + // shift-LBUTTON = select + if ( (buttons == MK_LBUTTON) + || (buttons == (MK_LBUTTON | MK_SHIFT)) + || (buttons == (MK_LBUTTON | MK_CONTROL)) + || (buttons == (MK_LBUTTON | MK_CONTROL | MK_SHIFT)) ) + { + Patch_SetView( (m_nViewType == XY) ? W_XY : (m_nViewType == YZ) ? W_YZ : W_XZ); + Drag_Begin (x, y, buttons, right, up, origin, dir); + return; + } + + int nMouseButton = g_PrefsDlg.m_nMouseButtons == 2 ? MK_RBUTTON : MK_MBUTTON; + + // control mbutton = move camera + if (m_nButtonstate == (MK_CONTROL|nMouseButton) ) + { + VectorCopyXY(point, g_pParentWnd->GetCamera()->Camera().origin); + Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY); + } + + // mbutton = angle camera + if ((g_PrefsDlg.m_nMouseButtons == 3 && m_nButtonstate == MK_MBUTTON) || + (g_PrefsDlg.m_nMouseButtons == 2 && m_nButtonstate == (MK_SHIFT|MK_CONTROL|MK_RBUTTON))) + { + VectorSubtract (point, g_pParentWnd->GetCamera()->Camera().origin, point); + + int n1 = (m_nViewType == XY) ? 1 : 2; + int n2 = (m_nViewType == YZ) ? 1 : 0; + int nAngle = (m_nViewType == XY) ? YAW : PITCH; + if (point[n1] || point[n2]) + { + g_pParentWnd->GetCamera()->Camera().angles[nAngle] = 180/Q_PI*atan2 (point[n1], point[n2]); + Sys_UpdateWindows (W_CAMERA_IFON|W_XY_OVERLAY); + } + } + + // shift mbutton = move z checker + if (m_nButtonstate == (MK_SHIFT | nMouseButton)) + { + if (RotateMode() || g_bPatchBendMode) + { + XY_ToGridPoint (x, y, point); + VectorCopyXY(point, g_vRotateOrigin); + if (g_bPatchBendMode) + { + VectorCopy(point, g_vBendOrigin); + } + Sys_UpdateWindows (W_XY); + return; + } + else + { + SnapToPoint (x, y, point); + if (m_nViewType == XY) + { + z.origin[0] = point[0]; + z.origin[1] = point[1]; + } + else if (m_nViewType == YZ) + { + z.origin[0] = point[1]; + z.origin[1] = point[2]; + } + else + { + z.origin[0] = point[0]; + z.origin[1] = point[2]; + } + Sys_UpdateWindows (W_XY_OVERLAY|W_Z); + return; + } + } +} + + +void CXYWnd::XY_MouseUp(int x, int y, int buttons) +{ + Drag_MouseUp (buttons); + if (!m_bPress_selection) + Sys_UpdateWindows (W_ALL); + m_nButtonstate = 0; + while (::ShowCursor(TRUE) < 0) + ; +} + +qboolean CXYWnd::DragDelta (int x, int y, vec3_t move) +{ + vec3_t xvec, yvec, delta; + int i; + + xvec[0] = 1 / m_fScale; + xvec[1] = xvec[2] = 0; + yvec[1] = 1 / m_fScale; + yvec[0] = yvec[2] = 0; + + for (i=0 ; i<3 ; i++) + { + delta[i] = xvec[i] * (x - m_nPressx) + yvec[i] * (y - m_nPressy); + delta[i] = floor(delta[i] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + } + VectorSubtract (delta, m_vPressdelta, move); + VectorCopy (delta, m_vPressdelta); + + if (move[0] || move[1] || move[2]) + return true; + return false; +} + + +/* +============== +NewBrushDrag +============== +*/ +void CXYWnd::NewBrushDrag (int x, int y) +{ + vec3_t mins, maxs, junk; + int i; + float temp; + brush_t *n; + + if (!DragDelta (x,y, junk)) + return; + + // delete the current selection + if (selected_brushes.next != &selected_brushes) + Brush_Free (selected_brushes.next); + + XY_ToGridPoint (m_nPressx, m_nPressy, mins); + + int nDim = (m_nViewType == XY) ? 2 : (m_nViewType == YZ) ? 0 : 1; + + mins[nDim] = g_qeglobals.d_gridsize * ((int)(g_qeglobals.d_new_brush_bottom_z / g_qeglobals.d_gridsize)); + XY_ToGridPoint (x, y, maxs); + maxs[nDim] = g_qeglobals.d_gridsize * ((int)(g_qeglobals.d_new_brush_top_z / g_qeglobals.d_gridsize)); + if (maxs[nDim] <= mins[nDim]) + maxs[nDim] = mins[nDim] + g_qeglobals.d_gridsize; + + for (i=0 ; i<3 ; i++) + { + if (mins[i] == maxs[i]) + return; // don't create a degenerate brush + if (mins[i] > maxs[i]) + { + temp = mins[i]; + mins[i] = maxs[i]; + maxs[i] = temp; + } + } + + n = Brush_Create (mins, maxs, &g_qeglobals.d_texturewin.texdef); + if (!n) + return; + + vec3_t vSize; + VectorSubtract(maxs, mins, vSize); + g_strStatus.Format("Size X:: %.1f Y:: %.1f Z:: %.1f", vSize[0], vSize[1], vSize[2]); + g_pParentWnd->SetStatusText(2, g_strStatus); + + Brush_AddToList (n, &selected_brushes); + + Entity_LinkBrush (world_entity, n); + + Brush_Build( n ); + + // Sys_UpdateWindows (W_ALL); + Sys_UpdateWindows (W_XY| W_CAMERA); + +} + +/* +============== +XY_MouseMoved +============== +*/ +void CXYWnd::XY_MouseMoved (int x, int y, int buttons) +{ + vec3_t point; + + if (!m_nButtonstate) + return; + + // lbutton without selection = drag new brush + if (m_nButtonstate == MK_LBUTTON && !m_bPress_selection && g_qeglobals.d_select_mode != sel_curvepoint) + { + NewBrushDrag (x, y); + return; + } + + // lbutton (possibly with control and or shift) + // with selection = drag selection + if (m_nButtonstate & MK_LBUTTON) + { + Drag_MouseMoved (x, y, buttons); + Sys_UpdateWindows (W_XY_OVERLAY | W_CAMERA_IFON | W_Z); + return; + } + + int nMouseButton = g_PrefsDlg.m_nMouseButtons == 2 ? MK_RBUTTON : MK_MBUTTON; + // control mbutton = move camera + if (m_nButtonstate == (MK_CONTROL|nMouseButton) ) + { + SnapToPoint (x, y, point); + VectorCopyXY(point, g_pParentWnd->GetCamera()->Camera().origin); + Sys_UpdateWindows (W_XY_OVERLAY | W_CAMERA); + return; + } + + // shift mbutton = move z checker + if (m_nButtonstate == (MK_SHIFT|nMouseButton) ) + { + if (RotateMode() || g_bPatchBendMode) + { + XY_ToGridPoint (x, y, point); + VectorCopyXY(point, g_vRotateOrigin); + if (g_bPatchBendMode) + { + VectorCopy(point, g_vBendOrigin); + } + Sys_UpdateWindows (W_XY); + return; + } + else + { + SnapToPoint (x, y, point); + if (m_nViewType == XY) + { + z.origin[0] = point[0]; + z.origin[1] = point[1]; + } + else if (m_nViewType == YZ) + { + z.origin[0] = point[1]; + z.origin[1] = point[2]; + } + else + { + z.origin[0] = point[0]; + z.origin[1] = point[2]; + } + } + Sys_UpdateWindows (W_XY_OVERLAY|W_Z); + return; + } + + // mbutton = angle camera + if ((g_PrefsDlg.m_nMouseButtons == 3 && m_nButtonstate == MK_MBUTTON) || + (g_PrefsDlg.m_nMouseButtons == 2 && m_nButtonstate == (MK_SHIFT|MK_CONTROL|MK_RBUTTON))) + { + SnapToPoint (x, y, point); + VectorSubtract (point, g_pParentWnd->GetCamera()->Camera().origin, point); + + int n1 = (m_nViewType == XY) ? 1 : 2; + int n2 = (m_nViewType == YZ) ? 1 : 0; + int nAngle = (m_nViewType == XY) ? YAW : PITCH; + if (point[n1] || point[n2]) + { + g_pParentWnd->GetCamera()->Camera().angles[nAngle] = 180/Q_PI*atan2 (point[n1], point[n2]); + Sys_UpdateWindows (W_CAMERA_IFON|W_XY_OVERLAY); + } + return; + } + + // rbutton = drag xy origin + if (m_nButtonstate == MK_RBUTTON) + { + Sys_GetCursorPos (&x, &y); + if (x != m_ptCursor.x || y != m_ptCursor.y) + { + int nDim1 = (m_nViewType == YZ) ? 1 : 0; + int nDim2 = (m_nViewType == XY) ? 1 : 2; + m_vOrigin[nDim1] -= (x - m_ptCursor.x) / m_fScale; + m_vOrigin[nDim2] += (y - m_ptCursor.y) / m_fScale; + SetCursorPos (m_ptCursor.x, m_ptCursor.y); + ::ShowCursor(FALSE); + //XY_Draw(); + //RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); + Sys_UpdateWindows (W_XY | W_XY_OVERLAY); + //::ShowCursor(TRUE); + } + return; + } +} + + +/* +============================================================================ + +DRAWING + +============================================================================ +*/ + + +/* +============== +XY_DrawGrid +============== +*/ +void CXYWnd::XY_DrawGrid() +{ + float x, y, xb, xe, yb, ye, step; + int w, h; + char text[32]; + + qglDisable(GL_TEXTURE_2D); + qglDisable(GL_TEXTURE_1D); + qglDisable(GL_DEPTH_TEST); + qglDisable(GL_BLEND); + + w = m_nWidth / 2 / m_fScale; + h = m_nHeight / 2 / m_fScale; + + + int nDim1 = (m_nViewType == YZ) ? 1 : 0; + int nDim2 = (m_nViewType == XY) ? 1 : 2; + //int nDim1 = 0; + //int nDim2 = 1; + + step = 64.0f; + if(m_fScale < 0.2f) + { + step = 512.0f; + } + if(m_fScale < 0.02f) + { + step = 2048.0f; + } + + xb = m_vOrigin[nDim1] - w; + if (xb < region_mins[nDim1]) + xb = region_mins[nDim1]; + xb = step * floor (xb/step); + + xe = m_vOrigin[nDim1] + w; + if (xe > region_maxs[nDim1]) + xe = region_maxs[nDim1]; + xe = step * ceil (xe/step); + + yb = m_vOrigin[nDim2] - h; + if (yb < region_mins[nDim2]) + yb = region_mins[nDim2]; + yb = step * floor (yb/step); + + ye = m_vOrigin[nDim2] + h; + if (ye > region_maxs[nDim2]) + ye = region_maxs[nDim2]; + ye = step * ceil (ye/step); + + // draw major blocks + + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR]); + + if ( g_qeglobals.d_showgrid ) + { + + qglBegin (GL_LINES); + + for (x=xb ; x<=xe ; x+=step) + { + qglVertex2f (x, yb); + qglVertex2f (x, ye); + } + for (y=yb ; y<=ye ; y+=step) + { + qglVertex2f (xb, y); + qglVertex2f (xe, y); + } + + qglEnd (); + + } + + // draw minor blocks + if ( g_qeglobals.d_showgrid && g_qeglobals.d_gridsize * m_fScale >= 4 && + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR] != g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK]) + { + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR]); + + qglBegin (GL_LINES); + for (x=xb ; xGetZWnd()->m_pZClip) // should always be the case at this point I think, but this is safer + { + if (g_pParentWnd->GetZWnd()->m_pZClip->IsEnabled()) + { + qglColor3f(ZCLIP_COLOUR); + qglLineWidth(2); + qglBegin (GL_LINES); + + qglVertex2f (xb, g_pParentWnd->GetZWnd()->m_pZClip->GetTop()); + qglVertex2f (xe, g_pParentWnd->GetZWnd()->m_pZClip->GetTop()); + + qglVertex2f (xb, g_pParentWnd->GetZWnd()->m_pZClip->GetBottom()); + qglVertex2f (xe, g_pParentWnd->GetZWnd()->m_pZClip->GetBottom()); + + qglEnd (); + qglLineWidth(1); + } + } + } + + + // draw coordinate text if needed + + if ( g_qeglobals.d_savedinfo.show_coordinates) + { + //glColor4f(0, 0, 0, 0); + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT]); + + for (x=xb ; x region_maxs[nDim1]) + xe = region_maxs[nDim1]; + xe = 1024 * ceil (xe/1024); + + yb = m_vOrigin[nDim2] - h; + if (yb < region_mins[nDim2]) + yb = region_mins[nDim2]; + yb = 1024 * floor (yb/1024); + + ye = m_vOrigin[nDim2] + h; + if (ye > region_maxs[nDim2]) + ye = region_maxs[nDim2]; + ye = 1024 * ceil (ye/1024); + + // draw major blocks + + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK]); + qglLineWidth (2); + + qglBegin (GL_LINES); + + for (x=xb ; x<=xe ; x+=1024) + { + qglVertex2f (x, yb); + qglVertex2f (x, ye); + } + for (y=yb ; y<=ye ; y+=1024) + { + qglVertex2f (xb, y); + qglVertex2f (xe, y); + } + + qglEnd (); + qglLineWidth (1); + + // draw coordinate text if needed + + for (x=xb ; xGetCamera()->Camera().origin[0]; + y = g_pParentWnd->GetCamera()->Camera().origin[1]; + a = g_pParentWnd->GetCamera()->Camera().angles[YAW]/180*Q_PI; + } + else if (m_nViewType == YZ) + { + x = g_pParentWnd->GetCamera()->Camera().origin[1]; + y = g_pParentWnd->GetCamera()->Camera().origin[2]; + a = g_pParentWnd->GetCamera()->Camera().angles[PITCH]/180*Q_PI; + } + else + { + x = g_pParentWnd->GetCamera()->Camera().origin[0]; + y = g_pParentWnd->GetCamera()->Camera().origin[2]; + a = g_pParentWnd->GetCamera()->Camera().angles[PITCH]/180*Q_PI; + } + + qglColor3f (0.0, 0.0, 1.0); + qglBegin(GL_LINE_STRIP); + qglVertex3f (x-16,y,0); + qglVertex3f (x,y+8,0); + qglVertex3f (x+16,y,0); + qglVertex3f (x,y-8,0); + qglVertex3f (x-16,y,0); + qglVertex3f (x+16,y,0); + qglEnd (); + + qglBegin(GL_LINE_STRIP); + qglVertex3f (x+48*cos(a+Q_PI/4), y+48*sin(a+Q_PI/4), 0); + qglVertex3f (x, y, 0); + qglVertex3f (x+48*cos(a-Q_PI/4), y+48*sin(a-Q_PI/4), 0); + qglEnd (); + +#if 0 + char text[128]; + qglRasterPos2f (x+64, y+64); + sprintf (text, "%f",g_pParentWnd->GetCamera()->Camera().angles[YAW]); + qglCallLists (strlen(text), GL_UNSIGNED_BYTE, text); +#endif +} + +void CXYWnd::DrawZIcon (void) +{ + if (m_nViewType == XY) + { + float x = z.origin[0]; + float y = z.origin[1]; + qglEnable (GL_BLEND); + qglDisable (GL_TEXTURE_2D); + qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + qglDisable (GL_CULL_FACE); + qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + qglColor4f (0.0, 0.0, 1.0, 0.25); + qglBegin(GL_QUADS); + qglVertex3f (x-8,y-8,0); + qglVertex3f (x+8,y-8,0); + qglVertex3f (x+8,y+8,0); + qglVertex3f (x-8,y+8,0); + qglEnd (); + qglDisable (GL_BLEND); + + qglColor4f (0.0, 0.0, 1.0, 1); + + qglBegin(GL_LINE_LOOP); + qglVertex3f (x-8,y-8,0); + qglVertex3f (x+8,y-8,0); + qglVertex3f (x+8,y+8,0); + qglVertex3f (x-8,y+8,0); + qglEnd (); + + qglBegin(GL_LINE_STRIP); + qglVertex3f (x-4,y+4,0); + qglVertex3f (x+4,y+4,0); + qglVertex3f (x-4,y-4,0); + qglVertex3f (x+4,y-4,0); + qglEnd (); + } +} + + +/* +================== +FilterBrush +================== +*/ + +// more funtionality added. (previous Z/NZ check is still fine) +// +// 0 = not filtered (ie visible/active) +// 1 = filtered (ie should be hidden/ignored) +// 2 = filtered, but filtered because of being a hidden group, but ghosting is on, so draw it grey +// (This value only returned if 'bCalledFromDisplayCode' == true, for ghost-drawing +// +// Note: Do NOT return anything other than the numbers above now, ie don't just return the results of a strcmp directly, +// since that'll also return -1 at times. +// +/*BOOL*/ int FilterBrush(brush_t *pb, BOOL bCalledFromDisplayCode /* = false */) +{ + if (!pb->owner) + return FALSE; // during construction + + if (g_pParentWnd->GetZWnd()->m_pZClip) // ZClip class up and running? (and hence Z window built) + { + if (g_pParentWnd->GetZWnd()->m_pZClip->IsEnabled()) + { + // ZClipping active... + // + if (pb->mins[2] > g_pParentWnd->GetZWnd()->m_pZClip->GetTop() // brush bottom edge is above clip top + || + pb->maxs[2] < g_pParentWnd->GetZWnd()->m_pZClip->GetBottom()// brush top edge is below clip bottom + ) + { + return TRUE; + } + } + } + + if (pb->owner) + { + int iReturn = Grouping_EntIsHidden(pb->owner, bCalledFromDisplayCode); + if (iReturn) + return iReturn; // but only if NZ + } + + if (pb->hiddenBrush) + { + return TRUE; + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_ALL_EXCEPT_DETAIL) + { + if (pb->brush_faces->texdef.contents & CONTENTS_DETAIL) + return false; + return true; // ignore everything else when using this flag + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_HINT) + { + if (strstr(pb->brush_faces->texdef.name, "hint")) + return TRUE; + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_CLIP) + { + if (strstr(pb->brush_faces->texdef.name, "clip")) + return TRUE; + + if (strstr(pb->brush_faces->texdef.name, "skip")) + return TRUE; + + // I could just check for "block" for these two, but you never know... + // + if (strstr(pb->brush_faces->texdef.name, "blocknpc")) + return TRUE; + + if (strstr(pb->brush_faces->texdef.name, "blockplayer")) + return TRUE; + + // and some more... + // + if (strstr(pb->brush_faces->texdef.name, "do_not_enter")) + return TRUE; + + //if (!strncmp(pb->brush_faces->texdef.name, "clip", 4)) + // return TRUE; + } + + + if (g_qeglobals.d_savedinfo.exclude & SHOW_CURVES_ONLY) // not actually an exclude + { + if (!(pb->curveBrush || pb->patchBrush)) + return TRUE; + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_WATER) + { + for (face_t* f = pb->brush_faces ; f ; f=f->next) + { + if (f->texdef.contents & (CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA)) + return TRUE; + } + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_DETAIL) + { + if (pb->brush_faces->texdef.contents & CONTENTS_DETAIL) + return TRUE; + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_NONSOLID) + { + if (pb->brush_faces->texdef.flags & SURF_NONSOLID) + return TRUE; + } + + + if (g_qeglobals.d_savedinfo.exclude & (EXCLUDE_EASY|EXCLUDE_MEDIUM|EXCLUDE_HARD)) + { + char *p = ValueForKey (pb->owner, "spawnflags"); + if (p && p[0]) + { + // this logic is pretty shitty, but it lets you move flags around to different defines etc + // and still works ok. This double-negative system of flags is a bit weird as well, but that's what + // the engine uses... + // + int iSpawnFlags = atoi(p); + + int iFlags = g_qeglobals.d_savedinfo.exclude & (EXCLUDE_EASY|EXCLUDE_MEDIUM|EXCLUDE_HARD); + iFlags^= (EXCLUDE_EASY|EXCLUDE_MEDIUM|EXCLUDE_HARD); + + iSpawnFlags&= ( (1<<(NOT_FLAGS_START+0)) | (1<<(NOT_FLAGS_START+1)) | (1<<(NOT_FLAGS_START+2)) ); + iSpawnFlags^= ( (1<<(NOT_FLAGS_START+0)) | (1<<(NOT_FLAGS_START+1)) | (1<<(NOT_FLAGS_START+2)) ); + + if (! + ( + ((iFlags&EXCLUDE_EASY) && (iSpawnFlags&(1<<(NOT_FLAGS_START+0)))) || + ((iFlags&EXCLUDE_MEDIUM)&& (iSpawnFlags&(1<<(NOT_FLAGS_START+1)))) || + ((iFlags&EXCLUDE_HARD) && (iSpawnFlags&(1<<(NOT_FLAGS_START+2)))) + ) + ) + { + return TRUE; // not drawn + } + } + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_CURVES) + { + if (pb->curveBrush || pb->patchBrush) + return TRUE; + } + + if (pb->owner == world_entity) + { + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_WORLD) + return TRUE; + return FALSE; + } + else + { + if (!(pb->owner && !stricmp(pb->owner->eclass->name, "func_group"))) // don't exclude func_groups if excluding entities + { + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_ENT) + return TRUE; + } + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_FUNC_GROUP) + { + if (pb->owner && !stricmp(pb->owner->eclass->name, "func_group")) + return TRUE; + } + +#ifdef QUAKE3 + if (g_qeglobals.d_savedinfo.exclude & SHOW_WAYPOINTS_ONLY) // not actually an exclude + { + if (!strncmp(pb->owner->eclass->name, "waypoint", 8)) + return FALSE; // we're a waypoint, so always show us + return TRUE; + } +#endif + + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_LIGHTS) + { + if (!strncmp(pb->owner->eclass->name, "light", 5)) + return TRUE; + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_PATHS) + { + if (!strncmp(pb->owner->eclass->name, "path", 4)) + return TRUE; + } + +#ifdef QUAKE3 + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_WAYPOINTS) + { + if (!strncmp(pb->owner->eclass->name, "waypoint", 8) || + !strncmp(pb->owner->eclass->name, "point_combat", 12) + ) + { + return TRUE; + } + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_MISCMODELS) + { + if (!strcmp(pb->owner->eclass->name, "misc_model")) // NOT strncmp(), or it'll affect misc_model_breakable as well + return TRUE; + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_MISCMODELBREAKABLES) + { + if (!strcmp(pb->owner->eclass->name, "misc_model_breakable")) + return TRUE; + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_MISCMODELXXXX) + { + // special case, to avoid confusion with above I'll ignore "misc_model_breakable" in this check + // + if (strcmp(pb->owner->eclass->name, "misc_model_breakable")) + { + if (!strncmp(pb->owner->eclass->name, "misc_model_",11)) + return TRUE; + } + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_TRIGGERXXXX) + { + if (!strncmp(pb->owner->eclass->name, "trigger_",8)) + return TRUE; + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_TARGETSPEAKERS) + { + if (!strncmp(pb->owner->eclass->name, "target_speaker", 14)) + return TRUE; + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_REFTAGS) + { + if (!strncmp(pb->owner->eclass->name, "ref_tag", 7)) + return TRUE; + } + + // special code for hidden waypoints (hidden as in the special hide for Mike G)... + // + if (!strncmp(pb->owner->eclass->name, "waypoint", 8)) + { + char *p = ValueForKey (pb->owner, sKEY_HIDDENWAYPOINT); + if (p && p[0]) + return TRUE; + } + +#endif + + return FALSE; +} + +/* +============================================================= + + PATH LINES + +============================================================= +*/ + +/* +================== +DrawPathLines + +Draws connections between entities. +Needs to consider all entities, not just ones on screen, +because the lines can be visible when neither end is. +Called for both camera view and xy view. +================== +*/ +void DrawPathLines (void) +{ + int i, j, k; + vec3_t mid, mid1; + entity_t *se, *te; + brush_t *sb, *tb; + char *psz; + vec3_t dir, s1, s2; + vec_t len, f; + int arrows; + int num_entities; + char *ent_target[MAX_MAP_ENTITIES]; + char *ent_target2[MAX_MAP_ENTITIES]; + char *ent_target3[MAX_MAP_ENTITIES]; + char *ent_target4[MAX_MAP_ENTITIES]; + char *ent_target5[MAX_MAP_ENTITIES]; + char *ent_target6[MAX_MAP_ENTITIES]; + char *ent_target7[MAX_MAP_ENTITIES]; + char *ent_target8[MAX_MAP_ENTITIES]; + entity_t *ent_entity[MAX_MAP_ENTITIES]; + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_PATHS) + return; +#ifdef QUAKE3 + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_ENT) + return; +#endif + + num_entities = 0; + for (te = entities.next ; te != &entities && num_entities != MAX_MAP_ENTITIES ; te = te->next) + { + ent_target [num_entities] = ValueForKey (te, "target"); + ent_target2[num_entities] = ValueForKey (te, "target2"); + ent_target3[num_entities] = ValueForKey (te, "target3"); + ent_target4[num_entities] = ValueForKey (te, "target4"); + ent_target5[num_entities] = ValueForKey (te, "NPC_target"); + ent_target6[num_entities] = ValueForKey (te, "paintarget"); + ent_target7[num_entities] = ValueForKey (te, "opentarget"); + ent_target8[num_entities] = ValueForKey (te, "closetarget"); + + if (ent_target [num_entities][0] || + ent_target2[num_entities][0] || + ent_target3[num_entities][0] || + ent_target4[num_entities][0] || + ent_target5[num_entities][0] || + ent_target6[num_entities][0] || + ent_target7[num_entities][0] || + ent_target8[num_entities][0] + ) + { + ent_entity[num_entities] = te; + num_entities++; + } + } + + for (se = entities.next ; se != &entities ; se = se->next) + { + psz = ValueForKey(se, "targetname"); + + if (psz == NULL || psz[0] == '\0') + continue; + + sb = se->brushes.onext; + if (sb == &se->brushes) + continue; + +#ifdef QUAKE3 + // don't draw lines to anyone if we're a hidden child waypoint + char *p = ValueForKey (se, sKEY_HIDDENWAYPOINT); + if (p && p[0]) + continue; + + // don't draw lines to us if we're a waypoint and waypoints are disabled... + // + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_WAYPOINTS) + { + if (!strncmp(se->eclass->name, "waypoint", 8)) + continue; + } + + // don't draw lines to us if we're on waypoints-only and we're not a waypoint... + // + if (g_qeglobals.d_savedinfo.exclude & SHOW_WAYPOINTS_ONLY) // not actually an exclude + { + if (strncmp(se->eclass->name, "waypoint", 8)) + continue; + } + + // don't draw lines if we're drawing to a trigger and they're disabled... + // + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_TRIGGERXXXX) + { + if (!strncmp(se->eclass->name, "trigger_",8)) + continue; + } + + if (FilterBrush(sb)) + continue; +#endif + + for (k=0 ; k + // + int iArrowHeads; + if (!strcmp (ent_target[k], psz)) + { + iArrowHeads = 1; + } + else + if (!strcmp (ent_target2[k], psz)) + { + iArrowHeads = 2; + } + else + if (!strcmp (ent_target3[k], psz)) + { + iArrowHeads = 3; + } + else + if (!strcmp (ent_target4[k], psz)) + { + iArrowHeads = 4; + } + else + if (!strcmp (ent_target5[k], psz) || + !strcmp (ent_target6[k], psz) || + !strcmp (ent_target7[k], psz) || + !strcmp (ent_target8[k], psz) + ) + { + iArrowHeads = 1; + } + else + continue; + + te = ent_entity[k]; + tb = te->brushes.onext; + if (tb == &te->brushes) + continue; +#ifdef QUAKE3 + // don't draw lines leading to hidden child-waypoints + char *p = ValueForKey (te, sKEY_HIDDENWAYPOINT); + if (p && p[0]) + continue; + + // don't draw lines if source (actually called 'te' here for target entity since lines are drawn backwards ... duh...) was a trigger and triggers are disabled... + // + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_TRIGGERXXXX) + { + if (!strncmp(te->eclass->name, "trigger_",8)) + continue; + } + + if (FilterBrush(tb)) + continue; +#endif + + for (i=0 ; i<3 ; i++) + mid[i] = (sb->mins[i] + sb->maxs[i])*0.5; + + for (i=0 ; i<3 ; i++) + mid1[i] = (tb->mins[i] + tb->maxs[i])*0.5; + + VectorSubtract (mid1, mid, dir); + len = VectorNormalize (dir); + s1[0] = -dir[1]*8 + dir[0]*8; + s2[0] = dir[1]*8 + dir[0]*8; + s1[1] = dir[0]*8 + dir[1]*8; + s2[1] = -dir[0]*8 + dir[1]*8; + + qglColor3f (se->eclass->color[0], se->eclass->color[1], se->eclass->color[2]); + + qglBegin(GL_LINES); + qglVertex3fv(mid); + qglVertex3fv(mid1); + + arrows = (int)(len / 256) + 1; + + for (i=0 ; inext) + { + if ( + ( + brush->mins[nDim1] > maxs[0] || + brush->mins[nDim2] > maxs[1] || + brush->maxs[nDim1] < mins[0] || + brush->maxs[nDim2] < mins[1] + ) + && + + // if about to cull, only let cull happen if we're not drawing lights+radii and this isn't a light + !( + !(g_qeglobals.d_savedinfo.exclude & EXCLUDE_LIGHTS)&& + !(g_qeglobals.d_savedinfo.exclude & EXCLUDE_LIGHTS_RADII)&& + brush->owner && !strcmpi(brush->owner->eclass->name, "light") + ) + + ) + { + culled++; + continue; // off screen + } + + int iFiltered = FilterBrush(brush, true); + if (iFiltered==1) // but allow 0 or 2 through + continue; + + drawn++; + + if (brush->owner != e && brush->owner) + { + // some designers have complained that they can't see the func_group colours (hardwired to black in QUAKED comments), so... + // + if (!strcmp(brush->owner->eclass->name,"func_group") && + (g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][0] + + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][1] + + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][2] + <0.6 + ) + ) + { + qglColor3f( 1.0f - g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][0], + 1.0f - g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][1], + 1.0f - g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][2] + ); + } + else + { + qglColor3fv(brush->owner->eclass->color); + } + } + else + { + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES]); + } + + if (iFiltered == 2) + { + qglColor4fv(v4GhostColor); + + qglLineStipple( 8, 0xAAAA); + qglEnable(GL_LINE_STIPPLE); + qglLineWidth(2); + } + + + Brush_DrawXY(brush, m_nViewType, (iFiltered == 2)); // bIsGhost? + + if (iFiltered == 2) // doesn't need to be checked, but saves unnecessary GL state changes + { + qglDisable(GL_LINE_STIPPLE); + qglLineWidth(1); + } + + } + + + if (m_bTiming) + end2 = Sys_DoubleTime(); + + + DrawPathLines (); + + // + // draw pointfile + // + if ( g_qeglobals.d_pointfile_display_list) + qglCallList (g_qeglobals.d_pointfile_display_list); + + + if (!(m_nViewType == XY)) + qglPopMatrix(); + + // + // draw block grid + // + if ( g_qeglobals.show_blocks) + XY_DrawBlockGrid (); + + // + // now draw selected brushes + // + if (m_nViewType != XY) + { + qglPushMatrix(); + if (m_nViewType == YZ) + qglRotatef (-90, 0, 1, 0); // put Z going up + //else + qglRotatef (-90, 1, 0, 0); // put Z going up + } + + + qglPushMatrix(); + qglTranslatef( g_qeglobals.d_select_translate[0], g_qeglobals.d_select_translate[1], g_qeglobals.d_select_translate[2]); + + if (RotateMode()) + qglColor3f(0.8, 0.1, 0.9); + else + if (ScaleMode()) + qglColor3f(0.1, 0.8, 0.1); + else + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES]); + + //qglEnable (GL_LINE_STIPPLE); + //qglLineStipple (3, 0xaaaa); + qglLineWidth (2); + + vec3_t vMinBounds; + vec3_t vMaxBounds; + vMinBounds[0] = vMinBounds[1] = vMinBounds[2] = MAX_WORLD_COORD; + vMaxBounds[0] = vMaxBounds[1] = vMaxBounds[2] = MIN_WORLD_COORD; + + int nSaveDrawn = drawn; + bool bFixedSize = false; + for (brush = selected_brushes.next ; brush != &selected_brushes ; brush=brush->next) + { + drawn++; + Brush_DrawXY(brush, m_nViewType); + + if (!bFixedSize) + { + if (brush->owner->eclass->fixedsize) + bFixedSize = true; + if (g_PrefsDlg.m_bSizePaint) + { + for (i = 0; i < 3; i ++) + { + if (brush->mins[i] < vMinBounds[i]) + vMinBounds[i] = brush->mins[i]; + if (brush->maxs[i] > vMaxBounds[i]) + vMaxBounds[i] = brush->maxs[i]; + } + } + } + } + + qglDisable (GL_LINE_STIPPLE); + qglLineWidth (1); + + if (!bFixedSize && !RotateMode() && !ScaleMode() && drawn - nSaveDrawn > 0 && g_PrefsDlg.m_bSizePaint) + PaintSizeInfo(nDim1, nDim2, vMinBounds, vMaxBounds); + + // edge / vertex flags + + if (g_qeglobals.d_select_mode == sel_vertex) + { + qglPointSize (4); + qglColor3f (0,1,0); + qglBegin (GL_POINTS); + for (i=0 ; iGetCamera()->Camera().origin, lastcamera); + + qglReadBuffer (GL_FRONT); + qglDrawBuffer (GL_BACK); + + qglRasterPos2f (lastz[0]-9, lastz[1]-9); + qglGetIntegerv (GL_CURRENT_RASTER_POSITION,r); + qglCopyPixels(r[0], r[1], 18,18, GL_COLOR); + + qglRasterPos2f (lastcamera[0]-50, lastcamera[1]-50); + qglGetIntegerv (GL_CURRENT_RASTER_POSITION,r); + qglCopyPixels(r[0], r[1], 100,100, GL_COLOR); + + // + // draw the new icons + // + qglDrawBuffer (GL_FRONT); + + qglShadeModel (GL_FLAT); + qglDisable(GL_TEXTURE_2D); + qglDisable(GL_TEXTURE_1D); + qglDisable(GL_DEPTH_TEST); + qglDisable(GL_BLEND); + qglColor3f(0, 0, 0); + + DrawCameraIcon (); + DrawZIcon (); + + qglDrawBuffer (GL_BACK); + qglFinish(); +} + + +vec3_t& CXYWnd::GetOrigin() +{ + return m_vOrigin; +} + +void CXYWnd::SetOrigin(vec3_t org) +{ + m_vOrigin[0] = org[0]; + m_vOrigin[1] = org[1]; + m_vOrigin[2] = org[2]; +} + +void CXYWnd::OnSize(UINT nType, int cx, int cy) +{ + CWnd::OnSize(nType, cx, cy); + CRect rect; + GetClientRect(rect); + m_nWidth = rect.Width(); + m_nHeight = rect.Height(); +} + +brush_t hold_brushes; +void CXYWnd::Clip() +{ + if (ClipMode()) + { + hold_brushes.next = &hold_brushes; + ProduceSplitLists(); + //brush_t* pList = (g_bSwitch) ? &g_brFrontSplits : &g_brBackSplits; + brush_t* pList; + if (g_PrefsDlg.m_bSwitchClip) + pList = ( (m_nViewType == XZ) ? g_bSwitch: !g_bSwitch) ? &g_brFrontSplits : &g_brBackSplits; + else + pList = ( (m_nViewType == XZ) ? !g_bSwitch: g_bSwitch) ? &g_brFrontSplits : &g_brBackSplits; + + + if (pList->next != pList) + { + Brush_CopyList(pList, &hold_brushes); + CleanList(&g_brFrontSplits); + CleanList(&g_brBackSplits); + Select_Delete(); + Brush_CopyList(&hold_brushes, &selected_brushes); + if (RogueClipMode()) + RetainClipMode(false); + else + RetainClipMode(true); + Sys_UpdateWindows(W_ALL); + } + } + else if (PathMode()) + { + FinishSmartCreation(); + if (g_pPathFunc) + g_pPathFunc(true, g_nPathCount); + g_pPathFunc = NULL; + g_nPathCount = 0; + g_bPathMode = false; + } +} + +static vector BrushesToSelect; +static void SelectList_Clear() +{ + BrushesToSelect.clear(); +} +static void SelectList_AddList(brush_t* pList) +{ + brush_t* pBrush = pList->next; + while (pBrush != NULL && pBrush != pList) + { + BrushesToSelect.push_back(pBrush); + pBrush = pBrush->next; + } +} +static void SelectList_Select() +{ + for (int i=0; inext != b) + { + m_vOrigin[nDim1] = b->mins[nDim1]; + m_vOrigin[nDim2] = b->mins[nDim2]; + } + else + { + m_vOrigin[nDim1] = g_pParentWnd->GetCamera()->Camera().origin[nDim1]; + m_vOrigin[nDim2] = g_pParentWnd->GetCamera()->Camera().origin[nDim2]; + } +} + +void CXYWnd::VectorCopyXY(vec3_t in, vec3_t out) +{ + if (m_nViewType == XY) + { + out[0] = in[0]; + out[1] = in[1]; + } + else + if (m_nViewType == XZ) + { + out[0] = in[0]; + out[2] = in[2]; + } + else + { + out[1] = in[1]; + out[2] = in[2]; + } +} + + +void CXYWnd::OnDestroy() +{ + QEW_StopGL( GetSafeHwnd(), s_hglrcXY, s_hdcXY ); + CWnd::OnDestroy(); + // delete this; +} + +void CXYWnd::SetViewType(int n) +{ + m_nViewType = n; + if (g_pParentWnd->CurrentStyle() == QR_QE4) + { + CString str = "YZ Side"; + if (m_nViewType == XY) + str = "XY Top"; + else if (m_nViewType == XZ) + str = "XZ Front"; + SetWindowText(str); + } +}; + +void CXYWnd::Redraw(unsigned int nBits) +{ + m_nUpdateBits = nBits; + RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); + m_nUpdateBits = W_XY; +} + +bool CXYWnd::RotateMode() +{ + return g_bRotateMode; +} + +bool CXYWnd::ScaleMode() +{ + return g_bScaleMode; +} + + + +//extern void CleanAbsoluteCoords(); +void CXYWnd::SetRotateMode(bool bMode) +{ + if (bMode && selected_brushes.next != &selected_brushes) + { + g_bRotateMode = true; + Select_GetTrueMid(g_vRotateOrigin); + g_vRotation[0] = g_vRotation[1] = g_vRotation[2] = 0.0; + //CleanAbsoluteCoords(); + } + else + { + g_bRotateMode = false; + } + RedrawWindow(); +} + +void CXYWnd::SetScaleMode(bool bMode) +{ + g_bScaleMode = bMode; + RedrawWindow(); +} + + + + +// xy - z +// xz - y +// yz - x + +void CXYWnd::OnSelectMouserotate() +{ + // TODO: Add your command handler code here + +} + +void CleanCopyEntities() +{ + entity_t* pe = g_enClipboard.next; + while (pe != NULL && pe != &g_enClipboard) + { + entity_t* next = pe->next; + epair_t* enext = NULL; + for (epair_t* ep = pe->epairs ; ep ; ep=enext) + { + enext = ep->next; + free (ep->key); + free (ep->value); + free (ep); + } + free (pe); + pe = next; + } + g_enClipboard.next = g_enClipboard.prev = &g_enClipboard; +} + +entity_t *Entity_CopyClone (entity_t *e) +{ + entity_t *n; + epair_t *ep, *np; + + n = (entity_t*)qmalloc(sizeof(*n)); + n->brushes.onext = n->brushes.oprev = &n->brushes; + n->eclass = e->eclass; + + // add the entity to the entity list + n->next = g_enClipboard.next; + g_enClipboard.next = n; + n->next->prev = n; + n->prev = &g_enClipboard; + + for (ep = e->epairs ; ep ; ep=ep->next) + { + np = (epair_t*)qmalloc(sizeof(*np)); + np->key = copystring(ep->key); + np->value = copystring(ep->value); + np->next = n->epairs; + n->epairs = np; + } + return n; +} + +bool OnList(entity_t* pFind, CPtrArray* pList) +{ + int nSize = pList->GetSize(); + while (nSize-- > 0) + { + entity_t* pEntity = reinterpret_cast(pList->GetAt(nSize)); + if (pEntity == pFind) + return true; + } + return false; +} + +void CXYWnd::Copy() +{ +#if 1 + CWaitCursor wait; + g_Clipboard.SetLength(0); + g_PatchClipboard.SetLength(0); + Map_SaveSelected(&g_Clipboard, &g_PatchClipboard); + + // send it to the real clipboard... + // + if (g_Clipboard.GetLength() > 0) + { + g_Clipboard.SeekToBegin(); + int nLen = g_Clipboard.GetLength(); + char* pBuffer = new char[nLen+1]; + g_Clipboard.Read(pBuffer, nLen); + pBuffer[nLen] = '\0'; + SendStringToClipboard(pBuffer); + delete []pBuffer; + } + + //CString strOut; + //::GetTempPath(1024, strOut.GetBuffer(1024)); + //strOut.ReleaseBuffer(); + //AddSlash(strOut); + //strOut += "RadiantClipboard.$$$"; + //Map_SaveSelected(strOut.GetBuffer(0)); +#else + CPtrArray holdArray; + CleanList(&g_brClipboard); + CleanCopyEntities(); + for (brush_t* pBrush = selected_brushes.next ; pBrush != NULL && pBrush != &selected_brushes ; pBrush=pBrush->next) + { + if (pBrush->owner == world_entity) + { + brush_t* pClone = Brush_Clone(pBrush); + pClone->owner = NULL; + Brush_AddToList (pClone, &g_brClipboard); + } + else + { + if (!OnList(pBrush->owner, &holdArray)) + { + entity_t* e = pBrush->owner; + holdArray.Add(reinterpret_cast(e)); + entity_t* pEClone = Entity_CopyClone(e); + for (brush_t* pEB = e->brushes.onext ; pEB != &e->brushes ; pEB=pEB->onext) + { + brush_t* pClone = Brush_Clone(pEB); + //Brush_AddToList (pClone, &g_brClipboard); + Entity_LinkBrush(pEClone, pClone); + Brush_Build(pClone); + } + } + } + } +#endif +} + +void CXYWnd::Undo() +{ + if (g_brUndo.next != &g_brUndo) + { + g_bScreenUpdates = false; + Select_Delete(); + for (brush_t* pBrush = g_brUndo.next ; pBrush != NULL && pBrush != &g_brUndo ; pBrush=pBrush->next) + { + brush_t* pClone = Brush_Clone(pBrush); + Brush_Build(pClone); + Brush_AddToList (pClone, &active_brushes); + Entity_LinkBrush (world_entity, pClone); + Select_Brush(pClone); + } + CleanList(&g_brUndo); + g_bScreenUpdates = true; + Sys_UpdateWindows(W_ALL); + } + else Sys_Printf("Nothing to paste.../n"); +} + +void CXYWnd::UndoCopy() +{ + CleanList(&g_brUndo); + for (brush_t* pBrush = selected_brushes.next ; pBrush != NULL && pBrush != &selected_brushes ; pBrush=pBrush->next) + { + if (pBrush->owner == world_entity) + { + brush_t* pClone = Brush_Clone(pBrush); + Brush_AddToList (pClone, &g_brUndo); + } + } +} + +bool CXYWnd::UndoAvailable() +{ + return (g_brUndo.next != &g_brUndo); +} + + + +void CXYWnd::Paste() +{ +#if 1 +/* // -slc + if (g_Clipboard.GetLength() > 0) + { + + g_Clipboard.SeekToBegin(); + int nLen = g_Clipboard.GetLength(); + char* pBuffer = new char[nLen+1]; + g_Clipboard.Read(pBuffer, nLen); + pBuffer[nLen] = '\0'; + Map_ImportBuffer(pBuffer); + delete []pBuffer; + } +*/ + + CWaitCursor wait; + CString strClipBoard; + if (GetStringFromClipboard(strClipBoard)) + { + if (strClipBoard.GetLength() > 0) + { + Map_ImportBuffer((char *)((LPCSTR) strClipBoard)); + } + } + + +#if 0 + if (g_PatchClipboard.GetLength() > 0) + { + g_PatchClipboard.SeekToBegin(); + int nLen = g_PatchClipboard.GetLength(); + char* pBuffer = new char[nLen+1]; + g_PatchClipboard.Read(pBuffer, nLen); + pBuffer[nLen] = '\0'; + Patch_ReadBuffer(pBuffer, true); + delete []pBuffer; + } +#endif + + //CString strOut; + //::GetTempPath(1024, strOut.GetBuffer(1024)); + //strOut.ReleaseBuffer(); + //AddSlash(strOut); + //strOut += "RadiantClipboard.$$$"; + //Map_ImportFile(strOut.GetBuffer(0)); +#else + if (g_brClipboard.next != &g_brClipboard || g_enClipboard.next != &g_enClipboard) + { + Select_Deselect(); + + for (brush_t* pBrush = g_brClipboard.next ; pBrush != NULL && pBrush != &g_brClipboard ; pBrush=pBrush->next) + { + brush_t* pClone = Brush_Clone(pBrush); + //pClone->owner = pBrush->owner; + if (pClone->owner == NULL) + Entity_LinkBrush (world_entity, pClone); + + Brush_AddToList (pClone, &selected_brushes); + Brush_Build(pClone); + } + + for (entity_t* pEntity = g_enClipboard.next; pEntity != NULL && pEntity != &g_enClipboard; pEntity = pEntity->next) + { + entity_t* pEClone = Entity_Clone(pEntity); + for (brush_t* pEB = pEntity->brushes.onext ; pEB != &pEntity->brushes ; pEB=pEB->onext) + { + brush_t* pClone = Brush_Clone(pEB); + Brush_AddToList (pClone, &selected_brushes); + Entity_LinkBrush(pEClone, pClone); + Brush_Build(pClone); + } + } + + Sys_UpdateWindows(W_ALL); + } + else Sys_Printf("Nothing to paste.../n"); +#endif +} + + +vec3_t& CXYWnd::Rotation() +{ + return g_vRotation; +} + +vec3_t& CXYWnd::RotateOrigin() +{ + return g_vRotateOrigin; +} + + +void CXYWnd::OnTimer(UINT nIDEvent) +{ + int nDim1 = (m_nViewType == YZ) ? 1 : 0; + int nDim2 = (m_nViewType == XY) ? 1 : 2; + m_vOrigin[nDim1] += m_ptDragAdj.x / m_fScale; + m_vOrigin[nDim2] -= m_ptDragAdj.y / m_fScale; + Sys_UpdateWindows(W_XY | W_CAMERA); + //int nH = (m_ptDrag.y == 0) ? -1 : m_ptDrag.y; + m_ptDrag += m_ptDragAdj; + m_ptDragTotal += m_ptDragAdj; + XY_MouseMoved (m_ptDrag.x, m_nHeight - 1 - m_ptDrag.y , m_nScrollFlags); + //m_vOrigin[nDim1] -= m_ptDrag.x / m_fScale; + //m_vOrigin[nDim1] -= m_ptDrag.x / m_fScale; +} + +void CXYWnd::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + g_pParentWnd->HandleKey(nChar, nRepCnt, nFlags, false); +} + +void CXYWnd::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp) +{ + CWnd::OnNcCalcSize(bCalcValidRects, lpncsp); +} + +void CXYWnd::OnKillFocus(CWnd* pNewWnd) +{ + CWnd::OnKillFocus(pNewWnd); + SendMessage(WM_NCACTIVATE, FALSE , 0 ); +} + +void CXYWnd::OnSetFocus(CWnd* pOldWnd) +{ + CWnd::OnSetFocus(pOldWnd); + SendMessage(WM_NCACTIVATE, TRUE , 0 ); +} + +void CXYWnd::OnClose() +{ + CWnd::OnClose(); +} + +// should be static as should be the rotate scale stuff +bool CXYWnd::AreaSelectOK() +{ + return RotateMode() ? false : ScaleMode() ? false : true; +} \ No newline at end of file diff --git a/utils/Radiant/xywnd.h b/utils/Radiant/xywnd.h new file mode 100644 index 0000000..bcb689a --- /dev/null +++ b/utils/Radiant/xywnd.h @@ -0,0 +1,210 @@ +#if !defined(AFX_XYWND_H__44B4BA04_781B_11D1_B53C_00AA00A410FC__INCLUDED_) +#define AFX_XYWND_H__44B4BA04_781B_11D1_B53C_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// XYWnd.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CXYWnd window + +#include "qe3.h" +#include "CamWnd.h" +#include "3DFXCamWnd.h" + +const int SCALE_X = 0x01; +const int SCALE_Y = 0x02; +const int SCALE_Z = 0x04; + + +typedef void (PFNPathCallback)(bool, int); +// as i didn't really encapsulate anything this +// should really be a struct.. +class CClipPoint +{ +public: + CClipPoint(){ Reset(); }; + void Reset(){ m_ptClip[0] = m_ptClip[1] = m_ptClip[2] = 0.0; m_bSet = false; m_pVec3 = NULL;}; + bool Set(){ return m_bSet; }; + void Set(bool b) { m_bSet = b; }; + void UpdatePointPtr() { if (m_pVec3) VectorCopy(m_ptClip, *m_pVec3); }; + void SetPointPtr(vec3_t* p) { m_pVec3 = p; }; + vec3_t m_ptClip; // the 3d point + vec3_t* m_pVec3; // optional ptr for 3rd party updates + CPoint m_ptScreen; // the onscreen xy point (for mousability) + bool m_bSet; + operator vec3_t&() {return m_ptClip;}; + operator vec3_t*() {return &m_ptClip;}; +}; + +class CXYWnd : public CWnd +{ + DECLARE_DYNCREATE(CXYWnd); +// Construction +public: + CXYWnd(); + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CXYWnd) + protected: + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + //}}AFX_VIRTUAL + +// Implementation +public: + bool AreaSelectOK(); + vec3_t& RotateOrigin(); + vec3_t& Rotation(); + bool UndoAvailable(); + void KillPathMode(); + void Undo(); + void UndoCopy(); + void Copy(); + void Paste(); + BOOL GetStringFromClipboard(CString& String); + BOOL SendStringToClipboard(LPCSTR psString); + void Redraw(unsigned int nBits); + void VectorCopyXY(vec3_t in, vec3_t out); + void PositionView(); + void FlipClip(); + void SplitClip(); + void Clip(); + vec3_t& GetOrigin(); + void SetOrigin(vec3_t org); // PGM + void XY_Init(); + void XY_Overlay(); + void XY_Draw(); + void DrawZIcon(); + void DrawRotateIcon(); + void DrawCameraIcon(); + void XY_DrawBlockGrid(); + void XY_DrawGrid(); + void XY_MouseMoved (int x, int y, int buttons); + void NewBrushDrag (int x, int y); + qboolean DragDelta (int x, int y, vec3_t move); + void XY_MouseUp(int x, int y, int buttons); + void XY_MouseDown (int x, int y, int buttons); + void XY_ToGridPoint (int x, int y, vec3_t point); + void XY_ToPoint (int x, int y, vec3_t point); + void SnapToPoint (int x, int y, vec3_t point); + void SetActive(bool b) {m_bActive = b;}; + bool Active() {return m_bActive;}; + void DropClipPoint(UINT nFlags, CPoint point); + + bool RogueClipMode(); + bool ClipMode(); + void SetClipMode(bool bMode); + void RetainClipMode(bool bMode); + + bool RotateMode(); + void SetRotateMode(bool bMode); + bool ScaleMode(); + void SetScaleMode(bool bMode); + + bool PathMode(); + void DropPathPoint(UINT nFlags, CPoint point); + + bool PointMode(); + void AddPointPoint(UINT nFlags, vec3_t* pVec); + void SetPointMode(bool b); + + + virtual ~CXYWnd(); + void SetViewType(int n); + int GetViewType() {return m_nViewType; }; + void SetScale(float f) {m_fScale = f;}; + float Scale() {return m_fScale;}; + int Width() {return m_nWidth;} + int Height() {return m_nHeight;} + bool m_bActive; + + // Generated message map functions +protected: + int m_nUpdateBits; + int m_nWidth; + int m_nHeight; + bool m_bTiming; + float m_fScale; + float m_TopClip; + float m_BottomClip; + bool m_bDirty; + vec3_t m_vOrigin; + CPoint m_ptCursor; + bool m_bRButtonDown; + + int m_nButtonstate; + int m_nPressx; + int m_nPressy; + vec3_t m_vPressdelta; + bool m_bPress_selection; + + friend CCamWnd; + friend C3DFXCamWnd; + + CMenu m_mnuDrop; + int m_nViewType; + + unsigned int m_nTimerID; + int m_nScrollFlags; + CPoint m_ptDrag; + CPoint m_ptDragAdj; + CPoint m_ptDragTotal; + + void OriginalButtonUp(UINT nFlags, CPoint point); + void OriginalButtonDown(UINT nFlags, CPoint point); + void ProduceSplits(brush_t** pFront, brush_t** pBack); + void ProduceSplitLists(); + void HandleDrop(); + void PaintSizeInfo(int nDim1, int nDim2, vec3_t vMinBounds, vec3_t vMaxBounds); + + void OnEntityCreate(unsigned int nID); + + void OnScriptPopup_Notepad(unsigned int nID); + void OnScriptPopup_MSDev(unsigned int nID); + void OnScriptPopup_BehavEd(unsigned int nID); + void OnPopup_GroupNamesShowHide(unsigned int nID); + void OnPopup_GroupNamesSelect(unsigned int nID); + void OnPopup_GroupNamesAddSelectedTo(unsigned int nID); + void OnPopup_GroupNamesAddSelectedEClassTo(unsigned int nID); + void OnPopup_GroupNamesAddSelectedModelsTo(unsigned int nID); + + CPoint m_ptDown; + //{{AFX_MSG(CXYWnd) + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnMButtonDown(UINT nFlags, CPoint point); + afx_msg void OnRButtonDown(UINT nFlags, CPoint point); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg void OnMButtonUp(UINT nFlags, CPoint point); + afx_msg void OnRButtonUp(UINT nFlags, CPoint point); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + afx_msg void OnPaint(); + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnDestroy(); + afx_msg void OnSelectMouserotate(); + afx_msg void OnTimer(UINT nIDEvent); + afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp); + afx_msg void OnKillFocus(CWnd* pNewWnd); + afx_msg void OnSetFocus(CWnd* pOldWnd); + afx_msg void OnClose(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_XYWND_H__44B4BA04_781B_11D1_B53C_00AA00A410FC__INCLUDED_) diff --git a/utils/Radiant/z.cpp b/utils/Radiant/z.cpp new file mode 100644 index 0000000..86a5eb8 --- /dev/null +++ b/utils/Radiant/z.cpp @@ -0,0 +1,485 @@ + +#include "stdafx.h" +#include "qe3.h" + +#define PAGEFLIPS 2 + +z_t z; + +/* +============ +Z_Init +============ +*/ +void Z_Init (void) +{ + z.origin[0] = 0; + z.origin[1] = 20; + z.origin[2] = 46; + + z.scale = 1; +} + + + +/* +============================================================================ + + MOUSE ACTIONS + +============================================================================ +*/ + +static int cursorx, cursory; + +/* +============== +Z_MouseDown +============== +*/ +void Z_MouseDown (int x, int y, int buttons) +{ + vec3_t org, dir, vup, vright; + brush_t *b; + + Sys_GetCursorPos (&cursorx, &cursory); + + vup[0] = 0; vup[1] = 0; vup[2] = 1/z.scale; + + VectorCopy (z.origin, org); + org[2] += (y - (z.height/2))/z.scale; + org[1] = MIN_WORLD_COORD; + + b = selected_brushes.next; + if (b != &selected_brushes) + { + org[0] = (b->mins[0] + b->maxs[0])/2; + } + + dir[0] = 0; dir[1] = 1; dir[2] = 0; + + vright[0] = 0; vright[1] = 0; vright[2] = 0; + + // new mouse code for ZClip, I'll do this stuff before falling through into the standard ZWindow mouse code... + // + if (g_pParentWnd->GetZWnd()->m_pZClip) // should always be the case I think, but this is safer + { + bool bToggle = false; + bool bSetTop = false; + bool bSetBot = false; + bool bReset = false; + + if (g_PrefsDlg.m_nMouseButtons == 2) + { + // 2 button mice... + // + bToggle = (GetKeyState(VK_F1) & 0x8000); + bSetTop = (GetKeyState(VK_F2) & 0x8000); + bSetBot = (GetKeyState(VK_F3) & 0x8000); + bReset = (GetKeyState(VK_F4) & 0x8000); + } + else + { + // 3 button mice... + // + bToggle = (buttons == (MK_RBUTTON|MK_SHIFT|MK_CONTROL)); + bSetTop = (buttons == (MK_RBUTTON|MK_SHIFT)); + bSetBot = (buttons == (MK_RBUTTON|MK_CONTROL)); + bReset = (GetKeyState(VK_F4) & 0x8000); + } + + if (bToggle) + { + g_pParentWnd->GetZWnd()->m_pZClip->Enable(!(g_pParentWnd->GetZWnd()->m_pZClip->IsEnabled())); + Sys_UpdateWindows (W_ALL); + return; + } + + if (bSetTop) + { + g_pParentWnd->GetZWnd()->m_pZClip->SetTop(org[2]); + Sys_UpdateWindows (W_ALL); + return; + } + + if (bSetBot) + { + g_pParentWnd->GetZWnd()->m_pZClip->SetBottom(org[2]); + Sys_UpdateWindows (W_ALL); + return; + } + + if (bReset) + { + g_pParentWnd->GetZWnd()->m_pZClip->Reset(); + Sys_UpdateWindows (W_ALL); + return; + } + } + + // LBUTTON = manipulate selection + // shift-LBUTTON = select + // middle button = grab texture + // ctrl-middle button = set entire brush to texture + // ctrl-shift-middle button = set single face to texture + // + // see code above for these next 3, I just commented them here as well for clarity... + // + // ctrl-shift-RIGHT button = toggle ZClip on/off + // shift-RIGHT button = set ZClip top marker + // ctrl-RIGHT button = set ZClip bottom marker + + int nMouseButton = g_PrefsDlg.m_nMouseButtons == 2 ? MK_RBUTTON : MK_MBUTTON; + if ( (buttons == MK_LBUTTON) + || (buttons == (MK_LBUTTON | MK_SHIFT)) + || (buttons == MK_MBUTTON) +// || (buttons == (MK_MBUTTON|MK_CONTROL)) + || (buttons == (nMouseButton|MK_SHIFT|MK_CONTROL)) ) + { + Drag_Begin (x, y, buttons, + vright, vup, + org, dir); + return; + } + + // control mbutton = move camera + if ((buttons == (MK_CONTROL|nMouseButton) ) || (buttons == (MK_CONTROL|MK_LBUTTON))) + { + g_pParentWnd->GetCamera()->Camera().origin[2] = org[2] ; + Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY|W_Z); + } + + +} + +/* +============== +Z_MouseUp +============== +*/ +void Z_MouseUp (int x, int y, int buttons) +{ + Drag_MouseUp (); +} + +/* +============== +Z_MouseMoved +============== +*/ +void Z_MouseMoved (int x, int y, int buttons) +{ + if (!buttons) + return; + if (buttons == MK_LBUTTON) + { + Drag_MouseMoved (x, y, buttons); + Sys_UpdateWindows (W_Z|W_CAMERA_IFON|W_XY); + return; + } + // rbutton = drag z origin + if (buttons == MK_RBUTTON) + { + Sys_GetCursorPos (&x, &y); + if ( y != cursory) + { + z.origin[2] += y-cursory; + Sys_SetCursorPos (cursorx, cursory); + Sys_UpdateWindows (W_Z); + } + return; + } + // control mbutton = move camera + int nMouseButton = g_PrefsDlg.m_nMouseButtons == 2 ? MK_RBUTTON : MK_MBUTTON; + if ((buttons == (MK_CONTROL|nMouseButton) ) || (buttons == (MK_CONTROL|MK_LBUTTON))) + { + g_pParentWnd->GetCamera()->Camera().origin[2] = (y - (z.height/2))/z.scale; + Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY|W_Z); + } + +} + + +/* +============================================================================ + +DRAWING + +============================================================================ +*/ + + +/* +============== +Z_DrawGrid +============== +*/ +void Z_DrawGrid (void) +{ + float zz, zb, ze; + int w, h; + char text[32]; + + w = z.width/2 / z.scale; + h = z.height/2 / z.scale; + + zb = z.origin[2] - h; + if (zb < region_mins[2]) + zb = region_mins[2]; + zb = 64 * floor (zb/64); + + ze = z.origin[2] + h; + if (ze > region_maxs[2]) + ze = region_maxs[2]; + ze = 64 * ceil (ze/64); + + // draw major blocks + + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR]); + + qglBegin (GL_LINES); + + qglVertex2f (0, zb); + qglVertex2f (0, ze); + + for (zz=zb ; zz= 4 && + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR] != g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK]) + { + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR]); + + qglBegin (GL_LINES); + for (zz=zb ; zzGetCamera()->Camera().origin[2]; + + qglColor3f (0.0, 0.0, 1.0); + qglBegin(GL_LINE_STRIP); + qglVertex3f (x-xCam,y,0); + qglVertex3f (x,y+CAM_GIZMO,0); + qglVertex3f (x+xCam,y,0); + qglVertex3f (x,y-CAM_GIZMO,0); + qglVertex3f (x-xCam,y,0); + qglVertex3f (x+xCam,y,0); + qglVertex3f (x+xCam,y-CAM_HEIGHT,0); + qglVertex3f (x-xCam,y-CAM_HEIGHT,0); + qglVertex3f (x-xCam,y,0); + qglEnd (); + +} + +void ZDrawZClip() +{ + float x,y; + + x = 0; + y = g_pParentWnd->GetCamera()->Camera().origin[2]; + + if (g_pParentWnd->GetZWnd()->m_pZClip) // should always be the case I think + g_pParentWnd->GetZWnd()->m_pZClip->Paint(); +} + +GLbitfield glbitClear = GL_COLOR_BUFFER_BIT; //HACK + +/* +============== +Z_Draw +============== +*/ +void Z_Draw (void) +{ + brush_t *brush; + float w, h; + double start, end; + qtexture_t *q; + float top, bottom; + vec3_t org_top, org_bottom, dir_up, dir_down; + int xCam = z.width/3; + + if (!active_brushes.next) + return; // not valid yet + + if (z.timing) + start = Sys_DoubleTime (); + + // + // clear + // + qglViewport(0, 0, z.width, z.height); + + qglClearColor ( + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][0], + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][1], + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][2], + 0); + + /* GL Bug */ + /* When not using hw acceleration, gl will fault if we clear the depth + buffer bit on the first pass. The hack fix is to set the GL_DEPTH_BUFFER_BIT + only after Z_Draw() has been called once. Yeah, right. */ + qglClear(glbitClear); + glbitClear |= GL_DEPTH_BUFFER_BIT; + + qglMatrixMode(GL_PROJECTION); + + qglLoadIdentity (); + w = z.width/2 / z.scale; + h = z.height/2 / z.scale; + qglOrtho (-w, w, z.origin[2]-h, z.origin[2]+h, -8, 8); + + qglDisable(GL_TEXTURE_2D); + qglDisable(GL_TEXTURE_1D); + qglDisable(GL_DEPTH_TEST); + qglDisable(GL_BLEND); + + + // + // now draw the grid + // + Z_DrawGrid (); + + // + // draw stuff + // + + qglDisable(GL_CULL_FACE); + + qglShadeModel (GL_FLAT); + + qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + + qglDisable(GL_TEXTURE_2D); + qglDisable(GL_BLEND); + qglDisable(GL_DEPTH_TEST); + + + // draw filled interiors and edges + dir_up[0] = 0 ; dir_up[1] = 0; dir_up[2] = 1; + dir_down[0] = 0 ; dir_down[1] = 0; dir_down[2] = -1; + VectorCopy (z.origin, org_top); + org_top[2] = MAX_WORLD_COORD;//4096; // MAX_WORLD_COORD ? (John said this didn't work, Hmmmmmm) // !suspect! + VectorCopy (z.origin, org_bottom); + org_bottom[2] = MIN_WORLD_COORD;//-4096; // MIN_WORLD_COORD? " " !suspect! + + for (brush = active_brushes.next ; brush != &active_brushes ; brush=brush->next) + { + if (brush->mins[0] >= z.origin[0] + || brush->maxs[0] <= z.origin[0] + || brush->mins[1] >= z.origin[1] + || brush->maxs[1] <= z.origin[1]) + continue; + + if (!Brush_Ray (org_top, dir_down, brush, &top)) + continue; + top = org_top[2] - top; + if (!Brush_Ray (org_bottom, dir_up, brush, &bottom)) + continue; + bottom = org_bottom[2] + bottom; + + q = Texture_ForName (brush->brush_faces->texdef.name); + qglColor3f (q->color[0], q->color[1], q->color[2]); + qglBegin (GL_QUADS); + qglVertex2f (-xCam, bottom); + qglVertex2f (xCam, bottom); + qglVertex2f (xCam, top); + qglVertex2f (-xCam, top); + qglEnd (); + + qglColor3f (1,1,1); + qglBegin (GL_LINE_LOOP); + qglVertex2f (-xCam, bottom); + qglVertex2f (xCam, bottom); + qglVertex2f (xCam, top); + qglVertex2f (-xCam, top); + qglEnd (); + } + + // + // now draw selected brushes + // + for (brush = selected_brushes.next ; brush != &selected_brushes ; brush=brush->next) + { + if ( !(brush->mins[0] >= z.origin[0] + || brush->maxs[0] <= z.origin[0] + || brush->mins[1] >= z.origin[1] + || brush->maxs[1] <= z.origin[1]) ) + { + if (Brush_Ray (org_top, dir_down, brush, &top)) + { + top = org_top[2] - top; + if (Brush_Ray (org_bottom, dir_up, brush, &bottom)) + { + bottom = org_bottom[2] + bottom; + + q = Texture_ForName (brush->brush_faces->texdef.name); + qglColor3f (q->color[0], q->color[1], q->color[2]); + qglBegin (GL_QUADS); + qglVertex2f (-xCam, bottom); + qglVertex2f (xCam, bottom); + qglVertex2f (xCam, top); + qglVertex2f (-xCam, top); + qglEnd (); + } + } + } + + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES]); + qglBegin (GL_LINE_LOOP); + qglVertex2f (-xCam, brush->mins[2]); + qglVertex2f (xCam, brush->mins[2]); + qglVertex2f (xCam, brush->maxs[2]); + qglVertex2f (-xCam, brush->maxs[2]); + qglEnd (); + } + + + ZDrawCameraIcon (); + ZDrawZClip(); + + qglFinish(); + QE_CheckOpenGLForErrors(); + + if (z.timing) + { + end = Sys_DoubleTime (); + Sys_Printf ("z: %i ms\n", (int)(1000*(end-start))); + } +} + diff --git a/utils/Radiant/z.h b/utils/Radiant/z.h new file mode 100644 index 0000000..185bc62 --- /dev/null +++ b/utils/Radiant/z.h @@ -0,0 +1,21 @@ + +// window system independent camera view code + +typedef struct +{ + int width, height; + + qboolean timing; + + vec3_t origin; // at center of window + float scale; +} z_t; + +extern z_t z; + +void Z_Init (void); +void Z_MouseDown (int x, int y, int buttons); +void Z_MouseUp (int x, int y, int buttons); +void Z_MouseMoved (int x, int y, int buttons); +void Z_Draw (void); + diff --git a/utils/Radiant/zclip.cpp b/utils/Radiant/zclip.cpp new file mode 100644 index 0000000..870015c --- /dev/null +++ b/utils/Radiant/zclip.cpp @@ -0,0 +1,171 @@ +// Filename:- zclip.cpp +// + +#include "stdafx.h" +#include "Radiant.h" +#include "ZWnd.h" +#include "qe3.h" +#include "zclip.h" + + +CZClip::CZClip() +{ + LONG + lSize = sizeof(m_bEnabled); + if (!LoadRegistryInfo("Radiant::ZClipEnabled", &m_bEnabled, &lSize)) + m_bEnabled = false; + + lSize = sizeof(m_iZClipTop); + if (!LoadRegistryInfo("Radiant::ZClipTop", &m_iZClipTop, &lSize)) + m_iZClipTop = 64; + + lSize = sizeof(m_iZClipBottom); + if (!LoadRegistryInfo("Radiant::ZClipBottom", &m_iZClipBottom, &lSize)) + m_iZClipBottom = -64; + + Legalise(); +} + +CZClip::~CZClip() +{ + // TODO: registry save + + SaveRegistryInfo("Radiant::ZClipEnabled", &m_bEnabled, sizeof(m_bEnabled)); + SaveRegistryInfo("Radiant::ZClipTop", &m_iZClipTop, sizeof(m_iZClipTop)); + SaveRegistryInfo("Radiant::ZClipBottom", &m_iZClipBottom, sizeof(m_iZClipBottom)); +} + +void CZClip::Reset(void) +{ + m_iZClipTop = 64; // arb. starting values, but must be at least 64 apart + m_iZClipBottom = -64; + m_bEnabled = false; + + Legalise(); +} + + +int CZClip::GetTop(void) +{ + return m_iZClipTop; +} + +int CZClip::GetBottom(void) +{ + return m_iZClipBottom; +} + +void CZClip::Legalise(void) +{ + // need swapping? + // + if (m_iZClipTop < m_iZClipBottom) + { + int iTemp = m_iZClipTop; + m_iZClipTop = m_iZClipBottom; + m_iZClipBottom = iTemp; + } + + // too close together? + // +#define ZCLIP_MIN_SPACING 64 + + if (abs(m_iZClipTop - m_iZClipBottom) < ZCLIP_MIN_SPACING) + m_iZClipBottom = m_iZClipTop - ZCLIP_MIN_SPACING; +} + + +void CZClip::SetTop(int iNewZ) +{ + m_iZClipTop = iNewZ; + + Legalise(); +} + +void CZClip::SetBottom(int iNewZ) +{ + m_iZClipBottom = iNewZ; + + Legalise(); +} + +bool CZClip::IsEnabled(void) +{ + return m_bEnabled; +} + + +bool CZClip::Enable(bool bOnOff) +{ + m_bEnabled = !m_bEnabled; + return IsEnabled(); +} + +#define ZCLIP_BAR_THICKNESS 8 +#define ZCLIP_ARROWHEIGHT (ZCLIP_BAR_THICKNESS*8) + +void CZClip::Paint(void) +{ + float x, y; + int xCam = z.width/4; // hmmm, a rather unpleasant and obscure global name, but it was already called that so... + + qglColor3f (ZCLIP_COLOUR);//1.0, 0.0, 1.0); + + // draw TOP marker... + // + x = 0; + y = m_iZClipTop; + + if (m_bEnabled) + qglBegin(GL_QUADS); + else + qglBegin(GL_LINE_LOOP); + + qglVertex3f (x-xCam,y,0); + qglVertex3f (x-xCam,y+ZCLIP_BAR_THICKNESS,0); + qglVertex3f (x+xCam,y+ZCLIP_BAR_THICKNESS,0); + qglVertex3f (x+xCam,y,0); + qglEnd (); + + qglColor3f (ZCLIP_COLOUR_DIM);//0.8, 0.0, 0.8); + + if (m_bEnabled) + qglBegin(GL_TRIANGLES); + else + qglBegin(GL_LINE_LOOP); + qglVertex3f (x,(y+ZCLIP_BAR_THICKNESS),0); + qglVertex3f (x-xCam,(y+ZCLIP_BAR_THICKNESS)+(ZCLIP_ARROWHEIGHT/2),0); + qglVertex3f (x+xCam,(y+ZCLIP_BAR_THICKNESS)+(ZCLIP_ARROWHEIGHT/2),0); + qglEnd (); + + // draw bottom marker... + // + qglColor3f (ZCLIP_COLOUR);//1.0, 0.0, 1.0); + x = 0; + y = m_iZClipBottom; + + if (m_bEnabled) + qglBegin(GL_QUADS); + else + qglBegin(GL_LINE_LOOP); + qglVertex3f (x-xCam,y,0); + qglVertex3f (x-xCam,y-ZCLIP_BAR_THICKNESS,0); + qglVertex3f (x+xCam,y-ZCLIP_BAR_THICKNESS,0); + qglVertex3f (x+xCam,y,0); + qglEnd (); + + qglColor3f (ZCLIP_COLOUR_DIM);//0.8, 0.0, 0.8); + + if (m_bEnabled) + qglBegin(GL_TRIANGLES); + else + qglBegin(GL_LINE_LOOP); + qglVertex3f (x,(y-ZCLIP_BAR_THICKNESS),0); + qglVertex3f (x-xCam,(y-ZCLIP_BAR_THICKNESS)-(ZCLIP_ARROWHEIGHT/2),0); + qglVertex3f (x+xCam,(y-ZCLIP_BAR_THICKNESS)-(ZCLIP_ARROWHEIGHT/2),0); + qglEnd (); +} + + +///////////////// eof /////////////////// + diff --git a/utils/Radiant/zclip.h b/utils/Radiant/zclip.h new file mode 100644 index 0000000..b3a3d2b --- /dev/null +++ b/utils/Radiant/zclip.h @@ -0,0 +1,43 @@ +// Filename:- zclip.h +// + +#ifndef ZCLIP_H +#define ZCLIP_H + +// I don't like doing macros without braces and with whitespace, but the compiler moans if I do these differently, +// and since they're only for use within glColor3f() calls anyway then this is ok... (that's my excuse anyway) +// +#define ZCLIP_COLOUR 1.0, 0.0, 1.0 +#define ZCLIP_COLOUR_DIM 0.8, 0.0, 0.8 + + +class CZClip +{ +public: + CZClip(); + ~CZClip(); + + int GetTop(void); + int GetBottom(void); + void SetTop(int iNewZ); + void SetBottom(int iNewZ); + void Reset(void); + bool IsEnabled(void); + bool Enable(bool bOnOff); + void Paint(void); + +protected: + void Legalise(void); + + bool m_bEnabled; + int m_iZClipTop; + int m_iZClipBottom; +}; + + +#endif // #ifndef ZCLIP_H + + +///////////// eof /////////////// + + diff --git a/utils/Radiant/zwnd.cpp b/utils/Radiant/zwnd.cpp new file mode 100644 index 0000000..98aa1c7 --- /dev/null +++ b/utils/Radiant/zwnd.cpp @@ -0,0 +1,253 @@ +// ZWnd.cpp : implementation file +// + +#include "stdafx.h" +#include "Radiant.h" +#include "ZWnd.h" +#include "qe3.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + + +///////////////////////////////////////////////////////////////////////////// +// CZWnd +IMPLEMENT_DYNCREATE(CZWnd, CWnd); + + +CZWnd::CZWnd() +{ + m_pZClip = NULL; +} + +CZWnd::~CZWnd() +{ +} + + +BEGIN_MESSAGE_MAP(CZWnd, CWnd) + //{{AFX_MSG_MAP(CZWnd) + ON_WM_CREATE() + ON_WM_DESTROY() + ON_WM_KEYDOWN() + ON_WM_LBUTTONDOWN() + ON_WM_MBUTTONDOWN() + ON_WM_RBUTTONDOWN() + ON_WM_PAINT() + ON_WM_GETMINMAXINFO() + ON_WM_MOUSEMOVE() + ON_WM_SIZE() + ON_WM_NCCALCSIZE() + ON_WM_KILLFOCUS() + ON_WM_SETFOCUS() + ON_WM_CLOSE() + ON_WM_LBUTTONUP() + ON_WM_MBUTTONUP() + ON_WM_RBUTTONUP() + ON_WM_KEYUP() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +///////////////////////////////////////////////////////////////////////////// +// CZWnd message handlers + +int CZWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CWnd::OnCreate(lpCreateStruct) == -1) + return -1; + + g_qeglobals.d_hwndZ = GetSafeHwnd(); + + m_dcZ = ::GetDC(GetSafeHwnd()); + QEW_SetupPixelFormat(m_dcZ, false); + if ((m_hglrcZ = qwglCreateContext(m_dcZ )) == 0) + Error("wglCreateContext in CZWnd::OnCreate failed"); + + if (!qwglShareLists(g_qeglobals.d_hglrcBase, m_hglrcZ)) + Error( "wglShareLists in CZWnd::OnCreate failed"); + + if (!qwglMakeCurrent(m_dcZ, m_hglrcZ)) + Error ("wglMakeCurrent in CZWnd::OnCreate failed"); + + m_pZClip = new CZClip(); + + return 0; +} + +void CZWnd::OnDestroy() +{ + if (m_pZClip) + { + delete m_pZClip; + m_pZClip = NULL; + } + + QEW_StopGL(GetSafeHwnd(), m_hglrcZ, m_dcZ); + CWnd::OnDestroy(); +} + +void CZWnd::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + g_pParentWnd->HandleKey(nChar, nRepCnt, nFlags); +} + +void CZWnd::OnLButtonDown(UINT nFlags, CPoint point) +{ + SetFocus(); + SetCapture(); + CRect rctZ; + GetClientRect(rctZ); + Z_MouseDown (point.x, rctZ.Height() - 1 - point.y , nFlags); +} + +void CZWnd::OnMButtonDown(UINT nFlags, CPoint point) +{ + SetFocus(); + SetCapture(); + CRect rctZ; + GetClientRect(rctZ); + Z_MouseDown (point.x, rctZ.Height() - 1 - point.y , nFlags); +} + +void CZWnd::OnRButtonDown(UINT nFlags, CPoint point) +{ + SetFocus(); + SetCapture(); + CRect rctZ; + GetClientRect(rctZ); + Z_MouseDown (point.x, rctZ.Height() - 1 - point.y , nFlags); +} + +void CZWnd::OnPaint() +{ + CPaintDC dc(this); // device context for painting + //if (!wglMakeCurrent(m_dcZ, m_hglrcZ)) + if (!qwglMakeCurrent(dc.m_hDC, m_hglrcZ)) + { + Sys_Printf("ERROR: wglMakeCurrent failed..\n "); + Sys_Printf("Please restart QERadiant if the Z view is not working\n"); + } + else + { + QE_CheckOpenGLForErrors(); + Z_Draw (); + //qwglSwapBuffers(m_dcZ); + qwglSwapBuffers(dc.m_hDC); +// TRACE("Z Paint\n"); + } +} + +void CZWnd::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) +{ + lpMMI->ptMinTrackSize.x = ZWIN_WIDTH; +} + +void CZWnd::OnMouseMove(UINT nFlags, CPoint point) +{ + CRect rctZ; + GetClientRect(rctZ); + float fz = z.origin[2] + ((rctZ.Height() - 1 - point.y) - (z.height/2)) / z.scale; + fz = floor(fz / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + CString strStatus; + strStatus.Format("Z:: %.1f", fz); + g_pParentWnd->SetStatusText(1, strStatus); + Z_MouseMoved (point.x, rctZ.Height() - 1 - point.y, nFlags); +} + +void CZWnd::OnSize(UINT nType, int cx, int cy) +{ + CWnd::OnSize(nType, cx, cy); + CRect rctZ; + GetClientRect(rctZ); + z.width = rctZ.right; + z.height = rctZ.bottom; + if (z.width < 10) + z.width = 10; + if (z.height < 10) + z.height = 10; + Invalidate(); +} + +void CZWnd::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp) +{ + CWnd::OnNcCalcSize(bCalcValidRects, lpncsp); +} + +void CZWnd::OnKillFocus(CWnd* pNewWnd) +{ + CWnd::OnKillFocus(pNewWnd); + SendMessage(WM_NCACTIVATE, FALSE , 0 ); +} + +void CZWnd::OnSetFocus(CWnd* pOldWnd) +{ + CWnd::OnSetFocus(pOldWnd); + SendMessage(WM_NCACTIVATE, TRUE , 0 ); +} + +void CZWnd::OnClose() +{ + CWnd::OnClose(); +} + +void CZWnd::OnLButtonUp(UINT nFlags, CPoint point) +{ + CRect rctZ; + GetClientRect(rctZ); + Z_MouseUp (point.x, rctZ.bottom - 1 - point.y, nFlags); + if (! (nFlags & (MK_LBUTTON|MK_RBUTTON|MK_MBUTTON))) + ReleaseCapture (); +} + +void CZWnd::OnMButtonUp(UINT nFlags, CPoint point) +{ + CRect rctZ; + GetClientRect(rctZ); + Z_MouseUp (point.x, rctZ.bottom - 1 - point.y, nFlags); + if (! (nFlags & (MK_LBUTTON|MK_RBUTTON|MK_MBUTTON))) + ReleaseCapture (); +} + +void CZWnd::OnRButtonUp(UINT nFlags, CPoint point) +{ + CRect rctZ; + GetClientRect(rctZ); + Z_MouseUp (point.x, rctZ.bottom - 1 - point.y, nFlags); + if (! (nFlags & (MK_LBUTTON|MK_RBUTTON|MK_MBUTTON))) + ReleaseCapture (); +} + + +BOOL CZWnd::PreCreateWindow(CREATESTRUCT& cs) +{ + WNDCLASS wc; + HINSTANCE hInstance = AfxGetInstanceHandle(); + if (::GetClassInfo(hInstance, Z_WINDOW_CLASS, &wc) == FALSE) + { + // Register a new class + memset (&wc, 0, sizeof(wc)); + wc.style = CS_NOCLOSE | CS_OWNDC; + wc.lpszClassName = Z_WINDOW_CLASS; + wc.hCursor = LoadCursor (NULL,IDC_ARROW); + wc.lpfnWndProc = ::DefWindowProc; + if (AfxRegisterClass(&wc) == FALSE) + Error ("CZWnd RegisterClass: failed"); + } + + cs.lpszClass = Z_WINDOW_CLASS; + cs.lpszName = "Z"; + if (cs.style != QE3_CHILDSTYLE) + cs.style = QE3_SPLITTER_STYLE; + + return CWnd::PreCreateWindow(cs); +} + + +void CZWnd::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + g_pParentWnd->HandleKey(nChar, nRepCnt, nFlags, false); +} diff --git a/utils/Radiant/zwnd.h b/utils/Radiant/zwnd.h new file mode 100644 index 0000000..8af228b --- /dev/null +++ b/utils/Radiant/zwnd.h @@ -0,0 +1,74 @@ +#if !defined(AFX_ZWND_H__44B4BA02_781B_11D1_B53C_00AA00A410FC__INCLUDED_) +#define AFX_ZWND_H__44B4BA02_781B_11D1_B53C_00AA00A410FC__INCLUDED_ + +#include "zclip.h" + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// ZWnd.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CZWnd window + +class CZWnd : public CWnd +{ + DECLARE_DYNCREATE(CZWnd); +// Construction +public: + CZWnd(); + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CZWnd) + protected: + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CZWnd(); + + CZClip *m_pZClip; + + // Generated message map functions +protected: + + HDC m_dcZ; + HGLRC m_hglrcZ; + //{{AFX_MSG(CZWnd) + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnDestroy(); + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnMButtonDown(UINT nFlags, CPoint point); + afx_msg void OnRButtonDown(UINT nFlags, CPoint point); + afx_msg void OnPaint(); + afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp); + afx_msg void OnKillFocus(CWnd* pNewWnd); + afx_msg void OnSetFocus(CWnd* pOldWnd); + afx_msg void OnClose(); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg void OnMButtonUp(UINT nFlags, CPoint point); + afx_msg void OnRButtonUp(UINT nFlags, CPoint point); + afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_ZWND_H__44B4BA02_781B_11D1_B53C_00AA00A410FC__INCLUDED_) diff --git a/utils/Utils.dsw b/utils/Utils.dsw new file mode 100644 index 0000000..21fa467 --- /dev/null +++ b/utils/Utils.dsw @@ -0,0 +1,365 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "AnimatEd"=.\AnimatEd\AnimatEd.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/Utils/AnimatEd", TLCAAAAA + .\animated + end source code control +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name cmdlib + End Project Dependency + Begin Project Dependency + Project_Dep_Name pak + End Project Dependency +}}} + +############################################################################### + +Project: "ConfusEd"=.\ConfusEd\ConfusEd.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/Utils/ConfusEd", CKCAAAAA + .\confused + end source code control +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name cmdlib + End Project Dependency + Begin Project Dependency + Project_Dep_Name pak + End Project Dependency +}}} + +############################################################################### + +Project: "Png2Jpg"=.\png2jpg\Png2Jpg.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/Utils/Png2Jpg", ZNFAAAAA + .\png2jpg + end source code control +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name cmdlib + End Project Dependency + Begin Project Dependency + Project_Dep_Name image + End Project Dependency + Begin Project Dependency + Project_Dep_Name pak + End Project Dependency +}}} + +############################################################################### + +Project: "Radiant"=.\q3radiant\Radiant.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/Utils/q3radiant", FQBAAAAA + .\q3radiant + end source code control +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name image + End Project Dependency + Begin Project Dependency + Project_Dep_Name pak + End Project Dependency +}}} + +############################################################################### + +Project: "ShaderEd2"=.\ShaderEd2\ShaderEd2.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/Utils/ShaderEd2", NNAAAAAA + .\shadered2 + end source code control +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name image + End Project Dependency + Begin Project Dependency + Project_Dep_Name pak + End Project Dependency + Begin Project Dependency + Project_Dep_Name cmdlib + End Project Dependency +}}} + +############################################################################### + +Project: "SklView"=.\SklView\SklView.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/Utils/SklView", BCGAAAAA + .\sklview + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "cmdlib"=.\libs\cmdlib\cmdlib.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/Utils/libs/cmdlib", NHCAAAAA + .\libs\cmdlib + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "ft2"=.\LIBS\ft2\ft2.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/Utils/libs/ft2", REFAAAAA + .\libs\ft2 + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "image"=.\libs\image\image.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/Utils/libs/image", XGCAAAAA + .\libs\image + end source code control +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name cmdlib + End Project Dependency + Begin Project Dependency + Project_Dep_Name jpeg6 + End Project Dependency + Begin Project Dependency + Project_Dep_Name png + End Project Dependency + Begin Project Dependency + Project_Dep_Name tga + End Project Dependency +}}} + +############################################################################### + +Project: "jpeg6"=.\libs\jpeg6\jpeg6.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/Utils/libs/jpeg6", VHCAAAAA + .\libs\jpeg6 + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "pak"=.\libs\pak\pak.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/Utils/libs/pak", QHCAAAAA + .\libs\pak + end source code control +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name zlib + End Project Dependency +}}} + +############################################################################### + +Project: "png"=.\libs\png\png.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/Utils/libs/png", OGCAAAAA + .\libs\png + end source code control +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name zlib + End Project Dependency +}}} + +############################################################################### + +Project: "sof2data"=.\q3data\q3data.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/Utils/q3data", DQBAAAAA + .\q3data + end source code control +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name cmdlib + End Project Dependency + Begin Project Dependency + Project_Dep_Name ft2 + End Project Dependency + Begin Project Dependency + Project_Dep_Name image + End Project Dependency + Begin Project Dependency + Project_Dep_Name pak + End Project Dependency +}}} + +############################################################################### + +Project: "sof2map"=.\q3map\q3map.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/Utils/q3map", EQBAAAAA + .\q3map + end source code control +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name image + End Project Dependency + Begin Project Dependency + Project_Dep_Name pak + End Project Dependency +}}} + +############################################################################### + +Project: "striped"=.\striped\striped.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/Utils/striped", DWEAAAAA + .\striped + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "tga"=.\libs\tga\tga.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/Utils/libs/tga", TGCAAAAA + .\libs\tga + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "zlib"=.\libs\zlib\zlib.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/Utils/libs/zlib", VECAAAAA + .\libs\zlib + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ + begin source code control + "$/Utils", INAAAAAA + . + end source code control +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/utils/carcass/mdx_format.h b/utils/carcass/mdx_format.h new file mode 100644 index 0000000..e8239d0 --- /dev/null +++ b/utils/carcass/mdx_format.h @@ -0,0 +1,338 @@ +// Filename:- mdx_format.h +// +// DO NOT UPDATE THIS FILE IN ANY WAY WHATSOEVER WITHOUT TELLING ME (-Ste), +// BECAUSE THE MASTER COPY IS IN A DIFFERENT SOURCESAFE DATABASE AND WILL +// JUST GET PASTED OVER THIS ONE WHENEVER I CHANGE IT. +// +// +// +// MDX file format (typically uses file extension GLX for mesh, and GLA for anim/skeleton file) +// +// Notes: +// +// - All offset fields are relative to the address of the structure they occur in +// - So far, the only external symbol needed is MAX_QPATH, plus the typedefs for vec3_t, vec2_t etc + +#ifndef MDX_FORMAT_H +#define MDX_FORMAT_H + + +#define MDXM_IDENT (('M'<<24)+('G'<<16)+('L'<<8)+'2') +#define MDXA_IDENT (('A'<<24)+('G'<<16)+('L'<<8)+'2') + +#define MAX_TAGNAME_BYTES 32 // matches MDR, can be changed if nec. + +// +// normal version numbers... +// +#define MDXM_VERSION 4 +#define MDXA_VERSION 4 +#define MDXA_VERSION_QUAT 5 + +// (Note that since there is now a "_info.txt" file written out by carcass any changes made in here that +// introduce new data should also be reflected in the info-output) + +// 32 bit-flags for ghoul2 bone properties... (all undefined fields will be blank) +// +#define G2BONEFLAG_ALWAYSXFORM 0x00000001 + +// same thing but for surfaces... (Carcass will only generate 1st 2 flags, others are ingame +// +#define G2SURFACEFLAG_ISBOLT 0x00000001 +#define G2SURFACEFLAG_OFF 0x00000002 // saves strcmp()ing for "_off" in surface names +#define G2SURFACEFLAG_SPARE0 0x00000004 // future-expansion fields, saves invalidating models if we add more +#define G2SURFACEFLAG_SPARE1 0x00000008 // +#define G2SURFACEFLAG_SPARE2 0x00000010 // +#define G2SURFACEFLAG_SPARE3 0x00000020 // +#define G2SURFACEFLAG_SPARE4 0x00000040 // +#define G2SURFACEFLAG_SPARE5 0x00000080 // +// +#define G2SURFACEFLAG_NODESCENDANTS 0x00000100 // ingame-stuff, never generated by Carcass.... +#define G2SURFACEFLAG_GENERATED 0x00000200 // + + + +// triangle side-ordering stuff for tags... +// +#define iG2_TRISIDE_MIDDLE 1 +#define iG2_TRISIDE_LONGEST 0 +#define iG2_TRISIDE_SHORTEST 2 + + +#define MAX_G2_BONEREFS_PER_SURFACE 28 // currently only enforced when compiling Q3/G2 models, not xmen view-only tests +#define sDEFAULT_GLA_NAME "*default" // used when making special simple ghoul2 models, usually from MD3 files + + +//////////////////////////////////// +// +// these structs are defined here purely because of structure dependancy order... +// +typedef struct { + int boneIndex; // these are indexes into the surface boneReferences, not the global bone index + float boneWeight; // not the global per-frame bone list +} mdxmWeight_t; + + +#ifdef __cplusplus +struct mdxaCompBone_t +#else +typedef struct +#endif +{ + unsigned char Comp[24]; // MC_COMP_BYTES is in MatComp.h, but don't want to couple + + // I'm defining this '<' operator so this struct can be used as an STL key... + // + #ifdef __cplusplus + bool operator < (const mdxaCompBone_t& _X) const {return (memcmp(Comp,_X.Comp,sizeof(Comp))<0);} + #endif +} +#ifndef __cplusplus +mdxaCompBone_t +#endif +; + +#ifdef __cplusplus +struct mdxaCompQuatBone_t +#else +typedef struct +#endif +{ + unsigned char Comp[14]; + + // I'm defining this '<' operator so this struct can be used as an STL key... + // + #ifdef __cplusplus + bool operator < (const mdxaCompQuatBone_t& _X) const {return (memcmp(Comp,_X.Comp,sizeof(Comp))<0);} + #endif +} +#ifndef __cplusplus +mdxaCompQuatBone_t +#endif +; + + +#ifndef MDXABONEDEF +typedef struct { + float matrix[3][4]; +} mdxaBone_t; +#endif + +//////////////////////////////////// + + + + + + +// mdxHeader_t - this contains the header for the file, with sanity checking and version checking, plus number of lod's to be expected +// +typedef struct { + // + // ( first 3 fields are same format as MD3/MDR so we can apply easy model-format-type checks ) + // + int ident; // "IDP3" = MD3, "RDM5" = MDR, "2LGM"(GL2 Mesh) = MDX (cruddy char order I know, but I'm following what was there in other versions) + int version; // 1,2,3 etc as per format revision + char name[MAX_QPATH]; // model name (eg "models/players/marine.glm") // note: extension supplied + char animName[MAX_QPATH];// name of animation file this mesh requires // note: extension missing + int animIndex; // filled in by game (carcass defaults it to 0) + + int numBones; // (for ingame version-checks only, ensure we don't ref more bones than skel file has) + + int numLODs; + int ofsLODs; + + int numSurfaces; // now that surfaces are drawn hierarchically, we have same # per LOD + int ofsSurfHierarchy; + + int ofsEnd; // EOF, which of course gives overall file size +} mdxmHeader_t; + + +// for each surface (doesn't actually need a struct for this, just makes source clearer) +// { + typedef struct + { + int offsets[1]; // variable sized (mdxmHeader_t->numSurfaces), each offset points to a mdxmSurfHierarchy_t below + } mdxmHierarchyOffsets_t; +// } + +// for each surface... (mdxmHeader_t->numSurfaces) +// { + // mdxmSurfHierarchy_t - contains hierarchical info for surfaces... + + typedef struct { + char name[MAX_QPATH]; + unsigned int flags; + char shader[MAX_QPATH]; + int shaderIndex; // for in-game use (carcass defaults to 0) + int parentIndex; // this points to the index in the file of the parent surface. -1 if null/root + int numChildren; // number of surfaces which are children of this one + int childIndexes[1]; // [mdxmSurfHierarch_t->numChildren] (variable sized) + } mdxmSurfHierarchy_t; // struct size = (int)( &((mdxmSurfHierarch_t *)0)->childIndexes[ mdxmSurfHierarch_t->numChildren ] ); +// } + + +// for each LOD... (mdxmHeader_t->numLODs) +// { + // mdxLOD_t - this contains the header for this LOD. Contains num of surfaces, offset to surfaces and offset to next LOD. Surfaces are shader sorted, so each surface = 1 shader + + typedef struct { + // (used to contain numSurface/ofsSurfaces fields, but these are same per LOD level now) + // + int ofsEnd; // offset to next LOD + } mdxmLOD_t; + + + typedef struct { // added in GLM version 3 for ingame use at Jake's request + int offsets[1]; // variable sized (mdxmHeader_t->numSurfaces), each offset points to surfaces below + } mdxmLODSurfOffset_t; + + + // for each surface... (mdxmHeader_t->numSurfaces) + // { + // mdxSurface_t - reuse of header format containing surface name, number of bones, offset to poly data and number of polys, offset to vertex information, and number of verts. NOTE offsets are relative to this header. + + typedef struct { + int ident; // this one field at least should be kept, since the game-engine may switch-case (but currently=0 in carcass) + + int thisSurfaceIndex; // 0...mdxmHeader_t->numSurfaces-1 (because of how ingame renderer works) + + int ofsHeader; // this will be a negative number, pointing back to main header + + int numVerts; + int ofsVerts; + + int numTriangles; + int ofsTriangles; + + int maxVertBoneWeights; // ... per vertex for hardware to reference. This number subtract the vert->numWeights gives # pad weights (which software will ignore) + + // Bone references are a set of ints representing all the bones + // present in any vertex weights for this surface. This is + // needed because a model may have surfaces that need to be + // drawn at different sort times, and we don't want to have + // to re-interpolate all the bones for each surface. + // + int numBoneReferences; + int ofsBoneReferences; + + int ofsEnd; // next surface follows + + } mdxmSurface_t; + + + // for each triangle... (mdxmSurface_t->numTriangles) + // { + // mdxTriangle_t - contains indexes into verts. One struct entry per poly. + + typedef struct { + int indexes[3]; + } mdxmTriangle_t; + // } + + + // for each vert... (mdxmSurface_t->numVerts) + // { + // mdxVertex_t - this is an array with number of verts from the surface definition as its bounds. It contains normal info, texture coors and number of weightings for this bone + + typedef struct { + vec3_t normal; + vec3_t vertCoords; + vec2_t texCoords; + int numWeights; // remember, this is for software counts, look at mdxmSurface_t->numActualWeights for skipping purposes to account for padded weights + mdxmWeight_t weights[1]; // variable sized + } mdxmVertex_t; + + // } vert + + // } surface +// } LOD + + + +//---------------------------------------------------------------------------- +// seperate file here for animation data... +// + + +// mdxaHeader_t - this contains the header for the file, with sanity checking and version checking, plus number of lod's to be expected +// +typedef struct { + // + // ( first 3 fields are same format as MD3/MDR so we can apply easy model-format-type checks ) + // + int ident; // "IDP3" = MD3, "RDM5" = MDR, "2LGA"(GL2 Anim) = MDXA + int version; // 1,2,3 etc as per format revision + // + char name[MAX_QPATH]; // GLA name (eg "skeletons/marine") // note: extension missing + float fScale; // will be zero if build before this field was defined, else scale it was built with + + // frames and bones are shared by all levels of detail + // + int numFrames; + int ofsFrames; + int numBones; // (no offset to these since they're inside the frames array) + int ofsCompBonePool; // offset to global compressed-bone pool that all frames use + int ofsSkel; // offset to mdxaSkel_t info + + int ofsEnd; // EOF, which of course gives overall file size + +} mdxaHeader_t; + + +// for each bone... (doesn't actually need a struct for this, just makes source clearer) +// { + typedef struct + { + int offsets[1]; // variable sized (mdxaHeader_t->numBones), each offset points to an mdxaSkel_t below + } mdxaSkelOffsets_t; +// } + + + +// for each bone... (mdxaHeader_t->numBones) +// { + // mdxaSkel_t - contains hierarchical info only... + + typedef struct { + char name[MAX_QPATH]; // name of bone + unsigned int flags; + int parent; // index of bone that is parent to this one, -1 = NULL/root + mdxaBone_t BasePoseMat; // base pose + mdxaBone_t BasePoseMatInv; // inverse, to save run-time calc + int numChildren; // number of children bones + int children[1]; // [mdxaSkel_t->numChildren] (variable sized) + } mdxaSkel_t; // struct size = (int)( &((mdxaSkel_t *)0)->children[ mdxaSkel_t->numChildren ] ); +// } + + + +// for each frame... (mdxaHeader_t->numFrames) +// { + // mdxaFrame_t - which contains the header for the bones for this surface, plus the actual bone matrices themselves + + typedef struct { + // (used to contain frame bounds info etc as well, doesn't now) + // + int boneIndexes[1]; // [numBones] ... into compressed bone pool + } mdxaFrame_t; // struct size = (int)( &((mdxaFrame_t *)0)->bones[ mdxaHeader_t->numBones ] ); + +// } + + +// Compressed-bone pool that all frames use (mdxaHeader_t->ofsCompBonePool) (defined at end because size unknown until end) +// for each bone in pool (unknown number, no actual total stored at the moment)... +// { + // mdxaCompBone_t (defined at file top because of struct dependancy) +// } + +//--------------------------------------------------------------------------- + + +#endif // #ifndef MDX_FORMAT_H + +//////////////////////// eof /////////////////////// + + + diff --git a/utils/common/aselib.c b/utils/common/aselib.c new file mode 100644 index 0000000..92b4bd9 --- /dev/null +++ b/utils/common/aselib.c @@ -0,0 +1,1222 @@ +#include "aselib.h" +#include +#include +#include + +#define MAX_ASE_MATERIALS 32 +#define MAX_ASE_OBJECTS 64 + 32 +#define MAX_ASE_ANIMATIONS 32 +//#define MAX_ASE_ANIMATION_FRAMES 1024 +#define MAX_ASE_ANIMATION_FRAMES 1280 + +#define VERBOSE( x ) { if ( ase.verbose ) { printf x ; } } + +typedef struct +{ + float x, y, z; + float nx, ny, nz; + float s, t; +} aseVertex_t; + +typedef struct +{ + float s, t; +} aseTVertex_t; + +typedef int aseFace_t[3]; + +typedef struct +{ + int numFaces; + int numVertexes; + int numTVertexes; + + int timeValue; + + aseVertex_t *vertexes; + aseTVertex_t *tvertexes; + aseFace_t *faces, *tfaces; + + int currentFace, currentVertex; +} aseMesh_t; + +typedef struct +{ + int numFrames; + aseMesh_t frames[MAX_ASE_ANIMATION_FRAMES]; + + int currentFrame; +} aseMeshAnimation_t; + +typedef struct +{ + char name[128]; +} aseMaterial_t; + +/* +** contains the animate sequence of a single surface +** using a single material +*/ +typedef struct +{ + char name[128]; + + int materialRef; + int numAnimations; + + aseMeshAnimation_t anim; + +} aseGeomObject_t; + +typedef struct +{ + int numMaterials; + aseMaterial_t materials[MAX_ASE_MATERIALS]; + aseGeomObject_t objects[MAX_ASE_OBJECTS]; + + char *buffer; + char *curpos; + int len; + + int currentObject; + qboolean verbose; + qboolean grabAnims; + +} ase_t; + + +static char s_token[1024]; +static ase_t ase; +static ase_t aseGrab; + +static void ASE_Process( int type ); +static void ASE_FreeGeomObject( int ndx ); + +/* +** ASE_Load +*/ +void ASE_Load( const char *filename, qboolean verbose, qboolean grabAnims, int type ) +{ + FILE *fp = fopen( filename, "rb" ); + + if ( !fp ) + Error( "File not found '%s'", filename ); + + memset( &ase, 0, sizeof( ase ) ); + + ase.verbose = verbose; + ase.grabAnims = grabAnims; + ase.len = Q_filelength( fp ); + + ase.curpos = ase.buffer = malloc( ase.len ); + assert (ase.buffer != NULL); + + printf( "Processing '%s'\n", filename ); + + if ( fread( ase.buffer, ase.len, 1, fp ) != 1 ) + { + fclose( fp ); + Error( "fread() != 1 for '%s'", filename ); + } + + fclose( fp ); + + ASE_Process(type); + free (ase.buffer); +} + +/* +** ASE_Free +*/ +void ASE_Free( void ) +{ + int i; + + for ( i = 0; i < ase.currentObject; i++ ) + { + ASE_FreeGeomObject( i ); + } +} + +/* +** ASE_GetNumSurfaces +*/ +int ASE_GetNumSurfaces( void ) +{ + return ase.currentObject; +} + +/* +** ASE_GetSurfaceName +*/ +const char *ASE_GetSurfaceName( int which ) +{ + aseGeomObject_t *pObject = &ase.objects[which]; + + if ( !pObject->anim.numFrames ) + return ""; + + return pObject->name; +} + +/* +** ASE_GetSurfaceAnimation +** +** Returns an animation (sequence of polysets) +*/ +polyset_t *ASE_GetSurfaceAnimation( int which, int *pNumFrames, int skipFrameStart, int skipFrameEnd, int maxFrames ) +{ + aseGeomObject_t *pObject = &ase.objects[which]; + polyset_t *psets; + int numFramesInAnimation; + int numFramesToKeep; + int i, f; + + if ( !pObject->anim.numFrames ) { + printf( "WARNING: ASE_GetSurfaceAnimation no frames on %s!\n", pObject->name); + return 0; + } + + if ( pObject->anim.numFrames >= maxFrames && maxFrames != -1 ) + { + numFramesInAnimation = maxFrames; + } + else + { + numFramesInAnimation = pObject->anim.numFrames; + if ( maxFrames != -1 ) + printf( "WARNING: ASE_GetSurfaceAnimation maxFrames(%d) > numFramesInAnimation(%d)\n",numFramesInAnimation, maxFrames ); + } + + if ( skipFrameEnd != -1 ) + numFramesToKeep = numFramesInAnimation - ( skipFrameEnd - skipFrameStart + 1 ); + else + numFramesToKeep = numFramesInAnimation; + + *pNumFrames = numFramesToKeep; + + psets = calloc( sizeof( polyset_t ) * numFramesToKeep, 1 ); + + for ( f = 0, i = 0; i < numFramesInAnimation; i++ ) + { + int t; + aseMesh_t *pMesh = &pObject->anim.frames[i]; + + if ( skipFrameStart != -1 ) + { + if ( i >= skipFrameStart && i <= skipFrameEnd ) + continue; + } + + strcpy( psets[f].name, pObject->name ); + strcpy( psets[f].materialname, ase.materials[pObject->materialRef].name ); + + psets[f].triangles = calloc( sizeof( triangle_t ) * pObject->anim.frames[i].numFaces, 1 ); + psets[f].numtriangles = pObject->anim.frames[i].numFaces; + + for ( t = 0; t < pObject->anim.frames[i].numFaces; t++ ) + { + int k; + + for ( k = 0; k < 3; k++ ) + { + psets[f].triangles[t].verts[k][0] = pMesh->vertexes[pMesh->faces[t][k]].x; + psets[f].triangles[t].verts[k][1] = pMesh->vertexes[pMesh->faces[t][k]].y; + psets[f].triangles[t].verts[k][2] = pMesh->vertexes[pMesh->faces[t][k]].z; + + if ( pMesh->tvertexes && pMesh->tfaces ) + { + psets[f].triangles[t].texcoords[k][0] = pMesh->tvertexes[pMesh->tfaces[t][k]].s; + psets[f].triangles[t].texcoords[k][1] = pMesh->tvertexes[pMesh->tfaces[t][k]].t; + } + + } + } + + f++; + } + + return psets; +} + +static void ASE_FreeGeomObject( int ndx ) +{ + aseGeomObject_t *pObject; + int i; + + pObject = &ase.objects[ndx]; + + for ( i = 0; i < pObject->anim.numFrames; i++ ) + { + if ( pObject->anim.frames[i].vertexes ) + { + free( pObject->anim.frames[i].vertexes ); + } + if ( pObject->anim.frames[i].tvertexes ) + { + free( pObject->anim.frames[i].tvertexes ); + } + if ( pObject->anim.frames[i].faces ) + { + free( pObject->anim.frames[i].faces ); + } + if ( pObject->anim.frames[i].tfaces ) + { + free( pObject->anim.frames[i].tfaces ); + } + } + + memset( pObject, 0, sizeof( *pObject ) ); +} + +static aseMesh_t *ASE_GetCurrentMesh( void ) +{ + aseGeomObject_t *pObject; + + if ( ase.currentObject >= MAX_ASE_OBJECTS ) + { + Error( "Too many GEOMOBJECTs" ); + return 0; // never called + } + + pObject = &ase.objects[ase.currentObject]; + + if ( pObject->anim.currentFrame >= MAX_ASE_ANIMATION_FRAMES ) + { + Error( "Too many MESHes" ); + return 0; + } + + return &pObject->anim.frames[pObject->anim.currentFrame]; +} + +static int CharIsTokenDelimiter( int ch ) +{ + if ( ch <= 32 ) + return 1; + return 0; +} + +static int ASE_GetToken( qboolean restOfLine ) +{ + int i = 0; + + if ( ase.buffer == 0 ) + return 0; + + if ( ( ase.curpos - ase.buffer ) == ase.len ) + return 0; + + // skip over crap + while ( ( ( ase.curpos - ase.buffer ) < ase.len ) && + ( *ase.curpos <= 32 ) ) + { + ase.curpos++; + } + + while ( ( ase.curpos - ase.buffer ) < ase.len ) + { + s_token[i] = *ase.curpos; + + ase.curpos++; + i++; + + if ( ( CharIsTokenDelimiter( s_token[i-1] ) && !restOfLine ) || + ( ( s_token[i-1] == '\n' ) || ( s_token[i-1] == '\r' ) ) ) + { + s_token[i-1] = 0; + break; + } + } + + s_token[i] = 0; + + return 1; +} + +static void ASE_ParseBracedBlock( void (*parser)( const char *token ) ) +{ + int indent = 0; + + while ( ASE_GetToken( qfalse ) ) + { + if ( !strcmp( s_token, "{" ) ) + { + indent++; + } + else if ( !strcmp( s_token, "}" ) ) + { + --indent; + if ( indent == 0 ) + break; + else if ( indent < 0 ) + Error( "Unexpected '}'" ); + } + else + { + if ( parser ) + parser( s_token ); + } + } +} + +static void ASE_SkipEnclosingBraces( void ) +{ + int indent = 0; + + while ( ASE_GetToken( qfalse ) ) + { + if ( !strcmp( s_token, "{" ) ) + { + indent++; + } + else if ( !strcmp( s_token, "}" ) ) + { + indent--; + if ( indent == 0 ) + break; + else if ( indent < 0 ) + Error( "Unexpected '}'" ); + } + } +} + +static void ASE_SkipRestOfLine( void ) +{ + ASE_GetToken( qtrue ); +} + +static void ASE_KeyMAP_DIFFUSE( const char *token ) +{ + char buffer[1024], buff1[1024], buff2[1024]; + char *buf1, *buf2; + int i = 0, count; + + if ( !strcmp( token, "*BITMAP" ) ) + { + ASE_GetToken( qfalse ); + + strcpy( buffer, s_token + 1 ); + if ( strchr( buffer, '"' ) ) + *strchr( buffer, '"' ) = 0; + + while ( buffer[i] ) + { + if ( buffer[i] == '\\' ) + buffer[i] = '/'; + i++; + } + + buf1 = gamedir; + buf2 = buffer; + // need to compare win32 volumes to potential unix junk + // + if ( (gamedir[1] == ':' && (buffer[0] == '/' && buffer[1] == '/')) || + (buffer[1] == ':' && (gamedir[0] == '/' && gamedir[1] == '/')) ) { + if (buffer[1] == ':') { + buf1 = gamedir + 2; + buf2 = buffer + 2; + } else { + buf1 = gamedir + 2; + buf2 = buffer +2; + } + count = 0; + while (*buf2 && count < 2) { + if (*buf2 == '/') { + count++; + } + buf2++; + } + } + strcpy(buff1, buf1); + strlwr(buff1); + strcpy(buff2, buf2); + strlwr(buff2); + if ( strstr( buff2, buff1 + 2 ) ) + { + strcpy( ase.materials[ase.numMaterials].name, strstr( buff2, buff1 + 2 ) + strlen( buff1 ) - 2 ); + } + else + { + sprintf( ase.materials[ase.numMaterials].name, "(not converted: '%s')", buffer ); + printf( "WARNING: illegal material name '%s'\n", buffer ); + } + } +} + +static void ASE_KeyMATERIAL( const char *token ) +{ + if ( !strcmp( token, "*MAP_DIFFUSE" ) ) + { + ASE_ParseBracedBlock( ASE_KeyMAP_DIFFUSE ); + } + else + { + } +} + +static void ASE_KeyMATERIAL_LIST( const char *token ) +{ + if ( !strcmp( token, "*MATERIAL_COUNT" ) ) + { + ASE_GetToken( qfalse ); + VERBOSE( ( "..num materials: %s\n", s_token ) ); + if ( atoi( s_token ) > MAX_ASE_MATERIALS ) + { + Error( "Too many materials!" ); + } + ase.numMaterials = 0; + } + else if ( !strcmp( token, "*MATERIAL" ) ) + { + VERBOSE( ( "..material %d ", ase.numMaterials ) ); + ASE_ParseBracedBlock( ASE_KeyMATERIAL ); + ase.numMaterials++; + } +} + +static void ASE_KeyMESH_VERTEX_LIST( const char *token ) +{ + aseMesh_t *pMesh = ASE_GetCurrentMesh(); + + if ( !strcmp( token, "*MESH_VERTEX" ) ) + { + ASE_GetToken( qfalse ); // skip number + + ASE_GetToken( qfalse ); + pMesh->vertexes[pMesh->currentVertex].y = atof( s_token ); + + ASE_GetToken( qfalse ); + pMesh->vertexes[pMesh->currentVertex].x = -atof( s_token ); + + ASE_GetToken( qfalse ); + pMesh->vertexes[pMesh->currentVertex].z = atof( s_token ); + + pMesh->currentVertex++; + + if ( pMesh->currentVertex > pMesh->numVertexes ) + { + Error( "pMesh->currentVertex >= pMesh->numVertexes" ); + } + } + else + { + Error( "Unknown token '%s' while parsing MESH_VERTEX_LIST", token ); + } +} + +static void ASE_KeyMESH_FACE_LIST( const char *token ) +{ + aseMesh_t *pMesh = ASE_GetCurrentMesh(); + + if ( !strcmp( token, "*MESH_FACE" ) ) + { + ASE_GetToken( qfalse ); // skip face number + + ASE_GetToken( qfalse ); // skip label + ASE_GetToken( qfalse ); // first vertex + pMesh->faces[pMesh->currentFace][0] = atoi( s_token ); + + ASE_GetToken( qfalse ); // skip label + ASE_GetToken( qfalse ); // second vertex + pMesh->faces[pMesh->currentFace][2] = atoi( s_token ); + + ASE_GetToken( qfalse ); // skip label + ASE_GetToken( qfalse ); // third vertex + pMesh->faces[pMesh->currentFace][1] = atoi( s_token ); + + ASE_GetToken( qtrue ); + +/* + if ( ( p = strstr( s_token, "*MESH_MTLID" ) ) != 0 ) + { + p += strlen( "*MESH_MTLID" ) + 1; + mtlID = atoi( p ); + } + else + { + Error( "No *MESH_MTLID found for face!" ); + } +*/ + + pMesh->currentFace++; + } + else + { + Error( "Unknown token '%s' while parsing MESH_FACE_LIST", token ); + } +} + +static void ASE_KeyTFACE_LIST( const char *token ) +{ + aseMesh_t *pMesh = ASE_GetCurrentMesh(); + + if ( !strcmp( token, "*MESH_TFACE" ) ) + { + int a, b, c; + + ASE_GetToken( qfalse ); + + ASE_GetToken( qfalse ); + a = atoi( s_token ); + ASE_GetToken( qfalse ); + c = atoi( s_token ); + ASE_GetToken( qfalse ); + b = atoi( s_token ); + + pMesh->tfaces[pMesh->currentFace][0] = a; + pMesh->tfaces[pMesh->currentFace][1] = b; + pMesh->tfaces[pMesh->currentFace][2] = c; + + pMesh->currentFace++; + } + else + { + Error( "Unknown token '%s' in MESH_TFACE", token ); + } +} + +static void ASE_KeyMESH_TVERTLIST( const char *token ) +{ + aseMesh_t *pMesh = ASE_GetCurrentMesh(); + + if ( !strcmp( token, "*MESH_TVERT" ) ) + { + char u[80], v[80], w[80]; + + ASE_GetToken( qfalse ); + + ASE_GetToken( qfalse ); + strcpy( u, s_token ); + + ASE_GetToken( qfalse ); + strcpy( v, s_token ); + + ASE_GetToken( qfalse ); + strcpy( w, s_token ); + + pMesh->tvertexes[pMesh->currentVertex].s = atof( u ); + pMesh->tvertexes[pMesh->currentVertex].t = 1.0f - atof( v ); + + pMesh->currentVertex++; + + if ( pMesh->currentVertex > pMesh->numTVertexes ) + { + Error( "pMesh->currentVertex > pMesh->numTVertexes" ); + } + } + else + { + Error( "Unknown token '%s' while parsing MESH_TVERTLIST" ); + } +} + +static void ASE_KeyMESH( const char *token ) +{ + aseMesh_t *pMesh = ASE_GetCurrentMesh(); + + if ( !strcmp( token, "*TIMEVALUE" ) ) + { + ASE_GetToken( qfalse ); + + pMesh->timeValue = atoi( s_token ); + VERBOSE( ( ".....timevalue: %d\n", pMesh->timeValue ) ); + } + else if ( !strcmp( token, "*MESH_NUMVERTEX" ) ) + { + ASE_GetToken( qfalse ); + + pMesh->numVertexes = atoi( s_token ); + VERBOSE( ( ".....TIMEVALUE: %d\n", pMesh->timeValue ) ); + VERBOSE( ( ".....num vertexes: %d\n", pMesh->numVertexes ) ); + } + else if ( !strcmp( token, "*MESH_NUMFACES" ) ) + { + ASE_GetToken( qfalse ); + + pMesh->numFaces = atoi( s_token ); + VERBOSE( ( ".....num faces: %d\n", pMesh->numFaces ) ); + } + else if ( !strcmp( token, "*MESH_NUMTVFACES" ) ) + { + ASE_GetToken( qfalse ); + + if ( atoi( s_token ) != pMesh->numFaces ) + { + Error( "MESH_NUMTVFACES != MESH_NUMFACES" ); + } + } + else if ( !strcmp( token, "*MESH_NUMTVERTEX" ) ) + { + ASE_GetToken( qfalse ); + + pMesh->numTVertexes = atoi( s_token ); + VERBOSE( ( ".....num tvertexes: %d\n", pMesh->numTVertexes ) ); + } + else if ( !strcmp( token, "*MESH_VERTEX_LIST" ) ) + { + pMesh->vertexes = calloc( sizeof( aseVertex_t ) * pMesh->numVertexes, 1 ); + pMesh->currentVertex = 0; + VERBOSE( ( ".....parsing MESH_VERTEX_LIST\n" ) ); + ASE_ParseBracedBlock( ASE_KeyMESH_VERTEX_LIST ); + } + else if ( !strcmp( token, "*MESH_TVERTLIST" ) ) + { + pMesh->currentVertex = 0; + pMesh->tvertexes = calloc( sizeof( aseTVertex_t ) * pMesh->numTVertexes, 1 ); + VERBOSE( ( ".....parsing MESH_TVERTLIST\n" ) ); + ASE_ParseBracedBlock( ASE_KeyMESH_TVERTLIST ); + } + else if ( !strcmp( token, "*MESH_FACE_LIST" ) ) + { + pMesh->faces = calloc( sizeof( aseFace_t ) * pMesh->numFaces, 1 ); + pMesh->currentFace = 0; + VERBOSE( ( ".....parsing MESH_FACE_LIST\n" ) ); + ASE_ParseBracedBlock( ASE_KeyMESH_FACE_LIST ); + } + else if ( !strcmp( token, "*MESH_TFACELIST" ) ) + { + pMesh->tfaces = calloc( sizeof( aseFace_t ) * pMesh->numFaces, 1 ); + pMesh->currentFace = 0; + VERBOSE( ( ".....parsing MESH_TFACE_LIST\n" ) ); + ASE_ParseBracedBlock( ASE_KeyTFACE_LIST ); + } + else if ( !strcmp( token, "*MESH_NORMALS" ) ) + { + ASE_ParseBracedBlock( 0 ); + } +} + +static void ASE_KeyMESH_ANIMATION( const char *token ) +{ + aseMesh_t *pMesh = ASE_GetCurrentMesh(); + + // loads a single animation frame + if ( !strcmp( token, "*MESH" ) ) + { + VERBOSE( ( "...found MESH\n" ) ); + assert( pMesh->faces == 0 ); + assert( pMesh->vertexes == 0 ); + assert( pMesh->tvertexes == 0 ); + memset( pMesh, 0, sizeof( *pMesh ) ); + + ASE_ParseBracedBlock( ASE_KeyMESH ); + + if ( ++ase.objects[ase.currentObject].anim.currentFrame == MAX_ASE_ANIMATION_FRAMES ) + { + Error( "Too many animation frames" ); + } + } + else + { + Error( "Unknown token '%s' while parsing MESH_ANIMATION", token ); + } +} + +static void ASE_KeyGEOMOBJECT( const char *token ) +{ + if ( !strcmp( token, "*NODE_NAME" ) ) + { + char *name = ase.objects[ase.currentObject].name; + + ASE_GetToken( qtrue ); + VERBOSE( ( " %s\n", s_token ) ); + strcpy( ase.objects[ase.currentObject].name, s_token + 1 ); + if ( strchr( ase.objects[ase.currentObject].name, '"' ) ) + *strchr( ase.objects[ase.currentObject].name, '"' ) = 0; + + if ( strstr( name, "tag" ) == name ) + { + while ( strchr( name, '_' ) != strrchr( name, '_' ) ) + { + *strrchr( name, '_' ) = 0; + } + while ( strrchr( name, ' ' ) ) + { + *strrchr( name, ' ' ) = 0; + } + } + } + else if ( !strcmp( token, "*NODE_PARENT" ) ) + { + ASE_SkipRestOfLine(); + } + // ignore unused data blocks + else if ( !strcmp( token, "*NODE_TM" ) || + !strcmp( token, "*TM_ANIMATION" ) ) + { + ASE_ParseBracedBlock( 0 ); + } + // ignore regular meshes that aren't part of animation + else if ( !strcmp( token, "*MESH" ) && !ase.grabAnims ) + { +/* + if ( strstr( ase.objects[ase.currentObject].name, "tag_" ) == ase.objects[ase.currentObject].name ) + { + s_forceStaticMesh = qtrue; + ASE_ParseBracedBlock( ASE_KeyMESH ); + s_forceStaticMesh = qfalse; + } +*/ + ASE_ParseBracedBlock( ASE_KeyMESH ); + if ( ++ase.objects[ase.currentObject].anim.currentFrame == MAX_ASE_ANIMATION_FRAMES ) + { + Error( "Too many animation frames" ); + } + ase.objects[ase.currentObject].anim.numFrames = ase.objects[ase.currentObject].anim.currentFrame; + ase.objects[ase.currentObject].numAnimations++; +/* + // ignore meshes that aren't part of animations if this object isn't a + // a tag + else + { + ASE_ParseBracedBlock( 0 ); + } +*/ + } + // according to spec these are obsolete + else if ( !strcmp( token, "*MATERIAL_REF" ) ) + { + ASE_GetToken( qfalse ); + + ase.objects[ase.currentObject].materialRef = atoi( s_token ); + } + // loads a sequence of animation frames + else if ( !strcmp( token, "*MESH_ANIMATION" ) ) + { + if ( ase.grabAnims ) + { + VERBOSE( ( "..found MESH_ANIMATION\n" ) ); + + if ( ase.objects[ase.currentObject].numAnimations ) + { + Error( "Multiple MESH_ANIMATIONS within a single GEOM_OBJECT" ); + } + ASE_ParseBracedBlock( ASE_KeyMESH_ANIMATION ); + ase.objects[ase.currentObject].anim.numFrames = ase.objects[ase.currentObject].anim.currentFrame; + ase.objects[ase.currentObject].numAnimations++; + } + else + { + ASE_SkipEnclosingBraces(); + } + } + // skip unused info + else if ( !strcmp( token, "*PROP_MOTIONBLUR" ) || + !strcmp( token, "*PROP_CASTSHADOW" ) || + !strcmp( token, "*PROP_RECVSHADOW" ) ) + { + ASE_SkipRestOfLine(); + } +} + +#if 0 +static void ConcatenateObjects( aseGeomObject_t *pObjA, aseGeomObject_t *pObjB ) +{ +// int numVertexes; + +} + +static void CollapseObjects( void ) +{ + int i; + int numObjects = ase.currentObject; + + for ( i = 0; i < numObjects; i++ ) + { + int j; + + // skip tags + if ( strstr( ase.objects[i].name, "tag" ) == ase.objects[i].name ) + { + continue; + } + + if ( !ase.objects[i].numAnimations ) + { + continue; + } + + for ( j = i + 1; j < numObjects; j++ ) + { + if ( strstr( ase.objects[j].name, "tag" ) == ase.objects[j].name ) + { + continue; + } + if ( ase.objects[i].materialRef == ase.objects[j].materialRef ) + { + if ( ase.objects[j].numAnimations ) + { + ConcatenateObjects( &ase.objects[i], &ase.objects[j] ); + } + } + } + } +} +#endif +/* +** ASE_Process +*/ +static void ASE_Process( int type ) +{ + while ( ASE_GetToken( qfalse ) ) + { + if ( !strcmp( s_token, "*3DSMAX_ASCIIEXPORT" ) || + !strcmp( s_token, "*COMMENT" ) ) + { + ASE_SkipRestOfLine(); + } + else if ( !strcmp( s_token, "*SCENE" ) ) + { + ASE_SkipEnclosingBraces(); + } + else if ( !strcmp( s_token, "*HELPEROBJECT" ) ) + { + ASE_SkipEnclosingBraces(); + } + else if ( !strcmp( s_token, "*MATERIAL_LIST" ) ) + { + VERBOSE( ("MATERIAL_LIST\n") ); + + ASE_ParseBracedBlock( ASE_KeyMATERIAL_LIST ); + } + else if ( !strcmp( s_token, "*GEOMOBJECT" ) ) + { + VERBOSE( ("GEOMOBJECT" ) ); + + ASE_ParseBracedBlock( ASE_KeyGEOMOBJECT ); + + if (!ase.objects[ase.currentObject].anim.frames[0].numFaces) //we didn't get any faces of animation! + { + Error( "WARNING: ASE_Process no triangles grabbed for GEOMOBJECT \"%s\"!\n", ase.objects[ase.currentObject].name); + } + _strlwr(ase.objects[ase.currentObject].name); + if ( strstr( ase.objects[ase.currentObject].name, "Bip" ) || + strstr( ase.objects[ase.currentObject].name, "ignore_" ) ) + { + VERBOSE( ( "(discarding BIP/ignore object)\n" ) ); + ASE_FreeGeomObject( ase.currentObject ); + } + else if ( (type /*== TYPE_PLAYER*/) && ( strstr( ase.objects[ase.currentObject].name, "h_" ) != ase.objects[ase.currentObject].name ) && + ( strstr( ase.objects[ase.currentObject].name, "l_" ) != ase.objects[ase.currentObject].name ) && + ( strstr( ase.objects[ase.currentObject].name, "u_" ) != ase.objects[ase.currentObject].name ) && + ( strstr( ase.objects[ase.currentObject].name, "tag" ) != ase.objects[ase.currentObject].name ) && + ase.grabAnims ) + { + VERBOSE( ( "(ignoring improperly labeled object '%s')\n", ase.objects[ase.currentObject].name ) ); + ASE_FreeGeomObject( ase.currentObject ); + } + else + { + if ( ++ase.currentObject == MAX_ASE_OBJECTS ) + { + Error( "Too many GEOMOBJECTs" ); + } + } + } + else if ( s_token[0] ) + { + printf( "Unknown token '%s'\n", s_token ); + } + } + + if ( !ase.currentObject ) + Error( "No animation data!" ); + +// CollapseObjects(); +} + +/* +** ASE_InitForGrabbing +** +** any sort of initialization required to use aseGrab +*/ +void ASE_InitForGrabbing(void) +{ + memset(&aseGrab, sizeof(ase_t), 0); +} + +/* +** ASE_CopyFrames +** +** helper for copying frames from one animation to another. +** do your bounds checking +*/ + +void ASE_CopyFrames(aseMeshAnimation_t *destAnim , + aseMeshAnimation_t *sourceAnim, int nDest, + int nSource, int nNum, framecopy_type fillType) +{ + int i = 0, s = nSource, d = 0; + + if (FRAMECOPY_NONE == fillType) + { + // what the heck are you tryin' to do? + return; + } + + for (i = 0; i < nNum; i++) + { + d = nDest + i; + if (FRAMECOPY_1TO1 == fillType) + { + s = nSource + i; + } + if (destAnim->frames[d].numVertexes) + { + free(destAnim->frames[d].vertexes); + } + if (destAnim->frames[d].numTVertexes) + { + free(destAnim->frames[d].tvertexes); + } + if (destAnim->frames[d].numFaces) + { + free(destAnim->frames[d].faces); + free(destAnim->frames[d].tfaces); + } + memcpy( &destAnim->frames[d], + &sourceAnim->frames[s], + sizeof(aseMesh_t)); + destAnim->frames[d].vertexes = calloc( sizeof( aseVertex_t ) * + sourceAnim->frames[s].numVertexes, 1 ); + memcpy(destAnim->frames[d].vertexes, sourceAnim->frames[s].vertexes, + sourceAnim->frames[s].numVertexes * sizeof(aseVertex_t)); + destAnim->frames[d].tvertexes = calloc( sizeof( aseTVertex_t ) * + sourceAnim->frames[s].numTVertexes, 1 ); + memcpy(destAnim->frames[d].tvertexes, sourceAnim->frames[s].tvertexes, + sourceAnim->frames[s].numTVertexes * sizeof(aseTVertex_t)); + destAnim->frames[d].faces = calloc( sizeof( aseFace_t ) * + sourceAnim->frames[s].numFaces, 1 ); + memcpy(destAnim->frames[d].faces, sourceAnim->frames[s].faces, + sourceAnim->frames[s].numFaces * sizeof(aseFace_t)); + destAnim->frames[d].tfaces = calloc( sizeof( aseFace_t ) * + sourceAnim->frames[s].numFaces, 1 ); + memcpy(destAnim->frames[d].tfaces, sourceAnim->frames[s].tfaces, + sourceAnim->frames[s].numFaces * sizeof(aseFace_t)); + } +} + +/* +** ASE_CopyGrabToConcatBuffer +** +** just copying information from ase into aseGrab...have to do some allocation 'n' stuff, +** so we're assuming aseGrab is zeroed out +*/ +void ASE_CopyGrabToConcatBuffer(const int nFillFrame) +{ + int i = 0; + aseGeomObject_t* sourceObj = NULL, *destObj = NULL; + aseMeshAnimation_t* sourceAnim = NULL, *destAnim = NULL; + + // don't care about copying ase's buffer or curpos information cuz we're + //never going to do any reading with aseGrab + aseGrab.currentObject = ase.currentObject; + aseGrab.grabAnims = ase.grabAnims; + aseGrab.len = ase.len; + aseGrab.numMaterials = ase.numMaterials; + aseGrab.verbose = ase.verbose; + + for (i = 0; i < ase.numMaterials; i++) + { + strcpy(aseGrab.materials[i].name, ase.materials[i].name); + } + + for (i = 0; i < ase.currentObject; i++) + { + sourceObj = &ase.objects[i]; + destObj = &aseGrab.objects[i]; + + destObj->materialRef = sourceObj->materialRef; + strcpy(destObj->name, sourceObj->name); + destObj->numAnimations = sourceObj->numAnimations; + destObj->anim.numFrames = sourceObj->anim.numFrames; + destObj->anim.currentFrame = sourceObj->anim.currentFrame; + sourceAnim = &sourceObj->anim; + destAnim = &destObj->anim; + + ASE_CopyFrames(destAnim, sourceAnim, 0, 0, sourceAnim->numFrames, FRAMECOPY_1TO1); + if (nFillFrame > destObj->anim.numFrames) + { + // fill the rest of the dest frames with the last source frame + ASE_CopyFrames(destAnim, sourceAnim, destObj->anim.numFrames, + sourceObj->anim.numFrames-1, nFillFrame - destAnim->numFrames, + FRAMECOPY_FILL); + destAnim->numFrames = nFillFrame; + destAnim->currentFrame = nFillFrame; + } + } +} + +/* +** ASE_CopyFromConcatBuffer +** +** just copying information from aseGrab into ase +*/ +void ASE_CopyFromConcatBuffer(void) +{ + int i = 0; + aseGeomObject_t* sourceObj = NULL, *destObj = NULL; + aseMeshAnimation_t* sourceAnim = NULL, *destAnim = NULL; + + // don't care about copying aseGrab's buffer or curpos information cuz this + //function _should_ only get called as part of the output sequence for a file + ase.currentObject = aseGrab.currentObject; + ase.grabAnims = aseGrab.grabAnims; + ase.len = aseGrab.len; + ase.numMaterials = aseGrab.numMaterials; + ase.verbose = aseGrab.verbose; + + for (i = 0; i < aseGrab.numMaterials; i++) + { + strcpy(ase.materials[i].name, aseGrab.materials[i].name); + } + + for (i = 0; i < aseGrab.currentObject; i++) + { + sourceObj = &aseGrab.objects[i]; + destObj = &ase.objects[i]; + + destObj->materialRef = sourceObj->materialRef; + strcpy(destObj->name, sourceObj->name); + destObj->numAnimations = sourceObj->numAnimations; + destObj->anim.numFrames = sourceObj->anim.numFrames; + destObj->anim.currentFrame = sourceObj->anim.currentFrame; + sourceAnim = &sourceObj->anim; + destAnim = &destObj->anim; + + ASE_CopyFrames(destAnim, sourceAnim, 0, 0, + sourceObj->anim.numFrames, FRAMECOPY_1TO1); + } +} + +/* +** ASE_CatGrabbedFrames +** +** precondition: ase has been loaded with a file +** +** concatenate the animation frames from the recently loaded ase struct with +**the existing model and frames already in aseGrab struct +*/ +qboolean ASE_CatGrabbedFrames(const int nSourceFrame, int nDestFrame, int nNumFrames, const int nFillFrame) +{ + qboolean bFailed = qfalse, bFirstGrab = (0 == aseGrab.currentObject); + int i, j, lastValidFrame, nFillPos, nMidFillPos,framesAvail; + aseMeshAnimation_t* sourceAnim = NULL, *destAnim = NULL; + + // gotta do a little special setup stuff if this is the first file we've grabbed + if (bFirstGrab) + { + //filling here means out to the last frame + ASE_CopyGrabToConcatBuffer(nFillFrame); + printf("\tFirst anim starts on 0, ends on %d (%d frames)\n", ase.objects[0].anim.numFrames-1, ase.objects[0].anim.numFrames); + return qfalse; + } + + // if no value was given for number of frames to grab, get 'em all + if (nNumFrames == -1) + { + nNumFrames = ase.objects[0].anim.numFrames; + } + + // if no value was given for dest frame, assume last one grabbed + if (nDestFrame== -1) + { + nDestFrame = aseGrab.objects[0].anim.numFrames; + } + + nFillPos = nDestFrame + nNumFrames; + lastValidFrame = ase.objects[0].anim.numFrames - 1; + framesAvail = ase.objects[0].anim.numFrames - nSourceFrame; + nMidFillPos = nDestFrame + framesAvail; + + // filling after the first grab, means only fill to the number of frames specified if the source doesn't have enough + + // nMidFillPos position in dest animation to start filling in using the last frame of our source animation + // nFillPos position after dest animation to start filling out to end + // + // lastValidFrame frame of the source animation to use as a fill frame in the dest animation + // + + // since we've done at least one prior file grab, now all we want from ase is + //frame info. go through every object in the new file (ase) and add its frames to + //the corresponding object in aseGrab. + printf("\t\t anim starts on %d, ends on %d (%d frames)\n", nDestFrame, nDestFrame+nNumFrames-1, nNumFrames); + for (i = 0; i < aseGrab.currentObject; i++) + { + destAnim = &aseGrab.objects[i].anim; + for (j = 0; j < ase.currentObject; j++) + { + if (strcmp(aseGrab.objects[i].name, ase.objects[j].name) == 0) + { + sourceAnim = &ase.objects[j].anim; + if (nDestFrame + nNumFrames > MAX_ASE_ANIMATION_FRAMES) + { + Error("can't put that many frames where you want 'em!"); + bFailed = qtrue; + } + else + { + // can't just copy the desired frames cuz they involve + //frame-specific allocations. first, free the original (dest) frame, + //then allocate new space for the incoming (source) frame. then copy. + if (framesAvail >= nNumFrames) { + ASE_CopyFrames(destAnim, sourceAnim, nDestFrame, nSourceFrame, + nNumFrames, FRAMECOPY_1TO1); + } + else { //there is a shortage + ASE_CopyFrames(destAnim, sourceAnim, nDestFrame, nSourceFrame, + framesAvail, FRAMECOPY_1TO1); + // fill the rest of the dest frames with the last source frame + ASE_CopyFrames(destAnim, sourceAnim, nMidFillPos, lastValidFrame, + nNumFrames - framesAvail, FRAMECOPY_FILL); + } + //mark the highest frame filled - better hope there aren't any holes in the seq + if (destAnim->numFrames < nFillPos) + { + destAnim->numFrames = nFillPos; + } + + if (nFillFrame) //now this is to fill out to maxFrame + { + // fill the rest of the dest frames with the last source frame + ASE_CopyFrames(destAnim, sourceAnim, nFillPos, lastValidFrame, + nFillFrame - nFillPos, FRAMECOPY_FILL); + if (destAnim->numFrames < nFillFrame) + destAnim->numFrames = nFillFrame; + } + destAnim->currentFrame = destAnim->numFrames; + } + break; + } + } + } + return bFailed; +} + +void ASE_FreeGrab(void) +{ + int i = 0, j = 0; + aseMesh_t* mesh = NULL; + + for (i = 0; i < aseGrab.currentObject; i++) + { + for (j = 0; j < aseGrab.objects[i].anim.numFrames; j++) + { + mesh = &aseGrab.objects[i].anim.frames[j]; + if (mesh->numVertexes) + { + free(mesh->vertexes); + } + if (mesh->numTVertexes) + { + free(mesh->tvertexes); + } + if (mesh->numFaces) + { + free(mesh->faces); + free(mesh->tfaces); + } + } + } +} diff --git a/utils/common/aselib.h b/utils/common/aselib.h new file mode 100644 index 0000000..5e3b211 --- /dev/null +++ b/utils/common/aselib.h @@ -0,0 +1,22 @@ +#include "cmdlib.h" +#include "mathlib.h" +#include "polyset.h" + +typedef enum +{ + FRAMECOPY_NONE = 0, + FRAMECOPY_1TO1, + FRAMECOPY_FILL +} framecopy_type; + + +void ASE_Load( const char *filename, qboolean verbose, qboolean meshanims, int type ); +int ASE_GetNumSurfaces( void ); +polyset_t *ASE_GetSurfaceAnimation( int ndx, int *numFrames, int skipFrameStart, int skipFrameEnd, int maxFrames ); +const char *ASE_GetSurfaceName( int ndx ); +void ASE_Free( void ); + +void ASE_InitForGrabbing(void); +qboolean ASE_CatGrabbedFrames(const int nSourceFrame, int nDestFrame, int nNumFrames, const int nFillFrame); +void ASE_FreeGrab(void); +void ASE_CopyFromConcatBuffer(void); diff --git a/utils/common/bspfile.c b/utils/common/bspfile.c new file mode 100644 index 0000000..238f89e --- /dev/null +++ b/utils/common/bspfile.c @@ -0,0 +1,531 @@ + +#include "cmdlib.h" +#include "mathlib.h" +#include "bspfile.h" +#include "scriplib.h" + +void GetLeafNums (void); + +//============================================================================= + +int nummodels; +dmodel_t dmodels[MAX_MAP_MODELS]; + +int numShaders; +dshader_t dshaders[MAX_MAP_SHADERS]; + +int entdatasize; +char dentdata[MAX_MAP_ENTSTRING]; + +int numleafs; +dleaf_t dleafs[MAX_MAP_LEAFS]; + +int numplanes; +dplane_t dplanes[MAX_MAP_PLANES]; + +int numnodes; +dnode_t dnodes[MAX_MAP_NODES]; + +int numleafsurfaces; +int dleafsurfaces[MAX_MAP_LEAFFACES]; + +int numleafbrushes; +int dleafbrushes[MAX_MAP_LEAFBRUSHES]; + +int numbrushes; +dbrush_t dbrushes[MAX_MAP_BRUSHES]; + +int numbrushsides; +dbrushside_t dbrushsides[MAX_MAP_BRUSHSIDES]; + +int numLightBytes; +byte lightBytes[MAX_MAP_LIGHTING]; + +int numGridPoints; +byte gridData[MAX_MAP_LIGHTGRID]; + +int numVisBytes; +byte visBytes[MAX_MAP_VISIBILITY]; + +int numDrawVerts; +drawVert_t drawVerts[MAX_MAP_DRAW_VERTS]; + +int numDrawIndexes; +int drawIndexes[MAX_MAP_DRAW_INDEXES]; + +int numDrawSurfaces; +dsurface_t drawSurfaces[MAX_MAP_DRAW_SURFS]; + +int numFogs; +dfog_t dfogs[MAX_MAP_FOGS]; + +//============================================================================= + +/* +============= +SwapBlock + +If all values are 32 bits, this can be used to swap everything +============= +*/ +void SwapBlock( int *block, int sizeOfBlock ) { + int i; + + sizeOfBlock >>= 2; + for ( i = 0 ; i < sizeOfBlock ; i++ ) { + block[i] = LittleLong( block[i] ); + } +} + +/* +============= +SwapBSPFile + +Byte swaps all data in a bsp file. +============= +*/ +void SwapBSPFile( void ) { + int i; + + // models + SwapBlock( (int *)dmodels, nummodels * sizeof( dmodels[0] ) ); + + // shaders (don't swap the name) + for ( i = 0 ; i < numShaders ; i++ ) { + dshaders[i].contentFlags = LittleLong( dshaders[i].contentFlags ); + dshaders[i].surfaceFlags = LittleLong( dshaders[i].surfaceFlags ); + } + + // planes + SwapBlock( (int *)dplanes, numplanes * sizeof( dplanes[0] ) ); + + // nodes + SwapBlock( (int *)dnodes, numnodes * sizeof( dnodes[0] ) ); + + // leafs + SwapBlock( (int *)dleafs, numleafs * sizeof( dleafs[0] ) ); + + // leaffaces + SwapBlock( (int *)dleafsurfaces, numleafsurfaces * sizeof( dleafsurfaces[0] ) ); + + // leafbrushes + SwapBlock( (int *)dleafbrushes, numleafbrushes * sizeof( dleafbrushes[0] ) ); + + // brushes + SwapBlock( (int *)dbrushes, numbrushes * sizeof( dbrushes[0] ) ); + + // brushsides + SwapBlock( (int *)dbrushsides, numbrushsides * sizeof( dbrushsides[0] ) ); + + // vis + ((int *)&visBytes)[0] = LittleLong( ((int *)&visBytes)[0] ); + ((int *)&visBytes)[1] = LittleLong( ((int *)&visBytes)[1] ); + + // drawverts (don't swap colors ) + for ( i = 0 ; i < numDrawVerts ; i++ ) { + drawVerts[i].lightmap[0] = LittleFloat( drawVerts[i].lightmap[0] ); + drawVerts[i].lightmap[1] = LittleFloat( drawVerts[i].lightmap[1] ); + drawVerts[i].st[0] = LittleFloat( drawVerts[i].st[0] ); + drawVerts[i].st[1] = LittleFloat( drawVerts[i].st[1] ); + drawVerts[i].xyz[0] = LittleFloat( drawVerts[i].xyz[0] ); + drawVerts[i].xyz[1] = LittleFloat( drawVerts[i].xyz[1] ); + drawVerts[i].xyz[2] = LittleFloat( drawVerts[i].xyz[2] ); + drawVerts[i].normal[0] = LittleFloat( drawVerts[i].normal[0] ); + drawVerts[i].normal[1] = LittleFloat( drawVerts[i].normal[1] ); + drawVerts[i].normal[2] = LittleFloat( drawVerts[i].normal[2] ); + } + + // drawindexes + SwapBlock( (int *)drawIndexes, numDrawIndexes * sizeof( drawIndexes[0] ) ); + + // drawsurfs + SwapBlock( (int *)drawSurfaces, numDrawSurfaces * sizeof( drawSurfaces[0] ) ); + + // fogs + for ( i = 0 ; i < numFogs ; i++ ) { + dfogs[i].brushNum = LittleLong( dfogs[i].brushNum ); + dfogs[i].visibleSide = LittleLong( dfogs[i].visibleSide ); + } +} + + + +/* +============= +CopyLump +============= +*/ +int CopyLump( dheader_t *header, int lump, void *dest, int size ) { + int length, ofs; + + length = header->lumps[lump].filelen; + ofs = header->lumps[lump].fileofs; + + if ( length % size ) { + Error ("LoadBSPFile: odd lump size"); + } + + memcpy( dest, (byte *)header + ofs, length ); + + return length / size; +} + +/* +============= +LoadBSPFile +============= +*/ +void LoadBSPFile( const char *filename ) { + dheader_t *header; + + // load the file header + LoadFile (filename, (void **)&header); + + // swap the header + SwapBlock( (int *)header, sizeof(*header) ); + + if ( header->ident != BSP_IDENT ) { + Error( "%s is not a IBSP file", filename ); + } + if ( header->version != BSP_VERSION ) { + Error( "%s is version %i, not %i", filename, header->version, BSP_VERSION ); + } + + numShaders = CopyLump( header, LUMP_SHADERS, dshaders, sizeof(dshader_t) ); + nummodels = CopyLump( header, LUMP_MODELS, dmodels, sizeof(dmodel_t) ); + numplanes = CopyLump( header, LUMP_PLANES, dplanes, sizeof(dplane_t) ); + numleafs = CopyLump( header, LUMP_LEAFS, dleafs, sizeof(dleaf_t) ); + numnodes = CopyLump( header, LUMP_NODES, dnodes, sizeof(dnode_t) ); + numleafsurfaces = CopyLump( header, LUMP_LEAFSURFACES, dleafsurfaces, sizeof(dleafsurfaces[0]) ); + numleafbrushes = CopyLump( header, LUMP_LEAFBRUSHES, dleafbrushes, sizeof(dleafbrushes[0]) ); + numbrushes = CopyLump( header, LUMP_BRUSHES, dbrushes, sizeof(dbrush_t) ); + numbrushsides = CopyLump( header, LUMP_BRUSHSIDES, dbrushsides, sizeof(dbrushside_t) ); + numDrawVerts = CopyLump( header, LUMP_DRAWVERTS, drawVerts, sizeof(drawVert_t) ); + numDrawSurfaces = CopyLump( header, LUMP_SURFACES, drawSurfaces, sizeof(dsurface_t) ); + numFogs = CopyLump( header, LUMP_FOGS, dfogs, sizeof(dfog_t) ); + numDrawIndexes = CopyLump( header, LUMP_DRAWINDEXES, drawIndexes, sizeof(drawIndexes[0]) ); + + numVisBytes = CopyLump( header, LUMP_VISIBILITY, visBytes, 1 ); + numLightBytes = CopyLump( header, LUMP_LIGHTMAPS, lightBytes, 1 ); + entdatasize = CopyLump( header, LUMP_ENTITIES, dentdata, 1); + + numGridPoints = CopyLump( header, LUMP_LIGHTGRID, gridData, 8 ); + + + free( header ); // everything has been copied out + + // swap everything + SwapBSPFile(); +} + + +//============================================================================ + +/* +============= +AddLump +============= +*/ +void AddLump( FILE *bspfile, dheader_t *header, int lumpnum, const void *data, int len ) { + lump_t *lump; + + lump = &header->lumps[lumpnum]; + + lump->fileofs = LittleLong( ftell(bspfile) ); + lump->filelen = LittleLong( len ); + SafeWrite( bspfile, data, (len+3)&~3 ); +} + +/* +============= +WriteBSPFile + +Swaps the bsp file in place, so it should not be referenced again +============= +*/ +void WriteBSPFile( const char *filename ) { + dheader_t outheader, *header; + FILE *bspfile; + + header = &outheader; + memset( header, 0, sizeof(dheader_t) ); + + SwapBSPFile(); + + header->ident = LittleLong( BSP_IDENT ); + header->version = LittleLong( BSP_VERSION ); + + bspfile = SafeOpenWrite( filename ); + SafeWrite( bspfile, header, sizeof(dheader_t) ); // overwritten later + + AddLump( bspfile, header, LUMP_SHADERS, dshaders, numShaders*sizeof(dshader_t) ); + AddLump( bspfile, header, LUMP_PLANES, dplanes, numplanes*sizeof(dplane_t) ); + AddLump( bspfile, header, LUMP_LEAFS, dleafs, numleafs*sizeof(dleaf_t) ); + AddLump( bspfile, header, LUMP_NODES, dnodes, numnodes*sizeof(dnode_t) ); + AddLump( bspfile, header, LUMP_BRUSHES, dbrushes, numbrushes*sizeof(dbrush_t) ); + AddLump( bspfile, header, LUMP_BRUSHSIDES, dbrushsides, numbrushsides*sizeof(dbrushside_t) ); + AddLump( bspfile, header, LUMP_LEAFSURFACES, dleafsurfaces, numleafsurfaces*sizeof(dleafsurfaces[0]) ); + AddLump( bspfile, header, LUMP_LEAFBRUSHES, dleafbrushes, numleafbrushes*sizeof(dleafbrushes[0]) ); + AddLump( bspfile, header, LUMP_MODELS, dmodels, nummodels*sizeof(dmodel_t) ); + AddLump( bspfile, header, LUMP_DRAWVERTS, drawVerts, numDrawVerts*sizeof(drawVert_t) ); + AddLump( bspfile, header, LUMP_SURFACES, drawSurfaces, numDrawSurfaces*sizeof(dsurface_t) ); + AddLump( bspfile, header, LUMP_VISIBILITY, visBytes, numVisBytes ); + AddLump( bspfile, header, LUMP_LIGHTMAPS, lightBytes, numLightBytes ); + AddLump( bspfile, header, LUMP_LIGHTGRID, gridData, 8 * numGridPoints ); + AddLump( bspfile, header, LUMP_ENTITIES, dentdata, entdatasize ); + AddLump( bspfile, header, LUMP_FOGS, dfogs, numFogs * sizeof(dfog_t) ); + AddLump( bspfile, header, LUMP_DRAWINDEXES, drawIndexes, numDrawIndexes * sizeof(drawIndexes[0]) ); + + fseek (bspfile, 0, SEEK_SET); + SafeWrite (bspfile, header, sizeof(dheader_t)); + fclose (bspfile); +} + +//============================================================================ + +/* +============= +PrintBSPFileSizes + +Dumps info about current file +============= +*/ +void PrintBSPFileSizes( void ) { + if ( !num_entities ) { + ParseEntities(); + } + + printf ("%6i models %7i\n" ,nummodels, (int)(nummodels*sizeof(dmodel_t))); + printf ("%6i shaders %7i\n" ,numShaders, (int)(numShaders*sizeof(dshader_t))); + printf ("%6i brushes %7i\n" ,numbrushes, (int)(numbrushes*sizeof(dbrush_t))); + printf ("%6i brushsides %7i\n" ,numbrushsides, (int)(numbrushsides*sizeof(dbrushside_t))); + printf ("%6i fogs %7i\n" ,numFogs, (int)(numFogs*sizeof(dfog_t))); + printf ("%6i planes %7i\n" ,numplanes, (int)(numplanes*sizeof(dplane_t))); + printf ("%6i entdata %7i\n", num_entities, entdatasize); + + printf ("\n"); + + printf ("%6i nodes %7i\n" ,numnodes, (int)(numnodes*sizeof(dnode_t))); + printf ("%6i leafs %7i\n" ,numleafs, (int)(numleafs*sizeof(dleaf_t))); + printf ("%6i leafsurfaces %7i\n" ,numleafsurfaces, (int)(numleafsurfaces*sizeof(dleafsurfaces[0]))); + printf ("%6i leafbrushes %7i\n" ,numleafbrushes, (int)(numleafbrushes*sizeof(dleafbrushes[0]))); + printf ("%6i drawverts %7i\n" ,numDrawVerts, (int)(numDrawVerts*sizeof(drawVerts[0]))); + printf ("%6i drawindexes %7i\n" ,numDrawIndexes, (int)(numDrawIndexes*sizeof(drawIndexes[0]))); + printf ("%6i drawsurfaces %7i\n" ,numDrawSurfaces, (int)(numDrawSurfaces*sizeof(drawSurfaces[0]))); + + printf ("%6i lightmaps %7i\n" ,numLightBytes / (LIGHTMAP_WIDTH*LIGHTMAP_HEIGHT*3), numLightBytes ); + printf (" visibility %7i\n" , numVisBytes ); + printf (" lightgrid %7i\n" , 8 * numGridPoints ); + +} + + +//============================================ + +int num_entities; +entity_t entities[MAX_MAP_ENTITIES]; + +void StripTrailing( char *e ) { + char *s; + + s = e + strlen(e)-1; + while (s >= e && *s <= 32) + { + *s = 0; + s--; + } +} + +/* +================= +ParseEpair +================= +*/ +epair_t *ParseEpair( void ) { + epair_t *e; + + e = malloc( sizeof(epair_t) ); + memset( e, 0, sizeof(epair_t) ); + + if ( strlen(token) >= MAX_KEY-1 ) { + Error ("ParseEpar: token too long"); + } + e->key = copystring( token ); + GetToken( qfalse ); + if ( strlen(token) >= MAX_VALUE-1 ) { + Error ("ParseEpar: token too long"); + } + e->value = copystring( token ); + + // strip trailing spaces that sometimes get accidentally + // added in the editor + StripTrailing( e->key ); + StripTrailing( e->value ); + + return e; +} + + +/* +================ +ParseEntity +================ +*/ +qboolean ParseEntity( void ) { + epair_t *e; + entity_t *mapent; + + if ( !GetToken (qtrue) ) { + return qfalse; + } + + if ( strcmp (token, "{") ) { + Error ("ParseEntity: { not found"); + } + if ( num_entities == MAX_MAP_ENTITIES ) { + Error ("num_entities == MAX_MAP_ENTITIES"); + } + mapent = &entities[num_entities]; + num_entities++; + + do { + if ( !GetToken (qtrue) ) { + Error ("ParseEntity: EOF without closing brace"); + } + if ( !strcmp (token, "}") ) { + break; + } + e = ParseEpair (); + e->next = mapent->epairs; + mapent->epairs = e; + } while (1); + + return qtrue; +} + +/* +================ +ParseEntities + +Parses the dentdata string into entities +================ +*/ +void ParseEntities( void ) { + num_entities = 0; + ParseFromMemory( dentdata, entdatasize ); + + while ( ParseEntity () ) { + } +} + + +/* +================ +UnparseEntities + +Generates the dentdata string from all the entities +This allows the utilities to add or remove key/value pairs +to the data created by the map editor. +================ +*/ +void UnparseEntities( void ) { + char *buf, *end; + epair_t *ep; + char line[2048]; + int i; + char key[1024], value[1024]; + + buf = dentdata; + end = buf; + *end = 0; + + for (i=0 ; inext ) { + strcpy (key, ep->key); + StripTrailing (key); + strcpy (value, ep->value); + StripTrailing (value); + + sprintf (line, "\"%s\" \"%s\"\n", key, value); + strcat (end, line); + end += strlen(line); + } + strcat (end,"}\n"); + end += 2; + + if (end > buf + MAX_MAP_ENTSTRING) { + Error ("Entity text too long"); + } + } + entdatasize = end - buf + 1; +} + +void PrintEntity( const entity_t *ent ) { + epair_t *ep; + + printf ("------- entity %p -------\n", ent); + for (ep=ent->epairs ; ep ; ep=ep->next) { + printf( "%s = %s\n", ep->key, ep->value ); + } + +} + +void SetKeyValue( entity_t *ent, const char *key, const char *value ) { + epair_t *ep; + + for ( ep=ent->epairs ; ep ; ep=ep->next ) { + if ( !strcmp (ep->key, key) ) { + free (ep->value); + ep->value = copystring(value); + return; + } + } + ep = malloc (sizeof(*ep)); + ep->next = ent->epairs; + ent->epairs = ep; + ep->key = copystring(key); + ep->value = copystring(value); +} + +const char *ValueForKey( const entity_t *ent, const char *key ) { + epair_t *ep; + + for (ep=ent->epairs ; ep ; ep=ep->next) { + if (!strcmp (ep->key, key) ) { + return ep->value; + } + } + return ""; +} + +vec_t FloatForKey( const entity_t *ent, const char *key ) { + const char *k; + + k = ValueForKey( ent, key ); + return atof(k); +} + +qboolean GetVectorForKey( const entity_t *ent, const char *key, vec3_t vec ) { + const char *k; + double v1, v2, v3; + + k = ValueForKey (ent, key); + + // scanf into doubles, then assign, so it is vec_t size independent + v1 = v2 = v3 = 0; + sscanf (k, "%lf %lf %lf", &v1, &v2, &v3); + vec[0] = v1; + vec[1] = v2; + vec[2] = v3; + + return !!strlen(k); +} + diff --git a/utils/common/bspfile.h b/utils/common/bspfile.h new file mode 100644 index 0000000..fb40be6 --- /dev/null +++ b/utils/common/bspfile.h @@ -0,0 +1,93 @@ + +#include "qfiles.h" +#include "surfaceflags.h" + +extern int nummodels; +extern dmodel_t dmodels[MAX_MAP_MODELS]; + +extern int numShaders; +extern dshader_t dshaders[MAX_MAP_MODELS]; + +extern int entdatasize; +extern char dentdata[MAX_MAP_ENTSTRING]; + +extern int numleafs; +extern dleaf_t dleafs[MAX_MAP_LEAFS]; + +extern int numplanes; +extern dplane_t dplanes[MAX_MAP_PLANES]; + +extern int numnodes; +extern dnode_t dnodes[MAX_MAP_NODES]; + +extern int numleafsurfaces; +extern int dleafsurfaces[MAX_MAP_LEAFFACES]; + +extern int numleafbrushes; +extern int dleafbrushes[MAX_MAP_LEAFBRUSHES]; + +extern int numbrushes; +extern dbrush_t dbrushes[MAX_MAP_BRUSHES]; + +extern int numbrushsides; +extern dbrushside_t dbrushsides[MAX_MAP_BRUSHSIDES]; + +extern int numLightBytes; +extern byte lightBytes[MAX_MAP_LIGHTING]; + +extern int numGridPoints; +extern byte gridData[MAX_MAP_LIGHTGRID]; + +extern int numVisBytes; +extern byte visBytes[MAX_MAP_VISIBILITY]; + +extern int numDrawVerts; +extern drawVert_t drawVerts[MAX_MAP_DRAW_VERTS]; + +extern int numDrawIndexes; +extern int drawIndexes[MAX_MAP_DRAW_INDEXES]; + +extern int numDrawSurfaces; +extern dsurface_t drawSurfaces[MAX_MAP_DRAW_SURFS]; + +extern int numFogs; +extern dfog_t dfogs[MAX_MAP_FOGS]; + +void LoadBSPFile( const char *filename ); +void WriteBSPFile( const char *filename ); +void PrintBSPFileSizes( void ); + +//=============== + + +typedef struct epair_s { + struct epair_s *next; + char *key; + char *value; +} epair_t; + +typedef struct { + vec3_t origin; + struct bspbrush_s *brushes; + struct parseMesh_s *patches; + int firstDrawSurf; + epair_t *epairs; +} entity_t; + +extern int num_entities; +extern entity_t entities[MAX_MAP_ENTITIES]; + +void ParseEntities( void ); +void UnparseEntities( void ); + +void SetKeyValue( entity_t *ent, const char *key, const char *value ); +const char *ValueForKey( const entity_t *ent, const char *key ); +// will return "" if not present + +vec_t FloatForKey( const entity_t *ent, const char *key ); +qboolean GetVectorForKey( const entity_t *ent, const char *key, vec3_t vec ); + +epair_t *ParseEpair( void ); + +void PrintEntity( const entity_t *ent ); + diff --git a/utils/common/cmdlib.c b/utils/common/cmdlib.c new file mode 100644 index 0000000..889d673 --- /dev/null +++ b/utils/common/cmdlib.c @@ -0,0 +1,1261 @@ +// cmdlib.c + +#include "cmdlib.h" +#include +#include +#ifdef Q3DATA +#include "conio.h" +extern qboolean gbKeyPress; +#endif + + +#ifdef WIN32 +#include +#endif + +#ifdef NeXT +#include +#endif + +#define BASEDIRNAME "base" // important that these 2 are in this order!!!! +#define BASEDIRNAME2 "game" // +#define PATHSEPERATOR '/' + +// set these before calling CheckParm +int myargc; +char **myargv; + +char com_token[1024]; +qboolean com_eof; + +qboolean archive; +char archivedir[1024]; +char baseDirName[64] = {BASEDIRNAME}; + +void setQDir( const char* newName) +{ + strncpy(baseDirName,newName, sizeof(baseDirName)); +} + +/* +=================== +ExpandWildcards + +Mimic unix command line expansion +=================== +*/ +#define MAX_EX_ARGC 1024 +int ex_argc; +char *ex_argv[MAX_EX_ARGC]; +#ifdef _WIN32 +#include "io.h" +void ExpandWildcards( int *argc, char ***argv ) +{ + struct _finddata_t fileinfo; + int handle; + int i; + char filename[1024]; + char filebase[1024]; + char *path; + + ex_argc = 0; + for (i=0 ; i<*argc ; i++) + { + path = (*argv)[i]; + if ( path[0] == '-' + || ( !strstr(path, "*") && !strstr(path, "?") ) ) + { + ex_argv[ex_argc++] = path; + continue; + } + + handle = _findfirst (path, &fileinfo); + if (handle == -1) + return; + + ExtractFilePath (path, filebase); + + do + { + sprintf (filename, "%s%s", filebase, fileinfo.name); + ex_argv[ex_argc++] = copystring (filename); + } while (_findnext( handle, &fileinfo ) != -1); + + _findclose (handle); + } + + *argc = ex_argc; + *argv = ex_argv; +} +#else +void ExpandWildcards (int *argc, char ***argv) +{ +} +#endif + +#ifdef WIN_ERROR +#include +/* +================= +Error + +For abnormal program terminations in windowed apps +================= +*/ +void Error( const char *error, ... ) +{ + va_list argptr; + char text[1024]; + char text2[1024]; + int err; + + err = GetLastError (); + + va_start (argptr,error); + vsprintf (text, error,argptr); + va_end (argptr); + + sprintf (text2, "%s\nGetLastError() = %i", text, err); + MessageBox(NULL, text2, "Error", 0 /* MB_OK */ ); + + exit (1); +} + +#else +/* +================= +Error + +For abnormal program terminations in console apps +================= +*/ +void Error( const char *error, ...) +{ + va_list argptr; + + printf ("\n************ ERROR ************\n"); + + va_start (argptr,error); + vprintf (error,argptr); + va_end (argptr); + printf ("\n"); + +#ifdef Q3DATA + if (gbKeyPress) + { + printf("\nPress any key...\n\n"); + getch(); + } +#endif + + exit (1); +} +#endif + +// only printf if in verbose mode +qboolean verbose = qfalse; +void qprintf( const char *format, ... ) { + va_list argptr; + + if (!verbose) + return; + + va_start (argptr,format); + vprintf (format,argptr); + va_end (argptr); +} + + +/* + +qdir will hold the path up to the quake directory, including the slash + + f:\quake\ + /raid/quake/ + +gamedir will hold qdir + the game directory (id1, id2, etc) + + */ + +char qdir[1024]; +char gamedir[1024]; +char writedir[1024]; + +static qboolean SetQdirFromPath2( const char *path, const char *psBaseDir ) +{ + const char *c; + const char *sep; + int len, count; + char temp[1024]; + + if (!(path[0] == '/' || path[0] == '\\' || path[1] == ':')) + { // path is partial + Q_getwd (temp); + strcat (temp, path); + path = temp; + } + else + { + strcpy(temp,path); + path = temp; + } + + + _strlwr((char*)path); + + // search for "base" in path from the RIGHT hand side (and must have a [back]slash just before it) + + len = strlen(psBaseDir); + for (c=path+strlen(path)-1 ; c != path ; c--) + { + int i; + + if (!strnicmp (c, psBaseDir, len) + && + (*(c-1) == '/' || *(c-1) == '\\') // would be more efficient to do this first, but only checking after a strncasecmp ok ensures no invalid pointer-1 access + ) + { + sep = c + len; + count = 1; + while (*sep && *sep != '/' && *sep != '\\') + { + sep++; + count++; + } + strncpy (gamedir, path, c+len+count-path); + gamedir[c+len+count-path]='\0'; + for ( i = 0; i < (int)strlen( gamedir ); i++ ) + { + if ( gamedir[i] == '\\' ) + gamedir[i] = '/'; + } +// qprintf ("gamedir: %s\n", gamedir); + + strncpy (qdir, path, c-path); + qdir[c-path] = 0; + for ( i = 0; i < (int)strlen( qdir ); i++ ) + { + if ( qdir[i] == '\\' ) + qdir[i] = '/'; + } +// qprintf ("qdir: %s\n", qdir); + + if ( !writedir[0] ) + strcpy( writedir, gamedir ); + else if ( writedir[strlen( writedir )-1] != '/' ) + { + writedir[strlen( writedir )] = '/'; + writedir[strlen( writedir )+1] = 0; + } + + + return qtrue; + } + } +// Error ("SetQdirFromPath: no '%s' in %s", BASEDIRNAME, path); + return qfalse; +} + + +/* +static qboolean SetQdirFromPath2( const char *path, const char *psBaseDirName ) +{ + char temp[1024]; + const char *c; + const char *sep; + int len, count; + + if (!(path[0] == '/' || path[0] == '\\' || path[1] == ':')) + { // path is partial + Q_getwd (temp); + strcat (temp, path); + path = temp; + } + + _strlwr((char*)path); + // search for "quake2" in path + + len = strlen(psBaseDirName); + for (c=path+strlen(path)-1 ; c != path ; c--) + { + int i; + + if (!Q_strncasecmp (c, psBaseDirName, len)) + { + // strncpy (qdir, path, c+len+2-path); + // the +2 assumes a 2 or 3 following quake which is not the + // case with a retail install + // so we need to add up how much to the next separator + sep = c + len; + count = 1; + while (*sep && *sep != '/' && *sep != '\\') + { + sep++; + count++; + } + strncpy (qdir, path, c+len+count-path); + qprintf ("qdir: %s\n", qdir); + for ( i = 0; i < strlen( qdir ); i++ ) + { + if ( qdir[i] == '\\' ) + qdir[i] = '/'; + } + + c += len+count; + while (*c) + { + if (*c == '/' || *c == '\\') + { + strncpy (gamedir, path, c+1-path); + + for ( i = 0; i < strlen( gamedir ); i++ ) + { + if ( gamedir[i] == '\\' ) + gamedir[i] = '/'; + } + + qprintf ("gamedir: %s\n", gamedir); + + if ( !writedir[0] ) + strcpy( writedir, gamedir ); + else if ( writedir[strlen( writedir )-1] != '/' ) + { + writedir[strlen( writedir )] = '/'; + writedir[strlen( writedir )+1] = 0; + } + + return qtrue; + } + c++; + } + Error ("No gamedir in %s", path); + return qfalse; + } + } +// Error ("SetQdirFromPath: no '%s' in %s\n(use -qdir X to change)", psBaseDirName, path); + return qfalse; +} +*/ +void SetQdirFromPath( const char *path ) +{ + if (SetQdirFromPath2( path, baseDirName ) // try software version, in case it was overridden + || + SetQdirFromPath2( path, BASEDIRNAME ) // ... else try the two hardwired ones + || + SetQdirFromPath2( path, BASEDIRNAME2 ) // actually, this last isn't needed inhouse, but will work for stuff like "game/demo" instead of "game/base" (I think). Whatever. + ) + { + return; + } + + // ... else give up... + // + Error("Unable to work out basedir from \"%s\"!\n",path); +} + +char *ExpandArg (const char *path) +{ + static char full[1024]; + + if (path[0] != '/' && path[0] != '\\' && path[1] != ':') + { + Q_getwd (full); + strcat (full, path); + } + else + strcpy (full, path); + return full; +} + +char *ExpandPath (const char *path) +{ + static char full[1024]; + if (!qdir) + Error ("ExpandPath called without qdir set"); + if (path[0] == '/' || path[0] == '\\' || path[1] == ':') { + strcpy( full, path ); + return full; + } + sprintf (full, "%s%s", qdir, path); + return full; +} + +char *ExpandGamePath (const char *path) +{ + static char full[1024]; + if (!qdir) + Error ("ExpandGamePath called without qdir set"); + if (path[0] == '/' || path[0] == '\\' || path[1] == ':') { + strcpy( full, path ); + return full; + } + sprintf (full, "%s%s", gamedir, path); + return full; +} + +char *ExpandPathAndArchive (const char *path) +{ + char *expanded; + char archivename[1024]; + + expanded = ExpandPath (path); + + if (archive) + { + sprintf (archivename, "%s/%s", archivedir, path); + QCopyFile (expanded, archivename); + } + return expanded; +} + + +char *copystring(const char *s) +{ + char *b; + b = malloc(strlen(s)+1); + strcpy (b, s); + return b; +} + + + +/* +================ +I_FloatTime +================ +*/ +double I_FloatTime (void) +{ + time_t t; + + time (&t); + + return t; +#if 0 +// more precise, less portable + struct timeval tp; + struct timezone tzp; + static int secbase; + + gettimeofday(&tp, &tzp); + + if (!secbase) + { + secbase = tp.tv_sec; + return tp.tv_usec/1000000.0; + } + + return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; +#endif +} + +void Q_getwd (char *out) +{ + int i = 0; + +#ifdef WIN32 + _getcwd (out, 256); + strcat (out, "\\"); +#else + getwd (out); + strcat (out, "/"); +#endif + + while ( out[i] != 0 ) + { + if ( out[i] == '\\' ) + out[i] = '/'; + i++; + } +} + + +void Q_mkdir (const char *path) +{ +#ifdef WIN32 + if (_mkdir (path) != -1) + return; +#else + if (mkdir (path, 0777) != -1) + return; +#endif + if (errno != EEXIST) + Error ("mkdir %s: %s",path, strerror(errno)); +} + +/* +============ +FileTime + +returns -1 if not present +============ +*/ +int FileTime (const char *path) +{ + struct stat buf; + + if (stat (path,&buf) == -1) + return -1; + + return buf.st_mtime; +} + + + +/* +============== +COM_Parse + +Parse a token out of a string +============== +*/ +char *COM_Parse (char *data) +{ + int c; + int len; + + len = 0; + com_token[0] = 0; + + if (!data) + return NULL; + +// skip whitespace +skipwhite: + while ( (c = *data) <= ' ') + { + if (c == 0) + { + com_eof = qtrue; + return NULL; // end of file; + } + data++; + } + +// skip // comments + if (c=='/' && data[1] == '/') + { + while (*data && *data != '\n') + data++; + goto skipwhite; + } + + +// handle quoted strings specially + if (c == '\"') + { + data++; + do + { + c = *data++; + if (c=='\"') + { + com_token[len] = 0; + return data; + } + com_token[len] = c; + len++; + } while (1); + } + +// parse single characters + if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') + { + com_token[len] = c; + len++; + com_token[len] = 0; + return data+1; + } + +// parse a regular word + do + { + com_token[len] = c; + data++; + len++; + c = *data; + if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') + break; + } while (c>32); + + com_token[len] = 0; + return data; +} + + +int Q_strncasecmp (const char *s1, const char *s2, int n) +{ + int c1, c2; + + do + { + c1 = *s1++; + c2 = *s2++; + + if (!n--) + return 0; // strings are equal until end point + + if (c1 != c2) + { + if (c1 >= 'a' && c1 <= 'z') + c1 -= ('a' - 'A'); + if (c2 >= 'a' && c2 <= 'z') + c2 -= ('a' - 'A'); + if (c1 != c2) + return -1; // strings not equal + } + } while (c1); + + return 0; // strings are equal +} + +int Q_stricmp (const char *s1, const char *s2) +{ + return Q_strncasecmp (s1, s2, 99999); +} + + +char *strupr (char *start) +{ + char *in; + in = start; + while (*in) + { + *in = toupper(*in); + in++; + } + return start; +} + +char *strlower (char *start) +{ + char *in; + in = start; + while (*in) + { + *in = tolower(*in); + in++; + } + return start; +} + + +/* +============================================================================= + + MISC FUNCTIONS + +============================================================================= +*/ + + +/* +================= +CheckParm + +Checks for the given parameter in the program's command line arguments +Returns the argument number (1 to argc-1) or 0 if not present +================= +*/ +int CheckParm (const char *check) +{ + int i; + + for (i = 1;i 0) { + nAllocSize += MEM_BLOCKSIZE - nBlock; + } + buffer = malloc (nAllocSize+1); + memset(buffer, 0, nAllocSize+1); + SafeRead (f, buffer, length); + fclose (f); + + *bufferptr = buffer; + return length; +} + + +/* +============== +TryLoadFile + +Allows failure +============== +*/ +int TryLoadFile (const char *filename, void **bufferptr) +{ + FILE *f; + int length; + void *buffer; + + *bufferptr = NULL; + + f = fopen (filename, "rb"); + if (!f) + return -1; + length = Q_filelength (f); + buffer = malloc (length+1); + ((char *)buffer)[length] = 0; + SafeRead (f, buffer, length); + fclose (f); + + *bufferptr = buffer; + return length; +} + + +/* +============== +SaveFile +============== +*/ +void SaveFile (const char *filename, const void *buffer, int count) +{ + FILE *f; + + f = SafeOpenWrite (filename); + SafeWrite (f, buffer, count); + fclose (f); +} + + + +void DefaultExtension (char *path, const char *extension) +{ + char *src; +// +// if path doesnt have a .EXT, append extension +// (extension should include the .) +// + src = path + strlen(path) - 1; + + while (*src != '/' && *src != '\\' && src != path) + { + if (*src == '.') + return; // it has an extension + src--; + } + + strcat (path, extension); +} + + +void DefaultPath (char *path, const char *basepath) +{ + char temp[128]; + + if (path[0] == PATHSEPERATOR) + return; // absolute path location + strcpy (temp,path); + strcpy (path,basepath); + strcat (path,temp); +} + + +void StripFilename (char *path) +{ + int length; + + length = strlen(path)-1; + while (length > 0 && path[length] != PATHSEPERATOR) + length--; + path[length] = 0; +} + +void StripExtension (char *path) +{ + int length; + + length = strlen(path)-1; + while (length > 0 && path[length] != '.') + { + length--; + if (path[length] == '/') + return; // no extension + } + if (length) + path[length] = 0; +} + + +/* +==================== +Extract file parts +==================== +*/ +// FIXME: should include the slash, otherwise +// backing to an empty path will be wrong when appending a slash +void ExtractFilePath (const char *path, char *dest) +{ + const char *src; + + src = path + strlen(path) - 1; + +// +// back up until a \ or the start +// + while (src != path && *(src-1) != '\\' && *(src-1) != '/') + src--; + + memcpy (dest, path, src-path); + dest[src-path] = 0; +} + +void ExtractFileBase (const char *path, char *dest) +{ + const char *src; + + src = path + strlen(path) - 1; + +// +// back up until a \ or the start +// +// while (src != path && *(src-1) != PATHSEPERATOR) + while (src != path && *(src-1) != '\\' && *(src-1) != '/') + src--; + + while (*src && *src != '.') + { + *dest++ = *src++; + } + *dest = 0; +} + +void ExtractFileExtension (const char *path, char *dest) +{ + const char *src; + + src = path + strlen(path) - 1; + +// +// back up until a . or the start +// + while (src != path && *(src-1) != '.') + src--; + if (src == path) + { + *dest = 0; // no extension + return; + } + + strcpy (dest,src); +} + + +/* +============== +ParseNum / ParseHex +============== +*/ +int ParseHex (const char *hex) +{ + const char *str; + int num; + + num = 0; + str = hex; + + while (*str) + { + num <<= 4; + if (*str >= '0' && *str <= '9') + num += *str-'0'; + else if (*str >= 'a' && *str <= 'f') + num += 10 + *str-'a'; + else if (*str >= 'A' && *str <= 'F') + num += 10 + *str-'A'; + else + Error ("Bad hex number: %s",hex); + str++; + } + + return num; +} + + +int ParseNum (const char *str) +{ + if (str[0] == '$') + return ParseHex (str+1); + if (str[0] == '0' && str[1] == 'x') + return ParseHex (str+2); + return atol (str); +} + + + +/* +============================================================================ + + BYTE ORDER FUNCTIONS + +============================================================================ +*/ + +#ifdef _SGI_SOURCE +#define __BIG_ENDIAN__ +#endif + +#ifdef __BIG_ENDIAN__ + +short LittleShort (short l) +{ + byte b1,b2; + + b1 = l&255; + b2 = (l>>8)&255; + + return (b1<<8) + b2; +} + +short BigShort (short l) +{ + return l; +} + + +int LittleLong (int l) +{ + byte b1,b2,b3,b4; + + b1 = l&255; + b2 = (l>>8)&255; + b3 = (l>>16)&255; + b4 = (l>>24)&255; + + return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; +} + +int BigLong (int l) +{ + return l; +} + + +float LittleFloat (float l) +{ + union {byte b[4]; float f;} in, out; + + in.f = l; + out.b[0] = in.b[3]; + out.b[1] = in.b[2]; + out.b[2] = in.b[1]; + out.b[3] = in.b[0]; + + return out.f; +} + +float BigFloat (float l) +{ + return l; +} + + +#else + + +short BigShort (short l) +{ + byte b1,b2; + + b1 = l&255; + b2 = (l>>8)&255; + + return (b1<<8) + b2; +} + +short LittleShort (short l) +{ + return l; +} + + +int BigLong (int l) +{ + byte b1,b2,b3,b4; + + b1 = l&255; + b2 = (l>>8)&255; + b3 = (l>>16)&255; + b4 = (l>>24)&255; + + return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; +} + +int LittleLong (int l) +{ + return l; +} + +float BigFloat (float l) +{ + union {byte b[4]; float f;} in, out; + + in.f = l; + out.b[0] = in.b[3]; + out.b[1] = in.b[2]; + out.b[2] = in.b[1]; + out.b[3] = in.b[0]; + + return out.f; +} + +float LittleFloat (float l) +{ + return l; +} + + +#endif + + +//======================================================= + + +// FIXME: byte swap? + +// this is a 16 bit, non-reflected CRC using the polynomial 0x1021 +// and the initial and final xor values shown below... in other words, the +// CCITT standard CRC used by XMODEM + +#define CRC_INIT_VALUE 0xffff +#define CRC_XOR_VALUE 0x0000 + +static unsigned short crctable[256] = +{ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; + +void CRC_Init(unsigned short *crcvalue) +{ + *crcvalue = CRC_INIT_VALUE; +} + +void CRC_ProcessByte(unsigned short *crcvalue, byte data) +{ + *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data]; +} + +unsigned short CRC_Value(unsigned short crcvalue) +{ + return crcvalue ^ CRC_XOR_VALUE; +} +//============================================================================= + +/* +============ +CreatePath +============ +*/ +void CreatePath (const char *path) +{ + const char *ofs; + char c; + char dir[1024]; + +#ifdef _WIN32 + int olddrive = -1; + + if ( path[1] == ':' ) + { + olddrive = _getdrive(); + _chdrive( toupper( path[0] ) - 'A' + 1 ); + } +#endif + + if (path[1] == ':') + path += 2; + + for (ofs = path+1 ; *ofs ; ofs++) + { + c = *ofs; + if (c == '/' || c == '\\') + { // create the directory + memcpy( dir, path, ofs - path ); + dir[ ofs - path ] = 0; + Q_mkdir( dir ); + } + } + +#ifdef _WIN32 + if ( olddrive != -1 ) + { + _chdrive( olddrive ); + } +#endif +} + + +/* +============ +QCopyFile + + Used to archive source files +============ +*/ +void QCopyFile (const char *from, const char *to) +{ + void *buffer; + int length; + + length = LoadFile (from, &buffer); + CreatePath (to); + SaveFile (to, buffer, length); + free (buffer); +} diff --git a/utils/common/cmdlib.h b/utils/common/cmdlib.h new file mode 100644 index 0000000..e69d74e --- /dev/null +++ b/utils/common/cmdlib.h @@ -0,0 +1,157 @@ +// cmdlib.h + +#ifndef __CMDLIB__ +#define __CMDLIB__ + +#ifdef _WIN32 +#pragma warning(disable : 4244) // MIPS +#pragma warning(disable : 4136) // X86 +#pragma warning(disable : 4051) // ALPHA + +#pragma warning(disable : 4018) // signed/unsigned mismatch +#pragma warning(disable : 4305) // truncate from double to float +#pragma warning(disable : 4273) // inconsistent dll linkage. dllexport assumed. (some shite that appeared when importing Carcass code into Assimilate) + +#pragma check_stack(off) +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 + +#pragma intrinsic( memset, memcpy ) + +#endif + +#ifndef __BYTEBOOL__ +#define __BYTEBOOL__ +typedef enum { qfalse, qtrue } qboolean; +typedef unsigned char byte; +#endif + +#define MAX_OS_PATH 1024 +#define MEM_BLOCKSIZE 4096 + +// the dec offsetof macro doesnt work very well... +#define myoffsetof(type,identifier) ((size_t)&((type *)0)->identifier) + + +// set these before calling CheckParm +extern int myargc; +extern char **myargv; + +char *strupr (char *in); +char *strlower (char *in); +int Q_strncasecmp( const char *s1, const char *s2, int n ); +int Q_stricmp( const char *s1, const char *s2 ); +void Q_getwd( char *out ); + +int Q_filelength (FILE *f); +int FileTime( const char *path ); + +void Q_mkdir( const char *path ); + +#ifdef __cplusplus +extern "C" +{ +#endif + extern char qdir[1024]; + extern char gamedir[1024]; + extern char writedir[1024]; +#ifdef __cplusplus +} +#endif +void SetQdirFromPath( const char *path ); +char *ExpandArg( const char *path ); // from cmd line +char *ExpandPath( const char *path ); // from scripts +char *ExpandGamePath (const char *path); +char *ExpandPathAndArchive( const char *path ); + + +double I_FloatTime( void ); + +void Error( const char *error, ... ); +int CheckParm( const char *check ); + +FILE *SafeOpenWrite( const char *filename ); +FILE *SafeOpenRead( const char *filename ); +void SafeRead (FILE *f, void *buffer, int count); +void SafeWrite (FILE *f, const void *buffer, int count); + +int LoadFile( const char *filename, void **bufferptr ); +int LoadFileBlock( const char *filename, void **bufferptr ); +int TryLoadFile( const char *filename, void **bufferptr ); +void SaveFile( const char *filename, const void *buffer, int count ); +qboolean FileExists( const char *filename ); + +void DefaultExtension( char *path, const char *extension ); +void DefaultPath( char *path, const char *basepath ); +void StripFilename( char *path ); +void StripExtension( char *path ); + +void ExtractFilePath( const char *path, char *dest ); +void ExtractFileBase( const char *path, char *dest ); +void ExtractFileExtension( const char *path, char *dest ); + +int ParseNum (const char *str); + +short BigShort (short l); +short LittleShort (short l); +int BigLong (int l); +int LittleLong (int l); +float BigFloat (float l); +float LittleFloat (float l); + + +char *COM_Parse (char *data); + +extern char com_token[1024]; +extern qboolean com_eof; + +char *copystring(const char *s); + + +void CRC_Init(unsigned short *crcvalue); +void CRC_ProcessByte(unsigned short *crcvalue, byte data); +unsigned short CRC_Value(unsigned short crcvalue); + +#ifdef __cplusplus +extern "C" +{ +#endif + void CreatePath( const char *path ); +#ifdef __cplusplus +} +#endif + + + + +void QCopyFile( const char *from, const char *to ); + +extern qboolean archive; +extern char archivedir[1024]; + + +extern qboolean verbose; +void qprintf( const char *format, ... ); + +void ExpandWildcards( int *argc, char ***argv ); + +void setQDir( const char* newName); + +// for compression routines +typedef struct +{ + void *data; + int count, width, height; +} cblock_t; + + +#endif diff --git a/utils/common/imagelib.c b/utils/common/imagelib.c new file mode 100644 index 0000000..110ef43 --- /dev/null +++ b/utils/common/imagelib.c @@ -0,0 +1,1138 @@ +// imagelib.c + +#include "cmdlib.h" +#include "imagelib.h" + + +int fgetLittleShort (FILE *f) +{ + byte b1, b2; + + b1 = fgetc(f); + b2 = fgetc(f); + + return (short)(b1 + b2*256); +} + +int fgetLittleLong (FILE *f) +{ + byte b1, b2, b3, b4; + + b1 = fgetc(f); + b2 = fgetc(f); + b3 = fgetc(f); + b4 = fgetc(f); + + return b1 + (b2<<8) + (b3<<16) + (b4<<24); +} + + + +/* +============================================================================ + + LBM STUFF + +============================================================================ +*/ + + +typedef unsigned char UBYTE; +//conflicts with windows typedef short WORD; +typedef unsigned short UWORD; +typedef long LONG; + +typedef enum +{ + ms_none, + ms_mask, + ms_transcolor, + ms_lasso +} mask_t; + +typedef enum +{ + cm_none, + cm_rle1 +} compress_t; + +typedef struct +{ + UWORD w,h; + short x,y; + UBYTE nPlanes; + UBYTE masking; + UBYTE compression; + UBYTE pad1; + UWORD transparentColor; + UBYTE xAspect,yAspect; + short pageWidth,pageHeight; +} bmhd_t; + +extern bmhd_t bmhd; // will be in native byte order + + + +#define FORMID ('F'+('O'<<8)+((int)'R'<<16)+((int)'M'<<24)) +#define ILBMID ('I'+('L'<<8)+((int)'B'<<16)+((int)'M'<<24)) +#define PBMID ('P'+('B'<<8)+((int)'M'<<16)+((int)' '<<24)) +#define BMHDID ('B'+('M'<<8)+((int)'H'<<16)+((int)'D'<<24)) +#define BODYID ('B'+('O'<<8)+((int)'D'<<16)+((int)'Y'<<24)) +#define CMAPID ('C'+('M'<<8)+((int)'A'<<16)+((int)'P'<<24)) + + +bmhd_t bmhd; + +int Align (int l) +{ + if (l&1) + return l+1; + return l; +} + + + +/* +================ +LBMRLEdecompress + +Source must be evenly aligned! +================ +*/ +byte *LBMRLEDecompress (byte *source,byte *unpacked, int bpwidth) +{ + int count; + byte b,rept; + + count = 0; + + do + { + rept = *source++; + + if (rept > 0x80) + { + rept = (rept^0xff)+2; + b = *source++; + memset(unpacked,b,rept); + unpacked += rept; + } + else if (rept < 0x80) + { + rept++; + memcpy(unpacked,source,rept); + unpacked += rept; + source += rept; + } + else + rept = 0; // rept of 0x80 is NOP + + count += rept; + + } while (countbpwidth) + Error ("Decompression exceeded width!\n"); + + + return source; +} + + +/* +================= +LoadLBM +================= +*/ +void LoadLBM (char *filename, byte **picture, byte **palette) +{ + byte *LBMbuffer, *picbuffer, *cmapbuffer; + int y; + byte *LBM_P, *LBMEND_P; + byte *pic_p; + byte *body_p; + + int formtype,formlength; + int chunktype,chunklength; + +// qiet compiler warnings + picbuffer = NULL; + cmapbuffer = NULL; + +// +// load the LBM +// + LoadFile (filename, (void **)&LBMbuffer); + +// +// parse the LBM header +// + LBM_P = LBMbuffer; + if ( *(int *)LBMbuffer != LittleLong(FORMID) ) + Error ("No FORM ID at start of file!\n"); + + LBM_P += 4; + formlength = BigLong( *(int *)LBM_P ); + LBM_P += 4; + LBMEND_P = LBM_P + Align(formlength); + + formtype = LittleLong(*(int *)LBM_P); + + if (formtype != ILBMID && formtype != PBMID) + Error ("Unrecognized form type: %c%c%c%c\n", formtype&0xff + ,(formtype>>8)&0xff,(formtype>>16)&0xff,(formtype>>24)&0xff); + + LBM_P += 4; + +// +// parse chunks +// + + while (LBM_P < LBMEND_P) + { + chunktype = LBM_P[0] + (LBM_P[1]<<8) + (LBM_P[2]<<16) + (LBM_P[3]<<24); + LBM_P += 4; + chunklength = LBM_P[3] + (LBM_P[2]<<8) + (LBM_P[1]<<16) + (LBM_P[0]<<24); + LBM_P += 4; + + switch ( chunktype ) + { + case BMHDID: + memcpy (&bmhd,LBM_P,sizeof(bmhd)); + bmhd.w = BigShort(bmhd.w); + bmhd.h = BigShort(bmhd.h); + bmhd.x = BigShort(bmhd.x); + bmhd.y = BigShort(bmhd.y); + bmhd.pageWidth = BigShort(bmhd.pageWidth); + bmhd.pageHeight = BigShort(bmhd.pageHeight); + break; + + case CMAPID: + cmapbuffer = malloc (768); + memset (cmapbuffer, 0, 768); + memcpy (cmapbuffer, LBM_P, chunklength); + break; + + case BODYID: + body_p = LBM_P; + + pic_p = picbuffer = malloc (bmhd.w*bmhd.h); + if (formtype == PBMID) + { + // + // unpack PBM + // + for (y=0 ; ydata; + + pcx->xmin = LittleShort(pcx->xmin); + pcx->ymin = LittleShort(pcx->ymin); + pcx->xmax = LittleShort(pcx->xmax); + pcx->ymax = LittleShort(pcx->ymax); + pcx->hres = LittleShort(pcx->hres); + pcx->vres = LittleShort(pcx->vres); + pcx->bytes_per_line = LittleShort(pcx->bytes_per_line); + pcx->palette_type = LittleShort(pcx->palette_type); + + if (pcx->manufacturer != 0x0a + || pcx->version != 5 + || pcx->encoding != 1 + || pcx->bits_per_pixel != 8 + || pcx->xmax >= 640 + || pcx->ymax >= 480) + Error ("Bad pcx file %s", filename); + + if (palette) + { + *palette = malloc(768); + memcpy (*palette, (byte *)pcx + len - 768, 768); + } + + if (width) + *width = pcx->xmax+1; + if (height) + *height = pcx->ymax+1; + + if (!pic) + return; + + out = malloc ( (pcx->ymax+1) * (pcx->xmax+1) ); + if (!out) + Error ("Skin_Cache: couldn't allocate"); + + *pic = out; + + pix = out; + + for (y=0 ; y<=pcx->ymax ; y++, pix += pcx->xmax+1) + { + for (x=0 ; x<=pcx->xmax ; ) + { + dataByte = *raw++; + + if((dataByte & 0xC0) == 0xC0) + { + runLength = dataByte & 0x3F; + dataByte = *raw++; + } + else + runLength = 1; + + while(runLength-- > 0) + pix[x++] = dataByte; + } + + } + + if ( raw - (byte *)pcx > len) + Error ("PCX file %s was malformed", filename); + + free (pcx); +} + +/* +============== +WritePCXfile +============== +*/ +void WritePCXfile (char *filename, byte *data, + int width, int height, byte *palette) +{ + int i, j, length; + pcx_t *pcx; + byte *pack; + + pcx = malloc (width*height*2+1000); + memset (pcx, 0, sizeof(*pcx)); + + pcx->manufacturer = 0x0a; // PCX id + pcx->version = 5; // 256 color + pcx->encoding = 1; // uncompressed + pcx->bits_per_pixel = 8; // 256 color + pcx->xmin = 0; + pcx->ymin = 0; + pcx->xmax = LittleShort((short)(width-1)); + pcx->ymax = LittleShort((short)(height-1)); + pcx->hres = LittleShort((short)width); + pcx->vres = LittleShort((short)height); + pcx->color_planes = 1; // chunky image + pcx->bytes_per_line = LittleShort((short)width); + pcx->palette_type = LittleShort(1); // not a grey scale + + // pack the image + pack = &pcx->data; + + for (i=0 ; i=0; row--) + { + pixbuf = targa_rgba + row*columns*4; + for(column=0; column=0; row--) { + pixbuf = targa_rgba + row*columns*4; + for(column=0; column0) + row--; + else + goto breakOut; + pixbuf = targa_rgba + row*columns*4; + } + } + } + else { // non run-length packet + for(j=0;j0) + row--; + else + goto breakOut; + pixbuf = targa_rgba + row*columns*4; + } + } + } + } + breakOut:; + } + } + + //free(buffer); +} + + + +/* +============= +LoadTGA +============= +*/ +void LoadTGA (char *name, byte **pixels, int *width, int *height) +{ + byte *buffer; + int nLen; + // + // load the file + // + nLen = LoadFile ( ( char * ) name, (void **)&buffer); + if (nLen == -1) + { + Error ("Couldn't read %s", name); + } + + LoadTGABuffer(buffer, pixels, width, height); + +} + + + +/* +================ +WriteTGA +================ +*/ +void WriteTGA (char *filename, byte *data, int width, int height) { + byte *buffer; + int i; + int c; + FILE *f; + + buffer = malloc(width*height*4 + 18); + memset (buffer, 0, 18); + buffer[2] = 2; // uncompressed type + buffer[12] = width&255; + buffer[13] = width>>8; + buffer[14] = height&255; + buffer[15] = height>>8; + buffer[16] = 32; // pixel size + + // swap rgb to bgr + c = 18 + width * height * 4; + for (i=18 ; i +#include "cmdlib.h" +#include "mathlib.h" +#include "trilib.h" +#include "l3dslib.h" + +#define MAIN3DS 0x4D4D +#define EDIT3DS 0x3D3D // this is the start of the editor config +#define EDIT_OBJECT 0x4000 +#define OBJ_TRIMESH 0x4100 +#define TRI_VERTEXL 0x4110 +#define TRI_FACEL1 0x4120 + +#define MAXVERTS 2000 + +typedef struct { + int v[4]; +} tri; + +float fverts[MAXVERTS][3]; +tri tris[MAXTRIANGLES]; + +int bytesread, level, numtris, totaltris; +int vertsfound, trisfound; + +triangle_t *ptri; + + +// Alias stores triangles as 3 explicit vertices in .tri files, so even though we +// start out with a vertex pool and vertex indices for triangles, we have to convert +// to raw, explicit triangles +void StoreAliasTriangles (void) +{ + int i, j, k; + + if ((totaltris + numtris) > MAXTRIANGLES) + Error ("Error: Too many triangles"); + + for (i=0; i MAXVERTS) + Error ("Error: Too many vertices"); + + for (i=0 ; i MAXTRIANGLES) + Error ("Error: Too many triangles"); + + for (i=0 ; i 0) + { + w -= ParseChunk (input); + } + + retval = length; + goto Done; + + default: + // skip other chunks + while (w > 0) + { + t = w; + + if (t > BLOCK_SIZE) + t = BLOCK_SIZE; + + if (feof(input)) + Error ("Error: unexpected end of file"); + + fread (&temp, t, 1, input); + bytesread += t; + + w -= t; + } + + retval = length; + goto Done; + } + +Done: + level--; + return retval; +} + + +void Load3DSTriangleList (char *filename, triangle_t **pptri, int *numtriangles) +{ + FILE *input; + short int tshort; + + bytesread = 0; + level = 0; + numtris = 0; + totaltris = 0; + vertsfound = 0; + trisfound = 0; + + if ((input = fopen(filename, "rb")) == 0) { + fprintf(stderr,"reader: could not open file '%s'\n", filename); + exit(0); + } + + fread(&tshort, sizeof(tshort), 1, input); + +// should only be MAIN3DS, but some files seem to start with EDIT3DS, with +// no MAIN3DS + if ((tshort != MAIN3DS) && (tshort != EDIT3DS)) { + fprintf(stderr,"File is not a 3DS file.\n"); + exit(0); + } + +// back to top of file so we can parse the first chunk descriptor + fseek(input, 0, SEEK_SET); + + ptri = malloc (MAXTRIANGLES * sizeof(triangle_t)); + + *pptri = ptri; + +// parse through looking for the relevant chunk tree (MAIN3DS | EDIT3DS | EDIT_OBJECT | +// OBJ_TRIMESH | {TRI_VERTEXL, TRI_FACEL1}) and skipping other chunks + ParseChunk (input); + + if (vertsfound || trisfound) + Error ("Incomplete triangle set"); + + *numtriangles = totaltris; + + fclose (input); +} + diff --git a/utils/common/l3dslib.h b/utils/common/l3dslib.h new file mode 100644 index 0000000..d155376 --- /dev/null +++ b/utils/common/l3dslib.h @@ -0,0 +1,5 @@ +// +// l3dslib.h: header file for loading triangles from a 3DS triangle file +// +void Load3DSTriangleList (char *filename, triangle_t **pptri, int *numtriangles); + diff --git a/utils/common/lbmlib.c b/utils/common/lbmlib.c new file mode 100644 index 0000000..5524303 --- /dev/null +++ b/utils/common/lbmlib.c @@ -0,0 +1,1087 @@ +// lbmlib.c + +#include "cmdlib.h" +#include "lbmlib.h" + + +int fgetLittleShort (FILE *f) +{ + byte b1, b2; + + b1 = fgetc(f); + b2 = fgetc(f); + + return (short)(b1 + b2*256); +} + +int fgetLittleLong (FILE *f) +{ + byte b1, b2, b3, b4; + + b1 = fgetc(f); + b2 = fgetc(f); + b3 = fgetc(f); + b4 = fgetc(f); + + return b1 + (b2<<8) + (b3<<16) + (b4<<24); +} + + + +/* +============================================================================ + + LBM STUFF + +============================================================================ +*/ + + +typedef unsigned char UBYTE; +//conflicts with windows typedef short WORD; +typedef unsigned short UWORD; +typedef long LONG; + +typedef enum +{ + ms_none, + ms_mask, + ms_transcolor, + ms_lasso +} mask_t; + +typedef enum +{ + cm_none, + cm_rle1 +} compress_t; + +typedef struct +{ + UWORD w,h; + short x,y; + UBYTE nPlanes; + UBYTE masking; + UBYTE compression; + UBYTE pad1; + UWORD transparentColor; + UBYTE xAspect,yAspect; + short pageWidth,pageHeight; +} bmhd_t; + +extern bmhd_t bmhd; // will be in native byte order + + + +#define FORMID ('F'+('O'<<8)+((int)'R'<<16)+((int)'M'<<24)) +#define ILBMID ('I'+('L'<<8)+((int)'B'<<16)+((int)'M'<<24)) +#define PBMID ('P'+('B'<<8)+((int)'M'<<16)+((int)' '<<24)) +#define BMHDID ('B'+('M'<<8)+((int)'H'<<16)+((int)'D'<<24)) +#define BODYID ('B'+('O'<<8)+((int)'D'<<16)+((int)'Y'<<24)) +#define CMAPID ('C'+('M'<<8)+((int)'A'<<16)+((int)'P'<<24)) + + +bmhd_t bmhd; + +int Align (int l) +{ + if (l&1) + return l+1; + return l; +} + + + +/* +================ +LBMRLEdecompress + +Source must be evenly aligned! +================ +*/ +byte *LBMRLEDecompress (byte *source,byte *unpacked, int bpwidth) +{ + int count; + byte b,rept; + + count = 0; + + do + { + rept = *source++; + + if (rept > 0x80) + { + rept = (rept^0xff)+2; + b = *source++; + memset(unpacked,b,rept); + unpacked += rept; + } + else if (rept < 0x80) + { + rept++; + memcpy(unpacked,source,rept); + unpacked += rept; + source += rept; + } + else + rept = 0; // rept of 0x80 is NOP + + count += rept; + + } while (countbpwidth) + Error ("Decompression exceeded width!\n"); + + + return source; +} + + +/* +================= +LoadLBM +================= +*/ +void LoadLBM (char *filename, byte **picture, byte **palette) +{ + byte *LBMbuffer, *picbuffer, *cmapbuffer; + int y; + byte *LBM_P, *LBMEND_P; + byte *pic_p; + byte *body_p; + + int formtype,formlength; + int chunktype,chunklength; + +// qiet compiler warnings + picbuffer = NULL; + cmapbuffer = NULL; + +// +// load the LBM +// + LoadFile (filename, (void **)&LBMbuffer); + +// +// parse the LBM header +// + LBM_P = LBMbuffer; + if ( *(int *)LBMbuffer != LittleLong(FORMID) ) + Error ("No FORM ID at start of file!\n"); + + LBM_P += 4; + formlength = BigLong( *(int *)LBM_P ); + LBM_P += 4; + LBMEND_P = LBM_P + Align(formlength); + + formtype = LittleLong(*(int *)LBM_P); + + if (formtype != ILBMID && formtype != PBMID) + Error ("Unrecognized form type: %c%c%c%c\n", formtype&0xff + ,(formtype>>8)&0xff,(formtype>>16)&0xff,(formtype>>24)&0xff); + + LBM_P += 4; + +// +// parse chunks +// + + while (LBM_P < LBMEND_P) + { + chunktype = LBM_P[0] + (LBM_P[1]<<8) + (LBM_P[2]<<16) + (LBM_P[3]<<24); + LBM_P += 4; + chunklength = LBM_P[3] + (LBM_P[2]<<8) + (LBM_P[1]<<16) + (LBM_P[0]<<24); + LBM_P += 4; + + switch ( chunktype ) + { + case BMHDID: + memcpy (&bmhd,LBM_P,sizeof(bmhd)); + bmhd.w = BigShort(bmhd.w); + bmhd.h = BigShort(bmhd.h); + bmhd.x = BigShort(bmhd.x); + bmhd.y = BigShort(bmhd.y); + bmhd.pageWidth = BigShort(bmhd.pageWidth); + bmhd.pageHeight = BigShort(bmhd.pageHeight); + break; + + case CMAPID: + cmapbuffer = malloc (768); + memset (cmapbuffer, 0, 768); + memcpy (cmapbuffer, LBM_P, chunklength); + break; + + case BODYID: + body_p = LBM_P; + + pic_p = picbuffer = malloc (bmhd.w*bmhd.h); + if (formtype == PBMID) + { + // + // unpack PBM + // + for (y=0 ; ydata; + + pcx->xmin = LittleShort(pcx->xmin); + pcx->ymin = LittleShort(pcx->ymin); + pcx->xmax = LittleShort(pcx->xmax); + pcx->ymax = LittleShort(pcx->ymax); + pcx->hres = LittleShort(pcx->hres); + pcx->vres = LittleShort(pcx->vres); + pcx->bytes_per_line = LittleShort(pcx->bytes_per_line); + pcx->palette_type = LittleShort(pcx->palette_type); + + if (pcx->manufacturer != 0x0a + || pcx->version != 5 + || pcx->encoding != 1 + || pcx->bits_per_pixel != 8 + || pcx->xmax >= 640 + || pcx->ymax >= 480) + Error ("Bad pcx file %s", filename); + + if (palette) + { + *palette = malloc(768); + memcpy (*palette, (byte *)pcx + len - 768, 768); + } + + if (width) + *width = pcx->xmax+1; + if (height) + *height = pcx->ymax+1; + + if (!pic) + return; + + out = malloc ( (pcx->ymax+1) * (pcx->xmax+1) ); + if (!out) + Error ("Skin_Cache: couldn't allocate"); + + *pic = out; + + pix = out; + + for (y=0 ; y<=pcx->ymax ; y++, pix += pcx->xmax+1) + { + for (x=0 ; x<=pcx->xmax ; ) + { + dataByte = *raw++; + + if((dataByte & 0xC0) == 0xC0) + { + runLength = dataByte & 0x3F; + dataByte = *raw++; + } + else + runLength = 1; + + while(runLength-- > 0) + pix[x++] = dataByte; + } + + } + + if ( raw - (byte *)pcx > len) + Error ("PCX file %s was malformed", filename); + + free (pcx); +} + +/* +============== +WritePCXfile +============== +*/ +void WritePCXfile (char *filename, byte *data, + int width, int height, byte *palette) +{ + int i, j, length; + pcx_t *pcx; + byte *pack; + + pcx = malloc (width*height*2+1000); + memset (pcx, 0, sizeof(*pcx)); + + pcx->manufacturer = 0x0a; // PCX id + pcx->version = 5; // 256 color + pcx->encoding = 1; // uncompressed + pcx->bits_per_pixel = 8; // 256 color + pcx->xmin = 0; + pcx->ymin = 0; + pcx->xmax = LittleShort((short)(width-1)); + pcx->ymax = LittleShort((short)(height-1)); + pcx->hres = LittleShort((short)width); + pcx->vres = LittleShort((short)height); + pcx->color_planes = 1; // chunky image + pcx->bytes_per_line = LittleShort((short)width); + pcx->palette_type = LittleShort(1); // not a grey scale + + // pack the image + pack = &pcx->data; + + for (i=0 ; i=0; row--) { + pixbuf = targa_rgba + row*columns*4; + for(column=0; column=0; row--) { + pixbuf = targa_rgba + row*columns*4; + for(column=0; column0) + row--; + else + goto breakOut; + pixbuf = targa_rgba + row*columns*4; + } + } + } + else { // non run-length packet + for(j=0;j0) + row--; + else + goto breakOut; + pixbuf = targa_rgba + row*columns*4; + } + } + } + } + breakOut:; + } + } + + fclose(fin); +} + + +/* +================ +WriteTGA +================ +*/ +void WriteTGA (char *filename, byte *data, int width, int height) { + byte *buffer; + int i; + int c; + FILE *f; + + buffer = malloc(width*height*4 + 18); + memset (buffer, 0, 18); + buffer[2] = 2; // uncompressed type + buffer[12] = width&255; + buffer[13] = width>>8; + buffer[14] = height&255; + buffer[15] = height>>8; + buffer[16] = 32; // pixel size + + // swap rgb to bgr + c = 18 + width * height * 4; + for (i=18 ; i 0 ) { + bytes[0] = 0; + bytes[1] = 0; // lat = 0, long = 0 + } else { + bytes[0] = 128; + bytes[1] = 0; // lat = 0, long = 128 + } + } else { + int a, b; + + a = RAD2DEG( atan2( normal[1], normal[0] ) ) * (255.0f / 360.0f ); + a &= 0xff; + + b = RAD2DEG( acos( normal[2] ) ) * ( 255.0f / 360.0f ); + b &= 0xff; + + bytes[0] = b; // longitude + bytes[1] = a; // lattitude + } +} + +/* +===================== +PlaneFromPoints + +Returns false if the triangle is degenrate. +The normal will point out of the clock for clockwise ordered points +===================== +*/ +qboolean PlaneFromPoints( vec4_t plane, const vec3_t a, const vec3_t b, const vec3_t c ) { + vec3_t d1, d2; + + VectorSubtract( b, a, d1 ); + VectorSubtract( c, a, d2 ); + CrossProduct( d2, d1, plane ); + if ( VectorNormalize( plane, plane ) == 0 ) { + return qfalse; + } + + plane[3] = DotProduct( a, plane ); + return qtrue; +} + +/* +================ +MakeNormalVectors + +Given a normalized forward vector, create two +other perpendicular vectors +================ +*/ +void MakeNormalVectors (vec3_t forward, vec3_t right, vec3_t up) +{ + float d; + + // this rotate and negate guarantees a vector + // not colinear with the original + right[1] = -forward[0]; + right[2] = forward[1]; + right[0] = forward[2]; + + d = DotProduct (right, forward); + VectorMA (right, -d, forward, right); + VectorNormalize (right, right); + CrossProduct (right, forward, up); +} + + +void Vec10Copy( vec_t *in, vec_t *out ) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[4] = in[4]; + out[5] = in[5]; + out[6] = in[6]; + out[7] = in[7]; + out[8] = in[8]; + out[9] = in[9]; +} + + +void VectorRotate3x3( vec3_t v, float r[3][3], vec3_t d ) +{ + d[0] = v[0] * r[0][0] + v[1] * r[1][0] + v[2] * r[2][0]; + d[1] = v[0] * r[0][1] + v[1] * r[1][1] + v[2] * r[2][1]; + d[2] = v[0] * r[0][2] + v[1] * r[1][2] + v[2] * r[2][2]; +} + +double VectorLength( const vec3_t v ) { + double length; + + length = sqrt (v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); + return length; +} + +qboolean VectorCompare( const vec3_t v1, const vec3_t v2 ) { + int i; + + for (i=0 ; i<3 ; i++) + if (fabs(v1[i]-v2[i]) > EQUAL_EPSILON) + return qfalse; + + return qtrue; +} + +void VectorRotate (vec3_t vIn, vec3_t vRotation, vec3_t out) +{ + vec3_t vWork, va; + int i; + int nIndex[3][2]; + nIndex[0][0] = 1; nIndex[0][1] = 2; + nIndex[1][0] = 2; nIndex[1][1] = 0; + nIndex[2][0] = 0; nIndex[2][1] = 1; + + VectorCopy(vIn, va); + VectorCopy(va, vWork); + + for (i = 0; i < 3; i++) + { + if (vRotation[i] != 0) + { + double dAngle = vRotation[i] / 180 * Q_PI; + double c = cos(dAngle); + double s = sin(dAngle); + vWork[nIndex[i][0]] = va[nIndex[i][0]] * c - va[nIndex[i][1]] * s; + vWork[nIndex[i][1]] = va[nIndex[i][0]] * s + va[nIndex[i][1]] * c; + } + VectorCopy(vWork, va); + } + VectorCopy(vWork, out); +} + + +vec_t Q_rint (vec_t in) +{ + return floor (in + 0.5); +} + +void VectorMA( const vec3_t va, double scale, const vec3_t vb, vec3_t vc ) { + vc[0] = va[0] + scale*vb[0]; + vc[1] = va[1] + scale*vb[1]; + vc[2] = va[2] + scale*vb[2]; +} + +void CrossProduct( const vec3_t v1, const vec3_t v2, vec3_t cross ) { + cross[0] = v1[1]*v2[2] - v1[2]*v2[1]; + cross[1] = v1[2]*v2[0] - v1[0]*v2[2]; + cross[2] = v1[0]*v2[1] - v1[1]*v2[0]; +} + +vec_t _DotProduct (vec3_t v1, vec3_t v2) +{ + return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]; +} + +void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out) +{ + out[0] = va[0]-vb[0]; + out[1] = va[1]-vb[1]; + out[2] = va[2]-vb[2]; +} + +void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out) +{ + out[0] = va[0]+vb[0]; + out[1] = va[1]+vb[1]; + out[2] = va[2]+vb[2]; +} + +void _VectorCopy (vec3_t in, vec3_t out) +{ + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; +} + +void _VectorScale (vec3_t v, vec_t scale, vec3_t out) +{ + out[0] = v[0] * scale; + out[1] = v[1] * scale; + out[2] = v[2] * scale; +} + +vec_t VectorNormalize( const vec3_t in, vec3_t out ) { + vec_t length, ilength; + + length = sqrt (in[0]*in[0] + in[1]*in[1] + in[2]*in[2]); + if (length == 0) + { + VectorClear (out); + return 0; + } + + ilength = 1.0/length; + out[0] = in[0]*ilength; + out[1] = in[1]*ilength; + out[2] = in[2]*ilength; + + return length; +} + +vec_t ColorNormalize( const vec3_t in, vec3_t out ) { + float max, scale; + + max = in[0]; + if (in[1] > max) + max = in[1]; + if (in[2] > max) + max = in[2]; + + if (max == 0) { + out[0] = out[1] = out[2] = 1.0; + return 0; + } + + scale = 1.0 / max; + + VectorScale (in, scale, out); + + return max; +} + + + +void VectorInverse (vec3_t v) +{ + v[0] = -v[0]; + v[1] = -v[1]; + v[2] = -v[2]; +} + +void ClearBounds (vec3_t mins, vec3_t maxs) +{ + mins[0] = mins[1] = mins[2] = 99999; + maxs[0] = maxs[1] = maxs[2] = -99999; +} + +void AddPointToBounds( const vec3_t v, vec3_t mins, vec3_t maxs ) { + int i; + vec_t val; + + for (i=0 ; i<3 ; i++) + { + val = v[i]; + if (val < mins[i]) + mins[i] = val; + if (val > maxs[i]) + maxs[i] = val; + } +} + + +/* +================= +PlaneTypeForNormal +================= +*/ +int PlaneTypeForNormal (vec3_t normal) { + if (normal[0] == 1.0 || normal[0] == -1.0) + return PLANE_X; + if (normal[1] == 1.0 || normal[1] == -1.0) + return PLANE_Y; + if (normal[2] == 1.0 || normal[2] == -1.0) + return PLANE_Z; + + return PLANE_NON_AXIAL; +} + +/* +================ +MatrixMultiply +================ +*/ +void MatrixMultiply(float in1[3][3], float in2[3][3], float out[3][3]) { + out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + + in1[0][2] * in2[2][0]; + out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + + in1[0][2] * in2[2][1]; + out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + + in1[0][2] * in2[2][2]; + out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + + in1[1][2] * in2[2][0]; + out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + + in1[1][2] * in2[2][1]; + out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + + in1[1][2] * in2[2][2]; + out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + + in1[2][2] * in2[2][0]; + out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + + in1[2][2] * in2[2][1]; + out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + + in1[2][2] * in2[2][2]; +} + +void ProjectPointOnPlane( vec3_t dst, const vec3_t p, const vec3_t normal ) +{ + float d; + vec3_t n; + float inv_denom; + + inv_denom = 1.0F / DotProduct( normal, normal ); + + d = DotProduct( normal, p ) * inv_denom; + + n[0] = normal[0] * inv_denom; + n[1] = normal[1] * inv_denom; + n[2] = normal[2] * inv_denom; + + dst[0] = p[0] - d * n[0]; + dst[1] = p[1] - d * n[1]; + dst[2] = p[2] - d * n[2]; +} + +/* +** assumes "src" is normalized +*/ +void PerpendicularVector( vec3_t dst, const vec3_t src ) +{ + int pos; + int i; + float minelem = 1.0F; + vec3_t tempvec; + + /* + ** find the smallest magnitude axially aligned vector + */ + for ( pos = 0, i = 0; i < 3; i++ ) + { + if ( fabs( src[i] ) < minelem ) + { + pos = i; + minelem = fabs( src[i] ); + } + } + tempvec[0] = tempvec[1] = tempvec[2] = 0.0F; + tempvec[pos] = 1.0F; + + /* + ** project the point onto the plane defined by src + */ + ProjectPointOnPlane( dst, tempvec, src ); + + /* + ** normalize the result + */ + VectorNormalize( dst, dst ); +} + +/* +=============== +RotatePointAroundVector + +This is not implemented very well... +=============== +*/ +void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, + float degrees ) { + float m[3][3]; + float im[3][3]; + float zrot[3][3]; + float tmpmat[3][3]; + float rot[3][3]; + int i; + vec3_t vr, vup, vf; + float rad; + + vf[0] = dir[0]; + vf[1] = dir[1]; + vf[2] = dir[2]; + + PerpendicularVector( vr, dir ); + CrossProduct( vr, vf, vup ); + + m[0][0] = vr[0]; + m[1][0] = vr[1]; + m[2][0] = vr[2]; + + m[0][1] = vup[0]; + m[1][1] = vup[1]; + m[2][1] = vup[2]; + + m[0][2] = vf[0]; + m[1][2] = vf[1]; + m[2][2] = vf[2]; + + memcpy( im, m, sizeof( im ) ); + + im[0][1] = m[1][0]; + im[0][2] = m[2][0]; + im[1][0] = m[0][1]; + im[1][2] = m[2][1]; + im[2][0] = m[0][2]; + im[2][1] = m[1][2]; + + memset( zrot, 0, sizeof( zrot ) ); + zrot[0][0] = zrot[1][1] = zrot[2][2] = 1.0F; + + rad = DEG2RAD( degrees ); + zrot[0][0] = cos( rad ); + zrot[0][1] = sin( rad ); + zrot[1][0] = -sin( rad ); + zrot[1][1] = cos( rad ); + + MatrixMultiply( m, zrot, tmpmat ); + MatrixMultiply( tmpmat, im, rot ); + + for ( i = 0; i < 3; i++ ) { + dst[i] = rot[i][0] * point[0] + rot[i][1] * point[1] + rot[i][2] * point[2]; + } +} diff --git a/utils/common/mathlib.h b/utils/common/mathlib.h new file mode 100644 index 0000000..0b47f2f --- /dev/null +++ b/utils/common/mathlib.h @@ -0,0 +1,77 @@ +#ifndef __MATHLIB__ +#define __MATHLIB__ + +// mathlib.h + +#include + +#ifdef DOUBLEVEC_T +typedef double vec_t; +#else +typedef float vec_t; +#endif +typedef vec_t vec2_t[2]; +typedef vec_t vec3_t[3]; +typedef vec_t vec4_t[4]; + +#define SIDE_FRONT 0 +#define SIDE_ON 2 +#define SIDE_BACK 1 +#define SIDE_CROSS -2 + +#define Q_PI 3.14159265358979323846 +#define DEG2RAD( a ) ( ( (a) * Q_PI ) / 180.0F ) +#define RAD2DEG( a ) ( ( (a) * 180.0f ) / Q_PI ) + +extern vec3_t vec3_origin; + +#define EQUAL_EPSILON 0.001 + +// plane types are used to speed some tests +// 0-2 are axial planes +#define PLANE_X 0 +#define PLANE_Y 1 +#define PLANE_Z 2 +#define PLANE_NON_AXIAL 3 + + +qboolean VectorCompare( const vec3_t v1, const vec3_t v2 ); +void VectorRotate (vec3_t vIn, vec3_t vRotation, vec3_t out); + +#define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+x[2]*y[2]) +#define VectorSubtract(a,b,c) {c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];} +#define VectorAdd(a,b,c) {c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];} +#define VectorCopy(a,b) {b[0]=a[0];b[1]=a[1];b[2]=a[2];} +#define VectorScale(a,b,c) {c[0]=b*a[0];c[1]=b*a[1];c[2]=b*a[2];} +#define VectorClear(x) {x[0] = x[1] = x[2] = 0;} +#define VectorNegate(x) {x[0]=-x[0];x[1]=-x[1];x[2]=-x[2];} +#define VectorSet(v, a, b, c) {v[0]=a;v[1]=b;v[2]=c;} +void Vec10Copy( vec_t *in, vec_t *out ); + +vec_t Q_rint (vec_t in); +vec_t _DotProduct (vec3_t v1, vec3_t v2); +void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out); +void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out); +void _VectorCopy (vec3_t in, vec3_t out); +void _VectorScale (vec3_t v, vec_t scale, vec3_t out); + +double VectorLength( const vec3_t v ); + +void VectorMA( const vec3_t va, double scale, const vec3_t vb, vec3_t vc ); + +void CrossProduct( const vec3_t v1, const vec3_t v2, vec3_t cross ); +vec_t VectorNormalize( const vec3_t in, vec3_t out ); +vec_t ColorNormalize( const vec3_t in, vec3_t out ); +void VectorInverse (vec3_t v); + +void ClearBounds (vec3_t mins, vec3_t maxs); +void AddPointToBounds( const vec3_t v, vec3_t mins, vec3_t maxs ); + +qboolean PlaneFromPoints( vec4_t plane, const vec3_t a, const vec3_t b, const vec3_t c ); + +void NormalToLatLong( const vec3_t normal, byte bytes[2] ); + +int PlaneTypeForNormal (vec3_t normal); +void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, + float degrees ); +#endif diff --git a/utils/common/md4.c b/utils/common/md4.c new file mode 100644 index 0000000..37de7f7 --- /dev/null +++ b/utils/common/md4.c @@ -0,0 +1,277 @@ +/* GLOBAL.H - RSAREF types and constants */ + +#include + +/* POINTER defines a generic pointer type */ +typedef unsigned char *POINTER; + +/* UINT2 defines a two byte word */ +typedef unsigned short int UINT2; + +/* UINT4 defines a four byte word */ +typedef unsigned long int UINT4; + + +/* MD4.H - header file for MD4C.C */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. + +All rights reserved. + +License to copy and use this software is granted provided that it is identified as the “RSA Data Security, Inc. MD4 Message-Digest Algorithm” in all material mentioning or referencing this software or this function. +License is also granted to make and use derivative works provided that such works are identified as “derived from the RSA Data Security, Inc. MD4 Message-Digest Algorithm” in all material mentioning or referencing the derived work. +RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided “as is” without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this documentation and/or software. */ + +/* MD4 context. */ +typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD4_CTX; + +void MD4Init (MD4_CTX *); +void MD4Update (MD4_CTX *, unsigned char *, unsigned int); +void MD4Final (unsigned char [16], MD4_CTX *); + + + +/* MD4C.C - RSA Data Security, Inc., MD4 message-digest algorithm */ +/* Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved. + +License to copy and use this software is granted provided that it is identified as the +RSA Data Security, Inc. MD4 Message-Digest Algorithm + in all material mentioning or referencing this software or this function. +License is also granted to make and use derivative works provided that such works are identified as +derived from the RSA Data Security, Inc. MD4 Message-Digest Algorithm +in all material mentioning or referencing the derived work. +RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided +as is without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this documentation and/or software. */ + +/* Constants for MD4Transform routine. */ +#define S11 3 +#define S12 7 +#define S13 11 +#define S14 19 +#define S21 3 +#define S22 5 +#define S23 9 +#define S24 13 +#define S31 3 +#define S32 9 +#define S33 11 +#define S34 15 + +static void MD4Transform (UINT4 [4], unsigned char [64]); +static void Encode (unsigned char *, UINT4 *, unsigned int); +static void Decode (UINT4 *, unsigned char *, unsigned int); +static void MD4_memcpy (POINTER, POINTER, unsigned int); +static void MD4_memset (POINTER, int, unsigned int); + +static unsigned char PADDING[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 +}; + +/* F, G and H are basic MD4 functions. */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) + +/* ROTATE_LEFT rotates x left n bits. */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG and HH are transformations for rounds 1, 2 and 3 */ +/* Rotation is separate from addition to prevent recomputation */ +#define FF(a, b, c, d, x, s) {(a) += F ((b), (c), (d)) + (x); (a) = ROTATE_LEFT ((a), (s));} + +#define GG(a, b, c, d, x, s) {(a) += G ((b), (c), (d)) + (x) + (UINT4)0x5a827999; (a) = ROTATE_LEFT ((a), (s));} + +#define HH(a, b, c, d, x, s) {(a) += H ((b), (c), (d)) + (x) + (UINT4)0x6ed9eba1; (a) = \ +ROTATE_LEFT ((a), (s)); } + + +/* MD4 initialization. Begins an MD4 operation, writing a new context. */ +void MD4Init (MD4_CTX *context) +{ + context->count[0] = context->count[1] = 0; + +/* Load magic initialization constants.*/ +context->state[0] = 0x67452301; +context->state[1] = 0xefcdab89; +context->state[2] = 0x98badcfe; +context->state[3] = 0x10325476; +} + +/* MD4 block update operation. Continues an MD4 message-digest operation, processing another message block, and updating the context. */ +void MD4Update (MD4_CTX *context, unsigned char *input, unsigned int inputLen) +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3))< ((UINT4)inputLen << 3)) + context->count[1]++; + + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible.*/ + if (inputLen >= partLen) + { + memcpy((POINTER)&context->buffer[index], (POINTER)input, partLen); + MD4Transform (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD4Transform (context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + memcpy ((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i); +} + + +/* MD4 finalization. Ends an MD4 message-digest operation, writing the the message digest and zeroizing the context. */ +void MD4Final (unsigned char digest[16], MD4_CTX *context) +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64.*/ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD4Update (context, PADDING, padLen); + + /* Append length (before padding) */ + MD4Update (context, bits, 8); + + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information.*/ + memset ((POINTER)context, 0, sizeof (*context)); +} + + +/* MD4 basic transformation. Transforms state based on block. */ +static void MD4Transform (UINT4 state[4], unsigned char block[64]) +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + +/* Round 1 */ +FF (a, b, c, d, x[ 0], S11); /* 1 */ +FF (d, a, b, c, x[ 1], S12); /* 2 */ +FF (c, d, a, b, x[ 2], S13); /* 3 */ +FF (b, c, d, a, x[ 3], S14); /* 4 */ +FF (a, b, c, d, x[ 4], S11); /* 5 */ +FF (d, a, b, c, x[ 5], S12); /* 6 */ +FF (c, d, a, b, x[ 6], S13); /* 7 */ +FF (b, c, d, a, x[ 7], S14); /* 8 */ +FF (a, b, c, d, x[ 8], S11); /* 9 */ +FF (d, a, b, c, x[ 9], S12); /* 10 */ +FF (c, d, a, b, x[10], S13); /* 11 */ +FF (b, c, d, a, x[11], S14); /* 12 */ +FF (a, b, c, d, x[12], S11); /* 13 */ +FF (d, a, b, c, x[13], S12); /* 14 */ +FF (c, d, a, b, x[14], S13); /* 15 */ +FF (b, c, d, a, x[15], S14); /* 16 */ + +/* Round 2 */ +GG (a, b, c, d, x[ 0], S21); /* 17 */ +GG (d, a, b, c, x[ 4], S22); /* 18 */ +GG (c, d, a, b, x[ 8], S23); /* 19 */ +GG (b, c, d, a, x[12], S24); /* 20 */ +GG (a, b, c, d, x[ 1], S21); /* 21 */ +GG (d, a, b, c, x[ 5], S22); /* 22 */ +GG (c, d, a, b, x[ 9], S23); /* 23 */ +GG (b, c, d, a, x[13], S24); /* 24 */ +GG (a, b, c, d, x[ 2], S21); /* 25 */ +GG (d, a, b, c, x[ 6], S22); /* 26 */ +GG (c, d, a, b, x[10], S23); /* 27 */ +GG (b, c, d, a, x[14], S24); /* 28 */ +GG (a, b, c, d, x[ 3], S21); /* 29 */ +GG (d, a, b, c, x[ 7], S22); /* 30 */ +GG (c, d, a, b, x[11], S23); /* 31 */ +GG (b, c, d, a, x[15], S24); /* 32 */ + +/* Round 3 */ +HH (a, b, c, d, x[ 0], S31); /* 33 */ +HH (d, a, b, c, x[ 8], S32); /* 34 */ +HH (c, d, a, b, x[ 4], S33); /* 35 */ +HH (b, c, d, a, x[12], S34); /* 36 */ +HH (a, b, c, d, x[ 2], S31); /* 37 */ +HH (d, a, b, c, x[10], S32); /* 38 */ +HH (c, d, a, b, x[ 6], S33); /* 39 */ +HH (b, c, d, a, x[14], S34); /* 40 */ +HH (a, b, c, d, x[ 1], S31); /* 41 */ +HH (d, a, b, c, x[ 9], S32); /* 42 */ +HH (c, d, a, b, x[ 5], S33); /* 43 */ +HH (b, c, d, a, x[13], S34); /* 44 */ +HH (a, b, c, d, x[ 3], S31); /* 45 */ +HH (d, a, b, c, x[11], S32); /* 46 */ +HH (c, d, a, b, x[ 7], S33); /* 47 */ +HH (b, c, d, a, x[15], S34); /* 48 */ + +state[0] += a; +state[1] += b; +state[2] += c; +state[3] += d; + + /* Zeroize sensitive information.*/ + memset ((POINTER)x, 0, sizeof (x)); +} + + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is a multiple of 4. */ +static void Encode (unsigned char *output, UINT4 *input, unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is a multiple of 4. */ +static void Decode (UINT4 *output, unsigned char *input, unsigned int len) +{ +unsigned int i, j; + +for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} + +//=================================================================== + +unsigned Com_BlockChecksum (void *buffer, int length) +{ + int digest[4]; + unsigned val; + MD4_CTX ctx; + + MD4Init (&ctx); + MD4Update (&ctx, (unsigned char *)buffer, length); + MD4Final ( (unsigned char *)digest, &ctx); + + val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3]; + + return val; +} diff --git a/utils/common/mutex.c b/utils/common/mutex.c new file mode 100644 index 0000000..acb91f2 --- /dev/null +++ b/utils/common/mutex.c @@ -0,0 +1,176 @@ + +#include "cmdlib.h" +#include "threads.h" +#include "mutex.h" + +/* +=================================================================== + +WIN32 + +=================================================================== +*/ +#ifdef WIN32 + +#define USED + +#include + +void MutexLock (mutex_t *m) +{ + CRITICAL_SECTION *crit; + + if (!m) + return; + crit = (CRITICAL_SECTION *) m; + EnterCriticalSection (crit); +} + +void MutexUnlock (mutex_t *m) +{ + CRITICAL_SECTION *crit; + + if (!m) + return; + crit = (CRITICAL_SECTION *) m; + LeaveCriticalSection (crit); +} + +mutex_t *MutexAlloc(void) +{ + CRITICAL_SECTION *crit; + + if (numthreads == 1) + return NULL; + crit = (CRITICAL_SECTION *) malloc(sizeof(CRITICAL_SECTION)); + InitializeCriticalSection (crit); + return (void *) crit; +} + +#endif + +/* +=================================================================== + +OSF1 + +=================================================================== +*/ + +#ifdef __osf__ +#define USED + +#include + +void MutexLock (mutex_t *m) +{ + pthread_mutex_t *my_mutex; + + if (!m) + return; + my_mutex = (pthread_mutex_t *) m; + pthread_mutex_lock (my_mutex); +} + +void MutexUnlock (mutex_t *m) +{ + pthread_mutex_t *my_mutex; + + if (!m) + return; + my_mutex = (pthread_mutex_t *) m; + pthread_mutex_unlock (my_mutex); +} + +mutex_t *MutexAlloc(void) +{ + pthread_mutex_t *my_mutex; + pthread_mutexattr_t mattrib; + + if (numthreads == 1) + return NULL; + my_mutex = malloc (sizeof(*my_mutex)); + if (pthread_mutexattr_create (&mattrib) == -1) + Error ("pthread_mutex_attr_create failed"); + if (pthread_mutexattr_setkind_np (&mattrib, MUTEX_FAST_NP) == -1) + Error ("pthread_mutexattr_setkind_np failed"); + if (pthread_mutex_init (my_mutex, mattrib) == -1) + Error ("pthread_mutex_init failed"); + return (void *) my_mutex; +} + +#endif + +/* +=================================================================== + +IRIX + +=================================================================== +*/ + +#ifdef _MIPS_ISA +#define USED + +#include +#include +#include +#include + +void MutexLock (mutex_t *m) +{ + abilock_t *lck; + + if (!m) + return; + lck = (abilock_t *) m; + spin_lock (lck); +} + +void MutexUnlock (mutex_t *m) +{ + abilock_t *lck; + + if (!m) + return; + lck = (abilock_t *) m; + release_lock (lck); +} + +mutex_t *MutexAlloc(void) +{ + abilock_t *lck; + + if (numthreads == 1) + return NULL; + lck = (abilock_t *) malloc(sizeof(abilock_t)); + init_lock (lck); + return (void *) lck; +} + +#endif + +/* +======================================================================= + + SINGLE THREAD + +======================================================================= +*/ + +#ifndef USED + +void MutexLock (mutex_t *m) +{ +} + +void MutexUnlock (mutex_t *m) +{ +} + +mutex_t *MutexAlloc(void) +{ + return NULL; +} + +#endif diff --git a/utils/common/mutex.h b/utils/common/mutex.h new file mode 100644 index 0000000..8675f36 --- /dev/null +++ b/utils/common/mutex.h @@ -0,0 +1,7 @@ + + +typedef void *mutex_t; + +void MutexLock (mutex_t *m); +void MutexUnlock (mutex_t *m); +mutex_t *MutexAlloc(void); diff --git a/utils/common/polylib.c b/utils/common/polylib.c new file mode 100644 index 0000000..1b34a9f --- /dev/null +++ b/utils/common/polylib.c @@ -0,0 +1,728 @@ + +#include "cmdlib.h" +#include "mathlib.h" +#include "polylib.h" +#include "qfiles.h" //for min_world_coord + +extern int numthreads; + +// counters are only bumped when running single threaded, +// because they are an awefull coherence problem +int c_active_windings; +int c_peak_windings; +int c_winding_allocs; +int c_winding_points; + +void pw(winding_t *w) +{ + int i; + for (i=0 ; inumpoints ; i++) + printf ("(%5.1f, %5.1f, %5.1f)\n",w->p[i][0], w->p[i][1],w->p[i][2]); +} + + +/* +============= +AllocWinding +============= +*/ +winding_t *AllocWinding (int points) +{ + winding_t *w; + int s; + + if (numthreads == 1) + { + c_winding_allocs++; + c_winding_points += points; + c_active_windings++; + if (c_active_windings > c_peak_windings) + c_peak_windings = c_active_windings; + } + s = sizeof(vec_t)*3*points + sizeof(int); + w = malloc (s); + if(!w) { + Error ("AllocWinding: failed to allocate winding"); + } + memset (w, 0, s); + return w; +} + +void FreeWinding (winding_t *w) +{ + if (*(unsigned *)w == 0xdeaddead) + Error ("FreeWinding: freed a freed winding"); + *(unsigned *)w = 0xdeaddead; + + if (numthreads == 1) + c_active_windings--; + free (w); +} + +/* +============ +RemoveColinearPoints +============ +*/ +int c_removed; + +void RemoveColinearPoints (winding_t *w) +{ + int i, j, k; + vec3_t v1, v2; + int nump; + vec3_t p[MAX_POINTS_ON_WINDING]; + + nump = 0; + for (i=0 ; inumpoints ; i++) + { + j = (i+1)%w->numpoints; + k = (i+w->numpoints-1)%w->numpoints; + VectorSubtract (w->p[j], w->p[i], v1); + VectorSubtract (w->p[i], w->p[k], v2); + VectorNormalize(v1,v1); + VectorNormalize(v2,v2); + if (DotProduct(v1, v2) < 0.999) + { + VectorCopy (w->p[i], p[nump]); + nump++; + } + } + + if (nump == w->numpoints) + return; + + if (numthreads == 1) + c_removed += w->numpoints - nump; + w->numpoints = nump; + memcpy (w->p, p, nump*sizeof(p[0])); +} + +/* +============ +WindingPlane +============ +*/ +void WindingPlane (winding_t *w, vec3_t normal, vec_t *dist) +{ + vec3_t v1, v2; + + VectorSubtract (w->p[1], w->p[0], v1); + VectorSubtract (w->p[2], w->p[0], v2); + CrossProduct (v2, v1, normal); + VectorNormalize (normal, normal); + *dist = DotProduct (w->p[0], normal); + +} + +/* +============= +WindingArea +============= +*/ +vec_t WindingArea (winding_t *w) +{ + int i; + vec3_t d1, d2, cross; + vec_t total; + + total = 0; + for (i=2 ; inumpoints ; i++) + { + VectorSubtract (w->p[i-1], w->p[0], d1); + VectorSubtract (w->p[i], w->p[0], d2); + CrossProduct (d1, d2, cross); + total += 0.5 * VectorLength ( cross ); + } + return total; +} + +void WindingBounds (winding_t *w, vec3_t mins, vec3_t maxs) +{ + vec_t v; + int i,j; + + mins[0] = mins[1] = mins[2] = 99999; + maxs[0] = maxs[1] = maxs[2] = -99999; + + for (i=0 ; inumpoints ; i++) + { + for (j=0 ; j<3 ; j++) + { + v = w->p[i][j]; + if (v < mins[j]) + mins[j] = v; + if (v > maxs[j]) + maxs[j] = v; + } + } +} + +/* +============= +WindingCenter +============= +*/ +void WindingCenter (winding_t *w, vec3_t center) +{ + int i; + float scale; + + VectorCopy (vec3_origin, center); + for (i=0 ; inumpoints ; i++) + VectorAdd (w->p[i], center, center); + + scale = 1.0/w->numpoints; + VectorScale (center, scale, center); +} + +/* +================= +BaseWindingForPlane +================= +*/ +winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist) +{ + int i, x; + vec_t max, v; + vec3_t org, vright, vup; + winding_t *w; + +// find the major axis + + max = MIN_WORLD_COORD; + x = -1; + for (i=0 ; i<3; i++) + { + v = fabs(normal[i]); + if (v > max) + { + x = i; + max = v; + } + } + if (x==-1) + Error ("BaseWindingForPlane: no axis found"); + + VectorCopy (vec3_origin, vup); + switch (x) + { + case 0: + case 1: + vup[2] = 1; + break; + case 2: + vup[0] = 1; + break; + } + + v = DotProduct (vup, normal); + VectorMA (vup, -v, normal, vup); + VectorNormalize (vup, vup); + + VectorScale (normal, dist, org); + + CrossProduct (vup, normal, vright); + + VectorScale (vup, 8192, vup); + VectorScale (vright, 8192, vright); + +// project a really big axis aligned box onto the plane + w = AllocWinding (4); + + VectorSubtract (org, vright, w->p[0]); + VectorAdd (w->p[0], vup, w->p[0]); + + VectorAdd (org, vright, w->p[1]); + VectorAdd (w->p[1], vup, w->p[1]); + + VectorAdd (org, vright, w->p[2]); + VectorSubtract (w->p[2], vup, w->p[2]); + + VectorSubtract (org, vright, w->p[3]); + VectorSubtract (w->p[3], vup, w->p[3]); + + w->numpoints = 4; + + return w; +} + +/* +================== +CopyWinding +================== +*/ +winding_t *CopyWinding (winding_t *w) +{ + int size; + winding_t *c; + + c = AllocWinding (w->numpoints); + size = (int)((winding_t *)0)->p[w->numpoints]; + memcpy (c, w, size); + return c; +} + +/* +================== +ReverseWinding +================== +*/ +winding_t *ReverseWinding (winding_t *w) +{ + int i; + winding_t *c; + + c = AllocWinding (w->numpoints); + for (i=0 ; inumpoints ; i++) + { + VectorCopy (w->p[w->numpoints-1-i], c->p[i]); + } + c->numpoints = w->numpoints; + return c; +} + + +/* +============= +ClipWindingEpsilon +============= +*/ +void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist, + vec_t epsilon, winding_t **front, winding_t **back) +{ + vec_t dists[MAX_POINTS_ON_WINDING+4]; + int sides[MAX_POINTS_ON_WINDING+4]; + int counts[3]; + static vec_t dot; // VC 4.2 optimizer bug if not static + int i, j; + vec_t *p1, *p2; + vec3_t mid; + winding_t *f, *b; + int maxpts; + + counts[0] = counts[1] = counts[2] = 0; + + if (in->numpoints > MAX_POINTS_ON_WINDING) + { + Error ("ClipWindingEpsilon: %d > MAX_POINTS_ON_WINDING (%d)\nSample points: (%5.2f %5.2f %5.2f) (%5.2f %5.2f %5.2f) (%5.2f %5.2f %5.2f)", + in->numpoints,MAX_POINTS_ON_WINDING, + in->p[0][0],in->p[0][1],in->p[0][2], + in->p[10][0],in->p[10][1],in->p[10][2], + in->p[20][0],in->p[20][1],in->p[20][2]); + } + +// determine sides for each point + for (i=0 ; inumpoints ; i++) + { + dot = DotProduct (in->p[i], normal); + dot -= dist; + dists[i] = dot; + if (dot > epsilon) + sides[i] = SIDE_FRONT; + else if (dot < -epsilon) + sides[i] = SIDE_BACK; + else + { + sides[i] = SIDE_ON; + } + counts[sides[i]]++; + } + sides[i] = sides[0]; + dists[i] = dists[0]; + + *front = *back = NULL; + + if (!counts[0]) + { + *back = CopyWinding (in); + return; + } + if (!counts[1]) + { + *front = CopyWinding (in); + return; + } + + maxpts = in->numpoints+4; // cant use counts[0]+2 because + // of fp grouping errors + + *front = f = AllocWinding (maxpts); + *back = b = AllocWinding (maxpts); + + for (i=0 ; inumpoints ; i++) + { + p1 = in->p[i]; + + if (sides[i] == SIDE_ON) + { + VectorCopy (p1, f->p[f->numpoints]); + f->numpoints++; + VectorCopy (p1, b->p[b->numpoints]); + b->numpoints++; + continue; + } + + if (sides[i] == SIDE_FRONT) + { + VectorCopy (p1, f->p[f->numpoints]); + f->numpoints++; + } + if (sides[i] == SIDE_BACK) + { + VectorCopy (p1, b->p[b->numpoints]); + b->numpoints++; + } + + if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) + continue; + + // generate a split point + p2 = in->p[(i+1)%in->numpoints]; + + dot = dists[i] / (dists[i]-dists[i+1]); + for (j=0 ; j<3 ; j++) + { // avoid round off error when possible + if (normal[j] == 1) + mid[j] = dist; + else if (normal[j] == -1) + mid[j] = -dist; + else + mid[j] = p1[j] + dot*(p2[j]-p1[j]); + } + + VectorCopy (mid, f->p[f->numpoints]); + f->numpoints++; + VectorCopy (mid, b->p[b->numpoints]); + b->numpoints++; + } + + if (f->numpoints > maxpts || b->numpoints > maxpts) + Error ("ClipWinding: points exceeded estimate"); + if (f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING) + Error ("ClipWinding: MAX_POINTS_ON_WINDING"); +} + + +/* +============= +ChopWindingInPlace +============= +*/ +void ChopWindingInPlace (winding_t **inout, vec3_t normal, vec_t dist, vec_t epsilon) +{ + winding_t *in; + vec_t dists[MAX_POINTS_ON_WINDING+4]; + int sides[MAX_POINTS_ON_WINDING+4]; + int counts[3]; + static vec_t dot; // VC 4.2 optimizer bug if not static + int i, j; + vec_t *p1, *p2; + vec3_t mid; + winding_t *f; + int maxpts; + + in = *inout; + counts[0] = counts[1] = counts[2] = 0; + +// determine sides for each point + for (i=0 ; inumpoints ; i++) + { + dot = DotProduct (in->p[i], normal); + dot -= dist; + dists[i] = dot; + if (dot > epsilon) + sides[i] = SIDE_FRONT; + else if (dot < -epsilon) + sides[i] = SIDE_BACK; + else + { + sides[i] = SIDE_ON; + } + counts[sides[i]]++; + } + sides[i] = sides[0]; + dists[i] = dists[0]; + + if (!counts[0]) + { + FreeWinding (in); + *inout = NULL; + return; + } + if (!counts[1]) + return; // inout stays the same + + maxpts = in->numpoints+4; // cant use counts[0]+2 because + // of fp grouping errors + + f = AllocWinding (maxpts); + + for (i=0 ; inumpoints ; i++) + { + p1 = in->p[i]; + + if (sides[i] == SIDE_ON) + { + VectorCopy (p1, f->p[f->numpoints]); + f->numpoints++; + continue; + } + + if (sides[i] == SIDE_FRONT) + { + VectorCopy (p1, f->p[f->numpoints]); + f->numpoints++; + } + + if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) + continue; + + // generate a split point + p2 = in->p[(i+1)%in->numpoints]; + + dot = dists[i] / (dists[i]-dists[i+1]); + for (j=0 ; j<3 ; j++) + { // avoid round off error when possible + if (normal[j] == 1) + mid[j] = dist; + else if (normal[j] == -1) + mid[j] = -dist; + else + mid[j] = p1[j] + dot*(p2[j]-p1[j]); + } + + VectorCopy (mid, f->p[f->numpoints]); + f->numpoints++; + } + + if (f->numpoints > maxpts) + Error ("ClipWinding: points exceeded estimate"); + if (f->numpoints > MAX_POINTS_ON_WINDING) + Error ("ClipWinding: MAX_POINTS_ON_WINDING"); + + FreeWinding (in); + *inout = f; +} + + +/* +================= +ChopWinding + +Returns the fragment of in that is on the front side +of the cliping plane. The original is freed. +================= +*/ +winding_t *ChopWinding (winding_t *in, vec3_t normal, vec_t dist) +{ + winding_t *f, *b; + + ClipWindingEpsilon (in, normal, dist, ON_EPSILON, &f, &b); + FreeWinding (in); + if (b) + FreeWinding (b); + return f; +} + + +/* +================= +CheckWinding + +================= +*/ +void CheckWinding (winding_t *w) +{ + int i, j; + vec_t *p1, *p2; + vec_t d, edgedist; + vec3_t dir, edgenormal, facenormal; + vec_t area; + vec_t facedist; + + if (w->numpoints < 3) + Error ("CheckWinding: %i points",w->numpoints); + + area = WindingArea(w); + if (area < 1) + Error ("CheckWinding: %f area", area); + + WindingPlane (w, facenormal, &facedist); + + for (i=0 ; inumpoints ; i++) + { + p1 = w->p[i]; + + for (j=0 ; j<3 ; j++) + if (p1[j] > MAX_WORLD_COORD || p1[j] < MIN_WORLD_COORD) + Error ("CheckFace: BUGUS_RANGE: %f",p1[j]); + + j = i+1 == w->numpoints ? 0 : i+1; + + // check the point is on the face plane + d = DotProduct (p1, facenormal) - facedist; + if (d < -ON_EPSILON || d > ON_EPSILON) + Error ("CheckWinding: point off plane"); + + // check the edge isnt degenerate + p2 = w->p[j]; + VectorSubtract (p2, p1, dir); + + if (VectorLength (dir) < ON_EPSILON) + Error ("CheckWinding: degenerate edge"); + + CrossProduct (facenormal, dir, edgenormal); + VectorNormalize (edgenormal, edgenormal); + edgedist = DotProduct (p1, edgenormal); + edgedist += ON_EPSILON; + + // all other points must be on front side + for (j=0 ; jnumpoints ; j++) + { + if (j == i) + continue; + d = DotProduct (w->p[j], edgenormal); + if (d > edgedist) + Error ("CheckWinding: non-convex"); + } + } +} + + +/* +============ +WindingOnPlaneSide +============ +*/ +int WindingOnPlaneSide (winding_t *w, vec3_t normal, vec_t dist) +{ + qboolean front, back; + int i; + vec_t d; + + front = qfalse; + back = qfalse; + for (i=0 ; inumpoints ; i++) + { + d = DotProduct (w->p[i], normal) - dist; + if (d < -ON_EPSILON) + { + if (front) + return SIDE_CROSS; + back = qtrue; + continue; + } + if (d > ON_EPSILON) + { + if (back) + return SIDE_CROSS; + front = qtrue; + continue; + } + } + + if (back) + return SIDE_BACK; + if (front) + return SIDE_FRONT; + return SIDE_ON; +} + + +/* +================= +AddWindingToConvexHull + +Both w and *hull are on the same plane +================= +*/ +#define MAX_HULL_POINTS 128 +void AddWindingToConvexHull( winding_t *w, winding_t **hull, vec3_t normal ) { + int i, j, k; + float *p, *copy; + vec3_t dir; + float d; + int numHullPoints, numNew; + vec3_t hullPoints[MAX_HULL_POINTS]; + vec3_t newHullPoints[MAX_HULL_POINTS]; + vec3_t hullDirs[MAX_HULL_POINTS]; + qboolean hullSide[MAX_HULL_POINTS]; + qboolean outside; + + if ( !*hull ) { + *hull = CopyWinding( w ); + return; + } + + numHullPoints = (*hull)->numpoints; + memcpy( hullPoints, (*hull)->p, numHullPoints * sizeof(vec3_t) ); + + for ( i = 0 ; i < w->numpoints ; i++ ) { + p = w->p[i]; + + // calculate hull side vectors + for ( j = 0 ; j < numHullPoints ; j++ ) { + k = ( j + 1 ) % numHullPoints; + + VectorSubtract( hullPoints[k], hullPoints[j], dir ); + VectorNormalize( dir, dir ); + CrossProduct( normal, dir, hullDirs[j] ); + } + + outside = qfalse; + for ( j = 0 ; j < numHullPoints ; j++ ) { + VectorSubtract( p, hullPoints[j], dir ); + d = DotProduct( dir, hullDirs[j] ); + if ( d >= ON_EPSILON ) { + outside = qtrue; + } + if ( d >= -ON_EPSILON ) { + hullSide[j] = qtrue; + } else { + hullSide[j] = qfalse; + } + } + + // if the point is effectively inside, do nothing + if ( !outside ) { + continue; + } + + // find the back side to front side transition + for ( j = 0 ; j < numHullPoints ; j++ ) { + if ( !hullSide[ j % numHullPoints ] && hullSide[ (j + 1) % numHullPoints ] ) { + break; + } + } + if ( j == numHullPoints ) { + continue; + } + + // insert the point here + VectorCopy( p, newHullPoints[0] ); + numNew = 1; + + // copy over all points that aren't double fronts + j = (j+1)%numHullPoints; + for ( k = 0 ; k < numHullPoints ; k++ ) { + if ( hullSide[ (j+k) % numHullPoints ] && hullSide[ (j+k+1) % numHullPoints ] ) { + continue; + } + copy = hullPoints[ (j+k+1) % numHullPoints ]; + VectorCopy( copy, newHullPoints[numNew] ); + numNew++; + } + + numHullPoints = numNew; + memcpy( hullPoints, newHullPoints, numHullPoints * sizeof(vec3_t) ); + } + + FreeWinding( *hull ); + w = AllocWinding( numHullPoints ); + w->numpoints = numHullPoints; + *hull = w; + memcpy( w->p, hullPoints, numHullPoints * sizeof(vec3_t) ); +} + + diff --git a/utils/common/polylib.h b/utils/common/polylib.h new file mode 100644 index 0000000..1f4e5cf --- /dev/null +++ b/utils/common/polylib.h @@ -0,0 +1,36 @@ + +typedef struct +{ + int numpoints; + vec3_t p[4]; // variable sized +} winding_t; + +#define MAX_POINTS_ON_WINDING 64 + +// you can define on_epsilon in the makefile as tighter +#ifndef ON_EPSILON +#define ON_EPSILON 0.1 +#endif + +winding_t *AllocWinding (int points); +vec_t WindingArea (winding_t *w); +void WindingCenter (winding_t *w, vec3_t center); +void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist, + vec_t epsilon, winding_t **front, winding_t **back); +winding_t *ChopWinding (winding_t *in, vec3_t normal, vec_t dist); +winding_t *CopyWinding (winding_t *w); +winding_t *ReverseWinding (winding_t *w); +winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist); +void CheckWinding (winding_t *w); +void WindingPlane (winding_t *w, vec3_t normal, vec_t *dist); +void RemoveColinearPoints (winding_t *w); +int WindingOnPlaneSide (winding_t *w, vec3_t normal, vec_t dist); +void FreeWinding (winding_t *w); +void WindingBounds (winding_t *w, vec3_t mins, vec3_t maxs); + +void AddWindingToConvexHull( winding_t *w, winding_t **hull, vec3_t normal ); + +void ChopWindingInPlace (winding_t **w, vec3_t normal, vec_t dist, vec_t epsilon); +// frees the original if clipped + +void pw(winding_t *w); diff --git a/utils/common/polyset.h b/utils/common/polyset.h new file mode 100644 index 0000000..1633e16 --- /dev/null +++ b/utils/common/polyset.h @@ -0,0 +1,38 @@ +#ifndef __POLYSET_H__ +#define __POLYSET_H__ + +#define POLYSET_MAXTRIANGLES 4096 +#define POLYSET_MAXPOLYSETS 64 + +typedef float st_t[2]; +typedef float rgb_t[3]; + +typedef struct { + vec3_t verts[3]; + vec3_t normals[3]; + st_t texcoords[3]; + int normal_recomputed[3]; +} triangle_t; + +typedef struct +{ + char name[100]; + char materialname[100]; + triangle_t *triangles; + int numtriangles; +} polyset_t; + +typedef struct +{ + triangle_t *triangle; + int which_vert; + int can_use; +}normal_compute_t; + +polyset_t *Polyset_LoadSets( const char *file, int *numpolysets, int maxTrisPerSet ); +polyset_t *Polyset_CollapseSets( polyset_t *psets, int numpolysets ); +polyset_t *Polyset_SplitSets( polyset_t *psets, int numpolysets, int *pNumNewPolysets, int maxTris ); +void Polyset_SnapSets( polyset_t *psets, int numpolysets ); +void Polyset_ComputeNormals( polyset_t *psets, int numpolysets ); + +#endif diff --git a/utils/common/qfiles.h b/utils/common/qfiles.h new file mode 100644 index 0000000..c5de2a2 --- /dev/null +++ b/utils/common/qfiles.h @@ -0,0 +1,504 @@ +#ifndef __QFILES_H__ +#define __QFILES_H__ + +// +// qfiles.h: quake file formats +// This file must be identical in the quake and utils directories +// + +// surface geometry should not exceed these limits +#define SHADER_MAX_VERTEXES 1000 +#define SHADER_MAX_INDEXES (6*SHADER_MAX_VERTEXES) + + +// the maximum size of game reletive pathnames +#define MAX_QPATH 64 + +/* +======================================================================== + +QVM files + +======================================================================== +*/ + +#define VM_MAGIC 0x12721444 +typedef struct { + int vmMagic; + + int instructionCount; + + int codeOffset; + int codeLength; + + int dataOffset; + int dataLength; + int litLength; // ( dataLength - litLength ) should be byteswapped on load + int bssLength; // zero filled memory appended to datalength +} vmHeader_t; + +/* +======================================================================== + +PCX files are used for 8 bit images + +======================================================================== +*/ + +typedef struct { + char manufacturer; + char version; + char encoding; + char bits_per_pixel; + unsigned short xmin,ymin,xmax,ymax; + unsigned short hres,vres; + unsigned char palette[48]; + char reserved; + char color_planes; + unsigned short bytes_per_line; + unsigned short palette_type; + char filler[58]; + unsigned char data; // unbounded +} pcx_t; + + +/* +======================================================================== + +TGA files are used for 24/32 bit images + +======================================================================== +*/ + +typedef struct _TargaHeader { + unsigned char id_length, colormap_type, image_type; + unsigned short colormap_index, colormap_length; + unsigned char colormap_size; + unsigned short x_origin, y_origin, width, height; + unsigned char pixel_size, attributes; +} TargaHeader; + + + +/* +======================================================================== + +.MD3 triangle model file format + +======================================================================== +*/ + +#define MD3_IDENT (('3'<<24)+('P'<<16)+('D'<<8)+'I') +#define MD3_VERSION 15 + +// limits +#define MD3_MAX_LODS 3 +#define MD3_MAX_TRIANGLES 8192 // per surface +#define MD3_MAX_VERTS 4096 // per surface +#define MD3_MAX_SHADERS 256 // per surface +#define MD3_MAX_FRAMES 1024 // per model +#define MD3_MAX_SURFACES 32 + 32 // per model +#define MD3_MAX_TAGS 16 // per frame + +// vertex scales +#define MD3_XYZ_SCALE (1.0/64) + +typedef struct md3Frame_s { + vec3_t bounds[2]; + vec3_t localOrigin; + float radius; + char name[16]; +} md3Frame_t; + +typedef struct md3Tag_s { + char name[MAX_QPATH]; // tag name + vec3_t origin; + vec3_t axis[3]; +} md3Tag_t; + +/* +** md3Surface_t +** +** CHUNK SIZE +** header sizeof( md3Surface_t ) +** shaders sizeof( md3Shader_t ) * numShaders +** triangles[0] sizeof( md3Triangle_t ) * numTriangles +** st sizeof( md3St_t ) * numVerts +** XyzNormals sizeof( md3XyzNormal_t ) * numVerts * numFrames +*/ +typedef struct { + int ident; // + + char name[MAX_QPATH]; // polyset name + + int flags; + int numFrames; // all surfaces in a model should have the same + + int numShaders; // all surfaces in a model should have the same + int numVerts; + + int numTriangles; + int ofsTriangles; + + int ofsShaders; // offset from start of md3Surface_t + int ofsSt; // texture coords are common for all frames + int ofsXyzNormals; // numVerts * numFrames + + int ofsEnd; // next surface follows +} md3Surface_t; + +typedef struct { + char name[MAX_QPATH]; + int shaderIndex; // for in-game use +} md3Shader_t; + +typedef struct { + int indexes[3]; +} md3Triangle_t; + +typedef struct { + float st[2]; +} md3St_t; + +typedef struct { + short xyz[3]; + short normal; +} md3XyzNormal_t; + +typedef struct { + int ident; + int version; + + char name[MAX_QPATH]; // model name + + int flags; + + int numFrames; + int numTags; + int numSurfaces; + + int numSkins; + + int ofsFrames; // offset for first frame + int ofsTags; // numFrames * numTags + int ofsSurfaces; // first surface, others follow + + int ofsEnd; // end of file +} md3Header_t; + + +/* +============================================================================== + +MD4 file format + +============================================================================== +*/ + +#define MD4_IDENT (('5'<<24)+('M'<<16)+('D'<<8)+'R') +#define MD4_VERSION 2 +#define MD4_MAX_BONES 128 + +typedef struct { + int boneIndex; // these are indexes into the boneReferences, + float boneWeight; // not the global per-frame bone list + vec3_t offset; +} md4Weight_t; + +typedef struct { + vec3_t normal; + vec2_t texCoords; + int numWeights; + md4Weight_t weights[1]; // variable sized +} md4Vertex_t; + +typedef struct { + int indexes[3]; +} md4Triangle_t; + +typedef struct { + int ident; + + char name[MAX_QPATH]; // polyset name + char shader[MAX_QPATH]; + int shaderIndex; // for in-game use + + int ofsHeader; // this will be a negative number + + int numVerts; + int ofsVerts; + + int numTriangles; + int ofsTriangles; + + // Bone references are a set of ints representing all the bones + // present in any vertex weights for this surface. This is + // needed because a model may have surfaces that need to be + // drawn at different sort times, and we don't want to have + // to re-interpolate all the bones for each surface. + int numBoneReferences; + int ofsBoneReferences; + + int ofsEnd; // next surface follows +} md4Surface_t; + +typedef struct { + float matrix[3][4]; +} md4Bone_t; + +typedef struct { + vec3_t bounds[2]; // bounds of all surfaces of all LOD's for this frame + vec3_t localOrigin; // midpoint of bounds, used for sphere cull + float radius; // dist from localOrigin to corner + char name[16]; + md4Bone_t bones[1]; // [numBones] +} md4Frame_t; + +typedef struct { + unsigned char Comp[24]; // MC_COMP_BYTES is in MatComp.h, but don't want to couple +} md4CompBone_t; + +typedef struct { + vec3_t bounds[2]; // bounds of all surfaces of all LOD's for this frame + vec3_t localOrigin; // midpoint of bounds, used for sphere cull + float radius; // dist from localOrigin to corner + md4CompBone_t bones[1]; // [numBones] +} md4CompFrame_t; + +typedef struct { + int numSurfaces; + int ofsSurfaces; // first surface, others follow + int ofsEnd; // next lod follows +} md4LOD_t; + +typedef struct { + int boneIndex; + char name[32]; +} md4Tag_t; + +typedef struct { + int ident; + int version; + + char name[MAX_QPATH]; // model name + + // frames and bones are shared by all levels of detail + int numFrames; + int numBones; + + // ofsFrames is negative for compressed frames...Which is then a md4CompFrame_t[numFrames] + int ofsFrames; // md4Frame_t[numFrames] + + // each level of detail has completely separate sets of surfaces + int numLODs; + int ofsLODs; + + int numTags; + int ofsTags; + + int ofsEnd; // end of file +} md4Header_t; + + +/* +============================================================================== + + .BSP file format + +============================================================================== +*/ + + +#define BSP_IDENT (('P'<<24)+('S'<<16)+('B'<<8)+'I') + // little-endian "IBSP" + +#define BSP_VERSION 46 + + +// there shouldn't be any problem with increasing these values at the +// expense of more memory allocation in the utilities +#define MAX_MAP_MODELS 0x400 +#define MAX_MAP_BRUSHES 0x8000 +#define MAX_MAP_ENTITIES 0x800 +#define MAX_MAP_ENTSTRING 0x40000 +#define MAX_MAP_SHADERS 0x400 + +#define MAX_MAP_AREAS 0x100 // MAX_MAP_AREA_BYTES in q_shared must match! +#define MAX_MAP_FOGS 0x100 +#define MAX_MAP_PLANES 0x20000 +#define MAX_MAP_NODES 0x20000 +#define MAX_MAP_BRUSHSIDES 0x20000 +#define MAX_MAP_LEAFS 0x20000 +#define MAX_MAP_LEAFFACES 0x20000 +#define MAX_MAP_LEAFBRUSHES 0x40000 +#define MAX_MAP_PORTALS 0x20000 +#define MAX_MAP_LIGHTING 0x800000 +#define MAX_MAP_LIGHTGRID 0x800000 +#define MAX_MAP_VISIBILITY 0x400000 + +#define MAX_MAP_DRAW_SURFS 0x20000 +#define MAX_MAP_DRAW_VERTS 0x80000 +#define MAX_MAP_DRAW_INDEXES 0x80000 + + +// key / value pair sizes in the entities lump +#define MAX_KEY 32 +#define MAX_VALUE 1024 + +// the editor uses these predefined yaw angles to orient entities up or down +#define ANGLE_UP -1 +#define ANGLE_DOWN -2 + +#define LIGHTMAP_WIDTH 128 +#define LIGHTMAP_HEIGHT 128 + + +#define MAX_WORLD_COORD ( 128*1024 ) +#define MIN_WORLD_COORD ( -128*1024 ) +#define WORLD_SIZE ( MAX_WORLD_COORD - MIN_WORLD_COORD ) + +//============================================================================= + + +typedef struct { + int fileofs, filelen; +} lump_t; + +#define LUMP_ENTITIES 0 +#define LUMP_SHADERS 1 +#define LUMP_PLANES 2 +#define LUMP_NODES 3 +#define LUMP_LEAFS 4 +#define LUMP_LEAFSURFACES 5 +#define LUMP_LEAFBRUSHES 6 +#define LUMP_MODELS 7 +#define LUMP_BRUSHES 8 +#define LUMP_BRUSHSIDES 9 +#define LUMP_DRAWVERTS 10 +#define LUMP_DRAWINDEXES 11 +#define LUMP_FOGS 12 +#define LUMP_SURFACES 13 +#define LUMP_LIGHTMAPS 14 +#define LUMP_LIGHTGRID 15 +#define LUMP_VISIBILITY 16 +#define HEADER_LUMPS 17 + +typedef struct { + int ident; + int version; + + lump_t lumps[HEADER_LUMPS]; +} dheader_t; + +typedef struct { + float mins[3], maxs[3]; + int firstSurface, numSurfaces; + int firstBrush, numBrushes; +} dmodel_t; + +typedef struct { + char shader[MAX_QPATH]; + int surfaceFlags; + int contentFlags; +} dshader_t; + +// planes x^1 is allways the opposite of plane x + +typedef struct { + float normal[3]; + float dist; +} dplane_t; + +typedef struct { + int planeNum; + int children[2]; // negative numbers are -(leafs+1), not nodes + int mins[3]; // for frustom culling + int maxs[3]; +} dnode_t; + +typedef struct { + int cluster; // -1 = opaque cluster (do I still store these?) + int area; + + int mins[3]; // for frustum culling + int maxs[3]; + + int firstLeafSurface; + int numLeafSurfaces; + + int firstLeafBrush; + int numLeafBrushes; +} dleaf_t; + +typedef struct { + int planeNum; // positive plane side faces out of the leaf + int shaderNum; +} dbrushside_t; + +typedef struct { + int firstSide; + int numSides; + int shaderNum; // the shader that determines the contents flags +} dbrush_t; + +typedef struct { + char shader[MAX_QPATH]; + int brushNum; + int visibleSide; // the brush side that ray tests need to clip against (-1 == none) +} dfog_t; + +typedef struct { + vec3_t xyz; + float st[2]; + float lightmap[2]; + vec3_t normal; + byte color[4]; +} drawVert_t; + +typedef enum { + MST_BAD, + MST_PLANAR, + MST_PATCH, + MST_TRIANGLE_SOUP, + MST_FLARE +} mapSurfaceType_t; + +typedef struct { + int shaderNum; + int fogNum; + int surfaceType; + + int firstVert; + int numVerts; + + int firstIndex; + int numIndexes; + + int lightmapNum; + int lightmapX, lightmapY; + int lightmapWidth, lightmapHeight; + + vec3_t lightmapOrigin; + vec3_t lightmapVecs[3]; // for patches, [0] and [1] are lodbounds + + int patchWidth; + int patchHeight; +} dsurface_t; + + + +typedef enum //# hunkAllocType_e +{ + HA_MISC, + HA_MAP, + HA_SHADERS, + HA_LIGHTING, + HA_FOG, + HA_PATCHES, + HA_VIS, + HA_SUBMODELS, + HA_MODELS, + MAX_HA_TYPES +} hunkAllocType_t; + +#endif diff --git a/utils/common/scriplib.c b/utils/common/scriplib.c new file mode 100644 index 0000000..f2753aa --- /dev/null +++ b/utils/common/scriplib.c @@ -0,0 +1,358 @@ +// scriplib.c + +#include "cmdlib.h" +#include "scriplib.h" + +/* +============================================================================= + + PARSING STUFF + +============================================================================= +*/ + +typedef struct +{ + char filename[1024]; + char *buffer,*script_p,*end_p; + int line; +} script_t; + +#define MAX_INCLUDES 8 +script_t scriptstack[MAX_INCLUDES]; +script_t *script; +int scriptline; + +char token[MAXTOKEN]; +qboolean endofscript; +qboolean tokenready; // only qtrue if UnGetToken was just called + +/* +============== +AddScriptToStack +============== +*/ +void AddScriptToStack( const char *filename ) { + int size; + + script++; + if (script == &scriptstack[MAX_INCLUDES]) + Error ("script file exceeded MAX_INCLUDES"); + strcpy (script->filename, ExpandPath (filename) ); + + size = LoadFile (script->filename, (void **)&script->buffer); + + printf ("entering %s\n", script->filename); + + script->line = 1; + + script->script_p = script->buffer; + script->end_p = script->buffer + size; +} + + +/* +============== +LoadScriptFile +============== +*/ +void LoadScriptFile( const char *filename ) { + script = scriptstack; + AddScriptToStack (filename); + + endofscript = qfalse; + tokenready = qfalse; +} + + +/* +============== +ParseFromMemory +============== +*/ +void ParseFromMemory (char *buffer, int size) +{ + script = scriptstack; + script++; + if (script == &scriptstack[MAX_INCLUDES]) + Error ("script file exceeded MAX_INCLUDES"); + strcpy (script->filename, "memory buffer" ); + + script->buffer = buffer; + script->line = 1; + script->script_p = script->buffer; + script->end_p = script->buffer + size; + + endofscript = qfalse; + tokenready = qfalse; +} + + +/* +============== +UnGetToken + +Signals that the current token was not used, and should be reported +for the next GetToken. Note that + +GetToken (qtrue); +UnGetToken (); +GetToken (qfalse); + +could cross a line boundary. +============== +*/ +void UnGetToken (void) +{ + tokenready = qtrue; +} + + +qboolean EndOfScript (qboolean crossline) +{ + if (!crossline) + Error ("Line %i is incomplete\n",scriptline); + + if (!strcmp (script->filename, "memory buffer")) + { + endofscript = qtrue; + return qfalse; + } + + if (script->buffer) + { + free (script->buffer); + script->buffer = NULL; + } + if (script == scriptstack+1) + { + endofscript = qtrue; + return qfalse; + } + script--; + scriptline = script->line; + printf ("returning to %s\n", script->filename); + return GetToken (crossline); +} + +/* +============== +GetToken +============== +*/ +qboolean GetToken (qboolean crossline) +{ + char *token_p; + + if (tokenready) // is a token allready waiting? + { + tokenready = qfalse; + return qtrue; + } + + if (script->script_p >= script->end_p) + return EndOfScript (crossline); + +// +// skip space +// +skipspace: + while (*script->script_p <= 32) + { + if (script->script_p >= script->end_p) + return EndOfScript (crossline); + if (*script->script_p++ == '\n') + { + if (!crossline) + Error ("Line %i is incomplete\n",scriptline); + scriptline = script->line++; + } + } + + if (script->script_p >= script->end_p) + return EndOfScript (crossline); + + // ; # // comments + if (*script->script_p == ';' || *script->script_p == '#' + || ( script->script_p[0] == '/' && script->script_p[1] == '/') ) + { + if (!crossline) + Error ("Line %i is incomplete\n",scriptline); + while (*script->script_p++ != '\n') + if (script->script_p >= script->end_p) + return EndOfScript (crossline); + scriptline = script->line++; + goto skipspace; + } + + // /* */ comments + if (script->script_p[0] == '/' && script->script_p[1] == '*') + { + if (!crossline) + Error ("Line %i is incomplete\n",scriptline); + script->script_p+=2; + while (script->script_p[0] != '*' && script->script_p[1] != '/') + { + if ( *script->script_p == '\n' ) { + scriptline = script->line++; + } + script->script_p++; + if (script->script_p >= script->end_p) + return EndOfScript (crossline); + } + script->script_p += 2; + goto skipspace; + } + +// +// copy token +// + token_p = token; + + if (*script->script_p == '"') + { + // quoted token + script->script_p++; + while (*script->script_p != '"') + { + *token_p++ = *script->script_p++; + if (script->script_p == script->end_p) + break; + if (token_p == &token[MAXTOKEN]) + Error ("Token too large on line %i\n",scriptline); + } + script->script_p++; + } + else // regular token + while ( *script->script_p > 32 && *script->script_p != ';') + { + *token_p++ = *script->script_p++; + if (script->script_p == script->end_p) + break; + if (token_p == &token[MAXTOKEN]) + Error ("Token too large on line %i\n",scriptline); + } + + *token_p = 0; + + if (!strcmp (token, "$include")) + { + GetToken (qfalse); + AddScriptToStack (token); + return GetToken (crossline); + } + + return qtrue; +} + + +/* +============== +TokenAvailable + +Returns qtrue if there is another token on the line +============== +*/ +qboolean TokenAvailable (void) { + int oldLine; + qboolean r; + + oldLine = script->line; + r = GetToken( qtrue ); + if ( !r ) { + return qfalse; + } + UnGetToken(); + if ( oldLine == script->line ) { + return qtrue; + } + return qfalse; +} + + +//===================================================================== + + +void MatchToken( char *match ) { + GetToken( qtrue ); + + if ( strcmp( token, match ) ) { + Error( "MatchToken( \"%s\" ) failed at line %i", match, scriptline ); + } +} + + +void Parse1DMatrix (int x, vec_t *m) { + int i; + + MatchToken( "(" ); + + for (i = 0 ; i < x ; i++) { + GetToken( qfalse ); + m[i] = atof(token); + } + + MatchToken( ")" ); +} + +void Parse2DMatrix (int y, int x, vec_t *m) { + int i; + + MatchToken( "(" ); + + for (i = 0 ; i < y ; i++) { + Parse1DMatrix (x, m + i * x); + } + + MatchToken( ")" ); +} + +void Parse3DMatrix (int z, int y, int x, vec_t *m) { + int i; + + MatchToken( "(" ); + + for (i = 0 ; i < z ; i++) { + Parse2DMatrix (y, x, m + i * x*y); + } + + MatchToken( ")" ); +} + + +void Write1DMatrix (FILE *f, int x, vec_t *m) { + int i; + + fprintf (f, "( "); + for (i = 0 ; i < x ; i++) { + if (m[i] == (int)m[i] ) { + fprintf (f, "%i ", (int)m[i]); + } else { + fprintf (f, "%f ", m[i]); + } + } + fprintf (f, ")"); +} + +void Write2DMatrix (FILE *f, int y, int x, vec_t *m) { + int i; + + fprintf (f, "( "); + for (i = 0 ; i < y ; i++) { + Write1DMatrix (f, x, m + i*x); + fprintf (f, " "); + } + fprintf (f, ")\n"); +} + + +void Write3DMatrix (FILE *f, int z, int y, int x, vec_t *m) { + int i; + + fprintf (f, "(\n"); + for (i = 0 ; i < z ; i++) { + Write2DMatrix (f, y, x, m + i*(x*y) ); + } + fprintf (f, ")\n"); +} + diff --git a/utils/common/scriplib.h b/utils/common/scriplib.h new file mode 100644 index 0000000..bc091d8 --- /dev/null +++ b/utils/common/scriplib.h @@ -0,0 +1,34 @@ +// scriplib.h + +#ifndef __CMDLIB__ +#include "cmdlib.h" +#endif +#ifndef __MATHLIB__ +#include "mathlib.h" +#endif + +#define MAXTOKEN 1024 + +extern char token[MAXTOKEN]; +extern char *scriptbuffer,*script_p,*scriptend_p; +extern int grabbed; +extern int scriptline; +extern qboolean endofscript; + + +void LoadScriptFile( const char *filename ); +void ParseFromMemory (char *buffer, int size); + +qboolean GetToken (qboolean crossline); +void UnGetToken (void); +qboolean TokenAvailable (void); + +void MatchToken( char *match ); + +void Parse1DMatrix (int x, vec_t *m); +void Parse2DMatrix (int y, int x, vec_t *m); +void Parse3DMatrix (int z, int y, int x, vec_t *m); + +void Write1DMatrix (FILE *f, int x, vec_t *m); +void Write2DMatrix (FILE *f, int y, int x, vec_t *m); +void Write3DMatrix (FILE *f, int z, int y, int x, vec_t *m); diff --git a/utils/common/surfaceflags.h b/utils/common/surfaceflags.h new file mode 100644 index 0000000..6f178a6 --- /dev/null +++ b/utils/common/surfaceflags.h @@ -0,0 +1,69 @@ +// Copyright (C) 1999-2000 Id Software, Inc. +// +// This file must be identical in the quake and utils directories + +// contents flags are seperate bits +// a given brush can contribute multiple content bits + +// these definitions also need to be in q_shared.h! + +#define CONTENTS_NONE 0 +#define CONTENTS_SOLID 1 // an eye is never valid in a solid +#define CONTENTS_LAVA 8 +#define CONTENTS_SLIME 16 +#define CONTENTS_WATER 32 +#define CONTENTS_FOG 64 +#define CONTENTS_LADDER 128 + +#define CONTENTS_LIGHTSABER 0x1000 // Used for lightsaber buddies, blocking, deflecting. + +#define CONTENTS_AREAPORTAL 0x8000 +/* +Ghoul2 Insert Start +*/ +#define CONTENTS_GHOUL2 0x4000 // used to set trace to do point to poly ghoul2 checks. Never set as a contents of an object/surface +/* +Ghoul2 Insert End +*/ + +#define CONTENTS_PLAYERCLIP 0x10000 +#define CONTENTS_MONSTERCLIP 0x20000 +#define CONTENTS_SHOTCLIP 0x40000 //These are not needed if CONTENTS_SOLID is included + +//q3 bot specific contents types +#define CONTENTS_TELEPORTER 0x40000 +#define CONTENTS_JUMPPAD 0x80000 //needed for bspc +#define CONTENTS_ITEM 0x80000 //Items can be touched but do not block movement (like triggers) but can be hit by the infoString trace +#define CONTENTS_CLUSTERPORTAL 0x100000 +#define CONTENTS_DONOTENTER 0x200000 +#define CONTENTS_BOTCLIP 0x400000 + +#define CONTENTS_ORIGIN 0x1000000 // removed before bsping an entity + +#define CONTENTS_BODY 0x2000000 // should never be on a brush, only in game +#define CONTENTS_CORPSE 0x4000000 +#define CONTENTS_DETAIL 0x8000000 // brushes not used for the bsp +#define CONTENTS_STRUCTURAL 0x10000000 // brushes used for the bsp +#define CONTENTS_TRANSLUCENT 0x20000000 // don't consume surface fragments inside +#define CONTENTS_TRIGGER 0x40000000 +#define CONTENTS_NODROP 0x80000000 // don't leave bodies or items (death fog, lava) + +#define SURF_NODAMAGE 0x1 // never give falling damage +#define SURF_SLICK 0x2 // effects game physics +#define SURF_SKY 0x4 // lighting from environment map +#define SURF_NOIMPACT 0x10 // don't make missile explosions +#define SURF_NOMARKS 0x20 // don't leave missile marks +#define SURF_FLESH 0x40 // make flesh sounds and effects +#define SURF_NODRAW 0x80 // don't generate a drawsurface at all +#define SURF_HINT 0x100 // make a primary bsp splitter +#define SURF_SKIP 0x200 // completely ignore, allowing non-closed brushes +#define SURF_NOLIGHTMAP 0x400 // surface doesn't need a lightmap +#define SURF_POINTLIGHT 0x800 // generate lighting info at vertexes +#define SURF_METALSTEPS 0x1000 // clanking footsteps +#define SURF_NOSTEPS 0x2000 // no footstep sounds +#define SURF_NONSOLID 0x4000 // don't collide against curves with this set +#define SURF_LIGHTFILTER 0x8000 // act as a light filter during q3map -light +#define SURF_ALPHASHADOW 0x10000 // do per-pixel light shadow casting in q3map +#define SURF_NODLIGHT 0x20000 // don't dlight even if solid (solid lava, skies) +#define SURF_FORCEFIELD 0x40000 // the surface in question is a forcefield + diff --git a/utils/common/threads.c b/utils/common/threads.c new file mode 100644 index 0000000..9195d14 --- /dev/null +++ b/utils/common/threads.c @@ -0,0 +1,420 @@ + +#include "cmdlib.h" +#include "threads.h" + +#define MAX_THREADS 64 + +int dispatch; +int workcount; +int oldf; +qboolean pacifier; + +qboolean threaded; + +/* +============= +GetThreadWork + +============= +*/ +int GetThreadWork (void) +{ + int r; + int f; + + ThreadLock (); + + if (dispatch == workcount) + { + ThreadUnlock (); + return -1; + } + + f = 10*dispatch / workcount; + if (f != oldf) + { + oldf = f; + if (pacifier) + printf ("%i...", f); + } + + r = dispatch; + dispatch++; + ThreadUnlock (); + + return r; +} + + +void (*workfunction) (int); + +void ThreadWorkerFunction (int threadnum) +{ + int work; + + while (1) + { + work = GetThreadWork (); + if (work == -1) + break; +//printf ("thread %i, work %i\n", threadnum, work); + workfunction(work); + } +} + +void RunThreadsOnIndividual (int workcnt, qboolean showpacifier, void(*func)(int)) +{ + if (numthreads == -1) + ThreadSetDefault (); + workfunction = func; + RunThreadsOn (workcnt, showpacifier, ThreadWorkerFunction); +} + + +/* +=================================================================== + +WIN32 + +=================================================================== +*/ +#ifdef WIN32 + +#define USED + +#include + +int numthreads = -1; +CRITICAL_SECTION crit; +static int enter; + +void ThreadSetDefault (void) +{ + SYSTEM_INFO info; + + if (numthreads == -1) // not set manually + { + GetSystemInfo (&info); + numthreads = info.dwNumberOfProcessors; + if (numthreads < 1 || numthreads > 32) + numthreads = 1; + } + + qprintf ("%i threads\n", numthreads); +} + + +void ThreadLock (void) +{ + if (!threaded) + return; + EnterCriticalSection (&crit); + if (enter) + Error ("Recursive ThreadLock\n"); + enter = 1; +} + +void ThreadUnlock (void) +{ + if (!threaded) + return; + if (!enter) + Error ("ThreadUnlock without lock\n"); + enter = 0; + LeaveCriticalSection (&crit); +} + +/* +============= +RunThreadsOn +============= +*/ +void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int)) +{ + int threadid[MAX_THREADS]; + HANDLE threadhandle[MAX_THREADS]; + int i; + int start, end; + + start = I_FloatTime (); + dispatch = 0; + workcount = workcnt; + oldf = -1; + pacifier = showpacifier; + threaded = qtrue; + + // + // run threads in parallel + // + InitializeCriticalSection (&crit); + + if (numthreads == 1) + { // use same thread + func (0); + } + else + { + for (i=0 ; i + +pthread_mutex_t *my_mutex; + +void ThreadLock (void) +{ + if (my_mutex) + pthread_mutex_lock (my_mutex); +} + +void ThreadUnlock (void) +{ + if (my_mutex) + pthread_mutex_unlock (my_mutex); +} + + +/* +============= +RunThreadsOn +============= +*/ +void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int)) +{ + int i; + pthread_t work_threads[MAX_THREADS]; + pthread_addr_t status; + pthread_attr_t attrib; + pthread_mutexattr_t mattrib; + int start, end; + + start = I_FloatTime (); + dispatch = 0; + workcount = workcnt; + oldf = -1; + pacifier = showpacifier; + threaded = qtrue; + + if (pacifier) + setbuf (stdout, NULL); + + if (!my_mutex) + { + my_mutex = malloc (sizeof(*my_mutex)); + if (pthread_mutexattr_create (&mattrib) == -1) + Error ("pthread_mutex_attr_create failed"); + if (pthread_mutexattr_setkind_np (&mattrib, MUTEX_FAST_NP) == -1) + Error ("pthread_mutexattr_setkind_np failed"); + if (pthread_mutex_init (my_mutex, mattrib) == -1) + Error ("pthread_mutex_init failed"); + } + + if (pthread_attr_create (&attrib) == -1) + Error ("pthread_attr_create failed"); + if (pthread_attr_setstacksize (&attrib, 0x100000) == -1) + Error ("pthread_attr_setstacksize failed"); + + for (i=0 ; i +#include +#include +#include + + +int numthreads = -1; +abilock_t lck; + +void ThreadSetDefault (void) +{ + if (numthreads == -1) + numthreads = prctl(PR_MAXPPROCS); + printf ("%i threads\n", numthreads); + usconfig (CONF_INITUSERS, numthreads); +} + + +void ThreadLock (void) +{ + spin_lock (&lck); +} + +void ThreadUnlock (void) +{ + release_lock (&lck); +} + + +/* +============= +RunThreadsOn +============= +*/ +void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int)) +{ + int i; + int pid[MAX_THREADS]; + int start, end; + + start = I_FloatTime (); + dispatch = 0; + workcount = workcnt; + oldf = -1; + pacifier = showpacifier; + threaded = qtrue; + + if (pacifier) + setbuf (stdout, NULL); + + init_lock (&lck); + + for (i=0 ; i +#include "cmdlib.h" +#include "mathlib.h" +#include "polyset.h" +#include "trilib.h" + +// on disk representation of a face + + +#define FLOAT_START 99999.0 +#define FLOAT_END -FLOAT_START +#define MAGIC 123322 + +//#define NOISY 1 + +typedef struct { + float v[3]; +} vector; + +typedef struct +{ + vector n; /* normal */ + vector p; /* point */ + vector c; /* color */ + float u; /* u */ + float v; /* v */ +} aliaspoint_t; + +typedef struct { + aliaspoint_t pt[3]; +} tf_triangle; + + +static void ByteSwapTri (tf_triangle *tri) +{ + int i; + + for (i=0 ; iverts[j][k] = tri.pt[j].p.v[k]; + ptri->normals[j][k] = tri.pt[j].n.v[k]; +// ptri->colors[j][k] = tri.pt[j].c.v[k]; + } + + ptri->texcoords[j][0] = tri.pt[j].u; + ptri->texcoords[j][1] = tri.pt[j].v; + } + + ptri++; + if ((ptri - tripool ) >= POLYSET_MAXTRIANGLES) + Error ("Error: too many triangles; increase POLYSET_MAXTRIANGLES\n"); + } +} + +void TRI_LoadPolysets( const char *filename, polyset_t **ppPSET, int *numpsets ) +{ + FILE *input; + float start; + char name[256], tex[256]; + int i, count, magic, pset = 0; + triangle_t *ptri; + polyset_t *pPSET; + int iLevel; + int exitpattern; + float t; + + t = -FLOAT_START; + *((unsigned char *)&exitpattern + 0) = *((unsigned char *)&t + 3); + *((unsigned char *)&exitpattern + 1) = *((unsigned char *)&t + 2); + *((unsigned char *)&exitpattern + 2) = *((unsigned char *)&t + 1); + *((unsigned char *)&exitpattern + 3) = *((unsigned char *)&t + 0); + + if ((input = fopen(filename, "rb")) == 0) + Error ("reader: could not open file '%s'", filename); + + iLevel = 0; + + fread(&magic, sizeof(int), 1, input); + if (BigLong(magic) != MAGIC) + Error ("%s is not a Alias object separated triangle file, magic number is wrong.", filename); + + pPSET = calloc( 1, POLYSET_MAXPOLYSETS * sizeof( polyset_t ) ); + ptri = calloc( 1, POLYSET_MAXTRIANGLES * sizeof( triangle_t ) ); + + *ppPSET = pPSET; + + while (feof(input) == 0) { + if (fread(&start, sizeof(float), 1, input) < 1) + break; + *(int *)&start = BigLong(*(int *)&start); + if (*(int *)&start != exitpattern) + { + if (start == FLOAT_START) { + /* Start of an object or group of objects. */ + i = -1; + do { + /* There are probably better ways to read a string from */ + /* a file, but this does allow you to do error checking */ + /* (which I'm not doing) on a per character basis. */ + ++i; + fread( &(name[i]), sizeof( char ), 1, input); + } while( name[i] != '\0' ); + + if ( i != 0 ) + strncpy( pPSET[pset].name, name, sizeof( pPSET[pset].name ) - 1 ); + else + strcpy( pPSET[pset].name , "(unnamed)" ); + strlwr( pPSET[pset].name ); + +// indent(); +// fprintf(stdout,"OBJECT START: %s\n",name); + fread( &count, sizeof(int), 1, input); + count = BigLong(count); + ++iLevel; + if (count != 0) { +// indent(); +// fprintf(stdout,"NUMBER OF TRIANGLES: %d\n",count); + + i = -1; + do { + ++i; + fread( &(tex[i]), sizeof( char ), 1, input); + } while( tex[i] != '\0' ); + +/* + if ( i != 0 ) + strncpy( pPSET[pset].texname, tex, sizeof( pPSET[pset].texname ) - 1 ); + else + strcpy( pPSET[pset].texname, "(unnamed)" ); + strlwr( pPSET[pset].texname ); +*/ + +// indent(); +// fprintf(stdout," Object texture name: '%s'\n",tex); + } + + /* Else (count == 0) this is the start of a group, and */ + /* no texture name is present. */ + } + else if (start == FLOAT_END) { + /* End of an object or group. Yes, the name should be */ + /* obvious from context, but it is in here just to be */ + /* safe and to provide a little extra information for */ + /* those who do not wish to write a recursive reader. */ + /* Mea culpa. */ + --iLevel; + i = -1; + do { + ++i; + fread( &(name[i]), sizeof( char ), 1, input); + } while( name[i] != '\0' ); + + if ( i != 0 ) + strncpy( pPSET[pset].name, name, sizeof( pPSET[pset].name ) - 1 ); + else + strcpy( pPSET[pset].name , "(unnamed)" ); + + strlwr( pPSET[pset].name ); + +// indent(); +// fprintf(stdout,"OBJECT END: %s\n",name); + continue; + } + } + +// +// read the triangles +// + if ( count > 0 ) + { + pPSET[pset].triangles = ptri; + ReadPolysetGeometry( pPSET[0].triangles, input, count, ptri ); + ptri += count; + pPSET[pset].numtriangles = count; + if ( ++pset >= POLYSET_MAXPOLYSETS ) + { + Error ("Error: too many polysets; increase POLYSET_MAXPOLYSETS\n"); + } + } + } + + *numpsets = pset; + + fclose (input); +} + diff --git a/utils/common/trilib.h b/utils/common/trilib.h new file mode 100644 index 0000000..48aa1cd --- /dev/null +++ b/utils/common/trilib.h @@ -0,0 +1,5 @@ +// +// trilib.h: header file for loading triangles from an Alias triangle file +// +void TRI_LoadPolysets( const char *filename, polyset_t **ppPSET, int *numpsets ); + diff --git a/utils/q3data/3dslib.c b/utils/q3data/3dslib.c new file mode 100644 index 0000000..d5e864f --- /dev/null +++ b/utils/q3data/3dslib.c @@ -0,0 +1,630 @@ +#include +#include "q3data.h" + +static void Load3DS( const char *filename, _3DS_t *p3DS, qboolean verbose ); + +static qboolean s_verbose; + +#define MAX_MATERIALS 100 +#define MAX_NAMED_OBJECTS 100 +#define MAX_MESH_MATERIAL_GROUPS 100 +#define MAX_TRI_OBJECTS 512 + +static char s_buffer[1000000]; + +static int ReadString( FILE *fp, char *buffer ) +{ + int i = 0; + int bytesRead = 0; + + do + { + fread( &buffer[i], 1, sizeof( char ), fp ); + bytesRead++; + } while ( buffer[i++] != 0 ); + buffer[i] = 0; + + return bytesRead; +} + +static int ReadChunkAndLength( FILE *fp, short *chunk, long *len ) +{ + if ( fread( chunk, sizeof( short ), 1, fp ) != 1 ) + return 0; + if ( fread( len, sizeof( long ), 1, fp ) != 1 ) + Error( "Unexpected EOF found" ); + return 1; +} + +static void LoadMapName( FILE *fp, char *buffer, int thisChunkLen ) +{ + unsigned short chunkID; + long chunkLen; + long bytesRead = 0; + + while ( ReadChunkAndLength( fp, &chunkID, &chunkLen ) ) + { + switch ( chunkID ) + { + case _3DS_CHUNK_MAT_MAPNAME: + fread( buffer, chunkLen - 6, 1, fp ); + break; + default: + fread( s_buffer, chunkLen - 6, 1, fp ); + break; + } + bytesRead += chunkLen; + if ( bytesRead >= thisChunkLen ) + return; + } +} + +static void LoadMaterialList( FILE *fp, long thisChunkLen, _3DSMaterial_t *pMat ) +{ + long chunkLen; + unsigned short chunkID; + long bytesRead = 0; + _3DSMaterial_t mat; + char curdir[1024]; + char buffer[2048]; + + memset( &mat, 0, sizeof( mat ) ); + + if ( s_verbose ) + printf( " >>> MATERIAL LIST\n" ); + + while ( ReadChunkAndLength( fp, &chunkID, &chunkLen ) ) + { + switch ( chunkID ) + { + case _3DS_CHUNK_MAT_NAME: + fread( mat.name, chunkLen - 6, 1, fp ); + if ( s_verbose ) + printf( " found mat name '%s'\n", mat.name ); + break; + case _3DS_CHUNK_TEXMAP: + LoadMapName( fp, mat.texture, chunkLen - 6 ); + if ( s_verbose ) + printf( " found texture '%s'\n", mat.texture ); + break; + case _3DS_CHUNK_SPECMAP: + LoadMapName( fp, mat.specular, chunkLen - 6 ); + if ( s_verbose ) + printf( " found specular map '%s'\n", mat.specular ); + break; + case _3DS_CHUNK_OPACMAP: + LoadMapName( fp, mat.opacity, chunkLen - 6 ); + if ( s_verbose ) + printf( " found opacity map '%s'\n", mat.opacity ); + break; + case _3DS_CHUNK_REFLMAP: + LoadMapName( fp, mat.reflection, chunkLen - 6 ); + if ( s_verbose ) + printf( " found reflection map '%s'\n", mat.reflection ); + break; + case _3DS_CHUNK_BUMPMAP: + LoadMapName( fp, mat.bump, chunkLen - 6 ); + if ( s_verbose ) + printf( " found bump map '%s'\n", mat.bump ); + break; + default: + fread( s_buffer, chunkLen - 6, 1, fp ); + break; + } + + bytesRead += chunkLen; + + if ( bytesRead >= thisChunkLen ) + break; + } + + Q_getwd( curdir ); + + if ( mat.texture[0] ) + { + sprintf( buffer, "%s%s", curdir, mat.texture ); + if ( strstr( buffer, gamedir + 1 ) ) + strcpy( mat.texture, strstr( buffer, gamedir + 1 ) + strlen( gamedir ) - 1 ); + else + strcpy( mat.texture, buffer ); + } + + if ( mat.specular[0] ) + { + sprintf( buffer, "%s%s", curdir, mat.specular ); + if ( strstr( buffer, gamedir + 1 ) ) + strcpy( mat.specular, strstr( buffer, gamedir + 1 ) + strlen( gamedir ) - 1 ); + else + strcpy( mat.specular, buffer ); + } + + if ( mat.bump[0] ) + { + sprintf( buffer, "%s%s", curdir, mat.bump ); + if ( strstr( buffer, gamedir + 1 ) ) + strcpy( mat.bump, strstr( buffer, gamedir + 1 ) + strlen( gamedir ) - 1 ); + else + strcpy( mat.bump, buffer ); + } + + if ( mat.reflection[0] ) + { + sprintf( buffer, "%s%s", curdir, mat.reflection ); + if ( strstr( buffer, gamedir + 1 ) ) + strcpy( mat.reflection, strstr( buffer, gamedir + 1 ) + strlen( gamedir ) - 1 ); + else + strcpy( mat.reflection, buffer ); + } + + if ( mat.opacity[0] ) + { + sprintf( buffer, "%s%s", curdir, mat.opacity ); + if ( strstr( buffer, gamedir + 1 ) ) + strcpy( mat.opacity, strstr( buffer, gamedir + 1 ) + strlen( gamedir ) - 1 ); + else + strcpy( mat.opacity, buffer ); + } + + *pMat = mat; +} + +static void LoadMeshMaterialGroup( FILE *fp, long thisChunkLen, _3DSMeshMaterialGroup_t *pMMG ) +{ + _3DSMeshMaterialGroup_t mmg; + + memset( &mmg, 0, sizeof( mmg ) ); + + ReadString( fp, mmg.name ); + + fread( &mmg.numFaces, sizeof( mmg.numFaces ), 1, fp ); + mmg.pFaces = malloc( sizeof( mmg.pFaces[0] ) * mmg.numFaces ); + fread( mmg.pFaces, sizeof( mmg.pFaces[0] ), mmg.numFaces, fp ); + + if ( s_verbose ) + { + printf( " >>> MESH MATERIAL GROUP '%s' (%d faces)\n", mmg.name, mmg.numFaces ); + + { + int i; + + for ( i = 0; i < mmg.numFaces; i++ ) + { + printf( " %d\n", mmg.pFaces[i] ); + } + } + } + + *pMMG = mmg; +} + +static void LoadNamedTriObject( FILE *fp, long thisChunkLen, _3DSTriObject_t *pTO ) +{ + long chunkLen; + unsigned short chunkID; + int i = 0; + long bytesRead = 0; + _3DSTriObject_t triObj; + _3DSMeshMaterialGroup_t meshMaterialGroups[MAX_MESH_MATERIAL_GROUPS]; + int numMeshMaterialGroups = 0; + + memset( &triObj, 0, sizeof( triObj ) ); + + if ( s_verbose ) + printf( " >>> NAMED TRI OBJECT\n" ); + + while ( ReadChunkAndLength( fp, &chunkID, &chunkLen ) ) + { + switch ( chunkID ) + { + case _3DS_CHUNK_MSH_MAT_GROUP: + LoadMeshMaterialGroup( fp, chunkLen - 6, &meshMaterialGroups[numMeshMaterialGroups] ); + bytesRead += chunkLen; + numMeshMaterialGroups++; + break; + case _3DS_CHUNK_FACE_ARRAY: + fread( &triObj.numFaces, sizeof( triObj.numFaces ), 1, fp ); + assert( triObj.pFaces == 0 ); + + triObj.pFaces = malloc( sizeof( triObj.pFaces[0] ) * triObj.numFaces ); + fread( triObj.pFaces, sizeof( triObj.pFaces[0] ), triObj.numFaces, fp ); + bytesRead += sizeof( triObj.numFaces ) + triObj.numFaces * sizeof( triObj.pFaces[0] ) + 6; + + if ( s_verbose ) + { + printf( " found face array with %d faces\n", triObj.numFaces ); + for ( i = 0; i < triObj.numFaces; i++ ) + { + printf( " %d: %d,%d,%d\n", i, triObj.pFaces[i].a, triObj.pFaces[i].b, triObj.pFaces[i].c ); + } + } + + break; + case _3DS_CHUNK_POINT_ARRAY: + fread( &triObj.numPoints, sizeof( triObj.numPoints ), 1, fp ); + triObj.pPoints = malloc( sizeof( triObj.pPoints[0] ) * triObj.numPoints ); + fread( triObj.pPoints, sizeof( triObj.pPoints[0] ), triObj.numPoints, fp ); + bytesRead += sizeof( triObj.numPoints ) + triObj.numPoints * sizeof( triObj.pPoints[0] ) + 6; + + // flip points around into our coordinate system + for ( i = 0; i < triObj.numPoints; i++ ) + { + float x, y, z; + + x = triObj.pPoints[i].x; + y = triObj.pPoints[i].y; + z = triObj.pPoints[i].z; + + triObj.pPoints[i].x = -y; + triObj.pPoints[i].y = x; + triObj.pPoints[i].z = z; + } + + if ( s_verbose ) + { + printf( " found point array with %d points\n", triObj.numPoints ); + for ( i = 0; i < triObj.numPoints; i++ ) + { + printf( " %d: %f,%f,%f\n", i, triObj.pPoints[i].x, triObj.pPoints[i].y, triObj.pPoints[i].z ); + } + } + break; + case _3DS_CHUNK_TEX_VERTS: + fread( &triObj.numTexVerts, sizeof( triObj.numTexVerts ), 1, fp ); + triObj.pTexVerts = malloc( sizeof( triObj.pTexVerts[0] ) * triObj.numTexVerts ); + fread( triObj.pTexVerts, sizeof( triObj.pTexVerts[0] ), triObj.numTexVerts, fp ); + bytesRead += sizeof( triObj.numTexVerts ) + sizeof( triObj.pTexVerts[0] ) * triObj.numTexVerts + 6; + + if ( s_verbose ) + { + printf( " found tex vert array with %d tex verts\n", triObj.numTexVerts ); + for ( i = 0; i < triObj.numTexVerts; i++ ) + { + printf( " %d: %f,%f\n", i, triObj.pTexVerts[i].s, triObj.pTexVerts[i].t ); + } + } + break; + default: + fread( s_buffer, chunkLen - 6, 1, fp ); + bytesRead += chunkLen; + break; + } + + if ( bytesRead >= thisChunkLen ) + break; + } + *pTO = triObj; + + if ( numMeshMaterialGroups == 0 ) + { + numMeshMaterialGroups = 1; + strcpy( meshMaterialGroups[0].name, "(null)" ); + if ( pTO->numTexVerts ) { + printf( "Warning: assigning (null) skin to tri object\n" ); + } + } + else + { + assert( pTO->numFaces == meshMaterialGroups[0].numFaces ); + } + + pTO->pMeshMaterialGroups = malloc( sizeof( _3DSMeshMaterialGroup_t ) * numMeshMaterialGroups ); + memcpy( pTO->pMeshMaterialGroups, meshMaterialGroups, numMeshMaterialGroups * sizeof( meshMaterialGroups[0] ) ); + pTO->numMeshMaterialGroups = numMeshMaterialGroups; + + // + // sanity checks + // + assert( numMeshMaterialGroups <= 1 ); +} + +static void LoadNamedObject( FILE *fp, long thisChunkLen, _3DSNamedObject_t *pNO ) +{ + long chunkLen; + unsigned short chunkID; + int i = 0; + long bytesRead = 0; + char name[100]; + _3DSTriObject_t triObj[MAX_TRI_OBJECTS]; + int numTriObjects = 0; + + memset( triObj, 0, sizeof( triObj ) ); + + bytesRead += ReadString( fp, name ); + + if ( s_verbose ) + printf( " >>> NAMED OBJECT '%s'\n", name ); + + while ( ReadChunkAndLength( fp, &chunkID, &chunkLen ) ) + { + switch ( chunkID ) + { + case _3DS_CHUNK_NAMED_TRI_OBJECT: + LoadNamedTriObject( fp, chunkLen - 6, &triObj[numTriObjects] ); + numTriObjects++; + break; + default: + fread( s_buffer, chunkLen - 6, 1, fp ); + break; + } + + bytesRead += chunkLen; + + if ( bytesRead >= thisChunkLen ) + break; + } + + strcpy( pNO->name, name ); + pNO->pTriObjects = malloc( sizeof( _3DSTriObject_t ) * numTriObjects ); + memcpy( pNO->pTriObjects, triObj, sizeof( triObj[0] ) * numTriObjects ); + pNO->numTriObjects = numTriObjects; + + assert( numTriObjects <= 1 ); +} + +static void LoadEditChunk( FILE *fp, long thisChunkLen, _3DSEditChunk_t *pEC ) +{ + unsigned short chunkID; + long chunkLen; + long bytesRead = 0; + _3DSEditChunk_t editChunk; + + _3DSMaterial_t mat[MAX_MATERIALS]; + _3DSNamedObject_t namedObjects[MAX_NAMED_OBJECTS]; + + int numMaterials = 0, numNamedObjects = 0; + + memset( &editChunk, 0, sizeof( editChunk ) ); + + if ( s_verbose ) + printf( ">>> EDIT CHUNK\n" ); + + while ( ReadChunkAndLength( fp, &chunkID, &chunkLen ) ) + { + switch ( chunkID ) + { + case _3DS_CHUNK_MAT_LIST: + LoadMaterialList( fp, chunkLen - 6, &mat[numMaterials] ); + numMaterials++; + break; + case _3DS_CHUNK_NAMED_OBJECT: + LoadNamedObject( fp, chunkLen - 6, &namedObjects[numNamedObjects] ); + if ( namedObjects[numNamedObjects].numTriObjects != 0 ) + ++numNamedObjects; + break; + case _3DS_CHUNK_MESH_VERSION: + default: + fread( s_buffer, chunkLen - 6, 1, fp ); + break; + } + + bytesRead += chunkLen; + + if ( bytesRead >= thisChunkLen ) + break; + } + + if ( numMaterials == 0 ) + { + numMaterials = 1; + strcpy( mat[0].name, "(null)" ); + printf( "Warning: no material definitions found\n" ); + } + + pEC->numNamedObjects = numNamedObjects; + + pEC->pMaterials = malloc( sizeof( _3DSMaterial_t ) * numMaterials ); + pEC->pNamedObjects = malloc( sizeof( _3DSNamedObject_t ) * numNamedObjects ); + + memcpy( pEC->pMaterials, mat, numMaterials * sizeof( mat[0] ) ); + memcpy( pEC->pNamedObjects, namedObjects, numNamedObjects * sizeof( namedObjects[0] ) ); +} + +static void Load3DS( const char *filename, _3DS_t *p3DS, qboolean verbose ) +{ + FILE *fp; + unsigned short chunkID; + long chunkLen; + _3DSEditChunk_t editChunk; + + s_verbose = verbose; + + if ( ( fp = fopen( filename, "rb" ) ) == 0 ) + Error( "Unable to open '%s'", filename ); + + // read magic number + if ( ( fread( &chunkID, sizeof( short ), 1, fp ) != 1 ) || + ( LittleShort( chunkID ) != _3DS_CHUNK_MAGIC ) ) + { + Error( "Missing or incorrect magic number in '%s'", filename ); + } + if ( fread( &chunkLen, sizeof( chunkLen ), 1, fp ) != 1 ) + Error( "Unexpected EOF encountered in '%s'", filename ); + // version number + if ( !ReadChunkAndLength( fp, &chunkID, &chunkLen ) ) + Error( "Missing version number in '%s'", filename ); + if ( fread( s_buffer, chunkLen - 6, 1, fp ) != 1 ) + Error( "Unexpected EOF encountered in '%s'", filename ); + + while ( ReadChunkAndLength( fp, &chunkID, &chunkLen ) ) + { + switch ( chunkID ) + { + case _3DS_CHUNK_EDIT: + LoadEditChunk( fp, chunkLen - 6, &editChunk ); + break; + case _3DS_CHUNK_KEYFRAME_DATA: + fread( s_buffer, chunkLen - 6, 1, fp ); + break; + default: + fread( s_buffer, chunkLen - 6, 1, fp ); + break; + } + } + + fclose( fp ); + + p3DS->editChunk = editChunk; +} + +static void ComputeNormals( _3DSTriObject_t *pTO, triangle_t *pTris ) +{ + vec3_t faceNormals[POLYSET_MAXTRIANGLES]; + vec3_t vertexNormals[POLYSET_MAXTRIANGLES*3]; + vec3_t side0, side1, facenormal; + int f, v; + + memset( faceNormals, 0, sizeof( faceNormals ) ); + memset( vertexNormals, 0, sizeof( vertexNormals ) ); + + // + // compute face normals + // + for ( f = 0; f < pTO->numFaces; f++ ) + { + VectorSubtract( pTris[f].verts[0], pTris[f].verts[1], side0 ); + VectorSubtract( pTris[f].verts[2], pTris[f].verts[1], side1 ); + + CrossProduct( side0, side1, facenormal ); + VectorNormalize( facenormal, faceNormals[f] ); + } + + // + // sum vertex normals + // + for ( v = 0; v < pTO->numPoints; v++ ) + { + for ( f = 0; f < pTO->numFaces; f++ ) + { + if ( ( pTO->pFaces[f].a == v ) || + ( pTO->pFaces[f].b == v ) || + ( pTO->pFaces[f].c == v ) ) + { + vertexNormals[v][0] += faceNormals[f][0]; + vertexNormals[v][1] += faceNormals[f][1]; + vertexNormals[v][2] += faceNormals[f][2]; + } + } + + VectorNormalize( vertexNormals[v], vertexNormals[v] ); + } + + // + // copy vertex normals into triangles + // + for ( f = 0; f < pTO->numFaces; f++ ) + { + int i0 = pTO->pFaces[f].c; + int i1 = pTO->pFaces[f].b; + int i2 = pTO->pFaces[f].a; + + VectorCopy( vertexNormals[i0], pTris[f].normals[0] ); + VectorCopy( vertexNormals[i1], pTris[f].normals[1] ); + VectorCopy( vertexNormals[i2], pTris[f].normals[2] ); + } +} + +/* +** void _3DS_LoadPolysets +*/ +void _3DS_LoadPolysets( const char *filename, polyset_t **ppPSET, int *numpsets, qboolean verbose ) +{ + _3DS_t _3ds; + int numPolysets; + polyset_t *pPSET; + triangle_t *ptri, *triangles; + int i; + + // load the 3DS + memset( &_3ds, 0, sizeof( _3ds ) ); + Load3DS( filename, &_3ds, verbose ); + + // compute information + numPolysets = _3ds.editChunk.numNamedObjects; + + // allocate memory + pPSET = calloc( 1, numPolysets * sizeof( polyset_t ) ); + triangles = ptri = calloc( 1, POLYSET_MAXTRIANGLES * sizeof( triangle_t ) ); + + // copy the data over + for ( i = 0; i < numPolysets; i++ ) + { + char matnamebuf[1024]; + int j; + triangle_t *tri; + _3DSTriObject_t *pTO = &_3ds.editChunk.pNamedObjects[i].pTriObjects[0]; + + pPSET[i].triangles = ptri; + pPSET[i].numtriangles = pTO->numFaces; + strcpy( pPSET[i].name, _3ds.editChunk.pNamedObjects[i].name ); + + strcpy( matnamebuf, filename ); + if ( strrchr( matnamebuf, '/' ) ) + *( strrchr( matnamebuf, '/' ) + 1 )= 0; + strcat( matnamebuf, pTO->pMeshMaterialGroups[0].name ); + + if ( strstr( matnamebuf, gamedir ) ) + strcpy( pPSET[i].materialname, strstr( matnamebuf, gamedir ) + strlen( gamedir ) ); + else + strcpy( pPSET[i].materialname, pTO->pMeshMaterialGroups[0].name ); + + assert( pPSET[i].numtriangles < POLYSET_MAXTRIANGLES ); + + for ( tri = ptri, j = 0; j < pPSET[i].numtriangles; j++ ) + { + int i0 = pTO->pFaces[j].c; + int i1 = pTO->pFaces[j].b; + int i2 = pTO->pFaces[j].a; + + tri->verts[0][0] = pTO->pPoints[i0].x; + tri->verts[0][1] = pTO->pPoints[i0].y; + tri->verts[0][2] = pTO->pPoints[i0].z; + + tri->verts[1][0] = pTO->pPoints[i1].x; + tri->verts[1][1] = pTO->pPoints[i1].y; + tri->verts[1][2] = pTO->pPoints[i1].z; + + tri->verts[2][0] = pTO->pPoints[i2].x; + tri->verts[2][1] = pTO->pPoints[i2].y; + tri->verts[2][2] = pTO->pPoints[i2].z; +/* + for ( k = 0; k < 3; k++ ) + { + tri->colors[0][k] = 1; + tri->colors[1][k] = 1; + tri->colors[2][k] = 1; + } +*/ + + if ( pTO->pTexVerts ) + { + tri->texcoords[0][0] = pTO->pTexVerts[i0].s; + tri->texcoords[0][1] = 1.0f - pTO->pTexVerts[i0].t; + tri->texcoords[1][0] = pTO->pTexVerts[i1].s; + tri->texcoords[1][1] = 1.0f - pTO->pTexVerts[i1].t; + tri->texcoords[2][0] = pTO->pTexVerts[i2].s; + tri->texcoords[2][1] = 1.0f - pTO->pTexVerts[i2].t; + } + + tri++; + } + + ptri += pPSET[i].numtriangles; + assert( ptri - triangles < POLYSET_MAXTRIANGLES ); + } + + // compute normal data +#if 0 + for ( i = 0; i < numPolysets; i++ ) + { + // unique vertices based solely on vertex position + ComputeNormals( &_3ds.editChunk.pNamedObjects[i].pTriObjects[0], + pPSET[i].triangles ); + } +#endif + + free( _3ds.editChunk.pMaterials ); + free( _3ds.editChunk.pNamedObjects ); + + *ppPSET = pPSET; + *numpsets = numPolysets; +} diff --git a/utils/q3data/3dslib.h b/utils/q3data/3dslib.h new file mode 100644 index 0000000..9675723 --- /dev/null +++ b/utils/q3data/3dslib.h @@ -0,0 +1,118 @@ +typedef struct +{ + float x, y, z; +} _3DSPoint_t; + +typedef struct +{ + short a, b, c; + short flags; +} _3DSFace_t; + +typedef struct +{ + float s, t; +} _3DSTexVert_t; + +typedef struct +{ + char name[100]; + short numFaces; + short *pFaces; +} _3DSMeshMaterialGroup_t; + +typedef struct +{ + char name[80]; + + char texture[100]; + char specular[100]; + char reflection[100]; + char bump[100]; + char opacity[100]; +} _3DSMaterial_t; + +typedef struct +{ + short numFaces, numPoints, numTexVerts; + int numMeshMaterialGroups; + + _3DSPoint_t *pPoints; + _3DSFace_t *pFaces; + _3DSTexVert_t *pTexVerts; + + _3DSMeshMaterialGroup_t *pMeshMaterialGroups; +} _3DSTriObject_t; + +typedef struct +{ + char name[100]; + + int numTriObjects; + _3DSTriObject_t *pTriObjects; +} _3DSNamedObject_t; + +typedef struct +{ + int numNamedObjects; + int numMaterials; + + _3DSNamedObject_t *pNamedObjects; + _3DSMaterial_t *pMaterials; + +} _3DSEditChunk_t; + +typedef struct +{ + _3DSEditChunk_t editChunk; +} _3DS_t; + +#define _3DS_CHUNK_NULL 0x0000 +#define _3DS_CHUNK_UNKNOWN0 0x0001 +#define _3DS_CHUNK_M3D_VERSION 0x0002 +#define _3DS_CHUNK_M3D_KFVERSION 0x0005 +#define _3DS_CHUNK_COLOR_F 0x0010 +#define _3DS_CHUNK_COLOR_24 0x0011 +#define _3DS_CHUNK_LIN_COLOR_24 0x0012 +#define _3DS_CHUNK_LIN_COLOR_F 0x0013 +#define _3DS_CHUNK_INT_PERCENTAGE 0x0030 +#define _3DS_CHUNK_FLOAT_PERCENT 0x0031 +#define _3DS_CHUNK_MASTER_SCALE 0x0100 +#define _3DS_CHUNK_CHUNK_TYPE 0x0995 +#define _3DS_CHUNK_CHUNK_UNIQUE 0x0996 +#define _3DS_CHUNK_NOT_CHUNK 0x0997 +#define _3DS_CHUNK_CONTAINER 0x0998 +#define _3DS_CHUNK_IS_CHUNK 0x0999 +#define _3DS_CHUNK_C_SXP_SELFI_MASKDATA 0x0c3c + +#define _3DS_CHUNK_BITMAP 0x1100 +#define _3DS_CHUNK_USE_BITMAP 0x1101 +#define _3DS_CHUNK_SOLID_BGND 0x1200 +#define _3DS_CHUNK_USE_SOLID_BGND 0x1201 + +#define _3DS_CHUNK_EDIT 0x3d3d +#define _3DS_CHUNK_MESH_VERSION 0x3d3e + +#define _3DS_CHUNK_NAMED_OBJECT 0x4000 +#define _3DS_CHUNK_NAMED_TRI_OBJECT 0x4100 +#define _3DS_CHUNK_POINT_ARRAY 0x4110 +#define _3DS_CHUNK_POINT_FLAG_ARRAY 0x4111 +#define _3DS_CHUNK_FACE_ARRAY 0x4120 +#define _3DS_CHUNK_MSH_MAT_GROUP 0x4130 +#define _3DS_CHUNK_TEX_VERTS 0x4140 +#define _3DS_CHUNK_SMOOTH_GROUP 0x4150 +#define _3DS_CHUNK_MESH_MATRIX 0x4160 +#define _3DS_CHUNK_MAGIC 0x4d4d + +#define _3DS_CHUNK_MAT_NAME 0xa000 +#define _3DS_CHUNK_TEXMAP 0xa200 +#define _3DS_CHUNK_SPECMAP 0xa204 +#define _3DS_CHUNK_OPACMAP 0xa210 +#define _3DS_CHUNK_REFLMAP 0xa220 +#define _3DS_CHUNK_BUMPMAP 0xa230 +#define _3DS_CHUNK_MAT_MAPNAME 0xa300 +#define _3DS_CHUNK_MAT_LIST 0xafff + +#define _3DS_CHUNK_KEYFRAME_DATA 0xb000 + +void _3DS_LoadPolysets( const char *filename, polyset_t **ppPSET, int *numpsets, qboolean verbose ); diff --git a/utils/q3data/compress.c b/utils/q3data/compress.c new file mode 100644 index 0000000..ede2a95 --- /dev/null +++ b/utils/q3data/compress.c @@ -0,0 +1,750 @@ +#include "q3data.h" + +#if 0 +/* +================== +MTF +================== +*/ +cblock_t MTF (cblock_t in) +{ + int i, j, b, code; + byte *out_p; + int index[256]; + cblock_t out; + + out_p = out.data = malloc(in.count + 4); + + // write count + *out_p++ = in.count&255; + *out_p++ = (in.count>>8)&255; + *out_p++ = (in.count>>16)&255; + *out_p++ = (in.count>>24)&255; + + for (i=0 ; i<256 ; i++) + index[i] = i; + + for (i=0 ; i b2) + return 1; + if (++i1 == bwt_size) + i1 = 0; + if (++i2 == bwt_size) + i2 = 0; + } + + return 0; +} + +/* +================== +BWT +================== +*/ +cblock_t BWT (cblock_t in) +{ + int *sorted; + int i; + byte *out_p; + cblock_t out; + + bwt_size = in.count; + bwt_data = in.data; + + sorted = malloc(in.count*sizeof(*sorted)); + for (i=0 ; i>8)&255; + *out_p++ = (in.count>>16)&255; + *out_p++ = (in.count>>24)&255; + + // write head index + for (i=0 ; i>8)&255; + *out_p++ = (i>>16)&255; + *out_p++ = (i>>24)&255; + + // write the L column + for (i=0 ; i 32) + Error ("bitcount > 32"); + charbits[nodenum] = bits; + charbitscount[nodenum] = bitcount; + return; + } + + node = &hnodes[nodenum]; + bits <<= 1; + BuildChars (node->children[0], bits, bitcount+1); + bits |= 1; + BuildChars (node->children[1], bits, bitcount+1); +} + +/* +================== +Huffman +================== +*/ +cblock_t Huffman (cblock_t in) +{ + int i; + hnode_t *node; + int outbits, c; + unsigned bits; + byte *out_p; + cblock_t out; + int max, maxchar; + + // count + memset (hnodes, 0, sizeof(hnodes)); + for (i=0 ; i max) + { + max = hnodes[i].count; + maxchar = i; + } + } + if (max == 0) + Error ("Huffman: max == 0"); + + for (i=0 ; i<256 ; i++) + { + hnodes[i].count = (hnodes[i].count*255+max-1) / max; + } + + // build the nodes + numhnodes = 256; + while (numhnodes != 511) + { + node = &hnodes[numhnodes]; + + // pick two lowest counts + node->children[0] = SmallestNode (); + if (node->children[0] == -1) + break; // no more + + node->children[1] = SmallestNode (); + if (node->children[1] == -1) + { + if (node->children[0] != numhnodes-1) + Error ("Bad smallestnode"); + break; + } + node->count = hnodes[node->children[0]].count + + hnodes[node->children[1]].count; + numhnodes++; + } + + BuildChars (numhnodes-1, 0, 0); + + out_p = out.data = malloc(in.count*2 + 1024); + memset (out_p, 0, in.count*2+1024); + + // write count + *out_p++ = in.count&255; + *out_p++ = (in.count>>8)&255; + *out_p++ = (in.count>>16)&255; + *out_p++ = (in.count>>24)&255; + + // save out the 256 normalized counts so the tree can be recreated + for (i=0 ; i<256 ; i++) + *out_p++ = hnodes[i].count; + + // write bits + outbits = 0; + for (i=0 ; i>3] |= 1<<(outbits&7); + outbits++; + } + } + + out_p += (outbits+7)>>3; + + out.count = out_p - out.data; + + return out; +} + +//========================================================================== + +/* +================== +RLE +================== +*/ +#define RLE_CODE 0xe8 +#define RLE_TRIPPLE 0xe9 + +int rle_counts[256]; +int rle_bytes[256]; + +cblock_t RLE (cblock_t in) +{ + int i; + byte *out_p; + int val; + int repeat; + cblock_t out; + + out_p = out.data = malloc (in.count*2); + + // write count + *out_p++ = in.count&255; + *out_p++ = (in.count>>8)&255; + *out_p++ = (in.count>>16)&255; + *out_p++ = (in.count>>24)&255; + + for (i=0 ; i 3 || val == RLE_CODE) + { + *out_p++ = RLE_CODE; + *out_p++ = val; + *out_p++ = repeat; + } + else + { + while (repeat--) + *out_p++ = val; + } + } + + out.count = out_p - out.data; + return out; +} + +//========================================================================== + +unsigned lzss_head[256]; +unsigned lzss_next[0x20000]; + +/* +================== +LZSS +================== +*/ +#define BACK_WINDOW 0x10000 +#define BACK_BITS 16 +#define FRONT_WINDOW 16 +#define FRONT_BITS 4 +cblock_t LZSS (cblock_t in) +{ + int i; + byte *out_p; + cblock_t out; + int val; + int j, start, max; + int bestlength, beststart; + int outbits; + +if (in.count >= sizeof(lzss_next)/4) +Error ("LZSS: too big"); + + memset (lzss_head, -1, sizeof(lzss_head)); + + out_p = out.data = malloc (in.count*2); + memset (out.data, 0, in.count*2); + + // write count + *out_p++ = in.count&255; + *out_p++ = (in.count>>8)&255; + *out_p++ = (in.count>>16)&255; + *out_p++ = (in.count>>24)&255; + + outbits = 0; + for (i=0 ; i in.count) + max = in.count - i; + + start = lzss_head[val]; + while (start != -1 && start >= i-BACK_WINDOW) + { + // count match length + for (j=0 ; j bestlength) + { + bestlength = j; + beststart = start; + } + start = lzss_next[start]; + } + +#else +// slow simple search + // search for a match + max = FRONT_WINDOW; + if (i + max > in.count) + max = in.count - i; + + start = i - BACK_WINDOW; + if (start < 0) + start = 0; + bestlength = 0; + beststart = 0; + for ( ; start < i ; start++) + { + if (in.data[start] != val) + continue; + // count match length + for (j=0 ; j bestlength) + { + bestlength = j; + beststart = start; + } + } +#endif + beststart = BACK_WINDOW - (i-beststart); + + if (bestlength < 3) + { // output a single char + bestlength = 1; + + out_p[outbits>>3] |= 1<<(outbits&7); // set bit to mark char + outbits++; + for (j=0 ; j<8 ; j++, outbits++) + if (val & (1<>3] |= 1<<(outbits&7); + } + else + { // output a phrase + outbits++; // leave a 0 bit to mark phrase + for (j=0 ; j>3] |= 1<<(outbits&7); + for (j=0 ; j>3] |= 1<<(outbits&7); + } + + while (bestlength--) + { + val = in.data[i]; + lzss_next[i] = lzss_head[val]; + lzss_head[val] = i; + i++; + } + } + + out_p += (outbits+7)>>3; + out.count = out_p - out.data; + return out; +} + +//========================================================================== + +#define MIN_REPT 15 +#define MAX_REPT 0 +#define HUF_TOKENS (256+MAX_REPT) + +unsigned charbits1[256][HUF_TOKENS]; +int charbitscount1[256][HUF_TOKENS]; + +hnode_t hnodes1[256][HUF_TOKENS*2]; +int numhnodes1[256]; + +int order0counts[256]; + +/* +================== +SmallestNode1 +================== +*/ +int SmallestNode1 (hnode_t *hnodes, int numhnodes) +{ + int i; + int best, bestnode; + + best = 99999999; + bestnode = -1; + for (i=0 ; i 32) + Error ("bitcount > 32"); + charbits1[prev][nodenum] = bits; + charbitscount1[prev][nodenum] = bitcount; + return; + } + + node = &hnodes1[prev][nodenum]; + bits <<= 1; + BuildChars1 (prev, node->children[0], bits, bitcount+1); + bits |= 1; + BuildChars1 (prev, node->children[1], bits, bitcount+1); +} + + +/* +================== +BuildTree1 +================== +*/ +void BuildTree1 (int prev) +{ + hnode_t *node, *nodebase; + int numhnodes; + + // build the nodes + numhnodes = HUF_TOKENS; + nodebase = hnodes1[prev]; + while (1) + { + node = &nodebase[numhnodes]; + + // pick two lowest counts + node->children[0] = SmallestNode1 (nodebase, numhnodes); + if (node->children[0] == -1) + break; // no more + + node->children[1] = SmallestNode1 (nodebase, numhnodes); + if (node->children[1] == -1) + break; + + node->count = nodebase[node->children[0]].count + + nodebase[node->children[1]].count; + numhnodes++; + } + numhnodes1[prev] = numhnodes-1; + BuildChars1 (prev, numhnodes-1, 0, 0); +} + + +/* +================== +Huffman1_Count +================== +*/ +void Huffman1_Count (cblock_t in) +{ + int i; + int prev; + int v; + int rept; + + prev = 0; + for (i=0 ; i MIN_REPT) + { + hnodes1[prev][255+rept].count++; + i += rept-1; + } +#endif + } +} + + +/* +================== +Huffman1_Build +================== +*/ +byte scaled[256][HUF_TOKENS]; +void Huffman1_Build (FILE *f) +{ + int i, j, v; + int max; + int total; + + for (i=0 ; i<256 ; i++) + { + // normalize and save the counts + max = 0; + for (j=0 ; j max) + max = hnodes1[i][j].count; + } + if (max == 0) + max = 1; + total = 0; + for (j=0 ; j 255) + Error ("v > 255"); + scaled[i][j] = hnodes1[i][j].count = v; + if (v) + total++; + } + if (total == 1) + { // must have two tokens + if (!scaled[i][0]) + scaled[i][0] = hnodes1[i][0].count = 1; + else + scaled[i][1] = hnodes1[i][1].count = 1; + } + + BuildTree1 (i); + } + +#if 0 + // count up the total bits + total = 0; + for (i=0 ; i<256 ; i++) + for (j=0 ; j<256 ; j++) + total += charbitscount1[i][j] * hnodes1[i][j].count; + + total = (total+7)/8; + printf ("%i bytes huffman1 compressed\n", total); +#endif + + fwrite (scaled, 1, sizeof(scaled), f); +} + +/* +================== +Huffman1 + +Order 1 compression with pre-built table +================== +*/ +cblock_t Huffman1 (cblock_t in) +{ + int i; + int outbits, c; + unsigned bits; + byte *out_p; + cblock_t out; + int prev; + int v; + int rept; + + out_p = out.data = malloc(in.count*2 + 1024); + memset (out_p, 0, in.count*2+1024); + + // write count + *out_p++ = in.count&255; + *out_p++ = (in.count>>8)&255; + *out_p++ = (in.count>>16)&255; + *out_p++ = (in.count>>24)&255; + + // write bits + outbits = 0; + prev = 0; + for (i=0 ; i>3] |= 1<<(outbits&7); + outbits++; + } + + prev = v; +#if 1 + // check for repeat encodes + for (rept=1 ; i+rept < in.count && rept < MAX_REPT ; rept++) + if (in.data[i+rept] != v) + break; + if (rept > MIN_REPT) + { + c = charbitscount1[prev][255+rept]; + bits = charbits1[prev][255+rept]; + if (!c) + Error ("!bits"); + while (c) + { + c--; + if (bits & (1<>3] |= 1<<(outbits&7); + outbits++; + } + i += rept-1; + } +#endif + } + + out_p += (outbits+7)>>3; + + out.count = out_p - out.data; + + return out; +} + +#endif diff --git a/utils/q3data/g2export.cpp b/utils/q3data/g2export.cpp new file mode 100644 index 0000000..9eac6a4 --- /dev/null +++ b/utils/q3data/g2export.cpp @@ -0,0 +1,607 @@ +// Filename:- g2export.cpp +// + +#include "q3data.h" +#include +/* +#include "system.h" // stuff needed for caller program, standar headers etc +#include "oddbits.h" +// +*/ +#include "matrix4.h" +#include "mdx_format.h" +#include "matcomp.h" +// +#include "g2export_interface.h" +#include "g2export.h" + +#define BASEDIRNAME "base" + + +static LPCSTR Filename_StripQuakeBase(LPCSTR psFullPathedName) +{ + static char sTemp[1024]; + + int iQuakeLen = strlen(gamedir); + strcpy(sTemp,&psFullPathedName[iQuakeLen]); + + return &sTemp[0]; +} + +static void ForwardSlash(char *psLocalName) +{ + while (*psLocalName) + { + if (*psLocalName == '\\') + *psLocalName = '/'; + + psLocalName++; + } +} + +// saves clogging the param stack... +// +static char *ExportGhoul2FromMD3_Main(FILE *fhGLM, /*FILE *fhGLA,*/ LPCSTR psFullPathedNameGLM) +{ + char *psErrMess = NULL; + + const int iMallocSize = 1024*1024*20; // 20MB should be enough for one model... :-) + byte *pbBuffer = (byte *) malloc(iMallocSize); + if (pbBuffer) + { + // filename used in both files... + // +// char sLocalNameGLA[MAX_QPATH]; +// strcpy( sLocalNameGLA,Filename_WithoutExt(Filename_StripQuakeBase(psFullPathedNameGLM))); +// ForwardSlash(sLocalNameGLA); + + // start writing... + // + byte *at = pbBuffer; + + // write GLM Header... + // + mdxmHeader_t *pMDXMHeader = (mdxmHeader_t *) at; + at += sizeof(mdxmHeader_t); + + {// GLM brace-match for skipping... + + pMDXMHeader->ident = MDXM_IDENT; + pMDXMHeader->version = MDXM_VERSION; + strcpy( pMDXMHeader->name,Filename_StripQuakeBase(psFullPathedNameGLM)); + strcpy( pMDXMHeader->animName,sDEFAULT_GLA_NAME);//sLocalNameGLA); + pMDXMHeader->animIndex = 0; // ingame use only + pMDXMHeader->numBones = 1; // ... and a fake one at that. + pMDXMHeader->numLODs = giNumLODs; + // pMDXMHeader->ofsLODs = ????????????????????????????????????????????? + pMDXMHeader->numSurfaces = giNumSurfaces + giNumTags; + // pMDXMHeader->ofsSurfHierarchy= ????????????????????????????????????????????? + // pMDXMHeader->ofsEnd = ????????????????????????????????????????????? + + + + // for hierarchiy purposes, I'm going to just write out the first surface as the parent, + // then make every other surface a child of that one... + // + // G2 surfaces come from MD3 meshes first, then the MD3 tags (since G2 uses tag surfaces) + // + mdxmHierarchyOffsets_t *pHierarchyOffsets = (mdxmHierarchyOffsets_t *) at; + at += sizeof(mdxmHierarchyOffsets_t) * pMDXMHeader->numSurfaces; + + pMDXMHeader->ofsSurfHierarchy = at - (byte *) pMDXMHeader; + + for (int iSurfaceIndex = 0; iSurfaceIndex < pMDXMHeader->numSurfaces; iSurfaceIndex++) + { + // Note: bool bSurfaceIsTag == (iSurfaceIndex < giNumSurfaces) + // + mdxmSurfHierarchy_t *pSurfHierarchy = (mdxmSurfHierarchy_t *) at; + // + // store this offset... + // + pHierarchyOffsets->offsets[iSurfaceIndex] = (byte *)pSurfHierarchy - (byte *)pHierarchyOffsets; + + // fill in surf hierarchy struct... + // + strcpy( pSurfHierarchy->name, G2Exporter_Surface_GetName(iSurfaceIndex)); + + pSurfHierarchy->flags = 0; + + if ( iSurfaceIndex >= giNumSurfaces) + { + pSurfHierarchy->flags |= G2SURFACEFLAG_ISBOLT; + } + if (!strnicmp(&pSurfHierarchy->name[strlen(pSurfHierarchy->name)-4],"_off",4)) + { + pSurfHierarchy->flags |= G2SURFACEFLAG_OFF; + } + + + strcpy( pSurfHierarchy->shader, G2Exporter_Surface_GetShaderName(iSurfaceIndex)); + pSurfHierarchy->shaderIndex = 0; // ingame use only + pSurfHierarchy->parentIndex = iSurfaceIndex?0:-1; + pSurfHierarchy->numChildren = iSurfaceIndex?0:pMDXMHeader->numSurfaces-1; + if (!iSurfaceIndex) + { + for (int i=0; inumChildren; i++) + { + pSurfHierarchy->childIndexes[i] = i+1; + } + } + + int iThisHierarchySize = (int)( &((mdxmSurfHierarchy_t *)0)->childIndexes[ pSurfHierarchy->numChildren ] ); + + at += iThisHierarchySize; + } + + + + // write out LODs... + // + pMDXMHeader->ofsLODs = at - (byte *) pMDXMHeader; + for (int iLODIndex = 0; iLODIndex < pMDXMHeader->numLODs; iLODIndex++) + { + mdxmLOD_t *pLOD = (mdxmLOD_t *) at; + at += sizeof(mdxmLOD_t); + + mdxmLODSurfOffset_t *pLODSurfOffsets = (mdxmLODSurfOffset_t *) at; + at += sizeof(mdxmLODSurfOffset_t) * pMDXMHeader->numSurfaces; + + for (int iSurfaceIndex = 0; iSurfaceIndex < pMDXMHeader->numSurfaces; iSurfaceIndex++) + { + mdxmSurface_t *pSurface = (mdxmSurface_t *) at; + at += sizeof(mdxmSurface_t); + // + // store this offset... + // + pLODSurfOffsets->offsets[iSurfaceIndex] = (byte *)pSurface - (byte *) pLODSurfOffsets; + // + // fill in this surface struct... + // + pSurface->ident = 0; // ingame-use only, defaulted to 0 here + pSurface->thisSurfaceIndex = iSurfaceIndex; + pSurface->ofsHeader = (byte *)pMDXMHeader - (byte *)pSurface; // offset back to main header + pSurface->numVerts = G2Exporter_Surface_GetNumVerts(iSurfaceIndex, iLODIndex); + pSurface->numTriangles = G2Exporter_Surface_GetNumTris (iSurfaceIndex, iLODIndex); + pSurface->maxVertBoneWeights= 1; // :-) + + // + // write out triangles... + // + pSurface->ofsTriangles = at - (byte *)pSurface; + for (int iTriangleIndex = 0; iTriangleIndex < pSurface->numTriangles; iTriangleIndex++) + { + mdxmTriangle_t *pTriangle = (mdxmTriangle_t *) at; + at += sizeof(mdxmTriangle_t); + + for (int i=0; i<3; i++) + { + pTriangle->indexes[i] = G2Exporter_Surface_GetTriIndex(iSurfaceIndex,iTriangleIndex,i, iLODIndex); + } + } + + // + // write out verts...(when exporting from MD3 these are all weighted to only 1 bone) + // + pSurface->ofsVerts = at - (byte *)pSurface; + for (int iVertIndex = 0; iVertIndex < pSurface->numVerts; iVertIndex++) + { + mdxmVertex_t *pVert = (mdxmVertex_t *) at; + + memcpy(pVert->normal, G2Exporter_Surface_GetVertNormal(iSurfaceIndex,iVertIndex,iLODIndex),sizeof(vec3_t)); + memcpy(pVert->vertCoords,G2Exporter_Surface_GetVertCoords(iSurfaceIndex,iVertIndex,iLODIndex),sizeof(vec3_t)); + memcpy(pVert->texCoords, G2Exporter_Surface_GetTexCoords (iSurfaceIndex,iVertIndex,iLODIndex),sizeof(vec2_t)); + pVert->numWeights = 1; // force-weight every vert... + + for (int iWeight=0; iWeightnumWeights; iWeight++) + { + pVert->weights[iWeight].boneIndex = 0; + pVert->weights[iWeight].boneWeight = 1.0f; + } + + at = (byte *) &pVert->weights[pVert->numWeights]; + } + + // remaining surface struct fields... + // + pSurface->numBoneReferences = 1; + pSurface->ofsBoneReferences = at - (byte *) pSurface; + int *piBonesUsed = (int *) at; + at += pSurface->numBoneReferences * sizeof(int); + piBonesUsed[0] = 0; // the one and only bone ref + + pSurface->ofsEnd = at - (byte *) pSurface; + } + + pLOD->ofsEnd = at - (byte *) pLOD; + } + + pMDXMHeader->ofsEnd = at - (byte *) pMDXMHeader; + } + + +/* + // now create GLA file... + // + mdxaHeader_t *pMDXAHeader = (mdxaHeader_t *) at; + at += sizeof(mdxaHeader_t); + {// for brace-skipping... + + pMDXAHeader->ident = MDXA_IDENT; + pMDXAHeader->version = MDXA_VERSION; + strncpy(pMDXAHeader->name,sLocalNameGLA,sizeof(pMDXAHeader->name)); + pMDXAHeader->name[sizeof(pMDXAHeader->name)-1]='\0'; + pMDXAHeader->fScale = 1.0f; + pMDXAHeader->numFrames = 1; // inherently, when doing MD3 to G2 files +// pMDXAHeader->ofsFrames = ?????????????????? + pMDXAHeader->numBones = 1; // inherently, when doing MD3 to G2 files +// pMDXAHeader->ofsCompBonePool= ?????????????????? +// pMDXAHeader->ofsSkel = ?????????????????? +// pMDXAHeader->ofsEnd = ?????????????????? + + // write out bone hierarchy... + // + mdxaSkelOffsets_t * pSkelOffsets = (mdxaSkelOffsets_t *) at; + at += (int)( &((mdxaSkelOffsets_t *)0)->offsets[ pMDXAHeader->numBones ] ); + + pMDXAHeader->ofsSkel = at - (byte *) pMDXAHeader; + for (int iSkelIndex = 0; iSkelIndex < pMDXAHeader->numBones; iSkelIndex++) + { + mdxaSkel_t *pSkel = (mdxaSkel_t *) at; + + pSkelOffsets->offsets[iSkelIndex] = (byte *) pSkel - (byte *) pSkelOffsets; + + // setup flags... + // + pSkel->flags = 0; + strcpy( pSkel->name, "Generated by Q3Data"); // doesn't matter what this is called I believe. + pSkel->parent= -1; // index of bone that is parent to this one, -1 = NULL/root + + Matrix4 BasePose; + BasePose.Identity(); + BasePose.To3x4(pSkel->BasePoseMat.matrix); + + Matrix4 BasePoseInverse; + BasePoseInverse.Inverse(BasePose); + BasePoseInverse.To3x4(pSkel->BasePoseMatInv.matrix); + + pSkel->numChildren = 0; // inherently, when doing MD3 to G2 files + + int iThisSkelSize = (int)( &((mdxaSkel_t *)0)->children[ pSkel->numChildren ] ); + + at += iThisSkelSize; + } + + + // write out frames... + // + pMDXAHeader->ofsFrames = at - (byte *) pMDXAHeader; + int iFrameSize = (int)( &((mdxaFrame_t *)0)->boneIndexes[ pMDXAHeader->numBones ] ); + for (int i=0; inumFrames; i++) + { + mdxaFrame_t *pFrame = (mdxaFrame_t *) at; + at += iFrameSize; // variable sized struct + + pFrame->boneIndexes[0] = 0; // inherently, when doing MD3 to G2 files + } + + // now write out compressed bone pool... + // + pMDXAHeader->ofsCompBonePool = at - (byte *) pMDXAHeader; + for (int iCompBoneIndex = 0; iCompBoneIndex < 1 //CompressedBones.size() + ; iCompBoneIndex++) + { + mdxaCompBone_t *pCompBone = (mdxaCompBone_t *) at; + at += sizeof(mdxaCompBone_t); + + float matThis[3][4]; + + Matrix4 Bone; + Bone.Identity(); + Bone.To3x4(matThis); + + byte Comp[sizeof(mdxaCompBone_t)]; + MC_Compress(matThis,Comp); + + memcpy(pCompBone->Comp,Comp,sizeof(Comp)); + } + +// int iPoolBytesUsed = (CompressedBones.size()*MC_COMP_BYTES) + (iStats_NumBoneRefs*sizeof(int)); +// printf("( Compressed bone bytes: %d, using pool = %d. Saving = %d bytes )\n",iOldBoneSize,iPoolBytesUsed, iOldBoneSize - iPoolBytesUsed); + + // done... + // + pMDXAHeader->ofsEnd = at - (byte *) pMDXAHeader; + } +*/ + + // write 'em out to disk... + // + int iGLMSize = fwrite(pMDXMHeader,1,/*(byte *)pMDXAHeader - (byte *)pMDXMHeader*/ pMDXMHeader->ofsEnd, fhGLM); + assert(iGLMSize == pMDXMHeader->ofsEnd); + +// int iGLASize = fwrite(pMDXAHeader,1,at - (byte *)pMDXAHeader, fhGLA); +// assert(iGLASize == pMDXAHeader->ofsEnd); + + // and finally... + // + free(pbBuffer); + } + else + { + psErrMess = va("Error! Unable to allocate %d bytes for workspace!\n",iMallocSize); + } + + return psErrMess; +} + + +GLM_ImportModel_t ImportedModel; + + +// return = NULL for no error, else string of error for caller to display... +// +char *ExportGhoul2FromMD3_ImportExisting(LPCSTR psFullPathedFilename) +{ + char *psErrorMess = NULL; + void *pvData = NULL; + + ImportedModel.ImportedLODs.clear(); + ImportedModel.SurfaceIndexRemaps.clear(); + + FILE *fhHandle = fopen( psFullPathedFilename, "rb" ); + if (fhHandle) + { + long lLen = filelength( fileno( fhHandle ) ); + + pvData = malloc( lLen ); + if (pvData) + { + long lBytesRead = fread( pvData, 1, lLen, fhHandle ); + + if (lBytesRead == lLen) + { + mdxmHeader_t *pMDXMHeader = (mdxmHeader_t *) pvData; + + if (pMDXMHeader->ident == MDXM_IDENT) + { + if (pMDXMHeader->version == MDXM_VERSION) + { + if (!strcmp(pMDXMHeader->animName, sDEFAULT_GLA_NAME)) + { + if (pMDXMHeader->numBones == 1) // actually we can skip this because of the sDEFAULT_GLA_NAME check, but wtf? + { + if (pMDXMHeader->numSurfaces == giNumSurfaces + giNumTags) + { + // now for the important stuff, let's read some data... + // + // setup remap tables so export code can query imported model transparently with same surface indexes as + // q3data version (they should be the same, but it's possible to not be and still be legal)... + // + mdxmHierarchyOffsets_t *pHierarchyOffsets = (mdxmHierarchyOffsets_t *) ((byte*)pMDXMHeader + sizeof(*pMDXMHeader)); + // + // scan imported surface names... + // + for (int iSurface = 0; iSurface < pMDXMHeader->numSurfaces; iSurface++) + { + mdxmSurfHierarchy_t *pSurfHierarchy = (mdxmSurfHierarchy_t *) ((byte*)pHierarchyOffsets + pHierarchyOffsets->offsets[iSurface]); + LPCSTR psSurfaceName = pSurfHierarchy->name; + // + // ... to match current qdata surface names... + // + for (int iQ3Surface = 0; iQ3Surface < pMDXMHeader->numSurfaces/*the same at this point*/; iQ3Surface++) + { + LPCSTR psQ3SurfaceName = G2Exporter_Surface_GetName(iQ3Surface); + + if (!stricmp(psSurfaceName,psQ3SurfaceName)) + { + ImportedModel.SurfaceIndexRemaps[iQ3Surface] = iSurface; // new remap table entry + break; + } + } + if (iQ3Surface == pMDXMHeader->numSurfaces) + { + psErrorMess = va("Unable to find imported surface \"%s\" in current Q3data model\n",psSurfaceName); + break; + } + } + + if (psErrorMess == NULL) // worth carrying on? + { + // now read the actual imported data... + // + ImportedModel.ImportedLODs.resize(pMDXMHeader->numLODs); + + mdxmLOD_t *pLOD = (mdxmLOD_t *) ((byte*) pMDXMHeader + pMDXMHeader->ofsLODs); + for (int iLOD = 0; iLOD < pMDXMHeader->numLODs; iLOD++) + { + mdxmLODSurfOffset_t *pLODSurfOffset = (mdxmLODSurfOffset_t *) &pLOD[1]; + + for (iSurface = 0; iSurface < pMDXMHeader->numSurfaces; iSurface++) + { + GLM_ImportSurface_t GLM_ImportSurface; + + mdxmSurface_t *pSurface = (mdxmSurface_t *) ((byte *) pLODSurfOffset + pLODSurfOffset->offsets[iSurface]); + + // triangles... + // + mdxmTriangle_t *pTriangle = (mdxmTriangle_t *) ((byte*) pSurface + pSurface->ofsTriangles); + for (int iTriangle = 0; iTriangle < pSurface->numTriangles; iTriangle++, pTriangle++) + { + GLM_ImportSurface.ImportedTris.push_back(*pTriangle); + } + + // verts... + // + mdxmVertex_t *pVert = (mdxmVertex_t *) ((byte*) pSurface + pSurface->ofsVerts); + for (int iVert = 0; iVert < pSurface->numVerts; iVert++) + { + GLM_ImportVertex_t GLM_ImportVertex; + + memcpy(GLM_ImportVertex.normal, pVert->normal, sizeof(GLM_ImportVertex.normal)); + memcpy(GLM_ImportVertex.vertCoords, pVert->vertCoords, sizeof(GLM_ImportVertex.vertCoords)); + memcpy(GLM_ImportVertex.texCoords, pVert->texCoords, sizeof(GLM_ImportVertex.texCoords)); + + GLM_ImportSurface.ImportedVerts.push_back(GLM_ImportVertex); + + pVert = (mdxmVertex_t *)&pVert->weights[/*pVert->numWeights*/pSurface->maxVertBoneWeights]; + } + + // done, so add this imported surface to the imported model... + // + ImportedModel.ImportedLODs[iLOD].ImportedSurfaces.push_back(GLM_ImportSurface); + } + + pLOD = (mdxmLOD_t *)((byte *)pLOD + pLOD->ofsEnd); + } + + // sanity... + // + assert(ImportedModel.SurfaceIndexRemaps.size() == pMDXMHeader->numSurfaces); + assert(ImportedModel.ImportedLODs.size() == pMDXMHeader->numLODs); + assert(ImportedModel.ImportedLODs[0].ImportedSurfaces.size() == pMDXMHeader->numSurfaces); + + // and finally, tell the export q3data-query code how many LODS there now are, + // so it knows when to query this data as opposed to the current q3data model... + // + giNumLODs += pMDXMHeader->numLODs; + } + } + else + { + psErrorMess = va("ExportGhoul2FromMD3_ImportExisting(): found %d surfaces instead of %d\nFile:\n\"%s\"\n",pMDXMHeader->numSurfaces, giNumSurfaces + giNumTags, psFullPathedFilename); + } + } + else + { + psErrorMess = va("ExportGhoul2FromMD3_ImportExisting(): found %d bones instead of %d\nFile: \"%s\"\n",pMDXMHeader->numBones,1, psFullPathedFilename); + } + } + else + { + psErrorMess = va("ExportGhoul2FromMD3_ImportExisting(): File did not have \"%s\" as animName.\nFile:\"%s\"\n",sDEFAULT_GLA_NAME, psFullPathedFilename); + } + } + else + { + psErrorMess = va("ExportGhoul2FromMD3_ImportExisting(): Bad version (%d, expecting %d) on file:\n\"%s\"\n",pMDXMHeader->version, MDXM_VERSION, psFullPathedFilename); + } + } + else + { + psErrorMess = va("ExportGhoul2FromMD3_ImportExisting(): Bad ident on file \"%s\"\n",psFullPathedFilename); + } + } + else + { + psErrorMess = va("ExportGhoul2FromMD3_ImportExisting(): Short read (%d bytes instead of %d)\nFile = \"%s\"\n",lBytesRead,lLen,psFullPathedFilename); + } + } + else + { + psErrorMess = va("ExportGhoul2FromMD3_ImportExisting(): Unable to allocate %d bytes\n",lLen); + } + } + else + { + psErrorMess = va("ExportGhoul2FromMD3_ImportExisting():\nUnable to open file \"%s\" for append!\n",psFullPathedFilename); + } + + if (fhHandle) + { + fclose(fhHandle); +// fhHandle = NULL; + } + + if (pvData) + { + free(pvData); + pvData = NULL; + } + + return psErrorMess; +} + + + +// assumes 'psFullPathedFilename' is full-pathed filename with ".glm" on the end, +// ppsFullPathedNameGLA is optional feedback param if you want to know what the GLA name was. +// +// return = NULL for no error, else string of error for caller to display... +// +extern "C" void CreatePath (const char *path); +char *ExportGhoul2FromMD3(LPCSTR psFullPathedFilename, int iNumLODs, int iNumSurfaces, int iNumTags +// ,LPCSTR *ppsFullPathedNameGLA /* = NULL */ + ) +{ + char *psErrorMess = NULL; + + giNumLODs = iNumLODs; + giNumSurfaces = iNumSurfaces; + giNumTags = iNumTags; + + CreatePath(psFullPathedFilename); // this has already been done earlier in q3data, but wtf? + + // new, we need to work out if this file is to be an output, or a LOD append (ie are we "_1.glm") ? + // + bool bAppending = false; + char sTemp[2048]; + strcpy(sTemp, Filename_WithoutExt( psFullPathedFilename )); + char *psSuffixPos = &sTemp[strlen(sTemp)-2]; + if (psSuffixPos[0] == '_' && isdigit(psSuffixPos[1])) + { + bAppending = true; + + // since we're appending, we first of all need to get the data from the file already out there... + // + *psSuffixPos='\0'; // chop off trailing "_1" etc + strcat(sTemp, ".glm"); + psErrorMess = ExportGhoul2FromMD3_ImportExisting( sTemp ); + if (psErrorMess) + return psErrorMess; // which will cause an app-exit + } + + FILE *fhGLM = fopen(bAppending?sTemp:psFullPathedFilename,"wb"); + if ( fhGLM) + { + static char sNameGLA[MAX_QPATH]; + +// strcpy(sNameGLA, Filename_WithoutExt(psFullPathedFilename) ); +// strcat(sNameGLA, ".gla"); + strcpy(sNameGLA, sDEFAULT_GLA_NAME ); + +// CreatePath(sNameGLA); +// FILE *fhGLA = fopen(sNameGLA,"wb"); +// if ( fhGLA) + { +// if ( ppsFullPathedNameGLA) +// { +// *ppsFullPathedNameGLA = &sNameGLA[0]; +// } + + psErrorMess = ExportGhoul2FromMD3_Main(fhGLM, /*fhGLA,*/ psFullPathedFilename); + +// fclose(fhGLA); + } +// else +// { +// psErrorMess = va("Error: Unable to open file '%s'!\n"); +// } + + fclose(fhGLM); + } + else + { + psErrorMess = va("Error: Unable to open file '%s'!\n"); + } + + if (bAppending && !psErrorMess) + { + printf("\n( Appended LOD to \"%s\" )\n\n",sTemp); + } + return psErrorMess; +} + + + +/////////////////// eof //////////////////// + diff --git a/utils/q3data/g2export.h b/utils/q3data/g2export.h new file mode 100644 index 0000000..7411948 --- /dev/null +++ b/utils/q3data/g2export.h @@ -0,0 +1,31 @@ +// Filename:- g2export.h +// + +#ifndef G2EXPORT_H +#define G2EXPORT_H + +#ifndef LPCSTR +#define LPCSTR const char * +#endif + + +#ifdef __cplusplus +extern "C" +{ +#endif +char *ExportGhoul2FromMD3(LPCSTR psFullPathedFilename, int iNumLODs, int iNumSurfaces, int iNumTags + //,LPCSTR *ppsFullPathedNameGLA /*= NULL*/ + ); + +#ifdef __cplusplus +} +#endif + + + + + +#endif // #ifndef G2EXPORT_H + +//////////////// eof /////////////// + diff --git a/utils/q3data/g2export_interface.cpp b/utils/q3data/g2export_interface.cpp new file mode 100644 index 0000000..05fbef2 --- /dev/null +++ b/utils/q3data/g2export_interface.cpp @@ -0,0 +1,475 @@ +// Filename:- g2export_interface.cpp +// +// This file contains interface between the functions that the ghoul2 exporter queries to get model info, and the app itself. + +#include +#include "q3data.h" +#include "models.h" +#include "mdx_format.h" +#include "matrix4.h" +// +#include "g2export_interface.h" + + +int giNumLODs; +int giNumSurfaces; +int giNumTags; + + +char *va(char *format, ...) +{ + va_list argptr; + static char string[8][1024]; + static int i=0; + + i=(i+1)&7; + + va_start (argptr, format); + vsprintf (string[i], format,argptr); + va_end (argptr); + + return string[i]; +} + + +// returns (eg) "\dir\name" for "\dir\name.bmp" +// +char *Filename_WithoutExt(LPCSTR psFilename) +{ + static char sString[MAX_PATH]; // *not* MAX_QPATH! + char *p; + char *p2; + + strcpy(sString,psFilename); + + while ((p = strchr(sString,'/'))!=NULL) *p='\\'; // q3data filenames have both types of slashes... sigh + + p = strrchr(sString,'.'); + p2= strrchr(sString,'\\'); + + // special check, make sure the first suffix we found from the end wasn't just a directory suffix (eg on a path'd filename with no extension anyway) + // + if (p && (p2==0 || (p>p2))) + *p=0; + + return sString; + +} + + +static md3SurfaceData_t *GetMD3SurfaceData(int iSurfaceIndex) +{ + // unfortunately, tags are still left as surfaces internally to this app, so I need to skip over them instead of + // just using the index directly...(sigh) + + int iSurfNumber = 0; + for ( int i = 0; i < g_data.model.numSurfaces; i++ ) + { + md3SurfaceData_t *pSurfData = &g_data.surfData[i]; + if ( strstr( pSurfData->header.name, "tag_" ) != pSurfData->header.name ) + { + md3Surface_t *psurf = &pSurfData->header; + + if ( psurf->numTriangles == 0 || psurf->numVerts == 0 ) + continue; + + if (iSurfNumber++ == iSurfaceIndex) + { + return pSurfData; + } + } + } + + assert(0); + return NULL; +} + +LPCSTR G2Exporter_Surface_GetName(int iSurfaceIndex) +{ + if (iSurfaceIndex < giNumSurfaces) + { + // standard surface... + // + md3SurfaceData_t *pSurfaceData = GetMD3SurfaceData(iSurfaceIndex); + if (pSurfaceData) + { + md3Surface_t *pSurf = &pSurfaceData->header; + + // return it without any trailing "_n" digits... + // + static char sTemp[1024]; + strcpy(sTemp,pSurf->name); + char *pSuffix = &sTemp[strlen(sTemp)-2]; + if (pSuffix[0] == '_' && isdigit(pSuffix[1])) + { + *pSuffix = '\0'; + } + return sTemp; + } + } + else + { + // tag surface... + // + static char sTemp[1024]; + md3Tag_t *pTag = &g_data.tags[0][iSurfaceIndex - giNumSurfaces]; + // + // prepend name with a '*', and remove "tag_" from name start if present... + // + strcpy(sTemp,"*"); + strcat(sTemp, (!strnicmp(pTag->name,"tag_",4)) ? &pTag->name[4] : pTag->name); + return sTemp; + } + + assert(0); + return "Error"; +} + +LPCSTR G2Exporter_Surface_GetShaderName(int iSurfaceIndex) +{ + if (iSurfaceIndex < giNumSurfaces) + { + // standard surface... + // + md3SurfaceData_t *pSurfaceData = GetMD3SurfaceData(iSurfaceIndex); + if (pSurfaceData) + { + return pSurfaceData->shaders[0].name; + } + } + else + { + // tag surface... + // + return "[NoMaterial]"; + } + + assert(0); + return "Error"; +} + +int G2Exporter_Surface_GetNumVerts(int iSurfaceIndex, int iLODIndex) +{ + if (iLODIndex == giNumLODs-1) + { + // q3data surface... + // + if (iSurfaceIndex < giNumSurfaces) + { + // standard surface... + // + md3SurfaceData_t *pSurfaceData = GetMD3SurfaceData(iSurfaceIndex); + if (pSurfaceData) + { + md3Surface_t *pSurf = &pSurfaceData->header; + return pSurf->numVerts; + } + } + else + { + // tag surface... + // + return 3; // for one triangle + } + } + else + { + // imported surface... + // + return ImportedModel.ImportedLODs[iLODIndex].ImportedSurfaces[ImportedModel.SurfaceIndexRemaps[iSurfaceIndex]].ImportedVerts.size(); + } + + assert(0); + return 0; +} + +int G2Exporter_Surface_GetNumTris(int iSurfaceIndex, int iLODIndex) +{ + if (iLODIndex == giNumLODs-1) + { + // q3data surface... + // + if (iSurfaceIndex < giNumSurfaces) + { + // standard surface... + // + md3SurfaceData_t *pSurfaceData = GetMD3SurfaceData(iSurfaceIndex); + if (pSurfaceData) + { + md3Surface_t *pSurf = &pSurfaceData->header; + return pSurf->numTriangles; + } + } + else + { + // tag surface... + // + return 1; // for one triangle + } + } + else + { + // imported surface... + // + return ImportedModel.ImportedLODs[iLODIndex].ImportedSurfaces[ImportedModel.SurfaceIndexRemaps[iSurfaceIndex]].ImportedTris.size(); + } + + assert(0); + return 0; +} + +int G2Exporter_Surface_GetTriIndex(int iSurfaceIndex, int iTriangleIndex, int iTriVert, int iLODIndex) +{ + if (iLODIndex == giNumLODs-1) + { + // q3data surface... + // + if (iSurfaceIndex < giNumSurfaces) + { + // standard surface... + // + md3SurfaceData_t *pSurfaceData = GetMD3SurfaceData(iSurfaceIndex); + if (pSurfaceData) + { + return pSurfaceData->orderedTriangles[iTriangleIndex][iTriVert]; + } + } + else + { + // tag surface... + // + return iTriVert; + } + } + else + { + // imported surface... + // + return ImportedModel.ImportedLODs[iLODIndex].ImportedSurfaces[ImportedModel.SurfaceIndexRemaps[iSurfaceIndex]].ImportedTris[iTriangleIndex].indexes[iTriVert]; + } + + assert(0); + return 0; +} + +vec3_t *G2Exporter_Surface_GetVertNormal(int iSurfaceIndex, int iVertIndex, int iLODIndex) +{ + static vec3_t v3={0}; + memset(v3,0,sizeof(v3)); + + if (iLODIndex == giNumLODs-1) + { + // q3data surface... + // + if (iSurfaceIndex < giNumSurfaces) + { + // standard surface... + // + md3SurfaceData_t *pSurfaceData = GetMD3SurfaceData(iSurfaceIndex); + if (pSurfaceData) + { + // this logic is kinda gay, not sure why the *6 etc, but that's how other q3data code works, so... + // + float **ppVerts = pSurfaceData->verts; + memcpy(v3,(vec3_t*) &ppVerts[0][iVertIndex*6+3], sizeof(v3)); + + { + Matrix4 Swap; + Swap.Identity(); + + if (1) + { + Swap.SetRow(0,Vect3(0.0f,-1.0f,0.0f)); + Swap.SetRow(1,Vect3(1.0f,0.0f,0.0f)); + } + Swap.CalcFlags(); + + Vect3 v3In((const float *)v3); + static Vect3 v3Out; + Swap.XFormVect(v3Out,v3In); + return (vec3_t*) &v3Out; + } + } + } + else + { + // tag surface... + // + // I don't think tag-surfaces normals have any meaning for ghoul2, so... + // + return &v3; + } + } + else + { + // imported surface... + // + vec3_t &v3Src = ImportedModel.ImportedLODs[iLODIndex].ImportedSurfaces[ImportedModel.SurfaceIndexRemaps[iSurfaceIndex]].ImportedVerts[iVertIndex].normal; + memcpy(v3,v3Src,sizeof(v3)); + return &v3; + } + + assert(0); + return &v3; +} + +vec3_t *G2Exporter_Surface_GetVertCoords(int iSurfaceIndex, int iVertIndex, int iLODIndex) +{ + static vec3_t v3={0}; + memset(&v3,0,sizeof(v3)); + + if (iLODIndex == giNumLODs-1) + { + // q3data surface... + // + if (iSurfaceIndex < giNumSurfaces) + { + // standard surface... + // + md3SurfaceData_t *pSurfaceData = GetMD3SurfaceData(iSurfaceIndex); + if (pSurfaceData) + { + // this logic is kinda gay, not sure why the *6 etc, but that's how other q3data code works, so... + // + float **ppVerts = pSurfaceData->verts; + static vec3_t v3; + for (int i=0; i<3; i++) + { + v3[i] = ppVerts[0][iVertIndex*6+i];// /MD3_XYZ_SCALE; + } + // return &v3; + + Matrix4 Swap; + Swap.Identity(); + + if (1) + { + Swap.SetRow(0,Vect3(0.0f,-1.0f,0.0f)); + Swap.SetRow(1,Vect3(1.0f,0.0f,0.0f)); + } + Swap.CalcFlags(); + + Vect3 v3In((const float *)v3); + static Vect3 v3Out; + Swap.XFormVect(v3Out,v3In); + return (vec3_t*) &v3Out; + } + } + else + { + // tag surface... + // + assert(iVertIndex<3); + + md3Tag_t *pTag = &g_data.tags[0][iSurfaceIndex - giNumSurfaces]; + vec3_t v3New; + + //#ifdef PERFECT_CONVERSION + + v3New[0] = pTag->axis[0][iVertIndex] ; + v3New[1] = pTag->axis[1][iVertIndex] ; + v3New[2] = pTag->axis[2][iVertIndex] ; + + // don't worry about how this crap works, it just does (arrived at by empirical methods... :-) + // + // (mega-thanks to Gil as usual) + // + if (iVertIndex==2) + { + VectorCopy(pTag->origin,v3); + } + else + if (iVertIndex==1) + { + v3New[0] = 2.0f * pTag->axis[1][iG2_TRISIDE_MIDDLE]; + v3New[1] = -(2.0f * pTag->axis[0][iG2_TRISIDE_MIDDLE]); + v3New[2] = 2.0f * pTag->axis[2][iG2_TRISIDE_MIDDLE]; + + VectorSubtract(pTag->origin,v3New,v3); + } + else + { + v3New[0] = pTag->axis[1][iG2_TRISIDE_LONGEST]; + v3New[1] = -pTag->axis[0][iG2_TRISIDE_LONGEST]; + v3New[2] = pTag->axis[2][iG2_TRISIDE_LONGEST]; + + VectorSubtract(pTag->origin,v3New,v3); + } + + // return (vec3_t*) &v3; + + Matrix4 Swap; + Swap.Identity(); + + if (1) + { + Swap.SetRow(0,Vect3(0.0f,-1.0f,0.0f)); + Swap.SetRow(1,Vect3(1.0f,0.0f,0.0f)); + } + Swap.CalcFlags(); + + Vect3 v3In((const float *)v3); + static Vect3 v3Out; + Swap.XFormVect(v3Out,v3In); + + return (vec3_t*) &v3Out; + } + } + else + { + // imported surface... + // + vec3_t &v3Src = ImportedModel.ImportedLODs[iLODIndex].ImportedSurfaces[ImportedModel.SurfaceIndexRemaps[iSurfaceIndex]].ImportedVerts[iVertIndex].vertCoords; + memcpy(v3,v3Src,sizeof(v3)); + return &v3; + } + + assert(0); + return &v3; +} + +vec2_t *G2Exporter_Surface_GetTexCoords(int iSurfaceIndex, int iVertIndex, int iLODIndex) +{ + static vec2_t v2={0}; + memset(v2,0,sizeof(v2)); + + if (iLODIndex == giNumLODs-1) + { + // q3data surface... + // + if (iSurfaceIndex < giNumSurfaces) + { + // standard surface... + // + md3SurfaceData_t *pSurfaceData = GetMD3SurfaceData(iSurfaceIndex); + if (pSurfaceData) + { + baseVertex_t *pBaseVertex = pSurfaceData->baseVertexes; + return (vec2_t*) &pBaseVertex[iVertIndex].st[0]; + } + } + else + { + // tag surface... + // + // Texture coords aren't relevant for tag-surfaces, so... + // + return &v2; + } + } + else + { + // imported surface... + // + vec2_t &v2Src = ImportedModel.ImportedLODs[iLODIndex].ImportedSurfaces[ImportedModel.SurfaceIndexRemaps[iSurfaceIndex]].ImportedVerts[iVertIndex].texCoords; + memcpy(v2,v2Src,sizeof(v2)); + return &v2; + } + + assert(0); + return &v2; +} + + +//////////////////// eof ///////////////////// + diff --git a/utils/q3data/g2export_interface.h b/utils/q3data/g2export_interface.h new file mode 100644 index 0000000..1a577f7 --- /dev/null +++ b/utils/q3data/g2export_interface.h @@ -0,0 +1,81 @@ +// Filename:- g2export_interface.h +// + + +#ifndef G2_EXPORT_INTERFACE_H +#define G2_EXPORT_INTERFACE_H + +#ifndef LPCSTR +#define LPCSTR const char * +#endif + +#ifndef MAX_PATH +#define MAX_PATH 160 // *not* MAX_QPATH +#endif + + + +char *va(char *format, ...); +char *Filename_WithoutExt(LPCSTR psFilename); + + +extern int giNumLODs; +extern int giNumSurfaces; +extern int giNumTags; + + + +// ghoul2 exporter interface functions... +// +LPCSTR G2Exporter_Surface_GetName(int iSurfaceIndex); +LPCSTR G2Exporter_Surface_GetShaderName(int iSurfaceIndex); +int G2Exporter_Surface_GetNumVerts (int iSurfaceIndex, int iLODIndex); +int G2Exporter_Surface_GetNumTris (int iSurfaceIndex, int iLODIndex); +int G2Exporter_Surface_GetTriIndex (int iSurfaceIndex, int iTriangleIndex, int iTriVert, int iLODIndex); +vec3_t *G2Exporter_Surface_GetVertNormal(int iSurfaceIndex, int iVertIndex, int iLODIndex); +vec3_t *G2Exporter_Surface_GetVertCoords(int iSurfaceIndex, int iVertIndex, int iLODIndex); +vec2_t *G2Exporter_Surface_GetTexCoords(int iSurfaceIndex, int iVertIndex, int iLODIndex); + + +#pragma warning ( disable : 4786) +#include +#include +using namespace std; +#pragma warning ( disable : 4786) + + + +// ghoul2 import structures used when appending to previous versions to add extra LODs... +// +typedef struct { + vec3_t normal; + vec3_t vertCoords; + vec2_t texCoords; +} GLM_ImportVertex_t; + +typedef struct +{ + vector ImportedVerts; + vector ImportedTris; +} GLM_ImportSurface_t; + +typedef struct +{ + vector ImportedSurfaces; +} GLM_ImportLOD_t; + +typedef struct +{ + vector ImportedLODs; + map SurfaceIndexRemaps; // key = q3data surface index, value = corresponding index in imported model +} GLM_ImportModel_t; + +extern GLM_ImportModel_t ImportedModel; + + +#endif // #ifndef G2_EXPORT_INTERFACE_H + + +//////////////////////// eof ////////////////////////// + + diff --git a/utils/q3data/images.c b/utils/q3data/images.c new file mode 100644 index 0000000..ea66175 --- /dev/null +++ b/utils/q3data/images.c @@ -0,0 +1,465 @@ +#include "q3data.h" + +byte *byteimage, *lbmpalette; +int byteimagewidth, byteimageheight; + + +char mip_prefix[1024]; // directory to dump the textures in + +qboolean colormap_issued; +byte colormap_palette[768]; + +/* +============== +Cmd_Grab + +$grab filename x y width height +============== +*/ +void Cmd_Grab (void) +{ + int xl,yl,w,h,y; + byte *cropped; + char savename[1024]; + char dest[1024]; + + GetToken (qfalse); + + if (token[0] == '/' || token[0] == '\\') + sprintf (savename, "%s%s.pcx", writedir, token+1); + else + sprintf (savename, "%spics/%s.pcx", writedir, token); + + if (g_release) + { + if (token[0] == '/' || token[0] == '\\') + sprintf (dest, "%s.pcx", token+1); + else + sprintf (dest, "pics/%s.pcx", token); + + ReleaseFile (dest); + return; + } + + GetToken (qfalse); + xl = atoi (token); + GetToken (qfalse); + yl = atoi (token); + GetToken (qfalse); + w = atoi (token); + GetToken (qfalse); + h = atoi (token); + + if (xl<0 || yl<0 || w<0 || h<0 || xl+w>byteimagewidth || yl+h>byteimageheight) + Error ("GrabPic: Bad size: %i, %i, %i, %i",xl,yl,w,h); + + // crop it to the proper size + cropped = malloc (w*h); + for (y=0 ; ybyteimagewidth || yl+h>byteimageheight) + Error ("GrabPic: Bad size: %i, %i, %i, %i",xl,yl,w,h); + + // crop it to the proper size + cropped = malloc (w*h); + for (y=0 ; y 255) + r = 255; + if (r < 0) + r = 0; + if (g > 255) + g = 255; + if (g < 0) + g = 0; + if (b > 255) + b = 255; + if (b < 0) + b = 0; +#ifndef TABLECOLORS + bestcolor = BestColor (r, g, b, 0, 254); +#else + bestcolor = palmap[r>>3][g>>3][b>>3]; +#endif + + return bestcolor; +} + + +void BuildPalmap (void) +{ +#ifdef TABLECOLORS + int r, g, b; + int bestcolor; + + if (palmap_built) + return; + palmap_built = qtrue; + + for (r=4 ; r<256 ; r+=8) + { + for (g=4 ; g<256 ; g+=8) + { + for (b=4 ; b<256 ; b+=8) + { + bestcolor = BestColor (r, g, b, 1, 254); + palmap[r>>3][g>>3][b>>3] = bestcolor; + } + } + } +#endif + + if (!colormap_issued) + Error ("You must issue a $colormap command first"); + +} + +/* +============= +AveragePixels +============= +*/ +byte AveragePixels (int count) +{ + int r,g,b; + int i; + int vis; + int pix; + int bestcolor; + byte *pal; + int fullbright; + + vis = 0; + r = g = b = 0; + fullbright = 0; + for (i=0 ; i +#include +#include +#include // for memcpy + +#define MC_MASK_X ((1<<(MC_BITS_X))-1) +#define MC_MASK_Y ((1<<(MC_BITS_Y))-1) +#define MC_MASK_Z ((1<<(MC_BITS_Z))-1) +#define MC_MASK_VECT ((1<<(MC_BITS_VECT))-1) + +#define MC_SCALE_VECT (1.0f/(float)((1<<(MC_BITS_VECT-1))-2)) + +#define MC_POS_X (0) +#define MC_SHIFT_X (0) + +#define MC_POS_Y ((((MC_BITS_X))/8)) +#define MC_SHIFT_Y ((((MC_BITS_X)%8))) + +#define MC_POS_Z ((((MC_BITS_X+MC_BITS_Y))/8)) +#define MC_SHIFT_Z ((((MC_BITS_X+MC_BITS_Y)%8))) + +#define MC_POS_V11 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z))/8)) +#define MC_SHIFT_V11 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z)%8))) + +#define MC_POS_V12 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT))/8)) +#define MC_SHIFT_V12 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT)%8))) + +#define MC_POS_V13 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*2))/8)) +#define MC_SHIFT_V13 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*2)%8))) + +#define MC_POS_V21 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*3))/8)) +#define MC_SHIFT_V21 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*3)%8))) + +#define MC_POS_V22 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*4))/8)) +#define MC_SHIFT_V22 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*4)%8))) + +#define MC_POS_V23 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*5))/8)) +#define MC_SHIFT_V23 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*5)%8))) + +#define MC_POS_V31 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*6))/8)) +#define MC_SHIFT_V31 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*6)%8))) + +#define MC_POS_V32 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*7))/8)) +#define MC_SHIFT_V32 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*7)%8))) + +#define MC_POS_V33 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*8))/8)) +#define MC_SHIFT_V33 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*8)%8))) + +void MC_Compress(const float mat[3][4],unsigned char * _comp) +{ + char comp[MC_COMP_BYTES*2]; + + int i,val; + for (i=0;i=(1<=(1<=(1<=(1<=(1<=(1<=(1<=(1<=(1<=(1<=(1<=(1<=0) + { + for (j=ii;j=0;i--) + { + sum=b[i]; + for (j=i+1;jbig) + big=temp; + if (big=big) + { + big=dum; + imax=i; + } + } + if (j!=imax) + { + for (k=0;k=0) + { + for (j=ii;j=0;i--) + { + sum=b[i]; + for (j=i+1;jbig) + big=temp; + if (big=big) + { + big=dum; + imax=i; + } + } + if (j!=imax) + { + for (k=0;kMATRIX_TOL) + return 0; + } + else + { + if (fabs(m[i][j])>MATRIX_TOL) + return 0; + } + } + } + flags=MATFLAG_IDENTITY; + return flags; +} + + +void Matrix4::Identity() +{ + int i; + float *f; + for (f=m[0],i=0;i<16;i++,f++) + *f=0; + for (i=0;i<4;i++) + m[i][i]=1.0;; + flags=MATFLAG_IDENTITY; +} + +float Matrix4::MaxAbsElement() +{ + if (flags&MATFLAG_IDENTITY) + { + assert(IntegrityCheck()); + return 1.0f; + } + int i; + float best=-1,*f; + for (f=m[0],i=0;i<16;i++,f++) + { + if (fabs(*f)>best) + best=fabs(*f); + } + return best; +} + +void Matrix4::Zero() +{ + int i; + float *f; + for (f=m[0],i=0;i<16;i++,f++) + *f=0; + flags=0; +} + +float Matrix4::Det() +{ + if (flags&MATFLAG_IDENTITY) + { + assert(IntegrityCheck()); + return 1.0f; + } + float d; + int i,indx[4]; + Matrix4 lu=*this; + if (!LUDecomposition4(lu.m,indx,&d)) + assert(0); + for (i=0;i<4;i++) + d*=lu.m[i][i]; + return d; +} + + +void Matrix4::Translate(const float tx,const float ty,const float tz) +{ + int i; + float *f; + for (f=m[0],i=0;i<12;i++,f++) + *f=0; + for (i=0;i<4;i++) + m[i][i]=1.0;; + m[3][0]=tx; + m[3][1]=ty; + m[3][2]=tz; + CalcFlags(); +} + +void Matrix4::Translate(const Vect3 &t) +{ + int i; + float *f; + for (f=m[0],i=0;i<12;i++,f++) + *f=0; + for (i=0;i<4;i++) + m[i][i]=1.0;; + m[3][0]=t[0]; + m[3][1]=t[1]; + m[3][2]=t[2]; + CalcFlags(); +} + +void Matrix4::Rotate(int axis,const float theta) +{ + int i; + float *f,s,c; + for (f=m[0],i=0;i<16;i++,f++) + *f=0; + for (i=0;i<4;i++) + m[i][i]=1.0; + s=sin(theta); + c=cos(theta); + switch(axis) + { + case 0: + m[1][1]=c; + m[1][2]=-s; + m[2][1]=s; + m[2][2]=c; + break; + case 1: + m[0][0]=c; + m[0][2]=s; + m[2][0]=-s; + m[2][2]=c; + break; + case 2: + m[0][0]=c; + m[0][1]=-s; + m[1][0]=s; + m[1][1]=c; + break; + } + CalcFlags(); +} + +void Matrix4::Rotate(const float rx,const float ry,const float rz) +{ + Matrix4 x,y,z,t; + x.Rotate(0,rx); + y.Rotate(1,ry); + z.Rotate(2,rz); + t.Concat(y,z); + Concat(x,t); +} + +void Matrix4::Rotate(const Vect3 v) +{ + Matrix4 x,y,z,t; + + x.Rotate(0, v[0]); + y.Rotate(1, v[1]); + z.Rotate(2, v[2]); + + t.Concat(y, z); + Concat(x, t); +} + +void Matrix4::Scale(const float sx,const float sy,const float sz) +{ + int i; + float *f; + for (f=m[0],i=0;i<16;i++,f++) + *f=0; + assert(fabs(sx)>1E-10); + assert(fabs(sy)>1E-10); + assert(fabs(sz)>1E-10); + m[0][0]=sx; + m[1][1]=sy; + m[2][2]=sz; + m[3][3]=1.0; + CalcFlags(); +} + +void Matrix4::Scale(const float sx) +{ + int i; + float *f; + for (f=m[0],i=0;i<16;i++,f++) + *f=0; + assert(fabs(sx)>1E-10); + m[0][0]=sx; + m[1][1]=sx; + m[2][2]=sx; + m[3][3]=1.0; + CalcFlags(); +} + +void Matrix4::Interp(const Matrix4 &m1,float s1,const Matrix4 &m2,float s2) +{ + if ((m1.flags&MATFLAG_IDENTITY)&&(m2.flags&MATFLAG_IDENTITY)) + { + assert(m1.IntegrityCheck()); + assert(m2.IntegrityCheck()); + Identity(); + return; + } + int i; + const float *f1; + const float *f2; + float *fd; + for (f1=m1.m[0],f2=m2.m[0],fd=m[0],i=0;i<16;i++,f1++,f2++,fd++) + *fd=s1*(*f1)+s2*(*f2); + flags=0; +} + +void Matrix4::Interp(const Matrix4 &m1,const Matrix4 &m2,float s1) +{ + if ((m1.flags&MATFLAG_IDENTITY)&&(m2.flags&MATFLAG_IDENTITY)) + { + assert(m1.IntegrityCheck()); + assert(m2.IntegrityCheck()); + Identity(); + return; + } + int i; + const float *f1; + const float *f2; + float *fd; + for (f1=m1.m[0],f2=m2.m[0],fd=m[0],i=0;i<16;i++,f1++,f2++,fd++) + *fd=s1*((*f1)-(*f2))+(*f2); + flags=0; +} + + +bool Matrix4::operator== (const Matrix4& t) const +{ + if ((flags&MATFLAG_IDENTITY)&&(t.flags&MATFLAG_IDENTITY)) + { + assert(IntegrityCheck()); + assert(t.IntegrityCheck()); + return true; + } + int i,j; + for (i=0;i<4;i++) + { + for (j=0;j<3;j++) + { + if (fabs(m[i][j]-t.m[i][j])>1E-4f) + return false; + } + } + return true; +} + +void Matrix4::Concat(const Matrix4 &m1,const Matrix4 &m2) +{ + if (m1.flags&MATFLAG_IDENTITY) + { + assert(m1.IntegrityCheck()); + *this=m2; + } + else if (m2.flags&MATFLAG_IDENTITY) + { + assert(m2.IntegrityCheck()); + *this=m1; + } + else + { + + assert(this!=&m1&&this!=&m2); + /* + int i,j,k; + float acc=0; + for (i=0;i<4;i++) + { + for (j=0;j<4;j++) + { + for(k=0,acc=0.;k<4;k++) + acc+=m1.m[i][k]*m2.m[k][j]; + m[i][j]=acc; + } + } + */ + m2.HXFormPointND(m[0],m1.m[0]); + m2.HXFormPointND(m[1],m1.m[1]); + m2.HXFormPointND(m[2],m1.m[2]); + m2.HXFormPointND(m[3],m1.m[3]); + flags=0; + } +} + +void Matrix4::XFormPoint(Vect3 &dest,const Vect3 &src) const +{ + if (flags&MATFLAG_IDENTITY) + { + assert(IntegrityCheck()); + dest=src; + return; + } +/* + dest[0]=m[0][0]*src[0]+m[1][0]*src[1]+m[2][0]*src[2]+m[3][0]; + dest[1]=m[0][0]*src[0]+m[1][0]*src[1]+m[2][0]*src[2]+m[3][1]; + dest[2]=m[3][2]; +*/ +/* + int i,j; + dest[0]=m[3][0]; + dest[1]=m[3][1]; + dest[2]=m[3][2]; + for (i=0;i<3;i++) + for (j=0;j<3;j++) + dest[i]+=m[j][i]*src[j]; +*/ + const float *s=&src.x(); + float *d=&dest.x(); + const float *mat=&m[0][0]; + __asm + { + mov ebx,[s] + mov eax,[mat] + mov ecx,[d] + fld [DWORD PTR ebx+8] // z + fld [DWORD PTR ebx+4] // y z + fld [DWORD PTR ebx] // x y z + + fld [DWORD PTR eax] // m00 x y z 1/m33+m23z+m13y+xm03 + fmul st,st(1) // m00x x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+4] // m01 m00x x y z 1/m33+m23z+m13y+xm03 + fmul st,st(2) // m01x m00x x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+8] // m02 m01x m00x x y z 1/m33+m23z+m13y+xm03 + fmulp st(3),st // m01x m00x m02x y z 1/m33+m23z+m13y+xm03 + fxch st(1) // m00x m01x m02x y z 1/m33+m23z+m13y+xm03 + fxch st(2) // m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+16] // m10 m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fmul st,st(4) // m10y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+20] // m11 m10y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + + + fmul st,st(5) // m11y m10y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fxch st(1) // m10y m11y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + faddp st(4),st // m11y m02x m01x m00x+m10y y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+24] // m12 m11y m02x m01x m00x+m10y y z 1/m33+m23z+m13y+xm03 + fmulp st(5),st // m11y m02x m01x m00x+m10y m12y z 1/m33+m23z+m13y+xm03 + + faddp st(2),st // m02x m01x+m11y m00x+m10y m12y z 1/m33+m23z+m13y+xm03 + faddp st(3),st // m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + + fld [DWORD PTR eax+32] // m20 m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + fmul st,st(4) // m20z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+36] // m21 m20z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + + fmul st,st(5) // m21z m20z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + fxch st(1) // m20z m21z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + faddp st(3),st // m21z m01x+m11y m00x+m10y+m20z m12y+m02x z 1/m33+m23z+m13y+xm03 + + fld [DWORD PTR eax+40] // m22 m21z m01x+m11y m00x+m10y+m20z m12y+m02x z 1/m33+m23z+m13y+xm03 + fmulp st(5),st // m21z m01x+m11y m00x+m10y+m20z m12y+m02x m22z 1/m33+m23z+m13y+xm03 + faddp st(1),st // m21z+m01x+m11y m00x+m10y+m20z m12y+m02x m22z 1/m33+m23z+m13y+xm03 + fxch st(3) // m22z m00x+m10y+m20z m12y+m02x m21z+m01x+m11y 1/m33+m23z+m13y+xm03 + faddp st(2),st // m00x+m10y+m20z m22z+m12y+m02x m21z+m01x+m11y 1/m33+m23z+m13y+xm03 + + + fadd [DWORD PTR eax+48] // m00x+m10y+m20z+m30 m22z+m12y+m02x m21z+m01x+m11y 1/m33+m23z+m13y+xm03 + fxch st(2) // m21z+m01x+m11y m22z+m12y+m02x m00x+m10y+m20z+m30 1/m33+m23z+m13y+xm03 + fadd [DWORD PTR eax+52] // m21z+m01x+m11y+m31 m22z+m12y+m02x m00x+m10y+m20z+m30 1/m33+m23z+m13y+xm03 + fxch st(1) // m22z+m12y+m02x m21z+m01x+m11y+m31 m00x+m10y+m20z+m30 1/m33+m23z+m13y+xm03 + fadd [DWORD PTR eax+56] // m22z+m12y+m02x+m32 m21z+m01x+m11y+m31 m00x+m10y+m20z+m30 1/m33+m23z+m13y+xm03 + + fxch st(2) // m00x+m10y+m20z+m30 m21z+m01x+m11y+m31 m22z+m12y+m02x+m32 1/m33+m23z+m13y+xm03 + fstp [DWORD PTR ecx] + fstp [DWORD PTR ecx+4] + fstp [DWORD PTR ecx+8] + } +} + +void Matrix4::XFormVect(Vect3 &dest,const Vect3 &src) const +{ + if (flags&MATFLAG_IDENTITY) + { + dest=src; + return; + } + int i,j; + dest=0.; + for (i=0;i<3;i++) + for (j=0;j<3;j++) + dest[i]+=m[j][i]*src[j]; +} + +void Matrix4::XFormVectTranspose(Vect3 &dest,const Vect3 &src) const +{ + if (flags&MATFLAG_IDENTITY) + { + assert(IntegrityCheck()); + dest=src; + return; + } +/* + int i,j; + dest=0.; + for (i=0;i<3;i++) + for (j=0;j<3;j++) + dest[i]+=m[i][j]*src[j]; +*/ + const float *s=&src.x(); + float *d=&dest.x(); + const float *mat=&m[0][0]; + __asm + { + mov ebx,[s] + mov eax,[mat] + mov ecx,[d] + fld [DWORD PTR ebx+8] // z + fld [DWORD PTR ebx+4] // y z + fld [DWORD PTR ebx] // x y z + + fld [DWORD PTR eax] // m00 x y z 1/m33+m23z+m13y+xm03 + fmul st,st(1) // m00x x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+16] // m01 m00x x y z 1/m33+m23z+m13y+xm03 + fmul st,st(2) // m01x m00x x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+32] // m02 m01x m00x x y z 1/m33+m23z+m13y+xm03 + fmulp st(3),st // m01x m00x m02x y z 1/m33+m23z+m13y+xm03 + fxch st(1) // m00x m01x m02x y z 1/m33+m23z+m13y+xm03 + fxch st(2) // m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+4] // m10 m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fmul st,st(4) // m10y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+20] // m11 m10y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + + + fmul st,st(5) // m11y m10y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fxch st(1) // m10y m11y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + faddp st(4),st // m11y m02x m01x m00x+m10y y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+36] // m12 m11y m02x m01x m00x+m10y y z 1/m33+m23z+m13y+xm03 + fmulp st(5),st // m11y m02x m01x m00x+m10y m12y z 1/m33+m23z+m13y+xm03 + + faddp st(2),st // m02x m01x+m11y m00x+m10y m12y z 1/m33+m23z+m13y+xm03 + faddp st(3),st // m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + + fld [DWORD PTR eax+8] // m20 m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + fmul st,st(4) // m20z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+24] // m21 m20z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + + fmul st,st(5) // m21z m20z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + fxch st(1) // m20z m21z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + faddp st(3),st // m21z m01x+m11y m00x+m10y+m20z m12y+m02x z 1/m33+m23z+m13y+xm03 + + fld [DWORD PTR eax+40] // m22 m21z m01x+m11y m00x+m10y+m20z m12y+m02x z 1/m33+m23z+m13y+xm03 + fmulp st(5),st // m21z m01x+m11y m00x+m10y+m20z m12y+m02x m22z 1/m33+m23z+m13y+xm03 + faddp st(1),st // m21z+m01x+m11y m00x+m10y+m20z m12y+m02x m22z 1/m33+m23z+m13y+xm03 + fxch st(3) // m22z m00x+m10y+m20z m12y+m02x m21z+m01x+m11y 1/m33+m23z+m13y+xm03 + faddp st(2),st // m00x+m10y+m20z m22z+m12y+m02x m21z+m01x+m11y 1/m33+m23z+m13y+xm03 + fstp [DWORD PTR ecx] + fstp [DWORD PTR ecx+8] + fstp [DWORD PTR ecx+4] + } +} + +#pragma warning( disable : 4725) //instruction may be inaccurate on some Pentiums + +float Matrix4::HXFormPoint(Vect3 &dest,const Vect3 &src) const +{ +/* + int i,j; + float h; + h=m[3][3]; + h+=m[0][3]*src[0]; + h+=m[1][3]*src[1]; + h+=m[2][3]*src[2]; + h=1.0f/h; + for (i=0;i<3;i++) + { + dest[i]=m[3][i]; + for (j=0;j<3;j++) + dest[i]+=m[j][i]*src[j]; + dest[i]*=h; + } +// return h; +*/ + static const float one=1.0f; + const float *s=&src.x(); + float *d=&dest.x(); + const float *mat=&m[0][0]; + float tret; + __asm + { + mov ebx,[s] + mov eax,[mat] + mov ecx,[d] + fld [DWORD PTR ebx] // x + fld [DWORD PTR eax+12] // m03 x + fmul st,st(1) // xm03 x + fld [DWORD PTR ebx+4] // y xm03 x + fld [DWORD PTR eax+28] // m13 y xm03 x + fmul st,st(1) // m13y y xm03 x + + fld [DWORD PTR ebx+8] // z m13y y xm03 x + fld [DWORD PTR eax+44] // m23 z m13y y xm03 x + fmul st,st(1) // m23z z m13y y xm03 x + fxch st(2) // m13y z m23z y xm03 x + faddp st(4),st // z m23z y m13y+xm03 x + fxch st(1) // m23z z y m13y+xm03 x + faddp st(3),st // z y m23z+m13y+xm03 x + fxch st(2) // m23z+m13y+xm03 y z x + + fadd [DWORD PTR eax+60] // m33+m23z+m13y+xm03 y z x + fdivr [one] // 1/m33+m23z+m13y+xm03 y z x + fxch st(3) // x y z 1/m33+m23z+m13y+xm03 + mov edx,[DWORD PTR eax] + mov edx,[DWORD PTR ecx] + mov edx,[DWORD PTR ecx+8] + + + + fld [DWORD PTR eax] // m00 x y z 1/m33+m23z+m13y+xm03 + fmul st,st(1) // m00x x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+4] // m01 m00x x y z 1/m33+m23z+m13y+xm03 + fmul st,st(2) // m01x m00x x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+8] // m02 m01x m00x x y z 1/m33+m23z+m13y+xm03 + fmulp st(3),st // m01x m00x m02x y z 1/m33+m23z+m13y+xm03 + fxch st(1) // m00x m01x m02x y z 1/m33+m23z+m13y+xm03 + fxch st(2) // m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+16] // m10 m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fmul st,st(4) // m10y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+20] // m11 m10y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + + + fmul st,st(5) // m11y m10y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fxch st(1) // m10y m11y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + faddp st(4),st // m11y m02x m01x m00x+m10y y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+24] // m12 m11y m02x m01x m00x+m10y y z 1/m33+m23z+m13y+xm03 + fmulp st(5),st // m11y m02x m01x m00x+m10y m12y z 1/m33+m23z+m13y+xm03 + + faddp st(2),st // m02x m01x+m11y m00x+m10y m12y z 1/m33+m23z+m13y+xm03 + faddp st(3),st // m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + + fld [DWORD PTR eax+32] // m20 m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + fmul st,st(4) // m20z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+36] // m21 m20z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + + fmul st,st(5) // m21z m20z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + fxch st(1) // m20z m21z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + faddp st(3),st // m21z m01x+m11y m00x+m10y+m20z m12y+m02x z 1/m33+m23z+m13y+xm03 + + fld [DWORD PTR eax+40] // m22 m21z m01x+m11y m00x+m10y+m20z m12y+m02x z 1/m33+m23z+m13y+xm03 + fmulp st(5),st // m21z m01x+m11y m00x+m10y+m20z m12y+m02x m22z 1/m33+m23z+m13y+xm03 + faddp st(1),st // m21z+m01x+m11y m00x+m10y+m20z m12y+m02x m22z 1/m33+m23z+m13y+xm03 + fxch st(3) // m22z m00x+m10y+m20z m12y+m02x m21z+m01x+m11y 1/m33+m23z+m13y+xm03 + faddp st(2),st // m00x+m10y+m20z m22z+m12y+m02x m21z+m01x+m11y 1/m33+m23z+m13y+xm03 + + + fadd [DWORD PTR eax+48] // m00x+m10y+m20z+m30 m22z+m12y+m02x m21z+m01x+m11y 1/m33+m23z+m13y+xm03 + fxch st(2) // m21z+m01x+m11y m22z+m12y+m02x m00x+m10y+m20z+m30 1/m33+m23z+m13y+xm03 + fadd [DWORD PTR eax+52] // m21z+m01x+m11y+m31 m22z+m12y+m02x m00x+m10y+m20z+m30 1/m33+m23z+m13y+xm03 + fxch st(1) // m22z+m12y+m02x m21z+m01x+m11y+m31 m00x+m10y+m20z+m30 1/m33+m23z+m13y+xm03 + fadd [DWORD PTR eax+56] // m22z+m12y+m02x+m32 m21z+m01x+m11y+m31 m00x+m10y+m20z+m30 1/m33+m23z+m13y+xm03 + + fxch st(2) // m00x+m10y+m20z+m30 m21z+m01x+m11y+m31 m22z+m12y+m02x+m32 1/m33+m23z+m13y+xm03 + fmul st,st(3) // sx m21z+m01x+m11y+m31 m22z+m12y+m02x+m32 1/m33+m23z+m13y+xm03 + fxch st(1) // m21z+m01x+m11y+m31 sx m22z+m12y+m02x+m32 1/m33+m23z+m13y+xm03 + fmul st,st(3) // sy sx m22z+m12y+m02x+m32 1/m33+m23z+m13y+xm03 + fxch st(2) // m22z+m12y+m02x+m32 sx sy 1/m33+m23z+m13y+xm03 + fmul st,st(3) // sz sx sy rhw + fstp [DWORD PTR ecx+8] + fstp [DWORD PTR ecx] + fstp [DWORD PTR ecx+4] + fstp [tret] + } + return tret; +} +#pragma warning( default: 4725) //instruction may be inaccurate on some Pentiums + +void Matrix4::HXFormPointND(float *d,const float *s) const +{ +/* + int i,j; + float h; + h=m[3][3]; + h+=m[0][3]*src[0]; + h+=m[1][3]*src[1]; + h+=m[2][3]*src[2]; + h=1.0f/h; + for (i=0;i<3;i++) + { + dest[i]=m[3][i]; + for (j=0;j<3;j++) + dest[i]+=m[j][i]*src[j]; + dest[i]*=h; + } +// return h; +*/ + const float *mat=&m[0][0]; + __asm + { + mov ebx,[s] + mov eax,[mat] + mov ecx,[d] + fld [DWORD PTR ebx] // x + fld [DWORD PTR eax+12] // m03 x + fmul st,st(1) // xm03 x + fld [DWORD PTR ebx+4] // y xm03 x + fld [DWORD PTR eax+28] // m13 y xm03 x + fmul st,st(1) // m13y y xm03 x + + fld [DWORD PTR ebx+8] // z m13y y xm03 x + fld [DWORD PTR eax+44] // m23 z m13y y xm03 x + fmul st,st(1) // m23z z m13y y xm03 x + fxch st(2) // m13y z m23z y xm03 x + faddp st(4),st // z m23z y m13y+xm03 x + fxch st(1) // m23z z y m13y+xm03 x + faddp st(3),st // z y m23z+m13y+xm03 x + fxch st(2) // m23z+m13y+xm03 y z x + fld [DWORD PTR ebx+12] // m33+m23z+m13y+xm03 y z x + fmul [DWORD PTR eax+60] // m33+m23z+m13y+xm03 y z x + faddp st(1),st + fxch st(3) // x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax] // m00 x y z 1/m33+m23z+m13y+xm03 + fmul st,st(1) // m00x x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+4] // m01 m00x x y z 1/m33+m23z+m13y+xm03 + fmul st,st(2) // m01x m00x x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+8] // m02 m01x m00x x y z 1/m33+m23z+m13y+xm03 + fmulp st(3),st // m01x m00x m02x y z 1/m33+m23z+m13y+xm03 + fxch st(1) // m00x m01x m02x y z 1/m33+m23z+m13y+xm03 + fxch st(2) // m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+16] // m10 m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fmul st,st(4) // m10y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+20] // m11 m10y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + + + fmul st,st(5) // m11y m10y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + fxch st(1) // m10y m11y m02x m01x m00x y z 1/m33+m23z+m13y+xm03 + faddp st(4),st // m11y m02x m01x m00x+m10y y z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+24] // m12 m11y m02x m01x m00x+m10y y z 1/m33+m23z+m13y+xm03 + fmulp st(5),st // m11y m02x m01x m00x+m10y m12y z 1/m33+m23z+m13y+xm03 + + faddp st(2),st // m02x m01x+m11y m00x+m10y m12y z 1/m33+m23z+m13y+xm03 + faddp st(3),st // m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + + fld [DWORD PTR eax+32] // m20 m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + fmul st,st(4) // m20z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+36] // m21 m20z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + + fmul st,st(5) // m21z m20z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + fxch st(1) // m20z m21z m01x+m11y m00x+m10y m12y+m02x z 1/m33+m23z+m13y+xm03 + faddp st(3),st // m21z m01x+m11y m00x+m10y+m20z m12y+m02x z 1/m33+m23z+m13y+xm03 + + fld [DWORD PTR eax+40] // m22 m21z m01x+m11y m00x+m10y+m20z m12y+m02x z 1/m33+m23z+m13y+xm03 + fmulp st(5),st // m21z m01x+m11y m00x+m10y+m20z m12y+m02x m22z 1/m33+m23z+m13y+xm03 + faddp st(1),st // m21z+m01x+m11y m00x+m10y+m20z m12y+m02x m22z 1/m33+m23z+m13y+xm03 + fxch st(3) // m22z m00x+m10y+m20z m12y+m02x m21z+m01x+m11y 1/m33+m23z+m13y+xm03 + faddp st(2),st // m00x+m10y+m20z m22z+m12y+m02x m21z+m01x+m11y 1/m33+m23z+m13y+xm03 + + +// fadd [DWORD PTR eax+48] // m00x+m10y+m20z+m30 m22z+m12y+m02x m21z+m01x+m11y 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+48] + fmul [DWORD PTR ebx+12] + faddp st(1),st + fxch st(2) // m21z+m01x+m11y m22z+m12y+m02x m00x+m10y+m20z+m30 1/m33+m23z+m13y+xm03 + + fld [DWORD PTR eax+52] + fmul [DWORD PTR ebx+12] + faddp st(1),st + fxch st(1) // m22z+m12y+m02x m21z+m01x+m11y+m31 m00x+m10y+m20z+m30 1/m33+m23z+m13y+xm03 + fld [DWORD PTR eax+56] + fmul [DWORD PTR ebx+12] + faddp st(1),st + + fstp [DWORD PTR ecx+8] + fstp [DWORD PTR ecx+4] + fstp [DWORD PTR ecx] + fstp [DWORD PTR ecx+12] + } +} + + + +void Matrix4::From3x4(const float mat[3][4]) +{ + for (int i=0; i<4; i++) + { + for (int j=0; j<3; j++) + { + m[i][j]=mat[j][i]; + } + } + + m[0][3] = 0.0f; + m[1][3] = 0.0f; + m[2][3] = 0.0f; + m[3][3] = 1.0f; + + CalcFlags(); +} + +void Matrix4::To3x4(float mat[3][4]) const +{ + for (int i=0; i<4; i++) + { + for (int j=0; j<3; j++) + { + mat[j][i] = m[i][j]; + } + } +} + +void Matrix4::SetRow(int i,const Vect3 &t) +{ + m[i][0]=t[0]; + m[i][1]=t[1]; + m[i][2]=t[2]; + flags=0; +} + +void Matrix4::SetColumn(int i,const Vect3 &t) +{ + m[0][i]=t[0]; + m[1][i]=t[1]; + m[2][i]=t[2]; + flags=0; +} + +void Matrix4::SetRow(int i) +{ + m[i][0]=0; + m[i][1]=0; + m[i][2]=0; + flags=0; +} + +void Matrix4::SetColumn(int i) +{ + m[0][i]=0; + m[1][i]=0; + m[2][i]=0; + flags=0; +} + +void Matrix4::SetFromDouble(double *d) +{ + int i; + float *f; + for (f=m[0],i=0;i<16;i++,f++,d++) + *f=(float)*d; + flags=0; +} + + +void Matrix4::MultiplyColumn(int i,float f) +{ + m[0][i]*=f; + m[1][i]*=f; + m[2][i]*=f; + flags=0; +} + +float Matrix4::GetColumnLen(int i) +{ + float d; + d=m[0][i]*m[0][i]+m[1][i]*m[1][i]+m[2][i]*m[2][i]; + assert(d>EPS_MATRIX); + return sqrt(d); +} + +void Matrix4::Transpose() +{ + int i,j; + float swap; + for (i=0;i<4;i++) + { + for (j=i+1;j<4;j++) + { + swap=m[i][j]; + m[i][j]=m[j][i]; + m[j][i]=swap; + } + } +} + diff --git a/utils/q3data/matrix4.h b/utils/q3data/matrix4.h new file mode 100644 index 0000000..121ed2e --- /dev/null +++ b/utils/q3data/matrix4.h @@ -0,0 +1,79 @@ +#if !defined(MATRIX4_INC) +#define MATRIX4_INC + +#include "vect3.h" +#include +#include + +#pragma warning( disable : 4244) +extern const float Pi; +extern const float Half_Pi; + + +#define MATFLAG_IDENTITY (1) +class Matrix4 +{ + float m[4][4]; + int flags; +public: + Matrix4() {flags=0;} + Matrix4(const Matrix4 &o); + float* operator[](int i) {return m[i];flags=0;} + const float* operator[](int i) const {return m[i];} + void SetElem(int r,int c,float v) {m[r][c]=v;flags=0;} + const float &Elem(int r,int c) const {return m[r][c];} + void SetFromMem(const float *mem) {memcpy(m,mem,sizeof(float)*16);CalcFlags();} + void GetFromMem(float *mem) const {memcpy(mem,m,sizeof(float)*16);} + void Identity(); + void Zero(); + int CalcFlags(); + int GetFlags() const {return flags;} + bool IntegrityCheck() const; + void Translate(const float tx,const float ty,const float tz); + void Translate(const Vect3 &t); + void Rotate(int axis,const float theta); + void Rotate(const float rx,const float ry,const float rz); + void Rotate(const Vect3 v); + void Scale(const float sx,const float sy,const float sz); + void Scale(const float sx); + void Concat(const Matrix4 &m1,const Matrix4 &m2); + void Interp(const Matrix4 &m1,float s1,const Matrix4 &m2,float s2); + void Interp(const Matrix4 &m1,const Matrix4 &m2,float s1); + void XFormPoint(Vect3 &dest,const Vect3 &src) const; + float HXFormPoint(Vect3 &dest,const Vect3 &src) const; + void HXFormPointND(float *dest,const float *src) const; + void XFormVect(Vect3 &dest,const Vect3 &src) const; + void XFormVectTranspose(Vect3 &dest,const Vect3 &src) const; + void SetRow(int i,const Vect3 &t); + void SetColumn(int i,const Vect3 &t); + void SetRow(int i); + void SetColumn(int i); + void SetFromDouble(double *d); + void MultiplyColumn(int i,float f); + float GetColumnLen(int i); + void Inverse(const Matrix4 &old); + void OrthoNormalInverse(const Matrix4 &old); + void FindFromPoints(const Vect3 base1[4],const Vect3 base2[4]); + void MakeEquiscalar(); + float Det(); + float MaxAbsElement(); + void GetRow(int r,Vect3 &v) const {v.x()=m[r][0];v.y()=m[r][1];v.z()=m[r][2];} + void GetColumn(int r,Vect3 &v) const {v.x()=m[0][r];v.y()=m[1][r];v.z()=m[2][r];} + void Transpose(); + bool operator== (const Matrix4& t) const; + void From3x4(const float mat[3][4]); + void To3x4(float mat[3][4]) const; +}; + + +int GetTextureSystem( + const Vect3 &p0,const Vect3 &uv0, //vertex 0 + const Vect3 &p1,const Vect3 &uv1, //vertex 1 + const Vect3 &p2,const Vect3 &uv2, //vertex 2 + const Vect3 &n, // normal vector for triangle + Vect3 &M, // returned gradient wrt u + Vect3 &N, // returned gradient wrt v + Vect3 &P); // returned location in world space where u=v=0 + + +#endif \ No newline at end of file diff --git a/utils/q3data/md3lib.c b/utils/q3data/md3lib.c new file mode 100644 index 0000000..484e0d5 --- /dev/null +++ b/utils/q3data/md3lib.c @@ -0,0 +1,190 @@ +#include +#include +#include "md3lib.h" + +/* +** MD3_ComputeTagFromTri +*/ +void MD3_ComputeTagFromTri( md3Tag_t *pTag, const float pTri[3][3] ) +{ + float len[3]; + vec3_t axes[3], sides[3]; + int longestSide, shortestSide, hypotSide; + int origin; + int j; + float d; + + memset( axes, 0, sizeof( axes ) ); + memset( sides, 0, sizeof( sides ) ); + + // + // compute sides + // + for ( j = 0; j < 3; j++ ) + { + sides[j][0] = pTri[(j+1)%3][0] - pTri[j][0]; + sides[j][1] = pTri[(j+1)%3][1] - pTri[j][1]; + sides[j][2] = pTri[(j+1)%3][2] - pTri[j][2]; + + len[j] = ( float ) sqrt( DotProduct( sides[j], sides[j] ) ); + } + +#if 0 + if ( len[0] > len[1] && len[0] > len[2] ) + { + longestSide = 0; shortestSide = 1; origin = 2; + } + else if ( len[1] > len[0] && len[1] > len[2] ) + { + longestSide = 1; shortestSide = 2; origin = 0; + } + else if ( len[2] > len[0] && len[2] > len[1] ) + { + longestSide = 2; shortestSide = 0; origin = 1; + } + else + { + Error( "invalid tag triangle, must be a right triangle with unequal length sides" ); + } +#endif + if ( len[0] > len[1] && len[0] > len[2] ) { + hypotSide = 0; + origin = 2; + } else if ( len[1] > len[0] && len[1] > len[2] ) { + hypotSide = 1; + origin = 0; + } else if ( len[2] > len[0] && len[2] > len[1] ) { + hypotSide = 2; + origin = 1; + } + else{ + Error( "invalid tag %s, must be a right triangle with unequal length sides", pTag->name); + } + len[hypotSide] = -1; + + if ( len[0] > len[1] && len[0] > len[2] ) { + longestSide = 0; + } else if ( len[1] > len[0] && len[1] > len[2] ) { + longestSide = 1; + } else if ( len[2] > len[0] && len[2] > len[1] ) { + longestSide = 2; + } + len[longestSide] = -1; + + if ( len[0] > len[1] && len[0] > len[2] ) { + shortestSide = 0; + } else if ( len[1] > len[0] && len[1] > len[2] ) { + shortestSide = 1; + } else if ( len[2] > len[0] && len[2] > len[1] ) { + shortestSide = 2; + } + len[shortestSide] = -1; + + + +// VectorNormalize( sides[shortestSide], axes[0] ); +// VectorNormalize( sides[longestSide], axes[1] ); + VectorNormalize( sides[longestSide], axes[0] ); + VectorNormalize( sides[shortestSide], axes[1] ); + + // project shortest side so that it is exactly 90 degrees to the longer side + d = DotProduct( axes[0], axes[1] ); + VectorMA( axes[0], -d, axes[1], axes[0] ); + VectorNormalize( axes[0], axes[0] ); + + CrossProduct( sides[longestSide], sides[shortestSide], axes[2] ); + VectorNormalize( axes[2], axes[2] ); + + pTag->origin[0] = pTri[origin][0]; + pTag->origin[1] = pTri[origin][1]; + pTag->origin[2] = pTri[origin][2]; + + VectorCopy( axes[0], pTag->axis[0] ); + VectorCopy( axes[1], pTag->axis[1] ); + VectorCopy( axes[2], pTag->axis[2] ); +} + +/* +============== +MD3_Dump +============== +*/ +void MD3_Dump( const char *filename ) +{ + md3Header_t header; + md3Tag_t *pTag; + md3Surface_t *pSurface; + FILE *fp; + void *_buffer; + void *buffer; + long fileSize; + int i; + + if ( ( fp = fopen( filename, "rb" ) ) == 0 ) + { + Error( "Unable to open '%s'\n", filename ); + } + + fileSize = filelength( fileno( fp ) ); + _buffer = malloc( filelength( fileno( fp ) ) ); + fread( _buffer, fileSize, 1, fp ); + fclose( fp ); + + buffer = ( char * ) _buffer; + header = *( md3Header_t * ) _buffer; + + if ( header.ident != MD3_IDENT ) + { + Error( "Incorrect ident for '%s'\n", filename ); + } + + printf( "Contents of '%s'\n", filename ); + printf( " version: %d\n", header.version ); + printf( " name: %s\n", header.name ); + printf( " num frames: %d\n", header.numFrames ); + printf( " num tags: %d\n", header.numTags ); + printf( " num surfaces: %d\n", header.numSurfaces ); + printf( " num skins: %d\n", header.numSkins ); + printf( " file size: %d\n", fileSize ); + + printf( "--- TAGS ---\n" ); + pTag = ( md3Tag_t * ) ( ( ( char * ) buffer ) + header.ofsTags ); + for ( i = 0; i < header.numTags; i++, pTag++ ) + { + printf( " tag %d ('%s')\n", i, pTag->name ); + printf( " origin: %f,%f,%f\n", pTag->origin[0], pTag->origin[1], pTag->origin[2] ); + printf( " vf: %f,%f,%f\n", pTag->axis[0][0], pTag->axis[0][1], pTag->axis[0][2] ); + printf( " vr: %f,%f,%f\n", pTag->axis[1][0], pTag->axis[1][1], pTag->axis[1][2] ); + printf( " vu: %f,%f,%f\n", pTag->axis[2][0], pTag->axis[2][1], pTag->axis[2][2] ); + } + + printf( "--- SURFACES ---\n" ); + pSurface = ( md3Surface_t * ) ( ( ( char * ) buffer ) + header.ofsSurfaces ); + + for ( i = 0; i < header.numSurfaces; i++ ) + { + int j; + + md3Shader_t *pShader = ( md3Shader_t * ) ( ( ( char * ) pSurface ) + pSurface->ofsShaders ); + + printf( "\n surface %d ('%s')\n", i, pSurface->name ); + printf( " num frames: %d\n", pSurface->numFrames ); + printf( " num shaders: %d\n", pSurface->numShaders ); + printf( " num tris: %d\n", pSurface->numTriangles ); + printf( " num verts: %d\n", pSurface->numVerts ); + + if ( pSurface->numShaders > 0 ) + { + printf( " --- SHADERS ---\n" ); + + for ( j = 0; j < pSurface->numShaders; j++, pShader++ ) + { + printf( " shader %d ('%s')\n", j, pShader->name ); + } + } + pSurface = ( md3Surface_t * ) ( ( ( char * ) pSurface ) + pSurface->ofsEnd ); + } + + free( _buffer ); +} + diff --git a/utils/q3data/md3lib.h b/utils/q3data/md3lib.h new file mode 100644 index 0000000..2abfae6 --- /dev/null +++ b/utils/q3data/md3lib.h @@ -0,0 +1,7 @@ +#include +#include "../common/cmdlib.h" +#include "../common/mathlib.h" +#include "../common/qfiles.h" + +void MD3_Dump( const char *filename ); +void MD3_ComputeTagFromTri( md3Tag_t *pTag, const float tri[3][3] ); \ No newline at end of file diff --git a/utils/q3data/mdx_format.h b/utils/q3data/mdx_format.h new file mode 100644 index 0000000..e8239d0 --- /dev/null +++ b/utils/q3data/mdx_format.h @@ -0,0 +1,338 @@ +// Filename:- mdx_format.h +// +// DO NOT UPDATE THIS FILE IN ANY WAY WHATSOEVER WITHOUT TELLING ME (-Ste), +// BECAUSE THE MASTER COPY IS IN A DIFFERENT SOURCESAFE DATABASE AND WILL +// JUST GET PASTED OVER THIS ONE WHENEVER I CHANGE IT. +// +// +// +// MDX file format (typically uses file extension GLX for mesh, and GLA for anim/skeleton file) +// +// Notes: +// +// - All offset fields are relative to the address of the structure they occur in +// - So far, the only external symbol needed is MAX_QPATH, plus the typedefs for vec3_t, vec2_t etc + +#ifndef MDX_FORMAT_H +#define MDX_FORMAT_H + + +#define MDXM_IDENT (('M'<<24)+('G'<<16)+('L'<<8)+'2') +#define MDXA_IDENT (('A'<<24)+('G'<<16)+('L'<<8)+'2') + +#define MAX_TAGNAME_BYTES 32 // matches MDR, can be changed if nec. + +// +// normal version numbers... +// +#define MDXM_VERSION 4 +#define MDXA_VERSION 4 +#define MDXA_VERSION_QUAT 5 + +// (Note that since there is now a "_info.txt" file written out by carcass any changes made in here that +// introduce new data should also be reflected in the info-output) + +// 32 bit-flags for ghoul2 bone properties... (all undefined fields will be blank) +// +#define G2BONEFLAG_ALWAYSXFORM 0x00000001 + +// same thing but for surfaces... (Carcass will only generate 1st 2 flags, others are ingame +// +#define G2SURFACEFLAG_ISBOLT 0x00000001 +#define G2SURFACEFLAG_OFF 0x00000002 // saves strcmp()ing for "_off" in surface names +#define G2SURFACEFLAG_SPARE0 0x00000004 // future-expansion fields, saves invalidating models if we add more +#define G2SURFACEFLAG_SPARE1 0x00000008 // +#define G2SURFACEFLAG_SPARE2 0x00000010 // +#define G2SURFACEFLAG_SPARE3 0x00000020 // +#define G2SURFACEFLAG_SPARE4 0x00000040 // +#define G2SURFACEFLAG_SPARE5 0x00000080 // +// +#define G2SURFACEFLAG_NODESCENDANTS 0x00000100 // ingame-stuff, never generated by Carcass.... +#define G2SURFACEFLAG_GENERATED 0x00000200 // + + + +// triangle side-ordering stuff for tags... +// +#define iG2_TRISIDE_MIDDLE 1 +#define iG2_TRISIDE_LONGEST 0 +#define iG2_TRISIDE_SHORTEST 2 + + +#define MAX_G2_BONEREFS_PER_SURFACE 28 // currently only enforced when compiling Q3/G2 models, not xmen view-only tests +#define sDEFAULT_GLA_NAME "*default" // used when making special simple ghoul2 models, usually from MD3 files + + +//////////////////////////////////// +// +// these structs are defined here purely because of structure dependancy order... +// +typedef struct { + int boneIndex; // these are indexes into the surface boneReferences, not the global bone index + float boneWeight; // not the global per-frame bone list +} mdxmWeight_t; + + +#ifdef __cplusplus +struct mdxaCompBone_t +#else +typedef struct +#endif +{ + unsigned char Comp[24]; // MC_COMP_BYTES is in MatComp.h, but don't want to couple + + // I'm defining this '<' operator so this struct can be used as an STL key... + // + #ifdef __cplusplus + bool operator < (const mdxaCompBone_t& _X) const {return (memcmp(Comp,_X.Comp,sizeof(Comp))<0);} + #endif +} +#ifndef __cplusplus +mdxaCompBone_t +#endif +; + +#ifdef __cplusplus +struct mdxaCompQuatBone_t +#else +typedef struct +#endif +{ + unsigned char Comp[14]; + + // I'm defining this '<' operator so this struct can be used as an STL key... + // + #ifdef __cplusplus + bool operator < (const mdxaCompQuatBone_t& _X) const {return (memcmp(Comp,_X.Comp,sizeof(Comp))<0);} + #endif +} +#ifndef __cplusplus +mdxaCompQuatBone_t +#endif +; + + +#ifndef MDXABONEDEF +typedef struct { + float matrix[3][4]; +} mdxaBone_t; +#endif + +//////////////////////////////////// + + + + + + +// mdxHeader_t - this contains the header for the file, with sanity checking and version checking, plus number of lod's to be expected +// +typedef struct { + // + // ( first 3 fields are same format as MD3/MDR so we can apply easy model-format-type checks ) + // + int ident; // "IDP3" = MD3, "RDM5" = MDR, "2LGM"(GL2 Mesh) = MDX (cruddy char order I know, but I'm following what was there in other versions) + int version; // 1,2,3 etc as per format revision + char name[MAX_QPATH]; // model name (eg "models/players/marine.glm") // note: extension supplied + char animName[MAX_QPATH];// name of animation file this mesh requires // note: extension missing + int animIndex; // filled in by game (carcass defaults it to 0) + + int numBones; // (for ingame version-checks only, ensure we don't ref more bones than skel file has) + + int numLODs; + int ofsLODs; + + int numSurfaces; // now that surfaces are drawn hierarchically, we have same # per LOD + int ofsSurfHierarchy; + + int ofsEnd; // EOF, which of course gives overall file size +} mdxmHeader_t; + + +// for each surface (doesn't actually need a struct for this, just makes source clearer) +// { + typedef struct + { + int offsets[1]; // variable sized (mdxmHeader_t->numSurfaces), each offset points to a mdxmSurfHierarchy_t below + } mdxmHierarchyOffsets_t; +// } + +// for each surface... (mdxmHeader_t->numSurfaces) +// { + // mdxmSurfHierarchy_t - contains hierarchical info for surfaces... + + typedef struct { + char name[MAX_QPATH]; + unsigned int flags; + char shader[MAX_QPATH]; + int shaderIndex; // for in-game use (carcass defaults to 0) + int parentIndex; // this points to the index in the file of the parent surface. -1 if null/root + int numChildren; // number of surfaces which are children of this one + int childIndexes[1]; // [mdxmSurfHierarch_t->numChildren] (variable sized) + } mdxmSurfHierarchy_t; // struct size = (int)( &((mdxmSurfHierarch_t *)0)->childIndexes[ mdxmSurfHierarch_t->numChildren ] ); +// } + + +// for each LOD... (mdxmHeader_t->numLODs) +// { + // mdxLOD_t - this contains the header for this LOD. Contains num of surfaces, offset to surfaces and offset to next LOD. Surfaces are shader sorted, so each surface = 1 shader + + typedef struct { + // (used to contain numSurface/ofsSurfaces fields, but these are same per LOD level now) + // + int ofsEnd; // offset to next LOD + } mdxmLOD_t; + + + typedef struct { // added in GLM version 3 for ingame use at Jake's request + int offsets[1]; // variable sized (mdxmHeader_t->numSurfaces), each offset points to surfaces below + } mdxmLODSurfOffset_t; + + + // for each surface... (mdxmHeader_t->numSurfaces) + // { + // mdxSurface_t - reuse of header format containing surface name, number of bones, offset to poly data and number of polys, offset to vertex information, and number of verts. NOTE offsets are relative to this header. + + typedef struct { + int ident; // this one field at least should be kept, since the game-engine may switch-case (but currently=0 in carcass) + + int thisSurfaceIndex; // 0...mdxmHeader_t->numSurfaces-1 (because of how ingame renderer works) + + int ofsHeader; // this will be a negative number, pointing back to main header + + int numVerts; + int ofsVerts; + + int numTriangles; + int ofsTriangles; + + int maxVertBoneWeights; // ... per vertex for hardware to reference. This number subtract the vert->numWeights gives # pad weights (which software will ignore) + + // Bone references are a set of ints representing all the bones + // present in any vertex weights for this surface. This is + // needed because a model may have surfaces that need to be + // drawn at different sort times, and we don't want to have + // to re-interpolate all the bones for each surface. + // + int numBoneReferences; + int ofsBoneReferences; + + int ofsEnd; // next surface follows + + } mdxmSurface_t; + + + // for each triangle... (mdxmSurface_t->numTriangles) + // { + // mdxTriangle_t - contains indexes into verts. One struct entry per poly. + + typedef struct { + int indexes[3]; + } mdxmTriangle_t; + // } + + + // for each vert... (mdxmSurface_t->numVerts) + // { + // mdxVertex_t - this is an array with number of verts from the surface definition as its bounds. It contains normal info, texture coors and number of weightings for this bone + + typedef struct { + vec3_t normal; + vec3_t vertCoords; + vec2_t texCoords; + int numWeights; // remember, this is for software counts, look at mdxmSurface_t->numActualWeights for skipping purposes to account for padded weights + mdxmWeight_t weights[1]; // variable sized + } mdxmVertex_t; + + // } vert + + // } surface +// } LOD + + + +//---------------------------------------------------------------------------- +// seperate file here for animation data... +// + + +// mdxaHeader_t - this contains the header for the file, with sanity checking and version checking, plus number of lod's to be expected +// +typedef struct { + // + // ( first 3 fields are same format as MD3/MDR so we can apply easy model-format-type checks ) + // + int ident; // "IDP3" = MD3, "RDM5" = MDR, "2LGA"(GL2 Anim) = MDXA + int version; // 1,2,3 etc as per format revision + // + char name[MAX_QPATH]; // GLA name (eg "skeletons/marine") // note: extension missing + float fScale; // will be zero if build before this field was defined, else scale it was built with + + // frames and bones are shared by all levels of detail + // + int numFrames; + int ofsFrames; + int numBones; // (no offset to these since they're inside the frames array) + int ofsCompBonePool; // offset to global compressed-bone pool that all frames use + int ofsSkel; // offset to mdxaSkel_t info + + int ofsEnd; // EOF, which of course gives overall file size + +} mdxaHeader_t; + + +// for each bone... (doesn't actually need a struct for this, just makes source clearer) +// { + typedef struct + { + int offsets[1]; // variable sized (mdxaHeader_t->numBones), each offset points to an mdxaSkel_t below + } mdxaSkelOffsets_t; +// } + + + +// for each bone... (mdxaHeader_t->numBones) +// { + // mdxaSkel_t - contains hierarchical info only... + + typedef struct { + char name[MAX_QPATH]; // name of bone + unsigned int flags; + int parent; // index of bone that is parent to this one, -1 = NULL/root + mdxaBone_t BasePoseMat; // base pose + mdxaBone_t BasePoseMatInv; // inverse, to save run-time calc + int numChildren; // number of children bones + int children[1]; // [mdxaSkel_t->numChildren] (variable sized) + } mdxaSkel_t; // struct size = (int)( &((mdxaSkel_t *)0)->children[ mdxaSkel_t->numChildren ] ); +// } + + + +// for each frame... (mdxaHeader_t->numFrames) +// { + // mdxaFrame_t - which contains the header for the bones for this surface, plus the actual bone matrices themselves + + typedef struct { + // (used to contain frame bounds info etc as well, doesn't now) + // + int boneIndexes[1]; // [numBones] ... into compressed bone pool + } mdxaFrame_t; // struct size = (int)( &((mdxaFrame_t *)0)->bones[ mdxaHeader_t->numBones ] ); + +// } + + +// Compressed-bone pool that all frames use (mdxaHeader_t->ofsCompBonePool) (defined at end because size unknown until end) +// for each bone in pool (unknown number, no actual total stored at the moment)... +// { + // mdxaCompBone_t (defined at file top because of struct dependancy) +// } + +//--------------------------------------------------------------------------- + + +#endif // #ifndef MDX_FORMAT_H + +//////////////////////// eof /////////////////////// + + + diff --git a/utils/q3data/models.c b/utils/q3data/models.c new file mode 100644 index 0000000..de35e79 --- /dev/null +++ b/utils/q3data/models.c @@ -0,0 +1,2741 @@ +#include +#include "q3data.h" +#include "models.h" +#include "g2export.h" + +//================================================================= + +static void OrderSurfaces( void ); +static void LoadBase( const char *filename ); +static int LoadModelFile( const char *filename, polyset_t **ppsets, int *pnumpolysets ); + + +q3data g_data; + +// the command list holds counts, the count * 3 xyz, st, normal indexes +// that are valid for every frame +#define MAXNAME 1024 +char g_cddir[MAXNAME]; +char g_modelname[MAXNAME]; + +// keep track of information regarding .ase's we're grabbing for concatenation +typedef struct animGrab_s +{ + // these fields get reset when a file grabbing session is init'd + int nGrabs; + int nFailedGrabs; + + // these fields get reset for each file grabbed + int nSourceFrame; + int nDestFrame; + int nNumFrames; + char curName[MAXNAME]; +} animGrab_t; + +animGrab_t g_animGrab; + +//============================================================== + + +/* +=============== +ClearModel +=============== +*/ +void ClearModel (void) +{ + int i; + + g_data.type = MD3_TYPE_UNKNOWN; + + for ( i = 0; i < MD3_MAX_SURFACES; i++ ) + { + memset( &g_data.surfData[i].header, 0, sizeof( g_data.surfData[i].header ) ); + memset( &g_data.surfData[i].shaders, 0, sizeof( g_data.surfData[i].shaders ) ); + memset( &g_data.surfData[i].verts, 0, sizeof( g_data.surfData[i].verts ) ); + } + + memset( g_data.tags, 0, sizeof( g_data.tags ) ); + memset (&g_data.model, 0, sizeof(g_data.model)); + memset (g_cddir, 0, sizeof(g_cddir)); + + g_modelname[0] = 0; + g_data.scale_up = 1.0; + VectorCopy (vec3_origin, g_data.adjust); + g_data.fixedwidth = g_data.fixedheight = 0; + g_skipmodel = qfalse; +} + +/* +** void WriteModelSurface( FILE *modelouthandle, md3SurfaceData_t *pSurfData ) +** +** This routine assumes that the file position has been adjusted +** properly prior to entry to point at the beginning of the surface. +** +** Since surface header information is completely relative, we can't +** just randomly seek to an arbitrary surface location right now. Is +** this something we should add? +*/ +void WriteModelSurface( FILE *modelouthandle, md3SurfaceData_t *pSurfData ) +{ + md3Surface_t *pSurf = &pSurfData->header; + md3Shader_t *pShader = pSurfData->shaders; + baseVertex_t *pBaseVertex = pSurfData->baseVertexes; + float **verts = pSurfData->verts; + + short xyznormals[MD3_MAX_VERTS][4]; + + float base_st[MD3_MAX_VERTS][2]; + md3Surface_t surftemp; + + int f, i, j, k; + + if ( strstr( pSurf->name, "tag_" ) == pSurf->name ) + return; + + // + // write out the header + // + surftemp = *pSurf; + surftemp.ident = LittleLong( MD3_IDENT ); + surftemp.flags = LittleLong( pSurf->flags ); + surftemp.numFrames = LittleLong( pSurf->numFrames ); + surftemp.numShaders = LittleLong( pSurf->numShaders ); + + surftemp.ofsShaders = LittleLong( pSurf->ofsShaders ); + + surftemp.ofsTriangles = LittleLong( pSurf->ofsTriangles ); + surftemp.numTriangles = LittleLong( pSurf->numTriangles ); + + surftemp.ofsSt = LittleLong( pSurf->ofsSt ); + surftemp.ofsXyzNormals = LittleLong( pSurf->ofsXyzNormals ); + surftemp.ofsEnd = LittleLong( pSurf->ofsEnd ); + + SafeWrite( modelouthandle, &surftemp, sizeof( surftemp ) ); + + if ( g_verbose ) + { + printf( "surface '%s'\n", pSurf->name ); + printf( "...num shaders: %d\n", pSurf->numShaders ); + } + + // + // write out shaders + // + for ( i = 0; i < pSurf->numShaders; i++ ) + { + md3Shader_t shadertemp; + + if ( g_verbose ) + printf( "......'%s'\n", pShader[i].name ); + + shadertemp = pShader[i]; + shadertemp.shaderIndex = LittleLong( shadertemp.shaderIndex ); + SafeWrite( modelouthandle, &shadertemp, sizeof( shadertemp ) ); + } + + // + // write out the triangles + // + for ( i = 0 ; i < pSurf->numTriangles ; i++ ) + { + for (j = 0 ; j < 3 ; j++) + { + int ivalue = LittleLong( pSurfData->orderedTriangles[i][j] ); + pSurfData->orderedTriangles[i][j] = ivalue; + } + } + + SafeWrite( modelouthandle, pSurfData->orderedTriangles, pSurf->numTriangles * sizeof( g_data.surfData[0].orderedTriangles[0] ) ); + + if ( g_verbose ) + { + printf( "\n...num verts: %d\n", pSurf->numVerts ); + printf( "...TEX COORDINATES\n" ); + } + + // + // write out the texture coordinates + // + for ( i = 0; i < pSurf->numVerts ; i++) { + base_st[i][0] = LittleFloat( pBaseVertex[i].st[0] ); + base_st[i][1] = LittleFloat( pBaseVertex[i].st[1] ); + if ( g_verbose ) + printf( "......%d: %f,%f\n", i, base_st[i][0], base_st[i][1] ); + } + SafeWrite( modelouthandle, base_st, pSurf->numVerts * sizeof(base_st[0])); + + // + // write the xyz_normal + // + if ( g_verbose ) + printf( "...XYZNORMALS\n" ); + for ( f = 0; f < g_data.model.numFrames; f++ ) + { + for (j=0 ; j< pSurf->numVerts; j++) + { + short value; + + for (k=0 ; k < 3 ; k++) + { + value = ( short ) ( verts[f][j*6+k] / MD3_XYZ_SCALE ); + xyznormals[j][k] = LittleShort( value ); + } + NormalToLatLong( &verts[f][j*6+3], (byte *)&xyznormals[j][3] ); + } + SafeWrite( modelouthandle, xyznormals, pSurf->numVerts * sizeof( short ) * 4 ); + } +} + +/* +** void WriteModelFile( FILE *modelouthandle ) +** +** CHUNK SIZE +** header sizeof( md3Header_t ) +** frames sizeof( md3Frame_t ) * numFrames +** tags sizeof( md3Tag_t ) * numFrames * numTags +** surfaces surfaceSum +*/ +void WriteModelFile( FILE *modelouthandle, const char *psFullPathedFilename, int type ) +{ + int f; + int i, j; + md3Header_t modeltemp; + long surfaceSum = 0; + int numRealSurfaces = 0; + int numFrames = g_data.model.numFrames; + + // compute offsets for all surfaces, sum their total size + for ( i = 0; i < g_data.model.numSurfaces; i++ ) + { + if ( strstr( g_data.surfData[i].header.name, "tag_" ) != g_data.surfData[i].header.name ) + { + md3Surface_t *psurf = &g_data.surfData[i].header; + + if ( psurf->numTriangles == 0 || psurf->numVerts == 0 ) + continue; + + // + // the triangle and vertex split threshold is controlled by a parameter + // to $base, a la $base blah.3ds 1900, where "1900" determines the number + // of triangles to split on + // + else if ( psurf->numVerts > MAX_SURFACE_VERTS ) + { + Error( "%s has too many vertices : %d > %d\n", psurf->name, psurf->numVerts, MAX_SURFACE_VERTS ); + } + + psurf->numFrames = numFrames; + + psurf->ofsShaders = sizeof( md3Surface_t ); + + if ( psurf->numTriangles > MAX_SURFACE_TRIS ) + { + Error( "%s has too many faces : %d > %d\n", psurf->name, psurf->numVerts, MAX_SURFACE_TRIS ); + } + + psurf->ofsTriangles = psurf->ofsShaders + psurf->numShaders * sizeof( md3Shader_t ); + + psurf->ofsSt = psurf->ofsTriangles + psurf->numTriangles * sizeof( md3Triangle_t ); + psurf->ofsXyzNormals = psurf->ofsSt + psurf->numVerts * sizeof( md3St_t ); + psurf->ofsEnd = psurf->ofsXyzNormals + psurf->numFrames * psurf->numVerts * ( sizeof( short ) * 4 ); + + surfaceSum += psurf->ofsEnd; + + numRealSurfaces++; + } + } + + g_data.model.ident = MD3_IDENT; + g_data.model.version = MD3_VERSION; + + g_data.model.ofsFrames = sizeof(md3Header_t); + g_data.model.ofsTags = g_data.model.ofsFrames + numFrames*sizeof(md3Frame_t); + g_data.model.ofsSurfaces = g_data.model.ofsTags + numFrames*g_data.model.numTags*sizeof(md3Tag_t); + g_data.model.ofsEnd = g_data.model.ofsSurfaces + surfaceSum; + + // + // write out the model header + // + modeltemp = g_data.model; + modeltemp.ident = LittleLong( modeltemp.ident ); + modeltemp.version = LittleLong( modeltemp.version ); + modeltemp.numFrames = LittleLong( modeltemp.numFrames ); + modeltemp.numTags = LittleLong( modeltemp.numTags ); + modeltemp.numSurfaces = LittleLong( numRealSurfaces ); + modeltemp.ofsFrames = LittleLong( modeltemp.ofsFrames ); + modeltemp.ofsTags = LittleLong( modeltemp.ofsTags ); + modeltemp.ofsSurfaces = LittleLong( modeltemp.ofsSurfaces ); + modeltemp.ofsEnd = LittleLong( modeltemp.ofsEnd ); + + if (type != TYPE_GHOUL2_1FRAME) + { + SafeWrite (modelouthandle, &modeltemp, sizeof(modeltemp)); + } + + // + // write out the frames + // + for (i=0 ; i < numFrames ; i++) + { + vec3_t tmpVec; + float maxRadius = 0; + + // + // compute localOrigin and radius + // + g_data.frames[i].localOrigin[0] = + g_data.frames[i].localOrigin[1] = + g_data.frames[i].localOrigin[2] = 0; + + for ( j = 0; j < 8; j++ ) + { + tmpVec[0] = g_data.frames[i].bounds[(j&1)!=0][0]; + tmpVec[1] = g_data.frames[i].bounds[(j&2)!=0][1]; + tmpVec[2] = g_data.frames[i].bounds[(j&4)!=0][2]; + + if ( VectorLength( tmpVec ) > maxRadius ) + maxRadius = VectorLength( tmpVec ); + } + + g_data.frames[i].radius = LittleFloat( maxRadius ); + + // swap + for (j=0 ; j<3 ; j++) { + g_data.frames[i].bounds[0][j] = LittleFloat( g_data.frames[i].bounds[0][j] ); + g_data.frames[i].bounds[1][j] = LittleFloat( g_data.frames[i].bounds[1][j] ); + g_data.frames[i].localOrigin[j] = LittleFloat( g_data.frames[i].localOrigin[j] ); + } + } + + if (type != TYPE_GHOUL2_1FRAME) + { + fseek (modelouthandle, g_data.model.ofsFrames, SEEK_SET); + SafeWrite( modelouthandle, g_data.frames, numFrames * sizeof(g_data.frames[0]) ); + fseek( modelouthandle, g_data.model.ofsTags, SEEK_SET ); + } + + // + // write out the tags + // + for (f=0 ; f 2 && g_data.surfData[i].header.name[slen-2] == '_' ) { + g_data.surfData[i].header.name[slen-2] = 0; + } + fprintf( defaultSkinHandle, "%s,%s\n", g_data.surfData[i].header.name, g_data.surfData[i].shaders[0].name ); + g_data.surfData[i].shaders[0].name[0] = 0; + } + fclose( defaultSkinHandle ); + } + + sprintf (name, "%s%s", writedir, g_modelname); + + // + // copy the model and its shaders to release directory tree + // if doing a release build + // + if ( g_release ) { + int i, j; + md3SurfaceData_t *pSurf; + + ReleaseFile( g_modelname ); + + for ( i = 0; i < g_data.model.numSurfaces; i++ ) { + pSurf = &g_data.surfData[i]; + for ( j = 0; j < g_data.model.numSkins; j++ ) { + ReleaseShader( pSurf->shaders[j].name ); + } + } + return; + } + + // + // write the model output file + // + CreatePath (name); + if (type != TYPE_GHOUL2_1FRAME) // file stuff done internally (I know, lazy and incompatible, but cut-paste reasons etc for now...) + { + printf ("saving to %s\n", name); + modelouthandle = SafeOpenWrite (name); + } + WriteModelFile (modelouthandle,name,type); // name param added because Ghoul2 may want it + + printf ("%4d surfaces\n", g_data.model.numSurfaces); + printf ("%4d frames\n", g_data.model.numFrames); + printf ("%4d tags\n", g_data.model.numTags); + if (type != TYPE_GHOUL2_1FRAME) // file stuff done internally + { + printf ("file size: %d\n", (int)ftell (modelouthandle) ); // ... and besides, g2 writes two files + } + printf ("---------------------\n"); + + if (type != TYPE_GHOUL2_1FRAME) // file stuff done internally + { + fclose (modelouthandle); + } +} + +/* +** OrderSurfaces +** +** Reorders triangles in all the surfaces. +*/ +static void OrderSurfaces( void ) +{ + int s; + extern qboolean g_stripify; + + // go through each surface and find best strip/fans possible + for ( s = 0; s < g_data.model.numSurfaces; s++ ) + { + int mesh[MD3_MAX_TRIANGLES][3]; + int i; + + for ( i = 0; i < g_data.surfData[s].header.numTriangles; i++ ) + { + mesh[i][0] = g_data.surfData[s].lodTriangles[i][0]; + mesh[i][1] = g_data.surfData[s].lodTriangles[i][1]; + mesh[i][2] = g_data.surfData[s].lodTriangles[i][2]; + } + + if ( g_stripify ) + { + printf( "stripifying surface %d/%d with %d tris\n", s, g_data.model.numSurfaces, g_data.surfData[s].header.numTriangles ); + + OrderMesh( mesh, // input + g_data.surfData[s].orderedTriangles, // output + g_data.surfData[s].header.numTriangles, + MD3_MAX_VERTS + ); + } + else + { + memcpy( g_data.surfData[s].orderedTriangles, mesh, sizeof( int ) * 3 * g_data.surfData[s].header.numTriangles ); + } + } +} + + +/* +=============================================================== + +BASE FRAME SETUP + +=============================================================== +*/ +/* +============ +CopyTrianglesToBaseTriangles + +============ +*/ +static void CopyTrianglesToBaseTriangles(triangle_t *ptri, int numtri, baseTriangle_t *bTri ) +{ + int i; +// int width, height, iwidth, iheight, swidth; +// float s_scale, t_scale; +// float scale; +// vec3_t mins, maxs; + float *pbasevert; + +/* + // + // find bounds of all the verts on the base frame + // + ClearBounds (mins, maxs); + + for (i=0 ; i= 150) + scale = 150.0 / width; + if (height*scale >= 190) + scale = 190.0 / height; + + s_scale = t_scale = scale; + + iwidth = ceil(width*s_scale); + iheight = ceil(height*t_scale); + + iwidth += 4; + iheight += 4; + } + else + { // new style + iwidth = g_data.fixedwidth / 2; + iheight = g_data.fixedheight; + + s_scale = (float)(iwidth-4) / width; + t_scale = (float)(iheight-4) / height; + } + + // make the width a multiple of 4; some hardware requires this, and it ensures + // dword alignment for each scan + swidth = iwidth*2; + g_data.skinwidth = (swidth + 3) & ~3; + g_data.skinheight = iheight; +*/ + + for (i=0; iverts[j]; + + VectorCopy( ptri->verts[j], bTri->v[j].xyz); + VectorCopy( ptri->normals[j], bTri->v[j].normal ); + + bTri->v[j].st[0] = ptri->texcoords[j][0]; + bTri->v[j].st[1] = ptri->texcoords[j][1]; + } + } +} + +static void BuildBaseFrame( const char *filename, ObjectAnimationFrame_t *pOAF ) +{ + baseTriangle_t *bTri; + baseVertex_t *bVert; + int i, j; + + // calculate the base triangles + for ( i = 0; i < g_data.model.numSurfaces; i++ ) + { + CopyTrianglesToBaseTriangles( pOAF->surfaces[i]->triangles, + pOAF->surfaces[i]->numtriangles, + g_data.surfData[i].baseTriangles ); + + strcpy( g_data.surfData[i].header.name, pOAF->surfaces[i]->name ); + + g_data.surfData[i].header.numTriangles = pOAF->surfaces[i]->numtriangles; + g_data.surfData[i].header.numVerts = 0; + +/* + if ( strstr( filename, gamedir + 1 ) ) + { + strcpy( shaderName, strstr( filename, gamedir + 1 ) + strlen( gamedir ) - 1 ); + } + else + { + strcpy( shaderName, filename ); + } + + if ( strrchr( shaderName, '/' ) ) + *( strrchr( shaderName, '/' ) + 1 ) = 0; + + + strcpy( shaderName, pOAF->surfaces[i]->materialname ); +*/ + //safety check here!!! + strncpy( g_data.surfData[i].shaders[g_data.surfData[i].header.numShaders].name, pOAF->surfaces[i]->materialname, sizeof(g_data.surfData[0].shaders[0].name) ); + g_data.surfData[i].shaders[g_data.surfData[i].header.numShaders].name[sizeof(g_data.surfData[0].shaders[0].name)-1]=0; + g_data.surfData[i].header.numShaders++; + } + + // + // compute unique vertices for each polyset + // + for ( i = 0; i < g_data.model.numSurfaces; i++ ) + { + int t; + + for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ ) + { + bTri = &g_data.surfData[i].baseTriangles[t]; + + for (j=0 ; j<3 ; j++) + { + int k; + + bVert = &bTri->v[j]; + + // get the xyz index + for ( k = 0; k < g_data.surfData[i].header.numVerts; k++ ) + { + if ( ( g_data.surfData[i].baseVertexes[k].st[0] == bVert->st[0] ) && + ( g_data.surfData[i].baseVertexes[k].st[1] == bVert->st[1] ) && + ( VectorCompare (bVert->xyz, g_data.surfData[i].baseVertexes[k].xyz) ) && + ( VectorCompare (bVert->normal, g_data.surfData[i].baseVertexes[k].normal) ) ) + { + break; // this vertex is already in the base vertex list + } + } + + if (k == g_data.surfData[i].header.numVerts) { // new index + g_data.surfData[i].baseVertexes[g_data.surfData[i].header.numVerts] = *bVert; + g_data.surfData[i].header.numVerts++; + } + + bVert->index = k; + + g_data.surfData[i].lodTriangles[t][j] = k; + } + } + } + + // + // find tags + // + for ( i = 0; i < g_data.model.numSurfaces; i++ ) + { + if ( strstr( pOAF->surfaces[i]->name, "tag_" ) == pOAF->surfaces[i]->name ) + { + if ( pOAF->surfaces[i]->numtriangles != 1 ) + { + Error( "tag polysets must consist of only one triangle" ); + } + if ( strstr( filename, "_flash.md3" ) && !strcmp( pOAF->surfaces[i]->name, "tag_parent" ) ) + continue; + printf( "found tag '%s'\n", pOAF->surfaces[i]->name ); + g_data.model.numTags++; + } + } + +} + +static int LoadModelFile( const char *filename, polyset_t **psets, int *numpolysets ) +{ + int time1; + char file1[MAXNAME]; + const char *frameFile; + + printf ("---------------------\n"); + if ( filename[1] != ':' ) + { + frameFile = filename; + sprintf( file1, "%s/%s", g_cddir, frameFile ); + } + else + { + strcpy( file1, filename ); + } + + time1 = FileTime (file1); + if (time1 == -1) + Error ("%s doesn't exist", file1); + + // + // load the base triangles + // + *psets = Polyset_LoadSets( file1, numpolysets, g_data.maxSurfaceTris ); + + // + // snap polysets + // + Polyset_SnapSets( *psets, *numpolysets ); + + if ( strstr( file1, ".3ds" ) || strstr( file1, ".3DS" ) ) + return MD3_TYPE_BASE3DS; + + Error( "Unknown model file type" ); + + return MD3_TYPE_UNKNOWN; +} + +/* +================= +Cmd_Base +================= +*/ +void Cmd_Base( void ) +{ + char filename[MAXNAME]; + + GetToken( qfalse ); + sprintf( filename, "%s/%s", g_cddir, token ); + LoadBase( filename ); +} + +static void LoadBase( const char *filename ) +{ + int numpolysets; + polyset_t *psets; + int i; + ObjectAnimationFrame_t oaf; + + // determine polyset splitting threshold + if ( TokenAvailable() ) + { + GetToken( qfalse ); + g_data.maxSurfaceTris = atoi( token ); + } + else + { + g_data.maxSurfaceTris = MAX_SURFACE_TRIS - 1; + } + + g_data.type = LoadModelFile( filename, &psets, &numpolysets ); + + Polyset_ComputeNormals( psets, numpolysets ); + + g_data.model.numSurfaces = numpolysets; + + memset( &oaf, 0, sizeof( oaf ) ); + + for ( i = 0; i < numpolysets; i++ ) + { + oaf.surfaces[i] = &psets[i]; + oaf.numSurfaces = numpolysets; + } + + BuildBaseFrame( filename, &oaf ); + + free( psets[0].triangles ); + free( psets ); +} + +/* +================= +Cmd_SpriteBase + +$spritebase xorg yorg width height + +Generate a single square for the model +================= +*/ +void Cmd_SpriteBase (void) +{ + float xl, yl, width, height; + + g_data.type = MD3_TYPE_SPRITE; + + GetToken (qfalse); + xl = atof(token); + GetToken (qfalse); + yl = atof(token); + GetToken (qfalse); + width = atof(token); + GetToken (qfalse); + height = atof(token); + +// if (g_skipmodel || g_release || g_archive) +// return; + + printf ("---------------------\n"); + + g_data.surfData[0].verts[0] = ( float * ) calloc( 1, sizeof( float ) * 6 * 4 ); + + g_data.surfData[0].header.numVerts = 4; + + g_data.surfData[0].verts[0][0+0] = 0; + g_data.surfData[0].verts[0][0+1] = -xl; + g_data.surfData[0].verts[0][0+2] = yl + height; + + g_data.surfData[0].verts[0][0+3] = -1; + g_data.surfData[0].verts[0][0+4] = 0; + g_data.surfData[0].verts[0][0+5] = 0; + g_data.surfData[0].baseVertexes[0].st[0] = 0; + g_data.surfData[0].baseVertexes[0].st[1] = 0; + + + g_data.surfData[0].verts[0][6+0] = 0; + g_data.surfData[0].verts[0][6+1] = -xl - width; + g_data.surfData[0].verts[0][6+2] = yl + height; + + g_data.surfData[0].verts[0][6+3] = -1; + g_data.surfData[0].verts[0][6+4] = 0; + g_data.surfData[0].verts[0][6+5] = 0; + g_data.surfData[0].baseVertexes[1].st[0] = 1; + g_data.surfData[0].baseVertexes[1].st[1] = 0; + + + g_data.surfData[0].verts[0][12+0] = 0; + g_data.surfData[0].verts[0][12+1] = -xl - width; + g_data.surfData[0].verts[0][12+2] = yl; + + g_data.surfData[0].verts[0][12+3] = -1; + g_data.surfData[0].verts[0][12+4] = 0; + g_data.surfData[0].verts[0][12+5] = 0; + g_data.surfData[0].baseVertexes[2].st[0] = 1; + g_data.surfData[0].baseVertexes[2].st[1] = 1; + + + g_data.surfData[0].verts[0][18+0] = 0; + g_data.surfData[0].verts[0][18+1] = -xl; + g_data.surfData[0].verts[0][18+2] = yl; + + g_data.surfData[0].verts[0][18+3] = -1; + g_data.surfData[0].verts[0][18+4] = 0; + g_data.surfData[0].verts[0][18+5] = 0; + g_data.surfData[0].baseVertexes[3].st[0] = 0; + g_data.surfData[0].baseVertexes[3].st[1] = 1; + + g_data.surfData[0].lodTriangles[0][0] = 0; + g_data.surfData[0].lodTriangles[0][1] = 1; + g_data.surfData[0].lodTriangles[0][2] = 2; + + g_data.surfData[0].lodTriangles[1][0] = 2; + g_data.surfData[0].lodTriangles[1][1] = 3; + g_data.surfData[0].lodTriangles[1][2] = 0; + + g_data.model.numSurfaces = 1; + + g_data.surfData[0].header.numTriangles = 2; + g_data.surfData[0].header.numVerts = 4; + + g_data.model.numFrames = 1; +} + +/* +=========================================================================== + + FRAME GRABBING + +=========================================================================== +*/ + +/* +=============== +GrabFrame +=============== +*/ +void GrabFrame (const char *frame) +{ + int i, j, k; + char file1[MAXNAME]; + md3Frame_t *fr; + md3Tag_t tagParent; + float *frameXyz; + float *frameNormals; + const char *framefile; + polyset_t *psets; + qboolean parentTagExists = qfalse; + int numpolysets; + int numtags = 0; + int tagcount; + + // the frame 'run1' will be looked for as either + // run.1 or run1.tri, so the new alias sequence save + // feature an be used + if ( frame[1] != ':' ) + { +// framefile = FindFrameFile (frame); + framefile = frame; + sprintf (file1, "%s/%s",g_cddir, framefile); + } + else + { + strcpy( file1, frame ); + } + printf ("grabbing %s\n", file1); + + if (g_data.model.numFrames >= MD3_MAX_FRAMES) + Error ("model.numFrames >= MD3_MAX_FRAMES"); + fr = &g_data.frames[g_data.model.numFrames]; + + strcpy (fr->name, frame); + + psets = Polyset_LoadSets( file1, &numpolysets, g_data.maxSurfaceTris ); + + // + // snap polysets + // + Polyset_SnapSets( psets, numpolysets ); + + // + // compute vertex normals + // + Polyset_ComputeNormals( psets, numpolysets ); + + // + // flip everything to compensate for the alias coordinate system + // and perform global scale and adjust + // + for ( i = 0; i < g_data.model.numSurfaces; i++ ) + { + triangle_t *ptri = psets[i].triangles; + int t; + + for ( t = 0; t < psets[i].numtriangles; t++ ) + { + + for ( j = 0; j < 3; j++ ) + { + + // scale and adjust + for ( k = 0 ; k < 3 ; k++ ) { + ptri[t].verts[j][k] = ptri[t].verts[j][k] * g_data.scale_up + + g_data.adjust[k]; + + if ( ptri[t].verts[j][k] > 1023 || + ptri[t].verts[j][k] < -1023 ) + { + Error( "Model extents too large" ); + } + } + } + } + } + + // + // find and count tags, locate parent tag + // + for ( i = 0; i < numpolysets; i++ ) + { + if ( strstr( psets[i].name, "tag_" ) == psets[i].name ) + { + if ( strstr( psets[i].name, "tag_parent" ) == psets[i].name ) + { + if ( strstr( psets[i].name, "tag_parent" ) ) + { + float tri[3][3]; + + if ( parentTagExists ) + Error( "Multiple parent tags not allowed" ); + + memcpy( tri[0], psets[i].triangles[0].verts[0], sizeof( float ) * 3 ); + memcpy( tri[1], psets[i].triangles[0].verts[1], sizeof( float ) * 3 ); + memcpy( tri[2], psets[i].triangles[0].verts[2], sizeof( float ) * 3 ); + + strcpy( tagParent.name, psets[i].name ); + MD3_ComputeTagFromTri( &tagParent, tri ); + g_data.tags[g_data.model.numFrames][numtags] = tagParent; + parentTagExists = qtrue; + + } + } + numtags++; + } + + if ( strcmp( psets[i].name, g_data.surfData[i].header.name ) ) + { + Error( "Mismatched surfaces from base('%s') to frame('%s') in model '%s'\n", g_data.surfData[i].header.name, psets[i].name, g_modelname ); + } + } + + if ( numtags != g_data.model.numTags ) + { + Error( "mismatched number of tags in frame(%d) vs. base(%d)", numtags, g_data.model.numTags ); + } + + if ( numpolysets != g_data.model.numSurfaces ) + { + Error( "mismatched number of surfaces in frame(%d) vs. base(%d)", numpolysets-numtags, g_data.model.numSurfaces ); + } + + // + // prepare to accumulate bounds and normals + // + ClearBounds( fr->bounds[0], fr->bounds[1] ); + + // + // store the frame's vertices in the same order as the base. This assumes the + // triangles and vertices in this frame are in exactly the same order as in the + // base + // + for ( i = 0, tagcount = 0; i < numpolysets; i++ ) + { + int t; + triangle_t *pTris = psets[i].triangles; + + strcpy( g_data.surfData[i].header.name, psets[i].name ); + + // + // parent tag adjust + // + if ( parentTagExists ) { + for ( t = 0; t < psets[i].numtriangles; t++ ) + { + for ( j = 0; j < 3 ; j++ ) + { + vec3_t tmp; + + VectorSubtract( pTris[t].verts[j], tagParent.origin, tmp ); + + pTris[t].verts[j][0] = DotProduct( tmp, tagParent.axis[0] ); + pTris[t].verts[j][1] = DotProduct( tmp, tagParent.axis[1] ); + pTris[t].verts[j][2] = DotProduct( tmp, tagParent.axis[2] ); + + VectorCopy( pTris[t].normals[j], tmp ); + pTris[t].normals[j][0] = DotProduct( tmp, tagParent.axis[0] ); + pTris[t].normals[j][1] = DotProduct( tmp, tagParent.axis[1] ); + pTris[t].normals[j][2] = DotProduct( tmp, tagParent.axis[2] ); + } + } + } + + // + // compute tag data + // + if ( strstr( psets[i].name, "tag_" ) == psets[i].name ) + { + md3Tag_t *pTag = &g_data.tags[g_data.model.numFrames][tagcount]; + float tri[3][3]; + + strcpy( pTag->name, psets[i].name ); + + memcpy( tri[0], pTris[0].verts[0], sizeof( float ) * 3 ); + memcpy( tri[1], pTris[0].verts[1], sizeof( float ) * 3 ); + memcpy( tri[2], pTris[0].verts[2], sizeof( float ) * 3 ); + + MD3_ComputeTagFromTri( pTag, tri ); + tagcount++; + } + else + { + if ( g_data.surfData[i].verts[g_data.model.numFrames] ) + free( g_data.surfData[i].verts[g_data.model.numFrames] ); + frameXyz = g_data.surfData[i].verts[g_data.model.numFrames] = calloc( 1, sizeof( float ) * 6 * g_data.surfData[i].header.numVerts ); + frameNormals = frameXyz + 3; + + for ( t = 0; t < psets[i].numtriangles; t++ ) + { + for ( j = 0; j < 3 ; j++ ) + { + int index; + + index = g_data.surfData[i].baseTriangles[t].v[j].index; + frameXyz[index*6+0] = pTris[t].verts[j][0]; + frameXyz[index*6+1] = pTris[t].verts[j][1]; + frameXyz[index*6+2] = pTris[t].verts[j][2]; + frameNormals[index*6+0] = pTris[t].normals[j][0]; + frameNormals[index*6+1] = pTris[t].normals[j][1]; + frameNormals[index*6+2] = pTris[t].normals[j][2]; + AddPointToBounds (&frameXyz[index*6], fr->bounds[0], fr->bounds[1] ); + } + } + } + } + + g_data.model.numFrames++; + + // only free the first triangle array, all of the psets in this array share the + // same triangle pool!!! +// free( psets[0].triangles ); +// free( psets ); +} + +//=========================================================================== + + + +/* +=============== +Cmd_Frame +=============== +*/ +void Cmd_Frame (void) +{ + while (TokenAvailable()) + { + GetToken (qfalse); + if (g_skipmodel) + continue; + if (g_release || g_archive) + { + g_data.model.numFrames = 1; // don't skip the writeout + continue; + } + + GrabFrame( token ); + } +} + + +/* +=============== +Cmd_Skin + +=============== +*/ +void SkinFrom3DS( const char *filename ) +{ + polyset_t *psets; + char name[MAXNAME]; + int numPolysets; + int i; + + _3DS_LoadPolysets( filename, &psets, &numPolysets, g_verbose ); + + for ( i = 0; i < numPolysets; i++ ) + { +/* + if ( strstr( filename, gamedir + 1 ) ) + { + strcpy( name, strstr( filename, gamedir + 1 ) + strlen( gamedir ) - 1 ); + } + else + { + strcpy( name, filename ); + } + + if ( strrchr( name, '/' ) ) + *( strrchr( name, '/' ) + 1 ) = 0; +*/ + strcpy( name, psets[i].materialname ); + strcpy( g_data.surfData[i].shaders[g_data.surfData[i].header.numShaders].name, name ); + + g_data.surfData[i].header.numShaders++; + } + + free( psets[0].triangles ); + free( psets ); +} + +void Cmd_Skin (void) +{ + char skinfile[MAXNAME]; + + if ( g_data.type == MD3_TYPE_BASE3DS ) + { + GetToken( qfalse ); + + sprintf( skinfile, "%s/%s", g_cddir, token ); + + if ( strstr( token, ".3ds" ) || strstr( token, ".3DS" ) ) + { + SkinFrom3DS( skinfile ); + } + else + { + Error( "Unknown file format for $skin '%s'\n", skinfile ); + } + } + else + { + Error( "invalid model type while processing $skin" ); + } + + g_data.model.numSkins++; +} + +/* +================= +Cmd_SpriteShader +================= + +This routine is also called for $oldskin + +*/ +void Cmd_SpriteShader() +{ + GetToken( qfalse ); + strcpy( g_data.surfData[0].shaders[g_data.surfData[0].header.numShaders].name, token ); + g_data.surfData[0].header.numShaders++; + g_data.model.numSkins++; +} + +/* +================= +Cmd_Origin +================= +*/ +void Cmd_Origin (void) +{ + // rotate points into frame of reference so model points down the + // positive x axis + // FIXME: use alias native coordinate system + GetToken (qfalse); + g_data.adjust[1] = -atof (token); + + GetToken (qfalse); + g_data.adjust[0] = atof (token); + + GetToken (qfalse); + g_data.adjust[2] = -atof (token); +} + + +/* +================= +Cmd_ScaleUp +================= +*/ +void Cmd_ScaleUp (void) +{ + GetToken (qfalse); + g_data.scale_up = atof (token); + if (g_skipmodel || g_release || g_archive) + return; + + printf ("Scale up: %f\n", g_data.scale_up); +} + + +/* +================= +Cmd_Skinsize + +Set a skin size other than the default +QUAKE3: not needed +================= +*/ +void Cmd_Skinsize (void) +{ + GetToken (qfalse); + g_data.fixedwidth = atoi(token); + GetToken (qfalse); + g_data.fixedheight = atoi(token); +} + +/* +================= +Cmd_Modelname + +Begin creating a model of the given name +================= +*/ +void Cmd_Modelname (void) +{ + FinishModel ( TYPE_UNKNOWN ); + ClearModel (); + + GetToken (qfalse); + strcpy (g_modelname, token); + StripExtension (g_modelname); + strcat (g_modelname, ".md3"); + strcpy (g_data.model.name, g_modelname); +} + +/* +=============== +fCmd_Cd +=============== +*/ +void Cmd_Cd (void) +{ + if ( g_cddir[0]) { + Error ("$cd command without a $modelname"); + } + + GetToken (qfalse); + + sprintf ( g_cddir, "%s%s", gamedir, token); + + // if -only was specified and this cd doesn't match, + // skip the model (you only need to match leading chars, + // so you could regrab all monsters with -only models/monsters) + if (!g_only[0]) + return; + if (strncmp(token, g_only, strlen(g_only))) + { + g_skipmodel = qtrue; + printf ("skipping %s\n", token); + } +} + +void Convert3DStoMD3( const char *file ) +{ + LoadBase( file ); + GrabFrame( file ); + SkinFrom3DS( file ); + + strcpy( g_data.model.name, g_modelname ); + + FinishModel( TYPE_UNKNOWN ); + ClearModel(); +} + +/* +** Cmd_3DSConvert +*/ +void Cmd_3DSConvert() +{ + char file[MAXNAME]; + + FinishModel( TYPE_UNKNOWN ); + ClearModel(); + + GetToken( qfalse ); + + sprintf( file, "%s%s", gamedir, token ); + strcpy( g_modelname, token ); + if ( strrchr( g_modelname, '.' ) ) + *strrchr( g_modelname, '.' ) = 0; + strcat( g_modelname, ".md3" ); + + if ( FileTime( file ) == -1 ) + Error( "%s doesn't exist", file ); + + if ( TokenAvailable() ) + { + GetToken( qfalse ); + g_data.scale_up = atof( token ); + } + + Convert3DStoMD3( file ); +} + +static void ConvertASE( const char *filename, int type, qboolean grabAnims, qboolean bInternal ); + +/* +** Cmd_ASEConvert +*/ +void Cmd_ASEConvert( qboolean grabAnims, qboolean bIsGhoul2) +{ + char *p; + char filename[MAXNAME]; + int type = TYPE_ITEM; + qboolean bInternal = qfalse; + + FinishModel( TYPE_UNKNOWN ); + ClearModel(); + + GetToken( qfalse ); + + while((p = strchr(token,'\\')) != NULL) + { + *p = '/'; + } + + if (strstr(token, "internal")) + { + // want to perform a conversion on the stuff stored in aseGrab rather + //than a file, so skip the ASE_Load() + bInternal = qtrue; + } + sprintf( filename, "%s%s", gamedir, token ); + + strcpy (g_modelname, token); + StripExtension (g_modelname); + strcat (g_modelname, bIsGhoul2?".glm":".md3"); + strcpy (g_data.model.name, g_modelname); + + if ( !strstr( filename, ".ase" ) && !strstr( filename, ".ASE" ) ) + strcat( filename, ".ASE" ); + + g_data.maxSurfaceTris = MAX_SURFACE_TRIS - 1; + + while ( TokenAvailable() ) + { + GetToken( qfalse ); + if ( !strcmp( token, "-origin" ) ) + { + if ( !TokenAvailable() ) + Error( "missing parameter for -origin" ); + GetToken( qfalse ); + g_data.aseAdjust[1] = -atof( token ); + + if ( !TokenAvailable() ) + Error( "missing parameter for -origin" ); + GetToken( qfalse ); + g_data.aseAdjust[0] = atof (token); + + if ( !TokenAvailable() ) + Error( "missing parameter for -origin" ); + GetToken( qfalse ); + g_data.aseAdjust[2] = -atof (token); + + if (bIsGhoul2) + printf("Warning: Ghoul2 ignores '-origin' args\n"); + } + else if ( !strcmp( token, "-lod" ) ) + { + if ( !TokenAvailable() ) + Error( "No parameter for -lod" ); + GetToken( qfalse ); + g_data.currentLod = atoi( token ); + if ( g_data.currentLod > MD3_MAX_LODS - 1 ) + { + Error( "-lod parameter too large! (%d)\n", g_data.currentLod ); + } +#if 0 + if ( !TokenAvailable() ) + Error( "No second parameter for -lod" ); + GetToken( qfalse ); + g_data.lodBias = atof( token ); +#endif + + if (bIsGhoul2) + printf("Warning: Ghoul2 (probably) ignores '-lod' args\n"); + } + else if ( !strcmp( token, "-maxtris" ) ) + { + if ( !TokenAvailable() ) + Error( "No parameter for -maxtris" ); + GetToken( qfalse ); + g_data.maxSurfaceTris = atoi( token ); + + if (bIsGhoul2) + printf("Warning: Ghoul2 (probably) ignores '-maxtris' args\n"); + } + else if ( !strcmp( token, "-playerparms" ) ) + { + if ( !TokenAvailable() ) + Error( "missing skip start parameter for -playerparms" ); + GetToken( qfalse ); + g_data.lowerSkipFrameStart = atoi( token ); + + if ( !TokenAvailable() ) + Error( "missing upper parameter for -playerparms" ); + GetToken( qfalse ); + g_data.maxUpperFrames = atoi( token ); + g_data.lowerSkipFrameEnd = g_data.maxUpperFrames - 1; + + if ( !TokenAvailable() ) + Error( "missing head parameter for -playerparms" ); + GetToken( qfalse ); + g_data.maxHeadFrames = atoi( token ); + + if ( type != TYPE_ITEM ) + Error( "invalid argument" ); + + // set default player origin offsets + // this is sort of a historic artifact... + g_data.aseAdjust[1] = 0; + g_data.aseAdjust[0] = 0; + g_data.aseAdjust[2] = -24; + + type = TYPE_PLAYER; + + if (bIsGhoul2) + printf("Warning: Ghoul2 ignores '-playerparms' args\n"); + } + else if ( !strcmp( token, "-weapon" ) ) + { + if ( type != TYPE_ITEM ) + Error( "invalid argument" ); + + type = TYPE_WEAPON; + + if (bIsGhoul2) + { + Error("Using the '-weapon' switch means do some very specific MD3 model stuff to do with producing hand & gun models, and can't currently be used with the Ghoul2 output option"); + } + } + else if ( !strcmp( token, "-scale" ) ) + { + if ( !TokenAvailable() ) + Error( "No parameter for -scale" ); + GetToken( qfalse ); + g_data.scale_up = atof( token ); + +// if (bIsGhoul2) +// printf("Warning: Ghoul2 (probably) ignores '-scale' args\n"); + } + } + + if (bIsGhoul2) + { + type = TYPE_GHOUL2_1FRAME; + if (grabAnims) + { + Error( "can't grab anims with ghoul2 1-frame models" ); + } + } + + g_data.type = MD3_TYPE_ASE; + + if ( type == TYPE_WEAPON && grabAnims ) + { + Error( "can't grab anims with weapon models" ); + } + if ( type == TYPE_PLAYER && !grabAnims ) + { + Error( "player models must be converted with $aseanimconvert" ); + } + + if ( type == TYPE_WEAPON ) + { + ConvertASE( filename, type, qfalse, bInternal); + ConvertASE( filename, TYPE_HAND, qtrue, bInternal); + } + else + { + ConvertASE( filename, type, grabAnims, bInternal); + } +} + +/* +** Cmd_ASEInitAnimGrab +** +** get ready to perform one or more Cmd_ASEAnimGrab's +*/ +void Cmd_ASEInitAnimGrab(void) +{ + // want these default values to be valid, not error values. that way the + //user doesn't have to enter "-frames x y z" info as part of the $aseanimgrab + g_animGrab.nSourceFrame = 0; + g_animGrab.nDestFrame = 0; + g_animGrab.nNumFrames = -1; // grab 'em all + g_animGrab.nGrabs = g_animGrab.nFailedGrabs = 0; + g_animGrab.curName[0]=0; + + ClearModel(); + + ASE_InitForGrabbing(); + + // I don't think this is necessary cuz animgrabs never really touch g_data, but... + g_data.maxSurfaceTris = MAX_SURFACE_TRIS - 1; +} + +/* +** Cmd_ASEFinalizeAnimGrab +** +** get name for output file and clean up whatever we allocated for performing Cmd_ASEAnimGrab's +*/ +void Cmd_ASEFinalizeAnimGrab(void) +{ +/* +** don't need an output name for player anims cuz they'll get split into +**upper.md3, lower.md3, and head.md3 anyway. if we ever perform animgrabs on +**non-player models then maybe this'll be useful +** + if (TokenAvailable()) + { + GetToken( qfalse ); + + strcpy (g_modelname, token); + } + else + { + Error("No name given for output file...using last input file w/.md3 extension!"); + strcpy(g_modelname, g_animGrab.curName); + } + StripExtension (g_modelname); + strcat (g_modelname, ".md3"); + strcpy (g_data.model.name, g_modelname); +*/ + + + // by now we should have all desired animations concatenated into aseGrab struct. we also + //have an output filename, so we should be good to go if the .qdt wants us to + //do an $aseanimconvert using the concatenated stuff instead of a single file. + if (g_animGrab.nFailedGrabs) + { + printf( "%d Failed Grabs out of %d\n", g_animGrab.nFailedGrabs, g_animGrab.nGrabs); + } +} + +/* +** Cmd_ASEAnimGrab +** +** load an .ase file and add its frames to our global ase structure as +**determined by command line parameters. this kinda assumes you're only going +**to try this with anims involving the same model. +*/ +void Cmd_ASEAnimGrab(void) +{ + char filename[MAXNAME]; + qboolean bFailed = qfalse; + int nFill = 0; + + // we'll get our input file name here, but the output filename is + //given in the $aseanimgrabfinalize command (see Cmd_ASEFinalizeAnimGrab) + GetToken( qfalse ); + // copy token into g_animGrab _before_ appending to gamedir (for use as default + //output filename) + strncpy(g_animGrab.curName, token, MAXNAME); + sprintf( filename, "%s%s", gamedir, token ); + + if ( !strstr( filename, ".ase" ) && !strstr( filename, ".ASE" ) ) + strcat( filename, ".ASE" ); + + + while ( TokenAvailable() ) + { + GetToken( qfalse ); + if ( !strcmp( token, "-frames" ) ) + { + if ( !TokenAvailable() ) + Error( "missing 'source frame' parameter for -frames" ); + GetToken( qfalse ); + g_animGrab.nSourceFrame = atoi( token ); + + if ( !TokenAvailable() ) + Error( "missing 'dest frame' parameter for -frames" ); + GetToken( qfalse ); + g_animGrab.nDestFrame = atoi(token); + + if ( !TokenAvailable() ) + Error( "missing 'number of frames' parameter for -frames" ); + GetToken( qfalse ); + g_animGrab.nNumFrames = atoi(token); + } + else if ( !strcmp( token, "-fill" ) ) + { + // user wants to fill the output file with the last frame of this file's anim + if ( !TokenAvailable() ) + Error( "missing 'source frame' parameter for -frames" ); + GetToken( qfalse ); + nFill = atoi( token ); + } + else if (!strcmp(token, "-enum")) + { + // ignore enum used by Assimilate + GetToken( qfalse ); + } + else if (!strcmp(token, "-action")) + { + // ignore action used by Assimilate + GetToken( qfalse ); + } + else if (!strcmp(token, "-sound")) + { + // ignore sound used by Assimilate + GetToken( qfalse ); + } + else if (!strcmp(token, "-loop")) + { + GetToken( qfalse ); + } + else if (!strcmp(token, "-qdskipstart")) + { + // ignore every token between here and the end marker + while (TokenAvailable()) + { + GetToken( qfalse ); + + if (!strcmp(token, "-qdskipstop")) + break; + } + } + } + + /* + ** load ASE into memory + */ + ASE_Load( filename, + g_verbose, + qtrue, + TYPE_PLAYER);// (for now, at least) we're only loading player anims + + { + //FIXME -- should do some error checking here to make sure our earlier grabbed info + //already in aseGrab matches with the new file we're grabbing (ase) + + if (0 == g_animGrab.nNumFrames) + { + Error("explicitly requesting 0 frames to be grabbed from %s",g_animGrab.curName); + } + else + { + bFailed = ASE_CatGrabbedFrames(g_animGrab.nSourceFrame,g_animGrab.nDestFrame, + g_animGrab.nNumFrames, nFill); + } + if (bFailed) + { + printf( "* Failed grab Source(%d) Dest(%d) NumFrames(%d) Fill(%d)\n", g_animGrab.nSourceFrame,g_animGrab.nDestFrame, + g_animGrab.nNumFrames, nFill); + g_animGrab.nFailedGrabs++; + } + else + { + g_animGrab.nGrabs++; + } + } + + // reset some per-grab info + g_animGrab.nDestFrame = 0; + g_animGrab.nSourceFrame = 0; + g_animGrab.nNumFrames = -1; +} + +void CleanUpAfterGrabbing() +{ + ASE_FreeGrab(); +} + +void Recompute_Normals(SurfaceAnimation_t sanims[], int current_frame, int current_surface, int current_triangle, int current_vert, int NumSurfaces) +{ + normal_compute_t normal_array[100]; + + polyset_t *poly_list; + int i, triangle, x; + int current_normal_entry = 0; + triangle_t *main_tri; + + main_tri = &sanims[current_surface].frames[current_frame].triangles[current_triangle]; + + // clear out the array weare going to use. + memset(normal_array, 0, sizeof(normal_array)); + + // search through all successive surfaces from the vertex/triangle/surface we are at now. + for (i = current_surface; iverts[current_vert][0] == poly_list[current_frame].triangles[triangle].verts[comp_vert][0]) + && (main_tri->verts[current_vert][1] == poly_list[current_frame].triangles[triangle].verts[comp_vert][1]) + && (main_tri->verts[current_vert][2] == poly_list[current_frame].triangles[triangle].verts[comp_vert][2])) + + { + // alright, we hit one!! + // stuff the relevant pointers in the normal array + normal_array[current_normal_entry].triangle = &poly_list[current_frame].triangles[triangle]; + normal_array[current_normal_entry].which_vert = comp_vert; + normal_array[current_normal_entry].can_use = 0; + current_normal_entry++; + } + } + } + } + } + } + } + // do we have any matches? + if (current_normal_entry) + { + // firstly, we need to go through all these new normals and see if any match, since if they do, we need to only use one iteration of it, + // otherwise it messes up the normal calculations + for (i=0; inormals[normal_array[i].which_vert][0] == normal_array[x].triangle->normals[normal_array[x].which_vert][0]) && + (normal_array[i].triangle->normals[normal_array[i].which_vert][1] == normal_array[x].triangle->normals[normal_array[x].which_vert][1]) && + (normal_array[i].triangle->normals[normal_array[i].which_vert][2] == normal_array[x].triangle->normals[normal_array[x].which_vert][2]))) + { + normal_array[x].can_use = 1; + } + } + } + + // yes, so we need to go through the normals, add them together and then re-normalise + for (i=0; i< current_normal_entry; i++) + { + if (normal_array[i].can_use) + { + main_tri->normals[current_vert][0] += normal_array[i].triangle->normals[normal_array[i].which_vert][0]; + main_tri->normals[current_vert][1] += normal_array[i].triangle->normals[normal_array[i].which_vert][1]; + main_tri->normals[current_vert][2] += normal_array[i].triangle->normals[normal_array[i].which_vert][2]; + } + } + + VectorNormalize( main_tri->normals[current_vert], main_tri->normals[current_vert] ); + // now that we have a new normal, lets stuff it in all the other verts that match, and flag them + for (i=0; i< current_normal_entry; i++) + { + VectorCopy(main_tri->normals[current_vert],normal_array[i].triangle->normals[normal_array[i].which_vert]); + normal_array[i].triangle->normal_recomputed[normal_array[i].which_vert]++; + } + main_tri->normal_recomputed[current_vert]++; + } +} + + +static int GetSurfaceAnimations( SurfaceAnimation_t sanims[MAX_ANIM_SURFACES], + const char *part, + int skipFrameStart, + int skipFrameEnd, + int maxFrames ) + +{ + int numSurfaces; + int numValidSurfaces; + int i, x, z; + int numFrames = -1; + polyset_t *poly_list; + + numSurfaces = ASE_GetNumSurfaces(); +/* if ( numSurfaces > MAX_ANIM_SURFACES ) + { + Error( "Too many surfaces in ASE (%d > %d)",numSurfaces, MAX_ANIM_SURFACES ); + } +*/ + for ( numValidSurfaces = 0, i = 0; i < numSurfaces; i++ ) + { + polyset_t *splitSets; + int numNewFrames; + const char *surfaceName = ASE_GetSurfaceName( i ); + + // clear out the valid frame marker; + sanims[i].valid_frame = 0; + + if ( !surfaceName ) + { + continue; +// Error( "Missing animation frames in model" ); + } + + if ( strstr( surfaceName, "tag_" ) || + !strcmp( part, "any" ) || + ( strstr( surfaceName, part ) == surfaceName ) ) + { + + // skip this if it's an inappropriate tag + if ( strcmp( part, "any" ) ) + { + // ignore non-"tag_head" or "tag_ear" tags if this is the head + if ( !strcmp( part, "h_" ) && strstr( surfaceName, "tag_" ) && (strcmp( surfaceName, "tag_head" ) && strcmp( surfaceName, "tag_ear" )) ) + continue; + + // ignore "tag_ear" if this is the torso + if ( !strcmp( part, "u_" ) && !strcmp( surfaceName, "tag_ear" ) ) + continue; + + // ignore "tag_ear" if this is the legs + if ( !strcmp( part, "l_" ) && !strcmp( surfaceName, "tag_ear" ) ) + continue; + // ignore "tag_head" if this is the legs + if ( !strcmp( part, "l_" ) && !strcmp( surfaceName, "tag_head" ) ) + continue; + // ignore "tag_weapon" if this is the legs + if ( !strcmp( part, "l_" ) && !strcmp( surfaceName, "tag_weapon" ) ) + continue; + + // ignore non-"tag_flash" if this is the flash + if ( !strcmp( part, "f_" ) && strstr( surfaceName, "tag_" ) && strcmp( surfaceName, "tag_flash" ) ) + continue; + // ignore non-"tag_flash" if this is the SECOND flash + if ( !strcmp( part, "f2_" ) && strstr( surfaceName, "tag_" ) && strcmp( surfaceName, "tag_flash" ) ) + continue; + // ignore non-"tag_flash" if this is the ALT flash + if ( !strcmp( part, "fa_" ) && strstr( surfaceName, "tag_" ) && strcmp( surfaceName, "tag_flash" ) ) + continue; + // ignore non-"tag_flash" if this is the SECOND ALT flash + if ( !strcmp( part, "fa2_" ) && strstr( surfaceName, "tag_" ) && strcmp( surfaceName, "tag_flash" ) ) + continue; + + // ignore non-"tag_barre" if this is the barrel + if ( !strcmp( part, "b_" ) && strstr( surfaceName, "tag_" ) && strcmp( surfaceName, "tag_barrel" ) ) + continue; + // ignore non-"tag_barrel2" if this is barrel 2 + if ( !strcmp( part, "b2_" ) && strstr( surfaceName, "tag_" ) && strcmp( surfaceName, "tag_barrel2" ) ) + continue; + // ignore non-"tag_barrel3" if this is barrel 3 + if ( !strcmp( part, "b3_" ) && strstr( surfaceName, "tag_" ) && strcmp( surfaceName, "tag_barrel3" ) ) + continue; + // ignore non-"tag_barrel4" if this is barrel 4 + if ( !strcmp( part, "b4_" ) && strstr( surfaceName, "tag_" ) && strcmp( surfaceName, "tag_barrel4" ) ) + continue; + + // ignore "tag_barrels" if this is the weapon + if ( !strcmp( part, "w_" ) && strstr( surfaceName, "tag_barrel" ) ) + continue; + } + + if ( numValidSurfaces >= MAX_ANIM_SURFACES ) + { + Error( "Too many surfaces in ASE (%d > %d)",numSurfaces, MAX_ANIM_SURFACES ); + } + + if ( ( sanims[numValidSurfaces].frames = ASE_GetSurfaceAnimation( i, &sanims[numValidSurfaces].numFrames, skipFrameStart, skipFrameEnd, maxFrames ) ) != 0 ) + { + splitSets = Polyset_SplitSets( sanims[numValidSurfaces].frames, sanims[numValidSurfaces].numFrames, &numNewFrames, g_data.maxSurfaceTris ); + + if ( numFrames == -1 ) + numFrames = sanims[numValidSurfaces].numFrames; + else if ( numFrames != sanims[numValidSurfaces].numFrames ) + Error( "Different number of animation frames on surfaces (%d != %d)",numFrames, sanims[numValidSurfaces].numFrames ); + + if ( sanims[numValidSurfaces].frames != splitSets ) + { + int j; + + // free old data if we split the surfaces + for ( j = 0; j < sanims[numValidSurfaces].numFrames; j++ ) + { + free( sanims[numValidSurfaces].frames[j].triangles ); + free( sanims[numValidSurfaces].frames ); + } + + sanims[numValidSurfaces].frames = splitSets; + sanims[numValidSurfaces].numFrames = numNewFrames; + } + if (!strstr( surfaceName, "tag_" ) ) { //let's not snap the tags since it causes wobbling. + Polyset_SnapSets( sanims[numValidSurfaces].frames, sanims[numValidSurfaces].numFrames ); + } + Polyset_ComputeNormals( sanims[numValidSurfaces].frames, sanims[numValidSurfaces].numFrames ); + + // this surface is valid + if (strstr( surfaceName, "h_")) + { + if (sanims[numValidSurfaces].frames) + { + sanims[numValidSurfaces].valid_frame++; + } + } + + numValidSurfaces++; + } + } + } + + // for each frame, scan through all the surfaces, starting with the first one, comparing each vertex X, Y and Z to all vertieces in the successive surfaces + // if we hit and match, and the normal has NOT already been recomputed (which would mean that it has already been matched by a previous comparision earlier + // in this loop) then add the pointer to this triangle to a list. Once all surfaces have been traversed for the comparing vertex, re-compute the normals + // for each entry. Then set the re-computed normal flag so this vertex is not looked at again. Or something like that. + + // now scan through each surface, looking for + for ( x = 0; x < sanims[0].numFrames; x++) + { + // no need to compare the last surface against anything, since it would only be comparing against the next surface anyway, and there won't be one + // if its the last one. + for ( i = 0; i < numSurfaces-1; i++ ) + { + // if we are looking at a surface thats actually valid... + if (sanims[i].valid_frame) + { + poly_list = sanims[i].frames; + // scan through its triangles array + for (z = 0; z < poly_list[x].numtriangles; z++) + { + // for each vert in the poly + int current_vert; + for (current_vert = 0; current_vert < 3; current_vert++) + { + // has this vertex already had its normal re-computered? Cos if it has, it's already been matched on a previous + // comparision through this loop + if (!(poly_list[x].triangles[z].normal_recomputed[current_vert])) + { + // ok, we found a triangle to compare all the rest from this point on against. + Recompute_Normals(sanims, x, i, z, current_vert, numSurfaces); + } + } + } + } + } + } + + return numValidSurfaces; +} + +static int SurfaceOrderToFrameOrder( SurfaceAnimation_t sanims[], ObjectAnimationFrame_t oanims[], int numSurfaces ) +{ + int i, s; + int numFrames = -1; + + /* + ** we have the data here arranged in surface order, now we need to convert it to + ** frame order + */ + for ( i = 0, s = 0; i < numSurfaces; i++ ) + { + int j; + + if ( sanims[i].frames ) + { + if ( numFrames == -1 ) + numFrames = sanims[i].numFrames; + else if ( numFrames != sanims[i].numFrames ) + Error( "numFrames != sanims[i].numFrames (%d != %d)\n", numFrames, sanims[i].numFrames ); + + for ( j = 0; j < sanims[i].numFrames; j++ ) + { + oanims[j].surfaces[s] = &sanims[i].frames[j]; + oanims[j].numSurfaces = numSurfaces; + } + s++; + } + } + + return numFrames; +} + +static void WriteMD3( const char *_filename, ObjectAnimationFrame_t oanims[], int numFrames ) +{ + char filename[MAXNAME]; + + strcpy( filename, _filename ); + if ( strchr( filename, '.' ) ) + *strchr( filename, '.' ) = 0; + strcat( filename, ".md3" ); +} + +static void BuildAnimationFromOAFs( const char *filename, ObjectAnimationFrame_t oanims[], int numFrames, int type ) +{ + int f, i, j, tagcount; + float *frameXyz; + float *frameNormals; + + g_data.model.numSurfaces = oanims[0].numSurfaces; + g_data.model.numFrames = numFrames; + if ( g_data.model.numFrames < 0) + Error ("model.numFrames < 0"); + if ( g_data.model.numFrames >= MD3_MAX_FRAMES) + Error ("model.numFrames >= MD3_MAX_FRAMES"); + if ( g_data.model.numSurfaces >= MD3_MAX_SURFACES) + Error ("model.numSurfaces >= MD3_MAX_SURFACES"); + + // build base frame + BuildBaseFrame( filename, &oanims[0] ); + + // build animation frames + for ( f = 0; f < numFrames; f++ ) + { + ObjectAnimationFrame_t *pOAF = &oanims[f]; + qboolean parentTagExists = qfalse; + md3Tag_t tagParent; + int numtags = 0; + md3Frame_t *fr; + + fr = &g_data.frames[f]; + + strcpy( fr->name, "(from ASE)" ); + + // scale and adjust frame + for ( i = 0; i < pOAF->numSurfaces; i++ ) + { + triangle_t *pTris = pOAF->surfaces[i]->triangles; + int t; + + for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ ) + { + for ( j = 0; j < 3; j++ ) + { + int k; + + // scale and adjust + for ( k = 0 ; k < 3 ; k++ ) { + pTris[t].verts[j][k] = pTris[t].verts[j][k] * g_data.scale_up + + g_data.aseAdjust[k]; + + if ( pTris[t].verts[j][k] > 1023 || + pTris[t].verts[j][k] < -1023 ) + { + Error( "Model extents too large" ); + } + } + } + } + } + + // + // find and count tags, locate parent tag + // + for ( i = 0; i < pOAF->numSurfaces; i++ ) + { + if ( strstr( pOAF->surfaces[i]->name, "tag_" ) != pOAF->surfaces[i]->name ) + { + // not a tag, make sure the surface name matches + if ( strcmp( pOAF->surfaces[i]->name, g_data.surfData[i].header.name ) ) + { + Error( "Mismatched surfaces from base('%s') to frame('%s') in model '%s'\n", g_data.surfData[i].header.name, pOAF->surfaces[i]->name, filename ); + } + continue; + } + // ignore any non flash tags when grabbing a weapon model and this is the flash portion + if ( strcmp( pOAF->surfaces[i]->name, "tag_flash" ) && strstr( filename, "_flash" ) ) + { + continue; + } + // ignore parent tags when grabbing a weapon model and this is the barrel file + if ( !strcmp( pOAF->surfaces[i]->name, "tag_parent" ) && strstr( filename, "_barrel" ) ) + { + continue; + } + + // see if this should be the parent for this set of surfaces + if ( !strstr( filename, "_hand.md3" ) && ( + ( !strcmp( pOAF->surfaces[i]->name, "tag_parent" ) ) || + ( !strcmp( pOAF->surfaces[i]->name, "tag_torso" ) && strstr( filename, "upper" ) ) || + ( !strcmp( pOAF->surfaces[i]->name, "tag_head" ) && strstr( filename, "head" ) ) || + ( !strcmp( pOAF->surfaces[i]->name, "tag_flash" ) && strstr( filename, "flash" ) ) || + ( !strcmp( pOAF->surfaces[i]->name, "tag_barrel" ) && strstr( filename, "barrel." ) ) || + ( !strcmp( pOAF->surfaces[i]->name, "tag_barrel2" ) && strstr( filename, "barrel2" ) ) || + ( !strcmp( pOAF->surfaces[i]->name, "tag_barrel3" ) && strstr( filename, "barrel3" ) ) || + ( !strcmp( pOAF->surfaces[i]->name, "tag_barrel4" ) && strstr( filename, "barrel4" ) ) || + ( !strcmp( pOAF->surfaces[i]->name, "tag_weapon" ) && type == TYPE_WEAPON ) ) ) + { + float tri[3][3]; + + if ( parentTagExists ) + Error( "Multiple parent tags not allowed" ); + + memcpy( tri[0], pOAF->surfaces[i]->triangles[0].verts[0], sizeof( float ) * 3 ); + memcpy( tri[1], pOAF->surfaces[i]->triangles[0].verts[1], sizeof( float ) * 3 ); + memcpy( tri[2], pOAF->surfaces[i]->triangles[0].verts[2], sizeof( float ) * 3 ); + + memset(tagParent.name,0,sizeof(tagParent.name)); + strcpy( tagParent.name, "tag_parent" ); + MD3_ComputeTagFromTri( &tagParent, tri ); + g_data.tags[f][numtags] = tagParent; + parentTagExists = qtrue; + } + else + { + float tri[3][3]; + + // this is just another tag we will store in the model for attaching other things + memcpy( tri[0], pOAF->surfaces[i]->triangles[0].verts[0], sizeof( float ) * 3 ); + memcpy( tri[1], pOAF->surfaces[i]->triangles[0].verts[1], sizeof( float ) * 3 ); + memcpy( tri[2], pOAF->surfaces[i]->triangles[0].verts[2], sizeof( float ) * 3 ); + + sprintf( g_data.tags[f][numtags].name,"%s frame %d", pOAF->surfaces[i]->name, f ); //DEBUG INFO passed in via name + MD3_ComputeTagFromTri( &g_data.tags[f][numtags], tri ); + strcpy( g_data.tags[f][numtags].name, pOAF->surfaces[i]->name ); //reset name + if ( strstr( g_data.tags[f][numtags].name, "tag_flash" ) ) + * ( strstr( g_data.tags[f][numtags].name, "tag_flash" ) + strlen( "tag_flash" ) ) = 0; + } + if ( numtags >= MD3_MAX_TAGS) + Error ("numtags >= MD3_MAX_TAGS"); + numtags++; + } + + + if ( numtags != g_data.model.numTags ) + { + Error( "mismatched number of tags in frame(%d) vs. base(%d)", numtags, g_data.model.numTags ); + } + + // + // prepare to accumulate bounds and normals + // + ClearBounds( fr->bounds[0], fr->bounds[1] ); + + // + // store the frame's vertices in the same order as the base. This assumes the + // triangles and vertices in this frame are in exactly the same order as in the + // base + // + for ( i = 0, tagcount = 0; i < pOAF->numSurfaces; i++ ) + { + int t; + triangle_t *pTris = pOAF->surfaces[i]->triangles; + + // + // parent tag adjust + // + if ( parentTagExists ) + { + for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ ) + { + for ( j = 0; j < 3 ; j++ ) + { + vec3_t tmp; + + VectorSubtract( pTris[t].verts[j], tagParent.origin, tmp ); + + pTris[t].verts[j][0] = DotProduct( tmp, tagParent.axis[0] ); + pTris[t].verts[j][1] = DotProduct( tmp, tagParent.axis[1] ); + pTris[t].verts[j][2] = DotProduct( tmp, tagParent.axis[2] ); + + VectorCopy( pTris[t].normals[j], tmp ); + pTris[t].normals[j][0] = DotProduct( tmp, tagParent.axis[0] ); + pTris[t].normals[j][1] = DotProduct( tmp, tagParent.axis[1] ); + pTris[t].normals[j][2] = DotProduct( tmp, tagParent.axis[2] ); + } + } + } + + // + // compute tag data + // + if ( strstr( pOAF->surfaces[i]->name, "tag_" ) == pOAF->surfaces[i]->name ) + { + md3Tag_t *pTag = &g_data.tags[f][tagcount]; + float tri[3][3]; + + strcpy( pTag->name, pOAF->surfaces[i]->name ); + + memcpy( tri[0], pTris[0].verts[0], sizeof( float ) * 3 ); + memcpy( tri[1], pTris[0].verts[1], sizeof( float ) * 3 ); + memcpy( tri[2], pTris[0].verts[2], sizeof( float ) * 3 ); + + MD3_ComputeTagFromTri( pTag, tri ); + tagcount++; + } + else + { + if ( g_data.surfData[i].verts[f] ) + free( g_data.surfData[i].verts[f] ); + frameXyz = g_data.surfData[i].verts[f] = calloc( 1, sizeof( float ) * 6 * g_data.surfData[i].header.numVerts ); + frameNormals = frameXyz + 3; + + for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ ) + { + for ( j = 0; j < 3 ; j++ ) + { + int index; + + index = g_data.surfData[i].baseTriangles[t].v[j].index; + frameXyz[index*6+0] = pTris[t].verts[j][0]; + frameXyz[index*6+1] = pTris[t].verts[j][1]; + frameXyz[index*6+2] = pTris[t].verts[j][2]; + frameNormals[index*6+0] = pTris[t].normals[j][0]; + frameNormals[index*6+1] = pTris[t].normals[j][1]; + frameNormals[index*6+2] = pTris[t].normals[j][2]; + AddPointToBounds (&frameXyz[index*6], fr->bounds[0], fr->bounds[1] ); + } + } + } + } + } + + if ( strstr( filename, gamedir + 1 ) ) // if gamedir exists within the filename... + { + strcpy( g_modelname, strstr( filename, gamedir + 1 ) + strlen( gamedir ) - 1 ); // ... then we need to remove it so we've got a local name + } + else + { + strcpy( g_modelname, filename ); + } + + FinishModel( type ); + ClearModel(); +} + +static void ConvertASE( const char *filename, int type, qboolean grabAnims, qboolean bInternal) +{ + int i, j; + int numSurfaces; + int numFrames = -1; + SurfaceAnimation_t surfaceAnimations[MAX_ANIM_SURFACES]; + ObjectAnimationFrame_t objectAnimationFrames[MAX_ANIM_FRAMES]; + char outfilename[MAXNAME]; + + if (bInternal) + { + /* + ** load from aseGrab rather than from a file + */ + ASE_CopyFromConcatBuffer(); + } + else + { + /* + ** load ASE into memory + */ + ASE_Load( filename, g_verbose, grabAnims, type ); + } + + /* + ** process parts + */ + if ( type == TYPE_ITEM || type == TYPE_GHOUL2_1FRAME) + { + numSurfaces = GetSurfaceAnimations( surfaceAnimations, "any", -1, -1, -1 ); + numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces ); + + if (type == TYPE_GHOUL2_1FRAME && numFrames>1) + { + printf("Warning: Model \"%s\" has %d frames, but you're requesting a ghoul2 single-frame model\n\n..... extra frames will therefore be ignored.\n",filename); + } + + strcpy( outfilename, filename ); + if ( strrchr( outfilename, '.' ) ) + *( strrchr( outfilename, '.' ) + 1 ) = 0; + strcat( outfilename, (type == TYPE_GHOUL2_1FRAME)?"glm":"md3" ); + BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type ); + + // free memory + for ( i = 0; i < numSurfaces; i++ ) + { + if ( surfaceAnimations[i].frames ) + { + for ( j = 0; j < surfaceAnimations[i].numFrames; j++ ) + { + free( surfaceAnimations[i].frames[j].triangles ); + } + free( surfaceAnimations[i].frames ); + surfaceAnimations[i].frames = 0; + } + } + } + else if ( type == TYPE_PLAYER ) + { + qboolean tagTorso = qfalse; + qboolean tagHead = qfalse; + qboolean tagWeapon = qfalse; + + // + // verify that all necessary tags exist + // + numSurfaces = ASE_GetNumSurfaces(); + for ( i = 0; i < numSurfaces; i++ ) + { + const char *surfaceName; + + surfaceName = ASE_GetSurfaceName( i ); + if ( !surfaceName ) { + continue; + } + if ( !strcmp( surfaceName, "tag_head" ) ) + { + tagHead = qtrue; + } + if ( !strcmp( surfaceName, "tag_torso" ) ) + { + tagTorso = qtrue; + } + if ( !strcmp( surfaceName, "tag_weapon" ) ) + { + tagWeapon = qtrue; + } + } + + if ( !tagHead ) + { + Error( "Missing tag_Head!" ); + } + if ( !tagTorso ) + { + Error( "Missing tag_torso!" ); + } + if ( !tagWeapon ) + { + Error( "Missing tag_weapon!" ); + } + + // get all upper body surfaces + numSurfaces = GetSurfaceAnimations( surfaceAnimations, "u_", -1, -1, g_data.maxUpperFrames ); + numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces ); + strcpy( outfilename, filename ); + if ( strrchr( outfilename, '/' ) ) + *( strrchr( outfilename, '/' ) + 1 ) = 0; + + if ( g_data.currentLod == 0 ) + { + strcat( outfilename, "upper.md3" ); + } + else + { + char temp[128]; + + sprintf( temp, "upper_%d.md3", g_data.currentLod ); + strcat( outfilename, temp ); + } + + BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type ); + + // free memory + for ( i = 0; i < numSurfaces; i++ ) + { + if ( surfaceAnimations[i].frames ) + { + for ( j = 0; j < surfaceAnimations[i].numFrames; j++ ) + { + free( surfaceAnimations[i].frames[j].triangles ); + } + free( surfaceAnimations[i].frames ); + surfaceAnimations[i].frames = 0; + } + } + + // get lower body surfaces + numSurfaces = GetSurfaceAnimations( surfaceAnimations, "l_", g_data.lowerSkipFrameStart, g_data.lowerSkipFrameEnd, -1 ); + numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces ); + strcpy( outfilename, filename ); + if ( strrchr( outfilename, '/' ) ) + *( strrchr( outfilename, '/' ) + 1 ) = 0; + + if ( g_data.currentLod == 0 ) + { + strcat( outfilename, "lower.md3" ); + } + else + { + char temp[128]; + + sprintf( temp, "lower_%d.md3", g_data.currentLod ); + strcat( outfilename, temp ); + } + BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type ); + + // free memory + for ( i = 0; i < numSurfaces; i++ ) + { + if ( surfaceAnimations[i].frames ) + { + for ( j = 0; j < surfaceAnimations[i].numFrames; j++ ) + { + free( surfaceAnimations[i].frames[j].triangles ); + } + free( surfaceAnimations[i].frames ); + surfaceAnimations[i].frames = 0; + } + } + + // get head surfaces + numSurfaces = GetSurfaceAnimations( surfaceAnimations, "h_", -1, -1, g_data.maxHeadFrames ); + numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces ); + strcpy( outfilename, filename ); + if ( strrchr( outfilename, '/' ) ) + *( strrchr( outfilename, '/' ) + 1 ) = 0; + + if ( g_data.currentLod == 0 ) + { + strcat( outfilename, "head.md3" ); + } + else + { + char temp[128]; + + sprintf( temp, "head_%d.md3", g_data.currentLod ); + strcat( outfilename, temp ); + } + BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type ); + + // free memory + for ( i = 0; i < numSurfaces; i++ ) + { + if ( surfaceAnimations[i].frames ) + { + for ( j = 0; j < surfaceAnimations[i].numFrames; j++ ) + { + free( surfaceAnimations[i].frames[j].triangles ); + } + free( surfaceAnimations[i].frames ); + surfaceAnimations[i].frames = 0; + } + } + } + else if ( type == TYPE_WEAPON ) + { + int zx; + // get the weapon surfaces + numSurfaces = GetSurfaceAnimations( surfaceAnimations, "w_", -1, -1, -1 ); + numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces ); + + strcpy( outfilename, filename ); + if ( strrchr( outfilename, '.' ) ) + *( strrchr( outfilename, '.' ) + 1 ) = 0; + strcat( outfilename, "md3" ); + BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type ); + + // free memory + for ( i = 0; i < numSurfaces; i++ ) + { + if ( surfaceAnimations[i].frames ) + { + for ( j = 0; j < surfaceAnimations[i].numFrames; j++ ) + { + free( surfaceAnimations[i].frames[j].triangles ); + } + free( surfaceAnimations[i].frames ); + surfaceAnimations[i].frames = 0; + } + } + + // get the flash surfaces + numSurfaces = GetSurfaceAnimations( surfaceAnimations, "f_", -1, -1, -1 ); + if (numSurfaces>1) {//one for the tag_flash, and at least 1 surf + numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces ); + + strcpy( outfilename, filename ); + if ( strrchr( outfilename, '.' ) ) + *strrchr( outfilename, '.' ) = 0; + strcat( outfilename, "_flash.md3" ); + BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, TYPE_ITEM ); + // free memory + for ( i = 0; i < numSurfaces; i++ ) + { + if ( surfaceAnimations[i].frames ) + { + for ( j = 0; j < surfaceAnimations[i].numFrames; j++ ) + { + free( surfaceAnimations[i].frames[j].triangles ); + } + free( surfaceAnimations[i].frames ); + surfaceAnimations[i].frames = 0; + } + } + } + + // get the SECOND flash surfaces + numSurfaces = GetSurfaceAnimations( surfaceAnimations, "f2_", -1, -1, -1 ); + if (numSurfaces>1) {//one for the tag_flash, and at least 1 surf + numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces ); + + strcpy( outfilename, filename ); + if ( strrchr( outfilename, '.' ) ) + *strrchr( outfilename, '.' ) = 0; + strcat( outfilename, "_flash2.md3" ); + BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, TYPE_ITEM ); + + // free memory + for ( i = 0; i < numSurfaces; i++ ) + { + if ( surfaceAnimations[i].frames ) + { + for ( j = 0; j < surfaceAnimations[i].numFrames; j++ ) + { + free( surfaceAnimations[i].frames[j].triangles ); + } + free( surfaceAnimations[i].frames ); + surfaceAnimations[i].frames = 0; + } + } + } + // get the ALT flash surfaces + numSurfaces = GetSurfaceAnimations( surfaceAnimations, "fa_", -1, -1, -1 ); + if (numSurfaces>1) {//one for the tag_flash, and at least 1 surf + numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces ); + + strcpy( outfilename, filename ); + if ( strrchr( outfilename, '.' ) ) + *strrchr( outfilename, '.' ) = 0; + strcat( outfilename, "_flasha.md3" ); + BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, TYPE_ITEM ); + + // free memory + for ( i = 0; i < numSurfaces; i++ ) + { + if ( surfaceAnimations[i].frames ) + { + for ( j = 0; j < surfaceAnimations[i].numFrames; j++ ) + { + free( surfaceAnimations[i].frames[j].triangles ); + } + free( surfaceAnimations[i].frames ); + surfaceAnimations[i].frames = 0; + } + } + } + + // get the SECOND ALT flash surfaces + numSurfaces = GetSurfaceAnimations( surfaceAnimations, "fa2_", -1, -1, -1 ); + if (numSurfaces>1) { //one for the tag_flash, and at least 1 surf + numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces ); + + strcpy( outfilename, filename ); + if ( strrchr( outfilename, '.' ) ) + *strrchr( outfilename, '.' ) = 0; + strcat( outfilename, "_flasha2.md3" ); + BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, TYPE_ITEM ); + + // free memory + for ( i = 0; i < numSurfaces; i++ ) + { + if ( surfaceAnimations[i].frames ) + { + for ( j = 0; j < surfaceAnimations[i].numFrames; j++ ) + { + free( surfaceAnimations[i].frames[j].triangles ); + } + free( surfaceAnimations[i].frames ); + surfaceAnimations[i].frames = 0; + } + } + } + + // get the barrel surfaces + numSurfaces = GetSurfaceAnimations( surfaceAnimations, "b_", -1, -1, -1 ); + if (numSurfaces>1) { //one for the tag_barrel, and at least 1 surf + numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces ); + + strcpy( outfilename, filename ); + if ( strrchr( outfilename, '.' ) ) + *strrchr( outfilename, '.' ) = 0; + + if ( g_data.currentLod == 0 ) + { + strcat( outfilename, "_barrel.md3" ); + } + else + { + char temp[128]; + + sprintf( temp, "_barrel_%d.md3", g_data.currentLod ); + strcat( outfilename, temp ); + } + + BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, TYPE_ITEM ); + // free memory + for ( i = 0; i < numSurfaces; i++ ) + { + if ( surfaceAnimations[i].frames ) + { + for ( j = 0; j < surfaceAnimations[i].numFrames; j++ ) + { + free( surfaceAnimations[i].frames[j].triangles ); + } + free( surfaceAnimations[i].frames ); + surfaceAnimations[i].frames = 0; + } + } + + } + + // now create a bunch of these barrel models + for (zx = 2; zx < 5; zx++) + { + char temp[128]; + // get the barrel surfaces + sprintf( temp, "b%d_", zx); + numSurfaces = GetSurfaceAnimations( surfaceAnimations, temp, -1, -1, -1 ); + + // if there is no surfaces, then skip on to the next one - don't create an empty model file + if ( numSurfaces > 1) { //at least one tag and one surf + numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces ); + + strcpy( outfilename, filename ); + if ( strrchr( outfilename, '.' ) ) + *strrchr( outfilename, '.' ) = 0; + + if ( g_data.currentLod == 0 ) + { + sprintf( temp, "_barrel%d.md3", zx ); + strcat( outfilename, temp ); + } + else + { + + sprintf( temp, "_barrel%d_%d.md3", zx, g_data.currentLod ); + strcat( outfilename, temp ); + } + + BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, TYPE_ITEM ); + + // free memory + for ( i = 0; i < numSurfaces; i++ ) + { + if ( surfaceAnimations[i].frames ) + { + for ( j = 0; j < surfaceAnimations[i].numFrames; j++ ) + { + free( surfaceAnimations[i].frames[j].triangles ); + } + free( surfaceAnimations[i].frames ); + surfaceAnimations[i].frames = 0; + } + } + } + } + } + else if ( type == TYPE_HAND ) + { + // get the hand tags + numSurfaces = GetSurfaceAnimations( surfaceAnimations, "tag_", -1, -1, -1 ); + numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces ); + + strcpy( outfilename, filename ); + if ( strrchr( outfilename, '.' ) ) + *strrchr( outfilename, '.' ) = 0; + strcat( outfilename, "_hand.md3" ); + BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, TYPE_HAND ); + + // free memory + for ( i = 0; i < numSurfaces; i++ ) + { + if ( surfaceAnimations[i].frames ) + { + for ( j = 0; j < surfaceAnimations[i].numFrames; j++ ) + { + free( surfaceAnimations[i].frames[j].triangles ); + } + free( surfaceAnimations[i].frames ); + surfaceAnimations[i].frames = 0; + } + } + } + else + { + Error( "Unknown type passed to ConvertASE()" ); + } + + g_data.currentLod = 0; + g_data.lodBias = 0; + g_data.maxHeadFrames = 0; + g_data.maxUpperFrames = 0; + g_data.lowerSkipFrameStart = 0; + g_data.lowerSkipFrameEnd = 0; + VectorCopy( vec3_origin, g_data.aseAdjust ); + + // unload ASE from memory + ASE_Free(); +} diff --git a/utils/q3data/models.h b/utils/q3data/models.h new file mode 100644 index 0000000..a44ba4b --- /dev/null +++ b/utils/q3data/models.h @@ -0,0 +1,114 @@ +// Filename:- models.h +// +// removed from .c file so I could get at this stuff for the ghoul2 exporter -ste +// + + +#ifndef MODELS_H +#define MODELS_H + + +#ifdef __cplusplus +extern "C" +{ +#endif + + #define MAX_SURFACE_TRIS (SHADER_MAX_INDEXES / 3) + #define MAX_SURFACE_VERTS SHADER_MAX_VERTEXES + + #define MD3_TYPE_UNKNOWN 0 + #define MD3_TYPE_BASE3DS 1 + #define MD3_TYPE_SPRITE 2 + #define MD3_TYPE_ASE 3 + + //#define MAX_ANIM_FRAMES 1024 + #define MAX_ANIM_FRAMES 1280 + + #define MAX_ANIM_SURFACES 32 + 32 + + typedef struct + { + polyset_t *frames; + int numFrames; + int valid_frame; + } SurfaceAnimation_t; + + typedef struct + { + polyset_t *surfaces[MAX_ANIM_SURFACES]; + int numSurfaces; + } ObjectAnimationFrame_t; + + typedef struct { + vec3_t xyz; + vec3_t normal; + vec3_t color; + float st[2]; + int index; + } baseVertex_t; + + typedef struct { + baseVertex_t v[3]; + } baseTriangle_t; + + //================================================================ + + typedef struct + { + md3Surface_t header; + md3Shader_t shaders[MD3_MAX_SHADERS]; + // all verts (xyz_normal) + float *verts[MD3_MAX_FRAMES]; + + baseTriangle_t baseTriangles[MD3_MAX_TRIANGLES]; + + // the triangles will be sorted so that they form long generalized tristrips + int orderedTriangles[MD3_MAX_TRIANGLES][3]; + int lodTriangles[MD3_MAX_TRIANGLES][3]; + baseVertex_t baseVertexes[MD3_MAX_VERTS]; + + } md3SurfaceData_t; + + typedef struct + { + int skinwidth, skinheight; + + md3SurfaceData_t surfData[MD3_MAX_SURFACES]; + + md3Tag_t tags[MD3_MAX_FRAMES][MD3_MAX_TAGS]; + md3Frame_t frames[MD3_MAX_FRAMES]; + + md3Header_t model; + float scale_up; // set by $scale + vec3_t adjust; // set by $origin + vec3_t aseAdjust; + int fixedwidth, fixedheight; // set by $skinsize + + int maxSurfaceTris; + + int lowerSkipFrameStart, lowerSkipFrameEnd; + int maxUpperFrames; + int maxHeadFrames; + int currentLod; + float lodBias; + + int type; // MD3_TYPE_BASE, MD3_TYPE_OLDBASE, MD3_TYPE_ASE, or MD3_TYPE_SPRITE + + } q3data; + + + extern q3data g_data; + + +#ifdef __cplusplus +} +#endif + +#endif // #ifndef MODELS_H + +////////////////////// eof ///////////////////// + + + + + diff --git a/utils/q3data/oldstuff.c b/utils/q3data/oldstuff.c new file mode 100644 index 0000000..2444f95 --- /dev/null +++ b/utils/q3data/oldstuff.c @@ -0,0 +1,130 @@ +#if 0 + +/* +** ReindexTriangle +** +** Given a triangle_t, find which indices match into the associated +** surface's base triangles. +*/ +static void ReindexTriangle( int surfno, triangle_t *pTri, int indices[3] ) +{ + int t, i; + md3SurfaceData_t *pSurfData = &g_data.surfData[surfno]; + int matches[3][3]; + int numMatches = 0; + + + indices[0] = -1; + indices[1] = -1; + indices[2] = -1; + + for ( i = 0; i < 3; i++ ) + { + numMatches = 0; + + matches[i][0] = -1; + matches[i][1] = -1; + matches[i][2] = -1; + + for ( t = 0; t < pSurfData->header.numVerts; t++ ) + { + if ( !VectorCompare( pTri->verts[i], pSurfData->baseVertexes[t].xyz ) ) + continue; + +/* + if ( !VectorCompare( pTri->normals[i], pSurfData->baseVertexes[t].normal ) ) + continue; + if ( pTri->texcoords[i][0] != pSurfData->baseVertexes[t].st[0] ) + continue; + if ( pTri->texcoords[i][1] != pSurfData->baseVertexes[t].st[1] ) + continue; +*/ + + matches[i][numMatches++] = t; + } + + if ( indices[i] == -1 ) + { +// Error( "Could not ReindexTriangle, vertex not found" ); + } + } + +#if 0 + for ( t = 0; t < psets[i].numtriangles; t++ ) + { + int b; + + bTri = &g_data.surfData[i].baseTriangles[t]; + + for (j=0 ; j<3 ; j++) + { + bVert = &bTri->v[j]; + + // get the xyz index + for ( k = 0; k < g_data.surfData[i].header.numVerts; k++ ) + { + if ( ( g_data.surfData[i].baseVertexes[k].st[0] == bVert->st[0] ) && + ( g_data.surfData[i].baseVertexes[k].st[1] == bVert->st[1] ) && + ( VectorCompare (bVert->xyz, g_data.surfData[i].baseVertexes[k].xyz) ) && + ( VectorCompare (bVert->normal, g_data.surfData[i].baseVertexes[k].normal) ) ) + { + break; // this vertex is already in the base vertex list + } + } + + if (k == g_data.surfData[i].header.numVerts) { // new index + g_data.surfData[i].baseVertexes[g_data.surfData[i].header.numVerts] = *bVert; + g_data.surfData[i].header.numVerts++; + } + + bVert->index = k; + } + } +#endif +} + +const char *FindFrameFile (const char *frame) +{ + int time1; + char file1[1024]; + static char retname[1024]; + char base[32]; + char suffix[32]; + const char *s; + + if (strstr (frame, ".")) + return frame; // allready in dot format + + // split 'run1' into 'run' and '1' + s = frame + strlen(frame)-1; + + while (s != frame && *s >= '0' && *s <= '9') + s--; + + strcpy (suffix, s+1); + strcpy (base, frame); + base[s-frame+1] = 0; + + // check for 'run1.tri' + sprintf (file1, "%s/%s%s.tri", g_cddir, base, suffix); + time1 = FileTime (file1); + if (time1 != -1) + { + sprintf (retname, "%s%s.tri", base, suffix); + return retname; + } + + // check for 'run.1' + sprintf (file1, "%s/%s.%s",g_cddir, base, suffix); + time1 = FileTime (file1); + if (time1 != -1) + { + sprintf (retname, "%s.%s", base, suffix); + return retname; + } + + Error ("frame %s could not be found",frame); + return NULL; +} + +#endif diff --git a/utils/q3data/p3dlib.c b/utils/q3data/p3dlib.c new file mode 100644 index 0000000..a936fad --- /dev/null +++ b/utils/q3data/p3dlib.c @@ -0,0 +1,317 @@ +#include "p3dlib.h" + +#include +#include +#include +#include + +#define MAX_POLYSETS 64 + +typedef struct +{ + long len; + + int numPairs; + char polysetNames[MAX_POLYSETS][256]; + char shaders[MAX_POLYSETS][256]; + + char *buffer, *curpos; +} p3d_t; + +static p3d_t p3d; + +static int P3DProcess(); +static int P3DGetToken( int restOfLine ); + +static char s_token[1024]; +static int s_curpair; + +/* +** P3DLoad +** +*/ +int P3DLoad( const char *filename ) +{ + FILE *fp = fopen( filename, "rb" ); + + if ( !fp ) + return 0; + + memset( &p3d, 0, sizeof( p3d ) ); + + p3d.len = filelength( fileno( fp ) ); + + p3d.curpos = p3d.buffer = malloc( p3d.len ); + + if ( fread( p3d.buffer, p3d.len, 1, fp ) != 1 ) + { + fclose( fp ); + return 0; + } + + fclose( fp ); + + return P3DProcess(); +} + +/* +** P3DClose +** +*/ +void P3DClose() +{ + if ( p3d.buffer ) + { + free( p3d.buffer ); + p3d.buffer = 0; + } +} + +int CharIsTokenDelimiter( int ch ) +{ + if ( ch <= 32 ) + return 1; + return 0; +} + +int P3DSkipToToken( const char *name ) +{ + while ( P3DGetToken( 0 ) ) + { + if ( !_strcmpi( s_token, name ) ) + return 1; + } + + return 0; +} + +/* +** P3DGetToken +** +*/ +int P3DGetToken( int restOfLine ) +{ + int i = 0; + + if ( p3d.buffer == 0 ) + return 0; + + if ( ( p3d.curpos - p3d.buffer ) == p3d.len ) + return 0; + + // skip over crap + while ( ( ( p3d.curpos - p3d.buffer ) < p3d.len ) && + ( *p3d.curpos <= 32 ) ) + { + p3d.curpos++; + } + + while ( ( p3d.curpos - p3d.buffer ) < p3d.len ) + { + s_token[i] = *p3d.curpos; + + p3d.curpos++; + i++; + + if ( ( CharIsTokenDelimiter( s_token[i-1] ) && !restOfLine ) || + ( ( s_token[i-1] == '\n' ) ) ) + { + s_token[i-1] = 0; + break; + } + } + + s_token[i] = 0; + + return 1; +} + +int P3DGetNextPair( char **psetName, char **associatedShader ) +{ + if ( s_curpair < p3d.numPairs ) + { + *psetName = p3d.polysetNames[s_curpair]; + *associatedShader = p3d.shaders[s_curpair]; + s_curpair++; + return 1; + } + + return 0; +} + +int P3DSkipToTokenInBlock( const char *name ) +{ + int iLevel = 0; + + while ( P3DGetToken( 0 ) ) + { + if ( !_strcmpi( s_token, "}" ) ) + iLevel--; + else if ( !_strcmpi( s_token, "{" ) ) + iLevel++; + + if ( !_strcmpi( s_token, name ) ) + return 1; + + if ( iLevel == 0 ) + { + return 0; + } + } + + return 0; +} + +/* +** P3DProcess +** +** Nothing fancy here. +*/ +int P3DProcess() +{ + + s_curpair = 0; + + // first token should be a string + P3DGetToken( 1 ); // Voodoo Ascii File + + // skip to the first Obj declaration + while ( P3DGetToken( 0 ) ) + { + if ( !_strcmpi( s_token, "Obj" ) ) + { + int j = 0, k = 0; + + if ( P3DSkipToToken( "Text" ) ) + { + if ( P3DSkipToTokenInBlock( "TMap" ) ) + { + char *p; + + if ( !P3DSkipToToken( "Path" ) ) + return 0; + + if ( !P3DGetToken( 1 ) ) + return 0; + + while ( s_token[j] != 0 ) + { + if ( s_token[j] == '\\' ) + { + j++; + p3d.shaders[p3d.numPairs][k] = '/'; + } + else + { + p3d.shaders[p3d.numPairs][k] = s_token[j]; + } + j++; + k++; + } + p3d.shaders[p3d.numPairs][k] = 0; + + // + // strip off any explicit extensions + // + if ( ( p = strrchr( p3d.shaders[p3d.numPairs], '/' ) ) != 0 ) + { + while ( *p ) + { + if ( *p == '.' ) + { + *p = 0; + break; + } + p++; + } + } + + // + // skip to the end of the Object and grab its name + // + if ( !P3DSkipToToken( "Name" ) ) + return 0; + + if ( P3DGetToken( 0 ) ) + { + // strip off leading 'Obj_' if it exists + if ( strstr( s_token, "Obj_" ) == s_token ) + strcpy( p3d.polysetNames[p3d.numPairs], s_token + strlen( "Obj_" ) ); + else + strcpy( p3d.polysetNames[p3d.numPairs], s_token ); + + // strip off trailing unused color information +// if ( strrchr( p3d.polysetNames[p3d.numPairs], '_' ) != 0 ) +// *strrchr( p3d.polysetNames[p3d.numPairs], '_' ) = 0; + + p3d.numPairs++; + } + else + { + return 0; + } + } + } + } + } + + s_curpair = 0; + + return 1; +} + +#if 0 +void SkinFromP3D( const char *file ) +{ + char filename[1024]; + char *psetName, *associatedShader; + + /* + ** a P3D file contains a list of polysets, each with a list of associated + ** texture names that constitute it's + ** + ** Thus: + ** + ** P3D file -> skin + ** polyset -> polyset + ** texture -> texture.SHADER becomes polyset's shader + */ + sprintf( filename, "%s/%s", g_cddir, file ); + + if ( !P3DLoad( filename ) ) + Error( "unable to load '%s'", filename ); + + while ( P3DGetNextPair( &psetName, &associatedShader ) ) + { + int i; + + // find the polyset in the object that this particular pset/shader pair + // corresponds to and append the shader to it + for ( i = 0; i < g_data.model.numSurfaces; i++ ) + { + if ( !_strcmpi( g_data.surfData[i].header.name, psetName) ) + { + char *p; + + if ( strstr( associatedShader, gamedir + 1 ) ) + { + p = strstr( associatedShader, gamedir + 1 ) + strlen( gamedir ) - 1; + } + else + { + p = associatedShader; + } + + strcpy( g_data.surfData[i].shaders[g_data.surfData[i].header.numShaders].name, p ); + + g_data.surfData[i].header.numShaders++; + } + } + + } + + P3DClose(); +} +#endif + + diff --git a/utils/q3data/p3dlib.h b/utils/q3data/p3dlib.h new file mode 100644 index 0000000..a6c0320 --- /dev/null +++ b/utils/q3data/p3dlib.h @@ -0,0 +1,8 @@ + +#define P3D_GET_CROSSLINE 1 +#define P3D_GET_RESTOFLINE 2 + +int P3DLoad( const char *filename ); +void P3DClose(); + +int P3DGetNextPair( char **name, char **associatedShader ); diff --git a/utils/q3data/polyset.c b/utils/q3data/polyset.c new file mode 100644 index 0000000..3ba6b98 --- /dev/null +++ b/utils/q3data/polyset.c @@ -0,0 +1,256 @@ +#include +#include "q3data.h" + +polyset_t *Polyset_SplitSets( polyset_t *psets, int numpolysets, int *pNumNewPolysets, int maxTris ) +{ + int p, np, op; + int numNewPolysets = 0; + int numSplitPolysets = 0; + polyset_t *newpsets; + int sumTriangles = 0; + + for ( p = 0; p < numpolysets; p++ ) + { + numNewPolysets += psets[p].numtriangles / maxTris + 1; + } + + if ( numNewPolysets == numpolysets ) + return psets; + + printf( "Warning: creating %d polysets from input of %d polysets\n", numNewPolysets, numpolysets ); + + newpsets = calloc( sizeof( polyset_t ) * numNewPolysets, 1 ); + + for ( np = 0, op = 0; op < numpolysets; op++ ) + { + numSplitPolysets = ( psets[op].numtriangles / ( maxTris + 1 ) ) + 1; + if ( numSplitPolysets == 1 ) + { + memcpy( &newpsets[np], &psets[op], sizeof( polyset_t ) ); + np++; + } + else + { + sumTriangles = 0; + + // split this pset into multiple smaller psets + for ( p = 0; p < numSplitPolysets; p++, np++ ) + { + memcpy( &newpsets[np], &psets[op], sizeof( polyset_t ) ); + + newpsets[np].triangles = psets[op].triangles + sumTriangles; + + if ( sumTriangles + maxTris > psets[op].numtriangles ) + newpsets[np].numtriangles = psets[op].numtriangles - sumTriangles; + else + newpsets[np].numtriangles = maxTris; + + sumTriangles += newpsets[np].numtriangles; + } + } + } + + *pNumNewPolysets = numNewPolysets; + + return newpsets; +} + +polyset_t *Polyset_LoadSets( const char *file, int *numpolysets, int maxTrisPerSet ) +{ + polyset_t *psets; + polyset_t *finalpsets; + + // + // load the frame + // + if ( strstr( file, ".3DS" ) || strstr( file, ".3ds" ) ) + _3DS_LoadPolysets( file, &psets, numpolysets, g_verbose ); + else + Error( "TRI files no longer supported" ); +// TRI_LoadPolysets( file, &psets, numpolysets ); + +/* + // + // scale polysets + // + for ( i = 0; i < psets; i++ ) + { + int j; + + for ( j = 0; j < psets[i].numtriangles; j++ ) + { + } + } +*/ + + // + // split polysets if necessary + // + finalpsets = Polyset_SplitSets( psets, *numpolysets, numpolysets, maxTrisPerSet ); + + return finalpsets; +} + +polyset_t *Polyset_CollapseSets( polyset_t *psets, int numpolysets ) +{ + int p; + int sumtriangles = 0; + + polyset_t *oldpsets = psets; + + // + // no tag checking because this is an $oldbase and thus shouldn't have any + // tags + // + for ( p = 0; p < numpolysets; p++ ) + { + sumtriangles += oldpsets[p].numtriangles; + } + + psets = calloc( 1, sizeof( polyset_t ) ); + psets[0].numtriangles = sumtriangles; + psets[0].triangles = malloc( MD3_MAX_TRIANGLES * sizeof( triangle_t ) ); + + // each call to "LoadPolysets" only allocates a single large chunk of + // triangle memory that is utilized by all the polysets loaded by + // that one call + memcpy( psets[0].triangles, oldpsets[0].triangles, sizeof( triangle_t ) * sumtriangles ); + + free( oldpsets[0].triangles ); + free( oldpsets ); + + return psets; +} + +static float SnapFloat( float x ) +{ + int ix; + + x *= 1.0f / MD3_XYZ_SCALE; + ix = ( int ) x; + x = ( float ) ix; + x *= MD3_XYZ_SCALE; + + return x; +} + +void Polyset_SnapSets( polyset_t *psets, int numpolysets ) +{ + int p; + + for ( p = 0; p < numpolysets; p++ ) + { + int t; + + for ( t = 0; t < psets[p].numtriangles; t++ ) + { + int v; + + for ( v = 0; v < 3; v++ ) + { + psets[p].triangles[t].verts[v][0] = SnapFloat( psets[p].triangles[t].verts[v][0] ); + psets[p].triangles[t].verts[v][1] = SnapFloat( psets[p].triangles[t].verts[v][1] ); + psets[p].triangles[t].verts[v][2] = SnapFloat( psets[p].triangles[t].verts[v][2] ); + } + } + } +} + +void Polyset_ComputeNormals( polyset_t *psets, int numpolysets ) +{ + int p; + int i, t; + int vertexIndex[MD3_MAX_TRIANGLES][3]; + vec3_t verts[MD3_MAX_VERTS]; + vec3_t normals[MD3_MAX_VERTS]; + vec3_t faceNormals[MD3_MAX_TRIANGLES]; + + // + // iterate through polysets + // + for ( p = 0; p < numpolysets; p++ ) + { + int numUniqueVertices = 0; + + assert( psets[p].numtriangles < MD3_MAX_TRIANGLES ); + + memset( vertexIndex, 0xff, sizeof( vertexIndex ) ); + memset( verts, 0, sizeof( verts ) ); + memset( normals, 0, sizeof( normals ) ); + + // + // unique vertices + // + for ( t = 0; t < psets[p].numtriangles; t++ ) + { + int j; + + for ( j = 0; j < 3; j++ ) + { + for ( i = 0; i < numUniqueVertices; i++ ) + { + if ( VectorCompare( psets[p].triangles[t].verts[j], verts[i] ) ) + { + break; + } + } + if ( i == numUniqueVertices ) + { + vertexIndex[t][j] = numUniqueVertices; + VectorCopy( (psets[p].triangles[t].verts[j]), (verts[numUniqueVertices]) ); + numUniqueVertices++; + } + else + { + vertexIndex[t][j] = i; + } + } + } + + // + // compute face normals + // + for ( t = 0; t < psets[p].numtriangles; t++ ) + { + vec3_t side0, side1, facenormal; + + VectorSubtract( psets[p].triangles[t].verts[0], psets[p].triangles[t].verts[1], side0 ); + VectorSubtract( psets[p].triangles[t].verts[2], psets[p].triangles[t].verts[1], side1); + + CrossProduct( side0, side1, facenormal ); + VectorNormalize( facenormal, faceNormals[t] ); + } + + // + // sum normals and copy them back + // + for ( i = 0; i < numUniqueVertices; i++ ) + { + for ( t = 0; t < psets[p].numtriangles; t++ ) + { + if ( vertexIndex[t][0] == i || + vertexIndex[t][1] == i || + vertexIndex[t][2] == i ) + { + normals[i][0] += faceNormals[t][0]; + normals[i][1] += faceNormals[t][1]; + normals[i][2] += faceNormals[t][2]; + } + } + VectorNormalize( normals[i], normals[i] ); + } + + + for ( t = 0; t < psets[p].numtriangles; t++ ) + { + VectorCopy( normals[vertexIndex[t][0]], psets[p].triangles[t].normals[0] ); + VectorCopy( normals[vertexIndex[t][1]], psets[p].triangles[t].normals[1] ); + VectorCopy( normals[vertexIndex[t][2]], psets[p].triangles[t].normals[2] ); + // set this up ready for re-computation of normals over surfaces. + psets[p].triangles[t].normal_recomputed[0] = 0; + psets[p].triangles[t].normal_recomputed[1] = 0; + psets[p].triangles[t].normal_recomputed[2] = 0; + } + } +} + diff --git a/utils/q3data/q3data.c b/utils/q3data/q3data.c new file mode 100644 index 0000000..adaf142 --- /dev/null +++ b/utils/q3data/q3data.c @@ -0,0 +1,767 @@ +#include +#include "q3data.h" +#include "md3lib.h" + +qboolean g_verbose; +qboolean g_stripify = qtrue; +qboolean g_release; // don't grab, copy output data to new tree +char g_releasedir[1024]; // c:\quake2\baseq2, etc +qboolean g_archive; // don't grab, copy source data to new tree +char g_only[256]; // if set, only grab this cd +qboolean g_skipmodel; // set true when a cd is not g_only +qboolean gbKeyPress = qfalse; +/* +======================================================= + + PAK FILES + +======================================================= +*/ + +unsigned Com_BlockChecksum (void *buffer, int length); + +typedef struct +{ + char name[56]; + int filepos, filelen; +} packfile_t; + +typedef struct +{ + char id[4]; + int dirofs; + int dirlen; +} packheader_t; + +packfile_t pfiles[16384]; +FILE *pakfile; +packfile_t *pf; +packheader_t pakheader; + +/* +============== +BeginPak +============== +*/ +#if 0 +void BeginPak (char *outname) +{ + if (!g_pak) + return; + + pakfile = SafeOpenWrite (outname); + + // leave space for header + SafeWrite (pakfile, &pakheader, sizeof(pakheader)); + + pf = pfiles; +} +#endif + + +/* +============== +ReleaseFile + +Filename should be gamedir reletive. +Either copies the file to the release dir, or adds it to +the pak file. +============== +*/ +void ReleaseFile (char *filename) +{ + char source[1024]; + char dest[1024]; + + if (!g_release) + return; + + sprintf (source, "%s%s", gamedir, filename); + +// if (!g_pak) + { // copy it + sprintf (dest, "%s/%s", g_releasedir, filename); + printf ("copying to %s\n", dest); + QCopyFile (source, dest); + return; + } +#if 0 + else + { + int len; + byte *buf; + + // pak it + printf ("paking %s\n", filename); + if (strlen(filename) >= sizeof(pf->name)) + Error ("Filename too long for pak: %s", filename); + + len = LoadFile (source, (void **)&buf); + + /* + if (g_compress_pak && len < 4096*1024 ) + { + cblock_t in, out; + cblock_t Huffman (cblock_t in); + + in.count = len; + in.data = buf; + + out = Huffman (in); + + if (out.count < in.count) + { + printf (" compressed from %i to %i\n", in.count, out.count); + free (in.data); + buf = out.data; + len = out.count; + } + else + free (out.data); + } + */ + + strcpy (pf->name, filename); + pf->filepos = LittleLong(ftell(pakfile)); + pf->filelen = LittleLong(len); + pf++; + + SafeWrite (pakfile, buf, len); + + free (buf); + } +#endif +} + +typedef struct +{ + // shader + // opaque + // opaque 2 + // blend + // blend 2 + char names[5][1024]; + int num; +} ShaderFiles_t; + +ShaderFiles_t s_shaderFiles; + +void FindShaderFiles( char *filename ) +{ + char buffer[1024]; + char stripped[1024]; + char linebuffer[1024]; + int len, i; + char *buf; + char *diffuseExtensions[] = + { + ".TGA", + ".WAL", + ".PCX", + 0 + }; + char *otherExtensions[] = + { + ".specular.TGA", + ".blend.TGA", + ".alpha.TGA", + 0 + }; + + s_shaderFiles.num = 0; + + strcpy( stripped, filename ); + if ( strrchr( stripped, '.' ) ) + *strrchr( stripped, '.' ) = 0; + strcat( stripped, ".shader" ); + + if ( FileExists( stripped ) ) + { + char *p; + char mapa[512], mapb[512]; + + strcpy( s_shaderFiles.names[s_shaderFiles.num], stripped ); + s_shaderFiles.num++; + + // load and parse + len = LoadFile( stripped, (void **)&buf); + + p = buf; + + while ( p - buf < len ) + { + i = 0; + + // skip spaces + while ( *p == ' ' || *p == '\n' || *p == '\t' ) + p++; + + // grab rest of the line + while ( *p != 0 && *p != '\n' ) + { + linebuffer[i] = *p; + i++; + p++; + } + if ( *p == '\n' ) + p++; + linebuffer[i] = 0; + + strlwr( linebuffer ); + + // see if the line specifies an opaque map or blendmap + if ( strstr( linebuffer, "opaquemap" ) == linebuffer || + strstr( linebuffer, "blendmap" ) == linebuffer ) + { + int j; + + i = 0; + + mapa[0] = mapb[0] = 0; + + // skip past the keyword + while ( linebuffer[i] != ' ' && linebuffer[i] != '\t' && linebuffer[i] ) + i++; + // skip past spaces + while ( ( linebuffer[i] == ' ' || linebuffer[i] == '\t' ) && linebuffer[i] ) + i++; + + // grab first map name + j = 0; + while ( linebuffer[i] != ' ' && linebuffer[i] != '\t' && linebuffer[i] ) + { + mapa[j] = linebuffer[i]; + j++; + i++; + } + mapa[j] = 0; + + // skip past spaces + while ( ( linebuffer[i] == ' ' || linebuffer[i] == '\t' ) && linebuffer[i] ) + i++; + + // grab second map name + j = 0; + while ( linebuffer[i] != ' ' && linebuffer[i] != '\t' && linebuffer[i] ) + { + mapb[j] = linebuffer[i]; + j++; + i++; + } + mapb[j] = 0; + + // store map names + if ( mapa[0] != 0 && mapa[0] != '-' ) + { + sprintf( s_shaderFiles.names[s_shaderFiles.num], "%s%s", gamedir, mapa ); + s_shaderFiles.num++; + } + if ( mapb[0] != 0 && mapb[0] != '-' && mapb[0] != '^' && mapb[0] != '*' ) + { + sprintf( s_shaderFiles.names[s_shaderFiles.num], "%s%s", gamedir, mapb ); + s_shaderFiles.num++; + } + } + } + } + else + { + if ( strrchr( stripped, '.' ) ) + *strrchr( stripped, '.' ) = 0; + + // look for diffuse maps + for ( i = 0; i < 3; i++ ) + { + strcpy( buffer, stripped ); + strcat( buffer, diffuseExtensions[i] ); + if ( FileExists( buffer ) ) + { + strcpy( s_shaderFiles.names[s_shaderFiles.num], buffer ); + s_shaderFiles.num++; + break; + } + } + for ( i = 0; i < 3; i++ ) + { + strcpy( buffer, stripped ); + strcat( buffer, otherExtensions[i] ); + if ( FileExists( buffer ) ) + { + strcpy( s_shaderFiles.names[s_shaderFiles.num], buffer ); + s_shaderFiles.num++; + } + } + } +} + +/* +============== +ReleaseShader + +Copies all needed files for a shader to the release directory +============== +*/ +void ReleaseShader( char *filename ) +{ + char fullpath[1024]; + char dest[1024]; + char stripped[1024]; + int i; + + sprintf( fullpath, "%s%s", gamedir, filename ); + + FindShaderFiles( fullpath ); + +// if ( !g_pak ) + { + for ( i = 0; i < s_shaderFiles.num; i++ ) + { + strcpy( stripped, s_shaderFiles.names[i] ); + if ( strstr( stripped, gamedir ) ) + { + memmove( stripped, stripped+ strlen( gamedir ), strlen( stripped ) ); + } + sprintf( dest, "%s/%s", g_releasedir, stripped ); + printf ("copying to %s\n", dest ); + QCopyFile( s_shaderFiles.names[i], dest ); + } + } +} + +/* +============== +FinishPak +============== +*/ +#if 0 +void FinishPak (void) +{ + int dirlen; + int d; + int i; + unsigned checksum; + + if (!g_pak) + return; + + pakheader.id[0] = 'P'; + pakheader.id[1] = 'A'; + pakheader.id[2] = 'C'; + pakheader.id[3] = 'K'; + dirlen = (byte *)pf - (byte *)pfiles; + pakheader.dirofs = LittleLong(ftell(pakfile)); + pakheader.dirlen = LittleLong(dirlen); + + checksum = Com_BlockChecksum ( (void *)pfiles, dirlen ); + + SafeWrite (pakfile, pfiles, dirlen); + + i = ftell (pakfile); + + fseek (pakfile, 0, SEEK_SET); + SafeWrite (pakfile, &pakheader, sizeof(pakheader)); + fclose (pakfile); + + d = pf - pfiles; + printf ("%i files packed in %i bytes\n",d, i); + printf ("checksum: 0x%x\n", checksum); +} +#endif + + +/* +=============== +Cmd_File + +This is only used to cause a file to be copied during a release +build (default.cfg, maps, etc) +=============== +*/ +void Cmd_File (void) +{ + GetToken (qfalse); + ReleaseFile (token); +} + +/* +=============== +PackDirectory_r + +=============== +*/ +#ifdef _WIN32 +#include "io.h" +void PackDirectory_r (char *dir) +{ + struct _finddata_t fileinfo; + int handle; + char dirstring[1024]; + char filename[1024]; + + sprintf (dirstring, "%s%s/*.*", gamedir, dir); + + handle = _findfirst (dirstring, &fileinfo); + if (handle == -1) + return; + + do + { + sprintf (filename, "%s/%s", dir, fileinfo.name); + if (fileinfo.attrib & _A_SUBDIR) + { // directory + if (fileinfo.name[0] != '.') // don't pak . and .. + PackDirectory_r (filename); + continue; + } + // copy or pack the file + ReleaseFile (filename); + } while (_findnext( handle, &fileinfo ) != -1); + + _findclose (handle); +} +#else + +#include +#ifdef NeXT +#include +#else +#include +#endif + +void PackDirectory_r (char *dir) +{ +#ifdef NeXT + struct direct **namelist, *ent; +#else + struct dirent **namelist, *ent; +#endif + int count; + struct stat st; + int i; + int len; + char fullname[1024]; + char dirstring[1024]; + char *name; + + sprintf (dirstring, "%s%s", gamedir, dir); + count = scandir(dirstring, &namelist, NULL, NULL); + + for (i=0 ; id_name; + + if (name[0] == '.') + continue; + + sprintf (fullname, "%s/%s", dir, name); + sprintf (dirstring, "%s%s/%s", gamedir, dir, name); + + if (stat (dirstring, &st) == -1) + Error ("fstating %s", pf->name); + if (st.st_mode & S_IFDIR) + { // directory + PackDirectory_r (fullname); + continue; + } + + // copy or pack the file + ReleaseFile (fullname); + } +} +#endif + + +/* +=============== +Cmd_Dir + +This is only used to cause a directory to be copied during a +release build (sounds, etc) +=============== +*/ +void Cmd_Dir (void) +{ + GetToken (qfalse); + PackDirectory_r (token); +} + +//======================================================================== + +#define MAX_RTEX 16384 +int numrtex; +char rtex[MAX_RTEX][64]; + +void ReleaseTexture (char *name) +{ + int i; + char path[1024]; + + for (i=0 ; i] [-dump ] [-release

] [-only ] [-3ds] [-verbose] [-keypress] [file.qdt]"); + + for ( ; i +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=q3data - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "q3data.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "q3data.mak" CFG="q3data - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "q3data - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "q3data - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName ""$/Utils/q3data", VAOAAAAA" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "q3data - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "../common" /D "Q3DATA" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /stack:0xf42400 /subsystem:console /debug /machine:I386 + +!ELSEIF "$(CFG)" == "q3data - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "../common" /D "Q3DATA" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /stack:0xf42400 /subsystem:console /debug /machine:I386 /pdbtype:sept +# SUBTRACT LINK32 /incremental:no + +!ENDIF + +# Begin Target + +# Name "q3data - Win32 Release" +# Name "q3data - Win32 Debug" +# Begin Group "Header Files" + +# PROP Default_Filter "*.h" +# Begin Source File + +SOURCE=.\3dslib.h +# End Source File +# Begin Source File + +SOURCE=..\common\aselib.h +# End Source File +# Begin Source File + +SOURCE=..\common\bspfile.h +# End Source File +# Begin Source File + +SOURCE=..\common\cmdlib.h +# End Source File +# Begin Source File + +SOURCE=.\g2export.h +# End Source File +# Begin Source File + +SOURCE=.\g2export_interface.h +# End Source File +# Begin Source File + +SOURCE=..\common\imagelib.h +# End Source File +# Begin Source File + +SOURCE=..\common\l3dslib.h +# End Source File +# Begin Source File + +SOURCE=..\common\lbmlib.h +# End Source File +# Begin Source File + +SOURCE=.\matcomp.h +# End Source File +# Begin Source File + +SOURCE=..\common\mathlib.h +# End Source File +# Begin Source File + +SOURCE=.\matrix4.h +# End Source File +# Begin Source File + +SOURCE=.\md3lib.h +# End Source File +# Begin Source File + +SOURCE=.\mdx_format.h +# End Source File +# Begin Source File + +SOURCE=.\models.h +# End Source File +# Begin Source File + +SOURCE=.\p3dlib.h +# End Source File +# Begin Source File + +SOURCE=..\common\polyset.h +# End Source File +# Begin Source File + +SOURCE=.\q3data.h +# End Source File +# Begin Source File + +SOURCE=..\common\qfiles.h +# End Source File +# Begin Source File + +SOURCE=..\common\scriplib.h +# End Source File +# Begin Source File + +SOURCE=.\stl_opt.h +# End Source File +# Begin Source File + +SOURCE=..\common\surfaceflags.h +# End Source File +# Begin Source File + +SOURCE=..\common\threads.h +# End Source File +# Begin Source File + +SOURCE=..\common\trilib.h +# End Source File +# Begin Source File + +SOURCE=.\vect3.h +# End Source File +# End Group +# Begin Source File + +SOURCE=.\3dslib.c +# End Source File +# Begin Source File + +SOURCE=..\common\aselib.c +# End Source File +# Begin Source File + +SOURCE=..\common\bspfile.c +# End Source File +# Begin Source File + +SOURCE=..\common\cmdlib.c +# End Source File +# Begin Source File + +SOURCE=.\compress.c +# End Source File +# Begin Source File + +SOURCE=.\g2export.cpp +# End Source File +# Begin Source File + +SOURCE=.\g2export_interface.cpp +# End Source File +# Begin Source File + +SOURCE=..\common\imagelib.c +# End Source File +# Begin Source File + +SOURCE=.\images.c +# End Source File +# Begin Source File + +SOURCE=.\matcomp.c +# End Source File +# Begin Source File + +SOURCE=..\common\mathlib.c +# End Source File +# Begin Source File + +SOURCE=.\matrix4.cpp +# End Source File +# Begin Source File + +SOURCE=.\md3lib.c +# End Source File +# Begin Source File + +SOURCE=..\common\md4.c +# End Source File +# Begin Source File + +SOURCE=.\models.c +# End Source File +# Begin Source File + +SOURCE=.\oldstuff.c +# End Source File +# Begin Source File + +SOURCE=.\p3dlib.c +# End Source File +# Begin Source File + +SOURCE=.\polyset.c +# End Source File +# Begin Source File + +SOURCE=.\q3data.c +# End Source File +# Begin Source File + +SOURCE=..\common\scriplib.c +# End Source File +# Begin Source File + +SOURCE=.\stl_opt.cpp +# End Source File +# Begin Source File + +SOURCE=.\stripper.c +# End Source File +# Begin Source File + +SOURCE=..\common\trilib.c +# End Source File +# Begin Source File + +SOURCE=.\vect3.cpp +# End Source File +# Begin Source File + +SOURCE=.\video.c +# End Source File +# End Target +# End Project diff --git a/utils/q3data/q3data.dsw b/utils/q3data/q3data.dsw new file mode 100644 index 0000000..b43f162 --- /dev/null +++ b/utils/q3data/q3data.dsw @@ -0,0 +1,37 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "q3data"=.\q3data.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/Utils/q3data", VAOAAAAA + . + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ + begin source code control + "$/Utils/q3data", VAOAAAAA + . + end source code control +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/utils/q3data/q3data.h b/utils/q3data/q3data.h new file mode 100644 index 0000000..65c8017 --- /dev/null +++ b/utils/q3data/q3data.h @@ -0,0 +1,84 @@ +// q3data.h + + +#include +#include +#include +#include +#include + +#include "cmdlib.h" +#include "scriplib.h" +#include "mathlib.h" +#include "polyset.h" +#include "trilib.h" +#include "imagelib.h" +#include "threads.h" +#include "l3dslib.h" +#include "bspfile.h" +#include "p3dlib.h" +#include "3dslib.h" +#include "aselib.h" +#include "md3lib.h" + +void Cmd_ASEConvert( qboolean grabAnims, qboolean bIsGhoul2); +void Cmd_ASEInitAnimGrab(void); +void Cmd_ASEFinalizeAnimGrab(void); +void Cmd_ASEAnimGrab(void); +void Cmd_3DSConvert( void ); +void Cmd_Modelname (void); +void Cmd_SpriteBase (void); +void Cmd_Base (void); +void Cmd_Cd (void); +void Cmd_Origin (void); +void Cmd_ScaleUp (void); +void Cmd_Frame (void); +void Cmd_Modelname (void); +void Cmd_SpriteShader(void); +void Cmd_Skin(void); +void Cmd_Skinsize (void); +void FinishModel (int type); + +void Cmd_Grab (void); +void Cmd_Raw (void); +void Cmd_Mip (void); +void Cmd_Environment (void); +void Cmd_Colormap (void); + +void Cmd_File (void); +void Cmd_Dir (void); +void Cmd_StartWad (void); +void Cmd_EndWad (void); +void Cmd_Mippal (void); +void Cmd_Mipdir (void); + +void Cmd_Video (void); + +void ReleaseFile (char *filename); +void ReleaseShader( char *filename ); + +void Convert3DStoMD3( const char *filename ); + +void OrderMesh( int input[][3], int output[][3], int numTris, int maxVerts ); +void CleanUpAfterGrabbing(); + +extern byte *byteimage, *lbmpalette; +extern int byteimagewidth, byteimageheight; + +extern qboolean g_release; // don't grab, copy output data to new tree +extern char g_releasedir[1024]; // c:\quake2\baseq2, etc +extern qboolean g_archive; // don't grab, copy source data to new tree +extern qboolean do3ds; +extern char g_only[256]; // if set, only grab this cd +extern qboolean g_skipmodel; // set true when a cd is not g_only +extern qboolean g_verbose; +extern qboolean gbKeyPress; + +extern char *trifileext; + +#define TYPE_ITEM 0 +#define TYPE_PLAYER 1 +#define TYPE_WEAPON 2 +#define TYPE_HAND 3 +#define TYPE_UNKNOWN 4 +#define TYPE_GHOUL2_1FRAME 5 diff --git a/utils/q3data/stl_opt.cpp b/utils/q3data/stl_opt.cpp new file mode 100644 index 0000000..47c8cd5 --- /dev/null +++ b/utils/q3data/stl_opt.cpp @@ -0,0 +1,142 @@ +// Filename:- stl_opt.cpp +// +#pragma warning( disable : 4786 ) // identifier was truncated + +#include // all this shit just to get OutputdebugString() proto!!!!! +#include + + +//#include "smodel.h" +#include "stl_opt.h" + +#include +#include +#include +#include +using namespace std; + + + +typedef set VertUsage_t; // stores user-tri numbers... +typedef vector VertUsageList_t; + VertUsageList_t VertUsageList; // ... for all verts + +typedef vector AbuttingTris_t; +typedef vector AbuttingTrisList_t; + AbuttingTrisList_t AbuttingTrisList; + +void TriangleAbutList_Gen(int mesh[][3], int iNumTris, int iMaxVerts) +{ + // not 100% sure how efficient this [re/]initialising is, but it works... + // + VertUsageList.clear(); + VertUsageList.resize(iMaxVerts); + // + AbuttingTrisList.clear(); + AbuttingTrisList.resize(iNumTris); + + // generate tris-using list per vert... + // + for (int iTriangle = 0; iTriangle < iNumTris; iTriangle++) + { + VertUsageList[ mesh[iTriangle][0] ].insert( VertUsageList[ mesh[iTriangle][0] ].begin(),iTriangle ); + VertUsageList[ mesh[iTriangle][1] ].insert( VertUsageList[ mesh[iTriangle][1] ].begin(),iTriangle ); + VertUsageList[ mesh[iTriangle][2] ].insert( VertUsageList[ mesh[iTriangle][2] ].begin(),iTriangle ); + } + +/* + for (int iVert=0; iVert NodesSharedPerTri_t; // key=tri, value = # nodes shared + NodesSharedPerTri_t NodesSharedPerTri; + + for (int iVert=0; iVert<3; iVert++) + { + int iThisVert = mesh[iTriangle][iVert]; + + for (VertUsage_t::iterator it = VertUsageList[iThisVert].begin(); it != VertUsageList[iThisVert].end(); ++it) + { +// if (NodesSharedPerTri.find(*it) != NodesSharedPerTri.end()) +// { +// OutputDebugString(va("Tri %d shares %d verts\n",*it,NodesSharedPerTri[*it])); +// } + + NodesSharedPerTri[*it]++; // amazingly enough this actually works, STL rules... + +// if (NodesSharedPerTri.find(*it) != NodesSharedPerTri.end()) +// { +// OutputDebugString(va("Tri %d shares %d verts\n",*it,NodesSharedPerTri[*it])); +// } +// else +// { +// assert(0); +// } + } + } + + for (NodesSharedPerTri_t::iterator it = NodesSharedPerTri.begin(); it != NodesSharedPerTri.end(); ++it) + { + int iOtherTriangle = (*it).first; + int iSharedVertTot = (*it).second; + + if ( iOtherTriangle != iTriangle && iSharedVertTot >= 2 ) // this tri shares 2 or more verts with us? + { + AbuttingTrisList[ iTriangle ].push_back( iOtherTriangle ); + } + } + } + +/* + int iLastTri = 0; + int iHighestAbutCount=0; + for (iTriangle = 0; iTriangle< iNumTris; iTriangle++) + { + if (AbuttingTrisList[ iTriangle ].size()) + { + if (AbuttingTrisList[ iTriangle ].size() > iHighestAbutCount) + iHighestAbutCount = AbuttingTrisList[ iTriangle ].size(); + + iLastTri = iTriangle; + char sTemp[1000]; + + sprintf(sTemp,"Tri %d/%d is abutted by triangles: ",iTriangle,iNumTris); + + for (int i=0; i +#include +#include + +#include "stl_opt.h" + +static int s_used[8192]; // same as MD3_MAX_TRIANGLES + +/* +** FindNextTriangleInStrip +** +** Given a surface and triangle this tries to find the next triangle +** in the strip that would continue the strip. The next triangle in +** the strip should have the same winding as this triangle. +*/ +static int FindNextTriangleInStripOrFan( int mesh[][3], int tri, int orientation, int numTris, int odd ) +{ + int t; + int sum = 0; + int currentTri[3]; + int side; + int a, b, c; + int refa, refb; + +#define USE_STL_OPTIMISATION +#ifdef USE_STL_OPTIMISATION + int iAbuttingCount; +const int* pAbuttingTris; + int iAbuttingTriIndex; +#endif + + currentTri[0] = mesh[tri][(0+orientation)%3]; + currentTri[1] = mesh[tri][(1+orientation)%3]; + currentTri[2] = mesh[tri][(2+orientation)%3]; + + if ( odd ) + { + refa = currentTri[1]; + refb = currentTri[2]; + } + else + { + refa = currentTri[2]; + refb = currentTri[0]; + } + + // go through all triangles and look for sides that match + // this triangle's + +#ifdef USE_STL_OPTIMISATION + // STL-optimised method... + // + pAbuttingTris = TriangleAbutList_Query(tri, &iAbuttingCount); + + for (iAbuttingTriIndex = 0; iAbuttingTriIndex < iAbuttingCount; iAbuttingTriIndex++) + { + t = pAbuttingTris[iAbuttingTriIndex]; + +#else + // old brute-force method... + // + for ( t = 0; t < numTris; t++ ) + { +#endif + // don't check against self or against previously used triangles + if ( t == tri ) + continue; + if ( s_used[t] ) + continue; + + // check all three sides of the candidate triangle + for ( side = 0; side < 3; side++ ) + { + // check only the second (abutting) side + if ( ( refa == mesh[t][(side+1)%3] ) && + ( refb == mesh[t][side] ) ) + { + + a = mesh[t][0]; + b = mesh[t][1]; + c = mesh[t][2]; + + // rotate the candidate triangle to align it properly in the strip + if ( side == 1 ) + { + mesh[t][0] = b; + mesh[t][1] = c; + mesh[t][2] = a; + } + else if ( side == 2 ) + { + mesh[t][0] = c; + mesh[t][1] = a; + mesh[t][2] = b; + } + + return t; + } +/* + else + { + Error( "fans not implemented yet" ); + + // check only the third (abutting) side + if ( ( currentTri[2] == pSurf->baseTriangles[t].v[side].index ) && + ( currentTri[0] == pSurf->baseTriangles[t].v[(side+1)%3].index ) ) + { + return t; + } + } +*/ + } + } + + return -1; +} + +/* +** StripLength +*/ +static int StripLength( int mesh[][3], int strip[][3], int tri, int orientation, int numInputTris, int fillNo ) +{ + int stripIndex = 0; + int next; + + int odd = 1; + + strip[stripIndex][0] = mesh[tri][(0+orientation)%3]; + strip[stripIndex][1] = mesh[tri][(1+orientation)%3]; + strip[stripIndex][2] = mesh[tri][(2+orientation)%3]; + s_used[tri] = fillNo; + stripIndex++; + + next = tri; + + while ( ( next = FindNextTriangleInStripOrFan( mesh, next, orientation, numInputTris, odd ) ) != -1 ) + { + s_used[next] = fillNo; + odd = !odd; + strip[stripIndex][0] = mesh[next][0]; + strip[stripIndex][1] = mesh[next][1]; + strip[stripIndex][2] = mesh[next][2]; + stripIndex++; + + // all iterations after first need to be with an unrotated reference triangle + orientation = 0; + } + + return stripIndex; +} + +/* +** BuildOptimizedList +** +** Attempts to build the longest strip/fan possible. Does not adhere +** to pure strip or fan, will intermix between the two so long as some +** type of connectivity can be maintained. +*/ +#define MAX_ORIENTATIONS 3 +#define MAX_MATCHED_SIDES 4 +#define MAX_SEED_TRIANGLES 16 + +static int BuildOptimizedList( int mesh[][3], int strip[][3], int numInputTris ) +{ + int t; + int stripLen = 0; + int startTri = -1; + int bestTri = -1, bestLength = 0, bestOrientation = -1; + int matchedSides = 0; + int orientation = 0; + int seedTriangles[MAX_MATCHED_SIDES][MAX_SEED_TRIANGLES]; + int seedLengths[MAX_ORIENTATIONS][MAX_MATCHED_SIDES][MAX_SEED_TRIANGLES]; + int numSeeds[MAX_MATCHED_SIDES] = { 0, 0, 0 }; + int i; + + // build a ranked list of candidate seed triangles based on + // number of offshoot strips. Precedence goes to orphans, + // then corners, then edges, and interiors. + memset( seedTriangles, 0xff, sizeof( seedTriangles ) ); + memset( seedLengths, 0xff, sizeof( seedLengths ) ); + + for ( i = 0; i < MAX_MATCHED_SIDES; i++ ) + { + // find the triangle with lowest number of child strips + for ( t = 0; t < numInputTris; t++ ) + { + int orientation; + int n; + + if ( s_used[t] ) + continue; + + // try the candidate triangle in three different orientations + matchedSides = 0; + for ( orientation = 0; orientation < 3; orientation++ ) + { + if ( ( n = FindNextTriangleInStripOrFan( mesh, t, orientation, numInputTris, 1 ) ) != -1 ) + { + matchedSides++; + } + } + + if ( matchedSides == i ) + { + seedTriangles[i][numSeeds[i]] = t; + numSeeds[i]++; + if ( numSeeds[i] == MAX_SEED_TRIANGLES ) + break; + } + } + } + + // we have a list of potential seed triangles, so we now go through each + // potential candidate and look to see which produces the longest strip + // and select our startTri based on this + for ( i = 0; i < MAX_MATCHED_SIDES; i++ ) + { + int j; + + for ( j = 0; j < numSeeds[i]; j++ ) + { + for ( orientation = 0; orientation < 3; orientation++ ) + { + int k; + + seedLengths[orientation][i][j] = StripLength( mesh, strip, seedTriangles[i][j], orientation, numInputTris, 2 ); + + if ( seedLengths[orientation][i][j] > bestLength ) + { + bestTri = seedTriangles[i][j]; + bestLength = seedLengths[orientation][i][j]; + bestOrientation = orientation; + } + + for ( k = 0; k < numInputTris; k++ ) + { + if ( s_used[k] == 2 ) + s_used[k] = 0; + } + } + } + + if ( bestTri != -1 ) + { + break; + } + } + + // build the strip for real + if ( bestTri != -1 ) + { + stripLen = StripLength( mesh, strip, bestTri, bestOrientation, numInputTris, 1 ); + } + + return stripLen; +} + +/* +** OrderMesh +** +** Given an input mesh and an output mesh, this routine will reorder +** the triangles within the mesh into strips/fans. +*/ + +// note, the 'maxVerts' param is purely a hint as to how big to make the STL optimise arrays so they don't need to +// be expanded at run-time... +// +void OrderMesh( int input[][3], int output[][3], int numTris, int maxVerts ) +{ + int i; + int sumStrippedTriangles = 0; + int strippedTriangles; + int totalStrips = 0; + int strip[8192][3]; // could dump directly into 'output', but + // this helps with debugging + + memset( s_used, 0, sizeof( s_used ) ); + + TriangleAbutList_Gen(input, numTris, maxVerts); + + +#if 0 + FILE *fp = fopen( "strip.txt", "wt" ); + + for ( i = 0; i < numTris; i++ ) + { + fprintf( fp, "%4d: %3d %3d %3d\n", i, input[i][0], input[i][1], input[i][2] ); + } + fclose( fp ); +#endif + + // while there are still triangles that are not part of a strip + while ( sumStrippedTriangles < numTris ) + { + printf("%c%d%%",0x0D,(sumStrippedTriangles*100)/numTris); + // build a strip + strippedTriangles = BuildOptimizedList( input, strip, numTris ); + + for ( i = 0; i < strippedTriangles; i++ ) + { + output[sumStrippedTriangles+i][0] = strip[i][0]; + output[sumStrippedTriangles+i][1] = strip[i][1]; + output[sumStrippedTriangles+i][2] = strip[i][2]; + } + + sumStrippedTriangles += strippedTriangles; + totalStrips++; + } + printf("\n"); + + printf( "Triangles on surface: %d\n", sumStrippedTriangles ); + printf( "Total strips from surface: %d\n", totalStrips ); + printf( "Average strip length: %f\n", ( float ) sumStrippedTriangles / totalStrips ); +} diff --git a/utils/q3data/vect3.cpp b/utils/q3data/vect3.cpp new file mode 100644 index 0000000..001de9b --- /dev/null +++ b/utils/q3data/vect3.cpp @@ -0,0 +1,108 @@ +#include "vect3.h" + + +const Vect3 Vect3X(1.f,0.f,0.f); +const Vect3 Vect3Y(0.f,1.f,0.f); +const Vect3 Vect3Z(0.f,0.f,1.f); +const Vect3 Vect3negX(-1.f,0.f,0.f); +const Vect3 Vect3negY(0.f,-1.f,0.f); +const Vect3 Vect3negZ(0.f,0.f,-1.f); +const Vect3 Vect3Zero(0.f,0.f,0.f); + +const Vect3 &Vect3::operator/= (const float d) +{ + float inv=1.f/d; + return (*this)*=inv; +} + +void Vect3::Cross(const Vect3& p) +{ + Vect3 t=*this; + v[0]=t.v[1]*p.v[2]-t.v[2]*p.v[1]; + v[1]=t.v[2]*p.v[0]-t.v[0]*p.v[2]; + v[2]=t.v[0]*p.v[1]-t.v[1]*p.v[0]; +} + +void Vect3::NegCross(const Vect3& p) +{ + Vect3 t=*this; + v[0]=p.v[1]*t.v[2]-p.v[2]*t.v[1]; + v[1]=p.v[2]*t.v[0]-p.v[0]*t.v[2]; + v[2]=p.v[0]*t.v[1]-p.v[1]*t.v[0]; +} + +float Vect3::Dist(const Vect3& p) const +{ + Vect3 t=*this; + t-=p; + return t.Len(); +} + +float Vect3::Dist2(const Vect3& p) const +{ + Vect3 t=*this; + t-=p; + return t^t; +} + +void Vect3::Perp() +{ + float rlen,tlen; + Vect3 r,t; + r=*this; + r.Cross(Vect3X); + rlen=r.Len(); + t=*this; + t.Cross(Vect3Y); + tlen=t.Len(); + if (tlen>rlen) + { + r=t; + rlen=tlen; + } + t=*this; + t.Cross(Vect3Z); + tlen=t.Len(); + if (tlen>rlen) + { + r=t; + rlen=tlen; + } + *this=r; +} + +void Vect3::Min(const Vect3& p) +{ + if (p.v[0]v[0]) + v[0]=p.v[0]; + if (p.v[1]>v[1]) + v[1]=p.v[1]; + if (p.v[2]>v[2]) + v[2]=p.v[2]; +} + +float Vect3::MaxElement() const +{ + return v[MaxElementIndex()]; +} + +int Vect3::MaxElementIndex() const +{ + if (fabs(v[0])>fabs(v[1])&&fabs(v[0])>fabs(v[2])) + return 0; + if (fabs(v[1])>fabs(v[2])) + return 1; + return 2; +} + + diff --git a/utils/q3data/vect3.h b/utils/q3data/vect3.h new file mode 100644 index 0000000..aa40451 --- /dev/null +++ b/utils/q3data/vect3.h @@ -0,0 +1,92 @@ +#if !defined(VECT3_INC) +#define VECT3_INC + +#include +#include + +class Vect3 +{ + float v[3]; +public: + Vect3(const float val) {v[0]=val;v[1]=val;v[2]=val;} + Vect3() {}//never put anything in here! too slow} + Vect3(const float x,const float y,const float z) {v[0]=x;v[1]=y;v[2]=z;} + Vect3(const Vect3& t) {v[0]=t.v[0];v[1]=t.v[1];v[2]=t.v[2];} + Vect3(const float *t) {v[0]=t[0];v[1]=t[1];v[2]=t[2];} + float& operator[](int i) {return v[i];} + float& x() {return v[0];} + float& y() {return v[1];} + float& z() {return v[2];} + const float& operator[](int i) const {return v[i];} + const float& x() const {return v[0];} + const float& y() const {return v[1];} + const float& z() const {return v[2];} + void Set(const float x,const float y,const float z) {v[0]=x;v[1]=y;v[2]=z;} + + float Len() const {return (float)sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);} + float Len2() const {return v[0]*v[0]+v[1]*v[1]+v[2]*v[2];} + void Norm() {(*this)/=this->Len();} + bool ZeroNorm() {float d=this->Len();if (d>1E-10) {(*this)/=d;return true;} (*this)=0.0f; return false;} + void SafeNorm() {assert(this->Len()>1E-10);(*this)/=this->Len();} + + const Vect3 &operator= (const float d) {v[0]=d;v[1]=d;v[2]=d; return *this; } + const Vect3 &operator= (const Vect3& t) {v[0]=t.v[0];v[1]=t.v[1];v[2]=t.v[2]; return *this; } + + const Vect3 &operator+= (const float d) {v[0]+=d;v[1]+=d;v[2]+=d; return *this; } + const Vect3 &operator+= (const Vect3& t) {v[0]+=t.v[0];v[1]+=t.v[1];v[2]+=t.v[2]; return *this; } + + const Vect3 &operator-= (const float d) {v[0]-=d;v[1]-=d;v[2]-=d; return *this; } + const Vect3 &operator-= (const Vect3& t) {v[0]-=t.v[0];v[1]-=t.v[1];v[2]-=t.v[2]; return *this; } + + const Vect3 &operator*= (const float d) {v[0]*=d;v[1]*=d;v[2]*=d; return *this; } + const Vect3 &operator*= (const Vect3& t) {v[0]*=t.v[0];v[1]*=t.v[1];v[2]*=t.v[2]; return *this; } + + const Vect3 &operator/= (const float d); + const Vect3 &operator/= (const Vect3& t) {v[0]/=t.v[0];v[1]/=t.v[1];v[2]/=t.v[2]; return *this; } + + float operator^ (const Vect3& t) const {return v[0]*t.v[0]+v[1]*t.v[1]+v[2]*t.v[2];} + + float Dist(const Vect3&) const; + float Dist2(const Vect3&) const; + void Cross(const Vect3&); + void NegCross(const Vect3&); + void Perp(); + void Min(const Vect3&); + void Max(const Vect3&); + float MaxElement() const; + int MaxElementIndex() const; + + void Interp(const Vect3 &v1,const Vect3 &v2,float t) {*this=v1;*this-=v2;*this*=t;*this+=v2;} +// bool operator== (const Vect3& t) const {return v[0]==t.v[0]&&v[1]==t.v[1]&&v[2]==t.v[2];} + bool operator== (const Vect3& t) const {return fabs(v[0]-t.v[0])<.001f&&fabs(v[1]-t.v[1])<.001f&&fabs(v[2]-t.v[2])<.001f;} + bool operator< (const Vect3& t) const {assert(0);return false;} + bool operator!= (const Vect3& t) const {return !(v[0]==t.v[0]&&v[1]==t.v[1]&&v[2]==t.v[2]);} + bool operator> (const Vect3& t) const {assert(0);return false;} + + inline Vect3 operator +(const Vect3 &rhs) const { return Vect3(v[0]+rhs.v[0], v[1]+rhs.v[1], v[2]+rhs.v[2]); } + inline Vect3 operator -(const Vect3 &rhs) const { return Vect3(v[0]-rhs.v[0], v[1]-rhs.v[1], v[2]-rhs.v[2]); } + inline Vect3 operator *(const Vect3 &rhs) const { return Vect3(v[0]*rhs.v[0], v[1]*rhs.v[1], v[2]*rhs.v[2]); } + inline Vect3 operator *(const float scalar) const { return Vect3(v[0]*scalar, v[1]*scalar, v[2]*scalar); } + inline friend Vect3 operator *(const float scalar, const Vect3 &rhs); + inline Vect3 operator /(const Vect3 &rhs) const { return Vect3(v[0]/rhs.v[0], v[1]/rhs.v[1], v[2]/rhs.v[2]); } + inline Vect3 operator /(const float scalar) const { return Vect3(v[0]/scalar, v[1]/scalar, v[2]/scalar); } +}; + +inline Vect3 operator *(const float scalar, const Vect3 &rhs) +{ + return Vect3(scalar*rhs.v[0], scalar*rhs.v[1], scalar*rhs.v[2]); +} + + +extern const Vect3 Vect3X; +extern const Vect3 Vect3Y; +extern const Vect3 Vect3Z; +extern const Vect3 Vect3negX; +extern const Vect3 Vect3negY; +extern const Vect3 Vect3negZ; +extern const Vect3 Vect3Zero; + + + + +#endif diff --git a/utils/q3data/video.c b/utils/q3data/video.c new file mode 100644 index 0000000..35e097b --- /dev/null +++ b/utils/q3data/video.c @@ -0,0 +1,1132 @@ +#include +#include "q3data.h" + +static int s_resample_width = 256; +static int s_resample_height = 256; + +#define OUTPUT_TGAS 1 + +#define UNCOMPRESSED 0 +#define BTC_COMPRESSION 1 + +static int s_compression_method = BTC_COMPRESSION; + +static const char *CIN_EXTENSION = "cn2"; +static const int CIN_SIGNATURE = ( 'C' << 24 ) | ( 'I' << 16 ) | ( 'N' << 8 ) | ( '2' ); + +static byte *s_soundtrack; +static char s_base[32]; +static char s_output_base[32]; + +/* +=============================================================================== + +WAV loading + +=============================================================================== +*/ + +typedef struct +{ + int rate; + int width; + int channels; + int loopstart; + int samples; + int dataofs; // chunk starts this many bytes from file start +} wavinfo_t; + + +byte *data_p; +byte *iff_end; +byte *last_chunk; +byte *iff_data; +int iff_chunk_len; + + +static int s_samplecounts[0x10000]; +static wavinfo_t s_wavinfo; + +short GetLittleShort(void) +{ + short val = 0; + val = *data_p; + val = val + (*(data_p+1)<<8); + data_p += 2; + return val; +} + +int GetLittleLong(void) +{ + int val = 0; + val = *data_p; + val = val + (*(data_p+1)<<8); + val = val + (*(data_p+2)<<16); + val = val + (*(data_p+3)<<24); + data_p += 4; + return val; +} + +void FindNextChunk(char *name) +{ + while (1) + { + data_p=last_chunk; + + if (data_p >= iff_end) + { // didn't find the chunk + data_p = NULL; + return; + } + + data_p += 4; + iff_chunk_len = GetLittleLong(); + if (iff_chunk_len < 0) + { + data_p = NULL; + return; + } +// if (iff_chunk_len > 1024*1024) +// Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len); + data_p -= 8; + last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 ); + if (!strncmp(data_p, name, 4)) + return; + } +} + +void FindChunk(char *name) +{ + last_chunk = iff_data; + FindNextChunk (name); +} + + +void DumpChunks(void) +{ + char str[5]; + + str[4] = 0; + data_p=iff_data; + do + { + memcpy (str, data_p, 4); + data_p += 4; + iff_chunk_len = GetLittleLong(); + printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len); + data_p += (iff_chunk_len + 1) & ~1; + } while (data_p < iff_end); +} + +/* +============ +GetWavinfo +============ +*/ +wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength) +{ + wavinfo_t info; + int i; + int format; + int samples; + + memset (&info, 0, sizeof(info)); + + if (!wav) + return info; + + iff_data = wav; + iff_end = wav + wavlength; + +// find "RIFF" chunk + FindChunk("RIFF"); + if (!(data_p && !strncmp(data_p+8, "WAVE", 4))) + { + printf("Missing RIFF/WAVE chunks\n"); + return info; + } + +// get "fmt " chunk + iff_data = data_p + 12; +// DumpChunks (); + + FindChunk("fmt "); + if (!data_p) + { + printf("Missing fmt chunk\n"); + return info; + } + data_p += 8; + format = GetLittleShort(); + if (format != 1) + { + printf("Microsoft PCM format only\n"); + return info; + } + + info.channels = GetLittleShort(); + info.rate = GetLittleLong(); + data_p += 4+2; + info.width = GetLittleShort() / 8; + +// get cue chunk + FindChunk("cue "); + if (data_p) + { + data_p += 32; + info.loopstart = GetLittleLong(); +// Com_Printf("loopstart=%d\n", sfx->loopstart); + + // if the next chunk is a LIST chunk, look for a cue length marker + FindNextChunk ("LIST"); + if (data_p) + { + if (!strncmp (data_p + 28, "mark", 4)) + { // this is not a proper parse, but it works with cooledit... + data_p += 24; + i = GetLittleLong (); // samples in loop + info.samples = info.loopstart + i; + } + } + } + else + info.loopstart = -1; + +// find data chunk + FindChunk("data"); + if (!data_p) + { + printf("Missing data chunk\n"); + return info; + } + + data_p += 4; + samples = GetLittleLong (); + + if (info.samples) + { + if (samples < info.samples) + Error ("Sound %s has a bad loop length", name); + } + else + info.samples = samples; + + info.dataofs = data_p - wav; + + return info; +} + +//===================================================================== + +/* +============== +LoadSoundtrack +============== +*/ +void LoadSoundtrack (void) +{ + char name[1024]; + FILE *f; + int len; + int i, val, j; + + s_soundtrack = NULL; + sprintf (name, "%svideo/%s/%s.wav", gamedir, s_base, s_base); + printf ("WAV: %s\n", name); + f = fopen (name, "rb"); + if (!f) + { + printf ("no soundtrack for %s\n", s_base); + return; + } + len = Q_filelength(f); + s_soundtrack = malloc(len); + fread (s_soundtrack, 1, len, f); + fclose (f); + + s_wavinfo = GetWavinfo (name, s_soundtrack, len); + + // count samples for compression + memset (s_samplecounts, 0, sizeof(s_samplecounts)); + + j = s_wavinfo.samples/2; + for (i=0 ; i s_wavinfo.samples || !s_soundtrack) + fwrite (&empty, 1, width, output); + else + fwrite (s_soundtrack + s_wavinfo.dataofs + sample*width, 1, width,output); + } +} + +//========================================================================== + +static float s_resampleXRatio; +static float s_resampleYRatio; + +static void BoxFilterHorizontalElements( unsigned char *dst, unsigned char *src, float s0, float s1 ) +{ + float w; + float rSum = 0, gSum = 0, bSum = 0; + float x = s0; + float sumWeight = 0; + + for ( x = s0; x < s1; x++, src += 4 ) + { + if ( x == s0 ) + { + w = ( int ) ( s0 + 1 ) - x; + } + else if ( x + 1 >= s1 ) + { + w = s1 - ( int ) x; + } + else + { + w = 1.0f; + } + + rSum += src[0] * w; + gSum += src[1] * w; + bSum += src[2] * w; + sumWeight += w; + } + + rSum /= sumWeight; + gSum /= sumWeight; + bSum /= sumWeight; + + dst[0] = ( unsigned char ) ( rSum + 0.5 ); + dst[1] = ( unsigned char ) ( gSum + 0.5 ); + dst[2] = ( unsigned char ) ( bSum + 0.5 ); +} + +static void BoxFilterVerticalElements( unsigned char *dst, // destination of the filter process + unsigned char *src, // source pixels + int srcStep, // stride of the source pixels + float s0, float s1 ) +{ + float w; + float rSum = 0, gSum = 0, bSum = 0; + float y = s0; + float sumWeight = 0; + + for ( y = s0; y < ( int ) ( s1 + 1 ) ; y++, src += srcStep ) + { + if ( y == s0 ) + { + w = ( int ) ( s0 + 1 ) - y; + } + else if ( y + 1 >= s1 ) + { + w = s1 - ( int ) y; + } + else + { + w = 1.0f; + } + + rSum += src[0] * w; + gSum += src[1] * w; + bSum += src[2] * w; + sumWeight += w; + } + + rSum /= sumWeight; + gSum /= sumWeight; + bSum /= sumWeight; + + dst[0] = ( unsigned char ) ( rSum + 0.5 ); + dst[1] = ( unsigned char ) ( gSum + 0.5 ); + dst[2] = ( unsigned char ) ( bSum + 0.5 ); + dst[3] = 0xff; + +} + +static void BoxFilterRow( unsigned char *dstStart, cblock_t *in, int dstRow, int rowWidth ) +{ + int i; + unsigned char *indata = ( unsigned char * ) in->data; + + indata += 4 * dstRow * in->width; + + for ( i = 0; i < rowWidth; i++ ) + { + float c0 = i * s_resampleXRatio; + float c1 = ( i + 1 ) * s_resampleXRatio; + + BoxFilterHorizontalElements( &dstStart[i*4], &indata[( ( int ) c0 ) * 4], c0, c1 ); + } +} + +static void BoxFilterColumn( unsigned char *dstStart, unsigned char *srcStart, int dstCol, int dstRowWidth, int dstColHeight, int srcRowWidthInPels ) +{ + float c0, c1; + int i; + + for ( i = 0; i < dstColHeight; i++ ) + { + c0 = i * s_resampleYRatio; + c1 = ( i + 1 ) * s_resampleYRatio; + + BoxFilterVerticalElements( &dstStart[i*4*dstRowWidth], &srcStart[(int)c0*srcRowWidthInPels*4], srcRowWidthInPels*4, c0, c1 ); + } +} + +#define DROP_SAMPLE 0 +#define BOX_FILTER 1 + +static void ResampleFrame( cblock_t *in, unsigned char *out, int method, int outWidth, int outHeight ) +{ + int row, column; + unsigned char *indata = ( unsigned char * ) in->data; + + s_resampleXRatio = in->width / ( float ) outWidth; + s_resampleYRatio = in->height / ( float ) outHeight; + + if ( method == DROP_SAMPLE ) + { + for ( row = 0; row < outHeight; row++ ) + { + int r = ( int ) ( row * s_resampleYRatio ); + + for ( column = 0; column < outWidth; column++ ) + { + int c = ( int ) ( column * s_resampleXRatio ); + + out[(row*outWidth+column)*4+0] = indata[(r*in->width+c)*4+0]; + out[(row*outWidth+column)*4+1] = indata[(r*in->width+c)*4+1]; + out[(row*outWidth+column)*4+2] = indata[(r*in->width+c)*4+2]; + out[(row*outWidth+column)*4+3] = 0xff; + } + } + } + else if ( method == BOX_FILTER ) + { + unsigned char intermediate[1024*1024*4]; + + assert( in->height <= 1024 ); + assert( in->width <= 1024 ); + + // + // filter our M x N source image into a RESAMPLE_WIDTH x N horizontally filtered image + // + for ( row = 0; row < in->height; row++ ) + { + BoxFilterRow( &intermediate[row*4*outWidth], in, row, outWidth ); + } + + // + // filter our RESAMPLE_WIDTH x N horizontally filtered image into a RESAMPLE_WIDTH x RESAMPLE_HEIGHT filtered image + // + for ( column = 0; column < outWidth; column++ ) + { + BoxFilterColumn( &out[column*4], &intermediate[column*4], column, outWidth, outHeight, s_resample_width ); + } + } +} + +static float BTCDistanceSquared( float a[3], float b[3] ) +{ + return ( b[0] - a[0] ) * ( b[0] - a[0] ) + + ( b[1] - a[1] ) * ( b[1] - a[1] ) + + ( b[2] - a[2] ) * ( b[2] - a[2] ); +} + +static void BTCFindEndpoints( float inBlock[4][4][3], unsigned int endPoints[2][2] ) +{ + float longestDistance = -1; + + int bX, bY; + + // + // find the two points farthest from each other + // + for ( bY = 0; bY < 4; bY++ ) + { + for ( bX = 0; bX < 4; bX++ ) + { + int cX, cY; + float d; + + // + // check the rest of the current row + // + for ( cX = bX + 1; cX < 4; cX++ ) + { + if ( ( d = BTCDistanceSquared( inBlock[bY][bX], inBlock[bY][cX] ) ) > longestDistance ) + { + longestDistance = d; + endPoints[0][0] = bX; + endPoints[0][1] = bY; + endPoints[1][0] = cX; + endPoints[1][1] = bY; + } + } + + // + // check remaining rows and columns + // + for ( cY = bY+1; cY < 4; cY++ ) + { + for ( cX = 0; cX < 4; cX++ ) + { + if ( ( d = BTCDistanceSquared( inBlock[bY][bX], inBlock[cY][cX] ) ) > longestDistance ) + { + longestDistance = d; + endPoints[0][0] = bX; + endPoints[0][1] = bY; + endPoints[1][0] = cX; + endPoints[1][1] = cY; + } + } + } + } + } +} + +static float BTCQuantizeBlock( float inBlock[4][4][3], unsigned long endPoints[2][2], int btcQuantizedBlock[4][4], float bestError ) +{ + int i; + int blockY, blockX; + float dR, dG, dB; + float R, G, B; + float error = 0; + float colorLine[4][3]; + + // + // build the color line + // + dR = inBlock[endPoints[1][1]][endPoints[1][0]][0] - + inBlock[endPoints[0][1]][endPoints[0][0]][0]; + dG = inBlock[endPoints[1][1]][endPoints[1][0]][1] - + inBlock[endPoints[0][1]][endPoints[0][0]][1]; + dB = inBlock[endPoints[1][1]][endPoints[1][0]][2] - + inBlock[endPoints[0][1]][endPoints[0][0]][2]; + + dR *= 0.33f; + dG *= 0.33f; + dB *= 0.33f; + + R = inBlock[endPoints[0][1]][endPoints[0][0]][0]; + G = inBlock[endPoints[0][1]][endPoints[0][0]][1]; + B = inBlock[endPoints[0][1]][endPoints[0][0]][2]; + + for ( i = 0; i < 4; i++ ) + { + colorLine[i][0] = R; + colorLine[i][1] = G; + colorLine[i][2] = B; + + R += dR; + G += dG; + B += dB; + } + + // + // quantize each pixel into the appropriate range + // + for ( blockY = 0; blockY < 4; blockY++ ) + { + for ( blockX = 0; blockX < 4; blockX++ ) + { + float distance = 10000000000; + int shortest = -1; + + for ( i = 0; i < 4; i++ ) + { + float d; + + if ( ( d = BTCDistanceSquared( inBlock[blockY][blockX], colorLine[i] ) ) < distance ) + { + distance = d; + shortest = i; + } + } + + error += distance; + + // + // if bestError is not -1 then that means this is a speculative quantization + // + if ( bestError != -1 ) + { + if ( error > bestError ) + return error; + } + + btcQuantizedBlock[blockY][blockX] = shortest; + } + } + + return error; +} + +/* +** float BTCCompressBlock +*/ +static float BTCCompressBlock( float inBlock[4][4][3], unsigned long out[2] ) +{ + int i; + int btcQuantizedBlock[4][4]; // values should be [0..3] + unsigned long encodedEndPoints, encodedBitmap; + unsigned int endPoints[2][2]; // endPoints[0] = color start, endPoints[1] = color end + int blockY, blockX; + float error = 0; + float bestError = 10000000000; + unsigned int bestEndPoints[2][2]; + +#if 0 + // + // find the "ideal" end points for the color vector + // + BTCFindEndpoints( inBlock, endPoints ); + error = BTCQuantizeBlock( inBlock, endPoints, btcQuantizedBlock ); + memcpy( bestEndPoints, endPoints, sizeof( bestEndPoints ) ); +#else + for ( blockY = 0; blockY < 4; blockY++ ) + { + for ( blockX = 0; blockX < 4; blockX++ ) + { + int x2, y2; + + for ( y2 = 0; y2 < 4; y2++ ) + { + for ( x2 = 0; x2 < 4; x2++ ) + { + if ( ( x2 == blockX ) && ( y2 == blockY ) ) + continue; + + endPoints[0][0] = blockX; + endPoints[0][1] = blockY; + endPoints[1][0] = x2; + endPoints[1][1] = y2; + + error = BTCQuantizeBlock( inBlock, endPoints, btcQuantizedBlock, -1 ); //bestError ); + + if ( error < bestError ) + { + bestError = error; + memcpy( bestEndPoints, endPoints, sizeof( bestEndPoints ) ); + } + } + } + } + } + + error = BTCQuantizeBlock( inBlock, bestEndPoints, btcQuantizedBlock, -1.0f ); +#endif + + // + // encode the results + // + encodedBitmap = 0; + for ( blockY = 0; blockY < 4; blockY++ ) + { + for ( blockX = 0; blockX < 4; blockX++ ) + { + int shift = ( blockX + blockY * 4 ) * 2; + encodedBitmap |= btcQuantizedBlock[blockY][blockX] << shift; + } + } + + // + // encode endpoints + // + encodedEndPoints = 0; + for ( i = 0; i < 2; i++ ) + { + int iR, iG, iB; + + iR = ( ( int ) inBlock[bestEndPoints[i][1]][bestEndPoints[i][0]][0] ); + if ( iR > 255 ) + iR = 255; + else if ( iR < 0 ) + iR = 0; + iR >>= 3; + + iG = ( ( int ) inBlock[bestEndPoints[i][1]][bestEndPoints[i][0]][1] ); + if ( iG > 255 ) + iG = 255; + else if ( iG < 0 ) + iG = 0; + iG >>= 2; + + iB = ( ( int ) inBlock[bestEndPoints[i][1]][bestEndPoints[i][0]][2] ); + if ( iB > 255 ) + iB = 255; + else if ( iB < 0 ) + iB = 0; + iB >>= 3; + + + encodedEndPoints |= ( ( ( iR << 11 ) | ( iG << 5 ) | ( iB ) ) << ( i * 16 ) ); + } + + // + // store + // + out[0] = encodedBitmap; + out[1] = encodedEndPoints; + + return error; +} + +/* +** void BTCDecompressFrame +*/ +static void BTCDecompressFrame( unsigned long *src, unsigned char *dst ) +{ + int x, y; + int iR, iG, iB; + int dstX, dstY; + float colorStart[3], colorEnd[3]; + unsigned char colorRampABGR[4][4]; + unsigned encoded; + + memset( colorRampABGR, 0xff, sizeof( colorRampABGR ) ); + + for ( y = 0; y < s_resample_height / 4; y++ ) + { + for ( x = 0; x < s_resample_width / 4; x++ ) + { + unsigned colorStartPacked = src[(y*s_resample_width/4 + x)*2 + 1] & 0xffff; + unsigned colorEndPacked = src[(y*s_resample_width/4 + x)*2 + 1] >> 16; + + // + // grab the end points + // 0 = color start + // 1 = color end + // + iR = ( ( colorStartPacked >> 11 ) & ( ( 1 << 5 ) - 1 ) ); + iR = ( iR << 3 ) | ( iR >> 2 ); + iG = ( ( colorStartPacked >> 5 ) & ( ( 1 << 6 ) - 1 ) ); + iG = ( iG << 2 ) | ( iG >> 4 ); + iB = ( ( colorStartPacked ) & ( ( 1 << 5 ) - 1 ) ); + iB = ( iB << 3 ) | ( iB >> 2 ); + + colorStart[0] = iR; + colorStart[1] = iG; + colorStart[2] = iB; + colorRampABGR[0][0] = iR; + colorRampABGR[0][1] = iG; + colorRampABGR[0][2] = iB; + + iR = ( ( colorEndPacked >> 11 ) & ( ( 1 << 5 ) - 1 ) ); + iR = ( iR << 3 ) | ( iR >> 2 ); + iG = ( ( colorEndPacked >> 5 ) & ( ( 1 << 6 ) - 1 ) ); + iG = ( iG << 2 ) | ( iG >> 4 ); + iB = ( colorEndPacked & ( ( 1 << 5 ) - 1 ) ); + iB = ( iB << 3 ) | ( iB >> 2 ); + + colorEnd[0] = iR; + colorEnd[1] = iG; + colorEnd[2] = iB; + colorRampABGR[3][0] = iR; + colorRampABGR[3][1] = iG; + colorRampABGR[3][2] = iB; + + // + // compute this block's color ramp + // FIXME: This needs to be reversed on big-endian machines + // + + colorRampABGR[1][0] = colorStart[0] * 0.66f + colorEnd[0] * 0.33f; + colorRampABGR[1][1] = colorStart[1] * 0.66f + colorEnd[1] * 0.33f; + colorRampABGR[1][2] = colorStart[2] * 0.66f + colorEnd[2] * 0.33f; + + colorRampABGR[2][0] = colorStart[0] * 0.33f + colorEnd[0] * 0.66f; + colorRampABGR[2][1] = colorStart[1] * 0.33f + colorEnd[1] * 0.66f; + colorRampABGR[2][2] = colorStart[2] * 0.33f + colorEnd[2] * 0.66f; + + // + // decode the color data + // information is encoded in 2-bit pixels, with low order bits corresponding + // to upper left pixels. These 2-bit values are indexed into the block's + // computer color ramp. + // + encoded = src[(y*s_resample_width/4 + x)*2 + 0]; + + for ( dstY = 0; dstY < 4; dstY++ ) + { + for ( dstX = 0; dstX < 4; dstX++ ) + { + memcpy( &dst[(y*4+dstY)*s_resample_width*4+x*4*4+dstX*4], colorRampABGR[encoded&3], sizeof( colorRampABGR[0] ) ); + encoded >>= 2; + } + } + } + } +} + +/* +** BTCCompressFrame +** +** Perform a BTC compression using a 2-bit encoding at each pixel. This +** compression method is performed by decomposing the incoming image into +** a sequence of 4x4 blocks. At each block two color values are computed +** that define the endpoints of a vector in color space that represent +** the two colors "farthest apart". +*/ +static float BTCCompressFrame( unsigned char *src, unsigned long *dst ) +{ + int x, y; + int bX, bY; + float btcBlock[4][4][3]; + + float error = 0; + + for ( y = 0; y < s_resample_height / 4; y++ ) + { + for ( x = 0; x < s_resample_width / 4; x++ ) + { + // + // fill in the BTC block with raw values + // + for ( bY = 0; bY < 4; bY++ ) + { + for ( bX = 0; bX < 4; bX++ ) + { + btcBlock[bY][bX][0] = src[(y*4+bY)*s_resample_width*4 + (x*4+bX)*4 + 0]; + btcBlock[bY][bX][1] = src[(y*4+bY)*s_resample_width*4 + (x*4+bX)*4 + 1]; + btcBlock[bY][bX][2] = src[(y*4+bY)*s_resample_width*4 + (x*4+bX)*4 + 2]; + } + } + + error += BTCCompressBlock( btcBlock, &dst[(y*s_resample_width/4+x)*2] ); + } + } + + return error / ( ( s_resample_width / 4 ) * ( s_resample_height / 4 ) ); +} + +/* +=================== +LoadFrame +=================== +*/ +cblock_t LoadFrame (char *base, int frame, int digits, byte **palette) +{ + int ten3, ten2, ten1, ten0; + cblock_t in; + int width, height; + char name[1024]; + FILE *f; + + in.data = NULL; + in.count = -1; + + ten3 = frame/1000; + ten2 = (frame-ten3*1000)/100; + ten1 = (frame-ten3*1000-ten2*100)/10; + ten0 = frame%10; + + if (digits == 4) + sprintf (name, "%svideo/%s/%s%i%i%i%i.tga", gamedir, base, base, ten3, ten2, ten1, ten0); + else + sprintf (name, "%svideo/%s/%s%i%i%i.tga", gamedir, base, base, ten2, ten1, ten0); + + f = fopen(name, "rb"); + if (!f) + { + in.data = NULL; + return in; + } + fclose (f); + + printf ("%s", name); + LoadTGA( name, ( unsigned char ** ) &in.data, &width, &height ); + if ( palette ) + *palette = 0; +// Load256Image (name, &in.data, palette, &width, &height); + in.count = width*height; + in.width = width; + in.height = height; +// FIXME: map 0 and 255! + +#if 0 + // rle compress + rle = RLE(in); + free (in.data); + + return rle; +#endif + + return in; +} + +/* +=============== +Cmd_Video + +video +=============== +*/ +void Cmd_Video (void) +{ + float sumError = 0, error = 0, maxError = 0; + char savename[1024]; + char name[1024]; + FILE *output; + int startframe, frame; + int width, height; + int i; + int digits; + int minutes; + float fseconds; + int remSeconds; + cblock_t in; + unsigned char *resampled; + unsigned long *compressed; + clock_t start, stop; + + GetToken (qfalse); + strcpy (s_base, token); + if (g_release) + { +// sprintf (savename, "video/%s.cin", token); +// ReleaseFile (savename); + return; + } + + GetToken( qfalse ); + strcpy( s_output_base, token ); + + GetToken (qfalse); + digits = atoi(token); + + GetToken( qfalse ); + + if ( !strcmp( token, "btc" ) ) + { + s_compression_method = BTC_COMPRESSION; + printf( "Compression: BTC\n" ); + } + else if ( !strcmp( token, "uc" ) ) + { + s_compression_method = UNCOMPRESSED; + printf( "Compression: none\n" ); + } + else + { + Error( "Uknown compression method '%s'\n", token ); + } + + GetToken( qfalse ); + s_resample_width = atoi( token ); + + GetToken( qfalse ); + s_resample_height = atoi( token ); + + resampled = malloc( sizeof( unsigned char ) * 4 * s_resample_width * s_resample_height ); + compressed = malloc( sizeof( long ) * 2 * ( s_resample_width / 4 ) * ( s_resample_height / 4 ) ); + + printf( "Resample width: %d\n", s_resample_width ); + printf( "Resample height: %d\n", s_resample_height ); + + // optionally skip frames + if (TokenAvailable ()) + { + GetToken (qfalse); + startframe = atoi(token); + } + else + startframe=0; + + sprintf (savename, "%svideo/%s.%s", writedir, s_output_base, CIN_EXTENSION ); + + // load the entire sound wav file if present + LoadSoundtrack (); + + if (digits == 4) + sprintf (name, "%svideo/%s/%s0000.tga", gamedir, s_base, s_base); + else + sprintf (name, "%svideo/%s/%s000.tga", gamedir, s_base, s_base); + + printf ("%s\n", name); + LoadTGA( name, NULL, &width, &height); + + output = fopen (savename, "wb"); + if (!output) + Error ("Can't open %s", savename); + + // write header info + i = LittleLong( CIN_SIGNATURE ); + fwrite (&i, 4, 1, output ); + i = LittleLong (s_resample_width); + fwrite (&i, 4, 1, output); + i = LittleLong (s_resample_height); + fwrite (&i, 4, 1, output); + i = LittleLong (s_wavinfo.rate); + fwrite (&i, 4, 1, output); + i = LittleLong (s_wavinfo.width); + fwrite (&i, 4, 1, output); + i = LittleLong (s_wavinfo.channels); + fwrite (&i, 4, 1, output); + i = LittleLong ( s_compression_method ); + fwrite (&i, 4, 1, output ); + + start = clock(); + + // perform compression on a per frame basis + for ( frame=startframe ; ; frame++) + { + printf ("%02d: ", frame); + in = LoadFrame (s_base, frame, digits, 0 ); + if (!in.data) + break; + + ResampleFrame( &in, ( unsigned char * ) resampled, BOX_FILTER, s_resample_width, s_resample_height ); + + if ( s_compression_method == UNCOMPRESSED ) + { + printf( "\n" ); + fwrite( resampled, 1, sizeof( unsigned char ) * s_resample_width * s_resample_height * 4, output ); + +#if OUTPUT_TGAS + { + int x, y; + char buffer[1000]; + + for ( y = 0; y < s_resample_height/2; y++ ) + { + for ( x = 0; x < s_resample_width; x++ ) + { + unsigned char tmp[4]; + + tmp[0] = resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 0]; + tmp[1] = resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 1]; + tmp[2] = resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 2]; + tmp[3] = resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 3]; + + resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 0] = resampled[y*s_resample_width*4 + x*4 + 0]; + resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 1] = resampled[y*s_resample_width*4 + x*4 + 1]; + resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 2] = resampled[y*s_resample_width*4 + x*4 + 2]; + resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 3] = resampled[y*s_resample_width*4 + x*4 + 3]; + + resampled[y*s_resample_width*4 + x*4 + 0] = tmp[0]; + resampled[y*s_resample_width*4 + x*4 + 1] = tmp[1]; + resampled[y*s_resample_width*4 + x*4 + 2] = tmp[2]; + resampled[y*s_resample_width*4 + x*4 + 3] = tmp[3]; + } + } + + sprintf( buffer, "%svideo/%s/uc%04d.tga", gamedir, s_base, frame ); + WriteTGA( buffer, resampled, s_resample_width, s_resample_height ); + } +#endif + } + else if ( s_compression_method == BTC_COMPRESSION ) + { + error = BTCCompressFrame( resampled, compressed ); + + sumError += error; + + if ( error > maxError ) + maxError = error; + + printf( " (error = %f)\n", error ); + fwrite( compressed, 1, 2 * sizeof( long ) * ( s_resample_width / 4 ) * ( s_resample_height / 4 ), output ); + +#if OUTPUT_TGAS + { + int x, y; + unsigned char *uncompressed; + char buffer[1000]; + + uncompressed = malloc( sizeof( unsigned char ) * 4 * s_resample_width * s_resample_height ); + BTCDecompressFrame( compressed, uncompressed ); + + for ( y = 0; y < s_resample_height/2; y++ ) + { + for ( x = 0; x < s_resample_width; x++ ) + { + unsigned char tmp[4]; + + tmp[0] = uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 0]; + tmp[1] = uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 1]; + tmp[2] = uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 2]; + tmp[3] = uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 3]; + + uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 0] = uncompressed[y*s_resample_width*4 + x*4 + 0]; + uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 1] = uncompressed[y*s_resample_width*4 + x*4 + 1]; + uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 2] = uncompressed[y*s_resample_width*4 + x*4 + 2]; + uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 3] = uncompressed[y*s_resample_width*4 + x*4 + 3]; + + uncompressed[y*s_resample_width*4 + x*4 + 0] = tmp[0]; + uncompressed[y*s_resample_width*4 + x*4 + 1] = tmp[1]; + uncompressed[y*s_resample_width*4 + x*4 + 2] = tmp[2]; + uncompressed[y*s_resample_width*4 + x*4 + 3] = tmp[3]; + } + } + + + sprintf( buffer, "%svideo/%s/btc%04d.tga", gamedir, s_base, frame ); + WriteTGA( buffer, uncompressed, s_resample_width, s_resample_height ); + + free( uncompressed ); + } +#endif + } + + WriteSound( output, frame ); + + free (in.data); + } + stop = clock(); + + printf ("\n"); + + printf ("Total size: %i\n", ftell( output ) ); + printf ("Average error: %f\n", sumError / ( frame - startframe ) ); + printf ("Max error: %f\n", maxError ); + + fseconds = ( stop - start ) / 1000.0f; + minutes = fseconds / 60; + remSeconds = fseconds - minutes * 60; + + printf ("Total time: %d s (%d m %d s)\n", ( int ) fseconds, minutes, remSeconds ); + printf ("Time/frame: %.2f seconds\n", fseconds / ( frame - startframe ) ); + + fclose (output); + + if ( s_soundtrack ) + { + free( s_soundtrack ); + s_soundtrack = 0; + } +} diff --git a/utils/roq2/jpeg/cderror.h b/utils/roq2/jpeg/cderror.h new file mode 100644 index 0000000..d6425ca --- /dev/null +++ b/utils/roq2/jpeg/cderror.h @@ -0,0 +1,264 @@ +/* + + * cderror.h + + * + + * Copyright (C) 1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file defines the error and message codes for the cjpeg/djpeg + + * applications. These strings are not needed as part of the JPEG library + + * proper. + + * Edit this file to add new codes, or to translate the message strings to + + * some other language. + + */ + + + +/* + + * To define the enum list of message codes, include this file without + + * defining macro JMESSAGE. To create a message string table, include it + + * again with a suitable JMESSAGE definition (see jerror.c for an example). + + */ + +#ifndef JMESSAGE + +#ifndef CDERROR_H + +#define CDERROR_H + +/* First time through, define the enum list */ + +#define JMAKE_ENUM_LIST + +#else + +/* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */ + +#define JMESSAGE(code,string) + +#endif /* CDERROR_H */ + +#endif /* JMESSAGE */ + + + +#ifdef JMAKE_ENUM_LIST + + + +typedef enum { + + + +#define JMESSAGE(code,string) code , + + + +#endif /* JMAKE_ENUM_LIST */ + + + +JMESSAGE(JMSG_FIRSTADDONCODE=1000, NULL) /* Must be first entry! */ + + + +#ifdef BMP_SUPPORTED + +JMESSAGE(JERR_BMP_BADCMAP, "Unsupported BMP colormap format") + +JMESSAGE(JERR_BMP_BADDEPTH, "Only 8- and 24-bit BMP files are supported") + +JMESSAGE(JERR_BMP_BADHEADER, "Invalid BMP file: bad header length") + +JMESSAGE(JERR_BMP_BADPLANES, "Invalid BMP file: biPlanes not equal to 1") + +JMESSAGE(JERR_BMP_COLORSPACE, "BMP output must be grayscale or RGB") + +JMESSAGE(JERR_BMP_COMPRESSED, "Sorry, compressed BMPs not yet supported") + +JMESSAGE(JERR_BMP_NOT, "Not a BMP file - does not start with BM") + +JMESSAGE(JTRC_BMP, "%ux%u 24-bit BMP image") + +JMESSAGE(JTRC_BMP_MAPPED, "%ux%u 8-bit colormapped BMP image") + +JMESSAGE(JTRC_BMP_OS2, "%ux%u 24-bit OS2 BMP image") + +JMESSAGE(JTRC_BMP_OS2_MAPPED, "%ux%u 8-bit colormapped OS2 BMP image") + +#endif /* BMP_SUPPORTED */ + + + +#ifdef GIF_SUPPORTED + +JMESSAGE(JERR_GIF_BUG, "GIF output got confused") + +JMESSAGE(JERR_GIF_CODESIZE, "Bogus GIF codesize %d") + +JMESSAGE(JERR_GIF_COLORSPACE, "GIF output must be grayscale or RGB") + +JMESSAGE(JERR_GIF_IMAGENOTFOUND, "Too few images in GIF file") + +JMESSAGE(JERR_GIF_NOT, "Not a GIF file") + +JMESSAGE(JTRC_GIF, "%ux%ux%d GIF image") + +JMESSAGE(JTRC_GIF_BADVERSION, + + "Warning: unexpected GIF version number '%c%c%c'") + +JMESSAGE(JTRC_GIF_EXTENSION, "Ignoring GIF extension block of type 0x%02x") + +JMESSAGE(JTRC_GIF_NONSQUARE, "Caution: nonsquare pixels in input") + +JMESSAGE(JWRN_GIF_BADDATA, "Corrupt data in GIF file") + +JMESSAGE(JWRN_GIF_CHAR, "Bogus char 0x%02x in GIF file, ignoring") + +JMESSAGE(JWRN_GIF_ENDCODE, "Premature end of GIF image") + +JMESSAGE(JWRN_GIF_NOMOREDATA, "Ran out of GIF bits") + +#endif /* GIF_SUPPORTED */ + + + +#ifdef PPM_SUPPORTED + +JMESSAGE(JERR_PPM_COLORSPACE, "PPM output must be grayscale or RGB") + +JMESSAGE(JERR_PPM_NONNUMERIC, "Nonnumeric data in PPM file") + +JMESSAGE(JERR_PPM_NOT, "Not a PPM file") + +JMESSAGE(JTRC_PGM, "%ux%u PGM image") + +JMESSAGE(JTRC_PGM_TEXT, "%ux%u text PGM image") + +JMESSAGE(JTRC_PPM, "%ux%u PPM image") + +JMESSAGE(JTRC_PPM_TEXT, "%ux%u text PPM image") + +#endif /* PPM_SUPPORTED */ + + + +#ifdef RLE_SUPPORTED + +JMESSAGE(JERR_RLE_BADERROR, "Bogus error code from RLE library") + +JMESSAGE(JERR_RLE_COLORSPACE, "RLE output must be grayscale or RGB") + +JMESSAGE(JERR_RLE_DIMENSIONS, "Image dimensions (%ux%u) too large for RLE") + +JMESSAGE(JERR_RLE_EMPTY, "Empty RLE file") + +JMESSAGE(JERR_RLE_EOF, "Premature EOF in RLE header") + +JMESSAGE(JERR_RLE_MEM, "Insufficient memory for RLE header") + +JMESSAGE(JERR_RLE_NOT, "Not an RLE file") + +JMESSAGE(JERR_RLE_TOOMANYCHANNELS, "Cannot handle %d output channels for RLE") + +JMESSAGE(JERR_RLE_UNSUPPORTED, "Cannot handle this RLE setup") + +JMESSAGE(JTRC_RLE, "%ux%u full-color RLE file") + +JMESSAGE(JTRC_RLE_FULLMAP, "%ux%u full-color RLE file with map of length %d") + +JMESSAGE(JTRC_RLE_GRAY, "%ux%u grayscale RLE file") + +JMESSAGE(JTRC_RLE_MAPGRAY, "%ux%u grayscale RLE file with map of length %d") + +JMESSAGE(JTRC_RLE_MAPPED, "%ux%u colormapped RLE file with map of length %d") + +#endif /* RLE_SUPPORTED */ + + + +#ifdef TARGA_SUPPORTED + +JMESSAGE(JERR_TGA_BADCMAP, "Unsupported Targa colormap format") + +JMESSAGE(JERR_TGA_BADPARMS, "Invalid or unsupported Targa file") + +JMESSAGE(JERR_TGA_COLORSPACE, "Targa output must be grayscale or RGB") + +JMESSAGE(JTRC_TGA, "%ux%u RGB Targa image") + +JMESSAGE(JTRC_TGA_GRAY, "%ux%u grayscale Targa image") + +JMESSAGE(JTRC_TGA_MAPPED, "%ux%u colormapped Targa image") + +#else + +JMESSAGE(JERR_TGA_NOTCOMP, "Targa support was not compiled") + +#endif /* TARGA_SUPPORTED */ + + + +JMESSAGE(JERR_BAD_CMAP_FILE, + + "Color map file is invalid or of unsupported format") + +JMESSAGE(JERR_TOO_MANY_COLORS, + + "Output file format cannot handle %d colormap entries") + +JMESSAGE(JERR_UNGETC_FAILED, "ungetc failed") + +#ifdef TARGA_SUPPORTED + +JMESSAGE(JERR_UNKNOWN_FORMAT, + + "Unrecognized input file format --- perhaps you need -targa") + +#else + +JMESSAGE(JERR_UNKNOWN_FORMAT, "Unrecognized input file format") + +#endif + +JMESSAGE(JERR_UNSUPPORTED_FORMAT, "Unsupported output file format") + + + +#ifdef JMAKE_ENUM_LIST + + + + JMSG_LASTADDONCODE + +} ADDON_MESSAGE_CODE; + + + +#undef JMAKE_ENUM_LIST + +#endif /* JMAKE_ENUM_LIST */ + + + +/* Zap JMESSAGE macro so that future re-inclusions do nothing by default */ + +#undef JMESSAGE + diff --git a/utils/roq2/jpeg/cdjpeg.c b/utils/roq2/jpeg/cdjpeg.c new file mode 100644 index 0000000..e9106a1 --- /dev/null +++ b/utils/roq2/jpeg/cdjpeg.c @@ -0,0 +1,358 @@ +/* + + * cdjpeg.c + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains common support routines used by the IJG application + + * programs (cjpeg, djpeg, jpegtran). + + */ + + + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#include /* to declare isupper(), tolower() */ + +#ifdef NEED_SIGNAL_CATCHER + +#include /* to declare signal() */ + +#endif + +#ifdef USE_SETMODE + +#include /* to declare setmode()'s parameter macros */ + +/* If you have setmode() but not , just delete this line: */ + +#include /* to declare setmode() */ + +#endif + + + + + +/* + + * Signal catcher to ensure that temporary files are removed before aborting. + + * NB: for Amiga Manx C this is actually a global routine named _abort(); + + * we put "#define signal_catcher _abort" in jconfig.h. Talk about bogus... + + */ + + + +#ifdef NEED_SIGNAL_CATCHER + + + +static j_common_ptr sig_cinfo; + + + +GLOBAL void /* must be global for Manx C */ + +signal_catcher (int signum) + +{ + + if (sig_cinfo != NULL) { + + if (sig_cinfo->err != NULL) /* turn off trace output */ + + sig_cinfo->err->trace_level = 0; + + jpeg_destroy(sig_cinfo); /* clean up memory allocation & temp files */ + + } + + exit(EXIT_FAILURE); + +} + + + + + +GLOBAL void + +enable_signal_catcher (j_common_ptr cinfo) + +{ + + sig_cinfo = cinfo; + + signal(SIGINT, signal_catcher); + +#ifdef SIGTERM /* not all systems have SIGTERM */ + + signal(SIGTERM, signal_catcher); + +#endif + +} + + + +#endif + + + + + +/* + + * Optional progress monitor: display a percent-done figure on stderr. + + */ + + + +#ifdef PROGRESS_REPORT + + + +METHODDEF void + +progress_monitor (j_common_ptr cinfo) + +{ + + cd_progress_ptr prog = (cd_progress_ptr) cinfo->progress; + + int total_passes = prog->pub.total_passes + prog->total_extra_passes; + + int percent_done = (int) (prog->pub.pass_counter*100L/prog->pub.pass_limit); + + + + if (percent_done != prog->percent_done) { + + prog->percent_done = percent_done; + + if (total_passes > 1) { + + fprintf(stderr, "\rPass %d/%d: %3d%% ", + + prog->pub.completed_passes + prog->completed_extra_passes + 1, + + total_passes, percent_done); + + } else { + + fprintf(stderr, "\r %3d%% ", percent_done); + + } + + fflush(stderr); + + } + +} + + + + + +GLOBAL void + +start_progress_monitor (j_common_ptr cinfo, cd_progress_ptr progress) + +{ + + /* Enable progress display, unless trace output is on */ + + if (cinfo->err->trace_level == 0) { + + progress->pub.progress_monitor = progress_monitor; + + progress->completed_extra_passes = 0; + + progress->total_extra_passes = 0; + + progress->percent_done = -1; + + cinfo->progress = &progress->pub; + + } + +} + + + + + +GLOBAL void + +end_progress_monitor (j_common_ptr cinfo) + +{ + + /* Clear away progress display */ + + if (cinfo->err->trace_level == 0) { + + fprintf(stderr, "\r \r"); + + fflush(stderr); + + } + +} + + + +#endif + + + + + +/* + + * Case-insensitive matching of possibly-abbreviated keyword switches. + + * keyword is the constant keyword (must be lower case already), + + * minchars is length of minimum legal abbreviation. + + */ + + + +GLOBAL boolean + +keymatch (char * arg, const char * keyword, int minchars) + +{ + + register int ca, ck; + + register int nmatched = 0; + + + + while ((ca = *arg++) != '\0') { + + if ((ck = *keyword++) == '\0') + + return FALSE; /* arg longer than keyword, no good */ + + if (isupper(ca)) /* force arg to lcase (assume ck is already) */ + + ca = tolower(ca); + + if (ca != ck) + + return FALSE; /* no good */ + + nmatched++; /* count matched characters */ + + } + + /* reached end of argument; fail if it's too short for unique abbrev */ + + if (nmatched < minchars) + + return FALSE; + + return TRUE; /* A-OK */ + +} + + + + + +/* + + * Routines to establish binary I/O mode for stdin and stdout. + + * Non-Unix systems often require some hacking to get out of text mode. + + */ + + + +GLOBAL FILE * + +read_stdin (void) + +{ + + FILE * input_file = stdin; + + + +#ifdef USE_SETMODE /* need to hack file mode? */ + + setmode(fileno(stdin), O_BINARY); + +#endif + +#ifdef USE_FDOPEN /* need to re-open in binary mode? */ + + if ((input_file = fdopen(fileno(stdin), READ_BINARY)) == NULL) { + + fprintf(stderr, "Cannot reopen stdin\n"); + + exit(EXIT_FAILURE); + + } + +#endif + + return input_file; + +} + + + + + +GLOBAL FILE * + +write_stdout (void) + +{ + + FILE * output_file = stdout; + + + +#ifdef USE_SETMODE /* need to hack file mode? */ + + setmode(fileno(stdout), O_BINARY); + +#endif + +#ifdef USE_FDOPEN /* need to re-open in binary mode? */ + + if ((output_file = fdopen(fileno(stdout), WRITE_BINARY)) == NULL) { + + fprintf(stderr, "Cannot reopen stdout\n"); + + exit(EXIT_FAILURE); + + } + +#endif + + return output_file; + +} + diff --git a/utils/roq2/jpeg/cdjpeg.h b/utils/roq2/jpeg/cdjpeg.h new file mode 100644 index 0000000..347e131 --- /dev/null +++ b/utils/roq2/jpeg/cdjpeg.h @@ -0,0 +1,358 @@ +/* + + * cdjpeg.h + + * + + * Copyright (C) 1994-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains common declarations for the sample applications + + * cjpeg and djpeg. It is NOT used by the core JPEG library. + + */ + + + +#define JPEG_CJPEG_DJPEG /* define proper options in jconfig.h */ + +#define JPEG_INTERNAL_OPTIONS /* cjpeg.c,djpeg.c need to see xxx_SUPPORTED */ + +#include "jinclude.h" + +#include "jpeglib.h" + +#include "jerror.h" /* get library error codes too */ + +#include "cderror.h" /* get application-specific error codes */ + + + + + +/* + + * Object interface for cjpeg's source file decoding modules + + */ + + + +typedef struct cjpeg_source_struct * cjpeg_source_ptr; + + + +struct cjpeg_source_struct { + + JMETHOD(void, start_input, (j_compress_ptr cinfo, + + cjpeg_source_ptr sinfo)); + + JMETHOD(JDIMENSION, get_pixel_rows, (j_compress_ptr cinfo, + + cjpeg_source_ptr sinfo)); + + JMETHOD(void, finish_input, (j_compress_ptr cinfo, + + cjpeg_source_ptr sinfo)); + + + + FILE *input_file; + + + + JSAMPARRAY buffer; + + JDIMENSION buffer_height; + +}; + + + + + +/* + + * Object interface for djpeg's output file encoding modules + + */ + + + +typedef struct djpeg_dest_struct * djpeg_dest_ptr; + + + +struct djpeg_dest_struct { + + /* start_output is called after jpeg_start_decompress finishes. + + * The color map will be ready at this time, if one is needed. + + */ + + JMETHOD(void, start_output, (j_decompress_ptr cinfo, + + djpeg_dest_ptr dinfo)); + + /* Emit the specified number of pixel rows from the buffer. */ + + JMETHOD(void, put_pixel_rows, (j_decompress_ptr cinfo, + + djpeg_dest_ptr dinfo, + + JDIMENSION rows_supplied)); + + /* Finish up at the end of the image. */ + + JMETHOD(void, finish_output, (j_decompress_ptr cinfo, + + djpeg_dest_ptr dinfo)); + + + + /* Target file spec; filled in by djpeg.c after object is created. */ + + FILE * output_file; + + + + /* Output pixel-row buffer. Created by module init or start_output. + + * Width is cinfo->output_width * cinfo->output_components; + + * height is buffer_height. + + */ + + JSAMPARRAY buffer; + + JDIMENSION buffer_height; + +}; + + + + + +/* + + * cjpeg/djpeg may need to perform extra passes to convert to or from + + * the source/destination file format. The JPEG library does not know + + * about these passes, but we'd like them to be counted by the progress + + * monitor. We use an expanded progress monitor object to hold the + + * additional pass count. + + */ + + + +struct cdjpeg_progress_mgr { + + struct jpeg_progress_mgr pub; /* fields known to JPEG library */ + + int completed_extra_passes; /* extra passes completed */ + + int total_extra_passes; /* total extra */ + + /* last printed percentage stored here to avoid multiple printouts */ + + int percent_done; + +}; + + + +typedef struct cdjpeg_progress_mgr * cd_progress_ptr; + + + + + +/* Short forms of external names for systems with brain-damaged linkers. */ + + + +#ifdef NEED_SHORT_EXTERNAL_NAMES + +#define jinit_read_bmp jIRdBMP + +#define jinit_write_bmp jIWrBMP + +#define jinit_read_gif jIRdGIF + +#define jinit_write_gif jIWrGIF + +#define jinit_read_ppm jIRdPPM + +#define jinit_write_ppm jIWrPPM + +#define jinit_read_rle jIRdRLE + +#define jinit_write_rle jIWrRLE + +#define jinit_read_targa jIRdTarga + +#define jinit_write_targa jIWrTarga + +#define read_quant_tables RdQTables + +#define read_scan_script RdScnScript + +#define set_quant_slots SetQSlots + +#define set_sample_factors SetSFacts + +#define read_color_map RdCMap + +#define enable_signal_catcher EnSigCatcher + +#define start_progress_monitor StProgMon + +#define end_progress_monitor EnProgMon + +#define read_stdin RdStdin + +#define write_stdout WrStdout + +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + + +/* Module selection routines for I/O modules. */ + + + +EXTERN cjpeg_source_ptr jinit_read_bmp JPP((j_compress_ptr cinfo)); + +EXTERN djpeg_dest_ptr jinit_write_bmp JPP((j_decompress_ptr cinfo, + + boolean is_os2)); + +EXTERN cjpeg_source_ptr jinit_read_gif JPP((j_compress_ptr cinfo)); + +EXTERN djpeg_dest_ptr jinit_write_gif JPP((j_decompress_ptr cinfo)); + +EXTERN cjpeg_source_ptr jinit_read_ppm JPP((j_compress_ptr cinfo)); + +EXTERN djpeg_dest_ptr jinit_write_ppm JPP((j_decompress_ptr cinfo)); + +EXTERN cjpeg_source_ptr jinit_read_rle JPP((j_compress_ptr cinfo)); + +EXTERN djpeg_dest_ptr jinit_write_rle JPP((j_decompress_ptr cinfo)); + +EXTERN cjpeg_source_ptr jinit_read_targa JPP((j_compress_ptr cinfo)); + +EXTERN djpeg_dest_ptr jinit_write_targa JPP((j_decompress_ptr cinfo)); + + + +/* cjpeg support routines (in rdswitch.c) */ + + + +EXTERN boolean read_quant_tables JPP((j_compress_ptr cinfo, char * filename, + + int scale_factor, boolean force_baseline)); + +EXTERN boolean read_scan_script JPP((j_compress_ptr cinfo, char * filename)); + +EXTERN boolean set_quant_slots JPP((j_compress_ptr cinfo, char *arg)); + +EXTERN boolean set_sample_factors JPP((j_compress_ptr cinfo, char *arg)); + + + +/* djpeg support routines (in rdcolmap.c) */ + + + +EXTERN void read_color_map JPP((j_decompress_ptr cinfo, FILE * infile)); + + + +/* common support routines (in cdjpeg.c) */ + + + +EXTERN void enable_signal_catcher JPP((j_common_ptr cinfo)); + +EXTERN void start_progress_monitor JPP((j_common_ptr cinfo, + + cd_progress_ptr progress)); + +EXTERN void end_progress_monitor JPP((j_common_ptr cinfo)); + +EXTERN boolean keymatch JPP((char * arg, const char * keyword, int minchars)); + +EXTERN FILE * read_stdin JPP((void)); + +EXTERN FILE * write_stdout JPP((void)); + + + +/* miscellaneous useful macros */ + + + +#ifdef DONT_USE_B_MODE /* define mode parameters for fopen() */ + +#define READ_BINARY "r" + +#define WRITE_BINARY "w" + +#else + +#define READ_BINARY "rb" + +#define WRITE_BINARY "wb" + +#endif + + + +#ifndef EXIT_FAILURE /* define exit() codes if not provided */ + +#define EXIT_FAILURE 1 + +#endif + +#ifndef EXIT_SUCCESS + +#ifdef VMS + +#define EXIT_SUCCESS 1 /* VMS is very nonstandard */ + +#else + +#define EXIT_SUCCESS 0 + +#endif + +#endif + +#ifndef EXIT_WARNING + +#ifdef VMS + +#define EXIT_WARNING 1 /* VMS is very nonstandard */ + +#else + +#define EXIT_WARNING 2 + +#endif + +#endif + diff --git a/utils/roq2/jpeg/jcapimin.c b/utils/roq2/jpeg/jcapimin.c new file mode 100644 index 0000000..c0e38ac --- /dev/null +++ b/utils/roq2/jpeg/jcapimin.c @@ -0,0 +1,456 @@ +/* + + * jcapimin.c + + * + + * Copyright (C) 1994-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains application interface code for the compression half + + * of the JPEG library. These are the "minimum" API routines that may be + + * needed in either the normal full-compression case or the transcoding-only + + * case. + + * + + * Most of the routines intended to be called directly by an application + + * are in this file or in jcapistd.c. But also see jcparam.c for + + * parameter-setup helper routines, jcomapi.c for routines shared by + + * compression and decompression, and jctrans.c for the transcoding case. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + + + +/* + + * Initialization of a JPEG compression object. + + * The error manager must already be set up (in case memory manager fails). + + */ + + + +GLOBAL void + +jpeg_create_compress (j_compress_ptr cinfo) + +{ + + int i; + + + + /* For debugging purposes, zero the whole master structure. + + * But error manager pointer is already there, so save and restore it. + + */ + + { + + struct jpeg_error_mgr * err = cinfo->err; + + MEMZERO(cinfo, SIZEOF(struct jpeg_compress_struct)); + + cinfo->err = err; + + } + + cinfo->is_decompressor = FALSE; + + + + /* Initialize a memory manager instance for this object */ + + jinit_memory_mgr((j_common_ptr) cinfo); + + + + /* Zero out pointers to permanent structures. */ + + cinfo->progress = NULL; + + cinfo->dest = NULL; + + + + cinfo->comp_info = NULL; + + + + for (i = 0; i < NUM_QUANT_TBLS; i++) + + cinfo->quant_tbl_ptrs[i] = NULL; + + + + for (i = 0; i < NUM_HUFF_TBLS; i++) { + + cinfo->dc_huff_tbl_ptrs[i] = NULL; + + cinfo->ac_huff_tbl_ptrs[i] = NULL; + + } + + + + cinfo->input_gamma = 1.0; /* in case application forgets */ + + + + /* OK, I'm ready */ + + cinfo->global_state = CSTATE_START; + +} + + + + + +/* + + * Destruction of a JPEG compression object + + */ + + + +GLOBAL void + +jpeg_destroy_compress (j_compress_ptr cinfo) + +{ + + jpeg_destroy((j_common_ptr) cinfo); /* use common routine */ + +} + + + + + +/* + + * Abort processing of a JPEG compression operation, + + * but don't destroy the object itself. + + */ + + + +GLOBAL void + +jpeg_abort_compress (j_compress_ptr cinfo) + +{ + + jpeg_abort((j_common_ptr) cinfo); /* use common routine */ + +} + + + + + +/* + + * Forcibly suppress or un-suppress all quantization and Huffman tables. + + * Marks all currently defined tables as already written (if suppress) + + * or not written (if !suppress). This will control whether they get emitted + + * by a subsequent jpeg_start_compress call. + + * + + * This routine is exported for use by applications that want to produce + + * abbreviated JPEG datastreams. It logically belongs in jcparam.c, but + + * since it is called by jpeg_start_compress, we put it here --- otherwise + + * jcparam.o would be linked whether the application used it or not. + + */ + + + +GLOBAL void + +jpeg_suppress_tables (j_compress_ptr cinfo, boolean suppress) + +{ + + int i; + + JQUANT_TBL * qtbl; + + JHUFF_TBL * htbl; + + + + for (i = 0; i < NUM_QUANT_TBLS; i++) { + + if ((qtbl = cinfo->quant_tbl_ptrs[i]) != NULL) + + qtbl->sent_table = suppress; + + } + + + + for (i = 0; i < NUM_HUFF_TBLS; i++) { + + if ((htbl = cinfo->dc_huff_tbl_ptrs[i]) != NULL) + + htbl->sent_table = suppress; + + if ((htbl = cinfo->ac_huff_tbl_ptrs[i]) != NULL) + + htbl->sent_table = suppress; + + } + +} + + + + + +/* + + * Finish JPEG compression. + + * + + * If a multipass operating mode was selected, this may do a great deal of + + * work including most of the actual output. + + */ + + + +GLOBAL void + +jpeg_finish_compress (j_compress_ptr cinfo) + +{ + + JDIMENSION iMCU_row; + + + + if (cinfo->global_state == CSTATE_SCANNING || + + cinfo->global_state == CSTATE_RAW_OK) { + + /* Terminate first pass */ + + if (cinfo->next_scanline < cinfo->image_height) + + ERREXIT(cinfo, JERR_TOO_LITTLE_DATA); + + (*cinfo->master->finish_pass) (cinfo); + + } else if (cinfo->global_state != CSTATE_WRCOEFS) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* Perform any remaining passes */ + + while (! cinfo->master->is_last_pass) { + + (*cinfo->master->prepare_for_pass) (cinfo); + + for (iMCU_row = 0; iMCU_row < cinfo->total_iMCU_rows; iMCU_row++) { + + if (cinfo->progress != NULL) { + + cinfo->progress->pass_counter = (long) iMCU_row; + + cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows; + + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + + } + + /* We bypass the main controller and invoke coef controller directly; + + * all work is being done from the coefficient buffer. + + */ + + if (! (*cinfo->coef->compress_data) (cinfo, (JSAMPIMAGE) NULL)) + + ERREXIT(cinfo, JERR_CANT_SUSPEND); + + } + + (*cinfo->master->finish_pass) (cinfo); + + } + + /* Write EOI, do final cleanup */ + + (*cinfo->marker->write_file_trailer) (cinfo); + + (*cinfo->dest->term_destination) (cinfo); + + /* We can use jpeg_abort to release memory and reset global_state */ + + jpeg_abort((j_common_ptr) cinfo); + +} + + + + + +/* + + * Write a special marker. + + * This is only recommended for writing COM or APPn markers. + + * Must be called after jpeg_start_compress() and before + + * first call to jpeg_write_scanlines() or jpeg_write_raw_data(). + + */ + + + +GLOBAL void + +jpeg_write_marker (j_compress_ptr cinfo, int marker, + + const JOCTET *dataptr, unsigned int datalen) + +{ + + if (cinfo->next_scanline != 0 || + + (cinfo->global_state != CSTATE_SCANNING && + + cinfo->global_state != CSTATE_RAW_OK && + + cinfo->global_state != CSTATE_WRCOEFS)) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + + + (*cinfo->marker->write_any_marker) (cinfo, marker, dataptr, datalen); + +} + + + + + +/* + + * Alternate compression function: just write an abbreviated table file. + + * Before calling this, all parameters and a data destination must be set up. + + * + + * To produce a pair of files containing abbreviated tables and abbreviated + + * image data, one would proceed as follows: + + * + + * initialize JPEG object + + * set JPEG parameters + + * set destination to table file + + * jpeg_write_tables(cinfo); + + * set destination to image file + + * jpeg_start_compress(cinfo, FALSE); + + * write data... + + * jpeg_finish_compress(cinfo); + + * + + * jpeg_write_tables has the side effect of marking all tables written + + * (same as jpeg_suppress_tables(..., TRUE)). Thus a subsequent start_compress + + * will not re-emit the tables unless it is passed write_all_tables=TRUE. + + */ + + + +GLOBAL void + +jpeg_write_tables (j_compress_ptr cinfo) + +{ + + if (cinfo->global_state != CSTATE_START) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + + + /* (Re)initialize error mgr and destination modules */ + + (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo); + + (*cinfo->dest->init_destination) (cinfo); + + /* Initialize the marker writer ... bit of a crock to do it here. */ + + jinit_marker_writer(cinfo); + + /* Write them tables! */ + + (*cinfo->marker->write_tables_only) (cinfo); + + /* And clean up. */ + + (*cinfo->dest->term_destination) (cinfo); + + /* We can use jpeg_abort to release memory. */ + + jpeg_abort((j_common_ptr) cinfo); + +} + diff --git a/utils/roq2/jpeg/jcapistd.c b/utils/roq2/jpeg/jcapistd.c new file mode 100644 index 0000000..da0c28a --- /dev/null +++ b/utils/roq2/jpeg/jcapistd.c @@ -0,0 +1,322 @@ +/* + + * jcapistd.c + + * + + * Copyright (C) 1994-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains application interface code for the compression half + + * of the JPEG library. These are the "standard" API routines that are + + * used in the normal full-compression case. They are not used by a + + * transcoding-only application. Note that if an application links in + + * jpeg_start_compress, it will end up linking in the entire compressor. + + * We thus must separate this file from jcapimin.c to avoid linking the + + * whole compression library into a transcoder. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + + + +/* + + * Compression initialization. + + * Before calling this, all parameters and a data destination must be set up. + + * + + * We require a write_all_tables parameter as a failsafe check when writing + + * multiple datastreams from the same compression object. Since prior runs + + * will have left all the tables marked sent_table=TRUE, a subsequent run + + * would emit an abbreviated stream (no tables) by default. This may be what + + * is wanted, but for safety's sake it should not be the default behavior: + + * programmers should have to make a deliberate choice to emit abbreviated + + * images. Therefore the documentation and examples should encourage people + + * to pass write_all_tables=TRUE; then it will take active thought to do the + + * wrong thing. + + */ + + + +GLOBAL void + +jpeg_start_compress (j_compress_ptr cinfo, boolean write_all_tables) + +{ + + if (cinfo->global_state != CSTATE_START) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + + + if (write_all_tables) + + jpeg_suppress_tables(cinfo, FALSE); /* mark all tables to be written */ + + + + /* (Re)initialize error mgr and destination modules */ + + (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo); + + (*cinfo->dest->init_destination) (cinfo); + + /* Perform master selection of active modules */ + + jinit_compress_master(cinfo); + + /* Set up for the first pass */ + + (*cinfo->master->prepare_for_pass) (cinfo); + + /* Ready for application to drive first pass through jpeg_write_scanlines + + * or jpeg_write_raw_data. + + */ + + cinfo->next_scanline = 0; + + cinfo->global_state = (cinfo->raw_data_in ? CSTATE_RAW_OK : CSTATE_SCANNING); + +} + + + + + +/* + + * Write some scanlines of data to the JPEG compressor. + + * + + * The return value will be the number of lines actually written. + + * This should be less than the supplied num_lines only in case that + + * the data destination module has requested suspension of the compressor, + + * or if more than image_height scanlines are passed in. + + * + + * Note: we warn about excess calls to jpeg_write_scanlines() since + + * this likely signals an application programmer error. However, + + * excess scanlines passed in the last valid call are *silently* ignored, + + * so that the application need not adjust num_lines for end-of-image + + * when using a multiple-scanline buffer. + + */ + + + +GLOBAL JDIMENSION + +jpeg_write_scanlines (j_compress_ptr cinfo, JSAMPARRAY scanlines, + + JDIMENSION num_lines) + +{ + + JDIMENSION row_ctr, rows_left; + + + + if (cinfo->global_state != CSTATE_SCANNING) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + if (cinfo->next_scanline >= cinfo->image_height) + + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + + + + /* Call progress monitor hook if present */ + + if (cinfo->progress != NULL) { + + cinfo->progress->pass_counter = (long) cinfo->next_scanline; + + cinfo->progress->pass_limit = (long) cinfo->image_height; + + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + + } + + + + /* Give master control module another chance if this is first call to + + * jpeg_write_scanlines. This lets output of the frame/scan headers be + + * delayed so that application can write COM, etc, markers between + + * jpeg_start_compress and jpeg_write_scanlines. + + */ + + if (cinfo->master->call_pass_startup) + + (*cinfo->master->pass_startup) (cinfo); + + + + /* Ignore any extra scanlines at bottom of image. */ + + rows_left = cinfo->image_height - cinfo->next_scanline; + + if (num_lines > rows_left) + + num_lines = rows_left; + + + + row_ctr = 0; + + (*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, num_lines); + + cinfo->next_scanline += row_ctr; + + return row_ctr; + +} + + + + + +/* + + * Alternate entry point to write raw data. + + * Processes exactly one iMCU row per call, unless suspended. + + */ + + + +GLOBAL JDIMENSION + +jpeg_write_raw_data (j_compress_ptr cinfo, JSAMPIMAGE data, + + JDIMENSION num_lines) + +{ + + JDIMENSION lines_per_iMCU_row; + + + + if (cinfo->global_state != CSTATE_RAW_OK) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + if (cinfo->next_scanline >= cinfo->image_height) { + + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + + return 0; + + } + + + + /* Call progress monitor hook if present */ + + if (cinfo->progress != NULL) { + + cinfo->progress->pass_counter = (long) cinfo->next_scanline; + + cinfo->progress->pass_limit = (long) cinfo->image_height; + + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + + } + + + + /* Give master control module another chance if this is first call to + + * jpeg_write_raw_data. This lets output of the frame/scan headers be + + * delayed so that application can write COM, etc, markers between + + * jpeg_start_compress and jpeg_write_raw_data. + + */ + + if (cinfo->master->call_pass_startup) + + (*cinfo->master->pass_startup) (cinfo); + + + + /* Verify that at least one iMCU row has been passed. */ + + lines_per_iMCU_row = cinfo->max_v_samp_factor * DCTSIZE; + + if (num_lines < lines_per_iMCU_row) + + ERREXIT(cinfo, JERR_BUFFER_SIZE); + + + + /* Directly compress the row. */ + + if (! (*cinfo->coef->compress_data) (cinfo, data)) { + + /* If compressor did not consume the whole row, suspend processing. */ + + return 0; + + } + + + + /* OK, we processed one iMCU row. */ + + cinfo->next_scanline += lines_per_iMCU_row; + + return lines_per_iMCU_row; + +} + diff --git a/utils/roq2/jpeg/jccoefct.c b/utils/roq2/jpeg/jccoefct.c new file mode 100644 index 0000000..083d3d9 --- /dev/null +++ b/utils/roq2/jpeg/jccoefct.c @@ -0,0 +1,896 @@ +/* + + * jccoefct.c + + * + + * Copyright (C) 1994-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains the coefficient buffer controller for compression. + + * This controller is the top level of the JPEG compressor proper. + + * The coefficient buffer lies between forward-DCT and entropy encoding steps. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + + + +/* We use a full-image coefficient buffer when doing Huffman optimization, + + * and also for writing multiple-scan JPEG files. In all cases, the DCT + + * step is run during the first pass, and subsequent passes need only read + + * the buffered coefficients. + + */ + +#ifdef ENTROPY_OPT_SUPPORTED + +#define FULL_COEF_BUFFER_SUPPORTED + +#else + +#ifdef C_MULTISCAN_FILES_SUPPORTED + +#define FULL_COEF_BUFFER_SUPPORTED + +#endif + +#endif + + + + + +/* Private buffer controller object */ + + + +typedef struct { + + struct jpeg_c_coef_controller pub; /* public fields */ + + + + JDIMENSION iMCU_row_num; /* iMCU row # within image */ + + JDIMENSION mcu_ctr; /* counts MCUs processed in current row */ + + int MCU_vert_offset; /* counts MCU rows within iMCU row */ + + int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + + + /* For single-pass compression, it's sufficient to buffer just one MCU + + * (although this may prove a bit slow in practice). We allocate a + + * workspace of C_MAX_BLOCKS_IN_MCU coefficient blocks, and reuse it for each + + * MCU constructed and sent. (On 80x86, the workspace is FAR even though + + * it's not really very big; this is to keep the module interfaces unchanged + + * when a large coefficient buffer is necessary.) + + * In multi-pass modes, this array points to the current MCU's blocks + + * within the virtual arrays. + + */ + + JBLOCKROW MCU_buffer[C_MAX_BLOCKS_IN_MCU]; + + + + /* In multi-pass modes, we need a virtual block array for each component. */ + + jvirt_barray_ptr whole_image[MAX_COMPONENTS]; + +} my_coef_controller; + + + +typedef my_coef_controller * my_coef_ptr; + + + + + +/* Forward declarations */ + +METHODDEF boolean compress_data + + JPP((j_compress_ptr cinfo, JSAMPIMAGE input_buf)); + +#ifdef FULL_COEF_BUFFER_SUPPORTED + +METHODDEF boolean compress_first_pass + + JPP((j_compress_ptr cinfo, JSAMPIMAGE input_buf)); + +METHODDEF boolean compress_output + + JPP((j_compress_ptr cinfo, JSAMPIMAGE input_buf)); + +#endif + + + + + +LOCAL void + +start_iMCU_row (j_compress_ptr cinfo) + +/* Reset within-iMCU-row counters for a new row */ + +{ + + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + + + /* In an interleaved scan, an MCU row is the same as an iMCU row. + + * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. + + * But at the bottom of the image, process only what's left. + + */ + + if (cinfo->comps_in_scan > 1) { + + coef->MCU_rows_per_iMCU_row = 1; + + } else { + + if (coef->iMCU_row_num < (cinfo->total_iMCU_rows-1)) + + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; + + else + + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; + + } + + + + coef->mcu_ctr = 0; + + coef->MCU_vert_offset = 0; + +} + + + + + +/* + + * Initialize for a processing pass. + + */ + + + +METHODDEF void + +start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode) + +{ + + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + + + coef->iMCU_row_num = 0; + + start_iMCU_row(cinfo); + + + + switch (pass_mode) { + + case JBUF_PASS_THRU: + + if (coef->whole_image[0] != NULL) + + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + coef->pub.compress_data = compress_data; + + break; + +#ifdef FULL_COEF_BUFFER_SUPPORTED + + case JBUF_SAVE_AND_PASS: + + if (coef->whole_image[0] == NULL) + + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + coef->pub.compress_data = compress_first_pass; + + break; + + case JBUF_CRANK_DEST: + + if (coef->whole_image[0] == NULL) + + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + coef->pub.compress_data = compress_output; + + break; + +#endif + + default: + + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + break; + + } + +} + + + + + +/* + + * Process some data in the single-pass case. + + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + + * per call, ie, v_samp_factor block rows for each component in the image. + + * Returns TRUE if the iMCU row is completed, FALSE if suspended. + + * + + * NB: input_buf contains a plane for each component in image. + + * For single pass, this is the same as the components in the scan. + + */ + + + +METHODDEF boolean + +compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) + +{ + + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + JDIMENSION MCU_col_num; /* index of current MCU within row */ + + JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; + + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + + int blkn, bi, ci, yindex, yoffset, blockcnt; + + JDIMENSION ypos, xpos; + + jpeg_component_info *compptr; + + + + /* Loop to write as much as one whole iMCU row */ + + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + + yoffset++) { + + for (MCU_col_num = coef->mcu_ctr; MCU_col_num <= last_MCU_col; + + MCU_col_num++) { + + /* Determine where data comes from in input_buf and do the DCT thing. + + * Each call on forward_DCT processes a horizontal row of DCT blocks + + * as wide as an MCU; we rely on having allocated the MCU_buffer[] blocks + + * sequentially. Dummy blocks at the right or bottom edge are filled in + + * specially. The data in them does not matter for image reconstruction, + + * so we fill them with values that will encode to the smallest amount of + + * data, viz: all zeroes in the AC entries, DC entries equal to previous + + * block's DC value. (Thanks to Thomas Kinsman for this idea.) + + */ + + blkn = 0; + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + + compptr = cinfo->cur_comp_info[ci]; + + blockcnt = (MCU_col_num < last_MCU_col) ? compptr->MCU_width + + : compptr->last_col_width; + + xpos = MCU_col_num * compptr->MCU_sample_width; + + ypos = yoffset * DCTSIZE; /* ypos == (yoffset+yindex) * DCTSIZE */ + + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + + if (coef->iMCU_row_num < last_iMCU_row || + + yoffset+yindex < compptr->last_row_height) { + + (*cinfo->fdct->forward_DCT) (cinfo, compptr, + + input_buf[ci], coef->MCU_buffer[blkn], + + ypos, xpos, (JDIMENSION) blockcnt); + + if (blockcnt < compptr->MCU_width) { + + /* Create some dummy blocks at the right edge of the image. */ + + jzero_far((void FAR *) coef->MCU_buffer[blkn + blockcnt], + + (compptr->MCU_width - blockcnt) * SIZEOF(JBLOCK)); + + for (bi = blockcnt; bi < compptr->MCU_width; bi++) { + + coef->MCU_buffer[blkn+bi][0][0] = coef->MCU_buffer[blkn+bi-1][0][0]; + + } + + } + + } else { + + /* Create a row of dummy blocks at the bottom of the image. */ + + jzero_far((void FAR *) coef->MCU_buffer[blkn], + + compptr->MCU_width * SIZEOF(JBLOCK)); + + for (bi = 0; bi < compptr->MCU_width; bi++) { + + coef->MCU_buffer[blkn+bi][0][0] = coef->MCU_buffer[blkn-1][0][0]; + + } + + } + + blkn += compptr->MCU_width; + + ypos += DCTSIZE; + + } + + } + + /* Try to write the MCU. In event of a suspension failure, we will + + * re-DCT the MCU on restart (a bit inefficient, could be fixed...) + + */ + + if (! (*cinfo->entropy->encode_mcu) (cinfo, coef->MCU_buffer)) { + + /* Suspension forced; update state counters and exit */ + + coef->MCU_vert_offset = yoffset; + + coef->mcu_ctr = MCU_col_num; + + return FALSE; + + } + + } + + /* Completed an MCU row, but perhaps not an iMCU row */ + + coef->mcu_ctr = 0; + + } + + /* Completed the iMCU row, advance counters for next one */ + + coef->iMCU_row_num++; + + start_iMCU_row(cinfo); + + return TRUE; + +} + + + + + +#ifdef FULL_COEF_BUFFER_SUPPORTED + + + +/* + + * Process some data in the first pass of a multi-pass case. + + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + + * per call, ie, v_samp_factor block rows for each component in the image. + + * This amount of data is read from the source buffer, DCT'd and quantized, + + * and saved into the virtual arrays. We also generate suitable dummy blocks + + * as needed at the right and lower edges. (The dummy blocks are constructed + + * in the virtual arrays, which have been padded appropriately.) This makes + + * it possible for subsequent passes not to worry about real vs. dummy blocks. + + * + + * We must also emit the data to the entropy encoder. This is conveniently + + * done by calling compress_output() after we've loaded the current strip + + * of the virtual arrays. + + * + + * NB: input_buf contains a plane for each component in image. All + + * components are DCT'd and loaded into the virtual arrays in this pass. + + * However, it may be that only a subset of the components are emitted to + + * the entropy encoder during this first pass; be careful about looking + + * at the scan-dependent variables (MCU dimensions, etc). + + */ + + + +METHODDEF boolean + +compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf) + +{ + + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + + JDIMENSION blocks_across, MCUs_across, MCUindex; + + int bi, ci, h_samp_factor, block_row, block_rows, ndummy; + + JCOEF lastDC; + + jpeg_component_info *compptr; + + JBLOCKARRAY buffer; + + JBLOCKROW thisblockrow, lastblockrow; + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + /* Align the virtual buffer for this component. */ + + buffer = (*cinfo->mem->access_virt_barray) + + ((j_common_ptr) cinfo, coef->whole_image[ci], + + coef->iMCU_row_num * compptr->v_samp_factor, + + (JDIMENSION) compptr->v_samp_factor, TRUE); + + /* Count non-dummy DCT block rows in this iMCU row. */ + + if (coef->iMCU_row_num < last_iMCU_row) + + block_rows = compptr->v_samp_factor; + + else { + + /* NB: can't use last_row_height here, since may not be set! */ + + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + + if (block_rows == 0) block_rows = compptr->v_samp_factor; + + } + + blocks_across = compptr->width_in_blocks; + + h_samp_factor = compptr->h_samp_factor; + + /* Count number of dummy blocks to be added at the right margin. */ + + ndummy = (int) (blocks_across % h_samp_factor); + + if (ndummy > 0) + + ndummy = h_samp_factor - ndummy; + + /* Perform DCT for all non-dummy blocks in this iMCU row. Each call + + * on forward_DCT processes a complete horizontal row of DCT blocks. + + */ + + for (block_row = 0; block_row < block_rows; block_row++) { + + thisblockrow = buffer[block_row]; + + (*cinfo->fdct->forward_DCT) (cinfo, compptr, + + input_buf[ci], thisblockrow, + + (JDIMENSION) (block_row * DCTSIZE), + + (JDIMENSION) 0, blocks_across); + + if (ndummy > 0) { + + /* Create dummy blocks at the right edge of the image. */ + + thisblockrow += blocks_across; /* => first dummy block */ + + jzero_far((void FAR *) thisblockrow, ndummy * SIZEOF(JBLOCK)); + + lastDC = thisblockrow[-1][0]; + + for (bi = 0; bi < ndummy; bi++) { + + thisblockrow[bi][0] = lastDC; + + } + + } + + } + + /* If at end of image, create dummy block rows as needed. + + * The tricky part here is that within each MCU, we want the DC values + + * of the dummy blocks to match the last real block's DC value. + + * This squeezes a few more bytes out of the resulting file... + + */ + + if (coef->iMCU_row_num == last_iMCU_row) { + + blocks_across += ndummy; /* include lower right corner */ + + MCUs_across = blocks_across / h_samp_factor; + + for (block_row = block_rows; block_row < compptr->v_samp_factor; + + block_row++) { + + thisblockrow = buffer[block_row]; + + lastblockrow = buffer[block_row-1]; + + jzero_far((void FAR *) thisblockrow, + + (size_t) (blocks_across * SIZEOF(JBLOCK))); + + for (MCUindex = 0; MCUindex < MCUs_across; MCUindex++) { + + lastDC = lastblockrow[h_samp_factor-1][0]; + + for (bi = 0; bi < h_samp_factor; bi++) { + + thisblockrow[bi][0] = lastDC; + + } + + thisblockrow += h_samp_factor; /* advance to next MCU in row */ + + lastblockrow += h_samp_factor; + + } + + } + + } + + } + + /* NB: compress_output will increment iMCU_row_num if successful. + + * A suspension return will result in redoing all the work above next time. + + */ + + + + /* Emit data to the entropy encoder, sharing code with subsequent passes */ + + return compress_output(cinfo, input_buf); + +} + + + + + +/* + + * Process some data in subsequent passes of a multi-pass case. + + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + + * per call, ie, v_samp_factor block rows for each component in the scan. + + * The data is obtained from the virtual arrays and fed to the entropy coder. + + * Returns TRUE if the iMCU row is completed, FALSE if suspended. + + * + + * NB: input_buf is ignored; it is likely to be a NULL pointer. + + */ + + + +METHODDEF boolean + +compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) + +{ + + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + JDIMENSION MCU_col_num; /* index of current MCU within row */ + + int blkn, ci, xindex, yindex, yoffset; + + JDIMENSION start_col; + + JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN]; + + JBLOCKROW buffer_ptr; + + jpeg_component_info *compptr; + + + + /* Align the virtual buffers for the components used in this scan. + + * NB: during first pass, this is safe only because the buffers will + + * already be aligned properly, so jmemmgr.c won't need to do any I/O. + + */ + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + + compptr = cinfo->cur_comp_info[ci]; + + buffer[ci] = (*cinfo->mem->access_virt_barray) + + ((j_common_ptr) cinfo, coef->whole_image[compptr->component_index], + + coef->iMCU_row_num * compptr->v_samp_factor, + + (JDIMENSION) compptr->v_samp_factor, FALSE); + + } + + + + /* Loop to process one whole iMCU row */ + + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + + yoffset++) { + + for (MCU_col_num = coef->mcu_ctr; MCU_col_num < cinfo->MCUs_per_row; + + MCU_col_num++) { + + /* Construct list of pointers to DCT blocks belonging to this MCU */ + + blkn = 0; /* index of current DCT block within MCU */ + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + + compptr = cinfo->cur_comp_info[ci]; + + start_col = MCU_col_num * compptr->MCU_width; + + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + + buffer_ptr = buffer[ci][yindex+yoffset] + start_col; + + for (xindex = 0; xindex < compptr->MCU_width; xindex++) { + + coef->MCU_buffer[blkn++] = buffer_ptr++; + + } + + } + + } + + /* Try to write the MCU. */ + + if (! (*cinfo->entropy->encode_mcu) (cinfo, coef->MCU_buffer)) { + + /* Suspension forced; update state counters and exit */ + + coef->MCU_vert_offset = yoffset; + + coef->mcu_ctr = MCU_col_num; + + return FALSE; + + } + + } + + /* Completed an MCU row, but perhaps not an iMCU row */ + + coef->mcu_ctr = 0; + + } + + /* Completed the iMCU row, advance counters for next one */ + + coef->iMCU_row_num++; + + start_iMCU_row(cinfo); + + return TRUE; + +} + + + +#endif /* FULL_COEF_BUFFER_SUPPORTED */ + + + + + +/* + + * Initialize coefficient buffer controller. + + */ + + + +GLOBAL void + +jinit_c_coef_controller (j_compress_ptr cinfo, boolean need_full_buffer) + +{ + + my_coef_ptr coef; + + + + coef = (my_coef_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(my_coef_controller)); + + cinfo->coef = (struct jpeg_c_coef_controller *) coef; + + coef->pub.start_pass = start_pass_coef; + + + + /* Create the coefficient buffer. */ + + if (need_full_buffer) { + +#ifdef FULL_COEF_BUFFER_SUPPORTED + + /* Allocate a full-image virtual array for each component, */ + + /* padded to a multiple of samp_factor DCT blocks in each direction. */ + + int ci; + + jpeg_component_info *compptr; + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + coef->whole_image[ci] = (*cinfo->mem->request_virt_barray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + + (long) compptr->h_samp_factor), + + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + + (long) compptr->v_samp_factor), + + (JDIMENSION) compptr->v_samp_factor); + + } + +#else + + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + +#endif + + } else { + + /* We only need a single-MCU buffer. */ + + JBLOCKROW buffer; + + int i; + + + + buffer = (JBLOCKROW) + + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + + for (i = 0; i < C_MAX_BLOCKS_IN_MCU; i++) { + + coef->MCU_buffer[i] = buffer + i; + + } + + coef->whole_image[0] = NULL; /* flag for no virtual arrays */ + + } + +} + diff --git a/utils/roq2/jpeg/jccolor.c b/utils/roq2/jpeg/jccolor.c new file mode 100644 index 0000000..64b8b58 --- /dev/null +++ b/utils/roq2/jpeg/jccolor.c @@ -0,0 +1,918 @@ +/* + + * jccolor.c + + * + + * Copyright (C) 1991-1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains input colorspace conversion routines. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + + + +/* Private subobject */ + + + +typedef struct { + + struct jpeg_color_converter pub; /* public fields */ + + + + /* Private state for RGB->YCC conversion */ + + INT32 * rgb_ycc_tab; /* => table for RGB to YCbCr conversion */ + +} my_color_converter; + + + +typedef my_color_converter * my_cconvert_ptr; + + + + + +/**************** RGB -> YCbCr conversion: most common case **************/ + + + +/* + + * YCbCr is defined per CCIR 601-1, except that Cb and Cr are + + * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. + + * The conversion equations to be implemented are therefore + + * Y = 0.29900 * R + 0.58700 * G + 0.11400 * B + + * Cb = -0.16874 * R - 0.33126 * G + 0.50000 * B + CENTERJSAMPLE + + * Cr = 0.50000 * R - 0.41869 * G - 0.08131 * B + CENTERJSAMPLE + + * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) + + * Note: older versions of the IJG code used a zero offset of MAXJSAMPLE/2, + + * rather than CENTERJSAMPLE, for Cb and Cr. This gave equal positive and + + * negative swings for Cb/Cr, but meant that grayscale values (Cb=Cr=0) + + * were not represented exactly. Now we sacrifice exact representation of + + * maximum red and maximum blue in order to get exact grayscales. + + * + + * To avoid floating-point arithmetic, we represent the fractional constants + + * as integers scaled up by 2^16 (about 4 digits precision); we have to divide + + * the products by 2^16, with appropriate rounding, to get the correct answer. + + * + + * For even more speed, we avoid doing any multiplications in the inner loop + + * by precalculating the constants times R,G,B for all possible values. + + * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table); + + * for 12-bit samples it is still acceptable. It's not very reasonable for + + * 16-bit samples, but if you want lossless storage you shouldn't be changing + + * colorspace anyway. + + * The CENTERJSAMPLE offsets and the rounding fudge-factor of 0.5 are included + + * in the tables to save adding them separately in the inner loop. + + */ + + + +#define SCALEBITS 16 /* speediest right-shift on some machines */ + +#define CBCR_OFFSET ((INT32) CENTERJSAMPLE << SCALEBITS) + +#define ONE_HALF ((INT32) 1 << (SCALEBITS-1)) + +#define FIX(x) ((INT32) ((x) * (1L< Y section */ + +#define G_Y_OFF (1*(MAXJSAMPLE+1)) /* offset to G => Y section */ + +#define B_Y_OFF (2*(MAXJSAMPLE+1)) /* etc. */ + +#define R_CB_OFF (3*(MAXJSAMPLE+1)) + +#define G_CB_OFF (4*(MAXJSAMPLE+1)) + +#define B_CB_OFF (5*(MAXJSAMPLE+1)) + +#define R_CR_OFF B_CB_OFF /* B=>Cb, R=>Cr are the same */ + +#define G_CR_OFF (6*(MAXJSAMPLE+1)) + +#define B_CR_OFF (7*(MAXJSAMPLE+1)) + +#define TABLE_SIZE (8*(MAXJSAMPLE+1)) + + + + + +/* + + * Initialize for RGB->YCC colorspace conversion. + + */ + + + +METHODDEF void + +rgb_ycc_start (j_compress_ptr cinfo) + +{ + + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + + INT32 * rgb_ycc_tab; + + INT32 i; + + + + /* Allocate and fill in the conversion tables. */ + + cconvert->rgb_ycc_tab = rgb_ycc_tab = (INT32 *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (TABLE_SIZE * SIZEOF(INT32))); + + + + for (i = 0; i <= MAXJSAMPLE; i++) { + + rgb_ycc_tab[i+R_Y_OFF] = FIX(0.29900) * i; + + rgb_ycc_tab[i+G_Y_OFF] = FIX(0.58700) * i; + + rgb_ycc_tab[i+B_Y_OFF] = FIX(0.11400) * i + ONE_HALF; + + rgb_ycc_tab[i+R_CB_OFF] = (-FIX(0.16874)) * i; + + rgb_ycc_tab[i+G_CB_OFF] = (-FIX(0.33126)) * i; + + /* We use a rounding fudge-factor of 0.5-epsilon for Cb and Cr. + + * This ensures that the maximum output will round to MAXJSAMPLE + + * not MAXJSAMPLE+1, and thus that we don't have to range-limit. + + */ + + rgb_ycc_tab[i+B_CB_OFF] = FIX(0.50000) * i + CBCR_OFFSET + ONE_HALF-1; + +/* B=>Cb and R=>Cr tables are the same + + rgb_ycc_tab[i+R_CR_OFF] = FIX(0.50000) * i + CBCR_OFFSET + ONE_HALF-1; + +*/ + + rgb_ycc_tab[i+G_CR_OFF] = (-FIX(0.41869)) * i; + + rgb_ycc_tab[i+B_CR_OFF] = (-FIX(0.08131)) * i; + + } + +} + + + + + +/* + + * Convert some rows of samples to the JPEG colorspace. + + * + + * Note that we change from the application's interleaved-pixel format + + * to our internal noninterleaved, one-plane-per-component format. + + * The input buffer is therefore three times as wide as the output buffer. + + * + + * A starting row offset is provided only for the output buffer. The caller + + * can easily adjust the passed input_buf value to accommodate any row + + * offset required on that side. + + */ + + + +METHODDEF void + +rgb_ycc_convert (j_compress_ptr cinfo, + + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + + JDIMENSION output_row, int num_rows) + +{ + + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + + register int r, g, b; + + register INT32 * ctab = cconvert->rgb_ycc_tab; + + register JSAMPROW inptr; + + register JSAMPROW outptr0, outptr1, outptr2; + + register JDIMENSION col; + + JDIMENSION num_cols = cinfo->image_width; + + + + while (--num_rows >= 0) { + + inptr = *input_buf++; + + outptr0 = output_buf[0][output_row]; + + outptr1 = output_buf[1][output_row]; + + outptr2 = output_buf[2][output_row]; + + output_row++; + + for (col = 0; col < num_cols; col++) { + + r = GETJSAMPLE(inptr[RGB_RED]); + + g = GETJSAMPLE(inptr[RGB_GREEN]); + + b = GETJSAMPLE(inptr[RGB_BLUE]); + + inptr += RGB_PIXELSIZE; + + /* If the inputs are 0..MAXJSAMPLE, the outputs of these equations + + * must be too; we do not need an explicit range-limiting operation. + + * Hence the value being shifted is never negative, and we don't + + * need the general RIGHT_SHIFT macro. + + */ + + /* Y */ + + outptr0[col] = (JSAMPLE) + + ((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF]) + + >> SCALEBITS); + + /* Cb */ + + outptr1[col] = (JSAMPLE) + + ((ctab[r+R_CB_OFF] + ctab[g+G_CB_OFF] + ctab[b+B_CB_OFF]) + + >> SCALEBITS); + + /* Cr */ + + outptr2[col] = (JSAMPLE) + + ((ctab[r+R_CR_OFF] + ctab[g+G_CR_OFF] + ctab[b+B_CR_OFF]) + + >> SCALEBITS); + + } + + } + +} + + + + + +/**************** Cases other than RGB -> YCbCr **************/ + + + + + +/* + + * Convert some rows of samples to the JPEG colorspace. + + * This version handles RGB->grayscale conversion, which is the same + + * as the RGB->Y portion of RGB->YCbCr. + + * We assume rgb_ycc_start has been called (we only use the Y tables). + + */ + + + +METHODDEF void + +rgb_gray_convert (j_compress_ptr cinfo, + + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + + JDIMENSION output_row, int num_rows) + +{ + + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + + register int r, g, b; + + register INT32 * ctab = cconvert->rgb_ycc_tab; + + register JSAMPROW inptr; + + register JSAMPROW outptr; + + register JDIMENSION col; + + JDIMENSION num_cols = cinfo->image_width; + + + + while (--num_rows >= 0) { + + inptr = *input_buf++; + + outptr = output_buf[0][output_row]; + + output_row++; + + for (col = 0; col < num_cols; col++) { + + r = GETJSAMPLE(inptr[RGB_RED]); + + g = GETJSAMPLE(inptr[RGB_GREEN]); + + b = GETJSAMPLE(inptr[RGB_BLUE]); + + inptr += RGB_PIXELSIZE; + + /* Y */ + + outptr[col] = (JSAMPLE) + + ((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF]) + + >> SCALEBITS); + + } + + } + +} + + + + + +/* + + * Convert some rows of samples to the JPEG colorspace. + + * This version handles Adobe-style CMYK->YCCK conversion, + + * where we convert R=1-C, G=1-M, and B=1-Y to YCbCr using the same + + * conversion as above, while passing K (black) unchanged. + + * We assume rgb_ycc_start has been called. + + */ + + + +METHODDEF void + +cmyk_ycck_convert (j_compress_ptr cinfo, + + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + + JDIMENSION output_row, int num_rows) + +{ + + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + + register int r, g, b; + + register INT32 * ctab = cconvert->rgb_ycc_tab; + + register JSAMPROW inptr; + + register JSAMPROW outptr0, outptr1, outptr2, outptr3; + + register JDIMENSION col; + + JDIMENSION num_cols = cinfo->image_width; + + + + while (--num_rows >= 0) { + + inptr = *input_buf++; + + outptr0 = output_buf[0][output_row]; + + outptr1 = output_buf[1][output_row]; + + outptr2 = output_buf[2][output_row]; + + outptr3 = output_buf[3][output_row]; + + output_row++; + + for (col = 0; col < num_cols; col++) { + + r = MAXJSAMPLE - GETJSAMPLE(inptr[0]); + + g = MAXJSAMPLE - GETJSAMPLE(inptr[1]); + + b = MAXJSAMPLE - GETJSAMPLE(inptr[2]); + + /* K passes through as-is */ + + outptr3[col] = inptr[3]; /* don't need GETJSAMPLE here */ + + inptr += 4; + + /* If the inputs are 0..MAXJSAMPLE, the outputs of these equations + + * must be too; we do not need an explicit range-limiting operation. + + * Hence the value being shifted is never negative, and we don't + + * need the general RIGHT_SHIFT macro. + + */ + + /* Y */ + + outptr0[col] = (JSAMPLE) + + ((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF]) + + >> SCALEBITS); + + /* Cb */ + + outptr1[col] = (JSAMPLE) + + ((ctab[r+R_CB_OFF] + ctab[g+G_CB_OFF] + ctab[b+B_CB_OFF]) + + >> SCALEBITS); + + /* Cr */ + + outptr2[col] = (JSAMPLE) + + ((ctab[r+R_CR_OFF] + ctab[g+G_CR_OFF] + ctab[b+B_CR_OFF]) + + >> SCALEBITS); + + } + + } + +} + + + + + +/* + + * Convert some rows of samples to the JPEG colorspace. + + * This version handles grayscale output with no conversion. + + * The source can be either plain grayscale or YCbCr (since Y == gray). + + */ + + + +METHODDEF void + +grayscale_convert (j_compress_ptr cinfo, + + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + + JDIMENSION output_row, int num_rows) + +{ + + register JSAMPROW inptr; + + register JSAMPROW outptr; + + register JDIMENSION col; + + JDIMENSION num_cols = cinfo->image_width; + + int instride = cinfo->input_components; + + + + while (--num_rows >= 0) { + + inptr = *input_buf++; + + outptr = output_buf[0][output_row]; + + output_row++; + + for (col = 0; col < num_cols; col++) { + + outptr[col] = inptr[0]; /* don't need GETJSAMPLE() here */ + + inptr += instride; + + } + + } + +} + + + + + +/* + + * Convert some rows of samples to the JPEG colorspace. + + * This version handles multi-component colorspaces without conversion. + + * We assume input_components == num_components. + + */ + + + +METHODDEF void + +null_convert (j_compress_ptr cinfo, + + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + + JDIMENSION output_row, int num_rows) + +{ + + register JSAMPROW inptr; + + register JSAMPROW outptr; + + register JDIMENSION col; + + register int ci; + + int nc = cinfo->num_components; + + JDIMENSION num_cols = cinfo->image_width; + + + + while (--num_rows >= 0) { + + /* It seems fastest to make a separate pass for each component. */ + + for (ci = 0; ci < nc; ci++) { + + inptr = *input_buf; + + outptr = output_buf[ci][output_row]; + + for (col = 0; col < num_cols; col++) { + + outptr[col] = inptr[ci]; /* don't need GETJSAMPLE() here */ + + inptr += nc; + + } + + } + + input_buf++; + + output_row++; + + } + +} + + + + + +/* + + * Empty method for start_pass. + + */ + + + +METHODDEF void + +null_method (j_compress_ptr cinfo) + +{ + + /* no work needed */ + +} + + + + + +/* + + * Module initialization routine for input colorspace conversion. + + */ + + + +GLOBAL void + +jinit_color_converter (j_compress_ptr cinfo) + +{ + + my_cconvert_ptr cconvert; + + + + cconvert = (my_cconvert_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(my_color_converter)); + + cinfo->cconvert = (struct jpeg_color_converter *) cconvert; + + /* set start_pass to null method until we find out differently */ + + cconvert->pub.start_pass = null_method; + + + + /* Make sure input_components agrees with in_color_space */ + + switch (cinfo->in_color_space) { + + case JCS_GRAYSCALE: + + if (cinfo->input_components != 1) + + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + + break; + + + + case JCS_RGB: + +#if RGB_PIXELSIZE != 3 + + if (cinfo->input_components != RGB_PIXELSIZE) + + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + + break; + +#endif /* else share code with YCbCr */ + + + + case JCS_YCbCr: + + if (cinfo->input_components != 3) + + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + + break; + + + + case JCS_CMYK: + + case JCS_YCCK: + + if (cinfo->input_components != 4) + + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + + break; + + + + default: /* JCS_UNKNOWN can be anything */ + + if (cinfo->input_components < 1) + + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + + break; + + } + + + + /* Check num_components, set conversion method based on requested space */ + + switch (cinfo->jpeg_color_space) { + + case JCS_GRAYSCALE: + + if (cinfo->num_components != 1) + + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + + if (cinfo->in_color_space == JCS_GRAYSCALE) + + cconvert->pub.color_convert = grayscale_convert; + + else if (cinfo->in_color_space == JCS_RGB) { + + cconvert->pub.start_pass = rgb_ycc_start; + + cconvert->pub.color_convert = rgb_gray_convert; + + } else if (cinfo->in_color_space == JCS_YCbCr) + + cconvert->pub.color_convert = grayscale_convert; + + else + + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + + break; + + + + case JCS_RGB: + + if (cinfo->num_components != 3) + + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + + if (cinfo->in_color_space == JCS_RGB && RGB_PIXELSIZE == 3) + + cconvert->pub.color_convert = null_convert; + + else + + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + + break; + + + + case JCS_YCbCr: + + if (cinfo->num_components != 3) + + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + + if (cinfo->in_color_space == JCS_RGB) { + + cconvert->pub.start_pass = rgb_ycc_start; + + cconvert->pub.color_convert = rgb_ycc_convert; + + } else if (cinfo->in_color_space == JCS_YCbCr) + + cconvert->pub.color_convert = null_convert; + + else + + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + + break; + + + + case JCS_CMYK: + + if (cinfo->num_components != 4) + + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + + if (cinfo->in_color_space == JCS_CMYK) + + cconvert->pub.color_convert = null_convert; + + else + + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + + break; + + + + case JCS_YCCK: + + if (cinfo->num_components != 4) + + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + + if (cinfo->in_color_space == JCS_CMYK) { + + cconvert->pub.start_pass = rgb_ycc_start; + + cconvert->pub.color_convert = cmyk_ycck_convert; + + } else if (cinfo->in_color_space == JCS_YCCK) + + cconvert->pub.color_convert = null_convert; + + else + + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + + break; + + + + default: /* allow null conversion of JCS_UNKNOWN */ + + if (cinfo->jpeg_color_space != cinfo->in_color_space || + + cinfo->num_components != cinfo->input_components) + + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + + cconvert->pub.color_convert = null_convert; + + break; + + } + +} + diff --git a/utils/roq2/jpeg/jcdctmgr.c b/utils/roq2/jpeg/jcdctmgr.c new file mode 100644 index 0000000..66b3860 --- /dev/null +++ b/utils/roq2/jpeg/jcdctmgr.c @@ -0,0 +1,776 @@ +/* + + * jcdctmgr.c + + * + + * Copyright (C) 1994-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains the forward-DCT management logic. + + * This code selects a particular DCT implementation to be used, + + * and it performs related housekeeping chores including coefficient + + * quantization. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + +#include "jdct.h" /* Private declarations for DCT subsystem */ + + + + + +/* Private subobject for this module */ + + + +typedef struct { + + struct jpeg_forward_dct pub; /* public fields */ + + + + /* Pointer to the DCT routine actually in use */ + + forward_DCT_method_ptr do_dct; + + + + /* The actual post-DCT divisors --- not identical to the quant table + + * entries, because of scaling (especially for an unnormalized DCT). + + * Each table is given in normal array order; note that this must + + * be converted from the zigzag order of the quantization tables. + + */ + + DCTELEM * divisors[NUM_QUANT_TBLS]; + + + +#ifdef DCT_FLOAT_SUPPORTED + + /* Same as above for the floating-point case. */ + + float_DCT_method_ptr do_float_dct; + + FAST_FLOAT * float_divisors[NUM_QUANT_TBLS]; + +#endif + +} my_fdct_controller; + + + +typedef my_fdct_controller * my_fdct_ptr; + + + + + +/* + + * Initialize for a processing pass. + + * Verify that all referenced Q-tables are present, and set up + + * the divisor table for each one. + + * In the current implementation, DCT of all components is done during + + * the first pass, even if only some components will be output in the + + * first scan. Hence all components should be examined here. + + */ + + + +METHODDEF void + +start_pass_fdctmgr (j_compress_ptr cinfo) + +{ + + my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct; + + int ci, qtblno, i; + + jpeg_component_info *compptr; + + JQUANT_TBL * qtbl; + + DCTELEM * dtbl; + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + qtblno = compptr->quant_tbl_no; + + /* Make sure specified quantization table is present */ + + if (qtblno < 0 || qtblno >= NUM_QUANT_TBLS || + + cinfo->quant_tbl_ptrs[qtblno] == NULL) + + ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, qtblno); + + qtbl = cinfo->quant_tbl_ptrs[qtblno]; + + /* Compute divisors for this quant table */ + + /* We may do this more than once for same table, but it's not a big deal */ + + switch (cinfo->dct_method) { + +#ifdef DCT_ISLOW_SUPPORTED + + case JDCT_ISLOW: + + /* For LL&M IDCT method, divisors are equal to raw quantization + + * coefficients multiplied by 8 (to counteract scaling). + + */ + + if (fdct->divisors[qtblno] == NULL) { + + fdct->divisors[qtblno] = (DCTELEM *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + DCTSIZE2 * SIZEOF(DCTELEM)); + + } + + dtbl = fdct->divisors[qtblno]; + + for (i = 0; i < DCTSIZE2; i++) { + + dtbl[i] = ((DCTELEM) qtbl->quantval[jpeg_zigzag_order[i]]) << 3; + + } + + break; + +#endif + +#ifdef DCT_IFAST_SUPPORTED + + case JDCT_IFAST: + + { + + /* For AA&N IDCT method, divisors are equal to quantization + + * coefficients scaled by scalefactor[row]*scalefactor[col], where + + * scalefactor[0] = 1 + + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + + * We apply a further scale factor of 8. + + */ + +#define CONST_BITS 14 + + static const INT16 aanscales[DCTSIZE2] = { + + /* precomputed values scaled up by 14 bits: in natural order */ + + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + + 22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270, + + 21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906, + + 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, + + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + + 12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552, + + 8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446, + + 4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247 + + }; + + SHIFT_TEMPS + + + + if (fdct->divisors[qtblno] == NULL) { + + fdct->divisors[qtblno] = (DCTELEM *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + DCTSIZE2 * SIZEOF(DCTELEM)); + + } + + dtbl = fdct->divisors[qtblno]; + + for (i = 0; i < DCTSIZE2; i++) { + + dtbl[i] = (DCTELEM) + + DESCALE(MULTIPLY16V16((INT32) qtbl->quantval[jpeg_zigzag_order[i]], + + (INT32) aanscales[i]), + + CONST_BITS-3); + + } + + } + + break; + +#endif + +#ifdef DCT_FLOAT_SUPPORTED + + case JDCT_FLOAT: + + { + + /* For float AA&N IDCT method, divisors are equal to quantization + + * coefficients scaled by scalefactor[row]*scalefactor[col], where + + * scalefactor[0] = 1 + + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + + * We apply a further scale factor of 8. + + * What's actually stored is 1/divisor so that the inner loop can + + * use a multiplication rather than a division. + + */ + + FAST_FLOAT * fdtbl; + + int row, col; + + static const double aanscalefactor[DCTSIZE] = { + + 1.0, 1.387039845, 1.306562965, 1.175875602, + + 1.0, 0.785694958, 0.541196100, 0.275899379 + + }; + + + + if (fdct->float_divisors[qtblno] == NULL) { + + fdct->float_divisors[qtblno] = (FAST_FLOAT *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + DCTSIZE2 * SIZEOF(FAST_FLOAT)); + + } + + fdtbl = fdct->float_divisors[qtblno]; + + i = 0; + + for (row = 0; row < DCTSIZE; row++) { + + for (col = 0; col < DCTSIZE; col++) { + + fdtbl[i] = (FAST_FLOAT) + + (1.0 / (((double) qtbl->quantval[jpeg_zigzag_order[i]] * + + aanscalefactor[row] * aanscalefactor[col] * 8.0))); + + i++; + + } + + } + + } + + break; + +#endif + + default: + + ERREXIT(cinfo, JERR_NOT_COMPILED); + + break; + + } + + } + +} + + + + + +/* + + * Perform forward DCT on one or more blocks of a component. + + * + + * The input samples are taken from the sample_data[] array starting at + + * position start_row/start_col, and moving to the right for any additional + + * blocks. The quantized coefficients are returned in coef_blocks[]. + + */ + + + +METHODDEF void + +forward_DCT (j_compress_ptr cinfo, jpeg_component_info * compptr, + + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + + JDIMENSION start_row, JDIMENSION start_col, + + JDIMENSION num_blocks) + +/* This version is used for integer DCT implementations. */ + +{ + + /* This routine is heavily used, so it's worth coding it tightly. */ + + my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct; + + forward_DCT_method_ptr do_dct = fdct->do_dct; + + DCTELEM * divisors = fdct->divisors[compptr->quant_tbl_no]; + + DCTELEM workspace[DCTSIZE2]; /* work area for FDCT subroutine */ + + JDIMENSION bi; + + + + sample_data += start_row; /* fold in the vertical offset once */ + + + + for (bi = 0; bi < num_blocks; bi++, start_col += DCTSIZE) { + + /* Load data into workspace, applying unsigned->signed conversion */ + + { register DCTELEM *workspaceptr; + + register JSAMPROW elemptr; + + register int elemr; + + + + workspaceptr = workspace; + + for (elemr = 0; elemr < DCTSIZE; elemr++) { + + elemptr = sample_data[elemr] + start_col; + +#if DCTSIZE == 8 /* unroll the inner loop */ + + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + +#else + + { register int elemc; + + for (elemc = DCTSIZE; elemc > 0; elemc--) { + + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + + } + + } + +#endif + + } + + } + + + + /* Perform the DCT */ + + (*do_dct) (workspace); + + + + /* Quantize/descale the coefficients, and store into coef_blocks[] */ + + { register DCTELEM temp, qval; + + register int i; + + register JCOEFPTR output_ptr = coef_blocks[bi]; + + + + for (i = 0; i < DCTSIZE2; i++) { + + qval = divisors[i]; + + temp = workspace[i]; + + /* Divide the coefficient value by qval, ensuring proper rounding. + + * Since C does not specify the direction of rounding for negative + + * quotients, we have to force the dividend positive for portability. + + * + + * In most files, at least half of the output values will be zero + + * (at default quantization settings, more like three-quarters...) + + * so we should ensure that this case is fast. On many machines, + + * a comparison is enough cheaper than a divide to make a special test + + * a win. Since both inputs will be nonnegative, we need only test + + * for a < b to discover whether a/b is 0. + + * If your machine's division is fast enough, define FAST_DIVIDE. + + */ + +#ifdef FAST_DIVIDE + +#define DIVIDE_BY(a,b) a /= b + +#else + +#define DIVIDE_BY(a,b) if (a >= b) a /= b; else a = 0 + +#endif + + if (temp < 0) { + + temp = -temp; + + temp += qval>>1; /* for rounding */ + + DIVIDE_BY(temp, qval); + + temp = -temp; + + } else { + + temp += qval>>1; /* for rounding */ + + DIVIDE_BY(temp, qval); + + } + + output_ptr[i] = (JCOEF) temp; + + } + + } + + } + +} + + + + + +#ifdef DCT_FLOAT_SUPPORTED + + + +METHODDEF void + +forward_DCT_float (j_compress_ptr cinfo, jpeg_component_info * compptr, + + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + + JDIMENSION start_row, JDIMENSION start_col, + + JDIMENSION num_blocks) + +/* This version is used for floating-point DCT implementations. */ + +{ + + /* This routine is heavily used, so it's worth coding it tightly. */ + + my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct; + + float_DCT_method_ptr do_dct = fdct->do_float_dct; + + FAST_FLOAT * divisors = fdct->float_divisors[compptr->quant_tbl_no]; + + FAST_FLOAT workspace[DCTSIZE2]; /* work area for FDCT subroutine */ + + JDIMENSION bi; + + + + sample_data += start_row; /* fold in the vertical offset once */ + + + + for (bi = 0; bi < num_blocks; bi++, start_col += DCTSIZE) { + + /* Load data into workspace, applying unsigned->signed conversion */ + + { register FAST_FLOAT *workspaceptr; + + register JSAMPROW elemptr; + + register int elemr; + + + + workspaceptr = workspace; + + for (elemr = 0; elemr < DCTSIZE; elemr++) { + + elemptr = sample_data[elemr] + start_col; + +#if DCTSIZE == 8 /* unroll the inner loop */ + + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + +#else + + { register int elemc; + + for (elemc = DCTSIZE; elemc > 0; elemc--) { + + *workspaceptr++ = (FAST_FLOAT) + + (GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + + } + + } + +#endif + + } + + } + + + + /* Perform the DCT */ + + (*do_dct) (workspace); + + + + /* Quantize/descale the coefficients, and store into coef_blocks[] */ + + { register FAST_FLOAT temp; + + register int i; + + register JCOEFPTR output_ptr = coef_blocks[bi]; + + + + for (i = 0; i < DCTSIZE2; i++) { + + /* Apply the quantization and scaling factor */ + + temp = workspace[i] * divisors[i]; + + /* Round to nearest integer. + + * Since C does not specify the direction of rounding for negative + + * quotients, we have to force the dividend positive for portability. + + * The maximum coefficient size is +-16K (for 12-bit data), so this + + * code should work for either 16-bit or 32-bit ints. + + */ + + output_ptr[i] = (JCOEF) ((int) (temp + (FAST_FLOAT) 16384.5) - 16384); + + } + + } + + } + +} + + + +#endif /* DCT_FLOAT_SUPPORTED */ + + + + + +/* + + * Initialize FDCT manager. + + */ + + + +GLOBAL void + +jinit_forward_dct (j_compress_ptr cinfo) + +{ + + my_fdct_ptr fdct; + + int i; + + + + fdct = (my_fdct_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(my_fdct_controller)); + + cinfo->fdct = (struct jpeg_forward_dct *) fdct; + + fdct->pub.start_pass = start_pass_fdctmgr; + + + + switch (cinfo->dct_method) { + +#ifdef DCT_ISLOW_SUPPORTED + + case JDCT_ISLOW: + + fdct->pub.forward_DCT = forward_DCT; + + fdct->do_dct = jpeg_fdct_islow; + + break; + +#endif + +#ifdef DCT_IFAST_SUPPORTED + + case JDCT_IFAST: + + fdct->pub.forward_DCT = forward_DCT; + + fdct->do_dct = jpeg_fdct_ifast; + + break; + +#endif + +#ifdef DCT_FLOAT_SUPPORTED + + case JDCT_FLOAT: + + fdct->pub.forward_DCT = forward_DCT_float; + + fdct->do_float_dct = jpeg_fdct_float; + + break; + +#endif + + default: + + ERREXIT(cinfo, JERR_NOT_COMPILED); + + break; + + } + + + + /* Mark divisor tables unallocated */ + + for (i = 0; i < NUM_QUANT_TBLS; i++) { + + fdct->divisors[i] = NULL; + +#ifdef DCT_FLOAT_SUPPORTED + + fdct->float_divisors[i] = NULL; + +#endif + + } + +} + diff --git a/utils/roq2/jpeg/jchuff.c b/utils/roq2/jpeg/jchuff.c new file mode 100644 index 0000000..1839a89 --- /dev/null +++ b/utils/roq2/jpeg/jchuff.c @@ -0,0 +1,1696 @@ +/* + + * jchuff.c + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains Huffman entropy encoding routines. + + * + + * Much of the complexity here has to do with supporting output suspension. + + * If the data destination module demands suspension, we want to be able to + + * back up to the start of the current MCU. To do this, we copy state + + * variables into local working storage, and update them back to the + + * permanent JPEG objects only upon successful completion of an MCU. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + +#include "jchuff.h" /* Declarations shared with jcphuff.c */ + + + + + +/* Expanded entropy encoder object for Huffman encoding. + + * + + * The savable_state subrecord contains fields that change within an MCU, + + * but must not be updated permanently until we complete the MCU. + + */ + + + +typedef struct { + + INT32 put_buffer; /* current bit-accumulation buffer */ + + int put_bits; /* # of bits now in it */ + + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ + +} savable_state; + + + +/* This macro is to work around compilers with missing or broken + + * structure assignment. You'll need to fix this code if you have + + * such a compiler and you change MAX_COMPS_IN_SCAN. + + */ + + + +#ifndef NO_STRUCT_ASSIGN + +#define ASSIGN_STATE(dest,src) ((dest) = (src)) + +#else + +#if MAX_COMPS_IN_SCAN == 4 + +#define ASSIGN_STATE(dest,src) \ + + ((dest).put_buffer = (src).put_buffer, \ + + (dest).put_bits = (src).put_bits, \ + + (dest).last_dc_val[0] = (src).last_dc_val[0], \ + + (dest).last_dc_val[1] = (src).last_dc_val[1], \ + + (dest).last_dc_val[2] = (src).last_dc_val[2], \ + + (dest).last_dc_val[3] = (src).last_dc_val[3]) + +#endif + +#endif + + + + + +typedef struct { + + struct jpeg_entropy_encoder pub; /* public fields */ + + + + savable_state saved; /* Bit buffer & DC state at start of MCU */ + + + + /* These fields are NOT loaded into local working state. */ + + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + + int next_restart_num; /* next restart number to write (0-7) */ + + + + /* Pointers to derived tables (these workspaces have image lifespan) */ + + c_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS]; + + c_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS]; + + + +#ifdef ENTROPY_OPT_SUPPORTED /* Statistics tables for optimization */ + + long * dc_count_ptrs[NUM_HUFF_TBLS]; + + long * ac_count_ptrs[NUM_HUFF_TBLS]; + +#endif + +} huff_entropy_encoder; + + + +typedef huff_entropy_encoder * huff_entropy_ptr; + + + +/* Working state while writing an MCU. + + * This struct contains all the fields that are needed by subroutines. + + */ + + + +typedef struct { + + JOCTET * next_output_byte; /* => next byte to write in buffer */ + + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + + savable_state cur; /* Current bit buffer & DC state */ + + j_compress_ptr cinfo; /* dump_buffer needs access to this */ + +} working_state; + + + + + +/* Forward declarations */ + +METHODDEF boolean encode_mcu_huff JPP((j_compress_ptr cinfo, + + JBLOCKROW *MCU_data)); + +METHODDEF void finish_pass_huff JPP((j_compress_ptr cinfo)); + +#ifdef ENTROPY_OPT_SUPPORTED + +METHODDEF boolean encode_mcu_gather JPP((j_compress_ptr cinfo, + + JBLOCKROW *MCU_data)); + +METHODDEF void finish_pass_gather JPP((j_compress_ptr cinfo)); + +#endif + + + + + +/* + + * Initialize for a Huffman-compressed scan. + + * If gather_statistics is TRUE, we do not output anything during the scan, + + * just count the Huffman symbols used and generate Huffman code tables. + + */ + + + +METHODDEF void + +start_pass_huff (j_compress_ptr cinfo, boolean gather_statistics) + +{ + + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + + int ci, dctbl, actbl; + + jpeg_component_info * compptr; + + + + if (gather_statistics) { + +#ifdef ENTROPY_OPT_SUPPORTED + + entropy->pub.encode_mcu = encode_mcu_gather; + + entropy->pub.finish_pass = finish_pass_gather; + +#else + + ERREXIT(cinfo, JERR_NOT_COMPILED); + +#endif + + } else { + + entropy->pub.encode_mcu = encode_mcu_huff; + + entropy->pub.finish_pass = finish_pass_huff; + + } + + + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + + compptr = cinfo->cur_comp_info[ci]; + + dctbl = compptr->dc_tbl_no; + + actbl = compptr->ac_tbl_no; + + /* Make sure requested tables are present */ + + /* (In gather mode, tables need not be allocated yet) */ + + if (dctbl < 0 || dctbl >= NUM_HUFF_TBLS || + + (cinfo->dc_huff_tbl_ptrs[dctbl] == NULL && !gather_statistics)) + + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, dctbl); + + if (actbl < 0 || actbl >= NUM_HUFF_TBLS || + + (cinfo->ac_huff_tbl_ptrs[actbl] == NULL && !gather_statistics)) + + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, actbl); + + if (gather_statistics) { + +#ifdef ENTROPY_OPT_SUPPORTED + + /* Allocate and zero the statistics tables */ + + /* Note that jpeg_gen_optimal_table expects 257 entries in each table! */ + + if (entropy->dc_count_ptrs[dctbl] == NULL) + + entropy->dc_count_ptrs[dctbl] = (long *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + 257 * SIZEOF(long)); + + MEMZERO(entropy->dc_count_ptrs[dctbl], 257 * SIZEOF(long)); + + if (entropy->ac_count_ptrs[actbl] == NULL) + + entropy->ac_count_ptrs[actbl] = (long *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + 257 * SIZEOF(long)); + + MEMZERO(entropy->ac_count_ptrs[actbl], 257 * SIZEOF(long)); + +#endif + + } else { + + /* Compute derived values for Huffman tables */ + + /* We may do this more than once for a table, but it's not expensive */ + + jpeg_make_c_derived_tbl(cinfo, cinfo->dc_huff_tbl_ptrs[dctbl], + + & entropy->dc_derived_tbls[dctbl]); + + jpeg_make_c_derived_tbl(cinfo, cinfo->ac_huff_tbl_ptrs[actbl], + + & entropy->ac_derived_tbls[actbl]); + + } + + /* Initialize DC predictions to 0 */ + + entropy->saved.last_dc_val[ci] = 0; + + } + + + + /* Initialize bit buffer to empty */ + + entropy->saved.put_buffer = 0; + + entropy->saved.put_bits = 0; + + + + /* Initialize restart stuff */ + + entropy->restarts_to_go = cinfo->restart_interval; + + entropy->next_restart_num = 0; + +} + + + + + +/* + + * Compute the derived values for a Huffman table. + + * Note this is also used by jcphuff.c. + + */ + + + +GLOBAL void + +jpeg_make_c_derived_tbl (j_compress_ptr cinfo, JHUFF_TBL * htbl, + + c_derived_tbl ** pdtbl) + +{ + + c_derived_tbl *dtbl; + + int p, i, l, lastp, si; + + char huffsize[257]; + + unsigned int huffcode[257]; + + unsigned int code; + + + + /* Allocate a workspace if we haven't already done so. */ + + if (*pdtbl == NULL) + + *pdtbl = (c_derived_tbl *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(c_derived_tbl)); + + dtbl = *pdtbl; + + + + /* Figure C.1: make table of Huffman code length for each symbol */ + + /* Note that this is in code-length order. */ + + + + p = 0; + + for (l = 1; l <= 16; l++) { + + for (i = 1; i <= (int) htbl->bits[l]; i++) + + huffsize[p++] = (char) l; + + } + + huffsize[p] = 0; + + lastp = p; + + + + /* Figure C.2: generate the codes themselves */ + + /* Note that this is in code-length order. */ + + + + code = 0; + + si = huffsize[0]; + + p = 0; + + while (huffsize[p]) { + + while (((int) huffsize[p]) == si) { + + huffcode[p++] = code; + + code++; + + } + + code <<= 1; + + si++; + + } + + + + /* Figure C.3: generate encoding tables */ + + /* These are code and size indexed by symbol value */ + + + + /* Set any codeless symbols to have code length 0; + + * this allows emit_bits to detect any attempt to emit such symbols. + + */ + + MEMZERO(dtbl->ehufsi, SIZEOF(dtbl->ehufsi)); + + + + for (p = 0; p < lastp; p++) { + + dtbl->ehufco[htbl->huffval[p]] = huffcode[p]; + + dtbl->ehufsi[htbl->huffval[p]] = huffsize[p]; + + } + +} + + + + + +/* Outputting bytes to the file */ + + + +/* Emit a byte, taking 'action' if must suspend. */ + +#define emit_byte(state,val,action) \ + + { *(state)->next_output_byte++ = (JOCTET) (val); \ + + if (--(state)->free_in_buffer == 0) \ + + if (! dump_buffer(state)) \ + + { action; } } + + + + + +LOCAL boolean + +dump_buffer (working_state * state) + +/* Empty the output buffer; return TRUE if successful, FALSE if must suspend */ + +{ + + struct jpeg_destination_mgr * dest = state->cinfo->dest; + + + + if (! (*dest->empty_output_buffer) (state->cinfo)) + + return FALSE; + + /* After a successful buffer dump, must reset buffer pointers */ + + state->next_output_byte = dest->next_output_byte; + + state->free_in_buffer = dest->free_in_buffer; + + return TRUE; + +} + + + + + +/* Outputting bits to the file */ + + + +/* Only the right 24 bits of put_buffer are used; the valid bits are + + * left-justified in this part. At most 16 bits can be passed to emit_bits + + * in one call, and we never retain more than 7 bits in put_buffer + + * between calls, so 24 bits are sufficient. + + */ + + + +// For VC++ + +//INLINE + +__inline + +LOCAL boolean + +emit_bits (working_state * state, unsigned int code, int size) + +/* Emit some bits; return TRUE if successful, FALSE if must suspend */ + +{ + + /* This routine is heavily used, so it's worth coding tightly. */ + + register INT32 put_buffer = (INT32) code; + + register int put_bits = state->cur.put_bits; + + + + /* if size is 0, caller used an invalid Huffman table entry */ + + if (size == 0) + + ERREXIT(state->cinfo, JERR_HUFF_MISSING_CODE); + + + + put_buffer &= (((INT32) 1)<cur.put_buffer; /* and merge with old buffer contents */ + + + + while (put_bits >= 8) { + + int c = (int) ((put_buffer >> 16) & 0xFF); + + + + emit_byte(state, c, return FALSE); + + if (c == 0xFF) { /* need to stuff a zero byte? */ + + emit_byte(state, 0, return FALSE); + + } + + put_buffer <<= 8; + + put_bits -= 8; + + } + + + + state->cur.put_buffer = put_buffer; /* update state variables */ + + state->cur.put_bits = put_bits; + + + + return TRUE; + +} + + + + + +LOCAL boolean + +flush_bits (working_state * state) + +{ + + if (! emit_bits(state, 0x7F, 7)) /* fill any partial byte with ones */ + + return FALSE; + + state->cur.put_buffer = 0; /* and reset bit-buffer to empty */ + + state->cur.put_bits = 0; + + return TRUE; + +} + + + + + +/* Encode a single block's worth of coefficients */ + + + +LOCAL boolean + +encode_one_block (working_state * state, JCOEFPTR block, int last_dc_val, + + c_derived_tbl *dctbl, c_derived_tbl *actbl) + +{ + + register int temp, temp2; + + register int nbits; + + register int k, r, i; + + + + /* Encode the DC coefficient difference per section F.1.2.1 */ + + + + temp = temp2 = block[0] - last_dc_val; + + + + if (temp < 0) { + + temp = -temp; /* temp is abs value of input */ + + /* For a negative input, want temp2 = bitwise complement of abs(input) */ + + /* This code assumes we are on a two's complement machine */ + + temp2--; + + } + + + + /* Find the number of bits needed for the magnitude of the coefficient */ + + nbits = 0; + + while (temp) { + + nbits++; + + temp >>= 1; + + } + + + + /* Emit the Huffman-coded symbol for the number of bits */ + + if (! emit_bits(state, dctbl->ehufco[nbits], dctbl->ehufsi[nbits])) + + return FALSE; + + + + /* Emit that number of bits of the value, if positive, */ + + /* or the complement of its magnitude, if negative. */ + + if (nbits) /* emit_bits rejects calls with size 0 */ + + if (! emit_bits(state, (unsigned int) temp2, nbits)) + + return FALSE; + + + + /* Encode the AC coefficients per section F.1.2.2 */ + + + + r = 0; /* r = run length of zeros */ + + + + for (k = 1; k < DCTSIZE2; k++) { + + if ((temp = block[jpeg_natural_order[k]]) == 0) { + + r++; + + } else { + + /* if run length > 15, must emit special run-length-16 codes (0xF0) */ + + while (r > 15) { + + if (! emit_bits(state, actbl->ehufco[0xF0], actbl->ehufsi[0xF0])) + + return FALSE; + + r -= 16; + + } + + + + temp2 = temp; + + if (temp < 0) { + + temp = -temp; /* temp is abs value of input */ + + /* This code assumes we are on a two's complement machine */ + + temp2--; + + } + + + + /* Find the number of bits needed for the magnitude of the coefficient */ + + nbits = 1; /* there must be at least one 1 bit */ + + while ((temp >>= 1)) + + nbits++; + + + + /* Emit Huffman symbol for run length / number of bits */ + + i = (r << 4) + nbits; + + if (! emit_bits(state, actbl->ehufco[i], actbl->ehufsi[i])) + + return FALSE; + + + + /* Emit that number of bits of the value, if positive, */ + + /* or the complement of its magnitude, if negative. */ + + if (! emit_bits(state, (unsigned int) temp2, nbits)) + + return FALSE; + + + + r = 0; + + } + + } + + + + /* If the last coef(s) were zero, emit an end-of-block code */ + + if (r > 0) + + if (! emit_bits(state, actbl->ehufco[0], actbl->ehufsi[0])) + + return FALSE; + + + + return TRUE; + +} + + + + + +/* + + * Emit a restart marker & resynchronize predictions. + + */ + + + +LOCAL boolean + +emit_restart (working_state * state, int restart_num) + +{ + + int ci; + + + + if (! flush_bits(state)) + + return FALSE; + + + + emit_byte(state, 0xFF, return FALSE); + + emit_byte(state, JPEG_RST0 + restart_num, return FALSE); + + + + /* Re-initialize DC predictions to 0 */ + + for (ci = 0; ci < state->cinfo->comps_in_scan; ci++) + + state->cur.last_dc_val[ci] = 0; + + + + /* The restart counter is not updated until we successfully write the MCU. */ + + + + return TRUE; + +} + + + + + +/* + + * Encode and output one MCU's worth of Huffman-compressed coefficients. + + */ + + + +METHODDEF boolean + +encode_mcu_huff (j_compress_ptr cinfo, JBLOCKROW *MCU_data) + +{ + + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + + working_state state; + + int blkn, ci; + + jpeg_component_info * compptr; + + + + /* Load up working state */ + + state.next_output_byte = cinfo->dest->next_output_byte; + + state.free_in_buffer = cinfo->dest->free_in_buffer; + + ASSIGN_STATE(state.cur, entropy->saved); + + state.cinfo = cinfo; + + + + /* Emit restart marker if needed */ + + if (cinfo->restart_interval) { + + if (entropy->restarts_to_go == 0) + + if (! emit_restart(&state, entropy->next_restart_num)) + + return FALSE; + + } + + + + /* Encode the MCU data blocks */ + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + + ci = cinfo->MCU_membership[blkn]; + + compptr = cinfo->cur_comp_info[ci]; + + if (! encode_one_block(&state, + + MCU_data[blkn][0], state.cur.last_dc_val[ci], + + entropy->dc_derived_tbls[compptr->dc_tbl_no], + + entropy->ac_derived_tbls[compptr->ac_tbl_no])) + + return FALSE; + + /* Update last_dc_val */ + + state.cur.last_dc_val[ci] = MCU_data[blkn][0][0]; + + } + + + + /* Completed MCU, so update state */ + + cinfo->dest->next_output_byte = state.next_output_byte; + + cinfo->dest->free_in_buffer = state.free_in_buffer; + + ASSIGN_STATE(entropy->saved, state.cur); + + + + /* Update restart-interval state too */ + + if (cinfo->restart_interval) { + + if (entropy->restarts_to_go == 0) { + + entropy->restarts_to_go = cinfo->restart_interval; + + entropy->next_restart_num++; + + entropy->next_restart_num &= 7; + + } + + entropy->restarts_to_go--; + + } + + + + return TRUE; + +} + + + + + +/* + + * Finish up at the end of a Huffman-compressed scan. + + */ + + + +METHODDEF void + +finish_pass_huff (j_compress_ptr cinfo) + +{ + + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + + working_state state; + + + + /* Load up working state ... flush_bits needs it */ + + state.next_output_byte = cinfo->dest->next_output_byte; + + state.free_in_buffer = cinfo->dest->free_in_buffer; + + ASSIGN_STATE(state.cur, entropy->saved); + + state.cinfo = cinfo; + + + + /* Flush out the last data */ + + if (! flush_bits(&state)) + + ERREXIT(cinfo, JERR_CANT_SUSPEND); + + + + /* Update state */ + + cinfo->dest->next_output_byte = state.next_output_byte; + + cinfo->dest->free_in_buffer = state.free_in_buffer; + + ASSIGN_STATE(entropy->saved, state.cur); + +} + + + + + +/* + + * Huffman coding optimization. + + * + + * This actually is optimization, in the sense that we find the best possible + + * Huffman table(s) for the given data. We first scan the supplied data and + + * count the number of uses of each symbol that is to be Huffman-coded. + + * (This process must agree with the code above.) Then we build an + + * optimal Huffman coding tree for the observed counts. + + * + + * The JPEG standard requires Huffman codes to be no more than 16 bits long. + + * If some symbols have a very small but nonzero probability, the Huffman tree + + * must be adjusted to meet the code length restriction. We currently use + + * the adjustment method suggested in the JPEG spec. This method is *not* + + * optimal; it may not choose the best possible limited-length code. But + + * since the symbols involved are infrequently used, it's not clear that + + * going to extra trouble is worthwhile. + + */ + + + +#ifdef ENTROPY_OPT_SUPPORTED + + + + + +/* Process a single block's worth of coefficients */ + + + +LOCAL void + +htest_one_block (JCOEFPTR block, int last_dc_val, + + long dc_counts[], long ac_counts[]) + +{ + + register int temp; + + register int nbits; + + register int k, r; + + + + /* Encode the DC coefficient difference per section F.1.2.1 */ + + + + temp = block[0] - last_dc_val; + + if (temp < 0) + + temp = -temp; + + + + /* Find the number of bits needed for the magnitude of the coefficient */ + + nbits = 0; + + while (temp) { + + nbits++; + + temp >>= 1; + + } + + + + /* Count the Huffman symbol for the number of bits */ + + dc_counts[nbits]++; + + + + /* Encode the AC coefficients per section F.1.2.2 */ + + + + r = 0; /* r = run length of zeros */ + + + + for (k = 1; k < DCTSIZE2; k++) { + + if ((temp = block[jpeg_natural_order[k]]) == 0) { + + r++; + + } else { + + /* if run length > 15, must emit special run-length-16 codes (0xF0) */ + + while (r > 15) { + + ac_counts[0xF0]++; + + r -= 16; + + } + + + + /* Find the number of bits needed for the magnitude of the coefficient */ + + if (temp < 0) + + temp = -temp; + + + + /* Find the number of bits needed for the magnitude of the coefficient */ + + nbits = 1; /* there must be at least one 1 bit */ + + while ((temp >>= 1)) + + nbits++; + + + + /* Count Huffman symbol for run length / number of bits */ + + ac_counts[(r << 4) + nbits]++; + + + + r = 0; + + } + + } + + + + /* If the last coef(s) were zero, emit an end-of-block code */ + + if (r > 0) + + ac_counts[0]++; + +} + + + + + +/* + + * Trial-encode one MCU's worth of Huffman-compressed coefficients. + + * No data is actually output, so no suspension return is possible. + + */ + + + +METHODDEF boolean + +encode_mcu_gather (j_compress_ptr cinfo, JBLOCKROW *MCU_data) + +{ + + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + + int blkn, ci; + + jpeg_component_info * compptr; + + + + /* Take care of restart intervals if needed */ + + if (cinfo->restart_interval) { + + if (entropy->restarts_to_go == 0) { + + /* Re-initialize DC predictions to 0 */ + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) + + entropy->saved.last_dc_val[ci] = 0; + + /* Update restart state */ + + entropy->restarts_to_go = cinfo->restart_interval; + + } + + entropy->restarts_to_go--; + + } + + + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + + ci = cinfo->MCU_membership[blkn]; + + compptr = cinfo->cur_comp_info[ci]; + + htest_one_block(MCU_data[blkn][0], entropy->saved.last_dc_val[ci], + + entropy->dc_count_ptrs[compptr->dc_tbl_no], + + entropy->ac_count_ptrs[compptr->ac_tbl_no]); + + entropy->saved.last_dc_val[ci] = MCU_data[blkn][0][0]; + + } + + + + return TRUE; + +} + + + + + +/* + + * Generate the optimal coding for the given counts, fill htbl. + + * Note this is also used by jcphuff.c. + + */ + + + +GLOBAL void + +jpeg_gen_optimal_table (j_compress_ptr cinfo, JHUFF_TBL * htbl, long freq[]) + +{ + +#define MAX_CLEN 32 /* assumed maximum initial code length */ + + UINT8 bits[MAX_CLEN+1]; /* bits[k] = # of symbols with code length k */ + + int codesize[257]; /* codesize[k] = code length of symbol k */ + + int others[257]; /* next symbol in current branch of tree */ + + int c1, c2; + + int p, i, j; + + long v; + + + + /* This algorithm is explained in section K.2 of the JPEG standard */ + + + + MEMZERO(bits, SIZEOF(bits)); + + MEMZERO(codesize, SIZEOF(codesize)); + + for (i = 0; i < 257; i++) + + others[i] = -1; /* init links to empty */ + + + + freq[256] = 1; /* make sure there is a nonzero count */ + + /* Including the pseudo-symbol 256 in the Huffman procedure guarantees + + * that no real symbol is given code-value of all ones, because 256 + + * will be placed in the largest codeword category. + + */ + + + + /* Huffman's basic algorithm to assign optimal code lengths to symbols */ + + + + for (;;) { + + /* Find the smallest nonzero frequency, set c1 = its symbol */ + + /* In case of ties, take the larger symbol number */ + + c1 = -1; + + v = 1000000000L; + + for (i = 0; i <= 256; i++) { + + if (freq[i] && freq[i] <= v) { + + v = freq[i]; + + c1 = i; + + } + + } + + + + /* Find the next smallest nonzero frequency, set c2 = its symbol */ + + /* In case of ties, take the larger symbol number */ + + c2 = -1; + + v = 1000000000L; + + for (i = 0; i <= 256; i++) { + + if (freq[i] && freq[i] <= v && i != c1) { + + v = freq[i]; + + c2 = i; + + } + + } + + + + /* Done if we've merged everything into one frequency */ + + if (c2 < 0) + + break; + + + + /* Else merge the two counts/trees */ + + freq[c1] += freq[c2]; + + freq[c2] = 0; + + + + /* Increment the codesize of everything in c1's tree branch */ + + codesize[c1]++; + + while (others[c1] >= 0) { + + c1 = others[c1]; + + codesize[c1]++; + + } + + + + others[c1] = c2; /* chain c2 onto c1's tree branch */ + + + + /* Increment the codesize of everything in c2's tree branch */ + + codesize[c2]++; + + while (others[c2] >= 0) { + + c2 = others[c2]; + + codesize[c2]++; + + } + + } + + + + /* Now count the number of symbols of each code length */ + + for (i = 0; i <= 256; i++) { + + if (codesize[i]) { + + /* The JPEG standard seems to think that this can't happen, */ + + /* but I'm paranoid... */ + + if (codesize[i] > MAX_CLEN) + + ERREXIT(cinfo, JERR_HUFF_CLEN_OVERFLOW); + + + + bits[codesize[i]]++; + + } + + } + + + + /* JPEG doesn't allow symbols with code lengths over 16 bits, so if the pure + + * Huffman procedure assigned any such lengths, we must adjust the coding. + + * Here is what the JPEG spec says about how this next bit works: + + * Since symbols are paired for the longest Huffman code, the symbols are + + * removed from this length category two at a time. The prefix for the pair + + * (which is one bit shorter) is allocated to one of the pair; then, + + * skipping the BITS entry for that prefix length, a code word from the next + + * shortest nonzero BITS entry is converted into a prefix for two code words + + * one bit longer. + + */ + + + + for (i = MAX_CLEN; i > 16; i--) { + + while (bits[i] > 0) { + + j = i - 2; /* find length of new prefix to be used */ + + while (bits[j] == 0) + + j--; + + + + bits[i] -= 2; /* remove two symbols */ + + bits[i-1]++; /* one goes in this length */ + + bits[j+1] += 2; /* two new symbols in this length */ + + bits[j]--; /* symbol of this length is now a prefix */ + + } + + } + + + + /* Remove the count for the pseudo-symbol 256 from the largest codelength */ + + while (bits[i] == 0) /* find largest codelength still in use */ + + i--; + + bits[i]--; + + + + /* Return final symbol counts (only for lengths 0..16) */ + + MEMCOPY(htbl->bits, bits, SIZEOF(htbl->bits)); + + + + /* Return a list of the symbols sorted by code length */ + + /* It's not real clear to me why we don't need to consider the codelength + + * changes made above, but the JPEG spec seems to think this works. + + */ + + p = 0; + + for (i = 1; i <= MAX_CLEN; i++) { + + for (j = 0; j <= 255; j++) { + + if (codesize[j] == i) { + + htbl->huffval[p] = (UINT8) j; + + p++; + + } + + } + + } + + + + /* Set sent_table FALSE so updated table will be written to JPEG file. */ + + htbl->sent_table = FALSE; + +} + + + + + +/* + + * Finish up a statistics-gathering pass and create the new Huffman tables. + + */ + + + +METHODDEF void + +finish_pass_gather (j_compress_ptr cinfo) + +{ + + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + + int ci, dctbl, actbl; + + jpeg_component_info * compptr; + + JHUFF_TBL **htblptr; + + boolean did_dc[NUM_HUFF_TBLS]; + + boolean did_ac[NUM_HUFF_TBLS]; + + + + /* It's important not to apply jpeg_gen_optimal_table more than once + + * per table, because it clobbers the input frequency counts! + + */ + + MEMZERO(did_dc, SIZEOF(did_dc)); + + MEMZERO(did_ac, SIZEOF(did_ac)); + + + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + + compptr = cinfo->cur_comp_info[ci]; + + dctbl = compptr->dc_tbl_no; + + actbl = compptr->ac_tbl_no; + + if (! did_dc[dctbl]) { + + htblptr = & cinfo->dc_huff_tbl_ptrs[dctbl]; + + if (*htblptr == NULL) + + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + + jpeg_gen_optimal_table(cinfo, *htblptr, entropy->dc_count_ptrs[dctbl]); + + did_dc[dctbl] = TRUE; + + } + + if (! did_ac[actbl]) { + + htblptr = & cinfo->ac_huff_tbl_ptrs[actbl]; + + if (*htblptr == NULL) + + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + + jpeg_gen_optimal_table(cinfo, *htblptr, entropy->ac_count_ptrs[actbl]); + + did_ac[actbl] = TRUE; + + } + + } + +} + + + + + +#endif /* ENTROPY_OPT_SUPPORTED */ + + + + + +/* + + * Module initialization routine for Huffman entropy encoding. + + */ + + + +GLOBAL void + +jinit_huff_encoder (j_compress_ptr cinfo) + +{ + + huff_entropy_ptr entropy; + + int i; + + + + entropy = (huff_entropy_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(huff_entropy_encoder)); + + cinfo->entropy = (struct jpeg_entropy_encoder *) entropy; + + entropy->pub.start_pass = start_pass_huff; + + + + /* Mark tables unallocated */ + + for (i = 0; i < NUM_HUFF_TBLS; i++) { + + entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL; + +#ifdef ENTROPY_OPT_SUPPORTED + + entropy->dc_count_ptrs[i] = entropy->ac_count_ptrs[i] = NULL; + +#endif + + } + +} + diff --git a/utils/roq2/jpeg/jchuff.h b/utils/roq2/jpeg/jchuff.h new file mode 100644 index 0000000..7ae05e8 --- /dev/null +++ b/utils/roq2/jpeg/jchuff.h @@ -0,0 +1,68 @@ +/* + + * jchuff.h + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains declarations for Huffman entropy encoding routines + + * that are shared between the sequential encoder (jchuff.c) and the + + * progressive encoder (jcphuff.c). No other modules need to see these. + + */ + + + +/* Derived data constructed for each Huffman table */ + + + +typedef struct { + + unsigned int ehufco[256]; /* code for each symbol */ + + char ehufsi[256]; /* length of code for each symbol */ + + /* If no code has been allocated for a symbol S, ehufsi[S] contains 0 */ + +} c_derived_tbl; + + + +/* Short forms of external names for systems with brain-damaged linkers. */ + + + +#ifdef NEED_SHORT_EXTERNAL_NAMES + +#define jpeg_make_c_derived_tbl jMkCDerived + +#define jpeg_gen_optimal_table jGenOptTbl + +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + + +/* Expand a Huffman table definition into the derived format */ + +EXTERN void jpeg_make_c_derived_tbl JPP((j_compress_ptr cinfo, + + JHUFF_TBL * htbl, c_derived_tbl ** pdtbl)); + + + +/* Generate an optimal table definition given the specified counts */ + +EXTERN void jpeg_gen_optimal_table JPP((j_compress_ptr cinfo, + + JHUFF_TBL * htbl, long freq[])); + diff --git a/utils/roq2/jpeg/jcinit.c b/utils/roq2/jpeg/jcinit.c new file mode 100644 index 0000000..cff35c1 --- /dev/null +++ b/utils/roq2/jpeg/jcinit.c @@ -0,0 +1,144 @@ +/* + + * jcinit.c + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains initialization logic for the JPEG compressor. + + * This routine is in charge of selecting the modules to be executed and + + * making an initialization call to each one. + + * + + * Logically, this code belongs in jcmaster.c. It's split out because + + * linking this routine implies linking the entire compression library. + + * For a transcoding-only application, we want to be able to use jcmaster.c + + * without linking in the whole library. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + + + +/* + + * Master selection of compression modules. + + * This is done once at the start of processing an image. We determine + + * which modules will be used and give them appropriate initialization calls. + + */ + + + +GLOBAL void + +jinit_compress_master (j_compress_ptr cinfo) + +{ + + /* Initialize master control (includes parameter checking/processing) */ + + jinit_c_master_control(cinfo, FALSE /* full compression */); + + + + /* Preprocessing */ + + if (! cinfo->raw_data_in) { + + jinit_color_converter(cinfo); + + jinit_downsampler(cinfo); + + jinit_c_prep_controller(cinfo, FALSE /* never need full buffer here */); + + } + + /* Forward DCT */ + + jinit_forward_dct(cinfo); + + /* Entropy encoding: either Huffman or arithmetic coding. */ + + if (cinfo->arith_code) { + + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + + } else { + + if (cinfo->progressive_mode) { + +#ifdef C_PROGRESSIVE_SUPPORTED + + jinit_phuff_encoder(cinfo); + +#else + + ERREXIT(cinfo, JERR_NOT_COMPILED); + +#endif + + } else + + jinit_huff_encoder(cinfo); + + } + + + + /* Need a full-image coefficient buffer in any multi-pass mode. */ + + jinit_c_coef_controller(cinfo, + + (cinfo->num_scans > 1 || cinfo->optimize_coding)); + + jinit_c_main_controller(cinfo, FALSE /* never need full buffer here */); + + + + jinit_marker_writer(cinfo); + + + + /* We can now tell the memory manager to allocate virtual arrays. */ + + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + + + /* Write the datastream header (SOI) immediately. + + * Frame and scan headers are postponed till later. + + * This lets application insert special markers after the SOI. + + */ + + (*cinfo->marker->write_file_header) (cinfo); + +} + diff --git a/utils/roq2/jpeg/jcmainct.c b/utils/roq2/jpeg/jcmainct.c new file mode 100644 index 0000000..12cb05a --- /dev/null +++ b/utils/roq2/jpeg/jcmainct.c @@ -0,0 +1,586 @@ +/* + + * jcmainct.c + + * + + * Copyright (C) 1994-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains the main buffer controller for compression. + + * The main buffer lies between the pre-processor and the JPEG + + * compressor proper; it holds downsampled data in the JPEG colorspace. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + + + +/* Note: currently, there is no operating mode in which a full-image buffer + + * is needed at this step. If there were, that mode could not be used with + + * "raw data" input, since this module is bypassed in that case. However, + + * we've left the code here for possible use in special applications. + + */ + +#undef FULL_MAIN_BUFFER_SUPPORTED + + + + + +/* Private buffer controller object */ + + + +typedef struct { + + struct jpeg_c_main_controller pub; /* public fields */ + + + + JDIMENSION cur_iMCU_row; /* number of current iMCU row */ + + JDIMENSION rowgroup_ctr; /* counts row groups received in iMCU row */ + + boolean suspended; /* remember if we suspended output */ + + J_BUF_MODE pass_mode; /* current operating mode */ + + + + /* If using just a strip buffer, this points to the entire set of buffers + + * (we allocate one for each component). In the full-image case, this + + * points to the currently accessible strips of the virtual arrays. + + */ + + JSAMPARRAY buffer[MAX_COMPONENTS]; + + + +#ifdef FULL_MAIN_BUFFER_SUPPORTED + + /* If using full-image storage, this array holds pointers to virtual-array + + * control blocks for each component. Unused if not full-image storage. + + */ + + jvirt_sarray_ptr whole_image[MAX_COMPONENTS]; + +#endif + +} my_main_controller; + + + +typedef my_main_controller * my_main_ptr; + + + + + +/* Forward declarations */ + +METHODDEF void process_data_simple_main + + JPP((j_compress_ptr cinfo, JSAMPARRAY input_buf, + + JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail)); + +#ifdef FULL_MAIN_BUFFER_SUPPORTED + +METHODDEF void process_data_buffer_main + + JPP((j_compress_ptr cinfo, JSAMPARRAY input_buf, + + JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail)); + +#endif + + + + + +/* + + * Initialize for a processing pass. + + */ + + + +METHODDEF void + +start_pass_main (j_compress_ptr cinfo, J_BUF_MODE pass_mode) + +{ + + my_main_ptr main = (my_main_ptr) cinfo->main; + + + + /* Do nothing in raw-data mode. */ + + if (cinfo->raw_data_in) + + return; + + + + main->cur_iMCU_row = 0; /* initialize counters */ + + main->rowgroup_ctr = 0; + + main->suspended = FALSE; + + main->pass_mode = pass_mode; /* save mode for use by process_data */ + + + + switch (pass_mode) { + + case JBUF_PASS_THRU: + +#ifdef FULL_MAIN_BUFFER_SUPPORTED + + if (main->whole_image[0] != NULL) + + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + +#endif + + main->pub.process_data = process_data_simple_main; + + break; + +#ifdef FULL_MAIN_BUFFER_SUPPORTED + + case JBUF_SAVE_SOURCE: + + case JBUF_CRANK_DEST: + + case JBUF_SAVE_AND_PASS: + + if (main->whole_image[0] == NULL) + + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + main->pub.process_data = process_data_buffer_main; + + break; + +#endif + + default: + + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + break; + + } + +} + + + + + +/* + + * Process some data. + + * This routine handles the simple pass-through mode, + + * where we have only a strip buffer. + + */ + + + +METHODDEF void + +process_data_simple_main (j_compress_ptr cinfo, + + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + + JDIMENSION in_rows_avail) + +{ + + my_main_ptr main = (my_main_ptr) cinfo->main; + + + + while (main->cur_iMCU_row < cinfo->total_iMCU_rows) { + + /* Read input data if we haven't filled the main buffer yet */ + + if (main->rowgroup_ctr < DCTSIZE) + + (*cinfo->prep->pre_process_data) (cinfo, + + input_buf, in_row_ctr, in_rows_avail, + + main->buffer, &main->rowgroup_ctr, + + (JDIMENSION) DCTSIZE); + + + + /* If we don't have a full iMCU row buffered, return to application for + + * more data. Note that preprocessor will always pad to fill the iMCU row + + * at the bottom of the image. + + */ + + if (main->rowgroup_ctr != DCTSIZE) + + return; + + + + /* Send the completed row to the compressor */ + + if (! (*cinfo->coef->compress_data) (cinfo, main->buffer)) { + + /* If compressor did not consume the whole row, then we must need to + + * suspend processing and return to the application. In this situation + + * we pretend we didn't yet consume the last input row; otherwise, if + + * it happened to be the last row of the image, the application would + + * think we were done. + + */ + + if (! main->suspended) { + + (*in_row_ctr)--; + + main->suspended = TRUE; + + } + + return; + + } + + /* We did finish the row. Undo our little suspension hack if a previous + + * call suspended; then mark the main buffer empty. + + */ + + if (main->suspended) { + + (*in_row_ctr)++; + + main->suspended = FALSE; + + } + + main->rowgroup_ctr = 0; + + main->cur_iMCU_row++; + + } + +} + + + + + +#ifdef FULL_MAIN_BUFFER_SUPPORTED + + + +/* + + * Process some data. + + * This routine handles all of the modes that use a full-size buffer. + + */ + + + +METHODDEF void + +process_data_buffer_main (j_compress_ptr cinfo, + + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + + JDIMENSION in_rows_avail) + +{ + + my_main_ptr main = (my_main_ptr) cinfo->main; + + int ci; + + jpeg_component_info *compptr; + + boolean writing = (main->pass_mode != JBUF_CRANK_DEST); + + + + while (main->cur_iMCU_row < cinfo->total_iMCU_rows) { + + /* Realign the virtual buffers if at the start of an iMCU row. */ + + if (main->rowgroup_ctr == 0) { + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + main->buffer[ci] = (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, main->whole_image[ci], + + main->cur_iMCU_row * (compptr->v_samp_factor * DCTSIZE), + + (JDIMENSION) (compptr->v_samp_factor * DCTSIZE), writing); + + } + + /* In a read pass, pretend we just read some source data. */ + + if (! writing) { + + *in_row_ctr += cinfo->max_v_samp_factor * DCTSIZE; + + main->rowgroup_ctr = DCTSIZE; + + } + + } + + + + /* If a write pass, read input data until the current iMCU row is full. */ + + /* Note: preprocessor will pad if necessary to fill the last iMCU row. */ + + if (writing) { + + (*cinfo->prep->pre_process_data) (cinfo, + + input_buf, in_row_ctr, in_rows_avail, + + main->buffer, &main->rowgroup_ctr, + + (JDIMENSION) DCTSIZE); + + /* Return to application if we need more data to fill the iMCU row. */ + + if (main->rowgroup_ctr < DCTSIZE) + + return; + + } + + + + /* Emit data, unless this is a sink-only pass. */ + + if (main->pass_mode != JBUF_SAVE_SOURCE) { + + if (! (*cinfo->coef->compress_data) (cinfo, main->buffer)) { + + /* If compressor did not consume the whole row, then we must need to + + * suspend processing and return to the application. In this situation + + * we pretend we didn't yet consume the last input row; otherwise, if + + * it happened to be the last row of the image, the application would + + * think we were done. + + */ + + if (! main->suspended) { + + (*in_row_ctr)--; + + main->suspended = TRUE; + + } + + return; + + } + + /* We did finish the row. Undo our little suspension hack if a previous + + * call suspended; then mark the main buffer empty. + + */ + + if (main->suspended) { + + (*in_row_ctr)++; + + main->suspended = FALSE; + + } + + } + + + + /* If get here, we are done with this iMCU row. Mark buffer empty. */ + + main->rowgroup_ctr = 0; + + main->cur_iMCU_row++; + + } + +} + + + +#endif /* FULL_MAIN_BUFFER_SUPPORTED */ + + + + + +/* + + * Initialize main buffer controller. + + */ + + + +GLOBAL void + +jinit_c_main_controller (j_compress_ptr cinfo, boolean need_full_buffer) + +{ + + my_main_ptr main; + + int ci; + + jpeg_component_info *compptr; + + + + main = (my_main_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(my_main_controller)); + + cinfo->main = (struct jpeg_c_main_controller *) main; + + main->pub.start_pass = start_pass_main; + + + + /* We don't need to create a buffer in raw-data mode. */ + + if (cinfo->raw_data_in) + + return; + + + + /* Create the buffer. It holds downsampled data, so each component + + * may be of a different size. + + */ + + if (need_full_buffer) { + +#ifdef FULL_MAIN_BUFFER_SUPPORTED + + /* Allocate a full-image virtual array for each component */ + + /* Note we pad the bottom to a multiple of the iMCU height */ + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + main->whole_image[ci] = (*cinfo->mem->request_virt_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + + compptr->width_in_blocks * DCTSIZE, + + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + + (long) compptr->v_samp_factor) * DCTSIZE, + + (JDIMENSION) (compptr->v_samp_factor * DCTSIZE)); + + } + +#else + + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + +#endif + + } else { + +#ifdef FULL_MAIN_BUFFER_SUPPORTED + + main->whole_image[0] = NULL; /* flag for no virtual arrays */ + +#endif + + /* Allocate a strip buffer for each component */ + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + main->buffer[ci] = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, + + compptr->width_in_blocks * DCTSIZE, + + (JDIMENSION) (compptr->v_samp_factor * DCTSIZE)); + + } + + } + +} + diff --git a/utils/roq2/jpeg/jcmarker.c b/utils/roq2/jpeg/jcmarker.c new file mode 100644 index 0000000..ce6fd1d --- /dev/null +++ b/utils/roq2/jpeg/jcmarker.c @@ -0,0 +1,1278 @@ +/* + + * jcmarker.c + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains routines to write JPEG datastream markers. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + + + +typedef enum { /* JPEG marker codes */ + + M_SOF0 = 0xc0, + + M_SOF1 = 0xc1, + + M_SOF2 = 0xc2, + + M_SOF3 = 0xc3, + + + + M_SOF5 = 0xc5, + + M_SOF6 = 0xc6, + + M_SOF7 = 0xc7, + + + + M_JPG = 0xc8, + + M_SOF9 = 0xc9, + + M_SOF10 = 0xca, + + M_SOF11 = 0xcb, + + + + M_SOF13 = 0xcd, + + M_SOF14 = 0xce, + + M_SOF15 = 0xcf, + + + + M_DHT = 0xc4, + + + + M_DAC = 0xcc, + + + + M_RST0 = 0xd0, + + M_RST1 = 0xd1, + + M_RST2 = 0xd2, + + M_RST3 = 0xd3, + + M_RST4 = 0xd4, + + M_RST5 = 0xd5, + + M_RST6 = 0xd6, + + M_RST7 = 0xd7, + + + + M_SOI = 0xd8, + + M_EOI = 0xd9, + + M_SOS = 0xda, + + M_DQT = 0xdb, + + M_DNL = 0xdc, + + M_DRI = 0xdd, + + M_DHP = 0xde, + + M_EXP = 0xdf, + + + + M_APP0 = 0xe0, + + M_APP1 = 0xe1, + + M_APP2 = 0xe2, + + M_APP3 = 0xe3, + + M_APP4 = 0xe4, + + M_APP5 = 0xe5, + + M_APP6 = 0xe6, + + M_APP7 = 0xe7, + + M_APP8 = 0xe8, + + M_APP9 = 0xe9, + + M_APP10 = 0xea, + + M_APP11 = 0xeb, + + M_APP12 = 0xec, + + M_APP13 = 0xed, + + M_APP14 = 0xee, + + M_APP15 = 0xef, + + + + M_JPG0 = 0xf0, + + M_JPG13 = 0xfd, + + M_COM = 0xfe, + + + + M_TEM = 0x01, + + + + M_ERROR = 0x100 + +} JPEG_MARKER; + + + + + +/* + + * Basic output routines. + + * + + * Note that we do not support suspension while writing a marker. + + * Therefore, an application using suspension must ensure that there is + + * enough buffer space for the initial markers (typ. 600-700 bytes) before + + * calling jpeg_start_compress, and enough space to write the trailing EOI + + * (a few bytes) before calling jpeg_finish_compress. Multipass compression + + * modes are not supported at all with suspension, so those two are the only + + * points where markers will be written. + + */ + + + +LOCAL void + +emit_byte (j_compress_ptr cinfo, int val) + +/* Emit a byte */ + +{ + + struct jpeg_destination_mgr * dest = cinfo->dest; + + + + *(dest->next_output_byte)++ = (JOCTET) val; + + if (--dest->free_in_buffer == 0) { + + if (! (*dest->empty_output_buffer) (cinfo)) + + ERREXIT(cinfo, JERR_CANT_SUSPEND); + + } + +} + + + + + +LOCAL void + +emit_marker (j_compress_ptr cinfo, JPEG_MARKER mark) + +/* Emit a marker code */ + +{ + + emit_byte(cinfo, 0xFF); + + emit_byte(cinfo, (int) mark); + +} + + + + + +LOCAL void + +emit_2bytes (j_compress_ptr cinfo, int value) + +/* Emit a 2-byte integer; these are always MSB first in JPEG files */ + +{ + + emit_byte(cinfo, (value >> 8) & 0xFF); + + emit_byte(cinfo, value & 0xFF); + +} + + + + + +/* + + * Routines to write specific marker types. + + */ + + + +LOCAL int + +emit_dqt (j_compress_ptr cinfo, int index) + +/* Emit a DQT marker */ + +/* Returns the precision used (0 = 8bits, 1 = 16bits) for baseline checking */ + +{ + + JQUANT_TBL * qtbl = cinfo->quant_tbl_ptrs[index]; + + int prec; + + int i; + + + + if (qtbl == NULL) + + ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, index); + + + + prec = 0; + + for (i = 0; i < DCTSIZE2; i++) { + + if (qtbl->quantval[i] > 255) + + prec = 1; + + } + + + + if (! qtbl->sent_table) { + + emit_marker(cinfo, M_DQT); + + + + emit_2bytes(cinfo, prec ? DCTSIZE2*2 + 1 + 2 : DCTSIZE2 + 1 + 2); + + + + emit_byte(cinfo, index + (prec<<4)); + + + + for (i = 0; i < DCTSIZE2; i++) { + + if (prec) + + emit_byte(cinfo, qtbl->quantval[i] >> 8); + + emit_byte(cinfo, qtbl->quantval[i] & 0xFF); + + } + + + + qtbl->sent_table = TRUE; + + } + + + + return prec; + +} + + + + + +LOCAL void + +emit_dht (j_compress_ptr cinfo, int index, boolean is_ac) + +/* Emit a DHT marker */ + +{ + + JHUFF_TBL * htbl; + + int length, i; + + + + if (is_ac) { + + htbl = cinfo->ac_huff_tbl_ptrs[index]; + + index += 0x10; /* output index has AC bit set */ + + } else { + + htbl = cinfo->dc_huff_tbl_ptrs[index]; + + } + + + + if (htbl == NULL) + + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, index); + + + + if (! htbl->sent_table) { + + emit_marker(cinfo, M_DHT); + + + + length = 0; + + for (i = 1; i <= 16; i++) + + length += htbl->bits[i]; + + + + emit_2bytes(cinfo, length + 2 + 1 + 16); + + emit_byte(cinfo, index); + + + + for (i = 1; i <= 16; i++) + + emit_byte(cinfo, htbl->bits[i]); + + + + for (i = 0; i < length; i++) + + emit_byte(cinfo, htbl->huffval[i]); + + + + htbl->sent_table = TRUE; + + } + +} + + + + + +LOCAL void + +emit_dac (j_compress_ptr cinfo) + +/* Emit a DAC marker */ + +/* Since the useful info is so small, we want to emit all the tables in */ + +/* one DAC marker. Therefore this routine does its own scan of the table. */ + +{ + +#ifdef C_ARITH_CODING_SUPPORTED + + char dc_in_use[NUM_ARITH_TBLS]; + + char ac_in_use[NUM_ARITH_TBLS]; + + int length, i; + + jpeg_component_info *compptr; + + + + for (i = 0; i < NUM_ARITH_TBLS; i++) + + dc_in_use[i] = ac_in_use[i] = 0; + + + + for (i = 0; i < cinfo->comps_in_scan; i++) { + + compptr = cinfo->cur_comp_info[i]; + + dc_in_use[compptr->dc_tbl_no] = 1; + + ac_in_use[compptr->ac_tbl_no] = 1; + + } + + + + length = 0; + + for (i = 0; i < NUM_ARITH_TBLS; i++) + + length += dc_in_use[i] + ac_in_use[i]; + + + + emit_marker(cinfo, M_DAC); + + + + emit_2bytes(cinfo, length*2 + 2); + + + + for (i = 0; i < NUM_ARITH_TBLS; i++) { + + if (dc_in_use[i]) { + + emit_byte(cinfo, i); + + emit_byte(cinfo, cinfo->arith_dc_L[i] + (cinfo->arith_dc_U[i]<<4)); + + } + + if (ac_in_use[i]) { + + emit_byte(cinfo, i + 0x10); + + emit_byte(cinfo, cinfo->arith_ac_K[i]); + + } + + } + +#endif /* C_ARITH_CODING_SUPPORTED */ + +} + + + + + +LOCAL void + +emit_dri (j_compress_ptr cinfo) + +/* Emit a DRI marker */ + +{ + + emit_marker(cinfo, M_DRI); + + + + emit_2bytes(cinfo, 4); /* fixed length */ + + + + emit_2bytes(cinfo, (int) cinfo->restart_interval); + +} + + + + + +LOCAL void + +emit_sof (j_compress_ptr cinfo, JPEG_MARKER code) + +/* Emit a SOF marker */ + +{ + + int ci; + + jpeg_component_info *compptr; + + + + emit_marker(cinfo, code); + + + + emit_2bytes(cinfo, 3 * cinfo->num_components + 2 + 5 + 1); /* length */ + + + + /* Make sure image isn't bigger than SOF field can handle */ + + if ((long) cinfo->image_height > 65535L || + + (long) cinfo->image_width > 65535L) + + ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) 65535); + + + + emit_byte(cinfo, cinfo->data_precision); + + emit_2bytes(cinfo, (int) cinfo->image_height); + + emit_2bytes(cinfo, (int) cinfo->image_width); + + + + emit_byte(cinfo, cinfo->num_components); + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + emit_byte(cinfo, compptr->component_id); + + emit_byte(cinfo, (compptr->h_samp_factor << 4) + compptr->v_samp_factor); + + emit_byte(cinfo, compptr->quant_tbl_no); + + } + +} + + + + + +LOCAL void + +emit_sos (j_compress_ptr cinfo) + +/* Emit a SOS marker */ + +{ + + int i, td, ta; + + jpeg_component_info *compptr; + + + + emit_marker(cinfo, M_SOS); + + + + emit_2bytes(cinfo, 2 * cinfo->comps_in_scan + 2 + 1 + 3); /* length */ + + + + emit_byte(cinfo, cinfo->comps_in_scan); + + + + for (i = 0; i < cinfo->comps_in_scan; i++) { + + compptr = cinfo->cur_comp_info[i]; + + emit_byte(cinfo, compptr->component_id); + + td = compptr->dc_tbl_no; + + ta = compptr->ac_tbl_no; + + if (cinfo->progressive_mode) { + + /* Progressive mode: only DC or only AC tables are used in one scan; + + * furthermore, Huffman coding of DC refinement uses no table at all. + + * We emit 0 for unused field(s); this is recommended by the P&M text + + * but does not seem to be specified in the standard. + + */ + + if (cinfo->Ss == 0) { + + ta = 0; /* DC scan */ + + if (cinfo->Ah != 0 && !cinfo->arith_code) + + td = 0; /* no DC table either */ + + } else { + + td = 0; /* AC scan */ + + } + + } + + emit_byte(cinfo, (td << 4) + ta); + + } + + + + emit_byte(cinfo, cinfo->Ss); + + emit_byte(cinfo, cinfo->Se); + + emit_byte(cinfo, (cinfo->Ah << 4) + cinfo->Al); + +} + + + + + +LOCAL void + +emit_jfif_app0 (j_compress_ptr cinfo) + +/* Emit a JFIF-compliant APP0 marker */ + +{ + + /* + + * Length of APP0 block (2 bytes) + + * Block ID (4 bytes - ASCII "JFIF") + + * Zero byte (1 byte to terminate the ID string) + + * Version Major, Minor (2 bytes - 0x01, 0x01) + + * Units (1 byte - 0x00 = none, 0x01 = inch, 0x02 = cm) + + * Xdpu (2 bytes - dots per unit horizontal) + + * Ydpu (2 bytes - dots per unit vertical) + + * Thumbnail X size (1 byte) + + * Thumbnail Y size (1 byte) + + */ + + + + emit_marker(cinfo, M_APP0); + + + + emit_2bytes(cinfo, 2 + 4 + 1 + 2 + 1 + 2 + 2 + 1 + 1); /* length */ + + + + emit_byte(cinfo, 0x4A); /* Identifier: ASCII "JFIF" */ + + emit_byte(cinfo, 0x46); + + emit_byte(cinfo, 0x49); + + emit_byte(cinfo, 0x46); + + emit_byte(cinfo, 0); + + /* We currently emit version code 1.01 since we use no 1.02 features. + + * This may avoid complaints from some older decoders. + + */ + + emit_byte(cinfo, 1); /* Major version */ + + emit_byte(cinfo, 1); /* Minor version */ + + emit_byte(cinfo, cinfo->density_unit); /* Pixel size information */ + + emit_2bytes(cinfo, (int) cinfo->X_density); + + emit_2bytes(cinfo, (int) cinfo->Y_density); + + emit_byte(cinfo, 0); /* No thumbnail image */ + + emit_byte(cinfo, 0); + +} + + + + + +LOCAL void + +emit_adobe_app14 (j_compress_ptr cinfo) + +/* Emit an Adobe APP14 marker */ + +{ + + /* + + * Length of APP14 block (2 bytes) + + * Block ID (5 bytes - ASCII "Adobe") + + * Version Number (2 bytes - currently 100) + + * Flags0 (2 bytes - currently 0) + + * Flags1 (2 bytes - currently 0) + + * Color transform (1 byte) + + * + + * Although Adobe TN 5116 mentions Version = 101, all the Adobe files + + * now in circulation seem to use Version = 100, so that's what we write. + + * + + * We write the color transform byte as 1 if the JPEG color space is + + * YCbCr, 2 if it's YCCK, 0 otherwise. Adobe's definition has to do with + + * whether the encoder performed a transformation, which is pretty useless. + + */ + + + + emit_marker(cinfo, M_APP14); + + + + emit_2bytes(cinfo, 2 + 5 + 2 + 2 + 2 + 1); /* length */ + + + + emit_byte(cinfo, 0x41); /* Identifier: ASCII "Adobe" */ + + emit_byte(cinfo, 0x64); + + emit_byte(cinfo, 0x6F); + + emit_byte(cinfo, 0x62); + + emit_byte(cinfo, 0x65); + + emit_2bytes(cinfo, 100); /* Version */ + + emit_2bytes(cinfo, 0); /* Flags0 */ + + emit_2bytes(cinfo, 0); /* Flags1 */ + + switch (cinfo->jpeg_color_space) { + + case JCS_YCbCr: + + emit_byte(cinfo, 1); /* Color transform = 1 */ + + break; + + case JCS_YCCK: + + emit_byte(cinfo, 2); /* Color transform = 2 */ + + break; + + default: + + emit_byte(cinfo, 0); /* Color transform = 0 */ + + break; + + } + +} + + + + + +/* + + * This routine is exported for possible use by applications. + + * The intended use is to emit COM or APPn markers after calling + + * jpeg_start_compress() and before the first jpeg_write_scanlines() call + + * (hence, after write_file_header but before write_frame_header). + + * Other uses are not guaranteed to produce desirable results. + + */ + + + +METHODDEF void + +write_any_marker (j_compress_ptr cinfo, int marker, + + const JOCTET *dataptr, unsigned int datalen) + +/* Emit an arbitrary marker with parameters */ + +{ + + if (datalen <= (unsigned int) 65533) { /* safety check */ + + emit_marker(cinfo, (JPEG_MARKER) marker); + + + + emit_2bytes(cinfo, (int) (datalen + 2)); /* total length */ + + + + while (datalen--) { + + emit_byte(cinfo, *dataptr); + + dataptr++; + + } + + } + +} + + + + + +/* + + * Write datastream header. + + * This consists of an SOI and optional APPn markers. + + * We recommend use of the JFIF marker, but not the Adobe marker, + + * when using YCbCr or grayscale data. The JFIF marker should NOT + + * be used for any other JPEG colorspace. The Adobe marker is helpful + + * to distinguish RGB, CMYK, and YCCK colorspaces. + + * Note that an application can write additional header markers after + + * jpeg_start_compress returns. + + */ + + + +METHODDEF void + +write_file_header (j_compress_ptr cinfo) + +{ + + emit_marker(cinfo, M_SOI); /* first the SOI */ + + + + if (cinfo->write_JFIF_header) /* next an optional JFIF APP0 */ + + emit_jfif_app0(cinfo); + + if (cinfo->write_Adobe_marker) /* next an optional Adobe APP14 */ + + emit_adobe_app14(cinfo); + +} + + + + + +/* + + * Write frame header. + + * This consists of DQT and SOFn markers. + + * Note that we do not emit the SOF until we have emitted the DQT(s). + + * This avoids compatibility problems with incorrect implementations that + + * try to error-check the quant table numbers as soon as they see the SOF. + + */ + + + +METHODDEF void + +write_frame_header (j_compress_ptr cinfo) + +{ + + int ci, prec; + + boolean is_baseline; + + jpeg_component_info *compptr; + + + + /* Emit DQT for each quantization table. + + * Note that emit_dqt() suppresses any duplicate tables. + + */ + + prec = 0; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + prec += emit_dqt(cinfo, compptr->quant_tbl_no); + + } + + /* now prec is nonzero iff there are any 16-bit quant tables. */ + + + + /* Check for a non-baseline specification. + + * Note we assume that Huffman table numbers won't be changed later. + + */ + + if (cinfo->arith_code || cinfo->progressive_mode || + + cinfo->data_precision != 8) { + + is_baseline = FALSE; + + } else { + + is_baseline = TRUE; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + if (compptr->dc_tbl_no > 1 || compptr->ac_tbl_no > 1) + + is_baseline = FALSE; + + } + + if (prec && is_baseline) { + + is_baseline = FALSE; + + /* If it's baseline except for quantizer size, warn the user */ + + TRACEMS(cinfo, 0, JTRC_16BIT_TABLES); + + } + + } + + + + /* Emit the proper SOF marker */ + + if (cinfo->arith_code) { + + emit_sof(cinfo, M_SOF9); /* SOF code for arithmetic coding */ + + } else { + + if (cinfo->progressive_mode) + + emit_sof(cinfo, M_SOF2); /* SOF code for progressive Huffman */ + + else if (is_baseline) + + emit_sof(cinfo, M_SOF0); /* SOF code for baseline implementation */ + + else + + emit_sof(cinfo, M_SOF1); /* SOF code for non-baseline Huffman file */ + + } + +} + + + + + +/* + + * Write scan header. + + * This consists of DHT or DAC markers, optional DRI, and SOS. + + * Compressed data will be written following the SOS. + + */ + + + +METHODDEF void + +write_scan_header (j_compress_ptr cinfo) + +{ + + int i; + + jpeg_component_info *compptr; + + + + if (cinfo->arith_code) { + + /* Emit arith conditioning info. We may have some duplication + + * if the file has multiple scans, but it's so small it's hardly + + * worth worrying about. + + */ + + emit_dac(cinfo); + + } else { + + /* Emit Huffman tables. + + * Note that emit_dht() suppresses any duplicate tables. + + */ + + for (i = 0; i < cinfo->comps_in_scan; i++) { + + compptr = cinfo->cur_comp_info[i]; + + if (cinfo->progressive_mode) { + + /* Progressive mode: only DC or only AC tables are used in one scan */ + + if (cinfo->Ss == 0) { + + if (cinfo->Ah == 0) /* DC needs no table for refinement scan */ + + emit_dht(cinfo, compptr->dc_tbl_no, FALSE); + + } else { + + emit_dht(cinfo, compptr->ac_tbl_no, TRUE); + + } + + } else { + + /* Sequential mode: need both DC and AC tables */ + + emit_dht(cinfo, compptr->dc_tbl_no, FALSE); + + emit_dht(cinfo, compptr->ac_tbl_no, TRUE); + + } + + } + + } + + + + /* Emit DRI if required --- note that DRI value could change for each scan. + + * If it doesn't, a tiny amount of space is wasted in multiple-scan files. + + * We assume DRI will never be nonzero for one scan and zero for a later one. + + */ + + if (cinfo->restart_interval) + + emit_dri(cinfo); + + + + emit_sos(cinfo); + +} + + + + + +/* + + * Write datastream trailer. + + */ + + + +METHODDEF void + +write_file_trailer (j_compress_ptr cinfo) + +{ + + emit_marker(cinfo, M_EOI); + +} + + + + + +/* + + * Write an abbreviated table-specification datastream. + + * This consists of SOI, DQT and DHT tables, and EOI. + + * Any table that is defined and not marked sent_table = TRUE will be + + * emitted. Note that all tables will be marked sent_table = TRUE at exit. + + */ + + + +METHODDEF void + +write_tables_only (j_compress_ptr cinfo) + +{ + + int i; + + + + emit_marker(cinfo, M_SOI); + + + + for (i = 0; i < NUM_QUANT_TBLS; i++) { + + if (cinfo->quant_tbl_ptrs[i] != NULL) + + (void) emit_dqt(cinfo, i); + + } + + + + if (! cinfo->arith_code) { + + for (i = 0; i < NUM_HUFF_TBLS; i++) { + + if (cinfo->dc_huff_tbl_ptrs[i] != NULL) + + emit_dht(cinfo, i, FALSE); + + if (cinfo->ac_huff_tbl_ptrs[i] != NULL) + + emit_dht(cinfo, i, TRUE); + + } + + } + + + + emit_marker(cinfo, M_EOI); + +} + + + + + +/* + + * Initialize the marker writer module. + + */ + + + +GLOBAL void + +jinit_marker_writer (j_compress_ptr cinfo) + +{ + + /* Create the subobject */ + + cinfo->marker = (struct jpeg_marker_writer *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(struct jpeg_marker_writer)); + + /* Initialize method pointers */ + + cinfo->marker->write_any_marker = write_any_marker; + + cinfo->marker->write_file_header = write_file_header; + + cinfo->marker->write_frame_header = write_frame_header; + + cinfo->marker->write_scan_header = write_scan_header; + + cinfo->marker->write_file_trailer = write_file_trailer; + + cinfo->marker->write_tables_only = write_tables_only; + +} + diff --git a/utils/roq2/jpeg/jcmaster.c b/utils/roq2/jpeg/jcmaster.c new file mode 100644 index 0000000..ff227be --- /dev/null +++ b/utils/roq2/jpeg/jcmaster.c @@ -0,0 +1,1156 @@ +/* + + * jcmaster.c + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains master control logic for the JPEG compressor. + + * These routines are concerned with parameter validation, initial setup, + + * and inter-pass control (determining the number of passes and the work + + * to be done in each pass). + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + + + +/* Private state */ + + + +typedef enum { + + main_pass, /* input data, also do first output step */ + + huff_opt_pass, /* Huffman code optimization pass */ + + output_pass /* data output pass */ + +} c_pass_type; + + + +typedef struct { + + struct jpeg_comp_master pub; /* public fields */ + + + + c_pass_type pass_type; /* the type of the current pass */ + + + + int pass_number; /* # of passes completed */ + + int total_passes; /* total # of passes needed */ + + + + int scan_number; /* current index in scan_info[] */ + +} my_comp_master; + + + +typedef my_comp_master * my_master_ptr; + + + + + +/* + + * Support routines that do various essential calculations. + + */ + + + +LOCAL void + +initial_setup (j_compress_ptr cinfo) + +/* Do computations that are needed before master selection phase */ + +{ + + int ci; + + jpeg_component_info *compptr; + + long samplesperrow; + + JDIMENSION jd_samplesperrow; + + + + /* Sanity check on image dimensions */ + + if (cinfo->image_height <= 0 || cinfo->image_width <= 0 + + || cinfo->num_components <= 0 || cinfo->input_components <= 0) + + ERREXIT(cinfo, JERR_EMPTY_IMAGE); + + + + /* Make sure image isn't bigger than I can handle */ + + if ((long) cinfo->image_height > (long) JPEG_MAX_DIMENSION || + + (long) cinfo->image_width > (long) JPEG_MAX_DIMENSION) + + ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) JPEG_MAX_DIMENSION); + + + + /* Width of an input scanline must be representable as JDIMENSION. */ + + samplesperrow = (long) cinfo->image_width * (long) cinfo->input_components; + + jd_samplesperrow = (JDIMENSION) samplesperrow; + + if ((long) jd_samplesperrow != samplesperrow) + + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + + + + /* For now, precision must match compiled-in value... */ + + if (cinfo->data_precision != BITS_IN_JSAMPLE) + + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + + + + /* Check that number of components won't exceed internal array sizes */ + + if (cinfo->num_components > MAX_COMPONENTS) + + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, + + MAX_COMPONENTS); + + + + /* Compute maximum sampling factors; check factor validity */ + + cinfo->max_h_samp_factor = 1; + + cinfo->max_v_samp_factor = 1; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + if (compptr->h_samp_factor<=0 || compptr->h_samp_factor>MAX_SAMP_FACTOR || + + compptr->v_samp_factor<=0 || compptr->v_samp_factor>MAX_SAMP_FACTOR) + + ERREXIT(cinfo, JERR_BAD_SAMPLING); + + cinfo->max_h_samp_factor = MAX(cinfo->max_h_samp_factor, + + compptr->h_samp_factor); + + cinfo->max_v_samp_factor = MAX(cinfo->max_v_samp_factor, + + compptr->v_samp_factor); + + } + + + + /* Compute dimensions of components */ + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + /* Fill in the correct component_index value; don't rely on application */ + + compptr->component_index = ci; + + /* For compression, we never do DCT scaling. */ + + compptr->DCT_scaled_size = DCTSIZE; + + /* Size in DCT blocks */ + + compptr->width_in_blocks = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, + + (long) (cinfo->max_h_samp_factor * DCTSIZE)); + + compptr->height_in_blocks = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, + + (long) (cinfo->max_v_samp_factor * DCTSIZE)); + + /* Size in samples */ + + compptr->downsampled_width = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, + + (long) cinfo->max_h_samp_factor); + + compptr->downsampled_height = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, + + (long) cinfo->max_v_samp_factor); + + /* Mark component needed (this flag isn't actually used for compression) */ + + compptr->component_needed = TRUE; + + } + + + + /* Compute number of fully interleaved MCU rows (number of times that + + * main controller will call coefficient controller). + + */ + + cinfo->total_iMCU_rows = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_height, + + (long) (cinfo->max_v_samp_factor*DCTSIZE)); + +} + + + + + +#ifdef C_MULTISCAN_FILES_SUPPORTED + + + +LOCAL void + +validate_script (j_compress_ptr cinfo) + +/* Verify that the scan script in cinfo->scan_info[] is valid; also + + * determine whether it uses progressive JPEG, and set cinfo->progressive_mode. + + */ + +{ + + const jpeg_scan_info * scanptr; + + int scanno, ncomps, ci, coefi, thisi; + + int Ss, Se, Ah, Al; + + boolean component_sent[MAX_COMPONENTS]; + +#ifdef C_PROGRESSIVE_SUPPORTED + + int * last_bitpos_ptr; + + int last_bitpos[MAX_COMPONENTS][DCTSIZE2]; + + /* -1 until that coefficient has been seen; then last Al for it */ + +#endif + + + + if (cinfo->num_scans <= 0) + + ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, 0); + + + + /* For sequential JPEG, all scans must have Ss=0, Se=DCTSIZE2-1; + + * for progressive JPEG, no scan can have this. + + */ + + scanptr = cinfo->scan_info; + + if (scanptr->Ss != 0 || scanptr->Se != DCTSIZE2-1) { + +#ifdef C_PROGRESSIVE_SUPPORTED + + cinfo->progressive_mode = TRUE; + + last_bitpos_ptr = & last_bitpos[0][0]; + + for (ci = 0; ci < cinfo->num_components; ci++) + + for (coefi = 0; coefi < DCTSIZE2; coefi++) + + *last_bitpos_ptr++ = -1; + +#else + + ERREXIT(cinfo, JERR_NOT_COMPILED); + +#endif + + } else { + + cinfo->progressive_mode = FALSE; + + for (ci = 0; ci < cinfo->num_components; ci++) + + component_sent[ci] = FALSE; + + } + + + + for (scanno = 1; scanno <= cinfo->num_scans; scanptr++, scanno++) { + + /* Validate component indexes */ + + ncomps = scanptr->comps_in_scan; + + if (ncomps <= 0 || ncomps > MAX_COMPS_IN_SCAN) + + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, ncomps, MAX_COMPS_IN_SCAN); + + for (ci = 0; ci < ncomps; ci++) { + + thisi = scanptr->component_index[ci]; + + if (thisi < 0 || thisi >= cinfo->num_components) + + ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno); + + /* Components must appear in SOF order within each scan */ + + if (ci > 0 && thisi <= scanptr->component_index[ci-1]) + + ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno); + + } + + /* Validate progression parameters */ + + Ss = scanptr->Ss; + + Se = scanptr->Se; + + Ah = scanptr->Ah; + + Al = scanptr->Al; + + if (cinfo->progressive_mode) { + +#ifdef C_PROGRESSIVE_SUPPORTED + + if (Ss < 0 || Ss >= DCTSIZE2 || Se < Ss || Se >= DCTSIZE2 || + + Ah < 0 || Ah > 13 || Al < 0 || Al > 13) + + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + + if (Ss == 0) { + + if (Se != 0) /* DC and AC together not OK */ + + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + + } else { + + if (ncomps != 1) /* AC scans must be for only one component */ + + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + + } + + for (ci = 0; ci < ncomps; ci++) { + + last_bitpos_ptr = & last_bitpos[scanptr->component_index[ci]][0]; + + if (Ss != 0 && last_bitpos_ptr[0] < 0) /* AC without prior DC scan */ + + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + + for (coefi = Ss; coefi <= Se; coefi++) { + + if (last_bitpos_ptr[coefi] < 0) { + + /* first scan of this coefficient */ + + if (Ah != 0) + + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + + } else { + + /* not first scan */ + + if (Ah != last_bitpos_ptr[coefi] || Al != Ah-1) + + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + + } + + last_bitpos_ptr[coefi] = Al; + + } + + } + +#endif + + } else { + + /* For sequential JPEG, all progression parameters must be these: */ + + if (Ss != 0 || Se != DCTSIZE2-1 || Ah != 0 || Al != 0) + + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + + /* Make sure components are not sent twice */ + + for (ci = 0; ci < ncomps; ci++) { + + thisi = scanptr->component_index[ci]; + + if (component_sent[thisi]) + + ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno); + + component_sent[thisi] = TRUE; + + } + + } + + } + + + + /* Now verify that everything got sent. */ + + if (cinfo->progressive_mode) { + +#ifdef C_PROGRESSIVE_SUPPORTED + + /* For progressive mode, we only check that at least some DC data + + * got sent for each component; the spec does not require that all bits + + * of all coefficients be transmitted. Would it be wiser to enforce + + * transmission of all coefficient bits?? + + */ + + for (ci = 0; ci < cinfo->num_components; ci++) { + + if (last_bitpos[ci][0] < 0) + + ERREXIT(cinfo, JERR_MISSING_DATA); + + } + +#endif + + } else { + + for (ci = 0; ci < cinfo->num_components; ci++) { + + if (! component_sent[ci]) + + ERREXIT(cinfo, JERR_MISSING_DATA); + + } + + } + +} + + + +#endif /* C_MULTISCAN_FILES_SUPPORTED */ + + + + + +LOCAL void + +select_scan_parameters (j_compress_ptr cinfo) + +/* Set up the scan parameters for the current scan */ + +{ + + int ci; + + + +#ifdef C_MULTISCAN_FILES_SUPPORTED + + if (cinfo->scan_info != NULL) { + + /* Prepare for current scan --- the script is already validated */ + + my_master_ptr master = (my_master_ptr) cinfo->master; + + const jpeg_scan_info * scanptr = cinfo->scan_info + master->scan_number; + + + + cinfo->comps_in_scan = scanptr->comps_in_scan; + + for (ci = 0; ci < scanptr->comps_in_scan; ci++) { + + cinfo->cur_comp_info[ci] = + + &cinfo->comp_info[scanptr->component_index[ci]]; + + } + + cinfo->Ss = scanptr->Ss; + + cinfo->Se = scanptr->Se; + + cinfo->Ah = scanptr->Ah; + + cinfo->Al = scanptr->Al; + + } + + else + +#endif + + { + + /* Prepare for single sequential-JPEG scan containing all components */ + + if (cinfo->num_components > MAX_COMPS_IN_SCAN) + + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, + + MAX_COMPS_IN_SCAN); + + cinfo->comps_in_scan = cinfo->num_components; + + for (ci = 0; ci < cinfo->num_components; ci++) { + + cinfo->cur_comp_info[ci] = &cinfo->comp_info[ci]; + + } + + cinfo->Ss = 0; + + cinfo->Se = DCTSIZE2-1; + + cinfo->Ah = 0; + + cinfo->Al = 0; + + } + +} + + + + + +LOCAL void + +per_scan_setup (j_compress_ptr cinfo) + +/* Do computations that are needed before processing a JPEG scan */ + +/* cinfo->comps_in_scan and cinfo->cur_comp_info[] are already set */ + +{ + + int ci, mcublks, tmp; + + jpeg_component_info *compptr; + + + + if (cinfo->comps_in_scan == 1) { + + + + /* Noninterleaved (single-component) scan */ + + compptr = cinfo->cur_comp_info[0]; + + + + /* Overall image size in MCUs */ + + cinfo->MCUs_per_row = compptr->width_in_blocks; + + cinfo->MCU_rows_in_scan = compptr->height_in_blocks; + + + + /* For noninterleaved scan, always one block per MCU */ + + compptr->MCU_width = 1; + + compptr->MCU_height = 1; + + compptr->MCU_blocks = 1; + + compptr->MCU_sample_width = DCTSIZE; + + compptr->last_col_width = 1; + + /* For noninterleaved scans, it is convenient to define last_row_height + + * as the number of block rows present in the last iMCU row. + + */ + + tmp = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + + if (tmp == 0) tmp = compptr->v_samp_factor; + + compptr->last_row_height = tmp; + + + + /* Prepare array describing MCU composition */ + + cinfo->blocks_in_MCU = 1; + + cinfo->MCU_membership[0] = 0; + + + + } else { + + + + /* Interleaved (multi-component) scan */ + + if (cinfo->comps_in_scan <= 0 || cinfo->comps_in_scan > MAX_COMPS_IN_SCAN) + + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->comps_in_scan, + + MAX_COMPS_IN_SCAN); + + + + /* Overall image size in MCUs */ + + cinfo->MCUs_per_row = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_width, + + (long) (cinfo->max_h_samp_factor*DCTSIZE)); + + cinfo->MCU_rows_in_scan = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_height, + + (long) (cinfo->max_v_samp_factor*DCTSIZE)); + + + + cinfo->blocks_in_MCU = 0; + + + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + + compptr = cinfo->cur_comp_info[ci]; + + /* Sampling factors give # of blocks of component in each MCU */ + + compptr->MCU_width = compptr->h_samp_factor; + + compptr->MCU_height = compptr->v_samp_factor; + + compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height; + + compptr->MCU_sample_width = compptr->MCU_width * DCTSIZE; + + /* Figure number of non-dummy blocks in last MCU column & row */ + + tmp = (int) (compptr->width_in_blocks % compptr->MCU_width); + + if (tmp == 0) tmp = compptr->MCU_width; + + compptr->last_col_width = tmp; + + tmp = (int) (compptr->height_in_blocks % compptr->MCU_height); + + if (tmp == 0) tmp = compptr->MCU_height; + + compptr->last_row_height = tmp; + + /* Prepare array describing MCU composition */ + + mcublks = compptr->MCU_blocks; + + if (cinfo->blocks_in_MCU + mcublks > C_MAX_BLOCKS_IN_MCU) + + ERREXIT(cinfo, JERR_BAD_MCU_SIZE); + + while (mcublks-- > 0) { + + cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci; + + } + + } + + + + } + + + + /* Convert restart specified in rows to actual MCU count. */ + + /* Note that count must fit in 16 bits, so we provide limiting. */ + + if (cinfo->restart_in_rows > 0) { + + long nominal = (long) cinfo->restart_in_rows * (long) cinfo->MCUs_per_row; + + cinfo->restart_interval = (unsigned int) MIN(nominal, 65535L); + + } + +} + + + + + +/* + + * Per-pass setup. + + * This is called at the beginning of each pass. We determine which modules + + * will be active during this pass and give them appropriate start_pass calls. + + * We also set is_last_pass to indicate whether any more passes will be + + * required. + + */ + + + +METHODDEF void + +prepare_for_pass (j_compress_ptr cinfo) + +{ + + my_master_ptr master = (my_master_ptr) cinfo->master; + + + + switch (master->pass_type) { + + case main_pass: + + /* Initial pass: will collect input data, and do either Huffman + + * optimization or data output for the first scan. + + */ + + select_scan_parameters(cinfo); + + per_scan_setup(cinfo); + + if (! cinfo->raw_data_in) { + + (*cinfo->cconvert->start_pass) (cinfo); + + (*cinfo->downsample->start_pass) (cinfo); + + (*cinfo->prep->start_pass) (cinfo, JBUF_PASS_THRU); + + } + + (*cinfo->fdct->start_pass) (cinfo); + + (*cinfo->entropy->start_pass) (cinfo, cinfo->optimize_coding); + + (*cinfo->coef->start_pass) (cinfo, + + (master->total_passes > 1 ? + + JBUF_SAVE_AND_PASS : JBUF_PASS_THRU)); + + (*cinfo->main->start_pass) (cinfo, JBUF_PASS_THRU); + + if (cinfo->optimize_coding) { + + /* No immediate data output; postpone writing frame/scan headers */ + + master->pub.call_pass_startup = FALSE; + + } else { + + /* Will write frame/scan headers at first jpeg_write_scanlines call */ + + master->pub.call_pass_startup = TRUE; + + } + + break; + +#ifdef ENTROPY_OPT_SUPPORTED + + case huff_opt_pass: + + /* Do Huffman optimization for a scan after the first one. */ + + select_scan_parameters(cinfo); + + per_scan_setup(cinfo); + + if (cinfo->Ss != 0 || cinfo->Ah == 0 || cinfo->arith_code) { + + (*cinfo->entropy->start_pass) (cinfo, TRUE); + + (*cinfo->coef->start_pass) (cinfo, JBUF_CRANK_DEST); + + master->pub.call_pass_startup = FALSE; + + break; + + } + + /* Special case: Huffman DC refinement scans need no Huffman table + + * and therefore we can skip the optimization pass for them. + + */ + + master->pass_type = output_pass; + + master->pass_number++; + + /*FALLTHROUGH*/ + +#endif + + case output_pass: + + /* Do a data-output pass. */ + + /* We need not repeat per-scan setup if prior optimization pass did it. */ + + if (! cinfo->optimize_coding) { + + select_scan_parameters(cinfo); + + per_scan_setup(cinfo); + + } + + (*cinfo->entropy->start_pass) (cinfo, FALSE); + + (*cinfo->coef->start_pass) (cinfo, JBUF_CRANK_DEST); + + /* We emit frame/scan headers now */ + + if (master->scan_number == 0) + + (*cinfo->marker->write_frame_header) (cinfo); + + (*cinfo->marker->write_scan_header) (cinfo); + + master->pub.call_pass_startup = FALSE; + + break; + + default: + + ERREXIT(cinfo, JERR_NOT_COMPILED); + + } + + + + master->pub.is_last_pass = (master->pass_number == master->total_passes-1); + + + + /* Set up progress monitor's pass info if present */ + + if (cinfo->progress != NULL) { + + cinfo->progress->completed_passes = master->pass_number; + + cinfo->progress->total_passes = master->total_passes; + + } + +} + + + + + +/* + + * Special start-of-pass hook. + + * This is called by jpeg_write_scanlines if call_pass_startup is TRUE. + + * In single-pass processing, we need this hook because we don't want to + + * write frame/scan headers during jpeg_start_compress; we want to let the + + * application write COM markers etc. between jpeg_start_compress and the + + * jpeg_write_scanlines loop. + + * In multi-pass processing, this routine is not used. + + */ + + + +METHODDEF void + +pass_startup (j_compress_ptr cinfo) + +{ + + cinfo->master->call_pass_startup = FALSE; /* reset flag so call only once */ + + + + (*cinfo->marker->write_frame_header) (cinfo); + + (*cinfo->marker->write_scan_header) (cinfo); + +} + + + + + +/* + + * Finish up at end of pass. + + */ + + + +METHODDEF void + +finish_pass_master (j_compress_ptr cinfo) + +{ + + my_master_ptr master = (my_master_ptr) cinfo->master; + + + + /* The entropy coder always needs an end-of-pass call, + + * either to analyze statistics or to flush its output buffer. + + */ + + (*cinfo->entropy->finish_pass) (cinfo); + + + + /* Update state for next pass */ + + switch (master->pass_type) { + + case main_pass: + + /* next pass is either output of scan 0 (after optimization) + + * or output of scan 1 (if no optimization). + + */ + + master->pass_type = output_pass; + + if (! cinfo->optimize_coding) + + master->scan_number++; + + break; + + case huff_opt_pass: + + /* next pass is always output of current scan */ + + master->pass_type = output_pass; + + break; + + case output_pass: + + /* next pass is either optimization or output of next scan */ + + if (cinfo->optimize_coding) + + master->pass_type = huff_opt_pass; + + master->scan_number++; + + break; + + } + + + + master->pass_number++; + +} + + + + + +/* + + * Initialize master compression control. + + */ + + + +GLOBAL void + +jinit_c_master_control (j_compress_ptr cinfo, boolean transcode_only) + +{ + + my_master_ptr master; + + + + master = (my_master_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(my_comp_master)); + + cinfo->master = (struct jpeg_comp_master *) master; + + master->pub.prepare_for_pass = prepare_for_pass; + + master->pub.pass_startup = pass_startup; + + master->pub.finish_pass = finish_pass_master; + + master->pub.is_last_pass = FALSE; + + + + /* Validate parameters, determine derived values */ + + initial_setup(cinfo); + + + + if (cinfo->scan_info != NULL) { + +#ifdef C_MULTISCAN_FILES_SUPPORTED + + validate_script(cinfo); + +#else + + ERREXIT(cinfo, JERR_NOT_COMPILED); + +#endif + + } else { + + cinfo->progressive_mode = FALSE; + + cinfo->num_scans = 1; + + } + + + + if (cinfo->progressive_mode) /* TEMPORARY HACK ??? */ + + cinfo->optimize_coding = TRUE; /* assume default tables no good for progressive mode */ + + + + /* Initialize my private state */ + + if (transcode_only) { + + /* no main pass in transcoding */ + + if (cinfo->optimize_coding) + + master->pass_type = huff_opt_pass; + + else + + master->pass_type = output_pass; + + } else { + + /* for normal compression, first pass is always this type: */ + + master->pass_type = main_pass; + + } + + master->scan_number = 0; + + master->pass_number = 0; + + if (cinfo->optimize_coding) + + master->total_passes = cinfo->num_scans * 2; + + else + + master->total_passes = cinfo->num_scans; + +} + diff --git a/utils/roq2/jpeg/jcomapi.c b/utils/roq2/jpeg/jcomapi.c new file mode 100644 index 0000000..b350c7e --- /dev/null +++ b/utils/roq2/jpeg/jcomapi.c @@ -0,0 +1,188 @@ +/* + + * jcomapi.c + + * + + * Copyright (C) 1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains application interface routines that are used for both + + * compression and decompression. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + + + +/* + + * Abort processing of a JPEG compression or decompression operation, + + * but don't destroy the object itself. + + * + + * For this, we merely clean up all the nonpermanent memory pools. + + * Note that temp files (virtual arrays) are not allowed to belong to + + * the permanent pool, so we will be able to close all temp files here. + + * Closing a data source or destination, if necessary, is the application's + + * responsibility. + + */ + + + +GLOBAL void + +jpeg_abort (j_common_ptr cinfo) + +{ + + int pool; + + + + /* Releasing pools in reverse order might help avoid fragmentation + + * with some (brain-damaged) malloc libraries. + + */ + + for (pool = JPOOL_NUMPOOLS-1; pool > JPOOL_PERMANENT; pool--) { + + (*cinfo->mem->free_pool) (cinfo, pool); + + } + + + + /* Reset overall state for possible reuse of object */ + + cinfo->global_state = (cinfo->is_decompressor ? DSTATE_START : CSTATE_START); + +} + + + + + +/* + + * Destruction of a JPEG object. + + * + + * Everything gets deallocated except the master jpeg_compress_struct itself + + * and the error manager struct. Both of these are supplied by the application + + * and must be freed, if necessary, by the application. (Often they are on + + * the stack and so don't need to be freed anyway.) + + * Closing a data source or destination, if necessary, is the application's + + * responsibility. + + */ + + + +GLOBAL void + +jpeg_destroy (j_common_ptr cinfo) + +{ + + /* We need only tell the memory manager to release everything. */ + + /* NB: mem pointer is NULL if memory mgr failed to initialize. */ + + if (cinfo->mem != NULL) + + (*cinfo->mem->self_destruct) (cinfo); + + cinfo->mem = NULL; /* be safe if jpeg_destroy is called twice */ + + cinfo->global_state = 0; /* mark it destroyed */ + +} + + + + + +/* + + * Convenience routines for allocating quantization and Huffman tables. + + * (Would jutils.c be a more reasonable place to put these?) + + */ + + + +GLOBAL JQUANT_TBL * + +jpeg_alloc_quant_table (j_common_ptr cinfo) + +{ + + JQUANT_TBL *tbl; + + + + tbl = (JQUANT_TBL *) + + (*cinfo->mem->alloc_small) (cinfo, JPOOL_PERMANENT, SIZEOF(JQUANT_TBL)); + + tbl->sent_table = FALSE; /* make sure this is false in any new table */ + + return tbl; + +} + + + + + +GLOBAL JHUFF_TBL * + +jpeg_alloc_huff_table (j_common_ptr cinfo) + +{ + + JHUFF_TBL *tbl; + + + + tbl = (JHUFF_TBL *) + + (*cinfo->mem->alloc_small) (cinfo, JPOOL_PERMANENT, SIZEOF(JHUFF_TBL)); + + tbl->sent_table = FALSE; /* make sure this is false in any new table */ + + return tbl; + +} + diff --git a/utils/roq2/jpeg/jconfig.h b/utils/roq2/jpeg/jconfig.h new file mode 100644 index 0000000..3ae5e82 --- /dev/null +++ b/utils/roq2/jpeg/jconfig.h @@ -0,0 +1,90 @@ +/* jconfig.h. Generated automatically by configure. */ + +/* jconfig.cfg --- source file edited by configure script */ + +/* see jconfig.doc for explanations */ + + + +#define HAVE_PROTOTYPES + +#define HAVE_UNSIGNED_CHAR + +#define HAVE_UNSIGNED_SHORT + +#undef void + +#undef const + +#undef CHAR_IS_UNSIGNED + +#define HAVE_STDDEF_H + +#define HAVE_STDLIB_H + +#undef NEED_BSD_STRINGS + +#undef NEED_SYS_TYPES_H + +#undef NEED_FAR_POINTERS + +#undef NEED_SHORT_EXTERNAL_NAMES + +/* Define this if you get warnings about undefined structures. */ + +#undef INCOMPLETE_TYPES_BROKEN + + + +#ifdef JPEG_INTERNALS + + + +#undef RIGHT_SHIFT_IS_UNSIGNED + +#define INLINE inline + +/* These are for configuring the JPEG memory manager. */ + +#undef DEFAULT_MAX_MEM + +#undef NO_MKTEMP + + + +#endif /* JPEG_INTERNALS */ + + + +#ifdef JPEG_CJPEG_DJPEG + + + +#define BMP_SUPPORTED /* BMP image file format */ + +#define GIF_SUPPORTED /* GIF image file format */ + +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ + +#undef RLE_SUPPORTED /* Utah RLE image file format */ + +#define TARGA_SUPPORTED /* Targa image file format */ + + + +#undef TWO_FILE_COMMANDLINE + +#undef NEED_SIGNAL_CATCHER + +#undef DONT_USE_B_MODE + + + +/* Define this if you want percent-done progress reports from cjpeg/djpeg. */ + +#undef PROGRESS_REPORT + + + +#endif /* JPEG_CJPEG_DJPEG */ + diff --git a/utils/roq2/jpeg/jcparam.c b/utils/roq2/jpeg/jcparam.c new file mode 100644 index 0000000..3e7e71a --- /dev/null +++ b/utils/roq2/jpeg/jcparam.c @@ -0,0 +1,1150 @@ +/* + + * jcparam.c + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains optional default-setting code for the JPEG compressor. + + * Applications do not have to use this file, but those that don't use it + + * must know a lot more about the innards of the JPEG code. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + + + +/* + + * Quantization table setup routines + + */ + + + +GLOBAL void + +jpeg_add_quant_table (j_compress_ptr cinfo, int which_tbl, + + const unsigned int *basic_table, + + int scale_factor, boolean force_baseline) + +/* Define a quantization table equal to the basic_table times + + * a scale factor (given as a percentage). + + * If force_baseline is TRUE, the computed quantization table entries + + * are limited to 1..255 for JPEG baseline compatibility. + + */ + +{ + + JQUANT_TBL ** qtblptr = & cinfo->quant_tbl_ptrs[which_tbl]; + + int i; + + long temp; + + + + /* Safety check to ensure start_compress not called yet. */ + + if (cinfo->global_state != CSTATE_START) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + + + if (*qtblptr == NULL) + + *qtblptr = jpeg_alloc_quant_table((j_common_ptr) cinfo); + + + + for (i = 0; i < DCTSIZE2; i++) { + + temp = ((long) basic_table[i] * scale_factor + 50L) / 100L; + + /* limit the values to the valid range */ + + if (temp <= 0L) temp = 1L; + + if (temp > 32767L) temp = 32767L; /* max quantizer needed for 12 bits */ + + if (force_baseline && temp > 255L) + + temp = 255L; /* limit to baseline range if requested */ + + (*qtblptr)->quantval[i] = (UINT16) temp; + + } + + + + /* Initialize sent_table FALSE so table will be written to JPEG file. */ + + (*qtblptr)->sent_table = FALSE; + +} + + + + + +GLOBAL void + +jpeg_set_linear_quality (j_compress_ptr cinfo, int scale_factor, + + boolean force_baseline) + +/* Set or change the 'quality' (quantization) setting, using default tables + + * and a straight percentage-scaling quality scale. In most cases it's better + + * to use jpeg_set_quality (below); this entry point is provided for + + * applications that insist on a linear percentage scaling. + + */ + +{ + + /* This is the sample quantization table given in the JPEG spec section K.1, + + * but expressed in zigzag order (as are all of our quant. tables). + + * The spec says that the values given produce "good" quality, and + + * when divided by 2, "very good" quality. + + */ + + static const unsigned int std_luminance_quant_tbl[DCTSIZE2] = { + + 16, 11, 12, 14, 12, 10, 16, 14, + + 13, 14, 18, 17, 16, 19, 24, 40, + + 26, 24, 22, 22, 24, 49, 35, 37, + + 29, 40, 58, 51, 61, 60, 57, 51, + + 56, 55, 64, 72, 92, 78, 64, 68, + + 87, 69, 55, 56, 80, 109, 81, 87, + + 95, 98, 103, 104, 103, 62, 77, 113, + + 121, 112, 100, 120, 92, 101, 103, 99 + + }; + + static const unsigned int std_chrominance_quant_tbl[DCTSIZE2] = { + + 17, 18, 18, 24, 21, 24, 47, 26, + + 26, 47, 99, 66, 56, 66, 99, 99, + + 99, 99, 99, 99, 99, 99, 99, 99, + + 99, 99, 99, 99, 99, 99, 99, 99, + + 99, 99, 99, 99, 99, 99, 99, 99, + + 99, 99, 99, 99, 99, 99, 99, 99, + + 99, 99, 99, 99, 99, 99, 99, 99, + + 99, 99, 99, 99, 99, 99, 99, 99 + + }; + + + + /* Set up two quantization tables using the specified scaling */ + + jpeg_add_quant_table(cinfo, 0, std_luminance_quant_tbl, + + scale_factor, force_baseline); + + jpeg_add_quant_table(cinfo, 1, std_chrominance_quant_tbl, + + scale_factor, force_baseline); + +} + + + + + +GLOBAL int + +jpeg_quality_scaling (int quality) + +/* Convert a user-specified quality rating to a percentage scaling factor + + * for an underlying quantization table, using our recommended scaling curve. + + * The input 'quality' factor should be 0 (terrible) to 100 (very good). + + */ + +{ + + /* Safety limit on quality factor. Convert 0 to 1 to avoid zero divide. */ + + if (quality <= 0) quality = 1; + + if (quality > 100) quality = 100; + + + + /* The basic table is used as-is (scaling 100) for a quality of 50. + + * Qualities 50..100 are converted to scaling percentage 200 - 2*Q; + + * note that at Q=100 the scaling is 0, which will cause j_add_quant_table + + * to make all the table entries 1 (hence, no quantization loss). + + * Qualities 1..50 are converted to scaling percentage 5000/Q. + + */ + + if (quality < 50) + + quality = 5000 / quality; + + else + + quality = 200 - quality*2; + + + + return quality; + +} + + + + + +GLOBAL void + +jpeg_set_quality (j_compress_ptr cinfo, int quality, boolean force_baseline) + +/* Set or change the 'quality' (quantization) setting, using default tables. + + * This is the standard quality-adjusting entry point for typical user + + * interfaces; only those who want detailed control over quantization tables + + * would use the preceding three routines directly. + + */ + +{ + + /* Convert user 0-100 rating to percentage scaling */ + + quality = jpeg_quality_scaling(quality); + + + + /* Set up standard quality tables */ + + jpeg_set_linear_quality(cinfo, quality, force_baseline); + +} + + + + + +/* + + * Huffman table setup routines + + */ + + + +LOCAL void + +add_huff_table (j_compress_ptr cinfo, + + JHUFF_TBL **htblptr, const UINT8 *bits, const UINT8 *val) + +/* Define a Huffman table */ + +{ + + if (*htblptr == NULL) + + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + + + + MEMCOPY((*htblptr)->bits, bits, SIZEOF((*htblptr)->bits)); + + MEMCOPY((*htblptr)->huffval, val, SIZEOF((*htblptr)->huffval)); + + + + /* Initialize sent_table FALSE so table will be written to JPEG file. */ + + (*htblptr)->sent_table = FALSE; + +} + + + + + +LOCAL void + +std_huff_tables (j_compress_ptr cinfo) + +/* Set up the standard Huffman tables (cf. JPEG standard section K.3) */ + +/* IMPORTANT: these are only valid for 8-bit data precision! */ + +{ + + static const UINT8 bits_dc_luminance[17] = + + { /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }; + + static const UINT8 val_dc_luminance[] = + + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; + + + + static const UINT8 bits_dc_chrominance[17] = + + { /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }; + + static const UINT8 val_dc_chrominance[] = + + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; + + + + static const UINT8 bits_ac_luminance[17] = + + { /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d }; + + static const UINT8 val_ac_luminance[] = + + { 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + + 0xf9, 0xfa }; + + + + static const UINT8 bits_ac_chrominance[17] = + + { /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 }; + + static const UINT8 val_ac_chrominance[] = + + { 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, + + 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + + 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, + + 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, + + 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, + + 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, + + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + + 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + + 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + + 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, + + 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, + + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, + + 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + + 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + + 0xf9, 0xfa }; + + + + add_huff_table(cinfo, &cinfo->dc_huff_tbl_ptrs[0], + + bits_dc_luminance, val_dc_luminance); + + add_huff_table(cinfo, &cinfo->ac_huff_tbl_ptrs[0], + + bits_ac_luminance, val_ac_luminance); + + add_huff_table(cinfo, &cinfo->dc_huff_tbl_ptrs[1], + + bits_dc_chrominance, val_dc_chrominance); + + add_huff_table(cinfo, &cinfo->ac_huff_tbl_ptrs[1], + + bits_ac_chrominance, val_ac_chrominance); + +} + + + + + +/* + + * Default parameter setup for compression. + + * + + * Applications that don't choose to use this routine must do their + + * own setup of all these parameters. Alternately, you can call this + + * to establish defaults and then alter parameters selectively. This + + * is the recommended approach since, if we add any new parameters, + + * your code will still work (they'll be set to reasonable defaults). + + */ + + + +GLOBAL void + +jpeg_set_defaults (j_compress_ptr cinfo) + +{ + + int i; + + + + /* Safety check to ensure start_compress not called yet. */ + + if (cinfo->global_state != CSTATE_START) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + + + /* Allocate comp_info array large enough for maximum component count. + + * Array is made permanent in case application wants to compress + + * multiple images at same param settings. + + */ + + if (cinfo->comp_info == NULL) + + cinfo->comp_info = (jpeg_component_info *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + + MAX_COMPONENTS * SIZEOF(jpeg_component_info)); + + + + /* Initialize everything not dependent on the color space */ + + + + cinfo->data_precision = BITS_IN_JSAMPLE; + + /* Set up two quantization tables using default quality of 75 */ + + jpeg_set_quality(cinfo, 75, TRUE); + + /* Set up two Huffman tables */ + + std_huff_tables(cinfo); + + + + /* Initialize default arithmetic coding conditioning */ + + for (i = 0; i < NUM_ARITH_TBLS; i++) { + + cinfo->arith_dc_L[i] = 0; + + cinfo->arith_dc_U[i] = 1; + + cinfo->arith_ac_K[i] = 5; + + } + + + + /* Default is no multiple-scan output */ + + cinfo->scan_info = NULL; + + cinfo->num_scans = 0; + + + + /* Expect normal source image, not raw downsampled data */ + + cinfo->raw_data_in = FALSE; + + + + /* Use Huffman coding, not arithmetic coding, by default */ + + cinfo->arith_code = FALSE; + + + + /* By default, don't do extra passes to optimize entropy coding */ + + cinfo->optimize_coding = FALSE; + + /* The standard Huffman tables are only valid for 8-bit data precision. + + * If the precision is higher, force optimization on so that usable + + * tables will be computed. This test can be removed if default tables + + * are supplied that are valid for the desired precision. + + */ + + if (cinfo->data_precision > 8) + + cinfo->optimize_coding = TRUE; + + + + /* By default, use the simpler non-cosited sampling alignment */ + + cinfo->CCIR601_sampling = FALSE; + + + + /* No input smoothing */ + + cinfo->smoothing_factor = 0; + + + + /* DCT algorithm preference */ + + cinfo->dct_method = JDCT_DEFAULT; + + + + /* No restart markers */ + + cinfo->restart_interval = 0; + + cinfo->restart_in_rows = 0; + + + + /* Fill in default JFIF marker parameters. Note that whether the marker + + * will actually be written is determined by jpeg_set_colorspace. + + */ + + cinfo->density_unit = 0; /* Pixel size is unknown by default */ + + cinfo->X_density = 1; /* Pixel aspect ratio is square by default */ + + cinfo->Y_density = 1; + + + + /* Choose JPEG colorspace based on input space, set defaults accordingly */ + + + + jpeg_default_colorspace(cinfo); + +} + + + + + +/* + + * Select an appropriate JPEG colorspace for in_color_space. + + */ + + + +GLOBAL void + +jpeg_default_colorspace (j_compress_ptr cinfo) + +{ + + switch (cinfo->in_color_space) { + + case JCS_GRAYSCALE: + + jpeg_set_colorspace(cinfo, JCS_GRAYSCALE); + + break; + + case JCS_RGB: + + jpeg_set_colorspace(cinfo, JCS_YCbCr); + + break; + + case JCS_YCbCr: + + jpeg_set_colorspace(cinfo, JCS_YCbCr); + + break; + + case JCS_CMYK: + + jpeg_set_colorspace(cinfo, JCS_CMYK); /* By default, no translation */ + + break; + + case JCS_YCCK: + + jpeg_set_colorspace(cinfo, JCS_YCCK); + + break; + + case JCS_UNKNOWN: + + jpeg_set_colorspace(cinfo, JCS_UNKNOWN); + + break; + + default: + + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + + } + +} + + + + + +/* + + * Set the JPEG colorspace, and choose colorspace-dependent default values. + + */ + + + +GLOBAL void + +jpeg_set_colorspace (j_compress_ptr cinfo, J_COLOR_SPACE colorspace) + +{ + + jpeg_component_info * compptr; + + int ci; + + + +#define SET_COMP(index,id,hsamp,vsamp,quant,dctbl,actbl) \ + + (compptr = &cinfo->comp_info[index], \ + + compptr->component_id = (id), \ + + compptr->h_samp_factor = (hsamp), \ + + compptr->v_samp_factor = (vsamp), \ + + compptr->quant_tbl_no = (quant), \ + + compptr->dc_tbl_no = (dctbl), \ + + compptr->ac_tbl_no = (actbl) ) + + + + /* Safety check to ensure start_compress not called yet. */ + + if (cinfo->global_state != CSTATE_START) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + + + /* For all colorspaces, we use Q and Huff tables 0 for luminance components, + + * tables 1 for chrominance components. + + */ + + + + cinfo->jpeg_color_space = colorspace; + + + + cinfo->write_JFIF_header = FALSE; /* No marker for non-JFIF colorspaces */ + + cinfo->write_Adobe_marker = FALSE; /* write no Adobe marker by default */ + + + + switch (colorspace) { + + case JCS_GRAYSCALE: + + cinfo->write_JFIF_header = TRUE; /* Write a JFIF marker */ + + cinfo->num_components = 1; + + /* JFIF specifies component ID 1 */ + + SET_COMP(0, 1, 1,1, 0, 0,0); + + break; + + case JCS_RGB: + + cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag RGB */ + + cinfo->num_components = 3; + + SET_COMP(0, 0x52 /* 'R' */, 1,1, 0, 0,0); + + SET_COMP(1, 0x47 /* 'G' */, 1,1, 0, 0,0); + + SET_COMP(2, 0x42 /* 'B' */, 1,1, 0, 0,0); + + break; + + case JCS_YCbCr: + + cinfo->write_JFIF_header = TRUE; /* Write a JFIF marker */ + + cinfo->num_components = 3; + + /* JFIF specifies component IDs 1,2,3 */ + + /* We default to 2x2 subsamples of chrominance */ + + SET_COMP(0, 1, 2,2, 0, 0,0); + + SET_COMP(1, 2, 1,1, 1, 1,1); + + SET_COMP(2, 3, 1,1, 1, 1,1); + + break; + + case JCS_CMYK: + + cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag CMYK */ + + cinfo->num_components = 4; + + SET_COMP(0, 0x43 /* 'C' */, 1,1, 0, 0,0); + + SET_COMP(1, 0x4D /* 'M' */, 1,1, 0, 0,0); + + SET_COMP(2, 0x59 /* 'Y' */, 1,1, 0, 0,0); + + SET_COMP(3, 0x4B /* 'K' */, 1,1, 0, 0,0); + + break; + + case JCS_YCCK: + + cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag YCCK */ + + cinfo->num_components = 4; + + SET_COMP(0, 1, 2,2, 0, 0,0); + + SET_COMP(1, 2, 1,1, 1, 1,1); + + SET_COMP(2, 3, 1,1, 1, 1,1); + + SET_COMP(3, 4, 2,2, 0, 0,0); + + break; + + case JCS_UNKNOWN: + + cinfo->num_components = cinfo->input_components; + + if (cinfo->num_components < 1 || cinfo->num_components > MAX_COMPONENTS) + + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, + + MAX_COMPONENTS); + + for (ci = 0; ci < cinfo->num_components; ci++) { + + SET_COMP(ci, ci, 1,1, 0, 0,0); + + } + + break; + + default: + + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + + } + +} + + + + + +#ifdef C_PROGRESSIVE_SUPPORTED + + + +LOCAL jpeg_scan_info * + +fill_a_scan (jpeg_scan_info * scanptr, int ci, + + int Ss, int Se, int Ah, int Al) + +/* Support routine: generate one scan for specified component */ + +{ + + scanptr->comps_in_scan = 1; + + scanptr->component_index[0] = ci; + + scanptr->Ss = Ss; + + scanptr->Se = Se; + + scanptr->Ah = Ah; + + scanptr->Al = Al; + + scanptr++; + + return scanptr; + +} + + + +LOCAL jpeg_scan_info * + +fill_scans (jpeg_scan_info * scanptr, int ncomps, + + int Ss, int Se, int Ah, int Al) + +/* Support routine: generate one scan for each component */ + +{ + + int ci; + + + + for (ci = 0; ci < ncomps; ci++) { + + scanptr->comps_in_scan = 1; + + scanptr->component_index[0] = ci; + + scanptr->Ss = Ss; + + scanptr->Se = Se; + + scanptr->Ah = Ah; + + scanptr->Al = Al; + + scanptr++; + + } + + return scanptr; + +} + + + +LOCAL jpeg_scan_info * + +fill_dc_scans (jpeg_scan_info * scanptr, int ncomps, int Ah, int Al) + +/* Support routine: generate interleaved DC scan if possible, else N scans */ + +{ + + int ci; + + + + if (ncomps <= MAX_COMPS_IN_SCAN) { + + /* Single interleaved DC scan */ + + scanptr->comps_in_scan = ncomps; + + for (ci = 0; ci < ncomps; ci++) + + scanptr->component_index[ci] = ci; + + scanptr->Ss = scanptr->Se = 0; + + scanptr->Ah = Ah; + + scanptr->Al = Al; + + scanptr++; + + } else { + + /* Noninterleaved DC scan for each component */ + + scanptr = fill_scans(scanptr, ncomps, 0, 0, Ah, Al); + + } + + return scanptr; + +} + + + + + +/* + + * Create a recommended progressive-JPEG script. + + * cinfo->num_components and cinfo->jpeg_color_space must be correct. + + */ + + + +GLOBAL void + +jpeg_simple_progression (j_compress_ptr cinfo) + +{ + + int ncomps = cinfo->num_components; + + int nscans; + + jpeg_scan_info * scanptr; + + + + /* Safety check to ensure start_compress not called yet. */ + + if (cinfo->global_state != CSTATE_START) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + + + /* Figure space needed for script. Calculation must match code below! */ + + if (ncomps == 3 && cinfo->jpeg_color_space == JCS_YCbCr) { + + /* Custom script for YCbCr color images. */ + + nscans = 10; + + } else { + + /* All-purpose script for other color spaces. */ + + if (ncomps > MAX_COMPS_IN_SCAN) + + nscans = 6 * ncomps; /* 2 DC + 4 AC scans per component */ + + else + + nscans = 2 + 4 * ncomps; /* 2 DC scans; 4 AC scans per component */ + + } + + + + /* Allocate space for script. */ + + /* We use permanent pool just in case application re-uses script. */ + + scanptr = (jpeg_scan_info *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + + nscans * SIZEOF(jpeg_scan_info)); + + cinfo->scan_info = scanptr; + + cinfo->num_scans = nscans; + + + + if (ncomps == 3 && cinfo->jpeg_color_space == JCS_YCbCr) { + + /* Custom script for YCbCr color images. */ + + /* Initial DC scan */ + + scanptr = fill_dc_scans(scanptr, ncomps, 0, 1); + + /* Initial AC scan: get some luma data out in a hurry */ + + scanptr = fill_a_scan(scanptr, 0, 1, 5, 0, 2); + + /* Chroma data is too small to be worth expending many scans on */ + + scanptr = fill_a_scan(scanptr, 2, 1, 63, 0, 1); + + scanptr = fill_a_scan(scanptr, 1, 1, 63, 0, 1); + + /* Complete spectral selection for luma AC */ + + scanptr = fill_a_scan(scanptr, 0, 6, 63, 0, 2); + + /* Refine next bit of luma AC */ + + scanptr = fill_a_scan(scanptr, 0, 1, 63, 2, 1); + + /* Finish DC successive approximation */ + + scanptr = fill_dc_scans(scanptr, ncomps, 1, 0); + + /* Finish AC successive approximation */ + + scanptr = fill_a_scan(scanptr, 2, 1, 63, 1, 0); + + scanptr = fill_a_scan(scanptr, 1, 1, 63, 1, 0); + + /* Luma bottom bit comes last since it's usually largest scan */ + + scanptr = fill_a_scan(scanptr, 0, 1, 63, 1, 0); + + } else { + + /* All-purpose script for other color spaces. */ + + /* Successive approximation first pass */ + + scanptr = fill_dc_scans(scanptr, ncomps, 0, 1); + + scanptr = fill_scans(scanptr, ncomps, 1, 5, 0, 2); + + scanptr = fill_scans(scanptr, ncomps, 6, 63, 0, 2); + + /* Successive approximation second pass */ + + scanptr = fill_scans(scanptr, ncomps, 1, 63, 2, 1); + + /* Successive approximation final pass */ + + scanptr = fill_dc_scans(scanptr, ncomps, 1, 0); + + scanptr = fill_scans(scanptr, ncomps, 1, 63, 1, 0); + + } + +} + + + +#endif /* C_PROGRESSIVE_SUPPORTED */ + diff --git a/utils/roq2/jpeg/jcphuff.c b/utils/roq2/jpeg/jcphuff.c new file mode 100644 index 0000000..1a3cce1 --- /dev/null +++ b/utils/roq2/jpeg/jcphuff.c @@ -0,0 +1,1666 @@ +/* + + * jcphuff.c + + * + + * Copyright (C) 1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains Huffman entropy encoding routines for progressive JPEG. + + * + + * We do not support output suspension in this module, since the library + + * currently does not allow multiple-scan files to be written with output + + * suspension. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + +#include "jchuff.h" /* Declarations shared with jchuff.c */ + + + +#ifdef C_PROGRESSIVE_SUPPORTED + + + +/* Expanded entropy encoder object for progressive Huffman encoding. */ + + + +typedef struct { + + struct jpeg_entropy_encoder pub; /* public fields */ + + + + /* Mode flag: TRUE for optimization, FALSE for actual data output */ + + boolean gather_statistics; + + + + /* Bit-level coding status. + + * next_output_byte/free_in_buffer are local copies of cinfo->dest fields. + + */ + + JOCTET * next_output_byte; /* => next byte to write in buffer */ + + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + + INT32 put_buffer; /* current bit-accumulation buffer */ + + int put_bits; /* # of bits now in it */ + + j_compress_ptr cinfo; /* link to cinfo (needed for dump_buffer) */ + + + + /* Coding status for DC components */ + + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ + + + + /* Coding status for AC components */ + + int ac_tbl_no; /* the table number of the single component */ + + unsigned int EOBRUN; /* run length of EOBs */ + + unsigned int BE; /* # of buffered correction bits before MCU */ + + char * bit_buffer; /* buffer for correction bits (1 per char) */ + + /* packing correction bits tightly would save some space but cost time... */ + + + + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + + int next_restart_num; /* next restart number to write (0-7) */ + + + + /* Pointers to derived tables (these workspaces have image lifespan). + + * Since any one scan codes only DC or only AC, we only need one set + + * of tables, not one for DC and one for AC. + + */ + + c_derived_tbl * derived_tbls[NUM_HUFF_TBLS]; + + + + /* Statistics tables for optimization; again, one set is enough */ + + long * count_ptrs[NUM_HUFF_TBLS]; + +} phuff_entropy_encoder; + + + +typedef phuff_entropy_encoder * phuff_entropy_ptr; + + + +/* MAX_CORR_BITS is the number of bits the AC refinement correction-bit + + * buffer can hold. Larger sizes may slightly improve compression, but + + * 1000 is already well into the realm of overkill. + + * The minimum safe size is 64 bits. + + */ + + + +#define MAX_CORR_BITS 1000 /* Max # of correction bits I can buffer */ + + + +/* IRIGHT_SHIFT is like RIGHT_SHIFT, but works on int rather than INT32. + + * We assume that int right shift is unsigned if INT32 right shift is, + + * which should be safe. + + */ + + + +#ifdef RIGHT_SHIFT_IS_UNSIGNED + +#define ISHIFT_TEMPS int ishift_temp; + +#define IRIGHT_SHIFT(x,shft) \ + + ((ishift_temp = (x)) < 0 ? \ + + (ishift_temp >> (shft)) | ((~0) << (16-(shft))) : \ + + (ishift_temp >> (shft))) + +#else + +#define ISHIFT_TEMPS + +#define IRIGHT_SHIFT(x,shft) ((x) >> (shft)) + +#endif + + + +/* Forward declarations */ + +METHODDEF boolean encode_mcu_DC_first JPP((j_compress_ptr cinfo, + + JBLOCKROW *MCU_data)); + +METHODDEF boolean encode_mcu_AC_first JPP((j_compress_ptr cinfo, + + JBLOCKROW *MCU_data)); + +METHODDEF boolean encode_mcu_DC_refine JPP((j_compress_ptr cinfo, + + JBLOCKROW *MCU_data)); + +METHODDEF boolean encode_mcu_AC_refine JPP((j_compress_ptr cinfo, + + JBLOCKROW *MCU_data)); + +METHODDEF void finish_pass_phuff JPP((j_compress_ptr cinfo)); + +METHODDEF void finish_pass_gather_phuff JPP((j_compress_ptr cinfo)); + + + + + +/* + + * Initialize for a Huffman-compressed scan using progressive JPEG. + + */ + + + +METHODDEF void + +start_pass_phuff (j_compress_ptr cinfo, boolean gather_statistics) + +{ + + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + + boolean is_DC_band; + + int ci, tbl; + + jpeg_component_info * compptr; + + + + entropy->cinfo = cinfo; + + entropy->gather_statistics = gather_statistics; + + + + is_DC_band = (cinfo->Ss == 0); + + + + /* We assume jcmaster.c already validated the scan parameters. */ + + + + /* Select execution routines */ + + if (cinfo->Ah == 0) { + + if (is_DC_band) + + entropy->pub.encode_mcu = encode_mcu_DC_first; + + else + + entropy->pub.encode_mcu = encode_mcu_AC_first; + + } else { + + if (is_DC_band) + + entropy->pub.encode_mcu = encode_mcu_DC_refine; + + else { + + entropy->pub.encode_mcu = encode_mcu_AC_refine; + + /* AC refinement needs a correction bit buffer */ + + if (entropy->bit_buffer == NULL) + + entropy->bit_buffer = (char *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + MAX_CORR_BITS * SIZEOF(char)); + + } + + } + + if (gather_statistics) + + entropy->pub.finish_pass = finish_pass_gather_phuff; + + else + + entropy->pub.finish_pass = finish_pass_phuff; + + + + /* Only DC coefficients may be interleaved, so cinfo->comps_in_scan = 1 + + * for AC coefficients. + + */ + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + + compptr = cinfo->cur_comp_info[ci]; + + /* Initialize DC predictions to 0 */ + + entropy->last_dc_val[ci] = 0; + + /* Make sure requested tables are present */ + + /* (In gather mode, tables need not be allocated yet) */ + + if (is_DC_band) { + + if (cinfo->Ah != 0) /* DC refinement needs no table */ + + continue; + + tbl = compptr->dc_tbl_no; + + if (tbl < 0 || tbl >= NUM_HUFF_TBLS || + + (cinfo->dc_huff_tbl_ptrs[tbl] == NULL && !gather_statistics)) + + ERREXIT1(cinfo,JERR_NO_HUFF_TABLE, tbl); + + } else { + + entropy->ac_tbl_no = tbl = compptr->ac_tbl_no; + + if (tbl < 0 || tbl >= NUM_HUFF_TBLS || + + (cinfo->ac_huff_tbl_ptrs[tbl] == NULL && !gather_statistics)) + + ERREXIT1(cinfo,JERR_NO_HUFF_TABLE, tbl); + + } + + if (gather_statistics) { + + /* Allocate and zero the statistics tables */ + + /* Note that jpeg_gen_optimal_table expects 257 entries in each table! */ + + if (entropy->count_ptrs[tbl] == NULL) + + entropy->count_ptrs[tbl] = (long *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + 257 * SIZEOF(long)); + + MEMZERO(entropy->count_ptrs[tbl], 257 * SIZEOF(long)); + + } else { + + /* Compute derived values for Huffman tables */ + + /* We may do this more than once for a table, but it's not expensive */ + + if (is_DC_band) + + jpeg_make_c_derived_tbl(cinfo, cinfo->dc_huff_tbl_ptrs[tbl], + + & entropy->derived_tbls[tbl]); + + else + + jpeg_make_c_derived_tbl(cinfo, cinfo->ac_huff_tbl_ptrs[tbl], + + & entropy->derived_tbls[tbl]); + + } + + } + + + + /* Initialize AC stuff */ + + entropy->EOBRUN = 0; + + entropy->BE = 0; + + + + /* Initialize bit buffer to empty */ + + entropy->put_buffer = 0; + + entropy->put_bits = 0; + + + + /* Initialize restart stuff */ + + entropy->restarts_to_go = cinfo->restart_interval; + + entropy->next_restart_num = 0; + +} + + + + + +/* Outputting bytes to the file. + + * NB: these must be called only when actually outputting, + + * that is, entropy->gather_statistics == FALSE. + + */ + + + +/* Emit a byte */ + +#define emit_byte(entropy,val) \ + + { *(entropy)->next_output_byte++ = (JOCTET) (val); \ + + if (--(entropy)->free_in_buffer == 0) \ + + dump_buffer(entropy); } + + + + + +LOCAL void + +dump_buffer (phuff_entropy_ptr entropy) + +/* Empty the output buffer; we do not support suspension in this module. */ + +{ + + struct jpeg_destination_mgr * dest = entropy->cinfo->dest; + + + + if (! (*dest->empty_output_buffer) (entropy->cinfo)) + + ERREXIT(entropy->cinfo, JERR_CANT_SUSPEND); + + /* After a successful buffer dump, must reset buffer pointers */ + + entropy->next_output_byte = dest->next_output_byte; + + entropy->free_in_buffer = dest->free_in_buffer; + +} + + + + + +/* Outputting bits to the file */ + + + +/* Only the right 24 bits of put_buffer are used; the valid bits are + + * left-justified in this part. At most 16 bits can be passed to emit_bits + + * in one call, and we never retain more than 7 bits in put_buffer + + * between calls, so 24 bits are sufficient. + + */ + + + +// For VC++ + +//INLINE + +__inline + +LOCAL void + +emit_bits (phuff_entropy_ptr entropy, unsigned int code, int size) + +/* Emit some bits, unless we are in gather mode */ + +{ + + /* This routine is heavily used, so it's worth coding tightly. */ + + register INT32 put_buffer = (INT32) code; + + register int put_bits = entropy->put_bits; + + + + /* if size is 0, caller used an invalid Huffman table entry */ + + if (size == 0) + + ERREXIT(entropy->cinfo, JERR_HUFF_MISSING_CODE); + + + + if (entropy->gather_statistics) + + return; /* do nothing if we're only getting stats */ + + + + put_buffer &= (((INT32) 1)<put_buffer; /* and merge with old buffer contents */ + + + + while (put_bits >= 8) { + + int c = (int) ((put_buffer >> 16) & 0xFF); + + + + emit_byte(entropy, c); + + if (c == 0xFF) { /* need to stuff a zero byte? */ + + emit_byte(entropy, 0); + + } + + put_buffer <<= 8; + + put_bits -= 8; + + } + + + + entropy->put_buffer = put_buffer; /* update variables */ + + entropy->put_bits = put_bits; + +} + + + + + +LOCAL void + +flush_bits (phuff_entropy_ptr entropy) + +{ + + emit_bits(entropy, 0x7F, 7); /* fill any partial byte with ones */ + + entropy->put_buffer = 0; /* and reset bit-buffer to empty */ + + entropy->put_bits = 0; + +} + + + + + +/* + + * Emit (or just count) a Huffman symbol. + + */ + + + +// For VC++ + +//INLINE + +__inline + +LOCAL void + +emit_symbol (phuff_entropy_ptr entropy, int tbl_no, int symbol) + +{ + + if (entropy->gather_statistics) + + entropy->count_ptrs[tbl_no][symbol]++; + + else { + + c_derived_tbl * tbl = entropy->derived_tbls[tbl_no]; + + emit_bits(entropy, tbl->ehufco[symbol], tbl->ehufsi[symbol]); + + } + +} + + + + + +/* + + * Emit bits from a correction bit buffer. + + */ + + + +LOCAL void + +emit_buffered_bits (phuff_entropy_ptr entropy, char * bufstart, + + unsigned int nbits) + +{ + + if (entropy->gather_statistics) + + return; /* no real work */ + + + + while (nbits > 0) { + + emit_bits(entropy, (unsigned int) (*bufstart), 1); + + bufstart++; + + nbits--; + + } + +} + + + + + +/* + + * Emit any pending EOBRUN symbol. + + */ + + + +LOCAL void + +emit_eobrun (phuff_entropy_ptr entropy) + +{ + + register int temp, nbits; + + + + if (entropy->EOBRUN > 0) { /* if there is any pending EOBRUN */ + + temp = entropy->EOBRUN; + + nbits = 0; + + while ((temp >>= 1)) + + nbits++; + + + + emit_symbol(entropy, entropy->ac_tbl_no, nbits << 4); + + if (nbits) + + emit_bits(entropy, entropy->EOBRUN, nbits); + + + + entropy->EOBRUN = 0; + + + + /* Emit any buffered correction bits */ + + emit_buffered_bits(entropy, entropy->bit_buffer, entropy->BE); + + entropy->BE = 0; + + } + +} + + + + + +/* + + * Emit a restart marker & resynchronize predictions. + + */ + + + +LOCAL void + +emit_restart (phuff_entropy_ptr entropy, int restart_num) + +{ + + int ci; + + + + emit_eobrun(entropy); + + + + if (! entropy->gather_statistics) { + + flush_bits(entropy); + + emit_byte(entropy, 0xFF); + + emit_byte(entropy, JPEG_RST0 + restart_num); + + } + + + + if (entropy->cinfo->Ss == 0) { + + /* Re-initialize DC predictions to 0 */ + + for (ci = 0; ci < entropy->cinfo->comps_in_scan; ci++) + + entropy->last_dc_val[ci] = 0; + + } else { + + /* Re-initialize all AC-related fields to 0 */ + + entropy->EOBRUN = 0; + + entropy->BE = 0; + + } + +} + + + + + +/* + + * MCU encoding for DC initial scan (either spectral selection, + + * or first pass of successive approximation). + + */ + + + +METHODDEF boolean + +encode_mcu_DC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) + +{ + + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + + register int temp, temp2; + + register int nbits; + + int blkn, ci; + + int Al = cinfo->Al; + + JBLOCKROW block; + + jpeg_component_info * compptr; + + ISHIFT_TEMPS + + + + entropy->next_output_byte = cinfo->dest->next_output_byte; + + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + + + /* Emit restart marker if needed */ + + if (cinfo->restart_interval) + + if (entropy->restarts_to_go == 0) + + emit_restart(entropy, entropy->next_restart_num); + + + + /* Encode the MCU data blocks */ + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + + block = MCU_data[blkn]; + + ci = cinfo->MCU_membership[blkn]; + + compptr = cinfo->cur_comp_info[ci]; + + + + /* Compute the DC value after the required point transform by Al. + + * This is simply an arithmetic right shift. + + */ + + temp2 = IRIGHT_SHIFT((int) ((*block)[0]), Al); + + + + /* DC differences are figured on the point-transformed values. */ + + temp = temp2 - entropy->last_dc_val[ci]; + + entropy->last_dc_val[ci] = temp2; + + + + /* Encode the DC coefficient difference per section G.1.2.1 */ + + temp2 = temp; + + if (temp < 0) { + + temp = -temp; /* temp is abs value of input */ + + /* For a negative input, want temp2 = bitwise complement of abs(input) */ + + /* This code assumes we are on a two's complement machine */ + + temp2--; + + } + + + + /* Find the number of bits needed for the magnitude of the coefficient */ + + nbits = 0; + + while (temp) { + + nbits++; + + temp >>= 1; + + } + + + + /* Count/emit the Huffman-coded symbol for the number of bits */ + + emit_symbol(entropy, compptr->dc_tbl_no, nbits); + + + + /* Emit that number of bits of the value, if positive, */ + + /* or the complement of its magnitude, if negative. */ + + if (nbits) /* emit_bits rejects calls with size 0 */ + + emit_bits(entropy, (unsigned int) temp2, nbits); + + } + + + + cinfo->dest->next_output_byte = entropy->next_output_byte; + + cinfo->dest->free_in_buffer = entropy->free_in_buffer; + + + + /* Update restart-interval state too */ + + if (cinfo->restart_interval) { + + if (entropy->restarts_to_go == 0) { + + entropy->restarts_to_go = cinfo->restart_interval; + + entropy->next_restart_num++; + + entropy->next_restart_num &= 7; + + } + + entropy->restarts_to_go--; + + } + + + + return TRUE; + +} + + + + + +/* + + * MCU encoding for AC initial scan (either spectral selection, + + * or first pass of successive approximation). + + */ + + + +METHODDEF boolean + +encode_mcu_AC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) + +{ + + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + + register int temp, temp2; + + register int nbits; + + register int r, k; + + int Se = cinfo->Se; + + int Al = cinfo->Al; + + JBLOCKROW block; + + + + entropy->next_output_byte = cinfo->dest->next_output_byte; + + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + + + /* Emit restart marker if needed */ + + if (cinfo->restart_interval) + + if (entropy->restarts_to_go == 0) + + emit_restart(entropy, entropy->next_restart_num); + + + + /* Encode the MCU data block */ + + block = MCU_data[0]; + + + + /* Encode the AC coefficients per section G.1.2.2, fig. G.3 */ + + + + r = 0; /* r = run length of zeros */ + + + + for (k = cinfo->Ss; k <= Se; k++) { + + if ((temp = (*block)[jpeg_natural_order[k]]) == 0) { + + r++; + + continue; + + } + + /* We must apply the point transform by Al. For AC coefficients this + + * is an integer division with rounding towards 0. To do this portably + + * in C, we shift after obtaining the absolute value; so the code is + + * interwoven with finding the abs value (temp) and output bits (temp2). + + */ + + if (temp < 0) { + + temp = -temp; /* temp is abs value of input */ + + temp >>= Al; /* apply the point transform */ + + /* For a negative coef, want temp2 = bitwise complement of abs(coef) */ + + temp2 = ~temp; + + } else { + + temp >>= Al; /* apply the point transform */ + + temp2 = temp; + + } + + /* Watch out for case that nonzero coef is zero after point transform */ + + if (temp == 0) { + + r++; + + continue; + + } + + + + /* Emit any pending EOBRUN */ + + if (entropy->EOBRUN > 0) + + emit_eobrun(entropy); + + /* if run length > 15, must emit special run-length-16 codes (0xF0) */ + + while (r > 15) { + + emit_symbol(entropy, entropy->ac_tbl_no, 0xF0); + + r -= 16; + + } + + + + /* Find the number of bits needed for the magnitude of the coefficient */ + + nbits = 1; /* there must be at least one 1 bit */ + + while ((temp >>= 1)) + + nbits++; + + + + /* Count/emit Huffman symbol for run length / number of bits */ + + emit_symbol(entropy, entropy->ac_tbl_no, (r << 4) + nbits); + + + + /* Emit that number of bits of the value, if positive, */ + + /* or the complement of its magnitude, if negative. */ + + emit_bits(entropy, (unsigned int) temp2, nbits); + + + + r = 0; /* reset zero run length */ + + } + + + + if (r > 0) { /* If there are trailing zeroes, */ + + entropy->EOBRUN++; /* count an EOB */ + + if (entropy->EOBRUN == 0x7FFF) + + emit_eobrun(entropy); /* force it out to avoid overflow */ + + } + + + + cinfo->dest->next_output_byte = entropy->next_output_byte; + + cinfo->dest->free_in_buffer = entropy->free_in_buffer; + + + + /* Update restart-interval state too */ + + if (cinfo->restart_interval) { + + if (entropy->restarts_to_go == 0) { + + entropy->restarts_to_go = cinfo->restart_interval; + + entropy->next_restart_num++; + + entropy->next_restart_num &= 7; + + } + + entropy->restarts_to_go--; + + } + + + + return TRUE; + +} + + + + + +/* + + * MCU encoding for DC successive approximation refinement scan. + + * Note: we assume such scans can be multi-component, although the spec + + * is not very clear on the point. + + */ + + + +METHODDEF boolean + +encode_mcu_DC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) + +{ + + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + + register int temp; + + int blkn; + + int Al = cinfo->Al; + + JBLOCKROW block; + + + + entropy->next_output_byte = cinfo->dest->next_output_byte; + + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + + + /* Emit restart marker if needed */ + + if (cinfo->restart_interval) + + if (entropy->restarts_to_go == 0) + + emit_restart(entropy, entropy->next_restart_num); + + + + /* Encode the MCU data blocks */ + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + + block = MCU_data[blkn]; + + + + /* We simply emit the Al'th bit of the DC coefficient value. */ + + temp = (*block)[0]; + + emit_bits(entropy, (unsigned int) (temp >> Al), 1); + + } + + + + cinfo->dest->next_output_byte = entropy->next_output_byte; + + cinfo->dest->free_in_buffer = entropy->free_in_buffer; + + + + /* Update restart-interval state too */ + + if (cinfo->restart_interval) { + + if (entropy->restarts_to_go == 0) { + + entropy->restarts_to_go = cinfo->restart_interval; + + entropy->next_restart_num++; + + entropy->next_restart_num &= 7; + + } + + entropy->restarts_to_go--; + + } + + + + return TRUE; + +} + + + + + +/* + + * MCU encoding for AC successive approximation refinement scan. + + */ + + + +METHODDEF boolean + +encode_mcu_AC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) + +{ + + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + + register int temp; + + register int r, k; + + int EOB; + + char *BR_buffer; + + unsigned int BR; + + int Se = cinfo->Se; + + int Al = cinfo->Al; + + JBLOCKROW block; + + int absvalues[DCTSIZE2]; + + + + entropy->next_output_byte = cinfo->dest->next_output_byte; + + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + + + /* Emit restart marker if needed */ + + if (cinfo->restart_interval) + + if (entropy->restarts_to_go == 0) + + emit_restart(entropy, entropy->next_restart_num); + + + + /* Encode the MCU data block */ + + block = MCU_data[0]; + + + + /* It is convenient to make a pre-pass to determine the transformed + + * coefficients' absolute values and the EOB position. + + */ + + EOB = 0; + + for (k = cinfo->Ss; k <= Se; k++) { + + temp = (*block)[jpeg_natural_order[k]]; + + /* We must apply the point transform by Al. For AC coefficients this + + * is an integer division with rounding towards 0. To do this portably + + * in C, we shift after obtaining the absolute value. + + */ + + if (temp < 0) + + temp = -temp; /* temp is abs value of input */ + + temp >>= Al; /* apply the point transform */ + + absvalues[k] = temp; /* save abs value for main pass */ + + if (temp == 1) + + EOB = k; /* EOB = index of last newly-nonzero coef */ + + } + + + + /* Encode the AC coefficients per section G.1.2.3, fig. G.7 */ + + + + r = 0; /* r = run length of zeros */ + + BR = 0; /* BR = count of buffered bits added now */ + + BR_buffer = entropy->bit_buffer + entropy->BE; /* Append bits to buffer */ + + + + for (k = cinfo->Ss; k <= Se; k++) { + + if ((temp = absvalues[k]) == 0) { + + r++; + + continue; + + } + + + + /* Emit any required ZRLs, but not if they can be folded into EOB */ + + while (r > 15 && k <= EOB) { + + /* emit any pending EOBRUN and the BE correction bits */ + + emit_eobrun(entropy); + + /* Emit ZRL */ + + emit_symbol(entropy, entropy->ac_tbl_no, 0xF0); + + r -= 16; + + /* Emit buffered correction bits that must be associated with ZRL */ + + emit_buffered_bits(entropy, BR_buffer, BR); + + BR_buffer = entropy->bit_buffer; /* BE bits are gone now */ + + BR = 0; + + } + + + + /* If the coef was previously nonzero, it only needs a correction bit. + + * NOTE: a straight translation of the spec's figure G.7 would suggest + + * that we also need to test r > 15. But if r > 15, we can only get here + + * if k > EOB, which implies that this coefficient is not 1. + + */ + + if (temp > 1) { + + /* The correction bit is the next bit of the absolute value. */ + + BR_buffer[BR++] = (char) (temp & 1); + + continue; + + } + + + + /* Emit any pending EOBRUN and the BE correction bits */ + + emit_eobrun(entropy); + + + + /* Count/emit Huffman symbol for run length / number of bits */ + + emit_symbol(entropy, entropy->ac_tbl_no, (r << 4) + 1); + + + + /* Emit output bit for newly-nonzero coef */ + + temp = ((*block)[jpeg_natural_order[k]] < 0) ? 0 : 1; + + emit_bits(entropy, (unsigned int) temp, 1); + + + + /* Emit buffered correction bits that must be associated with this code */ + + emit_buffered_bits(entropy, BR_buffer, BR); + + BR_buffer = entropy->bit_buffer; /* BE bits are gone now */ + + BR = 0; + + r = 0; /* reset zero run length */ + + } + + + + if (r > 0 || BR > 0) { /* If there are trailing zeroes, */ + + entropy->EOBRUN++; /* count an EOB */ + + entropy->BE += BR; /* concat my correction bits to older ones */ + + /* We force out the EOB if we risk either: + + * 1. overflow of the EOB counter; + + * 2. overflow of the correction bit buffer during the next MCU. + + */ + + if (entropy->EOBRUN == 0x7FFF || entropy->BE > (MAX_CORR_BITS-DCTSIZE2+1)) + + emit_eobrun(entropy); + + } + + + + cinfo->dest->next_output_byte = entropy->next_output_byte; + + cinfo->dest->free_in_buffer = entropy->free_in_buffer; + + + + /* Update restart-interval state too */ + + if (cinfo->restart_interval) { + + if (entropy->restarts_to_go == 0) { + + entropy->restarts_to_go = cinfo->restart_interval; + + entropy->next_restart_num++; + + entropy->next_restart_num &= 7; + + } + + entropy->restarts_to_go--; + + } + + + + return TRUE; + +} + + + + + +/* + + * Finish up at the end of a Huffman-compressed progressive scan. + + */ + + + +METHODDEF void + +finish_pass_phuff (j_compress_ptr cinfo) + +{ + + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + + + + entropy->next_output_byte = cinfo->dest->next_output_byte; + + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + + + /* Flush out any buffered data */ + + emit_eobrun(entropy); + + flush_bits(entropy); + + + + cinfo->dest->next_output_byte = entropy->next_output_byte; + + cinfo->dest->free_in_buffer = entropy->free_in_buffer; + +} + + + + + +/* + + * Finish up a statistics-gathering pass and create the new Huffman tables. + + */ + + + +METHODDEF void + +finish_pass_gather_phuff (j_compress_ptr cinfo) + +{ + + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + + boolean is_DC_band; + + int ci, tbl; + + jpeg_component_info * compptr; + + JHUFF_TBL **htblptr; + + boolean did[NUM_HUFF_TBLS]; + + + + /* Flush out buffered data (all we care about is counting the EOB symbol) */ + + emit_eobrun(entropy); + + + + is_DC_band = (cinfo->Ss == 0); + + + + /* It's important not to apply jpeg_gen_optimal_table more than once + + * per table, because it clobbers the input frequency counts! + + */ + + MEMZERO(did, SIZEOF(did)); + + + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + + compptr = cinfo->cur_comp_info[ci]; + + if (is_DC_band) { + + if (cinfo->Ah != 0) /* DC refinement needs no table */ + + continue; + + tbl = compptr->dc_tbl_no; + + } else { + + tbl = compptr->ac_tbl_no; + + } + + if (! did[tbl]) { + + if (is_DC_band) + + htblptr = & cinfo->dc_huff_tbl_ptrs[tbl]; + + else + + htblptr = & cinfo->ac_huff_tbl_ptrs[tbl]; + + if (*htblptr == NULL) + + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + + jpeg_gen_optimal_table(cinfo, *htblptr, entropy->count_ptrs[tbl]); + + did[tbl] = TRUE; + + } + + } + +} + + + + + +/* + + * Module initialization routine for progressive Huffman entropy encoding. + + */ + + + +GLOBAL void + +jinit_phuff_encoder (j_compress_ptr cinfo) + +{ + + phuff_entropy_ptr entropy; + + int i; + + + + entropy = (phuff_entropy_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(phuff_entropy_encoder)); + + cinfo->entropy = (struct jpeg_entropy_encoder *) entropy; + + entropy->pub.start_pass = start_pass_phuff; + + + + /* Mark tables unallocated */ + + for (i = 0; i < NUM_HUFF_TBLS; i++) { + + entropy->derived_tbls[i] = NULL; + + entropy->count_ptrs[i] = NULL; + + } + + entropy->bit_buffer = NULL; /* needed only in AC refinement scan */ + +} + + + +#endif /* C_PROGRESSIVE_SUPPORTED */ + diff --git a/utils/roq2/jpeg/jcprepct.c b/utils/roq2/jpeg/jcprepct.c new file mode 100644 index 0000000..b96a3d0 --- /dev/null +++ b/utils/roq2/jpeg/jcprepct.c @@ -0,0 +1,742 @@ +/* + + * jcprepct.c + + * + + * Copyright (C) 1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains the compression preprocessing controller. + + * This controller manages the color conversion, downsampling, + + * and edge expansion steps. + + * + + * Most of the complexity here is associated with buffering input rows + + * as required by the downsampler. See the comments at the head of + + * jcsample.c for the downsampler's needs. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + + + +/* At present, jcsample.c can request context rows only for smoothing. + + * In the future, we might also need context rows for CCIR601 sampling + + * or other more-complex downsampling procedures. The code to support + + * context rows should be compiled only if needed. + + */ + +#ifdef INPUT_SMOOTHING_SUPPORTED + +#define CONTEXT_ROWS_SUPPORTED + +#endif + + + + + +/* + + * For the simple (no-context-row) case, we just need to buffer one + + * row group's worth of pixels for the downsampling step. At the bottom of + + * the image, we pad to a full row group by replicating the last pixel row. + + * The downsampler's last output row is then replicated if needed to pad + + * out to a full iMCU row. + + * + + * When providing context rows, we must buffer three row groups' worth of + + * pixels. Three row groups are physically allocated, but the row pointer + + * arrays are made five row groups high, with the extra pointers above and + + * below "wrapping around" to point to the last and first real row groups. + + * This allows the downsampler to access the proper context rows. + + * At the top and bottom of the image, we create dummy context rows by + + * copying the first or last real pixel row. This copying could be avoided + + * by pointer hacking as is done in jdmainct.c, but it doesn't seem worth the + + * trouble on the compression side. + + */ + + + + + +/* Private buffer controller object */ + + + +typedef struct { + + struct jpeg_c_prep_controller pub; /* public fields */ + + + + /* Downsampling input buffer. This buffer holds color-converted data + + * until we have enough to do a downsample step. + + */ + + JSAMPARRAY color_buf[MAX_COMPONENTS]; + + + + JDIMENSION rows_to_go; /* counts rows remaining in source image */ + + int next_buf_row; /* index of next row to store in color_buf */ + + + +#ifdef CONTEXT_ROWS_SUPPORTED /* only needed for context case */ + + int this_row_group; /* starting row index of group to process */ + + int next_buf_stop; /* downsample when we reach this index */ + +#endif + +} my_prep_controller; + + + +typedef my_prep_controller * my_prep_ptr; + + + + + +/* + + * Initialize for a processing pass. + + */ + + + +METHODDEF void + +start_pass_prep (j_compress_ptr cinfo, J_BUF_MODE pass_mode) + +{ + + my_prep_ptr prep = (my_prep_ptr) cinfo->prep; + + + + if (pass_mode != JBUF_PASS_THRU) + + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + + + /* Initialize total-height counter for detecting bottom of image */ + + prep->rows_to_go = cinfo->image_height; + + /* Mark the conversion buffer empty */ + + prep->next_buf_row = 0; + +#ifdef CONTEXT_ROWS_SUPPORTED + + /* Preset additional state variables for context mode. + + * These aren't used in non-context mode, so we needn't test which mode. + + */ + + prep->this_row_group = 0; + + /* Set next_buf_stop to stop after two row groups have been read in. */ + + prep->next_buf_stop = 2 * cinfo->max_v_samp_factor; + +#endif + +} + + + + + +/* + + * Expand an image vertically from height input_rows to height output_rows, + + * by duplicating the bottom row. + + */ + + + +LOCAL void + +expand_bottom_edge (JSAMPARRAY image_data, JDIMENSION num_cols, + + int input_rows, int output_rows) + +{ + + register int row; + + + + for (row = input_rows; row < output_rows; row++) { + + jcopy_sample_rows(image_data, input_rows-1, image_data, row, + + 1, num_cols); + + } + +} + + + + + +/* + + * Process some data in the simple no-context case. + + * + + * Preprocessor output data is counted in "row groups". A row group + + * is defined to be v_samp_factor sample rows of each component. + + * Downsampling will produce this much data from each max_v_samp_factor + + * input rows. + + */ + + + +METHODDEF void + +pre_process_data (j_compress_ptr cinfo, + + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + + JDIMENSION in_rows_avail, + + JSAMPIMAGE output_buf, JDIMENSION *out_row_group_ctr, + + JDIMENSION out_row_groups_avail) + +{ + + my_prep_ptr prep = (my_prep_ptr) cinfo->prep; + + int numrows, ci; + + JDIMENSION inrows; + + jpeg_component_info * compptr; + + + + while (*in_row_ctr < in_rows_avail && + + *out_row_group_ctr < out_row_groups_avail) { + + /* Do color conversion to fill the conversion buffer. */ + + inrows = in_rows_avail - *in_row_ctr; + + numrows = cinfo->max_v_samp_factor - prep->next_buf_row; + + numrows = (int) MIN((JDIMENSION) numrows, inrows); + + (*cinfo->cconvert->color_convert) (cinfo, input_buf + *in_row_ctr, + + prep->color_buf, + + (JDIMENSION) prep->next_buf_row, + + numrows); + + *in_row_ctr += numrows; + + prep->next_buf_row += numrows; + + prep->rows_to_go -= numrows; + + /* If at bottom of image, pad to fill the conversion buffer. */ + + if (prep->rows_to_go == 0 && + + prep->next_buf_row < cinfo->max_v_samp_factor) { + + for (ci = 0; ci < cinfo->num_components; ci++) { + + expand_bottom_edge(prep->color_buf[ci], cinfo->image_width, + + prep->next_buf_row, cinfo->max_v_samp_factor); + + } + + prep->next_buf_row = cinfo->max_v_samp_factor; + + } + + /* If we've filled the conversion buffer, empty it. */ + + if (prep->next_buf_row == cinfo->max_v_samp_factor) { + + (*cinfo->downsample->downsample) (cinfo, + + prep->color_buf, (JDIMENSION) 0, + + output_buf, *out_row_group_ctr); + + prep->next_buf_row = 0; + + (*out_row_group_ctr)++; + + } + + /* If at bottom of image, pad the output to a full iMCU height. + + * Note we assume the caller is providing a one-iMCU-height output buffer! + + */ + + if (prep->rows_to_go == 0 && + + *out_row_group_ctr < out_row_groups_avail) { + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + expand_bottom_edge(output_buf[ci], + + compptr->width_in_blocks * DCTSIZE, + + (int) (*out_row_group_ctr * compptr->v_samp_factor), + + (int) (out_row_groups_avail * compptr->v_samp_factor)); + + } + + *out_row_group_ctr = out_row_groups_avail; + + break; /* can exit outer loop without test */ + + } + + } + +} + + + + + +#ifdef CONTEXT_ROWS_SUPPORTED + + + +/* + + * Process some data in the context case. + + */ + + + +METHODDEF void + +pre_process_context (j_compress_ptr cinfo, + + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + + JDIMENSION in_rows_avail, + + JSAMPIMAGE output_buf, JDIMENSION *out_row_group_ctr, + + JDIMENSION out_row_groups_avail) + +{ + + my_prep_ptr prep = (my_prep_ptr) cinfo->prep; + + int numrows, ci; + + int buf_height = cinfo->max_v_samp_factor * 3; + + JDIMENSION inrows; + + jpeg_component_info * compptr; + + + + while (*out_row_group_ctr < out_row_groups_avail) { + + if (*in_row_ctr < in_rows_avail) { + + /* Do color conversion to fill the conversion buffer. */ + + inrows = in_rows_avail - *in_row_ctr; + + numrows = prep->next_buf_stop - prep->next_buf_row; + + numrows = (int) MIN((JDIMENSION) numrows, inrows); + + (*cinfo->cconvert->color_convert) (cinfo, input_buf + *in_row_ctr, + + prep->color_buf, + + (JDIMENSION) prep->next_buf_row, + + numrows); + + /* Pad at top of image, if first time through */ + + if (prep->rows_to_go == cinfo->image_height) { + + for (ci = 0; ci < cinfo->num_components; ci++) { + + int row; + + for (row = 1; row <= cinfo->max_v_samp_factor; row++) { + + jcopy_sample_rows(prep->color_buf[ci], 0, + + prep->color_buf[ci], -row, + + 1, cinfo->image_width); + + } + + } + + } + + *in_row_ctr += numrows; + + prep->next_buf_row += numrows; + + prep->rows_to_go -= numrows; + + } else { + + /* Return for more data, unless we are at the bottom of the image. */ + + if (prep->rows_to_go != 0) + + break; + + } + + /* If at bottom of image, pad to fill the conversion buffer. */ + + if (prep->rows_to_go == 0 && + + prep->next_buf_row < prep->next_buf_stop) { + + for (ci = 0; ci < cinfo->num_components; ci++) { + + expand_bottom_edge(prep->color_buf[ci], cinfo->image_width, + + prep->next_buf_row, prep->next_buf_stop); + + } + + prep->next_buf_row = prep->next_buf_stop; + + } + + /* If we've gotten enough data, downsample a row group. */ + + if (prep->next_buf_row == prep->next_buf_stop) { + + (*cinfo->downsample->downsample) (cinfo, + + prep->color_buf, + + (JDIMENSION) prep->this_row_group, + + output_buf, *out_row_group_ctr); + + (*out_row_group_ctr)++; + + /* Advance pointers with wraparound as necessary. */ + + prep->this_row_group += cinfo->max_v_samp_factor; + + if (prep->this_row_group >= buf_height) + + prep->this_row_group = 0; + + if (prep->next_buf_row >= buf_height) + + prep->next_buf_row = 0; + + prep->next_buf_stop = prep->next_buf_row + cinfo->max_v_samp_factor; + + } + + /* If at bottom of image, pad the output to a full iMCU height. + + * Note we assume the caller is providing a one-iMCU-height output buffer! + + */ + + if (prep->rows_to_go == 0 && + + *out_row_group_ctr < out_row_groups_avail) { + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + expand_bottom_edge(output_buf[ci], + + compptr->width_in_blocks * DCTSIZE, + + (int) (*out_row_group_ctr * compptr->v_samp_factor), + + (int) (out_row_groups_avail * compptr->v_samp_factor)); + + } + + *out_row_group_ctr = out_row_groups_avail; + + break; /* can exit outer loop without test */ + + } + + } + +} + + + + + +/* + + * Create the wrapped-around downsampling input buffer needed for context mode. + + */ + + + +LOCAL void + +create_context_buffer (j_compress_ptr cinfo) + +{ + + my_prep_ptr prep = (my_prep_ptr) cinfo->prep; + + int rgroup_height = cinfo->max_v_samp_factor; + + int ci, i; + + jpeg_component_info * compptr; + + JSAMPARRAY true_buffer, fake_buffer; + + + + /* Grab enough space for fake row pointers for all the components; + + * we need five row groups' worth of pointers for each component. + + */ + + fake_buffer = (JSAMPARRAY) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (cinfo->num_components * 5 * rgroup_height) * + + SIZEOF(JSAMPROW)); + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + /* Allocate the actual buffer space (3 row groups) for this component. + + * We make the buffer wide enough to allow the downsampler to edge-expand + + * horizontally within the buffer, if it so chooses. + + */ + + true_buffer = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (JDIMENSION) (((long) compptr->width_in_blocks * DCTSIZE * + + cinfo->max_h_samp_factor) / compptr->h_samp_factor), + + (JDIMENSION) (3 * rgroup_height)); + + /* Copy true buffer row pointers into the middle of the fake row array */ + + MEMCOPY(fake_buffer + rgroup_height, true_buffer, + + 3 * rgroup_height * SIZEOF(JSAMPROW)); + + /* Fill in the above and below wraparound pointers */ + + for (i = 0; i < rgroup_height; i++) { + + fake_buffer[i] = true_buffer[2 * rgroup_height + i]; + + fake_buffer[4 * rgroup_height + i] = true_buffer[i]; + + } + + prep->color_buf[ci] = fake_buffer + rgroup_height; + + fake_buffer += 5 * rgroup_height; /* point to space for next component */ + + } + +} + + + +#endif /* CONTEXT_ROWS_SUPPORTED */ + + + + + +/* + + * Initialize preprocessing controller. + + */ + + + +GLOBAL void + +jinit_c_prep_controller (j_compress_ptr cinfo, boolean need_full_buffer) + +{ + + my_prep_ptr prep; + + int ci; + + jpeg_component_info * compptr; + + + + if (need_full_buffer) /* safety check */ + + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + + + prep = (my_prep_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(my_prep_controller)); + + cinfo->prep = (struct jpeg_c_prep_controller *) prep; + + prep->pub.start_pass = start_pass_prep; + + + + /* Allocate the color conversion buffer. + + * We make the buffer wide enough to allow the downsampler to edge-expand + + * horizontally within the buffer, if it so chooses. + + */ + + if (cinfo->downsample->need_context_rows) { + + /* Set up to provide context rows */ + +#ifdef CONTEXT_ROWS_SUPPORTED + + prep->pub.pre_process_data = pre_process_context; + + create_context_buffer(cinfo); + +#else + + ERREXIT(cinfo, JERR_NOT_COMPILED); + +#endif + + } else { + + /* No context, just make it tall enough for one row group */ + + prep->pub.pre_process_data = pre_process_data; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + prep->color_buf[ci] = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (JDIMENSION) (((long) compptr->width_in_blocks * DCTSIZE * + + cinfo->max_h_samp_factor) / compptr->h_samp_factor), + + (JDIMENSION) cinfo->max_v_samp_factor); + + } + + } + +} + diff --git a/utils/roq2/jpeg/jcsample.c b/utils/roq2/jpeg/jcsample.c new file mode 100644 index 0000000..1fb991b --- /dev/null +++ b/utils/roq2/jpeg/jcsample.c @@ -0,0 +1,1038 @@ +/* + + * jcsample.c + + * + + * Copyright (C) 1991-1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains downsampling routines. + + * + + * Downsampling input data is counted in "row groups". A row group + + * is defined to be max_v_samp_factor pixel rows of each component, + + * from which the downsampler produces v_samp_factor sample rows. + + * A single row group is processed in each call to the downsampler module. + + * + + * The downsampler is responsible for edge-expansion of its output data + + * to fill an integral number of DCT blocks horizontally. The source buffer + + * may be modified if it is helpful for this purpose (the source buffer is + + * allocated wide enough to correspond to the desired output width). + + * The caller (the prep controller) is responsible for vertical padding. + + * + + * The downsampler may request "context rows" by setting need_context_rows + + * during startup. In this case, the input arrays will contain at least + + * one row group's worth of pixels above and below the passed-in data; + + * the caller will create dummy rows at image top and bottom by replicating + + * the first or last real pixel row. + + * + + * An excellent reference for image resampling is + + * Digital Image Warping, George Wolberg, 1990. + + * Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7. + + * + + * The downsampling algorithm used here is a simple average of the source + + * pixels covered by the output pixel. The hi-falutin sampling literature + + * refers to this as a "box filter". In general the characteristics of a box + + * filter are not very good, but for the specific cases we normally use (1:1 + + * and 2:1 ratios) the box is equivalent to a "triangle filter" which is not + + * nearly so bad. If you intend to use other sampling ratios, you'd be well + + * advised to improve this code. + + * + + * A simple input-smoothing capability is provided. This is mainly intended + + * for cleaning up color-dithered GIF input files (if you find it inadequate, + + * we suggest using an external filtering program such as pnmconvol). When + + * enabled, each input pixel P is replaced by a weighted sum of itself and its + + * eight neighbors. P's weight is 1-8*SF and each neighbor's weight is SF, + + * where SF = (smoothing_factor / 1024). + + * Currently, smoothing is only supported for 2h2v sampling factors. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + + + +/* Pointer to routine to downsample a single component */ + +typedef JMETHOD(void, downsample1_ptr, + + (j_compress_ptr cinfo, jpeg_component_info * compptr, + + JSAMPARRAY input_data, JSAMPARRAY output_data)); + + + +/* Private subobject */ + + + +typedef struct { + + struct jpeg_downsampler pub; /* public fields */ + + + + /* Downsampling method pointers, one per component */ + + downsample1_ptr methods[MAX_COMPONENTS]; + +} my_downsampler; + + + +typedef my_downsampler * my_downsample_ptr; + + + + + +/* + + * Initialize for a downsampling pass. + + */ + + + +METHODDEF void + +start_pass_downsample (j_compress_ptr cinfo) + +{ + + /* no work for now */ + +} + + + + + +/* + + * Expand a component horizontally from width input_cols to width output_cols, + + * by duplicating the rightmost samples. + + */ + + + +LOCAL void + +expand_right_edge (JSAMPARRAY image_data, int num_rows, + + JDIMENSION input_cols, JDIMENSION output_cols) + +{ + + register JSAMPROW ptr; + + register JSAMPLE pixval; + + register int count; + + int row; + + int numcols = (int) (output_cols - input_cols); + + + + if (numcols > 0) { + + for (row = 0; row < num_rows; row++) { + + ptr = image_data[row] + input_cols; + + pixval = ptr[-1]; /* don't need GETJSAMPLE() here */ + + for (count = numcols; count > 0; count--) + + *ptr++ = pixval; + + } + + } + +} + + + + + +/* + + * Do downsampling for a whole row group (all components). + + * + + * In this version we simply downsample each component independently. + + */ + + + +METHODDEF void + +sep_downsample (j_compress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION in_row_index, + + JSAMPIMAGE output_buf, JDIMENSION out_row_group_index) + +{ + + my_downsample_ptr downsample = (my_downsample_ptr) cinfo->downsample; + + int ci; + + jpeg_component_info * compptr; + + JSAMPARRAY in_ptr, out_ptr; + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + in_ptr = input_buf[ci] + in_row_index; + + out_ptr = output_buf[ci] + (out_row_group_index * compptr->v_samp_factor); + + (*downsample->methods[ci]) (cinfo, compptr, in_ptr, out_ptr); + + } + +} + + + + + +/* + + * Downsample pixel values of a single component. + + * One row group is processed per call. + + * This version handles arbitrary integral sampling ratios, without smoothing. + + * Note that this version is not actually used for customary sampling ratios. + + */ + + + +METHODDEF void + +int_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + + JSAMPARRAY input_data, JSAMPARRAY output_data) + +{ + + int inrow, outrow, h_expand, v_expand, numpix, numpix2, h, v; + + JDIMENSION outcol, outcol_h; /* outcol_h == outcol*h_expand */ + + JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + + JSAMPROW inptr, outptr; + + INT32 outvalue; + + + + h_expand = cinfo->max_h_samp_factor / compptr->h_samp_factor; + + v_expand = cinfo->max_v_samp_factor / compptr->v_samp_factor; + + numpix = h_expand * v_expand; + + numpix2 = numpix/2; + + + + /* Expand input data enough to let all the output samples be generated + + * by the standard loop. Special-casing padded output would be more + + * efficient. + + */ + + expand_right_edge(input_data, cinfo->max_v_samp_factor, + + cinfo->image_width, output_cols * h_expand); + + + + inrow = 0; + + for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) { + + outptr = output_data[outrow]; + + for (outcol = 0, outcol_h = 0; outcol < output_cols; + + outcol++, outcol_h += h_expand) { + + outvalue = 0; + + for (v = 0; v < v_expand; v++) { + + inptr = input_data[inrow+v] + outcol_h; + + for (h = 0; h < h_expand; h++) { + + outvalue += (INT32) GETJSAMPLE(*inptr++); + + } + + } + + *outptr++ = (JSAMPLE) ((outvalue + numpix2) / numpix); + + } + + inrow += v_expand; + + } + +} + + + + + +/* + + * Downsample pixel values of a single component. + + * This version handles the special case of a full-size component, + + * without smoothing. + + */ + + + +METHODDEF void + +fullsize_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + + JSAMPARRAY input_data, JSAMPARRAY output_data) + +{ + + /* Copy the data */ + + jcopy_sample_rows(input_data, 0, output_data, 0, + + cinfo->max_v_samp_factor, cinfo->image_width); + + /* Edge-expand */ + + expand_right_edge(output_data, cinfo->max_v_samp_factor, + + cinfo->image_width, compptr->width_in_blocks * DCTSIZE); + +} + + + + + +/* + + * Downsample pixel values of a single component. + + * This version handles the common case of 2:1 horizontal and 1:1 vertical, + + * without smoothing. + + * + + * A note about the "bias" calculations: when rounding fractional values to + + * integer, we do not want to always round 0.5 up to the next integer. + + * If we did that, we'd introduce a noticeable bias towards larger values. + + * Instead, this code is arranged so that 0.5 will be rounded up or down at + + * alternate pixel locations (a simple ordered dither pattern). + + */ + + + +METHODDEF void + +h2v1_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + + JSAMPARRAY input_data, JSAMPARRAY output_data) + +{ + + int outrow; + + JDIMENSION outcol; + + JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + + register JSAMPROW inptr, outptr; + + register int bias; + + + + /* Expand input data enough to let all the output samples be generated + + * by the standard loop. Special-casing padded output would be more + + * efficient. + + */ + + expand_right_edge(input_data, cinfo->max_v_samp_factor, + + cinfo->image_width, output_cols * 2); + + + + for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) { + + outptr = output_data[outrow]; + + inptr = input_data[outrow]; + + bias = 0; /* bias = 0,1,0,1,... for successive samples */ + + for (outcol = 0; outcol < output_cols; outcol++) { + + *outptr++ = (JSAMPLE) ((GETJSAMPLE(*inptr) + GETJSAMPLE(inptr[1]) + + + bias) >> 1); + + bias ^= 1; /* 0=>1, 1=>0 */ + + inptr += 2; + + } + + } + +} + + + + + +/* + + * Downsample pixel values of a single component. + + * This version handles the standard case of 2:1 horizontal and 2:1 vertical, + + * without smoothing. + + */ + + + +METHODDEF void + +h2v2_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + + JSAMPARRAY input_data, JSAMPARRAY output_data) + +{ + + int inrow, outrow; + + JDIMENSION outcol; + + JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + + register JSAMPROW inptr0, inptr1, outptr; + + register int bias; + + + + /* Expand input data enough to let all the output samples be generated + + * by the standard loop. Special-casing padded output would be more + + * efficient. + + */ + + expand_right_edge(input_data, cinfo->max_v_samp_factor, + + cinfo->image_width, output_cols * 2); + + + + inrow = 0; + + for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) { + + outptr = output_data[outrow]; + + inptr0 = input_data[inrow]; + + inptr1 = input_data[inrow+1]; + + bias = 1; /* bias = 1,2,1,2,... for successive samples */ + + for (outcol = 0; outcol < output_cols; outcol++) { + + *outptr++ = (JSAMPLE) ((GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) + + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]) + + + bias) >> 2); + + bias ^= 3; /* 1=>2, 2=>1 */ + + inptr0 += 2; inptr1 += 2; + + } + + inrow += 2; + + } + +} + + + + + +#ifdef INPUT_SMOOTHING_SUPPORTED + + + +/* + + * Downsample pixel values of a single component. + + * This version handles the standard case of 2:1 horizontal and 2:1 vertical, + + * with smoothing. One row of context is required. + + */ + + + +METHODDEF void + +h2v2_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + + JSAMPARRAY input_data, JSAMPARRAY output_data) + +{ + + int inrow, outrow; + + JDIMENSION colctr; + + JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + + register JSAMPROW inptr0, inptr1, above_ptr, below_ptr, outptr; + + INT32 membersum, neighsum, memberscale, neighscale; + + + + /* Expand input data enough to let all the output samples be generated + + * by the standard loop. Special-casing padded output would be more + + * efficient. + + */ + + expand_right_edge(input_data - 1, cinfo->max_v_samp_factor + 2, + + cinfo->image_width, output_cols * 2); + + + + /* We don't bother to form the individual "smoothed" input pixel values; + + * we can directly compute the output which is the average of the four + + * smoothed values. Each of the four member pixels contributes a fraction + + * (1-8*SF) to its own smoothed image and a fraction SF to each of the three + + * other smoothed pixels, therefore a total fraction (1-5*SF)/4 to the final + + * output. The four corner-adjacent neighbor pixels contribute a fraction + + * SF to just one smoothed pixel, or SF/4 to the final output; while the + + * eight edge-adjacent neighbors contribute SF to each of two smoothed + + * pixels, or SF/2 overall. In order to use integer arithmetic, these + + * factors are scaled by 2^16 = 65536. + + * Also recall that SF = smoothing_factor / 1024. + + */ + + + + memberscale = 16384 - cinfo->smoothing_factor * 80; /* scaled (1-5*SF)/4 */ + + neighscale = cinfo->smoothing_factor * 16; /* scaled SF/4 */ + + + + inrow = 0; + + for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) { + + outptr = output_data[outrow]; + + inptr0 = input_data[inrow]; + + inptr1 = input_data[inrow+1]; + + above_ptr = input_data[inrow-1]; + + below_ptr = input_data[inrow+2]; + + + + /* Special case for first column: pretend column -1 is same as column 0 */ + + membersum = GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) + + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]); + + neighsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[1]) + + + GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[1]) + + + GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[2]) + + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[2]); + + neighsum += neighsum; + + neighsum += GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[2]) + + + GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[2]); + + membersum = membersum * memberscale + neighsum * neighscale; + + *outptr++ = (JSAMPLE) ((membersum + 32768) >> 16); + + inptr0 += 2; inptr1 += 2; above_ptr += 2; below_ptr += 2; + + + + for (colctr = output_cols - 2; colctr > 0; colctr--) { + + /* sum of pixels directly mapped to this output element */ + + membersum = GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) + + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]); + + /* sum of edge-neighbor pixels */ + + neighsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[1]) + + + GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[1]) + + + GETJSAMPLE(inptr0[-1]) + GETJSAMPLE(inptr0[2]) + + + GETJSAMPLE(inptr1[-1]) + GETJSAMPLE(inptr1[2]); + + /* The edge-neighbors count twice as much as corner-neighbors */ + + neighsum += neighsum; + + /* Add in the corner-neighbors */ + + neighsum += GETJSAMPLE(above_ptr[-1]) + GETJSAMPLE(above_ptr[2]) + + + GETJSAMPLE(below_ptr[-1]) + GETJSAMPLE(below_ptr[2]); + + /* form final output scaled up by 2^16 */ + + membersum = membersum * memberscale + neighsum * neighscale; + + /* round, descale and output it */ + + *outptr++ = (JSAMPLE) ((membersum + 32768) >> 16); + + inptr0 += 2; inptr1 += 2; above_ptr += 2; below_ptr += 2; + + } + + + + /* Special case for last column */ + + membersum = GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) + + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]); + + neighsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[1]) + + + GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[1]) + + + GETJSAMPLE(inptr0[-1]) + GETJSAMPLE(inptr0[1]) + + + GETJSAMPLE(inptr1[-1]) + GETJSAMPLE(inptr1[1]); + + neighsum += neighsum; + + neighsum += GETJSAMPLE(above_ptr[-1]) + GETJSAMPLE(above_ptr[1]) + + + GETJSAMPLE(below_ptr[-1]) + GETJSAMPLE(below_ptr[1]); + + membersum = membersum * memberscale + neighsum * neighscale; + + *outptr = (JSAMPLE) ((membersum + 32768) >> 16); + + + + inrow += 2; + + } + +} + + + + + +/* + + * Downsample pixel values of a single component. + + * This version handles the special case of a full-size component, + + * with smoothing. One row of context is required. + + */ + + + +METHODDEF void + +fullsize_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info *compptr, + + JSAMPARRAY input_data, JSAMPARRAY output_data) + +{ + + int outrow; + + JDIMENSION colctr; + + JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + + register JSAMPROW inptr, above_ptr, below_ptr, outptr; + + INT32 membersum, neighsum, memberscale, neighscale; + + int colsum, lastcolsum, nextcolsum; + + + + /* Expand input data enough to let all the output samples be generated + + * by the standard loop. Special-casing padded output would be more + + * efficient. + + */ + + expand_right_edge(input_data - 1, cinfo->max_v_samp_factor + 2, + + cinfo->image_width, output_cols); + + + + /* Each of the eight neighbor pixels contributes a fraction SF to the + + * smoothed pixel, while the main pixel contributes (1-8*SF). In order + + * to use integer arithmetic, these factors are multiplied by 2^16 = 65536. + + * Also recall that SF = smoothing_factor / 1024. + + */ + + + + memberscale = 65536L - cinfo->smoothing_factor * 512L; /* scaled 1-8*SF */ + + neighscale = cinfo->smoothing_factor * 64; /* scaled SF */ + + + + for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) { + + outptr = output_data[outrow]; + + inptr = input_data[outrow]; + + above_ptr = input_data[outrow-1]; + + below_ptr = input_data[outrow+1]; + + + + /* Special case for first column */ + + colsum = GETJSAMPLE(*above_ptr++) + GETJSAMPLE(*below_ptr++) + + + GETJSAMPLE(*inptr); + + membersum = GETJSAMPLE(*inptr++); + + nextcolsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(*below_ptr) + + + GETJSAMPLE(*inptr); + + neighsum = colsum + (colsum - membersum) + nextcolsum; + + membersum = membersum * memberscale + neighsum * neighscale; + + *outptr++ = (JSAMPLE) ((membersum + 32768) >> 16); + + lastcolsum = colsum; colsum = nextcolsum; + + + + for (colctr = output_cols - 2; colctr > 0; colctr--) { + + membersum = GETJSAMPLE(*inptr++); + + above_ptr++; below_ptr++; + + nextcolsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(*below_ptr) + + + GETJSAMPLE(*inptr); + + neighsum = lastcolsum + (colsum - membersum) + nextcolsum; + + membersum = membersum * memberscale + neighsum * neighscale; + + *outptr++ = (JSAMPLE) ((membersum + 32768) >> 16); + + lastcolsum = colsum; colsum = nextcolsum; + + } + + + + /* Special case for last column */ + + membersum = GETJSAMPLE(*inptr); + + neighsum = lastcolsum + (colsum - membersum) + colsum; + + membersum = membersum * memberscale + neighsum * neighscale; + + *outptr = (JSAMPLE) ((membersum + 32768) >> 16); + + + + } + +} + + + +#endif /* INPUT_SMOOTHING_SUPPORTED */ + + + + + +/* + + * Module initialization routine for downsampling. + + * Note that we must select a routine for each component. + + */ + + + +GLOBAL void + +jinit_downsampler (j_compress_ptr cinfo) + +{ + + my_downsample_ptr downsample; + + int ci; + + jpeg_component_info * compptr; + + boolean smoothok = TRUE; + + + + downsample = (my_downsample_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(my_downsampler)); + + cinfo->downsample = (struct jpeg_downsampler *) downsample; + + downsample->pub.start_pass = start_pass_downsample; + + downsample->pub.downsample = sep_downsample; + + downsample->pub.need_context_rows = FALSE; + + + + if (cinfo->CCIR601_sampling) + + ERREXIT(cinfo, JERR_CCIR601_NOTIMPL); + + + + /* Verify we can handle the sampling factors, and set up method pointers */ + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + if (compptr->h_samp_factor == cinfo->max_h_samp_factor && + + compptr->v_samp_factor == cinfo->max_v_samp_factor) { + +#ifdef INPUT_SMOOTHING_SUPPORTED + + if (cinfo->smoothing_factor) { + + downsample->methods[ci] = fullsize_smooth_downsample; + + downsample->pub.need_context_rows = TRUE; + + } else + +#endif + + downsample->methods[ci] = fullsize_downsample; + + } else if (compptr->h_samp_factor * 2 == cinfo->max_h_samp_factor && + + compptr->v_samp_factor == cinfo->max_v_samp_factor) { + + smoothok = FALSE; + + downsample->methods[ci] = h2v1_downsample; + + } else if (compptr->h_samp_factor * 2 == cinfo->max_h_samp_factor && + + compptr->v_samp_factor * 2 == cinfo->max_v_samp_factor) { + +#ifdef INPUT_SMOOTHING_SUPPORTED + + if (cinfo->smoothing_factor) { + + downsample->methods[ci] = h2v2_smooth_downsample; + + downsample->pub.need_context_rows = TRUE; + + } else + +#endif + + downsample->methods[ci] = h2v2_downsample; + + } else if ((cinfo->max_h_samp_factor % compptr->h_samp_factor) == 0 && + + (cinfo->max_v_samp_factor % compptr->v_samp_factor) == 0) { + + smoothok = FALSE; + + downsample->methods[ci] = int_downsample; + + } else + + ERREXIT(cinfo, JERR_FRACT_SAMPLE_NOTIMPL); + + } + + + +#ifdef INPUT_SMOOTHING_SUPPORTED + + if (cinfo->smoothing_factor && !smoothok) + + TRACEMS(cinfo, 0, JTRC_SMOOTH_NOTIMPL); + +#endif + +} + diff --git a/utils/roq2/jpeg/jctrans.c b/utils/roq2/jpeg/jctrans.c new file mode 100644 index 0000000..53afede --- /dev/null +++ b/utils/roq2/jpeg/jctrans.c @@ -0,0 +1,742 @@ +/* + + * jctrans.c + + * + + * Copyright (C) 1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains library routines for transcoding compression, + + * that is, writing raw DCT coefficient arrays to an output JPEG file. + + * The routines in jcapimin.c will also be needed by a transcoder. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + + + +/* Forward declarations */ + +LOCAL void transencode_master_selection + + JPP((j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays)); + +LOCAL void transencode_coef_controller + + JPP((j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays)); + + + + + +/* + + * Compression initialization for writing raw-coefficient data. + + * Before calling this, all parameters and a data destination must be set up. + + * Call jpeg_finish_compress() to actually write the data. + + * + + * The number of passed virtual arrays must match cinfo->num_components. + + * Note that the virtual arrays need not be filled or even realized at + + * the time write_coefficients is called; indeed, if the virtual arrays + + * were requested from this compression object's memory manager, they + + * typically will be realized during this routine and filled afterwards. + + */ + + + +GLOBAL void + +jpeg_write_coefficients (j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays) + +{ + + if (cinfo->global_state != CSTATE_START) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* Mark all tables to be written */ + + jpeg_suppress_tables(cinfo, FALSE); + + /* (Re)initialize error mgr and destination modules */ + + (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo); + + (*cinfo->dest->init_destination) (cinfo); + + /* Perform master selection of active modules */ + + transencode_master_selection(cinfo, coef_arrays); + + /* Wait for jpeg_finish_compress() call */ + + cinfo->next_scanline = 0; /* so jpeg_write_marker works */ + + cinfo->global_state = CSTATE_WRCOEFS; + +} + + + + + +/* + + * Initialize the compression object with default parameters, + + * then copy from the source object all parameters needed for lossless + + * transcoding. Parameters that can be varied without loss (such as + + * scan script and Huffman optimization) are left in their default states. + + */ + + + +GLOBAL void + +jpeg_copy_critical_parameters (j_decompress_ptr srcinfo, + + j_compress_ptr dstinfo) + +{ + + JQUANT_TBL ** qtblptr; + + jpeg_component_info *incomp, *outcomp; + + JQUANT_TBL *c_quant, *slot_quant; + + int tblno, ci, coefi; + + + + /* Safety check to ensure start_compress not called yet. */ + + if (dstinfo->global_state != CSTATE_START) + + ERREXIT1(dstinfo, JERR_BAD_STATE, dstinfo->global_state); + + /* Copy fundamental image dimensions */ + + dstinfo->image_width = srcinfo->image_width; + + dstinfo->image_height = srcinfo->image_height; + + dstinfo->input_components = srcinfo->num_components; + + dstinfo->in_color_space = srcinfo->jpeg_color_space; + + /* Initialize all parameters to default values */ + + jpeg_set_defaults(dstinfo); + + /* jpeg_set_defaults may choose wrong colorspace, eg YCbCr if input is RGB. + + * Fix it to get the right header markers for the image colorspace. + + */ + + jpeg_set_colorspace(dstinfo, srcinfo->jpeg_color_space); + + dstinfo->data_precision = srcinfo->data_precision; + + dstinfo->CCIR601_sampling = srcinfo->CCIR601_sampling; + + /* Copy the source's quantization tables. */ + + for (tblno = 0; tblno < NUM_QUANT_TBLS; tblno++) { + + if (srcinfo->quant_tbl_ptrs[tblno] != NULL) { + + qtblptr = & dstinfo->quant_tbl_ptrs[tblno]; + + if (*qtblptr == NULL) + + *qtblptr = jpeg_alloc_quant_table((j_common_ptr) dstinfo); + + MEMCOPY((*qtblptr)->quantval, + + srcinfo->quant_tbl_ptrs[tblno]->quantval, + + SIZEOF((*qtblptr)->quantval)); + + (*qtblptr)->sent_table = FALSE; + + } + + } + + /* Copy the source's per-component info. + + * Note we assume jpeg_set_defaults has allocated the dest comp_info array. + + */ + + dstinfo->num_components = srcinfo->num_components; + + if (dstinfo->num_components < 1 || dstinfo->num_components > MAX_COMPONENTS) + + ERREXIT2(dstinfo, JERR_COMPONENT_COUNT, dstinfo->num_components, + + MAX_COMPONENTS); + + for (ci = 0, incomp = srcinfo->comp_info, outcomp = dstinfo->comp_info; + + ci < dstinfo->num_components; ci++, incomp++, outcomp++) { + + outcomp->component_id = incomp->component_id; + + outcomp->h_samp_factor = incomp->h_samp_factor; + + outcomp->v_samp_factor = incomp->v_samp_factor; + + outcomp->quant_tbl_no = incomp->quant_tbl_no; + + /* Make sure saved quantization table for component matches the qtable + + * slot. If not, the input file re-used this qtable slot. + + * IJG encoder currently cannot duplicate this. + + */ + + tblno = outcomp->quant_tbl_no; + + if (tblno < 0 || tblno >= NUM_QUANT_TBLS || + + srcinfo->quant_tbl_ptrs[tblno] == NULL) + + ERREXIT1(dstinfo, JERR_NO_QUANT_TABLE, tblno); + + slot_quant = srcinfo->quant_tbl_ptrs[tblno]; + + c_quant = incomp->quant_table; + + if (c_quant != NULL) { + + for (coefi = 0; coefi < DCTSIZE2; coefi++) { + + if (c_quant->quantval[coefi] != slot_quant->quantval[coefi]) + + ERREXIT1(dstinfo, JERR_MISMATCHED_QUANT_TABLE, tblno); + + } + + } + + /* Note: we do not copy the source's Huffman table assignments; + + * instead we rely on jpeg_set_colorspace to have made a suitable choice. + + */ + + } + +} + + + + + +/* + + * Master selection of compression modules for transcoding. + + * This substitutes for jcinit.c's initialization of the full compressor. + + */ + + + +LOCAL void + +transencode_master_selection (j_compress_ptr cinfo, + + jvirt_barray_ptr * coef_arrays) + +{ + + /* Although we don't actually use input_components for transcoding, + + * jcmaster.c's initial_setup will complain if input_components is 0. + + */ + + cinfo->input_components = 1; + + /* Initialize master control (includes parameter checking/processing) */ + + jinit_c_master_control(cinfo, TRUE /* transcode only */); + + + + /* Entropy encoding: either Huffman or arithmetic coding. */ + + if (cinfo->arith_code) { + + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + + } else { + + if (cinfo->progressive_mode) { + +#ifdef C_PROGRESSIVE_SUPPORTED + + jinit_phuff_encoder(cinfo); + +#else + + ERREXIT(cinfo, JERR_NOT_COMPILED); + +#endif + + } else + + jinit_huff_encoder(cinfo); + + } + + + + /* We need a special coefficient buffer controller. */ + + transencode_coef_controller(cinfo, coef_arrays); + + + + jinit_marker_writer(cinfo); + + + + /* We can now tell the memory manager to allocate virtual arrays. */ + + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + + + /* Write the datastream header (SOI) immediately. + + * Frame and scan headers are postponed till later. + + * This lets application insert special markers after the SOI. + + */ + + (*cinfo->marker->write_file_header) (cinfo); + +} + + + + + +/* + + * The rest of this file is a special implementation of the coefficient + + * buffer controller. This is similar to jccoefct.c, but it handles only + + * output from presupplied virtual arrays. Furthermore, we generate any + + * dummy padding blocks on-the-fly rather than expecting them to be present + + * in the arrays. + + */ + + + +/* Private buffer controller object */ + + + +typedef struct { + + struct jpeg_c_coef_controller pub; /* public fields */ + + + + JDIMENSION iMCU_row_num; /* iMCU row # within image */ + + JDIMENSION mcu_ctr; /* counts MCUs processed in current row */ + + int MCU_vert_offset; /* counts MCU rows within iMCU row */ + + int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + + + /* Virtual block array for each component. */ + + jvirt_barray_ptr * whole_image; + + + + /* Workspace for constructing dummy blocks at right/bottom edges. */ + + JBLOCKROW dummy_buffer[C_MAX_BLOCKS_IN_MCU]; + +} my_coef_controller; + + + +typedef my_coef_controller * my_coef_ptr; + + + + + +LOCAL void + +start_iMCU_row (j_compress_ptr cinfo) + +/* Reset within-iMCU-row counters for a new row */ + +{ + + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + + + /* In an interleaved scan, an MCU row is the same as an iMCU row. + + * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. + + * But at the bottom of the image, process only what's left. + + */ + + if (cinfo->comps_in_scan > 1) { + + coef->MCU_rows_per_iMCU_row = 1; + + } else { + + if (coef->iMCU_row_num < (cinfo->total_iMCU_rows-1)) + + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; + + else + + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; + + } + + + + coef->mcu_ctr = 0; + + coef->MCU_vert_offset = 0; + +} + + + + + +/* + + * Initialize for a processing pass. + + */ + + + +METHODDEF void + +start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode) + +{ + + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + + + if (pass_mode != JBUF_CRANK_DEST) + + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + + + coef->iMCU_row_num = 0; + + start_iMCU_row(cinfo); + +} + + + + + +/* + + * Process some data. + + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + + * per call, ie, v_samp_factor block rows for each component in the scan. + + * The data is obtained from the virtual arrays and fed to the entropy coder. + + * Returns TRUE if the iMCU row is completed, FALSE if suspended. + + * + + * NB: input_buf is ignored; it is likely to be a NULL pointer. + + */ + + + +METHODDEF boolean + +compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) + +{ + + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + JDIMENSION MCU_col_num; /* index of current MCU within row */ + + JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; + + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + + int blkn, ci, xindex, yindex, yoffset, blockcnt; + + JDIMENSION start_col; + + JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN]; + + JBLOCKROW MCU_buffer[C_MAX_BLOCKS_IN_MCU]; + + JBLOCKROW buffer_ptr; + + jpeg_component_info *compptr; + + + + /* Align the virtual buffers for the components used in this scan. */ + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + + compptr = cinfo->cur_comp_info[ci]; + + buffer[ci] = (*cinfo->mem->access_virt_barray) + + ((j_common_ptr) cinfo, coef->whole_image[compptr->component_index], + + coef->iMCU_row_num * compptr->v_samp_factor, + + (JDIMENSION) compptr->v_samp_factor, FALSE); + + } + + + + /* Loop to process one whole iMCU row */ + + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + + yoffset++) { + + for (MCU_col_num = coef->mcu_ctr; MCU_col_num < cinfo->MCUs_per_row; + + MCU_col_num++) { + + /* Construct list of pointers to DCT blocks belonging to this MCU */ + + blkn = 0; /* index of current DCT block within MCU */ + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + + compptr = cinfo->cur_comp_info[ci]; + + start_col = MCU_col_num * compptr->MCU_width; + + blockcnt = (MCU_col_num < last_MCU_col) ? compptr->MCU_width + + : compptr->last_col_width; + + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + + if (coef->iMCU_row_num < last_iMCU_row || + + yindex+yoffset < compptr->last_row_height) { + + /* Fill in pointers to real blocks in this row */ + + buffer_ptr = buffer[ci][yindex+yoffset] + start_col; + + for (xindex = 0; xindex < blockcnt; xindex++) + + MCU_buffer[blkn++] = buffer_ptr++; + + } else { + + /* At bottom of image, need a whole row of dummy blocks */ + + xindex = 0; + + } + + /* Fill in any dummy blocks needed in this row. + + * Dummy blocks are filled in the same way as in jccoefct.c: + + * all zeroes in the AC entries, DC entries equal to previous + + * block's DC value. The init routine has already zeroed the + + * AC entries, so we need only set the DC entries correctly. + + */ + + for (; xindex < compptr->MCU_width; xindex++) { + + MCU_buffer[blkn] = coef->dummy_buffer[blkn]; + + MCU_buffer[blkn][0][0] = MCU_buffer[blkn-1][0][0]; + + blkn++; + + } + + } + + } + + /* Try to write the MCU. */ + + if (! (*cinfo->entropy->encode_mcu) (cinfo, MCU_buffer)) { + + /* Suspension forced; update state counters and exit */ + + coef->MCU_vert_offset = yoffset; + + coef->mcu_ctr = MCU_col_num; + + return FALSE; + + } + + } + + /* Completed an MCU row, but perhaps not an iMCU row */ + + coef->mcu_ctr = 0; + + } + + /* Completed the iMCU row, advance counters for next one */ + + coef->iMCU_row_num++; + + start_iMCU_row(cinfo); + + return TRUE; + +} + + + + + +/* + + * Initialize coefficient buffer controller. + + * + + * Each passed coefficient array must be the right size for that + + * coefficient: width_in_blocks wide and height_in_blocks high, + + * with unitheight at least v_samp_factor. + + */ + + + +LOCAL void + +transencode_coef_controller (j_compress_ptr cinfo, + + jvirt_barray_ptr * coef_arrays) + +{ + + my_coef_ptr coef; + + JBLOCKROW buffer; + + int i; + + + + coef = (my_coef_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(my_coef_controller)); + + cinfo->coef = (struct jpeg_c_coef_controller *) coef; + + coef->pub.start_pass = start_pass_coef; + + coef->pub.compress_data = compress_output; + + + + /* Save pointer to virtual arrays */ + + coef->whole_image = coef_arrays; + + + + /* Allocate and pre-zero space for dummy DCT blocks. */ + + buffer = (JBLOCKROW) + + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + + jzero_far((void FAR *) buffer, C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + + for (i = 0; i < C_MAX_BLOCKS_IN_MCU; i++) { + + coef->dummy_buffer[i] = buffer + i; + + } + +} + diff --git a/utils/roq2/jpeg/jdapimin.c b/utils/roq2/jpeg/jdapimin.c new file mode 100644 index 0000000..14542a2 --- /dev/null +++ b/utils/roq2/jpeg/jdapimin.c @@ -0,0 +1,796 @@ +/* + + * jdapimin.c + + * + + * Copyright (C) 1994-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains application interface code for the decompression half + + * of the JPEG library. These are the "minimum" API routines that may be + + * needed in either the normal full-decompression case or the + + * transcoding-only case. + + * + + * Most of the routines intended to be called directly by an application + + * are in this file or in jdapistd.c. But also see jcomapi.c for routines + + * shared by compression and decompression, and jdtrans.c for the transcoding + + * case. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + + + +/* + + * Initialization of a JPEG decompression object. + + * The error manager must already be set up (in case memory manager fails). + + */ + + + +GLOBAL void + +jpeg_create_decompress (j_decompress_ptr cinfo) + +{ + + int i; + + + + /* For debugging purposes, zero the whole master structure. + + * But error manager pointer is already there, so save and restore it. + + */ + + { + + struct jpeg_error_mgr * err = cinfo->err; + + MEMZERO(cinfo, SIZEOF(struct jpeg_decompress_struct)); + + cinfo->err = err; + + } + + cinfo->is_decompressor = TRUE; + + + + /* Initialize a memory manager instance for this object */ + + jinit_memory_mgr((j_common_ptr) cinfo); + + + + /* Zero out pointers to permanent structures. */ + + cinfo->progress = NULL; + + cinfo->src = NULL; + + + + for (i = 0; i < NUM_QUANT_TBLS; i++) + + cinfo->quant_tbl_ptrs[i] = NULL; + + + + for (i = 0; i < NUM_HUFF_TBLS; i++) { + + cinfo->dc_huff_tbl_ptrs[i] = NULL; + + cinfo->ac_huff_tbl_ptrs[i] = NULL; + + } + + + + /* Initialize marker processor so application can override methods + + * for COM, APPn markers before calling jpeg_read_header. + + */ + + jinit_marker_reader(cinfo); + + + + /* And initialize the overall input controller. */ + + jinit_input_controller(cinfo); + + + + /* OK, I'm ready */ + + cinfo->global_state = DSTATE_START; + +} + + + + + +/* + + * Destruction of a JPEG decompression object + + */ + + + +GLOBAL void + +jpeg_destroy_decompress (j_decompress_ptr cinfo) + +{ + + jpeg_destroy((j_common_ptr) cinfo); /* use common routine */ + +} + + + + + +/* + + * Abort processing of a JPEG decompression operation, + + * but don't destroy the object itself. + + */ + + + +GLOBAL void + +jpeg_abort_decompress (j_decompress_ptr cinfo) + +{ + + jpeg_abort((j_common_ptr) cinfo); /* use common routine */ + +} + + + + + +/* + + * Install a special processing method for COM or APPn markers. + + */ + + + +GLOBAL void + +jpeg_set_marker_processor (j_decompress_ptr cinfo, int marker_code, + + jpeg_marker_parser_method routine) + +{ + + if (marker_code == JPEG_COM) + + cinfo->marker->process_COM = routine; + + else if (marker_code >= JPEG_APP0 && marker_code <= JPEG_APP0+15) + + cinfo->marker->process_APPn[marker_code-JPEG_APP0] = routine; + + else + + ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, marker_code); + +} + + + + + +/* + + * Set default decompression parameters. + + */ + + + +LOCAL void + +default_decompress_parms (j_decompress_ptr cinfo) + +{ + + /* Guess the input colorspace, and set output colorspace accordingly. */ + + /* (Wish JPEG committee had provided a real way to specify this...) */ + + /* Note application may override our guesses. */ + + switch (cinfo->num_components) { + + case 1: + + cinfo->jpeg_color_space = JCS_GRAYSCALE; + + cinfo->out_color_space = JCS_GRAYSCALE; + + break; + + + + case 3: + + if (cinfo->saw_JFIF_marker) { + + cinfo->jpeg_color_space = JCS_YCbCr; /* JFIF implies YCbCr */ + + } else if (cinfo->saw_Adobe_marker) { + + switch (cinfo->Adobe_transform) { + + case 0: + + cinfo->jpeg_color_space = JCS_RGB; + + break; + + case 1: + + cinfo->jpeg_color_space = JCS_YCbCr; + + break; + + default: + + WARNMS1(cinfo, JWRN_ADOBE_XFORM, cinfo->Adobe_transform); + + cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */ + + break; + + } + + } else { + + /* Saw no special markers, try to guess from the component IDs */ + + int cid0 = cinfo->comp_info[0].component_id; + + int cid1 = cinfo->comp_info[1].component_id; + + int cid2 = cinfo->comp_info[2].component_id; + + + + if (cid0 == 1 && cid1 == 2 && cid2 == 3) + + cinfo->jpeg_color_space = JCS_YCbCr; /* assume JFIF w/out marker */ + + else if (cid0 == 82 && cid1 == 71 && cid2 == 66) + + cinfo->jpeg_color_space = JCS_RGB; /* ASCII 'R', 'G', 'B' */ + + else { + + TRACEMS3(cinfo, 1, JTRC_UNKNOWN_IDS, cid0, cid1, cid2); + + cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */ + + } + + } + + /* Always guess RGB is proper output colorspace. */ + + cinfo->out_color_space = JCS_RGB; + + break; + + + + case 4: + + if (cinfo->saw_Adobe_marker) { + + switch (cinfo->Adobe_transform) { + + case 0: + + cinfo->jpeg_color_space = JCS_CMYK; + + break; + + case 2: + + cinfo->jpeg_color_space = JCS_YCCK; + + break; + + default: + + WARNMS1(cinfo, JWRN_ADOBE_XFORM, cinfo->Adobe_transform); + + cinfo->jpeg_color_space = JCS_YCCK; /* assume it's YCCK */ + + break; + + } + + } else { + + /* No special markers, assume straight CMYK. */ + + cinfo->jpeg_color_space = JCS_CMYK; + + } + + cinfo->out_color_space = JCS_CMYK; + + break; + + + + default: + + cinfo->jpeg_color_space = JCS_UNKNOWN; + + cinfo->out_color_space = JCS_UNKNOWN; + + break; + + } + + + + /* Set defaults for other decompression parameters. */ + + cinfo->scale_num = 1; /* 1:1 scaling */ + + cinfo->scale_denom = 1; + + cinfo->output_gamma = 1.0; + + cinfo->buffered_image = FALSE; + + cinfo->raw_data_out = FALSE; + + cinfo->dct_method = JDCT_DEFAULT; + + cinfo->do_fancy_upsampling = TRUE; + + cinfo->do_block_smoothing = TRUE; + + cinfo->quantize_colors = FALSE; + + /* We set these in case application only sets quantize_colors. */ + + cinfo->dither_mode = JDITHER_FS; + +#ifdef QUANT_2PASS_SUPPORTED + + cinfo->two_pass_quantize = TRUE; + +#else + + cinfo->two_pass_quantize = FALSE; + +#endif + + cinfo->desired_number_of_colors = 256; + + cinfo->colormap = NULL; + + /* Initialize for no mode change in buffered-image mode. */ + + cinfo->enable_1pass_quant = FALSE; + + cinfo->enable_external_quant = FALSE; + + cinfo->enable_2pass_quant = FALSE; + +} + + + + + +/* + + * Decompression startup: read start of JPEG datastream to see what's there. + + * Need only initialize JPEG object and supply a data source before calling. + + * + + * This routine will read as far as the first SOS marker (ie, actual start of + + * compressed data), and will save all tables and parameters in the JPEG + + * object. It will also initialize the decompression parameters to default + + * values, and finally return JPEG_HEADER_OK. On return, the application may + + * adjust the decompression parameters and then call jpeg_start_decompress. + + * (Or, if the application only wanted to determine the image parameters, + + * the data need not be decompressed. In that case, call jpeg_abort or + + * jpeg_destroy to release any temporary space.) + + * If an abbreviated (tables only) datastream is presented, the routine will + + * return JPEG_HEADER_TABLES_ONLY upon reaching EOI. The application may then + + * re-use the JPEG object to read the abbreviated image datastream(s). + + * It is unnecessary (but OK) to call jpeg_abort in this case. + + * The JPEG_SUSPENDED return code only occurs if the data source module + + * requests suspension of the decompressor. In this case the application + + * should load more source data and then re-call jpeg_read_header to resume + + * processing. + + * If a non-suspending data source is used and require_image is TRUE, then the + + * return code need not be inspected since only JPEG_HEADER_OK is possible. + + * + + * This routine is now just a front end to jpeg_consume_input, with some + + * extra error checking. + + */ + + + +GLOBAL int + +jpeg_read_header (j_decompress_ptr cinfo, boolean require_image) + +{ + + int retcode; + + + + if (cinfo->global_state != DSTATE_START && + + cinfo->global_state != DSTATE_INHEADER) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + + + retcode = jpeg_consume_input(cinfo); + + + + switch (retcode) { + + case JPEG_REACHED_SOS: + + retcode = JPEG_HEADER_OK; + + break; + + case JPEG_REACHED_EOI: + + if (require_image) /* Complain if application wanted an image */ + + ERREXIT(cinfo, JERR_NO_IMAGE); + + /* Reset to start state; it would be safer to require the application to + + * call jpeg_abort, but we can't change it now for compatibility reasons. + + * A side effect is to free any temporary memory (there shouldn't be any). + + */ + + jpeg_abort((j_common_ptr) cinfo); /* sets state = DSTATE_START */ + + retcode = JPEG_HEADER_TABLES_ONLY; + + break; + + case JPEG_SUSPENDED: + + /* no work */ + + break; + + } + + + + return retcode; + +} + + + + + +/* + + * Consume data in advance of what the decompressor requires. + + * This can be called at any time once the decompressor object has + + * been created and a data source has been set up. + + * + + * This routine is essentially a state machine that handles a couple + + * of critical state-transition actions, namely initial setup and + + * transition from header scanning to ready-for-start_decompress. + + * All the actual input is done via the input controller's consume_input + + * method. + + */ + + + +GLOBAL int + +jpeg_consume_input (j_decompress_ptr cinfo) + +{ + + int retcode = JPEG_SUSPENDED; + + + + /* NB: every possible DSTATE value should be listed in this switch */ + + switch (cinfo->global_state) { + + case DSTATE_START: + + /* Start-of-datastream actions: reset appropriate modules */ + + (*cinfo->inputctl->reset_input_controller) (cinfo); + + /* Initialize application's data source module */ + + (*cinfo->src->init_source) (cinfo); + + cinfo->global_state = DSTATE_INHEADER; + + /*FALLTHROUGH*/ + + case DSTATE_INHEADER: + + retcode = (*cinfo->inputctl->consume_input) (cinfo); + + if (retcode == JPEG_REACHED_SOS) { /* Found SOS, prepare to decompress */ + + /* Set up default parameters based on header data */ + + default_decompress_parms(cinfo); + + /* Set global state: ready for start_decompress */ + + cinfo->global_state = DSTATE_READY; + + } + + break; + + case DSTATE_READY: + + /* Can't advance past first SOS until start_decompress is called */ + + retcode = JPEG_REACHED_SOS; + + break; + + case DSTATE_PRELOAD: + + case DSTATE_PRESCAN: + + case DSTATE_SCANNING: + + case DSTATE_RAW_OK: + + case DSTATE_BUFIMAGE: + + case DSTATE_BUFPOST: + + case DSTATE_STOPPING: + + retcode = (*cinfo->inputctl->consume_input) (cinfo); + + break; + + default: + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + } + + return retcode; + +} + + + + + +/* + + * Have we finished reading the input file? + + */ + + + +GLOBAL boolean + +jpeg_input_complete (j_decompress_ptr cinfo) + +{ + + /* Check for valid jpeg object */ + + if (cinfo->global_state < DSTATE_START || + + cinfo->global_state > DSTATE_STOPPING) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + return cinfo->inputctl->eoi_reached; + +} + + + + + +/* + + * Is there more than one scan? + + */ + + + +GLOBAL boolean + +jpeg_has_multiple_scans (j_decompress_ptr cinfo) + +{ + + /* Only valid after jpeg_read_header completes */ + + if (cinfo->global_state < DSTATE_READY || + + cinfo->global_state > DSTATE_STOPPING) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + return cinfo->inputctl->has_multiple_scans; + +} + + + + + +/* + + * Finish JPEG decompression. + + * + + * This will normally just verify the file trailer and release temp storage. + + * + + * Returns FALSE if suspended. The return value need be inspected only if + + * a suspending data source is used. + + */ + + + +GLOBAL boolean + +jpeg_finish_decompress (j_decompress_ptr cinfo) + +{ + + if ((cinfo->global_state == DSTATE_SCANNING || + + cinfo->global_state == DSTATE_RAW_OK) && ! cinfo->buffered_image) { + + /* Terminate final pass of non-buffered mode */ + + if (cinfo->output_scanline < cinfo->output_height) + + ERREXIT(cinfo, JERR_TOO_LITTLE_DATA); + + (*cinfo->master->finish_output_pass) (cinfo); + + cinfo->global_state = DSTATE_STOPPING; + + } else if (cinfo->global_state == DSTATE_BUFIMAGE) { + + /* Finishing after a buffered-image operation */ + + cinfo->global_state = DSTATE_STOPPING; + + } else if (cinfo->global_state != DSTATE_STOPPING) { + + /* STOPPING = repeat call after a suspension, anything else is error */ + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + } + + /* Read until EOI */ + + while (! cinfo->inputctl->eoi_reached) { + + if ((*cinfo->inputctl->consume_input) (cinfo) == JPEG_SUSPENDED) + + return FALSE; /* Suspend, come back later */ + + } + + /* Do final cleanup */ + + (*cinfo->src->term_source) (cinfo); + + /* We can use jpeg_abort to release memory and reset global_state */ + + jpeg_abort((j_common_ptr) cinfo); + + return TRUE; + +} + diff --git a/utils/roq2/jpeg/jdapistd.c b/utils/roq2/jpeg/jdapistd.c new file mode 100644 index 0000000..9f7b606 --- /dev/null +++ b/utils/roq2/jpeg/jdapistd.c @@ -0,0 +1,550 @@ +/* + + * jdapistd.c + + * + + * Copyright (C) 1994-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains application interface code for the decompression half + + * of the JPEG library. These are the "standard" API routines that are + + * used in the normal full-decompression case. They are not used by a + + * transcoding-only application. Note that if an application links in + + * jpeg_start_decompress, it will end up linking in the entire decompressor. + + * We thus must separate this file from jdapimin.c to avoid linking the + + * whole decompression library into a transcoder. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + + + +/* Forward declarations */ + +LOCAL boolean output_pass_setup JPP((j_decompress_ptr cinfo)); + + + + + +/* + + * Decompression initialization. + + * jpeg_read_header must be completed before calling this. + + * + + * If a multipass operating mode was selected, this will do all but the + + * last pass, and thus may take a great deal of time. + + * + + * Returns FALSE if suspended. The return value need be inspected only if + + * a suspending data source is used. + + */ + + + +GLOBAL boolean + +jpeg_start_decompress (j_decompress_ptr cinfo) + +{ + + if (cinfo->global_state == DSTATE_READY) { + + /* First call: initialize master control, select active modules */ + + jinit_master_decompress(cinfo); + + if (cinfo->buffered_image) { + + /* No more work here; expecting jpeg_start_output next */ + + cinfo->global_state = DSTATE_BUFIMAGE; + + return TRUE; + + } + + cinfo->global_state = DSTATE_PRELOAD; + + } + + if (cinfo->global_state == DSTATE_PRELOAD) { + + /* If file has multiple scans, absorb them all into the coef buffer */ + + if (cinfo->inputctl->has_multiple_scans) { + +#ifdef D_MULTISCAN_FILES_SUPPORTED + + for (;;) { + + int retcode; + + /* Call progress monitor hook if present */ + + if (cinfo->progress != NULL) + + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + + /* Absorb some more input */ + + retcode = (*cinfo->inputctl->consume_input) (cinfo); + + if (retcode == JPEG_SUSPENDED) + + return FALSE; + + if (retcode == JPEG_REACHED_EOI) + + break; + + /* Advance progress counter if appropriate */ + + if (cinfo->progress != NULL && + + (retcode == JPEG_ROW_COMPLETED || retcode == JPEG_REACHED_SOS)) { + + if (++cinfo->progress->pass_counter >= cinfo->progress->pass_limit) { + + /* jdmaster underestimated number of scans; ratchet up one scan */ + + cinfo->progress->pass_limit += (long) cinfo->total_iMCU_rows; + + } + + } + + } + +#else + + ERREXIT(cinfo, JERR_NOT_COMPILED); + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + + } + + cinfo->output_scan_number = cinfo->input_scan_number; + + } else if (cinfo->global_state != DSTATE_PRESCAN) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* Perform any dummy output passes, and set up for the final pass */ + + return output_pass_setup(cinfo); + +} + + + + + +/* + + * Set up for an output pass, and perform any dummy pass(es) needed. + + * Common subroutine for jpeg_start_decompress and jpeg_start_output. + + * Entry: global_state = DSTATE_PRESCAN only if previously suspended. + + * Exit: If done, returns TRUE and sets global_state for proper output mode. + + * If suspended, returns FALSE and sets global_state = DSTATE_PRESCAN. + + */ + + + +LOCAL boolean + +output_pass_setup (j_decompress_ptr cinfo) + +{ + + if (cinfo->global_state != DSTATE_PRESCAN) { + + /* First call: do pass setup */ + + (*cinfo->master->prepare_for_output_pass) (cinfo); + + cinfo->output_scanline = 0; + + cinfo->global_state = DSTATE_PRESCAN; + + } + + /* Loop over any required dummy passes */ + + while (cinfo->master->is_dummy_pass) { + +#ifdef QUANT_2PASS_SUPPORTED + + /* Crank through the dummy pass */ + + while (cinfo->output_scanline < cinfo->output_height) { + + JDIMENSION last_scanline; + + /* Call progress monitor hook if present */ + + if (cinfo->progress != NULL) { + + cinfo->progress->pass_counter = (long) cinfo->output_scanline; + + cinfo->progress->pass_limit = (long) cinfo->output_height; + + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + + } + + /* Process some data */ + + last_scanline = cinfo->output_scanline; + + (*cinfo->main->process_data) (cinfo, (JSAMPARRAY) NULL, + + &cinfo->output_scanline, (JDIMENSION) 0); + + if (cinfo->output_scanline == last_scanline) + + return FALSE; /* No progress made, must suspend */ + + } + + /* Finish up dummy pass, and set up for another one */ + + (*cinfo->master->finish_output_pass) (cinfo); + + (*cinfo->master->prepare_for_output_pass) (cinfo); + + cinfo->output_scanline = 0; + +#else + + ERREXIT(cinfo, JERR_NOT_COMPILED); + +#endif /* QUANT_2PASS_SUPPORTED */ + + } + + /* Ready for application to drive output pass through + + * jpeg_read_scanlines or jpeg_read_raw_data. + + */ + + cinfo->global_state = cinfo->raw_data_out ? DSTATE_RAW_OK : DSTATE_SCANNING; + + return TRUE; + +} + + + + + +/* + + * Read some scanlines of data from the JPEG decompressor. + + * + + * The return value will be the number of lines actually read. + + * This may be less than the number requested in several cases, + + * including bottom of image, data source suspension, and operating + + * modes that emit multiple scanlines at a time. + + * + + * Note: we warn about excess calls to jpeg_read_scanlines() since + + * this likely signals an application programmer error. However, + + * an oversize buffer (max_lines > scanlines remaining) is not an error. + + */ + + + +GLOBAL JDIMENSION + +jpeg_read_scanlines (j_decompress_ptr cinfo, JSAMPARRAY scanlines, + + JDIMENSION max_lines) + +{ + + JDIMENSION row_ctr; + + + + if (cinfo->global_state != DSTATE_SCANNING) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + if (cinfo->output_scanline >= cinfo->output_height) { + + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + + return 0; + + } + + + + /* Call progress monitor hook if present */ + + if (cinfo->progress != NULL) { + + cinfo->progress->pass_counter = (long) cinfo->output_scanline; + + cinfo->progress->pass_limit = (long) cinfo->output_height; + + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + + } + + + + /* Process some data */ + + row_ctr = 0; + + (*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, max_lines); + + cinfo->output_scanline += row_ctr; + + return row_ctr; + +} + + + + + +/* + + * Alternate entry point to read raw data. + + * Processes exactly one iMCU row per call, unless suspended. + + */ + + + +GLOBAL JDIMENSION + +jpeg_read_raw_data (j_decompress_ptr cinfo, JSAMPIMAGE data, + + JDIMENSION max_lines) + +{ + + JDIMENSION lines_per_iMCU_row; + + + + if (cinfo->global_state != DSTATE_RAW_OK) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + if (cinfo->output_scanline >= cinfo->output_height) { + + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + + return 0; + + } + + + + /* Call progress monitor hook if present */ + + if (cinfo->progress != NULL) { + + cinfo->progress->pass_counter = (long) cinfo->output_scanline; + + cinfo->progress->pass_limit = (long) cinfo->output_height; + + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + + } + + + + /* Verify that at least one iMCU row can be returned. */ + + lines_per_iMCU_row = cinfo->max_v_samp_factor * cinfo->min_DCT_scaled_size; + + if (max_lines < lines_per_iMCU_row) + + ERREXIT(cinfo, JERR_BUFFER_SIZE); + + + + /* Decompress directly into user's buffer. */ + + if (! (*cinfo->coef->decompress_data) (cinfo, data)) + + return 0; /* suspension forced, can do nothing more */ + + + + /* OK, we processed one iMCU row. */ + + cinfo->output_scanline += lines_per_iMCU_row; + + return lines_per_iMCU_row; + +} + + + + + +/* Additional entry points for buffered-image mode. */ + + + +#ifdef D_MULTISCAN_FILES_SUPPORTED + + + +/* + + * Initialize for an output pass in buffered-image mode. + + */ + + + +GLOBAL boolean + +jpeg_start_output (j_decompress_ptr cinfo, int scan_number) + +{ + + if (cinfo->global_state != DSTATE_BUFIMAGE && + + cinfo->global_state != DSTATE_PRESCAN) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* Limit scan number to valid range */ + + if (scan_number <= 0) + + scan_number = 1; + + if (cinfo->inputctl->eoi_reached && + + scan_number > cinfo->input_scan_number) + + scan_number = cinfo->input_scan_number; + + cinfo->output_scan_number = scan_number; + + /* Perform any dummy output passes, and set up for the real pass */ + + return output_pass_setup(cinfo); + +} + + + + + +/* + + * Finish up after an output pass in buffered-image mode. + + * + + * Returns FALSE if suspended. The return value need be inspected only if + + * a suspending data source is used. + + */ + + + +GLOBAL boolean + +jpeg_finish_output (j_decompress_ptr cinfo) + +{ + + if ((cinfo->global_state == DSTATE_SCANNING || + + cinfo->global_state == DSTATE_RAW_OK) && cinfo->buffered_image) { + + /* Terminate this pass. */ + + /* We do not require the whole pass to have been completed. */ + + (*cinfo->master->finish_output_pass) (cinfo); + + cinfo->global_state = DSTATE_BUFPOST; + + } else if (cinfo->global_state != DSTATE_BUFPOST) { + + /* BUFPOST = repeat call after a suspension, anything else is error */ + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + } + + /* Read markers looking for SOS or EOI */ + + while (cinfo->input_scan_number <= cinfo->output_scan_number && + + ! cinfo->inputctl->eoi_reached) { + + if ((*cinfo->inputctl->consume_input) (cinfo) == JPEG_SUSPENDED) + + return FALSE; /* Suspend, come back later */ + + } + + cinfo->global_state = DSTATE_BUFIMAGE; + + return TRUE; + +} + + + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + diff --git a/utils/roq2/jpeg/jdatadst.c b/utils/roq2/jpeg/jdatadst.c new file mode 100644 index 0000000..f9d2b87 --- /dev/null +++ b/utils/roq2/jpeg/jdatadst.c @@ -0,0 +1,302 @@ +/* + + * jdatadst.c + + * + + * Copyright (C) 1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains compression data destination routines for the case of + + * emitting JPEG data to a file (or any stdio stream). While these routines + + * are sufficient for most applications, some will want to use a different + + * destination manager. + + * IMPORTANT: we assume that fwrite() will correctly transcribe an array of + + * JOCTETs into 8-bit-wide elements on external storage. If char is wider + + * than 8 bits on your machine, you may need to do some tweaking. + + */ + + + +/* this is not a core library module, so it doesn't define JPEG_INTERNALS */ + +#include "jinclude.h" + +#include "jpeglib.h" + +#include "jerror.h" + + + + + +/* Expanded data destination object for stdio output */ + + + +typedef struct { + + struct jpeg_destination_mgr pub; /* public fields */ + + + + FILE * outfile; /* target stream */ + + JOCTET * buffer; /* start of buffer */ + +} my_destination_mgr; + + + +typedef my_destination_mgr * my_dest_ptr; + + + +#define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */ + + + + + +/* + + * Initialize destination --- called by jpeg_start_compress + + * before any data is actually written. + + */ + + + +METHODDEF void + +init_destination (j_compress_ptr cinfo) + +{ + + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + + + + /* Allocate the output buffer --- it will be released when done with image */ + + dest->buffer = (JOCTET *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + OUTPUT_BUF_SIZE * SIZEOF(JOCTET)); + + + + dest->pub.next_output_byte = dest->buffer; + + dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; + +} + + + + + +/* + + * Empty the output buffer --- called whenever buffer fills up. + + * + + * In typical applications, this should write the entire output buffer + + * (ignoring the current state of next_output_byte & free_in_buffer), + + * reset the pointer & count to the start of the buffer, and return TRUE + + * indicating that the buffer has been dumped. + + * + + * In applications that need to be able to suspend compression due to output + + * overrun, a FALSE return indicates that the buffer cannot be emptied now. + + * In this situation, the compressor will return to its caller (possibly with + + * an indication that it has not accepted all the supplied scanlines). The + + * application should resume compression after it has made more room in the + + * output buffer. Note that there are substantial restrictions on the use of + + * suspension --- see the documentation. + + * + + * When suspending, the compressor will back up to a convenient restart point + + * (typically the start of the current MCU). next_output_byte & free_in_buffer + + * indicate where the restart point will be if the current call returns FALSE. + + * Data beyond this point will be regenerated after resumption, so do not + + * write it out when emptying the buffer externally. + + */ + + + +METHODDEF boolean + +empty_output_buffer (j_compress_ptr cinfo) + +{ + + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + + + + if (JFWRITE(dest->outfile, dest->buffer, OUTPUT_BUF_SIZE) != + + (size_t) OUTPUT_BUF_SIZE) + + ERREXIT(cinfo, JERR_FILE_WRITE); + + + + dest->pub.next_output_byte = dest->buffer; + + dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; + + + + return TRUE; + +} + + + + + +/* + + * Terminate destination --- called by jpeg_finish_compress + + * after all data has been written. Usually needs to flush buffer. + + * + + * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding + + * application must deal with any cleanup that should happen even + + * for error exit. + + */ + + + +METHODDEF void + +term_destination (j_compress_ptr cinfo) + +{ + + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + + size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer; + + + + /* Write any data remaining in the buffer */ + + if (datacount > 0) { + + if (JFWRITE(dest->outfile, dest->buffer, datacount) != datacount) + + ERREXIT(cinfo, JERR_FILE_WRITE); + + } + + fflush(dest->outfile); + + /* Make sure we wrote the output file OK */ + + if (ferror(dest->outfile)) + + ERREXIT(cinfo, JERR_FILE_WRITE); + +} + + + + + +/* + + * Prepare for output to a stdio stream. + + * The caller must have already opened the stream, and is responsible + + * for closing it after finishing compression. + + */ + + + +GLOBAL void + +jpeg_stdio_dest (j_compress_ptr cinfo, FILE * outfile) + +{ + + my_dest_ptr dest; + + + + /* The destination object is made permanent so that multiple JPEG images + + * can be written to the same file without re-executing jpeg_stdio_dest. + + * This makes it dangerous to use this manager and a different destination + + * manager serially with the same JPEG object, because their private object + + * sizes may be different. Caveat programmer. + + */ + + if (cinfo->dest == NULL) { /* first time for this JPEG object? */ + + cinfo->dest = (struct jpeg_destination_mgr *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + + SIZEOF(my_destination_mgr)); + + } + + + + dest = (my_dest_ptr) cinfo->dest; + + dest->pub.init_destination = init_destination; + + dest->pub.empty_output_buffer = empty_output_buffer; + + dest->pub.term_destination = term_destination; + + dest->outfile = outfile; + +} + diff --git a/utils/roq2/jpeg/jdatasrc.c b/utils/roq2/jpeg/jdatasrc.c new file mode 100644 index 0000000..02b6559 --- /dev/null +++ b/utils/roq2/jpeg/jdatasrc.c @@ -0,0 +1,424 @@ +/* + + * jdatasrc.c + + * + + * Copyright (C) 1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains decompression data source routines for the case of + + * reading JPEG data from a file (or any stdio stream). While these routines + + * are sufficient for most applications, some will want to use a different + + * source manager. + + * IMPORTANT: we assume that fread() will correctly transcribe an array of + + * JOCTETs from 8-bit-wide elements on external storage. If char is wider + + * than 8 bits on your machine, you may need to do some tweaking. + + */ + + + +/* this is not a core library module, so it doesn't define JPEG_INTERNALS */ + +#include "jinclude.h" + +#include "jpeglib.h" + +#include "jerror.h" + + + + + +/* Expanded data source object for stdio input */ + + + +typedef struct { + + struct jpeg_source_mgr pub; /* public fields */ + + + + FILE * infile; /* source stream */ + + JOCTET * buffer; /* start of buffer */ + + boolean start_of_file; /* have we gotten any data yet? */ + +} my_source_mgr; + + + +typedef my_source_mgr * my_src_ptr; + + + +#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */ + + + + + +/* + + * Initialize source --- called by jpeg_read_header + + * before any data is actually read. + + */ + + + +METHODDEF void + +init_source (j_decompress_ptr cinfo) + +{ + + my_src_ptr src = (my_src_ptr) cinfo->src; + + + + /* We reset the empty-input-file flag for each image, + + * but we don't clear the input buffer. + + * This is correct behavior for reading a series of images from one source. + + */ + + src->start_of_file = TRUE; + +} + + + + + +/* + + * Fill the input buffer --- called whenever buffer is emptied. + + * + + * In typical applications, this should read fresh data into the buffer + + * (ignoring the current state of next_input_byte & bytes_in_buffer), + + * reset the pointer & count to the start of the buffer, and return TRUE + + * indicating that the buffer has been reloaded. It is not necessary to + + * fill the buffer entirely, only to obtain at least one more byte. + + * + + * There is no such thing as an EOF return. If the end of the file has been + + * reached, the routine has a choice of ERREXIT() or inserting fake data into + + * the buffer. In most cases, generating a warning message and inserting a + + * fake EOI marker is the best course of action --- this will allow the + + * decompressor to output however much of the image is there. However, + + * the resulting error message is misleading if the real problem is an empty + + * input file, so we handle that case specially. + + * + + * In applications that need to be able to suspend compression due to input + + * not being available yet, a FALSE return indicates that no more data can be + + * obtained right now, but more may be forthcoming later. In this situation, + + * the decompressor will return to its caller (with an indication of the + + * number of scanlines it has read, if any). The application should resume + + * decompression after it has loaded more data into the input buffer. Note + + * that there are substantial restrictions on the use of suspension --- see + + * the documentation. + + * + + * When suspending, the decompressor will back up to a convenient restart point + + * (typically the start of the current MCU). next_input_byte & bytes_in_buffer + + * indicate where the restart point will be if the current call returns FALSE. + + * Data beyond this point must be rescanned after resumption, so move it to + + * the front of the buffer rather than discarding it. + + */ + + + +METHODDEF boolean + +fill_input_buffer (j_decompress_ptr cinfo) + +{ + + my_src_ptr src = (my_src_ptr) cinfo->src; + + size_t nbytes; + + + + nbytes = JFREAD(src->infile, src->buffer, INPUT_BUF_SIZE); + + + + if (nbytes <= 0) { + + if (src->start_of_file) /* Treat empty input file as fatal error */ + + ERREXIT(cinfo, JERR_INPUT_EMPTY); + + WARNMS(cinfo, JWRN_JPEG_EOF); + + /* Insert a fake EOI marker */ + + src->buffer[0] = (JOCTET) 0xFF; + + src->buffer[1] = (JOCTET) JPEG_EOI; + + nbytes = 2; + + } + + + + src->pub.next_input_byte = src->buffer; + + src->pub.bytes_in_buffer = nbytes; + + src->start_of_file = FALSE; + + + + return TRUE; + +} + + + + + +/* + + * Skip data --- used to skip over a potentially large amount of + + * uninteresting data (such as an APPn marker). + + * + + * Writers of suspendable-input applications must note that skip_input_data + + * is not granted the right to give a suspension return. If the skip extends + + * beyond the data currently in the buffer, the buffer can be marked empty so + + * that the next read will cause a fill_input_buffer call that can suspend. + + * Arranging for additional bytes to be discarded before reloading the input + + * buffer is the application writer's problem. + + */ + + + +METHODDEF void + +skip_input_data (j_decompress_ptr cinfo, long num_bytes) + +{ + + my_src_ptr src = (my_src_ptr) cinfo->src; + + + + /* Just a dumb implementation for now. Could use fseek() except + + * it doesn't work on pipes. Not clear that being smart is worth + + * any trouble anyway --- large skips are infrequent. + + */ + + if (num_bytes > 0) { + + while (num_bytes > (long) src->pub.bytes_in_buffer) { + + num_bytes -= (long) src->pub.bytes_in_buffer; + + (void) fill_input_buffer(cinfo); + + /* note we assume that fill_input_buffer will never return FALSE, + + * so suspension need not be handled. + + */ + + } + + src->pub.next_input_byte += (size_t) num_bytes; + + src->pub.bytes_in_buffer -= (size_t) num_bytes; + + } + +} + + + + + +/* + + * An additional method that can be provided by data source modules is the + + * resync_to_restart method for error recovery in the presence of RST markers. + + * For the moment, this source module just uses the default resync method + + * provided by the JPEG library. That method assumes that no backtracking + + * is possible. + + */ + + + + + +/* + + * Terminate source --- called by jpeg_finish_decompress + + * after all data has been read. Often a no-op. + + * + + * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding + + * application must deal with any cleanup that should happen even + + * for error exit. + + */ + + + +METHODDEF void + +term_source (j_decompress_ptr cinfo) + +{ + + /* no work necessary here */ + +} + + + + + +/* + + * Prepare for input from a stdio stream. + + * The caller must have already opened the stream, and is responsible + + * for closing it after finishing decompression. + + */ + + + +GLOBAL void + +jpeg_stdio_src (j_decompress_ptr cinfo, FILE * infile) + +{ + + my_src_ptr src; + + + + /* The source object and input buffer are made permanent so that a series + + * of JPEG images can be read from the same file by calling jpeg_stdio_src + + * only before the first one. (If we discarded the buffer at the end of + + * one image, we'd likely lose the start of the next one.) + + * This makes it unsafe to use this manager and a different source + + * manager serially with the same JPEG object. Caveat programmer. + + */ + + if (cinfo->src == NULL) { /* first time for this JPEG object? */ + + cinfo->src = (struct jpeg_source_mgr *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + + SIZEOF(my_source_mgr)); + + src = (my_src_ptr) cinfo->src; + + src->buffer = (JOCTET *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + + INPUT_BUF_SIZE * SIZEOF(JOCTET)); + + } + + + + src = (my_src_ptr) cinfo->src; + + src->pub.init_source = init_source; + + src->pub.fill_input_buffer = fill_input_buffer; + + src->pub.skip_input_data = skip_input_data; + + src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ + + src->pub.term_source = term_source; + + src->infile = infile; + + src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ + + src->pub.next_input_byte = NULL; /* until buffer loaded */ + +} + diff --git a/utils/roq2/jpeg/jdcoefct.c b/utils/roq2/jpeg/jdcoefct.c new file mode 100644 index 0000000..ac48137 --- /dev/null +++ b/utils/roq2/jpeg/jdcoefct.c @@ -0,0 +1,1450 @@ +/* + + * jdcoefct.c + + * + + * Copyright (C) 1994-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains the coefficient buffer controller for decompression. + + * This controller is the top level of the JPEG decompressor proper. + + * The coefficient buffer lies between entropy decoding and inverse-DCT steps. + + * + + * In buffered-image mode, this controller is the interface between + + * input-oriented processing and output-oriented processing. + + * Also, the input side (only) is used when reading a file for transcoding. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + +/* Block smoothing is only applicable for progressive JPEG, so: */ + +#ifndef D_PROGRESSIVE_SUPPORTED + +#undef BLOCK_SMOOTHING_SUPPORTED + +#endif + + + +/* Private buffer controller object */ + + + +typedef struct { + + struct jpeg_d_coef_controller pub; /* public fields */ + + + + /* These variables keep track of the current location of the input side. */ + + /* cinfo->input_iMCU_row is also used for this. */ + + JDIMENSION MCU_ctr; /* counts MCUs processed in current row */ + + int MCU_vert_offset; /* counts MCU rows within iMCU row */ + + int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + + + /* The output side's location is represented by cinfo->output_iMCU_row. */ + + + + /* In single-pass modes, it's sufficient to buffer just one MCU. + + * We allocate a workspace of D_MAX_BLOCKS_IN_MCU coefficient blocks, + + * and let the entropy decoder write into that workspace each time. + + * (On 80x86, the workspace is FAR even though it's not really very big; + + * this is to keep the module interfaces unchanged when a large coefficient + + * buffer is necessary.) + + * In multi-pass modes, this array points to the current MCU's blocks + + * within the virtual arrays; it is used only by the input side. + + */ + + JBLOCKROW MCU_buffer[D_MAX_BLOCKS_IN_MCU]; + + + +#ifdef D_MULTISCAN_FILES_SUPPORTED + + /* In multi-pass modes, we need a virtual block array for each component. */ + + jvirt_barray_ptr whole_image[MAX_COMPONENTS]; + +#endif + + + +#ifdef BLOCK_SMOOTHING_SUPPORTED + + /* When doing block smoothing, we latch coefficient Al values here */ + + int * coef_bits_latch; + +#define SAVED_COEFS 6 /* we save coef_bits[0..5] */ + +#endif + +} my_coef_controller; + + + +typedef my_coef_controller * my_coef_ptr; + + + +/* Forward declarations */ + +METHODDEF int decompress_onepass + + JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); + +#ifdef D_MULTISCAN_FILES_SUPPORTED + +METHODDEF int decompress_data + + JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); + +#endif + +#ifdef BLOCK_SMOOTHING_SUPPORTED + +LOCAL boolean smoothing_ok JPP((j_decompress_ptr cinfo)); + +METHODDEF int decompress_smooth_data + + JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); + +#endif + + + + + +LOCAL void + +start_iMCU_row (j_decompress_ptr cinfo) + +/* Reset within-iMCU-row counters for a new row (input side) */ + +{ + + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + + + /* In an interleaved scan, an MCU row is the same as an iMCU row. + + * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. + + * But at the bottom of the image, process only what's left. + + */ + + if (cinfo->comps_in_scan > 1) { + + coef->MCU_rows_per_iMCU_row = 1; + + } else { + + if (cinfo->input_iMCU_row < (cinfo->total_iMCU_rows-1)) + + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; + + else + + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; + + } + + + + coef->MCU_ctr = 0; + + coef->MCU_vert_offset = 0; + +} + + + + + +/* + + * Initialize for an input processing pass. + + */ + + + +METHODDEF void + +start_input_pass (j_decompress_ptr cinfo) + +{ + + cinfo->input_iMCU_row = 0; + + start_iMCU_row(cinfo); + +} + + + + + +/* + + * Initialize for an output processing pass. + + */ + + + +METHODDEF void + +start_output_pass (j_decompress_ptr cinfo) + +{ + +#ifdef BLOCK_SMOOTHING_SUPPORTED + + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + + + /* If multipass, check to see whether to use block smoothing on this pass */ + + if (coef->pub.coef_arrays != NULL) { + + if (cinfo->do_block_smoothing && smoothing_ok(cinfo)) + + coef->pub.decompress_data = decompress_smooth_data; + + else + + coef->pub.decompress_data = decompress_data; + + } + +#endif + + cinfo->output_iMCU_row = 0; + +} + + + + + +/* + + * Decompress and return some data in the single-pass case. + + * Always attempts to emit one fully interleaved MCU row ("iMCU" row). + + * Input and output must run in lockstep since we have only a one-MCU buffer. + + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + + * + + * NB: output_buf contains a plane for each component in image. + + * For single pass, this is the same as the components in the scan. + + */ + + + +METHODDEF int + +decompress_onepass (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) + +{ + + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + JDIMENSION MCU_col_num; /* index of current MCU within row */ + + JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; + + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + + int blkn, ci, xindex, yindex, yoffset, useful_width; + + JSAMPARRAY output_ptr; + + JDIMENSION start_col, output_col; + + jpeg_component_info *compptr; + + inverse_DCT_method_ptr inverse_DCT; + + + + /* Loop to process as much as one whole iMCU row */ + + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + + yoffset++) { + + for (MCU_col_num = coef->MCU_ctr; MCU_col_num <= last_MCU_col; + + MCU_col_num++) { + + /* Try to fetch an MCU. Entropy decoder expects buffer to be zeroed. */ + + jzero_far((void FAR *) coef->MCU_buffer[0], + + (size_t) (cinfo->blocks_in_MCU * SIZEOF(JBLOCK))); + + if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) { + + /* Suspension forced; update state counters and exit */ + + coef->MCU_vert_offset = yoffset; + + coef->MCU_ctr = MCU_col_num; + + return JPEG_SUSPENDED; + + } + + /* Determine where data should go in output_buf and do the IDCT thing. + + * We skip dummy blocks at the right and bottom edges (but blkn gets + + * incremented past them!). Note the inner loop relies on having + + * allocated the MCU_buffer[] blocks sequentially. + + */ + + blkn = 0; /* index of current DCT block within MCU */ + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + + compptr = cinfo->cur_comp_info[ci]; + + /* Don't bother to IDCT an uninteresting component. */ + + if (! compptr->component_needed) { + + blkn += compptr->MCU_blocks; + + continue; + + } + + inverse_DCT = cinfo->idct->inverse_DCT[compptr->component_index]; + + useful_width = (MCU_col_num < last_MCU_col) ? compptr->MCU_width + + : compptr->last_col_width; + + output_ptr = output_buf[ci] + yoffset * compptr->DCT_scaled_size; + + start_col = MCU_col_num * compptr->MCU_sample_width; + + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + + if (cinfo->input_iMCU_row < last_iMCU_row || + + yoffset+yindex < compptr->last_row_height) { + + output_col = start_col; + + for (xindex = 0; xindex < useful_width; xindex++) { + + (*inverse_DCT) (cinfo, compptr, + + (JCOEFPTR) coef->MCU_buffer[blkn+xindex], + + output_ptr, output_col); + + output_col += compptr->DCT_scaled_size; + + } + + } + + blkn += compptr->MCU_width; + + output_ptr += compptr->DCT_scaled_size; + + } + + } + + } + + /* Completed an MCU row, but perhaps not an iMCU row */ + + coef->MCU_ctr = 0; + + } + + /* Completed the iMCU row, advance counters for next one */ + + cinfo->output_iMCU_row++; + + if (++(cinfo->input_iMCU_row) < cinfo->total_iMCU_rows) { + + start_iMCU_row(cinfo); + + return JPEG_ROW_COMPLETED; + + } + + /* Completed the scan */ + + (*cinfo->inputctl->finish_input_pass) (cinfo); + + return JPEG_SCAN_COMPLETED; + +} + + + + + +/* + + * Dummy consume-input routine for single-pass operation. + + */ + + + +METHODDEF int + +dummy_consume_data (j_decompress_ptr cinfo) + +{ + + return JPEG_SUSPENDED; /* Always indicate nothing was done */ + +} + + + + + +#ifdef D_MULTISCAN_FILES_SUPPORTED + + + +/* + + * Consume input data and store it in the full-image coefficient buffer. + + * We read as much as one fully interleaved MCU row ("iMCU" row) per call, + + * ie, v_samp_factor block rows for each component in the scan. + + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + + */ + + + +METHODDEF int + +consume_data (j_decompress_ptr cinfo) + +{ + + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + JDIMENSION MCU_col_num; /* index of current MCU within row */ + + int blkn, ci, xindex, yindex, yoffset; + + JDIMENSION start_col; + + JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN]; + + JBLOCKROW buffer_ptr; + + jpeg_component_info *compptr; + + + + /* Align the virtual buffers for the components used in this scan. */ + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + + compptr = cinfo->cur_comp_info[ci]; + + buffer[ci] = (*cinfo->mem->access_virt_barray) + + ((j_common_ptr) cinfo, coef->whole_image[compptr->component_index], + + cinfo->input_iMCU_row * compptr->v_samp_factor, + + (JDIMENSION) compptr->v_samp_factor, TRUE); + + /* Note: entropy decoder expects buffer to be zeroed, + + * but this is handled automatically by the memory manager + + * because we requested a pre-zeroed array. + + */ + + } + + + + /* Loop to process one whole iMCU row */ + + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + + yoffset++) { + + for (MCU_col_num = coef->MCU_ctr; MCU_col_num < cinfo->MCUs_per_row; + + MCU_col_num++) { + + /* Construct list of pointers to DCT blocks belonging to this MCU */ + + blkn = 0; /* index of current DCT block within MCU */ + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + + compptr = cinfo->cur_comp_info[ci]; + + start_col = MCU_col_num * compptr->MCU_width; + + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + + buffer_ptr = buffer[ci][yindex+yoffset] + start_col; + + for (xindex = 0; xindex < compptr->MCU_width; xindex++) { + + coef->MCU_buffer[blkn++] = buffer_ptr++; + + } + + } + + } + + /* Try to fetch the MCU. */ + + if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) { + + /* Suspension forced; update state counters and exit */ + + coef->MCU_vert_offset = yoffset; + + coef->MCU_ctr = MCU_col_num; + + return JPEG_SUSPENDED; + + } + + } + + /* Completed an MCU row, but perhaps not an iMCU row */ + + coef->MCU_ctr = 0; + + } + + /* Completed the iMCU row, advance counters for next one */ + + if (++(cinfo->input_iMCU_row) < cinfo->total_iMCU_rows) { + + start_iMCU_row(cinfo); + + return JPEG_ROW_COMPLETED; + + } + + /* Completed the scan */ + + (*cinfo->inputctl->finish_input_pass) (cinfo); + + return JPEG_SCAN_COMPLETED; + +} + + + + + +/* + + * Decompress and return some data in the multi-pass case. + + * Always attempts to emit one fully interleaved MCU row ("iMCU" row). + + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + + * + + * NB: output_buf contains a plane for each component in image. + + */ + + + +METHODDEF int + +decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) + +{ + + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + + JDIMENSION block_num; + + int ci, block_row, block_rows; + + JBLOCKARRAY buffer; + + JBLOCKROW buffer_ptr; + + JSAMPARRAY output_ptr; + + JDIMENSION output_col; + + jpeg_component_info *compptr; + + inverse_DCT_method_ptr inverse_DCT; + + + + /* Force some input to be done if we are getting ahead of the input. */ + + while (cinfo->input_scan_number < cinfo->output_scan_number || + + (cinfo->input_scan_number == cinfo->output_scan_number && + + cinfo->input_iMCU_row <= cinfo->output_iMCU_row)) { + + if ((*cinfo->inputctl->consume_input)(cinfo) == JPEG_SUSPENDED) + + return JPEG_SUSPENDED; + + } + + + + /* OK, output from the virtual arrays. */ + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + /* Don't bother to IDCT an uninteresting component. */ + + if (! compptr->component_needed) + + continue; + + /* Align the virtual buffer for this component. */ + + buffer = (*cinfo->mem->access_virt_barray) + + ((j_common_ptr) cinfo, coef->whole_image[ci], + + cinfo->output_iMCU_row * compptr->v_samp_factor, + + (JDIMENSION) compptr->v_samp_factor, FALSE); + + /* Count non-dummy DCT block rows in this iMCU row. */ + + if (cinfo->output_iMCU_row < last_iMCU_row) + + block_rows = compptr->v_samp_factor; + + else { + + /* NB: can't use last_row_height here; it is input-side-dependent! */ + + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + + if (block_rows == 0) block_rows = compptr->v_samp_factor; + + } + + inverse_DCT = cinfo->idct->inverse_DCT[ci]; + + output_ptr = output_buf[ci]; + + /* Loop over all DCT blocks to be processed. */ + + for (block_row = 0; block_row < block_rows; block_row++) { + + buffer_ptr = buffer[block_row]; + + output_col = 0; + + for (block_num = 0; block_num < compptr->width_in_blocks; block_num++) { + + (*inverse_DCT) (cinfo, compptr, (JCOEFPTR) buffer_ptr, + + output_ptr, output_col); + + buffer_ptr++; + + output_col += compptr->DCT_scaled_size; + + } + + output_ptr += compptr->DCT_scaled_size; + + } + + } + + + + if (++(cinfo->output_iMCU_row) < cinfo->total_iMCU_rows) + + return JPEG_ROW_COMPLETED; + + return JPEG_SCAN_COMPLETED; + +} + + + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + + + + + +#ifdef BLOCK_SMOOTHING_SUPPORTED + + + +/* + + * This code applies interblock smoothing as described by section K.8 + + * of the JPEG standard: the first 5 AC coefficients are estimated from + + * the DC values of a DCT block and its 8 neighboring blocks. + + * We apply smoothing only for progressive JPEG decoding, and only if + + * the coefficients it can estimate are not yet known to full precision. + + */ + + + +/* + + * Determine whether block smoothing is applicable and safe. + + * We also latch the current states of the coef_bits[] entries for the + + * AC coefficients; otherwise, if the input side of the decompressor + + * advances into a new scan, we might think the coefficients are known + + * more accurately than they really are. + + */ + + + +LOCAL boolean + +smoothing_ok (j_decompress_ptr cinfo) + +{ + + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + boolean smoothing_useful = FALSE; + + int ci, coefi; + + jpeg_component_info *compptr; + + JQUANT_TBL * qtable; + + int * coef_bits; + + int * coef_bits_latch; + + + + if (! cinfo->progressive_mode || cinfo->coef_bits == NULL) + + return FALSE; + + + + /* Allocate latch area if not already done */ + + if (coef->coef_bits_latch == NULL) + + coef->coef_bits_latch = (int *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + cinfo->num_components * + + (SAVED_COEFS * SIZEOF(int))); + + coef_bits_latch = coef->coef_bits_latch; + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + /* All components' quantization values must already be latched. */ + + if ((qtable = compptr->quant_table) == NULL) + + return FALSE; + + /* Verify DC & first 5 AC quantizers are nonzero to avoid zero-divide. */ + + for (coefi = 0; coefi <= 5; coefi++) { + + if (qtable->quantval[coefi] == 0) + + return FALSE; + + } + + /* DC values must be at least partly known for all components. */ + + coef_bits = cinfo->coef_bits[ci]; + + if (coef_bits[0] < 0) + + return FALSE; + + /* Block smoothing is helpful if some AC coefficients remain inaccurate. */ + + for (coefi = 1; coefi <= 5; coefi++) { + + coef_bits_latch[coefi] = coef_bits[coefi]; + + if (coef_bits[coefi] != 0) + + smoothing_useful = TRUE; + + } + + coef_bits_latch += SAVED_COEFS; + + } + + + + return smoothing_useful; + +} + + + + + +/* + + * Variant of decompress_data for use when doing block smoothing. + + */ + + + +METHODDEF int + +decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) + +{ + + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + + JDIMENSION block_num, last_block_column; + + int ci, block_row, block_rows, access_rows; + + JBLOCKARRAY buffer; + + JBLOCKROW buffer_ptr, prev_block_row, next_block_row; + + JSAMPARRAY output_ptr; + + JDIMENSION output_col; + + jpeg_component_info *compptr; + + inverse_DCT_method_ptr inverse_DCT; + + boolean first_row, last_row; + + JBLOCK workspace; + + int *coef_bits; + + JQUANT_TBL *quanttbl; + + INT32 Q00,Q01,Q02,Q10,Q11,Q20, num; + + int DC1,DC2,DC3,DC4,DC5,DC6,DC7,DC8,DC9; + + int Al, pred; + + + + /* Force some input to be done if we are getting ahead of the input. */ + + while (cinfo->input_scan_number <= cinfo->output_scan_number && + + ! cinfo->inputctl->eoi_reached) { + + if (cinfo->input_scan_number == cinfo->output_scan_number) { + + /* If input is working on current scan, we ordinarily want it to + + * have completed the current row. But if input scan is DC, + + * we want it to keep one row ahead so that next block row's DC + + * values are up to date. + + */ + + JDIMENSION delta = (cinfo->Ss == 0) ? 1 : 0; + + if (cinfo->input_iMCU_row > cinfo->output_iMCU_row+delta) + + break; + + } + + if ((*cinfo->inputctl->consume_input)(cinfo) == JPEG_SUSPENDED) + + return JPEG_SUSPENDED; + + } + + + + /* OK, output from the virtual arrays. */ + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + /* Don't bother to IDCT an uninteresting component. */ + + if (! compptr->component_needed) + + continue; + + /* Count non-dummy DCT block rows in this iMCU row. */ + + if (cinfo->output_iMCU_row < last_iMCU_row) { + + block_rows = compptr->v_samp_factor; + + access_rows = block_rows * 2; /* this and next iMCU row */ + + last_row = FALSE; + + } else { + + /* NB: can't use last_row_height here; it is input-side-dependent! */ + + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + + if (block_rows == 0) block_rows = compptr->v_samp_factor; + + access_rows = block_rows; /* this iMCU row only */ + + last_row = TRUE; + + } + + /* Align the virtual buffer for this component. */ + + if (cinfo->output_iMCU_row > 0) { + + access_rows += compptr->v_samp_factor; /* prior iMCU row too */ + + buffer = (*cinfo->mem->access_virt_barray) + + ((j_common_ptr) cinfo, coef->whole_image[ci], + + (cinfo->output_iMCU_row - 1) * compptr->v_samp_factor, + + (JDIMENSION) access_rows, FALSE); + + buffer += compptr->v_samp_factor; /* point to current iMCU row */ + + first_row = FALSE; + + } else { + + buffer = (*cinfo->mem->access_virt_barray) + + ((j_common_ptr) cinfo, coef->whole_image[ci], + + (JDIMENSION) 0, (JDIMENSION) access_rows, FALSE); + + first_row = TRUE; + + } + + /* Fetch component-dependent info */ + + coef_bits = coef->coef_bits_latch + (ci * SAVED_COEFS); + + quanttbl = compptr->quant_table; + + Q00 = quanttbl->quantval[0]; + + Q01 = quanttbl->quantval[1]; + + Q10 = quanttbl->quantval[2]; + + Q20 = quanttbl->quantval[3]; + + Q11 = quanttbl->quantval[4]; + + Q02 = quanttbl->quantval[5]; + + inverse_DCT = cinfo->idct->inverse_DCT[ci]; + + output_ptr = output_buf[ci]; + + /* Loop over all DCT blocks to be processed. */ + + for (block_row = 0; block_row < block_rows; block_row++) { + + buffer_ptr = buffer[block_row]; + + if (first_row && block_row == 0) + + prev_block_row = buffer_ptr; + + else + + prev_block_row = buffer[block_row-1]; + + if (last_row && block_row == block_rows-1) + + next_block_row = buffer_ptr; + + else + + next_block_row = buffer[block_row+1]; + + /* We fetch the surrounding DC values using a sliding-register approach. + + * Initialize all nine here so as to do the right thing on narrow pics. + + */ + + DC1 = DC2 = DC3 = (int) prev_block_row[0][0]; + + DC4 = DC5 = DC6 = (int) buffer_ptr[0][0]; + + DC7 = DC8 = DC9 = (int) next_block_row[0][0]; + + output_col = 0; + + last_block_column = compptr->width_in_blocks - 1; + + for (block_num = 0; block_num <= last_block_column; block_num++) { + + /* Fetch current DCT block into workspace so we can modify it. */ + + jcopy_block_row(buffer_ptr, (JBLOCKROW) workspace, (JDIMENSION) 1); + + /* Update DC values */ + + if (block_num < last_block_column) { + + DC3 = (int) prev_block_row[1][0]; + + DC6 = (int) buffer_ptr[1][0]; + + DC9 = (int) next_block_row[1][0]; + + } + + /* Compute coefficient estimates per K.8. + + * An estimate is applied only if coefficient is still zero, + + * and is not known to be fully accurate. + + */ + + /* AC01 */ + + if ((Al=coef_bits[1]) != 0 && workspace[1] == 0) { + + num = 36 * Q00 * (DC4 - DC6); + + if (num >= 0) { + + pred = (int) (((Q01<<7) + num) / (Q01<<8)); + + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + + pred = (int) (((Q10<<7) + num) / (Q10<<8)); + + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + + pred = (int) (((Q20<<7) + num) / (Q20<<8)); + + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + + pred = (int) (((Q11<<7) + num) / (Q11<<8)); + + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + + pred = (int) (((Q02<<7) + num) / (Q02<<8)); + + if (Al > 0 && pred >= (1< 0 && pred >= (1<DCT_scaled_size; + + } + + output_ptr += compptr->DCT_scaled_size; + + } + + } + + + + if (++(cinfo->output_iMCU_row) < cinfo->total_iMCU_rows) + + return JPEG_ROW_COMPLETED; + + return JPEG_SCAN_COMPLETED; + +} + + + +#endif /* BLOCK_SMOOTHING_SUPPORTED */ + + + + + +/* + + * Initialize coefficient buffer controller. + + */ + + + +GLOBAL void + +jinit_d_coef_controller (j_decompress_ptr cinfo, boolean need_full_buffer) + +{ + + my_coef_ptr coef; + + + + coef = (my_coef_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(my_coef_controller)); + + cinfo->coef = (struct jpeg_d_coef_controller *) coef; + + coef->pub.start_input_pass = start_input_pass; + + coef->pub.start_output_pass = start_output_pass; + +#ifdef BLOCK_SMOOTHING_SUPPORTED + + coef->coef_bits_latch = NULL; + +#endif + + + + /* Create the coefficient buffer. */ + + if (need_full_buffer) { + +#ifdef D_MULTISCAN_FILES_SUPPORTED + + /* Allocate a full-image virtual array for each component, */ + + /* padded to a multiple of samp_factor DCT blocks in each direction. */ + + /* Note we ask for a pre-zeroed array. */ + + int ci, access_rows; + + jpeg_component_info *compptr; + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + access_rows = compptr->v_samp_factor; + +#ifdef BLOCK_SMOOTHING_SUPPORTED + + /* If block smoothing could be used, need a bigger window */ + + if (cinfo->progressive_mode) + + access_rows *= 3; + +#endif + + coef->whole_image[ci] = (*cinfo->mem->request_virt_barray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, TRUE, + + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + + (long) compptr->h_samp_factor), + + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + + (long) compptr->v_samp_factor), + + (JDIMENSION) access_rows); + + } + + coef->pub.consume_data = consume_data; + + coef->pub.decompress_data = decompress_data; + + coef->pub.coef_arrays = coef->whole_image; /* link to virtual arrays */ + +#else + + ERREXIT(cinfo, JERR_NOT_COMPILED); + +#endif + + } else { + + /* We only need a single-MCU buffer. */ + + JBLOCKROW buffer; + + int i; + + + + buffer = (JBLOCKROW) + + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + D_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + + for (i = 0; i < D_MAX_BLOCKS_IN_MCU; i++) { + + coef->MCU_buffer[i] = buffer + i; + + } + + coef->pub.consume_data = dummy_consume_data; + + coef->pub.decompress_data = decompress_onepass; + + coef->pub.coef_arrays = NULL; /* flag for no virtual arrays */ + + } + +} + diff --git a/utils/roq2/jpeg/jdcolor.c b/utils/roq2/jpeg/jdcolor.c new file mode 100644 index 0000000..b587c46 --- /dev/null +++ b/utils/roq2/jpeg/jdcolor.c @@ -0,0 +1,734 @@ +/* + + * jdcolor.c + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains output colorspace conversion routines. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + + + +/* Private subobject */ + + + +typedef struct { + + struct jpeg_color_deconverter pub; /* public fields */ + + + + /* Private state for YCC->RGB conversion */ + + int * Cr_r_tab; /* => table for Cr to R conversion */ + + int * Cb_b_tab; /* => table for Cb to B conversion */ + + INT32 * Cr_g_tab; /* => table for Cr to G conversion */ + + INT32 * Cb_g_tab; /* => table for Cb to G conversion */ + +} my_color_deconverter; + + + +typedef my_color_deconverter * my_cconvert_ptr; + + + + + +/**************** YCbCr -> RGB conversion: most common case **************/ + + + +/* + + * YCbCr is defined per CCIR 601-1, except that Cb and Cr are + + * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. + + * The conversion equations to be implemented are therefore + + * R = Y + 1.40200 * Cr + + * G = Y - 0.34414 * Cb - 0.71414 * Cr + + * B = Y + 1.77200 * Cb + + * where Cb and Cr represent the incoming values less CENTERJSAMPLE. + + * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) + + * + + * To avoid floating-point arithmetic, we represent the fractional constants + + * as integers scaled up by 2^16 (about 4 digits precision); we have to divide + + * the products by 2^16, with appropriate rounding, to get the correct answer. + + * Notice that Y, being an integral input, does not contribute any fraction + + * so it need not participate in the rounding. + + * + + * For even more speed, we avoid doing any multiplications in the inner loop + + * by precalculating the constants times Cb and Cr for all possible values. + + * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table); + + * for 12-bit samples it is still acceptable. It's not very reasonable for + + * 16-bit samples, but if you want lossless storage you shouldn't be changing + + * colorspace anyway. + + * The Cr=>R and Cb=>B values can be rounded to integers in advance; the + + * values for the G calculation are left scaled up, since we must add them + + * together before rounding. + + */ + + + +#define SCALEBITS 16 /* speediest right-shift on some machines */ + +#define ONE_HALF ((INT32) 1 << (SCALEBITS-1)) + +#define FIX(x) ((INT32) ((x) * (1L<RGB colorspace conversion. + + */ + + + +LOCAL void + +build_ycc_rgb_table (j_decompress_ptr cinfo) + +{ + + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + + int i; + + INT32 x; + + SHIFT_TEMPS + + + + cconvert->Cr_r_tab = (int *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (MAXJSAMPLE+1) * SIZEOF(int)); + + cconvert->Cb_b_tab = (int *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (MAXJSAMPLE+1) * SIZEOF(int)); + + cconvert->Cr_g_tab = (INT32 *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (MAXJSAMPLE+1) * SIZEOF(INT32)); + + cconvert->Cb_g_tab = (INT32 *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (MAXJSAMPLE+1) * SIZEOF(INT32)); + + + + for (i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) { + + /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ + + /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ + + /* Cr=>R value is nearest int to 1.40200 * x */ + + cconvert->Cr_r_tab[i] = (int) + + RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS); + + /* Cb=>B value is nearest int to 1.77200 * x */ + + cconvert->Cb_b_tab[i] = (int) + + RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS); + + /* Cr=>G value is scaled-up -0.71414 * x */ + + cconvert->Cr_g_tab[i] = (- FIX(0.71414)) * x; + + /* Cb=>G value is scaled-up -0.34414 * x */ + + /* We also add in ONE_HALF so that need not do it in inner loop */ + + cconvert->Cb_g_tab[i] = (- FIX(0.34414)) * x + ONE_HALF; + + } + +} + + + + + +/* + + * Convert some rows of samples to the output colorspace. + + * + + * Note that we change from noninterleaved, one-plane-per-component format + + * to interleaved-pixel format. The output buffer is therefore three times + + * as wide as the input buffer. + + * A starting row offset is provided only for the input buffer. The caller + + * can easily adjust the passed output_buf value to accommodate any row + + * offset required on that side. + + */ + + + +METHODDEF void + +ycc_rgb_convert (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION input_row, + + JSAMPARRAY output_buf, int num_rows) + +{ + + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + + register int y, cb, cr; + + register JSAMPROW outptr; + + register JSAMPROW inptr0, inptr1, inptr2; + + register JDIMENSION col; + + JDIMENSION num_cols = cinfo->output_width; + + /* copy these pointers into registers if possible */ + + register JSAMPLE * range_limit = cinfo->sample_range_limit; + + register int * Crrtab = cconvert->Cr_r_tab; + + register int * Cbbtab = cconvert->Cb_b_tab; + + register INT32 * Crgtab = cconvert->Cr_g_tab; + + register INT32 * Cbgtab = cconvert->Cb_g_tab; + + SHIFT_TEMPS + + + + while (--num_rows >= 0) { + + inptr0 = input_buf[0][input_row]; + + inptr1 = input_buf[1][input_row]; + + inptr2 = input_buf[2][input_row]; + + input_row++; + + outptr = *output_buf++; + + for (col = 0; col < num_cols; col++) { + + y = GETJSAMPLE(inptr0[col]); + + cb = GETJSAMPLE(inptr1[col]); + + cr = GETJSAMPLE(inptr2[col]); + + /* Range-limiting is essential due to noise introduced by DCT losses. */ + + outptr[RGB_RED] = range_limit[y + Crrtab[cr]]; + + outptr[RGB_GREEN] = range_limit[y + + + ((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], + + SCALEBITS))]; + + outptr[RGB_BLUE] = range_limit[y + Cbbtab[cb]]; + + outptr += RGB_PIXELSIZE; + + } + + } + +} + + + + + +/**************** Cases other than YCbCr -> RGB **************/ + + + + + +/* + + * Color conversion for no colorspace change: just copy the data, + + * converting from separate-planes to interleaved representation. + + */ + + + +METHODDEF void + +null_convert (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION input_row, + + JSAMPARRAY output_buf, int num_rows) + +{ + + register JSAMPROW inptr, outptr; + + register JDIMENSION count; + + register int num_components = cinfo->num_components; + + JDIMENSION num_cols = cinfo->output_width; + + int ci; + + + + while (--num_rows >= 0) { + + for (ci = 0; ci < num_components; ci++) { + + inptr = input_buf[ci][input_row]; + + outptr = output_buf[0] + ci; + + for (count = num_cols; count > 0; count--) { + + *outptr = *inptr++; /* needn't bother with GETJSAMPLE() here */ + + outptr += num_components; + + } + + } + + input_row++; + + output_buf++; + + } + +} + + + + + +/* + + * Color conversion for grayscale: just copy the data. + + * This also works for YCbCr -> grayscale conversion, in which + + * we just copy the Y (luminance) component and ignore chrominance. + + */ + + + +METHODDEF void + +grayscale_convert (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION input_row, + + JSAMPARRAY output_buf, int num_rows) + +{ + + jcopy_sample_rows(input_buf[0], (int) input_row, output_buf, 0, + + num_rows, cinfo->output_width); + +} + + + + + +/* + + * Adobe-style YCCK->CMYK conversion. + + * We convert YCbCr to R=1-C, G=1-M, and B=1-Y using the same + + * conversion as above, while passing K (black) unchanged. + + * We assume build_ycc_rgb_table has been called. + + */ + + + +METHODDEF void + +ycck_cmyk_convert (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION input_row, + + JSAMPARRAY output_buf, int num_rows) + +{ + + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + + register int y, cb, cr; + + register JSAMPROW outptr; + + register JSAMPROW inptr0, inptr1, inptr2, inptr3; + + register JDIMENSION col; + + JDIMENSION num_cols = cinfo->output_width; + + /* copy these pointers into registers if possible */ + + register JSAMPLE * range_limit = cinfo->sample_range_limit; + + register int * Crrtab = cconvert->Cr_r_tab; + + register int * Cbbtab = cconvert->Cb_b_tab; + + register INT32 * Crgtab = cconvert->Cr_g_tab; + + register INT32 * Cbgtab = cconvert->Cb_g_tab; + + SHIFT_TEMPS + + + + while (--num_rows >= 0) { + + inptr0 = input_buf[0][input_row]; + + inptr1 = input_buf[1][input_row]; + + inptr2 = input_buf[2][input_row]; + + inptr3 = input_buf[3][input_row]; + + input_row++; + + outptr = *output_buf++; + + for (col = 0; col < num_cols; col++) { + + y = GETJSAMPLE(inptr0[col]); + + cb = GETJSAMPLE(inptr1[col]); + + cr = GETJSAMPLE(inptr2[col]); + + /* Range-limiting is essential due to noise introduced by DCT losses. */ + + outptr[0] = range_limit[MAXJSAMPLE - (y + Crrtab[cr])]; /* red */ + + outptr[1] = range_limit[MAXJSAMPLE - (y + /* green */ + + ((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], + + SCALEBITS)))]; + + outptr[2] = range_limit[MAXJSAMPLE - (y + Cbbtab[cb])]; /* blue */ + + /* K passes through unchanged */ + + outptr[3] = inptr3[col]; /* don't need GETJSAMPLE here */ + + outptr += 4; + + } + + } + +} + + + + + +/* + + * Empty method for start_pass. + + */ + + + +METHODDEF void + +start_pass_dcolor (j_decompress_ptr cinfo) + +{ + + /* no work needed */ + +} + + + + + +/* + + * Module initialization routine for output colorspace conversion. + + */ + + + +GLOBAL void + +jinit_color_deconverter (j_decompress_ptr cinfo) + +{ + + my_cconvert_ptr cconvert; + + int ci; + + + + cconvert = (my_cconvert_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(my_color_deconverter)); + + cinfo->cconvert = (struct jpeg_color_deconverter *) cconvert; + + cconvert->pub.start_pass = start_pass_dcolor; + + + + /* Make sure num_components agrees with jpeg_color_space */ + + switch (cinfo->jpeg_color_space) { + + case JCS_GRAYSCALE: + + if (cinfo->num_components != 1) + + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + + break; + + + + case JCS_RGB: + + case JCS_YCbCr: + + if (cinfo->num_components != 3) + + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + + break; + + + + case JCS_CMYK: + + case JCS_YCCK: + + if (cinfo->num_components != 4) + + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + + break; + + + + default: /* JCS_UNKNOWN can be anything */ + + if (cinfo->num_components < 1) + + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + + break; + + } + + + + /* Set out_color_components and conversion method based on requested space. + + * Also clear the component_needed flags for any unused components, + + * so that earlier pipeline stages can avoid useless computation. + + */ + + + + switch (cinfo->out_color_space) { + + case JCS_GRAYSCALE: + + cinfo->out_color_components = 1; + + if (cinfo->jpeg_color_space == JCS_GRAYSCALE || + + cinfo->jpeg_color_space == JCS_YCbCr) { + + cconvert->pub.color_convert = grayscale_convert; + + /* For color->grayscale conversion, only the Y (0) component is needed */ + + for (ci = 1; ci < cinfo->num_components; ci++) + + cinfo->comp_info[ci].component_needed = FALSE; + + } else + + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + + break; + + + + case JCS_RGB: + + cinfo->out_color_components = RGB_PIXELSIZE; + + if (cinfo->jpeg_color_space == JCS_YCbCr) { + + cconvert->pub.color_convert = ycc_rgb_convert; + + build_ycc_rgb_table(cinfo); + + } else if (cinfo->jpeg_color_space == JCS_RGB && RGB_PIXELSIZE == 3) { + + cconvert->pub.color_convert = null_convert; + + } else + + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + + break; + + + + case JCS_CMYK: + + cinfo->out_color_components = 4; + + if (cinfo->jpeg_color_space == JCS_YCCK) { + + cconvert->pub.color_convert = ycck_cmyk_convert; + + build_ycc_rgb_table(cinfo); + + } else if (cinfo->jpeg_color_space == JCS_CMYK) { + + cconvert->pub.color_convert = null_convert; + + } else + + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + + break; + + + + default: + + /* Permit null conversion to same output space */ + + if (cinfo->out_color_space == cinfo->jpeg_color_space) { + + cinfo->out_color_components = cinfo->num_components; + + cconvert->pub.color_convert = null_convert; + + } else /* unsupported non-null conversion */ + + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + + break; + + } + + + + if (cinfo->quantize_colors) + + cinfo->output_components = 1; /* single colormapped output component */ + + else + + cinfo->output_components = cinfo->out_color_components; + +} + diff --git a/utils/roq2/jpeg/jdct.h b/utils/roq2/jpeg/jdct.h new file mode 100644 index 0000000..cebb118 --- /dev/null +++ b/utils/roq2/jpeg/jdct.h @@ -0,0 +1,352 @@ +/* + + * jdct.h + + * + + * Copyright (C) 1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This include file contains common declarations for the forward and + + * inverse DCT modules. These declarations are private to the DCT managers + + * (jcdctmgr.c, jddctmgr.c) and the individual DCT algorithms. + + * The individual DCT algorithms are kept in separate files to ease + + * machine-dependent tuning (e.g., assembly coding). + + */ + + + + + +/* + + * A forward DCT routine is given a pointer to a work area of type DCTELEM[]; + + * the DCT is to be performed in-place in that buffer. Type DCTELEM is int + + * for 8-bit samples, INT32 for 12-bit samples. (NOTE: Floating-point DCT + + * implementations use an array of type FAST_FLOAT, instead.) + + * The DCT inputs are expected to be signed (range +-CENTERJSAMPLE). + + * The DCT outputs are returned scaled up by a factor of 8; they therefore + + * have a range of +-8K for 8-bit data, +-128K for 12-bit data. This + + * convention improves accuracy in integer implementations and saves some + + * work in floating-point ones. + + * Quantization of the output coefficients is done by jcdctmgr.c. + + */ + + + +#if BITS_IN_JSAMPLE == 8 + +typedef int DCTELEM; /* 16 or 32 bits is fine */ + +#else + +typedef INT32 DCTELEM; /* must have 32 bits */ + +#endif + + + +typedef JMETHOD(void, forward_DCT_method_ptr, (DCTELEM * data)); + +typedef JMETHOD(void, float_DCT_method_ptr, (FAST_FLOAT * data)); + + + + + +/* + + * An inverse DCT routine is given a pointer to the input JBLOCK and a pointer + + * to an output sample array. The routine must dequantize the input data as + + * well as perform the IDCT; for dequantization, it uses the multiplier table + + * pointed to by compptr->dct_table. The output data is to be placed into the + + * sample array starting at a specified column. (Any row offset needed will + + * be applied to the array pointer before it is passed to the IDCT code.) + + * Note that the number of samples emitted by the IDCT routine is + + * DCT_scaled_size * DCT_scaled_size. + + */ + + + +/* typedef inverse_DCT_method_ptr is declared in jpegint.h */ + + + +/* + + * Each IDCT routine has its own ideas about the best dct_table element type. + + */ + + + +typedef MULTIPLIER ISLOW_MULT_TYPE; /* short or int, whichever is faster */ + +#if BITS_IN_JSAMPLE == 8 + +typedef MULTIPLIER IFAST_MULT_TYPE; /* 16 bits is OK, use short if faster */ + +#define IFAST_SCALE_BITS 2 /* fractional bits in scale factors */ + +#else + +typedef INT32 IFAST_MULT_TYPE; /* need 32 bits for scaled quantizers */ + +#define IFAST_SCALE_BITS 13 /* fractional bits in scale factors */ + +#endif + +typedef FAST_FLOAT FLOAT_MULT_TYPE; /* preferred floating type */ + + + + + +/* + + * Each IDCT routine is responsible for range-limiting its results and + + * converting them to unsigned form (0..MAXJSAMPLE). The raw outputs could + + * be quite far out of range if the input data is corrupt, so a bulletproof + + * range-limiting step is required. We use a mask-and-table-lookup method + + * to do the combined operations quickly. See the comments with + + * prepare_range_limit_table (in jdmaster.c) for more info. + + */ + + + +#define IDCT_range_limit(cinfo) ((cinfo)->sample_range_limit + CENTERJSAMPLE) + + + +#define RANGE_MASK (MAXJSAMPLE * 4 + 3) /* 2 bits wider than legal samples */ + + + + + +/* Short forms of external names for systems with brain-damaged linkers. */ + + + +#ifdef NEED_SHORT_EXTERNAL_NAMES + +#define jpeg_fdct_islow jFDislow + +#define jpeg_fdct_ifast jFDifast + +#define jpeg_fdct_float jFDfloat + +#define jpeg_idct_islow jRDislow + +#define jpeg_idct_ifast jRDifast + +#define jpeg_idct_float jRDfloat + +#define jpeg_idct_4x4 jRD4x4 + +#define jpeg_idct_2x2 jRD2x2 + +#define jpeg_idct_1x1 jRD1x1 + +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + + +/* Extern declarations for the forward and inverse DCT routines. */ + + + +EXTERN void jpeg_fdct_islow JPP((DCTELEM * data)); + +EXTERN void jpeg_fdct_ifast JPP((DCTELEM * data)); + +EXTERN void jpeg_fdct_float JPP((FAST_FLOAT * data)); + + + +EXTERN void jpeg_idct_islow + + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); + +EXTERN void jpeg_idct_ifast + + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); + +EXTERN void jpeg_idct_float + + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); + +EXTERN void jpeg_idct_4x4 + + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); + +EXTERN void jpeg_idct_2x2 + + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); + +EXTERN void jpeg_idct_1x1 + + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); + + + + + +/* + + * Macros for handling fixed-point arithmetic; these are used by many + + * but not all of the DCT/IDCT modules. + + * + + * All values are expected to be of type INT32. + + * Fractional constants are scaled left by CONST_BITS bits. + + * CONST_BITS is defined within each module using these macros, + + * and may differ from one module to the next. + + */ + + + +#define ONE ((INT32) 1) + +#define CONST_SCALE (ONE << CONST_BITS) + + + +/* Convert a positive real constant to an integer scaled by CONST_SCALE. + + * Caution: some C compilers fail to reduce "FIX(constant)" at compile time, + + * thus causing a lot of useless floating-point operations at run time. + + */ + + + +#define FIX(x) ((INT32) ((x) * CONST_SCALE + 0.5)) + + + +/* Descale and correctly round an INT32 value that's scaled by N bits. + + * We assume RIGHT_SHIFT rounds towards minus infinity, so adding + + * the fudge factor is correct for either sign of X. + + */ + + + +#define DESCALE(x,n) RIGHT_SHIFT((x) + (ONE << ((n)-1)), n) + + + +/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. + + * This macro is used only when the two inputs will actually be no more than + + * 16 bits wide, so that a 16x16->32 bit multiply can be used instead of a + + * full 32x32 multiply. This provides a useful speedup on many machines. + + * Unfortunately there is no way to specify a 16x16->32 multiply portably + + * in C, but some C compilers will do the right thing if you provide the + + * correct combination of casts. + + */ + + + +#ifdef SHORTxSHORT_32 /* may work if 'int' is 32 bits */ + +#define MULTIPLY16C16(var,const) (((INT16) (var)) * ((INT16) (const))) + +#endif + +#ifdef SHORTxLCONST_32 /* known to work with Microsoft C 6.0 */ + +#define MULTIPLY16C16(var,const) (((INT16) (var)) * ((INT32) (const))) + +#endif + + + +#ifndef MULTIPLY16C16 /* default definition */ + +#define MULTIPLY16C16(var,const) ((var) * (const)) + +#endif + + + +/* Same except both inputs are variables. */ + + + +#ifdef SHORTxSHORT_32 /* may work if 'int' is 32 bits */ + +#define MULTIPLY16V16(var1,var2) (((INT16) (var1)) * ((INT16) (var2))) + +#endif + + + +#ifndef MULTIPLY16V16 /* default definition */ + +#define MULTIPLY16V16(var1,var2) ((var1) * (var2)) + +#endif + diff --git a/utils/roq2/jpeg/jddctmgr.c b/utils/roq2/jpeg/jddctmgr.c new file mode 100644 index 0000000..88b5675 --- /dev/null +++ b/utils/roq2/jpeg/jddctmgr.c @@ -0,0 +1,540 @@ +/* + + * jddctmgr.c + + * + + * Copyright (C) 1994-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains the inverse-DCT management logic. + + * This code selects a particular IDCT implementation to be used, + + * and it performs related housekeeping chores. No code in this file + + * is executed per IDCT step, only during output pass setup. + + * + + * Note that the IDCT routines are responsible for performing coefficient + + * dequantization as well as the IDCT proper. This module sets up the + + * dequantization multiplier table needed by the IDCT routine. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + +#include "jdct.h" /* Private declarations for DCT subsystem */ + + + + + +/* + + * The decompressor input side (jdinput.c) saves away the appropriate + + * quantization table for each component at the start of the first scan + + * involving that component. (This is necessary in order to correctly + + * decode files that reuse Q-table slots.) + + * When we are ready to make an output pass, the saved Q-table is converted + + * to a multiplier table that will actually be used by the IDCT routine. + + * The multiplier table contents are IDCT-method-dependent. To support + + * application changes in IDCT method between scans, we can remake the + + * multiplier tables if necessary. + + * In buffered-image mode, the first output pass may occur before any data + + * has been seen for some components, and thus before their Q-tables have + + * been saved away. To handle this case, multiplier tables are preset + + * to zeroes; the result of the IDCT will be a neutral gray level. + + */ + + + + + +/* Private subobject for this module */ + + + +typedef struct { + + struct jpeg_inverse_dct pub; /* public fields */ + + + + /* This array contains the IDCT method code that each multiplier table + + * is currently set up for, or -1 if it's not yet set up. + + * The actual multiplier tables are pointed to by dct_table in the + + * per-component comp_info structures. + + */ + + int cur_method[MAX_COMPONENTS]; + +} my_idct_controller; + + + +typedef my_idct_controller * my_idct_ptr; + + + + + +/* Allocated multiplier tables: big enough for any supported variant */ + + + +typedef union { + + ISLOW_MULT_TYPE islow_array[DCTSIZE2]; + +#ifdef DCT_IFAST_SUPPORTED + + IFAST_MULT_TYPE ifast_array[DCTSIZE2]; + +#endif + +#ifdef DCT_FLOAT_SUPPORTED + + FLOAT_MULT_TYPE float_array[DCTSIZE2]; + +#endif + +} multiplier_table; + + + + + +/* The current scaled-IDCT routines require ISLOW-style multiplier tables, + + * so be sure to compile that code if either ISLOW or SCALING is requested. + + */ + +#ifdef DCT_ISLOW_SUPPORTED + +#define PROVIDE_ISLOW_TABLES + +#else + +#ifdef IDCT_SCALING_SUPPORTED + +#define PROVIDE_ISLOW_TABLES + +#endif + +#endif + + + + + +/* + + * Prepare for an output pass. + + * Here we select the proper IDCT routine for each component and build + + * a matching multiplier table. + + */ + + + +METHODDEF void + +start_pass (j_decompress_ptr cinfo) + +{ + + my_idct_ptr idct = (my_idct_ptr) cinfo->idct; + + int ci, i; + + jpeg_component_info *compptr; + + int method = 0; + + inverse_DCT_method_ptr method_ptr = NULL; + + JQUANT_TBL * qtbl; + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + /* Select the proper IDCT routine for this component's scaling */ + + switch (compptr->DCT_scaled_size) { + +#ifdef IDCT_SCALING_SUPPORTED + + case 1: + + method_ptr = jpeg_idct_1x1; + + method = JDCT_ISLOW; /* jidctred uses islow-style table */ + + break; + + case 2: + + method_ptr = jpeg_idct_2x2; + + method = JDCT_ISLOW; /* jidctred uses islow-style table */ + + break; + + case 4: + + method_ptr = jpeg_idct_4x4; + + method = JDCT_ISLOW; /* jidctred uses islow-style table */ + + break; + +#endif + + case DCTSIZE: + + switch (cinfo->dct_method) { + +#ifdef DCT_ISLOW_SUPPORTED + + case JDCT_ISLOW: + + method_ptr = jpeg_idct_islow; + + method = JDCT_ISLOW; + + break; + +#endif + +#ifdef DCT_IFAST_SUPPORTED + + case JDCT_IFAST: + + method_ptr = jpeg_idct_ifast; + + method = JDCT_IFAST; + + break; + +#endif + +#ifdef DCT_FLOAT_SUPPORTED + + case JDCT_FLOAT: + + method_ptr = jpeg_idct_float; + + method = JDCT_FLOAT; + + break; + +#endif + + default: + + ERREXIT(cinfo, JERR_NOT_COMPILED); + + break; + + } + + break; + + default: + + ERREXIT1(cinfo, JERR_BAD_DCTSIZE, compptr->DCT_scaled_size); + + break; + + } + + idct->pub.inverse_DCT[ci] = method_ptr; + + /* Create multiplier table from quant table. + + * However, we can skip this if the component is uninteresting + + * or if we already built the table. Also, if no quant table + + * has yet been saved for the component, we leave the + + * multiplier table all-zero; we'll be reading zeroes from the + + * coefficient controller's buffer anyway. + + */ + + if (! compptr->component_needed || idct->cur_method[ci] == method) + + continue; + + qtbl = compptr->quant_table; + + if (qtbl == NULL) /* happens if no data yet for component */ + + continue; + + idct->cur_method[ci] = method; + + switch (method) { + +#ifdef PROVIDE_ISLOW_TABLES + + case JDCT_ISLOW: + + { + + /* For LL&M IDCT method, multipliers are equal to raw quantization + + * coefficients, but are stored in natural order as ints. + + */ + + ISLOW_MULT_TYPE * ismtbl = (ISLOW_MULT_TYPE *) compptr->dct_table; + + for (i = 0; i < DCTSIZE2; i++) { + + ismtbl[i] = (ISLOW_MULT_TYPE) qtbl->quantval[jpeg_zigzag_order[i]]; + + } + + } + + break; + +#endif + +#ifdef DCT_IFAST_SUPPORTED + + case JDCT_IFAST: + + { + + /* For AA&N IDCT method, multipliers are equal to quantization + + * coefficients scaled by scalefactor[row]*scalefactor[col], where + + * scalefactor[0] = 1 + + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + + * For integer operation, the multiplier table is to be scaled by + + * IFAST_SCALE_BITS. The multipliers are stored in natural order. + + */ + + IFAST_MULT_TYPE * ifmtbl = (IFAST_MULT_TYPE *) compptr->dct_table; + +#define CONST_BITS 14 + + static const INT16 aanscales[DCTSIZE2] = { + + /* precomputed values scaled up by 14 bits */ + + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + + 22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270, + + 21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906, + + 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, + + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + + 12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552, + + 8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446, + + 4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247 + + }; + + SHIFT_TEMPS + + + + for (i = 0; i < DCTSIZE2; i++) { + + ifmtbl[i] = (IFAST_MULT_TYPE) + + DESCALE(MULTIPLY16V16((INT32) qtbl->quantval[jpeg_zigzag_order[i]], + + (INT32) aanscales[i]), + + CONST_BITS-IFAST_SCALE_BITS); + + } + + } + + break; + +#endif + +#ifdef DCT_FLOAT_SUPPORTED + + case JDCT_FLOAT: + + { + + /* For float AA&N IDCT method, multipliers are equal to quantization + + * coefficients scaled by scalefactor[row]*scalefactor[col], where + + * scalefactor[0] = 1 + + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + + * The multipliers are stored in natural order. + + */ + + FLOAT_MULT_TYPE * fmtbl = (FLOAT_MULT_TYPE *) compptr->dct_table; + + int row, col; + + static const double aanscalefactor[DCTSIZE] = { + + 1.0, 1.387039845, 1.306562965, 1.175875602, + + 1.0, 0.785694958, 0.541196100, 0.275899379 + + }; + + + + i = 0; + + for (row = 0; row < DCTSIZE; row++) { + + for (col = 0; col < DCTSIZE; col++) { + + fmtbl[i] = (FLOAT_MULT_TYPE) + + ((double) qtbl->quantval[jpeg_zigzag_order[i]] * + + aanscalefactor[row] * aanscalefactor[col]); + + i++; + + } + + } + + } + + break; + +#endif + + default: + + ERREXIT(cinfo, JERR_NOT_COMPILED); + + break; + + } + + } + +} + + + + + +/* + + * Initialize IDCT manager. + + */ + + + +GLOBAL void + +jinit_inverse_dct (j_decompress_ptr cinfo) + +{ + + my_idct_ptr idct; + + int ci; + + jpeg_component_info *compptr; + + + + idct = (my_idct_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(my_idct_controller)); + + cinfo->idct = (struct jpeg_inverse_dct *) idct; + + idct->pub.start_pass = start_pass; + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + /* Allocate and pre-zero a multiplier table for each component */ + + compptr->dct_table = + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(multiplier_table)); + + MEMZERO(compptr->dct_table, SIZEOF(multiplier_table)); + + /* Mark multiplier table not yet set up for any method */ + + idct->cur_method[ci] = -1; + + } + +} + diff --git a/utils/roq2/jpeg/jdhuff.c b/utils/roq2/jpeg/jdhuff.c new file mode 100644 index 0000000..dde2a6c --- /dev/null +++ b/utils/roq2/jpeg/jdhuff.c @@ -0,0 +1,1148 @@ +/* + + * jdhuff.c + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains Huffman entropy decoding routines. + + * + + * Much of the complexity here has to do with supporting input suspension. + + * If the data source module demands suspension, we want to be able to back + + * up to the start of the current MCU. To do this, we copy state variables + + * into local working storage, and update them back to the permanent + + * storage only upon successful completion of an MCU. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + +#include "jdhuff.h" /* Declarations shared with jdphuff.c */ + + + + + +/* + + * Expanded entropy decoder object for Huffman decoding. + + * + + * The savable_state subrecord contains fields that change within an MCU, + + * but must not be updated permanently until we complete the MCU. + + */ + + + +typedef struct { + + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ + +} savable_state; + + + +/* This macro is to work around compilers with missing or broken + + * structure assignment. You'll need to fix this code if you have + + * such a compiler and you change MAX_COMPS_IN_SCAN. + + */ + + + +#ifndef NO_STRUCT_ASSIGN + +#define ASSIGN_STATE(dest,src) ((dest) = (src)) + +#else + +#if MAX_COMPS_IN_SCAN == 4 + +#define ASSIGN_STATE(dest,src) \ + + ((dest).last_dc_val[0] = (src).last_dc_val[0], \ + + (dest).last_dc_val[1] = (src).last_dc_val[1], \ + + (dest).last_dc_val[2] = (src).last_dc_val[2], \ + + (dest).last_dc_val[3] = (src).last_dc_val[3]) + +#endif + +#endif + + + + + +typedef struct { + + struct jpeg_entropy_decoder pub; /* public fields */ + + + + /* These fields are loaded into local variables at start of each MCU. + + * In case of suspension, we exit WITHOUT updating them. + + */ + + bitread_perm_state bitstate; /* Bit buffer at start of MCU */ + + savable_state saved; /* Other state at start of MCU */ + + + + /* These fields are NOT loaded into local working state. */ + + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + + + + /* Pointers to derived tables (these workspaces have image lifespan) */ + + d_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS]; + + d_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS]; + +} huff_entropy_decoder; + + + +typedef huff_entropy_decoder * huff_entropy_ptr; + + + + + +/* + + * Initialize for a Huffman-compressed scan. + + */ + + + +METHODDEF void + +start_pass_huff_decoder (j_decompress_ptr cinfo) + +{ + + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + + int ci, dctbl, actbl; + + jpeg_component_info * compptr; + + + + /* Check that the scan parameters Ss, Se, Ah/Al are OK for sequential JPEG. + + * This ought to be an error condition, but we make it a warning because + + * there are some baseline files out there with all zeroes in these bytes. + + */ + + if (cinfo->Ss != 0 || cinfo->Se != DCTSIZE2-1 || + + cinfo->Ah != 0 || cinfo->Al != 0) + + WARNMS(cinfo, JWRN_NOT_SEQUENTIAL); + + + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + + compptr = cinfo->cur_comp_info[ci]; + + dctbl = compptr->dc_tbl_no; + + actbl = compptr->ac_tbl_no; + + /* Make sure requested tables are present */ + + if (dctbl < 0 || dctbl >= NUM_HUFF_TBLS || + + cinfo->dc_huff_tbl_ptrs[dctbl] == NULL) + + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, dctbl); + + if (actbl < 0 || actbl >= NUM_HUFF_TBLS || + + cinfo->ac_huff_tbl_ptrs[actbl] == NULL) + + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, actbl); + + /* Compute derived values for Huffman tables */ + + /* We may do this more than once for a table, but it's not expensive */ + + jpeg_make_d_derived_tbl(cinfo, cinfo->dc_huff_tbl_ptrs[dctbl], + + & entropy->dc_derived_tbls[dctbl]); + + jpeg_make_d_derived_tbl(cinfo, cinfo->ac_huff_tbl_ptrs[actbl], + + & entropy->ac_derived_tbls[actbl]); + + /* Initialize DC predictions to 0 */ + + entropy->saved.last_dc_val[ci] = 0; + + } + + + + /* Initialize bitread state variables */ + + entropy->bitstate.bits_left = 0; + + entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ + + entropy->bitstate.printed_eod = FALSE; + + + + /* Initialize restart counter */ + + entropy->restarts_to_go = cinfo->restart_interval; + +} + + + + + +/* + + * Compute the derived values for a Huffman table. + + * Note this is also used by jdphuff.c. + + */ + + + +GLOBAL void + +jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, JHUFF_TBL * htbl, + + d_derived_tbl ** pdtbl) + +{ + + d_derived_tbl *dtbl; + + int p, i, l, si; + + int lookbits, ctr; + + char huffsize[257]; + + unsigned int huffcode[257]; + + unsigned int code; + + + + /* Allocate a workspace if we haven't already done so. */ + + if (*pdtbl == NULL) + + *pdtbl = (d_derived_tbl *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(d_derived_tbl)); + + dtbl = *pdtbl; + + dtbl->pub = htbl; /* fill in back link */ + + + + /* Figure C.1: make table of Huffman code length for each symbol */ + + /* Note that this is in code-length order. */ + + + + p = 0; + + for (l = 1; l <= 16; l++) { + + for (i = 1; i <= (int) htbl->bits[l]; i++) + + huffsize[p++] = (char) l; + + } + + huffsize[p] = 0; + + + + /* Figure C.2: generate the codes themselves */ + + /* Note that this is in code-length order. */ + + + + code = 0; + + si = huffsize[0]; + + p = 0; + + while (huffsize[p]) { + + while (((int) huffsize[p]) == si) { + + huffcode[p++] = code; + + code++; + + } + + code <<= 1; + + si++; + + } + + + + /* Figure F.15: generate decoding tables for bit-sequential decoding */ + + + + p = 0; + + for (l = 1; l <= 16; l++) { + + if (htbl->bits[l]) { + + dtbl->valptr[l] = p; /* huffval[] index of 1st symbol of code length l */ + + dtbl->mincode[l] = huffcode[p]; /* minimum code of length l */ + + p += htbl->bits[l]; + + dtbl->maxcode[l] = huffcode[p-1]; /* maximum code of length l */ + + } else { + + dtbl->maxcode[l] = -1; /* -1 if no codes of this length */ + + } + + } + + dtbl->maxcode[17] = 0xFFFFFL; /* ensures jpeg_huff_decode terminates */ + + + + /* Compute lookahead tables to speed up decoding. + + * First we set all the table entries to 0, indicating "too long"; + + * then we iterate through the Huffman codes that are short enough and + + * fill in all the entries that correspond to bit sequences starting + + * with that code. + + */ + + + + MEMZERO(dtbl->look_nbits, SIZEOF(dtbl->look_nbits)); + + + + p = 0; + + for (l = 1; l <= HUFF_LOOKAHEAD; l++) { + + for (i = 1; i <= (int) htbl->bits[l]; i++, p++) { + + /* l = current code's length, p = its index in huffcode[] & huffval[]. */ + + /* Generate left-justified code followed by all possible bit sequences */ + + lookbits = huffcode[p] << (HUFF_LOOKAHEAD-l); + + for (ctr = 1 << (HUFF_LOOKAHEAD-l); ctr > 0; ctr--) { + + dtbl->look_nbits[lookbits] = l; + + dtbl->look_sym[lookbits] = htbl->huffval[p]; + + lookbits++; + + } + + } + + } + +} + + + + + +/* + + * Out-of-line code for bit fetching (shared with jdphuff.c). + + * See jdhuff.h for info about usage. + + * Note: current values of get_buffer and bits_left are passed as parameters, + + * but are returned in the corresponding fields of the state struct. + + * + + * On most machines MIN_GET_BITS should be 25 to allow the full 32-bit width + + * of get_buffer to be used. (On machines with wider words, an even larger + + * buffer could be used.) However, on some machines 32-bit shifts are + + * quite slow and take time proportional to the number of places shifted. + + * (This is true with most PC compilers, for instance.) In this case it may + + * be a win to set MIN_GET_BITS to the minimum value of 15. This reduces the + + * average shift distance at the cost of more calls to jpeg_fill_bit_buffer. + + */ + + + +#ifdef SLOW_SHIFT_32 + +#define MIN_GET_BITS 15 /* minimum allowable value */ + +#else + +#define MIN_GET_BITS (BIT_BUF_SIZE-7) + +#endif + + + + + +GLOBAL boolean + +jpeg_fill_bit_buffer (bitread_working_state * state, + + register bit_buf_type get_buffer, register int bits_left, + + int nbits) + +/* Load up the bit buffer to a depth of at least nbits */ + +{ + + /* Copy heavily used state fields into locals (hopefully registers) */ + + register const JOCTET * next_input_byte = state->next_input_byte; + + register size_t bytes_in_buffer = state->bytes_in_buffer; + + register int c; + + + + /* Attempt to load at least MIN_GET_BITS bits into get_buffer. */ + + /* (It is assumed that no request will be for more than that many bits.) */ + + + + while (bits_left < MIN_GET_BITS) { + + /* Attempt to read a byte */ + + if (state->unread_marker != 0) + + goto no_more_data; /* can't advance past a marker */ + + + + if (bytes_in_buffer == 0) { + + if (! (*state->cinfo->src->fill_input_buffer) (state->cinfo)) + + return FALSE; + + next_input_byte = state->cinfo->src->next_input_byte; + + bytes_in_buffer = state->cinfo->src->bytes_in_buffer; + + } + + bytes_in_buffer--; + + c = GETJOCTET(*next_input_byte++); + + + + /* If it's 0xFF, check and discard stuffed zero byte */ + + if (c == 0xFF) { + + do { + + if (bytes_in_buffer == 0) { + + if (! (*state->cinfo->src->fill_input_buffer) (state->cinfo)) + + return FALSE; + + next_input_byte = state->cinfo->src->next_input_byte; + + bytes_in_buffer = state->cinfo->src->bytes_in_buffer; + + } + + bytes_in_buffer--; + + c = GETJOCTET(*next_input_byte++); + + } while (c == 0xFF); + + + + if (c == 0) { + + /* Found FF/00, which represents an FF data byte */ + + c = 0xFF; + + } else { + + /* Oops, it's actually a marker indicating end of compressed data. */ + + /* Better put it back for use later */ + + state->unread_marker = c; + + + + no_more_data: + + /* There should be enough bits still left in the data segment; */ + + /* if so, just break out of the outer while loop. */ + + if (bits_left >= nbits) + + break; + + /* Uh-oh. Report corrupted data to user and stuff zeroes into + + * the data stream, so that we can produce some kind of image. + + * Note that this code will be repeated for each byte demanded + + * for the rest of the segment. We use a nonvolatile flag to ensure + + * that only one warning message appears. + + */ + + if (! *(state->printed_eod_ptr)) { + + WARNMS(state->cinfo, JWRN_HIT_MARKER); + + *(state->printed_eod_ptr) = TRUE; + + } + + c = 0; /* insert a zero byte into bit buffer */ + + } + + } + + + + /* OK, load c into get_buffer */ + + get_buffer = (get_buffer << 8) | c; + + bits_left += 8; + + } + + + + /* Unload the local registers */ + + state->next_input_byte = next_input_byte; + + state->bytes_in_buffer = bytes_in_buffer; + + state->get_buffer = get_buffer; + + state->bits_left = bits_left; + + + + return TRUE; + +} + + + + + +/* + + * Out-of-line code for Huffman code decoding. + + * See jdhuff.h for info about usage. + + */ + + + +GLOBAL int + +jpeg_huff_decode (bitread_working_state * state, + + register bit_buf_type get_buffer, register int bits_left, + + d_derived_tbl * htbl, int min_bits) + +{ + + register int l = min_bits; + + register INT32 code; + + + + /* HUFF_DECODE has determined that the code is at least min_bits */ + + /* bits long, so fetch that many bits in one swoop. */ + + + + CHECK_BIT_BUFFER(*state, l, return -1); + + code = GET_BITS(l); + + + + /* Collect the rest of the Huffman code one bit at a time. */ + + /* This is per Figure F.16 in the JPEG spec. */ + + + + while (code > htbl->maxcode[l]) { + + code <<= 1; + + CHECK_BIT_BUFFER(*state, 1, return -1); + + code |= GET_BITS(1); + + l++; + + } + + + + /* Unload the local registers */ + + state->get_buffer = get_buffer; + + state->bits_left = bits_left; + + + + /* With garbage input we may reach the sentinel value l = 17. */ + + + + if (l > 16) { + + WARNMS(state->cinfo, JWRN_HUFF_BAD_CODE); + + return 0; /* fake a zero as the safest result */ + + } + + + + return htbl->pub->huffval[ htbl->valptr[l] + + + ((int) (code - htbl->mincode[l])) ]; + +} + + + + + +/* + + * Figure F.12: extend sign bit. + + * On some machines, a shift and add will be faster than a table lookup. + + */ + + + +#ifdef AVOID_TABLES + + + +#define HUFF_EXTEND(x,s) ((x) < (1<<((s)-1)) ? (x) + (((-1)<<(s)) + 1) : (x)) + + + +#else + + + +#define HUFF_EXTEND(x,s) ((x) < extend_test[s] ? (x) + extend_offset[s] : (x)) + + + +static const int extend_test[16] = /* entry n is 2**(n-1) */ + + { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, + + 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 }; + + + +static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */ + + { 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1, + + ((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1, + + ((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1, + + ((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 }; + + + +#endif /* AVOID_TABLES */ + + + + + +/* + + * Check for a restart marker & resynchronize decoder. + + * Returns FALSE if must suspend. + + */ + + + +LOCAL boolean + +process_restart (j_decompress_ptr cinfo) + +{ + + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + + int ci; + + + + /* Throw away any unused bits remaining in bit buffer; */ + + /* include any full bytes in next_marker's count of discarded bytes */ + + cinfo->marker->discarded_bytes += entropy->bitstate.bits_left / 8; + + entropy->bitstate.bits_left = 0; + + + + /* Advance past the RSTn marker */ + + if (! (*cinfo->marker->read_restart_marker) (cinfo)) + + return FALSE; + + + + /* Re-initialize DC predictions to 0 */ + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) + + entropy->saved.last_dc_val[ci] = 0; + + + + /* Reset restart counter */ + + entropy->restarts_to_go = cinfo->restart_interval; + + + + /* Next segment can get another out-of-data warning */ + + entropy->bitstate.printed_eod = FALSE; + + + + return TRUE; + +} + + + + + +/* + + * Decode and return one MCU's worth of Huffman-compressed coefficients. + + * The coefficients are reordered from zigzag order into natural array order, + + * but are not dequantized. + + * + + * The i'th block of the MCU is stored into the block pointed to by + + * MCU_data[i]. WE ASSUME THIS AREA HAS BEEN ZEROED BY THE CALLER. + + * (Wholesale zeroing is usually a little faster than retail...) + + * + + * Returns FALSE if data source requested suspension. In that case no + + * changes have been made to permanent state. (Exception: some output + + * coefficients may already have been assigned. This is harmless for + + * this module, since we'll just re-assign them on the next call.) + + */ + + + +METHODDEF boolean + +decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) + +{ + + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + + register int s, k, r; + + int blkn, ci; + + JBLOCKROW block; + + BITREAD_STATE_VARS; + + savable_state state; + + d_derived_tbl * dctbl; + + d_derived_tbl * actbl; + + jpeg_component_info * compptr; + + + + /* Process restart marker if needed; may have to suspend */ + + if (cinfo->restart_interval) { + + if (entropy->restarts_to_go == 0) + + if (! process_restart(cinfo)) + + return FALSE; + + } + + + + /* Load up working state */ + + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + + ASSIGN_STATE(state, entropy->saved); + + + + /* Outer loop handles each block in the MCU */ + + + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + + block = MCU_data[blkn]; + + ci = cinfo->MCU_membership[blkn]; + + compptr = cinfo->cur_comp_info[ci]; + + dctbl = entropy->dc_derived_tbls[compptr->dc_tbl_no]; + + actbl = entropy->ac_derived_tbls[compptr->ac_tbl_no]; + + + + /* Decode a single block's worth of coefficients */ + + + + /* Section F.2.2.1: decode the DC coefficient difference */ + + HUFF_DECODE(s, br_state, dctbl, return FALSE, label1); + + if (s) { + + CHECK_BIT_BUFFER(br_state, s, return FALSE); + + r = GET_BITS(s); + + s = HUFF_EXTEND(r, s); + + } + + + + /* Shortcut if component's values are not interesting */ + + if (! compptr->component_needed) + + goto skip_ACs; + + + + /* Convert DC difference to actual value, update last_dc_val */ + + s += state.last_dc_val[ci]; + + state.last_dc_val[ci] = s; + + /* Output the DC coefficient (assumes jpeg_natural_order[0] = 0) */ + + (*block)[0] = (JCOEF) s; + + + + /* Do we need to decode the AC coefficients for this component? */ + + if (compptr->DCT_scaled_size > 1) { + + + + /* Section F.2.2.2: decode the AC coefficients */ + + /* Since zeroes are skipped, output area must be cleared beforehand */ + + for (k = 1; k < DCTSIZE2; k++) { + + HUFF_DECODE(s, br_state, actbl, return FALSE, label2); + + + + r = s >> 4; + + s &= 15; + + + + if (s) { + + k += r; + + CHECK_BIT_BUFFER(br_state, s, return FALSE); + + r = GET_BITS(s); + + s = HUFF_EXTEND(r, s); + + /* Output coefficient in natural (dezigzagged) order. + + * Note: the extra entries in jpeg_natural_order[] will save us + + * if k >= DCTSIZE2, which could happen if the data is corrupted. + + */ + + (*block)[jpeg_natural_order[k]] = (JCOEF) s; + + } else { + + if (r != 15) + + break; + + k += 15; + + } + + } + + + + } else { + +skip_ACs: + + + + /* Section F.2.2.2: decode the AC coefficients */ + + /* In this path we just discard the values */ + + for (k = 1; k < DCTSIZE2; k++) { + + HUFF_DECODE(s, br_state, actbl, return FALSE, label3); + + + + r = s >> 4; + + s &= 15; + + + + if (s) { + + k += r; + + CHECK_BIT_BUFFER(br_state, s, return FALSE); + + DROP_BITS(s); + + } else { + + if (r != 15) + + break; + + k += 15; + + } + + } + + + + } + + } + + + + /* Completed MCU, so update state */ + + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + + ASSIGN_STATE(entropy->saved, state); + + + + /* Account for restart interval (no-op if not using restarts) */ + + entropy->restarts_to_go--; + + + + return TRUE; + +} + + + + + +/* + + * Module initialization routine for Huffman entropy decoding. + + */ + + + +GLOBAL void + +jinit_huff_decoder (j_decompress_ptr cinfo) + +{ + + huff_entropy_ptr entropy; + + int i; + + + + entropy = (huff_entropy_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(huff_entropy_decoder)); + + cinfo->entropy = (struct jpeg_entropy_decoder *) entropy; + + entropy->pub.start_pass = start_pass_huff_decoder; + + entropy->pub.decode_mcu = decode_mcu; + + + + /* Mark tables unallocated */ + + for (i = 0; i < NUM_HUFF_TBLS; i++) { + + entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL; + + } + +} + diff --git a/utils/roq2/jpeg/jdhuff.h b/utils/roq2/jpeg/jdhuff.h new file mode 100644 index 0000000..5e3132e --- /dev/null +++ b/utils/roq2/jpeg/jdhuff.h @@ -0,0 +1,404 @@ +/* + + * jdhuff.h + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains declarations for Huffman entropy decoding routines + + * that are shared between the sequential decoder (jdhuff.c) and the + + * progressive decoder (jdphuff.c). No other modules need to see these. + + */ + + + +/* Short forms of external names for systems with brain-damaged linkers. */ + + + +#ifdef NEED_SHORT_EXTERNAL_NAMES + +#define jpeg_make_d_derived_tbl jMkDDerived + +#define jpeg_fill_bit_buffer jFilBitBuf + +#define jpeg_huff_decode jHufDecode + +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + + + + +/* Derived data constructed for each Huffman table */ + + + +#define HUFF_LOOKAHEAD 8 /* # of bits of lookahead */ + + + +typedef struct { + + /* Basic tables: (element [0] of each array is unused) */ + + INT32 mincode[17]; /* smallest code of length k */ + + INT32 maxcode[18]; /* largest code of length k (-1 if none) */ + + /* (maxcode[17] is a sentinel to ensure jpeg_huff_decode terminates) */ + + int valptr[17]; /* huffval[] index of 1st symbol of length k */ + + + + /* Link to public Huffman table (needed only in jpeg_huff_decode) */ + + JHUFF_TBL *pub; + + + + /* Lookahead tables: indexed by the next HUFF_LOOKAHEAD bits of + + * the input data stream. If the next Huffman code is no more + + * than HUFF_LOOKAHEAD bits long, we can obtain its length and + + * the corresponding symbol directly from these tables. + + */ + + int look_nbits[1< 32 bits on your machine, and shifting/masking longs is + + * reasonably fast, making bit_buf_type be long and setting BIT_BUF_SIZE + + * appropriately should be a win. Unfortunately we can't do this with + + * something like #define BIT_BUF_SIZE (sizeof(bit_buf_type)*8) + + * because not all machines measure sizeof in 8-bit bytes. + + */ + + + +typedef struct { /* Bitreading state saved across MCUs */ + + bit_buf_type get_buffer; /* current bit-extraction buffer */ + + int bits_left; /* # of unused bits in it */ + + boolean printed_eod; /* flag to suppress multiple warning msgs */ + +} bitread_perm_state; + + + +typedef struct { /* Bitreading working state within an MCU */ + + /* current data source state */ + + const JOCTET * next_input_byte; /* => next byte to read from source */ + + size_t bytes_in_buffer; /* # of bytes remaining in source buffer */ + + int unread_marker; /* nonzero if we have hit a marker */ + + /* bit input buffer --- note these values are kept in register variables, + + * not in this struct, inside the inner loops. + + */ + + bit_buf_type get_buffer; /* current bit-extraction buffer */ + + int bits_left; /* # of unused bits in it */ + + /* pointers needed by jpeg_fill_bit_buffer */ + + j_decompress_ptr cinfo; /* back link to decompress master record */ + + boolean * printed_eod_ptr; /* => flag in permanent state */ + +} bitread_working_state; + + + +/* Macros to declare and load/save bitread local variables. */ + +#define BITREAD_STATE_VARS \ + + register bit_buf_type get_buffer; \ + + register int bits_left; \ + + bitread_working_state br_state + + + +#define BITREAD_LOAD_STATE(cinfop,permstate) \ + + br_state.cinfo = cinfop; \ + + br_state.next_input_byte = cinfop->src->next_input_byte; \ + + br_state.bytes_in_buffer = cinfop->src->bytes_in_buffer; \ + + br_state.unread_marker = cinfop->unread_marker; \ + + get_buffer = permstate.get_buffer; \ + + bits_left = permstate.bits_left; \ + + br_state.printed_eod_ptr = & permstate.printed_eod + + + +#define BITREAD_SAVE_STATE(cinfop,permstate) \ + + cinfop->src->next_input_byte = br_state.next_input_byte; \ + + cinfop->src->bytes_in_buffer = br_state.bytes_in_buffer; \ + + cinfop->unread_marker = br_state.unread_marker; \ + + permstate.get_buffer = get_buffer; \ + + permstate.bits_left = bits_left + + + +/* + + * These macros provide the in-line portion of bit fetching. + + * Use CHECK_BIT_BUFFER to ensure there are N bits in get_buffer + + * before using GET_BITS, PEEK_BITS, or DROP_BITS. + + * The variables get_buffer and bits_left are assumed to be locals, + + * but the state struct might not be (jpeg_huff_decode needs this). + + * CHECK_BIT_BUFFER(state,n,action); + + * Ensure there are N bits in get_buffer; if suspend, take action. + + * val = GET_BITS(n); + + * Fetch next N bits. + + * val = PEEK_BITS(n); + + * Fetch next N bits without removing them from the buffer. + + * DROP_BITS(n); + + * Discard next N bits. + + * The value N should be a simple variable, not an expression, because it + + * is evaluated multiple times. + + */ + + + +#define CHECK_BIT_BUFFER(state,nbits,action) \ + + { if (bits_left < (nbits)) { \ + + if (! jpeg_fill_bit_buffer(&(state),get_buffer,bits_left,nbits)) \ + + { action; } \ + + get_buffer = (state).get_buffer; bits_left = (state).bits_left; } } + + + +#define GET_BITS(nbits) \ + + (((int) (get_buffer >> (bits_left -= (nbits)))) & ((1<<(nbits))-1)) + + + +#define PEEK_BITS(nbits) \ + + (((int) (get_buffer >> (bits_left - (nbits)))) & ((1<<(nbits))-1)) + + + +#define DROP_BITS(nbits) \ + + (bits_left -= (nbits)) + + + +/* Load up the bit buffer to a depth of at least nbits */ + +EXTERN boolean jpeg_fill_bit_buffer JPP((bitread_working_state * state, + + register bit_buf_type get_buffer, register int bits_left, + + int nbits)); + + + + + +/* + + * Code for extracting next Huffman-coded symbol from input bit stream. + + * Again, this is time-critical and we make the main paths be macros. + + * + + * We use a lookahead table to process codes of up to HUFF_LOOKAHEAD bits + + * without looping. Usually, more than 95% of the Huffman codes will be 8 + + * or fewer bits long. The few overlength codes are handled with a loop, + + * which need not be inline code. + + * + + * Notes about the HUFF_DECODE macro: + + * 1. Near the end of the data segment, we may fail to get enough bits + + * for a lookahead. In that case, we do it the hard way. + + * 2. If the lookahead table contains no entry, the next code must be + + * more than HUFF_LOOKAHEAD bits long. + + * 3. jpeg_huff_decode returns -1 if forced to suspend. + + */ + + + +#define HUFF_DECODE(result,state,htbl,failaction,slowlabel) \ + +{ register int nb, look; \ + + if (bits_left < HUFF_LOOKAHEAD) { \ + + if (! jpeg_fill_bit_buffer(&state,get_buffer,bits_left, 0)) {failaction;} \ + + get_buffer = state.get_buffer; bits_left = state.bits_left; \ + + if (bits_left < HUFF_LOOKAHEAD) { \ + + nb = 1; goto slowlabel; \ + + } \ + + } \ + + look = PEEK_BITS(HUFF_LOOKAHEAD); \ + + if ((nb = htbl->look_nbits[look]) != 0) { \ + + DROP_BITS(nb); \ + + result = htbl->look_sym[look]; \ + + } else { \ + + nb = HUFF_LOOKAHEAD+1; \ + +slowlabel: \ + + if ((result=jpeg_huff_decode(&state,get_buffer,bits_left,htbl,nb)) < 0) \ + + { failaction; } \ + + get_buffer = state.get_buffer; bits_left = state.bits_left; \ + + } \ + +} + + + +/* Out-of-line case for Huffman code fetching */ + +EXTERN int jpeg_huff_decode JPP((bitread_working_state * state, + + register bit_buf_type get_buffer, register int bits_left, + + d_derived_tbl * htbl, int min_bits)); + diff --git a/utils/roq2/jpeg/jdinput.c b/utils/roq2/jpeg/jdinput.c new file mode 100644 index 0000000..3cc8487 --- /dev/null +++ b/utils/roq2/jpeg/jdinput.c @@ -0,0 +1,762 @@ +/* + + * jdinput.c + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains input control logic for the JPEG decompressor. + + * These routines are concerned with controlling the decompressor's input + + * processing (marker reading and coefficient decoding). The actual input + + * reading is done in jdmarker.c, jdhuff.c, and jdphuff.c. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + + + +/* Private state */ + + + +typedef struct { + + struct jpeg_input_controller pub; /* public fields */ + + + + boolean inheaders; /* TRUE until first SOS is reached */ + +} my_input_controller; + + + +typedef my_input_controller * my_inputctl_ptr; + + + + + +/* Forward declarations */ + +METHODDEF int consume_markers JPP((j_decompress_ptr cinfo)); + + + + + +/* + + * Routines to calculate various quantities related to the size of the image. + + */ + + + +LOCAL void + +initial_setup (j_decompress_ptr cinfo) + +/* Called once, when first SOS marker is reached */ + +{ + + int ci; + + jpeg_component_info *compptr; + + + + /* Make sure image isn't bigger than I can handle */ + + if ((long) cinfo->image_height > (long) JPEG_MAX_DIMENSION || + + (long) cinfo->image_width > (long) JPEG_MAX_DIMENSION) + + ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) JPEG_MAX_DIMENSION); + + + + /* For now, precision must match compiled-in value... */ + + if (cinfo->data_precision != BITS_IN_JSAMPLE) + + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + + + + /* Check that number of components won't exceed internal array sizes */ + + if (cinfo->num_components > MAX_COMPONENTS) + + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, + + MAX_COMPONENTS); + + + + /* Compute maximum sampling factors; check factor validity */ + + cinfo->max_h_samp_factor = 1; + + cinfo->max_v_samp_factor = 1; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + if (compptr->h_samp_factor<=0 || compptr->h_samp_factor>MAX_SAMP_FACTOR || + + compptr->v_samp_factor<=0 || compptr->v_samp_factor>MAX_SAMP_FACTOR) + + ERREXIT(cinfo, JERR_BAD_SAMPLING); + + cinfo->max_h_samp_factor = MAX(cinfo->max_h_samp_factor, + + compptr->h_samp_factor); + + cinfo->max_v_samp_factor = MAX(cinfo->max_v_samp_factor, + + compptr->v_samp_factor); + + } + + + + /* We initialize DCT_scaled_size and min_DCT_scaled_size to DCTSIZE. + + * In the full decompressor, this will be overridden by jdmaster.c; + + * but in the transcoder, jdmaster.c is not used, so we must do it here. + + */ + + cinfo->min_DCT_scaled_size = DCTSIZE; + + + + /* Compute dimensions of components */ + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + compptr->DCT_scaled_size = DCTSIZE; + + /* Size in DCT blocks */ + + compptr->width_in_blocks = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, + + (long) (cinfo->max_h_samp_factor * DCTSIZE)); + + compptr->height_in_blocks = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, + + (long) (cinfo->max_v_samp_factor * DCTSIZE)); + + /* downsampled_width and downsampled_height will also be overridden by + + * jdmaster.c if we are doing full decompression. The transcoder library + + * doesn't use these values, but the calling application might. + + */ + + /* Size in samples */ + + compptr->downsampled_width = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, + + (long) cinfo->max_h_samp_factor); + + compptr->downsampled_height = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, + + (long) cinfo->max_v_samp_factor); + + /* Mark component needed, until color conversion says otherwise */ + + compptr->component_needed = TRUE; + + /* Mark no quantization table yet saved for component */ + + compptr->quant_table = NULL; + + } + + + + /* Compute number of fully interleaved MCU rows. */ + + cinfo->total_iMCU_rows = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_height, + + (long) (cinfo->max_v_samp_factor*DCTSIZE)); + + + + /* Decide whether file contains multiple scans */ + + if (cinfo->comps_in_scan < cinfo->num_components || cinfo->progressive_mode) + + cinfo->inputctl->has_multiple_scans = TRUE; + + else + + cinfo->inputctl->has_multiple_scans = FALSE; + +} + + + + + +LOCAL void + +per_scan_setup (j_decompress_ptr cinfo) + +/* Do computations that are needed before processing a JPEG scan */ + +/* cinfo->comps_in_scan and cinfo->cur_comp_info[] were set from SOS marker */ + +{ + + int ci, mcublks, tmp; + + jpeg_component_info *compptr; + + + + if (cinfo->comps_in_scan == 1) { + + + + /* Noninterleaved (single-component) scan */ + + compptr = cinfo->cur_comp_info[0]; + + + + /* Overall image size in MCUs */ + + cinfo->MCUs_per_row = compptr->width_in_blocks; + + cinfo->MCU_rows_in_scan = compptr->height_in_blocks; + + + + /* For noninterleaved scan, always one block per MCU */ + + compptr->MCU_width = 1; + + compptr->MCU_height = 1; + + compptr->MCU_blocks = 1; + + compptr->MCU_sample_width = compptr->DCT_scaled_size; + + compptr->last_col_width = 1; + + /* For noninterleaved scans, it is convenient to define last_row_height + + * as the number of block rows present in the last iMCU row. + + */ + + tmp = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + + if (tmp == 0) tmp = compptr->v_samp_factor; + + compptr->last_row_height = tmp; + + + + /* Prepare array describing MCU composition */ + + cinfo->blocks_in_MCU = 1; + + cinfo->MCU_membership[0] = 0; + + + + } else { + + + + /* Interleaved (multi-component) scan */ + + if (cinfo->comps_in_scan <= 0 || cinfo->comps_in_scan > MAX_COMPS_IN_SCAN) + + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->comps_in_scan, + + MAX_COMPS_IN_SCAN); + + + + /* Overall image size in MCUs */ + + cinfo->MCUs_per_row = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_width, + + (long) (cinfo->max_h_samp_factor*DCTSIZE)); + + cinfo->MCU_rows_in_scan = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_height, + + (long) (cinfo->max_v_samp_factor*DCTSIZE)); + + + + cinfo->blocks_in_MCU = 0; + + + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + + compptr = cinfo->cur_comp_info[ci]; + + /* Sampling factors give # of blocks of component in each MCU */ + + compptr->MCU_width = compptr->h_samp_factor; + + compptr->MCU_height = compptr->v_samp_factor; + + compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height; + + compptr->MCU_sample_width = compptr->MCU_width * compptr->DCT_scaled_size; + + /* Figure number of non-dummy blocks in last MCU column & row */ + + tmp = (int) (compptr->width_in_blocks % compptr->MCU_width); + + if (tmp == 0) tmp = compptr->MCU_width; + + compptr->last_col_width = tmp; + + tmp = (int) (compptr->height_in_blocks % compptr->MCU_height); + + if (tmp == 0) tmp = compptr->MCU_height; + + compptr->last_row_height = tmp; + + /* Prepare array describing MCU composition */ + + mcublks = compptr->MCU_blocks; + + if (cinfo->blocks_in_MCU + mcublks > D_MAX_BLOCKS_IN_MCU) + + ERREXIT(cinfo, JERR_BAD_MCU_SIZE); + + while (mcublks-- > 0) { + + cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci; + + } + + } + + + + } + +} + + + + + +/* + + * Save away a copy of the Q-table referenced by each component present + + * in the current scan, unless already saved during a prior scan. + + * + + * In a multiple-scan JPEG file, the encoder could assign different components + + * the same Q-table slot number, but change table definitions between scans + + * so that each component uses a different Q-table. (The IJG encoder is not + + * currently capable of doing this, but other encoders might.) Since we want + + * to be able to dequantize all the components at the end of the file, this + + * means that we have to save away the table actually used for each component. + + * We do this by copying the table at the start of the first scan containing + + * the component. + + * The JPEG spec prohibits the encoder from changing the contents of a Q-table + + * slot between scans of a component using that slot. If the encoder does so + + * anyway, this decoder will simply use the Q-table values that were current + + * at the start of the first scan for the component. + + * + + * The decompressor output side looks only at the saved quant tables, + + * not at the current Q-table slots. + + */ + + + +LOCAL void + +latch_quant_tables (j_decompress_ptr cinfo) + +{ + + int ci, qtblno; + + jpeg_component_info *compptr; + + JQUANT_TBL * qtbl; + + + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + + compptr = cinfo->cur_comp_info[ci]; + + /* No work if we already saved Q-table for this component */ + + if (compptr->quant_table != NULL) + + continue; + + /* Make sure specified quantization table is present */ + + qtblno = compptr->quant_tbl_no; + + if (qtblno < 0 || qtblno >= NUM_QUANT_TBLS || + + cinfo->quant_tbl_ptrs[qtblno] == NULL) + + ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, qtblno); + + /* OK, save away the quantization table */ + + qtbl = (JQUANT_TBL *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(JQUANT_TBL)); + + MEMCOPY(qtbl, cinfo->quant_tbl_ptrs[qtblno], SIZEOF(JQUANT_TBL)); + + compptr->quant_table = qtbl; + + } + +} + + + + + +/* + + * Initialize the input modules to read a scan of compressed data. + + * The first call to this is done by jdmaster.c after initializing + + * the entire decompressor (during jpeg_start_decompress). + + * Subsequent calls come from consume_markers, below. + + */ + + + +METHODDEF void + +start_input_pass (j_decompress_ptr cinfo) + +{ + + per_scan_setup(cinfo); + + latch_quant_tables(cinfo); + + (*cinfo->entropy->start_pass) (cinfo); + + (*cinfo->coef->start_input_pass) (cinfo); + + cinfo->inputctl->consume_input = cinfo->coef->consume_data; + +} + + + + + +/* + + * Finish up after inputting a compressed-data scan. + + * This is called by the coefficient controller after it's read all + + * the expected data of the scan. + + */ + + + +METHODDEF void + +finish_input_pass (j_decompress_ptr cinfo) + +{ + + cinfo->inputctl->consume_input = consume_markers; + +} + + + + + +/* + + * Read JPEG markers before, between, or after compressed-data scans. + + * Change state as necessary when a new scan is reached. + + * Return value is JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + + * + + * The consume_input method pointer points either here or to the + + * coefficient controller's consume_data routine, depending on whether + + * we are reading a compressed data segment or inter-segment markers. + + */ + + + +METHODDEF int + +consume_markers (j_decompress_ptr cinfo) + +{ + + my_inputctl_ptr inputctl = (my_inputctl_ptr) cinfo->inputctl; + + int val; + + + + if (inputctl->pub.eoi_reached) /* After hitting EOI, read no further */ + + return JPEG_REACHED_EOI; + + + + val = (*cinfo->marker->read_markers) (cinfo); + + + + switch (val) { + + case JPEG_REACHED_SOS: /* Found SOS */ + + if (inputctl->inheaders) { /* 1st SOS */ + + initial_setup(cinfo); + + inputctl->inheaders = FALSE; + + /* Note: start_input_pass must be called by jdmaster.c + + * before any more input can be consumed. jdapi.c is + + * responsible for enforcing this sequencing. + + */ + + } else { /* 2nd or later SOS marker */ + + if (! inputctl->pub.has_multiple_scans) + + ERREXIT(cinfo, JERR_EOI_EXPECTED); /* Oops, I wasn't expecting this! */ + + start_input_pass(cinfo); + + } + + break; + + case JPEG_REACHED_EOI: /* Found EOI */ + + inputctl->pub.eoi_reached = TRUE; + + if (inputctl->inheaders) { /* Tables-only datastream, apparently */ + + if (cinfo->marker->saw_SOF) + + ERREXIT(cinfo, JERR_SOF_NO_SOS); + + } else { + + /* Prevent infinite loop in coef ctlr's decompress_data routine + + * if user set output_scan_number larger than number of scans. + + */ + + if (cinfo->output_scan_number > cinfo->input_scan_number) + + cinfo->output_scan_number = cinfo->input_scan_number; + + } + + break; + + case JPEG_SUSPENDED: + + break; + + } + + + + return val; + +} + + + + + +/* + + * Reset state to begin a fresh datastream. + + */ + + + +METHODDEF void + +reset_input_controller (j_decompress_ptr cinfo) + +{ + + my_inputctl_ptr inputctl = (my_inputctl_ptr) cinfo->inputctl; + + + + inputctl->pub.consume_input = consume_markers; + + inputctl->pub.has_multiple_scans = FALSE; /* "unknown" would be better */ + + inputctl->pub.eoi_reached = FALSE; + + inputctl->inheaders = TRUE; + + /* Reset other modules */ + + (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo); + + (*cinfo->marker->reset_marker_reader) (cinfo); + + /* Reset progression state -- would be cleaner if entropy decoder did this */ + + cinfo->coef_bits = NULL; + +} + + + + + +/* + + * Initialize the input controller module. + + * This is called only once, when the decompression object is created. + + */ + + + +GLOBAL void + +jinit_input_controller (j_decompress_ptr cinfo) + +{ + + my_inputctl_ptr inputctl; + + + + /* Create subobject in permanent pool */ + + inputctl = (my_inputctl_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + + SIZEOF(my_input_controller)); + + cinfo->inputctl = (struct jpeg_input_controller *) inputctl; + + /* Initialize method pointers */ + + inputctl->pub.consume_input = consume_markers; + + inputctl->pub.reset_input_controller = reset_input_controller; + + inputctl->pub.start_input_pass = start_input_pass; + + inputctl->pub.finish_input_pass = finish_input_pass; + + /* Initialize state: can't use reset_input_controller since we don't + + * want to try to reset other modules yet. + + */ + + inputctl->pub.has_multiple_scans = FALSE; /* "unknown" would be better */ + + inputctl->pub.eoi_reached = FALSE; + + inputctl->inheaders = TRUE; + +} + diff --git a/utils/roq2/jpeg/jdmainct.c b/utils/roq2/jpeg/jdmainct.c new file mode 100644 index 0000000..b00f22b --- /dev/null +++ b/utils/roq2/jpeg/jdmainct.c @@ -0,0 +1,1024 @@ +/* + + * jdmainct.c + + * + + * Copyright (C) 1994-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains the main buffer controller for decompression. + + * The main buffer lies between the JPEG decompressor proper and the + + * post-processor; it holds downsampled data in the JPEG colorspace. + + * + + * Note that this code is bypassed in raw-data mode, since the application + + * supplies the equivalent of the main buffer in that case. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + + + +/* + + * In the current system design, the main buffer need never be a full-image + + * buffer; any full-height buffers will be found inside the coefficient or + + * postprocessing controllers. Nonetheless, the main controller is not + + * trivial. Its responsibility is to provide context rows for upsampling/ + + * rescaling, and doing this in an efficient fashion is a bit tricky. + + * + + * Postprocessor input data is counted in "row groups". A row group + + * is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) + + * sample rows of each component. (We require DCT_scaled_size values to be + + * chosen such that these numbers are integers. In practice DCT_scaled_size + + * values will likely be powers of two, so we actually have the stronger + + * condition that DCT_scaled_size / min_DCT_scaled_size is an integer.) + + * Upsampling will typically produce max_v_samp_factor pixel rows from each + + * row group (times any additional scale factor that the upsampler is + + * applying). + + * + + * The coefficient controller will deliver data to us one iMCU row at a time; + + * each iMCU row contains v_samp_factor * DCT_scaled_size sample rows, or + + * exactly min_DCT_scaled_size row groups. (This amount of data corresponds + + * to one row of MCUs when the image is fully interleaved.) Note that the + + * number of sample rows varies across components, but the number of row + + * groups does not. Some garbage sample rows may be included in the last iMCU + + * row at the bottom of the image. + + * + + * Depending on the vertical scaling algorithm used, the upsampler may need + + * access to the sample row(s) above and below its current input row group. + + * The upsampler is required to set need_context_rows TRUE at global selection + + * time if so. When need_context_rows is FALSE, this controller can simply + + * obtain one iMCU row at a time from the coefficient controller and dole it + + * out as row groups to the postprocessor. + + * + + * When need_context_rows is TRUE, this controller guarantees that the buffer + + * passed to postprocessing contains at least one row group's worth of samples + + * above and below the row group(s) being processed. Note that the context + + * rows "above" the first passed row group appear at negative row offsets in + + * the passed buffer. At the top and bottom of the image, the required + + * context rows are manufactured by duplicating the first or last real sample + + * row; this avoids having special cases in the upsampling inner loops. + + * + + * The amount of context is fixed at one row group just because that's a + + * convenient number for this controller to work with. The existing + + * upsamplers really only need one sample row of context. An upsampler + + * supporting arbitrary output rescaling might wish for more than one row + + * group of context when shrinking the image; tough, we don't handle that. + + * (This is justified by the assumption that downsizing will be handled mostly + + * by adjusting the DCT_scaled_size values, so that the actual scale factor at + + * the upsample step needn't be much less than one.) + + * + + * To provide the desired context, we have to retain the last two row groups + + * of one iMCU row while reading in the next iMCU row. (The last row group + + * can't be processed until we have another row group for its below-context, + + * and so we have to save the next-to-last group too for its above-context.) + + * We could do this most simply by copying data around in our buffer, but + + * that'd be very slow. We can avoid copying any data by creating a rather + + * strange pointer structure. Here's how it works. We allocate a workspace + + * consisting of M+2 row groups (where M = min_DCT_scaled_size is the number + + * of row groups per iMCU row). We create two sets of redundant pointers to + + * the workspace. Labeling the physical row groups 0 to M+1, the synthesized + + * pointer lists look like this: + + * M+1 M-1 + + * master pointer --> 0 master pointer --> 0 + + * 1 1 + + * ... ... + + * M-3 M-3 + + * M-2 M + + * M-1 M+1 + + * M M-2 + + * M+1 M-1 + + * 0 0 + + * We read alternate iMCU rows using each master pointer; thus the last two + + * row groups of the previous iMCU row remain un-overwritten in the workspace. + + * The pointer lists are set up so that the required context rows appear to + + * be adjacent to the proper places when we pass the pointer lists to the + + * upsampler. + + * + + * The above pictures describe the normal state of the pointer lists. + + * At top and bottom of the image, we diddle the pointer lists to duplicate + + * the first or last sample row as necessary (this is cheaper than copying + + * sample rows around). + + * + + * This scheme breaks down if M < 2, ie, min_DCT_scaled_size is 1. In that + + * situation each iMCU row provides only one row group so the buffering logic + + * must be different (eg, we must read two iMCU rows before we can emit the + + * first row group). For now, we simply do not support providing context + + * rows when min_DCT_scaled_size is 1. That combination seems unlikely to + + * be worth providing --- if someone wants a 1/8th-size preview, they probably + + * want it quick and dirty, so a context-free upsampler is sufficient. + + */ + + + + + +/* Private buffer controller object */ + + + +typedef struct { + + struct jpeg_d_main_controller pub; /* public fields */ + + + + /* Pointer to allocated workspace (M or M+2 row groups). */ + + JSAMPARRAY buffer[MAX_COMPONENTS]; + + + + boolean buffer_full; /* Have we gotten an iMCU row from decoder? */ + + JDIMENSION rowgroup_ctr; /* counts row groups output to postprocessor */ + + + + /* Remaining fields are only used in the context case. */ + + + + /* These are the master pointers to the funny-order pointer lists. */ + + JSAMPIMAGE xbuffer[2]; /* pointers to weird pointer lists */ + + + + int whichptr; /* indicates which pointer set is now in use */ + + int context_state; /* process_data state machine status */ + + JDIMENSION rowgroups_avail; /* row groups available to postprocessor */ + + JDIMENSION iMCU_row_ctr; /* counts iMCU rows to detect image top/bot */ + +} my_main_controller; + + + +typedef my_main_controller * my_main_ptr; + + + +/* context_state values: */ + +#define CTX_PREPARE_FOR_IMCU 0 /* need to prepare for MCU row */ + +#define CTX_PROCESS_IMCU 1 /* feeding iMCU to postprocessor */ + +#define CTX_POSTPONED_ROW 2 /* feeding postponed row group */ + + + + + +/* Forward declarations */ + +METHODDEF void process_data_simple_main + + JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf, + + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)); + +METHODDEF void process_data_context_main + + JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf, + + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)); + +#ifdef QUANT_2PASS_SUPPORTED + +METHODDEF void process_data_crank_post + + JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf, + + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)); + +#endif + + + + + +LOCAL void + +alloc_funny_pointers (j_decompress_ptr cinfo) + +/* Allocate space for the funny pointer lists. + + * This is done only once, not once per pass. + + */ + +{ + + my_main_ptr main = (my_main_ptr) cinfo->main; + + int ci, rgroup; + + int M = cinfo->min_DCT_scaled_size; + + jpeg_component_info *compptr; + + JSAMPARRAY xbuf; + + + + /* Get top-level space for component array pointers. + + * We alloc both arrays with one call to save a few cycles. + + */ + + main->xbuffer[0] = (JSAMPIMAGE) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + cinfo->num_components * 2 * SIZEOF(JSAMPARRAY)); + + main->xbuffer[1] = main->xbuffer[0] + cinfo->num_components; + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + + /* Get space for pointer lists --- M+4 row groups in each list. + + * We alloc both pointer lists with one call to save a few cycles. + + */ + + xbuf = (JSAMPARRAY) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + 2 * (rgroup * (M + 4)) * SIZEOF(JSAMPROW)); + + xbuf += rgroup; /* want one row group at negative offsets */ + + main->xbuffer[0][ci] = xbuf; + + xbuf += rgroup * (M + 4); + + main->xbuffer[1][ci] = xbuf; + + } + +} + + + + + +LOCAL void + +make_funny_pointers (j_decompress_ptr cinfo) + +/* Create the funny pointer lists discussed in the comments above. + + * The actual workspace is already allocated (in main->buffer), + + * and the space for the pointer lists is allocated too. + + * This routine just fills in the curiously ordered lists. + + * This will be repeated at the beginning of each pass. + + */ + +{ + + my_main_ptr main = (my_main_ptr) cinfo->main; + + int ci, i, rgroup; + + int M = cinfo->min_DCT_scaled_size; + + jpeg_component_info *compptr; + + JSAMPARRAY buf, xbuf0, xbuf1; + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + + xbuf0 = main->xbuffer[0][ci]; + + xbuf1 = main->xbuffer[1][ci]; + + /* First copy the workspace pointers as-is */ + + buf = main->buffer[ci]; + + for (i = 0; i < rgroup * (M + 2); i++) { + + xbuf0[i] = xbuf1[i] = buf[i]; + + } + + /* In the second list, put the last four row groups in swapped order */ + + for (i = 0; i < rgroup * 2; i++) { + + xbuf1[rgroup*(M-2) + i] = buf[rgroup*M + i]; + + xbuf1[rgroup*M + i] = buf[rgroup*(M-2) + i]; + + } + + /* The wraparound pointers at top and bottom will be filled later + + * (see set_wraparound_pointers, below). Initially we want the "above" + + * pointers to duplicate the first actual data line. This only needs + + * to happen in xbuffer[0]. + + */ + + for (i = 0; i < rgroup; i++) { + + xbuf0[i - rgroup] = xbuf0[0]; + + } + + } + +} + + + + + +LOCAL void + +set_wraparound_pointers (j_decompress_ptr cinfo) + +/* Set up the "wraparound" pointers at top and bottom of the pointer lists. + + * This changes the pointer list state from top-of-image to the normal state. + + */ + +{ + + my_main_ptr main = (my_main_ptr) cinfo->main; + + int ci, i, rgroup; + + int M = cinfo->min_DCT_scaled_size; + + jpeg_component_info *compptr; + + JSAMPARRAY xbuf0, xbuf1; + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + + xbuf0 = main->xbuffer[0][ci]; + + xbuf1 = main->xbuffer[1][ci]; + + for (i = 0; i < rgroup; i++) { + + xbuf0[i - rgroup] = xbuf0[rgroup*(M+1) + i]; + + xbuf1[i - rgroup] = xbuf1[rgroup*(M+1) + i]; + + xbuf0[rgroup*(M+2) + i] = xbuf0[i]; + + xbuf1[rgroup*(M+2) + i] = xbuf1[i]; + + } + + } + +} + + + + + +LOCAL void + +set_bottom_pointers (j_decompress_ptr cinfo) + +/* Change the pointer lists to duplicate the last sample row at the bottom + + * of the image. whichptr indicates which xbuffer holds the final iMCU row. + + * Also sets rowgroups_avail to indicate number of nondummy row groups in row. + + */ + +{ + + my_main_ptr main = (my_main_ptr) cinfo->main; + + int ci, i, rgroup, iMCUheight, rows_left; + + jpeg_component_info *compptr; + + JSAMPARRAY xbuf; + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + /* Count sample rows in one iMCU row and in one row group */ + + iMCUheight = compptr->v_samp_factor * compptr->DCT_scaled_size; + + rgroup = iMCUheight / cinfo->min_DCT_scaled_size; + + /* Count nondummy sample rows remaining for this component */ + + rows_left = (int) (compptr->downsampled_height % (JDIMENSION) iMCUheight); + + if (rows_left == 0) rows_left = iMCUheight; + + /* Count nondummy row groups. Should get same answer for each component, + + * so we need only do it once. + + */ + + if (ci == 0) { + + main->rowgroups_avail = (JDIMENSION) ((rows_left-1) / rgroup + 1); + + } + + /* Duplicate the last real sample row rgroup*2 times; this pads out the + + * last partial rowgroup and ensures at least one full rowgroup of context. + + */ + + xbuf = main->xbuffer[main->whichptr][ci]; + + for (i = 0; i < rgroup * 2; i++) { + + xbuf[rows_left + i] = xbuf[rows_left-1]; + + } + + } + +} + + + + + +/* + + * Initialize for a processing pass. + + */ + + + +METHODDEF void + +start_pass_main (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) + +{ + + my_main_ptr main = (my_main_ptr) cinfo->main; + + + + switch (pass_mode) { + + case JBUF_PASS_THRU: + + if (cinfo->upsample->need_context_rows) { + + main->pub.process_data = process_data_context_main; + + make_funny_pointers(cinfo); /* Create the xbuffer[] lists */ + + main->whichptr = 0; /* Read first iMCU row into xbuffer[0] */ + + main->context_state = CTX_PREPARE_FOR_IMCU; + + main->iMCU_row_ctr = 0; + + } else { + + /* Simple case with no context needed */ + + main->pub.process_data = process_data_simple_main; + + } + + main->buffer_full = FALSE; /* Mark buffer empty */ + + main->rowgroup_ctr = 0; + + break; + +#ifdef QUANT_2PASS_SUPPORTED + + case JBUF_CRANK_DEST: + + /* For last pass of 2-pass quantization, just crank the postprocessor */ + + main->pub.process_data = process_data_crank_post; + + break; + +#endif + + default: + + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + break; + + } + +} + + + + + +/* + + * Process some data. + + * This handles the simple case where no context is required. + + */ + + + +METHODDEF void + +process_data_simple_main (j_decompress_ptr cinfo, + + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail) + +{ + + my_main_ptr main = (my_main_ptr) cinfo->main; + + JDIMENSION rowgroups_avail; + + + + /* Read input data if we haven't filled the main buffer yet */ + + if (! main->buffer_full) { + + if (! (*cinfo->coef->decompress_data) (cinfo, main->buffer)) + + return; /* suspension forced, can do nothing more */ + + main->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ + + } + + + + /* There are always min_DCT_scaled_size row groups in an iMCU row. */ + + rowgroups_avail = (JDIMENSION) cinfo->min_DCT_scaled_size; + + /* Note: at the bottom of the image, we may pass extra garbage row groups + + * to the postprocessor. The postprocessor has to check for bottom + + * of image anyway (at row resolution), so no point in us doing it too. + + */ + + + + /* Feed the postprocessor */ + + (*cinfo->post->post_process_data) (cinfo, main->buffer, + + &main->rowgroup_ctr, rowgroups_avail, + + output_buf, out_row_ctr, out_rows_avail); + + + + /* Has postprocessor consumed all the data yet? If so, mark buffer empty */ + + if (main->rowgroup_ctr >= rowgroups_avail) { + + main->buffer_full = FALSE; + + main->rowgroup_ctr = 0; + + } + +} + + + + + +/* + + * Process some data. + + * This handles the case where context rows must be provided. + + */ + + + +METHODDEF void + +process_data_context_main (j_decompress_ptr cinfo, + + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail) + +{ + + my_main_ptr main = (my_main_ptr) cinfo->main; + + + + /* Read input data if we haven't filled the main buffer yet */ + + if (! main->buffer_full) { + + if (! (*cinfo->coef->decompress_data) (cinfo, + + main->xbuffer[main->whichptr])) + + return; /* suspension forced, can do nothing more */ + + main->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ + + main->iMCU_row_ctr++; /* count rows received */ + + } + + + + /* Postprocessor typically will not swallow all the input data it is handed + + * in one call (due to filling the output buffer first). Must be prepared + + * to exit and restart. This switch lets us keep track of how far we got. + + * Note that each case falls through to the next on successful completion. + + */ + + switch (main->context_state) { + + case CTX_POSTPONED_ROW: + + /* Call postprocessor using previously set pointers for postponed row */ + + (*cinfo->post->post_process_data) (cinfo, main->xbuffer[main->whichptr], + + &main->rowgroup_ctr, main->rowgroups_avail, + + output_buf, out_row_ctr, out_rows_avail); + + if (main->rowgroup_ctr < main->rowgroups_avail) + + return; /* Need to suspend */ + + main->context_state = CTX_PREPARE_FOR_IMCU; + + if (*out_row_ctr >= out_rows_avail) + + return; /* Postprocessor exactly filled output buf */ + + /*FALLTHROUGH*/ + + case CTX_PREPARE_FOR_IMCU: + + /* Prepare to process first M-1 row groups of this iMCU row */ + + main->rowgroup_ctr = 0; + + main->rowgroups_avail = (JDIMENSION) (cinfo->min_DCT_scaled_size - 1); + + /* Check for bottom of image: if so, tweak pointers to "duplicate" + + * the last sample row, and adjust rowgroups_avail to ignore padding rows. + + */ + + if (main->iMCU_row_ctr == cinfo->total_iMCU_rows) + + set_bottom_pointers(cinfo); + + main->context_state = CTX_PROCESS_IMCU; + + /*FALLTHROUGH*/ + + case CTX_PROCESS_IMCU: + + /* Call postprocessor using previously set pointers */ + + (*cinfo->post->post_process_data) (cinfo, main->xbuffer[main->whichptr], + + &main->rowgroup_ctr, main->rowgroups_avail, + + output_buf, out_row_ctr, out_rows_avail); + + if (main->rowgroup_ctr < main->rowgroups_avail) + + return; /* Need to suspend */ + + /* After the first iMCU, change wraparound pointers to normal state */ + + if (main->iMCU_row_ctr == 1) + + set_wraparound_pointers(cinfo); + + /* Prepare to load new iMCU row using other xbuffer list */ + + main->whichptr ^= 1; /* 0=>1 or 1=>0 */ + + main->buffer_full = FALSE; + + /* Still need to process last row group of this iMCU row, */ + + /* which is saved at index M+1 of the other xbuffer */ + + main->rowgroup_ctr = (JDIMENSION) (cinfo->min_DCT_scaled_size + 1); + + main->rowgroups_avail = (JDIMENSION) (cinfo->min_DCT_scaled_size + 2); + + main->context_state = CTX_POSTPONED_ROW; + + } + +} + + + + + +/* + + * Process some data. + + * Final pass of two-pass quantization: just call the postprocessor. + + * Source data will be the postprocessor controller's internal buffer. + + */ + + + +#ifdef QUANT_2PASS_SUPPORTED + + + +METHODDEF void + +process_data_crank_post (j_decompress_ptr cinfo, + + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail) + +{ + + (*cinfo->post->post_process_data) (cinfo, (JSAMPIMAGE) NULL, + + (JDIMENSION *) NULL, (JDIMENSION) 0, + + output_buf, out_row_ctr, out_rows_avail); + +} + + + +#endif /* QUANT_2PASS_SUPPORTED */ + + + + + +/* + + * Initialize main buffer controller. + + */ + + + +GLOBAL void + +jinit_d_main_controller (j_decompress_ptr cinfo, boolean need_full_buffer) + +{ + + my_main_ptr main; + + int ci, rgroup, ngroups; + + jpeg_component_info *compptr; + + + + main = (my_main_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(my_main_controller)); + + cinfo->main = (struct jpeg_d_main_controller *) main; + + main->pub.start_pass = start_pass_main; + + + + if (need_full_buffer) /* shouldn't happen */ + + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + + + /* Allocate the workspace. + + * ngroups is the number of row groups we need. + + */ + + if (cinfo->upsample->need_context_rows) { + + if (cinfo->min_DCT_scaled_size < 2) /* unsupported, see comments above */ + + ERREXIT(cinfo, JERR_NOTIMPL); + + alloc_funny_pointers(cinfo); /* Alloc space for xbuffer[] lists */ + + ngroups = cinfo->min_DCT_scaled_size + 2; + + } else { + + ngroups = cinfo->min_DCT_scaled_size; + + } + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + + main->buffer[ci] = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, + + compptr->width_in_blocks * compptr->DCT_scaled_size, + + (JDIMENSION) (rgroup * ngroups)); + + } + +} + diff --git a/utils/roq2/jpeg/jdmarker.c b/utils/roq2/jpeg/jdmarker.c new file mode 100644 index 0000000..98cc9c9 --- /dev/null +++ b/utils/roq2/jpeg/jdmarker.c @@ -0,0 +1,2104 @@ +/* + + * jdmarker.c + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains routines to decode JPEG datastream markers. + + * Most of the complexity arises from our desire to support input + + * suspension: if not all of the data for a marker is available, + + * we must exit back to the application. On resumption, we reprocess + + * the marker. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + + + +typedef enum { /* JPEG marker codes */ + + M_SOF0 = 0xc0, + + M_SOF1 = 0xc1, + + M_SOF2 = 0xc2, + + M_SOF3 = 0xc3, + + + + M_SOF5 = 0xc5, + + M_SOF6 = 0xc6, + + M_SOF7 = 0xc7, + + + + M_JPG = 0xc8, + + M_SOF9 = 0xc9, + + M_SOF10 = 0xca, + + M_SOF11 = 0xcb, + + + + M_SOF13 = 0xcd, + + M_SOF14 = 0xce, + + M_SOF15 = 0xcf, + + + + M_DHT = 0xc4, + + + + M_DAC = 0xcc, + + + + M_RST0 = 0xd0, + + M_RST1 = 0xd1, + + M_RST2 = 0xd2, + + M_RST3 = 0xd3, + + M_RST4 = 0xd4, + + M_RST5 = 0xd5, + + M_RST6 = 0xd6, + + M_RST7 = 0xd7, + + + + M_SOI = 0xd8, + + M_EOI = 0xd9, + + M_SOS = 0xda, + + M_DQT = 0xdb, + + M_DNL = 0xdc, + + M_DRI = 0xdd, + + M_DHP = 0xde, + + M_EXP = 0xdf, + + + + M_APP0 = 0xe0, + + M_APP1 = 0xe1, + + M_APP2 = 0xe2, + + M_APP3 = 0xe3, + + M_APP4 = 0xe4, + + M_APP5 = 0xe5, + + M_APP6 = 0xe6, + + M_APP7 = 0xe7, + + M_APP8 = 0xe8, + + M_APP9 = 0xe9, + + M_APP10 = 0xea, + + M_APP11 = 0xeb, + + M_APP12 = 0xec, + + M_APP13 = 0xed, + + M_APP14 = 0xee, + + M_APP15 = 0xef, + + + + M_JPG0 = 0xf0, + + M_JPG13 = 0xfd, + + M_COM = 0xfe, + + + + M_TEM = 0x01, + + + + M_ERROR = 0x100 + +} JPEG_MARKER; + + + + + +/* + + * Macros for fetching data from the data source module. + + * + + * At all times, cinfo->src->next_input_byte and ->bytes_in_buffer reflect + + * the current restart point; we update them only when we have reached a + + * suitable place to restart if a suspension occurs. + + */ + + + +/* Declare and initialize local copies of input pointer/count */ + +#define INPUT_VARS(cinfo) \ + + struct jpeg_source_mgr * datasrc = (cinfo)->src; \ + + const JOCTET * next_input_byte = datasrc->next_input_byte; \ + + size_t bytes_in_buffer = datasrc->bytes_in_buffer + + + +/* Unload the local copies --- do this only at a restart boundary */ + +#define INPUT_SYNC(cinfo) \ + + ( datasrc->next_input_byte = next_input_byte, \ + + datasrc->bytes_in_buffer = bytes_in_buffer ) + + + +/* Reload the local copies --- seldom used except in MAKE_BYTE_AVAIL */ + +#define INPUT_RELOAD(cinfo) \ + + ( next_input_byte = datasrc->next_input_byte, \ + + bytes_in_buffer = datasrc->bytes_in_buffer ) + + + +/* Internal macro for INPUT_BYTE and INPUT_2BYTES: make a byte available. + + * Note we do *not* do INPUT_SYNC before calling fill_input_buffer, + + * but we must reload the local copies after a successful fill. + + */ + +#define MAKE_BYTE_AVAIL(cinfo,action) \ + + if (bytes_in_buffer == 0) { \ + + if (! (*datasrc->fill_input_buffer) (cinfo)) \ + + { action; } \ + + INPUT_RELOAD(cinfo); \ + + } \ + + bytes_in_buffer-- + + + +/* Read a byte into variable V. + + * If must suspend, take the specified action (typically "return FALSE"). + + */ + +#define INPUT_BYTE(cinfo,V,action) \ + + MAKESTMT( MAKE_BYTE_AVAIL(cinfo,action); \ + + V = GETJOCTET(*next_input_byte++); ) + + + +/* As above, but read two bytes interpreted as an unsigned 16-bit integer. + + * V should be declared unsigned int or perhaps INT32. + + */ + +#define INPUT_2BYTES(cinfo,V,action) \ + + MAKESTMT( MAKE_BYTE_AVAIL(cinfo,action); \ + + V = ((unsigned int) GETJOCTET(*next_input_byte++)) << 8; \ + + MAKE_BYTE_AVAIL(cinfo,action); \ + + V += GETJOCTET(*next_input_byte++); ) + + + + + +/* + + * Routines to process JPEG markers. + + * + + * Entry condition: JPEG marker itself has been read and its code saved + + * in cinfo->unread_marker; input restart point is just after the marker. + + * + + * Exit: if return TRUE, have read and processed any parameters, and have + + * updated the restart point to point after the parameters. + + * If return FALSE, was forced to suspend before reaching end of + + * marker parameters; restart point has not been moved. Same routine + + * will be called again after application supplies more input data. + + * + + * This approach to suspension assumes that all of a marker's parameters can + + * fit into a single input bufferload. This should hold for "normal" + + * markers. Some COM/APPn markers might have large parameter segments, + + * but we use skip_input_data to get past those, and thereby put the problem + + * on the source manager's shoulders. + + * + + * Note that we don't bother to avoid duplicate trace messages if a + + * suspension occurs within marker parameters. Other side effects + + * require more care. + + */ + + + + + +LOCAL boolean + +get_soi (j_decompress_ptr cinfo) + +/* Process an SOI marker */ + +{ + + int i; + + + + TRACEMS(cinfo, 1, JTRC_SOI); + + + + if (cinfo->marker->saw_SOI) + + ERREXIT(cinfo, JERR_SOI_DUPLICATE); + + + + /* Reset all parameters that are defined to be reset by SOI */ + + + + for (i = 0; i < NUM_ARITH_TBLS; i++) { + + cinfo->arith_dc_L[i] = 0; + + cinfo->arith_dc_U[i] = 1; + + cinfo->arith_ac_K[i] = 5; + + } + + cinfo->restart_interval = 0; + + + + /* Set initial assumptions for colorspace etc */ + + + + cinfo->jpeg_color_space = JCS_UNKNOWN; + + cinfo->CCIR601_sampling = FALSE; /* Assume non-CCIR sampling??? */ + + + + cinfo->saw_JFIF_marker = FALSE; + + cinfo->density_unit = 0; /* set default JFIF APP0 values */ + + cinfo->X_density = 1; + + cinfo->Y_density = 1; + + cinfo->saw_Adobe_marker = FALSE; + + cinfo->Adobe_transform = 0; + + + + cinfo->marker->saw_SOI = TRUE; + + + + return TRUE; + +} + + + + + +LOCAL boolean + +get_sof (j_decompress_ptr cinfo, boolean is_prog, boolean is_arith) + +/* Process a SOFn marker */ + +{ + + INT32 length; + + int c, ci; + + jpeg_component_info * compptr; + + INPUT_VARS(cinfo); + + + + cinfo->progressive_mode = is_prog; + + cinfo->arith_code = is_arith; + + + + INPUT_2BYTES(cinfo, length, return FALSE); + + + + INPUT_BYTE(cinfo, cinfo->data_precision, return FALSE); + + INPUT_2BYTES(cinfo, cinfo->image_height, return FALSE); + + INPUT_2BYTES(cinfo, cinfo->image_width, return FALSE); + + INPUT_BYTE(cinfo, cinfo->num_components, return FALSE); + + + + length -= 8; + + + + TRACEMS4(cinfo, 1, JTRC_SOF, cinfo->unread_marker, + + (int) cinfo->image_width, (int) cinfo->image_height, + + cinfo->num_components); + + + + if (cinfo->marker->saw_SOF) + + ERREXIT(cinfo, JERR_SOF_DUPLICATE); + + + + /* We don't support files in which the image height is initially specified */ + + /* as 0 and is later redefined by DNL. As long as we have to check that, */ + + /* might as well have a general sanity check. */ + + if (cinfo->image_height <= 0 || cinfo->image_width <= 0 + + || cinfo->num_components <= 0) + + ERREXIT(cinfo, JERR_EMPTY_IMAGE); + + + + if (length != (cinfo->num_components * 3)) + + ERREXIT(cinfo, JERR_BAD_LENGTH); + + + + if (cinfo->comp_info == NULL) /* do only once, even if suspend */ + + cinfo->comp_info = (jpeg_component_info *) (*cinfo->mem->alloc_small) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, + + cinfo->num_components * SIZEOF(jpeg_component_info)); + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + compptr->component_index = ci; + + INPUT_BYTE(cinfo, compptr->component_id, return FALSE); + + INPUT_BYTE(cinfo, c, return FALSE); + + compptr->h_samp_factor = (c >> 4) & 15; + + compptr->v_samp_factor = (c ) & 15; + + INPUT_BYTE(cinfo, compptr->quant_tbl_no, return FALSE); + + + + TRACEMS4(cinfo, 1, JTRC_SOF_COMPONENT, + + compptr->component_id, compptr->h_samp_factor, + + compptr->v_samp_factor, compptr->quant_tbl_no); + + } + + + + cinfo->marker->saw_SOF = TRUE; + + + + INPUT_SYNC(cinfo); + + return TRUE; + +} + + + + + +LOCAL boolean + +get_sos (j_decompress_ptr cinfo) + +/* Process a SOS marker */ + +{ + + INT32 length; + + int i, ci, n, c, cc; + + jpeg_component_info * compptr; + + INPUT_VARS(cinfo); + + + + if (! cinfo->marker->saw_SOF) + + ERREXIT(cinfo, JERR_SOS_NO_SOF); + + + + INPUT_2BYTES(cinfo, length, return FALSE); + + + + INPUT_BYTE(cinfo, n, return FALSE); /* Number of components */ + + + + if (length != (n * 2 + 6) || n < 1 || n > MAX_COMPS_IN_SCAN) + + ERREXIT(cinfo, JERR_BAD_LENGTH); + + + + TRACEMS1(cinfo, 1, JTRC_SOS, n); + + + + cinfo->comps_in_scan = n; + + + + /* Collect the component-spec parameters */ + + + + for (i = 0; i < n; i++) { + + INPUT_BYTE(cinfo, cc, return FALSE); + + INPUT_BYTE(cinfo, c, return FALSE); + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + if (cc == compptr->component_id) + + goto id_found; + + } + + + + ERREXIT1(cinfo, JERR_BAD_COMPONENT_ID, cc); + + + + id_found: + + + + cinfo->cur_comp_info[i] = compptr; + + compptr->dc_tbl_no = (c >> 4) & 15; + + compptr->ac_tbl_no = (c ) & 15; + + + + TRACEMS3(cinfo, 1, JTRC_SOS_COMPONENT, cc, + + compptr->dc_tbl_no, compptr->ac_tbl_no); + + } + + + + /* Collect the additional scan parameters Ss, Se, Ah/Al. */ + + INPUT_BYTE(cinfo, c, return FALSE); + + cinfo->Ss = c; + + INPUT_BYTE(cinfo, c, return FALSE); + + cinfo->Se = c; + + INPUT_BYTE(cinfo, c, return FALSE); + + cinfo->Ah = (c >> 4) & 15; + + cinfo->Al = (c ) & 15; + + + + TRACEMS4(cinfo, 1, JTRC_SOS_PARAMS, cinfo->Ss, cinfo->Se, + + cinfo->Ah, cinfo->Al); + + + + /* Prepare to scan data & restart markers */ + + cinfo->marker->next_restart_num = 0; + + + + /* Count another SOS marker */ + + cinfo->input_scan_number++; + + + + INPUT_SYNC(cinfo); + + return TRUE; + +} + + + + + +METHODDEF boolean + +get_app0 (j_decompress_ptr cinfo) + +/* Process an APP0 marker */ + +{ + +#define JFIF_LEN 14 + + INT32 length; + + UINT8 b[JFIF_LEN]; + + int buffp; + + INPUT_VARS(cinfo); + + + + INPUT_2BYTES(cinfo, length, return FALSE); + + length -= 2; + + + + /* See if a JFIF APP0 marker is present */ + + + + if (length >= JFIF_LEN) { + + for (buffp = 0; buffp < JFIF_LEN; buffp++) + + INPUT_BYTE(cinfo, b[buffp], return FALSE); + + length -= JFIF_LEN; + + + + if (b[0]==0x4A && b[1]==0x46 && b[2]==0x49 && b[3]==0x46 && b[4]==0) { + + /* Found JFIF APP0 marker: check version */ + + /* Major version must be 1, anything else signals an incompatible change. + + * We used to treat this as an error, but now it's a nonfatal warning, + + * because some bozo at Hijaak couldn't read the spec. + + * Minor version should be 0..2, but process anyway if newer. + + */ + + if (b[5] != 1) + + WARNMS2(cinfo, JWRN_JFIF_MAJOR, b[5], b[6]); + + else if (b[6] > 2) + + TRACEMS2(cinfo, 1, JTRC_JFIF_MINOR, b[5], b[6]); + + /* Save info */ + + cinfo->saw_JFIF_marker = TRUE; + + cinfo->density_unit = b[7]; + + cinfo->X_density = (b[8] << 8) + b[9]; + + cinfo->Y_density = (b[10] << 8) + b[11]; + + TRACEMS3(cinfo, 1, JTRC_JFIF, + + cinfo->X_density, cinfo->Y_density, cinfo->density_unit); + + if (b[12] | b[13]) + + TRACEMS2(cinfo, 1, JTRC_JFIF_THUMBNAIL, b[12], b[13]); + + if (length != ((INT32) b[12] * (INT32) b[13] * (INT32) 3)) + + TRACEMS1(cinfo, 1, JTRC_JFIF_BADTHUMBNAILSIZE, (int) length); + + } else { + + /* Start of APP0 does not match "JFIF" */ + + TRACEMS1(cinfo, 1, JTRC_APP0, (int) length + JFIF_LEN); + + } + + } else { + + /* Too short to be JFIF marker */ + + TRACEMS1(cinfo, 1, JTRC_APP0, (int) length); + + } + + + + INPUT_SYNC(cinfo); + + if (length > 0) /* skip any remaining data -- could be lots */ + + (*cinfo->src->skip_input_data) (cinfo, (long) length); + + + + return TRUE; + +} + + + + + +METHODDEF boolean + +get_app14 (j_decompress_ptr cinfo) + +/* Process an APP14 marker */ + +{ + +#define ADOBE_LEN 12 + + INT32 length; + + UINT8 b[ADOBE_LEN]; + + int buffp; + + unsigned int version, flags0, flags1, transform; + + INPUT_VARS(cinfo); + + + + INPUT_2BYTES(cinfo, length, return FALSE); + + length -= 2; + + + + /* See if an Adobe APP14 marker is present */ + + + + if (length >= ADOBE_LEN) { + + for (buffp = 0; buffp < ADOBE_LEN; buffp++) + + INPUT_BYTE(cinfo, b[buffp], return FALSE); + + length -= ADOBE_LEN; + + + + if (b[0]==0x41 && b[1]==0x64 && b[2]==0x6F && b[3]==0x62 && b[4]==0x65) { + + /* Found Adobe APP14 marker */ + + version = (b[5] << 8) + b[6]; + + flags0 = (b[7] << 8) + b[8]; + + flags1 = (b[9] << 8) + b[10]; + + transform = b[11]; + + TRACEMS4(cinfo, 1, JTRC_ADOBE, version, flags0, flags1, transform); + + cinfo->saw_Adobe_marker = TRUE; + + cinfo->Adobe_transform = (UINT8) transform; + + } else { + + /* Start of APP14 does not match "Adobe" */ + + TRACEMS1(cinfo, 1, JTRC_APP14, (int) length + ADOBE_LEN); + + } + + } else { + + /* Too short to be Adobe marker */ + + TRACEMS1(cinfo, 1, JTRC_APP14, (int) length); + + } + + + + INPUT_SYNC(cinfo); + + if (length > 0) /* skip any remaining data -- could be lots */ + + (*cinfo->src->skip_input_data) (cinfo, (long) length); + + + + return TRUE; + +} + + + + + +LOCAL boolean + +get_dac (j_decompress_ptr cinfo) + +/* Process a DAC marker */ + +{ + + INT32 length; + + int index, val; + + INPUT_VARS(cinfo); + + + + INPUT_2BYTES(cinfo, length, return FALSE); + + length -= 2; + + + + while (length > 0) { + + INPUT_BYTE(cinfo, index, return FALSE); + + INPUT_BYTE(cinfo, val, return FALSE); + + + + length -= 2; + + + + TRACEMS2(cinfo, 1, JTRC_DAC, index, val); + + + + if (index < 0 || index >= (2*NUM_ARITH_TBLS)) + + ERREXIT1(cinfo, JERR_DAC_INDEX, index); + + + + if (index >= NUM_ARITH_TBLS) { /* define AC table */ + + cinfo->arith_ac_K[index-NUM_ARITH_TBLS] = (UINT8) val; + + } else { /* define DC table */ + + cinfo->arith_dc_L[index] = (UINT8) (val & 0x0F); + + cinfo->arith_dc_U[index] = (UINT8) (val >> 4); + + if (cinfo->arith_dc_L[index] > cinfo->arith_dc_U[index]) + + ERREXIT1(cinfo, JERR_DAC_VALUE, val); + + } + + } + + + + INPUT_SYNC(cinfo); + + return TRUE; + +} + + + + + +LOCAL boolean + +get_dht (j_decompress_ptr cinfo) + +/* Process a DHT marker */ + +{ + + INT32 length; + + UINT8 bits[17]; + + UINT8 huffval[256]; + + int i, index, count; + + JHUFF_TBL **htblptr; + + INPUT_VARS(cinfo); + + + + INPUT_2BYTES(cinfo, length, return FALSE); + + length -= 2; + + + + while (length > 0) { + + INPUT_BYTE(cinfo, index, return FALSE); + + + + TRACEMS1(cinfo, 1, JTRC_DHT, index); + + + + bits[0] = 0; + + count = 0; + + for (i = 1; i <= 16; i++) { + + INPUT_BYTE(cinfo, bits[i], return FALSE); + + count += bits[i]; + + } + + + + length -= 1 + 16; + + + + TRACEMS8(cinfo, 2, JTRC_HUFFBITS, + + bits[1], bits[2], bits[3], bits[4], + + bits[5], bits[6], bits[7], bits[8]); + + TRACEMS8(cinfo, 2, JTRC_HUFFBITS, + + bits[9], bits[10], bits[11], bits[12], + + bits[13], bits[14], bits[15], bits[16]); + + + + if (count > 256 || ((INT32) count) > length) + + ERREXIT(cinfo, JERR_DHT_COUNTS); + + + + for (i = 0; i < count; i++) + + INPUT_BYTE(cinfo, huffval[i], return FALSE); + + + + length -= count; + + + + if (index & 0x10) { /* AC table definition */ + + index -= 0x10; + + htblptr = &cinfo->ac_huff_tbl_ptrs[index]; + + } else { /* DC table definition */ + + htblptr = &cinfo->dc_huff_tbl_ptrs[index]; + + } + + + + if (index < 0 || index >= NUM_HUFF_TBLS) + + ERREXIT1(cinfo, JERR_DHT_INDEX, index); + + + + if (*htblptr == NULL) + + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + + + + MEMCOPY((*htblptr)->bits, bits, SIZEOF((*htblptr)->bits)); + + MEMCOPY((*htblptr)->huffval, huffval, SIZEOF((*htblptr)->huffval)); + + } + + + + INPUT_SYNC(cinfo); + + return TRUE; + +} + + + + + +LOCAL boolean + +get_dqt (j_decompress_ptr cinfo) + +/* Process a DQT marker */ + +{ + + INT32 length; + + int n, i, prec; + + unsigned int tmp; + + JQUANT_TBL *quant_ptr; + + INPUT_VARS(cinfo); + + + + INPUT_2BYTES(cinfo, length, return FALSE); + + length -= 2; + + + + while (length > 0) { + + INPUT_BYTE(cinfo, n, return FALSE); + + prec = n >> 4; + + n &= 0x0F; + + + + TRACEMS2(cinfo, 1, JTRC_DQT, n, prec); + + + + if (n >= NUM_QUANT_TBLS) + + ERREXIT1(cinfo, JERR_DQT_INDEX, n); + + + + if (cinfo->quant_tbl_ptrs[n] == NULL) + + cinfo->quant_tbl_ptrs[n] = jpeg_alloc_quant_table((j_common_ptr) cinfo); + + quant_ptr = cinfo->quant_tbl_ptrs[n]; + + + + for (i = 0; i < DCTSIZE2; i++) { + + if (prec) + + INPUT_2BYTES(cinfo, tmp, return FALSE); + + else + + INPUT_BYTE(cinfo, tmp, return FALSE); + + quant_ptr->quantval[i] = (UINT16) tmp; + + } + + + + for (i = 0; i < DCTSIZE2; i += 8) { + + TRACEMS8(cinfo, 2, JTRC_QUANTVALS, + + quant_ptr->quantval[i ], quant_ptr->quantval[i+1], + + quant_ptr->quantval[i+2], quant_ptr->quantval[i+3], + + quant_ptr->quantval[i+4], quant_ptr->quantval[i+5], + + quant_ptr->quantval[i+6], quant_ptr->quantval[i+7]); + + } + + + + length -= DCTSIZE2+1; + + if (prec) length -= DCTSIZE2; + + } + + + + INPUT_SYNC(cinfo); + + return TRUE; + +} + + + + + +LOCAL boolean + +get_dri (j_decompress_ptr cinfo) + +/* Process a DRI marker */ + +{ + + INT32 length; + + unsigned int tmp; + + INPUT_VARS(cinfo); + + + + INPUT_2BYTES(cinfo, length, return FALSE); + + + + if (length != 4) + + ERREXIT(cinfo, JERR_BAD_LENGTH); + + + + INPUT_2BYTES(cinfo, tmp, return FALSE); + + + + TRACEMS1(cinfo, 1, JTRC_DRI, tmp); + + + + cinfo->restart_interval = tmp; + + + + INPUT_SYNC(cinfo); + + return TRUE; + +} + + + + + +METHODDEF boolean + +skip_variable (j_decompress_ptr cinfo) + +/* Skip over an unknown or uninteresting variable-length marker */ + +{ + + INT32 length; + + INPUT_VARS(cinfo); + + + + INPUT_2BYTES(cinfo, length, return FALSE); + + + + TRACEMS2(cinfo, 1, JTRC_MISC_MARKER, cinfo->unread_marker, (int) length); + + + + INPUT_SYNC(cinfo); /* do before skip_input_data */ + + (*cinfo->src->skip_input_data) (cinfo, (long) length - 2L); + + + + return TRUE; + +} + + + + + +/* + + * Find the next JPEG marker, save it in cinfo->unread_marker. + + * Returns FALSE if had to suspend before reaching a marker; + + * in that case cinfo->unread_marker is unchanged. + + * + + * Note that the result might not be a valid marker code, + + * but it will never be 0 or FF. + + */ + + + +LOCAL boolean + +next_marker (j_decompress_ptr cinfo) + +{ + + int c; + + INPUT_VARS(cinfo); + + + + for (;;) { + + INPUT_BYTE(cinfo, c, return FALSE); + + /* Skip any non-FF bytes. + + * This may look a bit inefficient, but it will not occur in a valid file. + + * We sync after each discarded byte so that a suspending data source + + * can discard the byte from its buffer. + + */ + + while (c != 0xFF) { + + cinfo->marker->discarded_bytes++; + + INPUT_SYNC(cinfo); + + INPUT_BYTE(cinfo, c, return FALSE); + + } + + /* This loop swallows any duplicate FF bytes. Extra FFs are legal as + + * pad bytes, so don't count them in discarded_bytes. We assume there + + * will not be so many consecutive FF bytes as to overflow a suspending + + * data source's input buffer. + + */ + + do { + + INPUT_BYTE(cinfo, c, return FALSE); + + } while (c == 0xFF); + + if (c != 0) + + break; /* found a valid marker, exit loop */ + + /* Reach here if we found a stuffed-zero data sequence (FF/00). + + * Discard it and loop back to try again. + + */ + + cinfo->marker->discarded_bytes += 2; + + INPUT_SYNC(cinfo); + + } + + + + if (cinfo->marker->discarded_bytes != 0) { + + WARNMS2(cinfo, JWRN_EXTRANEOUS_DATA, cinfo->marker->discarded_bytes, c); + + cinfo->marker->discarded_bytes = 0; + + } + + + + cinfo->unread_marker = c; + + + + INPUT_SYNC(cinfo); + + return TRUE; + +} + + + + + +LOCAL boolean + +first_marker (j_decompress_ptr cinfo) + +/* Like next_marker, but used to obtain the initial SOI marker. */ + +/* For this marker, we do not allow preceding garbage or fill; otherwise, + + * we might well scan an entire input file before realizing it ain't JPEG. + + * If an application wants to process non-JFIF files, it must seek to the + + * SOI before calling the JPEG library. + + */ + +{ + + int c, c2; + + INPUT_VARS(cinfo); + + + + INPUT_BYTE(cinfo, c, return FALSE); + + INPUT_BYTE(cinfo, c2, return FALSE); + + if (c != 0xFF || c2 != (int) M_SOI) + + ERREXIT2(cinfo, JERR_NO_SOI, c, c2); + + + + cinfo->unread_marker = c2; + + + + INPUT_SYNC(cinfo); + + return TRUE; + +} + + + + + +/* + + * Read markers until SOS or EOI. + + * + + * Returns same codes as are defined for jpeg_consume_input: + + * JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + + */ + + + +METHODDEF int + +read_markers (j_decompress_ptr cinfo) + +{ + + /* Outer loop repeats once for each marker. */ + + for (;;) { + + /* Collect the marker proper, unless we already did. */ + + /* NB: first_marker() enforces the requirement that SOI appear first. */ + + if (cinfo->unread_marker == 0) { + + if (! cinfo->marker->saw_SOI) { + + if (! first_marker(cinfo)) + + return JPEG_SUSPENDED; + + } else { + + if (! next_marker(cinfo)) + + return JPEG_SUSPENDED; + + } + + } + + /* At this point cinfo->unread_marker contains the marker code and the + + * input point is just past the marker proper, but before any parameters. + + * A suspension will cause us to return with this state still true. + + */ + + switch (cinfo->unread_marker) { + + case M_SOI: + + if (! get_soi(cinfo)) + + return JPEG_SUSPENDED; + + break; + + + + case M_SOF0: /* Baseline */ + + case M_SOF1: /* Extended sequential, Huffman */ + + if (! get_sof(cinfo, FALSE, FALSE)) + + return JPEG_SUSPENDED; + + break; + + + + case M_SOF2: /* Progressive, Huffman */ + + if (! get_sof(cinfo, TRUE, FALSE)) + + return JPEG_SUSPENDED; + + break; + + + + case M_SOF9: /* Extended sequential, arithmetic */ + + if (! get_sof(cinfo, FALSE, TRUE)) + + return JPEG_SUSPENDED; + + break; + + + + case M_SOF10: /* Progressive, arithmetic */ + + if (! get_sof(cinfo, TRUE, TRUE)) + + return JPEG_SUSPENDED; + + break; + + + + /* Currently unsupported SOFn types */ + + case M_SOF3: /* Lossless, Huffman */ + + case M_SOF5: /* Differential sequential, Huffman */ + + case M_SOF6: /* Differential progressive, Huffman */ + + case M_SOF7: /* Differential lossless, Huffman */ + + case M_JPG: /* Reserved for JPEG extensions */ + + case M_SOF11: /* Lossless, arithmetic */ + + case M_SOF13: /* Differential sequential, arithmetic */ + + case M_SOF14: /* Differential progressive, arithmetic */ + + case M_SOF15: /* Differential lossless, arithmetic */ + + ERREXIT1(cinfo, JERR_SOF_UNSUPPORTED, cinfo->unread_marker); + + break; + + + + case M_SOS: + + if (! get_sos(cinfo)) + + return JPEG_SUSPENDED; + + cinfo->unread_marker = 0; /* processed the marker */ + + return JPEG_REACHED_SOS; + + + + case M_EOI: + + TRACEMS(cinfo, 1, JTRC_EOI); + + cinfo->unread_marker = 0; /* processed the marker */ + + return JPEG_REACHED_EOI; + + + + case M_DAC: + + if (! get_dac(cinfo)) + + return JPEG_SUSPENDED; + + break; + + + + case M_DHT: + + if (! get_dht(cinfo)) + + return JPEG_SUSPENDED; + + break; + + + + case M_DQT: + + if (! get_dqt(cinfo)) + + return JPEG_SUSPENDED; + + break; + + + + case M_DRI: + + if (! get_dri(cinfo)) + + return JPEG_SUSPENDED; + + break; + + + + case M_APP0: + + case M_APP1: + + case M_APP2: + + case M_APP3: + + case M_APP4: + + case M_APP5: + + case M_APP6: + + case M_APP7: + + case M_APP8: + + case M_APP9: + + case M_APP10: + + case M_APP11: + + case M_APP12: + + case M_APP13: + + case M_APP14: + + case M_APP15: + + if (! (*cinfo->marker->process_APPn[cinfo->unread_marker - (int) M_APP0]) (cinfo)) + + return JPEG_SUSPENDED; + + break; + + + + case M_COM: + + if (! (*cinfo->marker->process_COM) (cinfo)) + + return JPEG_SUSPENDED; + + break; + + + + case M_RST0: /* these are all parameterless */ + + case M_RST1: + + case M_RST2: + + case M_RST3: + + case M_RST4: + + case M_RST5: + + case M_RST6: + + case M_RST7: + + case M_TEM: + + TRACEMS1(cinfo, 1, JTRC_PARMLESS_MARKER, cinfo->unread_marker); + + break; + + + + case M_DNL: /* Ignore DNL ... perhaps the wrong thing */ + + if (! skip_variable(cinfo)) + + return JPEG_SUSPENDED; + + break; + + + + default: /* must be DHP, EXP, JPGn, or RESn */ + + /* For now, we treat the reserved markers as fatal errors since they are + + * likely to be used to signal incompatible JPEG Part 3 extensions. + + * Once the JPEG 3 version-number marker is well defined, this code + + * ought to change! + + */ + + ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, cinfo->unread_marker); + + break; + + } + + /* Successfully processed marker, so reset state variable */ + + cinfo->unread_marker = 0; + + } /* end loop */ + +} + + + + + +/* + + * Read a restart marker, which is expected to appear next in the datastream; + + * if the marker is not there, take appropriate recovery action. + + * Returns FALSE if suspension is required. + + * + + * This is called by the entropy decoder after it has read an appropriate + + * number of MCUs. cinfo->unread_marker may be nonzero if the entropy decoder + + * has already read a marker from the data source. Under normal conditions + + * cinfo->unread_marker will be reset to 0 before returning; if not reset, + + * it holds a marker which the decoder will be unable to read past. + + */ + + + +METHODDEF boolean + +read_restart_marker (j_decompress_ptr cinfo) + +{ + + /* Obtain a marker unless we already did. */ + + /* Note that next_marker will complain if it skips any data. */ + + if (cinfo->unread_marker == 0) { + + if (! next_marker(cinfo)) + + return FALSE; + + } + + + + if (cinfo->unread_marker == + + ((int) M_RST0 + cinfo->marker->next_restart_num)) { + + /* Normal case --- swallow the marker and let entropy decoder continue */ + + TRACEMS1(cinfo, 2, JTRC_RST, cinfo->marker->next_restart_num); + + cinfo->unread_marker = 0; + + } else { + + /* Uh-oh, the restart markers have been messed up. */ + + /* Let the data source manager determine how to resync. */ + + if (! (*cinfo->src->resync_to_restart) (cinfo, + + cinfo->marker->next_restart_num)) + + return FALSE; + + } + + + + /* Update next-restart state */ + + cinfo->marker->next_restart_num = (cinfo->marker->next_restart_num + 1) & 7; + + + + return TRUE; + +} + + + + + +/* + + * This is the default resync_to_restart method for data source managers + + * to use if they don't have any better approach. Some data source managers + + * may be able to back up, or may have additional knowledge about the data + + * which permits a more intelligent recovery strategy; such managers would + + * presumably supply their own resync method. + + * + + * read_restart_marker calls resync_to_restart if it finds a marker other than + + * the restart marker it was expecting. (This code is *not* used unless + + * a nonzero restart interval has been declared.) cinfo->unread_marker is + + * the marker code actually found (might be anything, except 0 or FF). + + * The desired restart marker number (0..7) is passed as a parameter. + + * This routine is supposed to apply whatever error recovery strategy seems + + * appropriate in order to position the input stream to the next data segment. + + * Note that cinfo->unread_marker is treated as a marker appearing before + + * the current data-source input point; usually it should be reset to zero + + * before returning. + + * Returns FALSE if suspension is required. + + * + + * This implementation is substantially constrained by wanting to treat the + + * input as a data stream; this means we can't back up. Therefore, we have + + * only the following actions to work with: + + * 1. Simply discard the marker and let the entropy decoder resume at next + + * byte of file. + + * 2. Read forward until we find another marker, discarding intervening + + * data. (In theory we could look ahead within the current bufferload, + + * without having to discard data if we don't find the desired marker. + + * This idea is not implemented here, in part because it makes behavior + + * dependent on buffer size and chance buffer-boundary positions.) + + * 3. Leave the marker unread (by failing to zero cinfo->unread_marker). + + * This will cause the entropy decoder to process an empty data segment, + + * inserting dummy zeroes, and then we will reprocess the marker. + + * + + * #2 is appropriate if we think the desired marker lies ahead, while #3 is + + * appropriate if the found marker is a future restart marker (indicating + + * that we have missed the desired restart marker, probably because it got + + * corrupted). + + * We apply #2 or #3 if the found marker is a restart marker no more than + + * two counts behind or ahead of the expected one. We also apply #2 if the + + * found marker is not a legal JPEG marker code (it's certainly bogus data). + + * If the found marker is a restart marker more than 2 counts away, we do #1 + + * (too much risk that the marker is erroneous; with luck we will be able to + + * resync at some future point). + + * For any valid non-restart JPEG marker, we apply #3. This keeps us from + + * overrunning the end of a scan. An implementation limited to single-scan + + * files might find it better to apply #2 for markers other than EOI, since + + * any other marker would have to be bogus data in that case. + + */ + + + +GLOBAL boolean + +jpeg_resync_to_restart (j_decompress_ptr cinfo, int desired) + +{ + + int marker = cinfo->unread_marker; + + int action = 1; + + + + /* Always put up a warning. */ + + WARNMS2(cinfo, JWRN_MUST_RESYNC, marker, desired); + + + + /* Outer loop handles repeated decision after scanning forward. */ + + for (;;) { + + if (marker < (int) M_SOF0) + + action = 2; /* invalid marker */ + + else if (marker < (int) M_RST0 || marker > (int) M_RST7) + + action = 3; /* valid non-restart marker */ + + else { + + if (marker == ((int) M_RST0 + ((desired+1) & 7)) || + + marker == ((int) M_RST0 + ((desired+2) & 7))) + + action = 3; /* one of the next two expected restarts */ + + else if (marker == ((int) M_RST0 + ((desired-1) & 7)) || + + marker == ((int) M_RST0 + ((desired-2) & 7))) + + action = 2; /* a prior restart, so advance */ + + else + + action = 1; /* desired restart or too far away */ + + } + + TRACEMS2(cinfo, 4, JTRC_RECOVERY_ACTION, marker, action); + + switch (action) { + + case 1: + + /* Discard marker and let entropy decoder resume processing. */ + + cinfo->unread_marker = 0; + + return TRUE; + + case 2: + + /* Scan to the next marker, and repeat the decision loop. */ + + if (! next_marker(cinfo)) + + return FALSE; + + marker = cinfo->unread_marker; + + break; + + case 3: + + /* Return without advancing past this marker. */ + + /* Entropy decoder will be forced to process an empty segment. */ + + return TRUE; + + } + + } /* end loop */ + +} + + + + + +/* + + * Reset marker processing state to begin a fresh datastream. + + */ + + + +METHODDEF void + +reset_marker_reader (j_decompress_ptr cinfo) + +{ + + cinfo->comp_info = NULL; /* until allocated by get_sof */ + + cinfo->input_scan_number = 0; /* no SOS seen yet */ + + cinfo->unread_marker = 0; /* no pending marker */ + + cinfo->marker->saw_SOI = FALSE; /* set internal state too */ + + cinfo->marker->saw_SOF = FALSE; + + cinfo->marker->discarded_bytes = 0; + +} + + + + + +/* + + * Initialize the marker reader module. + + * This is called only once, when the decompression object is created. + + */ + + + +GLOBAL void + +jinit_marker_reader (j_decompress_ptr cinfo) + +{ + + int i; + + + + /* Create subobject in permanent pool */ + + cinfo->marker = (struct jpeg_marker_reader *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + + SIZEOF(struct jpeg_marker_reader)); + + /* Initialize method pointers */ + + cinfo->marker->reset_marker_reader = reset_marker_reader; + + cinfo->marker->read_markers = read_markers; + + cinfo->marker->read_restart_marker = read_restart_marker; + + cinfo->marker->process_COM = skip_variable; + + for (i = 0; i < 16; i++) + + cinfo->marker->process_APPn[i] = skip_variable; + + cinfo->marker->process_APPn[0] = get_app0; + + cinfo->marker->process_APPn[14] = get_app14; + + /* Reset marker processing state */ + + reset_marker_reader(cinfo); + +} + diff --git a/utils/roq2/jpeg/jdmaster.c b/utils/roq2/jpeg/jdmaster.c new file mode 100644 index 0000000..17ea5a3 --- /dev/null +++ b/utils/roq2/jpeg/jdmaster.c @@ -0,0 +1,1110 @@ +/* + + * jdmaster.c + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains master control logic for the JPEG decompressor. + + * These routines are concerned with selecting the modules to be executed + + * and with determining the number of passes and the work to be done in each + + * pass. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + + + +/* Private state */ + + + +typedef struct { + + struct jpeg_decomp_master pub; /* public fields */ + + + + int pass_number; /* # of passes completed */ + + + + boolean using_merged_upsample; /* TRUE if using merged upsample/cconvert */ + + + + /* Saved references to initialized quantizer modules, + + * in case we need to switch modes. + + */ + + struct jpeg_color_quantizer * quantizer_1pass; + + struct jpeg_color_quantizer * quantizer_2pass; + +} my_decomp_master; + + + +typedef my_decomp_master * my_master_ptr; + + + + + +/* + + * Determine whether merged upsample/color conversion should be used. + + * CRUCIAL: this must match the actual capabilities of jdmerge.c! + + */ + + + +LOCAL boolean + +use_merged_upsample (j_decompress_ptr cinfo) + +{ + +#ifdef UPSAMPLE_MERGING_SUPPORTED + + /* Merging is the equivalent of plain box-filter upsampling */ + + if (cinfo->do_fancy_upsampling || cinfo->CCIR601_sampling) + + return FALSE; + + /* jdmerge.c only supports YCC=>RGB color conversion */ + + if (cinfo->jpeg_color_space != JCS_YCbCr || cinfo->num_components != 3 || + + cinfo->out_color_space != JCS_RGB || + + cinfo->out_color_components != RGB_PIXELSIZE) + + return FALSE; + + /* and it only handles 2h1v or 2h2v sampling ratios */ + + if (cinfo->comp_info[0].h_samp_factor != 2 || + + cinfo->comp_info[1].h_samp_factor != 1 || + + cinfo->comp_info[2].h_samp_factor != 1 || + + cinfo->comp_info[0].v_samp_factor > 2 || + + cinfo->comp_info[1].v_samp_factor != 1 || + + cinfo->comp_info[2].v_samp_factor != 1) + + return FALSE; + + /* furthermore, it doesn't work if we've scaled the IDCTs differently */ + + if (cinfo->comp_info[0].DCT_scaled_size != cinfo->min_DCT_scaled_size || + + cinfo->comp_info[1].DCT_scaled_size != cinfo->min_DCT_scaled_size || + + cinfo->comp_info[2].DCT_scaled_size != cinfo->min_DCT_scaled_size) + + return FALSE; + + /* ??? also need to test for upsample-time rescaling, when & if supported */ + + return TRUE; /* by golly, it'll work... */ + +#else + + return FALSE; + +#endif + +} + + + + + +/* + + * Compute output image dimensions and related values. + + * NOTE: this is exported for possible use by application. + + * Hence it mustn't do anything that can't be done twice. + + * Also note that it may be called before the master module is initialized! + + */ + + + +GLOBAL void + +jpeg_calc_output_dimensions (j_decompress_ptr cinfo) + +/* Do computations that are needed before master selection phase */ + +{ + + int ci; + + jpeg_component_info *compptr; + + + + /* Prevent application from calling me at wrong times */ + + if (cinfo->global_state != DSTATE_READY) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + + +#ifdef IDCT_SCALING_SUPPORTED + + + + /* Compute actual output image dimensions and DCT scaling choices. */ + + if (cinfo->scale_num * 8 <= cinfo->scale_denom) { + + /* Provide 1/8 scaling */ + + cinfo->output_width = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_width, 8L); + + cinfo->output_height = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_height, 8L); + + cinfo->min_DCT_scaled_size = 1; + + } else if (cinfo->scale_num * 4 <= cinfo->scale_denom) { + + /* Provide 1/4 scaling */ + + cinfo->output_width = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_width, 4L); + + cinfo->output_height = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_height, 4L); + + cinfo->min_DCT_scaled_size = 2; + + } else if (cinfo->scale_num * 2 <= cinfo->scale_denom) { + + /* Provide 1/2 scaling */ + + cinfo->output_width = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_width, 2L); + + cinfo->output_height = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_height, 2L); + + cinfo->min_DCT_scaled_size = 4; + + } else { + + /* Provide 1/1 scaling */ + + cinfo->output_width = cinfo->image_width; + + cinfo->output_height = cinfo->image_height; + + cinfo->min_DCT_scaled_size = DCTSIZE; + + } + + /* In selecting the actual DCT scaling for each component, we try to + + * scale up the chroma components via IDCT scaling rather than upsampling. + + * This saves time if the upsampler gets to use 1:1 scaling. + + * Note this code assumes that the supported DCT scalings are powers of 2. + + */ + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + int ssize = cinfo->min_DCT_scaled_size; + + while (ssize < DCTSIZE && + + (compptr->h_samp_factor * ssize * 2 <= + + cinfo->max_h_samp_factor * cinfo->min_DCT_scaled_size) && + + (compptr->v_samp_factor * ssize * 2 <= + + cinfo->max_v_samp_factor * cinfo->min_DCT_scaled_size)) { + + ssize = ssize * 2; + + } + + compptr->DCT_scaled_size = ssize; + + } + + + + /* Recompute downsampled dimensions of components; + + * application needs to know these if using raw downsampled data. + + */ + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + /* Size in samples, after IDCT scaling */ + + compptr->downsampled_width = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_width * + + (long) (compptr->h_samp_factor * compptr->DCT_scaled_size), + + (long) (cinfo->max_h_samp_factor * DCTSIZE)); + + compptr->downsampled_height = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_height * + + (long) (compptr->v_samp_factor * compptr->DCT_scaled_size), + + (long) (cinfo->max_v_samp_factor * DCTSIZE)); + + } + + + +#else /* !IDCT_SCALING_SUPPORTED */ + + + + /* Hardwire it to "no scaling" */ + + cinfo->output_width = cinfo->image_width; + + cinfo->output_height = cinfo->image_height; + + /* jdinput.c has already initialized DCT_scaled_size to DCTSIZE, + + * and has computed unscaled downsampled_width and downsampled_height. + + */ + + + +#endif /* IDCT_SCALING_SUPPORTED */ + + + + /* Report number of components in selected colorspace. */ + + /* Probably this should be in the color conversion module... */ + + switch (cinfo->out_color_space) { + + case JCS_GRAYSCALE: + + cinfo->out_color_components = 1; + + break; + + case JCS_RGB: + +#if RGB_PIXELSIZE != 3 + + cinfo->out_color_components = RGB_PIXELSIZE; + + break; + +#endif /* else share code with YCbCr */ + + case JCS_YCbCr: + + cinfo->out_color_components = 3; + + break; + + case JCS_CMYK: + + case JCS_YCCK: + + cinfo->out_color_components = 4; + + break; + + default: /* else must be same colorspace as in file */ + + cinfo->out_color_components = cinfo->num_components; + + break; + + } + + cinfo->output_components = (cinfo->quantize_colors ? 1 : + + cinfo->out_color_components); + + + + /* See if upsampler will want to emit more than one row at a time */ + + if (use_merged_upsample(cinfo)) + + cinfo->rec_outbuf_height = cinfo->max_v_samp_factor; + + else + + cinfo->rec_outbuf_height = 1; + +} + + + + + +/* + + * Several decompression processes need to range-limit values to the range + + * 0..MAXJSAMPLE; the input value may fall somewhat outside this range + + * due to noise introduced by quantization, roundoff error, etc. These + + * processes are inner loops and need to be as fast as possible. On most + + * machines, particularly CPUs with pipelines or instruction prefetch, + + * a (subscript-check-less) C table lookup + + * x = sample_range_limit[x]; + + * is faster than explicit tests + + * if (x < 0) x = 0; + + * else if (x > MAXJSAMPLE) x = MAXJSAMPLE; + + * These processes all use a common table prepared by the routine below. + + * + + * For most steps we can mathematically guarantee that the initial value + + * of x is within MAXJSAMPLE+1 of the legal range, so a table running from + + * -(MAXJSAMPLE+1) to 2*MAXJSAMPLE+1 is sufficient. But for the initial + + * limiting step (just after the IDCT), a wildly out-of-range value is + + * possible if the input data is corrupt. To avoid any chance of indexing + + * off the end of memory and getting a bad-pointer trap, we perform the + + * post-IDCT limiting thus: + + * x = range_limit[x & MASK]; + + * where MASK is 2 bits wider than legal sample data, ie 10 bits for 8-bit + + * samples. Under normal circumstances this is more than enough range and + + * a correct output will be generated; with bogus input data the mask will + + * cause wraparound, and we will safely generate a bogus-but-in-range output. + + * For the post-IDCT step, we want to convert the data from signed to unsigned + + * representation by adding CENTERJSAMPLE at the same time that we limit it. + + * So the post-IDCT limiting table ends up looking like this: + + * CENTERJSAMPLE,CENTERJSAMPLE+1,...,MAXJSAMPLE, + + * MAXJSAMPLE (repeat 2*(MAXJSAMPLE+1)-CENTERJSAMPLE times), + + * 0 (repeat 2*(MAXJSAMPLE+1)-CENTERJSAMPLE times), + + * 0,1,...,CENTERJSAMPLE-1 + + * Negative inputs select values from the upper half of the table after + + * masking. + + * + + * We can save some space by overlapping the start of the post-IDCT table + + * with the simpler range limiting table. The post-IDCT table begins at + + * sample_range_limit + CENTERJSAMPLE. + + * + + * Note that the table is allocated in near data space on PCs; it's small + + * enough and used often enough to justify this. + + */ + + + +LOCAL void + +prepare_range_limit_table (j_decompress_ptr cinfo) + +/* Allocate and fill in the sample_range_limit table */ + +{ + + JSAMPLE * table; + + int i; + + + + table = (JSAMPLE *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (5 * (MAXJSAMPLE+1) + CENTERJSAMPLE) * SIZEOF(JSAMPLE)); + + table += (MAXJSAMPLE+1); /* allow negative subscripts of simple table */ + + cinfo->sample_range_limit = table; + + /* First segment of "simple" table: limit[x] = 0 for x < 0 */ + + MEMZERO(table - (MAXJSAMPLE+1), (MAXJSAMPLE+1) * SIZEOF(JSAMPLE)); + + /* Main part of "simple" table: limit[x] = x */ + + for (i = 0; i <= MAXJSAMPLE; i++) + + table[i] = (JSAMPLE) i; + + table += CENTERJSAMPLE; /* Point to where post-IDCT table starts */ + + /* End of simple table, rest of first half of post-IDCT table */ + + for (i = CENTERJSAMPLE; i < 2*(MAXJSAMPLE+1); i++) + + table[i] = MAXJSAMPLE; + + /* Second half of post-IDCT table */ + + MEMZERO(table + (2 * (MAXJSAMPLE+1)), + + (2 * (MAXJSAMPLE+1) - CENTERJSAMPLE) * SIZEOF(JSAMPLE)); + + MEMCOPY(table + (4 * (MAXJSAMPLE+1) - CENTERJSAMPLE), + + cinfo->sample_range_limit, CENTERJSAMPLE * SIZEOF(JSAMPLE)); + +} + + + + + +/* + + * Master selection of decompression modules. + + * This is done once at jpeg_start_decompress time. We determine + + * which modules will be used and give them appropriate initialization calls. + + * We also initialize the decompressor input side to begin consuming data. + + * + + * Since jpeg_read_header has finished, we know what is in the SOF + + * and (first) SOS markers. We also have all the application parameter + + * settings. + + */ + + + +LOCAL void + +master_selection (j_decompress_ptr cinfo) + +{ + + my_master_ptr master = (my_master_ptr) cinfo->master; + + boolean use_c_buffer; + + long samplesperrow; + + JDIMENSION jd_samplesperrow; + + + + /* Initialize dimensions and other stuff */ + + jpeg_calc_output_dimensions(cinfo); + + prepare_range_limit_table(cinfo); + + + + /* Width of an output scanline must be representable as JDIMENSION. */ + + samplesperrow = (long) cinfo->output_width * (long) cinfo->out_color_components; + + jd_samplesperrow = (JDIMENSION) samplesperrow; + + if ((long) jd_samplesperrow != samplesperrow) + + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + + + + /* Initialize my private state */ + + master->pass_number = 0; + + master->using_merged_upsample = use_merged_upsample(cinfo); + + + + /* Color quantizer selection */ + + master->quantizer_1pass = NULL; + + master->quantizer_2pass = NULL; + + /* No mode changes if not using buffered-image mode. */ + + if (! cinfo->quantize_colors || ! cinfo->buffered_image) { + + cinfo->enable_1pass_quant = FALSE; + + cinfo->enable_external_quant = FALSE; + + cinfo->enable_2pass_quant = FALSE; + + } + + if (cinfo->quantize_colors) { + + if (cinfo->raw_data_out) + + ERREXIT(cinfo, JERR_NOTIMPL); + + /* 2-pass quantizer only works in 3-component color space. */ + + if (cinfo->out_color_components != 3) { + + cinfo->enable_1pass_quant = TRUE; + + cinfo->enable_external_quant = FALSE; + + cinfo->enable_2pass_quant = FALSE; + + cinfo->colormap = NULL; + + } else if (cinfo->colormap != NULL) { + + cinfo->enable_external_quant = TRUE; + + } else if (cinfo->two_pass_quantize) { + + cinfo->enable_2pass_quant = TRUE; + + } else { + + cinfo->enable_1pass_quant = TRUE; + + } + + + + if (cinfo->enable_1pass_quant) { + +#ifdef QUANT_1PASS_SUPPORTED + + jinit_1pass_quantizer(cinfo); + + master->quantizer_1pass = cinfo->cquantize; + +#else + + ERREXIT(cinfo, JERR_NOT_COMPILED); + +#endif + + } + + + + /* We use the 2-pass code to map to external colormaps. */ + + if (cinfo->enable_2pass_quant || cinfo->enable_external_quant) { + +#ifdef QUANT_2PASS_SUPPORTED + + jinit_2pass_quantizer(cinfo); + + master->quantizer_2pass = cinfo->cquantize; + +#else + + ERREXIT(cinfo, JERR_NOT_COMPILED); + +#endif + + } + + /* If both quantizers are initialized, the 2-pass one is left active; + + * this is necessary for starting with quantization to an external map. + + */ + + } + + + + /* Post-processing: in particular, color conversion first */ + + if (! cinfo->raw_data_out) { + + if (master->using_merged_upsample) { + +#ifdef UPSAMPLE_MERGING_SUPPORTED + + jinit_merged_upsampler(cinfo); /* does color conversion too */ + +#else + + ERREXIT(cinfo, JERR_NOT_COMPILED); + +#endif + + } else { + + jinit_color_deconverter(cinfo); + + jinit_upsampler(cinfo); + + } + + jinit_d_post_controller(cinfo, cinfo->enable_2pass_quant); + + } + + /* Inverse DCT */ + + jinit_inverse_dct(cinfo); + + /* Entropy decoding: either Huffman or arithmetic coding. */ + + if (cinfo->arith_code) { + + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + + } else { + + if (cinfo->progressive_mode) { + +#ifdef D_PROGRESSIVE_SUPPORTED + + jinit_phuff_decoder(cinfo); + +#else + + ERREXIT(cinfo, JERR_NOT_COMPILED); + +#endif + + } else + + jinit_huff_decoder(cinfo); + + } + + + + /* Initialize principal buffer controllers. */ + + use_c_buffer = cinfo->inputctl->has_multiple_scans || cinfo->buffered_image; + + jinit_d_coef_controller(cinfo, use_c_buffer); + + + + if (! cinfo->raw_data_out) + + jinit_d_main_controller(cinfo, FALSE /* never need full buffer here */); + + + + /* We can now tell the memory manager to allocate virtual arrays. */ + + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + + + /* Initialize input side of decompressor to consume first scan. */ + + (*cinfo->inputctl->start_input_pass) (cinfo); + + + +#ifdef D_MULTISCAN_FILES_SUPPORTED + + /* If jpeg_start_decompress will read the whole file, initialize + + * progress monitoring appropriately. The input step is counted + + * as one pass. + + */ + + if (cinfo->progress != NULL && ! cinfo->buffered_image && + + cinfo->inputctl->has_multiple_scans) { + + int nscans; + + /* Estimate number of scans to set pass_limit. */ + + if (cinfo->progressive_mode) { + + /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ + + nscans = 2 + 3 * cinfo->num_components; + + } else { + + /* For a nonprogressive multiscan file, estimate 1 scan per component. */ + + nscans = cinfo->num_components; + + } + + cinfo->progress->pass_counter = 0L; + + cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows * nscans; + + cinfo->progress->completed_passes = 0; + + cinfo->progress->total_passes = (cinfo->enable_2pass_quant ? 3 : 2); + + /* Count the input pass as done */ + + master->pass_number++; + + } + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + +} + + + + + +/* + + * Per-pass setup. + + * This is called at the beginning of each output pass. We determine which + + * modules will be active during this pass and give them appropriate + + * start_pass calls. We also set is_dummy_pass to indicate whether this + + * is a "real" output pass or a dummy pass for color quantization. + + * (In the latter case, jdapi.c will crank the pass to completion.) + + */ + + + +METHODDEF void + +prepare_for_output_pass (j_decompress_ptr cinfo) + +{ + + my_master_ptr master = (my_master_ptr) cinfo->master; + + + + if (master->pub.is_dummy_pass) { + +#ifdef QUANT_2PASS_SUPPORTED + + /* Final pass of 2-pass quantization */ + + master->pub.is_dummy_pass = FALSE; + + (*cinfo->cquantize->start_pass) (cinfo, FALSE); + + (*cinfo->post->start_pass) (cinfo, JBUF_CRANK_DEST); + + (*cinfo->main->start_pass) (cinfo, JBUF_CRANK_DEST); + +#else + + ERREXIT(cinfo, JERR_NOT_COMPILED); + +#endif /* QUANT_2PASS_SUPPORTED */ + + } else { + + if (cinfo->quantize_colors && cinfo->colormap == NULL) { + + /* Select new quantization method */ + + if (cinfo->two_pass_quantize && cinfo->enable_2pass_quant) { + + cinfo->cquantize = master->quantizer_2pass; + + master->pub.is_dummy_pass = TRUE; + + } else if (cinfo->enable_1pass_quant) { + + cinfo->cquantize = master->quantizer_1pass; + + } else { + + ERREXIT(cinfo, JERR_MODE_CHANGE); + + } + + } + + (*cinfo->idct->start_pass) (cinfo); + + (*cinfo->coef->start_output_pass) (cinfo); + + if (! cinfo->raw_data_out) { + + if (! master->using_merged_upsample) + + (*cinfo->cconvert->start_pass) (cinfo); + + (*cinfo->upsample->start_pass) (cinfo); + + if (cinfo->quantize_colors) + + (*cinfo->cquantize->start_pass) (cinfo, master->pub.is_dummy_pass); + + (*cinfo->post->start_pass) (cinfo, + + (master->pub.is_dummy_pass ? JBUF_SAVE_AND_PASS : JBUF_PASS_THRU)); + + (*cinfo->main->start_pass) (cinfo, JBUF_PASS_THRU); + + } + + } + + + + /* Set up progress monitor's pass info if present */ + + if (cinfo->progress != NULL) { + + cinfo->progress->completed_passes = master->pass_number; + + cinfo->progress->total_passes = master->pass_number + + + (master->pub.is_dummy_pass ? 2 : 1); + + /* In buffered-image mode, we assume one more output pass if EOI not + + * yet reached, but no more passes if EOI has been reached. + + */ + + if (cinfo->buffered_image && ! cinfo->inputctl->eoi_reached) { + + cinfo->progress->total_passes += (cinfo->enable_2pass_quant ? 2 : 1); + + } + + } + +} + + + + + +/* + + * Finish up at end of an output pass. + + */ + + + +METHODDEF void + +finish_output_pass (j_decompress_ptr cinfo) + +{ + + my_master_ptr master = (my_master_ptr) cinfo->master; + + + + if (cinfo->quantize_colors) + + (*cinfo->cquantize->finish_pass) (cinfo); + + master->pass_number++; + +} + + + + + +#ifdef D_MULTISCAN_FILES_SUPPORTED + + + +/* + + * Switch to a new external colormap between output passes. + + */ + + + +GLOBAL void + +jpeg_new_colormap (j_decompress_ptr cinfo) + +{ + + my_master_ptr master = (my_master_ptr) cinfo->master; + + + + /* Prevent application from calling me at wrong times */ + + if (cinfo->global_state != DSTATE_BUFIMAGE) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + + + if (cinfo->quantize_colors && cinfo->enable_external_quant && + + cinfo->colormap != NULL) { + + /* Select 2-pass quantizer for external colormap use */ + + cinfo->cquantize = master->quantizer_2pass; + + /* Notify quantizer of colormap change */ + + (*cinfo->cquantize->new_color_map) (cinfo); + + master->pub.is_dummy_pass = FALSE; /* just in case */ + + } else + + ERREXIT(cinfo, JERR_MODE_CHANGE); + +} + + + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + + + + + +/* + + * Initialize master decompression control and select active modules. + + * This is performed at the start of jpeg_start_decompress. + + */ + + + +GLOBAL void + +jinit_master_decompress (j_decompress_ptr cinfo) + +{ + + my_master_ptr master; + + + + master = (my_master_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(my_decomp_master)); + + cinfo->master = (struct jpeg_decomp_master *) master; + + master->pub.prepare_for_output_pass = prepare_for_output_pass; + + master->pub.finish_output_pass = finish_output_pass; + + + + master->pub.is_dummy_pass = FALSE; + + + + master_selection(cinfo); + +} + diff --git a/utils/roq2/jpeg/jdmerge.c b/utils/roq2/jpeg/jdmerge.c new file mode 100644 index 0000000..60aa195 --- /dev/null +++ b/utils/roq2/jpeg/jdmerge.c @@ -0,0 +1,800 @@ +/* + + * jdmerge.c + + * + + * Copyright (C) 1994-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains code for merged upsampling/color conversion. + + * + + * This file combines functions from jdsample.c and jdcolor.c; + + * read those files first to understand what's going on. + + * + + * When the chroma components are to be upsampled by simple replication + + * (ie, box filtering), we can save some work in color conversion by + + * calculating all the output pixels corresponding to a pair of chroma + + * samples at one time. In the conversion equations + + * R = Y + K1 * Cr + + * G = Y + K2 * Cb + K3 * Cr + + * B = Y + K4 * Cb + + * only the Y term varies among the group of pixels corresponding to a pair + + * of chroma samples, so the rest of the terms can be calculated just once. + + * At typical sampling ratios, this eliminates half or three-quarters of the + + * multiplications needed for color conversion. + + * + + * This file currently provides implementations for the following cases: + + * YCbCr => RGB color conversion only. + + * Sampling ratios of 2h1v or 2h2v. + + * No scaling needed at upsample time. + + * Corner-aligned (non-CCIR601) sampling alignment. + + * Other special cases could be added, but in most applications these are + + * the only common cases. (For uncommon cases we fall back on the more + + * general code in jdsample.c and jdcolor.c.) + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + +#ifdef UPSAMPLE_MERGING_SUPPORTED + + + + + +/* Private subobject */ + + + +typedef struct { + + struct jpeg_upsampler pub; /* public fields */ + + + + /* Pointer to routine to do actual upsampling/conversion of one row group */ + + JMETHOD(void, upmethod, (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, + + JSAMPARRAY output_buf)); + + + + /* Private state for YCC->RGB conversion */ + + int * Cr_r_tab; /* => table for Cr to R conversion */ + + int * Cb_b_tab; /* => table for Cb to B conversion */ + + INT32 * Cr_g_tab; /* => table for Cr to G conversion */ + + INT32 * Cb_g_tab; /* => table for Cb to G conversion */ + + + + /* For 2:1 vertical sampling, we produce two output rows at a time. + + * We need a "spare" row buffer to hold the second output row if the + + * application provides just a one-row buffer; we also use the spare + + * to discard the dummy last row if the image height is odd. + + */ + + JSAMPROW spare_row; + + boolean spare_full; /* T if spare buffer is occupied */ + + + + JDIMENSION out_row_width; /* samples per output row */ + + JDIMENSION rows_to_go; /* counts rows remaining in image */ + +} my_upsampler; + + + +typedef my_upsampler * my_upsample_ptr; + + + +#define SCALEBITS 16 /* speediest right-shift on some machines */ + +#define ONE_HALF ((INT32) 1 << (SCALEBITS-1)) + +#define FIX(x) ((INT32) ((x) * (1L<RGB colorspace conversion. + + * This is taken directly from jdcolor.c; see that file for more info. + + */ + + + +LOCAL void + +build_ycc_rgb_table (j_decompress_ptr cinfo) + +{ + + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + int i; + + INT32 x; + + SHIFT_TEMPS + + + + upsample->Cr_r_tab = (int *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (MAXJSAMPLE+1) * SIZEOF(int)); + + upsample->Cb_b_tab = (int *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (MAXJSAMPLE+1) * SIZEOF(int)); + + upsample->Cr_g_tab = (INT32 *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (MAXJSAMPLE+1) * SIZEOF(INT32)); + + upsample->Cb_g_tab = (INT32 *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (MAXJSAMPLE+1) * SIZEOF(INT32)); + + + + for (i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) { + + /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ + + /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ + + /* Cr=>R value is nearest int to 1.40200 * x */ + + upsample->Cr_r_tab[i] = (int) + + RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS); + + /* Cb=>B value is nearest int to 1.77200 * x */ + + upsample->Cb_b_tab[i] = (int) + + RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS); + + /* Cr=>G value is scaled-up -0.71414 * x */ + + upsample->Cr_g_tab[i] = (- FIX(0.71414)) * x; + + /* Cb=>G value is scaled-up -0.34414 * x */ + + /* We also add in ONE_HALF so that need not do it in inner loop */ + + upsample->Cb_g_tab[i] = (- FIX(0.34414)) * x + ONE_HALF; + + } + +} + + + + + +/* + + * Initialize for an upsampling pass. + + */ + + + +METHODDEF void + +start_pass_merged_upsample (j_decompress_ptr cinfo) + +{ + + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + + + /* Mark the spare buffer empty */ + + upsample->spare_full = FALSE; + + /* Initialize total-height counter for detecting bottom of image */ + + upsample->rows_to_go = cinfo->output_height; + +} + + + + + +/* + + * Control routine to do upsampling (and color conversion). + + * + + * The control routine just handles the row buffering considerations. + + */ + + + +METHODDEF void + +merged_2v_upsample (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + + JDIMENSION in_row_groups_avail, + + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail) + +/* 2:1 vertical sampling case: may need a spare row. */ + +{ + + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + JSAMPROW work_ptrs[2]; + + JDIMENSION num_rows; /* number of rows returned to caller */ + + + + if (upsample->spare_full) { + + /* If we have a spare row saved from a previous cycle, just return it. */ + + jcopy_sample_rows(& upsample->spare_row, 0, output_buf + *out_row_ctr, 0, + + 1, upsample->out_row_width); + + num_rows = 1; + + upsample->spare_full = FALSE; + + } else { + + /* Figure number of rows to return to caller. */ + + num_rows = 2; + + /* Not more than the distance to the end of the image. */ + + if (num_rows > upsample->rows_to_go) + + num_rows = upsample->rows_to_go; + + /* And not more than what the client can accept: */ + + out_rows_avail -= *out_row_ctr; + + if (num_rows > out_rows_avail) + + num_rows = out_rows_avail; + + /* Create output pointer array for upsampler. */ + + work_ptrs[0] = output_buf[*out_row_ctr]; + + if (num_rows > 1) { + + work_ptrs[1] = output_buf[*out_row_ctr + 1]; + + } else { + + work_ptrs[1] = upsample->spare_row; + + upsample->spare_full = TRUE; + + } + + /* Now do the upsampling. */ + + (*upsample->upmethod) (cinfo, input_buf, *in_row_group_ctr, work_ptrs); + + } + + + + /* Adjust counts */ + + *out_row_ctr += num_rows; + + upsample->rows_to_go -= num_rows; + + /* When the buffer is emptied, declare this input row group consumed */ + + if (! upsample->spare_full) + + (*in_row_group_ctr)++; + +} + + + + + +METHODDEF void + +merged_1v_upsample (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + + JDIMENSION in_row_groups_avail, + + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail) + +/* 1:1 vertical sampling case: much easier, never need a spare row. */ + +{ + + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + + + /* Just do the upsampling. */ + + (*upsample->upmethod) (cinfo, input_buf, *in_row_group_ctr, + + output_buf + *out_row_ctr); + + /* Adjust counts */ + + (*out_row_ctr)++; + + (*in_row_group_ctr)++; + +} + + + + + +/* + + * These are the routines invoked by the control routines to do + + * the actual upsampling/conversion. One row group is processed per call. + + * + + * Note: since we may be writing directly into application-supplied buffers, + + * we have to be honest about the output width; we can't assume the buffer + + * has been rounded up to an even width. + + */ + + + + + +/* + + * Upsample and color convert for the case of 2:1 horizontal and 1:1 vertical. + + */ + + + +METHODDEF void + +h2v1_merged_upsample (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, + + JSAMPARRAY output_buf) + +{ + + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + register int y, cred, cgreen, cblue; + + int cb, cr; + + register JSAMPROW outptr; + + JSAMPROW inptr0, inptr1, inptr2; + + JDIMENSION col; + + /* copy these pointers into registers if possible */ + + register JSAMPLE * range_limit = cinfo->sample_range_limit; + + int * Crrtab = upsample->Cr_r_tab; + + int * Cbbtab = upsample->Cb_b_tab; + + INT32 * Crgtab = upsample->Cr_g_tab; + + INT32 * Cbgtab = upsample->Cb_g_tab; + + SHIFT_TEMPS + + + + inptr0 = input_buf[0][in_row_group_ctr]; + + inptr1 = input_buf[1][in_row_group_ctr]; + + inptr2 = input_buf[2][in_row_group_ctr]; + + outptr = output_buf[0]; + + /* Loop for each pair of output pixels */ + + for (col = cinfo->output_width >> 1; col > 0; col--) { + + /* Do the chroma part of the calculation */ + + cb = GETJSAMPLE(*inptr1++); + + cr = GETJSAMPLE(*inptr2++); + + cred = Crrtab[cr]; + + cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); + + cblue = Cbbtab[cb]; + + /* Fetch 2 Y values and emit 2 pixels */ + + y = GETJSAMPLE(*inptr0++); + + outptr[RGB_RED] = range_limit[y + cred]; + + outptr[RGB_GREEN] = range_limit[y + cgreen]; + + outptr[RGB_BLUE] = range_limit[y + cblue]; + + outptr += RGB_PIXELSIZE; + + y = GETJSAMPLE(*inptr0++); + + outptr[RGB_RED] = range_limit[y + cred]; + + outptr[RGB_GREEN] = range_limit[y + cgreen]; + + outptr[RGB_BLUE] = range_limit[y + cblue]; + + outptr += RGB_PIXELSIZE; + + } + + /* If image width is odd, do the last output column separately */ + + if (cinfo->output_width & 1) { + + cb = GETJSAMPLE(*inptr1); + + cr = GETJSAMPLE(*inptr2); + + cred = Crrtab[cr]; + + cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); + + cblue = Cbbtab[cb]; + + y = GETJSAMPLE(*inptr0); + + outptr[RGB_RED] = range_limit[y + cred]; + + outptr[RGB_GREEN] = range_limit[y + cgreen]; + + outptr[RGB_BLUE] = range_limit[y + cblue]; + + } + +} + + + + + +/* + + * Upsample and color convert for the case of 2:1 horizontal and 2:1 vertical. + + */ + + + +METHODDEF void + +h2v2_merged_upsample (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, + + JSAMPARRAY output_buf) + +{ + + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + register int y, cred, cgreen, cblue; + + int cb, cr; + + register JSAMPROW outptr0, outptr1; + + JSAMPROW inptr00, inptr01, inptr1, inptr2; + + JDIMENSION col; + + /* copy these pointers into registers if possible */ + + register JSAMPLE * range_limit = cinfo->sample_range_limit; + + int * Crrtab = upsample->Cr_r_tab; + + int * Cbbtab = upsample->Cb_b_tab; + + INT32 * Crgtab = upsample->Cr_g_tab; + + INT32 * Cbgtab = upsample->Cb_g_tab; + + SHIFT_TEMPS + + + + inptr00 = input_buf[0][in_row_group_ctr*2]; + + inptr01 = input_buf[0][in_row_group_ctr*2 + 1]; + + inptr1 = input_buf[1][in_row_group_ctr]; + + inptr2 = input_buf[2][in_row_group_ctr]; + + outptr0 = output_buf[0]; + + outptr1 = output_buf[1]; + + /* Loop for each group of output pixels */ + + for (col = cinfo->output_width >> 1; col > 0; col--) { + + /* Do the chroma part of the calculation */ + + cb = GETJSAMPLE(*inptr1++); + + cr = GETJSAMPLE(*inptr2++); + + cred = Crrtab[cr]; + + cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); + + cblue = Cbbtab[cb]; + + /* Fetch 4 Y values and emit 4 pixels */ + + y = GETJSAMPLE(*inptr00++); + + outptr0[RGB_RED] = range_limit[y + cred]; + + outptr0[RGB_GREEN] = range_limit[y + cgreen]; + + outptr0[RGB_BLUE] = range_limit[y + cblue]; + + outptr0 += RGB_PIXELSIZE; + + y = GETJSAMPLE(*inptr00++); + + outptr0[RGB_RED] = range_limit[y + cred]; + + outptr0[RGB_GREEN] = range_limit[y + cgreen]; + + outptr0[RGB_BLUE] = range_limit[y + cblue]; + + outptr0 += RGB_PIXELSIZE; + + y = GETJSAMPLE(*inptr01++); + + outptr1[RGB_RED] = range_limit[y + cred]; + + outptr1[RGB_GREEN] = range_limit[y + cgreen]; + + outptr1[RGB_BLUE] = range_limit[y + cblue]; + + outptr1 += RGB_PIXELSIZE; + + y = GETJSAMPLE(*inptr01++); + + outptr1[RGB_RED] = range_limit[y + cred]; + + outptr1[RGB_GREEN] = range_limit[y + cgreen]; + + outptr1[RGB_BLUE] = range_limit[y + cblue]; + + outptr1 += RGB_PIXELSIZE; + + } + + /* If image width is odd, do the last output column separately */ + + if (cinfo->output_width & 1) { + + cb = GETJSAMPLE(*inptr1); + + cr = GETJSAMPLE(*inptr2); + + cred = Crrtab[cr]; + + cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); + + cblue = Cbbtab[cb]; + + y = GETJSAMPLE(*inptr00); + + outptr0[RGB_RED] = range_limit[y + cred]; + + outptr0[RGB_GREEN] = range_limit[y + cgreen]; + + outptr0[RGB_BLUE] = range_limit[y + cblue]; + + y = GETJSAMPLE(*inptr01); + + outptr1[RGB_RED] = range_limit[y + cred]; + + outptr1[RGB_GREEN] = range_limit[y + cgreen]; + + outptr1[RGB_BLUE] = range_limit[y + cblue]; + + } + +} + + + + + +/* + + * Module initialization routine for merged upsampling/color conversion. + + * + + * NB: this is called under the conditions determined by use_merged_upsample() + + * in jdmaster.c. That routine MUST correspond to the actual capabilities + + * of this module; no safety checks are made here. + + */ + + + +GLOBAL void + +jinit_merged_upsampler (j_decompress_ptr cinfo) + +{ + + my_upsample_ptr upsample; + + + + upsample = (my_upsample_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(my_upsampler)); + + cinfo->upsample = (struct jpeg_upsampler *) upsample; + + upsample->pub.start_pass = start_pass_merged_upsample; + + upsample->pub.need_context_rows = FALSE; + + + + upsample->out_row_width = cinfo->output_width * cinfo->out_color_components; + + + + if (cinfo->max_v_samp_factor == 2) { + + upsample->pub.upsample = merged_2v_upsample; + + upsample->upmethod = h2v2_merged_upsample; + + /* Allocate a spare row buffer */ + + upsample->spare_row = (JSAMPROW) + + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (size_t) (upsample->out_row_width * SIZEOF(JSAMPLE))); + + } else { + + upsample->pub.upsample = merged_1v_upsample; + + upsample->upmethod = h2v1_merged_upsample; + + /* No spare row needed */ + + upsample->spare_row = NULL; + + } + + + + build_ycc_rgb_table(cinfo); + +} + + + +#endif /* UPSAMPLE_MERGING_SUPPORTED */ + diff --git a/utils/roq2/jpeg/jdphuff.c b/utils/roq2/jpeg/jdphuff.c new file mode 100644 index 0000000..2650ab4 --- /dev/null +++ b/utils/roq2/jpeg/jdphuff.c @@ -0,0 +1,1284 @@ +/* + + * jdphuff.c + + * + + * Copyright (C) 1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains Huffman entropy decoding routines for progressive JPEG. + + * + + * Much of the complexity here has to do with supporting input suspension. + + * If the data source module demands suspension, we want to be able to back + + * up to the start of the current MCU. To do this, we copy state variables + + * into local working storage, and update them back to the permanent + + * storage only upon successful completion of an MCU. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + +#include "jdhuff.h" /* Declarations shared with jdhuff.c */ + + + + + +#ifdef D_PROGRESSIVE_SUPPORTED + + + +/* + + * Expanded entropy decoder object for progressive Huffman decoding. + + * + + * The savable_state subrecord contains fields that change within an MCU, + + * but must not be updated permanently until we complete the MCU. + + */ + + + +typedef struct { + + unsigned int EOBRUN; /* remaining EOBs in EOBRUN */ + + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ + +} savable_state; + + + +/* This macro is to work around compilers with missing or broken + + * structure assignment. You'll need to fix this code if you have + + * such a compiler and you change MAX_COMPS_IN_SCAN. + + */ + + + +#ifndef NO_STRUCT_ASSIGN + +#define ASSIGN_STATE(dest,src) ((dest) = (src)) + +#else + +#if MAX_COMPS_IN_SCAN == 4 + +#define ASSIGN_STATE(dest,src) \ + + ((dest).EOBRUN = (src).EOBRUN, \ + + (dest).last_dc_val[0] = (src).last_dc_val[0], \ + + (dest).last_dc_val[1] = (src).last_dc_val[1], \ + + (dest).last_dc_val[2] = (src).last_dc_val[2], \ + + (dest).last_dc_val[3] = (src).last_dc_val[3]) + +#endif + +#endif + + + + + +typedef struct { + + struct jpeg_entropy_decoder pub; /* public fields */ + + + + /* These fields are loaded into local variables at start of each MCU. + + * In case of suspension, we exit WITHOUT updating them. + + */ + + bitread_perm_state bitstate; /* Bit buffer at start of MCU */ + + savable_state saved; /* Other state at start of MCU */ + + + + /* These fields are NOT loaded into local working state. */ + + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + + + + /* Pointers to derived tables (these workspaces have image lifespan) */ + + d_derived_tbl * derived_tbls[NUM_HUFF_TBLS]; + + + + d_derived_tbl * ac_derived_tbl; /* active table during an AC scan */ + +} phuff_entropy_decoder; + + + +typedef phuff_entropy_decoder * phuff_entropy_ptr; + + + +/* Forward declarations */ + +METHODDEF boolean decode_mcu_DC_first JPP((j_decompress_ptr cinfo, + + JBLOCKROW *MCU_data)); + +METHODDEF boolean decode_mcu_AC_first JPP((j_decompress_ptr cinfo, + + JBLOCKROW *MCU_data)); + +METHODDEF boolean decode_mcu_DC_refine JPP((j_decompress_ptr cinfo, + + JBLOCKROW *MCU_data)); + +METHODDEF boolean decode_mcu_AC_refine JPP((j_decompress_ptr cinfo, + + JBLOCKROW *MCU_data)); + + + + + +/* + + * Initialize for a Huffman-compressed scan. + + */ + + + +METHODDEF void + +start_pass_phuff_decoder (j_decompress_ptr cinfo) + +{ + + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + + boolean is_DC_band, bad; + + int ci, coefi, tbl; + + int *coef_bit_ptr; + + jpeg_component_info * compptr; + + + + is_DC_band = (cinfo->Ss == 0); + + + + /* Validate scan parameters */ + + bad = FALSE; + + if (is_DC_band) { + + if (cinfo->Se != 0) + + bad = TRUE; + + } else { + + /* need not check Ss/Se < 0 since they came from unsigned bytes */ + + if (cinfo->Ss > cinfo->Se || cinfo->Se >= DCTSIZE2) + + bad = TRUE; + + /* AC scans may have only one component */ + + if (cinfo->comps_in_scan != 1) + + bad = TRUE; + + } + + if (cinfo->Ah != 0) { + + /* Successive approximation refinement scan: must have Al = Ah-1. */ + + if (cinfo->Al != cinfo->Ah-1) + + bad = TRUE; + + } + + if (cinfo->Al > 13) /* need not check for < 0 */ + + bad = TRUE; + + if (bad) + + ERREXIT4(cinfo, JERR_BAD_PROGRESSION, + + cinfo->Ss, cinfo->Se, cinfo->Ah, cinfo->Al); + + /* Update progression status, and verify that scan order is legal. + + * Note that inter-scan inconsistencies are treated as warnings + + * not fatal errors ... not clear if this is right way to behave. + + */ + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + + int cindex = cinfo->cur_comp_info[ci]->component_index; + + coef_bit_ptr = & cinfo->coef_bits[cindex][0]; + + if (!is_DC_band && coef_bit_ptr[0] < 0) /* AC without prior DC scan */ + + WARNMS2(cinfo, JWRN_BOGUS_PROGRESSION, cindex, 0); + + for (coefi = cinfo->Ss; coefi <= cinfo->Se; coefi++) { + + int expected = (coef_bit_ptr[coefi] < 0) ? 0 : coef_bit_ptr[coefi]; + + if (cinfo->Ah != expected) + + WARNMS2(cinfo, JWRN_BOGUS_PROGRESSION, cindex, coefi); + + coef_bit_ptr[coefi] = cinfo->Al; + + } + + } + + + + /* Select MCU decoding routine */ + + if (cinfo->Ah == 0) { + + if (is_DC_band) + + entropy->pub.decode_mcu = decode_mcu_DC_first; + + else + + entropy->pub.decode_mcu = decode_mcu_AC_first; + + } else { + + if (is_DC_band) + + entropy->pub.decode_mcu = decode_mcu_DC_refine; + + else + + entropy->pub.decode_mcu = decode_mcu_AC_refine; + + } + + + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + + compptr = cinfo->cur_comp_info[ci]; + + /* Make sure requested tables are present, and compute derived tables. + + * We may build same derived table more than once, but it's not expensive. + + */ + + if (is_DC_band) { + + if (cinfo->Ah == 0) { /* DC refinement needs no table */ + + tbl = compptr->dc_tbl_no; + + if (tbl < 0 || tbl >= NUM_HUFF_TBLS || + + cinfo->dc_huff_tbl_ptrs[tbl] == NULL) + + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tbl); + + jpeg_make_d_derived_tbl(cinfo, cinfo->dc_huff_tbl_ptrs[tbl], + + & entropy->derived_tbls[tbl]); + + } + + } else { + + tbl = compptr->ac_tbl_no; + + if (tbl < 0 || tbl >= NUM_HUFF_TBLS || + + cinfo->ac_huff_tbl_ptrs[tbl] == NULL) + + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tbl); + + jpeg_make_d_derived_tbl(cinfo, cinfo->ac_huff_tbl_ptrs[tbl], + + & entropy->derived_tbls[tbl]); + + /* remember the single active table */ + + entropy->ac_derived_tbl = entropy->derived_tbls[tbl]; + + } + + /* Initialize DC predictions to 0 */ + + entropy->saved.last_dc_val[ci] = 0; + + } + + + + /* Initialize bitread state variables */ + + entropy->bitstate.bits_left = 0; + + entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ + + entropy->bitstate.printed_eod = FALSE; + + + + /* Initialize private state variables */ + + entropy->saved.EOBRUN = 0; + + + + /* Initialize restart counter */ + + entropy->restarts_to_go = cinfo->restart_interval; + +} + + + + + +/* + + * Figure F.12: extend sign bit. + + * On some machines, a shift and add will be faster than a table lookup. + + */ + + + +#ifdef AVOID_TABLES + + + +#define HUFF_EXTEND(x,s) ((x) < (1<<((s)-1)) ? (x) + (((-1)<<(s)) + 1) : (x)) + + + +#else + + + +#define HUFF_EXTEND(x,s) ((x) < extend_test[s] ? (x) + extend_offset[s] : (x)) + + + +static const int extend_test[16] = /* entry n is 2**(n-1) */ + + { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, + + 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 }; + + + +static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */ + + { 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1, + + ((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1, + + ((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1, + + ((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 }; + + + +#endif /* AVOID_TABLES */ + + + + + +/* + + * Check for a restart marker & resynchronize decoder. + + * Returns FALSE if must suspend. + + */ + + + +LOCAL boolean + +process_restart (j_decompress_ptr cinfo) + +{ + + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + + int ci; + + + + /* Throw away any unused bits remaining in bit buffer; */ + + /* include any full bytes in next_marker's count of discarded bytes */ + + cinfo->marker->discarded_bytes += entropy->bitstate.bits_left / 8; + + entropy->bitstate.bits_left = 0; + + + + /* Advance past the RSTn marker */ + + if (! (*cinfo->marker->read_restart_marker) (cinfo)) + + return FALSE; + + + + /* Re-initialize DC predictions to 0 */ + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) + + entropy->saved.last_dc_val[ci] = 0; + + /* Re-init EOB run count, too */ + + entropy->saved.EOBRUN = 0; + + + + /* Reset restart counter */ + + entropy->restarts_to_go = cinfo->restart_interval; + + + + /* Next segment can get another out-of-data warning */ + + entropy->bitstate.printed_eod = FALSE; + + + + return TRUE; + +} + + + + + +/* + + * Huffman MCU decoding. + + * Each of these routines decodes and returns one MCU's worth of + + * Huffman-compressed coefficients. + + * The coefficients are reordered from zigzag order into natural array order, + + * but are not dequantized. + + * + + * The i'th block of the MCU is stored into the block pointed to by + + * MCU_data[i]. WE ASSUME THIS AREA IS INITIALLY ZEROED BY THE CALLER. + + * + + * We return FALSE if data source requested suspension. In that case no + + * changes have been made to permanent state. (Exception: some output + + * coefficients may already have been assigned. This is harmless for + + * spectral selection, since we'll just re-assign them on the next call. + + * Successive approximation AC refinement has to be more careful, however.) + + */ + + + +/* + + * MCU decoding for DC initial scan (either spectral selection, + + * or first pass of successive approximation). + + */ + + + +METHODDEF boolean + +decode_mcu_DC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) + +{ + + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + + int Al = cinfo->Al; + + register int s, r; + + int blkn, ci; + + JBLOCKROW block; + + BITREAD_STATE_VARS; + + savable_state state; + + d_derived_tbl * tbl; + + jpeg_component_info * compptr; + + + + /* Process restart marker if needed; may have to suspend */ + + if (cinfo->restart_interval) { + + if (entropy->restarts_to_go == 0) + + if (! process_restart(cinfo)) + + return FALSE; + + } + + + + /* Load up working state */ + + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + + ASSIGN_STATE(state, entropy->saved); + + + + /* Outer loop handles each block in the MCU */ + + + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + + block = MCU_data[blkn]; + + ci = cinfo->MCU_membership[blkn]; + + compptr = cinfo->cur_comp_info[ci]; + + tbl = entropy->derived_tbls[compptr->dc_tbl_no]; + + + + /* Decode a single block's worth of coefficients */ + + + + /* Section F.2.2.1: decode the DC coefficient difference */ + + HUFF_DECODE(s, br_state, tbl, return FALSE, label1); + + if (s) { + + CHECK_BIT_BUFFER(br_state, s, return FALSE); + + r = GET_BITS(s); + + s = HUFF_EXTEND(r, s); + + } + + + + /* Convert DC difference to actual value, update last_dc_val */ + + s += state.last_dc_val[ci]; + + state.last_dc_val[ci] = s; + + /* Scale and output the DC coefficient (assumes jpeg_natural_order[0]=0) */ + + (*block)[0] = (JCOEF) (s << Al); + + } + + + + /* Completed MCU, so update state */ + + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + + ASSIGN_STATE(entropy->saved, state); + + + + /* Account for restart interval (no-op if not using restarts) */ + + entropy->restarts_to_go--; + + + + return TRUE; + +} + + + + + +/* + + * MCU decoding for AC initial scan (either spectral selection, + + * or first pass of successive approximation). + + */ + + + +METHODDEF boolean + +decode_mcu_AC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) + +{ + + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + + int Se = cinfo->Se; + + int Al = cinfo->Al; + + register int s, k, r; + + unsigned int EOBRUN; + + JBLOCKROW block; + + BITREAD_STATE_VARS; + + d_derived_tbl * tbl; + + + + /* Process restart marker if needed; may have to suspend */ + + if (cinfo->restart_interval) { + + if (entropy->restarts_to_go == 0) + + if (! process_restart(cinfo)) + + return FALSE; + + } + + + + /* Load up working state. + + * We can avoid loading/saving bitread state if in an EOB run. + + */ + + EOBRUN = entropy->saved.EOBRUN; /* only part of saved state we care about */ + + + + /* There is always only one block per MCU */ + + + + if (EOBRUN > 0) /* if it's a band of zeroes... */ + + EOBRUN--; /* ...process it now (we do nothing) */ + + else { + + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + + block = MCU_data[0]; + + tbl = entropy->ac_derived_tbl; + + + + for (k = cinfo->Ss; k <= Se; k++) { + + HUFF_DECODE(s, br_state, tbl, return FALSE, label2); + + r = s >> 4; + + s &= 15; + + if (s) { + + k += r; + + CHECK_BIT_BUFFER(br_state, s, return FALSE); + + r = GET_BITS(s); + + s = HUFF_EXTEND(r, s); + + /* Scale and output coefficient in natural (dezigzagged) order */ + + (*block)[jpeg_natural_order[k]] = (JCOEF) (s << Al); + + } else { + + if (r == 15) { /* ZRL */ + + k += 15; /* skip 15 zeroes in band */ + + } else { /* EOBr, run length is 2^r + appended bits */ + + EOBRUN = 1 << r; + + if (r) { /* EOBr, r > 0 */ + + CHECK_BIT_BUFFER(br_state, r, return FALSE); + + r = GET_BITS(r); + + EOBRUN += r; + + } + + EOBRUN--; /* this band is processed at this moment */ + + break; /* force end-of-band */ + + } + + } + + } + + + + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + + } + + + + /* Completed MCU, so update state */ + + entropy->saved.EOBRUN = EOBRUN; /* only part of saved state we care about */ + + + + /* Account for restart interval (no-op if not using restarts) */ + + entropy->restarts_to_go--; + + + + return TRUE; + +} + + + + + +/* + + * MCU decoding for DC successive approximation refinement scan. + + * Note: we assume such scans can be multi-component, although the spec + + * is not very clear on the point. + + */ + + + +METHODDEF boolean + +decode_mcu_DC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) + +{ + + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + + int p1 = 1 << cinfo->Al; /* 1 in the bit position being coded */ + + int blkn; + + JBLOCKROW block; + + BITREAD_STATE_VARS; + + + + /* Process restart marker if needed; may have to suspend */ + + if (cinfo->restart_interval) { + + if (entropy->restarts_to_go == 0) + + if (! process_restart(cinfo)) + + return FALSE; + + } + + + + /* Load up working state */ + + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + + + + /* Outer loop handles each block in the MCU */ + + + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + + block = MCU_data[blkn]; + + + + /* Encoded data is simply the next bit of the two's-complement DC value */ + + CHECK_BIT_BUFFER(br_state, 1, return FALSE); + + if (GET_BITS(1)) + + (*block)[0] |= p1; + + /* Note: since we use |=, repeating the assignment later is safe */ + + } + + + + /* Completed MCU, so update state */ + + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + + + + /* Account for restart interval (no-op if not using restarts) */ + + entropy->restarts_to_go--; + + + + return TRUE; + +} + + + + + +/* + + * MCU decoding for AC successive approximation refinement scan. + + */ + + + +METHODDEF boolean + +decode_mcu_AC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) + +{ + + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + + int Se = cinfo->Se; + + int p1 = 1 << cinfo->Al; /* 1 in the bit position being coded */ + + int m1 = (-1) << cinfo->Al; /* -1 in the bit position being coded */ + + register int s, k, r; + + unsigned int EOBRUN; + + JBLOCKROW block; + + JCOEFPTR thiscoef; + + BITREAD_STATE_VARS; + + d_derived_tbl * tbl; + + int num_newnz; + + int newnz_pos[DCTSIZE2]; + + + + /* Process restart marker if needed; may have to suspend */ + + if (cinfo->restart_interval) { + + if (entropy->restarts_to_go == 0) + + if (! process_restart(cinfo)) + + return FALSE; + + } + + + + /* Load up working state */ + + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + + EOBRUN = entropy->saved.EOBRUN; /* only part of saved state we care about */ + + + + /* There is always only one block per MCU */ + + block = MCU_data[0]; + + tbl = entropy->ac_derived_tbl; + + + + /* If we are forced to suspend, we must undo the assignments to any newly + + * nonzero coefficients in the block, because otherwise we'd get confused + + * next time about which coefficients were already nonzero. + + * But we need not undo addition of bits to already-nonzero coefficients; + + * instead, we can test the current bit position to see if we already did it. + + */ + + num_newnz = 0; + + + + /* initialize coefficient loop counter to start of band */ + + k = cinfo->Ss; + + + + if (EOBRUN == 0) { + + for (; k <= Se; k++) { + + HUFF_DECODE(s, br_state, tbl, goto undoit, label3); + + r = s >> 4; + + s &= 15; + + if (s) { + + if (s != 1) /* size of new coef should always be 1 */ + + WARNMS(cinfo, JWRN_HUFF_BAD_CODE); + + CHECK_BIT_BUFFER(br_state, 1, goto undoit); + + if (GET_BITS(1)) + + s = p1; /* newly nonzero coef is positive */ + + else + + s = m1; /* newly nonzero coef is negative */ + + } else { + + if (r != 15) { + + EOBRUN = 1 << r; /* EOBr, run length is 2^r + appended bits */ + + if (r) { + + CHECK_BIT_BUFFER(br_state, r, goto undoit); + + r = GET_BITS(r); + + EOBRUN += r; + + } + + break; /* rest of block is handled by EOB logic */ + + } + + /* note s = 0 for processing ZRL */ + + } + + /* Advance over already-nonzero coefs and r still-zero coefs, + + * appending correction bits to the nonzeroes. A correction bit is 1 + + * if the absolute value of the coefficient must be increased. + + */ + + do { + + thiscoef = *block + jpeg_natural_order[k]; + + if (*thiscoef != 0) { + + CHECK_BIT_BUFFER(br_state, 1, goto undoit); + + if (GET_BITS(1)) { + + if ((*thiscoef & p1) == 0) { /* do nothing if already changed it */ + + if (*thiscoef >= 0) + + *thiscoef += p1; + + else + + *thiscoef += m1; + + } + + } + + } else { + + if (--r < 0) + + break; /* reached target zero coefficient */ + + } + + k++; + + } while (k <= Se); + + if (s) { + + int pos = jpeg_natural_order[k]; + + /* Output newly nonzero coefficient */ + + (*block)[pos] = (JCOEF) s; + + /* Remember its position in case we have to suspend */ + + newnz_pos[num_newnz++] = pos; + + } + + } + + } + + + + if (EOBRUN > 0) { + + /* Scan any remaining coefficient positions after the end-of-band + + * (the last newly nonzero coefficient, if any). Append a correction + + * bit to each already-nonzero coefficient. A correction bit is 1 + + * if the absolute value of the coefficient must be increased. + + */ + + for (; k <= Se; k++) { + + thiscoef = *block + jpeg_natural_order[k]; + + if (*thiscoef != 0) { + + CHECK_BIT_BUFFER(br_state, 1, goto undoit); + + if (GET_BITS(1)) { + + if ((*thiscoef & p1) == 0) { /* do nothing if already changed it */ + + if (*thiscoef >= 0) + + *thiscoef += p1; + + else + + *thiscoef += m1; + + } + + } + + } + + } + + /* Count one block completed in EOB run */ + + EOBRUN--; + + } + + + + /* Completed MCU, so update state */ + + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + + entropy->saved.EOBRUN = EOBRUN; /* only part of saved state we care about */ + + + + /* Account for restart interval (no-op if not using restarts) */ + + entropy->restarts_to_go--; + + + + return TRUE; + + + +undoit: + + /* Re-zero any output coefficients that we made newly nonzero */ + + while (num_newnz > 0) + + (*block)[newnz_pos[--num_newnz]] = 0; + + + + return FALSE; + +} + + + + + +/* + + * Module initialization routine for progressive Huffman entropy decoding. + + */ + + + +GLOBAL void + +jinit_phuff_decoder (j_decompress_ptr cinfo) + +{ + + phuff_entropy_ptr entropy; + + int *coef_bit_ptr; + + int ci, i; + + + + entropy = (phuff_entropy_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(phuff_entropy_decoder)); + + cinfo->entropy = (struct jpeg_entropy_decoder *) entropy; + + entropy->pub.start_pass = start_pass_phuff_decoder; + + + + /* Mark derived tables unallocated */ + + for (i = 0; i < NUM_HUFF_TBLS; i++) { + + entropy->derived_tbls[i] = NULL; + + } + + + + /* Create progression status table */ + + cinfo->coef_bits = (int (*)[DCTSIZE2]) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + cinfo->num_components*DCTSIZE2*SIZEOF(int)); + + coef_bit_ptr = & cinfo->coef_bits[0][0]; + + for (ci = 0; ci < cinfo->num_components; ci++) + + for (i = 0; i < DCTSIZE2; i++) + + *coef_bit_ptr++ = -1; + +} + + + +#endif /* D_PROGRESSIVE_SUPPORTED */ + diff --git a/utils/roq2/jpeg/jdpostct.c b/utils/roq2/jpeg/jdpostct.c new file mode 100644 index 0000000..a67fcad --- /dev/null +++ b/utils/roq2/jpeg/jdpostct.c @@ -0,0 +1,580 @@ +/* + + * jdpostct.c + + * + + * Copyright (C) 1994-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains the decompression postprocessing controller. + + * This controller manages the upsampling, color conversion, and color + + * quantization/reduction steps; specifically, it controls the buffering + + * between upsample/color conversion and color quantization/reduction. + + * + + * If no color quantization/reduction is required, then this module has no + + * work to do, and it just hands off to the upsample/color conversion code. + + * An integrated upsample/convert/quantize process would replace this module + + * entirely. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + + + +/* Private buffer controller object */ + + + +typedef struct { + + struct jpeg_d_post_controller pub; /* public fields */ + + + + /* Color quantization source buffer: this holds output data from + + * the upsample/color conversion step to be passed to the quantizer. + + * For two-pass color quantization, we need a full-image buffer; + + * for one-pass operation, a strip buffer is sufficient. + + */ + + jvirt_sarray_ptr whole_image; /* virtual array, or NULL if one-pass */ + + JSAMPARRAY buffer; /* strip buffer, or current strip of virtual */ + + JDIMENSION strip_height; /* buffer size in rows */ + + /* for two-pass mode only: */ + + JDIMENSION starting_row; /* row # of first row in current strip */ + + JDIMENSION next_row; /* index of next row to fill/empty in strip */ + +} my_post_controller; + + + +typedef my_post_controller * my_post_ptr; + + + + + +/* Forward declarations */ + +METHODDEF void post_process_1pass + + JPP((j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + + JDIMENSION in_row_groups_avail, + + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail)); + +#ifdef QUANT_2PASS_SUPPORTED + +METHODDEF void post_process_prepass + + JPP((j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + + JDIMENSION in_row_groups_avail, + + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail)); + +METHODDEF void post_process_2pass + + JPP((j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + + JDIMENSION in_row_groups_avail, + + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail)); + +#endif + + + + + +/* + + * Initialize for a processing pass. + + */ + + + +METHODDEF void + +start_pass_dpost (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) + +{ + + my_post_ptr post = (my_post_ptr) cinfo->post; + + + + switch (pass_mode) { + + case JBUF_PASS_THRU: + + if (cinfo->quantize_colors) { + + /* Single-pass processing with color quantization. */ + + post->pub.post_process_data = post_process_1pass; + + /* We could be doing buffered-image output before starting a 2-pass + + * color quantization; in that case, jinit_d_post_controller did not + + * allocate a strip buffer. Use the virtual-array buffer as workspace. + + */ + + if (post->buffer == NULL) { + + post->buffer = (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, post->whole_image, + + (JDIMENSION) 0, post->strip_height, TRUE); + + } + + } else { + + /* For single-pass processing without color quantization, + + * I have no work to do; just call the upsampler directly. + + */ + + post->pub.post_process_data = cinfo->upsample->upsample; + + } + + break; + +#ifdef QUANT_2PASS_SUPPORTED + + case JBUF_SAVE_AND_PASS: + + /* First pass of 2-pass quantization */ + + if (post->whole_image == NULL) + + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + post->pub.post_process_data = post_process_prepass; + + break; + + case JBUF_CRANK_DEST: + + /* Second pass of 2-pass quantization */ + + if (post->whole_image == NULL) + + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + post->pub.post_process_data = post_process_2pass; + + break; + +#endif /* QUANT_2PASS_SUPPORTED */ + + default: + + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + break; + + } + + post->starting_row = post->next_row = 0; + +} + + + + + +/* + + * Process some data in the one-pass (strip buffer) case. + + * This is used for color precision reduction as well as one-pass quantization. + + */ + + + +METHODDEF void + +post_process_1pass (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + + JDIMENSION in_row_groups_avail, + + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail) + +{ + + my_post_ptr post = (my_post_ptr) cinfo->post; + + JDIMENSION num_rows, max_rows; + + + + /* Fill the buffer, but not more than what we can dump out in one go. */ + + /* Note we rely on the upsampler to detect bottom of image. */ + + max_rows = out_rows_avail - *out_row_ctr; + + if (max_rows > post->strip_height) + + max_rows = post->strip_height; + + num_rows = 0; + + (*cinfo->upsample->upsample) (cinfo, + + input_buf, in_row_group_ctr, in_row_groups_avail, + + post->buffer, &num_rows, max_rows); + + /* Quantize and emit data. */ + + (*cinfo->cquantize->color_quantize) (cinfo, + + post->buffer, output_buf + *out_row_ctr, (int) num_rows); + + *out_row_ctr += num_rows; + +} + + + + + +#ifdef QUANT_2PASS_SUPPORTED + + + +/* + + * Process some data in the first pass of 2-pass quantization. + + */ + + + +METHODDEF void + +post_process_prepass (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + + JDIMENSION in_row_groups_avail, + + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail) + +{ + + my_post_ptr post = (my_post_ptr) cinfo->post; + + JDIMENSION old_next_row, num_rows; + + + + /* Reposition virtual buffer if at start of strip. */ + + if (post->next_row == 0) { + + post->buffer = (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, post->whole_image, + + post->starting_row, post->strip_height, TRUE); + + } + + + + /* Upsample some data (up to a strip height's worth). */ + + old_next_row = post->next_row; + + (*cinfo->upsample->upsample) (cinfo, + + input_buf, in_row_group_ctr, in_row_groups_avail, + + post->buffer, &post->next_row, post->strip_height); + + + + /* Allow quantizer to scan new data. No data is emitted, */ + + /* but we advance out_row_ctr so outer loop can tell when we're done. */ + + if (post->next_row > old_next_row) { + + num_rows = post->next_row - old_next_row; + + (*cinfo->cquantize->color_quantize) (cinfo, post->buffer + old_next_row, + + (JSAMPARRAY) NULL, (int) num_rows); + + *out_row_ctr += num_rows; + + } + + + + /* Advance if we filled the strip. */ + + if (post->next_row >= post->strip_height) { + + post->starting_row += post->strip_height; + + post->next_row = 0; + + } + +} + + + + + +/* + + * Process some data in the second pass of 2-pass quantization. + + */ + + + +METHODDEF void + +post_process_2pass (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + + JDIMENSION in_row_groups_avail, + + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail) + +{ + + my_post_ptr post = (my_post_ptr) cinfo->post; + + JDIMENSION num_rows, max_rows; + + + + /* Reposition virtual buffer if at start of strip. */ + + if (post->next_row == 0) { + + post->buffer = (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, post->whole_image, + + post->starting_row, post->strip_height, FALSE); + + } + + + + /* Determine number of rows to emit. */ + + num_rows = post->strip_height - post->next_row; /* available in strip */ + + max_rows = out_rows_avail - *out_row_ctr; /* available in output area */ + + if (num_rows > max_rows) + + num_rows = max_rows; + + /* We have to check bottom of image here, can't depend on upsampler. */ + + max_rows = cinfo->output_height - post->starting_row; + + if (num_rows > max_rows) + + num_rows = max_rows; + + + + /* Quantize and emit data. */ + + (*cinfo->cquantize->color_quantize) (cinfo, + + post->buffer + post->next_row, output_buf + *out_row_ctr, + + (int) num_rows); + + *out_row_ctr += num_rows; + + + + /* Advance if we filled the strip. */ + + post->next_row += num_rows; + + if (post->next_row >= post->strip_height) { + + post->starting_row += post->strip_height; + + post->next_row = 0; + + } + +} + + + +#endif /* QUANT_2PASS_SUPPORTED */ + + + + + +/* + + * Initialize postprocessing controller. + + */ + + + +GLOBAL void + +jinit_d_post_controller (j_decompress_ptr cinfo, boolean need_full_buffer) + +{ + + my_post_ptr post; + + + + post = (my_post_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(my_post_controller)); + + cinfo->post = (struct jpeg_d_post_controller *) post; + + post->pub.start_pass = start_pass_dpost; + + post->whole_image = NULL; /* flag for no virtual arrays */ + + post->buffer = NULL; /* flag for no strip buffer */ + + + + /* Create the quantization buffer, if needed */ + + if (cinfo->quantize_colors) { + + /* The buffer strip height is max_v_samp_factor, which is typically + + * an efficient number of rows for upsampling to return. + + * (In the presence of output rescaling, we might want to be smarter?) + + */ + + post->strip_height = (JDIMENSION) cinfo->max_v_samp_factor; + + if (need_full_buffer) { + + /* Two-pass color quantization: need full-image storage. */ + + /* We round up the number of rows to a multiple of the strip height. */ + +#ifdef QUANT_2PASS_SUPPORTED + + post->whole_image = (*cinfo->mem->request_virt_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + + cinfo->output_width * cinfo->out_color_components, + + (JDIMENSION) jround_up((long) cinfo->output_height, + + (long) post->strip_height), + + post->strip_height); + +#else + + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + +#endif /* QUANT_2PASS_SUPPORTED */ + + } else { + + /* One-pass color quantization: just make a strip buffer. */ + + post->buffer = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, + + cinfo->output_width * cinfo->out_color_components, + + post->strip_height); + + } + + } + +} + diff --git a/utils/roq2/jpeg/jdsample.c b/utils/roq2/jpeg/jdsample.c new file mode 100644 index 0000000..f401310 --- /dev/null +++ b/utils/roq2/jpeg/jdsample.c @@ -0,0 +1,956 @@ +/* + + * jdsample.c + + * + + * Copyright (C) 1991-1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains upsampling routines. + + * + + * Upsampling input data is counted in "row groups". A row group + + * is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) + + * sample rows of each component. Upsampling will normally produce + + * max_v_samp_factor pixel rows from each row group (but this could vary + + * if the upsampler is applying a scale factor of its own). + + * + + * An excellent reference for image resampling is + + * Digital Image Warping, George Wolberg, 1990. + + * Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + + + +/* Pointer to routine to upsample a single component */ + +typedef JMETHOD(void, upsample1_ptr, + + (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr)); + + + +/* Private subobject */ + + + +typedef struct { + + struct jpeg_upsampler pub; /* public fields */ + + + + /* Color conversion buffer. When using separate upsampling and color + + * conversion steps, this buffer holds one upsampled row group until it + + * has been color converted and output. + + * Note: we do not allocate any storage for component(s) which are full-size, + + * ie do not need rescaling. The corresponding entry of color_buf[] is + + * simply set to point to the input data array, thereby avoiding copying. + + */ + + JSAMPARRAY color_buf[MAX_COMPONENTS]; + + + + /* Per-component upsampling method pointers */ + + upsample1_ptr methods[MAX_COMPONENTS]; + + + + int next_row_out; /* counts rows emitted from color_buf */ + + JDIMENSION rows_to_go; /* counts rows remaining in image */ + + + + /* Height of an input row group for each component. */ + + int rowgroup_height[MAX_COMPONENTS]; + + + + /* These arrays save pixel expansion factors so that int_expand need not + + * recompute them each time. They are unused for other upsampling methods. + + */ + + UINT8 h_expand[MAX_COMPONENTS]; + + UINT8 v_expand[MAX_COMPONENTS]; + +} my_upsampler; + + + +typedef my_upsampler * my_upsample_ptr; + + + + + +/* + + * Initialize for an upsampling pass. + + */ + + + +METHODDEF void + +start_pass_upsample (j_decompress_ptr cinfo) + +{ + + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + + + /* Mark the conversion buffer empty */ + + upsample->next_row_out = cinfo->max_v_samp_factor; + + /* Initialize total-height counter for detecting bottom of image */ + + upsample->rows_to_go = cinfo->output_height; + +} + + + + + +/* + + * Control routine to do upsampling (and color conversion). + + * + + * In this version we upsample each component independently. + + * We upsample one row group into the conversion buffer, then apply + + * color conversion a row at a time. + + */ + + + +METHODDEF void + +sep_upsample (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + + JDIMENSION in_row_groups_avail, + + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail) + +{ + + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + int ci; + + jpeg_component_info * compptr; + + JDIMENSION num_rows; + + + + /* Fill the conversion buffer, if it's empty */ + + if (upsample->next_row_out >= cinfo->max_v_samp_factor) { + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + /* Invoke per-component upsample method. Notice we pass a POINTER + + * to color_buf[ci], so that fullsize_upsample can change it. + + */ + + (*upsample->methods[ci]) (cinfo, compptr, + + input_buf[ci] + (*in_row_group_ctr * upsample->rowgroup_height[ci]), + + upsample->color_buf + ci); + + } + + upsample->next_row_out = 0; + + } + + + + /* Color-convert and emit rows */ + + + + /* How many we have in the buffer: */ + + num_rows = (JDIMENSION) (cinfo->max_v_samp_factor - upsample->next_row_out); + + /* Not more than the distance to the end of the image. Need this test + + * in case the image height is not a multiple of max_v_samp_factor: + + */ + + if (num_rows > upsample->rows_to_go) + + num_rows = upsample->rows_to_go; + + /* And not more than what the client can accept: */ + + out_rows_avail -= *out_row_ctr; + + if (num_rows > out_rows_avail) + + num_rows = out_rows_avail; + + + + (*cinfo->cconvert->color_convert) (cinfo, upsample->color_buf, + + (JDIMENSION) upsample->next_row_out, + + output_buf + *out_row_ctr, + + (int) num_rows); + + + + /* Adjust counts */ + + *out_row_ctr += num_rows; + + upsample->rows_to_go -= num_rows; + + upsample->next_row_out += num_rows; + + /* When the buffer is emptied, declare this input row group consumed */ + + if (upsample->next_row_out >= cinfo->max_v_samp_factor) + + (*in_row_group_ctr)++; + +} + + + + + +/* + + * These are the routines invoked by sep_upsample to upsample pixel values + + * of a single component. One row group is processed per call. + + */ + + + + + +/* + + * For full-size components, we just make color_buf[ci] point at the + + * input buffer, and thus avoid copying any data. Note that this is + + * safe only because sep_upsample doesn't declare the input row group + + * "consumed" until we are done color converting and emitting it. + + */ + + + +METHODDEF void + +fullsize_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) + +{ + + *output_data_ptr = input_data; + +} + + + + + +/* + + * This is a no-op version used for "uninteresting" components. + + * These components will not be referenced by color conversion. + + */ + + + +METHODDEF void + +noop_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) + +{ + + *output_data_ptr = NULL; /* safety check */ + +} + + + + + +/* + + * This version handles any integral sampling ratios. + + * This is not used for typical JPEG files, so it need not be fast. + + * Nor, for that matter, is it particularly accurate: the algorithm is + + * simple replication of the input pixel onto the corresponding output + + * pixels. The hi-falutin sampling literature refers to this as a + + * "box filter". A box filter tends to introduce visible artifacts, + + * so if you are actually going to use 3:1 or 4:1 sampling ratios + + * you would be well advised to improve this code. + + */ + + + +METHODDEF void + +int_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) + +{ + + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + JSAMPARRAY output_data = *output_data_ptr; + + register JSAMPROW inptr, outptr; + + register JSAMPLE invalue; + + register int h; + + JSAMPROW outend; + + int h_expand, v_expand; + + int inrow, outrow; + + + + h_expand = upsample->h_expand[compptr->component_index]; + + v_expand = upsample->v_expand[compptr->component_index]; + + + + inrow = outrow = 0; + + while (outrow < cinfo->max_v_samp_factor) { + + /* Generate one output row with proper horizontal expansion */ + + inptr = input_data[inrow]; + + outptr = output_data[outrow]; + + outend = outptr + cinfo->output_width; + + while (outptr < outend) { + + invalue = *inptr++; /* don't need GETJSAMPLE() here */ + + for (h = h_expand; h > 0; h--) { + + *outptr++ = invalue; + + } + + } + + /* Generate any additional output rows by duplicating the first one */ + + if (v_expand > 1) { + + jcopy_sample_rows(output_data, outrow, output_data, outrow+1, + + v_expand-1, cinfo->output_width); + + } + + inrow++; + + outrow += v_expand; + + } + +} + + + + + +/* + + * Fast processing for the common case of 2:1 horizontal and 1:1 vertical. + + * It's still a box filter. + + */ + + + +METHODDEF void + +h2v1_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) + +{ + + JSAMPARRAY output_data = *output_data_ptr; + + register JSAMPROW inptr, outptr; + + register JSAMPLE invalue; + + JSAMPROW outend; + + int inrow; + + + + for (inrow = 0; inrow < cinfo->max_v_samp_factor; inrow++) { + + inptr = input_data[inrow]; + + outptr = output_data[inrow]; + + outend = outptr + cinfo->output_width; + + while (outptr < outend) { + + invalue = *inptr++; /* don't need GETJSAMPLE() here */ + + *outptr++ = invalue; + + *outptr++ = invalue; + + } + + } + +} + + + + + +/* + + * Fast processing for the common case of 2:1 horizontal and 2:1 vertical. + + * It's still a box filter. + + */ + + + +METHODDEF void + +h2v2_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) + +{ + + JSAMPARRAY output_data = *output_data_ptr; + + register JSAMPROW inptr, outptr; + + register JSAMPLE invalue; + + JSAMPROW outend; + + int inrow, outrow; + + + + inrow = outrow = 0; + + while (outrow < cinfo->max_v_samp_factor) { + + inptr = input_data[inrow]; + + outptr = output_data[outrow]; + + outend = outptr + cinfo->output_width; + + while (outptr < outend) { + + invalue = *inptr++; /* don't need GETJSAMPLE() here */ + + *outptr++ = invalue; + + *outptr++ = invalue; + + } + + jcopy_sample_rows(output_data, outrow, output_data, outrow+1, + + 1, cinfo->output_width); + + inrow++; + + outrow += 2; + + } + +} + + + + + +/* + + * Fancy processing for the common case of 2:1 horizontal and 1:1 vertical. + + * + + * The upsampling algorithm is linear interpolation between pixel centers, + + * also known as a "triangle filter". This is a good compromise between + + * speed and visual quality. The centers of the output pixels are 1/4 and 3/4 + + * of the way between input pixel centers. + + * + + * A note about the "bias" calculations: when rounding fractional values to + + * integer, we do not want to always round 0.5 up to the next integer. + + * If we did that, we'd introduce a noticeable bias towards larger values. + + * Instead, this code is arranged so that 0.5 will be rounded up or down at + + * alternate pixel locations (a simple ordered dither pattern). + + */ + + + +METHODDEF void + +h2v1_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) + +{ + + JSAMPARRAY output_data = *output_data_ptr; + + register JSAMPROW inptr, outptr; + + register int invalue; + + register JDIMENSION colctr; + + int inrow; + + + + for (inrow = 0; inrow < cinfo->max_v_samp_factor; inrow++) { + + inptr = input_data[inrow]; + + outptr = output_data[inrow]; + + /* Special case for first column */ + + invalue = GETJSAMPLE(*inptr++); + + *outptr++ = (JSAMPLE) invalue; + + *outptr++ = (JSAMPLE) ((invalue * 3 + GETJSAMPLE(*inptr) + 2) >> 2); + + + + for (colctr = compptr->downsampled_width - 2; colctr > 0; colctr--) { + + /* General case: 3/4 * nearer pixel + 1/4 * further pixel */ + + invalue = GETJSAMPLE(*inptr++) * 3; + + *outptr++ = (JSAMPLE) ((invalue + GETJSAMPLE(inptr[-2]) + 1) >> 2); + + *outptr++ = (JSAMPLE) ((invalue + GETJSAMPLE(*inptr) + 2) >> 2); + + } + + + + /* Special case for last column */ + + invalue = GETJSAMPLE(*inptr); + + *outptr++ = (JSAMPLE) ((invalue * 3 + GETJSAMPLE(inptr[-1]) + 1) >> 2); + + *outptr++ = (JSAMPLE) invalue; + + } + +} + + + + + +/* + + * Fancy processing for the common case of 2:1 horizontal and 2:1 vertical. + + * Again a triangle filter; see comments for h2v1 case, above. + + * + + * It is OK for us to reference the adjacent input rows because we demanded + + * context from the main buffer controller (see initialization code). + + */ + + + +METHODDEF void + +h2v2_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) + +{ + + JSAMPARRAY output_data = *output_data_ptr; + + register JSAMPROW inptr0, inptr1, outptr; + +#if BITS_IN_JSAMPLE == 8 + + register int thiscolsum, lastcolsum, nextcolsum; + +#else + + register INT32 thiscolsum, lastcolsum, nextcolsum; + +#endif + + register JDIMENSION colctr; + + int inrow, outrow, v; + + + + inrow = outrow = 0; + + while (outrow < cinfo->max_v_samp_factor) { + + for (v = 0; v < 2; v++) { + + /* inptr0 points to nearest input row, inptr1 points to next nearest */ + + inptr0 = input_data[inrow]; + + if (v == 0) /* next nearest is row above */ + + inptr1 = input_data[inrow-1]; + + else /* next nearest is row below */ + + inptr1 = input_data[inrow+1]; + + outptr = output_data[outrow++]; + + + + /* Special case for first column */ + + thiscolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++); + + nextcolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++); + + *outptr++ = (JSAMPLE) ((thiscolsum * 4 + 8) >> 4); + + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + nextcolsum + 7) >> 4); + + lastcolsum = thiscolsum; thiscolsum = nextcolsum; + + + + for (colctr = compptr->downsampled_width - 2; colctr > 0; colctr--) { + + /* General case: 3/4 * nearer pixel + 1/4 * further pixel in each */ + + /* dimension, thus 9/16, 3/16, 3/16, 1/16 overall */ + + nextcolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++); + + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + lastcolsum + 8) >> 4); + + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + nextcolsum + 7) >> 4); + + lastcolsum = thiscolsum; thiscolsum = nextcolsum; + + } + + + + /* Special case for last column */ + + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + lastcolsum + 8) >> 4); + + *outptr++ = (JSAMPLE) ((thiscolsum * 4 + 7) >> 4); + + } + + inrow++; + + } + +} + + + + + +/* + + * Module initialization routine for upsampling. + + */ + + + +GLOBAL void + +jinit_upsampler (j_decompress_ptr cinfo) + +{ + + my_upsample_ptr upsample; + + int ci; + + jpeg_component_info * compptr; + + boolean need_buffer, do_fancy; + + int h_in_group, v_in_group, h_out_group, v_out_group; + + + + upsample = (my_upsample_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(my_upsampler)); + + cinfo->upsample = (struct jpeg_upsampler *) upsample; + + upsample->pub.start_pass = start_pass_upsample; + + upsample->pub.upsample = sep_upsample; + + upsample->pub.need_context_rows = FALSE; /* until we find out differently */ + + + + if (cinfo->CCIR601_sampling) /* this isn't supported */ + + ERREXIT(cinfo, JERR_CCIR601_NOTIMPL); + + + + /* jdmainct.c doesn't support context rows when min_DCT_scaled_size = 1, + + * so don't ask for it. + + */ + + do_fancy = cinfo->do_fancy_upsampling && cinfo->min_DCT_scaled_size > 1; + + + + /* Verify we can handle the sampling factors, select per-component methods, + + * and create storage as needed. + + */ + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + /* Compute size of an "input group" after IDCT scaling. This many samples + + * are to be converted to max_h_samp_factor * max_v_samp_factor pixels. + + */ + + h_in_group = (compptr->h_samp_factor * compptr->DCT_scaled_size) / + + cinfo->min_DCT_scaled_size; + + v_in_group = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + + cinfo->min_DCT_scaled_size; + + h_out_group = cinfo->max_h_samp_factor; + + v_out_group = cinfo->max_v_samp_factor; + + upsample->rowgroup_height[ci] = v_in_group; /* save for use later */ + + need_buffer = TRUE; + + if (! compptr->component_needed) { + + /* Don't bother to upsample an uninteresting component. */ + + upsample->methods[ci] = noop_upsample; + + need_buffer = FALSE; + + } else if (h_in_group == h_out_group && v_in_group == v_out_group) { + + /* Fullsize components can be processed without any work. */ + + upsample->methods[ci] = fullsize_upsample; + + need_buffer = FALSE; + + } else if (h_in_group * 2 == h_out_group && + + v_in_group == v_out_group) { + + /* Special cases for 2h1v upsampling */ + + if (do_fancy && compptr->downsampled_width > 2) + + upsample->methods[ci] = h2v1_fancy_upsample; + + else + + upsample->methods[ci] = h2v1_upsample; + + } else if (h_in_group * 2 == h_out_group && + + v_in_group * 2 == v_out_group) { + + /* Special cases for 2h2v upsampling */ + + if (do_fancy && compptr->downsampled_width > 2) { + + upsample->methods[ci] = h2v2_fancy_upsample; + + upsample->pub.need_context_rows = TRUE; + + } else + + upsample->methods[ci] = h2v2_upsample; + + } else if ((h_out_group % h_in_group) == 0 && + + (v_out_group % v_in_group) == 0) { + + /* Generic integral-factors upsampling method */ + + upsample->methods[ci] = int_upsample; + + upsample->h_expand[ci] = (UINT8) (h_out_group / h_in_group); + + upsample->v_expand[ci] = (UINT8) (v_out_group / v_in_group); + + } else + + ERREXIT(cinfo, JERR_FRACT_SAMPLE_NOTIMPL); + + if (need_buffer) { + + upsample->color_buf[ci] = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (JDIMENSION) jround_up((long) cinfo->output_width, + + (long) cinfo->max_h_samp_factor), + + (JDIMENSION) cinfo->max_v_samp_factor); + + } + + } + +} + diff --git a/utils/roq2/jpeg/jdtrans.c b/utils/roq2/jpeg/jdtrans.c new file mode 100644 index 0000000..fd34ec1 --- /dev/null +++ b/utils/roq2/jpeg/jdtrans.c @@ -0,0 +1,244 @@ +/* + + * jdtrans.c + + * + + * Copyright (C) 1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains library routines for transcoding decompression, + + * that is, reading raw DCT coefficient arrays from an input JPEG file. + + * The routines in jdapimin.c will also be needed by a transcoder. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + + + +/* Forward declarations */ + +LOCAL void transdecode_master_selection JPP((j_decompress_ptr cinfo)); + + + + + +/* + + * Read the coefficient arrays from a JPEG file. + + * jpeg_read_header must be completed before calling this. + + * + + * The entire image is read into a set of virtual coefficient-block arrays, + + * one per component. The return value is a pointer to the array of + + * virtual-array descriptors. These can be manipulated directly via the + + * JPEG memory manager, or handed off to jpeg_write_coefficients(). + + * To release the memory occupied by the virtual arrays, call + + * jpeg_finish_decompress() when done with the data. + + * + + * Returns NULL if suspended. This case need be checked only if + + * a suspending data source is used. + + */ + + + +GLOBAL jvirt_barray_ptr * + +jpeg_read_coefficients (j_decompress_ptr cinfo) + +{ + + if (cinfo->global_state == DSTATE_READY) { + + /* First call: initialize active modules */ + + transdecode_master_selection(cinfo); + + cinfo->global_state = DSTATE_RDCOEFS; + + } else if (cinfo->global_state != DSTATE_RDCOEFS) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* Absorb whole file into the coef buffer */ + + for (;;) { + + int retcode; + + /* Call progress monitor hook if present */ + + if (cinfo->progress != NULL) + + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + + /* Absorb some more input */ + + retcode = (*cinfo->inputctl->consume_input) (cinfo); + + if (retcode == JPEG_SUSPENDED) + + return NULL; + + if (retcode == JPEG_REACHED_EOI) + + break; + + /* Advance progress counter if appropriate */ + + if (cinfo->progress != NULL && + + (retcode == JPEG_ROW_COMPLETED || retcode == JPEG_REACHED_SOS)) { + + if (++cinfo->progress->pass_counter >= cinfo->progress->pass_limit) { + + /* startup underestimated number of scans; ratchet up one scan */ + + cinfo->progress->pass_limit += (long) cinfo->total_iMCU_rows; + + } + + } + + } + + /* Set state so that jpeg_finish_decompress does the right thing */ + + cinfo->global_state = DSTATE_STOPPING; + + return cinfo->coef->coef_arrays; + +} + + + + + +/* + + * Master selection of decompression modules for transcoding. + + * This substitutes for jdmaster.c's initialization of the full decompressor. + + */ + + + +LOCAL void + +transdecode_master_selection (j_decompress_ptr cinfo) + +{ + + /* Entropy decoding: either Huffman or arithmetic coding. */ + + if (cinfo->arith_code) { + + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + + } else { + + if (cinfo->progressive_mode) { + +#ifdef D_PROGRESSIVE_SUPPORTED + + jinit_phuff_decoder(cinfo); + +#else + + ERREXIT(cinfo, JERR_NOT_COMPILED); + +#endif + + } else + + jinit_huff_decoder(cinfo); + + } + + + + /* Always get a full-image coefficient buffer. */ + + jinit_d_coef_controller(cinfo, TRUE); + + + + /* We can now tell the memory manager to allocate virtual arrays. */ + + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + + + /* Initialize input side of decompressor to consume first scan. */ + + (*cinfo->inputctl->start_input_pass) (cinfo); + + + + /* Initialize progress monitoring. */ + + if (cinfo->progress != NULL) { + + int nscans; + + /* Estimate number of scans to set pass_limit. */ + + if (cinfo->progressive_mode) { + + /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ + + nscans = 2 + 3 * cinfo->num_components; + + } else if (cinfo->inputctl->has_multiple_scans) { + + /* For a nonprogressive multiscan file, estimate 1 scan per component. */ + + nscans = cinfo->num_components; + + } else { + + nscans = 1; + + } + + cinfo->progress->pass_counter = 0L; + + cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows * nscans; + + cinfo->progress->completed_passes = 0; + + cinfo->progress->total_passes = 1; + + } + +} + diff --git a/utils/roq2/jpeg/jerror.c b/utils/roq2/jpeg/jerror.c new file mode 100644 index 0000000..4424990 --- /dev/null +++ b/utils/roq2/jpeg/jerror.c @@ -0,0 +1,456 @@ +/* + + * jerror.c + + * + + * Copyright (C) 1991-1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains simple error-reporting and trace-message routines. + + * These are suitable for Unix-like systems and others where writing to + + * stderr is the right thing to do. Many applications will want to replace + + * some or all of these routines. + + * + + * These routines are used by both the compression and decompression code. + + */ + + + +/* this is not a core library module, so it doesn't define JPEG_INTERNALS */ + +#include "jinclude.h" + +#include "jpeglib.h" + +#include "jversion.h" + +#include "jerror.h" + + + +#ifndef EXIT_FAILURE /* define exit() codes if not provided */ + +#define EXIT_FAILURE 1 + +#endif + + + + + +/* + + * Create the message string table. + + * We do this from the master message list in jerror.h by re-reading + + * jerror.h with a suitable definition for macro JMESSAGE. + + * The message table is made an external symbol just in case any applications + + * want to refer to it directly. + + */ + + + +#ifdef NEED_SHORT_EXTERNAL_NAMES + +#define jpeg_std_message_table jMsgTable + +#endif + + + +#define JMESSAGE(code,string) string , + + + +const char * const jpeg_std_message_table[] = { + +#include "jerror.h" + + NULL + +}; + + + + + +/* + + * Error exit handler: must not return to caller. + + * + + * Applications may override this if they want to get control back after + + * an error. Typically one would longjmp somewhere instead of exiting. + + * The setjmp buffer can be made a private field within an expanded error + + * handler object. Note that the info needed to generate an error message + + * is stored in the error object, so you can generate the message now or + + * later, at your convenience. + + * You should make sure that the JPEG object is cleaned up (with jpeg_abort + + * or jpeg_destroy) at some point. + + */ + + + +METHODDEF void + +error_exit (j_common_ptr cinfo) + +{ + + /* Always display the message */ + + (*cinfo->err->output_message) (cinfo); + + + + /* Let the memory manager delete any temp files before we die */ + + jpeg_destroy(cinfo); + + + + exit(EXIT_FAILURE); + +} + + + + + +/* + + * Actual output of an error or trace message. + + * Applications may override this method to send JPEG messages somewhere + + * other than stderr. + + */ + + + +METHODDEF void + +output_message (j_common_ptr cinfo) + +{ + + char buffer[JMSG_LENGTH_MAX]; + + + + /* Create the message */ + + (*cinfo->err->format_message) (cinfo, buffer); + + + + /* Send it to stderr, adding a newline */ + + fprintf(stderr, "%s\n", buffer); + +} + + + + + +/* + + * Decide whether to emit a trace or warning message. + + * msg_level is one of: + + * -1: recoverable corrupt-data warning, may want to abort. + + * 0: important advisory messages (always display to user). + + * 1: first level of tracing detail. + + * 2,3,...: successively more detailed tracing messages. + + * An application might override this method if it wanted to abort on warnings + + * or change the policy about which messages to display. + + */ + + + +METHODDEF void + +emit_message (j_common_ptr cinfo, int msg_level) + +{ + + struct jpeg_error_mgr * err = cinfo->err; + + + + if (msg_level < 0) { + + /* It's a warning message. Since corrupt files may generate many warnings, + + * the policy implemented here is to show only the first warning, + + * unless trace_level >= 3. + + */ + + if (err->num_warnings == 0 || err->trace_level >= 3) + + (*err->output_message) (cinfo); + + /* Always count warnings in num_warnings. */ + + err->num_warnings++; + + } else { + + /* It's a trace message. Show it if trace_level >= msg_level. */ + + if (err->trace_level >= msg_level) + + (*err->output_message) (cinfo); + + } + +} + + + + + +/* + + * Format a message string for the most recent JPEG error or message. + + * The message is stored into buffer, which should be at least JMSG_LENGTH_MAX + + * characters. Note that no '\n' character is added to the string. + + * Few applications should need to override this method. + + */ + + + +METHODDEF void + +format_message (j_common_ptr cinfo, char * buffer) + +{ + + struct jpeg_error_mgr * err = cinfo->err; + + int msg_code = err->msg_code; + + const char * msgtext = NULL; + + const char * msgptr; + + char ch; + + boolean isstring; + + + + /* Look up message string in proper table */ + + if (msg_code > 0 && msg_code <= err->last_jpeg_message) { + + msgtext = err->jpeg_message_table[msg_code]; + + } else if (err->addon_message_table != NULL && + + msg_code >= err->first_addon_message && + + msg_code <= err->last_addon_message) { + + msgtext = err->addon_message_table[msg_code - err->first_addon_message]; + + } + + + + /* Defend against bogus message number */ + + if (msgtext == NULL) { + + err->msg_parm.i[0] = msg_code; + + msgtext = err->jpeg_message_table[0]; + + } + + + + /* Check for string parameter, as indicated by %s in the message text */ + + isstring = FALSE; + + msgptr = msgtext; + + while ((ch = *msgptr++) != '\0') { + + if (ch == '%') { + + if (*msgptr == 's') isstring = TRUE; + + break; + + } + + } + + + + /* Format the message into the passed buffer */ + + if (isstring) + + sprintf(buffer, msgtext, err->msg_parm.s); + + else + + sprintf(buffer, msgtext, + + err->msg_parm.i[0], err->msg_parm.i[1], + + err->msg_parm.i[2], err->msg_parm.i[3], + + err->msg_parm.i[4], err->msg_parm.i[5], + + err->msg_parm.i[6], err->msg_parm.i[7]); + +} + + + + + +/* + + * Reset error state variables at start of a new image. + + * This is called during compression startup to reset trace/error + + * processing to default state, without losing any application-specific + + * method pointers. An application might possibly want to override + + * this method if it has additional error processing state. + + */ + + + +METHODDEF void + +reset_error_mgr (j_common_ptr cinfo) + +{ + + cinfo->err->num_warnings = 0; + + /* trace_level is not reset since it is an application-supplied parameter */ + + cinfo->err->msg_code = 0; /* may be useful as a flag for "no error" */ + +} + + + + + +/* + + * Fill in the standard error-handling methods in a jpeg_error_mgr object. + + * Typical call is: + + * struct jpeg_compress_struct cinfo; + + * struct jpeg_error_mgr err; + + * + + * cinfo.err = jpeg_std_error(&err); + + * after which the application may override some of the methods. + + */ + + + +GLOBAL struct jpeg_error_mgr * + +jpeg_std_error (struct jpeg_error_mgr * err) + +{ + + err->error_exit = error_exit; + + err->emit_message = emit_message; + + err->output_message = output_message; + + err->format_message = format_message; + + err->reset_error_mgr = reset_error_mgr; + + + + err->trace_level = 0; /* default = no tracing */ + + err->num_warnings = 0; /* no warnings emitted yet */ + + err->msg_code = 0; /* may be useful as a flag for "no error" */ + + + + /* Initialize message table pointers */ + + err->jpeg_message_table = jpeg_std_message_table; + + err->last_jpeg_message = (int) JMSG_LASTMSGCODE - 1; + + + + err->addon_message_table = NULL; + + err->first_addon_message = 0; /* for safety */ + + err->last_addon_message = 0; + + + + return err; + +} + diff --git a/utils/roq2/jpeg/jerror.h b/utils/roq2/jpeg/jerror.h new file mode 100644 index 0000000..8fb5a4d --- /dev/null +++ b/utils/roq2/jpeg/jerror.h @@ -0,0 +1,546 @@ +/* + + * jerror.h + + * + + * Copyright (C) 1994-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file defines the error and message codes for the JPEG library. + + * Edit this file to add new codes, or to translate the message strings to + + * some other language. + + * A set of error-reporting macros are defined too. Some applications using + + * the JPEG library may wish to include this file to get the error codes + + * and/or the macros. + + */ + + + +/* + + * To define the enum list of message codes, include this file without + + * defining macro JMESSAGE. To create a message string table, include it + + * again with a suitable JMESSAGE definition (see jerror.c for an example). + + */ + +#ifndef JMESSAGE + +#ifndef JERROR_H + +/* First time through, define the enum list */ + +#define JMAKE_ENUM_LIST + +#else + +/* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */ + +#define JMESSAGE(code,string) + +#endif /* JERROR_H */ + +#endif /* JMESSAGE */ + + + +#ifdef JMAKE_ENUM_LIST + + + +typedef enum { + + + +#define JMESSAGE(code,string) code , + + + +#endif /* JMAKE_ENUM_LIST */ + + + +JMESSAGE(JMSG_NOMESSAGE, "Bogus message code %d") /* Must be first entry! */ + + + +/* For maintenance convenience, list is alphabetical by message code name */ + +JMESSAGE(JERR_ARITH_NOTIMPL, + + "Sorry, there are legal restrictions on arithmetic coding") + +JMESSAGE(JERR_BAD_ALIGN_TYPE, "ALIGN_TYPE is wrong, please fix") + +JMESSAGE(JERR_BAD_ALLOC_CHUNK, "MAX_ALLOC_CHUNK is wrong, please fix") + +JMESSAGE(JERR_BAD_BUFFER_MODE, "Bogus buffer control mode") + +JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS") + +JMESSAGE(JERR_BAD_DCTSIZE, "IDCT output block size %d not supported") + +JMESSAGE(JERR_BAD_IN_COLORSPACE, "Bogus input colorspace") + +JMESSAGE(JERR_BAD_J_COLORSPACE, "Bogus JPEG colorspace") + +JMESSAGE(JERR_BAD_LENGTH, "Bogus marker length") + +JMESSAGE(JERR_BAD_MCU_SIZE, "Sampling factors too large for interleaved scan") + +JMESSAGE(JERR_BAD_POOL_ID, "Invalid memory pool code %d") + +JMESSAGE(JERR_BAD_PRECISION, "Unsupported JPEG data precision %d") + +JMESSAGE(JERR_BAD_PROGRESSION, + + "Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d") + +JMESSAGE(JERR_BAD_PROG_SCRIPT, + + "Invalid progressive parameters at scan script entry %d") + +JMESSAGE(JERR_BAD_SAMPLING, "Bogus sampling factors") + +JMESSAGE(JERR_BAD_SCAN_SCRIPT, "Invalid scan script at entry %d") + +JMESSAGE(JERR_BAD_STATE, "Improper call to JPEG library in state %d") + +JMESSAGE(JERR_BAD_VIRTUAL_ACCESS, "Bogus virtual array access") + +JMESSAGE(JERR_BUFFER_SIZE, "Buffer passed to JPEG library is too small") + +JMESSAGE(JERR_CANT_SUSPEND, "Suspension not allowed here") + +JMESSAGE(JERR_CCIR601_NOTIMPL, "CCIR601 sampling not implemented yet") + +JMESSAGE(JERR_COMPONENT_COUNT, "Too many color components: %d, max %d") + +JMESSAGE(JERR_CONVERSION_NOTIMPL, "Unsupported color conversion request") + +JMESSAGE(JERR_DAC_INDEX, "Bogus DAC index %d") + +JMESSAGE(JERR_DAC_VALUE, "Bogus DAC value 0x%x") + +JMESSAGE(JERR_DHT_COUNTS, "Bogus DHT counts") + +JMESSAGE(JERR_DHT_INDEX, "Bogus DHT index %d") + +JMESSAGE(JERR_DQT_INDEX, "Bogus DQT index %d") + +JMESSAGE(JERR_EMPTY_IMAGE, "Empty JPEG image (DNL not supported)") + +JMESSAGE(JERR_EMS_READ, "Read from EMS failed") + +JMESSAGE(JERR_EMS_WRITE, "Write to EMS failed") + +JMESSAGE(JERR_EOI_EXPECTED, "Didn't expect more than one scan") + +JMESSAGE(JERR_FILE_READ, "Input file read error") + +JMESSAGE(JERR_FILE_WRITE, "Output file write error --- out of disk space?") + +JMESSAGE(JERR_FRACT_SAMPLE_NOTIMPL, "Fractional sampling not implemented yet") + +JMESSAGE(JERR_HUFF_CLEN_OVERFLOW, "Huffman code size table overflow") + +JMESSAGE(JERR_HUFF_MISSING_CODE, "Missing Huffman code table entry") + +JMESSAGE(JERR_IMAGE_TOO_BIG, "Maximum supported image dimension is %u pixels") + +JMESSAGE(JERR_INPUT_EMPTY, "Empty input file") + +JMESSAGE(JERR_INPUT_EOF, "Premature end of input file") + +JMESSAGE(JERR_MISMATCHED_QUANT_TABLE, + + "Cannot transcode due to multiple use of quantization table %d") + +JMESSAGE(JERR_MISSING_DATA, "Scan script does not transmit all data") + +JMESSAGE(JERR_MODE_CHANGE, "Invalid color quantization mode change") + +JMESSAGE(JERR_NOTIMPL, "Not implemented yet") + +JMESSAGE(JERR_NOT_COMPILED, "Requested feature was omitted at compile time") + +JMESSAGE(JERR_NO_BACKING_STORE, "Backing store not supported") + +JMESSAGE(JERR_NO_HUFF_TABLE, "Huffman table 0x%02x was not defined") + +JMESSAGE(JERR_NO_IMAGE, "JPEG datastream contains no image") + +JMESSAGE(JERR_NO_QUANT_TABLE, "Quantization table 0x%02x was not defined") + +JMESSAGE(JERR_NO_SOI, "Not a JPEG file: starts with 0x%02x 0x%02x") + +JMESSAGE(JERR_OUT_OF_MEMORY, "Insufficient memory (case %d)") + +JMESSAGE(JERR_QUANT_COMPONENTS, + + "Cannot quantize more than %d color components") + +JMESSAGE(JERR_QUANT_FEW_COLORS, "Cannot quantize to fewer than %d colors") + +JMESSAGE(JERR_QUANT_MANY_COLORS, "Cannot quantize to more than %d colors") + +JMESSAGE(JERR_SOF_DUPLICATE, "Invalid JPEG file structure: two SOF markers") + +JMESSAGE(JERR_SOF_NO_SOS, "Invalid JPEG file structure: missing SOS marker") + +JMESSAGE(JERR_SOF_UNSUPPORTED, "Unsupported JPEG process: SOF type 0x%02x") + +JMESSAGE(JERR_SOI_DUPLICATE, "Invalid JPEG file structure: two SOI markers") + +JMESSAGE(JERR_SOS_NO_SOF, "Invalid JPEG file structure: SOS before SOF") + +JMESSAGE(JERR_TFILE_CREATE, "Failed to create temporary file %s") + +JMESSAGE(JERR_TFILE_READ, "Read failed on temporary file") + +JMESSAGE(JERR_TFILE_SEEK, "Seek failed on temporary file") + +JMESSAGE(JERR_TFILE_WRITE, + + "Write failed on temporary file --- out of disk space?") + +JMESSAGE(JERR_TOO_LITTLE_DATA, "Application transferred too few scanlines") + +JMESSAGE(JERR_UNKNOWN_MARKER, "Unsupported marker type 0x%02x") + +JMESSAGE(JERR_VIRTUAL_BUG, "Virtual array controller messed up") + +JMESSAGE(JERR_WIDTH_OVERFLOW, "Image too wide for this implementation") + +JMESSAGE(JERR_XMS_READ, "Read from XMS failed") + +JMESSAGE(JERR_XMS_WRITE, "Write to XMS failed") + +JMESSAGE(JMSG_COPYRIGHT, JCOPYRIGHT) + +JMESSAGE(JMSG_VERSION, JVERSION) + +JMESSAGE(JTRC_16BIT_TABLES, + + "Caution: quantization tables are too coarse for baseline JPEG") + +JMESSAGE(JTRC_ADOBE, + + "Adobe APP14 marker: version %d, flags 0x%04x 0x%04x, transform %d") + +JMESSAGE(JTRC_APP0, "Unknown APP0 marker (not JFIF), length %u") + +JMESSAGE(JTRC_APP14, "Unknown APP14 marker (not Adobe), length %u") + +JMESSAGE(JTRC_DAC, "Define Arithmetic Table 0x%02x: 0x%02x") + +JMESSAGE(JTRC_DHT, "Define Huffman Table 0x%02x") + +JMESSAGE(JTRC_DQT, "Define Quantization Table %d precision %d") + +JMESSAGE(JTRC_DRI, "Define Restart Interval %u") + +JMESSAGE(JTRC_EMS_CLOSE, "Freed EMS handle %u") + +JMESSAGE(JTRC_EMS_OPEN, "Obtained EMS handle %u") + +JMESSAGE(JTRC_EOI, "End Of Image") + +JMESSAGE(JTRC_HUFFBITS, " %3d %3d %3d %3d %3d %3d %3d %3d") + +JMESSAGE(JTRC_JFIF, "JFIF APP0 marker, density %dx%d %d") + +JMESSAGE(JTRC_JFIF_BADTHUMBNAILSIZE, + + "Warning: thumbnail image size does not match data length %u") + +JMESSAGE(JTRC_JFIF_MINOR, "Unknown JFIF minor revision number %d.%02d") + +JMESSAGE(JTRC_JFIF_THUMBNAIL, " with %d x %d thumbnail image") + +JMESSAGE(JTRC_MISC_MARKER, "Skipping marker 0x%02x, length %u") + +JMESSAGE(JTRC_PARMLESS_MARKER, "Unexpected marker 0x%02x") + +JMESSAGE(JTRC_QUANTVALS, " %4u %4u %4u %4u %4u %4u %4u %4u") + +JMESSAGE(JTRC_QUANT_3_NCOLORS, "Quantizing to %d = %d*%d*%d colors") + +JMESSAGE(JTRC_QUANT_NCOLORS, "Quantizing to %d colors") + +JMESSAGE(JTRC_QUANT_SELECTED, "Selected %d colors for quantization") + +JMESSAGE(JTRC_RECOVERY_ACTION, "At marker 0x%02x, recovery action %d") + +JMESSAGE(JTRC_RST, "RST%d") + +JMESSAGE(JTRC_SMOOTH_NOTIMPL, + + "Smoothing not supported with nonstandard sampling ratios") + +JMESSAGE(JTRC_SOF, "Start Of Frame 0x%02x: width=%u, height=%u, components=%d") + +JMESSAGE(JTRC_SOF_COMPONENT, " Component %d: %dhx%dv q=%d") + +JMESSAGE(JTRC_SOI, "Start of Image") + +JMESSAGE(JTRC_SOS, "Start Of Scan: %d components") + +JMESSAGE(JTRC_SOS_COMPONENT, " Component %d: dc=%d ac=%d") + +JMESSAGE(JTRC_SOS_PARAMS, " Ss=%d, Se=%d, Ah=%d, Al=%d") + +JMESSAGE(JTRC_TFILE_CLOSE, "Closed temporary file %s") + +JMESSAGE(JTRC_TFILE_OPEN, "Opened temporary file %s") + +JMESSAGE(JTRC_UNKNOWN_IDS, + + "Unrecognized component IDs %d %d %d, assuming YCbCr") + +JMESSAGE(JTRC_XMS_CLOSE, "Freed XMS handle %u") + +JMESSAGE(JTRC_XMS_OPEN, "Obtained XMS handle %u") + +JMESSAGE(JWRN_ADOBE_XFORM, "Unknown Adobe color transform code %d") + +JMESSAGE(JWRN_BOGUS_PROGRESSION, + + "Inconsistent progression sequence for component %d coefficient %d") + +JMESSAGE(JWRN_EXTRANEOUS_DATA, + + "Corrupt JPEG data: %u extraneous bytes before marker 0x%02x") + +JMESSAGE(JWRN_HIT_MARKER, "Corrupt JPEG data: premature end of data segment") + +JMESSAGE(JWRN_HUFF_BAD_CODE, "Corrupt JPEG data: bad Huffman code") + +JMESSAGE(JWRN_JFIF_MAJOR, "Warning: unknown JFIF revision number %d.%02d") + +JMESSAGE(JWRN_JPEG_EOF, "Premature end of JPEG file") + +JMESSAGE(JWRN_MUST_RESYNC, + + "Corrupt JPEG data: found marker 0x%02x instead of RST%d") + +JMESSAGE(JWRN_NOT_SEQUENTIAL, "Invalid SOS parameters for sequential JPEG") + +JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") + + + +#ifdef JMAKE_ENUM_LIST + + + + JMSG_LASTMSGCODE + +} J_MESSAGE_CODE; + + + +#undef JMAKE_ENUM_LIST + +#endif /* JMAKE_ENUM_LIST */ + + + +/* Zap JMESSAGE macro so that future re-inclusions do nothing by default */ + +#undef JMESSAGE + + + + + +#ifndef JERROR_H + +#define JERROR_H + + + +/* Macros to simplify using the error and trace message stuff */ + +/* The first parameter is either type of cinfo pointer */ + + + +/* Fatal errors (print message and exit) */ + +#define ERREXIT(cinfo,code) \ + + ((cinfo)->err->msg_code = (code), \ + + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) + +#define ERREXIT1(cinfo,code,p1) \ + + ((cinfo)->err->msg_code = (code), \ + + (cinfo)->err->msg_parm.i[0] = (p1), \ + + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) + +#define ERREXIT2(cinfo,code,p1,p2) \ + + ((cinfo)->err->msg_code = (code), \ + + (cinfo)->err->msg_parm.i[0] = (p1), \ + + (cinfo)->err->msg_parm.i[1] = (p2), \ + + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) + +#define ERREXIT3(cinfo,code,p1,p2,p3) \ + + ((cinfo)->err->msg_code = (code), \ + + (cinfo)->err->msg_parm.i[0] = (p1), \ + + (cinfo)->err->msg_parm.i[1] = (p2), \ + + (cinfo)->err->msg_parm.i[2] = (p3), \ + + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) + +#define ERREXIT4(cinfo,code,p1,p2,p3,p4) \ + + ((cinfo)->err->msg_code = (code), \ + + (cinfo)->err->msg_parm.i[0] = (p1), \ + + (cinfo)->err->msg_parm.i[1] = (p2), \ + + (cinfo)->err->msg_parm.i[2] = (p3), \ + + (cinfo)->err->msg_parm.i[3] = (p4), \ + + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) + +#define ERREXITS(cinfo,code,str) \ + + ((cinfo)->err->msg_code = (code), \ + + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) + + + +#define MAKESTMT(stuff) do { stuff } while (0) + + + +/* Nonfatal errors (we can keep going, but the data is probably corrupt) */ + +#define WARNMS(cinfo,code) \ + + ((cinfo)->err->msg_code = (code), \ + + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) + +#define WARNMS1(cinfo,code,p1) \ + + ((cinfo)->err->msg_code = (code), \ + + (cinfo)->err->msg_parm.i[0] = (p1), \ + + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) + +#define WARNMS2(cinfo,code,p1,p2) \ + + ((cinfo)->err->msg_code = (code), \ + + (cinfo)->err->msg_parm.i[0] = (p1), \ + + (cinfo)->err->msg_parm.i[1] = (p2), \ + + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) + + + +/* Informational/debugging messages */ + +#define TRACEMS(cinfo,lvl,code) \ + + ((cinfo)->err->msg_code = (code), \ + + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) + +#define TRACEMS1(cinfo,lvl,code,p1) \ + + ((cinfo)->err->msg_code = (code), \ + + (cinfo)->err->msg_parm.i[0] = (p1), \ + + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) + +#define TRACEMS2(cinfo,lvl,code,p1,p2) \ + + ((cinfo)->err->msg_code = (code), \ + + (cinfo)->err->msg_parm.i[0] = (p1), \ + + (cinfo)->err->msg_parm.i[1] = (p2), \ + + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) + +#define TRACEMS3(cinfo,lvl,code,p1,p2,p3) \ + + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); \ + + (cinfo)->err->msg_code = (code); \ + + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) + +#define TRACEMS4(cinfo,lvl,code,p1,p2,p3,p4) \ + + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + + (cinfo)->err->msg_code = (code); \ + + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) + +#define TRACEMS8(cinfo,lvl,code,p1,p2,p3,p4,p5,p6,p7,p8) \ + + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + + _mp[4] = (p5); _mp[5] = (p6); _mp[6] = (p7); _mp[7] = (p8); \ + + (cinfo)->err->msg_code = (code); \ + + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) + +#define TRACEMSS(cinfo,lvl,code,str) \ + + ((cinfo)->err->msg_code = (code), \ + + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) + + + +#endif /* JERROR_H */ + diff --git a/utils/roq2/jpeg/jfdctflt.c b/utils/roq2/jpeg/jfdctflt.c new file mode 100644 index 0000000..ea79690 --- /dev/null +++ b/utils/roq2/jpeg/jfdctflt.c @@ -0,0 +1,336 @@ +/* + + * jfdctflt.c + + * + + * Copyright (C) 1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains a floating-point implementation of the + + * forward DCT (Discrete Cosine Transform). + + * + + * This implementation should be more accurate than either of the integer + + * DCT implementations. However, it may not give the same results on all + + * machines because of differences in roundoff behavior. Speed will depend + + * on the hardware's floating point capacity. + + * + + * A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT + + * on each column. Direct algorithms are also available, but they are + + * much more complex and seem not to be any faster when reduced to code. + + * + + * This implementation is based on Arai, Agui, and Nakajima's algorithm for + + * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in + + * Japanese, but the algorithm is described in the Pennebaker & Mitchell + + * JPEG textbook (see REFERENCES section in file README). The following code + + * is based directly on figure 4-8 in P&M. + + * While an 8-point DCT cannot be done in less than 11 multiplies, it is + + * possible to arrange the computation so that many of the multiplies are + + * simple scalings of the final outputs. These multiplies can then be + + * folded into the multiplications or divisions by the JPEG quantization + + * table entries. The AA&N method leaves only 5 multiplies and 29 adds + + * to be done in the DCT itself. + + * The primary disadvantage of this method is that with a fixed-point + + * implementation, accuracy is lost due to imprecise representation of the + + * scaled quantization values. However, that problem does not arise if + + * we use floating point arithmetic. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + +#include "jdct.h" /* Private declarations for DCT subsystem */ + + + +#ifdef DCT_FLOAT_SUPPORTED + + + + + +/* + + * This module is specialized to the case DCTSIZE = 8. + + */ + + + +#if DCTSIZE != 8 + + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ + +#endif + + + + + +/* + + * Perform the forward DCT on one block of samples. + + */ + + + +GLOBAL void + +jpeg_fdct_float (FAST_FLOAT * data) + +{ + + FAST_FLOAT tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + + FAST_FLOAT tmp10, tmp11, tmp12, tmp13; + + FAST_FLOAT z1, z2, z3, z4, z5, z11, z13; + + FAST_FLOAT *dataptr; + + int ctr; + + + + /* Pass 1: process rows. */ + + + + dataptr = data; + + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + + tmp0 = dataptr[0] + dataptr[7]; + + tmp7 = dataptr[0] - dataptr[7]; + + tmp1 = dataptr[1] + dataptr[6]; + + tmp6 = dataptr[1] - dataptr[6]; + + tmp2 = dataptr[2] + dataptr[5]; + + tmp5 = dataptr[2] - dataptr[5]; + + tmp3 = dataptr[3] + dataptr[4]; + + tmp4 = dataptr[3] - dataptr[4]; + + + + /* Even part */ + + + + tmp10 = tmp0 + tmp3; /* phase 2 */ + + tmp13 = tmp0 - tmp3; + + tmp11 = tmp1 + tmp2; + + tmp12 = tmp1 - tmp2; + + + + dataptr[0] = tmp10 + tmp11; /* phase 3 */ + + dataptr[4] = tmp10 - tmp11; + + + + z1 = (tmp12 + tmp13) * ((FAST_FLOAT) 0.707106781); /* c4 */ + + dataptr[2] = tmp13 + z1; /* phase 5 */ + + dataptr[6] = tmp13 - z1; + + + + /* Odd part */ + + + + tmp10 = tmp4 + tmp5; /* phase 2 */ + + tmp11 = tmp5 + tmp6; + + tmp12 = tmp6 + tmp7; + + + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + + z5 = (tmp10 - tmp12) * ((FAST_FLOAT) 0.382683433); /* c6 */ + + z2 = ((FAST_FLOAT) 0.541196100) * tmp10 + z5; /* c2-c6 */ + + z4 = ((FAST_FLOAT) 1.306562965) * tmp12 + z5; /* c2+c6 */ + + z3 = tmp11 * ((FAST_FLOAT) 0.707106781); /* c4 */ + + + + z11 = tmp7 + z3; /* phase 5 */ + + z13 = tmp7 - z3; + + + + dataptr[5] = z13 + z2; /* phase 6 */ + + dataptr[3] = z13 - z2; + + dataptr[1] = z11 + z4; + + dataptr[7] = z11 - z4; + + + + dataptr += DCTSIZE; /* advance pointer to next row */ + + } + + + + /* Pass 2: process columns. */ + + + + dataptr = data; + + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*7]; + + tmp7 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*7]; + + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*6]; + + tmp6 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*6]; + + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*5]; + + tmp5 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*5]; + + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*4]; + + tmp4 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*4]; + + + + /* Even part */ + + + + tmp10 = tmp0 + tmp3; /* phase 2 */ + + tmp13 = tmp0 - tmp3; + + tmp11 = tmp1 + tmp2; + + tmp12 = tmp1 - tmp2; + + + + dataptr[DCTSIZE*0] = tmp10 + tmp11; /* phase 3 */ + + dataptr[DCTSIZE*4] = tmp10 - tmp11; + + + + z1 = (tmp12 + tmp13) * ((FAST_FLOAT) 0.707106781); /* c4 */ + + dataptr[DCTSIZE*2] = tmp13 + z1; /* phase 5 */ + + dataptr[DCTSIZE*6] = tmp13 - z1; + + + + /* Odd part */ + + + + tmp10 = tmp4 + tmp5; /* phase 2 */ + + tmp11 = tmp5 + tmp6; + + tmp12 = tmp6 + tmp7; + + + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + + z5 = (tmp10 - tmp12) * ((FAST_FLOAT) 0.382683433); /* c6 */ + + z2 = ((FAST_FLOAT) 0.541196100) * tmp10 + z5; /* c2-c6 */ + + z4 = ((FAST_FLOAT) 1.306562965) * tmp12 + z5; /* c2+c6 */ + + z3 = tmp11 * ((FAST_FLOAT) 0.707106781); /* c4 */ + + + + z11 = tmp7 + z3; /* phase 5 */ + + z13 = tmp7 - z3; + + + + dataptr[DCTSIZE*5] = z13 + z2; /* phase 6 */ + + dataptr[DCTSIZE*3] = z13 - z2; + + dataptr[DCTSIZE*1] = z11 + z4; + + dataptr[DCTSIZE*7] = z11 - z4; + + + + dataptr++; /* advance pointer to next column */ + + } + +} + + + +#endif /* DCT_FLOAT_SUPPORTED */ + diff --git a/utils/roq2/jpeg/jfdctfst.c b/utils/roq2/jpeg/jfdctfst.c new file mode 100644 index 0000000..ab7b9d2 --- /dev/null +++ b/utils/roq2/jpeg/jfdctfst.c @@ -0,0 +1,448 @@ +/* + + * jfdctfst.c + + * + + * Copyright (C) 1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains a fast, not so accurate integer implementation of the + + * forward DCT (Discrete Cosine Transform). + + * + + * A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT + + * on each column. Direct algorithms are also available, but they are + + * much more complex and seem not to be any faster when reduced to code. + + * + + * This implementation is based on Arai, Agui, and Nakajima's algorithm for + + * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in + + * Japanese, but the algorithm is described in the Pennebaker & Mitchell + + * JPEG textbook (see REFERENCES section in file README). The following code + + * is based directly on figure 4-8 in P&M. + + * While an 8-point DCT cannot be done in less than 11 multiplies, it is + + * possible to arrange the computation so that many of the multiplies are + + * simple scalings of the final outputs. These multiplies can then be + + * folded into the multiplications or divisions by the JPEG quantization + + * table entries. The AA&N method leaves only 5 multiplies and 29 adds + + * to be done in the DCT itself. + + * The primary disadvantage of this method is that with fixed-point math, + + * accuracy is lost due to imprecise representation of the scaled + + * quantization values. The smaller the quantization table entry, the less + + * precise the scaled value, so this implementation does worse with high- + + * quality-setting files than with low-quality ones. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + +#include "jdct.h" /* Private declarations for DCT subsystem */ + + + +#ifdef DCT_IFAST_SUPPORTED + + + + + +/* + + * This module is specialized to the case DCTSIZE = 8. + + */ + + + +#if DCTSIZE != 8 + + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ + +#endif + + + + + +/* Scaling decisions are generally the same as in the LL&M algorithm; + + * see jfdctint.c for more details. However, we choose to descale + + * (right shift) multiplication products as soon as they are formed, + + * rather than carrying additional fractional bits into subsequent additions. + + * This compromises accuracy slightly, but it lets us save a few shifts. + + * More importantly, 16-bit arithmetic is then adequate (for 8-bit samples) + + * everywhere except in the multiplications proper; this saves a good deal + + * of work on 16-bit-int machines. + + * + + * Again to save a few shifts, the intermediate results between pass 1 and + + * pass 2 are not upscaled, but are represented only to integral precision. + + * + + * A final compromise is to represent the multiplicative constants to only + + * 8 fractional bits, rather than 13. This saves some shifting work on some + + * machines, and may also reduce the cost of multiplication (since there + + * are fewer one-bits in the constants). + + */ + + + +#define CONST_BITS 8 + + + + + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + + * causing a lot of useless floating-point operations at run time. + + * To get around this we use the following pre-calculated constants. + + * If you change CONST_BITS you may want to add appropriate values. + + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + + */ + + + +#if CONST_BITS == 8 + +#define FIX_0_382683433 ((INT32) 98) /* FIX(0.382683433) */ + +#define FIX_0_541196100 ((INT32) 139) /* FIX(0.541196100) */ + +#define FIX_0_707106781 ((INT32) 181) /* FIX(0.707106781) */ + +#define FIX_1_306562965 ((INT32) 334) /* FIX(1.306562965) */ + +#else + +#define FIX_0_382683433 FIX(0.382683433) + +#define FIX_0_541196100 FIX(0.541196100) + +#define FIX_0_707106781 FIX(0.707106781) + +#define FIX_1_306562965 FIX(1.306562965) + +#endif + + + + + +/* We can gain a little more speed, with a further compromise in accuracy, + + * by omitting the addition in a descaling shift. This yields an incorrectly + + * rounded result half the time... + + */ + + + +#ifndef USE_ACCURATE_ROUNDING + +#undef DESCALE + +#define DESCALE(x,n) RIGHT_SHIFT(x, n) + +#endif + + + + + +/* Multiply a DCTELEM variable by an INT32 constant, and immediately + + * descale to yield a DCTELEM result. + + */ + + + +#define MULTIPLY(var,const) ((DCTELEM) DESCALE((var) * (const), CONST_BITS)) + + + + + +/* + + * Perform the forward DCT on one block of samples. + + */ + + + +GLOBAL void + +jpeg_fdct_ifast (DCTELEM * data) + +{ + + DCTELEM tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + + DCTELEM tmp10, tmp11, tmp12, tmp13; + + DCTELEM z1, z2, z3, z4, z5, z11, z13; + + DCTELEM *dataptr; + + int ctr; + + SHIFT_TEMPS + + + + /* Pass 1: process rows. */ + + + + dataptr = data; + + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + + tmp0 = dataptr[0] + dataptr[7]; + + tmp7 = dataptr[0] - dataptr[7]; + + tmp1 = dataptr[1] + dataptr[6]; + + tmp6 = dataptr[1] - dataptr[6]; + + tmp2 = dataptr[2] + dataptr[5]; + + tmp5 = dataptr[2] - dataptr[5]; + + tmp3 = dataptr[3] + dataptr[4]; + + tmp4 = dataptr[3] - dataptr[4]; + + + + /* Even part */ + + + + tmp10 = tmp0 + tmp3; /* phase 2 */ + + tmp13 = tmp0 - tmp3; + + tmp11 = tmp1 + tmp2; + + tmp12 = tmp1 - tmp2; + + + + dataptr[0] = tmp10 + tmp11; /* phase 3 */ + + dataptr[4] = tmp10 - tmp11; + + + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_707106781); /* c4 */ + + dataptr[2] = tmp13 + z1; /* phase 5 */ + + dataptr[6] = tmp13 - z1; + + + + /* Odd part */ + + + + tmp10 = tmp4 + tmp5; /* phase 2 */ + + tmp11 = tmp5 + tmp6; + + tmp12 = tmp6 + tmp7; + + + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + + z5 = MULTIPLY(tmp10 - tmp12, FIX_0_382683433); /* c6 */ + + z2 = MULTIPLY(tmp10, FIX_0_541196100) + z5; /* c2-c6 */ + + z4 = MULTIPLY(tmp12, FIX_1_306562965) + z5; /* c2+c6 */ + + z3 = MULTIPLY(tmp11, FIX_0_707106781); /* c4 */ + + + + z11 = tmp7 + z3; /* phase 5 */ + + z13 = tmp7 - z3; + + + + dataptr[5] = z13 + z2; /* phase 6 */ + + dataptr[3] = z13 - z2; + + dataptr[1] = z11 + z4; + + dataptr[7] = z11 - z4; + + + + dataptr += DCTSIZE; /* advance pointer to next row */ + + } + + + + /* Pass 2: process columns. */ + + + + dataptr = data; + + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*7]; + + tmp7 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*7]; + + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*6]; + + tmp6 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*6]; + + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*5]; + + tmp5 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*5]; + + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*4]; + + tmp4 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*4]; + + + + /* Even part */ + + + + tmp10 = tmp0 + tmp3; /* phase 2 */ + + tmp13 = tmp0 - tmp3; + + tmp11 = tmp1 + tmp2; + + tmp12 = tmp1 - tmp2; + + + + dataptr[DCTSIZE*0] = tmp10 + tmp11; /* phase 3 */ + + dataptr[DCTSIZE*4] = tmp10 - tmp11; + + + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_707106781); /* c4 */ + + dataptr[DCTSIZE*2] = tmp13 + z1; /* phase 5 */ + + dataptr[DCTSIZE*6] = tmp13 - z1; + + + + /* Odd part */ + + + + tmp10 = tmp4 + tmp5; /* phase 2 */ + + tmp11 = tmp5 + tmp6; + + tmp12 = tmp6 + tmp7; + + + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + + z5 = MULTIPLY(tmp10 - tmp12, FIX_0_382683433); /* c6 */ + + z2 = MULTIPLY(tmp10, FIX_0_541196100) + z5; /* c2-c6 */ + + z4 = MULTIPLY(tmp12, FIX_1_306562965) + z5; /* c2+c6 */ + + z3 = MULTIPLY(tmp11, FIX_0_707106781); /* c4 */ + + + + z11 = tmp7 + z3; /* phase 5 */ + + z13 = tmp7 - z3; + + + + dataptr[DCTSIZE*5] = z13 + z2; /* phase 6 */ + + dataptr[DCTSIZE*3] = z13 - z2; + + dataptr[DCTSIZE*1] = z11 + z4; + + dataptr[DCTSIZE*7] = z11 - z4; + + + + dataptr++; /* advance pointer to next column */ + + } + +} + + + +#endif /* DCT_IFAST_SUPPORTED */ + diff --git a/utils/roq2/jpeg/jfdctint.c b/utils/roq2/jpeg/jfdctint.c new file mode 100644 index 0000000..63fceba --- /dev/null +++ b/utils/roq2/jpeg/jfdctint.c @@ -0,0 +1,566 @@ +/* + + * jfdctint.c + + * + + * Copyright (C) 1991-1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains a slow-but-accurate integer implementation of the + + * forward DCT (Discrete Cosine Transform). + + * + + * A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT + + * on each column. Direct algorithms are also available, but they are + + * much more complex and seem not to be any faster when reduced to code. + + * + + * This implementation is based on an algorithm described in + + * C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT + + * Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics, + + * Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991. + + * The primary algorithm described there uses 11 multiplies and 29 adds. + + * We use their alternate method with 12 multiplies and 32 adds. + + * The advantage of this method is that no data path contains more than one + + * multiplication; this allows a very simple and accurate implementation in + + * scaled fixed-point arithmetic, with a minimal number of shifts. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + +#include "jdct.h" /* Private declarations for DCT subsystem */ + + + +#ifdef DCT_ISLOW_SUPPORTED + + + + + +/* + + * This module is specialized to the case DCTSIZE = 8. + + */ + + + +#if DCTSIZE != 8 + + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ + +#endif + + + + + +/* + + * The poop on this scaling stuff is as follows: + + * + + * Each 1-D DCT step produces outputs which are a factor of sqrt(N) + + * larger than the true DCT outputs. The final outputs are therefore + + * a factor of N larger than desired; since N=8 this can be cured by + + * a simple right shift at the end of the algorithm. The advantage of + + * this arrangement is that we save two multiplications per 1-D DCT, + + * because the y0 and y4 outputs need not be divided by sqrt(N). + + * In the IJG code, this factor of 8 is removed by the quantization step + + * (in jcdctmgr.c), NOT in this module. + + * + + * We have to do addition and subtraction of the integer inputs, which + + * is no problem, and multiplication by fractional constants, which is + + * a problem to do in integer arithmetic. We multiply all the constants + + * by CONST_SCALE and convert them to integer constants (thus retaining + + * CONST_BITS bits of precision in the constants). After doing a + + * multiplication we have to divide the product by CONST_SCALE, with proper + + * rounding, to produce the correct output. This division can be done + + * cheaply as a right shift of CONST_BITS bits. We postpone shifting + + * as long as possible so that partial sums can be added together with + + * full fractional precision. + + * + + * The outputs of the first pass are scaled up by PASS1_BITS bits so that + + * they are represented to better-than-integral precision. These outputs + + * require BITS_IN_JSAMPLE + PASS1_BITS + 3 bits; this fits in a 16-bit word + + * with the recommended scaling. (For 12-bit sample data, the intermediate + + * array is INT32 anyway.) + + * + + * To avoid overflow of the 32-bit intermediate results in pass 2, we must + + * have BITS_IN_JSAMPLE + CONST_BITS + PASS1_BITS <= 26. Error analysis + + * shows that the values given below are the most effective. + + */ + + + +#if BITS_IN_JSAMPLE == 8 + +#define CONST_BITS 13 + +#define PASS1_BITS 2 + +#else + +#define CONST_BITS 13 + +#define PASS1_BITS 1 /* lose a little precision to avoid overflow */ + +#endif + + + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + + * causing a lot of useless floating-point operations at run time. + + * To get around this we use the following pre-calculated constants. + + * If you change CONST_BITS you may want to add appropriate values. + + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + + */ + + + +#if CONST_BITS == 13 + +#define FIX_0_298631336 ((INT32) 2446) /* FIX(0.298631336) */ + +#define FIX_0_390180644 ((INT32) 3196) /* FIX(0.390180644) */ + +#define FIX_0_541196100 ((INT32) 4433) /* FIX(0.541196100) */ + +#define FIX_0_765366865 ((INT32) 6270) /* FIX(0.765366865) */ + +#define FIX_0_899976223 ((INT32) 7373) /* FIX(0.899976223) */ + +#define FIX_1_175875602 ((INT32) 9633) /* FIX(1.175875602) */ + +#define FIX_1_501321110 ((INT32) 12299) /* FIX(1.501321110) */ + +#define FIX_1_847759065 ((INT32) 15137) /* FIX(1.847759065) */ + +#define FIX_1_961570560 ((INT32) 16069) /* FIX(1.961570560) */ + +#define FIX_2_053119869 ((INT32) 16819) /* FIX(2.053119869) */ + +#define FIX_2_562915447 ((INT32) 20995) /* FIX(2.562915447) */ + +#define FIX_3_072711026 ((INT32) 25172) /* FIX(3.072711026) */ + +#else + +#define FIX_0_298631336 FIX(0.298631336) + +#define FIX_0_390180644 FIX(0.390180644) + +#define FIX_0_541196100 FIX(0.541196100) + +#define FIX_0_765366865 FIX(0.765366865) + +#define FIX_0_899976223 FIX(0.899976223) + +#define FIX_1_175875602 FIX(1.175875602) + +#define FIX_1_501321110 FIX(1.501321110) + +#define FIX_1_847759065 FIX(1.847759065) + +#define FIX_1_961570560 FIX(1.961570560) + +#define FIX_2_053119869 FIX(2.053119869) + +#define FIX_2_562915447 FIX(2.562915447) + +#define FIX_3_072711026 FIX(3.072711026) + +#endif + + + + + +/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. + + * For 8-bit samples with the recommended scaling, all the variable + + * and constant values involved are no more than 16 bits wide, so a + + * 16x16->32 bit multiply can be used instead of a full 32x32 multiply. + + * For 12-bit samples, a full 32-bit multiplication will be needed. + + */ + + + +#if BITS_IN_JSAMPLE == 8 + +#define MULTIPLY(var,const) MULTIPLY16C16(var,const) + +#else + +#define MULTIPLY(var,const) ((var) * (const)) + +#endif + + + + + +/* + + * Perform the forward DCT on one block of samples. + + */ + + + +GLOBAL void + +jpeg_fdct_islow (DCTELEM * data) + +{ + + INT32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + + INT32 tmp10, tmp11, tmp12, tmp13; + + INT32 z1, z2, z3, z4, z5; + + DCTELEM *dataptr; + + int ctr; + + SHIFT_TEMPS + + + + /* Pass 1: process rows. */ + + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + + /* furthermore, we scale the results by 2**PASS1_BITS. */ + + + + dataptr = data; + + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + + tmp0 = dataptr[0] + dataptr[7]; + + tmp7 = dataptr[0] - dataptr[7]; + + tmp1 = dataptr[1] + dataptr[6]; + + tmp6 = dataptr[1] - dataptr[6]; + + tmp2 = dataptr[2] + dataptr[5]; + + tmp5 = dataptr[2] - dataptr[5]; + + tmp3 = dataptr[3] + dataptr[4]; + + tmp4 = dataptr[3] - dataptr[4]; + + + + /* Even part per LL&M figure 1 --- note that published figure is faulty; + + * rotator "sqrt(2)*c1" should be "sqrt(2)*c6". + + */ + + + + tmp10 = tmp0 + tmp3; + + tmp13 = tmp0 - tmp3; + + tmp11 = tmp1 + tmp2; + + tmp12 = tmp1 - tmp2; + + + + dataptr[0] = (DCTELEM) ((tmp10 + tmp11) << PASS1_BITS); + + dataptr[4] = (DCTELEM) ((tmp10 - tmp11) << PASS1_BITS); + + + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_541196100); + + dataptr[2] = (DCTELEM) DESCALE(z1 + MULTIPLY(tmp13, FIX_0_765366865), + + CONST_BITS-PASS1_BITS); + + dataptr[6] = (DCTELEM) DESCALE(z1 + MULTIPLY(tmp12, - FIX_1_847759065), + + CONST_BITS-PASS1_BITS); + + + + /* Odd part per figure 8 --- note paper omits factor of sqrt(2). + + * cK represents cos(K*pi/16). + + * i0..i3 in the paper are tmp4..tmp7 here. + + */ + + + + z1 = tmp4 + tmp7; + + z2 = tmp5 + tmp6; + + z3 = tmp4 + tmp6; + + z4 = tmp5 + tmp7; + + z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ + + + + tmp4 = MULTIPLY(tmp4, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + + tmp5 = MULTIPLY(tmp5, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + + tmp6 = MULTIPLY(tmp6, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + + tmp7 = MULTIPLY(tmp7, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + + z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + + z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + + z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + + z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + + + + z3 += z5; + + z4 += z5; + + + + dataptr[7] = (DCTELEM) DESCALE(tmp4 + z1 + z3, CONST_BITS-PASS1_BITS); + + dataptr[5] = (DCTELEM) DESCALE(tmp5 + z2 + z4, CONST_BITS-PASS1_BITS); + + dataptr[3] = (DCTELEM) DESCALE(tmp6 + z2 + z3, CONST_BITS-PASS1_BITS); + + dataptr[1] = (DCTELEM) DESCALE(tmp7 + z1 + z4, CONST_BITS-PASS1_BITS); + + + + dataptr += DCTSIZE; /* advance pointer to next row */ + + } + + + + /* Pass 2: process columns. + + * We remove the PASS1_BITS scaling, but leave the results scaled up + + * by an overall factor of 8. + + */ + + + + dataptr = data; + + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*7]; + + tmp7 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*7]; + + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*6]; + + tmp6 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*6]; + + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*5]; + + tmp5 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*5]; + + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*4]; + + tmp4 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*4]; + + + + /* Even part per LL&M figure 1 --- note that published figure is faulty; + + * rotator "sqrt(2)*c1" should be "sqrt(2)*c6". + + */ + + + + tmp10 = tmp0 + tmp3; + + tmp13 = tmp0 - tmp3; + + tmp11 = tmp1 + tmp2; + + tmp12 = tmp1 - tmp2; + + + + dataptr[DCTSIZE*0] = (DCTELEM) DESCALE(tmp10 + tmp11, PASS1_BITS); + + dataptr[DCTSIZE*4] = (DCTELEM) DESCALE(tmp10 - tmp11, PASS1_BITS); + + + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_541196100); + + dataptr[DCTSIZE*2] = (DCTELEM) DESCALE(z1 + MULTIPLY(tmp13, FIX_0_765366865), + + CONST_BITS+PASS1_BITS); + + dataptr[DCTSIZE*6] = (DCTELEM) DESCALE(z1 + MULTIPLY(tmp12, - FIX_1_847759065), + + CONST_BITS+PASS1_BITS); + + + + /* Odd part per figure 8 --- note paper omits factor of sqrt(2). + + * cK represents cos(K*pi/16). + + * i0..i3 in the paper are tmp4..tmp7 here. + + */ + + + + z1 = tmp4 + tmp7; + + z2 = tmp5 + tmp6; + + z3 = tmp4 + tmp6; + + z4 = tmp5 + tmp7; + + z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ + + + + tmp4 = MULTIPLY(tmp4, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + + tmp5 = MULTIPLY(tmp5, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + + tmp6 = MULTIPLY(tmp6, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + + tmp7 = MULTIPLY(tmp7, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + + z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + + z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + + z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + + z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + + + + z3 += z5; + + z4 += z5; + + + + dataptr[DCTSIZE*7] = (DCTELEM) DESCALE(tmp4 + z1 + z3, + + CONST_BITS+PASS1_BITS); + + dataptr[DCTSIZE*5] = (DCTELEM) DESCALE(tmp5 + z2 + z4, + + CONST_BITS+PASS1_BITS); + + dataptr[DCTSIZE*3] = (DCTELEM) DESCALE(tmp6 + z2 + z3, + + CONST_BITS+PASS1_BITS); + + dataptr[DCTSIZE*1] = (DCTELEM) DESCALE(tmp7 + z1 + z4, + + CONST_BITS+PASS1_BITS); + + + + dataptr++; /* advance pointer to next column */ + + } + +} + + + +#endif /* DCT_ISLOW_SUPPORTED */ + diff --git a/utils/roq2/jpeg/jidctflt.c b/utils/roq2/jpeg/jidctflt.c new file mode 100644 index 0000000..bf66e28 --- /dev/null +++ b/utils/roq2/jpeg/jidctflt.c @@ -0,0 +1,482 @@ +/* + + * jidctflt.c + + * + + * Copyright (C) 1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains a floating-point implementation of the + + * inverse DCT (Discrete Cosine Transform). In the IJG code, this routine + + * must also perform dequantization of the input coefficients. + + * + + * This implementation should be more accurate than either of the integer + + * IDCT implementations. However, it may not give the same results on all + + * machines because of differences in roundoff behavior. Speed will depend + + * on the hardware's floating point capacity. + + * + + * A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT + + * on each row (or vice versa, but it's more convenient to emit a row at + + * a time). Direct algorithms are also available, but they are much more + + * complex and seem not to be any faster when reduced to code. + + * + + * This implementation is based on Arai, Agui, and Nakajima's algorithm for + + * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in + + * Japanese, but the algorithm is described in the Pennebaker & Mitchell + + * JPEG textbook (see REFERENCES section in file README). The following code + + * is based directly on figure 4-8 in P&M. + + * While an 8-point DCT cannot be done in less than 11 multiplies, it is + + * possible to arrange the computation so that many of the multiplies are + + * simple scalings of the final outputs. These multiplies can then be + + * folded into the multiplications or divisions by the JPEG quantization + + * table entries. The AA&N method leaves only 5 multiplies and 29 adds + + * to be done in the DCT itself. + + * The primary disadvantage of this method is that with a fixed-point + + * implementation, accuracy is lost due to imprecise representation of the + + * scaled quantization values. However, that problem does not arise if + + * we use floating point arithmetic. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + +#include "jdct.h" /* Private declarations for DCT subsystem */ + + + +#ifdef DCT_FLOAT_SUPPORTED + + + + + +/* + + * This module is specialized to the case DCTSIZE = 8. + + */ + + + +#if DCTSIZE != 8 + + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ + +#endif + + + + + +/* Dequantize a coefficient by multiplying it by the multiplier-table + + * entry; produce a float result. + + */ + + + +#define DEQUANTIZE(coef,quantval) (((FAST_FLOAT) (coef)) * (quantval)) + + + + + +/* + + * Perform dequantization and inverse DCT on one block of coefficients. + + */ + + + +GLOBAL void + +jpeg_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JCOEFPTR coef_block, + + JSAMPARRAY output_buf, JDIMENSION output_col) + +{ + + FAST_FLOAT tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + + FAST_FLOAT tmp10, tmp11, tmp12, tmp13; + + FAST_FLOAT z5, z10, z11, z12, z13; + + JCOEFPTR inptr; + + FLOAT_MULT_TYPE * quantptr; + + FAST_FLOAT * wsptr; + + JSAMPROW outptr; + + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + + int ctr; + + FAST_FLOAT workspace[DCTSIZE2]; /* buffers data between passes */ + + SHIFT_TEMPS + + + + /* Pass 1: process columns from input, store into work array. */ + + + + inptr = coef_block; + + quantptr = (FLOAT_MULT_TYPE *) compptr->dct_table; + + wsptr = workspace; + + for (ctr = DCTSIZE; ctr > 0; ctr--) { + + /* Due to quantization, we will usually find that many of the input + + * coefficients are zero, especially the AC terms. We can exploit this + + * by short-circuiting the IDCT calculation for any column in which all + + * the AC terms are zero. In that case each output is equal to the + + * DC coefficient (with scale factor as needed). + + * With typical images and quantization tables, half or more of the + + * column DCT calculations can be simplified this way. + + */ + + + + if ((inptr[DCTSIZE*1] | inptr[DCTSIZE*2] | inptr[DCTSIZE*3] | + + inptr[DCTSIZE*4] | inptr[DCTSIZE*5] | inptr[DCTSIZE*6] | + + inptr[DCTSIZE*7]) == 0) { + + /* AC terms all zero */ + + FAST_FLOAT dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + + + + wsptr[DCTSIZE*0] = dcval; + + wsptr[DCTSIZE*1] = dcval; + + wsptr[DCTSIZE*2] = dcval; + + wsptr[DCTSIZE*3] = dcval; + + wsptr[DCTSIZE*4] = dcval; + + wsptr[DCTSIZE*5] = dcval; + + wsptr[DCTSIZE*6] = dcval; + + wsptr[DCTSIZE*7] = dcval; + + + + inptr++; /* advance pointers to next column */ + + quantptr++; + + wsptr++; + + continue; + + } + + + + /* Even part */ + + + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + + tmp1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + + tmp2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + + tmp3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + + + tmp10 = tmp0 + tmp2; /* phase 3 */ + + tmp11 = tmp0 - tmp2; + + + + tmp13 = tmp1 + tmp3; /* phases 5-3 */ + + tmp12 = (tmp1 - tmp3) * ((FAST_FLOAT) 1.414213562) - tmp13; /* 2*c4 */ + + + + tmp0 = tmp10 + tmp13; /* phase 2 */ + + tmp3 = tmp10 - tmp13; + + tmp1 = tmp11 + tmp12; + + tmp2 = tmp11 - tmp12; + + + + /* Odd part */ + + + + tmp4 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + + tmp5 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + + tmp6 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + + tmp7 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + + + z13 = tmp6 + tmp5; /* phase 6 */ + + z10 = tmp6 - tmp5; + + z11 = tmp4 + tmp7; + + z12 = tmp4 - tmp7; + + + + tmp7 = z11 + z13; /* phase 5 */ + + tmp11 = (z11 - z13) * ((FAST_FLOAT) 1.414213562); /* 2*c4 */ + + + + z5 = (z10 + z12) * ((FAST_FLOAT) 1.847759065); /* 2*c2 */ + + tmp10 = ((FAST_FLOAT) 1.082392200) * z12 - z5; /* 2*(c2-c6) */ + + tmp12 = ((FAST_FLOAT) -2.613125930) * z10 + z5; /* -2*(c2+c6) */ + + + + tmp6 = tmp12 - tmp7; /* phase 2 */ + + tmp5 = tmp11 - tmp6; + + tmp4 = tmp10 + tmp5; + + + + wsptr[DCTSIZE*0] = tmp0 + tmp7; + + wsptr[DCTSIZE*7] = tmp0 - tmp7; + + wsptr[DCTSIZE*1] = tmp1 + tmp6; + + wsptr[DCTSIZE*6] = tmp1 - tmp6; + + wsptr[DCTSIZE*2] = tmp2 + tmp5; + + wsptr[DCTSIZE*5] = tmp2 - tmp5; + + wsptr[DCTSIZE*4] = tmp3 + tmp4; + + wsptr[DCTSIZE*3] = tmp3 - tmp4; + + + + inptr++; /* advance pointers to next column */ + + quantptr++; + + wsptr++; + + } + + + + /* Pass 2: process rows from work array, store into output array. */ + + /* Note that we must descale the results by a factor of 8 == 2**3. */ + + + + wsptr = workspace; + + for (ctr = 0; ctr < DCTSIZE; ctr++) { + + outptr = output_buf[ctr] + output_col; + + /* Rows of zeroes can be exploited in the same way as we did with columns. + + * However, the column calculation has created many nonzero AC terms, so + + * the simplification applies less often (typically 5% to 10% of the time). + + * And testing floats for zero is relatively expensive, so we don't bother. + + */ + + + + /* Even part */ + + + + tmp10 = wsptr[0] + wsptr[4]; + + tmp11 = wsptr[0] - wsptr[4]; + + + + tmp13 = wsptr[2] + wsptr[6]; + + tmp12 = (wsptr[2] - wsptr[6]) * ((FAST_FLOAT) 1.414213562) - tmp13; + + + + tmp0 = tmp10 + tmp13; + + tmp3 = tmp10 - tmp13; + + tmp1 = tmp11 + tmp12; + + tmp2 = tmp11 - tmp12; + + + + /* Odd part */ + + + + z13 = wsptr[5] + wsptr[3]; + + z10 = wsptr[5] - wsptr[3]; + + z11 = wsptr[1] + wsptr[7]; + + z12 = wsptr[1] - wsptr[7]; + + + + tmp7 = z11 + z13; + + tmp11 = (z11 - z13) * ((FAST_FLOAT) 1.414213562); + + + + z5 = (z10 + z12) * ((FAST_FLOAT) 1.847759065); /* 2*c2 */ + + tmp10 = ((FAST_FLOAT) 1.082392200) * z12 - z5; /* 2*(c2-c6) */ + + tmp12 = ((FAST_FLOAT) -2.613125930) * z10 + z5; /* -2*(c2+c6) */ + + + + tmp6 = tmp12 - tmp7; + + tmp5 = tmp11 - tmp6; + + tmp4 = tmp10 + tmp5; + + + + /* Final output stage: scale down by a factor of 8 and range-limit */ + + + + outptr[0] = range_limit[(int) DESCALE((INT32) (tmp0 + tmp7), 3) + + & RANGE_MASK]; + + outptr[7] = range_limit[(int) DESCALE((INT32) (tmp0 - tmp7), 3) + + & RANGE_MASK]; + + outptr[1] = range_limit[(int) DESCALE((INT32) (tmp1 + tmp6), 3) + + & RANGE_MASK]; + + outptr[6] = range_limit[(int) DESCALE((INT32) (tmp1 - tmp6), 3) + + & RANGE_MASK]; + + outptr[2] = range_limit[(int) DESCALE((INT32) (tmp2 + tmp5), 3) + + & RANGE_MASK]; + + outptr[5] = range_limit[(int) DESCALE((INT32) (tmp2 - tmp5), 3) + + & RANGE_MASK]; + + outptr[4] = range_limit[(int) DESCALE((INT32) (tmp3 + tmp4), 3) + + & RANGE_MASK]; + + outptr[3] = range_limit[(int) DESCALE((INT32) (tmp3 - tmp4), 3) + + & RANGE_MASK]; + + + + wsptr += DCTSIZE; /* advance pointer to next row */ + + } + +} + + + +#endif /* DCT_FLOAT_SUPPORTED */ + diff --git a/utils/roq2/jpeg/jidctfst.c b/utils/roq2/jpeg/jidctfst.c new file mode 100644 index 0000000..8b0846a --- /dev/null +++ b/utils/roq2/jpeg/jidctfst.c @@ -0,0 +1,734 @@ +/* + + * jidctfst.c + + * + + * Copyright (C) 1994-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains a fast, not so accurate integer implementation of the + + * inverse DCT (Discrete Cosine Transform). In the IJG code, this routine + + * must also perform dequantization of the input coefficients. + + * + + * A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT + + * on each row (or vice versa, but it's more convenient to emit a row at + + * a time). Direct algorithms are also available, but they are much more + + * complex and seem not to be any faster when reduced to code. + + * + + * This implementation is based on Arai, Agui, and Nakajima's algorithm for + + * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in + + * Japanese, but the algorithm is described in the Pennebaker & Mitchell + + * JPEG textbook (see REFERENCES section in file README). The following code + + * is based directly on figure 4-8 in P&M. + + * While an 8-point DCT cannot be done in less than 11 multiplies, it is + + * possible to arrange the computation so that many of the multiplies are + + * simple scalings of the final outputs. These multiplies can then be + + * folded into the multiplications or divisions by the JPEG quantization + + * table entries. The AA&N method leaves only 5 multiplies and 29 adds + + * to be done in the DCT itself. + + * The primary disadvantage of this method is that with fixed-point math, + + * accuracy is lost due to imprecise representation of the scaled + + * quantization values. The smaller the quantization table entry, the less + + * precise the scaled value, so this implementation does worse with high- + + * quality-setting files than with low-quality ones. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + +#include "jdct.h" /* Private declarations for DCT subsystem */ + + + +#ifdef DCT_IFAST_SUPPORTED + + + + + +/* + + * This module is specialized to the case DCTSIZE = 8. + + */ + + + +#if DCTSIZE != 8 + + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ + +#endif + + + + + +/* Scaling decisions are generally the same as in the LL&M algorithm; + + * see jidctint.c for more details. However, we choose to descale + + * (right shift) multiplication products as soon as they are formed, + + * rather than carrying additional fractional bits into subsequent additions. + + * This compromises accuracy slightly, but it lets us save a few shifts. + + * More importantly, 16-bit arithmetic is then adequate (for 8-bit samples) + + * everywhere except in the multiplications proper; this saves a good deal + + * of work on 16-bit-int machines. + + * + + * The dequantized coefficients are not integers because the AA&N scaling + + * factors have been incorporated. We represent them scaled up by PASS1_BITS, + + * so that the first and second IDCT rounds have the same input scaling. + + * For 8-bit JSAMPLEs, we choose IFAST_SCALE_BITS = PASS1_BITS so as to + + * avoid a descaling shift; this compromises accuracy rather drastically + + * for small quantization table entries, but it saves a lot of shifts. + + * For 12-bit JSAMPLEs, there's no hope of using 16x16 multiplies anyway, + + * so we use a much larger scaling factor to preserve accuracy. + + * + + * A final compromise is to represent the multiplicative constants to only + + * 8 fractional bits, rather than 13. This saves some shifting work on some + + * machines, and may also reduce the cost of multiplication (since there + + * are fewer one-bits in the constants). + + */ + + + +#if BITS_IN_JSAMPLE == 8 + +#define CONST_BITS 8 + +#define PASS1_BITS 2 + +#else + +#define CONST_BITS 8 + +#define PASS1_BITS 1 /* lose a little precision to avoid overflow */ + +#endif + + + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + + * causing a lot of useless floating-point operations at run time. + + * To get around this we use the following pre-calculated constants. + + * If you change CONST_BITS you may want to add appropriate values. + + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + + */ + + + +#if CONST_BITS == 8 + +#define FIX_1_082392200 ((INT32) 277) /* FIX(1.082392200) */ + +#define FIX_1_414213562 ((INT32) 362) /* FIX(1.414213562) */ + +#define FIX_1_847759065 ((INT32) 473) /* FIX(1.847759065) */ + +#define FIX_2_613125930 ((INT32) 669) /* FIX(2.613125930) */ + +#else + +#define FIX_1_082392200 FIX(1.082392200) + +#define FIX_1_414213562 FIX(1.414213562) + +#define FIX_1_847759065 FIX(1.847759065) + +#define FIX_2_613125930 FIX(2.613125930) + +#endif + + + + + +/* We can gain a little more speed, with a further compromise in accuracy, + + * by omitting the addition in a descaling shift. This yields an incorrectly + + * rounded result half the time... + + */ + + + +#ifndef USE_ACCURATE_ROUNDING + +#undef DESCALE + +#define DESCALE(x,n) RIGHT_SHIFT(x, n) + +#endif + + + + + +/* Multiply a DCTELEM variable by an INT32 constant, and immediately + + * descale to yield a DCTELEM result. + + */ + + + +#define MULTIPLY(var,const) ((DCTELEM) DESCALE((var) * (const), CONST_BITS)) + + + + + +/* Dequantize a coefficient by multiplying it by the multiplier-table + + * entry; produce a DCTELEM result. For 8-bit data a 16x16->16 + + * multiplication will do. For 12-bit data, the multiplier table is + + * declared INT32, so a 32-bit multiply will be used. + + */ + + + +#if BITS_IN_JSAMPLE == 8 + +#define DEQUANTIZE(coef,quantval) (((IFAST_MULT_TYPE) (coef)) * (quantval)) + +#else + +#define DEQUANTIZE(coef,quantval) \ + + DESCALE((coef)*(quantval), IFAST_SCALE_BITS-PASS1_BITS) + +#endif + + + + + +/* Like DESCALE, but applies to a DCTELEM and produces an int. + + * We assume that int right shift is unsigned if INT32 right shift is. + + */ + + + +#ifdef RIGHT_SHIFT_IS_UNSIGNED + +#define ISHIFT_TEMPS DCTELEM ishift_temp; + +#if BITS_IN_JSAMPLE == 8 + +#define DCTELEMBITS 16 /* DCTELEM may be 16 or 32 bits */ + +#else + +#define DCTELEMBITS 32 /* DCTELEM must be 32 bits */ + +#endif + +#define IRIGHT_SHIFT(x,shft) \ + + ((ishift_temp = (x)) < 0 ? \ + + (ishift_temp >> (shft)) | ((~((DCTELEM) 0)) << (DCTELEMBITS-(shft))) : \ + + (ishift_temp >> (shft))) + +#else + +#define ISHIFT_TEMPS + +#define IRIGHT_SHIFT(x,shft) ((x) >> (shft)) + +#endif + + + +#ifdef USE_ACCURATE_ROUNDING + +#define IDESCALE(x,n) ((int) IRIGHT_SHIFT((x) + (1 << ((n)-1)), n)) + +#else + +#define IDESCALE(x,n) ((int) IRIGHT_SHIFT(x, n)) + +#endif + + + + + +/* + + * Perform dequantization and inverse DCT on one block of coefficients. + + */ + + + +GLOBAL void + +jpeg_idct_ifast (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JCOEFPTR coef_block, + + JSAMPARRAY output_buf, JDIMENSION output_col) + +{ + + DCTELEM tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + + DCTELEM tmp10, tmp11, tmp12, tmp13; + + DCTELEM z5, z10, z11, z12, z13; + + JCOEFPTR inptr; + + IFAST_MULT_TYPE * quantptr; + + int * wsptr; + + JSAMPROW outptr; + + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + + int ctr; + + int workspace[DCTSIZE2]; /* buffers data between passes */ + + SHIFT_TEMPS /* for DESCALE */ + + ISHIFT_TEMPS /* for IDESCALE */ + + + + /* Pass 1: process columns from input, store into work array. */ + + + + inptr = coef_block; + + quantptr = (IFAST_MULT_TYPE *) compptr->dct_table; + + wsptr = workspace; + + for (ctr = DCTSIZE; ctr > 0; ctr--) { + + /* Due to quantization, we will usually find that many of the input + + * coefficients are zero, especially the AC terms. We can exploit this + + * by short-circuiting the IDCT calculation for any column in which all + + * the AC terms are zero. In that case each output is equal to the + + * DC coefficient (with scale factor as needed). + + * With typical images and quantization tables, half or more of the + + * column DCT calculations can be simplified this way. + + */ + + + + if ((inptr[DCTSIZE*1] | inptr[DCTSIZE*2] | inptr[DCTSIZE*3] | + + inptr[DCTSIZE*4] | inptr[DCTSIZE*5] | inptr[DCTSIZE*6] | + + inptr[DCTSIZE*7]) == 0) { + + /* AC terms all zero */ + + int dcval = (int) DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + + + + wsptr[DCTSIZE*0] = dcval; + + wsptr[DCTSIZE*1] = dcval; + + wsptr[DCTSIZE*2] = dcval; + + wsptr[DCTSIZE*3] = dcval; + + wsptr[DCTSIZE*4] = dcval; + + wsptr[DCTSIZE*5] = dcval; + + wsptr[DCTSIZE*6] = dcval; + + wsptr[DCTSIZE*7] = dcval; + + + + inptr++; /* advance pointers to next column */ + + quantptr++; + + wsptr++; + + continue; + + } + + + + /* Even part */ + + + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + + tmp1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + + tmp2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + + tmp3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + + + tmp10 = tmp0 + tmp2; /* phase 3 */ + + tmp11 = tmp0 - tmp2; + + + + tmp13 = tmp1 + tmp3; /* phases 5-3 */ + + tmp12 = MULTIPLY(tmp1 - tmp3, FIX_1_414213562) - tmp13; /* 2*c4 */ + + + + tmp0 = tmp10 + tmp13; /* phase 2 */ + + tmp3 = tmp10 - tmp13; + + tmp1 = tmp11 + tmp12; + + tmp2 = tmp11 - tmp12; + + + + /* Odd part */ + + + + tmp4 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + + tmp5 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + + tmp6 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + + tmp7 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + + + z13 = tmp6 + tmp5; /* phase 6 */ + + z10 = tmp6 - tmp5; + + z11 = tmp4 + tmp7; + + z12 = tmp4 - tmp7; + + + + tmp7 = z11 + z13; /* phase 5 */ + + tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); /* 2*c4 */ + + + + z5 = MULTIPLY(z10 + z12, FIX_1_847759065); /* 2*c2 */ + + tmp10 = MULTIPLY(z12, FIX_1_082392200) - z5; /* 2*(c2-c6) */ + + tmp12 = MULTIPLY(z10, - FIX_2_613125930) + z5; /* -2*(c2+c6) */ + + + + tmp6 = tmp12 - tmp7; /* phase 2 */ + + tmp5 = tmp11 - tmp6; + + tmp4 = tmp10 + tmp5; + + + + wsptr[DCTSIZE*0] = (int) (tmp0 + tmp7); + + wsptr[DCTSIZE*7] = (int) (tmp0 - tmp7); + + wsptr[DCTSIZE*1] = (int) (tmp1 + tmp6); + + wsptr[DCTSIZE*6] = (int) (tmp1 - tmp6); + + wsptr[DCTSIZE*2] = (int) (tmp2 + tmp5); + + wsptr[DCTSIZE*5] = (int) (tmp2 - tmp5); + + wsptr[DCTSIZE*4] = (int) (tmp3 + tmp4); + + wsptr[DCTSIZE*3] = (int) (tmp3 - tmp4); + + + + inptr++; /* advance pointers to next column */ + + quantptr++; + + wsptr++; + + } + + + + /* Pass 2: process rows from work array, store into output array. */ + + /* Note that we must descale the results by a factor of 8 == 2**3, */ + + /* and also undo the PASS1_BITS scaling. */ + + + + wsptr = workspace; + + for (ctr = 0; ctr < DCTSIZE; ctr++) { + + outptr = output_buf[ctr] + output_col; + + /* Rows of zeroes can be exploited in the same way as we did with columns. + + * However, the column calculation has created many nonzero AC terms, so + + * the simplification applies less often (typically 5% to 10% of the time). + + * On machines with very fast multiplication, it's possible that the + + * test takes more time than it's worth. In that case this section + + * may be commented out. + + */ + + + +#ifndef NO_ZERO_ROW_TEST + + if ((wsptr[1] | wsptr[2] | wsptr[3] | wsptr[4] | wsptr[5] | wsptr[6] | + + wsptr[7]) == 0) { + + /* AC terms all zero */ + + JSAMPLE dcval = range_limit[IDESCALE(wsptr[0], PASS1_BITS+3) + + & RANGE_MASK]; + + + + outptr[0] = dcval; + + outptr[1] = dcval; + + outptr[2] = dcval; + + outptr[3] = dcval; + + outptr[4] = dcval; + + outptr[5] = dcval; + + outptr[6] = dcval; + + outptr[7] = dcval; + + + + wsptr += DCTSIZE; /* advance pointer to next row */ + + continue; + + } + +#endif + + + + /* Even part */ + + + + tmp10 = ((DCTELEM) wsptr[0] + (DCTELEM) wsptr[4]); + + tmp11 = ((DCTELEM) wsptr[0] - (DCTELEM) wsptr[4]); + + + + tmp13 = ((DCTELEM) wsptr[2] + (DCTELEM) wsptr[6]); + + tmp12 = MULTIPLY((DCTELEM) wsptr[2] - (DCTELEM) wsptr[6], FIX_1_414213562) + + - tmp13; + + + + tmp0 = tmp10 + tmp13; + + tmp3 = tmp10 - tmp13; + + tmp1 = tmp11 + tmp12; + + tmp2 = tmp11 - tmp12; + + + + /* Odd part */ + + + + z13 = (DCTELEM) wsptr[5] + (DCTELEM) wsptr[3]; + + z10 = (DCTELEM) wsptr[5] - (DCTELEM) wsptr[3]; + + z11 = (DCTELEM) wsptr[1] + (DCTELEM) wsptr[7]; + + z12 = (DCTELEM) wsptr[1] - (DCTELEM) wsptr[7]; + + + + tmp7 = z11 + z13; /* phase 5 */ + + tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); /* 2*c4 */ + + + + z5 = MULTIPLY(z10 + z12, FIX_1_847759065); /* 2*c2 */ + + tmp10 = MULTIPLY(z12, FIX_1_082392200) - z5; /* 2*(c2-c6) */ + + tmp12 = MULTIPLY(z10, - FIX_2_613125930) + z5; /* -2*(c2+c6) */ + + + + tmp6 = tmp12 - tmp7; /* phase 2 */ + + tmp5 = tmp11 - tmp6; + + tmp4 = tmp10 + tmp5; + + + + /* Final output stage: scale down by a factor of 8 and range-limit */ + + + + outptr[0] = range_limit[IDESCALE(tmp0 + tmp7, PASS1_BITS+3) + + & RANGE_MASK]; + + outptr[7] = range_limit[IDESCALE(tmp0 - tmp7, PASS1_BITS+3) + + & RANGE_MASK]; + + outptr[1] = range_limit[IDESCALE(tmp1 + tmp6, PASS1_BITS+3) + + & RANGE_MASK]; + + outptr[6] = range_limit[IDESCALE(tmp1 - tmp6, PASS1_BITS+3) + + & RANGE_MASK]; + + outptr[2] = range_limit[IDESCALE(tmp2 + tmp5, PASS1_BITS+3) + + & RANGE_MASK]; + + outptr[5] = range_limit[IDESCALE(tmp2 - tmp5, PASS1_BITS+3) + + & RANGE_MASK]; + + outptr[4] = range_limit[IDESCALE(tmp3 + tmp4, PASS1_BITS+3) + + & RANGE_MASK]; + + outptr[3] = range_limit[IDESCALE(tmp3 - tmp4, PASS1_BITS+3) + + & RANGE_MASK]; + + + + wsptr += DCTSIZE; /* advance pointer to next row */ + + } + +} + + + +#endif /* DCT_IFAST_SUPPORTED */ + diff --git a/utils/roq2/jpeg/jidctint.c b/utils/roq2/jpeg/jidctint.c new file mode 100644 index 0000000..baff12b --- /dev/null +++ b/utils/roq2/jpeg/jidctint.c @@ -0,0 +1,776 @@ +/* + + * jidctint.c + + * + + * Copyright (C) 1991-1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains a slow-but-accurate integer implementation of the + + * inverse DCT (Discrete Cosine Transform). In the IJG code, this routine + + * must also perform dequantization of the input coefficients. + + * + + * A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT + + * on each row (or vice versa, but it's more convenient to emit a row at + + * a time). Direct algorithms are also available, but they are much more + + * complex and seem not to be any faster when reduced to code. + + * + + * This implementation is based on an algorithm described in + + * C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT + + * Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics, + + * Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991. + + * The primary algorithm described there uses 11 multiplies and 29 adds. + + * We use their alternate method with 12 multiplies and 32 adds. + + * The advantage of this method is that no data path contains more than one + + * multiplication; this allows a very simple and accurate implementation in + + * scaled fixed-point arithmetic, with a minimal number of shifts. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + +#include "jdct.h" /* Private declarations for DCT subsystem */ + + + +#ifdef DCT_ISLOW_SUPPORTED + + + + + +/* + + * This module is specialized to the case DCTSIZE = 8. + + */ + + + +#if DCTSIZE != 8 + + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ + +#endif + + + + + +/* + + * The poop on this scaling stuff is as follows: + + * + + * Each 1-D IDCT step produces outputs which are a factor of sqrt(N) + + * larger than the true IDCT outputs. The final outputs are therefore + + * a factor of N larger than desired; since N=8 this can be cured by + + * a simple right shift at the end of the algorithm. The advantage of + + * this arrangement is that we save two multiplications per 1-D IDCT, + + * because the y0 and y4 inputs need not be divided by sqrt(N). + + * + + * We have to do addition and subtraction of the integer inputs, which + + * is no problem, and multiplication by fractional constants, which is + + * a problem to do in integer arithmetic. We multiply all the constants + + * by CONST_SCALE and convert them to integer constants (thus retaining + + * CONST_BITS bits of precision in the constants). After doing a + + * multiplication we have to divide the product by CONST_SCALE, with proper + + * rounding, to produce the correct output. This division can be done + + * cheaply as a right shift of CONST_BITS bits. We postpone shifting + + * as long as possible so that partial sums can be added together with + + * full fractional precision. + + * + + * The outputs of the first pass are scaled up by PASS1_BITS bits so that + + * they are represented to better-than-integral precision. These outputs + + * require BITS_IN_JSAMPLE + PASS1_BITS + 3 bits; this fits in a 16-bit word + + * with the recommended scaling. (To scale up 12-bit sample data further, an + + * intermediate INT32 array would be needed.) + + * + + * To avoid overflow of the 32-bit intermediate results in pass 2, we must + + * have BITS_IN_JSAMPLE + CONST_BITS + PASS1_BITS <= 26. Error analysis + + * shows that the values given below are the most effective. + + */ + + + +#if BITS_IN_JSAMPLE == 8 + +#define CONST_BITS 13 + +#define PASS1_BITS 2 + +#else + +#define CONST_BITS 13 + +#define PASS1_BITS 1 /* lose a little precision to avoid overflow */ + +#endif + + + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + + * causing a lot of useless floating-point operations at run time. + + * To get around this we use the following pre-calculated constants. + + * If you change CONST_BITS you may want to add appropriate values. + + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + + */ + + + +#if CONST_BITS == 13 + +#define FIX_0_298631336 ((INT32) 2446) /* FIX(0.298631336) */ + +#define FIX_0_390180644 ((INT32) 3196) /* FIX(0.390180644) */ + +#define FIX_0_541196100 ((INT32) 4433) /* FIX(0.541196100) */ + +#define FIX_0_765366865 ((INT32) 6270) /* FIX(0.765366865) */ + +#define FIX_0_899976223 ((INT32) 7373) /* FIX(0.899976223) */ + +#define FIX_1_175875602 ((INT32) 9633) /* FIX(1.175875602) */ + +#define FIX_1_501321110 ((INT32) 12299) /* FIX(1.501321110) */ + +#define FIX_1_847759065 ((INT32) 15137) /* FIX(1.847759065) */ + +#define FIX_1_961570560 ((INT32) 16069) /* FIX(1.961570560) */ + +#define FIX_2_053119869 ((INT32) 16819) /* FIX(2.053119869) */ + +#define FIX_2_562915447 ((INT32) 20995) /* FIX(2.562915447) */ + +#define FIX_3_072711026 ((INT32) 25172) /* FIX(3.072711026) */ + +#else + +#define FIX_0_298631336 FIX(0.298631336) + +#define FIX_0_390180644 FIX(0.390180644) + +#define FIX_0_541196100 FIX(0.541196100) + +#define FIX_0_765366865 FIX(0.765366865) + +#define FIX_0_899976223 FIX(0.899976223) + +#define FIX_1_175875602 FIX(1.175875602) + +#define FIX_1_501321110 FIX(1.501321110) + +#define FIX_1_847759065 FIX(1.847759065) + +#define FIX_1_961570560 FIX(1.961570560) + +#define FIX_2_053119869 FIX(2.053119869) + +#define FIX_2_562915447 FIX(2.562915447) + +#define FIX_3_072711026 FIX(3.072711026) + +#endif + + + + + +/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. + + * For 8-bit samples with the recommended scaling, all the variable + + * and constant values involved are no more than 16 bits wide, so a + + * 16x16->32 bit multiply can be used instead of a full 32x32 multiply. + + * For 12-bit samples, a full 32-bit multiplication will be needed. + + */ + + + +#if BITS_IN_JSAMPLE == 8 + +#define MULTIPLY(var,const) MULTIPLY16C16(var,const) + +#else + +#define MULTIPLY(var,const) ((var) * (const)) + +#endif + + + + + +/* Dequantize a coefficient by multiplying it by the multiplier-table + + * entry; produce an int result. In this module, both inputs and result + + * are 16 bits or less, so either int or short multiply will work. + + */ + + + +#define DEQUANTIZE(coef,quantval) (((ISLOW_MULT_TYPE) (coef)) * (quantval)) + + + + + +/* + + * Perform dequantization and inverse DCT on one block of coefficients. + + */ + + + +GLOBAL void + +jpeg_idct_islow (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JCOEFPTR coef_block, + + JSAMPARRAY output_buf, JDIMENSION output_col) + +{ + + INT32 tmp0, tmp1, tmp2, tmp3; + + INT32 tmp10, tmp11, tmp12, tmp13; + + INT32 z1, z2, z3, z4, z5; + + JCOEFPTR inptr; + + ISLOW_MULT_TYPE * quantptr; + + int * wsptr; + + JSAMPROW outptr; + + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + + int ctr; + + int workspace[DCTSIZE2]; /* buffers data between passes */ + + SHIFT_TEMPS + + + + /* Pass 1: process columns from input, store into work array. */ + + /* Note results are scaled up by sqrt(8) compared to a true IDCT; */ + + /* furthermore, we scale the results by 2**PASS1_BITS. */ + + + + inptr = coef_block; + + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + + wsptr = workspace; + + for (ctr = DCTSIZE; ctr > 0; ctr--) { + + /* Due to quantization, we will usually find that many of the input + + * coefficients are zero, especially the AC terms. We can exploit this + + * by short-circuiting the IDCT calculation for any column in which all + + * the AC terms are zero. In that case each output is equal to the + + * DC coefficient (with scale factor as needed). + + * With typical images and quantization tables, half or more of the + + * column DCT calculations can be simplified this way. + + */ + + + + if ((inptr[DCTSIZE*1] | inptr[DCTSIZE*2] | inptr[DCTSIZE*3] | + + inptr[DCTSIZE*4] | inptr[DCTSIZE*5] | inptr[DCTSIZE*6] | + + inptr[DCTSIZE*7]) == 0) { + + /* AC terms all zero */ + + int dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]) << PASS1_BITS; + + + + wsptr[DCTSIZE*0] = dcval; + + wsptr[DCTSIZE*1] = dcval; + + wsptr[DCTSIZE*2] = dcval; + + wsptr[DCTSIZE*3] = dcval; + + wsptr[DCTSIZE*4] = dcval; + + wsptr[DCTSIZE*5] = dcval; + + wsptr[DCTSIZE*6] = dcval; + + wsptr[DCTSIZE*7] = dcval; + + + + inptr++; /* advance pointers to next column */ + + quantptr++; + + wsptr++; + + continue; + + } + + + + /* Even part: reverse the even part of the forward DCT. */ + + /* The rotator is sqrt(2)*c(-6). */ + + + + z2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + + z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + + + z1 = MULTIPLY(z2 + z3, FIX_0_541196100); + + tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065); + + tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865); + + + + z2 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + + z3 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + + + + tmp0 = (z2 + z3) << CONST_BITS; + + tmp1 = (z2 - z3) << CONST_BITS; + + + + tmp10 = tmp0 + tmp3; + + tmp13 = tmp0 - tmp3; + + tmp11 = tmp1 + tmp2; + + tmp12 = tmp1 - tmp2; + + + + /* Odd part per figure 8; the matrix is unitary and hence its + + * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. + + */ + + + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + tmp1 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + + tmp2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + + tmp3 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + + + + z1 = tmp0 + tmp3; + + z2 = tmp1 + tmp2; + + z3 = tmp0 + tmp2; + + z4 = tmp1 + tmp3; + + z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ + + + + tmp0 = MULTIPLY(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + + tmp1 = MULTIPLY(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + + tmp2 = MULTIPLY(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + + tmp3 = MULTIPLY(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + + z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + + z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + + z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + + z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + + + + z3 += z5; + + z4 += z5; + + + + tmp0 += z1 + z3; + + tmp1 += z2 + z4; + + tmp2 += z2 + z3; + + tmp3 += z1 + z4; + + + + /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ + + + + wsptr[DCTSIZE*0] = (int) DESCALE(tmp10 + tmp3, CONST_BITS-PASS1_BITS); + + wsptr[DCTSIZE*7] = (int) DESCALE(tmp10 - tmp3, CONST_BITS-PASS1_BITS); + + wsptr[DCTSIZE*1] = (int) DESCALE(tmp11 + tmp2, CONST_BITS-PASS1_BITS); + + wsptr[DCTSIZE*6] = (int) DESCALE(tmp11 - tmp2, CONST_BITS-PASS1_BITS); + + wsptr[DCTSIZE*2] = (int) DESCALE(tmp12 + tmp1, CONST_BITS-PASS1_BITS); + + wsptr[DCTSIZE*5] = (int) DESCALE(tmp12 - tmp1, CONST_BITS-PASS1_BITS); + + wsptr[DCTSIZE*3] = (int) DESCALE(tmp13 + tmp0, CONST_BITS-PASS1_BITS); + + wsptr[DCTSIZE*4] = (int) DESCALE(tmp13 - tmp0, CONST_BITS-PASS1_BITS); + + + + inptr++; /* advance pointers to next column */ + + quantptr++; + + wsptr++; + + } + + + + /* Pass 2: process rows from work array, store into output array. */ + + /* Note that we must descale the results by a factor of 8 == 2**3, */ + + /* and also undo the PASS1_BITS scaling. */ + + + + wsptr = workspace; + + for (ctr = 0; ctr < DCTSIZE; ctr++) { + + outptr = output_buf[ctr] + output_col; + + /* Rows of zeroes can be exploited in the same way as we did with columns. + + * However, the column calculation has created many nonzero AC terms, so + + * the simplification applies less often (typically 5% to 10% of the time). + + * On machines with very fast multiplication, it's possible that the + + * test takes more time than it's worth. In that case this section + + * may be commented out. + + */ + + + +#ifndef NO_ZERO_ROW_TEST + + if ((wsptr[1] | wsptr[2] | wsptr[3] | wsptr[4] | wsptr[5] | wsptr[6] | + + wsptr[7]) == 0) { + + /* AC terms all zero */ + + JSAMPLE dcval = range_limit[(int) DESCALE((INT32) wsptr[0], PASS1_BITS+3) + + & RANGE_MASK]; + + + + outptr[0] = dcval; + + outptr[1] = dcval; + + outptr[2] = dcval; + + outptr[3] = dcval; + + outptr[4] = dcval; + + outptr[5] = dcval; + + outptr[6] = dcval; + + outptr[7] = dcval; + + + + wsptr += DCTSIZE; /* advance pointer to next row */ + + continue; + + } + +#endif + + + + /* Even part: reverse the even part of the forward DCT. */ + + /* The rotator is sqrt(2)*c(-6). */ + + + + z2 = (INT32) wsptr[2]; + + z3 = (INT32) wsptr[6]; + + + + z1 = MULTIPLY(z2 + z3, FIX_0_541196100); + + tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065); + + tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865); + + + + tmp0 = ((INT32) wsptr[0] + (INT32) wsptr[4]) << CONST_BITS; + + tmp1 = ((INT32) wsptr[0] - (INT32) wsptr[4]) << CONST_BITS; + + + + tmp10 = tmp0 + tmp3; + + tmp13 = tmp0 - tmp3; + + tmp11 = tmp1 + tmp2; + + tmp12 = tmp1 - tmp2; + + + + /* Odd part per figure 8; the matrix is unitary and hence its + + * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. + + */ + + + + tmp0 = (INT32) wsptr[7]; + + tmp1 = (INT32) wsptr[5]; + + tmp2 = (INT32) wsptr[3]; + + tmp3 = (INT32) wsptr[1]; + + + + z1 = tmp0 + tmp3; + + z2 = tmp1 + tmp2; + + z3 = tmp0 + tmp2; + + z4 = tmp1 + tmp3; + + z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ + + + + tmp0 = MULTIPLY(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + + tmp1 = MULTIPLY(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + + tmp2 = MULTIPLY(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + + tmp3 = MULTIPLY(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + + z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + + z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + + z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + + z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + + + + z3 += z5; + + z4 += z5; + + + + tmp0 += z1 + z3; + + tmp1 += z2 + z4; + + tmp2 += z2 + z3; + + tmp3 += z1 + z4; + + + + /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ + + + + outptr[0] = range_limit[(int) DESCALE(tmp10 + tmp3, + + CONST_BITS+PASS1_BITS+3) + + & RANGE_MASK]; + + outptr[7] = range_limit[(int) DESCALE(tmp10 - tmp3, + + CONST_BITS+PASS1_BITS+3) + + & RANGE_MASK]; + + outptr[1] = range_limit[(int) DESCALE(tmp11 + tmp2, + + CONST_BITS+PASS1_BITS+3) + + & RANGE_MASK]; + + outptr[6] = range_limit[(int) DESCALE(tmp11 - tmp2, + + CONST_BITS+PASS1_BITS+3) + + & RANGE_MASK]; + + outptr[2] = range_limit[(int) DESCALE(tmp12 + tmp1, + + CONST_BITS+PASS1_BITS+3) + + & RANGE_MASK]; + + outptr[5] = range_limit[(int) DESCALE(tmp12 - tmp1, + + CONST_BITS+PASS1_BITS+3) + + & RANGE_MASK]; + + outptr[3] = range_limit[(int) DESCALE(tmp13 + tmp0, + + CONST_BITS+PASS1_BITS+3) + + & RANGE_MASK]; + + outptr[4] = range_limit[(int) DESCALE(tmp13 - tmp0, + + CONST_BITS+PASS1_BITS+3) + + & RANGE_MASK]; + + + + wsptr += DCTSIZE; /* advance pointer to next row */ + + } + +} + + + +#endif /* DCT_ISLOW_SUPPORTED */ + diff --git a/utils/roq2/jpeg/jidctred.c b/utils/roq2/jpeg/jidctred.c new file mode 100644 index 0000000..1b652ae --- /dev/null +++ b/utils/roq2/jpeg/jidctred.c @@ -0,0 +1,794 @@ +/* + + * jidctred.c + + * + + * Copyright (C) 1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains inverse-DCT routines that produce reduced-size output: + + * either 4x4, 2x2, or 1x1 pixels from an 8x8 DCT block. + + * + + * The implementation is based on the Loeffler, Ligtenberg and Moschytz (LL&M) + + * algorithm used in jidctint.c. We simply replace each 8-to-8 1-D IDCT step + + * with an 8-to-4 step that produces the four averages of two adjacent outputs + + * (or an 8-to-2 step producing two averages of four outputs, for 2x2 output). + + * These steps were derived by computing the corresponding values at the end + + * of the normal LL&M code, then simplifying as much as possible. + + * + + * 1x1 is trivial: just take the DC coefficient divided by 8. + + * + + * See jidctint.c for additional comments. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + +#include "jdct.h" /* Private declarations for DCT subsystem */ + + + +#ifdef IDCT_SCALING_SUPPORTED + + + + + +/* + + * This module is specialized to the case DCTSIZE = 8. + + */ + + + +#if DCTSIZE != 8 + + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ + +#endif + + + + + +/* Scaling is the same as in jidctint.c. */ + + + +#if BITS_IN_JSAMPLE == 8 + +#define CONST_BITS 13 + +#define PASS1_BITS 2 + +#else + +#define CONST_BITS 13 + +#define PASS1_BITS 1 /* lose a little precision to avoid overflow */ + +#endif + + + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + + * causing a lot of useless floating-point operations at run time. + + * To get around this we use the following pre-calculated constants. + + * If you change CONST_BITS you may want to add appropriate values. + + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + + */ + + + +#if CONST_BITS == 13 + +#define FIX_0_211164243 ((INT32) 1730) /* FIX(0.211164243) */ + +#define FIX_0_509795579 ((INT32) 4176) /* FIX(0.509795579) */ + +#define FIX_0_601344887 ((INT32) 4926) /* FIX(0.601344887) */ + +#define FIX_0_720959822 ((INT32) 5906) /* FIX(0.720959822) */ + +#define FIX_0_765366865 ((INT32) 6270) /* FIX(0.765366865) */ + +#define FIX_0_850430095 ((INT32) 6967) /* FIX(0.850430095) */ + +#define FIX_0_899976223 ((INT32) 7373) /* FIX(0.899976223) */ + +#define FIX_1_061594337 ((INT32) 8697) /* FIX(1.061594337) */ + +#define FIX_1_272758580 ((INT32) 10426) /* FIX(1.272758580) */ + +#define FIX_1_451774981 ((INT32) 11893) /* FIX(1.451774981) */ + +#define FIX_1_847759065 ((INT32) 15137) /* FIX(1.847759065) */ + +#define FIX_2_172734803 ((INT32) 17799) /* FIX(2.172734803) */ + +#define FIX_2_562915447 ((INT32) 20995) /* FIX(2.562915447) */ + +#define FIX_3_624509785 ((INT32) 29692) /* FIX(3.624509785) */ + +#else + +#define FIX_0_211164243 FIX(0.211164243) + +#define FIX_0_509795579 FIX(0.509795579) + +#define FIX_0_601344887 FIX(0.601344887) + +#define FIX_0_720959822 FIX(0.720959822) + +#define FIX_0_765366865 FIX(0.765366865) + +#define FIX_0_850430095 FIX(0.850430095) + +#define FIX_0_899976223 FIX(0.899976223) + +#define FIX_1_061594337 FIX(1.061594337) + +#define FIX_1_272758580 FIX(1.272758580) + +#define FIX_1_451774981 FIX(1.451774981) + +#define FIX_1_847759065 FIX(1.847759065) + +#define FIX_2_172734803 FIX(2.172734803) + +#define FIX_2_562915447 FIX(2.562915447) + +#define FIX_3_624509785 FIX(3.624509785) + +#endif + + + + + +/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. + + * For 8-bit samples with the recommended scaling, all the variable + + * and constant values involved are no more than 16 bits wide, so a + + * 16x16->32 bit multiply can be used instead of a full 32x32 multiply. + + * For 12-bit samples, a full 32-bit multiplication will be needed. + + */ + + + +#if BITS_IN_JSAMPLE == 8 + +#define MULTIPLY(var,const) MULTIPLY16C16(var,const) + +#else + +#define MULTIPLY(var,const) ((var) * (const)) + +#endif + + + + + +/* Dequantize a coefficient by multiplying it by the multiplier-table + + * entry; produce an int result. In this module, both inputs and result + + * are 16 bits or less, so either int or short multiply will work. + + */ + + + +#define DEQUANTIZE(coef,quantval) (((ISLOW_MULT_TYPE) (coef)) * (quantval)) + + + + + +/* + + * Perform dequantization and inverse DCT on one block of coefficients, + + * producing a reduced-size 4x4 output block. + + */ + + + +GLOBAL void + +jpeg_idct_4x4 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JCOEFPTR coef_block, + + JSAMPARRAY output_buf, JDIMENSION output_col) + +{ + + INT32 tmp0, tmp2, tmp10, tmp12; + + INT32 z1, z2, z3, z4; + + JCOEFPTR inptr; + + ISLOW_MULT_TYPE * quantptr; + + int * wsptr; + + JSAMPROW outptr; + + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + + int ctr; + + int workspace[DCTSIZE*4]; /* buffers data between passes */ + + SHIFT_TEMPS + + + + /* Pass 1: process columns from input, store into work array. */ + + + + inptr = coef_block; + + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + + wsptr = workspace; + + for (ctr = DCTSIZE; ctr > 0; inptr++, quantptr++, wsptr++, ctr--) { + + /* Don't bother to process column 4, because second pass won't use it */ + + if (ctr == DCTSIZE-4) + + continue; + + if ((inptr[DCTSIZE*1] | inptr[DCTSIZE*2] | inptr[DCTSIZE*3] | + + inptr[DCTSIZE*5] | inptr[DCTSIZE*6] | inptr[DCTSIZE*7]) == 0) { + + /* AC terms all zero; we need not examine term 4 for 4x4 output */ + + int dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]) << PASS1_BITS; + + + + wsptr[DCTSIZE*0] = dcval; + + wsptr[DCTSIZE*1] = dcval; + + wsptr[DCTSIZE*2] = dcval; + + wsptr[DCTSIZE*3] = dcval; + + + + continue; + + } + + + + /* Even part */ + + + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + + tmp0 <<= (CONST_BITS+1); + + + + z2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + + z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + + + tmp2 = MULTIPLY(z2, FIX_1_847759065) + MULTIPLY(z3, - FIX_0_765366865); + + + + tmp10 = tmp0 + tmp2; + + tmp12 = tmp0 - tmp2; + + + + /* Odd part */ + + + + z1 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + z2 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + + z3 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + + z4 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + + + + tmp0 = MULTIPLY(z1, - FIX_0_211164243) /* sqrt(2) * (c3-c1) */ + + + MULTIPLY(z2, FIX_1_451774981) /* sqrt(2) * (c3+c7) */ + + + MULTIPLY(z3, - FIX_2_172734803) /* sqrt(2) * (-c1-c5) */ + + + MULTIPLY(z4, FIX_1_061594337); /* sqrt(2) * (c5+c7) */ + + + + tmp2 = MULTIPLY(z1, - FIX_0_509795579) /* sqrt(2) * (c7-c5) */ + + + MULTIPLY(z2, - FIX_0_601344887) /* sqrt(2) * (c5-c1) */ + + + MULTIPLY(z3, FIX_0_899976223) /* sqrt(2) * (c3-c7) */ + + + MULTIPLY(z4, FIX_2_562915447); /* sqrt(2) * (c1+c3) */ + + + + /* Final output stage */ + + + + wsptr[DCTSIZE*0] = (int) DESCALE(tmp10 + tmp2, CONST_BITS-PASS1_BITS+1); + + wsptr[DCTSIZE*3] = (int) DESCALE(tmp10 - tmp2, CONST_BITS-PASS1_BITS+1); + + wsptr[DCTSIZE*1] = (int) DESCALE(tmp12 + tmp0, CONST_BITS-PASS1_BITS+1); + + wsptr[DCTSIZE*2] = (int) DESCALE(tmp12 - tmp0, CONST_BITS-PASS1_BITS+1); + + } + + + + /* Pass 2: process 4 rows from work array, store into output array. */ + + + + wsptr = workspace; + + for (ctr = 0; ctr < 4; ctr++) { + + outptr = output_buf[ctr] + output_col; + + /* It's not clear whether a zero row test is worthwhile here ... */ + + + +#ifndef NO_ZERO_ROW_TEST + + if ((wsptr[1] | wsptr[2] | wsptr[3] | wsptr[5] | wsptr[6] | + + wsptr[7]) == 0) { + + /* AC terms all zero */ + + JSAMPLE dcval = range_limit[(int) DESCALE((INT32) wsptr[0], PASS1_BITS+3) + + & RANGE_MASK]; + + + + outptr[0] = dcval; + + outptr[1] = dcval; + + outptr[2] = dcval; + + outptr[3] = dcval; + + + + wsptr += DCTSIZE; /* advance pointer to next row */ + + continue; + + } + +#endif + + + + /* Even part */ + + + + tmp0 = ((INT32) wsptr[0]) << (CONST_BITS+1); + + + + tmp2 = MULTIPLY((INT32) wsptr[2], FIX_1_847759065) + + + MULTIPLY((INT32) wsptr[6], - FIX_0_765366865); + + + + tmp10 = tmp0 + tmp2; + + tmp12 = tmp0 - tmp2; + + + + /* Odd part */ + + + + z1 = (INT32) wsptr[7]; + + z2 = (INT32) wsptr[5]; + + z3 = (INT32) wsptr[3]; + + z4 = (INT32) wsptr[1]; + + + + tmp0 = MULTIPLY(z1, - FIX_0_211164243) /* sqrt(2) * (c3-c1) */ + + + MULTIPLY(z2, FIX_1_451774981) /* sqrt(2) * (c3+c7) */ + + + MULTIPLY(z3, - FIX_2_172734803) /* sqrt(2) * (-c1-c5) */ + + + MULTIPLY(z4, FIX_1_061594337); /* sqrt(2) * (c5+c7) */ + + + + tmp2 = MULTIPLY(z1, - FIX_0_509795579) /* sqrt(2) * (c7-c5) */ + + + MULTIPLY(z2, - FIX_0_601344887) /* sqrt(2) * (c5-c1) */ + + + MULTIPLY(z3, FIX_0_899976223) /* sqrt(2) * (c3-c7) */ + + + MULTIPLY(z4, FIX_2_562915447); /* sqrt(2) * (c1+c3) */ + + + + /* Final output stage */ + + + + outptr[0] = range_limit[(int) DESCALE(tmp10 + tmp2, + + CONST_BITS+PASS1_BITS+3+1) + + & RANGE_MASK]; + + outptr[3] = range_limit[(int) DESCALE(tmp10 - tmp2, + + CONST_BITS+PASS1_BITS+3+1) + + & RANGE_MASK]; + + outptr[1] = range_limit[(int) DESCALE(tmp12 + tmp0, + + CONST_BITS+PASS1_BITS+3+1) + + & RANGE_MASK]; + + outptr[2] = range_limit[(int) DESCALE(tmp12 - tmp0, + + CONST_BITS+PASS1_BITS+3+1) + + & RANGE_MASK]; + + + + wsptr += DCTSIZE; /* advance pointer to next row */ + + } + +} + + + + + +/* + + * Perform dequantization and inverse DCT on one block of coefficients, + + * producing a reduced-size 2x2 output block. + + */ + + + +GLOBAL void + +jpeg_idct_2x2 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JCOEFPTR coef_block, + + JSAMPARRAY output_buf, JDIMENSION output_col) + +{ + + INT32 tmp0, tmp10, z1; + + JCOEFPTR inptr; + + ISLOW_MULT_TYPE * quantptr; + + int * wsptr; + + JSAMPROW outptr; + + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + + int ctr; + + int workspace[DCTSIZE*2]; /* buffers data between passes */ + + SHIFT_TEMPS + + + + /* Pass 1: process columns from input, store into work array. */ + + + + inptr = coef_block; + + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + + wsptr = workspace; + + for (ctr = DCTSIZE; ctr > 0; inptr++, quantptr++, wsptr++, ctr--) { + + /* Don't bother to process columns 2,4,6 */ + + if (ctr == DCTSIZE-2 || ctr == DCTSIZE-4 || ctr == DCTSIZE-6) + + continue; + + if ((inptr[DCTSIZE*1] | inptr[DCTSIZE*3] | + + inptr[DCTSIZE*5] | inptr[DCTSIZE*7]) == 0) { + + /* AC terms all zero; we need not examine terms 2,4,6 for 2x2 output */ + + int dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]) << PASS1_BITS; + + + + wsptr[DCTSIZE*0] = dcval; + + wsptr[DCTSIZE*1] = dcval; + + + + continue; + + } + + + + /* Even part */ + + + + z1 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + + tmp10 = z1 << (CONST_BITS+2); + + + + /* Odd part */ + + + + z1 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + tmp0 = MULTIPLY(z1, - FIX_0_720959822); /* sqrt(2) * (c7-c5+c3-c1) */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + + tmp0 += MULTIPLY(z1, FIX_0_850430095); /* sqrt(2) * (-c1+c3+c5+c7) */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + + tmp0 += MULTIPLY(z1, - FIX_1_272758580); /* sqrt(2) * (-c1+c3-c5-c7) */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + + tmp0 += MULTIPLY(z1, FIX_3_624509785); /* sqrt(2) * (c1+c3+c5+c7) */ + + + + /* Final output stage */ + + + + wsptr[DCTSIZE*0] = (int) DESCALE(tmp10 + tmp0, CONST_BITS-PASS1_BITS+2); + + wsptr[DCTSIZE*1] = (int) DESCALE(tmp10 - tmp0, CONST_BITS-PASS1_BITS+2); + + } + + + + /* Pass 2: process 2 rows from work array, store into output array. */ + + + + wsptr = workspace; + + for (ctr = 0; ctr < 2; ctr++) { + + outptr = output_buf[ctr] + output_col; + + /* It's not clear whether a zero row test is worthwhile here ... */ + + + +#ifndef NO_ZERO_ROW_TEST + + if ((wsptr[1] | wsptr[3] | wsptr[5] | wsptr[7]) == 0) { + + /* AC terms all zero */ + + JSAMPLE dcval = range_limit[(int) DESCALE((INT32) wsptr[0], PASS1_BITS+3) + + & RANGE_MASK]; + + + + outptr[0] = dcval; + + outptr[1] = dcval; + + + + wsptr += DCTSIZE; /* advance pointer to next row */ + + continue; + + } + +#endif + + + + /* Even part */ + + + + tmp10 = ((INT32) wsptr[0]) << (CONST_BITS+2); + + + + /* Odd part */ + + + + tmp0 = MULTIPLY((INT32) wsptr[7], - FIX_0_720959822) /* sqrt(2) * (c7-c5+c3-c1) */ + + + MULTIPLY((INT32) wsptr[5], FIX_0_850430095) /* sqrt(2) * (-c1+c3+c5+c7) */ + + + MULTIPLY((INT32) wsptr[3], - FIX_1_272758580) /* sqrt(2) * (-c1+c3-c5-c7) */ + + + MULTIPLY((INT32) wsptr[1], FIX_3_624509785); /* sqrt(2) * (c1+c3+c5+c7) */ + + + + /* Final output stage */ + + + + outptr[0] = range_limit[(int) DESCALE(tmp10 + tmp0, + + CONST_BITS+PASS1_BITS+3+2) + + & RANGE_MASK]; + + outptr[1] = range_limit[(int) DESCALE(tmp10 - tmp0, + + CONST_BITS+PASS1_BITS+3+2) + + & RANGE_MASK]; + + + + wsptr += DCTSIZE; /* advance pointer to next row */ + + } + +} + + + + + +/* + + * Perform dequantization and inverse DCT on one block of coefficients, + + * producing a reduced-size 1x1 output block. + + */ + + + +GLOBAL void + +jpeg_idct_1x1 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JCOEFPTR coef_block, + + JSAMPARRAY output_buf, JDIMENSION output_col) + +{ + + int dcval; + + ISLOW_MULT_TYPE * quantptr; + + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + + SHIFT_TEMPS + + + + /* We hardly need an inverse DCT routine for this: just take the + + * average pixel value, which is one-eighth of the DC coefficient. + + */ + + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + + dcval = DEQUANTIZE(coef_block[0], quantptr[0]); + + dcval = (int) DESCALE((INT32) dcval, 3); + + + + output_buf[0][output_col] = range_limit[dcval & RANGE_MASK]; + +} + + + +#endif /* IDCT_SCALING_SUPPORTED */ + diff --git a/utils/roq2/jpeg/jinclude.h b/utils/roq2/jpeg/jinclude.h new file mode 100644 index 0000000..bc564ed --- /dev/null +++ b/utils/roq2/jpeg/jinclude.h @@ -0,0 +1,182 @@ +/* + + * jinclude.h + + * + + * Copyright (C) 1991-1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file exists to provide a single place to fix any problems with + + * including the wrong system include files. (Common problems are taken + + * care of by the standard jconfig symbols, but on really weird systems + + * you may have to edit this file.) + + * + + * NOTE: this file is NOT intended to be included by applications using the + + * JPEG library. Most applications need only include jpeglib.h. + + */ + + + + + +/* Include auto-config file to find out which system include files we need. */ + + + +#include "jconfig.h" /* auto configuration options */ + +#define JCONFIG_INCLUDED /* so that jpeglib.h doesn't do it again */ + + + +/* + + * We need the NULL macro and size_t typedef. + + * On an ANSI-conforming system it is sufficient to include . + + * Otherwise, we get them from or ; we may have to + + * pull in as well. + + * Note that the core JPEG library does not require ; + + * only the default error handler and data source/destination modules do. + + * But we must pull it in because of the references to FILE in jpeglib.h. + + * You can remove those references if you want to compile without . + + */ + + + +#ifdef HAVE_STDDEF_H + +#include + +#endif + + + +#ifdef HAVE_STDLIB_H + +#include + +#endif + + + +#ifdef NEED_SYS_TYPES_H + +#include + +#endif + + + +#include + + + +/* + + * We need memory copying and zeroing functions, plus strncpy(). + + * ANSI and System V implementations declare these in . + + * BSD doesn't have the mem() functions, but it does have bcopy()/bzero(). + + * Some systems may declare memset and memcpy in . + + * + + * NOTE: we assume the size parameters to these functions are of type size_t. + + * Change the casts in these macros if not! + + */ + + + +#ifdef NEED_BSD_STRINGS + + + +#include + +#define MEMZERO(target,size) bzero((void *)(target), (size_t)(size)) + +#define MEMCOPY(dest,src,size) bcopy((const void *)(src), (void *)(dest), (size_t)(size)) + + + +#else /* not BSD, assume ANSI/SysV string lib */ + + + +#include + +#define MEMZERO(target,size) memset((void *)(target), 0, (size_t)(size)) + +#define MEMCOPY(dest,src,size) memcpy((void *)(dest), (const void *)(src), (size_t)(size)) + + + +#endif + + + +/* + + * In ANSI C, and indeed any rational implementation, size_t is also the + + * type returned by sizeof(). However, it seems there are some irrational + + * implementations out there, in which sizeof() returns an int even though + + * size_t is defined as long or unsigned long. To ensure consistent results + + * we always use this SIZEOF() macro in place of using sizeof() directly. + + */ + + + +#define SIZEOF(object) ((size_t) sizeof(object)) + + + +/* + + * The modules that use fread() and fwrite() always invoke them through + + * these macros. On some systems you may need to twiddle the argument casts. + + * CAUTION: argument order is different from underlying functions! + + */ + + + +#define JFREAD(file,buf,sizeofbuf) \ + + ((size_t) fread((void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) + +#define JFWRITE(file,buf,sizeofbuf) \ + + ((size_t) fwrite((const void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) + diff --git a/utils/roq2/jpeg/jmemmgr.c b/utils/roq2/jpeg/jmemmgr.c new file mode 100644 index 0000000..e93e67e --- /dev/null +++ b/utils/roq2/jpeg/jmemmgr.c @@ -0,0 +1,2230 @@ +/* + + * jmemmgr.c + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains the JPEG system-independent memory management + + * routines. This code is usable across a wide variety of machines; most + + * of the system dependencies have been isolated in a separate file. + + * The major functions provided here are: + + * * pool-based allocation and freeing of memory; + + * * policy decisions about how to divide available memory among the + + * virtual arrays; + + * * control logic for swapping virtual arrays between main memory and + + * backing storage. + + * The separate system-dependent file provides the actual backing-storage + + * access code, and it contains the policy decision about how much total + + * main memory to use. + + * This file is system-dependent in the sense that some of its functions + + * are unnecessary in some systems. For example, if there is enough virtual + + * memory so that backing storage will never be used, much of the virtual + + * array control logic could be removed. (Of course, if you have that much + + * memory then you shouldn't care about a little bit of unused code...) + + */ + + + +#define JPEG_INTERNALS + +#define AM_MEMORY_MANAGER /* we define jvirt_Xarray_control structs */ + +#include "jinclude.h" + +#include "jpeglib.h" + +#include "jmemsys.h" /* import the system-dependent declarations */ + + + +#ifndef NO_GETENV + +#ifndef HAVE_STDLIB_H /* should declare getenv() */ + +extern char * getenv JPP((const char * name)); + +#endif + +#endif + + + + + +/* + + * Some important notes: + + * The allocation routines provided here must never return NULL. + + * They should exit to error_exit if unsuccessful. + + * + + * It's not a good idea to try to merge the sarray and barray routines, + + * even though they are textually almost the same, because samples are + + * usually stored as bytes while coefficients are shorts or ints. Thus, + + * in machines where byte pointers have a different representation from + + * word pointers, the resulting machine code could not be the same. + + */ + + + + + +/* + + * Many machines require storage alignment: longs must start on 4-byte + + * boundaries, doubles on 8-byte boundaries, etc. On such machines, malloc() + + * always returns pointers that are multiples of the worst-case alignment + + * requirement, and we had better do so too. + + * There isn't any really portable way to determine the worst-case alignment + + * requirement. This module assumes that the alignment requirement is + + * multiples of sizeof(ALIGN_TYPE). + + * By default, we define ALIGN_TYPE as double. This is necessary on some + + * workstations (where doubles really do need 8-byte alignment) and will work + + * fine on nearly everything. If your machine has lesser alignment needs, + + * you can save a few bytes by making ALIGN_TYPE smaller. + + * The only place I know of where this will NOT work is certain Macintosh + + * 680x0 compilers that define double as a 10-byte IEEE extended float. + + * Doing 10-byte alignment is counterproductive because longwords won't be + + * aligned well. Put "#define ALIGN_TYPE long" in jconfig.h if you have + + * such a compiler. + + */ + + + +#ifndef ALIGN_TYPE /* so can override from jconfig.h */ + +#define ALIGN_TYPE double + +#endif + + + + + +/* + + * We allocate objects from "pools", where each pool is gotten with a single + + * request to jpeg_get_small() or jpeg_get_large(). There is no per-object + + * overhead within a pool, except for alignment padding. Each pool has a + + * header with a link to the next pool of the same class. + + * Small and large pool headers are identical except that the latter's + + * link pointer must be FAR on 80x86 machines. + + * Notice that the "real" header fields are union'ed with a dummy ALIGN_TYPE + + * field. This forces the compiler to make SIZEOF(small_pool_hdr) a multiple + + * of the alignment requirement of ALIGN_TYPE. + + */ + + + +typedef union small_pool_struct * small_pool_ptr; + + + +typedef union small_pool_struct { + + struct { + + small_pool_ptr next; /* next in list of pools */ + + size_t bytes_used; /* how many bytes already used within pool */ + + size_t bytes_left; /* bytes still available in this pool */ + + } hdr; + + ALIGN_TYPE dummy; /* included in union to ensure alignment */ + +} small_pool_hdr; + + + +typedef union large_pool_struct FAR * large_pool_ptr; + + + +typedef union large_pool_struct { + + struct { + + large_pool_ptr next; /* next in list of pools */ + + size_t bytes_used; /* how many bytes already used within pool */ + + size_t bytes_left; /* bytes still available in this pool */ + + } hdr; + + ALIGN_TYPE dummy; /* included in union to ensure alignment */ + +} large_pool_hdr; + + + + + +/* + + * Here is the full definition of a memory manager object. + + */ + + + +typedef struct { + + struct jpeg_memory_mgr pub; /* public fields */ + + + + /* Each pool identifier (lifetime class) names a linked list of pools. */ + + small_pool_ptr small_list[JPOOL_NUMPOOLS]; + + large_pool_ptr large_list[JPOOL_NUMPOOLS]; + + + + /* Since we only have one lifetime class of virtual arrays, only one + + * linked list is necessary (for each datatype). Note that the virtual + + * array control blocks being linked together are actually stored somewhere + + * in the small-pool list. + + */ + + jvirt_sarray_ptr virt_sarray_list; + + jvirt_barray_ptr virt_barray_list; + + + + /* This counts total space obtained from jpeg_get_small/large */ + + long total_space_allocated; + + + + /* alloc_sarray and alloc_barray set this value for use by virtual + + * array routines. + + */ + + JDIMENSION last_rowsperchunk; /* from most recent alloc_sarray/barray */ + +} my_memory_mgr; + + + +typedef my_memory_mgr * my_mem_ptr; + + + + + +/* + + * The control blocks for virtual arrays. + + * Note that these blocks are allocated in the "small" pool area. + + * System-dependent info for the associated backing store (if any) is hidden + + * inside the backing_store_info struct. + + */ + + + +struct jvirt_sarray_control { + + JSAMPARRAY mem_buffer; /* => the in-memory buffer */ + + JDIMENSION rows_in_array; /* total virtual array height */ + + JDIMENSION samplesperrow; /* width of array (and of memory buffer) */ + + JDIMENSION maxaccess; /* max rows accessed by access_virt_sarray */ + + JDIMENSION rows_in_mem; /* height of memory buffer */ + + JDIMENSION rowsperchunk; /* allocation chunk size in mem_buffer */ + + JDIMENSION cur_start_row; /* first logical row # in the buffer */ + + JDIMENSION first_undef_row; /* row # of first uninitialized row */ + + boolean pre_zero; /* pre-zero mode requested? */ + + boolean dirty; /* do current buffer contents need written? */ + + boolean b_s_open; /* is backing-store data valid? */ + + jvirt_sarray_ptr next; /* link to next virtual sarray control block */ + + backing_store_info b_s_info; /* System-dependent control info */ + +}; + + + +struct jvirt_barray_control { + + JBLOCKARRAY mem_buffer; /* => the in-memory buffer */ + + JDIMENSION rows_in_array; /* total virtual array height */ + + JDIMENSION blocksperrow; /* width of array (and of memory buffer) */ + + JDIMENSION maxaccess; /* max rows accessed by access_virt_barray */ + + JDIMENSION rows_in_mem; /* height of memory buffer */ + + JDIMENSION rowsperchunk; /* allocation chunk size in mem_buffer */ + + JDIMENSION cur_start_row; /* first logical row # in the buffer */ + + JDIMENSION first_undef_row; /* row # of first uninitialized row */ + + boolean pre_zero; /* pre-zero mode requested? */ + + boolean dirty; /* do current buffer contents need written? */ + + boolean b_s_open; /* is backing-store data valid? */ + + jvirt_barray_ptr next; /* link to next virtual barray control block */ + + backing_store_info b_s_info; /* System-dependent control info */ + +}; + + + + + +#ifdef MEM_STATS /* optional extra stuff for statistics */ + + + +LOCAL void + +print_mem_stats (j_common_ptr cinfo, int pool_id) + +{ + + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + + small_pool_ptr shdr_ptr; + + large_pool_ptr lhdr_ptr; + + + + /* Since this is only a debugging stub, we can cheat a little by using + + * fprintf directly rather than going through the trace message code. + + * This is helpful because message parm array can't handle longs. + + */ + + fprintf(stderr, "Freeing pool %d, total space = %ld\n", + + pool_id, mem->total_space_allocated); + + + + for (lhdr_ptr = mem->large_list[pool_id]; lhdr_ptr != NULL; + + lhdr_ptr = lhdr_ptr->hdr.next) { + + fprintf(stderr, " Large chunk used %ld\n", + + (long) lhdr_ptr->hdr.bytes_used); + + } + + + + for (shdr_ptr = mem->small_list[pool_id]; shdr_ptr != NULL; + + shdr_ptr = shdr_ptr->hdr.next) { + + fprintf(stderr, " Small chunk used %ld free %ld\n", + + (long) shdr_ptr->hdr.bytes_used, + + (long) shdr_ptr->hdr.bytes_left); + + } + +} + + + +#endif /* MEM_STATS */ + + + + + +LOCAL void + +out_of_memory (j_common_ptr cinfo, int which) + +/* Report an out-of-memory error and stop execution */ + +/* If we compiled MEM_STATS support, report alloc requests before dying */ + +{ + +#ifdef MEM_STATS + + cinfo->err->trace_level = 2; /* force self_destruct to report stats */ + +#endif + + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, which); + +} + + + + + +/* + + * Allocation of "small" objects. + + * + + * For these, we use pooled storage. When a new pool must be created, + + * we try to get enough space for the current request plus a "slop" factor, + + * where the slop will be the amount of leftover space in the new pool. + + * The speed vs. space tradeoff is largely determined by the slop values. + + * A different slop value is provided for each pool class (lifetime), + + * and we also distinguish the first pool of a class from later ones. + + * NOTE: the values given work fairly well on both 16- and 32-bit-int + + * machines, but may be too small if longs are 64 bits or more. + + */ + + + +static const size_t first_pool_slop[JPOOL_NUMPOOLS] = + +{ + + 1600, /* first PERMANENT pool */ + + 16000 /* first IMAGE pool */ + +}; + + + +static const size_t extra_pool_slop[JPOOL_NUMPOOLS] = + +{ + + 0, /* additional PERMANENT pools */ + + 5000 /* additional IMAGE pools */ + +}; + + + +#define MIN_SLOP 50 /* greater than 0 to avoid futile looping */ + + + + + +METHODDEF void * + +alloc_small (j_common_ptr cinfo, int pool_id, size_t sizeofobject) + +/* Allocate a "small" object */ + +{ + + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + + small_pool_ptr hdr_ptr, prev_hdr_ptr; + + char * data_ptr; + + size_t odd_bytes, min_request, slop; + + + + /* Check for unsatisfiable request (do now to ensure no overflow below) */ + + if (sizeofobject > (size_t) (MAX_ALLOC_CHUNK-SIZEOF(small_pool_hdr))) + + out_of_memory(cinfo, 1); /* request exceeds malloc's ability */ + + + + /* Round up the requested size to a multiple of SIZEOF(ALIGN_TYPE) */ + + odd_bytes = sizeofobject % SIZEOF(ALIGN_TYPE); + + if (odd_bytes > 0) + + sizeofobject += SIZEOF(ALIGN_TYPE) - odd_bytes; + + + + /* See if space is available in any existing pool */ + + if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) + + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + prev_hdr_ptr = NULL; + + hdr_ptr = mem->small_list[pool_id]; + + while (hdr_ptr != NULL) { + + if (hdr_ptr->hdr.bytes_left >= sizeofobject) + + break; /* found pool with enough space */ + + prev_hdr_ptr = hdr_ptr; + + hdr_ptr = hdr_ptr->hdr.next; + + } + + + + /* Time to make a new pool? */ + + if (hdr_ptr == NULL) { + + /* min_request is what we need now, slop is what will be leftover */ + + min_request = sizeofobject + SIZEOF(small_pool_hdr); + + if (prev_hdr_ptr == NULL) /* first pool in class? */ + + slop = first_pool_slop[pool_id]; + + else + + slop = extra_pool_slop[pool_id]; + + /* Don't ask for more than MAX_ALLOC_CHUNK */ + + if (slop > (size_t) (MAX_ALLOC_CHUNK-min_request)) + + slop = (size_t) (MAX_ALLOC_CHUNK-min_request); + + /* Try to get space, if fail reduce slop and try again */ + + for (;;) { + + hdr_ptr = (small_pool_ptr) jpeg_get_small(cinfo, min_request + slop); + + if (hdr_ptr != NULL) + + break; + + slop /= 2; + + if (slop < MIN_SLOP) /* give up when it gets real small */ + + out_of_memory(cinfo, 2); /* jpeg_get_small failed */ + + } + + mem->total_space_allocated += min_request + slop; + + /* Success, initialize the new pool header and add to end of list */ + + hdr_ptr->hdr.next = NULL; + + hdr_ptr->hdr.bytes_used = 0; + + hdr_ptr->hdr.bytes_left = sizeofobject + slop; + + if (prev_hdr_ptr == NULL) /* first pool in class? */ + + mem->small_list[pool_id] = hdr_ptr; + + else + + prev_hdr_ptr->hdr.next = hdr_ptr; + + } + + + + /* OK, allocate the object from the current pool */ + + data_ptr = (char *) (hdr_ptr + 1); /* point to first data byte in pool */ + + data_ptr += hdr_ptr->hdr.bytes_used; /* point to place for object */ + + hdr_ptr->hdr.bytes_used += sizeofobject; + + hdr_ptr->hdr.bytes_left -= sizeofobject; + + + + return (void *) data_ptr; + +} + + + + + +/* + + * Allocation of "large" objects. + + * + + * The external semantics of these are the same as "small" objects, + + * except that FAR pointers are used on 80x86. However the pool + + * management heuristics are quite different. We assume that each + + * request is large enough that it may as well be passed directly to + + * jpeg_get_large; the pool management just links everything together + + * so that we can free it all on demand. + + * Note: the major use of "large" objects is in JSAMPARRAY and JBLOCKARRAY + + * structures. The routines that create these structures (see below) + + * deliberately bunch rows together to ensure a large request size. + + */ + + + +METHODDEF void FAR * + +alloc_large (j_common_ptr cinfo, int pool_id, size_t sizeofobject) + +/* Allocate a "large" object */ + +{ + + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + + large_pool_ptr hdr_ptr; + + size_t odd_bytes; + + + + /* Check for unsatisfiable request (do now to ensure no overflow below) */ + + if (sizeofobject > (size_t) (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr))) + + out_of_memory(cinfo, 3); /* request exceeds malloc's ability */ + + + + /* Round up the requested size to a multiple of SIZEOF(ALIGN_TYPE) */ + + odd_bytes = sizeofobject % SIZEOF(ALIGN_TYPE); + + if (odd_bytes > 0) + + sizeofobject += SIZEOF(ALIGN_TYPE) - odd_bytes; + + + + /* Always make a new pool */ + + if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) + + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + + + hdr_ptr = (large_pool_ptr) jpeg_get_large(cinfo, sizeofobject + + + SIZEOF(large_pool_hdr)); + + if (hdr_ptr == NULL) + + out_of_memory(cinfo, 4); /* jpeg_get_large failed */ + + mem->total_space_allocated += sizeofobject + SIZEOF(large_pool_hdr); + + + + /* Success, initialize the new pool header and add to list */ + + hdr_ptr->hdr.next = mem->large_list[pool_id]; + + /* We maintain space counts in each pool header for statistical purposes, + + * even though they are not needed for allocation. + + */ + + hdr_ptr->hdr.bytes_used = sizeofobject; + + hdr_ptr->hdr.bytes_left = 0; + + mem->large_list[pool_id] = hdr_ptr; + + + + return (void FAR *) (hdr_ptr + 1); /* point to first data byte in pool */ + +} + + + + + +/* + + * Creation of 2-D sample arrays. + + * The pointers are in near heap, the samples themselves in FAR heap. + + * + + * To minimize allocation overhead and to allow I/O of large contiguous + + * blocks, we allocate the sample rows in groups of as many rows as possible + + * without exceeding MAX_ALLOC_CHUNK total bytes per allocation request. + + * NB: the virtual array control routines, later in this file, know about + + * this chunking of rows. The rowsperchunk value is left in the mem manager + + * object so that it can be saved away if this sarray is the workspace for + + * a virtual array. + + */ + + + +METHODDEF JSAMPARRAY + +alloc_sarray (j_common_ptr cinfo, int pool_id, + + JDIMENSION samplesperrow, JDIMENSION numrows) + +/* Allocate a 2-D sample array */ + +{ + + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + + JSAMPARRAY result; + + JSAMPROW workspace; + + JDIMENSION rowsperchunk, currow, i; + + long ltemp; + + + + /* Calculate max # of rows allowed in one allocation chunk */ + + ltemp = (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr)) / + + ((long) samplesperrow * SIZEOF(JSAMPLE)); + + if (ltemp <= 0) + + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + + if (ltemp < (long) numrows) + + rowsperchunk = (JDIMENSION) ltemp; + + else + + rowsperchunk = numrows; + + mem->last_rowsperchunk = rowsperchunk; + + + + /* Get space for row pointers (small object) */ + + result = (JSAMPARRAY) alloc_small(cinfo, pool_id, + + (size_t) (numrows * SIZEOF(JSAMPROW))); + + + + /* Get the rows themselves (large objects) */ + + currow = 0; + + while (currow < numrows) { + + rowsperchunk = MIN(rowsperchunk, numrows - currow); + + workspace = (JSAMPROW) alloc_large(cinfo, pool_id, + + (size_t) ((size_t) rowsperchunk * (size_t) samplesperrow + + * SIZEOF(JSAMPLE))); + + for (i = rowsperchunk; i > 0; i--) { + + result[currow++] = workspace; + + workspace += samplesperrow; + + } + + } + + + + return result; + +} + + + + + +/* + + * Creation of 2-D coefficient-block arrays. + + * This is essentially the same as the code for sample arrays, above. + + */ + + + +METHODDEF JBLOCKARRAY + +alloc_barray (j_common_ptr cinfo, int pool_id, + + JDIMENSION blocksperrow, JDIMENSION numrows) + +/* Allocate a 2-D coefficient-block array */ + +{ + + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + + JBLOCKARRAY result; + + JBLOCKROW workspace; + + JDIMENSION rowsperchunk, currow, i; + + long ltemp; + + + + /* Calculate max # of rows allowed in one allocation chunk */ + + ltemp = (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr)) / + + ((long) blocksperrow * SIZEOF(JBLOCK)); + + if (ltemp <= 0) + + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + + if (ltemp < (long) numrows) + + rowsperchunk = (JDIMENSION) ltemp; + + else + + rowsperchunk = numrows; + + mem->last_rowsperchunk = rowsperchunk; + + + + /* Get space for row pointers (small object) */ + + result = (JBLOCKARRAY) alloc_small(cinfo, pool_id, + + (size_t) (numrows * SIZEOF(JBLOCKROW))); + + + + /* Get the rows themselves (large objects) */ + + currow = 0; + + while (currow < numrows) { + + rowsperchunk = MIN(rowsperchunk, numrows - currow); + + workspace = (JBLOCKROW) alloc_large(cinfo, pool_id, + + (size_t) ((size_t) rowsperchunk * (size_t) blocksperrow + + * SIZEOF(JBLOCK))); + + for (i = rowsperchunk; i > 0; i--) { + + result[currow++] = workspace; + + workspace += blocksperrow; + + } + + } + + + + return result; + +} + + + + + +/* + + * About virtual array management: + + * + + * The above "normal" array routines are only used to allocate strip buffers + + * (as wide as the image, but just a few rows high). Full-image-sized buffers + + * are handled as "virtual" arrays. The array is still accessed a strip at a + + * time, but the memory manager must save the whole array for repeated + + * accesses. The intended implementation is that there is a strip buffer in + + * memory (as high as is possible given the desired memory limit), plus a + + * backing file that holds the rest of the array. + + * + + * The request_virt_array routines are told the total size of the image and + + * the maximum number of rows that will be accessed at once. The in-memory + + * buffer must be at least as large as the maxaccess value. + + * + + * The request routines create control blocks but not the in-memory buffers. + + * That is postponed until realize_virt_arrays is called. At that time the + + * total amount of space needed is known (approximately, anyway), so free + + * memory can be divided up fairly. + + * + + * The access_virt_array routines are responsible for making a specific strip + + * area accessible (after reading or writing the backing file, if necessary). + + * Note that the access routines are told whether the caller intends to modify + + * the accessed strip; during a read-only pass this saves having to rewrite + + * data to disk. The access routines are also responsible for pre-zeroing + + * any newly accessed rows, if pre-zeroing was requested. + + * + + * In current usage, the access requests are usually for nonoverlapping + + * strips; that is, successive access start_row numbers differ by exactly + + * num_rows = maxaccess. This means we can get good performance with simple + + * buffer dump/reload logic, by making the in-memory buffer be a multiple + + * of the access height; then there will never be accesses across bufferload + + * boundaries. The code will still work with overlapping access requests, + + * but it doesn't handle bufferload overlaps very efficiently. + + */ + + + + + +METHODDEF jvirt_sarray_ptr + +request_virt_sarray (j_common_ptr cinfo, int pool_id, boolean pre_zero, + + JDIMENSION samplesperrow, JDIMENSION numrows, + + JDIMENSION maxaccess) + +/* Request a virtual 2-D sample array */ + +{ + + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + + jvirt_sarray_ptr result; + + + + /* Only IMAGE-lifetime virtual arrays are currently supported */ + + if (pool_id != JPOOL_IMAGE) + + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + + + /* get control block */ + + result = (jvirt_sarray_ptr) alloc_small(cinfo, pool_id, + + SIZEOF(struct jvirt_sarray_control)); + + + + result->mem_buffer = NULL; /* marks array not yet realized */ + + result->rows_in_array = numrows; + + result->samplesperrow = samplesperrow; + + result->maxaccess = maxaccess; + + result->pre_zero = pre_zero; + + result->b_s_open = FALSE; /* no associated backing-store object */ + + result->next = mem->virt_sarray_list; /* add to list of virtual arrays */ + + mem->virt_sarray_list = result; + + + + return result; + +} + + + + + +METHODDEF jvirt_barray_ptr + +request_virt_barray (j_common_ptr cinfo, int pool_id, boolean pre_zero, + + JDIMENSION blocksperrow, JDIMENSION numrows, + + JDIMENSION maxaccess) + +/* Request a virtual 2-D coefficient-block array */ + +{ + + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + + jvirt_barray_ptr result; + + + + /* Only IMAGE-lifetime virtual arrays are currently supported */ + + if (pool_id != JPOOL_IMAGE) + + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + + + /* get control block */ + + result = (jvirt_barray_ptr) alloc_small(cinfo, pool_id, + + SIZEOF(struct jvirt_barray_control)); + + + + result->mem_buffer = NULL; /* marks array not yet realized */ + + result->rows_in_array = numrows; + + result->blocksperrow = blocksperrow; + + result->maxaccess = maxaccess; + + result->pre_zero = pre_zero; + + result->b_s_open = FALSE; /* no associated backing-store object */ + + result->next = mem->virt_barray_list; /* add to list of virtual arrays */ + + mem->virt_barray_list = result; + + + + return result; + +} + + + + + +METHODDEF void + +realize_virt_arrays (j_common_ptr cinfo) + +/* Allocate the in-memory buffers for any unrealized virtual arrays */ + +{ + + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + + long space_per_minheight, maximum_space, avail_mem; + + long minheights, max_minheights; + + jvirt_sarray_ptr sptr; + + jvirt_barray_ptr bptr; + + + + /* Compute the minimum space needed (maxaccess rows in each buffer) + + * and the maximum space needed (full image height in each buffer). + + * These may be of use to the system-dependent jpeg_mem_available routine. + + */ + + space_per_minheight = 0; + + maximum_space = 0; + + for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { + + if (sptr->mem_buffer == NULL) { /* if not realized yet */ + + space_per_minheight += (long) sptr->maxaccess * + + (long) sptr->samplesperrow * SIZEOF(JSAMPLE); + + maximum_space += (long) sptr->rows_in_array * + + (long) sptr->samplesperrow * SIZEOF(JSAMPLE); + + } + + } + + for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { + + if (bptr->mem_buffer == NULL) { /* if not realized yet */ + + space_per_minheight += (long) bptr->maxaccess * + + (long) bptr->blocksperrow * SIZEOF(JBLOCK); + + maximum_space += (long) bptr->rows_in_array * + + (long) bptr->blocksperrow * SIZEOF(JBLOCK); + + } + + } + + + + if (space_per_minheight <= 0) + + return; /* no unrealized arrays, no work */ + + + + /* Determine amount of memory to actually use; this is system-dependent. */ + + avail_mem = jpeg_mem_available(cinfo, space_per_minheight, maximum_space, + + mem->total_space_allocated); + + + + /* If the maximum space needed is available, make all the buffers full + + * height; otherwise parcel it out with the same number of minheights + + * in each buffer. + + */ + + if (avail_mem >= maximum_space) + + max_minheights = 1000000000L; + + else { + + max_minheights = avail_mem / space_per_minheight; + + /* If there doesn't seem to be enough space, try to get the minimum + + * anyway. This allows a "stub" implementation of jpeg_mem_available(). + + */ + + if (max_minheights <= 0) + + max_minheights = 1; + + } + + + + /* Allocate the in-memory buffers and initialize backing store as needed. */ + + + + for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { + + if (sptr->mem_buffer == NULL) { /* if not realized yet */ + + minheights = ((long) sptr->rows_in_array - 1L) / sptr->maxaccess + 1L; + + if (minheights <= max_minheights) { + + /* This buffer fits in memory */ + + sptr->rows_in_mem = sptr->rows_in_array; + + } else { + + /* It doesn't fit in memory, create backing store. */ + + sptr->rows_in_mem = (JDIMENSION) (max_minheights * sptr->maxaccess); + + jpeg_open_backing_store(cinfo, & sptr->b_s_info, + + (long) sptr->rows_in_array * + + (long) sptr->samplesperrow * + + (long) SIZEOF(JSAMPLE)); + + sptr->b_s_open = TRUE; + + } + + sptr->mem_buffer = alloc_sarray(cinfo, JPOOL_IMAGE, + + sptr->samplesperrow, sptr->rows_in_mem); + + sptr->rowsperchunk = mem->last_rowsperchunk; + + sptr->cur_start_row = 0; + + sptr->first_undef_row = 0; + + sptr->dirty = FALSE; + + } + + } + + + + for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { + + if (bptr->mem_buffer == NULL) { /* if not realized yet */ + + minheights = ((long) bptr->rows_in_array - 1L) / bptr->maxaccess + 1L; + + if (minheights <= max_minheights) { + + /* This buffer fits in memory */ + + bptr->rows_in_mem = bptr->rows_in_array; + + } else { + + /* It doesn't fit in memory, create backing store. */ + + bptr->rows_in_mem = (JDIMENSION) (max_minheights * bptr->maxaccess); + + jpeg_open_backing_store(cinfo, & bptr->b_s_info, + + (long) bptr->rows_in_array * + + (long) bptr->blocksperrow * + + (long) SIZEOF(JBLOCK)); + + bptr->b_s_open = TRUE; + + } + + bptr->mem_buffer = alloc_barray(cinfo, JPOOL_IMAGE, + + bptr->blocksperrow, bptr->rows_in_mem); + + bptr->rowsperchunk = mem->last_rowsperchunk; + + bptr->cur_start_row = 0; + + bptr->first_undef_row = 0; + + bptr->dirty = FALSE; + + } + + } + +} + + + + + +LOCAL void + +do_sarray_io (j_common_ptr cinfo, jvirt_sarray_ptr ptr, boolean writing) + +/* Do backing store read or write of a virtual sample array */ + +{ + + long bytesperrow, file_offset, byte_count, rows, thisrow, i; + + + + bytesperrow = (long) ptr->samplesperrow * SIZEOF(JSAMPLE); + + file_offset = ptr->cur_start_row * bytesperrow; + + /* Loop to read or write each allocation chunk in mem_buffer */ + + for (i = 0; i < (long) ptr->rows_in_mem; i += ptr->rowsperchunk) { + + /* One chunk, but check for short chunk at end of buffer */ + + rows = MIN((long) ptr->rowsperchunk, (long) ptr->rows_in_mem - i); + + /* Transfer no more than is currently defined */ + + thisrow = (long) ptr->cur_start_row + i; + + rows = MIN(rows, (long) ptr->first_undef_row - thisrow); + + /* Transfer no more than fits in file */ + + rows = MIN(rows, (long) ptr->rows_in_array - thisrow); + + if (rows <= 0) /* this chunk might be past end of file! */ + + break; + + byte_count = rows * bytesperrow; + + if (writing) + + (*ptr->b_s_info.write_backing_store) (cinfo, & ptr->b_s_info, + + (void FAR *) ptr->mem_buffer[i], + + file_offset, byte_count); + + else + + (*ptr->b_s_info.read_backing_store) (cinfo, & ptr->b_s_info, + + (void FAR *) ptr->mem_buffer[i], + + file_offset, byte_count); + + file_offset += byte_count; + + } + +} + + + + + +LOCAL void + +do_barray_io (j_common_ptr cinfo, jvirt_barray_ptr ptr, boolean writing) + +/* Do backing store read or write of a virtual coefficient-block array */ + +{ + + long bytesperrow, file_offset, byte_count, rows, thisrow, i; + + + + bytesperrow = (long) ptr->blocksperrow * SIZEOF(JBLOCK); + + file_offset = ptr->cur_start_row * bytesperrow; + + /* Loop to read or write each allocation chunk in mem_buffer */ + + for (i = 0; i < (long) ptr->rows_in_mem; i += ptr->rowsperchunk) { + + /* One chunk, but check for short chunk at end of buffer */ + + rows = MIN((long) ptr->rowsperchunk, (long) ptr->rows_in_mem - i); + + /* Transfer no more than is currently defined */ + + thisrow = (long) ptr->cur_start_row + i; + + rows = MIN(rows, (long) ptr->first_undef_row - thisrow); + + /* Transfer no more than fits in file */ + + rows = MIN(rows, (long) ptr->rows_in_array - thisrow); + + if (rows <= 0) /* this chunk might be past end of file! */ + + break; + + byte_count = rows * bytesperrow; + + if (writing) + + (*ptr->b_s_info.write_backing_store) (cinfo, & ptr->b_s_info, + + (void FAR *) ptr->mem_buffer[i], + + file_offset, byte_count); + + else + + (*ptr->b_s_info.read_backing_store) (cinfo, & ptr->b_s_info, + + (void FAR *) ptr->mem_buffer[i], + + file_offset, byte_count); + + file_offset += byte_count; + + } + +} + + + + + +METHODDEF JSAMPARRAY + +access_virt_sarray (j_common_ptr cinfo, jvirt_sarray_ptr ptr, + + JDIMENSION start_row, JDIMENSION num_rows, + + boolean writable) + +/* Access the part of a virtual sample array starting at start_row */ + +/* and extending for num_rows rows. writable is true if */ + +/* caller intends to modify the accessed area. */ + +{ + + JDIMENSION end_row = start_row + num_rows; + + JDIMENSION undef_row; + + + + /* debugging check */ + + if (end_row > ptr->rows_in_array || num_rows > ptr->maxaccess || + + ptr->mem_buffer == NULL) + + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + + + /* Make the desired part of the virtual array accessible */ + + if (start_row < ptr->cur_start_row || + + end_row > ptr->cur_start_row+ptr->rows_in_mem) { + + if (! ptr->b_s_open) + + ERREXIT(cinfo, JERR_VIRTUAL_BUG); + + /* Flush old buffer contents if necessary */ + + if (ptr->dirty) { + + do_sarray_io(cinfo, ptr, TRUE); + + ptr->dirty = FALSE; + + } + + /* Decide what part of virtual array to access. + + * Algorithm: if target address > current window, assume forward scan, + + * load starting at target address. If target address < current window, + + * assume backward scan, load so that target area is top of window. + + * Note that when switching from forward write to forward read, will have + + * start_row = 0, so the limiting case applies and we load from 0 anyway. + + */ + + if (start_row > ptr->cur_start_row) { + + ptr->cur_start_row = start_row; + + } else { + + /* use long arithmetic here to avoid overflow & unsigned problems */ + + long ltemp; + + + + ltemp = (long) end_row - (long) ptr->rows_in_mem; + + if (ltemp < 0) + + ltemp = 0; /* don't fall off front end of file */ + + ptr->cur_start_row = (JDIMENSION) ltemp; + + } + + /* Read in the selected part of the array. + + * During the initial write pass, we will do no actual read + + * because the selected part is all undefined. + + */ + + do_sarray_io(cinfo, ptr, FALSE); + + } + + /* Ensure the accessed part of the array is defined; prezero if needed. + + * To improve locality of access, we only prezero the part of the array + + * that the caller is about to access, not the entire in-memory array. + + */ + + if (ptr->first_undef_row < end_row) { + + if (ptr->first_undef_row < start_row) { + + if (writable) /* writer skipped over a section of array */ + + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + undef_row = start_row; /* but reader is allowed to read ahead */ + + } else { + + undef_row = ptr->first_undef_row; + + } + + if (writable) + + ptr->first_undef_row = end_row; + + if (ptr->pre_zero) { + + size_t bytesperrow = (size_t) ptr->samplesperrow * SIZEOF(JSAMPLE); + + undef_row -= ptr->cur_start_row; /* make indexes relative to buffer */ + + end_row -= ptr->cur_start_row; + + while (undef_row < end_row) { + + jzero_far((void FAR *) ptr->mem_buffer[undef_row], bytesperrow); + + undef_row++; + + } + + } else { + + if (! writable) /* reader looking at undefined data */ + + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + } + + } + + /* Flag the buffer dirty if caller will write in it */ + + if (writable) + + ptr->dirty = TRUE; + + /* Return address of proper part of the buffer */ + + return ptr->mem_buffer + (start_row - ptr->cur_start_row); + +} + + + + + +METHODDEF JBLOCKARRAY + +access_virt_barray (j_common_ptr cinfo, jvirt_barray_ptr ptr, + + JDIMENSION start_row, JDIMENSION num_rows, + + boolean writable) + +/* Access the part of a virtual block array starting at start_row */ + +/* and extending for num_rows rows. writable is true if */ + +/* caller intends to modify the accessed area. */ + +{ + + JDIMENSION end_row = start_row + num_rows; + + JDIMENSION undef_row; + + + + /* debugging check */ + + if (end_row > ptr->rows_in_array || num_rows > ptr->maxaccess || + + ptr->mem_buffer == NULL) + + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + + + /* Make the desired part of the virtual array accessible */ + + if (start_row < ptr->cur_start_row || + + end_row > ptr->cur_start_row+ptr->rows_in_mem) { + + if (! ptr->b_s_open) + + ERREXIT(cinfo, JERR_VIRTUAL_BUG); + + /* Flush old buffer contents if necessary */ + + if (ptr->dirty) { + + do_barray_io(cinfo, ptr, TRUE); + + ptr->dirty = FALSE; + + } + + /* Decide what part of virtual array to access. + + * Algorithm: if target address > current window, assume forward scan, + + * load starting at target address. If target address < current window, + + * assume backward scan, load so that target area is top of window. + + * Note that when switching from forward write to forward read, will have + + * start_row = 0, so the limiting case applies and we load from 0 anyway. + + */ + + if (start_row > ptr->cur_start_row) { + + ptr->cur_start_row = start_row; + + } else { + + /* use long arithmetic here to avoid overflow & unsigned problems */ + + long ltemp; + + + + ltemp = (long) end_row - (long) ptr->rows_in_mem; + + if (ltemp < 0) + + ltemp = 0; /* don't fall off front end of file */ + + ptr->cur_start_row = (JDIMENSION) ltemp; + + } + + /* Read in the selected part of the array. + + * During the initial write pass, we will do no actual read + + * because the selected part is all undefined. + + */ + + do_barray_io(cinfo, ptr, FALSE); + + } + + /* Ensure the accessed part of the array is defined; prezero if needed. + + * To improve locality of access, we only prezero the part of the array + + * that the caller is about to access, not the entire in-memory array. + + */ + + if (ptr->first_undef_row < end_row) { + + if (ptr->first_undef_row < start_row) { + + if (writable) /* writer skipped over a section of array */ + + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + undef_row = start_row; /* but reader is allowed to read ahead */ + + } else { + + undef_row = ptr->first_undef_row; + + } + + if (writable) + + ptr->first_undef_row = end_row; + + if (ptr->pre_zero) { + + size_t bytesperrow = (size_t) ptr->blocksperrow * SIZEOF(JBLOCK); + + undef_row -= ptr->cur_start_row; /* make indexes relative to buffer */ + + end_row -= ptr->cur_start_row; + + while (undef_row < end_row) { + + jzero_far((void FAR *) ptr->mem_buffer[undef_row], bytesperrow); + + undef_row++; + + } + + } else { + + if (! writable) /* reader looking at undefined data */ + + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + } + + } + + /* Flag the buffer dirty if caller will write in it */ + + if (writable) + + ptr->dirty = TRUE; + + /* Return address of proper part of the buffer */ + + return ptr->mem_buffer + (start_row - ptr->cur_start_row); + +} + + + + + +/* + + * Release all objects belonging to a specified pool. + + */ + + + +METHODDEF void + +free_pool (j_common_ptr cinfo, int pool_id) + +{ + + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + + small_pool_ptr shdr_ptr; + + large_pool_ptr lhdr_ptr; + + size_t space_freed; + + + + if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) + + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + + +#ifdef MEM_STATS + + if (cinfo->err->trace_level > 1) + + print_mem_stats(cinfo, pool_id); /* print pool's memory usage statistics */ + +#endif + + + + /* If freeing IMAGE pool, close any virtual arrays first */ + + if (pool_id == JPOOL_IMAGE) { + + jvirt_sarray_ptr sptr; + + jvirt_barray_ptr bptr; + + + + for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { + + if (sptr->b_s_open) { /* there may be no backing store */ + + sptr->b_s_open = FALSE; /* prevent recursive close if error */ + + (*sptr->b_s_info.close_backing_store) (cinfo, & sptr->b_s_info); + + } + + } + + mem->virt_sarray_list = NULL; + + for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { + + if (bptr->b_s_open) { /* there may be no backing store */ + + bptr->b_s_open = FALSE; /* prevent recursive close if error */ + + (*bptr->b_s_info.close_backing_store) (cinfo, & bptr->b_s_info); + + } + + } + + mem->virt_barray_list = NULL; + + } + + + + /* Release large objects */ + + lhdr_ptr = mem->large_list[pool_id]; + + mem->large_list[pool_id] = NULL; + + + + while (lhdr_ptr != NULL) { + + large_pool_ptr next_lhdr_ptr = lhdr_ptr->hdr.next; + + space_freed = lhdr_ptr->hdr.bytes_used + + + lhdr_ptr->hdr.bytes_left + + + SIZEOF(large_pool_hdr); + + jpeg_free_large(cinfo, (void FAR *) lhdr_ptr, space_freed); + + mem->total_space_allocated -= space_freed; + + lhdr_ptr = next_lhdr_ptr; + + } + + + + /* Release small objects */ + + shdr_ptr = mem->small_list[pool_id]; + + mem->small_list[pool_id] = NULL; + + + + while (shdr_ptr != NULL) { + + small_pool_ptr next_shdr_ptr = shdr_ptr->hdr.next; + + space_freed = shdr_ptr->hdr.bytes_used + + + shdr_ptr->hdr.bytes_left + + + SIZEOF(small_pool_hdr); + + jpeg_free_small(cinfo, (void *) shdr_ptr, space_freed); + + mem->total_space_allocated -= space_freed; + + shdr_ptr = next_shdr_ptr; + + } + +} + + + + + +/* + + * Close up shop entirely. + + * Note that this cannot be called unless cinfo->mem is non-NULL. + + */ + + + +METHODDEF void + +self_destruct (j_common_ptr cinfo) + +{ + + int pool; + + + + /* Close all backing store, release all memory. + + * Releasing pools in reverse order might help avoid fragmentation + + * with some (brain-damaged) malloc libraries. + + */ + + for (pool = JPOOL_NUMPOOLS-1; pool >= JPOOL_PERMANENT; pool--) { + + free_pool(cinfo, pool); + + } + + + + /* Release the memory manager control block too. */ + + jpeg_free_small(cinfo, (void *) cinfo->mem, SIZEOF(my_memory_mgr)); + + cinfo->mem = NULL; /* ensures I will be called only once */ + + + + jpeg_mem_term(cinfo); /* system-dependent cleanup */ + +} + + + + + +/* + + * Memory manager initialization. + + * When this is called, only the error manager pointer is valid in cinfo! + + */ + + + +GLOBAL void + +jinit_memory_mgr (j_common_ptr cinfo) + +{ + + my_mem_ptr mem; + + long max_to_use; + + int pool; + + size_t test_mac; + + + + cinfo->mem = NULL; /* for safety if init fails */ + + + + /* Check for configuration errors. + + * SIZEOF(ALIGN_TYPE) should be a power of 2; otherwise, it probably + + * doesn't reflect any real hardware alignment requirement. + + * The test is a little tricky: for X>0, X and X-1 have no one-bits + + * in common if and only if X is a power of 2, ie has only one one-bit. + + * Some compilers may give an "unreachable code" warning here; ignore it. + + */ + + if ((SIZEOF(ALIGN_TYPE) & (SIZEOF(ALIGN_TYPE)-1)) != 0) + + ERREXIT(cinfo, JERR_BAD_ALIGN_TYPE); + + /* MAX_ALLOC_CHUNK must be representable as type size_t, and must be + + * a multiple of SIZEOF(ALIGN_TYPE). + + * Again, an "unreachable code" warning may be ignored here. + + * But a "constant too large" warning means you need to fix MAX_ALLOC_CHUNK. + + */ + + test_mac = (size_t) MAX_ALLOC_CHUNK; + + if ((long) test_mac != MAX_ALLOC_CHUNK || + + (MAX_ALLOC_CHUNK % SIZEOF(ALIGN_TYPE)) != 0) + + ERREXIT(cinfo, JERR_BAD_ALLOC_CHUNK); + + + + max_to_use = jpeg_mem_init(cinfo); /* system-dependent initialization */ + + + + /* Attempt to allocate memory manager's control block */ + + mem = (my_mem_ptr) jpeg_get_small(cinfo, SIZEOF(my_memory_mgr)); + + + + if (mem == NULL) { + + jpeg_mem_term(cinfo); /* system-dependent cleanup */ + + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 0); + + } + + + + /* OK, fill in the method pointers */ + + mem->pub.alloc_small = alloc_small; + + mem->pub.alloc_large = alloc_large; + + mem->pub.alloc_sarray = alloc_sarray; + + mem->pub.alloc_barray = alloc_barray; + + mem->pub.request_virt_sarray = request_virt_sarray; + + mem->pub.request_virt_barray = request_virt_barray; + + mem->pub.realize_virt_arrays = realize_virt_arrays; + + mem->pub.access_virt_sarray = access_virt_sarray; + + mem->pub.access_virt_barray = access_virt_barray; + + mem->pub.free_pool = free_pool; + + mem->pub.self_destruct = self_destruct; + + + + /* Initialize working state */ + + mem->pub.max_memory_to_use = max_to_use; + + + + for (pool = JPOOL_NUMPOOLS-1; pool >= JPOOL_PERMANENT; pool--) { + + mem->small_list[pool] = NULL; + + mem->large_list[pool] = NULL; + + } + + mem->virt_sarray_list = NULL; + + mem->virt_barray_list = NULL; + + + + mem->total_space_allocated = SIZEOF(my_memory_mgr); + + + + /* Declare ourselves open for business */ + + cinfo->mem = & mem->pub; + + + + /* Check for an environment variable JPEGMEM; if found, override the + + * default max_memory setting from jpeg_mem_init. Note that the + + * surrounding application may again override this value. + + * If your system doesn't support getenv(), define NO_GETENV to disable + + * this feature. + + */ + +#ifndef NO_GETENV + + { char * memenv; + + + + if ((memenv = getenv("JPEGMEM")) != NULL) { + + char ch = 'x'; + + + + if (sscanf(memenv, "%ld%c", &max_to_use, &ch) > 0) { + + if (ch == 'm' || ch == 'M') + + max_to_use *= 1000L; + + mem->pub.max_memory_to_use = max_to_use * 1000L; + + } + + } + + } + +#endif + + + +} + diff --git a/utils/roq2/jpeg/jmemnobs.c b/utils/roq2/jpeg/jmemnobs.c new file mode 100644 index 0000000..f0e2cba --- /dev/null +++ b/utils/roq2/jpeg/jmemnobs.c @@ -0,0 +1,218 @@ +/* + + * jmemnobs.c + + * + + * Copyright (C) 1992-1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file provides a really simple implementation of the system- + + * dependent portion of the JPEG memory manager. This implementation + + * assumes that no backing-store files are needed: all required space + + * can be obtained from malloc(). + + * This is very portable in the sense that it'll compile on almost anything, + + * but you'd better have lots of main memory (or virtual memory) if you want + + * to process big images. + + * Note that the max_memory_to_use option is ignored by this implementation. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + +#include "jmemsys.h" /* import the system-dependent declarations */ + + + +#ifndef HAVE_STDLIB_H /* should declare malloc(),free() */ + +extern void * malloc JPP((size_t size)); + +extern void free JPP((void *ptr)); + +#endif + + + + + +/* + + * Memory allocation and freeing are controlled by the regular library + + * routines malloc() and free(). + + */ + + + +GLOBAL void * + +jpeg_get_small (j_common_ptr cinfo, size_t sizeofobject) + +{ + + return (void *) malloc(sizeofobject); + +} + + + +GLOBAL void + +jpeg_free_small (j_common_ptr cinfo, void * object, size_t sizeofobject) + +{ + + free(object); + +} + + + + + +/* + + * "Large" objects are treated the same as "small" ones. + + * NB: although we include FAR keywords in the routine declarations, + + * this file won't actually work in 80x86 small/medium model; at least, + + * you probably won't be able to process useful-size images in only 64KB. + + */ + + + +GLOBAL void FAR * + +jpeg_get_large (j_common_ptr cinfo, size_t sizeofobject) + +{ + + return (void FAR *) malloc(sizeofobject); + +} + + + +GLOBAL void + +jpeg_free_large (j_common_ptr cinfo, void FAR * object, size_t sizeofobject) + +{ + + free(object); + +} + + + + + +/* + + * This routine computes the total memory space available for allocation. + + * Here we always say, "we got all you want bud!" + + */ + + + +GLOBAL long + +jpeg_mem_available (j_common_ptr cinfo, long min_bytes_needed, + + long max_bytes_needed, long already_allocated) + +{ + + return max_bytes_needed; + +} + + + + + +/* + + * Backing store (temporary file) management. + + * Since jpeg_mem_available always promised the moon, + + * this should never be called and we can just error out. + + */ + + + +GLOBAL void + +jpeg_open_backing_store (j_common_ptr cinfo, backing_store_ptr info, + + long total_bytes_needed) + +{ + + ERREXIT(cinfo, JERR_NO_BACKING_STORE); + +} + + + + + +/* + + * These routines take care of any system-dependent initialization and + + * cleanup required. Here, there isn't any. + + */ + + + +GLOBAL long + +jpeg_mem_init (j_common_ptr cinfo) + +{ + + return 0; /* just set max_memory_to_use to 0 */ + +} + + + +GLOBAL void + +jpeg_mem_term (j_common_ptr cinfo) + +{ + + /* no work */ + +} + diff --git a/utils/roq2/jpeg/jmemsys.h b/utils/roq2/jpeg/jmemsys.h new file mode 100644 index 0000000..9c8028b --- /dev/null +++ b/utils/roq2/jpeg/jmemsys.h @@ -0,0 +1,364 @@ +/* + + * jmemsys.h + + * + + * Copyright (C) 1992-1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This include file defines the interface between the system-independent + + * and system-dependent portions of the JPEG memory manager. No other + + * modules need include it. (The system-independent portion is jmemmgr.c; + + * there are several different versions of the system-dependent portion.) + + * + + * This file works as-is for the system-dependent memory managers supplied + + * in the IJG distribution. You may need to modify it if you write a + + * custom memory manager. If system-dependent changes are needed in + + * this file, the best method is to #ifdef them based on a configuration + + * symbol supplied in jconfig.h, as we have done with USE_MSDOS_MEMMGR. + + */ + + + + + +/* Short forms of external names for systems with brain-damaged linkers. */ + + + +#ifdef NEED_SHORT_EXTERNAL_NAMES + +#define jpeg_get_small jGetSmall + +#define jpeg_free_small jFreeSmall + +#define jpeg_get_large jGetLarge + +#define jpeg_free_large jFreeLarge + +#define jpeg_mem_available jMemAvail + +#define jpeg_open_backing_store jOpenBackStore + +#define jpeg_mem_init jMemInit + +#define jpeg_mem_term jMemTerm + +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + + + + +/* + + * These two functions are used to allocate and release small chunks of + + * memory. (Typically the total amount requested through jpeg_get_small is + + * no more than 20K or so; this will be requested in chunks of a few K each.) + + * Behavior should be the same as for the standard library functions malloc + + * and free; in particular, jpeg_get_small must return NULL on failure. + + * On most systems, these ARE malloc and free. jpeg_free_small is passed the + + * size of the object being freed, just in case it's needed. + + * On an 80x86 machine using small-data memory model, these manage near heap. + + */ + + + +EXTERN void * jpeg_get_small JPP((j_common_ptr cinfo, size_t sizeofobject)); + +EXTERN void jpeg_free_small JPP((j_common_ptr cinfo, void * object, + + size_t sizeofobject)); + + + +/* + + * These two functions are used to allocate and release large chunks of + + * memory (up to the total free space designated by jpeg_mem_available). + + * The interface is the same as above, except that on an 80x86 machine, + + * far pointers are used. On most other machines these are identical to + + * the jpeg_get/free_small routines; but we keep them separate anyway, + + * in case a different allocation strategy is desirable for large chunks. + + */ + + + +EXTERN void FAR * jpeg_get_large JPP((j_common_ptr cinfo,size_t sizeofobject)); + +EXTERN void jpeg_free_large JPP((j_common_ptr cinfo, void FAR * object, + + size_t sizeofobject)); + + + +/* + + * The macro MAX_ALLOC_CHUNK designates the maximum number of bytes that may + + * be requested in a single call to jpeg_get_large (and jpeg_get_small for that + + * matter, but that case should never come into play). This macro is needed + + * to model the 64Kb-segment-size limit of far addressing on 80x86 machines. + + * On those machines, we expect that jconfig.h will provide a proper value. + + * On machines with 32-bit flat address spaces, any large constant may be used. + + * + + * NB: jmemmgr.c expects that MAX_ALLOC_CHUNK will be representable as type + + * size_t and will be a multiple of sizeof(align_type). + + */ + + + +#ifndef MAX_ALLOC_CHUNK /* may be overridden in jconfig.h */ + +#define MAX_ALLOC_CHUNK 1000000000L + +#endif + + + +/* + + * This routine computes the total space still available for allocation by + + * jpeg_get_large. If more space than this is needed, backing store will be + + * used. NOTE: any memory already allocated must not be counted. + + * + + * There is a minimum space requirement, corresponding to the minimum + + * feasible buffer sizes; jmemmgr.c will request that much space even if + + * jpeg_mem_available returns zero. The maximum space needed, enough to hold + + * all working storage in memory, is also passed in case it is useful. + + * Finally, the total space already allocated is passed. If no better + + * method is available, cinfo->mem->max_memory_to_use - already_allocated + + * is often a suitable calculation. + + * + + * It is OK for jpeg_mem_available to underestimate the space available + + * (that'll just lead to more backing-store access than is really necessary). + + * However, an overestimate will lead to failure. Hence it's wise to subtract + + * a slop factor from the true available space. 5% should be enough. + + * + + * On machines with lots of virtual memory, any large constant may be returned. + + * Conversely, zero may be returned to always use the minimum amount of memory. + + */ + + + +EXTERN long jpeg_mem_available JPP((j_common_ptr cinfo, + + long min_bytes_needed, + + long max_bytes_needed, + + long already_allocated)); + + + + + +/* + + * This structure holds whatever state is needed to access a single + + * backing-store object. The read/write/close method pointers are called + + * by jmemmgr.c to manipulate the backing-store object; all other fields + + * are private to the system-dependent backing store routines. + + */ + + + +#define TEMP_NAME_LENGTH 64 /* max length of a temporary file's name */ + + + +#ifdef USE_MSDOS_MEMMGR /* DOS-specific junk */ + + + +typedef unsigned short XMSH; /* type of extended-memory handles */ + +typedef unsigned short EMSH; /* type of expanded-memory handles */ + + + +typedef union { + + short file_handle; /* DOS file handle if it's a temp file */ + + XMSH xms_handle; /* handle if it's a chunk of XMS */ + + EMSH ems_handle; /* handle if it's a chunk of EMS */ + +} handle_union; + + + +#endif /* USE_MSDOS_MEMMGR */ + + + +typedef struct backing_store_struct * backing_store_ptr; + + + +typedef struct backing_store_struct { + + /* Methods for reading/writing/closing this backing-store object */ + + JMETHOD(void, read_backing_store, (j_common_ptr cinfo, + + backing_store_ptr info, + + void FAR * buffer_address, + + long file_offset, long byte_count)); + + JMETHOD(void, write_backing_store, (j_common_ptr cinfo, + + backing_store_ptr info, + + void FAR * buffer_address, + + long file_offset, long byte_count)); + + JMETHOD(void, close_backing_store, (j_common_ptr cinfo, + + backing_store_ptr info)); + + + + /* Private fields for system-dependent backing-store management */ + +#ifdef USE_MSDOS_MEMMGR + + /* For the MS-DOS manager (jmemdos.c), we need: */ + + handle_union handle; /* reference to backing-store storage object */ + + char temp_name[TEMP_NAME_LENGTH]; /* name if it's a file */ + +#else + + /* For a typical implementation with temp files, we need: */ + + FILE * temp_file; /* stdio reference to temp file */ + + char temp_name[TEMP_NAME_LENGTH]; /* name of temp file */ + +#endif + +} backing_store_info; + + + +/* + + * Initial opening of a backing-store object. This must fill in the + + * read/write/close pointers in the object. The read/write routines + + * may take an error exit if the specified maximum file size is exceeded. + + * (If jpeg_mem_available always returns a large value, this routine can + + * just take an error exit.) + + */ + + + +EXTERN void jpeg_open_backing_store JPP((j_common_ptr cinfo, + + backing_store_ptr info, + + long total_bytes_needed)); + + + + + +/* + + * These routines take care of any system-dependent initialization and + + * cleanup required. jpeg_mem_init will be called before anything is + + * allocated (and, therefore, nothing in cinfo is of use except the error + + * manager pointer). It should return a suitable default value for + + * max_memory_to_use; this may subsequently be overridden by the surrounding + + * application. (Note that max_memory_to_use is only important if + + * jpeg_mem_available chooses to consult it ... no one else will.) + + * jpeg_mem_term may assume that all requested memory has been freed and that + + * all opened backing-store objects have been closed. + + */ + + + +EXTERN long jpeg_mem_init JPP((j_common_ptr cinfo)); + +EXTERN void jpeg_mem_term JPP((j_common_ptr cinfo)); + diff --git a/utils/roq2/jpeg/jmorecfg.h b/utils/roq2/jpeg/jmorecfg.h new file mode 100644 index 0000000..f9c9feb --- /dev/null +++ b/utils/roq2/jpeg/jmorecfg.h @@ -0,0 +1,688 @@ +/* + + * jmorecfg.h + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains additional configuration options that customize the + + * JPEG software for special applications or support machine-dependent + + * optimizations. Most users will not need to touch this file. + + */ + + + + + +/* + + * Define BITS_IN_JSAMPLE as either + + * 8 for 8-bit sample values (the usual setting) + + * 12 for 12-bit sample values + + * Only 8 and 12 are legal data precisions for lossy JPEG according to the + + * JPEG standard, and the IJG code does not support anything else! + + * We do not support run-time selection of data precision, sorry. + + */ + + + +#define BITS_IN_JSAMPLE 8 /* use 8 or 12 */ + + + + + +/* + + * Maximum number of components (color channels) allowed in JPEG image. + + * To meet the letter of the JPEG spec, set this to 255. However, darn + + * few applications need more than 4 channels (maybe 5 for CMYK + alpha + + * mask). We recommend 10 as a reasonable compromise; use 4 if you are + + * really short on memory. (Each allowed component costs a hundred or so + + * bytes of storage, whether actually used in an image or not.) + + */ + + + +#define MAX_COMPONENTS 10 /* maximum number of image components */ + + + + + +/* + + * Basic data types. + + * You may need to change these if you have a machine with unusual data + + * type sizes; for example, "char" not 8 bits, "short" not 16 bits, + + * or "long" not 32 bits. We don't care whether "int" is 16 or 32 bits, + + * but it had better be at least 16. + + */ + + + +/* Representation of a single sample (pixel element value). + + * We frequently allocate large arrays of these, so it's important to keep + + * them small. But if you have memory to burn and access to char or short + + * arrays is very slow on your hardware, you might want to change these. + + */ + + + +#if BITS_IN_JSAMPLE == 8 + +/* JSAMPLE should be the smallest type that will hold the values 0..255. + + * You can use a signed char by having GETJSAMPLE mask it with 0xFF. + + */ + + + +#ifdef HAVE_UNSIGNED_CHAR + + + +typedef unsigned char JSAMPLE; + +#define GETJSAMPLE(value) ((int) (value)) + + + +#else /* not HAVE_UNSIGNED_CHAR */ + + + +typedef char JSAMPLE; + +#ifdef CHAR_IS_UNSIGNED + +#define GETJSAMPLE(value) ((int) (value)) + +#else + +#define GETJSAMPLE(value) ((int) (value) & 0xFF) + +#endif /* CHAR_IS_UNSIGNED */ + + + +#endif /* HAVE_UNSIGNED_CHAR */ + + + +#define MAXJSAMPLE 255 + +#define CENTERJSAMPLE 128 + + + +#endif /* BITS_IN_JSAMPLE == 8 */ + + + + + +#if BITS_IN_JSAMPLE == 12 + +/* JSAMPLE should be the smallest type that will hold the values 0..4095. + + * On nearly all machines "short" will do nicely. + + */ + + + +typedef short JSAMPLE; + +#define GETJSAMPLE(value) ((int) (value)) + + + +#define MAXJSAMPLE 4095 + +#define CENTERJSAMPLE 2048 + + + +#endif /* BITS_IN_JSAMPLE == 12 */ + + + + + +/* Representation of a DCT frequency coefficient. + + * This should be a signed value of at least 16 bits; "short" is usually OK. + + * Again, we allocate large arrays of these, but you can change to int + + * if you have memory to burn and "short" is really slow. + + */ + + + +typedef short JCOEF; + + + + + +/* Compressed datastreams are represented as arrays of JOCTET. + + * These must be EXACTLY 8 bits wide, at least once they are written to + + * external storage. Note that when using the stdio data source/destination + + * managers, this is also the data type passed to fread/fwrite. + + */ + + + +#ifdef HAVE_UNSIGNED_CHAR + + + +typedef unsigned char JOCTET; + +#define GETJOCTET(value) (value) + + + +#else /* not HAVE_UNSIGNED_CHAR */ + + + +typedef char JOCTET; + +#ifdef CHAR_IS_UNSIGNED + +#define GETJOCTET(value) (value) + +#else + +#define GETJOCTET(value) ((value) & 0xFF) + +#endif /* CHAR_IS_UNSIGNED */ + + + +#endif /* HAVE_UNSIGNED_CHAR */ + + + + + +/* These typedefs are used for various table entries and so forth. + + * They must be at least as wide as specified; but making them too big + + * won't cost a huge amount of memory, so we don't provide special + + * extraction code like we did for JSAMPLE. (In other words, these + + * typedefs live at a different point on the speed/space tradeoff curve.) + + */ + + + +/* UINT8 must hold at least the values 0..255. */ + + + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char UINT8; + +#else /* not HAVE_UNSIGNED_CHAR */ + +#ifdef CHAR_IS_UNSIGNED + +typedef char UINT8; + +#else /* not CHAR_IS_UNSIGNED */ + +typedef short UINT8; + +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + + + +/* UINT16 must hold at least the values 0..65535. */ + + + +#ifdef HAVE_UNSIGNED_SHORT + +typedef unsigned short UINT16; + +#else /* not HAVE_UNSIGNED_SHORT */ + +typedef unsigned int UINT16; + +#endif /* HAVE_UNSIGNED_SHORT */ + + + +/* INT16 must hold at least the values -32768..32767. */ + + + +#ifndef XMD_H /* X11/xmd.h correctly defines INT16 */ + +typedef short INT16; + +#endif + + + +/* INT32 must hold at least signed 32-bit values. */ + + + +#ifndef XMD_H /* X11/xmd.h correctly defines INT32 */ + +typedef long INT32; + +#endif + + + +/* Datatype used for image dimensions. The JPEG standard only supports + + * images up to 64K*64K due to 16-bit fields in SOF markers. Therefore + + * "unsigned int" is sufficient on all machines. However, if you need to + + * handle larger images and you don't mind deviating from the spec, you + + * can change this datatype. + + */ + + + +typedef unsigned int JDIMENSION; + + + +#define JPEG_MAX_DIMENSION 65500L /* a tad under 64K to prevent overflows */ + + + + + +/* These defines are used in all function definitions and extern declarations. + + * You could modify them if you need to change function linkage conventions. + + * Another application is to make all functions global for use with debuggers + + * or code profilers that require it. + + */ + + + +#define METHODDEF static /* a function called through method pointers */ + +#define LOCAL static /* a function used only in its module */ + +#define GLOBAL /* a function referenced thru EXTERNs */ + +#define EXTERN extern /* a reference to a GLOBAL function */ + + + + + +/* Here is the pseudo-keyword for declaring pointers that must be "far" + + * on 80x86 machines. Most of the specialized coding for 80x86 is handled + + * by just saying "FAR *" where such a pointer is needed. In a few places + + * explicit coding is needed; see uses of the NEED_FAR_POINTERS symbol. + + */ + + + +#ifdef NEED_FAR_POINTERS + +#define FAR far + +#else + +#define FAR + +#endif + + + + + +/* + + * On a few systems, type boolean and/or its values FALSE, TRUE may appear + + * in standard header files. Or you may have conflicts with application- + + * specific header files that you want to include together with these files. + + * Defining HAVE_BOOLEAN before including jpeglib.h should make it work. + + */ + + + +#ifndef HAVE_BOOLEAN + +typedef int boolean; + +#endif + +#ifndef FALSE /* in case these macros already exist */ + +#define FALSE 0 /* values of boolean */ + +#endif + +#ifndef TRUE + +#define TRUE 1 + +#endif + + + + + +/* + + * The remaining options affect code selection within the JPEG library, + + * but they don't need to be visible to most applications using the library. + + * To minimize application namespace pollution, the symbols won't be + + * defined unless JPEG_INTERNALS or JPEG_INTERNAL_OPTIONS has been defined. + + */ + + + +#ifdef JPEG_INTERNALS + +#define JPEG_INTERNAL_OPTIONS + +#endif + + + +#ifdef JPEG_INTERNAL_OPTIONS + + + + + +/* + + * These defines indicate whether to include various optional functions. + + * Undefining some of these symbols will produce a smaller but less capable + + * library. Note that you can leave certain source files out of the + + * compilation/linking process if you've #undef'd the corresponding symbols. + + * (You may HAVE to do that if your compiler doesn't like null source files.) + + */ + + + +/* Arithmetic coding is unsupported for legal reasons. Complaints to IBM. */ + + + +/* Capability options common to encoder and decoder: */ + + + +#define DCT_ISLOW_SUPPORTED /* slow but accurate integer algorithm */ + +#define DCT_IFAST_SUPPORTED /* faster, less accurate integer method */ + +#define DCT_FLOAT_SUPPORTED /* floating-point: accurate, fast on fast HW */ + + + +/* Encoder capability options: */ + + + +#undef C_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ + +#define C_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ + +#define C_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ + +#define ENTROPY_OPT_SUPPORTED /* Optimization of entropy coding parms? */ + +/* Note: if you selected 12-bit data precision, it is dangerous to turn off + + * ENTROPY_OPT_SUPPORTED. The standard Huffman tables are only good for 8-bit + + * precision, so jchuff.c normally uses entropy optimization to compute + + * usable tables for higher precision. If you don't want to do optimization, + + * you'll have to supply different default Huffman tables. + + * The exact same statements apply for progressive JPEG: the default tables + + * don't work for progressive mode. (This may get fixed, however.) + + */ + +#define INPUT_SMOOTHING_SUPPORTED /* Input image smoothing option? */ + + + +/* Decoder capability options: */ + + + +#undef D_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ + +#define D_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ + +#define D_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ + +#define BLOCK_SMOOTHING_SUPPORTED /* Block smoothing? (Progressive only) */ + +#define IDCT_SCALING_SUPPORTED /* Output rescaling via IDCT? */ + +#undef UPSAMPLE_SCALING_SUPPORTED /* Output rescaling at upsample stage? */ + +#define UPSAMPLE_MERGING_SUPPORTED /* Fast path for sloppy upsampling? */ + +#define QUANT_1PASS_SUPPORTED /* 1-pass color quantization? */ + +#define QUANT_2PASS_SUPPORTED /* 2-pass color quantization? */ + + + +/* more capability options later, no doubt */ + + + + + +/* + + * Ordering of RGB data in scanlines passed to or from the application. + + * If your application wants to deal with data in the order B,G,R, just + + * change these macros. You can also deal with formats such as R,G,B,X + + * (one extra byte per pixel) by changing RGB_PIXELSIZE. Note that changing + + * the offsets will also change the order in which colormap data is organized. + + * RESTRICTIONS: + + * 1. The sample applications cjpeg,djpeg do NOT support modified RGB formats. + + * 2. These macros only affect RGB<=>YCbCr color conversion, so they are not + + * useful if you are using JPEG color spaces other than YCbCr or grayscale. + + * 3. The color quantizer modules will not behave desirably if RGB_PIXELSIZE + + * is not 3 (they don't understand about dummy color components!). So you + + * can't use color quantization if you change that value. + + */ + + + +#define RGB_RED 0 /* Offset of Red in an RGB scanline element */ + +#define RGB_GREEN 1 /* Offset of Green */ + +#define RGB_BLUE 2 /* Offset of Blue */ + +#define RGB_PIXELSIZE 3 /* JSAMPLEs per RGB scanline element */ + + + + + +/* Definitions for speed-related optimizations. */ + + + + + +/* If your compiler supports inline functions, define INLINE + + * as the inline keyword; otherwise define it as empty. + + */ + + + +#ifndef INLINE + +#ifdef __GNUC__ /* for instance, GNU C knows about inline */ + +#define INLINE __inline__ + +#endif + +#ifndef INLINE + +#define INLINE /* default is to define it as empty */ + +#endif + +#endif + + + + + +/* On some machines (notably 68000 series) "int" is 32 bits, but multiplying + + * two 16-bit shorts is faster than multiplying two ints. Define MULTIPLIER + + * as short on such a machine. MULTIPLIER must be at least 16 bits wide. + + */ + + + +#ifndef MULTIPLIER + +#define MULTIPLIER int /* type for fastest integer multiply */ + +#endif + + + + + +/* FAST_FLOAT should be either float or double, whichever is done faster + + * by your compiler. (Note that this type is only used in the floating point + + * DCT routines, so it only matters if you've defined DCT_FLOAT_SUPPORTED.) + + * Typically, float is faster in ANSI C compilers, while double is faster in + + * pre-ANSI compilers (because they insist on converting to double anyway). + + * The code below therefore chooses float if we have ANSI-style prototypes. + + */ + + + +#ifndef FAST_FLOAT + +#ifdef HAVE_PROTOTYPES + +#define FAST_FLOAT float + +#else + +#define FAST_FLOAT double + +#endif + +#endif + + + +#endif /* JPEG_INTERNAL_OPTIONS */ + diff --git a/utils/roq2/jpeg/jpeg.dsp b/utils/roq2/jpeg/jpeg.dsp new file mode 100644 index 0000000..bac6c69 --- /dev/null +++ b/utils/roq2/jpeg/jpeg.dsp @@ -0,0 +1,388 @@ +# Microsoft Developer Studio Project File - Name="jpeg" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=jpeg - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "jpeg.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "jpeg.mak" CFG="jpeg - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "jpeg - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "jpeg - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName ""$/roq/jpeg", DAEAAAAA" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "jpeg - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir ".\Release" +# PROP BASE Intermediate_Dir ".\Release" +# PROP BASE Target_Dir "." +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir ".\Release" +# PROP Intermediate_Dir ".\Release" +# PROP Target_Dir "." +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD BASE RSC /l 0x409 +# ADD RSC /l 0x409 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "jpeg - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir ".\Debug" +# PROP BASE Intermediate_Dir ".\Debug" +# PROP BASE Target_Dir "." +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir ".\Debug" +# PROP Intermediate_Dir ".\Debug" +# PROP Target_Dir "." +# ADD BASE CPP /nologo /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD BASE RSC /l 0x409 +# ADD RSC /l 0x409 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "jpeg - Win32 Release" +# Name "jpeg - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=cdjpeg.c +# End Source File +# Begin Source File + +SOURCE=jcapimin.c +# End Source File +# Begin Source File + +SOURCE=jcapistd.c +# End Source File +# Begin Source File + +SOURCE=jccoefct.c +# End Source File +# Begin Source File + +SOURCE=jccolor.c +# End Source File +# Begin Source File + +SOURCE=jcdctmgr.c +# End Source File +# Begin Source File + +SOURCE=jchuff.c +# End Source File +# Begin Source File + +SOURCE=jcinit.c +# End Source File +# Begin Source File + +SOURCE=jcmainct.c +# End Source File +# Begin Source File + +SOURCE=jcmarker.c +# End Source File +# Begin Source File + +SOURCE=jcmaster.c +# End Source File +# Begin Source File + +SOURCE=jcomapi.c +# End Source File +# Begin Source File + +SOURCE=jcparam.c +# End Source File +# Begin Source File + +SOURCE=jcphuff.c +# End Source File +# Begin Source File + +SOURCE=jcprepct.c +# End Source File +# Begin Source File + +SOURCE=jcsample.c +# End Source File +# Begin Source File + +SOURCE=jctrans.c +# End Source File +# Begin Source File + +SOURCE=jdapimin.c +# End Source File +# Begin Source File + +SOURCE=jdapistd.c +# End Source File +# Begin Source File + +SOURCE=jdatadst.c +# End Source File +# Begin Source File + +SOURCE=jdatasrc.c +# End Source File +# Begin Source File + +SOURCE=jdcoefct.c +# End Source File +# Begin Source File + +SOURCE=jdcolor.c +# End Source File +# Begin Source File + +SOURCE=jddctmgr.c +# End Source File +# Begin Source File + +SOURCE=jdhuff.c +# End Source File +# Begin Source File + +SOURCE=jdinput.c +# End Source File +# Begin Source File + +SOURCE=jdmainct.c +# End Source File +# Begin Source File + +SOURCE=jdmarker.c +# End Source File +# Begin Source File + +SOURCE=jdmaster.c +# End Source File +# Begin Source File + +SOURCE=jdmerge.c +# End Source File +# Begin Source File + +SOURCE=jdphuff.c +# End Source File +# Begin Source File + +SOURCE=jdpostct.c +# End Source File +# Begin Source File + +SOURCE=jdsample.c +# End Source File +# Begin Source File + +SOURCE=jdtrans.c +# End Source File +# Begin Source File + +SOURCE=jerror.c +# End Source File +# Begin Source File + +SOURCE=jfdctflt.c +# End Source File +# Begin Source File + +SOURCE=jfdctfst.c +# End Source File +# Begin Source File + +SOURCE=jfdctint.c +# End Source File +# Begin Source File + +SOURCE=jidctflt.c +# End Source File +# Begin Source File + +SOURCE=jidctfst.c +# End Source File +# Begin Source File + +SOURCE=jidctint.c +# End Source File +# Begin Source File + +SOURCE=jidctred.c +# End Source File +# Begin Source File + +SOURCE=jmemmgr.c +# End Source File +# Begin Source File + +SOURCE=jmemnobs.c +# End Source File +# Begin Source File + +SOURCE=jpegtran.c +# End Source File +# Begin Source File + +SOURCE=jquant1.c +# End Source File +# Begin Source File + +SOURCE=jquant2.c +# End Source File +# Begin Source File + +SOURCE=jutils.c +# End Source File +# Begin Source File + +SOURCE=rdbmp.c +# End Source File +# Begin Source File + +SOURCE=rdcolmap.c +# End Source File +# Begin Source File + +SOURCE=rdgif.c +# End Source File +# Begin Source File + +SOURCE=rdppm.c +# End Source File +# Begin Source File + +SOURCE=rdrle.c +# End Source File +# Begin Source File + +SOURCE=rdswitch.c +# End Source File +# Begin Source File + +SOURCE=rdtarga.c +# End Source File +# Begin Source File + +SOURCE=wrbmp.c +# End Source File +# Begin Source File + +SOURCE=wrgif.c +# End Source File +# Begin Source File + +SOURCE=wrppm.c +# End Source File +# Begin Source File + +SOURCE=wrrle.c +# End Source File +# Begin Source File + +SOURCE=wrtarga.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\cderror.h +# End Source File +# Begin Source File + +SOURCE=.\cdjpeg.h +# End Source File +# Begin Source File + +SOURCE=.\jchuff.h +# End Source File +# Begin Source File + +SOURCE=.\jconfig.h +# End Source File +# Begin Source File + +SOURCE=.\jdct.h +# End Source File +# Begin Source File + +SOURCE=.\jdhuff.h +# End Source File +# Begin Source File + +SOURCE=.\jerror.h +# End Source File +# Begin Source File + +SOURCE=.\jinclude.h +# End Source File +# Begin Source File + +SOURCE=.\jmemsys.h +# End Source File +# Begin Source File + +SOURCE=.\jmorecfg.h +# End Source File +# Begin Source File + +SOURCE=.\jpegint.h +# End Source File +# Begin Source File + +SOURCE=.\jpeglib.h +# End Source File +# Begin Source File + +SOURCE=.\jversion.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/utils/roq2/jpeg/jpegint.h b/utils/roq2/jpeg/jpegint.h new file mode 100644 index 0000000..9b02525 --- /dev/null +++ b/utils/roq2/jpeg/jpegint.h @@ -0,0 +1,776 @@ +/* + + * jpegint.h + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file provides common declarations for the various JPEG modules. + + * These declarations are considered internal to the JPEG library; most + + * applications using the library shouldn't need to include this file. + + */ + + + + + +/* Declarations for both compression & decompression */ + + + +typedef enum { /* Operating modes for buffer controllers */ + + JBUF_PASS_THRU, /* Plain stripwise operation */ + + /* Remaining modes require a full-image buffer to have been created */ + + JBUF_SAVE_SOURCE, /* Run source subobject only, save output */ + + JBUF_CRANK_DEST, /* Run dest subobject only, using saved data */ + + JBUF_SAVE_AND_PASS /* Run both subobjects, save output */ + +} J_BUF_MODE; + + + +/* Values of global_state field (jdapi.c has some dependencies on ordering!) */ + +#define CSTATE_START 100 /* after create_compress */ + +#define CSTATE_SCANNING 101 /* start_compress done, write_scanlines OK */ + +#define CSTATE_RAW_OK 102 /* start_compress done, write_raw_data OK */ + +#define CSTATE_WRCOEFS 103 /* jpeg_write_coefficients done */ + +#define DSTATE_START 200 /* after create_decompress */ + +#define DSTATE_INHEADER 201 /* reading header markers, no SOS yet */ + +#define DSTATE_READY 202 /* found SOS, ready for start_decompress */ + +#define DSTATE_PRELOAD 203 /* reading multiscan file in start_decompress*/ + +#define DSTATE_PRESCAN 204 /* performing dummy pass for 2-pass quant */ + +#define DSTATE_SCANNING 205 /* start_decompress done, read_scanlines OK */ + +#define DSTATE_RAW_OK 206 /* start_decompress done, read_raw_data OK */ + +#define DSTATE_BUFIMAGE 207 /* expecting jpeg_start_output */ + +#define DSTATE_BUFPOST 208 /* looking for SOS/EOI in jpeg_finish_output */ + +#define DSTATE_RDCOEFS 209 /* reading file in jpeg_read_coefficients */ + +#define DSTATE_STOPPING 210 /* looking for EOI in jpeg_finish_decompress */ + + + + + +/* Declarations for compression modules */ + + + +/* Master control module */ + +struct jpeg_comp_master { + + JMETHOD(void, prepare_for_pass, (j_compress_ptr cinfo)); + + JMETHOD(void, pass_startup, (j_compress_ptr cinfo)); + + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); + + + + /* State variables made visible to other modules */ + + boolean call_pass_startup; /* True if pass_startup must be called */ + + boolean is_last_pass; /* True during last pass */ + +}; + + + +/* Main buffer control (downsampled-data buffer) */ + +struct jpeg_c_main_controller { + + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + + JMETHOD(void, process_data, (j_compress_ptr cinfo, + + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + + JDIMENSION in_rows_avail)); + +}; + + + +/* Compression preprocessing (downsampling input buffer control) */ + +struct jpeg_c_prep_controller { + + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + + JMETHOD(void, pre_process_data, (j_compress_ptr cinfo, + + JSAMPARRAY input_buf, + + JDIMENSION *in_row_ctr, + + JDIMENSION in_rows_avail, + + JSAMPIMAGE output_buf, + + JDIMENSION *out_row_group_ctr, + + JDIMENSION out_row_groups_avail)); + +}; + + + +/* Coefficient buffer control */ + +struct jpeg_c_coef_controller { + + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + + JMETHOD(boolean, compress_data, (j_compress_ptr cinfo, + + JSAMPIMAGE input_buf)); + +}; + + + +/* Colorspace conversion */ + +struct jpeg_color_converter { + + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + + JMETHOD(void, color_convert, (j_compress_ptr cinfo, + + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + + JDIMENSION output_row, int num_rows)); + +}; + + + +/* Downsampling */ + +struct jpeg_downsampler { + + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + + JMETHOD(void, downsample, (j_compress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION in_row_index, + + JSAMPIMAGE output_buf, + + JDIMENSION out_row_group_index)); + + + + boolean need_context_rows; /* TRUE if need rows above & below */ + +}; + + + +/* Forward DCT (also controls coefficient quantization) */ + +struct jpeg_forward_dct { + + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + + /* perhaps this should be an array??? */ + + JMETHOD(void, forward_DCT, (j_compress_ptr cinfo, + + jpeg_component_info * compptr, + + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + + JDIMENSION start_row, JDIMENSION start_col, + + JDIMENSION num_blocks)); + +}; + + + +/* Entropy encoding */ + +struct jpeg_entropy_encoder { + + JMETHOD(void, start_pass, (j_compress_ptr cinfo, boolean gather_statistics)); + + JMETHOD(boolean, encode_mcu, (j_compress_ptr cinfo, JBLOCKROW *MCU_data)); + + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); + +}; + + + +/* Marker writing */ + +struct jpeg_marker_writer { + + /* write_any_marker is exported for use by applications */ + + /* Probably only COM and APPn markers should be written */ + + JMETHOD(void, write_any_marker, (j_compress_ptr cinfo, int marker, + + const JOCTET *dataptr, unsigned int datalen)); + + JMETHOD(void, write_file_header, (j_compress_ptr cinfo)); + + JMETHOD(void, write_frame_header, (j_compress_ptr cinfo)); + + JMETHOD(void, write_scan_header, (j_compress_ptr cinfo)); + + JMETHOD(void, write_file_trailer, (j_compress_ptr cinfo)); + + JMETHOD(void, write_tables_only, (j_compress_ptr cinfo)); + +}; + + + + + +/* Declarations for decompression modules */ + + + +/* Master control module */ + +struct jpeg_decomp_master { + + JMETHOD(void, prepare_for_output_pass, (j_decompress_ptr cinfo)); + + JMETHOD(void, finish_output_pass, (j_decompress_ptr cinfo)); + + + + /* State variables made visible to other modules */ + + boolean is_dummy_pass; /* True during 1st pass for 2-pass quant */ + +}; + + + +/* Input control module */ + +struct jpeg_input_controller { + + JMETHOD(int, consume_input, (j_decompress_ptr cinfo)); + + JMETHOD(void, reset_input_controller, (j_decompress_ptr cinfo)); + + JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); + + JMETHOD(void, finish_input_pass, (j_decompress_ptr cinfo)); + + + + /* State variables made visible to other modules */ + + boolean has_multiple_scans; /* True if file has multiple scans */ + + boolean eoi_reached; /* True when EOI has been consumed */ + +}; + + + +/* Main buffer control (downsampled-data buffer) */ + +struct jpeg_d_main_controller { + + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); + + JMETHOD(void, process_data, (j_decompress_ptr cinfo, + + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail)); + +}; + + + +/* Coefficient buffer control */ + +struct jpeg_d_coef_controller { + + JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); + + JMETHOD(int, consume_data, (j_decompress_ptr cinfo)); + + JMETHOD(void, start_output_pass, (j_decompress_ptr cinfo)); + + JMETHOD(int, decompress_data, (j_decompress_ptr cinfo, + + JSAMPIMAGE output_buf)); + + /* Pointer to array of coefficient virtual arrays, or NULL if none */ + + jvirt_barray_ptr *coef_arrays; + +}; + + + +/* Decompression postprocessing (color quantization buffer control) */ + +struct jpeg_d_post_controller { + + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); + + JMETHOD(void, post_process_data, (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, + + JDIMENSION *in_row_group_ctr, + + JDIMENSION in_row_groups_avail, + + JSAMPARRAY output_buf, + + JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail)); + +}; + + + +/* Marker reading & parsing */ + +struct jpeg_marker_reader { + + JMETHOD(void, reset_marker_reader, (j_decompress_ptr cinfo)); + + /* Read markers until SOS or EOI. + + * Returns same codes as are defined for jpeg_consume_input: + + * JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + + */ + + JMETHOD(int, read_markers, (j_decompress_ptr cinfo)); + + /* Read a restart marker --- exported for use by entropy decoder only */ + + jpeg_marker_parser_method read_restart_marker; + + /* Application-overridable marker processing methods */ + + jpeg_marker_parser_method process_COM; + + jpeg_marker_parser_method process_APPn[16]; + + + + /* State of marker reader --- nominally internal, but applications + + * supplying COM or APPn handlers might like to know the state. + + */ + + boolean saw_SOI; /* found SOI? */ + + boolean saw_SOF; /* found SOF? */ + + int next_restart_num; /* next restart number expected (0-7) */ + + unsigned int discarded_bytes; /* # of bytes skipped looking for a marker */ + +}; + + + +/* Entropy decoding */ + +struct jpeg_entropy_decoder { + + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + + JMETHOD(boolean, decode_mcu, (j_decompress_ptr cinfo, + + JBLOCKROW *MCU_data)); + +}; + + + +/* Inverse DCT (also performs dequantization) */ + +typedef JMETHOD(void, inverse_DCT_method_ptr, + + (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JCOEFPTR coef_block, + + JSAMPARRAY output_buf, JDIMENSION output_col)); + + + +struct jpeg_inverse_dct { + + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + + /* It is useful to allow each component to have a separate IDCT method. */ + + inverse_DCT_method_ptr inverse_DCT[MAX_COMPONENTS]; + +}; + + + +/* Upsampling (note that upsampler must also call color converter) */ + +struct jpeg_upsampler { + + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + + JMETHOD(void, upsample, (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, + + JDIMENSION *in_row_group_ctr, + + JDIMENSION in_row_groups_avail, + + JSAMPARRAY output_buf, + + JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail)); + + + + boolean need_context_rows; /* TRUE if need rows above & below */ + +}; + + + +/* Colorspace conversion */ + +struct jpeg_color_deconverter { + + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + + JMETHOD(void, color_convert, (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION input_row, + + JSAMPARRAY output_buf, int num_rows)); + +}; + + + +/* Color quantization or color precision reduction */ + +struct jpeg_color_quantizer { + + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, boolean is_pre_scan)); + + JMETHOD(void, color_quantize, (j_decompress_ptr cinfo, + + JSAMPARRAY input_buf, JSAMPARRAY output_buf, + + int num_rows)); + + JMETHOD(void, finish_pass, (j_decompress_ptr cinfo)); + + JMETHOD(void, new_color_map, (j_decompress_ptr cinfo)); + +}; + + + + + +/* Miscellaneous useful macros */ + + + +#undef MAX + +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +#undef MIN + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + + + + + +/* We assume that right shift corresponds to signed division by 2 with + + * rounding towards minus infinity. This is correct for typical "arithmetic + + * shift" instructions that shift in copies of the sign bit. But some + + * C compilers implement >> with an unsigned shift. For these machines you + + * must define RIGHT_SHIFT_IS_UNSIGNED. + + * RIGHT_SHIFT provides a proper signed right shift of an INT32 quantity. + + * It is only applied with constant shift counts. SHIFT_TEMPS must be + + * included in the variables of any routine using RIGHT_SHIFT. + + */ + + + +#ifdef RIGHT_SHIFT_IS_UNSIGNED + +#define SHIFT_TEMPS INT32 shift_temp; + +#define RIGHT_SHIFT(x,shft) \ + + ((shift_temp = (x)) < 0 ? \ + + (shift_temp >> (shft)) | ((~((INT32) 0)) << (32-(shft))) : \ + + (shift_temp >> (shft))) + +#else + +#define SHIFT_TEMPS + +#define RIGHT_SHIFT(x,shft) ((x) >> (shft)) + +#endif + + + + + +/* Short forms of external names for systems with brain-damaged linkers. */ + + + +#ifdef NEED_SHORT_EXTERNAL_NAMES + +#define jinit_compress_master jICompress + +#define jinit_c_master_control jICMaster + +#define jinit_c_main_controller jICMainC + +#define jinit_c_prep_controller jICPrepC + +#define jinit_c_coef_controller jICCoefC + +#define jinit_color_converter jICColor + +#define jinit_downsampler jIDownsampler + +#define jinit_forward_dct jIFDCT + +#define jinit_huff_encoder jIHEncoder + +#define jinit_phuff_encoder jIPHEncoder + +#define jinit_marker_writer jIMWriter + +#define jinit_master_decompress jIDMaster + +#define jinit_d_main_controller jIDMainC + +#define jinit_d_coef_controller jIDCoefC + +#define jinit_d_post_controller jIDPostC + +#define jinit_input_controller jIInCtlr + +#define jinit_marker_reader jIMReader + +#define jinit_huff_decoder jIHDecoder + +#define jinit_phuff_decoder jIPHDecoder + +#define jinit_inverse_dct jIIDCT + +#define jinit_upsampler jIUpsampler + +#define jinit_color_deconverter jIDColor + +#define jinit_1pass_quantizer jI1Quant + +#define jinit_2pass_quantizer jI2Quant + +#define jinit_merged_upsampler jIMUpsampler + +#define jinit_memory_mgr jIMemMgr + +#define jdiv_round_up jDivRound + +#define jround_up jRound + +#define jcopy_sample_rows jCopySamples + +#define jcopy_block_row jCopyBlocks + +#define jzero_far jZeroFar + +#define jpeg_zigzag_order jZIGTable + +#define jpeg_natural_order jZAGTable + +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + + + + +/* Compression module initialization routines */ + +EXTERN void jinit_compress_master JPP((j_compress_ptr cinfo)); + +EXTERN void jinit_c_master_control JPP((j_compress_ptr cinfo, + + boolean transcode_only)); + +EXTERN void jinit_c_main_controller JPP((j_compress_ptr cinfo, + + boolean need_full_buffer)); + +EXTERN void jinit_c_prep_controller JPP((j_compress_ptr cinfo, + + boolean need_full_buffer)); + +EXTERN void jinit_c_coef_controller JPP((j_compress_ptr cinfo, + + boolean need_full_buffer)); + +EXTERN void jinit_color_converter JPP((j_compress_ptr cinfo)); + +EXTERN void jinit_downsampler JPP((j_compress_ptr cinfo)); + +EXTERN void jinit_forward_dct JPP((j_compress_ptr cinfo)); + +EXTERN void jinit_huff_encoder JPP((j_compress_ptr cinfo)); + +EXTERN void jinit_phuff_encoder JPP((j_compress_ptr cinfo)); + +EXTERN void jinit_marker_writer JPP((j_compress_ptr cinfo)); + +/* Decompression module initialization routines */ + +EXTERN void jinit_master_decompress JPP((j_decompress_ptr cinfo)); + +EXTERN void jinit_d_main_controller JPP((j_decompress_ptr cinfo, + + boolean need_full_buffer)); + +EXTERN void jinit_d_coef_controller JPP((j_decompress_ptr cinfo, + + boolean need_full_buffer)); + +EXTERN void jinit_d_post_controller JPP((j_decompress_ptr cinfo, + + boolean need_full_buffer)); + +EXTERN void jinit_input_controller JPP((j_decompress_ptr cinfo)); + +EXTERN void jinit_marker_reader JPP((j_decompress_ptr cinfo)); + +EXTERN void jinit_huff_decoder JPP((j_decompress_ptr cinfo)); + +EXTERN void jinit_phuff_decoder JPP((j_decompress_ptr cinfo)); + +EXTERN void jinit_inverse_dct JPP((j_decompress_ptr cinfo)); + +EXTERN void jinit_upsampler JPP((j_decompress_ptr cinfo)); + +EXTERN void jinit_color_deconverter JPP((j_decompress_ptr cinfo)); + +EXTERN void jinit_1pass_quantizer JPP((j_decompress_ptr cinfo)); + +EXTERN void jinit_2pass_quantizer JPP((j_decompress_ptr cinfo)); + +EXTERN void jinit_merged_upsampler JPP((j_decompress_ptr cinfo)); + +/* Memory manager initialization */ + +EXTERN void jinit_memory_mgr JPP((j_common_ptr cinfo)); + + + +/* Utility routines in jutils.c */ + +EXTERN long jdiv_round_up JPP((long a, long b)); + +EXTERN long jround_up JPP((long a, long b)); + +EXTERN void jcopy_sample_rows JPP((JSAMPARRAY input_array, int source_row, + + JSAMPARRAY output_array, int dest_row, + + int num_rows, JDIMENSION num_cols)); + +EXTERN void jcopy_block_row JPP((JBLOCKROW input_row, JBLOCKROW output_row, + + JDIMENSION num_blocks)); + +EXTERN void jzero_far JPP((void FAR * target, size_t bytestozero)); + +/* Constant tables in jutils.c */ + +extern const int jpeg_zigzag_order[]; /* natural coef order to zigzag order */ + +extern const int jpeg_natural_order[]; /* zigzag coef order to natural order */ + + + +/* Suppress undefined-structure complaints if necessary. */ + + + +#ifdef INCOMPLETE_TYPES_BROKEN + +#ifndef AM_MEMORY_MANAGER /* only jmemmgr.c defines these */ + +struct jvirt_sarray_control { long dummy; }; + +struct jvirt_barray_control { long dummy; }; + +#endif + +#endif /* INCOMPLETE_TYPES_BROKEN */ + diff --git a/utils/roq2/jpeg/jpeglib.h b/utils/roq2/jpeg/jpeglib.h new file mode 100644 index 0000000..46e4771 --- /dev/null +++ b/utils/roq2/jpeg/jpeglib.h @@ -0,0 +1,2100 @@ +/* + + * jpeglib.h + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file defines the application interface for the JPEG library. + + * Most applications using the library need only include this file, + + * and perhaps jerror.h if they want to know the exact error codes. + + */ + + + +#ifndef JPEGLIB_H + +#define JPEGLIB_H + + + +/* + + * First we include the configuration files that record how this + + * installation of the JPEG library is set up. jconfig.h can be + + * generated automatically for many systems. jmorecfg.h contains + + * manual configuration options that most people need not worry about. + + */ + + + +#ifndef JCONFIG_INCLUDED /* in case jinclude.h already did */ + +#include "jconfig.h" /* widely used configuration options */ + +#endif + +#include "jmorecfg.h" /* seldom changed options */ + + + + + +/* Version ID for the JPEG library. + + * Might be useful for tests like "#if JPEG_LIB_VERSION >= 60". + + */ + + + +#define JPEG_LIB_VERSION 60 /* Version 6 */ + + + + + +/* Various constants determining the sizes of things. + + * All of these are specified by the JPEG standard, so don't change them + + * if you want to be compatible. + + */ + + + +#define DCTSIZE 8 /* The basic DCT block is 8x8 samples */ + +#define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ + +#define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ + +#define NUM_HUFF_TBLS 4 /* Huffman tables are numbered 0..3 */ + +#define NUM_ARITH_TBLS 16 /* Arith-coding tables are numbered 0..15 */ + +#define MAX_COMPS_IN_SCAN 4 /* JPEG limit on # of components in one scan */ + +#define MAX_SAMP_FACTOR 4 /* JPEG limit on sampling factors */ + +/* Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; + + * the PostScript DCT filter can emit files with many more than 10 blocks/MCU. + + * If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU + + * to handle it. We even let you do this from the jconfig.h file. However, + + * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe + + * sometimes emits noncompliant files doesn't mean you should too. + + */ + +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on blocks per MCU */ + +#ifndef D_MAX_BLOCKS_IN_MCU + +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on blocks per MCU */ + +#endif + + + + + +/* This macro is used to declare a "method", that is, a function pointer. + + * We want to supply prototype parameters if the compiler can cope. + + * Note that the arglist parameter must be parenthesized! + + */ + + + +#ifdef HAVE_PROTOTYPES + +#define JMETHOD(type,methodname,arglist) type (*methodname) arglist + +#else + +#define JMETHOD(type,methodname,arglist) type (*methodname) () + +#endif + + + + + +/* Data structures for images (arrays of samples and of DCT coefficients). + + * On 80x86 machines, the image arrays are too big for near pointers, + + * but the pointer arrays can fit in near memory. + + */ + + + +typedef JSAMPLE FAR *JSAMPROW; /* ptr to one image row of pixel samples. */ + +typedef JSAMPROW *JSAMPARRAY; /* ptr to some rows (a 2-D sample array) */ + +typedef JSAMPARRAY *JSAMPIMAGE; /* a 3-D sample array: top index is color */ + + + +typedef JCOEF JBLOCK[DCTSIZE2]; /* one block of coefficients */ + +typedef JBLOCK FAR *JBLOCKROW; /* pointer to one row of coefficient blocks */ + +typedef JBLOCKROW *JBLOCKARRAY; /* a 2-D array of coefficient blocks */ + +typedef JBLOCKARRAY *JBLOCKIMAGE; /* a 3-D array of coefficient blocks */ + + + +typedef JCOEF FAR *JCOEFPTR; /* useful in a couple of places */ + + + + + +/* Types for JPEG compression parameters and working tables. */ + + + + + +/* DCT coefficient quantization tables. */ + + + +typedef struct { + + /* This field directly represents the contents of a JPEG DQT marker. + + * Note: the values are always given in zigzag order. + + */ + + UINT16 quantval[DCTSIZE2]; /* quantization step for each coefficient */ + + /* This field is used only during compression. It's initialized FALSE when + + * the table is created, and set TRUE when it's been output to the file. + + * You could suppress output of a table by setting this to TRUE. + + * (See jpeg_suppress_tables for an example.) + + */ + + boolean sent_table; /* TRUE when table has been output */ + +} JQUANT_TBL; + + + + + +/* Huffman coding tables. */ + + + +typedef struct { + + /* These two fields directly represent the contents of a JPEG DHT marker */ + + UINT8 bits[17]; /* bits[k] = # of symbols with codes of */ + + /* length k bits; bits[0] is unused */ + + UINT8 huffval[256]; /* The symbols, in order of incr code length */ + + /* This field is used only during compression. It's initialized FALSE when + + * the table is created, and set TRUE when it's been output to the file. + + * You could suppress output of a table by setting this to TRUE. + + * (See jpeg_suppress_tables for an example.) + + */ + + boolean sent_table; /* TRUE when table has been output */ + +} JHUFF_TBL; + + + + + +/* Basic info about one component (color channel). */ + + + +typedef struct { + + /* These values are fixed over the whole image. */ + + /* For compression, they must be supplied by parameter setup; */ + + /* for decompression, they are read from the SOF marker. */ + + int component_id; /* identifier for this component (0..255) */ + + int component_index; /* its index in SOF or cinfo->comp_info[] */ + + int h_samp_factor; /* horizontal sampling factor (1..4) */ + + int v_samp_factor; /* vertical sampling factor (1..4) */ + + int quant_tbl_no; /* quantization table selector (0..3) */ + + /* These values may vary between scans. */ + + /* For compression, they must be supplied by parameter setup; */ + + /* for decompression, they are read from the SOS marker. */ + + /* The decompressor output side may not use these variables. */ + + int dc_tbl_no; /* DC entropy table selector (0..3) */ + + int ac_tbl_no; /* AC entropy table selector (0..3) */ + + + + /* Remaining fields should be treated as private by applications. */ + + + + /* These values are computed during compression or decompression startup: */ + + /* Component's size in DCT blocks. + + * Any dummy blocks added to complete an MCU are not counted; therefore + + * these values do not depend on whether a scan is interleaved or not. + + */ + + JDIMENSION width_in_blocks; + + JDIMENSION height_in_blocks; + + /* Size of a DCT block in samples. Always DCTSIZE for compression. + + * For decompression this is the size of the output from one DCT block, + + * reflecting any scaling we choose to apply during the IDCT step. + + * Values of 1,2,4,8 are likely to be supported. Note that different + + * components may receive different IDCT scalings. + + */ + + int DCT_scaled_size; + + /* The downsampled dimensions are the component's actual, unpadded number + + * of samples at the main buffer (preprocessing/compression interface), thus + + * downsampled_width = ceil(image_width * Hi/Hmax) + + * and similarly for height. For decompression, IDCT scaling is included, so + + * downsampled_width = ceil(image_width * Hi/Hmax * DCT_scaled_size/DCTSIZE) + + */ + + JDIMENSION downsampled_width; /* actual width in samples */ + + JDIMENSION downsampled_height; /* actual height in samples */ + + /* This flag is used only for decompression. In cases where some of the + + * components will be ignored (eg grayscale output from YCbCr image), + + * we can skip most computations for the unused components. + + */ + + boolean component_needed; /* do we need the value of this component? */ + + + + /* These values are computed before starting a scan of the component. */ + + /* The decompressor output side may not use these variables. */ + + int MCU_width; /* number of blocks per MCU, horizontally */ + + int MCU_height; /* number of blocks per MCU, vertically */ + + int MCU_blocks; /* MCU_width * MCU_height */ + + int MCU_sample_width; /* MCU width in samples, MCU_width*DCT_scaled_size */ + + int last_col_width; /* # of non-dummy blocks across in last MCU */ + + int last_row_height; /* # of non-dummy blocks down in last MCU */ + + + + /* Saved quantization table for component; NULL if none yet saved. + + * See jdinput.c comments about the need for this information. + + * This field is not currently used by the compressor. + + */ + + JQUANT_TBL * quant_table; + + + + /* Private per-component storage for DCT or IDCT subsystem. */ + + void * dct_table; + +} jpeg_component_info; + + + + + +/* The script for encoding a multiple-scan file is an array of these: */ + + + +typedef struct { + + int comps_in_scan; /* number of components encoded in this scan */ + + int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ + + int Ss, Se; /* progressive JPEG spectral selection parms */ + + int Ah, Al; /* progressive JPEG successive approx. parms */ + +} jpeg_scan_info; + + + + + +/* Known color spaces. */ + + + +typedef enum { + + JCS_UNKNOWN, /* error/unspecified */ + + JCS_GRAYSCALE, /* monochrome */ + + JCS_RGB, /* red/green/blue */ + + JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */ + + JCS_CMYK, /* C/M/Y/K */ + + JCS_YCCK /* Y/Cb/Cr/K */ + +} J_COLOR_SPACE; + + + +/* DCT/IDCT algorithm options. */ + + + +typedef enum { + + JDCT_ISLOW, /* slow but accurate integer algorithm */ + + JDCT_IFAST, /* faster, less accurate integer method */ + + JDCT_FLOAT /* floating-point: accurate, fast on fast HW */ + +} J_DCT_METHOD; + + + +#ifndef JDCT_DEFAULT /* may be overridden in jconfig.h */ + +#define JDCT_DEFAULT JDCT_ISLOW + +#endif + +#ifndef JDCT_FASTEST /* may be overridden in jconfig.h */ + +#define JDCT_FASTEST JDCT_IFAST + +#endif + + + +/* Dithering options for decompression. */ + + + +typedef enum { + + JDITHER_NONE, /* no dithering */ + + JDITHER_ORDERED, /* simple ordered dither */ + + JDITHER_FS /* Floyd-Steinberg error diffusion dither */ + +} J_DITHER_MODE; + + + + + +/* Common fields between JPEG compression and decompression master structs. */ + + + +#define jpeg_common_fields \ + + struct jpeg_error_mgr * err; /* Error handler module */\ + + struct jpeg_memory_mgr * mem; /* Memory manager module */\ + + struct jpeg_progress_mgr * progress; /* Progress monitor, or NULL if none */\ + + boolean is_decompressor; /* so common code can tell which is which */\ + + int global_state /* for checking call sequence validity */ + + + +/* Routines that are to be used by both halves of the library are declared + + * to receive a pointer to this structure. There are no actual instances of + + * jpeg_common_struct, only of jpeg_compress_struct and jpeg_decompress_struct. + + */ + +struct jpeg_common_struct { + + jpeg_common_fields; /* Fields common to both master struct types */ + + /* Additional fields follow in an actual jpeg_compress_struct or + + * jpeg_decompress_struct. All three structs must agree on these + + * initial fields! (This would be a lot cleaner in C++.) + + */ + +}; + + + +typedef struct jpeg_common_struct * j_common_ptr; + +typedef struct jpeg_compress_struct * j_compress_ptr; + +typedef struct jpeg_decompress_struct * j_decompress_ptr; + + + + + +/* Master record for a compression instance */ + + + +struct jpeg_compress_struct { + + jpeg_common_fields; /* Fields shared with jpeg_decompress_struct */ + + + + /* Destination for compressed data */ + + struct jpeg_destination_mgr * dest; + + + + /* Description of source image --- these fields must be filled in by + + * outer application before starting compression. in_color_space must + + * be correct before you can even call jpeg_set_defaults(). + + */ + + + + JDIMENSION image_width; /* input image width */ + + JDIMENSION image_height; /* input image height */ + + int input_components; /* # of color components in input image */ + + J_COLOR_SPACE in_color_space; /* colorspace of input image */ + + + + double input_gamma; /* image gamma of input image */ + + + + /* Compression parameters --- these fields must be set before calling + + * jpeg_start_compress(). We recommend calling jpeg_set_defaults() to + + * initialize everything to reasonable defaults, then changing anything + + * the application specifically wants to change. That way you won't get + + * burnt when new parameters are added. Also note that there are several + + * helper routines to simplify changing parameters. + + */ + + + + int data_precision; /* bits of precision in image data */ + + + + int num_components; /* # of color components in JPEG image */ + + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + + + jpeg_component_info * comp_info; + + /* comp_info[i] describes component that appears i'th in SOF */ + + + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + + /* ptrs to Huffman coding tables, or NULL if not defined */ + + + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + + + int num_scans; /* # of entries in scan_info array */ + + const jpeg_scan_info * scan_info; /* script for multi-scan file, or NULL */ + + /* The default value of scan_info is NULL, which causes a single-scan + + * sequential JPEG file to be emitted. To create a multi-scan file, + + * set num_scans and scan_info to point to an array of scan definitions. + + */ + + + + boolean raw_data_in; /* TRUE=caller supplies downsampled data */ + + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + + boolean optimize_coding; /* TRUE=optimize entropy encoding parms */ + + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + + int smoothing_factor; /* 1..100, or 0 for no input smoothing */ + + J_DCT_METHOD dct_method; /* DCT algorithm selector */ + + + + /* The restart interval can be specified in absolute MCUs by setting + + * restart_interval, or in MCU rows by setting restart_in_rows + + * (in which case the correct restart_interval will be figured + + * for each scan). + + */ + + unsigned int restart_interval; /* MCUs per restart, or 0 for no restart */ + + int restart_in_rows; /* if > 0, MCU rows per restart interval */ + + + + /* Parameters controlling emission of special markers. */ + + + + boolean write_JFIF_header; /* should a JFIF marker be written? */ + + /* These three values are not used by the JPEG code, merely copied */ + + /* into the JFIF APP0 marker. density_unit can be 0 for unknown, */ + + /* 1 for dots/inch, or 2 for dots/cm. Note that the pixel aspect */ + + /* ratio is defined by X_density/Y_density even when density_unit=0. */ + + UINT8 density_unit; /* JFIF code for pixel size units */ + + UINT16 X_density; /* Horizontal pixel density */ + + UINT16 Y_density; /* Vertical pixel density */ + + boolean write_Adobe_marker; /* should an Adobe marker be written? */ + + + + /* State variable: index of next scanline to be written to + + * jpeg_write_scanlines(). Application may use this to control its + + * processing loop, e.g., "while (next_scanline < image_height)". + + */ + + + + JDIMENSION next_scanline; /* 0 .. image_height-1 */ + + + + /* Remaining fields are known throughout compressor, but generally + + * should not be touched by a surrounding application. + + */ + + + + /* + + * These fields are computed during compression startup + + */ + + boolean progressive_mode; /* TRUE if scan script uses progressive mode */ + + int max_h_samp_factor; /* largest h_samp_factor */ + + int max_v_samp_factor; /* largest v_samp_factor */ + + + + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coef ctlr */ + + /* The coefficient controller receives data in units of MCU rows as defined + + * for fully interleaved scans (whether the JPEG file is interleaved or not). + + * There are v_samp_factor * DCTSIZE sample rows of each component in an + + * "iMCU" (interleaved MCU) row. + + */ + + + + /* + + * These fields are valid during any one scan. + + * They describe the components and MCUs actually appearing in the scan. + + */ + + int comps_in_scan; /* # of JPEG components in this scan */ + + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + + int MCU_membership[C_MAX_BLOCKS_IN_MCU]; + + /* MCU_membership[i] is index in cur_comp_info of component owning */ + + /* i'th block in an MCU */ + + + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + + + /* + + * Links to compression subobjects (methods and private variables of modules) + + */ + + struct jpeg_comp_master * master; + + struct jpeg_c_main_controller * main; + + struct jpeg_c_prep_controller * prep; + + struct jpeg_c_coef_controller * coef; + + struct jpeg_marker_writer * marker; + + struct jpeg_color_converter * cconvert; + + struct jpeg_downsampler * downsample; + + struct jpeg_forward_dct * fdct; + + struct jpeg_entropy_encoder * entropy; + +}; + + + + + +/* Master record for a decompression instance */ + + + +struct jpeg_decompress_struct { + + jpeg_common_fields; /* Fields shared with jpeg_compress_struct */ + + + + /* Source of compressed data */ + + struct jpeg_source_mgr * src; + + + + /* Basic description of image --- filled in by jpeg_read_header(). */ + + /* Application may inspect these values to decide how to process image. */ + + + + JDIMENSION image_width; /* nominal image width (from SOF marker) */ + + JDIMENSION image_height; /* nominal image height */ + + int num_components; /* # of color components in JPEG image */ + + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + + + /* Decompression processing parameters --- these fields must be set before + + * calling jpeg_start_decompress(). Note that jpeg_read_header() initializes + + * them to default values. + + */ + + + + J_COLOR_SPACE out_color_space; /* colorspace for output */ + + + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + + + double output_gamma; /* image gamma wanted in output */ + + + + boolean buffered_image; /* TRUE=multiple output passes */ + + boolean raw_data_out; /* TRUE=downsampled data wanted */ + + + + J_DCT_METHOD dct_method; /* IDCT algorithm selector */ + + boolean do_fancy_upsampling; /* TRUE=apply fancy upsampling */ + + boolean do_block_smoothing; /* TRUE=apply interblock smoothing */ + + + + boolean quantize_colors; /* TRUE=colormapped output wanted */ + + /* the following are ignored if not quantize_colors: */ + + J_DITHER_MODE dither_mode; /* type of color dithering to use */ + + boolean two_pass_quantize; /* TRUE=use two-pass color quantization */ + + int desired_number_of_colors; /* max # colors to use in created colormap */ + + /* these are significant only in buffered-image mode: */ + + boolean enable_1pass_quant; /* enable future use of 1-pass quantizer */ + + boolean enable_external_quant;/* enable future use of external colormap */ + + boolean enable_2pass_quant; /* enable future use of 2-pass quantizer */ + + + + /* Description of actual output image that will be returned to application. + + * These fields are computed by jpeg_start_decompress(). + + * You can also use jpeg_calc_output_dimensions() to determine these values + + * in advance of calling jpeg_start_decompress(). + + */ + + + + JDIMENSION output_width; /* scaled image width */ + + JDIMENSION output_height; /* scaled image height */ + + int out_color_components; /* # of color components in out_color_space */ + + int output_components; /* # of color components returned */ + + /* output_components is 1 (a colormap index) when quantizing colors; + + * otherwise it equals out_color_components. + + */ + + int rec_outbuf_height; /* min recommended height of scanline buffer */ + + /* If the buffer passed to jpeg_read_scanlines() is less than this many rows + + * high, space and time will be wasted due to unnecessary data copying. + + * Usually rec_outbuf_height will be 1 or 2, at most 4. + + */ + + + + /* When quantizing colors, the output colormap is described by these fields. + + * The application can supply a colormap by setting colormap non-NULL before + + * calling jpeg_start_decompress; otherwise a colormap is created during + + * jpeg_start_decompress or jpeg_start_output. + + * The map has out_color_components rows and actual_number_of_colors columns. + + */ + + int actual_number_of_colors; /* number of entries in use */ + + JSAMPARRAY colormap; /* The color map as a 2-D pixel array */ + + + + /* State variables: these variables indicate the progress of decompression. + + * The application may examine these but must not modify them. + + */ + + + + /* Row index of next scanline to be read from jpeg_read_scanlines(). + + * Application may use this to control its processing loop, e.g., + + * "while (output_scanline < output_height)". + + */ + + JDIMENSION output_scanline; /* 0 .. output_height-1 */ + + + + /* Current input scan number and number of iMCU rows completed in scan. + + * These indicate the progress of the decompressor input side. + + */ + + int input_scan_number; /* Number of SOS markers seen so far */ + + JDIMENSION input_iMCU_row; /* Number of iMCU rows completed */ + + + + /* The "output scan number" is the notional scan being displayed by the + + * output side. The decompressor will not allow output scan/row number + + * to get ahead of input scan/row, but it can fall arbitrarily far behind. + + */ + + int output_scan_number; /* Nominal scan number being displayed */ + + JDIMENSION output_iMCU_row; /* Number of iMCU rows read */ + + + + /* Current progression status. coef_bits[c][i] indicates the precision + + * with which component c's DCT coefficient i (in zigzag order) is known. + + * It is -1 when no data has yet been received, otherwise it is the point + + * transform (shift) value for the most recent scan of the coefficient + + * (thus, 0 at completion of the progression). + + * This pointer is NULL when reading a non-progressive file. + + */ + + int (*coef_bits)[DCTSIZE2]; /* -1 or current Al value for each coef */ + + + + /* Internal JPEG parameters --- the application usually need not look at + + * these fields. Note that the decompressor output side may not use + + * any parameters that can change between scans. + + */ + + + + /* Quantization and Huffman tables are carried forward across input + + * datastreams when processing abbreviated JPEG datastreams. + + */ + + + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + + /* ptrs to Huffman coding tables, or NULL if not defined */ + + + + /* These parameters are never carried across datastreams, since they + + * are given in SOF/SOS markers or defined to be reset by SOI. + + */ + + + + int data_precision; /* bits of precision in image data */ + + + + jpeg_component_info * comp_info; + + /* comp_info[i] describes component that appears i'th in SOF */ + + + + boolean progressive_mode; /* TRUE if SOFn specifies progressive mode */ + + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + + + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + + + unsigned int restart_interval; /* MCUs per restart interval, or 0 for no restart */ + + + + /* These fields record data obtained from optional markers recognized by + + * the JPEG library. + + */ + + boolean saw_JFIF_marker; /* TRUE iff a JFIF APP0 marker was found */ + + /* Data copied from JFIF marker: */ + + UINT8 density_unit; /* JFIF code for pixel size units */ + + UINT16 X_density; /* Horizontal pixel density */ + + UINT16 Y_density; /* Vertical pixel density */ + + boolean saw_Adobe_marker; /* TRUE iff an Adobe APP14 marker was found */ + + UINT8 Adobe_transform; /* Color transform code from Adobe marker */ + + + + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + + + + /* Remaining fields are known throughout decompressor, but generally + + * should not be touched by a surrounding application. + + */ + + + + /* + + * These fields are computed during decompression startup + + */ + + int max_h_samp_factor; /* largest h_samp_factor */ + + int max_v_samp_factor; /* largest v_samp_factor */ + + + + int min_DCT_scaled_size; /* smallest DCT_scaled_size of any component */ + + + + JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ + + /* The coefficient controller's input and output progress is measured in + + * units of "iMCU" (interleaved MCU) rows. These are the same as MCU rows + + * in fully interleaved JPEG scans, but are used whether the scan is + + * interleaved or not. We define an iMCU row as v_samp_factor DCT block + + * rows of each component. Therefore, the IDCT output contains + + * v_samp_factor*DCT_scaled_size sample rows of a component per iMCU row. + + */ + + + + JSAMPLE * sample_range_limit; /* table for fast range-limiting */ + + + + /* + + * These fields are valid during any one scan. + + * They describe the components and MCUs actually appearing in the scan. + + * Note that the decompressor output side must not use these fields. + + */ + + int comps_in_scan; /* # of JPEG components in this scan */ + + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + + int MCU_membership[D_MAX_BLOCKS_IN_MCU]; + + /* MCU_membership[i] is index in cur_comp_info of component owning */ + + /* i'th block in an MCU */ + + + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + + + /* This field is shared between entropy decoder and marker parser. + + * It is either zero or the code of a JPEG marker that has been + + * read from the data source, but has not yet been processed. + + */ + + int unread_marker; + + + + /* + + * Links to decompression subobjects (methods, private variables of modules) + + */ + + struct jpeg_decomp_master * master; + + struct jpeg_d_main_controller * main; + + struct jpeg_d_coef_controller * coef; + + struct jpeg_d_post_controller * post; + + struct jpeg_input_controller * inputctl; + + struct jpeg_marker_reader * marker; + + struct jpeg_entropy_decoder * entropy; + + struct jpeg_inverse_dct * idct; + + struct jpeg_upsampler * upsample; + + struct jpeg_color_deconverter * cconvert; + + struct jpeg_color_quantizer * cquantize; + +}; + + + + + +/* "Object" declarations for JPEG modules that may be supplied or called + + * directly by the surrounding application. + + * As with all objects in the JPEG library, these structs only define the + + * publicly visible methods and state variables of a module. Additional + + * private fields may exist after the public ones. + + */ + + + + + +/* Error handler object */ + + + +struct jpeg_error_mgr { + + /* Error exit handler: does not return to caller */ + + JMETHOD(void, error_exit, (j_common_ptr cinfo)); + + /* Conditionally emit a trace or warning message */ + + JMETHOD(void, emit_message, (j_common_ptr cinfo, int msg_level)); + + /* Routine that actually outputs a trace or error message */ + + JMETHOD(void, output_message, (j_common_ptr cinfo)); + + /* Format a message string for the most recent JPEG error or message */ + + JMETHOD(void, format_message, (j_common_ptr cinfo, char * buffer)); + +#define JMSG_LENGTH_MAX 200 /* recommended size of format_message buffer */ + + /* Reset error state variables at start of a new image */ + + JMETHOD(void, reset_error_mgr, (j_common_ptr cinfo)); + + + + /* The message ID code and any parameters are saved here. + + * A message can have one string parameter or up to 8 int parameters. + + */ + + int msg_code; + +#define JMSG_STR_PARM_MAX 80 + + union { + + int i[8]; + + char s[JMSG_STR_PARM_MAX]; + + } msg_parm; + + + + /* Standard state variables for error facility */ + + + + int trace_level; /* max msg_level that will be displayed */ + + + + /* For recoverable corrupt-data errors, we emit a warning message, + + * but keep going unless emit_message chooses to abort. emit_message + + * should count warnings in num_warnings. The surrounding application + + * can check for bad data by seeing if num_warnings is nonzero at the + + * end of processing. + + */ + + long num_warnings; /* number of corrupt-data warnings */ + + + + /* These fields point to the table(s) of error message strings. + + * An application can change the table pointer to switch to a different + + * message list (typically, to change the language in which errors are + + * reported). Some applications may wish to add additional error codes + + * that will be handled by the JPEG library error mechanism; the second + + * table pointer is used for this purpose. + + * + + * First table includes all errors generated by JPEG library itself. + + * Error code 0 is reserved for a "no such error string" message. + + */ + + const char * const * jpeg_message_table; /* Library errors */ + + int last_jpeg_message; /* Table contains strings 0..last_jpeg_message */ + + /* Second table can be added by application (see cjpeg/djpeg for example). + + * It contains strings numbered first_addon_message..last_addon_message. + + */ + + const char * const * addon_message_table; /* Non-library errors */ + + int first_addon_message; /* code for first string in addon table */ + + int last_addon_message; /* code for last string in addon table */ + +}; + + + + + +/* Progress monitor object */ + + + +struct jpeg_progress_mgr { + + JMETHOD(void, progress_monitor, (j_common_ptr cinfo)); + + + + long pass_counter; /* work units completed in this pass */ + + long pass_limit; /* total number of work units in this pass */ + + int completed_passes; /* passes completed so far */ + + int total_passes; /* total number of passes expected */ + +}; + + + + + +/* Data destination object for compression */ + + + +struct jpeg_destination_mgr { + + JOCTET * next_output_byte; /* => next byte to write in buffer */ + + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + + + + JMETHOD(void, init_destination, (j_compress_ptr cinfo)); + + JMETHOD(boolean, empty_output_buffer, (j_compress_ptr cinfo)); + + JMETHOD(void, term_destination, (j_compress_ptr cinfo)); + +}; + + + + + +/* Data source object for decompression */ + + + +struct jpeg_source_mgr { + + const JOCTET * next_input_byte; /* => next byte to read from buffer */ + + size_t bytes_in_buffer; /* # of bytes remaining in buffer */ + + + + JMETHOD(void, init_source, (j_decompress_ptr cinfo)); + + JMETHOD(boolean, fill_input_buffer, (j_decompress_ptr cinfo)); + + JMETHOD(void, skip_input_data, (j_decompress_ptr cinfo, long num_bytes)); + + JMETHOD(boolean, resync_to_restart, (j_decompress_ptr cinfo, int desired)); + + JMETHOD(void, term_source, (j_decompress_ptr cinfo)); + +}; + + + + + +/* Memory manager object. + + * Allocates "small" objects (a few K total), "large" objects (tens of K), + + * and "really big" objects (virtual arrays with backing store if needed). + + * The memory manager does not allow individual objects to be freed; rather, + + * each created object is assigned to a pool, and whole pools can be freed + + * at once. This is faster and more convenient than remembering exactly what + + * to free, especially where malloc()/free() are not too speedy. + + * NB: alloc routines never return NULL. They exit to error_exit if not + + * successful. + + */ + + + +#define JPOOL_PERMANENT 0 /* lasts until master record is destroyed */ + +#define JPOOL_IMAGE 1 /* lasts until done with image/datastream */ + +#define JPOOL_NUMPOOLS 2 + + + +typedef struct jvirt_sarray_control * jvirt_sarray_ptr; + +typedef struct jvirt_barray_control * jvirt_barray_ptr; + + + + + +struct jpeg_memory_mgr { + + /* Method pointers */ + + JMETHOD(void *, alloc_small, (j_common_ptr cinfo, int pool_id, + + size_t sizeofobject)); + + JMETHOD(void FAR *, alloc_large, (j_common_ptr cinfo, int pool_id, + + size_t sizeofobject)); + + JMETHOD(JSAMPARRAY, alloc_sarray, (j_common_ptr cinfo, int pool_id, + + JDIMENSION samplesperrow, + + JDIMENSION numrows)); + + JMETHOD(JBLOCKARRAY, alloc_barray, (j_common_ptr cinfo, int pool_id, + + JDIMENSION blocksperrow, + + JDIMENSION numrows)); + + JMETHOD(jvirt_sarray_ptr, request_virt_sarray, (j_common_ptr cinfo, + + int pool_id, + + boolean pre_zero, + + JDIMENSION samplesperrow, + + JDIMENSION numrows, + + JDIMENSION maxaccess)); + + JMETHOD(jvirt_barray_ptr, request_virt_barray, (j_common_ptr cinfo, + + int pool_id, + + boolean pre_zero, + + JDIMENSION blocksperrow, + + JDIMENSION numrows, + + JDIMENSION maxaccess)); + + JMETHOD(void, realize_virt_arrays, (j_common_ptr cinfo)); + + JMETHOD(JSAMPARRAY, access_virt_sarray, (j_common_ptr cinfo, + + jvirt_sarray_ptr ptr, + + JDIMENSION start_row, + + JDIMENSION num_rows, + + boolean writable)); + + JMETHOD(JBLOCKARRAY, access_virt_barray, (j_common_ptr cinfo, + + jvirt_barray_ptr ptr, + + JDIMENSION start_row, + + JDIMENSION num_rows, + + boolean writable)); + + JMETHOD(void, free_pool, (j_common_ptr cinfo, int pool_id)); + + JMETHOD(void, self_destruct, (j_common_ptr cinfo)); + + + + /* Limit on memory allocation for this JPEG object. (Note that this is + + * merely advisory, not a guaranteed maximum; it only affects the space + + * used for virtual-array buffers.) May be changed by outer application + + * after creating the JPEG object. + + */ + + long max_memory_to_use; + +}; + + + + + +/* Routine signature for application-supplied marker processing methods. + + * Need not pass marker code since it is stored in cinfo->unread_marker. + + */ + +typedef JMETHOD(boolean, jpeg_marker_parser_method, (j_decompress_ptr cinfo)); + + + + + +/* Declarations for routines called by application. + + * The JPP macro hides prototype parameters from compilers that can't cope. + + * Note JPP requires double parentheses. + + */ + + + +#ifdef HAVE_PROTOTYPES + +#define JPP(arglist) arglist + +#else + +#define JPP(arglist) () + +#endif + + + + + +/* Short forms of external names for systems with brain-damaged linkers. + + * We shorten external names to be unique in the first six letters, which + + * is good enough for all known systems. + + * (If your compiler itself needs names to be unique in less than 15 + + * characters, you are out of luck. Get a better compiler.) + + */ + + + +#ifdef NEED_SHORT_EXTERNAL_NAMES + +#define jpeg_std_error jStdError + +#define jpeg_create_compress jCreaCompress + +#define jpeg_create_decompress jCreaDecompress + +#define jpeg_destroy_compress jDestCompress + +#define jpeg_destroy_decompress jDestDecompress + +#define jpeg_stdio_dest jStdDest + +#define jpeg_stdio_src jStdSrc + +#define jpeg_set_defaults jSetDefaults + +#define jpeg_set_colorspace jSetColorspace + +#define jpeg_default_colorspace jDefColorspace + +#define jpeg_set_quality jSetQuality + +#define jpeg_set_linear_quality jSetLQuality + +#define jpeg_add_quant_table jAddQuantTable + +#define jpeg_quality_scaling jQualityScaling + +#define jpeg_simple_progression jSimProgress + +#define jpeg_suppress_tables jSuppressTables + +#define jpeg_alloc_quant_table jAlcQTable + +#define jpeg_alloc_huff_table jAlcHTable + +#define jpeg_start_compress jStrtCompress + +#define jpeg_write_scanlines jWrtScanlines + +#define jpeg_finish_compress jFinCompress + +#define jpeg_write_raw_data jWrtRawData + +#define jpeg_write_marker jWrtMarker + +#define jpeg_write_tables jWrtTables + +#define jpeg_read_header jReadHeader + +#define jpeg_start_decompress jStrtDecompress + +#define jpeg_read_scanlines jReadScanlines + +#define jpeg_finish_decompress jFinDecompress + +#define jpeg_read_raw_data jReadRawData + +#define jpeg_has_multiple_scans jHasMultScn + +#define jpeg_start_output jStrtOutput + +#define jpeg_finish_output jFinOutput + +#define jpeg_input_complete jInComplete + +#define jpeg_new_colormap jNewCMap + +#define jpeg_consume_input jConsumeInput + +#define jpeg_calc_output_dimensions jCalcDimensions + +#define jpeg_set_marker_processor jSetMarker + +#define jpeg_read_coefficients jReadCoefs + +#define jpeg_write_coefficients jWrtCoefs + +#define jpeg_copy_critical_parameters jCopyCrit + +#define jpeg_abort_compress jAbrtCompress + +#define jpeg_abort_decompress jAbrtDecompress + +#define jpeg_abort jAbort + +#define jpeg_destroy jDestroy + +#define jpeg_resync_to_restart jResyncRestart + +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + + + + +/* Default error-management setup */ + +EXTERN struct jpeg_error_mgr *jpeg_std_error JPP((struct jpeg_error_mgr *err)); + + + +/* Initialization and destruction of JPEG compression objects */ + +/* NB: you must set up the error-manager BEFORE calling jpeg_create_xxx */ + +EXTERN void jpeg_create_compress JPP((j_compress_ptr cinfo)); + +EXTERN void jpeg_create_decompress JPP((j_decompress_ptr cinfo)); + +EXTERN void jpeg_destroy_compress JPP((j_compress_ptr cinfo)); + +EXTERN void jpeg_destroy_decompress JPP((j_decompress_ptr cinfo)); + + + +/* Standard data source and destination managers: stdio streams. */ + +/* Caller is responsible for opening the file before and closing after. */ + +EXTERN void jpeg_stdio_dest JPP((j_compress_ptr cinfo, FILE * outfile)); + +EXTERN void jpeg_stdio_src JPP((j_decompress_ptr cinfo, FILE * infile)); + + + +/* Default parameter setup for compression */ + +EXTERN void jpeg_set_defaults JPP((j_compress_ptr cinfo)); + +/* Compression parameter setup aids */ + +EXTERN void jpeg_set_colorspace JPP((j_compress_ptr cinfo, + + J_COLOR_SPACE colorspace)); + +EXTERN void jpeg_default_colorspace JPP((j_compress_ptr cinfo)); + +EXTERN void jpeg_set_quality JPP((j_compress_ptr cinfo, int quality, + + boolean force_baseline)); + +EXTERN void jpeg_set_linear_quality JPP((j_compress_ptr cinfo, + + int scale_factor, + + boolean force_baseline)); + +EXTERN void jpeg_add_quant_table JPP((j_compress_ptr cinfo, int which_tbl, + + const unsigned int *basic_table, + + int scale_factor, + + boolean force_baseline)); + +EXTERN int jpeg_quality_scaling JPP((int quality)); + +EXTERN void jpeg_simple_progression JPP((j_compress_ptr cinfo)); + +EXTERN void jpeg_suppress_tables JPP((j_compress_ptr cinfo, + + boolean suppress)); + +EXTERN JQUANT_TBL * jpeg_alloc_quant_table JPP((j_common_ptr cinfo)); + +EXTERN JHUFF_TBL * jpeg_alloc_huff_table JPP((j_common_ptr cinfo)); + + + +/* Main entry points for compression */ + +EXTERN void jpeg_start_compress JPP((j_compress_ptr cinfo, + + boolean write_all_tables)); + +EXTERN JDIMENSION jpeg_write_scanlines JPP((j_compress_ptr cinfo, + + JSAMPARRAY scanlines, + + JDIMENSION num_lines)); + +EXTERN void jpeg_finish_compress JPP((j_compress_ptr cinfo)); + + + +/* Replaces jpeg_write_scanlines when writing raw downsampled data. */ + +EXTERN JDIMENSION jpeg_write_raw_data JPP((j_compress_ptr cinfo, + + JSAMPIMAGE data, + + JDIMENSION num_lines)); + + + +/* Write a special marker. See libjpeg.doc concerning safe usage. */ + +EXTERN void jpeg_write_marker JPP((j_compress_ptr cinfo, int marker, + + const JOCTET *dataptr, unsigned int datalen)); + + + +/* Alternate compression function: just write an abbreviated table file */ + +EXTERN void jpeg_write_tables JPP((j_compress_ptr cinfo)); + + + +/* Decompression startup: read start of JPEG datastream to see what's there */ + +EXTERN int jpeg_read_header JPP((j_decompress_ptr cinfo, + + boolean require_image)); + +/* Return value is one of: */ + +#define JPEG_SUSPENDED 0 /* Suspended due to lack of input data */ + +#define JPEG_HEADER_OK 1 /* Found valid image datastream */ + +#define JPEG_HEADER_TABLES_ONLY 2 /* Found valid table-specs-only datastream */ + +/* If you pass require_image = TRUE (normal case), you need not check for + + * a TABLES_ONLY return code; an abbreviated file will cause an error exit. + + * JPEG_SUSPENDED is only possible if you use a data source module that can + + * give a suspension return (the stdio source module doesn't). + + */ + + + +/* Main entry points for decompression */ + +EXTERN boolean jpeg_start_decompress JPP((j_decompress_ptr cinfo)); + +EXTERN JDIMENSION jpeg_read_scanlines JPP((j_decompress_ptr cinfo, + + JSAMPARRAY scanlines, + + JDIMENSION max_lines)); + +EXTERN boolean jpeg_finish_decompress JPP((j_decompress_ptr cinfo)); + + + +/* Replaces jpeg_read_scanlines when reading raw downsampled data. */ + +EXTERN JDIMENSION jpeg_read_raw_data JPP((j_decompress_ptr cinfo, + + JSAMPIMAGE data, + + JDIMENSION max_lines)); + + + +/* Additional entry points for buffered-image mode. */ + +EXTERN boolean jpeg_has_multiple_scans JPP((j_decompress_ptr cinfo)); + +EXTERN boolean jpeg_start_output JPP((j_decompress_ptr cinfo, + + int scan_number)); + +EXTERN boolean jpeg_finish_output JPP((j_decompress_ptr cinfo)); + +EXTERN boolean jpeg_input_complete JPP((j_decompress_ptr cinfo)); + +EXTERN void jpeg_new_colormap JPP((j_decompress_ptr cinfo)); + +EXTERN int jpeg_consume_input JPP((j_decompress_ptr cinfo)); + +/* Return value is one of: */ + +/* #define JPEG_SUSPENDED 0 Suspended due to lack of input data */ + +#define JPEG_REACHED_SOS 1 /* Reached start of new scan */ + +#define JPEG_REACHED_EOI 2 /* Reached end of image */ + +#define JPEG_ROW_COMPLETED 3 /* Completed one iMCU row */ + +#define JPEG_SCAN_COMPLETED 4 /* Completed last iMCU row of a scan */ + + + +/* Precalculate output dimensions for current decompression parameters. */ + +EXTERN void jpeg_calc_output_dimensions JPP((j_decompress_ptr cinfo)); + + + +/* Install a special processing method for COM or APPn markers. */ + +EXTERN void jpeg_set_marker_processor JPP((j_decompress_ptr cinfo, + + int marker_code, + + jpeg_marker_parser_method routine)); + + + +/* Read or write raw DCT coefficients --- useful for lossless transcoding. */ + +EXTERN jvirt_barray_ptr * jpeg_read_coefficients JPP((j_decompress_ptr cinfo)); + +EXTERN void jpeg_write_coefficients JPP((j_compress_ptr cinfo, + + jvirt_barray_ptr * coef_arrays)); + +EXTERN void jpeg_copy_critical_parameters JPP((j_decompress_ptr srcinfo, + + j_compress_ptr dstinfo)); + + + +/* If you choose to abort compression or decompression before completing + + * jpeg_finish_(de)compress, then you need to clean up to release memory, + + * temporary files, etc. You can just call jpeg_destroy_(de)compress + + * if you're done with the JPEG object, but if you want to clean it up and + + * reuse it, call this: + + */ + +EXTERN void jpeg_abort_compress JPP((j_compress_ptr cinfo)); + +EXTERN void jpeg_abort_decompress JPP((j_decompress_ptr cinfo)); + + + +/* Generic versions of jpeg_abort and jpeg_destroy that work on either + + * flavor of JPEG object. These may be more convenient in some places. + + */ + +EXTERN void jpeg_abort JPP((j_common_ptr cinfo)); + +EXTERN void jpeg_destroy JPP((j_common_ptr cinfo)); + + + +/* Default restart-marker-resync procedure for use by data source modules */ + +EXTERN boolean jpeg_resync_to_restart JPP((j_decompress_ptr cinfo, + + int desired)); + + + + + +/* These marker codes are exported since applications and data source modules + + * are likely to want to use them. + + */ + + + +#define JPEG_RST0 0xD0 /* RST0 marker code */ + +#define JPEG_EOI 0xD9 /* EOI marker code */ + +#define JPEG_APP0 0xE0 /* APP0 marker code */ + +#define JPEG_COM 0xFE /* COM marker code */ + + + + + +/* If we have a brain-damaged compiler that emits warnings (or worse, errors) + + * for structure definitions that are never filled in, keep it quiet by + + * supplying dummy definitions for the various substructures. + + */ + + + +#ifdef INCOMPLETE_TYPES_BROKEN + +#ifndef JPEG_INTERNALS /* will be defined in jpegint.h */ + +struct jvirt_sarray_control { long dummy; }; + +struct jvirt_barray_control { long dummy; }; + +struct jpeg_comp_master { long dummy; }; + +struct jpeg_c_main_controller { long dummy; }; + +struct jpeg_c_prep_controller { long dummy; }; + +struct jpeg_c_coef_controller { long dummy; }; + +struct jpeg_marker_writer { long dummy; }; + +struct jpeg_color_converter { long dummy; }; + +struct jpeg_downsampler { long dummy; }; + +struct jpeg_forward_dct { long dummy; }; + +struct jpeg_entropy_encoder { long dummy; }; + +struct jpeg_decomp_master { long dummy; }; + +struct jpeg_d_main_controller { long dummy; }; + +struct jpeg_d_coef_controller { long dummy; }; + +struct jpeg_d_post_controller { long dummy; }; + +struct jpeg_input_controller { long dummy; }; + +struct jpeg_marker_reader { long dummy; }; + +struct jpeg_entropy_decoder { long dummy; }; + +struct jpeg_inverse_dct { long dummy; }; + +struct jpeg_upsampler { long dummy; }; + +struct jpeg_color_deconverter { long dummy; }; + +struct jpeg_color_quantizer { long dummy; }; + +#endif /* JPEG_INTERNALS */ + +#endif /* INCOMPLETE_TYPES_BROKEN */ + + + + + +/* + + * The JPEG library modules define JPEG_INTERNALS before including this file. + + * The internal structure declarations are read only when that is true. + + * Applications using the library should not include jpegint.h, but may wish + + * to include jerror.h. + + */ + + + +#ifdef JPEG_INTERNALS + +#include "jpegint.h" /* fetch private declarations */ + +#include "jerror.h" /* fetch error codes too */ + +#endif + + + +#endif /* JPEGLIB_H */ + diff --git a/utils/roq2/jpeg/jpegtran.c b/utils/roq2/jpeg/jpegtran.c new file mode 100644 index 0000000..1dfcbd6 --- /dev/null +++ b/utils/roq2/jpeg/jpegtran.c @@ -0,0 +1,740 @@ +/* + + * jpegtran.c + + * + + * Copyright (C) 1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains a command-line user interface for JPEG transcoding. + + * It is very similar to cjpeg.c, but provides lossless transcoding between + + * different JPEG file formats. + + */ + + + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#include "jversion.h" /* for version message */ + + + +#ifdef USE_CCOMMAND /* command-line reader for Macintosh */ + +#ifdef __MWERKS__ + +#include /* Metrowerks declares it here */ + +#endif + +#ifdef THINK_C + +#include /* Think declares it here */ + +#endif + +#endif + + + + + +/* + + * Argument-parsing code. + + * The switch parser is designed to be useful with DOS-style command line + + * syntax, ie, intermixed switches and file names, where only the switches + + * to the left of a given file name affect processing of that file. + + * The main program in this file doesn't actually use this capability... + + */ + + + + + +static const char * progname; /* program name for error messages */ + +static char * outfilename; /* for -outfile switch */ + + + + + +LOCAL void + +usage (void) + +/* complain about bad command line */ + +{ + + fprintf(stderr, "usage: %s [switches] ", progname); + +#ifdef TWO_FILE_COMMANDLINE + + fprintf(stderr, "inputfile outputfile\n"); + +#else + + fprintf(stderr, "[inputfile]\n"); + +#endif + + + + fprintf(stderr, "Switches (names may be abbreviated):\n"); + +#ifdef ENTROPY_OPT_SUPPORTED + + fprintf(stderr, " -optimize Optimize Huffman table (smaller file, but slow compression)\n"); + +#endif + +#ifdef C_PROGRESSIVE_SUPPORTED + + fprintf(stderr, " -progressive Create progressive JPEG file\n"); + +#endif + + fprintf(stderr, "Switches for advanced users:\n"); + + fprintf(stderr, " -restart N Set restart interval in rows, or in blocks with B\n"); + + fprintf(stderr, " -maxmemory N Maximum memory to use (in kbytes)\n"); + + fprintf(stderr, " -outfile name Specify name for output file\n"); + + fprintf(stderr, " -verbose or -debug Emit debug output\n"); + + fprintf(stderr, "Switches for wizards:\n"); + +#ifdef C_ARITH_CODING_SUPPORTED + + fprintf(stderr, " -arithmetic Use arithmetic coding\n"); + +#endif + +#ifdef C_MULTISCAN_FILES_SUPPORTED + + fprintf(stderr, " -scans file Create multi-scan JPEG per script file\n"); + +#endif + + exit(EXIT_FAILURE); + +} + + + + + +LOCAL int + +parse_switches (j_compress_ptr cinfo, int argc, char **argv, + + int last_file_arg_seen, boolean for_real) + +/* Parse optional switches. + + * Returns argv[] index of first file-name argument (== argc if none). + + * Any file names with indexes <= last_file_arg_seen are ignored; + + * they have presumably been processed in a previous iteration. + + * (Pass 0 for last_file_arg_seen on the first or only iteration.) + + * for_real is FALSE on the first (dummy) pass; we may skip any expensive + + * processing. + + */ + +{ + + int argn; + + char * arg; + + boolean simple_progressive; + + char * scansarg = NULL; /* saves -scans parm if any */ + + + + /* Set up default JPEG parameters. */ + + simple_progressive = FALSE; + + outfilename = NULL; + + cinfo->err->trace_level = 0; + + + + /* Scan command line options, adjust parameters */ + + + + for (argn = 1; argn < argc; argn++) { + + arg = argv[argn]; + + if (*arg != '-') { + + /* Not a switch, must be a file name argument */ + + if (argn <= last_file_arg_seen) { + + outfilename = NULL; /* -outfile applies to just one input file */ + + continue; /* ignore this name if previously processed */ + + } + + break; /* else done parsing switches */ + + } + + arg++; /* advance past switch marker character */ + + + + if (keymatch(arg, "arithmetic", 1)) { + + /* Use arithmetic coding. */ + +#ifdef C_ARITH_CODING_SUPPORTED + + cinfo->arith_code = TRUE; + +#else + + fprintf(stderr, "%s: sorry, arithmetic coding not supported\n", + + progname); + + exit(EXIT_FAILURE); + +#endif + + + + } else if (keymatch(arg, "debug", 1) || keymatch(arg, "verbose", 1)) { + + /* Enable debug printouts. */ + + /* On first -d, print version identification */ + + static boolean printed_version = FALSE; + + + + if (! printed_version) { + + fprintf(stderr, "Independent JPEG Group's JPEGTRAN, version %s\n%s\n", + + JVERSION, JCOPYRIGHT); + + printed_version = TRUE; + + } + + cinfo->err->trace_level++; + + + + } else if (keymatch(arg, "maxmemory", 3)) { + + /* Maximum memory in Kb (or Mb with 'm'). */ + + long lval; + + char ch = 'x'; + + + + if (++argn >= argc) /* advance to next argument */ + + usage(); + + if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1) + + usage(); + + if (ch == 'm' || ch == 'M') + + lval *= 1000L; + + cinfo->mem->max_memory_to_use = lval * 1000L; + + + + } else if (keymatch(arg, "optimize", 1) || keymatch(arg, "optimise", 1)) { + + /* Enable entropy parm optimization. */ + +#ifdef ENTROPY_OPT_SUPPORTED + + cinfo->optimize_coding = TRUE; + +#else + + fprintf(stderr, "%s: sorry, entropy optimization was not compiled\n", + + progname); + + exit(EXIT_FAILURE); + +#endif + + + + } else if (keymatch(arg, "outfile", 4)) { + + /* Set output file name. */ + + if (++argn >= argc) /* advance to next argument */ + + usage(); + + outfilename = argv[argn]; /* save it away for later use */ + + + + } else if (keymatch(arg, "progressive", 1)) { + + /* Select simple progressive mode. */ + +#ifdef C_PROGRESSIVE_SUPPORTED + + simple_progressive = TRUE; + + /* We must postpone execution until num_components is known. */ + +#else + + fprintf(stderr, "%s: sorry, progressive output was not compiled\n", + + progname); + + exit(EXIT_FAILURE); + +#endif + + + + } else if (keymatch(arg, "restart", 1)) { + + /* Restart interval in MCU rows (or in MCUs with 'b'). */ + + long lval; + + char ch = 'x'; + + + + if (++argn >= argc) /* advance to next argument */ + + usage(); + + if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1) + + usage(); + + if (lval < 0 || lval > 65535L) + + usage(); + + if (ch == 'b' || ch == 'B') { + + cinfo->restart_interval = (unsigned int) lval; + + cinfo->restart_in_rows = 0; /* else prior '-restart n' overrides me */ + + } else { + + cinfo->restart_in_rows = (int) lval; + + /* restart_interval will be computed during startup */ + + } + + + + } else if (keymatch(arg, "scans", 2)) { + + /* Set scan script. */ + +#ifdef C_MULTISCAN_FILES_SUPPORTED + + if (++argn >= argc) /* advance to next argument */ + + usage(); + + scansarg = argv[argn]; + + /* We must postpone reading the file in case -progressive appears. */ + +#else + + fprintf(stderr, "%s: sorry, multi-scan output was not compiled\n", + + progname); + + exit(EXIT_FAILURE); + +#endif + + + + } else { + + usage(); /* bogus switch */ + + } + + } + + + + /* Post-switch-scanning cleanup */ + + + + if (for_real) { + + + +#ifdef C_PROGRESSIVE_SUPPORTED + + if (simple_progressive) /* process -progressive; -scans can override */ + + jpeg_simple_progression(cinfo); + +#endif + + + +#ifdef C_MULTISCAN_FILES_SUPPORTED + + if (scansarg != NULL) /* process -scans if it was present */ + + if (! read_scan_script(cinfo, scansarg)) + + usage(); + +#endif + + } + + + + return argn; /* return index of next arg (file name) */ + +} + + + + + +/* + + * The main program. + + */ + + + +GLOBAL int + +main (int argc, char **argv) + +{ + + struct jpeg_decompress_struct srcinfo; + + struct jpeg_compress_struct dstinfo; + + struct jpeg_error_mgr jsrcerr, jdsterr; + +#ifdef PROGRESS_REPORT + + struct cdjpeg_progress_mgr progress; + +#endif + + jvirt_barray_ptr * coef_arrays; + + int file_index; + + FILE * input_file; + + FILE * output_file; + + + + /* On Mac, fetch a command line. */ + +#ifdef USE_CCOMMAND + + argc = ccommand(&argv); + +#endif + + + + progname = argv[0]; + + if (progname == NULL || progname[0] == 0) + + progname = "jpegtran"; /* in case C library doesn't provide it */ + + + + /* Initialize the JPEG decompression object with default error handling. */ + + srcinfo.err = jpeg_std_error(&jsrcerr); + + jpeg_create_decompress(&srcinfo); + + /* Initialize the JPEG compression object with default error handling. */ + + dstinfo.err = jpeg_std_error(&jdsterr); + + jpeg_create_compress(&dstinfo); + + + + /* Now safe to enable signal catcher. + + * Note: we assume only the decompression object will have virtual arrays. + + */ + +#ifdef NEED_SIGNAL_CATCHER + + enable_signal_catcher((j_common_ptr) &srcinfo); + +#endif + + + + /* Scan command line to find file names. + + * It is convenient to use just one switch-parsing routine, but the switch + + * values read here are ignored; we will rescan the switches after opening + + * the input file. + + */ + + + + file_index = parse_switches(&dstinfo, argc, argv, 0, FALSE); + + jsrcerr.trace_level = jdsterr.trace_level; + + + +#ifdef TWO_FILE_COMMANDLINE + + /* Must have either -outfile switch or explicit output file name */ + + if (outfilename == NULL) { + + if (file_index != argc-2) { + + fprintf(stderr, "%s: must name one input and one output file\n", + + progname); + + usage(); + + } + + outfilename = argv[file_index+1]; + + } else { + + if (file_index != argc-1) { + + fprintf(stderr, "%s: must name one input and one output file\n", + + progname); + + usage(); + + } + + } + +#else + + /* Unix style: expect zero or one file name */ + + if (file_index < argc-1) { + + fprintf(stderr, "%s: only one input file\n", progname); + + usage(); + + } + +#endif /* TWO_FILE_COMMANDLINE */ + + + + /* Open the input file. */ + + if (file_index < argc) { + + if ((input_file = fopen(argv[file_index], READ_BINARY)) == NULL) { + + fprintf(stderr, "%s: can't open %s\n", progname, argv[file_index]); + + exit(EXIT_FAILURE); + + } + + } else { + + /* default input file is stdin */ + + input_file = read_stdin(); + + } + + + + /* Open the output file. */ + + if (outfilename != NULL) { + + if ((output_file = fopen(outfilename, WRITE_BINARY)) == NULL) { + + fprintf(stderr, "%s: can't open %s\n", progname, outfilename); + + exit(EXIT_FAILURE); + + } + + } else { + + /* default output file is stdout */ + + output_file = write_stdout(); + + } + + + +#ifdef PROGRESS_REPORT + + start_progress_monitor((j_common_ptr) &dstinfo, &progress); + +#endif + + + + /* Specify data source for decompression */ + + jpeg_stdio_src(&srcinfo, input_file); + + + + /* Read file header */ + + (void) jpeg_read_header(&srcinfo, TRUE); + + + + /* Read source file as DCT coefficients */ + + coef_arrays = jpeg_read_coefficients(&srcinfo); + + + + /* Initialize destination compression parameters from source values */ + + jpeg_copy_critical_parameters(&srcinfo, &dstinfo); + + + + /* Adjust default compression parameters by re-parsing the options */ + + file_index = parse_switches(&dstinfo, argc, argv, 0, TRUE); + + + + /* Specify data destination for compression */ + + jpeg_stdio_dest(&dstinfo, output_file); + + + + /* Start compressor */ + + jpeg_write_coefficients(&dstinfo, coef_arrays); + + + + /* ought to copy source comments here... */ + + + + /* Finish compression and release memory */ + + jpeg_finish_compress(&dstinfo); + + jpeg_destroy_compress(&dstinfo); + + (void) jpeg_finish_decompress(&srcinfo); + + jpeg_destroy_decompress(&srcinfo); + + + + /* Close files, if we opened them */ + + if (input_file != stdin) + + fclose(input_file); + + if (output_file != stdout) + + fclose(output_file); + + + +#ifdef PROGRESS_REPORT + + end_progress_monitor((j_common_ptr) &dstinfo); + +#endif + + + + /* All done. */ + + exit(jsrcerr.num_warnings + jdsterr.num_warnings ?EXIT_WARNING:EXIT_SUCCESS); + + return 0; /* suppress no-return-value warnings */ + +} + diff --git a/utils/roq2/jpeg/jquant1.c b/utils/roq2/jpeg/jquant1.c new file mode 100644 index 0000000..f02e4dc --- /dev/null +++ b/utils/roq2/jpeg/jquant1.c @@ -0,0 +1,1712 @@ +/* + + * jquant1.c + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains 1-pass color quantization (color mapping) routines. + + * These routines provide mapping to a fixed color map using equally spaced + + * color values. Optional Floyd-Steinberg or ordered dithering is available. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + +#ifdef QUANT_1PASS_SUPPORTED + + + + + +/* + + * The main purpose of 1-pass quantization is to provide a fast, if not very + + * high quality, colormapped output capability. A 2-pass quantizer usually + + * gives better visual quality; however, for quantized grayscale output this + + * quantizer is perfectly adequate. Dithering is highly recommended with this + + * quantizer, though you can turn it off if you really want to. + + * + + * In 1-pass quantization the colormap must be chosen in advance of seeing the + + * image. We use a map consisting of all combinations of Ncolors[i] color + + * values for the i'th component. The Ncolors[] values are chosen so that + + * their product, the total number of colors, is no more than that requested. + + * (In most cases, the product will be somewhat less.) + + * + + * Since the colormap is orthogonal, the representative value for each color + + * component can be determined without considering the other components; + + * then these indexes can be combined into a colormap index by a standard + + * N-dimensional-array-subscript calculation. Most of the arithmetic involved + + * can be precalculated and stored in the lookup table colorindex[]. + + * colorindex[i][j] maps pixel value j in component i to the nearest + + * representative value (grid plane) for that component; this index is + + * multiplied by the array stride for component i, so that the + + * index of the colormap entry closest to a given pixel value is just + + * sum( colorindex[component-number][pixel-component-value] ) + + * Aside from being fast, this scheme allows for variable spacing between + + * representative values with no additional lookup cost. + + * + + * If gamma correction has been applied in color conversion, it might be wise + + * to adjust the color grid spacing so that the representative colors are + + * equidistant in linear space. At this writing, gamma correction is not + + * implemented by jdcolor, so nothing is done here. + + */ + + + + + +/* Declarations for ordered dithering. + + * + + * We use a standard 16x16 ordered dither array. The basic concept of ordered + + * dithering is described in many references, for instance Dale Schumacher's + + * chapter II.2 of Graphics Gems II (James Arvo, ed. Academic Press, 1991). + + * In place of Schumacher's comparisons against a "threshold" value, we add a + + * "dither" value to the input pixel and then round the result to the nearest + + * output value. The dither value is equivalent to (0.5 - threshold) times + + * the distance between output values. For ordered dithering, we assume that + + * the output colors are equally spaced; if not, results will probably be + + * worse, since the dither may be too much or too little at a given point. + + * + + * The normal calculation would be to form pixel value + dither, range-limit + + * this to 0..MAXJSAMPLE, and then index into the colorindex table as usual. + + * We can skip the separate range-limiting step by extending the colorindex + + * table in both directions. + + */ + + + +#define ODITHER_SIZE 16 /* dimension of dither matrix */ + +/* NB: if ODITHER_SIZE is not a power of 2, ODITHER_MASK uses will break */ + +#define ODITHER_CELLS (ODITHER_SIZE*ODITHER_SIZE) /* # cells in matrix */ + +#define ODITHER_MASK (ODITHER_SIZE-1) /* mask for wrapping around counters */ + + + +typedef int ODITHER_MATRIX[ODITHER_SIZE][ODITHER_SIZE]; + +typedef int (*ODITHER_MATRIX_PTR)[ODITHER_SIZE]; + + + +static const UINT8 base_dither_matrix[ODITHER_SIZE][ODITHER_SIZE] = { + + /* Bayer's order-4 dither array. Generated by the code given in + + * Stephen Hawley's article "Ordered Dithering" in Graphics Gems I. + + * The values in this array must range from 0 to ODITHER_CELLS-1. + + */ + + { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 }, + + { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 }, + + { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 }, + + { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 }, + + { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 }, + + { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 }, + + { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 }, + + { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 }, + + { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 }, + + { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 }, + + { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 }, + + { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 }, + + { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 }, + + { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 }, + + { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 }, + + { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 } + +}; + + + + + +/* Declarations for Floyd-Steinberg dithering. + + * + + * Errors are accumulated into the array fserrors[], at a resolution of + + * 1/16th of a pixel count. The error at a given pixel is propagated + + * to its not-yet-processed neighbors using the standard F-S fractions, + + * ... (here) 7/16 + + * 3/16 5/16 1/16 + + * We work left-to-right on even rows, right-to-left on odd rows. + + * + + * We can get away with a single array (holding one row's worth of errors) + + * by using it to store the current row's errors at pixel columns not yet + + * processed, but the next row's errors at columns already processed. We + + * need only a few extra variables to hold the errors immediately around the + + * current column. (If we are lucky, those variables are in registers, but + + * even if not, they're probably cheaper to access than array elements are.) + + * + + * The fserrors[] array is indexed [component#][position]. + + * We provide (#columns + 2) entries per component; the extra entry at each + + * end saves us from special-casing the first and last pixels. + + * + + * Note: on a wide image, we might not have enough room in a PC's near data + + * segment to hold the error array; so it is allocated with alloc_large. + + */ + + + +#if BITS_IN_JSAMPLE == 8 + +typedef INT16 FSERROR; /* 16 bits should be enough */ + +typedef int LOCFSERROR; /* use 'int' for calculation temps */ + +#else + +typedef INT32 FSERROR; /* may need more than 16 bits */ + +typedef INT32 LOCFSERROR; /* be sure calculation temps are big enough */ + +#endif + + + +typedef FSERROR FAR *FSERRPTR; /* pointer to error array (in FAR storage!) */ + + + + + +/* Private subobject */ + + + +#define MAX_Q_COMPS 4 /* max components I can handle */ + + + +typedef struct { + + struct jpeg_color_quantizer pub; /* public fields */ + + + + /* Initially allocated colormap is saved here */ + + JSAMPARRAY sv_colormap; /* The color map as a 2-D pixel array */ + + int sv_actual; /* number of entries in use */ + + + + JSAMPARRAY colorindex; /* Precomputed mapping for speed */ + + /* colorindex[i][j] = index of color closest to pixel value j in component i, + + * premultiplied as described above. Since colormap indexes must fit into + + * JSAMPLEs, the entries of this array will too. + + */ + + boolean is_padded; /* is the colorindex padded for odither? */ + + + + int Ncolors[MAX_Q_COMPS]; /* # of values alloced to each component */ + + + + /* Variables for ordered dithering */ + + int row_index; /* cur row's vertical index in dither matrix */ + + ODITHER_MATRIX_PTR odither[MAX_Q_COMPS]; /* one dither array per component */ + + + + /* Variables for Floyd-Steinberg dithering */ + + FSERRPTR fserrors[MAX_Q_COMPS]; /* accumulated errors */ + + boolean on_odd_row; /* flag to remember which row we are on */ + +} my_cquantizer; + + + +typedef my_cquantizer * my_cquantize_ptr; + + + + + +/* + + * Policy-making subroutines for create_colormap and create_colorindex. + + * These routines determine the colormap to be used. The rest of the module + + * only assumes that the colormap is orthogonal. + + * + + * * select_ncolors decides how to divvy up the available colors + + * among the components. + + * * output_value defines the set of representative values for a component. + + * * largest_input_value defines the mapping from input values to + + * representative values for a component. + + * Note that the latter two routines may impose different policies for + + * different components, though this is not currently done. + + */ + + + + + +LOCAL int + +select_ncolors (j_decompress_ptr cinfo, int Ncolors[]) + +/* Determine allocation of desired colors to components, */ + +/* and fill in Ncolors[] array to indicate choice. */ + +/* Return value is total number of colors (product of Ncolors[] values). */ + +{ + + int nc = cinfo->out_color_components; /* number of color components */ + + int max_colors = cinfo->desired_number_of_colors; + + int total_colors, iroot, i, j; + + boolean changed; + + long temp; + + static const int RGB_order[3] = { RGB_GREEN, RGB_RED, RGB_BLUE }; + + + + /* We can allocate at least the nc'th root of max_colors per component. */ + + /* Compute floor(nc'th root of max_colors). */ + + iroot = 1; + + do { + + iroot++; + + temp = iroot; /* set temp = iroot ** nc */ + + for (i = 1; i < nc; i++) + + temp *= iroot; + + } while (temp <= (long) max_colors); /* repeat till iroot exceeds root */ + + iroot--; /* now iroot = floor(root) */ + + + + /* Must have at least 2 color values per component */ + + if (iroot < 2) + + ERREXIT1(cinfo, JERR_QUANT_FEW_COLORS, (int) temp); + + + + /* Initialize to iroot color values for each component */ + + total_colors = 1; + + for (i = 0; i < nc; i++) { + + Ncolors[i] = iroot; + + total_colors *= iroot; + + } + + /* We may be able to increment the count for one or more components without + + * exceeding max_colors, though we know not all can be incremented. + + * Sometimes, the first component can be incremented more than once! + + * (Example: for 16 colors, we start at 2*2*2, go to 3*2*2, then 4*2*2.) + + * In RGB colorspace, try to increment G first, then R, then B. + + */ + + do { + + changed = FALSE; + + for (i = 0; i < nc; i++) { + + j = (cinfo->out_color_space == JCS_RGB ? RGB_order[i] : i); + + /* calculate new total_colors if Ncolors[j] is incremented */ + + temp = total_colors / Ncolors[j]; + + temp *= Ncolors[j]+1; /* done in long arith to avoid oflo */ + + if (temp > (long) max_colors) + + break; /* won't fit, done with this pass */ + + Ncolors[j]++; /* OK, apply the increment */ + + total_colors = (int) temp; + + changed = TRUE; + + } + + } while (changed); + + + + return total_colors; + +} + + + + + +LOCAL int + +output_value (j_decompress_ptr cinfo, int ci, int j, int maxj) + +/* Return j'th output value, where j will range from 0 to maxj */ + +/* The output values must fall in 0..MAXJSAMPLE in increasing order */ + +{ + + /* We always provide values 0 and MAXJSAMPLE for each component; + + * any additional values are equally spaced between these limits. + + * (Forcing the upper and lower values to the limits ensures that + + * dithering can't produce a color outside the selected gamut.) + + */ + + return (int) (((INT32) j * MAXJSAMPLE + maxj/2) / maxj); + +} + + + + + +LOCAL int + +largest_input_value (j_decompress_ptr cinfo, int ci, int j, int maxj) + +/* Return largest input value that should map to j'th output value */ + +/* Must have largest(j=0) >= 0, and largest(j=maxj) >= MAXJSAMPLE */ + +{ + + /* Breakpoints are halfway between values returned by output_value */ + + return (int) (((INT32) (2*j + 1) * MAXJSAMPLE + maxj) / (2*maxj)); + +} + + + + + +/* + + * Create the colormap. + + */ + + + +LOCAL void + +create_colormap (j_decompress_ptr cinfo) + +{ + + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + JSAMPARRAY colormap; /* Created colormap */ + + int total_colors; /* Number of distinct output colors */ + + int i,j,k, nci, blksize, blkdist, ptr, val; + + + + /* Select number of colors for each component */ + + total_colors = select_ncolors(cinfo, cquantize->Ncolors); + + + + /* Report selected color counts */ + + if (cinfo->out_color_components == 3) + + TRACEMS4(cinfo, 1, JTRC_QUANT_3_NCOLORS, + + total_colors, cquantize->Ncolors[0], + + cquantize->Ncolors[1], cquantize->Ncolors[2]); + + else + + TRACEMS1(cinfo, 1, JTRC_QUANT_NCOLORS, total_colors); + + + + /* Allocate and fill in the colormap. */ + + /* The colors are ordered in the map in standard row-major order, */ + + /* i.e. rightmost (highest-indexed) color changes most rapidly. */ + + + + colormap = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (JDIMENSION) total_colors, (JDIMENSION) cinfo->out_color_components); + + + + /* blksize is number of adjacent repeated entries for a component */ + + /* blkdist is distance between groups of identical entries for a component */ + + blkdist = total_colors; + + + + for (i = 0; i < cinfo->out_color_components; i++) { + + /* fill in colormap entries for i'th color component */ + + nci = cquantize->Ncolors[i]; /* # of distinct values for this color */ + + blksize = blkdist / nci; + + for (j = 0; j < nci; j++) { + + /* Compute j'th output value (out of nci) for component */ + + val = output_value(cinfo, i, j, nci-1); + + /* Fill in all colormap entries that have this value of this component */ + + for (ptr = j * blksize; ptr < total_colors; ptr += blkdist) { + + /* fill in blksize entries beginning at ptr */ + + for (k = 0; k < blksize; k++) + + colormap[i][ptr+k] = (JSAMPLE) val; + + } + + } + + blkdist = blksize; /* blksize of this color is blkdist of next */ + + } + + + + /* Save the colormap in private storage, + + * where it will survive color quantization mode changes. + + */ + + cquantize->sv_colormap = colormap; + + cquantize->sv_actual = total_colors; + +} + + + + + +/* + + * Create the color index table. + + */ + + + +LOCAL void + +create_colorindex (j_decompress_ptr cinfo) + +{ + + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + JSAMPROW indexptr; + + int i,j,k, nci, blksize, val, pad; + + + + /* For ordered dither, we pad the color index tables by MAXJSAMPLE in + + * each direction (input index values can be -MAXJSAMPLE .. 2*MAXJSAMPLE). + + * This is not necessary in the other dithering modes. However, we + + * flag whether it was done in case user changes dithering mode. + + */ + + if (cinfo->dither_mode == JDITHER_ORDERED) { + + pad = MAXJSAMPLE*2; + + cquantize->is_padded = TRUE; + + } else { + + pad = 0; + + cquantize->is_padded = FALSE; + + } + + + + cquantize->colorindex = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (JDIMENSION) (MAXJSAMPLE+1 + pad), + + (JDIMENSION) cinfo->out_color_components); + + + + /* blksize is number of adjacent repeated entries for a component */ + + blksize = cquantize->sv_actual; + + + + for (i = 0; i < cinfo->out_color_components; i++) { + + /* fill in colorindex entries for i'th color component */ + + nci = cquantize->Ncolors[i]; /* # of distinct values for this color */ + + blksize = blksize / nci; + + + + /* adjust colorindex pointers to provide padding at negative indexes. */ + + if (pad) + + cquantize->colorindex[i] += MAXJSAMPLE; + + + + /* in loop, val = index of current output value, */ + + /* and k = largest j that maps to current val */ + + indexptr = cquantize->colorindex[i]; + + val = 0; + + k = largest_input_value(cinfo, i, 0, nci-1); + + for (j = 0; j <= MAXJSAMPLE; j++) { + + while (j > k) /* advance val if past boundary */ + + k = largest_input_value(cinfo, i, ++val, nci-1); + + /* premultiply so that no multiplication needed in main processing */ + + indexptr[j] = (JSAMPLE) (val * blksize); + + } + + /* Pad at both ends if necessary */ + + if (pad) + + for (j = 1; j <= MAXJSAMPLE; j++) { + + indexptr[-j] = indexptr[0]; + + indexptr[MAXJSAMPLE+j] = indexptr[MAXJSAMPLE]; + + } + + } + +} + + + + + +/* + + * Create an ordered-dither array for a component having ncolors + + * distinct output values. + + */ + + + +LOCAL ODITHER_MATRIX_PTR + +make_odither_array (j_decompress_ptr cinfo, int ncolors) + +{ + + ODITHER_MATRIX_PTR odither; + + int j,k; + + INT32 num,den; + + + + odither = (ODITHER_MATRIX_PTR) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(ODITHER_MATRIX)); + + /* The inter-value distance for this color is MAXJSAMPLE/(ncolors-1). + + * Hence the dither value for the matrix cell with fill order f + + * (f=0..N-1) should be (N-1-2*f)/(2*N) * MAXJSAMPLE/(ncolors-1). + + * On 16-bit-int machine, be careful to avoid overflow. + + */ + + den = 2 * ODITHER_CELLS * ((INT32) (ncolors - 1)); + + for (j = 0; j < ODITHER_SIZE; j++) { + + for (k = 0; k < ODITHER_SIZE; k++) { + + num = ((INT32) (ODITHER_CELLS-1 - 2*((int)base_dither_matrix[j][k]))) + + * MAXJSAMPLE; + + /* Ensure round towards zero despite C's lack of consistency + + * about rounding negative values in integer division... + + */ + + odither[j][k] = (int) (num<0 ? -((-num)/den) : num/den); + + } + + } + + return odither; + +} + + + + + +/* + + * Create the ordered-dither tables. + + * Components having the same number of representative colors may + + * share a dither table. + + */ + + + +LOCAL void + +create_odither_tables (j_decompress_ptr cinfo) + +{ + + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + ODITHER_MATRIX_PTR odither; + + int i, j, nci; + + + + for (i = 0; i < cinfo->out_color_components; i++) { + + nci = cquantize->Ncolors[i]; /* # of distinct values for this color */ + + odither = NULL; /* search for matching prior component */ + + for (j = 0; j < i; j++) { + + if (nci == cquantize->Ncolors[j]) { + + odither = cquantize->odither[j]; + + break; + + } + + } + + if (odither == NULL) /* need a new table? */ + + odither = make_odither_array(cinfo, nci); + + cquantize->odither[i] = odither; + + } + +} + + + + + +/* + + * Map some rows of pixels to the output colormapped representation. + + */ + + + +METHODDEF void + +color_quantize (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + + JSAMPARRAY output_buf, int num_rows) + +/* General case, no dithering */ + +{ + + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + JSAMPARRAY colorindex = cquantize->colorindex; + + register int pixcode, ci; + + register JSAMPROW ptrin, ptrout; + + int row; + + JDIMENSION col; + + JDIMENSION width = cinfo->output_width; + + register int nc = cinfo->out_color_components; + + + + for (row = 0; row < num_rows; row++) { + + ptrin = input_buf[row]; + + ptrout = output_buf[row]; + + for (col = width; col > 0; col--) { + + pixcode = 0; + + for (ci = 0; ci < nc; ci++) { + + pixcode += GETJSAMPLE(colorindex[ci][GETJSAMPLE(*ptrin++)]); + + } + + *ptrout++ = (JSAMPLE) pixcode; + + } + + } + +} + + + + + +METHODDEF void + +color_quantize3 (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + + JSAMPARRAY output_buf, int num_rows) + +/* Fast path for out_color_components==3, no dithering */ + +{ + + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + register int pixcode; + + register JSAMPROW ptrin, ptrout; + + JSAMPROW colorindex0 = cquantize->colorindex[0]; + + JSAMPROW colorindex1 = cquantize->colorindex[1]; + + JSAMPROW colorindex2 = cquantize->colorindex[2]; + + int row; + + JDIMENSION col; + + JDIMENSION width = cinfo->output_width; + + + + for (row = 0; row < num_rows; row++) { + + ptrin = input_buf[row]; + + ptrout = output_buf[row]; + + for (col = width; col > 0; col--) { + + pixcode = GETJSAMPLE(colorindex0[GETJSAMPLE(*ptrin++)]); + + pixcode += GETJSAMPLE(colorindex1[GETJSAMPLE(*ptrin++)]); + + pixcode += GETJSAMPLE(colorindex2[GETJSAMPLE(*ptrin++)]); + + *ptrout++ = (JSAMPLE) pixcode; + + } + + } + +} + + + + + +METHODDEF void + +quantize_ord_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + + JSAMPARRAY output_buf, int num_rows) + +/* General case, with ordered dithering */ + +{ + + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + register JSAMPROW input_ptr; + + register JSAMPROW output_ptr; + + JSAMPROW colorindex_ci; + + int * dither; /* points to active row of dither matrix */ + + int row_index, col_index; /* current indexes into dither matrix */ + + int nc = cinfo->out_color_components; + + int ci; + + int row; + + JDIMENSION col; + + JDIMENSION width = cinfo->output_width; + + + + for (row = 0; row < num_rows; row++) { + + /* Initialize output values to 0 so can process components separately */ + + jzero_far((void FAR *) output_buf[row], + + (size_t) (width * SIZEOF(JSAMPLE))); + + row_index = cquantize->row_index; + + for (ci = 0; ci < nc; ci++) { + + input_ptr = input_buf[row] + ci; + + output_ptr = output_buf[row]; + + colorindex_ci = cquantize->colorindex[ci]; + + dither = cquantize->odither[ci][row_index]; + + col_index = 0; + + + + for (col = width; col > 0; col--) { + + /* Form pixel value + dither, range-limit to 0..MAXJSAMPLE, + + * select output value, accumulate into output code for this pixel. + + * Range-limiting need not be done explicitly, as we have extended + + * the colorindex table to produce the right answers for out-of-range + + * inputs. The maximum dither is +- MAXJSAMPLE; this sets the + + * required amount of padding. + + */ + + *output_ptr += colorindex_ci[GETJSAMPLE(*input_ptr)+dither[col_index]]; + + input_ptr += nc; + + output_ptr++; + + col_index = (col_index + 1) & ODITHER_MASK; + + } + + } + + /* Advance row index for next row */ + + row_index = (row_index + 1) & ODITHER_MASK; + + cquantize->row_index = row_index; + + } + +} + + + + + +METHODDEF void + +quantize3_ord_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + + JSAMPARRAY output_buf, int num_rows) + +/* Fast path for out_color_components==3, with ordered dithering */ + +{ + + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + register int pixcode; + + register JSAMPROW input_ptr; + + register JSAMPROW output_ptr; + + JSAMPROW colorindex0 = cquantize->colorindex[0]; + + JSAMPROW colorindex1 = cquantize->colorindex[1]; + + JSAMPROW colorindex2 = cquantize->colorindex[2]; + + int * dither0; /* points to active row of dither matrix */ + + int * dither1; + + int * dither2; + + int row_index, col_index; /* current indexes into dither matrix */ + + int row; + + JDIMENSION col; + + JDIMENSION width = cinfo->output_width; + + + + for (row = 0; row < num_rows; row++) { + + row_index = cquantize->row_index; + + input_ptr = input_buf[row]; + + output_ptr = output_buf[row]; + + dither0 = cquantize->odither[0][row_index]; + + dither1 = cquantize->odither[1][row_index]; + + dither2 = cquantize->odither[2][row_index]; + + col_index = 0; + + + + for (col = width; col > 0; col--) { + + pixcode = GETJSAMPLE(colorindex0[GETJSAMPLE(*input_ptr++) + + + dither0[col_index]]); + + pixcode += GETJSAMPLE(colorindex1[GETJSAMPLE(*input_ptr++) + + + dither1[col_index]]); + + pixcode += GETJSAMPLE(colorindex2[GETJSAMPLE(*input_ptr++) + + + dither2[col_index]]); + + *output_ptr++ = (JSAMPLE) pixcode; + + col_index = (col_index + 1) & ODITHER_MASK; + + } + + row_index = (row_index + 1) & ODITHER_MASK; + + cquantize->row_index = row_index; + + } + +} + + + + + +METHODDEF void + +quantize_fs_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + + JSAMPARRAY output_buf, int num_rows) + +/* General case, with Floyd-Steinberg dithering */ + +{ + + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + register LOCFSERROR cur; /* current error or pixel value */ + + LOCFSERROR belowerr; /* error for pixel below cur */ + + LOCFSERROR bpreverr; /* error for below/prev col */ + + LOCFSERROR bnexterr; /* error for below/next col */ + + LOCFSERROR delta; + + register FSERRPTR errorptr; /* => fserrors[] at column before current */ + + register JSAMPROW input_ptr; + + register JSAMPROW output_ptr; + + JSAMPROW colorindex_ci; + + JSAMPROW colormap_ci; + + int pixcode; + + int nc = cinfo->out_color_components; + + int dir; /* 1 for left-to-right, -1 for right-to-left */ + + int dirnc; /* dir * nc */ + + int ci; + + int row; + + JDIMENSION col; + + JDIMENSION width = cinfo->output_width; + + JSAMPLE *range_limit = cinfo->sample_range_limit; + + SHIFT_TEMPS + + + + for (row = 0; row < num_rows; row++) { + + /* Initialize output values to 0 so can process components separately */ + + jzero_far((void FAR *) output_buf[row], + + (size_t) (width * SIZEOF(JSAMPLE))); + + for (ci = 0; ci < nc; ci++) { + + input_ptr = input_buf[row] + ci; + + output_ptr = output_buf[row]; + + if (cquantize->on_odd_row) { + + /* work right to left in this row */ + + input_ptr += (width-1) * nc; /* so point to rightmost pixel */ + + output_ptr += width-1; + + dir = -1; + + dirnc = -nc; + + errorptr = cquantize->fserrors[ci] + (width+1); /* => entry after last column */ + + } else { + + /* work left to right in this row */ + + dir = 1; + + dirnc = nc; + + errorptr = cquantize->fserrors[ci]; /* => entry before first column */ + + } + + colorindex_ci = cquantize->colorindex[ci]; + + colormap_ci = cquantize->sv_colormap[ci]; + + /* Preset error values: no error propagated to first pixel from left */ + + cur = 0; + + /* and no error propagated to row below yet */ + + belowerr = bpreverr = 0; + + + + for (col = width; col > 0; col--) { + + /* cur holds the error propagated from the previous pixel on the + + * current line. Add the error propagated from the previous line + + * to form the complete error correction term for this pixel, and + + * round the error term (which is expressed * 16) to an integer. + + * RIGHT_SHIFT rounds towards minus infinity, so adding 8 is correct + + * for either sign of the error value. + + * Note: errorptr points to *previous* column's array entry. + + */ + + cur = RIGHT_SHIFT(cur + errorptr[dir] + 8, 4); + + /* Form pixel value + error, and range-limit to 0..MAXJSAMPLE. + + * The maximum error is +- MAXJSAMPLE; this sets the required size + + * of the range_limit array. + + */ + + cur += GETJSAMPLE(*input_ptr); + + cur = GETJSAMPLE(range_limit[cur]); + + /* Select output value, accumulate into output code for this pixel */ + + pixcode = GETJSAMPLE(colorindex_ci[cur]); + + *output_ptr += (JSAMPLE) pixcode; + + /* Compute actual representation error at this pixel */ + + /* Note: we can do this even though we don't have the final */ + + /* pixel code, because the colormap is orthogonal. */ + + cur -= GETJSAMPLE(colormap_ci[pixcode]); + + /* Compute error fractions to be propagated to adjacent pixels. + + * Add these into the running sums, and simultaneously shift the + + * next-line error sums left by 1 column. + + */ + + bnexterr = cur; + + delta = cur * 2; + + cur += delta; /* form error * 3 */ + + errorptr[0] = (FSERROR) (bpreverr + cur); + + cur += delta; /* form error * 5 */ + + bpreverr = belowerr + cur; + + belowerr = bnexterr; + + cur += delta; /* form error * 7 */ + + /* At this point cur contains the 7/16 error value to be propagated + + * to the next pixel on the current line, and all the errors for the + + * next line have been shifted over. We are therefore ready to move on. + + */ + + input_ptr += dirnc; /* advance input ptr to next column */ + + output_ptr += dir; /* advance output ptr to next column */ + + errorptr += dir; /* advance errorptr to current column */ + + } + + /* Post-loop cleanup: we must unload the final error value into the + + * final fserrors[] entry. Note we need not unload belowerr because + + * it is for the dummy column before or after the actual array. + + */ + + errorptr[0] = (FSERROR) bpreverr; /* unload prev err into array */ + + } + + cquantize->on_odd_row = (cquantize->on_odd_row ? FALSE : TRUE); + + } + +} + + + + + +/* + + * Allocate workspace for Floyd-Steinberg errors. + + */ + + + +LOCAL void + +alloc_fs_workspace (j_decompress_ptr cinfo) + +{ + + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + size_t arraysize; + + int i; + + + + arraysize = (size_t) ((cinfo->output_width + 2) * SIZEOF(FSERROR)); + + for (i = 0; i < cinfo->out_color_components; i++) { + + cquantize->fserrors[i] = (FSERRPTR) + + (*cinfo->mem->alloc_large)((j_common_ptr) cinfo, JPOOL_IMAGE, arraysize); + + } + +} + + + + + +/* + + * Initialize for one-pass color quantization. + + */ + + + +METHODDEF void + +start_pass_1_quant (j_decompress_ptr cinfo, boolean is_pre_scan) + +{ + + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + size_t arraysize; + + int i; + + + + /* Install my colormap. */ + + cinfo->colormap = cquantize->sv_colormap; + + cinfo->actual_number_of_colors = cquantize->sv_actual; + + + + /* Initialize for desired dithering mode. */ + + switch (cinfo->dither_mode) { + + case JDITHER_NONE: + + if (cinfo->out_color_components == 3) + + cquantize->pub.color_quantize = color_quantize3; + + else + + cquantize->pub.color_quantize = color_quantize; + + break; + + case JDITHER_ORDERED: + + if (cinfo->out_color_components == 3) + + cquantize->pub.color_quantize = quantize3_ord_dither; + + else + + cquantize->pub.color_quantize = quantize_ord_dither; + + cquantize->row_index = 0; /* initialize state for ordered dither */ + + /* If user changed to ordered dither from another mode, + + * we must recreate the color index table with padding. + + * This will cost extra space, but probably isn't very likely. + + */ + + if (! cquantize->is_padded) + + create_colorindex(cinfo); + + /* Create ordered-dither tables if we didn't already. */ + + if (cquantize->odither[0] == NULL) + + create_odither_tables(cinfo); + + break; + + case JDITHER_FS: + + cquantize->pub.color_quantize = quantize_fs_dither; + + cquantize->on_odd_row = FALSE; /* initialize state for F-S dither */ + + /* Allocate Floyd-Steinberg workspace if didn't already. */ + + if (cquantize->fserrors[0] == NULL) + + alloc_fs_workspace(cinfo); + + /* Initialize the propagated errors to zero. */ + + arraysize = (size_t) ((cinfo->output_width + 2) * SIZEOF(FSERROR)); + + for (i = 0; i < cinfo->out_color_components; i++) + + jzero_far((void FAR *) cquantize->fserrors[i], arraysize); + + break; + + default: + + ERREXIT(cinfo, JERR_NOT_COMPILED); + + break; + + } + +} + + + + + +/* + + * Finish up at the end of the pass. + + */ + + + +METHODDEF void + +finish_pass_1_quant (j_decompress_ptr cinfo) + +{ + + /* no work in 1-pass case */ + +} + + + + + +/* + + * Switch to a new external colormap between output passes. + + * Shouldn't get to this module! + + */ + + + +METHODDEF void + +new_color_map_1_quant (j_decompress_ptr cinfo) + +{ + + ERREXIT(cinfo, JERR_MODE_CHANGE); + +} + + + + + +/* + + * Module initialization routine for 1-pass color quantization. + + */ + + + +GLOBAL void + +jinit_1pass_quantizer (j_decompress_ptr cinfo) + +{ + + my_cquantize_ptr cquantize; + + + + cquantize = (my_cquantize_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(my_cquantizer)); + + cinfo->cquantize = (struct jpeg_color_quantizer *) cquantize; + + cquantize->pub.start_pass = start_pass_1_quant; + + cquantize->pub.finish_pass = finish_pass_1_quant; + + cquantize->pub.new_color_map = new_color_map_1_quant; + + cquantize->fserrors[0] = NULL; /* Flag FS workspace not allocated */ + + cquantize->odither[0] = NULL; /* Also flag odither arrays not allocated */ + + + + /* Make sure my internal arrays won't overflow */ + + if (cinfo->out_color_components > MAX_Q_COMPS) + + ERREXIT1(cinfo, JERR_QUANT_COMPONENTS, MAX_Q_COMPS); + + /* Make sure colormap indexes can be represented by JSAMPLEs */ + + if (cinfo->desired_number_of_colors > (MAXJSAMPLE+1)) + + ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, MAXJSAMPLE+1); + + + + /* Create the colormap and color index table. */ + + create_colormap(cinfo); + + create_colorindex(cinfo); + + + + /* Allocate Floyd-Steinberg workspace now if requested. + + * We do this now since it is FAR storage and may affect the memory + + * manager's space calculations. If the user changes to FS dither + + * mode in a later pass, we will allocate the space then, and will + + * possibly overrun the max_memory_to_use setting. + + */ + + if (cinfo->dither_mode == JDITHER_FS) + + alloc_fs_workspace(cinfo); + +} + + + +#endif /* QUANT_1PASS_SUPPORTED */ + diff --git a/utils/roq2/jpeg/jquant2.c b/utils/roq2/jpeg/jquant2.c new file mode 100644 index 0000000..349cdc6 --- /dev/null +++ b/utils/roq2/jpeg/jquant2.c @@ -0,0 +1,2620 @@ +/* + + * jquant2.c + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains 2-pass color quantization (color mapping) routines. + + * These routines provide selection of a custom color map for an image, + + * followed by mapping of the image to that color map, with optional + + * Floyd-Steinberg dithering. + + * It is also possible to use just the second pass to map to an arbitrary + + * externally-given color map. + + * + + * Note: ordered dithering is not supported, since there isn't any fast + + * way to compute intercolor distances; it's unclear that ordered dither's + + * fundamental assumptions even hold with an irregularly spaced color map. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + +#ifdef QUANT_2PASS_SUPPORTED + + + + + +/* + + * This module implements the well-known Heckbert paradigm for color + + * quantization. Most of the ideas used here can be traced back to + + * Heckbert's seminal paper + + * Heckbert, Paul. "Color Image Quantization for Frame Buffer Display", + + * Proc. SIGGRAPH '82, Computer Graphics v.16 #3 (July 1982), pp 297-304. + + * + + * In the first pass over the image, we accumulate a histogram showing the + + * usage count of each possible color. To keep the histogram to a reasonable + + * size, we reduce the precision of the input; typical practice is to retain + + * 5 or 6 bits per color, so that 8 or 4 different input values are counted + + * in the same histogram cell. + + * + + * Next, the color-selection step begins with a box representing the whole + + * color space, and repeatedly splits the "largest" remaining box until we + + * have as many boxes as desired colors. Then the mean color in each + + * remaining box becomes one of the possible output colors. + + * + + * The second pass over the image maps each input pixel to the closest output + + * color (optionally after applying a Floyd-Steinberg dithering correction). + + * This mapping is logically trivial, but making it go fast enough requires + + * considerable care. + + * + + * Heckbert-style quantizers vary a good deal in their policies for choosing + + * the "largest" box and deciding where to cut it. The particular policies + + * used here have proved out well in experimental comparisons, but better ones + + * may yet be found. + + * + + * In earlier versions of the IJG code, this module quantized in YCbCr color + + * space, processing the raw upsampled data without a color conversion step. + + * This allowed the color conversion math to be done only once per colormap + + * entry, not once per pixel. However, that optimization precluded other + + * useful optimizations (such as merging color conversion with upsampling) + + * and it also interfered with desired capabilities such as quantizing to an + + * externally-supplied colormap. We have therefore abandoned that approach. + + * The present code works in the post-conversion color space, typically RGB. + + * + + * To improve the visual quality of the results, we actually work in scaled + + * RGB space, giving G distances more weight than R, and R in turn more than + + * B. To do everything in integer math, we must use integer scale factors. + + * The 2/3/1 scale factors used here correspond loosely to the relative + + * weights of the colors in the NTSC grayscale equation. + + * If you want to use this code to quantize a non-RGB color space, you'll + + * probably need to change these scale factors. + + */ + + + +#define R_SCALE 2 /* scale R distances by this much */ + +#define G_SCALE 3 /* scale G distances by this much */ + +#define B_SCALE 1 /* and B by this much */ + + + +/* Relabel R/G/B as components 0/1/2, respecting the RGB ordering defined + + * in jmorecfg.h. As the code stands, it will do the right thing for R,G,B + + * and B,G,R orders. If you define some other weird order in jmorecfg.h, + + * you'll get compile errors until you extend this logic. In that case + + * you'll probably want to tweak the histogram sizes too. + + */ + + + +#if RGB_RED == 0 + +#define C0_SCALE R_SCALE + +#endif + +#if RGB_BLUE == 0 + +#define C0_SCALE B_SCALE + +#endif + +#if RGB_GREEN == 1 + +#define C1_SCALE G_SCALE + +#endif + +#if RGB_RED == 2 + +#define C2_SCALE R_SCALE + +#endif + +#if RGB_BLUE == 2 + +#define C2_SCALE B_SCALE + +#endif + + + + + +/* + + * First we have the histogram data structure and routines for creating it. + + * + + * The number of bits of precision can be adjusted by changing these symbols. + + * We recommend keeping 6 bits for G and 5 each for R and B. + + * If you have plenty of memory and cycles, 6 bits all around gives marginally + + * better results; if you are short of memory, 5 bits all around will save + + * some space but degrade the results. + + * To maintain a fully accurate histogram, we'd need to allocate a "long" + + * (preferably unsigned long) for each cell. In practice this is overkill; + + * we can get by with 16 bits per cell. Few of the cell counts will overflow, + + * and clamping those that do overflow to the maximum value will give close- + + * enough results. This reduces the recommended histogram size from 256Kb + + * to 128Kb, which is a useful savings on PC-class machines. + + * (In the second pass the histogram space is re-used for pixel mapping data; + + * in that capacity, each cell must be able to store zero to the number of + + * desired colors. 16 bits/cell is plenty for that too.) + + * Since the JPEG code is intended to run in small memory model on 80x86 + + * machines, we can't just allocate the histogram in one chunk. Instead + + * of a true 3-D array, we use a row of pointers to 2-D arrays. Each + + * pointer corresponds to a C0 value (typically 2^5 = 32 pointers) and + + * each 2-D array has 2^6*2^5 = 2048 or 2^6*2^6 = 4096 entries. Note that + + * on 80x86 machines, the pointer row is in near memory but the actual + + * arrays are in far memory (same arrangement as we use for image arrays). + + */ + + + +#define MAXNUMCOLORS (MAXJSAMPLE+1) /* maximum size of colormap */ + + + +/* These will do the right thing for either R,G,B or B,G,R color order, + + * but you may not like the results for other color orders. + + */ + +#define HIST_C0_BITS 5 /* bits of precision in R/B histogram */ + +#define HIST_C1_BITS 6 /* bits of precision in G histogram */ + +#define HIST_C2_BITS 5 /* bits of precision in B/R histogram */ + + + +/* Number of elements along histogram axes. */ + +#define HIST_C0_ELEMS (1<cquantize; + + register JSAMPROW ptr; + + register histptr histp; + + register hist3d histogram = cquantize->histogram; + + int row; + + JDIMENSION col; + + JDIMENSION width = cinfo->output_width; + + + + for (row = 0; row < num_rows; row++) { + + ptr = input_buf[row]; + + for (col = width; col > 0; col--) { + + /* get pixel value and index into the histogram */ + + histp = & histogram[GETJSAMPLE(ptr[0]) >> C0_SHIFT] + + [GETJSAMPLE(ptr[1]) >> C1_SHIFT] + + [GETJSAMPLE(ptr[2]) >> C2_SHIFT]; + + /* increment, check for overflow and undo increment if so. */ + + if (++(*histp) <= 0) + + (*histp)--; + + ptr += 3; + + } + + } + +} + + + + + +/* + + * Next we have the really interesting routines: selection of a colormap + + * given the completed histogram. + + * These routines work with a list of "boxes", each representing a rectangular + + * subset of the input color space (to histogram precision). + + */ + + + +typedef struct { + + /* The bounds of the box (inclusive); expressed as histogram indexes */ + + int c0min, c0max; + + int c1min, c1max; + + int c2min, c2max; + + /* The volume (actually 2-norm) of the box */ + + INT32 volume; + + /* The number of nonzero histogram cells within this box */ + + long colorcount; + +} box; + + + +typedef box * boxptr; + + + + + +LOCAL boxptr + +find_biggest_color_pop (boxptr boxlist, int numboxes) + +/* Find the splittable box with the largest color population */ + +/* Returns NULL if no splittable boxes remain */ + +{ + + register boxptr boxp; + + register int i; + + register long maxc = 0; + + boxptr which = NULL; + + + + for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++) { + + if (boxp->colorcount > maxc && boxp->volume > 0) { + + which = boxp; + + maxc = boxp->colorcount; + + } + + } + + return which; + +} + + + + + +LOCAL boxptr + +find_biggest_volume (boxptr boxlist, int numboxes) + +/* Find the splittable box with the largest (scaled) volume */ + +/* Returns NULL if no splittable boxes remain */ + +{ + + register boxptr boxp; + + register int i; + + register INT32 maxv = 0; + + boxptr which = NULL; + + + + for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++) { + + if (boxp->volume > maxv) { + + which = boxp; + + maxv = boxp->volume; + + } + + } + + return which; + +} + + + + + +LOCAL void + +update_box (j_decompress_ptr cinfo, boxptr boxp) + +/* Shrink the min/max bounds of a box to enclose only nonzero elements, */ + +/* and recompute its volume and population */ + +{ + + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + hist3d histogram = cquantize->histogram; + + histptr histp; + + int c0,c1,c2; + + int c0min,c0max,c1min,c1max,c2min,c2max; + + INT32 dist0,dist1,dist2; + + long ccount; + + + + c0min = boxp->c0min; c0max = boxp->c0max; + + c1min = boxp->c1min; c1max = boxp->c1max; + + c2min = boxp->c2min; c2max = boxp->c2max; + + + + if (c0max > c0min) + + for (c0 = c0min; c0 <= c0max; c0++) + + for (c1 = c1min; c1 <= c1max; c1++) { + + histp = & histogram[c0][c1][c2min]; + + for (c2 = c2min; c2 <= c2max; c2++) + + if (*histp++ != 0) { + + boxp->c0min = c0min = c0; + + goto have_c0min; + + } + + } + + have_c0min: + + if (c0max > c0min) + + for (c0 = c0max; c0 >= c0min; c0--) + + for (c1 = c1min; c1 <= c1max; c1++) { + + histp = & histogram[c0][c1][c2min]; + + for (c2 = c2min; c2 <= c2max; c2++) + + if (*histp++ != 0) { + + boxp->c0max = c0max = c0; + + goto have_c0max; + + } + + } + + have_c0max: + + if (c1max > c1min) + + for (c1 = c1min; c1 <= c1max; c1++) + + for (c0 = c0min; c0 <= c0max; c0++) { + + histp = & histogram[c0][c1][c2min]; + + for (c2 = c2min; c2 <= c2max; c2++) + + if (*histp++ != 0) { + + boxp->c1min = c1min = c1; + + goto have_c1min; + + } + + } + + have_c1min: + + if (c1max > c1min) + + for (c1 = c1max; c1 >= c1min; c1--) + + for (c0 = c0min; c0 <= c0max; c0++) { + + histp = & histogram[c0][c1][c2min]; + + for (c2 = c2min; c2 <= c2max; c2++) + + if (*histp++ != 0) { + + boxp->c1max = c1max = c1; + + goto have_c1max; + + } + + } + + have_c1max: + + if (c2max > c2min) + + for (c2 = c2min; c2 <= c2max; c2++) + + for (c0 = c0min; c0 <= c0max; c0++) { + + histp = & histogram[c0][c1min][c2]; + + for (c1 = c1min; c1 <= c1max; c1++, histp += HIST_C2_ELEMS) + + if (*histp != 0) { + + boxp->c2min = c2min = c2; + + goto have_c2min; + + } + + } + + have_c2min: + + if (c2max > c2min) + + for (c2 = c2max; c2 >= c2min; c2--) + + for (c0 = c0min; c0 <= c0max; c0++) { + + histp = & histogram[c0][c1min][c2]; + + for (c1 = c1min; c1 <= c1max; c1++, histp += HIST_C2_ELEMS) + + if (*histp != 0) { + + boxp->c2max = c2max = c2; + + goto have_c2max; + + } + + } + + have_c2max: + + + + /* Update box volume. + + * We use 2-norm rather than real volume here; this biases the method + + * against making long narrow boxes, and it has the side benefit that + + * a box is splittable iff norm > 0. + + * Since the differences are expressed in histogram-cell units, + + * we have to shift back to JSAMPLE units to get consistent distances; + + * after which, we scale according to the selected distance scale factors. + + */ + + dist0 = ((c0max - c0min) << C0_SHIFT) * C0_SCALE; + + dist1 = ((c1max - c1min) << C1_SHIFT) * C1_SCALE; + + dist2 = ((c2max - c2min) << C2_SHIFT) * C2_SCALE; + + boxp->volume = dist0*dist0 + dist1*dist1 + dist2*dist2; + + + + /* Now scan remaining volume of box and compute population */ + + ccount = 0; + + for (c0 = c0min; c0 <= c0max; c0++) + + for (c1 = c1min; c1 <= c1max; c1++) { + + histp = & histogram[c0][c1][c2min]; + + for (c2 = c2min; c2 <= c2max; c2++, histp++) + + if (*histp != 0) { + + ccount++; + + } + + } + + boxp->colorcount = ccount; + +} + + + + + +LOCAL int + +median_cut (j_decompress_ptr cinfo, boxptr boxlist, int numboxes, + + int desired_colors) + +/* Repeatedly select and split the largest box until we have enough boxes */ + +{ + + int n,lb; + + int c0,c1,c2,cmax; + + register boxptr b1,b2; + + + + while (numboxes < desired_colors) { + + /* Select box to split. + + * Current algorithm: by population for first half, then by volume. + + */ + + if (numboxes*2 <= desired_colors) { + + b1 = find_biggest_color_pop(boxlist, numboxes); + + } else { + + b1 = find_biggest_volume(boxlist, numboxes); + + } + + if (b1 == NULL) /* no splittable boxes left! */ + + break; + + b2 = &boxlist[numboxes]; /* where new box will go */ + + /* Copy the color bounds to the new box. */ + + b2->c0max = b1->c0max; b2->c1max = b1->c1max; b2->c2max = b1->c2max; + + b2->c0min = b1->c0min; b2->c1min = b1->c1min; b2->c2min = b1->c2min; + + /* Choose which axis to split the box on. + + * Current algorithm: longest scaled axis. + + * See notes in update_box about scaling distances. + + */ + + c0 = ((b1->c0max - b1->c0min) << C0_SHIFT) * C0_SCALE; + + c1 = ((b1->c1max - b1->c1min) << C1_SHIFT) * C1_SCALE; + + c2 = ((b1->c2max - b1->c2min) << C2_SHIFT) * C2_SCALE; + + /* We want to break any ties in favor of green, then red, blue last. + + * This code does the right thing for R,G,B or B,G,R color orders only. + + */ + +#if RGB_RED == 0 + + cmax = c1; n = 1; + + if (c0 > cmax) { cmax = c0; n = 0; } + + if (c2 > cmax) { n = 2; } + +#else + + cmax = c1; n = 1; + + if (c2 > cmax) { cmax = c2; n = 2; } + + if (c0 > cmax) { n = 0; } + +#endif + + /* Choose split point along selected axis, and update box bounds. + + * Current algorithm: split at halfway point. + + * (Since the box has been shrunk to minimum volume, + + * any split will produce two nonempty subboxes.) + + * Note that lb value is max for lower box, so must be < old max. + + */ + + switch (n) { + + case 0: + + lb = (b1->c0max + b1->c0min) / 2; + + b1->c0max = lb; + + b2->c0min = lb+1; + + break; + + case 1: + + lb = (b1->c1max + b1->c1min) / 2; + + b1->c1max = lb; + + b2->c1min = lb+1; + + break; + + case 2: + + lb = (b1->c2max + b1->c2min) / 2; + + b1->c2max = lb; + + b2->c2min = lb+1; + + break; + + } + + /* Update stats for boxes */ + + update_box(cinfo, b1); + + update_box(cinfo, b2); + + numboxes++; + + } + + return numboxes; + +} + + + + + +LOCAL void + +compute_color (j_decompress_ptr cinfo, boxptr boxp, int icolor) + +/* Compute representative color for a box, put it in colormap[icolor] */ + +{ + + /* Current algorithm: mean weighted by pixels (not colors) */ + + /* Note it is important to get the rounding correct! */ + + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + hist3d histogram = cquantize->histogram; + + histptr histp; + + int c0,c1,c2; + + int c0min,c0max,c1min,c1max,c2min,c2max; + + long count; + + long total = 0; + + long c0total = 0; + + long c1total = 0; + + long c2total = 0; + + + + c0min = boxp->c0min; c0max = boxp->c0max; + + c1min = boxp->c1min; c1max = boxp->c1max; + + c2min = boxp->c2min; c2max = boxp->c2max; + + + + for (c0 = c0min; c0 <= c0max; c0++) + + for (c1 = c1min; c1 <= c1max; c1++) { + + histp = & histogram[c0][c1][c2min]; + + for (c2 = c2min; c2 <= c2max; c2++) { + + if ((count = *histp++) != 0) { + + total += count; + + c0total += ((c0 << C0_SHIFT) + ((1<>1)) * count; + + c1total += ((c1 << C1_SHIFT) + ((1<>1)) * count; + + c2total += ((c2 << C2_SHIFT) + ((1<>1)) * count; + + } + + } + + } + + + + cinfo->colormap[0][icolor] = (JSAMPLE) ((c0total + (total>>1)) / total); + + cinfo->colormap[1][icolor] = (JSAMPLE) ((c1total + (total>>1)) / total); + + cinfo->colormap[2][icolor] = (JSAMPLE) ((c2total + (total>>1)) / total); + +} + + + + + +LOCAL void + +select_colors (j_decompress_ptr cinfo, int desired_colors) + +/* Master routine for color selection */ + +{ + + boxptr boxlist; + + int numboxes; + + int i; + + + + /* Allocate workspace for box list */ + + boxlist = (boxptr) (*cinfo->mem->alloc_small) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, desired_colors * SIZEOF(box)); + + /* Initialize one box containing whole space */ + + numboxes = 1; + + boxlist[0].c0min = 0; + + boxlist[0].c0max = MAXJSAMPLE >> C0_SHIFT; + + boxlist[0].c1min = 0; + + boxlist[0].c1max = MAXJSAMPLE >> C1_SHIFT; + + boxlist[0].c2min = 0; + + boxlist[0].c2max = MAXJSAMPLE >> C2_SHIFT; + + /* Shrink it to actually-used volume and set its statistics */ + + update_box(cinfo, & boxlist[0]); + + /* Perform median-cut to produce final box list */ + + numboxes = median_cut(cinfo, boxlist, numboxes, desired_colors); + + /* Compute the representative color for each box, fill colormap */ + + for (i = 0; i < numboxes; i++) + + compute_color(cinfo, & boxlist[i], i); + + cinfo->actual_number_of_colors = numboxes; + + TRACEMS1(cinfo, 1, JTRC_QUANT_SELECTED, numboxes); + +} + + + + + +/* + + * These routines are concerned with the time-critical task of mapping input + + * colors to the nearest color in the selected colormap. + + * + + * We re-use the histogram space as an "inverse color map", essentially a + + * cache for the results of nearest-color searches. All colors within a + + * histogram cell will be mapped to the same colormap entry, namely the one + + * closest to the cell's center. This may not be quite the closest entry to + + * the actual input color, but it's almost as good. A zero in the cache + + * indicates we haven't found the nearest color for that cell yet; the array + + * is cleared to zeroes before starting the mapping pass. When we find the + + * nearest color for a cell, its colormap index plus one is recorded in the + + * cache for future use. The pass2 scanning routines call fill_inverse_cmap + + * when they need to use an unfilled entry in the cache. + + * + + * Our method of efficiently finding nearest colors is based on the "locally + + * sorted search" idea described by Heckbert and on the incremental distance + + * calculation described by Spencer W. Thomas in chapter III.1 of Graphics + + * Gems II (James Arvo, ed. Academic Press, 1991). Thomas points out that + + * the distances from a given colormap entry to each cell of the histogram can + + * be computed quickly using an incremental method: the differences between + + * distances to adjacent cells themselves differ by a constant. This allows a + + * fairly fast implementation of the "brute force" approach of computing the + + * distance from every colormap entry to every histogram cell. Unfortunately, + + * it needs a work array to hold the best-distance-so-far for each histogram + + * cell (because the inner loop has to be over cells, not colormap entries). + + * The work array elements have to be INT32s, so the work array would need + + * 256Kb at our recommended precision. This is not feasible in DOS machines. + + * + + * To get around these problems, we apply Thomas' method to compute the + + * nearest colors for only the cells within a small subbox of the histogram. + + * The work array need be only as big as the subbox, so the memory usage + + * problem is solved. Furthermore, we need not fill subboxes that are never + + * referenced in pass2; many images use only part of the color gamut, so a + + * fair amount of work is saved. An additional advantage of this + + * approach is that we can apply Heckbert's locality criterion to quickly + + * eliminate colormap entries that are far away from the subbox; typically + + * three-fourths of the colormap entries are rejected by Heckbert's criterion, + + * and we need not compute their distances to individual cells in the subbox. + + * The speed of this approach is heavily influenced by the subbox size: too + + * small means too much overhead, too big loses because Heckbert's criterion + + * can't eliminate as many colormap entries. Empirically the best subbox + + * size seems to be about 1/512th of the histogram (1/8th in each direction). + + * + + * Thomas' article also describes a refined method which is asymptotically + + * faster than the brute-force method, but it is also far more complex and + + * cannot efficiently be applied to small subboxes. It is therefore not + + * useful for programs intended to be portable to DOS machines. On machines + + * with plenty of memory, filling the whole histogram in one shot with Thomas' + + * refined method might be faster than the present code --- but then again, + + * it might not be any faster, and it's certainly more complicated. + + */ + + + + + +/* log2(histogram cells in update box) for each axis; this can be adjusted */ + +#define BOX_C0_LOG (HIST_C0_BITS-3) + +#define BOX_C1_LOG (HIST_C1_BITS-3) + +#define BOX_C2_LOG (HIST_C2_BITS-3) + + + +#define BOX_C0_ELEMS (1<actual_number_of_colors; + + int maxc0, maxc1, maxc2; + + int centerc0, centerc1, centerc2; + + int i, x, ncolors; + + INT32 minmaxdist, min_dist, max_dist, tdist; + + INT32 mindist[MAXNUMCOLORS]; /* min distance to colormap entry i */ + + + + /* Compute true coordinates of update box's upper corner and center. + + * Actually we compute the coordinates of the center of the upper-corner + + * histogram cell, which are the upper bounds of the volume we care about. + + * Note that since ">>" rounds down, the "center" values may be closer to + + * min than to max; hence comparisons to them must be "<=", not "<". + + */ + + maxc0 = minc0 + ((1 << BOX_C0_SHIFT) - (1 << C0_SHIFT)); + + centerc0 = (minc0 + maxc0) >> 1; + + maxc1 = minc1 + ((1 << BOX_C1_SHIFT) - (1 << C1_SHIFT)); + + centerc1 = (minc1 + maxc1) >> 1; + + maxc2 = minc2 + ((1 << BOX_C2_SHIFT) - (1 << C2_SHIFT)); + + centerc2 = (minc2 + maxc2) >> 1; + + + + /* For each color in colormap, find: + + * 1. its minimum squared-distance to any point in the update box + + * (zero if color is within update box); + + * 2. its maximum squared-distance to any point in the update box. + + * Both of these can be found by considering only the corners of the box. + + * We save the minimum distance for each color in mindist[]; + + * only the smallest maximum distance is of interest. + + */ + + minmaxdist = 0x7FFFFFFFL; + + + + for (i = 0; i < numcolors; i++) { + + /* We compute the squared-c0-distance term, then add in the other two. */ + + x = GETJSAMPLE(cinfo->colormap[0][i]); + + if (x < minc0) { + + tdist = (x - minc0) * C0_SCALE; + + min_dist = tdist*tdist; + + tdist = (x - maxc0) * C0_SCALE; + + max_dist = tdist*tdist; + + } else if (x > maxc0) { + + tdist = (x - maxc0) * C0_SCALE; + + min_dist = tdist*tdist; + + tdist = (x - minc0) * C0_SCALE; + + max_dist = tdist*tdist; + + } else { + + /* within cell range so no contribution to min_dist */ + + min_dist = 0; + + if (x <= centerc0) { + + tdist = (x - maxc0) * C0_SCALE; + + max_dist = tdist*tdist; + + } else { + + tdist = (x - minc0) * C0_SCALE; + + max_dist = tdist*tdist; + + } + + } + + + + x = GETJSAMPLE(cinfo->colormap[1][i]); + + if (x < minc1) { + + tdist = (x - minc1) * C1_SCALE; + + min_dist += tdist*tdist; + + tdist = (x - maxc1) * C1_SCALE; + + max_dist += tdist*tdist; + + } else if (x > maxc1) { + + tdist = (x - maxc1) * C1_SCALE; + + min_dist += tdist*tdist; + + tdist = (x - minc1) * C1_SCALE; + + max_dist += tdist*tdist; + + } else { + + /* within cell range so no contribution to min_dist */ + + if (x <= centerc1) { + + tdist = (x - maxc1) * C1_SCALE; + + max_dist += tdist*tdist; + + } else { + + tdist = (x - minc1) * C1_SCALE; + + max_dist += tdist*tdist; + + } + + } + + + + x = GETJSAMPLE(cinfo->colormap[2][i]); + + if (x < minc2) { + + tdist = (x - minc2) * C2_SCALE; + + min_dist += tdist*tdist; + + tdist = (x - maxc2) * C2_SCALE; + + max_dist += tdist*tdist; + + } else if (x > maxc2) { + + tdist = (x - maxc2) * C2_SCALE; + + min_dist += tdist*tdist; + + tdist = (x - minc2) * C2_SCALE; + + max_dist += tdist*tdist; + + } else { + + /* within cell range so no contribution to min_dist */ + + if (x <= centerc2) { + + tdist = (x - maxc2) * C2_SCALE; + + max_dist += tdist*tdist; + + } else { + + tdist = (x - minc2) * C2_SCALE; + + max_dist += tdist*tdist; + + } + + } + + + + mindist[i] = min_dist; /* save away the results */ + + if (max_dist < minmaxdist) + + minmaxdist = max_dist; + + } + + + + /* Now we know that no cell in the update box is more than minmaxdist + + * away from some colormap entry. Therefore, only colors that are + + * within minmaxdist of some part of the box need be considered. + + */ + + ncolors = 0; + + for (i = 0; i < numcolors; i++) { + + if (mindist[i] <= minmaxdist) + + colorlist[ncolors++] = (JSAMPLE) i; + + } + + return ncolors; + +} + + + + + +LOCAL void + +find_best_colors (j_decompress_ptr cinfo, int minc0, int minc1, int minc2, + + int numcolors, JSAMPLE colorlist[], JSAMPLE bestcolor[]) + +/* Find the closest colormap entry for each cell in the update box, + + * given the list of candidate colors prepared by find_nearby_colors. + + * Return the indexes of the closest entries in the bestcolor[] array. + + * This routine uses Thomas' incremental distance calculation method to + + * find the distance from a colormap entry to successive cells in the box. + + */ + +{ + + int ic0, ic1, ic2; + + int i, icolor; + + register INT32 * bptr; /* pointer into bestdist[] array */ + + JSAMPLE * cptr; /* pointer into bestcolor[] array */ + + INT32 dist0, dist1; /* initial distance values */ + + register INT32 dist2; /* current distance in inner loop */ + + INT32 xx0, xx1; /* distance increments */ + + register INT32 xx2; + + INT32 inc0, inc1, inc2; /* initial values for increments */ + + /* This array holds the distance to the nearest-so-far color for each cell */ + + INT32 bestdist[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS]; + + + + /* Initialize best-distance for each cell of the update box */ + + bptr = bestdist; + + for (i = BOX_C0_ELEMS*BOX_C1_ELEMS*BOX_C2_ELEMS-1; i >= 0; i--) + + *bptr++ = 0x7FFFFFFFL; + + + + /* For each color selected by find_nearby_colors, + + * compute its distance to the center of each cell in the box. + + * If that's less than best-so-far, update best distance and color number. + + */ + + + + /* Nominal steps between cell centers ("x" in Thomas article) */ + +#define STEP_C0 ((1 << C0_SHIFT) * C0_SCALE) + +#define STEP_C1 ((1 << C1_SHIFT) * C1_SCALE) + +#define STEP_C2 ((1 << C2_SHIFT) * C2_SCALE) + + + + for (i = 0; i < numcolors; i++) { + + icolor = GETJSAMPLE(colorlist[i]); + + /* Compute (square of) distance from minc0/c1/c2 to this color */ + + inc0 = (minc0 - GETJSAMPLE(cinfo->colormap[0][icolor])) * C0_SCALE; + + dist0 = inc0*inc0; + + inc1 = (minc1 - GETJSAMPLE(cinfo->colormap[1][icolor])) * C1_SCALE; + + dist0 += inc1*inc1; + + inc2 = (minc2 - GETJSAMPLE(cinfo->colormap[2][icolor])) * C2_SCALE; + + dist0 += inc2*inc2; + + /* Form the initial difference increments */ + + inc0 = inc0 * (2 * STEP_C0) + STEP_C0 * STEP_C0; + + inc1 = inc1 * (2 * STEP_C1) + STEP_C1 * STEP_C1; + + inc2 = inc2 * (2 * STEP_C2) + STEP_C2 * STEP_C2; + + /* Now loop over all cells in box, updating distance per Thomas method */ + + bptr = bestdist; + + cptr = bestcolor; + + xx0 = inc0; + + for (ic0 = BOX_C0_ELEMS-1; ic0 >= 0; ic0--) { + + dist1 = dist0; + + xx1 = inc1; + + for (ic1 = BOX_C1_ELEMS-1; ic1 >= 0; ic1--) { + + dist2 = dist1; + + xx2 = inc2; + + for (ic2 = BOX_C2_ELEMS-1; ic2 >= 0; ic2--) { + + if (dist2 < *bptr) { + + *bptr = dist2; + + *cptr = (JSAMPLE) icolor; + + } + + dist2 += xx2; + + xx2 += 2 * STEP_C2 * STEP_C2; + + bptr++; + + cptr++; + + } + + dist1 += xx1; + + xx1 += 2 * STEP_C1 * STEP_C1; + + } + + dist0 += xx0; + + xx0 += 2 * STEP_C0 * STEP_C0; + + } + + } + +} + + + + + +LOCAL void + +fill_inverse_cmap (j_decompress_ptr cinfo, int c0, int c1, int c2) + +/* Fill the inverse-colormap entries in the update box that contains */ + +/* histogram cell c0/c1/c2. (Only that one cell MUST be filled, but */ + +/* we can fill as many others as we wish.) */ + +{ + + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + hist3d histogram = cquantize->histogram; + + int minc0, minc1, minc2; /* lower left corner of update box */ + + int ic0, ic1, ic2; + + register JSAMPLE * cptr; /* pointer into bestcolor[] array */ + + register histptr cachep; /* pointer into main cache array */ + + /* This array lists the candidate colormap indexes. */ + + JSAMPLE colorlist[MAXNUMCOLORS]; + + int numcolors; /* number of candidate colors */ + + /* This array holds the actually closest colormap index for each cell. */ + + JSAMPLE bestcolor[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS]; + + + + /* Convert cell coordinates to update box ID */ + + c0 >>= BOX_C0_LOG; + + c1 >>= BOX_C1_LOG; + + c2 >>= BOX_C2_LOG; + + + + /* Compute true coordinates of update box's origin corner. + + * Actually we compute the coordinates of the center of the corner + + * histogram cell, which are the lower bounds of the volume we care about. + + */ + + minc0 = (c0 << BOX_C0_SHIFT) + ((1 << C0_SHIFT) >> 1); + + minc1 = (c1 << BOX_C1_SHIFT) + ((1 << C1_SHIFT) >> 1); + + minc2 = (c2 << BOX_C2_SHIFT) + ((1 << C2_SHIFT) >> 1); + + + + /* Determine which colormap entries are close enough to be candidates + + * for the nearest entry to some cell in the update box. + + */ + + numcolors = find_nearby_colors(cinfo, minc0, minc1, minc2, colorlist); + + + + /* Determine the actually nearest colors. */ + + find_best_colors(cinfo, minc0, minc1, minc2, numcolors, colorlist, + + bestcolor); + + + + /* Save the best color numbers (plus 1) in the main cache array */ + + c0 <<= BOX_C0_LOG; /* convert ID back to base cell indexes */ + + c1 <<= BOX_C1_LOG; + + c2 <<= BOX_C2_LOG; + + cptr = bestcolor; + + for (ic0 = 0; ic0 < BOX_C0_ELEMS; ic0++) { + + for (ic1 = 0; ic1 < BOX_C1_ELEMS; ic1++) { + + cachep = & histogram[c0+ic0][c1+ic1][c2]; + + for (ic2 = 0; ic2 < BOX_C2_ELEMS; ic2++) { + + *cachep++ = (histcell) (GETJSAMPLE(*cptr++) + 1); + + } + + } + + } + +} + + + + + +/* + + * Map some rows of pixels to the output colormapped representation. + + */ + + + +METHODDEF void + +pass2_no_dither (j_decompress_ptr cinfo, + + JSAMPARRAY input_buf, JSAMPARRAY output_buf, int num_rows) + +/* This version performs no dithering */ + +{ + + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + hist3d histogram = cquantize->histogram; + + register JSAMPROW inptr, outptr; + + register histptr cachep; + + register int c0, c1, c2; + + int row; + + JDIMENSION col; + + JDIMENSION width = cinfo->output_width; + + + + for (row = 0; row < num_rows; row++) { + + inptr = input_buf[row]; + + outptr = output_buf[row]; + + for (col = width; col > 0; col--) { + + /* get pixel value and index into the cache */ + + c0 = GETJSAMPLE(*inptr++) >> C0_SHIFT; + + c1 = GETJSAMPLE(*inptr++) >> C1_SHIFT; + + c2 = GETJSAMPLE(*inptr++) >> C2_SHIFT; + + cachep = & histogram[c0][c1][c2]; + + /* If we have not seen this color before, find nearest colormap entry */ + + /* and update the cache */ + + if (*cachep == 0) + + fill_inverse_cmap(cinfo, c0,c1,c2); + + /* Now emit the colormap index for this cell */ + + *outptr++ = (JSAMPLE) (*cachep - 1); + + } + + } + +} + + + + + +METHODDEF void + +pass2_fs_dither (j_decompress_ptr cinfo, + + JSAMPARRAY input_buf, JSAMPARRAY output_buf, int num_rows) + +/* This version performs Floyd-Steinberg dithering */ + +{ + + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + hist3d histogram = cquantize->histogram; + + register LOCFSERROR cur0, cur1, cur2; /* current error or pixel value */ + + LOCFSERROR belowerr0, belowerr1, belowerr2; /* error for pixel below cur */ + + LOCFSERROR bpreverr0, bpreverr1, bpreverr2; /* error for below/prev col */ + + register FSERRPTR errorptr; /* => fserrors[] at column before current */ + + JSAMPROW inptr; /* => current input pixel */ + + JSAMPROW outptr; /* => current output pixel */ + + histptr cachep; + + int dir; /* +1 or -1 depending on direction */ + + int dir3; /* 3*dir, for advancing inptr & errorptr */ + + int row; + + JDIMENSION col; + + JDIMENSION width = cinfo->output_width; + + JSAMPLE *range_limit = cinfo->sample_range_limit; + + int *error_limit = cquantize->error_limiter; + + JSAMPROW colormap0 = cinfo->colormap[0]; + + JSAMPROW colormap1 = cinfo->colormap[1]; + + JSAMPROW colormap2 = cinfo->colormap[2]; + + SHIFT_TEMPS + + + + for (row = 0; row < num_rows; row++) { + + inptr = input_buf[row]; + + outptr = output_buf[row]; + + if (cquantize->on_odd_row) { + + /* work right to left in this row */ + + inptr += (width-1) * 3; /* so point to rightmost pixel */ + + outptr += width-1; + + dir = -1; + + dir3 = -3; + + errorptr = cquantize->fserrors + (width+1)*3; /* => entry after last column */ + + cquantize->on_odd_row = FALSE; /* flip for next time */ + + } else { + + /* work left to right in this row */ + + dir = 1; + + dir3 = 3; + + errorptr = cquantize->fserrors; /* => entry before first real column */ + + cquantize->on_odd_row = TRUE; /* flip for next time */ + + } + + /* Preset error values: no error propagated to first pixel from left */ + + cur0 = cur1 = cur2 = 0; + + /* and no error propagated to row below yet */ + + belowerr0 = belowerr1 = belowerr2 = 0; + + bpreverr0 = bpreverr1 = bpreverr2 = 0; + + + + for (col = width; col > 0; col--) { + + /* curN holds the error propagated from the previous pixel on the + + * current line. Add the error propagated from the previous line + + * to form the complete error correction term for this pixel, and + + * round the error term (which is expressed * 16) to an integer. + + * RIGHT_SHIFT rounds towards minus infinity, so adding 8 is correct + + * for either sign of the error value. + + * Note: errorptr points to *previous* column's array entry. + + */ + + cur0 = RIGHT_SHIFT(cur0 + errorptr[dir3+0] + 8, 4); + + cur1 = RIGHT_SHIFT(cur1 + errorptr[dir3+1] + 8, 4); + + cur2 = RIGHT_SHIFT(cur2 + errorptr[dir3+2] + 8, 4); + + /* Limit the error using transfer function set by init_error_limit. + + * See comments with init_error_limit for rationale. + + */ + + cur0 = error_limit[cur0]; + + cur1 = error_limit[cur1]; + + cur2 = error_limit[cur2]; + + /* Form pixel value + error, and range-limit to 0..MAXJSAMPLE. + + * The maximum error is +- MAXJSAMPLE (or less with error limiting); + + * this sets the required size of the range_limit array. + + */ + + cur0 += GETJSAMPLE(inptr[0]); + + cur1 += GETJSAMPLE(inptr[1]); + + cur2 += GETJSAMPLE(inptr[2]); + + cur0 = GETJSAMPLE(range_limit[cur0]); + + cur1 = GETJSAMPLE(range_limit[cur1]); + + cur2 = GETJSAMPLE(range_limit[cur2]); + + /* Index into the cache with adjusted pixel value */ + + cachep = & histogram[cur0>>C0_SHIFT][cur1>>C1_SHIFT][cur2>>C2_SHIFT]; + + /* If we have not seen this color before, find nearest colormap */ + + /* entry and update the cache */ + + if (*cachep == 0) + + fill_inverse_cmap(cinfo, cur0>>C0_SHIFT,cur1>>C1_SHIFT,cur2>>C2_SHIFT); + + /* Now emit the colormap index for this cell */ + + { register int pixcode = *cachep - 1; + + *outptr = (JSAMPLE) pixcode; + + /* Compute representation error for this pixel */ + + cur0 -= GETJSAMPLE(colormap0[pixcode]); + + cur1 -= GETJSAMPLE(colormap1[pixcode]); + + cur2 -= GETJSAMPLE(colormap2[pixcode]); + + } + + /* Compute error fractions to be propagated to adjacent pixels. + + * Add these into the running sums, and simultaneously shift the + + * next-line error sums left by 1 column. + + */ + + { register LOCFSERROR bnexterr, delta; + + + + bnexterr = cur0; /* Process component 0 */ + + delta = cur0 * 2; + + cur0 += delta; /* form error * 3 */ + + errorptr[0] = (FSERROR) (bpreverr0 + cur0); + + cur0 += delta; /* form error * 5 */ + + bpreverr0 = belowerr0 + cur0; + + belowerr0 = bnexterr; + + cur0 += delta; /* form error * 7 */ + + bnexterr = cur1; /* Process component 1 */ + + delta = cur1 * 2; + + cur1 += delta; /* form error * 3 */ + + errorptr[1] = (FSERROR) (bpreverr1 + cur1); + + cur1 += delta; /* form error * 5 */ + + bpreverr1 = belowerr1 + cur1; + + belowerr1 = bnexterr; + + cur1 += delta; /* form error * 7 */ + + bnexterr = cur2; /* Process component 2 */ + + delta = cur2 * 2; + + cur2 += delta; /* form error * 3 */ + + errorptr[2] = (FSERROR) (bpreverr2 + cur2); + + cur2 += delta; /* form error * 5 */ + + bpreverr2 = belowerr2 + cur2; + + belowerr2 = bnexterr; + + cur2 += delta; /* form error * 7 */ + + } + + /* At this point curN contains the 7/16 error value to be propagated + + * to the next pixel on the current line, and all the errors for the + + * next line have been shifted over. We are therefore ready to move on. + + */ + + inptr += dir3; /* Advance pixel pointers to next column */ + + outptr += dir; + + errorptr += dir3; /* advance errorptr to current column */ + + } + + /* Post-loop cleanup: we must unload the final error values into the + + * final fserrors[] entry. Note we need not unload belowerrN because + + * it is for the dummy column before or after the actual array. + + */ + + errorptr[0] = (FSERROR) bpreverr0; /* unload prev errs into array */ + + errorptr[1] = (FSERROR) bpreverr1; + + errorptr[2] = (FSERROR) bpreverr2; + + } + +} + + + + + +/* + + * Initialize the error-limiting transfer function (lookup table). + + * The raw F-S error computation can potentially compute error values of up to + + * +- MAXJSAMPLE. But we want the maximum correction applied to a pixel to be + + * much less, otherwise obviously wrong pixels will be created. (Typical + + * effects include weird fringes at color-area boundaries, isolated bright + + * pixels in a dark area, etc.) The standard advice for avoiding this problem + + * is to ensure that the "corners" of the color cube are allocated as output + + * colors; then repeated errors in the same direction cannot cause cascading + + * error buildup. However, that only prevents the error from getting + + * completely out of hand; Aaron Giles reports that error limiting improves + + * the results even with corner colors allocated. + + * A simple clamping of the error values to about +- MAXJSAMPLE/8 works pretty + + * well, but the smoother transfer function used below is even better. Thanks + + * to Aaron Giles for this idea. + + */ + + + +LOCAL void + +init_error_limit (j_decompress_ptr cinfo) + +/* Allocate and fill in the error_limiter table */ + +{ + + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + int * table; + + int in, out; + + + + table = (int *) (*cinfo->mem->alloc_small) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, (MAXJSAMPLE*2+1) * SIZEOF(int)); + + table += MAXJSAMPLE; /* so can index -MAXJSAMPLE .. +MAXJSAMPLE */ + + cquantize->error_limiter = table; + + + +#define STEPSIZE ((MAXJSAMPLE+1)/16) + + /* Map errors 1:1 up to +- MAXJSAMPLE/16 */ + + out = 0; + + for (in = 0; in < STEPSIZE; in++, out++) { + + table[in] = out; table[-in] = -out; + + } + + /* Map errors 1:2 up to +- 3*MAXJSAMPLE/16 */ + + for (; in < STEPSIZE*3; in++, out += (in&1) ? 0 : 1) { + + table[in] = out; table[-in] = -out; + + } + + /* Clamp the rest to final out value (which is (MAXJSAMPLE+1)/8) */ + + for (; in <= MAXJSAMPLE; in++) { + + table[in] = out; table[-in] = -out; + + } + +#undef STEPSIZE + +} + + + + + +/* + + * Finish up at the end of each pass. + + */ + + + +METHODDEF void + +finish_pass1 (j_decompress_ptr cinfo) + +{ + + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + + + /* Select the representative colors and fill in cinfo->colormap */ + + cinfo->colormap = cquantize->sv_colormap; + + select_colors(cinfo, cquantize->desired); + + /* Force next pass to zero the color index table */ + + cquantize->needs_zeroed = TRUE; + +} + + + + + +METHODDEF void + +finish_pass2 (j_decompress_ptr cinfo) + +{ + + /* no work */ + +} + + + + + +/* + + * Initialize for each processing pass. + + */ + + + +METHODDEF void + +start_pass_2_quant (j_decompress_ptr cinfo, boolean is_pre_scan) + +{ + + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + hist3d histogram = cquantize->histogram; + + int i; + + + + /* Only F-S dithering or no dithering is supported. */ + + /* If user asks for ordered dither, give him F-S. */ + + if (cinfo->dither_mode != JDITHER_NONE) + + cinfo->dither_mode = JDITHER_FS; + + + + if (is_pre_scan) { + + /* Set up method pointers */ + + cquantize->pub.color_quantize = prescan_quantize; + + cquantize->pub.finish_pass = finish_pass1; + + cquantize->needs_zeroed = TRUE; /* Always zero histogram */ + + } else { + + /* Set up method pointers */ + + if (cinfo->dither_mode == JDITHER_FS) + + cquantize->pub.color_quantize = pass2_fs_dither; + + else + + cquantize->pub.color_quantize = pass2_no_dither; + + cquantize->pub.finish_pass = finish_pass2; + + + + /* Make sure color count is acceptable */ + + i = cinfo->actual_number_of_colors; + + if (i < 1) + + ERREXIT1(cinfo, JERR_QUANT_FEW_COLORS, 1); + + if (i > MAXNUMCOLORS) + + ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, MAXNUMCOLORS); + + + + if (cinfo->dither_mode == JDITHER_FS) { + + size_t arraysize = (size_t) ((cinfo->output_width + 2) * + + (3 * SIZEOF(FSERROR))); + + /* Allocate Floyd-Steinberg workspace if we didn't already. */ + + if (cquantize->fserrors == NULL) + + cquantize->fserrors = (FSERRPTR) (*cinfo->mem->alloc_large) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, arraysize); + + /* Initialize the propagated errors to zero. */ + + jzero_far((void FAR *) cquantize->fserrors, arraysize); + + /* Make the error-limit table if we didn't already. */ + + if (cquantize->error_limiter == NULL) + + init_error_limit(cinfo); + + cquantize->on_odd_row = FALSE; + + } + + + + } + + /* Zero the histogram or inverse color map, if necessary */ + + if (cquantize->needs_zeroed) { + + for (i = 0; i < HIST_C0_ELEMS; i++) { + + jzero_far((void FAR *) histogram[i], + + HIST_C1_ELEMS*HIST_C2_ELEMS * SIZEOF(histcell)); + + } + + cquantize->needs_zeroed = FALSE; + + } + +} + + + + + +/* + + * Switch to a new external colormap between output passes. + + */ + + + +METHODDEF void + +new_color_map_2_quant (j_decompress_ptr cinfo) + +{ + + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + + + /* Reset the inverse color map */ + + cquantize->needs_zeroed = TRUE; + +} + + + + + +/* + + * Module initialization routine for 2-pass color quantization. + + */ + + + +GLOBAL void + +jinit_2pass_quantizer (j_decompress_ptr cinfo) + +{ + + my_cquantize_ptr cquantize; + + int i; + + + + cquantize = (my_cquantize_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(my_cquantizer)); + + cinfo->cquantize = (struct jpeg_color_quantizer *) cquantize; + + cquantize->pub.start_pass = start_pass_2_quant; + + cquantize->pub.new_color_map = new_color_map_2_quant; + + cquantize->fserrors = NULL; /* flag optional arrays not allocated */ + + cquantize->error_limiter = NULL; + + + + /* Make sure jdmaster didn't give me a case I can't handle */ + + if (cinfo->out_color_components != 3) + + ERREXIT(cinfo, JERR_NOTIMPL); + + + + /* Allocate the histogram/inverse colormap storage */ + + cquantize->histogram = (hist3d) (*cinfo->mem->alloc_small) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, HIST_C0_ELEMS * SIZEOF(hist2d)); + + for (i = 0; i < HIST_C0_ELEMS; i++) { + + cquantize->histogram[i] = (hist2d) (*cinfo->mem->alloc_large) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, + + HIST_C1_ELEMS*HIST_C2_ELEMS * SIZEOF(histcell)); + + } + + cquantize->needs_zeroed = TRUE; /* histogram is garbage now */ + + + + /* Allocate storage for the completed colormap, if required. + + * We do this now since it is FAR storage and may affect + + * the memory manager's space calculations. + + */ + + if (cinfo->enable_2pass_quant) { + + /* Make sure color count is acceptable */ + + int desired = cinfo->desired_number_of_colors; + + /* Lower bound on # of colors ... somewhat arbitrary as long as > 0 */ + + if (desired < 8) + + ERREXIT1(cinfo, JERR_QUANT_FEW_COLORS, 8); + + /* Make sure colormap indexes can be represented by JSAMPLEs */ + + if (desired > MAXNUMCOLORS) + + ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, MAXNUMCOLORS); + + cquantize->sv_colormap = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo,JPOOL_IMAGE, (JDIMENSION) desired, (JDIMENSION) 3); + + cquantize->desired = desired; + + } else + + cquantize->sv_colormap = NULL; + + + + /* Only F-S dithering or no dithering is supported. */ + + /* If user asks for ordered dither, give him F-S. */ + + if (cinfo->dither_mode != JDITHER_NONE) + + cinfo->dither_mode = JDITHER_FS; + + + + /* Allocate Floyd-Steinberg workspace if necessary. + + * This isn't really needed until pass 2, but again it is FAR storage. + + * Although we will cope with a later change in dither_mode, + + * we do not promise to honor max_memory_to_use if dither_mode changes. + + */ + + if (cinfo->dither_mode == JDITHER_FS) { + + cquantize->fserrors = (FSERRPTR) (*cinfo->mem->alloc_large) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (size_t) ((cinfo->output_width + 2) * (3 * SIZEOF(FSERROR)))); + + /* Might as well create the error-limiting table too. */ + + init_error_limit(cinfo); + + } + +} + + + +#endif /* QUANT_2PASS_SUPPORTED */ + diff --git a/utils/roq2/jpeg/jutils.c b/utils/roq2/jpeg/jutils.c new file mode 100644 index 0000000..5639388 --- /dev/null +++ b/utils/roq2/jpeg/jutils.c @@ -0,0 +1,350 @@ +/* + + * jutils.c + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains tables and miscellaneous utility routines needed + + * for both compression and decompression. + + * Note we prefix all global names with "j" to minimize conflicts with + + * a surrounding application. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "jpeglib.h" + + + + + +/* + + * jpeg_zigzag_order[i] is the zigzag-order position of the i'th element + + * of a DCT block read in natural order (left to right, top to bottom). + + */ + + + +const int jpeg_zigzag_order[DCTSIZE2] = { + + 0, 1, 5, 6, 14, 15, 27, 28, + + 2, 4, 7, 13, 16, 26, 29, 42, + + 3, 8, 12, 17, 25, 30, 41, 43, + + 9, 11, 18, 24, 31, 40, 44, 53, + + 10, 19, 23, 32, 39, 45, 52, 54, + + 20, 22, 33, 38, 46, 51, 55, 60, + + 21, 34, 37, 47, 50, 56, 59, 61, + + 35, 36, 48, 49, 57, 58, 62, 63 + +}; + + + +/* + + * jpeg_natural_order[i] is the natural-order position of the i'th element + + * of zigzag order. + + * + + * When reading corrupted data, the Huffman decoders could attempt + + * to reference an entry beyond the end of this array (if the decoded + + * zero run length reaches past the end of the block). To prevent + + * wild stores without adding an inner-loop test, we put some extra + + * "63"s after the real entries. This will cause the extra coefficient + + * to be stored in location 63 of the block, not somewhere random. + + * The worst case would be a run-length of 15, which means we need 16 + + * fake entries. + + */ + + + +const int jpeg_natural_order[DCTSIZE2+16] = { + + 0, 1, 8, 16, 9, 2, 3, 10, + + 17, 24, 32, 25, 18, 11, 4, 5, + + 12, 19, 26, 33, 40, 48, 41, 34, + + 27, 20, 13, 6, 7, 14, 21, 28, + + 35, 42, 49, 56, 57, 50, 43, 36, + + 29, 22, 15, 23, 30, 37, 44, 51, + + 58, 59, 52, 45, 38, 31, 39, 46, + + 53, 60, 61, 54, 47, 55, 62, 63, + + 63, 63, 63, 63, 63, 63, 63, 63, /* extra entries for safety in decoder */ + + 63, 63, 63, 63, 63, 63, 63, 63 + +}; + + + + + +/* + + * Arithmetic utilities + + */ + + + +GLOBAL long + +jdiv_round_up (long a, long b) + +/* Compute a/b rounded up to next integer, ie, ceil(a/b) */ + +/* Assumes a >= 0, b > 0 */ + +{ + + return (a + b - 1L) / b; + +} + + + + + +GLOBAL long + +jround_up (long a, long b) + +/* Compute a rounded up to next multiple of b, ie, ceil(a/b)*b */ + +/* Assumes a >= 0, b > 0 */ + +{ + + a += b - 1L; + + return a - (a % b); + +} + + + + + +/* On normal machines we can apply MEMCOPY() and MEMZERO() to sample arrays + + * and coefficient-block arrays. This won't work on 80x86 because the arrays + + * are FAR and we're assuming a small-pointer memory model. However, some + + * DOS compilers provide far-pointer versions of memcpy() and memset() even + + * in the small-model libraries. These will be used if USE_FMEM is defined. + + * Otherwise, the routines below do it the hard way. (The performance cost + + * is not all that great, because these routines aren't very heavily used.) + + */ + + + +#ifndef NEED_FAR_POINTERS /* normal case, same as regular macros */ + +#define FMEMCOPY(dest,src,size) MEMCOPY(dest,src,size) + +#define FMEMZERO(target,size) MEMZERO(target,size) + +#else /* 80x86 case, define if we can */ + +#ifdef USE_FMEM + +#define FMEMCOPY(dest,src,size) _fmemcpy((void FAR *)(dest), (const void FAR *)(src), (size_t)(size)) + +#define FMEMZERO(target,size) _fmemset((void FAR *)(target), 0, (size_t)(size)) + +#endif + +#endif + + + + + +GLOBAL void + +jcopy_sample_rows (JSAMPARRAY input_array, int source_row, + + JSAMPARRAY output_array, int dest_row, + + int num_rows, JDIMENSION num_cols) + +/* Copy some rows of samples from one place to another. + + * num_rows rows are copied from input_array[source_row++] + + * to output_array[dest_row++]; these areas may overlap for duplication. + + * The source and destination arrays must be at least as wide as num_cols. + + */ + +{ + + register JSAMPROW inptr, outptr; + +#ifdef FMEMCOPY + + register size_t count = (size_t) (num_cols * SIZEOF(JSAMPLE)); + +#else + + register JDIMENSION count; + +#endif + + register int row; + + + + input_array += source_row; + + output_array += dest_row; + + + + for (row = num_rows; row > 0; row--) { + + inptr = *input_array++; + + outptr = *output_array++; + +#ifdef FMEMCOPY + + FMEMCOPY(outptr, inptr, count); + +#else + + for (count = num_cols; count > 0; count--) + + *outptr++ = *inptr++; /* needn't bother with GETJSAMPLE() here */ + +#endif + + } + +} + + + + + +GLOBAL void + +jcopy_block_row (JBLOCKROW input_row, JBLOCKROW output_row, + + JDIMENSION num_blocks) + +/* Copy a row of coefficient blocks from one place to another. */ + +{ + +#ifdef FMEMCOPY + + FMEMCOPY(output_row, input_row, num_blocks * (DCTSIZE2 * SIZEOF(JCOEF))); + +#else + + register JCOEFPTR inptr, outptr; + + register long count; + + + + inptr = (JCOEFPTR) input_row; + + outptr = (JCOEFPTR) output_row; + + for (count = (long) num_blocks * DCTSIZE2; count > 0; count--) { + + *outptr++ = *inptr++; + + } + +#endif + +} + + + + + +GLOBAL void + +jzero_far (void FAR * target, size_t bytestozero) + +/* Zero out a chunk of FAR memory. */ + +/* This might be sample-array data, block-array data, or alloc_large data. */ + +{ + +#ifdef FMEMZERO + + FMEMZERO(target, bytestozero); + +#else + + register char FAR * ptr = (char FAR *) target; + + register size_t count; + + + + for (count = bytestozero; count > 0; count--) { + + *ptr++ = 0; + + } + +#endif + +} + diff --git a/utils/roq2/jpeg/jversion.h b/utils/roq2/jpeg/jversion.h new file mode 100644 index 0000000..784a088 --- /dev/null +++ b/utils/roq2/jpeg/jversion.h @@ -0,0 +1,28 @@ +/* + + * jversion.h + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains software version identification. + + */ + + + + + +#define JVERSION "6 2-Aug-95" + + + +#define JCOPYRIGHT "Copyright (C) 1995, Thomas G. Lane" + diff --git a/utils/roq2/jpeg/rdbmp.c b/utils/roq2/jpeg/rdbmp.c new file mode 100644 index 0000000..6debea0 --- /dev/null +++ b/utils/roq2/jpeg/rdbmp.c @@ -0,0 +1,878 @@ +/* + + * rdbmp.c + + * + + * Copyright (C) 1994-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains routines to read input images in Microsoft "BMP" + + * format (MS Windows 3.x, OS/2 1.x, and OS/2 2.x flavors). + + * Currently, only 8-bit and 24-bit images are supported, not 1-bit or + + * 4-bit (feeding such low-depth images into JPEG would be silly anyway). + + * Also, we don't support RLE-compressed files. + + * + + * These routines may need modification for non-Unix environments or + + * specialized applications. As they stand, they assume input from + + * an ordinary stdio stream. They further assume that reading begins + + * at the start of the file; start_input may need work if the + + * user interface has already read some data (e.g., to determine that + + * the file is indeed BMP format). + + * + + * This code contributed by James Arthur Boucher. + + */ + + + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + + + +#ifdef BMP_SUPPORTED + + + + + +/* Macros to deal with unsigned chars as efficiently as compiler allows */ + + + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char U_CHAR; + +#define UCH(x) ((int) (x)) + +#else /* !HAVE_UNSIGNED_CHAR */ + +#ifdef CHAR_IS_UNSIGNED + +typedef char U_CHAR; + +#define UCH(x) ((int) (x)) + +#else + +typedef char U_CHAR; + +#define UCH(x) ((int) (x) & 0xFF) + +#endif + +#endif /* HAVE_UNSIGNED_CHAR */ + + + + + +#define ReadOK(file,buffer,len) (JFREAD(file,buffer,len) == ((size_t) (len))) + + + + + +/* Private version of data source object */ + + + +typedef struct _bmp_source_struct * bmp_source_ptr; + + + +typedef struct _bmp_source_struct { + + struct cjpeg_source_struct pub; /* public fields */ + + + + j_compress_ptr cinfo; /* back link saves passing separate parm */ + + + + JSAMPARRAY colormap; /* BMP colormap (converted to my format) */ + + + + jvirt_sarray_ptr whole_image; /* Needed to reverse row order */ + + JDIMENSION source_row; /* Current source row number */ + + JDIMENSION row_width; /* Physical width of scanlines in file */ + + + + int bits_per_pixel; /* remembers 8- or 24-bit format */ + +} bmp_source_struct; + + + + + +LOCAL int + +read_byte (bmp_source_ptr sinfo) + +/* Read next byte from BMP file */ + +{ + + register FILE *infile = sinfo->pub.input_file; + + register int c; + + + + if ((c = getc(infile)) == EOF) + + ERREXIT(sinfo->cinfo, JERR_INPUT_EOF); + + return c; + +} + + + + + +LOCAL void + +read_colormap (bmp_source_ptr sinfo, int cmaplen, int mapentrysize) + +/* Read the colormap from a BMP file */ + +{ + + int i; + + + + switch (mapentrysize) { + + case 3: + + /* BGR format (occurs in OS/2 files) */ + + for (i = 0; i < cmaplen; i++) { + + sinfo->colormap[2][i] = (JSAMPLE) read_byte(sinfo); + + sinfo->colormap[1][i] = (JSAMPLE) read_byte(sinfo); + + sinfo->colormap[0][i] = (JSAMPLE) read_byte(sinfo); + + } + + break; + + case 4: + + /* BGR0 format (occurs in MS Windows files) */ + + for (i = 0; i < cmaplen; i++) { + + sinfo->colormap[2][i] = (JSAMPLE) read_byte(sinfo); + + sinfo->colormap[1][i] = (JSAMPLE) read_byte(sinfo); + + sinfo->colormap[0][i] = (JSAMPLE) read_byte(sinfo); + + (void) read_byte(sinfo); + + } + + break; + + default: + + ERREXIT(sinfo->cinfo, JERR_BMP_BADCMAP); + + break; + + } + +} + + + + + +/* + + * Read one row of pixels. + + * The image has been read into the whole_image array, but is otherwise + + * unprocessed. We must read it out in top-to-bottom row order, and if + + * it is an 8-bit image, we must expand colormapped pixels to 24bit format. + + */ + + + +METHODDEF JDIMENSION + +get_8bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +/* This version is for reading 8-bit colormap indexes */ + +{ + + bmp_source_ptr source = (bmp_source_ptr) sinfo; + + register JSAMPARRAY colormap = source->colormap; + + JSAMPARRAY image_ptr; + + register int t; + + register JSAMPROW inptr, outptr; + + register JDIMENSION col; + + + + /* Fetch next row from virtual array */ + + source->source_row--; + + image_ptr = (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, source->whole_image, + + source->source_row, (JDIMENSION) 1, FALSE); + + + + /* Expand the colormap indexes to real data */ + + inptr = image_ptr[0]; + + outptr = source->pub.buffer[0]; + + for (col = cinfo->image_width; col > 0; col--) { + + t = GETJSAMPLE(*inptr++); + + *outptr++ = colormap[0][t]; /* can omit GETJSAMPLE() safely */ + + *outptr++ = colormap[1][t]; + + *outptr++ = colormap[2][t]; + + } + + + + return 1; + +} + + + + + +METHODDEF JDIMENSION + +get_24bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +/* This version is for reading 24-bit pixels */ + +{ + + bmp_source_ptr source = (bmp_source_ptr) sinfo; + + JSAMPARRAY image_ptr; + + register JSAMPROW inptr, outptr; + + register JDIMENSION col; + + + + /* Fetch next row from virtual array */ + + source->source_row--; + + image_ptr = (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, source->whole_image, + + source->source_row, (JDIMENSION) 1, FALSE); + + + + /* Transfer data. Note source values are in BGR order + + * (even though Microsoft's own documents say the opposite). + + */ + + inptr = image_ptr[0]; + + outptr = source->pub.buffer[0]; + + for (col = cinfo->image_width; col > 0; col--) { + + outptr[2] = *inptr++; /* can omit GETJSAMPLE() safely */ + + outptr[1] = *inptr++; + + outptr[0] = *inptr++; + + outptr += 3; + + } + + + + return 1; + +} + + + + + +/* + + * This method loads the image into whole_image during the first call on + + * get_pixel_rows. The get_pixel_rows pointer is then adjusted to call + + * get_8bit_row or get_24bit_row on subsequent calls. + + */ + + + +METHODDEF JDIMENSION + +preload_image (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +{ + + bmp_source_ptr source = (bmp_source_ptr) sinfo; + + register FILE *infile = source->pub.input_file; + + register int c; + + register JSAMPROW out_ptr; + + JSAMPARRAY image_ptr; + + JDIMENSION row, col; + + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; + + + + /* Read the data into a virtual array in input-file row order. */ + + for (row = 0; row < cinfo->image_height; row++) { + + if (progress != NULL) { + + progress->pub.pass_counter = (long) row; + + progress->pub.pass_limit = (long) cinfo->image_height; + + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + + } + + image_ptr = (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, source->whole_image, + + row, (JDIMENSION) 1, TRUE); + + out_ptr = image_ptr[0]; + + for (col = source->row_width; col > 0; col--) { + + /* inline copy of read_byte() for speed */ + + if ((c = getc(infile)) == EOF) + + ERREXIT(cinfo, JERR_INPUT_EOF); + + *out_ptr++ = (JSAMPLE) c; + + } + + } + + if (progress != NULL) + + progress->completed_extra_passes++; + + + + /* Set up to read from the virtual array in top-to-bottom order */ + + switch (source->bits_per_pixel) { + + case 8: + + source->pub.get_pixel_rows = get_8bit_row; + + break; + + case 24: + + source->pub.get_pixel_rows = get_24bit_row; + + break; + + default: + + ERREXIT(cinfo, JERR_BMP_BADDEPTH); + + } + + source->source_row = cinfo->image_height; + + + + /* And read the first row */ + + return (*source->pub.get_pixel_rows) (cinfo, sinfo); + +} + + + + + +/* + + * Read the file header; return image size and component count. + + */ + + + +METHODDEF void + +start_input_bmp (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +{ + + bmp_source_ptr source = (bmp_source_ptr) sinfo; + + U_CHAR bmpfileheader[14]; + + U_CHAR bmpinfoheader[64]; + +#define GET_2B(array,offset) ((unsigned int) UCH(array[offset]) + \ + + (((unsigned int) UCH(array[offset+1])) << 8)) + +#define GET_4B(array,offset) ((INT32) UCH(array[offset]) + \ + + (((INT32) UCH(array[offset+1])) << 8) + \ + + (((INT32) UCH(array[offset+2])) << 16) + \ + + (((INT32) UCH(array[offset+3])) << 24)) + + INT32 bfOffBits; + + INT32 headerSize; + + INT32 biWidth = 0; /* initialize to avoid compiler warning */ + + INT32 biHeight = 0; + + unsigned int biPlanes; + + INT32 biCompression; + + INT32 biXPelsPerMeter,biYPelsPerMeter; + + INT32 biClrUsed = 0; + + int mapentrysize = 0; /* 0 indicates no colormap */ + + INT32 bPad; + + JDIMENSION row_width; + + + + /* Read and verify the bitmap file header */ + + if (! ReadOK(source->pub.input_file, bmpfileheader, 14)) + + ERREXIT(cinfo, JERR_INPUT_EOF); + + if (GET_2B(bmpfileheader,0) != 0x4D42) /* 'BM' */ + + ERREXIT(cinfo, JERR_BMP_NOT); + + bfOffBits = (INT32) GET_4B(bmpfileheader,10); + + /* We ignore the remaining fileheader fields */ + + + + /* The infoheader might be 12 bytes (OS/2 1.x), 40 bytes (Windows), + + * or 64 bytes (OS/2 2.x). Check the first 4 bytes to find out which. + + */ + + if (! ReadOK(source->pub.input_file, bmpinfoheader, 4)) + + ERREXIT(cinfo, JERR_INPUT_EOF); + + headerSize = (INT32) GET_4B(bmpinfoheader,0); + + if (headerSize < 12 || headerSize > 64) + + ERREXIT(cinfo, JERR_BMP_BADHEADER); + + if (! ReadOK(source->pub.input_file, bmpinfoheader+4, headerSize-4)) + + ERREXIT(cinfo, JERR_INPUT_EOF); + + + + switch ((int) headerSize) { + + case 12: + + /* Decode OS/2 1.x header (Microsoft calls this a BITMAPCOREHEADER) */ + + biWidth = (INT32) GET_2B(bmpinfoheader,4); + + biHeight = (INT32) GET_2B(bmpinfoheader,6); + + biPlanes = GET_2B(bmpinfoheader,8); + + source->bits_per_pixel = (int) GET_2B(bmpinfoheader,10); + + + + switch (source->bits_per_pixel) { + + case 8: /* colormapped image */ + + mapentrysize = 3; /* OS/2 uses RGBTRIPLE colormap */ + + TRACEMS2(cinfo, 1, JTRC_BMP_OS2_MAPPED, (int) biWidth, (int) biHeight); + + break; + + case 24: /* RGB image */ + + TRACEMS2(cinfo, 1, JTRC_BMP_OS2, (int) biWidth, (int) biHeight); + + break; + + default: + + ERREXIT(cinfo, JERR_BMP_BADDEPTH); + + break; + + } + + if (biPlanes != 1) + + ERREXIT(cinfo, JERR_BMP_BADPLANES); + + break; + + case 40: + + case 64: + + /* Decode Windows 3.x header (Microsoft calls this a BITMAPINFOHEADER) */ + + /* or OS/2 2.x header, which has additional fields that we ignore */ + + biWidth = GET_4B(bmpinfoheader,4); + + biHeight = GET_4B(bmpinfoheader,8); + + biPlanes = GET_2B(bmpinfoheader,12); + + source->bits_per_pixel = (int) GET_2B(bmpinfoheader,14); + + biCompression = GET_4B(bmpinfoheader,16); + + biXPelsPerMeter = GET_4B(bmpinfoheader,24); + + biYPelsPerMeter = GET_4B(bmpinfoheader,28); + + biClrUsed = GET_4B(bmpinfoheader,32); + + /* biSizeImage, biClrImportant fields are ignored */ + + + + switch (source->bits_per_pixel) { + + case 8: /* colormapped image */ + + mapentrysize = 4; /* Windows uses RGBQUAD colormap */ + + TRACEMS2(cinfo, 1, JTRC_BMP_MAPPED, (int) biWidth, (int) biHeight); + + break; + + case 24: /* RGB image */ + + TRACEMS2(cinfo, 1, JTRC_BMP, (int) biWidth, (int) biHeight); + + break; + + default: + + ERREXIT(cinfo, JERR_BMP_BADDEPTH); + + break; + + } + + if (biPlanes != 1) + + ERREXIT(cinfo, JERR_BMP_BADPLANES); + + if (biCompression != 0) + + ERREXIT(cinfo, JERR_BMP_COMPRESSED); + + + + if (biXPelsPerMeter > 0 && biYPelsPerMeter > 0) { + + /* Set JFIF density parameters from the BMP data */ + + cinfo->X_density = (UINT16) (biXPelsPerMeter/100); /* 100 cm per meter */ + + cinfo->Y_density = (UINT16) (biYPelsPerMeter/100); + + cinfo->density_unit = 2; /* dots/cm */ + + } + + break; + + default: + + ERREXIT(cinfo, JERR_BMP_BADHEADER); + + break; + + } + + + + /* Compute distance to bitmap data --- will adjust for colormap below */ + + bPad = bfOffBits - (headerSize + 14); + + + + /* Read the colormap, if any */ + + if (mapentrysize > 0) { + + if (biClrUsed <= 0) + + biClrUsed = 256; /* assume it's 256 */ + + else if (biClrUsed > 256) + + ERREXIT(cinfo, JERR_BMP_BADCMAP); + + /* Allocate space to store the colormap */ + + source->colormap = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (JDIMENSION) biClrUsed, (JDIMENSION) 3); + + /* and read it from the file */ + + read_colormap(source, (int) biClrUsed, mapentrysize); + + /* account for size of colormap */ + + bPad -= biClrUsed * mapentrysize; + + } + + + + /* Skip any remaining pad bytes */ + + if (bPad < 0) /* incorrect bfOffBits value? */ + + ERREXIT(cinfo, JERR_BMP_BADHEADER); + + while (--bPad >= 0) { + + (void) read_byte(source); + + } + + + + /* Compute row width in file, including padding to 4-byte boundary */ + + if (source->bits_per_pixel == 24) + + row_width = (JDIMENSION) (biWidth * 3); + + else + + row_width = (JDIMENSION) biWidth; + + while ((row_width & 3) != 0) row_width++; + + source->row_width = row_width; + + + + /* Allocate space for inversion array, prepare for preload pass */ + + source->whole_image = (*cinfo->mem->request_virt_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + + row_width, (JDIMENSION) biHeight, (JDIMENSION) 1); + + source->pub.get_pixel_rows = preload_image; + + if (cinfo->progress != NULL) { + + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; + + progress->total_extra_passes++; /* count file input as separate pass */ + + } + + + + /* Allocate one-row buffer for returned data */ + + source->pub.buffer = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (JDIMENSION) (biWidth * 3), (JDIMENSION) 1); + + source->pub.buffer_height = 1; + + + + cinfo->in_color_space = JCS_RGB; + + cinfo->input_components = 3; + + cinfo->data_precision = 8; + + cinfo->image_width = (JDIMENSION) biWidth; + + cinfo->image_height = (JDIMENSION) biHeight; + +} + + + + + +/* + + * Finish up at the end of the file. + + */ + + + +METHODDEF void + +finish_input_bmp (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +{ + + /* no work */ + +} + + + + + +/* + + * The module selection routine for BMP format input. + + */ + + + +GLOBAL cjpeg_source_ptr + +jinit_read_bmp (j_compress_ptr cinfo) + +{ + + bmp_source_ptr source; + + + + /* Create module interface object */ + + source = (bmp_source_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(bmp_source_struct)); + + source->cinfo = cinfo; /* make back link for subroutines */ + + /* Fill in method ptrs, except get_pixel_rows which start_input sets */ + + source->pub.start_input = start_input_bmp; + + source->pub.finish_input = finish_input_bmp; + + + + return (cjpeg_source_ptr) source; + +} + + + +#endif /* BMP_SUPPORTED */ + diff --git a/utils/roq2/jpeg/rdcolmap.c b/utils/roq2/jpeg/rdcolmap.c new file mode 100644 index 0000000..9e09cce --- /dev/null +++ b/utils/roq2/jpeg/rdcolmap.c @@ -0,0 +1,506 @@ +/* + + * rdcolmap.c + + * + + * Copyright (C) 1994-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file implements djpeg's "-map file" switch. It reads a source image + + * and constructs a colormap to be supplied to the JPEG decompressor. + + * + + * Currently, these file formats are supported for the map file: + + * GIF: the contents of the GIF's global colormap are used. + + * PPM (either text or raw flavor): the entire file is read and + + * each unique pixel value is entered in the map. + + * Note that reading a large PPM file will be horrendously slow. + + * Typically, a PPM-format map file should contain just one pixel + + * of each desired color. Such a file can be extracted from an + + * ordinary image PPM file with ppmtomap(1). + + * + + * Rescaling a PPM that has a maxval unequal to MAXJSAMPLE is not + + * currently implemented. + + */ + + + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + + + +#ifdef QUANT_2PASS_SUPPORTED /* otherwise can't quantize to supplied map */ + + + +/* Portions of this code are based on the PBMPLUS library, which is: + +** + +** Copyright (C) 1988 by Jef Poskanzer. + +** + +** Permission to use, copy, modify, and distribute this software and its + +** documentation for any purpose and without fee is hereby granted, provided + +** that the above copyright notice appear in all copies and that both that + +** copyright notice and this permission notice appear in supporting + +** documentation. This software is provided "as is" without express or + +** implied warranty. + +*/ + + + + + +/* + + * Add a (potentially) new color to the color map. + + */ + + + +LOCAL void + +add_map_entry (j_decompress_ptr cinfo, int R, int G, int B) + +{ + + JSAMPROW colormap0 = cinfo->colormap[0]; + + JSAMPROW colormap1 = cinfo->colormap[1]; + + JSAMPROW colormap2 = cinfo->colormap[2]; + + int ncolors = cinfo->actual_number_of_colors; + + int index; + + + + /* Check for duplicate color. */ + + for (index = 0; index < ncolors; index++) { + + if (GETJSAMPLE(colormap0[index]) == R && + + GETJSAMPLE(colormap1[index]) == G && + + GETJSAMPLE(colormap2[index]) == B) + + return; /* color is already in map */ + + } + + + + /* Check for map overflow. */ + + if (ncolors >= (MAXJSAMPLE+1)) + + ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, (MAXJSAMPLE+1)); + + + + /* OK, add color to map. */ + + colormap0[ncolors] = (JSAMPLE) R; + + colormap1[ncolors] = (JSAMPLE) G; + + colormap2[ncolors] = (JSAMPLE) B; + + cinfo->actual_number_of_colors++; + +} + + + + + +/* + + * Extract color map from a GIF file. + + */ + + + +LOCAL void + +read_gif_map (j_decompress_ptr cinfo, FILE * infile) + +{ + + int header[13]; + + int i, colormaplen; + + int R, G, B; + + + + /* Initial 'G' has already been read by read_color_map */ + + /* Read the rest of the GIF header and logical screen descriptor */ + + for (i = 1; i < 13; i++) { + + if ((header[i] = getc(infile)) == EOF) + + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + + } + + + + /* Verify GIF Header */ + + if (header[1] != 'I' || header[2] != 'F') + + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + + + + /* There must be a global color map. */ + + if ((header[10] & 0x80) == 0) + + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + + + + /* OK, fetch it. */ + + colormaplen = 2 << (header[10] & 0x07); + + + + for (i = 0; i < colormaplen; i++) { + + R = getc(infile); + + G = getc(infile); + + B = getc(infile); + + if (R == EOF || G == EOF || B == EOF) + + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + + add_map_entry(cinfo, + + R << (BITS_IN_JSAMPLE-8), + + G << (BITS_IN_JSAMPLE-8), + + B << (BITS_IN_JSAMPLE-8)); + + } + +} + + + + + +/* Support routines for reading PPM */ + + + + + +LOCAL int + +pbm_getc (FILE * infile) + +/* Read next char, skipping over any comments */ + +/* A comment/newline sequence is returned as a newline */ + +{ + + register int ch; + + + + ch = getc(infile); + + if (ch == '#') { + + do { + + ch = getc(infile); + + } while (ch != '\n' && ch != EOF); + + } + + return ch; + +} + + + + + +LOCAL unsigned int + +read_pbm_integer (j_decompress_ptr cinfo, FILE * infile) + +/* Read an unsigned decimal integer from the PPM file */ + +/* Swallows one trailing character after the integer */ + +/* Note that on a 16-bit-int machine, only values up to 64k can be read. */ + +/* This should not be a problem in practice. */ + +{ + + register int ch; + + register unsigned int val; + + + + /* Skip any leading whitespace */ + + do { + + ch = pbm_getc(infile); + + if (ch == EOF) + + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + + } while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'); + + + + if (ch < '0' || ch > '9') + + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + + + + val = ch - '0'; + + while ((ch = pbm_getc(infile)) >= '0' && ch <= '9') { + + val *= 10; + + val += ch - '0'; + + } + + return val; + +} + + + + + +/* + + * Extract color map from a PPM file. + + */ + + + +LOCAL void + +read_ppm_map (j_decompress_ptr cinfo, FILE * infile) + +{ + + int c; + + unsigned int w, h, maxval, row, col; + + int R, G, B; + + + + /* Initial 'P' has already been read by read_color_map */ + + c = getc(infile); /* save format discriminator for a sec */ + + + + /* while we fetch the remaining header info */ + + w = read_pbm_integer(cinfo, infile); + + h = read_pbm_integer(cinfo, infile); + + maxval = read_pbm_integer(cinfo, infile); + + + + if (w <= 0 || h <= 0 || maxval <= 0) /* error check */ + + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + + + + /* For now, we don't support rescaling from an unusual maxval. */ + + if (maxval != (unsigned int) MAXJSAMPLE) + + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + + + + switch (c) { + + case '3': /* it's a text-format PPM file */ + + for (row = 0; row < h; row++) { + + for (col = 0; col < w; col++) { + + R = read_pbm_integer(cinfo, infile); + + G = read_pbm_integer(cinfo, infile); + + B = read_pbm_integer(cinfo, infile); + + add_map_entry(cinfo, R, G, B); + + } + + } + + break; + + + + case '6': /* it's a raw-format PPM file */ + + for (row = 0; row < h; row++) { + + for (col = 0; col < w; col++) { + + R = pbm_getc(infile); + + G = pbm_getc(infile); + + B = pbm_getc(infile); + + if (R == EOF || G == EOF || B == EOF) + + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + + add_map_entry(cinfo, R, G, B); + + } + + } + + break; + + + + default: + + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + + break; + + } + +} + + + + + +/* + + * Main entry point from djpeg.c. + + * Input: opened input file (from file name argument on command line). + + * Output: colormap and actual_number_of_colors fields are set in cinfo. + + */ + + + +GLOBAL void + +read_color_map (j_decompress_ptr cinfo, FILE * infile) + +{ + + /* Allocate space for a color map of maximum supported size. */ + + cinfo->colormap = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (JDIMENSION) (MAXJSAMPLE+1), (JDIMENSION) 3); + + cinfo->actual_number_of_colors = 0; /* initialize map to empty */ + + + + /* Read first byte to determine file format */ + + switch (getc(infile)) { + + case 'G': + + read_gif_map(cinfo, infile); + + break; + + case 'P': + + read_ppm_map(cinfo, infile); + + break; + + default: + + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + + break; + + } + +} + + + +#endif /* QUANT_2PASS_SUPPORTED */ + diff --git a/utils/roq2/jpeg/rdgif.c b/utils/roq2/jpeg/rdgif.c new file mode 100644 index 0000000..daca2b6 --- /dev/null +++ b/utils/roq2/jpeg/rdgif.c @@ -0,0 +1,1366 @@ +/* + + * rdgif.c + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + ************************************************************************** + + * WARNING: You will need an LZW patent license from Unisys in order to * + + * use this file legally in any commercial or shareware application. * + + ************************************************************************** + + * + + * This file contains routines to read input images in GIF format. + + * + + * These routines may need modification for non-Unix environments or + + * specialized applications. As they stand, they assume input from + + * an ordinary stdio stream. They further assume that reading begins + + * at the start of the file; input_init may need work if the + + * user interface has already read some data (e.g., to determine that + + * the file is indeed GIF format). + + */ + + + +/* + + * This code is loosely based on giftoppm from the PBMPLUS distribution + + * of Feb. 1991. That file contains the following copyright notice: + + * +-------------------------------------------------------------------+ + + * | Copyright 1990, David Koblas. | + + * | Permission to use, copy, modify, and distribute this software | + + * | and its documentation for any purpose and without fee is hereby | + + * | granted, provided that the above copyright notice appear in all | + + * | copies and that both that copyright notice and this permission | + + * | notice appear in supporting documentation. This software is | + + * | provided "as is" without express or implied warranty. | + + * +-------------------------------------------------------------------+ + + * + + * We are also required to state that + + * "The Graphics Interchange Format(c) is the Copyright property of + + * CompuServe Incorporated. GIF(sm) is a Service Mark property of + + * CompuServe Incorporated." + + */ + + + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + + + +#ifdef GIF_SUPPORTED + + + + + +#define MAXCOLORMAPSIZE 256 /* max # of colors in a GIF colormap */ + +#define NUMCOLORS 3 /* # of colors */ + +#define CM_RED 0 /* color component numbers */ + +#define CM_GREEN 1 + +#define CM_BLUE 2 + + + +#define MAX_LZW_BITS 12 /* maximum LZW code size */ + +#define LZW_TABLE_SIZE (1< table of prefix symbols */ + + UINT8 FAR *symbol_tail; /* => table of suffix bytes */ + + UINT8 FAR *symbol_stack; /* => stack for symbol expansions */ + + UINT8 FAR *sp; /* stack pointer */ + + + + /* State for interlaced image processing */ + + boolean is_interlaced; /* TRUE if have interlaced image */ + + jvirt_sarray_ptr interlaced_image; /* full image in interlaced order */ + + JDIMENSION cur_row_number; /* need to know actual row number */ + + JDIMENSION pass2_offset; /* # of pixel rows in pass 1 */ + + JDIMENSION pass3_offset; /* # of pixel rows in passes 1&2 */ + + JDIMENSION pass4_offset; /* # of pixel rows in passes 1,2,3 */ + +} gif_source_struct; + + + +typedef gif_source_struct * gif_source_ptr; + + + + + +/* Forward declarations */ + +METHODDEF JDIMENSION get_pixel_rows + + JPP((j_compress_ptr cinfo, cjpeg_source_ptr sinfo)); + +METHODDEF JDIMENSION load_interlaced_image + + JPP((j_compress_ptr cinfo, cjpeg_source_ptr sinfo)); + +METHODDEF JDIMENSION get_interlaced_row + + JPP((j_compress_ptr cinfo, cjpeg_source_ptr sinfo)); + + + + + +LOCAL int + +ReadByte (gif_source_ptr sinfo) + +/* Read next byte from GIF file */ + +{ + + register FILE * infile = sinfo->pub.input_file; + + int c; + + + + if ((c = getc(infile)) == EOF) + + ERREXIT(sinfo->cinfo, JERR_INPUT_EOF); + + return c; + +} + + + + + +LOCAL int + +GetDataBlock (gif_source_ptr sinfo, char *buf) + +/* Read a GIF data block, which has a leading count byte */ + +/* A zero-length block marks the end of a data block sequence */ + +{ + + int count; + + + + count = ReadByte(sinfo); + + if (count > 0) { + + if (! ReadOK(sinfo->pub.input_file, buf, count)) + + ERREXIT(sinfo->cinfo, JERR_INPUT_EOF); + + } + + return count; + +} + + + + + +LOCAL void + +SkipDataBlocks (gif_source_ptr sinfo) + +/* Skip a series of data blocks, until a block terminator is found */ + +{ + + char buf[256]; + + + + while (GetDataBlock(sinfo, buf) > 0) + + /* skip */; + +} + + + + + +LOCAL void + +ReInitLZW (gif_source_ptr sinfo) + +/* (Re)initialize LZW state; shared code for startup and Clear processing */ + +{ + + sinfo->code_size = sinfo->input_code_size + 1; + + sinfo->limit_code = sinfo->clear_code << 1; /* 2^code_size */ + + sinfo->max_code = sinfo->clear_code + 2; /* first unused code value */ + + sinfo->sp = sinfo->symbol_stack; /* init stack to empty */ + +} + + + + + +LOCAL void + +InitLZWCode (gif_source_ptr sinfo) + +/* Initialize for a series of LZWReadByte (and hence GetCode) calls */ + +{ + + /* GetCode initialization */ + + sinfo->last_byte = 2; /* make safe to "recopy last two bytes" */ + + sinfo->last_bit = 0; /* nothing in the buffer */ + + sinfo->cur_bit = 0; /* force buffer load on first call */ + + sinfo->out_of_blocks = FALSE; + + + + /* LZWReadByte initialization: */ + + /* compute special code values (note that these do not change later) */ + + sinfo->clear_code = 1 << sinfo->input_code_size; + + sinfo->end_code = sinfo->clear_code + 1; + + sinfo->first_time = TRUE; + + ReInitLZW(sinfo); + +} + + + + + +LOCAL int + +GetCode (gif_source_ptr sinfo) + +/* Fetch the next code_size bits from the GIF data */ + +/* We assume code_size is less than 16 */ + +{ + + register INT32 accum; + + int offs, ret, count; + + + + while ( (sinfo->cur_bit + sinfo->code_size) > sinfo->last_bit) { + + /* Time to reload the buffer */ + + if (sinfo->out_of_blocks) { + + WARNMS(sinfo->cinfo, JWRN_GIF_NOMOREDATA); + + return sinfo->end_code; /* fake something useful */ + + } + + /* preserve last two bytes of what we have -- assume code_size <= 16 */ + + sinfo->code_buf[0] = sinfo->code_buf[sinfo->last_byte-2]; + + sinfo->code_buf[1] = sinfo->code_buf[sinfo->last_byte-1]; + + /* Load more bytes; set flag if we reach the terminator block */ + + if ((count = GetDataBlock(sinfo, &sinfo->code_buf[2])) == 0) { + + sinfo->out_of_blocks = TRUE; + + WARNMS(sinfo->cinfo, JWRN_GIF_NOMOREDATA); + + return sinfo->end_code; /* fake something useful */ + + } + + /* Reset counters */ + + sinfo->cur_bit = (sinfo->cur_bit - sinfo->last_bit) + 16; + + sinfo->last_byte = 2 + count; + + sinfo->last_bit = sinfo->last_byte * 8; + + } + + + + /* Form up next 24 bits in accum */ + + offs = sinfo->cur_bit >> 3; /* byte containing cur_bit */ + +#ifdef CHAR_IS_UNSIGNED + + accum = sinfo->code_buf[offs+2]; + + accum <<= 8; + + accum |= sinfo->code_buf[offs+1]; + + accum <<= 8; + + accum |= sinfo->code_buf[offs]; + +#else + + accum = sinfo->code_buf[offs+2] & 0xFF; + + accum <<= 8; + + accum |= sinfo->code_buf[offs+1] & 0xFF; + + accum <<= 8; + + accum |= sinfo->code_buf[offs] & 0xFF; + +#endif + + + + /* Right-align cur_bit in accum, then mask off desired number of bits */ + + accum >>= (sinfo->cur_bit & 7); + + ret = ((int) accum) & ((1 << sinfo->code_size) - 1); + + + + sinfo->cur_bit += sinfo->code_size; + + return ret; + +} + + + + + +LOCAL int + +LZWReadByte (gif_source_ptr sinfo) + +/* Read an LZW-compressed byte */ + +{ + + register int code; /* current working code */ + + int incode; /* saves actual input code */ + + + + /* First time, just eat the expected Clear code(s) and return next code, */ + + /* which is expected to be a raw byte. */ + + if (sinfo->first_time) { + + sinfo->first_time = FALSE; + + code = sinfo->clear_code; /* enables sharing code with Clear case */ + + } else { + + + + /* If any codes are stacked from a previously read symbol, return them */ + + if (sinfo->sp > sinfo->symbol_stack) + + return (int) *(-- sinfo->sp); + + + + /* Time to read a new symbol */ + + code = GetCode(sinfo); + + + + } + + + + if (code == sinfo->clear_code) { + + /* Reinit state, swallow any extra Clear codes, and */ + + /* return next code, which is expected to be a raw byte. */ + + ReInitLZW(sinfo); + + do { + + code = GetCode(sinfo); + + } while (code == sinfo->clear_code); + + if (code > sinfo->clear_code) { /* make sure it is a raw byte */ + + WARNMS(sinfo->cinfo, JWRN_GIF_BADDATA); + + code = 0; /* use something valid */ + + } + + /* make firstcode, oldcode valid! */ + + sinfo->firstcode = sinfo->oldcode = code; + + return code; + + } + + + + if (code == sinfo->end_code) { + + /* Skip the rest of the image, unless GetCode already read terminator */ + + if (! sinfo->out_of_blocks) { + + SkipDataBlocks(sinfo); + + sinfo->out_of_blocks = TRUE; + + } + + /* Complain that there's not enough data */ + + WARNMS(sinfo->cinfo, JWRN_GIF_ENDCODE); + + /* Pad data with 0's */ + + return 0; /* fake something usable */ + + } + + + + /* Got normal raw byte or LZW symbol */ + + incode = code; /* save for a moment */ + + + + if (code >= sinfo->max_code) { /* special case for not-yet-defined symbol */ + + /* code == max_code is OK; anything bigger is bad data */ + + if (code > sinfo->max_code) { + + WARNMS(sinfo->cinfo, JWRN_GIF_BADDATA); + + incode = 0; /* prevent creation of loops in symbol table */ + + } + + /* this symbol will be defined as oldcode/firstcode */ + + *(sinfo->sp++) = (UINT8) sinfo->firstcode; + + code = sinfo->oldcode; + + } + + + + /* If it's a symbol, expand it into the stack */ + + while (code >= sinfo->clear_code) { + + *(sinfo->sp++) = sinfo->symbol_tail[code]; /* tail is a byte value */ + + code = sinfo->symbol_head[code]; /* head is another LZW symbol */ + + } + + /* At this point code just represents a raw byte */ + + sinfo->firstcode = code; /* save for possible future use */ + + + + /* If there's room in table, */ + + if ((code = sinfo->max_code) < LZW_TABLE_SIZE) { + + /* Define a new symbol = prev sym + head of this sym's expansion */ + + sinfo->symbol_head[code] = sinfo->oldcode; + + sinfo->symbol_tail[code] = (UINT8) sinfo->firstcode; + + sinfo->max_code++; + + /* Is it time to increase code_size? */ + + if ((sinfo->max_code >= sinfo->limit_code) && + + (sinfo->code_size < MAX_LZW_BITS)) { + + sinfo->code_size++; + + sinfo->limit_code <<= 1; /* keep equal to 2^code_size */ + + } + + } + + + + sinfo->oldcode = incode; /* save last input symbol for future use */ + + return sinfo->firstcode; /* return first byte of symbol's expansion */ + +} + + + + + +LOCAL void + +ReadColorMap (gif_source_ptr sinfo, int cmaplen, JSAMPARRAY cmap) + +/* Read a GIF colormap */ + +{ + + int i; + + + + for (i = 0; i < cmaplen; i++) { + +#if BITS_IN_JSAMPLE == 8 + +#define UPSCALE(x) (x) + +#else + +#define UPSCALE(x) ((x) << (BITS_IN_JSAMPLE-8)) + +#endif + + cmap[CM_RED][i] = (JSAMPLE) UPSCALE(ReadByte(sinfo)); + + cmap[CM_GREEN][i] = (JSAMPLE) UPSCALE(ReadByte(sinfo)); + + cmap[CM_BLUE][i] = (JSAMPLE) UPSCALE(ReadByte(sinfo)); + + } + +} + + + + + +LOCAL void + +DoExtension (gif_source_ptr sinfo) + +/* Process an extension block */ + +/* Currently we ignore 'em all */ + +{ + + int extlabel; + + + + /* Read extension label byte */ + + extlabel = ReadByte(sinfo); + + TRACEMS1(sinfo->cinfo, 1, JTRC_GIF_EXTENSION, extlabel); + + /* Skip the data block(s) associated with the extension */ + + SkipDataBlocks(sinfo); + +} + + + + + +/* + + * Read the file header; return image size and component count. + + */ + + + +METHODDEF void + +start_input_gif (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +{ + + gif_source_ptr source = (gif_source_ptr) sinfo; + + char hdrbuf[10]; /* workspace for reading control blocks */ + + unsigned int width, height; /* image dimensions */ + + int colormaplen, aspectRatio; + + int c; + + + + /* Allocate space to store the colormap */ + + source->colormap = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (JDIMENSION) MAXCOLORMAPSIZE, (JDIMENSION) NUMCOLORS); + + + + /* Read and verify GIF Header */ + + if (! ReadOK(source->pub.input_file, hdrbuf, 6)) + + ERREXIT(cinfo, JERR_GIF_NOT); + + if (hdrbuf[0] != 'G' || hdrbuf[1] != 'I' || hdrbuf[2] != 'F') + + ERREXIT(cinfo, JERR_GIF_NOT); + + /* Check for expected version numbers. + + * If unknown version, give warning and try to process anyway; + + * this is per recommendation in GIF89a standard. + + */ + + if ((hdrbuf[3] != '8' || hdrbuf[4] != '7' || hdrbuf[5] != 'a') && + + (hdrbuf[3] != '8' || hdrbuf[4] != '9' || hdrbuf[5] != 'a')) + + TRACEMS3(cinfo, 1, JTRC_GIF_BADVERSION, hdrbuf[3], hdrbuf[4], hdrbuf[5]); + + + + /* Read and decipher Logical Screen Descriptor */ + + if (! ReadOK(source->pub.input_file, hdrbuf, 7)) + + ERREXIT(cinfo, JERR_INPUT_EOF); + + width = LM_to_uint(hdrbuf[0],hdrbuf[1]); + + height = LM_to_uint(hdrbuf[2],hdrbuf[3]); + + colormaplen = 2 << (hdrbuf[4] & 0x07); + + /* we ignore the color resolution, sort flag, and background color index */ + + aspectRatio = hdrbuf[6] & 0xFF; + + if (aspectRatio != 0 && aspectRatio != 49) + + TRACEMS(cinfo, 1, JTRC_GIF_NONSQUARE); + + + + /* Read global colormap if header indicates it is present */ + + if (BitSet(hdrbuf[4], COLORMAPFLAG)) + + ReadColorMap(source, colormaplen, source->colormap); + + + + /* Scan until we reach start of desired image. + + * We don't currently support skipping images, but could add it easily. + + */ + + for (;;) { + + c = ReadByte(source); + + + + if (c == ';') /* GIF terminator?? */ + + ERREXIT(cinfo, JERR_GIF_IMAGENOTFOUND); + + + + if (c == '!') { /* Extension */ + + DoExtension(source); + + continue; + + } + + + + if (c != ',') { /* Not an image separator? */ + + WARNMS1(cinfo, JWRN_GIF_CHAR, c); + + continue; + + } + + + + /* Read and decipher Local Image Descriptor */ + + if (! ReadOK(source->pub.input_file, hdrbuf, 9)) + + ERREXIT(cinfo, JERR_INPUT_EOF); + + /* we ignore top/left position info, also sort flag */ + + width = LM_to_uint(hdrbuf[4],hdrbuf[5]); + + height = LM_to_uint(hdrbuf[6],hdrbuf[7]); + + source->is_interlaced = BitSet(hdrbuf[8], INTERLACE); + + + + /* Read local colormap if header indicates it is present */ + + /* Note: if we wanted to support skipping images, */ + + /* we'd need to skip rather than read colormap for ignored images */ + + if (BitSet(hdrbuf[8], COLORMAPFLAG)) { + + colormaplen = 2 << (hdrbuf[8] & 0x07); + + ReadColorMap(source, colormaplen, source->colormap); + + } + + + + source->input_code_size = ReadByte(source); /* get min-code-size byte */ + + if (source->input_code_size < 2 || source->input_code_size >= MAX_LZW_BITS) + + ERREXIT1(cinfo, JERR_GIF_CODESIZE, source->input_code_size); + + + + /* Reached desired image, so break out of loop */ + + /* If we wanted to skip this image, */ + + /* we'd call SkipDataBlocks and then continue the loop */ + + break; + + } + + + + /* Prepare to read selected image: first initialize LZW decompressor */ + + source->symbol_head = (UINT16 FAR *) + + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + LZW_TABLE_SIZE * SIZEOF(UINT16)); + + source->symbol_tail = (UINT8 FAR *) + + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + LZW_TABLE_SIZE * SIZEOF(UINT8)); + + source->symbol_stack = (UINT8 FAR *) + + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + LZW_TABLE_SIZE * SIZEOF(UINT8)); + + InitLZWCode(source); + + + + /* + + * If image is interlaced, we read it into a full-size sample array, + + * decompressing as we go; then get_interlaced_row selects rows from the + + * sample array in the proper order. + + */ + + if (source->is_interlaced) { + + /* We request the virtual array now, but can't access it until virtual + + * arrays have been allocated. Hence, the actual work of reading the + + * image is postponed until the first call to get_pixel_rows. + + */ + + source->interlaced_image = (*cinfo->mem->request_virt_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + + (JDIMENSION) width, (JDIMENSION) height, (JDIMENSION) 1); + + if (cinfo->progress != NULL) { + + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; + + progress->total_extra_passes++; /* count file input as separate pass */ + + } + + source->pub.get_pixel_rows = load_interlaced_image; + + } else { + + source->pub.get_pixel_rows = get_pixel_rows; + + } + + + + /* Create compressor input buffer. */ + + source->pub.buffer = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (JDIMENSION) width * NUMCOLORS, (JDIMENSION) 1); + + source->pub.buffer_height = 1; + + + + /* Return info about the image. */ + + cinfo->in_color_space = JCS_RGB; + + cinfo->input_components = NUMCOLORS; + + cinfo->data_precision = BITS_IN_JSAMPLE; /* we always rescale data to this */ + + cinfo->image_width = width; + + cinfo->image_height = height; + + + + TRACEMS3(cinfo, 1, JTRC_GIF, width, height, colormaplen); + +} + + + + + +/* + + * Read one row of pixels. + + * This version is used for noninterlaced GIF images: + + * we read directly from the GIF file. + + */ + + + +METHODDEF JDIMENSION + +get_pixel_rows (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +{ + + gif_source_ptr source = (gif_source_ptr) sinfo; + + register int c; + + register JSAMPROW ptr; + + register JDIMENSION col; + + register JSAMPARRAY colormap = source->colormap; + + + + ptr = source->pub.buffer[0]; + + for (col = cinfo->image_width; col > 0; col--) { + + c = LZWReadByte(source); + + *ptr++ = colormap[CM_RED][c]; + + *ptr++ = colormap[CM_GREEN][c]; + + *ptr++ = colormap[CM_BLUE][c]; + + } + + return 1; + +} + + + + + +/* + + * Read one row of pixels. + + * This version is used for the first call on get_pixel_rows when + + * reading an interlaced GIF file: we read the whole image into memory. + + */ + + + +METHODDEF JDIMENSION + +load_interlaced_image (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +{ + + gif_source_ptr source = (gif_source_ptr) sinfo; + + JSAMPARRAY image_ptr; + + register JSAMPROW sptr; + + register JDIMENSION col; + + JDIMENSION row; + + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; + + + + /* Read the interlaced image into the virtual array we've created. */ + + for (row = 0; row < cinfo->image_height; row++) { + + if (progress != NULL) { + + progress->pub.pass_counter = (long) row; + + progress->pub.pass_limit = (long) cinfo->image_height; + + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + + } + + image_ptr = (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, source->interlaced_image, + + row, (JDIMENSION) 1, TRUE); + + sptr = image_ptr[0]; + + for (col = cinfo->image_width; col > 0; col--) { + + *sptr++ = (JSAMPLE) LZWReadByte(source); + + } + + } + + if (progress != NULL) + + progress->completed_extra_passes++; + + + + /* Replace method pointer so subsequent calls don't come here. */ + + source->pub.get_pixel_rows = get_interlaced_row; + + /* Initialize for get_interlaced_row, and perform first call on it. */ + + source->cur_row_number = 0; + + source->pass2_offset = (cinfo->image_height + 7) / 8; + + source->pass3_offset = source->pass2_offset + (cinfo->image_height + 3) / 8; + + source->pass4_offset = source->pass3_offset + (cinfo->image_height + 1) / 4; + + + + return get_interlaced_row(cinfo, sinfo); + +} + + + + + +/* + + * Read one row of pixels. + + * This version is used for interlaced GIF images: + + * we read from the virtual array. + + */ + + + +METHODDEF JDIMENSION + +get_interlaced_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +{ + + gif_source_ptr source = (gif_source_ptr) sinfo; + + JSAMPARRAY image_ptr; + + register int c; + + register JSAMPROW sptr, ptr; + + register JDIMENSION col; + + register JSAMPARRAY colormap = source->colormap; + + JDIMENSION irow; + + + + /* Figure out which row of interlaced image is needed, and access it. */ + + switch ((int) (source->cur_row_number & 7)) { + + case 0: /* first-pass row */ + + irow = source->cur_row_number >> 3; + + break; + + case 4: /* second-pass row */ + + irow = (source->cur_row_number >> 3) + source->pass2_offset; + + break; + + case 2: /* third-pass row */ + + case 6: + + irow = (source->cur_row_number >> 2) + source->pass3_offset; + + break; + + default: /* fourth-pass row */ + + irow = (source->cur_row_number >> 1) + source->pass4_offset; + + break; + + } + + image_ptr = (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, source->interlaced_image, + + irow, (JDIMENSION) 1, FALSE); + + /* Scan the row, expand colormap, and output */ + + sptr = image_ptr[0]; + + ptr = source->pub.buffer[0]; + + for (col = cinfo->image_width; col > 0; col--) { + + c = GETJSAMPLE(*sptr++); + + *ptr++ = colormap[CM_RED][c]; + + *ptr++ = colormap[CM_GREEN][c]; + + *ptr++ = colormap[CM_BLUE][c]; + + } + + source->cur_row_number++; /* for next time */ + + return 1; + +} + + + + + +/* + + * Finish up at the end of the file. + + */ + + + +METHODDEF void + +finish_input_gif (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +{ + + /* no work */ + +} + + + + + +/* + + * The module selection routine for GIF format input. + + */ + + + +GLOBAL cjpeg_source_ptr + +jinit_read_gif (j_compress_ptr cinfo) + +{ + + gif_source_ptr source; + + + + /* Create module interface object */ + + source = (gif_source_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(gif_source_struct)); + + source->cinfo = cinfo; /* make back link for subroutines */ + + /* Fill in method ptrs, except get_pixel_rows which start_input sets */ + + source->pub.start_input = start_input_gif; + + source->pub.finish_input = finish_input_gif; + + + + return (cjpeg_source_ptr) source; + +} + + + +#endif /* GIF_SUPPORTED */ + diff --git a/utils/roq2/jpeg/rdppm.c b/utils/roq2/jpeg/rdppm.c new file mode 100644 index 0000000..e58cccf --- /dev/null +++ b/utils/roq2/jpeg/rdppm.c @@ -0,0 +1,900 @@ +/* + + * rdppm.c + + * + + * Copyright (C) 1991-1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains routines to read input images in PPM/PGM format. + + * The extended 2-byte-per-sample raw PPM/PGM formats are supported. + + * The PBMPLUS library is NOT required to compile this software + + * (but it is highly useful as a set of PPM image manipulation programs). + + * + + * These routines may need modification for non-Unix environments or + + * specialized applications. As they stand, they assume input from + + * an ordinary stdio stream. They further assume that reading begins + + * at the start of the file; start_input may need work if the + + * user interface has already read some data (e.g., to determine that + + * the file is indeed PPM format). + + */ + + + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + + + +#ifdef PPM_SUPPORTED + + + + + +/* Portions of this code are based on the PBMPLUS library, which is: + +** + +** Copyright (C) 1988 by Jef Poskanzer. + +** + +** Permission to use, copy, modify, and distribute this software and its + +** documentation for any purpose and without fee is hereby granted, provided + +** that the above copyright notice appear in all copies and that both that + +** copyright notice and this permission notice appear in supporting + +** documentation. This software is provided "as is" without express or + +** implied warranty. + +*/ + + + + + +/* Macros to deal with unsigned chars as efficiently as compiler allows */ + + + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char U_CHAR; + +#define UCH(x) ((int) (x)) + +#else /* !HAVE_UNSIGNED_CHAR */ + +#ifdef CHAR_IS_UNSIGNED + +typedef char U_CHAR; + +#define UCH(x) ((int) (x)) + +#else + +typedef char U_CHAR; + +#define UCH(x) ((int) (x) & 0xFF) + +#endif + +#endif /* HAVE_UNSIGNED_CHAR */ + + + + + +#define ReadOK(file,buffer,len) (JFREAD(file,buffer,len) == ((size_t) (len))) + + + + + +/* + + * On most systems, reading individual bytes with getc() is drastically less + + * efficient than buffering a row at a time with fread(). On PCs, we must + + * allocate the buffer in near data space, because we are assuming small-data + + * memory model, wherein fread() can't reach far memory. If you need to + + * process very wide images on a PC, you might have to compile in large-memory + + * model, or else replace fread() with a getc() loop --- which will be much + + * slower. + + */ + + + + + +/* Private version of data source object */ + + + +typedef struct { + + struct cjpeg_source_struct pub; /* public fields */ + + + + U_CHAR *iobuffer; /* non-FAR pointer to I/O buffer */ + + JSAMPROW pixrow; /* FAR pointer to same */ + + size_t buffer_width; /* width of I/O buffer */ + + JSAMPLE *rescale; /* => maxval-remapping array, or NULL */ + +} ppm_source_struct; + + + +typedef ppm_source_struct * ppm_source_ptr; + + + + + +LOCAL int + +pbm_getc (FILE * infile) + +/* Read next char, skipping over any comments */ + +/* A comment/newline sequence is returned as a newline */ + +{ + + register int ch; + + + + ch = getc(infile); + + if (ch == '#') { + + do { + + ch = getc(infile); + + } while (ch != '\n' && ch != EOF); + + } + + return ch; + +} + + + + + +LOCAL unsigned int + +read_pbm_integer (j_compress_ptr cinfo, FILE * infile) + +/* Read an unsigned decimal integer from the PPM file */ + +/* Swallows one trailing character after the integer */ + +/* Note that on a 16-bit-int machine, only values up to 64k can be read. */ + +/* This should not be a problem in practice. */ + +{ + + register int ch; + + register unsigned int val; + + + + /* Skip any leading whitespace */ + + do { + + ch = pbm_getc(infile); + + if (ch == EOF) + + ERREXIT(cinfo, JERR_INPUT_EOF); + + } while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'); + + + + if (ch < '0' || ch > '9') + + ERREXIT(cinfo, JERR_PPM_NONNUMERIC); + + + + val = ch - '0'; + + while ((ch = pbm_getc(infile)) >= '0' && ch <= '9') { + + val *= 10; + + val += ch - '0'; + + } + + return val; + +} + + + + + +/* + + * Read one row of pixels. + + * + + * We provide several different versions depending on input file format. + + * In all cases, input is scaled to the size of JSAMPLE. + + * + + * A really fast path is provided for reading byte/sample raw files with + + * maxval = MAXJSAMPLE, which is the normal case for 8-bit data. + + */ + + + + + +METHODDEF JDIMENSION + +get_text_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +/* This version is for reading text-format PGM files with any maxval */ + +{ + + ppm_source_ptr source = (ppm_source_ptr) sinfo; + + FILE * infile = source->pub.input_file; + + register JSAMPROW ptr; + + register JSAMPLE *rescale = source->rescale; + + JDIMENSION col; + + + + ptr = source->pub.buffer[0]; + + for (col = cinfo->image_width; col > 0; col--) { + + *ptr++ = rescale[read_pbm_integer(cinfo, infile)]; + + } + + return 1; + +} + + + + + +METHODDEF JDIMENSION + +get_text_rgb_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +/* This version is for reading text-format PPM files with any maxval */ + +{ + + ppm_source_ptr source = (ppm_source_ptr) sinfo; + + FILE * infile = source->pub.input_file; + + register JSAMPROW ptr; + + register JSAMPLE *rescale = source->rescale; + + JDIMENSION col; + + + + ptr = source->pub.buffer[0]; + + for (col = cinfo->image_width; col > 0; col--) { + + *ptr++ = rescale[read_pbm_integer(cinfo, infile)]; + + *ptr++ = rescale[read_pbm_integer(cinfo, infile)]; + + *ptr++ = rescale[read_pbm_integer(cinfo, infile)]; + + } + + return 1; + +} + + + + + +METHODDEF JDIMENSION + +get_scaled_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +/* This version is for reading raw-byte-format PGM files with any maxval */ + +{ + + ppm_source_ptr source = (ppm_source_ptr) sinfo; + + register JSAMPROW ptr; + + register U_CHAR * bufferptr; + + register JSAMPLE *rescale = source->rescale; + + JDIMENSION col; + + + + if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) + + ERREXIT(cinfo, JERR_INPUT_EOF); + + ptr = source->pub.buffer[0]; + + bufferptr = source->iobuffer; + + for (col = cinfo->image_width; col > 0; col--) { + + *ptr++ = rescale[UCH(*bufferptr++)]; + + } + + return 1; + +} + + + + + +METHODDEF JDIMENSION + +get_scaled_rgb_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +/* This version is for reading raw-byte-format PPM files with any maxval */ + +{ + + ppm_source_ptr source = (ppm_source_ptr) sinfo; + + register JSAMPROW ptr; + + register U_CHAR * bufferptr; + + register JSAMPLE *rescale = source->rescale; + + JDIMENSION col; + + + + if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) + + ERREXIT(cinfo, JERR_INPUT_EOF); + + ptr = source->pub.buffer[0]; + + bufferptr = source->iobuffer; + + for (col = cinfo->image_width; col > 0; col--) { + + *ptr++ = rescale[UCH(*bufferptr++)]; + + *ptr++ = rescale[UCH(*bufferptr++)]; + + *ptr++ = rescale[UCH(*bufferptr++)]; + + } + + return 1; + +} + + + + + +METHODDEF JDIMENSION + +get_raw_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +/* This version is for reading raw-byte-format files with maxval = MAXJSAMPLE. + + * In this case we just read right into the JSAMPLE buffer! + + * Note that same code works for PPM and PGM files. + + */ + +{ + + ppm_source_ptr source = (ppm_source_ptr) sinfo; + + + + if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) + + ERREXIT(cinfo, JERR_INPUT_EOF); + + return 1; + +} + + + + + +METHODDEF JDIMENSION + +get_word_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +/* This version is for reading raw-word-format PGM files with any maxval */ + +{ + + ppm_source_ptr source = (ppm_source_ptr) sinfo; + + register JSAMPROW ptr; + + register U_CHAR * bufferptr; + + register JSAMPLE *rescale = source->rescale; + + JDIMENSION col; + + + + if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) + + ERREXIT(cinfo, JERR_INPUT_EOF); + + ptr = source->pub.buffer[0]; + + bufferptr = source->iobuffer; + + for (col = cinfo->image_width; col > 0; col--) { + + register int temp; + + temp = UCH(*bufferptr++); + + temp |= UCH(*bufferptr++) << 8; + + *ptr++ = rescale[temp]; + + } + + return 1; + +} + + + + + +METHODDEF JDIMENSION + +get_word_rgb_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +/* This version is for reading raw-word-format PPM files with any maxval */ + +{ + + ppm_source_ptr source = (ppm_source_ptr) sinfo; + + register JSAMPROW ptr; + + register U_CHAR * bufferptr; + + register JSAMPLE *rescale = source->rescale; + + JDIMENSION col; + + + + if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) + + ERREXIT(cinfo, JERR_INPUT_EOF); + + ptr = source->pub.buffer[0]; + + bufferptr = source->iobuffer; + + for (col = cinfo->image_width; col > 0; col--) { + + register int temp; + + temp = UCH(*bufferptr++); + + temp |= UCH(*bufferptr++) << 8; + + *ptr++ = rescale[temp]; + + temp = UCH(*bufferptr++); + + temp |= UCH(*bufferptr++) << 8; + + *ptr++ = rescale[temp]; + + temp = UCH(*bufferptr++); + + temp |= UCH(*bufferptr++) << 8; + + *ptr++ = rescale[temp]; + + } + + return 1; + +} + + + + + +/* + + * Read the file header; return image size and component count. + + */ + + + +METHODDEF void + +start_input_ppm (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +{ + + ppm_source_ptr source = (ppm_source_ptr) sinfo; + + int c; + + unsigned int w, h, maxval; + + boolean need_iobuffer, use_raw_buffer, need_rescale; + + + + if (getc(source->pub.input_file) != 'P') + + ERREXIT(cinfo, JERR_PPM_NOT); + + + + c = getc(source->pub.input_file); /* save format discriminator for a sec */ + + + + /* fetch the remaining header info */ + + w = read_pbm_integer(cinfo, source->pub.input_file); + + h = read_pbm_integer(cinfo, source->pub.input_file); + + maxval = read_pbm_integer(cinfo, source->pub.input_file); + + + + if (w <= 0 || h <= 0 || maxval <= 0) /* error check */ + + ERREXIT(cinfo, JERR_PPM_NOT); + + + + cinfo->data_precision = BITS_IN_JSAMPLE; /* we always rescale data to this */ + + cinfo->image_width = (JDIMENSION) w; + + cinfo->image_height = (JDIMENSION) h; + + + + /* initialize flags to most common settings */ + + need_iobuffer = TRUE; /* do we need an I/O buffer? */ + + use_raw_buffer = FALSE; /* do we map input buffer onto I/O buffer? */ + + need_rescale = TRUE; /* do we need a rescale array? */ + + + + switch (c) { + + case '2': /* it's a text-format PGM file */ + + cinfo->input_components = 1; + + cinfo->in_color_space = JCS_GRAYSCALE; + + TRACEMS2(cinfo, 1, JTRC_PGM_TEXT, w, h); + + source->pub.get_pixel_rows = get_text_gray_row; + + need_iobuffer = FALSE; + + break; + + + + case '3': /* it's a text-format PPM file */ + + cinfo->input_components = 3; + + cinfo->in_color_space = JCS_RGB; + + TRACEMS2(cinfo, 1, JTRC_PPM_TEXT, w, h); + + source->pub.get_pixel_rows = get_text_rgb_row; + + need_iobuffer = FALSE; + + break; + + + + case '5': /* it's a raw-format PGM file */ + + cinfo->input_components = 1; + + cinfo->in_color_space = JCS_GRAYSCALE; + + TRACEMS2(cinfo, 1, JTRC_PGM, w, h); + + if (maxval > 255) { + + source->pub.get_pixel_rows = get_word_gray_row; + + } else if (maxval == MAXJSAMPLE && SIZEOF(JSAMPLE) == SIZEOF(U_CHAR)) { + + source->pub.get_pixel_rows = get_raw_row; + + use_raw_buffer = TRUE; + + need_rescale = FALSE; + + } else { + + source->pub.get_pixel_rows = get_scaled_gray_row; + + } + + break; + + + + case '6': /* it's a raw-format PPM file */ + + cinfo->input_components = 3; + + cinfo->in_color_space = JCS_RGB; + + TRACEMS2(cinfo, 1, JTRC_PPM, w, h); + + if (maxval > 255) { + + source->pub.get_pixel_rows = get_word_rgb_row; + + } else if (maxval == MAXJSAMPLE && SIZEOF(JSAMPLE) == SIZEOF(U_CHAR)) { + + source->pub.get_pixel_rows = get_raw_row; + + use_raw_buffer = TRUE; + + need_rescale = FALSE; + + } else { + + source->pub.get_pixel_rows = get_scaled_rgb_row; + + } + + break; + + + + default: + + ERREXIT(cinfo, JERR_PPM_NOT); + + break; + + } + + + + /* Allocate space for I/O buffer: 1 or 3 bytes or words/pixel. */ + + if (need_iobuffer) { + + source->buffer_width = (size_t) w * cinfo->input_components * + + ((maxval<=255) ? SIZEOF(U_CHAR) : (2*SIZEOF(U_CHAR))); + + source->iobuffer = (U_CHAR *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + source->buffer_width); + + } + + + + /* Create compressor input buffer. */ + + if (use_raw_buffer) { + + /* For unscaled raw-input case, we can just map it onto the I/O buffer. */ + + /* Synthesize a JSAMPARRAY pointer structure */ + + /* Cast here implies near->far pointer conversion on PCs */ + + source->pixrow = (JSAMPROW) source->iobuffer; + + source->pub.buffer = & source->pixrow; + + source->pub.buffer_height = 1; + + } else { + + /* Need to translate anyway, so make a separate sample buffer. */ + + source->pub.buffer = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (JDIMENSION) w * cinfo->input_components, (JDIMENSION) 1); + + source->pub.buffer_height = 1; + + } + + + + /* Compute the rescaling array if required. */ + + if (need_rescale) { + + INT32 val, half_maxval; + + + + /* On 16-bit-int machines we have to be careful of maxval = 65535 */ + + source->rescale = (JSAMPLE *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (size_t) (((long) maxval + 1L) * SIZEOF(JSAMPLE))); + + half_maxval = maxval / 2; + + for (val = 0; val <= (INT32) maxval; val++) { + + /* The multiplication here must be done in 32 bits to avoid overflow */ + + source->rescale[val] = (JSAMPLE) ((val*MAXJSAMPLE + half_maxval)/maxval); + + } + + } + +} + + + + + +/* + + * Finish up at the end of the file. + + */ + + + +METHODDEF void + +finish_input_ppm (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +{ + + /* no work */ + +} + + + + + +/* + + * The module selection routine for PPM format input. + + */ + + + +GLOBAL cjpeg_source_ptr + +jinit_read_ppm (j_compress_ptr cinfo) + +{ + + ppm_source_ptr source; + + + + /* Create module interface object */ + + source = (ppm_source_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(ppm_source_struct)); + + /* Fill in method ptrs, except get_pixel_rows which start_input sets */ + + source->pub.start_input = start_input_ppm; + + source->pub.finish_input = finish_input_ppm; + + + + return (cjpeg_source_ptr) source; + +} + + + +#endif /* PPM_SUPPORTED */ + diff --git a/utils/roq2/jpeg/rdrle.c b/utils/roq2/jpeg/rdrle.c new file mode 100644 index 0000000..2bf0fe4 --- /dev/null +++ b/utils/roq2/jpeg/rdrle.c @@ -0,0 +1,774 @@ +/* + + * rdrle.c + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains routines to read input images in Utah RLE format. + + * The Utah Raster Toolkit library is required (version 3.1 or later). + + * + + * These routines may need modification for non-Unix environments or + + * specialized applications. As they stand, they assume input from + + * an ordinary stdio stream. They further assume that reading begins + + * at the start of the file; start_input may need work if the + + * user interface has already read some data (e.g., to determine that + + * the file is indeed RLE format). + + * + + * Based on code contributed by Mike Lijewski, + + * with updates from Robert Hutchinson. + + */ + + + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + + + +#ifdef RLE_SUPPORTED + + + +/* rle.h is provided by the Utah Raster Toolkit. */ + + + +#include + + + +/* + + * We assume that JSAMPLE has the same representation as rle_pixel, + + * to wit, "unsigned char". Hence we can't cope with 12- or 16-bit samples. + + */ + + + +#if BITS_IN_JSAMPLE != 8 + + Sorry, this code only copes with 8-bit JSAMPLEs. /* deliberate syntax err */ + +#endif + + + +/* + + * We support the following types of RLE files: + + * + + * GRAYSCALE - 8 bits, no colormap + + * MAPPEDGRAY - 8 bits, 1 channel colomap + + * PSEUDOCOLOR - 8 bits, 3 channel colormap + + * TRUECOLOR - 24 bits, 3 channel colormap + + * DIRECTCOLOR - 24 bits, no colormap + + * + + * For now, we ignore any alpha channel in the image. + + */ + + + +typedef enum + + { GRAYSCALE, MAPPEDGRAY, PSEUDOCOLOR, TRUECOLOR, DIRECTCOLOR } rle_kind; + + + + + +/* + + * Since RLE stores scanlines bottom-to-top, we have to invert the image + + * to conform to JPEG's top-to-bottom order. To do this, we read the + + * incoming image into a virtual array on the first get_pixel_rows call, + + * then fetch the required row from the virtual array on subsequent calls. + + */ + + + +typedef struct _rle_source_struct * rle_source_ptr; + + + +typedef struct _rle_source_struct { + + struct cjpeg_source_struct pub; /* public fields */ + + + + rle_kind visual; /* actual type of input file */ + + jvirt_sarray_ptr image; /* virtual array to hold the image */ + + JDIMENSION row; /* current row # in the virtual array */ + + rle_hdr header; /* Input file information */ + + rle_pixel** rle_row; /* holds a row returned by rle_getrow() */ + + + +} rle_source_struct; + + + + + +/* + + * Read the file header; return image size and component count. + + */ + + + +METHODDEF void + +start_input_rle (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +{ + + rle_source_ptr source = (rle_source_ptr) sinfo; + + JDIMENSION width, height; + +#ifdef PROGRESS_REPORT + + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; + +#endif + + + + /* Use RLE library routine to get the header info */ + + source->header = *rle_hdr_init(NULL); + + source->header.rle_file = source->pub.input_file; + + switch (rle_get_setup(&(source->header))) { + + case RLE_SUCCESS: + + /* A-OK */ + + break; + + case RLE_NOT_RLE: + + ERREXIT(cinfo, JERR_RLE_NOT); + + break; + + case RLE_NO_SPACE: + + ERREXIT(cinfo, JERR_RLE_MEM); + + break; + + case RLE_EMPTY: + + ERREXIT(cinfo, JERR_RLE_EMPTY); + + break; + + case RLE_EOF: + + ERREXIT(cinfo, JERR_RLE_EOF); + + break; + + default: + + ERREXIT(cinfo, JERR_RLE_BADERROR); + + break; + + } + + + + /* Figure out what we have, set private vars and return values accordingly */ + + + + width = source->header.xmax - source->header.xmin + 1; + + height = source->header.ymax - source->header.ymin + 1; + + source->header.xmin = 0; /* realign horizontally */ + + source->header.xmax = width-1; + + + + cinfo->image_width = width; + + cinfo->image_height = height; + + cinfo->data_precision = 8; /* we can only handle 8 bit data */ + + + + if (source->header.ncolors == 1 && source->header.ncmap == 0) { + + source->visual = GRAYSCALE; + + TRACEMS2(cinfo, 1, JTRC_RLE_GRAY, width, height); + + } else if (source->header.ncolors == 1 && source->header.ncmap == 1) { + + source->visual = MAPPEDGRAY; + + TRACEMS3(cinfo, 1, JTRC_RLE_MAPGRAY, width, height, + + 1 << source->header.cmaplen); + + } else if (source->header.ncolors == 1 && source->header.ncmap == 3) { + + source->visual = PSEUDOCOLOR; + + TRACEMS3(cinfo, 1, JTRC_RLE_MAPPED, width, height, + + 1 << source->header.cmaplen); + + } else if (source->header.ncolors == 3 && source->header.ncmap == 3) { + + source->visual = TRUECOLOR; + + TRACEMS3(cinfo, 1, JTRC_RLE_FULLMAP, width, height, + + 1 << source->header.cmaplen); + + } else if (source->header.ncolors == 3 && source->header.ncmap == 0) { + + source->visual = DIRECTCOLOR; + + TRACEMS2(cinfo, 1, JTRC_RLE, width, height); + + } else + + ERREXIT(cinfo, JERR_RLE_UNSUPPORTED); + + + + if (source->visual == GRAYSCALE || source->visual == MAPPEDGRAY) { + + cinfo->in_color_space = JCS_GRAYSCALE; + + cinfo->input_components = 1; + + } else { + + cinfo->in_color_space = JCS_RGB; + + cinfo->input_components = 3; + + } + + + + /* + + * A place to hold each scanline while it's converted. + + * (GRAYSCALE scanlines don't need converting) + + */ + + if (source->visual != GRAYSCALE) { + + source->rle_row = (rle_pixel**) (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (JDIMENSION) width, (JDIMENSION) cinfo->input_components); + + } + + + + /* request a virtual array to hold the image */ + + source->image = (*cinfo->mem->request_virt_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + + (JDIMENSION) (width * source->header.ncolors), + + (JDIMENSION) height, (JDIMENSION) 1); + + + +#ifdef PROGRESS_REPORT + + if (progress != NULL) { + + /* count file input as separate pass */ + + progress->total_extra_passes++; + + } + +#endif + + + + source->pub.buffer_height = 1; + +} + + + + + +/* + + * Read one row of pixels. + + * Called only after load_image has read the image into the virtual array. + + * Used for GRAYSCALE, MAPPEDGRAY, TRUECOLOR, and DIRECTCOLOR images. + + */ + + + +METHODDEF JDIMENSION + +get_rle_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +{ + + rle_source_ptr source = (rle_source_ptr) sinfo; + + + + source->row--; + + source->pub.buffer = (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, source->image, source->row, (JDIMENSION) 1, FALSE); + + + + return 1; + +} + + + +/* + + * Read one row of pixels. + + * Called only after load_image has read the image into the virtual array. + + * Used for PSEUDOCOLOR images. + + */ + + + +METHODDEF JDIMENSION + +get_pseudocolor_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +{ + + rle_source_ptr source = (rle_source_ptr) sinfo; + + JSAMPROW src_row, dest_row; + + JDIMENSION col; + + rle_map *colormap; + + int val; + + + + colormap = source->header.cmap; + + dest_row = source->pub.buffer[0]; + + source->row--; + + src_row = * (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, source->image, source->row, (JDIMENSION) 1, FALSE); + + + + for (col = cinfo->image_width; col > 0; col--) { + + val = GETJSAMPLE(*src_row++); + + *dest_row++ = (JSAMPLE) (colormap[val ] >> 8); + + *dest_row++ = (JSAMPLE) (colormap[val + 256] >> 8); + + *dest_row++ = (JSAMPLE) (colormap[val + 512] >> 8); + + } + + + + return 1; + +} + + + + + +/* + + * Load the image into a virtual array. We have to do this because RLE + + * files start at the lower left while the JPEG standard has them starting + + * in the upper left. This is called the first time we want to get a row + + * of input. What we do is load the RLE data into the array and then call + + * the appropriate routine to read one row from the array. Before returning, + + * we set source->pub.get_pixel_rows so that subsequent calls go straight to + + * the appropriate row-reading routine. + + */ + + + +METHODDEF JDIMENSION + +load_image (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +{ + + rle_source_ptr source = (rle_source_ptr) sinfo; + + JDIMENSION row, col; + + JSAMPROW scanline, red_ptr, green_ptr, blue_ptr; + + rle_pixel **rle_row; + + rle_map *colormap; + + char channel; + +#ifdef PROGRESS_REPORT + + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; + +#endif + + + + colormap = source->header.cmap; + + rle_row = source->rle_row; + + + + /* Read the RLE data into our virtual array. + + * We assume here that (a) rle_pixel is represented the same as JSAMPLE, + + * and (b) we are not on a machine where FAR pointers differ from regular. + + */ + + RLE_CLR_BIT(source->header, RLE_ALPHA); /* don't read the alpha channel */ + + + +#ifdef PROGRESS_REPORT + + if (progress != NULL) { + + progress->pub.pass_limit = cinfo->image_height; + + progress->pub.pass_counter = 0; + + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + + } + +#endif + + + + switch (source->visual) { + + + + case GRAYSCALE: + + case PSEUDOCOLOR: + + for (row = 0; row < cinfo->image_height; row++) { + + rle_row = (rle_pixel **) (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, source->image, row, (JDIMENSION) 1, TRUE); + + rle_getrow(&source->header, rle_row); + +#ifdef PROGRESS_REPORT + + if (progress != NULL) { + + progress->pub.pass_counter++; + + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + + } + +#endif + + } + + break; + + + + case MAPPEDGRAY: + + case TRUECOLOR: + + for (row = 0; row < cinfo->image_height; row++) { + + scanline = * (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, source->image, row, (JDIMENSION) 1, TRUE); + + rle_row = source->rle_row; + + rle_getrow(&source->header, rle_row); + + + + for (col = 0; col < cinfo->image_width; col++) { + + for (channel = 0; channel < source->header.ncolors; channel++) { + + *scanline++ = (JSAMPLE) + + (colormap[GETJSAMPLE(rle_row[channel][col]) + 256 * channel] >> 8); + + } + + } + + + +#ifdef PROGRESS_REPORT + + if (progress != NULL) { + + progress->pub.pass_counter++; + + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + + } + +#endif + + } + + break; + + + + case DIRECTCOLOR: + + for (row = 0; row < cinfo->image_height; row++) { + + scanline = * (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, source->image, row, (JDIMENSION) 1, TRUE); + + rle_getrow(&source->header, rle_row); + + + + red_ptr = rle_row[0]; + + green_ptr = rle_row[1]; + + blue_ptr = rle_row[2]; + + + + for (col = cinfo->image_width; col > 0; col--) { + + *scanline++ = *red_ptr++; + + *scanline++ = *green_ptr++; + + *scanline++ = *blue_ptr++; + + } + + + +#ifdef PROGRESS_REPORT + + if (progress != NULL) { + + progress->pub.pass_counter++; + + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + + } + +#endif + + } + + } + + + +#ifdef PROGRESS_REPORT + + if (progress != NULL) + + progress->completed_extra_passes++; + +#endif + + + + /* Set up to call proper row-extraction routine in future */ + + if (source->visual == PSEUDOCOLOR) { + + source->pub.buffer = source->rle_row; + + source->pub.get_pixel_rows = get_pseudocolor_row; + + } else { + + source->pub.get_pixel_rows = get_rle_row; + + } + + source->row = cinfo->image_height; + + + + /* And fetch the topmost (bottommost) row */ + + return (*source->pub.get_pixel_rows) (cinfo, sinfo); + +} + + + + + +/* + + * Finish up at the end of the file. + + */ + + + +METHODDEF void + +finish_input_rle (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +{ + + /* no work */ + +} + + + + + +/* + + * The module selection routine for RLE format input. + + */ + + + +GLOBAL cjpeg_source_ptr + +jinit_read_rle (j_compress_ptr cinfo) + +{ + + rle_source_ptr source; + + + + /* Create module interface object */ + + source = (rle_source_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(rle_source_struct)); + + /* Fill in method ptrs */ + + source->pub.start_input = start_input_rle; + + source->pub.finish_input = finish_input_rle; + + source->pub.get_pixel_rows = load_image; + + + + return (cjpeg_source_ptr) source; + +} + + + +#endif /* RLE_SUPPORTED */ + diff --git a/utils/roq2/jpeg/rdswitch.c b/utils/roq2/jpeg/rdswitch.c new file mode 100644 index 0000000..b30533b --- /dev/null +++ b/utils/roq2/jpeg/rdswitch.c @@ -0,0 +1,684 @@ +/* + + * rdswitch.c + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains routines to process some of cjpeg's more complicated + + * command-line switches. Switches processed here are: + + * -qtables file Read quantization tables from text file + + * -scans file Read scan script from text file + + * -qslots N[,N,...] Set component quantization table selectors + + * -sample HxV[,HxV,...] Set component sampling factors + + */ + + + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#include /* to declare isdigit(), isspace() */ + + + + + +/* Hack: get access to jpeg_zigzag_order[] table in jutils.c. + + * Since it's declared in jpegint.h, normally can't see it from an application. + + */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES + +#define jpeg_zigzag_order jZIGTable + +#endif + +extern const int jpeg_zigzag_order[]; + + + + + +LOCAL int + +text_getc (FILE * file) + +/* Read next char, skipping over any comments (# to end of line) */ + +/* A comment/newline sequence is returned as a newline */ + +{ + + register int ch; + + + + ch = getc(file); + + if (ch == '#') { + + do { + + ch = getc(file); + + } while (ch != '\n' && ch != EOF); + + } + + return ch; + +} + + + + + +LOCAL boolean + +read_text_integer (FILE * file, long * result, int * termchar) + +/* Read an unsigned decimal integer from a file, store it in result */ + +/* Reads one trailing character after the integer; returns it in termchar */ + +{ + + register int ch; + + register long val; + + + + /* Skip any leading whitespace, detect EOF */ + + do { + + ch = text_getc(file); + + if (ch == EOF) { + + *termchar = ch; + + return FALSE; + + } + + } while (isspace(ch)); + + + + if (! isdigit(ch)) { + + *termchar = ch; + + return FALSE; + + } + + + + val = ch - '0'; + + while ((ch = text_getc(file)) != EOF) { + + if (! isdigit(ch)) + + break; + + val *= 10; + + val += ch - '0'; + + } + + *result = val; + + *termchar = ch; + + return TRUE; + +} + + + + + +GLOBAL boolean + +read_quant_tables (j_compress_ptr cinfo, char * filename, + + int scale_factor, boolean force_baseline) + +/* Read a set of quantization tables from the specified file. + + * The file is plain ASCII text: decimal numbers with whitespace between. + + * Comments preceded by '#' may be included in the file. + + * There may be one to NUM_QUANT_TBLS tables in the file, each of 64 values. + + * The tables are implicitly numbered 0,1,etc. + + * NOTE: does not affect the qslots mapping, which will default to selecting + + * table 0 for luminance (or primary) components, 1 for chrominance components. + + * You must use -qslots if you want a different component->table mapping. + + */ + +{ + + FILE * fp; + + int tblno, i, termchar; + + long val; + + unsigned int table[DCTSIZE2]; + + + + if ((fp = fopen(filename, "r")) == NULL) { + + fprintf(stderr, "Can't open table file %s\n", filename); + + return FALSE; + + } + + tblno = 0; + + + + while (read_text_integer(fp, &val, &termchar)) { /* read 1st element of table */ + + if (tblno >= NUM_QUANT_TBLS) { + + fprintf(stderr, "Too many tables in file %s\n", filename); + + fclose(fp); + + return FALSE; + + } + + table[0] = (unsigned int) val; + + for (i = 1; i < DCTSIZE2; i++) { + + if (! read_text_integer(fp, &val, &termchar)) { + + fprintf(stderr, "Invalid table data in file %s\n", filename); + + fclose(fp); + + return FALSE; + + } + + /* Convert from natural order in the file to zigzag table order. */ + + table[jpeg_zigzag_order[i]] = (unsigned int) val; + + } + + jpeg_add_quant_table(cinfo, tblno, table, scale_factor, force_baseline); + + tblno++; + + } + + + + if (termchar != EOF) { + + fprintf(stderr, "Non-numeric data in file %s\n", filename); + + fclose(fp); + + return FALSE; + + } + + + + fclose(fp); + + return TRUE; + +} + + + + + +#ifdef C_MULTISCAN_FILES_SUPPORTED + + + +LOCAL boolean + +read_scan_integer (FILE * file, long * result, int * termchar) + +/* Variant of read_text_integer that always looks for a non-space termchar; + + * this simplifies parsing of punctuation in scan scripts. + + */ + +{ + + register int ch; + + + + if (! read_text_integer(file, result, termchar)) + + return FALSE; + + ch = *termchar; + + while (ch != EOF && isspace(ch)) + + ch = text_getc(file); + + if (isdigit(ch)) { /* oops, put it back */ + + if (ungetc(ch, file) == EOF) + + return FALSE; + + ch = ' '; + + } else { + + /* Any separators other than ';' and ':' are ignored; + + * this allows user to insert commas, etc, if desired. + + */ + + if (ch != EOF && ch != ';' && ch != ':') + + ch = ' '; + + } + + *termchar = ch; + + return TRUE; + +} + + + + + +GLOBAL boolean + +read_scan_script (j_compress_ptr cinfo, char * filename) + +/* Read a scan script from the specified text file. + + * Each entry in the file defines one scan to be emitted. + + * Entries are separated by semicolons ';'. + + * An entry contains one to four component indexes, + + * optionally followed by a colon ':' and four progressive-JPEG parameters. + + * The component indexes denote which component(s) are to be transmitted + + * in the current scan. The first component has index 0. + + * Sequential JPEG is used if the progressive-JPEG parameters are omitted. + + * The file is free format text: any whitespace may appear between numbers + + * and the ':' and ';' punctuation marks. Also, other punctuation (such + + * as commas or dashes) can be placed between numbers if desired. + + * Comments preceded by '#' may be included in the file. + + * Note: we do very little validity checking here; + + * jcmaster.c will validate the script parameters. + + */ + +{ + + FILE * fp; + + int scanno, ncomps, termchar; + + long val; + + jpeg_scan_info * scanptr; + +#define MAX_SCANS 100 /* quite arbitrary limit */ + + jpeg_scan_info scans[MAX_SCANS]; + + + + if ((fp = fopen(filename, "r")) == NULL) { + + fprintf(stderr, "Can't open scan definition file %s\n", filename); + + return FALSE; + + } + + scanptr = scans; + + scanno = 0; + + + + while (read_scan_integer(fp, &val, &termchar)) { + + if (scanno >= MAX_SCANS) { + + fprintf(stderr, "Too many scans defined in file %s\n", filename); + + fclose(fp); + + return FALSE; + + } + + scanptr->component_index[0] = (int) val; + + ncomps = 1; + + while (termchar == ' ') { + + if (ncomps >= MAX_COMPS_IN_SCAN) { + + fprintf(stderr, "Too many components in one scan in file %s\n", + + filename); + + fclose(fp); + + return FALSE; + + } + + if (! read_scan_integer(fp, &val, &termchar)) + + goto bogus; + + scanptr->component_index[ncomps] = (int) val; + + ncomps++; + + } + + scanptr->comps_in_scan = ncomps; + + if (termchar == ':') { + + if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ') + + goto bogus; + + scanptr->Ss = (int) val; + + if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ') + + goto bogus; + + scanptr->Se = (int) val; + + if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ') + + goto bogus; + + scanptr->Ah = (int) val; + + if (! read_scan_integer(fp, &val, &termchar)) + + goto bogus; + + scanptr->Al = (int) val; + + } else { + + /* set non-progressive parameters */ + + scanptr->Ss = 0; + + scanptr->Se = DCTSIZE2-1; + + scanptr->Ah = 0; + + scanptr->Al = 0; + + } + + if (termchar != ';' && termchar != EOF) { + +bogus: + + fprintf(stderr, "Invalid scan entry format in file %s\n", filename); + + fclose(fp); + + return FALSE; + + } + + scanptr++, scanno++; + + } + + + + if (termchar != EOF) { + + fprintf(stderr, "Non-numeric data in file %s\n", filename); + + fclose(fp); + + return FALSE; + + } + + + + if (scanno > 0) { + + /* Stash completed scan list in cinfo structure. + + * NOTE: for cjpeg's use, JPOOL_IMAGE is the right lifetime for this data, + + * but if you want to compress multiple images you'd want JPOOL_PERMANENT. + + */ + + scanptr = (jpeg_scan_info *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + scanno * SIZEOF(jpeg_scan_info)); + + MEMCOPY(scanptr, scans, scanno * SIZEOF(jpeg_scan_info)); + + cinfo->scan_info = scanptr; + + cinfo->num_scans = scanno; + + } + + + + fclose(fp); + + return TRUE; + +} + + + +#endif /* C_MULTISCAN_FILES_SUPPORTED */ + + + + + +GLOBAL boolean + +set_quant_slots (j_compress_ptr cinfo, char *arg) + +/* Process a quantization-table-selectors parameter string, of the form + + * N[,N,...] + + * If there are more components than parameters, the last value is replicated. + + */ + +{ + + int val = 0; /* default table # */ + + int ci; + + char ch; + + + + for (ci = 0; ci < MAX_COMPONENTS; ci++) { + + if (*arg) { + + ch = ','; /* if not set by sscanf, will be ',' */ + + if (sscanf(arg, "%d%c", &val, &ch) < 1) + + return FALSE; + + if (ch != ',') /* syntax check */ + + return FALSE; + + if (val < 0 || val >= NUM_QUANT_TBLS) { + + fprintf(stderr, "JPEG quantization tables are numbered 0..%d\n", + + NUM_QUANT_TBLS-1); + + return FALSE; + + } + + cinfo->comp_info[ci].quant_tbl_no = val; + + while (*arg && *arg++ != ',') /* advance to next segment of arg string */ + + ; + + } else { + + /* reached end of parameter, set remaining components to last table */ + + cinfo->comp_info[ci].quant_tbl_no = val; + + } + + } + + return TRUE; + +} + + + + + +GLOBAL boolean + +set_sample_factors (j_compress_ptr cinfo, char *arg) + +/* Process a sample-factors parameter string, of the form + + * HxV[,HxV,...] + + * If there are more components than parameters, "1x1" is assumed for the rest. + + */ + +{ + + int ci, val1, val2; + + char ch1, ch2; + + + + for (ci = 0; ci < MAX_COMPONENTS; ci++) { + + if (*arg) { + + ch2 = ','; /* if not set by sscanf, will be ',' */ + + if (sscanf(arg, "%d%c%d%c", &val1, &ch1, &val2, &ch2) < 3) + + return FALSE; + + if ((ch1 != 'x' && ch1 != 'X') || ch2 != ',') /* syntax check */ + + return FALSE; + + if (val1 <= 0 || val1 > 4 || val2 <= 0 || val2 > 4) { + + fprintf(stderr, "JPEG sampling factors must be 1..4\n"); + + return FALSE; + + } + + cinfo->comp_info[ci].h_samp_factor = val1; + + cinfo->comp_info[ci].v_samp_factor = val2; + + while (*arg && *arg++ != ',') /* advance to next segment of arg string */ + + ; + + } else { + + /* reached end of parameter, set remaining components to 1x1 sampling */ + + cinfo->comp_info[ci].h_samp_factor = 1; + + cinfo->comp_info[ci].v_samp_factor = 1; + + } + + } + + return TRUE; + +} + diff --git a/utils/roq2/jpeg/rdtarga.c b/utils/roq2/jpeg/rdtarga.c new file mode 100644 index 0000000..ddc75a9 --- /dev/null +++ b/utils/roq2/jpeg/rdtarga.c @@ -0,0 +1,1000 @@ +/* + + * rdtarga.c + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains routines to read input images in Targa format. + + * + + * These routines may need modification for non-Unix environments or + + * specialized applications. As they stand, they assume input from + + * an ordinary stdio stream. They further assume that reading begins + + * at the start of the file; start_input may need work if the + + * user interface has already read some data (e.g., to determine that + + * the file is indeed Targa format). + + * + + * Based on code contributed by Lee Daniel Crocker. + + */ + + + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + + + +#ifdef TARGA_SUPPORTED + + + + + +/* Macros to deal with unsigned chars as efficiently as compiler allows */ + + + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char U_CHAR; + +#define UCH(x) ((int) (x)) + +#else /* !HAVE_UNSIGNED_CHAR */ + +#ifdef CHAR_IS_UNSIGNED + +typedef char U_CHAR; + +#define UCH(x) ((int) (x)) + +#else + +typedef char U_CHAR; + +#define UCH(x) ((int) (x) & 0xFF) + +#endif + +#endif /* HAVE_UNSIGNED_CHAR */ + + + + + +#define ReadOK(file,buffer,len) (JFREAD(file,buffer,len) == ((size_t) (len))) + + + + + +/* Private version of data source object */ + + + +typedef struct _tga_source_struct * tga_source_ptr; + + + +typedef struct _tga_source_struct { + + struct cjpeg_source_struct pub; /* public fields */ + + + + j_compress_ptr cinfo; /* back link saves passing separate parm */ + + + + JSAMPARRAY colormap; /* Targa colormap (converted to my format) */ + + + + jvirt_sarray_ptr whole_image; /* Needed if funny input row order */ + + JDIMENSION current_row; /* Current logical row number to read */ + + + + /* Pointer to routine to extract next Targa pixel from input file */ + + JMETHOD(void, read_pixel, (tga_source_ptr sinfo)); + + + + /* Result of read_pixel is delivered here: */ + + U_CHAR tga_pixel[4]; + + + + int pixel_size; /* Bytes per Targa pixel (1 to 4) */ + + + + /* State info for reading RLE-coded pixels; both counts must be init to 0 */ + + int block_count; /* # of pixels remaining in RLE block */ + + int dup_pixel_count; /* # of times to duplicate previous pixel */ + + + + /* This saves the correct pixel-row-expansion method for preload_image */ + + JMETHOD(JDIMENSION, get_pixel_rows, (j_compress_ptr cinfo, + + cjpeg_source_ptr sinfo)); + +} tga_source_struct; + + + + + +/* For expanding 5-bit pixel values to 8-bit with best rounding */ + + + +static const UINT8 c5to8bits[32] = { + + 0, 8, 16, 25, 33, 41, 49, 58, + + 66, 74, 82, 90, 99, 107, 115, 123, + + 132, 140, 148, 156, 165, 173, 181, 189, + + 197, 206, 214, 222, 230, 239, 247, 255 + +}; + + + + + + + +LOCAL int + +read_byte (tga_source_ptr sinfo) + +/* Read next byte from Targa file */ + +{ + + register FILE *infile = sinfo->pub.input_file; + + register int c; + + + + if ((c = getc(infile)) == EOF) + + ERREXIT(sinfo->cinfo, JERR_INPUT_EOF); + + return c; + +} + + + + + +LOCAL void + +read_colormap (tga_source_ptr sinfo, int cmaplen, int mapentrysize) + +/* Read the colormap from a Targa file */ + +{ + + int i; + + + + /* Presently only handles 24-bit BGR format */ + + if (mapentrysize != 24) + + ERREXIT(sinfo->cinfo, JERR_TGA_BADCMAP); + + + + for (i = 0; i < cmaplen; i++) { + + sinfo->colormap[2][i] = (JSAMPLE) read_byte(sinfo); + + sinfo->colormap[1][i] = (JSAMPLE) read_byte(sinfo); + + sinfo->colormap[0][i] = (JSAMPLE) read_byte(sinfo); + + } + +} + + + + + +/* + + * read_pixel methods: get a single pixel from Targa file into tga_pixel[] + + */ + + + +LOCAL void + +read_non_rle_pixel (tga_source_ptr sinfo) + +/* Read one Targa pixel from the input file; no RLE expansion */ + +{ + + register FILE *infile = sinfo->pub.input_file; + + register int i; + + + + for (i = 0; i < sinfo->pixel_size; i++) { + + sinfo->tga_pixel[i] = (U_CHAR) getc(infile); + + } + +} + + + + + +LOCAL void + +read_rle_pixel (tga_source_ptr sinfo) + +/* Read one Targa pixel from the input file, expanding RLE data as needed */ + +{ + + register FILE *infile = sinfo->pub.input_file; + + register int i; + + + + /* Duplicate previously read pixel? */ + + if (sinfo->dup_pixel_count > 0) { + + sinfo->dup_pixel_count--; + + return; + + } + + + + /* Time to read RLE block header? */ + + if (--sinfo->block_count < 0) { /* decrement pixels remaining in block */ + + i = read_byte(sinfo); + + if (i & 0x80) { /* Start of duplicate-pixel block? */ + + sinfo->dup_pixel_count = i & 0x7F; /* number of dups after this one */ + + sinfo->block_count = 0; /* then read new block header */ + + } else { + + sinfo->block_count = i & 0x7F; /* number of pixels after this one */ + + } + + } + + + + /* Read next pixel */ + + for (i = 0; i < sinfo->pixel_size; i++) { + + sinfo->tga_pixel[i] = (U_CHAR) getc(infile); + + } + +} + + + + + +/* + + * Read one row of pixels. + + * + + * We provide several different versions depending on input file format. + + */ + + + + + +METHODDEF JDIMENSION + +get_8bit_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +/* This version is for reading 8-bit grayscale pixels */ + +{ + + tga_source_ptr source = (tga_source_ptr) sinfo; + + register JSAMPROW ptr; + + register JDIMENSION col; + + + + ptr = source->pub.buffer[0]; + + for (col = cinfo->image_width; col > 0; col--) { + + (*source->read_pixel) (source); /* Load next pixel into tga_pixel */ + + *ptr++ = (JSAMPLE) UCH(source->tga_pixel[0]); + + } + + return 1; + +} + + + +METHODDEF JDIMENSION + +get_8bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +/* This version is for reading 8-bit colormap indexes */ + +{ + + tga_source_ptr source = (tga_source_ptr) sinfo; + + register int t; + + register JSAMPROW ptr; + + register JDIMENSION col; + + register JSAMPARRAY colormap = source->colormap; + + + + ptr = source->pub.buffer[0]; + + for (col = cinfo->image_width; col > 0; col--) { + + (*source->read_pixel) (source); /* Load next pixel into tga_pixel */ + + t = UCH(source->tga_pixel[0]); + + *ptr++ = colormap[0][t]; + + *ptr++ = colormap[1][t]; + + *ptr++ = colormap[2][t]; + + } + + return 1; + +} + + + +METHODDEF JDIMENSION + +get_16bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +/* This version is for reading 16-bit pixels */ + +{ + + tga_source_ptr source = (tga_source_ptr) sinfo; + + register int t; + + register JSAMPROW ptr; + + register JDIMENSION col; + + + + ptr = source->pub.buffer[0]; + + for (col = cinfo->image_width; col > 0; col--) { + + (*source->read_pixel) (source); /* Load next pixel into tga_pixel */ + + t = UCH(source->tga_pixel[0]); + + t += UCH(source->tga_pixel[1]) << 8; + + /* We expand 5 bit data to 8 bit sample width. + + * The format of the 16-bit (LSB first) input word is + + * xRRRRRGGGGGBBBBB + + */ + + ptr[2] = (JSAMPLE) c5to8bits[t & 0x1F]; + + t >>= 5; + + ptr[1] = (JSAMPLE) c5to8bits[t & 0x1F]; + + t >>= 5; + + ptr[0] = (JSAMPLE) c5to8bits[t & 0x1F]; + + ptr += 3; + + } + + return 1; + +} + + + +METHODDEF JDIMENSION + +get_24bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +/* This version is for reading 24-bit pixels */ + +{ + + tga_source_ptr source = (tga_source_ptr) sinfo; + + register JSAMPROW ptr; + + register JDIMENSION col; + + + + ptr = source->pub.buffer[0]; + + for (col = cinfo->image_width; col > 0; col--) { + + (*source->read_pixel) (source); /* Load next pixel into tga_pixel */ + + *ptr++ = (JSAMPLE) UCH(source->tga_pixel[2]); /* change BGR to RGB order */ + + *ptr++ = (JSAMPLE) UCH(source->tga_pixel[1]); + + *ptr++ = (JSAMPLE) UCH(source->tga_pixel[0]); + + } + + return 1; + +} + + + +/* + + * Targa also defines a 32-bit pixel format with order B,G,R,A. + + * We presently ignore the attribute byte, so the code for reading + + * these pixels is identical to the 24-bit routine above. + + * This works because the actual pixel length is only known to read_pixel. + + */ + + + +#define get_32bit_row get_24bit_row + + + + + +/* + + * This method is for re-reading the input data in standard top-down + + * row order. The entire image has already been read into whole_image + + * with proper conversion of pixel format, but it's in a funny row order. + + */ + + + +METHODDEF JDIMENSION + +get_memory_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +{ + + tga_source_ptr source = (tga_source_ptr) sinfo; + + JDIMENSION source_row; + + + + /* Compute row of source that maps to current_row of normal order */ + + /* For now, assume image is bottom-up and not interlaced. */ + + /* NEEDS WORK to support interlaced images! */ + + source_row = cinfo->image_height - source->current_row - 1; + + + + /* Fetch that row from virtual array */ + + source->pub.buffer = (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, source->whole_image, + + source_row, (JDIMENSION) 1, FALSE); + + + + source->current_row++; + + return 1; + +} + + + + + +/* + + * This method loads the image into whole_image during the first call on + + * get_pixel_rows. The get_pixel_rows pointer is then adjusted to call + + * get_memory_row on subsequent calls. + + */ + + + +METHODDEF JDIMENSION + +preload_image (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +{ + + tga_source_ptr source = (tga_source_ptr) sinfo; + + JDIMENSION row; + + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; + + + + /* Read the data into a virtual array in input-file row order. */ + + for (row = 0; row < cinfo->image_height; row++) { + + if (progress != NULL) { + + progress->pub.pass_counter = (long) row; + + progress->pub.pass_limit = (long) cinfo->image_height; + + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + + } + + source->pub.buffer = (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, source->whole_image, row, (JDIMENSION) 1, TRUE); + + (*source->get_pixel_rows) (cinfo, sinfo); + + } + + if (progress != NULL) + + progress->completed_extra_passes++; + + + + /* Set up to read from the virtual array in unscrambled order */ + + source->pub.get_pixel_rows = get_memory_row; + + source->current_row = 0; + + /* And read the first row */ + + return get_memory_row(cinfo, sinfo); + +} + + + + + +/* + + * Read the file header; return image size and component count. + + */ + + + +METHODDEF void + +start_input_tga (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +{ + + tga_source_ptr source = (tga_source_ptr) sinfo; + + U_CHAR targaheader[18]; + + int idlen, cmaptype, subtype, flags, interlace_type, components; + + unsigned int width, height, maplen; + + boolean is_bottom_up; + + + +#define GET_2B(offset) ((unsigned int) UCH(targaheader[offset]) + \ + + (((unsigned int) UCH(targaheader[offset+1])) << 8)) + + + + if (! ReadOK(source->pub.input_file, targaheader, 18)) + + ERREXIT(cinfo, JERR_INPUT_EOF); + + + + /* Pretend "15-bit" pixels are 16-bit --- we ignore attribute bit anyway */ + + if (targaheader[16] == 15) + + targaheader[16] = 16; + + + + idlen = UCH(targaheader[0]); + + cmaptype = UCH(targaheader[1]); + + subtype = UCH(targaheader[2]); + + maplen = GET_2B(5); + + width = GET_2B(12); + + height = GET_2B(14); + + source->pixel_size = UCH(targaheader[16]) >> 3; + + flags = UCH(targaheader[17]); /* Image Descriptor byte */ + + + + is_bottom_up = ((flags & 0x20) == 0); /* bit 5 set => top-down */ + + interlace_type = flags >> 6; /* bits 6/7 are interlace code */ + + + + if (cmaptype > 1 || /* cmaptype must be 0 or 1 */ + + source->pixel_size < 1 || source->pixel_size > 4 || + + (UCH(targaheader[16]) & 7) != 0 || /* bits/pixel must be multiple of 8 */ + + interlace_type != 0) /* currently don't allow interlaced image */ + + ERREXIT(cinfo, JERR_TGA_BADPARMS); + + + + if (subtype > 8) { + + /* It's an RLE-coded file */ + + source->read_pixel = read_rle_pixel; + + source->block_count = source->dup_pixel_count = 0; + + subtype -= 8; + + } else { + + /* Non-RLE file */ + + source->read_pixel = read_non_rle_pixel; + + } + + + + /* Now should have subtype 1, 2, or 3 */ + + components = 3; /* until proven different */ + + cinfo->in_color_space = JCS_RGB; + + + + switch (subtype) { + + case 1: /* Colormapped image */ + + if (source->pixel_size == 1 && cmaptype == 1) + + source->get_pixel_rows = get_8bit_row; + + else + + ERREXIT(cinfo, JERR_TGA_BADPARMS); + + TRACEMS2(cinfo, 1, JTRC_TGA_MAPPED, width, height); + + break; + + case 2: /* RGB image */ + + switch (source->pixel_size) { + + case 2: + + source->get_pixel_rows = get_16bit_row; + + break; + + case 3: + + source->get_pixel_rows = get_24bit_row; + + break; + + case 4: + + source->get_pixel_rows = get_32bit_row; + + break; + + default: + + ERREXIT(cinfo, JERR_TGA_BADPARMS); + + break; + + } + + TRACEMS2(cinfo, 1, JTRC_TGA, width, height); + + break; + + case 3: /* Grayscale image */ + + components = 1; + + cinfo->in_color_space = JCS_GRAYSCALE; + + if (source->pixel_size == 1) + + source->get_pixel_rows = get_8bit_gray_row; + + else + + ERREXIT(cinfo, JERR_TGA_BADPARMS); + + TRACEMS2(cinfo, 1, JTRC_TGA_GRAY, width, height); + + break; + + default: + + ERREXIT(cinfo, JERR_TGA_BADPARMS); + + break; + + } + + + + if (is_bottom_up) { + + /* Create a virtual array to buffer the upside-down image. */ + + source->whole_image = (*cinfo->mem->request_virt_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + + (JDIMENSION) width * components, (JDIMENSION) height, (JDIMENSION) 1); + + if (cinfo->progress != NULL) { + + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; + + progress->total_extra_passes++; /* count file input as separate pass */ + + } + + /* source->pub.buffer will point to the virtual array. */ + + source->pub.buffer_height = 1; /* in case anyone looks at it */ + + source->pub.get_pixel_rows = preload_image; + + } else { + + /* Don't need a virtual array, but do need a one-row input buffer. */ + + source->whole_image = NULL; + + source->pub.buffer = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (JDIMENSION) width * components, (JDIMENSION) 1); + + source->pub.buffer_height = 1; + + source->pub.get_pixel_rows = source->get_pixel_rows; + + } + + + + while (idlen--) /* Throw away ID field */ + + (void) read_byte(source); + + + + if (maplen > 0) { + + if (maplen > 256 || GET_2B(3) != 0) + + ERREXIT(cinfo, JERR_TGA_BADCMAP); + + /* Allocate space to store the colormap */ + + source->colormap = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, (JDIMENSION) maplen, (JDIMENSION) 3); + + /* and read it from the file */ + + read_colormap(source, (int) maplen, UCH(targaheader[7])); + + } else { + + if (cmaptype) /* but you promised a cmap! */ + + ERREXIT(cinfo, JERR_TGA_BADPARMS); + + source->colormap = NULL; + + } + + + + cinfo->input_components = components; + + cinfo->data_precision = 8; + + cinfo->image_width = width; + + cinfo->image_height = height; + +} + + + + + +/* + + * Finish up at the end of the file. + + */ + + + +METHODDEF void + +finish_input_tga (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) + +{ + + /* no work */ + +} + + + + + +/* + + * The module selection routine for Targa format input. + + */ + + + +GLOBAL cjpeg_source_ptr + +jinit_read_targa (j_compress_ptr cinfo) + +{ + + tga_source_ptr source; + + + + /* Create module interface object */ + + source = (tga_source_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(tga_source_struct)); + + source->cinfo = cinfo; /* make back link for subroutines */ + + /* Fill in method ptrs, except get_pixel_rows which start_input sets */ + + source->pub.start_input = start_input_tga; + + source->pub.finish_input = finish_input_tga; + + + + return (cjpeg_source_ptr) source; + +} + + + +#endif /* TARGA_SUPPORTED */ + diff --git a/utils/roq2/jpeg/wrbmp.c b/utils/roq2/jpeg/wrbmp.c new file mode 100644 index 0000000..c4c4dbe --- /dev/null +++ b/utils/roq2/jpeg/wrbmp.c @@ -0,0 +1,884 @@ +/* + + * wrbmp.c + + * + + * Copyright (C) 1994-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains routines to write output images in Microsoft "BMP" + + * format (MS Windows 3.x and OS/2 1.x flavors). + + * Either 8-bit colormapped or 24-bit full-color format can be written. + + * No compression is supported. + + * + + * These routines may need modification for non-Unix environments or + + * specialized applications. As they stand, they assume output to + + * an ordinary stdio stream. + + * + + * This code contributed by James Arthur Boucher. + + */ + + + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + + + +#ifdef BMP_SUPPORTED + + + + + +/* + + * To support 12-bit JPEG data, we'd have to scale output down to 8 bits. + + * This is not yet implemented. + + */ + + + +#if BITS_IN_JSAMPLE != 8 + + Sorry, this code only copes with 8-bit JSAMPLEs. /* deliberate syntax err */ + +#endif + + + +/* + + * Since BMP stores scanlines bottom-to-top, we have to invert the image + + * from JPEG's top-to-bottom order. To do this, we save the outgoing data + + * in a virtual array during put_pixel_row calls, then actually emit the + + * BMP file during finish_output. The virtual array contains one JSAMPLE per + + * pixel if the output is grayscale or colormapped, three if it is full color. + + */ + + + +/* Private version of data destination object */ + + + +typedef struct { + + struct djpeg_dest_struct pub; /* public fields */ + + + + boolean is_os2; /* saves the OS2 format request flag */ + + + + jvirt_sarray_ptr whole_image; /* needed to reverse row order */ + + JDIMENSION data_width; /* JSAMPLEs per row */ + + JDIMENSION row_width; /* physical width of one row in the BMP file */ + + int pad_bytes; /* number of padding bytes needed per row */ + + JDIMENSION cur_output_row; /* next row# to write to virtual array */ + +} bmp_dest_struct; + + + +typedef bmp_dest_struct * bmp_dest_ptr; + + + + + +/* Forward declarations */ + +LOCAL void write_colormap + + JPP((j_decompress_ptr cinfo, bmp_dest_ptr dest, + + int map_colors, int map_entry_size)); + + + + + +/* + + * Write some pixel data. + + * In this module rows_supplied will always be 1. + + */ + + + +METHODDEF void + +put_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + + JDIMENSION rows_supplied) + +/* This version is for writing 24-bit pixels */ + +{ + + bmp_dest_ptr dest = (bmp_dest_ptr) dinfo; + + JSAMPARRAY image_ptr; + + register JSAMPROW inptr, outptr; + + register JDIMENSION col; + + int pad; + + + + /* Access next row in virtual array */ + + image_ptr = (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, dest->whole_image, + + dest->cur_output_row, (JDIMENSION) 1, TRUE); + + dest->cur_output_row++; + + + + /* Transfer data. Note destination values must be in BGR order + + * (even though Microsoft's own documents say the opposite). + + */ + + inptr = dest->pub.buffer[0]; + + outptr = image_ptr[0]; + + for (col = cinfo->output_width; col > 0; col--) { + + outptr[2] = *inptr++; /* can omit GETJSAMPLE() safely */ + + outptr[1] = *inptr++; + + outptr[0] = *inptr++; + + outptr += 3; + + } + + + + /* Zero out the pad bytes. */ + + pad = dest->pad_bytes; + + while (--pad >= 0) + + *outptr++ = 0; + +} + + + +METHODDEF void + +put_gray_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + + JDIMENSION rows_supplied) + +/* This version is for grayscale OR quantized color output */ + +{ + + bmp_dest_ptr dest = (bmp_dest_ptr) dinfo; + + JSAMPARRAY image_ptr; + + register JSAMPROW inptr, outptr; + + register JDIMENSION col; + + int pad; + + + + /* Access next row in virtual array */ + + image_ptr = (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, dest->whole_image, + + dest->cur_output_row, (JDIMENSION) 1, TRUE); + + dest->cur_output_row++; + + + + /* Transfer data. */ + + inptr = dest->pub.buffer[0]; + + outptr = image_ptr[0]; + + for (col = cinfo->output_width; col > 0; col--) { + + *outptr++ = *inptr++; /* can omit GETJSAMPLE() safely */ + + } + + + + /* Zero out the pad bytes. */ + + pad = dest->pad_bytes; + + while (--pad >= 0) + + *outptr++ = 0; + +} + + + + + +/* + + * Startup: normally writes the file header. + + * In this module we may as well postpone everything until finish_output. + + */ + + + +METHODDEF void + +start_output_bmp (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) + +{ + + /* no work here */ + +} + + + + + +/* + + * Finish up at the end of the file. + + * + + * Here is where we really output the BMP file. + + * + + * First, routines to write the Windows and OS/2 variants of the file header. + + */ + + + +LOCAL void + +write_bmp_header (j_decompress_ptr cinfo, bmp_dest_ptr dest) + +/* Write a Windows-style BMP file header, including colormap if needed */ + +{ + + char bmpfileheader[14]; + + char bmpinfoheader[40]; + +#define PUT_2B(array,offset,value) \ + + (array[offset] = (char) ((value) & 0xFF), \ + + array[offset+1] = (char) (((value) >> 8) & 0xFF)) + +#define PUT_4B(array,offset,value) \ + + (array[offset] = (char) ((value) & 0xFF), \ + + array[offset+1] = (char) (((value) >> 8) & 0xFF), \ + + array[offset+2] = (char) (((value) >> 16) & 0xFF), \ + + array[offset+3] = (char) (((value) >> 24) & 0xFF)) + + INT32 headersize, bfSize; + + int bits_per_pixel, cmap_entries; + + + + /* Compute colormap size and total file size */ + + if (cinfo->out_color_space == JCS_RGB) { + + if (cinfo->quantize_colors) { + + /* Colormapped RGB */ + + bits_per_pixel = 8; + + cmap_entries = 256; + + } else { + + /* Unquantized, full color RGB */ + + bits_per_pixel = 24; + + cmap_entries = 0; + + } + + } else { + + /* Grayscale output. We need to fake a 256-entry colormap. */ + + bits_per_pixel = 8; + + cmap_entries = 256; + + } + + /* File size */ + + headersize = 14 + 40 + cmap_entries * 4; /* Header and colormap */ + + bfSize = headersize + (INT32) dest->row_width * (INT32) cinfo->output_height; + + + + /* Set unused fields of header to 0 */ + + MEMZERO(bmpfileheader, SIZEOF(bmpfileheader)); + + MEMZERO(bmpinfoheader, SIZEOF(bmpinfoheader)); + + + + /* Fill the file header */ + + bmpfileheader[0] = 0x42; /* first 2 bytes are ASCII 'B', 'M' */ + + bmpfileheader[1] = 0x4D; + + PUT_4B(bmpfileheader, 2, bfSize); /* bfSize */ + + /* we leave bfReserved1 & bfReserved2 = 0 */ + + PUT_4B(bmpfileheader, 10, headersize); /* bfOffBits */ + + + + /* Fill the info header (Microsoft calls this a BITMAPINFOHEADER) */ + + PUT_2B(bmpinfoheader, 0, 40); /* biSize */ + + PUT_4B(bmpinfoheader, 4, cinfo->output_width); /* biWidth */ + + PUT_4B(bmpinfoheader, 8, cinfo->output_height); /* biHeight */ + + PUT_2B(bmpinfoheader, 12, 1); /* biPlanes - must be 1 */ + + PUT_2B(bmpinfoheader, 14, bits_per_pixel); /* biBitCount */ + + /* we leave biCompression = 0, for none */ + + /* we leave biSizeImage = 0; this is correct for uncompressed data */ + + if (cinfo->density_unit == 2) { /* if have density in dots/cm, then */ + + PUT_4B(bmpinfoheader, 24, (INT32) (cinfo->X_density*100)); /* XPels/M */ + + PUT_4B(bmpinfoheader, 28, (INT32) (cinfo->Y_density*100)); /* XPels/M */ + + } + + PUT_2B(bmpinfoheader, 32, cmap_entries); /* biClrUsed */ + + /* we leave biClrImportant = 0 */ + + + + if (JFWRITE(dest->pub.output_file, bmpfileheader, 14) != (size_t) 14) + + ERREXIT(cinfo, JERR_FILE_WRITE); + + if (JFWRITE(dest->pub.output_file, bmpinfoheader, 40) != (size_t) 40) + + ERREXIT(cinfo, JERR_FILE_WRITE); + + + + if (cmap_entries > 0) + + write_colormap(cinfo, dest, cmap_entries, 4); + +} + + + + + +LOCAL void + +write_os2_header (j_decompress_ptr cinfo, bmp_dest_ptr dest) + +/* Write an OS2-style BMP file header, including colormap if needed */ + +{ + + char bmpfileheader[14]; + + char bmpcoreheader[12]; + + INT32 headersize, bfSize; + + int bits_per_pixel, cmap_entries; + + + + /* Compute colormap size and total file size */ + + if (cinfo->out_color_space == JCS_RGB) { + + if (cinfo->quantize_colors) { + + /* Colormapped RGB */ + + bits_per_pixel = 8; + + cmap_entries = 256; + + } else { + + /* Unquantized, full color RGB */ + + bits_per_pixel = 24; + + cmap_entries = 0; + + } + + } else { + + /* Grayscale output. We need to fake a 256-entry colormap. */ + + bits_per_pixel = 8; + + cmap_entries = 256; + + } + + /* File size */ + + headersize = 14 + 12 + cmap_entries * 3; /* Header and colormap */ + + bfSize = headersize + (INT32) dest->row_width * (INT32) cinfo->output_height; + + + + /* Set unused fields of header to 0 */ + + MEMZERO(bmpfileheader, SIZEOF(bmpfileheader)); + + MEMZERO(bmpcoreheader, SIZEOF(bmpcoreheader)); + + + + /* Fill the file header */ + + bmpfileheader[0] = 0x42; /* first 2 bytes are ASCII 'B', 'M' */ + + bmpfileheader[1] = 0x4D; + + PUT_4B(bmpfileheader, 2, bfSize); /* bfSize */ + + /* we leave bfReserved1 & bfReserved2 = 0 */ + + PUT_4B(bmpfileheader, 10, headersize); /* bfOffBits */ + + + + /* Fill the info header (Microsoft calls this a BITMAPCOREHEADER) */ + + PUT_2B(bmpcoreheader, 0, 12); /* bcSize */ + + PUT_2B(bmpcoreheader, 4, cinfo->output_width); /* bcWidth */ + + PUT_2B(bmpcoreheader, 6, cinfo->output_height); /* bcHeight */ + + PUT_2B(bmpcoreheader, 8, 1); /* bcPlanes - must be 1 */ + + PUT_2B(bmpcoreheader, 10, bits_per_pixel); /* bcBitCount */ + + + + if (JFWRITE(dest->pub.output_file, bmpfileheader, 14) != (size_t) 14) + + ERREXIT(cinfo, JERR_FILE_WRITE); + + if (JFWRITE(dest->pub.output_file, bmpcoreheader, 12) != (size_t) 12) + + ERREXIT(cinfo, JERR_FILE_WRITE); + + + + if (cmap_entries > 0) + + write_colormap(cinfo, dest, cmap_entries, 3); + +} + + + + + +/* + + * Write the colormap. + + * Windows uses BGR0 map entries; OS/2 uses BGR entries. + + */ + + + +LOCAL void + +write_colormap (j_decompress_ptr cinfo, bmp_dest_ptr dest, + + int map_colors, int map_entry_size) + +{ + + JSAMPARRAY colormap = cinfo->colormap; + + int num_colors = cinfo->actual_number_of_colors; + + FILE * outfile = dest->pub.output_file; + + int i; + + + + if (colormap != NULL) { + + if (cinfo->out_color_components == 3) { + + /* Normal case with RGB colormap */ + + for (i = 0; i < num_colors; i++) { + + putc(GETJSAMPLE(colormap[2][i]), outfile); + + putc(GETJSAMPLE(colormap[1][i]), outfile); + + putc(GETJSAMPLE(colormap[0][i]), outfile); + + if (map_entry_size == 4) + + putc(0, outfile); + + } + + } else { + + /* Grayscale colormap (only happens with grayscale quantization) */ + + for (i = 0; i < num_colors; i++) { + + putc(GETJSAMPLE(colormap[0][i]), outfile); + + putc(GETJSAMPLE(colormap[0][i]), outfile); + + putc(GETJSAMPLE(colormap[0][i]), outfile); + + if (map_entry_size == 4) + + putc(0, outfile); + + } + + } + + } else { + + /* If no colormap, must be grayscale data. Generate a linear "map". */ + + for (i = 0; i < 256; i++) { + + putc(i, outfile); + + putc(i, outfile); + + putc(i, outfile); + + if (map_entry_size == 4) + + putc(0, outfile); + + } + + } + + /* Pad colormap with zeros to ensure specified number of colormap entries */ + + if (i > map_colors) + + ERREXIT1(cinfo, JERR_TOO_MANY_COLORS, i); + + for (; i < map_colors; i++) { + + putc(0, outfile); + + putc(0, outfile); + + putc(0, outfile); + + if (map_entry_size == 4) + + putc(0, outfile); + + } + +} + + + + + +METHODDEF void + +finish_output_bmp (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) + +{ + + bmp_dest_ptr dest = (bmp_dest_ptr) dinfo; + + register FILE * outfile = dest->pub.output_file; + + JSAMPARRAY image_ptr; + + register JSAMPROW data_ptr; + + JDIMENSION row; + + register JDIMENSION col; + + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; + + + + /* Write the header and colormap */ + + if (dest->is_os2) + + write_os2_header(cinfo, dest); + + else + + write_bmp_header(cinfo, dest); + + + + /* Write the file body from our virtual array */ + + for (row = cinfo->output_height; row > 0; row--) { + + if (progress != NULL) { + + progress->pub.pass_counter = (long) (cinfo->output_height - row); + + progress->pub.pass_limit = (long) cinfo->output_height; + + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + + } + + image_ptr = (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, dest->whole_image, row-1, (JDIMENSION) 1, FALSE); + + data_ptr = image_ptr[0]; + + for (col = dest->row_width; col > 0; col--) { + + putc(GETJSAMPLE(*data_ptr), outfile); + + data_ptr++; + + } + + } + + if (progress != NULL) + + progress->completed_extra_passes++; + + + + /* Make sure we wrote the output file OK */ + + fflush(outfile); + + if (ferror(outfile)) + + ERREXIT(cinfo, JERR_FILE_WRITE); + +} + + + + + +/* + + * The module selection routine for BMP format output. + + */ + + + +GLOBAL djpeg_dest_ptr + +jinit_write_bmp (j_decompress_ptr cinfo, boolean is_os2) + +{ + + bmp_dest_ptr dest; + + JDIMENSION row_width; + + + + /* Create module interface object, fill in method pointers */ + + dest = (bmp_dest_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(bmp_dest_struct)); + + dest->pub.start_output = start_output_bmp; + + dest->pub.finish_output = finish_output_bmp; + + dest->is_os2 = is_os2; + + + + if (cinfo->out_color_space == JCS_GRAYSCALE) { + + dest->pub.put_pixel_rows = put_gray_rows; + + } else if (cinfo->out_color_space == JCS_RGB) { + + if (cinfo->quantize_colors) + + dest->pub.put_pixel_rows = put_gray_rows; + + else + + dest->pub.put_pixel_rows = put_pixel_rows; + + } else { + + ERREXIT(cinfo, JERR_BMP_COLORSPACE); + + } + + + + /* Calculate output image dimensions so we can allocate space */ + + jpeg_calc_output_dimensions(cinfo); + + + + /* Determine width of rows in the BMP file (padded to 4-byte boundary). */ + + row_width = cinfo->output_width * cinfo->output_components; + + dest->data_width = row_width; + + while ((row_width & 3) != 0) row_width++; + + dest->row_width = row_width; + + dest->pad_bytes = (int) (row_width - dest->data_width); + + + + /* Allocate space for inversion array, prepare for write pass */ + + dest->whole_image = (*cinfo->mem->request_virt_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + + row_width, cinfo->output_height, (JDIMENSION) 1); + + dest->cur_output_row = 0; + + if (cinfo->progress != NULL) { + + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; + + progress->total_extra_passes++; /* count file input as separate pass */ + + } + + + + /* Create decompressor output buffer. */ + + dest->pub.buffer = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, row_width, (JDIMENSION) 1); + + dest->pub.buffer_height = 1; + + + + return (djpeg_dest_ptr) dest; + +} + + + +#endif /* BMP_SUPPORTED */ + diff --git a/utils/roq2/jpeg/wrgif.c b/utils/roq2/jpeg/wrgif.c new file mode 100644 index 0000000..d1a10be --- /dev/null +++ b/utils/roq2/jpeg/wrgif.c @@ -0,0 +1,1010 @@ +/* + + * wrgif.c + + * + + * Copyright (C) 1991-1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + ************************************************************************** + + * WARNING: You will need an LZW patent license from Unisys in order to * + + * use this file legally in any commercial or shareware application. * + + ************************************************************************** + + * + + * This file contains routines to write output images in GIF format. + + * + + * These routines may need modification for non-Unix environments or + + * specialized applications. As they stand, they assume output to + + * an ordinary stdio stream. + + */ + + + +/* + + * This code is loosely based on ppmtogif from the PBMPLUS distribution + + * of Feb. 1991. That file contains the following copyright notice: + + * Based on GIFENCODE by David Rowley . + + * Lempel-Ziv compression based on "compress" by Spencer W. Thomas et al. + + * Copyright (C) 1989 by Jef Poskanzer. + + * Permission to use, copy, modify, and distribute this software and its + + * documentation for any purpose and without fee is hereby granted, provided + + * that the above copyright notice appear in all copies and that both that + + * copyright notice and this permission notice appear in supporting + + * documentation. This software is provided "as is" without express or + + * implied warranty. + + * + + * We are also required to state that + + * "The Graphics Interchange Format(c) is the Copyright property of + + * CompuServe Incorporated. GIF(sm) is a Service Mark property of + + * CompuServe Incorporated." + + */ + + + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + + + +#ifdef GIF_SUPPORTED + + + + + +#define MAX_LZW_BITS 12 /* maximum LZW code size (4096 symbols) */ + + + +typedef INT16 code_int; /* must hold -1 .. 2**MAX_LZW_BITS */ + + + +#define LZW_TABLE_SIZE ((code_int) 1 << MAX_LZW_BITS) + + + +#define HSIZE 5003 /* hash table size for 80% occupancy */ + + + +typedef int hash_int; /* must hold -2*HSIZE..2*HSIZE */ + + + +#define MAXCODE(n_bits) (((code_int) 1 << (n_bits)) - 1) + + + + + +/* + + * The LZW hash table consists of two parallel arrays: + + * hash_code[i] code of symbol in slot i, or 0 if empty slot + + * hash_value[i] symbol's value; undefined if empty slot + + * where slot values (i) range from 0 to HSIZE-1. The symbol value is + + * its prefix symbol's code concatenated with its suffix character. + + * + + * Algorithm: use open addressing double hashing (no chaining) on the + + * prefix code / suffix character combination. We do a variant of Knuth's + + * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime + + * secondary probe. + + * + + * The hash_value[] table is allocated from FAR heap space since it would + + * use up rather a lot of the near data space in a PC. + + */ + + + +typedef INT32 hash_entry; /* must hold (code_int<<8) | byte */ + + + +#define HASH_ENTRY(prefix,suffix) ((((hash_entry) (prefix)) << 8) | (suffix)) + + + + + +/* Private version of data destination object */ + + + +typedef struct { + + struct djpeg_dest_struct pub; /* public fields */ + + + + j_decompress_ptr cinfo; /* back link saves passing separate parm */ + + + + /* State for packing variable-width codes into a bitstream */ + + int n_bits; /* current number of bits/code */ + + code_int maxcode; /* maximum code, given n_bits */ + + int init_bits; /* initial n_bits ... restored after clear */ + + INT32 cur_accum; /* holds bits not yet output */ + + int cur_bits; /* # of bits in cur_accum */ + + + + /* LZW string construction */ + + code_int waiting_code; /* symbol not yet output; may be extendable */ + + boolean first_byte; /* if TRUE, waiting_code is not valid */ + + + + /* State for LZW code assignment */ + + code_int ClearCode; /* clear code (doesn't change) */ + + code_int EOFCode; /* EOF code (ditto) */ + + code_int free_code; /* first not-yet-used symbol code */ + + + + /* LZW hash table */ + + code_int *hash_code; /* => hash table of symbol codes */ + + hash_entry FAR *hash_value; /* => hash table of symbol values */ + + + + /* GIF data packet construction buffer */ + + int bytesinpkt; /* # of bytes in current packet */ + + char packetbuf[256]; /* workspace for accumulating packet */ + + + +} gif_dest_struct; + + + +typedef gif_dest_struct * gif_dest_ptr; + + + + + +/* + + * Routines to package compressed data bytes into GIF data blocks. + + * A data block consists of a count byte (1..255) and that many data bytes. + + */ + + + +LOCAL void + +flush_packet (gif_dest_ptr dinfo) + +/* flush any accumulated data */ + +{ + + if (dinfo->bytesinpkt > 0) { /* never write zero-length packet */ + + dinfo->packetbuf[0] = (char) dinfo->bytesinpkt++; + + if (JFWRITE(dinfo->pub.output_file, dinfo->packetbuf, dinfo->bytesinpkt) + + != (size_t) dinfo->bytesinpkt) + + ERREXIT(dinfo->cinfo, JERR_FILE_WRITE); + + dinfo->bytesinpkt = 0; + + } + +} + + + + + +/* Add a character to current packet; flush to disk if necessary */ + +#define CHAR_OUT(dinfo,c) \ + + { (dinfo)->packetbuf[++(dinfo)->bytesinpkt] = (char) (c); \ + + if ((dinfo)->bytesinpkt >= 255) \ + + flush_packet(dinfo); \ + + } + + + + + +/* Routine to convert variable-width codes into a byte stream */ + + + +LOCAL void + +output (gif_dest_ptr dinfo, code_int code) + +/* Emit a code of n_bits bits */ + +/* Uses cur_accum and cur_bits to reblock into 8-bit bytes */ + +{ + + dinfo->cur_accum |= ((INT32) code) << dinfo->cur_bits; + + dinfo->cur_bits += dinfo->n_bits; + + + + while (dinfo->cur_bits >= 8) { + + CHAR_OUT(dinfo, dinfo->cur_accum & 0xFF); + + dinfo->cur_accum >>= 8; + + dinfo->cur_bits -= 8; + + } + + + + /* + + * If the next entry is going to be too big for the code size, + + * then increase it, if possible. We do this here to ensure + + * that it's done in sync with the decoder's codesize increases. + + */ + + if (dinfo->free_code > dinfo->maxcode) { + + dinfo->n_bits++; + + if (dinfo->n_bits == MAX_LZW_BITS) + + dinfo->maxcode = LZW_TABLE_SIZE; /* free_code will never exceed this */ + + else + + dinfo->maxcode = MAXCODE(dinfo->n_bits); + + } + +} + + + + + +/* The LZW algorithm proper */ + + + + + +LOCAL void + +clear_hash (gif_dest_ptr dinfo) + +/* Fill the hash table with empty entries */ + +{ + + /* It's sufficient to zero hash_code[] */ + + MEMZERO(dinfo->hash_code, HSIZE * SIZEOF(code_int)); + +} + + + + + +LOCAL void + +clear_block (gif_dest_ptr dinfo) + +/* Reset compressor and issue a Clear code */ + +{ + + clear_hash(dinfo); /* delete all the symbols */ + + dinfo->free_code = dinfo->ClearCode + 2; + + output(dinfo, dinfo->ClearCode); /* inform decoder */ + + dinfo->n_bits = dinfo->init_bits; /* reset code size */ + + dinfo->maxcode = MAXCODE(dinfo->n_bits); + +} + + + + + +LOCAL void + +compress_init (gif_dest_ptr dinfo, int i_bits) + +/* Initialize LZW compressor */ + +{ + + /* init all the state variables */ + + dinfo->n_bits = dinfo->init_bits = i_bits; + + dinfo->maxcode = MAXCODE(dinfo->n_bits); + + dinfo->ClearCode = ((code_int) 1 << (i_bits - 1)); + + dinfo->EOFCode = dinfo->ClearCode + 1; + + dinfo->free_code = dinfo->ClearCode + 2; + + dinfo->first_byte = TRUE; /* no waiting symbol yet */ + + /* init output buffering vars */ + + dinfo->bytesinpkt = 0; + + dinfo->cur_accum = 0; + + dinfo->cur_bits = 0; + + /* clear hash table */ + + clear_hash(dinfo); + + /* GIF specifies an initial Clear code */ + + output(dinfo, dinfo->ClearCode); + +} + + + + + +LOCAL void + +compress_byte (gif_dest_ptr dinfo, int c) + +/* Accept and compress one 8-bit byte */ + +{ + + register hash_int i; + + register hash_int disp; + + register hash_entry probe_value; + + + + if (dinfo->first_byte) { /* need to initialize waiting_code */ + + dinfo->waiting_code = c; + + dinfo->first_byte = FALSE; + + return; + + } + + + + /* Probe hash table to see if a symbol exists for + + * waiting_code followed by c. + + * If so, replace waiting_code by that symbol and return. + + */ + + i = ((hash_int) c << (MAX_LZW_BITS-8)) + dinfo->waiting_code; + + /* i is less than twice 2**MAX_LZW_BITS, therefore less than twice HSIZE */ + + if (i >= HSIZE) + + i -= HSIZE; + + + + probe_value = HASH_ENTRY(dinfo->waiting_code, c); + + + + if (dinfo->hash_code[i] != 0) { /* is first probed slot empty? */ + + if (dinfo->hash_value[i] == probe_value) { + + dinfo->waiting_code = dinfo->hash_code[i]; + + return; + + } + + if (i == 0) /* secondary hash (after G. Knott) */ + + disp = 1; + + else + + disp = HSIZE - i; + + for (;;) { + + i -= disp; + + if (i < 0) + + i += HSIZE; + + if (dinfo->hash_code[i] == 0) + + break; /* hit empty slot */ + + if (dinfo->hash_value[i] == probe_value) { + + dinfo->waiting_code = dinfo->hash_code[i]; + + return; + + } + + } + + } + + + + /* here when hashtable[i] is an empty slot; desired symbol not in table */ + + output(dinfo, dinfo->waiting_code); + + if (dinfo->free_code < LZW_TABLE_SIZE) { + + dinfo->hash_code[i] = dinfo->free_code++; /* add symbol to hashtable */ + + dinfo->hash_value[i] = probe_value; + + } else + + clear_block(dinfo); + + dinfo->waiting_code = c; + +} + + + + + +LOCAL void + +compress_term (gif_dest_ptr dinfo) + +/* Clean up at end */ + +{ + + /* Flush out the buffered code */ + + if (! dinfo->first_byte) + + output(dinfo, dinfo->waiting_code); + + /* Send an EOF code */ + + output(dinfo, dinfo->EOFCode); + + /* Flush the bit-packing buffer */ + + if (dinfo->cur_bits > 0) { + + CHAR_OUT(dinfo, dinfo->cur_accum & 0xFF); + + } + + /* Flush the packet buffer */ + + flush_packet(dinfo); + +} + + + + + +/* GIF header construction */ + + + + + +LOCAL void + +put_word (gif_dest_ptr dinfo, unsigned int w) + +/* Emit a 16-bit word, LSB first */ + +{ + + putc(w & 0xFF, dinfo->pub.output_file); + + putc((w >> 8) & 0xFF, dinfo->pub.output_file); + +} + + + + + +LOCAL void + +put_3bytes (gif_dest_ptr dinfo, int val) + +/* Emit 3 copies of same byte value --- handy subr for colormap construction */ + +{ + + putc(val, dinfo->pub.output_file); + + putc(val, dinfo->pub.output_file); + + putc(val, dinfo->pub.output_file); + +} + + + + + +LOCAL void + +emit_header (gif_dest_ptr dinfo, int num_colors, JSAMPARRAY colormap) + +/* Output the GIF file header, including color map */ + +/* If colormap==NULL, synthesize a gray-scale colormap */ + +{ + + int BitsPerPixel, ColorMapSize, InitCodeSize, FlagByte; + + int cshift = dinfo->cinfo->data_precision - 8; + + int i; + + + + if (num_colors > 256) + + ERREXIT1(dinfo->cinfo, JERR_TOO_MANY_COLORS, num_colors); + + /* Compute bits/pixel and related values */ + + BitsPerPixel = 1; + + while (num_colors > (1 << BitsPerPixel)) + + BitsPerPixel++; + + ColorMapSize = 1 << BitsPerPixel; + + if (BitsPerPixel <= 1) + + InitCodeSize = 2; + + else + + InitCodeSize = BitsPerPixel; + + /* + + * Write the GIF header. + + * Note that we generate a plain GIF87 header for maximum compatibility. + + */ + + putc('G', dinfo->pub.output_file); + + putc('I', dinfo->pub.output_file); + + putc('F', dinfo->pub.output_file); + + putc('8', dinfo->pub.output_file); + + putc('7', dinfo->pub.output_file); + + putc('a', dinfo->pub.output_file); + + /* Write the Logical Screen Descriptor */ + + put_word(dinfo, (unsigned int) dinfo->cinfo->output_width); + + put_word(dinfo, (unsigned int) dinfo->cinfo->output_height); + + FlagByte = 0x80; /* Yes, there is a global color table */ + + FlagByte |= (BitsPerPixel-1) << 4; /* color resolution */ + + FlagByte |= (BitsPerPixel-1); /* size of global color table */ + + putc(FlagByte, dinfo->pub.output_file); + + putc(0, dinfo->pub.output_file); /* Background color index */ + + putc(0, dinfo->pub.output_file); /* Reserved (aspect ratio in GIF89) */ + + /* Write the Global Color Map */ + + /* If the color map is more than 8 bits precision, */ + + /* we reduce it to 8 bits by shifting */ + + for (i=0; i < ColorMapSize; i++) { + + if (i < num_colors) { + + if (colormap != NULL) { + + if (dinfo->cinfo->out_color_space == JCS_RGB) { + + /* Normal case: RGB color map */ + + putc(GETJSAMPLE(colormap[0][i]) >> cshift, dinfo->pub.output_file); + + putc(GETJSAMPLE(colormap[1][i]) >> cshift, dinfo->pub.output_file); + + putc(GETJSAMPLE(colormap[2][i]) >> cshift, dinfo->pub.output_file); + + } else { + + /* Grayscale "color map": possible if quantizing grayscale image */ + + put_3bytes(dinfo, GETJSAMPLE(colormap[0][i]) >> cshift); + + } + + } else { + + /* Create a gray-scale map of num_colors values, range 0..255 */ + + put_3bytes(dinfo, (i * 255 + (num_colors-1)/2) / (num_colors-1)); + + } + + } else { + + /* fill out the map to a power of 2 */ + + put_3bytes(dinfo, 0); + + } + + } + + /* Write image separator and Image Descriptor */ + + putc(',', dinfo->pub.output_file); /* separator */ + + put_word(dinfo, 0); /* left/top offset */ + + put_word(dinfo, 0); + + put_word(dinfo, (unsigned int) dinfo->cinfo->output_width); /* image size */ + + put_word(dinfo, (unsigned int) dinfo->cinfo->output_height); + + /* flag byte: not interlaced, no local color map */ + + putc(0x00, dinfo->pub.output_file); + + /* Write Initial Code Size byte */ + + putc(InitCodeSize, dinfo->pub.output_file); + + + + /* Initialize for LZW compression of image data */ + + compress_init(dinfo, InitCodeSize+1); + +} + + + + + +/* + + * Startup: write the file header. + + */ + + + +METHODDEF void + +start_output_gif (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) + +{ + + gif_dest_ptr dest = (gif_dest_ptr) dinfo; + + + + if (cinfo->quantize_colors) + + emit_header(dest, cinfo->actual_number_of_colors, cinfo->colormap); + + else + + emit_header(dest, 256, (JSAMPARRAY) NULL); + +} + + + + + +/* + + * Write some pixel data. + + * In this module rows_supplied will always be 1. + + */ + + + +METHODDEF void + +put_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + + JDIMENSION rows_supplied) + +{ + + gif_dest_ptr dest = (gif_dest_ptr) dinfo; + + register JSAMPROW ptr; + + register JDIMENSION col; + + + + ptr = dest->pub.buffer[0]; + + for (col = cinfo->output_width; col > 0; col--) { + + compress_byte(dest, GETJSAMPLE(*ptr++)); + + } + +} + + + + + +/* + + * Finish up at the end of the file. + + */ + + + +METHODDEF void + +finish_output_gif (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) + +{ + + gif_dest_ptr dest = (gif_dest_ptr) dinfo; + + + + /* Flush LZW mechanism */ + + compress_term(dest); + + /* Write a zero-length data block to end the series */ + + putc(0, dest->pub.output_file); + + /* Write the GIF terminator mark */ + + putc(';', dest->pub.output_file); + + /* Make sure we wrote the output file OK */ + + fflush(dest->pub.output_file); + + if (ferror(dest->pub.output_file)) + + ERREXIT(cinfo, JERR_FILE_WRITE); + +} + + + + + +/* + + * The module selection routine for GIF format output. + + */ + + + +GLOBAL djpeg_dest_ptr + +jinit_write_gif (j_decompress_ptr cinfo) + +{ + + gif_dest_ptr dest; + + + + /* Create module interface object, fill in method pointers */ + + dest = (gif_dest_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(gif_dest_struct)); + + dest->cinfo = cinfo; /* make back link for subroutines */ + + dest->pub.start_output = start_output_gif; + + dest->pub.put_pixel_rows = put_pixel_rows; + + dest->pub.finish_output = finish_output_gif; + + + + if (cinfo->out_color_space != JCS_GRAYSCALE && + + cinfo->out_color_space != JCS_RGB) + + ERREXIT(cinfo, JERR_GIF_COLORSPACE); + + + + /* Force quantization if color or if > 8 bits input */ + + if (cinfo->out_color_space != JCS_GRAYSCALE || cinfo->data_precision > 8) { + + /* Force quantization to at most 256 colors */ + + cinfo->quantize_colors = TRUE; + + if (cinfo->desired_number_of_colors > 256) + + cinfo->desired_number_of_colors = 256; + + } + + + + /* Calculate output image dimensions so we can allocate space */ + + jpeg_calc_output_dimensions(cinfo); + + + + if (cinfo->output_components != 1) /* safety check: just one component? */ + + ERREXIT(cinfo, JERR_GIF_BUG); + + + + /* Create decompressor output buffer. */ + + dest->pub.buffer = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, cinfo->output_width, (JDIMENSION) 1); + + dest->pub.buffer_height = 1; + + + + /* Allocate space for hash table */ + + dest->hash_code = (code_int *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + HSIZE * SIZEOF(code_int)); + + dest->hash_value = (hash_entry FAR *) + + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + HSIZE * SIZEOF(hash_entry)); + + + + return (djpeg_dest_ptr) dest; + +} + + + +#endif /* GIF_SUPPORTED */ + diff --git a/utils/roq2/jpeg/wrppm.c b/utils/roq2/jpeg/wrppm.c new file mode 100644 index 0000000..55ff485 --- /dev/null +++ b/utils/roq2/jpeg/wrppm.c @@ -0,0 +1,536 @@ +/* + + * wrppm.c + + * + + * Copyright (C) 1991-1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains routines to write output images in PPM/PGM format. + + * The extended 2-byte-per-sample raw PPM/PGM formats are supported. + + * The PBMPLUS library is NOT required to compile this software + + * (but it is highly useful as a set of PPM image manipulation programs). + + * + + * These routines may need modification for non-Unix environments or + + * specialized applications. As they stand, they assume output to + + * an ordinary stdio stream. + + */ + + + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + + + +#ifdef PPM_SUPPORTED + + + + + +/* + + * For 12-bit JPEG data, we either downscale the values to 8 bits + + * (to write standard byte-per-sample PPM/PGM files), or output + + * nonstandard word-per-sample PPM/PGM files. Downscaling is done + + * if PPM_NORAWWORD is defined (this can be done in the Makefile + + * or in jconfig.h). + + * (When the core library supports data precision reduction, a cleaner + + * implementation will be to ask for that instead.) + + */ + + + +#if BITS_IN_JSAMPLE == 8 + +#define PUTPPMSAMPLE(ptr,v) *ptr++ = (char) (v) + +#define BYTESPERSAMPLE 1 + +#define PPM_MAXVAL 255 + +#else + +#ifdef PPM_NORAWWORD + +#define PUTPPMSAMPLE(ptr,v) *ptr++ = (char) ((v) >> (BITS_IN_JSAMPLE-8)) + +#define BYTESPERSAMPLE 1 + +#define PPM_MAXVAL 255 + +#else + +/* The word-per-sample format always puts the LSB first. */ + +#define PUTPPMSAMPLE(ptr,v) \ + + { register int val_ = v; \ + + *ptr++ = (char) (val_ & 0xFF); \ + + *ptr++ = (char) ((val_ >> 8) & 0xFF); \ + + } + +#define BYTESPERSAMPLE 2 + +#define PPM_MAXVAL ((1<pub.output_file, dest->iobuffer, dest->buffer_width); + +} + + + + + +/* + + * This code is used when we have to copy the data and apply a pixel + + * format translation. Typically this only happens in 12-bit mode. + + */ + + + +METHODDEF void + +copy_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + + JDIMENSION rows_supplied) + +{ + + ppm_dest_ptr dest = (ppm_dest_ptr) dinfo; + + register char * bufferptr; + + register JSAMPROW ptr; + + register JDIMENSION col; + + + + ptr = dest->pub.buffer[0]; + + bufferptr = dest->iobuffer; + + for (col = dest->samples_per_row; col > 0; col--) { + + PUTPPMSAMPLE(bufferptr, GETJSAMPLE(*ptr++)); + + } + + (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width); + +} + + + + + +/* + + * Write some pixel data when color quantization is in effect. + + * We have to demap the color index values to straight data. + + */ + + + +METHODDEF void + +put_demapped_rgb (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + + JDIMENSION rows_supplied) + +{ + + ppm_dest_ptr dest = (ppm_dest_ptr) dinfo; + + register char * bufferptr; + + register int pixval; + + register JSAMPROW ptr; + + register JSAMPROW color_map0 = cinfo->colormap[0]; + + register JSAMPROW color_map1 = cinfo->colormap[1]; + + register JSAMPROW color_map2 = cinfo->colormap[2]; + + register JDIMENSION col; + + + + ptr = dest->pub.buffer[0]; + + bufferptr = dest->iobuffer; + + for (col = cinfo->output_width; col > 0; col--) { + + pixval = GETJSAMPLE(*ptr++); + + PUTPPMSAMPLE(bufferptr, GETJSAMPLE(color_map0[pixval])); + + PUTPPMSAMPLE(bufferptr, GETJSAMPLE(color_map1[pixval])); + + PUTPPMSAMPLE(bufferptr, GETJSAMPLE(color_map2[pixval])); + + } + + (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width); + +} + + + + + +METHODDEF void + +put_demapped_gray (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + + JDIMENSION rows_supplied) + +{ + + ppm_dest_ptr dest = (ppm_dest_ptr) dinfo; + + register char * bufferptr; + + register JSAMPROW ptr; + + register JSAMPROW color_map = cinfo->colormap[0]; + + register JDIMENSION col; + + + + ptr = dest->pub.buffer[0]; + + bufferptr = dest->iobuffer; + + for (col = cinfo->output_width; col > 0; col--) { + + PUTPPMSAMPLE(bufferptr, GETJSAMPLE(color_map[GETJSAMPLE(*ptr++)])); + + } + + (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width); + +} + + + + + +/* + + * Startup: write the file header. + + */ + + + +METHODDEF void + +start_output_ppm (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) + +{ + + ppm_dest_ptr dest = (ppm_dest_ptr) dinfo; + + + + /* Emit file header */ + + switch (cinfo->out_color_space) { + + case JCS_GRAYSCALE: + + /* emit header for raw PGM format */ + + fprintf(dest->pub.output_file, "P5\n%ld %ld\n%d\n", + + (long) cinfo->output_width, (long) cinfo->output_height, + + PPM_MAXVAL); + + break; + + case JCS_RGB: + + /* emit header for raw PPM format */ + + fprintf(dest->pub.output_file, "P6\n%ld %ld\n%d\n", + + (long) cinfo->output_width, (long) cinfo->output_height, + + PPM_MAXVAL); + + break; + + default: + + ERREXIT(cinfo, JERR_PPM_COLORSPACE); + + } + +} + + + + + +/* + + * Finish up at the end of the file. + + */ + + + +METHODDEF void + +finish_output_ppm (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) + +{ + + /* Make sure we wrote the output file OK */ + + fflush(dinfo->output_file); + + if (ferror(dinfo->output_file)) + + ERREXIT(cinfo, JERR_FILE_WRITE); + +} + + + + + +/* + + * The module selection routine for PPM format output. + + */ + + + +GLOBAL djpeg_dest_ptr + +jinit_write_ppm (j_decompress_ptr cinfo) + +{ + + ppm_dest_ptr dest; + + + + /* Create module interface object, fill in method pointers */ + + dest = (ppm_dest_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(ppm_dest_struct)); + + dest->pub.start_output = start_output_ppm; + + dest->pub.finish_output = finish_output_ppm; + + + + /* Calculate output image dimensions so we can allocate space */ + + jpeg_calc_output_dimensions(cinfo); + + + + /* Create physical I/O buffer. Note we make this near on a PC. */ + + dest->samples_per_row = cinfo->output_width * cinfo->out_color_components; + + dest->buffer_width = dest->samples_per_row * (BYTESPERSAMPLE * SIZEOF(char)); + + dest->iobuffer = (char *) (*cinfo->mem->alloc_small) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, dest->buffer_width); + + + + if (cinfo->quantize_colors || BITS_IN_JSAMPLE != 8 || + + SIZEOF(JSAMPLE) != SIZEOF(char)) { + + /* When quantizing, we need an output buffer for colormap indexes + + * that's separate from the physical I/O buffer. We also need a + + * separate buffer if pixel format translation must take place. + + */ + + dest->pub.buffer = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, + + cinfo->output_width * cinfo->output_components, (JDIMENSION) 1); + + dest->pub.buffer_height = 1; + + if (! cinfo->quantize_colors) + + dest->pub.put_pixel_rows = copy_pixel_rows; + + else if (cinfo->out_color_space == JCS_GRAYSCALE) + + dest->pub.put_pixel_rows = put_demapped_gray; + + else + + dest->pub.put_pixel_rows = put_demapped_rgb; + + } else { + + /* We will fwrite() directly from decompressor output buffer. */ + + /* Synthesize a JSAMPARRAY pointer structure */ + + /* Cast here implies near->far pointer conversion on PCs */ + + dest->pixrow = (JSAMPROW) dest->iobuffer; + + dest->pub.buffer = & dest->pixrow; + + dest->pub.buffer_height = 1; + + dest->pub.put_pixel_rows = put_pixel_rows; + + } + + + + return (djpeg_dest_ptr) dest; + +} + + + +#endif /* PPM_SUPPORTED */ + diff --git a/utils/roq2/jpeg/wrrle.c b/utils/roq2/jpeg/wrrle.c new file mode 100644 index 0000000..a0b128c --- /dev/null +++ b/utils/roq2/jpeg/wrrle.c @@ -0,0 +1,610 @@ +/* + + * wrrle.c + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains routines to write output images in RLE format. + + * The Utah Raster Toolkit library is required (version 3.1 or later). + + * + + * These routines may need modification for non-Unix environments or + + * specialized applications. As they stand, they assume output to + + * an ordinary stdio stream. + + * + + * Based on code contributed by Mike Lijewski, + + * with updates from Robert Hutchinson. + + */ + + + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + + + +#ifdef RLE_SUPPORTED + + + +/* rle.h is provided by the Utah Raster Toolkit. */ + + + +#include + + + +/* + + * We assume that JSAMPLE has the same representation as rle_pixel, + + * to wit, "unsigned char". Hence we can't cope with 12- or 16-bit samples. + + */ + + + +#if BITS_IN_JSAMPLE != 8 + + Sorry, this code only copes with 8-bit JSAMPLEs. /* deliberate syntax err */ + +#endif + + + + + +/* + + * Since RLE stores scanlines bottom-to-top, we have to invert the image + + * from JPEG's top-to-bottom order. To do this, we save the outgoing data + + * in a virtual array during put_pixel_row calls, then actually emit the + + * RLE file during finish_output. + + */ + + + + + +/* + + * For now, if we emit an RLE color map then it is always 256 entries long, + + * though not all of the entries need be used. + + */ + + + +#define CMAPBITS 8 + +#define CMAPLENGTH (1<<(CMAPBITS)) + + + +typedef struct { + + struct djpeg_dest_struct pub; /* public fields */ + + + + jvirt_sarray_ptr image; /* virtual array to store the output image */ + + rle_map *colormap; /* RLE-style color map, or NULL if none */ + + rle_pixel **rle_row; /* To pass rows to rle_putrow() */ + + + +} rle_dest_struct; + + + +typedef rle_dest_struct * rle_dest_ptr; + + + +/* Forward declarations */ + +METHODDEF void rle_put_pixel_rows + + JPP((j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + + JDIMENSION rows_supplied)); + + + + + +/* + + * Write the file header. + + * + + * In this module it's easier to wait till finish_output to write anything. + + */ + + + +METHODDEF void + +start_output_rle (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) + +{ + + rle_dest_ptr dest = (rle_dest_ptr) dinfo; + + size_t cmapsize; + + int i, ci; + +#ifdef PROGRESS_REPORT + + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; + +#endif + + + + /* + + * Make sure the image can be stored in RLE format. + + * + + * - RLE stores image dimensions as *signed* 16 bit integers. JPEG + + * uses unsigned, so we have to check the width. + + * + + * - Colorspace is expected to be grayscale or RGB. + + * + + * - The number of channels (components) is expected to be 1 (grayscale/ + + * pseudocolor) or 3 (truecolor/directcolor). + + * (could be 2 or 4 if using an alpha channel, but we aren't) + + */ + + + + if (cinfo->output_width > 32767 || cinfo->output_height > 32767) + + ERREXIT2(cinfo, JERR_RLE_DIMENSIONS, cinfo->output_width, + + cinfo->output_height); + + + + if (cinfo->out_color_space != JCS_GRAYSCALE && + + cinfo->out_color_space != JCS_RGB) + + ERREXIT(cinfo, JERR_RLE_COLORSPACE); + + + + if (cinfo->output_components != 1 && cinfo->output_components != 3) + + ERREXIT1(cinfo, JERR_RLE_TOOMANYCHANNELS, cinfo->num_components); + + + + /* Convert colormap, if any, to RLE format. */ + + + + dest->colormap = NULL; + + + + if (cinfo->quantize_colors) { + + /* Allocate storage for RLE-style cmap, zero any extra entries */ + + cmapsize = cinfo->out_color_components * CMAPLENGTH * SIZEOF(rle_map); + + dest->colormap = (rle_map *) (*cinfo->mem->alloc_small) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, cmapsize); + + MEMZERO(dest->colormap, cmapsize); + + + + /* Save away data in RLE format --- note 8-bit left shift! */ + + /* Shifting would need adjustment for JSAMPLEs wider than 8 bits. */ + + for (ci = 0; ci < cinfo->out_color_components; ci++) { + + for (i = 0; i < cinfo->actual_number_of_colors; i++) { + + dest->colormap[ci * CMAPLENGTH + i] = + + GETJSAMPLE(cinfo->colormap[ci][i]) << 8; + + } + + } + + } + + + + /* Set the output buffer to the first row */ + + dest->pub.buffer = (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, dest->image, (JDIMENSION) 0, (JDIMENSION) 1, TRUE); + + dest->pub.buffer_height = 1; + + + + dest->pub.put_pixel_rows = rle_put_pixel_rows; + + + +#ifdef PROGRESS_REPORT + + if (progress != NULL) { + + progress->total_extra_passes++; /* count file writing as separate pass */ + + } + +#endif + +} + + + + + +/* + + * Write some pixel data. + + * + + * This routine just saves the data away in a virtual array. + + */ + + + +METHODDEF void + +rle_put_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + + JDIMENSION rows_supplied) + +{ + + rle_dest_ptr dest = (rle_dest_ptr) dinfo; + + + + if (cinfo->output_scanline < cinfo->output_height) { + + dest->pub.buffer = (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, dest->image, + + cinfo->output_scanline, (JDIMENSION) 1, TRUE); + + } + +} + + + +/* + + * Finish up at the end of the file. + + * + + * Here is where we really output the RLE file. + + */ + + + +METHODDEF void + +finish_output_rle (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) + +{ + + rle_dest_ptr dest = (rle_dest_ptr) dinfo; + + rle_hdr header; /* Output file information */ + + rle_pixel **rle_row, *red, *green, *blue; + + JSAMPROW output_row; + + char cmapcomment[80]; + + int row, col; + + int ci; + +#ifdef PROGRESS_REPORT + + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; + +#endif + + + + /* Initialize the header info */ + + header = *rle_hdr_init(NULL); + + header.rle_file = dest->pub.output_file; + + header.xmin = 0; + + header.xmax = cinfo->output_width - 1; + + header.ymin = 0; + + header.ymax = cinfo->output_height - 1; + + header.alpha = 0; + + header.ncolors = cinfo->output_components; + + for (ci = 0; ci < cinfo->output_components; ci++) { + + RLE_SET_BIT(header, ci); + + } + + if (cinfo->quantize_colors) { + + header.ncmap = cinfo->out_color_components; + + header.cmaplen = CMAPBITS; + + header.cmap = dest->colormap; + + /* Add a comment to the output image with the true colormap length. */ + + sprintf(cmapcomment, "color_map_length=%d", cinfo->actual_number_of_colors); + + rle_putcom(cmapcomment, &header); + + } + + + + /* Emit the RLE header and color map (if any) */ + + rle_put_setup(&header); + + + + /* Now output the RLE data from our virtual array. + + * We assume here that (a) rle_pixel is represented the same as JSAMPLE, + + * and (b) we are not on a machine where FAR pointers differ from regular. + + */ + + + +#ifdef PROGRESS_REPORT + + if (progress != NULL) { + + progress->pub.pass_limit = cinfo->output_height; + + progress->pub.pass_counter = 0; + + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + + } + +#endif + + + + if (cinfo->output_components == 1) { + + for (row = cinfo->output_height-1; row >= 0; row--) { + + rle_row = (rle_pixel **) (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, dest->image, + + (JDIMENSION) row, (JDIMENSION) 1, FALSE); + + rle_putrow(rle_row, (int) cinfo->output_width, &header); + +#ifdef PROGRESS_REPORT + + if (progress != NULL) { + + progress->pub.pass_counter++; + + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + + } + +#endif + + } + + } else { + + for (row = cinfo->output_height-1; row >= 0; row--) { + + rle_row = (rle_pixel **) dest->rle_row; + + output_row = * (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, dest->image, + + (JDIMENSION) row, (JDIMENSION) 1, FALSE); + + red = rle_row[0]; + + green = rle_row[1]; + + blue = rle_row[2]; + + for (col = cinfo->output_width; col > 0; col--) { + + *red++ = GETJSAMPLE(*output_row++); + + *green++ = GETJSAMPLE(*output_row++); + + *blue++ = GETJSAMPLE(*output_row++); + + } + + rle_putrow(rle_row, (int) cinfo->output_width, &header); + +#ifdef PROGRESS_REPORT + + if (progress != NULL) { + + progress->pub.pass_counter++; + + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + + } + +#endif + + } + + } + + + +#ifdef PROGRESS_REPORT + + if (progress != NULL) + + progress->completed_extra_passes++; + +#endif + + + + /* Emit file trailer */ + + rle_puteof(&header); + + fflush(dest->pub.output_file); + + if (ferror(dest->pub.output_file)) + + ERREXIT(cinfo, JERR_FILE_WRITE); + +} + + + + + +/* + + * The module selection routine for RLE format output. + + */ + + + +GLOBAL djpeg_dest_ptr + +jinit_write_rle (j_decompress_ptr cinfo) + +{ + + rle_dest_ptr dest; + + + + /* Create module interface object, fill in method pointers */ + + dest = (rle_dest_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(rle_dest_struct)); + + dest->pub.start_output = start_output_rle; + + dest->pub.finish_output = finish_output_rle; + + + + /* Calculate output image dimensions so we can allocate space */ + + jpeg_calc_output_dimensions(cinfo); + + + + /* Allocate a work array for output to the RLE library. */ + + dest->rle_row = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, + + cinfo->output_width, (JDIMENSION) cinfo->output_components); + + + + /* Allocate a virtual array to hold the image. */ + + dest->image = (*cinfo->mem->request_virt_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + + (JDIMENSION) (cinfo->output_width * cinfo->output_components), + + cinfo->output_height, (JDIMENSION) 1); + + + + return (djpeg_dest_ptr) dest; + +} + + + +#endif /* RLE_SUPPORTED */ + diff --git a/utils/roq2/jpeg/wrtarga.c b/utils/roq2/jpeg/wrtarga.c new file mode 100644 index 0000000..2a89e7c --- /dev/null +++ b/utils/roq2/jpeg/wrtarga.c @@ -0,0 +1,506 @@ +/* + + * wrtarga.c + + * + + * Copyright (C) 1991-1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains routines to write output images in Targa format. + + * + + * These routines may need modification for non-Unix environments or + + * specialized applications. As they stand, they assume output to + + * an ordinary stdio stream. + + * + + * Based on code contributed by Lee Daniel Crocker. + + */ + + + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + + + +#ifdef TARGA_SUPPORTED + + + + + +/* + + * To support 12-bit JPEG data, we'd have to scale output down to 8 bits. + + * This is not yet implemented. + + */ + + + +#if BITS_IN_JSAMPLE != 8 + + Sorry, this code only copes with 8-bit JSAMPLEs. /* deliberate syntax err */ + +#endif + + + +/* + + * The output buffer needs to be writable by fwrite(). On PCs, we must + + * allocate the buffer in near data space, because we are assuming small-data + + * memory model, wherein fwrite() can't reach far memory. If you need to + + * process very wide images on a PC, you might have to compile in large-memory + + * model, or else replace fwrite() with a putc() loop --- which will be much + + * slower. + + */ + + + + + +/* Private version of data destination object */ + + + +typedef struct { + + struct djpeg_dest_struct pub; /* public fields */ + + + + char *iobuffer; /* physical I/O buffer */ + + JDIMENSION buffer_width; /* width of one row */ + +} tga_dest_struct; + + + +typedef tga_dest_struct * tga_dest_ptr; + + + + + +LOCAL void + +write_header (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, int num_colors) + +/* Create and write a Targa header */ + +{ + + char targaheader[18]; + + + + /* Set unused fields of header to 0 */ + + MEMZERO(targaheader, SIZEOF(targaheader)); + + + + if (num_colors > 0) { + + targaheader[1] = 1; /* color map type 1 */ + + targaheader[5] = (char) (num_colors & 0xFF); + + targaheader[6] = (char) (num_colors >> 8); + + targaheader[7] = 24; /* 24 bits per cmap entry */ + + } + + + + targaheader[12] = (char) (cinfo->output_width & 0xFF); + + targaheader[13] = (char) (cinfo->output_width >> 8); + + targaheader[14] = (char) (cinfo->output_height & 0xFF); + + targaheader[15] = (char) (cinfo->output_height >> 8); + + targaheader[17] = 0x20; /* Top-down, non-interlaced */ + + + + if (cinfo->out_color_space == JCS_GRAYSCALE) { + + targaheader[2] = 3; /* image type = uncompressed gray-scale */ + + targaheader[16] = 8; /* bits per pixel */ + + } else { /* must be RGB */ + + if (num_colors > 0) { + + targaheader[2] = 1; /* image type = colormapped RGB */ + + targaheader[16] = 8; + + } else { + + targaheader[2] = 2; /* image type = uncompressed RGB */ + + targaheader[16] = 24; + + } + + } + + + + if (JFWRITE(dinfo->output_file, targaheader, 18) != (size_t) 18) + + ERREXIT(cinfo, JERR_FILE_WRITE); + +} + + + + + +/* + + * Write some pixel data. + + * In this module rows_supplied will always be 1. + + */ + + + +METHODDEF void + +put_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + + JDIMENSION rows_supplied) + +/* used for unquantized full-color output */ + +{ + + tga_dest_ptr dest = (tga_dest_ptr) dinfo; + + register JSAMPROW inptr; + + register char * outptr; + + register JDIMENSION col; + + + + inptr = dest->pub.buffer[0]; + + outptr = dest->iobuffer; + + for (col = cinfo->output_width; col > 0; col--) { + + outptr[0] = (char) GETJSAMPLE(inptr[2]); /* RGB to BGR order */ + + outptr[1] = (char) GETJSAMPLE(inptr[1]); + + outptr[2] = (char) GETJSAMPLE(inptr[0]); + + inptr += 3, outptr += 3; + + } + + (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width); + +} + + + +METHODDEF void + +put_gray_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + + JDIMENSION rows_supplied) + +/* used for grayscale OR quantized color output */ + +{ + + tga_dest_ptr dest = (tga_dest_ptr) dinfo; + + register JSAMPROW inptr; + + register char * outptr; + + register JDIMENSION col; + + + + inptr = dest->pub.buffer[0]; + + outptr = dest->iobuffer; + + for (col = cinfo->output_width; col > 0; col--) { + + *outptr++ = (char) GETJSAMPLE(*inptr++); + + } + + (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width); + +} + + + + + +/* + + * Write some demapped pixel data when color quantization is in effect. + + * For Targa, this is only applied to grayscale data. + + */ + + + +METHODDEF void + +put_demapped_gray (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + + JDIMENSION rows_supplied) + +{ + + tga_dest_ptr dest = (tga_dest_ptr) dinfo; + + register JSAMPROW inptr; + + register char * outptr; + + register JSAMPROW color_map0 = cinfo->colormap[0]; + + register JDIMENSION col; + + + + inptr = dest->pub.buffer[0]; + + outptr = dest->iobuffer; + + for (col = cinfo->output_width; col > 0; col--) { + + *outptr++ = (char) GETJSAMPLE(color_map0[GETJSAMPLE(*inptr++)]); + + } + + (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width); + +} + + + + + +/* + + * Startup: write the file header. + + */ + + + +METHODDEF void + +start_output_tga (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) + +{ + + tga_dest_ptr dest = (tga_dest_ptr) dinfo; + + int num_colors, i; + + FILE *outfile; + + + + if (cinfo->out_color_space == JCS_GRAYSCALE) { + + /* Targa doesn't have a mapped grayscale format, so we will */ + + /* demap quantized gray output. Never emit a colormap. */ + + write_header(cinfo, dinfo, 0); + + if (cinfo->quantize_colors) + + dest->pub.put_pixel_rows = put_demapped_gray; + + else + + dest->pub.put_pixel_rows = put_gray_rows; + + } else if (cinfo->out_color_space == JCS_RGB) { + + if (cinfo->quantize_colors) { + + /* We only support 8-bit colormap indexes, so only 256 colors */ + + num_colors = cinfo->actual_number_of_colors; + + if (num_colors > 256) + + ERREXIT1(cinfo, JERR_TOO_MANY_COLORS, num_colors); + + write_header(cinfo, dinfo, num_colors); + + /* Write the colormap. Note Targa uses BGR byte order */ + + outfile = dest->pub.output_file; + + for (i = 0; i < num_colors; i++) { + + putc(GETJSAMPLE(cinfo->colormap[2][i]), outfile); + + putc(GETJSAMPLE(cinfo->colormap[1][i]), outfile); + + putc(GETJSAMPLE(cinfo->colormap[0][i]), outfile); + + } + + dest->pub.put_pixel_rows = put_gray_rows; + + } else { + + write_header(cinfo, dinfo, 0); + + dest->pub.put_pixel_rows = put_pixel_rows; + + } + + } else { + + ERREXIT(cinfo, JERR_TGA_COLORSPACE); + + } + +} + + + + + +/* + + * Finish up at the end of the file. + + */ + + + +METHODDEF void + +finish_output_tga (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) + +{ + + /* Make sure we wrote the output file OK */ + + fflush(dinfo->output_file); + + if (ferror(dinfo->output_file)) + + ERREXIT(cinfo, JERR_FILE_WRITE); + +} + + + + + +/* + + * The module selection routine for Targa format output. + + */ + + + +GLOBAL djpeg_dest_ptr + +jinit_write_targa (j_decompress_ptr cinfo) + +{ + + tga_dest_ptr dest; + + + + /* Create module interface object, fill in method pointers */ + + dest = (tga_dest_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(tga_dest_struct)); + + dest->pub.start_output = start_output_tga; + + dest->pub.finish_output = finish_output_tga; + + + + /* Calculate output image dimensions so we can allocate space */ + + jpeg_calc_output_dimensions(cinfo); + + + + /* Create I/O buffer. Note we make this near on a PC. */ + + dest->buffer_width = cinfo->output_width * cinfo->output_components; + + dest->iobuffer = (char *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (size_t) (dest->buffer_width * SIZEOF(char))); + + + + /* Create decompressor output buffer. */ + + dest->pub.buffer = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, dest->buffer_width, (JDIMENSION) 1); + + dest->pub.buffer_height = 1; + + + + return (djpeg_dest_ptr) dest; + +} + + + +#endif /* TARGA_SUPPORTED */ + diff --git a/utils/roq2/libim/im.c b/utils/roq2/libim/im.c new file mode 100644 index 0000000..947ecfa --- /dev/null +++ b/utils/roq2/libim/im.c @@ -0,0 +1,204 @@ +/** + ** $Header: /roq/libim/im.c 1 11/02/99 4:38p Zaphod $ + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + ** a division of General Atomics, San Diego, California, USA + ** + ** Users and possessors of this source code are hereby granted a + ** nonexclusive, royalty-free copyright and design patent license to + ** use this code in individual software. License is not granted for + ** commercial resale, in whole or in part, without prior written + ** permission from SDSC. This source is provided "AS IS" without express + ** or implied warranty of any kind. + ** + ** For further information contact: + ** E-Mail: info@sds.sdsc.edu + ** + ** Surface Mail: Information Center + ** San Diego Supercomputer Center + ** P.O. Box 85608 + ** San Diego, CA 92138-5608 + ** (619) 534-5000 + **/ + +#define HEADER " $Header: /roq/libim/im.c 1 11/02/99 4:38p Zaphod $" + +/** + ** FILE + ** im.c - Image Library Admin Functions + ** + ** PROJECT + ** libim - SDSC image manipulation library + ** + ** DESCRIPTION + ** im.c contains the error handling and other admin functions of + ** the image library. + ** + ** PUBLIC CONTENTS + ** d =defined constant + ** f =function + ** m =defined macro + ** t =typedef/struct/union + ** v =variable + ** ? =other + ** + ** ImPError f Print error message + ** ImQError f Query error message + ** + ** PRIVATE CONTENTS + ** none + ** + ** HISTORY + ** $Log: /roq/libim/im.c $ +* +* 1 11/02/99 4:38p Zaphod + ** Revision 1.22 1995/06/29 00:28:04 bduggan + ** updated copyright year + ** + ** Revision 1.21 1995/06/16 08:32:41 bduggan + ** Removed global variables. They are now in imerrno.c + ** + ** Revision 1.20 1995/05/19 13:02:26 bduggan + ** Added IMEFORMATUNKNOWN + ** + ** Revision 1.19 1995/04/13 00:02:23 bduggan + ** Added errors for channel maps + ** + ** Revision 1.18 94/10/03 11:29:22 nadeau + ** Updated to ANSI C and C++ compatibility. + ** Removed all use of register keyword. + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + ** Changed all float arguments to double. + ** Added forward declarations. + ** Added misc. casts to passify SGI and DEC compilers. + ** Changed all macros and defined constants to have names + ** starting with IM. + ** Updated comments. + ** Updated indenting on some code. + ** Updated copyright message. + ** + ** Revision 1.17 92/10/19 14:16:10 groening + ** added more error statements + ** + ** Revision 1.16 92/09/01 12:27:32 nadeau + ** Added IMEKEYFIELD and IMEADJUSTFIELD error codes and updated + ** error code names and messages for IMEOPERATION, IMEGRADUATION, + ** IMEKEY, IMEADJUST, and IMEBADRANGE. + ** + ** Revision 1.15 92/08/28 15:33:47 groening + ** Added more error codes. + ** + ** Revision 1.14 91/10/03 08:47:52 nadeau + ** Fixed #includes. + ** + ** Revision 1.13 91/09/07 16:21:45 todd + ** Added IMEDECODING and IMEENCODING error codes. + ** + ** Revision 1.12 91/03/08 14:25:46 nadeau + ** More error codes and messages. + ** + ** Revision 1.11 90/07/25 13:32:51 todd + ** added several new IME* errors + ** + ** Revision 1.10 90/07/23 13:48:01 nadeau + ** Added IMEDIFFSIZE error. + ** + ** Revision 1.9 90/07/09 07:12:23 mjb + ** replaced #include with declarations for malloc() and + ** realloc() because of Alliant + ** + ** Revision 1.8 90/07/02 15:47:54 mercurio + ** Error messages added + ** + ** Revision 1.7 90/06/28 20:15:33 mercurio + ** Added IMENOIMAGE and IMEUNSUPPORTED error codes + ** + ** Revision 1.6 90/06/28 15:14:57 nadeau + ** Added error handler globals, ImQError() and IMESYNTAX. + ** + ** Revision 1.5 90/06/25 14:34:51 nadeau + ** Added IMENULLTAGTABLE error code. + ** + ** Revision 1.4 90/06/25 14:15:07 nadeau + ** Removed error codes associated with tag table routines (they've been + ** moved to libsdsc.a). + ** + ** Revision 1.3 90/05/11 14:27:01 nadeau + ** Added IMENOTGRAY error code. + ** + ** Revision 1.2 90/05/11 09:53:03 nadeau + ** Added error messages and rearranged them. + ** + ** Revision 1.1 90/03/06 17:31:16 nadeau + ** Initial revision + ** + **/ + +#include "iminternal.h" + + +/** + ** CODE CREDITS + ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1990. + **/ + + + + + + +/* + * FUNCTION + * ImPError - Print error message + * + * DESCRIPTION + * The error text associated with the current ImErrNo is printed + * to stderr, preceded by the given leader string. + */ + +void /* Returns nothing */ +#ifdef __STDC__ +ImPError( char *s ) +#else +ImPError( s ) + char *s; /* Leader string */ +#endif +{ + if ( ImErrNo == IMESYS ) + perror( s ); + else if ( ImErrNo < 0 || ImErrNo >= ImNErr ) + fprintf( stderr, "Unknown error\n" ); + else if ( s && *s ) + fprintf( stderr, "%s: %s\n", s, ImErrList[ImErrNo] ); + else + fprintf( stderr, "%s\n", ImErrList[ImErrNo] ); +} + + + + + +/* + * FUNCTION + * ImQError - Query error message + * + * DESCRIPTION + * The error text associated with the current ImErrNo is returned. + */ + +char * /* Returns error text */ +#ifdef __STDC__ +ImQError( void ) +#else +ImQError( ) +#endif +{ + if ( ImErrNo == IMESYS ) + { + if ( errno < 0 || errno >= sys_nerr ) + return ( "Unknown error" ); + return ( sys_errlist[errno] ); + } + if ( ImErrNo < 0 || ImErrNo >= ImNErr ) + return ( "Unknown error" ); + return ( ImErrList[ImErrNo] ); +} diff --git a/utils/roq2/libim/im.h b/utils/roq2/libim/im.h new file mode 100644 index 0000000..cd7b06a --- /dev/null +++ b/utils/roq2/libim/im.h @@ -0,0 +1,1526 @@ +/** + ** $Header: /sdsc/dev/vis/image/imtools/v3.0/libim/src/include/RCS/im.h,v 1.48 1995/06/30 22:13:07 bduggan Exp $ + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + ** a division of General Atomics, San Diego, California, USA + ** + ** Users and possessors of this source code are hereby granted a + ** nonexclusive, royalty-free copyright and design patent license to + ** use this code in individual software. License is not granted for + ** commercial resale, in whole or in part, without prior written + ** permission from SDSC. This source is provided "AS IS" without express + ** or implied warranty of any kind. + ** + ** For further information contact: + ** E-Mail: info@sds.sdsc.edu + ** + ** Surface Mail: Information Center + ** San Diego Supercomputer Center + ** P.O. Box 85608 + ** San Diego, CA 92138-5608 + ** (619) 534-5000 + **/ + +/** + ** FILE + ** im.h - Image library include file + ** + ** PROJECT + ** libim - SDSC image manipulation library + ** + ** DESCRIPTION + ** im.h contains the structs, typedefs, externs, defines, and macros + ** needed to use the VFB, CLT, and File subpackages of the image + ** library. + ** + ** PUBLIC CONTENTS + ** d =defined constant + ** f =function + ** m =defined macro + ** t =typedef/struct/union + ** v =variable + ** ? =other + ** + ** __IMH__ d file inclusion flag + ** + ** ImErrNo v error number + ** ImNErr v number of error messages + ** ImErrList v error messages + ** IME... d error codes + ** + ** IMVFBNULL d NULL VFB + ** IMCLTNULL d NULL CLT + ** IMVFBNEW d Indicate a new VFB is to be allocated + ** IMCLTNEW d Indicate a new CLT is to be allocated + ** + ** ImCltPtr t pointer to CLT entry + ** ImClt t color lookup table + ** ImVfbPtr t pointer to VFB pixel + ** ImVfb t virtual frame buffer + ** ImHotSpot t image hotspot + ** + ** ImVfb... m set and query VFB attributes + ** ImClt... m Set and query CLT attributes + ** ImHotSpot... m set and query HotSpot attributes + ** + ** IMVFB... d fields mask values + ** IMVFB... d raster operations and inout values + ** IMVFB... d flip directions + ** IMVFB... d resolution change algorithms + ** + ** IMTYPEINDEX d Indexed image type + ** IMTYPERGB d RGB image type + ** IMTYPE2D d 2D primitives + ** IMCOMP... d Compression options + ** IMINTER... d Interlace options + ** IMCLT... d CLT options + ** IMALPHA... d Alpha plane options + ** + ** ImFileFormat t file format info + ** + ** ImFileFormatReadMap t map of how a format handles reads + ** ImFileFormatWriteMap t map of how a format handles writes + ** + ** Im... f Generic function declarations + ** ImVfb... f VFB function declarations + ** ImClt... f CLT function declarations + ** ImFile... f File I/O function declarations + ** ImGetFileFormats f Get the list of formats + ** + ** IMFILE... d Image file format-specific options + ** + ** IMERRORFATAL d fatal error + ** IMERRORWARNING d warning error + ** IMERRORINFO d information error + ** + ** IMMAXNAME d maximum name size + ** IMDEFMONOTHRESH d default monochrome threshold + ** + ** IMMAXIMUM d computed maximum field value + ** IMMINIMUM d computed minimum field value + ** IMUNIQUEVAL d computed number of unique field values + ** IMMAXNUMSTATS d number of statistics returned by ImVfbStat + ** + ** ImHistTable t histogram information table + ** IMHIST... d histogram table array index names + ** imhist_... d histogram table union field names + ** + ** PRIVATE CONTENTS + ** none + ** + ** HISTORY + ** $Log: im.h,v $ + ** Revision 1.48 1995/06/30 22:13:07 bduggan + ** added ImVfbFSDitherToMono non-ansi prototype + ** + ** Revision 1.47 1995/06/29 00:30:40 bduggan + ** Added IMVFBPRINTINTFLAOT + ** oops, i mean FLOAT + ** + ** Revision 1.46 1995/06/16 07:16:09 bduggan + ** removed IMFILECOMPZ + ** + ** Revision 1.45 1995/06/16 06:58:45 bduggan + ** added imvfbcopychannel + ** + ** Revision 1.44 1995/06/16 06:47:00 bduggan + ** Added compression support. + ** Removed global ImFileFormats variable. + ** Added ANSI declarations for pointers to functions. + ** + ** Revision 1.43 1995/05/18 00:09:51 bduggan + ** Added IMCOMPASCII, ImVfbPrint, ImGetTransparency, + ** IMFILECOMPZ, + ** ImVfbSameRGB, ImVfbSameRGBA, (etc.), + ** IMVFBPRINTFLOAT, IMVFBPRINTINT + ** + ** Revision 1.42 1995/04/03 22:11:26 bduggan + ** added support for channel mapping requests, + ** image quality requests (for jpeg compression), + ** and took out #ifdef NEWMAGIC + ** + ** Revision 1.41 1995/01/18 21:54:21 bduggan + ** Added GROUP macros and FSDitherToMono prototype + ** + ** Revision 1.40 1994/10/03 16:01:28 nadeau + ** Updated to ANSI C and C++ compatibility by adding function prototypes. + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + ** Changed all float arguments to double. + ** Rearranged magic number structures for format handlers. + ** Added definitions for IMMULTI, IMNOMULTI, IMPIPE, & IMNOPIPE. + ** Updated copyright message. + ** + ** Revision 1.39 93/03/18 12:29:28 todd + ** added extern ImVfb *ImVfbIndex() to quiet the compiler + ** + ** Revision 1.38 93/03/09 10:44:58 nadeau + ** Corrected non-ANSI-ism of an #endif comment. + ** + ** Revision 1.37 92/12/03 02:19:54 nadeau + ** Updated histogram information and function declarations. + ** + ** Revision 1.36 92/11/06 17:00:35 groening + ** change ImFIleFormats struct. + ** + ** Revision 1.35 92/10/06 13:44:42 nadeau + ** Added function declarations. + ** + ** Revision 1.34 92/09/04 18:35:45 groening + ** Added IMEIMPSHEAR error. + ** + ** Revision 1.33 92/08/31 14:49:26 nadeau + ** Added additional function declarations. Moved things around + ** a bit. Added comments to hotspot declarations. Added additiona + ** error codes. Changed names on some error codes. + ** + ** Revision 1.32 92/08/28 15:29:14 vle + ** Added hotspot support. + ** + ** Revision 1.31 92/08/18 13:08:21 groening + ** added error measges and stuff for imhist.c + ** plus #defines for operations, etc. + ** + ** Revision 1.30 92/06/06 13:36:52 vle + ** Fixed type problem with macros. + ** + ** Revision 1.29 92/04/15 12:23:56 nadeau + ** Added more extern declarations. + ** + ** Revision 1.28 91/10/03 12:56:07 nadeau + ** Removed out-of-date PBM and FPS flags. + ** + ** Revision 1.27 91/10/03 12:54:09 nadeau + ** Cosmetic changes. Added read and write map support. + ** + ** Revision 1.26 91/01/23 12:50:34 doeringd + ** Added prefix "IMFPS" to FPS enum list items. + ** + ** Revision 1.25 91/01/23 12:04:19 doeringd + ** Removed unneeded enum name "aperture" to prevent conflict with + ** int aperture. + ** + ** Revision 1.24 91/01/23 11:06:08 nadeau + ** Removed redundant aperture declaration. + ** + ** Revision 1.23 91/01/23 10:44:00 doeringd + ** Add fps enum definitions for apertures. + ** + ** Revision 1.22 91/01/23 10:27:08 nadeau + ** Added multiVFB flag to ImFileFormats struct. + ** + ** Revision 1.21 91/01/20 07:43:55 doeringd + ** Included imfps.h aperture enum values. + ** + ** Revision 1.20 91/01/16 10:30:04 doeringd + ** Add PBM defines + ** + ** Revision 1.19 91/01/16 10:13:27 nadeau + ** Added declarations for ImVfbIsMono() and ImVfbTo8Mono(). + ** + ** Revision 1.18 90/09/04 14:03:44 ferrerj + ** added ImVfbCopyArea to function list + ** + ** Revision 1.17 90/09/04 13:57:10 ferrerj + ** added ImVfbCopyArea() to function list + ** + ** Revision 1.17 90/09/04 02:00:00 ferrerj + ** added ImVfbCopyArea() to function list + ** + ** Revision 1.16 90/07/26 11:45:09 mcleodj + ** Added Pixar PIC format flag values + ** + ** Revision 1.15 90/07/25 16:25:14 nadeau + ** Changed RAS file format flag values and their names. + ** + ** Revision 1.14 90/07/25 13:34:05 todd + ** added several new IME* errors. + ** added IMTIFF flag values + ** + ** Revision 1.13 90/07/23 13:51:14 nadeau + ** Fixed Index16 macros to use vfb_i32off instead of vfb_ioff. Added HDF + ** write flags. Removed PIX write flag. + ** + ** Revision 1.12 90/07/15 09:30:28 mjb + ** added ImVfbCut() and ImvfbLightness() to function list + ** + ** Revision 1.11 90/07/02 15:48:25 mercurio + ** Error messages added + ** + ** Revision 1.10 90/06/28 20:14:29 mercurio + ** Added IMENOIMAGE and IMEUNSUPPORTED error codes + ** + ** Revision 1.9 90/06/28 15:16:00 nadeau + ** Removed tag table types and function declarations. Added #defines to + ** map from the old tag table function names to the new ones. Added + ** declarations and #defines for the error handling code. + ** + ** Revision 1.8 90/05/15 13:57:56 todd + ** Move IMFILEFD & FP PIPE and FILE macros to new iminternal.h file + ** + ** Revision 1.7 90/05/11 14:26:34 nadeau + ** Removed old IMFILE format defines. + ** + ** Revision 1.6 90/05/11 10:02:47 nadeau + ** Added ImFileFormat struct for file format information. Left old way + ** in still. + ** + ** Revision 1.5 90/05/02 11:13:25 mjb + ** More of the above + ** + ** Revision 1.4 90/05/02 07:56:57 mjb + ** moreof the above + ** + ** Revision 1.3 90/05/02 07:51:53 mjb + ** Changed ImVfb and ImClt typedefs to be structures, not pointers + ** This will require declarations of the form: + ** + ** ImClt *srcClt; + ** + ** Revision 1.2 90/04/30 10:28:54 nadeau + ** Changed tag list stuff to tag table stuff. Added error codes and + ** renumbered the ones already there. Updated the function declarations + ** and fixed various comments. Added file format flag defines. + ** + ** Revision 1.1 90/03/28 11:17:57 nadeau + ** Initial revision + ** + **/ + + +#ifndef __IMH__ +#define __IMH__ + +#ifndef __SDSCH__ +//#include "sdsc.h" +#endif /* __SDSCH__ */ + + + +/* + * GLOBAL VARIABLE + * ImErrNo - error number + * ImNErr - number of error messages + * ImErrList - error messages + * + * DESCRIPTION + * On an error, the image library routines return -1, or NULL, and set + * ImErrNo to an error code. The programmer may call ImPError + * to print the associated error message to stderr, or may do the + * message lookup in ImErrList directly. + */ + +extern int ImErrNo; /* Current error number */ +extern int ImNErr; /* Number of error messages */ +extern char *ImErrList[]; /* List of error messages */ + + + + + +/* + * CONSTANTS + * IME... - error codes + * + * DESCRIPTION + * ImErrNo may be set to these error codes as a result of an error in + * calling one of the image library routines. + */ + +#define IMESYS 0 /* System error */ +#define IMEMALLOC 1 /* Cannot allocate */ + +#define IMENOCONTENTS 2 /* Bad fields mask */ +#define IMENOFPDATA 3 /* Bad float data */ +#define IMEBADINOUT 4 /* Bad inout arg */ +#define IMEBADFBTYPE 5 /* Bad frame buffer type */ +#define IMEFORMAT 6 /* Bad format */ +#define IMENOFILE 7 /* No file? */ +#define IMENOTINFO 8 /* Not enough info in VFB */ +#define IMEBADFLIP 9 /* Bad flip selection */ +#define IMEBADALGORITHM 10 /* Bad res change algorithm */ + +#define IMENOREAD 11 /* Read not supported on format */ +#define IMENOWRITE 12 /* Write not supported on format*/ +#define IMENOVFB 13 /* No VFB given for image write */ +#define IMEMANYVFB 14 /* Too many VFB's for image write*/ +#define IMENOTRGB 15 /* VFB isn't an RGB image */ +#define IMEMAGIC 16 /* Bad magic number on image file*/ +#define IMEIMAGETYPE 17 /* Unknown image type */ +#define IMECLTTYPE 18 /* Unknown CLT type */ +#define IMEDEPTH 19 /* Unknown image depth */ +#define IMECLTLENGTH 20 /* Bad CLT length */ +#define IMENOCLT 21 /* No CLT given for image */ +#define IMEMANYCLT 22 /* Too many CLT's for image */ +#define IMEUNKNOWN 23 /* VFB isn't a GRAY image */ + +#define IMENULLTAGTABLE 24 /* NULL tag table given */ +#define IMESYNTAX 25 /* Syntax error */ +#define IMENOIMAGE 26 /* No images in input file */ +#define IMEUNSUPPORTED 27 /* Unsupported VFB type */ +#define IMEDIFFSIZE 28 /* VFB's have different sizes */ +#define IMEVERSION 29 /* Bad version number in header */ +#define IMEPLANES 30 /* Unknown image plane config */ +#define IMEOUTOFRANGE 31 /* Header value out of legal range */ +#define IMEORIENTATION 32 /* Unsupported image orientation*/ +#define IMEWIDTH 33 /* Zero or negative image width */ +#define IMEHEIGHT 34 /* Zero or negative image height*/ +#define IMECONFLICT 35 /* Conflicting info in im header*/ +#define IMENOTINDEX8 36 /* Not an Index 8 image */ +#define IMENOTINDEX16 37 /* Not an Index 16 image */ +#define IMENOTMONO 38 /* Not a monochrome image */ +#define IMEFIELD 39 /* Bad field mask */ +#define IMENOTPOSSIBLE 40 /* Conversion/Output not possible*/ +#define IMEDECODING 41 /* Error while decoding image */ +#define IMEENCODING 42 /* Error while encoding image */ +#define IMEOPERATION 43 /* Unknown operation */ +#define IMEGRADUATION 44 /* Unknown graduation */ +#define IMEHISTMAX 45 /* Histogram maximum exceeded */ +#define IMEBADRANGE 46 /* Value range inappropriate */ +#define IMEKEY 47 /* invalid key parameter passed */ +#define IMEADJUST 48 /* invalid adjust parameter passed */ +#define IMENOALPHA 49 /* no alpha present when needed */ +#define IMEPIXELREP 50 /* not exact multiples for pixel rep */ +#define IMEKEYFIELD 51 /* Bad keyField on adjust */ +#define IMEADJUSTFIELD 52 /* Bad adjustField on adjust */ +#define IMEIMPSHEAR 53 /* shear of 90 degrees encountered */ +#define IMEBADCHANNEL 54 /* Bad channel map request */ +#define IMEBADCHANNELS 55 /* Invalid channel map requests */ +#define IMEVALOUTOFRANGE 56 /* Value out of range */ +#define IMEFORMATUNKNOWN 57 /* Couldn't figure out format */ + /* 58-2^32-1 Reserved */ + + + + + +/* + * CONSTANTS + * IMVFBNULL - NULL VFB + * IMCLTNULL - NULL CLT + * IMVFBNEW - Indicate a new VFB is to be allocated + * IMCLTNEW - Indicate a new CLT is to be allocated + * + * DESCRIPTION + * These values are each used when a NULL (empty) VFB, or CLT is to be + * indicated. These values may be passed as the destination arguments + * to VFB, and CLT, and may be returned as error condition values from + * functions that normally return VFB's, or CLT's. + */ + +#define IMVFBNULL ((ImVfb *)0) +#define IMVFBNEW IMVFBNULL +#define IMCLTNULL ((ImClt *)0) +#define IMCLTNEW IMCLTNULL + + + + + +/* + * TYPEDEF & STRUCTURE + * ImCltPtr - pointer to CLT entry + * ImClt - color lookup table + * + * DESCRIPTION + * The ImClt struct contains a color lookup table. Such a CLT may, + * or may not, be referenced by a VFB. + * + * clt_ncolors is the number of entries in the CLT. Each entry + * consists of 3 consecutive 8-bit bytes for red, green, and blue. + * + * clt_clt is a pointer to the first byte of the first entry in the + * CLT. + */ + +typedef unsigned char *ImCltPtr; /* Color pointer */ + +typedef struct ImClt +{ + int clt_ncolors; /* # colors in CLT storage */ + ImCltPtr clt_clt; /* points to actual CLT storage */ +} ImClt; + + + + + +/* + * TYPEDEF & STRUCTURE + * ImVfbPtr - pointer to VFB pixel + * ImVfb - virtual frame buffer + * + * DESCRIPTION + * The ImVfb struct contains a virtual frame buffer (an image and its + * associated per-pixel data). + * + * vfb_width and vfb_height give the width and height of the VFB. + * + * vfb_fields is a mask that indicates what types of values are + * associated with each pixel in the VFB. + * + * vfb_nbytes is the number of bytes per pixel. + * + * vfb_clt is an optional CLT associated with the VFB. vfb_clt will + * be IMCLTNULL if there is no CLT. + * + * vfb_roff, vfb_goff, and vfb_boff are byte offsets to reach the + * red, green, and blue planes (if any) of the VFB. + * + * vfb_aoff is a byte offset to the alpha plane (if any) of the VFB. + * + * vfb_i8off, and vfb_i16off are byte offsets to the 8-bit or 16-bit + * color index planes (if any) of the VFB. + * + * vfb_wpoff is a byte offset to the write protect plane (if any) of the + * VFB. + * + * vfb_zoff is a byte offset to the Z-buffer plane (if any) of the VFB. + * + * vfb_fpoff and vfb_ioff are a byte offsets to the floating point and + * integer data planes (if any) of the VFB. + * + * vfb_pfirst and vfb_plast point to the first and last pixels in the + * VFB. + */ + +typedef unsigned char *ImVfbPtr; /* Pixel pointer */ + +typedef struct ImVfb +{ + int vfb_width; /* # cols */ + int vfb_height; /* # rows */ + int vfb_fields; /* mask specifying what is in each pixel*/ + int vfb_nbytes; /* # bytes per pixel */ + ImClt *vfb_clt; /* points to attached CLT, if any */ + int vfb_roff; /* offset (in bytes) to reach red */ + int vfb_goff; /* green */ + int vfb_boff; /* blue */ + int vfb_aoff; /* alpha-value */ + int vfb_i8off; /* color index */ + int vfb_wpoff; /* write protect offset */ + int vfb_i16off; /* color index */ + int vfb_zoff; /* z-value */ + int vfb_moff; /* mono */ + int vfb_fpoff; /* floating point */ + int vfb_ioff; /* integer */ + ImVfbPtr vfb_pfirst; /* points to first pixel */ + ImVfbPtr vfb_plast; /* points to last pixel */ +} ImVfb; + + + + + + +/* + * CONSTANTS + * IMVFB... - fields mask values + * + * DESCRIPTION + * The fields mask of a VFB is the bitwise OR of one or more of + * these mask values. Each value indicates that data of that type + * is to be stored in the VFB at each pixel location. + */ + +#define IMVFBALL ( ~0 ) /* Everything */ + +#define IMVFBIMAGEMASK ( 0xFF ) /* Image items */ +#define IMVFBMONO ( 1 << 0 ) +#define IMVFBINDEX8 ( 1 << 1 ) +#define IMVFBGRAY IMVFBINDEX8 +#define IMVFBGREY IMVFBINDEX8 +#define IMVFBINDEX16 ( 1 << 2 ) +#define IMVFBRGB ( 1 << 3 ) + /* 4-7 Reserved */ + +#define IMVFBOTHERMASK ( ~0xFF ) /* Non-image items */ +#define IMVFBALPHA ( 1 << 9 ) +#define IMVFBWPROT ( 1 << 10 ) +#define IMVFBZ ( 1 << 11 ) +#define IMVFBFDATA ( 1 << 12 ) +#define IMVFBIDATA ( 1 << 13 ) +#define IMVFBRED ( 1 << 14 ) +#define IMVFBGREEN ( 1 << 15 ) +#define IMVFBBLUE ( 1 << 16 ) + /* 17-31 Reserved */ + + + + + +/* + * CONSTANTS + * IM... - field specifiers + * + * DESCRIPTION + * These are used to pass fields around for adjustment, fill, and other + * tools capable of working in color spaces beyond those actually + * stored in the VFB (such as hue, saturation, and intensity). + * + * NOTE! These are not valid for passing to routines, like ImVfbAlloc, + * to specify actual fields in a VFB! Use the IMVFB* variants instead. + */ +#define IMMONO IMVFBMONO +#define IMGRAY IMVFBGRAY +#define IMGREY IMVFBGREY +#define IMINDEX8 IMVFBINDEX8 +#define IMINDEX16 IMVFBINDEX16 +#define IMRGB IMVFBRGB +#define IMALPHA IMVFBALPHA +#define IMWPROT IMVFBWPROT +#define IMZ IMVFBZ +#define IMFDATA IMVFBFDATA +#define IMIDATA IMVFBIDATA + +#define IMRED IMVFBRED +#define IMGREEN IMVFBGREEN +#define IMBLUE IMVFBBLUE +#define IMSATURATION ( 1 << 28 ) +#define IMINTENSITY ( 1 << 29 ) +#define IMHUE ( 1 << 30 ) + /* 1-16 should be equal to equivalents to IMVFB* + 17-27 are reserved */ + +/* + * CONSTANTS + * IM... - operations + * + * DESCRIPTION + * These are used to pass operations around + */ +#define IMADD ( 1 << 0 ) +#define IMSUBTRACT ( 1 << 1 ) +#define IMMULTIPLY ( 1 << 2 ) +#define IMDIVIDE ( 1 << 3 ) +#define IMSET ( 1 << 4 ) + /* 5-8 Reserved */ + +#define IMCOMPOVER ( 1 << 9 ) +#define IMCOMPINSIDE ( 1 << 10 ) +#define IMCOMPOUTSIDE ( 1 << 11 ) +#define IMCOMPATOP ( 1 << 12 ) + /* 13-32 Reserved */ + + + + + +/* + * MACROS + * ImVfb... - set and query VFB attributes + * + * DESCRIPTION + * These macros, masquerading as subroutines, take a VFB pointer and + * set or query information from it. + */ + +/* CLT and Characteristics. */ +#define ImVfbQWidth( v ) ((v)->vfb_width) +#define ImVfbQHeight( v ) ((v)->vfb_height) +#define ImVfbQFields( v ) ((v)->vfb_fields) +#define ImVfbQNBytes( v ) ((v)->vfb_nbytes) +#define ImVfbQClt( v ) ((v)->vfb_clt) + +#define ImVfbSClt( v, c ) ((v)->vfb_clt = (c)) + + +/* Pixel values. */ +#define ImVfbQMono( v, p ) ( (int) ( *( (p)+(v)->vfb_moff ) ) ) +#define ImVfbQIndex8( v, p ) ( (int) ( *( (p)+(v)->vfb_i8off ) ) ) +#define ImVfbQGrey( v, p ) ImVfbQIndex8(v,p) +#define ImVfbQGray( v, p ) ImVfbQIndex8(v,p) +#define ImVfbQIndex16( v, p ) ( *((sdsc_uint16 *)((p)+(v)->vfb_i16off ) ) ) +#define ImVfbQIndex( v, p ) \ + ( ( ImVfbQFields(v)&IMVFBINDEX8 ) ? ImVfbQIndex8(v,p) :\ + ImVfbQIndex16(v,p) ) +#define ImVfbQRed( v, p ) ( (int) ( *( (p)+(v)->vfb_roff ) ) ) +#define ImVfbQGreen( v, p ) ( (int) ( *( (p)+(v)->vfb_goff ) ) ) +#define ImVfbQBlue( v, p ) ( (int) ( *( (p)+(v)->vfb_boff ) ) ) + +#define ImVfbQAlpha( v, p ) ( (int) ( *( (p)+(v)->vfb_aoff ) ) ) +#define ImVfbQWProt( v, p ) ( (int) ( *( (p)+(v)->vfb_wpoff) ) ) +#define ImVfbQZ( v, p ) ( *((int *)((p)+(v)->vfb_zoff ) ) ) +#define ImVfbQFData( v, p ) ( *((float *)( (p)+(v)->vfb_fpoff ) ) ) +#define ImVfbQIData( v, p ) ( *((int *)( (p)+(v)->vfb_ioff ) ) ) + + +#define ImVfbSMono( v, p, m ) ( *( (p)+(v)->vfb_moff ) = (unsigned char)(m) ) +#define ImVfbSGray( v, p, g ) ImVfbSIndex8(v,p,g) +#define ImVfbSGrey( v, p, g ) ImVfbSIndex8(v,p,g) +#define ImVfbSIndex8( v, p, i ) ( *( (p)+(v)->vfb_i8off ) = (unsigned char)(i) ) +#define ImVfbSIndex16( v, p, i ) \ + ( *((sdsc_uint16 *)((p)+(v)->vfb_i16off) ) = (unsigned short)(i) ) +#define ImVfbSIndex( v, p, i ) \ + ( ( ImVfbQFields(v)&IMVFBINDEX8 ) ? ImVfbSIndex8(v,p,i) :\ + ImVfbSIndex16(v,p,i) ) +#define ImVfbSRed( v, p, r ) ( *( (p)+(v)->vfb_roff ) = (unsigned char)(r) ) +#define ImVfbSGreen( v, p, g ) ( *( (p)+(v)->vfb_goff ) = (unsigned char)(g) ) +#define ImVfbSBlue( v, p, b ) ( *( (p)+(v)->vfb_boff ) = (unsigned char)(b) ) + +#define ImVfbSAlpha( v, p, a ) ( *( (p)+(v)->vfb_aoff ) = (unsigned char)(a) ) +#define ImVfbSWProt( v, p, wp ) ( *( (p)+(v)->vfb_wpoff) = (unsigned char)(wp)) +#define ImVfbSZ( v, p, z ) ( *((int *)((p)+(v)->vfb_zoff) ) = (int)(z) ) +#define ImVfbSFData( v, p, fp ) ( *((float *)((p)+(v)->vfb_fpoff) )=(float)(fp)) +#define ImVfbSIData( v, p, i ) ( *((int *)((p)+(v)->vfb_ioff) ) = (int)(i)) + + +/* Pixel addressing. */ +#define ImVfbQFirst( v ) ( (v)->vfb_pfirst ) +#define ImVfbQLast( v ) ( (v)->vfb_plast ) +#define ImVfbQPtr( v, x, y ) ( ImVfbQFirst(v) + \ + ImVfbQNBytes(v)*( (y)*ImVfbQWidth(v) + \ + (x) ) ) +#define ImVfbQNext( v, p ) ( (p) + ImVfbQNBytes(v) ) +#define ImVfbQPrev( v, p ) ( (p) - ImVfbQNBytes(v) ) +#define ImVfbQLeft( v, p ) ImVfbQPrev(v,p) +#define ImVfbQRight( v, p ) ImVfbQNext(v,p) +#define ImVfbQUp( v, p ) ( (p) - ImVfbQWidth(v)*ImVfbQNBytes(v) ) +#define ImVfbQDown( v, p ) ( (p) + ImVfbQWidth(v)*ImVfbQNBytes(v) ) + +#define ImVfbSInc( v, p ) ( (p) += ImVfbQNBytes(v) ) +#define ImVfbSDec( v, p ) ( (p) -= ImVfbQNBytes(v) ) +#define ImVfbSLeft( v, p ) ImVfbSDec( v, p ) +#define ImVfbSRight( v, p ) ImVfbSInc( v, p ) +#define ImVfbSPrev( v, p ) ImVfbSDec( v, p ) +#define ImVfbSNext( v, p ) ImVfbSInc( v, p ) +#define ImVfbSUp( v, p ) ( (p) -= ImVfbQWidth(v)*ImVfbQNBytes(v) ) +#define ImVfbSDown( v, p ) ( (p) += ImVfbQWidth(v)*ImVfbQNBytes(v) ) + +#define ImVfbSameRGB(vfb, one, two) \ + ((ImVfbQRed(vfb, one)==ImVfbQRed(vfb,two)) && \ + (ImVfbQGreen(vfb, one)==ImVfbQGreen(vfb, two)) && \ + (ImVfbQBlue(vfb, one)==ImVfbQBlue(vfb, two)) ) + +#define ImVfbSameRGBA(vfb, one, two) \ + ((ImVfbQRed(vfb, one)==ImVfbQRed(vfb,two)) && \ + (ImVfbQGreen(vfb, one)==ImVfbQGreen(vfb, two)) && \ + (ImVfbQAlpha(vfb, one)==ImVfbQAlpha(vfb, two)) && \ + (ImVfbQBlue(vfb, one)==ImVfbQBlue(vfb, two)) ) + +#define ImVfbSameIndex8(vfb, one, two) \ + (ImVfbQIndex8(vfb, one)==ImVfbQIndex8(vfb,two)) + +#define ImVfbSameIndex16(vfb, one, two) \ + (ImVfbQIndex16(vfb, one)==ImVfbQIndex16(vfb,two)) + + + + + +/* + * MACROS + * ImClt... - Set and query CLT attributes + * + * DESCRIPTION + * These macros, masquerading as subroutines, take a CLT pointer and + * set or retrieve information from it. + */ + +/* Characteristics. */ +#define ImCltQNColors(c) ( (c)->clt_ncolors ) + + +/* Entry values. */ +#define ImCltQRed( p ) ( (int) ( *((p)+0) ) ) +#define ImCltQGreen( p ) ( (int) ( *((p)+1) ) ) +#define ImCltQBlue( p ) ( (int) ( *((p)+2) ) ) + +#define ImCltSRed( p, r ) ( *((p)+0) = (unsigned char)(r) ) +#define ImCltSGreen( p, g ) ( *((p)+1) = (unsigned char)(g) ) +#define ImCltSBlue( p, b ) ( *((p)+2) = (unsigned char)(b) ) + + +/* Entry addressing. */ +#define ImCltQFirst( c ) ( (c)->clt_clt ) +#define ImCltQPtr( c, i ) ( ImCltQFirst(c) + (3 * (i)) ) +#define ImCltQLast( c ) ( ImCltQFirst(c) + \ + ( 3*( ImCltQNColors(c) - 1 ) ) ) +#define ImCltQNext( c, p ) ( (p) + 3 ) +#define ImCltQPrev( c, p ) ( (p) - 3 ) + +#define ImCltSInc( c, p ) ( (p) += 3 ) +#define ImCltSDec( c, p ) ( (p) -= 3 ) + + + + + + +/* + * TYPEDEF & STRUCT + * ImFileFormatReadMap - map of how a format handles reads + * ImFileFormatWriteMap - map of how a format handles writes + * + * DESCRIPTION + * These two tables are essentially mirror images of each other. The + * read map tells what depths of imagery can be read from a format and + * how it maps to VFB's. The write map tells how different VFB's map + * to file depths. + * + * map_inType and map_outType select whether the image is represented + * as a CLT index per pixel (pseudo-color) or with the full RGB color + * per pixel (true-color). + * + * map_inNChannels and map_outNChannels give the number of channels per + * pixel (usually 1 or 3). + * + * map_inChannelDepth and map_outChannelDepth give the size of the + * channels, in bits. Usually 1-bit (mono) through 8-bit. + * + * map_inAttributes and map_outAttributes select the attributes of + * an incomming or outgoing file image, such as whether it has a CLT, + * alpha planes, or is compressed somehow. For read, the _outAttributes + * may not contain any compression indications. Likewise for write, the + * _inAttributes may not contain compression stuff. + * + * map_inField and map_outField select the type of VFB (as a VFB + * field mask) that is created by the read or taken by the write. Only + * image depth type field values should be included (ie., don't add + * IMVFBALPHA, etc). + * + * map_write is a function pointer to the write function to handle + * this specific output mapping. There is no equivalent read for the + * read map. + */ + +typedef struct ImFileFormatReadMap +{ + int map_inType; /* In image type (index | rgb) */ + int map_inNChannels; /* In # of channels */ + int map_inChannelDepth; /* In channel size (in bits) */ + int map_inAttributes; /* In image attributes */ + + int map_outField; /* Out VFB field */ + int map_outAttributes; /* Out VFB attributes */ +} ImFileFormatReadMap; + +typedef struct ImFileFormatWriteMap ImFileFormatWriteMap; + +struct ImFileFormatWriteMap +{ + int map_inField; /* In VFB field */ + int map_inAttributes; /* In VFB attributes */ + + int map_outType; /* Out image type (index | rgb) */ + int map_outNChannels; /* Out # of channels */ + int map_outChannelDepth; /* Out channel size (in bits) */ + int map_outAttributes; /* Out image attributes */ + +#ifdef __STDC__ + int (*map_write)( + ImFileFormatWriteMap * , + int , int , FILE* , + TagTable* , TagTable* );/* Out write function */ +#else + int (*map_write)( ); /* Out write function */ +#endif +}; + + + + + +/* + * CONSTANTS + * IMTYPEINDEX - Indexed image type + * IMTYPERGB - RGB image type + * IMTYPE2D - 2D primitives + * IMCOMP* - Compression options + * IMINTER* - Interlace options + * IMCLT* - CLT options + * IMALPHA* - Alpha plane options + * IMGROUP* - Grouping options + * IMFILECOMP* - File compression option(s) + * + * DESCRIPTION + * All of these are values used in initializing fields of the read + * and write mapping structures for the various formats. They may also + * be used as user request values for flagsTable flags: + * + * "image type request", int type = IMTYPEINDEX or IMTYPERGB + * "image channel number request", int nchannels = 1 or 3 usually + * "image channel depth request", int depth = 1-32 + * "image compression request", int comp = IMCOMP* + * "image interleave request", int inter = IMINTER* + * "image clt request", int clt = IMCLT* + * "image alpha request", int alpha = IMALPHA* + * "image group request", int group = IMGROUP* + * "image quality request" int quality = 1..100 usually + * "file compression" int type = 1 + * + */ + +#define IMTYPEINDEX (0) /* CLT index per pixel */ +#define IMTYPERGB (1) /* RGB color per pixel */ +#define IMTYPE2D (2) /* 2D primitives */ + +#define IMCOMPMASK 0x0F +#define IMCOMPNO (0<<0) /* No compression */ +#define IMCOMPNONE (0<<0) /* No compression */ +#define IMCOMPRLE (1<<0) /* Run-Length Encoded compression*/ +#define IMCOMPLZW (2<<0) /* Limpel-Ziv Welsh compression */ +#define IMCOMPPACKBITS (3<<0) /* Packbits compression */ +#define IMCOMPPB (3<<0) /* Packbits compression */ +#define IMCOMPMAC (3<<0) /* Packbits compression */ +#define IMCOMPDCT (4<<0) /* Descrete Cosine Transform */ +#define IMCOMPASCII (5<<0) /* Descrete Cosine Transform */ + /* (6<<0) - (15<<0) Reserved */ + +#define IMGROUPMASK 0xF0 +#define IMGROUPSCANLINES (0<<4) /* Group into scanlines(default)*/ +#define IMGROUPTILES (1<<4) /* Group into tiles */ + /* (2<<4) - (15<<4) Reserved */ + +#define IMINTERMASK 0xF00 +#define IMINTERNO (0<<8) /* Non-interleave */ +#define IMINTERNONE (0<<8) /* Non-interleave */ +#define IMINTERLINE (1<<8) /* Scanline interleave */ +#define IMINTERPLANE (2<<8) /* Plane interleave */ + /* (3<<8) - (15<<8) Reserved */ + +#define IMCLTMASK 0xF000 +#define IMCLTNO (0<<12) /* No CLT included */ +#define IMCLTNONE (0<<12) /* No CLT included */ +#define IMCLTNODUMP (0<<12) /* No CLT included */ +#define IMCLTYES (1<<12) /* CLT included */ +#define IMCLTDUMP (2<<12) /* CLT included */ + /* (3<<12) - (15<<12) Reserved */ + +#define IMALPHAMASK 0xF0000 +#define IMALPHANO (0<<16) /* No Alpha planes included */ +#define IMALPHANONE (0<<16) /* No Alpha planes included */ +#define IMALPHANODUMP (0<<16) /* No Alpha planes included */ +#define IMALPHAYES (1<<16) /* Alpha planes included */ +#define IMALPHADUMP (1<<16) /* Alpha planes included */ + /* (2<<16) - (15<<16) Reserved */ + +#define IMQUALITYMASK 0xF00000 +#define IMQUALITYNO (0<<20) /* No quality control allowed */ +#define IMQUALITYYES (1<<20) /* Optional quality control */ + /* (2<<20) - (15<<20) Reserved */ + + /* (1<<24) - (1<<31) Reserved */ + + + + +/* + * TYPEDEF & STRUCT + * ImFileMagic - magic number info + * ImFileFormat - file format info + * + * DESCRIPTION + * The imFileFormat struct describes everything we know generically + * about the set of file formats supported by the image library. + * + * format_names is a list of the names we know the format by. The + * first name in the list is its "primary" name (the name we like most). + * The rest are "equivalent" names. Names are used as command-line + * options in image tools, and as possible file extensions. The list + * is terminated with a NULL. Since all name checking is case-insensitive, + * only list things in lower case. + * + * format_help is a help string that explains what the format is, in + * 40 characters or less. For instance, "ras" would have "Sun rasterfile" + * as its help string. "pix" would have "Alias picture file" as its + * help string. Image library tools will use this as the help string + * for the option keyword in their argument parsing code. + * + * format_creator is a string indicating who created and/or supports + * this format. "rgb" would read "Silicon Graphics". "ras" would read + * "Sun Microsystems, Inc.". + * + * format_readSupport and format_writeSupport are one-or-more line strings + * describing the variants of the format that we support. The real + * detail specifics are left to the read and write maps (later), but these + * strings say the same thing in more human terms. The strings are used + * by image tools to list what can be supported. + * + * The struct ImFileMagic contains: + * format_magicLocation, _magicLength, and _magicNumber give information + * about the format's magic number, if any. The location is the # of + * bytes from the start of the file at which the magic number is found. + * This is almost always 0. The length is the # of bytes that constitute + * the magic number, and is usually 4. If this value is 0, there is no + * magic number for this format. The magic number itself is a string of + * bytes that must match the number in the file. Image library tools + * will typicaly check this against the file in both MBF and LBF order. + * + * format_readMultiVfb and format_writeMultiVfb are booleans indicating + * if the format can read in, and write out multiple images for the same + * file. + * + * format_readPipe and format_writePipe are booleans indicating if + * the format can handle a pipe. If they can't, the generic code will + * make a temp file for the format code. + * + * format_read is a function pointer to the top level read routine for + * the format. All format read requests enter here. + * + * format_readMap describes in detail the different file variants that + * can be handled and what kind of VFB they map to. + * + * format_writeMap describes in detail the different file variants that + * can be written and what kind of VFB they accept. The map also has + * function pointers to specific format write routines to handle those + * variants. + */ + +typedef struct ImFileMagic +{ + int format_magicLocation; /* Location of magic # */ + int format_magicLength; /* # bytes in magic # */ + unsigned char *format_magicNumber; /* Magic # */ +} ImFileMagic; + +typedef struct ImFileFormat +{ + char **format_names; /* Format name string */ + char *format_help; /* Long form of name */ + char *format_creator; /* Creator/source of format */ + char *format_readSupport; /* Message on read handling */ + char *format_writeSupport; /* Message on write handling */ + + ImFileMagic *format_magicMap; /* Magic numbers */ + + int format_readMultiVfb; /* Can read generate multiple VFBs?*/ + int format_readPipe; /* Can read handle a pipe? */ + int format_writeMultiVfb; /* Can write take multiple VFBs?*/ + int format_writePipe; /* Can write handle a pipe? */ + +#ifdef __STDC__ + int (*format_read)(int,int,FILE*, + TagTable*, TagTable*); /* Read format */ +#else + int (*format_read)( ); /* Read format */ +#endif + ImFileFormatReadMap *format_readMap;/* Read support mapping */ + ImFileFormatWriteMap *format_writeMap;/* Write support mapping */ +} ImFileFormat; + + +/* + * Values for format_readMultiVfb and format_writeMultiVfb: + */ +#define IMMULTI (1) /* Supports multi-image files */ +#define IMNOMULTI (0) /* Doesn't */ + +#define IMPIPE (1) /* Supports pipe input */ +#define IMNOPIPE (0) /* Doesn't */ + + +/* + * TYPEDEF and STRUCTURE + * ImCompressScheme + * + * DESCRIPTION + * This is the list of file compression/encoding schemes. + * + * The elements of the structure are as follows: + * + * compress_suffixes + * A list of suffixes used by the scheme. For instance, + * compress/uncompress use the "Z" suffix. + * + * compress_name + * The name of the scheme. "Z" compression is called + * "Lempel-Ziv Encoding" + * + * compress_magic_numbers + * The possible magic numbers for a file of this type. + * This magic number structure is the same as the one + * in the ImFileFormat structure above. + * + * compress_decode + * This is a subroutine which uncompresses a file, and destroys + * the compressed one. + * + * compress_encode + * This is a subroutine which encodes a file, and destroys the + * unencoded one. + * + * For more information about how to add your own compression schemes, + * refer to imschemes.c. + * + */ + + +typedef struct ImCompressScheme +{ + char **compress_suffixes; /* List of suffixes */ + char *compress_name; /* Name */ + ImFileMagic* compress_magic_numbers; /* Magic number(s) */ +#ifdef __STDC__ + int (*compress_decode)(char*,char*,TagTable*); /* Decode (read) routine*/ + int (*compress_encode)(char*,char*,TagTable*); /* Encode (write) routine*/ +#else + int (*compress_decode)(); /* Read routine */ + int (*compress_encode)(); /* Write routine */ +#endif + +} ImCompressScheme; + + + +/* + * CONSTANTS + * IMVFB... - raster operations and inout values + * + * DESCRIPTION + * The first set of constants define various different raster operations + * used by ImVfbFill() et al. + * + * IMVFBINSIDE and IMVFBOUTSIDE indicate how filling is to take place + * in ImVfbFill(). + */ + +#define IMVFBCLEAR 0 +#define IMVFBSET 15 + +#define IMVFBSOURCE 3 +#define IMVFBPATTERN 5 +#define IMVFBNSOURCE 12 +#define IMVFBNPATTERN 10 + +#define IMVFBAND 1 +#define IMVFBNAND 14 + +#define IMVFBOR 7 +#define IMVFBXOR 6 +#define IMVFBNOR 8 +#define IMVFBNXOR 9 + +#define IMVFBINSIDE 0 +#define IMVFBOUTSIDE 1 + +#define IMGRADNONE 0 +#define IMGRADHORIZ 1 +#define IMGRADVERT 2 + +#define IMVFBPRINTFLOAT (1<<0) +#define IMVFBPRINTINT (1<<1) +#define IMVFBPRINTINTFLOAT (IMVFBPRINTFLOAT|IMVFBPRINTINT) + + + + + + +/* + * CONSTANTS + * IMVFB... - flip directions + * + * DESCRIPTION + * These constants are used to select flip directions in ImVfbFlip(). + */ + +#define IMVFBXFLIP 0x1 /* X direction */ +#define IMVFBYFLIP 0x2 /* Y direction */ +#define IMVFBXYFLIP ( IMVFBXFLIP | IMVFBYFLIP ) /* Both */ +#define IMVFBLEFTRIGHT IMVFBXFLIP +#define IMVFBUPDOWN IMVFBYFLIP +#define IMVFBLEFTRIGHTUPDOWN IMVFBXYFLIP + /* 0x4-0xFFFFFFFF Reserved */ + + + + + +/* + * CONSTANTS + * IMVFB... - resolution change algorithms + * + * DESCRIPTION + * These constants select the algorithm to use when changing the + * resolution of an image using ImVfbResize(). + */ + +#define IMVFBPIXELREP 0 /* Pixel replecation */ +#define IMVFBBILINEAR 1 /* Bilinear interpolation */ + + + + +/* + * MACROS + * Im... - function pseudonims + * + * DESCRIPTION + * These macros are defiend to support various names for the same + * function, such as 'gray' vs. 'grey'. + */ + +#define ImVfbToGrey ImVfbToGray + + + + +/* + * CONSTANTS + * IMERRORFATAL - fatal error + * IMERRORWARNING - warning error + * IMERRORINFO - information error + * + * DESCRIPTION + * These three values flag the type of error causing the invocation of + * an error handler (nominated via the flagsTable). + */ + +#define IMERRORFATAL 0 +#define IMERRORWARNING 1 +#define IMERRORINFO 2 + /* 3-2^32 Reserved */ + + + + + +/* + * CONSTANT + * IMMAXNAME - maximum format name size + * IMDEFMONOTHRESH - default monochrome threshold + */ + +#define IMMAXNAME 100 +#define IMDEFMONOTHRESH 127 + + + + + +/* + * STRUCT & TYPEDEF + * ImHistTable - histogram information table + * + * DESCRIPTION + * ImVfbHist( ) computes a field-correlated histogram on an image + * and returns occurrence counts for each unique field value or + * combination of field values. That list of occurrence counts and + * unique values is returned in an ImHistTable structure. + * + * For example, a histogram is computed on the RED and GREEN fields + * of an image. The returned ImHistTable will have: + * + * imhist_nEntries + * The number of unique RED-GREEN color combinations found. + * imhist_fieldMask + * A bitmask mask of the fields checked: IMRED | IMGREEN + * imhist_nFields + * The number of fields checked... in this case 2: RED & GREEN. + * imhist_values[IMHISTRED] + * A list of imhist_nEntries red values as part of the RED-GREEN + * combo. + * imhist_values[IMHISTGREEN] + * A list of imhist_nEntries green values as part of the RED-GREEN + * combo. + * imhist_values[all others] + * NULL pointers because these fields were not part of the + * histogram calcuation requested. + * imhist_nOccur + * A list of the occurrence counts for each of the RED-GREEN + * pairs listed in imhist_values[IMHISTRED] and + * imhist_values[IMHISTGREEN]. This list will be sorted from + * high to low, if requested on the call to ImVfbHist( ). + * + * This information could be plotted by the user on a graph where the + * vertical axis is an occurrence count, and the horizontal axis is + * each RED-GREEN color combo listed one at a time. The imhist_nOccur + * value for each RED-GREEN pair would be the height of a data point + * for the horizontal position of the RED-GREEN pair on the graph. + * + * Such graphs are more typically used on single-field histograms, + * such as just the RED field, or just the HUE field, or whatever. + * In that case, occurrences would be the vertical axis, and the field + * value the horizontal axis. For example, a histogram of RED in an + * image would produce a graph like the following: + * + * 100 -| * * + * | * + * 80 -| ** + * | * * * + * 60 -| * * * + * | * * + * 40 -|* * + * | * * * * * + * 20 -| * * * * + * | * * ****** * * ***** ***************** ** *** + * 0 -|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-| + * 0 32 64 96 128 160 192 224 256 + * + * This kind of graph of histogram values shows us that this example + * image has a fair amount of bright red (right end of graph) quite a bit + * of low-midrange red (middle of graph) and a bit of dark red (left + * edge of graph). The blank spots in the graph are red values that do + * not occur in the image (the *'s along the bottom of the graph show + * occurrence values of 0). + */ + +#define IMMAXFIELDS 20 +typedef struct ImHistTable +{ + int imhist_nEntries; /* Number of unique colors in table*/ + int imhist_fieldMask; /* Fields for histogram */ + int imhist_nFields; /* Number of fields for histogram*/ + union + { + unsigned short *imhist_val16; /* list for index16 numbers */ + unsigned char *imhist_val8; /* list for rgba & index8 #s*/ + float *imhist_valFloat;/* list for hue, sat, intensity nums*/ + } imhist_values[IMMAXFIELDS]; + unsigned int *imhist_nOccur; /* list of occurrence counts */ +} ImHistTable; + + + + + +/* + * CONSTANTS + * IMHIST... - histogram table array index names + * imhist_... - histogram table union field names + * + * DESCRIPTION + * Values in an ImHistTable returned by ImVfbHist( ) are indexed through + * the imhist_values field. Indexes are named for fields on which the + * histogram may have been done. + * + * To provide a more intuitive look to code using a ImHistTable, the + * fields in the union are given names for quick reference. + */ +#define IMHISTRED 0 +#define IMHISTGREEN 1 +#define IMHISTBLUE 2 +#define IMHISTALPHA 3 +#define IMHISTHUE 4 +#define IMHISTSATURATION 5 +#define IMHISTINTENSITY 6 +#define IMHISTINDEX8 7 +#define IMHISTGRAY 7 +#define IMHISTGREY 7 +#define IMHISTINDEX16 8 +#define IMHISTMONO 9 + +#define imhist_red imhist_values[IMHISTRED].imhist_val8 +#define imhist_blue imhist_values[IMHISTBLUE].imhist_val8 +#define imhist_green imhist_values[IMHISTGREEN].imhist_val8 +#define imhist_alpha imhist_values[IMHISTALPHA].imhist_val8 +#define imhist_hue imhist_values[IMHISTHUE].imhist_valFloat +#define imhist_saturation imhist_values[IMHISTSATURATION].imhist_valFloat +#define imhist_intensity imhist_values[IMHISTINTENSITY].imhist_valFloat +#define imhist_gray imhist_values[IMHISTINDEX8].imhist_val8 +#define imhist_grey imhist_values[IMHISTINDEX8].imhist_val8 +#define imhist_index8 imhist_values[IMHISTINDEX8].imhist_val8 +#define imhist_index16 imhist_values[IMHISTINDEX16].imhist_val16 +#define imhist_mono imhist_values[IMHISTMONO].imhist_val8 + + + + + +/* + * CONSTANTS + * IMMAXIMUM - computed maximum field value + * IMMINIMUM - computed minimum field value + * IMUNIQUEVAL - computed number of unique field values + * IMMAXNUMSTATS - number of statistics returned by ImVfbStat + * + * DESCRIPTION + * ImVfbStat( ) computes simple statistics on a single pixel field in + * an image. Statistics are returned in a floating point array of + * IMMAXNUMSTATS entries. Individual statistics are obtained by + * indexing into this array using IMMAXIMUM, etc. + */ +#define IMMAXIMUM 0 +#define IMMINIMUM 1 +#define IMUNIQUEVAL 2 +#define IMMAXNUMSTATS 20 + + + + + +/* + * TYPEDEF & STRUCTURE + * ImHotSpot - image hotspot + * + * DESCRIPTION + * ImHotSpot contains a cursor hotspot: the point on a cursor image + * that corresponds to the cursor's location for picking purposes. + * For instance, this would be the point of an arrow cursor, the tip + * of a pencil cursor, or the end of the stinger on a bumble-bee cursor! + * + * ImHotSpot contains the cursor's hotspot location, as measured from + * the upper-left corner (0,0) of the image, in pixels. + */ + +typedef struct ImHotSpot +{ + int hot_x; /* x coordinate of hotspot */ + int hot_y; /* y coordinate of hotspot */ +} ImHotSpot; + +typedef ImHotSpot *ImHotSpotPtr; + + + + + +/* + * MACROS + * ImHotSpot... - set and query HotSpot attributes + * + * DESCRIPTION + * These macros, masquerading as subroutines, take a HotSpot pointer + * and set or query information from it. + */ + +#define ImHotSpotQX( hs ) ( (hs)->hot_x ) +#define ImHotSpotQY( hs ) ( (hs)->hot_y ) +#define ImHotSpotSX( hs, xcoord ) ( (hs)->hot_x = (int) xcoord ) +#define ImHotSpotSY( hs, ycoord ) ( (hs)->hot_y = (int) ycoord ) + + + + +/* + * FUNCTIONS + * Im... - Generic functions + * ImClt... - CLT functions + * ImVfb... - VFB functions + * ImFile... - File I/O functions + */ +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __STDC__ +extern void ImPError( char * ); +extern char *ImQError( void ); + +extern void ImRgbToHsi( int[3], float[3] ); +extern void ImHsiToRgb( float[3], int[3] ); + +extern ImClt *ImCltAlloc( int ); +extern void ImCltFree( ImClt * ); +extern ImClt *ImCltDup( ImClt * ); +extern ImClt *ImCltGrayRamp( ImClt *, int, int, int, int, ImClt * ); +extern ImClt *ImCltRoll( ImClt *, int, ImClt * ); + +extern ImVfb *ImVfbAdjust( ImVfb *, int, double, double, int, int, double, double, ImVfb * ); +extern ImVfb *ImVfbAlloc( int, int, int ); +extern ImVfb *ImVfbClear( int, int, ImVfb * ); +extern ImVfb *ImVfbComp( ImVfb *, int, int, int, int, int, int, ImVfb *, int, int ); +extern ImVfb *ImVfbCopy( ImVfb *, int, int, int, int, int, ImVfb *, int, int ); +extern ImVfb *ImVfbDup( ImVfb * ); +extern ImVfb *ImVfbFade( ImVfb *, double, int, ImVfb * ); +extern ImVfb *ImVfbFill( ImVfb *, int, int, int, int, int, double, double, int, int, ImVfb * ); +extern ImVfb *ImVfbFlip( ImVfb *, int, ImVfb * ); +extern void ImVfbFree( ImVfb * ); +extern ImHistTable *ImVfbHist( ImVfb *, int, int ); +extern void ImVfbHistTableFree( ImHistTable * ); +extern ImVfb *ImVfbLightness( ImVfb *, double, ImVfb * ); +extern ImVfb *ImVfbMix( ImVfb *, double, ImVfb *, double, int, ImVfb * ); +extern ImVfb *ImVfbResize( ImVfb *, int, ImVfb *, int, int ); +extern ImVfb *ImVfbRoll( ImVfb *, int, int, ImVfb * ); +extern ImVfb *ImVfbRotate( ImVfb *, double, ImVfb * ); +extern ImVfb *ImVfb90Rotate( ImVfb *, ImVfb * ); +extern float *ImVfbStat( ImVfb *, int, float * ); +extern ImVfb *ImVfbToIndex( ImVfb *, int, ImVfb * ); +extern ImVfb *ImVfbToIndex8( ImVfb *, ImVfb * ); +extern ImVfb *ImVfbToIndex16( ImVfb *, ImVfb * ); +extern ImVfb *ImVfbToMono( ImVfb *, int, ImVfb * ); +extern ImVfb *ImVfbFSDitherToMono( ImVfb *, ImVfb * ); +extern ImVfb *ImVfbToRgb( ImVfb *, ImVfb * ); +extern ImVfb *ImVfbToGray( ImVfb *, ImVfb * ); +extern ImVfb *ImVfbXShear( ImVfb *, double, ImVfb * ); +extern ImVfb *ImVfbYShear( ImVfb *, double, ImVfb * ); +extern void ImVfbPrint( FILE*, int, ImVfb* ); +extern ImVfb *ImVfbCopyChannel( ImVfb* inVfb, int inFieldMask, ImVfb* outVfb, int outFieldMask); + + +extern int ImFileRead( int, char *, TagTable *, TagTable * ); +extern int ImFileFRead( FILE *, char *, TagTable *, TagTable * ); +extern int ImFileWrite( int, char *, TagTable *, TagTable * ); +extern int ImFileFWrite( FILE *, char *, TagTable *, TagTable * ); +extern char *ImFileQFormat( int, char * ); +extern char *ImFileQFFormat( FILE *, char * ); +extern int ImFileQNFormat( void ); +extern int ImFileFormatOptions( int, ArgOption *, ArgOption ** ); +extern int ImFileFormatEquivs( int, ArgEquiv *, ArgEquiv ** ); +extern ImFileFormat **ImFileFindFormat( char * ); +extern char *ImFileQCompression(int , TagTable* ); +extern char *ImFileQFCompression(FILE* , TagTable* ); +ImCompressScheme *ImFileFindCompressionScheme( char* ); + +extern ImFileFormat **ImGetFileFormats( void ); +extern ImCompressScheme**ImGetCompressSchemes( void ); + +#else + +extern void ImPError( ); +extern char *ImQError( ); + +extern void ImRgbToHsi( ); +extern void ImHsiToRgb( ); + +extern ImClt *ImCltAlloc( ); +extern void ImCltFree( ); +extern ImClt *ImCltDup( ); +extern ImClt *ImCltGrayRamp( ); +extern ImClt *ImCltRoll( ); + +extern ImVfb *ImVfbAdjust( ); +extern ImVfb *ImVfbAlloc( ); +extern ImVfb *ImVfbClear( ); +extern ImVfb *ImVfbComp( ); +extern ImVfb *ImVfbCopy( ); +extern ImVfb *ImVfbDup( ); +extern ImVfb *ImVfbFade( ); +extern ImVfb *ImVfbFill( ); +extern ImVfb *ImVfbFlip( ); +extern void ImVfbFree( ); +extern ImVfb *ImVfbGamma( ); +extern ImHistTable *ImVfbHist( ); +extern void ImVfbHistTableFree( ); +extern ImVfb *ImVfbLightness( ); +extern ImVfb *ImVfbMix( ); +extern ImVfb *ImVfbResize( ); +extern ImVfb *ImVfbRoll( ); +extern ImVfb *ImVfbRotate( ); +extern ImVfb *ImVfb90Rotate( ); +extern float *ImVfbStat( ); +extern ImVfb *ImVfbToIndex( ); +extern ImVfb *ImVfbToIndex8( ); +extern ImVfb *ImVfbToIndex16( ); +extern ImVfb *ImVfbFSDitherToMono( ); +extern ImVfb *ImVfbToMono( ); +extern ImVfb *ImVfbToRgb( ); +extern ImVfb *ImVfbToGray( ); +extern ImVfb *ImVfbXShear( ); +extern ImVfb *ImVfbYShear( ); +extern void ImVfbPrint( ); +extern ImVfb *ImVfbCopyChannel( ); + +extern int ImFileRead( ); +extern int ImFileFRead( ); +extern int ImFileWrite( ); +extern int ImFileFWrite( ); +extern char *ImFileQFormat( ); +extern char *ImFileQFFormat( ); +extern int ImFileQNFormat( ); +extern int ImFileFormatOptions( ); +extern int ImFileFormatEquivs( ); +extern ImFileFormat **ImFileFindFormat( ); +extern char *ImFileQCompression( ); +extern ImCompressScheme *ImFileFindCompressionScheme( ); + +extern ImFileFormat **ImGetFileFormats( ); +extern ImCompressScheme**ImGetCompressSchemes( ); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __IMH__ */ + diff --git a/utils/roq2/libim/imbmp.c b/utils/roq2/libim/imbmp.c new file mode 100644 index 0000000..4bf3b4c --- /dev/null +++ b/utils/roq2/libim/imbmp.c @@ -0,0 +1,3714 @@ +/** + ** $Header: /roq/libim/imbmp.c 1 11/02/99 4:38p Zaphod $ + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + ** a division of General Atomics, San Diego, California, USA + ** + ** Users and possessors of this source code are hereby granted a + ** nonexclusive, royalty-free copyright and design patent license to + ** use this code in individual software. License is not granted for + ** commercial resale, in whole or in part, without prior written + ** permission from SDSC. This source is provided "AS IS" without express + ** or implied warranty of any kind. + ** + ** For further information contact: + ** E-Mail: info@sds.sdsc.edu + ** + ** Surface Mail: Information Center + ** San Diego Supercomputer Center + ** P.O. Box 85608 + ** San Diego, CA 92138-5608 + ** (619) 534-5000 + **/ + +#define HEADER " $Header: /roq/libim/imbmp.c 1 11/02/99 4:38p Zaphod $" + +/** + ** FILE + ** imbmp.c - Microsoft Windows BMP file I/O + ** + ** PROJECT + ** libim - SDSC image manipulation library + ** + ** DESCRIPTION + ** imbmp.c contains routines to read and write Microsoft Windows BMP + ** (DIB) files for the image manipulation library. Raster data read + ** in is stored in a VFB. Raster data written out is taken from a + ** tag table. + ** + ** PUBLIC CONTENTS + ** d =defined constant + ** f =function + ** m =defined macro + ** t =typedef/struct/union + ** v =variable + ** ? =other + ** + ** ImFileBmpFormat v file format information + ** + ** PRIVATE CONTENTS + ** imBmpFileHeader t file header information for read + ** imBmpHeaderFields v imBmpHeaderInfoRead description for Bin pkg + ** + ** imBmpFileHeaderWrite t file header information for write + ** imBmpHeaderFieldsWrite v imBmpHeaderInfoWrite description for Bin pkg + ** + ** imBmpRGBQuad t BMP colortable entry + ** imBmpRGBQuadFields v imBmpRGBQuad description for Bin pkg + ** + ** IMBMPCHECKBUFIN m test for full read buffer and do read + ** imBmpWrite2Bytes m write two RLE bytes + ** + ** imBmpRead f read BMP file + ** imBmpReadRLE4 f read 4-bit RLE BMP file + ** imBmpReadRLE8 f read 8-bit RLE BMP file + ** + ** imBmpWriteRLE4 f write 4-bit RLE BMP file + ** imBmpWriteRLE8 f write 8-bit RLE BMP file + ** imBmpWriteRaw1 f write 1-bit uncompressed BMP file + ** imBmpWriteRaw4 f write 4-bit uncompressed BMP file + ** imBmpWriteRaw8 f write 8-bit uncompressed BMP file + ** imBmpWriteRaw24 f write 24-bit uncompressed BMP file + ** + ** HISTORY + + ** $Log: /roq/libim/imbmp.c $ + ** + ** 1 11/02/99 4:38p Zaphod + ** Revision 1.14 1995/06/29 00:28:04 bduggan + ** updated copyright year + ** + ** Revision 1.13 1995/06/15 20:14:44 bduggan + ** Took out 'break's at unreachable points. Took out some + ** unused variables. + ** + ** Revision 1.12 1995/04/03 21:19:36 bduggan + ** took out -DNEWMAGIC + ** + ** Revision 1.11 1994/10/04 20:18:37 bduggan + ** added IM to all defines + ** + ** Revision 1.10 1994/07/20 11:42:23 nadeau + ** Changed SDSC custom types to match v3.0 sdsc.h, added ANSI + ** C function prototypes, updated read and write maps, updated + ** to the new magic number scheme. + ** + ** Revision 1.9 92/12/03 01:46:13 nadeau + ** Corrected info messages. + ** + ** Revision 1.8 92/11/23 17:49:31 nadeau + ** Removed use of IMINFOMSGWIDTH2 + ** + ** Revision 1.7 92/11/04 11:48:24 groening + ** put ImFIleFormat info and magic number info + ** from imfmt.c into this file. + ** + ** Revision 1.6 92/10/12 15:53:40 vle + ** Made changes to make Cray happy. + ** + ** Revision 1.5 92/09/29 17:59:17 vle + ** Added ImInfo messages. + ** + ** Revision 1.4 92/09/17 14:06:48 vle + ** Fixed "scanlinesize" math bug that caused images to be cropped. + ** + ** Revision 1.3 92/09/03 18:19:05 vle + ** Removed one function parameter from imBmpReadRLE4() and + ** imBmpReadRLE8(). + ** + ** Revision 1.2 92/08/31 17:20:57 vle + ** Updated copyright notice. + ** + ** Revision 1.1 92/08/12 16:46:02 vle + ** Initial revision + ** + ** + **/ + +//#include +#include "iminternal.h" + + +/** + ** FORMAT + ** bmp - Microsoft Windows bitmap image + ** + ** AKA + ** dib (Device Independent Bitmap) + ** + ** FORMAT REFERENCES + ** Microsoft Windows 3.1 Programmer's Reference, volume 3, Microsoft + ** Press, 1992. + ** + ** Microsoft Windows 3.1 Programmer's Reference, volume 3, Microsoft + ** Press, 1992. + ** + ** Graphics File Formats, David C. Kay, John R. Levine, McGraw-Hill, + ** 1992. + ** + ** Supercharged Bitmapped Graphics, Steve Rimmer, McGraw-Hill, 1992. + ** + ** CODE CREDITS + ** Custom development, Vinh Le, San Diego Supercomputer Center, 1992. + ** + ** DESCRIPTION + ** Microsoft's BMP (BitMaP) format, also known as DIB (Device + ** Independent Bitmap) format, is used in Microsoft's Windows + ** versions 3.0 and up for bitmap image storage. + ** + ** The BMP format consists of four segments: + ** + ** Bitmap-File Header + ** Bitmap-Info Header + ** ColorTable + ** BitmapBits + ** + ** + ** Bitmap-File Header (14 bytes) + ** ------------------ + ** + ** The Bitmap-File Header contains information about the type, + ** size, and layout of a device-independent bitmap file. + ** + ** Name Type Size Comments + ** ---- ---- ---- -------- + ** bfType uint 2 bytes type of file, must be BM + ** bfSize dword 4 bytes size of the file in bytes + ** bfReserved1 uint 2 bytes reserved, must be zero + ** bfReserved2 uint 2 bytes reserved, must be zero + ** bfOffBits dword 4 bytes offset in bytes from the + ** beginning of the file to + ** the actual bitmap data in + ** the file + ** + ** ( Note: types above and below are as reported in the Microsoft + ** specification and refer to IBM PC-sized values. Usage of the + ** SDSC Binary I/O library by this code insures that an appropriate + ** portable mapping is made from PC types and sizes to this host's. ) + ** + ** typedef struct tagBITMAPFILEHEADER + ** { + ** uint bfType; + ** dword bfSize; + ** uint bfReserved1; + ** uint bfReserved2; + ** dword bfOffBits; + ** } BITMAPFILEHEADER; + ** + ** + ** Bitmap-Info Header (40 bytes) + ** ------------------ + ** + ** The Bitmap-Info Header contains information about the bitmap's + ** dimensions, compression type, and color format. + ** + ** Name Type Size Comments + ** ---- ---- ---- -------- + ** biSize dword 4 bytes number of bytes in + ** Bitmap-Info Header + ** biWidth long 4 bytes width, in pixels + ** biHeight long 4 bytes height, in pixels + ** biPlanes word 2 bytes number of planes for the + ** display device, must be 1 + ** biBitCount word 2 bytes bits per pixel, must be 1, + ** 4, 8 or 24 + ** biCompression dword 4 bytes BI_RGB(0), no compression + ** BI_RLE8(1), RLE compression of + ** 8-bits/pixel image + ** BI_RLE4(2), RLE compression of + ** 4-bits/pixel image + ** biSizeImage dword 4 bytes image size in bytes + ** biXPelsPerMeter long 4 bytes horizontal resolution in + ** pixels per meter, used for + ** aspect scaling + ** biYPelsPerMeter long 4 bytes vertical resolution in + ** pixels per meter, used for + ** aspect scaling + ** biClrUsed dword 4 bytes number of color indexes + ** actually used by bitmap + ** biClrImportant dword 4 bytes number of color absolutely + ** necessary to display the + ** image; if 0, all colors are + ** needed + ** + ** typedef struct tagBITMAPINFOHEADER + ** { + ** dword biSize; + ** long biWidth; + ** long biHeight; + ** word biPlanes; + ** word biBitCount; + ** dword biCompression; + ** dword biSizeImage; + ** long biXPelsPerMeter; + ** long biYPelsPerMeter; + ** dword biClrUsed; + ** dword biClrImportant; + ** } BITMAPINFOHEADER; + ** + ** + ** ColorTable (4 bytes per color) + ** ---------- + ** + ** ColorTable is an array of RGBQuad's. Each RGBQuad contains color + ** intensity values for Red, Green, and Blue. + ** + ** RGBQuad + ** ------- + ** + ** Name Type Size Comment + ** ---- ---- ---- ------- + ** rgbBlue byte 1 byte intensity of blue + ** rgbGreen byte 1 byte intensity of green + ** rgbRed byte 1 byte intensity of red + ** rgbReserved byte 1 byte unused + ** + ** typedef struct tagRGBQUAD + ** { + ** byte rgbBlue; + ** byte rgbGreen; + ** byte rgbRed; + ** byte rgbReserved; + ** } RGBQUAD; + ** + ** + ** BitmapBits + ** ---------- + ** + ** BitmapBits is the actual bitmap image data. It is an array + ** of bytes representing consecutive scanlines. The number of + ** bytes varies with the resolution and number of colors. Each + ** scanline must be zero-padded to a 32-bit boundary. Scanlines + ** are stored from the bottom up; that is, the first-byte is in + ** the lower-left corner of the image. + ** + ** The biBitCount field of BITMAPINFOHEADER gives the bits per + ** pixel information for the bitmap. Valid values are 1 + ** (monochrome image, 2 color image), 4 (16 color image), + ** 8 (256 color image), or 24 (true color). + ** + ** Monochrome images have 2 color colortables. They are non- + ** compressed, but are pixel packed. 8 pixels are packed into + ** each stored byte. A clear bit means use the first color. + ** A set bit means use the second color. + ** + ** 4-bits/pixel images can be non-compressed or compressed. + ** Non-compressed images are pixel packed. Each byte stores + ** two 4-bit pixels. Compressed images are compressed with + ** a 4-bit RLE (RLE4) variant described below. + ** + ** 8-bits/pixel images can be non-compressed or compressed. + ** For non-compressed images, each byte stores one 8-bit pixel. + ** Compressed images are compressed with an 8-bit RLE (RLE8) + ** variant described below. + ** + ** 24-bits/pixel images are stored non-compressed. Each pixel + ** is represented by 3 bytes. Each byte represents Blue, + ** Green, and Red, in this order. + ** + ** RLE4 - 4-bit RLE Encoding Scheme + ** -------------------------------- + ** + ** The 4-bit RLE encoding scheme consists of two modes, Encoded + ** and Absolute. + ** + ** The Encoded Mode consists of two byte pairs. + ** 1st byte length of run + ** 2nd byte This byte contains 2 color indicies, one in + ** the high 4 bits and one in the low 4 bits. + ** The run is filled with alternating sequences + ** of these indicies. + ** + ** if the 1st byte is zero (0x00), then the 2nd byte is interpreted + ** as follows: + ** 0x00 End of scanline + ** 0x01 End of bitmap + ** 0x02 "Delta". The next two bytes indicate right + ** and down movements. + ** 0x03- Copy the next 3 to 255 4-bit byte halves + ** 0xFF literally. This is the Absolute Mode. + ** + ** The Absolute Mode is described as above. It always has zero + ** (0x00) as the 1st byte. The 2nd byte can range between 0x03 + ** to 0xFF as described. + ** + ** ex. Compressed Expanded + ** ---------- -------- + ** 03 04 0 4 0 + ** 05 06 0 6 0 6 0 + ** 00 06 45 56 67 00 4 5 5 6 6 7 + ** 00 02 05 01 Move 5 right and 1 down + ** 04 78 7 8 7 8 + ** 00 00 End of scanline + ** 09 1E 1 E 1 E 1 E 1 E 1 + ** 00 01 End of RLE bitmap + ** + ** Note: Runs must be word aligned! + ** + ** RLE8 - 8-bit RLE Encoding Scheme + ** -------------------------------- + ** + ** The 8-bit RLE encoding scheme consists of two modes, Encoded + ** and Absolute. + ** + ** The Encoded Mode consists of two byte pairs. + ** 1st byte length of run + ** 2nd byte byte to repeat in run (color index for pixel) + ** + ** if the 1st byte is zero (0x00), then the 2nd byte is interpreted + ** as follows: + ** 0x00 End of scanline + ** 0x01 End of bitmap + ** 0x02 "Delta". The next two bytes indicate right + ** and down movements. + ** 0x03- Copy the next 3 - 255 bytes literally. + ** 0xFF This is the Absolute Mode. + ** + ** The Absolute Mode is described as above. It always has zero + ** (0x00) as the 1st byte. The 2nd byte can range between 0x03 + ** to 0xFF as described. + ** + ** ex. Compressed Expanded + ** ---------- -------- + ** 03 04 04 04 04 + ** 05 06 06 06 06 06 06 + ** 00 03 45 56 67 00 45 56 67 + ** 00 02 05 01 Move 5 right and 1 down + ** 02 78 78 78 + ** 00 00 End of scanline + ** 09 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E + ** 00 01 End of RLE bitmap + ** + ** Note: Runs must be word aligned! + ** + **/ + + +/* + * FUNCTION DECLARATIONS + */ +#ifdef __STDC__ +static int imBmpRead( int, int, FILE *, TagTable *, TagTable * ); +static int imBmpReadRLE4( int, int, FILE *, ImVfb *, unsigned int ); +static int imBmpReadRLE8( int, int, FILE *, ImVfb *, unsigned int ); + +static int imBmpWriteRaw1( ImFileFormatWriteMap *, int, int, FILE *, + TagTable *, TagTable * ); +static int imBmpWriteRaw4( ImFileFormatWriteMap *, int, int, FILE *, + TagTable *, TagTable * ); +static int imBmpWriteRaw8( ImFileFormatWriteMap *, int, int, FILE *, + TagTable *, TagTable * ); +static int imBmpWriteRaw24( ImFileFormatWriteMap *, int, int, FILE *, + TagTable *, TagTable * ); +static int imBmpWriteRLE4( ImFileFormatWriteMap *, int, int, FILE *, + TagTable *, TagTable * ); +static int imBmpWriteRLE8( ImFileFormatWriteMap *, int, int, FILE *, + TagTable *, TagTable * ); +#else +static int imBmpRead( ), imBmpReadRLE4( ), imBmpReadRLE8( ); +static int imBmpWriteRaw1( ), imBmpWriteRaw4( ), imBmpWriteRaw8( ) ; +static int imBmpWriteRaw24( ), imBmpWriteRLE4( ), imBmpWriteRLE8( ) ; +#endif + + + + + +/* + * FORMAT INFORMATION + * imBmpNames - format's name and aliases + * imBmpReadMap - read attributes + * imBmpWriteMap - write attributes + * imBmpMagicNumber - magic number + * imBmpMagic - list of magic numbers + * ImFileBmpFormat - master format description + */ +static char *imBmpNames[ ] = { "bmp", "dib", NULL }; +static ImFileFormatReadMap imBmpReadMap[ ] = +{ + /* in out */ + /* type,ch,dep, attr. VFB type attr. */ + { RGB,3,8, 0, IMVFBRGB, 0 }, + { IN,1,8, C, IMVFBINDEX8, C }, + { IN,1,4, C, IMVFBINDEX8, C }, + { IN,1,8, RLE|C, IMVFBINDEX8, C }, + { IN,1,4, RLE|C, IMVFBINDEX8, C }, + { IN,1,1, C, IMVFBINDEX8, C }, + { -1, 0, -1, 0 }, +}; +static ImFileFormatWriteMap imBmpWriteMap[ ] = +{ + /* in out */ + /* VFB type, attr., type,ch,dep, attr., func */ + { IMVFBMONO, 0, IN,1,1, C, imBmpWriteRaw1 }, + { IMVFBINDEX8, C, IN,1,8, C, imBmpWriteRaw8 }, + { IMVFBINDEX8, C, IN,1,4, C, imBmpWriteRaw4 }, + { IMVFBINDEX8, C, IN,1,8, RLE|C, imBmpWriteRLE8 }, + { IMVFBINDEX8, C, IN,1,4, RLE|C, imBmpWriteRLE4 }, + { IMVFBRGB, 0, RGB,3,8, 0, imBmpWriteRaw24 }, + { -1, 0, -1, 0, NULL } +}; + +static unsigned char imBmpMagicNumber[ ] = { 'B', 'M' }; +static ImFileMagic imBmpMagic[ ] = +{ + { 0, 2, imBmpMagicNumber }, + { 0, 0, NULL }, +}; + +ImFileFormat ImFileBmpFormat = +{ + imBmpNames, /* Names */ + "Windows bitmap image file", /* Description */ + "Microsoft", /* Creator */ + "1-, 4-, and 8-bit color index, and 24-bit RGB color, and\n\ +- and 8-bit RLE compressed images.", /* Read support */ +"1-, 4-, and 8-bit color index, and 24-bit RGB color, and\n\ +- and 8-bit RLE compressed images.", /* Write support*/ + imBmpMagic, /* Magic #'s */ + IMNOMULTI, IMPIPE, /* Read? */ + IMNOMULTI, IMNOPIPE, /* Write? */ + imBmpRead, imBmpReadMap, imBmpWriteMap /* Maps */ +}; + + + + + +/* + * TYPEDEF & STRUCTURE + * imBmpHeaderInfoRead - BMP file header information for read + * imBmpHeaderFieldsRead - BMP file header fields for binary pkg. + * imBmpHeaderInfoWrite - BMP file header information for write + * imBmpHeaderFieldsWrite - BMP file header fields for binary pkg. + * imBmpRGBQuad - BMP color information + * imBmpRGBQuadFields - BMP color information fields for binary + * pkg. + * + * DESCRIPTION + * imBmpHeaderInfo contains both the Bitmap-File Header and + * Bitmap-Info Header as described in the format specifications. + * + * The imBmpRGBQuad is one entry in the colortable. + */ + +typedef struct imBmpHeaderInfoRead +{ + sdsc_uint32 bmp_size; /* size of file in bytes */ + sdsc_uint16 bmp_reserved1; /* reserved1, must be zero */ + sdsc_uint16 bmp_reserved2; /* reserved2, must be zero */ + sdsc_uint32 bmp_offsetbits; /* offset to actual bitmap data */ + sdsc_uint32 bmp_bisize; /* # bytes in bitmap info header*/ + sdsc_uint32 bmp_biwidth; /* image width in pixels */ + sdsc_uint32 bmp_biheight; /* image height in pixels */ + sdsc_uint16 bmp_biplanes; /* # planes for device, always 1*/ + sdsc_uint16 bmp_bibitcount; /* bits/pixel (1, 4, 8, or 24) */ + sdsc_uint32 bmp_bicompress; /* compression method */ + sdsc_uint32 bmp_bisizeimage; /* image size in bytes */ + sdsc_uint32 bmp_bixpm; /* x pixels per meter */ + sdsc_uint32 bmp_biypm; /* y pixels per meter */ + sdsc_uint32 bmp_biclrused; /* # colors actually used in CLT*/ + sdsc_uint32 bmp_biclrim; /* # required colors to display */ +} imBmpHeaderInfoRead; + + +static BinField imBmpHeaderFieldsRead[ ] = +{ + { UINT32, 4, 1 }, /* bmp_size */ + { UINT16, 2, 1 }, /* bmp_reserved1 */ + { UINT16, 2, 1 }, /* bmp_reserved2 */ + { UINT32, 4, 1 }, /* bmp_offsetbits */ + { UINT32, 4, 1 }, /* bmp_bisize */ + { UINT32, 4, 1 }, /* bmp_biwidth */ + { UINT32, 4, 1 }, /* bmp_biheight */ + { UINT16, 2, 1 }, /* bmp_biplanes */ + { UINT16, 2, 1 }, /* bmp_bibitcount */ + { UINT32, 4, 1 }, /* bmp_bicompress */ + { UINT32, 4, 1 }, /* bmp_bisizeimage */ + { UINT32, 4, 1 }, /* bmp_bixpm */ + { UINT32, 4, 1 }, /* bmp_biypm */ + { UINT32, 4, 1 }, /* bmp_biclrused */ + { UINT32, 4, 1 }, /* bmp_biclrim */ + { 0, 0, 0 } +}; + + +typedef struct imBmpHeaderInfoWrite +{ + sdsc_uint16 bmp_type; /* type of image, must be 'BM' */ + sdsc_uint32 bmp_size; /* size of file in bytes */ + sdsc_uint16 bmp_reserved1; /* reserved1, must be zero */ + sdsc_uint16 bmp_reserved2; /* reserved2, must be zero */ + sdsc_uint32 bmp_offsetbits; /* offset to actual bitmap data */ + sdsc_uint32 bmp_bisize; /* # bytes in bitmap info header*/ + sdsc_uint32 bmp_biwidth; /* image width in pixels */ + sdsc_uint32 bmp_biheight; /* image height in pixels */ + sdsc_uint16 bmp_biplanes; /* # planes for device, always 1*/ + sdsc_uint16 bmp_bibitcount; /* bits/pixel (1, 4, 8, or 24) */ + sdsc_uint32 bmp_bicompress; /* compression method */ + sdsc_uint32 bmp_bisizeimage; /* image size in bytes */ + sdsc_uint32 bmp_bixpm; /* x pixels per meter */ + sdsc_uint32 bmp_biypm; /* y pixels per meter */ + sdsc_uint32 bmp_biclrused; /* # colors actually used from CLT*/ + sdsc_uint32 bmp_biclrim; /* # required colors to display */ +} imBmpHeaderInfoWrite; + + +static BinField imBmpHeaderFieldsWrite[ ] = +{ + { UINT16, 2, 1 }, /* bmp_type */ + { UINT32, 4, 1 }, /* bmp_size */ + { UINT16, 2, 1 }, /* bmp_reserved1 */ + { UINT16, 2, 1 }, /* bmp_reserved2 */ + { UINT32, 4, 1 }, /* bmp_offsetbits */ + { UINT32, 4, 1 }, /* bmp_bisize */ + { UINT32, 4, 1 }, /* bmp_biwidth */ + { UINT32, 4, 1 }, /* bmp_biheight */ + { UINT16, 2, 1 }, /* bmp_biplanes */ + { UINT16, 2, 1 }, /* bmp_bibitcount */ + { UINT32, 4, 1 }, /* bmp_bicompress */ + { UINT32, 4, 1 }, /* bmp_bisizeimage */ + { UINT32, 4, 1 }, /* bmp_bixpm */ + { UINT32, 4, 1 }, /* bmp_biypm */ + { UINT32, 4, 1 }, /* bmp_biclrused */ + { UINT32, 4, 1 }, /* bmp_biclrim */ + { 0, 0, 0 } +}; + + +typedef struct imBmpRGBQuad +{ + unsigned char blue; /* blue intensity */ + unsigned char green; /* green intensity */ + unsigned char red; /* red intensity */ + unsigned char reserved; /* unused */ +} imBmpRGBQuad; + +static BinField imBmpRGBQuadFields[ ] = +{ + { UCHAR, 1, 1 }, /* blue */ + { UCHAR, 1, 1 }, /* green */ + { UCHAR, 1, 1 }, /* red */ + { UCHAR, 1, 1 }, /* reserved */ + { 0, 0, 0 } +}; + + + + + +/* + * CONSTANTS + * BMP* - assorted useful BMP constants + */ + +#define IMBMPMAGIC_MBF 0x424d + /* magic value, 'BM' */ +#define IMBMPMAGIC_LBF 0x4d42 + /* magic value, 'BM' in reverse byte order */ +#define IMBMPFILEHEADERSIZE 14 + /* 14 bytes */ +#define IMBMPINFOHEADERSIZE 40 + /* 40 bytes */ +#define IMBMPHEADERSIZE (IMBMPFILEHEADERSIZE + IMBMPINFOHEADERSIZE) + /* size of complete header */ +#define IMBMPRGBQUADSIZE 4 + /* 4 bytes */ +#define IMBMPRGB 0 + /* no compression */ +#define IMBMPRLE8 1 + /* 8-bit RLE compression */ +#define IMBMPRLE4 2 + /* 4-bit RLE compression */ +#define IMBLKSIZE (BUFSIZ) + /* number of bytes to read */ + +#define IMBMPCHECKBUFIN \ +if( bufin > (IMBLKSIZE-1) ) \ +{ \ + if( ImBinRead( ioType,fd,fp,buf,UCHAR,1,IMBLKSIZE )==-1) \ + { \ + ImReturnBinError( ); \ + } \ + bufin = 0; \ +} \ + + + + + + + +/* + + * FUNCTION + + * imBmpReadRLE8 - Read a BMP 8-bit RLE compressed image + + * + + * DESCRIPTION + + * This function reads in and decompresses a BMP 8-bit RLE image + + * into a vfb. See encoding scheme in FORMAT section. + + */ +static int /* Returns status */ +#ifdef __STDC__ +imBmpReadRLE8( int ioType, int fd, FILE *fp, ImVfb *vfb, unsigned int y ) +#else +imBmpReadRLE8( ioType, fd, fp, vfb, y ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + ImVfb *vfb; /* Pointer to image */ + unsigned int y; /* height of image */ +#endif +{ + ImVfbPtr vfbptr; /* Pointer into vfb image */ + unsigned char pair[ 2 ];/* RLE code pair */ + unsigned char ampair[ 2 ];/* RLE Absolute Mode pair */ + int ycount; /* Current scanline */ + unsigned char buf[ IMBLKSIZE ]; /* File input buffer */ + int bufin; /* File input buffer index */ + int i; /* Counters */ + + /* + * The image is stored from the bottom up; that is, + * the first scanline in the file is the last + * scanline in the image. + */ + + /* + * Go to the last scanline in the vfb + */ + vfbptr = ImVfbQPtr( vfb, 0, y-1 ); + + /* + * Initializations + */ + ycount = 0; + bufin = IMBLKSIZE; + + /* + * Read in and decompress image bitmap + */ + while( ycount < y) + { + /* + * Read in file data into buf + */ + IMBMPCHECKBUFIN; + pair[0] = buf[ bufin++ ]; + IMBMPCHECKBUFIN; + pair[1] = buf[ bufin++ ]; + + if ( pair[0] != 0x00 ) + { + /* Encoded Mode */ + for( i = 0; i < pair[0]; ++i ) + { + ImVfbSIndex8( vfb, vfbptr, pair[1] ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + } + continue; + } + + switch( pair[1] ) + { + case 0x00: /* end of scanline */ + vfbptr = ImVfbQUp( vfb, vfbptr ); + vfbptr = ImVfbQUp( vfb, vfbptr ); + ++ycount; + break; + + case 0x01: /* end of bitmap */ + if( ycount != y-1 ) + { + /* issue warning to user */ + ImErrorWarning( "Unexpected end of bitmap. Input file may be corrupted!", -1, IMEDECODING ); + } + return( 1 ); + + case 0x02: /* "delta", right and down movement */ + /* + * Read in file data into buf + */ + IMBMPCHECKBUFIN; + pair[0] = buf[ bufin++ ]; + IMBMPCHECKBUFIN; + pair[1] = buf[ bufin++ ]; + + for( i = 0; i < pair[0]; ++i ) + { + vfbptr = ImVfbQRight( vfb, vfbptr ); + } + for( i = 0; i < pair[1]; ++i ) + { + vfbptr = ImVfbQDown( vfb, vfbptr ); + } + break; + + default: /* Absolute Mode */ + for( i = 0; i < pair[1]/2; ++i ) + { + /* + * Read in file data into buf + */ + IMBMPCHECKBUFIN; + ampair[0] = buf[ bufin++ ]; + IMBMPCHECKBUFIN; + ampair[1] = buf[ bufin++ ]; + + ImVfbSIndex8( vfb, vfbptr, ampair[0] ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + + ImVfbSIndex8( vfb, vfbptr, ampair[1] ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + } + if( (pair[1]%2) != 0 ) + { + /* + * Read in file data into buf + */ + IMBMPCHECKBUFIN; + ampair[0] = buf[ bufin++ ]; + IMBMPCHECKBUFIN; + ampair[1] = buf[ bufin++ ]; + + ImVfbSIndex8( vfb, vfbptr, ampair[0] ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + } + break; + } /* end switch */ + } + return( 1 ); +} + + + + + +/* + * FUNCTION + * imBmpReadRLE4 - Read a BMP 4-bit RLE compressed image + * + * DESCRIPTION + * This function reads in and decompresses a BMP 4-bit RLE image + * into a vfb. See encoding scheme in FORMAT section. + */ +static int /* Returns status */ +#ifdef __STDC__ +imBmpReadRLE4( int ioType, int fd, FILE *fp, ImVfb *vfb, unsigned int y ) +#else +imBmpReadRLE4( ioType, fd, fp, vfb, y ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + ImVfb *vfb; /* Pointer to image */ + unsigned int y; /* height of image */ +#endif +{ + ImVfbPtr vfbptr; /* Pointer into vfb image */ + unsigned char pair[ 2 ];/* RLE code pair */ + unsigned char ampair[ 2 ];/* RLE Absolute Mode pair */ + int ycount; /* Current scanline */ + unsigned char buf[ IMBLKSIZE ]; /* File input buffer */ + int bufin; /* File input buffer pointer */ + int i; /* Counters */ + + /* + * The image is stored from the bottom up; that is, + * the first scanline in the file is the last + * scanline in the image. + */ + + /* + * Go to the last scanline in the vfb + */ + vfbptr = ImVfbQPtr( vfb, 0, y-1 ); + + /* + * Initializations + */ + ycount = 0; + bufin = IMBLKSIZE; + + /* + * Read in and decompress image bitmap + */ + while( ycount < y ) + { + /* + * Read in file data into buf + */ + IMBMPCHECKBUFIN; + pair[0] = buf[ bufin++ ]; + IMBMPCHECKBUFIN; + pair[1] = buf[ bufin++ ]; + + if ( pair[0] != 0x00 ) + { + /* Encoded Mode */ + for( i = 0; i < pair[0]/2; ++i ) + { + ImVfbSIndex8( vfb, vfbptr, (pair[1] >> 4) ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + + ImVfbSIndex8( vfb, vfbptr, pair[1] & 0x0F ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + } + if( pair[0]%2 != 0 ) + { + ImVfbSIndex8( vfb, vfbptr, (pair[1] >> 4) ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + } + continue; + } + + switch( pair[1] ) + { + case 0x00: /* end of scanline */ + vfbptr = ImVfbQUp( vfb, vfbptr ); + vfbptr = ImVfbQUp( vfb, vfbptr ); + ++ycount; + break; + + case 0x01: /* end of bitmap */ + if( ycount != y-1 ) + { + /* issue warning to user */ + ImErrorWarning( "Unexpected end of bitmap. Input file may be corrupted!", -1, IMEDECODING ); + } + return( 1 ); + + case 0x02: /* "delta", right and down movement */ + /* + * Read in file data into buf + */ + IMBMPCHECKBUFIN; + pair[0] = buf[ bufin++ ]; + IMBMPCHECKBUFIN; + pair[1] = buf[ bufin++ ]; + + for( i = 0; i < pair[0]; ++i ) + { + vfbptr = ImVfbQRight( vfb, vfbptr ); + } + for( i = 0; i < pair[1]; ++i ) + { + vfbptr = ImVfbQDown( vfb, vfbptr ); + } + break; + + default: /* Absolute Mode */ + for( i = 0; i < pair[1]/4; ++i ) + { + /* + * Read in file data into buf + */ + IMBMPCHECKBUFIN; + ampair[0] = buf[ bufin++ ]; + IMBMPCHECKBUFIN; + ampair[1] = buf[ bufin++ ]; + + ImVfbSIndex8( vfb, vfbptr, + (ampair[0] >> 4) ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + + ImVfbSIndex8( vfb, vfbptr, + ampair[0] & 0x0F ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + + ImVfbSIndex8( vfb, vfbptr, + (ampair[1] >> 4) ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + + ImVfbSIndex8( vfb, vfbptr, + ampair[1] & 0x0F ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + } + if( (pair[1]%4) != 0 ) + { + /* + * Read in file data into buf + */ + IMBMPCHECKBUFIN; + ampair[0] = buf[ bufin++ ]; + IMBMPCHECKBUFIN; + ampair[1] = buf[ bufin++ ]; + + ImVfbSIndex8( vfb, vfbptr, + (ampair[0] >> 4) ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + + if( pair[1]%4 > 1 ) + { + ImVfbSIndex8( vfb, vfbptr, + ampair[0] & 0x0F ); + vfbptr = ImVfbQRight( vfb, + vfbptr ); + } + if( pair[1]%4 > 2 ) + { + ImVfbSIndex8( vfb, vfbptr, + ampair[1] >> 4 ); + vfbptr = ImVfbQRight( vfb, + vfbptr ); + } + } + break; + } /* end switch */ + } + return( 1 ); +} + + + + + +/* + * FUNCTION + * imBmpRead - read a Microsoft Windows Bmp file + * + * DESCRIPTION + * The file header is read and the size of the image determined. + * Space is allocated for the VFB and the file is read into the + * VFB. + */ + +static int /* Returns # tags read in */ +#ifdef __STDC__ +imBmpRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) +#else +imBmpRead( ioType, fd, fp, flagsTable, tagTable ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag table to add to */ +#endif +{ + imBmpHeaderInfoRead header; /* BMP file header */ + ImVfb *vfb; /* Virtual Frame Buffer */ + ImVfbPtr vfbptr; /* Pixel pointer */ + ImClt *clt = IMCLTNULL; /* Colortable */ + ImCltPtr cltptr; /* Colortable entry pointer */ + int x,y; /* Convenient short names */ + unsigned char mask; /* Mask for extracting pixels */ + sdsc_uint16 bmp_type; /* BMP type (magic number) */ + unsigned char *rbuf = NULL; /* read buffer */ + unsigned char *rbufptr; /* read buffer data pointer */ + unsigned char *sbuf = NULL; /* skip buffer */ + unsigned int skipsize = 0; /* number of bytes to skip */ + unsigned int scanlinesize; /* length of scanline in bytes */ + unsigned int ncolors = 0; /* number of colors used */ + unsigned int i, j, k; /* Counters */ + char message[100]; /* ImInfo message */ + + + /* + * Set Binary Package byte order and read in BMP type + */ + BinByteOrder( BINLBF ); + if( ImBinRead( ioType, fd, fp, &bmp_type, UINT16, 2, 1) == -1 ) + { + ImReturnBinError( ); + } + + /* + * Use BMP type (magic number) to determine byte ordering of rest + * of file + */ + + switch( bmp_type ) + { + case IMBMPMAGIC_LBF: + /* no need to change byte ordering */ + ImInfo( "Byte Order", "Least Significant Byte First" ); + break; + case IMBMPMAGIC_MBF: + ImInfo( "Byte Order", "Most Significant Byte First" ); + BinByteOrder( BINMBF ); + break; + default: + /* + * Bad BMP type (magic number) + */ + ImErrorFatal( ImQError( ), -1, IMEMAGIC ); + } + + /* + * Read in rest of Bmp file header + */ + if( ImBinReadStruct( ioType, fd, fp, &header, + imBmpHeaderFieldsRead )== -1) + { + ImReturnBinError( ); + } + + x = header.bmp_biwidth; + y = header.bmp_biheight; + + sprintf( message, "%d x %d", x, y ); + ImInfo( "Resolution", message ); + + /* + * Allocate a VFB of the required size and type. + */ + switch( header.bmp_bibitcount ) + { + case 1: /* 1-bit index image */ + case 4: /* 4-bit index image */ + case 8: /* 8-bit index image */ + if( (vfb = ImVfbAlloc( x, y, IMVFBINDEX8 )) == IMVFBNULL ) + { + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + ImVfbClear( IMVFBINDEX8, 0, vfb ); + + sprintf( message, "%d-bit Color Indexed", header.bmp_bibitcount ); + ImInfo( "Type", message ); + break; + + case 24: /* 24-bit RGB image */ + if( (vfb = ImVfbAlloc( x, y, IMVFBRGB )) == IMVFBNULL ) + { + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + ImVfbClear( IMVFBRGB, 0, vfb ); + sprintf( message, "24-bit RGB" ); + ImInfo( "Type", message ); + break; + + default: /* unsupported image depth */ + ImErrorFatal( ImQError( ), -1, IMEDEPTH ); + } + + /* + * Allocate and read in colortable, if necessary + */ + if( header.bmp_bibitcount != 24) + { + /* + * Find number of colors used and allocate space + * for colortable + */ + ncolors = header.bmp_biclrused; + if( ncolors == 0 ) + { + ncolors = 1 << header.bmp_bibitcount; + } + clt = ImCltAlloc( ncolors ); + + /* + * Output -verbose messages + */ + sprintf( message, "%d Entries", ncolors ); + ImInfo( "Color Table", message ); + + /* + * Read in colortable and set RGB values + */ + cltptr = ImCltQFirst( clt ); + ImMalloc( rbuf, unsigned char*, ncolors*IMBMPRGBQUADSIZE ); + rbufptr = rbuf; + if( ImBinRead( ioType, fd, fp, rbuf, UCHAR, 1, + ncolors*IMBMPRGBQUADSIZE ) == -1 ) + { + ImReturnBinError( ); + } + for( i = 0; i < ncolors; ++i ) + { + ImCltSBlue( cltptr, *rbufptr ); + ++rbufptr; + ImCltSGreen( cltptr, *rbufptr ); + ++rbufptr; + ImCltSRed( cltptr, *rbufptr ); + ++rbufptr; + /* skip reserved field */ + ++rbufptr; + + cltptr = ImCltQNext( clt, cltptr ); + } + free( rbuf ); + + /* + * Attach colortable to vfb and append to tagTable + */ + ImVfbSClt( vfb, clt ); + TagTableAppend( tagTable, TagEntryAlloc( "image clt", + POINTER, &clt)); + } + + /* + * Skip to beginning of bitmap data + */ + skipsize = ( (header.bmp_offsetbits - IMBMPHEADERSIZE) - + (ncolors * IMBMPRGBQUADSIZE ) ); + if( skipsize > 0 ) + { + if( ioType & IMFILEIOFD ) + { + ImSeek( ioType, fd, fp, header.bmp_offsetbits, L_SET ); + } + else + { + ImMalloc( sbuf, unsigned char*, skipsize ); + if( ImBinRead( ioType, fd, fp, sbuf, UCHAR, 1, + skipsize ) == -1 ) + { + free( (char*) sbuf ); + ImReturnBinError( ); + } + free( (unsigned char*) sbuf ); + } + } + + /* + * Use image compression type info to determine whether to do + * verbatim copy into the vfb or RLE expansion into the vfb + */ + if( header.bmp_bicompress == IMBMPRGB ) /* no compression */ + { + /* + * The image is stored from the bottom up; that is, + * the first scanline in the file is the last + * scanline in the image. + */ + + /* + * Go to the last scanline in the vfb + */ + vfbptr = ImVfbQPtr( vfb, 0, y-1 ); + + /* + * Read in non-compressed image + */ + switch( header.bmp_bibitcount ) + { + case 1: + ImInfo( "Compression Type", "none" ); + + /* + * Read 1-bit non-compressed image + * + * 1 bytes contains info for 8 pixels + * + * -------- <- 1 byte + * 12345678 + * + * Set = first color in colortable + * Not Set = second color in colortable + * + */ + + /* + * Find scanline size to nearest 32-bit + * boundary and allocate read buffer + * + * Note: bit shifting is used to speed up + * calculations + */ + scanlinesize = x>>5; + if( x%32 ) scanlinesize++; + scanlinesize <<= 2; + + ImMalloc( rbuf, unsigned char*, scanlinesize ); + + for( i = 0; i < y ; ++i ) + { + if( ImBinRead( ioType, fd, fp, + rbuf, UCHAR, 1, scanlinesize ) == -1) + { + ImReturnBinError( ); + } + rbufptr = rbuf; + + for( j = 0; j < (x/8); ++j ) + { + mask = 128; + for( k = 0; k < 8; ++k ) + { + if( ( *(rbufptr) & mask ) == mask ) + { + ImVfbSIndex8( vfb, vfbptr, 1 ); + } + else + { + ImVfbSIndex8( vfb, vfbptr, 0 ); + } + vfbptr = ImVfbQRight( vfb, vfbptr ); + mask = mask >> 1; + } + ++rbufptr; + } + if( x%8 ) + { + mask = 128; + for( k = 0; k < x%8; ++k ) + { + if( ( *(rbufptr) & mask ) == mask ) + { + ImVfbSIndex8( vfb, vfbptr, 1 ); + } + else + { + ImVfbSIndex8( vfb, vfbptr, 0 ); + } + vfbptr = ImVfbQRight( vfb, vfbptr ); + mask = mask >> 1; + } + } + + /* + * Go up two scanline since we + * wrapped-around with ImVfbQRight( ) + */ + vfbptr = ImVfbQUp( vfb, vfbptr ); + vfbptr = ImVfbQUp( vfb, vfbptr ); + } + + break; + case 4: + ImInfo( "Compression Type", "none" ); + + /* + * Read 4-bit non-compressed image + * + * 1 byte contains info for 2 pixels + * + * ---- ---- <-- 1 byte + * 1111 2222 + * + */ + + /* + * Find scanline size to nearest 32-bit + * boundary and allocate read buffer + * + * Note: bit shifting is used to speed up + * calculations + */ + scanlinesize = x>>3; + if( x%8 ) scanlinesize++; + scanlinesize <<= 2; + + ImMalloc( rbuf, unsigned char*, scanlinesize ); + + /* + * Read in image and store in vfb + */ + for( i = 0; i < y; ++i ) + { + if( ImBinRead( ioType, fd, fp, + rbuf, UCHAR, 1, scanlinesize ) == -1) + { + ImReturnBinError( ); + } + rbufptr = rbuf; + + for( j = 0; j < (x/2); ++j ) + { + mask = 0xF0; + ImVfbSIndex8( vfb, vfbptr, + ((*(rbufptr) & mask) >> 4) ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + + mask = 0x0F; + ImVfbSIndex8( vfb, vfbptr, + *(rbufptr) & mask ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + + ++rbufptr; + } + if( x%2 ) + { + mask = 0xF0; + ImVfbSIndex8( vfb, vfbptr, + ((*(rbufptr) & mask) >> 4) ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + } + + /* + * Go up two scanline since we + * wrapped-around with ImVfbQRight( ) + */ + vfbptr = ImVfbQUp( vfb, vfbptr ); + vfbptr = ImVfbQUp( vfb, vfbptr ); + } + + break; + case 8: + ImInfo( "Compression Type", "none" ); + + /* + * Read 8-bit non-compressed image + * + * 1 byte contains info for 1 pixel + * + * -------- <-- 1 byte + * 11111111 + * + */ + + /* + * Find scanline size to nearest 32-bit + * boundary and allocate read buffer + * + * Note: bit shifting is used to speed up + * calculations + */ + scanlinesize = x>>2; + if( x%4 ) scanlinesize++; + scanlinesize <<= 2; + + ImMalloc( rbuf, unsigned char*, scanlinesize ); + + /* + * Read in image and store in vfb + */ + for( i = 0; i < y; ++i ) + { + if( ImBinRead( ioType, fd, fp, + rbuf, UCHAR, 1, scanlinesize ) == -1) + { + ImReturnBinError( ); + } + rbufptr = rbuf; + + for( j = 0; j < x; ++j ) + { + ImVfbSIndex8( vfb, vfbptr, + *(rbufptr) ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + + ++rbufptr; + } + + /* + * Go up two scanline since we + * wrapped-around with ImVfbQRight( ) + */ + vfbptr = ImVfbQUp( vfb, vfbptr ); + vfbptr = ImVfbQUp( vfb, vfbptr ); + } + + break; + case 24: + ImInfo( "Compression Type", "none" ); + + /* + * Read 24-bit non-compressed image + * + * 3 bytes contain info for 1 pixel + * + * -------- -------- -------- <-- 3 bytes + * BBBBBBBB GGGGGGGG RRRRRRRR + * + */ + + /* + * Find scanline size to nearest 32-bit + * boundary and allocate read buffer + * + * Note: bit shifting is used to speed up + * calculations + */ + scanlinesize = (x*3)>>2; + if( x%4 ) scanlinesize++; + scanlinesize <<= 2; + + ImMalloc( rbuf, unsigned char*, scanlinesize ); + + /* + * Read in image and store in vfb + */ + for( i = 0; i < y; ++i ) + { + if( ImBinRead( ioType, fd, fp, + rbuf, UCHAR, 1, scanlinesize ) == -1) + { + ImReturnBinError( ); + } + rbufptr = rbuf; + + for( j = 0; j < x; ++j ) + { + ImVfbSBlue( vfb, vfbptr, *(rbufptr) ); + ++rbufptr; + + ImVfbSGreen( vfb, vfbptr, *(rbufptr) ); + ++rbufptr; + + ImVfbSRed( vfb, vfbptr, *(rbufptr) ); + ++rbufptr; + + vfbptr = ImVfbQRight( vfb, vfbptr ); + + } + + /* + * Go up two scanline since we + * wrapped-around with ImVfbQRight( ) + */ + vfbptr = ImVfbQUp( vfb, vfbptr ); + vfbptr = ImVfbQUp( vfb, vfbptr ); + } + + break; + } /* end switch */ + + free( (unsigned char*) rbuf ); + } + else /* RLE decompression for 4-bit or 8-bit images */ + { + switch( header.bmp_bicompress ) + { + case IMBMPRLE4: + if( imBmpReadRLE4( ioType, fd, fp, vfb, y ) == -1 ) + { + return( -1 ); + } + + ImInfo( "Compression Type", + "Run Length Encoded (RLE4)" ); + break; + case IMBMPRLE8: + if( imBmpReadRLE8( ioType, fd, fp, vfb, y ) == -1 ) + { + return( -1 ); + } + ImInfo( "Compression Type", "Run Length Encoded (RLE8)" ); + break; + default: + /* unknown decompression type */ + ImErrorFatal( ImQError( ), -1, IMEIMAGETYPE ); + } /* end switch */ + } + + /* + * Add the VFB to the tagTable. + */ + TagTableAppend( tagTable, + TagEntryAlloc( "image vfb", POINTER, &vfb ) ); + + return ( 2 ); +} + + + + +/* + * FUNCTION + * imBmpWriteRaw1 - write an 1-bit non-compressed BMP file + * + * DESCRIPTION + * That VFB is queried, and the BMP file header set up and written out. + * The VFB data is then converted and written out. + */ + +static int /* Returns # of tags used */ +#ifdef __STDC__ +imBmpWriteRaw1( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + TagTable *flagsTable, TagTable *tagTable ) +#else +imBmpWriteRaw1( pMap, ioType, fd, fp, flagsTable, tagTable ) + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag list to read from */ +#endif +{ + imBmpHeaderInfoWrite header; /* BMP file header */ + ImVfb *vfb; /* Read in image */ + ImVfbPtr vfbptr; /* Vfb pixel pointer */ + imBmpRGBQuad cltentry; /* Colortable RGBQuad entry */ + ImClt *clt; /* Colortable */ + ImCltPtr cltptr; /* Colortable entry pointer */ + unsigned char pvalue; /* Pixel value */ + unsigned char *wbuf; /* Pointer to write buffer */ + unsigned char *wbufptr; /* Pointer to write buffer data */ + unsigned int scanlinesize; /* Length of scanline */ + unsigned int ncolors = 0; /* Number of colors used */ + unsigned char bitfield; /* Bitfield of 8 pixels */ + unsigned int x, y; /* Width, Height */ + int i, j, k; /* Counters */ + char message[100]; /* ImInfo message */ + + + /* + * Set byte ordering + */ + BinByteOrder( BINLBF ); + + /* + * Get vfb and clt + */ + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + clt = ImVfbQClt( vfb ); + + x = ImVfbQWidth( vfb ); + y = ImVfbQHeight( vfb ); + + /* + * Find scanline size to nearest 32-bit + * boundary and allocate write buffer + * + * Note: bit shifting is used to speed up + * calculations + */ + scanlinesize = x>>5; + if( x%32 ) scanlinesize++; + scanlinesize <<= 2; + + if( clt != IMCLTNULL ) + { + ncolors = ImCltQNColors( clt ); + if( ncolors > 2 ) + { + ncolors = 2; + } + } + + /* + * Setup header and write it out + */ + header.bmp_type = IMBMPMAGIC_LBF; + header.bmp_size = (unsigned int) ( IMBMPHEADERSIZE + + ( 2 * IMBMPRGBQUADSIZE ) + + ( scanlinesize * y ) ); + /* + * file size = complete header size + + * colortable size + image data size + */ + header.bmp_reserved1 = 0; + header.bmp_reserved2 = 0; + header.bmp_offsetbits = (unsigned int) IMBMPHEADERSIZE + + ( 2 * IMBMPRGBQUADSIZE ); + /* + * offset to image data = + * BMP header + + * colortable size + */ + header.bmp_bisize = IMBMPINFOHEADERSIZE; + header.bmp_biwidth = x; + header.bmp_biheight = y; + header.bmp_biplanes = 1; + header.bmp_bibitcount = 1; + header.bmp_bicompress = IMBMPRGB; + header.bmp_bisizeimage = scanlinesize * y; + header.bmp_bixpm = 0; + header.bmp_biypm = 0; + header.bmp_biclrused = 0; + header.bmp_biclrim = 0; + + if ( ImBinWriteStruct( ioType, fd, fp, &header, + imBmpHeaderFieldsWrite )==-1) + { + ImReturnBinError( ); + } + + /* + * Output -verbose messages + */ + ImInfo( "Byte Order", "Least Significant Byte First" ); + + sprintf( message, "%d x %d", x, y ); + ImInfo( "Resolution", message ); + + ImInfo( "Type", "1-bit Color Indexed" ); + ImInfo( "Color Table", "2 Entries" ); + ImInfo( "Compression Type", "none" ); + + /* + * Output Color Table + */ + cltentry.reserved = 0; + + cltentry.blue = 0; + cltentry.green = 0; + cltentry.red = 0; + + if( clt != IMCLTNULL ) + { + clt = ImVfbQClt( vfb ); + + cltptr = ImCltQFirst( clt ); + cltentry.blue = (unsigned char) ImCltQBlue( cltptr ); + cltentry.green = (unsigned char) ImCltQGreen( cltptr ); + cltentry.red = (unsigned char) ImCltQRed( cltptr ); + } + if ( ImBinWriteStruct(ioType,fd,fp,&cltentry,imBmpRGBQuadFields )==-1) + { + ImReturnBinError( ); + } + + cltentry.blue = 255; + cltentry.green = 255; + cltentry.red = 255; + + if( clt != IMCLTNULL ) + { + cltptr = ImCltQNext( clt, cltptr ); + cltentry.blue = (unsigned char) ImCltQBlue( cltptr ); + cltentry.green = (unsigned char) ImCltQGreen( cltptr ); + cltentry.red = (unsigned char) ImCltQRed( cltptr ); + } + + if ( ImBinWriteStruct(ioType,fd,fp,&cltentry,imBmpRGBQuadFields )==-1) + { + ImReturnBinError( ); + } + + /* + * Allocate write buffer space + */ + ImCalloc( wbuf, unsigned char*, scanlinesize, sizeof( unsigned char) ); + + /* + * Output image bitmap + */ + + /* + * Go to last vfb scanline + */ + vfbptr = ImVfbQPtr( vfb, 0, y-1 ); + + for( i = 0; i < y; ++i ) + { + wbufptr = wbuf; + for( j = 0; j < (x/8); ++j ) + { + /* + * Pack 8 pixels into a 1 byte bitfield + * + * -------- <-- 1 byte + * 12345678 + * + */ + bitfield = 0; + for( k = 0; k < 8; ++k ) + { + pvalue = (unsigned char) ImVfbQMono( vfb, vfbptr ); + if( pvalue ) { + bitfield = ( bitfield | (1 << (7-k) ) ); + } + vfbptr = ImVfbQRight( vfb, vfbptr ); + } + *( wbufptr ) = bitfield; + ++wbufptr; + } + + if( x%8 ) + { + /* + * Pack 8 pixels into a 1 byte bitfield + * + * -------- <-- 1 byte + * 12345678 + * + */ + bitfield = 0; + for( k = 0; k < x%8; ++k ) + { + pvalue = (unsigned char) ImVfbQMono( vfb, vfbptr ); + if( pvalue ) { + bitfield = ( bitfield | (1 << (7-k) ) ); + } + vfbptr = ImVfbQRight( vfb, vfbptr ); + } + *( wbufptr ) = bitfield; + } + + if( ImBinWrite( ioType,fd,fp,wbuf,UCHAR,1,scanlinesize ) == -1 ) + { + ImReturnBinError( ); + } + + /* + * Go up two scanline since we + * wrapped-around with ImVfbQRight( ) + */ + vfbptr = ImVfbQUp( vfb, vfbptr ); + vfbptr = ImVfbQUp( vfb, vfbptr ); + } + + free( (unsigned char*) wbuf ); + + return ( 1 ); +} + + + + + +/* + * FUNCTION + * imBmpWriteRaw4 - write a 4-bit non-compressed BMP file + * + * DESCRIPTION + * That VFB is queried, and the BMP file header set up and written out. + * The VFB data is then converted and written out. + */ + +static int /* Returns # of tags used */ +#ifdef __STDC__ +imBmpWriteRaw4( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + TagTable *flagsTable, TagTable *tagTable ) +#else +imBmpWriteRaw4( pMap, ioType, fd, fp, flagsTable, tagTable ) + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag list to read from */ +#endif +{ + imBmpHeaderInfoWrite header; /* BMP file header */ + ImVfb *vfb; /* Read in image */ + ImVfbPtr vfbptr; /* Vfb pixel pointer */ + imBmpRGBQuad cltentry; /* Colortable RGBQuad entry */ + ImClt *clt; /* Colortable */ + ImCltPtr cltptr; /* Colortable entry pointer */ + unsigned char pvalue1; /* Pixel value */ + unsigned char pvalue2; /* Pixel value */ + unsigned char *wbuf; /* Pointer to write buffer */ + unsigned char *wbufptr; /* Pointer to write buffer data */ + unsigned int scanlinesize; /* Length of scanline */ + unsigned int ncolors = 0; /* Number of colors used */ + unsigned int x, y; /* Width, Height */ + int i, j; /* Counters */ + char message[100]; /* ImInfo message */ + + + /* + * Set byte ordering + */ + BinByteOrder( BINLBF ); + + /* + * Get vfb and clt + */ + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + ImVfbToIndex( vfb, 16, vfb ); + clt = ImVfbQClt( vfb ); + + x = ImVfbQWidth( vfb ); + y = ImVfbQHeight( vfb ); + + /* + * Find scanline size to nearest 32-bit + * boundary and allocate write buffer + * + * Note: bit shifting is used to speed up + * calculations + */ + scanlinesize = x>>3; + if( x%8 ) scanlinesize++; + scanlinesize <<= 2; + + ncolors = ImCltQNColors( clt ); + if( ncolors > 16 ) + { + ncolors = 16; + } + + /* + * Setup header and write it out + */ + header.bmp_type = IMBMPMAGIC_LBF; + header.bmp_size = (unsigned int) ( IMBMPHEADERSIZE + + ( ncolors * IMBMPRGBQUADSIZE ) + + ( scanlinesize * y ) ); + /* + * file size = complete header size + + * colortable size + image data size + */ + header.bmp_reserved1 = 0; + header.bmp_reserved2 = 0; + header.bmp_offsetbits = (unsigned int) IMBMPHEADERSIZE + + ( ncolors * IMBMPRGBQUADSIZE ); + /* + * offset to image data = + * BMP header + + * colortable size + */ + header.bmp_bisize = IMBMPINFOHEADERSIZE; + header.bmp_biwidth = x; + header.bmp_biheight = y; + header.bmp_biplanes = 1; + header.bmp_bibitcount = 4; + header.bmp_bicompress = IMBMPRGB; + header.bmp_bisizeimage = scanlinesize * y; + header.bmp_bixpm = 0; + header.bmp_biypm = 0; + header.bmp_biclrused = (unsigned int) ncolors; + if( ncolors == 16 ) + { + header.bmp_biclrused = 0; + } + header.bmp_biclrim = (unsigned int) ncolors; + + if ( ImBinWriteStruct( ioType, fd, fp, &header, + imBmpHeaderFieldsWrite )==-1) + { + ImReturnBinError( ); + } + + /* + * Output -verbose messages + */ + ImInfo( "Byte Order", "Least Significant Byte First" ); + + sprintf( message, "%d x %d", x, y ); + ImInfo( "Resolution", message ); + + ImInfo( "Type", "4-bit Color Indexed" ); + + sprintf( message, "%d Entries", ncolors ); + ImInfo( "Color Table", message ); + + ImInfo( "Compression Type", "none" ); + + /* + * Write colortable + */ + cltptr = ImCltQFirst( clt ); + + ImMalloc( wbuf, unsigned char*, ncolors*IMBMPRGBQUADSIZE ); + wbufptr = wbuf; + for( i = 0; i < ncolors; ++i ) + { + *wbufptr = (unsigned char) ImCltQBlue( cltptr ); + ++wbufptr; + *wbufptr = (unsigned char) ImCltQGreen( cltptr ); + ++wbufptr; + *wbufptr = (unsigned char) ImCltQRed( cltptr ); + ++wbufptr; + *wbufptr = 0; + ++wbufptr; + + cltptr = ImCltQNext( clt, cltptr ); + } + if ( ImBinWrite( ioType, fd, fp, wbuf, UCHAR, 1, + ncolors*IMBMPRGBQUADSIZE ) == -1 ) + { + ImReturnBinError( ); + } + free( wbuf ); + + /* + * Allocate write buffer space + */ + ImCalloc( wbuf, unsigned char*, scanlinesize, sizeof( unsigned char) ); + + /* + * Output image bitmap + */ + + /* + * Go to last vfb scanline + */ + vfbptr = ImVfbQPtr( vfb, 0, y-1 ); + + for( i = 0; i < y; ++i ) + { + wbufptr = wbuf; + for( j = 0; j < (x/2); ++j ) + { + /* + * Pack 2 pixels into 1 byte + * + * ---- ---- <-- 1 byte + * 1111 2222 + * + */ + pvalue1 = (unsigned char) ImVfbQIndex8( vfb, vfbptr ); + + /* check for out of range pvalue */ + if( pvalue1 > 15 ) pvalue1 = 15; + + vfbptr = ImVfbQRight( vfb, vfbptr ); + + pvalue2 = (unsigned char) ImVfbQIndex8( vfb, vfbptr ); + + /* check for out of range pvalue */ + if( pvalue2 > 15 ) pvalue2 = 15; + + vfbptr = ImVfbQRight( vfb, vfbptr ); + + *( wbufptr ) = ( ( pvalue1 << 4 ) | pvalue2 ); + ++wbufptr; + } + + if( x%2 ) + { + pvalue1 = (unsigned char) ImVfbQIndex8( vfb, vfbptr ); + + /* check for out of range pvalue */ + if( pvalue1 > 15 ) pvalue1 = 15; + + vfbptr = ImVfbQRight( vfb, vfbptr ); + + *( wbufptr ) = ( pvalue1 << 4 ); + } + + if( ImBinWrite( ioType,fd,fp,wbuf,UCHAR,1,scanlinesize )== -1 ) + { + ImReturnBinError( ); + } + + /* + * Go up two scanline since we + * wrapped-around with ImVfbQRight( ) + */ + vfbptr = ImVfbQUp( vfb, vfbptr ); + vfbptr = ImVfbQUp( vfb, vfbptr ); + } + + free( (unsigned char*) wbuf ); + + return ( 1 ); +} + + + + + +/* + * FUNCTION + * imBmpWriteRaw8 - write an 8-bit non-compressed BMP file + * + * DESCRIPTION + * That VFB is queried, and the BMP file header set up and written out. + * The VFB data is then converted and written out. + */ +static int /* Returns # of tags used */ +#ifdef __STDC__ +imBmpWriteRaw8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + TagTable *flagsTable, TagTable *tagTable ) +#else +imBmpWriteRaw8( pMap, ioType, fd, fp, flagsTable, tagTable ) + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag list to read from */ +#endif +{ + imBmpHeaderInfoWrite header; /* BMP file header */ + ImVfb *vfb; /* Read in image */ + ImVfbPtr vfbptr; /* Vfb pixel pointer */ + ImClt *clt; /* Colortable */ + ImCltPtr cltptr; /* Colortable entry pointer */ + unsigned char *wbuf; /* Pointer to write buffer */ + unsigned char *wbufptr; /* Pointer to write buffer data */ + unsigned int scanlinesize; /* Length of scanline */ + unsigned int ncolors = 0; /* Number of colors used */ + unsigned int x, y; /* Width, Height */ + int i, j; /* Counters */ + char message[100]; /* ImInfo message */ + + + /* + * Set byte ordering + */ + BinByteOrder( BINLBF ); + + /* + * Get vfb and clt + */ + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + clt = ImVfbQClt( vfb ); + + x = ImVfbQWidth( vfb ); + y = ImVfbQHeight( vfb ); + + /* + * Find scanline size to nearest 32-bit + * boundary and allocate write buffer + * + * Note: bit shifting is used to speed up + * calculations + */ + scanlinesize = x>>2; + if( x%4 ) scanlinesize++; + scanlinesize <<= 2; + + ncolors = ImCltQNColors( clt ); + if( ncolors > 256 ) + { + ncolors = 256; + } + + /* + * Setup header and write it out + */ + header.bmp_type = IMBMPMAGIC_LBF; + header.bmp_size = (unsigned int) ( IMBMPHEADERSIZE + + ( ncolors * IMBMPRGBQUADSIZE ) + + ( scanlinesize * y ) ); + /* + * file size = complete header size + + * colortable size + image data size + */ + header.bmp_reserved1 = 0; + header.bmp_reserved2 = 0; + header.bmp_offsetbits = (unsigned int) IMBMPHEADERSIZE + + ( ncolors * IMBMPRGBQUADSIZE ); + /* + * offset to image data = + * BMP header + + * colortable size + */ + header.bmp_bisize = IMBMPINFOHEADERSIZE; + header.bmp_biwidth = x; + header.bmp_biheight = y; + header.bmp_biplanes = 1; + header.bmp_bibitcount = 8; + header.bmp_bicompress = IMBMPRGB; + header.bmp_bisizeimage = scanlinesize * y; + header.bmp_bixpm = 0; + header.bmp_biypm = 0; + header.bmp_biclrused = (unsigned int) ncolors; + if( ncolors == 256 ) + { + header.bmp_biclrused = 0; + } + header.bmp_biclrim = (unsigned int) ncolors; + + if( ImBinWriteStruct( ioType, fd, fp, &header, + imBmpHeaderFieldsWrite ) == -1) + { + ImReturnBinError( ); + } + + /* + * Output -verbose messages + */ + ImInfo( "Byte Order", "Least Significant Byte First" ); + + sprintf( message, "%d x %d", x, y ); + ImInfo( "Resolution", message ); + + ImInfo( "Type", "8-bit Color Indexed" ); + + sprintf( message, "%d Entries", ncolors ); + ImInfo( "Color Table", message ); + + ImInfo( "Compression Type", "none" ); + + /* + * Write colortable + */ + cltptr = ImCltQFirst( clt ); + + ImMalloc( wbuf, unsigned char*, ncolors*IMBMPRGBQUADSIZE ); + wbufptr = wbuf; + for( i = 0; i < ncolors; ++i ) + { + *wbufptr = (unsigned char) ImCltQBlue( cltptr ); + ++wbufptr; + *wbufptr = (unsigned char) ImCltQGreen( cltptr ); + ++wbufptr; + *wbufptr = (unsigned char) ImCltQRed( cltptr ); + ++wbufptr; + *wbufptr = 0; + ++wbufptr; + + cltptr = ImCltQNext( clt, cltptr ); + } + if ( ImBinWrite( ioType, fd, fp, wbuf, UCHAR, 1, + ncolors*IMBMPRGBQUADSIZE ) == -1 ) + { + ImReturnBinError( ); + } + free( wbuf ); + + /* + * Allocate write buffer space + */ + ImCalloc( wbuf, unsigned char*, scanlinesize, sizeof( unsigned char) ); + + /* + * Output image bitmap + */ + + /* + * Go to last vfb scanline + */ + vfbptr = ImVfbQPtr( vfb, 0, y-1 ); + + for( i = 0; i < y; ++i ) + { + wbufptr = wbuf; + for( j = 0; j < x; ++j ) + { + *( wbufptr ) = (unsigned char) ImVfbQIndex8( vfb, vfbptr ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + ++wbufptr; + } + if( ImBinWrite( ioType,fd,fp,wbuf,UCHAR,1,scanlinesize )== -1 ) + { + ImReturnBinError( ); + } + + /* + * Go up two scanline since we + * wrapped-around with ImVfbQRight( ) + */ + vfbptr = ImVfbQUp( vfb, vfbptr ); + vfbptr = ImVfbQUp( vfb, vfbptr ); + } + + free( (unsigned char*) wbuf ); + + return( 1 ); +} + + + + + +/* + * FUNCTION + * imBmpWriteRaw24 - write a 24-bit non-compressed BMP file + * + * DESCRIPTION + * That VFB is queried, and the BMP file header set up and written out. + * The VFB data is then converted and written out. + */ +static int /* Returns # of tags used */ +#ifdef __STDC__ +imBmpWriteRaw24( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + TagTable *flagsTable, TagTable *tagTable ) +#else +imBmpWriteRaw24( pMap, ioType, fd, fp, flagsTable, tagTable ) + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag list to read from */ +#endif +{ + imBmpHeaderInfoWrite header; /* BMP file header */ + ImVfb *vfb; /* Read in image */ + ImVfbPtr vfbptr; /* Vfb pixel pointer */ + unsigned char *wbuf; /* Pointer to write buffer */ + unsigned char *wbufptr; /* Pointer to write buffer data */ + unsigned int scanlinesize; /* Length of scanline */ + unsigned int x, y; /* Width, height */ + int i, j; /* Counters */ + char message[100]; /* ImInfo message */ + + + /* + * Set byte ordering + */ + BinByteOrder( BINLBF ); + + /* + * Get vfb + */ + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + x = ImVfbQWidth( vfb ); + y = ImVfbQHeight( vfb ); + + /* + * Find scanline size to nearest 32-bit + * boundary and allocate write buffer + * + * Note: bit shifting is used to speed up + * calculations + */ + scanlinesize = (x*3)>>2; + if( x%4 ) scanlinesize++; + scanlinesize <<= 2; + + /* + * Setup header and write it out + */ + header.bmp_type = IMBMPMAGIC_LBF; + header.bmp_size = (unsigned int) ( IMBMPHEADERSIZE + + ( scanlinesize * y ) ); + /* + * file size = complete header size + + * image data size + */ + header.bmp_reserved1 = 0; + header.bmp_reserved2 = 0; + header.bmp_offsetbits = IMBMPHEADERSIZE; + /* + * offset to image data = BMP header + */ + header.bmp_bisize = IMBMPINFOHEADERSIZE; + header.bmp_biwidth = x; + header.bmp_biheight = y; + header.bmp_biplanes = 1; + header.bmp_bibitcount = 24; + header.bmp_bicompress = IMBMPRGB; + header.bmp_bisizeimage = scanlinesize * y; + header.bmp_bixpm = 0; + header.bmp_biypm = 0; + header.bmp_biclrused = 0; + header.bmp_biclrim = 0; + + if( ImBinWriteStruct( ioType, fd, fp, &header, + imBmpHeaderFieldsWrite )==-1) + { + ImReturnBinError( ); + } + + /* + * Output -verbose messages + */ + ImInfo( "Byte Order", "Least Significant Byte First" ); + + sprintf( message, "%d x %d", x, y ); + ImInfo( "Resolution", message ); + + ImInfo( "Type", "24-bit RGB" ); + + ImInfo( "Compression Type", "none" ); + + /* + * Allocate write buffer space + */ + ImCalloc( wbuf, unsigned char*, scanlinesize, sizeof( unsigned char) ); + + /* + * Output image bitmap + */ + + /* + * Go to last vfb scanline + */ + vfbptr = ImVfbQPtr( vfb, 0, y-1 ); + + /* + * Output pixels + */ + for( i = 0; i < y; ++i ) + { + wbufptr = wbuf; + for( j = 0; j < x; ++j ) + { + *( wbufptr ) = (unsigned char) ImVfbQBlue( vfb, vfbptr ); + ++wbufptr; + + *( wbufptr ) = (unsigned char) ImVfbQGreen( vfb, vfbptr ); + ++wbufptr; + + *( wbufptr ) = (unsigned char) ImVfbQRed( vfb, vfbptr ); + ++wbufptr; + + vfbptr = ImVfbQRight( vfb, vfbptr ); + } + if( ImBinWrite( ioType,fd,fp,wbuf,UCHAR,1,scanlinesize )== -1 ) + { + ImReturnBinError( ); + } + + /* + * Go up two scanline since we + * wrapped-around with ImVfbQRight( ) + */ + vfbptr = ImVfbQUp( vfb, vfbptr ); + vfbptr = ImVfbQUp( vfb, vfbptr ); + } + + free( (unsigned char*) wbuf ); + + return( 1 ); +} + + +#define imBmpWrite2Bytes( arg1, arg2 ) \ + \ +buf[ bufin ] = arg1; \ +++bufin; \ +buf[ bufin ] = arg2; \ +++bufin; \ +if( bufin > (IMBLKSIZE-1) ) \ +{ \ + if( ImBinWrite( ioType,fd,fp,buf,UCHAR,1,IMBLKSIZE )==-1) \ + { \ + ImReturnBinError( ); \ + } \ + bufin = 0; \ +} \ + + + + + + +/* + * FUNCTION + * imBmpWriteRLE4 - write a 4-bit RLE compressed BMP file + * + * DESCRIPTION + * That VFB is queried, and the BMP file header set up and written out. + * The VFB data is then converted and written out. See encoding + * scheme in format section. + */ +static int /* Returns # of tags used */ +#ifdef __STDC__ +imBmpWriteRLE4( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + TagTable *flagsTable, TagTable *tagTable ) +#else +imBmpWriteRLE4( pMap, ioType, fd, fp, flagsTable, tagTable ) + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag list to read from */ +#endif +{ + imBmpHeaderInfoWrite header; /* BMP file header */ + ImVfb *vfb; /* Read in image */ + ImVfbPtr vfbptr; /* Vfb pixel pointer */ + ImClt *clt; /* Colortable */ + ImCltPtr cltptr; /* Colortable entry pointer */ + unsigned int ncolors = 0; /* Number of colors used */ + unsigned int x, y; /* Width, Height */ + unsigned int run; /* Number of consecutive bytes */ + unsigned char pixel; /* Temp pixel value */ + unsigned int bytecount = 0; /* Bytes in compressed image */ + unsigned int nowrite; /* Write status */ + unsigned char pixbuf[ 256 ]; /* Pixel buffer */ + unsigned char *cbuf; /* Buffer for colortable write */ + unsigned char *cbufptr; /* Buffer pointer */ + unsigned char buf[ IMBLKSIZE ];/* File write output buffer */ + int bufin; /* Output buffer index */ + int xcount, ycount; /* Counters */ + int i; /* Counters */ + int count; /* Counter */ + char message[100]; /* ImInfo message */ + + + /* + * Set byte ordering + */ + BinByteOrder( BINLBF ); + + /* + * Get vfb and clt + */ + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + ImVfbToIndex( vfb, 16, vfb ); + clt = ImVfbQClt( vfb ); + + x = ImVfbQWidth( vfb ); + y = ImVfbQHeight( vfb ); + + ncolors = ImCltQNColors( clt ); + if( ncolors > 16 ) + { + ncolors = 16; + } + + /* + * Setup header and write it out + */ + header.bmp_type = IMBMPMAGIC_LBF; + header.bmp_reserved1 = 0; + header.bmp_reserved2 = 0; + header.bmp_offsetbits = (unsigned int) IMBMPHEADERSIZE + + ( ncolors * IMBMPRGBQUADSIZE ); + /* + * offset to image data = + * BMP header + + * colortable size + */ + header.bmp_bisize = IMBMPINFOHEADERSIZE; + header.bmp_biwidth = x; + header.bmp_biheight = y; + header.bmp_biplanes = 1; + header.bmp_bibitcount = 4; + header.bmp_bicompress = IMBMPRLE4; + header.bmp_bixpm = 0; + header.bmp_biypm = 0; + header.bmp_biclrused = (unsigned int) ncolors; + if( ncolors == 16 ) + { + header.bmp_biclrused = 0; + } + header.bmp_biclrim = (unsigned int) ncolors; + + if( ImBinWriteStruct( ioType, fd, fp, &header, + imBmpHeaderFieldsWrite ) == -1 ) + { + ImReturnBinError( ); + } + + /* + * Output -verbose messages + */ + ImInfo( "Byte Order", "Least Significant Byte First" ); + + sprintf( message, "%d x %d", x, y ); + ImInfo( "Resolution", message ); + + ImInfo( "Type", "4-bit Color Indexed" ); + + sprintf( message, "%d Entries", ncolors ); + ImInfo( "Color Table", message ); + + ImInfo( "Compression Type", "Run Length Encoded (RLE4)" ); + + /* + * Write colortable + */ + cltptr = ImCltQFirst( clt ); + + ImMalloc( cbuf, unsigned char*, ncolors*IMBMPRGBQUADSIZE ); + cbufptr = cbuf; + for( i = 0; i < ncolors; ++i ) + { + *cbufptr = (unsigned char) ImCltQBlue( cltptr ); + ++cbufptr; + *cbufptr = (unsigned char) ImCltQGreen( cltptr ); + ++cbufptr; + *cbufptr = (unsigned char) ImCltQRed( cltptr ); + ++cbufptr; + *cbufptr = 0; + ++cbufptr; + + cltptr = ImCltQNext( clt, cltptr ); + } + if ( ImBinWrite( ioType, fd, fp, cbuf, UCHAR, 1, + ncolors*IMBMPRGBQUADSIZE ) == -1 ) + { + ImReturnBinError( ); + } + free( cbuf ); + + /* + * Output image bitmap + */ + + /* + * Go to last vfb scanline + */ + vfbptr = ImVfbQPtr( vfb, 0, y-1 ); + + /* + * RLE encode the image + */ + bytecount = 0; + xcount = 0; + ycount = 0; + bufin = 0; + pixel = ImVfbQIndex8( vfb, vfbptr ); + + /* check for out of range pixel */ + if( pixel > 15 ) pixel = 15; + + vfbptr = ImVfbQRight( vfb, vfbptr ); + while( ycount < y ) + { + /* + * Boundary condition: 1 pixel at the end + * of a scanline. + */ + if( xcount == (x-1) ) + { + /* + * Encoded pixel as run of 1 + */ + imBmpWrite2Bytes( 1, pixel << 4 ); + + /* + * Write end of scanline code + */ + imBmpWrite2Bytes( 0, 0 ); + + /* + * Adjust counters and vfb pointers + */ + bytecount += 4; + xcount = 0; + ++ycount; + vfbptr = ImVfbQUp( vfb, vfbptr ); + vfbptr = ImVfbQUp( vfb, vfbptr ); + pixel = ImVfbQIndex8( vfb, vfbptr ); + + /* check for out of range pixel */ + if( pixel > 15 ) pixel = 15; + + vfbptr = ImVfbQRight( vfb, vfbptr ); + } + + /* + * Boundary condition: 2 pixels at the end + * of a scanline. + */ + if( xcount == (x-2) ) + { + /* + * Write the last two pixels as a run of two + */ + imBmpWrite2Bytes( 2, ( pixel << 4 ) + | ImVfbQIndex8(vfb,vfbptr) ); + + /* + * Write end of scanline code + */ + imBmpWrite2Bytes( 0, 0 ); + + /* + * Adjust counters and vfb pointers + */ + bytecount += 4; + xcount = 0; + ++ycount; + vfbptr = ImVfbQRight( vfb, vfbptr ); + vfbptr = ImVfbQUp( vfb, vfbptr ); + vfbptr = ImVfbQUp( vfb, vfbptr ); + pixel = ImVfbQIndex8( vfb, vfbptr ); + + /* check for out of range pixel */ + if( pixel > 15 ) pixel = 15; + + vfbptr = ImVfbQRight( vfb, vfbptr ); + } + + /* + * Encoded Mode - encoding of multiple consecutive + * pixels of the same value into a run and + * pixel value + */ + if( pixel == ImVfbQIndex8( vfb, vfbptr ) ) + { + run = 0; + nowrite = TRUE; + + /* + * While two adjacent pixels are the same, + * count them up. If we reach the + * end of a scanline, write out the + * current run and pixel. If we + * have a run of 255 pixels, write + * out the run and pixel. + */ + while( pixel == ImVfbQIndex8( vfb, vfbptr ) ) + { + ++run; + ++xcount; + pixel = ImVfbQIndex8( vfb, vfbptr ); + + /* check for out of range pixel */ + if( pixel > 15 ) pixel = 15; + + vfbptr = ImVfbQRight( vfb, vfbptr ); + + /* + * End of a scanline + */ + if( xcount == x-1 ) + { + /* + * Write run and pixel value + */ + imBmpWrite2Bytes( run+1, (pixel<<4) + | pixel ); + + /* + * Write end of scanline code + */ + imBmpWrite2Bytes( 0, 0 ); + + /* + * Adjust counters and vfb pointers + */ + bytecount += 4; + xcount = 0; + ++ycount; + vfbptr = ImVfbQUp( vfb, vfbptr ); + vfbptr = ImVfbQUp( vfb, vfbptr ); + pixel = ImVfbQIndex8( vfb, vfbptr ); + + /* check for out of range pixel */ + if( pixel > 15 ) pixel = 15; + + vfbptr = ImVfbQRight( vfb, vfbptr ); + nowrite = FALSE; + break; + } + + /* + * End of 255 pixels + */ + if( run == 254 ) + { + /* + * Write run and pixel value + */ + imBmpWrite2Bytes( 255, (pixel<<4) + | pixel ); + + /* + * Adjust counters and vfb pointers + */ + bytecount += 2; + run = 0; + ++xcount; + pixel = ImVfbQIndex8( vfb, vfbptr ); + + /* check for out of range pixel */ + if( pixel > 15 ) pixel = 15; + + vfbptr = ImVfbQRight( vfb, vfbptr ); + nowrite = FALSE; + break; + } + } + + /* + * If we haven't written out the run and pixel, + * do it now. + */ + if( nowrite == TRUE ) + { + /* + * Write run and pixel + */ + imBmpWrite2Bytes( run+1, (pixel<<4) | pixel ); + + /* + * Adjust counters and vfb pointers + */ + bytecount += 2; + ++xcount; + pixel = ImVfbQIndex8( vfb, vfbptr ); + + /* check for out of range pixel */ + if( pixel > 15 ) pixel = 15; + + vfbptr = ImVfbQRight( vfb, vfbptr ); + } + } + + /* + * Absolute Mode - copying of consecutive different pixels + * literally + */ + if( (pixel != ImVfbQIndex8( vfb, vfbptr )) + && (xcount < (x-2) ) + && (ycount < y) ) + { + run = 0; + nowrite = TRUE; + + pixbuf[ 0 ] = pixel; + pixel = ImVfbQIndex8( vfb, vfbptr ); + + /* check for out of range pixel */ + if( pixel > 15 ) pixel = 15; + + vfbptr = ImVfbQRight( vfb, vfbptr ); + ++xcount; + + /* + * Since Absolute Mode requires that there + * be at least 3 different adjacent + * pixels, we check for this condition + * first. If there are only 1 or 2 + * different adjacent bytes, then + * we write them out as runs of 1 + * and pixel. + */ + if( pixel == ImVfbQIndex8( vfb, vfbptr ) ) + { + /* + * There is only 1 different pixel + */ + + /* + * Write out run and pixel + */ + imBmpWrite2Bytes( 1, pixbuf[ 0 ] << 4 ); + + bytecount += 2; + nowrite = FALSE; + } + else + { + pixbuf[ 1 ] = pixel; + pixel = ImVfbQIndex8( vfb, vfbptr ); + + /* check for out of range pixel */ + if( pixel > 15 ) pixel = 15; + + vfbptr = ImVfbQRight( vfb, vfbptr ); + ++xcount; + if( pixel == ImVfbQIndex8( vfb, vfbptr ) ) + { + /* + * There are only 2 different + * adjacent pixels + */ + + /* + * Write run and pixel + */ + imBmpWrite2Bytes( 2, (pixbuf[ 0 ]<<4) + | pixbuf[ 1 ]); + + bytecount += 2; + nowrite = FALSE; + } + else + { + /* + * There are at least 3 different + * adjacent pixels, so we reset + * the vfb pointers + */ + xcount -= 2; + vfbptr = ImVfbQLeft( vfb, vfbptr ); + vfbptr = ImVfbQLeft( vfb, vfbptr ); + vfbptr = ImVfbQLeft( vfb, vfbptr ); + pixel = ImVfbQIndex8( vfb, vfbptr ); + + /* check for out of range pixel */ + if( pixel > 15 ) pixel = 15; + + vfbptr = ImVfbQRight( vfb, vfbptr ); + } + } + + /* + * While adjacent pixels are different, count them + * up and store them in pixbuf. + */ + while( pixel != ImVfbQIndex8( vfb, vfbptr ) ) + { + pixbuf[ run ] = pixel; + pixel = ImVfbQIndex8( vfb, vfbptr ); + + /* check for out of range pixel */ + if( pixel > 15 ) pixel = 15; + + vfbptr = ImVfbQRight( vfb, vfbptr ); + ++run; + ++xcount; + + /* + * End of scanline + */ + if( xcount == x-1 ) + { + pixbuf[ run ] = pixel; + pixbuf[ run+1 ] = 0; + + /* + * Write escape(0) and run + */ + imBmpWrite2Bytes( 0, run+1 ); + + /* + * Write pixels in pixbuf + */ + count = 0; + for( i = 0; i < run+1; i += 2) + { + buf[ bufin ] = (pixbuf[ i ]<<4) + | pixbuf[ i+1 ]; + ++bufin; + if( bufin > (IMBLKSIZE-1) ) + { + if( ImBinWrite( ioType, + fd,fp,buf, + UCHAR,1, + IMBLKSIZE )==-1) + { + ImReturnBinError( ); + } + bufin = 0; + } + ++count; + } + bytecount += count; + + /* + * Pad to word boundary if necessary + */ + if( (count%2) != 0 ) + { + buf[ bufin ] = 0; + ++bufin; + if( bufin > (IMBLKSIZE-1) ) + { + if( ImBinWrite( ioType, + fd,fp,buf, + UCHAR,1, + IMBLKSIZE )==-1) + { + ImReturnBinError( ); + } + bufin = 0; + } + ++bytecount; + } + + /* + * Write end of scanline code + */ + imBmpWrite2Bytes( 0, 0 ); + + /* + * Adjust counters and vfb pointers + */ + bytecount += 4; + xcount = 0; + ++ycount; + vfbptr = ImVfbQUp( vfb, vfbptr ); + vfbptr = ImVfbQUp( vfb, vfbptr ); + pixel = ImVfbQIndex8( vfb, vfbptr ); + + /* check for out of range pixel */ + if( pixel > 15 ) pixel = 15; + + vfbptr = ImVfbQRight( vfb, vfbptr ); + nowrite = FALSE; + break; + } + + /* + * End of 255 consecutive different pixels + */ + if( run == 254 ) + { + pixbuf[ run ] = pixel; + pixbuf[ run+1 ] = 0; + + /* + * Write escape code(0) and run + */ + imBmpWrite2Bytes( 0, 255 ); + + /* + * Write pixel literals + */ + for( i = 0; i < 255; i += 2 ) + { + buf[ bufin ] = (pixbuf[ i ]<<4) + | pixbuf[ i+1 ]; + ++bufin; + if( bufin > (IMBLKSIZE-1) ) + { + if( ImBinWrite( ioType, + fd,fp,buf, + UCHAR,1, + IMBLKSIZE )==-1) + { + ImReturnBinError( ); + } + bufin = 0; + } + } + + /* + * Adjust counters and vfb pointers + */ + bytecount += 130; + run = 0; + ++xcount; + pixel = ImVfbQIndex8( vfb, vfbptr ); + + /* check for out of range pixel */ + if( pixel > 15 ) pixel = 15; + + vfbptr = ImVfbQRight( vfb, vfbptr ); + nowrite = FALSE; + break; + } + } + + /* + * If we haven't written out the pixels, then + * write it now + */ + if( nowrite == TRUE ) + { + pixbuf[ run ] = 0; + + /* + * Write escape code(0) and run + */ + imBmpWrite2Bytes( 0, run ); + + /* + * Write pixel literals + */ + count = 0; + for( i = 0; i < run; i += 2 ) + { + buf[ bufin ] = (pixbuf[ i ]<<4) + | pixbuf[ i+1 ]; + ++bufin; + if( bufin > (IMBLKSIZE-1) ) + { + if( ImBinWrite( ioType,fd,fp + ,buf,UCHAR,1 + ,IMBLKSIZE )==-1) + { + ImReturnBinError( ); + } + bufin = 0; + } + ++count; + } + bytecount += count; + + /* + * Pad to word boundary if necessary + */ + if( count%2 != 0 ) + { + buf[ bufin ] = 0; + ++bufin; + if( bufin > (IMBLKSIZE-1) ) + { + if( ImBinWrite( ioType, + fd,fp,buf, + UCHAR,1, + IMBLKSIZE )==-1) + { + ImReturnBinError( ); + } + bufin = 0; + } + ++bytecount; + } + + /* + * Adjust bytecount + */ + bytecount += 2; + } + } + } + + /* + * Write end of bitmap code + */ + buf[ bufin ] = 0; + ++bufin; + buf[ bufin ] = 1; + ++bufin; + if( ImBinWrite( ioType,fd,fp,buf,UCHAR,1,bufin )==-1) + { + ImReturnBinError( ); + } + + /* + * Adjust bytecount + */ + bytecount += 2; + + /* + * Seek to header and write file size + */ + if( ImSeek( ioType, fd, fp, 2, L_SET ) == -1 ) + { + ImReturnBinError( ); + } + header.bmp_size = IMBMPHEADERSIZE + ( ncolors * IMBMPRGBQUADSIZE ) + + bytecount; + if( ImBinWrite( ioType, fd, fp, &header.bmp_size, UINT32, 4, 1) == -1 ) + { + ImReturnBinError( ); + } + + /* + * Seek to header and write bitmap size + */ + if( ImSeek( ioType, fd, fp, 34, L_SET ) == -1 ) + { + ImReturnBinError( ); + } + header.bmp_bisizeimage = bytecount; + if( ImBinWrite( ioType, fd, fp, &header.bmp_bisizeimage, + UINT32, 4, 1) == -1 ) + { + ImReturnBinError( ); + } + + return( 1 ); +} + + + + + +/* + * FUNCTION + * imBmpWriteRLE8 - write an 8-bit RLE compressed BMP file + * + * DESCRIPTION + * That VFB is queried, and the BMP file header set up and written out. + * The VFB data is then converted and written out. See encoding + * scheme in FORMAT section. + */ +static int /* Returns # of tags used */ +#ifdef __STDC__ +imBmpWriteRLE8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + TagTable *flagsTable, TagTable *tagTable ) +#else +imBmpWriteRLE8( pMap, ioType, fd, fp, flagsTable, tagTable ) + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag list to read from */ +#endif +{ + imBmpHeaderInfoWrite header; /* BMP file header */ + ImVfb *vfb; /* Read in image */ + ImVfbPtr vfbptr; /* Vfb pixel pointer */ + ImClt *clt; /* Colortable */ + ImCltPtr cltptr; /* Colortable entry pointer */ + unsigned int ncolors = 0; /* Number of colors used */ + unsigned int x, y; /* Width, Height */ + unsigned int run; /* Number of consecutive bytes */ + unsigned char pixel; /* Temp pixel value */ + unsigned int bytecount = 0; /* Bytes in compressed image */ + unsigned int nowrite; /* Write status */ + unsigned char pixbuf[ 255 ]; /* Pixel buffer */ + unsigned char *cbuf; /* Buffer for colortable write */ + unsigned char *cbufptr; /* Buffer pointer */ + unsigned char buf[ IMBLKSIZE ];/* File write output buffer */ + int bufin; /* Output buffer index */ + int xcount, ycount; /* Counters */ + int i; /* Counters */ + char message[100]; /* ImInfo message */ + + + /* + * Set byte ordering + */ + BinByteOrder( BINLBF ); + + /* + * Get vfb and clt + */ + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + clt = ImVfbQClt( vfb ); + + x = ImVfbQWidth( vfb ); + y = ImVfbQHeight( vfb ); + + ncolors = ImCltQNColors( clt ); + if( ncolors > 256 ) + { + ncolors = 256; + } + + /* + * Setup header and write it out + */ + header.bmp_type = IMBMPMAGIC_LBF; + header.bmp_reserved1 = 0; + header.bmp_reserved2 = 0; + header.bmp_offsetbits = (unsigned int) IMBMPHEADERSIZE + + ( ncolors * IMBMPRGBQUADSIZE ); + /* + * offset to image data = + * BMP header + + * colortable size + */ + header.bmp_bisize = IMBMPINFOHEADERSIZE; + header.bmp_biwidth = x; + header.bmp_biheight = y; + header.bmp_biplanes = 1; + header.bmp_bibitcount = 8; + header.bmp_bicompress = IMBMPRLE8; + header.bmp_bixpm = 0; + header.bmp_biypm = 0; + header.bmp_biclrused = (unsigned int) ncolors; + if( ncolors == 256 ) + { + header.bmp_biclrused = 0; + } + header.bmp_biclrim = (unsigned int) ncolors;; + + if( ImBinWriteStruct( ioType, fd, fp, &header, + imBmpHeaderFieldsWrite ) == -1 ) + { + ImReturnBinError( ); + } + + /* + * Output -verbose messages + */ + ImInfo( "Byte Order", "Least Significant Byte First" ); + + sprintf( message, "%d x %d", x, y ); + ImInfo( "Resolution", message ); + + ImInfo( "Type", "8-bit Color Indexed" ); + + sprintf( message, "%d Entries", ncolors ); + ImInfo( "Color Table", message ); + + ImInfo( "Compression Type", "Run Length Encoded (RLE8)" ); + + /* + * Write colortable + */ + cltptr = ImCltQFirst( clt ); + + ImMalloc( cbuf, unsigned char*, ncolors*IMBMPRGBQUADSIZE ); + cbufptr = cbuf; + for( i = 0; i < ncolors; ++i ) + { + *cbufptr = (unsigned char) ImCltQBlue( cltptr ); + ++cbufptr; + *cbufptr = (unsigned char) ImCltQGreen( cltptr ); + ++cbufptr; + *cbufptr = (unsigned char) ImCltQRed( cltptr ); + ++cbufptr; + *cbufptr = 0; + ++cbufptr; + + cltptr = ImCltQNext( clt, cltptr ); + } + if ( ImBinWrite( ioType, fd, fp, cbuf, UCHAR, 1, + ncolors*IMBMPRGBQUADSIZE ) == -1 ) + { + ImReturnBinError( ); + } + free( cbuf ); + + /* + * Output image bitmap + */ + + /* + * Go to last vfb scanline + */ + vfbptr = ImVfbQPtr( vfb, 0, y-1 ); + + /* + * RLE encode the image + */ + bytecount = 0; + xcount = 0; + ycount = 0; + bufin = 0; + pixel = ImVfbQIndex8( vfb, vfbptr ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + while( ycount < y ) + { + /* + * Boundary condition: 1 pixel at the end + * of a scanline. + */ + if( xcount == (x-1) ) + { + /* + * Encoded pixel as run of 1 + */ + imBmpWrite2Bytes( 1, pixel ); + + /* + * Write end of scanline code + */ + imBmpWrite2Bytes( 0, 0 ); + + /* + * Adjust counters and vfb pointers + */ + bytecount += 4; + xcount = 0; + ++ycount; + vfbptr = ImVfbQUp( vfb, vfbptr ); + vfbptr = ImVfbQUp( vfb, vfbptr ); + pixel = ImVfbQIndex8( vfb, vfbptr ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + } + + /* + * Boundary condition: 2 pixels at the end + * of a scanline. + */ + if( xcount == (x-2) ) + { + /* + * If the last two pixels are the same + * then write them as a run of 2 with + * pixel value. Else, write each + * pixel value as runs of 1. + */ + if( pixel == ImVfbQIndex8( vfb, vfbptr ) ) + { + imBmpWrite2Bytes( 2, pixel ); + bytecount += 4; + } + else + { + imBmpWrite2Bytes( 1, pixel ); + imBmpWrite2Bytes( 1, ImVfbQIndex8(vfb,vfbptr)); + bytecount += 6; + } + + /* + * Write end of scanline code + */ + imBmpWrite2Bytes( 0, 0 ); + + /* + * Adjust counters and vfb pointers + */ + xcount = 0; + ++ycount; + vfbptr = ImVfbQRight( vfb, vfbptr ); + vfbptr = ImVfbQUp( vfb, vfbptr ); + vfbptr = ImVfbQUp( vfb, vfbptr ); + pixel = ImVfbQIndex8( vfb, vfbptr ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + } + + /* + * Encoded Mode - encoding of multiple consecutive + * pixels of the same value into a run and + * pixel value + */ + if( pixel == ImVfbQIndex8( vfb, vfbptr ) ) + { + run = 0; + nowrite = TRUE; + + /* + * While two adjacent pixels are the same, + * count them up. If we reach the + * end of a scanline, write out the + * current run and pixel. If we + * have a run of 255 pixels, write + * out the run and pixel. + */ + while( pixel == ImVfbQIndex8( vfb, vfbptr ) ) + { + ++run; + ++xcount; + pixel = ImVfbQIndex8( vfb, vfbptr ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + + /* + * End of a scanline + */ + if( xcount == x-1 ) + { + /* + * Write run and pixel value + */ + imBmpWrite2Bytes( run+1, pixel ); + + /* + * Write end of scanline code + */ + imBmpWrite2Bytes( 0, 0 ); + + /* + * Adjust counters and vfb pointers + */ + bytecount += 4; + xcount = 0; + ++ycount; + vfbptr = ImVfbQUp( vfb, vfbptr ); + vfbptr = ImVfbQUp( vfb, vfbptr ); + pixel = ImVfbQIndex8( vfb, vfbptr ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + nowrite = FALSE; + break; + } + + /* + * End of 255 pixels + */ + if( run == 254 ) + { + /* + * Write run and pixel value + */ + imBmpWrite2Bytes( 255, pixel ); + + /* + * Adjust counters and vfb pointers + */ + bytecount += 2; + run = 0; + ++xcount; + pixel = ImVfbQIndex8( vfb, vfbptr ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + nowrite = FALSE; + break; + } + } + + /* + * If we haven't written out the run and pixel, + * do it now. + */ + if( nowrite == TRUE ) + { + /* + * Write run and pixel + */ + imBmpWrite2Bytes( run+1, pixel ); + + /* + * Adjust counters and vfb pointers + */ + bytecount += 2; + ++xcount; + pixel = ImVfbQIndex8( vfb, vfbptr ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + } + } + + /* + * Absolute Mode - copying of consecutive different pixels + * literally + */ + if( (pixel != ImVfbQIndex8( vfb, vfbptr )) + && (xcount < (x-2) ) + && (ycount < y) ) + { + run = 0; + nowrite = TRUE; + + pixbuf[ 0 ] = pixel; + pixel = ImVfbQIndex8( vfb, vfbptr ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + ++xcount; + + /* + * Since Absolute Mode requires that there + * be at least 3 different adjacent + * pixels, we check for this condition + * first. If there are only 1 or 2 + * different adjacent bytes, then + * we write them out as runs of 1 + * and pixel. + */ + if( pixel == ImVfbQIndex8( vfb, vfbptr ) ) + { + /* + * There is only 1 different pixel + */ + + /* + * Write out run and pixel + */ + imBmpWrite2Bytes( 1, pixbuf[ 0 ] ); + + bytecount += 2; + nowrite = FALSE; + } + else + { + pixbuf[ 1 ] = pixel; + pixel = ImVfbQIndex8( vfb, vfbptr ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + ++xcount; + if( pixel == ImVfbQIndex8( vfb, vfbptr ) ) + { + /* + * There are only 2 different + * adjacent pixels + */ + + /* + * Write run and pixel + */ + imBmpWrite2Bytes( 1, pixbuf[ 0 ] ); + + /* + * Write run and pixel + */ + imBmpWrite2Bytes( 1, pixbuf[ 1 ] ); + + bytecount += 4; + nowrite = FALSE; + } + else + { + /* + * There are at least 3 different + * adjacent pixels, so we reset + * the vfb pointers + */ + xcount -= 2; + vfbptr = ImVfbQLeft( vfb, vfbptr ); + vfbptr = ImVfbQLeft( vfb, vfbptr ); + vfbptr = ImVfbQLeft( vfb, vfbptr ); + pixel = ImVfbQIndex8( vfb, vfbptr ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + } + } + + /* + * While adjacent pixels are different, count them + * up and store them in pixbuf. + */ + while( pixel != ImVfbQIndex8( vfb, vfbptr ) ) + { + pixbuf[ run ] = pixel; + pixel = ImVfbQIndex8( vfb, vfbptr ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + ++run; + ++xcount; + + /* + * End of scanline + */ + if( xcount == x-1 ) + { + pixbuf[ run ] = pixel; + + /* + * Write escape(0) and run + */ + imBmpWrite2Bytes( 0, run+1 ); + + /* + * Write pixels in pixbuf + */ + for( i = 0; i < run+1; ++i ) + { + buf[ bufin ] = pixbuf[ i ]; + ++bufin; + if( bufin > (IMBLKSIZE-1) ) + { + if( ImBinWrite( ioType, + fd,fp,buf, + UCHAR,1, + IMBLKSIZE )==-1) + { + ImReturnBinError( ); + } + bufin = 0; + } + } + + /* + * Pad to word boundary if necessary + */ + if( ((run+1)%2) != 0 ) + { + buf[ bufin ] = 0; + ++bufin; + if( bufin > (IMBLKSIZE-1) ) + { + if( ImBinWrite( ioType, + fd,fp,buf, + UCHAR,1, + IMBLKSIZE )==-1) + { + ImReturnBinError( ); + } + bufin = 0; + } + ++bytecount; + } + + /* + * Write end of scanline code + */ + imBmpWrite2Bytes( 0, 0 ); + + /* + * Adjust counters and vfb pointers + */ + bytecount += (5+run); + xcount = 0; + ++ycount; + vfbptr = ImVfbQUp( vfb, vfbptr ); + vfbptr = ImVfbQUp( vfb, vfbptr ); + pixel = ImVfbQIndex8( vfb, vfbptr ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + nowrite = FALSE; + break; + } + + /* + * End of 255 consecutive different pixels + */ + if( run == 254 ) + { + pixbuf[ run ] = pixel; + + /* + * Write escape code(0) and run + */ + imBmpWrite2Bytes( 0, 255 ); + + /* + * Write pixel literals + */ + for( i = 0; i < 255; ++i ) + { + buf[ bufin ] = pixbuf[ i ]; + ++bufin; + if( bufin > (IMBLKSIZE-1) ) + { + if( ImBinWrite( ioType, + fd,fp,buf, + UCHAR,1, + IMBLKSIZE )==-1) + { + ImReturnBinError( ); + } + bufin = 0; + } + } + + /* + * Write word boundary padding + */ + buf[ bufin ] = 0; + ++bufin; + if( bufin > (IMBLKSIZE-1) ) + { + if( ImBinWrite( ioType, + fd,fp,buf, + UCHAR,1, + IMBLKSIZE )==-1) + { + ImReturnBinError( ); + } + bufin = 0; + } + + /* + * Adjust counters and vfb pointers + */ + bytecount += 258; + run = 0; + ++xcount; + pixel = ImVfbQIndex8( vfb, vfbptr ); + vfbptr = ImVfbQRight( vfb, vfbptr ); + nowrite = FALSE; + break; + } + } + + /* + * If we haven't written out the pixels, then + * write it now + */ + if( nowrite == TRUE ) + { + /* + * Write escape code(0) and run + */ + imBmpWrite2Bytes( 0, run ); + + /* + * Write pixel literals + */ + for( i = 0; i < run; ++i ) + { + buf[ bufin ] = pixbuf[ i ]; + ++bufin; + if( bufin > (IMBLKSIZE-1) ) + { + if( ImBinWrite( ioType,fd,fp + ,buf,UCHAR,1 + ,IMBLKSIZE )==-1) + { + ImReturnBinError( ); + } + bufin = 0; + } + } + + /* + * Write padding to word boundary if + * necessary + */ + if( run%2 != 0 ) + { + buf[ bufin ] = 0; + ++bufin; + if( bufin > (IMBLKSIZE-1) ) + { + if( ImBinWrite( ioType,fd,fp + ,buf,UCHAR,1 + ,IMBLKSIZE )==-1) + { + ImReturnBinError( ); + } + bufin = 0; + } + ++bytecount; + } + + /* + * Adjust bytecount + */ + bytecount += (2+run); + } + } + } + + /* + * Write end of bitmap code + */ + buf[ bufin ] = 0; + ++bufin; + buf[ bufin ] = 1; + ++bufin; + if( ImBinWrite( ioType,fd,fp,buf,UCHAR,1,bufin )==-1) + { + ImReturnBinError( ); + } + + /* + * Adjust bytecount + */ + bytecount += 2; + + /* + * Seek to header and write file size + */ + if( ImSeek( ioType, fd, fp, 2, L_SET ) == -1 ) + { + ImReturnBinError( ); + } + header.bmp_size = IMBMPHEADERSIZE + ( ncolors * IMBMPRGBQUADSIZE ) + + bytecount; + if( ImBinWrite( ioType, fd, fp, &header.bmp_size, UINT32, 4, 1) == -1 ) + { + ImReturnBinError( ); + } + + /* + * Seek to header and write bitmap size + */ + if( ImSeek( ioType, fd, fp, 34, L_SET ) == -1 ) + { + ImReturnBinError( ); + } + header.bmp_bisizeimage = bytecount; + if( ImBinWrite( ioType, fd, fp, &header.bmp_bisizeimage, + UINT32, 4, 1) == -1 ) + { + ImReturnBinError( ); + } + + return( 1 ); +} diff --git a/utils/roq2/libim/imclt.c b/utils/roq2/libim/imclt.c new file mode 100644 index 0000000..0fb2ce9 --- /dev/null +++ b/utils/roq2/libim/imclt.c @@ -0,0 +1,527 @@ +/** + ** $Header: /roq/libim/imclt.c 1 11/02/99 4:38p Zaphod $ + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + ** a division of General Atomics, San Diego, California, USA + ** + ** Users and possessors of this source code are hereby granted a + ** nonexclusive, royalty-free copyright and design patent license to + ** use this code in individual software. License is not granted for + ** commercial resale, in whole or in part, without prior written + ** permission from SDSC. This source is provided "AS IS" without express + ** or implied warranty of any kind. + ** + ** For further information contact: + ** E-Mail: info@sds.sdsc.edu + ** + ** Surface Mail: Information Center + ** San Diego Supercomputer Center + ** P.O. Box 85608 + ** San Diego, CA 92138-5608 + ** (619) 534-5000 + **/ + +#define HEADER " $Header: /roq/libim/imclt.c 1 11/02/99 4:38p Zaphod $" + +/** + ** FILE + ** imclt.c - general CLT functions + ** + ** PROJECT + ** libim - SDSC image manipulation library + ** + ** DESCRIPTION + ** imclt.c contains functions for generating and manipulating CLT's. + ** + ** PUBLIC CONTENTS + ** d =defined constant + ** f =function + ** m =defined macro + ** t =typedef/struct/union + ** v =variable + ** ? =other + ** + ** ImCltAlloc f allocate a CLT + ** ImCltFree f deallocate a CLT + ** + ** ImCltDup f duplicate a CLT + ** ImCltGrayRamp f fill a CLT with a grayscale ramp + ** ImCltRoll f roll a cltby x amount + ** ImCltAdjust f adjust the elements in a clt + ** + ** PRIVATE CONTENTS + ** none + ** + ** HISTORY + ** $Log: /roq/libim/imclt.c $ + * + * 1 11/02/99 4:38p Zaphod + ** Revision 1.8 1995/06/29 00:28:04 bduggan + ** updated copyright year + ** + ** Revision 1.7 1995/06/16 08:36:29 bduggan + ** changed bcopy to memcpy. + ** Allowed for index16 vfb's in ImCltGrayRamp + ** + ** Revision 1.6 94/10/03 11:29:32 nadeau + ** Updated to ANSI C and C++ compatibility. + ** Removed all use of register keyword. + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + ** Changed all float arguments to double. + ** Added forward declarations. + ** Added misc. casts to passify SGI and DEC compilers. + ** Changed all macros and defined constants to have names + ** starting with IM. + ** Updated comments. + ** Updated indenting on some code. + ** Updated copyright message. + ** + ** Revision 1.5 92/12/03 01:47:22 nadeau + ** Cleaned up ImCltRoll. + ** + ** Revision 1.4 92/09/01 12:42:12 vle + ** Updated ImCltGrayRamp to include a srcClt and handle + ** lots of variations of input arguments. + ** + ** Revision 1.3 92/08/07 11:56:33 groening + ** added imcltroll + ** + ** Revision 1.2 91/10/03 08:48:59 nadeau + ** Minor cosmetic changes. No bug fixing. + ** + ** Revision 1.1 91/03/08 14:26:27 nadeau + ** Initial revision + ** + **/ + +#include "iminternal.h" + +/** + ** CODE CREDITS + ** Custom development, Mike Bailey, San Diego Supercomputer Center, 1991. + ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1991. + **/ + + + + + +/* + * FUNCTION + * ImCltAlloc - allocate a CLT + * + * DESCRIPTION + * Space for the desired length of CLT is allocated and a new CLT + * structure set up and returned. + */ + +ImClt * /* Returns a CLT */ +#ifdef __STDC__ +ImCltAlloc( int ncolors ) +#else +ImCltAlloc( ncolors ) + int ncolors; /* # entries in this ImClt */ +#endif +{ + ImClt *c; /* the ImClt being allocated */ + ImCltPtr ct; /* the actual color table */ + + + c = (ImClt *) malloc( sizeof( ImClt ) ); + if ( c == NULL ) + { + ImErrNo = IMEMALLOC; + return( IMCLTNULL ); + } + c->clt_ncolors = ncolors; + + + ct = (ImCltPtr) malloc( ncolors*3 ); + if ( ct == NULL ) + { + free( (char *)c ); + ImErrNo = IMEMALLOC; + return( IMCLTNULL ); + } + + c->clt_clt = ct; + + return( c ); +} + + + + + +/* + * FUNCTION + * ImCltFree - deallocate a CLT + * + * DESCRIPTION + * The CLT's space, and then the CLT structure itself are freed. + */ + +void /* Returns nothing */ +#ifdef __STDC__ +ImCltFree( ImClt *c ) +#else +ImCltFree( c ) + ImClt *c; /* the CLT being freed */ +#endif +{ + if ( c != IMCLTNULL ) + { + free( (char *) c->clt_clt ); + free( (char *) c ); + } +} + + + + + +/* + * FUNCTION + * ImCltDup - duplicate a CLT + * + * DESCRIPTION + * A new CLT is allocated that has the same length as the source + * CLT. The source CLT is copied to the new CLT. + */ + +ImClt * /* Returns new CLT */ +#ifdef __STDC__ +ImCltDup( ImClt *srcClt ) +#else +ImCltDup( srcClt ) + ImClt *srcClt; /* CLT to be duplicated */ +#endif +{ + ImClt *newClt; /* New CLT */ + + newClt = ImCltAlloc( ImCltQNColors( srcClt ) ); + if( newClt == 0 ) + { + ImErrNo = IMEMALLOC; + return( IMCLTNULL ); + } + + memcpy( (void *)ImCltQFirst( newClt ), (void *)ImCltQFirst( srcClt ), + 3*ImCltQNColors( srcClt ) ); + + return ( newClt ); +} + + + + + +/* + * FUNCTION + * ImCltGrayRamp - fill a CLT with a grayscale ramp + * + * DESCRIPTION + * If the destination CLT is IMCLTNEW, a new CLT is allocated. + * In any case, the CLT is walked for the selected number of entries, + * starting at the selected location, and grayscale values generated + * from the given low to the given max. The gray CLT is returned. + */ + +ImClt * /* Returns grayscale CLT */ +#ifdef __STDC__ +ImCltGrayRamp( ImClt *srcClt, int startIndex, int endIndex, int startGray, + int endGray, ImClt *dstClt ) +#else +ImCltGrayRamp( srcClt, startIndex, endIndex, startGray, endGray, dstClt ) + ImClt *srcClt; /* CLT to fill */ + int startIndex, endIndex; /* Range of CLT to fill */ + int startGray, endGray; /* Range of gray to fill with */ + ImClt *dstClt; /* CLT to return */ +#endif +{ + int i; /* Counter */ + float delta; /* Increment value for grays */ + float gray; /* Current gray value */ + ImCltPtr pClt; /* Pointer to CLT entry */ + ImCltPtr dptr; /* Pointer to dst CLT entry */ + ImCltPtr sptr; /* Pointer to src CLT entry */ + int maxindex; /* Maximum index in CLT */ + int numcopy; /* Number of CLT entries to copy*/ + + if( startIndex < 0 || endIndex < 0 ) + { + /* startIndex or endIndex too small */ + ImErrNo = IMEBADINOUT; + return( IMCLTNULL ); + } + + if( startGray < 0 || endGray < 0 || startGray > 65535 || endGray > 65535 ) + { + /* startGray or endGray out of valid range */ + ImErrNo = IMEBADINOUT; + return( IMCLTNULL ); + } + + if( srcClt == IMCLTNULL ) + { + if( dstClt == IMCLTNEW ) + { + if ( startIndex > endIndex ) + dstClt = ImCltAlloc( startIndex + 1 ); + else + dstClt = ImCltAlloc( endIndex + 1 ); + if ( dstClt == IMCLTNULL ) + { + ImErrNo = IMEMALLOC; + return ( IMCLTNULL ); + } + } + else if( dstClt == IMCLTNULL ) + { + ImErrNo = IMENOCLT; + return( IMCLTNULL ); + } + else /* dstClt is a valid pointer */ + { + maxindex = ImCltQNColors( dstClt ) - 1; + if( ( maxindex < startIndex ) || + ( maxindex < endIndex ) ) + { + /* Index out of range */ + ImErrNo = IMEBADINOUT; + return( IMCLTNULL ); + } + } + } + else if( srcClt == IMCLTNEW ) + { + if( dstClt == IMCLTNEW ) + { + if ( startIndex > endIndex ) + dstClt = ImCltAlloc( startIndex + 1 ); + else + dstClt = ImCltAlloc( endIndex + 1 ); + if ( dstClt == IMCLTNULL ) + { + ImErrNo = IMEMALLOC; + return ( IMCLTNULL ); + } + } + else if( dstClt == IMCLTNULL ) + { + /* Error */ + ImErrNo = IMENOCLT; + return( IMCLTNULL ); + } + else /* dstClt is a valid pointer */ + { + maxindex = ImCltQNColors( dstClt ) - 1; + if( ( maxindex < startIndex ) || + ( maxindex < endIndex ) ) + { + /* Index out of range */ + ImErrNo = IMEBADINOUT; + return( IMCLTNULL ); + } + } + } + else /* srcClt is a valid pointer */ + { + if( dstClt == IMCLTNEW ) + { + maxindex = ImCltQNColors( srcClt ) - 1; + if( ( maxindex < startIndex ) || + ( maxindex < endIndex ) ) + { + /* Index out of range */ + ImErrNo = IMEBADINOUT; + return( IMCLTNULL ); + } + + dstClt = ImCltDup( srcClt ); + if ( dstClt == IMCLTNULL ) + { + ImErrNo = IMEMALLOC; + return ( IMCLTNULL ); + } + } + else if( dstClt == IMCLTNULL ) + { + /* Error */ + ImErrNo = IMEBADINOUT; + return( IMCLTNULL ); + } + else /* dstClt is a valid pointer */ + { + if( srcClt != dstClt ) + { + maxindex = ImCltQNColors( dstClt ) - 1; + if( ( maxindex < startIndex ) || + ( maxindex < endIndex ) ) + { + /* Index out of range */ + ImErrNo = IMEBADINOUT; + return( IMCLTNULL ); + } + + /* + * Determine how many color entries to + * copy + */ + if( ImCltQNColors( srcClt ) > + ImCltQNColors( dstClt ) ) + { + numcopy = ImCltQNColors( dstClt ); + } + else + { + numcopy = ImCltQNColors( srcClt ); + } + + /* + * Copy the color entries + */ + sptr = ImCltQFirst( srcClt ); + dptr = ImCltQFirst( dstClt ); + + for( i = 0; i < numcopy; ++i ) + { + ImCltSRed( dptr, ImCltQRed( sptr ) ); + ImCltSGreen( dptr, ImCltQGreen(sptr) ); + ImCltSBlue( dptr, ImCltQBlue( sptr ) ); + + sptr = ImCltQNext( srcClt, sptr ); + dptr = ImCltQNext( dstClt, dptr ); + } + } + } + } + + if ( endIndex > startIndex ) + { + delta = endGray - startGray; + delta /= (float)(endIndex - startIndex); + + gray = startGray; + pClt = ImCltQPtr( dstClt, startIndex ); + for ( i = startIndex; i <= endIndex; i++, gray += delta ) + { + ImCltSRed( pClt, (int)gray ); + ImCltSGreen( pClt, (int)gray ); + ImCltSBlue( pClt, (int)gray ); + ImCltSInc( dstClt, pClt ); + } + return ( dstClt ); + } + + delta = endGray - startGray; + delta /= (float)(startIndex - endIndex); + + gray = startGray; + pClt = ImCltQPtr( dstClt, startIndex ); + for ( i = startIndex; i >= endIndex; i--, gray += delta ) + { + ImCltSRed( pClt, (int)gray ); + ImCltSGreen( pClt, (int)gray ); + ImCltSBlue( pClt, (int)gray ); + ImCltSInc( dstClt, pClt ); + } + return ( dstClt ); +} + + + + + +/* + * FUNCTION + * ImCltRoll - roll a CLT + * + * DESCRIPTION + * If no destination CLT is given, a new destination CLT is created. + * + * The source CLT is then copied into the destination CLT, rolling + * its entries. + */ + +ImClt * /* Returns rolled CLT */ +#ifdef __STDC__ +ImCltRoll( ImClt *sourceClt, int nEntries, ImClt *dstClt ) +#else +ImCltRoll( sourceClt, nEntries, dstClt ) + ImClt *sourceClt; /* CLT to roll */ + int nEntries; /* # of entries to roll */ + ImClt *dstClt; /* CLT to overwrite with rolled source */ +#endif +{ + ImClt *sClt; /* CLT to overwrite with rolled source */ + int i, j; /* counters */ + ImCltPtr psrc, pdst; /* src, dst pixel pointers */ + int nColors; /* number of colors in clt */ + + + /* + * Get information on the source CLT and check for errors. + */ + if ( sourceClt == IMCLTNULL ) + { + ImErrNo = IMENOCLT; + return ( IMCLTNULL ); + } + nColors = ImCltQNColors(sourceClt); + + nEntries %= nColors; + if ( nEntries < 0 ) + nEntries += nColors; + + + /* + * Allocate a new VFB if necessary. If the source and destination + * CLT's are the same, we'll need a copy of the source. + */ + sClt = sourceClt; + if (dstClt==IMCLTNEW) + { + dstClt = ImCltAlloc( nColors ); + if ( dstClt == IMCLTNULL ) + { + ImErrNo = IMEMALLOC; + return( IMCLTNULL ); + } + } + else if ( sourceClt == dstClt ) + { + if ( (sClt = ImCltDup(sourceClt)) == IMCLTNULL ) + return ( IMCLTNULL ); /* ImErrNo already set */ + } + else if ( nColors != ImCltQNColors( dstClt ) ) + { + ImErrNo = IMECLTLENGTH; + return ( IMCLTNULL ); + } + + + /* + * Roll it. + */ + psrc = ImCltQFirst( sClt ); + pdst = ImCltQPtr( dstClt, nEntries ); + + for ( j = 0, i = nEntries; j < nColors; j++, i++ ) + { + if ( i == nColors ) + { + i = 0; + pdst = ImCltQFirst( dstClt ); + } + + ImCltSRed( pdst, ImCltQRed( psrc ) ); + ImCltSGreen( pdst, ImCltQGreen( psrc ) ); + ImCltSBlue( pdst, ImCltQBlue( psrc ) ); + + ImCltSInc( sClt, psrc ); + ImCltSInc( dstClt, pdst ); + } + if ( sourceClt != sClt ) + ImCltFree( sClt ); + return ( dstClt ); +} diff --git a/utils/roq2/libim/imcur.c b/utils/roq2/libim/imcur.c new file mode 100644 index 0000000..0f6a6fb --- /dev/null +++ b/utils/roq2/libim/imcur.c @@ -0,0 +1,2614 @@ +/** + + ** $Header: /roq/libim/imcur.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/imcur.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** imcur.c - Microsoft Windows CUR file I/O + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** imcur.c contains routines to read and write Microsoft Windows CUR + + ** files for the image manipulation library. Raster data read + + ** in is stored in a VFB. Raster data written out is taken from a + + ** tag table. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** ImFileCurFormat v file format information + + ** + + ** PRIVATE CONTENTS + + ** imCurHeader t file header information + + ** imCurHeaderFields v imCurHeader description for Bin pkg. + + ** + + ** imCurDirEntry t cursor directory entry + + ** imCurDirEntryFields v imCurDirEntry description for Bin pkg. + + ** + + ** imCurInfo t cursor information + + ** imCurInfoFields v imCurInfo description for Bin pkg. + + ** + + ** imCurRead f read CUR file + + ** imCurImageRead f read the ith cursor image + + ** imCurImageReadVfb m read the ith cursor image + + ** + + ** imCurWriteRaw1 f write 1-bit uncompressed CUR file + + ** + + ** HISTORY + + ** $Log: /roq/libim/imcur.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.12 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.11 1995/06/15 20:16:03 bduggan + + ** took out unused variables. Took out commands at unreachable code. + + ** Removed magic number since it conflicted with tga's. + + ** + + ** Revision 1.10 1995/05/16 22:11:11 bduggan + + ** Took \n out of iminfo's + + ** made HotSpot a static variable (since its address is used + + ** in the tag table) + + ** + + ** Revision 1.9 1995/04/03 21:21:49 bduggan + + ** took out #ifdef NEWMAGIC + + ** + + ** Revision 1.8 94/10/03 11:30:02 nadeau + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Rearranged magic number structures for format handlers. + + ** Made format handler routines static (i.e., local to file). + + ** Updated comments, adding format descriptions and references. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.7 92/12/03 01:46:29 nadeau + + ** Corrected info messages. + + ** + + ** Revision 1.6 92/11/24 11:47:02 groening + + ** Removed use of IMINFOMSG. + + ** + + ** Revision 1.5 92/11/18 12:28:53 groening + + ** fixed error in offset of image + + ** + + ** Revision 1.4 92/11/04 11:46:19 groening + + ** added multiple read and write. + + ** put ImFIleFormat info and magic number info + + ** from imfmt.c into this file. + + ** + + ** Revision 1.3 92/10/12 15:54:17 vle + + ** Made changes to make Cray happy. + + ** + + ** Revision 1.2 92/09/29 17:59:03 vle + + ** Added ImInfo messages. + + ** + + ** Revision 1.1 92/09/17 14:31:40 vle + + ** Initial revision + + ** + + ** + + **/ + + + +#include "iminternal.h" + + + + + +/** + + ** FORMAT + + ** cur - Microsoft Windows cursor image + + ** + + ** AKA + + ** + + ** FORMAT REFERENCES + + ** Microsoft Windows 3.1 Programmer's Reference, volume 3, Microsoft + + ** Press, 1992. + + ** + + ** Microsoft Windows 3.1 Programmer's Reference, volume 3, Microsoft + + ** Press, 1992. + + ** + + ** CODE CREDITS + + ** Custom development, Vinh Le, San Diego Supercomputer Center, 1992. + + ** Extensions, Dave Nadeau, San Diego Supercomputer Center, 1993. + + ** + + ** DESCRIPTION + + ** Microsoft's CUR (CURsor) format is used in Microsoft's Windows + + ** versions 3.0 and up for storage of one or more cursors. + + ** + + ** The first item encountered in an CUR file is a Cursor Directory + + ** Header. This tells that the file is indeed a file containing + + ** cursors and that there are n number of them. + + ** + + ** Following the Cursor Directory Header is an Cursor Directory + + ** Entry for each cursor. A Cursor Directory Entry contains + + ** information on the cursor's location within the file, + + ** its width, height, etc. There are n Cursor Directory Entries. + + ** + + ** Following all this is the actual Cursor Images themselves, which + + ** consist of an info header, a colortable, and XOR and AND masks. + + ** The XOR mask could be considered the "real" cursor. The AND + + ** mask determines the XOR image's "transparency". XOR masks + + ** are 1-bit/pixel. 1-bit/pixel images are packed 8 pixels/byte. + + ** AND masks are always monochrome images, thus they are + + ** packed as 1-bit/pixel images, or 8-bits/byte. + + ** + + ** + + ** Stucture Overview + + ** ----------------- + + ** + + ** CURSOR DIRECTORY + + ** --------------------------------------------------------- + + ** |CURSOR |CURSOR |CURSOR | ... | | | | + + ** |ENTRY 1|ENTRY 2|ENTRY 3| | | | | + + ** --------------------------------------------------------- + + ** | + + ** | --------- + + ** +---------> |CURSOR | + + ** |IMAGE | + + ** |-------| + + ** |Header | + + ** |-------| + + ** |Color | + + ** |Table | + + ** |-------| + + ** |XOR | + + ** |Mask | + + ** |-------| + + ** |AND | + + ** |Mask | + + ** --------- + + ** + + ** + + ** Cursor Directory Header (6 bytes+ 16 bytes/cursor entry) + + ** ----------------------- + + ** + + ** The Cursor Directory Header contains the resource type and + + ** number of cursors in the Cursor Directory. + + ** + + ** Name Type Size Comments + + ** ---- ---- ---- -------- + + ** cdReserved word 2 bytes reserved, must be 0 + + ** cdType word 2 bytes resource type, + + ** must be 2 + + ** cdCount word 2 bytes number of cursors + + ** cdEntries[] CurosrDirEntry 16 bytes array of info for + + ** per entry individual cursors + + ** + + ** typedef struct CURSORDIR + + ** { + + ** word cdReserved; + + ** word cdType; + + ** word cdCount; + + ** CURSORDIRENTRY cdEntries[cdCount]; + + ** } CURSORDIR; + + ** + + ** + + ** Cursor Directory Entry (16 bytes/cursor entry) + + ** ---------------------- + + ** + + ** An Cursor Directory Entry stores information about the an cursor's + + ** dimensions and color attributes. + + ** + + ** Name Type Size Comments + + ** ---- ---- ---- -------- + + ** Width byte 1 byte width in pixels + + ** Height byte 1 byte height in pixels + + ** ColorCount byte 1 byte reserved, must be 0 + + ** Reserved byte 1 byte reserved, must be 0 + + ** XHotspot word 2 byte x coordinate of hotspot + + ** YHotspot word 2 byte y coordinate of hotspot + + ** BytesInRes dword 4 byte size of cursor in bytes + + ** ImageOffset dword 4 byte offset from beginning of file to + + ** Cursor Image + + ** + + ** typedef struct CURSORDIRENTRY + + ** { + + ** byte Width; + + ** byte Height; + + ** byte ColorCount; + + ** byte Reserved; + + ** word XHotspot; + + ** word YHotspot; + + ** dword BytesInRes; + + ** dword ImageOffset; + + ** } CURSORDIRENTRY; + + ** + + ** + + ** Cursor Image (40 bytes + 4 bytes/color entry + sizeof XOR mask + + + ** ------------ sizeof AND mask) + + ** + + ** Each Cursor Image stores 1 cursor. + + ** + + ** Name Type Size Comments + + ** ---- ---- ---- -------- + + ** crHeader BITMAPFILEHEADER 40 bytes only biSize + + ** through + + ** biBitCount + + ** and + + ** biSizeImage + + ** are used + + ** biHeight = + + ** 2*XOR Mask + + ** height + + ** biPlanes = 1 + + ** biBitCount = 1 + + ** all other + + ** fields = 0 + + ** crColors[] RGBQUAD 4 bytes per colortable used + + ** color in XOR mask + + ** crXOR[] byte 1 byte per XOR image mask + + ** element + + ** crAND[] byte 1 byte per AND image mask + + ** element + + ** + + ** + + ** Cur-Info Header (40 bytes) + + ** ------------------ + + ** + + ** The Cur-Info Header contains information about the cursor's + + ** size and dimensions. + + ** + + ** Name Type Size Comments + + ** ---- ---- ---- -------- + + ** biSize dword 4 bytes number of bytes in + + ** Cur-Info Header + + ** biWidth long 4 bytes width, in pixels + + ** biHeight long 4 bytes height, in pixels + + ** biPlanes word 2 bytes number of planes for the + + ** display device, must be 1 + + ** biBitCount word 2 bytes bits per pixel, must be 1 + + ** biCompression dword 4 bytes unused, must be 0 + + ** biSizeImage dword 4 bytes image size in bytes + + ** biXPelsPerMeter long 4 bytes unused, must be 0 + + ** biYPelsPerMeter long 4 bytes unused, must be 0 + + ** biClrUsed dword 4 bytes unused, must be 0 + + ** biClrImportant dword 4 bytes unused, must be 0 + + ** + + ** typedef struct tagBITMAPINFOHEADER + + ** { + + ** dword biSize; + + ** long biWidth; + + ** long biHeight; + + ** word biPlanes; + + ** word biBitCount; + + ** dword biCompression; + + ** dword biSizeImage; + + ** long biXPelsPerMeter; + + ** long biYPelsPerMeter; + + ** dword biClrUsed; + + ** dword biClrImportant; + + ** } BITMAPINFOHEADER; + + ** + + ** + + ** ColorTable (4 bytes per color) + + ** ---------- + + ** + + ** ColorTable is an array of RGBQuad's. Each RGBQuad contains color + + ** intensity values for Red, Green, and Blue. + + ** + + ** RGBQuad + + ** ------- + + ** + + ** Name Type Size Comment + + ** ---- ---- ---- ------- + + ** rgbBlue byte 1 byte intensity of blue + + ** rgbGreen byte 1 byte intensity of green + + ** rgbRed byte 1 byte intensity of red + + ** rgbReserved byte 1 byte unused + + ** + + ** typedef struct tagRGBQUAD + + ** { + + ** byte rgbBlue; + + ** byte rgbGreen; + + ** byte rgbRed; + + ** byte rgbReserved; + + ** } RGBQUAD; + + ** + + **/ + + + + + +/* + + * FUNCTION DECLARATIONS + + */ + +#ifdef __STDC__ + +static int imCurRead( int, int, FILE *, TagTable *, TagTable * ); + +static int imCurWriteRaw1( ImFileFormatWriteMap *, int, int, FILE *, + + TagTable *, TagTable * ); + +#else + +static int imCurRead( ); + +static int imCurWriteRaw1( ); + +#endif + + + + + + + + + + + +/* + + * FORMAT INFORMATION + + * imCurNames - format's name and aliases + + * imCurReadMap - read attributes + + * imCurWriteMap - write attributes + + * imCurMagicNumber - magic number + + * imCurMagic - list of magic numbers + + * ImFileCurFormat - master format description + + */ + +static char *imCurNames[ ] = { "cur", NULL }; + +static ImFileFormatReadMap imCurReadMap[ ] = + +{ + + /* in out */ + + /* type,ch,dep, attr. VFB type attr. */ + + { IN,1,1, C, IMVFBINDEX8, C|A }, + + { -1, 0, -1, 0 }, + +}; + +static ImFileFormatWriteMap imCurWriteMap[ ] = + +{ + + /* in out */ + + /* VFB type, attr., type,ch,dep, attr., func */ + + { IMVFBMONO, C|A, IN,1,1, C, imCurWriteRaw1 }, + + { -1, 0, -1, 0, NULL }, + +}; + + + +/* + + * Magic number: + + * The CUR magic number conflicts with + + * TGA (targa) rgb files. So, I've left + + * out the magic number so tga's can be + + * read safely. + + * + + * The effect of this is not drastic: + + * It just means that cur's can only be + + * identified by their filename extension. + + * + + */ + + + +/* static unsigned char imCurMagicNumber[ ] = { 0x00, 0x00, 0x02, 0x00 }; */ + +static ImFileMagic imCurMagic[ ] = + +{ + + /* { 0, 4, imCurMagicNumber }, */ + + { 0, 0, NULL }, + +}; + + + +ImFileFormat ImFileCurFormat = + +{ + + imCurNames, /* Names */ + + "Windows cursor image file", /* Description */ + + "Microsoft", /* Creator */ + + "1-bit color index images with alpha channels.",/* Read support */ + + "1-bit color index images with alpha channels.",/* Write support*/ + + imCurMagic, /* Magic #'s */ + + IMMULTI, IMPIPE, /* Read? */ + + IMMULTI, IMPIPE, /* Write? */ + + imCurRead, imCurReadMap, imCurWriteMap /* Maps */ + +}; + + + + + + + + + + + +/* + + * TYPEDEF & STRUCTURE + + * imCurHeader - CUR file header + + * imCurHeaderFields - CUR file header fields for binary pkg. + + * imCurDirEntry - CUR cursor directory entry + + * imCurDirEntryFields - CUR cursor directory entry for bin pkg. + + * imCurInfo - CUR file info + + * imCurInfoFields - CUR file info fields for bin pkg. + + * imCurRGBQuad - CUR color information + + * imCurRGBQuadFields - CUR color information fields for bin pkg. + + * + + * DESCRIPTION + + * The imCurRGBQuad is one entry in the colortable. + + */ + + + +typedef struct imCurHeader + +{ + + sdsc_uint16 cur_idReserved; /* reserved, must be zero */ + + sdsc_uint16 cur_idType; /* resource type, must be 2 */ + + sdsc_uint16 cur_idCount; /* number of entries in directory*/ + +} imCurHeader; + + + +static BinField imCurHeaderFields[ ] = + +{ + + { UINT16, 2, 1 }, /* cur_idReserved */ + + { UINT16, 2, 1 }, /* cur_idType */ + + { UINT16, 2, 1 }, /* cur_idCount */ + + { 0, 0, 0 } + +}; + + + + + + + +typedef struct imCurDirEntry + +{ + + unsigned char cur_Width; /* width in pixels */ + + unsigned char cur_Height; /* height in pixels */ + + unsigned char cur_ColorCount; /* reserved, must be 0 */ + + unsigned char cur_Reserved; /* reserved, must be 0 */ + + sdsc_uint16 cur_XHotspot; /* x coordinate of hotspot */ + + sdsc_uint16 cur_YHotspot; /* y coordinate of hotspot */ + + sdsc_uint32 cur_BytesInRes; /* size of cursor in bytes */ + + sdsc_uint32 cur_ImageOffset;/* offset of file to cursor */ + +} imCurDirEntry; + + + +static BinField imCurDirEntryFields[ ] = + +{ + + { UCHAR, 1, 1 }, /* cur_Width */ + + { UCHAR, 1, 1 }, /* cur_Height */ + + { UCHAR, 1, 1 }, /* cur_ColorCount */ + + { UCHAR, 1, 1 }, /* cur_Reserved */ + + { UINT16, 2, 1 }, /* cur_XHotspot */ + + { UINT16, 2, 1 }, /* cur_YHotspot */ + + { UINT32, 4, 1 }, /* cur_ByteInRes */ + + { UINT32, 4, 1 }, /* cur_ImageOffset */ + + { 0, 0, 0 } + +}; + + + + + + + +typedef struct imCurInfo + +{ + + sdsc_uint32 cur_bisize; /* # bytes in cursor info header*/ + + sdsc_uint32 cur_biwidth; /* image width in pixels */ + + sdsc_uint32 cur_biheight; /* image height in pixels */ + + sdsc_uint16 cur_biplanes; /* # planes for device, always 1*/ + + sdsc_uint16 cur_bibitcount; /* bits/pixel, must be 1 */ + + sdsc_uint32 cur_bicompress; /* unused, must be 0 */ + + sdsc_uint32 cur_bisizeimage;/* image size in bytes */ + + sdsc_uint32 cur_bixpm; /* unused, must be 0 */ + + sdsc_uint32 cur_biypm; /* unused, must be 0 */ + + sdsc_uint32 cur_biclrused; /* unused, must be 0 */ + + sdsc_uint32 cur_biclrim; /* unused, must be 0 */ + +} imCurInfo; + + + +static BinField imCurInfoFields[ ] = + +{ + + { UINT32, 4, 1 }, /* cur_bisize */ + + { UINT32, 4, 1 }, /* cur_biwidth */ + + { UINT32, 4, 1 }, /* cur_biheight */ + + { UINT16, 2, 1 }, /* cur_biplanes */ + + { UINT16, 2, 1 }, /* cur_bibitcount */ + + { UINT32, 4, 1 }, /* cur_bicompress */ + + { UINT32, 4, 1 }, /* cur_bisizeimage */ + + { UINT32, 4, 1 }, /* cur_bixpm */ + + { UINT32, 4, 1 }, /* cur_biypm */ + + { UINT32, 4, 1 }, /* cur_biclrused */ + + { UINT32, 4, 1 }, /* cur_biclrim */ + + { 0, 0, 0 } + +}; + + + + + +typedef struct imCurRGBQuad + +{ + + unsigned char blue; /* blue intensity */ + + unsigned char green; /* green intensity */ + + unsigned char red; /* red intensity */ + + unsigned char reserved; /* unused */ + +} imCurRGBQuad; + + + +static BinField imCurRGBQuadFields[ ] = + +{ + + { UCHAR, 1, 1 }, /* blue */ + + { UCHAR, 1, 1 }, /* green */ + + { UCHAR, 1, 1 }, /* red */ + + { UCHAR, 1, 1 }, /* reserved */ + + { 0, 0, 0 } + +}; + + + + + + + + + + + +/* + + * CONSTANTS + + * CUR* - assorted useful CUR constants + + */ + + + +#define IMCURMAGIC 2 + + /* magic resource number for cursors */ + +#define IMCURHEADERSIZE 6 + + /* 6 bytes */ + +#define IMCURDIRENTRYSIZE 16 + + /* 16 bytes */ + +#define IMCURINFOSIZE 40 + + /* 40 bytes */ + +#define IMCURRGBQUADSIZE 4 + + /* 4 bytes */ + +#define IMCURXOR 0 + + /* XOR vfb flag, IMVFBMONO */ + +#define IMCURAND 1 + + /* AND vfb flag, IMVFBMONO */ + + + + + + + + + + + +#ifdef __STDC__ + +static int imCurImageRead( int, int, FILE *, TagTable *, TagTable *, + + imCurDirEntry * ); + +#else + +static int imCurImageRead( ); + +#endif + + + + + + + + + + + +/* + + * FUNCTION + + * imCurRead - read a Microsoft Windows Cur file + + * + + * DESCRIPTION + + * The file header is read and the size of the image determined. + + * Space is allocated for the VFB and the file is read into the + + * VFB. + + */ + + + +static int /* Returns # tags read in */ + +#ifdef __STDC__ + +imCurRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imCurRead( ioType, fd, fp, flagsTable, tagTable ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + +#endif + +{ + + imCurHeader header; /* CUR file header */ + + imCurDirEntry *curdirentries; /* CUR dir entries */ + + imCurDirEntry *curdirentryptr; /* CUR dir entry pointer */ + + unsigned int i; /* Counter */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * Set Binary Package byte order and read in part of header + + */ + + BinByteOrder( BINLBF ); + + if( ImBinReadStruct( ioType, fd, fp, &header, + + imCurHeaderFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + /* + + * Use idType to determine byte ordering + + */ + + if( header.cur_idType != IMCURMAGIC ) + + { + + /* + + * Swap bytes + + */ + + header.cur_idType = (header.cur_idType&0xFF00)>>8 | + + (header.cur_idType&0x00FF)<<8; + + if( header.cur_idType != IMCURMAGIC ) + + { + + ImErrorFatal( ImQError( ), -1, IMEMAGIC ); + + } + + else + + { + + header.cur_idCount = (header.cur_idCount&0xFF00)>>8 | + + (header.cur_idCount&0x00FF)<<8; + + BinByteOrder( BINMBF ); + + ImInfo( "Byte Order", "Most Significant Byte First" ); + + } + + } + + else + + { + + ImInfo( "Byte Order", "Least Significant Byte First" ); + + } + + + + /* + + * Allocate space for cursor dir entries + + */ + + ImMalloc( curdirentries, imCurDirEntry*, + + header.cur_idCount * sizeof(imCurDirEntry) ); + + + + /* + + * Read in cursor dir entries + + */ + + curdirentryptr = curdirentries; + + for( i = 0; i < header.cur_idCount; ++i ) + + { + + if( ImBinReadStruct( ioType, fd, fp, curdirentryptr, + + imCurDirEntryFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + ++curdirentryptr; + + } + + + + /* + + * Read in cursor images + + */ + + curdirentryptr = curdirentries; + + for( i = 0; i < header.cur_idCount; ++i ) + + { + + /* + + * Read in ith cursor + + */ + + sprintf( message, "%d of %d", (i+1), header.cur_idCount ); + + ImInfo( "Image", message ); + + + + imCurImageRead( ioType, fd, fp, flagsTable, tagTable, + + curdirentryptr ); + + ++curdirentryptr; + + } + + + + free( curdirentries ); + + return header.cur_idCount; + +} + + + + + + + + + + + +#define imCurImageReadVfb( tmpvfb, vfbtype ) \ +/* \ + * The image is stored from the bottom up; that is, \ + * the first scanline in the file is the last \ + * scanline in the image. \ + */ \ + \ +/* \ + * Go to the last scanline in the tmpvfb \ + */ \ +vfbptr = ImVfbQPtr( tmpvfb, 0, y-1 ); \ + \ +switch( info.cur_bibitcount ) \ +{ \ + case 1: \ + /* \ + * Read 1-bit non-compressed image \ + * \ + * 1 bytes contains info for 8 pixels \ + * \ + * -------- <- 1 byte \ + * 12345678 \ + * \ + * Set = first color in colortable \ + * Not Set = second color in colortable \ + * \ + */ \ + \ + /* \ + * Find scanline size to nearest 32-bit \ + * boundary and allocate read buffer \ + * \ + * Note: bit shifting is used to speed up \ + * calculations \ + */ \ + scanlinesize = x>>5; \ + if( x%32 ) scanlinesize++; \ + scanlinesize <<= 2; \ + \ + ImMalloc( rbuf, unsigned char*, scanlinesize ); \ + \ + for( i = 0; i < y ; ++i ) \ + { \ + if( ImBinRead( ioType, fd, fp, \ + rbuf, UCHAR, 1, scanlinesize ) == -1) \ + { \ + ImReturnBinError( ); \ + } \ + rbufptr = rbuf; \ + \ + for( j = 0; j < (x/8); ++j ) \ + { \ + mask = 128; \ + for( k = 0; k < 8; ++k ) \ + { \ + if( ( *(rbufptr) & mask ) == mask ) \ + { \ + if( vfbtype == IMCURXOR ) \ + { \ + ImVfbSIndex8( tmpvfb, vfbptr, 1 );\ + } \ + else \ + { \ + ImVfbSAlpha( tmpvfb, vfbptr, 0 );\ + } \ + } \ + else \ + { \ + if( vfbtype == IMCURXOR ) \ + { \ + ImVfbSIndex8( tmpvfb, vfbptr, 0 );\ + } \ + else \ + { \ + ImVfbSAlpha( tmpvfb, vfbptr, 255 );\ + } \ + } \ + vfbptr = ImVfbQRight( tmpvfb, vfbptr ); \ + mask = mask >> 1; \ + } \ + ++rbufptr; \ + } \ + if( x%8 ) \ + { \ + mask = 128; \ + for( k = 0; k < x%8; ++k ) \ + { \ + if( ( *(rbufptr) & mask ) == mask ) \ + { \ + if( vfbtype == IMCURXOR ) \ + { \ + ImVfbSIndex8( tmpvfb, vfbptr, 1 );\ + } \ + else \ + { \ + ImVfbSAlpha( tmpvfb, vfbptr, 0 );\ + } \ + } \ + else \ + { \ + if( vfbtype == IMCURXOR ) \ + { \ + ImVfbSIndex8( tmpvfb, vfbptr, 0 );\ + } \ + else \ + { \ + ImVfbSAlpha( tmpvfb, vfbptr, 255 );\ + } \ + } \ + vfbptr = ImVfbQRight( tmpvfb, vfbptr ); \ + mask = mask >> 1; \ + } \ + } \ + \ + /* \ + * Go up two scanline since we \ + * wrapped-around with ImVfbQRight( ) \ + */ \ + vfbptr = ImVfbQUp( tmpvfb, vfbptr ); \ + vfbptr = ImVfbQUp( tmpvfb, vfbptr ); \ + } \ + \ + break; \ + \ + default: \ + ImErrorFatal( ImQError( ), -1, IMEPLANES ); \ + \ +} /* end switch */ \ + \ +free( (unsigned char*) rbuf ); \ + \ + + + + + + + + + + + +static int + +#ifdef __STDC__ + +imCurImageRead ( int ioType, int fd, FILE *fp, TagTable *flagsTable, + + TagTable *tagTable, imCurDirEntry *curdirentryptr ) + +#else + +imCurImageRead ( ioType, fd, fp, flagsTable, tagTable, curdirentryptr ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + + imCurDirEntry *curdirentryptr; /* Cursor dir entry pointer */ + +#endif + +{ + + imCurInfo info; /* Cursor image info */ + + ImVfb *vfb; /* Virtual Frame Buffers */ + + ImVfbPtr vfbptr; /* Pixel pointer */ + + ImClt *clt = IMCLTNULL; /* Colortable */ + + ImCltPtr cltptr; /* Colortable entry pointer */ + + static ImHotSpot hotspot; /* Cursor hotspot */ + + ImHotSpotPtr hotspotptr; /* Cursor hotspot pointer */ + + unsigned char mask; /* Mask for extracting pixels */ + + unsigned char *rbuf = NULL; /* Read buffer */ + + unsigned char *rbufptr; /* Read buffer data pointer */ + + unsigned int scanlinesize; /* Length of scanline in bytes */ + + unsigned int ncolors = 0; /* Number of colors used */ + + unsigned int x, y; /* Width and Height */ + + unsigned int i, j, k; /* Counters */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * Read in cursor image info. + + */ + + if( ImBinReadStruct( ioType, fd, fp, &info, imCurInfoFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + /* + + * Store hotspot in TagTable + + */ + + hotspotptr = &hotspot; + + ImHotSpotSX( hotspotptr, curdirentryptr->cur_XHotspot ); + + ImHotSpotSY( hotspotptr, curdirentryptr->cur_YHotspot ); + + TagTableAppend( tagTable, TagEntryAlloc( "image hot spot", + + POINTER, &hotspotptr) ); + + + + x = info.cur_biwidth; + + y = info.cur_biheight / 2; + + + + + + + + /* + + * Allocate a VFBs of the required size and type. + + */ + + switch( info.cur_bibitcount ) + + { + + case 1: /* 1-bit index image */ + + break; + + + + default: /* unsupported image depth */ + + ImErrorFatal( ImQError( ), -1, IMEDEPTH ); + + } + + vfb = ImVfbAlloc( x, y, IMVFBINDEX8 | IMVFBALPHA); + + if ( vfb == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + /* + + * Allocate and read in colortable + + */ + + ncolors = (1 << info.cur_bibitcount); + + switch( ncolors ) + + { + + case 2: /* 1-bit, monochrome */ + + break; + + default: + + ImErrorFatal( ImQError( ), -1, IMECLTLENGTH ); + + /* output warning */ + + } + + clt = ImCltAlloc( ncolors ); + + + + /* + + * Read in colortable and set RGB values + + */ + + cltptr = ImCltQFirst( clt ); + + ImMalloc( rbuf, unsigned char*, ncolors*IMCURRGBQUADSIZE ); + + rbufptr = rbuf; + + if( ImBinRead( ioType, fd, fp, rbuf, UCHAR, 1, + + ncolors*IMCURRGBQUADSIZE ) == -1 ) + + { + + ImReturnBinError( ); + + } + + for( i = 0; i < ncolors; ++i ) + + { + + ImCltSBlue( cltptr, *rbufptr ); + + ++rbufptr; + + ImCltSGreen( cltptr, *rbufptr ); + + ++rbufptr; + + ImCltSRed( cltptr, *rbufptr ); + + ++rbufptr; + + /* skip reserved field */ + + ++rbufptr; + + + + cltptr = ImCltQNext( clt, cltptr ); + + } + + free( rbuf ); + + + + /* + + * Attach colortable to vfb and append to tagTable + + */ + + ImVfbSClt( vfb, clt ); + + TagTableAppend( tagTable, TagEntryAlloc( "image clt", + + POINTER, &clt) ); + + + + /* + + * Read in XOR mask image + + */ + + imCurImageReadVfb( vfb, IMCURXOR ); + + + + /* + + * Read in AND mask image + + */ + + info.cur_bibitcount = 1; /* AND masks are mono images */ + + imCurImageReadVfb( vfb, IMCURAND ); + + + + /* + + * Add the VFB to the tagTable. + + */ + + TagTableAppend( tagTable, + + TagEntryAlloc( "image vfb", POINTER, &vfb ) ); + + + + /* + + * Output -verbose message + + */ + + sprintf( message, "%d x %d", x, y ); + + ImInfo( "Resolution", message ); + + + + ImInfo( "Type", "1-bit Color Indexed (XOR mask)" ); + + + + sprintf( message, "%d Entries", ncolors ); + + ImInfo( "Color Table", message ); + + + + ImInfo( "Alpha Channel", "1-bit Monochrome (AND mask)" ); + + + + sprintf( message, "%d, %d", hotspot.hot_x, hotspot.hot_y ); + + ImInfo( "Hot Spot", message ); + + + + return ( 2 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imCurWriteRaw1 - write an 1-bit non-compressed CUR file + + * + + * DESCRIPTION + + * That VFB is queried, and the CUR file header set up and written out. + + * The VFB data is then converted and written out. + + * Multiple image writes are supported. + + */ + + + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imCurWriteRaw1( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + + TagTable *flagsTable, TagTable *tagTable ) + +#else + +imCurWriteRaw1( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag list to read from */ + +#endif + +{ + + imCurHeader header; /* CUR file header */ + + imCurDirEntry direntry; /* CUR dir entry */ + + imCurInfo info; /* CUR info */ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr vfbptr; /* Vfb pixel pointer */ + + imCurRGBQuad cltentry; /* Colortable RGBQuad entry */ + + ImClt *clt; /* Colortable */ + + ImCltPtr cltptr; /* Colortable entry pointer */ + + ImHotSpotPtr hotspotptr; /* Cursor hotspot pointer */ + + unsigned char pvalue; /* Pixel value */ + + unsigned char *wbuf; /* Pointer to write buffer */ + + unsigned char *wbufptr; /* Pointer to write buffer data */ + + unsigned int scanlinesize; /* Length of scanline */ + + unsigned char bitfield; /* Bitfield of 8 pixels */ + + unsigned int x, y; /* Width, Height */ + + int i, j, k, loop; /* Counters */ + + unsigned int count; /* Counter */ + + int fields; /* Vfb fields */ + + char message[100]; /* ImInfo message */ + + int imageNum; /* number of images in a file */ + + TagEntry *tagEntry; /* Tag table entry holder */ + + unsigned long offsetTotal; /* where you are in image */ + + + + + + /* + + * Set byte ordering + + */ + + BinByteOrder( BINLBF ); + + ImInfo( "Byte Order", "Least Significant Byte First" ); + + + + /* + + * Get number of images + + */ + + imageNum = TagTableQNEntry( tagTable, "image vfb" ); + + + + /* + + * Setup header and write it out + + */ + + header.cur_idReserved = 0; + + header.cur_idType = (sdsc_uint16) IMCURMAGIC; + + header.cur_idCount = (sdsc_uint16) imageNum; + + + + if ( ImBinWriteStruct( ioType, fd, fp, &header, imCurHeaderFields )==-1) + + { + + ImReturnBinError( ); + + } + + + +#ifdef old + + offsetTotal = (sdsc_uint32)( sizeof(imCurHeader) + + + (imageNum * (sizeof(imCurDirEntry)))); + +#else + + /* + + * Compute the file offset to right after the header and individual + + * cursor directory entries. + + */ + + offsetTotal = (unsigned long) IMCURHEADERSIZE + + + (imageNum * (unsigned long) IMCURDIRENTRYSIZE); + +#endif + + + + + + /* + + * Start by doing a dummy pass through all the images in the + + * tag table. For each image, write out a directory entry and + + * compute where the image's bytes will eventually be in the file. + + */ + + for ( loop = 0; loop < imageNum; loop++ ) + + { + + /* + + * Get vfb and clt + + */ + + tagEntry = TagTableQDirect( tagTable, "image vfb", loop ); + + TagEntryQValue( tagEntry, &vfb ); + + clt = ImVfbQClt( vfb ); + + x = ImVfbQWidth( vfb ); + + y = ImVfbQHeight( vfb ); + + + + + + /* + + * Find scanline size to nearest 32-bit boundary. + + */ + + scanlinesize = x>>5; + + if ( x%32 ) scanlinesize++; + + scanlinesize <<= 2; + + + + + + /* + + * Setup cursor directory and write it out + + */ + + direntry.cur_Width = (unsigned char) x; + + direntry.cur_Height = (unsigned char) y; + + direntry.cur_ColorCount = (unsigned char) 2; /* Always 2 */ + + direntry.cur_Reserved = 0; + + if( TagEntryQValue( TagTableQDirect( tagTable, + + "image hot spot", loop ), &hotspotptr ) == -1 ) + + { + + direntry.cur_XHotspot = 0; + + direntry.cur_YHotspot = 0; + + } + + else + + { + + direntry.cur_XHotspot =ImHotSpotQX( hotspotptr); + + direntry.cur_YHotspot =ImHotSpotQY( hotspotptr); + + } + + /* + + * # of bytes = cursor info + colortable + + + * XOR mask + AND mask + + */ + +#ifdef old + + direntry.cur_BytesInRes = (sdsc_uint32) ( IMCURINFOSIZE + + + ( 2 * IMCURRGBQUADSIZE ) + (scanlinesize * y) + + + (x*y/8) ); + +#else + + direntry.cur_BytesInRes = (sdsc_uint32) ( IMCURINFOSIZE + + + ( 2 * IMCURRGBQUADSIZE ) + (scanlinesize * y*2)); + +#endif + + direntry.cur_ImageOffset= (sdsc_uint32) (offsetTotal); + + + + if ( ImBinWriteStruct( ioType, fd, fp, &direntry, + + imCurDirEntryFields )==-1) + + { + + ImReturnBinError( ); + + } + + + +#ifdef old + + offsetTotal += sizeof(imCurInfo) + + + (2 * sizeof(imCurRGBQuad)) + + + ( 2 * ((x/8) *y) ); + +#else + + offsetTotal += IMCURINFOSIZE + (2 * IMCURRGBQUADSIZE) + + + 2 * scanlinesize * y; + +#endif + + + + } + + + + + + /* + + * Now do it for real. Loop through all the images and write them + + * out. + + */ + + for ( loop = 0; loop < imageNum; loop++ ) + + { + + /* + + * Get vfb and clt + + */ + + tagEntry = TagTableQDirect( tagTable, "image vfb", loop ); + + TagEntryQValue( tagEntry, &vfb ); + + clt = ImVfbQClt( vfb ); + + x = ImVfbQWidth( vfb ); + + y = ImVfbQHeight( vfb ); + + + + /* + + * Find scanline size to nearest 32-bit boundary. + + */ + + scanlinesize = x>>5; + + if( x%32 ) scanlinesize++; + + scanlinesize <<= 2; + + + + + + /* + + * Setup cursor info and write it out + + */ + + info.cur_bisize = (sdsc_uint32) IMCURINFOSIZE; + + info.cur_biwidth = (sdsc_uint32) x; + + info.cur_biheight = (sdsc_uint32) (2*y); + + info.cur_biplanes = (sdsc_uint16) 1; + + info.cur_bibitcount = (sdsc_uint16) 1; + + info.cur_bicompress = 0; + + info.cur_bisizeimage = (sdsc_uint32) (scanlinesize * y); + + info.cur_bixpm = 0; + + info.cur_biypm = 0; + + info.cur_biclrused = 0; + + info.cur_biclrim = 0; + + + + if ( ImBinWriteStruct( ioType, fd, fp, &info, + + imCurInfoFields )==-1) + + { + + ImReturnBinError( ); + + } + + + + /* + + * Output -verbose message + + */ + + sprintf( message, "%d of %d", loop, imageNum ); + + ImInfo( "Image", message ); + + + + sprintf( message, "%d x %d", x, y ); + + ImInfo( "Resolution", message ); + + + + ImInfo( "Type", "1-bit Color Indexed (XOR mask)" ); + + ImInfo( "Color Table", "2 Entries" ); + + ImInfo( "Alpha Channel", "1-bit Monochrome (AND mask)" ); + + + + sprintf( message, "%d, %d", direntry.cur_XHotspot, + + direntry.cur_YHotspot ); + + ImInfo( "Hot Spot", message ); + + + + + + /* + + * Write colortable + + */ + + cltentry.reserved = 0; + + cltentry.blue = 0; + + cltentry.green = 0; + + cltentry.red = 0; + + + + if( clt != IMCLTNULL ) + + { + + clt = ImVfbQClt( vfb ); + + + + cltptr = ImCltQFirst( clt ); + + cltentry.blue = (unsigned char) ImCltQBlue( cltptr ); + + cltentry.green = (unsigned char) ImCltQGreen( cltptr ); + + cltentry.red = (unsigned char) ImCltQRed( cltptr ); + + } + + if ( ImBinWriteStruct( ioType, fd, fp, &cltentry, + + imCurRGBQuadFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + cltentry.blue = 255; + + cltentry.green = 255; + + cltentry.red = 255; + + + + if( ( clt != IMCLTNULL ) && (ImCltQNColors( clt ) > 1) ) + + { + + cltptr = ImCltQNext( clt, cltptr ); + + cltentry.blue = (unsigned char) ImCltQBlue( cltptr ); + + cltentry.green = (unsigned char) ImCltQGreen( cltptr ); + + cltentry.red = (unsigned char) ImCltQRed( cltptr ); + + } + + + + if ( ImBinWriteStruct(ioType,fd,fp,&cltentry,imCurRGBQuadFields )==-1) + + { + + ImReturnBinError( ); + + } + + + + /* + + * Allocate write buffer space + + */ + + ImCalloc( wbuf, unsigned char*, scanlinesize, + + sizeof( unsigned char ) ); + + + + /* + + * Write XOR mask image + + */ + + for( i = 0; i < y; ++i ) + + { + + vfbptr = ImVfbQPtr( vfb, 0, y-i-1 ); + + wbufptr = wbuf; + + for( j = 0; j < (x/8); ++j ) + + { + + /* + + * Pack 8 pixels into a 1 byte bitfield + + * + + * -------- <-- 1 byte + + * 12345678 + + * + + */ + + bitfield = 0; + + for( k = 0; k < 8; ++k ) + + { + + pvalue = (unsigned char) ImVfbQMono( vfb, vfbptr ); + + if( pvalue ) { + + bitfield = ( bitfield | (1 << (7-k) ) ); + + } + + vfbptr = ImVfbQRight( vfb, vfbptr ); + + } + + *( wbufptr ) = bitfield; + + ++wbufptr; + + } + + + + if( x%8 ) + + { + + /* + + * Pack 8 pixels into a 1 byte bitfield + + * + + * -------- <-- 1 byte + + * 12345678 + + * + + */ + + bitfield = 0; + + for( k = 0; k < x%8; ++k ) + + { + + pvalue = (unsigned char) ImVfbQMono( vfb, vfbptr ); + + if( pvalue ) { + + bitfield = ( bitfield | (1 << (7-k) ) ); + + } + + vfbptr = ImVfbQRight( vfb, vfbptr ); + + } + + *( wbufptr ) = bitfield; + + } + + + + if( ImBinWrite( ioType,fd,fp,wbuf,UCHAR,1,scanlinesize ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + /* + + * Go up two scanline since we + + * wrapped-around with ImVfbQRight( ) + + */ + + vfbptr = ImVfbQUp( vfb, vfbptr ); + + vfbptr = ImVfbQUp( vfb, vfbptr ); + + } + + + + /* + + * Check for an existing alpha channel + + */ + + fields = ImVfbQFields( vfb ); + + + + /* + + * If there's an alpha channel, use it as the AND mask + + */ + + if( fields & IMVFBALPHA ) + + { + + /* + + * Initialize + + */ + + count = 0; + + + + /* + + * Traverse through alpha plane and write out AND mask + + */ + + for( i = y-1; i > -1; --i ) + + { + + wbufptr = wbuf; + + for( j = 0; j < x; ++j ) + + { + + vfbptr = ImVfbQPtr( vfb, j, i ); + + pvalue = ImVfbQAlpha( vfb, vfbptr ); + + + + if( pvalue < 128 ) + + { + + bitfield = bitfield | 1; + + } + + + + ++count; + + if( count == 8 ) + + { + + *(wbufptr++) = bitfield; + + count = 0; + + bitfield = 0; + + } + + bitfield = bitfield << 1; + + } + + if( ImBinWrite( ioType,fd,fp,wbuf,UCHAR,1, + + scanlinesize )== -1 ) + + { + + ImReturnBinError( ); + + } + + } + + } + + else + + { + + /* + + * Clear wbuf + + */ + + wbufptr = wbuf; + + for( i = 0; i < scanlinesize; ++i ) + + { + + *(wbufptr++) = 0; + + } + + + + /* + + * Write AND mask scanlines + + */ + + for( i = 0; i < y; ++i ) + + { + + if( ImBinWrite( ioType,fd,fp,wbuf,UCHAR,1, + + scanlinesize )== -1 ) + + { + + ImReturnBinError( ); + + } + + } + + } + + free( (unsigned char*) wbuf ); + + } + + + + return ( 1 ); + +} + diff --git a/utils/roq2/libim/imeps.c b/utils/roq2/libim/imeps.c new file mode 100644 index 0000000..cc0eb3f --- /dev/null +++ b/utils/roq2/libim/imeps.c @@ -0,0 +1,2321 @@ +/** + + ** $Header: /roq/libim/imeps.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/imeps.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** imeps.c - Encapsulated PostScript file output + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** imeps.c contains a write routine to write Encapsulated PostScript + + ** files for the image manipulation library. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** ImFileEpsFormat v master format description + + ** + + ** PRIVATE CONTENTS + + ** imEpsNames v format's name and aliases + + ** imEpsWriteMap v write attributes + + ** imEpsMagicNumber v magic number + + ** imEpsMagic v list of magic numbers + + ** + + ** imEpsHex v Integer->Hex character conversion table + + ** imEpsHeader v PostScript header section + + ** imEpsProlog v PostScript prolog section + + ** imEpsImageColor v Color printing PostScript procedure + + ** imEpsImageGray v Grayscale printing PostScript procedure + + ** imEpsImageMono v Monochrome printing PostScript procedure + + ** imEpsScript v PostScript page script section + + ** imEpsData v PostScript data section + + ** imEpsTrailer v PostScript trailer section + + ** + + ** imEpsWrite1 f write a 1-bit monochrome image to a PostScript file + + ** imEpsWriteGray f write a grayscale image to a PostScript file + + ** imEpsWrite8 f write an 8-bit index image to a PostScript file + + ** imEpsWriteRGB f write an RGB image to a PostScript file + + ** + + ** HISTORY + + ** $Log: /roq/libim/imeps.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.12 1995/06/30 21:59:24 bduggan + + ** replaced long with time_t + + ** + + ** Revision 1.11 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.10 1995/06/15 20:18:01 bduggan + + ** Added some function prototypes. + + ** Removed {}'s from char[] declaration + + ** + + ** Revision 1.9 1995/04/03 21:22:57 bduggan + + ** took out #ifdef NEWMAGIC + + ** + + ** Revision 1.8 1995/01/10 23:21:11 bduggan + + ** Uncapitilized i's in local functions + + ** + + ** Revision 1.7 94/10/03 11:30:04 nadeau + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Rearranged magic number structures for format handlers. + + ** Made format handler routines static (i.e., local to file). + + ** Updated comments, adding format descriptions and references. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.6 92/12/03 01:47:38 nadeau + + ** Corrected info messages. + + ** + + ** Revision 1.5 92/11/04 11:59:57 groening + + ** put ImFIleFormat info and magic number info + + ** from imfmt.c into this file. + + ** + + ** Revision 1.4 92/10/19 14:15:21 groening + + ** added ImINfo statements + + ** + + ** Revision 1.3 92/09/25 12:38:01 groening + + ** added lines for ImInfo to printout verbose information + + ** + + ** Revision 1.2 92/08/31 17:22:40 vle + + ** Updated copyright notice. + + ** + + ** Revision 1.1 91/10/03 08:49:41 nadeau + + ** Initial revision + + ** + + ** Revision 1.7 91/09/17 20:17:10 nadeau + + ** Added color PostScript support. + + ** + + ** Revision 1.6 91/02/12 10:53:58 nadeau + + ** Removed the tag table checking and VFB conversion now + + ** handled by ImFileWrite. Updated to use INDEX8 instead + + ** of GRAY VFB. Changed 'Creator' comment to include the + + ** program name from the flags table, if given. + + ** + + ** Revision 1.5 90/11/27 16:40:44 mjb + + ** Removed spaces in between hex bytes (xerox says this will make it + + ** read much faster). Made 'image' byte string size the same as vfb + + ** width (ditto above comment) + + ** + + ** Revision 1.4 90/07/02 13:21:33 nadeau + + ** Updated to the new error handling mechanism. + + ** + + ** Revision 1.3 90/06/25 14:36:16 nadeau + + ** Changed ImTag* to Tag* (new names). + + ** + + ** Revision 1.2 90/05/16 07:47:03 todd + + ** Add #include "iminternal.h" to top of file + + ** + + ** Revision 1.1 90/05/11 14:28:23 nadeau + + ** Initial revision + + ** + + **/ + + + +#include + +#include "iminternal.h" + + + +/** + + ** FORMAT + + ** eps - Adobe Encapsulated PostScript + + ** + + ** AKA + + ** + + ** FORMAT REFERENCES + + ** PostScript Language Reference Manual, second edition, Adobe Systems, + + ** Addison Wesley, 1990. + + ** + + ** Bit-Mapped Graphics, Steve Rimmer, McGraw-Hill, 1990. + + ** + + ** Adobe PhotoShop 2.0 EPS File Format, Thomas Knoll, 1990-1991. + + ** + + ** CODE CREDITS + + ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1990. + + ** + + ** Partially based upon ideas and methods provided by Loren "Buck" + + ** Buchanan, Naval Research Laboratory, Kestrel Associates Inc. + + ** + + ** DESCRIPTION + + ** Adobe's EPS format is used by applications that create graphics or + + ** imagery for inclusion in another document by another application. + + ** Those graphics or imagery are described using Adobe's PostScript + + ** programming language, but augmented with a rigid set of special EPS + + ** file comments. Those comments help the including application figure + + ** out what's in the EPS file, how big it is, what it's font requirements + + ** are, and so on. + + ** + + ** Adobe PhotoShop further defines an additional EPS file comment that + + ** lets PhotoShop find and read in image data stored in an EPS file. + + ** + + ** Header + + ** EPS files begin with a special comment: + + ** + + ** %!PS-Adobe-3.0 EPSF-3.0 + + ** + + ** This line is treated as a magic number by the SDSC Image Tools. + + ** + + ** Following the magic number line are a series of header comments + + ** describing the file's contents and purpose: + + ** + + ** %%BoundingBox: lx ly ux uy + + ** %%Creator: program + + ** %%For: person + + ** %%CreationDate: date + + ** %%Title: filename + + ** %%Pages: # of pages + + ** %%EndComments + + ** + + ** The %%BoundingBox comment is required of all EPS files and tells the + + ** reader how much space to leave in the including document for the + + ** EPS file's contents. Applications may just present the user with a + + ** square box of this size and let the user move it about to locate + + ** the included image in the document. Such applications need not read + + ** or understand the rest of the EPS file. + + ** + + ** The rest of the header comments are optional and are just for + + ** documentation purposes. The header ends with a %%EndComments comment. + + ** + + ** PhotoShop Support + + ** Following this header is PhotoShop's custom "%ImageData" comment: + + ** + + ** %ImageData: w h d ch pch block binhex "startline" + + ** + + ** This comment is, essentially, a pointer down to the raw image data + + ** stored later in the file. "w" and "h" are the width and height of + + ** the EPS image. "d" is it's channel depth (1 or 8 bits). "ch" is + + ** the number of image channels (1, 3, or 4). + + ** + + ** "pch" is the number of pad channels. These are extra channels added + + ** to the data that aren't to be imaged (PhotoShop uses this for a + + ** pre-computed grayscale image). "binhex" is a flag indicating if the + + ** raw image data is binary (1) or hex (2). + + ** + + ** The final argument to %ImageData is a copy of the entire PostScript + + ** line immediately preceeding the raw image data. PhotoShop uses this + + ** in a pattern match search through the file to find the line before + + ** the raw data. It then uses the rest of the arguments to figure out + + ** how to read that data. + + ** + + ** The SDSC Image Tools write out this comment so that PhotoShop can + + ** read in our EPS images. + + ** + + ** Preview + + ** In general, an EPS file need not contain raw image data (though that's + + ** what the SDSC Image Tools and PhotoShop output). An EPS file may + + ** contain almost any PostScript algorithm and data to draw anything. + + ** This presents something of a problem to applications wishing to import + + ** an EPS file and include it in a document (such as a diagram in a Word + + ** document). How can that application show something meaningful to the + + ** user so that the user can move and adjust the imported EPS data? The + + ** application can't be expected to parse and execute the PostScript code + + ** of the EPS file. So, now what? + + ** + + ** The answer is something called a "Preview Image". Programs that + + ** generate EPS files are kindly asked to rasterize the file's contents, + + ** possibly at low res, and always in grayscale, and place that rasterized + + ** version at the head of the file in a preview image comment. Now, + + ** applications that include the EPS file can present that low res gray + + ** image to the user as a crude representation of what's really in the + + ** EPS file and what will really get printed when the thing is sent to a + + ** PostScript printer. + + ** + + ** The Preview Image is a comment block starting and ending with: + + ** + + ** %%BeginPreview: h w d lines + + ** ... + + ** %%EndPreview + + ** + + ** The %%BeginPreview comment gives the preview image's height, width, + + ** depth (1, 2, 4, or 8) and number of lines of text preview data that + + ** follow. Applications that wish to strip out the preview image (such + + ** as lpr filters) can toss anything between %%BeginPreview and + + ** %%EndPreview. + + ** + + ** Why doesn't PhotoShop use this preview image? First, it's not + + ** required to be the same resolution as the real image. Second, it's + + ** always grayscale, even when the real image is color. + + ** + + ** The SDSC Image Tools output a Preview Image for those applications + + ** that need it. The image is always grayscale, and always the same + + ** resolution as the real image later on in the file. + + ** + + ** Prolog + + ** EPS files next contain a prolog section intended to be used to define + + ** assorted functions and variables for later use in the file. We use + + ** the prolog section to define a function "ImEpsImage" to read in and + + ** image the raw image data onto the page. That function is defined + + ** differently for monochrome, grayscale, and RGB image data. For RGB + + ** data, that function figures out if the printer to which the EPS data + + ** is being sent can handle color. If not, the RGB data is converted to + + ** grayscale on the fly. + + ** + + ** The Prolog is surrounded by: + + ** + + ** %%BeingProlog + + ** ... + + ** %%EndProlog + + ** + + ** Page + + ** The actual EPS drawing is surrounded by: + + ** + + ** %%Page: n m + + ** ... + + ** %%PageTrailer + + ** + + ** Within this comment structure are the PostScript commands to create + + ** the EPS image. For us, that means the raster image. + + ** + + ** Raster Image + + ** The EPS spec requests that raster image data be enclosed within + + ** the comments: + + ** + + ** %%BeginData: format + + ** ... + + ** %%EndData + + ** + + ** Applications wishing to skip this data (such as a PostScript pretty + + ** printer) use the "format" argument of the %%BeginData line to see + + ** how much to skip. There are several format types. We use the form + + ** that lists the number of hex data lines that follow. We intentionally + + ** use hex, rather than binary, so that our EPS data files can be sent + + ** to printers over 7-bit ASCII connections. + + ** + + ** Immediately following the %%BeginData comment is an invokation of our + + ** ImEpsImage function defined in the prolog. That function reads in + + ** the rest of the data and renders the image on to the page. Also note + + ** that it is this line (the one invoking ImEpsImage) that is referenced + + ** by the %ImageData comment for PhotoShop found earlier in the file. + + ** So, PhotoShop can scan down to this line, then read the same raw + + ** image data that follows as does ImEpsImage when printing. + + ** + + ** Note that the ImEpsImage invokation is within the %%BeginData/%%EndData + + ** comments. As such we need to add one extra line to our hex line count + + ** on the %%BeginData line. + + ** + + ** Trailer + + ** The whole file is ended with a %%Trailer and %%EOF set of comments. + + ** + + **/ + + + + + + + + + + + +/* + + * FUNCTION DECLARATIONS + + */ + +#ifdef __STDC__ + +static int imEpsWrite1( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + + TagTable *flagsTable, TagTable *tagTable ); + +static int imEpsWriteGray( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + + TagTable *flagsTable, TagTable *tagTable ); + +static int imEpsWrite8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + + TagTable *flagsTable, TagTable *tagTable ); + +static int imEpsWriteRGB( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + + TagTable *flagsTable, TagTable *tagTable ); + +#else + +static int imEpsWrite1( ); + +static int imEpsWriteGray( ); + +static int imEpsWrite8( ); + +static int imEpsWriteRGB( ); + +#endif + + + + + + + + + + + +/* + + * FORMAT INFORMATION + + * imEpsNames - format's name and aliases + + * imEpsWriteMap - write attributes + + * imEpsMagicNumber - magic number + + * imEpsMagic - list of magic numbers + + * ImFileEpsFormat - master format description + + */ + +static char *imEpsNames[ ] = { "eps", "epi", "epsf", "epsi", NULL }; + +static ImFileFormatWriteMap imEpsWriteMap[ ] = + +{ + + /* in out */ + + /* VFB type, attr., type,ch,dep, attr., func */ + + { IMVFBMONO, 0, IN,1,1, 0, imEpsWrite1 }, + + { IMVFBINDEX8, 0, IN,1,8, 0, imEpsWriteGray }, + + { IMVFBINDEX8, C, IN,1,8, C, imEpsWrite8 }, + + { IMVFBRGB, 0, RGB,3,8, 0, imEpsWriteRGB }, + + { -1, 0, -1, 0, NULL }, + +}; + + + +static unsigned char imEpsMagicNumber[ ] = { '!', '%', 'P', 'S', '-', + + 'A', 'd', 'o', 'b', 'e', '-', '3', '.', '0', ' ', + + 'E', 'P', 'S', 'F', '-', '3', '.', '0' }; + +static ImFileMagic imEpsMagic[ ] = + +{ + + { 0, 23, imEpsMagicNumber }, + + { 0, 0, NULL }, + +}; + + + +ImFileFormat ImFileEpsFormat= + +{ + + imEpsNames, /* Names */ + + "Encapsulated PostScript file", /* Description */ + + "Adobe", /* Creator */ + + "None. Encapsulated PostScript files cannot be read!",/* Read support*/ + + "1-bit monochrome, 8-bit grayscale, 8-bit color index, and 24-bit\n\ + RGB color images.", /* Write support*/ + + imEpsMagic, /* Magic #'s */ + + IMNOMULTI, IMNOPIPE, /* Read? */ + + IMNOMULTI, IMPIPE, /* Write? */ + + NULL, NULL, imEpsWriteMap /* Maps */ + +}; + + + + + + + + + + + +static char imEpsHex[] = "0123456789abcdef"; + + + + + +/* + + * GLOBAL + + * imEpsHeader - PostScript header section + + * + + * DESCRIPTION + + * The PostScript file header is a set of special comments conforming + + * to the Adobe Document Structuring Conventions (DSC) as specified + + * in the Adobe PostScript Language Reference Manual (second edition). + + * + + * The DSC comments give the document name, creator, and whatnot. + + * Most importantly, the DSC also gives the bounding box for the + + * document. + + * + + * After the comment header is the %ImageData comment used by Adobe's + + * PhotoShop to locate the raw image data for it to read in. + + * + + * Note that this string will be sent through printf() to fill in the + + * comment arguments. So, we need to escape all % comment characters + + * with a second % to insure it gets through to the output. + + */ + + + +static char *imEpsHeader = "\ +%%!PS-Adobe-3.0 EPSF-3.0\n\ +%%%%BoundingBox: 0 0 %d %d\n\ +%%%%Creator: %s\n\ +%%%%For: %s\n\ +%%%%CreationDate: %s\ +%%%%Title: %s\n\ +%%%%Pages: 0\n\ +%%%%EndComments\n\ +%%ImageData: %d %d %d %d %d %d %d \"ImEpsImage\"\n\ +"; + + + + + + + + + + + +/* + + * GLOBALS + + * imEpsImageColor - Color printing PostScript procedure + + * imEpsImageGray - Grayscale printing PostScript procedure + + * imEpsImageMono - Monochrome printing PostScript procedure + + * + + * DESCRIPTION + + * Each of these three globals defines the same function (proc) for the + + * PostScript file header. In each case, the ImEpsImage function takes + + * hex image data and renders it onto the page. The difference is only + + * in the type of data being fed ImEpsImage. + + * + + * Some parts of this PostScript code were inspired by similar procedures + + * designed by Loren "Buck" Buchanan of the Naval Research Lab at + + * Kestrel Associates Inc. His ideas are used here with his permission + + * and our thanks. + + */ + + + +static char *imEpsImageColor = "\ +%%BeginProlog\n\ +% PROC\n\ +% ImEpsImage\n\ +%\n\ +% DESCRIPTION\n\ +% RGB hex image data is read from the current file. If the PostScript\n\ +% device can handle color, the RGB image is imaged as is. If not, the\n\ +% RGB pixels are converted to grayscale using the NTSC Y equation and\n\ +% imaged.\n\ +\n\ +/ImEpsImage\n\ +{\n\ + /buffer ImEpsImageW 3 mul string def\n\ + /graybuffer ImEpsImageW string def\n\ +\n\ + ImEpsImageW ImEpsImageH 8\n\ + [ 1 0\n\ + 0 -1\n\ + 0 ImEpsImageH ]\n\ +\n\ + % Determine if the PostScript device can handle color by checking if\n\ + % the colorimage operator is defined.\n\ + systemdict /colorimage known\n\ + {\n\ + {\n\ + currentfile buffer readhexstring pop\n\ + } false 3\n\ + colorimage\n\ + }\n\ + {\n\ + % The PostScript device cannot do color. Convert to grayscale.\n\ + {\n\ + % Get the RGB data\n\ + currentfile buffer readhexstring pop pop\n\ +\n\ + % For each pixel...\n\ + 0 1 ImEpsImageW 1 sub\n\ + {\n\ + % Compute gray value and store in graybuffer\n\ + graybuffer exch\n\ + dup 3 mul dup 1 add dup 1 add\n\ + buffer exch get 0.114 mul\n\ + exch buffer exch get 0.587 mul add\n\ + exch buffer exch get 0.299 mul add\n\ + cvi\n\ + put\n\ + } for\n\ + graybuffer\n\ + }\n\ + image\n\ + } ifelse\n\ + showpage\n\ +} bind def\n\ +\n\ +%%EndProlog\n\ +"; + + + + + +static char *imEpsImageGray = "\ +%%BeginProlog\n\ +% PROC\n\ +% ImEpsImage\n\ +%\n\ +% DESCRIPTION\n\ +% Grayscale hex image data is read from the current file and imaged.\n\ +\n\ +/ImEpsImage\n\ +{\n\ + /buffer ImEpsImageW string def\n\ +\n\ + ImEpsImageW ImEpsImageH 8\n\ + [ 1 0\n\ + 0 -1\n\ + 0 ImEpsImageH ]\n\ + {\n\ + currentfile buffer readhexstring pop\n\ + }\n\ + image\n\ + showpage\n\ +} bind def\n\ +\n\ +%%EndProlog\n\ +"; + + + + + +static char *imEpsImageMono = "\ +%%BeginProlog\n\ +% PROC\n\ +% ImEpsImage\n\ +%\n\ +% DESCRIPTION\n\ +% Grayscale hex image data is read from the current file and imaged.\n\ +\n\ +/ImEpsImage\n\ +{\n\ + /buffer ImEpsImageW 7 add 8 idiv string def\n\ +\n\ + ImEpsImageW ImEpsImageH 1\n\ + [ 1 0\n\ + 0 -1\n\ + 0 ImEpsImageH ]\n\ + {\n\ + currentfile buffer readhexstring pop\n\ + }\n\ + image\n\ + showpage\n\ +} bind def\n\ +\n\ +%%EndProlog\n\ +"; + + + + + + + + + + +/* + + * GLOBAL + + * imEpsData - PostScript data section + + * + + * DESCRIPTION + + * The data section of a PostScript script gives the image data itself. + + * In our case it defines the size of the image and invokes the image + + * rendering procedure. Following it we dump the image data. + + */ + + + +static char *imEpsData = "\ +%%%%Page: 1 1\n\ +userdict begin\n\ +/ImEpsImageW %d def %% Width in pixels\n\ +/ImEpsImageH %d def %% Height in pixels\n\ +%%%%BeginData: %d Hex Lines\n\ +ImEpsImage\n\ +"; + + + + + + + + + + + +/* + + * GLOBAL + + * imEpsTrailer - PostScript trailer section + + * + + * DESCRIPTION + + * The trailer of the page, and file, is just a bunch of DSC keywords. + + */ + + + +static char *imEpsTrailer = "\ +%%EndData\n\ +end\n\ +%%PageTrailer\n\ +%%Trailer\n\ +%%EOF\n\ +"; + + + + + + + + + + + +/* + + * FUNCTION + + * imEpsWrite1 - write a 1-bit monochrome image to a PostScript file + + * imEpsWriteGray - write a grayscale image to a PostScript file + + * imEpsWrite8 - write an 8-bit index image to a PostScript file + + * imEpsWriteRGB - write an RGB image to a PostScript file + + * + + * DESCRIPTION + + * The image is retrieved from the tag table and a PostScript header + + * giving the image's dimensions output. The appropriate PostScript + + * procedure is dumped (grayscale or color pixel handling), followed + + * by standard execution code and the pixels themselves. + + */ + + + +static int /* Returns # of entries used */ + +#ifdef __STDC__ + +imEpsWrite1( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + + TagTable *flagsTable, TagTable *tagTable ) + +#else + +imEpsWrite1( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* Type of I/O to perform */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Table of info to write */ + +#endif + +{ + + int ipix; /* pixel counter */ + + ImVfbPtr p; /* VFB pixel pointer */ + + int pixel; /* Gray pixel color */ + + int x, y; /* Pixel location */ + + int xdim; /* # columns */ + + int ydim; /* # rows */ + + ImVfb *srcVfb; /* VFB to convert */ + + time_t clock; /* Clock time */ + + char message[100]; /* information for ImInfo */ + + + + + + /* Turn fd into fp so we can use fprintf. */ + + if ( (ioType & IMFILEIOFD) && (fp = fdopen( fd, "rb" )) == NULL ) + + { + + ImErrNo = IMESYS; + + return ( -1 ); + + } + + + + /* Dump the header, prolog, script, and data sections. */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &srcVfb ); + + xdim = ImVfbQWidth( srcVfb ); + + ydim = ImVfbQHeight( srcVfb ); + + clock = time( NULL ); + +#ifndef WIN32 + + fprintf( fp, imEpsHeader, + + xdim, ydim, /* BoundingBox: */ + + ImErrorProgramName, /* Creator: */ + + getlogin( ), /* For: */ + + ctime( &clock ), /* CreationDate: */ + + ImErrorFileName, /* Title: */ + + /* ImageData: */ + + xdim, ydim, 1, /* Width, height, depth */ + + 1, 0, /* # channels, pad chan */ + + (xdim*1+7)/8, /* block size */ + + 2 ); /* hex format */ + +#else + + fprintf( fp, imEpsHeader, + + xdim, ydim, /* BoundingBox: */ + + ImErrorProgramName, /* Creator: */ + + "Virtual Monty", /* For: */ + + ctime( &clock ), /* CreationDate: */ + + ImErrorFileName, /* Title: */ + + /* ImageData: */ + + xdim, ydim, 1, /* Width, height, depth */ + + 1, 0, /* # channels, pad chan */ + + (xdim*1+7)/8, /* block size */ + + 2 ); /* hex format */ + +#endif + + sprintf (message, ImErrorFileName); + + ImInfo ("Image Name",message); + + sprintf (message, "%d x %d",xdim, ydim); + + ImInfo ("Resolution",message); + + ImInfo ("Type","1-bit Monochrome"); + + + + /* + + * Dump the preview. Nearly identical to image data except: + + * - 0 = white and 1 = black instead of the usual way. + + * - bottom to top instead of top to bottom. + + */ + + fprintf( fp, "%%%%BeginPreview: %d %d %d %d\n%%", xdim, ydim, 1, + + (((xdim + 7) / 8) * 2 * ydim + 71) / 72 ); + + for ( ipix = 0, y = ydim-1; y >= 0; y-- ) + + { + + p = ImVfbQPtr( srcVfb, 0, y ); + + for ( pixel = 0, x = 1; x <= xdim; x++, ImVfbSInc( srcVfb, p ) ) + + { + + pixel = (pixel << 1) | ((~ImVfbQMono( srcVfb, p ))&0x1); + + if ( (x & 0x7) == 0 ) + + { + + putc( imEpsHex[ ((pixel >> 4) & 0xF) ], fp ); + + putc( imEpsHex[ (pixel & 0xF) ], fp ); + + pixel = 0; + + if( ++ipix >= 36 ) + + { + + putc( '\n', fp ); + + putc( '%', fp ); + + ipix = 0; + + } + + } + + } + + if ( (xdim & 0x7) != 0 ) + + { + + pixel <<= 8 - (xdim & 0x7); + + putc( imEpsHex[ ((pixel >> 4) & 0xF) ], fp ); + + putc( imEpsHex[ (pixel & 0xF) ], fp ); + + if( ++ipix >= 36 ) + + { + + putc( '\n', fp ); + + putc( '%', fp ); + + ipix = 0; + + } + + } + + } + + fprintf( fp, "\n%%%%EndPreview\n" ); + + + + + + /* + + * Dump the data routine. + + * + + * Compute the number of hex lines of data written out and put + + * that in the args of the %%BeginData comment. Note that we + + * add 1 extra line for the "ImPsImage" function invokation. + + */ + + fprintf( fp, "%s", imEpsImageMono ); + + fprintf( fp, imEpsData, + + xdim, /* ImEpsImageWidth */ + + ydim, /* ImEpsImageHeight */ + + 1 + (((xdim + 7) / 8) * 2 * ydim + 71) / 72 ); /* Lines*/ + + + + + + /* Dump the pixels. */ + + p = ImVfbQFirst( srcVfb ); + + for ( ipix = 0, y = ydim - 1; y >= 0; y-- ) + + { + + for ( pixel = 0, x = 1; x <= xdim; x++, ImVfbSInc( srcVfb, p ) ) + + { + + pixel = (pixel << 1) | (ImVfbQMono( srcVfb, p ) & 0x1); + + if ( (x & 0x7) == 0 ) + + { + + putc( imEpsHex[ ((pixel >> 4) & 0xF) ], fp ); + + putc( imEpsHex[ (pixel & 0xF) ], fp ); + + pixel = 0; + + if( ++ipix >= 36 ) + + { + + /* Add a line break every 36*2=72 char*/ + + putc( '\n', fp ); + + ipix = 0; + + } + + } + + } + + if ( (xdim & 0x7) != 0 ) + + { + + pixel <<= 8 - (xdim & 0x7); + + putc( imEpsHex[ ((pixel >> 4) & 0xF) ], fp ); + + putc( imEpsHex[ (pixel & 0xF) ], fp ); + + if( ++ipix >= 36 ) + + { + + /* Add a line break every 36*2=72 characters*/ + + putc( '\n', fp ); + + ipix = 0; + + } + + } + + } + + + + /* Dump the trailer. */ + + fprintf( fp, "\n%s", imEpsTrailer ); + + fflush( fp ); + + + + return ( 1 ); /* Used VFB from tag table */ + +} + + + + + +static void /* Returns nothing */ + +#ifdef __STDC__ + +imEpsWriteGrayPreview( FILE *fp, ImVfb *srcVfb ) + +#else + +imEpsWriteGrayPreview( fp, srcVfb ) + + FILE *fp; /* File to write to */ + + ImVfb *srcVfb; /* VFB to write out */ + +#endif + +{ + + int ipix; /* pixel counter */ + + ImVfbPtr p; /* VFB pixel pointer */ + + int pixel; /* Gray pixel color */ + + ImVfb *grayVfb; /* Grayscale VFB */ + + int x, y; /* Pixel location */ + + int xdim; /* # columns */ + + int ydim; /* # rows */ + + + + xdim = ImVfbQWidth( srcVfb ); + + ydim = ImVfbQHeight( srcVfb ); + + + + if ( (ImVfbQFields( srcVfb ) & IMVFBINDEX8) == 0 || + + ImVfbQClt( srcVfb ) != IMCLTNULL ) + + { + + /* Not a grayscale VFB. Make one first. */ + + grayVfb = ImVfbToGray( srcVfb, IMVFBNEW ); + + } + + else + + grayVfb = srcVfb; + + + + /* Write out a grayscale preview image. */ + + fprintf( fp, "%%%%BeginPreview: %d %d %d %d\n%%", xdim, ydim, 8, + + (xdim * 2 * ydim + 71) / 72); + + for ( ipix = 0, y = ydim-1; y >= 0; y-- ) + + { + + p = ImVfbQPtr( grayVfb, 0, y ); + + for ( x = 0; x < xdim; x++, ImVfbSInc( grayVfb, p ) ) + + { + + pixel = ImVfbQGray( grayVfb, p ); + + putc( imEpsHex[ (pixel>>4) & 0xf ], fp ); + + putc( imEpsHex[ pixel & 0xf ], fp ); + + + + if( ++ipix >= 36 ) + + { + + /* Add a line break every 36*2=72 characters*/ + + putc( '\n', fp ); + + putc( '%', fp ); + + ipix = 0; + + } + + } + + } + + fprintf( fp, "\n%%%%EndPreview\n" ); + + + + if ( grayVfb != srcVfb ) + + ImVfbFree( grayVfb ); + +} + + + +static int /* Returns # of entries used */ + +#ifdef __STDC__ + +imEpsWriteGray( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + + TagTable *flagsTable, TagTable *tagTable ) + +#else + +imEpsWriteGray( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* Type of I/O to perform */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Table of info to write */ + +#endif + +{ + + int ipix; /* pixel counter */ + + ImVfbPtr p; /* VFB pixel pointer */ + + int pixel; /* Gray pixel color */ + + ImVfbPtr last; /* Last pixel in VFB */ + + int xdim; /* # columns */ + + int ydim; /* # rows */ + + ImVfb *srcVfb; /* VFB to convert */ + + time_t clock; /* Clock time */ + + char message[100]; /* information for ImInfo */ + + + + + + /* Turn fd into fp so we can use fprintf. */ + + if ( (ioType & IMFILEIOFD) && (fp = fdopen( fd, "rb" )) == NULL ) + + { + + ImErrNo = IMESYS; + + return ( -1 ); + + } + + + + + + /* Dump the header. */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &srcVfb ); + + xdim = ImVfbQWidth( srcVfb ); + + ydim = ImVfbQHeight( srcVfb ); + + clock = time( NULL ); + +#ifndef WIN32 + + fprintf( fp, imEpsHeader, + + xdim, ydim, /* BoundingBox: */ + + ImErrorProgramName, /* Creator: */ + + getlogin( ), /* For: */ + + ctime( &clock ), /* CreationDate: */ + + ImErrorFileName, /* Title: */ + + /* ImageData: */ + + xdim, ydim, 8, /* Width, height, depth */ + + 1, 0, /* # channels, pad chan */ + + (xdim*1+7)/8, /* block size */ + + 2 ); /* hex format */ + +#else + + fprintf( fp, imEpsHeader, + + xdim, ydim, /* BoundingBox: */ + + ImErrorProgramName, /* Creator: */ + + "Virtual Monty", /* For: */ + + ctime( &clock ), /* CreationDate: */ + + ImErrorFileName, /* Title: */ + + /* ImageData: */ + + xdim, ydim, 8, /* Width, height, depth */ + + 1, 0, /* # channels, pad chan */ + + (xdim*1+7)/8, /* block size */ + + 2 ); /* hex format */ + +#endif + + sprintf (message, ImErrorFileName); + + ImInfo ("Image Name",message); + + sprintf (message, "%d x %d",xdim, ydim); + + ImInfo ("Resolution",message); + + ImInfo ("Type","8-bit Grayscale"); + + + + + + /* + + * Dump the Preview data. Identical to grayscale data except + + * bottom up. + + */ + + imEpsWriteGrayPreview( fp, srcVfb ); + + + + + + /* + + * Dump the data routine. + + * + + * Compute the number of hex lines of data written out and put + + * that in the args of the %%BeginData comment. Note that we + + * add 1 extra line for the "ImPsImage" function invokation. + + */ + + fprintf( fp, "%s", imEpsImageGray ); + + fprintf( fp, imEpsData, + + xdim, /* ImEpsImageWidth */ + + ydim, /* ImEpsImageHeight */ + + 1 + (xdim * 2 * ydim + 71) / 72 ); /* # of lines */ + + + + + + /* Dump the pixels. */ + + p = ImVfbQFirst( srcVfb ); + + last = ImVfbQLast( srcVfb ); + + for ( ipix = 0; p <= last; ImVfbSInc( srcVfb, p ) ) + + { + + pixel = ImVfbQGray( srcVfb, p ); + + putc( imEpsHex[ (pixel>>4) & 0xf ], fp ); + + putc( imEpsHex[ pixel & 0xf ], fp ); + + + + if( ++ipix >= 36 ) + + { + + /* Add a line break every 36*2=72 characters */ + + putc( '\n', fp ); + + ipix = 0; + + } + + } + + + + /* Dump the trailer. */ + + fprintf( fp, "\n%s", imEpsTrailer ); + + fflush( fp ); + + + + return ( 1 ); /* Used VFB from tag table */ + +} + + + +static int /* Returns # of entries used */ + +#ifdef __STDC__ + +imEpsWrite8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + + TagTable *flagsTable, TagTable *tagTable ) + +#else + +imEpsWrite8( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* Type of I/O to perform */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Table of info to write */ + +#endif + +{ + + int ipix; /* pixel counter */ + + ImVfbPtr p; /* VFB pixel pointer */ + + int value; /* CLT value */ + + ImVfbPtr last; /* Last pixel in VFB */ + + int pixel; /* Gray pixel color */ + + ImClt *clt; /* VFB's color lookup table */ + + ImCltPtr c; /* CLT entry pointer */ + + int xdim; /* # columns */ + + int ydim; /* # rows */ + + ImVfb *srcVfb; /* VFB to convert */ + + time_t clock; /* Clock time */ + + char message[100]; /* information for ImInfo */ + + + + + + /* Turn fd into fp so we can use fprintf. */ + + if ( (ioType & IMFILEIOFD) && (fp = fdopen( fd, "rb" )) == NULL ) + + { + + ImErrNo = IMESYS; + + return ( -1 ); + + } + + + + + + /* Dump the header. */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &srcVfb ); + + xdim = ImVfbQWidth( srcVfb ); + + ydim = ImVfbQHeight( srcVfb ); + + clock = time( NULL ); + +#ifndef WIN32 + + fprintf( fp, imEpsHeader, + + xdim, ydim, /* BoundingBox: */ + + ImErrorProgramName, /* Creator: */ + + getlogin( ), /* For: */ + + ctime( &clock ), /* CreationDate: */ + + ImErrorFileName, /* Title: */ + + /* ImageData: */ + + xdim, ydim, 8, /* Width, height, depth */ + + 3, 0, /* # channels, pad chan */ + + 1, /* block size (interleaved)*/ + + 2 ); /* hex format */ + +#else + + fprintf( fp, imEpsHeader, + + xdim, ydim, /* BoundingBox: */ + + ImErrorProgramName, /* Creator: */ + + "Virtual Monty", /* For: */ + + ctime( &clock ), /* CreationDate: */ + + ImErrorFileName, /* Title: */ + + /* ImageData: */ + + xdim, ydim, 8, /* Width, height, depth */ + + 3, 0, /* # channels, pad chan */ + + 1, /* block size (interleaved)*/ + + 2 ); /* hex format */ + +#endif + + sprintf (message, ImErrorFileName); + + ImInfo ("Image Name",message); + + sprintf (message, "%d x %d",xdim, ydim); + + ImInfo ("Resolution",message); + + ImInfo ("Type","8-bit Color Indexed"); + + ImInfo ("Color Table","256 Entries"); + + + + + + /* Dump the preview image as 8-bit grayscale, bottom to top. */ + + imEpsWriteGrayPreview( fp, srcVfb ); + + + + + + /* + + * Dump the data routine. + + * + + * Compute the number of hex lines of data written out and put + + * that in the args of the %%BeginData comment. Note that we + + * add 1 extra line for the "ImPsImage" function invokation. + + */ + + fprintf( fp, "%s", imEpsImageColor ); + + fprintf( fp, imEpsData, + + xdim, /* ImEpsImageWidth */ + + ydim, /* ImEpsImageHeight */ + + 1 + (xdim * 6 * ydim + 71) / 72 ); /* # of lines */ + + + + + + /* + + * Dump the pixels. RGBRGBRGB... + + */ + + p = ImVfbQFirst( srcVfb ); + + last = ImVfbQLast( srcVfb ); + + clt = ImVfbQClt( srcVfb ); + + for ( ipix = 0; p <= last; ImVfbSInc( srcVfb, p ) ) + + { + + pixel = ImVfbQIndex8( srcVfb, p ); + + c = ImCltQPtr( clt, pixel ); + + + + value = ImCltQRed( c ); + + putc( imEpsHex[ (value>>4) & 0xf ], fp ); + + putc( imEpsHex[ value & 0xf ], fp ); + + + + value = ImCltQGreen( c ); + + putc( imEpsHex[ (value>>4) & 0xf ], fp ); + + putc( imEpsHex[ value & 0xf ], fp ); + + + + value = ImCltQBlue( c ); + + putc( imEpsHex[ (value>>4) & 0xf ], fp ); + + putc( imEpsHex[ value & 0xf ], fp ); + + + + if( ++ipix >= 12 ) + + { + + /* Add a line break every 12*3*2=72 characters */ + + putc( '\n', fp ); + + ipix = 0; + + } + + } + + + + /* Dump the trailer. */ + + fprintf( fp, "\n%s", imEpsTrailer ); + + fflush( fp ); + + + + return ( 2 ); /* Used VFB & CLT from tag table */ + +} + + + +static int /* Returns # of entries used */ + +#ifdef __STDC__ + +imEpsWriteRGB( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + + TagTable *flagsTable, TagTable *tagTable ) + +#else + +imEpsWriteRGB( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* Type of I/O to perform */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Table of info to write */ + +#endif + +{ + + int ipix; /* pixel counter */ + + ImVfbPtr p; /* VFB pixel pointer */ + + int pixel; /* RGB pixel color */ + + ImVfbPtr last; /* Last pixel in VFB */ + + int xdim; /* # columns */ + + int ydim; /* # rows */ + + ImVfb *srcVfb; /* VFB to convert */ + + time_t clock; /* Clock time */ + + char message[100]; /* information for ImInfo */ + + + + + + /* Turn fd into fp so we can use fprintf. */ + + if ( (ioType & IMFILEIOFD) && (fp = fdopen( fd, "rb" )) == NULL ) + + { + + ImErrNo = IMESYS; + + return ( -1 ); + + } + + + + + + /* Dump the header. */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &srcVfb ); + + xdim = ImVfbQWidth( srcVfb ); + + ydim = ImVfbQHeight( srcVfb ); + + clock = time( NULL ); + +#ifndef WIN32 + + fprintf( fp, imEpsHeader, + + xdim, ydim, /* BoundingBox: */ + + ImErrorProgramName, /* Creator: */ + + getlogin( ), /* For: */ + + ctime( &clock ), /* CreationDate: */ + + ImErrorFileName, /* Title: */ + + /* ImageData: */ + + xdim, ydim, 8, /* Width, height, depth */ + + 3, 0, /* # channels, pad chan */ + + 1, /* block size (interleaved)*/ + + 2 ); /* hex format */ + +#else + + fprintf( fp, imEpsHeader, + + xdim, ydim, /* BoundingBox: */ + + ImErrorProgramName, /* Creator: */ + + "Virtual Monty", /* For: */ + + ctime( &clock ), /* CreationDate: */ + + ImErrorFileName, /* Title: */ + + /* ImageData: */ + + xdim, ydim, 8, /* Width, height, depth */ + + 3, 0, /* # channels, pad chan */ + + 1, /* block size (interleaved)*/ + + 2 ); /* hex format */ + +#endif + + + + sprintf (message, ImErrorFileName); + + ImInfo ("Image Name",message); + + sprintf (message, "%d x %d",xdim, ydim); + + ImInfo ("Resolution",message); + + ImInfo ("Type","24-bit RGB"); + + + + + + /* Dump the preview image as 8-bit grayscale, bottom to top. */ + + imEpsWriteGrayPreview( fp, srcVfb ); + + + + + + /* + + * Dump the data routine. + + * + + * Compute the number of hex lines of data written out and put + + * that in the args of the %%BeginData comment. Note that we + + * add 1 extra line for the "ImPsImage" function invokation. + + */ + + fprintf( fp, "%s", imEpsImageColor ); + + fprintf( fp, imEpsData, + + xdim, /* ImEpsImageWidth */ + + ydim, /* ImEpsImageHeight */ + + 1 + (xdim * 6 * ydim + 71) / 72 ); /* # of lines */ + + + + /* + + * Dump the pixels. RGBRGBRGB... + + */ + + p = ImVfbQFirst( srcVfb ); + + last = ImVfbQLast( srcVfb ); + + for ( ipix = 0; p <= last; ImVfbSInc( srcVfb, p ) ) + + { + + pixel = ImVfbQRed( srcVfb, p ); + + putc( imEpsHex[ (pixel>>4) & 0xf ], fp ); + + putc( imEpsHex[ pixel & 0xf ], fp ); + + + + pixel = ImVfbQGreen( srcVfb, p ); + + putc( imEpsHex[ (pixel>>4) & 0xf ], fp ); + + putc( imEpsHex[ pixel & 0xf ], fp ); + + + + pixel = ImVfbQBlue( srcVfb, p ); + + putc( imEpsHex[ (pixel>>4) & 0xf ], fp ); + + putc( imEpsHex[ pixel & 0xf ], fp ); + + + + if ( ++ipix >= 12 ) + + { + + /* Add a line break every 12*3*2=72 characters */ + + putc( '\n', fp ); + + ipix = 0; + + } + + } + + + + /* Dump the trailer. */ + + fprintf( fp, "\n%s", imEpsTrailer ); + + fflush( fp ); + + + + return ( 1 ); /* Used VFB from tag table */ + +} + diff --git a/utils/roq2/libim/imerrno.c b/utils/roq2/libim/imerrno.c new file mode 100644 index 0000000..9ae7a36 --- /dev/null +++ b/utils/roq2/libim/imerrno.c @@ -0,0 +1,394 @@ +/** + + ** $Header: /roq/libim/imerrno.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/imerrno.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** imerrno.c - Global Variables regarding errors + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** imerrno.c contains several global variables related to errors + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** ImErrorProgramName v program name + + ** ImErrorFileName v file name + + ** ImErrorHandler v error handler function pointer + + ** ImErrorStream v error message output stream + + ** + + ** ImErrNo v error number + + ** ImNNrr v number of error messages + + ** ImErrList v error messages + + ** + + ** NOTE: Don't add any functions to this file! This file was + + ** created because sun requires that global variables be + + ** isolated in seperate files when making shared objects! + + ** + + ** PRIVATE CONTENTS + + ** none + + ** + + ** HISTORY + + ** $Log: /roq/libim/imerrno.c $ + * + * 1 11/02/99 4:38p Zaphod + + * Revision 1.2 1995/06/29 00:28:04 bduggan + + * updated copyright year + + * + + * Revision 1.1 1995/06/16 08:34:53 bduggan + + * Initial revision + + * + + ** + + **/ + + + +#include "iminternal.h" + + + + + +/** + + ** CODE CREDITS + + ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1990. + + ** Custom development, Brian Duggan, San Diego Supercomputer Center, 1990. + + **/ + + + + + + + + + + + +/* + + * GLOBAL VARIABLE + + * ImErrorProgramName - program name + + * ImErrorFileName - file name + + * ImErrorHandler - error handler function pointer + + * ImErrorStream - error message output stream + + * ImInfoHandler - info handler function pointer + + * ImInfoStream - info message output stream + + * + + * FUNCTION + + * These globals are used by the ImError* macros in the reporting of + + * errors. All are set by quering a flagsTable using ImErrorFlags(). + + */ + + + +char *ImErrorProgramName = "program"; /* Program name */ + +char *ImErrorFileName = "stream"; /* File name */ + +FILE *ImErrorStream = NULL; /* Error stream */ + +FILE *ImInfoStream = NULL; /* Info stream */ + +#ifdef __STDC__ + +int (*ImErrorHandler)(int, int, char* ) = NULL; /* Error handler */ + +int (*ImInfoHandler)(char*,char*,char* )= NULL; /* Info handler */ + +#else + +int (*ImErrorHandler)( ) = NULL; /* Error handler */ + +int (*ImInfoHandler)( ) = NULL; /* Info handler */ + +#endif + + + + + + + + + + + +/* + + * GLOBAL VARIABLE + + * ImErrNo - error number + + * ImNNrr - number of error messages + + * ImErrList - error messages + + * + + * DESCRIPTION + + * On an error, the image manipulation routines return -1 and set + + * ImErrNo to an error code. The programmer may call ImPError + + * to print the associated error message to stderr, or may do the + + * message lookup in ImErrList themselves. + + */ + +int ImErrNo = -1; /* VFB package error number */ + +char *ImErrList[] = /* Error message list */ + +{ + + /* 0 IMESYS */ "System call error; see errno", + + /* 1 IMEMALLOC */ "Cannot allocate host memory", + + + + /* 2 IMENOCONTENTS */ "VFB contents mask == 0?", + + /* 3 IMENOFPDATA */ "No floating point data in VFB", + + /* 4 IMEBADINOUT */ "Illegal value for 'inout'", + + /* 5 IMEBADFBTYPE */ "Bad frame buffer type", + + /* 6 IMEFORMAT */ "Bad image file format selection", + + /* 7 IMENOFILE */ "File cannot be opened", + + /* 8 IMENOTINFO */ "Not enough information in VFB", + + /* 9 IMEBADFLIP */ "Illegal flip direction for VFB", + + /* 10 IMEBADALGORITHM */"Bad resolution change algorithm", + + + + /* 11 IMENOREAD */ "Read operation not possible on this image format", + + /* 12 IMENOWRITE */ "Write operation not possible on this image format", + + /* 13 IMENOVFB */ "No VFB provided to a function that needs one", + + /* 14 IMEMANYFB */ "Too many VFB's provided to a function that needs only one", + + /* 15 IMENOTRGB */ "RGB VFB image required", + + /* 16 IMEMAGIC */ "Bad magic number on image file", + + /* 17 IMEIMAGETYPE */ "Unknown image type", + + /* 18 IMECLTTYPE */ "Unknown CLT type", + + /* 19 IMEDEPTH */ "Unknown image depth", + + /* 20 IMECLTLENGTH */ "Bad CLT length", + + /* 21 IMENOCLT */ "No CLT provided to a function than needs one", + + /* 22 IMEMANYCLT */ "Too many CLT's provided to a function that needs only one", + + /* 23 IMEUNKNOWN */ "Unknown", + + /* 24 IMENULLTAGTABLE */"Null tag table given", + + /* 25 IMESYNTAX */ "Syntax error", + + /* 26 IMENOIMAGE */ "No image present in input file", + + /* 27 IMEUNSUPPORTED */ "VFB type unsupported for this operation", + + /* 28 IMEDIFFSIZE */ "VFB's have different X and Y dimensions", + + /* 29 IMEVERSION */ "Bad version number in header", + + /* 30 IMEPLANES */ "Unknown image plane config", + + /* 31 IMEOUTOFRANGE */ "Header value out of legal range", + + /* 32 IMEORIENTATION*/ "Unsupported image orientation", + + /* 33 IMEWIDTH */ "Zero or negative image width", + + /* 34 IMEHEIGHT */ "Zero or negative image height", + + /* 35 IMECONFLICT */ "Conflicting info in im header", + + /* 36 IMENOTINDEX8 */ "Index 8 VFB image required", + + /* 37 IMENOTINDEX16 */ "Index 16 VFB image required", + + /* 38 IMENOTMONO */ "Mono VFB image required", + + /* 39 IMEFIELD */ "Bad field mask", + + /* 40 IMENOTPOSSIBLE */ "Image conversion/output not possible as specified", + + /* 41 IMEDECODING */ "Error encountered while decoding image", + + /* 42 IMEENCODING */ "Error encountered while encoding image", + + /* 43 IMEOPERATION */ "Unknown operation", + + /* 44 IMEGRADUATION */ "Unknown graduation", + + /* 45 IMEHISTMAX */ "Maximum number of unqiue colors exceeded given parameter", + + /* 46 IMEBADRANGE */ "Inappropriate use of value range", + + /* 47 IMEKEY */ "Invalid keying parameter", + + /* 48 IMEADJUST */ "Invalid adjust parameter", + + /* 49 IMENOALPHA */ "No alpha field present when needed", + + /* 50 IMEPIXELREP */ "Source and destination are not exact multiples of each other for pixel replication", + + /* 51 IMEKEYFIELD */ "Bad keyField on pixel adjustment", + + /* 52 IMEADJUSTFIELD */ "Bad adjustField on pixel adjustment", + + /* 53 IMEIMPSHEAR */ "Impossible shear attempted (<-90 or >90)", + + /* 54 IMEBADCHANNEL */ "Invalid channel mask request", + + /* 55 IMEBADCHANNELS */ "Inconsistent channel mapping requests", + + /* 56 IMEVALOUTOFRANGE*/ "Value out of range", + + /* 56 IMEUNKNOWNFORMAT*/ "Could not discern format for file", + +}; + + + + + +int ImNErr = 58; /* Number of error messages */ + + + + + + + + + + + diff --git a/utils/roq2/libim/imfile.c b/utils/roq2/libim/imfile.c new file mode 100644 index 0000000..dd4af35 --- /dev/null +++ b/utils/roq2/libim/imfile.c @@ -0,0 +1,2673 @@ +/** + ** $Header: /roq/libim/imfile.c 1 11/02/99 4:38p Zaphod $ + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + ** a division of General Atomics, San Diego, California, USA + ** + ** Users and possessors of this source code are hereby granted a + ** nonexclusive, royalty-free copyright and design patent license to + ** use this code in individual software. License is not granted for + ** commercial resale, in whole or in part, without prior written + ** permission from SDSC. This source is provided "AS IS" without express + ** or implied warranty of any kind. + ** + ** For further information contact: + ** E-Mail: info@sds.sdsc.edu + ** + ** Surface Mail: Information Center + ** San Diego Supercomputer Center + ** P.O. Box 85608 + ** San Diego, CA 92138-5608 + ** (619) 534-5000 + **/ + +#define HEADER " $Header: /roq/libim/imfile.c 1 11/02/99 4:38p Zaphod $" + +/** + ** FILE + ** imfile.c - file I/O for the image manipulation library + ** + ** PROJECT + ** libim - SDSC image manipulation library + ** + ** DESCRIPTION + ** imfile.c contains the top level read and write routines of the + ** image library and the dispatch table for each format supported. + ** + ** + ** PUBLIC CONTENTS + ** d =defined constant + ** f =function + ** m =defined macro + ** t =typedef/struct/union + ** v =variable + ** ? =other + ** + ** ImFileQFormat f query the format of a file + ** ImFileQFFormat f query the format of a file + ** + ** ImFileQNFormat f query # of formats + ** + ** ImFileRead f read an image file from a file descriptor + ** ImFileFRead f read an image file from a file pointer + ** ImFileWrite f write an image file to a file descriptor + ** ImFileFWrite f write an image file to a file pointer + ** ImFileFindFormat f find the format info given a format name + ** + ** imFileQCompression f query the compression scheme of a file + ** ImFileFindCompressionScheme f find the scheme info given a name + ** + ** + ** PRIVATE CONTENTS + ** IOTYPE d figure the IoType for an unbuffered stream + ** FIOTYPE d figure the IoType for a buffered stream + ** + ** imFileRead f do the real work of read + ** imFileSelectMap f select a good write map to use + ** imFileReadCompressedFile f Read a compressed file + ** imFileWriteCompressedFile f Write a compressed file + ** + ** HISTORY + ** $Log: /roq/libim/imfile.c $ + * + * 1 11/02/99 4:38p Zaphod + ** Revision 1.44 1995/08/15 08:37:32 bduggan + ** Modified ImFileWrite to return the name of the format. + ** + ** Revision 1.44 1995/08/15 08:37:32 bduggan + ** Modified ImFileWrite to return the name of the format. + ** + ** Revision 1.43 1995/06/30 21:59:39 bduggan + ** removed mktemp prototype + ** put 'int' between 'static' and the variable name + ** + ** Revision 1.42 1995/06/29 00:28:04 bduggan + ** updated copyright year + ** + ** Revision 1.41 1995/06/29 00:19:29 bduggan + ** changed error message + ** + ** Revision 1.40 1995/06/16 08:40:19 bduggan + ** Improved compression support + ** + ** Revision 1.39 1995/05/19 13:01:17 bduggan + ** Added imcompress interface + ** + ** Revision 1.38 1995/05/06 01:43:56 bduggan + ** Added .Z compression support! + ** + ** Revision 1.38 1995/05/06 01:43:56 bduggan + ** Added .Z compression support! + ** + ** Revision 1.37 1995/04/03 21:23:57 bduggan + ** took out #ifdef NEWMAGIC + ** + ** Revision 1.36 1995/02/16 21:38:44 bduggan + ** Added support checking for 'image compression quality' tag + ** Moved ImFileFormatEquivs and ImFileFormatOptions from here + ** to imtools.c + ** + ** Revision 1.35 1995/01/10 23:22:33 bduggan + ** Added grouping option for tiles vs. scanlines + ** + ** Revision 1.34 94/10/03 11:29:33 nadeau + ** Updated to ANSI C and C++ compatibility. + ** Removed all use of register keyword. + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + ** Changed all float arguments to double. + ** Added forward declarations. + ** Added misc. casts to passify SGI and DEC compilers. + ** Changed all macros and defined constants to have names + ** starting with IM. + ** Rearranged magic number structures for format handlers. + ** Made format handler routines static (i.e., local to file). + ** Updated comments, adding format descriptions and references. + ** Updated indenting on some code. + ** Updated copyright message. + ** + ** Revision 1.33 93/10/22 23:20:17 nadeau + ** Added restoration of binary I/O library state around all + ** file I/O calls. + ** + ** Revision 1.32 92/11/24 11:48:58 groening + ** Corrected problem that caused the wrong write-map to be + ** selected for non-RGB images when the flagsTable requested + ** interleaving. + ** + ** Revision 1.31 92/11/04 11:45:03 groening + ** since ImFileFormats got changed around + ** I changed all the types dealing with + ** ImFileFormats, also added multiple magic + ** number capabilities. cheerio! + ** + ** Revision 1.30 92/10/12 15:55:23 vle + ** Added code to handle ImInfo stuff. + ** + ** Revision 1.29 92/09/03 17:00:55 nadeau + ** Fixed a minor typo. + ** + ** Revision 1.28 92/09/03 16:47:18 vle + ** Updated calls to ImVfbGrayRamp() to match new parameters. + ** + ** Revision 1.27 92/04/09 09:33:11 groening + ** In order for the compiler to like the code had to add extern statements. + ** + ** Revision 1.26 91/10/03 08:59:13 nadeau + ** Changed 'interlace' to 'interleave'. Made format arguments + ** only show up under -fullhelp. + ** + ** Revision 1.25 91/03/12 10:59:49 nadeau + ** Fixed bug in fopen of temp files for read and write. + ** + ** Revision 1.24 91/03/08 14:27:01 nadeau + ** Totally rewrite. Moved the formats table and declarations into + ** a separate file (imfmt.c). Enhanced ImFileRead to automatically + ** copy pipe data into a temp file for formats that cannot handle + ** pipes directly. Enhanced ImFileWrite to do likewise. Lots of + ** changes to ImFileWrite to make it automatically do VFB depth + ** conversions prior to calling format write code. It also does + ** lots of standard error checking. + ** + ** Revision 1.23 91/01/30 17:14:36 nadeau + ** First attempt at restructuring to support more error checking + ** in ImFile before it gets to the format routines. + ** + ** Revision 1.22 91/01/22 09:48:23 nadeau + ** NULL-ed out all info query calls. Added more equiv names for + ** various formats. + ** + ** Revision 1.21 90/11/14 18:35:03 mjb + ** Added cgm support + ** + ** Revision 1.20 90/08/30 10:00:27 moreland + ** Added MacPaint support + ** + ** Revision 1.19 90/08/02 13:32:51 mcleodj + ** Added PIC support + ** + ** Revision 1.18 90/07/31 12:42:39 mjb + ** Changed PICT routine names to match those in impict.c + ** + ** Revision 1.17 90/07/30 16:38:47 moreland + ** Enabled Apple PICT format. + ** Sorry, no single magic number... + ** + ** Revision 1.16 90/07/30 16:31:20 nadeau + ** Remove 'x' as one of the equivalent names for XWD. Added support for + ** the 'x' format used by Stardent for their AVS system. + ** + ** Revision 1.15 90/07/25 13:32:02 todd + ** Add imtiff back in to the list of known formats + ** + ** Revision 1.14 90/07/24 15:34:23 ferrerj + ** Temporarily removed TIFF support. + ** + ** Revision 1.13 90/07/24 13:51:23 nadeau + ** Added XWD support. + ** + ** Revision 1.12 90/07/23 13:48:34 nadeau + ** Added HDF support and prepared for PIC support. + ** + ** Revision 1.11 90/07/05 11:41:36 todd + ** add .tif and .TIF to equivalent name table. Changed tif magic number + ** to 4d4d (MBF). 4f4f is LBF. + ** + ** Revision 1.10 90/07/02 13:18:15 nadeau + ** Changed ImTag* to Tag* (new names), added new error handling code, added + ** flags table code, entered SYNU format, unifdefed TIFF format, and + ** rearranged the names of all read/write calls from ImReadXXX to + ** ImXXXRead, etc. + ** + ** Revision 1.9 90/06/25 14:32:19 nadeau + ** Changed ImTag* to Tag* (new names). + ** + ** Revision 1.8 90/06/25 14:27:11 ferrerj + ** Removed #ifdef's from RGB format initialization. + ** + ** Revision 1.7 90/06/04 10:42:08 nadeau + ** Just added some curly braces. + ** + ** Revision 1.6 90/05/15 16:29:13 todd + ** Add imiff.c and imrla.c to tables + ** + ** Revision 1.5 90/05/15 13:59:30 nadeau + ** Removed old format table code between #ifdefs and added more comments. + ** + ** Revision 1.4 90/05/11 09:53:29 nadeau + ** Changed format arg to read/write calls to a character string. Added + ** a master file format table with lots of info on the format, including + ** magic number and equivalent names. Added format query calls to intuit + ** a file's format. Added calls to add the format names to the options + ** and equivs lists for the arg package. All additions are #ifdef'ed in. + ** + ** Revision 1.3 90/04/09 10:58:56 todd + ** Added Sun-TAAC IFF format ImReadIff() and ImWriteIff() + ** + ** Revision 1.2 90/03/28 11:15:17 nadeau + ** Dropped the format set/query functions. Added format and flags + ** arguments to the read and write calls. Added two new variations + ** of the read and write calls: fd and fp read. + ** + ** Revision 1.1 90/03/06 17:32:14 nadeau + ** Initial revision + ** + **/ + +//#include +//#include +#include "im.h" +#include "iminternal.h" +#include "impref.h" + +/** + ** CODE CREDITS + ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1990. + ** Custom development, Brian Duggan, San Diego Supercomputer Center, 1995. + **/ + + +/* NOTE + * + * Note that here and throughout the library, we try to refer to a file + * compression 'scheme' and an image 'format'; not a compression + * 'format', (and not an image 'scheme') in order to keep things clear. + */ + + +/* + * DEFINES + * IMREADMAGIC + * IMREADFMAGIC + * IMMAXMAGIC + * + * DESCRIPTION + * These macros are used by input routines in + * imfile.c and imcompress.c to query the magic + * number of a file. + */ + +#define IMMAXMAGIC 100 /* Max size magic number */ + +#define IMREADMAGIC(fd,buf,size,at) \ +{ \ + long location; /* Saved file location */\ + location = lseek( (fd), 0, L_INCR ); \ + lseek( (fd), (at), L_SET ); \ + BinRead( (fd), (buf), UCHAR, 1, (size) ); \ + lseek( (fd), location, L_SET ); \ +} +#define IMREADFMAGIC(fp,buf,size,at) \ +{ \ + long location; /* Saved file location */\ + location = fseek( (fp), 0, F_INCR ); \ + fseek( (fp), (at), F_SET ); \ + BinFRead( (fp), (buf), UCHAR, 1, (size) ); \ + fseek( (fp), location, F_SET ); \ +} + + + +/* + * FUNCTION DECLARATIONS + */ +#ifdef __STDC__ +/* extern char *mktemp( char * ); */ + +static char *imFileQFormat( int, int, FILE *, char * ); +static void imFileErrorTags( TagTable * ); +static void imFileInfoTags( TagTable * ); +static void imFileImageTags( TagTable *, int *, int *, int *, int *, + int *, int *, int *, int *, int *, int * ); +static int imFileRead( int, int, FILE *, char *, TagTable *, + TagTable * ); +static ImFileFormatWriteMap *imFileSelectMap( ImVfb *, ImFileFormat **, int, + int, int, int, int, int, int, int ); +static int imFileWrite( int, int, FILE *, char *, TagTable *, + TagTable * ); +static int imFileWriteSingle( int, int, FILE *, ImFileFormat **, + TagTable *, TagTable * ); +static int imFileWriteMultiple( int, int, FILE *, ImFileFormat **, + TagTable *, TagTable * ); +static int imFileWriteIt( ImFileFormatWriteMap *, int, int, FILE *, + ImFileFormat **, TagTable *, TagTable * ); +static int imFileReadCompressedFile(int , int , FILE* , TagTable* , TagTable* , char* ); +static int imFileWriteCompressedFile(int, int, FILE*, TagTable*, TagTable*, + ImFileFormatWriteMap *, char* ); +static char * imFileQCompression(int ioType, int fd, FILE* fp, TagTable* flagsTable); +#else +extern char *mktemp( ); + +static char *imFileQFormat( ); +static void imFileErrorTags( ); +static void imFileInfoTags( ); +static void imFileImageTags( ); +static int imFileRead( ); +static ImFileFormatWriteMap *imFileSelectMap( ); +static int imFileWrite( ); +static int imFileWriteSingle( ); +static int imFileWriteMultiple( ); +static int imFileReadCompressedFile(); +static int imFileWriteCompressedFile(); +static char * imFileQCompression(); +#endif + + + + + +/* + * FUNCTION + * imMakeTempFile() + * + * DESCRIPTION + * Because some files use byte offsets that point hither and yon + * throughout the file, we can't always handle an input or output + * stream "on the fly". + * + * For reads we copy the input stream to a file, then read it. + * For writes we write to a file, then copy it to the output stream. + * + * imMakeTempFile() generates a char* which should be used as the + * a temporary file. + */ + +#ifdef __STDC__ +static char* imMakeTempFile(void); +#else +static char* imMakeTempFile(); +#endif + + + + + + +/* + * MACRO + * IOTYPE - figure the IoType for an unbuffered stream + * FIOTYPE - figure the IoType for a buffered stream + * + * DESCRIPTION + * These macros determine whether the stream corresponds to a tty, + * pipe, or file. Tty's and pipes must be handled specially (without + * seeking) by the lower level read and write code. + * + * The following cases must be recognized (using input as an example): + * + * Command line Meaning Classification + * tool file read from file file + * tool < file redirect from file file + * tool < /dev/tty read from tty pipe + * tool read from tty pipe + * cat file | tool read from pipe pipe + * + * lseek() or fseek() on a pipe will fail. Seeking on a tty is supposed + * to fail, but doesn't on some versions of UNIX. + * + * isatty() reports whether an fd is a tty device. + */ + +#define IOTYPE(fd) \ + ((((lseek( (fd), 0, L_INCR )==-1) && (errno==ESPIPE))||isatty( (fd) ))?\ + (IMFILEIOFD | IMFILEIOPIPE) \ + : \ + (IMFILEIOFD | IMFILEIOFILE) \ + ) + +#define FIOTYPE(fp) \ + (((fseek( (fp), 0, F_INCR )==-1)||isatty( fileno(fp) ))? \ + (IMFILEIOFP | IMFILEIOPIPE) \ + : \ + (IMFILEIOFP | IMFILEIOFILE) \ + ) + + + + + +/* + * FUNCTION + * ImFileFindFormat - find the format info given a format name + * + * DESCRIPTION + * The format's name is converted to lower case, then looked up in the + * format table. A pointer to the found format table entry is returned. + */ + +ImFileFormat ** /* Returns ptr to file format info*/ +#ifdef __STDC__ +ImFileFindFormat( char *format ) +#else +ImFileFindFormat( format ) + char *format; /* Format name to look up */ +#endif +{ + ImFileFormat **pFormat; /* Pointer into format table */ + char **pName; /* Name list pointer */ + char lcFormat[IMMAXNAME];/* Lower case name */ + char *s; /* String pointer */ + + if (format==NULL || *format==NULL) + { + ImErrNo = IMEFORMATUNKNOWN; + return NULL; + } + + /* Convert to lower case. */ + for ( s = lcFormat; *format; s++, format++ ) + { + if ( isupper( *format ) ) + *s = tolower( *format ); + else + *s = *format; + } + *s = '\0'; + + /* Scan the format table. */ + for ( pFormat = ImGetFileFormats(); *pFormat; pFormat++ ) + { + for ( pName = (*pFormat)->format_names; *pName; pName++ ) + if ( strcmp( *pName, lcFormat ) == 0 ) + break; + if ( *pName ) + break; + } + + if ( ( pFormat== NULL ) || (!(*pName)) ) + { + ImErrNo = IMEFORMATUNKNOWN; + return ( NULL ); + } + return ( pFormat ); +} + + + + + +/* + * FUNCTION + * ImFileQFormat - query the format of a file + * ImFileQFFormat - query the format of a file + * imFileQFormat - do the actual format search + * + * DESCRIPTION + * ImFileQFormat( ) and ImFileQFFormat( ) call the underlying + * imFileQFormat( ) to do the actual work, telling it what type of + * file is open and how it was opened (descriptor or pointer). + * + * If input isn't a pipe, tty, or write-only file, we check the magic + * number against the format list. If that doesn't work, we check the + * file name extension against the format list. The name of the format, + * or NULL is returned. + */ + +char * /* Returns format name */ +#ifdef __STDC__ +ImFileQFormat( int fd, char *fileName ) +#else +ImFileQFormat( fd, fileName ) + int fd; /* File's descriptor */ + char *fileName; /* File's name */ +#endif +{ + return ( imFileQFormat( /*IOTYPE(fd)*/ 0, fd, NULL, fileName ) ); +} + + +char * /* Returns format name */ +#ifdef __STDC__ +ImFileQFFormat( FILE *fp, char *fileName ) +#else +ImFileQFFormat( fp, fileName ) + FILE *fp; /* File's pointer */ + char *fileName; /* File's name */ +#endif +{ + return ( imFileQFormat( /*FIOTYPE(fp)*/ 0, -1, fp, fileName ) ); +} + + +static char * /* Returns format name */ +#ifdef __STDC__ +imFileQFormat( int ioType, int fd, FILE *fp, char *fileName ) +#else +imFileQFormat( ioType, fd, fp, fileName ) + int ioType; /* File descriptor or pointer */ + int fd; /* File's descriptor */ + FILE *fp; /* File's pointer */ + char *fileName; /* File's name */ +#endif +{ + ImFileFormat **pFmt; /* Format list pointer */ + int length; /* Length of magic # */ + unsigned char magic1[IMMAXMAGIC];/* Test magic # */ + unsigned char magic2[IMMAXMAGIC];/* Another test magic # */ + unsigned char *magic; /* Current magic # */ + unsigned char *fmtMagic; /* Current format's magic # */ + int i; /* Counter */ + int doMagic = TRUE; /* Do we check magic numbers? */ + char *extension; /* File name extension */ + int ioOperation; /* Read or write? */ + ImFileMagic *pMagic; /* pointer to magic file info */ + char tmpExtension[128];/* Holds the extension */ + char *tmp; /* points to a char* */ + + /* + * Should we check the magic number? + * 1. If file is a pipe or a tty, NO. + * 2. If file is open for writing only, NO. + * 3. Otherwise, YES. + */ +/* if ( ioType & IMFILEIOPIPE ) + doMagic = FALSE; // Input is a pipe or a tty + else + { + if ( ioType & IMFILEIOFD ) + ioOperation = fcntl( fd, F_GETFL, &ioOperation ); + else + ioOperation = fcntl( fileno(fp), F_GETFL, &ioOperation); + if ( ioOperation == -1 ) + { + ImErrNo = IMESYS; // Bad fp/fd? + return ( NULL ); + } + if ( ioOperation == O_WRONLY ) + doMagic = FALSE; + } +*/ + + /* + * Get the magic number from the file and check it against each of + * the file format list. + */ + if ( doMagic ) + { + /* + * Read in the magic number at location 0 (where most are). + */ + if ( ioType & IMFILEIOFD ) + { + IMREADMAGIC( fd, magic1, IMMAXMAGIC, 0 ); + } + else + { + IMREADFMAGIC( fp, magic1, IMMAXMAGIC, 0 ); + } + + + /* + * Check it against the format list. If a format doesn't + * have a magic number, skip it. If it has a magic number, + * but not at byte 0 in the file, then read in and test + * with that magic. Otherwise test with the magic numbers + * read in above. + */ + magic = magic1; + for ( pFmt = ImGetFileFormats(); *pFmt; pFmt++ ) + { + pMagic = (*pFmt)->format_magicMap; + if ( pMagic == NULL ) + { + /* This format has no magic numbers. */ + continue; + } + for ( ; pMagic->format_magicNumber != NULL; pMagic++ ) + { + if ( pMagic->format_magicLength == 0 ) + continue; /* No magic # */ + if ( pMagic->format_magicLocation != 0 ) + { + if ( ioType & IMFILEIOFD ) + { + IMREADMAGIC( fd, magic2, + pMagic->format_magicLength, + pMagic->format_magicLocation ); + } + else + { + IMREADFMAGIC( fp, magic2, + pMagic->format_magicLength, + pMagic->format_magicLocation ); + } + magic = magic2; + } + length = pMagic->format_magicLength; + fmtMagic = pMagic->format_magicNumber; + + /* See if it matches the magic number */ + for ( i = 0; i < length; i++ ) + if ( fmtMagic[i] != magic[i] ) + break; + if ( i == length ) + return ( (*pFmt)->format_names[0] ); + magic = magic1; + } + } + /* No match. Fall through to the file name check. */ + } + + + /* + * Get the file name extension, then check it against the format list. + */ + extension = &fileName[strlen(fileName)-1]; + while ( extension != fileName && *extension != '.' ) + extension--; + if ( *extension != '.' ) + { + ImErrNo = IMEFORMATUNKNOWN; + return ( NULL ); + } + strcpy (tmpExtension, extension+1); + + pFmt = ImFileFindFormat( tmpExtension ); + if ( pFmt != NULL ) + return ( (*pFmt)->format_names[0] ); + + /* + * There may be a compression suffix on the file name. + * Search back to one more '.'. + */ + extension--; + while ( extension != fileName && *extension != '.' ) + extension--; + if ( *extension != '.' ) + { + ImErrNo = IMEFORMATUNKNOWN; + return ( NULL ); + } + strcpy(tmpExtension, extension+1); + + /* Take the compression suffix off */ + tmp = strchr(tmpExtension,'.'); + *tmp = '\0'; + + pFmt = ImFileFindFormat( tmpExtension ); + + if (pFmt==NULL) + { + ImErrNo = IMEFORMATUNKNOWN; + return ( NULL ); + } + + return ( (*pFmt)->format_names[0] ); +} + + + + + +/* + * FUNCTION + * ImFileQNFormat - query # of formats + * + * DESCRIPTION + * On the first call, the number of formats in the format table is + * counted and saved away. On all subsequent calls, this saved + * count is returned. + */ + +int /* Retursn # of formats */ +#ifdef __STDC__ +ImFileQNFormat( void ) +#else +ImFileQNFormat( ) +#endif +{ + ImFileFormat **pFmt; /* File format list pointer */ + static int count = -1; /* Saved format count */ + + if ( count != -1 ) + return ( count ); + for ( count = 0, pFmt = ImGetFileFormats(); *pFmt; count++, pFmt++ ) + ; + return ( count ); +} + + + +/* + * FUNCTION + * imFileErrorTags - process error tags + * imFileImageTags - process image tags + * imFileInfoTags - process info tags + * + * DESCRIPTION + * imFileErrorTags looks for the generic error handling tags in the + * flagsTable and sets globals accordingly. + * + * imFileImageTags looks for the image write handling tags in the + * flagsTable and sets arguments accordingly. + * + * imFileInfoTags looks for the generic info handling tags in the + * flagsTable and sets globals accordingly. + */ + +static void /* Returns nothing */ +#ifdef __STDC__ +imFileErrorTags( TagTable *flagsTable ) +#else +imFileErrorTags( flagsTable ) + TagTable *flagsTable; /* Flags */ +#endif +{ + TagEntry *tmpEntry; /* Tmp table entry holder */ + + ImErrorProgramName = "program"; + ImErrorFileName = "stream"; + ImErrorHandler = NULL; + ImErrorStream = NULL; + + if ( flagsTable == TAGTABLENULL ) + return; + + tmpEntry = TagTableQDirect( flagsTable, "program name", 0 ); + if ( tmpEntry != TAGENTRYNULL ) + TagEntryQValue( tmpEntry, &ImErrorProgramName ); + tmpEntry = TagTableQDirect( flagsTable, "file name", 0 ); + if ( tmpEntry != TAGENTRYNULL ) + TagEntryQValue( tmpEntry, &ImErrorFileName ); + tmpEntry = TagTableQDirect( flagsTable, "error handler", 0 ); + if ( tmpEntry != TAGENTRYNULL ) + TagEntryQValue( tmpEntry, &ImErrorHandler ); + tmpEntry = TagTableQDirect( flagsTable, "error stream", 0 ); + if ( tmpEntry != TAGENTRYNULL ) + TagEntryQValue( tmpEntry, &ImErrorStream ); +} + +static void /* Returns nothing */ +#ifdef __STDC__ +imFileImageTags( TagTable *flagsTable, int *monoThreshold, int *typeRequest, + int *channelRequest, int *depthRequest, int *interRequest, + int *groupRequest, int * qualityRequest, int *compRequest, + int *cltRequest, int *alphaRequest ) +#else +imFileImageTags( flagsTable, monoThreshold, typeRequest, channelRequest, + depthRequest, interRequest, groupRequest, qualityRequest, compRequest, + cltRequest, alphaRequest ) + TagTable *flagsTable; /* User flags */ + int *monoThreshold; /* Mono conversion threshold */ + int *typeRequest; /* Image type request */ + int *channelRequest; /* # of channels requested */ + int *depthRequest; /* Depth request */ + int *interRequest; /* Interleave request */ + int *groupRequest; /* Group request */ + int *qualityRequest; /* Quality request */ + int *compRequest; /* Compression request */ + int *cltRequest; /* CLT request */ + int *alphaRequest; /* Alpha request */ +#endif +{ + TagEntry *tmpEntry; /* Table entry holder */ + + *monoThreshold = IMDEFMONOTHRESH; + *typeRequest = -1; + *channelRequest = -1; + *depthRequest = -1; + *interRequest = -1; + *compRequest = -1; + *cltRequest = -1; + *alphaRequest = -1; + *groupRequest = -1; + *qualityRequest = -1; + + if ( flagsTable == TAGTABLENULL ) + return; + + tmpEntry = TagTableQDirect( flagsTable, "image mono threshold", 0 ); + if ( tmpEntry != TAGENTRYNULL ) + TagEntryQValue( tmpEntry, monoThreshold ); + + tmpEntry = TagTableQDirect( flagsTable, "image interleave request", 0 ); + if ( tmpEntry != TAGENTRYNULL ) + TagEntryQValue( tmpEntry, interRequest ); + + tmpEntry = TagTableQDirect( flagsTable, "image type request", 0 ); + if ( tmpEntry != TAGENTRYNULL ) + TagEntryQValue( tmpEntry, typeRequest ); + + tmpEntry = TagTableQDirect( flagsTable, "image channel number request", 0 ); + if ( tmpEntry != TAGENTRYNULL ) + TagEntryQValue( tmpEntry, channelRequest ); + + tmpEntry = TagTableQDirect( flagsTable, "image channel depth request", 0 ); + if ( tmpEntry != TAGENTRYNULL ) + TagEntryQValue( tmpEntry, depthRequest ); + + tmpEntry = TagTableQDirect( flagsTable, "image compression request", 0); + if ( tmpEntry != TAGENTRYNULL ) + TagEntryQValue( tmpEntry, compRequest ); + + tmpEntry = TagTableQDirect( flagsTable, "image clt request", 0 ); + if ( tmpEntry != TAGENTRYNULL ) + TagEntryQValue( tmpEntry, cltRequest ); + + tmpEntry = TagTableQDirect( flagsTable, "image alpha request", 0 ); + if ( tmpEntry != TAGENTRYNULL ) + TagEntryQValue( tmpEntry, alphaRequest ); + + tmpEntry = TagTableQDirect( flagsTable, "image group request", 0 ); + if ( tmpEntry != TAGENTRYNULL ) + TagEntryQValue( tmpEntry, groupRequest ); + + tmpEntry = TagTableQDirect( flagsTable, "image compression quality request", 0 ); + if ( tmpEntry != TAGENTRYNULL ) + { + /* We don't care about the value; we just care that the + request is there. */ + *qualityRequest = IMQUALITYYES; + } +} + +static void /* Returns nothing */ +#ifdef __STDC__ +imFileInfoTags( TagTable *flagsTable ) +#else +imFileInfoTags( flagsTable ) + TagTable *flagsTable; /* Flags */ +#endif +{ + TagEntry *tmpEntry; /* Tmp table entry holder */ + + ImInfoHandler = NULL; + ImInfoStream = NULL; + + if ( flagsTable == TAGTABLENULL ) + return; + + tmpEntry = TagTableQDirect( flagsTable, "info handler", 0 ); + if ( tmpEntry != TAGENTRYNULL ) + TagEntryQValue( tmpEntry, &ImInfoHandler ); + tmpEntry = TagTableQDirect( flagsTable, "info stream", 0 ); + if ( tmpEntry != TAGENTRYNULL ) + TagEntryQValue( tmpEntry, &ImInfoStream ); +} + + + + + +/* + * FUNCTION + * ImFileRead - read an image file from a file descriptor + * ImFileFRead - read an image file from a file pointer + * imFileRead - do the real work of read + * + * DESCRIPTION + * ImFileRead and ImFileFRead just call imFileRead to do their work. + * + * Incomming arguments are checked and the flagsTable scanned for + * relevant flags. The format table is searched for the format's + * read function, and that function called. + */ + +int /* Returns number of entries added*/ +#ifdef __STDC__ +ImFileRead( int fd, char *format, TagTable *flagsTable, TagTable *tagTable ) +#else +ImFileRead( fd, format, flagsTable, tagTable ) + int fd; /* File descriptor to read from */ + char *format; /* File format to use */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag table to append to */ +#endif +{ + return ( imFileRead( /*IOTYPE(fd)*/ 0, fd, NULL, format, + flagsTable, tagTable ) ); +} + +int /* Returns number of entries added*/ +#ifdef __STDC__ +ImFileFRead( FILE *fp, char *format, TagTable *flagsTable, TagTable *tagTable ) +#else +ImFileFRead( fp, format, flagsTable, tagTable ) + FILE *fp; /* File pointer to read from */ + char *format; /* File format to use */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag table to append to */ +#endif +{ + return ( imFileRead( FIOTYPE(fp), -1, fp, format, + flagsTable, tagTable ) ); +} + +static int /* Returns status */ +#ifdef __STDC__ +imFileRead( int ioType, int fd, FILE *fp, char *format, TagTable *flagsTable, + TagTable *tagTable ) +#else +imFileRead( ioType, fd, fp, format, flagsTable, tagTable ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + char *format; /* File format to use */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag table to append to */ +#endif +{ + ImFileFormat **pFormat; /* Pointer into format table */ + FILE *tmpFp; /* Temporary fp */ + int tmpFd; /* Temporary fd */ + char tmpName[1024]; /* Temporary file name */ + unsigned char buffer[1024]; /* Temporary write buffer */ + char message[1024]; /* Holds iminfo message */ + int status; /* Return status */ + int n; /* # of bytes read */ + int binByteOrder; /* Current byte order */ + int binFloatFormat; /* Current floating point format*/ + TagEntry *tmpEntry; /* Tmp table entry holder */ + char *fileName; /* name of file */ + char *tmp; /* temp string */ + ImCompressScheme* pCompress; /* compression scheme */ + + + /* + * Get and save the current state of the Binary I/O package. + */ + binByteOrder = BinQByteOrder( ); + binFloatFormat = BinQFloatFormat( ); + + + /* + * Get stuff from the flags table. + */ + imFileErrorTags( flagsTable ); + imFileInfoTags( flagsTable ); + + /* Check tagTable */ + if ( tagTable == TAGTABLENULL ) + { + ImErrNo = IMENULLTAGTABLE; + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + + /* + * Check for compression. + */ + if ((tmp = imFileQCompression(ioType, fd, fp, flagsTable))!=NULL) + { + pCompress = ImFileFindCompressionScheme(tmp); + sprintf(message,"'%s' (%s)",pCompress->compress_suffixes[0],pCompress->compress_name); + ImInfo("File Compression",message); + if (imFileReadCompressedFile(ioType, fd, fp, flagsTable, tagTable, tmp)==-1) + return -1; + } + else + { + /* + * Read an uncompressed file + */ + + /* + * If no format was specifed, get the format from the + * file name, or flags in the flags table. + */ + if (format==NULL || *format=='\0') + { + tmpEntry = TagTableQDirect( flagsTable, "file name", 0 ); + if ( tmpEntry != TAGENTRYNULL ) + { + TagEntryQValue( tmpEntry, &fileName); + } + else + fileName = NULL; + if (ioType & IMFILEIOFD) + format = ImFileQFormat(fd, fileName); + else + format = ImFileQFFormat(fp, fileName); + } + + /* + * Look up the format + */ + if ( (pFormat = ImFileFindFormat( format )) == NULL ) + { + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + + if ( (*pFormat)->format_read == NULL ) + { + ImErrNo = IMENOREAD; + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + + sprintf(message,"'%s' (%s)",(*pFormat)->format_names[0],(*pFormat)->format_help); + ImInfo("Image Format",message); + + /* + * If the fd/fp is a file, or it's a pipe and the handler can read + * directly to a pipe, read it. + */ + if ( ioType & IMFILEIOFILE || (*pFormat)->format_readPipe == IMPIPE ) + { + status = (*(*pFormat)->format_read)( ioType, fd, fp, + flagsTable, tagTable ); + } + else + { + /* + * Create a temporary file, copy input to the temp file, then do + * all reading from it. When complete, delete the file, then + * return. If errors occur, save them until the unlink has been done. + */ + strcpy( tmpName, imMakeTempFile() ); + if ( ioType & IMFILEIOFD ) + { + if ( (tmpFd = open( tmpName, O_RDWR|O_CREAT, 0666 )) == -1 ) + { + ImErrNo = IMESYS; + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + while ( (n = read( fd, buffer, sizeof( buffer ) )) > 0 ) + write( tmpFd, buffer, n ); + lseek( tmpFd, 0, 0 ); + status = (*(*pFormat)->format_read)( IMFILEIOFD | IMFILEIOFILE, + tmpFd, NULL, flagsTable, tagTable ); + close( tmpFd ); + unlink( tmpName ); + } + else + { + if ( (tmpFp = fopen( tmpName, "w+b" )) == NULL ) + { + ImErrNo = IMESYS; + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + while ( (n = fread( buffer, 1, sizeof( buffer ), fp )) != 0 ) + fwrite( buffer, 1, n, tmpFp ); + fseek( tmpFp, 0, 0 ); + status = (*(*pFormat)->format_read)( IMFILEIOFP | IMFILEIOFILE, -1, + tmpFp, flagsTable, tagTable ); + fclose( tmpFp ); + unlink( tmpName ); + } + } + } + /* + * Restore the Binary I/O library state. + */ + BinByteOrder( binByteOrder ); + BinFloatFormat( binFloatFormat ); + + /* + * Process input requests + */ + if ( flagsTable && TagTableQDirect(flagsTable, "channel map request", 0) != TAGENTRYNULL ) + { + if (ImVfbProcessMapRequests( flagsTable, tagTable )<=0) + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + + return ( status ); +} + + + + + +/* + * FUNCTION + * imFileSelectMap - select a good write map to use + * + * DESCRIPTION + * The format's write map is scanned to find the best match for output + * of the given VFB based upon the user's requests. + */ + +static ImFileFormatWriteMap * /* Returns write map entry */ +#ifdef __STDC__ +imFileSelectMap( ImVfb *pVfb, ImFileFormat **pFormat, int typeRequest, + int channelRequest, int depthRequest, int interRequest, + int groupRequest, int qualityRequest, int otherRequest, int otherRequestMask ) +#else +imFileSelectMap( pVfb, pFormat, typeRequest, channelRequest, depthRequest, + interRequest, groupRequest, qualityRequest, otherRequest, otherRequestMask ) + ImVfb *pVfb; /* Image to handle */ + ImFileFormat **pFormat; /* Format conversion info */ + int typeRequest; /* Type request */ + int channelRequest; /* Channel request */ + int depthRequest; /* Depth request */ + int interRequest; /* Interleave request */ + int groupRequest; /* Group request */ + int qualityRequest; /* Quality request */ + int otherRequest; /* CLT/Alpha/Comp requests */ + int otherRequestMask; /* Which of those are in otherRequest*/ +#endif +{ + ImFileFormatWriteMap *pMap; /* Write map entry */ + int fields; /* VFB field mask */ + imFilePrefTable *pPrefTable; /* Preference table */ + imFilePref *pPrefSubTable; /* Preference subtable */ + imFilePref *pPref; /* Preference table entry ptr */ + int bestIndex; /* Best preference index */ + ImFileFormatWriteMap *pBest; /* Best write map entry */ + int i; /* Counters */ + + + /* + * To gauge which of possibly many output image types (and their + * CLT, alpha, interleave, grouping, and compression attributes) is + * "best" for the incomming VFB we use a series of "preference tables". + * There is one master table for each VFB type (mono, index8, + * index16, and rgb). Within each table are four sub-tables, for + * each combination of (have/don't have CLT) and (have/don't have + * alpha planes). + * + * Within each sub-table are 16 entries for every combination of + * (mono, index8, index16, rgb), (have/don't have CLT), and + * (have/don't have alpha). These are ordered from most preferable + * to least preferable for this VFB. + * + * In evaluating a given format's selection of write map entries, + * we look up each one in the preference table and get the table + * index of it's entry. The entry with the lowest index, of those + * available to the format, is the one we select. + * + * Among several write map entries with the same preference value + * (same incomming VFB and attributes but different channel numbers + * and depths), the earliest one in the write map is chosen. + */ + fields = ImVfbQFields( pVfb ); + if ( fields & IMVFBMONO ) + pPrefTable = &imPrefMono; + else if ( fields & IMVFBINDEX8 ) + pPrefTable = &imPrefIndex8; + else if ( fields & IMVFBINDEX16 ) + pPrefTable = &imPrefIndex16; + else if ( fields & IMVFBRGB ) + pPrefTable = &imPrefRgb; + else + { + /* What kind of image is stored here? */ + return ( NULL ); + } + if ( fields & IMVFBALPHA ) + { + if ( ImVfbQClt( pVfb ) != IMCLTNULL ) + pPrefSubTable = pPrefTable->pref_cltAlpha; + else + pPrefSubTable = pPrefTable->pref_noCltAlpha; + } + else + { + if ( ImVfbQClt( pVfb ) != IMCLTNULL ) + pPrefSubTable = pPrefTable->pref_cltNoAlpha; + else + pPrefSubTable = pPrefTable->pref_noCltNoAlpha; + } + + bestIndex = 1000; + pBest = NULL; + for ( pMap = (*pFormat)->format_writeMap; pMap->map_inField != -1; pMap++ ) + { + /* Reject if not the type, # channel, channel depth requested.*/ + if ( typeRequest != -1 && pMap->map_outType != typeRequest ) + { + continue; + } + if ( channelRequest != -1 && pMap->map_outNChannels != channelRequest ) + { + continue; + } + if ( depthRequest != -1 && pMap->map_outChannelDepth != depthRequest ) + { + continue; + } + + /* Reject if not the CLT/Alpha/Comp requested. */ + if ( (pMap->map_outAttributes & otherRequestMask) != + otherRequest ) + { + continue; + } + + /* Reject if not the interleave requested. */ + if ( (interRequest != -1) && + ((pMap->map_outAttributes & IMINTERMASK) != interRequest) ) + { + continue; + } + + /* Reject if not the group request */ + if ( (groupRequest != -1) && + ((pMap->map_outAttributes & IMGROUPMASK) != groupRequest)) + { + continue; + } + + /* Reject if not the quality request */ + if ( (qualityRequest != -1) && + ((pMap->map_outAttributes & IMQUALITYMASK) != qualityRequest)) + { + continue; + } + + /* Find it's preference index. */ + for ( pPref = pPrefSubTable, i = 0; i < 16; i++, pPref++ ) + { + if ( pMap->map_inField != pPref->pref_field || + pMap->map_inAttributes !=pPref->pref_attributes) + continue; + + /* Match. Better than last match? */ + if ( i < bestIndex ) + { + bestIndex = i; + pBest = pMap; + } + break; + } + } + + if ( pBest == NULL ) + { + /* Nothing got past user requirements! */ + return ( NULL ); + } + return ( pBest ); +} + + + + + +/* + * FUNCTION + * ImFileWrite - write an image file to a file descriptor + * ImFileFWrite - write an image file to a file pointer + * imFileWrite - do the real work of write + * + * DESCRIPTION + * ImFileWRite and ImFileFWRite just call imFileWRite to do their work. + * + * Incoming arguments are checked and the flagsTable scanned for + * relevant flags. The format table is searched for the format's + * write map. The write map is scanned to find the best match, or + * the match requested by incomming flags. If the match requires + * depth conversion of the VFB(s), it's done. The match's write + * routine is then called. + * + * If NULL is passed as the image file format, then the format is + * discerned from this routine. + * + * If the format parameter points to NULL, then the format is discerned + * here, AND the format which is ultimately chosen is returned in the format + * parameter. + */ + +int /* Returns number of entries used*/ +#ifdef __STDC__ +ImFileWrite( int fd, char *format, TagTable *flagsTable, TagTable *tagTable ) +#else +ImFileWrite( fd, format, flagsTable, tagTable ) + int fd; /* Output file descriptor */ + char *format; /* File format to use */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag table to read from */ +#endif +{ + return ( imFileWrite( IOTYPE(fd), fd, NULL, format, + flagsTable, tagTable ) ); +} + +int /* Returns number of entries used*/ +#ifdef __STDC__ +ImFileFWrite( FILE *fp, char *format, TagTable *flagsTable, TagTable *tagTable ) +#else +ImFileFWrite( fp, format, flagsTable, tagTable ) + FILE *fp; /* Output file pointer */ + char *format; /* File format to use */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag table to read from */ +#endif +{ + return ( imFileWrite( FIOTYPE(fp), -1, fp, format, + flagsTable, tagTable ) ); +} + + +static int /* Returns number of entries used*/ +#ifdef __STDC__ +imFileWrite( int ioType, int fd, FILE *fp, char *format, TagTable *flagsTable, + TagTable *tagTable ) +#else +imFileWrite( ioType, fd, fp, format, flagsTable, tagTable ) + int ioType; /* I/O flags */ + int fd; /* Output file descriptor */ + FILE *fp; /* Output file pointer */ + char *format; /* File format to use */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag table to read from */ +#endif +{ + int nVfb; /* # of vfb's in tag table */ + ImFileFormat **pFormat; /* Pointer into format table */ + int status; /* Return status */ + int binByteOrder; /* Current byte order */ + int binFloatFormat; /* Current floating point format*/ + char* fileName; /* name of file (in flagsTable) */ + TagEntry* tmpEntry; /* entry in tag table */ + + + /* + * Get and save the current state of the Binary I/O package. + */ + binByteOrder = BinQByteOrder( ); + binFloatFormat = BinQFloatFormat( ); + + /* Check integrity of tagTable */ + if ( tagTable == TAGTABLENULL ) + { + ImErrNo = IMENULLTAGTABLE; + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + nVfb = TagTableQNEntry( tagTable, "image vfb" ); + if ( nVfb < 1 ) + { + ImErrNo = IMENOVFB; + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + + /* + * Get stuff from the flags table. + */ + imFileErrorTags( flagsTable ); + imFileInfoTags( flagsTable ); + + /* + * If no format was specifed, get the format from the + * file name, or flags in the flags table. + */ + if (format==NULL || *format=='\0') + { + tmpEntry = TagTableQDirect( flagsTable, "file name", 0 ); + if ( tmpEntry != TAGENTRYNULL ) + { + TagEntryQValue( tmpEntry, &fileName); + } + else + fileName = NULL; + /* + * Return the format name in 'format' if + * NULL was not passed + */ + if (format==NULL) + { + if (ioType & IMFILEIOFD) + format = ImFileQFormat(fd, fileName); + else + format = ImFileQFFormat(fp, fileName); + } + else + { + if (ioType & IMFILEIOFD) + strcpy(format,ImFileQFormat(fd, fileName)); + else + strcpy(format, ImFileQFFormat(fp, fileName)); + } + } + + + /* + * Handle any channel mapping requests + */ + if ( flagsTable && TagTableQDirect(flagsTable, "channel map request", 0) + != TAGENTRYNULL ) + { + if (ImVfbProcessMapRequests( flagsTable, tagTable )<=0) + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + + /* + * Find the format + */ + if ( (pFormat = ImFileFindFormat( format )) == NULL ) + { + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + if ( (*pFormat)->format_writeMap == NULL ) + { + ImErrNo = IMENOWRITE; + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + + /* + * Handle the single- and multiple-VFB cases separately. + */ + if ( nVfb == 1 ) + status = imFileWriteSingle( ioType, fd, fp, + pFormat, flagsTable, tagTable ); + else + status = imFileWriteMultiple( ioType, fd, fp, + pFormat, flagsTable, tagTable ); + + /* + * Restore the Binary I/O library state. + */ + BinByteOrder( binByteOrder ); + BinFloatFormat( binFloatFormat ); + + return ( status ); +} + + +static int /* Returns number of entries used*/ +#ifdef __STDC__ +imFileWriteSingle( int ioType, int fd, FILE *fp, ImFileFormat **pFormat, + TagTable *flagsTable, TagTable *tagTable ) +#else +imFileWriteSingle( ioType, fd, fp, pFormat, flagsTable, tagTable ) + int ioType; /* I/O flags */ + int fd; /* Output file descriptor */ + FILE *fp; /* Output file pointer */ + ImFileFormat **pFormat; /* File format to use */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag table to read from */ +#endif +{ + TagEntry *tmpEntry; /* Tmp table entry holder */ + TagEntry *newEntry; /* New table entry holder */ + TagTable *newTable; /* New Tag Table */ + + ImFileFormatWriteMap *pMap; /* Write map entry */ + + ImVfb *origVfb; /* Original VFB */ + int origFields; /* Original VFB field mask */ + ImVfb *newVfb; /* New VFB */ + ImVfb *statusVfb; /* Return status as a VFB */ + int newFields; /* New VFB field mask */ + ImClt *newClt; /* New CLT */ + + int status; /* Return status */ + int nEntry; /* Tag entry # */ + + int monoThreshold; /* Mono conversion threshold */ + int typeRequest; /* Type request */ + int channelRequest; /* Channel request */ + int depthRequest; /* Depth request */ + int interRequest; /* Interleave request */ + int groupRequest; /* Group request */ + int qualityRequest; /* Quality request */ + int compRequest; /* Compression request */ + int cltRequest; /* CLT request */ + int alphaRequest; /* Alpha request */ + int otherRequest; /* CLT/Alpha/Comp requests */ + int otherRequestMask; /* Which of those are in otherRequest*/ + + + /* + * Get stuff from the flags table. + */ + imFileImageTags( flagsTable, &monoThreshold, &typeRequest, + &channelRequest, &depthRequest, &interRequest, + &groupRequest, &qualityRequest, &compRequest, &cltRequest, + &alphaRequest ); + otherRequest = 0; + otherRequestMask = 0; + if ( compRequest != -1 ) + { + otherRequest |= (compRequest & IMCOMPMASK); + otherRequestMask |= IMCOMPMASK; + } + if ( cltRequest != -1 ) + { + otherRequest |= (cltRequest & IMCLTMASK); + otherRequestMask |= IMCLTMASK; + } + if ( alphaRequest != -1 ) + { + otherRequest |= (alphaRequest & IMALPHAMASK); + otherRequestMask |= IMALPHAMASK; + } + if ( groupRequest != -1 ) + { + otherRequest |= (groupRequest & IMGROUPMASK); + otherRequestMask |= IMGROUPMASK; + } + if ( qualityRequest != -1 ) + { + otherRequest |= (qualityRequest & IMQUALITYMASK); + otherRequestMask |= IMQUALITYMASK; + } + + /* + * Get the single VFB in the tag table and determine its type. + */ + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &origVfb ); + origFields = ImVfbQFields( origVfb ); + + + /* + * Get the write map entry that matches this VFB best. + */ + pMap = imFileSelectMap( origVfb, pFormat, typeRequest, channelRequest, + depthRequest, interRequest, groupRequest, qualityRequest, otherRequest, + otherRequestMask ); + if ( pMap == NULL ) + { + /* Can't do it. */ + ImErrNo = IMENOTPOSSIBLE; + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + + + /* + * Avoid making a new VFB and new tag table if the current + * one matches in depth and doesn't require an alpha plane. + */ + if ( (origFields & pMap->map_inField) && + !( (pMap->map_inAttributes & IMALPHAYES) && + !(origFields & IMVFBALPHA) ) ) + { + /* + * Generate a CLT if we need to. + */ + if ( (ImVfbQClt( origVfb ) == IMCLTNULL) && + (pMap->map_inAttributes & IMCLTYES) ) + { + /* + * Make a CLT, attach it, write it all out, + * then remove the CLT and throw it away. + * + * Note that we always generate a grayscale CLT: + * if Mono, no CLT means grayscale. + * if Index8, no CLT must default to grayscale. + * if Index16, no CLT must default to grayscale. + * if RGB, any CLT is meaningless. So grayscale. + */ + if ( origFields & IMVFBMONO ) + newClt = ImCltGrayRamp( IMCLTNULL, 0, 1, 0, + 255, IMCLTNEW); + else if ( origFields & IMVFBINDEX16 ) + newClt = ImCltGrayRamp( IMCLTNULL, 0, 65535, + 0, 65535, IMCLTNEW); + else + newClt = ImCltGrayRamp( IMCLTNULL, 0, 255, 0, + 255, IMCLTNEW); + if ( newClt == IMCLTNULL ) + { + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + ImVfbSClt( origVfb, newClt ); + status = imFileWriteIt( pMap, + ioType, fd, fp, pFormat, + flagsTable, tagTable ); + ImVfbSClt( origVfb, IMCLTNULL ); + ImCltFree( newClt ); + return ( status ); + } + + /* + * The VFB matches just fine. Write it out. + */ + return ( imFileWriteIt( pMap, ioType, fd, fp, pFormat, + flagsTable, tagTable ) ); + } + + + /* + * Allocate a new VFB. + */ + newFields = pMap->map_inField; + if ( pMap->map_inAttributes & IMALPHAYES ) + newFields |= IMVFBALPHA; + + newVfb = ImVfbAlloc( ImVfbQWidth( origVfb ), ImVfbQHeight( origVfb ), + newFields ); + if ( newVfb == IMVFBNULL ) + { + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + + if ( newFields & IMVFBALPHA ) + { + if ( origFields & IMVFBALPHA ) + { + /* + * Copy alpha from original VFB. + */ + if ( ImVfbCopy( origVfb, 0, 0, ImVfbQWidth( origVfb ), + ImVfbQHeight( origVfb ), IMVFBALPHA, + newVfb, 0, 0 ) == IMVFBNULL ) + { + ImVfbFree( newVfb ); + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + } + else + { + /* + * Generate an empty alpha plane. + */ + if ( ImVfbClear( IMVFBALPHA, ~0, newVfb ) == IMVFBNULL ) + { + ImVfbFree( newVfb ); + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + } + } + + + /* + * Copy the original image to the new VFB, changing + * depth if necessary. + */ + if ( newFields & IMVFBMONO ) + statusVfb = ImVfbToMono( origVfb, monoThreshold, newVfb ); + else if ( newFields & IMVFBINDEX8 ) + statusVfb = ImVfbToIndex8( origVfb, newVfb ); + else if ( newFields & IMVFBINDEX16 ) + statusVfb = ImVfbToIndex16( origVfb, newVfb ); + else if ( newFields & IMVFBRGB ) + statusVfb = ImVfbToRgb( origVfb, newVfb ); + else + { + /* Unknown VFB selection? How is that possible? */ + ImVfbFree( newVfb ); + ImErrNo = IMENOTPOSSIBLE; + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + if ( statusVfb == IMVFBNULL ) + { + ImVfbFree( newVfb ); + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + + + /* + * Add a CLT if necessary. + */ + newClt = ImVfbQClt( newVfb ); + if ( (newClt == IMCLTNULL) && (pMap->map_inAttributes & IMCLTYES) ) + { + /* + * Note that we always generate a grayscale CLT: + * if Mono, no CLT means grayscale. + * if Index8, no CLT must default to grayscale. + * if Index16, no CLT must default to grayscale. + * if RGB, any CLT is meaningless. So grayscale. + * Mono gets a 2 entry CLT. All others 255 entries. + */ + if ( newFields & IMVFBMONO ) + newClt = ImCltGrayRamp( IMCLTNULL, 0, 1, 0, 255, + IMCLTNEW ); + else if ( newFields & IMVFBINDEX16 ) + newClt = ImCltGrayRamp( IMCLTNULL, 0, 65535, 0, 65535, + IMCLTNEW ); + else + newClt = ImCltGrayRamp( IMCLTNULL, 0, 255,0,255, + IMCLTNEW ); + if ( newClt == IMCLTNULL ) + { + ImVfbFree( newVfb ); + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + ImVfbSClt( newVfb, newClt ); + } + + + /* + * Copy the tag table, replacing the VFB and CLT, if any. + * + * Note: we can't just temporarly swap the new VFB into the + * original tag table. Doing so causes the VFB's tag entry to be + * freed and a new one allocated. If the caller had a pointer to + * that entry, it would become garbage. So, we are not allowed + * to change, in any way, the incomming tag table. + */ + newTable = TagTableAlloc( ); + if ( newTable == TAGTABLENULL ) + { + ImCltFree( newClt ); + ImVfbFree( newVfb ); + ImErrNo = IMEMALLOC; + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + TagTableCopy( tagTable, newTable, 0, TagTableQNEntry( tagTable, NULL )-1 ); + + /* Replace the VFB. */ + tmpEntry = TagTableQDirect( newTable, "image vfb", 0 ); + nEntry = TagEntryQNthEntry( tmpEntry ); + TagTableReplace( newTable, TagEntryQNthEntry( tmpEntry ), + TagEntryAlloc( "image vfb", POINTER, &newVfb ) ); + + /* Replace the CLT (assume there is at most one!). */ + tmpEntry = TagTableQDirect( newTable, "image clt", 0 ); + if ( newClt == IMCLTNULL ) + { + /* New format doesn't want CLT. Remove it, if any. */ + if ( tmpEntry != TAGENTRYNULL ) + TagTableDelete( newTable, TagEntryQNthEntry( tmpEntry)); + } + else + { + /* New format requires CLT. Replace or insert. */ + newEntry = TagEntryAlloc( "image clt", POINTER, &newClt); + if ( tmpEntry != TAGENTRYNULL ) + TagTableReplace( newTable, TagEntryQNthEntry( tmpEntry), + newEntry ); + else + TagTableInsert( newTable, nEntry - 1, newEntry ); + } + + + /* + * Write it out, then clean up. + */ + status = imFileWriteIt( pMap, ioType, fd, fp, pFormat, + flagsTable, newTable ); + + if ( newClt != IMCLTNULL ) + ImCltFree( newClt ); + ImVfbFree( newVfb ); + TagTableFree( newTable ); + + return ( status ); +} + +static int /* Returns number of entries used*/ +#ifdef __STDC__ +imFileWriteMultiple( int ioType, int fd, FILE *fp, ImFileFormat **pFormat, + TagTable *flagsTable, TagTable *tagTable ) +#else +imFileWriteMultiple( ioType, fd, fp, pFormat, flagsTable, tagTable ) + int ioType; /* I/O flags */ + int fd; /* Output file descriptor */ + FILE *fp; /* Output file pointer */ + ImFileFormat **pFormat; /* File format to use */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag table to read from */ +#endif +{ + TagEntry *tmpEntry; /* Tmp table entry holder */ + TagTable *newTable; /* New Tag Table */ + + ImFileFormatWriteMap *pMap; /* Write map entry */ + + ImVfb *origVfb; /* Original VFB */ + int origFields; /* Original VFB field mask */ + ImVfb *newVfb; /* New VFB */ + int newFields; /* New VFB field mask */ + ImClt *newClt; /* New CLT */ + ImVfb *statusVfb; /* Return status as a VFB */ + + int status; /* Return status */ + int nEntry; /* Tag entry # */ + int i; /* Entry # */ + char *tag; /* Tag name */ + int error; /* Error occured? */ + + int monoThreshold; /* Mono conversion threshold */ + int typeRequest; /* Type request */ + int channelRequest; /* Channel request */ + int depthRequest; /* Depth request */ + int interRequest; /* Interleave request */ + int groupRequest; /* Group request */ + int qualityRequest; /* Quality request */ + int compRequest; /* Compression request */ + int cltRequest; /* CLT request */ + int alphaRequest; /* Alpha request */ + int otherRequest; /* CLT/Alpha/Comp requests */ + int otherRequestMask; /* Which of those are in otherRequest*/ + + + /* + * Get stuff from the flags table. + */ + imFileImageTags( flagsTable, &monoThreshold, &typeRequest, + &channelRequest, &depthRequest, &interRequest, + &groupRequest, & qualityRequest, &compRequest, + &cltRequest, &alphaRequest ); + otherRequest = 0; + otherRequestMask = 0; + if ( compRequest != -1 ) + { + otherRequest |= (compRequest & IMCOMPMASK); + otherRequestMask |= IMCOMPMASK; + } + if ( cltRequest != -1 ) + { + otherRequest |= (cltRequest & IMCLTMASK); + otherRequestMask |= IMCLTMASK; + } + if ( alphaRequest != -1 ) + { + otherRequest |= (alphaRequest & IMALPHAMASK); + otherRequestMask |= IMALPHAMASK; + } + if ( groupRequest != -1 ) + { + otherRequest |= (groupRequest & IMGROUPMASK); + otherRequestMask |= IMGROUPMASK; + } + if ( qualityRequest != -1 ) + { + otherRequest |= (qualityRequest & IMQUALITYMASK); + otherRequestMask |= IMQUALITYMASK; + } + + + /* + * Allocate a new tag table. + */ + newTable = TagTableAlloc( ); + + + /* + * Walk through the original tag table, watching for VFB's and CLT's. + * Everything else is copied straight across. + */ + nEntry = TagTableQNEntry( tagTable, NULL ); + error = FALSE; + for ( i = 0; i < nEntry; i++ ) + { + tmpEntry = TagTableQLinear( tagTable, i ); + tag = TagEntryQTag( tmpEntry ); + + if ( strcmp( tag, "image clt" ) == 0 ) + continue; /* Remove these for now */ + + if ( strcmp( tag, "image vfb" ) != 0 ) + { + /* Some other tag. Copy to new table. */ + TagTableCopy( tagTable, newTable, i, i ); + continue; + } + + /* + * At this point we have a VFB from the original tag table. + */ + TagEntryQValue( tmpEntry, &origVfb ); + origFields = ImVfbQFields( origVfb ); + + + /* + * Get the write map entry that matches this VFB best. + */ + pMap = imFileSelectMap( origVfb, pFormat, typeRequest, + channelRequest, depthRequest, interRequest, + groupRequest, qualityRequest, otherRequest, otherRequestMask ); + if ( pMap == NULL ) + { + /* Can't do it. */ + ImErrNo = IMENOTPOSSIBLE; + error = TRUE; + break; + } + + + /* + * Allocate a new VFB. + */ + newFields = pMap->map_inField; + if ( pMap->map_inAttributes & IMALPHAYES ) + newFields |= IMVFBALPHA; + + newVfb = ImVfbAlloc( ImVfbQWidth( origVfb ), + ImVfbQHeight( origVfb ), newFields ); + if ( newVfb == IMVFBNULL ) + { + error = TRUE; + break; + } + + if ( newFields & IMVFBALPHA ) + { + if ( origFields & IMVFBALPHA ) + { + /* + * Copy alpha from original VFB. + */ + if ( ImVfbCopy( origVfb, 0, 0, + ImVfbQWidth( origVfb ), + ImVfbQHeight( origVfb ), IMVFBALPHA, + newVfb, 0, 0 ) == IMVFBNULL ) + { + ImVfbFree( newVfb ); + ImErrorFatal( ImQError( ), -1, ImErrNo); + } + } + else + { + /* + * Generate an empty alpha plane. + */ + if ( ImVfbClear( IMVFBALPHA, ~0, newVfb ) == IMVFBNULL ) + { + ImVfbFree( newVfb ); + ImErrorFatal( ImQError( ), -1, ImErrNo); + } + } + } + + + /* + * Copy the original image to the new VFB, changing + * depth if necessary. + */ + if ( newFields & IMVFBMONO ) + statusVfb = ImVfbToMono( origVfb, monoThreshold,newVfb); + else if ( newFields & IMVFBINDEX8 ) + statusVfb = ImVfbToIndex8( origVfb, newVfb ); + else if ( newFields & IMVFBINDEX16 ) + statusVfb = ImVfbToIndex16( origVfb, newVfb ); + else if ( newFields & IMVFBRGB ) + statusVfb = ImVfbToRgb( origVfb, newVfb ); + else + { + /* Unknown VFB selection? How is that possible?*/ + ImErrNo = IMENOTPOSSIBLE; + error = TRUE; + break; + } + if ( statusVfb == IMVFBNULL ) + { + error = TRUE; + break; + } + + + /* + * Add a CLT if necessary. + */ + newClt = ImVfbQClt( newVfb ); + if ( (newClt == IMCLTNULL) && (pMap->map_inAttributes & IMCLTYES) ) + { + /* + * Note that we always generate a grayscale CLT: + * if Mono, no CLT means grayscale. + * if Index8, no CLT must default to grayscale. + * if Index16, no CLT must default to grayscale. + * if RGB, any CLT is meaningless. So grayscale. + * Mono gets a 2 entry CLT. All others 255 entries. + */ + if ( newFields & IMVFBMONO ) + newClt = ImCltGrayRamp( IMCLTNULL, 0, 1, 0, 255, + IMCLTNEW ); + else if ( newFields & IMVFBINDEX16 ) + newClt = ImCltGrayRamp( IMCLTNULL, 0, 65535, 0, + 65535, IMCLTNEW); + else + newClt = ImCltGrayRamp( IMCLTNULL, 0, 255,0, + 255, IMCLTNEW); + if ( newClt == IMCLTNULL ) + { + error = TRUE; + break; + } + ImVfbSClt( newVfb, newClt ); + } + + + /* + * Add the new CLT & VFB to the tag table and continue. + */ + if ( newClt != IMCLTNULL ) + TagTableAppend( newTable, + TagEntryAlloc( "image clt", POINTER, &newClt )); + TagTableAppend( newTable, + TagEntryAlloc( "image vfb", POINTER, &newVfb ) ); + } + + + /* + * Write out the new tag table. + */ + if ( !error ) + status = imFileWriteIt( pMap, ioType, fd, fp, pFormat, + flagsTable, newTable ); + + + /* + * Scan the new table for all the CLT's and VFB's we created + * and blow them away. + */ + nEntry = TagTableQNEntry( newTable, "image clt" ); + for ( i = 0; i < nEntry; i++ ) + { + tmpEntry = TagTableQDirect( newTable, "image clt", i ); + TagEntryQValue( tmpEntry, &newClt ); + ImCltFree( newClt ); + } + nEntry = TagTableQNEntry( newTable, "image vfb" ); + for ( i = 0; i < nEntry; i++ ) + { + tmpEntry = TagTableQDirect( newTable, "image vfb", i ); + TagEntryQValue( tmpEntry, &newVfb ); + ImVfbFree( newVfb ); + } + TagTableFree( newTable ); + + if ( error ) + { + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + return ( status ); +} + + +static int /* Returns number of entries used*/ +#ifdef __STDC__ +imFileWriteIt( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + ImFileFormat **pFormat, TagTable *flagsTable, TagTable *tagTable ) +#else +imFileWriteIt( pMap, ioType, fd, fp, pFormat, flagsTable, tagTable ) + ImFileFormatWriteMap *pMap; /* Write map entry to use */ + int ioType; /* I/O flags */ + int fd; /* Output file descriptor */ + FILE *fp; /* Output file pointer */ + ImFileFormat **pFormat; /* Format to use */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag table to read from */ +#endif +{ + FILE *tmpFp; /* Temporary fp */ + int tmpFd; /* Temporary fd */ + char tmpName[1024]; /* Temporary file name */ + unsigned char buffer[1024]; /* Temporary write buffer */ + int status; /* Return status */ + int n; /* Number of bytes read */ + char message[1024]; /* Holds a message */ + ImCompressScheme* pCompress; /* compression scheme */ + char *tmp; /* temp string */ + + if ((tmp=imFileQCompression(ioType, fd, fp, flagsTable ))!=NULL) + { + pCompress = ImFileFindCompressionScheme(tmp); + sprintf(message,"'%s' (%s)",pCompress->compress_suffixes[0],pCompress->compress_name); + ImInfo("File Compression",message); + sprintf(message,"'%s' (%s)",(*pFormat)->format_names[0],(*pFormat)->format_help); + ImInfo("Image Format",message); + status = imFileWriteCompressedFile(ioType, fd, fp, flagsTable, tagTable, pMap, tmp); + } + else + { + /* Write an uncompressed file */ + sprintf(message,"'%s' (%s)",(*pFormat)->format_names[0],(*pFormat)->format_help); + ImInfo("Image Format",message); + + /* + * If the fd/fp is a file, or it's a pipe and the handler can write + * directly to a pipe, write it. + */ + if ( ioType & IMFILEIOFILE || (*pFormat)->format_writePipe == IMPIPE ) + return ( (*pMap->map_write)( pMap, ioType, fd, fp, + flagsTable, tagTable ) ); + + /* + * Create a temporary file. Write to it, then copy it out to the + * stream and delete it. If errors occur, save them until the + * unlink has been done. + */ + strcpy( tmpName, imMakeTempFile() ); + if ( ioType & IMFILEIOFD ) + { + if ( (tmpFd = open( tmpName, O_RDWR|O_CREAT, 0666 )) == -1 ) + { + ImErrNo = IMESYS; + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + status = (*pMap->map_write)( pMap, IMFILEIOFD | IMFILEIOFILE, + tmpFd, NULL, flagsTable, tagTable ); + lseek( tmpFd, 0, 0 ); + while ( (n = read( tmpFd, buffer, sizeof( buffer ) )) > 0 ) + write( fd, buffer, n ); + close( tmpFd ); + unlink( tmpName ); + return ( status ); + } + if ( (tmpFp = fopen( tmpName, "w+b" )) == NULL ) + { + ImErrNo = IMESYS; + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + status = (*pMap->map_write)( pMap, IMFILEIOFP | IMFILEIOFILE, -1, tmpFp, + flagsTable, tagTable ); + fseek( tmpFp, 0, 0 ); + while ( (n = fread( buffer, 1, sizeof( buffer ), tmpFp )) != 0 ) + fwrite( buffer, 1, n, fp ); + fclose( tmpFp ); + unlink( tmpName ); + } + return ( status ); +} + + +/* + * FUNCTIONS + * ImFileQCompression + * ImFileQFCompression + * imFileQCompression + * + * DESCRIPTION + * Query the compression scheme of a file using the + * file descriptor, file pointer, or ioType. + * + */ +char * +#ifdef __STDC__ +ImFileQCompression( int fd, TagTable* flagsTable) +#else +ImFileQCompression( fd, flagsTable) +int fd; +TagTable* flagsTable; +#endif +{ + return imFileQCompression(IOTYPE(fd), fd, NULL, flagsTable); +} + +char * +#ifdef __STDC__ +ImFileQFCompression( FILE* fp, TagTable* flagsTable) +#else +ImFileQFCompression( fp, flagsTable) +FILE* fp; +TagTable* flagsTable; +#endif +{ + return imFileQCompression(FIOTYPE(fp), -1, fp, flagsTable); +} + +/* + * FUNCTION + * imFileQCompression + * + * DESCRIPTION + * Determine whether or not a file is compressed. If it is, + * return the name of the compression scheme. + */ +static char * /* returns name of compression scheme or NULL */ +#ifdef __STDC__ +imFileQCompression(int ioType, int fd, FILE* fp, TagTable* flagsTable) +#else +imFileQCompression(ioType, fd, fp, flagsTable) +int ioType; +int fd; +FILE* fp; +TagTable* flagsTable; +#endif +{ + char* fileName; /* file name */ + TagEntry* tmpEntry; /* holds a tag table entry */ + int doMagic; /* should we check magic#? */ + int ioOperation; /* Read or write? */ + char *strTmp; /* holds a string */ + int i; /* index */ + int length; /* length of a magic # */ + ImCompressScheme**ppScheme;/* points into the table of schemes */ + ImCompressScheme*pScheme; /* points into the table of schemes */ + unsigned char magic1[IMMAXMAGIC]; /* Test magic number */ + unsigned char magic2[IMMAXMAGIC]; /* another magic # */ + unsigned char *magic; /* current magic # */ + unsigned char *fmtMagic; /* current format's magic # */ + ImFileMagic *pMagic; /* pointer to magic file info */ + + + /* + * We are compressed if... + * the file compression option is in the flags table + * OR we have one of the magic numbers in the compression table + * OR the filename in the flags table ends in a suffix in the compression table + */ + + if (TagTableQNEntry( flagsTable, "file compression" )>0) + { + tmpEntry = TagTableQDirect( flagsTable, "file compression", 0 ); + TagEntryQValue( tmpEntry, &strTmp); + if (ImFileFindCompressionScheme(strTmp)!=NULL) + return strTmp; + return NULL; + } + + /* + * Should we check the magic number? + * 1. If file is a pipe or a tty, NO. + * 2. If file is open for writing only, NO. + * 3. Otherwise, YES. + */ + doMagic = TRUE; +#if 0 + if ( ioType & IMFILEIOPIPE ) + doMagic = FALSE; /* Input is a pipe or a tty */ + else + { + if ( ioType & IMFILEIOFD ) + ioOperation = fcntl( fd, F_GETFL, &ioOperation ); + else + ioOperation = fcntl( fileno(fp), F_GETFL, &ioOperation); + if ( ioOperation == -1 ) + { + ImErrNo = IMESYS; /* Bad fp/fd? */ + return ( 0 ); + } + if ( ioOperation == O_WRONLY ) + doMagic = FALSE; + } +#endif + /* + * Get the magic number from the file and check it against each of + * the ones in the compression scheme list. + */ + if (doMagic) + { + /* + * Read in the magic number at location 0 (where most are) + */ + if (ioType & IMFILEIOFD) + { + IMREADMAGIC( fd, magic1, IMMAXMAGIC, 0); + } + else + { + IMREADFMAGIC( fp, magic1, IMMAXMAGIC, 0 ); + } + + /* + * Check it against the scheme list. If a scheme + * doesn't have a magic number, skip it. If it has + * a magic number, but not at byte 0, then read in + * and test with that magic. Otherwise test with the + * the magic number ead in above. + */ + + magic = magic1; + for (ppScheme = ImGetCompressSchemes(); *ppScheme; ppScheme++) + { + pMagic = (*ppScheme)->compress_magic_numbers; + if (pMagic == NULL ) + { + /* No magic numbers */ + continue; + } + for ( ; pMagic->format_magicNumber != NULL; pMagic++) + { + if (pMagic->format_magicLength==0) + continue; /* no magic number */ + if (pMagic->format_magicLocation != 0 ) + { + if ( ioType & IMFILEIOFD ) + { + IMREADMAGIC( fd, magic2, + pMagic->format_magicLength, + pMagic->format_magicLocation ); + } + else + { + IMREADFMAGIC( fp, magic2, + pMagic->format_magicLength, + pMagic->format_magicLocation ); + } + magic = magic2; + } + length = pMagic->format_magicLength; + fmtMagic = pMagic->format_magicNumber; + + /* See if it matches the magic number */ + for (i=0; i < length; i++) + if (fmtMagic[i] != magic[i] ) + break; + if (i==length) + return ( (*ppScheme)->compress_suffixes[0] ); + magic = magic1; + } + + } + + } + + /* + * No magic number match. Check the file name. + */ + + tmpEntry = TagTableQDirect( flagsTable, "file name", 0 ); + if ( tmpEntry != TAGENTRYNULL ) + { + TagEntryQValue( tmpEntry, &fileName); + /* + * Set strTmp to be the one more than the last period + * in the filename. + */ + strTmp = fileName + strlen(fileName) - 1; + while (*strTmp!='.' && strTmp!=fileName) + strTmp--; + if (*strTmp=='.') + { + strTmp++; + if ( (pScheme=ImFileFindCompressionScheme(strTmp))!=NULL) + return pScheme->compress_suffixes[0]; + } + } + + return NULL; +} + + + + +/* + * FUNCTION + * imFileWriteCompressedFile + * + * DESCRIPTION + * Write a compressed file from the tagTable + */ +static int /* returns status */ +#ifdef __STDC__ +imFileWriteCompressedFile(int ioType, int fd, FILE* fp, TagTable* flagsTable, TagTable* tagTable, + ImFileFormatWriteMap *pMap, char* scheme) +#else +imFileWriteCompressedFile(ioType, fd, fp, flagsTable, tagTable, pMap, scheme) +int ioType; +int fd; +FILE* fp; +TagTable* flagsTable; +TagTable* tagTable; +ImFileFormatWriteMap *pMap; +char* scheme; /* Compression scheme to use */ +#endif +{ + char tmpName[1024]; /* uncompressed file */ + char newName[1024]; /* compressed file */ + FILE* tmpFp; /* a file pointer */ + int status; /* status */ + int n; /* bytes read */ + unsigned char buffer[1024]; /* Temporary write buffer */ + ImCompressScheme* pScheme; /* points into our table */ + + /* + * 1. Dump all of the data into a temp file. + * 2. Compress the temp file using the write routine from our table. + * (This will destroy the uncompressed file) + * 3. Dump the compressed file to our output stream. + * 4. Remove the compressed file. + */ + strcpy(tmpName,imMakeTempFile()); + tmpFp = fopen(tmpName,"wb"); + if (tmpFp==NULL) + { + ImErrNo = IMESYS; + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + + status = (*pMap->map_write)( pMap, IMFILEIOFP | IMFILEIOFILE, -1, tmpFp, + flagsTable, tagTable ); + fclose(tmpFp); + + /* + * Look up the compression scheme + */ + pScheme = ImFileFindCompressionScheme(scheme); + if (pScheme==NULL) + { + ImErrNo = IMEFORMATUNKNOWN; + return NULL; + } + + if ( pScheme->compress_encode == NULL ) + { + ImErrNo = IMENOREAD; + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + + /* + * Compress our temp file, and destroy the uncompressed one. + */ + if ((*pScheme->compress_encode)(tmpName, newName, flagsTable)==-1) + { + return -1; + } + + tmpFp = fopen(newName,"rb"); + if (tmpFp==NULL) + { + ImErrNo = IMESYS; + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + + /* + * Send the new file to the outgoing stream + */ + if ( ioType & IMFILEIOFD ) + { + while ( (n = fread( buffer, 1, sizeof( buffer ), tmpFp )) != 0 ) + write( fd, buffer, n ); + } + else + { + while ( (n = fread( buffer, 1, sizeof( buffer ), tmpFp )) != 0 ) + fwrite( buffer, 1, n, fp ); + } + fclose(tmpFp); + unlink(newName); + + return status; +} + +/* + * FUNCTION + * imFileReadCompressedFile + * + * DESCRIPTION + * Read a compressed file into the tagTable + */ +static int +#ifdef __STDC__ +imFileReadCompressedFile(int ioType, int fd, FILE* fp, TagTable* flagsTable, TagTable* tagTable, char* scheme) +#else +imFileReadCompressedFile(ioType, fd, fp, flagsTable, tagTable, scheme) +int ioType; +int fd; +FILE* fp; +TagTable* flagsTable; +TagTable* tagTable; +char* scheme; +#endif +{ + char tmpName[1024]; /* compressed file */ + char newName[1024]; /* uncompressed file */ + FILE* tmpFp; /* holds an fp */ + int tmpFd; /* holds an fd */ + unsigned char buffer[1024]; /* Temporary write buffer */ + int status = 1; /* status */ + int n; /* num of bytes read */ + ImFileFormat **pFormat; /* format that we're reading */ + char* fileName; /* Name of file (in flagsTable) */ + TagEntry* tmpEntry; /* holds a tag table entry */ + char* format; /* format name */ + char message[1024]; /* holds a message */ + ImCompressScheme* pScheme; /* compression scheme entry */ + + /* + * We could do things here with lots of processes + * and lots of pipes. However, that would get + * quite messy. Instead we're just going to + * use temp files. + * + * Algorithm: + * + * 1. Put all the data in a temp file + * 2. Uncompress the temp file + * 3. Read the uncompressed file + * 4. Destroy both files. + */ + strcpy( tmpName , imMakeTempFile() ); + + /* + * Write all the data + */ + if ( ioType & IMFILEIOFD ) + { + if ( (tmpFd = open( tmpName, O_RDWR|O_CREAT, 0666 )) == -1 ) + { + ImErrNo = IMESYS; + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + while ( (n = read( fd, buffer, sizeof( buffer ) )) > 0 ) + write( tmpFd, buffer, n ); + close( tmpFd ); + } + else + { + if ( (tmpFp = fopen( tmpName, "w+b" )) == NULL ) + { + ImErrNo = IMESYS; + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + while ( (n = fread( buffer, 1, sizeof( buffer ), fp )) != 0 ) + fwrite( buffer, 1, n, tmpFp ); + /* Write an EOF */ + fclose( tmpFp ); + } + + /* + * Call the scheme's read routine. + * This routine will uncompress the file, + * delete the file we pass it, + * and tell us the name of the new file. + */ + pScheme = ImFileFindCompressionScheme(scheme); + if (pScheme==NULL) + { + ImErrNo = IMEFORMATUNKNOWN; + return NULL; + } + if ( pScheme->compress_decode == NULL ) + { + ImErrNo = IMENOREAD; + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + if ((*pScheme->compress_decode)(tmpName, newName, flagsTable)==-1) + { + return -1; + } + + /* + * Read the uncompressed file + */ + tmpFp = fopen(newName,"rb"); + if (tmpFp==NULL) + { + ImErrNo = IMESYS; + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + + /* + * What format is the image file? + */ + tmpEntry = TagTableQDirect( flagsTable, "file name", 0 ); + if ( tmpEntry != TAGENTRYNULL ) + { + TagEntryQValue( tmpEntry, &fileName); + } + else + fileName = NULL; + if ((format = ImFileQFFormat(tmpFp, fileName))==NULL) + { + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + + /* + * Look up the format + */ + if ( (pFormat = ImFileFindFormat( format )) == NULL ) + { + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + + if ( (*pFormat)->format_read == NULL ) + { + ImErrNo = IMENOREAD; + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + + sprintf(message,"'%s' (%s)",(*pFormat)->format_names[0],(*pFormat)->format_help); + ImInfo("Image Format",message); + + + status = (*(*pFormat)->format_read)( IMFILEIOFP | IMFILEIOFILE, -1, + tmpFp, flagsTable, tagTable ); + /* + * Destroy the uncompressed file + */ + unlink(newName); + + return status; +} + + +/* + * FUNCTION + * ImFileFindCompressionScheme + * + * DESCRIPTION + * Search through the compression table for this + * scheme. + */ +ImCompressScheme * +#ifdef __STDC__ +ImFileFindCompressionScheme( char* scheme) +#else +ImFileFindCompressionScheme(scheme) +char* scheme; +#endif +{ + int found; /* Have we found it yet? */ + ImCompressScheme** pCompress; /* points into the table */ + char** strPtr; /* points to list of strs */ + + found = 0; + pCompress = ImGetCompressSchemes(); + while (!found && *pCompress) + { + /* + * Check all the names for this scheme + */ + strPtr = (*pCompress)->compress_suffixes; + while (*strPtr!=NULL && strcmp(*strPtr,scheme)!=0) + strPtr++; + if (*strPtr!=NULL) + found = 1; + if (!found) + pCompress++; + } + if (!found) + { + ImErrNo = IMEFORMATUNKNOWN; + return NULL; + } + return *pCompress; +} + +/* + * FUNCTION + * imMakeTempFile + * DESCRIPTION + * make file name for a temp file. + * This name will have the full path of + * the temp file. (i.e. in the correct + * temp directory) + */ +static char* +#ifdef __STDC__ +imMakeTempFile(void) +#else +imMakeTempFile() +#endif +{ + char* tmpDir; /* temp directory */ + static char ret[1024]; /* string to return */ + + /* Get tmpDir from environment. */ + tmpDir = getenv("TMPDIR"); + if (tmpDir==NULL) + sprintf(ret,"/tmp/im.XXXXXX"); + else + sprintf(ret,"%s/im.XXXXXX",tmpDir); + mktemp(ret); + + return ret; +} + + + + diff --git a/utils/roq2/libim/imfmt.c b/utils/roq2/libim/imfmt.c new file mode 100644 index 0000000..fdedab0 --- /dev/null +++ b/utils/roq2/libim/imfmt.c @@ -0,0 +1,2142 @@ +/** + + ** $Header: /roq/libim/imfmt.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/imfmt.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** imfmt.c - master file format table for the image library + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** imfmt.c contains the master file format table for the image + + ** library. The table lists all of the image file formats supported. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** ImGetFileFormats f Get the master list of formats. + + ** + + ** PRIVATE CONTENTS + + ** + + ** imFileFormats v list of file format information + + ** + + ** None + + ** + + ** HISTORY + + ** $Log: /roq/libim/imfmt.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.14 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.13 1995/06/16 08:41:13 bduggan + + ** Made ImFileFormats static, and added a function which returns the list. + + ** The reason for this is that when making a shared object on the sun, + + ** all global variables must be in a seperate file. It's easier just + + ** to make this global variable static, and have a global function. + + ** (Sorry -- that's inaccurate. Not ALL global variable must be in + + ** a seperate file. All the global variables which are used by the tools + + ** need to be in a seperate file.) + + ** + + ** Revision 1.12 1995/05/17 23:43:43 bduggan + + ** added miff, xpm + + ** Merged rpbm, rpnm, rppm, ppm, pnm, pbm + + ** + + ** Revision 1.11 1995/02/16 21:40:00 bduggan + + ** Jpegs + + ** + + ** Revision 1.10 1994/10/03 11:29:36 nadeau + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Updated comments. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.9 92/11/04 12:00:19 groening + + ** took the ImFileFormat info and put + + ** them in their corresponding image files. + + ** + + ** Revision 1.8 92/09/17 14:38:16 vle + + ** Added BMP, CUR, and ICO formats. Re-ordered some declarations. + + ** Updated copyright notice. + + ** + + ** Revision 1.7 92/06/18 16:01:17 groening + + ** targa and viff formats added + + ** + + ** Revision 1.6 92/04/07 15:32:43 vle + + ** Re-ordered the format specifications for IRIS Rgb to be consistent + + ** with rest of imfmt.c. + + ** + + ** Revision 1.5 92/04/03 18:13:52 vle + + ** imconv can no longer output an xwd image without a colortable. + + ** Added lines to allow imconv to support 8-bit image read and + + ** writes and changed read/write function names to be consistent + + ** with rest of code. + + ** + + ** Revision 1.4 91/10/03 09:01:29 nadeau + + ** Changed 'interlace' to 'interleave'. Commented out CGM + + ** support. Added EPS, ICON, and PCX support. Changed + + ** PBM and RPBM entries to be multiple entries, one per + + ** format variant. Updated support for XWD and RAS. Changed + + ** comments for HDF, PS, PIC, and others (cosmetic changes). + + ** + + ** Revision 1.3 91/03/13 12:49:57 nadeau + + ** Fixed various can/can't handle pipe setting bugs. + + ** Reordered RGB alpha compression write preferences for TIFF. + + ** + + ** Revision 1.2 91/03/12 11:02:12 nadeau + + ** Changed pic support to allow pipe read and write. + + ** + + ** Revision 1.1 91/03/08 14:39:30 nadeau + + ** Initial revision + + ** + + **/ + + + +#include "iminternal.h" + + + +/** + + ** TABLES AND INITIALIZATIONS + + ** imFileFormats - master file format table + + ** + + ** CODE CREDITS + + ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1991. + + ** + + ** DESCRIPTION + + ** The SDSC Image Tools' file format handling is driven by a master table + + ** listing the supported image file formats, their attributes, and the + + ** functions to call to read and write data in that format. That master + + ** table is "imFileFormats" declared and initialized here in this file. + + ** + + ** File formats are read and written by format "handlers": C functions + + ** found in separate files in this package. For instance, all of the + + ** handlers for the Sun Rasterfile format are found in "imras.c". + + ** + + ** Each format handler file also declares and initializes a set of + + ** structures and arrays that serve to describe that format. These + + ** structures give the name of the format, alternative names, details + + ** on what types of data might be found in the file, what variants the + + ** handlers can read and write, the magic numbers characteristic of that + + ** format, and so on. All of this is contained within a struct of type + + ** "ImFileFormat". The master imFileFormats array declared here, in this + + ** file, is just an array of pointers to these individual file format + + ** handler descriptions. The format descriptions themselves are in the + + ** same source file as the handler routines for that format, such as + + ** "imras.c" for Sun Rasterfiles. + + ** + + ** ADDING A NEW FILE FORMAT + + ** The SDSC Image Tools are highly configurable to support new file + + ** formats. Adding a format is largely a matter of adding an additional + + ** initialization to the imFileFormats array. Thereafter all of the + + ** SDSC Image Tools will automatically support that new format (after + + ** relinking) without any change to their code. + + ** + + ** Here's a brief outline of the steps to go through to add a new file + + ** format. We'll go into greater detail on each of these steps later in + + ** this comment block. + + ** + + ** 1. Write the read and write routines. + + ** + + ** 2. Declare and initialize the ImFileFormat struct and it's + + ** associated sub-structures. + + ** + + ** 3. Add an "extern" statement to this file to pull in your + + ** new file format's ImFileFormat struct. It's good practice + + ** to also add a #define name and #ifdef's around that extern + + ** statement (see below). + + ** + + ** 4. Add an initialization to the imFileFormats array to point + + ** to your file format's ImFileFormat struct. + + ** + + ** PLEASE! Add new entries in alphabetical order, sorted + + ** by the first file extension name of the format. This + + ** will insure that printed file format lists, such as by + + ** the "imformats" tool, will look good. + + ** + + ** 5. Update the Makefile, build the library and relink the tools. + + ** + + ** WRITING THE READ ROUTINE + + ** Every format has one top-level read routine called by ImFileRead( ) + + ** when the user requests that format. Typically, read routines are + + ** named with the convention of starting with "im" followed by the format's + + ** name, followed by "Read". So, for instance, the Sun Rasterfile read + + ** routine is named "imRasRead". + + ** + + ** The read routine should be declared as "static" (private) to the + + ** handler source file. There is no need for it to be called directly + + ** from outside the handler file. It will, instead, be called via a + + ** function pointer that you place into your handler's "ImFileFormat" + + ** struct. + + ** + + ** A handler read routine is called with the following arguments: + + ** + + ** int ioType + + ** The type of file descriptor or pointer on which to + + ** do I/O. The user's calls to either ImFileRead (with + + ** a file descriptor) or ImFileFRead (with a file pointer) + + ** both vector down into the same handler read function. + + ** Read functions should do if's on the ioType to see + + ** which kind of file I/O to do. + + ** + + ** int fd + + ** The file descriptor if (ioType & IMFILEIOFD) is true. + + ** + + ** FILE *fp + + ** The file pointer if (ioType & IMFILEIOFP) is true. + + ** + + ** TagTable *flagsTable + + ** A table of flags that the user has supplied to steer + + ** the read operation. Global flags that set up error + + ** message redirection, and so on, have already been + + ** extracted and handled. In general, read handlers + + ** need not check the flagsTable. + + ** + + ** TagTable *tagTable + + ** The table into which to put any data read from the + + ** file. + + ** + + ** The read handler should direct all file I/O through the SDSC Binary + + ** I/O Library to insure maximum portability among hardware architectures + + ** with different byte orders, word sizes, floating point formats, and + + ** structure padding. Please see the SDSC Binary I/O man pages for + + ** details. + + ** + + ** Images read from the file should be placed into ImVfb's allocated via + + ** ImVfbAlloc( ). Please see the ImVfbAlloc( ) man page for details and + + ** pointers to other relevant man pages. + + ** + + ** Color tables read from the file should be placed into ImClt's allocated + + ** via ImCltAlloc( ). Please see the ImCltAlloc( ) man page for details + + ** and pointers to other relevant man pages. + + ** + + ** Data from the file should be appended to the tagTable using + + ** TagTableAppend( ) in the SDSC TagTable Library. Please see the + + ** TagTableAppend( ) man page for details. + + ** + + ** When everything else is done, the read handler should return a -1 on + + ** an error (and set ImErrNo to an appropriate error code), or a positive + + ** constant counting the number of things added to the tagTable. + + ** + + ** Error messages should always be output using one of three macros: + + ** + + ** ImErrorInfo( message, return_code, errno ) + + ** ImErrorWarning( message, return_code, errno ) + + ** ImErrorFatal( message, return_code, errno ) + + ** + + ** ImErrorInfo( ) messages are for information purposes only. These + + ** are typically general info about what has been found in the file. + + ** Command-line tools often block such output unless the -verbose option + + ** has been given. + + ** + + ** ImErrorWarning( ) messages are to warn of possible problems in reading + + ** the file. Warnings are for recoverable problems. Command-line tools + + ** typically print these to stderr. + + ** + + ** ImErrorFatal( ) messages are to alert to a fatal reading problem and + + ** are not recoverable. Command-line tools typically print these to + + ** stderr. + + ** + + ** Each of these three macros covers up a set of if-statements that + + ** select printing the message to a user-supplied stream, calling a + + ** user-supplied callback, or skipping the message entirely. If a + + ** callback is called, the callback may return a flag indicating that + + ** the read routine is to abort (imagine an X dialog box with a user + + ** choice for "continue" or "abort"). If the callback says to abort, + + ** the macro will automatically exit the read handler routine, returning + + ** the given "return_code" and setting ImErrNo to the given "errno". + + ** Please note: ImErrorFatal( ) ALWAYS exits the read routine, + + ** regardless of the callback's return value. + + ** + + ** WRITING THE WRITE ROUTINES + + ** Every format has one or more write routines. When the user calls + + ** ImFileWrite( ), that function scans the list of supported output + + ** image types described in the format's write map (see below) and + + ** calls the write function appropriate for the given VFB. So, typically + + ** there is one write routine for each VFB type that can be written to + + ** the output file. + + ** + + ** Write handler's have the same call arguments as read handlers, with + + ** one extra argument at the front of the list: + + ** + + ** ImFileFormatWriteMap *pMap + + ** While the write map is described in more detail below, + + ** the gist of it is that each file format has an array + + ** of structs called a "write map". Each array entry + + ** describes a VFB type that can be written to that + + ** format. Each array entry also has a pointer to a + + ** write function to do the writing of that VFB type. + + ** When that write function is called, the "pMap" + + ** argument is set to point to that array entry. + + ** + + ** In general, write handlers can ignore the pMap + + ** argument. + + ** + + ** int ioType + + ** int fd + + ** FILE *fp + + ** TagTable *flagsTable + + ** TagTable *tagTable + + ** + + ** Most of the flags in the flagsTable have already been processed by + + ** ImFileWrite( ) and used to decide which write handler function to call. + + ** Most write handlers can ignore the flagsTable. + + ** + + ** Data written to the output file should be pulled directly from the + + ** tagTable and should be written in the order in which it appears in + + ** the tagTable. + + ** + + ** All file I/O should be done through the SDSC Binary I/O library in + + ** order to insure maximum portability. + + ** + + ** Info, warning, and fatal error messages should be printed using the + + ** same set of macros as described above for read handlers. + + ** + + ** When the write handler is complete, it should return a -1 if an error + + ** occurred (and set ImErrNo to an appropriate error code), or a positive + + ** integer indiciating the number of tags in the tagTable that were + + ** used. + + ** + + ** MAPPING TABLE INITIALIZATIONS + + ** The mapping tables tell the generic portions of the library what the + + ** various formats are capable of handling. This lets the higher-level + + ** code return errors to the user if, say, the user wants to write a + + ** JPEG-compressed image to a GIF file, which can't do JPEG compression. + + ** At the same time it lets that higher-level code convert images to + + ** different pixel depths if necessary *before* handing them to the + + ** write handler. This means that if the user asks to write a 24-bit RGB + + ** image to a GIF file, which can't do 24-bit images, the higher-level + + ** code will automatically drop it down to 8-bit before calling the write + + ** handler. + + ** + + ** Read Mapping + + ** The read mapping table is primarily informational. Very little code + + ** uses it except for nice printouts. + + ** + + ** The read map table is an array of structures, each one describing one + + ** type of data that might be found in the file format. One entry might, + + ** for instance, say the format can support an 8-bit pseudo-color image + + ** with a color table. The next entry might say the same format could + + ** instead have a 24-bit RGB image. And the third entry might say the + + ** format could have a 24-bit RGB image with an alpha channel. Each + + ** variant gets it's own struct in the array and it's own initialization. + + ** + + ** Structure initializations are divided into columns (one column per + + ** struct field). + + ** + + ** Columns 1-3 describe the image storage of the incomming image. The + + ** first is a type indicator (color index or RGB). The second is the + + ** number of channels of data (usually 1 or 3). The third is the size, + + ** in bits, of each channel (usually 1 or 8). Do not include the + + ** alpha channel (if any) in the depth. That info goes in Column 4. + + ** + + ** Example: IN,1,8 -- 8-bit index + + ** RGB,3,8 -- 3*8-bit = 24-bit RGB + + ** + + ** Column 4 is a mask of attributes for the incomming image. The + + ** mask indicates if a CLT or alpha plane is included, how images + + ** are interleaved (RGB only), and what compression scheme is used. + + ** + + ** Example: 0 -- nothing + + ** C -- has CLT + + ** C|A -- has CLT and alpha + + ** A|RLE|PI -- plane interleaved RLE & alpha + + ** + + ** Column 5 is the VFB type of the resulting image. Do not include + + ** IMVFBALPHA if an alpha plane is present. That info goes in Column 6. + + ** + + ** Example: IMVFBINDEX8 -- 8-bit index + + ** IMVFBRGB -- RGB + + ** + + ** Column 6 is a mask of attributes for the VFB. The mask indicates if + + ** a CLT or alpha plane is included. Interleave and compression codes + + ** are inappropriate for this column. + + ** + + ** Example: 0 -- no clt or alpha + + ** C|A -- clt and alpha + + ** + + ** For example: a mythical format "Xyz" supports 8-bit color index and + + ** 24-bit true-color RGB images. 8-bit images always have a CLT. RGB + + ** images never do. RGB images may be uninterleaved, or plane interleaved. + + ** And finally, the image could be uncompressed, or run-length encoded + + ** (RLE). + + ** + + ** static ImFileFormatReadMap imXyzReadMap[ ] = + + ** { + + ** { IN,1,8, RLE | C, IMVFBINDEX8, C }, + + ** { IN,1,8, C, IMVFBINDEX8, C }, + + ** { RGB,3,8, RLE | PI, IMVFBRGB, 0 }, + + ** { RGB,3,8, PI, IMVFBRGB, 0 }, + + ** { RGB,3,8, RLE, IMVFBRGB, 0 }, + + ** { RGB,3,8, 0, IMVFBRGB, 0 }, + + ** { -1, 0, -1, 0 }, + + ** }; + + ** + + ** Always end the list with a -1 depth entry. + + ** + + ** 8-bit index occurs twice in this example, once with "RLE" Or-ed into + + ** the incomming attribute mask, and once without. If no compression + + ** flag is Or-ed in, it is assumed to mean uncompressed. + + ** + + ** 24-bit RGB occurs four times: twice with "RLE", and twice without. + + ** For each of the RLE cases, one has "PI" for "Plane Interleave" + + ** Or-ed in, and one doesn't. Likewise with the non-"RLE" cases. + + ** + + ** The "C", "PI", "RLE", and so on flags are short-cut #define's found + + ** in iminternal.h. They include: + + ** + + ** C - CLT is included + + ** A - Alpha plane is included + + ** + + ** LI - Line interleaved RGB + + ** PI - Plane interleaved RGB + + ** + + ** RLE - Run-length encoded + + ** LZW - Lempel-Ziv & Welsh compressed + + ** PB - Macintosh PackBits + + ** DCT - Descrete Cosine Transform compression + + ** + + ** IN - Index type image + + ** RGB - RGB type image + + ** + + ** If C is not present, it is assumed there is no CLT. + + ** + + ** If A is not present, it is assumed there is no alpha plane. + + ** + + ** If neither LI or PI are present, it is assumed that the image + + ** is not interleaved. + + ** + + ** If none of RLE, LZW, PB, or DCT are present, it is assumed that the + + ** image is uncompressed. + + ** + + ** Write Mapping + + ** The write mapping table is critical. It is used by the ImFileWrite( ) + + ** code to convert the VFB to be written in to something the format can + + ** write. Depth's are changed. CLT's are added. And so on. The + + ** table is also used to make nice printouts. + + ** + + ** As with the read map, the write map is really an array of structs + + ** initialized in the handler's source file. That initialization takes + + ** the form of a series of rows and columns of data. Each row describes + + ** one output variant supported. The column meanings are described below: + + ** + + ** Column 1 is the incomming VFB type. Do not include IMVFBALPHA if an + + ** alpha plane is to be present. That info goes in column 2. + + ** + + ** Example: IMVFBINDEX8 -- 8-bit index + + ** IMVFBRGB -- RGB + + ** IMVFBMONO -- monochrome index + + ** + + ** Column 2 is a mask of required attributes for the incomming VFB. The + + ** mask indicates if a CLT or alpha plane must be present. Interleave + + ** and compression scheme codes are inappropriate for this column. + + ** + + ** Example: 0 -- neither clt or alpha + + ** C|A -- clt and alpha + + ** + + ** Columns 3-5 are the image type and depth attributes of the outgoing + + ** (in the file) image. This includes the type (index or RGB), number + + ** of channels (usually 1 or 3), and number of bits per channel (usually + + ** 1 or 8). Do not include an alpha channel in the depth. That goes + + ** in column 6. + + ** + + ** Example: IN,1,8 -- 8-bit index + + ** RGB,3,8 -- 3*8-bit = 24-bit RGB + + ** + + ** Column 6 is a mask of resulting attributes for the outgoing image. + + ** The mask indicates if a CLT or alpha plane is included, how images + + ** are interleaved (RGB only), and what compression scheme is used. + + ** + + ** Example: 0 -- no clt, no alpha, ... + + ** C|A|RLE -- clt + alpha + RLE compress + + ** A|LI -- alpha + line interleaved + + ** + + ** Column 7 is an integer function pointer to the function to do the + + ** writing of that variant of the format. This is one of the write + + ** handlers for the format. + + ** + + ** For example: the same mythical "Xyz" format used for the read map + + ** example is used. + + ** + + ** private ImFileFormatWriteMap imXyzWriteMap[ ] = + + ** { + + ** { IMVFBINDEX8, 0, IN,1,8, RLE | C, ImXyzWrite8_RLE_C}, + + ** { IMVFBINDEX8, C, IN,1,8, RLE | C, ImXyzWrite8_RLE_C}, + + ** { IMVFBINDEX8, 0, IN,1,8, C, ImXyzWrite8_C }, + + ** { IMVFBINDEX8, C, IN,1,8, C, ImXyzWrite8_C }, + + ** + + ** { IMVFBRGB, 0, RGB,3,8, RLE | PI,ImXyzWriteRGB_RLE_PI }, + + ** { IMVFBRGB, 0, RGB,3,8, RLE, ImXyzWriteRGB_RLE }, + + ** { IMVFBRGB, 0, RGB,3,8, PI, ImXyzWriteRGB_PI}, + + ** { IMVFBRGB, 0, RGB,3,8, 0, ImXyzWriteRGB }, + + ** { -1, 0, -1, 0, NULL }, + + ** }; + + ** + + ** Always end the list with a -1 VFB type entry. + + ** + + ** When multiple entries are made for the same VFB type and incomming + + ** attributes, list from "best" to "worst". List compressed versions + + ** before uncompressed. List plane and line interleaved RGB before + + ** uninterleaved. List non-alpha versions before alpha, and so on. + + ** + + ** In this example, 8-bit VFB's occur four times: twice for RLE output + + ** encoding, and twice without. Within each of those, one occurrence + + ** requires no CLT, and one requires one. However, both of those cases + + ** go to the same routine (which presumably checks if the VFB has a CLT, + + ** and if not creates one). + + ** + + ** 24-bit RGB VFB's occur four times: twice for RLE output encoding, + + ** and twice without. Within each of those, one occurrence is + + ** uninterleaved, and one plane interleaved. + + ** + + ** There may not be two identical lines in the table. However, any number + + ** of lines may use the same input conditions and/or the same write + + ** function. + + ** + + ** ImFileWrite() checks this table and the flagsTable giving the user's + + ** write request flags, and decides which write function to call. If + + ** it can't find one that matches the request, and it can't convert the + + ** VFB into one that matches, then it returns an error to the user without + + ** every calling any format handler function. + + ** + + ** MAGIC NUMBERS + + ** Magic numbers are a UNIX trick to help in identifying the type of data + + ** found in a file. The typically just amount to 2 to 4 bytes at the + + ** start of the file that are a kind of signature for that type of data. + + ** A Sun Rasterfile, for instance, starts with 0x59, 0xA6, 0x6A, 0x95. + + ** An SGI RGB file starts with 0x01, 0xDA. And so on. + + ** + + ** Not all format types have magic numbers, but many do. The SDSC Image + + ** Tools use magic numbers to help figure out what type of file format + + ** is being used by an incomming image file. Based upon that we can + + ** figure out which format handler to call to read it in. + + ** + + ** Magic numbers are described an array of ImFileMagic structs declared + + ** and initialized within the format handler's source file. Each array + + ** entry describes one possible magic number for that format. Formats + + ** with multiple possible magic numbers (such as the PBM+ format) will + + ** list multiple numbers. + + ** + + ** Each array entry gives the file byte offset at which a magic number + + ** can be found (usually 0), the number of bytes in the magic number + + ** (usually 2 or 4), and a pointer to an unsigned char array of those + + ** bytes. For example, here is the set of declarations for the magic + + ** number of the Microsoft Windows Cursor file format: + + ** + + ** static unsigned char imCurMagicNumber[ ] = + + ** { 0x00, 0x00, 0x02, 0x00 }; + + ** static ImFileMagic imCurMagic[ ] = + + ** { + + ** { 0, 4, imCurMagicNumber }, + + ** { 0, 0, NULL } + + ** }; + + ** + + ** The magic number initialization is always terminated with a + + ** { 0, 0, NULL } entry. + + ** + + ** If the format has no magic number, then the array should be initialized + + ** to just one { 0, 0, NULL }. + + ** + + ** FORMAT NAMES + + ** Most image file formats have multiple common names by which they are + + ** known. The Sun Rasterfile format, for instance, is known by at + + ** least the following names and common file name extensions: + + ** + + ** ras, sun, sr, scr + + ** + + ** The PBM+ files are known by: + + ** + + ** pbm, pgm, ppm, pnm, rpbm, rpgm, rppm, rpnm + + ** + + ** And so on. Users of the SDSC Image Tools are able to use any of + + ** these file format names, and filename extensions, when refering to + + ** the format. Writers of new file format handlers need to list those + + ** names so that the higher-level code can recognize them. Such a list + + ** is simply an initialized array of char *'s. The following, for + + ** instance, is the array initialization for the Sun Rasterfile handlers: + + ** + + ** static char *imRasNames[ ] = + + ** { "ras", "sun", "sr", "scr", NULL }; + + ** + + ** Note that the list must end with a NULL pointer. + + ** + + ** The first entry in the array has special significance and is usually + + ** considered the primary name for the format. + + ** + + ** THE IMFILEFORMAT INITIALIZATION + + ** Every file format has one, globally accessible, ImFileFormat struct + + ** and it's initialization. It is a pointer to that struct that is + + ** placed into the imFileFormats array initialization found here in this + + ** file. This one ImFileFormat struct for the format describes + + ** everything we need to know about the format. + + ** + + ** The ImFileFormat struct has a number of fields as follows: + + ** + + ** format_names + + ** The name of the format names array discussed earlier. + + ** This array lists the various names by which the + + ** file format is known. + + ** + + ** format_help + + ** This is a character string giving the long form of + + ** the file format's name. For instance, Adobe EPS + + ** file format says: "Encapsulated PostScript file". + + ** This string is used in format listings and help lists. + + ** + + ** format_creator + + ** This is a character string giving the name of the + + ** creator of the file format (not the programmer of + + ** this code!). So, for Adobe EPS, the creator string + + ** says: "Adobe". This info is used in format listings. + + ** + + ** format_readSupport + + ** This is a one or more line character string describing + + ** in human-readable terms what variants of the file + + ** format can be read by the handler. This should closely + + ** mimic what is found in the handler's read map, but be + + ** more friendly to read. This info is used in format + + ** listings. + + ** + + ** format_writeSupport + + ** This is a one or more line character string describing + + ** in human-readable terms what variants of the file + + ** format can be written by the handler. This should + + ** closely mimic what is found in the handler's write + + ** map, but be more friendly to read. This info is used + + ** in format listings. + + ** + + ** format_magicMap + + ** This is initialized to a pointer to the magic number + + ** array discussed earlier. This array lists the + + ** various magic numbers for the file format. + + ** + + ** format_readMultiVfb + + ** Can the read handler read multiple images from the + + ** same file? This depends upon the abilities of the + + ** file format. If so, initialize this to IMMULTI. If + + ** not, initialize this to IMNOMULTI. + + ** + + ** format_readPipe + + ** Can the read handler read from a pipe? If the handler + + ** doesn't do any seeks, the answer is IMPIPE. Otherwise + + ** the answer is IMNOPIPE. For IMNOPIPE formats, the + + ** higher-level code will automatically stage things to + + ** an intermediate file and hand that file to the read + + ** handler instead of the original pipe. + + ** + + ** format_writeMultiVfb + + ** Can the write handler write multiple images to the + + ** same file? This depends upon the abilities of the + + ** file format. If so, initialize this flag to IMMULTI. + + ** Otherwise initialize it to IMNOMULTI. + + ** + + ** format_writePipe + + ** Can the write handler write to a pipe? If the handler + + ** doesn't do any seeks, the answer is IMPIPE. Otherwise + + ** the answer is IMNOPIPE and the higher-level code will + + ** automatically stage to a temp file rather than giving + + ** the pipe to the write handler. + + ** + + ** format_read + + ** This is initialized to the handler's read function. + + ** + + ** format_readMap + + ** This is initialized to a pointer to the read map + + ** array discussed earlier. + + ** + + ** format_writeMap + + ** This is initialized to a pointer to the write map + + ** array discussed earlier. + + ** + + ** For example, here's the ImFileFormat array structure declaration + + ** and initialization for the Compuserve GIF format: + + ** + + ** ImFileFormat ImFileGifFormat = + + ** { + + ** imGifNames, "Graphics Image File", + + ** "Compuserve", + + ** "1- thru 8-bit color index Lempel-Ziv & Welsh-compressed files", + + ** "1 and 8-bit color index Lempel-Ziv & Welsh-compressed files", + + ** imGifMagic, + + ** IMNOMULTI, IMNOPIPE, + + ** IMNOMULTI, IMNOPIPE, + + ** imGifRead, imGifReadMap, imGifWriteMap, + + ** }; + + **/ + + + + + + + + + + + + + + + +/* + + * FORMATS + + * These #define's enable inclusion of the initialization for that + + * format into the imFileFormats table. + + * + + * It is considered good style to define one of these for every file + + * format added. It is not, however, required. These names are only + + * known within the scope of this file. + + */ + + + +#define bmp + +#define cur + +#define eps + +#define gif + +#define hdf + +#define ico + +#define icon + +#define iff + +#define jpeg + +#define miff + +#define mpnt + +#define pbm + +#define pcx + +#define pic + +#define pict + +#define pix + +#define ras + +#define rgb + +#define rla + +#define rle + +#define softimage + +#define synu + +#define tga + +#define tiff + +#define viff + +#define x + +#define xbm + +#define xpm + +#define xwd + + + + + + + + + + + +/* + + * FUNCTION DECLARATIONS + + * ImFile***Format - external file format description structures + + * + + * It is considered good style to put an #ifdef around each extern + + * statement so that sites that don't want all of these formats installed + + * can block one or more of them. + + */ + + + +#ifdef bmp + + extern ImFileFormat ImFileBmpFormat; + +#endif /* bmp */ + + + +#ifdef cur + + extern ImFileFormat ImFileCurFormat; + +#endif /* cur */ + + + +#ifdef eps + + extern ImFileFormat ImFileEpsFormat; + +#endif /* eps */ + + + +#ifdef gif + + extern ImFileFormat ImFileGifFormat; + +#endif /* gif */ + + + +#ifdef hdf + + extern ImFileFormat ImFileHdfFormat; + +#endif /* hdf */ + + + +#ifdef ico + + extern ImFileFormat ImFileIcoFormat; + +#endif /* ico */ + + + +#ifdef icon + + extern ImFileFormat ImFileIconFormat; + +#endif /* icon */ + + + +#ifdef iff + + extern ImFileFormat ImFileIffFormat; + +#endif /* iff */ + + + +#ifdef jpeg + + extern ImFileFormat ImFileJpegFormat; + +#endif /* jpeg */ + + + +#ifdef miff + + extern ImFileFormat ImFileMiffFormat; + +#endif /* miff */ + + + +#ifdef mpnt + + extern ImFileFormat ImFileMpntFormat; + +#endif /* mpnt */ + + + +#ifdef pbm + + extern ImFileFormat ImFilePbmFormat; + +#endif /* pbm */ + + + +#ifdef pcx + + extern ImFileFormat ImFilePcxFormat; + +#endif /* pcx */ + + + +#ifdef pic + + extern ImFileFormat ImFilePicFormat; + +#endif /* pic */ + + + +#ifdef pict + + extern ImFileFormat ImFilePictFormat; + +#endif /* pict */ + + + +#ifdef pix + + extern ImFileFormat ImFilePixFormat; + +#endif /* pix */ + + + +#ifdef ps + + extern ImFileFormat ImFilePsFormat; + +#endif /* ps */ + + + +#ifdef ras + + extern ImFileFormat ImFileRasFormat; + +#endif /* ras */ + + + +#ifdef rgb + + extern ImFileFormat ImFileRgbFormat; + +#endif /* rgb */ + + + +#ifdef rla + + extern ImFileFormat ImFileRlaFormat; + +#endif /* rla */ + + + +#ifdef rle + + extern ImFileFormat ImFileRleFormat; + +#endif /* rle */ + + + +#ifdef softimage + + extern ImFileFormat ImFileSoftimageFormat; + +#endif /* softimage */ + + + +#ifdef synu + + extern ImFileFormat ImFileSynuFormat; + +#endif /* synu */ + + + +#ifdef tga + + extern ImFileFormat ImFileTgaFormat; + +#endif /* tga */ + + + +#ifdef tiff + + extern ImFileFormat ImFileTiffFormat; + +#endif /* tiff */ + + + +#ifdef viff + + extern ImFileFormat ImFileViffFormat; + +#endif /* viff */ + + + +#ifdef x + + extern ImFileFormat ImFileXFormat; + +#endif /* x */ + + + +#ifdef xbm + + extern ImFileFormat ImFileXbmFormat; + +#endif /* xbm */ + + + +#ifdef xpm + + extern ImFileFormat ImFileXpmFormat; + +#endif /* xpm */ + + + +#ifdef xwd + + extern ImFileFormat ImFileXwdFormat; + +#endif /* xwd */ + + + + + + + + + + + +/* + + * STATIC VARIABLE + + * imFileFormats - ** Master file format table ! ** + + * + + * DESCRIPTION + + * Add one pointer to this array initialization for each file format + + * struct. Please add them in alphabetical order, sorted by the first + + * name in the format's name list. This makes output listings look + + * nicer. + + * + + * It is considered good style to put an #ifdef around each initialization + + * so that sites that don't wish all of these formats to be installed + + * can simply and easily block one or more. + + */ + + + +static ImFileFormat *imFileFormats[ ] = + +{ + + + +#ifdef bmp + + &ImFileBmpFormat, + +#endif /* bmp */ + + + + + +#ifdef cur + + &ImFileCurFormat, + +#endif /* cur */ + + + + + +#ifdef eps + + &ImFileEpsFormat, + +#endif /* eps */ + + + + + +#ifdef gif + + &ImFileGifFormat, + +#endif /* gif */ + + + + + +#ifdef hdf + + &ImFileHdfFormat, + +#endif /* hdf */ + + + + + +#ifdef ico + + &ImFileIcoFormat, + +#endif /* ico */ + + + + + +#ifdef icon + + &ImFileIconFormat, + +#endif /* icon */ + + + + + +#ifdef iff + + &ImFileIffFormat, + +#endif /* iff */ + + + + + +#ifdef jpeg + + &ImFileJpegFormat, + +#endif /* jpeg */ + + + + + +#ifdef miff + + &ImFileMiffFormat, + +#endif /* miff */ + + + + + +#ifdef mpnt + + &ImFileMpntFormat, + +#endif /* mpnt */ + + + + + +#ifdef pbm + + &ImFilePbmFormat, + +#endif /* pbm */ + + + + + +#ifdef pcx + + &ImFilePcxFormat, + +#endif /* pcx */ + + + + + +#ifdef pgm + + &ImFilePgmFormat, + +#endif /* pgm */ + + + + + +#ifdef pic + + &ImFilePicFormat, + +#endif /* pic */ + + + + + +#ifdef pict + + &ImFilePictFormat, + +#endif /* pict */ + + + + + +#ifdef pix + + &ImFilePixFormat, + +#endif /* pix */ + + + + + +#ifdef ps + + &ImFilePsFormat, + +#endif /* ps */ + + + + + +#ifdef ras + + &ImFileRasFormat, + +#endif /* ras */ + + + + + +#ifdef rgb + + &ImFileRgbFormat, + +#endif /* rgb */ + + + + + +#ifdef rla + + &ImFileRlaFormat, + +#endif /* rla */ + + + + + +#ifdef rle + + &ImFileRleFormat, + +#endif /* rle */ + + + + + +#ifdef softimage + + &ImFileSoftimageFormat, + +#endif /* softimage */ + + + + + +#ifdef synu + + &ImFileSynuFormat, + +#endif /* synu */ + + + + + +#ifdef tga + + &ImFileTgaFormat, + +#endif /* tga */ + + + +#ifdef tiff + + &ImFileTiffFormat, + +#endif /* tiff */ + + + + + +#ifdef viff + + &ImFileViffFormat, + +#endif /* viff */ + + + + + +#ifdef x + + &ImFileXFormat, + +#endif /* x */ + + + + + +#ifdef xbm + + &ImFileXbmFormat, + +#endif /* xbm */ + + + + + +#ifdef xpm + + &ImFileXpmFormat, + +#endif /* xpm */ + + + + + +#ifdef xwd + + &ImFileXwdFormat, + +#endif /* xwd */ + + + + + + NULL /* The table must end with a NULL */ + +}; + + + +/* + + * FUNCTION + + * ImGetFileFormats + + * + + * DESCRIPTION + + * Give the table to the user. + + */ + +ImFileFormat** + +#ifdef __STDC__ + +ImGetFileFormats( void ) + +#else + +ImGetFileFormats() + +#endif + +{ + + return imFileFormats; + +} + + + diff --git a/utils/roq2/libim/imgif.c b/utils/roq2/libim/imgif.c new file mode 100644 index 0000000..c152dc3 --- /dev/null +++ b/utils/roq2/libim/imgif.c @@ -0,0 +1,4067 @@ +/** + + ** $Header: /roq/libim/imgif.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/imgif.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** imgif.c - Compuserve Graphics Interchange Format + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** imgif.c contains routines to read and write Compuserve GIF image + + ** format for the image manipulation library. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** none + + ** + + ** PRIVATE CONTENTS + + ** imGifHeaderInfo t GIF header information + + ** imGifHeaderFields v imGifHeaderInfo description for Bin pkg + + ** imGifHeader v Gif header holder + + ** imGifImDescInfo t Gif image descriptor information + + ** imGifImDescFields v imGifImDescInfo description for Bin pkg + + ** imGifImDesc v Gif image descriptor holder + + ** + + ** IMGIFMAGIC d file magic number + + ** IMGIFBITPIXEL d mask to read #bits/pixel in the image + + ** IMGIFGLOBALCM d mask to read if a global colormap is present + + ** IMGIFLOCALCM d mask to read if a local colormap is present + + ** IMGIFINTERLACED d mask to read the pixel display sequence used + + ** IMGIFTERMINATOR d termination character of a Gif image file + + ** IMGIFEXTENSION d Gif extension block introducer character + + ** IMGIFSEPARATOR d image separator character + + ** IMGIFENDBLOCK d terminates extension blocks + + ** IMGIFNCOLORRES d # bits of color resolution + + ** IMGIFNBITSPIXEL d # bits per pixel + + ** IMGIFBACKGROUND d index of background color + + ** IMGIFCODESIZE d initial code size used for lzw algorithm + + ** IMGIFNOCOLORMAP d no color lookup table present + + ** + + ** imGifVfbRead1 f Read a mono Gif image + + ** imGifCltRead f Read a color lookup table + + ** imGifCltWrite f Write a color lookup table + + ** imGifVfbWrite8 f Write a 8-index Vfb + + ** imGifVfbWrite1 f Write a mono Vfb + + ** imGifRead f read a Compuserve GIF image + + ** imGifWrite f write a Compuserve GIF image + + ** + + ** HISTORY + + ** $Log: /roq/libim/imgif.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.22 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.21 1995/06/15 20:19:28 bduggan + + ** removed use of 'new' + + ** Added some casts + + ** + + ** Revision 1.20 1995/05/30 16:57:24 bduggan + + ** Changed comment. + + ** + + ** Revision 1.19 1995/05/17 23:44:54 bduggan + + ** Added transparency support + + ** + + ** Revision 1.18 1995/04/03 21:24:38 bduggan + + ** took out #ifdef NEWMAGIC + + ** + + ** Revision 1.17 1995/01/10 23:23:37 bduggan + + ** Made read/write functions static + + ** + + ** Revision 1.17 1995/01/10 23:23:37 bduggan + + ** Made read/write functions static + + ** + + ** Revision 1.16 94/10/03 11:30:06 nadeau + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Rearranged magic number structures for format handlers. + + ** Made format handler routines static (i.e., local to file). + + ** Updated comments, adding format descriptions and references. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.15 92/12/09 18:33:05 nadeau + + ** Corrected misinterpretation of CLT write flags that wrote + + ** grayscale INDEX8 images incorrectly. Also corrected and + + ** updated various info messages. + + ** + + ** Revision 1.14 92/12/03 01:48:12 nadeau + + ** Corrected info messages. + + ** + + ** Revision 1.13 92/11/23 18:42:14 nadeau + + ** Removed use of IMINFOMSG. + + ** + + ** Revision 1.12 92/11/04 11:49:03 groening + + ** put ImFIleFormat info and magic number info + + ** from imfmt.c into this file. + + ** + + ** Revision 1.11 92/10/12 15:57:13 vle + + ** Fixed typo: Limpel -> Lempel + + ** + + ** Revision 1.10 92/09/29 17:58:50 vle + + ** Added ImInfo messages. + + ** + + ** Revision 1.9 92/09/17 14:42:03 vle + + ** Updated to 89a specification. Fixed bug in 2-bit image read. + + ** Updated copyright notice. + + ** + + ** Revision 1.8 92/04/03 17:43:14 nadeau + + ** Added extern declarations of functions so that the SGI + + ** compiler and linker don't get confused. + + ** + + ** Revision 1.7 91/10/03 09:03:01 nadeau + + ** Fixed #includes. Turned off DEBUG. + + ** + + ** Revision 1.6 91/03/13 17:09:44 soraya + + ** fixing bugs + + ** + + ** Revision 1.5 91/03/11 09:16:40 soraya + + ** Optimization, finish of write, and removal of debug + + ** print statements. + + ** + + ** Revision 1.4 91/02/12 11:45:52 nadeau + + ** Removed the tag table checking and VFB conversion now + + ** handled by ImFileRead and ImFileWrite. Made the error + + ** checking more robust. + + ** + + ** Revision 1.3 91/01/31 08:34:20 soraya + + ** *** empty log message *** + + ** + + ** Revision 1.2 90/12/26 18:50:20 soraya + + ** *** empty log message *** + + ** + + ** Revision 1.1 90/12/26 13:24:43 soraya + + ** Initial revision + + ** + + **/ + + + +#include "iminternal.h" + + + + + +/** + + ** FORMAT + + ** gif - Compuserve Graphics Interchange Format + + ** + + ** AKA + + ** giff + + ** + + ** FORMAT REFERENCES + + ** Graphics Interchange Format, version 89a, CompuServe, 1990. + + ** + + ** Bit-Mapped Graphics, Steve Rimmer, McGraw-Hill, 1990. + + ** + + ** Graphics File Formats, David C. Kay, John R. Levine, McGraw-Hill, + + ** 1992. + + ** + + ** Supercharged Bitmapped Graphics, Steve Rimmer, McGraw-Hill, 1992. + + ** + + ** CODE CREDITS + + ** Custom development, Soraya Gonzales, Intevep S.A., Venzuela, 1990. + + ** Extensions, Vinh Le, San Diego Supercomputer Center, 1992. + + ** Extensions, Dave Nadeau, San Diego Supercomputer Center, 1993. + + ** + + ** DESCRIPTION + + ** CompuServe's GIF (Graphics Interchange Format) is used on on-line + + ** image libraries throughout the world for the storage of medium and + + ** low resolution images of 8-bit depths or less. + + ** + + ** Header Block + + ** GIF data streams begin with a header block of the form: + + ** + + ** Field Size + + ** ----- ---- + + ** Signature 3 bytes + + ** Version 3 bytes + + ** + + ** The "Signature" field is always "GIF". The "Version" field is + + ** one of "87a" or "89a", corresponding to the two outstanding versions + + ** of the GIF format specification. + + ** + + ** This GIF translator reads 87a and 89a spec GIF files, but + + ** per recommendations in the GIF spec, always writes the + + ** lowest-common-denominator GIF files at 87a spec levels. + + ** + + ** Logical Screen Block + + ** Immediately following the GIF header is a logical screen block of + + ** the form: + + ** + + ** Field Size + + ** ----- ---- + + ** Logical Screen Width 2 bytes, unsigned integer + + ** Logical Screen Height 2 bytes, unsigned integer + + ** Flags 1 byte + + ** Background Color Index 1 byte + + ** Pixel Aspect Ratio 1 byte + + ** + + ** The screen width and height refer to the size of the logical screen + + ** onto which the GIF image is to be displayed on the display device. + + ** + + ** This GIF translator ignores the screen width and height. + + ** + + ** The "Flags" field is a bitmask with the following bit meanings: + + ** + + ** Field Bits + + ** ----- ---- + + ** Global Color Table 7 + + ** Color Resolution 6,5,4 + + ** Sort 3 + + ** Size of Global Color Tb 2,1,0 + + ** + + ** The "Global Color Table" flag indicates if the file has a global + + ** color table. If so, it'll immediately follow the Logical Screen + + ** Block in the file. Bit 7 = 0 = no global color table. Bit 7 = 1 = + + ** there is a global color table. + + ** + + ** This GIF translator can read global color tables, but the + + ** writer does not generate them. The writer uses local color + + ** tables instead. + + ** + + ** The "Color Resolution" flag gives the number of bits per primary color, + + ** minus 1. A value of 7 means 8-bits per primary and is typical. + + ** + + ** This GIF translator can read color table resolutions up to + + ** 8-bits per channel and converts them internally to ImClt's + + ** with 8-bits per channel. GIF files written by this + + ** translator are always 8-bits per channel. + + ** + + ** The "Sort" flag indicates if the global color table (if there is one) + + ** is sorted from most to least important color. Bit 3 = 0 = unsorted. + + ** Bit 3 = 1 = sorted. + + ** + + ** This GIF translator ignores color table sorting on reading. + + ** On writing, the color table is always unsorted. + + ** + + ** The "Global Color Table Size" field is used to calculate the length of + + ** the global color table. Take 2 and raise it to a power equal to this + + ** field plus 1. So, a value of 7 for the field gives 2^(7+1) entries, + + ** or 2^8 = 256 entries. Even when there is no global color table, this + + ** field should be set to the size of a typical color table to help + + ** future display routines. + + ** + + ** This GIF translator uses this field on reading to determine + + ** the global color table size, and sets this field on writing + + ** to an appropriate value. + + ** + + ** The "Background Color Index" field is an index into the global + + ** color table (if the table is present) and selects the color to use + + ** for background pixels not covered by image data from the GIF file. + + ** If there is no global color table, this field should be 0. + + ** + + ** This GIF translator doesn't use the background color on + + ** reading, and sets it to 0 on writing. + + ** + + ** The "Pixel Aspect Ratio" field gives the aspect ratio of pixels in + + ** the image and is meant to account for non-square pixels on crude + + ** graphics systems. A value of 0 means no aspect ratio information is + + ** provided in the field. + + ** + + ** This GIF translator ignores the pixel aspect ratio on reading, + + ** and sets it to 0 on writing. + + ** + + ** Global Color Table Block + + ** The global color table is a default color table for images in the file + + ** that don't have a local color table associated with them. This block + + ** is only present if the global color table flag is set in the Logical + + ** Screen block above. + + ** + + ** If present, this block will contain a number of bytes equal to: + + ** + + ** 3 * 2^(table size + 1) + + ** + + ** where the "table size" is given in the Logical Screen Block above. + + ** + + ** Color table values are in the order RGB, RGB, RGB, one byte per channel, + + ** from color table entry 0 to the maximum. The maximum number of entries + + ** allowed by the spec is 256. + + ** + + ** This GIF translator handles global color tables on reading + + ** image files, but never generates them on writing. All + + ** written images are provided with local color tables. + + ** + + ** Image Descriptor + + ** The image descriptor describes a raster image in the GIF file. There + + ** will be one image descriptor for each such image, with no upper limit + + ** on the number of images contained within a single GIF file. + + ** + + ** Image descriptions begin with an image descriptor block of the form: + + ** + + ** Field Size + + ** ----- ---- + + ** Separator 1 byte + + ** Left Position 2 bytes, unsigned integer + + ** Top Position 2 bytes, unsigned integer + + ** Width 2 bytes, unsigned integer + + ** Height 2 bytes, unsigned integer + + ** Flags 1 byte + + ** + + ** The image separator marks the beginning of the descriptor and helps + + ** to separate images from each other in a multi-image file. The + + ** separator always contains the hex value "0x2C". + + ** + + ** The image left and top position values are relative to the logical + + ** screen, as defined in the logical screen block. (0,0) is at the + + ** top left of the logical screen. + + ** + + ** This GIF translator does not pay attention to logical screens. + + ** So, image left-top positions within such are ignored. On + + ** writing images, the left and top position values are always + + ** set to 0. + + ** + + ** The width and height fields give the image size in pixels. + + ** + + ** The "Flags" field is a bitmask with the following bit meanings: + + ** + + ** Field Bits + + ** ----- ---- + + ** Local Color Table 7 + + ** Interlace 6 + + ** Sort 5 + + ** Reserved 4,3 + + ** Size of Local CLT 2,1,0 + + ** + + ** The "Local Color Table" flag indicates if there is a local CLT + + ** immediately following the image descriptor. Bit 7 = 1 = there is + + ** a local CLT. Bit 7 = 0 = there isn't. + + ** + + ** This GIF translator handles local color tables with read + + ** images. Written images always have a local CLT if the + + ** VFB to be written had one. Grayscale VFB's without CLTs + + ** are written without local CLTs. + + ** + + ** The "Interlace" flag indicates if the image is interlaced using a + + ** 4-pass interlace pattern. Interlacing is discussed later on in this + + ** comment block. Bit 6 = 1 = the image is interlaced. Bit 6 = 0 = + + ** it isn't interlaced. + + ** + + ** This GIF translator handles interlaced incoming images, + + ** but only writes non-interlaced images. + + ** + + ** The "Sort" flag indicates if the local color table (if there is one) + + ** is sorted from most to least important color. Bit 5 = 0 = unsorted. + + ** Bit 5 = 1 = sorted. + + ** + + ** This GIF translator ignores color table sorting on reading. + + ** On writing, the color table is always unsorted. + + ** + + ** The "Size of Local Color Table" field is used to calculate the length of + + ** the local color table. Take 2 and raise it to a power equal to this + + ** field plus 1. So, a value of 7 for the field gives 2^(7+1) entries, + + ** or 2^8 = 256 entries. If there is no local CLT, this field should be + + ** set to 0. + + ** + + ** This GIF translator uses this field on reading to determine + + ** the local color table size, and sets this field on writing + + ** to an appropriate value. + + ** + + ** Local Color Table Block + + ** If an image has a local color table (see the "Flags" field of the + + ** image descriptor block), then a description of that table immediate + + ** follows the image descriptor for the image. This local CLT overrides + + ** any global CLT for this image only. + + ** + + ** If present, this block will contain a number of bytes equal to: + + ** + + ** 3 * 2^(table size + 1) + + ** + + ** where the "table size" is given in the Image Descriptor Block above. + + ** + + ** Color table values are in the order RGB, RGB, RGB, one byte per channel, + + ** from color table entry 0 to the maximum. The maximum number of entries + + ** allowed by the spec is 256. + + ** + + ** This GIF translator reads local CLTs as needed. On writing + + ** images, local CLTs are generated for VFBs with CLTs. + + ** Grayscale VFBs without CLTs are written without a local CLT. + + ** + + ** Image Data Block + + ** The image data for an image follows immediately after the local CLT + + ** (if any) following the image descriptor for the image. Image data is + + ** always compressed using a Limpel-Ziv & Welch (LZW) compression scheme + + ** described below. + + ** + + ** GIF Trailer + + ** The GIF data stream is terminated with a GIF trailer block of the + + ** form: + + ** + + ** Field Size + + ** ----- ---- + + ** GIF Trailer 1 byte + + ** + + ** The "GIF Trailer" byte always has the hex value "0x3B". + + ** + + ** Extensions + + ** Various extensions are defined by the GIF spec. These extensions + + ** allow the embedding of various display and application-specific + + ** controls. All extensions begin with the following three fields: + + ** + + ** Field Size + + ** ----- ---- + + ** Extension Introducer 1 byte + + ** Extension Label 1 byte + + ** Extension Block Size 1 byte + + ** + + ** The "Extension Introducer" is a flag indicating the start of an + + ** extension block and always has the hex value "0x21". + + ** + + ** The "Extension Label" indicates the type of extension. The 1989 spec + + ** defines the following extension labels: + + ** + + ** Extension Label Meaning + + ** --------------- ------- + + ** 0xF9 Graphic Control Extension + + ** 0xFE Comment Extension + + ** 0x01 Plain Text Extension + + ** 0xFF Application Extension + + ** + + ** The "Extension Block Size" field gives the size, in bytes, of the + + ** extension block. + + ** + + ** This GIF translator ignores all extensions on reading GIF + + ** files, and generates no extensions when writing them. + + ** + + ** Image Data Interlacing + + ** The "Interlace" flag in the "Flags" field of an Image Descriptor + + ** Block indicates whether the following image data is interlaced or + + ** not. The basic idea is to order the scanlines in the image data + + ** so that widely separated scanlines come first, and then we fill in + + ** the ones we missed. This scheme was designed to allow quick previewing + + ** of an incomming GIF file by seeing some of the scanlines, all over + + ** the image, first. Then, if you didn't want to continue reading the + + ** GIF file you could cancel the read and save time by not bother with + + ** reading the rest in. Although this idea was meant for slower technology, + + ** it has proved useful once more, since transmission of data from one + + ** corner of the internet to the other is not instanataneous, and programs + + ** such as netScape display the image as it is received. + + ** + + ** So, we generate interlaced gif's. + + ** + + ** When interlacing is enabled, the image data is grouped into 4 passes: + + ** + + ** Pass Contains + + ** ---- -------- + + ** 1 Every 8th row, starting with row 0 + + ** 2 Every 8th row, starting with row 4 + + ** 3 Every 4th row, starting with row 2 + + ** 4 Every 2nd row, starting with row 1 + + ** + + ** When interlacing is turned off, the image data instead contains + + ** image rows in sequential order from row 0 (top) to the last row. + + **/ + + + + + + + +/* + + * FUNCTION DECLARATIONS + + */ + +#ifdef __STDC__ + +static int imGifVfbRead1(int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb **pVfb, + + unsigned char interlaced,unsigned char codesize ); + +static int imGifVfbRead8(int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb **pVfb, + + unsigned char interlaced, unsigned char codesize ); + +static int imGifNBitsPixel (int ncolors,int *); + +static int imGifRead( int ioType, int fd, FILE *fp,TagTable *flagsTable, TagTable *tagTable); + +static int imGifWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imGifWriteTransparency( int ioType, int fd, FILE* fp, int transValue); + +#else + +static int imGifVfbRead1( ); + +static int imGifVfbRead8( ); + +static int imGifNBitsPixel( ); + + + +static int imGifRead( ); + +static int imGifWrite( ); + +static int imGifWriteTransparency( ); + +#endif + + + + + + + + + +/* + + * FORMAT INFORMATION + + * imGifNames - format's name and aliases + + * imGifReadMap - read attributes + + * imGifWriteMap - write attributes + + * imGifMagicNumber - magic number + + * imGifMagic - list of magic numbers + + * ImFileGifFormat - master format description + + */ + +static char *imGifNames[ ] = { "gif", "giff", NULL }; + +static ImFileFormatReadMap imGifReadMap[ ] = + +{ + + /* in out */ + + /* type,ch,dep, attr. VFB type attr. */ + + { IN,1,1, LZW, IMVFBMONO, 0 }, + + { IN,1,2, LZW, IMVFBINDEX8, 0 }, + + { IN,1,3, LZW, IMVFBINDEX8, 0 }, + + { IN,1,4, LZW, IMVFBINDEX8, 0 }, + + { IN,1,5, LZW, IMVFBINDEX8, 0 }, + + { IN,1,6, LZW, IMVFBINDEX8, 0 }, + + { IN,1,7, LZW, IMVFBINDEX8, 0 }, + + { IN,1,8, LZW, IMVFBINDEX8, 0 }, + + + + { IN,1,1, LZW|C, IMVFBMONO, C }, + + { IN,1,2, LZW|C, IMVFBINDEX8, C }, + + { IN,1,3, LZW|C, IMVFBINDEX8, C }, + + { IN,1,4, LZW|C, IMVFBINDEX8, C }, + + { IN,1,5, LZW|C, IMVFBINDEX8, C }, + + { IN,1,6, LZW|C, IMVFBINDEX8, C }, + + { IN,1,7, LZW|C, IMVFBINDEX8, C }, + + { IN,1,8, LZW|C, IMVFBINDEX8, C }, + + { -1, 0, -1, 0 }, + +}; + +static ImFileFormatWriteMap imGifWriteMap[ ] = + +{ + + /* in out */ + + /* VFB type, attr., type,ch,dep, attr., func */ + + { IMVFBMONO, C|A, IN,1,1, LZW|C|A,imGifWrite }, + + { IMVFBMONO, C, IN,1,1, LZW|C, imGifWrite }, + + { IMVFBINDEX8, C|A, IN,1,8, LZW|C|A,imGifWrite }, + + { IMVFBINDEX8, C, IN,1,8, LZW|C, imGifWrite }, + + { -1, 0, -1, 0, NULL }, + +}; + + + +static unsigned char imGifMagicNumber87[ ] = { 'G', 'I', 'F', '8', '7', 'a' }; + +static unsigned char imGifMagicNumber89[ ] = { 'G', 'I', 'F', '8', '9', 'a' }; + +static ImFileMagic imFileGifMagic[ ] = + +{ + + { 0, 6, imGifMagicNumber87 }, + + { 0, 6, imGifMagicNumber89 }, + + { 0, 0, NULL }, + +}; + + + +ImFileFormat ImFileGifFormat = + +{ + + imGifNames, /* Names */ + + "Graphics Image File", /* Description */ + + "Compuserve", /* Creator */ + + "1- thru 8-bit color index Lempel-Ziv & Welch-compressed single- or\n\ +multi-image files.", /* Read support */ + "1 and 8-bit color index Lempel-Ziv & Welch-compressed single- or\n\ +multi-image files.", /* Write support*/ + + imFileGifMagic, /* Magic #'s */ + + IMMULTI, IMPIPE, /* Read? */ + + IMMULTI, IMPIPE, /* Write? */ + + imGifRead, imGifReadMap, imGifWriteMap, /* Maps */ + +}; + + + + + + + + + + + +/* + + * TYPEDEF & STRUCTURE + + * imGifHeaderInfo - Gif header information + + * imGifHeaderFields - Gif Info description for Bin pkg + + * imGifHeader - Gif header holder + + * + + * DESCRIPTION + + * The imGifHeaderInfo includes the GIF Header block and Logical Screen + + * block fields. + + */ + + + +typedef struct imGifHeaderInfo + +{ + + unsigned char gif_magic[6]; /* GIF signature + version */ + + sdsc_uint16 gif_width; /* Screen width */ + + sdsc_uint16 gif_height; /* Screen height */ + + unsigned char gif_flags; /* Assorted flags */ + + unsigned char gif_background; /* Color index of screen background*/ + + unsigned char gif_aspect; /* Pixel aspect ratio */ + +} imGifHeaderInfo; + + + +static imGifHeaderInfo imGifHeader; /* GIF file header */ + + + +static BinField imGifHeaderFields[ ] = + +{ + + { UCHAR, 1, 6 }, /* gif_magic */ + + { UINT16, 2, 1 }, /* gif_width */ + + { UINT16, 2, 1 }, /* gif_height */ + + { UCHAR, 1, 1 }, /* gif_flags */ + + { UCHAR, 1, 1 }, /* gif_background */ + + { UCHAR, 1, 1 }, /* gif_aspect */ + + { 0, 0, 0 } + +}; + + + + + + + + + + + +/* + + * TYPEDEF & STRUCTURE + + * imGifImDescInfo - Gif image descriptor information + + * imGifImDescFields - imGifImDescInfo description for Bin pkg + + * imGifImDesc - Gif image descriptor holder + + * + + * DESCRIPTION + + * The image descriptor defines the actual placement and extents of the + + * following image within the space defined by screen width and height. + + * Also defined are flags to indicate the presence of a local color + + * lookup map, the pixel display sequence and the #bits per pixel + + * defined for this image. + + * + + */ + + + +typedef struct imGifImDescInfo + +{ + + sdsc_uint16 im_left; /* Start of image from left side*/ + + sdsc_uint16 im_top; /* Start of image from top */ + + sdsc_uint16 im_width; /* Width of the image in pixels */ + + sdsc_uint16 im_height; /* Height of the image in pixels*/ + + unsigned char im_flags; /* Assorted flags */ + + + +}imGifImDescInfo; + + + +static imGifImDescInfo imGifImDesc; /* GIF image descriptor */ + + + +static BinField imGifImDescFields[] = + +{ + + { UINT16, 2, 1 }, /* im_left */ + + { UINT16, 2, 1 }, /* im_top */ + + { UINT16, 2, 1 }, /* im_width */ + + { UINT16, 2, 1 }, /* im_height */ + + { UCHAR, 1, 1 }, /* im_flags */ + + { 0, 0, 0 } + +}; + + + + + + + +/* + + * TYPEDEF + + * imGifControlBlock - Gif Control Block structure + + * + + * DESCRIPTION + + * This is the structure that is contained in an graphics control + + * block. We just use it to set the transparency field. + + */ + +typedef struct imGifControlBlock + +{ + + unsigned char blocksize; /* always 4 */ + + unsigned char flags; /* what's in the block */ + + sdsc_uint16 delay; /* how long to wait for a keypress */ + + unsigned char transparent_color; /* The color that is transparent */ + + unsigned char terminator; /* always 0 */ + +} imGifControlBlock; + + + +static BinField imGifControlBlockFields[] = + +{ + + { UCHAR, 1, 1}, /* blocksize */ + + { UCHAR, 1, 1}, /* flags */ + + { UINT16, 2, 1}, /* delay */ + + { UCHAR, 1, 1}, /* transparent_color */ + + { UCHAR, 1, 1}, /* terminator */ + + { 0, 0, 0 } + +}; + + + + + +/* + + * CONSTANTS + + * IMGIFMAGIC87a - file magic number, version 87a + + * IMGIFMAGIC89a - file magic number, version 89a + + * IMGIFBITPIXEL - mask to read #bits/pixel in the image + + * IMGIFGLOBALCM - mask to read if a global colormap is present + + * IMGIFLOCALCM - mask to read if a local colormap is present + + * IMGIFINTERLACED - mask to read the pixel display sequence used + + * IMGIFTERMINATOR - termination character of a Gif image file + + * IMGIFEXTENSION - Gif extension block introducer character + + * IMGIFSEPARATOR - image separator character + + * IMGIFENDBLOCK - terminates extension blocks + + * IMGIFNCOLORRES - # bits of color resolution + + * IMGIFNBITSPIXEL - # bits per pixel + + * IMGIFBACKGROUND - index of background color + + * IMGIFCODESIZE - initial code size used for lzw algorithm + + * IMGIFNOCOLORMAP - no color lookup table present + + * + + */ + + + +#define IMGIFMAGIC87a "GIF87a" + +#define IMGIFMAGIC89a "GIF89a" + +#define IMGIFBITPIXEL 0x07 + +#define IMGIFGLOBALCM 0x80 + +#define IMGIFLOCALCM 0x80 + +#define IMGIFINTERLACED 0x40 + +#define IMGIFTERMINATOR ';' + +#define IMGIFEXTENSION '!' + +#define IMGIFSEPARATOR ',' + +#define IMGIFENDBLOCK 0 + +#define IMGIFNCOLORRES 8 + +#define IMGIFNBITSPIXEL 8 + +#define IMGIFBACKGROUND 0 + +#define IMGIFCODESIZE IMGIFNBITSPIXEL + +#define IMGIFNOCOLORMAP 0 + +#define IMGIFGRAPHICCONTROL 0xF9 + +#define IMGIFCOMMENT 0xFE + +#define IMGIFPLAINTEXT 0x01 + +#define IMGIFAPPLICATION 0xFF + +#define IMGIFTRANSPARENCY 0x01 /* For graphic control blocks */ + + + + + + + + + + + +/* + + * FUNCTION + + * imGifCltRead - read a color lookup table + + * + + * DESCRIPTION + + * Used for reading either global or local color tables, this routine + + * reads in a a table of nClt colors and stores it into a new ImClt + + * which is then returned. + + */ + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imGifCltRead( int ioType, int fd, FILE *fp, int nClt, ImClt **clt ) + +#else + +imGifCltRead( ioType, fd, fp, nClt, clt ) + + int ioType; /* Type of I/O to do */ + + int fd; /* Descriptor to read from */ + + FILE *fp; /* Pointer to read from */ + + int nClt; /* # of colors to read */ + + ImClt **clt; /* Where to put them */ + +#endif + +{ + + ImCltPtr cptr; /* Color pointer */ + + unsigned char *cltBuffer; /* Color lookup table buffer */ + + unsigned char *colorp; /* Color buffer pointer */ + + int i; /* Counter */ + + + + + + /* + + * Allocate a new ImClt and a temporary buffer to store the + + * the raw incomming data. + + */ + + if ( (*clt = ImCltAlloc(nClt)) == IMCLTNULL) + + { + + ImErrorFatal ( ImQError(), -1, ImErrNo ); + + } + + ImMalloc( cltBuffer, unsigned char *, sizeof( UCHAR ) * nClt * 3 ); + + + + + + /* Read in Red, Green, and Blues. */ + + if ( ImBinRead( ioType, fd, fp, cltBuffer, UCHAR, 1, nClt * 3 ) == -1 ) + + { + + free( (char *)cltBuffer ); + + ImReturnBinError( ); + + } + + + + + + /* And copy them to the ImClt. */ + + cptr = ImCltQFirst( *clt ); + + colorp = cltBuffer; + + for ( i = 0; i < nClt; i++ ) + + { + + ImCltSRed( cptr, *(colorp++) ); + + ImCltSGreen( cptr, *(colorp++) ); + + ImCltSBlue( cptr, *(colorp++) ); + + ImCltSInc( *clt, cptr ); + + } + + free( (char *) cltBuffer ); + + return ( 0 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imGifExtRead - Read the gif extension blocks + + * + + * DESCRIPTION + + * The only extension we pay attention to is image transparency. + + * If we see this, we add it to the data table. + + * + + * Note that we intentionally don't use seek() calls to skip the + + * extension data. This allows us to read from pipes. + + */ + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imGifExtRead( int ioType, int fd, FILE *fp, TagTable* tagTable ) + +#else + +imGifExtRead( ioType, fd, fp, tagTable ) + + int ioType; /* Type of I/O to do */ + + int fd; /* Descriptor to read from */ + + FILE *fp; /* Pointer to read from */ + + TagTable *tagTable; /* tag table */ + +#endif + +{ + + unsigned char c, buf[256]; /* Temp buffer */ + + int transparency=0; /* Is this a transparency block? */ + + imGifControlBlock block; /* structure with transparency */ + + char* tmp; /* temporary string for tagtable */ + + char message[100]; /* buffer for message */ + + + + /* + + * Read in extension label + + */ + + if ( ImBinRead( ioType, fd, fp, &c, UCHAR, 1, 1 ) == -1 ) + + { + + ImReturnBinError(); + + } + + + + /* + + * Make sure extension blocks are valid ones + + */ + + switch( c ) + + { + + case IMGIFGRAPHICCONTROL: + + transparency = 1; + + break; + + case IMGIFCOMMENT: + + case IMGIFPLAINTEXT: + + case IMGIFAPPLICATION: + + break; + + default: + + ImErrNo = IMESYNTAX; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + if (transparency==1) + + { + + if ( ImBinReadStruct( ioType, fd, fp, &block, + + imGifControlBlockFields ) == -1 ) + + { + + ImReturnBinError(); + + } + + /* Put block.transparent_color in tagTable */ + + if (block.flags & IMGIFTRANSPARENCY) + + { + + ImMalloc(tmp, char * , 15 ); + + sprintf(tmp,"index=%d",(int)block.transparent_color); + + TagTableAppend( tagTable, + + TagEntryAlloc( "transparency value", POINTER, &tmp)); + + } + + sprintf(message,"Pixels with index %d.",(int)block.transparent_color); + + ImInfo ("Transparency",message); + + } + + else + + { + + /* + + * Skip over extension data + + */ + + for ( ; ; ) + + { + + /* + + * Read in block size or block terminator + + */ + + if ( ImBinRead( ioType, fd, fp, &c, UCHAR, 1, 1 ) == -1 ) + + { + + ImReturnBinError(); + + } + + /* + + * If block terminator, quit loop + + */ + + if ( c == IMGIFENDBLOCK ) + + break; + + + + /* + + * Skip over block + + */ + + if ( ImBinRead( ioType, fd, fp, buf, UCHAR, 1, c ) == -1 ) + + { + + ImReturnBinError(); + + } + + } + + } + + + + return( 0 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imGifRead - read a gif image file + + * + + * DESCRIPTION + + * The file header is read and the magic number checked. + + * Separate routines are then called to handle different + + * sizes (1 and anything from 2 to 8) + + */ + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imGifRead( int ioType, int fd, FILE *fp,TagTable *flagsTable, TagTable *tagTable) + +#else + +imGifRead( ioType, fd, fp, flagsTable, tagTable ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + +#endif + +{ + + + + ImVfb *vfb; /* Read in image */ + + int nGClt,nLClt; /* Number of Global and Local */ + + /* CLT entries */ + + ImClt *gClt, *lClt; /* Read in colormaps */ + + unsigned char c; /* aux var */ + + int end; /* end to reading images */ + + int status; /* Return status */ + + int imageCount = 1;/* Number of images processed */ + + char message[100]; /* ImInfo message */ + + int nbits; /* # of bits per pixel */ + + + + + + /* + + * Set the Binary I/O package's byte order + + */ + + BinByteOrder( BINLBF ); + + + + /* + + * Read GIF header + + */ + + if ( ImBinReadStruct( ioType, fd, fp, &imGifHeader, + + imGifHeaderFields )== -1) + + { + + ImReturnBinError( ); + + } + + + + /* + + * Check for valid Signature and version (treated as magic + + * numbers). Always 6 bytes long. + + */ + + if( strncmp( (char*)imGifHeader.gif_magic, IMGIFMAGIC87a, 6 ) == 0 ) + + { + + ImInfo( "Version", "GIF87a" ); + + } + + else if ( strncmp( (char*)imGifHeader.gif_magic, IMGIFMAGIC89a, 6 ) == 0 ) + + { + + ImInfo( "Version", "GIF89a" ); + + } + + else + + { + + ImErrNo = IMEMAGIC; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + ImInfo( "Byte Order", "Least Significant Byte First" ); + + + + + + /* + + * Read the Global Color Map if it exists + + */ + + gClt = IMCLTNULL; + + lClt = IMCLTNULL; + + if ( (imGifHeader.gif_flags & IMGIFGLOBALCM) == IMGIFGLOBALCM ) + + { + + /* + + * Compute the size of the GCLT and read it in. + + */ + + nGClt = 2 << ( imGifHeader.gif_flags & IMGIFBITPIXEL ); + + if ( imGifCltRead( ioType, fd, fp, nGClt, &gClt ) == -1 ) + + return ( -1 ); /* Error already handled */ + + sprintf( message, "%d Entries", nGClt ); + + ImInfo( "Global Color Table", message ); + + } + + else + + { + + ImInfo( "Global Color Table", "none" ); + + } + + + + /* + + * Starts to read the images following + + */ + + end = 0; + + while(!end) + + { + + if ( ImBinRead(ioType,fd,fp,&c,UCHAR,1,1 ) == -1 ) + + { + + ImReturnBinError(); + + } + + switch(c) + + { + + case IMGIFSEPARATOR: /* Image separator character */ + + + + /* + + * Read the gif image descriptor + + */ + + + + if ( ImBinReadStruct( ioType, fd, fp, &imGifImDesc, + + imGifImDescFields ) == -1 ) + + { + + ImReturnBinError(); + + } + + + + /* + + * Output -verbose messages + + */ + + sprintf( message, "%d of ?", imageCount++ ); + + ImInfo( "Image", message ); + + + + sprintf( message, "%d x %d", imGifImDesc.im_width, + + imGifImDesc.im_height ); + + ImInfo( "Resolution", message ); + + + + if ( (imGifImDesc.im_flags & IMGIFLOCALCM) == IMGIFLOCALCM) + + { + + nbits = (imGifImDesc.im_flags & IMGIFBITPIXEL) + 1; + + sprintf( message, "%d-bit Color Indexed", nbits ); + + ImInfo( "Type", message ); + + } + + else + + { + + /* No local color table. */ + + nbits = (imGifHeader.gif_flags&IMGIFBITPIXEL) + 1; + + if ( gClt == NULL && nbits == 1 ) + + { + + ImInfo( "Type", "1-bit Monochrome" ); + + } + + else if ( gClt == NULL ) + + { + + sprintf( message, "%d-bit Grayscale", nbits ); + + ImInfo( "Type", message ); + + } + + else + + { + + sprintf( message, "%d-bit Color Indexed",nbits); + + ImInfo( "Type", message ); + + } + + } + + + + /* + + * Read the local color map if it exists + + */ + + + + if ( (imGifImDesc.im_flags & IMGIFLOCALCM) == IMGIFLOCALCM) + + { + + nLClt = 2 << ( imGifImDesc.im_flags & IMGIFBITPIXEL ); + + if ( imGifCltRead(ioType,fd,fp,nLClt,&lClt) == -1 ) + + return ( -1 ); + + sprintf( message, "%d Entries", nLClt ); + + ImInfo( "Local Color Table", message ); + + } + + else + + { + + ImInfo( "Local Color Table", "none" ); + + } + + + + ImInfo( "Compression Type", "Lempel-Ziv and Welch (LZW)"); + + + + /* + + * Read the initial code size + + */ + + + + if(ImBinRead(ioType,fd,fp,&c, UCHAR, 1, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + /* + + * Read the raster data into the vfb + + */ + + + + /* + + * check the #bits/pixel + + */ + + + + if ( nbits == 1 ) + + status = imGifVfbRead1(ioType, fd, fp, flagsTable, + + tagTable, &vfb, + + (imGifImDesc.im_flags & IMGIFINTERLACED),c); + + else + + status = imGifVfbRead8( ioType, fd, fp, flagsTable, + + tagTable, &vfb, + + (imGifImDesc.im_flags & IMGIFINTERLACED),c); + + if ( status == -1 ) + + return ( -1 ); /* Error already handled*/ + + + + + + /* + + * Set the color lookup table + + */ + + + + + + /* + + * if a local color map is present then it is assigned + + * to the vfb. if not, then the global color table + + * is used. + + */ + + + + if (((imGifImDesc.im_flags & IMGIFLOCALCM) == IMGIFLOCALCM ) + + && ( lClt != IMCLTNULL) ) + + { + + ImVfbSClt( vfb, lClt ); + + TagTableAppend( tagTable, + + TagEntryAlloc( "image clt", POINTER, &lClt)); + + } + + else if (gClt != IMCLTNULL) + + { + + ImVfbSClt( vfb, gClt ); + + TagTableAppend( tagTable, + + TagEntryAlloc( "image clt", POINTER, &gClt)); + + } + + TagTableAppend( tagTable, + + TagEntryAlloc( "image vfb", POINTER, &vfb)); + + break; + + + + case IMGIFEXTENSION: /* Gif extension block introducer */ + + if ( imGifExtRead(ioType,fd,fp, tagTable) == -1 ) + + return ( -1 ); /* Error already handled*/ + + break; + + + + case IMGIFTERMINATOR: /* GIf terminator */ + + end = 1; + + break; + + } + + } + + return ( 1 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imGifVfbRead8 - read 2-8 bit Gif image + + * + + * DESCRIPTION + + * A new VFB is allocated. The image is read in, pixel by pixel, + + * using the LZW algorithm, into the VFB. The order of the + + * scanlines is given by the value of interlaced. If it is + + * equal to IMGIFINTERLACED means that the scanlines are + + * organized in an interlaced way. If not, they are written + + * sequentially. + + */ + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imGifVfbRead8(int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb **pVfb, + + unsigned char interlaced, unsigned char codesize ) + +#else + +imGifVfbRead8(ioType, fd, fp, flagsTable, tagTable, pVfb, interlaced, codesize ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + + ImVfb **pVfb; /* VFB to fill and return */ + + unsigned char interlaced; /* tells if the image is inter */ + + unsigned char codesize; /* Inital code size used by LWZ */ + + /* laced */ + +#endif + +{ + + unsigned char index; /* color index */ + + ImVfb *vfb; /* New vfb */ + + ImVfbPtr pptr; /* Pixel pointer */ + + int column; /* Column pixel into the image */ + + int row; /* Row pixel into the image */ + + int pass; /* used for interlaced image */ + + int value; /* pixel value returned by LWZ */ + + int size; /* size of image in pixels */ + + int cont; /* counter */ + + + + + + + + /* + + * Allocate a VFB of the required size. + + */ + + if ( (*pVfb = ImVfbAlloc( imGifImDesc.im_width, imGifImDesc.im_height, + + IMVFBINDEX8 )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + vfb = *pVfb; + + + + /* initializing lzw structures */ + + if ( imLzwReadByte(ioType, fd, fp, TRUE, codesize ) < 0 ) + + return( 0 ); + + + + size = imGifImDesc.im_width * imGifImDesc.im_height; + + pptr = ImVfbQFirst( vfb ); + + cont = 0; + + if (interlaced != IMGIFINTERLACED) + + { + + while ((cont < size) && + + ((value = imLzwReadByte(ioType, fd, fp, FALSE, codesize)) >= 0) ) + + { + + index = value; + + ImVfbSIndex8( vfb, pptr, index); + + ImVfbSInc( vfb, pptr ); + + cont++; + + } + + } + + else + + { + + column = row = pass = 0; + + while ((cont < size) && + + ((value = imLzwReadByte(ioType, fd, fp, FALSE, codesize)) >= 0 )) + + { + + pptr = ImVfbQPtr( vfb, column,row ); + + index = value; + + ImVfbSIndex8( vfb, pptr, index); + + cont++; + + if (++column == imGifImDesc.im_width) + + { + + column = 0; + + switch (pass) { + + case 0: + + case 1: + + row+= 8; + + break; + + case 2: + + row+= 4; + + break; + + case 3: + + row+= 2; + + break; + + } + + if (row >= imGifImDesc.im_height ) + + switch(++pass) + + { + + case 1: + + row = 4; + + break; + + case 2: + + row = 2; + + break; + + case 3: + + row = 1; + + break; + + } + + } + + } + + } + + return 1; + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imGifVfbRead1 - read 1-bit Gif image + + * + + * DESCRIPTION + + * A new VFB is allocated. The image is read in, one scanline at + + * a time, and converted into the VFB. + + * + + */ + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imGifVfbRead1(int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb **pVfb, + + unsigned char interlaced,unsigned char codesize ) + +#else + +imGifVfbRead1(ioType, fd, fp, flagsTable, tagTable, pVfb, interlaced,codesize ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + + ImVfb **pVfb; /* VFB to fill and return */ + + unsigned char interlaced; /* tells if the image is inter */ + + unsigned char codesize; /* Initial code size used by LWZ*/ + + /* laced */ + +#endif + +{ + + ImVfb *vfb; /* New vfb */ + + ImVfbPtr pptr; /* Pixel pointer */ + + int column; /* Column pixel into the image */ + + int row; /* Row pixel into the image */ + + int pass; /* used for interlaced image */ + + int value; /* pixel value returned by LWZ */ + + int size; /* size of image in pixels */ + + int cont; + + + + + + + + /* + + * Allocate a VFB of the required size. + + */ + + if ( (*pVfb = ImVfbAlloc( imGifImDesc.im_width, imGifImDesc.im_height, + + IMVFBMONO )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + vfb = *pVfb; + + + + /* initializing lzw structures */ + + if ( imLzwReadByte(ioType, fd, fp, TRUE, (int) codesize ) < 0 ) + + return( 0 ); + + + + size = imGifImDesc.im_width * imGifImDesc.im_height; + + pptr = ImVfbQFirst( vfb ); + + cont = 0; + + if (interlaced != IMGIFINTERLACED) + + { + + while ((cont < size) && + + ((value = imLzwReadByte(ioType, fd, fp, FALSE, codesize)) >= 0) ) + + { + + ImVfbSMono( vfb, pptr, (value) ? 1 : 0); + + ImVfbSInc( vfb, pptr ); + + cont++; + + } + + } + + else + + { + + column = row = pass = 0; + + while ((cont < size) && + + ((value = imLzwReadByte(ioType, fd, fp, FALSE, codesize)) >= 0)) + + { + + pptr = ImVfbQPtr( vfb, column,row ); + + ImVfbSMono ( vfb, pptr, (value) ? 1 : 0); + + cont++; + + if (++column == imGifImDesc.im_width) + + { + + column = 0; + + switch (pass) { + + case 0: + + case 1: + + row+= 8; + + break; + + case 2: + + row+= 4; + + break; + + case 3: + + row+= 2; + + break; + + } + + if (row >= imGifImDesc.im_height ) + + switch(++pass) + + { + + case 1: + + row = 4; + + break; + + case 2: + + row = 2; + + break; + + case 3: + + row = 1; + + break; + + } + + } + + } + + } + + return 1; + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imGifCltWrite - Write a color lookup table + + * + + * DESCRIPTION + + * Write the color lockup table clt into the file + + */ + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imGifCltWrite(int ioType, int fd, FILE *fp, int nClt,int newClt, ImClt *clt) + +#else + +imGifCltWrite(ioType, fd, fp, nClt,newClt, clt) + + int ioType; + + int fd; + + FILE *fp; + + int nClt; + + int newClt; + + ImClt *clt; + +#endif + +{ + + ImCltPtr cptr; /* Color pointer */ + + unsigned char *cltBuffer; /* Color lookup table buffer */ + + unsigned char *colorp; /* Color buffer pointer */ + + int i; + + + + ImMalloc( cltBuffer, unsigned char *, sizeof( unsigned char ) * newClt * 3 ); + + + + colorp = cltBuffer; + + cptr = ImCltQFirst( clt ); + + + + for (i=0; i< nClt; i++) + + { + + *(colorp++) = ImCltQRed ( cptr ); + + *(colorp++) = ImCltQGreen( cptr ); + + *(colorp++) = ImCltQBlue ( cptr ); + + ImCltSInc( clt, cptr ); + + } + + + + + + if ( ImBinWrite( ioType, fd, fp, cltBuffer, UCHAR, 1, newClt * 3 ) == -1) + + { + + free( (unsigned char *)cltBuffer ); + + ImReturnBinError( ); + + } + + return 1; + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imGifVfbWrite8 - Write an 8-bit vfb into a gif file + + * + + * DESCRIPTION + + */ + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imGifVfbWrite8( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb *vfb, + + unsigned char interlaced ) + +#else + +imGifVfbWrite8( ioType, fd, fp, flagsTable, tagTable, vfb, interlaced ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + + ImVfb *vfb; /* VFB to write */ + + unsigned char interlaced; /* tells if the image is inter */ + + /* laced */ + +#endif + +{ + + unsigned char *rasterimage; /* image pixel values */ + + int i; + + int size; /* size of the image */ + + int pass; + + int row,column; + + ImVfbPtr ptr; + + unsigned char c; + + + + + + + + /* + + * Write an Image separator + + */ + + + + c = IMGIFSEPARATOR; + + if ( ImBinWrite( ioType, fd, fp, &c, UCHAR, 1, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + /* + + * Setup the Image Descriptor and write it out + + */ + + + + imGifImDesc.im_left = imGifImDesc.im_top = 0; + + imGifImDesc.im_width = ImVfbQWidth ( vfb ); + + imGifImDesc.im_height = ImVfbQHeight ( vfb ); + + imGifImDesc.im_flags = interlaced; + + + + if ( ImBinWriteStruct( ioType, fd, fp, &imGifImDesc, + + imGifImDescFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + + + /* + + * Write out the initial code size before + + * start to compress the raster data + + */ + + + + c = IMGIFCODESIZE; + + if ( ImBinWrite( ioType, fd, fp, &c, UCHAR, 1, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + /* + + * Allocate a buffer to store the image pixel values + + * to be compressed + + */ + + + + size = imGifImDesc.im_width * imGifImDesc.im_height; + + ImMalloc( rasterimage, unsigned char *, size ); + + if ( rasterimage == NULL ) + + { + + ImErrNo = IMEMALLOC; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + /* + + * Fill the buffer with the pixel values + + */ + + + + ptr = ImVfbQFirst(vfb); + + if (interlaced != IMGIFINTERLACED) + + for (i=0; i< size; i++) + + { + + rasterimage[i] = ImVfbQIndex8(vfb,ptr); + + ImVfbSInc(vfb,ptr); + + } + + else + + { + + column = row = pass = 0; + + for (i=0; i< size; i++) + + { + + ptr = ImVfbQPtr( vfb, column,row ); + + rasterimage[i] = ImVfbQIndex8(vfb,ptr); + + if (++column == imGifImDesc.im_width) + + { + + column = 0; + + switch (pass) { + + case 0: + + case 1: + + row+= 8; + + break; + + case 2: + + row+= 4; + + break; + + case 3: + + row+= 2; + + break; + + } + + if (row >= imGifImDesc.im_height ) + + switch(++pass) + + { + + case 1: + + row = 4; + + break; + + + + case 2: + + row = 2; + + break; + + case 3: + + row = 1; + + break; + + } + + } + + } + + } + + + + /* + + * Compress the raster data + + */ + + + + + + imLzwCompGif( ioType, fd, fp, IMGIFCODESIZE + 1, rasterimage, size); + + + + free ((unsigned char *) rasterimage); + + + + /* + + * Write out a Zero-length packet + + */ + + + + + + c = 0; + + if ( ImBinWrite( ioType, fd, fp, &c, UCHAR, 1, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + /* + + * Write the Gif terminator + + */ + + + + c = IMGIFTERMINATOR; + + if ( ImBinWrite( ioType, fd, fp, &c, UCHAR, 1, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + return(1); + +} + + + +/* + + * FUNCTION + + * imGifVfbWrite1 - Write an 1-bit vfb into a gif file + + * + + */ + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imGifVfbWrite1( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb *vfb, + + unsigned char interlaced ) + +#else + +imGifVfbWrite1( ioType, fd, fp, flagsTable, tagTable, vfb, interlaced ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + + ImVfb *vfb; /* VFB to write */ + + unsigned char interlaced; /* tells if the image is inter */ + + /* laced */ + +#endif + +{ + + unsigned char *rasterimage; /* image pixel values */ + + int i; /* counter */ + + int size; /* size of the image */ + + int pass; /* pass number */ + + int row,column; /* pixel row and pixel column */ + + ImVfbPtr ptr; /* pixel pointer */ + + unsigned char c; /* auxiliar var */ + + + + + + + + + + /* + + * Write an Image separator + + */ + + + + c = IMGIFSEPARATOR; + + if ( ImBinWrite( ioType, fd, fp, &c, UCHAR, 1, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + /* + + * Setup the Image Descriptor and write it out + + */ + + + + imGifImDesc.im_left = imGifImDesc.im_top = 0; + + imGifImDesc.im_width = ImVfbQWidth ( vfb ); + + imGifImDesc.im_height = ImVfbQHeight ( vfb ); + + imGifImDesc.im_flags = interlaced; + + + + if ( ImBinWriteStruct( ioType, fd, fp, &imGifImDesc, + + imGifImDescFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + + + /* + + * Write out the initial code size before + + * start to compress the raster data. When + + * the number of bits per pixel is 1 the + + * initial code size used for LWZ algorithm is + + * equal to 2. + + */ + + + + c = 2; + + + + if ( ImBinWrite( ioType, fd, fp, &c, UCHAR, 1, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + /* + + * Allocate a buffer to store the image pixel values + + * to be compressed + + */ + + + + size = imGifImDesc.im_width * imGifImDesc.im_height; + + ImMalloc( rasterimage, unsigned char *, size ); + + if ( rasterimage == NULL ) + + { + + ImErrNo = IMEMALLOC; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + /* + + * Fill the buffer with the pixel values + + */ + + + + + + ptr = ImVfbQFirst(vfb); + + if (interlaced != IMGIFINTERLACED) + + for (i=0; i< size; i++) + + { + + rasterimage[i] = (ImVfbQMono(vfb,ptr)? 1 : 0); + + ImVfbSInc(vfb,ptr); + + } + + else + + { + + column = row = pass = 0; + + for (i=0; i< size; i++) + + { + + ptr = ImVfbQPtr( vfb, column,row ); + + rasterimage[i] = (ImVfbQMono(vfb,ptr)? 1 : 0); + + if (++column == imGifImDesc.im_width) + + { + + column = 0; + + switch (pass) { + + case 0: + + case 1: + + row+= 8; + + break; + + case 2: + + row+= 4; + + break; + + case 3: + + row+= 2; + + break; + + } + + if (row >= imGifImDesc.im_height ) + + switch(++pass) + + { + + case 1: + + row = 4; + + break; + + + + case 2: + + row = 2; + + break; + + case 3: + + row = 1; + + break; + + } + + } + + } + + } + + /* + + * Compress the raster data + + */ + + + + + + /* + + * 3 = initial code size + 1 + + * the initial code size is always 2 for + + * one-bit images + + */ + + + + imLzwCompGif( ioType, fd, fp, 3, rasterimage, size); + + + + free ((unsigned char *) rasterimage); + + + + /* + + * Write out a Zero-length packet + + */ + + + + + + c = 0; + + if ( ImBinWrite( ioType, fd, fp, &c, UCHAR, 1, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + /* + + * Write the Gif terminator + + */ + + + + c = IMGIFTERMINATOR; + + if ( ImBinWrite( ioType, fd, fp, &c, UCHAR, 1, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + + + return(1); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imGifWrite - write a vfb image into gif image format + + * + + * DESCRIPTION + + */ + + + +static int /* Returns # of tags written */ + +#ifdef __STDC__ + +imGifWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imGifWrite( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + TagTable *flagsTable; /* Format Flags */ + + TagTable *tagTable; /* Tag list to add to */ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + int nClt; /* Number of CLT entries */ + + ImClt *clt; /* Read in colormap */ + + int newClt; /* Number of CLT entries rounded to a power of two */ + + int nbitspixel; /* number of bits per pixel */ + + char message[100]; /* ImInfo message */ + + int transparency; /* transparency value */ + + + + + + + + ImInfo( "Version", "GIF89a" ); + + ImInfo( "Byte Order", "Least Significant Byte First" ); + + + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + + + /* + + * Set up the header and write it out + + */ + + strncpy((char*)imGifHeader.gif_magic,IMGIFMAGIC89a,6); + + imGifHeader.gif_width = ImVfbQWidth ( vfb ); + + imGifHeader.gif_height = ImVfbQHeight( vfb ); + + + + /* + + * Decide how to write out the GIF file. + + */ + + if (pMap->map_outAttributes & IMCLTYES) + + { + + /* + + * A CLT should be written out. Higher level code will + + * have insured that it has a CLT. Figure out how many + + * bits per pixel based upon the CLT size. + + */ + + clt = ImVfbQClt( vfb ); + + nClt= ImCltQNColors( clt ); + + nbitspixel = imGifNBitsPixel(nClt,&newClt); + + imGifHeader.gif_flags = IMGIFGLOBALCM; + + imGifHeader.gif_flags|= (IMGIFNCOLORRES - 1) << 4; + + } + + else + + { + + /* + + * No CLT to write out. Could be grayscale or monochrome. + + */ + + if ( ImVfbQFields( vfb ) & IMVFBINDEX8 ) + + nbitspixel = 8; + + else + + nbitspixel = 1; + + imGifHeader.gif_flags = IMGIFNOCOLORMAP; + + } + + imGifHeader.gif_flags |= (nbitspixel-1); + + imGifHeader.gif_background = IMGIFBACKGROUND; + + imGifHeader.gif_aspect = + + ( ImVfbQWidth( vfb ) / ImVfbQHeight( vfb ) + 15 ) / 64; + + + + BinByteOrder( BINLBF ); + + + + if ( ImBinWriteStruct( ioType, fd, fp, &imGifHeader, + + imGifHeaderFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + + + + + /* + + * Write the CLT if exists + + */ + + if (pMap->map_outAttributes & IMCLTYES) + + { + + if ( imGifCltWrite(ioType, fd, fp, nClt, newClt, clt) == -1 ) + + return ( -1 ); + + sprintf( message, "%d Entries", nClt ); + + ImInfo( "Global Color Table", message ); + + } + + else + + ImInfo( "Global Color Table", "none" ); + + + + ImInfo( "Image", "1 of 1" ); + + + + sprintf( message, "%d x %d", imGifHeader.gif_width, + + imGifHeader.gif_height ); + + ImInfo( "Resolution", message ); + + + + /* + + * If there is a request for transparency or if + + * there's transparency in the tagTable, add it to + + * the image. + + */ + + + + if ((transparency=ImGetTransparency(tagTable, flagsTable, vfb))!=-1) + + { + + if (imGifWriteTransparency( ioType, fd, fp, transparency)==-1) + + { + + return -1; + + } + + } + + + + /* query the vfb and call the appropiate routine */ + + if ((ImVfbQFields( vfb ) & IMVFBMONO )) + + { + + ImInfo( "Type", "1-bit Monochrome" ); + + ImInfo( "Local Color Table", "none" ); + + ImInfo( "Compression Type", "Lempel-Ziv and Welch (LZW)" ); + + + + return(imGifVfbWrite1(ioType,fd,fp,flagsTable,tagTable, + + vfb,IMGIFINTERLACED)); + + } + + else + + { + + if ( pMap->map_outAttributes & IMCLTYES ) + + sprintf( message, "%d-bit Color Indexed", nbitspixel ); + + else + + sprintf( message, "%d-bit Grayscale", nbitspixel ); + + ImInfo( "Type", message ); + + ImInfo( "Local Color Table", "none" ); + + ImInfo( "Compression Type", "Lempel-Ziv and Welch (LZW)" ); + + + + return(imGifVfbWrite8(ioType,fd,fp,flagsTable,tagTable, + + vfb,IMGIFINTERLACED)); + + } + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imGifNBitsPixel - compute # of bits to represent a color + + * + + * DESCRIPTION + + * Rounds the number of colors to the closest power + + * of two and return the necesary number of bits to + + * represent this number of colors. + + */ + + + +static int /* Returns # of bits */ + +#ifdef __STDC__ + +imGifNBitsPixel (int ncolors,int *newNumber) + +#else + +imGifNBitsPixel (ncolors,newNumber) + + int ncolors; /* number of colors */ + + int *newNumber; /* new number of colors */ + +#endif + +{ + + int nbits; + + + + *newNumber = 2; + + nbits = 1; + + if ( ncolors > 256) + + { + + nbits = 8; + + *newNumber = 256; + + } + + else + + { + + while (*newNumber < ncolors) + + { + + (*newNumber) <<= 1; + + nbits++; + + } + + } + + return(nbits); + +} + + + + + +/* + + * FUNCTION + + * imGifWriteTransparency + + * + + * DESCRIPTION + + * Write out the color which is transparent in the image + + * as specified by the flags table. (As a default, write out + + * the most popular color in the image.) + + * + + * If the tagEntry parameter specifies an index value, use that. + + * + + * If the tagEntry parameter specifes an rgb value, use the most + + * common index value with that rgb value. + + * + + * If the tagEntry parameter speciefies "most common", use the + + * most common index value in the image. + + * + + * + + */ + +static int /* returns status */ + +#ifdef __STDC__ + +imGifWriteTransparency( int ioType, int fd, FILE* fp, int transparency) + +#else + +imGifWriteTransparency( ioType, fd, fp, transparency) + +int ioType; + +int fd; + +FILE* fp; + +int transparency; + +#endif + +{ + + imGifControlBlock controlBlock; /* data */ + + unsigned char blockId; /* id for block */ + + char message[1000]; /*message buffer */ + + + + /* Specify that this is an extension */ + + blockId = IMGIFEXTENSION; + + if ( ImBinWrite( ioType, fd, fp, &blockId, UCHAR, 1, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + /* Specify what type of extension this is */ + + blockId = IMGIFGRAPHICCONTROL; + + if ( ImBinWrite( ioType, fd, fp, &blockId, UCHAR, 1, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + sprintf(message,"Pixels with index %d.",transparency); + + ImInfo("Transparency",message); + + + + controlBlock.blocksize = 0x04; + + controlBlock.flags = 0x01; + + controlBlock.delay = 0x00; + + controlBlock.transparent_color = (unsigned char)transparency; + + controlBlock.terminator = 0x00; + + + + if ( ImBinWriteStruct( ioType, fd, fp, &controlBlock, + + imGifControlBlockFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + + + return 1; + +} + + + + + diff --git a/utils/roq2/libim/imgiflzw.c b/utils/roq2/libim/imgiflzw.c new file mode 100644 index 0000000..1e2e97d --- /dev/null +++ b/utils/roq2/libim/imgiflzw.c @@ -0,0 +1,1492 @@ +/** + + ** $Header: /roq/libim/imgiflzw.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/imgiflzw.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** imgiflzw.c - Do Lempel-Ziv & Welch uncompress/compress + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** imlzw.c contains rountines for decoding and encoding bytes + + ** using Lempel-Ziv & Welch uncompress/compress algorithm. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** imLzwCompGif f + + ** + + ** PRIVATE CONTENTS + + ** + + ** imLzwGetCode f + + ** imLzwError f + + ** imLzwGifNextPixel f + + ** imLzwPackBits f + + ** + + ** struct str_table_node t + + ** struct str_table_node t + + ** strTableNode t + + ** strTableNodePtr t + + ** strTable t + + ** + + ** + + ** IMMAX_LWZ_BITS d maximum number of bits allowed by LWZ + + ** IMMAXIMUMCODE d maximum code size + + ** IMBLOCKSIZE d maximum block byte count plus one + + ** IMNULLPREFIX d null prefix + + ** + + ** ImGifEasyFail m non fatal error ocurred. report the error. + + ** + + ** CODE CREDITS + + ** David Koblas, 1990. + + ** Custom development, Soraya Gonzales, Intevep S.A., Venzuela, 1991. + + ** Extensions, Vinh Le, San Diego Supercomputer Center, 1992. + + ** Extensions, Dave Nadeau, San Diego Supercomputer Center, 1993. + + ** + + ** + + ** HISTORY + + ** $Log: /roq/libim/imgiflzw.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.11 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.10 1995/06/16 08:43:52 bduggan + + ** cleaned up code formatting + + ** + + ** Revision 1.9 1995/01/10 23:25:55 bduggan + + ** Updated copyright notice + + ** + + ** Revision 1.8 1994/10/03 11:29:39 nadeau + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Updated comments. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.7 92/08/31 17:24:09 vle + + ** Updated copyright notice. + + ** + + ** Revision 1.6 91/10/03 09:03:23 nadeau + + ** Fixed #includes. + + ** + + ** Revision 1.5 91/03/21 08:51:38 nadeau + + ** ifdef-ed out inclusion of strings.h. Nothing seems + + ** to need it and it confuses the compiler on SysV vs. + + ** BSD machines (string.h vs. strings.h). + + ** + + ** Revision 1.4 91/03/13 17:11:53 soraya + + ** *** empty log message *** + + ** + + ** Revision 1.3 91/03/11 09:19:07 soraya + + ** Optimization and comment cleanup. + + ** + + ** Revision 1.2 91/01/31 08:34:42 soraya + + ** Adding comments + + ** + + ** + + ** + + **/ + + + +#if 0 + +The lines between the dashes are taken directly from the file jrdgif.c: + +----------------------------------------------------------------------------- + +/* + + * jrdgif.c + + * + + * Copyright (C) 1991, 1992, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains routines to read input images in GIF format. + + * + + * These routines may need modification for non-Unix environments or + + * specialized applications. As they stand, they assume input from + + * an ordinary stdio stream. They further assume that reading begins + + * at the start of the file; input_init may need work if the + + * user interface has already read some data (e.g., to determine that + + * the file is indeed GIF format). + + * + + * These routines are invoked via the methods get_input_row + + * and input_init/term. + + */ + + + +/* + + * This code is loosely based on giftoppm from the PBMPLUS distribution + + * of Feb. 1991. That file contains the following copyright notice: + + * +-------------------------------------------------------------------+ + + * | Copyright 1990, David Koblas. | + + * | Permission to use, copy, modify, and distribute this software | + + * | and its documentation for any purpose and without fee is hereby | + + * | granted, provided that the above copyright notice appear in all | + + * | copies and that both that copyright notice and this permission | + + * | notice appear in supporting documentation. This software is | + + * | provided "as is" without express or implied warranty. | + + * +-------------------------------------------------------------------+ + + * + + * We are also required to state that + + * "The Graphics Interchange Format(c) is the Copyright property of + + * CompuServe Incorporated. GIF(sm) is a Service Mark property of + + * CompuServe Incorporated." + + */ + +----------------------------------------------------------------------------- + + + +#endif + + + +/* + + * imLzwGetCode + + * imLzwReadByte + + * + + * Gif Image decompression - from LZW to Gif + + * Written by David Koblas. + + * Copyright 1989 + + * Modified by Soraya Gonzalez + + * San Diego Supercomputer Center + + * to include it in the image library + + * ================================================================== + + * imLzwError + + * imLzwGifNextPixel + + * imLzwPackBits + + * imLzwCompGif + + * + + * + + * GIF Image compression - LZW algorithm implemented with Trie type + + * structure. + + * Written by Bailey Brown, Jr. + + * last change May 24, 1990 + + * Modified by Soraya Gonzalez. + + * San Diego Supercomputer Center. + + * December 1990 + + * to include it in the image library + + * + + * + + */ + + + + + +#ifdef USE_STDLIB_H + +#include + +#else + + + +#ifdef old + +#ifdef USE_STRING_H + +#include + +#else + +#include + +#endif + +#endif + + + + + +#endif /* USE_STDLIB_H */ + + + +#include "iminternal.h" + + + + + + + + + +/* + + * TYPEDEF & STRUCTURE + + * struct str_table_node + + * struct str_table_node + + * strTableNode + + * strTableNodePtr + + * strTable + + * DESCRIPTION + + * + + */ + + + +typedef struct str_table_entry { + + int code; + + int prefix; + + int suffix; + +} strTableEntry; + + + +typedef struct str_table_node { + + strTableEntry entry; + + struct str_table_node *left; + + struct str_table_node *right; + + struct str_table_node *children; + +} strTableNode, *strTableNodePtr, **strTable; + + + +/* + + * CONSTANTS + + * + + * IMMAX_LWZ_BITS - maximum number of bits allowed by LWZ + + * IMMAXIMUMCODE - maximum code size + + * IMBLOCKSIZE - maximum block byte count plus one + + * IMNULLPREFIX - null prefix + + */ + + + +#define IMMAX_LWZ_BITS 12 + +#define IMMAXIMUMCODE 4095 /* 2**maximum_code_size */ + +#define IMBLOCKSIZE 256 /* max block byte count + 1 */ + +#define IMNULLPREFIX -1 + + + + + +/* + + * MACROS + + * ImGifEasyFail - non fatal error occurred. report the error. + + */ + + + +#define ImGifEasyFail(str,status) \ + { \ + ImErrorWarning(str,status,IMEUNKNOWN); \ + return(status); \ + } + + + +/* + + * FILE-WIDE VARIABLE + + */ + + + +static int RasterIndex; /* index over the rasterdata array */ + + + +/* + + * FUNCTION + + * imLzwGetCode - return the next code from the input stream. + + * + + * DESCRIPTION + + * return the next code from the input stream. The code read + + * has code_size size. flag is used to initialize + + * the internal variables. + + */ + + + +static int + +#ifdef __STDC__ + +imLzwGetCode(int ioType, int fd, FILE *fp, int code_size, int flag) + +#else + +imLzwGetCode(ioType, fd, fp, code_size, flag) + +int ioType; /* I/O flags */ + +int fd; /* Input file descriptor */ + +FILE *fp; /* Input file pointer */ + +int code_size; /* current code size used */ + +int flag; /* used for to initializations */ + +#endif + +{ + + static unsigned char buf[280]; + + static int curbit,lastbit,done,last_byte; + + int i, j, ret; + + unsigned char count; + + + + if (flag) + + { + + curbit = 0; + + lastbit = 0; + + done = FALSE; + + return 0; + + } + + + + if ( (curbit+code_size) >= lastbit) + + { + + if (done) + + if (curbit>=lastbit) + + ImGifEasyFail("Ran off the end of my bits",-1); + + buf[0] = buf[last_byte-2]; + + buf[1] = buf[last_byte-1]; + + if ( ImBinRead( ioType,fd,fp,&count, UCHAR, 1, 1 ) == -1 ) + + ImReturnBinError( ); + + if (count == 0) + + done = TRUE; + + else if ( ImBinRead( ioType,fd,fp,&buf[2],UCHAR,1,count ) == -1 ) + + ImReturnBinError( ); + + last_byte = 2 + count; + + curbit = (curbit - lastbit) + 16; + + lastbit = (2+count)*8 ; + + } + + ret = 0; + + for( i = curbit, j = 0; j < code_size; i++, j++ ) + + ret |= ((buf[ i / 8 ] & (1 << (i % 8))) != 0) << j; + + + + curbit += code_size; + + + + return ret; + +} + + + +/* + + * FUNCTION + + * ImLzwReadByte - return the next decoded pixel + + * + + * DESCRIPTION + + * Return the next decoded pixel value. + + */ + + + +int + +#ifdef __STDC__ + +imLzwReadByte(int ioType,int fd,FILE *fp,int flag,int input_code_size) + +#else + +imLzwReadByte(ioType,fd,fp,flag,input_code_size) + +int ioType; /* I/O flags */ + +int fd; /* Input file descriptor */ + +FILE *fp; /* Input file pointer */ + +int flag; /* to make intializations*/ + +int input_code_size;/* code size */ + +#endif + +{ + + static int fresh=FALSE; + + int code,incode; + + unsigned char count,junk; + + static int code_size,set_code_size; + + static int max_code,max_code_size; + + static int firstcode,oldcode; + + static int clear_code,end_code; + + static int table[2][(1<< IMMAX_LWZ_BITS)]; + + static int stack[(1<<(IMMAX_LWZ_BITS))*2],*sp; + + int i; + + + + if (flag) + + { + + set_code_size = input_code_size; + + code_size = set_code_size+1; + + clear_code = 1 << set_code_size ; + + end_code = clear_code + 1; + + max_code_size = 2*clear_code; + + max_code = clear_code+2; + + + + imLzwGetCode(ioType,fd,fp,NULL,TRUE); + + + + fresh=TRUE; + + + + for (i=0;i stack) + + return *--sp; + + + + while ((code=imLzwGetCode(ioType,fd,fp,code_size,FALSE))>=0) + + { + + if (code == clear_code) + + { + + for (i=0;i0 && ImBinRead( ioType,fd,fp,&junk,UCHAR,1,1) != -1); + + if (count!=0) + + ImGifEasyFail("Missing EOD in data stream (common occurance)",-1); + + return -1; + + } + + + + incode = code; + + + + if (code >= max_code) + + { + + *sp++ = firstcode; + + code = oldcode; + + } + + + + while (code >= clear_code) + + { + + *sp++ = table[1][code]; + + if (code == table[0][code]) + + ImGifEasyFail("Circular table entry BIG ERROR",-1); + + code = table[0][code]; + + } + + + + *sp++ = firstcode = table[1][code]; + + + + if ((code=max_code)<(1<= max_code_size) && + + (max_code_size < (1< stack) + + return *--sp; + + } + + return code; + +} + + + +/* + + * FUNCTION + + * ImLzwError - prints the error ocurred in the standar error file + + * and return with status equal to -1 + + */ + + + + + +static int + +#ifdef __STDC__ + +imLzwError(char *s) + +#else + +imLzwError(s) + +char *s; + +#endif + +{ + + ImErrorFatal(s, -1, IMEUNKNOWN); + +} + + + + + +/* + + * FUNCTION + + * ImLzwGifNextPixel - Return the next pixel from the raster data + + * + + * DESCRIPTION + + * The pixel values are stored in the rasterdata buffer. This + + * function returns the next pixel value. + + */ + + + +static int + +#ifdef __STDC__ + +imLzwGifNextPixel(unsigned char *rasterdata,int size) + +#else + +imLzwGifNextPixel( rasterdata,size) + +unsigned char *rasterdata; + +int size; + +#endif + +{ + + + + if ( RasterIndex == size ) + + return (EOF); + + return( rasterdata[RasterIndex++] ); + +} + + + +/* + + * FUNCTION + + * imLzwPackBits - packs the bits of the codes generated + + * by imLzwCompGif. + + * DESCRIPTION + + * imLzwPackBits() packs the bits of the codes generated by ImLzwCompGif + + * into a 1..256 byte output block. The first byte of the block is the + + * number 0..255 of data bytes in the block. To flush or initialize + + * the block, pass a negative argument. + + */ + + + +static int + +#ifdef __STDC__ + +imLzwPackBits(int compress_size, int prefix, int ioType, int fd, FILE *fp) + +#else + +imLzwPackBits(compress_size, prefix, ioType, fd, fp) + +int compress_size; + +int prefix; + +int ioType; + +int fd; + +FILE *fp; + +#endif + +{ + + static int cur_bit = 8; + + static unsigned char block[IMBLOCKSIZE] = { 0 }; + + int i, left_over_bits; + + + + /* if we are about to excede the bounds of block or if the flush + + code (code_bis < 0) we output the block */ + + if((cur_bit + compress_size > (IMBLOCKSIZE-1)*8) || (prefix < 0)) + + { + + /* handle case of data overlapping blocks */ + + if ((left_over_bits = (((cur_bit>>3) + + + ((cur_bit & 7) != 0))<<3) - cur_bit) != 0) + + { + + for (i=0; i < left_over_bits; i++) + + { + + if (prefix & (1<>3] |= (char)(1<<(cur_bit & 7)); + + /* note n>>3 == n/8 and n & 7 == n % 8 */ + + cur_bit++; + + } + + } + + compress_size -= left_over_bits; + + prefix = prefix>>left_over_bits; + + block[0] = (unsigned char)((cur_bit>>3) - 1); + + if (block[0]) + + if ( ImBinWrite( ioType, fd, fp, block, UCHAR, 1, block[0]+1) == -1) + + ImReturnBinError(); + + for(i=0; i < IMBLOCKSIZE; i++) + + block[i] = 0; + + cur_bit = 8; + + } + + if (prefix >= 0) + + { + + for (i=0; i < compress_size; i++) + + { + + if (prefix & (1<>3] |= (unsigned char)(1<<(cur_bit & 7)); + + /* note n>>3 == n/8 and n & 7 == n % 8 */ + + cur_bit++; + + } + + } + + return (1); + +} + + + + + +/* + + * FUNCTION + + * ImLzwCompGif - compresses and pack the pixels values that are + + * stored in rasterdata buffer using the + + * LZW algorithm. + + * DESCRIPTION + + * ImLzwCompGif () recieves pointers to a buffer and an output + + * stream, and the code size as parameters and outputs successive + + * blocks of LZW compressed gif data. The calling routine should + + * have aready written the GIF file header out to the output file. + + * It assumes that there will be no more than 8 bits/pixel and that + + * each data item comes from successive bytes returned by + + * ImLzwGifNextPixel(). + + */ + + + +int + +#ifdef __STDC__ + +imLzwCompGif(int ioType,int fd,FILE *fp,int code_size, unsigned char *rasterdata, int size) + +#else + +imLzwCompGif(ioType,fd,fp,code_size, rasterdata, size) + +int ioType; /* I/O flags */ + +int fd; /* Input file descriptor */ + +FILE *fp; /* Input file pointer */ + +int code_size; /* i.e. for 8 bits/pixel code_size = 9; */ + +unsigned char *rasterdata; /* rasterdata to be compress */ + +int size; /* size of the raster data */ + +#endif + +{ + + strTable heap; /* our very own memory manager */ + + int heap_index; + + int clear_code, end_code, cur_code; + + int i, found, num_colors, prefix, compress_size; + + int cur_char, end_of_data, bits_per_pix; + + strTableNodePtr cur_node; + + strTable root; /* root of string table for LZW compression */ + + /* is an array of 2**bits_per_pix pointers + + to atomic nodes */ + + heap_index = 0; + + heap = (strTable)malloc(sizeof(strTableNodePtr)*IMMAXIMUMCODE); + + if (heap == NULL) + + imLzwError("can't allocate heap"); + + for (i=0; i < IMMAXIMUMCODE; i++) + + { + + heap[i] = (strTableNodePtr)malloc(sizeof(strTableNode)); + + if (heap[i] == NULL) + + imLzwError("can't allocate heap"); + + } + + bits_per_pix = code_size - 1; + + compress_size = code_size; + + num_colors = 1<<(bits_per_pix); + + clear_code = num_colors; + + end_code = clear_code + 1; + + cur_code = end_code + 1; + + RasterIndex = 0; + + prefix = IMNULLPREFIX; + + root = (strTable)malloc(sizeof(strTableNodePtr)*num_colors); + + if (!root) + + imLzwError("memory allocation failure (root)"); + + for(i=0; ientry.code = i; + + root[i]->entry.prefix = IMNULLPREFIX; + + root[i]->entry.suffix = i; + + root[i]->left = NULL; + + root[i]->right = NULL; + + root[i]->children = NULL; + + } + + + + /* initialize output block */ + + imLzwPackBits(compress_size, -1, ioType,fd,fp); + + imLzwPackBits(compress_size, clear_code, ioType,fd,fp); + + end_of_data = 0; + + if ((cur_char = imLzwGifNextPixel(rasterdata,size)) == EOF) + + imLzwError("premature end of data"); + + while (!end_of_data) + + { + + prefix = cur_char; + + cur_node = root[prefix]; + + found = 1; + + if ((cur_char = imLzwGifNextPixel(rasterdata,size)) == EOF) + + { + + end_of_data = 1; + + break; + + } + + while(cur_node->children && found) + + { + + cur_node = cur_node->children; + + while(cur_node->entry.suffix != cur_char) + + { + + if (cur_char < cur_node->entry.suffix) + + { + + if (cur_node->left) + + cur_node = cur_node->left; + + else + + { + + cur_node->left = heap[heap_index++]; + + cur_node = cur_node->left; + + found = 0; + + break; + + } + + } + + else + + { + + if (cur_node->right) + + cur_node = cur_node->right; + + else + + { + + cur_node->right = heap[heap_index++]; + + cur_node = cur_node->right; + + found = 0; + + break; + + } + + } + + } + + if (found) + + { + + prefix = cur_node->entry.code; + + if((cur_char = imLzwGifNextPixel(rasterdata,size)) == EOF) + + { + + end_of_data = 1; + + break; + + } + + } + + } + + if (end_of_data) + + break; + + if (found) + + { + + cur_node->children = heap[heap_index++]; + + cur_node = cur_node->children; + + } + + cur_node->children = NULL; + + cur_node->left = NULL; + + cur_node->right = NULL; + + cur_node->entry.code = cur_code; + + cur_node->entry.prefix = prefix; + + cur_node->entry.suffix = cur_char; + + imLzwPackBits(compress_size, prefix, ioType,fd,fp); + + if (cur_code > ((1<<(compress_size))-1)) + + compress_size++; + + if (cur_code < IMMAXIMUMCODE) + + { + + cur_code++; + + } + + else + + { + + heap_index = num_colors; /* reinitialize string table */ + + for (i=0; i < num_colors; i++ ) + + root[i]->children = NULL; + + imLzwPackBits(compress_size, clear_code, ioType,fd,fp); + + compress_size = bits_per_pix + 1; + + cur_code = end_code + 1; + + } + + } + + imLzwPackBits(compress_size, prefix, ioType,fd,fp); + + imLzwPackBits(compress_size, end_code, ioType,fd,fp); + + imLzwPackBits(compress_size, -1, ioType,fd,fp); + + for (i=0; i < IMMAXIMUMCODE; i++) + + free(heap[i]); + + free(heap); + + free(root); + + return (1); + +} + + + diff --git a/utils/roq2/libim/imhdfinternal.h b/utils/roq2/libim/imhdfinternal.h new file mode 100644 index 0000000..5272db7 --- /dev/null +++ b/utils/roq2/libim/imhdfinternal.h @@ -0,0 +1,1152 @@ +/** + + ** $Header: /sdsc/dev/vis/image/imtools/v3.0/libim/src/include/RCS/imhdfinternal.h,v 1.5 1995/06/29 00:32:03 bduggan Exp $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +/** + + ** FILE + + ** imhdfinternal.h - HDF image file I/O include + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** imhdfinternal.h contains macros and structure definitions used + + ** by the HDF read and write code of the image library. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** IMHDFT* d tag numbers + + ** IMHDFNT* d MT and NT values + + ** IMHDFC* d color formats + + ** + + ** RED, GREEN, BLUE d RGB flags + + ** + + ** IMHDFTMPFILE d tmp file for holding stream data + + ** + + ** imHdfDD t Data Descriptor entry from an HDF file + + ** + + ** imHdfDDList v list of DDs + + ** imHdfDDListEnd v pointer to end of DD list + + ** imHdfDDCount v # of entries in DD list + + ** + + ** imHdfDDQNEntry m query number of entries in DD list + + ** imHdfDDQTag m query tag for a DD entry + + ** imHdfDDQRef m query reference number for a DD entry + + ** imHdfDDQDataOffset m query data offset for a DD entry + + ** imHdfDDQDataLength m query data length for a DD entry + + ** imHdfDDQNext m query next in DD list + + ** imHdfDDQFirst m query first in DD list + + ** + + ** imHdfClt t CLT that has been written out + + ** + + ** imHdfCltList v list of CLT's being written out + + ** imHdfCltListEnd v end of the CLT list + + ** + + ** imHdfCltQRefLUT m query LUT reference number for an entry + + ** imHdfCltQRefLD m query LD reference number for an entry + + ** imHdfCltQClt m query CLT pointer for an entry + + ** + + ** ReadStruct m read a structure + + ** WriteStruct m write a structure + + ** Read m read an item + + ** Write m write an item + + ** Seek m seek to a file location + + ** Tell m tell current file locaiton + + ** + + ** imHdfByteOrder v data byte order + + ** imHdfFloatFormat v data float format + + ** imHdfRef v current reference number + + ** + + ** imHdfDim t dimmension information + + ** imHdfRIG t RIG information collection + + ** + + ** PRIVATE CONTENTS + + ** none + + ** + + ** HISTORY + + ** $Log: imhdfinternal.h,v $ + + ** Revision 1.5 1995/06/29 00:32:03 bduggan + + ** updated copyright + + ** + + ** Revision 1.4 1994/10/03 16:03:03 nadeau + + ** Updated to ANSI C and C++ compatibility by adding function prototypes. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all macros and defined constants to have names starting with IM. + + ** Updated copyright message. + + ** + + ** Revision 1.3 92/09/02 13:18:52 vle + + ** Updated copyright notice. + + ** + + ** Revision 1.2 91/10/03 13:04:37 nadeau + + ** Changed 'interlace' to 'interleave'. + + ** + + ** Revision 1.1 91/10/03 12:56:45 nadeau + + ** Initial revision + + ** + + **/ + + + +#ifndef __IMHDFINTERNALH__ + + + + + + + + + + + +/* + + * CONSTANTS + + * IMHDFT* - tag numbers + + * + + * DESCRIPTION + + * Each tag in an HDF file is represented as an unsigned 16-bit number. + + * The following #defines are derived from "df.h", an include file + + * from the NCSA HDF library source. We can't include "df.h" itself + + * here, or we'd have to distribute "df.h" without our source. + + */ + + + +#define IMHDFTNULL ((sdsc_uint16)1) /* no data */ + + + +/* Utility set */ + +#define IMHDFTRLE ((sdsc_uint16)11) /* run length encoding */ + +#define IMHDFTIMC ((sdsc_uint16)12) /* IMCOMP compression */ + +#define IMHDFTFID ((sdsc_uint16)100) /* File identifier */ + +#define IMHDFTFD ((sdsc_uint16)101) /* File description */ + +#define IMHDFTTID ((sdsc_uint16)102) /* Tag identifier */ + +#define IMHDFTTD ((sdsc_uint16)103) /* Tag descriptor */ + +#define IMHDFTDIL ((sdsc_uint16)104) /* data identifier label */ + +#define IMHDFTDIA ((sdsc_uint16)105) /* data identifier annotation */ + +#define IMHDFTNT ((sdsc_uint16)106) /* number type */ + +#define IMHDFTMT ((sdsc_uint16)107) /* machine type */ + + + +/* Raster-8 set */ + +#define IMHDFTID8 ((sdsc_uint16)200) /* 8-bit Image dimension */ + +#define IMHDFTIP8 ((sdsc_uint16)201) /* 8-bit Image palette */ + +#define IMHDFTRI8 ((sdsc_uint16)202) /* Raster-8 image */ + +#define IMHDFTCI8 ((sdsc_uint16)203) /* RLE compressed 8-bit image */ + +#define IMHDFTII8 ((sdsc_uint16)204) /* IMCOMP compressed 8-bit image */ + + + +/* Raster Image set */ + +#define IMHDFTID ((sdsc_uint16)300) /* Image DimRec */ + +#define IMHDFTLUT ((sdsc_uint16)301) /* Image Palette */ + +#define IMHDFTRI ((sdsc_uint16)302) /* Raster Image */ + +#define IMHDFTCI ((sdsc_uint16)303) /* Compressed Image */ + + + +#define IMHDFTRIG ((sdsc_uint16)306) /* Raster Image Group */ + +#define IMHDFTLD ((sdsc_uint16)307) /* Palette DimRec */ + +#define IMHDFTMD ((sdsc_uint16)308) /* Matte DimRec */ + +#define IMHDFTMA ((sdsc_uint16)309) /* Matte Data */ + +#define IMHDFTCCN ((sdsc_uint16)310) /* color correction */ + +#define IMHDFTCFM ((sdsc_uint16)311) /* color format */ + +#define IMHDFTAR ((sdsc_uint16)312) /* aspect ratio */ + + + +/* Composition set */ + +#define IMHDFTDRAW ((sdsc_uint16)400) /* Draw these images in sequence */ + +#define IMHDFTRUN ((sdsc_uint16)401) /* run this as a program/script */ + + + +#define IMHDFTXYP ((sdsc_uint16)500) /* x-y position */ + +#define IMHDFTMTO ((sdsc_uint16)501) /* machine-type override */ + + + +/* Tektronix */ + +#define IMHDFTT14 ((sdsc_uint16)602) /* TEK 4014 data */ + +#define IMHDFTT105 ((sdsc_uint16)603) /* TEK 4105 data */ + + + +/* Scientific Data set */ + +#define IMHDFTSDG ((sdsc_uint16)700) /* Scientific Data Group */ + +#define IMHDFTSDD ((sdsc_uint16)701) /* Scientific Data DimRec */ + +#define IMHDFTSD ((sdsc_uint16)702) /* Scientific Data */ + +#define IMHDFTSDS ((sdsc_uint16)703) /* Scales */ + +#define IMHDFTSDL ((sdsc_uint16)704) /* Labels */ + +#define IMHDFTSDU ((sdsc_uint16)705) /* Units */ + +#define IMHDFTSDF ((sdsc_uint16)706) /* Formats */ + +#define IMHDFTSDM ((sdsc_uint16)707) /* Max/Min */ + +#define IMHDFTSDC ((sdsc_uint16)708) /* Coord sys */ + +#define IMHDFTSDT ((sdsc_uint16)709) /* Transpose */ + + + + + + + + + + + +/* + + * CONSTANTS + + * IMHDFNT* - MT and NT values + + * + + * DESCRIPTION + + * The MT (machine type) tag uses the 16-bit reference number field as + + * 4 4-bit values broken down in the order (high to low): + + * + + * unsigned char + + * unsigned int + + * float + + * double + + * + + * The unsigned char 4-bit field has as a value one of the char class + + * codes. Likewise for the unsigned int, float, and double. Class + + * code constants have names starting with IMHDFNT* and are the same as + + * used by the NT tag. + + * + + * The NT tag has 4 8-bit values assiciated with it, one of which + + * specifies the type being specified (unsigned int, int, etc), and another + + * the class code representing the description (its a VAX float, etc). + + */ + + + +#define IMHDFNTVERSION 1 /* current version of NT info */ + + + +/* Type codes */ + +#define IMHDFNTUINT 1 + +#define IMHDFNTINT 2 + +#define IMHDFNTUCHAR 3 + +#define IMHDFNTCHAR 4 + +#define IMHDFNTFLOAT 5 + +#define IMHDFNTDOUBLE 6 + + + +/* Class codes for unsigned int and int. */ + +#define IMHDFINTMBO 1 /* Motorola byte order 2's compl */ + +#define IMHDFINTVBO 2 /* Vax byte order 2's compl */ + +#define IMHDFINTIBO 4 /* Intel byte order 2's compl */ + + + +/* Class codes for float and double. */ + +#define IMHDFFLOATIEEE 1 /* IEEE format */ + +#define IMHDFFLOATVAX 2 /* Vax format */ + +#define IMHDFFLOATCRAY 3 /* Cray format */ + +#define IMHDFFLOATPC 4 /* PC floats - flipped IEEE */ + + + +/* Class codes for uchar and char. */ + +#define IMHDFCHARBYTE 0 /* bitwise/numeric field */ + +#define IMHDFCHARASCII 1 /* ASCII */ + +#define IMHDFCHAREBCDIC 5 /* EBCDIC */ + + + + + + + + + + + +/* + + * CONSTANTS + + * IMHDFC* - color formats + + * + + * DESCRIPTION + + * Color formats are described by character strings in the data for + + * the CFM tag. Internally we treat them as the following integer + + * constants for quicker comparisons. + + */ + + + +#define IMHDFCVALUE 0 /* Pseudo-Color */ + +#define IMHDFCRGB 1 /* RGB */ + +#define IMHDFCXYZ 2 /* CIE XYZ */ + +#define IMHDFCHSV 3 /* Hue-Saturation-Value */ + +#define IMHDFCHSI 4 /* Hue-Saturation-Intensity */ + +#define IMHDFCSPECTRAL 5 /* Spectral sampling */ + + + + + + + + + + + +/* + + * CONSTANTS + + * RED, GREEN, BLUE - RGB flags + + * + + * DESCRIPTION + + * These values are used during run-length encoding and decoding of + + * RGB images in order to keep track of the channel value currently + + * being handled. + + */ + + + +#define BLUE 0 + +#define GREEN 1 + +#define RED 2 + + + + + + + + + + + +/* + + * DD List Management + + */ + + + +/* + + * TYPEDEF & STRUCT + + * imHdfDD - Data Descriptor entry from an HDF file + + * + + * DESCRIPTION + + * An imHdfDD describes one piece of data in an HDF file. When an HDF + + * table of contents is read in, a list of all of the data items in + + * the file is made. + + */ + + + +typedef struct imHdfDD + +{ + + unsigned int dd_tag; /* Tag number */ + + unsigned int dd_ref; /* Reference count */ + + long dd_dataOffset; /* File offset to data */ + + unsigned int dd_dataLength; /* Length (in bytes) of data */ + + + + struct imHdfDD *dd_next; /* Next in linked list */ + +} imHdfDD; + + + + + + + + + + + +/* + + * GLOBALS + + * imHdfDDList - list of DDs + + * imHdfDDListEnd - pointer to end of DD list + + * imHdfDDCount - # of entries in DD list + + */ + + + +extern imHdfDD *imHdfDDList; /* List of DD's */ + +extern imHdfDD *imHdfDDListEnd; /* Pointer to end of DD list */ + +extern int imHdfDDCount; /* # of DD's in list */ + + + + + + + + + + + +/* + + * MACROS + + * imHdfDDQNEntry - query number of entries in DD list + + * imHdfDDQTag - query tag for a DD entry + + * imHdfDDQRef - query reference number for a DD entry + + * imHdfDDQDataOffset - query data offset for a DD entry + + * imHdfDDQDataLength - query data length for a DD entry + + * imHdfDDQNext - query next in DD list + + * imHdfDDQFirst - query first in DD list + + * + + * DESCRIPTION + + * Query stuff from a DD list. + + */ + + + +#define imHdfDDQNEntry() (imHdfDDCount) + +#define imHdfDDQTag(pDD) (pDD->dd_tag) + +#define imHdfDDQRef(pDD) (pDD->dd_ref) + +#define imHdfDDQDataOffset(pDD) (pDD->dd_dataOffset) + +#define imHdfDDQDataLength(pDD) (pDD->dd_dataLength) + +#define imHdfDDQNext(pDD) (pDD->dd_next) + +#define imHdfDDQFirst() (imHdfDDList) + + + + + + + + + + + +/* + + * CLT List Management + + */ + + + +/* + + * TYPEDEF & STRUCT + + * imHdfClt - CLT that has been written out + + * + + * DESCRIPTION + + * As CLT's are written out, their pointer, and LD and LUT tag/ref's + + * are saved to possibly be included in a future RIG. + + */ + + + +typedef struct imHdfClt + +{ + + ImClt *clt_clt; /* CLT pointer */ + + unsigned int clt_refLUT; /* LUT's ref */ + + unsigned int clt_refLD; /* LD's ref */ + + + + struct imHdfClt *clt_next; /* Next in list */ + +} imHdfClt; + + + + + + + + + + + +/* + + * GLOBALS + + * imHdfCltList - list of CLT's being written out + + * imHdfCltListEnd - end of the CLT list + + */ + + + +extern imHdfClt *imHdfCltList; + +extern imHdfClt *imHdfCltListEnd; + + + + + + + + + + + +/* + + * MACROS + + * imHdfCltQRefLUT - query LUT reference number for an entry + + * imHdfCltQRefLD - query LD reference number for an entry + + * imHdfCltQClt - query CLT pointer for an entry + + * + + * DESCRIPTION + + * Query stuff from an HDF CLT. + + */ + + + +#define imHdfCltQRefLUT(pClt) (pClt->clt_refLUT) + +#define imHdfCltQRefLD(pClt) (pClt->clt_refLD) + +#define imHdfCltQClt(pClt) (pClt->clt_clt) + + + + + + + + + + + +/* + + * MACROS + + * ReadStruct - read a structure + + * WriteStruct - write a structure + + * Read - read an item + + * Write - write an item + + * Seek - seek to a file location + + * Tell - tell current file locaiton + + * + + * DESCRIPTION + + * These macros just cover up the standard I/O calls and return + + * a fatal error code. They assume the local variables 'ioType', 'fd', + + * and 'fp' exist. + + */ + + + +#define ReadStruct(ptr,bin) \ + + if ( ImBinReadStruct( ioType, fd, fp, (ptr), (bin) ) == -1 ) \ + + ImReturnBinError( ); + +#define WriteStruct(ptr,bin) \ + + if ( ImBinWriteStruct( ioType, fd, fp, (ptr), (bin) ) == -1 ) \ + + ImReturnBinError( ); + +#define Read(ptr,type,nbytes,num) \ + + if ( ImBinRead( ioType, fd, fp, (ptr), (type), (nbytes), (num) )== -1)\ + + ImReturnBinError( ); + +#define Write(ptr,type,nbytes,num) \ + + if ( ImBinWrite( ioType, fd, fp, (ptr), (type), (nbytes), (num) )== -1)\ + + ImReturnBinError( ); + +#define Seek(offset) \ + + ImSeek( ioType, fd, fp, (offset), 0 ); + +#define Tell() \ + + ImTell( ioType, fd, fp ); + + + + + + + + + + + +/* + + * GLOBALS + + * imHdfByteOrder - data byte order + + * imHdfFloatFormat - data float format + + * imHdfRef - current reference number + + */ + +extern int imHdfByteOrder; /* Default byte order */ + +extern int imHdfFloatFormat; /* Default float format */ + +extern sdsc_uint16 imHdfRef; /* Current reference number */ + + + + + + + + + + + +/* + + * TYPEDEF & STRUCT + + * imHdfDim - dimmension information + + * + + * DESCRIPTION + + * The ID, LD, and MD tags each use the same structure in an HDF file. + + * The each given a width and height. They point to an NT tag to + + * indicate how big a channel (red, green, etc) for a pixel is. They + + * tell how many channels it takes to make a pixel (3 for RGB) and + + * what storage interleave scheme is used for the image. They also point + + * to a compression tag to describe the compression scheme. + + * + + * imHdfDim holds all this information after being read in, or before + + * being written out. + + */ + + + +typedef struct imHdfDim + +{ + + int dim_width; /* Image width */ + + int dim_height; /* Image height */ + + int dim_channelType; /* Type used for channel data */ + + int dim_channelSize; /* # of bytes per channel */ + + int dim_channelByteOrder; /* Channel byte order */ + + int dim_channelFloatFormat; /* Channel float format */ + + int dim_pixelSize; /* # of channels per pixel */ + + int dim_interleave; /* Interleave scheme */ + + int dim_compression; /* Compression scheme */ + +} imHdfDim; + + + + + + + + + + + +/* + + * TYPEDEF & STRUCT + + * imHdfRIG - RIG information collection + + * + + * DESCRIPTION + + * A RIG (raster image group) references several other tags. Much of + + * the information these tags point to must be read in and available + + * before the image, CLT, and matte image themselves can be read in + + * and uncompressed. imHdfRIG is is a repository for this collection of + + * information prior to the image being read in. + + */ + + + +typedef struct imHdfRIG + +{ + + imHdfDD *rig_dd; /* RIG data descriptor */ + + + + /* Image Dimension (ID) info */ + + imHdfDim *rig_imageDim; /* Image dimension info */ + + + + /* Raster Image (RI) info */ + + imHdfDD *rig_imageDD; /* DD pointing to image */ + + + + /* Lookup table Dimension (LD) info */ + + imHdfDim *rig_cltDim; /* CLT dimension info */ + + + + /* LookUp Table (LUT) info */ + + imHdfDD *rig_cltDD; /* DD pointing to CLT */ + + + + /* Matte channel Dimension (MD) info */ + + imHdfDim *rig_matteDim; /* Matte dimension info */ + + + + /* MAtte channel (MA) info */ + + imHdfDD *rig_matteDD; /* DD pointing to matte */ + + + + /* Color CorrectionN (CCN) info */ + + int rig_ccnGiven; /* Was an CCN tag supplied? */ + + float rig_gamma; /* Gamma correction factor */ + + float rig_red[3]; /* XYZ for red */ + + float rig_green[3]; /* XYZ for green */ + + float rig_blue[3]; /* XYZ for blue */ + + float rig_white[3]; /* XYZ for white */ + + + + /* Color ForMat (CFM) info */ + + int rig_colorFormat; /* Color format for image */ + + + + /* Aspect Ratio (AR) info */ + + float rig_aspectRatio; /* Image aspect ratio */ + + + + /* Machine Type Override (MTO) info */ + + + + + + struct imHdfRIG *rig_next; /* Next in RIG list */ + +} imHdfRIG; + + + + + + + + + + + +/* + + * FUNCTIONS + + * imHdf* - private functions to imhdfread.c and imhdfwrite.c + + */ + + + +#ifdef __STDC__ + +extern void imHdfDDEmpty(void ); + +extern imHdfDD *imHdfDDAppend(unsigned int tag, unsigned int ref, long dataOffset, long dataLength ); + +extern imHdfDD *imHdfDDFind(unsigned int tag, unsigned int ref ); + + + +extern void imHdfCltEmpty( void ); + +extern imHdfClt *imHdfCltAppend( ImClt* clt, unsigned int refLUT, unsigned int refLD); + +extern imHdfClt *imHdfCltFind( ImClt * clt ); + +extern imHdfClt *imHdfCltFindRef( unsigned int ref); + +#else + +extern void imHdfDDEmpty( ); + +extern imHdfDD *imHdfDDAppend( ); + +extern imHdfDD *imHdfDDFind( ); + + + +extern void imHdfCltEmpty( ); + +extern imHdfClt *imHdfCltAppend( ); + +extern imHdfClt *imHdfCltFind( ); + +extern imHdfClt *imHdfCltFindRef( ); + +#endif + + + + + +#endif /* __IMHDFINTERNALH__ */ + diff --git a/utils/roq2/libim/imhdfread.c b/utils/roq2/libim/imhdfread.c new file mode 100644 index 0000000..b704f00 --- /dev/null +++ b/utils/roq2/libim/imhdfread.c @@ -0,0 +1,2632 @@ +/** + ** $Header: /roq/libim/imhdfread.c 1 11/02/99 4:38p Zaphod $ + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + ** a division of General Atomics, San Diego, California, USA + ** + ** Users and possessors of this source code are hereby granted a + ** nonexclusive, royalty-free copyright and design patent license to + ** use this code in individual software. License is not granted for + ** commercial resale, in whole or in part, without prior written + ** permission from SDSC. This source is provided "AS IS" without express + ** or implied warranty of any kind. + ** + ** For further information contact: + ** E-Mail: info@sds.sdsc.edu + ** + ** Surface Mail: Information Center + ** San Diego Supercomputer Center + ** P.O. Box 85608 + ** San Diego, CA 92138-5608 + ** (619) 534-5000 + **/ + +#define HEADER " $Header: /roq/libim/imhdfread.c 1 11/02/99 4:38p Zaphod $" + +/** + ** FILE + ** imhdfread.c - HDF image file read + ** + ** PROJECT + ** libim - SDSC image manipulation library + ** + ** DESCRIPTION + ** imhdf.c contains routines to read HDF image files for + ** the image manipulation library. Raster data read in is stored + ** in a VFB and optional CLT in a tag table. + ** + ** PUBLIC CONTENTS + ** d =defined constant + ** f =function + ** m =defined macro + ** t =typedef/struct/union + ** v =variable + ** ? =other + ** + ** none + ** + ** PRIVATE CONTENTS + ** ImHdfRead f read an HDF file + ** + ** imHdfDDList v list of DDs + ** imHdfDDListEnd v pointer to end of DD list + ** imHdfDDCount v # of entries in DD list + ** + ** imHdfDDEmpty f empty the DD list + ** imHdfDDAppend f append to the DD list + ** imHdfDDFind f search the DD list + ** + ** imHdfCltList v list of CLT's being written out + ** imHdfCltListEnd v end of the CLT list + ** + ** imHdfCltEmpty f empty the Clt list + ** imHdfCltAppend f append to the Clt list + ** imHdfCltFind f find entry based on its CLT pointer + ** imHdfCltFindRef f find entry based on its LUT reference number + ** + ** imHdfByteOrder v data byte order + ** imHdfFloatFormat v data float format + ** imHdfRef v current reference number + ** + ** imHdfDimRead f read dimensions + ** imHdfCltRead f read in a CLT + ** imHdfVfbRead f read in a VFB + ** + ** imHdfVfbRead8 f read 8-bit uncomp. VFB + ** imHdfVfbReadRLE8 f read 8-bit RLE comp. VFB + ** imHdfVfbRead32 f read 32-bit uncomp. VFB + ** imHdfVfbReadRGB f read 24-bit uncomp. uninterleaved VFB + ** imHdfVfbReadRGBLine f read 24-bit uncomp. line interleaved VFB + ** imHdfVfbReadRGBPlane f read 24-bit uncomp. plane interleaved VFB + ** imHdfVfbReadRLERGB f read 24-bit RLE comp. uninterleaved VFB + ** imHdfVfbReadRLERGBLine f read 24-bit RLE comp. line interleaved VFB + ** imHdfVfbReadRLERGBPlane f read 24-bit RLE comp. plane interleaved VFB + ** + ** HISTORY + ** $Log: /roq/libim/imhdfread.c $ +* +* 1 11/02/99 4:38p Zaphod + ** Revision 1.20 1995/06/30 22:00:40 bduggan + ** changed comparison, since we got stuck in a + ** loop with c++ on decalpha's + ** + ** Revision 1.19 1995/06/29 00:28:04 bduggan + ** updated copyright year + ** + ** Revision 1.18 1995/06/15 20:23:28 bduggan + ** Added an include, a prototype, and a return. + ** removed an unused variable or two. + ** + ** Revision 1.17 1995/04/03 21:25:23 bduggan + ** took out #ifdef NEWMAGIC + ** + ** Revision 1.16 1995/01/10 23:26:36 bduggan + ** Put in IMMULTI/IMPIPE instead of TRUE/FALSE, + ** Made read/write routines static + ** + ** Revision 1.15 94/10/03 11:30:09 nadeau + ** Updated to ANSI C and C++ compatibility. + ** Removed all use of register keyword. + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + ** Changed all float arguments to double. + ** Added forward declarations. + ** Added misc. casts to passify SGI and DEC compilers. + ** Changed all macros and defined constants to have names + ** starting with IM. + ** Rearranged magic number structures for format handlers. + ** Made format handler routines static (i.e., local to file). + ** Updated comments, adding format descriptions and references. + ** Updated indenting on some code. + ** Updated copyright message. + ** + ** Revision 1.14 92/12/03 01:48:26 nadeau + ** Corrected info messages. + ** + ** Revision 1.13 92/12/01 17:28:48 nadeau + ** Updated write map and corrected info messages. + ** + ** Revision 1.12 92/11/23 18:42:26 nadeau + ** Removed use of IMINFOMSG. + ** + ** Revision 1.11 92/11/04 11:49:36 groening + ** put ImFIleFormat info and magic number info + ** from imfmt.c into this file. + ** + ** Revision 1.10 92/10/19 14:14:29 groening + ** added ImInfo statements + ** + ** Revision 1.9 92/08/31 17:24:52 vle + ** Updated copyright notice. + ** + ** Revision 1.8 92/04/09 09:34:23 groening + ** To make the compiler happy added extern statements. + ** + ** Revision 1.7 91/10/03 09:04:17 nadeau + ** Changed 'interlace' to 'interleave'. Moved write + ** support to imhdfwrite.c so that the file was smaller + ** and would compile easier on small systems. Moved + ** opcode and flag #defines to imhdfinternal.h. Various + ** cosmetic changes. + ** + ** Revision 1.6 91/02/12 10:51:18 nadeau + ** Removed the tag table checking, temp file creation, and + ** VFB conversion now handled by ImFileRead and ImFileWrite. + ** + ** Revision 1.5 91/01/30 18:06:29 nadeau + ** Changed a bit of obscure C trickery to something more + ** acceptable to the Alliant FX2800 C compiler. + ** + ** Revision 1.4 91/01/09 13:38:32 nadeau + ** Fixed malloc sizes for RLE buffers. They didn't handle the worst + ** case expansion factor on the HDF RLE scheme. + ** + ** Revision 1.3 90/12/12 20:09:21 rama + ** Added function declarations for imHdfVfbWrite functions + ** since they return a long value rather than an int. + ** + ** Revision 1.2 90/07/25 16:23:57 nadeau + ** Updated comments. + ** + ** Revision 1.1 90/07/23 13:47:31 nadeau + ** Initial revision + ** + **/ + +//#include +#include "iminternal.h" +#include "imhdfinternal.h" + + + + +/* + ** FORMAT + ** HDF - Hierarchical Data Format + ** + ** AKA + ** df, ncsa + ** + ** FORMAT REFERENCES + ** NCSA HDF Specifications, NCSA + ** + ** CODE CREDITS + ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1990. + ** + ** DESCRIPTION + ** An HDF (Hierarchical Data Format) file is a "tagged" data format. + ** This means that each piece of information in the file is labeled + ** in a standard way. These labels are called "Tags". + ** + ** An HDF file starts with a magic number and a "DD Block". A DD block + ** is a block of space filled with DD's. A DD is a "Data Descriptor" + ** and is just a group of tags (and related information). A DD block + ** starts with a DDH (DD Header) that tells how many DD's there are in + ** the block, and a file byte offset to the next DD block in the file. + ** If there are no more DD blocks, this offset is a 0. + ** + ** A DD contains the tag (a 16-bit unsigned integer), a reference + ** number (just a 16-bit unsigned occurrence number for that type of + ** tag), a file byte offset to the tag's data, and the number of bytes + ** of data at that location. + ** + ** There are lots of different tags. Tags are informally grouped by + ** functionalilty into "sets". HDF currently defines the "utility set" + ** of miscellaneous tags, the "raster-8 set" of 8-bit raster image + ** tags (now out of date, but still supported), the "raster image set" + ** for generic raster images, the "composite set" for describing the + ** compositing of images, the "vector set" for Tektronix command streams, + ** and the "scientific data set" for scientific data (huge arrays of + ** floating point numbers, usually). + ** + ** This code supports some of the utility tags and all of the raster + ** image set tags. The raster-8 set tags are considered redundant and + ** are ignored in preference to the generic raster image set tags. + ** Vector, composite, and scientific data set tags are ignored. + ** + ** The raster image set defines a special tag, called the "raster image + ** group" that just lists other tags (and their reference numbers) that + ** together describe an image. A typical raster image group would list + ** a tag describing the image's dimensions (the ID tag), the raster + ** image (the RI tag), and possibly a CLT dimension tag (the LD tag) + ** and the CLT (the LUT tag). + ** + ** FORMAT HISTORY + ** HDF was developed by NCSA (National Center for Supercomputing + ** Applications), one of four national supercomputer centers funded + ** by the National Science Foundation. SDSC (San Diego Supercomputer + ** Center) is another of the four centers. + ** + ** NCSA supplies a C and FORTRAN callable routine library for read and + ** writing HDF files. + ** + ** We do not use the NCSA HDF library for a variety of reasons: + ** + ** 1. NCSA's HDF library is not structured to be easily portable. + ** A great many of the byte order and floating point format + ** oddities in the world are exposed to the HDF library user. + ** + ** We have opted to use the portable binary I/O library + ** developed at SDSC to portably cover up this stuff as it + ** is being read in. + ** + ** 2. NCSA's HDF library does not handle pipes. It does the + ** opening itself and thus requires a file name. The image + ** library has been structured more generically. Input might + ** not be a file. In any case, the file's name is not always + ** available at this level. + ** + ** 3. NCSA's HDF library only supports one open HDF file at a + ** time (as of version 2.37). The application may be using + ** the HDF library to manage a scientific data set file when + ** it calls the image library. If we usurp the HDF library + ** for reading in the image, we have messed up the application. + ** + ** 4. If we use the NCSA HDF library, then all applications that + ** link with the image library must also linked with an HDF + ** library, whether they make HDF calls themselves or not. + ** This is awkward. Inclusion of the HDF code within the + ** image library is equally awkward and slightly immoral. + ** + ** Additionally, because the rest of the image library is + ** more portable than NCSA's code, there will be cases + ** where the image library can function on a new machine, + ** but NCSA hasn't ported the HDF library to it yet. We + ** will be stuck waiting on NCSA. + ** + ** So, we read in the HDF files directly, without use of NCSA's library. + ** We use the portable binary I/O library and none of NCSA's source, + ** and none of their include files. + ** + ** FORMAT PROBLEMS + ** HDF excels as a flexible file format.... at the cost of increased + ** complexity. That complexity makes it difficult to make a generic + ** access library that covers up byte order and floating point formats. + ** + ** HDF tags come in three flavors: + ** + ** 1. Self-contained tags. These tags give the names of things, + ** attributes of a type (int, float, etc), or attributes of + ** something else (image aspect ratio). + ** + ** 2. Tags that reference other tags. These tags include + ** within their data the tag number and reference number of + ** one or more other tags. The raster image group is just + ** a list of such tag/ref pairs. The image dimension tag (ID) + ** indicates a tag/ref describing a pixel channel (red, green, + ** whatever), and another tag/ref giving the compression + ** scheme used. + ** + ** 3. Tags that require other tags in order to be read in. + ** A raster image requires info in an image dimension tag + ** in order to be read in. Similarly with CLT's. + ** + ** Because of this high degree of interconnectivity, a generic package + ** design is tough. There is no obvious place at which to begin to + ** modularize... except at the "read it all in and give me a list of + ** images" level represented by this source. + ** + ** NCSA's HDF library modularizes by having calls to read in a tag and + ** its byte stream of data. It is then up to the caller to figure out + ** byte order and floating point format oddities. + ** + ** The upshot of all this is that we are using a monolithic coding style + ** to read in the HDF file. Modularization is purely for the convenience + ** of this code and does not constitute any kind of generic HDF file + ** read/write package. It's not clear a good design for such a package + ** is even possible. + ** + ** Nevertheless, SDSC does support and applaud HDF. It is an excellent, + ** generic, and portable file format that completely describes its own + ** contents. Good work NCSA! + ** + ** FORMAT VARIATIONS SUPPORTED + ** The following image forms are read and written: + ** + ** Chan. # of Inter- Comp- + ** Size Chan. lace ress. Meaning + ** ------- ------- ------- ------- --------------------------------------- + ** 1 byte 1 none none 8-bit index + ** RLE 8-bit index, compressed + ** + ** 2 byte 1 none none 16-bit index + ** + ** 3 byte 1 none none 24-bit index + ** + ** 4 byte 1 none none 32-bit index + ** + ** 1 byte 3 none none 24-bit RGB + ** RLE 24-bit RGB, compressed + ** line none 24-bit RGB, scanline interleave + ** RLE 24-bit RGB, scanline interleave, comp. + ** plane none 24-bit RGB, plane interleave + ** RLE 24-bit RGB, plane interleave, compressed + ** + ** RLE compression is not supported on 2, 3, and 4 byte indexes because + ** of the poor choice of RLE definition used by the HDF spec. RLE + ** is defined to be byte-wise. On multiple-byte-per-channel data, this + ** would try to find runs between bytes, rather than between full channel + ** integers. A run would only occur when consequitive bytes of the same + ** channel's integer happened to have the same bit pattern. Very rare and + ** very stupid. It also prevents the hiding of byte-order nonsense + ** below the level of format knowledge (such as in the binary I/O package + ** used by this code). So, RLE is not supported for multi-byte-per-channel + ** data. + ** + ** NCSA's IMCOMP compression scheme is not supported. + ** + ** The following CLT forms are read and written: + ** + ** Chan. # of Inter- Comp- + ** Size Chan. lace ress. Meaning + ** ------- ------- ------- ------- --------------------------------------- + ** 1 byte 3 none none 8-bit per primary + ** line 8-bit per primary, line interleaved + ** + ** Plane interleave has no meaning on CLT's. + ** + ** RLE and IMCOMP compressions are not supported on CLT's. + ** + ** Only 1-byte-per-primary and 3-primary CLT's are handled, ie. RGB CLT's. + ** The Image library doesn't currently support CLT's with larger + ** primaries or more than 3 primaries. + ** + ** CLT's may be of any length, including longer than is indexable by + ** a pixel in the CLT. + ** + ** The following tags are read and written: + ** + ** Tag Set Meaning + ** ------- --------------- ------------------------------------------------ + ** NT Utility Number type + ** MT Utility Machine type + ** RLE Utility RLE compression + ** + ** ID8 Raster-8 8-bit image dimensions + ** IP8 Raster-8 8-bit CLT + ** RI8 Raster-8 8-bit image + ** CI8 Raster-8 8-bit compressed image + ** + ** RIG Raster Raster image group + ** ID Raster Image dimensions + ** LD Raster CLT dimensions + ** RI Raster Raster image + ** CI Raster Compressed image + ** LUT Raster CLT + ** CFM Raster Color format + ** + ** The CCN (Color correction) and AR (aspect ratio) tags of the general + ** raster set are not supported. + ** + ** Matte channels are not yet supported. + ** + ** Composition, vector, and scientific data set tags are not supported. + **/ + + + +#ifdef __STDC__ +static int imHdfDimRead( int ioType, int fd, FILE *fp, imHdfDim **pDim ); +static int imHdfCltRead( int ioType, int fd, FILE *fp, imHdfRIG *pRIG, ImClt **pClt ); +static int imHdfVfbRead(int ioType, int fd, FILE *fp, imHdfRIG *pRIG, ImVfb **pVfb ); +static int imHdfVfbRead8( int ioType, int fd, FILE *fp, imHdfRIG *pRIG, ImVfb **pVfb); +static int imHdfVfbReadRLE8( int ioType, int fd, FILE *fp, imHdfRIG *pRIG, ImVfb **pVfb); +static int imHdfVfbRead32( int ioType, int fd, FILE *fp, imHdfRIG *pRIG, ImVfb **pVfb); +static int imHdfVfbReadRGB( int ioType, int fd, FILE *fp, imHdfRIG *pRIG, ImVfb **pVfb); +static int imHdfVfbReadRGBLine( int ioType, int fd, FILE *fp, imHdfRIG *pRIG, ImVfb **pVfb); +static int imHdfVfbReadRGBPlane( int ioType, int fd, FILE *fp, imHdfRIG *pRIG, ImVfb **pVfb); +static int imHdfVfbReadRLERGB( int ioType, int fd, FILE *fp, imHdfRIG *pRIG, ImVfb **pVfb); +static int imHdfVfbReadRLERGBLine( int ioType, int fd, FILE *fp, imHdfRIG *pRIG, ImVfb **pVfb); +static int imHdfVfbReadRLERGBPlane( int ioType, int fd, FILE *fp, imHdfRIG *pRIG, ImVfb **pVfb); +static int ImHdfRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); +extern int ImHdfWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, + TagTable *tagTable ); +#else +static int imHdfDimRead(); +static int imHdfCltRead(); +static int imHdfVfbRead(); +static int imHdfVfbRead8(); +static int imHdfVfbReadRLE8(); +static int imHdfVfbRead32( ); +static int imHdfVfbReadRGB(); +static int imHdfVfbReadRGBLine(); +static int imHdfVfbReadRGBPlane(); +static int imHdfVfbReadRLERGB() ; +static int imHdfVfbReadRLERGBLine(); +static int ImHdfRead( ); +extern int ImHdfWrite( ); +#endif + +/* + * HDF - Hierarchical Data Format + * For information on these structures, how to use them, etc. please + * see imfmt.c. + */ + +static char *imHdfNames[ ] = { "hdf", "df", "ncsa", NULL }; +static unsigned char imHdfMagicNumber[ ] = { 0x0E, 0x03, 0x13, 0x01 }; +static ImFileMagic imFileHdfMagic []= +{ + { 0, 4, imHdfMagicNumber}, + { 0, 0, NULL }, + NULL +}; + +static ImFileFormatReadMap imHdfReadMap[ ] = +{ + /* in out */ + /* type,ch,dep, attr. VFB type attr. */ + { IN,1,8, 0, IMVFBINDEX8, 0 }, + { IN,1,8, C, IMVFBINDEX8, C }, + + { IN,1,16, 0, IMVFBINDEX16, 0 }, + { IN,1,16, C, IMVFBINDEX16, C }, + + { IN,1,24, 0, IMVFBINDEX16, 0 }, + { IN,1,24, C, IMVFBINDEX16, C }, + + { IN,1,32, 0, IMVFBINDEX16, 0 }, + { IN,1,32, C, IMVFBINDEX16, C }, + + { RGB,3,8, 0, IMVFBRGB, 0 }, + { RGB,3,8, LI, IMVFBRGB, 0 }, + { RGB,3,8, PI, IMVFBRGB, 0 }, + + { IN,1,8, RLE, IMVFBINDEX8, 0 }, + { IN,1,8, RLE | C,IMVFBINDEX8, C }, + + { RGB,3,8, RLE, IMVFBRGB, 0 }, + { RGB,3,8, RLE |LI,IMVFBRGB, 0 }, + { RGB,3,8, RLE |PI,IMVFBRGB, 0 }, + { -1, 0, -1, 0 }, +}; +static ImFileFormatWriteMap imHdfWriteMap[ ] = +{ + /* + * For HDF, all image types vector thru to the same ImHdfWrite() + * routine. This is necessary because a tagTable may be passed in + * that contains multiple VFB's, each with different depth, CLT + * and alpha attributes. The write map primarily serves as a + * filter to make sure all such incomming VFB's are in one of the + * supported formats. + */ + + /* in out */ + /* VFB type, attr., type,ch,dep, attr., func */ + { IMVFBINDEX8, C, IN,1,8, C, ImHdfWrite }, + { IMVFBINDEX8, 0, IN,1,8, 0, ImHdfWrite }, + { IMVFBINDEX8, C, IN,1,8, RLE|C, ImHdfWrite }, + { IMVFBINDEX8, 0, IN,1,8, RLE, ImHdfWrite }, + + /* No RLE index 16 because HDF's RLE definition is braindamaged */ + { IMVFBINDEX16, C, IN,1,16, C, ImHdfWrite }, + { IMVFBINDEX16, 0, IN,1,16, 0, ImHdfWrite }, + +#ifdef hdf_24_compressed + /* + * NCSA's HDF tools don't fully support 24-bit images: + * NCSA Image 2.0 tries to read in 24-bit images, but bombs. + * NCSA Image 3.0 rejects 24-bit images with a cryptic message. + * SypGlass View 1.0 rejects 24-bit images with a message. + * + * hdf24hdf8 accepts uncompressed 24-bit images, rejects + * compressed interleaved 24-bit images, and incorrectly grabs + * only the 1st third of the data for compressed uninterleaved + * 24-bit images. + * + * Until NCSA's library fully supports compressed 24-bit images, + * our support will be disabled. Note that we will continue to + * read compressed 24-bit images. + */ + { IMVFBRGB, 0, RGB,3,8, RLE|PI, ImHdfWrite }, + { IMVFBRGB, 0, RGB,3,8, RLE|LI, ImHdfWrite }, + { IMVFBRGB, 0, RGB,3,8, RLE, ImHdfWrite }, +#endif + { IMVFBRGB, 0, RGB,3,8, 0, ImHdfWrite }, + { IMVFBRGB, 0, RGB,3,8, LI, ImHdfWrite }, + { IMVFBRGB, 0, RGB,3,8, PI, ImHdfWrite }, + { -1, 0, -1, 0, NULL }, +}; + + +ImFileFormat ImFileHdfFormat = +{ + imHdfNames, "Hierarchical Data File", + "NCSA", + "8-bit color index images, un- and RLE-compressed. 16-, 24-, and\n\ +2-bit color index images, un-compressed. 24-bit RGB, un- and RLE-\n\ +ompressed, un-, scanline- and plane-interleaved. Raster image\n\ +roups as well as older Raster-8 files.", +"8-bit color index images, un- and RLE-compressed. 16-bit color\n\ +ndex images, un-compressed. 24-bit RGB, uncompressed, un-,\n\ +canline- and plane-interleaved. Raster image group and older\n\ +aster-8 tags included.", +imFileHdfMagic, + IMMULTI, IMNOPIPE, /* Read */ + IMMULTI, IMNOPIPE, /* Write */ + ImHdfRead, imHdfReadMap, imHdfWriteMap +}; + +/* + * DD List Management + */ + +/* + * GLOBALS + * imHdfDDList - list of DDs + * imHdfDDListEnd - pointer to end of DD list + * imHdfDDCount - # of entries in DD list + */ + +imHdfDD *imHdfDDList = NULL; /* List of DD's */ +imHdfDD *imHdfDDListEnd = NULL;/* Pointer to end of DD list */ +int imHdfDDCount = 0; /* # of DD's in list */ + + + + + +/* + * FUNCTION + * imHdfDDEmpty - empty the DD list + * imHdfDDAppend - append to the DD list + * imHdfDDFind - search the DD list + * + * DESCRIPTION + * A DD list of tag/ref values is created and maintained. + */ + +void /* Returns nothing */ +imHdfDDEmpty( ) +{ + imHdfDD *pDD; /* DD list pointer */ + imHdfDD *pDD2; /* 2nd DD list pointer */ + + for ( pDD = imHdfDDList; pDD; ) + { + pDD2 = pDD; + pDD = pDD->dd_next; + free( (char *)pDD2 ); + } + imHdfDDList = imHdfDDListEnd = NULL; + imHdfDDCount = 0; +} + +imHdfDD * /* Returns new DD pointer */ +#ifdef __STDC__ +imHdfDDAppend( unsigned int tag, unsigned int ref, long dataOffset, long dataLength ) +#else +imHdfDDAppend( tag, ref, dataOffset, dataLength ) + unsigned int tag; /* Tag number */ + unsigned int ref; /* Reference number */ + long dataOffset; /* File offset */ + long dataLength; /* Number of bytes */ +#endif +{ + imHdfDD *pDD; /* New DD */ + + if ( (pDD = (imHdfDD *)malloc( sizeof( imHdfDD ) )) == NULL ) + { + ImErrNo = IMEMALLOC; + ImErrorFatal( ImQError(), NULL, ImErrNo ); + } + pDD->dd_tag = tag; + pDD->dd_ref = ref; + pDD->dd_dataOffset = dataOffset; + pDD->dd_dataLength = dataLength; + + if ( imHdfDDList == NULL ) + imHdfDDList = pDD; + else + imHdfDDListEnd->dd_next = pDD; + pDD->dd_next = NULL; + imHdfDDListEnd = pDD; + imHdfDDCount++; + return ( pDD ); +} + +imHdfDD * /* Returns found DD pointer */ +#ifdef __STDC__ +imHdfDDFind( unsigned int tag, unsigned int ref ) +#else +imHdfDDFind( tag, ref ) + unsigned int tag; /* Tag number */ + unsigned int ref; /* Reference number */ +#endif +{ + imHdfDD *pDD; /* DD list pointer */ + + for ( pDD = imHdfDDList; pDD; pDD = pDD->dd_next ) + if ( pDD->dd_tag == tag && pDD->dd_ref == ref ) + break; + return ( pDD ); /* Could be NULL */ +} + + + + + + +/* + * CLT List Management + */ + +/* + * GLOBALS + * imHdfCltList - list of CLT's being written out + * imHdfCltListEnd - end of the CLT list + */ + +imHdfClt *imHdfCltList = NULL; +imHdfClt *imHdfCltListEnd = NULL; + + + + + +/* + * FUNCTION + * imHdfCltEmpty - empty the Clt list + * imHdfCltAppend - append to the Clt list + * imHdfCltFind - find entry based on its CLT pointer + * imHdfCltFindRef - find entry based on its LUT reference number + * + * DESCRIPTION + * A Clt list of tag/ref values is created and maintained. + */ + +void /* Returns nothing */ +imHdfCltEmpty( ) +{ + imHdfClt *pClt; /* Clt list pointer */ + imHdfClt *pClt2; /* 2nd Clt list pointer */ + + for ( pClt = imHdfCltList; pClt; ) + { + pClt2 = pClt; + pClt = pClt->clt_next; + free( (char *)pClt2 ); + } + imHdfCltList = imHdfCltListEnd = NULL; +} + +imHdfClt * /* Returns new Clt pointer */ +#ifdef __STDC__ +imHdfCltAppend( ImClt* clt, unsigned int refLUT, unsigned int refLD ) +#else +imHdfCltAppend( clt, refLUT, refLD ) + ImClt *clt; /* CLT to add to list */ + unsigned int refLUT; /* It's LUT tag reference # */ + unsigned int refLD; /* It's LD tag reference # */ +#endif +{ + imHdfClt *pClt; /* New Clt list entry */ + + if ( (pClt = (imHdfClt *)malloc( sizeof( imHdfClt ) )) == NULL ) + { + ImErrNo = IMEMALLOC; + ImErrorFatal( ImQError(), NULL, ImErrNo ); + } + pClt->clt_clt = clt; + pClt->clt_refLUT = refLUT; + pClt->clt_refLD = refLD; + + if ( imHdfCltList == NULL ) + imHdfCltList = pClt; + else + imHdfCltListEnd->clt_next = pClt; + pClt->clt_next = NULL; + imHdfCltListEnd = pClt; + return ( pClt ); +} + +imHdfClt * /* Returns found Clt pointer */ +#ifdef __STDC__ +imHdfCltFind(ImClt * clt ) +#else +imHdfCltFind( clt ) + ImClt *clt; /* CLT to look for */ +#endif +{ + imHdfClt *pClt; /* Clt list pointer */ + + for ( pClt = imHdfCltList; pClt; pClt = pClt->clt_next ) + if ( pClt->clt_clt == clt ) + break; + return ( pClt ); /* Could be NULL */ +} + +imHdfClt * /* Returns found Clt pointer */ +#ifdef __STDC__ +imHdfCltFindRef( unsigned int ref ) +#else +imHdfCltFindRef( ref ) + unsigned int ref; /* Reference number to look for */ +#endif +{ + imHdfClt *pClt; /* Clt list pointer */ + + for ( pClt = imHdfCltList; pClt; pClt = pClt->clt_next ) + if ( pClt->clt_refLUT == ref ) + break; + return ( pClt ); /* Could be NULL */ +} + + + + + +/* + * GLOBALS + * imHdfByteOrder - data byte order + * imHdfFloatFormat - data float format + * imHdfRef - current reference number + */ +int imHdfByteOrder; /* Default byte order */ +int imHdfFloatFormat; /* Default float format */ +sdsc_uint16 imHdfRef = 100; /* Current reference number */ + + + + + +/* + * FUNCTION + * ImHdfRead - read an HDF file + * + * DESCRIPTION + * ImHdfRead() reads in an HDF file and adds all images and CLTs to + * the tag table. + * + * HDF files are structured such that we have to do multiple passes on + * the file, and then on the data we read in in order to get things + * done... + * + * 1. Read in all of the Data Descriptors (DDs), each one describing + * one tag in the file. A DD includes the file byte offset to get + * to the actual data for the tag. + * + * We can't immediately go out and get the data when we read in a + * DD because we don't know what to do with it yet. All raster + * image set tags are used by one or more raster image groups (RIGs) + * and we don't know what to do with, say, an ID (image dimension) + * tag until we've read in the RIG(s) that reference it. + * + * While reading in DD's, we toss ones for tags we don't handle. + * We also build up a linked list of RIG tags that we've found. The + * RIG tags are sorted by reference count, which may be (but usually + * isn't) different from the order found in the file. + * + * 2. Read in all the RIGs. Each RIG is a list of tag/ref values + * that refer to tags that in turn refer to data relevant for this + * image. The order of tags listed in a RIG is undefined. We'd + * like to just read in the data for each of the RIG's tags, + * but we can't always. For instance, to read in an image, + * we have to first know how big it is. That's told by an ID tag. + * But we might not have read in the ID tag for the RIG yet. Sigh. + * + * 3. Read in each RIG's CLT, image, and matte data based upon the + * ID, etc, tag information we read in in (2). Now we can process + * the file's CLT into an ImCLT, and uncompress the file's image + * into an ImVfb. As we finish each RIG, we add the ImClt and ImVfb + * to the tag table. + */ + +static int /* Returns status */ +#ifdef __STDC__ +ImHdfRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) +#else +ImHdfRead( ioType, fd, fp, flagsTable, tagTable ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + TagTable *flagsTable; /* Format flags */ + TagTable *tagTable; /* Tag list to add to */ +#endif +{ + int i; /* Counter */ + ImVfb *vfb; /* virtual frame buffer */ + ImClt *clt; /* color lookup table */ + + unsigned char magic[4]; /* Magic number */ + char message[1024]; /* Error message holder */ + + unsigned int nDD; /* # of DD's in block */ + long offset; /* Offset to next DD block */ + + imHdfDD *pDD; /* DD pointer */ + imHdfDD *pDD2; /* 2nd DD pointer */ + imHdfDD *pPrevDD; /* Previous DD pointer */ + + imHdfClt *pClt; /* Clt information */ + + imHdfRIG *rigList; /* List of RIG's */ + imHdfRIG *rigListEnd; /* Pointer to end of RIG list */ + imHdfRIG *pRIG; /* RIG pointer */ + imHdfRIG *pRIG2; /* 2nd RIG pointer */ + imHdfRIG *pPrevRIG; /* Previous RIG pointer */ + + int nTag; /* # of tags in a RIG */ + unsigned int tagList[50]; /* List of tags in a RIG */ + unsigned int refList[50]; /* and their ref numbers */ + + unsigned int tag; /* DD tag */ + unsigned int ref; /* DD reference number */ + long dataOffset; /* Offset to data */ + long dataLength; /* Length of data */ + int imageNum; /* number of images in a rig list */ + int imageNumII; /* number of images in a rig list */ + + /* + * All administrative stuff (tags, reference counts, file offsets) + * are in MBF byte order. + */ + BinByteOrder( BINMBF ); + + ImInfo ("Byte Order (header)", + "Most Significant Byte First"); + + + /* + * Read in the magic number. + */ + Read( magic, UCHAR, 1, 4 ); + if ( magic[0] != 0x0E || magic[1] != 0x03 || magic[2] != 0x13 || + magic[3] != 0x01 ) + { + ImErrNo = IMEMAGIC; + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + + + /* + * Read in the Data Descriptor Header (DDH) and each of the + * Data Descriptors (DDs) in the block. Create a linked list of + * the DDs, then advance to the next DDH. + */ + imHdfByteOrder = BINMBF; + imHdfFloatFormat = BINIEEE; + imHdfDDEmpty( ); + rigList = rigListEnd = NULL; + for ( ; ; ) + { + Read( &nDD, UINT, 2, 1 ); + Read( &offset, LONG, 4, 1 ); + for ( i = 0; i < nDD; i++ ) + { + Read( &tag, UINT, 2, 1 ); + Read( &ref, UINT, 2, 1 ); + Read( &dataOffset, LONG, 4, 1 ); + Read( &dataLength, LONG, 4, 1 ); + + /* + * Toss tags we don't care about and process those + * we do. + */ + switch ( tag ) + { + /* Keep number type tags */ + case IMHDFTNT: /* Number type */ + if ( imHdfDDAppend( tag, ref, dataOffset, + dataLength ) == NULL ) + return( -1 ); /* Error handled*/ + break; + + /* Keep raster image set tags */ + case IMHDFTCFM: /* Color format */ + case IMHDFTID: /* Image dimension */ + case IMHDFTLD: /* Lookup table dimension */ + case IMHDFTMD: /* Matte dimension */ + case IMHDFTMA: /* Matte */ + case IMHDFTCCN: /* Color correction */ + case IMHDFTAR: /* Aspect ratio */ + case IMHDFTRI: /* Raster image */ + case IMHDFTCI: /* Compressed raster image */ + case IMHDFTLUT: /* Lookup table */ + case IMHDFTMTO: /* Machine-Type Override */ + if ( imHdfDDAppend( tag, ref, dataOffset, + dataLength ) == NULL ) + return( -1 ); /* Error handled*/ + break; + + /* Add RIG's to a list, in ref number order */ + case IMHDFTRIG: /* Raster image group */ + if ( (pDD = imHdfDDAppend( tag, ref, dataOffset, + dataLength ) ) == NULL ) + return( -1 ); /* Error handled*/ + + ImMalloc( pRIG, imHdfRIG *, sizeof( imHdfRIG ) ); + pRIG->rig_dd = pDD; + + /* Set defaults for RIGs. */ + pRIG->rig_imageDim = NULL; + pRIG->rig_imageDD = NULL; + pRIG->rig_cltDim = NULL; + pRIG->rig_cltDD = NULL; + pRIG->rig_matteDim = NULL; + pRIG->rig_matteDD = NULL; + pRIG->rig_ccnGiven = FALSE; + pRIG->rig_colorFormat = IMHDFCVALUE; + pRIG->rig_aspectRatio = 1.0; + + /* If list is empty, make it start of list*/ + if ( rigList == NULL ) + { + rigList = rigListEnd = pRIG; + pRIG->rig_next = NULL; + break; + } + + /* If ref > last in list, append to list*/ + if ( imHdfDDQRef( pDD ) > imHdfDDQRef( rigListEnd->rig_dd ) ) + { + rigListEnd->rig_next = pRIG; + pRIG->rig_next = NULL; + rigListEnd = pRIG; + break; + } + + /* If ref < first in list, start list */ + if ( imHdfDDQRef( pDD ) < imHdfDDQRef( rigList->rig_dd ) ) + { + pRIG->rig_next = rigList; + rigList = pRIG; + break; + } + + /* Search list for proper position */ + pPrevRIG = rigList; + pRIG2 = rigList->rig_next; + while ( pRIG2 ) + { + if ( imHdfDDQRef( pDD ) > imHdfDDQRef( pRIG2->rig_dd ) ) + { + pPrevRIG = pRIG2; + pRIG2 = pRIG2->rig_next; + continue; + } + pRIG->rig_next = pRIG2; + pPrevRIG->rig_next = pRIG; + break; + } + break; + + /* Immediately process machine type tags */ + case IMHDFTMT: /* Machine type */ + switch ( (ref>>12)&0xF ) + { + case IMHDFINTMBO: /* Motorola byte order (MBF)*/ + ImInfo ("Byte Order (RIG)", + "Most Significant Byte First"); + imHdfByteOrder = BINMBF; + break; + case IMHDFINTVBO: /* Vax byte order (LBF) */ + case IMHDFINTIBO: /* Intel byte order (LBF)*/ + ImInfo ("Byte Order (RIG)", + "Least Significant Byte First"); + imHdfByteOrder = BINLBF; + break; + } + + switch ( (ref>>8)&0xF ) + { + case IMHDFFLOATIEEE:/* IEEE */ + imHdfFloatFormat = BINIEEE; + break; + case IMHDFFLOATVAX:/* VAX */ + imHdfFloatFormat = BINVAX; + break; + case IMHDFFLOATCRAY:/* Cray */ + imHdfFloatFormat = BINCRAYMP; + break; + case IMHDFFLOATPC:/* PC? */ + imHdfFloatFormat = BINIEEE;/* unknown!*/ + break; + } + continue; + + /* Skip null tags */ + case IMHDFTNULL: /* No data */ + continue; + + /* Skip generic compression description tags */ + case IMHDFTRLE: /* Run length encoded data */ + case IMHDFTIMC: /* IMCOMP compressed data */ + continue; + + /* Skip file, tag, and data identifiers */ + case IMHDFTFID: /* File identifier */ + case IMHDFTFD: /* File descriptor */ + case IMHDFTTID: /* Tag identifier */ + case IMHDFTTD: /* Tag descriptor */ + case IMHDFTDIL: /* Data identifier label */ + case IMHDFTDIA: /* Data identifier annotation */ + continue; + + /* Skip redundant raster-8 tags */ + case IMHDFTID8: /* Image dimension-8 */ + case IMHDFTIP8: /* Image palette-8 */ + case IMHDFTRI8: /* Raster image-8 */ + case IMHDFTCI8: /* Compressed image-8 */ + case IMHDFTII8: /* IMCOMP image-8 */ + continue; + + /* Skip composition tags */ + case IMHDFTDRAW: /* Draw */ + case IMHDFTRUN: /* Run */ + case IMHDFTXYP: /* XY position */ + continue; + + /* Skip vector set tags */ + case IMHDFTT14: /* Tektronix 4014 */ + case IMHDFTT105: /* Tektronix 4105 */ + continue; + + /* Skip scientific data set tags */ + case IMHDFTSDG: /* Scientific data group */ + case IMHDFTSDD: /* Scientific data dimension record*/ + case IMHDFTSD: /* Scientific data */ + case IMHDFTSDS: /* Scientific data scales */ + case IMHDFTSDL: /* Scientific data labels */ + case IMHDFTSDU: /* Scientific data units */ + case IMHDFTSDF: /* Scientific data format */ + case IMHDFTSDC: /* Scientific data coordinates */ + case IMHDFTSDM: /* Scientific data max/min */ + case IMHDFTSDT: /* Scientific data transpose */ + continue; + + /* Skip all unknown tags */ + default: + continue; + } + } + if ( offset <= 0 ) + break; + Seek( offset ); + } + + + /* + * Walk the RIG list. For each RIG, read in its list of tags and + * reference numbers, find each one, read it in, and add data to + * the RIG structure. + * + * We have to postpone reading in the actual CLT, image, and matte + * data until the rest of the RIG tags have been read in and processed. + * For instance, to read in an image we have to have the ID tag's + * info at hand. But that tag may be listed after the tag pointing + * to the image data. Sigh. + */ + BinByteOrder( imHdfByteOrder ); /* Data byte order */ + BinFloatFormat( imHdfFloatFormat ); /* Data float format */ + for ( pRIG = rigList; pRIG; pRIG = pRIG->rig_next ) + { + pDD = pRIG->rig_dd; + Seek( imHdfDDQDataOffset( pDD ) ); + nTag = imHdfDDQDataLength( pDD ) / 4;/* 4 bytes per tag/ref*/ + + for ( i = 0; i < nTag; i++ ) + { + Read( &tagList[i], UINT, 2, 1 ); + Read( &refList[i], UINT, 2, 1 ); + } + + + /* + * For each tag/ref in the RIG, search the DD list, then + * read in its data (if possible). + */ + for ( i = 0; i < nTag; i++ ) + { + if ( (pDD = imHdfDDFind( tagList[i], refList[i] )) == NULL) + { + sprintf( message, "RIG %d references non-existant tag %d/%d", + imHdfDDQRef( pRIG->rig_dd ), + tagList[i], refList[i] ); + ImErrorFatal( message, -1, IMESYNTAX ); + } + + switch ( tagList[i] ) + { + case IMHDFTRI: /* Raster image */ + case IMHDFTCI: /* Compressed raster image */ + pRIG->rig_imageDD = pDD; + break; + + case IMHDFTLUT: /* CLT */ + pRIG->rig_cltDD = pDD; + break; + + case IMHDFTMA: /* Matte channel */ + pRIG->rig_matteDD = pDD; + break; + + case IMHDFTAR: /* Aspect ratio */ + Seek( imHdfDDQDataOffset( pDD ) ); + Read( &pRIG->rig_aspectRatio, FLOAT, 4, 1 ); + break; + + case IMHDFTCCN: /* Color correction */ + pRIG->rig_ccnGiven = TRUE; + Seek( imHdfDDQDataOffset( pDD ) ); + Read( &pRIG->rig_gamma, FLOAT, 4, 1 ); + Read( pRIG->rig_red, FLOAT, 4, 3 ); + Read( pRIG->rig_green, FLOAT, 4, 3 ); + Read( pRIG->rig_blue, FLOAT, 4, 3 ); + Read( pRIG->rig_white, FLOAT, 4, 3 ); + break; + + case IMHDFTCFM: /* Color format */ + { + char *buffer; + + ImMalloc( buffer, char *, sizeof( char ) * + (imHdfDDQDataLength( pDD ) + 1) ); + Seek( imHdfDDQDataOffset( pDD ) ); + Read( buffer, CHAR, 1, imHdfDDQDataLength( pDD ) ); + buffer[imHdfDDQDataLength( pDD )] = '\0'; + + if ( strcmp( "VALUE", buffer ) == 0 ) + pRIG->rig_colorFormat = IMHDFCVALUE; + else if ( strcmp( "RGB", buffer ) == 0 ) + pRIG->rig_colorFormat = IMHDFCRGB; + else if ( strcmp( "XYZ", buffer ) == 0 ) + pRIG->rig_colorFormat = IMHDFCXYZ; + else if ( strcmp( "HSV", buffer ) == 0 ) + pRIG->rig_colorFormat = IMHDFCHSV; + else if ( strcmp( "HSI", buffer ) == 0 ) + pRIG->rig_colorFormat = IMHDFCHSI; + else if ( strcmp( "SPECTRAL", buffer ) == 0 ) + pRIG->rig_colorFormat = IMHDFCSPECTRAL; + else + { + sprintf( message, "Unknown color format '%s'", buffer ); + ImErrorFatal( message, -1, IMESYNTAX ); + } + + free( (char *)buffer ); + break; + } + + case IMHDFTID: /* Image dimension */ + Seek( imHdfDDQDataOffset( pDD ) ); + if ( imHdfDimRead( ioType, fd, fp, + &pRIG->rig_imageDim ) == -1 ) + return ( -1 ); /* Error stuff done*/ + break; + + case IMHDFTLD: /* Lookup table dimension */ + Seek( imHdfDDQDataOffset( pDD ) ); + if ( imHdfDimRead( ioType, fd, fp, + &pRIG->rig_cltDim ) == -1 ) + return ( -1 ); /* Error stuff done*/ + break; + + case IMHDFTMD: /* Matte dimension */ + Seek( imHdfDDQDataOffset( pDD ) ); + if ( imHdfDimRead( ioType, fd, fp, + &pRIG->rig_matteDim ) == -1 ) + return ( -1 ); /* Error stuff done*/ + break; + } + } + + + /* + * There may or may not be an LD tag giving the dimensions of + * the lookup table. SDSC tools always add the LD tag. + * NCSA tools apparently do not. + * + * Without an explicit LD, we create one: + * width = 256 + * height = 1 + * channel type = UCHAR + * channel size = 1 + * channel byte order = BINMBF (unneeded) + * channel float format = BINIEEE (unneeded) + * pixel size = 3 + * interleave = 0 (RGB, RGB, RGB, ...) + * compression = 0 (none) + * + * These attributes match those of the IP8 pallette most + * commonly used in cases where the LD tag is "forgotten". + */ + if ( pRIG->rig_cltDD != NULL && pRIG->rig_cltDim == NULL ) + { + ImMalloc( pRIG->rig_cltDim, imHdfDim *, sizeof( imHdfDim ) ); + pRIG->rig_cltDim->dim_width = 256; + pRIG->rig_cltDim->dim_height = 1; + pRIG->rig_cltDim->dim_channelType = UCHAR; + pRIG->rig_cltDim->dim_channelSize = 1; + pRIG->rig_cltDim->dim_channelByteOrder = BINMBF; + pRIG->rig_cltDim->dim_channelFloatFormat = BINIEEE; + pRIG->rig_cltDim->dim_pixelSize = 3; + pRIG->rig_cltDim->dim_interleave = 0; + pRIG->rig_cltDim->dim_compression = 0; + } + + + /* + * Confirm that we got all that we needed. + */ + if ( pRIG->rig_imageDD != NULL && pRIG->rig_imageDim == NULL ) + ImErrorFatal( "Missing image dimension tag for raster image", + -1, IMESYNTAX ); + if ( pRIG->rig_matteDD != NULL && pRIG->rig_matteDim == NULL ) + ImErrorFatal( "Missing matte dimension tag for matte", + -1, IMESYNTAX ); + } + + + /* + * Walk the RIG list and read in each RIG's CLT (if any) and image + * (if any). + * + * We don't keep track of multiple RIG's pointing to the same VFB. + * We do keep track of multiple RIG's using the same CLT. + */ + nTag = 0; + imHdfCltEmpty( ); + + + /* Count the number of images */ + imageNum=0; + imageNumII=0; + for ( pRIG = rigList; pRIG; pRIG = pRIG->rig_next ) + if ( pRIG->rig_imageDD ) imageNum++; + + for ( pRIG = rigList; pRIG; pRIG = pRIG->rig_next ) + { + if ( pRIG->rig_imageDD ) + { + imageNumII++; + sprintf (message, "%d of %d",imageNumII, imageNum); + ImInfo ("Image",message); + } + /* + * Read in the CLT. + */ + clt = IMCLTNULL; + if ( pRIG->rig_cltDD ) + { + /* + * Check if CLT has already been read in for another + * image. + */ + if ( (pClt = imHdfCltFindRef( imHdfDDQRef( pRIG->rig_cltDD ) )) == NULL ) + { + /* Nope. Read it in and add it to our list.*/ + if ( imHdfCltRead( ioType, fd, fp, pRIG, &clt ) == -1 ) + return ( -1 ); /* Error stuff done already*/ + imHdfCltAppend( clt, imHdfDDQRef( pRIG->rig_cltDD ), + 0 ); + TagTableAppend( tagTable, + TagEntryAlloc( "image clt", POINTER, &clt ) ); + } + else + clt = imHdfCltQClt( pClt ); + nTag++; + } + + + /* + * Read in the image. + */ + vfb = IMVFBNULL; + if ( pRIG->rig_imageDD ) + { + if ( imHdfVfbRead( ioType, fd, fp, pRIG, &vfb ) == -1 ) + return ( -1 ); /* Error stuff done already*/ + ImVfbSClt( vfb, clt ); /* Could be NULL clt */ + TagTableAppend( tagTable, + TagEntryAlloc( "image vfb", POINTER, &vfb ) ); + nTag++; + } + } + + imHdfDDEmpty( ); + imHdfCltEmpty( ); + + return ( nTag ); +} + + + + + +/* + * FUNCTION + * imHdfDimRead - read dimensions + * + * DESCRIPTION + * The ID, LD, and MD tags all share the same structuring of their + * data. imHdfDimRead() reads in this set of dimension information and + * returns it in a new imHdfDim structure. + * + * A dimension tag also references an NT tag (for the size of a pixel + * channel), and an optional compression tag. These tags are searched + * for in the DD list and their information incorporated into the imHdfDim + * structure. + */ + +static int /* Returns status */ +#ifdef __STDC__ +imHdfDimRead( int ioType, int fd, FILE *fp, imHdfDim **pDim ) +#else +imHdfDimRead( ioType, fd, fp, pDim ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + imHdfDim **pDim; /* Returned dimensions */ +#endif +{ + unsigned int tagNT, refNT; /* NT tag and reference number */ + unsigned int tagComp, refComp; /* Compression tag and ref number*/ + unsigned char nt[4]; /* NT information */ + imHdfDD *pDD2; /* 2nd DD pointer */ + imHdfDim *dim; /* New dimension information */ + char message[1024]; /* Tmp error message text */ + + + /* + * Create a new dimension description and read in the basic info. + */ + ImMalloc( dim, imHdfDim *, sizeof( imHdfDim ) ); + Read( &dim->dim_width, INT, 4, 1 ); + Read( &dim->dim_height, INT, 4, 1 ); + Read( &tagNT, UINT, 2, 1 ); + Read( &refNT, UINT, 2, 1 ); + Read( &dim->dim_pixelSize, INT, 2, 1 ); + Read( &dim->dim_interleave, INT, 2, 1 ); + Read( &tagComp, UINT, 2, 1 ); + Read( &refComp, UINT, 2, 1 ); + + + /* + * Search the DD list for the NT tag. + */ + if ( (pDD2 = imHdfDDFind( tagNT, refNT )) == NULL ) + ImErrorFatal( "Dimension tag references nonexistant NT tag", + -1, IMESYNTAX ); + + + /* + * Read in the NT tag and decode it. + * + * The NT tag's data is 4 1-byte quantities: + * + * 0 version number of NT tag (version 1 supported here) + * 1 type code + * 2 width of type, in bits + * 3 class code (byte order, float format, etc) + */ + Seek( imHdfDDQDataOffset( pDD2 ) ); + Read( nt, UCHAR, 1, 4 ); + + dim->dim_channelSize = ((nt[2] + 7) / 8); + dim->dim_channelByteOrder = imHdfByteOrder; /* Default */ + dim->dim_channelFloatFormat = imHdfFloatFormat; /* Default */ + + switch ( nt[1] ) + { + case IMHDFNTUINT: /* unsigned int */ + case IMHDFNTINT: /* int */ + dim->dim_channelType = (nt[1] == IMHDFNTUINT) ? UINT : INT ; + switch ( nt[3] ) + { + default: + case IMHDFINTMBO: + dim->dim_channelByteOrder = BINMBF; + ImInfo ("Byte Order (data)", + "Most Significant Byte First"); + break; + case IMHDFINTVBO: + case IMHDFINTIBO: + ImInfo ("Byte Order (data)", + "Least Significant Byte First"); + dim->dim_channelByteOrder = BINLBF; + break; + } + break; + + case IMHDFNTUCHAR:/* unsigned char */ + /* Ignore nt[3] = ASCII or bitwise-numeric. */ + dim->dim_channelType = UCHAR; + break; + + case IMHDFNTCHAR: /* char */ + /* Ignore nt[3] = ASCII or bitwise-numeric. */ + dim->dim_channelType = CHAR; + break; + + case IMHDFNTFLOAT:/* float */ + dim->dim_channelType = FLOAT; + case IMHDFNTDOUBLE:/* double */ + dim->dim_channelType = (nt[1] == IMHDFNTFLOAT) ? FLOAT : DOUBLE ; + switch ( nt[3] ) + { + default: + case IMHDFFLOATPC: + case IMHDFFLOATIEEE: + dim->dim_channelFloatFormat = BINIEEE; + break; + case IMHDFFLOATVAX: + dim->dim_channelFloatFormat = BINVAX; + break; + case IMHDFFLOATCRAY: + dim->dim_channelFloatFormat = BINCRAYMP; + break; + } + break; + + default: + sprintf( message, "Unknown NT type code '%d'", nt[1] ); + ImErrorFatal( message, -1, IMESYNTAX ); + } + + + /* + * Check the compression tag type. + * + * As of this version, 3 types of compression are defined: + * + * 0 none + * RLE run-length encode + * IMCOMP special compression + * + * The later two have tags, but those tags don't actually point + * to any data, so we don't bother reading them in. If, someday, + * other compression types exist, those types may have data and + * we'd have to seek and read them in. + * + * Note: in practice, NCSA tools appear to simply skip adding the + * compression tags, since they have no data. Mentioning their + * tag number is enough. SDSC tools more closely follow the HDF + * spec and do add the no-data compression tags. + */ + switch ( tagComp ) + { + case 0: /* No compression used */ + case IMHDFTRLE: /* RLE used */ + case IMHDFTIMC: /* IMCOMP used */ + dim->dim_compression = tagComp; + break; + default: + sprintf( message, "Unknown image compression tag '%d'", + tagComp ); + ImErrorFatal( message, -1, IMESYNTAX ); + } + + *pDim = dim; + return ( 0 ); +} + + + + + +/* + * FUNCTION + * imHdfCltRead - read in a CLT + * + * DESCRIPTION + * HDF files support lots of variations on the traditional CLT. + * Fortunately, virtually all HDF files use the same small subset of + * the possibilities. We restrict ourselves to this same subset by + * ruling out the following possibilities: + * + * 2D CLT's + * CLT's are expected to have a width that is the number of + * colors in the CLT, and a height of 1. Height values other + * than 1 are rejected. + * + * Non-RGB CLT's (CLT's that aren't 3 8-bit chars) + * A channel (red, green, whatever) has a type, a size, a + * byte order and/or a floating point format. A number of + * these channels together form a pixel value. + * + * HDF allows one to describe obscure CLT's that have, say, + * 10 channels, each one a 3 byte IEEE (no such thing) floating + * point number. Woa! This is too much. We restrict ourselves + * to the traditional 3 channel, 1 char each CLT. + * + * Compressed CLT's + * The LD tag can give a compression mode. However, there are + * no compression modes currently defined for CLT's. Its + * unclear why one would bother. + */ + +static int /* Returns status */ +#ifdef __STDC__ +imHdfCltRead( int ioType, int fd, FILE *fp, imHdfRIG *pRIG, ImClt **pClt ) +#else +imHdfCltRead( ioType, fd, fp, pRIG, pClt ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + imHdfRIG *pRIG; /* Image group description */ + ImClt **pClt; /* Returned CLT */ +#endif +{ + int n; /* Number of CLT entries */ + int i; /* Counter */ + ImClt *clt; /* New clt */ + ImCltPtr pColor; /* CLT entry "pointer" */ + char message[1024]; /* Error message buffer */ + imHdfDim *pDim; /* CLT dimension information */ + unsigned char *buffer; /* Temp CLT buffer */ + + + /* + * Check for strange CLT specifications. + */ + pDim = pRIG->rig_cltDim; + if ( pDim->dim_height != 1 ) + { + sprintf( message, "Unsupported CLT height '%d' (should be 1)", + pDim->dim_height ); + ImErrorFatal( message, -1, IMESYNTAX ); + } + + if ( pDim->dim_channelType != UCHAR || pDim->dim_channelSize != 1 || + pDim->dim_pixelSize != 3 ) + ImErrorFatal( "Non-RGB (24-bit) CLT's not supported", + -1, IMESYNTAX ); + if ( pDim->dim_compression != 0 ) + { + sprintf( message, "Unsupported CLT compression scheme '%d' (should be 0)", + pDim->dim_compression ); + ImErrorFatal( message, -1, IMESYNTAX ); + } + + /* + * Allocate a new CLT. + */ + n = pDim->dim_width; + if ( (clt = ImCltAlloc( n )) == IMCLTNULL ) + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + sprintf (message, "%d Entries",pDim->dim_width); + ImInfo ("Color Table",message); + + /* + * Read it in, based on the interleave mode. + */ + Seek( imHdfDDQDataOffset( pRIG->rig_cltDD ) ); + switch ( pDim->dim_interleave ) + { + case 0: /* RGB, RGB, RGB, ... */ + ImMalloc( buffer, unsigned char *, sizeof( unsigned char ) * n * 3 ); + pColor = ImCltQFirst( clt ); + Read( buffer, UCHAR, 1, n * 3 ); + n *= 3; + for ( i = 0; i < n; i+=3 ) + { + ImCltSRed( pColor, buffer[i] ); + ImCltSGreen( pColor, buffer[i+1] ); + ImCltSBlue( pColor, buffer[i+2] ); + ImCltSInc( clt, pColor ); + } + free( (char *)buffer ); + break; + + case 1: /* RRRR..., GGGG..., BBBB... */ + ImMalloc( buffer, unsigned char *, sizeof( unsigned char ) * n ); + pColor = ImCltQFirst( clt ); + Read( buffer, UCHAR, 1, n ); + for ( i = 0; i < n; i++ ) + { + ImCltSRed( pColor, buffer[i] ); + ImCltSInc( clt, pColor ); + } + + pColor = ImCltQFirst( clt ); + Read( buffer, UCHAR, 1, n ); + for ( i = 0; i < n; i++ ) + { + ImCltSGreen( pColor, buffer[i] ); + ImCltSInc( clt, pColor ); + } + + pColor = ImCltQFirst( clt ); + Read( buffer, UCHAR, 1, n ); + for ( i = 0; i < n; i++ ) + { + ImCltSBlue( pColor, buffer[i] ); + ImCltSInc( clt, pColor ); + } + free( (char *)buffer ); + break; + + default: + sprintf( message, "Unsupported CLT interleave scheme '%d'", + pDim->dim_interleave ); + ImErrorFatal( message, -1, IMESYNTAX ); + } + *pClt = clt; + return ( 0 ); +} + + + + + +/* + * FUNCTION + * imHdfVfbRead - read in a VFB + * + * DESCRIPTION + * HDF files represent images using one of three "interleave" methods: + * + * #0: RGB per pixel per scanline. + * #1: Red scanline, then Green, then Blue. + * #2: Red plane, then Green, then Blue. + * + * Each of these three methods may be optionally compressed: + * + * None: Values are not compressed. + * + * RLE: Bytewise run-length encoded: + * + * ... + * + * if < 0, repeate - times + * else, include next values as is + * + * Note: worst case encoding: ABBABBABB... where A and + * B are different 's. Encoded result is: + * 1A-2BB1A-2BB1A-2BB..., giving a 33% expansion. + * + * IMCOMP: Color sampling. + * + * HDF allows one to describe bizarre image storage configurations that + * have, for instance, 7 channels per pixel, each represented by a + * 13-byte VAX float (no such thing) in the CIE XYZ color space (but + * with 7 channels?). Yeah, right. We restrict ourselves to the more + * traditional configurations: + * + * 1 8-bit channel: 8-bit color index + CLT + * 1 16-bit channel: 16-bit color index + CLT + * 1 24-bit channel: 24-bit color index + CLT + * 1 32-bit channel: 32-bit color index + CLT + * + * 3 8-bit channels: 24-bit RGB color + * + * All other variations cause errors. + * + * Note that for 16-, 24-, and 32-bit index configurations we do not + * support encoding. HDF defines their RLE encoding as being byte-wise, + * even on multi-byte integer values. This is nonsense. Encoding would + * only do anything if each byte in the integer had the same bit-pattern! + * Very dumb. It also makes it impossible to hide byte order stuff + * beneath the encoding scheme (and in the Binary I/O package). + * + * HDF supports a variety of color formats for pixel storage, including + * RGB, CIE, HSI, HSV, and spectral samples. We only support RGB and + * pseudo-color (VALUE). + */ + +static int /* Returns status */ +#ifdef __STDC__ +imHdfVfbRead( int ioType, int fd, FILE *fp, imHdfRIG *pRIG, ImVfb **pVfb ) +#else +imHdfVfbRead( ioType, fd, fp, pRIG, pVfb ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + imHdfRIG *pRIG; /* Image group description */ + ImVfb **pVfb; /* Returned VFB */ +#endif +{ + imHdfDim *pDim; /* Image dimension information */ + char message[1024]; /* Error message buffer */ + + + /* + * Check for unsupported image specifications and vector off to + * the appropriate read routine. + */ + pDim = pRIG->rig_imageDim; + if ( pRIG->rig_ccnGiven ) + ImErrorInfo( "Color correction specifications ignored", + -1, IMESYNTAX ); + + /* + * NCSA tools fail to add the color format tag, so we have to assume + * it is either a value (for 8- and 16-bit images) or an RGB color (for + * 24-bit images). SDSC tools always add the color format tag. + */ + if ( pRIG->rig_colorFormat != IMHDFCRGB && + pRIG->rig_colorFormat != IMHDFCVALUE ) + ImErrorFatal( "Non-RGB color formats not supported", + -1, IMESYNTAX ); + + /* + * Check for illegal combinations. + */ + if ( pDim->dim_pixelSize == 1 ) + { + /* 1 channel. We support 1, 2, 3 and 4 byte channels. */ + if ( pDim->dim_channelSize < 1 || pDim->dim_channelSize > 4 ) + { + sprintf( message, "Unsupported channel size: '%d' bytes\n", + pDim->dim_channelSize ); + ImErrorFatal( message, -1, IMESYNTAX ); + } + if ( pDim->dim_channelSize != 1 && pDim->dim_compression != 0 ) + ImErrorFatal( "RLE image compression not supported on channels larger than 1 byte", + -1, IMESYNTAX ); + if ( pDim->dim_interleave != 0 ) + { + sprintf( message, "Interleave '%d' meaningless on single-channel images", + pDim->dim_interleave ); + ImErrorInfo( message, -1, IMESYNTAX ); + } + } + else if ( pDim->dim_pixelSize != 3 ) + { + sprintf( message, "Unsupported number of channels per pixel: '%d'", + pDim->dim_pixelSize ); + ImErrorFatal( message, -1, IMESYNTAX ); + } + + if ( pDim->dim_channelType == FLOAT || pDim->dim_channelType == DOUBLE ) + ImErrorFatal( "Floating point channels not supported", + -1, IMESYNTAX ); + + if ( pDim->dim_compression != 0 && pDim->dim_compression != IMHDFTRLE ) + { + sprintf( message, "Unsupported image compression scheme '%d'", + pDim->dim_compression ); + ImErrorFatal( message, -1, IMESYNTAX ); + } + + if ( pDim->dim_interleave < 0 || pDim->dim_interleave > 3 ) + { + sprintf( message, "Unknown interleave scheme '%d'", + pDim->dim_interleave ); + ImErrorFatal( message, -1, IMESYNTAX ); + } + + + sprintf (message, "%d x %d",pDim->dim_width, pDim->dim_height); + ImInfo("Resolution",message); + + /* + * Call the appropriate VFB read routine. + */ + if ( pDim->dim_pixelSize == 1 ) + { + switch ( pDim->dim_channelSize ) + { + case 1: /* 8-bit, single channel per pixel image. */ + ImInfo ("Type","8-bit Color Indexed"); + switch ( pDim->dim_compression ) + { + case 0: /* Uncompressed */ + ImInfo ("Compression Type","None"); + return ( imHdfVfbRead8( ioType, fd, fp, + pRIG, pVfb ) ); + case IMHDFTRLE:/* Run-length encoded */ + ImInfo ("Compression Type","Run Length Encoded (RLE)"); + return ( imHdfVfbReadRLE8( ioType, fd, fp, + pRIG, pVfb ) ); + } + case 2: /* 16-bit, single channel per pixel image. */ + case 3: /* 24-bit, single channel per pixel image. */ + case 4: /* 32-bit, single channel per pixel image. */ + /* Uncompressed */ + ImInfo ("Type","32-bit Color Indexed"); + ImInfo ("Compression Type","None"); + return ( imHdfVfbRead32( ioType, fd, fp, + pRIG, pVfb ) ); + + } + } + + /* RGB image */ + ImInfo ("Type","24-bit RGB"); + switch ( pDim->dim_compression ) + { + case 0: /* Uncompressed */ + ImInfo ("Compression Type","None"); + switch ( pDim->dim_interleave ) + { + case 0: /* Uninterleaved */ + ImInfo ("Interleave Type","None (Non-interleaved)"); + return ( imHdfVfbReadRGB( ioType, fd, fp, + pRIG, pVfb ) ); + case 1: /* Scanline interleaved */ + ImInfo ("Interleave Type","Scanline"); + return ( imHdfVfbReadRGBLine( ioType, fd, fp, + pRIG, pVfb )); + case 2: /* Plane interleaved */ + ImInfo ("Interleave Type","Plane"); + return ( imHdfVfbReadRGBPlane( ioType, fd, fp, + pRIG, pVfb)); + } + + case IMHDFTRLE:/* Run-length encoded */ + ImInfo ("Compression Type","Run Length Encoded (RLE)"); + switch ( pDim->dim_interleave ) + { + case 0: /* Uninterleaved */ + ImInfo ("Interleave Type","None (Non-inteleaved)"); + return ( imHdfVfbReadRLERGB( ioType, fd, fp, + pRIG, pVfb ) ); + case 1: /* Scanline interleaved */ + ImInfo ("Interleave Type","Scanline"); + return ( imHdfVfbReadRLERGBLine( ioType, fd, fp, + pRIG, pVfb )); + case 2: /* Plane interleaved */ + ImInfo ("Interleave Type","Plane"); + return ( imHdfVfbReadRLERGBPlane( ioType, fd, fp, + pRIG, pVfb)); + } + } + /*NOT REACHED*/ + return 0; +} + + + + + +/* + * FUNCTION + * imHdfVfbRead8 - read 8-bit uncomp. VFB + * imHdfVfbReadRLE8 - read 8-bit RLE comp. VFB + * + * imHdfVfbRead32 - read 32-bit uncomp. VFB + * + * imHdfVfbReadRGB - read 24-bit uncomp. uninterleaved VFB + * imHdfVfbReadRGBLine - read 24-bit uncomp. line interleaved VFB + * imHdfVfbReadRGBPlane - read 24-bit uncomp. plane interleaved VFB + * + * imHdfVfbReadRLERGB - read 24-bit RLE comp. uninterleaved VFB + * imHdfVfbReadRLERGBLine - read 24-bit RLE comp. line interleaved VFB + * imHdfVfbReadRLERGBPlane - read 24-bit RLE comp. plane interleaved VFB + * + * DESCRIPTION + * Each of these routines allocate a new VFB, then read in the image + * data from the file. If RLE compression is in use, runs are decoded. + */ + +static int /* Returns status */ +#ifdef __STDC__ +imHdfVfbRead8( int ioType, int fd, FILE *fp, imHdfRIG *pRIG, ImVfb **pVfb ) +#else +imHdfVfbRead8( ioType, fd, fp, pRIG, pVfb ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + imHdfRIG *pRIG; /* Image group description */ + ImVfb **pVfb; /* Returned VFB */ +#endif +{ + imHdfDim *pDim; /* Image dimension information */ + ImVfb *vfb; /* New VFB */ + ImVfbPtr pPixel; /* Pixel pointer */ + unsigned char *buffer; /* Input buffer */ + unsigned char *pBuffer; /* Pointer into input buffer */ + int i, j; /* Counters */ + + /* + * Allocate a new 8-bit index VFB. + */ + pDim = pRIG->rig_imageDim; + if ( (vfb = ImVfbAlloc( pDim->dim_width, pDim->dim_height, IMVFBINDEX8)) + == IMVFBNULL ) + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + + /* + * Allocate a buffer that is big enough for one scanline. Read in + * the image one scanline at a time. + */ + ImMalloc( buffer, unsigned char *, sizeof( unsigned char ) * pDim->dim_width ); + pPixel = ImVfbQFirst( vfb ); + Seek( imHdfDDQDataOffset( pRIG->rig_imageDD ) ); + + for ( i = 0; i < pDim->dim_height; i++ ) + { + Read( buffer, UCHAR, 1, pDim->dim_width ); + pBuffer = buffer; + for ( j = 0; j < pDim->dim_width; j++ ) + { + ImVfbSIndex8( vfb, pPixel, *pBuffer++ ); + ImVfbSInc( vfb, pPixel ); + } + } + free( (char *)buffer ); + *pVfb = vfb; + return ( 0 ); +} + +static int /* Returns status */ +#ifdef __STDC__ +imHdfVfbReadRLE8( int ioType, int fd, FILE *fp, imHdfRIG *pRIG, ImVfb **pVfb ) +#else +imHdfVfbReadRLE8( ioType, fd, fp, pRIG, pVfb ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + imHdfRIG *pRIG; /* Image group description */ + ImVfb **pVfb; /* Returned VFB */ +#endif +{ + imHdfDim *pDim; /* Image dimension information */ + ImVfb *vfb; /* New VFB */ + ImVfbPtr pPixel; /* Pixel pointer */ + unsigned char *buffer; /* Input buffer */ + unsigned char *pBuffer; /* Pointer into input buffer */ + unsigned char *pBufferEnd; /* Pointer to end of buffer */ + unsigned char count; /* Run length count */ + + + /* + * Allocate a new 8-bit index VFB. + */ + pDim = pRIG->rig_imageDim; + if ( (vfb = ImVfbAlloc( pDim->dim_width, pDim->dim_height, IMVFBINDEX8)) + == IMVFBNULL ) + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + + /* + * Allocate a buffer big enough for the whole image, read it in, + * then process the whole thing. This takes more memory, but we + * have no othe rway of alloating space sufficient for just one + * scanline, then reading in only that scanline. + */ + Seek( imHdfDDQDataOffset( pRIG->rig_imageDD ) ); + pPixel = ImVfbQFirst( vfb ); + ImMalloc( buffer, unsigned char *, sizeof( unsigned char ) * imHdfDDQDataLength( pRIG->rig_imageDD ) ); + pBuffer = buffer; + pBufferEnd = buffer + imHdfDDQDataLength( pRIG->rig_imageDD ); + Read( buffer, UCHAR, 1, imHdfDDQDataLength( pRIG->rig_imageDD ) ); + + while ( pBuffer < pBufferEnd ) + { + count = *pBuffer++; + if ( !(count & 0x80) ) + { + /* Take next 'count' bytes as literal. */ + while ( count-- ) + { + ImVfbSIndex8( vfb, pPixel, *pBuffer++ ); + ImVfbSInc( vfb, pPixel ); + } + continue; + } + + /* Repeat next byte 'count' times. */ + count &= 0x7F; + while ( count-- ) + { + ImVfbSIndex8( vfb, pPixel, *pBuffer ); + ImVfbSInc( vfb, pPixel ); + } + pBuffer++; + } + free( (char *)buffer ); + *pVfb = vfb; + return ( 0 ); +} + +static int /* Returns status */ +#ifdef __STDC__ +imHdfVfbRead32( int ioType, int fd, FILE *fp, imHdfRIG *pRIG, ImVfb **pVfb ) +#else +imHdfVfbRead32( ioType, fd, fp, pRIG, pVfb ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + imHdfRIG *pRIG; /* Image group description */ + ImVfb **pVfb; /* Returned VFB */ +#endif +{ + imHdfDim *pDim; /* Image dimension information */ + ImVfb *vfb; /* New VFB */ + ImVfbPtr pPixel; /* Pixel pointer */ + sdsc_uint32 *buffer; /* Input buffer */ + sdsc_uint32 *pBuffer; /* Pointer into input buffer */ + int i, j; /* Counters */ + + + /* + * Allocate a new 16-bit index VFB. + */ + pDim = pRIG->rig_imageDim; + if ( (vfb = ImVfbAlloc( pDim->dim_width, pDim->dim_height,IMVFBINDEX16)) + == IMVFBNULL ) + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + + /* + * Allocate a buffer that is big enough for one scanline. Read in + * the image one scanline at a time. + */ + ImMalloc( buffer, sdsc_uint32 *, sizeof( sdsc_uint32 ) * pDim->dim_width ); + pPixel = ImVfbQFirst( vfb ); + Seek( imHdfDDQDataOffset( pRIG->rig_imageDD ) ); + + for ( i = 0; i < pDim->dim_height; i++ ) + { + Read( buffer, UINT32, pDim->dim_channelSize, pDim->dim_width ); + pBuffer = buffer; + for ( j = 0; j < pDim->dim_width; j++ ) + { + /* Truncate upper bits. */ + ImVfbSIndex16( vfb, pPixel, *pBuffer++ ); + ImVfbSInc( vfb, pPixel ); + } + } + free( (char *)buffer ); + *pVfb = vfb; + return ( 0 ); +} + +static int /* Returns status */ +#ifdef __STDC__ +imHdfVfbReadRGB( int ioType, int fd, FILE *fp, imHdfRIG *pRIG, ImVfb **pVfb ) +#else +imHdfVfbReadRGB( ioType, fd, fp, pRIG, pVfb ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + imHdfRIG *pRIG; /* Image group description */ + ImVfb **pVfb; /* Returned VFB */ +#endif +{ + imHdfDim *pDim; /* Image dimension information */ + ImVfb *vfb; /* New VFB */ + ImVfbPtr pPixel; /* Pixel pointer */ + unsigned char *buffer; /* Input buffer */ + unsigned char *pBuffer; /* Pointer into input buffer */ + int i, j; /* Counters */ + + + /* + * Allocate a new 24-bit index VFB. + */ + pDim = pRIG->rig_imageDim; + if ( (vfb = ImVfbAlloc( pDim->dim_width, pDim->dim_height, IMVFBRGB )) + == IMVFBNULL ) + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + + /* + * Allocate a buffer that is big enough for one scanline. Read in + * the image one scanline at a time. + */ + pPixel = ImVfbQFirst( vfb ); + Seek( imHdfDDQDataOffset( pRIG->rig_imageDD ) ); + ImMalloc( buffer, unsigned char *, sizeof( unsigned char )* pDim->dim_width*3 ); + + for ( i = 0; i < pDim->dim_height; i++ ) + { + Read( buffer, UCHAR, 1, pDim->dim_width * 3 ); + pBuffer = buffer; + for ( j = 0; j < pDim->dim_width; j++ ) + { + ImVfbSRed( vfb, pPixel, *pBuffer++ ); + ImVfbSGreen( vfb, pPixel, *pBuffer++ ); + ImVfbSBlue( vfb, pPixel, *pBuffer++ ); + ImVfbSInc( vfb, pPixel ); + } + } + free( (char *)buffer ); + *pVfb = vfb; + return ( 0 ); +} + +static int /* Returns status */ +#ifdef __STDC__ +imHdfVfbReadRGBLine( int ioType, int fd, FILE *fp, imHdfRIG *pRIG, ImVfb **pVfb ) +#else +imHdfVfbReadRGBLine( ioType, fd, fp, pRIG, pVfb ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + imHdfRIG *pRIG; /* Image group description */ + ImVfb **pVfb; /* Returned VFB */ +#endif +{ + imHdfDim *pDim; /* Image dimension information */ + ImVfb *vfb; /* New VFB */ + ImVfbPtr pPixel; /* Pixel pointer */ + unsigned char *buffer; /* Input buffer */ + unsigned char *pBuffer1; /* Pointer into input buffer */ + unsigned char *pBuffer2; /* Pointer into input buffer */ + unsigned char *pBuffer3; /* Pointer into input buffer */ + int i, j; /* Counters */ + + + /* + * Allocate a new 24-bit index VFB. + */ + pDim = pRIG->rig_imageDim; + if ( (vfb = ImVfbAlloc( pDim->dim_width, pDim->dim_height, IMVFBRGB )) + == IMVFBNULL ) + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + + /* + * Allocate a buffer that is big enough for one scanline. Read in + * the image one scanline at a time. + */ + pPixel = ImVfbQFirst( vfb ); + Seek( imHdfDDQDataOffset( pRIG->rig_imageDD ) ); + ImMalloc( buffer, unsigned char *, sizeof( unsigned char )* pDim->dim_width*3 ); + + for ( i = 0; i < pDim->dim_height; i++ ) + { + Read( buffer, UCHAR, 1, pDim->dim_width * 3 ); + pBuffer1 = buffer; + pBuffer2 = buffer + pDim->dim_width; + pBuffer3 = pBuffer2 + pDim->dim_width; + for ( j = 0; j < pDim->dim_width; j++ ) + { + ImVfbSRed( vfb, pPixel, *pBuffer1++ ); + ImVfbSGreen( vfb, pPixel, *pBuffer2++ ); + ImVfbSBlue( vfb, pPixel, *pBuffer3++ ); + ImVfbSInc( vfb, pPixel ); + } + } + free( (char *)buffer ); + *pVfb = vfb; + return ( 0 ); +} + +static int /* Returns status */ +#ifdef __STDC__ +imHdfVfbReadRGBPlane( int ioType, int fd, FILE *fp, imHdfRIG *pRIG, ImVfb **pVfb ) +#else +imHdfVfbReadRGBPlane( ioType, fd, fp, pRIG, pVfb ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + imHdfRIG *pRIG; /* Image group description */ + ImVfb **pVfb; /* Returned VFB */ +#endif +{ + imHdfDim *pDim; /* Image dimension information */ + ImVfb *vfb; /* New VFB */ + ImVfbPtr pPixel; /* Pixel pointer */ + unsigned char *buffer; /* Input buffer */ + unsigned char *pBuffer; /* Pointer into input buffer */ + int i, j; /* Counters */ + + + /* + * Allocate a new 24-bit index VFB. + */ + pDim = pRIG->rig_imageDim; + if ( (vfb = ImVfbAlloc( pDim->dim_width, pDim->dim_height, IMVFBRGB )) + == IMVFBNULL ) + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + + /* + * Allocate a buffer that is big enough for one scanline. Read in + * the image one scanline at a time. + */ + Seek( imHdfDDQDataOffset( pRIG->rig_imageDD ) ); + ImMalloc( buffer, unsigned char *, sizeof( unsigned char ) * pDim->dim_width ); + + pPixel = ImVfbQFirst( vfb ); + for ( i = 0; i < pDim->dim_height; i++ ) + { + Read( buffer, UCHAR, 1, pDim->dim_width ); + pBuffer = buffer; + for ( j = 0; j < pDim->dim_width; j++ ) + { + ImVfbSRed( vfb, pPixel, *pBuffer++ ); + ImVfbSInc( vfb, pPixel ); + } + } + pPixel = ImVfbQFirst( vfb ); + for ( i = 0; i < pDim->dim_height; i++ ) + { + Read( buffer, UCHAR, 1, pDim->dim_width ); + pBuffer = buffer; + for ( j = 0; j < pDim->dim_width; j++ ) + { + ImVfbSGreen( vfb, pPixel, *pBuffer++ ); + ImVfbSInc( vfb, pPixel ); + } + } + pPixel = ImVfbQFirst( vfb ); + for ( i = 0; i < pDim->dim_height; i++ ) + { + Read( buffer, UCHAR, 1, pDim->dim_width ); + pBuffer = buffer; + for ( j = 0; j < pDim->dim_width; j++ ) + { + ImVfbSBlue( vfb, pPixel, *pBuffer++ ); + ImVfbSInc( vfb, pPixel ); + } + } + free( (char *)buffer ); + *pVfb = vfb; + return ( 0 ); +} + +static int /* Returns status */ +#ifdef __STDC__ +imHdfVfbReadRLERGB( int ioType, int fd, FILE *fp, imHdfRIG *pRIG, ImVfb **pVfb ) +#else +imHdfVfbReadRLERGB( ioType, fd, fp, pRIG, pVfb ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + imHdfRIG *pRIG; /* Image group description */ + ImVfb **pVfb; /* Returned VFB */ +#endif +{ + imHdfDim *pDim; /* Image dimension information */ + ImVfb *vfb; /* New VFB */ + ImVfbPtr pPixel; /* Pixel pointer */ + unsigned char *buffer; /* Input buffer */ + unsigned char *pBufferEnd; /* Pointer to end of buffer */ + unsigned char *pBuffer; /* Pointer into input buffer */ + unsigned char count; /* Run length count */ + int onRGB; /* Which channel are we on? */ + + + /* + * Allocate a new 24-bit index VFB. + */ + pDim = pRIG->rig_imageDim; + if ( (vfb = ImVfbAlloc( pDim->dim_width, pDim->dim_height, IMVFBRGB )) + == IMVFBNULL ) + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + + /* + * Allocate a buffer big enough for the whole image, read it in, + * then process the whole thing. This takes more memory, but we + * have no othe rway of alloating space sufficient for just one + * scanline, then reading in only that scanline. + */ + Seek( imHdfDDQDataOffset( pRIG->rig_imageDD ) ); + pPixel = ImVfbQFirst( vfb ); + ImMalloc( buffer, unsigned char *, sizeof( unsigned char ) * + imHdfDDQDataLength( pRIG->rig_imageDD ) ); + pBuffer = buffer; + pBufferEnd = buffer + imHdfDDQDataLength( pRIG->rig_imageDD ); + Read( buffer, UCHAR, 1, imHdfDDQDataLength( pRIG->rig_imageDD ) ); + + onRGB = RED; + while ( pBuffer < pBufferEnd ) + { + count = *pBuffer++; + if ( !(count & 0x80) ) + { + /* Take next 'count' bytes as literal. */ + while ( count-- ) + { + switch ( onRGB ) + { + case RED: + ImVfbSRed( vfb, pPixel, *pBuffer++ ); + onRGB = GREEN; + continue; + case GREEN: + ImVfbSGreen( vfb, pPixel, *pBuffer++ ); + onRGB = BLUE; + continue; + case BLUE: + ImVfbSBlue( vfb, pPixel, *pBuffer++ ); + ImVfbSInc( vfb, pPixel ); + onRGB = RED; + continue; + } + } + continue; + } + + /* Repeat next byte 'count' times. */ + count &= 0x7F; + while ( count-- ) + { + switch ( onRGB ) + { + case RED: + ImVfbSRed( vfb, pPixel, *pBuffer ); + onRGB = GREEN; + continue; + case GREEN: + ImVfbSGreen( vfb, pPixel, *pBuffer ); + onRGB = BLUE; + continue; + case BLUE: + ImVfbSBlue( vfb, pPixel, *pBuffer ); + ImVfbSInc( vfb, pPixel ); + onRGB = RED; + continue; + } + } + pBuffer++; + } + free( (char *)buffer ); + *pVfb = vfb; + return ( 0 ); +} + +static int /* Returns status */ +#ifdef __STDC__ +imHdfVfbReadRLERGBLine( int ioType, int fd, FILE *fp, imHdfRIG *pRIG, ImVfb **pVfb ) +#else +imHdfVfbReadRLERGBLine( ioType, fd, fp, pRIG, pVfb ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + imHdfRIG *pRIG; /* Image group description */ + ImVfb **pVfb; /* Returned VFB */ +#endif +{ + imHdfDim *pDim; /* Image dimension information */ + ImVfb *vfb; /* New VFB */ + ImVfbPtr pPixel; /* Pixel pointer */ + ImVfbPtr pPixelStart; /* Start of scanline */ + unsigned char *buffer; /* Input buffer */ + unsigned char *pBufferEnd; /* Pointer to end of buffer */ + unsigned char *pBuffer; /* Pointer into input buffer */ + unsigned char count; /* Run length count */ + int onRGB; /* Which channel are we on? */ + int i, j; /* Counters */ + + + /* + * Allocate a new 24-bit index VFB. + */ + pDim = pRIG->rig_imageDim; + if ( (vfb = ImVfbAlloc( pDim->dim_width, pDim->dim_height, IMVFBRGB )) + == IMVFBNULL ) + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + + /* + * Allocate a buffer big enough for the whole image, read it in, + * then process the whole thing. This takes more memory, but we + * have no othe rway of alloating space sufficient for just one + * scanline, then reading in only that scanline. + */ + Seek( imHdfDDQDataOffset( pRIG->rig_imageDD ) ); + pPixel = ImVfbQFirst( vfb ); + ImMalloc( buffer, unsigned char *, sizeof( unsigned char ) * + imHdfDDQDataLength( pRIG->rig_imageDD ) ); + pBuffer = buffer; + pBufferEnd = buffer + imHdfDDQDataLength( pRIG->rig_imageDD ); + Read( buffer, UCHAR, 1, imHdfDDQDataLength( pRIG->rig_imageDD ) ); + + /* + * This algorithm is made slower by the possibility that a run + * from one scanline extends into the next one (ie, a run at the + * end of the RED channel flows into the GREEN channel). SDSC + * tools end scanline interleaved runs at the end of a channel's + * scanline. The HDF spec, however, makes no such guarantee. + */ + onRGB = RED; + i = 0; + pPixelStart = pPixel; + while ( pBuffer < pBufferEnd ) + { + count = *pBuffer++; + if ( !(count & 0x80) ) + { + /* Take next 'count' bytes as literal. */ + while ( count-- ) + { + switch ( onRGB ) + { + case RED: + ImVfbSRed( vfb, pPixel, *pBuffer++ ); + ImVfbSInc( vfb, pPixel ); + if ( ++i >= pDim->dim_width ) + { + onRGB = GREEN; + i = 0; + pPixel = pPixelStart; + } + continue; + case GREEN: + ImVfbSGreen( vfb, pPixel, *pBuffer++ ); + ImVfbSInc( vfb, pPixel ); + if ( ++i >= pDim->dim_width ) + { + onRGB = BLUE; + i = 0; + pPixel = pPixelStart; + } + continue; + case BLUE: + ImVfbSBlue( vfb, pPixel, *pBuffer++ ); + ImVfbSInc( vfb, pPixel ); + if ( ++i >= pDim->dim_width ) + { + onRGB = RED; + i = 0; + pPixelStart = pPixel; + } + continue; + } + } + continue; + } + + /* Repeat next byte 'count' times. */ + count &= 0x7F; + while ( count-- ) + { + switch ( onRGB ) + { + case RED: + ImVfbSRed( vfb, pPixel, *pBuffer ); + ImVfbSInc( vfb, pPixel ); + if ( ++i >= pDim->dim_width ) + { + onRGB = GREEN; + i = 0; + pPixel = pPixelStart; + } + continue; + case GREEN: + ImVfbSGreen( vfb, pPixel, *pBuffer ); + ImVfbSInc( vfb, pPixel ); + if ( ++i >= pDim->dim_width ) + { + onRGB = BLUE; + i = 0; + pPixel = pPixelStart; + } + continue; + case BLUE: + ImVfbSBlue( vfb, pPixel, *pBuffer ); + ImVfbSInc( vfb, pPixel ); + if ( ++i >= pDim->dim_width ) + { + onRGB = RED; + i = 0; + pPixelStart = pPixel; + } + continue; + } + } + pBuffer++; + } + free( (char *)buffer ); + *pVfb = vfb; + return ( 0 ); +} + +static int /* Returns status */ +#ifdef __STDC__ +imHdfVfbReadRLERGBPlane( int ioType, int fd, FILE *fp, imHdfRIG *pRIG, ImVfb **pVfb ) +#else +imHdfVfbReadRLERGBPlane( ioType, fd, fp, pRIG, pVfb ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + imHdfRIG *pRIG; /* Image group description */ + ImVfb **pVfb; /* Returned VFB */ +#endif +{ + imHdfDim *pDim; /* Image dimension information */ + ImVfb *vfb; /* New VFB */ + ImVfbPtr pPixel; /* Pixel pointer */ + unsigned char *buffer; /* Input buffer */ + unsigned char *pBufferEnd; /* Pointer to end of buffer */ + unsigned char *pBuffer; /* Pointer into input buffer */ + unsigned char count; /* Run length count */ + int i, j; /* Counters */ + + + /* + * Allocate a new 24-bit index VFB. + */ + pDim = pRIG->rig_imageDim; + if ( (vfb = ImVfbAlloc( pDim->dim_width, pDim->dim_height, IMVFBRGB )) + == IMVFBNULL ) + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + + /* + * Allocate a buffer big enough for the whole image, read it in, + * then process the whole thing. This takes more memory, but we + * have no othe rway of alloating space sufficient for just one + * scanline, then reading in only that scanline. + */ + Seek( imHdfDDQDataOffset( pRIG->rig_imageDD ) ); + ImMalloc( buffer, unsigned char *, sizeof( unsigned char ) * + imHdfDDQDataLength( pRIG->rig_imageDD ) ); + pBuffer = buffer; + pBufferEnd = buffer + imHdfDDQDataLength( pRIG->rig_imageDD ); + Read( buffer, UCHAR, 1, imHdfDDQDataLength( pRIG->rig_imageDD ) ); + + j = pDim->dim_width * pDim->dim_height; + i = 0; + pPixel = ImVfbQFirst( vfb ); + while ( pBuffer < pBufferEnd && i < j ) + { + count = *pBuffer++; + if ( !(count & 0x80) ) + { + /* Take next 'count' bytes as literal. */ + i += count; + while ( count-- ) + { + ImVfbSRed( vfb, pPixel, *pBuffer++ ); + ImVfbSInc( vfb, pPixel ); + } + continue; + } + + /* Repeat next byte 'count' times. */ + count &= 0x7F; + i += count; + while ( count-- ) + { + ImVfbSRed( vfb, pPixel, *pBuffer ); + ImVfbSInc( vfb, pPixel ); + } + pBuffer++; + } + i = 0; + pPixel = ImVfbQFirst( vfb ); + while ( pBuffer < pBufferEnd && i < j ) + { + count = *pBuffer++; + if ( !(count & 0x80) ) + { + /* Take next 'count' bytes as literal. */ + i += count; + while ( count-- ) + { + ImVfbSGreen( vfb, pPixel, *pBuffer++ ); + ImVfbSInc( vfb, pPixel ); + } + continue; + } + + /* Repeat next byte 'count' times. */ + count &= 0x7F; + i += count; + while ( count-- ) + { + ImVfbSGreen( vfb, pPixel, *pBuffer ); + ImVfbSInc( vfb, pPixel ); + } + pBuffer++; + } + i = 0; + pPixel = ImVfbQFirst( vfb ); + while ( pBuffer < pBufferEnd && i < j ) + { + count = *pBuffer++; + if ( !(count & 0x80) ) + { + /* Take next 'count' bytes as literal. */ + i += count; + while ( count-- ) + { + ImVfbSBlue( vfb, pPixel, *pBuffer++ ); + ImVfbSInc( vfb, pPixel ); + } + continue; + } + + /* Repeat next byte 'count' times. */ + count &= 0x7F; + i += count; + while ( count-- ) + { + ImVfbSBlue( vfb, pPixel, *pBuffer ); + ImVfbSInc( vfb, pPixel ); + } + pBuffer++; + } + free( (char *)buffer ); + *pVfb = vfb; + return ( 0 ); +} diff --git a/utils/roq2/libim/imhdfwrite.c b/utils/roq2/libim/imhdfwrite.c new file mode 100644 index 0000000..c297701 --- /dev/null +++ b/utils/roq2/libim/imhdfwrite.c @@ -0,0 +1,3284 @@ +/** + + ** $Header: /roq/libim/imhdfwrite.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/imhdfwrite.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** imhdfwrite.c - HDF image file write + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** imhdf.c contains routines to write HDF image files for + + ** the image manipulation library. Raster data written + + ** out is taken from a tag table. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** ImHdfWrite f write an HDF file + + ** + + ** PRIVATE CONTENTS + + ** imHdfDDList v list of DDs + + ** imHdfDDListEnd v pointer to end of DD list + + ** imHdfDDCount v # of entries in DD list + + ** + + ** imHdfDDEmpty f empty the DD list + + ** imHdfDDAppend f append to the DD list + + ** imHdfDDFind f search the DD list + + ** + + ** imHdfCltList v list of CLT's being written out + + ** imHdfCltListEnd v end of the CLT list + + ** + + ** imHdfCltEmpty f empty the Clt list + + ** imHdfCltAppend f append to the Clt list + + ** imHdfCltFind f find entry based on its CLT pointer + + ** imHdfCltFindRef f find entry based on its LUT reference number + + ** + + ** imHdfByteOrder v data byte order + + ** imHdfFloatFormat v data float format + + ** imHdfRef v current reference number + + ** + + ** imHdfWrite f handle the writing of an HDF file + + ** imHdfDimWrite f write dimension tag to file + + ** imHdfCltWrite f write CLT to file + + ** imHdfVfbWrite f write VFB to file + + ** + + ** imHdfVfbWrite8 f write 8-bit uncomp. VFB + + ** imHdfVfbWriteRLE8 f write 8-bit RLE comp. VFB + + ** imHdfVfbWrite16 f write 16-bit uncomp. VFB + + ** imHdfVfbWriteRGB f write 24-bit uncomp. uninterleaved VFB + + ** imHdfVfbWriteRGBLine f write 24-bit uncomp. line interleaved VFB + + ** imHdfVfbWriteRGBPlan f write 24-bit uncomp. plane interleaved VFB + + ** imHdfVfbWriteRLERGB f write 24-bit RLE comp. uninterleaved VFB + + ** imHdfVfbWriteRLERGBLine f write 24-bit RLE comp. line interleaved VFB + + ** imHdfVfbWriteRLERGBPlane f write 24-bit RLE comp. plane interleaved VFB + + ** + + ** HISTORY + + ** $Log: /roq/libim/imhdfwrite.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.10 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.9 1994/10/03 11:30:12 nadeau + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Rearranged magic number structures for format handlers. + + ** Made format handler routines static (i.e., local to file). + + ** Updated comments, adding format descriptions and references. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.8 93/02/18 18:10:25 secoskyj + + ** Fixed carriage return being output before Image: output + + ** message. + + ** + + ** Revision 1.7 92/12/03 01:48:40 nadeau + + ** Corrected info messages. + + ** + + ** Revision 1.6 92/11/23 18:42:31 nadeau + + ** Removed use of IMINFOMSG. + + ** + + ** Revision 1.5 92/11/04 11:58:08 groening + + ** made minor changes in iminfo + + ** + + ** Revision 1.4 92/10/19 14:14:47 groening + + ** added ImInfo statements + + ** + + ** Revision 1.3 92/09/02 13:15:05 vle + + ** Updated copyright notice. + + ** + + ** Revision 1.2 92/04/09 09:34:58 groening + + ** To make the compiler happy added extern statements. + + ** + + ** Revision 1.1 91/10/03 09:05:31 nadeau + + ** Initial revision + + ** + + ** + + **/ + + + +#include "iminternal.h" + +#include "imhdfinternal.h" + + + +/** + + ** CODE CREDITS + + ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1992. + + **/ + + + + + + + +/* + + * Function declarations, since they return longs, rather than + + * integers. This caused a problem on a Cray. + + */ + + + +#ifdef __STDC__ + +static long imHdfVfbWrite8( int ioType, int fd, FILE *fp, ImVfb *vfb ); + +static long imHdfVfbWriteRLE8(int ioType, int fd, FILE *fp, ImVfb *vfb); + +static long imHdfVfbWrite16(int ioType, int fd, FILE *fp, ImVfb *vfb); + +static long imHdfVfbWriteRGB(int ioType, int fd, FILE *fp, ImVfb *vfb); + +static long imHdfVfbWriteRGBLine(int ioType, int fd, FILE *fp, ImVfb *vfb); + +static long imHdfVfbWriteRGBPlane(int ioType, int fd, FILE *fp, ImVfb *vfb); + +static long imHdfVfbWriteRLERGB(int ioType, int fd, FILE *fp, ImVfb *vfb); + +static long imHdfVfbWriteRLERGBLine(int ioType, int fd, FILE *fp, ImVfb *vfb); + +static long imHdfVfbWriteRLERGBPlane(int ioType, int fd, FILE *fp, ImVfb *vfb); + +static int imHdfCltWrite( int ioType, int fd, FILE *fp, ImClt *clt ); + +static int imHdfVfbWrite( int ioType, int fd, FILE *fp, int interRequest, int compRequest, int cltRequest, + + int alphaRequest, ImVfb *vfb ); + +#else + +static long imHdfVfbWrite8(); + +static long imHdfVfbWriteRLE8(); + +static long imHdfVfbWrite16(); + +static long imHdfVfbWriteRGB(); + +static long imHdfVfbWriteRGBLine(); + +static long imHdfVfbWriteRGBPlane(); + +static long imHdfVfbWriteRLERGB(); + +static long imHdfVfbWriteRLERGBLine(); + +static long imHdfVfbWriteRLERGBPlane(); + +static int imHdfCltWrite(); + +static int imHdfVfbWrite(); + +#endif + + + + + + + + + + + +/* + + * FORMAT + + * HDF - Hierarchical Data Format + + * + + * DESCRIPTION + + * See imhdfread.c. + + */ + + + + + + + + + + + +/* + + * FUNCTION + + * ImHdfWrite - write an HDF file + + * + + * DESCRIPTION + + * ImHdfWrite() walks the tag table and writes out CLT's and VFB's it + + * finds there. Because more than one VFB can use the same CLT, we'd + + * like to avoid writing out the same CLT more than once. CLT's are + + * added to a CLT list. As each new VFB is encountered, the CLT list + + * is checked to see if we've already written out its CLT. If so, + + * we just point to it. Otherwise that VFB's CLT is written out and + + * added to the list. + + * + + * After a VFB has been written out, a raster image group (RIG) is + + * created for all of the tags related to the image. + + * + + * As each new tag is written to the file, it is also added to a DD list. + + * When we're done, the DD list is written out as a DD block and we go + + * back and patch up the DD header at the top of the file to make it + + * point to the DD block at the end of the file. + + * + + * The tags written to the file are as follows (but not in this order): + + * + + * NCSA For + + * tools Image + + * Tag output? Depths? Meaning + + * + + * MT yes all Machine characteristics + + * + + * RIG yes all Raster image group tag list + + * + + * RI yes all Raster image (uncompressed) + + * CI yes all Compressed image + + * ID yes all Image dimensions + + * NT yes all Channel size/type + + * RLE no all Flag RLE compression scheme + + * CFM no all Color format + + * + + * LUT yes 8-bit CLT + + * LD no 8-bit CLT dimensions + + * + + * RI8 yes 8-bit Raster image (uncompressed) + + * CI8 yes 8-bit Compressed image + + * ID8 yes 8-bit Image dimensions + + * IP8 yes 8-bit CLT + + * + + * Note: some of the tags we output are standard tags that the HDF + + * spec states should be written, yet the NCSA tools and NCSA HDF + + * library doesn't normally write them. + + */ + + + + + +int /* Returns # of tags written */ + +#ifdef __STDC__ + +ImHdfWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +ImHdfWrite( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + TagTable *flagsTable; /* Format flags */ + + TagTable *tagTable; /* Tag list to add to */ + +#endif + +{ + + sdsc_uint16 nDD; /* # of DD's in block */ + + imHdfDD *pDD; /* DD list pointer */ + + unsigned char buffer[1024]; /* Temporary write buffer */ + + + + unsigned int tag; /* DD tag */ + + unsigned int ref; /* DD reference number */ + + long dataOffset; /* Offset to data */ + + long dataLength; /* Length of data */ + + + + BinMachineInfo *machine; /* Machine information */ + + ImClt *clt; /* CLT pointer */ + + ImVfb *vfb; /* VFB pointer */ + + char *s; /* Tag name string */ + + long offset; /* File offset to next DD header*/ + + + + int nTag = 0; /* # of tags written */ + + int n; /* # of tag table entries */ + + int i; /* Counter */ + + TagEntry *tagEntry; /* Tag table entry holder */ + + + + int interRequest; /* Interleave request */ + + int compRequest; /* Compression request */ + + int cltRequest; /* CLT request */ + + int alphaRequest; /* Alpha plane request */ + + int imageNum; /* number of vfb's in tagtable */ + + int imageNumII; /* number of vfb's in tagtable */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * Get the requests from the flags table. + + */ + + interRequest = IMINTERPLANE; + + compRequest = IMCOMPRLE; + + cltRequest = IMCLTYES; + + alphaRequest = IMALPHAYES; + + if ( flagsTable != TAGTABLENULL ) + + { + + tagEntry = TagTableQDirect( flagsTable, "image interleave request", 0 ); + + if ( tagEntry != TAGENTRYNULL ) + + TagEntryQValue( tagEntry, &interRequest ); + + /* interRequest is guaranteed to be one we can support. */ + + + + tagEntry = TagTableQDirect( flagsTable, "image compression request", 0 ); + + if ( tagEntry != TAGENTRYNULL ) + + TagEntryQValue( tagEntry, &compRequest ); + + /* compRequest is guaranteed to be one we can support. */ + + + + tagEntry = TagTableQDirect( flagsTable, "image clt request", 0 ); + + if ( tagEntry != TAGENTRYNULL ) + + TagEntryQValue( tagEntry, &cltRequest ); + + /* cltRequest is guaranteed to be one we can support. */ + + + + tagEntry = TagTableQDirect( flagsTable, "image alpha request", 0 ); + + if ( tagEntry != TAGENTRYNULL ) + + TagEntryQValue( tagEntry, &alphaRequest ); + + /* alphaRequest is guaranteed to be one we can support. */ + + } + + + + + + /* + + * All administrative stuff (tags, reference counts, file offsets) + + * are in MBF byte order. + + */ + + BinByteOrder( BINMBF ); + + + + + + /* + + * Write the magic number. + + */ + + buffer[0] = 0x0E; /* CTRL-N */ + + buffer[1] = 0x03; /* CTRL-C */ + + buffer[2] = 0x13; /* CTRL-S */ + + buffer[3] = 0x01; /* CTRL-A */ + + Write( buffer, UCHAR, 1, 4 ); + + + + + + /* + + * Write out an initial data block with one DD (data descriptor) + + * describing the machine type (MT tag). The rest of the data + + * for the file will be listed in another DD placed at the end of + + * the file. When we're done, we'll come back to this block and + + * update its offset to point to that final DD. + + */ + + nDD = 1; /* 1 DD in this block */ + + Write( &nDD, UINT16, 2, 1 ); + + offset = 0; /* Filled in later */ + + Write( &offset, LONG, 4, 1 ); + + + + tag = IMHDFTMT; /* MT = machine type */ + + dataOffset = 0; /* No data */ + + dataLength = 0; /* No data */ + + + + machine = BinQMachine( ); + + ref = 0; + + imHdfByteOrder = machine->bin_byteOrder; + + switch ( machine->bin_byteOrder ) + + { + + case BINLBF: /* Least-significant byte first */ + + ImInfo ("Byte Order","Least Significant Byte First"); + + ref = (IMHDFINTVBO<<12); /* unsigned ints are Vax bo */ + + break; + + case BINMBF: /* Most-significant byte first */ + + ImInfo ("Byte Order","Most Significant Byte First"); + + ref = (IMHDFINTMBO<<12); /* unsigned ints are Motorola bo*/ + + break; + + } + + + + imHdfFloatFormat = machine->bin_floatFormat; + + switch ( machine->bin_floatFormat ) + + { + + case BINIEEE: /* IEEE floats and doubles */ + + if ( machine->bin_byteOrder == BINMBF ) + + ref |= (IMHDFFLOATIEEE<<8) | (IMHDFFLOATIEEE<<4); + + else + + ref |= (IMHDFFLOATPC<<8) | (IMHDFFLOATPC<<4); + + break; + + case BINVAX: /* Vax floats and doubles */ + + ref |= (IMHDFFLOATVAX<<8) | (IMHDFFLOATVAX<<4); + + break; + + case BINCRAYMP: /* Cray floats and doubles */ + + ref |= (IMHDFFLOATCRAY<<8) | (IMHDFFLOATCRAY<<4); + + break; + + } + + + + ref |= IMHDFCHARASCII; /* unsigned chars are in ASCII */ + + + + Write( &tag, UINT, 2, 1 ); + + Write( &ref, UINT, 2, 1 ); + + Write( &dataOffset, LONG, 4, 1 ); + + Write( &dataLength, LONG, 4, 1 ); + + + + + + + + /* + + * Walk the tag table, watching for CLT's and VFB's. + + */ + + BinByteOrder( imHdfByteOrder ); + + BinFloatFormat( imHdfFloatFormat ); + + n = TagTableQNEntry( tagTable, NULL ); + + imHdfDDEmpty( ); + + imHdfRef = 100; + + + + /* count number of vfb's */ + + imageNum=0; + + imageNumII=1; + + for ( i = 0; i < n; i++, imHdfRef += 5 ) + + { + + tagEntry = TagTableQLinear( tagTable, i ); + + s = TagEntryQTag( tagEntry ); + + if ( strcmp( s, "image vfb" ) == 0 ) imageNum++; + + } + + + + for ( i = 0; i < n; i++, imHdfRef += 5 ) + + { + + tagEntry = TagTableQLinear( tagTable, i ); + + s = TagEntryQTag( tagEntry ); + + + + if ( (cltRequest == IMCLTYES) && strcmp( s, "image clt" ) == 0 ) + + { + + TagEntryQValue( tagEntry, &clt ); + + if ( (nTag += imHdfCltWrite( ioType, fd, fp, clt ))== -1) + + return ( -1 ); /* Error already posted */ + + } + + + + if ( strcmp( s, "image vfb" ) == 0 ) + + { + + sprintf (message, "%d of %d",imageNumII++,imageNum); + + ImInfo ("Image",message); + + TagEntryQValue( tagEntry, &vfb ); + + if ( (nTag += imHdfVfbWrite( ioType, fd, fp, + + interRequest, compRequest, cltRequest, + + alphaRequest, vfb ))== -1) + + return ( -1 ); /* Error already posted */ + + } + + + + /* + + * Ignore all other tags. + + */ + + } + + + + + + /* + + * Write out the DD list, preceded by its header. + + */ + + BinByteOrder( BINMBF ); + + offset = Tell( ); + + nDD = imHdfDDQNEntry( ); + + dataOffset = 0; /* End of header list */ + + Write( &nDD, UINT16, 2, 1 ); + + Write( &dataOffset, LONG, 4, 1 ); + + + + for ( pDD = imHdfDDQFirst( ); pDD; pDD = imHdfDDQNext( pDD ) ) + + { + + tag = imHdfDDQTag( pDD ); + + ref = imHdfDDQRef( pDD ); + + dataOffset = imHdfDDQDataOffset( pDD ); + + dataLength = imHdfDDQDataLength( pDD ); + + + + Write( &tag, UINT, 2, 1 ); + + Write( &ref, UINT, 2, 1 ); + + Write( &dataOffset, LONG, 4, 1 ); + + Write( &dataLength, LONG, 4, 1 ); + + } + + + + + + /* + + * Seek back to the first DD header nad change its offset pointer + + * to point to the new DD header we just added at the end of the file. + + */ + + Seek( 4 + 2 ); /* Seek to 6th byte */ + + Write( &offset, LONG, 4, 1 ); + + + + return ( nTag ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imHdfDimWrite - write dimension tag to file + + * + + * DESCRIPTION + + * Tags describing a dimension are written out: + + * + + * NT numeric type + + * RLE compression scheme + + * + + * The dimension tag's data itself is also written out. + + */ + + + +static long /* Returns file offset */ + +#ifdef __STDC__ + +imHdfDimWrite( int ioType, int fd, FILE *fp, int tag, imHdfDim *pDim ) + +#else + +imHdfDimWrite( ioType, fd, fp, tag, pDim ) + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + int tag; /* Tag type */ + + imHdfDim *pDim; /* Dimension information */ + +#endif + +{ + + long offset; /* File offset */ + + unsigned char nt[4]; /* NT buffer */ + + sdsc_uint16 tmpTag; /* Temp tag holder */ + + sdsc_uint16 tmpRef; /* Temp ref holder */ + + + + + + /* + + * Write out an NT tag's data describing the type for a channel. + + * + + * The NT tag's data is 4 1-byte quantities: + + * + + * 0 version number of NT tag (version 1 supported here) + + * 1 type code + + * 2 width of type, in bits + + * 3 class code (byte order, float format, etc) + + */ + + offset = Tell( ); + + nt[0] = 1; /* Version 1 */ + + if ( pDim->dim_channelType == UINT ) + + { + + nt[1] = IMHDFNTUINT; + + if ( pDim->dim_channelByteOrder == BINLBF ) + + nt[3] = IMHDFINTVBO; + + else + + nt[3] = IMHDFINTMBO; + + } + + else + + { + + nt[1] = IMHDFNTUCHAR; + + nt[3] = IMHDFCHARBYTE; /* bitwise numeric */ + + } + + nt[2] = pDim->dim_channelSize * 8; + + Write( nt, UCHAR, 1, 4 ); + + if ( tag == IMHDFTID ) + + tmpRef = imHdfRef; + + else + + tmpRef = imHdfRef + 1; + + imHdfDDAppend( IMHDFTNT, tmpRef, offset, 4 ); + + + + + + /* + + * Add a no-data RLE tag if we are doing compression. + + */ + + if ( pDim->dim_compression == IMHDFTRLE ) + + imHdfDDAppend( IMHDFTRLE, imHdfRef, 0, 0 ); + + + + + + /* + + * Write out the dimension tag. + + */ + + offset = Tell( ); + + tmpTag = IMHDFTNT; + + Write( &pDim->dim_width, INT, 4, 1 ); + + Write( &pDim->dim_height, INT, 4, 1 ); + + Write( &tmpTag, UINT16, 2, 1 ); + + Write( &tmpRef, UINT16, 2, 1 ); + + Write( &pDim->dim_pixelSize, INT, 2, 1 ); + + Write( &pDim->dim_interleave, INT, 2, 1 ); + + Write( &pDim->dim_compression, UINT, 2, 1 ); + + if ( pDim->dim_compression == 0 ) + + { + + tmpRef = 0; + + Write( &tmpRef, UINT16, 2, 1 ); + + } + + else + + Write( &imHdfRef, UINT16, 2, 1 ); + + imHdfDDAppend( tag, imHdfRef, offset, 20 ); + + + + return ( 0 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imHdfCltWrite - write CLT to file + + * + + * DESCRIPTION + + * Tags describing a CLT are written out: + + * + + * LD lookup table dimensions + + * LUT lookup table + + * IP8 8-bit lookup table (if 256 or fewer CLT entries) + + * + + * The CLT is written out uncompressed and uninterleaved. + + */ + + + +static int /* Returns # of tags written */ + +#ifdef __STDC__ + +imHdfCltWrite( int ioType, int fd, FILE *fp, ImClt *clt ) + +#else + +imHdfCltWrite( ioType, fd, fp, clt ) + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + ImClt *clt; /* CLT to write out */ + +#endif + +{ + + imHdfDim dim; /* Dimensions of CLT */ + + long offset; /* tag data offset */ + + int i; /* Counter */ + + ImCltPtr pColor; /* CLT color pointer */ + + unsigned char *buffer; /* RGB buffer */ + + int n; /* # of colors */ + + + + + + /* + + * Write out an LD dimensions tag. + + * Width is # of colors + + * Height is always 1 + + * Each channel is a unsigned char (byte order & float format unused) + + * 3 channels per pixel (RGB) + + * CLT not interleaved or compressed + + */ + + + + dim.dim_width = n = ImCltQNColors( clt ); + + dim.dim_height = 1; + + dim.dim_channelType = UCHAR; + + dim.dim_channelSize = 1; + + dim.dim_channelByteOrder = imHdfByteOrder; + + dim.dim_channelFloatFormat = imHdfFloatFormat; + + dim.dim_pixelSize = 3; + + dim.dim_interleave = 0; + + dim.dim_compression = 0; + + + + if ( imHdfDimWrite( ioType, fd, fp, IMHDFTLD, &dim ) == -1 ) + + return ( -1 ); /* Error stuff already handled */ + + + + + + /* + + * Write out the CLT, not interleaved or compressed. + + */ + + offset = Tell( ); + + n *= 3; + + if ( n < 256*3 ) + + { + + ImMalloc( buffer, unsigned char *, sizeof( unsigned char ) * 256 * 3 ); + + } + + else + + { + + ImMalloc( buffer, unsigned char *, sizeof( unsigned char ) * n ); + + } + + pColor = ImCltQFirst( clt ); + + for ( i = 0; i < n; ) + + { + + buffer[i++] = ImCltQRed( pColor ); + + buffer[i++] = ImCltQGreen( pColor ); + + buffer[i++] = ImCltQBlue( pColor ); + + ImCltSInc( clt, pColor ); + + } + + if ( dim.dim_width <= 256 ) + + { + + /* + + * With CLT's of 256 entries or less, we can support the + + * IP8 tag. However, IP8 palettes must be 256 entries + + * long. So, we pad the CLT with enough zero entries to + + * make it the right size. + + * + + * The LD tag, which allows arbitrary CLT lengths, will only + + * report the number of entries filled with real data, not + + * our pad entries. + + * + + * The IP8 tag, which assumes 256 CLT entries, will have + + * a data length that includes the pad entries. + + * + + * The LUT tag, which allows any CLT length, will have a + + * data length that does not include the pad entries. + + */ + + while ( i < 256*3 ) + + { + + buffer[i++] = 0; + + buffer[i++] = 0; + + buffer[i++] = 0; + + } + + imHdfDDAppend( IMHDFTIP8, imHdfRef, offset, 256 * 3 ); + + Write( buffer, UCHAR, 1, 256 * 3 ); + + } + + else + + Write( buffer, UCHAR, 1, n ); + + free( (char *)buffer ); + + imHdfDDAppend( IMHDFTLUT, imHdfRef, offset, n ); + + + + imHdfCltAppend( clt, imHdfRef, imHdfRef ); + + + + return ( 1 ); /* Wrote CLT from tag table */ + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imHdfVfbWrite - write VFB to file + + * + + * DESCRIPTION + + * The tags associated with a VFB are written out: + + * + + * ID image dimensions + + * RI raster image + + * CI compressed raster image + + * RLE run-length encoded + + * CFM color format + + * RIG raster image group + + * + + * The image is written out as either a compressed or uncompressed + + * image, uninterleaved, scanline interleaved, or plane interleaved, + + * all as selected by the interRequest from the flagsTable. + + */ + + + +static int /* Returns # of tags written */ + +#ifdef __STDC__ + +imHdfVfbWrite( int ioType, int fd, FILE *fp, int interRequest, int compRequest, int cltRequest, int alphaRequest, ImVfb *vfb ) + +#else + +imHdfVfbWrite( ioType, fd, fp, interRequest, compRequest, cltRequest, alphaRequest, vfb ) + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + int interRequest; /* Interleave request */ + + int compRequest; /* Compression request */ + + int cltRequest; /* CLT channel request */ + + int alphaRequest; /* Alpha channel request */ + + ImVfb *vfb; /* VFB to write out */ + +#endif + +{ + + int i; /* Counter */ + + imHdfDim dim; /* Dimensions of VFB */ + + ImVfbPtr pPixel; /* VFB pixel pointer */ + + ImClt *clt; /* VFB's CLT */ + + imHdfClt *hdfClt; /* CLT list entry */ + + int n; /* # of colors */ + + long offset; /* tag data offset */ + + unsigned int fields; /* Field mask */ + + int nTag = 0; /* # of tags written */ + + unsigned char *buffer; /* RGB buffer */ + + int nRIG = 0; /* # of items in the RIG */ + + unsigned int tagList[50]; /* RIG tags */ + + unsigned int refList[50]; /* RIG refs */ + + TagEntry *tagEntry; /* Tag table entry holder */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * If the VFB has a CLT, look it up in the CLT list to see if it + + * has already been written out. + + */ + + if ( (cltRequest == IMCLTYES) && (clt = ImVfbQClt( vfb )) != IMCLTNULL ) + + { + + if ( (hdfClt = imHdfCltFind( clt )) == NULL ) + + { + + + + sprintf (message, "%d Entries", ImCltQNColors (clt)); + + ImInfo ("Color Table",message); + + /* + + * Not already written. Write it and add it to the + + * written CLT list. + + */ + + if ( (nTag = imHdfCltWrite( ioType, fd, fp, clt ))== -1) + + return ( -1 ); /* Error already posted */ + + hdfClt = imHdfCltFind( clt ); + + } + + tagList[nRIG] = IMHDFTLD; + + refList[nRIG++] = imHdfCltQRefLD( hdfClt ); + + tagList[nRIG] = IMHDFTLUT; + + refList[nRIG++] = imHdfCltQRefLUT( hdfClt ); + + } + + + + + + /* + + * Write out an ID dimensions tag. + + * Width and height of image + + * Each channel is a unsigned char (RGB & INDEX8) or a unsigned int (INDEX16) + + * Machine's byte order and float format + + * 3 channels per pixel (RGB) or 1 per pixel (INDEX8 & INDEX16) + + * Uninterleaved, scanline interleaved, or plane interleaved + + * Compressed or uncompressed + + */ + + + + dim.dim_width = ImVfbQWidth( vfb ); + + dim.dim_height = ImVfbQHeight( vfb ); + + dim.dim_channelByteOrder = imHdfByteOrder; + + dim.dim_channelFloatFormat = imHdfFloatFormat; + + fields = ImVfbQFields( vfb ); + + if ( fields & IMVFBINDEX8 ) + + { + + dim.dim_channelType= UCHAR; + + dim.dim_pixelSize = 1; + + dim.dim_channelSize= 1; + + } + + else if ( fields & IMVFBINDEX16 ) + + { + + dim.dim_channelType= UINT; + + dim.dim_pixelSize = 1; + + clt = ImVfbQClt( vfb ); + + if ( clt != IMCLTNULL ) + + { + + n = ImCltQNColors( clt ); + + if ( n < 256 ) /* 8-bit */ + + dim.dim_channelSize = 1; + + else if ( n < 65536 ) /* 16-bit */ + + dim.dim_channelSize = 2; + + else if ( n < 16777216 ) /* 24-bit */ + + dim.dim_channelSize = 3; + + else /* 32-bit */ + + dim.dim_channelSize = 4; + + } + + else + + dim.dim_channelSize = 4; + + } + + else + + { + + /* RGB */ + + dim.dim_channelType = UCHAR; + + dim.dim_pixelSize = 3; + + dim.dim_channelSize= 1; + + } + + + + if ( dim.dim_pixelSize == 1 ) + + dim.dim_interleave = 0; /* Can't interleave indexes*/ + + else if ( interRequest == IMINTERLINE ) + + dim.dim_interleave = 1; /* Scanline interleaved */ + + else if ( interRequest == IMINTERPLANE ) + + dim.dim_interleave = 2; /* Plane interleaved */ + + else + + dim.dim_interleave = 0; /* Uninterleaved */ + + + + if ( (dim.dim_channelType != UINT) && (compRequest == IMCOMPRLE) ) + + dim.dim_compression= IMHDFTRLE; /* RLE compression */ + + else + + dim.dim_compression= 0; /* No compression */ + + + + if ( imHdfDimWrite( ioType, fd, fp, IMHDFTID, &dim ) == -1 ) + + return ( -1 ); /* Error stuff already handled */ + + tagList[nRIG] = IMHDFTID; + + refList[nRIG++] = imHdfRef; + + + + + + /* + + * If the image is an 8-bit image, write out an ID8 tag. + + */ + + if ( fields & IMVFBINDEX8 ) + + { + + offset = Tell( ); + + Write( &dim.dim_width, INT, 2, 1 ); + + Write( &dim.dim_height, INT, 2, 1 ); + + imHdfDDAppend( IMHDFTID8, imHdfRef, offset, 4 ); + + } + + + + + + /* + + * Write out the color format used + + */ + + if ( dim.dim_pixelSize == 1 ) /* 8-bit and 16-bit indexes*/ + + { + + offset = Tell( ); + + Write( "VALUE", CHAR, 1, 7 ); + + imHdfDDAppend( IMHDFTCFM, imHdfRef, offset, 7 ); + + tagList[nRIG] = IMHDFTCFM; + + refList[nRIG++] = imHdfRef; + + } + + else + + { + + offset = Tell( ); + + Write( "RGB", CHAR, 1, 4 ); + + imHdfDDAppend( IMHDFTCFM, imHdfRef, offset, 4 ); + + tagList[nRIG] = IMHDFTCFM; + + refList[nRIG++] = imHdfRef; + + } + + + + sprintf (message, "%d x %d",dim.dim_width, dim.dim_height); + + ImInfo ("Resolution", message); + + + + /* + + * Write out the VFB. + + */ + + offset = Tell( ); + + if ( fields & IMVFBINDEX8 ) + + { + + switch ( dim.dim_compression ) + + { + + case 0: /* No compression. */ + + ImInfo ("Type","8-bit Color Indexed"); + + ImInfo ("Compression Type","none"); + + if ( (n = imHdfVfbWrite8( ioType, fd, fp, vfb )) == -1 ) + + return ( -1 ); /* Errors already handled*/ + + imHdfDDAppend( IMHDFTRI8, imHdfRef, offset, n ); + + imHdfDDAppend( IMHDFTRI, imHdfRef, offset, n ); + + tagList[nRIG] = IMHDFTRI; + + refList[nRIG++] = imHdfRef; + + break; + + + + case IMHDFTRLE: /* Run-length encoding */ + + ImInfo ("Type","8-bit Color Indexed"); + + ImInfo ("Compression Type","Run Length Encoded (RLE)"); + + if ( (n = imHdfVfbWriteRLE8( ioType, fd, fp, vfb )) == -1 ) + + return ( -1 ); /* Errors already handled*/ + + imHdfDDAppend( IMHDFTCI8, imHdfRef, offset, n ); + + imHdfDDAppend( IMHDFTCI, imHdfRef, offset, n ); + + tagList[nRIG] = IMHDFTCI; + + refList[nRIG++] = imHdfRef; + + break; + + } + + } + + else if ( fields & IMVFBINDEX16 ) + + { + + ImInfo ("Type","16-bit Color Indexed"); + + ImInfo ("Compression Type","none"); + + if ( (n = imHdfVfbWrite16( ioType, fd, fp, vfb )) == -1 ) + + return ( -1 ); /* Errors already handled*/ + + imHdfDDAppend( IMHDFTRI, imHdfRef, offset, n ); + + tagList[nRIG] = IMHDFTRI; + + refList[nRIG++] = imHdfRef; + + } + + else /* RGB */ + + { + + switch ( dim.dim_compression ) + + { + + case 0: /* No compression. */ + + switch ( dim.dim_interleave ) + + { + + case 0: /* Uninterleaved */ + + ImInfo ("Type","24-bit RGB"); + + ImInfo ("Compression Type","none"); + + ImInfo ("Interleave Type","none (Non-interleaved)"); + + if ( (n = imHdfVfbWriteRGB( ioType, fd, fp, + + vfb )) == -1 ) + + return ( -1 ); /* Errors done*/ + + break; + + case 1: /* Scanline interleaved */ + + ImInfo ("Type","24-bit RGB"); + + ImInfo ("Compression Type","none"); + + ImInfo ("Interleave Type","Scanline"); + + if ( (n = imHdfVfbWriteRGBLine( ioType, fd, fp, + + vfb )) == -1 ) + + return ( -1 ); /* Errors done*/ + + break; + + case 2: /* Plane interleaved */ + + ImInfo ("Type","24-bit RGB"); + + ImInfo ("Compression Type","none"); + + ImInfo ("Interleave Type","Plane"); + + if ( (n = imHdfVfbWriteRGBPlane( ioType, fd, fp, + + vfb )) == -1 ) + + return ( -1 ); /* Errors done*/ + + break; + + } + + imHdfDDAppend( IMHDFTRI, imHdfRef, offset, n ); + + tagList[nRIG] = IMHDFTRI; + + refList[nRIG++] = imHdfRef; + + break; + + + + case IMHDFTRLE: /* Run-length encoding */ + + switch ( dim.dim_interleave ) + + { + + case 0: /* Uninterleaved */ + + ImInfo ("Type","24-bit RGB"); + + ImInfo ("Compression Type","Run Length Encoded (RLE)"); + + ImInfo ("Interleave Type","none (Non-interleaved)"); + + if ( (n = imHdfVfbWriteRLERGB( ioType, fd, fp, + + vfb )) == -1 ) + + return ( -1 ); /* Errors done*/ + + break; + + case 1: /* Scanline interleaved */ + + ImInfo ("Type","24-bit RGB"); + + ImInfo ("Compression Type","Run Length Encoded (RLE)"); + + ImInfo ("Interleave Type","Scanline"); + + if ( (n = imHdfVfbWriteRLERGBLine( ioType,fd,fp, + + vfb )) == -1 ) + + return ( -1 ); /* Errors done*/ + + break; + + case 2: /* Plane interleaved */ + + ImInfo ("Type","24-bit RGB"); + + ImInfo ("Compression Type","Run Length Encoded (RLE)"); + + ImInfo ("Interleave Type","Plane"); + + if ( (n = imHdfVfbWriteRLERGBPlane( ioType, fd, fp, + + vfb )) == -1 ) + + return ( -1 ); /* Errors done*/ + + break; + + } + + imHdfDDAppend( IMHDFTCI, imHdfRef, offset, n ); + + tagList[nRIG] = IMHDFTCI; + + refList[nRIG++] = imHdfRef; + + break; + + } + + } + + + + + + /* + + * Write out the RIG + + */ + + offset = Tell( ); + + for ( i = 0; i < nRIG; i++ ) + + { + + Write( &tagList[i], UINT, 2, 1 ); + + Write( &refList[i], UINT, 2, 1 ); + + } + + imHdfDDAppend( IMHDFTRIG, imHdfRef, offset, nRIG * 4 ); + + + + return ( nTag + 1 ); /* VFB and optional CLT */ + +} + + + + + + + + + + + +/* + + * MACRO + + * IMADDTOBUFFER - add index to run buffer + + * + + * DESCRIPTION + + * Each of the RLE routines which follow use the same basic scheme + + * for adding a new value to the run buffer. Their only differences + + * are in how the get the value to add (red, green, blue, index8, or + + * index16). To make the code easier (and shorter), this macro + + * implements the add-value-to-buffer scheme. It assumes all of + + * these variables have been declared by the caller and that the + + * value to add is in 'index'. + + */ + + + +#define IMADDTOBUFFER() \ +{ \ + if ( index != oldIndex ) \ + { \ + /* Another unique pixel. */ \ + if ( count < 0 ) \ + { \ + /* Fill in run count first. */ \ + *pCount = ((unsigned char)(-count)) | 0x80; \ + count = 0; \ + pCount = rbp++; \ + } \ + else if ( count >= 127 ) \ + { \ + /* Fill in run count first. */ \ + *pCount = (unsigned char)count; \ + count = 0; \ + pCount = rbp++; \ + } \ + \ + /* Increase the unique pixel count */ \ + oldIndex = index; \ + ++count; \ + *rbp++ = index; \ + continue; \ + } \ + \ + /* Another non-unique pixel for the run. */ \ + if ( count <= -127 ) \ + { \ + *pCount = ((unsigned char)(-count)) | 0x80; \ + count = 1; \ + pCount = rbp++; \ + *rbp++ = index; \ + oldIndex = index; \ + continue; \ + } \ + \ + if ( count < 0 ) \ + { \ + /* Continue the run */ \ + --count; \ + continue; \ + } \ + \ + if ( count == 1 ) \ + { \ + /* Change unique pixel to a run of 2 */ \ + count = -2; \ + continue; \ + } \ + \ + *pCount = (unsigned char)(count - 1); \ + oldIndex = index; \ + count = -2; \ + pCount = rbp - 1; \ + *rbp++ = index; \ +} + + + + + + + + + + + +/* + + * MACROS + + * IMCOPYPLANE - copy a plane from a VFB to the output file + + * IMENCODEPLANE - encode a plane from a VFB to the output file + + * + + * DESCRIPTION + + * These macros represent commonly used code to take information from + + * a VFB and write it out to the output file. This code is placed + + * into macros to make it easier to maintain the same algorithm used + + * by multiple functions, without the additional overhead of another + + * subroutine call. + + * + + * IMCOPYPLANE() uses the given query functions (such as ImVfbQIndex8() + + * or ImVfbQRed()), calls it to get the next pixel value, and copies + + * it into the run buffer. After each scanline, it writes out the + + * run buffer. + + * + + * IMENCODEPLANE() uses the query function, calls it to get the next + + * pixel value, and worries about run-length encoding (using + + * IMADDTOBUFFER()). After each scanline, it writes out the run buffer. + + * + + * These macros assume that all the appropriate variables have been + + * declared by the caller. + + */ + + + +#define IMCOPYPLANE(query,type,size) \ +{ \ + for ( y = 0; y < height; y++ ) \ + { \ + rbp = runBuffer; \ + for ( x = 0; x < width; x++ ) \ + { \ + *rbp++ = query( vfb, pPixel ); \ + ImVfbSInc( vfb, pPixel ); \ + } \ + Write( runBuffer, type, size, width ); \ + } \ +} + + + +#define IMENCODEPLANE(query,type,size) \ +{ \ + for ( y = 0; y < height; y++ ) \ + { \ + rbp = runBuffer; \ + oldIndex = query( vfb, pPixel ); \ + count = 1; \ + ImVfbSInc( vfb, pPixel ); \ + pCount = rbp++; \ + *rbp++ = oldIndex; \ + for ( x = 1; x < width; x++ ) \ + { \ + index = query( vfb, pPixel ); \ + ImVfbSInc( vfb, pPixel ); \ + IMADDTOBUFFER( ); \ + } \ + if ( count < 0 ) \ + *pCount = ((unsigned char)(-count)) | 0x80; \ + else \ + *pCount = (unsigned char)count; \ + Write( runBuffer, type, size, (rbp - runBuffer) ); \ + nBytes += (rbp - runBuffer); \ + } \ +} + + + + + + + + + +/* + + * FUNCTION + + * imHdfVfbWrite8 - write 8-bit uncomp. VFB + + * imHdfVfbWriteRLE8 - write 8-bit RLE comp. VFB + + * + + * imHdfVfbWrite16 - write 16-bit uncomp. VFB + + * + + * imHdfVfbWriteRGB - write 24-bit uncomp. uninterleaved VFB + + * imHdfVfbWriteRGBLine - write 24-bit uncomp. line interleaved VFB + + * imHdfVfbWriteRGBPlane - write 24-bit uncomp. plane interleaved VFB + + * + + * imHdfVfbWriteRLERGB - write 24-bit RLE comp. uninterleaved VFB + + * imHdfVfbWriteRLERGBLine - write 24-bit RLE comp. line interleaved VFB + + * imHdfVfbWriteRLERGBPlane- write 24-bit RLE comp. plane interleaved VFB + + * + + * DESCRIPTION + + * The given VFB is written out as an 8-bit, 16-bit, or 24-bit, + + * uncompressed or RLE compressed, uninterleaved, scanline interleaved, + + * or plane interleaved image. + + */ + + + +static long /* Returns number of bytes */ + +#ifdef __STDC__ + +imHdfVfbWrite8( int ioType, int fd, FILE *fp, ImVfb *vfb ) + +#else + +imHdfVfbWrite8( ioType, fd, fp, vfb ) + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + ImVfb *vfb; /* VFB to write out */ + +#endif + +{ + + int width, height; /* Image size */ + + int x, y; /* X and Y image coordinates */ + + unsigned char *runBuffer; /* Buffered up runs */ + + unsigned char *rbp; /* Run buffer pointer */ + + ImVfbPtr pPixel; /* VFB pixel pointer */ + + + + + + /* + + * Write out the uncompressed 8-bit index VFB. + + */ + + pPixel = ImVfbQFirst( vfb ); + + width = ImVfbQWidth( vfb ); + + height = ImVfbQHeight( vfb ); + + ImMalloc( runBuffer, unsigned char *, sizeof( unsigned char ) * width ); + + + + IMCOPYPLANE( ImVfbQIndex8, UCHAR, 1 ); + + + + free( (char *)runBuffer ); + + + + return ( width * height ); + +} + + + +static long /* Returns number of bytes */ + +#ifdef __STDC__ + +imHdfVfbWriteRLE8( int ioType, int fd, FILE *fp, ImVfb *vfb ) + +#else + +imHdfVfbWriteRLE8( ioType, fd, fp, vfb ) + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + ImVfb *vfb; /* VFB to write out */ + +#endif + +{ + + int width, height; /* Image size */ + + int x, y; /* X and Y image coordinates */ + + unsigned char *runBuffer; /* Buffered up runs */ + + unsigned char *rbp; /* Run buffer pointer */ + + unsigned char *pCount; /* Last run's count byte */ + + + + int index; /* New pixel index */ + + int oldIndex; /* Old pixel index */ + + int count; /* Run length */ + + ImVfbPtr pPixel; /* VFB pixel pointer */ + + + + long nBytes = 0; /* Total number of bytes written*/ + + + + + + /* + + * Run-length encode the 8-bit index VFB. + + * Worst case compression: + + * input: ABB + + * output: 1A2BB + + * Worst case takes 5/3 * width bytes. Run buffer is + + * allocated to be big enough for the worst case. + + */ + + pPixel = ImVfbQFirst( vfb ); + + width = ImVfbQWidth( vfb ); + + height = ImVfbQHeight( vfb ); + + ImMalloc( runBuffer, unsigned char *, sizeof( unsigned char ) * width * 2 ); + + + + IMENCODEPLANE( ImVfbQIndex8, UCHAR, 1 ); + + + + free( (char *)runBuffer ); + + + + return ( nBytes ); + +} + + + +static long /* Returns number of bytes */ + +#ifdef __STDC__ + +imHdfVfbWrite16( int ioType, int fd, FILE *fp, ImVfb *vfb ) + +#else + +imHdfVfbWrite16( ioType, fd, fp, vfb ) + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + ImVfb *vfb; /* VFB to write out */ + +#endif + +{ + + int width, height; /* Image size */ + + int x, y; /* X and Y image coordinates */ + + sdsc_uint32 *runBuffer; /* Buffered up runs */ + + sdsc_uint32 *rbp; /* Run buffer pointer */ + + ImVfbPtr pPixel; /* VFB pixel pointer */ + + ImClt *clt; /* VFB's clt */ + + int n; /* Number of colors */ + + int b; /* Number of bytes when in file */ + + + + + + /* + + * Write out the uncompressed 16-bit index VFB. + + * Worst case compression: + + * input: ABB + + * output: 1A2BB + + * Worst case takes 5/3 * width bytes. Run buffer is + + * allocated to be big enough for the worst case. + + */ + + pPixel = ImVfbQFirst( vfb ); + + width = ImVfbQWidth( vfb ); + + height = ImVfbQHeight( vfb ); + + ImMalloc( runBuffer, sdsc_uint32 *, sizeof( sdsc_uint32 ) * width * 2 ); + + + + clt = ImVfbQClt( vfb ); + + if ( clt != IMCLTNULL ) + + { + + n = ImCltQNColors( clt ); + + if ( n <= 256 ) /* 8-bit */ + + b = 1; + + else /* 16-bit */ + + b = 2; + + } + + else + + b = 2; + + + + IMCOPYPLANE( ImVfbQIndex16, UINT32, b ); + + + + free( (char *)runBuffer ); + + + + return ( width * height ); + +} + + + +static long /* Returns number of bytes */ + +#ifdef __STDC__ + +imHdfVfbWriteRGB( int ioType, int fd, FILE *fp, ImVfb *vfb ) + +#else + +imHdfVfbWriteRGB( ioType, fd, fp, vfb ) + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + ImVfb *vfb; /* VFB to write out */ + +#endif + +{ + + int width, height; /* Image size */ + + int width3; /* Width * 3 */ + + int x, y; /* X and Y image coordinates */ + + unsigned char *runBuffer; /* Buffered up runs */ + + unsigned char *rbp; /* Run buffer pointer */ + + ImVfbPtr pPixel; /* VFB pixel pointer */ + + + + + + /* + + * Write out the uncompressed 24-bit RGB VFB, uninterleaved. + + */ + + pPixel = ImVfbQFirst( vfb ); + + width = ImVfbQWidth( vfb ); + + width3 = width * 3; + + height = ImVfbQHeight( vfb ); + + ImMalloc( runBuffer, unsigned char *, sizeof( unsigned char ) * width3 ); + + + + for ( y = 0; y < height; y++ ) + + { + + rbp = runBuffer; + + for ( x = 0; x < width; x++ ) + + { + + *rbp++ = ImVfbQRed( vfb, pPixel ); + + *rbp++ = ImVfbQGreen( vfb, pPixel ); + + *rbp++ = ImVfbQBlue( vfb, pPixel ); + + ImVfbSInc( vfb, pPixel ); + + } + + + + Write( runBuffer, UCHAR, 1, width3 ); + + } + + free( (char *)runBuffer ); + + + + return ( width3 * height ); + +} + + + +static long /* Returns number of bytes */ + +#ifdef __STDC__ + +imHdfVfbWriteRGBLine( int ioType, int fd, FILE *fp, ImVfb *vfb ) + +#else + +imHdfVfbWriteRGBLine( ioType, fd, fp, vfb ) + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + ImVfb *vfb; /* VFB to write out */ + +#endif + +{ + + int width, height; /* Image size */ + + int x, y; /* X and Y image coordinates */ + + unsigned char *runBuffer; /* Buffered up runs */ + + unsigned char *rbp; /* Run buffer pointer */ + + ImVfbPtr pPixel; /* VFB pixel pointer */ + + ImVfbPtr pPixel2; /* Start of scanline pixel ptr */ + + + + + + /* + + * Write out the uncompressed 24-bit RGB VFB, scanline interleaved. + + */ + + pPixel = ImVfbQFirst( vfb ); + + width = ImVfbQWidth( vfb ); + + height = ImVfbQHeight( vfb ); + + ImMalloc( runBuffer, unsigned char *, sizeof( unsigned char ) * width ); + + + + for ( y = 0; y < height; y++ ) + + { + + pPixel2 = pPixel; + + rbp = runBuffer; + + for ( x = 0; x < width; x++ ) + + { + + *rbp++ = ImVfbQRed( vfb, pPixel ); + + ImVfbSInc( vfb, pPixel ); + + } + + Write( runBuffer, UCHAR, 1, width ); + + + + pPixel = pPixel2; + + rbp = runBuffer; + + for ( x = 0; x < width; x++ ) + + { + + *rbp++ = ImVfbQGreen( vfb, pPixel ); + + ImVfbSInc( vfb, pPixel ); + + } + + Write( runBuffer, UCHAR, 1, width ); + + + + pPixel = pPixel2; + + rbp = runBuffer; + + for ( x = 0; x < width; x++ ) + + { + + *rbp++ = ImVfbQBlue( vfb, pPixel ); + + ImVfbSInc( vfb, pPixel ); + + } + + Write( runBuffer, UCHAR, 1, width ); + + } + + free( (char *)runBuffer ); + + + + return ( width * height * 3 ); + +} + + + +static long /* Returns number of bytes */ + +#ifdef __STDC__ + +imHdfVfbWriteRGBPlane( int ioType, int fd, FILE *fp, ImVfb *vfb ) + +#else + +imHdfVfbWriteRGBPlane( ioType, fd, fp, vfb ) + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + ImVfb *vfb; /* VFB to write out */ + +#endif + +{ + + int width, height; /* Image size */ + + int x, y; /* X and Y image coordinates */ + + unsigned char *runBuffer; /* Buffered up runs */ + + unsigned char *rbp; /* Run buffer pointer */ + + ImVfbPtr pPixel; /* VFB pixel pointer */ + + + + + + /* + + * Write out the uncompressed 24-bit RGB VFB, plane interleaved. + + */ + + width = ImVfbQWidth( vfb ); + + height = ImVfbQHeight( vfb ); + + ImMalloc( runBuffer, unsigned char *, sizeof( unsigned char ) * width ); + + + + pPixel = ImVfbQFirst( vfb ); + + IMCOPYPLANE( ImVfbQRed, UCHAR, 1 ); + + + + pPixel = ImVfbQFirst( vfb ); + + IMCOPYPLANE( ImVfbQGreen, UCHAR, 1 ); + + + + pPixel = ImVfbQFirst( vfb ); + + IMCOPYPLANE( ImVfbQBlue, UCHAR, 1 ); + + + + free( (char *)runBuffer ); + + + + return ( width * height * 3 ); + +} + + + +static long /* Returns number of bytes */ + +#ifdef __STDC__ + +imHdfVfbWriteRLERGB( int ioType, int fd, FILE *fp, ImVfb *vfb ) + +#else + +imHdfVfbWriteRLERGB( ioType, fd, fp, vfb ) + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + ImVfb *vfb; /* VFB to write out */ + +#endif + +{ + + int width, height; /* Image size */ + + int width3; /* 3 times width */ + + int x, y; /* X and Y image coordinates */ + + unsigned char *runBuffer; /* Buffered up runs */ + + unsigned char *rbp; /* Run buffer pointer */ + + unsigned char *pCount; /* Last run's count byte */ + + + + int index; /* New pixel index */ + + int oldIndex; /* Old pixel index */ + + int count; /* Run length */ + + ImVfbPtr pPixel; /* VFB pixel pointer */ + + int onRGB; /* Which RGB component are we on?*/ + + + + long nBytes = 0; /* Total number of bytes written*/ + + + + + + /* + + * Run-length encode the 24-bit RGB VFB, uninterleaved. + + * Worst case compression: + + * input: ABB + + * output: 1A2BB + + * Worst case takes 5/3 * width * 3 bytes. Run buffer is + + * allocated to be big enough for the worst case. + + */ + + pPixel = ImVfbQFirst( vfb ); + + width = ImVfbQWidth( vfb ); + + height = ImVfbQHeight( vfb ); + + width3 = width * 3; + + ImMalloc( runBuffer, unsigned char *, sizeof( unsigned char ) * width3 * 2 ); + + + + for ( y = 0; y < height; y++ ) + + { + + rbp = runBuffer; + + oldIndex = ImVfbQRed( vfb, pPixel ); + + onRGB = GREEN; + + count = 1; + + + + pCount = rbp++; + + *rbp++ = oldIndex; + + + + for ( x = 1; x < width3; x++ ) + + { + + switch ( onRGB ) + + { + + case RED: index = ImVfbQRed( vfb, pPixel ); + + onRGB = GREEN; + + break; + + case GREEN: index = ImVfbQGreen( vfb, pPixel ); + + onRGB = BLUE; + + break; + + case BLUE: index = ImVfbQBlue( vfb, pPixel ); + + ImVfbSInc( vfb, pPixel ); + + onRGB = RED; + + break; + + } + + IMADDTOBUFFER( ); + + } + + if ( count < 0 ) + + *pCount = ((unsigned char)(-count)) | 0x80; + + else + + *pCount = (unsigned char)count; + + + + /* Write out the run buffer. */ + + Write( runBuffer, UCHAR, 1, (rbp - runBuffer) ); + + nBytes += (rbp - runBuffer); + + } + + free( (char *)runBuffer ); + + + + return ( nBytes ); + +} + + + +static long /* Returns number of bytes */ + +#ifdef __STDC__ + +imHdfVfbWriteRLERGBLine( int ioType, int fd, FILE *fp, ImVfb *vfb ) + +#else + +imHdfVfbWriteRLERGBLine( ioType, fd, fp, vfb ) + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + ImVfb *vfb; /* VFB to write out */ + +#endif + +{ + + int width, height; /* Image size */ + + int x, y; /* X and Y image coordinates */ + + unsigned char *runBuffer; /* Buffered up runs */ + + unsigned char *rbp; /* Run buffer pointer */ + + unsigned char *pCount; /* Last run's count byte */ + + + + int index; /* New pixel index */ + + int oldIndex; /* Old pixel index */ + + int count; /* Run length */ + + ImVfbPtr pPixel; /* VFB pixel pointer */ + + ImVfbPtr pPixel2; /* Start of line pixel pointer */ + + + + long nBytes = 0; /* Total number of bytes written*/ + + + + + + /* + + * Run-length encode the 24-bit RGB VFB, line interleaved. + + * Worst case compression: + + * input: ABB + + * output: 1A2BB + + * Worst case takes 5/3 * width bytes * 3. Run buffer is + + * allocated to be big enough for the worst case. + + */ + + pPixel = ImVfbQFirst( vfb ); + + width = ImVfbQWidth( vfb ); + + height = ImVfbQHeight( vfb ); + + ImMalloc( runBuffer, unsigned char *, sizeof( unsigned char ) * width * 6 ); + + + + for ( y = 0; y < height; y++ ) + + { + + rbp = runBuffer; + + + + /* Red part of scan-line. */ + + pPixel2 = pPixel; + + oldIndex = ImVfbQRed( vfb, pPixel ); + + ImVfbSInc( vfb, pPixel ); + + count = 1; + + pCount = rbp++; + + *rbp++ = oldIndex; + + for ( x = 1; x < width; x++ ) + + { + + index = ImVfbQRed( vfb, pPixel ); + + ImVfbSInc( vfb, pPixel ); + + IMADDTOBUFFER( ); + + } + + if ( count < 0 ) + + *pCount = ((unsigned char)(-count)) | 0x80; + + else + + *pCount = (unsigned char)count; + + + + /* Green part of scan-line. */ + + pPixel = pPixel2; + + oldIndex = ImVfbQGreen( vfb, pPixel ); + + ImVfbSInc( vfb, pPixel ); + + count = 1; + + pCount = rbp++; + + *rbp++ = oldIndex; + + for ( x = 1; x < width; x++ ) + + { + + index = ImVfbQGreen( vfb, pPixel ); + + ImVfbSInc( vfb, pPixel ); + + IMADDTOBUFFER( ); + + } + + if ( count < 0 ) + + *pCount = ((unsigned char)(-count)) | 0x80; + + else + + *pCount = (unsigned char)count; + + + + /* Blue part of scan-line. */ + + pPixel = pPixel2; + + oldIndex = ImVfbQBlue( vfb, pPixel ); + + ImVfbSInc( vfb, pPixel ); + + count = 1; + + pCount = rbp++; + + *rbp++ = oldIndex; + + for ( x = 1; x < width; x++ ) + + { + + index = ImVfbQBlue( vfb, pPixel ); + + ImVfbSInc( vfb, pPixel ); + + IMADDTOBUFFER( ); + + } + + if ( count < 0 ) + + *pCount = ((unsigned char)(-count)) | 0x80; + + else + + *pCount = (unsigned char)count; + + + + /* Write out the run buffer. */ + + Write( runBuffer, UCHAR, 1, (rbp - runBuffer) ); + + nBytes += (rbp - runBuffer); + + } + + free( (char *)runBuffer ); + + + + return ( nBytes ); + +} + + + +static long /* Returns number of bytes */ + +#ifdef __STDC__ + +imHdfVfbWriteRLERGBPlane( int ioType, int fd, FILE *fp, ImVfb *vfb ) + +#else + +imHdfVfbWriteRLERGBPlane( ioType, fd, fp, vfb ) + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + ImVfb *vfb; /* VFB to write out */ + +#endif + +{ + + int width, height; /* Image size */ + + int x, y; /* X and Y image coordinates */ + + unsigned char *runBuffer; /* Buffered up runs */ + + unsigned char *rbp; /* Run buffer pointer */ + + unsigned char *pCount; /* Last run's count byte */ + + + + int index; /* New pixel index */ + + int oldIndex; /* Old pixel index */ + + int count; /* Run length */ + + ImVfbPtr pPixel; /* VFB pixel pointer */ + + + + long nBytes = 0; /* Total number of bytes written*/ + + + + + + /* + + * Run-length encode the 24-bit RGB VFB, uninterleaved. + + * Worst case compression: + + * input: ABB + + * output: 1A2BB + + * Worst case takes 5/3 * width bytes. Run buffer is + + * allocated to be big enough for the worst case. + + */ + + width = ImVfbQWidth( vfb ); + + height = ImVfbQHeight( vfb ); + + ImMalloc( runBuffer, unsigned char *, sizeof( unsigned char ) * width * 2 ); + + + + /* Red plane. */ + + pPixel = ImVfbQFirst( vfb ); + + IMENCODEPLANE( ImVfbQRed, UCHAR, 1 ); + + + + /* Green plane. */ + + pPixel = ImVfbQFirst( vfb ); + + IMENCODEPLANE( ImVfbQGreen, UCHAR, 1 ); + + + + /* Blue plane. */ + + pPixel = ImVfbQFirst( vfb ); + + IMENCODEPLANE( ImVfbQBlue, UCHAR, 1 ); + + + + free( (char *)runBuffer ); + + + + return ( nBytes ); + +} + diff --git a/utils/roq2/libim/imico.c b/utils/roq2/libim/imico.c new file mode 100644 index 0000000..0a8aa23 --- /dev/null +++ b/utils/roq2/libim/imico.c @@ -0,0 +1,3340 @@ +/** + + ** $Header: /roq/libim/imico.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/imico.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** imico.c - Microsoft Windows ICO file I/O + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** imico.c contains routines to read and write Microsoft Windows ICO + + ** files for the image manipulation library. Raster data read + + ** in is stored in a VFB. Raster data written out is taken from a + + ** tag table. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** none + + ** + + ** PRIVATE CONTENTS + + ** imIcoRead f read a Microsoft Windows ICO file + + ** imIcoWrite f control for what to write + + ** + + ** imIcoHeader t file header information + + ** imIcoHeaderFields v imIcoHeader description for Bin pkg. + + ** imIcoDirEntry t icon directory entry + + ** imIcoDirEntryFields v imIcoDirEntry description for Bin pkg. + + ** imIcoInfo t icon information + + ** imIcoInfoFields v imIcoInfo description for Bin pkg. + + ** + + ** imIcoImageRead f read the ith icon image + + ** imIcoReadImage f read the icon image + + ** + + ** imIcoWriteImage f write the icon image + + ** imIcoWriteRaw1 f write a Microsoft Windows ICO 1-bit file + + ** imIcoWriteRaw4 f write a Microsoft Windows ICO 4-bit file + + ** + + ** HISTORY + + ** $Log: /roq/libim/imico.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.12 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.11 1995/06/15 20:26:23 bduggan + + ** took out some unused vars. + + ** turned bzero into memset + + ** + + ** Revision 1.10 1995/04/03 21:26:04 bduggan + + ** took out #ifdef NEWMAGIC + + ** + + ** Revision 1.9 1995/01/16 22:08:46 bduggan + + ** Fixed a typo that was hindering reading of + + ** alpha channels (changed 0x100 to 0x80) + + ** + + ** Revision 1.8 1995/01/10 23:27:34 bduggan + + ** uncapitlized i's in local functions, made read/write routines static, + + ** put in IMMULTI, IMPIPE instead of TRUE/FALSE + + ** + + ** Revision 1.7 1994/08/31 17:34:50 nadeau + + ** Updated to ANSI C comaptibility. Updated comments. + + ** Rearranged code a bit for better readability. Reformatted + + ** code for consistent indentation style. Misc cleanup. + + ** + + ** Revision 1.7 1994/08/31 17:34:50 nadeau + + ** Updated to ANSI C comaptibility. Updated comments. + + ** Rearranged code a bit for better readability. Reformatted + + ** code for consistent indentation style. Misc cleanup. + + ** + + ** Revision 1.6 92/12/03 01:49:00 nadeau + + ** Corrected info messages. Got rid of IMINFOMSG references. + + ** + + ** Revision 1.5 92/11/04 11:47:52 groening + + ** added multiple read and write. + + ** put ImFIleFormat info and magic number info + + ** from imfmt.c into this file. + + ** + + ** Revision 1.4 92/10/12 15:54:33 vle + + ** Made changes to make Cray happy. + + ** + + ** Revision 1.3 92/09/29 17:58:34 vle + + ** Added ImInfo messages. + + ** + + ** Revision 1.2 92/09/17 14:23:09 vle + + ** Fixed "scanlinesize" math bug that caused images to be cropped. + + ** + + ** Revision 1.1 92/08/18 12:21:58 vle + + ** Initial revision + + ** + + **/ + + + +#include "iminternal.h" + + + + + +/** + + ** FORMAT + + ** ICO Microsoft Windows icon image + + ** + + ** AKA + + ** None + + ** + + ** FORMAT REFERENCES + + ** Microsoft Windows 3.1 Programmer's Reference, volume 3, Microsoft + + ** Press, 1992. + + ** + + ** Microsoft Windows 3.1 Programmer's Reference, volume 3, Microsoft + + ** Press, 1992. + + ** + + ** CODE CREDITS + + ** Custom development, Vinh Le, San Diego Supercomputer Center, 1992. + + ** + + ** DESCRIPTION + + ** Microsoft's ICO (ICOn) format is used in Microsoft's Windows + + ** versions 3.0 and up for storage of one or more icons. + + ** + + ** The first item encountered in an ICO file is an Icon Directory + + ** Header. This tells that the file is indeed a file containing + + ** icons and that there are n number of them. + + ** + + ** Following the Icon Directory Header is an Icon Directory + + ** Entry for each icon. An Icon Directory Entry contains + + ** information on the icon's location within the file, + + ** its width, height, etc. There are n Icon Directory Entries. + + ** + + ** Following all this is the actual Icon Images themselves, which + + ** consist of an info header, a colortable, and XOR and AND masks. + + ** The XOR mask could be considered the "real" icon. The AND + + ** mask determines the XOR image's "tranparency". XOR masks + + ** can either be 1-, 3-, or 4-bits/pixel. 4-bits/pixel images + + ** are packed 2 pixels/byte. 3-bits/pixel images are currently + + ** unsupported. 1-bit/pixel images are packed 8 pixels/byte. + + ** AND masks are always monochrome images, thus they are + + ** packed as 1-bit/pixel images, or 8-bits/byte. + + ** + + ** + + ** Stucture Overview + + ** ----------------- + + ** + + ** ICON DIRECTORY + + ** --------------------------------------------------------- + + ** |ICON |ICON |ICON | ... | | | | + + ** |ENTRY 1|ENTRY 2|ENTRY 3| | | | | + + ** --------------------------------------------------------- + + ** | + + ** | --------- + + ** +---------> |ICON | + + ** |IMAGE | + + ** |-------| + + ** |Header | + + ** |-------| + + ** |Color | + + ** |Table | + + ** |-------| + + ** |XOR | + + ** |Mask | + + ** |-------| + + ** |AND | + + ** |Mask | + + ** --------- + + ** + + ** + + ** Icon Directory Header (6 bytes + 16 bytes/icon entry) + + ** --------------------- + + ** + + ** The Icon Directory Header contains the resource type and + + ** number of icons in the Icon Directory. + + ** + + ** Name Type Size Comments + + ** ---- ---- ---- -------- + + ** idReserved word 2 bytes reserved, must be 0 + + ** idType word 2 bytes resource type, + + ** must be 1 + + ** idCount word 2 bytes number of icons + + ** idEntries[] IconDirEntry 16 bytes array of info for + + ** per entry individual icons + + ** + + ** typedef struct ICONDIR + + ** { + + ** word idReserved; + + ** word idType; + + ** word idCount; + + ** ICONDIRENTRY idEntries[idCount]; + + ** } ICONDIR; + + ** + + ** Icon Directory Entry (16 bytes/icon entry) + + ** -------------------- + + ** + + ** An Icon Directory Entry stores information about the an icon's + + ** dimensions and color attributes. + + ** + + ** Name Type Size Comments + + ** ---- ---- ---- -------- + + ** Width byte 1 byte width in pixels, must be 16, 32, or 64 + + ** Height byte 1 byte height in pixels, must be 16, 32, or 64 + + ** ColorCount byte 1 byte number of colors, must be 2, 8 or 16 + + ** Reserved byte 1 byte reserved, must be 0 + + ** Planes word 2 byte number of color planes + + ** BitCount word 2 byte number of bits + + ** BytesInRes dword 4 byte size of icon in bytes + + ** ImageOffset dword 4 byte offset from beginning of file to + + ** Icon Image + + ** + + ** typedef struct ICONDIRENTRY + + ** { + + ** byte Width; + + ** byte Height; + + ** byte ColorCount; + + ** byte Reserved; + + ** word Planes; + + ** word BitCount; + + ** dword BytesInRes; + + ** dword ImageOffset; + + ** } ICONDIRENTRY; + + ** + + ** Icon Image (40 bytes + 4 bytes/color entry + sizeof XOR mask + + + ** ---------- sizeof AND mask) + + ** + + ** Each Icon Image stores 1 icon. + + ** + + ** Name Type Size Comments + + ** ---- ---- ---- -------- + + ** icHeader BITMAPFILEHEADER 40 bytes only biSize + + ** through + + ** biBitCount + + ** and + + ** biSizeImage + + ** are used + + ** icColors[] RGBQUAD 4 bytes per colortable used + + ** color in XOR mask + + ** icXOR[] byte 1 byte per XOR image mask + + ** element + + ** icAND[] byte 1 byte per AND image mask + + ** element + + ** + + ** Ico-Info Header (40 bytes) + + ** ------------------ + + ** + + ** The Ico-Info Header contains information about the icon's + + ** size and dimensions. + + ** + + ** Name Type Size Comments + + ** ---- ---- ---- -------- + + ** biSize dword 4 bytes number of bytes in + + ** Ico-Info Header + + ** biWidth long 4 bytes width, in pixels + + ** biHeight long 4 bytes height, in pixels + + ** biPlanes word 2 bytes number of planes for the + + ** display device, must be 1 + + ** biBitCount word 2 bytes bits per pixel, must be 1, + + ** 3, 4 + + ** biCompression dword 4 bytes unused, must be 0 + + ** biSizeImage dword 4 bytes image size in bytes + + ** biXPelsPerMeter long 4 bytes unused, must be 0 + + ** biYPelsPerMeter long 4 bytes unused, must be 0 + + ** biClrUsed dword 4 bytes unused, must be 0 + + ** biClrImportant dword 4 bytes unused, must be 0 + + ** + + ** typedef struct tagBITMAPINFOHEADER + + ** { + + ** dword biSize; + + ** long biWidth; + + ** long biHeight; + + ** word biPlanes; + + ** word biBitCount; + + ** dword biCompression; + + ** dword biSizeImage; + + ** long biXPelsPerMeter; + + ** long biYPelsPerMeter; + + ** dword biClrUsed; + + ** dword biClrImportant; + + ** } BITMAPINFOHEADER; + + ** + + ** ColorTable (4 bytes per color) + + ** ---------- + + ** + + ** ColorTable is an array of RGBQuad's. Each RGBQuad contains color + + ** intensity values for Red, Green, and Blue. + + ** + + ** RGBQuad + + ** ------- + + ** + + ** Name Type Size Comment + + ** ---- ---- ---- ------- + + ** rgbBlue byte 1 byte intensity of blue + + ** rgbGreen byte 1 byte intensity of green + + ** rgbRed byte 1 byte intensity of red + + ** rgbReserved byte 1 byte unused + + ** + + ** typedef struct tagRGBQUAD + + ** { + + ** byte rgbBlue; + + ** byte rgbGreen; + + ** byte rgbRed; + + ** byte rgbReserved; + + ** } RGBQUAD; + + ** + + ** IMPLEMENTATION SPECIFIC NOTES + + ** 3-bits/pixel (8 color) icons are not supported due to the + + ** lack of documentation on the packing method for such icons. + + ** + + **/ + + + + + + + + + + + +/* + + * ICO - Microsoft Windows icon + + * For information on these structures, how to use them, etc. please + + * see imfmt.c. + + */ + +#ifdef __STDC__ + +static int imIcoRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imIcoWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + + TagTable *flagsTable, TagTable *tagTable ); + +#else + +static int imIcoRead( ), imIcoWrite( ); + +#endif + +static char *imIcoNames[ ] = { "ico", NULL }; + +static unsigned char imIcoMagicNumber[ ] = { 0x00, 0x00, 0x01, 0x00 }; + +static ImFileMagic imFileIcoMagic []= + +{ + + { 0, 4, imIcoMagicNumber }, + + { 0, 0, NULL }, + +}; + + + +static ImFileFormatReadMap imIcoReadMap[ ] = + +{ + + /* in out */ + + /* type,ch,dep, attr. VFB type attr. */ + + { IN,1,1, C|A, IMVFBINDEX8, C|A }, + + { IN,1,4, C|A, IMVFBINDEX8, C|A }, + + { -1, 0, -1, 0 }, + +}; + +static ImFileFormatWriteMap imIcoWriteMap[ ] = + +{ + + /* in out */ + + /* VFB type, attr., type,ch,dep, attr., func */ + + { IMVFBMONO, 0, IN,1,1, C|A, imIcoWrite }, + + { IMVFBMONO, C, IN,1,1, C|A, imIcoWrite }, + + { IMVFBMONO, A, IN,1,1, C|A, imIcoWrite }, + + { IMVFBMONO, C|A, IN,1,1, C|A, imIcoWrite }, + + { IMVFBINDEX8, 0, IN,1,4, C|A, imIcoWrite }, + + { IMVFBINDEX8, C, IN,1,4, C|A, imIcoWrite }, + + { IMVFBINDEX8, A, IN,1,4, C|A, imIcoWrite }, + + { IMVFBINDEX8, C|A, IN,1,4, C|A, imIcoWrite }, + + { -1, 0, -1, 0, NULL }, + +}; + + + +ImFileFormat ImFileIcoFormat = + +{ + + imIcoNames, "Windows icon image file", + + "Microsoft Corp.", + + "1- and 4-bit color index images with alpha channels.", + + "1- and 4-bit color index images with alpha channels.", + + imFileIcoMagic, + + IMMULTI, IMPIPE, /* Read */ + + IMMULTI, IMPIPE, /* Write */ + + imIcoRead, imIcoReadMap, imIcoWriteMap + +}; + + + + + + + + + + + +/* + + * TYPEDEF & STRUCTURE + + * imIcoHeader - ICO file header + + * imIcoHeaderFields - ICO file header fields for binary pkg. + + * imIcoDirEntry - ICO icon directory entry + + * imIcoDirEntryFields - ICO icon directory entry for bin pkg. + + * imIcoInfo - ICO file info + + * imIcoInfoFields - ICO file info fields for bin pkg. + + * imIcoRGBQuad - ICO color information + + * imIcoRGBQuadFields - ICO color information fields for bin pkg. + + * + + * DESCRIPTION + + * The imIcoRGBQuad is one entry in the colortable. + + */ + + + +typedef struct imIcoHeader + +{ + + sdsc_uint16 ico_idReserved; /* reserved, must be zero */ + + sdsc_uint16 ico_idType; /* resource type, must be 1 */ + + sdsc_uint16 ico_idCount; /* number of entries in directory */ + +} imIcoHeader; + + + +static BinField imIcoHeaderFields[ ] = + +{ + + { UINT16, 2, 1 }, /* ico_idReserved */ + + { UINT16, 2, 1 }, /* ico_idType */ + + { UINT16, 2, 1 }, /* ico_idCount */ + + { 0, 0, 0 } + +}; + + + +#define IMICOHEADERSIZE 6 /* bytes */ + + + + + + + +typedef struct imIcoDirEntry + +{ + + unsigned char ico_Width; /* width in pixels, must be 16,32,or 64 */ + + unsigned char ico_Height; /* height in pixels, must be 16,32,or 64*/ + + unsigned char ico_ColorCount; /* number of colors, must be 2,8 or 16 */ + + unsigned char ico_Reserved; /* reserved, must be 0 */ + + sdsc_uint16 ico_Planes; /* number of color planes */ + + sdsc_uint16 ico_BitCount; /* number of bits */ + + sdsc_uint32 ico_BytesInRes; /* size of icon in bytes */ + + sdsc_uint32 ico_ImageOffset;/* offset from beginning of file to + + Icon Image */ + +} imIcoDirEntry; + + + +static BinField imIcoDirEntryFields[ ] = + +{ + + { UCHAR, 1, 1 }, /* ico_Width */ + + { UCHAR, 1, 1 }, /* ico_Height */ + + { UCHAR, 1, 1 }, /* ico_ColorCount */ + + { UCHAR, 1, 1 }, /* ico_Reserved */ + + { UINT16, 2, 1 }, /* ico_Planes */ + + { UINT16, 2, 1 }, /* ico_BitCount */ + + { UINT32, 4, 1 }, /* ico_ByteInRes */ + + { UINT32, 4, 1 }, /* ico_ImageOffset */ + + { 0, 0, 0 } + +}; + + + +#define IMICODIRENTRYSIZE 16 /* bytes */ + + + + + + + +typedef struct imIcoInfo + +{ + + sdsc_uint32 ico_bisize; /* number of bytes in icon info header */ + + sdsc_uint32 ico_biwidth; /* image width in pixels */ + + sdsc_uint32 ico_biheight; /* image height in pixels */ + + sdsc_uint16 ico_biplanes; /* number of planes for device, always 1*/ + + sdsc_uint16 ico_bibitcount; /* bits/pixel (1, 3, or 4) */ + + sdsc_uint32 ico_bicompress; /* unused, must be 0 */ + + sdsc_uint32 ico_bisizeimage;/* image size in bytes */ + + sdsc_uint32 ico_bixpm; /* unused, must be 0 */ + + sdsc_uint32 ico_biypm; /* unused, must be 0 */ + + sdsc_uint32 ico_biclrused; /* unused, must be 0 */ + + sdsc_uint32 ico_biclrim; /* unused, must be 0 */ + +} imIcoInfo; + + + +static BinField imIcoInfoFields[ ] = + +{ + + { UINT32, 4, 1 }, /* ico_bisize */ + + { UINT32, 4, 1 }, /* ico_biwidth */ + + { UINT32, 4, 1 }, /* ico_biheight */ + + { UINT16, 2, 1 }, /* ico_biplanes */ + + { UINT16, 2, 1 }, /* ico_bibitcount */ + + { UINT32, 4, 1 }, /* ico_bicompress */ + + { UINT32, 4, 1 }, /* ico_bisizeimage */ + + { UINT32, 4, 1 }, /* ico_bixpm */ + + { UINT32, 4, 1 }, /* ico_biypm */ + + { UINT32, 4, 1 }, /* ico_biclrused */ + + { UINT32, 4, 1 }, /* ico_biclrim */ + + { 0, 0, 0 } + +}; + + + +#define IMICOINFOSIZE 40 /* bytes */ + + + + + + + +typedef struct imIcoRGBQuad + +{ + + unsigned char blue; /* blue intensity */ + + unsigned char green; /* green intensity */ + + unsigned char red; /* red intensity */ + + unsigned char reserved; /* unused */ + +} imIcoRGBQuad; + + + +static BinField imIcoRGBQuadFields[ ] = + +{ + + { UCHAR, 1, 1 }, /* blue */ + + { UCHAR, 1, 1 }, /* green */ + + { UCHAR, 1, 1 }, /* red */ + + { UCHAR, 1, 1 }, /* reserved */ + + { 0, 0, 0 } + +}; + + + +#define IMICORGBQUADSIZE 4 /* bytes */ + + + + + + + +/* + + * Various useful ICO specific constants + + */ + + + +#define IMICOMAGIC 1 + + /* magic resource number for icons */ + +#define IMICOXOR 0 + + /* XOR vfb flag, IMVFBINDEX8 */ + +#define IMICOAND 1 + + /* AND vfb flag, IMVFBMONO */ + + + + + + + +#ifdef __STDC__ + +static int imIcoImageRead ( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, + + imIcoDirEntry *icodirentryptr); + +static int imIcoReadImage( int ioType, int fd, FILE *fp, ImVfb *vfb, int what ); + +static int imIcoWriteRaw1(ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, ImVfb *vfb, ImClt *clt, + + imIcoDirEntry *direntry ); + +static int imIcoWriteRaw4( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, ImVfb *vfb, ImClt *clt, + + imIcoDirEntry *direntry); + +static int imIcoWriteImage( int ioType, int fd, FILE *fp, ImVfb *vfb, int what ); + +#else + +static int imIcoImageRead( ); + +static int imIcoReadImage( ); + +static int imIcoWriteRaw1( ); + +static int imIcoWriteRaw4( ); + +static int imIcoWriteImage( ); + +#endif + + + + + + + + + + + +/* + + * FUNCTION + + * imIcoRead - read a Microsoft Windows Ico file + + * + + * DESCRIPTION + + * The file header is read and the size of the image determined. + + * Space is allocated for the VFB and the file is read into the + + * VFB. + + */ + + + +static int /* Returns # tags read in */ + +#ifdef __STDC__ + +imIcoRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imIcoRead( ioType, fd, fp, flagsTable, tagTable ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + +#endif + +{ + + imIcoHeader header; /* ICO file header */ + + imIcoDirEntry *icodirentries; /* ICO dir entries */ + + imIcoDirEntry *icodirentryptr; /* ICO dir entry pointer */ + + unsigned int i; /* Counter */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * Set Binary Package byte order and read in part of header + + */ + + BinByteOrder( BINLBF ); + + if( ImBinReadStruct( ioType, fd, fp, &header, + + imIcoHeaderFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + /* + + * Use idType to determine byte ordering + + */ + + if( header.ico_idType != IMICOMAGIC ) + + { + + /* + + * Swap bytes + + */ + + header.ico_idType = (header.ico_idType&0xFF00)>>8 | + + (header.ico_idType&0x00FF)<<8; + + if( header.ico_idType != IMICOMAGIC ) + + { + + ImErrorFatal( ImQError( ), -1, IMEMAGIC ); + + } + + else + + { + + header.ico_idCount = (header.ico_idCount&0xFF00)>>8 | + + (header.ico_idCount&0x00FF)<<8; + + BinByteOrder( BINMBF ); + + ImInfo( "Byte Order", "Most Significant Byte First"); + + } + + } + + else + + { + + ImInfo( "Byte Order", "Least Significant Byte First" ); + + } + + + + /* + + * Allocate space for icon dir entries + + */ + + ImMalloc( icodirentries, imIcoDirEntry*, + + header.ico_idCount * sizeof(imIcoDirEntry) ); + + + + /* + + * Read in icon dir entries + + */ + + icodirentryptr = icodirentries; + + for( i = 0; i < header.ico_idCount; ++i ) + + { + + if( ImBinReadStruct( ioType, fd, fp, icodirentryptr, + + imIcoDirEntryFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + ++icodirentryptr; + + } + + + + /* + + * Read in icon images + + */ + + icodirentryptr = icodirentries; + + for( i = 0; i < header.ico_idCount; ++i ) + + { + + /* + + * Read in ith icon + + */ + + sprintf( message, "%d of %d", (i+1), header.ico_idCount ); + + ImInfo( "Image", message ); + + + + imIcoImageRead( ioType, fd, fp, flagsTable, tagTable, + + icodirentryptr); + + ++icodirentryptr; + + } + + + + free( icodirentries ); + + return header.ico_idCount; + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imIcoReadImage - read in an image's pixels + + * + + * DESCRIPTION + + * A temporary buffer is allocated and the image's pixels read in. + + * Pixels are unpacked from the buffer and dispersed into the VFB. + + */ + + + +#define IMREADMONO 0 + +#define IMREADINDEX8 1 + +#define IMREADALPHA 2 + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imIcoReadImage( int ioType, int fd, FILE *fp, ImVfb *vfb, int what ) + +#else + +imIcoReadImage( ioType, fd, fp, vfb, what ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + ImVfb *vfb; /* Image to read into */ + + int what; /* What kind of thing to read */ + +#endif + +{ + + ImVfbPtr vfbptr; /* Pixel pointer */ + + unsigned char mask; /* Mask for extracting pixels */ + + unsigned char *rbuf = NULL; /* Read buffer */ + + unsigned char *rbufptr; /* Read buffer data pointer */ + + int scanlinesize; /* Length of scanline in bytes */ + + int x, y; /* Width and Height */ + + int i, j, k; /* Counters */ + + int xgroup; /* # of groups of pixels */ + + + + + + /* + + * Allocate read buffer space + + */ + + y = ImVfbQHeight( vfb ); + + x = ImVfbQWidth( vfb ); + + switch ( what ) + + { + + case IMREADMONO: + + case IMREADALPHA: + + scanlinesize = (x * 1) / 8; + + xgroup = x/8; /* 8 1-bit quantities per byte */ + + break; + + case IMREADINDEX8: + + scanlinesize = (x * 4) / 8; + + xgroup = x/2; /* 2 4-bit quantities per byte */ + + break; + + } + + if ( scanlinesize & 0x3 ) + + scanlinesize = (scanlinesize & ~0x3) + 4; + + ImCalloc( rbuf, unsigned char*, scanlinesize, sizeof( unsigned char) ); + + + + + + /* + + * Read in pixels and put them into the selected VFB field. + + */ + + switch ( what ) + + { + + case IMREADMONO: /* 8 pixels to a byte. */ + + /* + + * An OFF bit uses the 1st CLT entry, while an ON bit + + * uses the 2nd CLT entry. + + */ + + for ( i = y-1; i >= 0 ; --i ) + + { + + vfbptr = ImVfbQPtr( vfb, 0, i ); + + if ( ImBinRead( ioType, fd, fp, rbuf, UCHAR, 1, + + scanlinesize ) == -1 ) + + { + + ImReturnBinError( ); + + } + + rbufptr = rbuf; + + + + for ( j = 0; j < xgroup; ++j ) + + { + + mask = 0x80; + + for ( k = 0; k < 8; k++ ) + + { + + if ( (*rbufptr & mask) == mask ) + + ImVfbSIndex8( vfb, vfbptr, 1 ); + + else + + ImVfbSIndex8( vfb, vfbptr, 0 ); + + ImVfbSInc( vfb, vfbptr ); + + mask >>= 1; + + } + + ++rbufptr; + + } + + } + + break; + + + + case IMREADALPHA: /* 8 pixels to a byte. */ + + /* + + * An ON bit maps to 255 (opaque) and an OFF bit to + + * 0 (transparent). + + */ + + for ( i = y-1; i >= 0 ; --i ) + + { + + vfbptr = ImVfbQPtr( vfb, 0, i ); + + if ( ImBinRead( ioType, fd, fp, rbuf, UCHAR, 1, + + scanlinesize ) == -1 ) + + { + + ImReturnBinError( ); + + } + + rbufptr = rbuf; + + + + for ( j = 0; j < xgroup; ++j ) + + { + + mask = 0x80; + + for ( k = 0; k < 8; k++ ) + + { + + if ( (*rbufptr & mask) == mask ) + + ImVfbSAlpha( vfb, vfbptr, 255 ); + + else + + ImVfbSAlpha( vfb, vfbptr, 0 ); + + ImVfbSInc( vfb, vfbptr ); + + mask >>= 1; + + } + + ++rbufptr; + + } + + } + + break; + + + + case IMREADINDEX8:/* 2 pixels to a byte. */ + + for ( i = y-1; i >= 0 ; --i ) + + { + + vfbptr = ImVfbQPtr( vfb, 0, i ); + + if ( ImBinRead( ioType, fd, fp, rbuf, UCHAR, 1, + + scanlinesize ) == -1 ) + + { + + ImReturnBinError( ); + + } + + rbufptr = rbuf; + + + + for ( j = 0; j < xgroup; ++j ) + + { + + ImVfbSIndex8( vfb, vfbptr, (*rbufptr>>4)&0xF ); + + ImVfbSInc( vfb, vfbptr ); + + ImVfbSIndex8( vfb, vfbptr, *rbufptr&0x0F ); + + ImVfbSInc( vfb, vfbptr ); + + ++rbufptr; + + } + + } + + break; + + } + + + + free( (char *)rbuf ); + + + + return ( 0 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imIcoImageRead - read in an image entry + + * + + * DESCRIPTION + + * The next image's header, CLT, and pixels are read in and added + + * to the tag table. + + */ + +static int + +#ifdef __STDC__ + +imIcoImageRead ( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, imIcoDirEntry *icodirentryptr) + +#else + +imIcoImageRead ( ioType, fd, fp, flagsTable, tagTable, icodirentryptr) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + + imIcoDirEntry *icodirentryptr; /* Icon dir entry pointer */ + +#endif + +{ + + imIcoInfo info; /* Icon image info */ + + ImVfb *vfb; /* Virtual Frame Buffers */ + + ImClt *clt = IMCLTNULL; /* Colortable */ + + ImCltPtr cltptr; /* Colortable entry pointer */ + + unsigned char *rbuf = NULL; /* Read buffer */ + + unsigned char *rbufptr; /* Read buffer data pointer */ + + unsigned char *sbuf = NULL; /* Skip buffer */ + + unsigned int skipsize = 0; /* Number of bytes to skip */ + + unsigned int ncolors = 0; /* Number of colors used */ + + unsigned int x, y; /* Width and Height */ + + int i; /* Counters */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * Read in icon image info. + + */ + + if( ImBinReadStruct( ioType, fd, fp, &info, imIcoInfoFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + + + /* + + * Allocate a VFBs of the required size and type. + + */ + + x = info.ico_biwidth; + + y = info.ico_biheight; + + switch( info.ico_bibitcount ) + + { + + case 1: /* 1-bit index image */ + + case 4: /* 4-bit index image */ + + if((vfb = ImVfbAlloc( x, y, IMVFBINDEX8 | IMVFBALPHA)) == IMVFBNULL) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + break; + + + + default: /* unsupported image depth */ + + ImErrorFatal( ImQError( ), -1, IMEDEPTH ); + + } + + + + + + /* + + * Allocate and read in colortable + + */ + + ncolors = (1 << info.ico_bibitcount); + + switch( ncolors ) + + { + + case 2: /* 1-bit, monochrome */ + + case 16: /* 4-bit, indexed color */ + + break; + + default: + + ImErrorFatal( ImQError( ), -1, IMECLTLENGTH ); + + /* output warning */ + + } + + + + clt = ImCltAlloc( ncolors ); + + if ( clt == IMCLTNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + cltptr = ImCltQFirst( clt ); + + ImMalloc( rbuf, unsigned char*, ncolors*IMICORGBQUADSIZE ); + + rbufptr = rbuf; + + if( ImBinRead( ioType, fd, fp, rbuf, UCHAR, 1, + + ncolors*IMICORGBQUADSIZE ) == -1 ) + + { + + ImReturnBinError( ); + + } + + for( i = 0; i < ncolors; ++i ) + + { + + ImCltSBlue( cltptr, *rbufptr ); + + ++rbufptr; + + ImCltSGreen( cltptr, *rbufptr ); + + ++rbufptr; + + ImCltSRed( cltptr, *rbufptr ); + + ++rbufptr; + + /* skip reserved field */ + + ++rbufptr; + + + + cltptr = ImCltQNext( clt, cltptr ); + + } + + free( rbuf ); + + + + + + /* + + * Attach colortable to vfb and append to tagTable + + */ + + ImVfbSClt( vfb, clt ); + + TagTableAppend( tagTable, TagEntryAlloc( "image clt", + + POINTER, &clt) ); + + + + + + /* + + * Read in XOR mask image + + */ + + switch( info.ico_bibitcount ) + + { + + case 1: /* 1-bit index image */ + + if ( imIcoReadImage( ioType, fd, fp, vfb, IMREADMONO ) == -1 ) + + return ( -1 ); /* ImErrNo already set */ + + break; + + + + case 4: /* 4-bit index image */ + + if ( imIcoReadImage( ioType, fd, fp, vfb, IMREADINDEX8 ) == -1 ) + + return ( -1 ); /* ImErrNo already set */ + + break; + + + + } + + + + + + /* + + * Read in AND mask image + + */ + + if ( imIcoReadImage( ioType, fd, fp, vfb, IMREADALPHA ) == -1 ) + + return ( -1 ); /* ImErrNo already set */ + + + + + + /* + + * Add the VFB to the tagTable. + + */ + + TagTableAppend( tagTable, + + TagEntryAlloc( "image vfb", POINTER, &(vfb) ) ); + + + + + + /* + + * Output -verbose message + + */ + + sprintf( message, "%d x %d", x, y ); + + ImInfo( "Resolution", message ); + + if ( info.ico_bibitcount == 1 ) + + { + + ImInfo( "Type", "1-bit Color Indexed (XOR mask)" ); + + } + + else + + { + + ImInfo( "Type", "4-bit Color Indexed (XOR mask)" ); + + } + + sprintf( message, "%d Entries", ncolors ); + + ImInfo( "Color Table", message ); + + ImInfo( "Alpha Channel", "1-bit Monochrome (AND mask)" ); + + + + return ( 2 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imIcoWrite - top level control for writing Ico files + + * + + * DESCRIPTION + + * A file header is written out, followed by a directory entry for + + * each image in the VFB. The VFB's are then processed, one by one, + + * to write them out with their individual headers, color tables, and + + * pixels. + + */ + + + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imIcoWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imIcoWrite( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag list to read from */ + +#endif + +{ + + imIcoHeader header; /* ICO file header */ + + imIcoDirEntry *direntries = NULL;/* List of ICO dir entries */ + + int ndirentries = 0;/* Number of ICO dir entries */ + + imIcoInfo info; /* ICO info */ + + ImVfb *vfb; /* Read in image */ + + ImClt *clt; /* Colortable */ + + unsigned int scanlinesize; /* Length of scanline */ + + unsigned int x, y; /* Width, Height */ + + int loop; /* Loop counter */ + + unsigned int iconx, icony; /* Width and Height of icon */ + + char message[100]; /* ImInfo message */ + + TagEntry *tagEntry; /* Tag table entry holder */ + + int fileOffset; /* Running file offset */ + + + + + + /* + + * Set byte ordering + + */ + + BinByteOrder( BINLBF ); + + + + + + /* + + * Get number of images + + */ + + ndirentries = TagTableQNEntry( tagTable, "image vfb" ); + + ImMalloc( direntries, imIcoDirEntry *, + + (sizeof( imIcoDirEntry ) * ndirentries) ); + + + + + + /* + + * Setup the file header and write it out + + */ + + header.ico_idReserved = 0; + + header.ico_idType = IMICOMAGIC; + + header.ico_idCount = ndirentries; + + + + if ( ImBinWriteStruct( ioType, fd, fp, &header, + + imIcoHeaderFields )==-1) + + { + + ImReturnBinError( ); + + } + + + + + + /* + + * Create and write out a directory entry for each VFB in the tag + + * table. + + * + + * Note: the directory entry for an icon includes a file offset to + + * that icon's data. Fortunately, we can compute this ahead of time + + * by knowing the sizes and depths of the images being written out, + + * knowing their order in the file (the same as that in the tag + + * table), and knowing that they aren't being compressed (yeah!). + + */ + + fileOffset = IMICOHEADERSIZE + /* File header */ + + ndirentries * IMICODIRENTRYSIZE; /* All the dir entries */ + + for (loop = 0; loop < ndirentries; loop++) + + { + + /* + + * Get vfb and clt + + */ + + tagEntry = TagTableQDirect( tagTable, "image vfb", loop ); + + TagEntryQValue( tagEntry, &vfb ); + + clt = ImVfbQClt( vfb ); /* Might be IMCLTNULL */ + + + + + + /* + + * ICO icons are constrained to be 16, 32, or 64 pixels wide + + * and high. If the VFB is larger than these, we'll have to + + * to clip it. + + */ + + x = ImVfbQWidth( vfb ); + + y = ImVfbQHeight( vfb ); + + if ( x >= 64 ) iconx = x = 64; + + else if ( x >= 32 ) iconx = x = 32; + + else if ( x >= 16 ) iconx = x = 16; + + else iconx = 16; + + + + if ( y >= 64 ) icony = y = 64; + + else if ( y >= 32 ) icony = y = 32; + + else if ( y >= 16 ) icony = y = 16; + + else icony = 16; + + + + + + /* + + * Setup icon directory and write it out. + + * + + * Note that only 2 color and 16 color icons are supported. + + * 8 color icons are insufficiently described in the MicroSoft + + * documentation. + + */ + + direntries[loop].ico_Width = iconx; + + direntries[loop].ico_Height = icony; + + + + if ( ImVfbQFields( vfb ) & IMVFBMONO ) + + /* Monochrome or only uses 2 colors. */ + + direntries[loop].ico_ColorCount = 2; + + else + + /* Uses more than 2 colors, so use 16 color version.*/ + + direntries[loop].ico_ColorCount = 16; + + + + direntries[loop].ico_Reserved = 0; + + direntries[loop].ico_Planes = 1; + + if ( direntries[loop].ico_ColorCount == 2 ) + + /* Monochrome */ + + direntries[loop].ico_BitCount = 1; + + else + + /* Color */ + + direntries[loop].ico_BitCount = 4; + + + + /* + + * Resource size is the size of the icon header (IMICOINFOSIZE), + + * plus the size of the color table (IMICORGBQUADSIZE for each + + * entry), plus the icon byte data (icony scanlines), plus the + + * size of the AND mask (iconx * icony bits). + + * + + * The scanline size is computed by calculating the number of + + * bits on a scanline, then packing them into 8-bit bytes, then + + * rounding up to the next higher 32-bit boundary. + + */ + + scanlinesize = (iconx * direntries[loop].ico_BitCount) / 8; + + if ( scanlinesize & 0x3 ) + + scanlinesize = (scanlinesize & ~0x3) + 4; + + direntries[loop].ico_BytesInRes = IMICOINFOSIZE + + + direntries[loop].ico_ColorCount * IMICORGBQUADSIZE + + + (scanlinesize * icony) + (iconx * icony / 8); + + + + direntries[loop].ico_ImageOffset = fileOffset; + + fileOffset += direntries[loop].ico_BytesInRes; + + + + + + /* + + * Write out the directory entry. + + */ + + if ( ImBinWriteStruct( ioType, fd, fp, &direntries[loop], + + imIcoDirEntryFields )==-1) + + { + + ImReturnBinError( ); + + } + + } + + + + + + /* + + * Loop through the images and write them out. + + */ + + for (loop = 0; loop < ndirentries; loop++) + + { + + /* + + * Get vfb and clt + + */ + + tagEntry = TagTableQDirect( tagTable, "image vfb", loop ); + + clt = ImVfbQClt( vfb ); /* Might be IMCLTNULL */ + + + + + + /* + + * ICO icons are constrained to be 16, 32, or 64 pixels wide + + * and high. If the VFB is larger than these, we'll have to + + * to clip it. + + */ + + x = ImVfbQWidth( vfb ); + + y = ImVfbQHeight( vfb ); + + if ( x >= 64 ) iconx = x = 64; + + else if ( x >= 32 ) iconx = x = 32; + + else if ( x >= 16 ) iconx = x = 16; + + else iconx = 16; + + + + if ( y >= 64 ) icony = y = 64; + + else if ( y >= 32 ) icony = y = 32; + + else if ( y >= 16 ) icony = y = 16; + + else icony = 16; + + + + + + /* + + * The scanline size is computed by calculating the number of + + * bits on a scanline, then packing them into 8-bit bytes, then + + * rounding up to the next higher 32-bit boundary. + + */ + + scanlinesize = (iconx * direntries[loop].ico_BitCount) / 8; + + if ( scanlinesize & 0x3 ) + + scanlinesize = (scanlinesize & ~0x3) + 4; + + + + + + /* + + * Setup icon info and write it out + + */ + + info.ico_bisize = IMICOINFOSIZE; + + info.ico_biwidth = iconx; + + info.ico_biheight = icony; + + info.ico_biplanes = 1; + + info.ico_bibitcount = direntries[loop].ico_BitCount; + + info.ico_bicompress = 0; + + info.ico_bisizeimage = scanlinesize * icony; + + info.ico_bixpm = 0; + + info.ico_biypm = 0; + + info.ico_biclrused = 0; + + info.ico_biclrim = 0; + + if ( ImBinWriteStruct( ioType, fd, fp, &info, + + imIcoInfoFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + + + /* + + * Output -verbose messages + + */ + + ImInfo( "Byte Order", "Least Significant Byte First" ); + + sprintf( message, "%d of %d", loop+1, ndirentries ); + + ImInfo( "Image", message ); + + sprintf( message, "%d x %d", iconx, icony ); + + ImInfo( "Resolution", message ); + + if ( info.ico_bibitcount == 1 ) + + { + + ImInfo( "Type", "1-bit Color Indexed (XOR mask)" ); + + ImInfo( "Color Table", "2 Entries" ); + + } + + else + + { + + ImInfo( "Type", "4-bit Color Indexed (XOR mask)" ); + + ImInfo( "Color Table", "16 Entries" ); + + } + + ImInfo( "Alpha Channel", "1-bit Monochrome (AND mask)" ); + + + + + + /* + + * Write out the icon. + + */ + + if ( info.ico_bibitcount == 1 ) + + imIcoWriteRaw1( pMap, ioType, fd, fp, vfb, clt, + + &direntries[loop] ); + + else + + imIcoWriteRaw4( pMap, ioType, fd, fp, vfb, clt, + + &direntries[loop] ); + + } + + + + free( (char *)direntries ); + + return ndirentries; + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imIcoWriteImage - write an icon image + + * + + * DESCRIPTION + + * An icon image is written out, packing multiple pixels to a byte + + * as appropriate. + + */ + + + +#define IMWRITEMONO 0 + +#define IMWRITEINDEX8 1 + +#define IMWRITEALPHA 2 + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imIcoWriteImage( int ioType, int fd, FILE *fp, ImVfb *vfb, int what ) + +#else + +imIcoWriteImage( ioType, fd, fp, vfb, what ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + ImVfb *vfb; /* Read in image */ + + int what; /* What to write out */ + +#endif + +{ + + ImVfbPtr vfbptr; /* Vfb pixel pointer */ + + unsigned char pvalue; /* Pixel value */ + + unsigned char *wbuf; /* Pointer to write buffer */ + + unsigned char *wbufptr; /* Pointer to write buffer data */ + + unsigned int scanlinesize; /* Length of scanline */ + + unsigned char bitfield; /* Bitfield of 8 pixels */ + + unsigned int x, y; /* Width, Height */ + + int iconx, icony; /* Written icon size */ + + int i, j, k; /* Counters */ + + int xgroup; /* Number of pixel groups */ + + int xleft; /* Left over for last group */ + + + + + + /* + + * Compute the written icon size (rounded up to 16, 32, or 64 + + * pixels). + + */ + + x = ImVfbQWidth( vfb ); + + y = ImVfbQHeight( vfb ); + + if ( x >= 64 ) iconx = x = 64; + + else if ( x >= 32 ) iconx = x = 32; + + else if ( x >= 16 ) iconx = x = 16; + + else iconx = 16; + + + + if ( y >= 64 ) icony = y = 64; + + else if ( y >= 32 ) icony = y = 32; + + else if ( y >= 16 ) icony = y = 16; + + else icony = 16; + + + + + + /* + + * Allocate write buffer space + + */ + + switch ( what ) + + { + + case IMWRITEMONO: + + case IMWRITEALPHA: + + scanlinesize = (iconx * 1) / 8; + + xgroup = x/8; /* 8 1-bit quantities per byte */ + + xleft = x%8; + + break; + + case IMWRITEINDEX8: + + scanlinesize = (iconx * 4) / 8; + + xgroup = x/2; /* 2 4-bit quantities per byte */ + + xleft = x%2; + + break; + + } + + if ( scanlinesize & 0x3 ) + + scanlinesize = (scanlinesize & ~0x3) + 4; + + ImCalloc( wbuf, unsigned char*, scanlinesize, sizeof( unsigned char) ); + + + + + + /* + + * Write image + + */ + + if ( what == IMWRITEALPHA && !(ImVfbQFields( vfb ) & IMVFBALPHA) ) + + { + + /* No alpha on VFB. Write a zero alpha. */ + + memset( wbuf, 0x00, scanlinesize ); + + for ( i = 0; i < icony; ++i ) + + { + + if( ImBinWrite( ioType, fd, fp, wbuf, UCHAR, 1, + + scanlinesize )== -1 ) + + { + + ImReturnBinError( ); + + } + + } + + free( (char *)wbuf ); + + return ( 0 ); + + } + + + + if ( y < icony ) + + { + + /* + + * Image is smaller than icon being written. Dump some + + * empty lines first. + + */ + + memset( wbuf, 0x00, scanlinesize ); + + for ( i = icony - y; i > 0; i-- ) + + { + + if( ImBinWrite( ioType,fd,fp,wbuf,UCHAR, + + 1,scanlinesize )== -1 ) + + { + + ImReturnBinError( ); + + } + + } + + } + + + + + + for ( i = y-1; i >= 0; --i ) + + { + + vfbptr = ImVfbQPtr( vfb, 0, i ); + + memset( wbuf, 0x00, scanlinesize ); + + wbufptr = wbuf; + + for( j = 0; j < xgroup; ++j ) + + { + + switch ( what ) + + { + + case IMWRITEMONO: /* Pack 8 pixels to a byte */ + + for ( bitfield = 0, k = 0; k < 8; ++k ) + + { + + pvalue = ImVfbQMono( vfb, vfbptr ); + + if ( pvalue ) + + bitfield |= (1 << (7-k)); + + ImVfbSInc( vfb, vfbptr ); + + } + + break; + + + + case IMWRITEALPHA:/* Pack 8 pixels to a byte */ + + for ( bitfield = 0, k = 0; k < 8; ++k ) + + { + + pvalue = ImVfbQAlpha( vfb, vfbptr ); + + if ( pvalue < 128 ) + + bitfield |= (1 << (7-k)); + + ImVfbSInc( vfb, vfbptr ); + + } + + break; + + + + case IMWRITEINDEX8:/* Pack 2 pixels to a byte */ + + pvalue = ImVfbQIndex8( vfb, vfbptr ) & 0xF; + + bitfield = pvalue << 4; + + ImVfbSInc( vfb, vfbptr ); + + pvalue = ImVfbQIndex8( vfb, vfbptr ) & 0xF; + + bitfield |= pvalue; + + ImVfbSInc( vfb, vfbptr ); + + break; + + } + + *wbufptr++ = bitfield; + + } + + + + if ( xleft ) + + { + + switch ( what ) + + { + + case IMWRITEMONO: /* Pack <8 pixels to a byte */ + + for ( bitfield = 0, k = 0; k < xleft; ++k ) + + { + + pvalue = ImVfbQMono( vfb, vfbptr ); + + if ( pvalue ) + + bitfield |= (1 << (7-k)); + + ImVfbSInc( vfb, vfbptr ); + + } + + break; + + + + case IMWRITEALPHA:/* Pack <8 pixels to a byte */ + + for ( bitfield = 0, k = 0; k < xleft; ++k ) + + { + + pvalue = ImVfbQAlpha( vfb, vfbptr ); + + if ( pvalue < 128 ) + + bitfield |= (1 << (7-k)); + + ImVfbSInc( vfb, vfbptr ); + + } + + break; + + + + case IMWRITEINDEX8:/* Pack <2 pixels to a byte */ + + pvalue = ImVfbQIndex8( vfb, vfbptr ) & 0xF; + + bitfield = pvalue << 4; + + ImVfbSInc( vfb, vfbptr ); + + break; + + } + + *wbufptr++ = bitfield; + + } + + + + if( ImBinWrite( ioType,fd,fp,wbuf,UCHAR,1,scanlinesize ) == -1 ) + + { + + ImReturnBinError( ); + + } + + } + + + + + + free( (unsigned char*) wbuf ); + + + + return ( 0 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imIcoWriteRaw1 - write a 1-bit non-compressed ICO file + + * + + * DESCRIPTION + + * An icon header and 2-color color table is written out, followed by + + * the monochrome icon image. + + */ + + + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imIcoWriteRaw1( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, ImVfb *vfb, ImClt *clt, imIcoDirEntry *direntry ) + +#else + +imIcoWriteRaw1( pMap, ioType, fd, fp, vfb, clt, direntry ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + ImVfb *vfb; /* Read in image */ + + ImClt *clt; /* Colortable */ + + imIcoDirEntry *direntry; /* ICO dir entry */ + +#endif + +{ + + ImVfbPtr vfbptr; /* Vfb pixel pointer */ + + imIcoRGBQuad cltentry; /* Colortable RGBQuad entry */ + + ImCltPtr cltptr; /* Colortable entry pointer */ + + unsigned char bitfield; /* Bitfield of 8 pixels */ + + + + + + /* + + * Write colortable + + */ + + if ( clt == IMCLTNULL ) + + { + + /* Define the first entry to be black, by default. */ + + cltentry.reserved = 0; + + cltentry.blue = 0; + + cltentry.green = 0; + + cltentry.red = 0; + + if ( ImBinWriteStruct( ioType, fd, fp, &cltentry, + + imIcoRGBQuadFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + /* And the second to be white. */ + + cltentry.blue = 255; + + cltentry.green = 255; + + cltentry.red = 255; + + if ( ImBinWriteStruct( ioType, fd, fp, &cltentry, + + imIcoRGBQuadFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + } + + else + + { + + /* Use the first and second entries. */ + + cltptr = ImCltQFirst( clt ); + + cltentry.reserved = 0; + + cltentry.blue = ImCltQBlue( cltptr ); + + cltentry.green = ImCltQGreen( cltptr ); + + cltentry.red = ImCltQRed( cltptr ); + + if ( ImBinWriteStruct( ioType, fd, fp, &cltentry, + + imIcoRGBQuadFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + if ( ImCltQNColors( clt ) > 1 ) + + { + + cltptr = ImCltQNext( clt, cltptr ); + + cltentry.blue = ImCltQBlue( cltptr ); + + cltentry.green = ImCltQGreen( cltptr ); + + cltentry.red = ImCltQRed( cltptr ); + + } + + else + + { + + cltentry.blue = 255; + + cltentry.green = 255; + + cltentry.red = 255; + + } + + if ( ImBinWriteStruct( ioType, fd, fp, &cltentry, + + imIcoRGBQuadFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + } + + + + + + /* + + * Write XOR mask and AND mask. + + */ + + if ( imIcoWriteImage( ioType, fd, fp, vfb, IMWRITEMONO ) != 0 ) + + return ( -1 ); /* ImErrNo already set */ + + if ( imIcoWriteImage( ioType, fd, fp, vfb, IMWRITEALPHA ) != 0 ) + + return ( -1 ); /* ImErrNo already set */ + + + + return ( 1 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imIcoWriteRaw4 - write a 4-bit non-compressed ICO file + + * + + * DESCRIPTION + + * An icon header and 16-color color table is written out, followed by + + * the color icon image. + + */ + + + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imIcoWriteRaw4( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, ImVfb *vfb, ImClt *clt, imIcoDirEntry *direntry ) + +#else + +imIcoWriteRaw4( pMap, ioType, fd, fp, vfb, clt, direntry ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + ImVfb *vfb; /* Read in image */ + + ImClt *clt; /* Colortable */ + + imIcoDirEntry *direntry; /* ICO dir entry */ + +#endif + +{ + + ImVfb *newvfb; /* Icon image */ + + ImCltPtr cltptr; /* Colortable entry pointer */ + + unsigned char *wbuf; /* Pointer to write buffer */ + + unsigned char *wbufptr; /* Pointer to write buffer data */ + + unsigned int ncolors = 0; /* Number of colors used */ + + unsigned int x, y; /* Width, Height */ + + int i; /* Counter */ + + unsigned int iconx, icony; /* Width, Height of result icon */ + + char message[100]; /* ImInfo message */ + + + + + + x = ImVfbQWidth( vfb ); + + y = ImVfbQHeight( vfb ); + + if ( x >= 64 ) iconx = x = 64; + + else if ( x >= 32 ) iconx = x = 32; + + else if ( x >= 16 ) iconx = x = 16; + + else iconx = 16; + + if ( y >= 64 ) icony = y = 64; + + else if ( y >= 32 ) icony = y = 32; + + else if ( y >= 16 ) icony = y = 16; + + else icony = 16; + + + + + + /* + + * We may have to reduce the number of colors in the image in order + + * to make it fit into a 16 color table. To generally simplify the + + * whole process, we'll copy the upper-left quadrant of the image + + * into a new temporary VFB (up to 64 x 64) and only convert that + + * smaller image. + + */ + + newvfb = ImVfbAlloc( iconx, icony, IMVFBINDEX8 | IMVFBALPHA ); + + ImVfbClear( IMVFBINDEX8, 0, newvfb ); + + ImVfbClear( IMVFBALPHA, 255, newvfb ); + + ImVfbSClt( newvfb, ImVfbQClt( vfb ) ); + + ImVfbCopy( vfb, 0, 0, x, y, IMVFBINDEX8 | IMVFBALPHA, newvfb, 0, 0 ); + + vfb = newvfb; + + + + + + /* + + * Reduce the number of colors (if necessary) and get clt + + */ + + ImVfbToIndex( vfb, 16, vfb ); + + clt = ImVfbQClt( vfb ); + + ncolors = ImCltQNColors( clt ); + + if( ncolors > 16 ) + + ncolors = 16; + + + + + + + + /* + + * Write colortable + + */ + + cltptr = ImCltQFirst( clt ); + + ImCalloc( wbuf, unsigned char*, 16, IMICORGBQUADSIZE ); + + memset( wbuf, 0x00, 16 * IMICORGBQUADSIZE ); + + wbufptr = wbuf; + + for( i = 0; i < ncolors; ++i ) + + { + + *wbufptr++ = ImCltQBlue( cltptr ); + + *wbufptr++ = ImCltQGreen( cltptr ); + + *wbufptr++ = ImCltQRed( cltptr ); + + wbufptr++; /* skip reserved field */ + + cltptr = ImCltQNext( clt, cltptr ); + + } + + if ( ImBinWrite( ioType, fd, fp, wbuf, UCHAR, 1, + + 16*IMICORGBQUADSIZE ) == -1 ) + + { + + ImReturnBinError( ); + + } + + free( wbuf ); + + + + + + /* + + * Write XOR mask and AND mask. + + */ + + if ( imIcoWriteImage( ioType, fd, fp, vfb, IMWRITEINDEX8 ) != 0 ) + + return ( -1 ); /* ImErrNo already set */ + + if ( imIcoWriteImage( ioType, fd, fp, vfb, IMWRITEALPHA ) != 0 ) + + return ( -1 ); /* ImErrNo already set */ + + + + + + ImVfbFree( newvfb ); + + + + return ( 1 ); + +} + diff --git a/utils/roq2/libim/imicon.c b/utils/roq2/libim/imicon.c new file mode 100644 index 0000000..7be53ec --- /dev/null +++ b/utils/roq2/libim/imicon.c @@ -0,0 +1,1104 @@ +/** + + ** $Header: /roq/libim/imicon.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/imicon.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** imicon.c - Sun icon/cursor format I/O + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** imicon.c contains routines to read and write Sun Icon files for + + ** the image manipulation library. Raster data read in is stored + + ** in a VFB and optional CLT in a tag list. Raster data written + + ** out is taken from a tag list. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** none + + ** + + ** PRIVATE CONTENTS + + ** imIconRead f read a Sun Icon file + + ** imIconWrite f write a Sun Icon file + + ** + + ** HISTORY + + ** $Log: /roq/libim/imicon.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.10 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.9 1995/06/15 20:27:45 bduggan + + ** removed unused var.s + + ** + + ** Revision 1.8 1995/04/03 21:26:45 bduggan + + ** took out #ifdef NEWMAGIC + + ** + + ** Revision 1.7 1995/01/10 23:31:08 bduggan + + ** uncapitlized i's in local functions + + ** made read/write routines static + + ** + + ** Revision 1.6 94/10/03 11:30:15 nadeau + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Rearranged magic number structures for format handlers. + + ** Made format handler routines static (i.e., local to file). + + ** Updated comments, adding format descriptions and references. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.5 92/11/23 18:42:34 nadeau + + ** Removed use of IMINFOMSG. + + ** + + ** Revision 1.4 92/11/04 12:01:29 groening + + ** put ImFIleFormat info and magic number info + + ** from imfmt.c into this file. + + ** + + ** Revision 1.3 92/09/29 17:58:16 vle + + ** Added ImInfo messages. + + ** + + ** Revision 1.2 92/09/01 20:15:51 vle + + ** Updated copyright notice. + + ** + + ** Revision 1.1 91/10/03 09:05:46 nadeau + + ** Initial revision + + ** + + **/ + + + +#include + +#include "iminternal.h" + + + + + +/** + + ** FORMAT + + ** icon - Sun Icon/Cursor/Image + + ** + + ** AKA + + ** icon, cursor, pr + + ** + + ** FORMAT REFERENCES + + ** - iconedit(1) from the Sun OpenWindows man page set. + + ** - Pixrect Reference Manual, Sun Microsystems. + + ** + + ** CODE CREDITS + + ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1992. + + ** + + ** DESCRIPTION + + ** Sun icon files are used to describe icons, cursors, widgets, and + + ** full black-and-white images on Sun workstations. Originally used + + ** for the Sunview window system, the Sun icon format is now used by + + ** OpenWindows and XView. + + ** + + ** Sun icon files are simple C code including a comment header, and + + ** a string of comma-separated hex values. + + ** + + ** The comment header has the format: + + ** + + ** + + ** Format_version=1, Width=XX, Height=YY, Depth=1, Valid_bits_per_item=16 + + ** + + ** + + ** The Format_version is always 1. + + ** + + ** The Width and Height fields give the width (XX) and height (YY) of + + ** the icon in pixels. The width **MUST** be a multiple of 16! When + + ** writing images appropriate black padding is added to the right side. + + ** ( Note: The PBM suite chose to pad by putting equal padding on the + + ** left and right side. We choose to pad by adding only on the right. + + ** This seems more intuitive and easier to compensate for during use. ) + + ** + + ** The Depth is always 1. + + ** + + ** The Valid_bits_per_item is always 16. + + ** + + ** The comment header may be preceded by blank lines and other comments, + + ** and may be followed by additional comments and white space. Most of + + ** the standard icons found in /usr/include/images on Sun systems + + ** have headers of the form: + + ** + + ** + + ** Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16 + + ** Description: Tombstone with letters R.I.P + + ** Background: White + + ** + + ** Following the file header is a list of hex values for the icon. + + ** Any number of values may occur per line, with any amount of white + + ** space, and any number of lines. Each hex value is of the form: + + ** "0x0000" (without the quotes). Hex values are separated by a comma. + + ** + + ** The number of hex values may be computed from the width and height: + + ** + + ** nvalues = Width ** Height / Valid_bits_per_item + + ** + + ** 0 bits are black. 1 bits are white. Bits are ordered with + + ** the left-most pixel bit the left-most (highest) in each 16-bit value. + + **/ + + + +/* + + * ICON - Sun Icon and Cursor bitmap + + * For information on these structures, how to use them, etc. please + + * see imfmt.c. + + */ + +#ifdef __STDC__ + +static int imIconRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imIconWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +#else + +static int imIconRead( ); + +static int imIconWrite(); + +#endif + + + +static char *imIconNames[ ] = { "icon", "cursor", "pr", NULL }; + +static ImFileFormatReadMap imIconReadMap[ ] = + +{ + + /* in out */ + + /* type,ch,dep, attr. VFB type attr. */ + + { IN,1,1, 0, IMVFBMONO, 0 }, + + { -1, 0, -1, 0 }, + +}; + +static ImFileFormatWriteMap imIconWriteMap[ ] = + +{ + + /* in out */ + + /* VFB type, attr., type,ch,dep, attr., func */ + + { IMVFBMONO, 0, IN,1,1, 0, imIconWrite }, + + { -1, 0, -1, 0, NULL }, + +}; + + + +static ImFileMagic imFileIconMagic []= + +{ + + { 0, 0, NULL}, + +}; + + + +ImFileFormat ImFileIconFormat = + +{ + + imIconNames, "Sun Icon and Cursor file", + + "Sun Microsystems, Inc.", + + "1-bit ASCII bitmap files.", + + "1-bit ASCII bitmap files.", + + imFileIconMagic, + + IMNOMULTI, IMPIPE, + + IMNOMULTI, IMPIPE, + + imIconRead, imIconReadMap, imIconWriteMap + +}; + + + + + + + +/* + + * FUNCTION + + * imIconRead - read an Sun Icon file + + * + + * DESCRIPTION + + * The file is read and it's mono image written to a new mono VFB. + + */ + + + +static int /* Returns # tags read in */ + +#ifdef __STDC__ + +imIconRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imIconRead( ioType, fd, fp, flagsTable, tagTable ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + +#endif + +{ + + int xdim; /* # columns */ + + int ydim; /* # rows */ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pPixel; /* Pixel pointer */ + + ImVfbPtr pLast; /* Last pixel pointer */ + + int pixel; /* 16-bits of pixels */ + + int i, j, k; /* Counters */ + + + + int c; /* Character holder */ + + char token[1024]; /* Input token */ + + char *t; /* Token pointer */ + + int value; /* Token value */ + + int found; /* Header information found? */ + + char message[100]; /* ImInfo message */ + + + + + + if ( (ioType & IMFILEIOFD) && (fp = fdopen( fd, "rb" )) == NULL ) + + { + + ImErrNo = IMESYS; + + return ( -1 ); + + } + + + + + + for ( xdim = ydim = -1, found = FALSE; !found; ) + + { + + /* Skip to the start of a C comment. */ + + while ( (c = getc( fp )) != '/' && c != EOF ) + + ; + + if ( c == EOF ) + + ImErrorFatal( "Premature EOF in icon file header", -1, IMESYNTAX ); + + if ( (c = getc( fp )) != '*' ) + + continue; + + c = getc( fp ); + + + + for ( ; ; ) + + { + + /* Skip whitespace. */ + + for ( ; isspace( c ) || c == ','; c = getc( fp ) ) + + ; + + if ( c == EOF ) + + ImErrorFatal( "Premature EOF in icon file header", -1, IMESYNTAX ); + + + + if ( c == '*' ) + + { + + if ( (c = getc( fp )) == '/' ) + + break; /* Done with comment */ + + continue; + + } + + + + /* Get the next token. */ + + t = token; + + for ( *t++ = c; (c = getc( fp )) != EOF && + + c != ',' && c != '=' && c != '*' && !isspace(c); + + *t++ = c ) + + ; + + if ( c == EOF ) + + ImErrorFatal( "Premature EOF in icon file header", -1, IMESYNTAX ); + + *t = '\0'; + + + + if ( c == '*' ) + + { + + if ( (c = getc( fp )) == '/' ) + + break; /* Done with comment */ + + continue; + + } + + + + if ( c != '=' ) + + { + + /* Not a token we want. Skip to whitespace*/ + + for ( ; !isspace( c ); c = getc( fp ) ) + + ; + + continue; + + } + + + + /* Get the value following the '='. */ + + if ( fscanf( fp, "%d", &value ) != 1 ) + + { + + /* No value. Not a token we want. */ + + for ( c = getc( fp ); !isspace( c ); c = getc( fp ) ) + + ; + + continue; + + } + + c = getc( fp ); + + + + /* Check which token it is. */ + + if ( strcmp( token, "Format_version" ) == 0 ) + + { + + if ( value != 1 ) + + ImErrorFatal( "Only format version 1 Sun icon files are supported", -1, IMESYNTAX ); + + found = TRUE; + + + + ImInfo( "Version", "1" ); + + continue; + + } + + if ( strcmp( token, "Width" ) == 0 ) + + { + + if ( value <= 0 ) + + ImErrorFatal( "Sun icon width must be positive", -1, IMESYNTAX ); + + if ( (value & 0xF) != 0 ) + + ImErrorFatal( "Sun icon width must be multiple of 16 pixels", -1, IMESYNTAX ); + + xdim = value; + + found = TRUE; + + continue; + + } + + if ( strcmp( token, "Height" ) == 0 ) + + { + + if ( value <= 0 ) + + ImErrorFatal( "Sun icon height must be positive", -1, IMESYNTAX ); + + ydim = value; + + found = TRUE; + + continue; + + } + + if ( strcmp( token, "Depth" ) == 0 ) + + { + + if ( value != 1 ) + + ImErrorFatal( "Only Depth=1 Sun icon files are supported", -1, IMESYNTAX ); + + found = TRUE; + + continue; + + } + + if ( strcmp( token, "Valid_bits_per_item" ) == 0 ) + + { + + if ( value != 16 ) + + ImErrorFatal( "Only Valid_bits_per_item=16 Sun icon files are supported", -1, IMESYNTAX ); + + found = TRUE; + + continue; + + } + + + + /* Unknown token. Skip to whitespace. */ + + for ( ; !isspace( c ); c = getc( fp ) ) + + ; + + } + + } + + + + if ( xdim == -1 ) + + ImErrorFatal( "Missing width for Sun icon", -1, IMESYNTAX ); + + if ( ydim == -1 ) + + ImErrorFatal( "Missing height for Sun icon", -1, IMESYNTAX ); + + + + sprintf( message, "%d x %d", xdim, ydim ); + + ImInfo( "Resolution", message ); + + + + ImInfo( "Type", "1-bit Monochrome" ); + + + + /* + + * Allocate a VFB of the required size. + + */ + + if ( (vfb = ImVfbAlloc( xdim, ydim, IMVFBMONO )) == IMVFBNULL ) + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + + + + + /* + + * Scan the file, reading in each hex character value holding + + * 16 bits. Unpack the bits into mono values (0 = black, + + * 1 = white). + + */ + + pPixel = ImVfbQFirst( vfb ); + + for ( i = 0; i < ydim; i++ ) + + { + + /* + + * Handle a scanline, modulo 16. + + */ + + for ( j = 0; j < xdim; j += 16 ) + + { + + /* + + * Skip to the next hex number, read it in, and + + * break it down into mono single-bit pixels. + + */ + + for ( c = getc( fp ); c != 'x' && c != EOF ; ) + + c = getc( fp ); + + if ( c == EOF ) + + break; + + + + if ( fscanf( fp, "%x", &pixel ) != 1 ) + + break; + + for ( k = 15; k >= 0; k-- ) + + { + + ImVfbSMono( vfb, pPixel, ((pixel>>k)&1) ); + + ImVfbSInc( vfb, pPixel ); + + } + + } + + if ( c == EOF ) + + break; + + } + + + + for ( pLast=ImVfbQLast( vfb ); pPixel <= pLast; ImVfbSInc( vfb, pPixel)) + + ImVfbSMono( vfb, pPixel, 0 ); + + + + + + /* + + * Add the VFB to the tagTable. + + */ + + TagTableAppend( tagTable, TagEntryAlloc( "image vfb", POINTER, &vfb ) ); + + + + return ( 1 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imIconWrite - write a Sun Icon file + + * + + * DESCRIPTION + + * The Sun Icon header and image content are written out. + + */ + + + +static int /* Returns # of entries used */ + +#ifdef __STDC__ + +imIconWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imIconWrite( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* Type of I/O to perform */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Table of info to write */ + +#endif + +{ + + int xdim; /* # columns */ + + int xdim2; /* # columns (nearest 8bit bndry)*/ + + int xdimExtra; /* # columns mod 8 */ + + int ydim; /* # rows */ + + ImVfb *srcVfb; /* VFB to convert */ + + ImVfbPtr pPixel; /* pixel pointer */ + + char buf[1024]; /* Output buffer */ + + int pixel; /* pixel value */ + + int j, k; /* Counters */ + + int nchars; /* Number of characters output */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * Write out the header. + + */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &srcVfb ); + + xdim = ImVfbQWidth( srcVfb ); + + ydim = ImVfbQHeight( srcVfb ); + + + + xdimExtra = xdim & 0xF; /* Bits in last 2 bytes */ + + xdim2 = xdim - xdimExtra; /* Bits before last 2 bytes */ + + j = ((xdim + 15) / 16) * 16; /* Bits rounded up to next 16*/ + + + + sprintf( buf, "/* Format_version=1, Width=%d, Height=%d, Depth=1, Valid_bits_per_item=16\n */\n", j, ydim ); + + if ( ioType & IMFILEIOFD ) + + write( fd, buf, strlen( buf ) ); + + else + + fprintf( fp, buf ); + + + + /* + + * Output -verbose message + + */ + + ImInfo( "Version", "1" ); + + + + sprintf( message, "%d x %d", j, ydim ); + + ImInfo( "Resolution", message ); + + + + ImInfo( "Type", "1-bit Monochrome" ); + + + + /* + + * Write out the image. + + */ + + pPixel = ImVfbQFirst( srcVfb ); + + for ( nchars = 0; ydim--; ) + + { + + for ( j = 0; j < xdim2; j += 16 ) + + { + + pixel = 0; + + for ( k = 15; k >= 0; k-- ) + + { + + if ( ImVfbQMono( srcVfb, pPixel ) > 0 ) + + pixel |= (1< 72 ) + + { + + strcat( buf, "\n" ); + + nchars = 0; + + } + + if ( ioType & IMFILEIOFD ) + + write( fd, buf, strlen( buf ) ); + + else + + fprintf( fp, buf ); + + } + + if ( xdimExtra == 0 ) + + continue; + + + + pixel = 0; + + for ( k = xdimExtra - 1; k >= 0; k-- ) + + { + + if ( ImVfbQMono( srcVfb, pPixel ) > 0 ) + + pixel |= (1< 72 ) + + { + + strcat( buf, "\n" ); + + nchars = 0; + + } + + if ( ioType & IMFILEIOFD ) + + write( fd, buf, strlen( buf ) ); + + else + + fprintf( fp, buf ); + + } + + if ( ioType & IMFILEIOFD ) + + write( fd, "\n", 2 ); + + else + + fprintf( fp, "\n" ); + + + + return ( 1 ); /* Used 1 tagTable entry: VFB */ + +} + diff --git a/utils/roq2/libim/imiff.c b/utils/roq2/libim/imiff.c new file mode 100644 index 0000000..855c1f2 --- /dev/null +++ b/utils/roq2/libim/imiff.c @@ -0,0 +1,1834 @@ +/** + + ** $Header: /roq/libim/imiff.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/imiff.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** imiff.c - Sun TAAC file format I/O + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** imiff.c contains routines to read and write Sun TAAC IFF files for + + ** the image manipulation library. Raster data read in is stored + + ** in a VFB and optional CLT in a tag list. Raster data written + + ** out is taken from a tag list. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** none + + ** + + ** PRIVATE CONTENTS + + ** imIffRead f read a Sun-TAAC image file + + ** imIffWrite f write a Sun-TAAC image file + + ** + + ** imIffHeaderInfo t IFF file header information + + ** imGetFDStr f Read a stream of bytes + + ** + + ** HISTORY + + ** $Log: /roq/libim/imiff.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.24 1995/06/30 22:16:18 bduggan + + ** changed index to strchr + + ** + + ** Revision 1.23 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.22 1995/06/15 20:30:31 bduggan + + ** changed "dont" to "don't". Added cast for strncmp. Removed prototype for strtok. + + ** + + ** Revision 1.21 1995/04/03 21:27:18 bduggan + + ** took out #ifdef NEWMAGIC + + ** + + ** Revision 1.20 1995/02/16 21:40:27 bduggan + + ** Made write function static + + ** + + ** Revision 1.19 1995/01/16 22:09:45 bduggan + + ** Added prototype for index() + + ** + + ** Revision 1.18 1995/01/10 23:31:31 bduggan + + ** uncapitlized i's in local functions + + ** put in IMMULTI, IMPIPE instead of TRUE/FALSE + + ** + + ** Revision 1.18 1995/01/10 23:31:31 bduggan + + ** uncapitlized i's in local functions + + ** put in IMMULTI, IMPIPE instead of TRUE/FALSE + + ** + + ** Revision 1.17 94/10/03 11:30:16 nadeau + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Rearranged magic number structures for format handlers. + + ** Made format handler routines static (i.e., local to file). + + ** Updated comments, adding format descriptions and references. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.16 92/12/03 01:49:45 nadeau + + ** Corrected info messages. + + ** + + ** Revision 1.15 92/11/23 18:42:36 nadeau + + ** Removed use of IMINFOMSG. + + ** + + ** Revision 1.14 92/11/04 11:58:35 groening + + ** put ImFIleFormat info and magic number info + + ** from imfmt.c into this file. + + ** + + ** Revision 1.13 92/10/19 14:14:10 groening + + ** added ImInfo statements + + ** + + ** Revision 1.12 92/09/02 15:58:04 vle + + ** Updated copyright notice. + + ** + + ** Revision 1.11 92/09/02 15:48:31 todd + + ** modify to handle sun VX/MVX vff files. minor changes. + + ** + + ** Revision 1.10 92/04/09 09:31:00 groening + + ** To make the compiler happy added extern statements. + + ** + + ** Revision 1.9 91/10/04 10:50:21 nadeau + + ** Fixed shift problem for VAX. + + ** + + ** Revision 1.8 91/10/03 09:06:07 nadeau + + ** Fixed #includes. + + ** + + ** Revision 1.7 91/02/12 11:33:09 nadeau + + ** Removed the tag table checking now handled by + + ** ImFileRead and ImFileWrite. + + ** + + ** Revision 1.6 91/01/29 11:09:03 todd + + ** minor changes + + ** + + ** Revision 1.5 90/09/18 08:18:08 todd + + ** Added 8 bit image with colormap functionality. + + ** cleaned up header reading code. + + ** + + ** Revision 1.4 90/07/02 13:20:03 nadeau + + ** Updated to the new error handling mechanism. + + ** + + ** Revision 1.3 90/06/25 14:46:36 nadeau + + ** Changed ImTag* to Tag* (new names). + + ** + + ** Revision 1.2 90/06/25 13:22:24 todd + + ** Fixed a few little bugs. Version 2.4 of voxvu write ^L out to the + + ** image file as the ascii string "^L" where version 2.3 writes it out + + ** as the printf string "\f". I think this is a bug in 2.4. I haven't + + ** done anything about it yet. + + ** + + ** Revision 1.1 90/04/05 17:05:53 todd + + ** Initial revision + + ** + + **/ + + + +#include "iminternal.h" + + + + + +/** + + ** + + ** FORMAT + + ** IFF - Sun TAAC Image File Format + + ** + + ** AKA + + ** vff, suniff, taac + + ** + + ** FORMAT REFERENCES + + ** Sun-Taac User Manual, Volume Rendering Package, Sun Microsystems + + ** + + ** CODE CREDITS + + ** Custom development, Todd Elvins, San Diego Supercomputer Center, 1990. + + ** + + ** DESCRIPTION + + ** A typical IFF ascii header looks like this: + + ** + + ** ncaa + + ** rank=2; + + ** size=512 512; + + ** bands=3; + + ** bits=8 8 8; + + ** format=base; + + ** ^L + + ** (followed by lots of raw pixel data) + + ** + + ** The header can also contain a colormap such as this one: + + ** bands=1; + + ** bits=8; + + ** colormapsize=256; + + ** colormap= ... + + ** + + ** + + ** Storage formats "Block Pseudo" and "Two Bit Movie Dump" are specific + + ** to the Sun-TAAC system, and may well become obsolete within 6 months. + + ** For these reasons they are not supported at this time. + + ** + + ** + + ** + + **/ + + + +#ifdef __STDC__ + +static int imIffRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imIffWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +#else + +static int imIffRead( ), imIffWrite( ); + +#endif + +static char *imIffNames[ ] = { "iff", "vff", "suniff", "taac", NULL }; + +static unsigned char imIffMagicNumber[ ] = { 'n', 'c', 'a', 'a' }; + +static ImFileMagic imFileIffMagic []= + +{ + + { 0, 4, imIffMagicNumber}, + + { 0, 0, NULL }, + +}; + + + +static ImFileFormatReadMap imIffReadMap[ ] = + +{ + + /* in out */ + + /* type,ch,dep, attr. VFB type attr. */ + + { IN,1,8, 0, IMVFBINDEX8, 0 }, + + { IN,1,8, C, IMVFBINDEX8, C }, + + { RGB,3,8, 0, IMVFBRGB, 0 }, + + { RGB,3,8, A, IMVFBRGB, A }, + + { -1, 0, -1, 0 }, + +}; + +static ImFileFormatWriteMap imIffWriteMap[ ] = + +{ + + /* in out */ + + /* VFB type, attr., type,ch,dep, attr., func */ + + { IMVFBINDEX8, 0, IN,1,8, 0, imIffWrite }, + + { IMVFBINDEX8, C, IN,1,8, C, imIffWrite }, + + { IMVFBRGB, 0, RGB,3,8, 0, imIffWrite }, + + { IMVFBRGB, A, RGB,3,8, A, imIffWrite }, + + { -1, 0, -1, 0, NULL }, + +}; + + + + + +ImFileFormat ImFileIffFormat = + +{ + + imIffNames, "Sun TAAC Image File Format", + + "Sun Microsystems, Inc.", + + "8-bit, 24-bit RGB, and 32-bit RGB+alpha image.", + + "8-bit, 24-bit RGB, and 32-bit RGB+alpha image.", + + imFileIffMagic, + + IMNOMULTI, IMNOPIPE, + + IMNOMULTI, IMNOPIPE, + + imIffRead, imIffReadMap, imIffWriteMap + +}; + + + + + + + + + +/* + + * TYPEDEF & STRUCTURE + + * IMIFFMAGIC The magic number at the beginning of an IFF file. + + * IMIFFMAGICCNT The number of bytes of magic number in an IFF file. + + * IMIFF24BITHEADSTRINGS Required number of keywords + + * IMIFFHEADSTRINGS Required plus optional keywords + + * IMIFFSTRLENGTH The number of bytes in a long string. + + * IMIFFLONGBUFF In case we have to read in an ascii colormap + + * IMRANK The index of the rank entry in the imIffHeader array. + + * IMSIZE The index of the size entry in the imIffHeader array. + + * IMBANDS The index of the bands entry in the imIffHeader array. + + * IMBITS The index of the bits entry in the imIffHeader array. + + * IMFORMAT The index of the format entry in the imIffHeader array. + + * IMCOLORMAPSIZE The index of the colormapsize entry in imIffHeader array + + * IMCOLORMAP The index of the colormap entry in the imIffHeader array + + * + + * DESCRIPTION + + * These are keywords to be expected in an IFF file image header. + + */ + + + +#ifdef __STDC__ + +static int imGetFDStr( int ioType, int fd, FILE *fp, char *s ); + +#else + +static int imGetFDStr( ); + +#endif + + + +#define IMIFFMAGIC "ncaa" + +#define IMIFFMAGICCNT 4 + +#define IMIFFSTARTPIXELS 1 + +#define IMIFF24BITHEADSTRINGS 5 + +#define IMIFFHEADSTRINGS 7 + +#define IMIFFSTRLENGTH 256 + +#define IMIFFLONGBUF 8192 + +#define IMRANK 0 + +#define IMSIZE 1 + +#define IMBANDS 2 + +#define IMBITS 3 + +#define IMFORMAT 4 + +#define IMCOLORMAPSIZE 5 + +#define IMCOLORMAP 6 + + + + + +/* + + * TYPEDEF & STRUCTURE + + * imIffHeader - IFF file header information + + * + + * DESCRIPTION + + * A IFF file's header contains the image's dimensionality, + + * width, height, depth, bits/pixel, and storage. + + */ + + + +typedef struct imIffHeaderInfo + +{ + + char *name; + + int set; + +} imIffHeaderInfo ; + + + + + +static imIffHeaderInfo imIffHeader[] = + +{ + + "rank", FALSE, + + "size", FALSE, + + "bands", FALSE, + + "bits", FALSE, + + "format", FALSE, + + "colormapsize", FALSE, + + "colormap", FALSE + +} ; + + + + + + + +/* + + * FUNCTION + + * imGetFDStr - Read a stream of bytes + + * + + * DESCRIPTION + + * Read from the file descriptor fd until EOL. Put the string + + * into s. Return -1 on failure. + + */ + +static int + +#ifdef __STDC__ + +imGetFDStr( int ioType, int fd, FILE *fp, char *s ) + +#else + +imGetFDStr( ioType, fd, fp, s ) + + int ioType; + + int fd; + + FILE *fp; + + char *s; + +#endif + +{ + + char *buf; /* The buffer to fill up */ + + int cnt; /* Count the number of byte read */ + + + + buf = s; + + cnt = 0; + + + + do + + { + + if ( ImBinRead( ioType, fd, fp, buf, CHAR, 1, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + if (++cnt >= IMIFFLONGBUF ) + + { + + ImErrNo = IMESYNTAX; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + if (*buf == 0x0c ) /* ^L */ + + { + + /* Skip the following carriage return */ + + if ( ImBinRead( ioType, fd, fp, buf, CHAR, 1, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + return( IMIFFSTARTPIXELS ); + + } + + } + + while ( *buf++ != '\n' ); + + + + *--buf = '\0'; + + return( 0 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imIffRead - read a Sun-TAAC image file + + * + + * DESCRIPTION + + * The file header is read and the size of the image determined. + + * Space is allocated for the VFB. The image data is stored in + + * either RGB or RGBA. The RGB[A] data is read into the VFB one + + * scanline at a time and the completed VFB is added to the tag list. + + */ + + + +static int /* Returns # tags read in */ + +#ifdef __STDC__ + +imIffRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imIffRead( ioType, fd, fp, flagsTable, tagTable ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag list to add to */ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointer */ + + ImClt *clt=NULL; /* Read in image */ + + ImCltPtr cptr; /* Pixel pointer */ + + unsigned char *runBuffer; /* Run buffer */ + + unsigned char *rbp; /* Run buffer pointer */ + + char *eSign; /* Pointer into an input string*/ + + char *nextstr; /* Pointer into an input string*/ + + unsigned char magic[IMIFFSTRLENGTH]; /* Store the magic number*/ + + char buf[IMIFFLONGBUF]; /* Keyword buffer */ + + char key[IMIFFSTRLENGTH]; /* Keyword buffer */ + + char format[IMIFFSTRLENGTH]; /* Storage == "Base" */ + + char msg[IMIFFSTRLENGTH]; /* Tmp message holder */ + + int rank; /* Dimensionality */ + + int sizex, sizey; /* Image width and height */ + + int bands; /* Either 3 or 4 RGB or RGBA */ + + int bitsr,bitsg,bitsb,bitsa; /* Image depth */ + + int status; /* Returned function status */ + + int cltsize; /* How many clt entries */ + + unsigned int rgbFlag; /* Is there an alpha channel */ + + unsigned int rgbvals; /* An rgb clt entry */ + + int i,j,cnt=0; /* Loop counters */ + + char message[100]; /* ImInfo message */ + + + + /* + + * IFF files are usually generated on Sun-TAACs, which have an MBF + + * byte order. There is no floating point in the file so we + + * won't bother setting the float format for BIN. + + */ + + BinByteOrder( BINMBF ); + + + + /* + + * Read in the magic number and check it. + + */ + + if ( ImBinRead( ioType, fd, fp, magic, UCHAR, 1, IMIFFMAGICCNT+1)== -1 ) + + ImReturnBinError( ); + + + + if ( strncmp( IMIFFMAGIC, (char*)magic, IMIFFMAGICCNT ) != 0) + + { + + ImErrNo = IMEMAGIC; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + /* + + * Read in the file header. + + */ + + while((status = imGetFDStr( ioType, fd, fp, buf )) != IMIFFSTARTPIXELS ) + + { + + if ( status == -1 ) + + return( -1 ); /* ImErrNo already set */ + + + + if ( *buf == '\n' || *buf == '\0' ) + + continue; + + + + /* + + * Get the keyword out of the string into key var + + */ + + if ( (eSign = (char *)strchr( buf, '=' )) == NULL ) + + { + + ImErrNo = IMESYNTAX; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + *eSign++ = '\0'; /* Skip the equal sign */ + + sscanf( buf, "%s", key ); + + + + /* + + * Look in the table of known keywords for a match + + */ + + for( j=0; j>16 & 0xff ); + + ImCltSGreen( cptr, rgbvals>> 8 & 0xff ); + + ImCltSRed( cptr, rgbvals & 0xff ); + + ImCltSInc( clt, cptr ); + + + + for( i=1; i>16 & 0xff ); + + ImCltSGreen( cptr, rgbvals>> 8 & 0xff ); + + ImCltSRed( cptr, rgbvals & 0xff ); + + ImCltSInc( clt, cptr ); + + } + + break; + + + + default: + + sprintf( msg, "Unknown keyword '%s' in file header\n", + + key ); + + ImErrorWarning( msg, -1, IMESYNTAX ); + + + + } /* end switch */ + + + + } /* end foreach keyword loop */ + + + + + + /* + + * Do sanity checking. Some of the keywords are mandatory. + + */ + + for( i=0; imap_outAttributes & IMALPHAYES ) + + bands++; + + } + + + + /* + + * IFF files are usually generated on Sun-TAACs, which have an MBF + + * byte order. There is no floating point in the file so we + + * won't bother setting the float format for BIN. + + */ + + BinByteOrder( BINMBF ); + + + + /* + + * Set up the header and write it out. + + */ + + sizex = ImVfbQWidth( vfb ); + + sizey = ImVfbQHeight( vfb ); + + + + /* + + * Dump out the ascii header + + */ + + sprintf( buf, "ncaa\n\0" ); /* Magic */ + + ImBinWrite( ioType, fd, fp, buf, UCHAR, 1, strlen(buf) ); + + + + sprintf( buf, "rank=%d;\n\0", 2 ); /* Two dimensional */ + + ImBinWrite( ioType, fd, fp, buf, UCHAR, 1, strlen(buf) ); + + + + sprintf( buf, "bands=%d;\n\0", bands ); /* Depth */ + + ImBinWrite( ioType, fd, fp, buf, UCHAR, 1, strlen(buf) ); + + + + sprintf( buf, "size=%d %d;\n\0", sizex, sizey );/* Width height */ + + ImBinWrite( ioType, fd, fp, buf, UCHAR, 1, strlen(buf) ); + + sprintf (message, "%d x %d",sizex,sizey); + + ImInfo ("Resolution",message); + + + + sprintf( buf, "format=%s;\n\0", "base" ); /* Storage format */ + + ImBinWrite( ioType, fd, fp, buf, UCHAR, 1, strlen(buf) ); + + + + if ( bands == 1 ) + + { + + sprintf( buf, "bits=%d;\n\0", bits ); + + ImInfo ("Type","8-bit Color Indexed"); + + } + + else if ( bands == 3 ) + + { + + sprintf( buf, "bits=%d %d %d;\n\0", bits, bits, bits ); + + ImInfo ("Type","24-bit RGB"); + + } + + else if ( bands == 4 ) + + { + + sprintf( buf, "bits=%d %d %d %d;\n\0", bits, bits, bits, bits ); + + ImInfo ("Type","32-bit RGB and Alpha"); + + } + + ImBinWrite( ioType, fd, fp, buf, UCHAR, 1, strlen(buf) ); + + + + if ( pMap->map_outAttributes & IMCLTYES ) + + { + + clt = ImVfbQClt( vfb ); + + cltsize = ImCltQNColors( clt ); + + pclt = ImCltQFirst( clt ); + + + + sprintf (message, "%d Entries",cltsize); + + ImInfo ("Color Table",message); + + + + sprintf( buf, "colormapsize=%d;\n\0", cltsize); + + ImBinWrite( ioType, fd, fp, buf, UCHAR, 1, strlen(buf) ); + + + + sprintf( buf, "colormap=\0"); + + ImBinWrite( ioType, fd, fp, buf, UCHAR, 1, strlen(buf) ); + + + + for( i=0; i and prototypes for strlen and strcpy + ** + ** Revision 1.10 94/10/03 16:05:10 nadeau + ** Added definitions of TRUE, FALSE, L_SET et al, and F_SET et al for + ** internal routine use. + ** Updated copyright message. + ** + ** Revision 1.9 92/12/03 02:19:42 nadeau + ** Updated histogram information. + ** + ** Revision 1.8 92/11/06 17:02:24 groening + ** added #defines for format reading + ** + ** Revision 1.7 92/10/12 16:12:52 vle + ** Added ImInfo declarations. + ** + ** Revision 1.6 92/09/23 15:27:50 groening + ** Added histogram declarations. + ** + ** Revision 1.5 91/10/03 12:57:05 nadeau + ** Added error code to ImErrorHandler() call. + ** + ** Revision 1.4 90/07/23 13:52:38 nadeau + ** Added curly braces to ImMalloc macro. Added ImTell macro. + ** + ** Revision 1.3 90/07/02 13:24:00 nadeau + ** Added new error handling macros and globals and updated existing macros + ** to use them. + ** + ** Revision 1.2 90/06/26 09:10:39 todd + ** Useful macros for writing image file read and write routines. + ** These should be taken out of imras.c someday. + ** + ** Revision 1.1 90/05/15 14:00:06 todd + ** Initial revision + ** + ** + **/ + +#ifndef __IMINTERNALH__ +#define __IMINTERNALH__ + +#ifndef __SDSCCOPYRIGHTH__ +#include "sdsccopyright.h" +#endif + +#include +extern int sys_nerr; +extern char *sys_errlist[]; + +#include +#include + +#include "unistd.h" +#include +#include + +#ifndef __SDSCH__ +#include "sdsc.h" +#endif /* __SDSCH__ */ + +#ifndef __IMH__ +#include "im.h" +#endif /* __IMH__ */ + +#ifndef NULL +#define NULL (0) +#endif /* NULL */ + +#ifndef TRUE +#define TRUE (1) +#endif + +#ifndef FALSE +#define FALSE (0) +#endif + +#include + + + +/* + * CONSTANTS + * L_* - file descriptor seek types + * F_* - file pointer seek types + * + * DESCRIPTION + * Not all OS variants define values for the lseek() and fseek() + * seek types, though the actual type values are highly standardized + * by existing heavy use as raw numbers. If this OS hasn't defined + * constants for these, we do. + */ +#ifndef L_SET +#define L_SET 0 /* Absolute offset */ +#define L_INCR 1 /* Relative to current offset */ +#define L_XTND 2 /* Relative to end of file */ +#endif + +#ifndef F_SET +#define F_SET 0 /* Absolute offset */ +#define F_INCR 1 /* Relative to current offset */ +#define F_XTND 2 /* Relative to end of file */ +#endif + + + + + +/* + * CONSTANTS + * XXX - abbreviations for Read/Write map fields + * + * DESCRIPTION + * These constants are simply shorthands for flags used in initialization + * of the file format read/write maps. They are only provided so that + * such initializations don't look too cluttered. + */ +#define C IMCLTYES /* CLT included */ +#define A IMALPHAYES /* Alpha plane included */ + +#define RLE IMCOMPRLE /* RLE compression */ +#define LZW IMCOMPLZW /* LZW compression */ +#define PB IMCOMPPACKBITS /* Packbits compression */ +#define DCT IMCOMPDCT /* DCT compression */ +#define ASC IMCOMPASCII /* ASCII compression */ + +#define LI IMINTERLINE /* Line interleaved */ +#define PI IMINTERPLANE /* Plane interleaved */ + +#define IN IMTYPEINDEX /* Index type of image */ +#define RGB IMTYPERGB /* RGB type of image */ + +#define T IMGROUPTILES /* Group into tiles */ + +#define Q IMQUALITYYES /* Optional quality control */ + + + +/* + * CONSTANTS + * IMFILEIO... - Image file I/O type + * + * DESCRIPTIONS + * These constants are bitwise OR-ed together to create the ioType + * argument to the internal per-format read and write routines. + * They should never be used by the applications programmer. + */ + +#define IMFILEIOFD 0x1 /* Use file descriptor */ +#define IMFILEIOFP 0x2 /* Use file pointer */ + +#define IMFILEIOFILE 0x10 /* I/O on file */ +#define IMFILEIOPIPE 0x20 /* I/O on pipe */ + + + + + +/* + * MACROS + * ImReturnBinError - set correct error based on Bin call, and return -1 + * ImReturnValBinError - Same as above, but return specified value + * ImCalloc - calloc memory & return -1 on error + * ImCallocRetOnError - calloc memory & return specified value on error + * ImMalloc - malloc memory & return -1 on error + * ImMallocRetOnError - malloc memory & return specified value on error + * ImBinReadStruct - binary struct read from fd or fp + * ImBinRead - binary read from fd or fp + * ImBinWrite - binary write from fd or fp + * ImSeek - file seek from fd or fp + * + * DESCRIPTION + * These actions occur so often in the imfile read/write code that they + * have been turned into macros in order to reduce clutter and make the + * algorithms more apparent. + */ + +#define ImReturnBinError() \ + ImReturnValBinError( -1 ) + +#define ImReturnValBinError( ret ) \ +{ \ + if ( BinErrNo == BINEMALLOC ) \ + { \ + ImErrorFatal( BinQError( ), ret, IMEMALLOC ); \ + } \ + else \ + { \ + ImErrorFatal( BinQError( ), ret, IMESYS ); \ + } \ +} + + +#define ImMallocRetOnError(variable,type,nbytes,retvalue) \ +{ \ + if ( ((variable) = (type)malloc( (nbytes) )) == NULL ) \ + { \ + ImErrNo = IMEMALLOC; \ + ImErrorFatal( ImQError( ), retvalue, IMEMALLOC ); \ + } \ +} + + + +#define ImMalloc(variable,type,nbytes) \ + ImMallocRetOnError(variable,type,nbytes,-1) + + +#define ImCallocRetOnError(variable,type,nbytes,size,errvalue) \ +{ \ + if ( ((variable) = (type)calloc( (nbytes),(size) )) == NULL ) \ + { \ + ImErrNo = IMEMALLOC; \ + ImErrorFatal( ImQError( ), errvalue, IMEMALLOC ); \ + } \ +} + + +#define ImCalloc(variable,type,nbytes,size) \ + ImCallocRetOnError(variable,type,nbytes,size,-1) + +#define ImBinReadStruct(ioType,fd,fp,buf,fields) \ + (((ioType)&IMFILEIOFD) ? \ + BinReadStruct( (fd), (buf), (fields) ) \ + : \ + BinFReadStruct( (fp), (buf), (fields) ) \ + ) + +#define ImBinRead(ioType,fd,fp,buf,type,nbytes,len) \ + (((ioType)&IMFILEIOFD) ? \ + BinRead( (fd), (buf), (type), (nbytes), (len) ) \ + : \ + BinFRead( (fp), (buf), (type), (nbytes), (len) ) \ + ) + +#define ImBinWriteStruct(ioType,fd,fp,buf,fields) \ + (((ioType)&IMFILEIOFD) ? \ + BinWriteStruct( (fd), (buf), (fields) ) \ + : \ + BinFWriteStruct( (fp), (buf), (fields) ) \ + ) + +#define ImBinWrite(ioType,fd,fp,buf,type,nbytes,len) \ + (((ioType)&IMFILEIOFD) ? \ + BinWrite( (fd), (buf), (type), (nbytes), (len) ) \ + : \ + BinFWrite( (fp), (buf), (type), (nbytes), (len) ) \ + ) + +#define ImSeek(ioType,fd,fp,offset,type) \ + (((ioType)&IMFILEIOFD) ? \ + lseek( (fd), (offset), (type) ) \ + : \ + fseek( (fp), (offset), (type) ) \ + ) + +#define ImTell(ioType,fd,fp) \ + (((ioType)&IMFILEIOFD) ? \ + lseek( (fd), 0, 1 ) \ + : \ + ftell( (fp) ) \ + ) + + +/* + * MACROS and CONSTANTS + * ImErrorFatal - issue a fatal error and return a value + * ImErrorFatalExit - issue a fatal error and exit the process + * ImErrorWarning - issue a warning error and possibly return a value + * ImErrorInfo - issue an info error + * ImInfo - issue an information message + * IMINFOMSGWIDTH... - Image Info Message Widths + * IMINFOMSGLENGTH - Image Info Message Length + * IM_NOTHING - Nothing + * + * DESCRIPTION + * These macros cover up some of the busy-work of issueing error + * and info messages and codes while processing file formats. + * + * IM_NOTHING can be used with the error macros, if your + * function does not return a value. + */ + +#define IM_NOTHING /* nothing */ + +extern char *ImErrorProgramName; /* Program name */ +extern char *ImErrorFileName; /* File name */ +extern FILE *ImErrorStream; /* Error stream */ +extern FILE *ImInfoStream; /* Info stream */ +#ifdef __STDC__ +extern int (*ImErrorHandler)(int, int, char* ); /* Error handler */ +extern int (*ImInfoHandler)(char*,char*,char*); /* Info handler */ +#else +extern int (*ImErrorHandler)( ); /* Error handler */ +extern int (*ImInfoHandler)( ); /* Info handler */ +#endif + +#define ImErrorFatal(msg,ret,er) \ +{ \ + if ( ImErrorHandler ) \ + { \ + char errorMessage[500]; \ + sprintf( errorMessage, "%s: %s: %s\n", ImErrorProgramName,\ + ImErrorFileName, (msg) ); \ + (*ImErrorHandler)( IMERRORFATAL, er, errorMessage ); \ + ImErrNo = (er); \ + return ret ; \ + } \ + else if ( ImErrorStream ) \ + { \ + fprintf( ImErrorStream, "%s: %s: %s\n", ImErrorProgramName,\ + ImErrorFileName, (msg) ); \ + ImErrNo = (er); \ + return ret ; \ + } \ + else \ + { \ + ImErrNo = (er); \ + return ret ; \ + } \ +} + +#define ImErrorWarning(msg,ret,er) \ +{ \ + if ( ImErrorHandler ) \ + { \ + char errorMessage[500]; \ + sprintf( errorMessage, "%s: %s: %s\n", ImErrorProgramName,\ + ImErrorFileName, (msg) ); \ + if ( (*ImErrorHandler)( IMERRORWARNING, er, errorMessage ) == -1 )\ + { \ + ImErrNo = (er); \ + return ret ; \ + } \ + } \ + else if ( ImErrorStream ) \ + fprintf( ImErrorStream, "%s: %s: %s\n", ImErrorProgramName,\ + ImErrorFileName, (msg) ); \ + else \ + { \ + ImErrNo = (er); \ + return ret ; \ + } \ +} + + +#define ImErrorInfo(msg,ret,er) \ +{ \ + if ( ImErrorHandler ) \ + { \ + char errorMessage[500]; \ + sprintf( errorMessage, "%s: %s: %s\n", ImErrorProgramName,\ + ImErrorFileName, (msg) ); \ + if ( (*ImErrorHandler)( IMERRORINFO, er, errorMessage ) == -1 )\ + { \ + ImErrNo = (er); \ + return ret ; \ + } \ + } \ + else if ( ImErrorStream ) \ + fprintf( ImErrorStream, "%s: %s: %s\n", ImErrorProgramName,\ + ImErrorFileName, (msg) ); \ +} + + +#define ImErrorFatalExit(msg,status,er) \ +{ \ + if ( ImErrorHandler ) \ + { \ + char errorMessage[500]; \ + sprintf( errorMessage, "%s: %s: %s\n", ImErrorProgramName,\ + ImErrorFileName, (msg) ); \ + (*ImErrorHandler)( IMERRORFATAL, er, errorMessage ); \ + exit (status) ; \ + } \ + else if ( ImErrorStream ) \ + { \ + fprintf( ImErrorStream, "%s: %s: %s\n", ImErrorProgramName,\ + ImErrorFileName, (msg) ); \ + exit (status) ; \ + } \ + else \ + { \ + exit (status) ; \ + } \ +} + +#define IMINFOMSGWIDTH1 20 + + +#define ImInfo(label,msg) \ +{ \ + char infoMessage[500]; \ + char infoLabel[IMINFOMSGWIDTH1+2]; \ + sprintf( infoLabel, "%s:", label ); \ + sprintf( infoMessage, "%-*s %s\n", IMINFOMSGWIDTH1, infoLabel, msg );\ + if ( ImInfoHandler ) \ + (*ImInfoHandler)( ImErrorProgramName, ImErrorFileName, \ + infoMessage ); \ + else if ( ImInfoStream ) \ + fprintf( ImInfoStream, "%s: %s: %s", ImErrorProgramName,\ + ImErrorFileName, (infoMessage) ); \ +} + +/* + * Following are some functions that are used in a variety of places + * within the library. + */ +#ifdef __STDC__ +extern int ImGetTransparency(TagTable* tagTable, TagTable* flagsTable, ImVfb* vfb); +extern int ImVfbProcessMapRequests( TagTable* flagsTable, TagTable* tagTable); +extern int imLzwReadByte(int ioType,int fd,FILE *fp,int flag,int input_code_size); +extern int imLzwCompGif(int ioType,int fd,FILE *fp,int code_size, unsigned char *rasterdata, int size); +extern void PackBits( unsigned char *cIn, unsigned char *cOut, unsigned int *width ); +extern void PackBits3( unsigned char *cIn, unsigned char *cOut, unsigned int * width ); +extern void UnpackBits( unsigned char *srcPtr, unsigned char *dstPtr, unsigned int *cnt ); +extern void UnpackBits3( unsigned char *srcPtr, unsigned char *dstPtr, unsigned int *cnt ); + +#else +extern int ImGetTransparency( ); /* declared in imvfb.c */ +extern int ImVfbProcessMapRequests( ); /* declared in imvfbchan.c */ +extern int imLzwReadByte(); /* declared in imgiflzw.c */ +extern int imLzwCompGif(); /* declared in imgiflzw.c */ +extern void PackBits(); /* macpack.c */ +extern void UnPackBits(); /* macpack.c */ +extern void UnpackBits( ); /* macpack.c */ +extern void UnpackBits3( ); /* macpack.c */ +#endif + +#endif /* __IMINTERNALH__ */ diff --git a/utils/roq2/libim/imipw.c b/utils/roq2/libim/imipw.c new file mode 100644 index 0000000..7ddc873 --- /dev/null +++ b/utils/roq2/libim/imipw.c @@ -0,0 +1,356 @@ +/** + ** $Header: /roq/libim/imipw.c 1 11/02/99 4:38p Zaphod $ + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + ** a division of General Atomics, San Diego, California, USA + ** + ** Users and possessors of this source code are hereby granted a + ** nonexclusive, royalty-free copyright and design patent license to + ** use this code in individual software. License is not granted for + ** commercial resale, in whole or in part, without prior written + ** permission from SDSC. This source is provided "AS IS" without express + ** or implied warranty of any kind. + ** + ** For further information contact: + ** E-Mail: info@sds.sdsc.edu + ** + ** Surface Mail: Information Center + ** San Diego Supercomputer Center + ** P.O. Box 85608 + ** San Diego, CA 92138-5608 + ** (619) 534-5000 + **/ + +#define HEADER " $Header: /roq/libim/imipw.c 1 11/02/99 4:38p Zaphod $ " + +/** + ** FILE + ** imipw.c - UCSB IPW image file + ** + ** PROJECT + ** libim - SDSC image manipulation library + ** + ** DESCRIPTION + ** imipw.c contains routines to read and write UCSB IPW files for + ** the image manipulation library. Raster data read in is stored + ** in a VFB. Raster data written out is taken from a tag table. + ** + ** PUBLIC CONTENTS + ** d =defined constant + ** f =function + ** m =defined macro + ** t =typedef/struct/union + ** v =variable + ** ? =other + ** + ** ImIPWRead f read an IPW file + ** ImIPWWrite f write an IPW file + ** + ** PRIVATE CONTENTS + ** none + ** + ** HISTORY + ** $Log: /roq/libim/imipw.c $ +* +* 1 11/02/99 4:38p Zaphod + * Revision 1.4 1995/06/29 00:28:04 bduggan + * updated copyright year + * + * Revision 1.3 1995/04/03 21:27:51 bduggan + * took out #ifdef NEWMAGIC + * + * Revision 1.2 1995/01/10 23:32:25 bduggan + * put in IMMULTI, IMPIPE instead of TRUE/FALSE + * + * Revision 1.1 1994/10/03 11:32:30 nadeau + * Initial revision + * + * Revision 1.1 1994/10/03 11:32:30 nadeau + * Initial revision + * + ** + **/ + +#include "iminternal.h" + + +#ifndef L_SET +#define L_SET 0 /* Absolute offset */ +#define L_CUR 1 +#define L_END 2 +#endif + + +/* header string for IPW files: */ + +/* #define HEADER "!
" */ + + + + +/* + * FORMAT + * IPW - UCSB Image Processing Workbench image file + * + * DESCRIPTION + * UCSB IPW image files start with a two 32-bit words giving the + * image width and height, respectively. Following these size words + * are a stream of (widht*height) 32-bit words giving the RGB value + * for consecutive pixels. Scanlines are not padded. + * + * Each 32-bit pixel value is structured as four 8-bit quantities: + * + * high-order matte (alpha) + * red + * green + * low-order blue + */ + + +/* + * IPW - UCSB IPW image file + * For information on these structures, how to use them, etc. please + * see imfmt.c. + */ +#ifdef __STDC__ +extern int ImIPWRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); +extern int ImIPWWrite(ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); +#else +extern int ImIPWRead( ); +extern int ImIPWWrite( ); +#endif +static char *imIPWNames[ ] = { "ipw", "ucsb", "mbfx", "mbfavs", NULL }; +static ImFileFormatReadMap imIPWReadMap[ ] = +{ + /* in out */ + /* type,ch,dep, attr. VFB type attr. */ + { RGB,3,8, A, IMVFBRGB, A }, + { -1, 0, -1, 0 }, +}; +static ImFileFormatWriteMap imIPWWriteMap[ ] = +{ + /* in out */ + /* VFB type, attr., type,ch,dep, attr., func */ + { IMVFBRGB, 0, RGB,3,8, A, ImIPWWrite }, + { IMVFBRGB, A, RGB,3,8, A, ImIPWWrite }, + { -1, 0, -1, 0, NULL }, +}; + +static ImFileMagic imFileIPWMagic[ ] = +{ + { 0,0,NULL }, +}; + + + +ImFileFormat ImFileXFormat = +{ + imIPWNames, "UCSB IPW image file", + "UCSB", + "8-bit grayscale uncompressed image files.", + "24-bit RGB uncompressed image files.", + imFileIPWMagic, + IMNOMULTI, IMPIPE, /* Read */ + IMNOMULTI, IMPIPE, /* Write */ + ImIPWRead, imIPWReadMap, imIPWWriteMap +}; + + +/* + * FUNCTION + * ImIPWRead - read a UCSB IPW file + * + * DESCRIPTION + * The file header is read in, followed by the pixel values. + * One VFB per band is added to the tag table. + */ + +int /* Returns # tags read in */ +#ifdef __STDC__ +ImIPWRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) +#else +ImIPWRead( ioType, fd, fp, flagsTable, tagTable ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag table to add to */ +#endif +{ + ImVfb *vfb; /* Read in image */ + ImVfbPtr pPixel; /* Pixel pointer */ + sdsc_uint32 *buffer; /* Run buffer */ + sdsc_uint32 *pBuffer; /* Run buffer pointer */ + int width, height; /* Image dimensions */ + int i; /* Counter */ + char message[200]; /* contains information for ImInfo */ + + /* + * Read in the image size. + */ + BinByteOrder( BINMBF ); + if ( ImBinRead( ioType, fd, fp, &width, INT, 4, 1 ) == -1 ) + { + ImReturnBinError( ); + } + if ( ImBinRead( ioType, fd, fp, &height, INT, 4, 1 ) == -1 ) + { + ImReturnBinError( ); + } + + sprintf (message, "Most Significant Byte First"); + ImInfo ("Byte Order",message); + sprintf (message, "%d x %d", width, height); + ImInfo ("Resolution",message); + ImInfo ("Type","24-bit RGB"); + ImInfo ("Alpha Channel","8-bit"); + + /* + * Allocate an RGB and Alpha plane VFB of the required size. + */ + if ( (vfb = ImVfbAlloc( width, height, IMVFBRGB|IMVFBALPHA )) == IMVFBNULL ) + { + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + + + /* + * Read in and copy to the VFB, one scanline at a time. + */ + pPixel = ImVfbQFirst( vfb ); + ImMalloc( buffer, sdsc_uint32 *, sizeof( sdsc_uint32 ) * width ); + while ( height-- ) + { + pBuffer = buffer; + if ( ImBinRead( ioType, fd, fp, buffer, UINT32, 4, width )== -1) + { + free( (char *)buffer ); + ImVfbFree( vfb ); + ImReturnBinError( ); + } + + for ( i = 0; i < width; i++, pBuffer++ ) + { + ImVfbSAlpha( vfb, pPixel, ((*pBuffer)>>24) & 0xFF ); + ImVfbSRed( vfb, pPixel, ((*pBuffer)>>16) & 0xFF ); + ImVfbSGreen( vfb, pPixel, ((*pBuffer)>>8) & 0xFF ); + ImVfbSBlue( vfb, pPixel, (*pBuffer) & 0xFF ); + ImVfbSInc( vfb, pPixel ); + } + } + free( (char *)buffer ); + + TagTableAppend( tagTable, TagEntryAlloc( "image vfb", POINTER, &vfb ) ); + + return ( 1 ); +} + + + + + +/* + * FUNCTION + * ImIPWWrite - write a UCSB IPW file + * + * DESCRIPTION + * That VFB is queried, and the X file header written out. + * The VFB data is then copied to the file. + */ + +int /* Returns # of tags used */ +#ifdef _STDC__ +ImIPWWrite(ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) +#else +ImIPWWrite( pMap, ioType, fd, fp, flagsTable, tagTable ) + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag table to read from */ +#endif +{ + ImVfb *vfb; /* Read in image */ + ImVfbPtr pPixel; /* Pixel pointer */ + sdsc_uint32 *buffer; /* Run buffer */ + sdsc_uint32 *pBuffer; /* Run buffer pointer */ + int width, height; /* Image dimensions */ + int i; /* Counter */ + char message[200]; /* contains information for ImInfo */ + + + /* + * Write out the image size. + */ + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + width = ImVfbQWidth( vfb ); + height = ImVfbQHeight( vfb ); + BinByteOrder( BINMBF ); + if ( ImBinWrite( ioType, fd, fp, &width, INT, 4, 1 ) == -1 ) + { + ImReturnBinError( ); + } + if ( ImBinWrite( ioType, fd, fp, &height, INT, 4, 1 ) == -1 ) + { + ImReturnBinError( ); + } + + + sprintf (message, "Most Significant Byte First"); + ImInfo ("Byte Order",message); + sprintf (message, "%d x %d", width, height); + ImInfo ("Resolution",message); + ImInfo ("Type","24-bit RGB" ); + ImInfo ("Alpha Channel","8-bit"); + + /* + * Copy the image to the file. If there's an alpha channel, add it. + */ + pPixel = ImVfbQFirst( vfb ); + ImMalloc( buffer, sdsc_uint32 *, sizeof( sdsc_uint32 ) * width ); + if ( ImVfbQFields( vfb ) & IMVFBALPHA ) + { + while ( height-- ) + { + pBuffer = buffer; + for ( i = 0; i < width; i++, pBuffer++ ) + { + *pBuffer = + (((ImVfbQAlpha(vfb,pPixel))&0xFF)<<24) | + (((ImVfbQRed(vfb,pPixel))&0xFF)<<16)| + (((ImVfbQGreen(vfb,pPixel))&0xFF)<<8)| + ((ImVfbQBlue(vfb,pPixel))&0xFF); + ImVfbSInc( vfb, pPixel ); + } + + if ( ImBinWrite( ioType, fd, fp, buffer, UINT32, 4, width ) == -1 ) + { + free( (char *)buffer ); + ImReturnBinError( ); + } + } + free( (char *)buffer ); + return ( 1 ); + } + + while ( height-- ) + { + pBuffer = buffer; + for ( i = 0; i < width; i++, pBuffer++ ) + { + *pBuffer = + (((ImVfbQRed(vfb,pPixel))&0xFF)<<16) | + (((ImVfbQGreen(vfb,pPixel))&0xFF)<<8) | + ((ImVfbQBlue(vfb,pPixel))&0xFF); + ImVfbSInc( vfb, pPixel ); + } + + if ( ImBinWrite( ioType, fd, fp, buffer, UINT32, 4, width)== -1) + { + free( (char *)buffer ); + ImReturnBinError( ); + } + } + free( (char *)buffer ); + return ( 1 ); +} diff --git a/utils/roq2/libim/imjpeg.c b/utils/roq2/libim/imjpeg.c new file mode 100644 index 0000000..8ccb801 --- /dev/null +++ b/utils/roq2/libim/imjpeg.c @@ -0,0 +1,628 @@ +/** + ** $Header: /roq/libim/imjpeg.c 1 11/02/99 4:38p Zaphod $ + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + ** a division of General Atomics, San Diego, California, USA + ** + ** Users and possessors of this source code are hereby granted a + ** nonexclusive, royalty-free copyright and design patent license to + ** use this code in individual software. License is not granted for + ** commercial resale, in whole or in part, without prior written + ** permission from SDSC. This source is provided "AS IS" without express + ** or implied warranty of any kind. + ** + ** For further information contact: + ** E-Mail: info@sds.sdsc.edu + ** + ** Surface Mail: Information Center + ** San Diego Supercomputer Center + ** P.O. Box 85608 + ** San Diego, CA 92138-5608 + ** (619) 534-5000 + **/ + +#define HEADER " $Header: /roq/libim/imjpeg.c 1 11/02/99 4:38p Zaphod $" + +/** + ** FILE + ** imjpeg.c - JPEG (Joint Photographic Experts Group) file routines + ** + ** PROJECT + ** libim - SDSC image manipulation library + ** + ** DESCRIPTION + ** imjpeg.c contains routines to read and write JPEG files for + ** the image manipulation library. Raster data read in is stored + ** in a VFB and optional CLT in a tag list. Raster data written + ** out is taken from a tag list. + ** + ** PUBLIC CONTENTS + ** d =defined constant + ** f =function + ** m =defined macro + ** t =typedef/struct/union + ** v =variable + ** ? =other + ** + ** none + ** + ** PRIVATE CONTENTS + ** imJpegRead f read a JPEG image file + ** imJpegWrite f write a JPEG image file + ** + ** HISTORY + ** $Log: /roq/libim/imjpeg.c $ + * + * 1 11/02/99 4:38p Zaphod + * Revision 1.4 1995/06/29 00:28:04 bduggan + * updated copyright year + * + * Revision 1.3 1995/06/15 20:09:19 bduggan + * Added iminfo message "no alpha channel" + * + * Revision 1.2 1995/04/03 21:28:34 bduggan + * took out #ifdef NEWMAGIC + * + * Revision 1.1 1995/02/16 21:37:45 bduggan + * Initial revision + * + ** + **/ + + +#ifdef USE_JPEG_LIB +#include +#include +#include +#include "jpeglib.h" +#include "iminternal.h" + +#else +#include "iminternal.h" + +#endif + +#define IMJPEG_TRACE_LEVEL 1 /* level of verbosity for jpeg library */ + /* 0 = no messages. A higher number */ + /* indicates more messages. */ + +/** + ** + ** FORMAT + ** JPEG - Joint Photographic Experts Group image file format + ** + ** AKA + ** jpg + ** + ** FORMAT REFERENCES + ** Wallace, Gregory K. "The JPEG Still Picture Compression Standard", + ** Communications of the ACM, April 1991 (vol. 34 no. 4), pp. 30-44 + ** "The Data Compression Book" by Mark Nelson, published by M&T Books (Redwood + ** City, CA), 1991, ISBN 1-55851-216-0. + ** "JPEG Still Image Data Compression Standard", by Pennebaker and Mitchell + ** published by Van Nostrand Reinhold, 1993, ISBN 0-442-01272-1. + ** + ** CODE CREDITS + ** Custom development, Brian Duggan, San Diego Supercomputer Center, 1995. + ** + ** DESCRIPTION + ** The routines in this file use the library developed by the Independent + ** JPEG group. This "official" site of this library is ftp.uu.net (192.48.96.9) + ** in the directory graphics/jpeg. + ** + ** Please refer to the documentation in that library for more information + ** about the JPEG format. + **/ + + +#ifdef USE_JPEG_LIB + +#ifdef __STDC__ +static int imJpegRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); +static int imJpegWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); +#else +static int imJpegRead( ), imJpegWrite( ); +#endif + +#endif /* USE_JPEG_LIB */ + +static char *imJpegNames[ ] = { "jpeg", "jpg", "jfif", NULL }; + + +static unsigned char imJpegMagicNumber[ ] = { 0xFF, 0xD8 }; +static ImFileMagic imFileJpegMagic []= +{ + { 0, 2, imJpegMagicNumber}, + { 0, 0, NULL }, +}; + +static ImFileFormatReadMap imJpegReadMap[ ] = +{ + /* in out */ + /* type,ch,dep, attr. VFB type attr. */ + { IN,1,8, T, IMVFBINDEX8, 0 }, + { RGB,3,8, T, IMVFBRGB, 0 }, + { -1, 0, -1, 0 }, +}; +static ImFileFormatWriteMap imJpegWriteMap[ ] = +{ + /* in out */ + /* VFB type, attr., type,ch,dep, attr., func */ +#ifdef USE_JPEG_LIB + { IMVFBINDEX8, 0, IN,1,8, DCT|Q|T, imJpegWrite }, + { IMVFBRGB, 0, RGB,3,8, DCT|Q|T, imJpegWrite }, +#endif + { -1, 0, -1, 0, NULL }, +}; + + +ImFileFormat ImFileJpegFormat = +{ + imJpegNames, "JPEG Image File Format", + "Joint Photographic Experts Group ", +#ifdef USE_JPEG_LIB + "8-bit, 24-bit RGB image.", + "8-bit, 24-bit RGB image.", +#else + "none", + "none", +#endif + imFileJpegMagic, + IMNOMULTI, IMPIPE, + IMNOMULTI, IMPIPE, +#ifdef USE_JPEG_LIB + imJpegRead, imJpegReadMap, imJpegWriteMap +#else + NULL, NULL, NULL +#endif +}; + + + + +#ifdef USE_JPEG_LIB + +/* We use C's setjmp/longjmp facility to return control to our routine, + * rather than simply allowing the JPEG library to exit(). This means that the + * we must first execute a setjmp() call to establish the return point. + * We want the replacement error_exit to do a longjmp(). But we need to + * make the setjmp buffer accessible to the error_exit routine. To do this, + * we make a private extension of the standard JPEG error handler object. + * (If we were using C++, we'd say we were making a subclass of the regular + * error handler.) + */ + +/* Error handler struct: */ + +struct im_error_mgr { + struct jpeg_error_mgr pub; /* "public" fields */ + + jmp_buf setjmp_buffer; /* for return to caller */ +}; + +typedef struct im_error_mgr * im_error_ptr; + +/* + * Routine that will replace the standard error_exit method: + */ + +METHODDEF void +im_error_exit (j_common_ptr cinfo) +{ + /* cinfo->err really points to a im_error_mgr struct, so coerce pointer */ + im_error_ptr myerr = (im_error_ptr) cinfo->err; + + /* Always display the message. */ + /* We could postpone this until after returning, if we chose. */ + (*cinfo->err->output_message) (cinfo); + + /* Return control to the setjmp point */ + longjmp(myerr->setjmp_buffer, 1); +} + + +/* + * FUNCTION + * imJpegError + * + * DESCRIPTION + * Print an error using the image tools error + * handler stuff + */ + +METHODDEF void +#ifdef __STDC__ +imEmitMessage (j_common_ptr cinfo, int msg_level) +#else +imEmitMessage (cinfo, msg_level) +j_common_ptr cinfo; +int msg_level; +#endif +{ + char buffer[JMSG_LENGTH_MAX]; + struct jpeg_error_mgr* err = cinfo->err; + + /* Create the message */ + (*cinfo->err->format_message) (cinfo, buffer); + + if (msg_level < 0) + { /* warning */ + ImErrorWarning(buffer, IM_NOTHING, IMEUNKNOWN ); + err->num_warnings++; + } + else + { + /* info message */ + if (IMJPEG_TRACE_LEVEL >= msg_level) + ImInfo("JPEG information",buffer); + } +} + +/* + * FUNCTION + * imOutputMessage + * + * DESCRIPTION + * Print a single message line using image + * library's error handler stuff + */ + +METHODDEF void +#ifdef __STDC__ +imOutputMessage (j_common_ptr cinfo) +#else +imOutputMessage (cinfo) +j_common_ptr cinfo; +#endif +{ + char buffer[JMSG_LENGTH_MAX]; + + /* Create the message */ + (*cinfo->err->format_message) (cinfo, buffer); + + /* This could be a warning or a fatal error. We don't know + * which it is, so just call it a warning. + */ + ImErrorWarning(buffer, IM_NOTHING, IMEUNKNOWN ); +} + + + + +/* + * FUNCTION + * imJpegRead + * + * DESCRIPTION + * Read in a jpeg file, using the + * Independent JPEG Group's library. + */ + +static int /* Returns # tags read in */ +#ifdef __STDC__ +imJpegRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) +#else +imJpegRead( ioType, fd, fp, flagsTable, tagTable ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag list to add to */ +#endif +{ + struct jpeg_decompress_struct jpeg; /* holds data about our file */ + JSAMPARRAY buffer; /* Buffer for reading scanlines */ + int scanline_size; /* Length of each scanline */ + ImVfb* vfb; /* new vfb we're creating */ + ImVfbPtr vfbptr; /* pointer into this vfb */ + int width, height; /* Dimensions of the vfb */ + char message[1000]; /* Buffer for messages */ + int numChans; /* number of channels in the image */ + int i; /* loop index */ + struct im_error_mgr jerr; /* Error struct */ + J_COLOR_SPACE jpg_color_space; /* Holds the type of the image */ + + if (!(ioType & IMFILEIOFP)) + { + fp = fdopen( fd, "rb"); + rewind (fp); + } + + /* + * Set up the error handler + */ + jpeg.err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = im_error_exit; /* Override standard exit routine */ + jerr.pub.output_message = imOutputMessage; /* Override standard output routine */ + jerr.pub.emit_message = imEmitMessage; /* Override standard emitting routine */ + + /* Establish the setjmp return context for im_error_exit to use */ + if (setjmp(jerr.setjmp_buffer)) + { + /* The JPEG code has signalled an error. */ + jpeg_destroy_decompress(&jpeg); + return 0; + } + + /* Initialize jpeg decompression object */ + jpeg_create_decompress(&jpeg); + + /* Specify the data source (fp) */ + jpeg_stdio_src(&jpeg, fp); + + /* Read JPEG header */ + jpeg_read_header(&jpeg, TRUE); + + /* Output compression type */ + ImInfo ("Compression Type","Discrete Cosine Transform"); + + ImInfo ("Alpha Channel","None"); + + /* Start jpeg decompression */ + jpeg_start_decompress(&jpeg); + + /* Discern the scanline size */ + scanline_size = jpeg.output_width * jpeg.output_components; + + /* Discern the image dimensions */ + height = jpeg.image_height; + width = jpeg.image_width; + sprintf(message,"%d x %d",width,height); + ImInfo("Resolution",message); + + /* DIscern the type of the image */ + jpg_color_space = jpeg.out_color_space; + + ImMalloc( buffer, unsigned char**, sizeof(unsigned char*)* 1 ); + ImMalloc( buffer[0], unsigned char* , sizeof(unsigned char) * scanline_size ); + + /* Figure out what we have, then read the stuff in! */ + + numChans = jpeg.num_components; + switch (numChans) + { + case 1: + if (jpg_color_space!=JCS_GRAYSCALE) + { + ImErrorFatal( "Unknown color space",-1, IMEUNSUPPORTED); + } + ImInfo ("Type", "8-bit Greyscale"); + /* Read a greyscale / index8 image */ + vfb = ImVfbAlloc( width, height, IMVFBGREY); + vfbptr = ImVfbQPtr(vfb, 0, 0); + + /* Read the scanlines */ + while (jpeg.output_scanline < jpeg.output_height) + { + jpeg_read_scanlines(&jpeg, buffer, 1); + + /* Add scanline to the vfb */ + for (i=0; imap_outNChannels; + if (numChans == 3) + { + sprintf(message,"%d-bit RGB",numChans * 8); + ImInfo("Type",message); + jpeg.in_color_space = JCS_RGB; + } + else + { + ImInfo("Type","8-bit grayscale"); + jpeg.in_color_space = JCS_GRAYSCALE; + } + + ImInfo ("Compression Type","Discrete Cosine Transform"); + + jpeg.input_components = numChans; + + /* Set default compression parameters */ + jpeg_set_defaults(&jpeg); + + /* Set the quality, if a request was given. Otherwise JPEG creates a default one. */ + if (TagTableQNEntry(flagsTable, "image compression quality request") > 0) + { + TagEntryQValue( TagTableQDirect( flagsTable, + "image compression quality request", 0), &quality); + /* Set it. TRUE limits to baseline-JPEG values. i.e. + it ensures that lots of jpeg-readers can understand + the file we create. */ + if (quality<0 || quality>100) + { + sprintf(message,"Ignoring invalid quality request: %d. Must be between 1 and 100.", + quality); + ImErrorWarning (message, -1, IMEOUTOFRANGE); + } + sprintf(message,"%d",quality); + ImInfo("Compression Quality",message); + jpeg_set_quality(&jpeg, quality, TRUE); + } + + /* Start up compression */ + jpeg_start_compress(&jpeg, TRUE); + + /* Allocate some memory for our scanline buffer */ + ImMalloc( buffer , unsigned char**, sizeof(unsigned char*)* 1 ); + ImMalloc( buffer[0], unsigned char* , sizeof(unsigned char) * width * numChans ); + + /* Start at the top */ + vfbptr = ImVfbQPtr(vfb, 0, 0); + + + /* Write scanlines to jpeg! */ + switch (numChans) + { + case 1: /* Write grayscale */ + for (y=0;y + +#include "iminternal.h" + + + +#ifdef __STDC__ + +static int imLzwGetNextCode(unsigned char *buf); + +static int imLzwPutNextCode( int ioType, int fd, FILE *fp, int c); + +static int imLzwClearBlock( int ioType, int fd, FILE *fp); + +static int imLzwWriteData( int ioType, int fd, FILE *fp ); + +#else + +static int imLzwGetNextCode( ); + +static int imLzwPutNextCode( ); + +static int imLzwClearBlock( ); + +static int imLzwWriteData( ); + +#endif + + + +/* + + * MACROS + + * + + * + + * DESCRIPTION + + * Decode one scanline of pixels. + + * The TIFF imLzwSpec imLzwSpecifies that encoded bit strings range + + * from 9 to 12 bits. This is somewhat unfortunate in that + + * experience indicates full color RGB pictures often need + + * ~14 bits for reasonable compression. + + */ + +#define IMMAXCODE(n) ((1 << (n)) - 1) + +#define IMBITS_MIN 9 /* start with 9 bits */ + +#define IMBITS_MAX 12 /* max of 12 bit strings */ + + + +/* + + * predefined codes + + */ + + + +#define IMCODE_CLEAR 256 /* code to clear string table */ + +#define IMCODE_EOI 257 /* end-of-information code */ + +#define IMCODE_FIRST 258 /* first free code entry */ + +#define IMCODE_MAX IMMAXCODE(IMBITS_MAX) + + + +#ifdef notdef + +#define IMHSIZE 9001 /* 91% occupancy */ + +#define IMHSHIFT (8-(16-13)) + +#else + +#define IMHSIZE 5003 /* 80% occupancy */ + +#define IMHSHIFT (8-(16-12)) + +#endif + + + + + +/* + + * NB: The 5.0 spec describes a different algorithm than Aldus + + * implements. Specifically, Aldus does code length transitions + + * one code earlier than should be done (for real LZW). + + * Earlier versions of this library implemented the correct + + * LZW algorithm, but emitted codes in a bit order opposite + + * to the TIFF spec. Thus, to maintain compatibility w/ Aldus + + * we interpret MSB-LSB ordered codes to be images written w/ + + * old versions of this library, but otherwise adhere to the + + * Aldus "off by one" algorithm. + + * + + * Future revisions to the TIFF spec are expected to "clarify this issue". + + */ + +#define IMSETMAXCODE(sp, v) { \ + (sp)->lzwMaxcode = (v)-1; \ + if ((sp)->lzwFlags & IMLZW_COMPAT) \ + (sp)->lzwMaxcode++; \ +} + + + + + + + +/* + + * TYPEDEF & STRUCTURE + + * + + * + + * DESCRIPTION + + * Decoding and encoding imLzwSpecific state. + + */ + +struct decode { + + short prefixtab[IMHSIZE]; /* prefix(code) */ + + unsigned char suffixtab[IMCODE_MAX+1]; /* suffix(code) */ + + unsigned char stack[IMHSIZE-(IMCODE_MAX+1)]; + + unsigned char *stackp; /* stack pointer */ + + int firstchar; /* of string associated w/ last code */ + +}; + + + +/* + + * Encoding-imLzwSpecific state. + + */ + +struct encode { + + int checkpoint; /* point at which to clear table */ + +#define IMCHECK_GAP 10000 /* enc_ratio check interval */ + + long ratio; /* current compression ratio */ + + int incount; /* (input) data bytes encoded */ + + int outcount; /* encoded (output) bytes */ + + int htab[IMHSIZE]; /* hash table */ + + short codetab[IMHSIZE]; /* code table */ + +}; + + + +/* + + * State block for each open TIFF + + * file using Lzw compression. + + */ + +typedef struct LzwState + +{ + + int lzwOldcode; /* last code encountered */ + +/*new*/ unsigned char lzwHorDif; /* Undefined in 2.2 */ + +#define IMLZW_HORDIFF4 0x01 /* hor. diff w/ 4-bit samples */ + +#define IMLZW_HORDIFF8 0x02 /* hor. diff w/ 8-bit samples */ + +#define IMLZW_HORDIFF16 0x04 /* hor. diff w/ 16-bit samples */ + +#define IMLZW_HORDIFF32 0x08 /* hor. diff w/ 32-bit samples */ + + unsigned short lzwFlags; /* flags */ + +#define IMLZW_RESTART 0x01 /* restart interrupted decode */ + +#define IMLZW_COMPAT 0x02 /* read old bit-reversed codes */ + + unsigned short lzwNbits; /* number of bits/code */ + +/*new*/ unsigned short lzwStride; /* number of bits/code */ + + int lzwMaxcode; /* maximum code for lzwNbits */ + + int lzwBitoff; /* bit offset into data */ + + int lzwBitsize; /* size of strip in bits */ + + int lzwFree_ent; /* next free entry in hash tabl */ + + int lzwRawcc; /* byte count of unread/written */ + + int lzwBytesWritten; /* byte count of unread/written */ + + char *lzwRawdata; /* raw data buffer */ + + union { + + struct decode dec; + + struct encode enc; + + } u; + +} LzwState; + + + +/* + + * TYPEDEF & STRUCTURE + + * dec & enc - shorthand structure field names + + * + + * DESCRIPTION + + * Decoding and encoding imLzwSpecific state. + + */ + +#define IM_DEC_PREFIX u.dec.prefixtab + +#define IM_DEC_SUFFIX u.dec.suffixtab + +#define IM_DEC_STACK u.dec.stack + +#define IM_DEC_STACKP u.dec.stackp + +#define IM_DEC_FIRSTCHAR u.dec.firstchar + + + +#define IM_ENC_CHECKPOINT u.enc.checkpoint + +#define IM_ENC_RATIO u.enc.ratio + +#define IM_ENC_INCOUNT u.enc.incount + +#define IM_ENC_OUTCOUNT u.enc.outcount + +#define IM_ENC_HTAB u.enc.htab + +#define IM_ENC_CODETAB u.enc.codetab + + + +/* + + * TYPEDEF & STRUCTURE + + * + + * + + * DESCRIPTION + + * Decoding and encoding imLzwSpecific state. + + */ + +/* + + * masks for extracting/inserting variable length bit codes + + */ + +static unsigned char rmask[9] = + + { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff }; + +static unsigned char lmask[9] = + + { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; + + + +/* + + * Old lmask + +static unsigned char lmask[9] = + + { 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00 }; + +*/ + + + + + + + +#ifdef __STDC__ + +static void imLzwClearHash(void); + +#else + +static void imLzwClearHash(); + +#endif + + + + + +/* + + * STRUCTURE + + * imLzwSp - pointer to the global State info structure + + * + + * DESCRIPTION + + */ + +LzwState *imLzwSp=NULL; + + + + + + + +/* + + * Lzw Decoder. + + */ + + + +/* + + * FUNCTION + + * ImLzwPreDecode - Inititialize structures for encode/decode + + * + + * DESCRIPTION + + * Set up the encode/decode structure + + */ + +int + +#ifdef __STDC__ + +ImLzwPreDecode( unsigned char *buf, int size ) + +#else + +ImLzwPreDecode( buf, size ) + + unsigned char *buf; /* Encoded bytes so we can check first 2 bytes */ + + int size; /* Max number of bytes to be decoded */ + +#endif + +{ + + int code; + + + + if (imLzwSp == NULL) + + { + + ImCalloc( imLzwSp, LzwState *, sizeof(LzwState), 1 ); + + imLzwSp->lzwFlags = 0; + + imLzwSp->lzwHorDif = 0; + + imLzwSp->lzwStride = 0; + + } + + else + + imLzwSp->lzwFlags &= ~IMLZW_RESTART; + + + + imLzwSp->lzwNbits = IMBITS_MIN; + + + + /* + + * Pre-load the table. + + */ + + for (code = 255; code >= 0; code--) + + imLzwSp->IM_DEC_SUFFIX[code] = (unsigned char)code; + + + + imLzwSp->lzwFree_ent = IMCODE_FIRST; + + imLzwSp->lzwBitoff = 0; + + + + /* + + * Calculate data size in bits + + */ + + imLzwSp->lzwBitsize = size; + + imLzwSp->lzwBitsize = (imLzwSp->lzwBitsize << 3) - (IMBITS_MAX-1); + + imLzwSp->IM_DEC_STACKP = imLzwSp->IM_DEC_STACK; + + imLzwSp->lzwOldcode = -1; + + imLzwSp->IM_DEC_FIRSTCHAR = -1; + + imLzwSp->lzwRawcc = size; + + imLzwSp->lzwBytesWritten = 0; + + imLzwSp->lzwRawdata = NULL; + + + + /* + + * Check for old bit-reversed codes. All the flag + + * manipulations are to insure only one warning is + + * given for a file. + + */ + + if ((buf[0] == 0) && (buf[1] & 0x1)) + + { + + if ((imLzwSp->lzwFlags & IMLZW_COMPAT) == 0) + + ImErrorWarning("Old LZW codes-converting",-1,IMESYNTAX); + + + + imLzwSp->lzwFlags |= IMLZW_COMPAT; + + } else + + imLzwSp->lzwFlags &= ~IMLZW_COMPAT; + + + + + + IMSETMAXCODE( imLzwSp, IMMAXCODE(IMBITS_MIN)); + + return (0); + +} + + + + + +/* + + * FUNCTION + + * ImLzwDecode - Decode one scanline of pixels. + + * + + * DESCRIPTION + + * Decode one scanline of pixels. + + */ + +int + +#ifdef __STDC__ + +ImLzwDecode(unsigned char *buf, char *op, int occ) + +#else + +ImLzwDecode(buf, op, occ) + + unsigned char *buf; /* Pixels to be decoded */ + + char *op; /* Decoded pixels returned */ + + int occ; + +#endif + +{ + + int code; /* A compression code */ + + int retCnt=0; /* The number of bytes uncompre */ + + unsigned char *stackp; /* Stack pointer */ + + int firstchar, oldcode, incode; + + + + + + stackp = imLzwSp->IM_DEC_STACKP; + + + + /* + + * Restart interrupted unstacking operations. + + */ + + if (imLzwSp->lzwFlags & IMLZW_RESTART) + + { + + do + + { + + if (--occ < 0) /* end of scanline */ + + { + + imLzwSp->IM_DEC_STACKP = stackp; + + return (1); + + } + + *op++ = *--stackp; + + retCnt++; + + } while (stackp > imLzwSp->IM_DEC_STACK); + + imLzwSp->lzwFlags &= ~IMLZW_RESTART; + + } + + + + oldcode = imLzwSp->lzwOldcode; + + firstchar = imLzwSp->IM_DEC_FIRSTCHAR; + + + + while (occ > 0 && (code = imLzwGetNextCode(buf)) != IMCODE_EOI) + + { + + if (code == IMCODE_CLEAR) + + { + + memset(imLzwSp->IM_DEC_PREFIX, 0x00, sizeof (imLzwSp->IM_DEC_PREFIX)); + + imLzwSp->lzwFree_ent = IMCODE_FIRST; + + imLzwSp->lzwNbits = IMBITS_MIN; + + IMSETMAXCODE( imLzwSp, IMMAXCODE(IMBITS_MIN)); + + if ((code = imLzwGetNextCode(buf)) == IMCODE_EOI) + + break; + + *op++ = code, occ--; + + retCnt++; + + oldcode = firstchar = code; + + continue; + + } + + + + incode = code; + + + + /* + + * When a code is not in the table we use (as shown): + + * StringFromCode(oldcode) + + + * FirstChar(StringFromCode(oldcode)) + + */ + + if (code >= imLzwSp->lzwFree_ent) /* code not in table */ + + { + + *stackp++ = firstchar; + + code = oldcode; + + } + + + + /* + + * Generate output string (first in reverse). + + */ + + for (; code >= 256; code = imLzwSp->IM_DEC_PREFIX[code]) + + *stackp++ = imLzwSp->IM_DEC_SUFFIX[code]; + + + + *stackp++ = firstchar = imLzwSp->IM_DEC_SUFFIX[code]; + + + + do + + { + + if (--occ < 0) /* end of scanline */ + + { + + imLzwSp->lzwFlags |= IMLZW_RESTART; + + break; + + } + + *op++ = *--stackp; + + retCnt++; + + + + } while (stackp > imLzwSp->IM_DEC_STACK); + + + + /* + + * Add the new entry to the code table. + + */ + + if ((code = imLzwSp->lzwFree_ent) < IMCODE_MAX) + + { + + imLzwSp->IM_DEC_PREFIX[code] = (unsigned short)oldcode; + + imLzwSp->IM_DEC_SUFFIX[code] = firstchar; + + imLzwSp->lzwFree_ent++; + + /* + + * If the next entry is too big for the + + * current code size, then increase the + + * size up to the maximum possible. + + */ + + if (imLzwSp->lzwFree_ent > imLzwSp->lzwMaxcode) { + + imLzwSp->lzwNbits++; + + if (imLzwSp->lzwNbits > IMBITS_MAX) + + imLzwSp->lzwNbits = IMBITS_MAX; + + IMSETMAXCODE(imLzwSp, IMMAXCODE(imLzwSp->lzwNbits)); + + } + + } + + oldcode = incode; + + } + + imLzwSp->IM_DEC_STACKP = stackp; + + imLzwSp->lzwOldcode = oldcode; + + imLzwSp->IM_DEC_FIRSTCHAR = firstchar; + + + + /* + + * If we were doing prediction we would have to put some + + * horizontal differencing code here + + */ + + + + if (occ > 0) + + ImErrorFatal("Not enough data for scanline", -1, IMENOREAD ); + + + + return ( retCnt ); + +} + + + + + +/* + + * FUNCTION + + * ImLzwPostDecode - Clean up after uncompression + + * + + * DESCRIPTION + + * + + */ + +int + +ImLzwPostDecode() + +{ + + return ( 1 ); + +} + + + + + +/* + + * FUNCTION + + * imLzwGetNextCode - Get the next code out of the buffer + + * + + * DESCRIPTION + + * Get the next code from the raw data buffer. + + */ + +static int + +#ifdef __STDC__ + +imLzwGetNextCode(unsigned char *buf) + +#else + +imLzwGetNextCode(buf) + + unsigned char *buf; /* Undecoded data */ + +#endif + +{ + + int code, r_off, bits; + + unsigned char *bp; + + + + /* + + * This check shouldn't be necessary because each + + * strip is suppose to be terminated with IMCODE_EOI. + + * At worst it's a substitute for the IMCODE_EOI that's + + * supposed to be there (see calculation of lzwBitsize + + * in LzwPreDecode()). + + */ + + if (imLzwSp->lzwBitoff > imLzwSp->lzwBitsize) + + return (IMCODE_EOI); + + + + r_off = imLzwSp->lzwBitoff; + + bits = imLzwSp->lzwNbits; + + + + /* + + * Get to the first byte. + + */ + + bp = (unsigned char *)buf + (r_off >> 3); + + r_off &= 7; + + + + if (imLzwSp->lzwFlags & IMLZW_COMPAT) + + { + + /* Get first part (low order bits) */ + + code = (*bp++ >> r_off); + + r_off = 8 - r_off; /* now, offset into code word */ + + bits -= r_off; + + /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */ + + if (bits >= 8) { + + code |= *bp++ << r_off; + + r_off += 8; + + bits -= 8; + + } + + /* high order bits. */ + + code |= (*bp & rmask[bits]) << r_off; + + } + + else + + { + + r_off = 8 - r_off; /* convert offset to count */ + + code = *bp++ & rmask[r_off]; /* high order bits */ + + bits -= r_off; + + if (bits >= 8) { + + code = (code<<8) | *bp++; + + bits -= 8; + + } + + /* low order bits */ + + code = (code << bits) | ((*bp & lmask[bits]) >> (8 - bits)); + + } + + + + imLzwSp->lzwBitoff += imLzwSp->lzwNbits; + + return (code); + +} + + + + + + + +/* + + * Lzw Encoding. + + */ + + + +/* + + * FUNCTION + + * ImLzwPreEncode - initialize data structure + + * + + * DESCRIPTION + + * Allocate memory and initialize values as necessary. + + * Reset encoding state at the start of a strip. + + */ + +int + +#ifdef __STDC__ + +ImLzwPreEncode(int size) + +#else + +ImLzwPreEncode(size) + + int size; /* The number of bytes to be decoded */ + +#endif + +{ + + if (imLzwSp == NULL) + + { + + ImCalloc( imLzwSp, LzwState *, sizeof(LzwState), 1 ); + + imLzwSp->lzwFlags = 0; + + imLzwSp->lzwHorDif = 0; + + imLzwSp->lzwStride = 0; + + } + + + + imLzwSp->IM_ENC_RATIO = 0; + + imLzwSp->IM_ENC_CHECKPOINT = IMCHECK_GAP; + + IMSETMAXCODE(imLzwSp, IMMAXCODE(imLzwSp->lzwNbits = IMBITS_MIN)+1); + + imLzwSp->lzwFree_ent = IMCODE_FIRST; + + imLzwSp->lzwBitoff = 0; + + imLzwSp->lzwBitsize = (size << 3) - (IMBITS_MAX-1); + + imLzwClearHash(); /* clear hash table */ + + imLzwSp->lzwOldcode = -1; /* generates IMCODE_CLEAR in LzwEncode */ + + imLzwSp->lzwRawcc = size; + + imLzwSp->lzwBytesWritten = 0; + + + + /* + + * This is stinky. The lzw code uses a fixed size buffer to store + + * the compressed data. Compressed data should be smaller so we + + * allocate a buffer twice as big just in case. + + */ + + if (imLzwSp->lzwRawdata == NULL) + + ImCalloc( imLzwSp->lzwRawdata, char *, 1, size*2 ); + + + + return( 0 ); + +} + + + +/* + + * FUNCTION + + * imLzwEncode - Encode a scanline of pixels + + * + + * DESCRIPTION + + * Uses an open addressing double hashing (no chaining) on the + + * prefix code/next character combination. We do a variant of + + * Knuth's algorithm D (vol. 3, sec. 6.4) along with G. Knott's + + * relatively-prime secondary probe. Here, the modular division + + * first probe is gives way to a faster exclusive-or manipulation. + + * Also do block compression with an adaptive reset, whereby the + + * code table is cleared when the compression ratio decreases, + + * but after the table fills. The variable-length output codes + + * are re-sized at this point, and a IMCODE_CLEAR is generated + + * for the decoder. + + */ + +int + +#ifdef __STDC__ + +ImLzwEncode(int ioType, int fd, FILE *fp, unsigned char *bp, int cc) + +#else + +ImLzwEncode(ioType, fd, fp, bp, cc) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + unsigned char *bp; /* Bytes to be encoded */ + + int cc; /* Count of bytes to be encoded */ + +#endif + +{ + + long fcode; + + int h, c, ent, disp; + + + + if ( imLzwSp == NULL ) + + { + + ImErrorFatal("Null pointer in LZW encode", -1, IMEENCODING ); + + } + + + + /* + + * If we were doing prediction we would have to put some + + * horizontal differencing code here + + */ + + + + ent = imLzwSp->lzwOldcode; + + + + if (ent == -1 && cc > 0) + + { + + if (imLzwPutNextCode(ioType, fd, fp, IMCODE_CLEAR) == -1) + + return( -1 ); /* ImErrNo already set */ + + + + ent = *bp++; cc--; imLzwSp->IM_ENC_INCOUNT++; + + } + + + + while (cc > 0) + + { + + c = *bp++; cc--; imLzwSp->IM_ENC_INCOUNT++; + + + + fcode = ((long)c << IMBITS_MAX) + ent; + + h = (c << IMHSHIFT) ^ ent; /* xor hashing */ + + + + if (imLzwSp->IM_ENC_HTAB[h] == fcode) + + { + + ent = imLzwSp->IM_ENC_CODETAB[h]; + + continue; + + } + + + + if (imLzwSp->IM_ENC_HTAB[h] >= 0) + + { + + /* + + * Primary hash failed, check secondary hash. + + */ + + disp = IMHSIZE - h; + + if (h == 0) + + disp = 1; + + do + + { + + if ((h -= disp) < 0) + + h += IMHSIZE; + + if (imLzwSp->IM_ENC_HTAB[h] == fcode) + + { + + ent = imLzwSp->IM_ENC_CODETAB[h]; + + goto hit; + + } + + } while (imLzwSp->IM_ENC_HTAB[h] >= 0); + + } + + + + /* + + * New entry, add to table. + + */ + + if (imLzwPutNextCode(ioType, fd, fp, ent) == -1) + + return( -1 ); /* ImErrNo already set */ + + ent = c; + + + + imLzwSp->IM_ENC_CODETAB[h] = imLzwSp->lzwFree_ent++; + + imLzwSp->IM_ENC_HTAB[h] = (int) fcode; + + + + if (imLzwSp->lzwFree_ent == IMCODE_MAX-1) /* Table is full */ + + { + + imLzwSp->IM_ENC_RATIO = 0; + + imLzwClearHash(); /* clear hash table */ + + imLzwSp->lzwFree_ent = IMCODE_FIRST; + + if (imLzwPutNextCode(ioType, fd, fp, IMCODE_CLEAR) == -1) + + return( -1 ); /* ImErrNo already set */ + + IMSETMAXCODE(imLzwSp, + + IMMAXCODE(imLzwSp->lzwNbits = IMBITS_MIN)+1); + + } + + else /* Table is not full */ + + { + + if (imLzwSp->IM_ENC_INCOUNT >= imLzwSp->IM_ENC_CHECKPOINT) + + { + + if ( imLzwClearBlock( ioType, fd, fp ) == -1 ) + + return( -1 ); /* ImErrNo already set */ + + } + + if (imLzwSp->lzwFree_ent > imLzwSp->lzwMaxcode) + + { + + imLzwSp->lzwNbits++; + + if ( ! (imLzwSp->lzwNbits <= IMBITS_MAX)) + + { + + ImErrorFatal("Nbits <= MaxBits", -1, + + IMEENCODING ); + + } + + IMSETMAXCODE(imLzwSp, + + IMMAXCODE(imLzwSp->lzwNbits)+1); + + } + + } + + hit: + + ; + + } + + + + imLzwSp->lzwOldcode = ent; + + + + return ( 0 ); + +} + + + + + +/* + + * FUNCTION + + * ImLzwPostEncode - Clean up after compression + + * + + * DESCRIPTION + + * Finish off an encoded strip by flushing the last + + * string and tacking on an End Of Information code. + + * Returns the total number of bytes written out. + + */ + +int + +#ifdef __STDC__ + +ImLzwPostEncode( int ioType, int fd, FILE *fp ) + +#else + +ImLzwPostEncode( ioType, fd, fp ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + +#endif + +{ + + if (imLzwSp->lzwOldcode != -1) + + if (imLzwPutNextCode(ioType, fd, fp, imLzwSp->lzwOldcode) == -1) + + return( -1 ); /* ImErrNo already set */ + + + + if (imLzwPutNextCode( ioType, fd, fp, IMCODE_EOI) == -1) + + return( -1 ); /* ImErrNo already set */ + + + + if (imLzwWriteData( ioType, fd, fp ) == -1) + + return( -1 ); /* ImErrNo already set */ + + + + return ( imLzwSp->lzwBytesWritten ); + +} + + + + + +/* + + * FUNCTION + + * imLzwPutNextCode - Put the next code to output + + * + + * DESCRIPTION + + * Put the next code to output + + */ + +static int + +#ifdef __STDC__ + +imLzwPutNextCode( int ioType, int fd, FILE *fp, int c) + +#else + +imLzwPutNextCode( ioType, fd, fp, c) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + int c; /* Code */ + +#endif + +{ + + int r_off, bits, code = c; + + char *bp; + + + + r_off = imLzwSp->lzwBitoff; + + bits = imLzwSp->lzwNbits; + + + + /* + + * Flush buffer if code doesn't fit. + + */ + + if (r_off + bits > imLzwSp->lzwBitsize) + + { + + /* + + * Calculate the number of full bytes that can be + + * written and save anything else for the next write. + + */ + + if (r_off & 7) + + { + + imLzwSp->lzwRawcc = r_off >> 3; + + bp = imLzwSp->lzwRawdata + imLzwSp->lzwRawcc; + + + + if (imLzwWriteData( ioType, fd, fp ) == -1) + + return( -1 ); /* ImErrNo already set */ + + + + imLzwSp->lzwRawdata[0] = *bp; + + } + + else + + { + + /* + + * Otherwise, on a byte boundary (in + + * which lzwRawcc is already correct). + + */ + + if (imLzwWriteData( ioType, fd, fp ) == -1) + + return( -1 ); /* ImErrNo already set */ + + } + + bp = imLzwSp->lzwRawdata; + + imLzwSp->lzwBitoff = (r_off &= 7); + + } + + else + + { + + /* + + * Get to the first byte. + + */ + + bp = imLzwSp->lzwRawdata + (r_off >> 3); + + r_off &= 7; + + } + + + + /* + + * Note that lzw_bitoff is maintained as the bit offset + + * into the buffer w/ a right-to-left orientation (i.e. + + * lsb-to-msb). The bits, however, go in the file in + + * an msb-to-lsb order. + + */ + + bits -= (8 - r_off); + + *bp = (*bp & lmask[r_off]) | (code >> bits); + + bp++; + + if (bits >= 8) { + + bits -= 8; + + *bp++ = code >> bits; + + } + + if (bits) + + *bp = (code & rmask[bits]) << (8 - bits); + + + + /* + + * IM_ENC_OUTCOUNT is used by the compression analysis machinery + + * which resets the compression tables when the compression + + * ratio goes up. lzwBitoff is used here (in imLzwPutNextCode) for + + * inserting codes into the output buffer. imLzwSp_rawcc must + + * be updated for the mainline write code in TIFFWriteScanline() + + * so that data is flushed when the end of a strip is reached. + + * Note that the latter is rounded up to ensure that a non-zero + + * byte count is present. + + */ + + imLzwSp->IM_ENC_OUTCOUNT += imLzwSp->lzwNbits; + + imLzwSp->lzwBitoff += imLzwSp->lzwNbits; + + imLzwSp->lzwRawcc = (imLzwSp->lzwBitoff + 7) >> 3; + + + + return( 0 ); + +} + + + + + +/* + + * FUNCTION + + * imLzwWriteData - Write compressed data to output + + * + + * DESCRIPTION + + * Write compressed data to output + + */ + +static int + +#ifdef __STDC__ + +imLzwWriteData( int ioType, int fd, FILE *fp ) + +#else + +imLzwWriteData( ioType, fd, fp ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + +#endif + +{ + + if ( ImBinWrite( ioType, fd, fp, + + imLzwSp->lzwRawdata, UCHAR, 1, imLzwSp->lzwRawcc ) == -1 ) + + ImReturnBinError( ); + + + + imLzwSp->lzwBytesWritten += imLzwSp->lzwRawcc; + + + + imLzwSp->lzwRawcc = 0; + + + + return( 0 ); + +} + + + + + +/* + + * FUNCTION + + * imLzwClearBlock - Check compression ratio + + * + + * DESCRIPTION + + * Check compression ratio and, if things seem to + + * be slipping, clear the hash table and reset state. + + */ + +static int + +#ifdef __STDC__ + +imLzwClearBlock( int ioType, int fd, FILE *fp) + +#else + +imLzwClearBlock( ioType, fd, fp) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + +#endif + +{ + + long rat; + + + + imLzwSp->IM_ENC_CHECKPOINT = imLzwSp->IM_ENC_INCOUNT + IMCHECK_GAP; + + + + if (imLzwSp->IM_ENC_INCOUNT > 0x007fffff) /* shift will overflow */ + + { + + rat = imLzwSp->IM_ENC_OUTCOUNT >> 8; + + rat = (rat == 0 ? 0x7fffffff : imLzwSp->IM_ENC_INCOUNT / rat); + + } + + else /* 8 fract bits */ + + rat = (imLzwSp->IM_ENC_INCOUNT << 8) / imLzwSp->IM_ENC_OUTCOUNT; + + + + if (rat <= imLzwSp->IM_ENC_RATIO) + + { + + imLzwSp->IM_ENC_RATIO = 0; + + imLzwClearHash(); + + imLzwSp->lzwFree_ent = IMCODE_FIRST; + + if (imLzwPutNextCode( ioType, fd, fp, IMCODE_CLEAR) == -1) + + return( -1 ); /* ImErrNo already set */ + + IMSETMAXCODE( imLzwSp, IMMAXCODE(imLzwSp->lzwNbits = IMBITS_MIN)+1); + + } + + else + + imLzwSp->IM_ENC_RATIO = rat; + + + + return( 0 ); + +} + + + + + +/* + + * FUNCTION + + * imClearHash - Get the next code out of the buffer + + * + + * DESCRIPTION + + * Get the next code from the raw data buffer. + + */ + +/* + + * Reset code table. + + */ + +static void + +imLzwClearHash() + +{ + + int *htab_p = imLzwSp->IM_ENC_HTAB+IMHSIZE; + + int i, m1 = -1; + + + + i = IMHSIZE - 16; + + + + do { + + *(htab_p-16) = m1; + + *(htab_p-15) = m1; + + *(htab_p-14) = m1; + + *(htab_p-13) = m1; + + *(htab_p-12) = m1; + + *(htab_p-11) = m1; + + *(htab_p-10) = m1; + + *(htab_p-9) = m1; + + *(htab_p-8) = m1; + + *(htab_p-7) = m1; + + *(htab_p-6) = m1; + + *(htab_p-5) = m1; + + *(htab_p-4) = m1; + + *(htab_p-3) = m1; + + *(htab_p-2) = m1; + + *(htab_p-1) = m1; + + htab_p -= 16; + + } while ((i -= 16) >= 0); + + + + for (i += 16; i > 0; i--) + + *--htab_p = m1; + +} + + + +/* + + * FUNCTION + + * ImLzwCleanup - free storage + + * + + * DESCRIPTION + + * Release the structure memory used by lzw compression + + */ + +void + +ImLzwCleanup() + +{ + + if (imLzwSp->lzwRawdata) + + free(imLzwSp->lzwRawdata); + + if (imLzwSp) + + free(imLzwSp); + + + + imLzwSp = NULL; + +} + + + diff --git a/utils/roq2/libim/immiff.c b/utils/roq2/libim/immiff.c new file mode 100644 index 0000000..47d21c6 --- /dev/null +++ b/utils/roq2/libim/immiff.c @@ -0,0 +1,3340 @@ +/** + + ** $Header: /roq/libim/immiff.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/immiff.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** immiff.c- ImageMagick's Machine Independent File Format file i/o + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** immiff.c contains routines to read and write ImageMagick's MIFF + + ** files for the image manipulation library. Raster data read in + + ** is stored in a VFB. Raster data written out is taken from a tag table. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** none + + ** + + ** PRIVATE CONTENTS + + ** imMiffRead f read a MIFF file + + ** imMiffWriteRGB f write an RGB MIFF file + + ** imMiffWriteIndex f write an Indexed MIFF file + + ** + + ** HISTORY + + ** $Log: /roq/libim/immiff.c $ + * + * 1 11/02/99 4:38p Zaphod + + * Revision 1.6 1995/06/30 22:09:07 bduggan + + * removed strings.h + + * + + * Revision 1.5 1995/06/29 00:28:04 bduggan + + * updated copyright year + + * + + * Revision 1.4 1995/06/29 00:20:26 bduggan + + * changed comment + + * + + * Revision 1.3 1995/06/15 20:10:59 bduggan + + * took out some useless vars + + * + + * Revision 1.2 1995/05/17 23:45:35 bduggan + + * Used library 'imSameRGB' calls instead of local miff calls + + * + + ** Revision 1.1 1995/04/03 21:29:34 bduggan + + ** Initial revision + + ** + + ** + + **/ + + + +#include + +#include "iminternal.h" + + + + + + + +/** + + ** FORMAT + + ** MIFF - Magick Image File Format + + ** + + ** AKA + + ** (none) + + ** + + ** FORMAT REFERENCES + + ** Anonymous ftp at ftp.x.org, + + ** file: contrib/applications/ImageMagick/ImageMagick-3.6.tar.gz + + ** + + ** CODE CREDITS + + ** Brian Duggan, Custom Development, San Diego Supercomputer Center, 1995. + + ** + + ** DESCRIPTION + + ** MIFF files may contain multiple images. To store multiple images, + + ** one can simply create several MIFF's, then concatenate them together. + + ** A single MIFF file contains a header, followed by a clt (optional), + + ** followed by pixel data. + + ** + + ** Header + + ** ------ + + ** The header is text based. The idea is that if you `more` a + + ** .miff, you'll be able to see what type of file it is. + + ** + + ** A miff header consists of labels of the form, "field=value", + + ** and comments contained in curly braces ({}). The header ends + + ** with a colon (:) followed by a newline character. + + ** + + ** By convention, a formfeed character and a newline character + + ** appear before the colon. (Hence, using 'more' will pause + + ** before the binary data.) + + ** + + ** The header fields are as follows: + + ** id=ImageMagick + + ** This indicates that we have a MIFF file. + + ** This field is mandatory. + + ** + + ** class= + + ** can be one of: + + ** DirectClass (RGB Image) + + ** PseudoClass (Indexed Image) + + ** If this field is omitted, the image is RGB. + + ** + + ** colors= + + ** If this is not present, there is not clt. + + ** (i.e. the image is RGB or grayscale) + + ** If this is present and the image is RGB, then + + ** this field is supposed to contain the number + + ** of colors in the image. + + ** + + ** columns= + + ** rows= + + ** These fields are required. + + ** + + ** compression= + + ** can be one of + + ** RunlengthEncoded + + ** QEncoded + + ** The RLE algorithm for RunlengthEncoded images is explained below. + + ** The algorithm for QEncoded images is similar to JPEG encoding. It + + ** is not supported here. + + ** If this field is omitted, there is no compression. + + ** + + ** matte= + + ** if is True, we have an alpha channel. + + ** Otherwise, we don't. Color indexed images + + ** can't have alpha channels. + + ** + + ** packets= + + ** This is used for QEncoding. We ignore it. + + ** + + ** montage=x{+-}{+-} + + ** This is miff's handy way of storying a storyboard + + ** of pictures as one file, while seperating + + ** them within the file. It is not supported here. + + ** + + ** scene= + + ** signature= + + ** These fields are used when a miff is part of + + ** a sequence of pictures, and hence may have + + ** a CLT stored in a different frame. These + + ** options are not supported here. + + ** + + ** Sample MIFF header: + + ** { + + ** This is a color indexed image. + + ** } + + ** id=ImageMagick + + ** class=PseudoClass colors=2 + + ** compression=RunlengthEncoded + + ** columns=768 rows=640 + + ** + + ** : + + ** Notice that fields are seperate by spaces and/or + + ** carriage returns, and may occur in any order. + + ** + + ** Pixel Storage + + ** ------------- + + ** If there is no compression, the pixels are simply stored + + ** as RGBRGB or RGBARGBA or IIIII (I = index). If there is + + ** RLE encoding then the following scheme is used: + + ** + + ** RLE encoding scheme: + + ** + + ** For RGB images, the data are encoded in 4 byte packages. + + ** (or 5 bytes if there is an alpha channel.) The first three + + ** (or four) bytes contain the data for a pixel. The fourth + + ** (fifth) byte contains one less than the number of pixels in + + ** the run. This value is in the range 0-255. + + ** + + ** For instance 4A 0D 00 05 indicates a run of six pixels + + ** with red component 4A, green component 0D, and blue component + + ** 00. + + ** + + ** The same scheme is used for encoding color-indexed images. + + ** The number of bytes in a package is either two or three + + ** depending on whether this is a 16-bit or an 8-bit color + + ** indexed image. + + ** + + ** Storage of the color lookup table: + + ** + + ** The CLT is stored directly after the header (if there + + ** is a CLT). It is simply a series of R, G, and B values. + + ** The number of triplets is indicated by the "colors" field + + ** in the header. + + **/ + + + +/* + + * MIFF - ImageMagick's MIFF file format + + * For information on these structures, how to use them, etc. please + + * see imfmt.c. + + */ + + + +#ifdef __STDC__ + +static int imMiffRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imMiffWriteRGB( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + + TagTable *flagsTable, TagTable *tagTable); + +static int imMiffWriteRawRGB( ImVfb* vfb, int fd, FILE* fp, int numChans, int ioType); + +static int imMiffWriteRLERGB( ImVfb* vfb, int fd, FILE* fp, int ioType); + +static int imMiffWriteRLERGBA( ImVfb* vfb, int fd, FILE* fp, int ioType); + +static int imMiffWriteRawIndex8(ImVfb* vfb, int fd, FILE* fp, int ioType); + +static int imMiffWriteRawIndex16(ImVfb* vfb, int fd, FILE* fp, int ioType); + +static int imMiffWriteRLEIndex8(ImVfb* vfb, int fd, FILE* fp, int ioType); + +static int imMiffWriteRLEIndex16(ImVfb* vfb, int fd, FILE* fp, int ioType); + +static int imMiffWriteIndex( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + + TagTable *flagsTable, TagTable *tagTable); + +static char* imMiffGetHeaderField( char* header, char* field); + +static ImClt * imMiffReadClt( int ioType, int fd, FILE* fp, int cltSize ); + +static ImVfb* imMiffReadRLE(int ioType, int fd, FILE* fp, int width,int height,int numChans, int chanDepth); + +static ImVfb* imMiffReadRaw(int ioType, int fd, FILE* fp, int width,int height,int numChans, int chanDepth); + +static char* strToLower( char* str); + + + +#else + + + +static int imMiffRead( ); + +static int imMiffWriteRGB( ); + +static char* imMiffGetHeaderField( ); + +static ImClt * imMiffReadClt( ); + +static ImVfb* imMiffReadRLE( ); + +static ImVfb* imMiffReadRaw( ); + +static char* strToLower( ); + +static int imMiffWriteIndex(); + +static int imMiffWriteRawRGB( ); + +static int imMiffWriteRLERGB( ); + +static int imMiffWriteRLERGBA( ); + +static int imMiffWriteRLEIndex8(); + +static int imMiffWriteRLEIndex16(); + +static int imMiffWriteRawIndex8(); + +static int imMiffWriteRawIndex16(); + +#endif + + + +static char *imMiffNames[ ] = { "miff", NULL }; + +static ImFileFormatReadMap imMiffReadMap[ ] = + +{ + + /* in out */ + + /* type,ch,dep, attr. VFB type attr. */ + + { IN,1,8, 0, IMVFBINDEX8, 0 }, + + { IN,1,8, RLE, IMVFBINDEX8, 0 }, + + { IN,1,8, C, IMVFBINDEX8, 0 }, + + { IN,1,8, RLE|C, IMVFBINDEX8, 0 }, + + { IN,1,16, C, IMVFBINDEX16, 0 }, + + { IN,1,16, RLE | C,IMVFBINDEX16, 0 }, + + { RGB,3,8, 0, IMVFBRGB, 0 }, + + { RGB,3,8, RLE, IMVFBRGB, 0 }, + + { RGB,4,8, A, IMVFBRGB, A }, + + { RGB,4,8, RLE|A, IMVFBRGB, A }, + + { -1, 0, -1, 0 }, + +}; + +static ImFileFormatWriteMap imMiffWriteMap[ ] = + +{ + + /* in out */ + + /* VFB type, attr., type,ch,dep, attr., func */ + + { IMVFBRGB, 0, RGB,3,8, RLE, imMiffWriteRGB}, + + { IMVFBRGB, A, RGB,4,8, A|RLE, imMiffWriteRGB}, + + { IMVFBRGB, 0, RGB,3,8, 0, imMiffWriteRGB}, + + { IMVFBRGB, A, RGB,4,8, A, imMiffWriteRGB}, + + { IMVFBINDEX8, 0, IN,1,8, RLE, imMiffWriteIndex}, + + { IMVFBINDEX8, C, IN,1,8, C|RLE, imMiffWriteIndex}, + + { IMVFBINDEX8, 0, IN,1,8, 0, imMiffWriteIndex}, + + { IMVFBINDEX8, C, IN,1,8, C, imMiffWriteIndex}, + + { IMVFBINDEX16, C, IN,1,16, C|RLE, imMiffWriteIndex}, + + { IMVFBINDEX16, C, IN,1,16, C, imMiffWriteIndex}, + + { -1, 0, -1, 0, NULL }, + +}; + + + +static ImFileMagic imFileMiffMagic []= + +{ + + { 0, 0, NULL }, + +}; + + + +ImFileFormat ImFileMiffFormat = + +{ + + imMiffNames, "ImageMagick's MIFF file format", + + "John Cristy", + + "8 or 16-bit color index with optional CLT and 24-bit RGB color images with optional alpha channels,\n\ +uncompressed (verbatim) and RLE-compressed.", + "8 or 16-bit color index with optional CLT and 24-bit RGB color images with optional alpha channels,\n\ +uncompressed (verbatim) and RLE-compressed.", + + imFileMiffMagic, + + IMMULTI, IMPIPE, + + IMMULTI, IMPIPE, + + imMiffRead, imMiffReadMap, imMiffWriteMap + +}; + + + + + + + +/* + + * FUNCTION + + * imMiffRead - Read a MIFF file + + * + + * DESCRIPTION + + * The file header is read and the size of the image determined. + + * Space is allocated for the VFB and the run codes read in to + + * a run buffer. The run buffer is then expanded into straight + + * RGB values in the VFB and the completed VFB added to the tag list. + + */ + + + +static int /* Returns # tags read in */ + +#ifdef __STDC__ + +imMiffRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imMiffRead( ioType, fd, fp, flagsTable, tagTable ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + +#endif + +{ + + char *header; /* Contains the (text) header */ + + char c; /* Contains a single char */ + + int skippingComment; /* flag that says we're skipping stuff */ + + int index; /* index into character array */ + + char* fieldValue; /* value of a field in the header */ + + int numChans; /* 1,3 or 4 (Indexed, rgb or rgba) */ + + int chanDepth; /* 8 or 16 (16 for index16) */ + + int compression; /* IMCOMPNONE or IMCOMPRLE */ + + int width, height; /* Dimensions */ + + int cltSize; /* 0 means no CLT */ + + char message[300]; /* Holds message */ + + ImVfb *vfb; /* new vfb */ + + ImClt* clt; /* new clt */ + + int moreImages; /* flag saying to continue reading */ + + int numImages; /* number of images read */ + + int retVal; /* return value from BinRead call */ + + + + /* + + * Read in the header. + + */ + + ImMalloc (header, char* , 1024*sizeof(char)); + + numImages = 0; + + moreImages = 1; + + + + while (moreImages) + + { + + index = 0; + + c = '\0'; + + skippingComment = 0; + + /* + + * Read the next character if ... + + * we haven't read a :, and we're not inside a comment block + + * OR + + * we are inside a comment block + + * + + * This loop reads in anything that is not contained between two + + * curly braces. (It also skips any left curly braces.) + + */ + + while ( (c!=':' && !skippingComment) || skippingComment) + + { + + if( (retVal = ImBinRead( ioType,fd,fp,&c,CHAR,sizeof(char),1))<0) + + ImReturnBinError( ); + + if (retVal==0) + + break; + + if (skippingComment==1 && c=='}') + + skippingComment = 0; + + if (c=='{') + + skippingComment = 1; + + if (skippingComment==0 && c!='}') + + { /* Add this character to our array */ + + header[index++] = c; + + if (index >= 1024) + + { + + /* This'll only happen if there + + * are an extraordinary number of + + * whitespace characters in the + + * header, or if there are a lot + + * of illegal fields. We shan't + + * cater to such miff files. + + */ + + ImErrorFatal(ImQError(), -1, IMEMALLOC); + + } + + } + + + + } + + header[index] = '\0'; + + if (retVal==0) + + break; + + + + /* + + * We just read in a ':'. Read in a newline character, + + * then we'll be done with the header. + + */ + + if( ImBinRead( ioType,fd,fp,&c,CHAR,sizeof(char),1)<=0) + + ImReturnBinError( ); + + + + numImages++; /* If we made it this far, hopefully there's an image. */ + + if (numImages > 1) + + { + + sprintf(message,"%d",numImages); + + ImInfo ("Image", message); + + } + + + + /* + + * Get the image dimensions + + */ + + fieldValue = imMiffGetHeaderField(header,"rows"); + + if (fieldValue==NULL) + + { + + ImErrorFatal (ImQError( ), -1, IMENOIMAGE); + + } + + height = atoi(fieldValue); + + + + fieldValue = imMiffGetHeaderField(header,"columns"); + + if (fieldValue==NULL) + + { + + ImErrorFatal (ImQError( ), -1, IMENOIMAGE); + + } + + width = atoi(fieldValue); + + + + sprintf(message,"%d x %d",width, height); + + ImInfo("Resolution",message); + + + + fieldValue = imMiffGetHeaderField(header,"class"); + + if (fieldValue==NULL || strcmp(fieldValue,"directclass")==0) + + { + + ImInfo ("Type","24-bit RGB"); + + + + /* Is there an alpha channel ? */ + + fieldValue = imMiffGetHeaderField(header,"matte"); + + if (fieldValue && strcmp(fieldValue,"true")==0) + + { + + numChans = 4; + + ImInfo ("Alpha Channel","8-bit"); + + } + + else + + { + + numChans = 3; + + ImInfo ("Alpha Channel","none"); + + } + + chanDepth = 8; + + + + /* Can't a have a clt with an RGB image */ + + cltSize = 0; + + ImInfo("Color Table", "none"); + + } + + else + + { + + /* Color indexed */ + + numChans = 1; + + + + /* Are we 8 or 16 bit? */ + + fieldValue = imMiffGetHeaderField(header,"colors"); + + if (fieldValue==NULL) + + cltSize = 0; + + else + + cltSize = atoi(fieldValue); + + + + if (cltSize > (1<<8)) + + chanDepth = 16; + + else + + chanDepth = 8; + + + + sprintf(message,"%d-bit color indexed",chanDepth); + + ImInfo ("Type",message); + + + + /* Do we have a clt? */ + + if (cltSize==0) + + { + + ImInfo("Color Table","none"); + + } + + else + + { + + sprintf(message,"%d entries",cltSize); + + ImInfo("Color Table", message); + + } + + } + + + + /* + + * Is there compression? + + */ + + fieldValue = imMiffGetHeaderField(header,"compression"); + + if (fieldValue==NULL) + + { + + compression = IMCOMPNONE; + + ImInfo ("Compression","none"); + + } + + else + + if (strcmp(fieldValue,"runlengthencoded")==0) + + { + + compression = IMCOMPRLE; + + ImInfo ("Compression","Run Length Encoded"); + + } + + else + + { + + ImErrorFatal ("Unsupported compression scheme", -1, IMENOIMAGE); + + } + + + + + + /* + + * Read the clt if there is one + + */ + + if (cltSize!=0) + + { + + clt = imMiffReadClt( ioType, fd, fp, cltSize ); + + if (clt==IMCLTNULL) /* error */ + + return 0; + + } + + else + + clt = IMCLTNULL; + + + + /* Read the vfb! */ + + if (compression==IMCOMPRLE) + + { + + vfb = imMiffReadRLE(ioType, fd, fp, width, height, numChans, chanDepth); + + } + + else + + { + + vfb = imMiffReadRaw(ioType, fd, fp, width, height, numChans, chanDepth); + + } + + if (vfb==IMVFBNULL) + + return 0; + + + + if (clt!=IMCLTNULL) + + { + + ImVfbSClt (vfb, clt); + + TagTableAppend( tagTable, TagEntryAlloc( "image clt", POINTER, &clt)); + + } + + else + + { + + ImVfbSClt (vfb, IMCLTNULL); + + } + + + + TagTableAppend( tagTable, TagEntryAlloc( "image vfb", POINTER, &vfb)); + + /* + + * Are there any more images ? + + */ + + moreImages = 1; /* Yes. The only way to exit the loop is by */ + + /* failing to read the header. */ + + } /* End of while-more images */ + + + + return numImages; + +} + + + + + +/* + + * FUNCTION + + * imMiffGetHeaderField + + * + + * DESCRIPTION + + * Parse the string, looking for something of + + * the form field=value. Return value (in lower case). + + * If field is not found, return NULL. + + */ + + + +static char* /* returns value for this field in lower case */ + +#ifdef __STDC__ + +imMiffGetHeaderField( char* header, char* field) + +#else + +imMiffGetHeaderField( header, field) + +char* header; + +char* field; + +#endif + +{ + + char* ptr; /* points into the string */ + + static char ret[100]; /* string to return */ + + + + if (header==NULL) return NULL; + + if (field==NULL) return NULL; + + + + ptr = strstr(header,field); + + + + if (ptr==NULL) + + return NULL; + + ptr += strlen(field); + + + + if ((*ptr)!='=') return NULL; /* need an '=' */ + + sscanf (ptr+1,"%s",ret); /* Read the next word */ + + + + return strToLower(ret); + +} + + + +/* + + * FUNCTION + + * imMiffReadClt + + * + + * DECSRIPTION + + * Read in the clt from a MIFF image file + + */ + + + +static ImClt * /* Returns a brand new clt */ + +#ifdef __STDC__ + +imMiffReadClt( int ioType, int fd, FILE* fp,int cltSize ) + +#else + +imMiffReadClt( ioType, fd, fp, cltSize ) + +int ioType; + +int fd; + +FILE* fp; + +int cltSize; + +#endif + +{ + + ImClt* clt; /* Our new clt */ + + ImCltPtr cltPtr; /* points at our new clt */ + + unsigned char* rbuf; /* holds the clt data */ + + unsigned char* rbufptr; /* points into rbuf */ + + int i; /* loop index */ + + + + clt = ImCltAlloc (cltSize); + + + + /* Read in the values */ + + cltPtr = ImCltQFirst(clt); + + ImMallocRetOnError (rbuf, unsigned char*, 3 * cltSize, IMCLTNULL); + + rbufptr = rbuf; + + if ( ImBinRead (ioType, fd, fp, rbuf, UCHAR, 1, 3 * cltSize) == -1) + + { + + ImReturnValBinError( IMCLTNULL ); + + } + + + + /* + + * Now we've read in the data. Put it in the clt. + + */ + + for (i=0; i 0) + + { + + for (i=0; i < rbuf[1] + 1; i++) + + { + + ImVfbSIndex8( vfb, vfbPtr, rbuf[0]); + + vfbPtr = ImVfbQNext( vfb, vfbPtr); + + } + + } + + } + + else + + { /* index16 */ + + if ((vfb=ImVfbAlloc( width, height, IMVFBINDEX16 ))==IMVFBNULL) + + { + + ImErrorFatal (ImQError( ), IMVFBNULL, ImErrNo); + + } + + vfbPtr = ImVfbQPtr( vfb, 0, 0); + + + + /* Read packets of size 3 */ + + + + while ( ImBinRead (ioType, fd, fp, rbuf, UCHAR, 1, 3) > 0) + + { + + uintval = rbuf[1]; + + uintval |= rbuf[0] << 8; + + for (i=0; i < rbuf[2] + 1; i++) + + { + + ImVfbSIndex16( vfb, vfbPtr, uintval); + + vfbPtr = ImVfbQNext( vfb, vfbPtr); + + } + + } + + } /* End of index16 */ + + break; + + + + case 3 : /* RGB */ + + if ((vfb=ImVfbAlloc( width, height, IMVFBRGB ))==IMVFBNULL) + + { + + ImErrorFatal (ImQError( ), IMVFBNULL, ImErrNo); + + } + + vfbPtr = ImVfbQPtr( vfb, 0, 0); + + + + /* Read packets of size 4 */ + + + + while ( ImBinRead (ioType, fd, fp, rbuf, UCHAR, 1, 4) > 0) + + { + + for (i=0;i 0) + + { + + for (i=0;i 1) + + { + + sprintf(message,"%d of %d",curImage+1, numImages); + + ImInfo("Image",message); + + } + + sprintf(message,"%d x %d", x, y); + + ImInfo ("Resolution", message); + + ImInfo ("Type", "24-bit RGB"); + + ImInfo ("Color Table","none"); + + if (pMap->map_outAttributes & IMCOMPRLE) + + { + + ImInfo ("Compression Type", "Run Length Encoded"); + + } + + else + + { + + ImInfo ("Compression Type", "none"); + + } + + if (pMap->map_outAttributes & IMALPHAYES) + + { + + numChans = 4; + + ImInfo ("Alpha Channel", "8-bit"); + + } + + else + + { + + numChans = 3; + + ImInfo ("Alpha Channel", "none"); + + } + + + + if (!flagsTable || TagTableQNEntry (flagsTable, "file name")==0) + + { + + filename = NULL; + + } + + else + + { + + TagEntryQValue( TagTableQDirect ( flagsTable, "file name", 0 ), &filename); + + } + + + + /* + + * Write the header + + */ + + sprintf(header, "{\n %s\n %s\n}\nid=ImageMagick\nclass=DirectClass\nrows=%d\ncolumns=%d\n%s%s\f\n:\n", + + filename ? filename : "(untitled)", + + "This file was created by the SDSC Image Tools.", + + y, /* rows */ + + x, /* columns */ + + numChans==4 ? "matte=True\n" : "matte=False\n", + + (pMap->map_outAttributes & IMCOMPRLE) ? "compression=RunlengthEncoded\n" : "" + + ); + + + + if ( ImBinWrite( ioType, fd, fp, header, CHAR, 1, strlen(header))==-1) + + { + + ImReturnBinError( ); + + } + + + + /* + + * Write the pixels + + */ + + if (!(pMap->map_outAttributes & IMCOMPRLE)) + + { + + if (imMiffWriteRawRGB(vfb, fd, fp, numChans, ioType)==-1) + + return -1; + + } + + else /* RLE compress */ + + { + + if (pMap->map_outAttributes & IMALPHAYES) + + { + + if (imMiffWriteRLERGBA(vfb, fd, fp, ioType)==-1) + + return -1; + + } + + else + + { + + if (imMiffWriteRLERGB(vfb, fd, fp, ioType)==-1) + + return -1; + + } + + } + + } + + + + return numImages; + +} + + + +/* + + * FUNCTION + + * imMiffWriteRawRGB + + * + + * DESCRIPTION + + * Write the pixels of the vfb without compressing them. + + */ + +static int /* returns status */ + +#ifdef __STDC__ + +imMiffWriteRawRGB( ImVfb* vfb, int fd, FILE* fp, int numChans, int ioType) + +#else + +imMiffWriteRawRGB( vfb, fd, fp, numChans, ioType) + +ImVfb* vfb; + +int fd; + +FILE* fp; + +int numChans; + +int ioType; + +#endif + +{ + + unsigned char* runBuffer; /* holds pixels */ + + ImVfbPtr vfbptr; /* points into a vfb */ + + int runIndex, i,j,x,y; /* indexes, dimensions */ + + + + x = ImVfbQWidth(vfb); + + y = ImVfbQHeight(vfb); + + + + ImMalloc( runBuffer, unsigned char *, sizeof (unsigned char) * x * numChans); + + vfbptr = ImVfbQPtr( vfb, 0, 0); + + for (i=0; i> 8); \ + dumpBuf[1] = (v & 0x00ff); \ + dumpBuf[2] = len; \ + if ( ImBinWrite( ioType, fd, fp, dumpBuf, UCHAR, 1, 3)==-1) \ + { \ + ImReturnBinError( ); \ + } + + + + + +/* + + * FUNCTION + + * imMiffWriteRLERGB + + * + + * DESCRIPTION + + * Write the pixels of the vfb using RLE compression. + + */ + +static int /* returns status */ + +#ifdef __STDC__ + +imMiffWriteRLERGB( ImVfb* vfb, int fd, FILE* fp, int ioType) + +#else + +imMiffWriteRLERGB( vfb, fd, fp, ioType) + +ImVfb* vfb; + +int fd; + +FILE* fp; + +int ioType; + +#endif + +{ + + int runLength, i,x,y; /* index, dimensions */ + + ImVfbPtr thisPixel; /* this pixel */ + + ImVfbPtr lastPixel; /* last pixel */ + + unsigned char dumpBuf[10]; /* Buffer for output */ + + + + /* + + * The unsigned ints above are used to store red,green, and blue + + * values for pixels. i.e. three of the bytes are used for these + + * values. + + */ + + + + x = ImVfbQWidth(vfb); + + y = ImVfbQHeight(vfb); + + + + runLength = 0; + + + + lastPixel = ImVfbQPtr( vfb, 0, 0); + + thisPixel = ImVfbQPtr( vfb, 0, 0); + + for (i=1; i 0xff) + + { + + runLength--; + + /* Dump run */ + + imMiffDumpRun3(ImVfbQRed(vfb,lastPixel), + + ImVfbQGreen(vfb,lastPixel), + + ImVfbQBlue(vfb,lastPixel), + + runLength); + + runLength = 0; + + } + + } + + else /* new run. Dump the last one. */ + + { + + imMiffDumpRun3(ImVfbQRed(vfb,lastPixel), + + ImVfbQGreen(vfb,lastPixel), + + ImVfbQBlue(vfb,lastPixel), + + runLength); + + runLength = 0; + + } + + lastPixel = thisPixel; + + } + + + + /* Dump the last run */ + +imMiffDumpRun3(ImVfbQRed(vfb,lastPixel), + + ImVfbQGreen(vfb,lastPixel), + + ImVfbQBlue(vfb,lastPixel), + + runLength); + + return 1; + +} + + + + + +/* + + * FUNCTION + + * imMiffWriteRLERGBA + + * + + * DESCRIPTION + + * Write the pixels of the vfb (with alpha) using RLE compression. + + */ + +static int /* returns status */ + +#ifdef __STDC__ + +imMiffWriteRLERGBA( ImVfb* vfb, int fd, FILE* fp, int ioType) + +#else + +imMiffWriteRLERGBA( vfb, fd, fp, ioType) + +ImVfb* vfb; + +int fd; + +FILE* fp; + +int ioType; + +#endif + +{ + + ImVfbPtr vfbptr; /* points into a vfb */ + + int runLength, i,x,y; /* indexes, dimensions */ + + ImVfbPtr thisPixel; /* this pixel */ + + ImVfbPtr lastPixel; /* last pixel */ + + unsigned char dumpBuf[10]; /* Buffer for output */ + + + + /* + + * The unsigned ints above are used to store red,green, and blue + + * values for pixels. i.e. three of the bytes are used for these + + * values. + + */ + + + + x = ImVfbQWidth(vfb); + + y = ImVfbQHeight(vfb); + + + + runLength = 0; + + + + lastPixel = ImVfbQPtr( vfb, 0, 0); + + thisPixel = ImVfbQPtr( vfb, 0, 0); + + for (i=1; i 0xff) + + { + + runLength--; + + /* Dump run */ + + imMiffDumpRun4(ImVfbQRed(vfb,lastPixel), + + ImVfbQGreen(vfb,lastPixel), + + ImVfbQBlue(vfb,lastPixel), + + ImVfbQAlpha(vfb,lastPixel), + + runLength); + + runLength = 0; + + } + + } + + else /* new run. Dump the last one. */ + + { + + imMiffDumpRun4(ImVfbQRed(vfb,lastPixel), + + ImVfbQGreen(vfb,lastPixel), + + ImVfbQBlue(vfb,lastPixel), + + ImVfbQAlpha(vfb,lastPixel), + + runLength); + + runLength = 0; + + } + + lastPixel = thisPixel; + + } + + + + /* Dump the last run */ + +imMiffDumpRun4(ImVfbQRed(vfb,lastPixel), + + ImVfbQGreen(vfb,lastPixel), + + ImVfbQBlue(vfb,lastPixel), + + ImVfbQAlpha(vfb,lastPixel), + + runLength); + + return 1; + +} + + + +/* + + * FUNCTION + + * imMiffWriteIndex + + * + + * DESCRIPTION + + * Write an indexed miff file with... + + * - no compression + + * - possibly a clt + + */ + +static int /* returns number of tags used */ + +#ifdef __STDC__ + +imMiffWriteIndex( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + + TagTable *flagsTable, TagTable *tagTable) + +#else + +imMiffWriteIndex( pMap, ioType, fd, fp, flagsTable, tagTable) + +ImFileFormatWriteMap * pMap; + +int ioType; + +int fd; + +FILE* fp; + +TagTable* flagsTable; + +TagTable* tagTable; + +#endif + +{ + + ImVfb* vfb; /* image vfb */ + + char header[400]; /* header (text based) */ + + int i; /* loop indexes */ + + int runIndex; /* yet another index */ + + unsigned char* runBuffer;/* buffer */ + + char message[100]; /* another buffer */ + + char* filename; /* filename */ + + ImClt* clt; /* image clt */ + + ImCltPtr cltptr; /* points into the clt */ + + char colorstring[30]; /* string for the header */ + + int numColors; /* num of colors in clt*/ + + int chanDepth; /* 8 or 16 */ + + int numImages; /* number of images */ + + int curImage; /* loop index */ + + int numZeros; /* how much to pad the clt */ + + + + BinByteOrder( BINMBF ); + + numImages = TagTableQNEntry (tagTable, "image vfb"); + + + + for (curImage=0;curImagemap_outAttributes & IMCLTYES) + + { + + clt = ImVfbQClt(vfb); + + numColors = ImCltQNColors(clt); + + + + if (pMap->map_outChannelDepth==16) + + { + + chanDepth = 16; + + ImInfo ("Type", "16-bit Color Indexed"); + + /* Need to pad the color table if we don't have enough colors */ + + if (numColors <= 256) + + numZeros = 257-numColors; + + else + + numZeros = 0; + + } + + else + + { + + chanDepth = 8; + + ImInfo ("Type", "8-bit Color Indexed"); + + numZeros = 0; + + } + + + + sprintf(message,"%d entries",numColors+numZeros); + + ImInfo ("Color Table",message); + + sprintf(colorstring,"colors=%d\n",numColors+numZeros); + + } + + else /* no clt */ + + { + + strcpy(colorstring, "" ); + + chanDepth = pMap -> map_outChannelDepth; + + sprintf(message,"%d-bit Greyscale",chanDepth); + + ImInfo ("Color Table","none"); + + ImInfo ("Type",message); + + } + + + + sprintf(message,"%d x %d", ImVfbQWidth(vfb), ImVfbQHeight(vfb)); + + ImInfo ("Resolution", message); + + ImInfo ("Alpha Channel", "none"); + + + + if (!flagsTable || TagTableQNEntry (flagsTable, "file name")==0) + + { + + filename = NULL; + + } + + else + + { + + TagEntryQValue( TagTableQDirect ( flagsTable, "file name", 0 ), &filename); + + } + + + + /* + + * Write the header + + */ + + sprintf(header, "{\n %s\n %s\n}\nid=ImageMagick\nclass=PseudoClass\nrows=%d\ncolumns=%d\n%s%s\f\n:\n", + + filename ? filename : "(untitled)", + + "This file was created by the SDSC Image Tools.", + + ImVfbQHeight(vfb), /* rows */ + + ImVfbQWidth(vfb), /* columns */ + + (pMap->map_outAttributes & IMCOMPRLE) ? "compression=RunlengthEncoded\n" : "", + + colorstring + + ); + + + + if ( ImBinWrite( ioType, fd, fp, header, CHAR, 1, strlen(header))==-1) + + { + + ImReturnBinError( ); + + } + + + + + + if (pMap->map_outAttributes & IMCLTYES) + + { + + /* + + * Write the clt + + */ + + cltptr = ImCltQFirst( clt ); + + ImCalloc ( runBuffer, unsigned char *, (numColors + numZeros) * 3, 1); + + runIndex = 0; + + for (i=0;imap_outAttributes & IMCOMPRLE) + + { + + if (imMiffWriteRLEIndex8(vfb, fd, fp, ioType)<0) + + return -1; + + ImInfo ("Compression Type", "Run Length Encoded"); + + } + + else + + { + + ImInfo ("Compression Type", "none"); + + if (imMiffWriteRawIndex8(vfb, fd, fp, ioType)<0) + + return -1; + + } + + } + + else /* 16-bit */ + + { + + if (pMap->map_outAttributes & IMCOMPRLE) + + { + + if (imMiffWriteRLEIndex16(vfb, fd, fp, ioType)<0) + + return -1; + + ImInfo ("Compression Type", "Run Length Encoded"); + + } + + else + + { + + if (imMiffWriteRawIndex16(vfb, fd, fp, ioType)<0) + + return -1; + + ImInfo ("Compression Type", "none"); + + } + + + + } + + } + + + + return numImages; + +} + + + + + +/* + + * FUNCTION + + * imMiffWriteRawIndex8 + + * + + * DESCRIPTION + + * Write the pixels of an indexed vfb with no compression + + */ + +static int /* returns status */ + +#ifdef __STDC__ + +imMiffWriteRawIndex8(ImVfb* vfb, int fd, FILE* fp, int ioType) + +#else + +imMiffWriteRawIndex8(vfb, fd, fp, ioType) + +ImVfb* vfb; + +int fd; + +FILE* fp; + +int ioType; + +#endif + +{ + + int runIndex; /* index */ + + unsigned char* runBuffer;/* buffer */ + + ImVfbPtr vfbptr; /* points into a vfb */ + + int x, y; /* dimensions of vfb */ + + int i,j; /* loop indexes */ + + + + x = ImVfbQWidth(vfb); + + y = ImVfbQHeight(vfb); + + + + vfbptr = ImVfbQPtr( vfb, 0, 0); + + ImMalloc( runBuffer, unsigned char *, sizeof (unsigned char) * x ); + + + + for (i=0; i> 8; + + runBuffer[runIndex++] = (ImVfbQIndex16(vfb, vfbptr)) & 0x00ff; + + vfbptr = ImVfbQNext(vfb,vfbptr); + + } + + if ( ImBinWrite( ioType, fd, fp, runBuffer, UCHAR, 1, x*2 )==-1) + + { + + ImReturnBinError( ); + + } + + } + + + + free(runBuffer); + +return 1; + +} + + + + + + + +/* + + * FUNCTION + + * imMiffWriteRLEIndex8 + + * + + * DESCRIPTION + + * Write the pixels of the vfb using RLE compression. + + */ + +static int /* returns status */ + +#ifdef __STDC__ + +imMiffWriteRLEIndex8( ImVfb* vfb, int fd, FILE* fp, int ioType) + +#else + +imMiffWriteRLEIndex8( vfb, fd, fp, ioType) + +ImVfb* vfb; + +int fd; + +FILE* fp; + +int ioType; + +#endif + +{ + + int runLength, i,x,y; /* indexes, dimensions */ + + ImVfbPtr thisPixel; /* this pixel */ + + ImVfbPtr lastPixel; /* last pixel */ + + unsigned char dumpBuf[10]; /* Buffer for output */ + + + + x = ImVfbQWidth(vfb); + + y = ImVfbQHeight(vfb); + + + + runLength = 0; + + + + lastPixel = ImVfbQPtr( vfb, 0, 0); + + thisPixel = ImVfbQPtr( vfb, 0, 0); + + for (i=1; i 0xff) + + { + + runLength--; + + /* Dump run */ + + imMiffDumpRun1(ImVfbQIndex(vfb,lastPixel), + + runLength); + + runLength = 0; + + } + + } + + else /* new run. Dump the last one. */ + + { + + imMiffDumpRun1(ImVfbQIndex(vfb,lastPixel), + + runLength); + + runLength = 0; + + } + + lastPixel = thisPixel; + + } + + + + /* Dump the last run */ + +imMiffDumpRun1(ImVfbQIndex8(vfb,lastPixel), runLength); + + + + return 1; + +} + + + + + + + +/* + + * FUNCTION + + * imMiffWriteRLEIndex16 + + * + + * DESCRIPTION + + * Write the pixels of the vfb using RLE compression. + + */ + +static int /* returns status */ + +#ifdef __STDC__ + +imMiffWriteRLEIndex16( ImVfb* vfb, int fd, FILE* fp, int ioType) + +#else + +imMiffWriteRLEIndex16( vfb, fd, fp, ioType) + +ImVfb* vfb; + +int fd; + +FILE* fp; + +int ioType; + +#endif + +{ + + int runLength, i,x,y; /* indexes, dimensions */ + + ImVfbPtr thisPixel; /* this pixel */ + + ImVfbPtr lastPixel; /* last pixel */ + + unsigned char dumpBuf[10]; /* Buffer for output */ + + + + x = ImVfbQWidth(vfb); + + y = ImVfbQHeight(vfb); + + + + runLength = 0; + + + + lastPixel = ImVfbQPtr( vfb, 0, 0); + + thisPixel = ImVfbQPtr( vfb, 0, 0); + + for (i=1; i 0xff) + + { + + runLength--; + + /* Dump run */ + + imMiffDumpRun2(ImVfbQIndex16(vfb,lastPixel), + + runLength); + + runLength = 0; + + } + + } + + else /* new run. Dump the last one. */ + + { + + imMiffDumpRun2(ImVfbQIndex16(vfb,lastPixel), + + runLength); + + runLength = 0; + + } + + lastPixel = thisPixel; + + } + + + + /* Dump the last run */ + +imMiffDumpRun2(ImVfbQIndex16(vfb,lastPixel), runLength); + + + + return 1; + +} + + + + + + + +/* + + * FUNCTION + + * strToLower + + * DESCRIPTION + + * convert a string into lower case + + */ + + + +static char* /* returns the string */ + +#ifdef __STDC__ + +strToLower(char* str) + +#else + +strToLower(str) + +char* str; + +#endif + +{ + + char* ptr; + + if (str==NULL) + + return NULL; + + ptr = str; + + while (*ptr!='\0') + + { + + if (( *ptr >= 'A') && (*ptr <= 'Z')) + + *ptr -= ('A' - 'a'); + + ptr++; + + } + + + + return str; + +} + + + diff --git a/utils/roq2/libim/immpnt.c b/utils/roq2/libim/immpnt.c new file mode 100644 index 0000000..753c220 --- /dev/null +++ b/utils/roq2/libim/immpnt.c @@ -0,0 +1,948 @@ +/** + + ** $Header: /roq/libim/immpnt.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/immpnt.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** immpnt.c - Apple Macintosh MacPaint image file I/O + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** immpnt.c contains routines to read and write Apple Macintosh + + ** MacPaint files for the image manipulation library. Raster data + + ** read in is stored in a VFB. Raster data written out is taken + + ** from a tag table. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** none + + ** + + ** PRIVATE CONTENTS + + ** imMpntRead f read an Apple Macintosh MacPaint file + + ** imMpntWrite f write an Apple Macintosh MacPaint file + + ** + + ** IMtstBit m Test an unsigned bit value + + ** IMsetBit m Set an unsigned bit value + + ** + + ** HISTORY + + ** $Log: /roq/libim/immpnt.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.17 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.16 1995/06/15 20:32:31 bduggan + + ** Removed anything printing to stderr. (i.e. debug messages) + + ** + + ** Revision 1.15 1995/06/14 18:58:32 bduggan + + ** Added some casts, took out some useless variables. + + ** + + ** Revision 1.14 1995/04/03 21:30:18 bduggan + + ** took out #ifdef NEWMAGIC + + ** + + ** Revision 1.13 1995/01/10 23:33:45 bduggan + + ** put in IMMULTI, IMPIPE instead of TRUE/FALSE + + ** uncapitlized i's in local functions + + ** made read/write routines static + + ** + + ** Revision 1.12 94/10/03 11:30:18 nadeau + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Rearranged magic number structures for format handlers. + + ** Made format handler routines static (i.e., local to file). + + ** Updated comments, adding format descriptions and references. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.11 92/12/03 01:50:02 nadeau + + ** Corrected info messages. + + ** + + ** Revision 1.10 92/11/23 18:42:39 nadeau + + ** Removed use of IMINFOMSG. + + ** + + ** Revision 1.9 92/11/04 12:04:02 groening + + ** put ImFIleFormat info and magic number info + + ** from imfmt.c into this file. + + ** + + ** Revision 1.8 92/09/29 17:57:58 vle + + ** Added ImInfo messages. + + ** + + ** Revision 1.7 92/08/31 17:28:24 vle + + ** Updated copyright notice. + + ** + + ** Revision 1.6 91/10/03 09:14:30 nadeau + + ** Fixed #includes. + + ** + + ** Revision 1.5 91/03/12 17:56:23 nadeau + + ** Fixed bug with writing incomming VFBs that are smaller than + + ** mpnt standard size. The right side of such images were comming + + ** out black, when they should have been white. + + ** + + ** Revision 1.4 91/02/18 16:24:34 moreland + + ** fixed ! + + ** + + ** Revision 1.3 91/02/12 11:34:38 nadeau + + ** Removed the tag table checking and VFB conversion now + + ** handled by ImFileRead and ImFileWrite. Removed the + + ** IMtstBit and IMsetBit functions and turned them into macros. + + ** Issued warning messages in write when the incomming VFB + + ** is bigger than MacPaint will allow an image to be. + + ** Optimized write. Changed GRAY VFB references to MONO. + + ** + + ** Revision 1.2 91/01/28 10:49:43 moreland + + ** Worked on Write routine. + + ** + + ** Revision 1.1 91/01/10 16:44:45 soraya + + ** Initial revision + + **/ + + + +#include "iminternal.h" + + + + + + + +/** + + ** FORMAT + + ** MacPaint - Apple Macintosh MacPaint image file + + ** + + ** AKA + + ** mpnt, macp, pntg + + ** + + ** FORMAT REFERENCES + + ** - Bit-Mapped Graphics, Steve Rimmer + + ** - Graphics File Formats, David C. Kay, John R. Levine + + ** + + ** CODE CREDITS + + ** Custom development, Soraya Gonzalez, San Diego Supercomputer Center, 1991. + + ** + + ** DESCRIPTION + + ** Apple Macintosh MacPaint image files start with a 512 byte header + + ** which is typically all 0 (zero) values, and is usually ignored. + + ** Following the 512 byte header is 720 packed scan lines. + + ** macpack.c has information about how the lines are packed. + + **/ + + + +/* + + * MPNT - Apple Macintosh MacPaint + + * For information on these structures, how to use them, etc. please + + * see imfmt.c. + + */ + +#ifdef __STDC__ + +static int imMpntRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imMpntWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +#else + +static int imMpntRead( ); + +static int imMpntWrite( ); + +#endif + +static char *imMpntNames[ ] = { "mpnt", "macp", "pntg", NULL }; + +static ImFileFormatReadMap imMpntReadMap[ ] = + +{ + + /* in out */ + + /* type,ch,dep, attr. VFB type attr. */ + + { IN,1,1, PB, IMVFBMONO, 0 }, + + { -1, 0, -1, 0 }, + +}; + +static ImFileFormatWriteMap imMpntWriteMap[ ] = + +{ + + /* in out */ + + /* VFB type, attr., type,ch,dep, attr., func */ + + { IMVFBMONO, 0, IN,1,1, PB, imMpntWrite }, + + { -1, 0, -1, 0, NULL }, + +}; + + + +static ImFileMagic imFileMpntMagic []= + +{ + + { 0, 0, NULL }, + +}; + + + +ImFileFormat ImFileMpntFormat = + +{ + + imMpntNames, "Apple Macintosh MacPaint file", + + "Apple Computer, Inc.", + + "1-bit Packbits-compressed MacPaint images.", + + "1-bit Packbits-compressed MacPaint images.", + + imFileMpntMagic, + + IMNOMULTI, IMPIPE, + + IMNOMULTI, IMPIPE, + + imMpntRead, imMpntReadMap, imMpntWriteMap + +}; + + + +/* + + * Macpaint defines + + */ + +#define IMMAC_HEADER_SIZE 512 + +#define IMMAC_MAX_UNPACKED_SIZE 51840 + +#define IMMAC_IMAGE_HEIGHT 720 + +#define IMMAC_IMAGE_WIDTH 576 + +#define IMMAC_ROW_BYTES ( IMMAC_IMAGE_WIDTH/8) + +#define IMMAC_BLACK 0 + +#define IMMAC_WHITE 1 + + + + + +/* + + * MACROS + + * IMtstBit - Test a bit value + + * IMsetBit - Set a bit value + + * + + * DESCRIPTION + + * IMtstBit: return the on/off setting of the selected bit. + + * IMsetBit: if val == 0, turn the bit off, otherwise on. + + */ + + + +#define IMtstBit(byte,bit) ((1<<(bit))&(byte)) + +#define IMsetBit(pByte,bit) (*(pByte) |= (1<<(bit))) + +#define IMunsetBit(pByte,bit) (*(pByte) &= ~(1<<(bit))) + + + + + +/* + + * FUNCTION + + * imMpntRead - read an Apple Macintosh MacPaint file + + * + + * DESCRIPTION + + * A Macpaint header and image are read in. The image is unpacked + + * (Mac packbits compression) and copied into a monochrome VFB. + + */ + + + +static int /* Returns # tags read in */ + +#ifdef __STDC__ + +imMpntRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imMpntRead( ioType, fd, fp, flagsTable, tagTable ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE * fp; /* Input file pointer */ + + TagTable * flagsTable; /* Flags */ + + TagTable * tagTable; /* Tag table to add to */ + +#endif + +{ + + ImVfb * vfb; /* Read in image */ + + ImVfbPtr pPixel; /* Pixel pointer */ + + unsigned char packed[ IMMAC_MAX_UNPACKED_SIZE ];/* Packed bits buffer*/ + + unsigned char unpacked[ IMMAC_IMAGE_WIDTH ];/* Unpacked bits buffer */ + + unsigned char *pPtr, *uPtr; /* Bits buffer pointers */ + + int sLine; /* Current scanline */ + + int col; /* Current column */ + + int byte; /* Byte run counter */ + + unsigned int bit; /* Which bit in a byte */ + + unsigned long byteCount; /* # of bytes read */ + + unsigned int count, oldcount;/* Run counters */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * Read (and ignore) the 512 byte header + + */ + + BinByteOrder( BINMBF ); + + if ( ImBinRead( ioType, fd, fp, packed, UCHAR, 1, IMMAC_HEADER_SIZE ) == -1 ) + + { + + ImReturnBinError(); + + } + + + + + + /* + + * Allocate a Monochrome VFB of the required size. + + */ + + if ( (vfb = ImVfbAlloc( IMMAC_IMAGE_WIDTH, IMMAC_IMAGE_HEIGHT, IMVFBMONO )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError(), -1, ImErrNo ); + + } + + + + /* + + * Read the whole compressed image into a buffer. + + */ + + if ( (byteCount = ImBinRead( ioType, fd, fp, packed, UCHAR, 1, IMMAC_MAX_UNPACKED_SIZE )) == (unsigned long)-1 ) + + { + + ImReturnBinError(); + + } + + + + /* + + * Copy to the VFB, one scanline at a time. + + */ + + uPtr = unpacked; + + pPtr = packed; + + sLine = 0; + + col = 0; + + pPixel = ImVfbQFirst( vfb ); + + while ( sLine< IMMAC_IMAGE_HEIGHT ) + + { + + uPtr = unpacked; + + if ( pPtr[0] > 127 ) + + /* Unpack next 2 bytes of compressed data */ + + count = 2; + + else + + /* Unpack next ? bytes of non-compressed data */ + + count = pPtr[0] + 2; + + oldcount = count; /* remember BYTE count */ + + UnpackBits( pPtr, uPtr, &count ); + + pPtr += oldcount; /* Inc packedPtr by BYTE count*/ + + + + for ( byte=0; byte= IMMAC_IMAGE_WIDTH ) + + { + + sLine++; + + col = 0; + + } + + } + + } + + + + } + + + + TagTableAppend( tagTable, TagEntryAlloc( "image vfb", POINTER, &vfb ) ); + + + + /* + + * Output -verbose message + + */ + + ImInfo( "Byte Order", "Most Significant Byte First" ); + + + + sprintf( message, "%d x %d", IMMAC_IMAGE_WIDTH, IMMAC_IMAGE_HEIGHT ); + + ImInfo( "Resolution", message ); + + + + ImInfo( "Type", "1-bit Monochrome" ); + + ImInfo( "Compression Type", "Apple Macintosh Packbits" ); + + + + return ( 1 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imMpntWrite - write an Apple Macintosh MacPaint file + + * + + * DESCRIPTION + + * Write a monochrome MacPaint image. + + */ + + + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imMpntWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imMpntWrite( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE * fp; /* Input file pointer */ + + TagTable * flagsTable; /* Flags */ + + TagTable * tagTable; /* Tag table to read from */ + +#endif + +{ + + ImVfb *vfb; /* Image to output */ + + ImVfbPtr pPixel; /* Pixel pointer */ + + char * buf[ IMMAC_HEADER_SIZE ]; /* MacPaint file header */ + + unsigned char packed[ IMMAC_MAX_UNPACKED_SIZE ]; + + unsigned char unpacked[ IMMAC_IMAGE_WIDTH ]; + + unsigned char *pPtr; + + unsigned short sLine, bit, bitVal; + + unsigned long byteCount; + + unsigned int count, oldcount; + + unsigned short col; + + int n; + + int vfbHeight, vfbWidth; + + int mono_threshold; + + char message[100]; /* ImInfo message */ + + + + + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + vfbHeight = ImVfbQHeight( vfb ); + + vfbWidth = ImVfbQWidth( vfb ); + + + + BinByteOrder( BINMBF ); + + + + /* + + * Write out the 512 byte (blank) header. + + */ + + memset( buf, 0x00, IMMAC_HEADER_SIZE ); + + if ( ImBinWrite( ioType, fd, fp, buf, UCHAR, 1, IMMAC_HEADER_SIZE ) == -1 ) + + { + + ImReturnBinError(); + + } + + + + /* + + * Output -verbose message + + */ + + ImInfo( "Byte Order", "Most Significant Byte First" ); + + + + sprintf( message, "%d x %d", IMMAC_IMAGE_WIDTH, IMMAC_IMAGE_HEIGHT ); + + ImInfo( "Resolution", message ); + + + + ImInfo( "Type", "1-bit Monochrome" ); + + ImInfo( "Compression Type", "Apple Macintosh Packbits" ); + + + + /* + + * Write the VFB out one scanline at a time. If the image is taller + + * than a MacPaint file allows, clip off the bottom. If smaller, + + * pad with zeroes. + + */ + + if ( vfbWidth > IMMAC_IMAGE_WIDTH ) + + { + + ImErrorWarning( "Image too wide for MacPaint format. Image has been clipped.", -1, IMEWIDTH ); + + vfbWidth = IMMAC_IMAGE_WIDTH; + + } + + if ( vfbHeight > IMMAC_IMAGE_HEIGHT ) + + { + + ImErrorWarning( "Image too tall for MacPaint format. Image has been clipped.", -1, IMEHEIGHT ); + + vfbHeight = IMMAC_IMAGE_HEIGHT; + + } + + for ( sLine=0; sLine + +#include "iminternal.h" + + + +/** + + ** FORMAT + + ** PBM - Jef Poskanzer's PBM file formats + + ** + + ** AKA + + ** rpbm,pgm,rpgm,ppm,rppm + + ** + + ** FORMAT REFERENCES + + ** Graphics File Formats, David C. Kay, John R. Levine + + ** + + ** CODE CREDITS + + ** Custom development, Donald Doering, San Diego Supercomputer Center, 1991. + + ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1991. + + ** + + ** DESCRIPTION + + ** The PBM suite really defines six (6) file formats: + + ** + + ** PBM ASCII Monochrome bitmap + + ** PGM ASCII Grayscale pixel map + + ** PPM ASCII Color pixel map + + ** + + ** RPBM Raw Monochrome bitmap + + ** RPGM Raw Grayscale pixel map + + ** RPPM Raw Color pixel map + + ** + + ** The ASCII formats store the image as ASCII, human-readable files, + + ** while the raw formats store them as raw binary files. Since the + + ** binary files are still only byte-based (8-bit grayscale, 8-bit per + + ** channel RGB), they are still portable (but a lot smaller and faster). + + ** + + ** All six formats start with a file header of the form: + + ** + + ** unsigned char magic1; + + ** unsigned char magic2; + + ** unsigned int width, height; + + ** + + ** magic1 is always a 'P'. + + ** + + ** magic2 selects which of the six formats: + + ** + + ** '1' PBM + + ** '2' PGM + + ** '3' PPM + + ** '4' RPBM + + ** '5' RPGM + + ** '6' RPPM + + ** + + ** width and height are the width and height of the image in pixels. + + ** + + ** For formats other than PBM and RPBM, the header also contains: + + ** + + ** unsigned int maxIntensity; + + ** + + ** maxIntensity is the highest value allowed for one channel of the + + ** image. For instance, for 8-bit grayscale, maxIntensity is 255. + + ** For 24-bit RGB images, maxIntensity is also 255 (8-bit channels). + + ** + + ** When reading images, incomming pixel values are scaled from the + + ** range 0-maxIntensity, up or down to the VFB 8-bit range 0-255. + + ** + + ** For monochrome PBM and RPBM files, 0 is white and 1 is black. This + + ** is the opposite of the image library's conventions. However, PGM, + + ** RPGM, PPM, and RPPM files all follow the convention that 0 is black, + + ** and 1 is brighter than black (255 is full intensity). + + ** + + ** NOTE: the header is always ASCII, including maxIntensity. + + ** + + ** ASCII FORMATS + + ** For the three ASCII formats, the rest of the file is filled with + + ** ASCII integer values separated by white space. Carriage returns, + + ** form feeds, spaces, and tabs are ignored. Comments starting with + + ** a '#' and extending to the end of the line are ignored. + + ** + + ** When we write ASCII formats, to make the format more readable, + + ** carriage returns are inserted after every 70 characters or so. + + ** + + ** BINARY FORMATS + + ** For the three "raw" binary formats, the rest of the file is filled + + ** with 8-bit values. + + **/ + + + +/* + + * PBM - ASCII bitmap (monochrome) + + * PGM - ASCII gray (grayscale) + + * PNM - any ASCII format + + * PPM - ASCII pixel (RGB color) + + * RPBM - Raw bitmap (monochrome) + + * RPGM - Raw gray (grayscale) + + * RPNM - any Raw format + + * RPPM - Raw pixel (RGB color) + + * + + * All 8 formats share the same read call. Some share the same write calls. + + */ + +#ifdef __STDC__ + +static int imPbmRead(int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable); + +static int imPbmWrite1( ImFileFormatWriteMap * pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imPbmWrite8( ImFileFormatWriteMap * pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imPbmWriteRGB( ImFileFormatWriteMap * pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imRpbmWrite1( ImFileFormatWriteMap * pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imRpbmWrite8( ImFileFormatWriteMap * pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imRpbmWriteRGB( ImFileFormatWriteMap * pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +#else + +static int imPbmRead( ); + +static int imPbmWrite1( ), imPbmWrite8( ), imPbmWriteRGB( ); + +static int imRpbmWrite1( ), imRpbmWrite8( ), imRpbmWriteRGB( ); + +#endif + + + + + +/* + + * Magic constants + + */ + +#define IMPBM_MAGIC1 'P' + +#define IMPBM_MAGIC2 '1' + +#define IMPGM_MAGIC2 '2' + +#define IMPPM_MAGIC2 '3' + +#define IMRPBM_MAGIC2 '4' + +#define IMRPGM_MAGIC2 '5' + +#define IMRPPM_MAGIC2 '6' + + + + + +static char *imPbmNames[ ] = { "pbm", "rpbm", "pgm", "rpgm", "pnm", "rpnm", "ppm", "rppm", NULL }; + + + +static unsigned char imPbmMagicNumber[ ] = { IMPBM_MAGIC1, IMPBM_MAGIC2 }; + +static unsigned char imRpbmMagicNumber[ ] = { IMPBM_MAGIC1, IMRPBM_MAGIC2 }; + +static unsigned char imPgmMagicNumber[ ] = { IMPBM_MAGIC1, IMPGM_MAGIC2 }; + +static unsigned char imRpgmMagicNumber[ ] = { IMPBM_MAGIC1, IMRPGM_MAGIC2 }; + +static unsigned char imPpmMagicNumber[ ] = { IMPBM_MAGIC1, IMPPM_MAGIC2 }; + +static unsigned char imRppmMagicNumber[ ] = { IMPBM_MAGIC1, IMRPPM_MAGIC2 }; + + + + + +static ImFileFormatReadMap imPbmReadMap[ ] = + +{ + + /* in out */ + + /* type,ch,dep, attr. VFB type attr. */ + + { IN,1,1, 0, IMVFBMONO, 0 }, + + { IN,1,8, 0, IMVFBINDEX8, 0 }, + + { RGB,3,8, 0, IMVFBRGB, 0 }, + + { IN,1,1, ASC, IMVFBMONO, 0 }, + + { IN,1,8, ASC, IMVFBINDEX8, 0 }, + + { RGB,3,8, ASC, IMVFBRGB, 0 }, + + { -1, 0, -1, 0 }, + +}; + + + + + +static ImFileFormatWriteMap imPbmWriteMap[ ] = + +{ + + /* in out */ + + /* VFB type, attr., type,ch,dep, attr., func */ + + { IMVFBMONO, 0, IN,1,1, ASC, imPbmWrite1 }, + + { IMVFBMONO, 0, IN,1,1, 0, imRpbmWrite1 }, + + { IMVFBINDEX8, 0, IN,1,8, ASC, imPbmWrite8 }, + + { IMVFBINDEX8, 0, IN,1,8, 0, imRpbmWrite8 }, + + { IMVFBRGB, 0, RGB,3,8, ASC, imPbmWriteRGB }, + + { IMVFBRGB, 0, RGB,3,8, 0, imRpbmWriteRGB }, + + { -1, 0, -1, 0, NULL }, + +}; + + + + + + + +static ImFileMagic imFilePbmMagic []= + +{ + + {0, 2, imPbmMagicNumber }, + + {0, 2, imRpbmMagicNumber }, + + {0, 2, imPgmMagicNumber }, + + {0, 2, imRpgmMagicNumber }, + + {0, 2, imPpmMagicNumber }, + + {0, 2, imRppmMagicNumber }, + + { 0, 0, NULL }, + +}; + + + +ImFileFormat ImFilePbmFormat = + +{ + + imPbmNames, "PBM Portable Bit Map file", + + "Jef Poskanzer", + + "1-bit monochrome, 8-bit grayscale, 24-bit rgb, ascii encoded or binary", + + "1-bit monochrome PBM images.", + + imFilePbmMagic, + + IMNOMULTI, IMPIPE, + + IMNOMULTI, IMPIPE, + + imPbmRead, imPbmReadMap, imPbmWriteMap + +}; + + + + + + + +#ifdef __STDC__ + +static int skipEOL( int ioTyne, int fd, FILE *fp ); + +static int readAsciiInt( int ioType, int fd, FILE *fp, int *value ); + +#else + +static int skipEOL( ); + +static int readAsciiInt(); + +#endif + + + + + +#define IMWrite(ioType,fd,fp,buffer,size) \ + ((ioType&IMFILEIOFD) ? \ + write( fd, buffer, size ) \ + : \ + fwrite( buffer, 1, size, fp )) + + + + +/* + + * FUNCTION + + * imPbmRead - read a PBM file + + * + + * DESCRIPTION + + * The header is read in, followed by the image pixels in either + + * ASCII or raw format. + + */ + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imPbmRead(int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable) + +#else + +imPbmRead(ioType, fd, fp, flagsTable, tagTable) + + int ioType; /* I/O type */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file buffer pointer */ + + TagTable *flagsTable; /* Input parameter/flags */ + + TagTable *tagTable; /* Tag table to add to */ + +#endif + +{ + + ImVfbPtr pPixel; /* VFB pixel pointer */ + + unsigned char *pBuffer; /* Buffer pointer */ + + int bitShift; /* Shift amount for expanding Mono*/ + + int width, height; /* Image size */ + + int fields; /* VFB fields */ + + int maxIntensity; /* Maximum channel intensity */ + + float colorScale; /* Intensity scaling */ + + unsigned char magic[2]; /* Magic number */ + + unsigned char byte; /* Byte holder */ + + int hue; /* Incomming channel value */ + + ImVfb *vfb; /* New VFB */ + + unsigned char *buffer; /* Incomming byte buffer */ + + int x, y; /* VFB location */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * Get the magic number + + */ + + BinByteOrder( BINMBF ); + + ImInfo ("Byte Order","Most Significant Byte First"); + + if ( ImBinRead( ioType, fd, fp, magic, UCHAR, 1, 2 ) == -1 ) + + ImReturnBinError( ); + + if ( magic[0] != IMPBM_MAGIC1 ) + + { + + ImErrNo = IMEMAGIC; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + switch ( magic[1] ) + + { + + case IMPBM_MAGIC2: + + case IMRPBM_MAGIC2: + + fields = IMVFBMONO; + + break; + + + + case IMPGM_MAGIC2: + + case IMRPGM_MAGIC2: + + fields = IMVFBINDEX8; + + break; + + + + case IMPPM_MAGIC2: + + case IMRPPM_MAGIC2: + + fields = IMVFBRGB; + + break; + + + + default: + + ImErrNo = IMEMAGIC; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + + + /* + + * Read image size. + + */ + + if ( readAsciiInt( ioType, fd, fp, &width ) == -1) + + return ( -1 ); /* Error already handled */ + + if ( readAsciiInt( ioType, fd, fp, &height ) == -1) + + return ( -1 ); /* Error already handled */ + + + + + + sprintf (message, "%d x %d",width, height); + + ImInfo ("Resolution",message); + + switch ( magic[1] ) + + { + + case IMPBM_MAGIC2: + + ImInfo ("Type","1-bit Monochrome"); + + ImInfo ("Compression", "Ascii"); + + break; + + case IMRPBM_MAGIC2: + + ImInfo ("Type","1-bit Monochrome"); + + ImInfo ("Compression", "none"); + + break; + + + + case IMPGM_MAGIC2: + + ImInfo ("Type","8-bit Grayscale"); + + ImInfo ("Compression", "Ascii"); + + break; + + case IMRPGM_MAGIC2: + + ImInfo ("Type","8-bit Grayscale"); + + ImInfo ("Compression", "none"); + + break; + + + + case IMPPM_MAGIC2: + + ImInfo ("Type","24-bit RGB"); + + ImInfo ("Compression", "Ascii"); + + break; + + case IMRPPM_MAGIC2: + + ImInfo ("Type","24-bit RGB"); + + ImInfo ("Compression", "none"); + + break; + + } + + + + /* + + * Except for PBM and RPBM formats, read in the maximum channel + + * value 'maxIntensity'. + + * + + * Compute how to scale color values from their incomming value + + * range of 0-maxIntensity, to our range of 0-255 (8-bit indexes + + * and RGB components). + + */ + + maxIntensity = 0; + + if ( magic[1] != IMPBM_MAGIC2 && magic[1] != IMRPBM_MAGIC2 ) + + { + + if ( readAsciiInt( ioType, fd, fp, &maxIntensity ) == -1 ) + + return ( -1 ); /* Error already handled */ + + colorScale = 255.0 / (float) maxIntensity; + + } + + skipEOL( ioType, fd, fp ); + + + + + + /* + + * Allocate a VFB of the required size. + + */ + + if ((vfb = ImVfbAlloc( width, height, fields )) == IMVFBNULL) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + + + /* + + * Read in the image, depending upon how it is stored. + + */ + + switch ( magic[1] ) + + { + + case IMPPM_MAGIC2: /* ASCII Color */ + + pPixel = ImVfbQFirst( vfb ); + + for (y = 0; y < height; y++) + + { + + for ( x = 0; x < width; x++) + + { + + if ( readAsciiInt( ioType, fd, fp, &hue ) == -1) + + return (-1); + + ImVfbSRed( vfb, pPixel, colorScale * hue ); + + + + if ( readAsciiInt( ioType, fd, fp, &hue ) == -1) + + return (-1); + + ImVfbSGreen( vfb, pPixel, colorScale * hue ); + + + + if ( readAsciiInt( ioType, fd, fp, &hue ) == -1) + + return (-1); + + ImVfbSBlue( vfb, pPixel, colorScale * hue ); + + + + ImVfbSInc( vfb, pPixel ); + + } + + } + + break; + + + + case IMRPPM_MAGIC2: /* Raw Color */ + + /* Skip carriage return. */ + + ImMalloc( buffer, unsigned char *, width * 3 ); + + pPixel = ImVfbQFirst( vfb ); + + for (y = 0; y < height; y++) + + { + + if ( ImBinRead( ioType, fd, fp, buffer, UCHAR, 1, width * 3 ) == -1 ) + + { + + free( (char *)buffer ); + + ImReturnBinError( ); + + } + + pBuffer = buffer; + + for ( x = 0; x < width; x++ ) + + { + + ImVfbSRed( vfb, pPixel, colorScale * *pBuffer++ ); + + ImVfbSGreen( vfb, pPixel, colorScale * *pBuffer++ ); + + ImVfbSBlue( vfb, pPixel, colorScale * *pBuffer++ ); + + ImVfbSInc( vfb, pPixel ); + + } + + } + + free( (char *) buffer ); + + break; + + + + case IMPGM_MAGIC2: /* ASCII grayscale */ + + pPixel = ImVfbQFirst( vfb ); + + for (y = 0; y < height; y++) + + { + + for (x = 0; x < width; x++ ) + + { + + if ( readAsciiInt( ioType, fd, fp, &hue ) == -1) + + return (-1); + + ImVfbSIndex8( vfb, pPixel, colorScale * hue ); + + ImVfbSInc( vfb, pPixel ); + + } + + } + + break; + + + + case IMRPGM_MAGIC2: /* Raw grayscale */ + + /* Skip carriage return. */ + + ImMalloc( buffer, unsigned char *, width ); + + pPixel = ImVfbQFirst( vfb ); + + for (y = 0; y < height; y++) + + { + + if ( ImBinRead( ioType, fd, fp, buffer, UCHAR, 1, width ) == -1 ) + + { + + free( (char *)buffer ); + + ImReturnBinError( ); + + } + + pBuffer = buffer; + + for ( x = 0; x < width; x++ ) + + { + + ImVfbSIndex8( vfb, pPixel, colorScale * *pBuffer++ ); + + ImVfbSInc( vfb, pPixel ); + + } + + } + + free( (char *) buffer ); + + break; + + + + case IMPBM_MAGIC2: /* ASCII bitmap (mono) */ + + pPixel = ImVfbQFirst( vfb ); + + for (y = 0; y < height; y++) + + { + + for (x = 0; x < width; x++ ) + + { + + if ( readAsciiInt( ioType, fd, fp, &hue ) == -1) + + return (-1); + + /* + + * hue = 0 (white) maps to VFB 1 (white). + + * hue = 1 (black) maps to VFB 0 (black). + + */ + + ImVfbSMono( vfb, pPixel, (~hue) & 0x1 ); + + ImVfbSInc( vfb, pPixel ); + + } + + } + + break; + + + + case IMRPBM_MAGIC2: /* Raw bitmap (mono) */ + + /* Skip carriage return. */ + + ImMalloc( buffer, unsigned char *, (width + 7) / 8 ); + + pPixel = ImVfbQFirst( vfb ); + + for (y = 0; y < height; y++) + + { + + bitShift = -1; + + if ( ImBinRead( ioType, fd, fp, buffer, UCHAR, 1, (width + 7) / 8 ) == -1 ) + + { + + free( (char *)buffer ); + + ImReturnBinError( ); + + } + + pBuffer = buffer; + + for ( x = 0; x < width; x++ ) + + { + + if (bitShift == -1) + + { + + byte = ~(*pBuffer++); + + bitShift = 7; + + } + + /* + + * hue = 0 (white) maps to VFB 1 (white). + + * hue = 1 (black) maps to VFB 0 (black). + + */ + + ImVfbSMono( vfb, pPixel, + + !((byte >> bitShift--)& 0x1) ); + + ImVfbSInc( vfb, pPixel ); + + } + + } + + free( (char *) buffer ); + + break; + + } + + + + + + /* + + * Add the VFB to the tagTable. + + */ + + TagTableAppend( tagTable, TagEntryAlloc( "image vfb", POINTER, &vfb ) ); + + + + return ( 1 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * skipEOL - skip to end of line + + * readAsciiInt - read in an ASCII string integer + + * + + * DESCRIPTION + + * White space and comments are skipped up to the first digit of a number. + + * The number is read in and returned. If a read problem occurs, a -1 + + * is returned as the function value. Otherwise 0 is returned. + + */ + + + +static int /* Returns status */ + +#ifdef __STDC__ + +skipEOL( int ioType, int fd, FILE *fp ) + +#else + +skipEOL( ioType, fd, fp ) + + int ioType; /* I/O type */ + + int fd; /* File descriptor to use */ + + FILE *fp; /* File pointer to use */ + +#endif + +{ + + int ich; /* Input character (as integer) */ + + unsigned char ch; /* Input character */ + + + + ch = '\0'; + + if ( ioType & IMFILEIOFD ) + + { + + while ( ch != '\n' ) + + { + + if ( read( fd, &ch, 1 ) <= 0 ) + + { + + ImErrNo = IMESYNTAX; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + } + + return ( 0 ); + + } + + + + while ( (ich = getc( fp )) != '\n' && ich != EOF ) + + ; + + if ( ich == EOF ) + + { + + ImErrNo = IMESYNTAX; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + return ( 0 ); + +} + + + +static int /* Returns status */ + +#ifdef __STDC__ + +readAsciiInt( int ioType, int fd, FILE *fp, int *value ) + +#else + +readAsciiInt( ioType, fd, fp, value ) + + int ioType; /* I/O type */ + + int fd; /* File descriptor to use */ + + FILE *fp; /* File pointer to use */ + + int *value; /* Returned integer value */ + +#endif + +{ + + int ich; /* Input character (as integer) */ + + unsigned char ch; /* Input character */ + + + + if ( ioType & IMFILEIOFD ) + + { + + /* Skip white space and comments that start with a '#' */ + + do + + { + + if ( read( fd, &ch, 1 ) <= 0 ) + + { + + ImErrNo = IMESYNTAX; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + if ( ch == '#' ) + + { + + if ( skipEOL( ioType, fd, fp ) == -1 ) + + return ( -1 ); + + ch = '\n'; + + } + + } while ( isspace( ch ) ); + + + + /* Read in and assemble an integer. */ + + *value = 0; + + while ( isdigit( ch ) ) + + { + + *value = *value * 10 + (ch - '0'); + + if ( read( fd, &ch, 1 ) <= 0 ) + + { + + ImErrNo = IMESYNTAX; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + } + + return ( 0 ); + + } + + + + /* + + * Skip white space and comments starting with '#'. Read in + + * a single integer. + + */ + + while ( (ich = getc( fp )) == '#' || isspace( ich ) ) + + { + + if ( ich == '#' ) + + if ( skipEOL( ioType, fd, fp ) == -1 ) + + return ( -1 ); + + } + + if ( ich == EOF ) + + { + + ImErrNo = IMESYNTAX; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + ungetc( ich, fp ); + + if ( fscanf( fp, "%d", value ) != 1 ) + + { + + ImErrNo = IMESYNTAX; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + return ( 0 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imPbmWrite1 - write a 1-bit ASCII PBM file + + * imPbmWrite8 - write an 8-bit ASCII PGM file + + * imPbmWriteRGB - write an RGB ASCII PPM file + + * + + * DESCRIPTION + + * 1-bit: The image is written out in ASCII as a series of 1-bit values + + * each in the form: "i ". To make the PBM file more readable, + + * carriage returns are added every 70 characters or so. + + * + + * 8-bit: The image is written out in ASCII as a series of 8-bit index + + * values, each in the form: "iii ". To make the PGM file more readable, + + * carriage returns are added every 70 characters or so. + + * + + * RGB: The image is written out in ASCII as a series of RGB values, each + + * in the form: "rrr ggg bbb ". To make the PPM file more readable, + + * carriage returns are added every 70 characters or so (after the nearest + + * complete tripplet). + + */ + + + +static int + +#ifdef __STDC__ + +imPbmWrite1(ImFileFormatWriteMap * pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imPbmWrite1( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O type flag */ + + int fd; /* Unbuffered file descriptor */ + + FILE *fp; /* Buffered file pointer */ + + TagTable *flagsTable; /* Max color value flag */ + + TagTable *tagTable; /* Tag list to read from */ + +#endif + +{ + + int x, y; /* Pixel location counters */ + + ImVfbPtr pPixel; /* Current pixel */ + + ImVfb *vfb; /* Image to output */ + + int width; /* Image width */ + + int height; /* Image height */ + + unsigned char buffer[200]; /* Line buffer */ + + unsigned char *pBuffer; /* Current location in buffer */ + + unsigned char *pBufferStop; /* Where to stop & flush in buffer*/ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * Get the VFB and its attributes. Write the PBM header. + + */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + width = ImVfbQWidth( vfb ); + + height = ImVfbQHeight( vfb ); + + sprintf( (char *)buffer, "%c%c\n%d %d\n", IMPBM_MAGIC1, IMPBM_MAGIC2, + + width, height ); + + IMWrite( ioType, fd, fp, buffer, strlen( (char*)buffer ) ); + + + + ImInfo ("Byte Order","Most Significant Byte First"); + + ImInfo ("Type","1-bit Monochrome"); + + sprintf (message, "%d x %d",width, height); + + ImInfo ("Resolution",message); + + ImInfo ("Compression", "Ascii"); + + + + pPixel = ImVfbQFirst( vfb ); + + pBufferStop = buffer + 70; /* Flush soon after 70 chars */ + + pBuffer = buffer; + + + + for ( y = 0; y < height; y++ ) + + { + + for ( x = 0; x < width; x++ ) + + { + + if ( pBuffer >= pBufferStop ) + + { + + *pBuffer = '\n'; + + IMWrite( ioType, fd, fp, buffer,pBuffer-buffer+1); + + pBuffer = buffer; + + } + + sprintf( (char *)pBuffer, "%1d ", + + (~ImVfbQMono( vfb, pPixel )) & 0x1 ); + + pBuffer += 2; + + ImVfbSInc( vfb, pPixel ); + + } + + if ( pBuffer != buffer ) + + { + + *pBuffer = '\n'; + + IMWrite( ioType, fd, fp, buffer,pBuffer-buffer+1); + + pBuffer = buffer; + + } + + } + + return ( 1 ); + +} + + + +static int + +#ifdef __STDC__ + +imPbmWrite8(ImFileFormatWriteMap * pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imPbmWrite8( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O type flag */ + + int fd; /* Unbuffered file descriptor */ + + FILE *fp; /* Buffered file pointer */ + + TagTable *flagsTable; /* Max color value flag */ + + TagTable *tagTable; /* Tag list to read from */ + +#endif + +{ + + int x, y; /* Pixel location counters */ + + ImVfbPtr pPixel; /* Current pixel */ + + ImVfb *vfb; /* Image to output */ + + int width; /* Image width */ + + int height; /* Image height */ + + unsigned char buffer[200]; /* Line buffer */ + + unsigned char *pBuffer; /* Current location in buffer */ + + unsigned char *pBufferStop; /* Where to stop & flush in buffer*/ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * Get the VFB and its attributes. Write the PGM header. + + */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + width = ImVfbQWidth( vfb ); + + height = ImVfbQHeight( vfb ); + + sprintf( (char *)buffer, "%c%c\n%d %d\n255\n", IMPBM_MAGIC1, IMPGM_MAGIC2, + + width, height ); + + IMWrite( ioType, fd, fp, buffer, strlen((char*) buffer ) ); + + + + ImInfo ("Byte Order","Most Significant Byte First"); + + ImInfo ("Type","8-bit Grayscale"); + + sprintf (message, "%d x %d",width, height); + + ImInfo ("Resolution",message); + + ImInfo ("Compression", "Ascii"); + + + + pPixel = ImVfbQFirst( vfb ); + + pBufferStop = buffer + 68; /* Flush soon after 68 chars */ + + pBuffer = buffer; + + + + for ( y = 0; y < height; y++ ) + + { + + for ( x = 0; x < width; x++ ) + + { + + if ( pBuffer >= pBufferStop ) + + { + + *pBuffer = '\n'; + + IMWrite( ioType, fd, fp, buffer,pBuffer-buffer+1); + + pBuffer = buffer; + + } + + sprintf( (char *)pBuffer, "%03d ", + + (ImVfbQIndex8( vfb, pPixel ) & 0xFF) ); + + pBuffer += 4; + + ImVfbSInc( vfb, pPixel ); + + } + + if ( pBuffer != buffer ) + + { + + *pBuffer = '\n'; + + IMWrite( ioType, fd, fp, buffer,pBuffer-buffer+1); + + pBuffer = buffer; + + } + + } + + return ( 1 ); + +} + + + +static int + +#ifdef __STDC__ + +imPbmWriteRGB(ImFileFormatWriteMap * pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imPbmWriteRGB( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O type flag */ + + int fd; /* Unbuffered file descriptor */ + + FILE *fp; /* Buffered file pointer */ + + TagTable *flagsTable; /* Max color value flag */ + + TagTable *tagTable; /* Tag list to read from */ + +#endif + +{ + + int x, y; /* Pixel location counters */ + + ImVfbPtr pPixel; /* Current pixel */ + + ImVfb *vfb; /* Image to output */ + + int width; /* Image width */ + + int height; /* Image height */ + + unsigned char buffer[200]; /* Line buffer */ + + unsigned char *pBuffer; /* Current location in buffer */ + + unsigned char *pBufferStop; /* Where to stop & flush in buffer*/ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * Get the VFB and its attributes. Write the PPM header. + + */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + width = ImVfbQWidth( vfb ); + + height = ImVfbQHeight( vfb ); + + sprintf( (char *)buffer, "%c%c\n%d %d\n255\n", IMPBM_MAGIC1, IMPPM_MAGIC2, + + width, height ); + + IMWrite( ioType, fd, fp, buffer, strlen((char*) buffer ) ); + + + + ImInfo ("Byte Order","Most Significant Byte First"); + + ImInfo ("Type","24-bit RGB"); + + sprintf (message, "%d x %d",width, height); + + ImInfo ("Resolution",message); + + ImInfo ("Compression", "Ascii"); + + + + pPixel = ImVfbQFirst( vfb ); + + pBufferStop = buffer + 65; /* Flush soon after 65 chars */ + + pBuffer = buffer; + + + + for ( y = 0; y < height; y++ ) + + { + + for ( x = 0; x < width; x++ ) + + { + + if ( pBuffer >= pBufferStop ) + + { + + *pBuffer = '\n'; + + IMWrite( ioType, fd, fp, buffer,pBuffer-buffer+1); + + pBuffer = buffer; + + } + + sprintf( (char *)pBuffer, "%03d %03d %03d ", + + (ImVfbQRed( vfb, pPixel ) & 0xFF), + + (ImVfbQGreen( vfb, pPixel ) & 0xFF), + + (ImVfbQBlue( vfb, pPixel ) & 0xFF) ); + + pBuffer += 13; + + ImVfbSInc( vfb, pPixel ); + + } + + if ( pBuffer != buffer ) + + { + + *pBuffer = '\n'; + + IMWrite( ioType, fd, fp, buffer,pBuffer-buffer+1); + + pBuffer = buffer; + + } + + } + + return ( 1 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imRpbmWrite1 - write an 1-bit raw PBM file + + * imRpbmWrite8 - write an 8-bit raw PGM file + + * imRpbmWriteRGB - write an RGB raw PPM file + + * + + * DESCRIPTION + + * 1-bit: The image is written out in binary as a series of 8-bit values, + + * each one constructed from 8 1-bit mono values. + + * + + * 8-bit: The image is written out in binary as a series of 8-bit index + + * values. + + * + + * RGB: The image is written out in binary as a series of RGB values. + + */ + + + +static int + +#ifdef __STDC__ + +imRpbmWrite1(ImFileFormatWriteMap * pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imRpbmWrite1( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O type flag */ + + int fd; /* Unbuffered file descriptor */ + + FILE *fp; /* Buffered file pointer */ + + TagTable *flagsTable; /* Max color value flag */ + + TagTable *tagTable; /* Tag list to read from */ + +#endif + +{ + + int x, y; /* Pixel location counters */ + + ImVfbPtr pPixel; /* Current pixel */ + + ImVfb *vfb; /* Image to output */ + + int width; /* Image width */ + + int height; /* Image height */ + + unsigned char buf[200]; /* Temp buffer */ + + unsigned char *buffer; /* Line buffer */ + + unsigned char *pBuffer; /* Current location in buffer */ + + int shift; /* How far to shift */ + + int byte; /* Byte being built up */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * Get the VFB and its attributes. Write the PBM header. + + */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + width = ImVfbQWidth( vfb ); + + height = ImVfbQHeight( vfb ); + + sprintf( (char *)buf, "%c%c\n%d %d\n", IMPBM_MAGIC1, IMRPBM_MAGIC2, + + width, height ); + + IMWrite( ioType, fd, fp, buf, strlen((char*) buf ) ); + + + + ImInfo ("Byte Order","Most Significant Byte First"); + + ImInfo ("Type","1-bit Monochrome"); + + sprintf (message, "%d x %d",width, height); + + ImInfo ("Resolution",message); + + ImInfo ("Compression", "none"); + + + + ImMalloc( buffer, unsigned char *, (width + 7) / 8 ); + + pPixel = ImVfbQFirst( vfb ); + + for ( y = 0; y < height; y++ ) + + { + + byte = 0; + + shift = 7; + + pBuffer = buffer; + + for ( x = 0; x < width; x++ ) + + { + + byte |= (ImVfbQMono( vfb, pPixel ) & 0x1) << shift--; + + ImVfbSInc( vfb, pPixel ); + + if ( shift == -1 ) + + { + + *pBuffer++ = byte; + + shift = 7; + + byte = 0; + + } + + } + + if ( shift != 7 ) + + *pBuffer++ = byte; + + IMWrite( ioType, fd, fp, buffer, pBuffer - buffer ); + + } + + + + free( (char *)buffer ); + + return ( 1 ); + +} + + + +static int + +#ifdef __STDC__ + +imRpbmWrite8(ImFileFormatWriteMap * pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imRpbmWrite8( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O type flag */ + + int fd; /* Unbuffered file descriptor */ + + FILE *fp; /* Buffered file pointer */ + + TagTable *flagsTable; /* Max color value flag */ + + TagTable *tagTable; /* Tag list to read from */ + +#endif + +{ + + int x, y; /* Pixel location counters */ + + ImVfbPtr pPixel; /* Current pixel */ + + ImVfb *vfb; /* Image to output */ + + int width; /* Image width */ + + int height; /* Image height */ + + unsigned char buf[200]; /* Temp buffer */ + + unsigned char *buffer; /* Line buffer */ + + unsigned char *pBuffer; /* Current location in buffer */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * Get the VFB and its attributes. Write the PBM header. + + */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + width = ImVfbQWidth( vfb ); + + height = ImVfbQHeight( vfb ); + + sprintf( (char *)buf, "%c%c\n%d %d\n255\n", IMPBM_MAGIC1, IMRPGM_MAGIC2, + + width, height ); + + IMWrite( ioType, fd, fp, buf, strlen((char*) buf ) ); + + + + ImInfo ("Byte Order","Most Significant Byte First"); + + ImInfo ("Type","8-bit Grayscale"); + + sprintf (message, "%d x %d",width, height); + + ImInfo ("Resolution",message); + + ImInfo ("Compression", "none"); + + + + ImMalloc( buffer, unsigned char *, width ); + + pPixel = ImVfbQFirst( vfb ); + + for ( y = 0; y < height; y++ ) + + { + + pBuffer = buffer; + + for ( x = 0; x < width; x++ ) + + { + + *pBuffer++ = ImVfbQIndex8( vfb, pPixel ); + + ImVfbSInc( vfb, pPixel ); + + } + + IMWrite( ioType, fd, fp, buffer, pBuffer - buffer ); + + } + + + + free( (char *)buffer ); + + return ( 1 ); + +} + + + +static int + +#ifdef __STDC__ + +imRpbmWriteRGB(ImFileFormatWriteMap * pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imRpbmWriteRGB( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O type flag */ + + int fd; /* Unbuffered file descriptor */ + + FILE *fp; /* Buffered file pointer */ + + TagTable *flagsTable; /* Max color value flag */ + + TagTable *tagTable; /* Tag list to read from */ + +#endif + +{ + + int x, y; /* Pixel location counters */ + + ImVfbPtr pPixel; /* Current pixel */ + + ImVfb *vfb; /* Image to output */ + + int width; /* Image width */ + + int height; /* Image height */ + + unsigned char buf[200]; /* Temp buffer */ + + unsigned char *buffer; /* Line buffer */ + + unsigned char *pBuffer; /* Current location in buffer */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * Get the VFB and its attributes. Write the PPM header. + + */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + width = ImVfbQWidth( vfb ); + + height = ImVfbQHeight( vfb ); + + sprintf( (char *)buf, "%c%c\n%d %d\n255\n", IMPBM_MAGIC1, IMRPPM_MAGIC2, + + width, height ); + + IMWrite( ioType, fd, fp, buf, strlen((char*) buf ) ); + + + + ImInfo ("Byte Order","Most Significant Byte First"); + + ImInfo ("Type","24-bit RGB"); + + sprintf (message, "%d x %d",width, height); + + ImInfo ("Resolution",message); + + ImInfo ("Compression", "none"); + + + + ImMalloc( buffer, unsigned char *, width * 3 ); + + pPixel = ImVfbQFirst( vfb ); + + for ( y = 0; y < height; y++ ) + + { + + pBuffer = buffer; + + for ( x = 0; x < width; x++ ) + + { + + *pBuffer++ = ImVfbQRed( vfb, pPixel ); + + *pBuffer++ = ImVfbQGreen( vfb, pPixel ); + + *pBuffer++ = ImVfbQBlue( vfb, pPixel ); + + ImVfbSInc( vfb, pPixel ); + + } + + IMWrite( ioType, fd, fp, buffer, pBuffer - buffer ); + + } + + + + free( (char *)buffer ); + + return ( 1 ); + +} + diff --git a/utils/roq2/libim/impcx.c b/utils/roq2/libim/impcx.c new file mode 100644 index 0000000..1d54ff6 --- /dev/null +++ b/utils/roq2/libim/impcx.c @@ -0,0 +1,3193 @@ +/** + + ** $Header: /roq/libim/impcx.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/impcx.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** impcx.c - ZSoft IBM PC Paintbrush image file I/O + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** impcx.c contains routines to read and write ZSoft PCX files for + + ** the image manipulation library. Raster data read in is stored + + ** in a VFB. Raster data written out is taken from a tag table. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** imPcxRead f read an PCX file + + ** imPcxWrite1 f write a 1-bit PCX file + + ** imPcxWrite8 f write an 8-bit PCX file + + ** + + ** PRIVATE CONTENTS + + ** imPcxHeaderInfo t header for PCX files + + ** imPcxHeaderFields v header description for binary I/O package + + ** + + ** HISTORY + + ** $Log: /roq/libim/impcx.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.12 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.11 1995/06/15 20:48:58 bduggan + + ** Removed useless ++. Changed bzero to memset. + + ** + + ** Revision 1.10 1995/04/03 21:32:34 bduggan + + ** took out #ifdef NEWMAGIC + + ** + + ** Revision 1.9 1995/01/10 23:36:33 bduggan + + ** put in IMMULTI, IMPIPE instead of TRUE/FALSE + + ** uncapitlized i's in local functions + + ** made read/write routines static + + ** + + ** Revision 1.8 94/10/03 11:30:21 nadeau + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Rearranged magic number structures for format handlers. + + ** Made format handler routines static (i.e., local to file). + + ** Updated comments, adding format descriptions and references. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.7 92/12/03 01:50:23 nadeau + + ** Corrected info messages. + + ** + + ** Revision 1.6 92/11/23 18:42:43 nadeau + + ** Removed use of IMINFOMSG. + + ** + + ** Revision 1.5 92/11/04 12:12:37 groening + + ** put ImFIleFormat info and magic number info + + ** from imfmt.c into this file. + + ** + + ** Revision 1.4 92/09/29 18:00:29 vle + + ** Added ImInfo messages. + + ** + + ** Revision 1.3 92/08/31 17:30:11 vle + + ** Updated copyright notice. + + ** + + ** Revision 1.2 91/10/03 09:15:18 nadeau + + ** Added undocumented PC PaintBrush CLT type. + + ** + + ** Revision 1.1 91/09/17 20:16:28 nadeau + + ** Initial revision + + ** + + **/ + + + +#include "iminternal.h" + + + + + +/** + + ** $Header: /roq/libim/impcx.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1994 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/impcx.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** impcx.c - ZSoft IBM PC Paintbrush image file I/O + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** impcx.c contains routines to read and write ZSoft PCX files for + + ** the image manipulation library. Raster data read in is stored + + ** in a VFB. Raster data written out is taken from a tag table. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** done + + ** + + ** PRIVATE CONTENTS + + ** imPcxRead f read an PCX file + + ** imPcxWrite1 f write a 1-bit PCX file + + ** imPcxWrite8 f write an 8-bit PCX file + + ** imPcxHeaderInfo t header for PCX files + + ** imPcxHeaderFields v header description for binary I/O package + + ** + + ** HISTORY + + ** $Log: /roq/libim/impcx.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.12 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.11 1995/06/15 20:48:58 bduggan + + ** Removed useless ++. Changed bzero to memset. + + ** + + ** Revision 1.10 1995/04/03 21:32:34 bduggan + + ** took out #ifdef NEWMAGIC + + ** + + ** Revision 1.9 1995/01/10 23:36:33 bduggan + + ** put in IMMULTI, IMPIPE instead of TRUE/FALSE + + ** uncapitlized i's in local functions + + ** made read/write routines static + + ** + + ** Revision 1.8 94/10/03 11:30:21 nadeau + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Rearranged magic number structures for format handlers. + + ** Made format handler routines static (i.e., local to file). + + ** Updated comments, adding format descriptions and references. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.7 92/12/03 01:50:23 nadeau + + ** Corrected info messages. + + ** + + ** Revision 1.6 92/11/23 18:42:43 nadeau + + ** Removed use of IMINFOMSG. + + ** + + ** Revision 1.5 92/11/04 12:12:37 groening + + ** put ImFIleFormat info and magic number info + + ** from imfmt.c into this file. + + ** + + ** Revision 1.4 92/09/29 18:00:29 vle + + ** Added ImInfo messages. + + ** + + ** Revision 1.3 92/08/31 17:30:11 vle + + ** Updated copyright notice. + + ** + + ** Revision 1.2 91/10/03 09:15:18 nadeau + + ** Added undocumented PC PaintBrush CLT type. + + ** + + ** Revision 1.1 91/09/17 20:16:28 nadeau + + ** Initial revision + + ** + + **/ + + + +#include "iminternal.h" + + + + + + + + + +/** + + ** FORMAT + + ** PCX - ZSoft IBM PC Paintbrush image + + ** + + ** AKA + + ** pcc + + ** + + ** FORMAT REFERENCES + + ** - Technical Reference Manual for PC Paintbrush et al, ZSoft Corp. + + ** - Bit-Mapped Graphics, Steve Rimmer + + ** - Supercharged Bitmapped Graphics, Steve Rimmer + + ** - Graphics File Formats, David C. Kay, John R. Levine + + ** - HP LaserJet Programming, Andrew Binstock, David Babcock, Marv Luse + + ** + + ** CODE CREDITS + + ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1991. + + ** + + ** DESCRIPTION + + ** ZSoft's PCX format is used by a variety of IBM PC software, including + + ** ZSoft's own Publisher's Paintbrush, PC Paintbrush Plus, and PC + + ** Paintbrush. + + ** + + ** This code supports the format as described in: + + ** + + ** Technical Reference Manual + + ** ZSoft Corporation + + ** 450 Franklin Rd. Suite 100 + + ** Marietta, GA 30067 + + ** (404) 428-0008 + + ** + + ** This report is dated 1988. + + ** + + ** Additional format information was obtained from the book "Bit-Mapped + + ** Graphics" by Steve Rimmer and published by WINDCREST/McGraw-Hill. + + ** + + ** PCX files start with a fixed-length 128-byte header consisting of + + ** the following: + + ** + + ** Byte Item Size Description/Comments + + ** + + ** 0 Manufacturer 1 10 = ZSoft .PCX + + ** + + ** 1 Version 1 Version # + + ** 0 = Version 2.5 + + ** 2 = Version 2.8 w/palette info + + ** 3 = Version 2.8 wo/palette info + + ** 5 = Version 3.0 + + ** + + ** 2 Encoding 1 1 = .PCX run length encoding + + ** + + ** 3 Bits per pixel 1 # of bits/pixel per plane + + ** 1 = monochrome (CGA) + + ** 4 = 16-color (EGA) + + ** 8 = 256-color (VGA) + + ** + + ** 4 Window 4*2 Picture dimensions + + ** xmin + + ** ymin + + ** xmax + + ** ymax + + ** + + ** 12 Horizontal Res 2 Horizontal resolution of dev, in pixels + + ** + + ** 14 Vertical Res 2 Vertical resolution of dev, in pixels + + ** + + ** 16 Colormap 16*3 Basic color table + + ** + + ** 64 Reserved 1 + + ** + + ** 65 No. Planes 1 # of color planes + + ** + + ** 66 Bytes per line 2 # of bytes per scanline per color + + ** color plane (always even) + + ** + + ** 68 Colormap type 2 Colormap type + + ** 1 = color/BW + + ** 2 = grayscale + + ** + + ** 70 Filler 58 Blanks + + ** + + ** All 2-byte integers are given in Intel byte order (LBF). + + ** + + ** Image data immediately follows the header. In version 6.0 PCX files, + + ** an optional 256-entry color palette may follow the image data (but + + ** see below). + + ** + + ** HEADER DETAILS + + ** The 'Manufacturer' field is always '10' for PCX files. ZSoft has never + + ** introduced further 'Manufacturer' codes. The SDSC Image Tools use + + ** the 'Manufacturer' field as the file's magic number. + + ** + + ** The 'Encoding' field is always '1'. ZSoft has never introduced other + + ** encoding schemes (which it should have, since this one is particularly + + ** stupid). ZSoft does not support unencoded images in PCX files. + + ** + + ** The 'Window' field is used to compute the image dimensions using: + + ** + + ** width = xmax - xmin + 1; + + ** height = ymax - ymin + 1; + + ** + + ** Note the '+1' needed on both of these. Just a quirk of PCX files. + + ** On write, the SDSC Image Tools set the 'xmin' and 'ymin' fields to 0, + + ** and the 'xmax' and 'ymax' fields to the width and height, minus 1. + + ** + + ** The 'Horizontal Res' and 'Vertical Res' fields are irrelevant. + + ** SDSC Image Tools code ignores these on read, and sets them to 0's + + ** on write. + + ** + + ** NOTE: The popular UNIX PBM+ tools set the horizontal and + + ** vertical device resolution to the image size on write, but + + ** ignore them on read. This is incorrect. The resolution fields + + ** are defined by the spec to be HARDWARE resolution, not image + + ** resolution. We feel that setting them to zeroes is less + + ** incorrect than setting them to some value. + + ** + + ** The 'Reserved' and 'Filler' fields are always ignored by us on + + ** read, and always set to 0's on write. + + ** + + ** The remaining fields have specific combinations of values depending + + ** upon the type of image being stored. + + ** + + ** MONOCHROME IMAGES + + ** Monochrome PCX files were intended for the original CGA card as well + + ** as single-plane use of the EGA card. + + ** + + ** Header values set to the following flag monochrome images: + + ** + + ** Version = 0, 2, or 3 + + ** Bits per pixel = 1 + + ** Colormap = ignored + + ** No. Planes = 1 + + ** Bytes per line = (width + 7) / 8 rounded up to even byte count + + ** Colormap type = ignored + + ** + + ** Image data following the header is an encoded buffer with 8 pixels + + ** to the byte. The left-most bit in each byte corresponds to the + + ** left-most pixel of each group of 8. Scanline buffers are rounded up + + ** to an even number of bytes, then encoded (see below). + + ** + + ** "Extra" image data may follow the last scanline in order to bring the + + ** image data up to a multiple of 8 or 16 scanlines. On read, we ignore + + ** such extra data. On write we don't write extra data. + + ** + + ** 4-BIT COLOR IMAGES + + ** 4-bit color PCX files were intended for the four-plane mode of the + + ** EGA card. + + ** + + ** Header values set to the following flag 4-bit color images: + + ** + + ** Version = 2 or 3 + + ** Bits per pixel = 1 + + ** Colormap = used (see below) + + ** No. Planes = 4 + + ** Bytes per line = (width + 7)/8 rounded up to even byte count + + ** Colormap type = ignored + + ** + + ** If the 'Version' field of the header is a '2', the header contains + + ** a color palette in the 'Colormap' portion of the header. Palette + + ** values are given from low to high and consist of 16 3-byte RGB triplets. + + ** + + ** If the 'Version' field of the header is a '3', the header does not + + ** contain a palette for the image. For our purposes, this makes the + + ** image grayscale. + + ** + + ** Image data following the header is grouped as four consequetive + + ** 1-bit scanlines of data. Each 1-bit scanline is packed into bytes + + ** at 8 pixels to the byte, with the left most bit in each byte + + ** corresponding to the left-most pixel of each group of 8. The entire + + ** set of 4 1-bit scanlines is considered one buffer, and as a whole + + ** is padded to the next higher even byte boundary, then encoded. + + ** + + ** For encoding purposes, the group of 4 1-bit scanlines is considered + + ** a single buffer. Encoding always breaks at the end of such a buffer, + + ** but might not break going from one 1-bit scanline to the next within + + ** such a group. + + ** + + ** "Extra" image data may follow the last scanline in order to bring the + + ** image data up to a multiple of 8 or 16 scanlines. We neither read it + + ** or write it. + + ** + + ** 8-BIT COLOR IMAGES + + ** 8-bit color PCX files were intended for the VGA card. + + ** + + ** Header values set to the following flag 8-bit color images: + + ** + + ** Version = 5 + + ** Bits per plane = 8 + + ** Colormap = unused (see below) + + ** No. Planes = 1 + + ** Bytes per line = ((width + 1)/2) * 2 + + ** Colormap type = 1 or 2 + + ** + + ** The 'Version' field will always be 5 for 8-bit color images. + + ** + + ** The color palette for 8-bit color images is too big for the 'Colormap' + + ** field of the header. Instead, the palette, if any, is tacked onto + + ** the end of the image data. The "official" procedure (from the spec) + + ** is to seek to 769 bytes before the end of the file and read a byte. + + ** If it is a 0x0C (12 decimal), then the next 768 bytes are 256 3-byte + + ** RGB triplets. + + ** + + ** The 'Colormap type' field is meant to toggle the VGA card between + + ** grayscale and color. In our case, it is meaningless. The existance + + ** of a palette makes the image color. Nonexistance means the image is + + ** grayscale. On read, we ignore this field. On write, we always set + + ** it to 1 (color). + + ** + + ** Image data following the header is encoded from a stream of bytes, + + ** at one byte per pixel. Consequetive bits in each byte correspond to + + ** consequetive color planes (as with normal graphics hardware). + + ** As with monochrome and 4-bit images, each scanline buffer is rounded + + ** up to an even number of bytes. Encoding breaks at the end of each + + ** scanline. + + ** + + ** "Extra" image data may follow the last scanline in order to bring the + + ** image data up to a multiple of 8 or 16 scanlines. We neither read or + + ** write such extra scanlines. + + ** + + ** ENCODING + + ** Image data buffers are encoded with a type of run-length encoding. + + ** A data byte with its upper two bits set is a run count for the number + + ** of times to repeat the next byte in the file. Data bytes without + + ** their upper two bits set are literal values. For example: + + ** + + ** Decode: + + ** pEncoded = encodedBuffer; + + ** pDecoded = decodedBuffer; + + ** pEnd = decodedBuffer + BytesPerLine; + + ** while ( pDecoded < pEnd ) + + ** { + + ** if ( (*pEncoded & 0xC0) == 0xC0 ) + + ** { + + ** count = (*pEncoded++) & 0x3F; + + ** value = *pEncoded++; + + ** for ( j = 0; j < count; j++ ) + + ** *pDecoded++ = value; + + ** } + + ** else + + ** *pDecoded++ = *pEncoded++; + + ** } + + ** + + ** When encoding one must watch for data values with their upper two + + ** bits set. Such bytes are encoded as a run byte of 1 followed by + + ** the data byte. + + ** + + ** The worst case image only contains data bytes with their upper two + + ** bits set. This will cause an encoded image to take DOUBLE the space + + ** of the unencoded image. Brain dead. + + ** + + ** COLOR TABLE CAVEATS + + ** #1: PBM+ Problems. + + ** According to the spec, the existance and style of CLT is determined + + ** by the header's version number: + + ** + + ** 0 2.5 no CLT + + ** 2 2.8W CLT in header + + ** 3 2.8WO no CLT + + ** 5 3.0 CLT at end of file (possibly) + + ** + + ** Unfortunately, the popular PBM+ package for UNIX systems always + + ** generates PCX files for verison 3.0, and assumes other packages + + ** will check the header plane and bits per pixel fields to decide + + ** where a CLT is. + + ** + + ** In order to be compatible with this software, we ignore the version + + ** number header and similarly depend upon the plane and bits per pixel + + ** counts to decide what type of CLT, if any, is present. The only + + ** version number we check is 2.8WO which explicitly says there is no + + ** CLT for the image and that the header CLT should be ignored. + + ** + + ** Fortunately, this change of parsing still maintains compatibility + + ** with PCX usage. For CLT writing, we choose the right version number + + ** the way we're supposed to. + + ** + + ** #2: Spec Problems. + + ** The PCX spec recommends finding end-of-file CLTs by the following: + + ** + + ** lseek( fd, -769, 2 ); -- seek to end of file, -769 bytes + + ** read( fd, buffer, 1 ); -- read 1 byte + + ** if ( *buffer == 0x0C ) + + ** there is a CLT + + ** + + ** This is not a robust way of checking for an EOF CLT. Imagine the + + ** case where there is no CLT, and seeking back 769 bytes gets you + + ** into the middle of image data. 0x0C is not a byte that is impossible + + ** to encounter in image data. If you happened to encounter it, you + + ** would mistakenly conclude there was a CLT. + + ** + + ** The popular UNIX PBM+ package simply reads the image data in, one + + ** byte at a time. When it has finished reading in the data, it looks + + ** at the next byte to see if it is 0x0C. If so, it reads in a CLT. + + ** + + ** This is neither speedy (lots of single byte reads) nor robust. + + ** The spec says: + + ** "There may be extra scan lines at the bottom of the image, + + ** to round to 8 or 16 scan lines." + + ** + + ** If they were there, PBM+ would not find the CLT. + + ** + + ** Our approach is more robust and much faster. + + ** + + ** The CLT is read in first by the seek approach. Doing it first is + + ** merely convenient algorithmically. It could be done last as well. + + ** + + ** The image data is then read in by making a worst-case guess at its + + ** size in bytes (twice the size of a decoded image). This will + + ** very likely read too much data. This is OK since we watch the + + ** decode count while decoding so that we don't decode off the end of + + ** the image. The advantage is that we can read it all in with one + + ** read system call. Much much much faster. + + ** + + ** After decoding, a count is returned of the number of encoded bytes + + ** used. To determine if the CLT we read in earlier is truely a CLT, + + ** and not image data improperly interpreted, we compare the file position + + ** of the 769-bytes-from-the-end CLT with the position we'd be at if we + + ** had only read in the image data (header + # of encoded bytes used). + + ** If the CLT file position is in advance of the end-of-image data + + ** position, then it is considered a valid CLT. + + ** + + ** It is still possible that there are lots and lots of "extra" scanlines + + ** after the image data and that they contain 0x0C's. A seek into the + + ** midst of these would still falsly conclude there was a CLT there. + + ** This seems very unlikely. Software that pads with extra scanlines + + ** usually sets them to zeroes. + + ** + + ** IMAGE DATA CAVEATS + + ** #1: Even-byte-count buffers + + ** According to the spec, all scanlines, prior to encoding, are rounded + + ** up to an even number of bytes. Unfortunately, the popular PBM+ + + ** package for UNIX systems fails to do this. + + ** + + ** In order to be compatible with this software, we check the header's + + ** "Bytes per line" field and skip the difference between it and the + + ** size of a decoded buffer. If the PCX generator made the byte count + + ** even, this will skip 1 byte for odd-length lines. If the PCX generator + + ** did not make this even, such as PBM+, then this will skip no bytes + + ** in such cases. + + ** + + ** Fortunately, this change of skipping still maintains compatibility + + ** with PCX usage. For image writing, we round up the way we're + + ** supposed to. + + ** + + ** #2: 2-bit and 3-bit color images + + ** According to the documentation, 2-bit and 3-bit images aren't really + + ** allowed. However, the poplular UNIX PBM+ package freely generates + + ** them. + + ** + + ** In order to be compatible with such code, we also accept 2-bit and + + ** 3-bit images as variations of the 4-bit case. + + ** + + ** Header values set to the following flag 4-bit color images: + + ** + + ** Version = 2 or 3 + + ** Bits per pixel = 1 + + ** Colormap = used (see below) + + ** No. Planes = 2 or 3 + + ** Bytes per line = (width + 7)/8 rounded up to even byte count + + ** Colormap type = ignored + + ** + + ** 2-bit and 3-bit cases are stored the same as for the 4-bit case. + + */ + + + + + +/* + + * PCX - ZSoft IBM PC Paintbrush + + * For information on these structures, how to use them, etc. please + + * see imfmt.c. + + */ + +#ifdef __STDC__ + +static int imPcxRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imPcxWrite1( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imPcxWrite8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +#else + +static int imPcxRead( ); + +static int imPcxWrite1( ), imPcxWrite8( ); + +#endif + +static char *imPcxNames[ ] = { "pcx", "pcc", NULL }; + +static unsigned char imPcxMagicNumber[ ] = { 0x0a }; + +static ImFileFormatReadMap imPcxReadMap[ ] = + +{ + + /* in out */ + + /* type,ch,dep, attr. VFB type attr. */ + + { IN,1,1, RLE, IMVFBMONO, 0 }, + + { IN,1,2, RLE, IMVFBINDEX8, 0 }, + + { IN,1,2, RLE|C, IMVFBINDEX8, C }, + + { IN,1,3, RLE, IMVFBINDEX8, 0 }, + + { IN,1,3, RLE|C, IMVFBINDEX8, C }, + + { IN,1,4, RLE, IMVFBINDEX8, 0 }, + + { IN,1,4, RLE|C, IMVFBINDEX8, C }, + + { IN,1,8, RLE, IMVFBINDEX8, 0 }, + + { IN,1,8, RLE|C, IMVFBINDEX8, C }, + + { -1, 0, -1, 0 }, + +}; + +static ImFileFormatWriteMap imPcxWriteMap[ ] = + +{ + + /* in out */ + + /* VFB type, attr., type,ch,dep, attr., func */ + + { IMVFBMONO, 0, IN,1,1, RLE, imPcxWrite1 }, + + { IMVFBINDEX8, 0, IN,1,8, RLE, imPcxWrite8 }, + + { IMVFBINDEX8, C, IN,1,8, RLE| C, imPcxWrite8 }, + + { -1, 0, -1, 0, NULL }, + +}; + + + +static ImFileMagic imFilePcxMagic[]= + +{ + + { 0, 1, imPcxMagicNumber }, + + { 0, 0, NULL }, + +}; + + + +ImFileFormat ImFilePcxFormat = + +{ + + imPcxNames, "ZSoft IBM PC Paintbrush file", + + "ZSoft Corp.", + + "1-bit monochrome (CGA), 2-bit, 3-bit, and 4-bit color (EGA) files\n\ +with or without a color table, and 8-bit color (VGA) files with or\n\ +without a color table. All are RLE-encoded.", + "1-bit monochrome (CGA), and 8-bit color (VGA) files with or\n\ +without a color table. All are RLE-encoded.", + imFilePcxMagic, + + IMNOMULTI, IMNOPIPE, + + IMNOMULTI, IMPIPE, + + imPcxRead, imPcxReadMap, imPcxWriteMap + +}; + + + + + + + +/* + + * TYPEDEF & STRUCT + + * imPcxHeaderInfo - header for PCX files + + * imPcxHeaderFields - header description for binary I/O package + + * + + * DESCRIPTION + + * PCX files start with a header that gives various details of the + + * image contained in the file. + + */ + + + +typedef struct imPcxHeaderInfo /* PCX */ + +{ + + unsigned char pcx_manufacturer; + + unsigned char pcx_version; + + unsigned char pcx_encoding; + + unsigned char pcx_bitsPerPixel; + + unsigned short pcx_window[4]; + + unsigned short pcx_horizontalRes; + + unsigned short pcx_verticalRes; + + unsigned char pcx_clt[16][3]; + + unsigned char pcx_reserved; + + unsigned char pcx_nPlanes; + + unsigned short pcx_bytesPerLine; + + unsigned short pcx_cltType; + + unsigned char pcx_filler[58]; + +} imPcxHeaderInfo; + + + +static BinField imPcxHeaderFields[ ] = + +{ + + /* pcx_manufacturer */ UCHAR, 1, 1, + + /* pcx_version */ UCHAR, 1, 1, + + /* pcx_encoding */ UCHAR, 1, 1, + + /* pcx_bitsPerPixel */ UCHAR, 1, 1, + + /* pcx_window */ USHORT, 2, 4, + + /* pcx_horizontalRes */ USHORT, 2, 1, + + /* pcx_verticalRes */ USHORT, 2, 1, + + /* pcx_clt */ UCHAR, 1, 48, + + /* pcx_reserved */ UCHAR, 1, 1, + + /* pcx_nPlanes */ UCHAR, 1, 1, + + /* pcx_bytesPerLine */ USHORT, 2, 1, + + /* pcx_cltType */ USHORT, 2, 1, + + /* pcx_filler */ UCHAR, 1, 58, + + 0, 0, 0, + +}; + + + +#define IMPCXHEADERSIZE (128) + + + + + +/* + + * Value for fields: + + */ + +#define IMPCXMANUFACTURER 0x0A /* ZSoft .PCX */ + + + +#define IMPCXV_2_5 0 /* Version 2.5 */ + +#define IMPCXV_2_8W 2 /* Version 2.8 w/clt */ + +#define IMPCXV_2_8WO 3 /* Version 2.8 wo/clt */ + +#define IMPCXV_3_0 5 /* Version 3.0 */ + + + +#define IMPCXENCODING 1 /* Run-length encoded */ + + + +#define IMPCXCOLOR 1 /* Color or BW CLT */ + +#define IMPCXGRAY 2 /* Grayscale CLT */ + +#define IMPCXPAINTBRUSH 2992 /* Strange PC Paintbrush type */ + + + + + + + + + + + +/* + + * FUNCTION + + * imPcxDecode - decode a run buffer + + * + + * DESCRIPTION + + * An incomming encoded data buffer is decoded into a second buffer. + + */ + + + +static int /* Returns # of bytes decoded */ + +#ifdef __STDC__ + +imPcxDecode( unsigned char *src, int nDst, unsigned char *dst ) + +#else + +imPcxDecode( src, nDst, dst ) + + unsigned char *src; /* Source buffer (encoded) */ + + int nDst; /* # of bytes in destination buffer*/ + + unsigned char *dst; /* Destination buffer (decoded) */ + +#endif + +{ + + int i; /* Counter */ + + unsigned char value; /* Run value */ + + unsigned char *dstLast; /* Last byte +1 of dst buffer */ + + unsigned char *srcSave; /* Saved src pointer */ + + + + srcSave = src; + + dstLast = dst + nDst; + + while ( dst < dstLast ) + + { + + if ( (*src & 0xC0) != 0xC0 ) + + *dst++ = *src++; + + else + + { + + i = *src++ & 0x3F; + + for ( value = *src++; i; i-- ) + + *dst++ = value; + + } + + } + + + + return ( src - srcSave ); + +} + + + + + + + + + +/* + + * FUNCTION + + * imPcxEncode - encode a run buffer + + * + + * DESCRIPTION + + * An incomming data buffer is encoded into a second buffer. Bytes + + * with their upper two bits set are flagged and stored as runs of 1. + + * + + * Note: becuase the upper two bits of a byte flag a run, we get two + + * special cases to watch for: + + * 1. The maximum run length is 63 (lower 6 bits set). + + * 2. Data bytes with the upper two bits set have to be + + * treated as runs of 1. + + */ + + + +static int /* Returns # of bytes encoded */ + +#ifdef __STDC__ + +imPcxEncode( unsigned char *src, int nSrc, unsigned char *dst ) + +#else + +imPcxEncode( src, nSrc, dst ) + + unsigned char *src; /* Source buffer (encoded) */ + + int nSrc; /* # of bytes in source buffer */ + + unsigned char *dst; /* Destination buffer (decoded) */ + +#endif + +{ + + int run; /* Run count */ + + unsigned char *srcLast; /* Last byte +1 of source buffer*/ + + unsigned char *dstSave; /* Saved original dst value */ + + + + srcLast = src + nSrc - 1; + + dstSave = dst; + + while ( src <= srcLast ) + + { + + run = 0; + + while ( *src == *(src+1) && src < srcLast && run < 62 ) + + { + + run++; + + src++; + + } + + if ( run > 0 ) + + *dst++ = (run + 1) | 0xC0;/* Store the run count*/ + + else if ( (*src & 0xC0) == 0xC0 ) + + *dst++ = 1 | 0xC0; /* Make it a run of 1 */ + + *dst++ = *src++; + + } + + return ( dst - dstSave ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imPcxRead - read a PCX file + + * + + * DESCRIPTION + + * The file header is read and checked. The color table, if any, is + + * read in. The image data is read in and decoded, then dispersed into + + * a VFB of the appropriate depth. The VFB and CLT are added to the + + * tag table. + + */ + + + +static int /* Returns # tags read in */ + +#ifdef __STDC__ + +imPcxRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imPcxRead( ioType, fd, fp, flagsTable, tagTable ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pPixel; /* Pcxel pointer */ + + + + ImClt *clt; /* Image CLT */ + + ImCltPtr pColor; /* Color pointer */ + + + + unsigned char cltBuffer[769]; /* VGA card palette */ + + unsigned char *pCltBuffer; /* Buffer pointer */ + + + + unsigned char *encoded; /* Encoded scanline buffer */ + + unsigned char *decoded; /* Decoded scanline buffer */ + + unsigned char *pDecoded; /* Decoded buffer pointer */ + + unsigned char value; /* Pixel value */ + + unsigned char value2; /* Old pixel value */ + + + + imPcxHeaderInfo header; /* PCX file header */ + + + + int height, width; /* Image dimensions */ + + int width8; /* Width / 8 */ + + int widthextra; /* Width % 8 */ + + + + int n; /* Byte count */ + + int i, j; /* Counter */ + + int x, y; /* Current scanline */ + + + + int cltInHeader; /* Get the CLT from the header */ + + int cltAtEnd; /* Get the CLT from the EOF */ + + + + long cltFilePosition; /* File position of CLT */ + + long imageFilePosition; /* File position of Image */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * Read in the header in LBF (Intel) byte order. Check that it is + + * valid. + + */ + + BinByteOrder( BINLBF ); + + if ( ImBinReadStruct( ioType, fd, fp, &header, imPcxHeaderFields )== -1) + + ImReturnBinError( ); + + + + if ( header.pcx_manufacturer != IMPCXMANUFACTURER ) + + ImErrorFatal( "Bad manufacturer code in PCX file header", -1, IMESYNTAX ); + + + + switch ( header.pcx_version ) + + { + + case IMPCXV_2_5: + + ImInfo( "Version", "2.5" ); + + break; + + case IMPCXV_2_8W: + + ImInfo( "Version", "2.8 with Palette" ); + + break; + + case IMPCXV_2_8WO: + + ImInfo( "Version", "2.8 without Palette" ); + + break; + + case IMPCXV_3_0: + + ImInfo( "Version", "3.0" ); + + break; + + default: + + ImErrorFatal( "Unknown file version in PCX file header", -1, IMESYNTAX ); + + } + + + + if ( header.pcx_encoding != IMPCXENCODING ) + + ImErrorFatal( "Unknown encoding method in PCX file header", -1, IMESYNTAX ); + + + + switch ( header.pcx_bitsPerPixel ) + + { + + case 1: /* 1-bit monochrome (CGA) */ + + /* 2-bit color (EGA) */ + + /* 3-bit color (EGA) */ + + /* 4-bit color (EGA) */ + + if ( header.pcx_nPlanes > 4 ) + + ImErrorFatal( "Bad color plane count in PCX file header", -1, IMESYNTAX ); + + if ( header.pcx_version == IMPCXV_2_8WO ) + + cltInHeader = FALSE; + + else + + cltInHeader = TRUE; + + cltAtEnd = FALSE; + + break; + + + + case 8: /* 8-bit color (VGA) */ + + if ( header.pcx_nPlanes != 1 ) + + ImErrorFatal( "Bad color plane count in PCX file header", -1, IMESYNTAX ); + + cltInHeader = FALSE; + + cltAtEnd = TRUE; + + break; + + default: + + ImErrorFatal( "Unknown image depth. Must be 1, 4, or 8.", -1, IMESYNTAX ); + + } + + + + width = header.pcx_window[2] - header.pcx_window[0] + 1; + + height = header.pcx_window[3] - header.pcx_window[1] + 1; + + + + + + switch ( header.pcx_cltType ) + + { + + case 0: /* For backwards compatibility */ + + case IMPCXCOLOR: + + case IMPCXGRAY: + + case IMPCXPAINTBRUSH: + + break; + + + + default: + + ImErrorFatal( "Unknown palette type in PCX file header", -1, IMESYNTAX ); + + } + + + + ImInfo( "Byte Order", "Least Significant Byte First" ); + + + + sprintf( message, "%d x %d", width, height ); + + ImInfo( "Resolution", message ); + + + + sprintf( message, "%d-bit Color Indexed", header.pcx_bitsPerPixel); + + ImInfo( "Type", message ); + + + + /* + + * Make a CLT if we need one. + + */ + + clt = IMCLTNULL; + + if ( cltInHeader ) + + { + + /* 16-color CLT included in header. */ + + if ( (clt = ImCltAlloc( 16 )) == IMCLTNULL ) + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + pColor = ImCltQFirst( clt ); + + for ( i = 0; i < 16; i++ ) + + { + + ImCltSRed( pColor, header.pcx_clt[i][0] ); + + ImCltSGreen( pColor, header.pcx_clt[i][1] ); + + ImCltSBlue( pColor, header.pcx_clt[i][2] ); + + ImCltSInc( clt, pColor ); + + } + + + + ImInfo( "Color Table", "16 Entries" ); + + } + + else if ( cltAtEnd ) + + { + + /* + + * Seek to 769 bytes from the bottom and see if there + + * is a 0x0C there. If so, we've got a 256 entry palette. + + * + + * HOWEVER, there is the chance that seeking to 769 bytes + + * from the end will put us within the image data if there + + * really isn't a CLT. If that next image data byte just + + * happened to be a 0x0C, then we'd falsely conclude there + + * was a CLT. This is a problem in the PCX spec. + + * + + * To handle this case, we remember where things are and + + * later compare where we got the alleged CLT versus the + + * end of the image data. If it is too early in the file, + + * we were fooled and we get rid of the bogus CLT. + + */ + + imageFilePosition = ImTell( ioType, fd, fp ); + + ImSeek( ioType, fd, fp, -769, 2 ); + + cltFilePosition = ImTell( ioType, fd, fp ); + + n = ImBinRead( ioType, fd, fp, cltBuffer, UCHAR, 1, 769 ); + + if ( n != -1 && *cltBuffer == 0x0C ) + + { + + if ( (clt = ImCltAlloc( 256 )) == IMCLTNULL ) + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + pColor = ImCltQFirst( clt ); + + pCltBuffer = cltBuffer + 1; + + for ( i = 0; i < 256; i++ ) + + { + + ImCltSRed( pColor, *pCltBuffer++ ); + + ImCltSGreen( pColor, *pCltBuffer++ ); + + ImCltSBlue( pColor, *pCltBuffer++ ); + + ImCltSInc( clt, pColor ); + + } + + + + ImInfo( "Color Table", "256 Entries" ); + + } + + else + + { + + ImInfo( "Color Table", "none" ); + + } + + ImSeek( ioType,fd, fp, IMPCXHEADERSIZE, 0 ); + + } + + + + + + ImInfo( "Compression Type", "Run Length Encoded (RLE)" ); + + + + /* + + * Read in the image data. Unfortunately, because it is encoded, + + * we don't know how much data there is. We do know our worst + + * case encoding could produce a file twice the size of the image. + + * So, we allocate a buffer that big and try to read that much + + * data. It is very likely that BinRead will return less than + + * that. Fine. We then decode that buffer into a new buffer + + * that is the size of the image (in bytes). + + */ + + n = header.pcx_bytesPerLine * header.pcx_nPlanes * height; + + ImMalloc( encoded, unsigned char *, 2 * n ); + + ImMalloc( decoded, unsigned char *, n ); + + + + if ( ImBinRead( ioType, fd, fp, encoded, UCHAR, 1, 2 * n ) == -1 ) + + { + + free( (char *)encoded ); + + free( (char *)decoded ); + + if ( clt != IMCLTNULL ) + + ImCltFree( clt ); + + ImReturnBinError( ); + + } + + + + n = imPcxDecode( encoded, n, decoded ); + + + + + + /* + + * If the location at which we found the CLT is within the image + + * data we just read in, then there wasn't really a CLT there at + + * all. It just happened that seeking to 769 bytes from the end + + * fell upon a 0x0C within the image data, and we falsly concluded + + * there was a CLT. We fix our mistake now. + + */ + + if ( cltAtEnd && imageFilePosition + n > cltFilePosition ) + + { + + ImCltFree( clt ); + + clt = IMCLTNULL; + + } + + + + + + /* + + * Walk the decoded buffer and expand it into a new VFB. + + */ + + if ( header.pcx_bitsPerPixel == 1 && header.pcx_nPlanes == 1 ) + + { + + /* + + * Monochrome. The decoded buffer contains packed bytes, + + * each holding 8 1-bit pixel values. Watch for widths that + + * are not a multiple of 8, and remember to toss an extra + + * byte at the end of each scanline if the width (in bytes) + + * is not even. + + */ + + if ( (vfb=ImVfbAlloc( width, height, IMVFBMONO )) == IMVFBNULL ) + + { + + free( (char *)encoded ); + + free( (char *)decoded ); + + if ( clt != IMCLTNULL ) + + ImCltFree( clt ); + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + pPixel = ImVfbQFirst( vfb ); + + pDecoded = decoded; + + width8 = width / 8; /* Width in bytes*/ + + widthextra = width & 0xF; /* width % 8 */ + + for ( y = 0; y < height; y++ ) + + { + + for ( x = 0; x < width8; x++ ) + + { + + value = *pDecoded++; + + for ( i = 7; i >= 0; i-- ) + + { + + ImVfbSMono( vfb, pPixel, + + (value>>i) & 0x1 ); + + ImVfbSInc( vfb, pPixel ); + + } + + } + + if ( widthextra ) + + { + + value = *pDecoded++; + + for ( i = 7; i >= (8-widthextra); i-- ) + + { + + ImVfbSMono( vfb, pPixel, + + (value>>i) & 0x1 ); + + ImVfbSInc( vfb, pPixel ); + + } + + } + + + + /* + + * According to the spec, we only have to skip a + + * byte if the width is odd. However, some packages + + * don't adhear to the even-byte-count requirement + + * (such as PBM+). So, we only skip if the header + + * says we should, and then as much as it says we + + * should. + + */ + + for ( i = width8 + (widthextra?1:0); + + i < header.pcx_bytesPerLine; i++ ) + + pDecoded++; /* Skip */ + + } + + } + + else if ( header.pcx_bitsPerPixel == 1 ) + + { + + /* + + * n-bit color (usually 4-bit). The decoded buffer contains + + * packed bytes, each holding 8 1-bit pixel values... yes, + + * 1-bit pixel values! Each scanline is stored as nPlanes + + * of 1-bit planes. We need to remember to toss an extra + + * byte per set if each scanline plane set's byte count is + + * not even. + + */ + + if ( (vfb=ImVfbAlloc( width, height, IMVFBINDEX8 ))==IMVFBNULL ) + + { + + free( (char *)encoded ); + + free( (char *)decoded ); + + if ( clt != IMCLTNULL ) + + ImCltFree( clt ); + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + pDecoded = decoded; + + width8 = width / 8; /* Width in bytes*/ + + widthextra = width & 0xF; /* width % 8 */ + + for ( y = 0; y < height; y++ ) + + { + + /* First plane. */ + + pPixel = ImVfbQPtr( vfb, 0, y ); + + for ( x = 0; x < width8; x++ ) + + { + + value = *pDecoded++; + + for ( i = 7; i >= 0; i-- ) + + { + + ImVfbSIndex8( vfb, pPixel, + + ((value>>i) & 0x1) ); + + ImVfbSInc( vfb, pPixel ); + + } + + } + + if ( widthextra ) + + { + + value = *pDecoded++; + + for ( i = 7; i >= (8-widthextra); i-- ) + + { + + ImVfbSIndex8( vfb, pPixel, + + ((value>>i) & 0x1) ); + + ImVfbSInc( vfb, pPixel ); + + } + + } + + + + /* Additional planes. */ + + for ( j = 1; j < header.pcx_nPlanes; j++ ) + + { + + pPixel = ImVfbQPtr( vfb, 0, y ); + + for ( x = 0; x < width8; x++ ) + + { + + value = *pDecoded++; + + for ( i = 7; i >= 0; i-- ) + + { + + value2 = ImVfbQIndex8( vfb, + + pPixel ); + + ImVfbSIndex8( vfb, pPixel, + + value2 | + + ((value>>i) & 0x1)<= (8-widthextra); i-- ) + + { + + value2 = ImVfbQIndex8( vfb, + + pPixel ); + + ImVfbSIndex8( vfb, pPixel, + + value2 | + + ((value>>i) & 0x1)<= 0; i-- ) + + { + + value |= (ImVfbQMono( vfb, pPixel ) & 0x1) << i; + + ImVfbSInc( vfb, pPixel ); + + } + + *pDecoded++ = value; + + } + + if ( widthextra ) + + { + + value = 0; + + for ( i = 7; i >= (8-widthextra); i-- ) + + { + + value |= (ImVfbQMono( vfb, pPixel ) & 0x1) << i; + + ImVfbSInc( vfb, pPixel ); + + } + + *pDecoded++ = value; + + } + + + + + + /* + + * Encoded it and write it out. + + */ + + n = imPcxEncode( decoded, header.pcx_bytesPerLine, encoded ); + + if ( ImBinWrite( ioType, fd, fp, encoded, UCHAR, 1, n ) == -1 ) + + { + + free( (char *)encoded ); + + free( (char *)decoded ); + + ImReturnBinError( ); + + } + + } + + + + free( (char *)encoded ); + + free( (char *)decoded ); + + return ( 1 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imPcxWrite8 - write an 8-bit PCX file + + * + + * DESCRIPTION + + * The PCX file header set up and written out followed by the encoded + + * image data. + + */ + + + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imPcxWrite8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imPcxWrite8( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pPixel; /* Pixel pointer */ + + + + ImClt *clt; /* Color lookup table */ + + ImCltPtr pColor; /* Color pointer */ + + + + imPcxHeaderInfo header; /* PCX file header */ + + + + unsigned char *encoded; /* Encoded data buffer */ + + unsigned char *decoded; /* Decoded data buffer */ + + unsigned char *pDecoded; /* Encoded buffer pointer */ + + + + unsigned char *colorBuf; /* Color table output buffer */ + + unsigned char *pColorBuf; /* Color table buffer pointer */ + + + + int n; /* Byte count */ + + int i, j; /* Color counters */ + + int x, y; /* Pixel counters */ + + int height, width; /* Image size */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * Set up the header and write it out. + + */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + clt = ImVfbQClt( vfb ); + + width = ImVfbQWidth( vfb ); + + height = ImVfbQHeight( vfb ); + + + + BinByteOrder( BINLBF ); + + + + memset( (void *)&header, 0x00, sizeof( header ) ); + + header.pcx_manufacturer = IMPCXMANUFACTURER; + + header.pcx_version = IMPCXV_3_0; /* VGA color */ + + header.pcx_encoding = IMPCXENCODING; + + header.pcx_bitsPerPixel = 8; /* 8-bit color */ + + header.pcx_window[0] = 0; /* xmin */ + + header.pcx_window[1] = 0; /* ymin */ + + header.pcx_window[2] = width - 1; /* xmax (minus 1) */ + + header.pcx_window[3] = height - 1; /* ymax (minus 1) */ + + header.pcx_nPlanes = 1; /* 1 color plane */ + + header.pcx_bytesPerLine = width; + + if ( header.pcx_bytesPerLine & 0x1 ) + + ++header.pcx_bytesPerLine; /* Make it even */ + + header.pcx_cltType = IMPCXCOLOR; /* Yup, its color */ + + + + /* Remaining fields left zero. */ + + + + if ( ImBinWriteStruct( ioType, fd, fp, &header, imPcxHeaderFields )==-1) + + { + + ImReturnBinError( ); + + } + + + + /* + + * Output -verbose message + + */ + + ImInfo( "Version", "3.0" ); + + ImInfo( "Byte Order", "Least Significant Byte First" ); + + + + sprintf( message, "%d x %d", width, height ); + + ImInfo( "Resolution", message); + + + + ImInfo( "Type", "8-bit Color Indexed" ); + + ImInfo( "Color Table", "256 Entries" ); + + ImInfo( "Compression Type", "Run Length Encoded (RLE)" ); + + + + /* + + * Buffer up, encode, and write out the image data. Remember that + + * an encoded buffer could take up twice as much space as the + + * decoded one, in the pathelogical case. + + */ + + ImMalloc( decoded, unsigned char *, header.pcx_bytesPerLine ); + + ImMalloc( encoded, unsigned char *, 2 * header.pcx_bytesPerLine ); + + memset( (void *)decoded, 0x00, header.pcx_bytesPerLine ); + + + + pPixel = ImVfbQFirst( vfb ); + + for ( y = 0; y < height; y++ ) + + { + + pDecoded = decoded; + + for ( x = 0; x < width; x++ ) + + { + + *pDecoded++ = ImVfbQIndex8( vfb, pPixel ) & 0xFF; + + ImVfbSInc( vfb, pPixel ); + + } + + + + + + /* + + * Encoded it and write it out. + + */ + + n = imPcxEncode( decoded, header.pcx_bytesPerLine, encoded ); + + if ( ImBinWrite( ioType, fd, fp, encoded, UCHAR, 1, n ) == -1 ) + + { + + free( (char *)encoded ); + + free( (char *)decoded ); + + ImReturnBinError( ); + + } + + } + + free( (char *)encoded ); + + free( (char *)decoded ); + + + + + + /* + + * Write out the color table (if any). Remember to start off the + + * color table with the special "yes it's here" byte: 0x0C. + + */ + + if ( clt == IMCLTNULL ) + + return ( 1 ); /* No color table */ + + + + n = 256 * 3 + 1; /* Always 256 entries */ + + ImMalloc( colorBuf, unsigned char *, n ); + + memset( (void *)colorBuf, 0x00, n ); + + + + pColorBuf = colorBuf; + + *pColorBuf++ = 0x0C; /* Yes, it's here */ + + + + /* Fill in with CLT entries from the VFB. */ + + i = ImCltQNColors( clt ); + + pColor = ImCltQFirst( clt ); + + for ( j = 0; j < i; j++ ) + + { + + *pColorBuf++ = ImCltQRed( pColor ) & 0xFF; + + *pColorBuf++ = ImCltQGreen( pColor ) & 0xFF; + + *pColorBuf++ = ImCltQBlue( pColor ) & 0xFF; + + ImCltSInc( clt, pColor ); + + } + + + + /* Pad to 256 entries. */ + + for ( ; j < 256; j++ ) + + { + + *pColorBuf++ = 0; + + *pColorBuf++ = 0; + + *pColorBuf++ = 0; + + } + + + + /* Write it out. */ + + if ( ImBinWrite( ioType, fd, fp, colorBuf, UCHAR, 1, n ) == -1 ) + + { + + free( (char *)colorBuf ); + + ImReturnBinError( ); + + } + + + + free( (char *)colorBuf ); + + return ( 2 ); /* VFB + CLT */ + +} + + + diff --git a/utils/roq2/libim/impic.c b/utils/roq2/libim/impic.c new file mode 100644 index 0000000..33070b8 --- /dev/null +++ b/utils/roq2/libim/impic.c @@ -0,0 +1,6629 @@ +/** + + ** $Header: /roq/libim/impic.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/impic.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** impic.c - PIXAR picture file I/O + + ** + + ** PROJECT + + ** libimage - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** impic.c contains routines to read and write PIXAR picture files for + + ** the image manipulation library. Raster data read in is stored + + ** in a VFB. Raster data written out is taken from a tag list. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** done + + ** + + ** PRIVATE CONTENTS + + ** + + ** imPicRead f read a PIXAR picture file + + ** imPicWriteRGBA f write a four-channel PIC file, dump + + ** imPicWriteRGBARLE f write a four-channel PIC file, encoded + + ** imPicWriteRGB f write a three-channel PIC file, dump + + ** imPicWriteRGBRLE f write a three-channel PIC file, encoded + + ** imPicWrite8 f write a one-channel PIC file, dump + + ** imPicWrite8RLE f write a one-channel PIC file, encoded + + ** IMPICENCODE8 d 8-bit encode signifier + + ** IMPICENCODE12 d 12-bit encode signifier + + ** IMPICDUMP8 d 8-bit dump signifier + + ** IMPICDUMP12 d 12-bit dump signifier + + ** IMFULLRGBA d 4 channel bit representation + + ** IMRGBBACK d 3 channel bit representation + + ** IMSINGLECHAN d 1 channel bit representation + + ** IMISALPHA d PIC alpha mode signifier + + ** IMNOALPHA d PIC non alpha mode signifier + + ** IMPICLABEL d static array dimension for label + + ** IMPICGAP d static array dimension for gap in header + + ** IMPICSPACE d static array dimension for space in header + + ** IMPICUNUSED d static array dimension for unused section + + ** IMPICHEADERSIZE d static size for header & first tile info + + ** IMPICNUMBER d int representation of magic number + + ** IMPICBLOCK d static size for optimum block transfers + + ** IMPICWRITESTART d int byte offset for writing PIC info + + ** + + ** imPicHeaderInfo t PIC header + + ** imPicHeaderFields t PIC header description for Bin pkg + + ** imPicFr2Buf m free 2 storage buffers of type unsigned char + + ** imPicFrTwoBuf m free 2 storage buffers: types sdsc_uint16 & unsigned char + + ** imPicPrHeader f Debug routine for printing header + + ** + + ** imPicReadEncode8 f convert 8 bit encoded PIC pixels to vfb + + ** imPicReadEncode12 f convert 12 bit encoded PIC pixels to vfb + + ** imPicReadDump8 f transfer 8 bit dumped PIC pixels to vfb + + ** imPicReadDump12 f transfer 12 bit dumped PIC pixels to vfb + + ** + + ** imPicWriteHeader f write header for a Pic file + + ** + + ** HISTORY + + ** $Log: /roq/libim/impic.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.17 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.16 1995/06/15 20:51:04 bduggan + + ** changed bzero to memset. cleaned up indenting. added casts. + + ** + + ** Revision 1.15 1995/04/03 21:33:06 bduggan + + ** took out #ifdef NEWMAGIC + + ** + + ** Revision 1.14 1995/01/10 23:37:24 bduggan + + ** put in IMMULTI, IMPIPE instead of TRUE/FALSE + + ** uncapitlized i's in local functions + + ** made read/write routines static + + ** + + ** Revision 1.13 94/10/03 11:30:23 nadeau + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Rearranged magic number structures for format handlers. + + ** Made format handler routines static (i.e., local to file). + + ** Updated comments, adding format descriptions and references. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.12 92/12/03 01:50:39 nadeau + + ** Corrected info messages. + + ** + + ** Revision 1.11 92/11/23 18:42:46 nadeau + + ** Removed use of IMINFOMSG. + + ** + + ** Revision 1.10 92/11/04 12:04:26 groening + + ** put ImFIleFormat info and magic number info + + ** from imfmt.c into this file. + + ** + + ** Revision 1.9 92/10/19 14:16:35 groening + + ** added ImInfo statements + + ** + + ** Revision 1.8 92/08/31 17:31:03 vle + + ** Updated copyright notice. + + ** + + ** Revision 1.7 92/04/09 09:35:57 groening + + ** To make the compiler happy added extern statements. + + ** + + ** Revision 1.6 91/10/03 12:50:36 mcleodj + + ** modified Write routines to determine total + + ** size of pixel info and change location of + + ** where the pixel info starts being written + + ** + + ** Revision 1.5 91/07/10 16:25:56 nadeau + + ** Removed extra include. + + ** + + ** Revision 1.4 91/03/12 11:09:20 nadeau + + ** Changed zeroing loops to bzero() calls. Optimized mallocs. + + ** + + ** Revision 1.3 91/02/12 11:31:19 nadeau + + ** Removed the tag table checking and VFB conversion now + + ** handled by ImFileRead and ImFileWRite. Did some major + + ** reorganization of write code to separate different VFB + + ** handling into different routines for direct calling + + ** by ImFileWrite. + + ** + + ** Revision 1.2 91/01/31 12:29:09 mcleodj + + ** compression of procedures and modifications of PIC writing + + ** + + ** Revision 1.1 90/07/24 13:00:32 mcleodj + + ** Initial revision + + ** + + ** + + **/ + + + + + +#include + +#include + +#include "sdsc.h" + +#include "im.h" + +#include "iminternal.h" + + + + + +/** + + ** FORMAT + + ** PIXAR picture file + + ** + + ** AKA + + ** pic, picio, pixar + + ** + + ** FORMAT REFERENCES + + ** - PIXAR Image Computer Programmer's Manual, PIXAR. + + ** - PIXAR Image Computer ChapLibraries User's Guide, PIXAR. + + ** - The RenderMan Companion, Steve Upstill, PIXAR. + + ** + + ** + + ** CODE CREDITS + + ** Custom development, Jim McLeod, San Diego Supercomputer Center, 1992. + + ** + + ** DESCRIPTION + + ** + + ** The Pixar picture format accomodates several formats: + + ** multiple channels; different number of bits; + + ** encoded and dumped format; and arbitrary picture size. + + ** + + ** Large pictures can be handled by breaking the picture into + + ** smaller uniform rectangular pieces called " TILES ." + + ** + + ** Multiple byte data is stored with the LEAST SIGNIFICANT 8 bits + + ** in the first byte ( ie: the first 4 bytes of each file are + + ** 0x80, 0xe8, 0x00, 0x00 ) + + ** + + ** + + ** + + ** PIXAR PICTURE IMAGE STORAGE + + ** Header: + + ** byte number #bytes name + + ** 000 4 magic number = 0x00, 0x00, 0xe8, 0x80 + + ** 004 2 version number = 0 + + ** 006 246 label + + ** 252 4 labelptr - for continuation + + ** gap between data + + ** 416 2 picture height (+ num) + + ** 418 2 picture width (+ num) + + ** 420 2 tile height (+ num, <= pic h) + + ** 422 2 tile width (+ num, <= pic w) + + ** 424 2 picture format + + ** 426 2 picture storage + + ** 428 2 blocking factor + + ** 430 2 aplha mode (mat-to-black=0,unassoc.=1) + + ** 432 2 x offset + + ** 434 2 y offset + + ** space between data + + ** 448 4 unused + + ** 452 28 unused + + ** 512 8*n tile pointer table (n = # of tiles) + + ** + + ** Label: Ascii description - labels can be arbitrarily long as the + + ** label pointer in the header points to any continuation + + ** ( allocated in chunks the size of the blocking factor, with + + ** last four bytes of this chunk reserved for further blocks ) + + ** + + ** Pic Format: Any subset of RGBA channels. Single channel "R pictures" + + ** are recovered as grey scale pictures. RGBA channels correspond + + ** to bits 3210; so that, as an example: + + ** RGB selection has 1110 binary. + + ** + + ** Pic Storage: Four modes: (0) 8-bit "encoded" + + ** (1) 12-bit "encoded" + + ** (2) 8-bit dumped + + ** (3) 12-bit dumped + + ** (note: 12 bits stored as two bytes) + + ** + + ** Encoded Tiles: Pixel information is broken into "packets." + + ** NO packet may span multiple scanlines. + + ** NO packet spans multiple disk blocks. + + ** HOWEVER, each scanline may have ANY COMBINATION of the + + ** four types of packets. + + ** + + ** flag count RGBA RGB R + + ** 1 c RGBARGBARGBA. RGBRGBRGB.. RRRR.. + + ** 2 c lRGBAlRGBA... lRGBlRGB... lRlR.. + + ** 3 c ARGBRGBRGB... n/a n/a + + ** 4 c AlRGBlRGB.... n/a n/a + + ** + + ** 0 signifies end of block + + ** + + ** flag and count are packed into 16 bits as follows: + + ** 1st byte: count<0:7> + + ** 2nd byte: flag<0:3> count<8:11> + + ** + + ** if flag = 1 or 3: c = 1 less than the number of dummped pixels + + ** if flag = 2 or 4: c = 1 less than the number of run lengths + + ** and l = number of repetitions + + ** (ie: l=0 indicates 1 instance 0 repetitions) + + ** Zeros fill out a block. + + ** + + ** Dummped Tiles: No excess bytes are used. + + ** (ex: RGB format: RGBRGBRGBRGB.....) + + ** + + ** Blocking Factor: Optimum disk transfer chunk ( normally 8192 bytes ) + + ** + + ** Alpha mode: Has something to do with background of image but does not + + ** influence the current implementation of this im utility. + + ** + + ** Picture Offsets: For proper restoration of image on Pixar buffer window. + + ** + + ** Tiles: Each tile has a four byte pointer and four byte length. + + ** Tiles are numbered across from + + ** 0 to (num_x_tiles*num_y_tiles -1 ) + + ** where num_x_tiles is 1+(pic_width-1)/tile_width + + ** and num_y_tiles is 1+(pic_height-1)/tile_height. + + ** + + ** Tile 0 is in the UPPER LEFT CORNER: pic_pixel(0,0)=tile_0(0,0). + + ** Tiles can extend down or to the right beyond the picture + + ** boundaries, but the pixels outside the picture and inside the + + ** tile are undefined with regard to the picture. + + ** ( note: they are still properly encoded.) + + ** A tile pointer of 0 indicates null tile, a positive + + ** pointer and a count of -1 indicates an incomplete tile. + + ** + + ** ( IMPLEMENTATION NOTE: Tiles have NOT been handled by the + + ** initial revision of the READ routine. It is assumed that + + ** all PIC files will only have ONE tile which is of the same + + ** dimension of the picture. + + ** For the WRITE routine, the PIC file created will only have + + ** one tile which is the entire image. ) + + ** + + **/ + + + +/** + + ** PIC - Pixar Picture file + + ** For information on these structures, how to use them, etc. please + + ** see imfmt.c. + + **/ + +#ifdef __STDC__ + +static int imPicRead( int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable ); + +static int imPicWrite8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable); + +static int imPicWriteRGB( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable); + +static int imPicWriteRGBA( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable); + +static int imPicWrite8RLE( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable); + +static int imPicWriteRGBRLE( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable); + +static int imPicWriteRGBARLE( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable); + +#else + +static int imPicRead( ); + +static int imPicWrite8( ), imPicWriteRGB( ), imPicWriteRGBA( ); + +static int imPicWrite8RLE( ), imPicWriteRGBRLE( ), imPicWriteRGBARLE( ); + +#endif + + + +static char *imPicNames[ ] = { "pic", "picio", "pixar", NULL }; + +static unsigned char imPicMagicNumber[ ] = { 0x80, 0xE8, 0x00, 0x00 }; + +static ImFileFormatReadMap imPicReadMap[ ] = + +{ + + /* in out */ + + /* type,ch,dep, attr. VFB type attr. */ + + { IN,1,8, RLE, IMVFBINDEX8, 0 }, + + { IN,1,8, 0, IMVFBINDEX8, 0 }, + + { IN,1,12, RLE, IMVFBINDEX16, 0 }, + + { IN,1,12, 0, IMVFBINDEX16, 0 }, + + { RGB,3,8, RLE, IMVFBRGB, 0 }, + + { RGB,3,8, 0, IMVFBRGB, 0 }, + + { RGB,3,12, RLE, IMVFBRGB, 0 }, + + { RGB,3,12, 0, IMVFBRGB, 0 }, + + { RGB,3,8, RLE|A, IMVFBRGB, A }, + + { RGB,3,8, A, IMVFBRGB, A }, + + { RGB,3,12, RLE|A, IMVFBRGB, A }, + + { RGB,3,12, A, IMVFBRGB, A }, + + { -1, 0, -1, 0 }, + +}; + +static ImFileFormatWriteMap imPicWriteMap[ ] = + +{ + + /* in out */ + + /* VFB type, attr., type,ch,dep, attr., func */ + + { IMVFBINDEX8, 0, IN,1,8, RLE, imPicWrite8RLE }, + + { IMVFBINDEX8, 0, IN,1,8, 0, imPicWrite8 }, + + + + { IMVFBRGB, 0, RGB,3,8, RLE, imPicWriteRGBRLE }, + + { IMVFBRGB, A, RGB,3,8, RLE|A, imPicWriteRGBARLE}, + + { IMVFBRGB, 0, RGB,3,8, 0, imPicWriteRGB }, + + { IMVFBRGB, A, RGB,3,8, A, imPicWriteRGBA }, + + { -1, 0, -1, 0, NULL }, + +}; + + + + + +static ImFileMagic imFilePicMagic []= + +{ + + { 0, 4, imPicMagicNumber }, + + { 0, 0, NULL }, + +}; + + + + + +ImFileFormat ImFilePicFormat = + +{ + + imPicNames, "PIXAR picture file", + + "PIXAR", + + "8- and 12-bit greyscale image files, (no CLT). 24-bit\n\ +(8-bit/channel) and 36-bit (12-bit/channel) RGB image files.\n\ +Uncompressed (dump) and RLE-compressed (packet encoding) files.", + "8-bit greyscale image files (no CLT). 24-bit (8-bit/channel)\n\ +RGB image files. Uncompressed (dump) and RLE-compressed (packet encoding)\n\ +files.", + + imFilePicMagic, + + IMNOMULTI, IMPIPE, + + IMNOMULTI, IMNOPIPE, + + imPicRead, imPicReadMap, imPicWriteMap + +}; + + + + + + + + + + + + + +#ifndef NULL + +#define NULL (0) + +#endif + + + +FILE *fperr; + + + +/* + + * TYPEDEF & STRUCTURE + + * imPicHeaderInfo - PIC file header information + + * imPicHeaderFields - Description for Binio package + + * + + * DESCRIPTION + + * A PIC file's header contains the image's width, height, and + + * picture format and storage type and many other fields. + + */ + + + +#define IMPICENCODE8 (0) + +#define IMPICENCODE12 (1) + +#define IMPICDUMP8 (2) + +#define IMPICDUMP12 (3) + +#define IMFULLRGBA (15) + +#define IMRGBBACK (14) + +#define IMSINGLECHAN (8) + +#define IMISALPHA (1) + +#define IMNOALPHA (0) + +#define IMPICLABEL (246) + +#define IMPICGAP (160) + +#define IMPICSPACE (12) + +#define IMPICUNUSED (60) + +#define IMPICHEADERSIZE (520) + +#define IMPICNUMBER (59520) + +#define IMPICBLOCK (8192) + +#define IMPICWRITESTART (8192) + + + + /* NOTE: IMPICWRITESTART is assumed to be bigger than IMPICHEADERSIZE + + * If this is changed, don't make it less than. + + * BIGNOTE: Pixar Machines ( from which pic images originated ) + + * seem to REQUIRE that the byte information for the pixel + + * info start at least one full block after the header ( ie: + + * at a byte count of 8192 ). Experiments to begin writing the + + * data before byte count 8192 resulted in the failure of the + + * PIXAR machines to correctly display an image. + + * Therefore, if IMPICWRITESTART must be changed, only increase + + * this constant. + + * + + * ALSO, the read algorithm assumes ( since it was not clearly + + * stated in the PIXAR specification sheets for the pic format ) + + * that the pixel information is stored in block size pieces + + * starting from the location indicated by the tile_pointer. + + * (ie: if the tile pointer does not begin at the beginning of + + * an integer number of the block size, ( in this case + + * 8192 or 16384 ... ) a full block of information + + * will still be read starting from that tile position + + * as well as when writing the pixel information: + + * instead of starting the number of bytes written + + * initially to IMPICWRITESTART % IMPICBLOCKSIZE, this + + * numWritten count begins at 0 ) + + */ + + + +typedef struct imPicHeaderInfo + +{ + + unsigned int magic_number; /* 0x00,0x00,0xe8,0x80(MSBshown)*/ + + unsigned short version_number; /* Zero for current release */ + + unsigned char label[IMPICLABEL]; /* Ascii description of image */ + + long labelptr; /* Ptr to label continuation */ + + unsigned char gap[IMPICGAP]; /* Unused space in header info */ + + unsigned short pic_height; /* Pixel height of full image */ + + unsigned short pic_width; /* Pixel width of full image */ + + unsigned short tile_height; /* Pixel height of each tile */ + + unsigned short tile_width; /* Pixel width of each tile */ + + unsigned short pic_format; /* four bits designating RGBA */ + + unsigned short pic_storage; /* encoding and number of bits */ + + unsigned short block; /* optimum disk transfer chunk */ + + unsigned short alpha; /* matted-to-black:0 unassoc.:1 */ + + unsigned short x_offset; /* horizontal offset for picture*/ + + unsigned short y_offset; /* vertical offest for picture */ + + unsigned char space[IMPICSPACE]; /* mysterious void in header */ + + unsigned int intspace; /* Unused int space */ + + unsigned char unused[IMPICUNUSED]; /* Unused expansion space */ + + long tile_ptr; /* Location of tile */ + + unsigned int tile_len; /* Length of tile */ + +} imPicHeaderInfo; + + + + + +BinField imPicHeaderFields[ ] = + +{ + + { UINT, 4, 1 }, /* Magic number */ + + { USHORT, 2, 1 }, /* Zero for current release */ + + { UCHAR, 1, IMPICLABEL }, /* Ascii description of image */ + + { LONG, 4, 1 }, /* Ptr to label continuation */ + + { UCHAR, 1, IMPICGAP }, /* Unused space in header info */ + + { USHORT, 2, 1 }, /* Pixel height of full image */ + + { USHORT, 2, 1 }, /* Pixel width of full image */ + + { USHORT, 2, 1 }, /* Pixel height of each tile */ + + { USHORT, 2, 1 }, /* Pixel width of each tile */ + + { USHORT, 2, 1 }, /* four bits designating RGBA */ + + { USHORT, 2, 1 }, /* encoding and number of bits */ + + { USHORT, 2, 1 }, /* optimum disk transfer chunk */ + + { USHORT, 2, 1 }, /* matted-to-black:0 unassoc.:1 */ + + { USHORT, 2, 1 }, /* horizontal offset for picture*/ + + { USHORT, 2, 1 }, /* vertical offset for picture */ + + { UCHAR, 1, IMPICSPACE }, /* mysterious void in header */ + + { UINT, 4, 1 }, /* Unused int space */ + + { UCHAR, 1, IMPICUNUSED }, /* Unused expansion space */ + + { LONG, 4, 1 }, /* Location of tile */ + + { UINT, 4, 1 }, /* Length of tile */ + + { 0, 0, 0 } + +}; + + + +/* + + * MACRO + + * imPicFr2Buf - Free two chunks of memory + + * + + * DESCRIPTION + + * Call free two times to free the two chunks of memory pointed to by + + * a-b. + + */ + + + +#define imPicFr2Buf(a,b) free((char*)(a)); free((char*)(b)) + + + +/* + + * MACRO + + * imPicFrTwoBuf - Free two chunks of memory of different types + + * + + * DESCRIPTION + + * Call free two times to free the two chunks of memory pointed to by + + * a-b. + + */ + + + +#define imPicFrTwoBuf(a,b) free((sdsc_uint16*)(a)); free((char*)(b)) + + + + + +#ifdef __STDC__ + +static int imPicReadEncode8( int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable, imPicHeaderInfo *h, int fields ); + +static int imPicReadDump8( int ioType, int fd, FILE* fp, TagTable *flags, TagTable *tagTable, imPicHeaderInfo *h, int fields, int channels ); + +static int imPicReadEncode12( int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable, imPicHeaderInfo *h, int fields ); + +static int imPicReadDump12( int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable, imPicHeaderInfo *h, int fields, int channels ); + +#else + +static int imPicReadEncode8( ); + +static int imPicReadDump8( ); + +static int imPicReadEncode12( ); + +static int imPicReadDump12( ); + +#endif + + + + + +/* + + * FUNCTION + + * imPicRead - read a PIXAR picture file + + * + + * DESCRIPTION + + * The file header is read and the size of the image is detemined. + + * The type of PIXAR storage format and the type of vfb are determined. + + * The appropriate private function is called to allocate the vfb and + + * complete the read of the PIXAR file. + + */ + + + +static int /* Returns # tags read in */ + +#ifdef __STDC__ + +imPicRead( int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable ) + +#else + +imPicRead( ioType, fd, fp, flags, tagTable ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flags; /* Format flags */ + + TagTable *tagTable; /* Tag list to add to */ + +#endif + +{ + + imPicHeaderInfo header; /* PIC file header */ + + int fields; /* VFB alocation information */ + + int channels; /* Num of bytes for each pixel */ + + char message[100]; /* ImInfo message */ + + + + /* + + * PIC files are usually generated on PIXARs, which stores + + * MULTIPLE-byte data with the least significant 8 in the first + + * byte. This is an LBF byte order. + + * There is no floating point in the file so we + + * won't bother setting the float format for BIN. + + */ + + BinByteOrder( BINLBF ); + + + + /* + + * Read in the header. + + */ + + if (ImBinReadStruct(ioType, fd, fp, &header, imPicHeaderFields ) == -1) + + { + + ImReturnBinError(); + + } + + + + /* + + * These lines are used if a flag is set to printout file info. + + */ + + sprintf (message,"%d",header.version_number); + + ImInfo ("Version",message); + + ImInfo ("Byte Order","Least Significant Byte First"); + + sprintf (message, "%d x %d",header.pic_width, header.pic_height); + + ImInfo ("Resolution",message); + + if ( (header.pic_storage == IMPICENCODE12) || + + (header.pic_storage == IMPICDUMP12) ) + + { + + switch ( header.pic_format ) + + { + + case IMFULLRGBA: + + sprintf (message, "48-bit RGB and Alpha"); + + + + case IMRGBBACK: + + sprintf (message, "36-bit RGB"); + + + + case IMSINGLECHAN: + + sprintf (message, "12-bit Grayscale"); + + } + + } + + else + + { + + switch ( header.pic_format ) + + { + + case IMFULLRGBA: + + sprintf (message, "32-bit RGB and Alpha"); + + + + case IMRGBBACK: + + sprintf (message, "24-bit RGB"); + + + + case IMSINGLECHAN: + + sprintf (message, "8-bit Grayscale"); + + } + + } + + ImInfo( "Type", message ); + + if ( (header.pic_storage == IMPICENCODE12) || + + (header.pic_storage == IMPICENCODE8) ) + + ImInfo ("Compression Type","Run Length Encoded (RLE)") + + else + + ImInfo ("Compression Type","none (dump)"); + + if (header.pic_format==IMFULLRGBA) ImInfo ("Alpha Channel","8-bit") + + else ImInfo ("Alpha Channel","none"); + + + + + + /* + + * Check the important header parameters for picture format + + * (1) Were we given a pixar PIC picture file ? + + */ + + if ( header.magic_number != IMPICNUMBER ) + + { + + ImErrNo = IMEMAGIC; + + ImErrorFatal ( ImQError(), -1, ImErrNo ); + + } + + + + /* + + * (2) Do we have just ONE tile ? + + * (NOTE: This is a temporary consideration for multiple tiles ) + + */ + + if ( ( header.pic_width != header.tile_width ) || + + ( header.pic_height != header.tile_height ) ) + + { + + ImErrorFatal( "PIXAR pic files with multiple tiles not yet implemented", + + -1, IMESYNTAX ); + + } + + + + /* + + * (3) What pixar picture format have we been given? + + * Set the appropriate vfb fields for future construction. + + */ + + switch ( header.pic_format ) + + { + + case IMFULLRGBA: + + + + fields = IMVFBRGB | IMVFBALPHA; + + channels = 4; + + break; + + + + case IMRGBBACK: + + + + fields = IMVFBRGB; + + channels = 3; + + break; + + + + case IMSINGLECHAN: + + + + if ( (header.pic_storage == IMPICENCODE12) || + + (header.pic_storage == IMPICDUMP12) ) + + fields = IMVFBINDEX16; + + + + else + + fields = IMVFBINDEX8; + + + + channels = 1; + + break; + + + + default: + + + + ImErrNo = IMESYNTAX; + + ImErrorFatal( "Unknown Pic picture format type", -1, ImErrNo ); + + } + + + + /* + + * (4) How is the pixar picture file stored? + + * Call specific routine for each case. + + */ + + switch ( header.pic_storage ) + + { + + case IMPICENCODE8: + + + + return ( imPicReadEncode8 ( ioType, fd, fp, flags, tagTable, + + &header, fields ) ); + + + + case IMPICDUMP8: + + + + return ( imPicReadDump8 ( ioType, fd, fp, flags, tagTable, + + &header, fields, channels ) ); + + + + case IMPICENCODE12: + + + + return ( imPicReadEncode12 ( ioType, fd, fp, flags, tagTable, + + &header, fields ) ); + + + + case IMPICDUMP12: + + + + return ( imPicReadDump12 ( ioType, fd, fp, flags, tagTable, + + &header, fields, channels ) ); + + + + default: + + + + ImErrNo = IMESYNTAX; + + ImErrorFatal( "Uknown Pic storage mode", -1, ImErrNo ); + + } + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imPicReadEncode8 - read a PIXAR 8-bit encoded picture file + + * + + * DESCRIPTION + + * Space is allocated for the VFB and the PIXAR pixel packets are + + * decoded and expanded if necessary into run buffers of straight + + * RGB values. These values are transfered to the VFB and the + + * completed VFB added to the tag list. + + */ + + + +static int /* Returns # tags read in */ + +#ifdef __STDC__ + +imPicReadEncode8( int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable, imPicHeaderInfo *h, int fields ) + +#else + +imPicReadEncode8( ioType, fd, fp, flags, tagTable, h, fields ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flags; /* Format flags */ + + TagTable *tagTable; /* Tag list to add to */ + + imPicHeaderInfo *h; /* The Pixar picture header */ + + int fields; /* The flags for the vfb */ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointer */ + + unsigned char *info; /* Buffer for PIXAR pic data */ + + unsigned char *infoptr; /* Pointer to info data */ + + int x,y; /* Convenient short names */ + + int i; /* Loop counters */ + + int lcnt; /* Number of pixels in line */ + + int byte1; /* 1st byte of 16-bit id */ + + int byte2; /* 2nd byte of 16-bit id */ + + int flag; /* Signifies the type of packet */ + + int bytecount; /* Tracks bytes for block read */ + + int count; /* Number of pixel/runs in pack */ + + int lambda; /* Number of pixels in run */ + + unsigned char alpha; /* Holds constant alpha values */ + + + + x = h->pic_width; + + y = h->pic_height; + + bytecount = 0; + + + + /* + + * Allocate a VFB of the required size. + + */ + + if ( (vfb = ImVfbAlloc( x, y, fields )) == IMVFBNULL ) + + { + + ImErrNo = IMEMALLOC; + + ImErrorFatal( ImQError(), -1, ImErrNo ); + + } + + + + /* + + * Position the pointer at the beginning of the first scanline + + */ + + pptr = ImVfbQFirst( vfb ); + + + + /* + + * Allocate the data input buffer for the info to be read + + */ + + ImMalloc( info, unsigned char *, h->block); + + infoptr = info; + + + + /* + + * First read in unused portion up to the first tile + + */ + + if ( (h->tile_ptr - IMPICHEADERSIZE) > 0 ) + + { + + if ( ioType & IMFILEIOFILE ) + + { + + ImSeek(ioType,fd,fp,h->tile_ptr-IMPICHEADERSIZE, 1 ); + + } + + else + + { + + unsigned char *empty; /* Local unused section of pic data */ + + + + ImMalloc(empty, unsigned char*, h->tile_ptr-IMPICHEADERSIZE ); + + if (ImBinRead(ioType, fd, fp, empty, UCHAR, + + 1, h->tile_ptr - IMPICHEADERSIZE) == -1) + + { + + free( (char *)empty ); + + free( (char *)info ); + + ImReturnBinError(); + + } + + + + free( (char *)empty ); + + } + + } + + + + /* + + * Then read first block of pixel info + + * (note: if pixel info does not start at logical beginning of a + + * block, a full block of info will still be read in ) + + */ + + if (ImBinRead(ioType, fd, fp, info, UCHAR, 1, h->block)==-1) + + { + + free( (char *)info ); + + ImReturnBinError(); + + } + + + + /* + + * Determine what PIC file format we have. + + * In each case: + + * Loop through the scan lines. Decode packets of info for each line. + + * Placing each decoded packet directly into the vfb. + + * + + * (NOTE: No default needed in switch as pic_format checked before + + * this procedure) + + */ + + switch ( h->pic_format ) + + { + + case IMFULLRGBA: + + + + for ( i=0; i> 4; + + count = ( (byte2 & 0x0f) << 8 ) | byte1; + + + + + + switch ( flag ) + + { + + case 1: + + lcnt = lcnt + count +1; + + while ( count-- >= 0 ) + + { + + ImVfbSRed( vfb, pptr, (*infoptr++) ); + + ImVfbSGreen( vfb, pptr, (*infoptr++) ); + + ImVfbSBlue( vfb, pptr, (*infoptr++) ); + + ImVfbSAlpha( vfb, pptr, (*infoptr++) ); + + + + + + ImVfbSInc( vfb, pptr ); + + bytecount += 4; + + } + + break; + + + + case 2: + + while ( count-- >= 0 ) + + { + + lambda = *infoptr++; + + bytecount++; + + + + + + lcnt = lcnt + lambda +1; + + while ( lambda-- >= 0 ) + + { + + ImVfbSRed( vfb, pptr, (*(infoptr)) ); + + ImVfbSGreen( vfb, pptr, (*(infoptr+1))); + + ImVfbSBlue( vfb, pptr, (*(infoptr+2))); + + ImVfbSAlpha( vfb, pptr, (*(infoptr+3))); + + ImVfbSInc( vfb, pptr ); + + } + + + + + + infoptr += 4; + + bytecount += 4; + + } + + break; + + + + case 3: + + alpha = *infoptr++; + + bytecount++; + + + + lcnt = lcnt + count +1; + + while ( count-- >= 0 ) + + { + + ImVfbSRed( vfb, pptr, (*infoptr++) ); + + ImVfbSGreen( vfb, pptr, (*infoptr++) ); + + ImVfbSBlue( vfb, pptr, (*infoptr++) ); + + ImVfbSAlpha( vfb, pptr, alpha ); + + + + + + ImVfbSInc( vfb, pptr ); + + bytecount += 3; + + } + + break; + + + + case 4: + + alpha = *infoptr++; + + bytecount++; + + + + while ( count-- >= 0 ) + + { + + lambda = *infoptr++; + + bytecount++; + + + + + + lcnt = lcnt + lambda +1; + + while ( lambda-- >= 0 ) + + { + + ImVfbSRed( vfb, pptr, (*(infoptr)) ); + + ImVfbSGreen( vfb, pptr, (*(infoptr+1))); + + ImVfbSBlue( vfb, pptr, (*(infoptr+2))); + + ImVfbSAlpha( vfb, pptr, alpha ); + + ImVfbSInc( vfb, pptr ); + + } + + + + infoptr += 3; + + bytecount += 3; + + } + + break; + + + + case 0: + + /* + + * This signifies end of block + + */ + + bytecount = h->block; + + break; + + + + default: + + ImErrNo = IMESYNTAX; + + ImErrorFatal( ImQError(), -1, ImErrNo ); + + } + + + + + + /* If the end of a block has been reached, + + * read next block of picture info. + + */ + + if ( bytecount >= h->block-2 ) + + { + + + + + + if (ImBinRead(ioType, fd, fp, info, UCHAR, + + 1, h->block) == -1) + + { + + free( (char *)info ); + + ImReturnBinError(); + + } + + + + infoptr = info; + + bytecount = 0; + + + + + + } + + } + + } + + + + break; + + + + case IMRGBBACK: + + + + for ( i=0; i> 4; + + count = ( (byte2 & 0x0f) << 8 ) | byte1; + + + + + + switch ( flag ) + + { + + case 1: + + lcnt = lcnt + count +1; + + while ( count-- >= 0 ) + + { + + ImVfbSRed( vfb, pptr, (*infoptr++) ); + + ImVfbSGreen( vfb, pptr, (*infoptr++) ); + + ImVfbSBlue( vfb, pptr, (*infoptr++) ); + + + + ImVfbSInc( vfb, pptr ); + + bytecount += 3; + + } + + break; + + + + case 2: + + while ( count-- >= 0 ) + + { + + lambda = *infoptr++; + + bytecount++; + + + + + + lcnt = lcnt + lambda +1; + + while ( lambda-- >= 0 ) + + { + + ImVfbSRed( vfb, pptr, (*(infoptr)) ); + + ImVfbSGreen( vfb, pptr, (*(infoptr+1))); + + ImVfbSBlue( vfb, pptr, (*(infoptr+2))); + + ImVfbSInc( vfb, pptr ); + + } + + + + + + infoptr += 3; + + bytecount += 3; + + } + + break; + + + + case 0: + + /* + + * This signifies end of block + + */ + + bytecount = h->block; + + break; + + + + default: + + ImErrNo = IMESYNTAX; + + ImErrorFatal( ImQError(), -1, ImErrNo ); + + } + + + + + + /* + + * If the end of a block has been reached, + + * read next block of picture info. + + */ + + if ( bytecount >= h->block-2 ) + + { + + + + + + if (ImBinRead(ioType, fd, fp, info, UCHAR, + + 1, h->block) == -1) + + { + + free( (char *)info ); + + ImReturnBinError(); + + } + + + + infoptr = info; + + bytecount = 0; + + + + + + } + + } + + } + + + + break; + + + + case IMSINGLECHAN: + + + + for ( i=0; i> 4; + + count = ( (byte2 & 0x0f) << 8 ) | byte1; + + + + switch ( flag ) + + { + + case 1: + + lcnt = lcnt + count +1; + + while ( count-- >= 0 ) + + { + + ImVfbSIndex8( vfb, pptr, (*infoptr++) ); + + ImVfbSInc( vfb, pptr ); + + bytecount += 1; + + } + + break; + + + + case 2: + + while ( count-- >= 0 ) + + { + + lambda = *infoptr++; + + bytecount++; + + + + lcnt = lcnt + lambda +1; + + while ( lambda-- >= 0 ) + + { + + ImVfbSIndex8( vfb, pptr, (*infoptr) ); + + ImVfbSInc( vfb, pptr ); + + } + + + + infoptr++; + + bytecount++; + + } + + break; + + + + case 0: + + /* + + * This signifies end of block + + */ + + bytecount = h->block; + + break; + + + + default: + + ImErrNo = IMESYNTAX; + + ImErrorFatal( ImQError(), -1, ImErrNo ); + + } + + + + /* + + * If the end of a block has been reached, + + * read next block of picture info. + + */ + + if ( bytecount >= h->block-2 ) + + { + + + + if (ImBinRead(ioType, fd, fp, info, UCHAR, + + 1, h->block) == -1) + + { + + free( (char *)info ); + + ImReturnBinError(); + + } + + + + infoptr = info; + + bytecount = 0; + + + + } + + } + + } + + } + + + + /* + + * Free the pixel storage arrays + + */ + + free( (char *)info ); + + + + /* + + * Add the VFB to the tagTable. + + */ + + TagTableAppend( tagTable, TagEntryAlloc( "image vfb", POINTER, &vfb ) ); + + + + + + return ( 1 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imPicReadDump8 - read a PIXAR 8-bit dumped picture file + + * + + * DESCRIPTION + + * Space is allocated for the VFB and the PIXAR dumped pixels are + + * are directly transfered to the VFB based on the number of channels + + * and the completed VFB is added to the tag list. + + */ + + + +static int /* Returns # tags read in */ + +#ifdef __STDC__ + +imPicReadDump8( int ioType, int fd, FILE* fp, TagTable *flags, TagTable *tagTable, imPicHeaderInfo *h, int fields, int channels ) + +#else + +imPicReadDump8( ioType, fd, fp, flags, tagTable, h, fields, channels ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flags; /* Format flags */ + + TagTable *tagTable; /* Tag list to add to */ + + imPicHeaderInfo *h; /* The Pixar picture header */ + + int fields; /* The flags for the vfb */ + + int channels; /* The Pixar channel format */ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointer */ + + unsigned char *info; /* Buffer for PIXAR pic data */ + + unsigned char *infoHead; /* The start of the info buffer */ + + unsigned char *empty; /* Buffer for unused area */ + + int x,y; /* Convenient short names */ + + int i,j; /* Loop counters */ + + + + x = h->pic_width; + + y = h->pic_height; + + + + /* + + * Allocate a VFB of the required size. + + */ + + if ( (vfb = ImVfbAlloc( x, y, fields )) == IMVFBNULL ) + + { + + ImErrNo = IMEMALLOC; + + ImErrorFatal( ImQError(), -1, ImErrNo ); + + } + + + + /* + + * Position the pointer at the beginning of the first scanline + + */ + + pptr = ImVfbQFirst( vfb ); + + + + /* + + * Allocate buffer large enough for one scanline of pixel info + + * and for the unused portion between the header and dumped pixels. + + * Store start of info buffer for future reads. + + */ + + ImMalloc( info, unsigned char *, (channels*x)); + + infoHead = info; + + + + /* + + * First read in unused portion up to the first tile + + */ + + if ( (h->tile_ptr - IMPICHEADERSIZE) > 0 ) + + { + + if ( ioType & IMFILEIOFILE ) + + { + + ImSeek(ioType,fd,fp,h->tile_ptr-IMPICHEADERSIZE, 1 ); + + } + + else + + { + + ImMalloc(empty, unsigned char*, h->tile_ptr - IMPICHEADERSIZE ); + + if (ImBinRead(ioType, fd, fp, empty, UCHAR, + + 1, h->tile_ptr - IMPICHEADERSIZE) == -1) + + { + + free( (char *)empty ); + + free( (char *)info ); + + ImReturnBinError(); + + } + + } + + } + + + + /* + + * Determine what PIC Image format we have and + + * Loop through the scan lines. Dump pixels from info for each line + + * into the vfb based on # of channels. + + * + + * (NOTE: No default needed as the error checking for form done before) + + */ + + switch ( h->pic_format ) + + { + + case IMFULLRGBA: + + + + for ( i=0; ipic_width; + + y = h->pic_height; + + bytecount = 0; + + + + /* + + * Allocate a VFB of the required size. + + */ + + if ( (vfb = ImVfbAlloc( x, y, fields )) == IMVFBNULL ) + + { + + ImErrNo = IMEMALLOC; + + ImErrorFatal( ImQError(), -1, ImErrNo ); + + } + + + + /* + + * Position the pointer at the beginning of the first scanline + + */ + + pptr = ImVfbQFirst( vfb ); + + + + /* + + * Allocate the data input buffer for the info to be read + + */ + + ImMalloc( info, sdsc_uint16 *, h->block); + + infoptr = info; + + + + /* + + * First read in unused portion up to the first tile + + */ + + if ( (h->tile_ptr - IMPICHEADERSIZE) > 0 ) + + { + + if ( ioType & IMFILEIOFILE ) + + { + + ImSeek(ioType,fd,fp,h->tile_ptr-IMPICHEADERSIZE, 1); + + } + + else + + { + + ImMalloc(empty, unsigned char*, h->tile_ptr - IMPICHEADERSIZE ); + + if (ImBinRead(ioType, fd, fp, empty, UCHAR, + + 1, h->tile_ptr - IMPICHEADERSIZE) == -1) + + { + + free( (char *)empty ); + + free( (char *)info ); + + ImReturnBinError(); + + } + + } + + } + + + + /* + + * Then read first block of pixel info + + */ + + if (ImBinRead(ioType, fd, fp, info, UINT16, 2, h->block/2) == -1) + + { + + free( (char *)info ); + + ImReturnBinError(); + + } + + + + /* + + * Determine what PIC file format we have. + + * In each case: + + * Loop through the scan lines. Decode packets of info for each line. + + * Placing each decoded packet directly into the vfb. + + * + + * (NOTE: No default needed as pic_format checked before this procedure) + + */ + + switch ( h->pic_format ) + + { + + case IMFULLRGBA: + + + + /* + + * Since Vfb can only hold 8-bit RGB values, the 12-bit values + + * must be truncated. Need to warn the user that this occurs + + */ + + ImErrorWarning( "12-bit data being reduced to 8-bit data", + + -1, IMESYNTAX ); + + + + for ( i=0; i count<8:11> count<0:7> + + * (Note: this is flipped from the byte listing + + * in descriptions above) + + */ + + flag = twobyte >> 12; + + count = twobyte & 0x0fff; + + + + switch ( flag ) + + { + + case 1: + + lcnt = lcnt + count +1; + + while ( count-- >= 0 ) + + { + + ImVfbSRed( vfb, pptr, ((*infoptr++)>>4) ); + + ImVfbSGreen( vfb, pptr, ((*infoptr++)>>4) ); + + ImVfbSBlue( vfb, pptr, ((*infoptr++)>>4) ); + + ImVfbSAlpha( vfb, pptr, ((*infoptr++)>>4) ); + + ImVfbSInc( vfb, pptr ); + + bytecount += 8; + + } + + break; + + + + case 2: + + while ( count-- >= 0 ) + + { + + lambda = *infoptr++; + + bytecount += 2; + + + + lcnt = lcnt + lambda +1; + + while ( lambda-- >= 0 ) + + { + + ImVfbSRed( vfb,pptr,((*(infoptr))>>4) ); + + ImVfbSGreen(vfb,pptr,((*(infoptr+1))>>4)); + + ImVfbSBlue( vfb,pptr,((*(infoptr+2))>>4)); + + ImVfbSAlpha(vfb,pptr,((*(infoptr+3))>>4)); + + ImVfbSInc( vfb,pptr ); + + } + + + + infoptr += 4; + + bytecount += 8; + + } + + break; + + + + case 3: + + alpha = *infoptr++; + + bytecount += 2; + + + + lcnt = lcnt + count +1; + + while ( count-- >= 0 ) + + { + + ImVfbSRed( vfb, pptr, ((*infoptr++)>>4) ); + + ImVfbSGreen( vfb, pptr, ((*infoptr++)>>4) ); + + ImVfbSBlue( vfb, pptr, ((*infoptr++)>>4) ); + + ImVfbSAlpha( vfb, pptr, alpha ); + + ImVfbSInc( vfb, pptr ); + + bytecount += 6; + + } + + break; + + + + case 4: + + alpha = *infoptr++; + + bytecount += 2; + + + + while ( count-- >= 0 ) + + { + + lambda = *infoptr++; + + bytecount += 2; + + + + lcnt = lcnt + lambda +1; + + while ( lambda-- >= 0 ) + + { + + ImVfbSRed( vfb,pptr,((*(infoptr))>>4) ); + + ImVfbSGreen(vfb,pptr,((*(infoptr+1))>>4)); + + ImVfbSBlue( vfb,pptr,((*(infoptr+2))>>4)); + + ImVfbSAlpha( vfb, pptr, alpha ); + + ImVfbSInc( vfb, pptr ); + + } + + + + infoptr += 3; + + bytecount += 6; + + } + + break; + + + + case 0: + + /* + + * This signifies end of block + + */ + + bytecount = h->block; + + break; + + + + default: + + ImErrNo = IMESYNTAX; + + ImErrorFatal( ImQError(), -1, ImErrNo ); + + } + + + + /* If the end of a block has been reached, + + * read next block of picture info. + + */ + + if ( bytecount >= h->block-2 ) + + { + + if (ImBinRead(ioType, fd, fp, info, UINT16, + + 2, h->block/2 ) == -1) + + { + + free( (char *)info ); + + ImReturnBinError(); + + } + + + + infoptr = info; + + bytecount = 0; + + } + + } + + } + + + + break; + + + + case IMRGBBACK: + + + + /* + + * Since Vfb can only hold 8-bit RGB values, the 12-bit values + + * must be truncated. Need to warn the user that this occurs + + */ + + ImErrorWarning( "12-bit data being reduced to 8-bit data", + + -1, IMESYNTAX ); + + + + for ( i=0; i count<8:11> count<0:7> + + * (Note: this is flipped from the byte listing + + * in descriptions above) + + */ + + flag = twobyte >> 12; + + count = twobyte & 0x0fff; + + + + switch ( flag ) + + { + + case 1: + + lcnt = lcnt + count +1; + + while ( count-- >= 0 ) + + { + + ImVfbSRed( vfb, pptr, ((*infoptr++)>>4) ); + + ImVfbSGreen( vfb, pptr, ((*infoptr++)>>4) ); + + ImVfbSBlue( vfb, pptr, ((*infoptr++)>>4) ); + + ImVfbSInc( vfb, pptr ); + + bytecount += 6; + + } + + break; + + + + case 2: + + while ( count-- >= 0 ) + + { + + lambda = *infoptr++; + + bytecount += 2; + + + + lcnt = lcnt + lambda +1; + + while ( lambda-- >= 0 ) + + { + + ImVfbSRed( vfb,pptr,((*(infoptr))>>4) ); + + ImVfbSGreen(vfb,pptr,((*(infoptr+1))>>4)); + + ImVfbSBlue( vfb,pptr,((*(infoptr+2))>>4)); + + ImVfbSInc( vfb,pptr ); + + } + + + + infoptr += 3; + + bytecount += 6; + + } + + break; + + + + case 0: + + /* + + * This signifies end of block + + */ + + bytecount = h->block; + + break; + + + + default: + + ImErrNo = IMESYNTAX; + + ImErrorFatal( ImQError(), -1, ImErrNo ); + + } + + + + /* If the end of a block has been reached, + + * read next block of picture info. + + */ + + if ( bytecount >= h->block-2 ) + + { + + if (ImBinRead(ioType, fd, fp, info, UINT16, + + 2, h->block/2 ) == -1) + + { + + free( (char *)info ); + + ImReturnBinError(); + + } + + + + infoptr = info; + + bytecount = 0; + + } + + } + + } + + + + case IMSINGLECHAN: + + + + for ( i=0; i count<8:11> count<0:7> + + * (Note: this is flipped from the byte listing + + * in descriptions above) + + */ + + flag = twobyte >> 12; + + count = twobyte & 0x0fff; + + + + switch ( flag ) + + { + + case 1: + + lcnt = lcnt + count +1; + + while ( count-- >= 0 ) + + { + + ImVfbSIndex16( vfb, pptr, (*infoptr++) ); + + ImVfbSInc( vfb, pptr ); + + bytecount += 2; + + } + + break; + + + + case 2: + + while ( count-- >= 0 ) + + { + + lambda = *infoptr++; + + bytecount++; + + + + lcnt = lcnt + lambda +1; + + while ( lambda-- >= 0 ) + + { + + ImVfbSIndex16( vfb, pptr, (*infoptr) ); + + ImVfbSInc( vfb, pptr ); + + } + + + + infoptr++; + + bytecount +=2; + + } + + break; + + + + case 0: + + /* + + * This signifies end of block + + */ + + bytecount = h->block; + + break; + + + + default: + + ImErrNo = IMESYNTAX; + + ImErrorFatal( ImQError(), -1, ImErrNo ); + + } + + + + /* + + * If the end of a block has been reached, + + * read next block of picture info. + + */ + + if ( bytecount >= h->block-2 ) + + { + + + + if (ImBinRead(ioType, fd, fp, info, UINT16, + + 2, h->block/2 ) == -1) + + { + + free( (char *)info ); + + ImReturnBinError(); + + } + + + + infoptr = info; + + bytecount = 0; + + + + } + + } + + } + + } + + + + /* + + * Free the pixel storage arrays picture data + + */ + + free( (char *)info ); + + + + /* + + * Add the VFB to the tagTable. + + */ + + TagTableAppend( tagTable, TagEntryAlloc( "image vfb", POINTER, &vfb ) ); + + + + return ( 1 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imPicReadDump12 - read a PIXAR 12-bit dumped picture file + + * + + * DESCRIPTION + + * Space is allocated for the VFB and the PIXAR dumped pixels are + + * directly transfered to the VFB based on the number of channels + + * after reducing info to an 8 bit representation, and then + + * the completed VFB is added to the tag list. + + * + + * (Note: that the 12-bit pixel values in info are first reduced to + + * an 8-bit representation before being transfered to the + + * the appropriate vfb color indicators. ) + + */ + + + +static int /* Returns # tags read in */ + +#ifdef __STDC__ + +imPicReadDump12( int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable, imPicHeaderInfo *h, int fields, int channels ) + +#else + +imPicReadDump12( ioType, fd, fp, flags, tagTable, h, fields, channels ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flags; /* Format flags */ + + TagTable *tagTable; /* Tag list to add to */ + + imPicHeaderInfo *h; /* The Pixar picture header */ + + int fields; /* The flags for the vfb */ + + int channels; /* The Pixar picture format */ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointer */ + + sdsc_uint16 *info; /* Buffer for PIXAR pic data */ + + sdsc_uint16 *infoHead; /* The start of the info buffer */ + + unsigned char *empty; /* Buffer for unused area */ + + int x,y; /* Convenient short names */ + + int i,j; /* Loop counters */ + + + + x = h->pic_width; + + y = h->pic_height; + + + + /* + + * Allocate a VFB of the required size. + + */ + + if ( (vfb = ImVfbAlloc( x, y, fields )) == IMVFBNULL ) + + { + + ImErrNo = IMEMALLOC; + + ImErrorFatal( ImQError(), -1, ImErrNo ); + + } + + + + /* + + * Position the pointer at the beginning of the first scanline + + */ + + pptr = ImVfbQFirst( vfb ); + + + + /* + + * Allocate buffer large enough for one scanline of pixel info + + * (NOTE: 12 bit data stored in TWO bytes ) + + * and for the unused portion between the header and dumped pixels. + + * Save start point of info buffer. + + */ + + ImMalloc( info, sdsc_uint16 *, (2*channels*x) ); + + infoHead = info; + + + + /* + + * First read in unused portion up to the first tile + + */ + + if ( (h->tile_ptr - IMPICHEADERSIZE) > 0 ) + + { + + if ( ioType & IMFILEIOFILE ) + + { + + ImSeek(ioType,fd,fp,h->tile_ptr-IMPICHEADERSIZE, 1 ); + + } + + else + + { + + ImMalloc( empty, unsigned char *, h->block-IMPICHEADERSIZE); + + if (ImBinRead(ioType, fd, fp, empty, UCHAR, + + 1, h->tile_ptr - IMPICHEADERSIZE) == -1) + + { + + free( (char *)empty ); + + free( (char *)info ); + + ImReturnBinError(); + + } + + } + + } + + + + /* + + * Determine what PIC Image format we have and + + * Loop through the scan lines. Dump pixels from info for each line + + * into the vfb based on # of channels. + + * + + * (NOTE: No default needed as error checking for format done before) + + */ + + switch ( h->pic_format ) + + { + + case IMFULLRGBA: + + + + /* + + * Since Vfb can only hold 8-bit RGB values, the 12-bit values + + * must be truncated. Need to warn the user that this occurs + + */ + + ImErrorWarning( "12-bit data being reduced to 8-bit data", + + -1, IMESYNTAX ); + + + + for ( i=0; i> 4) ); + + ImVfbSGreen( vfb, pptr, ((*info++) >> 4) ); + + ImVfbSBlue( vfb, pptr, ((*info++) >> 4) ); + + ImVfbSAlpha( vfb, pptr, ((*info++) >> 4) ); + + ImVfbSInc( vfb, pptr ); + + } + + + + info = infoHead; + + } + + break; + + + + case IMRGBBACK: + + + + /* + + * Since Vfb can only hold 8-bit RGB values, the 12-bit values + + * must be truncated. Need to warn the user that this occurs + + */ + + ImErrorWarning( "12-bit data being reduced to 8-bit data", + + -1, IMESYNTAX ); + + + + for ( i=0; i> 4) ); + + ImVfbSGreen( vfb, pptr, ((*info++) >> 4) ); + + ImVfbSBlue( vfb, pptr, ((*info++) >> 4) ); + + ImVfbSInc( vfb, pptr ); + + } + + + + info = infoHead; + + } + + break; + + + + case IMSINGLECHAN: + + + + for ( i=0; i=(IMPICBLOCK-4) ) + + { + + /* + + * The blocksize will be exceeded if we + + * fill the buffer with next packet, and + + * since packets cannot span successive + + * blocks, the current buffer needs to + + * be written and the remainder of + + * the block filled with 0's. + + */ + + + + /* + + * A rare case occurs if the end of a + + * scanline was previously written and + + * the next packet to fill would cause + + * block to overflow; thus, signify + + * end of block + + */ + + if ( numToWrite == 2 ) + + { + + count = 0; + + flag = 0; + + } + + + + /* + + * store count and flag bits and + + * update count for subsequent check + + */ + + *bufStart = (unsigned char) (count & 0xff); + + *(bufStart+1) = (unsigned char) ((flag << 4) + (count >> 8) ); + + numWritten += numToWrite; + + + + /* + + * Fill out remainder of block + + * with zero's which signifies end. + + */ + + if ( numWritten < IMPICBLOCK ) + + { + + k = IMPICBLOCK - numWritten; + + memset( (void *)buf, 0x00, k ); + + numToWrite += k; + + buf += k; + + } + + + + /* + + * Write stored buffer runlength info + + */ + + if(ImBinWrite(ioType,fd,fp,bufStart, + + UCHAR,1,numToWrite) == -1 ) + + { + + free(bufStart); + + ImReturnBinError(); + + } + + + + totWritten += numToWrite; + + + + /* + + * Reset variables for next block + + */ + + buf = bufStart+2; + + count = (unsigned int)-1; + + numToWrite = 2; + + numWritten = 0; + + if ( flag == 0 ) flag = 2; + + + + } + + + + /* + + * Fill buffer with next packet info + + */ + + count ++; + + *buf++ = reps; + + *buf++ = redBuf[0]; + + *buf++ = grnBuf[0]; + + *buf++ = bluBuf[0]; + + *buf++ = alpBuf[0]; + + numToWrite += 5; + + + + reps = 0; + + + + /* + + * Reset flag and set current pixel as the + + * pixel to be compared to for future runs. + + */ + + fillBuf = 0; + + redBuf[0] = redBuf[1]; + + grnBuf[0] = grnBuf[1]; + + bluBuf[0] = bluBuf[1]; + + alpBuf[0] = alpBuf[1]; + + } + + } + + + + /* + + * Completed scanline. Write out packed 16 bits (flag & count) + + * and info of packets contained in buffer. + + */ + + + + /* + + * Fill buffer with next packet info + + */ + + count ++; + + *buf++ = reps; + + *buf++ = redBuf[0]; + + *buf++ = grnBuf[0]; + + *buf++ = bluBuf[0]; + + *buf++ = alpBuf[0]; + + + + + + numToWrite += 5; + + + + *bufStart = (unsigned char) (count & 0xff); + + *(bufStart+1) = (unsigned char) ((flag << 4) + (count >> 8)); + + + + + + if(ImBinWrite(ioType,fd,fp,bufStart,UCHAR,1,numToWrite)== -1 ) + + { + + free( bufStart ); + + ImReturnBinError(); + + } + + + + buf = bufStart+2; + + numWritten += numToWrite; + + totWritten += numToWrite; + + } + + + + + + /* + + * The rest of the last block needs to be completed so that any block + + * reads that do not check for the end of the file can read blindly + + * NOTE: Some tests were performed on the technique to just close the + + * last block with 4 zero's ( which signify the end of the block ), + + * but just in case some applications may get confused if the + + * block is not totally complete, the remainder of the block will + + * be filled with zeros. + + */ + + if ( numWritten < IMPICBLOCK ) + + { + + unsigned char *buf; + + + + numToWrite = IMPICBLOCK - numWritten; + + ImMalloc( buf, unsigned char*, numToWrite ); + + memset( (void *)buf, 0x00, numToWrite ); + + + + if(ImBinWrite(ioType,fd,fp,buf,UCHAR,1,numToWrite)== -1 ) + + { + + free( buf ); + + ImReturnBinError(); + + } + + + + totWritten += numToWrite; + + free( buf ); + + } + + + + /* + + * Now that all the image data has been written, the header must + + * be written; thus, seek back to the start of the file and write it + + */ + + ImSeek( ioType, fd, fp, 0, 0 ); + + + + /* + + * Write the PIXAR picture file header. + + */ + + if ( imPicWriteHeader( ioType, fd, fp, vfb, IMFULLRGBA, IMPICENCODE8, IMISALPHA, totWritten ) == -1 ) + + { + + return ( -1 ); /* Error already handled */ + + } + + + + free( bufStart ); + + + + + + + + return ( 1 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imPicWriteRGB - write a three-channel PIC file, dump + + * imPicWriteRGBRLE - write a three-channel PIC file, encoded + + * + + * DESCRIPTION + + * Vfb pixel info is read into buffers and is either run length encoded + + * by counting the number of repitiions on each scanline, or the info + + * is dumped directly into the PIXAR Picture file. + + */ + + + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imPicWriteRGB(ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable) + +#else + +imPicWriteRGB( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Format flags */ + + TagTable *tagTable; /* Tag list to read from */ + +#endif + +{ + + ImVfb *vfb; /* Image to write */ + + ImVfbPtr pptr; /* Pixel pointer */ + + unsigned char *buf; /* Run buffer */ + + unsigned char *bufStart; /* Pointer to first loc in buf */ + + int i,j; /* Loop counters */ + + int x,y; /* Loop bounds */ + + + + /* + + * PIC files are usually generated on PIXARs, which stores + + * MULTIPLE-byte data with the least significant 8 in the first + + * byte. This is an LBF byte order. + + * There is no floating point in the file so we + + * won't bother setting the float format for BIN. + + */ + + BinByteOrder( BINLBF ); + + + + /* + + * Initialize the things we need + + */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + x = ImVfbQWidth( vfb ); + + y = ImVfbQHeight( vfb ); + + pptr = ImVfbQFirst( vfb ); + + + + /* + + * Write the PIXAR picture file header. + + * NOTE: we can pre-compute the length of the pixel data since + + * we know the each pixel written will have 3 bytes associated + + * with it one for R,G,and B. Therefore, Length = 3*x*y + + */ + + if ( imPicWriteHeader( ioType, fd, fp, vfb, IMRGBBACK, IMPICDUMP8, IMNOALPHA, (3*x*y) ) == -1 ) + + return ( -1 ); /* Error already handled */ + + + + /* Allocate a buffer big enough for one scanline, read pixel info + + * from vfb, and write it directly to pixar picture file. + + */ + + ImMalloc( bufStart, unsigned char*, 3*x ); + + + + for( i=0; i=(IMPICBLOCK-4) ) + + { + + /* + + * The blocksize will be exceeded if we + + * fill the buffer with next packet, and + + * since packets cannot span successive + + * blocks, the current buffer needs to + + * be written and the remainder of + + * the block filled with 0's. + + */ + + + + /* + + * A rare case occurs if the end of a + + * scanline was previously written and + + * the next packet to fill would cause + + * block to overflow -- + + * signify end of block + + */ + + if ( numToWrite == 2 ) + + { + + count = 0; + + flag = 0; + + } + + + + /* + + * store count and flag bits and + + * update count for subsequent check + + */ + + *bufStart = (unsigned char) (count & 0xff); + + *(bufStart+1) = (unsigned char) ((flag << 4) + + + (count >> 8) ); + + + + + + numWritten += numToWrite; + + + + /* + + * Fill out remainder of block + + * with zero's to signify end. + + */ + + if ( numWritten < IMPICBLOCK ) + + { + + k = IMPICBLOCK - numWritten; + + memset( (void *)buf, 0x00, k ); + + numToWrite += k; + + buf += k; + + } + + + + /* + + * Write stored buffer runlength info + + */ + + if(ImBinWrite(ioType,fd,fp,bufStart, + + UCHAR,1,numToWrite) == -1 ) + + { + + free(bufStart); + + ImReturnBinError(); + + } + + + + totWritten += numToWrite; + + + + /* + + * Reset variables for next block + + */ + + buf = bufStart+2; + + count = (unsigned int)-1; + + numToWrite = 2; + + numWritten = 0; + + if ( flag == 0 ) flag = 2; + + + + } + + + + /* + + * Fill buffer with next packet info + + */ + + count ++; + + *buf++ = reps; + + *buf++ = redBuf[0]; + + *buf++ = grnBuf[0]; + + *buf++ = bluBuf[0]; + + numToWrite += 4; + + + + + + reps = 0; + + + + /* + + * Reset flag and set current pixel as the + + * pixel to be compared to for future runs. + + */ + + fillBuf = 0; + + redBuf[0] = redBuf[1]; + + grnBuf[0] = grnBuf[1]; + + bluBuf[0] = bluBuf[1]; + + } + + } + + + + /* + + * Completed scanline. Write out packed 16 bits (flag & count) + + * and info of packets contained in buffer. + + */ + + + + /* + + * Fill buffer with next packet info + + */ + + count ++; + + *buf++ = reps; + + *buf++ = redBuf[0]; + + *buf++ = grnBuf[0]; + + *buf++ = bluBuf[0]; + + + + + + numToWrite += 4; + + + + *bufStart = (unsigned char) (count & 0xff); + + *(bufStart+1) = (unsigned char) ((flag << 4) + (count >> 8)); + + + + + + + + if(ImBinWrite(ioType,fd,fp,bufStart,UCHAR,1,numToWrite)== -1 ) + + { + + free( bufStart ); + + ImReturnBinError(); + + } + + + + buf = bufStart+2; + + numWritten += numToWrite; + + totWritten += numToWrite; + + } + + + + + + /* + + * The rest of the last block needs to be completed so that any block + + * reads that do not check for the end of the file can read blindly + + * NOTE: Some tests were performed on the technique to just close the + + * last block with 4 zero's ( which signify the end of the block ), + + * but just in case some applications may get confused if the + + * block is not totally complete, the remainder of the block will + + * be filled with zeros. + + */ + + if ( numWritten < IMPICBLOCK ) + + { + + unsigned char *buf; + + + + numToWrite = IMPICBLOCK - numWritten; + + ImMalloc( buf, unsigned char*, numToWrite ); + + memset( (void *)buf, 0x00, numToWrite ); + + + + if(ImBinWrite(ioType,fd,fp,buf,UCHAR,1,numToWrite)== -1 ) + + { + + free( buf ); + + ImReturnBinError(); + + } + + + + totWritten += numToWrite; + + free( buf ); + + } + + + + /* + + * Now that all the image data has been written, the header must + + * be written; thus, seek back to the start of the file and write it + + */ + + ImSeek( ioType, fd, fp, 0, 0 ); + + + + /* + + * Write the PIXAR picture file header. + + */ + + if ( imPicWriteHeader( ioType, fd, fp, vfb, IMRGBBACK, IMPICENCODE8, IMNOALPHA, totWritten ) == -1 ) + + return ( -1 ); /* Error already handled */ + + + + free( bufStart ); + + + + + + return ( 1 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imPicWrite8 - write a one-channel PIC file, dump + + * imPicWrite8RLE - write a one-channel PIC file, encoded + + * + + * DESCRIPTION + + * Vfb pixel info is read into buffers and is either run length encoded + + * by counting the number of repitiions on each scanline, or the info + + * is dumped directly into the PIXAR Picture file. + + */ + + + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imPicWrite8(ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable) + +#else + +imPicWrite8( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Format flags */ + + TagTable *tagTable; /* Tag list to read from */ + +#endif + +{ + + ImVfb *vfb; /* Image to write */ + + ImVfbPtr pptr; /* Pixel pointer */ + + unsigned char *buf; /* Run buffer */ + + unsigned char *bufStart; /* Pointer to first loc in buf */ + + int i,j; /* Loop counters */ + + int x,y; /* Loop bounds */ + + + + /* + + * PIC files are usually generated on PIXARs, which stores + + * MULTIPLE-byte data with the least significant 8 in the first + + * byte. This is an LBF byte order. + + * There is no floating point in the file so we + + * won't bother setting the float format for BIN. + + */ + + BinByteOrder( BINLBF ); + + + + /* + + * Initialize the things we need + + */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + x = ImVfbQWidth( vfb ); + + y = ImVfbQHeight( vfb ); + + pptr = ImVfbQFirst( vfb ); + + + + /* + + * Write the PIXAR picture file header. + + * NOTE: we can pre-compute the length of the pixel data since + + * we know the each pixel written will have 1 byte associated + + * with it one for R. Therefore, Length = x*y + + */ + + if ( imPicWriteHeader( ioType, fd, fp, vfb, IMSINGLECHAN, IMPICDUMP8, IMNOALPHA, (x*y) ) == -1 ) + + return ( -1 ); /* Error already handled */ + + + + /* Allocate a buffer big enough for one scanline, read pixel info + + * from vfb, and write it directly to pixar picture file. + + */ + + ImMalloc( bufStart, unsigned char*, x ); + + + + for( i=0; i=(IMPICBLOCK-4) ) + + { + + /* + + * The blocksize will be exceeded if we + + * fill the buffer with next packet, and + + * since packets cannot span successive + + * blocks, the current buffer needs to + + * be written and the remainder of + + * the block filled with 0's. + + */ + + + + /* + + * A rare case occurs if the end of a + + * scanline was previously written and + + * the next packet to fill would cause + + * block to overflow -- + + * signify end of block + + */ + + if ( numToWrite == 2 ) + + { + + count = 0; + + flag = 0; + + } + + + + /* + + * store count and flag bits and + + * update count for subsequent check + + */ + + *bufStart = (unsigned char) (count & 0xff); + + *(bufStart+1) = (unsigned char) ((flag << 4) + + + (count >> 8) ); + + + + numWritten += numToWrite; + + + + /* + + * Fill out remainder of block + + * with zero's to signify end. + + */ + + if ( numWritten < IMPICBLOCK ) + + { + + k = IMPICBLOCK - numWritten; + + memset( (void *)buf, 0x00, k ); + + numToWrite += k; + + buf += k; + + } + + + + /* + + * Write stored buffer runlength info + + */ + + if(ImBinWrite(ioType,fd,fp,bufStart, + + UCHAR,1,numToWrite) == -1 ) + + { + + free(bufStart); + + ImReturnBinError(); + + } + + + + totWritten += numToWrite; + + + + /* + + * Reset variables for next block + + */ + + buf = bufStart+2; + + count = (unsigned int)-1; + + numToWrite = 2; + + numWritten = 0; + + if ( flag == 0 ) flag = 2; + + + + } + + + + /* + + * Fill buffer with next packet info + + */ + + count ++; + + *buf++ = reps; + + *buf++ = redBuf[0]; + + numToWrite += 2; + + reps = 0; + + + + /* + + * Reset flag and set current pixel as the + + * pixel to be compared to for future runs. + + */ + + fillBuf = 0; + + redBuf[0] = redBuf[1]; + + } + + } + + + + /* + + * Completed scanline. Write out packed 16 bits (flag & count) + + * and info of packets contained in buffer. + + */ + + + + /* + + * Fill buffer with next packet info + + */ + + count ++; + + *buf++ = reps; + + *buf++ = redBuf[0]; + + numToWrite += 2; + + + + *bufStart = (unsigned char) (count & 0xff); + + *(bufStart+1) = (unsigned char) ((flag << 4) + (count >> 8)); + + + + if(ImBinWrite(ioType,fd,fp,bufStart,UCHAR,1,numToWrite)== -1 ) + + { + + free( bufStart ); + + ImReturnBinError(); + + } + + + + buf = bufStart+2; + + numWritten += numToWrite; + + totWritten += numToWrite; + + } + + + + /* + + * The rest of the last block needs to be completed so that any block + + * reads that do not check for the end of the file can read blindly + + */ + + if ( numWritten < IMPICBLOCK ) + + { + + unsigned char *buf; + + + + numToWrite = IMPICBLOCK - numWritten; + + ImMalloc( buf, unsigned char*, numToWrite ); + + memset( (void *)buf, 0x00, numToWrite ); + + + + if(ImBinWrite(ioType,fd,fp,buf,UCHAR,1,numToWrite)== -1 ) + + { + + free( buf ); + + ImReturnBinError(); + + } + + + + totWritten += numToWrite; + + free( buf ); + + } + + + + /* + + * Now that all the image data has been written, the header must + + * be written; thus, seek back to the start of the file and write it + + */ + + ImSeek( ioType, fd, fp, 0, 0 ); + + + + /* + + * Write the PIXAR picture file header. + + */ + + if ( imPicWriteHeader( ioType, fd, fp, vfb, IMSINGLECHAN, IMPICENCODE8, IMNOALPHA, totWritten ) == -1 ) + + return ( -1 ); /* Error already handled */ + + + + free( bufStart ); + + + + return ( 1 ); + +} + + + diff --git a/utils/roq2/libim/impict.c b/utils/roq2/libim/impict.c new file mode 100644 index 0000000..de25e06 --- /dev/null +++ b/utils/roq2/libim/impict.c @@ -0,0 +1,13600 @@ +/** + + ** + + ** $Header: /roq/libim/impict.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/impict.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** impict.c - Apple Macintosh PICT file I/O + + ** + + ** PROJECT + + ** libimage - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** impict.c contains routines to read/write Apple Macintosh PICT files for + + ** the image manipulation library. Raster data read in is stored + + ** in a VFB. Raster data written out is taken from a tag table. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** none + + ** + + ** PRIVATE CONTENTS + + ** imPictRead f read a Macintosh PICT file + + ** imPictWrite f write a Macintosh PICT file + + ** NULL d an empty pointer + + ** + + ** imPictHeaderInfo t PICT file header information + + ** imPictHeaderFields v imPictHeaderInfo description for Bin pkg + + ** + + ** imReturnBinError m return right error based on Bin call + + ** imMalloc m malloc memory & return on error + + ** imBinRead m binary read from fd or fp + + ** + + ** HISTORY + + ** + + ** $Log: /roq/libim/impict.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.28 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.27 1995/06/15 20:51:58 bduggan + + ** Added #ifdef DEBUG's around messages going to stderr + + ** + + ** Revision 1.26 1995/06/14 19:17:38 bduggan + + ** Added some casts, turned bzero into memset, took out some useless var's + + ** + + ** Revision 1.25 1995/04/03 21:33:42 bduggan + + ** took out #ifdef NEWMAGIC + + ** + + ** Revision 1.24 1995/02/16 05:32:17 moreland + + ** Fixed a pesky bug that turned out to be major - it has to do with + + ** the cmpCount field of a direct pixel map (32 bit). First, the code + + ** did not calculate a channel offset for each bitplane. Second, the + + ** programmer did not know that Apple chose to store 32-bit pixels + + ** with the alpha plane first (ie: ARGB, NOT RGBA). Oh well, what can + + ** you do with an undocumented proprietary file format... :^| + + ** + + ** Revision 1.23 1995/01/10 23:39:19 bduggan + + ** put in IMMULTI, IMPIPE instead of TRUE/FALSE + + ** uncapitlized i's in local functions + + ** made read/write routines static + + ** + + ** Revision 1.22 94/10/03 11:30:28 nadeau + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Rearranged magic number structures for format handlers. + + ** Made format handler routines static (i.e., local to file). + + ** Updated comments, adding format descriptions and references. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.21 93/10/21 15:20:20 moreland + + ** Added support for 16 and 32 bit direct pixel image opcodes. + + ** Rearanged some code to support multiple ImFileWrite entry points. + + ** Fixed some gross mistakes in code that was "re-aranged" by the + + ** last person to diddle with the PICT module. + + ** PICT now supports 1, 2, 4, 8, 16, and 32 bit image reads and writes ! + + ** + + ** Revision 1.20 93/03/09 10:49:36 nadeau + + ** Corrected non-ANSI-ism on an #endif comment. + + ** + + ** Revision 1.19 92/12/03 01:50:55 nadeau + + ** Corrected info messages. + + ** + + ** Revision 1.18 92/11/24 17:03:54 groening + + ** changed a SHORT to USHORT to maintian type compatibility. + + ** Made the variable clt global since it was used by different + + ** routines but it was not getting passed but declared in each routine. + + ** this fixed problems on crayy with losing the clt and problems + + ** on crayy when writing, which produced a truncation error. + + ** Happy, Happy. + + ** + + ** Revision 1.17 92/11/24 16:34:51 nadeau + + ** Corrected format information string. + + ** + + ** Revision 1.16 92/11/23 18:42:51 nadeau + + ** Removed use of IMINFOMSG. + + ** + + ** Revision 1.15 92/11/04 12:05:00 groening + + ** put ImFIleFormat info and magic number info + + ** from imfmt.c into this file. + + ** + + ** Revision 1.14 92/10/12 15:59:38 vle + + ** Added ImInfo messages. + + ** + + ** Revision 1.13 92/08/31 17:31:43 vle + + ** Updated copyright notice. + + ** + + ** Revision 1.12 92/07/01 13:34:56 groening + + ** added name of struct after terminating braces + + ** in struct to make the next compiler happy. + + ** + + ** Revision 1.11 91/09/26 14:11:35 moreland + + ** Added support for pen-mode (transfer modes) + + ** Fixed core-dumping bug on the Iris (overflowing buffer) + + ** + + ** + + ** Revision 1.10 91/06/25 08:09:33 moreland + + ** Cleaned up some of the small inconsistancies reported as warnings + + ** by the "lint" utility. Added some explicit casts, removed unused + + ** variables, and caught some typos in several 'unused' opcode handlers. + + ** + + ** Revision 1.9 91/06/20 09:40:35 moreland + + ** Rewrote imPictRead to use table-driven parser and to + + ** eliminate the difficult-to-compile/giant "switch" statement. + + ** Added better error/status messages (including "English" descriptions + + ** of opcodes being skipped). + + ** + + ** Revision 1.8 91/04/09 11:33:41 nadeau + + ** Removed inclusion of sys/fcntl.h. + + ** + + ** Revision 1.7 91/02/19 07:08:47 moreland + + ** Added code for FrameSameRect opcode + + ** + + ** Revision 1.6 91/02/18 16:24:09 moreland + + ** fixed ! + + ** + + ** Revision 1.5 91/02/13 13:38:37 nadeau + + ** Removed tag table checking now handled by ImFileRead and + + ** ImFileWrite. Corrected a machine byte-order dependency + + ** in CLT writing. + + ** + + ** Revision 1.4 90/12/20 07:27:26 moreland + + ** *** empty log message *** + + ** + + ** Revision 1.3 90/12/13 13:14:41 rama + + ** Pictsize changed to unsigned int + + ** + + ** Revision 1.2 90/12/13 07:08:11 moreland + + ** *** empty log message *** + + ** + + ** Revision 1.1 90/11/09 09:06:40 moreland + + ** Initial revision + + ** + + ** Revision 1.15 90/08/15 07:30:59 moreland + + ** Removed the outer-most for loop - disabled multiple bands per VFB + + ** This fixed "the problem" and imPictWrite now works !!!! :^) + + ** Turns out rowBytes DOES have to be even. + + ** + + ** Revision 1.14 90/08/02 08:55:56 moreland + + ** imPictWrite works ! (with my color smiley face test) + + ** Turns out rowBytes does NOT have to be even after all. + + ** + + ** Revision 1.13 90/07/30 15:55:54 moreland + + ** Filled in Apple's reserved HeaderOp fields in imPictWrite. + + ** Color Lookup Table "val" field set to offset/index value. + + ** + + ** Revision 1.12 90/06/21 15:42:12 moreland + + ** PackBits routine has been written and tested (a little), + + ** It is now time to start writing imPictWrite. + + ** + + ** Revision 1.11 90/06/14 14:49:08 moreland + + ** Cleaned up "holes" for non-handled/skipped opcodes and put fprintf + + ** statements in to let a user know which opcodes are skipped. + + ** + + ** Revision 1.10b 90/06/11 15:17:24 moreland + + ** Got "Exposure INIT" PICT ScreenDumps to parse. + + ** + + ** Revision 1.10 90/06/11 15:17:24 moreland + + ** Got first PixMaps (pict1 & pict2) to display ! + + ** + + ** Revision 1.9 90/06/07 12:23:58 moreland + + ** Finished Unpackbits. + + ** Got PackBitsRect opcode working for version 1 PICT files. + + ** + + ** Revision 1.8 90/06/05 15:32:32 moreland + + ** The UnpackBits routine MAY be working, but something is causing + + ** strange munging of bitmaps. (maybe tstBit is broken ?). + + ** + + ** Revision 1.7 90/06/04 14:49:28 moreland + + ** First attempt at PO_PackBitsRect opcode - spews raw bits into vfb + + ** At LEAST it skips over all of the PackBitsRect data correctly. + + ** + + ** Revision 1.6 90/05/23 16:12:44 moreland + + ** gave up on xpict (no 24bit colors) and wrote pict2pix + + ** todd provided "pixtorle" and "getsun/getx" (rle to sunview/X) + + ** DISPLAYED FIRST PICT (pict8) IMAGE !!! :^) + + ** + + ** Revision 1.5 90/05/22 16:00:51 moreland + + ** finished opcodes to support test-file "pict8" + + ** added DEBUG fprintf statements and cpp DEBUG flag + + ** began constructing "xpict" tool to display PICT files under X + + ** + + ** Revision 1.4 90/05/21 16:21:29 moreland + + ** wrote "draft" code for opcodes 0x0013 to 0x0030 + + ** fixed stupid syntax errors so that the module compiles + + ** + + ** Revision 1.3 90/05/18 16:56:33 moreland + + ** constructed main parser SWITCH block for all opcodes + + ** wrote "draft" code for first 19 or so opcodes + + ** + + ** Revision 1.2 90/05/16 16:57:05 moreland + + ** typed in OPCODE define statements + + ** + + ** Revision 1.1 90/05/16 13:10:33 moreland + + ** Initial revision + + ** + + ** Revision 1.0 90/05/15 09:55:19 moreland + + ** First version creation + + ** + + **/ + + + +/** + + ** FORMAT + + ** PICT - Apple Macintosh pictures + + ** + + ** AKA + + ** pict2 + + ** + + ** FORMAT REFERENCES + + ** Inside Macintosh, Volumes I-V, Apple Computer, Inc. + + ** + + ** CODE CREDITS + + ** Custom development, John Moreland, San Diego Supercomputer Center, 1990. + + ** + + ** DESCRIPTION + + ** + + **/ + + + + + +#include + +#include + +#include + +#include + +#include "iminternal.h" + +#include "im.h" + + + +/* + + * PICT - Apple Macintosh Picture + + * For information on these structures, how to use them, etc. please + + * see imfmt.c. + + */ + + + +#ifdef __STDC__ + +static int imPictRead( int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable ); + +static int imPictWrite( ImFileFormatWriteMap *ppMap, int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable); + + + +static int imPictWritePB1(ImFileFormatWriteMap *ppMap, int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable ); + +static int imPictWritePB8( ImFileFormatWriteMap *ppMap, int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable); + +static int imPictWritePB16( ImFileFormatWriteMap *ppMap, int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable); + +static int imPictWritePBRGB( ImFileFormatWriteMap *ppMap, int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable); + +#else + +static int imPictRead( ); + +static int imPictWrite( ); + + + +static int imPictWritePB1( ); + +static int imPictWritePB8( ); + +static int imPictWritePB16( ); + +static int imPictWritePBRGB( ); + +#endif + + + +static char *imPictNames[ ] = { "pict", "pict2", NULL }; + +static ImFileFormatReadMap imPictReadMap[ ] = + +{ + + /* in out */ + + /* type,ch,dep, attr. VFB type attr. */ + + { IN,1,1, PB, IMVFBRGB, 0 }, + + { IN,1,2, PB | C, IMVFBRGB, 0 }, + + { IN,1,4, PB | C, IMVFBRGB, 0 }, + + { IN,1,8, PB | C, IMVFBRGB, 0 }, + + { RGB,3,5, PB, IMVFBRGB, 0 }, + + { RGB,3,8, PB, IMVFBRGB, 0 }, + + { -1, 0, -1, 0 }, + +}; + +static ImFileFormatWriteMap imPictWriteMap[ ] = + +{ + + /* in out */ + + /* VFB type, attr., type,ch,dep, attr., func */ + + { IMVFBMONO, 0, IN,1,1, PB, imPictWritePB1 }, + + { IMVFBINDEX8, C, IN,1,8, PB | C, imPictWritePB8 }, + + { IMVFBINDEX16, 0, RGB,3,5, PB, imPictWritePB16 }, + + { IMVFBRGB, 0, RGB,3,8, PB, imPictWritePBRGB }, + + { -1, 0, -1, 0, NULL }, + +}; + + + +static ImFileMagic imFilePictMagic []= + +{ + + { 0, 0, NULL}, + +}; + + + +ImFileFormat ImFilePictFormat = + +{ + + imPictNames, "Apple Macintosh QuickDraw/PICT file", + + "Apple Computer, Inc.", + + "1-, 2-, 4-, 8-, 16-, and 32-bit PackBits-compressed color index\n\ +and RGB image files in PICT and PICT2 formats.", + "1-, 8-, 16-, and 32-bit PackBits-compressed color index and RGB\n\ +image files in PICT2 format.", + imFilePictMagic, + + IMNOMULTI, IMPIPE, + + IMNOMULTI, IMPIPE, + + imPictRead, imPictReadMap, imPictWriteMap + +}; + + + + + +static ImClt * clt=NULL; /* VFB ColorTable - made global because of way + + that code has already been written */ + + + + + +#ifndef NULL + +#define NULL (0) + +#endif + + + +#define IModd(a) ( ( ( (a) % 2 ) == 0 )?FALSE:TRUE ) + +#define IMeven(a) ( ( ( (a) % 2 ) == 0 )?TRUE:FALSE ) + + + +/* + + * + + * DEFINES + + * + + */ + + + +#define IMFIXED LONG + + + +#define IMBLACK 0 + +#define IMWHITE 255 + + + +/* Pen Transfer Modes */ + +#define IMTM_TRANSPARENT 36 + + + +#define IMSKIP_BUF_SIZ 4096 /* Generic "skip" buffer size */ + +#define IMSTR_LEN 256 /* Generic message-line-string size */ + +#define IMPICT_V1 0x1101 /* Version 1 PICT */ + +#define IMPICT_V2 0x0011 /* Version 2 PICT */ + + + + + +/* PICT COMMENTS */ + +/* NAME NUMBER DESCRIPTION DATA(bytes) */ + +#define IMPC_PICLPAREN 0 /* Begin QD grouping 0 */ + +#define IMPC_PICRPAREN 1 /* End QD grouping 0 */ + +#define IMPC_PICAPPCOMMENT 100 /* Application-specific comment var */ + +#define IMPC_PICDWGBEG 130 /* Begin MacDraw picture 0 */ + +#define IMPC_PICDWGEND 131 /* End MacDraw picture 0 */ + +#define IMPC_PICGRPBEG 140 /* Begin grouped objects 0 */ + +#define IMPC_PICGRPEND 141 /* End grouped objects 0 */ + +#define IMPC_PICBITBEG 142 /* Begin series of bitmap bands 0 */ + +#define IMPC_PICBITEND 143 /* End series of bitmap bands 0 */ + +/* etc... */ + + + + + +/* + + * + + * TYPEDEFS AND STRUCTURES + + * + + */ + + + + + +typedef struct Rect + +{ + + short int top, left, bottom, right; + +} Rect; + + + +static BinField RectFields[] = + +{ + + SHORT,2,1, + + SHORT,2,1, + + SHORT,2,1, + + SHORT,2,1, + + 0,0,0 + +}; + + + + + +typedef struct Pict2Header + +{ + + unsigned short headerOp; + + unsigned short version; + + unsigned short reserved1; + + unsigned long hRes; + + unsigned long vRes; + + short srcRect_top; + + short srcRect_left; + + short srcRect_bottom; + + short srcRect_right; + + unsigned long reserved2; + +} Pict2Header; + + + +static BinField Pict2HeaderFields[ ] = + +{ + + USHORT, 2, 1, + + USHORT, 2, 1, + + USHORT, 2, 1, + + ULONG, 4, 1, + + ULONG, 4, 1, + + SHORT, 2, 4, + + ULONG, 4, 1, + + 0, 0, 0, + +}; + + + + + +typedef long Fixed; + + + +typedef struct Point + +{ + + short int v, h; + +} Point; + + + +static BinField PointFields[] = + +{ + + SHORT,2,1, + + SHORT,2,1, + + 0,0,0 + +}; + + + + + +typedef struct PMap + +{ + + struct Rect bnd; + + short int version; + + short int packType; + + long int packSize; + + Fixed hRes; + + Fixed vRes; + + short int pixelType; + + short int pixelSize; + + short int cmpCount; + + short int cmpSize; + + long int planeBytes; + + long int pmTable; + + long int pmReserved; + +} PMap; + + + +static BinField PMapFields[] = + +{ + + SHORT,2,1, SHORT,2,1, SHORT,2,1, SHORT,2,1, + + SHORT,2,1, + + SHORT,2,1, + + LONG,4,1, + + IMFIXED,4,1, + + IMFIXED,4,1, + + SHORT,2,1, + + SHORT,2,1, + + SHORT,2,1, + + SHORT,2,1, + + LONG,4,1, + + LONG,4,1, + + LONG,4,1, + + 0,0,0 + +}; + + + + + +typedef struct CTable + +{ + + long int ctSeed; + + short int ctFlags; + + short int ctSize; + +} CTable; + + + +static BinField CTableFields[] = + +{ + + LONG,4,1, + + SHORT,2,1, + + SHORT,2,1, + + 0,0,0 + +}; + + + + + +typedef struct CTEntry + +{ + + unsigned short val, red, green, blue; + +} CTEntry; + + + +static BinField CTEntryFields[] = + +{ + + SHORT,2,1, + + SHORT,2,1, + + SHORT,2,1, + + SHORT,2,1, + + 0,0,0 + +}; + + + + + +typedef struct RGBColor + +{ + + short int red, green, blue; + +} RGBColor; + + + +static BinField RGBColorFields[] = + +{ + + SHORT,2,1, + + SHORT,2,1, + + SHORT,2,1, + + 0,0,0 + +}; + + + + + +typedef struct Region + +{ + + short int rgnSize; + + struct Rect rgnBBox; + +} Region; + + + +static BinField RegionFields[] = + +{ + + SHORT,2,1, + + SHORT,2,1, SHORT,2,1, SHORT,2,1, SHORT,2,1, + + 0,0,0 + +}; + + + + + +typedef unsigned char Pattern[8]; + + + + + +typedef struct BMap + +{ + + struct Rect bnd; + + struct Rect srcRect; + + struct Rect dstRect; + + short int mode; + +} BMap; + + + +static BinField BMapFields[] = + +{ + + SHORT,2,1, SHORT,2,1, SHORT,2,1, SHORT,2,1, + + SHORT,2,1, SHORT,2,1, SHORT,2,1, SHORT,2,1, + + SHORT,2,1, SHORT,2,1, SHORT,2,1, SHORT,2,1, + + SHORT,2,1, + + 0,0,0 + +}; + + + + + +typedef struct GState + +{ + + /* EXTRACTED FIELDS FROM MACINTOSH "cGrafPort" STRUCTURE */ + + short chExtra; + + short pnLocHFrac; + + struct Rect portRect; + + struct Region visRgn; + + struct Region clipRgn; + + struct PMap bkPixPat; + + struct RGBColor rgbFgColor; /* Used to "fill" objects */ + + struct RGBColor rgbBkColor; /* Used to "paint" objects */ + + struct Point pnLoc; + + struct Point pnSize; + + short pnMode; + + struct PMap pnPixPat; + + struct PMap fillPixPat; + + short pnVis; + + short txFont; + + int txFace; + + short txMode; + + short txSize; + + Fixed spExtra; + + long fgColor; + + long bkColor; + + short colrBit; + + short patStretch; + + /* CUSTOM FIELDS */ + + short hiliteMode; /* 0x001C - hilite mode flag - 0 */ + + short opColor; /* 0x001F - RGB OpColor : arith. modes - 0 */ + + struct RGBColor hiliteColor; + + struct Rect lastRect; /* used for "Same" routines */ + + Pattern fgPat; + + Pattern bkPat; + + Pattern pnPat; + + Pattern fillPat; + + struct Point ovSize; + + struct Point txRatNum; + + struct Point txRatDen; + + char version; + + struct Point origin; + +} GState; + + + + + +/* Opcode handling functions */ + +static int PO_NOP(); + +static int PO_Clip(); + +static int PO_BkPat(); + +static int PO_TxFont(); + +static int PO_TxFace(); + +static int PO_TxMode(); + +static int PO_SpExtra(); + +static int PO_PnSize(); + +static int PO_PnMode(); + +static int PO_PnPat(); + +static int PO_FillPat(); + +static int PO_OvSize(); + +static int PO_Origin(); + +static int PO_TxSize(); + +static int PO_FgColor(); + +static int PO_BkColor(); + +static int PO_TxRatio(); + +static int PO_Version(); + +static int PO_BkPixPat(); + +static int PO_PnPixPat(); + +static int PO_FillPixPat(); + +static int PO_PnLocHFrac(); + +static int PO_ChExtra(); + +static int PO_reserved1(); + +static int PO_reserved2(); + +static int PO_reserved3(); + +static int PO_RGBFgCol(); + +static int PO_RGBBkCol(); + +static int PO_HiliteMode(); + +static int PO_HiliteColor(); + +static int PO_DefHilite(); + +static int PO_OpColor(); + +static int PO_Line(); + +static int PO_LineFrom(); + +static int PO_ShortLine(); + +static int PO_ShortLineFrom(); + +static int PO_reserved4(); + +static int PO_reserved5(); + +static int PO_reserved6(); + +static int PO_reserved7(); + +static int PO_LongText(); + +static int PO_DHText(); + +static int PO_DVText(); + +static int PO_DHDVText(); + +static int PO_reserved8(); + +static int PO_reserved9(); + +static int PO_reserved10(); + +static int PO_reserved11(); + +static int PO_FrameRect(); + +static int PO_PaintRect(); + +static int PO_EraseRect(); + +static int PO_InvertRect(); + +static int PO_FillRect(); + +static int PO_reserved12(); + +static int PO_reserved13(); + +static int PO_reserved14(); + +static int PO_FrameSameRect(); + +static int PO_PaintSameRect(); + +static int PO_EraseSameRect(); + +static int PO_InvertSameRect(); + +static int PO_FillSameRect(); + +static int PO_reserved15(); + +static int PO_reserved16(); + +static int PO_reserved17(); + +static int PO_FrameRRect(); + +static int PO_PaintRRect(); + +static int PO_EraseRRect(); + +static int PO_InvertRRect(); + +static int PO_FillRRect(); + +static int PO_reserved18(); + +static int PO_reserved19(); + +static int PO_reserved20(); + +static int PO_FrameSameRRect(); + +static int PO_PaintSameRRect(); + +static int PO_EraseSameRRect(); + +static int PO_InvertSameRRect(); + +static int PO_FillSameRRect(); + +static int PO_reserved21(); + +static int PO_reserved22(); + +static int PO_reserved23(); + +static int PO_FrameOval(); + +static int PO_PaintOval(); + +static int PO_EraseOval(); + +static int PO_InvertOval(); + +static int PO_FillOval(); + +static int PO_reserved24(); + +static int PO_reserved25(); + +static int PO_reserved26(); + +static int PO_FrameSameOval(); + +static int PO_PaintSameOval(); + +static int PO_EraseSameOval(); + +static int PO_InvertSameOval(); + +static int PO_FillSameOval(); + +static int PO_reserved27(); + +static int PO_reserved28(); + +static int PO_reserved29(); + +static int PO_FrameArc(); + +static int PO_PaintArc(); + +static int PO_EraseArc(); + +static int PO_InvertArc(); + +static int PO_FillArc(); + +static int PO_reserved30(); + +static int PO_reserved31(); + +static int PO_reserved32(); + +static int PO_FrameSameArc(); + +static int PO_PaintSameArc(); + +static int PO_EraseSameArc(); + +static int PO_InvertSameArc(); + +static int PO_FillSameArc(); + +static int PO_reserved33(); + +static int PO_reserved34(); + +static int PO_reserved35(); + +static int PO_FramePoly(); + +static int PO_PaintPoly(); + +static int PO_ErasePoly(); + +static int PO_InvertPoly(); + +static int PO_FillPoly(); + +static int PO_reserved36(); + +static int PO_reserved37(); + +static int PO_reserved38(); + +static int PO_FrameSamePoly(); + +static int PO_PaintSamePoly(); + +static int PO_EraseSamePoly(); + +static int PO_InvertSamePoly(); + +static int PO_FillSamePoly(); + +static int PO_reserved39(); + +static int PO_reserved40(); + +static int PO_reserved41(); + +static int PO_FrameRgn(); + +static int PO_PaintRgn(); + +static int PO_EraseRgn(); + +static int PO_InvertRgn(); + +static int PO_FillRgn(); + +static int PO_reserved42(); + +static int PO_reserved43(); + +static int PO_reserved44(); + +static int PO_FrameSameRgn(); + +static int PO_PaintSameRgn(); + +static int PO_EraseSameRgn(); + +static int PO_InvertSameRgn(); + +static int PO_FillSameRgn(); + +static int PO_reserved45(); + +static int PO_reserved46(); + +static int PO_reserved47(); + +static int PO_BitsRect(); + +static int PO_BitsRgn(); + +static int PO_reserved48(); + +static int PO_reserved49(); + +static int PO_reserved50(); + +static int PO_reserved51(); + +static int PO_reserved52(); + +static int PO_reserved53(); + +static int PO_PackBitsRect(); + +static int PO_PackBitsRgn(); + +static int PO_DirectBitsRect(); + +static int PO_DirectBitsRgn(); + +static int PO_reserved56(); + +static int PO_reserved57(); + +static int PO_reserved58(); + +static int PO_reserved59(); + +static int PO_ShortComment(); + +static int PO_LongComment(); + +static int PO_reserved60(); + +/* etc... */ + +static int PO_reserved73(); + +static int PO_reserved74(); + +/* etc... */ + +static int PO_reserved105(); + +static int PO_reserved106(); + +/* etc... */ + +static int PO_reserved152(); + +static int PO_opEndPic(); + +static int PO_reserved153(); + +/* etc... */ + +static int PO_reserved408(); + +static int PO_reserved409(); + +/* etc... */ + +static int PO_reserved2968(); + +static int PO_HeaderOp(); + +static int PO_reserved2969(); + +/* etc... */ + +static int PO_reserved9999(); + + + + + +/* + + * + + * + + * PARSER TYPEDEFS AND STRUCTURES + + * + + */ + + + + + +struct POCHandler + +{ + + int opcode; + + char * name; + + int (*handler)(); + +}; + + + + + +typedef struct POCHandler POCHandler; + + + + + +/* PICT OPCODE VALUE/FUNCTION/DESCRIPTION LOOKUP TABLE */ + +POCHandler POCHandlerList[] = + +{ + + /* VALUE DESCRIPTION HANDLER DATA(bytes) */ + + { 0x0000, "Null Operation", PO_NOP }, /* 0 */ + + { 0x0001, "Clip Region", PO_Clip }, /* rgn size */ + + { 0x0002, "Background Pattern", PO_BkPat }, /* 8 */ + + { 0x0003, "Text Font", PO_TxFont }, /* 2 */ + + { 0x0004, "Text Face", PO_TxFace }, /* 1 */ + + { 0x0005, "Text Mode", PO_TxMode }, /* 2 */ + + { 0x0006, "Space Extra", PO_SpExtra }, /* 4 */ + + { 0x0007, "Pen Size", PO_PnSize }, /* 4 */ + + { 0x0008, "Pen Mode", PO_PnMode }, /* 2 */ + + { 0x0009, "Pen Pattern", PO_PnPat }, /* 8 */ + + { 0x000A, "Fill Pattern", PO_FillPat }, /* 8 */ + + { 0x000B, "Oval Size", PO_OvSize }, /* 4 */ + + { 0x000C, "Set Origin - dh, dv", PO_Origin }, /* 4 */ + + { 0x000D, "Text Size", PO_TxSize }, /* 2 */ + + { 0x000E, "Foreground Color", PO_FgColor }, /* 4 */ + + { 0x000F, "Background Color", PO_BkColor }, /* 4 */ + + { 0x0010, "Text Ratio", PO_TxRatio }, /* 4 */ + + { 0x0011, "Version", PO_Version }, /* 1 */ + + { 0x0012, "Background Pixel Pattern", PO_BkPixPat }, /* var */ + + { 0x0013, "Pen Pixel Pattern", PO_PnPixPat }, /* var */ + + { 0x0014, "Fill Pixel Pattern", PO_FillPixPat }, /* var */ + + { 0x0015, "Fractional Pen Position", PO_PnLocHFrac }, /* 2 */ + + { 0x0016, "Extra for each character", PO_ChExtra }, /* 2 */ + + { 0x0017, "Reserved1 for Apple use", PO_reserved1 }, /* 0 */ + + { 0x0018, "Reserved2 for Apple use", PO_reserved2 }, /* 0 */ + + { 0x0019, "Reserved3 for Apple use", PO_reserved3 }, /* 0 */ + + { 0x001A, "RGB Forground Color", PO_RGBFgCol }, /* var */ + + { 0x001B, "RGB Background Color", PO_RGBBkCol }, /* var */ + + { 0x001C, "Hilite Mode Flag", PO_HiliteMode }, /* 0 */ + + { 0x001D, "RGB Hilite Color", PO_HiliteColor }, /* var */ + + { 0x001E, "Use Default Hilite Color", PO_DefHilite }, /* 0 */ + + { 0x001F, "RGB OpColor / Arith Modes", PO_OpColor }, /* 0 */ + + { 0x0020, "Line (penLoc, newPt)", PO_Line }, /* 8 */ + + { 0x0021, "Line From (newPt)", PO_LineFrom }, /* 4 */ + + { 0x0022, "Short Line (pnLoc)", PO_ShortLine }, /* 6 */ + + { 0x0023, "Short Line From (dh, dv)", PO_ShortLineFrom }, /* 2 */ + + { 0x0024, "Reserved4 for Apple use", PO_reserved4 }, /* 2+data */ + + { 0x0025, "Reserved5 for Apple use", PO_reserved5 }, /* 2+data */ + + { 0x0026, "Reserved6 for Apple use", PO_reserved6 }, /* 2+data */ + + { 0x0027, "Reserved7 for Apple use", PO_reserved7 }, /* 2+data */ + + { 0x0028, "Long Text (loc, cnt, txt)", PO_LongText }, /* 5+text */ + + { 0x0029, "HOffset Text (dh,cnt,txt)", PO_DHText }, /* 2+text */ + + { 0x002A, "VOffset Text (dv,cnt,txt)", PO_DVText }, /* 2+text */ + + { 0x002B, "DHDV Text (dh,dv,cnt,txt)", PO_DHDVText }, /* 3+text */ + + { 0x002C, "Reserved8 for Apple use", PO_reserved8 }, /* 2+data */ + + { 0x002D, "Reserved9 for Apple use", PO_reserved9 }, /* 2+data */ + + { 0x002E, "Reserved10 for Apple use", PO_reserved10 }, /* 2+data */ + + { 0x002F, "Reserved11 for Apple use", PO_reserved11 }, /* 2+data */ + + { 0x0030, "Frame Rect (rect)", PO_FrameRect }, /* 8 */ + + { 0x0031, "Paint Rect (rect)", PO_PaintRect }, /* 8 */ + + { 0x0032, "Erase Rect (rect)", PO_EraseRect }, /* 8 */ + + { 0x0033, "Invert Rect (rect)", PO_InvertRect }, /* 8 */ + + { 0x0034, "Fill Rect (rect)", PO_FillRect }, /* 8 */ + + { 0x0035, "Reserved12 for Apple use", PO_reserved12 }, /* 8 */ + + { 0x0036, "Reserved13 for Apple use", PO_reserved13 }, /* 8 */ + + { 0x0037, "Reserved14 for Apple use", PO_reserved14 }, /* 8 */ + + { 0x0038, "Frame Same Rect", PO_FrameSameRect }, /* 0 */ + + { 0x0039, "Paint Same Rect", PO_PaintSameRect }, /* 0 */ + + { 0x003A, "Erase Same Rect", PO_EraseSameRect }, /* 0 */ + + { 0x003B, "Invert Same Rect", PO_InvertSameRect }, /* 0 */ + + { 0x003C, "Fill Same Rect", PO_FillSameRect }, /* 0 */ + + { 0x003D, "Reserved15 for Apple use", PO_reserved15 }, /* 0 */ + + { 0x003E, "Reserved16 for Apple use", PO_reserved16 }, /* 0 */ + + { 0x003F, "Reserved17 for Apple use", PO_reserved17 }, /* 0 */ + + { 0x0040, "Frame Round Rect (rect)", PO_FrameRRect }, /* 8 */ + + { 0x0041, "Paint Round Rect (rect)", PO_PaintRRect }, /* 8 */ + + { 0x0042, "Erase Round Rect (rect)", PO_EraseRRect }, /* 8 */ + + { 0x0043, "Invert Round Rect (rect)", PO_InvertRRect }, /* 8 */ + + { 0x0044, "Fill Round Rect (rect)", PO_FillRRect }, /* 8 */ + + { 0x0045, "Reserved18 for Apple use", PO_reserved18 }, /* 8 */ + + { 0x0046, "Reserved19 for Apple use", PO_reserved19 }, /* 8 */ + + { 0x0047, "Reserved20 for Apple use", PO_reserved20 }, /* 8 */ + + { 0x0048, "Frame Same Round Rect", PO_FrameSameRRect }, /* 0 */ + + { 0x0049, "Paint Same Round Rect", PO_PaintSameRRect }, /* 0 */ + + { 0x004A, "Erase Same Round Rect", PO_EraseSameRRect }, /* 0 */ + + { 0x004B, "Invert Same Round Rect", PO_InvertSameRRect }, /* 0 */ + + { 0x004C, "Fill Same Round Rect", PO_FillSameRRect }, /* 0 */ + + { 0x004D, "Reserved21 for Apple use", PO_reserved21 }, /* 0 */ + + { 0x004E, "Reserved22 for Apple use", PO_reserved22 }, /* 0 */ + + { 0x004F, "Reserved23 for Apple use", PO_reserved23 }, /* 0 */ + + { 0x0050, "Frame Oval (rect)", PO_FrameOval }, /* 8 */ + + { 0x0051, "Paint Oval (rect)", PO_PaintOval }, /* 8 */ + + { 0x0052, "Erase Oval (rect)", PO_EraseOval }, /* 8 */ + + { 0x0053, "Invert Oval (rect)", PO_InvertOval }, /* 8 */ + + { 0x0054, "Fill Oval (rect)", PO_FillOval }, /* 8 */ + + { 0x0055, "Reserved24 for Apple use", PO_reserved24 }, /* 8 */ + + { 0x0056, "Reserved25 for Apple use", PO_reserved25 }, /* 8 */ + + { 0x0057, "Reserved26 for Apple use", PO_reserved26 }, /* 8 */ + + { 0x0058, "Frame Same Oval (rect)", PO_FrameSameOval }, /* 0 */ + + { 0x0059, "Paint Same Oval (rect)", PO_PaintSameOval }, /* 0 */ + + { 0x005A, "Erase Same Oval (rect)", PO_EraseSameOval }, /* 0 */ + + { 0x005B, "Invert Same Oval (rect)", PO_InvertSameOval }, /* 0 */ + + { 0x005C, "Fill Same Oval (rect)", PO_FillSameOval }, /* 0 */ + + { 0x005D, "Reserved27 for Apple use", PO_reserved27 }, /* 0 */ + + { 0x005E, "Reserved28 for Apple use", PO_reserved28 }, /* 0 */ + + { 0x005F, "Reserved29 for Apple use", PO_reserved29 }, /* 0 */ + + { 0x0060, "Frame Arc (r,beg,angle)", PO_FrameArc }, /* 12 */ + + { 0x0061, "Paint Arc (r,beg,angle)", PO_PaintArc }, /* 12 */ + + { 0x0062, "Erase Arc (r,beg,angle)", PO_EraseArc }, /* 12 */ + + { 0x0063, "Invert Arc (r,beg,angle)", PO_InvertArc }, /* 12 */ + + { 0x0064, "Fill Arc (r,beg,angle)", PO_FillArc }, /* 12 */ + + { 0x0065, "Reserved30 for Apple use", PO_reserved30 }, /* 12 */ + + { 0x0066, "Reserved31 for Apple use", PO_reserved31 }, /* 12 */ + + { 0x0066, "Reserved32 for Apple use", PO_reserved32 }, /* 12 */ + + { 0x0068, "Frame Same Arc", PO_FrameSameArc }, /* 4 */ + + { 0x0069, "Paint Same Arc", PO_PaintSameArc }, /* 4 */ + + { 0x006A, "Erase Same Arc", PO_EraseSameArc }, /* 4 */ + + { 0x006B, "Invert Same Arc", PO_InvertSameArc }, /* 4 */ + + { 0x006C, "Fill Same Arc", PO_FillSameArc }, /* 4 */ + + { 0x006D, "Reserved33 for Apple use", PO_reserved33 }, /* 4 */ + + { 0x006E, "Reserved34 for Apple use", PO_reserved34 }, /* 4 */ + + { 0x006F, "Reserved35 for Apple use", PO_reserved35 }, /* 4 */ + + { 0x0070, "Frame Polygon", PO_FramePoly }, /* poly size */ + + { 0x0071, "Paint Polygon", PO_PaintPoly }, /* poly size */ + + { 0x0072, "Erase Polygon", PO_ErasePoly }, /* poly size */ + + { 0x0073, "Invert Polygon", PO_InvertPoly }, /* poly size */ + + { 0x0074, "Fill Polygon", PO_FillPoly }, /* poly size */ + + { 0x0075, "Reserved36 for Apple use", PO_reserved36 }, /* poly Size */ + + { 0x0076, "Reserved37 for Apple use", PO_reserved37 }, /* poly Size */ + + { 0x0077, "Reserved38 for Apple use", PO_reserved38 }, /* poly Size */ + + { 0x0078, "Frame Same Poly", PO_FrameSamePoly }, /* ? */ + + { 0x0079, "Paint Same Poly", PO_PaintSamePoly }, /* ? */ + + { 0x007A, "Erase Same Poly", PO_EraseSamePoly }, /* ? */ + + { 0x007B, "Invert Same Poly", PO_InvertSamePoly }, /* ? */ + + { 0x007C, "Fill Same Poly", PO_FillSamePoly }, /* ? */ + + { 0x007D, "Reserved39 for Apple use", PO_reserved39 }, /* 0 */ + + { 0x007E, "Reserved40 for Apple use", PO_reserved40 }, /* 0 */ + + { 0x007F, "Reserved41 for Apple use", PO_reserved41 }, /* 0 */ + + { 0x0080, "Frame Region", PO_FrameRgn }, /* rgn size */ + + { 0x0081, "Paint Region", PO_PaintRgn }, /* rgn size */ + + { 0x0082, "Erase Region", PO_EraseRgn }, /* rgn size */ + + { 0x0083, "Invert Region", PO_InvertRgn }, /* rgn size */ + + { 0x0084, "Fill Region", PO_FillRgn }, /* rgn size */ + + { 0x0085, "Reserved for Apple use", PO_reserved42 }, /* rgn size */ + + { 0x0086, "Reserved for Apple use", PO_reserved43 }, /* rgn size */ + + { 0x0087, "Reserved for Apple use", PO_reserved44 }, /* rgn size */ + + { 0x0088, "Frame Same Region", PO_FrameSameRgn }, /* ? */ + + { 0x0089, "Paint Same Region", PO_PaintSameRgn }, /* ? */ + + { 0x008A, "Erase Same Region", PO_EraseSameRgn }, /* ? */ + + { 0x008B, "Invert Same Region", PO_InvertSameRgn }, /* ? */ + + { 0x008C, "Fill Same Region", PO_FillSameRgn }, /* ? */ + + { 0x008D, "Reserved45 for Apple use", PO_reserved45 }, /* 0 */ + + { 0x008E, "Reserved46 for Apple use", PO_reserved46 }, /* 0 */ + + { 0x008F, "Reserved47 for Apple use", PO_reserved47 }, /* 0 */ + + { 0x0090, "Copybits, rect clipped", PO_BitsRect }, /* var */ + + { 0x0091, "Copybits, rgn clipped", PO_BitsRgn }, /* var */ + + { 0x0092, "Reserved48 for Apple use", PO_reserved48 }, /* 2+data */ + + { 0x0093, "Reserved49 for Apple use", PO_reserved49 }, /* 2+data */ + + { 0x0094, "Reserved50 for Apple use", PO_reserved50 }, /* 2+data */ + + { 0x0095, "Reserved51 for Apple use", PO_reserved51 }, /* 2+data */ + + { 0x0096, "Reserved52 for Apple use", PO_reserved52 }, /* 2+data */ + + { 0x0097, "Reserved53 for Apple use", PO_reserved53 }, /* 2+data */ + + { 0x0098, "Packed Bits (rect clipped)", PO_PackBitsRect }, /* var */ + + { 0x0099, "Packed Bits (rgn clipped)", PO_PackBitsRgn }, /* var */ + + { 0x009A, "32-bit QuickDraw RGB map", PO_DirectBitsRect }, /* 2+data */ + + { 0x009B, "32-bit QuickDraw RGB map", PO_DirectBitsRgn }, /* 2+data */ + + { 0x009C, "Reserved56 for Apple use", PO_reserved56 }, /* 2+data */ + + { 0x009D, "Reserved57 for Apple use", PO_reserved57 }, /* 2+data */ + + { 0x009E, "Reserved58 for Apple use", PO_reserved58 }, /* 2+data */ + + { 0x009F, "Reserved59 for Apple use", PO_reserved59 }, /* 2+data */ + + { 0x00A0, "Short Comment", PO_ShortComment }, /* 2 */ + + { 0x00A1, "Long Comment", PO_LongComment } /* 4+data */ + + + +#define IMLAST_INDEXED_OPCODE 0x00A1 + + + +#ifdef CANT_BE_INDEXED + + { 0x00A2, "Reserved60 for Apple use", PO_reserved60 }, /* 2+data */ + + /* etc... */ + + { 0x00AF, "Reserved for Apple use", PO_reserved73 }, /* 2+data */ + + { 0x00B0, "Reserved for Apple use", PO_reserved74 }, /* 0 */ + + /* etc... */ + + { 0x00CF, "Reserved for Apple use", PO_reserved105 }, /* 0 */ + + { 0x00D0, "Reserved for Apple use", PO_reserved106 }, /* 4+data */ + + /* etc... */ + + { 0x00FE, "Reserved for Apple use", PO_reserved152 }, /* 4+data */ + + { 0x00FF, "End of picture", PO_opEndPic }, /* 0 */ + + { 0x0100, "Reserved for Apple use", PO_reserved153 }, /* 2 */ + + /* etc... */ + + { 0x01FF, "Reserved for Apple use", PO_reserved408 }, /* 2 */ + + { 0x0200, "Reserved for Apple use", PO_reserved409 }, /* 4 */ + + /* etc... */ + + { 0x0BFF, "Reserved for Apple use", PO_reserved2968 }, /* 4 */ + + { 0x0C00, "Reserved for Apple use", PO_HeaderOp }, /* 2 */ + + { 0x0C01, "Reserved for Apple use", PO_reserved2969 }, /* ? */ + + /* etc... */ + + { 0xFFFF, "Reserved for Apple use", PO_reserved9999 }, /* ? */ + +#endif /* CANT_BE_INDEXED */ + + + +}; + + + + + +/* + + * + + * + + * GLOBAL VARIABLES + + * + + * + + */ + + + +/* g_ = global + + * l_ = local + + * e_ = external + + */ + + + +unsigned short g_Opcode = 0; /* Current (Last Read) PICT opcode */ + +struct GState g_gs; /* Current Graphics State */ + + + +int g_ioType; /* I/O flags */ + +int g_fd; /* Input file descriptor */ + +FILE * g_fp; /* Input file pointer */ + + + +ImVfb * g_vfb; /* The current image/vfb */ + +short g_pictVers = 0; /* PICT version */ + +struct Rect g_pictFrame; /* bounding rectangle of the image */ + + + +char g_buf[ IMSKIP_BUF_SIZ ]; /* Used to skip data */ + + + + + +/* + + * + + * + + * UTILILTY FUNCTIONS + + * + + */ + + + + + +/* + + * Returns TRUE if the two RGB colors are equal, or FALSE otherwise. + + * + + */ + +#ifdef __STDC__ + +int EqualRGB( struct RGBColor color1, struct RGBColor color2 ) + +#else + +int EqualRGB( color1, color2 ) + +struct RGBColor color1; + +struct RGBColor color2; + +#endif + +{ + + if ( color1.red != color2.red ) return( FALSE ); + + if ( color1.green != color2.green ) return( FALSE ); + + if ( color1.blue != color2.blue ) return( FALSE ); + + + + return( TRUE ); + +} + + + + + +/* + + * This is the main routine that is called to SET a VFB pixel at the + + * specified (x,y) coordinate to the given RGB color. This routine + + * also CLIPS (does not draw) pixels which fall outside the VFB + + * drawing area. This routine DOES NOT USE the pnSize "pen size" + + * to color pixels adjacent to the pen location. + + * + + * This routine uses the current pnMode (pen transfer mode) to determine + + * how to blend the source pixel with the destination pixel. + + * + + */ + +#ifdef __STDC__ + +static void SetPixel( short int x, short int y, struct RGBColor color ) + +#else + +static void SetPixel( x, y, color ) + + short int x, y; + + struct RGBColor color; + +#endif + +{ + + ImVfbPtr pptr; + + + + /* Do nothing and return if the pixel is out of bounds */ + + if ( (x < 0) || (y < 0) ) return; + + + + /* If the current pnMode is "transparent", */ + + /* and "color" is the same as the current background color, */ + + /* then there is no need to do any drawing. Return */ + + /* This has the effect of leaving transparent "holes" in an image. */ + + if ( (g_gs.pnMode == IMTM_TRANSPARENT) && + + EqualRGB( color, g_gs.rgbBkColor ) ) + + return; + + + + pptr = ImVfbQPtr( g_vfb, x, y ); + + ImVfbSRed( g_vfb, pptr, color.red ); + + ImVfbSGreen( g_vfb, pptr, color.green ); + + ImVfbSBlue( g_vfb, pptr, color.blue ); + +} + + + + + +/* + + * + + * This is the main routine that is called to PAINT a VFB pixel at the + + * specified (x,y) coordinate to the given RGB color. This routine + + * also CLIPS (does not draw) pixels which fall outside the VFB + + * drawing area. Additionally, this routine uses the pnSize "pen size" + + * to color pixels adjacent to the pen location based on the width and + + * height of the current pen size setting. + + * + + */ + +#ifdef __STDC__ + +static void PaintPixel( short int x, short int y, struct RGBColor color ) + +#else + +static void PaintPixel( x, y, color ) + + short int x, y; + + struct RGBColor color; + +#endif + +{ + + int i, j; + + + + if ( (g_gs.pnSize.h < 1) || (g_gs.pnSize.v < 1)) return; + + + + for ( i=x; i<(x+g_gs.pnSize.h); i++ ) { + + for ( j=y; j<(y+g_gs.pnSize.v); j++ ) { + + SetPixel( i, j, color ); + + } + + } + +} + + + + + +/* + + * + + * Bresenham's vector -> raster line conversion alogrithm, + + * written in special brute-force-and-ignorance style. + + * + + */ + +#ifdef __STDC__ + +static void rline( short int x1, short int y1, short int x2, short int y2 ) + +#else + +static void rline( x1, y1, x2, y2 ) + + short int x1, y1, x2, y2; + +#endif + +{ + + int dx, dy, step1, step2, d, deltap, deltaq; + + + +#ifdef DEBUG + + fprintf( stderr, "DEBUG: rline: (%d,%d) to (%d,%d)\n", x1, y1, x2, y2 ); + +#endif + + + + g_gs.pnLoc.h = x2; + + g_gs.pnLoc.v = y2; + + + + dx= abs( x2-x1 ); + + dy= abs( y2-y1 ); + + step1 = ( (x1= 0x0000 since it is unsigned. */ + + if ( oc <= IMLAST_INDEXED_OPCODE ) + + sprintf( msg, "Skipped opcode '%s' (0x%0x)", POCHandlerList[oc].name, oc ); + + else + + sprintf( msg, "Skipped opcode 'NO DESCRIPTION AVAILABLE' 0x%x", oc ); + + + + ImErrorWarning( msg, -1, ImErrNo = IMEUNSUPPORTED ); + + + + return( -1 ); + +} + + + + + +/* + + * + + * We have encountered an opcode which we do not know how to deal with. + + * The opcode/data is too complex to even be able to skip, so tell the + + * user we are going to have to stop where we are. + + * + + */ + +#ifdef __STDC__ + +static int OpcodeUns( unsigned short oc ) + +#else + +static int OpcodeUns( oc ) + + unsigned short oc; + +#endif + +{ + + char msg[ IMSTR_LEN ]; + + sprintf( msg, "Unsupported opcode 0x%x", oc ); + + ImErrorFatal( msg, -1, ImErrNo = IMESYNTAX ); + +} + + + + + +/* + + * + + * We have read an opcode that we could normaly handle, but we have + + * noticed that there is bad data following the opcode. Tell the user + + * that we can not continue. + + * + + */ + +#ifdef __STDC__ + +static int OpcodeErr( unsigned short oc ) + +#else + +static int OpcodeErr( oc ) + + unsigned short oc; + +#endif + +{ + + char msg[ IMSTR_LEN ]; + + sprintf( msg, "Bad data in opcode 0x%x", oc ); + + ImErrorFatal( msg, -1, ImErrNo = IMESYNTAX ); + +} + + + + + +#ifdef __STDC__ + +static int tstBit( unsigned char uchr, int cnt ) + +#else + +static int tstBit( uchr, cnt ) + + unsigned char uchr; + + int cnt; + +#endif + +{ + + if ( (cnt>7) || (cnt<0) ) return( (int) -1 ); + + + + return( (int) ((1< 0 ) + + { + + if ( count <= IMSKIP_BUF_SIZ ) + + skip = count; + + else + + skip = IMSKIP_BUF_SIZ; + + + + result = ImBinRead( g_ioType, g_fd, g_fp, g_buf, CHAR, 1, skip ); + + if ( result <= -1 ) return( result ); + + count -= skip; + + } + + + + return( result ); + +} + + + +/* + + * + + * Opcode handlers / functions + + * + + * + + */ + + + +/* 0x0000 - Null operation - 0 */ + +static int + +PO_NOP( ) + +{ + + int result = 0; + + + + return( result ); + +} + + + + + +/* 0x0001 - define clipRegion - region size */ + +static int + +PO_Clip( ) + +{ + + int result = 10; + + struct Region rgn; + + + + if ( ImBinReadStruct( g_ioType, g_fd, g_fp, &rgn, RegionFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + if ( rgn.rgnSize != 10 ) + + { + + result += (rgn.rgnSize - 10); + + if ( SkipBytes( (long) rgn.rgnSize - 10 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + } + + else + + { + + g_gs.clipRgn.rgnBBox.top = rgn.rgnBBox.top; + + g_gs.clipRgn.rgnBBox.left = rgn.rgnBBox.left; + + g_gs.clipRgn.rgnBBox.bottom = rgn.rgnBBox.bottom; + + g_gs.clipRgn.rgnBBox.right = rgn.rgnBBox.right; + + } + + + + return( result ); + + + +} + + + + + +/* 0x0002 - background pattern - 8 */ + +static int + +PO_BkPat( ) + +{ + + int result = 8; + + unsigned char uc; + + + + uc = (unsigned char) result; + + if ( ImBinRead( g_ioType, g_fd, g_fp, g_gs.bkPat, UCHAR, 1, uc ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + return( result ); + +} + + + + + +/* 0x0003 - text font (word) - 2 */ + +static int + +PO_TxFont( ) + +{ + + int result = 2; + + + + if ( ImBinRead( g_ioType, g_fd, g_fp, &g_gs.txFont, SHORT, 2, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + return( result ); + + + +} + + + + + +/* 0x0004 - text face (byte) - 1 */ + +static int + +PO_TxFace( ) + +{ + + int result = 1; + + if ( ImBinRead( g_ioType, g_fd, g_fp, &g_gs.txFace, UCHAR, 1, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + return( result ); + + + +} + + + + + +/* 0x0005 - text mode (word) - 2 */ + +static int + +PO_TxMode( ) + +{ + + int result = 2; + + + + if ( ImBinRead( g_ioType, g_fd, g_fp, &g_gs.txMode, SHORT, 2, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + return( result ); + + + +} + + + + + +/* 0x0006 - space extra (fixed point) - 4 */ + +static int + +PO_SpExtra( ) + +{ + + int result = 4; + + if ( ImBinRead( g_ioType, g_fd, g_fp, &g_gs.spExtra, IMFIXED, 4, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + return( result ); + +} + + + + + +/* 0x0007 - pen size (point) - 4 */ + +static int + +PO_PnSize( ) + +{ + + int result = 4; + + if ( ImBinReadStruct( g_ioType, g_fd, g_fp, &g_gs.pnSize, PointFields)== -1) + + { + + ImReturnBinError( ); + + } + + + + return( result ); + + + +} + + + + + +/* 0x0008 - pen mode (word) - 2 */ + +static int + +PO_PnMode( ) + +{ + + int result = 2; + + + + if ( ImBinRead( g_ioType, g_fd, g_fp, &g_gs.pnMode, SHORT, 2, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + return( result ); + + + +} + + + + + +/* 0x0009 - pen pattern - 8 */ + +static int + +PO_PnPat( ) + +{ + + int result = 8; + + unsigned char uc; + + + + uc = (unsigned char) result; + + if ( ImBinRead( g_ioType, g_fd, g_fp, g_gs.pnPat, UCHAR, 1, uc ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + return( result ); + + + +} + + + + + +/* 0x000A - fill pattern - 8 */ + +static int + +PO_FillPat( ) + +{ + + int result = 8; + + unsigned char uc; + + + + uc = (unsigned char) result; + + if ( ImBinRead( g_ioType, g_fd, g_fp, g_gs.fillPat, UCHAR, 1, uc )== -1) + + { + + ImReturnBinError( ); + + } + + + + return( result ); + + + +} + + + + + +/* 0x000B - oval size (point) - 4 */ + +static int + +PO_OvSize( ) + +{ + + int result = 4; + + + + if ( ImBinReadStruct( g_ioType, g_fd, g_fp, &g_gs.ovSize, PointFields)== -1) + + { + + ImReturnBinError( ); + + } + + + + return( result ); + + + +} + + + + + +/* 0x000C - dh, dv (word) - 4 */ + +static int + +PO_Origin( ) + +{ + + int result = 4; + + + + if ( ImBinReadStruct( g_ioType, g_fd, g_fp, &g_gs.origin, PointFields)== -1) + + { + + ImReturnBinError( ); + + } + + + + return( result ); + + + +} + + + + + +/* 0x000D - text size (word) - 2 */ + +static int + +PO_TxSize( ) + +{ + + int result = 2; + + + + if ( ImBinRead( g_ioType, g_fd, g_fp, &g_gs.txSize, SHORT, 2, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + return( result ); + + + +} + + + + + +/* 0x000E - foreground color (long) - 4 */ + +static int + +PO_FgColor( ) + +{ + + int result = 4; + + if ( ImBinRead( g_ioType, g_fd, g_fp, &g_gs.fgColor, LONG, 4, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + return( result ); + +} + + + + + +/* 0x000F - background color (long) - 4 */ + +static int + +PO_BkColor( ) + +{ + + int result = 4; + + if ( ImBinRead( g_ioType, g_fd, g_fp, &g_gs.bkColor, LONG, 4, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + return( result ); + + + +} + + + + + +/* 0x0010 - numer, denom (points) - 4 */ + +static int + +PO_TxRatio( ) + +{ + + int result = 8; + + + + if ( ImBinReadStruct( g_ioType, g_fd, g_fp, &g_gs.txRatNum, PointFields)== -1) + + { + + ImReturnBinError( ); + + } + + if ( ImBinReadStruct( g_ioType, g_fd, g_fp, &g_gs.txRatDen, PointFields)== -1) + + { + + ImReturnBinError( ); + + } + + + + return( result ); + + + +} + + + + + +/* 0x0011 - version (byte) - 1 */ + +static int + +PO_Version( ) + +{ + + int result = 1; + + + + if ( ImBinRead( g_ioType, g_fd, g_fp, &g_gs.version, UCHAR, 1, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + return( result ); + + + +} + + + + + +/* 0x0012 - color background pattern - variable */ + +static int + +PO_BkPixPat( ) + +{ + + int result = 2; + + unsigned short ushort1; + + + + if ( ImBinRead( g_ioType, g_fd, g_fp, &ushort1, SHORT, 2, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + if ( ushort1 == 1 ) + + { + + result += -666; + + if ( SkipBytes( (long) -666 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + } + + else + + { /* ushort1 == 2 */ + + result += 70; + + if ( SkipBytes( (long) 70 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + } + + OpcodeWarn( g_Opcode ); + + + + return( result ); + + + +} + + + + + +/* 0x0013 - color pen pattern - variable */ + +static int + +PO_PnPixPat( ) + +{ + + int result = 2; + + unsigned short ushort1=0; + + + + if ( ImBinRead( g_ioType, g_fd, g_fp, &ushort1, SHORT, 2, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + if ( ushort1 == 1 ) + + { + + result += -666; + + if ( SkipBytes( (long) -666 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + } + + else + + { /* ushort1 == 2 */ + + result += 70; + + if ( SkipBytes( (long) 70 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + } + + OpcodeWarn( g_Opcode ); + + + + return( result ); + + + +} + + + + + +/* 0x0014 - color fill pattern - variable */ + +static int + +PO_FillPixPat( ) + +{ + + int result = 2; + + int i; + + + + if ( ImBinRead( g_ioType, g_fd, g_fp, &i, INT, 2, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + if ( i == 1 ) + + { + + result += -666; + + if ( SkipBytes( (long) -666 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + } + + else + + { /* i == 2 */ + + result += 70; + + if ( SkipBytes( (long) 70 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + } + + OpcodeWarn( g_Opcode ); + + + + return( result ); + + + +} + + + + + +/* 0x0015 - fractional pen position - 2 */ + +static int + +PO_PnLocHFrac( ) + +{ + + int result = 2; + + + + if ( ImBinRead( g_ioType, g_fd, g_fp, &g_gs.pnLocHFrac, SHORT, 2, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + return( result ); + + + +} + + + + + +/* 0x0016 - extra for each character - 2 */ + +static int + +PO_ChExtra( ) + +{ + + int result = 2; + + + + if ( ImBinRead( g_ioType, g_fd, g_fp, &g_gs.chExtra, SHORT, 2, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + return( result ); + + + +} + + + + + +/* 0x0017 - reserved for Apple use - 0 */ + +static int + +PO_reserved1( ) + +{ + + int result = 0; + + + + return( result ); + + + +} + + + + + +/* 0x0018 - reserved for Apple use - 0 */ + +static int + +PO_reserved2( ) + +{ + + int result = 0; + + + + return( result ); + + + +} + + + + + +/* 0x0019 - reserved for Apple use - 0 */ + +static int + +PO_reserved3( ) + +{ + + int result = 0; + + + + return( result ); + + + +} + + + + + +/* 0x001A - set RGB foreground color - variable */ + +static int + +PO_RGBFgCol( ) + +{ + + int result = 4; + + + + if ( ImBinReadStruct( g_ioType, g_fd, g_fp, &g_gs.rgbFgColor, RGBColorFields)== -1) + + { + + ImReturnBinError( ); + + } + + + + return( result ); + + + +} + + + + + +/* 0x001B - set RGB background color - variable */ + +static int + +PO_RGBBkCol( ) + +{ + + int result = 4; + + + + if ( ImBinReadStruct( g_ioType, g_fd, g_fp, &g_gs.rgbBkColor, RGBColorFields)== -1) + + { + + ImReturnBinError( ); + + } + + + + return( result ); + + + +} + + + + + +/* 0x001C - hilite mode flag - 0 */ + +static int + +PO_HiliteMode( ) + +{ + + int result = 0; + + + + g_gs.hiliteMode = ! g_gs.hiliteMode; + + + + return( result ); + + + +} + + + + + +/* 0x001D - RGB hilite color - variable */ + +static int + +PO_HiliteColor( ) + +{ + + int result = 4; + + + + if ( ImBinReadStruct( g_ioType, g_fd, g_fp, &g_gs.hiliteColor, RGBColorFields)== -1) + + { + + ImReturnBinError( ); + + } + + + + return( result ); + + + +} + + + + + +/* 0x001E - use default hilite color - 0 */ + +static int + +PO_DefHilite( ) + +{ + + int result = 0; + + + + g_gs.hiliteColor.red = 0; + + g_gs.hiliteColor.green = 0; + + g_gs.hiliteColor.blue = 0; + + + + return( result ); + + + +} + + + + + +/* 0x001F - RGB OpColor : arith. modes - 0 */ + +static int + +PO_OpColor( ) + +{ + + int result = 0; + + + + g_gs.opColor = ! g_gs.opColor; + + + + return( result ); + + + +} + + + + + +/* 0x0020 - pnLoc, newPt (points) - 8 */ + +static int + +PO_Line( ) + +{ + + int result = 8; + + struct Point point1; + + + + if (ImBinReadStruct( g_ioType, g_fd, g_fp, &g_gs.pnLoc, PointFields )==-1) + + { + + ImReturnBinError( ); + + } + + if (ImBinReadStruct( g_ioType, g_fd, g_fp, &point1, PointFields )==-1) + + { + + ImReturnBinError( ); + + } + + rline( g_gs.pnLoc.h, g_gs.pnLoc.v, point1.h, point1.v ); + + + + return( result ); + + + +} + + + + + +/* 0x0021 - newPt (point) - 4 */ + +static int + +PO_LineFrom( ) + +{ + + int result = 4; + + struct Point point1; + + + + if ( ImBinReadStruct( g_ioType, g_fd, g_fp, &point1, PointFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + rline( g_gs.pnLoc.h, g_gs.pnLoc.v, point1.h, point1.v ); + + + + return( result ); + + + +} + + + + + +/* 0x0022 - pnLoc (point), dh, dv - 6 */ + +static int + +PO_ShortLine( ) + +{ + + int result = 6; + + char char1, char2; + + + + if ( ImBinReadStruct( g_ioType, g_fd, g_fp, &g_gs.pnLoc, PointFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + if ( ImBinRead( g_ioType, g_fd, g_fp, &char1, CHAR, 1, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + if ( ImBinRead( g_ioType, g_fd, g_fp, &char2, CHAR, 1, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + rline( g_gs.pnLoc.h, g_gs.pnLoc.v, + + (g_gs.pnLoc.h + char1), (g_gs.pnLoc.v + char2) ); + + + + return( result ); + + + +} + + + + + +/* 0x0023 - dh (byte), dv (byte) - 2 */ + +static int + +PO_ShortLineFrom( ) + +{ + + int result = 2; + + char char1, char2; + + + + if ( ImBinRead( g_ioType, g_fd, g_fp, &char1, CHAR, 1, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + if ( ImBinRead( g_ioType, g_fd, g_fp, &char2, CHAR, 1, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + rline( g_gs.pnLoc.h, g_gs.pnLoc.v, + + g_gs.pnLoc.h + char1, g_gs.pnLoc.v + char2 ); + + + + return( result ); + + + +} + + + + + +/* 0x0024 - reserved for Apple use - 2+data */ + +static int + +PO_reserved4( ) + +{ + + int result = 2; + + unsigned short ushort1=0; + + + + if ( ImBinRead( g_ioType, g_fd, g_fp, &ushort1, USHORT, 2, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + if ( ushort1 > 0 ) + + { + + result += ushort1; + + if ( SkipBytes( (long) ushort1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + } + + OpcodeWarn( g_Opcode ); + + + + return( result ); + + + +} + + + + + +/* 0x0025 - reserved for Apple use - 2+data */ + +static int + +PO_reserved5( ) + +{ + + int result = 2; + + unsigned short ushort1=0; + + + + if ( ImBinRead( g_ioType, g_fd, g_fp, &ushort1, USHORT, 2, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + if ( ushort1 > 0 ) + + { + + result += ushort1; + + if ( SkipBytes( (long) ushort1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + } + + OpcodeWarn( g_Opcode ); + + + + return( result ); + + + +} + + + + + +/* 0x0026 - reserved for Apple use - 2+data */ + +static int + +PO_reserved6( ) + +{ + + int result = 2; + + unsigned short ushort1=0; + + + + if ( ImBinRead( g_ioType, g_fd, g_fp, &ushort1, USHORT, 2, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + if ( ushort1 > 0 ) + + { + + result += ushort1; + + if ( SkipBytes( (long) ushort1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + } + + OpcodeWarn( g_Opcode ); + + + + return( result ); + + + +} + + + + + +/* 0x0027 - reserved for Apple use - 2+data */ + +static int + +PO_reserved7( ) + +{ + + int result = 2; + + unsigned short ushort1=0; + + + + if ( ImBinRead( g_ioType, g_fd, g_fp, &ushort1, USHORT, 2, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + if ( ushort1 > 0 ) + + { + + result += ushort1; + + if ( SkipBytes( (long) ushort1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + } + + OpcodeWarn( g_Opcode ); + + + + return( result ); + + + +} + + + + + +/* 0x0028 - txLoc (point), count, text - 5+text */ + +static int + +PO_LongText( ) + +{ + + int result = 5; + + struct Point point1; + + unsigned char uchar1; + + char msg[ IMSTR_LEN ]; + + + + if ( ImBinReadStruct( g_ioType, g_fd, g_fp, &point1, PointFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + if ( ImBinRead( g_ioType, g_fd, g_fp, &uchar1, UCHAR, 1, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + result += uchar1; + + if ( SkipBytes( (long) uchar1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + OpcodeWarn( g_Opcode ); + + + + g_buf[uchar1] = '\0'; + + + +#ifdef DEBUG + + fprintf( stderr, "SKIPPED TEXT: '%s'\n", g_buf ); + +#endif + + + + return( result ); + + + +} + + + + + +/* 0x0029 - dh, count, text - 2+text */ + +static int + +PO_DHText( ) + +{ + + int result = 2; + + unsigned char uchar1, uchar2; + + + + if ( ImBinRead( g_ioType, g_fd, g_fp, &uchar1, UCHAR, 1, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + if ( ImBinRead( g_ioType, g_fd, g_fp, &uchar2, UCHAR, 1, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + result += uchar2; + + if ( SkipBytes( (long) uchar2 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + OpcodeWarn( g_Opcode ); + + + + g_buf[uchar2] = '\0'; + + + +#ifdef DEBUG + + fprintf( stderr, "SKIPPED TEXT: '%s'\n", g_buf ); + +#endif + + + + return( result ); + + + +} + + + + + +/* 0x002A - dv, count, text - 2+text */ + +static int + +PO_DVText( ) + +{ + + int result = 2; + + unsigned char uchar1, uchar2; + + + + if ( ImBinRead( g_ioType, g_fd, g_fp, &uchar1, UCHAR, 1, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + if ( ImBinRead( g_ioType, g_fd, g_fp, &uchar2, UCHAR, 1, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + result += uchar2; + + if ( SkipBytes( (long) uchar2 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + OpcodeWarn( g_Opcode ); + + + + g_buf[uchar2] = '\0'; + + + +#ifdef DEBUG + + fprintf( stderr, "SKIPPED TEXT: '%s'\n", g_buf ); + +#endif + + + + return( result ); + + + +} + + + + + +/* 0x002B - dh, dv, count, text - 3+text */ + +static int + +PO_DHDVText( ) + +{ + + int result = 3; + + unsigned char uchar1, uchar2, uchar3; + + + + if ( ImBinRead( g_ioType, g_fd, g_fp, &uchar1, UCHAR, 1, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + if ( ImBinRead( g_ioType, g_fd, g_fp, &uchar2, UCHAR, 1, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + if ( ImBinRead( g_ioType, g_fd, g_fp, &uchar3, UCHAR, 1, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + result += uchar3; + + if ( SkipBytes( (long) uchar3 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + OpcodeWarn( g_Opcode ); + + + + g_buf[uchar3] = '\0'; + + + +#ifdef DEBUG + + fprintf( stderr, "SKIPPED TEXT: '%s'\n", g_buf ); + +#endif + + + + return( result ); + + + +} + + + + + +/* 0x002C - reserved for Apple use - 2+data */ + +static int + +PO_reserved8( ) + +{ + + int result = 2; + + short short1=0; + + + + if ( ImBinRead( g_ioType, g_fd, g_fp, &short1, SHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + result += short1; + + if ( SkipBytes( (long) short1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + OpcodeWarn( g_Opcode ); + + + + return( result ); + + + +} + + + + + +/* 0x002D - reserved for Apple use - 2+data */ + +static int + +PO_reserved9( ) + +{ + + int result = 2; + + short short1=0; + + + + if ( ImBinRead( g_ioType, g_fd, g_fp, &short1, SHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + result += short1; + + if ( SkipBytes( (long) short1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + OpcodeWarn( g_Opcode ); + + + + return( result ); + + + +} + + + + + +/* 0x002E - reserved for Apple use - 2+data */ + +static int + +PO_reserved10( ) + +{ + + int result = 2; + + short short1=0; + + + + if ( ImBinRead( g_ioType, g_fd, g_fp, &short1, SHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + result += short1; + + if ( SkipBytes( (long) short1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + OpcodeWarn( g_Opcode ); + + + + return( result ); + + + +} + + + + + +/* 0x002F - reserved for Apple use - 2+data */ + +static int + +PO_reserved11( ) + +{ + + int result = 2; + + short short1=0; + + + + if ( ImBinRead( g_ioType, g_fd, g_fp, &short1, SHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + result += short1; + + if ( SkipBytes( (long) short1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + OpcodeWarn( g_Opcode ); + + + + return( result ); + + + +} + + + + + +/* 0x0030 - rect - 8 */ + +static int + +PO_FrameRect( ) + +{ + + int result = 8; + + int i=0; + + + + if ( ImBinReadStruct( g_ioType, g_fd, g_fp, &g_gs.lastRect, RectFields)== -1) + + { + + ImReturnBinError(); + + } + +#ifdef DEBUG + +fprintf( stderr, "DEBUG FrameRect: t, l, b, r = 0x%0x, 0x%0x, 0x%0x, 0x%0x\n", g_gs.lastRect.top, g_gs.lastRect.left, g_gs.lastRect.bottom, g_gs.lastRect.right ); + +#endif + + /* Draw the sides of the rectangle */ + + for ( i=g_gs.lastRect.top; i<=(g_gs.lastRect.bottom-g_gs.pnSize.v); i++ ) + + { + + PaintPixel( g_gs.lastRect.left, i, g_gs.rgbFgColor ); + + PaintPixel( g_gs.lastRect.right-g_gs.pnSize.h, i, g_gs.rgbFgColor ); + + } + + /* Draw the top & bottom of the rectangle */ + + for ( i=g_gs.lastRect.left; i<=(g_gs.lastRect.right-g_gs.pnSize.h); i++ ) + + { + + PaintPixel( i, g_gs.lastRect.top, g_gs.rgbFgColor ); + + PaintPixel( i, g_gs.lastRect.bottom-g_gs.pnSize.v, g_gs.rgbFgColor ); + + } + + + + return( result ); + + + +} + + + + + +/* 0x0031 - rect - 8 */ + +static int + +PO_PaintRect( ) + +{ + + int result = 8; + + int i=0, j=0; + + + + if ( ImBinReadStruct( g_ioType, g_fd, g_fp, &g_gs.lastRect, RectFields)== -1) + + { + + ImReturnBinError(); + + } + + for ( i=g_gs.lastRect.left+1; i 250 then byteCount is a word */ + + /* otherwise byteCount is a byte */ + + if ( rowBytes > 250 ) BCLen = 2; + + else BCLen = 1; + + result += 26; + + if ( ImBinReadStruct(g_ioType, g_fd, g_fp, &bMap, BMapFields)== -1) + + { + + ImReturnBinError(); + + } + + if ( g_Opcode == 0x0091 || g_Opcode == 0x0099 ) + + { + + /* + + * If we have a region, read and ignore rgn data + + */ + + result += 10; + + if ( ImBinRead( g_ioType, g_fd, g_fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + if (ImBinReadStruct(g_ioType, g_fd, g_fp, &rect1, RectFields)==-1) + + { + + ImReturnBinError(); + + } + + if ( ushort1 > 10 ) + + { + + ushort2 = ushort1 - 10; + + result += ushort2; + + if ( SkipBytes( (long) ushort2 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + } + + } + +#ifdef DEBUG + +fprintf( stderr, "DEBUG PackBitsRect ; rowBytes = %d\n", rowBytes ); + +fprintf( stderr, "DEBUG PackBitsRect ; bnd = {%d,%d,%d,%d}\n", bMap.bnd.top, bMap.bnd.left, bMap.bnd.bottom, bMap.bnd.right ); + +fprintf( stderr, "DEBUG PackBitsRect ; src = {%d,%d,%d,%d}\n", bMap.srcRect.top, bMap.srcRect.left, bMap.srcRect.bottom, bMap.srcRect.right ); + +fprintf( stderr, "DEBUG PackBitsRect ; dst = {%d,%d,%d,%d}\n", bMap.dstRect.top, bMap.dstRect.left, bMap.dstRect.bottom, bMap.dstRect.right ); + +#endif + + padRight = bMap.bnd.right - bMap.bnd.left; + + sLines = bMap.bnd.bottom - bMap.bnd.top; + + for ( lin=0; lin padRight ) + + break; + + x = bMap.dstRect.left + byt*8 + bit; + + y = bMap.dstRect.top + lin; + + if ( tstBit( runBuffer[byt], 7-bit ) ) + + bitC = IMBLACK; + + else + + bitC = IMWHITE; + + color.red = color.green = color.blue = bitC; + + SetPixel( x, y, color ); + + } /* bits in each byte in each scanline */ + + } /* byte in each scanline */ + + } /* each scanline */ + + + + free( (char *)runBuffer ); + + free( (char *)pixBuffer ); + + return( result ); + +} + + + + + +/* 0x0090, 0x0091, 0x0098, 0x0099 - CopyBits */ + +/* PixMaps - Version 2 PICTs */ + +static int + +PixMap( ) + +{ + + int result=0; /* return value - number of bytes read */ + + int i=0; /* generic loop counter */ + + unsigned short rowBytes; /* PICT PixMap row byte count */ + + short BCLen=0; /* PICT PixMap byteCount length */ + + struct PMap pMap; /* PICT PixMap structure */ + + unsigned short ushort1=0; + + unsigned short ushort2=0; + + struct Rect rect1; + + short padRight; + + short sLines, lin; + + unsigned int byteCount=0; /* PICT PixMap byteCount */ + + struct CTable cTab; /* PICT ColorLookupTable struct */ + + unsigned short byt; + + int x, y; /* Convenient short names */ + + struct CTEntry ctEnt; /* PICT ColorLookupTable Entry struct */ + + ImCltPtr ctPtr; /* VFB ColorTable pointer */ + + unsigned char * runBuffer; + + unsigned char * pixBuffer; + + char msg[ IMSTR_LEN ]; + + struct RGBColor color; + + struct Rect pm_srcRect; + + struct Rect pm_dstRect; + + int old_mode; /* Remember the old pen transfer mode */ + + char message[100]; /* ImInfo message */ + + + + result = 2; + + /* read rowBytes */ + + if ( ImBinRead( g_ioType, g_fd, g_fp, &rowBytes, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + runBuffer = (unsigned char*) malloc( 4 * rowBytes ); + + pixBuffer = (unsigned char*) malloc( 4 * rowBytes ); + + if ( (runBuffer == NULL) || (pixBuffer == NULL) ) + + { + + sprintf( msg, "Failed to allocate runBuffer" ); + + ImErrorFatal( msg, -1, ImErrNo = IMESYNTAX ); + + } + + + + /* if rowBytes >= 0x8000 then we have a PixMap */ + + if ( rowBytes >= 0x8000 ) + + { + + rowBytes = 0x7FFF & rowBytes; + + } + + /* if rowBytes > 250 then byteCount is a word */ + + /* otherwise byteCount is a byte */ + + if ( rowBytes > 250 ) + + BCLen = 2; + + else + + BCLen = 1; + + result += 46; + + if ( ImBinReadStruct(g_ioType, g_fd, g_fp, &pMap, PMapFields)== -1) + + { + + ImReturnBinError(); + + } + + /* Do we have a REGION-Clipped PixMaps ? */ + + if ( g_Opcode == 0x0091 || g_Opcode == 0x0099 ) + + { + + /* If we have a region, read and ignore rgn data */ + + result += 10; + + if ( ImBinRead( g_ioType, g_fd, g_fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + if (ImBinReadStruct(g_ioType, g_fd, g_fp, &rect1, RectFields)==-1) + + { + + ImReturnBinError(); + + } + + if ( ushort1 > 10 ) + + { + + ushort2 = ushort1 - 10; + + result += ushort2; + + if ( SkipBytes( (long) ushort2 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + } + + } + +#ifdef DEBUG + +fprintf( stderr, "DEBUG PackBitsRect ; rowBytes = %d\n", rowBytes ); + +fprintf( stderr, "DEBUG PackBitsRect ; bnd = {%d,%d,%d,%d}\n", pMap.bnd.top, pMap.bnd.left, pMap.bnd.bottom, pMap.bnd.right ); + +#endif + + padRight = pMap.bnd.right - pMap.bnd.left; + + sLines = pMap.bnd.bottom - pMap.bnd.top; + + /* Read the colorTable header */ + + result += 8; + + + + sprintf( message, "%d-bit Color Indexed", pMap.pixelSize ); + + ImInfo( "Type", message ); + + + + if (ImBinReadStruct(g_ioType, g_fd, g_fp, &cTab, CTableFields)==-1) + + { + + ImReturnBinError(); + + } + + /* Allocate a new IM-CLT */ + + if ( clt == NULL ) + + { + + clt = ImCltAlloc( cTab.ctSize + 1 ); + + ImVfbSClt( g_vfb, clt ); + + sprintf( message, "%d Entries", (cTab.ctSize+1) ); + + ImInfo( "Color Table", message ); + + } + + else + + { + + ImInfo( "Color Table", "none" ); + + } + + + + /* Read the colorTable data */ + + for ( i=0; i<=cTab.ctSize; i++ ) + + { + + result += 8; + + if (ImBinReadStruct(g_ioType,g_fd,g_fp,&ctEnt,CTEntryFields)==-1) + + { + + ImReturnBinError(); + + } + +#ifdef DEBUG + +if ( i==0 || i==cTab.ctSize ) { + + fprintf( stderr, "DEBUG PackBitsRect ; ctEnt[%d] = (r=0x%0x,g=0x%0x,b=0x%0x)\n", i, ctEnt.red, ctEnt.green, ctEnt.blue ); + + if ( i==0 ) fprintf( stderr, "...etc...\n" ); + +} + +#endif + + ctPtr = ImCltQPtr( clt, i ); + + ImCltSRed( ctPtr, ctEnt.red ); + + ImCltSGreen( ctPtr, ctEnt.green ); + + ImCltSBlue( ctPtr, ctEnt.blue ); + + } + + + + /* Read the srcRect, dstRect, and transfer mode */ + + result += 18; + + if (ImBinReadStruct(g_ioType,g_fd,g_fp,&pm_srcRect,RectFields)==-1) + + { + + ImReturnBinError(); + + } + + if (ImBinReadStruct(g_ioType,g_fd,g_fp,&pm_dstRect,RectFields)==-1) + + { + + ImReturnBinError(); + + } + + old_mode = g_gs.pnMode; /* Remember global pen transfer mode */ + + if ( ImBinRead( g_ioType, g_fd, g_fp, &g_gs.pnMode, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + /* Read and process the PixData scanlines */ + + for ( lin=0; lin= pMap.bnd.right ) break; + +#ifdef DEBUG + + y = g_pictFrame.bottom - (pMap.bnd.top + lin); + +#else + + y = pMap.bnd.top + lin; + +#endif + + ctPtr = ImCltQPtr( clt, runBuffer[byt] ); + + color.red = ImCltQRed( ctPtr ); + + color.green = ImCltQGreen( ctPtr ); + + color.blue = ImCltQBlue( ctPtr ); + + SetPixel( x, y, color ); + + } /* byte in each scanline */ + + } /* each scanline */ + + + + g_gs.pnMode = old_mode; /* Restore the global pen transfer mode */ + + + + return( result ); + +} + + + + + +/* 0x009A, 0x009B - DirectBits CopyBits */ + +/* 16 and 32 bit DirectBits PixMaps - Version 2 PICTs */ + +static int + +DirectBits( ) + +{ + + int result=0; /* return value - number of bytes read */ + + int i=0; /* generic loop counter */ + + unsigned short rowBytes; /* PICT PixMap row byte count */ + + short BCLen=0; /* PICT PixMap byteCount length */ + + struct PMap pMap; /* PICT PixMap structure */ + + unsigned short ushort1=0; + + unsigned short ushort2=0; + + struct Rect rect1; + + short padRight; + + short sLines, lin; + + unsigned int byteCount=0; /* PICT PixMap byteCount */ + + unsigned short byt; + + int x, y; /* Convenient short names */ + + struct CTEntry ctEnt; /* PICT ColorLookupTable Entry struct */ + + ImCltPtr ctPtr; /* VFB ColorTable pointer */ + + unsigned char * runBuffer; + + unsigned char * pixBuffer; + + char msg[ IMSTR_LEN ]; + + struct RGBColor color; + + struct Rect pm_srcRect; + + struct Rect pm_dstRect; + + int old_mode; /* Remember the old pen transfer mode */ + + char message[100]; /* ImInfo message */ + + int chanOff; /* Component offset into pixel data */ + + int alphaOff; /* Alpha component offset into pixel data */ + + + + result = 2; + + + + /* read and skip baseAddr */ + + result += 4; + + if ( SkipBytes( (long) 4 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + /* read rowBytes */ + + if ( ImBinRead( g_ioType, g_fd, g_fp, &rowBytes, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + runBuffer = (unsigned char*) malloc( 4 * rowBytes ); + + pixBuffer = (unsigned char*) malloc( 4 * rowBytes ); + + if ( (runBuffer == NULL) || (pixBuffer == NULL) ) + + { + + sprintf( msg, "Failed to allocate runBuffer" ); + + ImErrorFatal( msg, -1, ImErrNo = IMESYNTAX ); + + } + + + + /* if rowBytes >= 0x8000 then we have a PixMap */ + + if ( rowBytes >= 0x8000 ) + + { + + rowBytes = 0x7FFF & rowBytes; + + } + + /* if rowBytes > 250 then byteCount is a word */ + + /* otherwise byteCount is a byte */ + + if ( rowBytes > 250 ) + + BCLen = 2; + + else + + BCLen = 1; + + + + result += 46; + + if ( ImBinReadStruct(g_ioType, g_fd, g_fp, &pMap, PMapFields)== -1) + + { + + ImReturnBinError(); + + } + + + + /* Do we have a REGION-Clipped PixMaps ? */ + + if ( g_Opcode == 0x009B ) + + { + + /* If we have a region, read and ignore rgn data */ + + result += 10; + + if ( ImBinRead( g_ioType, g_fd, g_fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + if (ImBinReadStruct(g_ioType, g_fd, g_fp, &rect1, RectFields)==-1) + + { + + ImReturnBinError(); + + } + + if ( ushort1 > 10 ) + + { + + ushort2 = ushort1 - 10; + + result += ushort2; + + if ( SkipBytes( (long) ushort2 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + } + + } + +#ifdef DEBUG + +fprintf( stderr, "DEBUG PackBitsRect ; rowBytes = %d\n", rowBytes ); + +fprintf( stderr, "DEBUG PackBitsRect ; bnd = {%d,%d,%d,%d}\n", pMap.bnd.top, pMap.bnd.left, pMap.bnd.bottom, pMap.bnd.right ); + +#endif + + padRight = pMap.bnd.right - pMap.bnd.left; + + sLines = pMap.bnd.bottom - pMap.bnd.top; + + + + /* Read the srcRect, dstRect, and transfer mode */ + + result += 18; + + if (ImBinReadStruct(g_ioType,g_fd,g_fp,&pm_srcRect,RectFields)==-1) + + { + + ImReturnBinError(); + + } + + if (ImBinReadStruct(g_ioType,g_fd,g_fp,&pm_dstRect,RectFields)==-1) + + { + + ImReturnBinError(); + + } + + old_mode = g_gs.pnMode; /* Remember global pen transfer mode */ + + if ( ImBinRead( g_ioType, g_fd, g_fp, &g_gs.pnMode, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + /* Read and process the PixData scanlines */ + + for ( lin=0; lin> 2; + + color.blue = color.blue | (ushort1 & 0x7); + + ushort1 = ushort1 >> 3; + + /* Construct GREEN */ + + color.green = ushort1 & 0x1f; + + color.green = color.green << 3; + + ushort1 = ushort1 >> 2; + + color.green = color.green | (ushort1 & 0x7); + + ushort1 = ushort1 >> 3; + + /* Construct RED */ + + color.red = ushort1 & 0x1f; + + color.red = color.red << 3; + + ushort1 = ushort1 >> 2; + + color.red = color.red | (ushort1 & 0x7); + + ushort1 = ushort1 >> 3; + + + + /* Don't draw pad-bits (beyond pixmap bounds) */ + + if ( x >= pMap.bnd.right ) break; + + y = pMap.bnd.top + lin; + + SetPixel( x, y, color ); + + } + + + + } else if ( pMap.packType == 4 ) + + { + + + + /* ITS A 32 BIT PIXMAP */ + + + + UnpackBits( pixBuffer, runBuffer, &byteCount ); + + + + chanOff = byteCount/pMap.cmpCount; + + + + /* See if there is an alpha channel (cmpCount == 4 ) */ + + /* Note: Apple stores alpha channels FIRST (ARGB) */ + + /* For now, ignore alpha channel by stepping over it */ + + if ( pMap.cmpCount == 4 ) + + alphaOff = chanOff; + + else + + alphaOff = 0; + + + + y = pMap.bnd.top + lin; + + + + for ( byt=0; byt= pMap.bnd.right ) break; + + SetPixel( x, y, color ); + + } + + + + } else + + { + + sprintf( msg, "Bad packType %d", pMap.packType ); + + ImErrorFatal( msg, -1, ImErrNo = IMESYNTAX ); + + } + + + +#ifdef DEBUG + + printf( "DEBUG: byteCount = %d, x = %d, y = %d\n", byteCount, x, y ); + +#endif + + + + } /* each scanline */ + + + + g_gs.pnMode = old_mode; /* Restore the global pen transfer mode */ + + + + return( result ); + +} + + + + + +/* 0x0090, 0x0091, 0x0098, 0x0099 - CopyBits */ + +static int + +CopyBits( ) + +{ + + int result=0; /* return value - number of bytes read */ + + + + if ( g_pictVers == IMPICT_V1 ) + + { + + /* Bitmap - 1 bit QuickDraw */ + + result = BitMap( ); + + } else /* g_pictVers == IMPICT_V2 */ + + { + + if ( g_Opcode == 0x009A || g_Opcode == 0x009B ) { + + /* DirectBits - 16 and 32 bit QuickDraw */ + + result = DirectBits( ); + + } else { + + /* Indexed Color - 2, 4, and 8 bit QuickDraw */ + + result = PixMap( ); + + } + + } + + + + return( result ); + + + +} + + + + + + + +/* 0x0090 - copybits, rect clip - variable */ + +static int + +PO_BitsRect( ) + +{ + + + + return( CopyBits( ) ); + + + +} + + + + + +/* 0x0091 - copybits, rgn clip - variable */ + +static int + +PO_BitsRgn( ) + +{ + + return( CopyBits( ) ); + + + +} + + + + + +/* 0x0098 - packed, rect clip - variable */ + +static int + +PO_PackBitsRect( ) + +{ + + + + return( CopyBits( ) ); + + + +} + + + + + +/* 0x0099 - packed, rgn clip - variable */ + +static int + +PO_PackBitsRgn( ) + +{ + + return( CopyBits( ) ); + +} + + + + + +/* 0x009A - 32-bit QuickDraw - 2+data */ + +static int + +PO_DirectBitsRect( ) + +{ + + + + return( CopyBits( ) ); + + + +} + + + + + +/* 0x009B - 32-bit QuickDraw - 2+data */ + +static int + +PO_DirectBitsRgn( ) + +{ + + + + return( CopyBits( ) ); + + + +} + + + +/* 0x009C - reserved for Apple use - 2+data */ + +static int + +PO_reserved56( ) + +{ + + int result = 2; + + + + OpcodeUns( g_Opcode ); + + + + return( result ); + + + +} + + + + + +/* 0x009D - reserved for Apple use - 2+data */ + +static int + +PO_reserved57( ) + +{ + + int result = 2; + + + + OpcodeUns( g_Opcode ); + + + + return( result ); + + + +} + + + + + +/* 0x009E - reserved for Apple use - 2+data */ + +static int + +PO_reserved58( ) + +{ + + int result = 2; + + + + OpcodeUns( g_Opcode ); + + + + return( result ); + + + +} + + + + + +/* 0x009F - reserved for Apple use - 2+data */ + +static int + +PO_reserved59( ) + +{ + + int result = 2; + + + + OpcodeUns( g_Opcode ); + + + + return( result ); + + + +} + + + + + +/* 0x00A0 - kind (word) - 2 */ + +static int + +PO_ShortComment( ) + +{ + + int result = 2; + + unsigned short ushort1 = 0; + + + + if ( ImBinRead( g_ioType, g_fd, g_fp, &ushort1, USHORT, 2, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + /* This is too anoying to see the warning over and over */ + + /* OpcodeWarn( g_Opcode ); */ + + + + return( result ); + + + +} + + + + + +/* 0x00A1 - kind, size (words), data - 4+data */ + +static int + +PO_LongComment( ) + +{ + + int result = 4; + + unsigned short ushort1 = 0; + + unsigned short ushort2 = 0; + + + + if ( ImBinRead( g_ioType, g_fd, g_fp, &ushort1, USHORT, 2, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + if ( ImBinRead( g_ioType, g_fd, g_fp, &ushort2, USHORT, 2, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + result += ushort2; + + if ( SkipBytes( (long) ushort2 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + OpcodeWarn( g_Opcode ); + + + + return( result ); + +} + + + + + +/* 0x00FF - end of picture - 2 */ + +static int + +PO_opEndPic( ) + +{ + + int result = 2; + + + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: OPCode just read is 'End Of Picture' (0x%0x)\n", g_Opcode ); + +#endif + + + + return( result ); + + + +} + + + + + +/* 0x0C00 - Header Opcode - 2 */ + +static int + +PO_HeaderOp( ) + +{ + + int result = 2; + + + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: OPCode just read is 'Header Opcode' (0x%0x)\n", g_Opcode ); + +#endif + + + + return( result ); + + + +} + + + + + +/* + + * + + * libim functions + + * + + * + + */ + + + +/* + + * FUNCTION + + * imPictRead - read a Macintosh PICT file + + * + + * DESCRIPTION + + * The file header is read and the size of the image determined. + + * Space is allocated for the VFB and the run codes read in to + + * a run buffer. The run buffer is then expanded into straight + + * RGB values in the VFB and the completed VFB added to the tag list. + + */ + + + +static int /* Returns # tags read in */ + +#ifdef __STDC__ + +imPictRead( int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable ) + +#else + +imPictRead( ioType, fd, fp, flags, tagTable ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable * flags; /* Format flags */ + + TagTable * tagTable; /* Tag list to add to */ + +#endif + +{ + + int result=0; + + int x, y, i, j; /* Convenient short names */ + + + + /* PICT STUFF */ + + unsigned short pictSize=0; /* size (in bytes) of image data */ + + short OCLen=0; /* PICT opcode length */ + + + + /* bitmap-pixmap stuff */ + + int NData=0; /* Amount of data read for one opcode */ + + + + /* GENERIC VARS */ + + unsigned short ushort1; + + unsigned long ulong1; + + char msg[ IMSTR_LEN ]; + + char message[100]; /* ImInfo message */ + + + + + + /* SET GLOBAL COPIES OF ARGUMENTS */ + + g_ioType = ioType; + + g_fd = fd; + + g_fp = fp; + + + + + + /* + + * Initialize the "Graphics State" structure elements to "zero". + + */ + + memset( (void *)&g_gs, 0x00, sizeof( struct GState ) ); + + g_gs.rgbBkColor.red = IMWHITE; + + g_gs.rgbBkColor.green = IMWHITE; + + g_gs.rgbBkColor.blue = IMWHITE; + + g_gs.rgbFgColor.red = IMBLACK; + + g_gs.rgbFgColor.green = IMBLACK; + + g_gs.rgbFgColor.blue = IMBLACK; + + g_gs.pnSize.h = 1; + + g_gs.pnSize.v = 1; + + + + + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: set our byte order to MBF\n" ); + +#endif + + /* + + * PICT files are usually generated on Macs, which have an MBF + + * byte order. There is no floating point in the file so we + + * won't bother setting the float format for BIN. + + */ + + BinByteOrder( BINMBF ); + + + + + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: read and ignore the 512 byte header\n" ); + +#endif + + /* + + * Read in the 512 byte header. + + * Its application specific so after we read it we can throw it away. + + */ + + if ( SkipBytes( (long) 512 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: read pictSize\n" ); + +#endif + + /* + + * Read in the 2 byte "pictSize" field. + + * Since PICT1 format PICTs can only be 32k the 2 bytes works fine, + + * but for PICT2 format PICTs this 2 bytes only represents the low + + * order word. Apple says we can ignore this value. + + */ + + if ( ImBinRead( ioType, fd, fp, &pictSize, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: pictSize (LSW) = 0x%0x (%d)\n", pictSize, pictSize ); + +#endif + + + + + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: read g_pictFrame\n" ); + +#endif + + /* + + * Read in the 8 byte "g_pictFrame" field. + + * The g_pictFrame describes the bounding rectangle of the image. + + * We can use this to tell how big our Virtual Frame Buffer should be. + + */ + + if ( ImBinReadStruct( ioType, fd, fp, &g_pictFrame, RectFields) == -1) + + { + + ImReturnBinError( ); + + } + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: g_pictFrame.top = 0x%0x (%d)\n", g_pictFrame.top, g_pictFrame.top ); + +fprintf( stderr, "DEBUG: g_pictFrame.left = 0x%0x (%d)\n", g_pictFrame.left, g_pictFrame.left ); + +fprintf( stderr, "DEBUG: g_pictFrame.bottom = 0x%0x (%d)\n", g_pictFrame.bottom, g_pictFrame.bottom ); + +fprintf( stderr, "DEBUG: g_pictFrame.right = 0x%0x (%d)\n", g_pictFrame.right, g_pictFrame.right ); + +#endif + + + + + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: read version\n" ); + +#endif + + /* + + * Read in the 2 bytes of pict version numbers. + + * If it is a PICT2 then also skip over the extra 28 bytes of junk. + + */ + + if ( ImBinRead( ioType, fd, fp, &g_pictVers, SHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + if ( g_pictVers == IMPICT_V1 ) + + { + + /* 1 BYTE OPCODES */ + + OCLen = 1; + + strcpy( message, "1 (original PICT)" ); + + } + + else if ( g_pictVers == IMPICT_V2 ) + + { + + /* 2 BYTE OPCODES */ + + OCLen = 2; + + + + /* PICT2 has some extra "reserved junk" we can skip over */ + + if ( SkipBytes( (long) 28 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + strcpy( message, "2 (32-bit QuickDraw PICT2)" ); + + } + + else + + { + + sprintf( msg, "Unknown PICT version 0x%0x (%d)", g_pictVers, g_pictVers); + + ImErrorFatal( msg, -1, ImErrNo = IMESYNTAX ); + + } + + ImInfo( "Version", message ); + +#ifdef DEBUG + +if ( g_pictVers == IMPICT_V1 ) + + fprintf( stderr, "DEBUG: This is a version 1 PICT file\n" ); + +if ( g_pictVers == IMPICT_V2 ) + + fprintf( stderr, "DEBUG: This is a version 2 PICT file\n" ); + +#endif + + + + ImInfo( "Byte Order", "Most Significant Byte First" ); + + + + /* + + * We are now ready to start reading opcodes and data, + + * so we might as well allocate a VFB of the required size... + + */ + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: allocate a vfb\n" ); + +#endif + + x = g_pictFrame.right /* - g_pictFrame.left */ ; + + y = g_pictFrame.bottom /* - g_pictFrame.top */ ; + + + + sprintf( message, "%d x %d", x, y ); + + ImInfo( "Resolution", message ); + + + + if ( (g_vfb = ImVfbAlloc( x, y, IMVFBRGB )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + + + /* + + * Fill the VFB with the default background color. + + */ + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: fill the vfb with background color\n" ); + +#endif + + for ( i=0; i= 0x0000 since it is unsigned. */ + + if ( g_Opcode <= IMLAST_INDEXED_OPCODE ) + + { + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: OPCode just read is '%s' (0x%0x)\n", POCHandlerList[g_Opcode].name, g_Opcode ); + +#endif + + NData += (*POCHandlerList[g_Opcode].handler)( ); + + } + + else + + { + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: OPCode just read = 0x%0x\n", g_Opcode ); + +#endif + + /* 0x00A2 to 0x00AF - reserved for Apple use - 2+data */ + + if ( (g_Opcode>=0x00A2) && (g_Opcode<=0x00AF) ) + + { + + NData += 2; + + if ( ImBinRead( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + if ( ushort1 > 0 ) + + { + + NData += ushort1; + + if ( SkipBytes( (long) ushort1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + } + + OpcodeWarn( g_Opcode ); + + /* 0x00B0 to 0x00CF - reserved for Apple use - 0 */ + + } + + else if ( (g_Opcode>=0x00B0) && (g_Opcode<=0x00CF) ) + + { + + NData += 0; + + OpcodeWarn( g_Opcode ); + + /* 0x00D0 to 0x00FE - reserved for Apple use - 4+data */ + + } + + else if ( (g_Opcode>=0x00D0) && (g_Opcode<=0x00FE) ) + + { + + NData += 4; + + if ( ImBinRead( ioType, fd, fp, &ulong1, ULONG, 4, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + if ( ulong1 > 0 ) + + { + + NData += (int) ulong1; + + if ( SkipBytes( (long) ulong1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + } + + OpcodeWarn( g_Opcode ); + + /* 0x0100 to 0x01FF - reserved for Apple use - 2 */ + + } + + else if ( (g_Opcode>=0x0100) && (g_Opcode<=0x01FF) ) + + { + + NData += 2; + + if ( ImBinRead( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + OpcodeWarn( g_Opcode ); + + /* 0x0200 to 0x0BFF - reserved for Apple use - 2*nnXX */ + + } + + else if ( (g_Opcode>=0x0200) && (g_Opcode<=0x7FFF) ) + + { + + NData += (g_Opcode >> 8) * 2; + + if ( SkipBytes( (long) NData ) == -1 ) + + { + + ImReturnBinError( ); + + } + + OpcodeWarn( g_Opcode ); + + /* 0x8100 to 0xFFFF - reserved for Apple use - 2*nnXX */ + + } + + else if ( (g_Opcode>=0x8100) && (g_Opcode<=0xFFFF) ) + + { + + NData += 4; + + if ( ImBinRead( ioType, fd, fp, &ulong1, ULONG, 4, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + if ( ulong1 > 0 ) + + { + + NData += (int) ulong1; + + if ( SkipBytes( (long) ulong1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + } + + OpcodeWarn( g_Opcode ); + + } + + else + + { + + NData = 0; + + OpcodeUns( g_Opcode ); + + } + + } + + + + /* + + * If we are reading PICT2, the next opcode must be word + + * aligned relative to the last opcode. So, if we are not + + * on an even boundary, read on extra "junk" byte. + + * NOTE: If the amount of data read after an opcode is odd + + * then we know we have to read an extra byte. + + */ + + if ( g_pictVers == IMPICT_V2 ) + + { + + if ( (NData % 2) == 1 ) + + { + + NData += 1; + + if ( ImBinRead( ioType, fd, fp, &i, INT, 1, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + } + + } + + result += NData; + + NData = 0; + + + + /* + + * read the next opcode + + */ + + if ( ImBinRead( ioType, fd, fp, &g_Opcode, USHORT, OCLen, 1)== -1) + + { + + ImReturnBinError( ); + + } + + + + } + + + + ImInfo( "Compression Type", "Apple Macintosh PackBits" ); + + + + /* + + * Add the CLUT to the tagTable. + + */ + + TagTableAppend( tagTable, TagEntryAlloc( "image clt", POINTER, &clt ) ); + + + + /* + + * Add the VFB to the tagTable. + + */ + + TagTableAppend( tagTable, TagEntryAlloc( "image vfb", POINTER, &g_vfb ) ); + + + + return ( result ); + +} + + + + + + + +/* + + * FUNCTION + + * imPictWrite - write a Macintosh PICT file + + * + + * DESCRIPTION + + * The tag list is checked to see that there is a VFB in it, and + + * that there's only one VFB. That VFB is then queried, and the + + * PICT file header set up and written out. The VFB data is then + + * read out and converted to run-codes, and those codes written out. + + */ + + + + + +/* + + * + + * Write a PICT file containing a 1-bit BitMap + + * + + * Returns # of tags used + + * + + */ + +static int + +#ifdef __STDC__ + +imPictWritePB1( ImFileFormatWriteMap *ppMap, int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable ) + +#else + +imPictWritePB1( ppMap, ioType, fd, fp, flags, tagTable ) + + ImFileFormatWriteMap *ppMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable * flags; /* Format flags */ + + TagTable * tagTable; /* Tag list to read from */ + +#endif + +{ + + ImVfb * vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointer */ + + unsigned char * runBuffer; /* Run buffer */ + + unsigned char * rbp; /* Run buffer pointer */ + + ImClt * clt; /* A VFB ColorTable */ + + ImCltPtr ctPtr; /* A VFB ColorTable pointer */ + + + + /* PICT STUFF */ + + unsigned int byteCount; /* PICT bit-pixMap byteCount */ + + unsigned short rowBytes; /* PICT bit-pixMap rowBytes */ + + short pictSize; /* size (in bytes) of image data */ + + short OCLen; /* PICT opcode length */ + + short BCLen; /* PICT bit-pixMap byteCount length */ + + struct PMap pMap; /* PICT PixMap structure */ + + struct CTable cTab; /* PICT ColorLookupTable struct */ + + struct CTEntry ctEnt; /* PICT ColorLookupTable Entry struct*/ + + int nColors; + + int sLines; + + int clutBytes; + + int bytesPerSLine; + + int bytesPerBand; + + int sLinesPerBand; + + int sLine; + + int i = 0; + + int col; + + unsigned long OddCount; + + Pict2Header header; /* PICT 2 file header */ + + int nBands; + + + + /* GENERIC VARS */ + + struct Region rgn; + + unsigned short ushort1; + + char message[100]; /* ImInfo message */ + + + + + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + clt = ImVfbQClt( vfb ); + + + + + + /* + + * PICT files are usually generated on Macs, which have an MBF + + * byte order. There is no floating point in the file so we + + * won't bother setting the float format for BIN. + + */ + + BinByteOrder( BINMBF ); + + + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: write 512 byte header\n" ); + +#endif + + /* + + * Write in the 512 byte (blank) header. + + */ + + memset( (void *)g_buf, 0x00, 512 ); + + if ( ImBinWrite( ioType, fd, fp, g_buf, UCHAR, 1, 512 )== -1) + + { + + ImReturnBinError( ); + + } + + + + + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: write pictSize\n" ); + +#endif + + /* + + * Write the 2 byte "pictSize" field. + + * Since PICT1 format PICTs can only be 32k the 2 bytes works fine, + + * but for PICT2 format PICTs this 2 bytes only represents the low + + * order word. As IM V, p92 states, "Many applications already support + + * PICT resources larger than 32k. ... This was made possible by having + + * QuickDraw ignore the size word and simply read the picture until the + + * end-of-picture opcode is reached." + + */ + + pictSize = 0x0000; + + if ( ImBinWrite( ioType, fd, fp, &pictSize, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: write g_pictFrame\n" ); + +#endif + + /* + + * Write the 8 byte "g_pictFrame" field. + + * The g_pictFrame describes the bounding rectangle of the image. + + * We can get this by looking at the size of our Virtual Frame Buffer. + + */ + + g_pictFrame.top = 0; + + g_pictFrame.left = 0; + + g_pictFrame.bottom = ImVfbQHeight( vfb ); + + g_pictFrame.right = ImVfbQWidth( vfb ); + + if ( ImBinWriteStruct( ioType, fd, fp, &g_pictFrame, RectFields)== -1) + + { + + ImReturnBinError( ); + + } + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: g_pictFrame.top = 0x%0x (%d)\n", g_pictFrame.top, g_pictFrame.top ); + +fprintf( stderr, "DEBUG: g_pictFrame.left = 0x%0x (%d)\n", g_pictFrame.left, g_pictFrame.left ); + +fprintf( stderr, "DEBUG: g_pictFrame.bottom = 0x%0x (%d)\n", g_pictFrame.bottom, g_pictFrame.bottom ); + +fprintf( stderr, "DEBUG: g_pictFrame.right = 0x%0x (%d)\n", g_pictFrame.right, g_pictFrame.right ); + +#endif + + + + + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: write version\n" ); + +#endif + + /* + + * Write the 2 bytes of pict version numbers. + + * Note that we always write version 2 PICT files. + + */ + + ushort1 = 0x0011; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + ushort1 = 0x02FF; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + ImInfo( "Version", "2" ); + + ImInfo( "Byte Order", "Most Significant Byte First" ); + + sprintf( message, "%d x %d", g_pictFrame.right, g_pictFrame.bottom ); + + ImInfo( "Resolution", message ); + + + + /* PICT2 has some extra "reserved junk" we have to fill */ + + header.headerOp = 0x0C00; + + header.version = ~0; + + header.reserved1 = 0; + + header.hRes = 0x00480000; + + header.vRes = 0x00480000; + + header.srcRect_top = 0; + + header.srcRect_left = 0; + + header.srcRect_bottom = g_pictFrame.bottom; + + header.srcRect_right = g_pictFrame.right; + + header.reserved2 = 0; + + if ( ImBinWriteStruct( ioType, fd, fp, &header, Pict2HeaderFields)== -1) + + { + + ImReturnBinError( ); + + } + + + + + + OCLen = 2; /* 2 BYTE OPCODES */ + + + + /* + + * Most PICT2 files usualy have several opcodes to get bitmaps started: + + */ + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: write the traditional start-em-off opcodes\n" ); + +#endif + + + + /* ShortComment { picDwgBeg }; */ + + ushort1 = 0x00A0; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + ushort1 = IMPC_PICDWGBEG; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + + + /* DefHilite {}; */ + + ushort1 = 0x001E; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + /* ClipRgn { 10, { 0, 0, g_pictFrame.right, g_pictFrame.bottom }, $"" }; */ + + ushort1 = 0x0001; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + rgn.rgnSize = 10; + + rgn.rgnBBox.top = 0; + + rgn.rgnBBox.left = 0; + + rgn.rgnBBox.bottom = g_pictFrame.bottom; + + rgn.rgnBBox.right = g_pictFrame.right; + + if ( ImBinWriteStruct( ioType, fd, fp, &rgn, RegionFields)== -1) + + { + + ImReturnBinError( ); + + } + + + + + + /* + + * Allocate space for a run buffer large enough for 1 unencoded + + * scanline. An encoded scanline should be smaller. + + */ + + ImMalloc( runBuffer, unsigned char *, 4 * g_pictFrame.right ); + + + + + + /* + + * Construct all of the "magic" numbers we need to write the image. + + */ + + + + /* How many colors do we have to work with in the CLUT ? */ + + nColors = ImCltQNColors( clt ); + + + + /* + + * Figure out how many PixMap bands we want to break the image into. + + * Note that for each band we have to write out a new copy of the CLUT, + + * so we will try to figure on writting the least amount of data, + + * with the additional constraint that bands are no larger than 32k. + + */ + + sLines = g_pictFrame.bottom; + + clutBytes = nColors * 8; + + bytesPerSLine = g_pictFrame.right + 2; + + bytesPerBand = 32000 - clutBytes; + + sLinesPerBand = bytesPerBand / bytesPerSLine; + + /* The last band may be small. */ + + nBands = (sLines / sLinesPerBand) + 1; + + + + /* Calculate the rowBytes and ByteCountSize for the pixMap */ + + rowBytes = ( ( ( 8 * ( g_pictFrame.right ) + 31 ) >> 5 ) << 2 ); + + /* if rowBytes > 250 then byteCount is a word, else it is a byte */ + + if ( rowBytes > 250 ) + + BCLen = 2; + + else + + BCLen = 1; + + /* rowBytes = 0x8000 | rowBytes; High-bit lit means "PixMap" */ + + + + + + /* ShortComment { picBitBeg }; */ + + ushort1 = 0x00A0; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + ushort1 = IMPC_PICBITBEG; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + /* Data counter to keep track of opcode alignment */ + + OddCount = 0; + + + + /* Write out the [Pack]PixMap g_Opcode itself */ + + if ( rowBytes >= 8 ) + + ushort1 = 0x0098; + + else + + ushort1 = 0x0090; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + /* Write rowBytes */ + + rowBytes |= 0x8000; /* High-bit lit means "PixMap" */ + + if ( ImBinWrite( ioType, fd, fp, &rowBytes, USHORT, 2, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + rowBytes &= 0x7FFF; /* Undo the High-bit so we can use rowBytes "normally" */ + + + + /* Write PixMap header */ + + pMap.bnd.top = 0; + + pMap.bnd.left = 0; + + pMap.bnd.bottom = g_pictFrame.bottom; + + pMap.bnd.right = g_pictFrame.right; + + pMap.version = 0; + + pMap.packType = 0; + + pMap.packSize = 0x00000000; + + pMap.hRes = 0x00480000; + + pMap.vRes = 0x00480000; + + pMap.pixelType = 0; + + pMap.pixelSize = 8; + + pMap.cmpCount = 1; + + pMap.cmpSize = 8; + + pMap.planeBytes = 0x00000000; + + pMap.pmTable = 0L; + + pMap.pmReserved = 0x00000000; + + if ( ImBinWriteStruct(ioType, fd, fp, &pMap, PMapFields)== -1) + + { + + ImReturnBinError(); + + } + + + + /* Write out the CLUT (Color Lookup Table) for each PixMap (yuk!) */ + + cTab.ctSeed = 0L; + + cTab.ctFlags = 0x0000; + + cTab.ctSize = nColors - 1; + + if ( ImBinWriteStruct( ioType, fd, fp, &cTab, CTableFields ) == -1 ) + + { + + ImReturnBinError(); + + } + + ctPtr = ImCltQFirst( clt ); + + for ( i=0; i= 8 ) + + { + + PackBits( runBuffer, (unsigned char*) g_buf, &byteCount ); + + rbp = (unsigned char*) g_buf; + + } + + else + + rbp = (unsigned char*) runBuffer; + + + + /* Write the packed scanline */ + + if ( ImBinWrite( ioType, fd, fp, &byteCount, UINT, + + BCLen, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + if ( ImBinWrite( ioType, fd, fp, rbp, UCHAR, + + 1, byteCount ) == -1 ) + + { + + ImReturnBinError( ); + + } + + OddCount += (BCLen + byteCount); + + } + + + + /* + + * If we have written an odd amount of data for the opcode, + + * then we have to pad an extra byte so that the next opcode + + * is word aligned + + */ + + if ( IModd( OddCount ) ) + + { + + g_buf[0] = '\0'; + + if ( ImBinWrite( ioType, fd, fp, g_buf, UCHAR, 1, 1)== -1) + + { + + ImReturnBinError( ); + + } + + } + + + + /* ShortComment { picBitEnd }; */ + + ushort1 = 0x00A0; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + ushort1 = IMPC_PICBITEND; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + + + /* ShortComment { picDwgEnd }; */ + + ushort1 = 0x00A0; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + ushort1 = IMPC_PICDWGEND; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: write the END opcode\n" ); + +#endif + + + + /* PO_opEndPic { }; */ + + ushort1 = 0x00FF; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + free( (char *)runBuffer ); + + + + return ( 1 ); + +} + + + + + +/* + + * + + * Write a PICT file containing an 8-bit CLUT and PixMap + + * + + * Returns # of tags used + + * + + */ + +static int + +#ifdef __STDC__ + +imPictWritePB8( ImFileFormatWriteMap *ppMap, int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable ) + +#else + +imPictWritePB8( ppMap, ioType, fd, fp, flags, tagTable ) + + ImFileFormatWriteMap *ppMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable * flags; /* Format flags */ + + TagTable * tagTable; /* Tag list to read from */ + +#endif + +{ + + ImVfb * vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointer */ + + unsigned char * runBuffer; /* Run buffer */ + + unsigned char * rbp; /* Run buffer pointer */ + + ImClt * clt; /* A VFB ColorTable */ + + ImCltPtr ctPtr; /* A VFB ColorTable pointer */ + + + + /* PICT STUFF */ + + unsigned int byteCount; /* PICT bit-pixMap byteCount */ + + unsigned short rowBytes; /* PICT bit-pixMap rowBytes */ + + short pictSize; /* size (in bytes) of image data */ + + short OCLen; /* PICT opcode length */ + + short BCLen; /* PICT bit-pixMap byteCount length */ + + struct PMap pMap; /* PICT PixMap structure */ + + struct CTable cTab; /* PICT ColorLookupTable struct */ + + struct CTEntry ctEnt; /* PICT ColorLookupTable Entry struct*/ + + int nColors; + + int sLines; + + int clutBytes; + + int bytesPerSLine; + + int bytesPerBand; + + int sLinesPerBand; + + int sLine; + + int i = 0; + + int col; + + unsigned long OddCount; + + Pict2Header header; /* PICT 2 file header */ + + int nBands; + + + + /* GENERIC VARS */ + + struct Region rgn; + + unsigned short ushort1; + + char message[100]; /* ImInfo message */ + + + + + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + clt = ImVfbQClt( vfb ); + + + + + + /* + + * PICT files are usually generated on Macs, which have an MBF + + * byte order. There is no floating point in the file so we + + * won't bother setting the float format for BIN. + + */ + + BinByteOrder( BINMBF ); + + + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: write 512 byte header\n" ); + +#endif + + /* + + * Write in the 512 byte (blank) header. + + */ + + memset( (void *)g_buf, 0x00, 512 ); + + if ( ImBinWrite( ioType, fd, fp, g_buf, UCHAR, 1, 512 )== -1) + + { + + ImReturnBinError( ); + + } + + + + + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: write pictSize\n" ); + +#endif + + /* + + * Write the 2 byte "pictSize" field. + + * Since PICT1 format PICTs can only be 32k the 2 bytes works fine, + + * but for PICT2 format PICTs this 2 bytes only represents the low + + * order word. As IM V, p92 states, "Many applications already support + + * PICT resources larger than 32k. ... This was made possible by having + + * QuickDraw ignore the size word and simply read the picture until the + + * end-of-picture opcode is reached." + + */ + + pictSize = 0x0000; + + if ( ImBinWrite( ioType, fd, fp, &pictSize, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: write g_pictFrame\n" ); + +#endif + + /* + + * Write the 8 byte "g_pictFrame" field. + + * The g_pictFrame describes the bounding rectangle of the image. + + * We can get this by looking at the size of our Virtual Frame Buffer. + + */ + + g_pictFrame.top = 0; + + g_pictFrame.left = 0; + + g_pictFrame.bottom = ImVfbQHeight( vfb ); + + g_pictFrame.right = ImVfbQWidth( vfb ); + + if ( ImBinWriteStruct( ioType, fd, fp, &g_pictFrame, RectFields)== -1) + + { + + ImReturnBinError( ); + + } + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: g_pictFrame.top = 0x%0x (%d)\n", g_pictFrame.top, g_pictFrame.top ); + +fprintf( stderr, "DEBUG: g_pictFrame.left = 0x%0x (%d)\n", g_pictFrame.left, g_pictFrame.left ); + +fprintf( stderr, "DEBUG: g_pictFrame.bottom = 0x%0x (%d)\n", g_pictFrame.bottom, g_pictFrame.bottom ); + +fprintf( stderr, "DEBUG: g_pictFrame.right = 0x%0x (%d)\n", g_pictFrame.right, g_pictFrame.right ); + +#endif + + + + + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: write version\n" ); + +#endif + + /* + + * Write the 2 bytes of pict version numbers. + + * Note that we always write version 2 PICT files. + + */ + + ushort1 = 0x0011; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + ushort1 = 0x02FF; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + ImInfo( "Version", "2" ); + + ImInfo( "Byte Order", "Most Significant Byte First" ); + + sprintf( message, "%d x %d", g_pictFrame.right, g_pictFrame.bottom ); + + ImInfo( "Resolution", message ); + + + + /* PICT2 has some extra "reserved junk" we have to fill */ + + header.headerOp = 0x0C00; + + header.version = ~0; + + header.reserved1 = 0; + + header.hRes = 0x00480000; + + header.vRes = 0x00480000; + + header.srcRect_top = 0; + + header.srcRect_left = 0; + + header.srcRect_bottom = g_pictFrame.bottom; + + header.srcRect_right = g_pictFrame.right; + + header.reserved2 = 0; + + if ( ImBinWriteStruct( ioType, fd, fp, &header, Pict2HeaderFields)== -1) + + { + + ImReturnBinError( ); + + } + + + + + + OCLen = 2; /* 2 BYTE OPCODES */ + + + + /* + + * Most PICT2 files usualy have several opcodes to get bitmaps started: + + */ + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: write the traditional start-em-off opcodes\n" ); + +#endif + + + + /* ShortComment { picDwgBeg }; */ + + ushort1 = 0x00A0; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + ushort1 = IMPC_PICDWGBEG; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + + + /* DefHilite {}; */ + + ushort1 = 0x001E; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + /* ClipRgn { 10, { 0, 0, g_pictFrame.right, g_pictFrame.bottom }, $"" }; */ + + ushort1 = 0x0001; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + rgn.rgnSize = 10; + + rgn.rgnBBox.top = 0; + + rgn.rgnBBox.left = 0; + + rgn.rgnBBox.bottom = g_pictFrame.bottom; + + rgn.rgnBBox.right = g_pictFrame.right; + + if ( ImBinWriteStruct( ioType, fd, fp, &rgn, RegionFields)== -1) + + { + + ImReturnBinError( ); + + } + + + + + + /* + + * Allocate space for a run buffer large enough for 1 unencoded + + * scanline. An encoded scanline should be smaller. + + */ + + ImMalloc( runBuffer, unsigned char *, 4 * g_pictFrame.right ); + + + + + + /* + + * Construct all of the "magic" numbers we need to write the image. + + */ + + + + /* How many colors do we have to work with in the CLUT ? */ + + nColors = ImCltQNColors( clt ); + + + + /* + + * Figure out how many PixMap bands we want to break the image into. + + * Note that for each band we have to write out a new copy of the CLUT, + + * so we will try to figure on writting the least amount of data, + + * with the additional constraint that bands are no larger than 32k. + + */ + + sLines = g_pictFrame.bottom; + + clutBytes = nColors * 8; + + bytesPerSLine = g_pictFrame.right + 2; + + bytesPerBand = 32000 - clutBytes; + + sLinesPerBand = bytesPerBand / bytesPerSLine; + + /* The last band may be small. */ + + nBands = (sLines / sLinesPerBand) + 1; + + + + /* Calculate the rowBytes and ByteCountSize for the pixMap */ + + rowBytes = ( ( ( 8 * ( g_pictFrame.right ) + 31 ) >> 5 ) << 2 ); + + /* if rowBytes > 250 then byteCount is a word, else it is a byte */ + + if ( rowBytes > 250 ) + + BCLen = 2; + + else + + BCLen = 1; + + /* rowBytes = 0x8000 | rowBytes; High-bit lit means "PixMap" */ + + + + + + /* ShortComment { picBitBeg }; */ + + ushort1 = 0x00A0; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + ushort1 = IMPC_PICBITBEG; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + /* Data counter to keep track of opcode alignment */ + + OddCount = 0; + + + + /* Write out the [Pack]PixMap g_Opcode itself */ + + if ( rowBytes >= 8 ) + + ushort1 = 0x0098; + + else + + ushort1 = 0x0090; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + /* Write rowBytes */ + + rowBytes |= 0x8000; /* High-bit lit means "PixMap" */ + + if ( ImBinWrite( ioType, fd, fp, &rowBytes, USHORT, 2, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + rowBytes &= 0x7FFF; /* Undo the High-bit so we can use rowBytes "normally" */ + + + + /* Write PixMap header */ + + pMap.bnd.top = 0; + + pMap.bnd.left = 0; + + pMap.bnd.bottom = g_pictFrame.bottom; + + pMap.bnd.right = g_pictFrame.right; + + pMap.version = 0; + + pMap.packType = 0; + + pMap.packSize = 0x00000000; + + pMap.hRes = 0x00480000; + + pMap.vRes = 0x00480000; + + pMap.pixelType = 0; + + pMap.pixelSize = 8; + + pMap.cmpCount = 1; + + pMap.cmpSize = 8; + + pMap.planeBytes = 0x00000000; + + pMap.pmTable = 0L; + + pMap.pmReserved = 0x00000000; + + if ( ImBinWriteStruct(ioType, fd, fp, &pMap, PMapFields)== -1) + + { + + ImReturnBinError(); + + } + + + + /* Write out the CLUT (Color Lookup Table) for each PixMap (yuk!) */ + + cTab.ctSeed = 0L; + + cTab.ctFlags = 0x0000; + + cTab.ctSize = nColors - 1; + + if ( ImBinWriteStruct( ioType, fd, fp, &cTab, CTableFields ) == -1 ) + + { + + ImReturnBinError(); + + } + + ctPtr = ImCltQFirst( clt ); + + for ( i=0; i= 8 ) + + { + + PackBits( runBuffer, (unsigned char*) g_buf, &byteCount ); + + rbp = (unsigned char*) g_buf; + + } + + else + + rbp = (unsigned char*) runBuffer; + + + + /* Write the packed scanline */ + + if ( ImBinWrite( ioType, fd, fp, &byteCount, UINT, + + BCLen, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + if ( ImBinWrite( ioType, fd, fp, rbp, UCHAR, + + 1, byteCount ) == -1 ) + + { + + ImReturnBinError( ); + + } + + OddCount += (BCLen + byteCount); + + } + + + + /* + + * If we have written an odd amount of data for the opcode, + + * then we have to pad an extra byte so that the next opcode + + * is word aligned + + */ + + if ( IModd( OddCount ) ) + + { + + g_buf[0] = '\0'; + + if ( ImBinWrite( ioType, fd, fp, g_buf, UCHAR, 1, 1)== -1) + + { + + ImReturnBinError( ); + + } + + } + + + + /* ShortComment { picBitEnd }; */ + + ushort1 = 0x00A0; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + ushort1 = IMPC_PICBITEND; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + + + /* ShortComment { picDwgEnd }; */ + + ushort1 = 0x00A0; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + ushort1 = IMPC_PICDWGEND; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: write the END opcode\n" ); + +#endif + + + + /* PO_opEndPic { }; */ + + ushort1 = 0x00FF; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + free( (char *)runBuffer ); + + + + return ( 1 ); + +} + + + + + +/* + + * + + * Write a PICT file containing a 16-bit PixMap + + * + + * Returns # of tags used + + * + + */ + +static int + +#ifdef __STDC__ + +imPictWritePB16( ImFileFormatWriteMap *ppMap, int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable ) + +#else + +imPictWritePB16( ppMap, ioType, fd, fp, flags, tagTable ) + + ImFileFormatWriteMap *ppMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable * flags; /* Format flags */ + + TagTable * tagTable; /* Tag list to read from */ + +#endif + +{ + + ImVfb * vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointer */ + + unsigned char * runBuffer; /* Run buffer */ + + unsigned char * rbp; /* Run buffer pointer */ + + ImClt * clt; /* A VFB ColorTable */ + + ImCltPtr ctPtr; /* A VFB ColorTable pointer */ + + + + /* PICT STUFF */ + + unsigned int byteCount; /* PICT bit-pixMap byteCount */ + + unsigned short rowBytes; /* PICT bit-pixMap rowBytes */ + + short pictSize; /* size (in bytes) of image data */ + + short OCLen; /* PICT opcode length */ + + short BCLen; /* PICT bit-pixMap byteCount length */ + + struct PMap pMap; /* PICT PixMap structure */ + + struct CTable cTab; /* PICT ColorLookupTable struct */ + + struct CTEntry ctEnt; /* PICT ColorLookupTable Entry struct*/ + + int nColors; + + int sLines; + + int clutBytes; + + int bytesPerSLine; + + int bytesPerBand; + + int sLinesPerBand; + + int sLine; + + int i = 0; + + int col; + + unsigned long OddCount; + + Pict2Header header; /* PICT 2 file header */ + + int nBands; + + + + /* GENERIC VARS */ + + struct Region rgn; + + unsigned short ushort1; + + char message[100]; /* ImInfo message */ + + + + + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + clt = ImVfbQClt( vfb ); + + + + + + /* + + * PICT files are usually generated on Macs, which have an MBF + + * byte order. There is no floating point in the file so we + + * won't bother setting the float format for BIN. + + */ + + BinByteOrder( BINMBF ); + + + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: write 512 byte header\n" ); + +#endif + + /* + + * Write in the 512 byte (blank) header. + + */ + + memset( (void *)g_buf, 0x00,512 ); + + if ( ImBinWrite( ioType, fd, fp, g_buf, UCHAR, 1, 512 )== -1) + + { + + ImReturnBinError( ); + + } + + + + + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: write pictSize\n" ); + +#endif + + /* + + * Write the 2 byte "pictSize" field. + + * Since PICT1 format PICTs can only be 32k the 2 bytes works fine, + + * but for PICT2 format PICTs this 2 bytes only represents the low + + * order word. As IM V, p92 states, "Many applications already support + + * PICT resources larger than 32k. ... This was made possible by having + + * QuickDraw ignore the size word and simply read the picture until the + + * end-of-picture opcode is reached." + + */ + + pictSize = 0x0000; + + if ( ImBinWrite( ioType, fd, fp, &pictSize, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: write g_pictFrame\n" ); + +#endif + + /* + + * Write the 8 byte "g_pictFrame" field. + + * The g_pictFrame describes the bounding rectangle of the image. + + * We can get this by looking at the size of our Virtual Frame Buffer. + + */ + + g_pictFrame.top = 0; + + g_pictFrame.left = 0; + + g_pictFrame.bottom = ImVfbQHeight( vfb ); + + g_pictFrame.right = ImVfbQWidth( vfb ); + + if ( ImBinWriteStruct( ioType, fd, fp, &g_pictFrame, RectFields)== -1) + + { + + ImReturnBinError( ); + + } + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: g_pictFrame.top = 0x%0x (%d)\n", g_pictFrame.top, g_pictFrame.top ); + +fprintf( stderr, "DEBUG: g_pictFrame.left = 0x%0x (%d)\n", g_pictFrame.left, g_pictFrame.left ); + +fprintf( stderr, "DEBUG: g_pictFrame.bottom = 0x%0x (%d)\n", g_pictFrame.bottom, g_pictFrame.bottom ); + +fprintf( stderr, "DEBUG: g_pictFrame.right = 0x%0x (%d)\n", g_pictFrame.right, g_pictFrame.right ); + +#endif + + + + + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: write version\n" ); + +#endif + + /* + + * Write the 2 bytes of pict version numbers. + + * Note that we always write version 2 PICT files. + + */ + + ushort1 = 0x0011; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + ushort1 = 0x02FF; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + ImInfo( "Version", "2" ); + + ImInfo( "Byte Order", "Most Significant Byte First" ); + + sprintf( message, "%d x %d", g_pictFrame.right, g_pictFrame.bottom ); + + ImInfo( "Resolution", message ); + + + + /* PICT2 has some extra "reserved junk" we have to fill */ + + header.headerOp = 0x0C00; + + header.version = ~0; + + header.reserved1 = 0; + + header.hRes = 0x00480000; + + header.vRes = 0x00480000; + + header.srcRect_top = 0; + + header.srcRect_left = 0; + + header.srcRect_bottom = g_pictFrame.bottom; + + header.srcRect_right = g_pictFrame.right; + + header.reserved2 = 0; + + if ( ImBinWriteStruct( ioType, fd, fp, &header, Pict2HeaderFields)== -1) + + { + + ImReturnBinError( ); + + } + + + + + + OCLen = 2; /* 2 BYTE OPCODES */ + + + + /* + + * Most PICT2 files usualy have several opcodes to get bitmaps started: + + */ + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: write the traditional start-em-off opcodes\n" ); + +#endif + + + + /* ShortComment { picDwgBeg }; */ + + ushort1 = 0x00A0; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + ushort1 = IMPC_PICDWGBEG; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + + + /* DefHilite {}; */ + + ushort1 = 0x001E; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + /* ClipRgn { 10, { 0, 0, g_pictFrame.right, g_pictFrame.bottom }, $"" }; */ + + ushort1 = 0x0001; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + rgn.rgnSize = 10; + + rgn.rgnBBox.top = 0; + + rgn.rgnBBox.left = 0; + + rgn.rgnBBox.bottom = g_pictFrame.bottom; + + rgn.rgnBBox.right = g_pictFrame.right; + + if ( ImBinWriteStruct( ioType, fd, fp, &rgn, RegionFields)== -1) + + { + + ImReturnBinError( ); + + } + + + + + + /* + + * Allocate space for a run buffer large enough for 1 unencoded + + * scanline. An encoded scanline should be smaller. + + */ + + ImMalloc( runBuffer, unsigned char *, 4 * g_pictFrame.right ); + + + + + + /* + + * Construct all of the "magic" numbers we need to write the image. + + */ + + + + /* How many colors do we have to work with in the CLUT ? */ + + nColors = ImCltQNColors( clt ); + + + + /* + + * Figure out how many PixMap bands we want to break the image into. + + * Note that for each band we have to write out a new copy of the CLUT, + + * so we will try to figure on writting the least amount of data, + + * with the additional constraint that bands are no larger than 32k. + + */ + + sLines = g_pictFrame.bottom; + + clutBytes = nColors * 8; + + bytesPerSLine = g_pictFrame.right + 2; + + bytesPerBand = 32000 - clutBytes; + + sLinesPerBand = bytesPerBand / bytesPerSLine; + + /* The last band may be small. */ + + nBands = (sLines / sLinesPerBand) + 1; + + + + /* Calculate the rowBytes and ByteCountSize for the pixMap */ + + rowBytes = ( ( ( 8 * ( g_pictFrame.right ) + 31 ) >> 5 ) << 2 ); + + /* if rowBytes > 250 then byteCount is a word, else it is a byte */ + + if ( rowBytes > 250 ) + + BCLen = 2; + + else + + BCLen = 1; + + /* rowBytes = 0x8000 | rowBytes; High-bit lit means "PixMap" */ + + + + + + /* ShortComment { picBitBeg }; */ + + ushort1 = 0x00A0; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + ushort1 = IMPC_PICBITBEG; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + /* Data counter to keep track of opcode alignment */ + + OddCount = 0; + + + + /* Write out the [Pack]PixMap g_Opcode itself */ + + if ( rowBytes >= 8 ) + + ushort1 = 0x0098; + + else + + ushort1 = 0x0090; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + /* Write rowBytes */ + + rowBytes |= 0x8000; /* High-bit lit means "PixMap" */ + + if ( ImBinWrite( ioType, fd, fp, &rowBytes, USHORT, 2, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + rowBytes &= 0x7FFF; /* Undo the High-bit so we can use rowBytes "normally" */ + + + + /* Write PixMap header */ + + pMap.bnd.top = 0; + + pMap.bnd.left = 0; + + pMap.bnd.bottom = g_pictFrame.bottom; + + pMap.bnd.right = g_pictFrame.right; + + pMap.version = 0; + + pMap.packType = 0; + + pMap.packSize = 0x00000000; + + pMap.hRes = 0x00480000; + + pMap.vRes = 0x00480000; + + pMap.pixelType = 0; + + pMap.pixelSize = 8; + + pMap.cmpCount = 1; + + pMap.cmpSize = 8; + + pMap.planeBytes = 0x00000000; + + pMap.pmTable = 0L; + + pMap.pmReserved = 0x00000000; + + if ( ImBinWriteStruct(ioType, fd, fp, &pMap, PMapFields)== -1) + + { + + ImReturnBinError(); + + } + + + + /* Write out the CLUT (Color Lookup Table) for each PixMap (yuk!) */ + + cTab.ctSeed = 0L; + + cTab.ctFlags = 0x0000; + + cTab.ctSize = nColors - 1; + + if ( ImBinWriteStruct( ioType, fd, fp, &cTab, CTableFields ) == -1 ) + + { + + ImReturnBinError(); + + } + + ctPtr = ImCltQFirst( clt ); + + for ( i=0; i= 8 ) + + { + + PackBits( runBuffer, (unsigned char*) g_buf, &byteCount ); + + rbp = (unsigned char*) g_buf; + + } + + else + + rbp = (unsigned char*) runBuffer; + + + + /* Write the packed scanline */ + + if ( ImBinWrite( ioType, fd, fp, &byteCount, UINT, + + BCLen, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + if ( ImBinWrite( ioType, fd, fp, rbp, UCHAR, + + 1, byteCount ) == -1 ) + + { + + ImReturnBinError( ); + + } + + OddCount += (BCLen + byteCount); + + } + + + + /* + + * If we have written an odd amount of data for the opcode, + + * then we have to pad an extra byte so that the next opcode + + * is word aligned + + */ + + if ( IModd( OddCount ) ) + + { + + g_buf[0] = '\0'; + + if ( ImBinWrite( ioType, fd, fp, g_buf, UCHAR, 1, 1)== -1) + + { + + ImReturnBinError( ); + + } + + } + + + + /* ShortComment { picBitEnd }; */ + + ushort1 = 0x00A0; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + ushort1 = IMPC_PICBITEND; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + + + /* ShortComment { picDwgEnd }; */ + + ushort1 = 0x00A0; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + ushort1 = IMPC_PICDWGEND; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + +#ifdef DEBUG + +fprintf( stderr, "DEBUG: write the END opcode\n" ); + +#endif + + + + /* PO_opEndPic { }; */ + + ushort1 = 0x00FF; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + free( (char *)runBuffer ); + + + + return ( 1 ); + +} + + + + + +/* + + * + + * Write a PICT file containing a 32-bit PixMap + + * + + * Returns # of tags used + + * + + */ + +static int + +#ifdef __STDC__ + +imPictWritePBRGB( ImFileFormatWriteMap *ppMap, int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable ) + +#else + +imPictWritePBRGB( ppMap, ioType, fd, fp, flags, tagTable ) + + ImFileFormatWriteMap *ppMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable * flags; /* Format flags */ + + TagTable * tagTable; /* Tag list to read from */ + +#endif + +{ + + ImVfb * vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointer */ + + unsigned char * runBuffer; /* Run buffer */ + + unsigned char * rbp; /* Run buffer pointer */ + + + + /* PICT STUFF */ + + unsigned int byteCount; /* PICT bit-pixMap byteCount */ + + unsigned short rowBytes; /* PICT bit-pixMap rowBytes */ + + short pictSize; /* size (bytes) of image data */ + + short OCLen; /* PICT opcode length */ + + short BCLen; /* PICT bit-pixMap byteCount length */ + + struct PMap pMap; /* PICT PixMap structure */ + + int sLines; + + int sLine; + + int i = 0; + + int col; + + unsigned long OddCount = 0; /* Keeps track of opcode alignment */ + + Pict2Header header; /* PICT 2 file header */ + + int depth; + + + + /* GENERIC VARS */ + + struct Region rgn; + + unsigned short ushort1; + + unsigned long ulong1; + + char message[100]; /* ImInfo message */ + + + + + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + + + + + /* + + * PICT files are usually generated on Macs, which have an MBF + + * byte order. There is no floating point in the file so we + + * won't bother setting the float format for BIN. + + */ + + BinByteOrder( BINMBF ); + + + +#ifdef DEBUG + + fprintf( stderr, "DEBUG: write 512 byte header\n" ); + +#endif + + /* + + * Write in the 512 byte (blank) header. + + */ + + memset( (void *)g_buf, 0x00, 512 ); + + if ( ImBinWrite( ioType, fd, fp, g_buf, UCHAR, 1, 512 )== -1) + + { + + ImReturnBinError( ); + + } + + + + + +#ifdef DEBUG + + fprintf( stderr, "DEBUG: write pictSize\n" ); + +#endif + + /* + + * Write the 2 byte "pictSize" field. + + * Since PICT1 format PICTs can only be 32k the 2 bytes works fine, + + * but for PICT2 format PICTs this 2 bytes only represents the low + + * order word. As IM V, p92 states, "Many applications already support + + * PICT resources larger than 32k. ... This was made possible by having + + * QuickDraw ignore the size word and simply read the picture until the + + * end-of-picture opcode is reached." + + */ + + pictSize = 0x0000; + + if ( ImBinWrite( ioType, fd, fp, &pictSize, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + + +#ifdef DEBUG + + fprintf( stderr, "DEBUG: write g_pictFrame\n" ); + +#endif + + /* + + * Write the 8 byte "g_pictFrame" field. + + * The g_pictFrame describes the bounding rectangle of the image. + + * We can get this by looking at the size of our Virtual Frame Buffer. + + */ + + g_pictFrame.top = 0; + + g_pictFrame.left = 0; + + g_pictFrame.bottom = ImVfbQHeight( vfb ); + + g_pictFrame.right = ImVfbQWidth( vfb ); + + if ( ImBinWriteStruct( ioType, fd, fp, &g_pictFrame, RectFields)== -1) + + { + + ImReturnBinError( ); + + } + +#ifdef DEBUG + + fprintf( stderr, "DEBUG: g_pictFrame.top = 0x%0x (%d)\n", g_pictFrame.top, g_pictFrame.top ); + + fprintf( stderr, "DEBUG: g_pictFrame.left = 0x%0x (%d)\n", g_pictFrame.left, g_pictFrame.left ); + + fprintf( stderr, "DEBUG: g_pictFrame.bottom = 0x%0x (%d)\n", g_pictFrame.bottom, g_pictFrame.bottom ); + + fprintf( stderr, "DEBUG: g_pictFrame.right = 0x%0x (%d)\n", g_pictFrame.right, g_pictFrame.right ); + +#endif + + + + + +#ifdef DEBUG + + fprintf( stderr, "DEBUG: write version\n" ); + +#endif + + /* + + * Write the 2 bytes of pict version numbers. + + * Note that we always write version 2 PICT files. + + */ + + ushort1 = 0x0011; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + ushort1 = 0x02FF; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + ImInfo( "Version", "2" ); + + ImInfo( "Byte Order", "Most Significant Byte First" ); + + sprintf( message, "%d x %d", g_pictFrame.right, g_pictFrame.bottom ); + + ImInfo( "Resolution", message ); + + + + /* PICT2 has some extra "reserved junk" we have to fill */ + + header.headerOp = 0x0C00; + + header.version = 0xfffe; /* = -2 Extended version 2 PICT */ + + header.reserved1 = 0; + + header.hRes = 0x00480000; + + header.vRes = 0x00480000; + + header.srcRect_top = 0; + + header.srcRect_left = 0; + + header.srcRect_bottom = g_pictFrame.bottom; + + header.srcRect_right = g_pictFrame.right; + + header.reserved2 = 0; + + if ( ImBinWriteStruct( ioType, fd, fp, &header, Pict2HeaderFields)== -1) + + { + + ImReturnBinError( ); + + } + + + + + + OCLen = 2; /* 2 BYTE OPCODES */ + + + + /* + + * Most PICT2 files usualy have several opcodes to get bitmaps started: + + */ + +#ifdef DEBUG + + fprintf( stderr, "DEBUG: write the traditional start opcodes\n" ); + +#endif + + + + /* ShortComment { picDwgBeg }; */ + + ushort1 = 0x00A0; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + ushort1 = IMPC_PICDWGBEG; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + + + /* DefHilite {}; */ + + ushort1 = 0x001E; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + /* ClipRgn { 10, { 0, 0, g_pictFrame.right, g_pictFrame.bottom } } */ + + ushort1 = 0x0001; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + rgn.rgnSize = 10; + + rgn.rgnBBox.top = 0; + + rgn.rgnBBox.left = 0; + + rgn.rgnBBox.bottom = g_pictFrame.bottom; + + rgn.rgnBBox.right = g_pictFrame.right; + + if ( ImBinWriteStruct( ioType, fd, fp, &rgn, RegionFields)== -1) + + { + + ImReturnBinError( ); + + } + + + + + + /* + + * Allocate space for a run buffer large enough for 1 unencoded + + * scanline. An encoded scanline should be smaller. + + */ + + ImMalloc( runBuffer, unsigned char *, 4 * g_pictFrame.right ); + + + + + + /* + + * Construct all of the "magic" numbers we need to write the image. + + */ + + + + sLines = g_pictFrame.bottom; + + + + /* + + * Calculate the rowBytes and ByteCountSize for the pixMap. + + * This formula multiplies the number of pixels across the PixMap by + + * the pixel depth to get the number of bits, and then this is divided + + * by eight to get the number of bytes. This division by eight looks + + * very strange because the number of bytes per row must be even, so + + * this formula takes advantage of integer division and multiplication + + * to make the result come out even. This particular formula + + * additionally makes sure that the number of bytes per row is a + + * multiple of four. This helps optimize the performance of Color + + * QuickDraw operations because it allows Color QuickDraw to refer + + * to each row beginning on a long word boundary in memory. + + */ + + + + /* OLD WAY OF CALCULATING ROWBYTES (ONLY WORD ALIGNED) */ + + /* rowBytes = 3 * g_pictFrame.right; */ + + /* if ( IModd( rowBytes ) ) rowBytes++; */ + + + + depth = 32; + + rowBytes = ((depth * (g_pictFrame.right - g_pictFrame.left) + 31) / 32) * 4; + + + + /* if rowBytes > 250 then byteCount is a word, else it is a byte */ + + if ( rowBytes > 250 ) + + BCLen = 2; + + else + + BCLen = 1; + + + + /* ShortComment { picBitBeg } */ + + ushort1 = 0x00A0; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + ushort1 = IMPC_PICBITBEG; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + + + /* Write out the DirectBitsRect opcode itself */ + + ushort1 = 0x009A; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + /* Write out the baseAddr flag */ + + ulong1 = 0x000000FF; /* Means direct pixels */ + + if ( ImBinWrite( ioType, fd, fp, &ulong1, ULONG, 4, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + + + /* Write rowBytes */ + + rowBytes |= 0x8000; /* High-bit lit means "PixMap" */ + + if ( ImBinWrite( ioType, fd, fp, &rowBytes, USHORT, 2, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + rowBytes &= 0x7FFF; /* Undo High-bit to use rowBytes "normally" */ + + + + + + /* Write PixMap header */ + + pMap.bnd.top = 0; + + pMap.bnd.left = 0; + + pMap.bnd.bottom = g_pictFrame.bottom; + + pMap.bnd.right = g_pictFrame.right; + + pMap.version = 0; + + pMap.packType = 4; /* RGB RLE encoding */ + + pMap.packSize = 0x00000000; + + pMap.hRes = 0x00480000; /* 72 dpi */ + + pMap.vRes = 0x00480000; /* 72 dpi */ + + pMap.pixelType = 16; /* For Direct Pixels */ + + pMap.pixelSize = 32; /* RGB data */ + + pMap.cmpCount = 3; /* Red, Green, Blue */ + + pMap.cmpSize = 8; + + pMap.planeBytes = 0x00000000; + + pMap.pmTable = 0L; + + pMap.pmReserved = 0x00000000; + + if ( ImBinWriteStruct(ioType, fd, fp, &pMap, PMapFields)== -1) + + { + + ImReturnBinError(); + + } + + + + /* Write the srcRect, dstRect, and mode */ + + if ( ImBinWriteStruct( ioType, fd, fp, &g_pictFrame, RectFields)== -1) + + { + + ImReturnBinError( ); + + } + + if ( ImBinWriteStruct( ioType, fd, fp, &g_pictFrame, RectFields)== -1) + + { + + ImReturnBinError( ); + + } + + ushort1 = 64; /* ditherCopy */ + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + + + /* Construct-Compress-Write the scanlines */ + + for ( sLine=0; sLine= 8 ) + + { + + PackBits( runBuffer, (unsigned char*)g_buf, &byteCount ); + + rbp = (unsigned char*) g_buf; + + } else + + { + + rbp = (unsigned char*) runBuffer; + + } + + + + /* Write the packed scanline */ + + if ( ImBinWrite( ioType, fd, fp, &byteCount, UINT, + + BCLen, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + if ( ImBinWrite( ioType, fd, fp, rbp, UCHAR, + + 1, byteCount ) == -1 ) + + { + + ImReturnBinError( ); + + } + + OddCount += (BCLen + byteCount); + + } + + + + /* + + * If we have written an odd amount of data for the opcode, + + * then we have to pad an extra byte so that the next opcode + + * is word aligned + + */ + + if ( IModd( OddCount ) ) + + { + + g_buf[0] = '\0'; + + if ( ImBinWrite( ioType, fd, fp, g_buf, UCHAR, 1, 1)== -1) + + { + + ImReturnBinError( ); + + } + + } + + + + /* ShortComment { picBitEnd }; */ + + ushort1 = 0x00A0; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + ushort1 = IMPC_PICBITEND; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + + + /* ShortComment { picDwgEnd }; */ + + ushort1 = 0x00A0; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + ushort1 = IMPC_PICDWGEND; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, 2, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + +#ifdef DEBUG + + fprintf( stderr, "DEBUG: write the END opcode\n" ); + +#endif + + + + /* PO_opEndPic { }; */ + + ushort1 = 0x00FF; + + if ( ImBinWrite( ioType, fd, fp, &ushort1, USHORT, OCLen, 1 )== -1) + + { + + ImReturnBinError( ); + + } + + + + free( (char *)runBuffer ); + + + + return ( 1 ); + +} + + + diff --git a/utils/roq2/libim/impix.c b/utils/roq2/libim/impix.c new file mode 100644 index 0000000..07ae5e7 --- /dev/null +++ b/utils/roq2/libim/impix.c @@ -0,0 +1,1704 @@ +/** + + ** $Header: /roq/libim/impix.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/impix.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** impix.c - Alias PIX file I/O + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** impix.c contains routines to read and write Alias PIX files for + + ** the image manipulation library. Raster data read in is stored + + ** in a VFB. Raster data written out is taken from a tag table. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** none + + ** + + ** PRIVATE CONTENTS + + ** imPixRead f read an Alias PIX file + + ** imPixWrite f write an Alias PIX file + + ** + + ** imPixHeaderInfo t PIX file header information + + ** imPixHeaderFields v imPixHeaderInfo description for Bin pkg + + ** + + ** HISTORY + + ** $Log: /roq/libim/impix.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.26 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.25 1995/04/03 21:34:19 bduggan + + ** took out #ifdef NEWMAGIC + + ** + + ** Revision 1.24 1995/02/16 21:41:24 bduggan + + ** Added support for Matte (index8) files + + ** + + ** Revision 1.23 1995/01/16 22:53:39 bduggan + + ** fixed spacing in code + + ** + + ** Revision 1.22 1995/01/10 23:39:57 bduggan + + ** put in IMMULTI, IMPIPE instead of TRUE/FALSE + + ** made read/write routines static + + ** + + ** Revision 1.21 94/10/03 11:30:43 nadeau + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Rearranged magic number structures for format handlers. + + ** Made format handler routines static (i.e., local to file). + + ** Updated comments, adding format descriptions and references. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.20 93/09/22 11:41:23 nadeau + + ** Corrected spelling error in message. + + ** + + ** Revision 1.19 92/12/03 01:51:14 nadeau + + ** Corrected info messages. + + ** ., + + ** + + ** Revision 1.18 92/11/23 18:43:02 nadeau + + ** Removed use of IMINFOMSG. + + ** + + ** Revision 1.17 92/11/04 12:05:30 groening + + ** put ImFIleFormat info and magic number info + + ** from imfmt.c into this file. + + ** + + ** Revision 1.16 92/10/16 11:41:07 groening + + ** added ImInfo statements + + ** + + ** Revision 1.15 92/08/31 17:32:34 vle + + ** Updated copyright notice. + + ** + + ** Revision 1.14 91/10/04 10:52:01 nadeau + + ** Fixed VAX bit shift problem. + + ** + + ** Revision 1.13 91/10/03 09:15:50 nadeau + + ** Fixed #includes. + + ** + + ** Revision 1.12 91/03/08 14:31:04 nadeau + + ** Ooops! Forget to get the VFB before trying to write it out! + + ** Major core dump time. + + ** + + ** Revision 1.11 91/02/12 10:52:58 nadeau + + ** Removed the tag table checking and VFB conversion now + + ** handled by ImFileRead and ImFileWrite. + + ** + + ** Revision 1.10 90/12/03 12:47:39 nadeau + + ** Fixed read bug that would cause memory to be overwritten if + + ** the number of bytes in the file was not a multiple of 4 (ie, + + ** there were extra bytes at the end). + + ** + + ** Revision 1.9 90/07/02 13:21:02 nadeau + + ** Updated to the new error handling mechanism. + + ** + + ** Revision 1.8 90/06/25 14:40:57 nadeau + + ** Changed ImTag* to Tag* (new names). + + ** + + ** Revision 1.7 90/06/25 13:20:54 todd + + ** Fixed a few little bugs. + + ** + + ** Revision 1.6 90/05/16 07:46:32 todd + + ** Move some macros into new include file iminternal.h for shared use + + ** + + ** Revision 1.5 90/05/11 09:55:19 nadeau + + ** Updated call interface to handle file descriptors and file pointers. + + ** Updated code to watch for both of these and make the correct bin pkg + + ** call. + + ** + + ** Revision 1.4 90/04/05 17:07:21 todd + + ** Take out code that checks if input is a pipe. BinRead as much as + + ** possible. numbytes_read/4 is the number of runs. + + ** Change pixel arrays from ints to arrays of unsigned chars for portability. + + ** + + ** Revision 1.3 90/03/29 08:29:28 todd + + ** Changed some ints to unsigned ints. PIX Read and Write routines seem + + ** to work fine. + + ** + + ** Revision 1.2 90/03/28 11:13:53 nadeau + + ** Lots of fiddling and bug fixing. Still doesn't work, but Todd + + ** has graciously volunteered to work on it now! + + ** + + ** Revision 1.1 90/03/06 17:32:24 nadeau + + ** Initial revision + + ** + + **/ + + + + + +#include "iminternal.h" + + + +/* + + ** + + ** + + ** FORMAT + + ** + + ** PIX - Alias Pixel file + + ** + + ** AKA + + ** .alias + + ** + + ** FORMAT REFERENCES + + ** Alias Reference Manual, Alias Research, Inc. + + ** + + ** CODE CREDITS + + ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1990. + + ** Custom development, Todd Elvins, San Diego Supercomputer Center, 1990. + + ** + + ** DESCRIPTION + + ** Alias' pix file has a 10-byte header followed by run-length-encoded + + ** image data. + + ** + + ** Header + + ** ====== + + ** + + ** The header for a pix file has the following information: + + ** + + ** bytes: description: + + ** ------ ------------- + + ** 0,1 width (x resolution in pixels) + + ** 2,3 height (y resolution in pixels) + + ** 4,5 xoffset (unused) + + ** 6,7 yoffset (unused) + + ** 8,9 bits per pixel: + + ** 0x18 for pix files + + ** 0x08 for matte files (greyscale) + + ** + + ** Encoding of Pixel Data + + ** ====================== + + ** + + ** The pixels are encoded in 4-byte packages, starting with the top + + ** scanline. Runs do not extend beyond the end of a scanline. The + + ** packages have the following format: + + ** + + ** byte: data range: description + + ** ----- ----------- ----------- + + ** 1 1-255 runlength (number of pixels in + + ** succession with given RGB) + + ** 2 0-255 blue (value of blue component) + + ** 3 0-255 green (green component) + + ** 4 0-255 red (red component) + + ** + + ** Example + + ** ======= + + ** Here is the hex of a file that is 8 pixels wide and 6 pixels high, + + ** representing a ramp that goes from black at the bottom of the image to + + ** blue at the top: + + ** + + ** 0008 0006 0000 0005 0018 08ff 0000 08cc + + ** 0000 0899 0000 0866 0000 0833 0000 0000 + + ** 0000 + + ** + + ** The header is 0008 0006 0000 0005 0018 + + ** The first line is a run of eight pixels of B=0xff (255), G=0, R=0, + + ** The second line is a run of eight pixels of B=0xcc (204), G=0, R=0 + + ** etc... + + ** + + ** + + ** + + **/ + + + +#ifdef __STDC__ + +static int imPixRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imPixWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imPixReadIndex8( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, int x, int y ); + +static int imPixWriteIndex8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable + +); + +#else + +static int imPixRead( ); + +static int imPixWrite( ); + +static int imPixReadIndex8( ); + +static int imPixWriteIndex8( ); + +#endif + + + +static char *imPixNames[ ] = { "pix", "alias", NULL }; + +static ImFileFormatReadMap imPixReadMap[ ] = + +{ + + /* in out */ + + /* type,ch,dep, attr. VFB type attr. */ + + { IMVFBRGB,3,8, RLE, IMVFBRGB, 0 }, + + { IMVFBINDEX8,1,8, RLE, IMVFBRGB, 0 }, + + { -1, 0, -1, 0 }, + +}; + +static ImFileFormatWriteMap imPixWriteMap[ ] = + +{ + + /* in out */ + + /* VFB type, attr., type,ch,dep, attr., func */ + + { IMVFBRGB, 0, RGB,3,8, RLE, imPixWrite }, + + { IMVFBINDEX8, 0, IN,1,8, RLE, imPixWriteIndex8 }, + + { -1, 0, -1, 0, NULL }, + +}; + + + +static ImFileMagic imFilePixMagic []= + +{ + + { 0, 0, NULL}, + +}; + + + +ImFileFormat ImFilePixFormat = + +{ + + imPixNames, "Alias image file", + + "Alias Research, Inc.", + + "24-bit RGB RLE-compressed images, 8-bit greyscale RLE_compressed images", + + "24-bit RGB RLE-compressed images, 8-bit greyscale RLE_compressed images", + + imFilePixMagic, + + IMNOMULTI, IMPIPE, + + IMNOMULTI, IMPIPE, + + imPixRead, imPixReadMap, imPixWriteMap + +}; + + + + + + + +/* + + * TYPEDEF & STRUCTURE + + * imPixHeaderInfo - PIX file header information + + * imPixHeaderFields - PIX file header fields for binary pkg. + + * + + * DESCRIPTION + + * A PIX file's header contains the image's width, height, and depth, + + * and two unused dummy fields. + + */ + + + +typedef struct imPixHeaderInfo + +{ + + short pix_width; /* Image width */ + + short pix_height; /* Image height */ + + short pix_dummy1; /* Unused dummy field */ + + short pix_dummy2; /* Unused dummy field */ + + short pix_depth; /* Image depth */ + +} imPixHeaderInfo; + + + +static BinField imPixHeaderFields[ ] = + +{ + + { SHORT, 2, 1 }, /* pix_width */ + + { SHORT, 2, 1 }, /* pix_height */ + + { SHORT, 2, 1 }, /* pix_dummy1 */ + + { SHORT, 2, 1 }, /* pix_dummy1 */ + + { SHORT, 2, 1 }, /* pix_depth */ + + { 0, 0, 0 } + +}; + + + + + + + + + + + +/* + + * FUNCTION + + * imPixRead - read an Alias PIX file + + * + + * DESCRIPTION + + * The file header is read and the size of the image determined. + + * Space is allocated for the VFB and the run codes read in to + + * a run buffer. The run buffer is then expanded into straight + + * RGB values in the VFB and the completed VFB added to the tag list. + + */ + + + +static int /* Returns # tags read in */ + +#ifdef __STDC__ + +imPixRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imPixRead( ioType, fd, fp, flagsTable, tagTable ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointer */ + + imPixHeaderInfo header; /* PIX file header */ + + unsigned char *runBuffer; /* Run buffer */ + + unsigned char *rbp; /* Run buffer pointer */ + + unsigned char r, g, b; /* Red, green, and blue */ + + long seekPtr; /* File seek pointer */ + + int nRead; /* Number of pixel runs */ + + int x,y; /* Convenient short names */ + + unsigned int count; /* Run length */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * PIX files are usually generated on Irises, which have an MBF + + * byte order. There is no floating point in the file so we + + * won't bother setting the float format for BIN. + + */ + + BinByteOrder( BINMBF ); + + + + ImInfo ("Byte Order","Most Significant Byte First"); + + /* + + * Read in the header. + + */ + + if ( ImBinReadStruct( ioType, fd, fp, &header, imPixHeaderFields )== -1) + + { + + ImReturnBinError( ); + + } + + x = header.pix_width; + + y = header.pix_height; + + + + if (header.pix_depth==0x08) + + { /* Greyscale image */ + + return imPixReadIndex8(ioType, fd, fp, flagsTable, tagTable, x, y); + + } + + + + sprintf (message, "%d x %d", x,y); + + ImInfo ("Resolution", message); + + ImInfo ( "Type", "24-bit RGB"); + + ImInfo ( "Compression Type", "Run Length Encoded (RLE)"); + + + + + + /* + + * Allocate a VFB of the required size. + + */ + + if ( (vfb = ImVfbAlloc( x, y, IMVFBRGB )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + + + /* + + * Allocate a run buffer large enough for the worst case run + + * length encoding. + + */ + + ImMalloc( runBuffer, unsigned char *, 4 * x * y ); + + + + + + /* + + * Read the entire image into the run buffer. The number of bytes + + * read divided by 4 is the number of runs. + + */ + + if ( (nRead = ImBinRead( ioType, fd, fp, runBuffer, UCHAR, 1, 4*x*y )) == -1 ) + + { + + free( (char *)runBuffer ); + + ImReturnBinError( ); + + } + + + + /* + + * Expand the runs to create a raw RGB image in the VFB. + + */ + + pptr = ImVfbQFirst( vfb ); + + for ( rbp = runBuffer; nRead > 3; nRead-=4, rbp+=4 ) + + { + + r = *(rbp+3); + + g = *(rbp+2); + + b = *(rbp+1); + + for ( count = *rbp; count > 0; count-- ) + + { + + ImVfbSRed( vfb, pptr, r ); + + ImVfbSGreen( vfb, pptr, g ); + + ImVfbSBlue( vfb, pptr, b ); + + ImVfbSInc( vfb, pptr ); + + } + + } + + free( (char *)runBuffer ); + + + + + + /* + + * Add the VFB to the tagTable. + + */ + + TagTableAppend( tagTable, TagEntryAlloc( "image vfb", POINTER, &vfb ) ); + + + + return ( 1 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imPixWrite - write an Alias PIX file + + * + + * DESCRIPTION + + * The PIX file header set up and written out. The VFB data is then + + * read, converted to run-codes, and written out. + + */ + + + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imPixWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imPixWrite( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointer */ + + imPixHeaderInfo header; /* PIX file header */ + + unsigned char *runBuffer; /* Run buffer */ + + unsigned char *rbp; /* Run buffer pointer */ + + unsigned int count; /* Run length */ + + int nRuns; /* Number of pixel runs */ + + int n; /* Counter */ + + int x, y; /* Pixel counters */ + + int rgb; /* New rgb value */ + + int oldrgb; /* Old rgb value */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * PIX files are usually generated on Irises, which have an MBF + + * byte order. There is no floating point in the file so we + + * won't bother setting the float format for BIN. + + */ + + BinByteOrder( BINMBF ); + + + + + + /* + + * Set up the header and write it out. + + */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + header.pix_width = ImVfbQWidth( vfb ); + + header.pix_height = ImVfbQHeight( vfb ); + + header.pix_dummy1 = 0; + + header.pix_dummy2 = header.pix_height - 1; + + header.pix_depth = 24; + + + + if ( ImBinWriteStruct( ioType, fd, fp, &header, imPixHeaderFields )==-1) + + { + + ImReturnBinError( ); + + } + + + + sprintf (message, "%d x %d", header.pix_width, header.pix_height); + + ImInfo ("Resolution", message); + + ImInfo ( "Type", "24-bit RGB"); + + ImInfo ( "Compression Type", "Run Length Encoded (RLE)"); + + + + + + /* + + * Allocate space for a run buffer large enough for 1 unencoded + + * scanline. An encoded scanline will be smaller. + + */ + + if ( (runBuffer = (unsigned char *)malloc( 4 * header.pix_width )) == NULL ) + + { + + ImErrNo = IMEMALLOC; + + ImErrorFatal( ImQError( ), -1, IMEMALLOC ); + + } + + + + + + /* + + * Run-length encode the VFB. In PIX files, run's always stop + + * at the end of a scanline, even if the start of the next scanline + + * is the same color. + + */ + + pptr = ImVfbQFirst( vfb ); + + for ( y = 0; y < header.pix_height; y++ ) + + { + + rbp = runBuffer; + + nRuns = 0; + + oldrgb = (((unsigned int)ImVfbQBlue( vfb, pptr )) << 16) | + + (((unsigned int)ImVfbQGreen( vfb, pptr )) << 8) | + + (unsigned int)ImVfbQRed( vfb, pptr ); + + ImVfbSInc( vfb, pptr ); + + count = 1; + + for ( x = 1; x < header.pix_width; x++ ) + + { + + rgb = (((unsigned int)ImVfbQBlue( vfb, pptr )) << 16) | + + (((unsigned int)ImVfbQGreen( vfb, pptr )) << 8) | + + (unsigned int)ImVfbQRed( vfb, pptr ); + + ImVfbSInc( vfb, pptr ); + + + + if ( rgb == oldrgb ) + + { + + /* Add 1 to the run. */ + + if ( ++count == 255 ) + + { + + /* Overflow 1 byte. Dump run. */ + + *rbp++ = 255; + + *rbp++ = (oldrgb >> 16) & 0xFF; + + *rbp++ = (oldrgb >> 8) & 0xFF; + + *rbp++ = (oldrgb) & 0xFF; + + nRuns++; + + count = 0; + + } + + } + + else + + { + + /* Different color. Dump run. */ + + if ( count != 0 ) + + { + + *rbp++ = count; + + *rbp++ = (oldrgb >> 16) & 0xFF; + + *rbp++ = (oldrgb >> 8) & 0xFF; + + *rbp++ = (oldrgb) & 0xFF; + + nRuns++; + + } + + count = 1; + + oldrgb = rgb; + + } + + } + + if ( count != 0 ) + + { + + *rbp++ = count; + + *rbp++ = (oldrgb >> 16) & 0xFF; + + *rbp++ = (oldrgb >> 8) & 0xFF; + + *rbp++ = (oldrgb) & 0xFF; + + nRuns++; + + } + + + + /* + + * Write out the run buffer. + + */ + + if ( ImBinWrite( ioType, fd, fp, runBuffer, UCHAR, 1, nRuns * 4 ) == -1 ) + + { + + free( (char *)runBuffer ); + + ImReturnBinError( ); + + } + + } + + + + + + /* + + * Add a terminating run with a count of 0 and write it out. + + */ + + *runBuffer = 0; /* Terminate the runs */ + + if ( ImBinWrite( ioType, fd, fp, runBuffer, UCHAR, 1, 4 ) == -1 ) + + { + + free( (char *)runBuffer ); + + ImReturnBinError( ); + + } + + + + free( (char *)runBuffer ); + + + + return ( 1 ); + +} + + + + + + + +/* + + * FUNCTION + + * imPixReadIndex8 - read an Alias PIX file + + * + + * DESCRIPTION + + * The file header has been read and the size of the image has + + * been determined by imPixRead. + + * Here, space is allocated for the VFB and the run codes read in to + + * a run buffer. The run buffer is then expanded into straight + + * greyscale values in the VFB and the completed VFB added to the tag list. + + */ + + + +static int /* Returns # tags read in */ + +#ifdef __STDC__ + +imPixReadIndex8( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, int x, int y ) + +#else + +imPixReadIndex8( ioType, fd, fp, flagsTable, tagTable, x, y ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + + int x; /* Dimensions of the image */ + + int y; + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointer */ + + unsigned char *runBuffer; /* Run buffer */ + + unsigned char *rbp; /* Run buffer pointer */ + + unsigned char g; /* Gray value */ + + long seekPtr; /* File seek pointer */ + + int nRead; /* Number of bytes read */ + + unsigned int count; /* Run length */ + + char message[100];/* ImInfo message */ + + + + + + /* The byte order has been set already by imReadPix */ + + + + sprintf (message, "%d x %d", x,y); + + ImInfo ("Resolution", message); + + ImInfo ( "Type", "8-bit Grayscale"); + + ImInfo ( "Compression Type", "Run Length Encoded (RLE)"); + + + + + + /* + + * Allocate a VFB of the required size. + + */ + + + + if ( (vfb = ImVfbAlloc( x, y, IMVFBINDEX8 )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + + + /* + + * Allocate a run buffer large enough for the worst case run + + * length encoding. + + */ + + + + ImMalloc( runBuffer, unsigned char *, 2 * x * y ); + + + + + + /* + + * Read the entire image into the run buffer. The number of bytes + + * read divided by 2 is the number of runs. + + */ + + + + if ( (nRead = ImBinRead( ioType, fd, fp, runBuffer, UCHAR, 1, 2*x*y )) == -1 ) + + { + + free( (char *)runBuffer ); + + ImReturnBinError( ); + + } + + + + /* + + * Expand the runs to create a grayscale image in the VFB. + + */ + + + + pptr = ImVfbQFirst( vfb ); + + for ( rbp = runBuffer; nRead > 1; nRead-=2, rbp+=2 ) + + { + + g = *(rbp+1); + + for ( count = *rbp; count > 0; count-- ) + + { + + ImVfbSIndex8(vfb, pptr, g ); + + ImVfbSInc( vfb, pptr ); + + } + + } + + free( (char *)runBuffer ); + + + + + + /* + + * Add the VFB to the tagTable. + + */ + + TagTableAppend( tagTable, TagEntryAlloc( "image vfb", POINTER, &vfb ) ); + + + + return ( 1 ); + +} + + + + + + + + + +/* + + * FUNCTION + + * imPixWriteIndex8 - write an Alias PIX file + + * + + * DESCRIPTION + + * The Matte file header set up and written out. The VFB data is then + + * read, converted to run-codes, and written out. + + */ + + + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imPixWriteIndex8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imPixWriteIndex8( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointer */ + + imPixHeaderInfo header; /* PIX file header */ + + unsigned char *runBuffer; /* Run buffer */ + + unsigned char *rbp; /* Run buffer pointer */ + + unsigned int count; /* Run length */ + + int nRuns; /* Number of pixel runs */ + + int n; /* Counter */ + + int x, y; /* Pixel counters */ + + int grey; /* New greyscale value */ + + unsigned char oldgrey; /* Old greyscale value */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * PIX files are usually generated on Irises, which have an MBF + + * byte order. There is no floating point in the file so we + + * won't bother setting the float format for BIN. + + */ + + BinByteOrder( BINMBF ); + + + + + + /* + + * Set up the header and write it out. + + */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + header.pix_width = ImVfbQWidth( vfb ); + + header.pix_height = ImVfbQHeight( vfb ); + + header.pix_dummy1 = 0; + + header.pix_dummy2 = header.pix_height - 1; + + header.pix_depth = 8; + + + + if ( ImBinWriteStruct( ioType, fd, fp, &header, imPixHeaderFields )==-1) + + { + + ImReturnBinError( ); + + } + + + + sprintf (message, "%d x %d", header.pix_width, header.pix_height); + + ImInfo ("Resolution", message); + + ImInfo ( "Type", "8-bit Grayscale"); + + ImInfo ( "Compression Type", "Run Length Encoded (RLE)"); + + + + + + /* + + * Allocate space for a run buffer large enough for 1 unencoded + + * scanline. An encoded scanline will be smaller. + + */ + + ImMalloc( runBuffer, unsigned char *, 2 * header.pix_width); + + + + + + /* + + * Run-length encode the VFB. In Matte files, runs always stop + + * at the end of a scanline, even if the start of the next scanline + + * is the same color. + + */ + + pptr = ImVfbQFirst( vfb ); + + for ( y = 0; y < header.pix_height; y++ ) + + { + + rbp = runBuffer; + + nRuns = 0; + + oldgrey = ImVfbQIndex8( vfb, pptr); + + ImVfbSInc( vfb, pptr ); + + count = 1; + + for ( x = 1; x < header.pix_width; x++ ) + + { + + grey = ImVfbQIndex8( vfb, pptr ); + + ImVfbSInc( vfb, pptr ); + + + + if ( grey == oldgrey ) + + { + + /* Add 1 to the run. */ + + if ( ++count == 255 ) + + { + + /* Overflow 1 byte. Dump run. */ + + *rbp++ = 255; + + *rbp++ = oldgrey; + + nRuns++; + + count = 0; + + } + + } + + else + + { + + /* Different color. Dump run. */ + + if ( count != 0 ) + + { + + *rbp++ = count; + + *rbp++ = oldgrey; + + nRuns++; + + } + + count = 1; + + oldgrey = grey; + + } + + } + + if ( count != 0 ) + + { + + *rbp++ = count; + + *rbp++ = oldgrey; + + nRuns++; + + } + + + + /* + + * Write out the run buffer. + + */ + + if ( ImBinWrite( ioType, fd, fp, runBuffer, UCHAR, 1, nRuns * 2 ) == -1 ) + + { + + free( (char *)runBuffer ); + + ImReturnBinError( ); + + } + + } + + + + + + /* + + * Add a terminating run with a count of 0 and write it out. + + */ + + *runBuffer = 0; /* Terminate the runs */ + + if ( ImBinWrite( ioType, fd, fp, runBuffer, UCHAR, 1, 2 ) == -1 ) + + { + + free( (char *)runBuffer ); + + ImReturnBinError( ); + + } + + + + free( (char *)runBuffer ); + + + + return ( 1 ); + +} + diff --git a/utils/roq2/libim/impref.h b/utils/roq2/libim/impref.h new file mode 100644 index 0000000..372e036 --- /dev/null +++ b/utils/roq2/libim/impref.h @@ -0,0 +1,998 @@ +/** + + ** $Header: /sdsc/dev/vis/image/imtools/v3.0/libim/src/include/RCS/impref.h,v 1.4 1995/06/29 00:32:03 bduggan Exp $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +/** + + ** FILE + + ** impref.h - Automatic format conversion preferences + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** impref.h contains initializations for several tables that indicate + + ** what format conversions are prefered over what other conversions. + + ** These tables are used by ImFileWrite() to decide which of several + + ** possible file format variants to choose given a VFB. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** __IMPREFH__ d file inclusion flag + + ** + + ** imFilePref t format preference table entry + + ** imFilePrefTable t format preference table + + ** + + ** imPrefMono v monochrome preference table + + ** imPrefIndex8 v 8-bit index preference table + + ** imPrefIndex16 v 16-bit index preference table + + ** imPrefRgb v RGB preference table + + ** + + ** PRIVATE CONTENTS + + ** none + + ** + + ** HISTORY + + ** $Log: impref.h,v $ + + ** Revision 1.4 1995/06/29 00:32:03 bduggan + + ** updated copyright + + ** + + ** Revision 1.3 1994/10/03 16:04:05 nadeau + + ** Updated to ANSI C and C++ compatibility. + + ** Updated copyright message. + + ** + + ** Revision 1.2 92/09/02 13:19:39 vle + + ** Updated copyright notice. + + ** + + ** Revision 1.1 91/10/03 12:57:51 nadeau + + ** Initial revision + + ** + + **/ + + + + + +#ifndef __IMPREFH__ + +#define __IMPREFH__ + + + + + + + + + + + +/* + + * TYPEDEF & STRUCT + + * imFilePref - format preference table entry + + * imFilePrefTable - format preference table + + * + + * GLOBALS + + * imPrefMono - monochrome preference table + + * imPrefIndex8 - 8-bit index preference table + + * imPrefIndex16 - 16-bit index preference table + + * imPrefRgb - RGB preference table + + * + + * DESCRIPTION + + * These tables are used by ImFileWrite to decide which of several + + * output variants supported by a file format is the best one to + + * choose given the attributes of an incomming VFB. + + * + + * Each table contains four sub-tables, one for each combination of + + * having a CLT and having alpha planes. Within each sub-table, + + * output format attributes (depth, CLT status, and alpha status) are + + * ordered from most preferable to least preferable. + + * + + * Preferences have determined by a human (ie., me) based upon the + + * following general guidelines: + + * + + * 1. Would rather promote to a "higher" image type than lose CLT. + + * 2. Would rather promote to a "higher" image type than lose Alpha. + + * 3. Would rather generate a bogus CLT than promote. + + * 4. If original is index8, promoting to index16 is better than RGB. + + * 5. All things being equal, promotion is in the path: + + * mono -> index8 -> rgb -> index16 + + * index16 is generally worse than rgb because of increased storage. + + * 6. Only demote to "lower" image type when all else fails. + + * 7. All things being equal, demotion is in the path: + + * rgb -> index16 -> index8 -> mono + + * 8. In a choice between loosing CLT or alpha, will lose alpha. + + * 9. Would rather lose CLT than demote. + + * 10. Would rather lose alpha than demote. + + * + + * When looking at a file format's supported depth variations (1-bit, + + * 8-bit, 16-bit, RGB, whatever), depths that fall between the cracks + + * of those represented in VFB's are treated as the next higher VFB + + * type in the order: + + * mono -> index8 -> index16 -> rgb + + * + + * EXAMPLE + + * A given format "Xyz" supports the following: + + * 1-bit mono, no CLT, no alpha + + * 8-bit index, no CLT, no alpha + + * 8-bit index, CLT, no alpha + + * 24-bit RGB, no CLT, no alpha + + * 24-bit RGB, no CLT, alpha + + * + + * We are given an 8-bit VFB, with CLT and no alpha planes. To a + + * human, a glance is enough to say "Yup, use the 3rd variant for Xyz". + + * To ImFileWrite the following is done: + + * + + * 1. Determine incomming VFB type. + + * 2. Get 'imIndex8' preferences table. + + * 3. Find 'Xyz' in format table. + + * 4. Get 'Xyz's write map. + + * 5. For each variant in the write map, look it up in the + + * 'imIndex8' preference table. Record the index of the + + * table entry that matches. + + * 6. Use the entry with the lowest index (ie, most prefered). + + */ + + + +typedef struct imFilePref + +{ + + int pref_field; /* VFB field mask */ + + int pref_attributes; /* Attribute mask */ + +} imFilePref; + + + +typedef struct imFilePrefTable + +{ + + imFilePref pref_noCltNoAlpha[16];/* No CLT, no Alpha plane */ + + imFilePref pref_cltNoAlpha[16]; /* Has CLT, no Alpha plane */ + + imFilePref pref_noCltAlpha[16]; /* No CLT, has Alpha plane */ + + imFilePref pref_cltAlpha[16]; /* Has CLT, has Alpha plane */ + +} imFilePrefTable; + + + + + +static imFilePrefTable imPrefMono = + +{ + + /* Mono VFB, no CLT, no Alpha planes */ + + { + + { IMVFBMONO, IMCLTNO | IMALPHANO, }, + + { IMVFBMONO, IMCLTYES | IMALPHANO, }, + + { IMVFBMONO, IMCLTNO | IMALPHAYES, }, + + { IMVFBMONO, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHAYES, }, + + { IMVFBRGB, IMCLTNO | IMALPHANO, }, + + { IMVFBRGB, IMCLTNO | IMALPHAYES, }, + + { IMVFBRGB, IMCLTYES | IMALPHANO, }, + + { IMVFBRGB, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHAYES, }, + + }, + + + + /* Mono VFB, CLT, no Alpha planes */ + + { + + { IMVFBMONO, IMCLTYES | IMALPHANO, }, + + { IMVFBMONO, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHAYES, }, + + { IMVFBRGB, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHANO, }, + + { IMVFBRGB, IMCLTYES | IMALPHANO, }, + + { IMVFBRGB, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHAYES, }, + + { IMVFBRGB, IMCLTYES | IMALPHAYES, }, + + { IMVFBMONO, IMCLTNO | IMALPHANO, }, + + { IMVFBMONO, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHAYES, }, + + }, + + + + /* Mono VFB, no CLT, Alpha planes */ + + { + + { IMVFBMONO, IMCLTNO | IMALPHAYES, }, + + { IMVFBMONO, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHAYES, }, + + { IMVFBRGB, IMCLTNO | IMALPHAYES, }, + + { IMVFBRGB, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHAYES, }, + + { IMVFBMONO, IMCLTNO | IMALPHANO, }, + + { IMVFBMONO, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHANO, }, + + { IMVFBRGB, IMCLTNO | IMALPHANO, }, + + { IMVFBRGB, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHANO, }, + + }, + + + + /* Mono VFB, CLT, Alpha planes */ + + { + + { IMVFBMONO, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHAYES, }, + + { IMVFBRGB, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHAYES, }, + + { IMVFBRGB, IMCLTYES | IMALPHAYES, }, + + { IMVFBMONO, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHAYES, }, + + { IMVFBMONO, IMCLTYES | IMALPHANO, }, + + { IMVFBMONO, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHANO, }, + + { IMVFBRGB, IMCLTNO | IMALPHANO, }, + + { IMVFBRGB, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHANO, }, + + }, + +}; + + + + + +static imFilePrefTable imPrefIndex8 = + +{ + + /* Index8 VFB, no CLT, no Alpha planes */ + + { + + { IMVFBINDEX8, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHAYES, }, + + { IMVFBRGB, IMCLTNO | IMALPHANO, }, + + { IMVFBRGB, IMCLTYES | IMALPHANO, }, + + { IMVFBRGB, IMCLTNO | IMALPHAYES, }, + + { IMVFBRGB, IMCLTYES | IMALPHAYES, }, + + { IMVFBMONO, IMCLTNO | IMALPHANO, }, + + { IMVFBMONO, IMCLTYES | IMALPHANO, }, + + { IMVFBMONO, IMCLTNO | IMALPHAYES, }, + + { IMVFBMONO, IMCLTYES | IMALPHAYES, }, + + }, + + + + /* Index8 VFB, CLT, no Alpha planes */ + + { + + { IMVFBINDEX8, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHANO, }, + + { IMVFBRGB, IMCLTNO | IMALPHANO, }, + + { IMVFBRGB, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHAYES, }, + + { IMVFBRGB, IMCLTNO | IMALPHAYES, }, + + { IMVFBRGB, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHAYES, }, + + { IMVFBMONO, IMCLTYES | IMALPHANO, }, + + { IMVFBMONO, IMCLTYES | IMALPHAYES, }, + + { IMVFBMONO, IMCLTNO | IMALPHANO, }, + + { IMVFBMONO, IMCLTNO | IMALPHAYES, }, + + }, + + + + /* Index8 VFB, no CLT, Alpha planes */ + + { + + { IMVFBINDEX8, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHAYES, }, + + { IMVFBRGB, IMCLTNO | IMALPHAYES, }, + + { IMVFBRGB, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHANO, }, + + { IMVFBRGB, IMCLTNO | IMALPHANO, }, + + { IMVFBRGB, IMCLTYES | IMALPHANO, }, + + { IMVFBMONO, IMCLTNO | IMALPHAYES, }, + + { IMVFBMONO, IMCLTYES | IMALPHAYES, }, + + { IMVFBMONO, IMCLTNO | IMALPHANO, }, + + { IMVFBMONO, IMCLTYES | IMALPHANO, }, + + }, + + + + /* Index8 VFB, CLT, Alpha planes */ + + { + + { IMVFBINDEX8, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHAYES, }, + + { IMVFBRGB, IMCLTNO | IMALPHAYES, }, + + { IMVFBRGB, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHANO, }, + + { IMVFBRGB, IMCLTNO | IMALPHANO, }, + + { IMVFBRGB, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHANO, }, + + { IMVFBMONO, IMCLTYES | IMALPHAYES, }, + + { IMVFBMONO, IMCLTYES | IMALPHANO, }, + + { IMVFBMONO, IMCLTNO | IMALPHAYES, }, + + { IMVFBMONO, IMCLTNO | IMALPHANO, }, + + }, + +}; + + + + + +static imFilePrefTable imPrefIndex16 = + +{ + + /* Index16 VFB, no CLT, no Alpha planes */ + + { + + { IMVFBINDEX16, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHAYES, }, + + { IMVFBRGB, IMCLTNO | IMALPHANO, }, + + { IMVFBRGB, IMCLTYES | IMALPHANO, }, + + { IMVFBRGB, IMCLTNO | IMALPHAYES, }, + + { IMVFBRGB, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHAYES, }, + + { IMVFBMONO, IMCLTNO | IMALPHANO, }, + + { IMVFBMONO, IMCLTYES | IMALPHANO, }, + + { IMVFBMONO, IMCLTNO | IMALPHAYES, }, + + { IMVFBMONO, IMCLTYES | IMALPHAYES, }, + + }, + + + + /* Index16 VFB, CLT, no Alpha planes */ + + { + + { IMVFBINDEX16, IMCLTYES | IMALPHANO, }, + + { IMVFBRGB, IMCLTNO | IMALPHANO, }, + + { IMVFBRGB, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHAYES, }, + + { IMVFBRGB, IMCLTNO | IMALPHAYES, }, + + { IMVFBRGB, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHAYES, }, + + { IMVFBMONO, IMCLTYES | IMALPHANO, }, + + { IMVFBMONO, IMCLTYES | IMALPHAYES, }, + + { IMVFBMONO, IMCLTNO | IMALPHANO, }, + + { IMVFBMONO, IMCLTNO | IMALPHAYES, }, + + }, + + + + /* Index16 VFB, no CLT, Alpha planes */ + + { + + { IMVFBINDEX16, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHAYES, }, + + { IMVFBRGB, IMCLTNO | IMALPHAYES, }, + + { IMVFBRGB, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHANO, }, + + { IMVFBRGB, IMCLTNO | IMALPHANO, }, + + { IMVFBRGB, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHANO, }, + + { IMVFBMONO, IMCLTNO | IMALPHAYES, }, + + { IMVFBMONO, IMCLTYES | IMALPHAYES, }, + + { IMVFBMONO, IMCLTNO | IMALPHANO, }, + + { IMVFBMONO, IMCLTYES | IMALPHANO, }, + + }, + + + + /* Index16 VFB, CLT, Alpha planes */ + + { + + { IMVFBINDEX16, IMCLTYES | IMALPHAYES, }, + + { IMVFBRGB, IMCLTNO | IMALPHAYES, }, + + { IMVFBRGB, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHANO, }, + + { IMVFBRGB, IMCLTNO | IMALPHANO, }, + + { IMVFBRGB, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHANO, }, + + { IMVFBMONO, IMCLTYES | IMALPHAYES, }, + + { IMVFBMONO, IMCLTYES | IMALPHANO, }, + + { IMVFBMONO, IMCLTNO | IMALPHAYES, }, + + { IMVFBMONO, IMCLTNO | IMALPHANO, }, + + }, + +}; + + + + + +static imFilePrefTable imPrefRgb = + +{ + + /* Rgb VFB, no CLT, no Alpha planes */ + + { + + { IMVFBRGB, IMCLTNO | IMALPHANO, }, + + { IMVFBRGB, IMCLTYES | IMALPHANO, }, + + { IMVFBRGB, IMCLTNO | IMALPHAYES, }, + + { IMVFBRGB, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHAYES, }, + + { IMVFBMONO, IMCLTYES | IMALPHANO, }, + + { IMVFBMONO, IMCLTYES | IMALPHAYES, }, + + { IMVFBMONO, IMCLTNO | IMALPHANO, }, + + { IMVFBMONO, IMCLTNO | IMALPHAYES, }, + + }, + + + + /* Rgb VFB, CLT, no Alpha planes */ + + { + + { IMVFBRGB, IMCLTYES | IMALPHANO, }, + + { IMVFBRGB, IMCLTYES | IMALPHAYES, }, + + { IMVFBRGB, IMCLTNO | IMALPHANO, }, + + { IMVFBRGB, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHAYES, }, + + { IMVFBMONO, IMCLTYES | IMALPHANO, }, + + { IMVFBMONO, IMCLTYES | IMALPHAYES, }, + + { IMVFBMONO, IMCLTNO | IMALPHANO, }, + + { IMVFBMONO, IMCLTNO | IMALPHAYES, }, + + }, + + + + /* Rgb VFB, no CLT, Alpha planes */ + + { + + { IMVFBRGB, IMCLTNO | IMALPHAYES, }, + + { IMVFBRGB, IMCLTYES | IMALPHAYES, }, + + { IMVFBRGB, IMCLTNO | IMALPHANO, }, + + { IMVFBRGB, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHANO, }, + + { IMVFBMONO, IMCLTYES | IMALPHAYES, }, + + { IMVFBMONO, IMCLTYES | IMALPHANO, }, + + { IMVFBMONO, IMCLTNO | IMALPHAYES, }, + + { IMVFBMONO, IMCLTNO | IMALPHANO, }, + + }, + + + + /* Rgb VFB, CLT, Alpha planes */ + + { + + { IMVFBRGB, IMCLTYES | IMALPHAYES, }, + + { IMVFBRGB, IMCLTYES | IMALPHANO, }, + + { IMVFBRGB, IMCLTNO | IMALPHAYES, }, + + { IMVFBRGB, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX16, IMCLTNO | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTYES | IMALPHANO, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHAYES, }, + + { IMVFBINDEX8, IMCLTNO | IMALPHANO, }, + + { IMVFBMONO, IMCLTYES | IMALPHAYES, }, + + { IMVFBMONO, IMCLTYES | IMALPHANO, }, + + { IMVFBMONO, IMCLTNO | IMALPHAYES, }, + + { IMVFBMONO, IMCLTNO | IMALPHANO, }, + + }, + +}; + + + + + +#endif /* __IMVFBREFH__ */ + diff --git a/utils/roq2/libim/imras.c b/utils/roq2/libim/imras.c new file mode 100644 index 0000000..3d84616 --- /dev/null +++ b/utils/roq2/libim/imras.c @@ -0,0 +1,5207 @@ +/** $Header: /roq/libim/imras.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/imras.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** imras.c - Sun Rasterfile I/O + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** imras.c contains routines to read and write Sun Rasterfiles for + + ** the image manipulation library. Raster data read in is stored + + ** in a VFB and optional CLT in a tag list. Raster data written + + ** out is taken from a tag list. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** none + + ** + + ** PRIVATE CONTENTS + + ** + + ** imRasRead f read a Sun Rasterfile + + ** imRasWrite1 f write 1-bit standard RAS format + + ** imRasWrite8 f write 8-bit standard RAS format + + ** imRasWriteRGB f write 24-bit RGB standard RAS format + + ** imRasWriteRGBA f write 32-bit RGBA standard RAS format + + ** imRasWriteRLE1 f write 1-bit encoded RAS format + + ** imRasWriteRLE8 f write 8-bit encoded RAS format + + ** imRasWriteRLERGB f write 24-bit RGB encoded RAS format + + ** imRasWriteRLERGBA f write 32-bit RGBA encoded RAS format + + ** + + ** imRasHeaderInfo t Rasterfile header information + + ** imRasHeaderFields v imRasHeaderInfo description for Bin pkg + + ** imRasHeader v Rasterfile header holder + + ** + + ** IMRASMAGIC d file magic number + + ** IMRASESC d escape code for run-length encoding + + ** IMRT* d raster encoding types + + ** IMRMT* d raster colormap encoding types + + ** + + ** IMRAS_RED,IMRAS_GREEN, + + ** IMRAS_BLUE,IMRAS_ALPHA d RGBA color cycle counter values + + ** + + ** imRasRead1 f read 1-bit RAS format + + ** imRasRead8 f read 8-bit standard RAS format + + ** imRasRead24RGB f read 24-bit RGB standard RAS format + + ** imRasRead32RGBA f read 32-bit RGBA standard RAS format + + ** imRasReadRLE1 f read 1-bit encoded RAS format + + ** imRasReadRLE8 f read 8-bit encoded RAS format + + ** imRasReadRLE24RGB f read 24-bit RGB encoded RAS format + + ** imRasReadRLE32RGBA f read 32-bit RGBA encoded RAS format + + ** + + ** imRasWriteHeaderClt f write header and CLT + + ** imRasCltToBuffer f convert CLT to file format buffer + + ** + + ** IMAddRun m add another pixel to RLE buffer + + ** IMDumpRun m dump an RLE run into the run buffer + + ** + + ** HISTORY + + ** $Log: /roq/libim/imras.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.22 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.21 1995/04/03 21:35:15 bduggan + + ** took out #ifdef NEWMAGIC + + ** + + ** Revision 1.20 1995/01/10 23:41:09 bduggan + + ** put in IMMULTI, IMPIPE instead of TRUE/FALSE + + ** made read/write routines static + + ** + + ** Revision 1.19 94/10/03 11:30:45 nadeau + + ** Fixed bug in RGB+Alpha handling that did incorrect scanling padding, + + ** causing corrupted images for some image widths. + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Rearranged magic number structures for format handlers. + + ** Made format handler routines static (i.e., local to file). + + ** Updated comments, adding format descriptions and references. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.18 92/12/03 01:51:39 nadeau + + ** Corrected info messages. + + ** + + ** Revision 1.17 92/11/23 18:43:05 nadeau + + ** Removed use of IMINFOMSG. + + ** + + ** Revision 1.16 92/11/04 12:06:10 groening + + ** put ImFIleFormat info and magic number info + + ** from imfmt.c into this file. + + ** + + ** Revision 1.15 92/09/29 17:57:38 vle + + ** Added ImInfo messages. + + ** + + ** Revision 1.14 92/08/31 17:33:39 vle + + ** Updated copyright notice. + + ** + + ** Revision 1.13 92/04/09 09:35:21 groening + + ** To make the compiler happy added extern statements. + + ** + + ** Revision 1.12 91/10/03 09:16:52 nadeau + + ** Reversed order from R-G-B to B-G-R on 24-bit image files. + + ** Added 32-bit image file read and write support. + + ** + + ** Revision 1.11 91/03/13 17:37:10 nadeau + + ** Fixed RGB ordering bug for RGB uncompressed read. + + ** + + ** Revision 1.10 91/02/12 10:44:47 nadeau + + ** Changed read and write routine names. Removed the top level + + ** write call and updated the VFB-specific write calls to be + + ** entry points from the generic ImFileWrite code. Encapsulated + + ** the run-length encoding into a few macros to reduce the + + ** apparent complexity of the RLE write routines. Removed the + + ** tag table error checking now handled by ImFileWrite. Removed + + ** temp file creation, now handled by ImFileWrite. + + ** + + ** Revision 1.9 91/01/30 18:10:04 nadeau + + ** Added monochrome support for read and write. + + ** + + ** Revision 1.8 90/12/03 12:51:14 nadeau + + ** Added checks to skip the extra pad byte on odd-length scanlines + + ** when reading RLE images. + + ** + + ** Revision 1.7 90/07/25 16:20:28 nadeau + + ** Documented the RAS format more fully. Changed image write to use + + ** a scratch file when writing to pipes. Fixed a byte padding problem + + ** on encoded images (and added description of problem to RAS comments). + + ** Removed stubs for 1-bit deep image read/write. + + ** + + ** Revision 1.6 90/07/23 13:49:04 nadeau + + ** Reversed order of channels on reading RGB images from BGR to RGB. + + ** + + ** Revision 1.5 90/07/02 13:21:43 nadeau + + ** Updated to the new error handling mechanism. + + ** + + ** Revision 1.4 90/06/25 14:39:01 nadeau + + ** Changed ImTag* to Tag* (new names). + + ** + + ** Revision 1.3 90/05/16 07:47:46 todd + + ** Add #include "ininternal.h" to top of file + + ** + + ** Revision 1.2 90/05/11 09:56:11 nadeau + + ** Totally restructured everything. Broke format variants into separate + + ** routines. For write, broke pipe vs file I/O into separate routines. + + ** Updated call interface and code to handle I/O on file descriptors nad + + ** file pointers. Finished 24-bit code. + + ** + + ** Revision 1.1 90/03/06 17:29:09 nadeau + + ** Initial revision + + ** + + **/ + + + +#include "iminternal.h" + + + +/** + + ** FORMAT + + ** RAS - Sun Rasterfile + + ** + + ** AKA + + ** sun, sr, scr + + ** + + ** FORMAT REFERENCES + + ** Graphics File Formats, David C. Kay, John R. Levine + + ** + + ** CODE CREDITS + + ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1990. + + ** + + ** DESCRIPTION + + ** Sun rasterfiles start with a fixed size header: + + ** + + ** magic The file's magic number. + + ** + + ** width The width of the image, in pixels. + + ** + + ** height The height of the image, in pixels. + + ** + + ** depth The depth of the image, in bits. + + ** + + ** length The total image storage size, in bytes. + + ** + + ** type The type of image encoding used. One of: + + ** + + ** Standard Unencoded (see below). + + ** ByteEncoded Run-length encoded (see below). + + ** + + ** mapType The type of CLT (if any). One of: + + ** + + ** None No CLT included. + + ** EqualRGB All red, then green, then blue + + ** Raw Unkown!!! + + ** + + ** mapLength The length of the CLT (if any), in bytes. + + ** + + ** Following the header is the CLT, stored as a string of red values, + + ** then green, then blue, for consecutive entries in the CLT. + + ** + + ** STANDARD FORMAT (unencoded) + + ** For standard (unencoded) images, consecutive bytes in the file + + ** correspond to consecutive pixels (or components of the pixel for + + ** RGB images). + + ** + + ** BYTE-ENCODING FORMAT (run-length encoded) + + ** The Sun rasterfile encodes the image byte stream by counting up + + ** adjacent identical bytes and flagging a count-index pair with an + + ** "escape" byte: + + ** + + ** + + ** The count is always biased by -1 (ie, count=1 means do a 2-byte run, + + ** count=2 means do a 3-byte run, and so on). The maximum run length + + ** is therefore 256. Runs always end on scanline boundaries. + + ** + + ** To include the "escape" byte in the image, give a count of 0, + + ** followed by no index: + + ** <0> + + ** + + ** To avoid expanding the storage size of images without any runs (like + + ** the ubiquitous Mandrill), just give the index by itself: + + ** + + ** + + ** The worst case scenario is an image filled with "escape" bytes. + + ** Each "escape" byte will be expanded into an <0> pair, + + ** thus doubling the size of the image file. + + ** + + ** The "escape" byte is an 0x80 (0200) (128). + + ** + + ** EXAMPLE #1 + + ** Unencoded 8-bit Index Stream: + + ** 0xAA 0xBB 0xBB 0xCC 0x80 0xDD 0xDD 0xDD + + ** + + ** Encoded Stream: + + ** 0xAA 0x80 0x01 0xBB 0xCC 0x80 0x00 0x80 0x02 0xDD + + ** + + ** There are two runs (2 * 0xBB, and 3 * 0xDD) and one occurrence of + + ** the escape byte. The encoded stream turns out to be 2 bytes larger + + ** than the unencoded one. + + ** + + ** + + ** EXAMPLE #2 + + ** Unencoded RGB Stream: + + ** 0x00 0x00 0x00 0x45 0x52 0x12 0x12 0x12 0x43 + + ** (blue) (green) (red) ... + + ** + + ** Encoded Stream: + + ** 0x80 0x02 0x00 0x45 0x52 0x80 0x02 0x12 0x43 + + ** + + ** There are two runs (3 * 0x0, and 3 * 0x12). The encoded stream comes + + ** out the same size as the unencoded one. Note that the 0x12 of the + + ** second RGB triplet, and the 0x12's of the third RGB triplet form a + + ** run of three 0x12's. + + ** + + ** ODDITIES + + ** Scanlines with an odd number of pixels must be padded with one dummy + + ** pixel. Note that the addition of a pad byte is based upon the scanline + + ** length... + + ** + + ** For 8-bit and 24-bit "standard" images, the pad byte makes + + ** each scanline's stream of bytes end on a 16-bit boundary. + + ** This is clearly the original intent of the padding. + + ** + + ** For 8-bit and 24-bit "encoded" images, the length of the + + ** scanline's encoded stream could be odd or even, depending upon + + ** the way runs are created from the data, not dependent upon the + + ** evenness or oddness of the original scanline length. + + ** Nevertheless, if the original scanline had an odd number of + + ** pixels in it, then a pad byte must be added, even if this + + ** makes the encoded stream have an odd number of bytes. + + ** + + ** For 32-bit images (with an alpha channel), there is no padding. + + **/ + + + +#ifdef __STDC__ + +static int imRasRead1(int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb **pVfb ); + +static int imRasRead8( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb **pVfb); + +static int imRasRead24RGB( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb **pVfb); + +static int imRasRead32RGBA( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb **pVfb); + +static int imRasReadRLE1( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb **pVfb); + +static int imRasReadRLE8( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb **pVfb); + +static int imRasReadRLE24RGB( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb **pVfb); + +static int imRasReadRLE32RGBA( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb **pVfb); + +static int imRasCltToBuffer( ImVfb *vfb, unsigned char **cltBuffer, int *nClt ); + +#else + +static int imRasRead1( ); + +static int imRasRead8( ); + +static int imRasRead24RGB( ); + +static int imRasRead32RGBA( ); + +static int imRasReadRLE1( ); + +static int imRasReadRLE8( ); + +static int imRasReadRLE24RGB( ); + +static int imRasReadRLE32RGBA( ); + +static int imRasCltToBuffer( ); + +#endif + + + +/* + + * RAS - Sun Rasterfile + + * For information on these structures, how to use them, etc. please + + * see imfmt.c. + + */ + +#ifdef __STDC__ + +static int imRasRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imRasWrite1( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, + + TagTable *tagTable ); + +static int imRasWrite8(ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, + + TagTable *tagTable ); + +static int imRasWriteRGB( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, + + TagTable *tagTable ); + +static int imRasWriteRGBA( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, + + TagTable *tagTable ); + +static int imRasWriteRLE1( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, + + TagTable *tagTable ); + +static int imRasWriteRLE8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, + + TagTable *tagTable ); + +static int imRasWriteRLERGB( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, + + TagTable *tagTable ); + +static int imRasWriteRLERGBA( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, + + TagTable *tagTable ); + +#else + +static int imRasRead( ); + +static int imRasWrite1( ), imRasWrite8( ); + +static int imRasWriteRGB( ), imRasWriteRGBA( ); + +static int imRasWriteRLE1( ), imRasWriteRLE8( ); + +static int imRasWriteRLERGB( ), imRasWriteRLERGBA( ); + +#endif + +static char *imRasNames[ ] = { "ras", "sun", "sr", "scr", NULL }; + +static unsigned char imRasMagicNumber[ ] = { 0x59, 0xA6, 0x6A, 0x95 }; + + + +static ImFileFormatReadMap imRasReadMap[ ] = + +{ + + /* in out */ + + /* type,ch,dep, attr. VFB type attr. */ + + { IN,1,1, 0, IMVFBMONO, 0 }, + + { IN,1,1, C, IMVFBMONO, C }, + + { IN,1,8, 0, IMVFBINDEX8, 0 }, + + { IN,1,8, C, IMVFBINDEX8, C }, + + { RGB,3,8, 0, IMVFBRGB, 0 }, + + { RGB,3,8, C, IMVFBRGB, C }, + + { RGB,3,8, A, IMVFBRGB, A }, + + { RGB,3,8, A | C, IMVFBRGB, A | C }, + + + + { IN,1,1, RLE, IMVFBMONO, 0 }, + + { IN,1,1, RLE | C,IMVFBMONO, C }, + + { IN,1,8, RLE, IMVFBINDEX8, 0 }, + + { IN,1,8, RLE | C,IMVFBINDEX8, C }, + + { RGB,3,8, RLE, IMVFBRGB, 0 }, + + { RGB,3,8, RLE | C,IMVFBRGB, C }, + + { RGB,3,8, RLE|A, IMVFBRGB, A }, + + { RGB,3,8, RLE|A|C,IMVFBRGB, A | C }, + + { -1, 0, -1, 0 }, + +}; + +static ImFileFormatWriteMap imRasWriteMap[ ] = + +{ + + /* in out */ + + /* VFB type, attr., type,ch,dep, attr., func */ + + { IMVFBMONO, 0, IN,1,1, RLE, imRasWriteRLE1 }, + + { IMVFBMONO, C, IN,1,1, RLE | C,imRasWriteRLE1 }, + + { IMVFBMONO, 0, IN,1,1, 0, imRasWrite1 }, + + { IMVFBMONO, C, IN,1,1, C, imRasWrite1 }, + + + + { IMVFBINDEX8, 0, IN,1,8, RLE, imRasWriteRLE8 }, + + { IMVFBINDEX8, C, IN,1,8, RLE | C,imRasWriteRLE8 }, + + { IMVFBINDEX8, 0, IN,1,8, 0, imRasWrite8 }, + + { IMVFBINDEX8, C, IN,1,8, C, imRasWrite8 }, + + + + { IMVFBRGB, 0, RGB,3,8, RLE, imRasWriteRLERGB}, + + { IMVFBRGB, C, RGB,3,8, RLE|C, imRasWriteRLERGB}, + + { IMVFBRGB, 0, RGB,3,8, 0, imRasWriteRGB}, + + { IMVFBRGB, C, RGB,3,8, C, imRasWriteRGB}, + + + + { IMVFBRGB, A, RGB,3,8, RLE|A, imRasWriteRLERGBA}, + + { IMVFBRGB, A | C, RGB,3,8, RLE|A|C,imRasWriteRLERGBA}, + + { IMVFBRGB, A, RGB,3,8, A, imRasWriteRGBA}, + + { IMVFBRGB, A | C, RGB,3,8, A|C, imRasWriteRGBA}, + + { -1, 0, -1, 0, NULL }, + +}; + + + + + +static ImFileMagic imFileRasMagic []= + +{ + + { 0, 4, imRasMagicNumber}, + + { 0, 0, NULL }, + +}; + + + +ImFileFormat ImFileRasFormat = + +{ + + imRasNames, "Sun Rasterfile", + + "Sun Microsystems, Inc.", + + "1- and 8-bit color index image files, with or without CLT's.\n\ +24-bit RGB and 32-bit RGB+alpha image files, with or without CLT's.\n\ +Standard (uncompressed) and byte-encoded (RLE compressed).", + "1- and 8-bit color index image files, with or without CLT's.\n\ +24-bit RGB and 32-bit RGB+alpha image files, with or without CLT's.\n\ +Standard (uncompressed) and byte-encoded (RLE compressed).", + imFileRasMagic, + + IMNOMULTI, IMNOPIPE, + + IMNOMULTI, IMNOPIPE, + + imRasRead, imRasReadMap, imRasWriteMap + +}; + + + + + + + + + + + +/* + + * TYPEDEF & STRUCTURE + + * imRasHeaderInfo - Rasterfile header information + + * imRasHeaderFields - imRasHeaderInfo description for Bin pkg + + * imRasHeader - Rasterfile header holder + + * + + * DESCRIPTION + + * A rasterfile's header contains a magic number, the image's width, + + * height, and depth, the length of the file, the type of run length + + * encoding in use, and the CLT, if any. + + */ + + + +typedef struct imRasHeaderInfo + +{ + + unsigned int ras_magic; /* Magic number */ + + unsigned int ras_width; /* Image width */ + + unsigned int ras_height; /* Image height */ + + unsigned int ras_depth; /* Image depth */ + + unsigned int ras_length; /* Image length (in bytes) */ + + unsigned int ras_type; /* Type of encoding */ + + unsigned int ras_maptype; /* Type of CLT (color map) */ + + unsigned int ras_maplength; /* Length of CLT */ + +} imRasHeaderInfo; + + + +static BinField imRasHeaderFields[ ] = + +{ + + { UINT, 4, 1 }, /* ras_magic */ + + { UINT, 4, 1 }, /* ras_width */ + + { UINT, 4, 1 }, /* ras_height */ + + { UINT, 4, 1 }, /* ras_depth */ + + { UINT, 4, 1 }, /* ras_length */ + + { UINT, 4, 1 }, /* ras_type */ + + { UINT, 4, 1 }, /* ras_maptype */ + + { UINT, 4, 1 }, /* ras_maplength */ + + { 0, 0, 0 }, + +}; + + + +static imRasHeaderInfo imRasHeader; /* RAS file header */ + + + + + + + + + + + +/* + + * CONSTANTS + + * IMRASMAGIC - file magic number + + * IMRASESC - escape code for run-length encoding + + * IMRT* - raster encoding types + + * IMRMT* - raster colormap encoding types + + * + + * DESCRIPTION + + * IMRASMAGIC (Sun's RAS_MAGIC) is the 32-bit magic number at the + + * top of all rasterfiles. + + * + + * IMRASESC indicates the beginning of a run in a run length + + * encoded raster file. + + * + + * IMRT* (Sun's RT_*) select the type of run-length encoding + + * used to encode the image data in the rasterfile. + + * + + * IMRMT* (Sun's RMT_*) select the type of colormap (CLT) + + * encoding used to encode the CLT data in the rasterfile, if any. + + */ + +#define IMRASMAGIC 0x59A66A95 + +#define IMRASESC 0x80 + + + +#define IMRTOLD 0 /* Raw pixrect image in 68K order*/ + +#define IMRTSTANDARD 1 /* Raw pixrect image in 68K order*/ + +#define IMRTBYTEENCODED 2 /* Run-length encoded */ + +#define IMRTEXPERIMENTAL 0xFFFF /* Reserved by Sun for testing */ + + + +#define IMRMTNONE 0 /* No CLT. ras_maplength == 0 */ + +#define IMRMTEQUALRGB 1 /* All red, then green, then blue*/ + +#define IMRMTRAW 2 /* Raw CLT (?) */ + + + + + + + + + + + +/* + + * CONSTANTS + + * IMRAS_RED, IMRAS_GREEN, IMRAS_BLUE, IMRAS_ALPHA - RGBA color cycle counter values + + * + + * DESCRIPTION + + * IMRAS_RED, IMRAS_GREEN, IMRAS_BLUE, and IMRAS_ALPHA are used as cycle values for a counter + + * when encoding and decoding run-length stuff. + + */ + + + +#define IMRAS_BLUE 0 + +#define IMRAS_GREEN 1 + +#define IMRAS_RED 2 + +#define IMRAS_ALPHA 3 + + + + + + + + + +#ifndef L_SET + +#define L_SET 0 /* Absolute offset */ + +#define L_INCR 1 /* Relative to current offset */ + +#define L_XTND 2 /* Relative to end of file */ + +#endif + + + +#ifndef F_SET + +#define F_SET 0 /* Absolute offset */ + +#define F_INCR 1 /* Relative to current offset */ + +#define F_XTND 2 /* Relative to end of file */ + +#endif + + + + + + + + + + + +/* + + * FUNCTION + + * imRasRead - read a Sun Rasterfile + + * + + * DESCRIPTION + + * The file header is read and the magic number checked. If there is + + * a CLT in the file, it is read in and converted into an ImClt. + + * Separate routines are then called to handle different depth and + + * storage format variations of the image data. + + * + + * Read supports no format flags. + + */ + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imRasRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imRasRead( ioType, fd, fp, flagsTable, tagTable ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + int nClt; /* Number of CLT entries */ + + ImClt *clt; /* Read in colormap */ + + ImCltPtr cptr; /* Color pointer */ + + unsigned char *cltBuffer; /* Color lookup table buffer */ + + unsigned char *redp; /* Red CLT range pointer */ + + unsigned char *greenp; /* Green CLT range pointer */ + + unsigned char *bluep; /* Blue CLT range pointer */ + + int i; /* Counter */ + + int status; /* Return status holder */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * Set the Binary I/O package's byte order, read in the file's + + * header, and check the magic number. + + */ + + BinByteOrder( BINMBF ); + + if ( ImBinReadStruct( ioType, fd, fp, &imRasHeader, + + imRasHeaderFields )== -1) + + { + + ImReturnBinError( ); + + } + + if ( imRasHeader.ras_magic != IMRASMAGIC ) + + { + + ImErrNo = IMEMAGIC; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + ImInfo( "Byte Order", "Most Significant Byte First" ); + + + + sprintf( message, "%d x %d", imRasHeader.ras_width, + + imRasHeader.ras_height ); + + ImInfo( "Resolution", message ); + + + + if( imRasHeader.ras_depth >= 24 ) + + sprintf( message, "%d-bit RGB", imRasHeader.ras_depth ); + + else + + sprintf( message, "%d-bit Color Indexed", imRasHeader.ras_depth ); + + ImInfo( "Type", message ); + + + + /* + + * Read in the CLT, if any. + + */ + + switch ( imRasHeader.ras_maptype ) + + { + + case IMRMTNONE: /* No CLT. */ + + if ( imRasHeader.ras_maplength != 0 ) + + { + + /* No CLT, yet the length says there is one!? */ + + ImErrNo = IMECLTLENGTH; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + clt = IMCLTNULL; + + + + ImInfo( "Color Table", "none" ); + + break; + + + + case IMRMTEQUALRGB: /* RGB CLT. */ + + if ( imRasHeader.ras_maplength == 0 ) + + { + + /* Is a CLT, yet the length says not!? */ + + ImErrNo = IMECLTLENGTH; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + nClt = imRasHeader.ras_maplength / 3; + + if ( (clt = ImCltAlloc( nClt )) ==IMCLTNULL) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + ImMalloc( cltBuffer, unsigned char *, imRasHeader.ras_maplength ); + + + + /* Read in Red, Green, and Blue. */ + + if ( ImBinRead( ioType, fd, fp, cltBuffer, UCHAR, 1, + + imRasHeader.ras_maplength ) == -1 ) + + { + + free( (char *)cltBuffer ); + + ImReturnBinError( ); + + } + + cptr = ImCltQFirst( clt ); + + redp = cltBuffer; + + greenp = cltBuffer + nClt; + + bluep = cltBuffer + nClt + nClt; + + for ( i = 0; i < nClt; i++ ) + + { + + ImCltSRed( cptr, *redp++ ); + + ImCltSGreen( cptr, *greenp++ ); + + ImCltSBlue( cptr, *bluep++ ); + + ImCltSInc( clt, cptr ); + + } + + + + free( (char *)cltBuffer ); + + + + sprintf( message, "%d Entries", nClt ); + + ImInfo( "Color Table", message ); + + break; + + + + case IMRMTRAW: /* Raw CLT ? */ + + + + /* Fall through to error. */ + + + + default: + + ImErrNo = IMESYNTAX; + + ImErrorFatal( "Unkown CLT type in file header.", -1, ImErrNo ); + + } + + + + + + /* + + * Based on the image depth and encoding scheme, call a function + + * to do the rest of the work. These functions are never called + + * again anywhere else. It just breaks up this routine into + + * smaller bite-sized chunks for easier debugging. + + */ + + switch ( imRasHeader.ras_type ) + + { + + case IMRTOLD: + + /* + + * Old format rasterfiles have a 0 in the ras_length field. + + * Fortunately, old format rasterfiles don't do run-length + + * encoding, so the size of the image data is just the height + + * times the width times the depth, in bytes. + + */ + + imRasHeader.ras_length = imRasHeader.ras_width * + + imRasHeader.ras_height * imRasHeader.ras_depth; + + + + /* Fall through to standard. */ + + + + case IMRTSTANDARD: + + /* Un-encoded raster image. */ + + + + ImInfo( "Compression Type", "none (Standard)" ); + + + + switch ( imRasHeader.ras_depth ) + + { + + case 1: /* Monochrome image. */ + + status = imRasRead1( ioType, fd, fp, flagsTable, + + tagTable, &vfb ); + + ImInfo( "Alpha Channel", "none" ); + + break; + + case 8: /* CLT image. */ + + status = imRasRead8( ioType, fd, fp, flagsTable, + + tagTable, &vfb ); + + ImInfo( "Alpha Channel", "none" ); + + break; + + case 24:/* RGB image. */ + + status = imRasRead24RGB( ioType, fd, fp, flagsTable, + + tagTable, &vfb ); + + ImInfo( "Alpha Channel", "none" ); + + break; + + case 32:/* RGBA image. */ + + status = imRasRead32RGBA( ioType, fd, fp, flagsTable, + + tagTable, &vfb ); + + ImInfo( "Alpha Channel", "8-bit" ); + + break; + + default: + + ImErrNo = IMEDEPTH; + + ImErrorFatal( "Unknown standard-format image depth", -1, ImErrNo ); + + } + + break; + + + + case IMRTBYTEENCODED: + + /* Run-length encoded raster image. */ + + + + ImInfo("Compression Type","Run Length Encoded (Byte Encoded)"); + + + + switch ( imRasHeader.ras_depth ) + + { + + case 1: /* Monochrome image. */ + + status = imRasReadRLE1( ioType, fd, fp, flagsTable, + + tagTable, &vfb ); + + ImInfo( "Alpha Channel", "none" ); + + break; + + case 8: /* CLT image. */ + + status = imRasReadRLE8( ioType, fd, fp, flagsTable, + + tagTable, &vfb ); + + ImInfo( "Alpha Channel", "none" ); + + break; + + case 24:/* RGB image. */ + + status = imRasReadRLE24RGB( ioType, fd, fp, flagsTable, + + tagTable, &vfb ); + + ImInfo( "Alpha Channel", "none" ); + + break; + + case 32:/* RGBA image. */ + + status = imRasReadRLE32RGBA( ioType, fd, fp, flagsTable, + + tagTable, &vfb ); + + ImInfo( "Alpha Channel", "8-bit" ); + + break; + + default: + + ImErrNo = IMEDEPTH; + + ImErrorFatal( "Unkown byte-encoded-format image depth", -1, ImErrNo ); + + } + + break; + + + + case IMRTEXPERIMENTAL: + + /* Sun private experimental format. Unsupported by us.*/ + + + + /* Fall through to error. */ + + + + default: + + ImErrNo = IMEIMAGETYPE; + + ImErrorFatal( "Unknown image type in file header", -1, ImErrNo ); + + } + + + + + + if ( status == -1 ) + + return ( -1 ); + + + + if ( clt != IMCLTNULL ) + + { + + ImVfbSClt( vfb, clt ); + + TagTableAppend( tagTable, + + TagEntryAlloc( "image clt", POINTER, &clt )); + + } + + TagTableAppend( tagTable, TagEntryAlloc( "image vfb", POINTER, &vfb )); + + + + return ( 0 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imRasRead1 - read 1-bit RAS format + + * imRasRead8 - read 8-bit RAS format + + * imRasRead24RGB - read 24-bit RGB RAS format + + * imRasRead32RGBA - read 32-bit RGBA RAS format + + * + + * DESCRIPTION + + * Each of these routines deal with "standard" format RAS files. + + * The input stream is a file or a pipe. We don't care. + + * + + * A new VFB is allocated. The image is read in, one scanline at + + * a time, and converted into the VFB. + + */ + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imRasRead1( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb **pVfb ) + +#else + +imRasRead1( ioType, fd, fp, flagsTable, tagTable, pVfb ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + + ImVfb **pVfb; /* VFB to fill and return */ + +#endif + +{ + + unsigned char *rbp; /* Run buffer pointer */ + + int i, j; /* Counters */ + + ImVfb *vfb; /* New vfb */ + + ImVfbPtr pptr; /* Pixel pointer */ + + int n; /* # of bytes in a scanline */ + + int byte; /* Byte of 8 mono pixels */ + + unsigned char *runBuffer; /* Run buffer */ + + + + + + /* + + * Allocate a VFB of the required size. + + */ + + if ( (*pVfb = ImVfbAlloc( imRasHeader.ras_width, imRasHeader.ras_height, + + IMVFBMONO )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + vfb = *pVfb; + + + + + + /* + + * Allocate a buffer that is big enough for one scanline + + * (rounded to 2-bytes). Read in and process the image one + + * scanline at a time. + + */ + + pptr = ImVfbQFirst( vfb ); + + n = (imRasHeader.ras_width + 7) / 8; + + if ( (n&1) != 0 ) + + n++; /* Round out to multiple of 2 */ + + + + ImMalloc( runBuffer, unsigned char *, sizeof( unsigned char ) * n ); + + + + for ( i = 0; i < imRasHeader.ras_height; i++ ) + + { + + if ( ImBinRead( ioType, fd, fp, runBuffer, UCHAR, 1, n ) == -1 ) + + { + + free( (char *)runBuffer ); + + ImVfbFree( vfb ); + + ImReturnBinError( ); + + } + + rbp = runBuffer; + + for ( j = 0; j < imRasHeader.ras_width; j++ ) + + { + + if ( (j&7) == 0 ) + + byte = *rbp++; + + ImVfbSMono( vfb, pptr, (byte & 0x80) ? 0 : 1 ); + + ImVfbSInc( vfb, pptr ); + + byte <<= 1; + + } + + } + + + + free( (char *)runBuffer ); + + return ( 0 ); + +} + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imRasRead8( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb **pVfb ) + +#else + +imRasRead8( ioType, fd, fp, flagsTable, tagTable, pVfb ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + + ImVfb **pVfb; /* VFB to fill and return */ + +#endif + +{ + + unsigned char *rbp; /* Run buffer pointer */ + + int i, j; /* Counters */ + + ImVfb *vfb; /* New vfb */ + + ImVfbPtr pptr; /* Pixel pointer */ + + int n; /* # of bytes in a scanline */ + + unsigned char *runBuffer; /* Run buffer */ + + + + + + /* + + * Allocate a VFB of the required size. + + */ + + if ( (*pVfb = ImVfbAlloc( imRasHeader.ras_width, imRasHeader.ras_height, + + IMVFBINDEX8 )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + vfb = *pVfb; + + + + + + /* + + * Allocate a buffer that is big enough for one scanline + + * (rounded to 2-bytes). Read in and process the image one + + * scanline at a time. + + */ + + pptr = ImVfbQFirst( vfb ); + + n = imRasHeader.ras_width; + + if ( (n&1) != 0 ) + + n++; /* Round out to multiple of 2 */ + + + + ImMalloc( runBuffer, unsigned char *, sizeof( unsigned char ) * n ); + + + + for ( i = 0; i < imRasHeader.ras_height; i++ ) + + { + + if ( ImBinRead( ioType, fd, fp, runBuffer, UCHAR, 1, n ) == -1 ) + + { + + free( (char *)runBuffer ); + + ImVfbFree( vfb ); + + ImReturnBinError( ); + + } + + rbp = runBuffer; + + for ( j = 0; j < imRasHeader.ras_width; j++ ) + + { + + ImVfbSIndex8( vfb, pptr, *rbp++ ); + + ImVfbSInc( vfb, pptr ); + + } + + } + + + + free( (char *)runBuffer ); + + return ( 0 ); + +} + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imRasRead24RGB( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb **pVfb ) + +#else + +imRasRead24RGB( ioType, fd, fp, flagsTable, tagTable, pVfb ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + + ImVfb **pVfb; /* VFB to fill and return */ + +#endif + +{ + + unsigned char *rbp; /* Run buffer pointer */ + + int i, j; /* Counters */ + + ImVfb *vfb; /* New vfb */ + + ImVfbPtr pptr; /* Pixel pointer */ + + int n; /* # of bytes in a scanline */ + + unsigned char *runBuffer; /* Run buffer */ + + + + + + /* + + * Allocate a VFB of the required size. + + */ + + if ( (*pVfb = ImVfbAlloc( imRasHeader.ras_width, imRasHeader.ras_height, + + IMVFBRGB )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + vfb = *pVfb; + + + + + + /* + + * Allocate a buffer that is big enough for one scanline + + * (rounded to 2-bytes). Read in and process the image one + + * scanline at a time. + + */ + + pptr = ImVfbQFirst( vfb ); + + n = imRasHeader.ras_width * 3; /* # of bytes */ + + if ( (n&1) != 0 ) + + n++; /* Round out to multiple of 2 */ + + + + ImMalloc( runBuffer, unsigned char *, sizeof( unsigned char ) * n ); + + + + for ( i = 0; i < imRasHeader.ras_height; i++ ) + + { + + if ( ImBinRead( ioType, fd, fp, runBuffer, UCHAR, 1, n ) == -1 ) + + { + + free( (char *)runBuffer ); + + ImVfbFree( vfb ); + + ImReturnBinError( ); + + } + + rbp = runBuffer; + + + + for ( j = 0; j < imRasHeader.ras_width; j++ ) + + { + + ImVfbSBlue( vfb, pptr, *rbp++ ); + + ImVfbSGreen( vfb, pptr, *rbp++ ); + + ImVfbSRed( vfb, pptr, *rbp++ ); + + ImVfbSInc( vfb, pptr ); + + } + + } + + + + free( (char *)runBuffer ); + + return ( 0 ); + +} + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imRasRead32RGBA( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb **pVfb ) + +#else + +imRasRead32RGBA( ioType, fd, fp, flagsTable, tagTable, pVfb ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + + ImVfb **pVfb; /* VFB to fill and return */ + +#endif + +{ + + unsigned char *rbp; /* Run buffer pointer */ + + int i, j; /* Counters */ + + ImVfb *vfb; /* New vfb */ + + ImVfbPtr pptr; /* Pixel pointer */ + + int n; /* # of bytes in a scanline */ + + unsigned char *runBuffer; /* Run buffer */ + + + + + + /* + + * Allocate a VFB of the required size. + + */ + + if ( (*pVfb = ImVfbAlloc( imRasHeader.ras_width, imRasHeader.ras_height, + + IMVFBRGB | IMVFBALPHA )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + vfb = *pVfb; + + + + + + /* + + * Allocate a buffer that is big enough for one scanline + + * (rounded to 2-bytes). Read in and process the image one + + * scanline at a time. + + */ + + pptr = ImVfbQFirst( vfb ); + + n = imRasHeader.ras_width * 4; /* # of bytes */ + + if ( (n&1) != 0 ) + + n++; /* Round out to multiple of 2 */ + + + + ImMalloc( runBuffer, unsigned char *, sizeof( unsigned char ) * n ); + + + + for ( i = 0; i < imRasHeader.ras_height; i++ ) + + { + + if ( ImBinRead( ioType, fd, fp, runBuffer, UCHAR, 1, n ) == -1 ) + + { + + free( (char *)runBuffer ); + + ImVfbFree( vfb ); + + ImReturnBinError( ); + + } + + rbp = runBuffer; + + + + for ( j = 0; j < imRasHeader.ras_width; j++ ) + + { + + ImVfbSAlpha( vfb, pptr, *rbp++ ); + + ImVfbSBlue( vfb, pptr, *rbp++ ); + + ImVfbSGreen( vfb, pptr, *rbp++ ); + + ImVfbSRed( vfb, pptr, *rbp++ ); + + ImVfbSInc( vfb, pptr ); + + } + + } + + + + free( (char *)runBuffer ); + + return ( 0 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imRasReadRLE1 - read 1-bit encoded RAS format + + * imRasReadRLE8 - read 8-bit encoded RAS format + + * imRasReadRLE24RGB - read 24-bit RGB encoded RAS format + + * imRasReadRLE32RGBA - read 32-bit RGBA encoded RAS format + + * + + * DESCRIPTION + + * Each of these routines deal with "encoded" format RAS files. + + * The output stream is a file or a pipe. We don't care. + + * + + * A new VFB is allocated. The image is read into an image-sized + + * buffer, then converted into the VFB. + + * + + * We allocate a buffer the size of the file's run's because it is + + * awkward to decode runs in chunks. This should be done in the future + + * in order to reduce the memory use of these routines. + + */ + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imRasReadRLE1( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb **pVfb ) + +#else + +imRasReadRLE1( ioType, fd, fp, flagsTable, tagTable, pVfb ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + + ImVfb **pVfb; /* VFB to fill and return */ + +#endif + +{ + + unsigned char *rbp; /* Run buffer pointer */ + + ImVfb *vfb; /* New vfb */ + + ImVfbPtr pptr; /* Pixel pointer */ + + unsigned char color; /* Color */ + + int count; /* Run-length encodeing count */ + + int n; /* # of bytes in image */ + + unsigned char *runBuffer; /* Run buffer */ + + unsigned char *endrbp; /* Ending rbp pointer value */ + + int nPixels; /* # of pixels so far in scanline*/ + + int byte; /* Byte of pixels */ + + int pad; /* Pad scanline? */ + + long fileOffset; /* Location in file */ + + + + + + /* + + * Allocate a VFB of the required size. + + */ + + if ( (*pVfb = ImVfbAlloc( imRasHeader.ras_width, imRasHeader.ras_height, + + IMVFBMONO )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + vfb = *pVfb; + + + + + + /* + + * The imRasHeader.ras_length field of the file header is supposed + + * to be set to the number of bytes of image data in the file. + + * Unfortunately, this has been interpreted to mean several different + + * things by different people: + + * + + * - height * width, even when compressed. + + * - the actual number of compressed bytes. + + * - zero. + + * - a number of variants on these. + + * + + * This makes the ras_length field an undependable value. + + * + + * So, we'll ignore it. We seek to the end of the file and see + + * how many bytes there are from here to there, then read them all in. + + * This is a potential big waste of memory, but there's little we + + * can safely do about it. + + */ + + fileOffset = ImTell( ioType, fd, fp ); + + ImSeek( ioType, fd, fp, 0, L_XTND ); + + n = ImTell( ioType, fd, fp ) - fileOffset; + + ImSeek( ioType, fd, fp, fileOffset, L_SET ); + + + + + + /* + + * Allocate a buffer big enough for all those bytes. + + */ + + pptr = ImVfbQFirst( vfb ); + + ImMalloc( runBuffer, unsigned char *, sizeof( unsigned char ) * n ); + + rbp = runBuffer; + + + + + + /* + + * And read them in. + + */ + + if ( (n = ImBinRead( ioType, fd, fp, runBuffer, UCHAR, 1, n )) == -1 ) + + { + + free( (char *)runBuffer ); + + ImVfbFree( vfb ); + + ImReturnBinError( ); + + } + + + + endrbp = &runBuffer[n-1]; + + nPixels = 0; + + pad = ((int)((imRasHeader.ras_width + 7)/ 8) & 0x01) ? TRUE : FALSE; + + while ( rbp <= endrbp ) + + { + + color = *rbp++; + + count = 1; + + if ( color == IMRASESC && (count += *rbp++) != 1 ) + + color = *rbp++; + + + + while ( count-- != 0 ) + + { + + if ( nPixels >= imRasHeader.ras_width ) + + { + + /* End of scan line. */ + + nPixels = 0; + + if ( pad ) + + continue; /* Skip pixel */ + + } + + + + /* + + * Expand byte into up to 8 pixels. + + */ + + if ( (nPixels + 8) <= imRasHeader.ras_width ) + + { + + /* Expand full byte. Unwrap loop for speed.*/ + + ImVfbSMono( vfb, pptr, (color&0x80) ? 0 : 1 ); + + ImVfbSInc( vfb, pptr ); + + ImVfbSMono( vfb, pptr, (color&0x40) ? 0 : 1 ); + + ImVfbSInc( vfb, pptr ); + + ImVfbSMono( vfb, pptr, (color&0x20) ? 0 : 1 ); + + ImVfbSInc( vfb, pptr ); + + ImVfbSMono( vfb, pptr, (color&0x10) ? 0 : 1 ); + + ImVfbSInc( vfb, pptr ); + + ImVfbSMono( vfb, pptr, (color&0x08) ? 0 : 1 ); + + ImVfbSInc( vfb, pptr ); + + ImVfbSMono( vfb, pptr, (color&0x04) ? 0 : 1 ); + + ImVfbSInc( vfb, pptr ); + + ImVfbSMono( vfb, pptr, (color&0x02) ? 0 : 1 ); + + ImVfbSInc( vfb, pptr ); + + ImVfbSMono( vfb, pptr, (color&0x01) ? 0 : 1 ); + + ImVfbSInc( vfb, pptr ); + + nPixels += 8; + + continue; + + } + + + + /* Expand part of byte. Unwrap loop for speed. */ + + byte = color; + + switch ( imRasHeader.ras_width - nPixels ) + + { + + case 7: ImVfbSMono( vfb, pptr, (byte&0x80) ? 0 : 1 ); + + ImVfbSInc( vfb, pptr ); + + byte <<= 1; + + case 6: ImVfbSMono( vfb, pptr, (byte&0x80) ? 0 : 1 ); + + ImVfbSInc( vfb, pptr ); + + byte <<= 1; + + case 5: ImVfbSMono( vfb, pptr, (byte&0x80) ? 0 : 1 ); + + ImVfbSInc( vfb, pptr ); + + byte <<= 1; + + case 4: ImVfbSMono( vfb, pptr, (byte&0x80) ? 0 : 1 ); + + ImVfbSInc( vfb, pptr ); + + byte <<= 1; + + case 3: ImVfbSMono( vfb, pptr, (byte&0x80) ? 0 : 1 ); + + ImVfbSInc( vfb, pptr ); + + byte <<= 1; + + case 2: ImVfbSMono( vfb, pptr, (byte&0x80) ? 0 : 1 ); + + ImVfbSInc( vfb, pptr ); + + byte <<= 1; + + case 1: ImVfbSMono( vfb, pptr, (byte&0x80) ? 0 : 1 ); + + ImVfbSInc( vfb, pptr ); + + byte <<= 1; + + } + + nPixels = imRasHeader.ras_width; + + } + + } + + + + free( (char *)runBuffer ); + + return ( 0 ); + +} + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imRasReadRLE8( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb **pVfb ) + +#else + +imRasReadRLE8( ioType, fd, fp, flagsTable, tagTable, pVfb ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + + ImVfb **pVfb; /* VFB to fill and return */ + +#endif + +{ + + unsigned char *rbp; /* Run buffer pointer */ + + ImVfb *vfb; /* New vfb */ + + ImVfbPtr pptr; /* Pixel pointer */ + + int n; /* # of bytes in image */ + + unsigned char *runBuffer; /* Run buffer */ + + unsigned char color; /* Color */ + + int count; /* Run-length encodeing count */ + + unsigned char *endrbp; /* Ending rbp pointer value */ + + int nPixels; /* # of pixels in scanline */ + + long fileOffset; /* Location in file */ + + + + + + /* + + * Allocate a VFB of the required size. + + */ + + if ( (*pVfb = ImVfbAlloc( imRasHeader.ras_width, imRasHeader.ras_height, + + IMVFBINDEX8 )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + vfb = *pVfb; + + + + + + /* + + * The imRasHeader.ras_length field of the file header is supposed + + * to be set to the number of bytes of image data in the file. + + * Unfortunately, this has been interpreted to mean several different + + * things by different people: + + * + + * - height * width, even when compressed. + + * - the actual number of compressed bytes. + + * - zero. + + * - a number of variants on these. + + * + + * This makes the ras_length field an undependable value. + + * + + * So, we'll ignore it. We seek to the end of the file and see + + * how many bytes there are from here to there, then read them all in. + + * This is a potential big waste of memory, but there's little we + + * can safely do about it. + + */ + + fileOffset = ImTell( ioType, fd, fp ); + + ImSeek( ioType, fd, fp, 0, L_XTND ); + + n = ImTell( ioType, fd, fp ) - fileOffset; + + ImSeek( ioType, fd, fp, fileOffset, L_SET ); + + + + + + /* + + * Allocate a buffer big enough for all those bytes. + + */ + + pptr = ImVfbQFirst( vfb ); + + ImMalloc( runBuffer, unsigned char *, sizeof( unsigned char ) * n ); + + rbp = runBuffer; + + + + /* + + * And read them in. + + */ + + if ( (n = ImBinRead( ioType, fd, fp, runBuffer, UCHAR, 1, n )) == -1 ) + + { + + free( (char *)runBuffer ); + + ImVfbFree( vfb ); + + ImReturnBinError( ); + + } + + + + nPixels = 0; + + endrbp = &runBuffer[n-1]; + + while ( rbp <= endrbp ) + + { + + color = *rbp++; + + count = 1; + + if ( color == IMRASESC && (count += *rbp++) != 1 ) + + color = *rbp++; + + + + if ( (imRasHeader.ras_width & 0x01) == 0 ) + + { + + /* Even width. No pad bytes. */ + + while ( count-- != 0 ) + + { + + ImVfbSIndex8( vfb, pptr, color ); + + ImVfbSInc( vfb, pptr ); + + } + + continue; + + } + + + + /* Odd width. Skip pad byte after every 'width' pixels.*/ + + while ( count-- != 0 ) + + { + + if ( nPixels >= imRasHeader.ras_width ) + + { + + nPixels = 0; + + continue; /* Skip this pixel. */ + + } + + ImVfbSIndex8( vfb, pptr, color ); + + ImVfbSInc( vfb, pptr ); + + nPixels++; + + } + + } + + + + free( (char *)runBuffer ); + + return ( 0 ); + +} + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imRasReadRLE24RGB( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb **pVfb ) + +#else + +imRasReadRLE24RGB( ioType, fd, fp, flagsTable, tagTable, pVfb ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + + ImVfb **pVfb; /* VFB to fill and return */ + +#endif + +{ + + unsigned char *rbp; /* Run buffer pointer */ + + ImVfb *vfb; /* New vfb */ + + ImVfbPtr pptr; /* Pixel pointer */ + + int n; /* # of bytes in image */ + + unsigned char *runBuffer; /* Run buffer */ + + unsigned char color; /* Color */ + + int count; /* Run-length encodeing count */ + + unsigned char *endrbp; /* Ending rbp pointer value */ + + int onRGB; /* RGB color cycle counter */ + + int nPixels; /* # of pixels in scanline */ + + long fileOffset; /* Location in file */ + + + + + + /* + + * Allocate a VFB of the required size. + + */ + + if ( (*pVfb = ImVfbAlloc( imRasHeader.ras_width, imRasHeader.ras_height, + + IMVFBRGB )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + vfb = *pVfb; + + + + + + /* + + * The imRasHeader.ras_length field of the file header is supposed + + * to be set to the number of bytes of image data in the file. + + * Unfortunately, this has been interpreted to mean several different + + * things by different people: + + * + + * - height * width, even when compressed. + + * - the actual number of compressed bytes. + + * - zero. + + * - a number of variants on these. + + * + + * This makes the ras_length field an undependable value. + + * + + * So, we'll ignore it. We seek to the end of the file and see + + * how many bytes there are from here to there, then read them all in. + + * This is a potential big waste of memory, but there's little we + + * can safely do about it. + + */ + + fileOffset = ImTell( ioType, fd, fp ); + + ImSeek( ioType, fd, fp, 0, L_XTND ); + + n = ImTell( ioType, fd, fp ) - fileOffset; + + ImSeek( ioType, fd, fp, fileOffset, L_SET ); + + + + + + /* + + * Allocate a buffer big enough for all those bytes. + + */ + + pptr = ImVfbQFirst( vfb ); + + ImMalloc( runBuffer, unsigned char *, sizeof( unsigned char ) * n ); + + rbp = runBuffer; + + + + /* + + * And read them in. + + */ + + if ( (n = ImBinRead( ioType, fd, fp, runBuffer, UCHAR, 1, n )) == -1 ) + + { + + free( (char *)runBuffer ); + + ImVfbFree( vfb ); + + ImReturnBinError( ); + + } + + + + nPixels = 0; + + endrbp = &runBuffer[n-1]; + + onRGB = IMRAS_BLUE; + + while ( rbp <= endrbp ) + + { + + color = *rbp++; + + count = 1; + + if ( color == IMRASESC && (count += *rbp++) != 1 ) + + color = *rbp++; + + + + if ( (imRasHeader.ras_width & 0x01) == 0 ) + + { + + /* Even image width. No padding needed. */ + + while ( count-- != 0 ) + + { + + switch ( onRGB ) + + { + + case IMRAS_BLUE: ImVfbSBlue( vfb, pptr, color ); + + onRGB = IMRAS_GREEN; + + continue; + + case IMRAS_GREEN: ImVfbSGreen( vfb, pptr, color ); + + onRGB = IMRAS_RED; + + continue; + + case IMRAS_RED: ImVfbSRed( vfb, pptr, color ); + + ImVfbSInc( vfb, pptr ); + + nPixels++; + + onRGB = IMRAS_BLUE; + + continue; + + } + + } + + continue; + + } + + + + /* Odd image width. Pad 1 byte per scanline. */ + + while ( count-- != 0 ) + + { + + if ( nPixels >= imRasHeader.ras_width ) + + { + + nPixels = 0; + + continue; /* Skip this byte. */ + + } + + switch ( onRGB ) + + { + + case IMRAS_BLUE: ImVfbSBlue( vfb, pptr, color ); + + onRGB = IMRAS_GREEN; + + continue; + + case IMRAS_GREEN: ImVfbSGreen( vfb, pptr, color ); + + onRGB = IMRAS_RED; + + continue; + + case IMRAS_RED: ImVfbSRed( vfb, pptr, color ); + + ImVfbSInc( vfb, pptr ); + + nPixels++; + + onRGB = IMRAS_BLUE; + + continue; + + } + + } + + } + + + + free( (char *)runBuffer ); + + return ( 0 ); + +} + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imRasReadRLE32RGBA( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable, ImVfb **pVfb ) + +#else + +imRasReadRLE32RGBA( ioType, fd, fp, flagsTable, tagTable, pVfb ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + + ImVfb **pVfb; /* VFB to fill and return */ + +#endif + +{ + + unsigned char *rbp; /* Run buffer pointer */ + + ImVfb *vfb; /* New vfb */ + + ImVfbPtr pptr; /* Pixel pointer */ + + int n; /* # of bytes in image */ + + unsigned char *runBuffer; /* Run buffer */ + + unsigned char color; /* Color */ + + int count; /* Run-length encodeing count */ + + unsigned char *endrbp; /* Ending rbp pointer value */ + + int onRGB; /* RGB color cycle counter */ + + int nPixels; /* # of pixels in scanline */ + + long fileOffset; /* Location in file */ + + + + + + /* + + * Allocate a VFB of the required size. + + */ + + if ( (*pVfb = ImVfbAlloc( imRasHeader.ras_width, imRasHeader.ras_height, + + IMVFBRGB | IMVFBALPHA )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + vfb = *pVfb; + + + + + + /* + + * The imRasHeader.ras_length field of the file header is supposed + + * to be set to the number of bytes of image data in the file. + + * Unfortunately, this has been interpreted to mean several different + + * things by different people: + + * + + * - height * width, even when compressed. + + * - the actual number of compressed bytes. + + * - zero. + + * - a number of variants on these. + + * + + * This makes the ras_length field an undependable value. + + * + + * So, we'll ignore it. We seek to the end of the file and see + + * how many bytes there are from here to there, then read them all in. + + * This is a potential big waste of memory, but there's little we + + * can safely do about it. + + */ + + fileOffset = ImTell( ioType, fd, fp ); + + ImSeek( ioType, fd, fp, 0, L_XTND ); + + n = ImTell( ioType, fd, fp ) - fileOffset; + + ImSeek( ioType, fd, fp, fileOffset, L_SET ); + + + + + + /* + + * Allocate a buffer big enough for all those bytes. + + */ + + pptr = ImVfbQFirst( vfb ); + + ImMalloc( runBuffer, unsigned char *, sizeof( unsigned char ) * n ); + + rbp = runBuffer; + + + + /* + + * And read them in. + + */ + + if ( (n = ImBinRead( ioType, fd, fp, runBuffer, UCHAR, 1, n )) == -1 ) + + { + + free( (char *)runBuffer ); + + ImVfbFree( vfb ); + + ImReturnBinError( ); + + } + + + + nPixels = 0; + + endrbp = &runBuffer[n-1]; + + onRGB = IMRAS_ALPHA; + + while ( rbp <= endrbp ) + + { + + color = *rbp++; + + count = 1; + + if ( color == IMRASESC && (count += *rbp++) != 1 ) + + color = *rbp++; + + + + while ( count-- != 0 ) + + { + + switch ( onRGB ) + + { + + case IMRAS_ALPHA: ImVfbSAlpha( vfb, pptr, color ); + + onRGB = IMRAS_BLUE; + + continue; + + case IMRAS_BLUE: ImVfbSBlue( vfb, pptr, color ); + + onRGB = IMRAS_GREEN; + + continue; + + case IMRAS_GREEN: ImVfbSGreen( vfb, pptr, color ); + + onRGB = IMRAS_RED; + + continue; + + case IMRAS_RED: ImVfbSRed( vfb, pptr, color ); + + ImVfbSInc( vfb, pptr ); + + nPixels++; + + onRGB = IMRAS_ALPHA; + + continue; + + } + + } + + } + + + + free( (char *)runBuffer ); + + return ( 0 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imRasWriteHeaderClt - write header and CLT + + * + + * DESCRIPTION + + * A RAS file header is intialized and written out. If a CLT is to + + * be written out, it is converted to a CLT buffer, then written + + * following the header. + + */ + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imRasWriteHeaderClt( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, ImVfb *vfb, int depth, int type ) + +#else + +imRasWriteHeaderClt( pMap, ioType, fd, fp, vfb, depth, type ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + ImVfb *vfb; /* VFB to write out */ + + int depth; /* Image depth */ + + int type; /* Image type */ + +#endif + +{ + + unsigned char *cltBuffer; /* Converted CLT */ + + int nClt; /* # of CLT entries */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * Set up the basic header. + + */ + + imRasHeader.ras_magic = IMRASMAGIC; + + imRasHeader.ras_width = ImVfbQWidth( vfb ); + + imRasHeader.ras_height = ImVfbQHeight( vfb ); + + imRasHeader.ras_depth = depth; + + imRasHeader.ras_type = type; + + imRasHeader.ras_length = imRasHeader.ras_width * + + imRasHeader.ras_height * depth / 8; + + if ( (imRasHeader.ras_width * depth / 8) % 2 != 0 ) + + { + + /* Pad each scanline to 2-byte boundary. */ + + imRasHeader.ras_length += imRasHeader.ras_height * depth / 8; + + } + + + + + + /* + + * If we've been requested to write the CLT, convert it to a tmp + + * buffer first. + + */ + + nClt = 0; + + if ( (pMap->map_outAttributes & IMCLTYES) && + + imRasCltToBuffer( vfb, &cltBuffer, &nClt ) == -1) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + /* + + * Output -verbose message + + */ + + ImInfo( "Byte Order", "Most Significant Byte First" ); + + + + sprintf( message, "%d x %d", ImVfbQWidth( vfb ), ImVfbQHeight( vfb ) ); + + ImInfo( "Resolution", message ); + + + + if( depth >= 24 ) + + sprintf( message, "%d-bit RGB", depth ); + + else + + sprintf( message, "%d-bit Color Indexed", depth ); + + ImInfo( "Type", message ); + + + + if( nClt == 0 ) + + sprintf( message, "none" ); + + else + + sprintf( message, "%d Entries", nClt ); + + ImInfo( "Color Table", message ); + + + + if( type == IMRTBYTEENCODED ) + + { + + ImInfo("Compression Type","Run Length Encoded (Byte Encoded)"); + + } + + else + + ImInfo( "Compression Type", "none (Standard)" ); + + + + if( depth > 24 ) + + { + + ImInfo( "Alpha Channel", "8-bit" ); + + } + + else + + ImInfo( "Alpha Channel", "none" ); + + + + /* + + * If there's no CLT to write out, just write the header. Otherwise + + * write the header followed by the CLT tmp buffer. + + */ + + if ( nClt == 0 ) + + { + + imRasHeader.ras_maplength = 0; + + imRasHeader.ras_maptype = IMRMTNONE; + + if ( ImBinWriteStruct( ioType, fd, fp, &imRasHeader, + + imRasHeaderFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + return ( 0 ); + + } + + + + imRasHeader.ras_maplength = nClt * 3; + + imRasHeader.ras_maptype = IMRMTEQUALRGB; + + if ( ImBinWriteStruct( ioType, fd, fp, &imRasHeader, + + imRasHeaderFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + if ( ImBinWrite( ioType, fd, fp, cltBuffer, UCHAR, 1, + + imRasHeader.ras_maplength ) == -1 ) + + { + + free( (char *)cltBuffer ); + + ImReturnBinError( ); + + } + + + + free( (char *)cltBuffer ); + + return ( 0 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imRasCltToBuffer - convert CLT to file format buffer + + * + + * DESCRIPTION + + * The CLT for the given VFB is converted into a CLT buffer for later + + * writing out to a RAS image file. + + * + + * Since RAS CLT's are not a fixed size, we just have to convert + + * the actual entries in our CLT and not worry about padding the + + * convert buffer with dummy CLT entries. + + */ + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imRasCltToBuffer( ImVfb *vfb, unsigned char **cltBuffer, int *nClt ) + +#else + +imRasCltToBuffer( vfb, cltBuffer, nClt ) + + ImVfb *vfb; /* VFB to check */ + + unsigned char **cltBuffer; /* Returned buffer */ + + int *nClt; /* Returned # of CLT entries */ + +#endif + +{ + + ImCltPtr cptr; /* CLT pointer */ + + unsigned char *redp; /* Red CLT range pointer */ + + unsigned char *greenp; /* Green CLT range pointer */ + + unsigned char *bluep; /* Blue CLT range pointer */ + + ImClt *clt; /* Colormap */ + + int i; /* Counter */ + + int n; /* Number of CLT entries */ + + + + + + /* Get the VFB's CLT (if any). */ + + clt = ImVfbQClt( vfb ); + + if ( clt == IMCLTNULL ) + + { + + /* Isn't one. Never mind. */ + + *nClt = 0; + + *cltBuffer = NULL; + + return ( 0 ); /* No CLT, but that's OK. */ + + } + + + + *nClt = n = ImCltQNColors( clt ); + + ImMalloc( *cltBuffer, unsigned char *, sizeof( unsigned char ) * n * 3 ); + + + + redp = *cltBuffer; + + greenp = *cltBuffer + n; + + bluep = *cltBuffer + n + n; + + cptr = ImCltQFirst( clt ); + + for ( i = 0; i < n; i++ ) + + { + + *redp++ = ImCltQRed( cptr ); + + *greenp++ = ImCltQGreen( cptr ); + + *bluep++ = ImCltQBlue( cptr ); + + ImCltSInc( clt, cptr ); + + } + + + + return ( 0 ); /* CLT came from VFB */ + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imRasWrite1 - write 1-bit standard RAS format + + * imRasWrite8 - write 8-bit standard RAS format + + * imRasWriteRGB - write 24-bit RGB standard RAS format + + * imRasWriteRGBA - write 32-bit RGBA standard RAS format + + * + + * DESCRIPTION + + * Each of these routines deal with "standard" format RAS files + + * (ie., no run-length encoding). The output stream may be a pipe + + * or a file. We don't care. + + * + + * The header and CLT (if any) are written out. The VFB is then + + * converted and written out one scanline at a time. Scanlines + + * are always padded to 2-byte boundaries. + + */ + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imRasWrite1( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imRasWrite1( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + int i, j; /* Counters */ + + ImVfbPtr pptr; /* Pixel pointer */ + + unsigned char *rbp; /* Run buffer pointer */ + + ImVfb *vfb; /* VFB to write out */ + + unsigned char *runBuffer; /* Run buffer */ + + int n; /* # of bytes to write */ + + unsigned char byte; /* New byte of pixels */ + + + + + + /* + + * Write out the header and possible CLT. + + */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + BinByteOrder( BINMBF ); + + if ( imRasWriteHeaderClt( pMap, ioType, fd, fp, vfb, 1, + + IMRTSTANDARD ) == -1 ) + + return ( -1 ); /* Error already handled */ + + + + + + /* + + * Convert and write out the image. + + */ + + n = (imRasHeader.ras_width + 7) / 8; /* # bytes */ + + if ( (n&1) != 0 ) + + n++; /* Pad to 2-byte boundary*/ + + ImMalloc( runBuffer, unsigned char *, sizeof( unsigned char ) * n ); + + runBuffer[n-1] = 0; /* Set the pad byte */ + + pptr = ImVfbQFirst( vfb ); + + + + for ( i = 0; i < imRasHeader.ras_height; i++ ) + + { + + rbp = runBuffer; + + byte = 0; + + for ( j = 0; j < imRasHeader.ras_width; j++ ) + + { + + byte <<= 1; + + if ( ImVfbQMono( vfb, pptr ) == 0 ) + + byte |= 1; + + ImVfbSInc( vfb, pptr ); + + if ( (j&0x7) == 0x7 ) + + { + + *rbp++ = byte; + + byte = 0; + + } + + } + + if ( (j&0x7) != 0 ) + + *rbp++ = byte; + + + + if ( ImBinWrite( ioType, fd, fp, runBuffer, UCHAR, 1, n ) == -1) + + { + + free( (unsigned char *)runBuffer ); + + ImReturnBinError( ); + + } + + } + + free( (unsigned char *)runBuffer ); + + + + return ( 1 ); + +} + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imRasWrite8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imRasWrite8( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + int i, j; /* Counters */ + + ImVfbPtr pptr; /* Pixel pointer */ + + unsigned char *rbp; /* Run buffer pointer */ + + ImVfb *vfb; /* VFB to write out */ + + unsigned char *runBuffer; /* Run buffer */ + + int n; /* # of bytes to write */ + + + + + + /* + + * Write out the header and possible CLT. + + */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + BinByteOrder( BINMBF ); + + if ( imRasWriteHeaderClt( pMap, ioType, fd, fp, vfb, 8, + + IMRTSTANDARD ) == -1 ) + + return ( -1 ); /* Error already handled */ + + + + + + /* + + * Convert and write out the image. + + */ + + n = imRasHeader.ras_width * imRasHeader.ras_depth / 8;/* # bytes*/ + + if ( (n&1) != 0 ) + + n++; /* Pad to 2-byte boundary*/ + + ImMalloc( runBuffer, unsigned char *, sizeof( unsigned char ) * n ); + + runBuffer[n-1] = 0; /* Set the pad byte */ + + pptr = ImVfbQFirst( vfb ); + + + + for ( i = 0; i < imRasHeader.ras_height; i++ ) + + { + + rbp = runBuffer; + + for ( j = 0; j < imRasHeader.ras_width; j++ ) + + { + + *rbp++ = ImVfbQIndex8( vfb, pptr ); + + ImVfbSInc( vfb, pptr ); + + } + + + + if ( ImBinWrite( ioType, fd, fp, runBuffer, UCHAR, 1, n ) == -1) + + { + + free( (unsigned char *)runBuffer ); + + ImReturnBinError( ); + + } + + } + + free( (unsigned char *)runBuffer ); + + + + return ( 1 ); + +} + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imRasWriteRGB( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imRasWriteRGB( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + int i, j; /* Counters */ + + ImVfbPtr pptr; /* Pixel pointer */ + + unsigned char *rbp; /* Run buffer pointer */ + + ImVfb *vfb; /* VFB to write out */ + + unsigned char *runBuffer; /* Run buffer */ + + int n; /* # of bytes to write */ + + + + + + /* + + * Write out the header and possible CLT. + + */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + BinByteOrder( BINMBF ); + + if ( imRasWriteHeaderClt( pMap, ioType, fd, fp, vfb, 24, + + IMRTSTANDARD ) == -1 ) + + return ( -1 ); /* Error already handled */ + + + + + + /* + + * Convert and write out the image. + + */ + + n = imRasHeader.ras_width * imRasHeader.ras_depth / 8; + + if ( (n&1) != 0 ) + + n++; /* Pad to 2-byte boundary*/ + + ImMalloc( runBuffer, unsigned char *, sizeof( unsigned char ) * n ); + + runBuffer[n-1] = 0; /* Set the pad byte */ + + pptr = ImVfbQFirst( vfb ); + + + + for ( i = 0; i < imRasHeader.ras_height; i++ ) + + { + + rbp = runBuffer; + + for ( j = 0; j < imRasHeader.ras_width; j++ ) + + { + + *rbp++ = ImVfbQBlue( vfb, pptr ); + + *rbp++ = ImVfbQGreen( vfb, pptr ); + + *rbp++ = ImVfbQRed( vfb, pptr ); + + ImVfbSInc( vfb, pptr ); + + } + + + + if ( ImBinWrite( ioType, fd, fp, runBuffer, UCHAR, 1, n ) == -1) + + { + + free( (unsigned char *)runBuffer ); + + ImReturnBinError( ); + + } + + } + + free( (unsigned char *)runBuffer ); + + + + return ( 1 ); + +} + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imRasWriteRGBA( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imRasWriteRGBA( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + int i, j; /* Counters */ + + ImVfbPtr pptr; /* Pixel pointer */ + + unsigned char *rbp; /* Run buffer pointer */ + + ImVfb *vfb; /* VFB to write out */ + + unsigned char *runBuffer; /* Run buffer */ + + int n; /* # of bytes to write */ + + + + + + /* + + * Write out the header and possible CLT. + + */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + BinByteOrder( BINMBF ); + + if ( imRasWriteHeaderClt( pMap, ioType, fd, fp, vfb, 32, + + IMRTSTANDARD ) == -1 ) + + return ( -1 ); /* Error already handled */ + + + + + + /* + + * Convert and write out the image. + + */ + + n = imRasHeader.ras_width * imRasHeader.ras_depth / 8; + + if ( (n&1) != 0 ) + + n++; /* Pad to 2-byte boundary*/ + + ImMalloc( runBuffer, unsigned char *, sizeof( unsigned char ) * n ); + + runBuffer[n-1] = 0; /* Set the pad byte */ + + pptr = ImVfbQFirst( vfb ); + + + + for ( i = 0; i < imRasHeader.ras_height; i++ ) + + { + + rbp = runBuffer; + + for ( j = 0; j < imRasHeader.ras_width; j++ ) + + { + + *rbp++ = ImVfbQAlpha( vfb, pptr ); + + *rbp++ = ImVfbQBlue( vfb, pptr ); + + *rbp++ = ImVfbQGreen( vfb, pptr ); + + *rbp++ = ImVfbQRed( vfb, pptr ); + + ImVfbSInc( vfb, pptr ); + + } + + + + if ( ImBinWrite( ioType, fd, fp, runBuffer, UCHAR, 1, n ) == -1) + + { + + free( (unsigned char *)runBuffer ); + + ImReturnBinError( ); + + } + + } + + free( (unsigned char *)runBuffer ); + + + + return ( 1 ); + +} + + + + + + + + + + + +/* + + * MACROS + + * IMAddRun - add another pixel to RLE buffer + + * IMDumpRun - dump an RLE run into the run buffer + + * + + * DESCRIPTION + + * The 'index' pixel is added to the caller's run-length encoding + + * buffer. This involves checking if it differs from 'oldindex' + + * (the previous index value). + + * + + * If not, the run count is incremented. However, if it reaches 256, + + * we have to stop the run, copy it into the outgoing buffer, and + + * start a new run. + + * + + * If 'index' isn't the same as the current run index, then the run + + * is over. If it was a run of ESC values, the run is dumped as a + + * list of ESC,0 pairs. If the run was a count of 1, then the pixel + + * is dumped into the run buffer raw. Otherwise a run is dumped to + + * the run buffer. + + */ + + + +#define IMAddRun(count,oldindex,index,rbp,nBytes) \ +{ \ + if ( (index) == (oldindex) ) \ + { \ + if ( ++(count) != 256 ) \ + continue; \ + /* Overflow. Dump run. */\ + IMDumpRun( count, oldindex, index, rbp, nBytes ); \ + (count) = 0; \ + } \ + else \ + { \ + /* End of run. Dump run. */\ + IMDumpRun( count, oldindex, index, rbp, nBytes ); \ + (oldindex) = (index); \ + (count) = 1; \ + } \ +} + + + +#define IMDumpRun( count, oldindex, index, rbp, nBytes ) \ + if ( (count) != 0 ) \ + { \ + if ( (oldindex) == IMRASESC ) \ + { \ + while ( (count)-- ) \ + { \ + *(rbp)++ = IMRASESC; \ + *(rbp)++ = 0; \ + (nBytes) += 2; \ + } \ + } \ + else if ( count == 1 ) \ + { \ + /* Use the raw format. */\ + *(rbp)++ = (oldindex); \ + (nBytes)++; \ + } \ + else \ + { \ + /* Use count format. */\ + *(rbp)++ = IMRASESC; \ + *(rbp)++ = (count) - 1; \ + *(rbp)++ = (oldindex); \ + (nBytes)+= 3; \ + } \ + } + + + + + + + + + + + +/* + + * FUNCTION + + * imRasWriteRLE1 - write 1-bit encoded RAS format + + * imRasWriteRLE8 - write 8-bit encoded RAS format + + * imRasWriteRLERGB - write 24-bit RGB encoded RAS format + + * imRasWriteRLERGBA - write 32-bit RGBA encoded RAS format + + * + + * DESCRIPTION + + * Each of these routines deal with "encoded" format RAS files. + + * The output stream is a file. + + * + + * The header and CLT (if any) are written out. The VFB is then + + * converted and written out in chunks. When we are all done and + + * know how big the encoded image was, we seek back and fill in + + * the length field of the header. + + */ + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imRasWriteRLE1( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imRasWriteRLE1( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + int i, j, k; /* Counters */ + + ImVfbPtr pptr; /* Pixel pointer */ + + unsigned char *rbp; /* Run buffer pointer */ + + ImVfb *vfb; /* VFB to write out */ + + unsigned char *runBuffer; /* Run buffer */ + + int n; /* # of bytes to write */ + + int nBytes; /* # of bytes to write */ + + int count; /* Run length count */ + + int index; /* Run index */ + + int oldindex; /* Prevous run index */ + + int width; /* Width in bytes */ + + + + + + /* + + * Write out the header and possible CLT. + + */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + BinByteOrder( BINMBF ); + + if ( imRasWriteHeaderClt( pMap, ioType, fd, fp, vfb, 1, + + IMRTBYTEENCODED ) == -1 ) + + return ( -1 ); /* Error already handled */ + + + + + + /* + + * Convert and write out the image. Maximum run buffer is twice + + * size of one scanline. + + */ + + width = (imRasHeader.ras_width + 7) / 8; + + n = 2 * width; + + ImMalloc( runBuffer, unsigned char *, sizeof( unsigned char ) * n ); + + pptr = ImVfbQFirst( vfb ); + + imRasHeader.ras_length = 0; + + + + + + for ( i = 0; i < imRasHeader.ras_height; i++ ) + + { + + rbp = runBuffer; + + nBytes = 0; + + count = 1; + + + + if ( imRasHeader.ras_width < 8 ) + + { + + for ( oldindex = 0, k = 0; k < imRasHeader.ras_width; k++ ) + + { + + oldindex <<= 1; + + if ( ImVfbQMono( vfb, pptr ) == 0 ) + + oldindex |= 1; + + ImVfbSInc( vfb, pptr ); + + } + + oldindex <<= (8-k); + + } + + else + + { + + for ( oldindex = 0, k = 0; k < 8; k++ ) + + { + + oldindex <<= 1; + + if ( ImVfbQMono( vfb, pptr ) == 0 ) + + oldindex |= 1; + + ImVfbSInc( vfb, pptr ); + + } + + } + + + + for ( j = k; j < imRasHeader.ras_width; ) + + { + + if ( imRasHeader.ras_width < (j+8) ) + + { + + for ( index = 0, k = 0; j < imRasHeader.ras_width; j++, k++ ) + + { + + index <<= 1; + + if ( ImVfbQMono( vfb, pptr ) == 0 ) + + index |= 1; + + ImVfbSInc( vfb, pptr ); + + } + + index <<= (8-k); + + } + + else + + { + + for ( index = 0, k = 0; k < 8; k++ ) + + { + + index <<= 1; + + if ( ImVfbQMono( vfb, pptr ) == 0 ) + + index |= 1; + + ImVfbSInc( vfb, pptr ); + + } + + j += 8; + + } + + IMAddRun( count, oldindex, index, rbp, nBytes ); + + } + + + + /* Dump last run. */ + + IMDumpRun( count, oldindex, index, rbp, nBytes ); + + if ( width & 0x1 ) + + { + + nBytes++; /* Strange 2-byte padding*/ + + *rbp++ = 0; + + } + + if ( ImBinWrite( ioType, fd, fp, runBuffer, UCHAR, 1, nBytes ) == -1 ) + + { + + free( (unsigned char *)runBuffer ); + + ImReturnBinError( ); + + } + + imRasHeader.ras_length += nBytes; + + } + + free( (unsigned char *)runBuffer ); + + + + + + /* + + * Seek back to the head of the file and rewrite the header. + + */ + + ImSeek( ioType, fd, fp, 0, 0 ); + + if ( ImBinWriteStruct( ioType, fd, fp, &imRasHeader, + + imRasHeaderFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + return ( 1 ); + +} + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imRasWriteRLE8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imRasWriteRLE8( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + int i, j; /* Counters */ + + ImVfbPtr pptr; /* Pixel pointer */ + + unsigned char *rbp; /* Run buffer pointer */ + + ImVfb *vfb; /* VFB to write out */ + + unsigned char *runBuffer; /* Run buffer */ + + int n; /* # of bytes to write */ + + int nBytes; /* # of bytes to write */ + + int count; /* Run length count */ + + int index; /* Run index */ + + int oldindex; /* Prevous run index */ + + + + + + /* + + * Write out the header and possible CLT. + + */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + BinByteOrder( BINMBF ); + + if ( imRasWriteHeaderClt( pMap, ioType, fd, fp, vfb, 8, + + IMRTBYTEENCODED ) == -1 ) + + return ( -1 ); /* Error already handled */ + + + + + + /* + + * Convert and write out the image. Maximum run buffer is twice + + * size of one scanline. + + */ + + n = 2 * imRasHeader.ras_width * imRasHeader.ras_depth / 8; + + ImMalloc( runBuffer, unsigned char *, sizeof( unsigned char ) * n ); + + pptr = ImVfbQFirst( vfb ); + + imRasHeader.ras_length = 0; + + + + for ( i = 0; i < imRasHeader.ras_height; i++ ) + + { + + rbp = runBuffer; + + nBytes = 0; + + count = 1; + + + + oldindex = ImVfbQIndex8( vfb, pptr ); + + ImVfbSInc( vfb, pptr ); + + + + for ( j = 1; j < imRasHeader.ras_width; j++ ) + + { + + index = ImVfbQIndex8( vfb, pptr ); + + ImVfbSInc( vfb, pptr ); + + IMAddRun( count, oldindex, index, rbp, nBytes ); + + } + + + + /* Dump last run. */ + + IMDumpRun( count, oldindex, index, rbp, nBytes ); + + if ( imRasHeader.ras_width & 0x1 ) + + { + + nBytes++; /* Strange 2-byte padding*/ + + *rbp++ = 0; + + } + + if ( ImBinWrite( ioType, fd, fp, runBuffer, UCHAR, 1, nBytes ) == -1 ) + + { + + free( (unsigned char *)runBuffer ); + + ImReturnBinError( ); + + } + + imRasHeader.ras_length += nBytes; + + } + + free( (unsigned char *)runBuffer ); + + + + + + /* + + * Seek back to the head of the file and rewrite the header. + + */ + + ImSeek( ioType, fd, fp, 0, 0 ); + + if ( ImBinWriteStruct( ioType, fd, fp, &imRasHeader, + + imRasHeaderFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + return ( 1 ); + +} + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imRasWriteRLERGB( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imRasWriteRLERGB( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + int i, j; /* Counters */ + + ImVfbPtr pptr; /* Pixel pointer */ + + unsigned char *rbp; /* Run buffer pointer */ + + ImVfb *vfb; /* VFB to write out */ + + unsigned char *runBuffer; /* Run buffer */ + + int nBytes; /* # of bytes to write */ + + int count; /* Run length count */ + + int index; /* Run index */ + + int oldindex; /* Prevous run index */ + + int onRGB; /* Which channel from VFB next? */ + + int n; /* # of bytes to write */ + + + + + + /* + + * Write out the header and possible CLT. + + */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + BinByteOrder( BINMBF ); + + if ( imRasWriteHeaderClt( pMap, ioType, fd, fp, vfb, 24, + + IMRTBYTEENCODED ) == -1 ) + + return ( -1 ); /* Error already handled */ + + + + /* + + * Convert and write out the image. Maximum run buffer is twice + + * size of one scanline. + + */ + + n = 2 * imRasHeader.ras_width * imRasHeader.ras_depth / 8; + + ImMalloc( runBuffer, unsigned char *, sizeof( unsigned char ) * n ); + + pptr = ImVfbQFirst( vfb ); + + imRasHeader.ras_length = 0; + + + + for ( i = 0; i < imRasHeader.ras_height; i++ ) + + { + + rbp = runBuffer; + + nBytes = 0; + + count = 1; + + + + oldindex = ImVfbQBlue( vfb, pptr ); + + onRGB = IMRAS_GREEN; + + + + for ( j = 1; j < imRasHeader.ras_width*3; j++ ) + + { + + switch ( onRGB ) + + { + + case IMRAS_BLUE: /* Get IMRAS_BLUE next. */ + + index = ImVfbQBlue( vfb, pptr ); + + onRGB = IMRAS_GREEN; + + break; + + case IMRAS_GREEN: /* Get IMRAS_GREEN next. */ + + index = ImVfbQGreen( vfb, pptr ); + + onRGB = IMRAS_RED; + + break; + + case IMRAS_RED: /* Get IMRAS_RED next. */ + + index = ImVfbQRed( vfb, pptr ); + + ImVfbSInc( vfb, pptr ); + + onRGB = IMRAS_BLUE; + + break; + + } + + + + IMAddRun( count, oldindex, index, rbp, nBytes ); + + } + + + + /* Dump last run. */ + + IMDumpRun( count, oldindex, index, rbp, nBytes ); + + if ( imRasHeader.ras_width & 0x1 ) + + { + + nBytes++; /* Strange 2-byte padding*/ + + *rbp++ = 0; + + } + + + + if ( ImBinWrite( ioType, fd, fp, runBuffer, UCHAR, 1, nBytes ) == -1 ) + + { + + free( (unsigned char *)runBuffer ); + + ImReturnBinError( ); + + } + + imRasHeader.ras_length += nBytes; + + } + + free( (unsigned char *)runBuffer ); + + + + + + /* + + * Seek back to the head of the file and rewrite the header. + + */ + + ImSeek( ioType, fd, fp, 0, 0 ); + + if ( ImBinWriteStruct( ioType, fd, fp, &imRasHeader, + + imRasHeaderFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + return ( 1 ); + +} + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imRasWriteRLERGBA( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imRasWriteRLERGBA( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + int i, j; /* Counters */ + + ImVfbPtr pptr; /* Pixel pointer */ + + unsigned char *rbp; /* Run buffer pointer */ + + ImVfb *vfb; /* VFB to write out */ + + unsigned char *runBuffer; /* Run buffer */ + + int nBytes; /* # of bytes to write */ + + int count; /* Run length count */ + + int index; /* Run index */ + + int oldindex; /* Prevous run index */ + + int onRGB; /* Which channel from VFB next? */ + + int n; /* # of bytes to write */ + + + + + + /* + + * Write out the header and possible CLT. + + */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + BinByteOrder( BINMBF ); + + if ( imRasWriteHeaderClt( pMap, ioType, fd, fp, vfb, 32, + + IMRTBYTEENCODED ) == -1 ) + + return ( -1 ); /* Error already handled */ + + + + /* + + * Convert and write out the image. Maximum run buffer is twice + + * size of one scanline. + + */ + + n = 2 * imRasHeader.ras_width * imRasHeader.ras_depth / 8; + + ImMalloc( runBuffer, unsigned char *, sizeof( unsigned char ) * n ); + + pptr = ImVfbQFirst( vfb ); + + imRasHeader.ras_length = 0; + + + + for ( i = 0; i < imRasHeader.ras_height; i++ ) + + { + + rbp = runBuffer; + + nBytes = 0; + + count = 1; + + + + oldindex = ImVfbQAlpha( vfb, pptr ); + + onRGB = IMRAS_BLUE; + + + + for ( j = 1; j < imRasHeader.ras_width*4; j++ ) + + { + + switch ( onRGB ) + + { + + case IMRAS_ALPHA: /* Get IMRAS_ALPHA next. */ + + index = ImVfbQAlpha( vfb, pptr ); + + onRGB = IMRAS_BLUE; + + break; + + case IMRAS_BLUE: /* Get IMRAS_BLUE next. */ + + index = ImVfbQBlue( vfb, pptr ); + + onRGB = IMRAS_GREEN; + + break; + + case IMRAS_GREEN: /* Get IMRAS_GREEN next. */ + + index = ImVfbQGreen( vfb, pptr ); + + onRGB = IMRAS_RED; + + break; + + case IMRAS_RED: /* Get IMRAS_RED next. */ + + index = ImVfbQRed( vfb, pptr ); + + ImVfbSInc( vfb, pptr ); + + onRGB = IMRAS_ALPHA; + + break; + + } + + + + IMAddRun( count, oldindex, index, rbp, nBytes ); + + } + + + + /* Dump last run. */ + + IMDumpRun( count, oldindex, index, rbp, nBytes ); + + + + if ( ImBinWrite( ioType, fd, fp, runBuffer, UCHAR, 1, nBytes ) == -1 ) + + { + + free( (unsigned char *)runBuffer ); + + ImReturnBinError( ); + + } + + imRasHeader.ras_length += nBytes; + + } + + free( (unsigned char *)runBuffer ); + + + + + + /* + + * Seek back to the head of the file and rewrite the header. + + */ + + ImSeek( ioType, fd, fp, 0, 0 ); + + if ( ImBinWriteStruct( ioType, fd, fp, &imRasHeader, + + imRasHeaderFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + return ( 1 ); + +} + diff --git a/utils/roq2/libim/imrgb.c b/utils/roq2/libim/imrgb.c new file mode 100644 index 0000000..fa4b8ac --- /dev/null +++ b/utils/roq2/libim/imrgb.c @@ -0,0 +1,5055 @@ +/** + + ** $Header: /roq/libim/imrgb.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/imrgb.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** imrgb.c - Iris RGB file I/O + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** imrgb.c contains routines to read and write Iris RGB files for + + ** the image manipulation library. Raster data read in is stored + + ** in a VFB. Raster data written out is taken from a tag table. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** none + + ** + + ** PRIVATE CONTENTS + + ** imRgbRead f read an Iris RGB file + + ** imRgbWriteRLE8 f write an Iris grayscale file run + + ** length encoded + + ** imRgbWriteRLERGB f write an Iris RGB file run length encoded + + ** imRgbWriteRaw8 f write an Iris grayscale file no + + ** compression + + ** imRgbWriteRawRGB f write an Iris RGB file no compression + + ** + + ** imRgbHeaderInfo t RGB file header information + + ** imRgbHeaderFields v imPixHeaderInfo description for Bin pkg + + ** + + ** imExpandRun f expand run length encoding + + ** imRgbEncode f compute run length encoding + + ** imRgbFrBuf m free internal buffers + + ** + + ** HISTORY + + ** $Log: /roq/libim/imrgb.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.22 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.21 1995/06/15 21:10:58 bduggan + + ** changed bzero to memset + + ** + + ** Revision 1.20 1995/04/14 23:06:47 bduggan + + ** made rle the default + + ** + + ** Revision 1.19 1995/04/03 21:35:40 bduggan + + ** took out #ifdef NEWMAGIC + + ** + + ** Revision 1.18 1995/01/10 23:41:55 bduggan + + ** made read/write routines static + + ** put in IMMULTI, IMPIPE instead of TRUE/FALSE + + ** + + ** Revision 1.17 94/10/03 11:30:49 nadeau + + ** Added read and write support for RGB+Alpha images, compressed + + ** or verbatim. + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Rearranged magic number structures for format handlers. + + ** Made format handler routines static (i.e., local to file). + + ** Updated comments, adding format descriptions and references. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.16 92/12/09 16:52:26 nadeau + + ** Fixed bug in reading of RGB RLE images that incorrectly computed + + ** the maximum size needed for a runbuffer. This caused memory to + + ** be overwritten and a core dump the next time malloc referenced + + ** its free list for allocating a new memory block. + + ** + + ** Revision 1.15 92/12/03 01:51:56 nadeau + + ** Corrected info messages. + + ** + + ** Revision 1.14 92/11/23 18:43:11 nadeau + + ** Removed use of IMINFOMSG. + + ** + + ** Revision 1.13 92/11/04 12:06:37 groening + + ** put ImFIleFormat info and magic number info + + ** from imfmt.c into this file. + + ** + + ** Revision 1.12 92/10/19 14:12:29 groening + + ** fixed bug in dealing with the name field in the header + + ** + + ** Revision 1.11 92/09/29 17:57:23 vle + + ** Added ImInfo messages. + + ** + + ** Revision 1.10 92/08/31 17:34:08 vle + + ** Updated copyright notice. + + ** + + ** Revision 1.9 92/06/12 12:58:01 vle + + ** Updated image format description. + + ** + + ** Revision 1.8 92/04/03 18:11:40 vle + + ** Added 8-bit image read and write support. Cleaned up code + + ** a little. + + ** + + ** Revision 1.7 92/01/28 15:45:51 u27101 + + ** Cleaned up old comments, added documentation for format, rewrote + + ** imRgbRead to process the image on disk instead of reading in the + + ** whole file first and then processing. - Vinh + + ** + + ** Revision 1.6 92/01/16 15:24:36 nadeau + + ** With Vinh's help, corrected file read code that failed to + + ** use the rowstart table to determine the runbuffer start of + + ** each encoded scanline of pixels. + + ** + + ** Revision 1.5 91/10/03 09:17:22 nadeau + + ** Fixed #includes. + + ** + + ** Revision 1.4 91/03/18 15:46:10 todd + + ** added a call to bzero() to fix a bug on the cray + + ** + + ** Revision 1.3 91/03/08 16:17:08 todd + + ** Rewrote imRgbWriteRLE from scratch. + + ** Optimized the rest. + + ** Removed MinPixel and MaxPixel code. histo is the only rgb program + + ** that used this information. + + ** + + ** Revision 1.2 91/03/02 18:34:04 nadeau + + ** Removed the tag table checking and VFB conversion now + + ** handled by ImFileRead and ImFileWrite. + + ** + + ** Revision 1.1 91/01/31 08:34:53 soraya + + ** Initial revision + + ** + + ** Revision 1.0 90/05/04 09:47:00 jesus ferrer + + ** Initial revision + + ** + + **/ + + + +#include "iminternal.h" + + + + + + + +/** + + ** FORMAT + + ** RGB - Silicon Graphics IRIS image + + ** + + ** AKA + + ** sgi + + ** + + ** FORMAT REFERENCES + + ** Silicon Graphics RGB Specification, Silicon Graphics Inc. + + ** + + ** CODE CREDITS + + ** Custom development, Soraya Gonzalez, San Diego Supercomputer Center, 1991. + + ** + + ** DESCRIPTION + + ** Silicon Graphics' RGB format, also know as SGI format, is used + + ** on the Silicon Graphics IRIS family of computers. Images of this + + ** format are generated by iristools. This format was created by + + ** Paul Haeberli, 1984. + + ** + + ** Additional format information can be derived from: + + ** 4Dgifts/iristools code + + ** + + ** + + ** The IRIS RGB format consists of two segments, a 512 byte header + + ** segment and a data segment, which length varies depending on + + ** the image size and if a compression scheme is used. + + ** + + ** Header Segment Format (header size 512 bytes) + + ** --------------------- + + ** + + ** Name Type Size Comments + + ** ---- ---- ---- -------- + + ** imagic unsigned short 2 bytes imagic = 0x01da, + + ** /4Dgifts/iristools/libimage/open.c + + ** uses this to determine the + + ** byte ordering and revision + + ** number (see dorev) + + ** type unsigned short 2 bytes storage type: + + ** type = RLE (0x0100) + + ** RLE compression + + ** type = VERBATIM (0x0000) + + ** no compression + + ** type or'ed with BBP + + ** (Bytes Per Pixel) + + ** BPP = 0x01, normally used + + ** BPP = 0x02 + + ** dim unsigned short 2 bytes image dimensions + + ** dim = 0x01, xsize only + + ** dim = 0x02, xsize and ysize + + ** dim = 0x03, xsize, ysize + + ** and depth + + ** xsize unsigned short 2 bytes width + + ** ysize unsigned short 2 bytes height + + ** zsize unsigned short 2 bytes depth + + ** zsize = 1 for 8-bit or + + ** 16-bit image, + + ** depending on BPP + + ** (grayscale image) + + ** zsize = 2 for 16-bit or + + ** 32-bit image, + + ** depending on BPP + + ** (never encountered yet) + + ** zsize = 3 for 24-bit or + + ** 48-bit image, + + ** depending on BPP + + ** (usually this type) + + ** zsize = 4 for 32 bit or + + ** 64 bit image, + + ** depending on BPP + + ** (for alpha channels) + + ** min int 4 bytes minimum pixel value + + ** max int 4 bytes maximum pixel value + + ** wastebytes int 4 bytes number of bytes wasted by image + + ** name char 80 bytes name of image, null terminated + + ** string + + ** colormap int 4 bytes color map type + + ** colormap = CM_NORMAL (0x0000) + + ** colormap = CM_DITHERED (0x0001) + + ** colormap = CM_SCREEN (0x0002) + + ** colormap = CM_COLORMAP (0x0003) + + ** file long 4 bytes *not used + + ** flags unsigned short 2 bytes *not used + + ** dorev short 2 bytes revision number + + ** dorev = 0, most significant + + ** byte first + + ** dorev = 1, least significant + + ** byte first + + ** x short 2 bytes *not used + + ** y short 2 bytes *not used + + ** z short 2 bytes *not used + + ** cnt short 2 bytes *not used + + ** *ptr unsigned short 4 bytes *not used + + ** *base unsigned short 4 bytes *not used + + ** *tmpbuf unsigned short 4 bytes *not used + + ** offset unsigned long 4 bytes *not used + + ** rleend unsigned long 4 bytes file location of end of RLE + + ** *rowstart unsigned long 4 bytes file location of row start table + + ** *rowsize long 4 bytes file location of row size table + + ** pad char 360 bytes end of header padding + + ** + + ** + + ** *not used - these fields are used only in the 4Dgifts code, so do not + + ** provide any useful information for the image + + ** + + ** + + ** + + ** Data Segment Format + + ** ------------------- + + ** + + ** Currently, there are two formats for the data segment, VERBATIM data + + ** and RLE compressed data. + + ** + + ** + + ** The VERBATIM data format: + + ** + + ** For depth = 1, + + ** grayscale plane + + ** + + ** For depth = 3, + + ** R plane + + ** G plane + + ** B plane + + ** This is the normal plane interleaved convention. + + ** + + ** For depth = 4, + + ** R plane + + ** G plane + + ** B plane + + ** alpha plane + + ** + + ** + + ** The RLE compressed data format: + + ** + + ** For the RLE compressed data format, there are two tables that are used. + + ** These two tables are pointed to by two pointers (file offsets from the + + ** beginning of the file) in the header, *rowstart and *rowsize. + + ** + + ** unsigned long startTable [ height * depth ] This table contains file offsets + + ** from the beginning of the file + + ** to locations in the file where + + ** an RLE chunk for one scanline + + ** is stored. + + ** + + ** unsigned long sizeTable [ height * depth ] This table contains the size of + + ** each RLE scanline chunk. One + + ** RLE scanline chunk decompresses + + ** into one scanline. + + ** + + ** For depth = 1, the tables are not divided: + + ** [0..height-1] for grayscale + + ** + + ** For depth = 3, the tables are divided into three parts: + + ** [0..height-1] for R + + ** [height..2*height-1] for G + + ** [2*height..3*height-1] for B + + ** (in other words, the tables are plane interleaved) + + ** + + ** For depth = 4, the tables are divided into three parts: + + ** [0..height-1] for R + + ** [height..2*height-1] for G + + ** [2*height..3*height-1] for B + + ** [3*height..4*height-1] for alpha + + ** + + ** + + ** To decompress one complete grayscale scanline, + + ** + + ** 1) use the startTable to find the file offset for one RLE compressed + + ** grayscale scanline chunk + + ** 2) use the sizeTable to get the RLE compressed grayscale scanline + + ** chunk size + + ** 3) read in and decompress the RLE compressed grayscale scanline + + ** chunk to get the real grayscale scanline + + ** + + ** To decompress one complete RGB scanline, + + ** + + ** 1) use the startTable to find the file offset for one RLE compressed + + ** R scanline chunk + + ** 2) use the sizeTable to get the RLE compressed R scanline chunk size + + ** 3) read in and decompress the RLE compressed R scanline chunk to + + ** get the real R scanline + + ** 4) use the startTable to find the file offset for one RLE compressed + + ** G scanline chunk + + ** 5) use the sizeTable to get the RLE compressed G scanline chunk size + + ** 6) read in and decompress the RLE compressed G scanline chunk to + + ** get the real G scanline + + ** 7) use the startTable to find the file offset for one RLE compressed + + ** B scanline chunk + + ** 8) use the sizeTable to get the RLE compressed B scanline chunk size + + ** 9) read in and decompress the RLE compressed B scanline chunk to + + ** get the real B scanline + + ** 10) combine the real R, G, and B scanlines to get the full RGB scanline + + ** + + ** + + ** RLE decompression: (for byte type IRIS RGB images only) + + ** + + ** There are two types of RLE data segment bytes: control data bytes + + ** and real data bytes. + + ** + + ** A control data byte consists of a 1 bit compression flag and 7 bit + + ** count value. + + ** ________ ________ ________ ... + + ** ^^ ^ ^ + + ** ||_____| |____ next bytes... + + ** | | + + ** | |______ count value (range 1 - 127, 0 means end of scanline) + + ** |______ compression flag (most significant bit) + + ** + + ** compression flag = 0, copy the next byte [count value] times, + + ** i.e. run compression + + ** compression flag = 1, copy the next [count value] bytes literally, + + ** i.e. no run compression + + ** + + ** A real data byte is just an 8-bit R, G, or B value to be copied. + + ** + + ** To decompress, + + ** 1) read in a control byte + + ** 2) if [count value] == 0, end of scanline, else 3) + + ** 3) based on compression flag, expand run segment accordingly + + ** 4) repeat from 1) + + ** + + ** IMPLEMENTATION SPECIFIC NOTES + + ** + + ** 1) Only 1 Byte Per Plane type images are supported, although 2 + + ** Bytes Per Plane type images are possible. All images + + ** encountered so far are 1 Byte Per Plane. + + ** 2) Byte ordering swapped images are handled. + + ** + + **/ + + + +/* + + * RGB - Silicon Graphics Iris RGB file + + * For information on these structures, how to use them, etc. please + + * see imfmt.c. + + */ + +#ifdef __STDC__ + +static int imRgbRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imRgbWriteRaw8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, + + TagTable *tagTable); + +static int imRgbWriteRLE8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, + + TagTable *tagTable); + +static int imRgbWriteRawRGB( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, + + TagTable *tagTable); + +static int imRgbWriteRLERGB( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, + + TagTable *tagTable); + +static int imRgbWriteRawRGBA( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, + + TagTable *tagTable); + +static int imRgbWriteRLERGBA( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, + + TagTable *tagTable); + +#else + +static int imRgbRead( ); + +static int imRgbWriteRaw8( ), imRgbWriteRLE8( ); + +static int imRgbWriteRawRGB( ), imRgbWriteRLERGB( ); + +static int imRgbWriteRawRGBA( ), imRgbWriteRLERGBA( ); + +#endif + +static char *imRgbNames[ ] = { "rgb", "iris", "sgi", NULL }; + +static unsigned char imRgbMagicNumber[ ] = { 0x01, 0xDA }; + +static ImFileFormatReadMap imRgbReadMap[ ] = + +{ + + /* in out */ + + /* type,ch,dep, attr. VFB type attr. */ + + { IN,1,8, LI, IMVFBINDEX8, 0 }, + + { IN,1,8, RLE, IMVFBINDEX8, 0 }, + + { RGB,3,8, LI, IMVFBRGB, 0 }, + + { RGB,3,8, RLE, IMVFBRGB, 0 }, + + { RGB,4,8, LI|A, IMVFBRGB, A }, + + { RGB,4,8, RLE|A, IMVFBRGB, A }, + + { -1, 0, -1, 0 }, + +}; + +static ImFileFormatWriteMap imRgbWriteMap[ ] = + +{ + + /* in out */ + + /* VFB type, attr., type,ch,dep, attr., func */ + + { IMVFBINDEX8, 0, IN,1,8, RLE, imRgbWriteRLE8 }, + + { IMVFBINDEX8, 0, IN,1,8, LI, imRgbWriteRaw8 }, + + { IMVFBRGB, 0, RGB,3,8, RLE, imRgbWriteRLERGB }, + + { IMVFBRGB, 0, RGB,3,8, LI, imRgbWriteRawRGB }, + + { IMVFBRGB, A, RGB,4,8, RLE|A, imRgbWriteRLERGBA }, + + { IMVFBRGB, A, RGB,4,8, LI|A, imRgbWriteRawRGBA }, + + { -1, 0, -1, 0, NULL }, + +}; + + + +static ImFileMagic imFileRgbMagic []= + +{ + + { 0, 2, imRgbMagicNumber}, + + { 0, 0, NULL }, + +}; + + + +ImFileFormat ImFileRgbFormat = + +{ + + imRgbNames, "SGI RGB image file", + + "Silicon Graphics, Inc.", + + "8-bit color index without CLT and 24-bit RGB color images with optional alpha channels,\n\ +uncompressed (verbatim) and RLE-compressed.", + "8-bit color index without CLT and 24-bit RGB color images with optional alpha channels,\n\ +uncompressed (verbatim) and RLE-compressed.", + + imFileRgbMagic, + + IMNOMULTI, IMPIPE, + + IMNOMULTI, IMNOPIPE, + + imRgbRead, imRgbReadMap, imRgbWriteMap + +}; + + + +#ifndef L_SET + +#define L_SET 0 /* Absolute offset */ + +#define L_INCR 1 /* Relative to current offset */ + +#define L_XTND 2 /* Relative to end of file */ + +#endif + + + +#ifndef F_SET + +#define F_SET 0 /* Absolute offset */ + +#define F_INCR 1 /* Relative to current offset */ + +#define F_XTND 2 /* Relative to end of file */ + +#endif + + + + + +/* + + * TYPEDEF & STRUCTURE + + * imRgbHeaderInfo - RGB file header information + + * imRgbHeaderFields - RGB file header fields for binary pkg. + + * + + * DESCRIPTION + + * A RGB file's header contains the image's width, height, and depth, + + * and two unused dummy fields. + + */ + + + +typedef struct imRgbHeaderInfo + +{ + + unsigned short rgb_imagic; /* Magic number (0x01da) */ + + unsigned short rgb_stype; /* Storage type */ + + unsigned short rgb_dim; /* Number of dimension of image */ + + unsigned short rgb_width; /* Image width */ + + unsigned short rgb_height; /* Image height */ + + unsigned short rgb_zsize; /* # channels is 3 or 4 in RGB images*/ + + int rgb_min; /* Minimun pixel value */ + + int rgb_max; /* Maximun pixel value */ + + int rgb_wastebytes; /* Number of bytes wasted by image */ + + char rgb_name[80]; /* Image name */ + + int rgb_colormap; /* Color map type */ + + + + char rgb_dummy[32]; /* Unused dummy field */ + + + + unsigned int rgb_rleend; /* File location of end of rle data */ + + unsigned int rgb_rowstart; /* File location of row start table */ + + unsigned int rgb_rowsize; /* File location of row size table */ + + char rgb_pad[360]; /* Unused dummy field */ + +} imRgbHeaderInfo; + + + +static BinField imRgbHeaderFields[ ] = + +{ + + { USHORT, 2, 1 }, /* rgb_imagic */ + + { USHORT, 2, 1 }, /* rgb_type */ + + { USHORT, 2, 1 }, /* rgb_dim */ + + { USHORT, 2, 1 }, /* rgb_width */ + + { USHORT, 2, 1 }, /* rgb_height */ + + { USHORT, 2, 1 }, /* rgb_zsize */ + + { INT, 4, 1 }, /* rgb_min */ + + { INT, 4, 1 }, /* rgb_max */ + + { INT, 4, 1 }, /* rgb_wastedbytes */ + + { CHAR, 1, 80 }, /* rgb_name */ + + { INT, 4, 1 }, /* rgb_colormap */ + + + + { CHAR, 1, 32 }, /* rgb_dummy */ + + + + { UINT, 4, 1 }, /* rgb_rleend */ + + { UINT, 4, 1 }, /* rgb_rowstart */ + + { UINT, 4, 1 }, /* rgb_rowsize */ + + { CHAR, 1, 360 }, /* rgb_pad */ + + { 0, 0, 0 } + +}; + + + +/* + + * MACRO + + * imRgbFrBuf - Free six chunks of memory + + * + + * DESCRIPTION + + * Call free six times to free the six chunks of memory pointed to by + + * a-f. + + */ + + + +#define imRgbFrBuf(a,b,c,d,e,f) free((a));free((b));free((c));free((d));free((e));free((f)) + +#define imRgbFrBufAlp(a,b,c,d,e,f,g) free((a));free((b));free((c));free((d));free((e));free((f));free((g)) + + + + + +/* + + * Various useful RGB specific constants and macros + + */ + + + +#define IMRGB_ITYPE_RLE 0x0100 + +#define IMRGB_ITYPE_VERBATIM 0x0000 + +#define IMRGB_ITYPE_BPP1 0x0001 + +#define IMRGB_ISRLE(type) (((type) & 0xff00) == IMRGB_ITYPE_RLE) + +#define IMRGB_ISVERBATIM(type) (((type) & 0xff00) == IMRGB_ITYPE_VERBATIM) + +#define IMRGB_ISBPP1(type) (((type) & 0x00ff) == IMRGB_ITYPE_BPP1) + +#define IMRGBHEADERSIZE (512L) + +#define IMRGB_BYTES_PER_PLANE 0x01 + +#define IMRGB_IMAGIC_MBF 0x01da + +#define IMRGB_IMAGIC_LBF 0xda01 + + + + + + + + + +/* + + * FUNCTION + + * imExpandRun - Expand run length encoded pixels + + * + + * DESCRIPTION + + * Expand the runs to create a raw RGB image in the VFB. + + * + + * RLE coding scheme for 1 byte type images (IMRGB_BYTES_PER_PLANE = 0x01): + + * + + * Each control byte consists of 1 bit compression flag (F) , and + + * 7 bit count value (C). + + * + + * F = 0, write next byte C times, example... + + * + + * RLE: 0x85 0x9d + + * image: 0x9d 0x9d 0x9d 0x9d 0x9d + + * + + * F = 1, write next C bytes literally, example... + + * + + * RLE: 0x05 0xff 0x12 0x32 0xaf 0x1d + + * image: 0xff 0x12 0x32 0xaf 0x1d + + * + + * Data bytes use all 8 bits and follow a control byte or other + + * data bytes. + + */ + +static void + +#ifdef __STDC__ + +imExpandRun(ImVfb *vfb,ImVfbPtr vfbptr,unsigned char *rbp,int channel) + +#else + +imExpandRun(vfb,vfbptr,rbp,channel) + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr vfbptr; /* Pointer to vfb */ + + unsigned char *rbp; /* Pointer to output buffer */ + + int channel; /* Channel to be processed + + (Grayscale,Red,Green,Blue,Alpha) */ + +#endif + +{ + + ImVfbPtr vfbpfirst; + + unsigned short count; + + unsigned short c; /* color component */ + + + + vfbpfirst = vfbptr; + + switch ( channel ) + + { + + case 0 : /* Color index */ + + while ( count = (*rbp & 0x7f) ) + + { + + if ( *rbp & 0x80 ) + + for ( ; (count > 0); count-- ) + + { + + (rbp)++; + + c = *rbp; + + ImVfbSIndex8( vfb, vfbptr, c ); + + ImVfbSInc( vfb , vfbptr ); + + } + + else + + { + + (rbp)++; + + c = *rbp; + + for ( ; ( count > 0); count-- ) + + { + + ImVfbSIndex8( vfb, vfbptr, c ); + + ImVfbSInc( vfb, vfbptr ); + + } + + } + + (rbp)++; + + } + + (rbp)++; + + break; + + + + case 1 : /* Red channel */ + + while ( count = (*rbp & 0x7f) ) + + { + + if ( *rbp & 0x80 ) + + for ( ; (count > 0); count-- ) + + { + + (rbp)++; + + c = *rbp; + + ImVfbSRed( vfb, vfbptr, c ); + + ImVfbSInc( vfb , vfbptr ); + + } + + else + + { + + (rbp)++; + + c = *rbp; + + for ( ; ( count > 0); count-- ) + + { + + ImVfbSRed( vfb, vfbptr, c ); + + ImVfbSInc( vfb, vfbptr ); + + } + + } + + (rbp)++; + + } + + (rbp)++; + + break; + + + + case 2 : /* Green channel */ + + while ( count = (*rbp & 0x7f) ) + + { + + if ( *rbp & 0x80 ) + + for ( ; (count > 0); count-- ) + + { + + (rbp)++; + + c = *rbp; + + ImVfbSGreen( vfb, vfbptr, c ); + + ImVfbSInc( vfb , vfbptr ); + + } + + else + + { + + (rbp)++; + + c = *rbp; + + for ( ; ( count > 0); count-- ) + + { + + ImVfbSGreen( vfb, vfbptr, c ); + + ImVfbSInc( vfb, vfbptr ); + + } + + } + + (rbp)++; + + } + + (rbp)++; + + break; + + + + case 3 : /* Blue channel */ + + while ( count = (*rbp & 0x7f) ) + + { + + if ( *rbp & 0x80 ) + + for ( ; (count > 0); count-- ) + + { + + (rbp)++; + + c = *rbp; + + ImVfbSBlue( vfb, vfbptr, c ); + + ImVfbSInc( vfb , vfbptr ); + + } + + else + + { + + (rbp)++; + + c = *rbp; + + for ( ; ( count > 0); count-- ) + + { + + ImVfbSBlue( vfb, vfbptr, c ); + + ImVfbSInc( vfb, vfbptr ); + + } + + } + + (rbp)++; + + } + + (rbp)++; + + break; + + case 4 : /* Alpha channel */ + + while ( count = (*rbp & 0x7f) ) + + { + + if ( *rbp & 0x80 ) + + for ( ; (count > 0); count-- ) + + { + + (rbp)++; + + c = *rbp; + + ImVfbSAlpha( vfb, vfbptr, c ); + + ImVfbSInc( vfb , vfbptr ); + + } + + else + + { + + (rbp)++; + + c = *rbp; + + for ( ; ( count > 0); count-- ) + + { + + ImVfbSAlpha( vfb, vfbptr, c ); + + ImVfbSInc( vfb, vfbptr ); + + } + + } + + (rbp)++; + + } + + (rbp)++; + + break; + + } /* End of switch */ + +} + + + + + + + + + +/* + + * FUNCTION + + * imRgbRead - read an Iris RGB file + + * + + * DESCRIPTION + + * The file header is read and the size of the image determined. + + * Space is allocated for the VFB and the run codes read in to + + * a run buffer. The run buffer is then expanded into straight + + * RGB values in the VFB and the completed VFB added to the tag list. + + */ + + + +static int /* Returns # tags read in */ + +#ifdef __STDC__ + +imRgbRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imRgbRead( ioType, fd, fp, flagsTable, tagTable ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointer */ + + imRgbHeaderInfo header; /* RGB file header */ + + char *rgbName; /* Name of the rgb image in file*/ + + unsigned char *runBuffer; /* Run buffer */ + + unsigned char *rbp; /* Run buffer pointer */ + + int x,y,z; /* Convenient short names */ + + unsigned long *starttable; /* Rowstart table */ + + long *sizetable; /* Rowsize table */ + + int xty, xtyt2, xtyt3; /* Loop invariants */ + + int i,j,k; /* Counters */ + + int status; /* Status returned from seek */ + + unsigned long maxrun; /* Maximum run size */ + + char message[100]; /* ImInfo message */ + + + + /* + + * Read in magic number in header, first 2 bytes. + + */ + + BinByteOrder( BINMBF ); + + if ( ImBinRead( ioType, fd, fp, &(header.rgb_imagic),USHORT,2,1) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + /* + + * Use magic number to determine byte ordering of rest + + * of file and to make sure we can handle this particular + + * image type. + + */ + + switch ( header.rgb_imagic ) + + { + + case IMRGB_IMAGIC_MBF: + + BinByteOrder( BINMBF ); + + break; + + case IMRGB_IMAGIC_LBF: + + BinByteOrder( BINLBF ); + + break; + + default: + + /* + + * Bad magic number. + + */ + + ImErrorFatal( ImQError( ), -1, IMEMAGIC ); + + } + + + + /* + + * Reset fd or fp to beginning of file. Read in complete header. + + */ + + status = ImSeek( ioType, fd, fp, 0, L_SET ); + + if ( status == -1 ) + + { + + ImErrorFatal( ImQError( ), -1, IMESYS ); + + } + + if ( ImBinReadStruct( ioType, fd, fp, &header, imRgbHeaderFields )== -1) + + { + + ImReturnBinError( ); + + } + + + + /* + + * Make sure we support the number of Bytes Per Plane. + + * Currently, only 1 Byte Per Plane is supported. + + */ + + if( !IMRGB_ISBPP1( header.rgb_stype ) ) + + { + + ImErrorFatal( ImQError( ), -1, IMEPLANES ); + + } + + + + x = header.rgb_width; + + y = header.rgb_height; + + z = header.rgb_zsize; + + + + /* + + * Output -verbose message + + */ + + ImInfo( "Image Name", header.rgb_name ); + + switch ( header.rgb_imagic ) + + { + + case IMRGB_IMAGIC_MBF: + + ImInfo( "Byte Order", "Most Significant Byte First" ); + + break; + + case IMRGB_IMAGIC_LBF: + + ImInfo( "Byte Order", "Least Significant Byte First" ); + + break; + + } + + sprintf( message, "%d x %d", x, y ); + + ImInfo( "Resolution", message ); + + + + switch( z ) + + { + + case 1: + + ImInfo( "Type", "8-bit Grayscale" ); + + ImInfo( "Alpha Channel", "none" ); + + break; + + case 3: + + ImInfo( "Type", "24-bit RGB" ); + + ImInfo( "Alpha Channel", "none" ); + + break; + + case 4: + + ImInfo( "Type", "32-bit RGB" ); + + ImInfo( "Alpha Channel", "8-bit" ); + + break; + + } + + + + if( IMRGB_ISRLE( header.rgb_stype ) ) + + { + + ImInfo( "Compression Type", "Run Length Encoded (RLE)" ); + + } + + else + + ImInfo( "Compression Type", "none (Verbatim)" ); + + + + /* + + * Allocate a VFB of the required size and type. + + */ + + switch( z ) + + { + + case 1: /* 8-bit grayscale image */ + + if( (vfb = ImVfbAlloc( x, y, IMVFBINDEX8 )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + break; + + + + case 3: /* 24-bit RGB image */ + + if( (vfb = ImVfbAlloc( x, y, IMVFBRGB )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + break; + + case 4: /* 32-bit RGB image + alpha */ + + if( (vfb = ImVfbAlloc( x, y, IMVFBRGB|IMVFBALPHA )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + break; + + + + default: /* unsupported image depth */ + + ImErrorFatal( ImQError( ), -1, IMEDEPTH ); + + } + + + + /* + + * Use image type to determine whether to do RLE expansion into + + * vfb or do verbatim copy into vfb. + + */ + + pptr = ImVfbQFirst( vfb ); + + if( IMRGB_ISRLE( header.rgb_stype ) ) + + { + + /* + + * Allocate row start and row size tables. + + */ + + ImMalloc( starttable, unsigned long *, y * z * sizeof( unsigned long ) ); + + ImMalloc( sizetable, long *, y * z * sizeof( long ) ); + + + + /* + + * Read rowstart table & rowsize tables. + + */ + + if( ImBinRead( ioType, fd,fp,starttable,ULONG, 4, y * z) == -1 ) + + { + + free( (char *)starttable ); + + free( (char *)sizetable ); + + ImReturnBinError( ); + + } + + if( ImBinRead( ioType, fd, fp, sizetable,LONG, 4, y * z) == -1 ) + + { + + free( (char *)starttable ); + + free( (char *)sizetable ); + + ImReturnBinError( ); + + } + + /* + + * Allocate worst case runBuffer, largest size in sizetable + + */ + + maxrun = sizetable[0]; + + for( j = 1; j < y*z; j++ ) + + { + + if( sizetable[j] > maxrun ) + + { + + maxrun = sizetable[j]; + + } + + } + + ImMalloc( runBuffer, unsigned char *, maxrun+1 ); + + rbp = runBuffer; + + + + /* + + * Check image depth, z, and expand accordingly. + + */ + + switch( z ) + + { + + case 1: /* 8-bit grayscale image */ + + for( j = 0; j < y; j++ ) + + { + + /* + + * Read and expand a Grayscale scanline + + */ + + status = ImSeek( ioType,fd,fp,starttable[j],L_SET ); + + if ( status == -1 ) + + { + + ImErrorFatal( ImQError( ), -1, IMESYS ); + + } + + if ( ImBinRead ( ioType, fd, fp, runBuffer, + + UCHAR, 1, sizetable[j]) == -1 ) + + { + + free( (char *)runBuffer ); + + free( (char *)starttable ); + + free( (char *)sizetable ); + + ImReturnBinError( ); + + } + + imExpandRun( vfb, pptr, rbp, 0 ); + + + + /* + + * Move to next scanline in vfb + + */ + + pptr = ImVfbQDown( vfb, pptr ); + + } + + break; + + + + case 3: /* 24-bit RGB image */ + + /* + + * Read in each of the R, G, and B scanlines and alpha in + + * turn and expand the runs into the VFB. + + */ + + for ( j = 0; j < y; j++ ) + + { + + /* + + * Read and expand a Red scanline + + */ + + status = ImSeek( ioType,fd,fp,starttable[0*y+j],L_SET ); + + if ( status == -1 ) + + { + + ImErrorFatal( ImQError( ), -1, IMESYS ); + + } + + if ( ImBinRead ( ioType, fd, fp, runBuffer, + + UCHAR, 1, sizetable[0*y+j]) == -1 ) + + { + + free( (char *)runBuffer ); + + free( (char *)starttable ); + + free( (char *)sizetable ); + + ImReturnBinError( ); + + } + + imExpandRun( vfb, pptr, rbp, 1 ); + + + + /* + + * Read and expand a Blue scanline + + */ + + status = ImSeek( ioType,fd,fp,starttable[1*y+j],L_SET ); + + if ( status == -1 ) + + { + + ImErrorFatal( ImQError( ), -1, IMESYS ); + + } + + if ( ImBinRead ( ioType, fd, fp, runBuffer, + + UCHAR, 1, sizetable[1*y+j]) == -1 ) + + { + + free( (char *)runBuffer ); + + free( (char *)starttable ); + + free( (char *)sizetable ); + + ImReturnBinError( ); + + } + + imExpandRun( vfb, pptr, rbp, 2 ); + + + + /* + + * Read and expand a Green scanline + + */ + + status = ImSeek( ioType,fd,fp,starttable[2*y+j],L_SET ); + + if ( status == -1 ) + + { + + ImErrorFatal( ImQError( ), -1, IMESYS ); + + } + + if ( ImBinRead ( ioType, fd, fp, runBuffer, + + UCHAR, 1, sizetable[2*y+j]) == -1 ) + + { + + free( (char *)runBuffer ); + + free( (char *)starttable ); + + free( (char *)sizetable ); + + ImReturnBinError( ); + + } + + imExpandRun( vfb, pptr, rbp, 3 ); + + + + /* + + * Move to next scanline in vfb + + */ + + pptr = ImVfbQDown( vfb, pptr ); + + } /* End of For */ + + break; + + + + case 4: /* 32-bit RGB image + alpha channel */ + + /* + + * Read in each of the R, G, and B scanlines and possibly alpha in + + * turn and expand the runs into the VFB. + + */ + + for ( j = 0; j < y; j++ ) + + { + + /* + + * Read and expand a Red scanline + + */ + + status = ImSeek( ioType,fd,fp,starttable[0*y+j],L_SET ); + + if ( status == -1 ) + + { + + ImErrorFatal( ImQError( ), -1, IMESYS ); + + } + + if ( ImBinRead ( ioType, fd, fp, runBuffer, + + UCHAR, 1, sizetable[0*y+j]) == -1 ) + + { + + free( (char *)runBuffer ); + + free( (char *)starttable ); + + free( (char *)sizetable ); + + ImReturnBinError( ); + + } + + imExpandRun( vfb, pptr, rbp, 1 ); + + + + /* + + * Read and expand a Blue scanline + + */ + + status = ImSeek( ioType,fd,fp,starttable[1*y+j],L_SET ); + + if ( status == -1 ) + + { + + ImErrorFatal( ImQError( ), -1, IMESYS ); + + } + + if ( ImBinRead ( ioType, fd, fp, runBuffer, + + UCHAR, 1, sizetable[1*y+j]) == -1 ) + + { + + free( (char *)runBuffer ); + + free( (char *)starttable ); + + free( (char *)sizetable ); + + ImReturnBinError( ); + + } + + imExpandRun( vfb, pptr, rbp, 2 ); + + + + /* + + * Read and expand a Green scanline + + */ + + status = ImSeek( ioType,fd,fp,starttable[2*y+j],L_SET ); + + if ( status == -1 ) + + { + + ImErrorFatal( ImQError( ), -1, IMESYS ); + + } + + if ( ImBinRead ( ioType, fd, fp, runBuffer, + + UCHAR, 1, sizetable[2*y+j]) == -1 ) + + { + + free( (char *)runBuffer ); + + free( (char *)starttable ); + + free( (char *)sizetable ); + + ImReturnBinError( ); + + } + + imExpandRun( vfb, pptr, rbp, 3 ); + + + + /* + + * Read and expand an alpha scanline + + */ + + status = ImSeek( ioType,fd,fp,starttable[3*y+j],L_SET ); + + if ( status == -1 ) + + { + + ImErrorFatal( ImQError( ), -1, IMESYS ); + + } + + if ( ImBinRead ( ioType, fd, fp, runBuffer, + + UCHAR, 1, sizetable[3*y+j]) == -1 ) + + { + + free( (char *)runBuffer ); + + free( (char *)starttable ); + + free( (char *)sizetable ); + + ImReturnBinError( ); + + } + + imExpandRun( vfb, pptr, rbp, 4 ); + + /* + + * Move to next scanline in vfb + + */ + + pptr = ImVfbQDown( vfb, pptr ); + + + + } /* End of for loop */ + + break; + + + + default: /* unsupported image depth */ + + ImErrorFatal( ImQError( ), -1, IMEDEPTH ); + + } /* end switch */ + + + + /* + + * free starttable and sizetable + + */ + + + + free( (char *)sizetable ); + + free( (char *)starttable ); + + } + + else /* VERBATIM format */ + + { + + /* + + * Allocate a run buffer large enough for the image. + + * Read it in. + + */ + + ImMalloc( runBuffer, unsigned char *, 4 * x * y ); + + if ( ImBinRead( ioType, fd, fp, runBuffer, UCHAR, 1, 4*x*y)==-1) + + { + + free( (char *)runBuffer ); + + ImReturnBinError( ); + + } + + + + /* + + * Copy it to the VFB. + + */ + + rbp = runBuffer; + + xty = x * y; + + xtyt2 = x * y * 2; + + xtyt3 = x * y * 3; + + + + switch( z ) + + { + + case 1: /* 8-bit grayscale image */ + + for ( j = 1; j <= y; j++ ) + + { + + for ( i = 1; i <= x; i++, rbp++ ) + + { + + ImVfbSIndex8( vfb, pptr, *rbp ); + + ImVfbSInc( vfb, pptr ); + + } + + } + + break; + + + + case 3: /* 24-bit RGB image */ + + for ( j = 1; j <= y; j++ ) + + { + + for ( i = 1; i <= x; i++, rbp++ ) + + { + + ImVfbSRed( vfb, pptr, *rbp ); + + ImVfbSGreen( vfb, pptr, *(rbp+xty) ); + + ImVfbSBlue( vfb, pptr, *(rbp+xtyt2) ); + + ImVfbSInc( vfb, pptr ); + + } + + } + + break; + + + + case 4: /* 32-bit RGB image + alpha channel */ + + for ( j = 1; j <= y; j++ ) + + { + + for ( i = 1; i <= x; i++, rbp++ ) + + { + + ImVfbSRed( vfb, pptr, *rbp ); + + ImVfbSGreen( vfb, pptr, *(rbp+xty) ); + + ImVfbSBlue( vfb, pptr, *(rbp+xtyt2) ); + + ImVfbSAlpha( vfb, pptr, *(rbp+xtyt3) ); + + ImVfbSInc( vfb, pptr ); + + } + + } + + break; + + + + default: /* unsupported image depth */ + + ImErrorFatal( ImQError( ), -1, IMEDEPTH ); + + + + } /* end switch */ + + } + + ImVfbFlip( vfb, IMVFBYFLIP, vfb ); + + free( runBuffer ); + + + + + + /* + + * Save the image name in a more permanent location + + * so we can put it in the tag table + + */ + + + + if ( strlen( header.rgb_name ) > 0 ) + + { + + ImMalloc( rgbName, char *, strlen(header.rgb_name)+1 ); + + strcpy( rgbName, header.rgb_name ); + + + + TagTableAppend( tagTable, + + TagEntryAlloc( "image name", POINTER, &rgbName ) ); + + } + + + + /* + + * Add the VFB to the tagTable. + + */ + + TagTableAppend( tagTable, + + TagEntryAlloc( "image vfb", POINTER, &vfb ) ); + + + + return ( 2 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imRgbWriteRaw8 - write an 8-bit uncompressed Iris RGB file (verbatim) + + * + + * DESCRIPTION + + * That VFB is queried, and the RGB file header set up and written out. + + * The VFB data is then converted and written out. + + */ + + + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imRgbWriteRaw8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imRgbWriteRaw8( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag list to read from */ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointers */ + + imRgbHeaderInfo header; /* RGB file header */ + + unsigned char *runBuffer; /* Run buffer */ + + unsigned char *rbp; /* Run buffer point. */ + + int n; /* Counter */ + + int x, y; /* Pixel counters */ + + TagEntry *tagEntry; /* Tmp table entry */ + + char *tmpString; /* Tmp string holder */ + + int i, j; /* Counters */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * Set up and write out the header. + + */ + + BinByteOrder( BINMBF ); + + + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + + + header.rgb_stype = IMRGB_ITYPE_VERBATIM | IMRGB_BYTES_PER_PLANE; + + header.rgb_imagic = 0x01da; + + header.rgb_dim = 2; + + header.rgb_width = ImVfbQWidth( vfb ); + + header.rgb_height = ImVfbQHeight( vfb ); + + header.rgb_zsize = 1; + + header.rgb_min = 0; + + header.rgb_max = 255; + + header.rgb_wastebytes = 3; + + header.rgb_colormap = 0; + + header.rgb_rleend = 0; + + header.rgb_rowstart = 0; + + header.rgb_rowsize = 0; + + + + tagEntry = TagTableQDirect( tagTable, "image name", 0 ); + + if ( tagEntry == TAGENTRYNULL ) + + strcpy( header.rgb_name, "untitled" ); + + else + + { + + TagEntryQValue( tagEntry, &tmpString ); + + strncpy( header.rgb_name, tmpString, 80 ); + + } + + + + if ( ImBinWriteStruct( ioType, fd, fp, &header, imRgbHeaderFields )==-1) + + ImReturnBinError( ); + + + + /* + + * Output -verbose message + + */ + + ImInfo( "Image Name", header.rgb_name ); + + ImInfo( "Byte Order", "Most Significant Byte First" ); + + + + sprintf( message, "%d x %d", header.rgb_width, header.rgb_height ); + + ImInfo( "Resolution", message ); + + + + ImInfo( "Type", "8-bit Grayscale" ); + + ImInfo( "Compression Type", "none (Verbatim)" ); + + ImInfo( "Alpha Channel", "none" ); + + + + /* + + * Allocate space for a run buffer large enough for : + + * (width * height * 1) unencoded scanlines. + + */ + + x = header.rgb_width; + + y = header.rgb_height; + + + + ImMalloc( runBuffer, unsigned char *, y * x ); + + + + rbp = runBuffer; + + + + /* + + * Output grayscale. + + */ + + pptr = ImVfbQLast( vfb ); + + for (i=1; i < x; i++) + + ImVfbSDec( vfb, pptr ); + + for (j = 1; (j <= y); j++) + + { + + for (i = 1; (i <= x); i++) + + { + + *rbp = ImVfbQIndex8( vfb, pptr ); + + ImVfbSInc( vfb, pptr ); + + rbp++; + + } + + ImVfbSDec( vfb, pptr ); + + pptr = ImVfbQUp( vfb, pptr ); + + for (i=1; i < x; i++) + + ImVfbSDec( vfb, pptr ); + + } + + + + /* + + * Write out the buffer. + + */ + + if ( ImBinWrite( ioType, fd, fp, runBuffer, UCHAR, 1, x * y ) == -1) + + { + + free( (char *)runBuffer ); + + ImReturnBinError( ); + + } + + + + + + free( (char *)runBuffer ); + + return ( 1 ); + +} + + + + + + + + + +/* + + * FUNCTION + + * imRgbWriteRawRGB - write an uncompressed Iris RGB file (verbatim) + + * + + * DESCRIPTION + + * That VFB is queried, and the RGB file header set up and written out. + + * The VFB data is then converted and written out. + + */ + + + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imRgbWriteRawRGB( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imRgbWriteRawRGB( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag list to read from */ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointers */ + + imRgbHeaderInfo header; /* RGB file header */ + + unsigned char *runBuffer; /* Run buffer */ + + unsigned char *rbp; /* Run buffer point. */ + + int n; /* Counter */ + + int x, y; /* Pixel counters */ + + TagEntry *tagEntry; /* Tmp table entry */ + + char *tmpString; /* Tmp string holder */ + + int i, j; /* Counters */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * Set up and write out the header. + + */ + + BinByteOrder( BINMBF ); + + + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + + + header.rgb_stype = IMRGB_ITYPE_VERBATIM | IMRGB_BYTES_PER_PLANE; + + header.rgb_imagic = 0x01da; + + header.rgb_dim = 3; + + header.rgb_width = ImVfbQWidth( vfb ); + + header.rgb_height = ImVfbQHeight( vfb ); + + header.rgb_zsize = 3; + + header.rgb_min = 0; + + header.rgb_max = 255; + + header.rgb_wastebytes = 3; + + header.rgb_colormap = 0; + + header.rgb_rleend = 0; + + header.rgb_rowstart = 0; + + header.rgb_rowsize = 0; + + + + tagEntry = TagTableQDirect( tagTable, "image name", 0 ); + + if ( tagEntry == TAGENTRYNULL ) + + strcpy( header.rgb_name, "untitled" ); + + else + + { + + TagEntryQValue( tagEntry, &tmpString ); + + strncpy( header.rgb_name, tmpString, 80 ); + + } + + + + if ( ImBinWriteStruct( ioType, fd, fp, &header, imRgbHeaderFields )==-1) + + ImReturnBinError( ); + + + + /* + + * Output -verbose message + + */ + + ImInfo( "Image Name", header.rgb_name ); + + ImInfo( "Byte Order", "Most Significant Byte First" ); + + + + sprintf( message, "%d x %d", header.rgb_width, header.rgb_height ); + + ImInfo( "Resolution", message ); + + + + ImInfo( "Type", "24-bit RGB" ); + + ImInfo( "Compression Type", "none (Verbatim)" ); + + ImInfo( "Alpha Channel", "none" ); + + + + /* + + * Allocate space for a run buffer large enough for : + + * (width * height * 3) unencoded scanlines. + + */ + + x = header.rgb_width; + + y = header.rgb_height; + + + + ImMalloc( runBuffer, unsigned char *, 3 * y * x ); + + + + rbp = runBuffer; + + pptr = ImVfbQLast( vfb ); + + for (i=1; i < x; i++) + + ImVfbSDec( vfb, pptr ); + + + + + + /* + + * Red, Green, then Blue channel. + + */ + + for (j = 1; (j <= y); j++) + + { + + for (i = 1; (i <= x); i++) + + { + + *rbp = ImVfbQRed( vfb, pptr ); + + ImVfbSInc( vfb, pptr ); + + rbp++; + + } + + ImVfbSDec( vfb, pptr ); + + pptr = ImVfbQUp( vfb, pptr ); + + for (i=1; i < x; i++) + + ImVfbSDec( vfb, pptr ); + + } + + + + pptr = ImVfbQLast( vfb ); + + for(i=1; i < x; i++) ImVfbSDec( vfb, pptr ); + + for (j = 1; (j <= y); j++) + + { + + for (i = 1; (i <= x); i++) + + { + + *rbp = ImVfbQGreen( vfb, pptr ); + + ImVfbSInc( vfb, pptr ); + + rbp++; + + } + + ImVfbSDec( vfb, pptr ); + + pptr = ImVfbQUp( vfb, pptr ); + + for (i=1; i < x; i++) + + ImVfbSDec( vfb, pptr ); + + } + + + + pptr = ImVfbQLast( vfb ); + + for(i=1; i < x; i++) ImVfbSDec( vfb, pptr ); + + for (j = 1; (j <= y); j++) + + { + + for (i = 1; (i <= x); i++) + + { + + *rbp = ImVfbQBlue( vfb, pptr ); + + ImVfbSInc( vfb, pptr ); + + rbp++; + + } + + ImVfbSDec( vfb, pptr ); + + pptr = ImVfbQUp( vfb, pptr ); + + for (i=1; i < x; i++) + + ImVfbSDec( vfb, pptr ); + + } + + + + + + /* + + * Write out the buffer. + + */ + + if ( ImBinWrite( ioType, fd, fp, runBuffer, UCHAR, 1, x * y * 3 ) == -1) + + { + + free( (char *)runBuffer ); + + ImReturnBinError( ); + + } + + + + + + free( (char *)runBuffer ); + + return ( 1 ); + +} + + + + + +/* + + * FUNCTION + + * imRgbWriteRawRGBA - write an uncompressed Iris RGB file (verbatim) + + * + + * DESCRIPTION + + * That VFB is queried, and the RGB file header set up and written out. + + * The VFB data is then converted and written out. + + */ + + + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imRgbWriteRawRGBA( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imRgbWriteRawRGBA( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag list to read from */ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointers */ + + imRgbHeaderInfo header; /* RGB file header */ + + unsigned char *runBuffer; /* Run buffer */ + + unsigned char *rbp; /* Run buffer point. */ + + int n; /* Counter */ + + int x, y; /* Pixel counters */ + + TagEntry *tagEntry; /* Tmp table entry */ + + char *tmpString; /* Tmp string holder */ + + int i, j; /* Counters */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * Set up and write out the header. + + */ + + BinByteOrder( BINMBF ); + + + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + + + header.rgb_stype = IMRGB_ITYPE_VERBATIM | IMRGB_BYTES_PER_PLANE; + + header.rgb_imagic = 0x01da; + + header.rgb_dim = 3; + + header.rgb_width = ImVfbQWidth( vfb ); + + header.rgb_height = ImVfbQHeight( vfb ); + + header.rgb_zsize = 4; + + header.rgb_min = 0; + + header.rgb_max = 255; + + header.rgb_wastebytes = 3; + + header.rgb_colormap = 0; + + header.rgb_rleend = 0; + + header.rgb_rowstart = 0; + + header.rgb_rowsize = 0; + + + + tagEntry = TagTableQDirect( tagTable, "image name", 0 ); + + if ( tagEntry == TAGENTRYNULL ) + + strcpy( header.rgb_name, "untitled" ); + + else + + { + + TagEntryQValue( tagEntry, &tmpString ); + + strncpy( header.rgb_name, tmpString, 80 ); + + } + + + + if ( ImBinWriteStruct( ioType, fd, fp, &header, imRgbHeaderFields )==-1) + + ImReturnBinError( ); + + + + /* + + * Output -verbose message + + */ + + ImInfo( "Image Name", header.rgb_name ); + + ImInfo( "Byte Order", "Most Significant Byte First" ); + + + + sprintf( message, "%d x %d", header.rgb_width, header.rgb_height ); + + ImInfo( "Resolution", message ); + + + + ImInfo( "Type", "24-bit RGB" ); + + ImInfo( "Compression Type", "none (Verbatim)" ); + + + + ImInfo( "Alpha Channel", "8-bit" ); + + + + /* + + * Allocate space for a run buffer large enough for : + + * (width * height * 3) unencoded scanlines. + + */ + + x = header.rgb_width; + + y = header.rgb_height; + + + + ImMalloc( runBuffer, unsigned char *, 4 * y * x ); + + + + rbp = runBuffer; + + pptr = ImVfbQLast( vfb ); + + for (i=1; i < x; i++) + + ImVfbSDec( vfb, pptr ); + + + + + + /* + + * Red, Green, Blue, then Alpha channel. + + */ + + for (j = 1; (j <= y); j++) + + { + + for (i = 1; (i <= x); i++) + + { + + *rbp = ImVfbQRed( vfb, pptr ); + + ImVfbSInc( vfb, pptr ); + + rbp++; + + } + + ImVfbSDec( vfb, pptr ); + + pptr = ImVfbQUp( vfb, pptr ); + + for (i=1; i < x; i++) + + ImVfbSDec( vfb, pptr ); + + } + + + + pptr = ImVfbQLast( vfb ); + + for(i=1; i < x; i++) ImVfbSDec( vfb, pptr ); + + for (j = 1; (j <= y); j++) + + { + + for (i = 1; (i <= x); i++) + + { + + *rbp = ImVfbQGreen( vfb, pptr ); + + ImVfbSInc( vfb, pptr ); + + rbp++; + + } + + ImVfbSDec( vfb, pptr ); + + pptr = ImVfbQUp( vfb, pptr ); + + for (i=1; i < x; i++) + + ImVfbSDec( vfb, pptr ); + + } + + + + pptr = ImVfbQLast( vfb ); + + for(i=1; i < x; i++) ImVfbSDec( vfb, pptr ); + + for (j = 1; (j <= y); j++) + + { + + for (i = 1; (i <= x); i++) + + { + + *rbp = ImVfbQBlue( vfb, pptr ); + + ImVfbSInc( vfb, pptr ); + + rbp++; + + } + + ImVfbSDec( vfb, pptr ); + + pptr = ImVfbQUp( vfb, pptr ); + + for (i=1; i < x; i++) + + ImVfbSDec( vfb, pptr ); + + } + + + + pptr = ImVfbQLast( vfb ); + + for(i=1; i < x; i++) ImVfbSDec( vfb, pptr ); + + for (j = 1; (j <= y); j++) + + { + + for (i = 1; (i <= x); i++) + + { + + *rbp = ImVfbQAlpha( vfb, pptr ); + + ImVfbSInc( vfb, pptr ); + + rbp++; + + } + + ImVfbSDec( vfb, pptr ); + + pptr = ImVfbQUp( vfb, pptr ); + + for (i=1; i < x; i++) + + ImVfbSDec( vfb, pptr ); + + } + + + + /* + + * Write out the buffer. + + */ + + if ( ImBinWrite( ioType, fd, fp, runBuffer, UCHAR, 1, x * y * 4 ) == -1) + + { + + free( (char *)runBuffer ); + + ImReturnBinError( ); + + } + + + + + + free( (char *)runBuffer ); + + return ( 1 ); + +} + + + + + + + +/* + + * FUNCTION + + * imRgbEncode - encode one channel of one scanline + + * + + * DESCRIPTION + + * One unencoded channel of one scanline (cIn) is encoded into + + * Iris RGB format and returned in cOut. + + * Width is the length of unencoded data. + + */ + + + +static int + +#ifdef __STDC__ + +imRgbEncode( unsigned char *cOut, unsigned char *cIn, int width ) + +#else + +imRgbEncode( cOut, cIn, width ) + + unsigned char *cOut; + + unsigned char *cIn; + + int width; + +#endif + +{ + + int cnt; + + int len; + + + + len = 0; + + + + /* + + * Loop thru the unencoded bytes + + */ + + while ( width > 0 ) + + { + + if (( width > 1 ) && ( cIn[0] == cIn[1] )) + + { + + /* Encode case */ + + for( cnt=2; cnt < width; cnt++ ) + + { + + if ( cIn[cnt] != cIn[cnt-1] ) + + break; + + if ( cnt >= 127 ) + + break; + + } + + + + /* Write out count */ + + *cOut++ = cnt; + + len++; + + + + /* Write out value */ + + *cOut++ = *cIn; + + len++; + + width -= cnt; + + cIn += cnt; + + } + + else /* Don't encode */ + + { + + for( cnt=1; cnt < width; cnt++ ) + + { + + if ((width-cnt > 1) && (cIn[cnt]==cIn[cnt+1])) + + break; + + if ( cnt >= 127 ) + + break; + + } + + + + /* Write out count (*cOut++ = 256-cnt) */ + + *cOut++ = 0x80 | cnt; + + len++; + + + + /* Copy string of pixels */ + + for ( ; cnt-- > 0; len++, width-- ) + + *cOut++ = *cIn++; + + } + + } + + + + /* + + * Mark the end of a scanline with a zero count + + */ + + *cOut++ = 0; + + len++; + + return( len ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imRgbWriteRLE8 - write an 8-bit Iris RGB file + + * + + * DESCRIPTION + + * The VFB is queried, and the RGB file header set up and written out. + + * The VFB data is then read out and converted to run-codes, and those + + * codes written out. + + */ + + + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imRgbWriteRLE8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imRgbWriteRLE8( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag list to read from */ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointer */ + + ImVfbPtr leftpptr; /* Points to start of scanlines */ + + TagEntry *tagEntry; /* Tmp table entry */ + + unsigned char *grayBuf; /* Run buffer */ + + unsigned char *buf; /* Run buffer */ + + char *tmpString; /* For the image name */ + + int x,y,z; /* Convenient short names */ + + int i,j; /* Loop counters */ + + int t; /* Table index */ + + int *offsetTable; /* Array of scanline offsets */ + + int *sizeTable; /* Array of scanline sizes */ + + int tablesize; /* size of the scanline tables */ + + int writeCount; /* Keep count of file offset */ + + int status; /* Status returned from seek */ + + int len; /* Number of bytes unencoded */ + + imRgbHeaderInfo header; /* RGB file header */ + + char message[100]; /* ImInfo message */ + + + + + + + + /* + + * RGB files are usually generated on Irises, which have an MBF + + * byte order. There is no floating point in the file so we + + * won't bother setting the float format for BIN. + + */ + + BinByteOrder( BINMBF ); + + + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + + + tagEntry = TagTableQDirect( tagTable, "image name", 0 ); + + if ( tagEntry == TAGENTRYNULL ) + + strcpy( header.rgb_name, "untitled" ); + + else + + { + + TagEntryQValue( tagEntry, &tmpString ); + + strncpy( header.rgb_name, tmpString, 80 ); + + } + + + + /* + + * Set header fields to reasonable values + + */ + + header.rgb_stype = IMRGB_ITYPE_RLE | IMRGB_BYTES_PER_PLANE; + + header.rgb_imagic = 0x01da; + + header.rgb_dim = 2; + + header.rgb_width = ImVfbQWidth( vfb ); + + header.rgb_height = ImVfbQHeight( vfb ); + + header.rgb_zsize = 1; + + header.rgb_min = 0; + + header.rgb_max = 255; + + header.rgb_wastebytes = 3; + + header.rgb_colormap = 0; + + + + /* Shorthand */ + + x = header.rgb_width; + + y = header.rgb_height; + + z = header.rgb_zsize; + + + + pptr = leftpptr = ImVfbQPtr( vfb, 0, y-1 ); + + + + tablesize = y * z * 4; /* 4 is sizeof int in file */ + + header.rgb_rowstart = IMRGBHEADERSIZE; + + header.rgb_rowsize = IMRGBHEADERSIZE + tablesize; + + header.rgb_rleend = IMRGBHEADERSIZE + (2 * tablesize); + + + + /* + + * Write out the RGB image file header + + */ + + + + if ( ImBinWriteStruct( ioType, fd, fp, &header, imRgbHeaderFields)==-1) + + ImReturnBinError( ); + + + + /* + + * Output -verbose message + + */ + + ImInfo( "Image Name", header.rgb_name ); + + ImInfo( "Byte Order", "Most Significant Byte First" ); + + + + sprintf( message, "%d x %d", header.rgb_width, header.rgb_height ); + + ImInfo( "Resolution", message ); + + + + ImInfo( "Type", "8-bit Grayscale" ); + + ImInfo( "Compression Type", "Run Length Encoded (RLE)" ); + + ImInfo( "Alpha Channel", "none" ); + + + + /* + + * Allocate offset and size tables and write out as place holders + + */ + + ImMalloc( offsetTable, int *, y * z * sizeof(int) ); + + ImMalloc( sizeTable, int *, y * z * sizeof(int) ); + + + + memset( offsetTable, 0x00, y * z * sizeof(int) ); + + memset( sizeTable, 0x00, y * z * sizeof(int) ); + + + + if ( ImBinWrite( ioType, fd, fp, offsetTable, INT, 4, y*z ) == -1 ) + + { + + free( (char *)offsetTable ); + + ImReturnBinError( ); + + } + + if ( ImBinWrite( ioType, fd, fp, sizeTable, INT, 4, y*z ) == -1 ) + + { + + free( (char *)offsetTable ); + + free( (char *)sizeTable ); + + ImReturnBinError( ); + + } + + + + /* + + * Keep track of how many bytes written so far + + */ + + writeCount = IMRGBHEADERSIZE + (2 * tablesize); + + + + /* + + * Allocate run buffers large enough for one scanline per color + + */ + + ImCalloc( grayBuf, unsigned char *, x, 1 ); + + ImCalloc( buf, unsigned char *, x*2, 1 ); + + + + /* + + * Loop through the scan lines. Fill the buffers with channel values, + + * encode them, and then write them out. + + */ + + t = 0; + + + + for( i=0; i + +//#include + +#include "iminternal.h" + + + + + +/* + + * RLA/RLB - Wavefront image file + + * For information on these structures, how to use them, etc. please + + * see imfmt.c. + + */ + +#ifdef __STDC__ + +static int imRlaRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imRlaWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +#else + +static int imRlaRead( ), imRlaWrite( ); + +#endif + +static char *imRlaNames[ ] = { "rla", "rlb", NULL }; + +static ImFileFormatReadMap imRlaReadMap[ ] = + +{ + + /* in out */ + + /* type,ch,dep, attr. VFB type attr. */ + + { RGB,3,8, RLE, IMVFBRGB, 0 }, + + { RGB,3,8, RLE|A, IMVFBRGB, A }, + + { -1, 0, -1, 0 }, + +}; + +static ImFileFormatWriteMap imRlaWriteMap[ ] = + +{ + + /* in out */ + + /* VFB type, attr., type,ch,dep, attr., func */ + + { IMVFBRGB, 0, RGB,3,8, RLE|A, imRlaWrite }, + + { IMVFBRGB, A, RGB,3,8, RLE|A, imRlaWrite }, + + { -1, 0, -1, 0, NULL }, + +}; + + + +static ImFileMagic imFileRlaMagic []= + +{ + + { 0, 0, NULL }, + +}; + + + +ImFileFormat ImFileRlaFormat = + +{ + + imRlaNames, "Wavefront raster image file", + + "Wavefront", + + "24-bit RGB and 32-bit RGB+alpha RLE-compressed image files in both\n\ +RLB and the older RLA format.", + "24-bit RGB and 32-bit RGB+alpha RLE-compressed image files in RLB\n\ +format.", + + imFileRlaMagic, + + IMNOMULTI, IMNOPIPE, + + IMNOMULTI, IMNOPIPE, + + imRlaRead, imRlaReadMap, imRlaWriteMap + +}; + + + + + +/* + + * TYPEDEF & STRUCTURE + + * imRlaHeaderInfo - RLA file header information + + * imRlaHeaderFields - Description for Binio package + + * + + * DESCRIPTION + + * A RLA file's header contains the image's width, height, and depth, + + * and many other fields. + + */ + + + +#define IMRLANGAMMA (16) + +#define IMRLANPRI (24) + +#define IMRLANNAME (128) + +#define IMRLANPROG (64) + +#define IMRLANSTR (32) + +#define IMRLANDATE (20) + +#define IMRLANASPECT (24) + +#define IMRLANAR (8) + +#define IMRLANSPACE (100) + +#define IMRLAHEADERSIZE (740) + + + +typedef struct imRlaHeaderInfo + +{ + + short window_left; /* Boundaries of complete image */ + + short window_right; + + short window_bottom; + + short window_top; + + short active_window_left; /* Boundaries of non-zero image */ + + short active_window_right; + + short active_window_bottom; + + short active_window_top; + + short frame; /* Frame number */ + + short storage_type; /* Byte or word data */ + + short num_chan; /* Number of image channels */ + + short num_matte; /* Number of matte channels */ + + short num_aux; /* Number of aux channels */ + + short aux_mask; /* Aux channel type mask */ + + char gamma[IMRLANGAMMA]; /* Image storage gamma */ + + char red_pri[IMRLANPRI]; /* Image red primary chromaticity */ + + char green_pri[IMRLANPRI]; /* Image grn primary chromaticity */ + + char blue_pri[IMRLANPRI]; /* Image blu primary chromaticity */ + + char white_pt[IMRLANPRI]; /* White point */ + + int job_num; /* Job Number */ + + char name[IMRLANNAME]; /* Original file name */ + + char desc[IMRLANNAME]; /* File description number */ + + char program[IMRLANPROG]; /* Creating program name */ + + char machine[IMRLANSTR]; /* Creating machine name */ + + char user[IMRLANSTR]; /* Creating user */ + + char date[IMRLANDATE]; /* Creation date */ + + char aspect[IMRLANASPECT]; /* Image aspect type */ + + char aspect_ratio[IMRLANAR]; /* Image aspect ratio */ + + char chan[IMRLANSTR]; /* Image color space type */ + + short field; /* Rendered on fields flag */ + + short filter_type; /* Post filtered interleaved flag*/ + + int magic_number; /* unused */ + + int lut_size; /* unused */ + + int user_space_size; /* unused */ + + int wf_space_size; /* unused */ + + short lut_type; /* unused */ + + short mix_type; /* unused */ + + short encode_type; /* unused */ + + short padding; /* unused */ + + char space[IMRLANSPACE]; /* Unused expansion space */ + +} imRlaHeaderInfo; + + + + + +BinField imRlaHeaderFields[ ] = + +{ + + { SHORT, 2, 1 }, /* Window boundaries */ + + { SHORT, 2, 1 }, + + { SHORT, 2, 1 }, + + { SHORT, 2, 1 }, + + { SHORT, 2, 1 }, /* Active window boundaries */ + + { SHORT, 2, 1 }, + + { SHORT, 2, 1 }, + + { SHORT, 2, 1 }, + + { SHORT, 2, 1 }, /* Frame */ + + { SHORT, 2, 1 }, /* Storage type */ + + { SHORT, 2, 1 }, /* Number of image channels */ + + { SHORT, 2, 1 }, /* Number of matte channels */ + + { SHORT, 2, 1 }, /* Number of aux channels */ + + { SHORT, 2, 1 }, /* Aux channel type mask */ + + { CHAR, 1, IMRLANGAMMA }, /* Image storage gamma */ + + { CHAR, 1, IMRLANPRI }, /* Image red primary chromaticity */ + + { CHAR, 1, IMRLANPRI }, /* Image grn primary chromaticity */ + + { CHAR, 1, IMRLANPRI }, /* Image blu primary chromaticity */ + + { CHAR, 1, IMRLANPRI }, /* Image whi primary chromaticity */ + + { INT, 4, 1 }, /* Job number */ + + { CHAR, 1, IMRLANNAME }, /* Original file name */ + + { CHAR, 1, IMRLANNAME }, /* File description */ + + { CHAR, 1, IMRLANPROG }, /* Creating program name */ + + { CHAR, 1, IMRLANSTR }, /* Creating machine name */ + + { CHAR, 1, IMRLANSTR }, /* Creating user name */ + + { CHAR, 1, IMRLANDATE }, /* Creation date */ + + { CHAR, 1, IMRLANASPECT }, /* Image aspect type */ + + { CHAR, 1, IMRLANAR }, /* Image aspect ratio */ + + { CHAR, 1, IMRLANSTR }, /* Image color space type */ + + { SHORT, 2, 1 }, /* Rendered on fields flag */ + + { SHORT, 2, 1 }, /* Post filtered interleaved flag*/ + + { INT, 4, 1 }, /* unused */ + + { INT, 4, 1 }, /* unused */ + + { INT, 4, 1 }, /* unused */ + + { INT, 4, 1 }, /* unused */ + + { SHORT, 2, 1 }, /* unused */ + + { SHORT, 2, 1 }, /* unused */ + + { SHORT, 2, 1 }, /* unused */ + + { SHORT, 2, 1 }, /* unused */ + + { CHAR, 1, IMRLANSPACE }, /* Unused expansion space */ + + { 0, 0, 0 } + +}; + + + + + + + + + + + +/* + + * MACRO + + * imRlaFrBuf - Free six chunks of memory + + * + + * DESCRIPTION + + * Call free six times to free the six chunks of memory pointed to by + + * a-f. + + */ + + + +#define imRlaFrBuf(a,b,c,d,e,f) free((a));free((b));free((c));free((d));free((e));free((f)) + + + + + + + + + + + +/* + + * FUNCTION + + * imPrRlaHeader - print out the header of a rla file + + * + + * DESCRIPTION + + * The contents of the ImRlaHeader structure is printed on the + + * standard error. + + */ + + + +static void + +#ifdef __STDC__ + +imRlaPrHeader(imRlaHeaderInfo *p) + +#else + +imRlaPrHeader(p) + + imRlaHeaderInfo *p; + +#endif + +{ + + char message[200]; /* contains information for ImInfo */ + + + + ImInfo ("Image Name", p->name); + + + + ImInfo ("Byte Order", "Most Significant Byte First"); + + + + sprintf (message, "%d x %d", + + ( (p->active_window_right) - (p->active_window_left) + 1), + + ( (p->active_window_top) - (p->active_window_bottom) + 1)); + + + + ImInfo ("Resolution", message); + + + + if (p->storage_type==0) + + sprintf ( message , "24 bit RGB"); + + else sprintf ( message , "3 word RGB"); + + ImInfo ("Type",message); + + + + if (p->num_matte>0) + + { + + if (p->storage_type==0) + + sprintf ( message , "8 bit"); + + else sprintf ( message , "1 word"); + + } + + + + else sprintf (message, "None"); + + + + ImInfo ("Alpha Channel", message); + + + +#ifdef DEBUG + + fprintf(stderr,"Channels: Image %d Matte %d Aux %d Aux Mask %02x\n", + + p->num_chan, p->num_matte, p->num_aux, p->aux_mask); + + + + fprintf(stderr,"Job Number %ld Name \"%s\"\n",p->job_num,p->name); + + fprintf(stderr,"Desc \"%s\"\n",p->desc); + + fprintf(stderr,"Program \"%s\" Machine \"%s\"\n",p->program,p->machine); + + + + fprintf(stderr,"User %s Date %s Aspect %s Aspect ratio %s\n", + + p->user,p->date,p->aspect,p->aspect_ratio); + + fprintf(stderr,"Chan %s Field %d Filter_type %d Magic %d\n", + + p->chan, p->field, p->filter_type, p->magic_number ); + +#endif + + + + sprintf (message, "%d", p->frame); + + ImInfo ("Frame Number", message); + + + + ImInfo ( "Gamma", p->gamma); + + + + ImInfo ( "Red Primary", p->red_pri); + + + + ImInfo ( "Green Primary", p->green_pri); + + + + ImInfo ( "Blue Primary", p->blue_pri); + + + + ImInfo ( "White Point", p->white_pt); + + + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imRlaDecode - decode one channel of one scanline + + * + + * DESCRIPTION + + * One channel of one scanline (cIn) in Wavefront RLA format is decoded + + * and returned in cOut. len is the number of bytes in cIn. + + */ + +static void + +#ifdef __STDC__ + +imRlaDecode( unsigned char *cIn, unsigned char *cOut, int len ) + +#else + +imRlaDecode( cIn, cOut, len ) + + unsigned char *cIn; + + unsigned char *cOut; + + int len; + +#endif + +{ + + int cnt; + + + + /* + + * Loop through the bytes to be decoded + + */ + + while( len > 0 ) + + { + + cnt = *cIn++; + + len--; + + if ( cnt < 128 ) + + { + + /* + + * Repeat pixel value cnt+1 times + + */ + + while ( cnt-- >= 0 ) + + *cOut++ = *cIn; + + cIn++; + + len--; + + } + + else + + /* + + * Just copy the unencoded bytes directly + + */ + + for ( cnt=256-cnt; cnt-- > 0; len-- ) + + *cOut++ = *cIn++; + + } + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imRlaRead - read an Wavefront RLA file + + * + + * DESCRIPTION + + * The file header is read and the size of the image determined. + + * Space is allocated for the VFB and the run codes read in to + + * a run buffer. The run buffer is then expanded into straight + + * RGB values in the VFB and the completed VFB added to the tag table. + + */ + + + +static int /* Returns # tags read in */ + +#ifdef __STDC__ + +imRlaRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imRlaRead( ioType, fd, fp, flagsTable, tagTable ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag list to add to */ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointer */ + + ImVfbPtr leftpptr; /* Points to start of scanlines */ + + imRlaHeaderInfo header; /* RLA file header */ + + unsigned char *redBuf; /* Run buffer */ + + unsigned char *grnBuf; /* Run buffer */ + + unsigned char *bluBuf; /* Run buffer */ + + unsigned char *alpBuf; /* Run buffer */ + + unsigned char *buf; /* Run buffer */ + + short len; /* Number of bytes unencoded */ + + int *offsetTable; /* Array of scanline offsets */ + + int status; /* Status returned from seek */ + + int x,y,dep; /* Convenient short names */ + + int i,j; /* Loop counters */ + + int width; /* true width of image */ + + int height; /* true height of image */ + + int bPad; /* bottom padding amount */ + + int lPad; /* left padding amount */ + + unsigned char* freak; /* See the paragraph below */ + + + + /* The variable 'freak' is to have a totally gratuitous + + * cast from an int into an unsigned char*. The purpose + + * of this is to stop the decalpha c++ compiler from making + + * some sort of weird crazy screwed up optimization that + + * results in an endless loop. + + * + + * I don't understand why this works. But take out the + + * cast, and this routine won't work on decalpha's with c++. + + * + + * You don't believe me? Try it! + + * + + * I haven't had enough free time to investigate this compiler + + * bug, or pinpoint it very well. + + */ + + + + /* + + * RLA files are usually generated on Irises, which have an MBF + + * byte order. There is no floating point in the file so we + + * won't bother setting the float format for BIN. + + */ + + BinByteOrder( BINMBF ); + + + + + + /* + + * Read in the header. + + */ + + if (ImBinReadStruct(ioType, fd, fp, &header, imRlaHeaderFields ) == -1) + + { + + ImReturnBinError( ); + + } + + + + imRlaPrHeader( &header ); + + + + width = 1 + header.window_right - header.window_left; + + height = 1 + header.window_top - header.window_bottom; + + + + x = 1 + header.active_window_right - header.active_window_left; + + y = 1 + header.active_window_top - header.active_window_bottom; + + + + bPad = (int) (header.active_window_bottom - header.window_bottom); + + lPad = (int) (header.active_window_left - header.window_left); + + /* + + * Here's the moronic hack of all hacks to defeat + + * the evil decalpha c++ compiler: + + */ + + freak = (unsigned char*)&lPad; + + + + dep = header.num_chan + header.num_matte; + + + + + + /* + + * Check the important header parameters for legal bounds. + + * header.chan must be 'rgb' + + * x and y must have positive active window size + + * image depth must be 3 (RGB) or 4 (RGBA) + + * header.num_aux must be 0 + + * + + * We'd like to check these as well, however while RLB files have + + * them, RLA files don't, so we just skip checking their values. + + * header.field must be 0 + + * header.magic_number must be 0 + + * header.filter_type must be 0 + + */ + + if ((strcmp( header.chan, "rgb" ) != 0) || x < 1 || y < 1 || + + (dep != 4 && dep != 3) || header.num_aux != 0 ) + + { + + ImErrNo = IMESYNTAX; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + + + /* + + * Allocate space for the offset table and read it in + + */ + + ImMalloc( offsetTable, int *, sizeof(int) * y ); + + if ( ImBinRead( ioType, fd, fp, offsetTable, INT, 4, y ) == -1 ) + + { + + free( (char *)offsetTable ); + + ImReturnBinError( ); + + } + + + + + + /* + + * Allocate a VFB of the required size and clear it. + + */ + + if ( header.num_matte == 1 ) + + vfb = ImVfbAlloc( width, height, IMVFBRGB | IMVFBALPHA ); + + else + + vfb = ImVfbAlloc( width, height, IMVFBRGB ); + + if ( vfb == IMVFBNULL ) + + { + + free( (char *)offsetTable ); + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + ImVfbClear( ImVfbQFields( vfb ), 0, vfb ); + + + + + + /* + + * Allocate run buffers large enough for one scanline per channel. + + */ + + ImCalloc( redBuf, unsigned char *, x, 1 ); + + ImCalloc( grnBuf, unsigned char *, x, 1 ); + + ImCalloc( bluBuf, unsigned char *, x, 1 ); + + ImCalloc( alpBuf, unsigned char *, x, 1 ); + + ImCalloc( buf, unsigned char *, x*2, 1 ); + + + + + + /* + + * Loop through the scan lines. Read in a scanline and decode it. + + * Once all of the channels are decoded, copy them into the vfb, + + * going from bottom to top. + + */ + + pptr = leftpptr = ImVfbQPtr( vfb, 0, height-bPad-1 ); + + for( i=0; i 0 ) + + { + + if (( width > 1 ) && ( cIn[0] == cIn[1] )) + + { + + /* Encode case */ + + for( cnt=2; cnt < width; cnt++ ) + + { + + if ( cIn[cnt] != cIn[cnt-1] ) + + break; + + if ( cnt >= 127 ) + + break; + + } + + + + /* Write out count */ + + *cOut++ = cnt-1; + + len++; + + + + /* Write out value */ + + *cOut++ = *cIn; + + len++; + + width -= cnt; + + cIn += cnt; + + } + + else /* Don't encode */ + + { + + for( cnt=1; cnt < width; cnt++ ) + + { + + if ((width-cnt > 1) && (cIn[cnt]==cIn[cnt+1])) + + break; + + if ( cnt >= 127 ) + + break; + + } + + + + /* Write out count */ + + *cOut++ = 256 - cnt; + + len++; + + + + /* Copy string of pixels */ + + for ( ; cnt-- > 0; len++, width-- ) + + *cOut++ = *cIn++; + + } + + } + + return( len ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imRlaWrite - write an Wavefront RLA file + + * + + * DESCRIPTION + + * The VFB is queried, and the RLA file header set up and written out. + + * The VFB data is then read out and converted to run-codes, and those + + * codes written out. + + */ + + + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imRlaWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imRlaWrite( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag list to read from */ + +#endif + +{ + + int x,y,dep; /* Convenient short names */ + + ImVfb *vfb; /* Read in image */ + + imRlaHeaderInfo header; /* RLA file header */ + + time_t seconds; /* Return value from time() */ + + ImVfbPtr pptr; /* Pixel pointer */ + + ImVfbPtr leftpptr; /* Points to start of scanlines */ + + unsigned char *redBuf; /* Run buffer */ + + unsigned char *grnBuf; /* Run buffer */ + + unsigned char *bluBuf; /* Run buffer */ + + unsigned char *alpBuf; /* Run buffer */ + + unsigned char *buf; /* Run buffer */ + + int *offsetTable; /* Array of scanline offsets */ + + int writeCount; /* Keep count of file offset */ + + short len; /* Number of bytes unencoded */ + + int i,j; /* Loop counters */ + + + + char *name; /* Login name */ + + char tmp[100]; /* Temp buffer */ + + struct passwd *pass; /* Password file entry */ + + + + + + /* + + * RLA files are usually generated on Irises, which have an MBF + + * byte order. There is no floating point in the file so we + + * won't bother setting the float format for BIN. + + */ + + BinByteOrder( BINMBF ); + + + + + + /* + + * Set up the header and write it out. + + */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + x = ImVfbQWidth( vfb ); + + y = ImVfbQHeight( vfb ); + + + + header.window_right = x-1; /* Right side */ + + header.window_top = y-1; /* Top side */ + + header.window_left = 0; /* Left side */ + + header.window_bottom = 0; /* Bottom side */ + + + + header.active_window_right = x-1; /* Right side */ + + header.active_window_top = y-1; /* Top side */ + + header.active_window_left = 0; /* Left side */ + + header.active_window_bottom= 0; /* Bottom side */ + + + + header.frame = 1; /* First frame (anything) */ + + header.storage_type = 0; /* Image data (must be 0) */ + + header.num_chan = 3; /* RGB (must be 3) */ + + header.num_matte = 1; /* Alpha included (0 or 1) */ + + header.num_aux = 0; /* no aux channels (must be 0) */ + + header.aux_mask = 0; /* no aux channels (must be 0) */ + + header.job_num = 0; /* job 0 (anything) */ + + header.field = 0; /* not on fields (0 or 1) */ + + header.filter_type = 0; /* non-interleaved (0 or 1) */ + + header.magic_number = 0; /* no magic (must be 0) */ + + header.lut_size = 0; /* no CLT (must be 0) */ + + header.user_space_size = 0; /* no user space (must be 0) */ + + header.wf_space_size = 0; /* no wf space (must be 0) */ + + header.lut_type = 0; /* no CLT (must be 0) */ + + header.mix_type = 0; /* no mix (must be 0) */ + + header.encode_type = 0; /* no encoding (must be 0) */ + + header.padding = 0; /* no padding (must be 0) */ + + + + strcpy( header.gamma, "2.200" ); + + strcpy( header.red_pri, "00.6700 00.3300" ); + + strcpy( header.green_pri, "00.2100 00.7100" ); + + strcpy( header.blue_pri, "00.1400 00.0800" ); + + strcpy( header.white_pt, "00.3100 00.3160" ); + + + + strncpy( header.name, ImErrorFileName, IMRLANNAME ); + + strcpy( header.desc, "image file" ); + + strncpy( header.program, ImErrorProgramName, IMRLANPROG ); + + strcpy( header.chan, "rgb" ); + + + + /* + + if ( (name = getlogin( )) == NULL ) + + { + + pass = getpwuid( getuid( ) ); + + name = pass->pw_name; + + } + + */ + + + + name = "Creator"; + + + + strncpy( header.user, name, IMRLANSTR ); + + + + strcpy( header.aspect, "user defined"); + + sprintf( tmp, "%3.2f", (float)x/y ); + + strncpy( header.aspect_ratio, tmp, IMRLANAR ); + + + + seconds = time( (time_t *)NULL ); + + strncpy( header.date, ctime( &seconds ), 19 ); + + + +#ifndef WIN32 + + gethostname( header.machine, IMRLANSTR ); + +#else + + strcpy(header.machine, "jaws"); + +#endif + + + + /* + + * Write out the RLA image file header + + */ + + if (( writeCount = ImBinWriteStruct( ioType, fd, fp, &header, imRlaHeaderFields )) == -1 ) + + { + + ImReturnBinError( ); + + } + + if ( writeCount != IMRLAHEADERSIZE ) + + { + + ImErrNo = IMESYS; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + imRlaPrHeader( &header ); + + + + /* + + * Allocate an offset table and initialize it to zeroes. We'll + + * write it out as zeroes now, fill it while we encode the data, + + * then seek back and write the real table out when we're done. + + */ + + ImMalloc( offsetTable, int *, sizeof(int) * y ); + + memset( offsetTable, 0x00, sizeof(int) * y ); + + if ( ImBinWrite( ioType, fd, fp, offsetTable, INT, 4, y ) == -1 ) + + { + + free( (char *)offsetTable ); + + ImReturnBinError( ); + + } + + writeCount += 4 * y; + + + + + + /* + + * Allocate run buffers large enough for one scanline per color + + */ + + ImCalloc( redBuf, unsigned char *, x, 1 ); + + ImCalloc( grnBuf, unsigned char *, x, 1 ); + + ImCalloc( bluBuf, unsigned char *, x, 1 ); + + ImCalloc( alpBuf, unsigned char *, x, 1 ); + + ImCalloc( buf, unsigned char *, x*2, 1 ); + + + + + + /* + + * Loop through the scan lines, from the bottom up. Fill the + + * buffers with channel values, encode them, and then write them out. + + */ + + pptr = leftpptr = ImVfbQPtr( vfb, 0, y-1 ); + + for( i=0; i + +#include + +#include "unistd.h" + +#include "iminternal.h" + + + + + + + + + + + +/* + + ** FORMAT + + ** RLE - Utah Raster Toolkit Run-Length Encoded image file + + ** + + ** AKA + + ** + + ** FORMAT REFERENCES + + ** Design of the Utah RLE Format, Spencer W. Thomas, University + + ** of Utah + + ** + + ** CODE CREDITS + + ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1991. + + ** + + ** DESCRIPTION + + ** RLE files start with a fixed size header giving: + + ** + + ** magic number + + ** image location (offset from (0,0) of bottom left corner) + + ** image size + + ** flags + + ** number of channels per image pixel + + ** number of bits in a channel + + ** number of channels per color map entry + + ** number of color map entries + + ** + + ** See the imRleHeaderInfo comments below for more details. + + ** + + ** Following the fixed size header is an optional background color + + ** for flooding the image prior to loading in pixel data. + + ** + + ** Next is a variable length color map. + + ** + + ** Next are zero or more comments. + + ** + + ** Finally comes the image data. Image data contains a variety of + + ** opcodes telling how to uncompress the data. Opcodes and their + + ** operands come in one of two flavors: short and long. + + ** + + ** short form: byte 0: opcode + + ** byte 1: operand + + ** + + ** long form: byte 0: opcode + + ** byte 1: unused + + ** byte 2-3: LBF operand + + ** + + ** The long form is used if the operand is a value too large to fit + + ** in one byte. The long form is flagged by the setting of the IMLONGOP + + ** bit in the opcode's byte. + + ** + + ** The image opcodes control three state variables: + + ** x = current X position on scanline + + ** y = current Y position in image + + ** channel = current channel + + ** + + ** x varies from 0 (left) to the image size - 1 (right). + + ** + + ** y varies from 0 (bottom) to the image size - 1 (top). Note that + + ** this is the opposite of VFB's where line 0 is the top line, not + + ** the bottom. + + ** + + ** IMOPSetColor + + ** Select which channel of the image all following data is to + + ** be put into. operand is the channel number. By convention: + + ** 0 = color index, or red + + ** 1 = green + + ** 2 = blue + + ** 255 = alpha + + ** This also sets the current 'x' position in the image to 0. + + ** + + ** IMOPSkipLines + + ** The current 'y' position is incremented by the operand. + + ** This also sets the current 'x' position in the image to 0. + + ** + + ** IMOPSkipPixels + + ** The current 'x' position is incremented by the operand. + + ** + + ** IMOPPixelData + + ** The operand is one less than the count of literal 1-byte + + ** pixel values that follow. If that count (operand + 1) is + + ** odd, a single pad byte is added to bring the literal pixel + + ** run to a 16-bit boundary. After processing the run of literal + + ** pixel values, the current 'x' position will be (operand+1) + + ** pixels further to the right. + + ** + + ** IMOPRunData + + ** The operand is one less than the count of image pixels that + + ** should be filled with the single 1-byte pixel value that + + ** follows. A single pad byte is added to bring things back to + + ** a 16-bit boundary. After processing the run of identical pixel + + ** values, the current 'x' position will be (operand+1) pixels + + ** further to the right. + + ** + + ** IMOPEOF + + ** The end of the file. An I/O EOF also ends things. + + ** + + ** USE OF BACKGROUND COLOR + + ** The background color scheme of RLE reasons that most images have a + + ** background color that occurs quite a bit. Rather than represent + + ** that color as a run, disk space can be saved by using the IMOPSkipPixels + + ** opcode to skip over those background pixels. + + ** + + ** If an incomming image has a background color, the IMRLE_CLEARFIRST bit + + ** will be set in the header's flags word, and the IMRLE_NOBACKGROUND bit + + ** not set. The background color to use then follows the header. + + ** + + ** One of two approaches may be used in handling background colors: + + ** + + ** Initialize the image to the background color then treat + + ** IMOPSkipPixels as a skip. + + ** + + ** Don't initialize the image but treat an IMOPSkipPixels as a + + ** fill with the background color. + + ** + + ** We have chosen to do the first approach. + + ** + + ** COLOR MAP STORAGE + + ** The color map is stored as a series of color channel's (red, green, + + ** blue). With each of these color channels, the entry value is stored + + ** starting with entry 0. + + ** + + ** Each color map entry value is 16-bits wide. RLE documentation says + + ** the upper 8-bits should be used on systems that only support 8-bit + + ** color channels (virtually all current graphics hardware). The lower + + ** 8-bits should be ignored. + + ** + + ** So, a color map is a series of nested structures that can be + + ** read out with looping like: + + ** + + ** for each color channel (header's ncmap field), + + ** for each entry (header's cmaplen field sorta), + + ** entry value = short << 8 + + ** + + ** Note that the header gives the color map length by specifying the + + ** power of 2 to use. So, for a 256 entry color map, cmaplen would be 8. + + ** + + ** RESTRICTIONS + + ** The RLE format can support image configurations that don't really + + ** make sense, such as a 5-channel pixel image where each channel has + + ** a 42-channel color map. Huh? + + ** + + ** In the interests of simplicity and to reduce the RLE format to the + + ** common use case, we only support the following combinations: + + ** + + ** 1 channel per pixel + + ** with or without a 3-channel (RGB) color map + + ** with or without an alpha plane + + ** mapped to an IMVFBINDEX8 ( | IMVFBALPHA) + + ** + + ** 3 channels per pixel + + ** with or without a color map + + ** with or without an alpha plane + + ** mapped to an IMVFBRGB ( | IMVFBALPHA) + + ** + + ** Any other combination is complained about and rejected. + + ** + + ** When writing images, we do not set the IMRLE_CLEARFIRST bit in the header + + ** and do not worry about a background color. To do so would require + + ** us to scan the VFB and make some sort of guess about what would be + + ** a good color to use as the background. This is timeconsuming and + + ** really not all that worthwhile. Consider: + + ** + + ** An IMOPSkipPixels opcode/operand takes 2-4 bytes. + + ** + + ** An IMOPRunData opcode/operand and pixel value takes 4-6 bytes. + + ** + + ** We are talking about a savings of (hold your hats now), 2 bytes per + + ** use. Sorry, not worth the effort or time of scanning the image first. + + ** + + ** When writing color maps, VFB CLT's can be any length, but RLE CLT's + + ** are constrained to be a power of 2 in length. So, when we have to + + ** write a color map bigger than the VFB's CLT, we fill in with black. + + **/ + + + +/* + + * RLE - Utah Raster Toolkit run-length encoded image file + + * For information on these structures, how to use them, etc. please + + * see imfmt.c. + + */ + +#ifdef __STDC__ + +static int imRleRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imRleWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +#else + +static int imRleRead( ); + +static int imRleWrite( ); + +#endif + +static char *imRleNames[ ] = { "rle", NULL }; + +static unsigned char imRleMagicNumber[ ] = { 0xCC, 0x52 }; + +static ImFileFormatReadMap imRleReadMap[ ] = + +{ + + /* in out */ + + /* type,ch,dep, attr. VFB type attr. */ + + { IN,1,8, RLE, IMVFBINDEX8, 0 }, + + { IN,1,8, RLE|C, IMVFBINDEX8, C }, + + { IN,1,8, RLE|A, IMVFBINDEX8, A }, + + { IN,1,8, RLE|C|A,IMVFBINDEX8, C|A }, + + { RGB,3,8, RLE, IMVFBRGB, 0 }, + + { RGB,3,8, RLE|A, IMVFBRGB, A }, + + { -1, 0, -1, 0 }, + +}; + +static ImFileFormatWriteMap imRleWriteMap[ ] = + +{ + + /* in out */ + + /* VFB type, attr., type,ch,dep, attr., func */ + + { IMVFBINDEX8, C, IN,1,8, RLE|C, imRleWrite }, + + { IMVFBINDEX8, C|A, IN,1,8, RLE|C|A,imRleWrite }, + + { IMVFBINDEX8, A, IN,1,8, RLE|A, imRleWrite }, + + { IMVFBINDEX8, 0, IN,1,8, RLE, imRleWrite }, + + { IMVFBRGB, 0, RGB,3,8, RLE, imRleWrite }, + + { IMVFBRGB, A, RGB,3,8, RLE|A, imRleWrite }, + + { -1, 0, -1, 0, NULL }, + +}; + + + +static ImFileMagic imFileRleMagic[]= + +{ + + { 0, 2, imRleMagicNumber }, + + { 0, 0, NULL}, + +}; + + + +ImFileFormat ImFileRleFormat = + +{ + + imRleNames, "Utah Run length encoded image file", + + "Utah Raster Toolkit, University of Utah", + + "8-bit RLE-compressed color index image files, with or without CLT,\n\ +with or without 8-bit Alpha plane. 24-bit RGB RLE-compressed image\n\ +files, with or without 8-bit Alpha plane.", + "8-bit RLE-compressed color index image files, with or without CLT,\n\ +with or without 8-bit Alpha plane. 24-bit RGB RLE-compressed image\n\ +files, with or without 8-bit Alpha plane.", + imFileRleMagic, + IMNOMULTI, IMPIPE, + + IMNOMULTI, IMPIPE, + + imRleRead, imRleReadMap, imRleWriteMap + +}; + + + + + + + + + +/* + + * TYPEDEF & STRUCT + + * imRleHeaderInfo - RLE file header + + * + + * DESCRIPTION + + * RLE files start with a fixed size file header. + + * + + * rle_magic is the RLE magic number, and is always 0xCC52. + + * + + * rle_xpos, _ypos, _xsize, and _ysize give the position and size of + + * the image. We ignore the position, and use the size to determine + + * how big a VFB to allocate. + + * + + * rle_flags indicates how the background of the VFB should be treated. + + * + + * rle_ncolors is the number of color channels saved, between 0 and 254. + + * A color-index image would be 1, while an RGB image would be 3. + + * + + * rle_pixelbits is the number of bits per pixel per color channel. + + * This must be 8 (original Utah RLE code makes the same restriction). + + * + + * rle_ncmap is the number of channels in the color map. In most cases + + * this will be 3, for R, G, and B. + + * + + * rle_cmaplen is the log base 2 of the length of the color map for + + * each channel. For instance, for a 256 entry CLT, rle_cmaplen would + + * be 8. + + * + + * EXAMPLES + + * An 8-bit color index image: + + * rle_ncolors = 1 -- just a CLT index + + * rle_pixelbits = 8 -- 8-bits wide CLT index + + * rle_ncmap = 3 -- RGB for each CLT entry + + * rle_cmaplen = 8 -- 256 entries long + + * + + * A 24-bit RGB image: + + * rle_ncolors = 3 -- R, G, and B per pixel + + * rle_pixelbits = 8 -- 8-bits for R, for G, and for B + + * rle_ncmap = 0 -- No CLT + + * rle_cmaplen = 0 -- No CLT + + * + + * A 24-bit RGB image with a CLT: + + * rle_ncolors = 3 -- R, G, and B per pixel + + * rle_pixelbits = 8 -- 8-bits for R, for G, and for B + + * rle_ncmap = 3 -- RGB for each cLT entry + + * rle_cmaplen = 8 -- 256 entries long + + */ + + + +typedef struct imRleHeaderInfo + +{ + + unsigned short rle_magic; /* Magic number */ + + unsigned short rle_xpos; /* X position of image */ + + unsigned short rle_ypos; /* Y position of image */ + + unsigned short rle_xsize; /* Width of image */ + + unsigned short rle_ysize; /* Height of image */ + + unsigned char rle_flags; /* Flags */ + + unsigned char rle_ncolors; /* # of color channels saved */ + + unsigned char rle_pixelbits; /* # of bits in a pixel */ + + unsigned char rle_ncmap; /* # of color channels in CLT */ + + unsigned char rle_cmaplen; /* Length of CLT */ + +} imRleHeaderInfo; + + + +static BinField imRleHeaderFields[] = + +{ + + USHORT, 2, 1, /* rle_magic */ + + USHORT, 2, 1, /* rle_xpos */ + + USHORT, 2, 1, /* rle_ypos */ + + USHORT, 2, 1, /* rle_xsize */ + + USHORT, 2, 1, /* rle_ysize */ + + UCHAR, 1, 1, /* rle_flags */ + + UCHAR, 1, 1, /* rle_ncolors */ + + UCHAR, 1, 1, /* rle_pixelbits */ + + UCHAR, 1, 1, /* rle_ncmap */ + + UCHAR, 1, 1, /* rle_cmaplen */ + + 0, 0, 0 + +}; + + + + + +/* + + * imRleHeaderInfo field values and flags: + + */ + +#define IMRLEMAGIC (0xCC52) /* RLE magic number */ + + + +#define IMRLE_CLEARFIRST ( 0x1 ) /* If set, clear framebuffer */ + +#define IMRLE_NOBACKGROUND ( 0x2 ) /* If set, no bg color supplied */ + +#define IMRLE_ALPHA ( 0x4 ) /* If set, alpha channel present*/ + +#define IMRLE_COMMENT ( 0x8 ) /* If set, comments present */ + + + + + + + + + + + +/* + + * Opcodes + + */ + +#define IMOPSkipLines 1 + +#define IMOPSetColor 2 + +#define IMOPSkipPixels 3 + +#define IMOPPixelData 5 + +#define IMOPRunData 6 + +#define IMOPEOF 7 + + + +#define IMLONGOP 0x40 + + + + + +/* + + * Which channel we are reading into. + + */ + +#define IMONINDEX8 0 + +#define IMONRED 1 + +#define IMONGREEN 2 + +#define IMONBLUE 3 + +#define IMONALPHA 4 + + + + + + + + + + + +/* + + * FUNCTION + + * imRleRead - read an RLE file + + * + + * DESCRIPTION + + * The fixed size header is read in and the magic number checked. + + * The header is also checked for reasonable and supported variations + + * on number of image and color map channels. + + * + + * If present, a background color is read in. + + * + + * If present, a color map is read in. + + * + + * If present, comments are read in and tossed. + + * + + * If present, an image is read in and the various opcodes processed. + + */ + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imRleRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imRleRead( ioType, fd, fp, flagsTable, tagTable ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file buffer pointer */ + + TagTable *flagsTable; /* Input parameter/flags */ + + TagTable *tagTable; /* Tag table to add to */ + +#endif + +{ + + imRleHeaderInfo header; /* File header */ + + char msg[80]; /* Error message */ + + unsigned char bgColor[3]; /* Background color (if any) */ + + int fields; /* Fields for VFB */ + + unsigned int len; /* Length of comments */ + + int i, j; /* Counters */ + + int x, y; /* Row and column counters */ + + + + int cmaplen; /* Length of color map */ + + unsigned short *cmap; /* Color map buffer */ + + unsigned short *pRed; /* Pointer into color map buffer*/ + + unsigned short *pGreen; /* Pointer into color map buffer*/ + + unsigned short *pBlue; /* Pointer into color map buffer*/ + + + + ImVfb *vfb; /* New image */ + + ImVfbPtr pPixel; /* Pointer to pixel in VFB */ + + ImClt *clt; /* New color table */ + + ImCltPtr pColor; /* Pointer to color in CLT */ + + + + unsigned char *buffer; /* Generic buffer */ + + unsigned char *pBuffer; /* Buffer pointer */ + + unsigned int operand; /* Operand of opcode */ + + int chan; /* Current VFB channel */ + + char message[256]; /* buffer for use with ImInfo */ + + + + + + /* + + * Read in the fixed size header and check for a good magic number + + * and reasonable header values. + + */ + + BinByteOrder( BINLBF ); + + if ( ImBinReadStruct( ioType, fd, fp, &header, imRleHeaderFields) == -1) + + { + + ImReturnBinError(); + + } + + if ( header.rle_magic != IMRLEMAGIC ) + + { + + ImErrNo = IMEMAGIC; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + if ( header.rle_pixelbits != 8 ) + + { + + ImErrorFatal( "RLE images with other than 8-bit pixel channels are not supported", -1, IMESYNTAX ); + + } + + + + /* following lines are printouts for Iminfo if imfile + + or inconv have the -verbose flags attached */ + + ImInfo ("Byte Order", "Least Significant Byte First"); + + sprintf (message, "%d x %d",header.rle_xsize,header.rle_ysize); + + ImInfo ("Resolution",message); + + + + if (header.rle_ncolors==1) + + sprintf (message, "%d-bit Color Indexed",header.rle_pixelbits); + + else if (header.rle_ncolors==3) + + sprintf (message, "%d-bit RGB",3*header.rle_pixelbits); + + ImInfo ("Type",message); + + + + if (header.rle_ncmap!=0) + + { + + sprintf (message, "%d Entries", header.rle_cmaplen); + + ImInfo ("Color Table", message); + + } + + + + ImInfo ("Compression Type","Run Length Encoded (RLE)"); + + + + if (header.rle_flags & IMRLE_ALPHA) + + sprintf (message, "8-bit"); + + else sprintf (message, "none"); + + ImInfo ("Alpha Channel",message); + + + + switch ( header.rle_ncolors ) + + { + + case 0: + + /* + + * No channels per pixel. This means there is no image in + + * the file, but there might be a color map. If so, we + + * read that in and we're done. + + */ + + fields = 0; + + switch ( header.rle_ncmap ) + + { + + case 0: /* No color map. Nothing in file! */ + + return ( 0 ); + + case 3: /* 3 color channels. RGB CLT. */ + + break; + + default: + + sprintf( msg, "RLE color maps with %d color channels are not supported", header.rle_ncmap ); + + ImErrorFatal( msg, -1, IMESYNTAX ); + + } + + break; + + + + case 1: + + /* + + * 1 channel per pixel. It is assumed this is a color index + + * image. The color map can be non-existant, or have 3 + + * color channels (RGB). Anything else is weird. + + */ + + fields = IMVFBINDEX8; + + switch ( header.rle_ncmap ) + + { + + case 0: /* No color map. Grayscale image. */ + + break; + + case 3: /* 3 color channels. RGB CLT. */ + + break; + + default: + + sprintf( msg, "RLE color maps with %d color channels are not supported", header.rle_ncmap ); + + ImErrorFatal( msg, -1, IMESYNTAX ); + + } + + break; + + + + case 3: + + /* + + * 3 channels per pixel. It is assumed this is an RGB image. + + * The color map must be non-existant. Other combinations + + * are weird. + + */ + + fields = IMVFBRGB; + + switch ( header.rle_ncmap ) + + { + + case 0: /* No color map. Normal RGB image. */ + + break; + + case 3: /* 3 color channels. RGB CLT. */ + + break; + + default: + + sprintf( msg, "RLE color maps with %d color channels are not supported", header.rle_ncmap ); + + ImErrorFatal( msg, -1, IMESYNTAX ); + + } + + break; + + + + default: + + /* + + * Strange number of channels. Cannot support. + + */ + + sprintf( msg, "RLE images with %d channels are not supported", + + header.rle_ncolors ); + + ImErrorFatal( msg, -1, IMESYNTAX ); + + } + + + + + + /* + + * At this point we've narrowed things down to: + + * 8-bit color index or 24-bit RGB image + + * Optional RGB CLT + + */ + + + + + + /* + + * Allocate a VFB if there is an image or alpha plane in the file. + + */ + + if ( header.rle_flags & IMRLE_ALPHA ) + + fields |= IMVFBALPHA; + + vfb = IMVFBNULL; + + if ( fields != 0 ) + + { + + vfb = ImVfbAlloc( header.rle_xsize, header.rle_ysize, fields); + + if ( vfb == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + } + + + + + + + + /* + + * Read in the variable length background color following the + + * header. There will be one 8-bit value for each channel in the + + * image (rle_ncolors). We've already restricted our RLE handling to: + + * + + * rle_ncolors = 0 no image, so no background color + + * rle_ncolors = 1 index image, 1 8-bit value + + * rle_ncolors = 3 RGB image, 3 8-bit values + + * + + * For the rle_ncolors = 0 case, a single pad byte is present. + + * + + * Clear the VFB to the given background color, if the IMRLE_CLEARFIRST + + * flag is set in the header. + + * + + * The background color for the alpha plane, if present, is always 0. + + */ + + switch ( header.rle_ncolors ) + + { + + case 0: + + if ( ImBinRead( ioType, fd, fp, bgColor, UCHAR, 1, 1 ) == -1 ) + + { + + ImReturnBinError( ); + + } + + break; /* bgColor is pad byte */ + + + + case 1: + + if ( ImBinRead( ioType, fd, fp, bgColor, UCHAR, 1, 1 ) == -1 ) + + { + + ImVfbFree( vfb ); + + ImReturnBinError( ); + + } + + if ( header.rle_flags & IMRLE_NOBACKGROUND ) + + break; /* bgColor is pad byte */ + + if ( !(header.rle_flags & IMRLE_CLEARFIRST) ) + + break; /* Nothing to do */ + + if ( fields & IMVFBALPHA ) + + { + + if ( bgColor[0] == 0 ) + + ImVfbClear( IMVFBALL, bgColor[0], vfb ); + + else + + { + + ImVfbClear( IMVFBINDEX8, bgColor[0], vfb ); + + ImVfbClear( IMVFBALPHA, 0, vfb ); + + } + + } + + else + + ImVfbClear( IMVFBALL, bgColor[0], vfb ); + + break; + + + + case 3: + + if ( header.rle_flags & IMRLE_NOBACKGROUND ) + + { + + if ( ImBinRead( ioType, fd, fp, bgColor, UCHAR, 1, 1 ) == -1 ) + + { + + ImVfbFree( vfb ); + + ImReturnBinError( ); + + } + + break; /* bgColor is pad byte */ + + } + + if ( ImBinRead( ioType, fd, fp, bgColor, UCHAR, 1, 3 ) == -1 ) + + { + + ImVfbFree( vfb ); + + ImReturnBinError( ); + + } + + if ( !(header.rle_flags & IMRLE_CLEARFIRST) ) + + break; /* Nothing to do */ + + if ( bgColor[0] == bgColor[1] && bgColor[0] == bgColor[2] ) + + { + + if ( fields & IMVFBALPHA ) + + { + + if ( bgColor[0] == 0 ) + + ImVfbClear( IMVFBALL, bgColor[0], vfb ); + + else + + { + + ImVfbClear( IMVFBRGB, bgColor[0], vfb ); + + ImVfbClear( IMVFBALPHA, 0, vfb ); + + } + + } + + else + + ImVfbClear( IMVFBALL, bgColor[0], vfb ); + + break; + + } + + if ( fields & IMVFBALPHA ) + + ImVfbClear( IMVFBALPHA, 0, vfb ); + + pPixel = ImVfbQFirst( vfb ); + + for ( y = 0; y < header.rle_ysize; y++ ) + + { + + for ( x = 0; x < header.rle_xsize; x++ ) + + { + + ImVfbSRed( vfb, pPixel, bgColor[0] ); + + ImVfbSGreen( vfb, pPixel, bgColor[1] ); + + ImVfbSBlue( vfb, pPixel, bgColor[2] ); + + ImVfbSInc( vfb, pPixel ); + + } + + } + + break; + + } + + + + + + /* + + * Read in the variable length color map. We constrain things to: + + * + + * rle_ncmap = 0 no color map + + * rle_ncmap = 3 RGB color map + + */ + + clt = IMCLTNULL; + + if ( header.rle_ncmap == 3 ) + + { + + /* + + * Color map entries are 16-bit values. When mapping to + + * display hardware (or a VFB's CLT), the upper bits are + + * the most useful. Since we limit CLT color channel + + * values to 8-bits each, we shift each color map entry + + * to the right by 8 bits, and use what's left. + + * + + * Color map values are stored in the order: + + * RRR...GGG...BBB... + + */ + + cmaplen = 1 << header.rle_cmaplen; + + ImMalloc( cmap, unsigned short *, sizeof( ushort ) * cmaplen * 3 ); + + if ( ImBinRead( ioType, fd, fp, cmap, USHORT, 2, cmaplen * 3 )== -1 ) + + { + + free( (char *)cmap ); + + if ( vfb != IMVFBNULL ) + + ImVfbFree( vfb ); + + ImReturnBinError( ); + + } + + + + if ( (clt = ImCltAlloc( cmaplen )) == IMCLTNULL ) + + { + + free( (char *)cmap ); + + if ( vfb != IMVFBNULL ) + + ImVfbFree( vfb ); + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + pColor = ImCltQFirst( clt ); + + pRed = cmap; + + pGreen = pRed + cmaplen; + + pBlue = pGreen + cmaplen; + + + + for ( i = 0; i < cmaplen; i++ ) + + { + + ImCltSRed( pColor, ((*pRed++ >> 8)) & 0xFF ); + + ImCltSGreen( pColor, ((*pGreen++ >> 8)) & 0xFF ); + + ImCltSBlue( pColor, ((*pBlue++ >> 8)) & 0xFF ); + + ImCltSInc( clt, pColor ); + + } + + free( (char *) cmap ); + + + + ImVfbSClt( vfb, clt ); + + TagTableAppend( tagTable, + + TagEntryAlloc( "image clt", POINTER, &clt ) ); + + } + + + + + + /* + + * Read in and ignore comments, if present. + + */ + + if ( header.rle_flags & IMRLE_COMMENT ) + + { + + /* + + * Comments are preceded by a 2-byte value giving the + + * length, in bytes, of the comments. If this length + + * is odd, an extra pad byte is added. + + * + + * We don't handle these comments, so we just toss them. + + */ + + + + if ( ImBinRead( ioType, fd, fp, &len, UINT, 2, 1 )== -1 ) + + { + + if ( vfb != IMVFBNULL ) + + ImVfbFree( vfb ); + + ImReturnBinError( ); + + } + + len = (len + 1) & ~0x1; /* Round to 16-bits */ + + + + /* + + * If reading from a file, just seek past the comments. + + * Otherwise we have to read them in and toss them. + + */ + + if ( ioType & IMFILEIOFILE ) + + ImSeek( ioType, fd, fp, len, 1 ); + + else + + { + + ImMalloc( buffer, unsigned char *, len ); + + if ( ImBinRead( ioType, fd, fp, buffer, UCHAR, 1, len )== -1 ) + + { + + free( (char *)buffer ); + + if ( vfb != IMVFBNULL ) + + ImVfbFree( vfb ); + + ImReturnBinError( ); + + } + + free( (char *)buffer ); + + } + + } + + + + + + /* + + * If there is no image, stop. + + */ + + if ( header.rle_ncolors == 0 && !(header.rle_flags & IMRLE_ALPHA) ) + + return ( (clt == IMCLTNULL) ? 0 : 1 ); + + + + + + /* + + * Read in the image. Each two byte opcode/operand pair is read + + * in and checked to see if the opcode has its "long form" bit set. + + * If so, the byte paired with the opcode is ignored and another + + * two bytes read in and treated as a 16-bit operand. + + * + + * The opcode is then processed. IMOPSkipLines and IMOPSkipPixels both + + * skip over pixels, setting nothing. IMOPSetColor switches to a + + * different channel in the image. IMOPPixelData has literal pixel + + * values for the current channel, while IMOPRunData has a run of a + + * single pixel value for the current channel. + + * + + * Encountering file EOF, or the IMOPEOF opcode stops the image read. + + * + + * NOTE: VFB's have (0,0) in the upper-left. RLE images have it + + * in the lower left. So, we walk from the bottom of the image, up, + + * as we read it in. + + */ + + i = (header.rle_xsize + 1) & ~0x1; + + ImMalloc( buffer, unsigned char *, i * sizeof( unsigned char ) ); + + + + x = 0; + + y = header.rle_ysize - 1; + + switch ( header.rle_ncolors ) + + { + + case 0: chan = IMONALPHA; break; + + case 1: chan = IMONINDEX8; break; + + case 3: chan = IMONRED; break; + + } + + + + for ( ; ; ) + + { + + switch ( (j = ImBinRead( ioType, fd, fp, buffer, UCHAR, 1, 2 ))) + + { + + case -1: /* Error */ + + free( (char *)buffer ); + + ImVfbFree( vfb ); + + ImReturnBinError( ); + + + + case 0: /* EOF */ + + free( (char *)buffer ); + + TagTableAppend( tagTable, + + TagEntryAlloc( "image vfb", POINTER, &vfb ) ); + + return ( 1 ); + + } + + operand = buffer[1]; + + if ( buffer[0] & IMLONGOP ) + + { + + if ( ImBinRead( ioType, fd, fp, &operand, UINT, 2, 1 ) == -1 ) + + { + + free( (char *)buffer ); + + ImVfbFree( vfb ); + + ImReturnBinError( ); + + } + + } + + + + switch ( buffer[0] ) + + { + + case IMOPSkipLines | IMLONGOP: + + case IMOPSkipLines: + + /* Skip to the start of the next line. */ + + y -= operand; + + x = 0; + + continue; + + + + case IMOPSetColor: + + /* Start on a different channel. */ + + x = 0; + + switch ( operand ) + + { + + case 0: chan = (header.rle_ncolors==1)?IMONINDEX8:IMONRED; + + break; + + case 1: chan = IMONGREEN; break; + + case 2: chan = IMONBLUE; break; + + case 255:chan = IMONALPHA; break; + + default: + + free( (char *)buffer ); + + ImVfbFree( vfb ); + + sprintf( msg, "RLE image data selects channel %d, which doesn't exist!", operand ); + + ImErrorFatal( msg, -1, IMESYNTAX ); + + } + + continue; + + + + case IMOPSkipPixels | IMLONGOP: + + case IMOPSkipPixels: + + /* Skip forward on the scanline. */ + + x += operand; + + continue; + + + + case IMOPPixelData | IMLONGOP: + + case IMOPPixelData: + + /* Read in literal pixel values. */ + + operand++; + + i = (operand + 1) & ~0x1; + + if ( ImBinRead( ioType, fd, fp, buffer, UCHAR, 1, i ) == -1 ) + + { + + free( (char *)buffer ); + + ImVfbFree( vfb ); + + ImReturnBinError( ); + + } + + pBuffer = buffer; + + pPixel = ImVfbQPtr( vfb, x, y ); + + x += operand; + + switch ( chan ) + + { + + case IMONINDEX8: + + for ( i = 0; i < operand; i++ ) + + { + + ImVfbSIndex8( vfb, pPixel, *pBuffer++ ); + + ImVfbSInc( vfb, pPixel ); + + } + + break; + + case IMONRED: + + for ( i = 0; i < operand; i++ ) + + { + + ImVfbSRed( vfb, pPixel, *pBuffer++ ); + + ImVfbSInc( vfb, pPixel ); + + } + + break; + + case IMONGREEN: + + for ( i = 0; i < operand; i++ ) + + { + + ImVfbSGreen( vfb, pPixel, *pBuffer++ ); + + ImVfbSInc( vfb, pPixel ); + + } + + break; + + case IMONBLUE: + + for ( i = 0; i < operand; i++ ) + + { + + ImVfbSBlue( vfb, pPixel, *pBuffer++ ); + + ImVfbSInc( vfb, pPixel ); + + } + + break; + + case IMONALPHA: + + for ( i = 0; i < operand; i++ ) + + { + + ImVfbSAlpha( vfb, pPixel, *pBuffer++ ); + + ImVfbSInc( vfb, pPixel ); + + } + + break; + + } + + continue; + + + + case IMOPRunData | IMLONGOP: + + case IMOPRunData: + + /* Read in and process a run of the same pixel value*/ + + if ( ImBinRead( ioType, fd, fp, buffer, UCHAR, 1, 2 ) == -1 ) + + { + + free( (char *)buffer ); + + ImVfbFree( vfb ); + + ImReturnBinError( ); + + } + + operand++; + + pPixel = ImVfbQPtr( vfb, x, y ); + + x += operand; + + switch ( chan ) + + { + + case IMONINDEX8: + + for ( i = 0; i < operand; i++ ) + + { + + ImVfbSIndex8( vfb, pPixel, *buffer ); + + ImVfbSInc( vfb, pPixel ); + + } + + break; + + case IMONRED: + + for ( i = 0; i < operand; i++ ) + + { + + ImVfbSRed( vfb, pPixel, *buffer ); + + ImVfbSInc( vfb, pPixel ); + + } + + break; + + case IMONGREEN: + + for ( i = 0; i < operand; i++ ) + + { + + ImVfbSGreen( vfb, pPixel, *buffer ); + + ImVfbSInc( vfb, pPixel ); + + } + + break; + + case IMONBLUE: + + for ( i = 0; i < operand; i++ ) + + { + + ImVfbSBlue( vfb, pPixel, *buffer ); + + ImVfbSInc( vfb, pPixel ); + + } + + break; + + case IMONALPHA: + + for ( i = 0; i < operand; i++ ) + + { + + ImVfbSAlpha( vfb, pPixel, *buffer ); + + ImVfbSInc( vfb, pPixel ); + + } + + break; + + } + + continue; + + + + case IMOPEOF: + + free( (char *)buffer ); + + TagTableAppend( tagTable, + + TagEntryAlloc( "image vfb", POINTER, &vfb ) ); + + return ( 1 ); + + + + default: + + free( (char *)buffer ); + + ImVfbFree( vfb ); + + sprintf( msg, "Unknown RLE image opcode: 0x%02x", + + buffer[0] ); + + ImErrorFatal( msg, -1, IMESYNTAX ); + + } + + } + +} + + + + + + + + + + + +/* + + * MACROS + + * IMDumpRun - dump a run of identical pixels + + * IMDumpLitRun - dump a run of literal pixels + + * + + * DESCRIPTIONS + + * Both of these macros assume a variety of local variables. These + + * algorithms are encapsulated in macros in order to simplify the + + * write routines below. They are *not* made into subroutines to + + * avoid the extra expense of subroutine calls, argument passing, and + + * global variable referencing that that would entail. + + * + + * IMDumpRun outputs a RunData opcode in either short or long form, + + * followed by a single byte of pixel value for the pixel value to run + + * across several image pixels. A pad byte is added to bring the + + * opcode and run operand to a 16-bit boundary. + + * + + * IMDumpLitRun outputs a PixelData opcode in either short or long form, + + * followed by a string of literal pixel values to write into adjacent + + * image pixels. If the number of pixel values is odd, a pad byte is + + * added to bring the opcode and literal run to a 16-bit boundary. + + */ + + + +#define IMDumpRun() \ +{ \ + /* \ + * Write opcode (possibly in long form). \ + * Operand is runtCount - 1. \ + */ \ + if ( --runCount <= 255 ) \ + { \ + command[0] = IMOPRunData; \ + command[1] = runCount; \ + if ( ImBinWrite( ioType, fd, fp, command, \ + UCHAR, 1, 2 ) == -1 ) \ + { \ + free( (char *)buffer ); \ + ImReturnBinError(); \ + } \ + } \ + else \ + { \ + command[0] = IMOPRunData | IMLONGOP; \ + command[1] = 0; \ + if ( ImBinWrite( ioType, fd, fp, command, \ + UCHAR, 1, 2 ) == -1 ) \ + { \ + free( (char *)buffer ); \ + ImReturnBinError(); \ + } \ + if ( ImBinWrite( ioType, fd, fp, &runCount, \ + UINT, 2, 1 ) == -1 ) \ + { \ + free( (char *)buffer ); \ + ImReturnBinError(); \ + } \ + } \ + \ + /* \ + * Output single run byte, plus pad byte to round to \ + * next highest 16-bit boundary. \ + */ \ + buffer[1] = 0; /* Pad */ \ + if ( ImBinWrite( ioType, fd, fp, buffer, UCHAR, 1, 2 ) == -1 ) \ + { \ + free( (char *)buffer ); \ + ImReturnBinError(); \ + } \ +} + + + +#define IMDumpLitRun() \ +{ \ + /* \ + * Write opcode (possibly in long form). \ + * Operand is litCount - 1. \ + */ \ + if ( --litCount <= 255 ) \ + { \ + command[0] = IMOPPixelData; \ + command[1] = litCount; \ + if ( ImBinWrite( ioType, fd, fp, \ + command, UCHAR, 1, 2 ) == -1 ) \ + { \ + free( (char *)buffer ); \ + ImReturnBinError(); \ + } \ + } \ + else \ + { \ + command[0] = IMOPPixelData | IMLONGOP; \ + command[1] = 0; \ + if ( ImBinWrite( ioType, fd, fp, \ + command, UCHAR, 1, 2 ) == -1 ) \ + { \ + free( (char *)buffer ); \ + ImReturnBinError(); \ + } \ + if ( ImBinWrite( ioType, fd, fp, &litCount, \ + UINT, 2, 1 ) == -1 ) \ + { \ + free( (char *)buffer ); \ + ImReturnBinError(); \ + } \ + } \ + \ + /* \ + * Output buffer built up so far. Pad to \ + * next highest 16-bit boundary. \ + */ \ + if ( ImBinWrite( ioType, fd, fp, buffer, \ + UCHAR, 1, (litCount + 2) & ~0x1 ) == -1) \ + { \ + free( (char *)buffer ); \ + ImReturnBinError(); \ + } \ +} + + + + + + + + + + + +/* + + * FUNCTION + + * imRleWrite - write an RLE file + + * + + * DESCRIPTION + + * This same routine handles INDEX8 and RGB VFB's, with or without + + * alpha channels, and with or without CLT's. + + * + + * The file header is set up and written out. + + * + + * No background color. + + * + + * A color map is constructed and written out, if necessary. + + * + + * No comments. + + * + + * The image is processed one scanline at a time, run-length encoding + + * each channel and writing it out. + + */ + + + +static int + +#ifdef __STDC__ + +imRleWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imRleWrite( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file buffer pointer */ + + TagTable *flagsTable; /* Output parameter/flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + imRleHeaderInfo header; /* File header */ + + int i, j, k; /* Counters */ + + int x, y; /* Row and column counters */ + + + + ImVfb *vfb; /* Image to output */ + + ImVfbPtr pPixel; /* VFB pixel pointer */ + + int fields; /* VFB fields */ + + unsigned char *buffer; /* Pixel buffer */ + + unsigned char *pBuffer; /* Buffer pointer */ + + unsigned char command[4]; /* Command buffer */ + + + + ImClt *clt; /* CLT to output */ + + ImCltPtr pColor; /* CLT color pointer */ + + int cltLen; /* Length of CLT */ + + int cltPadLen; /* Length of Pad entries for CLT*/ + + unsigned short *cmap; /* Color map buffer */ + + unsigned short *pRed; /* Pointer into color map buffer*/ + + unsigned short *pGreen; /* Pointer into color map buffer*/ + + unsigned short *pBlue; /* Pointer into color map buffer*/ + + + + int index; /* Current color index */ + + int oldindex; /* Old color index */ + + unsigned int runCount; /* Run count */ + + ImVfbPtr pLitPixel; /* Literal pixel pointer */ + + unsigned int litCount; /* Literal pixel count */ + + char message[256]; /* buffer to be printed in ImInfo */ + + + + + + /* + + * Set up and write out the file header. + + */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + fields = ImVfbQFields( vfb ); + + clt = ImVfbQClt( vfb ); + + header.rle_magic = IMRLEMAGIC; + + header.rle_xpos = 0; + + header.rle_ypos = 0; + + header.rle_xsize = ImVfbQWidth( vfb ); + + header.rle_ysize = ImVfbQHeight( vfb ); + + header.rle_flags = IMRLE_NOBACKGROUND; + + if ( (fields & IMVFBALPHA) && (pMap->map_outAttributes & IMALPHAYES) ) + + header.rle_flags |= IMRLE_ALPHA; + + header.rle_pixelbits = 8; + + if ( fields & IMVFBINDEX8 ) + + { + + header.rle_ncolors = 1; + + if ( (clt != IMCLTNULL) && (pMap->map_outAttributes & IMCLTYES)) + + { + + /* + + * Compute the next higher power of 2 to use for + + * the number of CLT entries. + + */ + + cltLen = ImCltQNColors( clt ); + + for ( j = 0, i = cltLen; i > 1; j++ ) + + i >>= 1; + + if ( (1 << j) < cltLen ) + + j++; + + cltPadLen = (1 << j) - cltLen; + + header.rle_cmaplen = j; + + header.rle_ncmap = 3; + + } + + else + + { + + /* + + * Don't output a color map. + + */ + + header.rle_cmaplen = 0; + + header.rle_ncmap = 0; + + } + + } + + else + + { + + header.rle_ncolors = 3; + + header.rle_cmaplen = 0; + + header.rle_ncmap = 0; + + } + + BinByteOrder( BINLBF ); + + if ( ImBinWriteStruct( ioType, fd, fp, &header, imRleHeaderFields)== -1) + + { + + ImReturnBinError(); + + } + + + + /* following lines are printouts for Iminfo if imfile + + or inconv have the -verbose flags attached */ + + ImInfo ("Byte Order", "Least Significant Byte First"); + + sprintf (message, "%d x %d",header.rle_xsize,header.rle_ysize); + + ImInfo ("Resolution",message); + + + + if (header.rle_ncolors==1) + + sprintf (message, "%d-bit Color Indexed",header.rle_pixelbits); + + else if (header.rle_ncolors==3) + + sprintf (message, "%d-bit RGB",3*header.rle_pixelbits); + + ImInfo ("Type",message); + + + + if (header.rle_ncmap!=0) + + { + + sprintf (message, "%d Entries", header.rle_cmaplen); + + ImInfo ("Color Table", message); + + } + + + + ImInfo ("Compression Type","Run Length Encoded (RLE)"); + + + + if (header.rle_flags & IMRLE_ALPHA) + + sprintf (message, "8-bit"); + + else sprintf (message, "none"); + + ImInfo ("Alpha Channel",message); + + + + + + + + /* + + * No need for a background color because we set the IMRLE_NOBACKGROUND + + * bit in the header flags word. Just output a pad byte to bring + + * the header to a 16-bit boundary. + + */ + + i = 0; + + if ( ImBinWrite( ioType, fd, fp, &i, INT, 1, 1 ) == -1 ) + + { + + ImReturnBinError(); + + } + + + + + + /* + + * Write out the CLT. The red, then green, then blue channels, in + + * their entirity, are written out one at a time. Each channel + + * value is shifted into the high byte of a 16-bit word. The lower + + * byte is set to zeroes. + + * + + * Since the RLE header requires that CLT's be a power of 2 in length, + + * but VFB's don't, we might have a CLT that is shorter than a + + * power of 2. In such cases a batch of "pad" black CLT entries are + + * output. + + */ + + if ( header.rle_ncmap != 0 ) + + { + + k = cltLen + cltPadLen; + + ImMalloc( cmap, unsigned short *, sizeof( ushort ) * 3 * k ); + + + + pColor = ImCltQFirst( clt ); + + pRed = cmap; + + pGreen = pRed + k; + + pBlue = pGreen + k; + + + + for ( i = 0; i < cltLen; i++ ) + + { + + *pRed++ = (ImCltQRed( pColor ) << 8); + + *pGreen++ = (ImCltQGreen( pColor ) << 8); + + *pBlue++ = (ImCltQBlue( pColor ) << 8); + + ImCltSInc( clt, pColor ); + + } + + + + for ( i = 0; i < cltPadLen; i++ ) + + { + + *pRed++ = 0; + + *pGreen++ = 0; + + *pBlue++ = 0; + + } + + + + if ( ImBinWrite( ioType, fd, fp, cmap, USHORT, 2, 3 * k ) == -1) + + { + + free( (char *)cmap ); + + ImReturnBinError(); + + } + + free( (char *)cmap ); + + } + + + + + + /* + + * We left the IMRLE_COMMENT flag off in the header, so there are no + + * comments in the file. + + */ + + + + /* + + * Output the image. Process pixels from the bottom of the image, up. + + */ + + ImMalloc( buffer, unsigned char *, (header.rle_xsize + 1) & ~1 ); + + + + /* Process each scanline. */ + + for ( y = header.rle_ysize - 1; y >= 0; y-- ) + + { + + if ( fields & IMVFBRGB ) + + k = 3; + + else + + k = 1; + + if ( header.rle_flags & IMRLE_ALPHA ) + + k++; + + for ( j = 0; j < k; j++ ) + + { + + if ( j == 1 && (fields & IMVFBINDEX8) ) + + j = 3; + + + + /* Select channel. */ + + command[0] = IMOPSetColor; + + if ( j == 3 ) + + command[1] = 255; /* Alpha */ + + else + + command[1] = j; + + if ( ImBinWrite( ioType, fd, fp, command, + + UCHAR, 1, 2 ) == -1 ) + + { + + ImReturnBinError(); + + } + + + + pPixel = ImVfbQPtr( vfb, 0, y ); + + runCount = 0; + + litCount = 1; + + switch ( j ) + + { + + case 0: if ( fields & IMVFBINDEX8 ) + + oldindex = ImVfbQIndex8( vfb, pPixel ); + + else + + oldindex = ImVfbQRed( vfb, pPixel ); + + break; + + case 1: oldindex = ImVfbQGreen( vfb, pPixel ); break; + + case 2: oldindex = ImVfbQBlue( vfb, pPixel ); break; + + case 3: oldindex = ImVfbQAlpha( vfb, pPixel ); break; + + } + + pBuffer = buffer; + + *pBuffer++ = oldindex; + + ImVfbSInc( vfb, pPixel ); + + + + for ( x = 1; x < header.rle_xsize; x++ ) + + { + + switch ( j ) + + { + + case 0: if ( fields & IMVFBINDEX8 ) + + index=ImVfbQIndex8( vfb,pPixel); + + else + + index=ImVfbQRed( vfb, pPixel ); + + break; + + case 1: index=ImVfbQGreen( vfb, pPixel ); break; + + case 2: index=ImVfbQBlue( vfb, pPixel ); break; + + case 3: index=ImVfbQAlpha( vfb, pPixel ); break; + + } + + ImVfbSInc( vfb, pPixel ); + + + + if ( index == oldindex ) + + { + + if ( ++runCount != 1 ) + + continue; /* Cont run*/ + + + + /* + + * Start of run. Dump previous batch + + * of literal pixels, if any. + + */ + + runCount++; + + if ( --litCount == 0 ) + + continue; /* No lit*/ + + IMDumpLitRun( ); + + pBuffer = buffer; + + *pBuffer++ = index; + + litCount = 0; + + continue; + + } + + oldindex = index; + + + + + + if ( ++litCount != 1 ) + + { + + *pBuffer++ = index; + + continue; /* Cont literal run*/ + + } + + + + /* + + * Start of literal run. Dump previous batch + + * of run pixels. + + */ + + IMDumpRun( ); + + pBuffer = buffer; + + *pBuffer++ = index; + + runCount = 0; + + } + + if ( litCount != 0 ) + + { + + IMDumpLitRun( ); + + } + + else if ( runCount != 0 ) + + IMDumpRun( ); + + } + + command[0] = IMOPSkipLines; + + command[1] = 1; + + if ( ImBinWrite( ioType, fd, fp, command, UCHAR, 1, 2 ) == -1 ) + + { + + ImReturnBinError(); + + } + + } + + + + free( (char *)buffer ); + + command[0] = IMOPEOF; + + command[1] = 0; + + if ( ImBinWrite( ioType, fd, fp, command, UCHAR, 1, 2 ) == -1 ) + + { + + ImReturnBinError(); + + } + + return ( 1 ); + +} + diff --git a/utils/roq2/libim/imschemes.c b/utils/roq2/libim/imschemes.c new file mode 100644 index 0000000..e029446 --- /dev/null +++ b/utils/roq2/libim/imschemes.c @@ -0,0 +1,408 @@ +/** + + ** $Header: /roq/libim/imschemes.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/imschemes.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** imschemes.c - compression scheme structure declaration + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** imschemes.c contains the structure that holds routines/ info for + + ** reading/ writing encoded files. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** ImGetCompressSchemes f get the list of compression/encoding schemes + + ** + + ** PRIVATE CONTENTS + + ** + + ** imCompressSchemes v list of compression/encoding schemes + + ** + + ** + + ** HISTORY + + ** $Log: /roq/libim/imschemes.c $ + * + * 1 11/02/99 4:38p Zaphod + + * Revision 1.3 1995/06/29 00:28:04 bduggan + + * updated copyright year + + * + + * Revision 1.2 1995/06/16 08:45:42 bduggan + + * Took out declarations of schemes. Moved 'em into + + * immiscschemes.c + + * + + * Revision 1.1 1995/05/24 17:05:59 bduggan + + * Initial revision + + * + + **/ + + + +//#include + +//#include + +#include "im.h" + +#include "iminternal.h" + + + + + +/** + + ** CODE CREDITS + + ** Custom development, Brian Duggan, San Diego Supercomputer Center, 1995. + + **/ + + + +/* + + * VARIABLE + + * imCompressSchemes + + * + + * DESCRIPTION + + * A list of compression schemes that are supported. + + * + + * ADDING A NEW SCHEME + + * + + * Note that here and throughout the library, we try to refer to a file + + * compression 'scheme' and an image 'format', and not a compression + + * 'format', in order to keep things clear. + + * + + * Schemes may be added to this list quite easily. + + * Here's how to do it: + + * + + * 1. Add the declaration 'extern ImCompressScheme myscheme' + + * 2. Add &myscheme to the list 'imCompressSchemes'. + + * 3. Fill up the structure 'myscheme' with the following + + * elements: + + * - Suffixes of the compressed files + + * - The name of the scheme + + * - Magic number(s) for the scheme + + * - a subroutine that will encode a file + + * - a subroutine that will decode a file + + * + + * Here's an explanation of these structure elements for + + * Lempel-Ziv Encoding (.Z files). + + * + + * 1. Suffix list. + + * First we declare imCompressZNames as... + + * extern char* imCompressZNames[] = { "Z", NULL }; + + * Then imCompressZNames is the first element of the ImCompressScheme + + * structure. + + * + + * 2. Scheme name. + + * "Lempel-Ziv Encoding" is the second element of the ImCompressScheme + + * structure. + + * + + * 3. Magic numbers + + * We make the following declarations: + + * static unsigned char imZMagic1[] = { 0x1f, 0x9d }; + + * static unsigned char imZMagic2[] = { 0x9d, 0x1f }; + + * static ImFileMagic imZMagic[] = + + * { + + * { 0, 2, imZMagic1}, + + * { 0, 2, imZMagic2}, + + * { 0, 0, NULL} + + * }; + + * This signifies that out files may have one of the following two + + * magic numbers: 1) a magic number of length 2 at location 0 (1f9d) + + * or 2) a magic number of length 2 at location 0 (9d1f) + + * + + * 4. Decoding subroutine + + * The subroutine 'imZDecode' has the responsibility of decoding the + + * the file passed to it, and destroying that file. It must also name + + * the new file that it creates, and return that name. + + * + + * 5. Encoding subroutine. + + * The subroutine 'imZEncode' has the responsibility of encoding the + + * file passed to it, and destroying that file. It must also name the + + * new file that it creates, and return that name. + + * + + * IMPORTANT: + + * The decoding and encoding subroutines will receive a file + + * such as /usr/tmp/tmpfile. When creating a new file, the new file name + + * should be as close to the old file name as possible. More + + * specifically, the file should be in the temp directory. Thus, creating + + * a file such as /usr/tmp/tmpfile.z is fine, but creating a file like + + * 'tmpfile.z' (in the working directory) is not. + + * + + * The reason we don't want to create a file in the working + + * directory is that the file that is created by these routines will later + + * be destroyed. + + * + + * Many of these routines assume that it's okay to destroy a file + + * that has a name similar to the incoming file. e.g. if the incoming + + * file is '/usr/tmp/im.zzyxxy', then it's okay to destroy anything named + + * '/usr/tmp/im.zzyxxy.Z'. + + * + + * + + * The routines in this file are all simple calls to external programs. + + * If you wish to do something more elaborate, then you may want to make a + + * seperate file for your structure, and function declarations, and simply + + * declare the structure as an 'extern' in this file. + + * + + */ + + + +#ifndef WIN32 + +extern ImCompressScheme imCompressuu; + +extern ImCompressScheme imCompressGZ; + +extern ImCompressScheme imCompressz; + +extern ImCompressScheme imCompressZ; + + + +static ImCompressScheme *imCompressSchemes[] = + +{ + + &imCompressuu, + + &imCompressGZ, + + &imCompressz, + + &imCompressZ, + + NULL + +}; + + + +#else + +static ImCompressScheme *imCompressSchemes[] = + +{ + + NULL + +}; + + + +#endif + +/* + + * FUNCTION + + * ImGetCompressSchemes + + * + + * DESCRIPTION + + * Get the list of schemes. + + */ + +ImCompressScheme** + +#ifdef __STDC__ + +ImGetCompressSchemes( void ) + +#else + +ImGetCompressSchemes( ) + +#endif + +{ + + return imCompressSchemes; + +} + + + + + diff --git a/utils/roq2/libim/imsoftimage.c b/utils/roq2/libim/imsoftimage.c new file mode 100644 index 0000000..a0bfd58 --- /dev/null +++ b/utils/roq2/libim/imsoftimage.c @@ -0,0 +1,2706 @@ +/** + + ** $Header: /roq/libim/imsoftimage.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/imsoftimage.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** imsoftimage.c - Soft Image image file i/o + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** imsoftimage.c contains routines to read and write SoftImages's image + + ** files for the image manipulation library. Raster data read in + + ** is stored in a VFB. Raster data written out is taken from a tag table. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** none + + ** + + ** PRIVATE CONTENTS + + ** imSoftImageRead f read a MIFF file + + ** + + ** HISTORY + + ** $Log: /roq/libim/imsoftimage.c $ + * + * 1 11/02/99 4:38p Zaphod + + * Revision 1.4 1995/06/30 22:10:16 bduggan + + * added byte ordering stuff + + * + + * Revision 1.3 1995/06/29 00:28:04 bduggan + + * updated copyright year + + * + + * Revision 1.2 1995/06/15 20:03:15 bduggan + + * added line to return status + + * + + * Revision 1.1 1995/04/21 17:53:11 bduggan + + * Initial revision + + * + + ** + + **/ + + + +#include + +#include "iminternal.h" + + + + + + + +/** + + ** FORMAT + + ** SI - SoftImage image file format + + ** + + ** AKA + + ** pic + + ** + + ** FORMAT REFERENCES + + ** DKit User's Guide, SoftImage, Inc. + + ** + + ** CODE CREDITS + + ** Brian Duggan, Custom Development, San Diego Supercomputer Center, 1995. + + ** + + ** DESCRIPTION + + ** SI's are single-image picture files. + + ** They are divided into three sections: + + ** 1. header + + ** 2. channel information + + ** 3. image data + + ** + + ** ====== + + ** Header + + ** ====== + + ** Offset Length Name Description + + ** ----------------------------------- + + ** 0 4 magic Magic number + + ** 4 4 version version of format + + ** 8 80 comment user comment + + ** 88 4 id "PICT" + + ** 92 2 width image width (pixels) + + ** 94 2 height image height + + ** 96 4 ratio pixel width/height ratio + + ** = 1.0 for square pixels + + ** 100 2 fields image fields type + + ** This indicates whether every scanline, + + ** odd scanlines, even scanlines, or no + + ** scanlines are stored. We just handle + + ** every scanline. + + ** 102 2 pad nothing + + ** + + ** =================== + + ** Channel Information + + ** =================== + + ** + + ** This section of the file consists of a series of packets, describing + + ** the image data that follows. Each packet describes a section of image + + ** data. The organization of a packet is as follows: + + ** + + ** Byte number Description + + ** ---------------------------- + + ** 1 Are there more packets after this one? + + ** 0x00 means "no". + + ** 0x01 means "no". + + ** 2 How many bits per pixel per channel? + + ** This should always be 8. + + ** 3 Is there compression? + + ** 0x02 means there is rle compression. + + ** 0x00 means there is no compression. + + ** 4 Which channel is stored? + + ** 0x80 means a red channel + + ** 0x40 means a green channel + + ** 0x20 means a blue channel + + ** 0x10 means an alpha channel. + + ** This field may be a bit-or of the above channels. Hence + + ** each packet may contain several channels. + + ** + + ** ========== + + ** Pixel data + + ** ========== + + ** + + ** The organization of the pixels depends on the stuff in the "channel information" + + ** section. For instance, if the channel info section has two packets, + + ** one indicating red, green, blue, no compression, and another indicating + + ** alpha and no compression, then the pixel data will be stored as... + + ** Scanline 1: RGBRGBRGB..RGBAAAAAAA...AAA + + ** Scanline 2: RGBRGBRGB..RGBAAAAAAA...AAA + + ** Notice that the channel information reflects the pixel data on a scanline by + + ** scanline basis. + + ** + + ** Some portions of a scanline may be rle compressed. Here's the rle compression + + ** scheme: + + ** + + ** There are two types of runs, variable value runs and constant value runs. + + ** A variable value run looks like this: + + ** Encoded: 03 01 01 01 02 02 02 03 03 03 04 04 04 + + ** Unencoded: pixel 1: 01 01 01 + + ** pixel 2: 02 02 02 + + ** pixel 3: 03 03 03 + + ** pixel 4: 04 04 04 + + ** The '03' at the beginning signifies that there are 4 (3 + 1) pixels. + + ** Such a run must contain 128 or fewer pixels. + + ** + + ** A constant value run looks like this: + + ** Encoded: 83 01 01 01 + + ** Unencoded: pixel 1: 01 01 01 + + ** pixel 2: 01 01 01 + + ** pixel 3: 01 01 01 + + ** pixel 4: 01 01 01 + + ** The high bit of the first byte is set to indicate a constant run. + + ** The number of pixels in the run is then one more than the value of + + ** the lower 7 bits. + + ** You can have a constant run with more than 127 bytes by using the + + ** following... + + ** Encoded: 80 aa aa 01 01 01 + + ** Unencoded: 0xaaaa pixels with value 01 01 01. + + ** The 80 indicates that the next two bytes have the count. + + ** + + ** All runs must be contained within a single scanline. + + ** + + ** /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ + + ** + + ** "Standard" SoftImage files are organized as follows: + + ** Packet 1 indicates red, green, & blue channels, rle compressed + + ** Packet 2 indicates alpha channel, rle compressed. + + ** + + ** Although the official description of a softimage file is as described above, + + ** I have yet to see a non-standard one. Hence, as of this revision, there is + + ** no support herein for non-standard softimage files. We only support + + ** RLE or raw softImages files, with 1 or 2 packets. + + ** + + **/ + + + +/* + + * SI - SoftImage's picture file format + + * For information on these structures, how to use them, etc. please + + * see imfmt.c. + + */ + + + +#ifdef __STDC__ + +static int imSoftimageRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imSoftReadRLERedGreenBlue( int ioType, int fd, FILE* fp, ImVfb* vfb, ImVfbPtr vfbPtr, unsigned int numPixels ); + +static int imSoftReadRLEAlpha( int ioType, int fd, FILE* fp, ImVfb* vfb, ImVfbPtr vfbPtr, unsigned int numPixels ); + +static int imSoftimageWriteRLE ( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + + TagTable *flagsTable, TagTable *tagTable ); + +static int imSoftimageWriteRLEA ( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + + TagTable *flagsTable, TagTable *tagTable ); + +static int imSoftimageWriteRaw ( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + + TagTable *flagsTable, TagTable *tagTable ); + +static int imSoftimageWriteRawA ( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + + TagTable *flagsTable, TagTable *tagTable ); + + + +static int imSoftimageWriteHeader( ImFileFormatWriteMap* pMap, int ioType, int fd, FILE* fp, ImVfb* vfb); + +static int imSoftimageWriteRLERGBLine(ImVfb* vfb, ImVfbPtr vfbPtr, int numPixels, int ioType, FILE* fp, int fd); + +static int imSoftimageWriteRLEALine(ImVfb* vfb, ImVfbPtr vfbPtr, int numPixels, int ioType, FILE* fp, int fd); + +static int imSoftReadRawLineRedGreenBlue( int ioType, int fd, FILE* fp, ImVfb* vfb, ImVfbPtr vfbPtr ); + +static int imSoftReadRawLineAlpha ( int ioType, int fd, FILE* fp, ImVfb* vfb, ImVfbPtr vfbPtr ); + + + +#else + + + +static int imSoftimageRead( ); + +static int imSoftReadRLERedGreenBlue( ); + +static int imSoftReadRLEAlpha( ); + + + +static int imSoftimageWriteRLE ( ); + +static int imSoftimageWriteRLEA ( ); + +static int imSoftimageWriteHeader( ); + +static int imSoftimageWriteRaw ( ); + +static int imSoftimageWriteRawA ( ); + +static int imSoftimageWriteRLERGBLine ( ); + +static int imSoftimageWriteRLEALine ( ); + +static int imSoftReadRawLineRedGreenBlue( ); + +static int imSoftReadRawLineAlpha ( ); + + + +#endif + + + +/* + + * We omit "pic" from the list of suffixes because it + + * conflicts with pixar's suffix. + + */ + + + +static char *imSoftimageNames[ ] = { "si", "softimage", NULL }; + +static ImFileFormatReadMap imSoftimageReadMap[ ] = + +{ + + /* in out */ + + /* type,ch,dep, attr. VFB type attr. */ + + { RGB,3,8, 0, IMVFBRGB, 0 }, + + { RGB,3,8, RLE, IMVFBRGB, 0 }, + + { RGB,4,8, A, IMVFBRGB, A }, + + { RGB,4,8, RLE|A, IMVFBRGB, A }, + + { -1, 0, -1, 0 }, + +}; + +static ImFileFormatWriteMap imSoftimageWriteMap[ ] = + +{ + + /* in out */ + + /* VFB type, attr., type,ch,dep, attr., func */ + + + + { IMVFBRGB, 0, RGB,3,8, RLE, imSoftimageWriteRLE }, + + { IMVFBRGB, A, RGB,4,8, A|RLE, imSoftimageWriteRLEA }, + + { IMVFBRGB, 0, RGB,3,8, 0, imSoftimageWriteRaw }, + + { IMVFBRGB, A, RGB,4,8, A, imSoftimageWriteRawA }, + + + + { -1, 0, -1, 0, NULL }, + +}; + + + +static unsigned char imSoftimageMagicNumber[ ] = { 0x53, 0x80, 0xf6, 0x34 }; + + + +static ImFileMagic imFileSoftimageMagic []= + +{ + + { 0, 4, imSoftimageMagicNumber }, + + { 0, 0, NULL }, + +}; + + + +ImFileFormat ImFileSoftimageFormat = + +{ + + imSoftimageNames, "SoftImage's image file format", + + "SoftImage, Inc.", + + "24-bit RGB color images with optional alpha channels,\n\ +uncompressed (verbatim) and RLE-compressed.", + "24-bit RGB color images with optional alpha channels,\n\ +uncompressed (verbatim) and RLE-compressed.", + + imFileSoftimageMagic, + + IMNOMULTI, IMPIPE, + + IMNOMULTI, IMPIPE, + + imSoftimageRead, imSoftimageReadMap, imSoftimageWriteMap + +}; + + + + + + + +typedef struct imSoftHeaderInfo + +{ + + unsigned char si_magic[4]; /* SI magic # */ + + float si_version; /* SI version # */ + + char si_comments[80]; /* comments */ + + char si_id[4]; /* "PICT" */ + + unsigned int si_width; /* width */ + + unsigned int si_height; /* height */ + + float si_ratio; /* w/h ratio */ + + sdsc_uint16 si_fields; /* fields type */ + + unsigned char si_pad[2]; /* pad */ + +} imSoftHeaderInfo; + + + +static BinField imSoftHeaderFields[ ] = + +{ + + { UCHAR, 1, 4 }, /* si_magic */ + + { FLOAT, 4, 1 }, /* si_version */ + + { CHAR, 1, 80}, /* si_comments */ + + { CHAR, 1, 4}, /* si_id */ + + { UINT, 2, 1}, /* si_width */ + + { UINT, 2, 1}, /* si_height */ + + { FLOAT, 4, 1}, /* si_ratio */ + + { UINT16, 2, 1 }, /* si_fields */ + + { UCHAR, 1, 2 }, /* si_pad */ + + { 0, 0, 0 } + +}; + + + +/* + + * DEFINES + + * These are possible values for the fields in the + + * imSoftPacketInfo structure. + + */ + +#define IMSI_ISLASTPACKET 0x00 + +#define IMSI_ISNOTLASTPACKET 0x01 + + + +#define IMSI_NO_COMPRESSION 0x00 + +#define IMSI_RLE_COMPRESSION 0x02 + + + +#define IMSI_RED 0x80 + +#define IMSI_GREEN 0x40 + +#define IMSI_BLUE 0x20 + +#define IMSI_ALPHA 0x10 + + + +typedef struct imSoftPacketInfo + +{ + + unsigned char si_last; /* Is this the last channel ? */ + + unsigned char si_size; /* should be 8 (bits per chan)*/ + + unsigned char si_compression; /* RLE or none */ + + unsigned char si_channels; /* describes channels */ + +} imSoftPacketInfo; + + + +static BinField imSoftPacketFields[ ] = + +{ + + { UCHAR, 1, 1 }, /* si_last */ + + { UCHAR, 1, 1 }, /* si_size */ + + { UCHAR, 1, 1 }, /* si_compression */ + + { UCHAR, 1, 1 }, /* si_channels */ + + { 0, 0, 0 } + +}; + + + +/* + + * FUNCTION + + * imSoftimageRead - Read a SoftImage file + + * + + * DESCRIPTION + + * The file header is read and the size of the image determined. + + */ + + + +static int /* Returns # tags read in */ + +#ifdef __STDC__ + +imSoftimageRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imSoftimageRead( ioType, fd, fp, flagsTable, tagTable ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + +#endif + +{ + + imSoftHeaderInfo header; /* file header */ + + imSoftPacketInfo packet[9]; /* Channel info packets */ + + char message[300]; /* buffer */ + + int numPackets; /* number of channel packets */ + + int vfbType; /* type of the vfb */ + + ImVfb* vfb; /* a vfb */ + + ImVfbPtr vfbPtr; /* points at a vfb */ + + int compression; /* flag indicating rle */ + + int i, j; /* loop indexes */ + + + + BinByteOrder( BINMBF ); + + /* Read the header */ + + if( ImBinReadStruct( ioType, fd, fp, &header, imSoftHeaderFields )== -1) + + { + + ImReturnBinError( ); + + } + + ImInfo("Byte Order","Most Significant Byte First"); + + sprintf(message,"%d x %d",header.si_width,header.si_height); + + ImInfo("Resolution",message); + + ImInfo("Type","24-bit RGB"); + + vfbType = IMVFBRGB; + + + + /* Read in the channel packets */ + + numPackets = 0; + + compression = 0; + + packet[0].si_last = IMSI_ISLASTPACKET; + + while (numPackets==0 || packet[numPackets-1].si_last==IMSI_ISNOTLASTPACKET) + + { + + /* Read another packet */ + + if( ImBinReadStruct( ioType, fd, fp, &packet[numPackets], imSoftPacketFields )== -1) + + { + + ImReturnBinError( ); + + } + + + + if (packet[numPackets].si_channels & IMSI_ALPHA) + + vfbType |= IMVFBALPHA; + + if (packet[numPackets].si_compression==IMSI_RLE_COMPRESSION) + + compression = 1; + + numPackets++; + + } + + + + if (vfbType & IMVFBALPHA) + + { + + ImInfo("Alpha Channel","8-bit"); + + } + + else + + { + + ImInfo("Alpha Channel","none"); + + } + + + + if (compression) + + { + + ImInfo("Compression","Run Length Encoded"); + + } + + else + + { + + ImInfo("Compression","none"); + + } + + + + if (strncmp(header.si_id,"PICT",4)!=0) + + { + + ImErrorFatal("Not a softimage picture file!", -1, IMENOIMAGE); + + } + + if (header.si_fields!=3) + + { + + /* + + * if the value is 1, odd scanlines are present. + + * if it's 2 even scanlines are present. + + * if it's 0, no scanlines are present. + + */ + + ImErrorWarning("Scanlines may be missing",-1, IMEUNSUPPORTED); + + } + + if (header.si_comments[0]) + + { + + ImInfo("Description",header.si_comments); + + } + + + + if ((vfb=ImVfbAlloc(header.si_width, header.si_height, vfbType))==IMVFBNULL) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + for (i=0;i= 128 */ \ + imSoftBuf[0] = r; \ + imSoftBuf[1] = g; \ + imSoftBuf[2] = b; \ + imSoftDumpVarRun(3, imSoftBuf, 0); \ + } \ + else \ + if (len>=128) \ + { \ + imSoftBuf[0] = 0x80; \ + imSoftBuf[1] = ((len+1) & 0xff00) >> 8; \ + imSoftBuf[2] = (len+1) & 0xff; \ + imSoftBuf[3] = r; \ + imSoftBuf[4] = g; \ + imSoftBuf[5] = b; \ + if ( ImBinWrite( ioType, fd, fp, imSoftBuf, UCHAR, 1, 6)==-1) \ + { \ + ImReturnBinError( ); \ + } \ + } \ + else \ + { \ + imSoftBuf[0] = (unsigned char) len; \ + imSoftBuf[0] |= 0x80; \ + imSoftBuf[1] = r; \ + imSoftBuf[2] = g; \ + imSoftBuf[3] = b; \ + if ( ImBinWrite( ioType, fd, fp, imSoftBuf, UCHAR, 1, 4)==-1) \ + { \ + ImReturnBinError( ); \ + } \ + } + + + +#define imSoftDumpConstantRun1(a,len) \ + if (len==0) \ + { /* Can't have a run of one pixel due to */ \ + /* an ambiguity between this and when the */ \ + /* length is >= 128 */ \ + imSoftBuf[0] = a; \ + imSoftDumpVarRun(1, imSoftBuf, 0); \ + } \ + else \ + if (len>=128) \ + { \ + imSoftBuf[0] = 0x80; \ + imSoftBuf[1] = ((len+1) & 0xff00) >> 8; \ + imSoftBuf[2] = (len+1) & 0xff; \ + imSoftBuf[3] = a; \ + if ( ImBinWrite( ioType, fd, fp, imSoftBuf, UCHAR, 1, 4)==-1) \ + { \ + ImReturnBinError( ); \ + } \ + } \ + else \ + { \ + imSoftBuf[0] = (unsigned char) len; \ + imSoftBuf[0] |= 0x80; \ + imSoftBuf[1] = a; \ + if ( ImBinWrite( ioType, fd, fp, imSoftBuf, UCHAR, 1, 2)==-1) \ + { \ + ImReturnBinError( ); \ + } \ + } + + + +/* + + * FUNCTION + + * imSoftimageWriteRLE + + * + + * DESCRIPTION + + * Create an SI using RLE. + + * + + */ + +static int /* return # of tags used */ + +#ifdef __STDC__ + +imSoftimageWriteRLE ( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + + TagTable *flagsTable, TagTable *tagTable ) + +#else + +imSoftimageWriteRLE ( pMap, ioType, fd, fp, flagsTable, tagTable ) + +ImFileFormatWriteMap *pMap; + +int ioType; + +int fd; + +FILE *fp; + +TagTable *flagsTable; + +TagTable *tagTable; + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr vfbPtr; /* Pixel pointer */ + + imSoftHeaderInfo header; /* file header */ + + int i; /* loop index */ + + + + TagEntryQValue (TagTableQDirect (tagTable, "image vfb", 0), &vfb); + + BinByteOrder( BINMBF ); + + ImInfo("Byte Order","Most Significant Byte First"); + + + + /* Set up and write out the header */ + + if (imSoftimageWriteHeader ( pMap, ioType, fd, fp, vfb )==-1) + + return -1; + + + + vfbPtr = ImVfbQFirst(vfb); + + + + for (i = 0;i < ImVfbQHeight( vfb ) ; i++) + + { + + if ( i>0 ) + + vfbPtr = ImVfbQDown(vfb, vfbPtr); + + + + if (imSoftimageWriteRLERGBLine(vfb, vfbPtr, ImVfbQWidth(vfb), ioType, fp, fd)<0) + + return -1; + + } + + + + return (1); + +} + + + + + +/* + + * FUNCTION + + * imSoftimageWriteRLEA + + * + + * DESCRIPTION + + * Create an SI with alpha, using RLE. + + * + + */ + +static int /* return # of tags used */ + +#ifdef __STDC__ + +imSoftimageWriteRLEA ( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + + TagTable *flagsTable, TagTable *tagTable ) + +#else + +imSoftimageWriteRLEA ( pMap, ioType, fd, fp, flagsTable, tagTable ) + +ImFileFormatWriteMap *pMap; + +int ioType; + +int fd; + +FILE *fp; + +TagTable *flagsTable; + +TagTable *tagTable; + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr vfbPtr; /* Pixel pointer */ + + imSoftHeaderInfo header; /* file header */ + + int i; /* loop index */ + + + + ImInfo("Byte Order","Most Significant Byte First"); + + BinByteOrder( BINMBF ); + + TagEntryQValue (TagTableQDirect (tagTable, "image vfb", 0), &vfb); + + + + /* Set up and write out the header */ + + if (imSoftimageWriteHeader ( pMap, ioType, fd, fp, vfb )==-1) + + return -1; + + + + vfbPtr = ImVfbQFirst(vfb); + + + + for (i = 0;i < ImVfbQHeight( vfb ) ; i++) + + { + + if ( i>0 ) + + vfbPtr = ImVfbQDown(vfb, vfbPtr); + + + + if (imSoftimageWriteRLERGBLine(vfb, vfbPtr, ImVfbQWidth(vfb), ioType, fp, fd)<0) + + return -1; + + + + if (imSoftimageWriteRLEALine(vfb, vfbPtr, ImVfbQWidth(vfb), ioType, fp, fd)<0) + + return -1; + + } + + + + return (1); + +} + + + +/* + + * FUNCTION + + * imSoftimageWriteRLERGBLine + + * + + * DESCRIPTION + + * Write a certain number of pixels' rgb values, rle encoding 'em + + * + + */ + +static int /* Returns status */ + +#ifdef __STDC__ + +imSoftimageWriteRLERGBLine(ImVfb* vfb, ImVfbPtr vfbPtr, int numPixels, int ioType, FILE* fp, int fd) + +#else + +imSoftimageWriteRLERGBLine(vfb, vfbPtr, numPixels, ioType, fp, fd) + +ImVfb* vfb; + +ImVfbPtr vfbPtr; + +int numPixels; + +int ioType; + +FILE* fp; + +int fd; + +#endif + +{ + + int i; /* index */ + + unsigned char varRun[128*3]; /* Holds a variable run */ + + ImVfbPtr lastPixel, thisPixel; /* point to pixels. */ + + int varRunLength; /* Length of this variable run. */ + + int constantRunLength; /* Length of this constant run. */ + + + + /* Initialize stuff. */ + + varRunLength = 0; + + constantRunLength = 0; + + thisPixel = vfbPtr; + + lastPixel = thisPixel; + + + + for (i=1;i 0, dump varRun. + + * otherwise, add this pixel to constantRun. + + */ + + if (ImVfbSameRGB(vfb,lastPixel,thisPixel)) + + { + + if (varRunLength > 0) + + { + + imSoftDumpVarRun(3,varRun, varRunLength - 1); + + varRunLength = 0; + + } + + constantRunLength++; + + if (constantRunLength==65535) + + { /* Dump a constant run. */ + + imSoftDumpConstantRun3(ImVfbQRed(vfb,lastPixel), + + ImVfbQGreen(vfb, lastPixel), + + ImVfbQBlue(vfb, lastPixel), + + constantRunLength - 1); + + constantRunLength = 0; + + } + + } + + else /* This is different from the last character. */ + + /* Do the exact opposite of the if-branch. */ + + { + + varRun[varRunLength*3] = ImVfbQRed(vfb, lastPixel); + + varRun[varRunLength*3+1] = ImVfbQGreen(vfb, lastPixel); + + varRun[varRunLength*3+2] = ImVfbQBlue(vfb, lastPixel); + + + + if (constantRunLength > 0) + + { + + imSoftDumpConstantRun3(ImVfbQRed(vfb, lastPixel), + + ImVfbQGreen(vfb, lastPixel), + + ImVfbQBlue(vfb, lastPixel), + + constantRunLength); + + constantRunLength = 0; + + } + + else + + { /* Only increment if this is not the end of a constant run. */ + + varRunLength++; + + } + + if (varRunLength==128) + + { /* Dump variable run. */ + + imSoftDumpVarRun(3,varRun, varRunLength - 1); + + varRunLength = 0; + + } + + } + + lastPixel = thisPixel; + + } + + + + /* Dump whatever is left. */ + + if (constantRunLength==0 && varRunLength==0) + + { /* single pixel is left. */ + + imSoftDumpConstantRun3(ImVfbQRed(vfb, thisPixel), + + ImVfbQGreen(vfb, thisPixel), + + ImVfbQBlue(vfb, thisPixel), 0 ); + + } + + if (constantRunLength > 0) + + { + + imSoftDumpConstantRun3(ImVfbQRed(vfb, thisPixel), + + ImVfbQGreen(vfb, thisPixel), + + ImVfbQBlue(vfb, thisPixel), + + constantRunLength ); + + } + + if (varRunLength > 0) + + { + + varRun[varRunLength*3] = ImVfbQRed (vfb, lastPixel); + + varRun[varRunLength*3+1] = ImVfbQGreen(vfb, lastPixel); + + varRun[varRunLength*3+2] = ImVfbQBlue (vfb, lastPixel); + + imSoftDumpVarRun(3,varRun, varRunLength ); + + } + + + + return ( 1 ); + +} + + + + + +/* + + * FUNCTION + + * imSoftimageWriteRLEALine + + * + + * DESCRIPTION + + * Write a certain number of pixels' alpha values, rle encoding 'em + + * + + */ + +static int /* Returns status */ + +#ifdef __STDC__ + +imSoftimageWriteRLEALine(ImVfb* vfb, ImVfbPtr vfbPtr, int numPixels, int ioType, FILE* fp, int fd) + +#else + +imSoftimageWriteRLEALine(vfb, vfbPtr, numPixels, ioType, fp, fd) + +ImVfb* vfb; + +ImVfbPtr vfbPtr; + +int numPixels; + +int ioType; + +FILE* fp; + +int fd; + +#endif + +{ + + int i; /* index */ + + unsigned char varRun[128]; /* Holds a variable run */ + + ImVfbPtr lastPixel, thisPixel; /* point to pixels. */ + + int varRunLength; /* Length of this variable run. */ + + int constantRunLength; /* Length of this constant run. */ + + + + /* Initialize stuff. */ + + varRunLength = 0; + + constantRunLength = 0; + + thisPixel = vfbPtr; + + lastPixel = thisPixel; + + + + for (i=1;i 0, dump varRun. + + * otherwise, add this pixel to constantRun. + + */ + + if (ImVfbQAlpha(vfb,lastPixel)==ImVfbQAlpha(vfb,thisPixel)) + + { + + if (varRunLength > 0) + + { + + imSoftDumpVarRun(1,varRun, varRunLength - 1); + + varRunLength = 0; + + } + + constantRunLength++; + + if (constantRunLength==65535) + + { /* Dump a constant run. */ + + imSoftDumpConstantRun1(ImVfbQAlpha(vfb,lastPixel), + + constantRunLength - 1); + + constantRunLength = 0; + + } + + } + + else /* This is different from the last character. */ + + /* Do the exact opposite of the if-branch. */ + + { + + varRun[varRunLength] = ImVfbQAlpha(vfb, lastPixel); + + + + if (constantRunLength > 0) + + { + + imSoftDumpConstantRun1(ImVfbQAlpha(vfb, lastPixel), + + constantRunLength); + + constantRunLength = 0; + + } + + else + + { /* Only increment if this is not the end of a constant run. */ + + varRunLength++; + + } + + if (varRunLength==128) + + { /* Dump variable run. */ + + imSoftDumpVarRun(1,varRun, varRunLength - 1); + + varRunLength = 0; + + } + + } + + lastPixel = thisPixel; + + } + + + + /* Dump whatever is left. */ + + if (constantRunLength==0 && varRunLength==0) + + { /* single pixel is left. */ + + imSoftDumpConstantRun1(ImVfbQAlpha(vfb, thisPixel), 0 ); + + } + + if (constantRunLength > 0) + + { + + imSoftDumpConstantRun1(ImVfbQAlpha(vfb, thisPixel), constantRunLength ); + + } + + if (varRunLength > 0) + + { + + varRun[varRunLength] = ImVfbQAlpha (vfb, lastPixel); + + imSoftDumpVarRun(1,varRun, varRunLength ); + + } + + + + return ( 1 ); + +} + + + +/* + + * FUNCTION + + * imSoftimageWriteRaw + + * + + * DESCRIPTION + + * Create an SI using no compression. + + * + + */ + +static int /* return # of tags used */ + +#ifdef __STDC__ + +imSoftimageWriteRaw ( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + + TagTable *flagsTable, TagTable *tagTable ) + +#else + +imSoftimageWriteRaw ( pMap, ioType, fd, fp, flagsTable, tagTable ) + +ImFileFormatWriteMap *pMap; + +int ioType; + +int fd; + +FILE *fp; + +TagTable *flagsTable; + +TagTable *tagTable; + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr vfbPtr; /* Pixel pointer */ + + imSoftHeaderInfo header; /* file header */ + + int i,j; /* loop index */ + + unsigned char *buffer; /* buffer. */ + + + + ImInfo("Byte Order","Most Significant Byte First"); + + BinByteOrder( BINMBF ); + + TagEntryQValue (TagTableQDirect (tagTable, "image vfb", 0), &vfb); + + ImMalloc (buffer, unsigned char* , ImVfbQWidth(vfb) * 3 ); + + + + /* Set up and write out the header */ + + if (imSoftimageWriteHeader ( pMap, ioType, fd, fp, vfb )==-1) + + return -1; + + + + vfbPtr = ImVfbQFirst(vfb); + + + + for (i = 0;i < ImVfbQHeight( vfb ) ; i++) + + { + + /* Write RGB values. */ + + for (j=0;j < ImVfbQWidth(vfb); j++) + + { + + buffer[j*3] = ImVfbQRed(vfb,vfbPtr); + + buffer[j*3+1] = ImVfbQGreen(vfb,vfbPtr); + + buffer[j*3+2] = ImVfbQBlue(vfb,vfbPtr); + + ImVfbSInc(vfb, vfbPtr); + + } + + + + if ( ImBinWrite( ioType, fd, fp, buffer, UCHAR, 1, 3 * ImVfbQWidth(vfb))==-1) + + { + + ImReturnBinError( ); + + } + + } + + + + return (1); + +} + + + + + +/* + + * FUNCTION + + * imSoftimageWriteRawA + + * + + * DESCRIPTION + + * Create an SI with alpha using no compression. + + * + + */ + +static int /* return # of tags used */ + +#ifdef __STDC__ + +imSoftimageWriteRawA ( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + + TagTable *flagsTable, TagTable *tagTable ) + +#else + +imSoftimageWriteRawA ( pMap, ioType, fd, fp, flagsTable, tagTable ) + +ImFileFormatWriteMap *pMap; + +int ioType; + +int fd; + +FILE *fp; + +TagTable *flagsTable; + +TagTable *tagTable; + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr vfbPtr; /* Pixel pointer */ + + imSoftHeaderInfo header; /* file header */ + + int i,j; /* loop index */ + + unsigned char *buffer; /* buffer. */ + + unsigned char *alphaBuffer; /* buffer. */ + + + + ImInfo("Byte Order","Most Significant Byte First"); + + BinByteOrder( BINMBF ); + + TagEntryQValue (TagTableQDirect (tagTable, "image vfb", 0), &vfb); + + ImMalloc (buffer, unsigned char* , ImVfbQWidth(vfb) * 3 ); + + ImMalloc (alphaBuffer, unsigned char* , ImVfbQWidth(vfb) ); + + + + /* Set up and write out the header */ + + if (imSoftimageWriteHeader ( pMap, ioType, fd, fp, vfb )==-1) + + return -1; + + + + vfbPtr = ImVfbQFirst(vfb); + + + + for (i = 0;i < ImVfbQHeight( vfb ) ; i++) + + { + + /* Get RGB, A values. */ + + for (j=0;j < ImVfbQWidth(vfb); j++) + + { + + buffer[j*3] = ImVfbQRed(vfb,vfbPtr); + + buffer[j*3+1] = ImVfbQGreen(vfb,vfbPtr); + + buffer[j*3+2] = ImVfbQBlue(vfb,vfbPtr); + + alphaBuffer[j]= ImVfbQAlpha(vfb,vfbPtr); + + ImVfbSInc(vfb, vfbPtr); + + } + + + + /* Write RGB */ + + if ( ImBinWrite( ioType, fd, fp, buffer, UCHAR, 1, 3 * ImVfbQWidth(vfb))==-1) + + { + + ImReturnBinError( ); + + } + + + + /* Write alpha */ + + if ( ImBinWrite( ioType, fd, fp, alphaBuffer, UCHAR, 1, ImVfbQWidth(vfb))==-1) + + { + + ImReturnBinError( ); + + } + + } + + + + return (1); + +} + + + + + +/* + + * FUNCTION + + * imSoftimageWriteHeader + + * + + * DESCRIPTION + + * Write the header. + + */ + +static int /* return status */ + +#ifdef __STDC__ + +imSoftimageWriteHeader( ImFileFormatWriteMap* pMap, int ioType, int fd, FILE* fp, ImVfb* vfb) + +#else + +imSoftimageWriteHeader( pMap, ioType, fd, fp, vfb) + +ImFileFormatWriteMap* pMap; + +int ioType; + +int fd; + +FILE* fp; + +ImVfb* vfb; + +#endif + +{ + + imSoftHeaderInfo header; /* Header structure */ + + imSoftPacketInfo packet; /* Channel info structure */ + + int alpha; /* flag. Is there alpha? */ + + int compression; /* flag. Is there compression? */ + + char message[140]; /* message */ + + + + header.si_magic[0] = imSoftimageMagicNumber[0]; + + header.si_magic[1] = imSoftimageMagicNumber[1]; + + header.si_magic[2] = imSoftimageMagicNumber[2]; + + header.si_magic[3] = imSoftimageMagicNumber[3]; + + header.si_version = 2.6; + + strcpy(header.si_comments, + + "Created by the SDSC Image Library\0This space available for advertising."); + + strcpy(header.si_id, "PICT"); + + header.si_width = ImVfbQWidth(vfb); + + header.si_height = ImVfbQHeight(vfb); + + header.si_ratio = 1.0; + + header.si_fields = 3; /* Signifies that every scanline is in this picture */ + + header.si_pad[0] = 0x00; + + header.si_pad[1] = 0x00; + + + + ImInfo("Type","24-bit RGB"); + + sprintf(message,"%d x %d",header.si_width,header.si_height); + + ImInfo("Resolution",message); + + + + if ( ImBinWriteStruct( ioType, fd, fp, &header, imSoftHeaderFields )==-1) + + { + + ImReturnBinError( ); + + } + + + + if (pMap->map_outAttributes & IMALPHAYES) + + { + + alpha = 1; + + ImInfo("Alpha Channel","8-bit"); + + } + + else + + { + + ImInfo("Alpha Channel","none"); + + alpha = 0; + + } + + + + if (pMap->map_outAttributes & IMCOMPRLE) + + { + + ImInfo("Compression","Run Length Encoded"); + + compression = 1; + + } + + else + + { + + ImInfo("Compression","none"); + + compression = 0; + + } + + + + packet.si_last = (alpha ? IMSI_ISNOTLASTPACKET : IMSI_ISLASTPACKET); + + packet.si_size = 0x08; + + packet.si_compression = (compression ? IMSI_RLE_COMPRESSION : IMSI_NO_COMPRESSION); + + packet.si_channels = IMSI_RED|IMSI_GREEN|IMSI_BLUE; + + + + if ( ImBinWriteStruct( ioType, fd, fp, &packet, imSoftPacketFields )==-1) + + { + + ImReturnBinError( ); + + } + + + + if (alpha) /* Write another packet */ + + { + + packet.si_last = IMSI_ISLASTPACKET; + + packet.si_size = 0x08; + + packet.si_compression = (compression ? IMSI_RLE_COMPRESSION : IMSI_NO_COMPRESSION); + + packet.si_channels = IMSI_ALPHA; + + if ( ImBinWriteStruct( ioType, fd, fp, &packet, imSoftPacketFields )==-1) + + { + + ImReturnBinError( ); + + } + + } + + return 1; + +} + + + diff --git a/utils/roq2/libim/imsynu.c b/utils/roq2/libim/imsynu.c new file mode 100644 index 0000000..67153d3 --- /dev/null +++ b/utils/roq2/libim/imsynu.c @@ -0,0 +1,1404 @@ +/** + + ** $Header: /roq/libim/imsynu.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/imsynu.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** imsynu.c - Synu image file I/O + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** imsynu.c contains routines to read and write Synu image files + + ** for the image manipulation library. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** none + + ** + + ** PRIVATE CONTENTS + + ** + + ** imSynuRead f read a Synu image file + + ** imSynuWrite f write a Synu image file + + ** imSynuRead f read a Synu image file + + ** imSynuWrite f write a Synu image file + + ** + + ** IMSYNUMAGIC d file magic "number" + + ** IMSYNURGB d mode string identifying RGB format + + ** IMSYNUGRAY d mode string identifying gray-scale format + + ** + + ** imGetFDStr f read a string in from fd or fp + + ** + + ** HISTORY + + ** $Log: /roq/libim/imsynu.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.16 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.15 1995/06/15 21:14:33 bduggan + + ** Took out an embedded comment + + ** + + ** Revision 1.14 1995/04/03 21:37:14 bduggan + + ** took out #ifdef NEWMAGIC + + ** + + ** Revision 1.13 1995/01/10 23:43:54 bduggan + + ** put in IMMULTI, IMPIPE instead of TRUE/FALSE + + ** made read/write routines static + + ** + + ** Revision 1.12 94/10/03 11:30:58 nadeau + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Rearranged magic number structures for format handlers. + + ** Made format handler routines static (i.e., local to file). + + ** Updated comments, adding format descriptions and references. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.11 92/12/03 01:52:31 nadeau + + ** Corrected info messages. + + ** + + ** Revision 1.10 92/11/04 12:07:48 groening + + ** put ImFIleFormat info and magic number info + + ** from imfmt.c into this file. + + ** + + ** Revision 1.9 92/10/19 14:09:40 groening + + ** added ImINfo statements + + ** + + ** Revision 1.8 92/08/31 17:35:37 vle + + ** Updated copyright notice. + + ** + + ** Revision 1.7 92/04/09 09:31:57 groening + + ** To make the compiler happy added extern statements. + + ** + + ** Revision 1.6 91/10/03 09:19:34 nadeau + + ** Fixed #includes. + + ** + + ** Revision 1.5 91/03/14 14:41:14 nadeau + + ** Changed 1L to 4L in line skip of image object. + + ** Added return(0) to end of write... was causing + + ** weirdness on Alliant FX/2800. + + ** + + ** Revision 1.4 91/03/08 14:33:43 nadeau + + ** Cleaned up some error checking and changed the name of the + + ** defined name globals giving buffer sizes. + + ** + + ** Revision 1.3 91/02/12 11:39:56 nadeau + + ** Almost completely rewrote. Removed the tag table checking + + ** now handled by ImFileRead and ImFileWrite. Removed + + ** goto's. Removed bogus static variables. Moved loop- + + ** invariant code outside of loops. Removed illegal read + + ** call that read directly into VFB (thus assuming internal + + ** data structure it shouldn't have). Buffered up writes by + + ** scanline rather than issue a write system call PER PIXEL. + + ** And generally optimized the code into a usable state. + + ** + + ** Revision 1.2 91/01/09 14:02:00 mercurio + + ** Minor spelling errors in comments fixed. + + ** + + ** Revision 1.1 90/09/20 09:46:38 mercurio + + ** Initial revision + + ** + + **/ + + + + + +#include "iminternal.h" + + + + + + + +/* + + ** FORMAT + + ** SYNU - Synthetic Universe + + ** + + ** AKA + + ** + + ** FORMAT REFERENCES + + ** Synu Reference Manual, San Diego Supercomputer Center + + ** + + ** CODE CREDITS + + ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1991. + + ** + + ** DESCRIPTION + + ** Synu images are stored in Synu object files, which may contain + + ** any of a number of objects of any type. We're interested in + + ** only the "image" (IMSYNUMAGIC) objects--all others are skipped + + ** past. + + ** + + ** Each object is preceeded by a header string, in ASCII, terminated + + ** by a newline character. The first space-delimited string in the + + ** header is the object type. The remaining strings are number-letter + + ** pairs (eg.: 786432b) describing the object contents. The letter + + ** may be either 'l', indicating that number of lines (newline-delimited + + ** strings), or 'b', indicating that number of bytes. 'L' and 'B' + + ** are also acceptable, and mean the same as their lower-case counter- + + ** parts. For example: + + ** + + ** image 4l 786432b + + ** + + ** indicates an "image" object, consisting of 4 lines followed by + + ** 786432 bytes. Reading four lines then 786432 bytes will skip + + ** past the entire object. + + ** + + ** The image object type will always consist of 4 header lines + + ** followed by some number of bytes. The header lines are of the + + ** form: + + ** + + ** + + ** + + ** + + ** + + ** + + ** where and are the width and height of the image, + + ** is the number of bytes per pixel, and is a string describing + + ** how the pixels are stored. Currently, will either be + + ** "rgb" for a three-byte-per-pixel, red-green-blue format, or + + ** "gray" for a one-byte-per-pixel, gray-scale format. Possible + + ** future extensions include "rgba", for RGB plus an alpha channel, + + ** or allowing the pixel components to be specified in another + + ** order, such as "bgr". + + ** + + ** Following the image header lines are * * bytes, + + ** representing the pixel values in scanline order. The origin + + ** of the image is in the lower left corner. + + **/ + + + +/* + + * SYNU - SDSC Synthetic Universe image renderer file + + */ + + + +#ifdef __STDC__ + +static int imGetFDStr( int ioType, int fd, FILE *fp, char *buf, int size); + +static int imSynuRead(int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable); + +static int imSynuWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +#else + +static int imGetFDStr(); + +static int imSynuRead( ); + +static int imSynuWrite( ); + +#endif + +static char *imSynuNames[ ] = { "synu", NULL }; + +static ImFileFormatReadMap imSynuReadMap[ ] = + +{ + + /* in out */ + + /* type,ch,dep, attr. VFB type attr. */ + + { IN,1,8, 0, IMVFBINDEX8, 0 }, + + { RGB,3,8, 0, IMVFBRGB, 0 }, + + { -1, 0, -1, 0 }, + +}; + +static ImFileFormatWriteMap imSynuWriteMap[ ] = + +{ + + /* + + * For Synu, all image types vector thru to the same imSynuWrite() + + * routine. This is necessary because a tagTable may be passed in + + * that contains multiple VFB's, each with different depth, CLT + + * and alpha attributes. The write map primarily serves as a + + * filter to make sure all such incomming VFB's are in one of the + + * supported formats. + + */ + + + + /* in out */ + + /* VFB type, attr., type,ch,dep, attr., func */ + + { IMVFBINDEX8, 0, IN,1,8, 0, imSynuWrite }, + + { IMVFBRGB, 0, RGB,3,8, 0, imSynuWrite }, + + { -1, 0, -1, 0, NULL }, + +}; + + + +static ImFileMagic imFileSynuMagic []= + +{ + + { 0, 0, NULL}, + +}; + + + +ImFileFormat ImFileSynuFormat = + +{ + + imSynuNames, "SDSC Synu image file", + + "SDSC", + + "8-bit grayscale and 24-bit RGB uncompressed image files.", + + "8-bit grayscale and 24-bit RGB uncompressed image files.", + + imFileSynuMagic, + + IMMULTI, IMPIPE, + + IMMULTI, IMPIPE, + + imSynuRead, imSynuReadMap, imSynuWriteMap + +}; + + + + + + + + + + + +/* + + * CONSTANTS + + * IMSYNUMAGIC - file magic "number" (see below) + + * IMSYNURGB - mode string indicating RGB data + + * IMSYNUGRAY - mode string indicating gray-scale data + + * + + * DESCRIPTION + + * IMSYNUMAGIC is the string identifying an image in the + + * Synu object header. It serves as a magic number, also. + + * + + * IMSYNURGB and IMSYNUGRAY are possible values for the mode + + * field of an image header line. + + */ + +#define IMSYNUMAGIC "image" + +#define IMSYNURGB "rgb" + +#define IMSYNUGRAY "gray" + + + + + + + + + + + +/* + + * FUNCTION + + * imSynuRead - read a Synu image file + + * + + * DESCRIPTION + + * Each object in the input stream is read. "image" objects + + * are reading 24- or 8-bit VFBs and added to the tagTable. + + * All other objects are ignored. + + */ + +#define IMSYNUSTRSIZE (256) /* size of header string buffer */ + +#define IMSYNUBUFSIZE (2048) /* buffer size */ + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imSynuRead(int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable) + +#else + +imSynuRead(ioType, fd, fp, flagsTable, tagTable) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + +#endif + +{ + + + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr p; /* pixel pointer */ + + int i,y; /* Counters */ + + int status; /* Return status holder */ + + char str[IMSYNUSTRSIZE]; /* string used for header input */ + + char junk[IMSYNUBUFSIZE]; /* garbage buffer */ + + char * pHdr; /* pointer into header string */ + + int size; /* size of object part */ + + char type; /* type of object part */ + + int nRead; /* # of bytes scanned from string */ + + int width; /* width of image */ + + int height; /* height of image */ + + int nBytes; /* number of bytes per pixel */ + + char mode[IMSYNUSTRSIZE]; /* storage mode for image */ + + unsigned char *buffer; /* Allocated buffer */ + + unsigned char *bp; /* Buffer pointer */ + + int n; /* # of bytes */ + + char message[256]; /* print buffer for use with ImInfo */ + + + + /* + + * SYNU files don't really have a byte order, since everything + + * is either ASCII or raw bytes. We set an order anyway, just to + + * be sure. + + */ + + BinByteOrder( BINMBF ); + + + + /* + + * Loop until we get an EOF + + */ + + for ( ; ; ) + + { + + /* + + * Read in the magic number. + + */ + + status = imGetFDStr(ioType,fd,fp,str,IMSYNUSTRSIZE); + + if ( status == -1) /* EOF reached, not an error */ + + break; + + if ( status == -2) /* a read error */ + + { + + ImReturnBinError(); + + } + + + + if ( strncmp(IMSYNUMAGIC,str,strlen(IMSYNUMAGIC)) != 0) + + { /* uninteresting object, skip it */ + + pHdr = &str[strlen(IMSYNUMAGIC)]; + + while(sscanf(pHdr,"%d%c%n",&size,&type,&nRead) >= 2) + + { + + pHdr += nRead; + + + + switch(type) + + { + + case 'b': + + case 'B': /* skip bytes */ + + for ( ; size > 0; size-=IMSYNUBUFSIZE) + + if ( ImBinRead(ioType,fd,fp, + + junk,CHAR,1,IMSYNUBUFSIZE)<0) + + { + + ImReturnBinError(); + + } + + break; + + + + case 'l': + + case 'L': /* skip lines */ + + for ( ; size > 0; size-- ) + + if ( imGetFDStr(ioType,fd,fp, + + junk,IMSYNUBUFSIZE) < 0) + + { + + ImReturnBinError(); + + } + + break; + + } + + } + + continue; + + } + + + + /* An image, read it in */ + + + + /* + + * Read width, height, nBytes, and mode (4 separate lines). + + */ + + if ( imGetFDStr(ioType,fd,fp,str,IMSYNUSTRSIZE) < 0) + + { + + ImReturnBinError( ); + + } + + sscanf(str,"%d",&width); + + + + if ( imGetFDStr(ioType,fd,fp,str,IMSYNUSTRSIZE) < 0) + + { + + ImReturnBinError( ); + + } + + sscanf(str,"%d",&height); + + + + if ( imGetFDStr(ioType,fd,fp,str,IMSYNUSTRSIZE) < 0) + + { + + ImReturnBinError( ); + + } + + sscanf(str,"%d",&nBytes); + + + + if ( imGetFDStr(ioType,fd,fp,str,IMSYNUSTRSIZE) < 0) + + { + + ImReturnBinError( ); + + } + + sscanf(str,"%s",mode); + + + + /* These lines sre to printout if a verbose flag + + has been set in imfile or imconv */ + + ImInfo ("Byte Order", "Most Significant Byte First"); + + sprintf (message, "%d x %d",width, height); + + ImInfo ("Resolution",message); + + + + /* + + * Allocate the correct VFB + + */ + + if ( strcmp(mode,IMSYNURGB) == 0) + + { + + ImInfo ("Type","24-bit RGB"); + + vfb = ImVfbAlloc(width,height,IMVFBRGB); + + } + + else + + { + + ImInfo ("Type","8-bit Grayscale"); + + vfb = ImVfbAlloc(width,height,IMVFBINDEX8); + + } + + if ( vfb == IMVFBNULL) + + { + + ImErrorFatal(ImQError(), -1, ImErrNo); + + } + + + + + + /* + + * Allocate a temp buffer big enough for 1 scanline. + + */ + + n = nBytes * width; + + ImMalloc( buffer, unsigned char *, n ); + + + + + + /* + + * Read pixels in directly, one scanline at a time. + + */ + + p = ImVfbQPtr( vfb, 0, height - 1 ); + + for ( y = height-1; y >= 0; y-- ) + + { + + if ( ImBinRead( ioType, fd, fp, buffer, UCHAR, 1, n ) != n ) + + { + + free( (char *)buffer ); + + ImReturnBinError(); + + } + + + + if ( ImVfbQFields( vfb ) & IMVFBINDEX8 ) + + { + + for ( i = 0, bp = buffer; i < width; i++ ) + + { + + ImVfbSIndex8( vfb, p, *bp++ ); + + ImVfbSInc( vfb, p ); + + } + + } + + else + + { + + for ( i = 0, bp = buffer; i < width; i++ ) + + { + + ImVfbSRed( vfb, p, *bp ); + + ImVfbSGreen( vfb, p, *(bp+1) ); + + ImVfbSBlue( vfb, p, *(bp+2) ); + + ImVfbSInc( vfb, p ); + + bp += nBytes; + + } + + } + + ImVfbSUp( vfb, p ); + + ImVfbSUp( vfb, p ); + + } + + + + /* + + * Append the new VFB to the tag table + + */ + + TagTableAppend( tagTable, + + TagEntryAlloc( "image vfb", POINTER, &vfb ) ); + + + + free( (char *)buffer ); + + } + + + + return ( 0 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imSynuWrite - write a Synu image file + + * + + * DESCRIPTION + + * All of the VFBs are retrieved from the Tag Table and written + + * to the output in Synu image format. Gray-scale VFBs are + + * written as gray-scale Synu images, all others are written + + * as RGB Synu images. Monochrome VFBs are not supported. + + */ + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imSynuWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imSynuWrite( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + ImVfb * vfb; /* Written out image */ + + ImVfbPtr p; /* pixel pointer */ + + int fields; /* VFB flag fields */ + + int i,x,y; /* Counters */ + + int nVfbs; /* number of VFBs */ + + int width; /* width of image */ + + int height; /* height of image */ + + char str[IMSYNUSTRSIZE]; /* string used for header input */ + + unsigned char *buffer; /* Tmp scanline buffer */ + + unsigned char *bp; /* Buffer pointer */ + + char message[512]; /* print buffer for ImInfo */ + + + + + + /* + + * Process each of the VFBs + + */ + + nVfbs = TagTableQNEntry(tagTable,"image vfb"); + + for ( i=0; i < nVfbs; i++) + + { + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", i ), &vfb ); + + fields = ImVfbQFields(vfb); + + width = ImVfbQWidth(vfb); + + height = ImVfbQHeight(vfb); + + + + /* These lines sre to printout if a verbose flag + + has been set in imfile or imconv */ + + ImInfo ("Byte Order", "Most Significant Byte First"); + + sprintf (message, "%d x %d",width, height); + + ImInfo ("Resolution",message); + + + + if ( (fields & IMVFBINDEX8) != 0) + + { + + sprintf (message,"8-bit Grayscale"); + + ImInfo ("Type", message); + + } + + else + + { + + sprintf (message,"24-bit RGB"); + + ImInfo ("Type", message); + + } + + /* + + * Process the VFB. + + */ + + if ( (fields & IMVFBINDEX8) != 0) + + { + + /* + + * Write the object header + + */ + + sprintf(str,"%s 4L %db\n",IMSYNUMAGIC,width*height); + + if ( ImBinWrite(ioType,fd,fp,str,CHAR,1,strlen(str)) + + == -1 ) + + { + + ImReturnBinError(); + + } + + + + /* + + * Write the image header + + */ + + sprintf(str,"%d\n%d\n1\n%s\n",width,height,IMSYNUGRAY); + + if ( ImBinWrite(ioType,fd,fp,str,CHAR,1,strlen(str)) + + == -1 ) + + { + + ImReturnBinError(); + + } + + + + /* + + * Allocate a buffer for one scanline. + + */ + + ImMalloc( buffer, unsigned char *, width ); + + + + /* + + * Write the pixel values, inverting the scanlines + + * (since a VFB's origin is in the upper left) + + */ + + for ( y = height-1; y >= 0; y-- ) + + { + + p = ImVfbQPtr( vfb, 0, y ); + + + + for ( bp = buffer, x = 0; x < width; x++, bp++ ) + + { + + *bp = ImVfbQIndex8( vfb, p ); + + ImVfbSInc( vfb, p ); + + } + + if ( ImBinWrite( ioType, fd, fp, buffer, + + UCHAR, 1, width ) == -1 ) + + { + + free( (char *)buffer ); + + ImReturnBinError( ); + + } + + } + + + + free( (char *)buffer ); + + continue; + + } + + + + /* + + * Write the object header + + */ + + sprintf(str,"%s 4L %db\n",IMSYNUMAGIC, width*height*3); + + if ( ImBinWrite(ioType,fd,fp,str,CHAR,1,strlen(str)) == -1 ) + + { + + ImReturnBinError(); + + } + + + + /* + + * Write the image header + + */ + + sprintf(str,"%d\n%d\n%d\n%s\n",width,height,3, IMSYNURGB); + + if ( ImBinWrite(ioType,fd,fp,str,CHAR,1,strlen(str)) == -1 ) + + { + + ImReturnBinError(); + + } + + + + /* + + * Allocate a buffer for one scanline. + + */ + + ImMalloc( buffer, unsigned char *, width * 3 ); + + + + /* + + * Write the pixel values, inverting the scanlines + + * (since a VFB's origin is in the upper left) + + */ + + for ( y = height-1; y >= 0; y-- ) + + { + + p = ImVfbQPtr( vfb, 0, y ); + + for ( bp = buffer, x = 0; x < width; x++ ) + + { + + *bp++ = ImVfbQRed( vfb, p ); + + *bp++ = ImVfbQGreen( vfb, p ); + + *bp++ = ImVfbQBlue( vfb, p ); + + ImVfbSInc( vfb, p ); + + } + + if ( ImBinWrite( ioType, fd, fp, buffer, UCHAR, 1, width * 3 ) == -1 ) + + { + + free( (char *)buffer ); + + ImReturnBinError(); + + } + + } + + free( (char *)buffer ); + + } + + return ( 0 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imGetFDStr - Read a stream of bytes, looking for a newline + + * + + * DESCRIPTION + + * Read from the file descriptor fd until EOL. Put the string + + * into s. Return -2 on failure, -1 on EOF, 0 on success. + + * + + * STOLEN FROM + + * imiff.c Thanks, Todd! + + */ + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imGetFDStr( int ioType, int fd, FILE *fp, char *buf, int size) + +#else + +imGetFDStr( ioType, fd, fp, buf, size) + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + char *buf; /* Buffer to use */ + + int size; /* Size of that buffer */ + +#endif + +{ + + size--; /* predecrement, to prevent read on size == 0 */ + + do + + { + + switch ( ImBinRead( ioType, fd, fp, buf, CHAR, 1, 1 ) ) + + { + + case -1: return ( -2 ); /* read error */ + + case 0: return ( -1 ); /* EOF */ + + default: break; + + } + + } + + while ( size-- && *buf++ != '\n' ); + + + + if ( size <= 0) + + return(-2); /* No newline? */ + + + + *--buf = '\0'; + + return( 0 ); + +} + diff --git a/utils/roq2/libim/imtga.c b/utils/roq2/libim/imtga.c new file mode 100644 index 0000000..b0627c4 --- /dev/null +++ b/utils/roq2/libim/imtga.c @@ -0,0 +1,6152 @@ +/** + + ** $Header: /roq/libim/imtga.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/imtga.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** imtga.c - Targa + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** imtga.c contains routines to read and write Targa image files for + + ** the image manipulation library. Raster data read in is stored + + ** in a VFB. Raster data written out is taken from a tag table. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** none + + ** + + ** PRIVATE CONTENTS + + ** + + ** imTgaRead f read a Targa file + + ** imTgaWriteRGB16 f write an uncomp RGB file with 16 bits/pixel + + ** imTgaWriteRGB24 f write an uncomp RGB file with 24 bits/pixel + + ** imTgaWriteRGB32 f write an uncomp RGB file with 32 bits/pixel + + ** imTgaWriteRGBRLE16 f write an RLE comp RGB file with 16 bits/pixel + + ** imTgaWriteRGBRLE24 f write an RLE comp RGB file with 24 bits/pixel + + ** imTgaWriteRGBRLE32 f write an RLE comp RGB file with 32 bits/pixel + + ** imTgaWriteCLT8 f write an uncomp CLT file with 8 bits/pixel + + ** ImTgaWritCLT16 f write an uncomp CLT file with 16 bits/pixel + + ** imTgaWriteCLTRLE8 f write an RLE comp CLT file with 8 bits/pixel + + ** imTgaWriteCLTRLE16 f write an RLE comp CLT file with 16 bits/pixel + + ** imTgaWriteGREY8 f write an uncomp grscl file with 8 bits/pixel + + ** ImTgaWritGREY16 f write an uncomp grscl file with 16 bits/pixel + + ** imTgaWriteGREYRLE8 f write an RLE comp grsclfile with 8 bits/pixel + + ** imTgaWriteGREYRLE16 f write an RLE comp grscfile with 16 bits/pixel + + ** + + ** imTga2ByteExpand_PIX( ); f expands 2 bytes of color into the vfb + + ** imTga2ByteExpand_CLT( ); f expands 2 bytes of color into the clt + + ** imReadTga_1and3(); f reads in a Targa type 1 or type 3 file + + ** imReadTga_2(); f reads in a Targa type 2 file + + ** imReadTga_9and11(); f reads in a Targa type 9 or type 11 file + + ** imReadTga_10(); f reads in a Targa type 10 file + + ** imTgaWriteHeader(); f writes out a Targa header file + + ** imTgaWriteRLE8(); f writes out a RLE encoded 8bit/pixel file + + ** imTgaWrite8image(); f writes out an unencoded 8bit/pixel file + + ** imTgaWriteRLE16(); f writes out a RLE encoded 16bit/pixel file + + ** imTgaWrite16image(); f writes out an unencoded 16bit/pixel file + + ** + + ** + + ** HISTORY + + ** $Log: /roq/libim/imtga.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.17 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.16 1995/06/15 21:15:56 bduggan + + ** removed unreachable code. + + ** changed spelling of significant + + ** + + ** Revision 1.15 1995/05/17 23:47:42 bduggan + + ** Fixed crummy rle encoding algorithm + + ** + + ** Revision 1.14 1995/04/03 21:37:40 bduggan + + ** took out #ifdef NEWMAGIC + + ** + + ** Revision 1.13 1995/01/10 23:44:43 bduggan + + ** put in IMMULTI, IMPIPE instead of TRUE/FALSE + + ** made read/write routines static + + ** + + ** Revision 1.12 94/10/03 11:30:59 nadeau + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Rearranged magic number structures for format handlers. + + ** Made format handler routines static (i.e., local to file). + + ** Updated comments, adding format descriptions and references. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.11 92/12/03 01:52:48 nadeau + + ** Corrected info messages. + + ** + + ** Revision 1.10 92/11/04 12:08:08 groening + + ** put ImFIleFormat info and magic number info + + ** from imfmt.c into this file. + + ** + + ** Revision 1.9 92/10/19 14:18:54 groening + + ** forg + + ** forgot to remove debug statements + + ** + + ** Revision 1.8 92/10/19 14:08:59 groening + + ** fixed bug in rle + + ** added ImInfo stuff again + + ** file was mangled in disk crash + + ** + + ** Revision 1.7 92/09/25 12:05:58 groening + + ** added print information for iminfo + + ** + + ** Revision 1.6 92/09/05 11:33:32 groening + + ** fixed bug in read index 16 + + ** + + ** Revision 1.5 92/09/03 18:12:36 vle + + ** Fixed vfb initialization bug in imTgaWrite16image(). + + ** + + ** Revision 1.4 92/09/02 17:38:08 groening + + ** fixed insignificant stupid bug in index 16 conversion + + ** + + ** Revision 1.3 92/08/31 17:36:06 vle + + ** Updated copyright notice. + + ** + + ** Revision 1.2 92/08/24 14:28:49 groening + + ** minor stuff + + ** + + ** Revision 1.1 92/06/02 09:33:24 groening + + ** Initial revision + + ** + + ** + + **/ + + + +#include "iminternal.h" + + + +/** + + ** FORMAT + + ** TGA - Truevision Targa image file + + ** + + ** AKA + + ** vda,ivb + + ** + + ** FORMAT REFERENCES + + ** Supercharged Bitmapped Graphics, Steve Rimmer + + ** Graphics File Formats, David C. Kay, John R. Levine + + ** + + ** CODE CREDITS + + ** Custom development, Chris Groening, San Diego Supercomputer Center, 1992. + + ** + + ** DESCRIPTION + + ** There are nine different ways that a targa file can be stored: + + ** 0 - No image data included. + + ** 1 - Uncompressed, color-mapped images. + + ** 2 - Umcompressed, RGB images. + + ** 3 - Uncompressed, black and white images. + + ** 9 - Runlength encoded color mapped images. + + ** 10 - Runlength encoded RGB images. + + ** 11 - Compressed black and white images. + + ** 32 - Compressed color-mapped data, using Huffman, Delta + + ** and RLE encoding. + + ** 33 - Compressed color-mapped data, using Huffman, Delta + + ** and RLE encoding, 4-pass quadtree-type process. + + ** + + ************************************************************************* + + ** AS OF NOW ONLY TYPES 0,1,2,3,9,10,11 ARE SUPPORTED + + ** + + ** 2 AND 4 WAY TYPE INTERLEAVING ARE NOT SUPPORTED + + ** + + ** COLOR MAP TABLES ARE ONLY WRITTEN RED, GREEN, BLUE WITH 1 BYTE EACH + + ** + + ** NOTE: Word form truevision has it that types 32 and 33 are obsolete, and + + ** were never that widespread. The same with two and four way interleaving. + + ************************************************************************* + + ** + + ** The 'header' lies in the first 18 bytes of the targa file. + + ** It is described as follows: + + ** + + ** OFFSET LENGTH DESCRIPTION + + ** ----------------------------------------------------------------- + + ** | | | | + + ** | 0 | 1 | Number of characters in Identification field. | + + ** | | | | + + ** | | | This field is a one byte unsigned integer, | + + ** | | | specifying the length of the Image | + + ** | | | Identification Filed. It ranges from 0-255. | + + ** | | | A 0 means that no Image Ident. field is there.| + + ** | | | | + + ** ----------------------------------------------------------------- + + ** | | | | + + ** | 1 | 1 | Color Map type | + + ** | | | | + + ** | | | 0 - no color map included. | + + ** | | | 1 - color map included. | + + ** | | | | + + ** ----------------------------------------------------------------- + + ** | | | | + + ** | 2 | 1 | Image Type Code | + + ** | | | | + + ** | | | What kind of Data type is in the file. | + + ** | | | ('magic number'? (0,1,2,3,9,10,11,32,33)) | + + ** | | | | + + ** ----------------------------------------------------------------- + + ** | | | | + + ** | 3 | 5 | Color map Specification | + + ** | | | | + + ** | 3 | 2 | Color Map Origin | + + ** | | | Integer (lo -hi) index of first colormap entry| + + ** | | | | + + ** | 5 | 2 | Color Map Length | + + ** | | | Integer (lo-hi) count of colormap entries | + + ** | | | | + + ** | 7 | 1 | Color Map entry Size | + + ** | | | Number of bits in color map entry. | + + ** | | | 16 for Targa 16, 24 for targa 24, 32 for 32 | + + ** | | | | + + ** ----------------------------------------------------------------- + + ** | | | | + + ** | 8 | 10 | Image specification | + + ** | | | | + + ** | 8 | 2 | X origin of Image. | + + ** | | | Integer (lo-hi) X coordinate of lower left | + + ** | | | corner of image. | + + ** | | | | + + ** | 10 | 2 | Y origin of Image. | + + ** | | | Integer (lo-hi) Y coordinate of lower left | + + ** | | | corner of image. | + + ** | | | | + + ** | 12 | 2 | Width of image. | + + ** | | | Integer (lo-hi) width of image in pixels. | + + ** | | | | + + ** | 14 | 2 | Length of image. | + + ** | | | Integer (lo-hi) length of image in pixels. | + + ** | | | | + + ** | 16 | l | Image Pixel size | + + ** | | | Number of bits in a pixel, 16 for Targa 16 etc| + + ** | | | | + + ** | 17 | 1 | Image Descriptor Byte | + + ** | | | BITS 3-0: number of attribute bits associated | + + ** | | | with each pixel. For targa 16 this should be | + + ** | | | 0 or 1. For the Targa 24 it should be 0 and | + + ** | | | for the 32 it should be 8 | + + ** | | | BIT 4 & BIT 5 : Pixel data storage order | + + ** | | | Screen dest of first pixel | + + ** | | | 0 0 Bottom left | + + ** | | | 0 1 Top left | + + ** | | | 1 0 Bottom right | + + ** | | | 1 1 Top right | + + ** | | | BITS 7-6: must be zero | + + ** | | | | + + ** ----------------------------------------------------------------- + + ** | | | | + + ** | 18 |varies | Image identification field. | + + ** | | | Contains a free-form identification field of | + + ** | | | the length specified in byte 1 of the image | + + ** | | | record. It's usually omitted (byte 1 =0), but| + + ** | | | can be up to 255 characters. If more info is | + + ** | | | needed, it can be stored after the image data | + + ** | | | | + + ** ----------------------------------------------------------------- + + ** | | | | + + ** |varies |varies | Color Map Data | + + ** | | | The offset is determined by the size of the | + + ** | | | Image Identifaction Field. | + + ** | | | The length is determined by the Color Map | + + ** | | | Specification, which describes the size of | + + ** | | | each entry and the number of entries. | + + ** | | | Each color map entry is 2, 3, or 4 bytes. | + + ** | | | Unused bits are assumed to specify attribute | + + ** | | | bits. | + + ** | | | | + + ** | | | 4 bytes - 1 red, 1 green, 1 blue and 1 att. | + + ** | | | | + + ** | | | 3 bytes - 1 red, 1 green, 1 blue. | + + ** | | | | + + ** | | | 2 bytes - ARRRRRGG GGGBBBBB each letter= 1 bit| + + ** | | | There is lo-hi storage order so bytes will | + + ** | | | be reversed | + + ** | | | | + + ** ----------------------------------------------------------------- + + ** | | | | + + ** |varies |varies | Image Data Field | + + ** | | | | + + ** | | | This field specifies (width) x (height) | + + ** | | | color map indices. The indices are stored in| + + ** | | | packets. There are many kinds of packets. | + + ** | | | they consist of a 1 byte header, which | + + ** | | | idenitfies the type of packet and specifies | + + ** | | | a count, folowed by a variable-length body. | + + ** | | | | + + ** | | | Run-length packet: | + + ** | | | First bit ID =1 then 7 bit repetition count | + + ** | | | minus one. (largest possible size is 128) | + + ** | | | | + + ** | | | Raw packet: | + + ** | | | First bit ID =0 then 7 bit repetition count | + + ** | | | minus one. (largest possible size is 128) | + + ** | | | | + + ** | | | Both of these can cross scanlines | + + ** | | | | + + ** ----------------------------------------------------------------- + + **/ + + + +/* + + * TGA - Targa File Format + + * For information on these structures, how to use them, etc. please + + * see imfmt.c. + + * TGA + + */ + +#ifdef __STDC__ + +static int imTgaRead( int ioType, int fd, FILE* fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imTgaWriteRGB16( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imTgaWriteRGBRLE16( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE* fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imTgaWriteRGB24( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imTgaWriteRGBRLE24( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable); + +static int imTgaWriteRGB32( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable); + +static int imTgaWriteRGBRLE32( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable); + +static int imTgaWriteCLT8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable); + +static int imTgaWriteCLTRLE8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable); + +static int imTgaWriteCLT16( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable); + +static int imTgaWriteCLTRLE16( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable); + +static int imTgaWriteGREY8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable); + +static int imTgaWriteGREYRLE8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable); + +static int imTgaWriteGREY16( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable); + +static int imTgaWriteGREYRLE16( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable); + +#else + +static int imTgaRead(); + +static int imTgaWriteRGB16(); + +static int imTgaWriteRGBRLE16(); + +static int imTgaWriteRGB24(); + +static int imTgaWriteRGBRLE24(); + +static int imTgaWriteRGB32( ); + +static int imTgaWriteRGBRLE32(); + +static int imTgaWriteCLT8(); + +static int imTgaWriteCLTRLE8(); + +static int imTgaWriteCLT16( ); + +static int imTgaWriteCLTRLE16( ); + +static int imTgaWriteGREY8( ); + +static int imTgaWriteGREYRLE8(); + +static int imTgaWriteGREY16( ); + +static int imTgaWriteGREYRLE16( ); + +#endif + +static char *imTgaNames[ ] = { "tga", "vda", "ivb", NULL}; + +static ImFileFormatReadMap imTgaReadMap[ ] = + +{ + + /* in out */ + + /* type, ch, dep, attr. VFB type attr. */ + + { IN,1,8, C|RLE, IMVFBINDEX8, C}, + + { IN,1,16, C|RLE, IMVFBINDEX16, C}, + + { RGB,3,8, C|RLE, IMVFBRGB, C}, + + { RGB,3,8, A|C|RLE,IMVFBRGB, A|C}, + + { RGB,3,5, A|C|RLE,IMVFBRGB, A|C}, + + { RGB,3,5, A|RLE, IMVFBRGB, A}, + + { RGB,3,8, RLE, IMVFBRGB, 0}, + + { RGB,3,8, A|RLE, IMVFBRGB, A}, + + { IN,1,8, RLE, IMVFBGREY, 0}, + + { IN,1,16, RLE, IMVFBGREY, 0}, + + { IN,1,8, C, IMVFBINDEX8, C}, + + { IN,1,16, C, IMVFBINDEX16, C}, + + { RGB,3,8, C, IMVFBRGB, C}, + + { RGB,3,8, A|C, IMVFBRGB, A|C}, + + { RGB,3,5, A|C, IMVFBRGB, A|C}, + + { RGB,3,5, A, IMVFBRGB, A}, + + { RGB,3,8, 0, IMVFBRGB, 0}, + + { RGB,3,8, A, IMVFBRGB, A}, + + { IN,1,8, 0, IMVFBGREY, 0}, + + { IN,1,16, 0, IMVFBGREY, 0}, + + { -1, 0, -1, 0}, + +}; + + + +static ImFileFormatWriteMap imTgaWriteMap[ ] = + +{ + + /* in out */ + + /* VFB type, attr., type,ch,dep, attr., func */ + + + + { IMVFBINDEX8, C, IN,1,8, C|RLE, imTgaWriteCLTRLE8 }, + + { IMVFBINDEX8, C, IN,1,8, C, imTgaWriteCLT8 }, + + { IMVFBINDEX8, 0, IN,1,8, RLE, imTgaWriteGREYRLE8 }, + + { IMVFBINDEX8, 0, IN,1,8, 0, imTgaWriteGREY8 }, + + { IMVFBINDEX16, 0, IN,1,16, RLE, imTgaWriteGREYRLE16 }, + + { IMVFBINDEX16, 0, IN,1,16, 0, imTgaWriteGREY16 }, + + { IMVFBINDEX16, C, IN,1,16, C|RLE, imTgaWriteCLTRLE16 }, + + { IMVFBINDEX16, C, IN,1,16, C, imTgaWriteCLT16 }, + + { IMVFBRGB, 0, RGB,3,8, RLE, imTgaWriteRGBRLE24 }, + + { IMVFBRGB, A, RGB,3,8, A|RLE, imTgaWriteRGBRLE32 }, + + { IMVFBRGB, 0, RGB,3,8, 0, imTgaWriteRGB24 }, + + { IMVFBRGB, A, RGB,3,8, A, imTgaWriteRGB32 }, + + { IMVFBRGB, A, RGB,3,5, A, imTgaWriteRGB16 }, + + { IMVFBRGB, A, RGB,3,5, A|RLE, imTgaWriteRGBRLE16 }, + + { -1, 0, -1, 0}, + +}; + + + +static ImFileMagic imFileTgaMagic []= + +{ + + { 0, 0, NULL}, + +}; + + + +ImFileFormat ImFileTgaFormat = + +{ + + imTgaNames, "Truevision Targa image file", + + "Truevision", + + "8- and 16-bit monochrome, 8- and 16-bit color index, 16-, 24-, and\n\ +32-bit RGB+alpha, uncompressed (standard) and RLE compressed image files.", + "8- and 16-bit monochrome, 8- and 16-bit color index, 16-, 24-, and\n\ +32-bit RGB+alpha, uncompressed (standard) and RLE compressed image files.", + imFileTgaMagic, + + IMNOMULTI, IMNOPIPE, + + IMNOMULTI, IMNOPIPE, + + imTgaRead, imTgaReadMap, imTgaWriteMap + +}; + + + +#ifdef __STDC__ + +static void imTga2ByteExpand_PIX (int byte2, int byte1, ImVfbPtr pPixel, ImVfb *vfb); + +static void imTga2ByteExpand_CLT (int byte2, int byte1, ImCltPtr pColor); + +static int imReadTga_1and3 (int ioType,int fd,FILE *fp,ImVfb *vfb, int q,int h,int w); + +static int imReadTga_2 (int ioType,int fd,FILE *fp,ImVfb *vfb, int q,int h,int w); + +static int imReadTga_9and11 (int ioType,int fd,FILE* fp,ImVfb *vfb, int q,int h,int w); + +static int imReadTga_10 (int ioType,int fd,FILE* fp,ImVfb *vfb, int q,int h,int w); + +static int imTgaWriteHeader( int ioType, int fd, FILE *fp,TagTable *flagsTable, TagTable *tagTable,int stype,int spix,int cmsize ); + +static int imTgaWriteRLE8( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imTgaWrite8image( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imTgaWriteRLE16( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imTgaWrite16image( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +#else + +static void imTga2ByteExpand_PIX( ); + +static void imTga2ByteExpand_CLT( ); + +static int imReadTga_1and3(); + +static int imReadTga_2(); + +static int imReadTga_9and11(); + +static int imReadTga_10(); + +static int imTgaWriteHeader(); + +static int imTgaWriteRLE8(); + +static int imTgaWrite8image(); + +static int imTgaWriteRLE16(); + +static int imTgaWrite16image(); + +#endif + + + +#define IMTGA_NODATA 0 + +#define IMTGA_UNCOMPCOLORMAP 1 + +#define IMTGA_UNCOMPRGB 2 + +#define IMTGA_UNCOMPBW 3 + +#define IMTGA_RLECOLORMAP 9 + +#define IMTGA_RLERGB 10 + +#define IMTGA_RLEBW 11 + +#define IMTGA_HUFF 32 + +#define IMTGA_HUFF4TYPE 33 + + + + + +/* TYPEDEF & STRUCTURE + + * imTgaHeaderInfo - Targa file header information + + * imTgaHeaderFields - Targa file header fields for binary package + + */ + + + +typedef struct imTgaHeaderInfo + +{ + + int tga_numc_ident; /* number of chars. in ident. field */ + + unsigned short tga_cmtype; /* whether or not a colormap exists */ + + int tga_stype; /* Storage type */ + + int tga_cmorigin; /* Color map origin */ + + int tga_cmlen; /* Color map length */ + + int tga_cmsize; /* Color map entry size */ + + int tga_imxorg; /* X origin of image */ + + int tga_imyorg; /* Y origin of image */ + + int tga_imwid; /* Width of image */ + + int tga_imhgt; /* Height of image */ + + int tga_spix; /* Image pixel size */ + + int tga_imdesc; /* Image descriptor byte */ + +} imTgaHeaderInfo; + + + +static BinField imTgaHeaderFields[]= + +{ + + { UINT, 1, 1 }, /* tga_numc_ident */ + + { USHORT, 1, 1 }, /* tga_cmtype */ + + { UINT, 1, 1 }, /* tga_stype */ + + { UINT, 2, 1 }, /* tga_cmorigin */ + + { UINT, 2, 1 }, /* tga_cmlen */ + + { UINT, 1, 1 }, /* tga_cmsize */ + + { UINT, 2, 1 }, /* tga_imxorg */ + + { UINT, 2, 1 }, /* tga_imyorg */ + + { UINT, 2, 1 }, /* tga_imwid */ + + { UINT, 2, 1 }, /* tga_imlen */ + + { UINT, 1, 1 }, /* tga_spix */ + + { UINT, 1, 1 }, /* tga_imdesc */ + + { 0, 0, 0} + +}; + + + + + +/* FUNCTION + + * + + * imTga2ByteExpand_PIX + + * + + * DESCRIPTION + + * Takes a long word (2 bytes) which is configured as per Targa + + * specs as ARRRRRGG GGGBBBBB. It then breaks it up into the + + * corresponding components. And adds them into the vfb. + + */ + +static void + +#ifdef __STDC__ + +imTga2ByteExpand_PIX (int byte2, int byte1, ImVfbPtr pPixel, ImVfb* vfb) + +#else + +imTga2ByteExpand_PIX (byte2, byte1, pPixel, vfb) + + int byte2,byte1; /* 2bytes to be expanded */ + + ImVfbPtr pPixel; + + ImVfb *vfb; + +#endif + +{ + + int expatr; /* The attribute bit */ + + int expred; /* The five red bits */ + + int expgreen; /* The five green bits */ + + int expblue; /* The five blue bits */ + + int exptemp; + + expatr = (byte1 &0x80) >> 7; + + expred = (byte1 &0x7c) >> 2; + + expgreen = (byte1 &0x03) << 3; + + exptemp = (byte2 &0xe0) >> 5; + + expgreen = (expgreen |exptemp); + + expblue = (byte2 &0x1f) ; + + + + ImVfbSRed (vfb, pPixel, expred*8); + + ImVfbSGreen (vfb, pPixel, expgreen*8); + + ImVfbSBlue (vfb, pPixel, expblue*8); + + ImVfbSAlpha (vfb, pPixel, expatr); + +} + + + + + +/* FUNCTION + + * + + * imTga2ByteExpand_CLT + + * + + * DESCRIPTION + + * Takes a long word (2 bytes) which is configured as per Targa + + * specs as ARRRRRGG GGGBBBBB. It then breaks it up into the + + * corresponding components. And adds them into the clt. + + */ + +static void + +#ifdef __STDC__ + +imTga2ByteExpand_CLT (int byte2, int byte1, ImCltPtr pColor) + +#else + +imTga2ByteExpand_CLT (byte2, byte1, pColor) + + int byte2,byte1; /* 2bytes to be expanded */ + + ImCltPtr pColor; + +#endif + +{ + + int expatr; /* The attribute bit */ + + int expred; /* The five red bits */ + + int expgreen; /* The five green bits */ + + int expblue; /* The five blue bits */ + + int exptemp; + + + + expatr = (byte1 &0x80) >> 7; + + expred = (byte1 &0x7c) >> 2; + + expgreen = (byte1 &0x03) << 3; + + exptemp = (byte2 &0xe0) >> 5; + + expgreen = (expgreen |exptemp); + + expblue = (byte2 &0x1f) ; + + + + ImCltSBlue (pColor, expblue); + + ImCltSGreen (pColor, expgreen); + + ImCltSRed (pColor, expred); + +} + + + + + +/* + + * FUNCTION + + * ImXRead - read a Stardent AVS X file + + * + + * DESCRIPTION + + * The image size is read in, followed by the pixel values. + + * A VFB is added to the tag table. + + */ + + + +static int /* returns number of tags read in */ + +#ifdef __STDC__ + +imTgaRead( int ioType, int fd, FILE* fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imTgaRead( ioType, fd, fp, flagsTable, tagTable ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + +#endif + +{ + + unsigned char *pBuffer; /* buffer pointer */ + + ImVfbPtr pPixel; /* VFB pixel pointer */ + + ImVfb *vfb; /* Read in image */ + + ImClt *clt; /* New clt */ + + ImCltPtr pColor; /* CLT entry pointer */ + + imTgaHeaderInfo header; /* Tga header info pointer */ + + unsigned char *buffer; /* Run buffer */ + + char *TgaName; /* Name of targa image in file */ + + int i,x,y; /* Counter */ + + int colortable; /* color map flag */ + + int n,m,h,w,z,q; /* convenient short names */ + + int tga_attbits; /* number of attribute bits per pixel */ + + int tga_orgbita; /* screen origin bit 4 */ + + int tga_orgbitb; /* screen origin bit 5 */ + + int tga_rsrvd; /* data storage interleave bits */ + + char message[500]; /* Tmp error message text */ + + + + /* the binary byte order is lo-hi */ + + BinByteOrder (BINLBF); + + + + /* + + * Read in the header.. + + */ + + if (ImBinReadStruct (ioType, fd, fp, &header, imTgaHeaderFields)==-1) + + { + + ImReturnBinError(); + + } + + w = header.tga_imwid; + + h = header.tga_imhgt; + + z = header.tga_stype; + + q = header.tga_spix; + + tga_attbits = header.tga_imdesc & 0xf; + + tga_orgbita = (header.tga_imdesc & 0x10) >> 4; + + tga_orgbitb = (header.tga_imdesc & 0x20) >> 5; + + tga_rsrvd = (header.tga_imdesc & 0xc0) >> 6; + + + + /* makes sure the type is one we deal with, check color map + + flag if there should be one */ + + colortable=0; + + switch (z) + + { + + case IMTGA_NODATA: + + ImErrorFatal (ImQError(), -1, IMENOIMAGE); + + /* break; (not reached) */ + + case IMTGA_UNCOMPCOLORMAP: + + case IMTGA_RLECOLORMAP: + + colortable=1; + + break; + + case IMTGA_UNCOMPRGB: + + case IMTGA_UNCOMPBW: + + case IMTGA_RLERGB: + + case IMTGA_RLEBW: + + break; + + case IMTGA_HUFF: + + case IMTGA_HUFF4TYPE: + + default: + + ImErrorFatal (ImQError(), -1, IMEFORMAT); + + /* break; (not reached) */ + + } + + + + /* Make sure that there is a color map if the type requires one */ + + if ((colortable) & (!header.tga_cmtype)) + + ImErrorFatal (ImQError(), -1, IMENOCLT); + + + + /* If there is a color table when one doesn't need to be there + + save it anyway */ + + if (header.tga_cmtype) colortable=1; + + + + /* Allocate a VFB of the required size and type */ + + switch (z) + + { + + case IMTGA_UNCOMPCOLORMAP: /*color mapped image */ + + case IMTGA_UNCOMPBW: /*monochrome image */ + + case IMTGA_RLECOLORMAP: /*RLE color mapped image */ + + case IMTGA_RLEBW: /* compressed monochrome image*/ + + switch (q) + + { + + case 8: /*Targa 16 */ + + if ( (vfb = ImVfbAlloc (w, h, IMVFBINDEX8) ) ==IMVFBNULL) + + { + + ImErrorFatal (ImQError(), -1, IMEMALLOC); + + } + + break; + + case 16: /*Targa 24 (and hopefully 32*/ + + if ( (vfb = ImVfbAlloc (w, h, IMVFBINDEX16) ) ==IMVFBNULL) + + { + + ImErrorFatal (ImQError(), -1, IMEMALLOC); + + } + + break; + + default: /* unsupported type 1 */ + + sprintf (message, "Weird number of bits per pixel stored'%i'",q); + + ImErrorFatal (message, -1, IMESYNTAX); + + /* break; (not reached) */ + + } + + break; + + + + case IMTGA_UNCOMPRGB: /*RGB image */ + + case IMTGA_RLERGB: /*RLE RGB image */ + + switch (q) + + { + + case 24: /* No attribute byte */ + + if ( (vfb =ImVfbAlloc (w,h,IMVFBRGB) ) == IMVFBNULL) + + { + + ImErrorFatal (ImQError(), -1, IMEMALLOC); + + } + + break; + + case 16: /* ARRRRRGG GGGBBBBB */ + + case 32: /* Attribute byte as well */ + + if ((vfb=ImVfbAlloc (w,h,IMVFBRGB|IMVFBALPHA))==IMVFBNULL) + + { + + ImErrorFatal (ImQError(), -1, IMEMALLOC); + + } + + break; + + default: /*kind've a weird amount of bits*/ + + sprintf (message, "Weird number of bits per pixel stored'%i'",q); + + ImErrorFatal (message, -1, IMESYNTAX); + + /* break; (not reached) */ + + } + + break; + + + + default: + + ImErrorFatal (ImQError(), -1, IMEFORMAT); + + /* break; (not reached) */ + + } + + + + /* if needed allocate a name field and read into it */ + + if (header.tga_numc_ident) + + { + + ImMalloc (TgaName, char*, header.tga_numc_ident+1); + + ImBinRead (ioType, fd, fp, TgaName, UCHAR,1, header.tga_numc_ident); + + + + /* information to printout if verbose flag has been set */ + + sprintf( message, "%s", TgaName); + + ImInfo( "Image Name", message ); + + } + + + + /* information to printout if verbose flag has been set */ + + sprintf( message, "Least Significant Byte First"); + + ImInfo( "Byte Order", message ); + + sprintf( message, "%d x %d",w,h); + + ImInfo( "Resolution", message ); + + + + if ( (z==IMTGA_UNCOMPCOLORMAP) || (z==IMTGA_RLECOLORMAP) ) + + sprintf (message, "%d-bit Color Indexed", header.tga_spix); + + + + if ( (z==IMTGA_UNCOMPRGB) || (z==IMTGA_RLERGB) ) + + sprintf (message, "%d-bit RGB", header.tga_spix); + + + + if ( (z==IMTGA_UNCOMPBW) || (z==IMTGA_RLEBW) ) + + sprintf (message, "%d-bit Grayscale", header.tga_spix); + + ImInfo( "Type", message ); + + + + /* if needed allocate a color lookup table and read into it*/ + + if (colortable) + + { + + n=header.tga_cmlen; + + m=header.tga_cmsize; + + if ((clt = ImCltAlloc (n)) == IMCLTNULL) + + ImErrorFatal (ImQError(), -1, IMEMALLOC); + + + + /* information to printout if verbose flag has been set */ + + sprintf (message, "%d Entries",m); + + ImInfo( "Color Table", message ); + + + + /* there appears to be no interleaving for targa color tables */ + + /* there is however a variable number of bytes per entry */ + + switch (m) + + { + + case 16: /* ARRRRRGG GGGBBBBB */ + + ImMalloc ( buffer, unsigned char *, sizeof(unsigned char)*n*2); + + pColor = ImCltQFirst (clt); + + + + if (ImBinRead (ioType,fd,fp,buffer, UCHAR, 1, n*2)== -1) + + { + + free ( (char *)buffer); + + ImReturnBinError(); + + } + + n *=2; + + for (i =0; i8) && (z<12)) + + sprintf (message, "Run Length Encoded"); + + else + + sprintf (message, "none"); + + ImInfo( "Compression Type", message ); + + + + if (((q==16) || (q==32) ) && ( (z==IMTGA_UNCOMPRGB) || (z==IMTGA_RLERGB))) + + sprintf (message, "%d-bit", q); + + else + + sprintf (message, "none"); + + ImInfo( "Alpha Channel", message ); + + + + /* Now we read the image pixel data in from our data file */ + + + + switch (z) + + { + + case IMTGA_UNCOMPCOLORMAP: /* CLT unencoded */ + + case IMTGA_UNCOMPBW: /* GreyScale */ + + imReadTga_1and3 (ioType,fd,fp,vfb, q,h,w); + + break; + + + + case IMTGA_UNCOMPRGB: /* RGB unencoded */ + + imReadTga_2 (ioType,fd,fp,vfb, q,h,w); + + break; + + + + case IMTGA_RLECOLORMAP: /* CLT RLE encoded */ + + case IMTGA_RLEBW: /* B&W RLE encoded */ + + imReadTga_9and11 (ioType,fd,fp,vfb,q,h,w); + + break; + + + + case IMTGA_RLERGB: /* RGB RLE encoded */ + + imReadTga_10 (ioType,fd,fp,vfb,q,h,w); + + break; + + + + default: + + ImErrorFatal (ImQError(), -1, IMEFORMAT); + + /* break; (not reached) */ + + + + } + + + + /* Attach (set) the vfb's color lookup table if one */ + + if (colortable) + + ImVfbSClt(vfb, clt); + + + + if ( (!tga_orgbita) & (!tga_orgbitb)) + + { + + /* If the screen origin is in the lower left hand corner flip */ + + sprintf (message, "Lower Left-Hand Corner"); + + ImVfbFlip (vfb, IMVFBYFLIP, vfb); + + } + + else if ( (tga_orgbita) & (!tga_orgbitb)) + + { + + /* If the screen origin is in the lower right hand corner flip*/ + + sprintf (message, "Lower Right-Hand Corner"); + + ImVfbFlip (vfb, IMVFBXYFLIP, vfb); + + } + + else if ( (tga_orgbita) & (tga_orgbitb)) + + { + + /* If the screen origin is in the top right hand corner flip */ + + sprintf (message, "Upper Right-Hand Corner"); + + ImVfbFlip (vfb, IMVFBXFLIP, vfb); + + } + + else + + sprintf (message, "Upper Left-Hand Corner"); + + + + ImInfo( "Screen Origin", message ); + + + + /* Attach name field to vfb is there is one */ + + if (header.tga_numc_ident) + + { + + TagTableAppend (tagTable, + + TagEntryAlloc("image name",POINTER,&TgaName)); + + } + + + + /* Attach vfb to tag table */ + + TagTableAppend (tagTable, TagEntryAlloc ("image vfb", POINTER, &vfb)); + + return(1); + +} + + + +/* + + * FUNCTION + + * imReadTga_1and3 - read either a type 1 or type 3 that has 1 or 2 bytes + + * per pixel + + * + + * type 1 - color lookup table that is not encoded. + + * type 3 - greyscale image that is not encoded. + + */ + +static int /* Returns 1 on successful read */ + +#ifdef __STDC__ + +imReadTga_1and3 (int ioType,int fd,FILE *fp,ImVfb *vfb, int q,int h,int w) + +#else + +imReadTga_1and3 (ioType,fd,fp,vfb, q,h,w) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + ImVfb *vfb; /* Read in Image */ + + int q; /* How many bits per pixel */ + + int h; /* Height of image */ + + int w; /* width of image */ + +#endif + +{ + + sdsc_uint16 *bufferl; /* Run buffer (type long) */ + + sdsc_uint16 *pBufferl; /* bufferl pointer */ + + ImVfbPtr pPixel; /*VFB pixel pointer */ + + int x,y; /* Counters */ + + unsigned char *buffer; /* Run buffer */ + + unsigned char *pBuffer; /* buffer pointer */ + + /* interleaving best be noninterleaved as per specs*/ + + + + if (q==8) /* one byte per index */ + + { + + ImMalloc (buffer, unsigned char *, w * sizeof(unsigned char) ); + + pPixel = ImVfbQFirst (vfb); + + for (y =0; y0) { + + if (ImBinRead (ioType,fd,fp,buffer,UCHAR,1,1) == -1) + + { + + free ( (char *)buffer); + + ImReturnBinError(); + + } + + + + pBuffer = buffer; + + + + leadByte = (*pBuffer++); + + rle = (leadByte&0x80); /* rle = first bit */ + + count = (leadByte&0x7F)+1; /*count = last 7 bytes +1 */ + + + + if (rle) /* run length encoded packet */ + + { + + if (ImBinRead (ioType,fd,fp,buffer,UCHAR,1,1) == -1) + + { + + free ( (char *)buffer); + + ImReturnBinError(); + + } + + pBuffer = buffer; + + for (y=0; y0 ) { + + if (ImBinRead (ioType,fd,fp,bufferl,UINT16,2,1) == -1) + + { + + free ( (sdsc_uint16 *)bufferl); + + ImReturnBinError(); + + } + + + + pBufferl = bufferl; + + + + leadByte = (*pBufferl++); + + rle = (leadByte&0x80); /* rle = first bit */ + + count = (leadByte&0x7F)+1; /*count = last 7 bytes +1 */ + + + + if (rle) /* run length encoded packet */ + + { + + if (ImBinRead (ioType,fd,fp,bufferl,UINT16,2,1) == -1) + + { + + free ( (sdsc_uint16 *)bufferl); + + ImReturnBinError(); + + } + + pBufferl = bufferl; + + for (y=0; y0) { + + if (ImBinRead (ioType,fd,fp,buffer,UCHAR,1,1) == -1) + + { + + free ( (char *)buffer); + + ImReturnBinError(); + + } + + + + pBuffer = buffer; + + + + leadByte = (*pBuffer++); + + rle = (leadByte&0x80); /* rle = first bit */ + + count = (leadByte&0x7F)+1; /*count = last 7 bytes +1 */ + + + + if (rle) /* run length encoded packet */ + + { + + if (ImBinRead (ioType,fd,fp,buffer,UCHAR,1,2) == -1) + + { + + free ( (char *)buffer); + + ImReturnBinError(); + + } + + pBuffer = buffer; + + for (y=0; y0 ) { + + if (ImBinRead (ioType,fd,fp,buffer,UCHAR,1,1) == -1) + + { + + free ( (char *)buffer); + + ImReturnBinError(); + + } + + + + pBuffer = buffer; + + + + leadByte = (*pBuffer++); + + rle = (leadByte&0x80); /* rle = first bit */ + + count = (leadByte&0x7F)+1; /*count = last 7 bytes +1 */ + + + + if (rle) /* run length encoded packet */ + + { + + if (ImBinRead (ioType,fd,fp,buffer,UCHAR,1,3) == -1) + + { + + free ( (char *)buffer); + + ImReturnBinError(); + + } + + pBuffer = buffer; + + for (y=0; y 0) { + + if (ImBinRead (ioType,fd,fp,buffer,UCHAR,1,1) == -1) + + { + + free ( (char *)buffer); + + ImReturnBinError(); + + } + + + + pBuffer = buffer; + + + + leadByte = (*pBuffer++); + + rle = (leadByte&0x80); /* rle = first bit */ + + count = (leadByte&0x7F)+1; /*count = last 7 bytes +1 */ + + + + if (rle) /* run length encoded packet */ + + { + + if (ImBinRead (ioType,fd,fp,buffer,UCHAR,1,4) == -1) + + { + + free ( (char *)buffer); + + ImReturnBinError(); + + } + + pBuffer = buffer; + + for (y=0; y> 8; \ + imTgaRLEBuf[2] = (index16 & 0x00ff); \ + if ( ImBinWrite( ioType, fd, fp, imTgaRLEBuf, UCHAR, 1, 3)==-1) \ + { \ + ImReturnBinError( ); \ + } + + + +#define imTgaDumpConstantRun3(b,g,r,len) \ + imTgaRLEBuf[0] = (unsigned char) len; \ + imTgaRLEBuf[0] |= 0x80; \ + imTgaRLEBuf[1] = b; \ + imTgaRLEBuf[2] = g; \ + imTgaRLEBuf[3] = r; \ + if ( ImBinWrite( ioType, fd, fp, imTgaRLEBuf, UCHAR, 1, 4)==-1) \ + { \ + ImReturnBinError( ); \ + } + + +#define imTgaDumpConstantRun4(b,g,r,a,len) \ + imTgaRLEBuf[0] = (unsigned char) len; \ + imTgaRLEBuf[0] |= 0x80; \ + imTgaRLEBuf[1] = b; \ + imTgaRLEBuf[2] = g; \ + imTgaRLEBuf[3] = r; \ + imTgaRLEBuf[4] = a; \ + if ( ImBinWrite( ioType, fd, fp, imTgaRLEBuf, UCHAR, 1, 5)==-1) \ + { \ + ImReturnBinError( ); \ + } + + +/* + + * FUNCTION + + * imTgaWriteRGB24 - write a Targa RGB file + + * + + * DESCRIPTION + + * That VFB is queried, and the Targa file header written out. + + * The VFB data is then copied to the file. + + */ + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imTgaWriteRGB24( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imTgaWriteRGB24( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointer */ + + imTgaHeaderInfo header; /* Tga file header */ + + unsigned char *buffer; /* line buffer */ + + unsigned char *pBuffer; /* current location in buffer */ + + int n,x,y,i,j; /* Counter */ + + TagEntry *tagEntry; /* Tmp table entry */ + + char *tmpString; /* Tmp string holder */ + + int stype; /* Storage type */ + + int spix; /* Number of bits per image pixel */ + + int cmsize; /* Number of bits per color map pixel */ + + + + /* Set up and write out the header */ + + BinByteOrder (BINLBF); + + + + TagEntryQValue (TagTableQDirect (tagTable, "image vfb", 0), &vfb); + + + + stype =IMTGA_UNCOMPRGB; + + spix =24; + + cmsize =24; + + /* write the header out */ + + imTgaWriteHeader ( ioType, fd, fp,flagsTable, tagTable,stype,spix,cmsize); + + x = ImVfbQWidth(vfb); + + y = ImVfbQHeight(vfb); + + + + /* allocate buffer space for (width*length*3) scan lines */ + + ImMalloc (buffer, unsigned char *,3*x); + + pptr = ImVfbQFirst (vfb); + + + + for (j=0; j 0, dump varRun. + + * otherwise, add this pixel to constantRun. + + */ + + if (ImVfbSameRGB(vfb,lastPixel,thisPixel)) + + { + + if (varRunLength > 0) + + { + + imTgaDumpVarRun(3,varRun, varRunLength - 1); + + varRunLength = 0; + + } + + constantRunLength++; + + if (constantRunLength==128) + + { /* Dump a constant run. */ + + imTgaDumpConstantRun3(ImVfbQBlue(vfb,lastPixel), + + ImVfbQGreen(vfb, lastPixel), + + ImVfbQRed(vfb, lastPixel), + + constantRunLength - 1); + + constantRunLength = 0; + + } + + } + + else /* This is different from the last character. */ + + /* Do the exact opposite of the if-branch. */ + + { + + varRun[varRunLength*3] = ImVfbQBlue(vfb, lastPixel); + + varRun[varRunLength*3+1] = ImVfbQGreen(vfb, lastPixel); + + varRun[varRunLength*3+2] = ImVfbQRed(vfb, lastPixel); + + + + if (constantRunLength > 0) + + { + + imTgaDumpConstantRun3(ImVfbQBlue(vfb, lastPixel), + + ImVfbQGreen(vfb, lastPixel), + + ImVfbQRed(vfb, lastPixel), + + constantRunLength); + + constantRunLength = 0; + + } + + else + + { /* Only increment if this is not the end of a constant run. */ + + varRunLength++; + + } + + if (varRunLength==128) + + { /* Dump variable run. */ + + imTgaDumpVarRun(3,varRun, varRunLength - 1); + + varRunLength = 0; + + } + + } + + lastPixel = thisPixel; + + } + + + + /* Dump whatever is left. */ + + if (constantRunLength==0 && varRunLength==0) + + { /* single pixel is left. */ + + imTgaDumpConstantRun3(ImVfbQBlue(vfb, thisPixel), + + ImVfbQGreen(vfb, thisPixel), + + ImVfbQRed(vfb, thisPixel), + + 0 ); + + } + + if (constantRunLength > 0) + + { + + imTgaDumpConstantRun3(ImVfbQBlue(vfb, thisPixel), + + ImVfbQGreen(vfb, thisPixel), + + ImVfbQRed(vfb, thisPixel), + + constantRunLength ); + + } + + if (varRunLength > 0) + + { + + varRun[varRunLength*3] = ImVfbQBlue (vfb, lastPixel); + + varRun[varRunLength*3+1] = ImVfbQGreen(vfb, lastPixel); + + varRun[varRunLength*3+2] = ImVfbQRed (vfb, lastPixel); + + imTgaDumpVarRun(3,varRun, varRunLength ); + + } + + + + return (1); + +} + + + +/* + + * FUNCTION + + * imTgaWriteRGB32 - write a Targa RGB file that has an alpha channel + + * + + * DESCRIPTION + + * That VFB is queried, and the Targa file header written out. + + * The VFB data is then copied to the file. + + */ + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imTgaWriteRGB32( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable) + +#else + +imTgaWriteRGB32( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointer */ + + imTgaHeaderInfo header; /* Tga file header */ + + unsigned char *buffer; /* line buffer */ + + unsigned char *pBuffer; /* current location in buffer */ + + int n,x,y,i,j; /* Counter */ + + TagEntry *tagEntry; /* Tmp table entry */ + + char *tmpString; /* Tmp string holder */ + + int stype; /* Storage type */ + + int spix; /* Number of bits per image pixel */ + + int cmsize; /* Number of bits per color map pixel */ + + + + + + /* Set up and write out the header */ + + BinByteOrder (BINLBF); + + + + TagEntryQValue (TagTableQDirect (tagTable, "image vfb", 0), &vfb); + + + + stype = IMTGA_UNCOMPRGB; + + spix =32; + + cmsize =24; + +/* write the header out */ + + imTgaWriteHeader ( ioType, fd, fp,flagsTable, tagTable,stype,spix,cmsize); + + + + x = ImVfbQWidth(vfb); + + y = ImVfbQHeight(vfb); + + + + /* allocate buffer space for (width*length83) scan lines */ + + ImMalloc (buffer, unsigned char *,4*x); + + pptr = ImVfbQFirst (vfb); + + + + for (j=0; j 0, dump varRun. + + * otherwise, add this pixel to constantRun. + + */ + + if (ImVfbSameRGBA(vfb,lastPixel,thisPixel)) + + { + + if (varRunLength > 0) + + { + + imTgaDumpVarRun(4,varRun, varRunLength - 1); + + varRunLength = 0; + + } + + constantRunLength++; + + if (constantRunLength==128) + + { /* Dump a constant run. */ + + imTgaDumpConstantRun4( + + ImVfbQBlue(vfb,lastPixel), + + ImVfbQGreen(vfb, lastPixel), + + ImVfbQRed(vfb, lastPixel), + + ImVfbQAlpha(vfb,lastPixel), + + constantRunLength - 1); + + constantRunLength = 0; + + } + + } + + else /* This is different from the last character. */ + + /* Do the exact opposite of the if-branch. */ + + { + + varRun[varRunLength*4] = ImVfbQBlue(vfb, lastPixel); + + varRun[varRunLength*4+1] = ImVfbQGreen(vfb, lastPixel); + + varRun[varRunLength*4+2] = ImVfbQRed(vfb, lastPixel); + + varRun[varRunLength*4+3] = ImVfbQAlpha(vfb, lastPixel); + + + + if (constantRunLength > 0) + + { + + imTgaDumpConstantRun4( + + ImVfbQBlue(vfb, lastPixel), + + ImVfbQGreen(vfb, lastPixel), + + ImVfbQRed(vfb, lastPixel), + + ImVfbQAlpha(vfb, lastPixel), + + constantRunLength); + + constantRunLength = 0; + + } + + else + + { /* Only increment if this is not the end of a constant run. */ + + varRunLength++; + + } + + if (varRunLength==128) + + { /* Dump variable run. */ + + imTgaDumpVarRun(4,varRun, varRunLength - 1); + + varRunLength = 0; + + } + + } + + lastPixel = thisPixel; + + } + + + + /* Dump whatever is left. */ + + if (constantRunLength==0 && varRunLength==0) + + { /* single pixel is left. */ + + imTgaDumpConstantRun4( + + ImVfbQBlue(vfb, thisPixel), + + ImVfbQGreen(vfb, thisPixel), + + ImVfbQRed(vfb, thisPixel), + + ImVfbQAlpha(vfb,thisPixel), + + 0 ); + + } + + if (constantRunLength > 0) + + { + + imTgaDumpConstantRun4( + + ImVfbQBlue(vfb, thisPixel), + + ImVfbQGreen(vfb, thisPixel), + + ImVfbQRed(vfb, thisPixel), + + ImVfbQAlpha(vfb,thisPixel), + + constantRunLength ); + + } + + if (varRunLength > 0) + + { + + varRun[varRunLength*4] = ImVfbQBlue (vfb, lastPixel); + + varRun[varRunLength*4+1] = ImVfbQGreen(vfb, lastPixel); + + varRun[varRunLength*4+2] = ImVfbQRed (vfb, lastPixel); + + varRun[varRunLength*4+3] = ImVfbQAlpha (vfb, lastPixel); + + imTgaDumpVarRun(4,varRun, varRunLength ); + + } + + + + return (1); + +} + + + + + + + +/* + + * FUNCTION + + * imTgaWriteCLT8 - write a Targa CLT file that has 8 bits pre channel + + * + + * DESCRIPTION + + * That VFB is queried, and the Targa file header written out. + + * The VFB data is then copied to the file. + + */ + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imTgaWriteCLT8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable) + +#else + +imTgaWriteCLT8( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + TagEntry *tagEntry; /* Tmp table entry */ + + int stype; /* Storage type */ + + int spix; /* Number of bits per image pixel */ + + int cmsize; /* Number of bits per color map pixel */ + + /* Set up and write out the header */ + + stype = IMTGA_UNCOMPCOLORMAP; + + spix = 8; + + cmsize = 24; + + /*write header out */ + + imTgaWriteHeader (ioType, fd, fp, flagsTable, tagTable,stype,spix,cmsize); + + + + imTgaWrite8image (ioType, fd, fp, flagsTable, tagTable); + + return(1); + +} + + + + + +/* + + * FUNCTION + + * imTgaWrite8image - write a Targa file that has 8 bits pre channel + + * + + * DESCRIPTION + + * That VFB is queried, The VFB data is then copied to the file. + + */ + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imTgaWrite8image( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imTgaWrite8image( ioType, fd, fp, flagsTable, tagTable ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointer */ + + imTgaHeaderInfo header; /* Tga file header */ + + unsigned char *buffer; /* line buffer */ + + unsigned char *pBuffer; /* current location in buffer */ + + int x,y,i,j; /* Counter */ + + TagEntry *tagEntry; /* Tmp table entry */ + + + + BinByteOrder (BINLBF); + + + + TagEntryQValue (TagTableQDirect (tagTable, "image vfb", 0), &vfb); + + x =ImVfbQWidth(vfb); + + y = ImVfbQHeight(vfb); + + + + /* allocate buffer space for (width*length83) scan lines */ + + ImMalloc (buffer, unsigned char *, 1*x); + + pptr = ImVfbQFirst (vfb); + + + + for (j=0; j 0, dump varRun. + + * otherwise, add this pixel to constantRun. + + */ + + if (ImVfbSameIndex8(vfb,lastPixel,thisPixel)) + + { + + if (varRunLength > 0) + + { + + imTgaDumpVarRun(1,varRun, varRunLength - 1); + + varRunLength = 0; + + } + + constantRunLength++; + + if (constantRunLength==128) + + { /* Dump a constant run. */ + + imTgaDumpConstantRun1(ImVfbQIndex(vfb,lastPixel), + + constantRunLength - 1); + + constantRunLength = 0; + + } + + } + + else /* This is different from the last character. */ + + /* Do the exact opposite of the if-branch. */ + + { + + varRun[varRunLength] = ImVfbQIndex(vfb, lastPixel); + + + + if (constantRunLength > 0) + + { + + imTgaDumpConstantRun1(ImVfbQIndex(vfb, lastPixel), + + constantRunLength); + + constantRunLength = 0; + + } + + else + + { /* Only increment if this is not the end of a constant run. */ + + varRunLength++; + + } + + if (varRunLength==128) + + { /* Dump variable run. */ + + imTgaDumpVarRun(1,varRun, varRunLength - 1); + + varRunLength = 0; + + } + + } + + lastPixel = thisPixel; + + } + + + + /* Dump whatever is left. */ + + if (constantRunLength==0 && varRunLength==0) + + { /* single pixel is left. */ + + imTgaDumpConstantRun1(ImVfbQIndex(vfb, thisPixel), 0 ); + + } + + if (constantRunLength > 0) + + { + + imTgaDumpConstantRun1(ImVfbQIndex(vfb, thisPixel), + + constantRunLength ); + + } + + if (varRunLength > 0) + + { + + varRun[varRunLength] = ImVfbQIndex (vfb, lastPixel); + + imTgaDumpVarRun(1,varRun, varRunLength ); + + } + + return (1); + +} + + + +/* + + * FUNCTION + + * imTgaWriteCLT16 - write a Targa CLT file that has 16 bits pre channel + + * + + * DESCRIPTION + + * That VFB is queried, and the Targa file header written out. + + * The VFB data is then copied to the file. + + */ + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imTgaWriteCLT16( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable) + +#else + +imTgaWriteCLT16( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + int stype; /* Storage type */ + + int spix; /* Number of bits per image pixel */ + + int cmsize; /* Number of bits per color map pixel */ + + + + /* Set up and write out the header */ + + stype = IMTGA_UNCOMPCOLORMAP; + + spix = 16; + + cmsize = 24; + + + + /* Write header out */ + + + + imTgaWriteHeader (ioType, fd, fp, flagsTable, tagTable,stype,spix,cmsize); + + + + imTgaWrite16image (ioType, fd, fp, flagsTable, tagTable); + + return(1); + +} + + + + + + + +/* + + * FUNCTION + + * imTgaWrite16image - write a Targa CLT file that has 16 bits per channel + + * + + * DESCRIPTION + + * That VFB is queried, The VFB data is then copied to the file. + + */ + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imTgaWrite16image( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imTgaWrite16image( ioType, fd, fp, flagsTable, tagTable ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointer */ + + imTgaHeaderInfo header; /* Tga file header */ + + unsigned char *bufferl; /* line buffer */ + + unsigned char *pBufferl; /* current location in buffer */ + + int x,y,i,j; /* Counter */ + + TagEntry *tagEntry; /* Tmp table entry */ + + + + + + BinByteOrder (BINLBF); + + + + TagEntryQValue (TagTableQDirect (tagTable, "image vfb", 0), &vfb); + + + + x = ImVfbQWidth(vfb); + + y = ImVfbQHeight(vfb); + + + + /* allocate buffer space for (width*length) scan lines */ + + + + ImMalloc (bufferl, unsigned char *, 2 * x * sizeof(unsigned char)); + + pptr = ImVfbQFirst (vfb); + + + + for (j=0; j> 8; + + ImVfbSInc (vfb, pptr); + + } + + + + if (ImBinWrite (ioType, fd, fp, bufferl, UCHAR, 1, x*2 ) ==-1) + + { + + free ( bufferl); + + ImReturnBinError( ); + + } + + + + } + + + + free (bufferl); + + return (1); + + + +} + + + +/* + + * FUNCTION + + * imTgaWriteCLTRLE16 - write a Targa CLT file that has 16 bits per channel + + * and is RLE. + + * + + * DESCRIPTION + + * That VFB is queried, and the Targa file header written out. + + * The VFB data is then copied to the file. + + */ + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imTgaWriteCLTRLE16( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable) + +#else + +imTgaWriteCLTRLE16( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + int stype; /* Storage type */ + + int spix; /* Number of bits per image pixel */ + + int cmsize; /* Number of bits per color map pixel */ + + + + /* Set up and write out the header */ + + + + BinByteOrder (BINLBF); + + stype = IMTGA_RLECOLORMAP; + + spix = 16; + + cmsize = 24; + + + + /* write the header out */ + + imTgaWriteHeader ( ioType, fd, fp,flagsTable, tagTable,stype,spix,cmsize); + + + + imTgaWriteRLE16(ioType,fd,fp,flagsTable,tagTable); + + return(1); + +} + + + +/* + + * FUNCTION + + * imTgaWriteRLE16 - write a Targa CLT file that has 16 bits pre channel + + * and is RLE. + + * + + * DESCRIPTION + + * That VFB is queried, The VFB data is then copied to the file. + + */ + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imTgaWriteRLE16( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imTgaWriteRLE16( ioType, fd, fp, flagsTable, tagTable ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr vfbPtr; /* Pixel pointer */ + + TagEntry *tagEntry; /* Tmp table entry */ + + int stype; /* Storage type */ + + int spix; /* Number of bits per image pixel */ + + int cmsize; /* Number of bits per color map pixel */ + + unsigned char varRun[128*2]; /* Holds a variable run */ + + int varRunLength; /* Length of this variable run. */ + + int constantRunLength; /* Length of this constant run. */ + + ImVfbPtr lastPixel, thisPixel; /* point to pixels. */ + + int i; /* loop index */ + + + + /* + + * In this function, a variable run refers to a series of different + + * pixel values. The 'length' of such a run is one less than the + + * number of pixels. For instance, if our pixel values were + + * "A B C D" then this would be a "variable run of length 3". + + * + + * Similarly, "A A A" would be a "constant run of length 3". + + */ + + + + TagEntryQValue (TagTableQDirect (tagTable, "image vfb", 0), &vfb); + + + + /* Initialize stuff. */ + + varRunLength = 0; + + constantRunLength = 0; + + thisPixel = ImVfbQFirst( vfb ); + + lastPixel = thisPixel; + + + + for (i = 1;i < ImVfbQWidth(vfb) * ImVfbQHeight( vfb ) ; i++) + + { + + ImVfbSInc(vfb,thisPixel); + + + + /* + + * If this pixel is the same as the last one then: + + * if varRunLength > 0, dump varRun. + + * otherwise, add this pixel to constantRun. + + */ + + if (ImVfbSameIndex16(vfb,lastPixel,thisPixel)) + + { + + if (varRunLength > 0) + + { + + imTgaDumpVarRun(2,varRun, varRunLength - 1); + + varRunLength = 0; + + } + + constantRunLength++; + + if (constantRunLength==128) + + { /* Dump a constant run. */ + + imTgaDumpConstantRun2(ImVfbQIndex16(vfb,lastPixel), + + constantRunLength - 1); + + constantRunLength = 0; + + } + + } + + else /* This is different from the last character. */ + + /* Do the exact opposite of the if-branch. */ + + { + + varRun[varRunLength*2] = (ImVfbQIndex16(vfb, lastPixel) & 0xff00) >> 8; + + varRun[varRunLength*2+1] = ImVfbQIndex16(vfb, lastPixel) & 0x00ff; + + + + if (constantRunLength > 0) + + { + + imTgaDumpConstantRun2(ImVfbQIndex16(vfb, lastPixel), + + constantRunLength); + + constantRunLength = 0; + + } + + else + + { /* Only increment if this is not the end of a constant run. */ + + varRunLength++; + + } + + if (varRunLength==128) + + { /* Dump variable run. */ + + imTgaDumpVarRun(2,varRun, varRunLength - 1); + + varRunLength = 0; + + } + + } + + lastPixel = thisPixel; + + } + + + + /* Dump whatever is left. */ + + if (constantRunLength==0 && varRunLength==0) + + { /* single pixel is left. */ + + imTgaDumpConstantRun2(ImVfbQIndex16(vfb, thisPixel), 0 ); + + } + + if (constantRunLength > 0) + + { + + imTgaDumpConstantRun2(ImVfbQIndex16(vfb, thisPixel), + + constantRunLength ); + + } + + if (varRunLength > 0) + + { + + varRun[varRunLength*2] = (ImVfbQIndex16(vfb, lastPixel) & 0xff00) >> 8; + + varRun[varRunLength*2+1] = ImVfbQIndex16(vfb, lastPixel) & 0x00ff; + + imTgaDumpVarRun(2,varRun, varRunLength ); + + } + + + + return (1); + +} + + + + + +/* + + * FUNCTION + + * imTgaWriteRGB16 - write a Targa RGB with 2 bytes per pixel file + + * + + * DESCRIPTION + + * That VFB is queried, and the Targa file header written out. + + * The VFB data is then copied to the file. + + */ + + + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imTgaWriteRGB16( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imTgaWriteRGB16( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointer */ + + imTgaHeaderInfo header; /* Tga file header */ + + unsigned char *buffer; /* line buffer */ + + unsigned char *pBuffer; /* current location in buffer */ + + int n,x,y,i,j; /* Counter */ + + TagEntry *tagEntry; /* Tmp table entry */ + + char *tmpString; /* Tmp string holder */ + + int stype; /* Storage type */ + + int spix; /* Number of bits per image pixel */ + + int cmsize; /* Number of bits per color map pixel */ + + unsigned char expBlue,expGreen,expRed; /* Pixels before shrunk */ + + unsigned char byte1,byte2; /* Pixels after shrunk to 2 bytes */ + + + + + + /* Set up and write out the header */ + + BinByteOrder (BINLBF); + + + + TagEntryQValue (TagTableQDirect (tagTable, "image vfb", 0), &vfb); + + + + stype = IMTGA_UNCOMPRGB; + + spix = 16; + + cmsize = 24; + + /* write the header out */ + + imTgaWriteHeader ( ioType, fd, fp,flagsTable, tagTable,stype,spix,cmsize); + + x = ImVfbQWidth(vfb); + + y = ImVfbQHeight(vfb); + + + + /* allocate buffer space for (width*length*3) scan lines */ + + ImMalloc (buffer, unsigned char *,2*x); + + pptr = ImVfbQFirst (vfb); + + + + for (j=0; j>3) <<2 ; + + expGreen= ((ImVfbQGreen (vfb, pptr))>>2) >>4 ; + + byte2 = (byte2 | expRed |expGreen); + + byte1 = ((ImVfbQGreen (vfb, pptr))>>2) << 5; + + expBlue= (ImVfbQBlue (vfb, pptr))>>3 ; + + byte1 = (byte1 |expBlue); + + *pBuffer++ =byte1; + + *pBuffer++ =byte2; + + ImVfbSInc (vfb, pptr); + + } + + + + + + if (ImBinWrite (ioType, fd, fp, buffer, UCHAR,1,pBuffer - buffer) ==-1) + + { + + free ( (char *)buffer); + + ImReturnBinError( ); + + } + + + + } + + + + free ( (char *)buffer); + + return (1); + +} + + + +/* + + * FUNCTION + + * imTgaWriteRGBRLE16 - write a Targa RGB file that is RLE 2 bytes + + * per pixel. + + * + + * DESCRIPTION + + * That VFB is queried, and the Targa file header written out. + + * The VFB data is then copied to the file. + + */ + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imTgaWriteRGBRLE16( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE* fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imTgaWriteRGBRLE16( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointer */ + + imTgaHeaderInfo header; /* Tga file header */ + + unsigned char *bufferRle; /* line buffer for rle packets */ + + unsigned char *bufferUn; /* line buffer for unencoded */ + + unsigned char *bufferCount; /* Count buffer */ + + unsigned char *pBuffer; /* current location in buffer */ + + int n,x,y,i,j; /* Counter */ + + TagEntry *tagEntry; /* Tmp table entry */ + + char *tmpString; /* Tmp string holder */ + + int stype; /* Storage type */ + + int spix; /* Number of bits per image pixel */ + + int cmsize; /* Number of bits per color map pixel */ + + int total; /* counter for number of bytes encoded*/ + + int currentColor; /* What color from vfb are we on */ + + int field; /* The field from the color we are on */ + + int fieldNext; /* The field from the color we are on */ + + int fieldRed; /* The field from the color we are on */ + + int fieldGreen; /* The field from the color we are on */ + + int fieldBlue; /* The field from the color we are on */ + + int fieldAlpha; /* The field from the color we are on */ + + int fieldRedNext; /* The field from the color we are on */ + + int fieldGreenNext; /* The field from the color we are on */ + + int fieldBlueNext; /* The field from the color we are on */ + + int fieldAlphaNext; /* The field from the color we are on */ + + int count; /* Numbe to store in packet */ + + unsigned char byte1,byte2,byte3,byte4;/* Bytes to write from rle packet */ + + int atend; /* whether at end of vfb or not */ + + int stopSpot; /* when should stop reading out of vfb*/ + + unsigned char expBlue,expGreen,expRed; /* Pixels before shrunk */ + + + + + + + + /* Set up and write out the header */ + + BinByteOrder (BINLBF); + + + + TagEntryQValue (TagTableQDirect (tagTable, "image vfb", 0), &vfb); + + + + + + stype = IMTGA_RLERGB; + + spix = 16; + + cmsize = 24; + + /* write the header out */ + + imTgaWriteHeader ( ioType, fd, fp,flagsTable, tagTable,stype,spix,cmsize); + + + + y = ImVfbQWidth(vfb); + + x = ImVfbQHeight(vfb); + + stopSpot=(x*y*1)-3; + + + + /* allocate buffer space for one RLE packet */ + + /* unencoded it can reach up to 129 bytes */ + + ImMalloc (bufferUn, unsigned char *,260); + + ImMalloc (bufferRle, unsigned char *,4); + + ImMalloc (bufferCount, unsigned char *,1); + + pptr = ImVfbQFirst (vfb); + + + + /* get the first pixel color to start with */ + + total =0; /* a running count of bytes encoded */ + + currentColor = 0; /* Color to start with */ + + atend =0; /*not at the end of the vfb yet */ + + + + fieldRed = ImVfbQRed(vfb,pptr); + + fieldGreen = ImVfbQGreen(vfb,pptr); + + fieldBlue = ImVfbQBlue(vfb,pptr); + + ImVfbSInc(vfb,pptr); + + fieldRedNext = ImVfbQRed(vfb,pptr); + + fieldGreenNext = ImVfbQGreen(vfb,pptr); + + fieldBlueNext = ImVfbQBlue(vfb,pptr); + + if ((fieldRed==fieldGreen) && (fieldGreen==fieldBlue)) field=fieldRed; + + else field = -1; + + if ((fieldRedNext==fieldGreenNext) && (fieldGreenNext==fieldBlueNext)) + + fieldNext=fieldRedNext; + + else fieldNext = -2; + + + + do { /* encode width by height scanlines */ + + count=0; /* Count-1 is what's stored*/ + + + + /* see if you should rle something */ + + while ( (count<128) && (field == fieldNext) && (!atend) ) + + { + + if ( ( count+total) > stopSpot) atend=1; + + count++; + + fieldRed = fieldRedNext; + + fieldGreen = fieldGreenNext; + + fieldBlue = fieldBlueNext; + + fieldAlpha = fieldAlphaNext; + + ImVfbSInc(vfb,pptr); + + fieldRedNext = ImVfbQRed(vfb,pptr); + + fieldGreenNext = ImVfbQGreen(vfb,pptr); + + fieldBlueNext = ImVfbQBlue(vfb,pptr); + + fieldAlphaNext = ImVfbQAlpha(vfb,pptr); + + if ((fieldRed==fieldGreen) && (fieldGreen==fieldBlue) + + && (fieldBlue==fieldAlpha)) field=fieldRed; + + else field = -1; + + if ((fieldRedNext==fieldGreenNext) && (fieldGreenNext==fieldBlueNext) && (fieldBlueNext==fieldAlphaNext)) + + fieldNext=fieldRedNext; + + else fieldNext = -2; + + } + + if (count>0) /* Actually have something to encode */ + + { + + count--; + + pBuffer = bufferRle; + + byte1= count | 0x80; /* Store 1 then count in one byte */ + + *pBuffer++ = byte1; + + byte4 = fieldAlpha << 7; + + expRed= (fieldRed>>3) <<2 ; + + expGreen= (fieldGreen>>2) >>4 ; + + byte4 = (byte4 | expRed |expGreen); + + byte3 = (fieldGreen>>2) << 5; + + expBlue= fieldBlue >>3 ; + + byte3 = (byte3 |expBlue); + + *pBuffer++ = byte3; + + *pBuffer++ = byte4; + + if (ImBinWrite (ioType,fd,fp,bufferRle,UCHAR,1,3)==-1) + + { + + free ( (char *) bufferRle); + + ImReturnBinError( ); + + } + + } + + else + + { + + count= -1; + + pBuffer = bufferUn; + + while ( (count<127) && (field !=fieldNext) && (!atend) ) + + { + + byte4 = fieldAlpha << 7; + + expRed= (fieldRed>>3) <<2 ; + + expGreen= (fieldGreen>>2) >>4 ; + + byte4 = (byte4 | expRed |expGreen); + + byte3 = (fieldGreen>>2) << 5; + + expBlue= fieldBlue >>3 ; + + byte3 = (byte3 |expBlue); + + *pBuffer++ = byte3; + + *pBuffer++ = byte4; + + if ( ( count+total) > stopSpot) atend=1; + + count++; + + fieldRed = fieldRedNext; + + fieldGreen = fieldGreenNext; + + fieldBlue = fieldBlueNext; + + fieldAlpha = fieldAlphaNext; + + ImVfbSInc(vfb,pptr); + + fieldRedNext = ImVfbQRed(vfb,pptr); + + fieldGreenNext = ImVfbQGreen(vfb,pptr); + + fieldBlueNext = ImVfbQBlue(vfb,pptr); + + fieldAlphaNext = ImVfbQAlpha(vfb,pptr); + + if ((fieldRed==fieldGreen) && (fieldGreen==fieldBlue) + + && (fieldBlue==fieldAlpha)) field=fieldRed; + + else field = -1; + + if ((fieldRedNext==fieldGreenNext) && (fieldGreenNext==fieldBlueNext) && (fieldBlueNext==fieldAlphaNext)) + + fieldNext=fieldRedNext; + + else fieldNext = -2; + + } + + + + + + byte1= count & 0x7F; /* Store 0 then count in one byte*/ + + pBuffer = bufferCount; + + *pBuffer = byte1; + + if (ImBinWrite (ioType,fd,fp,bufferCount,UCHAR,1,1)==-1) + + { + + free ( (char *) bufferCount); + + ImReturnBinError( ); + + } + + if (ImBinWrite (ioType,fd,fp,bufferUn,UCHAR,1,2*(count+1))==-1) + + { + + free ( (char *) bufferUn); + + ImReturnBinError( ); + + } + + + + + + } + + + + + + total+=count+1; + + + + } while (!atend); /* encode width by height scanlines */ + + + + free ( (char *)bufferRle); + + free ( (char *)bufferUn); + + free ( (char *)bufferCount); + + + + return (1); + + + +} + + + + + + + +/* + + * FUNCTION + + * imTgaWriteGREY8 - write a Targa file that is greyscale with 8bits + + * + + * DESCRIPTION + + * That VFB is queried, and the Targa file header written out. + + * The VFB data is then copied to the file. + + */ + + + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imTgaWriteGREY8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable) + +#else + +imTgaWriteGREY8( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + int stype; /* Storage type */ + + int spix; /* Number of bits per image pixel */ + + int cmsize; /* Number of bits per color map pixel */ + + + + BinByteOrder (BINLBF); + + + + /* Set up and write out the header */ + + stype = IMTGA_UNCOMPBW; + + spix = 8; + + cmsize = 0; + + /* write the header out */ + + imTgaWriteHeader ( ioType, fd, fp,flagsTable, tagTable,stype,spix,cmsize); + + imTgaWrite8image ( ioType, fd, fp,flagsTable, tagTable); + + return (1); + +} + +/* + + * FUNCTION + + * imTgaWriteGREYRLE8 - write a Targa file that is greyscale + + * run-length-encoded with 2 bytes per pixel. + + * + + * DESCRIPTION + + * That VFB is queried, and the Targa file header written out. + + * The VFB data is then copied to the file. + + */ + + + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imTgaWriteGREYRLE8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable) + +#else + +imTgaWriteGREYRLE8( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + int stype; /* Storage type */ + + int spix; /* Number of bits per image pixel */ + + int cmsize; /* Number of bits per color map pixel */ + + + + + + BinByteOrder (BINLBF); + + + + stype = IMTGA_RLEBW; + + spix = 8; + + cmsize = 0; + + /* write the header out */ + + imTgaWriteHeader ( ioType, fd, fp,flagsTable, tagTable,stype,spix,cmsize); + + + + imTgaWriteRLE8 (ioType, fd, fp, flagsTable,tagTable); + + return (1); + +} + +/* + + * FUNCTION + + * imTgaWriteGREY16 - write a Targa file that is greyscale with 16bits. + + * + + * DESCRIPTION + + * That VFB is queried, and the Targa file header written out. + + * The VFB data is then copied to the file. + + */ + + + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imTgaWriteGREY16( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable) + +#else + +imTgaWriteGREY16( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + int stype; /* Storage type */ + + int spix; /* Number of bits per image pixel */ + + int cmsize; /* Number of bits per color map pixel */ + + + + BinByteOrder (BINLBF); + + + + /* Set up and write out the header */ + + stype = IMTGA_UNCOMPBW; + + spix = 16; + + cmsize = 0; + + + + /* write the header out */ + + imTgaWriteHeader ( ioType, fd, fp,flagsTable, tagTable,stype,spix,cmsize); + + imTgaWrite16image (ioType, fd, fp, flagsTable,tagTable); + + return (1); + +} + + + + + + + +/* + + * FUNCTION + + * imTgaWriteGREYRLE16 - write a Targa file run-length-encoded + + * with 16 bits per pixel. + + * + + * DESCRIPTION + + * That VFB is queried, and the Targa file header written out. + + * The VFB data is then copied to the file. + + */ + + + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imTgaWriteGREYRLE16( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable) + +#else + +imTgaWriteGREYRLE16( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to read from */ + +#endif + +{ + + int stype; /* Storage type */ + + int spix; /* Number of bits per image pixel */ + + int cmsize; /* Number of bits per color map pixel */ + + + + + + BinByteOrder (BINLBF); + + + + /* Set up and write out the header */ + + stype = IMTGA_RLEBW; + + spix = 16; + + cmsize = 0; + + + + /* write the header out */ + + imTgaWriteHeader ( ioType, fd, fp,flagsTable, tagTable,stype,spix,cmsize); + + imTgaWriteRLE16 (ioType, fd, fp, flagsTable,tagTable); + + return (1); + +} + + + + + +/* + + * FUNCTION + + * imTgaWriteHeader - write a Targa file header + + * + + * DESCRIPTION + + * Based on info passed to it a standard Targa file header written out. + + * If there is a color lookup table then it too is written out. + + * If there is a name field it too is written out. + + */ + +static int /* Returns # of tags used */ + +#ifdef __STDC__ + +imTgaWriteHeader( int ioType, int fd, FILE *fp,TagTable *flagsTable, TagTable *tagTable,int stype,int spix,int cmsize ) + +#else + +imTgaWriteHeader( ioType, fd, fp,flagsTable, tagTable,stype,spix,cmsize ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; + + TagTable *tagTable; + + int stype; /* what data storage type it is */ + + int spix; /* The number of bits per image pixel*/ + + int cmsize; /* The number of bits per clt pixel*/ + +#endif + +{ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pptr; /* Pixel pointer */ + + ImClt *clt; /* CLT pointer */ + + ImCltPtr pColor; + + imTgaHeaderInfo header; /* Tga file header */ + + unsigned char *buffer; /* line buffer */ + + unsigned char *pBuffer; /* current location in buffer */ + + int n,x,y,i,j; /* Counter */ + + TagEntry *tagEntry; /* Tmp table entry */ + + char *tmpString; /* Tmp string holder */ + + int tga_attbits; /* number of attribute bits per pix. */ + + int tga_rsrvd; /* Reserved bit */ + + int tga_orgbita; /* Screen origin bit 4 */ + + int tga_orgbitb; /* Screen origin bit 5 */ + + char message[200]; /* Used for ImInfo string */ + + + + + + /* Set up and write out the header */ + + BinByteOrder (BINLBF); + + + + TagEntryQValue (TagTableQDirect (tagTable, "image vfb", 0), &vfb); + + + + clt = ImVfbQClt(vfb); + + + + /* Get the name field if there is one */ + + tagEntry = TagTableQDirect (tagTable, "image name",0); + + if (tagEntry == TAGENTRYNULL) header.tga_numc_ident = 0; + + else + + { + + TagEntryQValue(tagEntry, &tmpString); + + header.tga_numc_ident=strlen(tmpString); + + if (header.tga_numc_ident>255) header.tga_numc_ident=255; + + ImInfo( "Image Name", tmpString ); + + } + + + + if (ImVfbQClt(vfb)==IMCLTNULL) + + header.tga_cmtype = 0; + + else if (cmsize == 0) + + header.tga_cmtype = 0; + + else + + header.tga_cmtype = 1; + + + + header.tga_stype = stype; + + header.tga_cmorigin = 0; + + if (header.tga_cmtype) + + header.tga_cmlen = ImCltQNColors(clt); + + else header.tga_cmlen = 0; + + header.tga_cmsize = cmsize; + + header.tga_imxorg = 0; + + header.tga_imyorg = 0; + + header.tga_imwid = ImVfbQWidth(vfb); + + header.tga_imhgt = ImVfbQHeight( vfb); + + header.tga_spix = spix; + + tga_attbits = 0; + + tga_orgbita = 0 << 4; + + tga_orgbitb = 1 << 5; + + tga_rsrvd = 0 << 6; + + header.tga_imdesc=tga_attbits|tga_orgbita|tga_orgbitb|tga_rsrvd; + + + + /* information to printout if verbose flag has been set */ + + ImInfo ("Byte Order", "Least Significant Byte First"); + + + + sprintf (message, "%d x %d", ImVfbQWidth(vfb), ImVfbQHeight(vfb)); + + ImInfo ("Resolution", message); + + + + if ( (stype==IMTGA_UNCOMPCOLORMAP) || (stype==IMTGA_RLECOLORMAP) ) + + sprintf (message, "%d-bit Color Indexed", spix); + + + + if ( (stype==IMTGA_UNCOMPRGB) || (stype==IMTGA_RLERGB) ) + + sprintf (message, "%d-bit RGB", spix); + + + + if ( (stype==IMTGA_UNCOMPBW) || (stype==IMTGA_RLEBW) ) + + sprintf (message, "%d-bit Greyscale", spix); + + ImInfo( "Type", message ); + + + + if (cmsize>0) + + { + + sprintf (message, "%d Entries",cmsize); + + ImInfo( "Color Table", message ); + + } + + + + /* information to printout if verbose flag has been set */ + + if ((stype>8) && (stype<12)) + + sprintf (message, "Run Length Encoded (RLE)"); + + else + + sprintf (message, "none"); + + ImInfo( "Compression Type", message ); + + + + if (((spix==16) || (spix==32) ) && ( (stype==IMTGA_UNCOMPRGB) || (stype==IMTGA_RLERGB))) + + sprintf (message, "%d-bit", spix); + + else + + sprintf (message, "none"); + + ImInfo( "Alpha Channel", message ); + + + + /* Now we read the image pixel data in from our data file */ + + /* write the header out */ + + if (ImBinWriteStruct (ioType, fd, fp, &header, imTgaHeaderFields) ==-1) + + ImReturnBinError( ); + + + + if (header.tga_numc_ident) /* if there is a name to write */ + + + + ImBinWrite (ioType, fd, fp, tmpString, UCHAR,1,header.tga_numc_ident); + + + + + + /* write out a clt if there is one */ + + if (header.tga_cmlen) + + { + + ImMalloc (buffer, unsigned char *, 3*header.tga_cmlen) + + pColor = ImCltQFirst(clt); + + pBuffer = buffer; + + + + for (j=0; j + +#include + +//#include + +#include + +#include + +#include "iminternal.h" + + + +#ifdef USE_TIFF_LIB + +#include "tiff.h" + +#include "tiffio.h" + +#endif + + + + + + + +/* + + ** + + ** FORMAT + + ** TIFF - Aldus Tagged Image File Format + + ** + + ** AKA + + ** tif + + ** + + ** FORMAT REFERENCES + + ** - Bit-Mapped Graphics, Steve Rimmer + + ** - Supercharged Bitmapped Graphics, Steve Rimmer + + ** - Graphics File Formats, David C. Kay, John R. Levine + + ** - TIFF Revision 6.0, Aldus + + ** + + ** + + ** CODE CREDITS + + ** Custom development, Todd Elvins, San Diego Supercomputer Center, 1992. + + ** + + ** DESCRIPTION + + ** TIFF files are composed of a small file header, one or more + + ** image directories containing tagged information about an image + + ** in the file, and one or more images. Images of any depth + + ** can be stored in a Tiff file. We only deal with + + ** reading 1, 4, 8, and 24 bit images, and writing 8 and 24 bit + + ** images. The pixel data in Tiff files can be encoded in a number + + ** of different ways. Currently we are only handling unencoded + + ** pixel data, and Lempil-Ziv Welsh encoding. In the future + + ** we might want to add Thunderscan, Macintosh PackBits, or Pixar picio. + + ** + + ** + + ** The sections of a tiff file are as follows : + + ** + + ** - header 8 bytes Header info. + + ** - image file directory variable points to image data + + ** (IFD) There may be + + ** more than one IFD. + + ** - actual data + + ** The sections are not necessarily in that order; the header point + + ** to the first IFD, which points to subsequent IFDs and image data. + + ** + + ** Header + + ** ------ + + ** + + ** Bytes Description + + ** 0-1 Byte Order (MSB or LSB) + + ** 2-3 Magic number (stored according to byte order) + + ** 4-7 Location of the first IFD in the file + + ** + + ** Image File Directories + + ** ---------------------- + + ** An IFD consists of the following information: + + ** - the number of entries in the directory (2 bytes) + + ** - a series of entries (12 bytes each) + + ** - the location of the next IFD or 0 for the (4 bytes) + + ** last entry + + ** Each entry in the IFD contains: + + ** - a tag identifying the field (bytes 0-1) + + ** - the field type (bytes 2-3) + + ** - the size of the data: 'count' + + ** (the actual size of the data is + + ** sizeof(field_type) * count bytes) (bytes 4-7) + + ** - the location of the data in the file (must + + ** begin on a word boundary, so this is an even + + ** number) (bytes 8-11) + + ** + + **/ + + + +/** + + ** The TIFF library + + ** ---------------- + + ** + + ** This library claims to support many many many different + + ** possible image types. The specifications for TIFF allow for + + ** any number of channels in an image, and any depth for each channel. + + ** BUT, most software packages that use the tiff library do not + + ** support such a variety of formats. + + ** + + ** Here is what the routines that follow support : + + ** + + ** Read support: + + ** + + ** 1. RGB Images with + + ** 1,2,4,8 or 16 bits per channel + + ** alpha or no alpha channel + + ** non-interleaved + + ** grouped by scanlines + + ** + + ** 2. RGB Images with + + ** 8 bits per channel + + ** alpha or no alpha channel + + ** plane-interleaved + + ** grouped by scanlines or tiles + + ** + + ** 3. Color Indexed or Greyscale Images with + + ** 1,2,4,8, or 16 bits per channel + + ** plane-interleaved or non-interleaved + + ** (those are equivalent since there's one plane) + + ** grouped by scanlines or tiles + + ** + + ** Compression is handled by the tiff library. So, whatever + + ** compression schemes the tiff library handles are supported + + ** by the image tools. + + ** + + ** Write support: + + ** + + ** 1. RGB Images with + + ** 8 bits per channel + + ** alpha or no alpha channel + + ** non-interleaved or plane-interleaved + + ** grouped by scanlines or tiles + + ** + + ** 2. Color Indexed or Greyscale images with + + ** 1,2,4,8,16 bits per channel + + ** plane or non-interleaved (equivalent) + + ** grouped by scanlines + + ** + + ** 3. Color Indexed or Greyscale images with + + ** 8 bits per channel + + ** plane or non-interleaved (equivalent) + + ** grouped by tiles + + ** + + ** The compression schemes supported are: + + ** + + ** Macintosh PackBits encoding, + + ** Lempel-Ziv & Welch encoding, + + ** JPEG encoding (Discrete Cosine Transform), + + ** none + + ** + + ** + + ** The reason for not having write capability for rgb images + + ** with other than 8 bits per channel is because such files + + ** make Amazon and xv dump core. The capability IS in place + + ** to write such images, and this can be accomplished simply + + ** by adding these specifications to the write map below. + + ** + + ** i.e. adding + + ** + + ** { IMVFBRGB, 0, RGB,3,1, 0, imTiffWrite }, + + ** { IMVFBRGB, 0, RGB,3,2, 0, imTiffWrite }, + + ** { IMVFBRGB, 0, RGB,3,4, 0, imTiffWrite }, + + ** + + ** would allow for writing RGB images with no compression and + + ** 1,2, or 4 bits per channel. + + ** + + **/ + + + +/* + + * TIFF - Tagged Image File Format + + * For information on these structures, how to use them, etc. please + + * see imfmt.c. + + */ + +#ifdef __STDC__ + +static int imTiffRead( int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable ); + +static int imTiffWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +#else + +static int imTiffRead( ); + +static int imTiffWrite( ); + +#endif + +static char *imTiffNames[ ] = { "tiff", "tif", NULL }; + +static unsigned char imTiffMagicNumberA[ ] = {0x4d, 0x4d }; + +static unsigned char imTiffMagicNumberB[ ] = {0x49, 0x49 }; + +static ImFileFormatReadMap imTiffReadMap[ ] = + +{ + + /* in out */ + + /* type,ch,dep, attr. VFB type attr. */ + +#ifdef USE_TIFF_LIB + + { IN,1,1, 0, IMVFBMONO, 0 }, + + { IN,1,4, C, IMVFBINDEX8, C }, + + { IN,1,4, 0, IMVFBINDEX8, 0 }, + + { IN,1,4, LZW|C, IMVFBINDEX8, C }, + + { IN,1,4, LZW, IMVFBINDEX8, 0 }, + + { IN,1,4, PB|C, IMVFBINDEX8, C }, + + { IN,1,4, PB, IMVFBINDEX8, 0 }, + + { IN,1,8, 0, IMVFBINDEX8, 0 }, + + { IN,1,8, 0, IMVFBINDEX8, 0 }, + + { IN,1,8, C, IMVFBINDEX8, C }, + + { IN,1,32, 0, IMVFBINDEX16, 0 }, + + { IN,1,32, C, IMVFBINDEX16, C }, + + { RGB,3,8, 0, IMVFBRGB, 0 }, + + { RGB,3,8, A, IMVFBRGB, A }, + + + + { IN,1,1, PB, IMVFBMONO, 0 }, + + { IN,1,4, PB, IMVFBINDEX8, 0 }, + + { IN,1,8, PB, IMVFBINDEX8, 0 }, + + { IN,1,8, PB, IMVFBINDEX8, 0 }, + + { IN,1,8, PB|C, IMVFBINDEX8, C }, + + { IN,1,32, PB, IMVFBINDEX16, 0 }, + + { IN,1,32, PB|C, IMVFBINDEX16, C }, + + { RGB,3,8, PB, IMVFBRGB, 0 }, + + { RGB,3,8, PB|A, IMVFBRGB, A }, + + + + { IN,1,1, LZW, IMVFBMONO, 0 }, + + { IN,1,4, LZW, IMVFBINDEX8, 0 }, + + { IN,1,8, LZW, IMVFBINDEX8, 0 }, + + { IN,1,8, LZW, IMVFBINDEX8, 0 }, + + { IN,1,8, LZW|C, IMVFBINDEX8, C }, + + { IN,1,32, LZW, IMVFBINDEX16, 0 }, + + { IN,1,32, LZW|C, IMVFBINDEX16, C }, + + { RGB,3,8, LZW, IMVFBRGB, 0 }, + + { RGB,3,8, LZW|A, IMVFBRGB, A }, + +#else + + { IN,1,1, 0, IMVFBMONO, 0 }, + + { IN,1,4, 0, IMVFBINDEX8, 0 }, + + { IN,1,8, 0, IMVFBINDEX8, 0 }, + + { IN,1,8, 0, IMVFBINDEX8, 0 }, + + { IN,1,8, C, IMVFBINDEX8, C }, + + { IN,1,32, 0, IMVFBINDEX16, 0 }, + + { IN,1,32, C, IMVFBINDEX16, C }, + + { RGB,3,8, 0, IMVFBRGB, 0 }, + + { RGB,3,8, A, IMVFBRGB, A }, + + + + { IN,1,1, PB, IMVFBMONO, 0 }, + + { IN,1,4, PB, IMVFBINDEX8, 0 }, + + { IN,1,8, PB, IMVFBINDEX8, 0 }, + + { IN,1,8, PB, IMVFBINDEX8, 0 }, + + { IN,1,8, PB|C, IMVFBINDEX8, C }, + + { IN,1,32, PB, IMVFBINDEX16, 0 }, + + { IN,1,32, PB|C, IMVFBINDEX16, C }, + + { RGB,3,8, PB, IMVFBRGB, 0 }, + + { RGB,3,8, PB|A, IMVFBRGB, A }, + + + + { IN,1,1, LZW, IMVFBMONO, 0 }, + + { IN,1,4, LZW, IMVFBINDEX8, 0 }, + + { IN,1,8, LZW, IMVFBINDEX8, 0 }, + + { IN,1,8, LZW, IMVFBINDEX8, 0 }, + + { IN,1,8, LZW|C, IMVFBINDEX8, C }, + + { IN,1,32, LZW, IMVFBINDEX16, 0 }, + + { IN,1,32, LZW|C, IMVFBINDEX16, C }, + + { RGB,3,8, LZW, IMVFBRGB, 0 }, + + { RGB,3,8, LZW|A, IMVFBRGB, A }, + +#endif + + { -1, 0, -1, 0 }, + +}; + +static ImFileFormatWriteMap imTiffWriteMap[ ] = + +{ + + /* in out */ + + /* VFB type, attr., type,ch,dep, attr., func */ + + + +#ifdef USE_TIFF_LIB + + + + { IMVFBMONO, 0, IN,1,1, LZW, imTiffWrite }, + + { IMVFBMONO, 0, IN,1,1, PB, imTiffWrite }, + + { IMVFBMONO, 0, IN,1,1, 0, imTiffWrite }, + + + + { IMVFBMONO, C, IN,1,1, C|LZW, imTiffWrite }, + + { IMVFBMONO, C, IN,1,1, C|PB, imTiffWrite }, + + { IMVFBMONO, C, IN,1,1, C, imTiffWrite }, + + + + { IMVFBMONO, 0, IN,1,1, PI|LZW, imTiffWrite }, + + { IMVFBMONO, 0, IN,1,1, PI|PB, imTiffWrite }, + + { IMVFBMONO, 0, IN,1,1, PI, imTiffWrite }, + + + + { IMVFBMONO, C, IN,1,1, PI|C|LZW,imTiffWrite }, + + { IMVFBMONO, C, IN,1,1, PI|C|PB, imTiffWrite }, + + { IMVFBMONO, C, IN,1,1, PI|C, imTiffWrite }, + + + + { IMVFBINDEX8, 0, IN,1,8, LZW, imTiffWrite }, + + { IMVFBINDEX8, 0, IN,1,8, PB, imTiffWrite }, + + { IMVFBINDEX8, 0, IN,1,8, 0, imTiffWrite }, + + + + { IMVFBINDEX8, C, IN,1,8, C|LZW, imTiffWrite }, + + { IMVFBINDEX8, C, IN,1,8, C|PB, imTiffWrite }, + + { IMVFBINDEX8, C, IN,1,8, C, imTiffWrite }, + + + + { IMVFBINDEX8, 0, IN,1,8, PI|LZW, imTiffWrite }, + + { IMVFBINDEX8, 0, IN,1,8, PI|PB, imTiffWrite }, + + { IMVFBINDEX8, 0, IN,1,8, PI, imTiffWrite }, + + + + { IMVFBINDEX8, C, IN,1,8, PI|C|LZW,imTiffWrite }, + + { IMVFBINDEX8, C, IN,1,8, PI|C|PB, imTiffWrite }, + + { IMVFBINDEX8, C, IN,1,8, PI|C, imTiffWrite }, + + + + { IMVFBINDEX8, 0, IN,1,4, LZW, imTiffWrite }, + + { IMVFBINDEX8, 0, IN,1,4, PB, imTiffWrite }, + + { IMVFBINDEX8, 0, IN,1,4, 0, imTiffWrite }, + + + + { IMVFBINDEX8, C, IN,1,4, C|LZW, imTiffWrite }, + + { IMVFBINDEX8, C, IN,1,4, C|PB, imTiffWrite }, + + { IMVFBINDEX8, C, IN,1,4, C, imTiffWrite }, + + + + { IMVFBINDEX8, 0, IN,1,4, PI|LZW, imTiffWrite }, + + { IMVFBINDEX8, 0, IN,1,4, PI|PB, imTiffWrite }, + + { IMVFBINDEX8, 0, IN,1,4, PI, imTiffWrite }, + + + + { IMVFBINDEX8, C, IN,1,4, PI|C|LZW,imTiffWrite }, + + { IMVFBINDEX8, C, IN,1,4, PI|C|PB, imTiffWrite }, + + { IMVFBINDEX8, C, IN,1,4, PI|C, imTiffWrite }, + + + + { IMVFBINDEX8, 0, IN,1,2, LZW, imTiffWrite }, + + { IMVFBINDEX8, 0, IN,1,2, PB, imTiffWrite }, + + { IMVFBINDEX8, 0, IN,1,2, 0, imTiffWrite }, + + + + { IMVFBINDEX8, C, IN,1,2, C|LZW, imTiffWrite }, + + { IMVFBINDEX8, C, IN,1,2, C|PB, imTiffWrite }, + + { IMVFBINDEX8, C, IN,1,2, C, imTiffWrite }, + + + + { IMVFBINDEX8, 0, IN,1,2, PI|LZW, imTiffWrite }, + + { IMVFBINDEX8, 0, IN,1,2, PI|PB, imTiffWrite }, + + { IMVFBINDEX8, 0, IN,1,2, PI, imTiffWrite }, + + + + { IMVFBINDEX8, C, IN,1,2, PI|C|LZW,imTiffWrite }, + + { IMVFBINDEX8, C, IN,1,2, PI|C|PB, imTiffWrite }, + + { IMVFBINDEX8, C, IN,1,2, PI|C, imTiffWrite }, + + + +#if 0 /* Nobody seems capable of reading index16 tiffs. */ + + { IMVFBINDEX16, 0, IN,1,16, LZW, imTiffWrite }, + + { IMVFBINDEX16, 0, IN,1,16, PB, imTiffWrite }, + + { IMVFBINDEX16, 0, IN,1,16, 0, imTiffWrite }, + + + + { IMVFBINDEX16, C, IN,1,16, C|LZW, imTiffWrite }, + + { IMVFBINDEX16, C, IN,1,16, C|PB, imTiffWrite }, + + { IMVFBINDEX16, C, IN,1,16, C, imTiffWrite }, + + + + { IMVFBINDEX16, 0, IN,1,16, PI|LZW, imTiffWrite }, + + { IMVFBINDEX16, 0, IN,1,16, PI|PB, imTiffWrite }, + + { IMVFBINDEX16, 0, IN,1,16, PI, imTiffWrite }, + + + + { IMVFBINDEX16, C, IN,1,16, PI|C|LZW,imTiffWrite }, + + { IMVFBINDEX16, C, IN,1,16, PI|C|PB, imTiffWrite }, + + { IMVFBINDEX16, C, IN,1,16, PI|C, imTiffWrite }, + +#endif + + + + + + { IMVFBRGB, 0, RGB,3,8, LZW, imTiffWrite }, + + { IMVFBRGB, 0, RGB,3,8, PB, imTiffWrite }, + + { IMVFBRGB, 0, RGB,3,8, 0, imTiffWrite }, + + + + { IMVFBRGB, A, RGB,3,8, A|LZW, imTiffWrite }, + + { IMVFBRGB, A, RGB,3,8, A|PB, imTiffWrite }, + + { IMVFBRGB, A, RGB,3,8, A, imTiffWrite }, + + + + { IMVFBRGB, 0, RGB,3,8, PI|LZW, imTiffWrite }, + + { IMVFBRGB, 0, RGB,3,8, PI|PB, imTiffWrite }, + + { IMVFBRGB, 0, RGB,3,8, PI, imTiffWrite }, + + + + { IMVFBRGB, A, RGB,3,8, PI|A|LZW,imTiffWrite }, + + { IMVFBRGB, A, RGB,3,8, PI|A|PB, imTiffWrite }, + + { IMVFBRGB, A, RGB,3,8, PI|A, imTiffWrite }, + + + +/* tiles */ + + + + { IMVFBINDEX8, 0, IN,1,8, T|LZW, imTiffWrite }, + + { IMVFBINDEX8, 0, IN,1,8, T|PB, imTiffWrite }, + + { IMVFBINDEX8, 0, IN,1,8, T|0, imTiffWrite }, + + { IMVFBINDEX8, 0, IN,1,8, T|DCT, imTiffWrite }, + + + + { IMVFBINDEX8, C, IN,1,8, T|C|LZW, imTiffWrite }, + + { IMVFBINDEX8, C, IN,1,8, T|C|PB, imTiffWrite }, + + { IMVFBINDEX8, C, IN,1,8, T|C, imTiffWrite }, + + { IMVFBINDEX8, C, IN,1,8, T|C|DCT, imTiffWrite }, + + + + { IMVFBINDEX8, 0, IN,1,8, T|PI|LZW, imTiffWrite }, + + { IMVFBINDEX8, 0, IN,1,8, T|PI|PB, imTiffWrite }, + + { IMVFBINDEX8, 0, IN,1,8, T|PI, imTiffWrite }, + + { IMVFBINDEX8, 0, IN,1,8, T|PI|DCT, imTiffWrite }, + + + + { IMVFBINDEX8, C, IN,1,8, T|PI|C|LZW,imTiffWrite }, + + { IMVFBINDEX8, C, IN,1,8, T|PI|C|PB, imTiffWrite }, + + { IMVFBINDEX8, C, IN,1,8, T|PI|C, imTiffWrite }, + + { IMVFBINDEX8, C, IN,1,8, T|PI|C|DCT,imTiffWrite }, + + + + + + { IMVFBRGB, 0, RGB,3,8, T|LZW, imTiffWrite }, + + { IMVFBRGB, 0, RGB,3,8, T|PB, imTiffWrite }, + + { IMVFBRGB, 0, RGB,3,8, T, imTiffWrite }, + + { IMVFBRGB, 0, RGB,3,8, T|DCT, imTiffWrite }, + + + + { IMVFBRGB, A, RGB,3,8, T|A|LZW, imTiffWrite }, + + { IMVFBRGB, A, RGB,3,8, T|A|PB, imTiffWrite }, + + { IMVFBRGB, A, RGB,3,8, T|A, imTiffWrite }, + + { IMVFBRGB, A, RGB,3,8, T|A|DCT, imTiffWrite }, + + + + { IMVFBRGB, 0, RGB,3,8, T|PI|LZW, imTiffWrite }, + + { IMVFBRGB, 0, RGB,3,8, T|PI|PB, imTiffWrite }, + + { IMVFBRGB, 0, RGB,3,8, T|PI, imTiffWrite }, + + { IMVFBRGB, 0, RGB,3,8, T|PI|DCT, imTiffWrite }, + + + + { IMVFBRGB, A, RGB,3,8, T|PI|A|LZW,imTiffWrite }, + + { IMVFBRGB, A, RGB,3,8, T|PI|A|PB, imTiffWrite }, + + { IMVFBRGB, A, RGB,3,8, T|PI|A, imTiffWrite }, + + { IMVFBRGB, A, RGB,3,8, T|PI|A|DCT,imTiffWrite }, + +#else + + + + { IMVFBMONO, 0, IN,1,1, LZW, imTiffWrite }, + + { IMVFBMONO, 0, IN,1,1, PB, imTiffWrite }, + + { IMVFBMONO, 0, IN,1,1, 0, imTiffWrite }, + + + + { IMVFBINDEX8, 0, IN,1,8, LZW, imTiffWrite }, + + { IMVFBINDEX8, 0, IN,1,8, PB, imTiffWrite }, + + { IMVFBINDEX8, 0, IN,1,8, 0, imTiffWrite }, + + + + { IMVFBINDEX8, C, IN,1,8, LZW|C, imTiffWrite }, + + { IMVFBINDEX8, C, IN,1,8, PB|C, imTiffWrite }, + + { IMVFBINDEX8, C, IN,1,8, C, imTiffWrite }, + + + + { IMVFBRGB, A, RGB,3,8, LZW|A, imTiffWrite }, + + { IMVFBRGB, A, RGB,3,8, PB|A, imTiffWrite }, + + { IMVFBRGB, A, RGB,3,8, A, imTiffWrite }, + + + + { IMVFBRGB, 0, RGB,3,8, LZW, imTiffWrite }, + + { IMVFBRGB, 0, RGB,3,8, PB, imTiffWrite }, + + { IMVFBRGB, 0, RGB,3,8, 0, imTiffWrite }, + +#endif + + + + { -1, 0, -1, 0, NULL }, + +}; + + + +static ImFileMagic imFileTiffMagic []= + +{ + + { 0, 2, imTiffMagicNumberA}, + + { 0, 2, imTiffMagicNumberB}, + + { 0, 0, NULL}, + +}; + + + +ImFileFormat ImFileTiffFormat = + +{ + + imTiffNames, "Tagged image file", + + "Aldus, MicroSoft, and NeXT", + + "1-, 4-, 8-, and 32-bit color index images. 24-bit RGB and 32-bit\n\ +RGB+alpha images. Standard (uncompressed), Mac Packbits, and Lempel-\n\ +Ziv & Welsh compression.", + "1-, 8-, and 32-bit color index images. 24-bit RGB and 32-bit\n\ +RGB+alpha images. Standard (uncompressed), Mac Packbits, and Lempel-\n\ +Ziv & Welsh compression.", + + imFileTiffMagic, + +#ifdef USE_TIFF_LIB + + IMMULTI, IMNOPIPE, /* Read */ + + IMMULTI, IMNOPIPE, /* Write */ + +#else + + IMNOMULTI, IMNOPIPE, + + IMNOMULTI, IMNOPIPE, + +#endif + + imTiffRead, imTiffReadMap, imTiffWriteMap + +}; + + + +#ifdef USE_TIFF_LIB + + + +/* + + * MACRO + + * IM_QCHANNEL( value, channel) + + * + + * DESCRIPTION + + * Set a value to be this attribute of this pixel + + * + + */ + + + +#define IM_QCHANNEL(value, channel) \ +switch (channel) \ +{ \ + case IMVFBGREY : /* same as INDEX8 */ \ + value = (uint16) ImVfbQGrey (vfb, vfbptr); \ + break; \ + case IMVFBRED : \ + value = (uint16) ImVfbQRed (vfb, vfbptr); \ + break; \ + case IMVFBGREEN : \ + value = (uint16) ImVfbQGreen(vfb, vfbptr); \ + break; \ + case IMVFBBLUE : \ + value = (uint16) ImVfbQBlue (vfb, vfbptr); \ + break; \ + case IMVFBALPHA : \ + value = (uint16) ImVfbQAlpha (vfb, vfbptr); \ + break; \ + case IMVFBINDEX16 : \ + value = (uint16) ImVfbQIndex16 (vfb, vfbptr); \ + break; \ +} + + + +#define IMTIFFNULL NULL + +#define IM_MIN(x,y) ( (x) < (y) ? (x) : (y) ) + + + +#ifdef __STDC__ + + + +static int imTiffReadNonInterleavedScanlines( TIFF* tiff, ImVfb* vfb); + +static int imTiffReadPlaneInterleavedScanlines( TIFF *tif, ImVfb* vfb); + +static int imTiffReadNonInterleavedTiles (TIFF *tif, ImVfb *vfb); + +static int imTiffReadPlaneInterleavedTiles ( TIFF *tif, ImVfb *vfb); + +static int imTiffReadClt(TIFF* tif, ImClt **cltBuf); + + + +static int imTiffReadRedScanlinePlane(TIFF* tif, ImVfb* vfb); + +static int imTiffReadGreenScanlinePlane(TIFF* tif, ImVfb* vfb); + +static int imTiffReadBlueScanlinePlane(TIFF* tif, ImVfb* vfb); + +static int imTiffReadAlphaScanlinePlane(TIFF* tif, ImVfb* vfb); + + + +static int imTiffReadRedTiledPlane( TIFF* tif, ImVfb* vfb, uint32 tileWidth, + + uint32 tileLength, uint32 imageWidth, uint32 imageLength, tsize_t tileSize); + +static int imTiffReadGreenTiledPlane( TIFF* tif, ImVfb* vfb, uint32 tileWidth, + + uint32 tileLength, uint32 imageWidth, uint32 imageLength, tsize_t tileSize); + +static int imTiffReadBlueTiledPlane( TIFF* tif, ImVfb* vfb, uint32 tileWidth, + + uint32 tileLength, uint32 imageWidth, uint32 imageLength, tsize_t tileSize); + +static int imTiffReadAlphaTiledPlane( TIFF* tif, ImVfb* vfb, uint32 tileWidth, + + uint32 tileLength, uint32 imageWidth, uint32 imageLength, tsize_t tileSize); + + + +static int imTiffWriteNonInterleavedScanlines( TIFF* tiff, ImVfb* vfb, ImFileFormatWriteMap* pMap); + +static int imTiffWritePlaneInterleavedScanlines( TIFF *tif, ImVfb* vfb, ImFileFormatWriteMap* pMap); + +static int imTiffWriteNonInterleavedTiles (TIFF *tif, ImVfb *vfb, ImFileFormatWriteMap* pMap ); + +static int imTiffWritePlaneInterleavedTiles ( TIFF *tif, ImVfb *vfb, ImFileFormatWriteMap* pMap); + + + +static int imTiffWriteClt(TIFF* tif, ImClt *cltBuf, long numEntries); + +static int imTiffWriteRedScanlinePlane(ImVfb *vfb, TIFF *tif, uint16 chandepth); + +static int imTiffWriteGreenScanlinePlane(ImVfb *vfb, TIFF *tif, uint16 chandepth); + +static int imTiffWriteBlueScanlinePlane(ImVfb *vfb, TIFF *tif, uint16 chandepth); + +static int imTiffWriteAlphaScanlinePlane(ImVfb *vfb, TIFF *tif, uint16 chandepth); + +static int imTiffWriteIndex8ScanlinePlane(ImVfb *vfb, TIFF *tif, uint16 chandepth); + +static int imTiffWriteIndex16ScanlinePlane(ImVfb *vfb, TIFF *tif, uint16 chandepth); + + + +static int imTiffWriteRedTiledPlane(ImVfb* vfb, TIFF *tif, uint16 chandepth, uint32 tileWidth, uint32 tileHeight); + +static int imTiffWriteGreenTiledPlane( ImVfb* vfb, TIFF* tif, uint16 chanDepth, uint32 tileWidth, uint32 tileHeight); + +static int imTiffWriteBlueTiledPlane( ImVfb* vfb, TIFF* tif, uint16 chanDepth, uint32 tileWidth, uint32 tileHeight); + +static int imTiffWriteAlphaTiledPlane( ImVfb* vfb, TIFF* tif, uint16 chanDepth, uint32 tileWidth, uint32 tileHeight); + +static int imTiffWriteIndex8TiledPlane( ImVfb* vfb, TIFF* tif, uint16 chanDepth, uint32 tileWidth, uint32 tileHeight); + + + +static void imTiffSwapBytes(unsigned char* linebuf, tsize_t linesize); + +#else /* __STDC__ is not defined */ + + + +static int imTiffReadNonInterleavedScanlines( ); + +static int imTiffReadPlaneInterleavedScanlines(); + +static int imTiffReadNonInterleavedTiles ( ); + +static int imTiffReadPlaneInterleavedTiles ( ); + +static int imTiffReadClt(); + + + +static int imTiffReadRedScanlinePlane(); + +static int imTiffReadGreenScanlinePlane(); + +static int imTiffReadBlueScanlinePlane(); + +static int imTiffReadAlphaScanlinePlane(); + + + +static int imTiffReadRedTiledPlane( ); + +static int imTiffReadGreenTiledPlane( ); + +static int imTiffReadBlueTiledPlane( ); + +static int imTiffReadAlphaTiledPlane( ); + + + +static int imTiffWriteNonInterleavedScanlines(); + +static int imTiffWritePlaneInterleavedScanlines( ); + +static int imTiffWriteNonInterleavedTiles (); + +static int imTiffWritePlaneInterleavedTiles ( ); + +static int imTiffWriteClt(); + +static int imTiffWriteRedScanlinePlane(); + +static int imTiffWriteGreenScanlinePlane(); + +static int imTiffWriteBlueScanlinePlane(); + +static int imTiffWriteAlphaScanlinePlane(); + +static int imTiffWriteIndex8ScanlinePlane(); + +static int imTiffWriteIndex16ScanlinePlane(); + + + +static int imTiffWriteRedTiledPlane(); + +static int imTiffWriteGreenTiledPlane(); + +static int imTiffWriteBluedTiledPlane(); + +static int imTiffWriteAlphadTiledPlane(); + +static int imTiffWriteIndex8TiledPlane(); + + + +static void imTiffSwapBytes(); + +#endif /* __STDC__ */ + + + +#endif /* USE_TIFF_LIB */ + + + +/* + + * + + * Code to read tiff files without using the tiff library starts here. + + * + + */ + + + +#ifndef USE_TIFF_LIB + + + +/* + + * Prototypes + + * These functions are in imlzw.c or macpack.c + + */ + + + +#ifdef __STDC__ + +int ImLzwPreDecode( unsigned char *buf, int size ); + +int ImLzwDecode(unsigned char *buf, char *op, int occ); + +int ImLzwPostDecode(); + +int ImLzwPreEncode(int size); + +int ImLzwEncode(int ioType, int fd, FILE *fp, unsigned char *bp, int cc); + +int ImLzwPostEncode( int ioType, int fd, FILE *fp ); + +void ImLzwCleanup(); + +void UnpackBits( unsigned char *srcPtr, unsigned char *dstPtr, unsigned int *cnt ); + +void PackBits3( unsigned char *cIn, unsigned char *cOut, unsigned int * width ); + +void PackBits( unsigned char *cIn, unsigned char *cOut, unsigned int *width ); + + + + + +#else + +int ImLzwPreDecode( ); + +int ImLzwDecode(); + +int ImLzwPostDecode(); + +int ImLzwPreEncode(); + +int ImLzwEncode(); + +int ImLzwPostEncode( ); + +void ImLzwCleanup(); + +void UnpackBits( ); + +void PackBits3( ); + +void PackBits( ); + +#endif + + + +/* + + * TYPEDEF & STRUCTURE + + * imTiffHeaderInfo - Rasterfile header information + + * imTiffHeaderFields - imTiffHeaderInfo description for Bin pkg + + * imTiffDirEntry - An Image File Directory entry + + * imTiffDirFields - imTiffDirEntry description for Bin pkg + + * imTiffDataWidth - An array of values indicating data size + + * imTiffMask - A bit mask corresponding to data size + + * imTiffDirData - Program internal representation of dir data + + * imTiffDirInfo - Program internal representation of directory + + * imTmpFile - A filename format for a temporary file. + + * + + * DESCRIPTION + + * A TIFF file header contains a magic number, the version number, + + * and an offset to the first directory entry. Directories are + + * variable length. The number of dir entries is in the short preceeding + + * the first directory entry. The offset of the next directory in + + * the file is in the 4 bytes after the last directory entry. Most + + * directory entries contain a tag, type, size, and a single value. + + * Other entries contain a tag, type, size, and offset to an array of + + * values (or a single double that would not fit in the value field). + + * + + * NOTE: + + * TIFF's tiff.h is **NOT** directly included here! + + * Therefore we recreate the essentials ourselves. + + */ + + + +typedef struct imTiffHeaderInfo + +{ + + unsigned short tiff_magic; /* Magic number (byte order) */ + + unsigned short tiff_version; /* TIFF version number */ + + unsigned int tiff_diroff; /* byte offset to first dir */ + +} imTiffHeaderInfo; + + + + + +static BinField imTiffHeaderFields[ ] = + +{ + + { USHORT, 2, 1 }, /* tiff_magic */ + + { USHORT, 2, 1 }, /* tiff_version */ + + { UINT, 4, 1 }, /* tiff_diroff */ + + { 0, 0, 0 }, + +}; + + + + + +typedef struct imTiffDirEntry + +{ + + unsigned short tdir_tag; /* Tagged file format type */ + + unsigned short tdir_type; /* Data type */ + + unsigned int tdir_count; /* Number of items; length */ + + unsigned int tdir_offset; /* byte offset to field data */ + + /* Last field is read separately*/ + +} imTiffDirEntry; + + + +/* + + * Read the tdir_offset separately depending on whether the + + * type indicates it contains, bytes, shorts, or a long. + + */ + +static BinField imTiffDirFields[ ] = + +{ + + { USHORT, 2, 1 }, /* tdir_tag */ + + { USHORT, 2, 1 }, /* tdir_type */ + + { UINT, 4, 1 }, /* tdir_count */ + + { 0, 0, 0 }, + +}; + + + + + +/* + + * These are the sizes of various datatypes as they exist in tiff files. + + * Always. We have to remember that in memory they can be wildly different. + + */ + +static int imTiffDataWidth[] = { + + 1, /* Nothing */ + + 1, /* Byte */ + + 1, /* ASCII */ + + 2, /* Short */ + + 4, /* Long */ + + 8, /* Rational */ + +}; + + + +static int imTiffMask[]= + +{ + + 0xff, /* Nothing */ + + 0xff, /* Byte */ + + 0xff, /* ASCII */ + + 0xffff, /* Short */ + + 0xffffffff, /* Long */ + +}; + + + + + +typedef struct imTiffDirData + +{ + + int type; /* Byte, short, or long */ + + int cnt; /* How many of them */ + + unsigned char *data; /* The actual array of values */ + +} imTiffDirData; + + + + + +typedef struct imTiffDirInfo + +{ + + unsigned int t_width; /* Image width */ + + unsigned int t_height; /* Image height */ + + unsigned short t_depth; /* Image depth in bits */ + + unsigned int t_compression; /* Image compression scheme */ + + unsigned int t_photometric; /* Photometric interpretation */ + + unsigned int t_orientation; /* Image orientation */ + + unsigned int t_rowsperstrip; /* Image rows/strip of data */ + + unsigned int t_samplesperpixel; /* Image samples/pixel */ + + unsigned int t_planarconfig; /* Storage type */ + + imTiffDirData t_xposition; /* Image x position */ + + imTiffDirData t_yposition; /* Image y position */ + + imTiffDirData t_bitspersample;/* Image depth in bits */ + + imTiffDirData t_stripoffsets; /* Image bytes counts for strips*/ + + imTiffDirData t_stripbytecounts; /* Image bytes counts for strips*/ + + imTiffDirData t_colormap; /* RGB map for pallette image */ + + unsigned char *t_imagedescr; /* Image description */ + + unsigned char *t_software; /* Software used to create */ + + unsigned char *t_datetime; /* Date/time created */ + + unsigned char *t_artist; /* Creator */ + + unsigned char *t_host; /* Host computer used */ + + unsigned char *t_whitepoint; /* Image white point */ + + unsigned char *t_primarychrom; /* Primary chromaticities */ + + unsigned int t_predictor; /* Predict pixel differences */ + + unsigned int t_matteing; /* Alpha channel is present ? */ + +} imTiffDirInfo; + + + + + +static imTiffHeaderInfo imTiffHeader; /* TIFF file header */ + + + + + + + +/* + + * CONSTANTS + + * IMTIFFBIGENDIANMAGIC - file magic number + + * IMTIFFLITTLEENDIANMAGIC - escape code for run-length encoding + + * IMTIFFVERSION - TIFF version number + + * IMTIFFBYTESPERWORD - Number of bytes in a word + + * IMTIFFSHORTSPERWORD - Number of shorts in a word + + * IMTIFFBITSPERBYTE - Number of bits in a byte + + * IMTIFFNIBBLESPERBYTE - Number of nibbles in a byte + + * IMTIFFNIBBLESMULT - Multiply by 16 to fill out to 8 bits + + * IMTIFFBITMULT - Multiply by 255 to fill out to 8 bits + + * IMTIFF(type) - enumerated types + + * + + * DESCRIPTION + + * IMTIFFBIGENDIANMAGIC and IMTIFFLITTLEENDIANMAGIC are + + * the 32-bit magic numbers at the top of all tiff files. + + * The former indicates MBF byte ordering and the latter indicates + + * LBF byte ordering. + + * + + * IMTIFFVERSION is always 42. + + * + + * The enumerated types are found in the directory type field to + + * describe what is to be found in the offset field or whereever the + + * offset points to. + + * + + */ + + + +#define IMTIFFBIGENDIANMAGIC 0x4d4d + +#define IMTIFFLITTLEENDIANMAGIC 0x4949 + +#define IMTIFFVERSION (42) + +#define IMTIFFBITSPERBYTE (8) + +#define IMTIFFBITSPERLONG (32) + +#define IMTIFFBYTESPERWORD (4) + +#define IMTIFFSHORTSPERWORD (2) + +#define IMTIFFNIBBLESPERBYTE (2) + +#define IMTIFFNIBBLEMULT (16) + + + +#define IMTIFFBYTE (1) + +#define IMTIFFASCII (2) + +#define IMTIFFSHORT (3) + +#define IMTIFFLONG (4) + +#define IMTIFFRATIONAL (5) + + + +/* + +#define DEBUG + +*/ + + + + + + + + + + + +/* + + * CONSTANTS + + * NULL - an empty pointer + + * + + * DESCRIPTION + + * NULL might not be defined by the include files, but we need it. + + */ + + + +#ifndef NULL + +#define NULL (0) + +#endif + + + + + +/* + + * CONSTANTS + + * TIFF Tag Definitions. + + * + + * DESCRIPTIONS + + * These are straight out of tiff.h (with an IM_added ) + + * Those marked with a + are obsoleted by revision 5.0 + + */ + + + +#define IM_TIFFTAG_SUBFILETYPE 254 /* subfile data descriptor */ + +#define FILETYPE_REDUCEDIMAGE 0x1 /* reduced resolution version */ + +#define FILETYPE_PAGE 0x2 /* one page of many */ + +#define FILETYPE_MASK 0x4 /* transparency mask */ + +#define IM_TIFFTAG_OSUBFILETYPE 255 /* +kind of data in subfile */ + +#define OFILETYPE_IMAGE 1 /* full resolution image data */ + +#define OFILETYPE_REDUCEDIMAGE 2 /* reduced size image data */ + +#define OFILETYPE_PAGE 3 /* one page of many */ + +#define IM_TIFFTAG_IMAGEWIDTH 256 /* image width in pixels */ + +#define IM_TIFFTAG_IMAGELENGTH 257 /* image height in pixels */ + +#define IM_TIFFTAG_BITSPERSAMPLE 258 /* bits per channel (sample) */ + +#define IM_TIFFTAG_COMPRESSION 259 /* data compression technique */ + +#define IM_COMPRESSION_NONE 1 /* dump mode */ + +#define IM_COMPRESSION_CCITTRLE 2 /* CCITT modified Huffman RLE */ + +#define IM_COMPRESSION_CCITTFAX3 3 /* CCITT Group 3 fax encoding */ + +#define IM_COMPRESSION_CCITTFAX4 4 /* CCITT Group 4 fax encoding */ + +#define IM_COMPRESSION_LZW 5 /* Lempel-Ziv & Welch */ + +#define IM_COMPRESSION_NEXT 32766 /* NeXT 2-bit RLE */ + +#define IM_COMPRESSION_CCITTRLEW 32771 /* #1 w/ word alignment */ + +#define IM_COMPRESSION_PACKBITS 32773 /* Macintosh RLE */ + +#define IM_COMPRESSION_THUNDERSCAN 32809 /* ThunderScan RLE */ + +#define IM_COMPRESSION_PICIO 32900 /* old Pixar picio RLE */ + +#define IM_COMPRESSION_SGIRLE 32901 /* Silicon Graphics RLE */ + +#define IM_TIFFTAG_PHOTOMETRIC 262 /* photometric interpretation */ + +#define IM_PHOTOMETRIC_MINISWHITE 0 /* min value is white */ + +#define IM_PHOTOMETRIC_MINISBLACK 1 /* min value is black */ + +#define IM_PHOTOMETRIC_RGB 2 /* RGB color model */ + +#define IM_PHOTOMETRIC_PALETTE 3 /* color map indexed */ + +#define IM_PHOTOMETRIC_MASK 4 /* holdout mask */ + +#define IM_PHOTOMETRIC_DEPTH 32768 /* z-depth data */ + +#define IM_TIFFTAG_THRESHHOLDING 263 /* +thresholding used on data */ + +#define IM_THRESHHOLD_BILEVEL 1 /* b&w art scan */ + +#define IM_THRESHHOLD_HALFTONE 2 /* or dithered scan */ + +#define IM_THRESHHOLD_ERRORDIFFUSE 3 /* usually floyd-steinberg */ + +#define IM_TIFFTAG_CELLWIDTH 264 /* +dithering matrix width */ + +#define IM_TIFFTAG_CELLLENGTH 265 /* +dithering matrix height */ + +#define IM_TIFFTAG_FILLORDER 266 /* +data order within a byte */ + +#define IM_FILLORDER_MSB2LSB 1 /* most significant -> least */ + +#define IM_FILLORDER_LSB2MSB 2 /* least significant -> most */ + +#define IM_TIFFTAG_DOCUMENTNAME 269 /* name of doc. image is from */ + +#define IM_TIFFTAG_IMAGEDESCRIPTION 270 /* info about image */ + +#define IM_TIFFTAG_MAKE 271 /* scanner manufacturer name */ + +#define IM_TIFFTAG_MODEL 272 /* scanner model name/number */ + +#define IM_TIFFTAG_STRIPOFFSETS 273 /* offsets to data strips */ + +#define IM_TIFFTAG_ORIENTATION 274 /* +image orientation */ + +#define IM_ORIENTATION_TOPLEFT 1 /* row 0 top, col 0 lhs */ + +#define IM_ORIENTATION_TOPRIGHT 2 /* row 0 top, col 0 rhs */ + +#define IM_ORIENTATION_BOTRIGHT 3 /* row 0 bottom, col 0 rhs */ + +#define IM_ORIENTATION_BOTLEFT 4 /* row 0 bottom, col 0 lhs */ + +#define IM_ORIENTATION_LEFTTOP 5 /* row 0 lhs, col 0 top */ + +#define IM_ORIENTATION_RIGHTTOP 6 /* row 0 rhs, col 0 top */ + +#define IM_ORIENTATION_RIGHTBOT 7 /* row 0 rhs, col 0 bottom */ + +#define IM_ORIENTATION_LEFTBOT 8 /* row 0 lhs, col 0 bottom */ + +#define IM_TIFFTAG_SAMPLESPERPIXEL 277 /* samples per pixel */ + +#define IM_TIFFTAG_ROWSPERSTRIP 278 /* rows per strip of data */ + +#define IM_TIFFTAG_STRIPBYTECOUNTS 279 /* bytes counts for strips */ + +#define IM_TIFFTAG_MINSAMPLEVALUE 280 /* +minimum sample value */ + +#define IM_TIFFTAG_MAXSAMPLEVALUE 281 /* maximum sample value */ + +#define IM_TIFFTAG_XRESOLUTION 282 /* pixels/resolution in x */ + +#define IM_TIFFTAG_YRESOLUTION 283 /* pixels/resolution in y */ + +#define IM_TIFFTAG_PLANARCONFIG 284 /* storage organization */ + +#define IM_PLANARCONFIG_CONTIG 1 /* single image plane */ + +#define IM_PLANARCONFIG_SEPARATE 2 /* separate planes of data */ + +#define IM_TIFFTAG_PAGENAME 285 /* page name image is from */ + +#define IM_TIFFTAG_XPOSITION 286 /* x page offset of image lhs */ + +#define IM_TIFFTAG_YPOSITION 287 /* y page offset of image lhs */ + +#define IM_TIFFTAG_FREEOFFSETS 288 /* +byte offset to free block */ + +#define IM_TIFFTAG_FREEBYTECOUNTS 289 /* +sizes of free blocks */ + +#define IM_TIFFTAG_GRAYRESPONSEUNIT 290 /* gray scale curve accuracy */ + +#define IM_GRAYRESPONSEUNIT_10S 1 /* tenths of a unit */ + +#define IM_GRAYRESPONSEUNIT_100S 2 /* hundredths of a unit */ + +#define IM_GRAYRESPONSEUNIT_1000S 3 /* thousandths of a unit */ + +#define IM_GRAYRESPONSEUNIT_10000S 4 /* ten-thousandths of a unit */ + +#define IM_GRAYRESPONSEUNIT_100000S 5 /* hundred-thousandths */ + +#define IM_TIFFTAG_GRAYRESPONSECURVE 291 /* gray scale response curve */ + +#define IM_TIFFTAG_GROUP3OPTIONS 292 /* 32 flag bits */ + +#define IM_GROUP3OPT_2DENCODING 0x1 /* 2-dimensional coding */ + +#define IM_GROUP3OPT_UNCOMPRESSED 0x2 /* data not compressed */ + +#define IM_GROUP3OPT_FILLBITS 0x4 /* fill to byte boundary */ + +#define IM_TIFFTAG_GROUP4OPTIONS 293 /* 32 flag bits */ + +#define IM_GROUP4OPT_UNCOMPRESSED 0x2 /* data not compressed */ + +#define IM_TIFFTAG_RESOLUTIONUNIT 296 /* units of resolutions */ + +#define IM_RESUNIT_NONE 1 /* no meaningful units */ + +#define IM_RESUNIT_INCH 2 /* english */ + +#define IM_RESUNIT_CENTIMETER 3 /* metric */ + +#define IM_TIFFTAG_PAGENUMBER 297 /* page numbers of multi-page */ + +#define IM_TIFFTAG_COLORRESPONSEUNIT 300 /* color scale curve accuracy */ + +#define IM_COLORRESPONSEUNIT_10S 1 /* tenths of a unit */ + +#define IM_COLORRESPONSEUNIT_100S 2 /* hundredths of a unit */ + +#define IM_COLORRESPONSEUNIT_1000S 3 /* thousandths of a unit */ + +#define IM_COLORRESPONSEUNIT_10000S 4 /* ten-thousandths of a unit */ + +#define IM_COLORRESPONSEUNIT_100000S 5 /* hundred-thousandths */ + +#define IM_TIFFTAG_COLORRESPONSECURVE 301 /* RGB response curve */ + +#define IM_TIFFTAG_SOFTWARE 305 /* name & release */ + +#define IM_TIFFTAG_DATETIME 306 /* creation date and time */ + +#define IM_TIFFTAG_ARTIST 315 /* creator of image */ + +#define IM_TIFFTAG_HOSTCOMPUTER 316 /* machine where created */ + +#define IM_TIFFTAG_PREDICTOR 317 /* prediction scheme w/ LZW */ + +#define IM_TIFFTAG_WHITEPOINT 318 /* image white point */ + +#define IM_TIFFTAG_PRIMARYCHROMATICITIES 319 /* primary chromaticities */ + +#define IM_TIFFTAG_COLORMAP 320 /* RGB map for pallette image */ + +#define IM_TIFFTAG_BADFAXLINES 326 /* lines w/ wrong pixel count */ + +#define IM_TIFFTAG_CLEANFAXDATA 327 /* regenerated line info */ + +#define IM_CLEANFAXDATA_CLEAN 0 /* no errors detected */ + +#define IM_CLEANFAXDATA_REGENERATED 1 /* receiver regenerated lines */ + +#define IM_CLEANFAXDATA_UNCLEAN 2 /* uncorrected errors exist */ + +#define IM_TIFFTAG_CONSECUTIVEBADFAXLINES 328 /* max consecutive bad lines */ + +/* tags 32995-32999 are private tags registered to SGI */ + +#define IM_TIFFTAG_MATTEING 32995 /* alpha channel is present */ + +#define IM_TIFFTAG_SGICOLORMAP 32996 /* SGI colormap indicator */ + + + + + +/* + + * STRUCTURE + + * tagnames - an array of the names of the tages + + * + + * DESCRIPTION + + * An array of tagnames so that we can print and debug easily. + + */ + + + +static + +struct ImTiffTagNames { + + int tag; + + char *name; + +} ImTiffTagNames[] = { + + { IM_TIFFTAG_SUBFILETYPE, "SubFileType" }, + + { IM_TIFFTAG_OSUBFILETYPE, "OldSubFileType" }, + + { IM_TIFFTAG_IMAGEWIDTH, "ImageWidth" }, + + { IM_TIFFTAG_IMAGELENGTH, "ImageLength" }, + + { IM_TIFFTAG_BITSPERSAMPLE, "BitsPerSample" }, + + { IM_TIFFTAG_COMPRESSION, "Compression" }, + + { IM_TIFFTAG_PHOTOMETRIC, "Photometric" }, + + { IM_TIFFTAG_THRESHHOLDING, "Threshholding" }, + + { IM_TIFFTAG_CELLWIDTH, "CellWidth" }, + + { IM_TIFFTAG_CELLLENGTH, "CellLength" }, + + { IM_TIFFTAG_FILLORDER, "FillOrder" }, + + { IM_TIFFTAG_DOCUMENTNAME, "DocumentName" }, + + { IM_TIFFTAG_IMAGEDESCRIPTION, "ImageDescription" }, + + { IM_TIFFTAG_MAKE, "Make" }, + + { IM_TIFFTAG_MODEL, "Model" }, + + { IM_TIFFTAG_STRIPOFFSETS, "StripOffsets" }, + + { IM_TIFFTAG_ORIENTATION, "Orientation" }, + + { IM_TIFFTAG_SAMPLESPERPIXEL, "SamplesPerPixel" }, + + { IM_TIFFTAG_ROWSPERSTRIP, "RowsPerStrip" }, + + { IM_TIFFTAG_STRIPBYTECOUNTS, "StripByteCounts" }, + + { IM_TIFFTAG_MINSAMPLEVALUE, "MinSampleValue" }, + + { IM_TIFFTAG_MAXSAMPLEVALUE, "MaxSampleValue" }, + + { IM_TIFFTAG_XRESOLUTION, "XResolution" }, + + { IM_TIFFTAG_YRESOLUTION, "YResolution" }, + + { IM_TIFFTAG_PLANARCONFIG, "PlanarConfig" }, + + { IM_TIFFTAG_PAGENAME, "PageName" }, + + { IM_TIFFTAG_XPOSITION, "XPosition" }, + + { IM_TIFFTAG_YPOSITION, "YPosition" }, + + { IM_TIFFTAG_FREEOFFSETS, "FreeOffsets" }, + + { IM_TIFFTAG_FREEBYTECOUNTS, "FreeByteCounts" }, + + { IM_TIFFTAG_GRAYRESPONSEUNIT, "GrayResponseUnit" }, + + { IM_TIFFTAG_GRAYRESPONSECURVE,"GrayResponseCurve" }, + + { IM_TIFFTAG_GROUP3OPTIONS, "Group3Options" }, + + { IM_TIFFTAG_GROUP4OPTIONS, "Group4Options" }, + + { IM_TIFFTAG_RESOLUTIONUNIT, "ResolutionUnit" }, + + { IM_TIFFTAG_PAGENUMBER, "PageNumber" }, + + { IM_TIFFTAG_COLORRESPONSEUNIT,"ColorResponseUnit" }, + + { IM_TIFFTAG_COLORRESPONSECURVE,"ColorResponseCurve" }, + + { IM_TIFFTAG_SOFTWARE, "Software" }, + + { IM_TIFFTAG_DATETIME, "DateTime" }, + + { IM_TIFFTAG_ARTIST, "Artist" }, + + { IM_TIFFTAG_HOSTCOMPUTER, "HostComputer" }, + + { IM_TIFFTAG_PREDICTOR, "Predictor" }, + + { IM_TIFFTAG_WHITEPOINT, "Whitepoint" }, + + { IM_TIFFTAG_PRIMARYCHROMATICITIES,"PrimaryChromaticities" }, + + { IM_TIFFTAG_COLORMAP, "Colormap" }, + + { IM_TIFFTAG_BADFAXLINES, "Bad FAX Lines" }, + + { IM_TIFFTAG_CLEANFAXDATA, "Clean Fax Data" }, + + { IM_TIFFTAG_CONSECUTIVEBADFAXLINES, "Consecutive bad fax lines" }, + + { IM_TIFFTAG_SGICOLORMAP, "SGIColormap" }, + + { 32768, "OLD BOGUS Matteing tag" }, + + { IM_TIFFTAG_MATTEING, "Matteing" }, + +}; + + + +#define IM_NTAGS (sizeof (ImTiffTagNames) / sizeof (ImTiffTagNames[0])) + + + +#define IMTIFF_IMAGEWIDTH_INDEX 0 + +#define IMTIFF_IMAGELENGTH_INDEX 1 + +#define IMTIFF_BITSPERSAMPLE_INDEX 2 + +#define IMTIFF_COMPRESSION_INDEX 3 + +#define IMTIFF_PHOTOMETRIC_INDEX 4 + +#define IMTIFF_STRIPOFFSETS_INDEX 5 + +#define IMTIFF_ORIENTATION_INDEX 6 + +#define IMTIFF_SAMPLESPERPIXEL_INDEX 7 + +#define IMTIFF_ROWSPERSTRIP_INDEX 8 + +#define IMTIFF_STRIPBYTECOUNTS_INDEX 9 + +#define IMTIFF_PLANARCONFIG_INDEX 10 + +#define IMTIFF_XPOSITION_INDEX 11 + +#define IMTIFF_YPOSITION_INDEX 12 + +#define IMTIFF_MATTEING_INDEX 13 + +#define IMTIFF_COLORMAP_INDEX 14 /* Alway last ! */ + + + +/* + + * By default we write out a 1 bit deep file with 1 sample per pixel. + + * No compression and origin in the topleft corner. + + * + + * Note: These must be ordered by tag. + + */ + +static imTiffDirEntry imTiffWriteDir[] = + +{ + + { IM_TIFFTAG_IMAGEWIDTH, IMTIFFSHORT, 1, 0 }, + + { IM_TIFFTAG_IMAGELENGTH, IMTIFFSHORT, 1, 0 }, + + { IM_TIFFTAG_BITSPERSAMPLE, IMTIFFSHORT, 1, (8 << 16) }, + + { IM_TIFFTAG_COMPRESSION, IMTIFFSHORT, 1, (IM_COMPRESSION_NONE << 16) }, + + { IM_TIFFTAG_PHOTOMETRIC, IMTIFFSHORT, 1, (IM_PHOTOMETRIC_PALETTE << 16) }, + + { IM_TIFFTAG_STRIPOFFSETS, IMTIFFLONG, 1, 0 }, + + { IM_TIFFTAG_ORIENTATION, IMTIFFSHORT, 1, (IM_ORIENTATION_TOPLEFT << 16) }, + + { IM_TIFFTAG_SAMPLESPERPIXEL, IMTIFFSHORT, 1, 1 }, + + { IM_TIFFTAG_ROWSPERSTRIP, IMTIFFLONG, 1, 1 }, + + { IM_TIFFTAG_STRIPBYTECOUNTS, IMTIFFLONG, 1, 0 }, + + { IM_TIFFTAG_PLANARCONFIG, IMTIFFSHORT,1, (IM_PLANARCONFIG_CONTIG << 16) }, + + { IM_TIFFTAG_XPOSITION, IMTIFFRATIONAL, 1, 0 }, + + { IM_TIFFTAG_YPOSITION, IMTIFFRATIONAL, 1, 0 }, + + { IM_TIFFTAG_MATTEING, IMTIFFSHORT, 1, 0 }, + + { IM_TIFFTAG_COLORMAP, IMTIFFSHORT, 768, 0 }, + +}; + + + +#define IMTIFFWRITEDIRENTRIES sizeof(imTiffWriteDir)/sizeof(imTiffDirEntry) + + + +#ifdef __STDC__ + +static int imTiffReadDirectory( int ioType, int fd, FILE *fp, unsigned int *dirOffset, imTiffDirInfo *imTDI ); + +static int imTiffRead16( int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable, imTiffDirInfo *imTDI, + + ImVfb **pVfb ); + +static int imTiffRead24Cont( int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable, imTiffDirInfo *imTDI, + + ImVfb **pVfb ); + +static int imTiffRead24Sep( int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable, imTiffDirInfo *imTDI, + + ImVfb **pVfb ); + +static int imTiffRead8( int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable, imTiffDirInfo *imTDI, ImVfb **pVfb ); + +static int imTiffRead4( int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable, imTiffDirInfo *imTDI, ImVfb **pVfb ); + +static int imTiffRead1( int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable, imTiffDirInfo *imTDI, ImVfb **pVfb ); + +static int imTiffGetClt( imTiffDirInfo *imTDI, ImClt **pClt ); + +static int imTiffVfbWrite( int ioType, int fd, FILE *fp, TagTable *flagsTable, ImVfb *vfb ); + +#else + +static int imTiffReadDirectory( ); + +static int imTiffRead16( ); + +static int imTiffRead24Cont( ); + +static int imTiffRead24Sep( ); + +static int imTiffRead8( ); + +static int imTiffRead4( ); + +static int imTiffRead1( ); + +static int imTiffGetClt( ); + +static int imTiffVfbWrite( ); + +#endif + + + +/* + + * FUNCTION + + * imPrintTiffTag - print the name of tag + + * + + * DESCRIPTION + + * Given a tag value, seach through the table until the entry with + + * the matching tag is found. Print the string in the second field. + + */ + + + +static void /* Doesn't return a value */ + +#ifdef __STDC__ + +imPrintTiffTag(unsigned short tag) + +#else + +imPrintTiffTag(tag) + + unsigned short tag; /* The tag number */ + +#endif /* __STDC__ */ + +{ + + struct ImTiffTagNames *tp; + + char message[1024]; + + + + for (tp = ImTiffTagNames; tp < &ImTiffTagNames[IM_NTAGS]; tp++) + + { + + if (tp->tag == tag) + + { + + sprintf(message, "%s (%d) \n", tp->name, tag); + + ImErrorWarning(message,IM_NOTHING,IMEUNSUPPORTED); + + return; + + } + + } + + sprintf(message, "%d (0x%x) Tag Not Found \n", tag, tag); + + ImErrorWarning(message,IM_NOTHING,IMEUNSUPPORTED); + +} + + + + + + + +#ifdef DEBUG + + + +/* + + * FUNCTION + + * imPrintTiffDirLongEntry - Print the fields in a data entry + + * imPrintTiffDirInfo - Print the fields in a Tiff directory + + * + + * DESCRIPTION + + * Print the fields from the internal tiff directory structure in + + * human readable form. + + * This is strictly for debugging. + + * + + * WARNING: This routine assumes that a double is 8 bytes. + + */ + + + +static void + +#ifdef __STDC__ + +imPrintTiffDirLongEntry( imTiffDirData *imTD, char *str ) + +#else + +imPrintTiffDirLongEntry( imTD, str ) + + imTiffDirData *imTD; /* The internal directory info */ + + char *str; + +#endif + +{ + + int i; + + int val; + + + + fprintf(stderr,"%s: type %d count %d data %d \n <", + + str, imTD->type, imTD->cnt, imTD->data ); + + + + if ( imTD->data == NULL ) + + { + + fprintf(stderr," > \n"); + + return; + + } + + + + if ( imTD->type == IMTIFFASCII ) + + { + + fprintf(stderr,"%s > \n",imTD->data ); + + return; + + } + + + + for( i=0; icnt; i++) + + { + + val = imTiffDataItem( imTD, i ); + + fprintf(stderr,"%d ",val); + + } + + fprintf(stderr,"> \n "); + +} + + + + + +static void + +#ifdef __STDC__ + +imPrintTiffDirInfo( imTiffDirInfo * imTI ) + +#else + +imPrintTiffDirInfo( imTI ) + + imTiffDirInfo* imTI; /* The internal directory info */ + +#endif + +{ + + fprintf(stderr,"Tiff Directory Info \n"); + + fprintf(stderr,"------------------- \n"); + + fprintf(stderr,"width : %d \n", imTI->t_width); + + fprintf(stderr,"heigt : %d \n", imTI->t_height); + + fprintf(stderr,"depth : %d \n", imTI->t_depth); + + fprintf(stderr,"compr : %d \n", imTI->t_compression); + + fprintf(stderr,"photo : %d \n", imTI->t_photometric); + + fprintf(stderr,"planr : %d \n", imTI->t_planarconfig); + + fprintf(stderr,"orien : %d \n", imTI->t_orientation); + + fprintf(stderr,"sampl : %d \n", imTI->t_samplesperpixel); + + fprintf(stderr,"rowsp : %d \n", imTI->t_rowsperstrip); + + fprintf(stderr,"descrip : %s \n", imTI->t_imagedescr); + + fprintf(stderr,"software : %s \n", imTI->t_software); + + fprintf(stderr,"datetime : %s \n", imTI->t_datetime); + + fprintf(stderr,"user : %s \n", imTI->t_artist); + + fprintf(stderr,"host : %s \n", imTI->t_host); + + fprintf(stderr,"whitepoint : %s \n", imTI->t_whitepoint); + + fprintf(stderr,"primarychr : %s \n", imTI->t_primarychrom); + + fprintf(stderr,"predictor : %d \n", imTI->t_predictor); + + fprintf(stderr,"matting : %d \n", imTI->t_matteing); + + + + imPrintTiffDirLongEntry( &imTI->t_xposition, "xposition" ); + + imPrintTiffDirLongEntry( &imTI->t_yposition, "yposition" ); + + imPrintTiffDirLongEntry( &imTI->t_bitspersample, "bitspersample" ); + + imPrintTiffDirLongEntry( &imTI->t_colormap, "colormap" ); + + imPrintTiffDirLongEntry( &imTI->t_stripbytecounts, "stripbytecounts" ); + + imPrintTiffDirLongEntry( &imTI->t_stripoffsets, "stripoffsets" ); + + + + fprintf(stderr,"------------------- \n"); + +} + + + + + +static void + +#ifdef __STDC__ + +imPrintTiffWriteDir( char *s, unsigned int *sc, unsigned int *so ) + +#else + +imPrintTiffWriteDir( s, sc, so ) + +char *s; + +unsigned int *sc, *so; + +#endif + +{ + + int i, y; + + + + fprintf(stderr,"%s \n",s); + + + + for(i=0;i \n"); + + + + y = imTiffWriteDir[IMTIFF_IMAGELENGTH_INDEX].tdir_offset >> 16; + + + + for( i=0; i ", *sc++, *so++ ); + +} + + + +#endif /* DEBUG */ + + + + + +/* + + * FUNCTION + + * imTiffRead - read a TIFF image file + + * + + * DESCRIPTION + + * The file header is read and the magic number checked. If there is + + * a CLT in the file, it is read in and converted into an ImClt. + + * Separate routines are then called to handle different depth and + + * storage format variations of the image data. + + */ + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imTiffRead( int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable ) + +#else + +imTiffRead( ioType, fd, fp, flags, tagTable ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flags; /* Format flags */ + + TagTable *tagTable; /* Tag table to add to */ + +#endif + +{ + + unsigned int nextDir; /* Offset to next directory */ + + unsigned int imageCount; /* Number of images in file */ + + ImVfb *vfb; /* vfb holder */ + + ImClt *clt; /* clt holder */ + + unsigned char fileBuf; /* Read in the whole file */ + + int status; /* Status returned from readdir */ + + imTiffDirInfo imTiffInfo; /* Structure with useful info */ + + int depth; /* The bit depth of the image */ + + char message[100]; /* ImInfo message */ + + + + imageCount = 0; + + + + /* + + * Initialize the byte ordering to something so that we can read the + + * header + + */ + + BinByteOrder( BINMBF ); + + BinFloatFormat( BINIEEE ); + + + + /* + + * Read the magic number, check it, initialize byte order, then + + * read in the version number and directory offset to the first + + * directory. + + */ + + + + if ( ImBinRead( ioType, fd, fp, &imTiffHeader.tiff_magic, USHORT, 2, 1 ) == -1) + + { + + ImReturnBinError( ); + + } + + + + + + if ( imTiffHeader.tiff_magic == IMTIFFBIGENDIANMAGIC ) + + BinByteOrder( BINMBF ); + + + + else if ( imTiffHeader.tiff_magic == IMTIFFLITTLEENDIANMAGIC ) + + BinByteOrder( BINLBF ); + + + + else + + { + + ImErrNo = IMEMAGIC; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + if ( ImBinRead( ioType, fd, fp, &imTiffHeader.tiff_version, + + USHORT, 2, 1 ) == -1) + + { + + ImReturnBinError( ); + + } + + + + if ( imTiffHeader.tiff_version != IMTIFFVERSION ) + + { + + ImErrNo = IMEVERSION; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + /* + + * Output -verbose message + + */ + + sprintf( message, "%d", IMTIFFVERSION ); + + ImInfo( "Version", message ); + + if ( imTiffHeader.tiff_magic == IMTIFFBIGENDIANMAGIC ) + + { + + ImInfo( "Byte Order", "Most Significant Byte First" ); + + } + + else + + ImInfo( "Byte Order", "Least Significant Byte First" ); + + + + + + if ( ImBinRead( ioType, fd, fp, &imTiffHeader.tiff_diroff, + + UINT, 4, 1 ) == -1) + + { + + ImReturnBinError( ); + + } + + + + /* + + * Loop for every Image File Directory (IFD) in the file + + */ + + + + nextDir = imTiffHeader.tiff_diroff; + + + + while ( nextDir ) + + { + + /* + + * Read in the directory info. It is variable length. + + * Put the important info in the imTiffDirInfo struct. + + */ + + + + memset( &imTiffInfo, 0x00, sizeof(imTiffDirInfo) ); + + + + status = imTiffReadDirectory( ioType, fd, fp, &nextDir, + + &imTiffInfo ); + + + + if ( status == -1 ) + + return( -1 ); /* ImErrNo already set */ + + + +#ifdef DEBUG + + imPrintTiffDirInfo( &imTiffInfo ); + +#endif + + + + /* + + * Output -verbose message + + */ + + sprintf( message, "%d", (imageCount+1) ); + + ImInfo( "Image", message ); + + + + if( imTiffInfo.t_imagedescr != NULL ) + + { + + ImInfo( "Description", imTiffInfo.t_imagedescr ); + + } + + else + + ImInfo( "Description", "none" ); + + + + sprintf( message, "%d x %d", imTiffInfo.t_width, + + imTiffInfo.t_height ); + + ImInfo( "Resolution", message ); + + + + depth = imTiffInfo.t_depth * imTiffInfo.t_samplesperpixel; + + if( depth < 24 ) + + sprintf( message, "%d-bit Color Indexed", depth ); + + else + + sprintf( message, "%d-bit RGB", depth ); + + ImInfo( "Type", message ); + + + + if( imTiffInfo.t_planarconfig == IM_PLANARCONFIG_CONTIG ) + + sprintf( message, "Chunky (Contiguous)" ); + + else + + sprintf( message, "Planar (Separate)" ); + + ImInfo( "Plane Configuration", message ); + + + + /* + + * Call the appropriate routine for the depth of the image. + + * These routines are not called anywhere else but at least + + * it breaks up the code for debugging and modularity. + + * The routines called here have to make their own decisions + + * about what to do with encoded pixel data. + + */ + + + + depth = imTiffInfo.t_depth * imTiffInfo.t_samplesperpixel; + + + + /* + + * 32 images refer to index32. 24 includes the alpha channel + + */ + + if ( imTiffInfo.t_matteing ) + + depth = 24; + + + + switch( depth ) + + { + + case 16: + + status = imTiffRead16( ioType, fd, fp, flags, + + tagTable, &imTiffInfo, &vfb ); + + break; + + + + case 24: + + if ( imTiffInfo.t_planarconfig == IM_PLANARCONFIG_CONTIG ) + + status = imTiffRead24Cont(ioType,fd, fp, flags, + + tagTable, &imTiffInfo, &vfb ); + + else + + if ( imTiffInfo.t_planarconfig == IM_PLANARCONFIG_SEPARATE) + + status = imTiffRead24Sep(ioType,fd, fp, flags, + + tagTable, &imTiffInfo, &vfb ); + + else + + { + + ImErrNo = IMEPLANES; + + status = -1; + + } + + break; + + + + case 8: + + status = imTiffRead8( ioType, fd, fp, flags, + + tagTable, &imTiffInfo, &vfb ); + + break; + + + + case 4: + + status = imTiffRead4( ioType, fd, fp, flags, + + tagTable, &imTiffInfo, &vfb ); + + break; + + + + case 1: + + status = imTiffRead1( ioType, fd, fp, flags, + + tagTable, &imTiffInfo, &vfb ); + + break; + + + + + + case 32: + + default: + + ImErrNo = IMEDEPTH; /* We only understand 24,8,1 */ + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + if ( status == -1 ) + + return( -1 ); /* ImErrNo already set */ + + + + /* + + * Check the image orientation + + */ + + if ( imTiffInfo.t_orientation == IM_ORIENTATION_BOTLEFT || + + imTiffInfo.t_orientation == IM_ORIENTATION_LEFTBOT ) + + if ( ImVfbFlip( vfb, IMVFBYFLIP, vfb ) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + /* + + * Get an associated CLT if there is one + + */ + + if ( imTiffGetClt( &imTiffInfo, &clt ) == -1 ) + + return( -1 ); /* ImErrNo already set */ + + + + if ( clt != IMCLTNULL ) + + { + + ImVfbSClt( vfb, clt ); + + TagTableAppend( tagTable, + + TagEntryAlloc( "image clt", POINTER, &clt )); + + } + + TagTableAppend(tagTable, TagEntryAlloc( "image vfb", + + POINTER, &vfb )); + + + + /* + + * Output -verbose message + + */ + + switch( imTiffInfo.t_compression ) + + { + + case IM_COMPRESSION_NONE: + + sprintf( message, "none (Dump)" ); + + break; + + case IM_COMPRESSION_CCITTRLE: + + sprintf( message, "CCITT Run Length Encoded \ + (Huffman RLE)" ); + + break; + + case IM_COMPRESSION_CCITTFAX3: + + sprintf( message, "CCITT Group 3 FAX" ); + + break; + + case IM_COMPRESSION_CCITTFAX4: + + sprintf( message, "CCITT Group 4 FAX" ); + + break; + + case IM_COMPRESSION_LZW: + + sprintf(message,"Lempel-Ziv and Welch (LZW)" ); + + break; + + case IM_COMPRESSION_NEXT: + + sprintf( message, "Run Length Encoded \ +(NeXT 2-bit RLE)" ); + + break; + + case IM_COMPRESSION_CCITTRLEW: + + sprintf( message, "Run Length Encoded W \ +(RLE with word alignment)" ); + + break; + + case IM_COMPRESSION_PACKBITS: + + sprintf( message, "Apple Macintosh Packbits"); + + break; + + case IM_COMPRESSION_THUNDERSCAN: + + sprintf( message, "Run Length Encoded \ +(Thunderscan RLE)" ); + + break; + + case IM_COMPRESSION_PICIO: + + sprintf( message, "Run Length Encoded \ +(Pixar RLE)" ); + + break; + + case IM_COMPRESSION_SGIRLE: + + sprintf( message, "Run Length Encoded \ +(Silicon Graphics RLE)" ); + + break; + + } + + ImInfo( "Compression Type", message ); + + + + if( imTiffInfo.t_matteing ) + + { + + ImInfo( "Alpha Channel", "8-bit\n" ); + + } + + else + + ImInfo( "Alpha Channel", "none\n" ); + + + + + + imageCount++; + + } + + + + if ( imTiffInfo.t_compression == IM_COMPRESSION_LZW ) + + ImLzwCleanup( ); + + + + /* + + * Return the number of vfb's in the tag table + + */ + + return( imageCount ); + +} + + + + + + + +/* + + * FUNCTION + + * imTiffDecode - Decode the type, length, and offset fields + + * + + * DESCRIPTION + + * This is the case where the actual data item is stored in the offset + + * field. The type indicates how many bytes we want (i think). The + + * bytes are left justified so we have to shift to get them out. + + */ + + + +static int + +#ifdef __STDC__ + +imTiffDecode( imTiffDirEntry *tifDir, unsigned char *p ) + +#else + +imTiffDecode( tifDir, p ) + + imTiffDirEntry *tifDir; + + unsigned char *p; + +#endif + +{ + + int shift; + + int da; + + + + switch( tifDir->tdir_type ) + + { + + case IMTIFFBYTE: + + case IMTIFFASCII: + + shift = (IMTIFFBYTESPERWORD - tifDir->tdir_count) * IMTIFFBITSPERBYTE; + + da = tifDir->tdir_offset >> shift; + + memcpy( p, &da, sizeof(int) ); + + break; + + + + case IMTIFFSHORT: + + shift = (IMTIFFSHORTSPERWORD - tifDir->tdir_count) * IMTIFFBITSPERBYTE; + + da = tifDir->tdir_offset >> (shift * IMTIFFSHORTSPERWORD); + + memcpy( p, &da, sizeof(int) ); + + break; + + + + case IMTIFFLONG: + + memcpy( p, &tifDir->tdir_offset, sizeof(int) ); + + break; + + + + case IMTIFFRATIONAL: /* It is an error to have 8 byte data here*/ + + default: + + ImErrNo = IMEOUTOFRANGE; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + return( 0 ); + +} + + + + + +/* + + * FUNCTION + + * imTiffDecOrFetch - Decode or fetch directory data + + * + + * DESCRIPTION + + * Seek to the proper place in the file, allocate memory and then + + * read in the pertinent data. This is used to read in arrays of + + * data including strings, and colormaps. + + * If the data is actually just stuffed in the offset field then + + * copy that to the malloced space. + + * Return a pointer to the dataspace. + + */ + + + +static int /* Returns pointer or -1 for error */ + +#ifdef __STDC__ + +imTiffDecOrFetch( int ioType, int fd, FILE *fp, imTiffDirEntry *d, unsigned char **p ) + +#else + +imTiffDecOrFetch( ioType, fd, fp, d, p ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + imTiffDirEntry *d; /* The directory entry to fill */ + + unsigned char **p; /* Return a pointer result */ + +#endif + +{ + + int size; /* Size of the dir data */ + + int status; /* Returned from Bin Read */ + + int *ip=NULL; + + unsigned short *sp=NULL; + + double *dp=NULL; + + + + /* + + * Size of the data in the tiff file, NOT size in memory + + */ + + size = d->tdir_count * imTiffDataWidth[d->tdir_type]; + + + + if ( size <= 4 ) + + { + + if (imTiffDecode( d, (unsigned char*)p ) == -1) + + return( -1 ); /* ImErrNo already set */ + + } + + else + + { + + ImSeek( ioType, fd, fp, d->tdir_offset, 0 ); + + + + switch( d->tdir_type ) + + { + + case IMTIFFRATIONAL: + + ImMalloc( dp, double *, d->tdir_count * sizeof(double)); + + status = ImBinRead( ioType, fd, fp, dp, DOUBLE, 8, + + d->tdir_count ); + + *p = (unsigned char *)dp; + + break; + + + + case IMTIFFLONG: + + ImMalloc( ip, int *, d->tdir_count * sizeof(int)); + + status = ImBinRead( ioType, fd, fp, ip, INT, 4, + + d->tdir_count ); + + *p = (unsigned char *)ip; + + break; + + + + case IMTIFFSHORT: + + ImMalloc( sp, unsigned short *, d->tdir_count * sizeof(ushort)); + + status = ImBinRead( ioType, fd, fp, sp, USHORT, 2, + + d->tdir_count ); + + *p = (unsigned char *)sp; + + break; + + + + case IMTIFFBYTE: + + case IMTIFFASCII: + + ImMalloc( *p, unsigned char *, d->tdir_count * sizeof(unsigned char)); + + status = ImBinRead( ioType, fd, fp, *p, UCHAR, 1, + + d->tdir_count ); + + break; + + } + + + + if ( status == -1 ) + + { + + ImReturnBinError( ); + + } + + } + + + + return( 0 ); + +} + + + + + + + +/* + + * FUNCTION + + * imTiffDataItem - Get a data value out of the data field + + * + + * DESCRIPTION + + * Data may either be resident in the data field or else may be + + * out in an data array. Given an index and a XXX structure, find + + * and return the right size data element. + + */ + + + +static int /* Returns pointer or -1 for error */ + +#ifdef __STDC__ + +imTiffDataItem( imTiffDirData *iTD, int index ) + +#else + +imTiffDataItem( iTD, index ) + + imTiffDirData *iTD; /* The data item structure */ + + int index; /* What number item */ + +#endif + +{ + + int size; /* Size of one item of dir data */ + + int shift; /* Number of bits to shift */ + + unsigned char *cd; /* Array of chars */ + + short *sd; /* Array of shorts */ + + int *id; /* Array of ints */ + + double *dd; /* Array of doubles */ + + + + /* + + * Size of the data in the tiff file, Not size in memory + + */ + + size = imTiffDataWidth[iTD->type]; + + + + if ( iTD->cnt * size <= 4 ) /* Data is resident in data field */ + + { + + shift = index * size * IMTIFFBITSPERBYTE; + + return(((int)iTD->data >> shift) & imTiffMask[iTD->type]); + + } + + else + + { + + /* + + * Use the correct pointer type, and then indices work ! + + */ + + switch (iTD->type) + + { + + case IMTIFFBYTE: + + case IMTIFFASCII: + + cd = (unsigned char *)iTD->data; + + return ((int)cd[index]); + + break; + + + + case IMTIFFSHORT: + + sd = (short *)iTD->data; + + return ((int)sd[index]); + + break; + + + + case IMTIFFLONG: + + id = (int *)iTD->data; + + return ((int)id[index]); + + break; + + + + case IMTIFFRATIONAL: + + dd = (double *)iTD->data; + + return ((int)dd[index]); + + break; + + } + + + + } + +} + + + + + +/* + + * imTiffReadDirectory - read a TIFF Rasterfile Directory + + * + + * DESCRIPTION + + * Read a Tiff directory. Parse the directory and associated pixel data. + + * Store the resulting vfb and possibly clt in the tagtable. + + * Return the offset to the next tiff directory in dirOffset. + + */ + + + +static int + +#ifdef __STDC__ + +imTiffReadDirectory( int ioType, int fd, FILE *fp, unsigned int *dirOffset, imTiffDirInfo *imTDI ) + +#else + +imTiffReadDirectory( ioType, fd, fp, dirOffset, imTDI ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + unsigned int *dirOffset; /* The offset of the tiff dir */ + + imTiffDirInfo *imTDI; /* Unencoded directory info */ + +#endif + +{ + + imTiffDirEntry *tifDir; /* Store an array of entries */ + + unsigned char *bits; /* Point to an array of data */ + + unsigned short numEntries; /* Number of directory entries */ + + unsigned short so[2]; /* Temp storage of two shorts */ + + int i; /* Loop counter */ + + int c,t; /* count and type of data data */ + + int depth; /* Bit depth of image */ + + int status=0; /* Hold returned status */ + + int size; /* Number of bytes of offset */ + + + + + + /* + + * Seek to the directory at offset dirOffset in the file. + + */ + + ImSeek( ioType, fd, fp, *dirOffset, 0 ); + + + + /* + + * Read in the number of entries in this directory, allocate space + + * and then read in the directory entries one by one. + + */ + + if ( ImBinRead( ioType, fd, fp, &numEntries, USHORT, 2, 1) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + ImCalloc( tifDir, imTiffDirEntry *, sizeof(imTiffDirEntry), numEntries); + + + + /* + + * Read all of the directory entries into an array of structs + + */ + + for( i=0; i 4 ) + + { + + if ( ImBinRead( ioType, fd, fp, + + &(tifDir[i].tdir_offset), INT, 4, 1) == -1 ) + + ImReturnBinError( ); + + continue; + + } + + + + /* + + * If it is not an offset, then there is data in the + + * offset field. We have to read it as the correct type + + */ + + switch( tifDir[i].tdir_type ) + + { + + case IMTIFFBYTE: + + case IMTIFFASCII: + + status = ImBinRead( ioType, fd, fp, + + &(tifDir[i].tdir_offset), UCHAR, 1, 4); + + break; + + + + case IMTIFFSHORT: + + status = ImBinRead( ioType, fd, fp, so, USHORT, 2, 2); + + tifDir[i].tdir_offset = (so[0] << 16) | so[1]; + + break; + + + + case IMTIFFLONG: + + status = ImBinRead( ioType, fd, fp, + + &(tifDir[i].tdir_offset), INT, 4, 1); + + break; + + } + + + + if ( status == -1 ) + + ImReturnBinError( ); + + } + + + + /* + + * Read the offset of the next directory. It will probably be + + * zero indicating that this file only has one directory. + + */ + + if ( ImBinRead( ioType, fd, fp, dirOffset, UINT, 4, 1) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + /* + + * Parse the directory entries and do something sensible with each + + * value we are interested in. + + */ + + for( i=0; it_width ); + + break; + + + + case IM_TIFFTAG_IMAGELENGTH: /* This means height */ + + status = imTiffDecOrFetch( ioType, fd, fp, &(tifDir[i]), + + (unsigned char**)&imTDI->t_height ); + + break; + + + + case IM_TIFFTAG_COMPRESSION: + + status = imTiffDecOrFetch( ioType, fd, fp, &(tifDir[i]), + + (unsigned char**)&imTDI->t_compression ); + + break; + + + + case IM_TIFFTAG_PHOTOMETRIC: + + status = imTiffDecOrFetch( ioType, fd, fp, &(tifDir[i]), + + (unsigned char**)&imTDI->t_photometric ); + + break; + + + + case IM_TIFFTAG_IMAGEDESCRIPTION: + + status = imTiffDecOrFetch( ioType, fd, fp, &(tifDir[i]), + + &imTDI->t_imagedescr ); + + break; + + + + case IM_TIFFTAG_ORIENTATION: + + status = imTiffDecOrFetch( ioType, fd, fp, &(tifDir[i]), + + (unsigned char**)&imTDI->t_orientation ); + + if ( imTDI->t_orientation != IM_ORIENTATION_TOPLEFT && + + imTDI->t_orientation != IM_ORIENTATION_BOTLEFT && + + imTDI->t_orientation != IM_ORIENTATION_LEFTTOP && + + imTDI->t_orientation != IM_ORIENTATION_LEFTBOT ) + + { + + ImErrNo = IMEORIENTATION; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + break; + + + + case IM_TIFFTAG_SAMPLESPERPIXEL: + + status= imTiffDecOrFetch( ioType, fd, fp, &(tifDir[i]), + + (unsigned char**)&imTDI->t_samplesperpixel ); + + break; + + + + case IM_TIFFTAG_ROWSPERSTRIP: + + status = imTiffDecOrFetch( ioType, fd, fp, &(tifDir[i]), + + (unsigned char**)&imTDI->t_rowsperstrip ); + + break; + + + + case IM_TIFFTAG_BITSPERSAMPLE: + + imTDI->t_bitspersample.type = tifDir[i].tdir_type; + + imTDI->t_bitspersample.cnt = tifDir[i].tdir_count; + + status = imTiffDecOrFetch( ioType, fd, fp, &(tifDir[i]), + + &imTDI->t_bitspersample.data ); + + + + /* + + * The depth may be (8 8 8) for eight bits each + + * channel. Just save one of the counts in the + + * in the t_depth field for easier access. + + */ + + + + imTDI->t_depth=imTiffDataItem(&imTDI->t_bitspersample,0); + + break; + + + + case IM_TIFFTAG_STRIPBYTECOUNTS: + + imTDI->t_stripbytecounts.type = tifDir[i].tdir_type; + + imTDI->t_stripbytecounts.cnt = tifDir[i].tdir_count; + + status = imTiffDecOrFetch( ioType, fd, fp, &(tifDir[i]), + + &imTDI->t_stripbytecounts.data ); + + break; + + + + case IM_TIFFTAG_STRIPOFFSETS: + + imTDI->t_stripoffsets.type = tifDir[i].tdir_type; + + imTDI->t_stripoffsets.cnt = tifDir[i].tdir_count; + + status = imTiffDecOrFetch( ioType, fd, fp, &(tifDir[i]), + + &imTDI->t_stripoffsets.data ); + + break; + + + + case IM_TIFFTAG_COLORMAP: + + imTDI->t_colormap.type = tifDir[i].tdir_type; + + imTDI->t_colormap.cnt = tifDir[i].tdir_count; + + status = imTiffDecOrFetch( ioType, fd, fp, &(tifDir[i]), + + &imTDI->t_colormap.data ); + + break; + + + + case IM_TIFFTAG_PLANARCONFIG: + + status = imTiffDecOrFetch( ioType, fd, fp, &(tifDir[i]), + + (unsigned char**)&imTDI->t_planarconfig ); + + break; + + + + case IM_TIFFTAG_XPOSITION: + + imTDI->t_xposition.type = tifDir[i].tdir_type; + + imTDI->t_xposition.cnt = tifDir[i].tdir_count; + + status = imTiffDecOrFetch( ioType, fd, fp, &(tifDir[i]), + + &imTDI->t_xposition.data ); + + break; + + + + case IM_TIFFTAG_YPOSITION: + + imTDI->t_yposition.type = tifDir[i].tdir_type; + + imTDI->t_yposition.cnt = tifDir[i].tdir_count; + + status = imTiffDecOrFetch( ioType, fd, fp, &(tifDir[i]), + + &imTDI->t_yposition.data ); + + break; + + + + case IM_TIFFTAG_SOFTWARE: + + status = imTiffDecOrFetch( ioType, fd, fp, &(tifDir[i]), + + &imTDI->t_software ); + + break; + + + + case IM_TIFFTAG_DATETIME: + + status = imTiffDecOrFetch( ioType, fd, fp, &(tifDir[i]), + + &imTDI->t_datetime ); + + break; + + + + case IM_TIFFTAG_ARTIST: + + status = imTiffDecOrFetch( ioType, fd, fp, &(tifDir[i]), + + &imTDI->t_artist ); + + break; + + + + case IM_TIFFTAG_HOSTCOMPUTER: + + status = imTiffDecOrFetch( ioType, fd, fp, &(tifDir[i]), + + &imTDI->t_host ); + + break; + + + + case IM_TIFFTAG_WHITEPOINT: + + status = imTiffDecOrFetch( ioType, fd, fp, &(tifDir[i]), + + &imTDI->t_whitepoint ); + + break; + + + + case IM_TIFFTAG_PRIMARYCHROMATICITIES: + + status = imTiffDecOrFetch( ioType, fd, fp, &(tifDir[i]), + + &imTDI->t_primarychrom ); + + break; + + + + case IM_TIFFTAG_PREDICTOR: + + status = imTiffDecOrFetch( ioType, fd, fp, &(tifDir[i]), + + (unsigned char**)&imTDI->t_predictor ); + + break; + + + + case IM_TIFFTAG_MATTEING: + + status = imTiffDecOrFetch( ioType, fd, fp, &(tifDir[i]), + + (unsigned char**)&imTDI->t_matteing ); + + break; + + + + default: + + ImErrNo = IMEFORMAT; + + ImErrorWarning("Ignoring tiff dir tag", -1, ImErrNo ); + + imPrintTiffTag( tifDir[i].tdir_tag ); + + break; + + } + + + + if ( status == -1 ) + + return( -1 ); /* ImErrNo already set */ + + + + } /* End of parsing directory */ + + + + /* + + * Check to be sure that values are reasonable + + */ + + + + if ( imTDI->t_predictor > 1 ) + + { + + ImErrNo = IMEUNSUPPORTED; + + ImErrorFatal( "LZW+Prediction not supported", -1, ImErrNo ); + + } + + if ( imTDI->t_width < 1 ) + + { + + ImErrNo = IMEWIDTH; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + if ( imTDI->t_height < 1 ) + + { + + ImErrNo = IMEHEIGHT; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + if ( imTDI->t_rowsperstrip == 0 ) + + imTDI->t_rowsperstrip = imTDI->t_height; + + + + if ( imTDI->t_depth == 0 ) + + imTDI->t_depth = 1; + + + + if ( imTDI->t_samplesperpixel == 0 ) + + imTDI->t_samplesperpixel = 1; + + + + if ( imTDI->t_compression == 0 ) + + imTDI->t_compression = IM_COMPRESSION_NONE; + + + + if ( imTDI->t_orientation == 0 ) + + imTDI->t_orientation = IM_ORIENTATION_TOPLEFT; + + + + if ( imTDI->t_compression != IM_COMPRESSION_NONE && + + imTDI->t_compression != IM_COMPRESSION_PACKBITS && + + imTDI->t_compression != IM_COMPRESSION_LZW ) + + { + + ImErrNo = IMEUNSUPPORTED; + + ImErrorFatal("TIFF compression scheme not supported", -1, ImErrNo ); + + } + + + + /* + + * If the bytecounts field is missing and there is more than one + + * strip offset given, then give up, it is too hard to figure out. + + * The bytecounts field is mandatory but someprograms don't write it + + * anyway. + + */ + + + + if ( imTDI->t_stripbytecounts.data == NULL ) + + { + + if ( imTDI->t_compression != IM_COMPRESSION_NONE ) + + { + + ImErrNo = IMECONFLICT; + + ImErrorFatal( + + "TIFF required tag 'stripbytecounts' not present", + + -1, ImErrNo ); + + } + + + + if ( imTDI->t_stripoffsets.cnt > 1 ) + + { + + ImErrNo = IMECONFLICT; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + ImErrorWarning( + + "TIFF required tag 'stripbytecounts' tag not present, trying anyway", + + -1, IMESYNTAX ); + + + + imTDI->t_stripbytecounts.cnt = 1; + + imTDI->t_stripbytecounts.type = IMTIFFLONG; + + imTDI->t_stripbytecounts.data = (unsigned char*) ((imTDI->t_width * + + imTDI->t_height * imTDI->t_depth) / IMTIFFBITSPERBYTE); + + } + + + + free( tifDir ); + + return ( 0 ); + +} + + + + + +#define imTiffFree() \ + if(strip!=NULL) \ + free(strip); \ + if(ucstrip!=NULL) \ + free(ucstrip); + + + +/* + + * FUNCTION + + * imTiffRead1 - read 1-bit TIFF format + + * imTiffRead4 - read 4-bit TIFF format + + * imTiffRead8 - read 8-bit TIFF format + + * imTiffRead16 - read 16-bit TIFF format (CLT longer than 256) + + * imTiffRead24Sep - read 24-bit TIFF format separate planes + + * imTiffRead24Cont - read 24-bit TIFF format contiguous planes + + * + + * DESCRIPTION + + * Each of these routines deal with TIFF files. + + * The input stream is a file or a pipe. We don't care. + + * + + * A new VFB is allocated. The image is read in + + * and converted into the VFB. + + * + + * The 1 and 4 bit reads should be changed when we implement 1 and 4 + + * bit vfbs. + + */ + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imTiffRead1( int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable, imTiffDirInfo *imTDI, ImVfb **pVfb ) + +#else + +imTiffRead1( ioType, fd, fp, flags, tagTable, imTDI, pVfb ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flags; /* Format flags */ + + TagTable *tagTable; /* Tag table to add to */ + + imTiffDirInfo *imTDI; /* Directory information */ + + ImVfb **pVfb; /* VFB to fill and return */ + +#endif + +{ + + ImVfbPtr pptr; /* Pixel pointer */ + + ImVfb *vfb; /* A more convenient pointer */ + + unsigned char *strip; /* Storage for pixel indices */ + + unsigned char *ucstrip=NULL; /* If encoded, use this buffer */ + + unsigned char *stripptr; /* Walk thru the pixels */ + + int size; /* The number of bytes of index */ + + int i,j; /* Loop counters */ + + int where; /* Where to start reading */ + + int howMany; /* How many to read */ + + int decodedsize; /* How many bytes when decoded */ + + int k; /* Counter */ + + int rows; /* Number of scanlines this time*/ + + int rowsread=0; /* Number of scanlines readsofar*/ + + int shift; /* Number of bits to shift */ + + int val; /* Pixel value, either 0 or 255 */ + + + + /* + + * Allocate a VFB of the required size. + + */ + + + + if ( (*pVfb = ImVfbAlloc( imTDI->t_width, imTDI->t_height, + + IMVFBMONO )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + vfb = *pVfb; + + pptr = ImVfbQFirst( vfb ); + + + + /* + + * Allocate space for the pixel values + + * Sometimes things are bigger when compresses, so we make it 2x + + */ + + + + size = (imTDI->t_width * imTDI->t_rowsperstrip) / IMTIFFBITSPERBYTE; + + ImMalloc( strip, unsigned char *, size*2 ); + + + + /* + + * If compressed, we need a extra buffer + + */ + + + + if ( imTDI->t_compression != IM_COMPRESSION_NONE ) + + ImMalloc( ucstrip, unsigned char *, size ); + + + + rows = imTDI->t_rowsperstrip; + + + + /* + + * Read in the strips and put them in the vfb one at a time + + */ + + + + for( i=0; i < imTDI->t_stripoffsets.cnt; i++ ) + + { + + /* + + * We have to know how many pixels are left to read, so we + + * keep track of the number of rows read so far. We keep + + * these numbers in uncompressed bytes even if reading a + + * compressed file. + + */ + + + + if ( rows > imTDI->t_height - rowsread ) + + rows = imTDI->t_height % imTDI->t_rowsperstrip; + + + + decodedsize = rows * ((imTDI->t_samplesperpixel * + + imTDI->t_width + (IMTIFFBITSPERBYTE - 1)) / + + IMTIFFBITSPERBYTE); + + + + where = imTiffDataItem( &imTDI->t_stripoffsets, i ); + + howMany = imTiffDataItem( &imTDI->t_stripbytecounts, i ); + + + + ImSeek( ioType, fd, fp, where, 0 ); + + + + if ( ImBinRead( ioType, fd, fp, strip, UCHAR, 1, howMany )== -1) + + { + + ImReturnBinError( ); + + } + + + + rowsread += rows; + + stripptr = strip; + + + + /* + + * If the rows are compressed, uncompress them + + */ + + + + if ( imTDI->t_compression == IM_COMPRESSION_LZW ) + + { + + if (ImLzwPreDecode( strip, howMany ) == -1) + + { + + imTiffFree(); + + ImVfbFree(vfb); + + return( -1 ); + + } + + if (ImLzwDecode( strip, ucstrip, decodedsize ) == -1) + + { + + imTiffFree(); + + ImVfbFree(vfb); + + return( -1 ); + + } + + ImLzwPostDecode(); + + + + stripptr = ucstrip; + + } + + + + if ( imTDI->t_compression == IM_COMPRESSION_PACKBITS ) + + { + + UnpackBits( strip, ucstrip, (unsigned int*)&howMany ); + + decodedsize = howMany; + + stripptr = ucstrip; + + } + + + + for ( k = 0; k < rows; k++ ) + + { + + /* + + * Added to VFB. Watch for the last byte of each + + * row. If the image width is not a multiple of 8, + + * the last byte will not be filled and we'll need + + * to skip the extra bits. + + */ + + for ( j = 0; j < imTDI->t_width; j++ ) + + { + + shift=IMTIFFBITSPERBYTE-(j % IMTIFFBITSPERBYTE)-1; + + val= (*stripptr >> shift) & 0x01; + + if (shift == 0) + + stripptr++; + + ImVfbSMono( vfb, pptr, val ); + + ImVfbSInc( vfb, pptr ); + + } + + if ( shift != 0 ) + + stripptr++; + + } + + } + + + + imTiffFree(); + + return(1); + +} + + + + + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imTiffRead4( int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable, imTiffDirInfo *imTDI, ImVfb **pVfb ) + +#else + +imTiffRead4( ioType, fd, fp, flags, tagTable, imTDI, pVfb ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flags; /* Format flags */ + + TagTable *tagTable; /* Tag table to add to */ + + imTiffDirInfo *imTDI; /* Directory information */ + + ImVfb **pVfb; /* VFB to fill and return */ + +#endif + +{ + + ImVfbPtr pptr; /* Pixel pointer */ + + ImVfb *vfb; /* A more convenient pointer */ + + unsigned char *strip; /* Storage for pixel indices */ + + unsigned char *ucstrip=NULL; /* If encoded, use this buffer */ + + unsigned char *stripptr; /* Walk thru the pixels */ + + int size; /* The number of bytes of index */ + + int i,j; /* Loop counters */ + + int where; /* Where to start reading */ + + int howMany; /* How many to read */ + + int decodedsize; /* How many bytes when decoded */ + + int rows; /* Number of scanlines this time*/ + + int rowsread=0; /* Number of scanlines readsofar*/ + + int shift; /* Number of bits to shift */ + + int val; /* Pixel value, either 0 or 255 */ + + + + /* + + * Allocate a VFB of the required size. + + */ + + if ( (*pVfb = ImVfbAlloc( imTDI->t_width, imTDI->t_height, + + IMVFBINDEX8 )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + vfb = *pVfb; + + pptr = ImVfbQFirst( vfb ); + + + + /* + + * Allocate space for the pixel values + + * Sometimes things are bigger when compresses, so we make it 2x + + */ + + + + size = (imTDI->t_width * imTDI->t_rowsperstrip) / IMTIFFNIBBLESPERBYTE; + + ImMalloc( strip, unsigned char *, size*2 ); + + + + /* + + * If compressed, we need a extra buffer + + */ + + + + if ( imTDI->t_compression != IM_COMPRESSION_NONE ) + + ImMalloc( ucstrip, unsigned char *, size ); + + + + rows = imTDI->t_rowsperstrip; + + + + /* + + * Read in the strips and put them in the vfb one at a time + + */ + + + + for( i=0; i < imTDI->t_stripoffsets.cnt; i++ ) + + { + + /* + + * We have to know how many pixels are left to read, so we + + * keep track of the number of rows read so far. We keep + + * these numbers in uncompressed bytes even if reading a + + * compressed file. + + */ + + + + if ( rows > imTDI->t_height - rowsread ) + + rows = imTDI->t_height % imTDI->t_rowsperstrip; + + + + decodedsize = (rows * imTDI->t_samplesperpixel * imTDI->t_width + + * imTDI->t_depth) / IMTIFFBITSPERBYTE; + + + + where = imTiffDataItem( &imTDI->t_stripoffsets, i ); + + howMany = imTiffDataItem( &imTDI->t_stripbytecounts, i ); + + + + ImSeek( ioType, fd, fp, where, 0 ); + + + + if ( ImBinRead( ioType, fd, fp, strip, UCHAR, 1, howMany )== -1) + + { + + ImReturnBinError( ); + + } + + + + rowsread += rows; + + stripptr = strip; + + + + /* + + * If the rows are compressed, uncompress them + + */ + + + + if ( imTDI->t_compression == IM_COMPRESSION_LZW ) + + { + + if (ImLzwPreDecode( strip, howMany ) == -1) + + { + + imTiffFree(); + + ImVfbFree(vfb); + + return( -1 ); + + } + + if (ImLzwDecode( strip, ucstrip, decodedsize ) == -1) + + { + + imTiffFree(); + + ImVfbFree(vfb); + + return( -1 ); + + } + + ImLzwPostDecode(); + + + + stripptr = ucstrip; + + } + + + + if ( imTDI->t_compression == IM_COMPRESSION_PACKBITS ) + + { + + UnpackBits( strip, ucstrip, (unsigned int*)&howMany ); + + decodedsize = howMany; + + stripptr = ucstrip; + + } + + + + + + /* + + * If there is no colormap, then put them in a gray vfb + + */ + + for( j=0; j < decodedsize*IMTIFFNIBBLESPERBYTE; j++ ) + + { + + if ( j % IMTIFFNIBBLESPERBYTE == 0 ) + + val=((*stripptr >> 4) & 0x0f); + + else + + { + + val=(*stripptr & 0x0f); + + stripptr++; + + } + + ImVfbSIndex8( vfb, pptr, val * IMTIFFNIBBLEMULT ); + + ImVfbSInc( vfb, pptr ); + + } + + } + + + + imTiffFree(); + + return(1); + +} + + + + + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imTiffRead8( int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable, imTiffDirInfo *imTDI, ImVfb **pVfb ) + +#else + +imTiffRead8( ioType, fd, fp, flags, tagTable, imTDI, pVfb ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flags; /* Format flags */ + + TagTable *tagTable; /* Tag table to add to */ + + imTiffDirInfo *imTDI; /* Directory information */ + + ImVfb **pVfb; /* VFB to fill and return */ + +#endif + +{ + + ImVfbPtr pptr; /* Pixel pointer */ + + ImVfb *vfb; /* A more convenient pointer */ + + unsigned char *strip; /* Storage for pixel indices */ + + unsigned char *ucstrip=NULL; /* If encoded, use this buffer */ + + unsigned char *stripptr; /* Walk thru the pixels */ + + int size; /* The number of bytes of index */ + + int i,j; /* Loop counters */ + + int where; /* Where to start reading */ + + int howMany; /* How many to read */ + + int decodedsize; /* How many bytes when decoded */ + + int rows; /* Number of scanlines this time*/ + + int rowsread=0; /* Number of scanlines readsofar*/ + + + + /* + + * Allocate a VFB of the required size. + + */ + + if ( (*pVfb = ImVfbAlloc( imTDI->t_width, imTDI->t_height, + + IMVFBINDEX8 )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + vfb = *pVfb; + + pptr = ImVfbQFirst( vfb ); + + + + /* + + * Allocate space for the pixel indices + + * Sometimes things are bigger when compresses, so we make it 2x + + */ + + + + size = imTDI->t_width * imTDI->t_rowsperstrip; + + ImMalloc( strip, unsigned char *, size*2 ); + + + + /* + + * If compressed, we need a extra buffer + + */ + + + + if ( imTDI->t_compression != IM_COMPRESSION_NONE ) + + ImMalloc( ucstrip, unsigned char *, size ); + + + + rows = imTDI->t_rowsperstrip; + + + + + + /* + + * Read in the strips and put them in the vfb one at a time + + */ + + + + for( i=0; i < imTDI->t_stripoffsets.cnt; i++ ) + + { + + /* + + * We have to know how many pixels are left to read, so we + + * keep track of the number of rows read so far. We keep + + * these numbers in uncompressed bytes even if reading a + + * compressed file. + + */ + + + + if ( rows > imTDI->t_height - rowsread ) + + rows = imTDI->t_height % imTDI->t_rowsperstrip; + + + + decodedsize = (rows * imTDI->t_samplesperpixel * imTDI->t_width + + * imTDI->t_depth) / IMTIFFBITSPERBYTE; + + + + where = imTiffDataItem( &imTDI->t_stripoffsets, i ); + + howMany = imTiffDataItem( &imTDI->t_stripbytecounts, i ); + + + + ImSeek( ioType, fd, fp, where, 0 ); + + + + if ( ImBinRead( ioType, fd, fp, strip, UCHAR, 1, howMany )== -1) + + { + + ImReturnBinError( ); + + } + + + + rowsread += rows; + + stripptr = strip; + + + + /* + + * If the rows are compressed, uncompress them + + */ + + + + if ( imTDI->t_compression == IM_COMPRESSION_LZW ) + + { + + if (ImLzwPreDecode( strip, howMany ) == -1) + + { + + imTiffFree(); + + ImVfbFree(vfb); + + return( -1 ); + + } + + if (ImLzwDecode( strip, ucstrip, decodedsize ) == -1) + + { + + imTiffFree(); + + ImVfbFree(vfb); + + return( -1 ); + + } + + ImLzwPostDecode(); + + + + stripptr = ucstrip; + + } + + + + if ( imTDI->t_compression == IM_COMPRESSION_PACKBITS ) + + { + + UnpackBits( strip, ucstrip, (unsigned int*)&howMany ); + + decodedsize = howMany; + + stripptr = ucstrip; + + } + + + + + + for( j=0; j < decodedsize; j++ ) + + { + + ImVfbSIndex8( vfb, pptr, *stripptr++ ); + + ImVfbSInc( vfb, pptr ); + + } + + } + + + + imTiffFree(); + + return(1); + +} + + + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imTiffRead16( int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable, imTiffDirInfo *imTDI, ImVfb **pVfb ) + +#else + +imTiffRead16( ioType, fd, fp, flags, tagTable, imTDI, pVfb ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flags; /* Format flags */ + + TagTable *tagTable; /* Tag table to add to */ + + imTiffDirInfo *imTDI; /* Directory information */ + + ImVfb **pVfb; /* VFB to fill and return */ + +#endif + +{ + + ImVfbPtr pptr; /* Pixel pointer */ + + ImVfb *vfb; /* A more convenient pointer */ + + unsigned char *strip; /* Storage for pixel indices */ + + unsigned char *ucstrip=NULL; /* If encoded, use this buffer */ + + unsigned char *stripptr; /* Walk thru the pixels */ + + unsigned char *rbp; /* Walk thru an array of uchars */ + + int size; /* The number of bytes of index */ + + int i,j; /* Loop counters */ + + unsigned int tmp; /* Temp integer holder */ + + int where; /* Where to start reading */ + + int howMany; /* How many to read */ + + int decodedsize; /* How many bytes when decoded */ + + int rows; /* Number of scanlines this time*/ + + int rowsread=0; /* Number of scanlines readsofar*/ + + + + /* + + * Allocate a VFB of the required size. + + */ + + + + if ( (*pVfb = ImVfbAlloc( imTDI->t_width, imTDI->t_height, + + IMVFBINDEX16 )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + vfb = *pVfb; + + pptr = ImVfbQFirst( vfb ); + + + + /* + + * Allocate space for the pixel indices + + * Sometimes things are bigger when compresses, so we make it 2x + + */ + + + + size = imTDI->t_width * imTDI->t_rowsperstrip * imTDI->t_depth/8; + + ImMalloc( strip, unsigned char *, size*2 ); + + + + /* + + * If compressed, we need a extra buffer + + */ + + + + if ( imTDI->t_compression != IM_COMPRESSION_NONE ) + + ImMalloc( ucstrip, unsigned char *, size ); + + + + rows = imTDI->t_rowsperstrip; + + + + /* + + * Read in the strips and put them in the vfb one at a time + + */ + + + + for( i=0; i < imTDI->t_stripoffsets.cnt; i++ ) + + { + + /* + + * We have to know how many pixels are left to read, so we + + * keep track of the number of rows read so far. We keep + + * these numbers in uncompressed bytes even if reading a + + * compressed file. + + */ + + + + if ( rows > imTDI->t_height - rowsread ) + + rows = imTDI->t_height % imTDI->t_rowsperstrip; + + + + decodedsize = (rows * imTDI->t_samplesperpixel * imTDI->t_width + + * imTDI->t_depth) / IMTIFFBITSPERBYTE; + + + + where = imTiffDataItem( &imTDI->t_stripoffsets, i ); + + howMany = imTiffDataItem( &imTDI->t_stripbytecounts, i ); + + + + ImSeek( ioType, fd, fp, where, 0 ); + + + + if ( ImBinRead( ioType, fd, fp, strip, UCHAR, 1, howMany )== -1) + + { + + ImReturnBinError( ); + + } + + + + rowsread += rows; + + stripptr = strip; + + + + /* + + * If the rows are compressed, uncompress them + + */ + + + + if ( imTDI->t_compression == IM_COMPRESSION_LZW ) + + { + + if (ImLzwPreDecode( strip, howMany ) == -1) + + { + + imTiffFree(); + + ImVfbFree(vfb); + + return( -1 ); + + } + + if (ImLzwDecode( strip, ucstrip, decodedsize ) == -1) + + { + + imTiffFree(); + + ImVfbFree(vfb); + + return( -1 ); + + } + + ImLzwPostDecode(); + + + + stripptr = ucstrip; + + } + + + + if ( imTDI->t_compression == IM_COMPRESSION_PACKBITS ) + + { + + UnpackBits( strip, ucstrip, (unsigned int*)&howMany ); + + decodedsize = howMany; + + stripptr = ucstrip; + + } + + + + /* + + * Put the values into a vfb. + + */ + + if ( imTiffHeader.tiff_magic == IMTIFFBIGENDIANMAGIC ) + + { + + /* MBF byte order. */ + + rbp = stripptr; + + for( j=0; j < decodedsize / 2; j++ ) + + { + + tmp = ((*rbp) << 8) | (*(rbp+1)); + + rbp += 2; + + ImVfbSIndex16( vfb, pptr, tmp ); + + ImVfbSInc( vfb, pptr ); + + } + + } + + else + + { + + /* LBF byte order. */ + + rbp = stripptr; + + for( j=0; j < decodedsize / 2; j++ ) + + { + + tmp = ((*(rbp+1)) << 8) | (*rbp); + + rbp += 2; + + ImVfbSIndex16( vfb, pptr, tmp ); + + ImVfbSInc( vfb, pptr ); + + } + + } + + } + + + + imTiffFree(); + + return(1); + +} + + + + + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imTiffRead24Cont( int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable, imTiffDirInfo *imTDI, ImVfb **pVfb ) + +#else + +imTiffRead24Cont( ioType, fd, fp, flags, tagTable, imTDI, pVfb ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flags; /* Format flags */ + + TagTable *tagTable; /* Tag table to add to */ + + imTiffDirInfo *imTDI; /* Directory information */ + + ImVfb **pVfb; /* VFB to fill and return */ + +#endif + +{ + + ImVfbPtr pptr; /* Pixel pointer */ + + ImVfb *vfb; /* A more convenient pointer */ + + unsigned char *strip; /* Storage for pixel indices */ + + unsigned char *ucstrip=NULL; /* If encoded, use this buffer */ + + unsigned char *stripptr; /* Walk thru the pixels */ + + int size; /* The number of bytes of index */ + + int i,j; /* Loop counters */ + + int where; /* Where to start reading */ + + int howMany; /* How many to read */ + + int decodedsize; /* How many bytes when decoded */ + + int rows; /* Number of scanlines this time*/ + + int alpha; /* Is there an alpha channel */ + + int rowsread=0; /* Number of scanlines readsofar*/ + + + + /* + + * Allocate a VFB of the required size. + + * If there is an alpha channel make room for it. + + * The location of the alpha channel is undocumented so we are assuming + + * that it is mixed in with the color channels. + + */ + + + + alpha = imTDI->t_matteing; + + + + if ( alpha ) + + { + + if ( (*pVfb = ImVfbAlloc( imTDI->t_width, imTDI->t_height, + + IMVFBRGB|IMVFBALPHA )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + size = imTDI->t_width * imTDI->t_rowsperstrip * 4; + + } + + else + + { + + if ( (*pVfb = ImVfbAlloc( imTDI->t_width, imTDI->t_height, + + IMVFBRGB )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + size = imTDI->t_width * imTDI->t_rowsperstrip * 3; + + } + + + + vfb = *pVfb; + + pptr = ImVfbQFirst( vfb ); + + + + /* + + * Allocate space for the pixel values + + * Sometimes things are bigger when compresses, so we make it 2x + + */ + + + + ImMalloc( strip, unsigned char *, size*2 ); + + + + /* + + * If compressed, we need a extra buffer + + */ + + + + if ( imTDI->t_compression != IM_COMPRESSION_NONE ) + + ImMalloc( ucstrip, unsigned char *, size ); + + + + rows = imTDI->t_rowsperstrip; + + + + /* + + * Read in the strips and put them in the vfb one at a time + + */ + + + + for( i=0; i < imTDI->t_stripoffsets.cnt; i++ ) + + { + + /* + + * We have to know how many pixels are left to read, so we + + * keep track of the number of rows read so far. We keep + + * these numbers in uncompressed bytes even if reading a + + * compressed file. + + */ + + + + if ( rows > imTDI->t_height - rowsread ) + + rows = imTDI->t_height % imTDI->t_rowsperstrip; + + + + decodedsize = (rows * imTDI->t_samplesperpixel * imTDI->t_width + + * imTDI->t_depth) / IMTIFFBITSPERBYTE; + + + + where = imTiffDataItem( &imTDI->t_stripoffsets, i ); + + howMany = imTiffDataItem( &imTDI->t_stripbytecounts, i ); + + + + ImSeek( ioType, fd, fp, where, 0 ); + + + + if ( ImBinRead( ioType, fd, fp, strip, UCHAR, 1, howMany )== -1) + + { + + ImReturnBinError( ); + + } + + + + rowsread += rows; + + stripptr = strip; + + + + /* + + * If the rows are compressed, uncompress them + + */ + + + + if ( imTDI->t_compression == IM_COMPRESSION_LZW ) + + { + + if (ImLzwPreDecode( strip, howMany ) == -1) + + { + + imTiffFree(); + + ImVfbFree(vfb); + + return( -1 ); + + } + + + + if (ImLzwDecode( strip, ucstrip, decodedsize ) == -1) + + { + + imTiffFree(); + + ImVfbFree(vfb); + + return( -1 ); + + } + + ImLzwPostDecode(); + + + + stripptr = ucstrip; + + } + + + + if ( imTDI->t_compression == IM_COMPRESSION_PACKBITS ) + + { + + UnpackBits( strip, ucstrip, (unsigned int*)&howMany ); + + decodedsize = howMany; + + stripptr = ucstrip; + + } + + + + + + /* + + * If there is no colormap, then put them in a gray vfb + + */ + + if ( alpha ) + + for( j=0; j < decodedsize/4; j++ ) + + { + + ImVfbSRed( vfb, pptr, *stripptr++ ); + + ImVfbSGreen( vfb, pptr, *stripptr++ ); + + ImVfbSBlue( vfb, pptr, *stripptr++ ); + + ImVfbSAlpha( vfb, pptr, *stripptr++ ); + + ImVfbSInc( vfb, pptr ); + + } + + else + + for( j=0; j < decodedsize/3; j++ ) + + { + + ImVfbSRed( vfb, pptr, *stripptr++ ); + + ImVfbSGreen( vfb, pptr, *stripptr++ ); + + ImVfbSBlue( vfb, pptr, *stripptr++ ); + + ImVfbSInc( vfb, pptr ); + + } + + } + + + + imTiffFree(); + + return(1); + +} + + + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imTiffRead24Sep( int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable, imTiffDirInfo *imTDI, ImVfb **pVfb ) + +#else + +imTiffRead24Sep( ioType, fd, fp, flags, tagTable, imTDI, pVfb ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flags; /* Format flags */ + + TagTable *tagTable; /* Tag table to add to */ + + imTiffDirInfo *imTDI; /* Directory information */ + + ImVfb **pVfb; /* VFB to fill and return */ + +#endif + +{ + + ImVfbPtr pptr; /* Pixel pointer */ + + ImVfb *vfb; /* A more convenient pointer */ + + unsigned char *strip; /* Storage for pixels */ + + unsigned char *ucstrip=NULL; /* If encoded, use this buffer */ + + unsigned char *stripptr; /* Walk thru the pixels red */ + + int where; /* Where to start reading */ + + int howMany; /* How many reds to read */ + + int i,j; /* Loop counters */ + + int decodedsize; /* How many bytes when decoded */ + + int alpha=0; /* Is there an alpha channel */ + + int samples; /* Samples per pixel */ + + int rows; /* Number of scanlines this time*/ + + int size; /* The number of bytes of index */ + + int rowsread; /* Number of scanlines readsofar*/ + + int color; /* Loop counter for colors */ + + int stripspercolor;/* Number of strips/component */ + + + + /* + + * Tiff files with separate image planes store all of the red rows + + * followed by all of the green rows and then all of the blue and + + * alpha rows (I think). + + */ + + + + alpha = imTDI->t_matteing; + + samples = imTDI->t_samplesperpixel; + + rows = imTDI->t_rowsperstrip; + + size = imTDI->t_width * rows; + + stripspercolor = imTDI->t_stripoffsets.cnt/samples; + + + + /* + + * Allocate a VFB of the required size. + + * If there is an alpha channel make room for it. + + * The location of the alpha channel is undocumented so we are assume + + * that it follows the blue plane + + */ + + + + if ( alpha ) + + { + + if ( (*pVfb = ImVfbAlloc( imTDI->t_width, imTDI->t_height, + + IMVFBRGB|IMVFBALPHA )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + } + + else + + { + + if ( (*pVfb = ImVfbAlloc( imTDI->t_width, imTDI->t_height, + + IMVFBRGB )) == IMVFBNULL ) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + } + + + + vfb = *pVfb; + + + + /* + + * Allocate space for one component of one row of pixels + + * Sometimes things are bigger when compresses, so we make it 2x + + */ + + + + ImMalloc( strip, unsigned char *, size*2 ); + + + + /* + + * If compressed, we need a extra buffer + + */ + + + + if ( imTDI->t_compression != IM_COMPRESSION_NONE ) + + ImMalloc( ucstrip, unsigned char *, size ); + + + + /* + + * Cycle through red, green, blue, alpha(?) and for each one + + * read in all of the scanlines (rows), decode them (?), and + + * put them into a vfb. + + */ + + + + for( color=0; color imTDI->t_height - rowsread ) + + decodedsize = imTDI->t_width * + + (imTDI->t_height % imTDI->t_rowsperstrip); + + else + + decodedsize = imTDI->t_width * rows; + + + + /* + + * Get the stripoffset and stripbytecount from the file + + */ + + where = imTiffDataItem( &imTDI->t_stripoffsets, + + (stripspercolor * color) + i ); + + howMany = imTiffDataItem( &imTDI->t_stripbytecounts, + + (stripspercolor * color) + i ); + + + + ImSeek( ioType, fd, fp, where, 0 ); + + + + if ( ImBinRead(ioType, fd, fp, strip, UCHAR, 1, + + howMany) == -1) + + { + + ImReturnBinError( ); + + } + + + + /* + + * Keep track of the number of rows because the last + + * row may not contain as many scanlines as the others + + */ + + rowsread += (decodedsize/imTDI->t_width); + + stripptr = strip; + + + + /* + + * If the rows are compressed, uncompress them + + */ + + + + if ( imTDI->t_compression == IM_COMPRESSION_LZW ) + + { + + if (ImLzwPreDecode( strip, howMany ) == -1) + + { + + imTiffFree(); + + return( -1 ); + + } + + if (ImLzwDecode(strip,ucstrip,decodedsize)== -1) + + { + + imTiffFree(); + + return( -1 ); + + } + + ImLzwPostDecode(); + + stripptr = ucstrip; + + } + + + + if ( imTDI->t_compression == IM_COMPRESSION_PACKBITS ) + + { + + UnpackBits( strip, ucstrip, (unsigned int*)&howMany ); + + decodedsize = howMany; + + stripptr = ucstrip; + + } + + + + /* + + * Put one component of one row in a vfb + + */ + + switch ( color ) + + { + + case 0: + + for( j=0; j < decodedsize; j++ ) + + { + + ImVfbSRed( vfb, pptr, *stripptr++ ); + + ImVfbSInc( vfb, pptr ); + + } + + break; + + case 1: + + for( j=0; j < decodedsize; j++ ) + + { + + ImVfbSGreen( vfb, pptr, *stripptr++ ); + + ImVfbSInc( vfb, pptr ); + + } + + break; + + case 2: + + for( j=0; j < decodedsize; j++ ) + + { + + ImVfbSBlue( vfb, pptr, *stripptr++ ); + + ImVfbSInc( vfb, pptr ); + + } + + break; + + case 3: + + for( j=0; j < decodedsize; j++ ) + + { + + ImVfbSAlpha( vfb, pptr, *stripptr++ ); + + ImVfbSInc( vfb, pptr ); + + } + + break; + + } + + + + } /* End foreach row */ + + + + } /* End foreach color */ + + + + imTiffFree(); + + return(1); + +} + + + + + +/* + + * FUNCTION + + * imTiffGetClt - Transfer a colormap into a CLT + + * + + * DESCRIPTION + + * Elements in a TIFF colormap may be bytes or shorts or whatever. + + * So we use memcpys to get them out of the colormap and into the CLT. + + */ + +static int + +#ifdef __STDC__ + +imTiffGetClt( imTiffDirInfo *imTDI, ImClt **pClt ) + +#else + +imTiffGetClt( imTDI, pClt ) + + imTiffDirInfo *imTDI; /* Directory information */ + + ImClt **pClt; /* VFB to fill and return */ + +#endif + +{ + + ImClt *clt; /* Color pointer */ + + ImCltPtr cptr; /* Color pointer */ + + int i; /* Loop counter */ + + unsigned int redp; /* Red CLT range pointer */ + + unsigned int grnp; /* Green CLT range pointer */ + + unsigned int blup; /* Blue CLT range pointer */ + + unsigned int n; /* Number of colors */ + + unsigned char red; /* Temporary storage of red val */ + + unsigned char grn; /* Temporary storage of grn val */ + + unsigned char blu; /* Temporary storage of blu val */ + + unsigned char *cd; /* Treat array as chars */ + + unsigned short *sd; /* Treat array as shorts */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * If there is a colormap in the file, put it in a CLT + + */ + + + + *pClt = IMCLTNULL; + + + + if ( imTDI->t_colormap.data != NULL ) + + { + + clt = *pClt = ImCltAlloc( imTDI->t_colormap.cnt/3 ); + + cptr = ImCltQFirst( clt ); + + + + n = imTDI->t_colormap.cnt / 3; + + redp = 0; + + grnp = n; + + blup = n+n; + + + + /* + + * Output -verbose message + + */ + + sprintf( message, "%d Entries", n ); + + ImInfo( "Color Table", message ); + + + + switch ( imTDI->t_colormap.type ) + + { + + case IMTIFFBYTE: + + case IMTIFFASCII: + + cd = (unsigned char *)imTDI->t_colormap.data; + + for ( i = 0; i < n; i++ ) + + { + + red = cd[redp++]; + + grn = cd[grnp++]; + + blu = cd[blup++]; + + ImCltSRed (cptr, red); + + ImCltSGreen (cptr, grn); + + ImCltSBlue (cptr, blu); + + ImCltSInc( clt, cptr ); + + } + + break; + + case IMTIFFSHORT: + + sd = (unsigned short *)imTDI->t_colormap.data; + + for ( i = 0; i < n; i++ ) + + { + + /* + + * Since we can only store 8-bit R, G and + + * B values, we have to use some method + + * of removing 8-bits of data from the 16-bit + + * short. This usually involves a + + * scaling function to reduce the + + * range of colors. For TIFF colortable + + * entries, all we need to do is use + + * the upper 8-bits and ignore the lower + + * 8-bits. + + */ + + red = (unsigned char) (sd[redp++] ); + + grn = (unsigned char) (sd[grnp++] ); + + blu = (unsigned char) (sd[blup++] ); + + + + ImCltSRed (cptr, red); + + ImCltSGreen (cptr, grn); + + ImCltSBlue (cptr, blu); + + ImCltSInc( clt, cptr ); + + } + + break; + + } + + } + + else + + ImInfo( "Color Table", "none" ); + + + + if ( imTDI->t_colormap.data != NULL ) + + free( imTDI->t_colormap.data ); + + + + return( 0 ); + +} + + + + + +/* + + * FUNCTION + + * imTiffWrite - Write a TIFF format file or stream + + * + + * DESCRIPTION + + * Each of these routines deal with TIFF files. + + */ + + + +static int /* Returns # of tags written */ + +#ifdef __STDC__ + +imTiffWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imTiffWrite( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + TagTable *flagsTable; /* Format flags */ + + TagTable *tagTable; /* Tag list to add to */ + +#endif + +{ + + ImClt *clt; /* CLT pointer */ + + ImVfb *vfb; /* VFB pointer */ + + char *s; /* Tag name string */ + + + + int nTag = 0; /* # of tags written */ + + int n; /* # of tag table entries */ + + int i; /* Counter */ + + int flags; /* Write flags */ + + TagEntry *tagEntry; /* Tag table entry holder */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * All administrative stuff (tags, reference counts, file offsets) + + * are in MBF byte order. + + */ + + BinByteOrder( BINMBF ); + + BinFloatFormat( BINIEEE ); + + + + ImInfo( "Version", "42" ); + + ImInfo( "Byte Order", "Most Significant Byte First" ); + + + + /* + + * Write out the file header, initialize the byte order and check + + * the magic number, check the version number. + + */ + + memset( (void *)&imTiffHeader, 0x00, sizeof( imTiffHeader ) ); + + imTiffHeader.tiff_magic = IMTIFFBIGENDIANMAGIC; + + imTiffHeader.tiff_version = IMTIFFVERSION; + + imTiffHeader.tiff_diroff = 0; /* Fill this in later */ + + + + if ( ImBinWriteStruct( ioType, fd, fp, &imTiffHeader, + + imTiffHeaderFields ) == -1 ) + + { + + ImReturnBinError( ); + + } + + + + n = TagTableQNEntry( tagTable, "image vfb" ); + + + + for ( i = 0; i < n; i++ ) + + { + + sprintf( message, "%d of %d\n", (i+1), n ); + + ImInfo( "Image", message ); + + ImInfo( "Description", "none" ); + + + + tagEntry = TagTableQDirect( tagTable, "image vfb", i ); + + s = TagEntryQTag( tagEntry ); + + TagEntryQValue( tagEntry, &vfb ); + + if ( imTiffVfbWrite( ioType, fd, fp, flagsTable, vfb )== -1) + + return ( -1 ); /* Error already posted */ + + nTag++; + + } + + + + /* + + * Write out a zero into the next-dir-offset location indicating + + * that there are no more directories + + */ + + + + n = 0; + + if (ImBinWrite( ioType, fd, fp, &n, UINT, 4, 1 ) == -1) + + { + + ImReturnBinError( ); + + } + + return( 0 ); + +} + + + + + +/* + + * FUNCTION + + * imTiffVfbWrite - write VFB to file + + * + + * DESCRIPTION + + * Write out Clt, Vfb, and then stripbyte counts, and + + * stripoffsets. Then write out the directory afterwards. + + * Seek back and fill in the directory offset. + + * This routine may be called multiple times for + + * multiple vfb/clt sets. + + */ + + + +static int /* Returns # of tags written */ + +#ifdef __STDC__ + +imTiffVfbWrite( int ioType, int fd, FILE *fp, TagTable *flagsTable, ImVfb *vfb ) + +#else + +imTiffVfbWrite( ioType, fd, fp, flagsTable, vfb ) + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + TagTable *flagsTable; /* Format flags */ + + ImVfb *vfb; /* VFB to write out */ + +#endif + +{ + + ImClt *clt; /* VFB's CLT */ + + ImCltPtr pColor; /* VFB's CLT */ + + ImVfbPtr pptr; /* VFB's pixel ptr */ + + unsigned short sh; /* For writing out a temp const */ + + unsigned short so[2]; /* Temp storage for writing */ + + int i,j; /* Loop counters */ + + int n; /* Number of colors in colormap */ + + int junk; /* A dummy value to write out */ + + int fields; /* VFB header fields */ + + int sx,x,y,d; /* Convenient dimension access */ + + int pixcnt; /* The number of bytes written */ + + int compress; /* Do lzw compression ? */ + + int cnt; /* Byte count after compression */ + + int diroffsetposition; /* Offset in file */ + + unsigned int *stripbytecountsptr; /* Walk thru byte counts */ + + unsigned int *stripbytecounts; /* Byte count array */ + + unsigned int *stripoffsetsptr; /* Byte count after compression */ + + unsigned int *stripoffsets; /* Byte offsets to pixel info */ + + int newdiroffset; /* The offset to the new direc */ + + int offset; /* Used to calc word boundaries */ + + int shift; /* Number of bits to shift */ + + int size; /* Number of bytes in offset fld*/ + + int status; /* Returned from binwrite */ + + unsigned char *buffer=NULL; /* Pixel buffer */ + + unsigned char *pbuffer=NULL; /* Pack bits pixel buffer */ + + unsigned short *cbuffer=NULL; /* Colormap buffer */ + + unsigned char *rbp; /* Walk thru the buffer as bytes*/ + + unsigned char bw; /* A value, either 255 or 0 */ + + unsigned int *rip; /* Walk thru the buffer as ints */ + + char errstr[256]; /* For printing out error stings*/ + +/* + + int xoffset; + + int yoffset; + +*/ + + double xoffset; + + double yoffset; + + + + unsigned int tmp; /* Integer holder */ + + int interRequest; /* Interleave request */ + + int compRequest; /* Compression request */ + + int cltRequest; /* CLT request */ + + int alphaRequest; /* Alpha plane request */ + + TagEntry *tagEntry; /* Flags table entry holder */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * Get the requests from the flags table. + + */ + + interRequest = IMINTERPLANE; + + compRequest = IMCOMPRLE; + + cltRequest = IMCLTYES; + + alphaRequest = IMALPHAYES; + + if ( flagsTable != TAGTABLENULL ) + + { + + tagEntry = TagTableQDirect( flagsTable, "image interleave request", 0 ); + + if ( tagEntry != TAGENTRYNULL ) + + TagEntryQValue( tagEntry, &interRequest ); + + /* interRequest is guaranteed to be one we can support. */ + + + + tagEntry = TagTableQDirect( flagsTable, "image compression request", 0 ); + + if ( tagEntry != TAGENTRYNULL ) + + TagEntryQValue( tagEntry, &compRequest ); + + /* compRequest is guaranteed to be one we can support. */ + + + + tagEntry = TagTableQDirect( flagsTable, "image clt request", 0 ); + + if ( tagEntry != TAGENTRYNULL ) + + TagEntryQValue( tagEntry, &cltRequest ); + + /* cltRequest is guaranteed to be one we can support. */ + + + + tagEntry = TagTableQDirect( flagsTable, "image alpha request", 0 ); + + if ( tagEntry != TAGENTRYNULL ) + + TagEntryQValue( tagEntry, &alphaRequest ); + + /* alphaRequest is guaranteed to be one we can support. */ + + } + + + + + + /* + + * Eventually we need to get the x & y image offsets out of the + + * the tag table + + */ + + xoffset = 0; + + yoffset = 0; + + + + /* + + * The current write position is 4 bytes past + + * where the "next-dir-offset" is going + + * to go, when we know what that is. + + */ + + diroffsetposition = ImTell( ioType, fd, fp ) - 4; + + + + + + if ( compRequest == IMCOMPLZW ) + + { + + imTiffWriteDir[IMTIFF_COMPRESSION_INDEX].tdir_offset = + + IM_COMPRESSION_LZW << 16; + + compress = IM_COMPRESSION_LZW; + + } + + else if ( compRequest == IMCOMPPB ) + + { + + imTiffWriteDir[IMTIFF_COMPRESSION_INDEX].tdir_offset = + + IM_COMPRESSION_PACKBITS << 16; + + compress = IM_COMPRESSION_PACKBITS; + + } + + else + + { + + imTiffWriteDir[IMTIFF_COMPRESSION_INDEX].tdir_offset = + + IM_COMPRESSION_NONE << 16; + + compress = IM_COMPRESSION_NONE; + + } + + + + + + /* + + * Write out the clt if there is one and we are supposed to write it + + * out. Remember where it is and such. + + */ + + clt = ImVfbQClt( vfb ); + + + + if ( clt != IMCLTNULL && cltRequest == IMCLTYES ) + + { + + imTiffWriteDir[IMTIFF_COLORMAP_INDEX].tdir_count = n + + = 3 * ImCltQNColors(clt); + + + + imTiffWriteDir[IMTIFF_COLORMAP_INDEX].tdir_offset = ImTell( ioType, fd, fp ); + + + + ImMalloc( cbuffer, unsigned short*, sizeof(ushort) * n ); + + + + pColor = ImCltQFirst( clt ); + + + + for ( i = 0; i < n/3; i++ ) + + { + + cbuffer[i] = (unsigned short) ImCltQRed( pColor ); + + cbuffer[i+n/3] = (unsigned short) ImCltQGreen( pColor ); + + cbuffer[i+n/3+n/3] = (unsigned short) ImCltQBlue( pColor ); + + ImCltSInc( clt, pColor ); + + } + + if (ImBinWrite( ioType, fd, fp, cbuffer, USHORT, 2, n ) == -1) + + { + + ImReturnBinError( ); + + } + + + + free( cbuffer ); + + } + + else + + { + + imTiffWriteDir[IMTIFF_COLORMAP_INDEX].tdir_count= 0; + + imTiffWriteDir[IMTIFF_COLORMAP_INDEX].tdir_offset = 0; + + } + + + + /* + + * Set width and height in the tiff directory + + */ + + pptr = ImVfbQFirst( vfb ); + + x = ImVfbQWidth( vfb ); + + y = ImVfbQHeight( vfb ); + + imTiffWriteDir[IMTIFF_IMAGEWIDTH_INDEX].tdir_offset = x << 16; + + imTiffWriteDir[IMTIFF_IMAGELENGTH_INDEX].tdir_offset = y << 16; + + + + sprintf( message, "%d x %d", x, y ); + + ImInfo( "Resolution", message ); + + + + /* + + * Set depth and bitspersample in the tiff directory + + */ + + + + fields = ImVfbQFields( vfb ); + + + + if ( fields & IMVFBMONO ) + + { + + d = 1; + + imTiffWriteDir[IMTIFF_BITSPERSAMPLE_INDEX].tdir_offset = 1<<16; + + imTiffWriteDir[IMTIFF_SAMPLESPERPIXEL_INDEX].tdir_offset= 1<<16; + + imTiffWriteDir[IMTIFF_PHOTOMETRIC_INDEX].tdir_offset = + + IM_PHOTOMETRIC_MINISBLACK << 16; + + } + + else if ( fields & IMVFBINDEX8 ) + + { + + d = 8; + + imTiffWriteDir[IMTIFF_BITSPERSAMPLE_INDEX].tdir_offset = 8<<16; + + imTiffWriteDir[IMTIFF_SAMPLESPERPIXEL_INDEX].tdir_offset= 1<<16; + + imTiffWriteDir[IMTIFF_PHOTOMETRIC_INDEX].tdir_offset = + + IM_PHOTOMETRIC_PALETTE << 16; + + } + + else if ( fields & IMVFBINDEX16 ) + + { + + d = 16; + + imTiffWriteDir[IMTIFF_BITSPERSAMPLE_INDEX].tdir_offset = 8<<16; + + imTiffWriteDir[IMTIFF_SAMPLESPERPIXEL_INDEX].tdir_offset= 4<<16; + + imTiffWriteDir[IMTIFF_PHOTOMETRIC_INDEX].tdir_offset = + + IM_PHOTOMETRIC_PALETTE << 16; + + } + + else if ( (fields & IMVFBRGB) && (fields & IMVFBALPHA) && alphaRequest == IMALPHAYES ) + + { + + d = 32; + + imTiffWriteDir[IMTIFF_BITSPERSAMPLE_INDEX].tdir_offset = 8<<16; + + imTiffWriteDir[IMTIFF_SAMPLESPERPIXEL_INDEX].tdir_offset= 4<<16; + + imTiffWriteDir[IMTIFF_MATTEING_INDEX].tdir_offset = 1<<16; + + imTiffWriteDir[IMTIFF_PHOTOMETRIC_INDEX].tdir_offset = + + IM_PHOTOMETRIC_RGB << 16; + + } + + else if ( fields & IMVFBRGB ) + + { + + d = 24; + + imTiffWriteDir[IMTIFF_BITSPERSAMPLE_INDEX].tdir_offset = 8<<16; + + imTiffWriteDir[IMTIFF_SAMPLESPERPIXEL_INDEX].tdir_offset= 3<<16; + + imTiffWriteDir[IMTIFF_PHOTOMETRIC_INDEX].tdir_offset = + + IM_PHOTOMETRIC_RGB << 16; + + } + + else + + { + + ImErrNo = IMEDEPTH; + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + if( d < 24 ) + + sprintf( message, "%d-bit Color Indexed", d ); + + else + + sprintf( message, "%d-bit RGB", d ); + + ImInfo( "Type", message ); + + ImInfo( "Plane Configuration", "Chunky (Contiguous)" ); + + + + if( clt != NULL ) + + sprintf( message, "%d Entries", ImCltQNColors( clt ) ); + + else + + sprintf( message, "none" ); + + ImInfo( "Color Table", message ); + + + + /* + + * Write out place holders for the stripbyte counts and + + * the stripbyteoffsets. Remember where they are and such. + + */ + + ImCalloc( stripbytecounts, unsigned int *, sizeof(uint), y ); + + + + + + stripbytecountsptr = stripbytecounts; + + imTiffWriteDir[IMTIFF_STRIPBYTECOUNTS_INDEX].tdir_count = y; + + imTiffWriteDir[IMTIFF_STRIPBYTECOUNTS_INDEX].tdir_offset = ImTell( ioType, fd, fp ); + + if (ImBinWrite( ioType, fd, fp, stripbytecounts, UINT, 4, y ) == -1) + + { + + ImReturnBinError( ); + + } + + + + + + ImCalloc( stripoffsets, unsigned int *, sizeof(uint), y ); + + stripoffsetsptr = stripoffsets; + + imTiffWriteDir[IMTIFF_STRIPOFFSETS_INDEX].tdir_count = y; + + imTiffWriteDir[IMTIFF_STRIPOFFSETS_INDEX].tdir_offset = ImTell( ioType, fd, fp ); + + if (ImBinWrite( ioType, fd, fp, stripoffsets, UINT, 4, y ) == -1) + + { + + ImReturnBinError( ); + + } + + + + /* + + * Write out x and y image offset. Always zero for now + + */ + + + + imTiffWriteDir[IMTIFF_XPOSITION_INDEX].tdir_offset = + + ImTell( ioType, fd, fp ); + +/* + + * We need to fix the binary IO package to do doubles + + * In the mean time just write out two ints. x &y offset are + + * both zero until we figure out how to put them in the tab table + + * + + */ + + if (ImBinWrite( ioType, fd, fp, &xoffset, DOUBLE, 8, 1 ) == -1) + + { + + ImReturnBinError( ); + + } + + + +/* + + if (ImBinWrite( ioType, fd, fp, &xoffset, INT, 4, 1 ) == -1) + + { + + ImReturnBinError( ); + + } + + if (ImBinWrite( ioType, fd, fp, &xoffset, INT, 4, 1 ) == -1) + + { + + ImReturnBinError( ); + + } + +*/ + + + + imTiffWriteDir[IMTIFF_YPOSITION_INDEX].tdir_offset = + + ImTell( ioType, fd, fp ); + + + +/* + + if (ImBinWrite( ioType, fd, fp, &yoffset, INT, 4, 1 ) == -1) + + { + + ImReturnBinError( ); + + } + + if (ImBinWrite( ioType, fd, fp, &yoffset, INT, 4, 1 ) == -1) + + { + + ImReturnBinError( ); + + } + +*/ + + if (ImBinWrite( ioType, fd, fp, &yoffset, DOUBLE, 8, 1 ) == -1) + + { + + ImReturnBinError( ); + + } + + + + /* + + * Get ready to write out some pixel values + + */ + + if ( d == 1 ) + + cnt = sx = (x + (IMTIFFBITSPERBYTE-1)) / IMTIFFBITSPERBYTE; + + else + + cnt = sx = x * d / IMTIFFBITSPERBYTE; + + + + ImMalloc( buffer, unsigned char *, sx ); + + + + if ( compress != IM_COMPRESSION_NONE ) + + ImMalloc( pbuffer, unsigned char *, sx+1 ); + + + + switch( compress ) + + { + + case IM_COMPRESSION_LZW: + + ImInfo("Compression Type","Lempel-Ziv and Welch (LZW)"); + + break; + + case IM_COMPRESSION_PACKBITS: + + ImInfo("Compression Type","Apple Macintosh Packbits" ); + + break; + + case IM_COMPRESSION_NONE: + + ImInfo( "Compression Type", "none" ); + + break; + + } + + if ( (fields & IMVFBRGB) && (fields & IMVFBALPHA) && alphaRequest == IMALPHAYES ) + + { + + ImInfo( "Alpha Channel", "8-bit\n" ); + + } + + else + + ImInfo( "Alpha Channel", "none\n" ); + + + + + + /* + + * For every scanline + + */ + + for( j=0; j>8) & 0xFF; + + *rbp++ = tmp & 0xFF; + + } + + break; + + + + default: + + ImErrNo = IMEDEPTH; + + sprintf( errstr,"Can't write %d bit image files",d); + + ImErrorFatal( errstr, -1, ImErrNo ); + + } + + + + /* + + * Only write scanlines starting on word boundaries + + */ + + offset = ImTell( ioType, fd, fp ); + + offset += 4 - (offset % 4); + + ImSeek( ioType, fd, fp, offset, 0 ); + + + + if ( compress == IM_COMPRESSION_LZW ) + + { + + ImLzwPreEncode( sx ); + + + + if ( ImLzwEncode( ioType, fd, fp, buffer, sx ) == -1 ) + + return( -1 ); /* ImErrNo already set */ + + + + if ( ( cnt = ImLzwPostEncode( ioType, fd, fp )) == -1 ) + + return( -1 ); /* ImErrNo already set */ + + + + } + + else if ( compress == IM_COMPRESSION_PACKBITS ) + + { + + cnt = sx; + + PackBits( buffer, pbuffer, (unsigned int*)&cnt ); + + + + if (ImBinWrite( ioType, fd, fp, pbuffer, + + UCHAR, 1, cnt ) == -1) + + { + + ImReturnBinError( ); + + } + + } + + else + + { + + if (ImBinWrite(ioType,fd,fp,buffer, UCHAR, 1, cnt)== -1) + + { + + ImReturnBinError( ); + + } + + } + + + + *stripbytecountsptr++ = cnt; + + *stripoffsetsptr++ = offset; + + } + + + +#ifdef DEBUG + + imPrintTiffWriteDir( "After", stripbytecounts, stripoffsets ); + +#endif + + + + if ( compress == IM_COMPRESSION_LZW ) + + ImLzwCleanup( ); + + + + /* + + * Seek back and write out the offset of the new direntry + + */ + + newdiroffset = ImTell( ioType, fd, fp ); + + newdiroffset += 4 - (newdiroffset % 4); + + + + ImSeek( ioType, fd, fp, diroffsetposition, 0 ); + + if (ImBinWrite( ioType, fd, fp, &newdiroffset, UINT, 4, 1 ) == -1) + + { + + ImReturnBinError( ); + + } + + + + + + /* + + * Seek to the stripoffset location and write out some real values + + */ + + ImSeek( ioType, fd, fp, + + imTiffWriteDir[IMTIFF_STRIPOFFSETS_INDEX].tdir_offset, 0 ); + + if (ImBinWrite( ioType, fd, fp, stripoffsets, UINT, 4, y ) == -1) + + { + + ImReturnBinError( ); + + } + + + + + + /* + + * Seek to the stripbytecounts location and write out some real values + + */ + + ImSeek( ioType, fd, fp, + + imTiffWriteDir[IMTIFF_STRIPBYTECOUNTS_INDEX].tdir_offset, 0 ); + + if (ImBinWrite( ioType, fd, fp, stripbytecounts, UINT, 4, y ) == -1) + + { + + ImReturnBinError( ); + + } + + + + + + /* + + * Seek to the new dir location and write out the dir entries + + */ + + ImSeek( ioType, fd, fp, newdiroffset, 0 ); + + + + if ( cltRequest == IMCLTNO || clt == IMCLTNULL ) + + sh = IMTIFFWRITEDIRENTRIES - 1; + + else + + sh = IMTIFFWRITEDIRENTRIES; + + + + if (ImBinWrite( ioType, fd, fp, &sh, USHORT, 2, 1 ) == -1) + + { + + ImReturnBinError( ); + + } + + + + /* + + * Write out all of the directory entries + + */ + + for( i=0; i 4 ) + + { + + if ( ImBinWrite( ioType, fd, fp, + + &(imTiffWriteDir[i].tdir_offset), INT, 4, 1) == -1 ) + + ImReturnBinError(); + + continue; + + } + + + + /* + + * If it is not an offset, then there is data in the + + * offset field. We have to read it as the correct type + + */ + + switch( imTiffWriteDir[i].tdir_type ) + + { + + case IMTIFFBYTE: + + case IMTIFFASCII: + + status = ImBinWrite( ioType, fd, fp, + + &(imTiffWriteDir[i].tdir_offset), UCHAR, 1, 4); + + break; + + + + case IMTIFFSHORT: + + so[0]=(imTiffWriteDir[i].tdir_offset & 0xffff0000) >>16; + + so[1]=imTiffWriteDir[i].tdir_offset & 0xffff; + + status = ImBinWrite( ioType, fd, fp, so, SHORT, 2, 2); + + break; + + + + case IMTIFFLONG: + + status = ImBinWrite( ioType, fd, fp, + + &(imTiffWriteDir[i].tdir_offset), INT, 4, 1); + + break; + + } + + + + if ( status == -1 ) + + ImReturnBinError( ); + + + + } + + + + /* + + * The file pointer is currently positioned at the place where the + + * "offset-to-next-directory" value goes. The next call to this + + * routine (imTiffVfbWrite()) calls ImTell() to get this offset. + + */ + + free( buffer ); + + if ( pbuffer ) + + free( pbuffer ); + + return( 0 ); + +} + + + + + +#else /* from #ifndef USE_TIFF_LIB */ + + + +/* + + * + + * Code to use the tiff library routines starts here. + + * + + */ + + + + + +/* + + * FUNCTION + + * imTiffRead + + * + + * DESCRIPTION + + * Read in a tiff file using the routines in the tiff library. + + * + + */ + + + +static int /* Returns status */ + +#ifdef __STDC__ + +imTiffRead( int ioType, int fd, FILE *fp, TagTable *flags, TagTable *tagTable ) + +#else + +imTiffRead( ioType, fd, fp, flags, tagTable ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flags; /* Format flags */ + + TagTable *tagTable; /* Tag table to add to */ + +#endif + +{ + + TIFF* tif; /* Structure for manipulating TIFF image. */ + + ImVfb* vfb; /* Vfb we will be creating. */ + + uint32 xSize, ySize; /* Image Size (in bytes) for allocating vfb */ + + int fieldMask; /* Fields in the Vfb. */ + + uint16 numchans; /* number of channels */ + + uint16 chandepth; /* number of bits per channel */ + + uint16 interleave_method; /* method of interleaving. */ + + uint16 photometric; /* indicates color index / rgb / b&w */ + + ImClt* cltBuf; /* Color lookup table */ + + uint16 byteOrder; /* byte order in file (just for ImInfo) */ + + uint16 compression; /* compression method (just for ImInfo) */ + + char* filename; /* filename */ + + char message[500]; /* message buffer */ + + int curImage; /* Index of current image in the file */ + + int moreImages; /* Flag for the while loop */ + + TagEntry* entry; /* Entry to get file name */ + + + + /* + + * We need a file descriptor. So, if we have a + + * file pointer, get a file descriptor + + * for it. + + */ + + + + if (ioType & IMFILEIOFP) + + { + + fd = fileno(fp); + + fflush(fp); + + rewind(fp); + + } + + + + + + /* + + * If we can't discern the filename, use 'libtiff'. This way, error messages + + * will say things like 'libtiff: Can't open file'. + + */ + + + + if (!flags || TagTableQNEntry (flags, "file name") == 0) + + { + + filename = NULL; + + } + + else + + { + + entry = TagTableQDirect ( flags, "file name", 0 ); + + if (TagEntryQValue( entry, &filename) == -1) + + filename = NULL; + + } + + + + + + tif = TIFFFdOpen( fd, filename ? filename : "libtiff", "rb"); + + if (tif == IMTIFFNULL) + + { + + ImErrorFatal( "Error opening file.", -1, IMENOFILE); + + } + + + + + + moreImages = 1; + + curImage = 0; + + + + /* Loop through the images in the file */ + + + + while (moreImages) + + { + + curImage++; + + sprintf( message, "%d", curImage ); + + ImInfo( "Image", message ); + + + + /* + + * Figure out how big the image is and what fields it contains. + + * Then allocate our vfb accordingly. + + */ + + + + /* Get the image size */ + + + + TIFFGetField( tif, TIFFTAG_IMAGEWIDTH, &xSize); + + TIFFGetField( tif, TIFFTAG_IMAGELENGTH, &ySize); + + sprintf(message,"%d x %d",xSize,ySize); + + ImInfo( "Resolution", message); + + + + /* Get the image type */ + + + + TIFFGetField( tif, TIFFTAG_PHOTOMETRIC, &photometric); + + TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &chandepth); + + TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &numchans); + + + + /* + + * Translate tiff's idea of an image type into the image tools' + + * idea of an image type + + */ + + + + fieldMask = 0; + + switch (numchans) + + { + + case 1: /* Greyscale or color-indexed */ + + if (chandepth<=8) + + { + + sprintf(message,"%d-bit %s", chandepth, + + photometric==PHOTOMETRIC_PALETTE ? "Color Indexed" : "Greyscale" ); + + fieldMask = IMVFBINDEX8; + + ImInfo ( "Type", message); + + if (photometric==PHOTOMETRIC_MINISWHITE) + + ImInfo("Greyscale Mode", "Minimum is white"); + + if (photometric==PHOTOMETRIC_MINISBLACK) + + ImInfo("Greyscale Mode", "Minimum is black"); + + } + + else + + if (chandepth==16) + + { + + fieldMask = IMVFBINDEX16; + + ImInfo ("Type", "16-bit Color indexed"); + + } + + break; + + + + case 3: /* RGB with no alpha */ + + fieldMask = IMVFBRGB; + + sprintf(message,"%d-bit RGB", chandepth * numchans); + + ImInfo ("Type", message); + + break; + + + + case 4: /* RGB with alpha */ + + fieldMask = IMVFBRGB | IMVFBALPHA; + + break; + + default: break; + + } /* End of switch */ + + + + if (fieldMask==0) + + { + + sprintf(message,"Cannot handle images with %d channels, and depth %d", + + numchans, chandepth); + + ImErrorFatal( message, -1, IMEUNSUPPORTED); + + } + + + + /* + + * No support for the following. + + */ + + + + if (photometric==PHOTOMETRIC_YCBCR) + + { + + ImErrorFatal( "No support for YCBCR photometric interepretation",-1, + + IMEUNSUPPORTED); + + } + + + + /* Allocate our vfb */ + + + + vfb = ImVfbAlloc( xSize, ySize, fieldMask ); + + if ( (vfb = ImVfbAlloc( xSize, ySize, fieldMask )) == IMVFBNULL) + + { + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + } + + + + /* Discern the byte order for ImInfo message */ + + + + TIFFGetFieldDefaulted(tif, TIFFTAG_FILLORDER, &byteOrder); + + + + if (byteOrder==FILLORDER_MSB2LSB) + + ImInfo( "Byte Order", "Most Significant Byte First") + + else + + ImInfo( "Byte Order", "Least Significant Byte First") + + + + /* Discern the compression scheme */ + + + + TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &compression); + + + + switch (compression) + + { + + case COMPRESSION_NONE: + + ImInfo ("Compression Type", "none"); + + break; + + case COMPRESSION_CCITTRLE: + + ImInfo ("Compression Type", "CCITT modified Huffman encoding"); + + break; + + case COMPRESSION_CCITTFAX3: + + ImInfo ("Compression Type", "CCITT Group 3 facsimile encoding"); + + break; + + case COMPRESSION_CCITTFAX4: + + ImInfo ("Compression Type", "CCITT Group 4 facsimile encoding"); + + break; + + case COMPRESSION_CCITTRLEW: + + ImInfo ("Compression Type", "CCITT modified Huffman encoding w/ word alignment"); + + break; + + case COMPRESSION_PACKBITS: + + ImInfo ("Compression Type", "Macintosh PackBits encoding"); + + break; + + case COMPRESSION_THUNDERSCAN: + + ImInfo ("Compression Type", "ThunderScan 4-bit encoding"); + + break; + + case COMPRESSION_LZW: + + ImInfo ("Compression Type", "Lempel-Ziv & Welch encoding"); + + break; + + case COMPRESSION_NEXT: + + ImInfo ("Compression Type", "NeXT 2-bit encoding"); + + break; + + case COMPRESSION_JPEG: + + ImInfo ("Compression Type", "JPEG encoding"); + + break; + + default: + + ImInfo ("Compression Type", "Unknown"); + + break; + + } + + + + /* + + * + + * Determine the method of interleaving, and grouping. + + * Then call the appropriate subroutine to decode the pixel + + * data. + + * + + */ + + + + if (TIFFIsTiled(tif)) + + ImInfo( "Grouping Method", "Tiles" ) + + else + + ImInfo( "Grouping Method", "Scanlines" ) + + + + TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &interleave_method); + + + + switch (interleave_method) + + { + + case PLANARCONFIG_CONTIG : /* contiguous (noninterleaved) */ + + + + ImInfo( "Interleave Method", "Non-interleaved" ); + + if (TIFFIsTiled(tif)) + + imTiffReadNonInterleavedTiles (tif, vfb); + + else + + imTiffReadNonInterleavedScanlines( tif, vfb); + + break; + + + + case PLANARCONFIG_SEPARATE : /* plane interleaved */ + + + + ImInfo( "Interleave Method", "Plane Interleaved" ); + + if (TIFFIsTiled (tif)) + + imTiffReadPlaneInterleavedTiles ( tif, vfb); + + else + + imTiffReadPlaneInterleavedScanlines ( tif, vfb); + + break; + + + + default : + + ImErrorFatal( "Unknown interleave method.",-1, IMEUNSUPPORTED); + + } + + + + /* + + * If there is a clt, read it in. + + */ + + + + if (photometric == PHOTOMETRIC_PALETTE) + + { + + sprintf(message,"%d-bit", chandepth); + + ImInfo ("Color Table", message ); + + if (imTiffReadClt(tif, &cltBuf) != 0) + + { + + ImVfbSClt (vfb, cltBuf); + + TagTableAppend( tagTable, + + TagEntryAlloc( "image clt", POINTER, &cltBuf ) ); + + } + + /* else error is reported by imTiffReadClt */ + + } + + else + + ImInfo ("Color Table", "none"); + + + + /* + + * Append the vfb to the tagtable + + */ + + + + TagTableAppend( tagTable, + + TagEntryAlloc( "image vfb", POINTER, &vfb ) ); + + + + moreImages = TIFFReadDirectory(tif); + + } /* End of while moreimages */ + + + + TIFFClose( tif ); + + + + return curImage; + +} + + + + + +/* + + * Macro to scale value to be between 0 and 255 + + */ + + + +#define IM_SCALE_0_255(currentValue,chandepth) ( ((currentValue) * 255) / ( (1L << (chandepth)) - 1)) + + + + + + + +/* + + * + + * FUNCTION + + * imTiffReadNonInterleavedScanlines + + * + + * DESCRIPTION + + * Read data into a vfb, from a TIFF structure. + + * The data is stored in the form RGBRGBRGB.... + + * The tiff library calls this "contiguous". We + + * call it "non-interleaved". + + * + + * RETURN VALUE + + * 1 if everything went smoothly. + + * 0 otherwise. + + * + + */ + + + +static int + +#ifdef __STDC__ + +imTiffReadNonInterleavedScanlines( TIFF* tif, ImVfb* vfb) + +#else + +imTiffReadNonInterleavedScanlines( tif, vfb) + +TIFF* tif; /* incoming data */ + +ImVfb* vfb; /* outgoing vfb */ + +#endif + +{ + + unsigned char* linebuf; /* buffer for one scanline */ + + uint16* linebuf16; /* the line as an array of 16-bit values */ + + tsize_t scanlineSize; /* size of scanlines */ + + ImVfbPtr vfbptr; /* ptr into a vfb */ + + uint32 row, col; /* Loop indices */ + + uint32 ySize; /* Image size (y) */ + + uint16 numchans; /* number of channels */ + + uint16 chandepth; /* number of bits per channel */ + + uint16 mask; /* mask to read pixels from the array */ + + uint16 startmask; /* start with this mask */ + + uint16 currentValue; /* value of current channel of current pixel */ + + int sampleNum; /* Index of the current value in the scanline */ + + int samplesPerScanline; /* number of samples per scanline. */ + + int chanIndex; /* which channel is being set */ + + /* (index in the chanList array) */ + + int index16; /* index within 16-bit array */ + + int chanList[5]; /* List of channels to cycle through */ + + uint16 photometric; /* Tells us the type of image this is. */ + + int currentShift; /* How many bits to shift this sample */ + + char message[200]; /* Buffer for error messages */ + + int isGreyscale; /* flag indicating greyscale image (vs clt) */ + + uint16 compression; /* Compression type */ + + int byteOrder; /* indicates byte order */ + + + + /* Get some important fields from the tiff structure. */ + + + + TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &chandepth); + + TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &numchans); + + TIFFGetField( tif, TIFFTAG_IMAGELENGTH, &ySize); + + TIFFGetField( tif, TIFFTAG_PHOTOMETRIC, &photometric); + + TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &compression); + + scanlineSize = TIFFScanlineSize(tif); + + samplesPerScanline = scanlineSize * 8 / chandepth; + + byteOrder = BinQMachine()->bin_byteOrder; + + + + /* + + * According to page 17 of the TIFF 6.0 specification, grayscale images that + + * are compressed with certain compression schemes should be reversed. + + */ + + + + if ( (compression==COMPRESSION_CCITTRLE) || + + (compression==COMPRESSION_CCITTFAX3) || + + (compression==COMPRESSION_CCITTRLEW) || + + (compression==COMPRESSION_CCITTFAX4) ) + + { + + /* Switch the photometric if it is a greyscale image */ + + if (photometric==PHOTOMETRIC_MINISBLACK) + + photometric = PHOTOMETRIC_MINISWHITE; + + else if (photometric==PHOTOMETRIC_MINISWHITE) + + photometric = PHOTOMETRIC_MINISBLACK; + + } + + + + /* + + * Here is what's going on here: + + * + + * The scanlines are read one at a time. + + * + + * For each scanline... + + * + + * We need to extract values from the scanline that are not necessarily + + * 8-bits (in particular, that are 1,2,4,8 or 16 bits). + + * + + * To do this, we cast the array of values into an array of 16-bit values, + + * and then use a bit-mask, in conjunction with an array index, to traverse + + * the array. + + * + + * i.e. + + * array: +----+----+----+----+ ... + + * | | | | | + + * +----+----+----+----+ ... + + * mask: 1100 + + * index16: 0 + + * + + * when index16 is 0, and the mask masks the upper 4 bits of a uint16, then + + * we are looking at the value in the first 4 bits of the first array element. + + * + + * When index16 is 1, we are looking at the first 4 bits of the second array element. + + * et cetera + + * + + * This allows us to extract values from the scanline. Then, for each value, we + + * determine which channel we are on by using an array containing the channels through + + * which we are looping. + + * + + * This will allow us to read a scanline of the form + + * RGBRGBRGBRGB + + * + + * where each R is only 2 bits long. + + * (and each G is only 2 bits long and each B is only 2 bits long) + + */ + + + + /* Set up the original mask (Mask the leftmost chandepth bits) */ + + + + mask = (uint16) 0; + + switch (chandepth) + + { + + case 16 : mask = mask | (1<<0); + + mask = mask | (1<<1); + + mask = mask | (1<<2); + + mask = mask | (1<<3); + + mask = mask | (1<<4); + + mask = mask | (1<<5); + + mask = mask | (1<<6); + + mask = mask | (1<<7); /* fall through */ + + case 8 : mask = mask | (1<<8); + + mask = mask | (1<<9); + + mask = mask | (1<<10); + + mask = mask | (1<<11); /* fall through */ + + case 4 : mask = mask | (1<<12); + + mask = mask | (1<<13); /* fall through */ + + case 2 : mask = mask | (1<<14); /* fall through */ + + case 1 : mask = mask | (1<<15); + + break; + + default : + + sprintf(message,"Unsupported channel depth. No support for %d bits per sample",chandepth); + + ImErrorWarning(message,0,IMEUNSUPPORTED); + + return 0; + + } + + startmask = mask; + + + + /* + + * Set up the list of channels through which we are going to cycle. + + */ + + + + switch (numchans) + + { + + case 1 : + + /* + + * Determine whether this is a greyscale image or a color + + * indexed image. (So that we know whether or not to scale + + * the incoming value to be between 0 and 255. + + */ + + + + if (photometric!=PHOTOMETRIC_PALETTE) + + isGreyscale = 1; + + else + + isGreyscale = 0; + + if (chandepth==16) + + chanList[0] = IMVFBINDEX16; + + else + + chanList[0] = IMVFBINDEX8; + + break; + + case 3 : + + chanList[0] = IMVFBRED; + + chanList[1] = IMVFBGREEN; + + chanList[2] = IMVFBBLUE; + + break; + + case 4 : + + chanList[0] = IMVFBRED; + + chanList[1] = IMVFBGREEN; + + chanList[2] = IMVFBBLUE; + + chanList[3] = IMVFBALPHA; + + break; + + default : + + sprintf(message,"Unsupported channel depth (bits per sample) : %d",chandepth); + + ImErrorWarning(message,0,IMEUNSUPPORTED); + + } + + + + /* + + * currentShift tells us how many bits to shift the value + + * to the right, after we get it from the array. + + * i.e. after using mask to collect the upper 2 bits of + + * a uint16, we need to shift the value 14 bits to the right + + * to get our final value. + + */ + + currentShift = 16 - chandepth; + + + + /* Allocate space and cast our array into a 16-bit one. */ + + + + ImMalloc(linebuf,unsigned char *,scanlineSize); + + linebuf16 = (uint16 *) linebuf; + + + + for (row=0; row< ySize ; row++) + + { + + /* Read a scanline */ + + + + if (TIFFReadScanline(tif, linebuf, row, 0) < 0) + + break; + + if (byteOrder==BINLBF) + + imTiffSwapBytes(linebuf, samplesPerScanline); + + + + + + /* + + * mask and index16 start at the upper chandepth + + * bits of the first array element + + */ + + + + index16 = 0; + + mask = startmask; + + + + /* + + * start with the first channel (i.e. red for RGB, + + * INDEX8 for indexed images ... + + */ + + + + chanIndex = 0; + + + + /* Keep track of how many samples we've traversed (so far 0) */ + + + + sampleNum = 0; + + + + /* currentShift's function is explained above */ + + + + currentShift = 16 - chandepth; + + + + /* move to the beginning of the row */ + + + + vfbptr = ImVfbQPtr(vfb, 0, row); + + + + while (sampleNum < samplesPerScanline) + + { + + /* + + * index16 is the index relative to the 16-bit array. + + * mask is the mask within the current array element + + * (i.e. the index relative to the current 16-bit value) + + * sampleNum is the index of the sample in the scanline. + + * + + */ + + + + currentValue = (mask & *(linebuf16 + index16)); + + currentValue = currentValue >> currentShift; + + currentShift -= chandepth; + + if (currentShift < 0) + + currentShift = 16 - chandepth; + + + + /* Is 0 black or white? */ + + if (photometric == PHOTOMETRIC_MINISWHITE) + + { + + currentValue = ~currentValue; + + /* Knock off upper (16 - chandepth) bits */ + + currentValue = currentValue << (16 - chandepth); + + currentValue = currentValue >> (16 - chandepth); + + } + + + + + + switch (chanList[chanIndex]) + + { + + case IMVFBINDEX8 : /* same as IMVFBGREY */ + + if (isGreyscale) + + ImVfbSGrey(vfb, vfbptr, IM_SCALE_0_255(currentValue,chandepth)); + + else + + ImVfbSIndex8 (vfb, vfbptr, currentValue); + + break; + + case IMVFBRED : + + ImVfbSRed (vfb, vfbptr, IM_SCALE_0_255(currentValue,chandepth) ); + + break; + + case IMVFBGREEN : + + ImVfbSGreen(vfb, vfbptr, IM_SCALE_0_255(currentValue,chandepth) ); + + break; + + case IMVFBBLUE : + + ImVfbSBlue (vfb, vfbptr, IM_SCALE_0_255(currentValue,chandepth) ); + + break; + + case IMVFBALPHA : + + ImVfbSAlpha (vfb, vfbptr, IM_SCALE_0_255(currentValue,chandepth) ); + + break; + + case IMVFBINDEX16 : + + ImVfbSIndex16 (vfb, vfbptr, currentValue ); + + break; + + } + + + + /* + + * Increment: the channel index, the sample number, + + * the mask, possibly the vfb pointer, and possibly + + * the index in the scanline array. + + */ + + + + chanIndex++; + + if (chanIndex > (numchans - 1)) + + { + + ImVfbSNext (vfb, vfbptr); + + chanIndex = 0; + + } + + sampleNum++; + + + + /* Shift mask and possibly index16 */ + + mask = mask>>chandepth; + + if (mask == 0) + + { + + mask = startmask; + + index16++; + + } + + } + + } + + free ((unsigned char *) linebuf); + + + + return 1; + +} + + + + + + + + + + + +/* + + * + + * FUNCTION + + * imTiffReadPlaneInterleavedScanlines + + * + + * DESCRIPTION + + * Read data into a vfb, from a TIFF structure. + + * The data is stored in the form RRR...GGG...BBB....B + + * The tiff library calls this "seperate". We + + * call it "plane-interleaved". + + * + + */ + + + +static int /* returns 1 for success, 0 for failure */ + +#ifdef __STDC__ + +imTiffReadPlaneInterleavedScanlines( TIFF* tif, ImVfb* vfb) + +#else + +imTiffReadPlaneInterleavedScanlines( tif, vfb) + +TIFF* tif; /* incoming data */ + +ImVfb* vfb; /* outgoing vfb */ + +#endif + +{ + + uint16 numchans; /* number of channels */ + + uint16 chandepth; /* Depth of each channel */ + + char message[200]; /* Buffer for messages */ + + + + /* + + * Get the number of channels and channel depth from tiff + + */ + + + + TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &numchans); + + TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &chandepth); + + + + /* + + * Go through the vfb once for each channel, and + + * read the appropriate plane from the TIFF structure. + + */ + + + + switch (numchans) + + { + + case 1: + + /* + + * Being plane interleaved and having only one + + * plane is silly. It's the same as being non-interleaved. + + * Nevertheless, tiff's flags can indicate such a thing + + * occurring. + + * So, let's just call the non-interleaved function. + + * (which handles varying channel depths.) + + */ + + + + imTiffReadNonInterleavedScanlines(tif, vfb); + + break; + + case 3: + + if (chandepth!=8) + + { + + sprintf(message,"Can't read %d bits per channel plane-interleaved images\n",chandepth); + + ImErrorFatal(message,0,IMEUNSUPPORTED); + + } + + imTiffReadRedScanlinePlane(tif, vfb); + + imTiffReadGreenScanlinePlane(tif, vfb); + + imTiffReadBlueScanlinePlane(tif, vfb); + + break; + + case 4: + + if (chandepth!=8) + + { + + sprintf(message,"Can't read %d bits per channel plane-interleaved images\n",chandepth); + + ImErrorFatal(message,0,IMEUNSUPPORTED); + + } + + imTiffReadRedScanlinePlane(tif, vfb); + + imTiffReadGreenScanlinePlane(tif, vfb); + + imTiffReadBlueScanlinePlane(tif, vfb); + + imTiffReadAlphaScanlinePlane(tif, vfb); + + break; + + default : + + sprintf(message,"Can't read %d channels plane-interleaved\n",numchans); + + ImErrorFatal(message,0,IMEUNSUPPORTED); + + } + + + + return 1; + +} + + + +/* + + * + + * FUNCTION + + * imTiffReadClt + + * + + * DESCRIPTION + + * Allocate space for, and then read a clt from a tiff file. + + * + + */ + + + +static int /* returns 1 for success, 0 for failure */ + +#ifdef __STDC__ + +imTiffReadClt(TIFF* tif, ImClt **cltBuf) + +#else + +imTiffReadClt(tif, cltBuf) + +TIFF* tif; /* TIFF structure */ + +ImClt **cltBuf; /* buffer for new clt */ + +#endif + +{ + + int nClt; /* Number of entries in the clt */ + + uint16 *redmap, /* Red color table entry components */ + + *greenmap, /* Green color table entry components */ + + *bluemap; /* Blue color table entry components */ + + uint16 chandepth; /* Depth of each channel */ + + ImCltPtr cltptr; /* Pointer into a clt */ + + int i; /* loop index */ + + + + /* + + * Read in the table + + */ + + + + if (!TIFFGetField(tif, TIFFTAG_COLORMAP, &redmap, &greenmap, &bluemap)) + + { + + ImErrorFatal("No color table for image",0,IMENOCLT); + + } + + + + /* Compute number of entries in the clt. */ + + + + TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &chandepth); + + nClt = 1<bin_byteOrder; + + + + /* + + * According to page 17 of the TIFF 6.0 specification, grayscale images that + + * are compressed with certain compression schemes should be reversed. + + * + + */ + + if ( (compression==COMPRESSION_CCITTRLE) || + + (compression==COMPRESSION_CCITTFAX3) || + + (compression==COMPRESSION_CCITTRLEW) || + + (compression==COMPRESSION_CCITTFAX4) ) + + { + + /* Switch the photometric if it is a greyscale image */ + + if (photometric==PHOTOMETRIC_MINISBLACK) + + photometric = PHOTOMETRIC_MINISWHITE; + + else if (photometric==PHOTOMETRIC_MINISWHITE) + + photometric = PHOTOMETRIC_MINISBLACK; + + } + + + + /* + + * Here is what's going on here: + + * + + * The tiles are read one at a time. + + * + + * For each tile... + + * + + * We need to extract values from the tile that are not necessarily + + * 8-bits (in particular, that are 1,2,4,8 or 16 bits). + + * + + * To do this, we cast the array of values into an array of 16-bit values, + + * and then use a bit-mask, in conjunction with an array index, to traverse + + * the array. + + * + + * i.e. + + * array: +----+----+----+----+ ... + + * | | | | | + + * +----+----+----+----+ ... + + * mask: 1100 + + * index16: 0 + + * + + * when index16 is 0, and the mask masks the upper 4 bits of a uint16, then + + * we are looking at the value in the first 4 bits of the first array element. + + * + + * When index16 is 1, we are looking at the first 4 bits of the second array element. + + * et cetera + + * + + * This allows us to extract values from the tile. Then, for each value, we + + * determine which channel we are on by using an array containing the channels through + + * which we are looping. + + * + + * This will allow us to read a tile of the form + + * RGBRGBRGBRGB + + * + + * where each R is only 2 bits long. + + * (and each G is only 2 bits long and each B is only 2 bits long) + + * + + * After reading a value, the correct channel for the vfbptr is set, the vfbptr + + * from from left to right, and top to bottom along each tile. + + */ + + + + /* + + * Note also that if we read a tile that is x by y, then there + + * will always be x*y*number-of-channes pixels in the buffer. + + * This means that if a tile goes beyond the boundaries of a + + * vfb, then it will be padded with junk. + + */ + + + + /* Set up the original mask (Mask the leftmost chandepth bits) */ + + + + mask = (uint16) 0; + + switch (chandepth) + + { + + case 16 : mask = mask | (1<<0); + + mask = mask | (1<<1); + + mask = mask | (1<<2); + + mask = mask | (1<<3); + + mask = mask | (1<<4); + + mask = mask | (1<<5); + + mask = mask | (1<<6); + + mask = mask | (1<<7); /* fall through */ + + case 8 : mask = mask | (1<<8); + + mask = mask | (1<<9); + + mask = mask | (1<<10); + + mask = mask | (1<<11); /* fall through */ + + case 4 : mask = mask | (1<<12); + + mask = mask | (1<<13); /* fall through */ + + case 2 : mask = mask | (1<<14); /* fall through */ + + case 1 : mask = mask | (1<<15); + + break; + + default : + + sprintf(message,"Unsupported channel depth (bits per sample) : %d",chandepth); + + ImErrorWarning(message,0,IMEUNSUPPORTED); + + } + + startmask = mask; + + + + /* + + * Set up the list of channels through which we are going to cycle. + + */ + + + + switch (numchans) + + { + + + + case 1 : + + /* + + * Determine whether this is a greyscale image or a color + + * indexed image. (So that we know whether or not to scale + + * the incoming value to be between 0 and 255. + + */ + + + + if (photometric!=PHOTOMETRIC_PALETTE) + + isGreyscale = 1; + + else + + isGreyscale = 0; + + + + if (chandepth==16) + + chanList[0] = IMVFBINDEX16; + + else + + chanList[0] = IMVFBINDEX8; + + break; + + case 3 : + + chanList[0] = IMVFBRED; + + chanList[1] = IMVFBGREEN; + + chanList[2] = IMVFBBLUE; + + break; + + case 4 : + + chanList[0] = IMVFBRED; + + chanList[1] = IMVFBGREEN; + + chanList[2] = IMVFBBLUE; + + chanList[3] = IMVFBALPHA; + + break; + + default : + + sprintf(message,"No support for %d channels in an image. Skipping image",numchans); + + ImErrorWarning(message,0,IMEUNSUPPORTED); + + } + + chanIndex = 0; + + + + /* + + * currentShift tells us how many bits to shift the value + + * to the right, after we get it from the array. + + * i.e. after using mask to collect the upper 2 bits of + + * a uint16, we need to shift the value 14 bits to the right + + * to get our final value. + + */ + + currentShift = 16 - chandepth; + + + + /* + + * Allocate space for our tile buffer, and cast + + * it into an array of 16-bit values + + */ + + ImMalloc(tilebuf,unsigned char *,tileSize+1); + + tilebuf16 = (uint16 *) tilebuf; + + + + for (y = 0; y < imageLength ; y+=tileLength) + + { + + + + for (x = 0; x < imageWidth; x += tileWidth) + + { + + /* Keep track of where we are precisely with vfbX and vfbY */ + + vfbY = y; + + vfbX = x; + + + + /* Read the tile! */ + + if (TIFFReadTile(tif, tilebuf, x, y, 0, 0) < 0) + + break; + + if (byteOrder==BINLBF) + + imTiffSwapBytes(tilebuf, tileSize); + + + + /* + + * Set vfbptr to the upper left hand corner of where this + + * tile goes in the image. + + */ + + + + vfbptr = ImVfbQPtr(vfb, x , y); + + + + /* Start with the first sample of the first pixel in the first array element*/ + + pixelNum = 0; + + index16 = 0; + + sampleNum = 0; + + + + while (vfbY < y + tileLength ) + + { + + /* + + * index16 is the index relative to the 16-bit array. + + * mask is the mask within the current array element + + * (i.e. the index relative to the current 16-bit value) + + * sampleNum is the index of the sample in the tile. + + */ + + + + currentValue = (mask & tilebuf16[index16]); + + currentValue = currentValue >> currentShift; + + currentShift -= chandepth; + + if (currentShift < 0) + + currentShift = 16 - chandepth; + + + + /* Is 0 black or white? */ + + + + if (photometric == PHOTOMETRIC_MINISWHITE) + + { + + currentValue = ~currentValue; + + /* Knock off upper (16 - chandepth) bits */ + + currentValue = currentValue << (16 - chandepth); + + currentValue = currentValue >> (16 - chandepth); + + } + + + + + + /* + + * Only set the value if we are within the vfb + + */ + + if (vfbX < imageWidth && vfbY < imageLength) + + { + + switch (chanList[chanIndex]) + + { + + case IMVFBGREY : /* same as INDEX8 */ + + if (isGreyscale) + + ImVfbSGrey(vfb, vfbptr, IM_SCALE_0_255(currentValue,chandepth)); + + else + + ImVfbSIndex8 (vfb, vfbptr, currentValue); + + break; + + case IMVFBRED : + + ImVfbSRed (vfb, vfbptr, IM_SCALE_0_255(currentValue,chandepth) ); + + break; + + case IMVFBGREEN : + + ImVfbSGreen(vfb, vfbptr, IM_SCALE_0_255(currentValue,chandepth) ); + + break; + + case IMVFBBLUE : + + ImVfbSBlue (vfb, vfbptr, IM_SCALE_0_255(currentValue,chandepth) ); + + break; + + case IMVFBALPHA : + + ImVfbSAlpha (vfb, vfbptr, IM_SCALE_0_255(currentValue,chandepth) ); + + break; + + case IMVFBINDEX16 : + + ImVfbSIndex16 (vfb, vfbptr, currentValue ); + + break; + + } + + } + + + + /* + + * Increment: the channel index, the sample number, + + * the mask, possibly the vfb pointer, and possibly + + * the index in the scanline array. + + */ + + + + chanIndex++; + + if (chanIndex > (numchans - 1)) + + { + + /* + + * Move right one pixel in the vfb. + + * If we are at a boundary of the tile, then + + * move down one line and left to y + + */ + + + + vfbX++; + + pixelNum++; + + if (pixelNum == tileWidth) + + { /* Reached the right edge of the tile */ + + pixelNum = 0; + + vfbY += 1; + + vfbX = x; + + if (vfbX < imageWidth && vfbY < imageLength) + + vfbptr = ImVfbQPtr(vfb, x, vfbY); + + } + + else + + { + + /* If we're still in the vfb */ + + if (vfbX < imageWidth && vfbY < imageLength) + + ImVfbSRight (vfb, vfbptr); + + } + + chanIndex = 0; + + } + + sampleNum++; + + + + /* Shift mask and possibly index16 */ + + mask = mask>>chandepth; + + + + if (mask == 0) + + { + + mask = startmask; + + index16++; + + } + + } + + } + + } + + free ((unsigned char *) tilebuf); + + + + return 1; + +} + + + + /* + + * FUNCTION + + * + + * imTiffReadPlaneInterleavedTiles + + * + + * DESCRIPTION + + * + + * Read the data in a tif file into a vfb, where the storage + + * method is non-interleaved tiles. + + * + + */ + + + +static int /* returns 1 for success, 0 for failure */ + +#ifdef __STDC__ + +imTiffReadPlaneInterleavedTiles ( TIFF *tif, ImVfb *vfb) + +#else + +imTiffReadPlaneInterleavedTiles ( tif, vfb) + +TIFF* tif; + +ImVfb* vfb; + +#endif + +{ + + uint16 numchans; /* number of channels */ + + uint16 chandepth; /* Depth of each channel */ + + char message[200]; /* Buffer for messages */ + + uint32 tileWidth, tileLength; /* size of tiles */ + + uint32 imageWidth, imageLength;/* size of image */ + + tsize_t tileSize; /* # of bytes per tile */ + + + + /* + + * Go through the vfb once for each channel. + + * Call the appropriate read function for each channel. + + */ + + + + /* + + * Get the number of channels and channel depth from tiff + + */ + + + + TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &numchans); + + TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &chandepth); + + tileSize = TIFFTileSize(tif); + + TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &imageWidth ); + + TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &imageLength); + + TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tileWidth ); + + TIFFGetField(tif, TIFFTAG_TILELENGTH, &tileLength ); + + + + switch (numchans) + + { + + case 1: + + /* + + * It is silly to have one plane, and call it plane + + * interleaved. This is non-interleaved. Hence, just + + * call the read-non-intereleaved-tiles function. + + * (which allows for varying channel depths) + + */ + + imTiffReadNonInterleavedTiles(tif, vfb); + + break; + + case 3: + + if (chandepth!=8) + + { + + sprintf(message,"Can't read %d bits per channel plane-interleaved, tiled images\n",chandepth); + + ImErrorFatal(message,0,IMEUNSUPPORTED); + + } + + imTiffReadRedTiledPlane (tif, vfb, tileWidth, tileLength, imageWidth, imageLength, tileSize); + + imTiffReadGreenTiledPlane(tif, vfb, tileWidth, tileLength, imageWidth, imageLength, tileSize); + + imTiffReadBlueTiledPlane (tif, vfb, tileWidth, tileLength, imageWidth, imageLength, tileSize); + + break; + + case 4: + + if (chandepth!=8) + + { + + sprintf(message,"Can't read %d bits per channel plane-interleaved, tiled images\n",chandepth); + + ImErrorFatal(message,0,IMEUNSUPPORTED); + + } + + imTiffReadRedTiledPlane (tif, vfb, tileWidth, tileLength, imageWidth, imageLength, tileSize); + + imTiffReadGreenTiledPlane(tif, vfb, tileWidth, tileLength, imageWidth, imageLength, tileSize); + + imTiffReadBlueTiledPlane (tif, vfb, tileWidth, tileLength, imageWidth, imageLength, tileSize); + + imTiffReadAlphaTiledPlane(tif, vfb, tileWidth, tileLength, imageWidth, imageLength, tileSize); + + break; + + default : + + sprintf(message,"Can't read %d channels plane-interleaved\n",numchans); + + ImErrorFatal(message,0,IMEUNSUPPORTED); + + } + + + + return 1; + +} + + + + + +static int /* Returns # of tags written */ + +#ifdef __STDC__ + +imTiffWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imTiffWrite( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* I/O flags */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + TagTable *flagsTable; /* Format flags */ + + TagTable *tagTable; /* Tag list to add to */ + +#endif + +{ + + + + TIFF* tif; /* TIFF structure */ + + char message[200]; /* buffer for messages. */ + + ImVfb* vfb; /* Holds a vfb */ + + ImClt* clt; /* Holds a clt */ + + char *filename; /* Holds file name */ + + int curImage; /* index for the current image */ + + int numImages; /* number of images in the vfb */ + + TagEntry* entry; /* entry in table with file name */ + + + + /* + + * Open our tif structure for writing. + + */ + + + + if (ioType & IMFILEIOFP) + + { + + fd = fileno(fp); + + } + + + + /* Search flags table for filename */ + + + + if (!flagsTable || TagTableQNEntry (flagsTable, "file name") == 0) + + { + + filename = NULL; + + } + + else + + { + + entry = TagTableQDirect ( flagsTable, "file name", 0 ); + + if (TagEntryQValue( entry, &filename) == -1) + + filename = NULL; + + } + + + + tif = TIFFFdOpen(fd, filename ? filename : "libtiff", "wb"); + + if (!tif) + + { + + ImErrorFatal("Could not write to output file", -1, IMENOTPOSSIBLE); + + } + + + + + + /* + + * Loop through images in vfb + + */ + + + + numImages = TagTableQNEntry( tagTable, "image vfb"); + + + + for (curImage = 0; curImage < numImages; curImage++) + + { + + /* Retreieve this vfb from the table */ + + + + TagEntryQValue (TagTableQDirect (tagTable, "image vfb", curImage), &vfb); + + + + if (numImages > 1) + + { + + sprintf (message,"%d of %d", curImage + 1, numImages); + + ImInfo ( "Image", message ); + + } + + + + /* + + * Set resolution + + */ + + + + sprintf(message,"%d x %d",ImVfbQWidth(vfb),ImVfbQHeight(vfb)); + + ImInfo( "Resolution", message ); + + TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, ImVfbQWidth(vfb) ); + + TIFFSetField(tif, TIFFTAG_IMAGELENGTH, ImVfbQHeight(vfb) ); + + + + /* + + * Set orientation + + */ + + + + TIFFSetField (tif,TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + + + + /* + + * Set min, max values. + + */ + + + + TIFFSetField (tif, TIFFTAG_MINSAMPLEVALUE, 0 ); + + TIFFSetField (tif, TIFFTAG_MAXSAMPLEVALUE, ((1L)<<(pMap->map_outChannelDepth)) - 1L); + + + + + + /* + + * Set byte order + + */ + + if ( BinQMachine()->bin_byteOrder == BINLBF) + + { + + TIFFSetField(tif, TIFFTAG_FILLORDER,FILLORDER_LSB2MSB); + + ImInfo ("Byte Order", "Least Significant Byte First"); + + } + + else + + { + + TIFFSetField(tif, TIFFTAG_FILLORDER,FILLORDER_MSB2LSB); + + ImInfo ("Byte Order", "Most Significant Byte First"); + + } + + + + /* + + * Set image type + + */ + + + + if (pMap->map_outAttributes & IMALPHAYES || (ImVfbQFields(vfb) & IMVFBALPHA)) + + sprintf(message,"%d-bit ",pMap->map_outChannelDepth * ( pMap->map_outNChannels + 1)) ; + + else + + sprintf(message,"%d-bit ",pMap->map_outChannelDepth * pMap->map_outNChannels) ; + + + + if (pMap->map_outType == IMTYPERGB || (ImVfbQFields(vfb) & IMVFBRGB) ) + + { + + strcat(message,"RGB"); + + TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,PHOTOMETRIC_RGB); + + } + + else + + { + + strcat(message,"Color Indexed"); + + + + /* + + * Is this a color-indexed image or a mono image or a greyscale image? + + */ + + + + if (ImVfbQClt(vfb) != IMCLTNULL) + + TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,PHOTOMETRIC_PALETTE); + + else /* mono or grayscale. Treat them the same. */ + + TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,PHOTOMETRIC_MINISBLACK); + + } + + + + ImInfo ("Type",message); + + TIFFSetField (tif, TIFFTAG_BITSPERSAMPLE ,pMap->map_outChannelDepth); + + + + + + /* + + * Set alpha channel if one exists + + */ + + + + + + if (pMap->map_outAttributes & IMALPHAYES || (ImVfbQFields(vfb) & IMVFBALPHA)) + + { + + uint16 fields[2]; + + + + fields[0] = EXTRASAMPLE_ASSOCALPHA; + + + + sprintf(message,"%d-bit",pMap->map_outChannelDepth); + + ImInfo ("Alpha Channel", message); + + TIFFSetField (tif, TIFFTAG_SAMPLESPERPIXEL ,pMap->map_outNChannels + 1); + + TIFFSetField (tif, TIFFTAG_EXTRASAMPLES, 1, fields ); + + } + + else + + { + + ImInfo ("Alpha Channel", "none"); + + TIFFSetField (tif, TIFFTAG_SAMPLESPERPIXEL ,pMap->map_outNChannels); + + } + + + + + + + + /* + + * Set Interleave method + + */ + + + + if (pMap->map_outAttributes & IMINTERPLANE) + + { + + ImInfo ("Interleave method","Plane Interleaved"); + + TIFFSetField (tif,TIFFTAG_PLANARCONFIG, PLANARCONFIG_SEPARATE ); + + } + + else + + { + + ImInfo ("Interleave Method","Non-interleaved"); + + TIFFSetField (tif,TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG ); + + } + + + + /* + + * Set Color Table + + */ + + + + if (pMap->map_outAttributes & IMCLTYES || (ImVfbQClt(vfb) != IMCLTNULL) ) + + { + + sprintf(message,"%d entries",(1L)<map_outChannelDepth); + + clt = ImVfbQClt(vfb); + + if (clt==NULL) + + { + + ImErrorFatal("No color table for image",0,IMENOCLT); + + } + + imTiffWriteClt (tif, clt , (1L)<map_outChannelDepth); + + } + + else + + { + + sprintf(message,"none"); + + } + + + + ImInfo ("Color Table", message); + + + + /* + + * Set Compression scheme + + */ + + + + switch (pMap->map_outAttributes & IMCOMPMASK) + + { + + case IMCOMPPACKBITS : + + ImInfo ("Compression Type","Macintosh PackBits encoding"); + + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_PACKBITS); + + break; + + case IMCOMPLZW : + + ImInfo ("Compression Type", "Lempel-Ziv & Welch encoding"); + + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_LZW); + + break; + + case IMCOMPDCT : + + ImInfo ("Compression Type", "JPEG encoding (DCT)"); + + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_JPEG); + + break; + + case IMCOMPNO : + + ImInfo ("Compression Type", "none"); + + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + + break; + + } + + + + /* + + * Set Grouping method + + */ + + + + if ( (pMap->map_outAttributes & IMGROUPMASK) == IMGROUPTILES) + + { + + ImInfo ("Grouping Method", "Tiles"); + + + + /* Determine interleaving method and call the correct routine. */ + + if ( pMap->map_outAttributes & IMINTERPLANE ) + + { + + imTiffWritePlaneInterleavedTiles(tif,vfb, pMap); + + } + + else + + { + + imTiffWriteNonInterleavedTiles(tif, vfb, pMap); + + } + + } + + else /* store as scanlines */ + + { + + ImInfo ("Grouping Method", "Scanlines"); + + + + /* Determine interleaving method and call the correct routine. */ + + if ( pMap->map_outAttributes & IMINTERPLANE ) + + { + + imTiffWritePlaneInterleavedScanlines(tif,vfb, pMap); + + } + + else + + { + + imTiffWriteNonInterleavedScanlines(tif, vfb, pMap); + + } + + } + + + + + + /* + + * Write the directory to prepare for the next vfb + + */ + + + + TIFFWriteDirectory(tif); + + + + } /* End of for loop */ + + TIFFClose (tif); + + + + return numImages; + +} + + + + + +/* + + * + + * FUNCTION + + * + + * imTiffWriteNonInterleavedScanlines + + * + + * DESCRIPTION + + * + + * Write a tiff file with the pixels noninterleaved, + + * and grouping by scanlines. + + * + + */ + + + +static int /* Returns 1 for okay, 0 for failure */ + +#ifdef __STDC__ + +imTiffWriteNonInterleavedScanlines( TIFF* tif, ImVfb* vfb, ImFileFormatWriteMap* pMap) + +#else + +imTiffWriteNonInterleavedScanlines( tif, vfb, pMap) + +TIFF* tif; + +ImVfb* vfb; + +ImFileFormatWriteMap *pMap; + +#endif + +{ + + unsigned char* linebuf; /* buffer for one scanline */ + + uint16* ptr16; /* pointer to the scanline as a 16-bit array */ + + tsize_t scanlineSize; /* size of scanlines */ + + ImVfbPtr vfbptr; /* ptr into a vfb */ + + uint32 x,y; /* Loop indices */ + + uint32 ySize; /* Image size (y) */ + + uint32 xSize; /* Image size (x) */ + + uint16 numchans; /* number of channels */ + + uint16 chandepth; /* number of bits per channel */ + + uint16 currentValue; /* value of current channel of current pixel */ + + int chanIndex; /* which channel is being set */ + + int chanList[5]; /* List of channels to cycle through */ + + uint16 photometric; /* Tells us the type of image this is. */ + + int currentShift; /* How many bits to shift this sample */ + + char message[200]; /* Buffer for error messages */ + + uint16 lastBits; /* Contains the chandepth bits of data */ + + + + /* + + * Determine some important things about the vfb + + */ + + + + chandepth = pMap->map_outChannelDepth; + + numchans = pMap->map_outNChannels; + + xSize = ImVfbQWidth (vfb); + + ySize = ImVfbQHeight(vfb); + + + + if (ImVfbQFields(vfb) & IMVFBALPHA) + + numchans++; + + /* + + * Set Rows per strip to "infinite" + + */ + + TIFFSetField (tif, TIFFTAG_ROWSPERSTRIP, (uint32) -1); + + + + /* Allocate space for the scanline buffer */ + + scanlineSize = xSize * numchans * chandepth / 8 + 2; + + + + ImMalloc(linebuf, unsigned char *, scanlineSize ); + + + + /* + + * Set up the list of channels through which we are going to cycle. + + */ + + + + switch (numchans) + + { + + case 1 : + + if (chandepth==16) + + chanList[0] = IMVFBINDEX16; + + else + + chanList[0] = IMVFBINDEX8; + + break; + + case 3 : + + chanList[0] = IMVFBRED; + + chanList[1] = IMVFBGREEN; + + chanList[2] = IMVFBBLUE; + + break; + + case 4 : + + chanList[0] = IMVFBRED; + + chanList[1] = IMVFBGREEN; + + chanList[2] = IMVFBBLUE; + + chanList[3] = IMVFBALPHA; + + break; + + default : + + sprintf(message,"No support for %d channels in an image. Skipping image",numchans); + + ImErrorWarning(message,0,IMEUNSUPPORTED); + + break; + + } /* end of switch */ + + + + /* + + * Algorithm for writing non-interleaved scanlines: + + * + + * Loop through the pixels in the file, and for + + * each channel of each pixel, query the value, and + + * store it in a scanline buffer. + + * + + * Note that if we are writing less than 8-bits per channel, + + * then we need to do some bit shifting and |'ing to place + + * the data from the vfb in the correct place. + + * + + * Admittedly, having a switch statement within the loop that + + * writes pixels will slow down processing time, but this enables + + * us to handle a wider variety of formats. + + * + + */ + + + + for (y=0; y< ySize ; y++) + + { + + + + /* Write Scanline #y */ + + + + ptr16 = (uint16 *) linebuf; + + x = 0; + + vfbptr = ImVfbQPtr( vfb, 0, y); + + chanIndex = 0; + + currentShift = 0; + + + + /* Clear the first entry in the array */ + + + + *ptr16 = (uint16) 0; + + + + /* Fill up linebuf with information from the vfb */ + + + + while (x < xSize) + + { + + + + /* + + * Get current data from VFB + + */ + + + + IM_QCHANNEL (currentValue, chanList[chanIndex]) + + + + /* + + * Store the last chandepth bits of currentValue + + * in the first chandepth bits of lastBits. + + */ + + + + lastBits = (currentValue << (16 - chandepth)) ; + + /* + + * Shift the value by currentShift (to the right) and bit-or it + + * with *ptr16 (ptr16 is traversing the scanline in 16-bit increments) + + */ + + + + *ptr16 = (*ptr16) | (lastBits >> currentShift); + + + + /* + + * Increment the channel index. This signifies which aspect + + * of the pixel we will query next. + + */ + + + + chanIndex++; + + if (chanIndex == numchans) + + { /* We're done with this pixel */ + + ImVfbSNext(vfb, vfbptr); + + chanIndex = 0; + + x++; + + } + + + + /* Increment currentShift. If we get a number bigger than 16, + + * increment ptr16. + + */ + + + + currentShift += chandepth; + + if (currentShift >= 16) + + { + + ptr16++; + + currentShift = 0; + + /* Clear the next entry in the array */ + + *ptr16 = (uint16) 0; + + } + + + + } /* end of while x < xSize */ + + + + if ( BinQMachine()->bin_byteOrder == BINLBF) + + imTiffSwapBytes(linebuf, scanlineSize); + + + + /* Okay, now write the scanline to the tiff file. */ + + if (TIFFWriteScanline( tif, linebuf, y, 0)==-1) + + { + + ImErrorFatal("Error writing scanline",0,IMESYS); + + } + + + + } /* end of for y = 0 to ySize */ + + + + return 1; + + + +} + + + + + + + + + +/* + + * + + * FUNCTION + + * + + * imTiffWritePlaneInterleavedScanlines + + * + + * DESCRIPTION + + * + + * Write a tiff file, plane-interleaving the pixels, + + * and using scanlines. + + * + + */ + + + +static int /* Returns 1 for okay, 0 for failure */ + +#ifdef __STDC__ + +imTiffWritePlaneInterleavedScanlines( TIFF *tif, ImVfb* vfb, ImFileFormatWriteMap* pMap) + +#else + +imTiffWritePlaneInterleavedScanlines( tif, vfb, pMap) + +TIFF* tif; + +ImVfb* vfb; + +ImFileFormatWriteMap* pMap; + +#endif + +{ + + + + char message[300]; /* Buffer for error message */ + + uint16 numchans; /* number of channels */ + + uint16 chandepth; /* number of bits per channel */ + + + + /* + + * Algorithm : + + * + + * This is basically just like writing non-interleaved scanlines, + + * once for each channel. So this routine does just that. + + * It calls the functions : + + * imTiffWriteRedScanlinePlane, imTiffWriteGreenScanlinePlane + + * imTiffWriteBlueScanlinePlane, imTiffWriteAlphaScanlinePlane + + * + + * Each of those functions simply acts like + + * imTiffWriteNonInterleavedScanlines + + * except that it only writes one channel. + + * + + */ + + + + + + /* + + * Determine depth and number of channels + + */ + + + + chandepth = pMap->map_outChannelDepth; + + numchans = pMap->map_outNChannels; + + if (ImVfbQFields(vfb) & IMVFBALPHA) + + numchans++; + + + + /* + + * Set Rows per strip to "infinite" + + */ + + + + TIFFSetField (tif, TIFFTAG_ROWSPERSTRIP, (uint32) -1); + + + + switch (numchans) + + { + + case 1: + + if (ImVfbQFields(vfb) & IMVFBINDEX16) + + imTiffWriteIndex16ScanlinePlane(vfb, tif, chandepth); + + else + + imTiffWriteIndex8ScanlinePlane(vfb, tif, chandepth); + + break; + + case 3: + + imTiffWriteRedScanlinePlane (vfb, tif, chandepth); + + imTiffWriteGreenScanlinePlane(vfb, tif, chandepth); + + imTiffWriteBlueScanlinePlane (vfb, tif, chandepth); + + break; + + case 4: + + imTiffWriteRedScanlinePlane (vfb, tif, chandepth); + + imTiffWriteGreenScanlinePlane(vfb, tif, chandepth); + + imTiffWriteBlueScanlinePlane (vfb, tif, chandepth); + + imTiffWriteAlphaScanlinePlane(vfb, tif, chandepth); + + break; + + default: + + sprintf(message,"Can't handle %d channels plane-interleaved scanlines\n", numchans); + + ImErrorFatal(message,0,IMEUNSUPPORTED); + + } + + + + return 1; + +} + + + + + +/* + + * + + * FUNCTION + + * + + * imTiffWriteNonInterleavedTiles + + * + + * DESCRIPTION + + * + + * Write a tiff file, using tiles, and not interleaving + + * the pixels. + + * + + */ + + + +static int /* Returns 1 for okay, 0 for failure */ + +#ifdef __STDC__ + +imTiffWriteNonInterleavedTiles (TIFF *tif, ImVfb *vfb, ImFileFormatWriteMap* pMap) + +#else + +imTiffWriteNonInterleavedTiles (tif, vfb, pMap) + +TIFF* tif; + +ImVfb* vfb; + +ImFileFormatWriteMap* pMap; + +#endif + +{ + + uint32 vfbX, vfbY; /* loop indexes */ + + unsigned char* tilebuf; /* buffer for tiles */ + + ImVfbPtr vfbptr; /* pointer into a vfb */ + + int vfbwidth, vfbheight; /* dimensions of the vfb */ + + int tileindex; /* index into tilebuf */ + + int x,y; /* indexes within a tile */ + + uint16 numchans; /* number of channels */ + + uint16 chandepth; /* channel depth */ + + uint32 tileWidth, tileHeight; /* size of tiles */ + + char message[120]; /* buffer for error messages */ + + + + /* + + * Discern the number of channels and channel depth + + */ + + + + numchans = pMap->map_outNChannels; + + if (ImVfbQFields(vfb) & IMVFBALPHA) + + numchans++; + + chandepth = pMap->map_outChannelDepth; + + + + if (chandepth!=8) + + { + + sprintf(message,"No support non-interleaved tiles with depth %d",chandepth); + + ImErrorWarning(message,0,IMEUNSUPPORTED); + + } + + + + /* Let's just make the tiles 16 by 16 (the dimensions must be multiples of 16) */ + + + + tileWidth = (uint32) 16; + + tileHeight = (uint32) 16; + + TIFFSetField (tif, TIFFTAG_TILEWIDTH, tileWidth); + + TIFFSetField (tif, TIFFTAG_TILELENGTH, tileHeight); + + + + /* + + * if only one plane then this is equivalent to writing plane + + * interleaved tiles + + */ + + + + if (numchans==1) + + { + + return imTiffWriteIndex8TiledPlane(vfb, tif, chandepth, tileWidth, tileHeight); + + } + + + + /* Determine vfb dimensions */ + + + + vfbwidth = ImVfbQWidth(vfb); + + vfbheight = ImVfbQHeight(vfb); + + + + /* + + * Allocate space for tilebuf + + */ + + + + ImMalloc(tilebuf, unsigned char *, (int) (tileWidth * tileHeight * numchans) + 1 ); + + + + /* + + * Loop through tile positions and create either an RGB or an RGBA image + + */ + + + + for (vfbX=0; vfbX= vfbwidth) + + { + + tilebuf[tileindex++] = (unsigned char) 0; + + tilebuf[tileindex++] = (unsigned char) 0; + + tilebuf[tileindex++] = (unsigned char) 0; + + if (numchans==4) + + tilebuf[tileindex++] = (unsigned char) 0; + + } + + else /* put in the real values */ + + { + + tilebuf[tileindex++] = ImVfbQRed(vfb,vfbptr); + + tilebuf[tileindex++] = ImVfbQGreen(vfb,vfbptr); + + tilebuf[tileindex++] = ImVfbQBlue(vfb,vfbptr); + + if (numchans==4) + + tilebuf[tileindex++] = ImVfbQAlpha(vfb,vfbptr); + + } + + + + /* Increment index within tile */ + + x++; + + + + /* + + * if we're at the end of the tile + + * move down one line + + * and back to the start of the + + * tile. + + */ + + + + if ( x >= (int) tileWidth ) + + { + + y++; + + if (vfbY + y < vfbheight) + + vfbptr = ImVfbQPtr(vfb, vfbX, vfbY + y); + + x = 0; + + } + + else /* just move to the right */ + + { + + if (vfbY + y < vfbheight) + + vfbptr = ImVfbQRight(vfb,vfbptr); + + } + + } + + /* Send out tile to tiff */ + + if ( BinQMachine()->bin_byteOrder == BINLBF) + + imTiffSwapBytes(tilebuf, tileWidth * tileHeight * numchans); + + TIFFWriteTile(tif, tilebuf, (uint32) vfbX, (uint32) vfbY, 0, 0); + + } + + } + + return 1; + + + +} + + + + + + + +/* + + * + + * FUNCTION + + * + + * imTiffWritePlaneInterleavedTiles + + * + + * DESCRIPTION + + * + + * Write a tiff file, using plane-interleaving and + + * tiles. + + * + + */ + + + +static int /* Returns 1 for okay, 0 for failure */ + +#ifdef __STDC__ + +imTiffWritePlaneInterleavedTiles ( TIFF *tif, ImVfb *vfb, ImFileFormatWriteMap* pMap) + +#else + +imTiffWritePlaneInterleavedTiles ( tif, vfb, pMap) + +TIFF* tif; + +ImVfb* vfb; + +ImFileFormatWriteMap* pMap; + +#endif + +{ + + + + uint16 numchans; /* number of channels */ + + uint16 chandepth; /* number of bits per channel */ + + uint32 tileWidth; /* width of tiles */ + + uint32 tileHeight; /* height of tiles */ + + char message[100]; /* buffer for messages */ + + int numTiles; /* Number of tiles in the image*/ + + + + /* + + * For each plane in the image, call a function to write + + * the plane to the tiff structure + + */ + + + + chandepth = pMap->map_outChannelDepth; + + numchans = pMap->map_outNChannels; + + if (ImVfbQFields(vfb) & IMVFBALPHA) + + numchans++; + + + + /* + + * No support for depths other than 8 at this point in time + + */ + + + + if (chandepth!=8) + + { + + sprintf(message,"Can't write %d depth tiled images",chandepth); + + ImErrorFatal(message,0,IMEUNSUPPORTED); + + } + + + + /* + + * Let's make the tiles 16 x 16. + + * If anyone ever wants to change this, just be sure + + * that the dimensions are multiples of 16 (as per + + * tiff's standards). Or maybe multiples of 8. TIFF + + * has some contradictions in their standards. The man + + * pages for TIFFSetField say 8. The README file says 16. + + * + + */ + + + + tileWidth = (uint32) 16; + + tileHeight = (uint32) 16; + + TIFFSetField (tif, TIFFTAG_TILEWIDTH, tileWidth); + + TIFFSetField (tif, TIFFTAG_TILELENGTH, tileHeight); + + + + switch (numchans) + + { + + case 1: + + imTiffWriteIndex8TiledPlane(vfb, tif, chandepth, tileWidth, tileHeight); + + break; + + case 3: + + imTiffWriteRedTiledPlane (vfb, tif, chandepth, tileWidth, tileHeight); + + imTiffWriteGreenTiledPlane(vfb, tif, chandepth, tileWidth, tileHeight); + + imTiffWriteBlueTiledPlane (vfb, tif, chandepth, tileWidth, tileHeight); + + break; + + case 4: + + imTiffWriteRedTiledPlane (vfb, tif, chandepth, tileWidth, tileHeight); + + imTiffWriteGreenTiledPlane(vfb, tif, chandepth, tileWidth, tileHeight); + + imTiffWriteBlueTiledPlane (vfb, tif, chandepth, tileWidth, tileHeight); + + imTiffWriteAlphaTiledPlane(vfb, tif, chandepth, tileWidth, tileHeight); + + break; + + default: + + sprintf(message,"Can't handle %d channels plane-interleaved tiles\n", numchans); + + ImErrorFatal(message,0,IMEUNSUPPORTED); + + } + + return 1; + + + +} + + + + + +/* + + * + + * FUNCTION + + * + + * imTiffWriteClt + + * + + * DESCRIPTION + + * + + * Write the clt for an image to a file. + + * + + */ + + + +static + +int /* Returns 1 for okay, 0 for failure */ + +#ifdef __STDC__ + +imTiffWriteClt(TIFF* tif, ImClt *clt, long numEntries) + +#else + +imTiffWriteClt(tif, clt, numEntries) + +TIFF* tif; + +ImClt* clt; + +long numEntries; + +#endif + +{ + + uint16* redmap; /* Stores red components for color map entries */ + + uint16* bluemap; /* Stores blue components for color map entries */ + + uint16* greenmap; /* Stores green components for color map entries */ + + ImCltPtr cltptr; /* pointer into the clt */ + + long i; /* loop index */ + + long realNumEntries; /* minimum of desired and actual no. of entries */ + + + + /* Allocate space for arrays which are passed to tif structure */ + + + + ImMalloc(redmap , uint16* , numEntries * sizeof(uint16) ); + + ImMalloc(bluemap , uint16* , numEntries * sizeof(uint16)); + + ImMalloc(greenmap, uint16* , numEntries * sizeof(uint16)); + + + + /* Do we really have numEntries entries in the clt? Or are there fewer? */ + + + + realNumEntries = numEntries; + + if (ImCltQNColors(clt)> currentShift); \ + + currentShift += chandepth; \ + + if (currentShift >= 16) \ + + { \ + + ptr16++; \ + + currentShift = 0; \ + + /* Clear the next entry in the array */ \ + + *ptr16 = (uint16) 0; \ + + } \ + + \ + + ImVfbSNext(vfb, vfbptr); \ + + } \ + + \ + + if ( BinQMachine()->bin_byteOrder == BINLBF) \ + + imTiffSwapBytes(linebuf, scanlineSize); \ + + \ + + /* Okay, now write the scanline to the tiff file. */ \ + + if (TIFFWriteScanline( tif, linebuf, y, imagePlane)==-1) \ + + { \ + + ImErrorFatal("Error writing scanline",0,IMESYS); \ + + } \ + + \ + + } /* end of for y = 0 to ySize */ \ + + \ + + return 1; \ + + \ + +} + + + +IM_MAKE_SCANLINE_PLANE_WRITE_FUNCTION(imTiffWriteRedScanlinePlane,ImVfbQRed, 0) + +IM_MAKE_SCANLINE_PLANE_WRITE_FUNCTION(imTiffWriteGreenScanlinePlane,ImVfbQGreen, 1) + +IM_MAKE_SCANLINE_PLANE_WRITE_FUNCTION(imTiffWriteBlueScanlinePlane,ImVfbQBlue, 2) + +IM_MAKE_SCANLINE_PLANE_WRITE_FUNCTION(imTiffWriteAlphaScanlinePlane,ImVfbQAlpha, 3) + +IM_MAKE_SCANLINE_PLANE_WRITE_FUNCTION(imTiffWriteIndex8ScanlinePlane,ImVfbQIndex8, 0) + +IM_MAKE_SCANLINE_PLANE_WRITE_FUNCTION(imTiffWriteIndex16ScanlinePlane,ImVfbQIndex16, 0) + + + +/* + + * FUNCTION + + * imTiffReadRedScanlinePlane + + * imTiffReadGreenScanlinePlane + + * imTiffReadBlueScanlinePlane + + * + + * DESCRIPTION + + * Read the plane into the vfb. + + * This routine only works for 8-bit images. + + * + + */ + + + +#ifdef __STDC__ + +#define FunctionHeaderSPR(functionName) \ + +static int \ + +functionName(TIFF* tif, ImVfb* vfb) + +#else + + + +#define FunctionHeaderSPR(functionName) \ + +static int \ + +functionName(tif,vfb) \ + +TIFF* tif; \ + +ImVfb* vfb; + +#endif + + + +#define IM_MAKE_SCANLINE_PLANE_READ_FUNCTION(functionName,ImVfbSSomething,imagePlane) \ + +FunctionHeaderSPR(functionName) \ + +{ \ + + unsigned char* linebuf; /* buffer for one scanline */ \ + + tsize_t scanlineSize; /* size of scanlines */ \ + + ImVfbPtr vfbptr; /* ptr into a vfb */ \ + + uint32 row, col; /* Loop indices */ \ + + uint32 ySize; /* Image size (y) */ \ + + \ + + TIFFGetField( tif, TIFFTAG_IMAGELENGTH, &ySize); \ + + scanlineSize = TIFFScanlineSize(tif); \ + + ImMalloc(linebuf,unsigned char *,scanlineSize); \ + + vfbptr = ImVfbQPtr(vfb, 0, 0); \ + + for (row=0; row= vfbwidth) \ + + { \ + + tilebuf[tileindex] = (unsigned char) 0; \ + + } \ + + else /* put in the real value */ \ + + { \ + + tilebuf[tileindex] = ImVfbQSomething(vfb,vfbptr); \ + + } \ + + \ + + /* Increment index within tile */ \ + + x++; \ + + \ + + /* \ + + * if we're at the end of the tile \ + + * move down one line \ + + * and back to the start of the \ + + * tile. \ + + */ \ + + if ( x >= (int) tileWidth ) \ + + { \ + + y++; \ + + if (vfbY + y < vfbheight) \ + + vfbptr = ImVfbQPtr(vfb, vfbX, vfbY + y); \ + + x = 0; \ + + } \ + + else /* just move to the right */ \ + + { \ + + if (vfbY + y < vfbheight) \ + + vfbptr = ImVfbQRight(vfb,vfbptr); \ + + } \ + + tileindex++; \ + + } \ + + /* Send out tile to tiff */ \ + + if ( BinQMachine()->bin_byteOrder == BINLBF) \ + + imTiffSwapBytes(tilebuf, tileWidth * tileHeight); \ + + TIFFWriteTile(tif, tilebuf, (uint32) vfbX, (uint32) vfbY, 0, imagePlane); \ + + } \ + + } \ + +return 1; \ + +} + + + +IM_MAKE_TIFF_TILE_PLANE_WRITE_FUNCTION(imTiffWriteIndex8TiledPlane,ImVfbQIndex8,0) + +IM_MAKE_TIFF_TILE_PLANE_WRITE_FUNCTION(imTiffWriteRedTiledPlane,ImVfbQRed,0) + +IM_MAKE_TIFF_TILE_PLANE_WRITE_FUNCTION(imTiffWriteGreenTiledPlane,ImVfbQGreen,1) + +IM_MAKE_TIFF_TILE_PLANE_WRITE_FUNCTION(imTiffWriteBlueTiledPlane,ImVfbQBlue,2) + +IM_MAKE_TIFF_TILE_PLANE_WRITE_FUNCTION(imTiffWriteAlphaTiledPlane,ImVfbQAlpha,3) + + + +/* + + * FUNCTIONS + + * + + * imTiffReadIndex8TiledPlane + + * imTiffReadRedTiledPlane + + * imTiffReadGreenTiledPlane + + * imTiffReadBlueTiledPlane + + * imTiffReadAlphaTiledPlane + + * + + * DESCRIPTION + + * Read this channel into the entire vfb, when the + + * data is grouped in tiles. + + * + + */ + + + +#ifdef __STDC__ + +#define functionHeaderRTPF(functionName) \ + +static int /* returns 1 for success, 0 for failure */ \ + +functionName( TIFF* tif, ImVfb* vfb, uint32 tileWidth, \ + + uint32 tileLength, uint32 imageWidth, uint32 imageLength, tsize_t tileSize) + +#else + +#define functionHeaderRTPF(functionName) \ + +static int /* returns 1 for success, 0 for failure */ \ + +functionName( tif, vfb, tileWidth,tileLength, imageWidth, imageLength, tileSize) \ + +TIFF* tif; \ + +ImVfb* vfb; \ + +uint32 tileWidth; \ + +uint32 tileLength; \ + +uint32 imageWidth; \ + +uint32 imageLength; \ + +tsize_t tileSize; + +#endif + + + +#define IM_TIFF_MAKE_READ_TILED_PLANE_FUNCTION(functionName, ImVfbSSomething, imagePlane) \ + +functionHeaderRTPF(functionName) \ + +{ \ + + ImVfbPtr vfbptr; /* Pointer into the vfb */ \ + + uint32 tileX, tileY; /* relative to the tile */ \ + + uint32 vfbX, vfbY; /* start of tile relative to vfb */ \ + + unsigned char* tilebuf;/* buffer for tile data */ \ + + int tileIndex; /* index within tilebuf */ \ + + \ + + /* Allocate space for tilebuf */ \ + + \ + + ImMalloc( tilebuf, unsigned char *, tileSize); \ + + \ + +for (vfbY = 0; vfbY < imageLength; vfbY += tileLength) \ + +{ \ + + for (vfbX = 0; vfbX < imageWidth; vfbX += tileWidth) \ + + { \ + + TIFFReadTile( tif, tilebuf, vfbX, vfbY, 0, imagePlane); \ + + tileX = 0; \ + + tileY = 0; \ + + \ + + /* Move to upper left hand corner of this tile */ \ + + \ + + vfbptr = ImVfbQPtr( vfb, vfbX, vfbY); \ + + tileIndex = 0; \ + + \ + + /* Read this tile! */ \ + + \ + + while (tileIndex < tileSize) \ + + { \ + + /* Only write to the vfb if we're within our boundaries */ \ + + \ + + if (vfbX + tileX < imageWidth && vfbY + tileY < imageLength) \ + + { \ + + ImVfbSSomething (vfb, vfbptr, tilebuf[tileIndex]); \ + + ImVfbSRight (vfb, vfbptr); \ + + } \ + + tileX++; \ + + if (tileX >= tileWidth) \ + + { \ + + /* move down a line and back to the left edge of the tile */ \ + + tileX = 0; \ + + tileY++; \ + + if (vfbY + tileY < imageLength) \ + + { \ + + vfbptr = ImVfbQPtr (vfb, vfbX + 0, vfbY + tileY); \ + + } \ + + } \ + + tileIndex++; \ + + } \ + + } \ + + } \ + + return 1; \ + +} + + + +IM_TIFF_MAKE_READ_TILED_PLANE_FUNCTION(imTiffReadRedTiledPlane, ImVfbSRed, 0) + +IM_TIFF_MAKE_READ_TILED_PLANE_FUNCTION(imTiffReadGreenTiledPlane, ImVfbSGreen, 1) + +IM_TIFF_MAKE_READ_TILED_PLANE_FUNCTION(imTiffReadBlueTiledPlane, ImVfbSBlue, 2) + +IM_TIFF_MAKE_READ_TILED_PLANE_FUNCTION(imTiffReadAlphaTiledPlane, ImVfbSAlpha, 0) + + + + + +/* + + * FUNCTION + + * imTiffSwapBytes + + * + + * DESCRIPTION + + * Traverse a line and switch every pair of bytes + + * An extra byte should be allocated when passing a buffer with + + * an odd size to this routine. + + * + + */ + + + +static void /* Returns nothing */ + +#ifdef __STDC__ + +imTiffSwapBytes(unsigned char* linebuf, tsize_t linesize) + +#else + +imTiffSwapBytes(linebuf, linesize) + +unsigned char* linebuf; + +tsize_t linesize; + +#endif + +{ + + int i; /* loop index */ + + unsigned char temp; /* temporary variable */ + + + + for (i=0;i ImVfb*, ImClt -> ImClt* type changes plus minor cleanup. + ** + ** Revision 1.1 90/03/06 17:33:19 mjb + ** Initial revision + ** + **/ + +/** + ** CODE CREDITS + ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1991. + ** Custom development, Michael Bailey, San Diego Supercomputer Center, 1990. + **/ +#include "iminternal.h" + +#ifdef __STDC__ +static void imVfbFillRect( ImVfb *dstVfb, int sxl, int sxr, int syt, + int syb, double xInc, double yInc, double fillStart, int fillField ); +#else +static void imVfbFillRect( ); +#endif + + + +/* sizes of various variable types: */ +#define IM_SIZEOFBYTE sizeof( unsigned char ) +#define IM_SIZEOFSHORT sizeof( unsigned short ) +#define IM_SIZEOFINT sizeof( unsigned int ) +#define IM_SIZEOFFLOAT sizeof( float ) + + +/* ways of aligning to various types: */ +#define IM_ALIGNBYTE(n) +#define IM_ALIGNINT(n) ( n += ( (m=(n%IM_SIZEOFINT) ) == 0 ? 0 : IM_SIZEOFINT-m ) ) +#define IM_ALIGNFLOAT(n) ( n += ( (m=(n%IM_SIZEOFFLOAT)) == 0 ? 0 : IM_SIZEOFFLOAT-m )) + + + + + +/* + * FUNCTION + * ImVfbAlloc - alocate a virtual frame buffer + * + * DESCRIPTION + * The size of the VFB is computed based upon the fields mask. + * Space is allocated and byte offsets set up in the VFB structure. + */ + +ImVfb * /* Returns VFB */ +#ifdef __STDC__ +ImVfbAlloc( int width, int height, int fields ) +#else +ImVfbAlloc( width, height, fields ) + int width, height; /* size of framebuffer */ + int fields; /* mask of what information to store */ +#endif +{ + ImVfb *v; /* ImVfb allocated */ + int nbytes; /* # bytes per pixel */ + ImVfbPtr p; /* pointer to the frame buffer */ + int m; /* mod( nbytes, sizeof(int) ) */ + int allbytes; /* TRUE if only allocating byte data */ + + + /* check if have been asked to allocate anything: */ + + if ( fields == 0 ) + { + ImErrNo = IMEFIELD; + return ( IMVFBNULL ); + } + + + /* initialize: */ + + allbytes = TRUE; + nbytes = 0; + + v = (ImVfb *) malloc( sizeof(ImVfb) ); + if ( v == NULL ) + { + ImErrNo = IMEMALLOC; + return ( IMVFBNULL ); + } + + + if ( fields & IMVFBRED ) + fields = (fields & ~IMVFBRED) | IMVFBRGB; + if ( fields & IMVFBGREEN ) + fields = (fields & ~IMVFBGREEN) | IMVFBRGB; + if ( fields & IMVFBBLUE ) + fields = (fields & ~IMVFBBLUE) | IMVFBRGB; + + v->vfb_width = width; + v->vfb_height = height; + v->vfb_fields = fields; + + v->vfb_clt = IMCLTNULL; + + v->vfb_roff = -1; + v->vfb_goff = -1; + v->vfb_boff = -1; + v->vfb_i8off = -1; + v->vfb_i16off = -1; + v->vfb_aoff = -1; + v->vfb_wpoff = -1; + v->vfb_zoff = -1; + v->vfb_moff = -1; + v->vfb_fpoff = -1; + v->vfb_ioff = -1; + + + /* check each fields possibility: */ + + if ( fields & IMVFBRGB ) + { + IM_ALIGNBYTE( nbytes ); + v->vfb_roff = nbytes; + nbytes += IM_SIZEOFBYTE; + + IM_ALIGNBYTE( nbytes ); + v->vfb_goff = nbytes; + nbytes += IM_SIZEOFBYTE; + + IM_ALIGNBYTE( nbytes ); + v->vfb_boff = nbytes; + nbytes += IM_SIZEOFBYTE; + } + + if ( fields & IMVFBALPHA ) + { + IM_ALIGNBYTE( nbytes ); + v->vfb_aoff = nbytes; + nbytes += IM_SIZEOFBYTE; + } + + if ( fields & IMVFBINDEX8 ) + { + IM_ALIGNBYTE( nbytes ); + v->vfb_i8off = nbytes; + nbytes += IM_SIZEOFBYTE; + } + + if ( fields & IMVFBMONO ) + { + /* Use 1 byte for each mono value. */ + IM_ALIGNBYTE( nbytes ); + v->vfb_moff = nbytes; + nbytes += IM_SIZEOFBYTE; + } + + if ( fields & IMVFBWPROT ) + { + IM_ALIGNBYTE( nbytes ); + v->vfb_wpoff = nbytes; + nbytes += IM_SIZEOFBYTE; + } + + if ( fields & IMVFBINDEX16 ) + { + IM_ALIGNINT( nbytes ); + v->vfb_i16off = nbytes; + nbytes += IM_SIZEOFSHORT; + allbytes = FALSE; + } + + if ( fields & IMVFBZ ) + { + IM_ALIGNINT( nbytes ); + v->vfb_zoff = nbytes; + nbytes += IM_SIZEOFINT; + allbytes = FALSE; + } + + if ( fields & IMVFBFDATA ) + { + IM_ALIGNFLOAT( nbytes ); + v->vfb_fpoff = nbytes; + nbytes += IM_SIZEOFFLOAT; + allbytes = FALSE; + } + + if ( fields & IMVFBIDATA ) + { + IM_ALIGNINT( nbytes ); + v->vfb_ioff = nbytes; + nbytes += IM_SIZEOFINT; + allbytes = FALSE; + } + + + + /* pad so produces an even integer size, if necessary: */ + + if ( ! allbytes ) + IM_ALIGNINT( nbytes ); + + v->vfb_nbytes = nbytes; + + + + /* allocate the necessary frame buffer: */ + + p = (ImVfbPtr) malloc( width*height*nbytes ); + if ( p == (ImVfbPtr)NULL ) + { + free( (char *) v ); + ImErrNo = IMEMALLOC; + return ( IMVFBNULL ); + } + + v->vfb_pfirst = p; + v->vfb_plast = p + ( nbytes * width * height ) - nbytes; + + + +#ifdef DEBUG + fprintf( stderr, "vfb_pfirst = %0x\n", v->vfb_pfirst ); + fprintf( stderr, "vfb_plast = %0x\n", v->vfb_plast ); + fprintf( stderr, "vfb_width = %3d\n", v->vfb_width ); + fprintf( stderr, "vfb_height = %3d\n", v->vfb_height ); + fprintf( stderr, "vfb_fields = %3d\n", v->vfb_fields ); + fprintf( stderr, "vfb_nbytes = %3d\n", v->vfb_nbytes ); + fprintf( stderr, "vfb_roff = %3d\n", v->vfb_roff ); + fprintf( stderr, "vfb_goff = %3d\n", v->vfb_goff ); + fprintf( stderr, "vfb_boff = %3d\n", v->vfb_boff ); + fprintf( stderr, "vfb_aoff = %3d\n", v->vfb_aoff ); + fprintf( stderr, "vfb_i8off = %3d\n", v->vfb_i8off ); + fprintf( stderr, "vfb_moff = %3d\n", v->vfb_moff ); + fprintf( stderr, "vfb_wpoff = %3d\n", v->vfb_wpoff ); + fprintf( stderr, "vfb_i16off = %3d\n", v->vfb_i16off ); + fprintf( stderr, "vfb_zoff = %3d\n", v->vfb_zoff ); + fprintf( stderr, "vfb_fpoff = %3d\n", v->vfb_fpoff ); + fprintf( stderr, "vfb_ioff = %3d\n", v->vfb_ioff ); +#endif + + + + /* done: return the pointer to the ImVfb structure: */ + + return( v ); +} + + + + + +/* + * FUNCTION + * ImVfbFree - deallocate a VFB + * + * DESCRIPTION + * The VFB's data planes are freed, and then the VFB struct itself. + */ + +void /* Returns nothing */ +#ifdef __STDC__ +ImVfbFree( ImVfb *v ) +#else +ImVfbFree( v ) + ImVfb *v; /* VFB to free */ +#endif +{ + if ( v != IMVFBNULL ) + { + free( (char *) ImVfbQFirst(v) ); + free( (char *) v ); + } +} + + + + + +/* + * FUNCTION + * ImVfbDup - duplicate a VFB + * + * DESCRIPTION + * A new VFB is allocated that has the same attributes as the source + * VFB. The source VFB's data is copied to the new VFB. + * + * If the source VFB has a CLT, it is copied and added to the new VFB. + */ + +ImVfb * /* Returns new VFB */ +#ifdef __STDC__ +ImVfbDup( ImVfb* srcVfb ) +#else +ImVfbDup( srcVfb ) + ImVfb *srcVfb; /* VFB to duplicate */ +#endif +{ + ImVfb *newVfb; /* New VFB */ + ImClt *srcClt; /* Original CLT */ + ImClt *newClt; /* New CLT */ + + newVfb = ImVfbAlloc( ImVfbQWidth( srcVfb ), ImVfbQHeight( srcVfb ), + ImVfbQFields( srcVfb ) ); + if( newVfb == 0 ) + { + ImErrNo = IMEMALLOC; + return( IMVFBNULL ); + } + + memcpy( (void *)ImVfbQFirst( newVfb ), (void *)ImVfbQFirst( srcVfb ), + ImVfbQNBytes( srcVfb ) * ImVfbQHeight( srcVfb ) * + ImVfbQWidth( srcVfb ) ); + + srcClt = ImVfbQClt( srcVfb ); + if ( srcClt != IMCLTNULL ) + { + newClt = ImCltDup( srcClt ); + if ( newClt == IMCLTNULL ) + { + ImVfbFree( newVfb ); + return ( IMVFBNULL ); /* ImErrNo already set */ + } + } + ImVfbSClt( newVfb, srcClt ); + + return ( newVfb ); +} + + + + + +/* + * FUNCTION + * ImVfbCopy - copy a subarea within a virtual frame buffer. + * + * DESCRIPTION + * Copy a portion of a virtual frame buffer to a new location. + * + * When the incomming and outgoing VFB's have the same set of fields, + * we just do a memcopy(). Otherwise we have to copy each field + * explicitly. + */ + +ImVfb * /* Returnes copy of source VFB */ +#ifdef __STDC__ +ImVfbCopy( ImVfb *srcVfb, int srcXLeft, int srcYTop, int srcDX, int srcDY, int fieldMask, + ImVfb* dstVfb, int dstXLeft, int dstYTop ) +#else +ImVfbCopy( srcVfb, srcXLeft, srcYTop, srcDX, srcDY, fieldMask, + dstVfb, dstXLeft, dstYTop ) + ImVfb *srcVfb; /* the vfb whose size will change */ + int srcXLeft, srcYTop; /* upper left corner of copy area */ + int srcDX, srcDY; /* size of copy area */ + int fieldMask; /* Fields to copy */ + ImVfb *dstVfb; /* the vfb to put the result in */ + int dstXLeft, dstYTop; /* upper left corner of the dest. area */ +#endif +{ + ImVfbPtr psrc; /* pointer into source vfb */ + ImVfbPtr pdst; /* pointer into destination vfb */ + int i, j; /* counter and temp storage */ + int nbytes; /* # bytes of vfb storage per scanline */ + int f; /* vfb field description */ + int sxl, sxr, syb, syt; /* copy boundaries in source vfb */ + int dw, dh; /* copy size */ + + + /* Truncate desired size, if necessary. */ + sxl = srcXLeft; + if ( sxl < 0 ) + sxl = 0; + if ( sxl >= ImVfbQWidth( srcVfb ) ) + sxl = ImVfbQWidth( srcVfb ) - 1; + + sxr = srcXLeft + srcDX - 1; + if ( sxr < 0 ) + sxr = 0; + if ( sxr >= ImVfbQWidth( srcVfb ) ) + sxr = ImVfbQWidth( srcVfb ) - 1; + + syt = srcYTop; + if ( syt < 0 ) + syt = 0; + if ( syt >= ImVfbQHeight( srcVfb ) ) + syt = ImVfbQHeight( srcVfb ) - 1; + + syb = srcYTop + srcDY - 1; + if ( syb < 0 ) + syb = 0; + if ( syb >= ImVfbQHeight( srcVfb ) ) + syb = ImVfbQHeight( srcVfb ) - 1; + + + /* Swap left and right, top and bottom if necessary. */ + if ( sxl > sxr ) + { + i = sxl; sxl = sxr; sxr = i; + } + if ( syt > syb ) + { + i = syb; syb = syt; syt = i; + } + + + /* determine widths and see if they are legal: */ + dw = sxr - sxl + 1; + if ( dw <= 0 ) + { + ImErrNo = IMEWIDTH; + return( IMVFBNULL ); + } + + dh = syb - syt + 1; + if ( dh <= 0 ) + { + ImErrNo = IMEHEIGHT; + return( IMVFBNULL ); + } + + + + /* determine the fields on this vfb: */ + f = ImVfbQFields( srcVfb ); + if ( fieldMask == IMVFBALL ) + fieldMask = f; + else + fieldMask = f & fieldMask; + + if ( fieldMask == 0 ) + { + ImErrNo = IMEFIELD; + return ( IMVFBNULL ); + } + + + if ( dstVfb == IMVFBNEW ) + { + /* Allocate the new vfb. */ + dstVfb = ImVfbAlloc( dw, dh, fieldMask ); + if ( dstVfb == IMVFBNULL ) + { + ImErrNo = IMEMALLOC; + return( IMVFBNULL ); + } + } + else + { + fieldMask &= ImVfbQFields( dstVfb ); + } + + + /* Loop through destination framebuffer, filling it as we go. */ + if ( f == fieldMask && f == ImVfbQFields( dstVfb ) ) + { + /* # bytes per scanline: */ + nbytes = ImVfbQNBytes( srcVfb ) * dw; + + psrc = ImVfbQPtr( srcVfb, sxl, syt ); + pdst = ImVfbQPtr(dstVfb, dstXLeft, dstYTop); + for( i = syt; i <= syb; i++ ) + { + memcpy( (void *) pdst, (void *) psrc, nbytes ); + ImVfbSDown( srcVfb, psrc ); + ImVfbSDown( dstVfb, pdst ); + } + return( dstVfb ); + } + for ( i = syt; i <= syb; i++, dstYTop++ ) + { + psrc = ImVfbQPtr( srcVfb, sxl, i ); + pdst = ImVfbQPtr( dstVfb, dstXLeft, dstYTop ); + for ( j = sxl; j <= sxr; j++ ) + { + if ( fieldMask & IMVFBMONO ) + { + ImVfbSMono( dstVfb, pdst, ImVfbQMono( srcVfb, psrc ) ); + } + if ( fieldMask & IMVFBINDEX8 ) + { + ImVfbSIndex8( dstVfb, pdst, ImVfbQIndex8( srcVfb, psrc ) ); + } + if ( fieldMask & IMVFBINDEX16 ) + { + ImVfbSIndex16( dstVfb, pdst, ImVfbQIndex16( srcVfb, psrc ) ); + } + if ( fieldMask & IMVFBRGB ) + { + ImVfbSRed( dstVfb, pdst, ImVfbQRed( srcVfb, psrc ) ); + ImVfbSGreen( dstVfb, pdst, ImVfbQGreen( srcVfb, psrc ) ); + ImVfbSBlue( dstVfb, pdst, ImVfbQBlue( srcVfb, psrc ) ); + } + if ( fieldMask & IMVFBALPHA ) + { + ImVfbSAlpha( dstVfb, pdst, ImVfbQAlpha( srcVfb, psrc ) ); + } + if ( fieldMask & IMVFBIDATA ) + { + ImVfbSIData( dstVfb, pdst, ImVfbQIData( srcVfb, psrc ) ); + } + if ( fieldMask & IMVFBFDATA ) + { + ImVfbSFData( dstVfb, pdst, ImVfbQFData( srcVfb, psrc ) ); + } + if ( fieldMask & IMVFBZ ) + { + ImVfbSZ( dstVfb, pdst, ImVfbQZ( srcVfb, psrc ) ); + } + if ( fieldMask & IMVFBWPROT ) + { + ImVfbSWProt( dstVfb, pdst, ImVfbQWProt( srcVfb, psrc ) ); + } + ImVfbSInc( srcVfb, psrc ); + ImVfbSInc( dstVfb, pdst ); + } + } + return( dstVfb ); +} + + + + + +/* + * FUNCTION + * IM_SETPIXEL - set a pixel to a value. + * ImVfbClear - clear a virtual frame buffer. + * + * DESCRIPTION + * Erases a portion of a VFB. + */ + +#define IM_SETPIXEL(dstVfb,pdst,fieldMask,value) \ +{ \ + if ( fieldMask & IMVFBRGB ) \ + { \ + ImVfbSRed( dstVfb, pdst, (value) ); \ + ImVfbSGreen( dstVfb, pdst, (value) ); \ + ImVfbSBlue( dstVfb, pdst, (value) ); \ + } \ + if ( fieldMask & IMVFBMONO ) \ + ImVfbSMono( dstVfb, pdst, (value) ); \ + if ( fieldMask & IMVFBINDEX8 ) \ + ImVfbSIndex8( dstVfb, pdst, (value) ); \ + if ( fieldMask & IMVFBINDEX16 ) \ + ImVfbSIndex16( dstVfb, pdst, (value) ); \ + if ( fieldMask & IMVFBALPHA ) \ + ImVfbSAlpha( dstVfb, pdst, (value) ); \ + if ( fieldMask & IMVFBIDATA ) \ + ImVfbSIData( dstVfb, pdst, (value) ); \ + if ( fieldMask & IMVFBFDATA ) \ + ImVfbSFData( dstVfb, pdst, (value) ); \ + if ( fieldMask & IMVFBZ ) \ + ImVfbSZ( dstVfb, pdst, (value) ); \ + if ( fieldMask & IMVFBWPROT ) \ + ImVfbSWProt( dstVfb, pdst, (value) ); \ +} + +ImVfb * /* Returns cleared VFB */ +#ifdef __STDC__ +ImVfbClear( int fieldMask, int value, ImVfb* dstVfb ) +#else +ImVfbClear( fieldMask, value, dstVfb ) + int fieldMask; /* Fields to copy */ + int value; /* Value to set fields to */ + ImVfb *dstVfb; /* the vfb to put the result in */ +#endif +{ + ImVfbPtr pdst; /* pointer into destination vfb */ + int i, j; /* counter and temp storage */ + int nbytes; /* # bytes of vfb storage per scanline */ + int f; /* vfb field description */ + int width, height; /* Image width and height */ + + + width = ImVfbQWidth( dstVfb ); + height = ImVfbQHeight( dstVfb ); + f = ImVfbQFields( dstVfb ); + fieldMask = f & fieldMask; + + + /* Loop through destination framebuffer, filling it as we go. */ + if ( f == fieldMask && f == ImVfbQFields( dstVfb ) && value == 0 ) + { + /* # bytes per scanline: */ + nbytes = ImVfbQNBytes( dstVfb ) * width; + + pdst = ImVfbQFirst( dstVfb ); + for( i = 0; i < height; i++ ) + { + memset( (void *) pdst, 0x00, nbytes ); + ImVfbSDown( dstVfb, pdst ); + } + return( dstVfb ); + } + + pdst = ImVfbQFirst( dstVfb ); + for ( i = 0; i < height; i++ ) + { + for ( j = 0; j < width; j++ ) + { + IM_SETPIXEL( dstVfb, pdst, fieldMask, value ); + ImVfbSInc( dstVfb, pdst ); + } + } + + return ( dstVfb ); +} + + + + +/* + * FUNCTION + * ImVfbMix - mix together two virtual frame buffers + * + * DESCRIPTION + * Pixels in the two source images are mixed together based upon the + * given weighting factors: + * + * w1 = src1Weight / (src1Weight + src2Weight) + * w2 = src2Weight / (src1Weight + src2Weight) + * + * dst = src1 * w1 + src2 * w2 + */ + +ImVfb * /* Returns new VFB */ +#ifdef __STDC__ +ImVfbMix(ImVfb* src1Vfb, double src1Weight, ImVfb* src2Vfb, double src2Weight, int fieldMask, ImVfb *dstVfb ) +#else +ImVfbMix( src1Vfb, src1Weight, src2Vfb, src2Weight, fieldMask, dstVfb ) + ImVfb *src1Vfb; /* First source VFB */ + double src1Weight; /* First source's weighting factor */ + ImVfb *src2Vfb; /* Second source VFB */ + double src2Weight; /* Second source's weighting factor */ + int fieldMask; /* Fields to combine */ + ImVfb *dstVfb; /* Resulting VFB */ +#endif +{ + ImVfbPtr psrc1, psrc2; /* Source VFB pixel pointers */ + ImVfbPtr pdst; /* Destination VFB pixel pointer */ + int pixel1, pixel2; /* Source pixel values */ + int i, j; /* Counters and temp storage */ + int f; /* vfb field description */ + int dw, dh; /* copy size */ + float divisor; /* Weighting factor divisor */ + int tempMask; + + /* + * Normalize weighting factors. + */ + divisor = 1.0 / (src1Weight + src2Weight); + src1Weight *= divisor; + src2Weight *= divisor; + + + /* + * Determine the fields to be affected. + */ + tempMask = fieldMask; + f = ImVfbQFields( src1Vfb ) & ImVfbQFields( src2Vfb ); + if ( fieldMask == IMVFBALL ) + fieldMask = f; + else + fieldMask = f & fieldMask; + if ( (tempMask&IMVFBRED) && (f&IMVFBRGB)) fieldMask |= IMVFBRED ; + if ( (tempMask&IMVFBGREEN) && (f&IMVFBRGB)) fieldMask |= IMVFBGREEN ; + if ( (tempMask&IMVFBBLUE) && (f&IMVFBRGB)) fieldMask |= IMVFBBLUE ; + if ( fieldMask == 0 ) + { + ImErrNo = IMEFIELD; + return ( IMVFBNULL ); + } + + /* + * Determine XY range of pixels to be dissolved. + */ + dw = ImVfbQWidth( src1Vfb ) < ImVfbQWidth( src2Vfb ) ? + ImVfbQWidth( src1Vfb ) : ImVfbQWidth( src2Vfb ); + dh = ImVfbQHeight( src1Vfb ) < ImVfbQHeight( src2Vfb ) ? + ImVfbQHeight( src1Vfb ) : ImVfbQHeight( src2Vfb ); + + if ( dstVfb == IMVFBNEW ) + { + /* Create a new VFB at the size of the smallest source. */ + dstVfb = ImVfbAlloc( dw, dh, fieldMask ); + if ( dstVfb == IMVFBNULL ) + { + ImErrNo = IMEMALLOC; + return( IMVFBNULL ); + } + } + else + { + if ( dw > ImVfbQWidth( dstVfb ) ) + dw = ImVfbQWidth( dstVfb ); + if ( dh > ImVfbQHeight( dstVfb ) ) + dh = ImVfbQHeight( dstVfb ); + } + + if ( dw <= 0 ) + { + ImErrNo = IMEWIDTH; + return( IMVFBNULL ); + } + if ( dh <= 0 ) + { + ImErrNo = IMEHEIGHT; + return( IMVFBNULL ); + } + + + /* + * Loop through the VFB's, copying pixels to the destination VFB + * using the formula: + * + * dst = src1 * src1Wieght + src2 * src2Weight + */ + for ( i = 0; i < dh; i++ ) + { + psrc1 = ImVfbQPtr( src1Vfb, 0, i ); + psrc2 = ImVfbQPtr( src2Vfb, 0, i ); + pdst = ImVfbQPtr( dstVfb, 0, i ); + + for ( j = 0; j < dw; j++ ) + { + if ( fieldMask & IMVFBMONO ) + { + pixel1 = ImVfbQMono( src1Vfb, psrc1 ); + pixel2 = ImVfbQMono( src2Vfb, psrc2 ); + ImVfbSMono( dstVfb, pdst, + (int)( pixel1 * src1Weight + + pixel2 * src2Weight ) ); + } + if ( fieldMask & IMVFBINDEX8 ) + { + pixel1 = ImVfbQIndex8( src1Vfb, psrc1 ); + pixel2 = ImVfbQIndex8( src2Vfb, psrc2 ); + ImVfbSIndex8( dstVfb, pdst, + (int)( pixel1 * src1Weight + + pixel2 * src2Weight ) ); + } + if ( fieldMask & IMVFBINDEX16 ) + { + pixel1 = ImVfbQIndex16( src1Vfb, psrc1 ); + pixel2 = ImVfbQIndex16( src2Vfb, psrc2 ); + ImVfbSIndex16( dstVfb, pdst, + (int)( pixel1 * src1Weight + + pixel2 * src2Weight ) ); + } + if ( fieldMask & IMVFBRED ) + { + pixel1 = ImVfbQRed( src1Vfb, psrc1 ); + pixel2 = ImVfbQRed( src2Vfb, psrc2 ); + ImVfbSRed( dstVfb, pdst, + (int)( pixel1 * src1Weight + + pixel2 * src2Weight ) ); + } + if ( fieldMask & IMVFBGREEN ) + { + pixel1 = ImVfbQGreen( src1Vfb, psrc1 ); + pixel2 = ImVfbQGreen( src2Vfb, psrc2 ); + ImVfbSGreen( dstVfb, pdst, + (int)( pixel1 * src1Weight + + pixel2 * src2Weight ) ); + } + if ( fieldMask & IMVFBBLUE ) + { + pixel1 = ImVfbQBlue( src1Vfb, psrc1 ); + pixel2 = ImVfbQBlue( src2Vfb, psrc2 ); + ImVfbSBlue( dstVfb, pdst, + (int)( pixel1 * src1Weight + + pixel2 * src2Weight ) ); + } + if ( fieldMask & IMVFBRGB ) + { + pixel1 = ImVfbQRed( src1Vfb, psrc1 ); + pixel2 = ImVfbQRed( src2Vfb, psrc2 ); + ImVfbSRed( dstVfb, pdst, + (int)( pixel1 * src1Weight + + pixel2 * src2Weight ) ); + pixel1 = ImVfbQGreen( src1Vfb, psrc1 ); + pixel2 = ImVfbQGreen( src2Vfb, psrc2 ); + ImVfbSGreen( dstVfb, pdst, + (int)( pixel1 * src1Weight + + pixel2 * src2Weight ) ); + pixel1 = ImVfbQBlue( src1Vfb, psrc1 ); + pixel2 = ImVfbQBlue( src2Vfb, psrc2 ); + ImVfbSBlue( dstVfb, pdst, + (int)( pixel1 * src1Weight + + pixel2 * src2Weight ) ); + } + if ( fieldMask & IMVFBALPHA ) + { + pixel1 = ImVfbQAlpha( src1Vfb, psrc1 ); + pixel2 = ImVfbQAlpha( src2Vfb, psrc2 ); + ImVfbSAlpha( dstVfb, pdst, + (int)( pixel1 * src1Weight + + pixel2 * src2Weight ) ); + } + if ( fieldMask & IMVFBIDATA ) + { + pixel1 = ImVfbQIData( src1Vfb, psrc1 ); + pixel2 = ImVfbQIData( src2Vfb, psrc2 ); + ImVfbSIData( dstVfb, pdst, + (int)( pixel1 * src1Weight + + pixel2 * src2Weight ) ); + } + if ( fieldMask & IMVFBFDATA ) + { + ImVfbSFData( dstVfb, pdst, + ( ImVfbQFData( src1Vfb, psrc1 ) * + src1Weight + + ImVfbQFData( src2Vfb, psrc2 ) * + src2Weight ) ); + } + if ( fieldMask & IMVFBZ ) + { + pixel1 = ImVfbQZ( src1Vfb, psrc1 ); + pixel2 = ImVfbQZ( src2Vfb, psrc2 ); + ImVfbSZ( dstVfb, pdst, + (int)( pixel1 * src1Weight + + pixel2 * src2Weight ) ); + } + if ( fieldMask & IMVFBWPROT ) + { + pixel1 = ImVfbQWProt( src1Vfb, psrc1 ); + pixel2 = ImVfbQWProt( src2Vfb, psrc2 ); + ImVfbSWProt( dstVfb, pdst, + (int)( pixel1 * src1Weight + + pixel2 * src2Weight ) ); + } + ImVfbSInc( src1Vfb, psrc1 ); + ImVfbSInc( src2Vfb, psrc2 ); + ImVfbSInc( dstVfb, pdst ); + } + } + return( dstVfb ); +} + + + + + +/* + * FUNCTION + * ImVfbFade - fade a virtual frame buffer to 0's + * + * DESCRIPTION + * Pixels are faded towards zero in all fields using the given weighting + * factor: + * + * dst = src * weight + */ + +ImVfb * /* Returns new VFB */ +#ifdef __STDC__ +ImVfbFade( ImVfb* srcVfb, double srcWeight, int fieldMask, ImVfb* dstVfb ) +#else +ImVfbFade( srcVfb, srcWeight, fieldMask, dstVfb ) + ImVfb *srcVfb; /* First source VFB */ + double srcWeight; /* First source's weighting factor */ + int fieldMask; /* Fields to combine */ + ImVfb *dstVfb; /* Resulting VFB */ +#endif +{ + ImVfbPtr psrc; /* Source VFB pixel pointers */ + ImVfbPtr pdst; /* Destination VFB pixel pointer */ + int pixel; /* Source pixel values */ + int i, j; /* Counters and temp storage */ + int f; /* vfb field description */ + int dw, dh; /* copy size */ + + + /* + * Determine the fields to be affected. + */ + f = ImVfbQFields( srcVfb ); + if ( fieldMask == IMVFBALL ) + fieldMask = f; + else + fieldMask = f & fieldMask; + if ( fieldMask == 0 ) + { + ImErrNo = IMEFIELD; + return ( IMVFBNULL ); + } + + + /* + * Determine XY range of pixels to be faded. + */ + dw = ImVfbQWidth( srcVfb ); + dh = ImVfbQHeight( srcVfb ); + if ( dstVfb == IMVFBNEW ) + { + /* Create a new VFB at the size of the smallest source. */ + dstVfb = ImVfbAlloc( dw, dh, fieldMask ); + if ( dstVfb == IMVFBNULL ) + { + ImErrNo = IMEMALLOC; + return( IMVFBNULL ); + } + } + else + { + fieldMask &= ImVfbQFields( dstVfb ); + if ( dw > ImVfbQWidth( dstVfb ) ) + dw = ImVfbQWidth( dstVfb ); + if ( dh > ImVfbQHeight( dstVfb ) ) + dh = ImVfbQHeight( dstVfb ); + } + if ( dw <= 0 ) + { + ImErrNo = IMEWIDTH; + return( IMVFBNULL ); + } + if ( dh <= 0 ) + { + ImErrNo = IMEHEIGHT; + return( IMVFBNULL ); + } + + + /* + * Loop through the VFB's, copying pixels to the destination VFB + * using the formula: + * + * dst = src * srcWieght + */ + for ( i = 0; i < dh; i++ ) + { + psrc = ImVfbQPtr( srcVfb, 0, i ); + pdst = ImVfbQPtr( dstVfb, 0, i ); + + for ( j = 0; j < dw; j++ ) + { + if ( fieldMask & IMVFBMONO ) + { + pixel = ImVfbQMono( srcVfb, psrc ); + ImVfbSMono( dstVfb, pdst, + (int)( pixel * srcWeight ) & 0x1 ); + } + if ( fieldMask & IMVFBINDEX8 ) + { + pixel = ImVfbQIndex8( srcVfb, psrc ); + ImVfbSIndex8( dstVfb, pdst, + (int)( pixel * srcWeight ) & 0xFF ); + } + if ( fieldMask & IMVFBINDEX16 ) + { + pixel = ImVfbQIndex16( srcVfb, psrc ); + ImVfbSIndex16( dstVfb, pdst, + (int)( pixel * srcWeight ) & 0xFFFF ); + } + if ( fieldMask & IMVFBRGB ) + { + pixel = ImVfbQRed( srcVfb, psrc ); + ImVfbSRed( dstVfb, pdst, + (int)( pixel * srcWeight ) & 0xFF ); + pixel = ImVfbQGreen( srcVfb, psrc ); + ImVfbSGreen( dstVfb, pdst, + (int)( pixel * srcWeight ) & 0xFF ); + pixel = ImVfbQBlue( srcVfb, psrc ); + ImVfbSBlue( dstVfb, pdst, + (int)( pixel * srcWeight ) & 0xFF ); + } + if ( fieldMask & IMVFBALPHA ) + { + pixel = ImVfbQAlpha( srcVfb, psrc ); + ImVfbSAlpha( dstVfb, pdst, + (int)( pixel * srcWeight ) & 0xFF ); + } + if ( fieldMask & IMVFBIDATA ) + { + pixel = ImVfbQIData( srcVfb, psrc ); + ImVfbSIData( dstVfb, pdst, + (int)( pixel * srcWeight ) ); + } + if ( fieldMask & IMVFBFDATA ) + { + ImVfbSFData( dstVfb, pdst, + ( ImVfbQFData( srcVfb, psrc ) * + srcWeight ) ); + } + if ( fieldMask & IMVFBZ ) + { + pixel = ImVfbQZ( srcVfb, psrc ); + ImVfbSZ( dstVfb, pdst, + (int)( pixel * srcWeight ) ); + } + if ( fieldMask & IMVFBWPROT ) + { + pixel = ImVfbQWProt( srcVfb, psrc ); + ImVfbSWProt( dstVfb, pdst, + (int)( pixel * srcWeight ) ); + } + ImVfbSInc( srcVfb, psrc ); + ImVfbSInc( dstVfb, pdst ); + } + } + return( dstVfb ); +} + + + + +/* + * FUNCTION + * ImHsiToRgb( hsi, rgb ) + * + * DESCRIPTION + * convert a hue-saturation-intensity into a red-green-blue value + */ + +#define IM_Round(x) ( (int)( (x) + 0.5 ) ) + +void /* Returns nothing */ +#ifdef __STDC__ +ImHsiToRgb( float hsi[3], int rgb[3] ) +#else +ImHsiToRgb( hsi, rgb ) + float hsi[3]; /* HSI to convert */ + int rgb[3]; /* Returned RGB */ +#endif +{ + float h, s, i; /* hue, sat, intens */ + float delta; /* change in color intens */ + float r, g, b; /* red, green, blue */ + int ih; /* integer hue */ + + /* guarantee valid input: */ + h = hsi[0] /360.; + + ih = (int) h; + h = h - (float)ih; + + s = hsi[1]; + if( s < 0. ) + s = 0.; + if( s > 1. ) + s = 1.; + + i = hsi[2]; + if( i < 0. ) + i = 0.; + if( i > 1. ) + i = 1.; + + + /* get an rgb from the hue itself: */ + if( h <= (1./6.) ) + { + r = 1.0; + g = 6. * h; + b = 0.; + } + else if( h <= (2./6.) ) + { + r = 2. - 6. * h; + g = 1.; + b = 0.; + } + else if( h <= (3./6.) ) + { + r = 0.; + g = 1.; + b = 6. * h - 2.; + } + else if( h <= (4./6.) ) + { + r = 0.; + g = 4. - 6. * h; + b = 1.; + } + else if( h <= (5./6.) ) + { + r = 6. * h - 4.; + g = 0.; + b = 1.; + } + else + { + r = 1.; + g = 0.; + b = 6. - 6. * h; + } + + + /* add in the saturation and intensity effects: */ + /* red: */ + delta = s * ( r - 0.5 ); + if( i <= 0.5 ) + r = 2.*i*( 0.5 + delta ); + else + r = 0.5 + delta + (2.*i-1.)*( 0.5 - delta ); + + /* green: */ + delta = s * ( g - 0.5 ); + if( i <= 0.5 ) + g = 2.*i*( 0.5 + delta ); + else + g = 0.5 + delta + (2.*i-1.)*( 0.5 - delta ); + + /* blue: */ + delta = s * ( b - 0.5 ); + if( i <= 0.5 ) + b = 2.*i*( 0.5 + delta ); + else + b = 0.5 + delta + (2.*i-1.)*( 0.5 - delta ); + + + /* convert to integer in the range 0-255: */ + rgb[0] = IM_Round( 255. * r ); + rgb[1] = IM_Round( 255. * g ); + rgb[2] = IM_Round( 255. * b ); +} + + + + + +/* + * FUNCTION + * RgbHsi + * + * DESCRIPTION + * convert a red-green-blue value into hue-saturation-intensity + */ + +void /* Returns nothing */ +#ifdef __STDC__ +ImRgbToHsi( int rgb[3], float hsi[3] ) +#else +ImRgbToHsi( rgb, hsi ) + int rgb[3]; /* RGB to convert */ + float hsi[3]; /* HSI to return */ +#endif +{ + int r, g, b; /* red, green, blue */ + int min, max; /* min and max rgb values */ + float fmin, fmax, diff; /* min, max, and range of rgb vals */ + float hue, sat, intens; /* h s i */ + float cr, cg, cb; /* coefficients for computing hue */ + + + /* guarantee valid input: */ + r = rgb[0] & 255; + g = rgb[1] & 255; + b = rgb[2] & 255; + + + /* determine min and max color primary values: */ + min = r; + max = r; + if( g < min ) min = g; + if( g > max ) max = g; + if( b < min ) min = b; + if( b > max ) max = b; + + fmin = (float)min; + fmax = (float)max; + diff = fmax - fmin; + + + /* special case r==g==b==0 */ + if( max == 0 ) + { + hsi[0] = 0.; + hsi[1] = 0.; + hsi[2] = 0.; + return; + } + + + /* special case r==g==b==255 */ + if( min == 255 ) + { + hsi[0] = 0.; + hsi[1] = 1.; + hsi[2] = 1.; + return; + } + + + /* compute saturation: */ + intens = ( fmin + fmax ) / (255.+255.); + + if( intens <= 0.5 ) + sat = diff / (fmax + fmin); + else + sat = diff / ( 255.+255. - fmax - fmin ); + + + /* compute hue: */ + if( max == min ) + hue = 0.0; + else + { + cr = ( fmax-(float)r ) / diff; + cg = ( fmax-(float)g ) / diff; + cb = ( fmax-(float)b ) / diff; + + if( max == r ) + hue = cb - cg; + else if( max == g ) + hue = 2. + cr - cb; + else if( max == b ) + hue = 4. + cg - cr; + } + + + hue *= 60.0; + if( hue < 0.0 ) + hue += 360.0; + if( hue > 360.0 ) + hue -= 360.0; + + + /* store output values: */ + hsi[0] = hue; + hsi[1] = sat; + hsi[2] = intens; +} + + + +/* + * FUNCTION + * ImVfbRoll - roll a VFB + * + * DESCRIPTION + * If no destination VFB is given, a new destination VFB is created. + * + * The source VFB is then copied into the destination VFB, rolling + * its pixels horizontally, vertically, or both. + */ + +ImVfb * /* Returns rolled VFB */ +#ifdef __STDC__ +ImVfbRoll( ImVfb *sourceVfb, int xPixels, int yPixels, ImVfb* dstVfb) +#else +ImVfbRoll( sourceVfb, xPixels, yPixels, dstVfb) + ImVfb *sourceVfb; /* VFB to roll */ + int xPixels; /* amount of horizontal pixels to roll */ + int yPixels; /* amount of vertical pixels to roll */ + ImVfb *dstVfb; /* VFB to overwrite with rolled source */ +#endif +{ + ImVfb *sVfb; /* VFB to overwrite with rolled source */ + int dw, dh; /* column, row indices */ + int i,j; /* counters */ + int x, y; /* New pixel location */ + ImVfbPtr psrc, psrc2; /* Source VFB pixel pointers */ + ImVfbPtr pdst, pdst2; /* Destination VFB pixel pointers */ + int fieldMask; /* Fields to roll */ + int n1, n2; /* # of bytes to copy */ + + + /* + * Check that we've been given acceptable source and destination + * VFBs and valid pixel roll amounts. + */ + if ( sourceVfb == IMVFBNULL ) + { + ImErrNo = IMENOVFB; + return ( IMVFBNULL ); + } + dw = ImVfbQWidth( sourceVfb ); + dh = ImVfbQHeight( sourceVfb ); + fieldMask = ImVfbQFields( sourceVfb ); + if ( dstVfb != IMVFBNEW ) + { + if ( dw != ImVfbQWidth( dstVfb ) ) + { + ImErrNo = IMEWIDTH; + return ( IMVFBNULL ); + } + if ( dh != ImVfbQHeight( dstVfb ) ) + { + ImErrNo = IMEHEIGHT; + return ( IMVFBNULL ); + } + if ( fieldMask != ImVfbQFields( dstVfb ) ) + { + ImErrNo = IMEFIELD; + return ( IMVFBNULL ); + } + } + + xPixels %= dw; + yPixels %= dh; + if ( xPixels < 0 ) + xPixels += dw; + if ( yPixels < 0 ) + yPixels += dh; + + + /* + * Create a new destination VFB if necessary. + * If the source and destination are the same, create a temporary + * copy of the source before doing the roll. + */ + sVfb = sourceVfb; + if ( dstVfb == IMVFBNEW ) + { + if ( (dstVfb = ImVfbAlloc( dw, dh, fieldMask )) == IMVFBNULL ) + { + ImErrNo = IMEMALLOC; + return( IMVFBNULL ); + } + } + else if ( sourceVfb == dstVfb ) + { + if ( (sVfb = ImVfbDup( sourceVfb )) == IMVFBNULL ) + return ( IMVFBNULL ); /* ImErrNo already set */ + } + + + /* + * Roll the image by copying pixels from the source to the + * destination. If the source and destination VFB's have the + * same fields, we can speed up the copy by using memcopy(). + */ + if ( fieldMask == ImVfbQFields( dstVfb ) ) + { + psrc = ImVfbQFirst( sVfb ); + psrc2 = ImVfbQPtr( sVfb, (dw - xPixels), 0 ); + pdst = ImVfbQPtr( dstVfb, xPixels, yPixels ); + pdst2 = ImVfbQPtr( dstVfb, 0, yPixels ); + n1 = ImVfbQNBytes( sVfb ) * (dw - xPixels); + n2 = ImVfbQNBytes( sVfb ) * xPixels; + + for( i = 0, y = yPixels; i < dh; i++, y++ ) + { + if ( y == dh ) + { + y = 0; + pdst = ImVfbQPtr( dstVfb, xPixels, 0 ); + pdst2 = ImVfbQPtr( dstVfb, 0, 0 ); + } + if ( n1 != 0 ) + memcpy( (void *)pdst, (void *)psrc, n1 ); + if ( n2 != 0 ) + memcpy( (void *)pdst2, (void *)psrc2, n2 ); + ImVfbSDown( sVfb, psrc ); + ImVfbSDown( sVfb, psrc2 ); + ImVfbSDown( dstVfb, pdst ); + ImVfbSDown( dstVfb, pdst2 ); + } + + if ( sourceVfb != sVfb ) + ImVfbFree( sVfb ); + return( dstVfb ); + } + + + psrc = ImVfbQFirst( sVfb ); + for ( i = 0, y = yPixels; i < dh; i++, y++ ) + { + pdst = ImVfbQPtr( dstVfb, xPixels, y ); + if ( y == dh ) + { + y = 0; + pdst = ImVfbQPtr( dstVfb, xPixels, 0 ); + } + for ( j = 0, x = xPixels; j < dw; j++, x++ ) + { + if ( x == dw ) + { + x = 0; + pdst = ImVfbQPtr( dstVfb, 0, y ); + } + if ( fieldMask & IMVFBMONO ) + ImVfbSMono( dstVfb, pdst, ImVfbQMono( sVfb, psrc ) ); + if ( fieldMask & IMVFBINDEX8 ) + ImVfbSIndex8( dstVfb, pdst, ImVfbQIndex8( sVfb, psrc ) ); + if ( fieldMask & IMVFBINDEX16 ) + ImVfbSIndex16( dstVfb, pdst, ImVfbQIndex16( sVfb, psrc ) ); + if ( fieldMask & IMVFBRGB ) + { + ImVfbSRed( dstVfb, pdst, ImVfbQRed( sVfb, psrc ) ); + ImVfbSGreen( dstVfb, pdst, ImVfbQGreen( sVfb, psrc ) ); + ImVfbSBlue( dstVfb, pdst, ImVfbQBlue( sVfb, psrc ) ); + } + if ( fieldMask & IMVFBALPHA ) + ImVfbSAlpha( dstVfb, pdst, ImVfbQAlpha( sVfb, psrc ) ); + if ( fieldMask & IMVFBIDATA ) + ImVfbSIData( dstVfb, pdst, ImVfbQIData( sVfb, psrc ) ); + if ( fieldMask & IMVFBFDATA ) + ImVfbSFData( dstVfb, pdst, ImVfbQFData( sVfb, psrc ) ); + if ( fieldMask & IMVFBZ ) + ImVfbSZ( dstVfb, pdst, ImVfbQZ( sVfb, psrc ) ); + if ( fieldMask & IMVFBWPROT ) + ImVfbSWProt( dstVfb, pdst, ImVfbQWProt( sVfb, psrc ) ); + ImVfbSInc( sVfb, psrc ); + ImVfbSInc( dstVfb, pdst ); + } + } + + if ( sourceVfb != sVfb ) + ImVfbFree( sVfb ); + return ( dstVfb ); +} + + + + + +/* + * FUNCTION + * ImVfbFill - fill a subarea within a virtual frame buffer. + * + * DESCRIPTION + * The source is copied to the destination. The rectangular region + * in the image is scanned and each pixel's fillField filled with a + * new pixel value calculated as a ramp in the 'graduate' direction + * between the fill start and end values. + * + * The majority of the code here is just to check that all the incomming + * arguments make sense! + */ +ImVfb * /* Returns filled destination vfb */ +#ifdef __STDC__ +ImVfbFill(ImVfb* srcVfb, int srcXLeft, int srcYTop, int srcDx, int srcDy, + int fillField, double fillStart, double fillEnd, int inOut, int graduate, ImVfb* dstVfb ) +#else +ImVfbFill( srcVfb, srcXLeft, srcYTop, srcDx, srcDy, + fillField, fillStart, fillEnd, inOut, graduate, dstVfb ) + + ImVfb *srcVfb; /* Source vfb */ + int srcXLeft; /* upper lefthand corner of area */ + int srcYTop; + int srcDx; /* Width of area */ + int srcDy; /* Height of area */ + int fillField; /* Mask for the incoming data */ + double fillStart; /* Value to start replacing in fieldmask*/ + double fillEnd; /* Value to end replacing in fieldmask */ + int inOut; /* whether to fill inside or outside area*/ + int graduate; /* which direction to graduate in */ + ImVfb *dstVfb; /* Destination vfb */ +#endif +{ + int i; /* Temp value holder */ + int fields; /* Image fields */ + int sxl, sxr, syb, syt; /* Fill region */ + int x, y; /* fill area width and height */ + int w, h; /* Image width and height */ + float xInc, yInc; /* X and Y direction grad increments */ + + + /* + * Clamp location and size values to the boundaries of the image. + */ + if ( srcVfb == IMVFBNULL ) + { + ImErrNo = IMENOVFB; + return ( IMVFBNULL ); + } + w = ImVfbQWidth( srcVfb ); + sxl = srcXLeft; + if ( sxl < 0 ) sxl = 0; + if ( sxl >= w ) sxl = w - 1; + + sxr = srcXLeft + srcDx - 1; + if ( sxr < 0 ) sxr = 0; + if ( sxr >= w ) sxr = w - 1; + + h = ImVfbQHeight( srcVfb ); + syt = srcYTop; + if ( syt < 0 ) syt = 0; + if ( syt >= h ) syt = h - 1; + + syb = srcYTop + srcDy - 1; + if ( syb < 0 ) syb = 0; + if ( syb >= h ) syb = h - 1; + + + /* + * Swap left & right, top & bottom, if necessary. + */ + if ( sxl > sxr ) + { + i = sxl; sxl = sxr; sxr = i; + } + if ( syt > syb ) + { + i = syb; syb = syt; syt = i; + } + + + /* + * Confirm widths are legal. + */ + x = sxr - sxl + 1; + if ( x <= 0 ) + { + ImErrNo = IMEWIDTH; + return( IMVFBNULL ); + } + y = syb - syt + 1; + if ( y <= 0 ) + { + ImErrNo = IMEHEIGHT; + return( IMVFBNULL ); + } + + + /* + * Figure out what fields we require be in the source & destination + * and make sure they are there. + */ + fields = 0; + if ( fillField & (IMRED | IMGREEN | IMBLUE | IMHUE | IMSATURATION | IMINTENSITY) ) + fields |= IMVFBRGB; + if ( fillField & IMMONO ) + fields |= IMVFBMONO; + if ( fillField & IMINDEX8 ) + fields |= IMVFBINDEX8; + if ( fillField & IMINDEX16 ) + fields |= IMVFBINDEX16; + if ( fillField & IMALPHA ) + fields |= IMVFBALPHA; + if ( fillField & IMWPROT ) + fields |= IMVFBWPROT; + if ( fillField & IMZ ) + fields |= IMVFBZ; + if ( fillField & IMIDATA ) + fields |= IMVFBIDATA; + if ( fillField & IMFDATA ) + fields |= IMVFBFDATA; + if ( (fields & ImVfbQFields( srcVfb )) != fields ) + { + ImErrNo = IMEFIELD; + return ( IMVFBNULL ); + } + + + /* + * Make sure the fill range is good. + */ + switch ( fillField ) + { + case IMMONO: + if ( fillStart < 0.0 ) fillStart = 0.0; + else if ( fillStart > 1.0 ) fillStart = 1.0; + if ( fillEnd < 0.0 ) fillEnd = 0.0; + else if ( fillEnd > 1.0 ) fillEnd = 1.0; + break; + + case IMRED: + case IMGREEN: + case IMBLUE: + case IMALPHA: + case IMINDEX8: + case IMWPROT: + if ( fillStart < 0.0 ) fillStart = 0.0; + else if ( fillStart > 255.0 ) fillStart = 255.0; + if ( fillEnd < 0.0 ) fillEnd = 0.0; + else if ( fillEnd > 255.0 ) fillEnd = 255.0; + break; + + case IMINDEX16: + if ( fillStart < 0.0 ) fillStart = 0.0; + else if ( fillStart > 65535.0 ) fillStart = 65535.0; + if ( fillEnd < 0.0 ) fillEnd = 0.0; + else if ( fillEnd > 65535.0 ) fillEnd = 65535.0; + break; + + case IMHUE: + if ( fillStart < 0.0 ) fillStart = 0.0; + else if ( fillStart > 360.0 ) fillStart = 360.0; + if ( fillEnd < 0.0 ) fillEnd = 0.0; + else if ( fillEnd > 360.0 ) fillEnd = 360.0; + break; + + case IMSATURATION: + case IMINTENSITY: + if ( fillStart < 0.0 ) fillStart = 0.0; + else if ( fillStart > 1.0 ) fillStart = 1.0; + if ( fillEnd < 0.0 ) fillEnd = 0.0; + else if ( fillEnd > 1.0 ) fillEnd = 1.0; + break; + + case IMZ: + if ( fillStart < 0.0 ) fillStart = 0.0; + if ( fillEnd < 0.0 ) fillEnd = 0.0; + break; + + case IMIDATA: + case IMFDATA: + /* All values allowed. */ + break; + + default: + ImErrNo = IMEFIELD; + return ( IMVFBNULL ); + } + + + /* + * Determine the pixel-to-pixel value increment. Could be negative. + * + * The increment is computed as follows: + * If the first pixel is color s, and the last is color e, + * and there are n pixels, and the increment is i, then + * we have s + (n-1)i = e. Or i = (e-s)/(n-1). + */ + switch ( graduate ) + { + case IMGRADHORIZ: /* Horizontal graduation. */ + yInc = 0.0; + xInc = fillEnd - fillStart; + switch ( inOut ) + { + case IMVFBINSIDE: if (x!=1) xInc /= (x-1); break; + case IMVFBOUTSIDE: if (w!=1) xInc /= (w-1); break; + default: + ImErrNo = IMEBADINOUT; + return ( IMVFBNULL ); + } + break; + + case IMGRADVERT: /* Vertical graduation. */ + xInc = 0.0; + yInc = fillEnd - fillStart; + switch ( inOut ) + { + case IMVFBINSIDE: if (y!=1) yInc /= (y-1); break; + case IMVFBOUTSIDE: if (h!=1) yInc /= (h-1); break; + default: + ImErrNo = IMEBADINOUT; + return ( IMVFBNULL ); + } + break; + + case IMGRADNONE: /* No graduation. */ + xInc = yInc = 0.0; + switch ( inOut ) + { + case IMVFBINSIDE: + case IMVFBOUTSIDE: + break; + default: + ImErrNo = IMEBADINOUT; + return ( IMVFBNULL ); + } + break; + + default: + ImErrNo = IMEGRADUATION; + return( IMVFBNULL ); + } + + + /* + * Make a destination VFB, if needed. + */ + if ( srcVfb != dstVfb ) + { + dstVfb = ImVfbCopy( srcVfb, 0, 0, w, h, IMVFBALL, dstVfb,0,0); + if ( dstVfb == IMVFBNULL ) + return ( IMVFBNULL ); /* ImErrNo already set */ + } + + + /* + * Fill the image. + */ + if ( inOut == IMVFBINSIDE ) + { + imVfbFillRect( dstVfb, sxl, sxr, syt, syb, + xInc, yInc, fillStart, fillField ); + } + else + { + /* + * Fill 4 rectangles on the 4 sides of the area to be left + * unfilled. + */ + switch ( graduate ) + { + case IMGRADHORIZ: + imVfbFillRect( dstVfb, 0, w-1, 0, syt, + xInc, yInc, fillStart, fillField ); + imVfbFillRect( dstVfb, 0, sxl, syt, syb, + xInc, yInc, fillStart, fillField ); + imVfbFillRect( dstVfb, 0, w-1, syb, h-1, + xInc, yInc, fillStart, fillField ); + imVfbFillRect( dstVfb, sxr, w-1, syt, syb, + xInc, yInc, fillStart + sxr*xInc, fillField ); + break; + case IMGRADVERT: + imVfbFillRect( dstVfb, 0, sxl, 0, h-1, + xInc, yInc, fillStart, fillField ); + imVfbFillRect( dstVfb, sxl, sxr, 0, syt, + xInc, yInc, fillStart, fillField ); + imVfbFillRect( dstVfb, sxr, w-1, 0, h-1, + xInc, yInc, fillStart, fillField ); + imVfbFillRect( dstVfb, sxl, sxr, syb, h-1, + xInc, yInc, fillStart + syb*yInc, fillField ); + break; + case IMGRADNONE: + imVfbFillRect( dstVfb, 0, w-1, 0, syt, + xInc, yInc, fillStart, fillField ); + imVfbFillRect( dstVfb, 0, sxl, syt, syb, + xInc, yInc, fillStart, fillField ); + imVfbFillRect( dstVfb, 0, w-1, syb, h-1, + xInc, yInc, fillStart, fillField ); + imVfbFillRect( dstVfb, sxr, w-1, syt, syb, + xInc, yInc, fillStart, fillField ); + break; + } + } + + return ( dstVfb ); +} + + + + + +/* + * FUNCTION + * imVfbFillRect - fill a rectangle in a source image + * + * DESCRIPTION + * The rectangular region is looped through and each pixel's fillField + * changed to a fill value calculated based on a starting value and + * X and Y direction increments. + */ +static void /* Returns nothing */ +#ifdef __STDC__ +imVfbFillRect( ImVfb* dstVfb, int sxl, int sxr, int syt, int syb, double xInc, double yInc, double fillStart, int fillField ) +#else +imVfbFillRect( dstVfb, sxl, sxr, syt, syb, xInc, yInc, fillStart, fillField ) + ImVfb *dstVfb; /* Destination VFB */ + int sxl, sxr, syt, syb; /* Rectangle to fill */ + double xInc, yInc; /* Directional pixel increments */ + double fillStart; /* Starting fill value */ + int fillField; /* Field of VFB to fill */ +#endif +{ + ImVfbPtr pDst; /* VFB pixel pointer */ + int i,j; /* counters */ + float pixel; /* New pixel value */ + float hsi[3]; /* HSI pixel value */ + int rgb[3]; /* RGB pixel value */ + + + for ( i = syt; i <= syb; i++ ) + { + pDst = ImVfbQPtr( dstVfb, sxl, i ); + pixel = fillStart + (i-syt) * yInc; + switch ( fillField ) + { + case IMRED: + for ( j = sxl; j <= sxr; j++ ) + { + ImVfbSRed( dstVfb, pDst, (int)pixel ); + ImVfbSInc( dstVfb, pDst ); + pixel += xInc; + } + break; + case IMGREEN: + for ( j = sxl; j <= sxr; j++ ) + { + ImVfbSGreen( dstVfb, pDst, (int)pixel ); + ImVfbSInc( dstVfb, pDst ); + pixel += xInc; + } + break; + case IMBLUE: + for ( j = sxl; j <= sxr; j++ ) + { + ImVfbSBlue( dstVfb, pDst, (int)pixel ); + ImVfbSInc( dstVfb, pDst ); + pixel += xInc; + } + break; + case IMINDEX8: + for ( j = sxl; j <= sxr; j++ ) + { + ImVfbSIndex8( dstVfb, pDst, (int)pixel ); + ImVfbSInc( dstVfb, pDst ); + pixel += xInc; + } + break; + case IMINDEX16: + for ( j = sxl; j <= sxr; j++ ) + { + ImVfbSIndex16( dstVfb, pDst, (int)pixel ); + ImVfbSInc( dstVfb, pDst ); + pixel += xInc; + } + break; + case IMMONO: + for ( j = sxl; j <= sxr; j++ ) + { + ImVfbSMono( dstVfb, pDst, (int)pixel ); + ImVfbSInc( dstVfb, pDst ); + pixel += xInc; + } + break; + case IMHUE: + case IMSATURATION: + case IMINTENSITY: + for ( j = sxl; j <= sxr; j++ ) + { + + rgb[0] = ImVfbQRed( dstVfb, pDst ); + rgb[1] = ImVfbQGreen( dstVfb, pDst ); + rgb[2] = ImVfbQBlue( dstVfb, pDst ); + ImRgbToHsi( rgb, hsi ); + switch ( fillField ) + { + case IMHUE: hsi[0] = pixel; break; + case IMSATURATION: hsi[1] = pixel; break; + case IMINTENSITY: hsi[2] = pixel; break; + } + ImHsiToRgb( hsi, rgb ); + ImVfbSRed( dstVfb, pDst, rgb[0] ); + ImVfbSGreen( dstVfb, pDst, rgb[1] ); + ImVfbSBlue( dstVfb, pDst, rgb[2] ); + ImVfbSInc( dstVfb, pDst ); + pixel += xInc; + } + break; + case IMALPHA: + for ( j = sxl; j <= sxr; j++ ) + { + ImVfbSAlpha( dstVfb, pDst, (int)pixel ); + ImVfbSInc( dstVfb, pDst ); + pixel += xInc; + } + break; + case IMWPROT: + for ( j = sxl; j <= sxr; j++ ) + { + ImVfbSWProt( dstVfb, pDst, (int)pixel ); + ImVfbSInc( dstVfb, pDst ); + pixel += xInc; + } + break; + case IMZ: + for ( j = sxl; j <= sxr; j++ ) + { + ImVfbSZ( dstVfb, pDst, (int)pixel ); + ImVfbSInc( dstVfb, pDst ); + pixel += xInc; + } + break; + case IMFDATA: + for ( j = sxl; j <= sxr; j++ ) + { + ImVfbSFData( dstVfb, pDst, pixel ); + ImVfbSInc( dstVfb, pDst ); + pixel += xInc; + } + break; + case IMIDATA: + for ( j = sxl; j <= sxr; j++ ) + { + ImVfbSIData( dstVfb, pDst, (int)pixel ); + ImVfbSInc( dstVfb, pDst ); + pixel += xInc; + } + break; + } + } +} + + +/* + * FUNCTION + * ImVfbPrint + * + * DESCRIPTION + * Print the pixels of a vfb, as floats or integers, as + * specified by the 'format' parameter. (This should be + * IMVFBPRINTFLOAT or IMVFBPRINTINT) + * + */ + +void /* returns nothing */ +#ifdef __STDC__ +ImVfbPrint(FILE* fp, int format, ImVfb* vfb) +#else +ImVfbPrint(fp, format, vfb) +FILE* fp; /* where to direct the output */ +int format; /* the format of the numbers */ +ImVfb* vfb; /* the vfb */ +#endif +{ + int fields; /* The fields in the vfb. */ + int i; /* loop index */ + ImVfbPtr vfbptr; /* Points into the vfb */ + + /* Determine the fields in the vfb. */ + fields = 0; + fields = ImVfbQFields(vfb); + + vfbptr = ImVfbQPtr(vfb, 0, 0); + for (i=0; i < ImVfbQWidth(vfb) * ImVfbQHeight(vfb); i++) + { + /* Print this pixel */ + + if (format & IMVFBPRINTFLOAT) + { + if (fields & IMVFBRGB) + { + fprintf( fp, "%f %f %f ", + (float)(ImVfbQRed ( vfb, vfbptr )) / 255.0, + (float)(ImVfbQGreen( vfb, vfbptr )) / 255.0, + (float)(ImVfbQBlue ( vfb, vfbptr )) / 255.0 ); + } + if (fields & IMVFBALPHA) + { + fprintf( fp, "%f ", + (float)(ImVfbQAlpha (vfb, vfbptr )) / 255.0 ); + } + if (fields & IMVFBINDEX8) + { + fprintf( fp, "%f ", + (float)(ImVfbQIndex( vfb, vfbptr )) / 255.0 ); + } + if (fields & IMVFBINDEX16) + { + fprintf( fp, "%f ", + (float)(ImVfbQIndex16( vfb, vfbptr )) / 65535.0 ); + } + fprintf( fp, " "); + } + if (format & IMVFBPRINTINT) + { + if (fields & IMVFBRGB) + { + fprintf( fp, "%d %d %d ", + ImVfbQRed ( vfb, vfbptr ), + ImVfbQGreen( vfb, vfbptr ), + ImVfbQBlue ( vfb, vfbptr )); + } + if (fields & IMVFBALPHA) + { + fprintf( fp, "%d ", + ImVfbQAlpha (vfb, vfbptr ) ); + } + if (fields & IMVFBINDEX8) + { + fprintf( fp, "%d ", + ImVfbQIndex( vfb, vfbptr ) ); + } + if (fields & IMVFBINDEX16) + { + fprintf( fp, "%ld ", + ImVfbQIndex16( vfb, vfbptr ) ); + } + } + fprintf(fp,"\n"); + vfbptr = ImVfbQNext(vfb, vfbptr); + } /* End of pixel loop */ +} + + +/* + * FUNCTION + * ImGetTransparency + * + * DESCRIPTION + * The transparency value of an image is an index in the color + * lookup table. Pixels with this index are deemed transparent. + * While RGB images use alpha channels for transparency, indexed + * images sometimes use transparency values for transparency. + * + * This routine looks in the flags table for a request, + * then looks in the tagTable for a value, and returns the + * transparency value of the image based on these two checks. + * + * The vfb passed to this routine must be of type INDEX8 + * If there is a transparency value in the tagTable, or a + * request in the flagsTable, this routine figures out that + * value, and returns it. + * + * An "image transparency request" in the flags table may have one + * of the following values: + * + * "most common" <- means we choose the most common value in the image + * "index=nn" <- means pixels with nn should be transparent + * "rgb=rr gg bb" <- means pixels with rr gg bb should be tranparent + * + * If there is no transparency request in either table, we return -1. + * + */ +int /* returns an index value or -1 */ +#ifdef __STDC__ +ImGetTransparency(TagTable* tagTable, TagTable* flagsTable, ImVfb* vfb) +#else +ImGetTransparency(tagTable, flagsTable, vfb) +TagTable* tagTable; +TagTable* flagsTable; +ImVfb* vfb; +#endif +{ + TagEntry* tagEntry; /* entry */ + char* request; /* the request */ + int transInt; /* transparency value as an integer */ + int redInt=-1, + greenInt=0, blueInt=0; /* rgb requested values */ + ImClt* clt; /* image clt */ + ImCltPtr cltPtr; /* points into the clt */ + ImHistTable* histogram=NULL; /* a histogram */ + long int largestWeight; /* biggest weight */ + long int thisWeight; /* one weight */ + int field; /* mono or index8 */ + char message[200]; /* message buffer */ + int transColor=-1; /* The value to return */ + int numPixels; /* # of pix w/ this index value */ + int i; + + + /* + * First look for a request in the flags table + */ + if (flagsTable!=TAGTABLENULL) + tagEntry = TagTableQDirect(flagsTable, "image transparency request", 0); + else + tagEntry=TAGENTRYNULL; + + /* + * then check the tagTable + */ + if (tagEntry==TAGENTRYNULL) + { + tagEntry = TagTableQDirect(tagTable, "transparency value", 0); + } + + if (tagEntry==TAGENTRYNULL) + { /* No transparency! */ + return -1; + } + + + /* + * There is transparency. + * What should be do with it? + */ + + TagEntryQValue( tagEntry, &request); + + /* + * The request can be one of the following: + * "most common" <- means we choose the most common value in the image + * "index=nn" <- means pixels with nn should be transparent + * "rgb=rr gg bb" <- means pixels with rr gg bb should be tranparent + */ + + if (strncmp(request,"index",5)==0) + { + sscanf(request,"index=%d",&transInt); + transColor = (unsigned char)transInt; + } + else if (strncmp(request,"rgb",3)==0) + { + /* Find the first entry with this rgb value */ + sscanf(request,"rgb=%d %d %d",&redInt, &greenInt, &blueInt); + clt = ImVfbQClt(vfb); + transInt = -1; + cltPtr = ImCltQFirst(clt); + for (i=0; i< ImCltQNColors(clt); i++) + { + if ( (redInt == ImCltQRed(cltPtr)) && + (greenInt==ImCltQGreen(cltPtr)) && + (blueInt ==ImCltQBlue (cltPtr))) + { + transInt = i; + i = ImCltQNColors(clt)+1; + } + cltPtr = ImCltQNext (clt, cltPtr ); + } + + if (transInt==-1) + { + sprintf(message,"Couldn't find any %d, %d, %d pixels in the image!", + redInt, greenInt, blueInt); + ImErrorWarning(message, IMEVALOUTOFRANGE, -1); + return 1; + } + transColor = (unsigned char) transInt; + } + else if (strcmp(request,"most common")==0) + { + if (ImVfbQFields(vfb) & IMVFBINDEX8) + field = IMINDEX8; + else + field = IMMONO; + histogram = ImVfbHist( vfb, field, 0 ); + + /* + * Step through the histogram. Get the most frequent color. + */ + largestWeight = (long) 0; + transColor = 1; + for (i=0; i< histogram->imhist_nEntries; i++) + { + numPixels = histogram->imhist_nOccur[i]; + thisWeight = numPixels; + if (thisWeight > largestWeight) + { + largestWeight = thisWeight; + if (field==IMINDEX8) + transColor = histogram->imhist_index8[i]; + else /* mono */ + transColor = histogram->imhist_mono[i]; + } + } + ImVfbHistTableFree(histogram); + } + else + { + sprintf(message,"Unknown transparency request: '%s'", + request); + ImErrorWarning(message, IMEVALOUTOFRANGE, -1); + return -1; + } + + return transColor; +} + + + diff --git a/utils/roq2/libim/imvfbadjust.c b/utils/roq2/libim/imvfbadjust.c new file mode 100644 index 0000000..26fb136 --- /dev/null +++ b/utils/roq2/libim/imvfbadjust.c @@ -0,0 +1,1809 @@ +/** + + ** $Header: /roq/libim/imvfbadjust.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/imvfbadjust.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** imvfbadjust.c - adjust a VFB + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** imvfbadjust.c contains routines for adjusting components of pixels in a VFB + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** ImVfbAdjust f adjust the field values of a virtual frame buffer + + ** + + ** PRIVATE CONTENTS + + ** imAdjustPixel m adjust a pixel component + + ** imCheckField m check that a field value is within legal range + + ** IM_ERROR m return an error + + ** + + ** HISTORY + + ** $Log: /roq/libim/imvfbadjust.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.7 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.6 1994/10/03 11:29:45 nadeau + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Updated comments. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.5 92/12/03 01:53:59 nadeau + + ** Changed names of ImRgbHsi and ImHsiRgb to new names. + + ** + + ** Revision 1.4 92/09/01 20:11:06 vle + + ** Updated copyright notice. + + ** + + ** Revision 1.3 92/09/01 12:24:45 nadeau + + ** Rearranged and rewrote much of the code to add in additional + + ** error checks, support keying off of non-RGB images when given + + ** RGB or HSI key fields, support monochrome, write protect, + + ** Z-buffer, integer data, and float data fields. + + ** + + ** Revision 1.2 92/08/26 12:45:45 groening + + ** minor error corrections + + ** + + ** Revision 1.1 92/08/10 08:52:09 groening + + ** Initial revision + + ** + + ** + + **/ + + + +/** + + ** CODE CREDITS + + ** Custom development, Chris Groening, San Diego Supercomputer Center, 1992. + + ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1992. + + **/ + +#include "iminternal.h" + + + + + + + + + + + +/* + + * MACRO + + * imAdjustPixel - adjust a pixel component + + * + + * DESCRIPTION + + * Used by each of the adjustment operators of ImVfbAdjust(), + + * this macro queries a pixel's component value, adjusts it as + + * requested, checks that it is a legal value, then saves it + + * back to the VFB. + + * + + * The macro is provide to encapsulate an algorithm that is + + * nearly identical for each adjustment operator. Note that + + * the 'op' argument is a C operator, like "+=" or "*=" that + + * is substituted into the code. It is not a variable and is + + * not a character string. This macro should be invoked with + + * a statement like: + + * + + * imAdjustPixel( += ) + + * + + * in order to implement an ADD operation. Pretty tricky, huh? + + */ + +#define imAdjustPixel(op) \ +{ \ + float hsi[3]; \ + int rgb[3]; \ + switch ( adjustField ) \ + { \ + case IMRED: \ + pixel = ImVfbQRed( dstVfb, pDst ); \ + pixel op value; \ + if ( pixel > 255 ) pixel = 255; \ + if ( pixel < 0 ) pixel = 0; \ + ImVfbSRed( dstVfb, pDst, (int)pixel ); \ + break; \ + case IMGREEN: \ + pixel = ImVfbQGreen( dstVfb, pDst ); \ + pixel op value; \ + if ( pixel > 255 ) pixel = 255; \ + if ( pixel < 0 ) pixel = 0; \ + ImVfbSGreen( dstVfb, pDst, (int)pixel ); \ + break; \ + case IMBLUE: \ + pixel = ImVfbQBlue( dstVfb, pDst ); \ + pixel op value; \ + if ( pixel > 255 ) pixel = 255; \ + if ( pixel < 0 ) pixel = 0; \ + ImVfbSBlue( dstVfb, pDst, (int)pixel ); \ + break; \ + case IMINDEX8: \ + pixel = ImVfbQIndex8( dstVfb, pDst ); \ + pixel op value; \ + if ( pixel > 255 ) pixel = 255; \ + if ( pixel < 0 ) pixel = 0; \ + ImVfbSIndex8( dstVfb, pDst, (int)pixel ); \ + break; \ + case IMINDEX16: \ + pixel = ImVfbQIndex16( dstVfb, pDst ); \ + pixel op value; \ + if ( pixel > 65535 ) pixel = 65535; \ + if ( pixel < 0 ) pixel = 0; \ + ImVfbSIndex16( dstVfb, pDst, (int)pixel ); \ + break; \ + case IMMONO: \ + pixel = ImVfbQMono( dstVfb, pDst ); \ + pixel op value; \ + if ( pixel > 1 ) pixel = 1; \ + if ( pixel < 0 ) pixel = 0; \ + ImVfbSMono( dstVfb, pDst, (int)pixel ); \ + break; \ + case IMHUE: \ + case IMSATURATION: \ + case IMINTENSITY: \ + rgb[0] = ImVfbQRed( dstVfb, pDst ); \ + rgb[1] = ImVfbQGreen( dstVfb, pDst ); \ + rgb[2] = ImVfbQBlue( dstVfb, pDst ); \ + ImRgbToHsi( rgb, hsi ); \ + if ( adjustField == IMHUE ) hsi[0] op value;\ + else if ( adjustField == IMSATURATION) hsi[1] op value;\ + else hsi[2] op value;\ + ImHsiToRgb( hsi, rgb ); \ + ImVfbSRed( dstVfb, pDst, rgb[0] ); \ + ImVfbSGreen( dstVfb, pDst, rgb[1] ); \ + ImVfbSBlue( dstVfb, pDst, rgb[2] ); \ + break; \ + case IMALPHA: \ + pixel = ImVfbQAlpha( dstVfb, pDst ); \ + pixel op value; \ + if ( pixel > 255 ) pixel = 255; \ + if ( pixel < 0 ) pixel = 0; \ + ImVfbSAlpha( dstVfb, pDst, (int)pixel ); \ + break; \ + case IMWPROT: \ + pixel = ImVfbQWProt( dstVfb, pDst ); \ + pixel op value; \ + if ( pixel > 255 ) pixel = 255; \ + if ( pixel < 0 ) pixel = 0; \ + ImVfbSWProt( dstVfb, pDst, (int)pixel ); \ + break; \ + case IMZ: \ + pixel = ImVfbQZ( dstVfb, pDst ); \ + pixel op value; \ + ImVfbSZ( dstVfb, pDst, (int)pixel ); \ + break; \ + case IMFDATA: \ + pixel = ImVfbQFData( dstVfb, pDst ); \ + pixel op value; \ + ImVfbSFData( dstVfb, pDst, pixel ); \ + break; \ + case IMIDATA: \ + pixel = ImVfbQIData( dstVfb, pDst ); \ + pixel op value; \ + ImVfbSIData( dstVfb, pDst, (int)pixel ); \ + break; \ + } \ +} + + + + + + + + + +/* + + * MACRO + + * IM_ERROR - return an error code + + * + + * DESCRIPTION + + * This macro encapsulates setting ImErrNo and returning and is + + * used just to make the code shorter and easier to read. + + */ + +#define IM_ERROR(errnum) \ +{ \ + ImErrNo = (errnum); \ + return( IMVFBNULL ); \ +} + + + + + + + + + + + +/* + + * MACRO + + * imCheckField - check that a value is within a legal range + + * + + * DESCRIPTION + + * This macro is used to check each of the key and adjust values + + * to make sure they are within acceptable ranges. + + */ + +#define imCheckField(extent,error) \ +{ \ + switch ( operation ) \ + { \ + case IMADD: \ + case IMSUBTRACT: \ + if ( adjustLow < -(extent) || adjustHigh > (extent) ) \ + IM_ERROR( error ); \ + break; \ + case IMMULTIPLY: \ + if ( adjustLow < 0 ) \ + IM_ERROR( error ); \ + break; \ + case IMDIVIDE: \ + if ( adjustLow <= 0 ) \ + IM_ERROR( error ); \ + break; \ + case IMSET: \ + if ( adjustLow < 0 || adjustHigh > (extent) ) \ + IM_ERROR( error ); \ + break; \ + } \ +} + + + + + + + + + + + +/* + + * FUNCTION + + * ImVfbAdjust - adjust the fields in a virtual frame buffer. + + * + + * DESCRIPTION + + * The source image's pixels are adjusted and written to the destination + + * image. Adjustements are performed on pixels selected by the 'key' + + * arguments and modify those pixel's 'adjust' fields using the given + + * operation. + + * + + * The majority of the code here is just to check that all the incomming + + * arguments make sense. Whew! + + */ + + + +#ifdef __STDC__ + +ImVfb * /* Returns filled destination vfb */ + +ImVfbAdjust( ImVfb *srcVfb, int keyField, double keyStart, double keyEnd, int operation, + + int adjustField, double adjustStart, double adjustEnd, ImVfb *dstVfb ) + +#else + +ImVfb * /* Returns filled destination vfb */ + +ImVfbAdjust( srcVfb, keyField, keyStart, keyEnd, operation, + + adjustField, adjustStart, adjustEnd, dstVfb) + + ImVfb *srcVfb; /* incoming vfb */ + + int keyField; /* what field to change */ + + double keyStart; /* Start range of field to change */ + + double keyEnd; /* End range of field to change */ + + int operation; /* What operation to perform on field */ + + int adjustField; /* what field to adjust */ + + double adjustStart; /* Start range of field to adjust */ + + double adjustEnd; /* End range of field to adjust */ + + ImVfb *dstVfb; /* Destination vfb */ + +#endif + +{ + + int x,y; /* Width and height of image */ + + int i,j; /* Counters */ + + float factor; /* Replacement value factor */ + + int fields; /* Fields of source VFB */ + + int pixelRgb[3]; /* RGB pixel value */ + + float pixelHsi[3]; /* HSI pixel value */ + + float pixel; /* Pixel value */ + + float value; /* Value with which to modify pixel */ + + ImClt *clt; /* Image's color lookup table */ + + ImVfbPtr pDst; /* Pointer to destination VFB */ + + float keyLow; /* Low key value */ + + float keyHigh; /* High key value */ + + float adjustLow; /* Low adjust value */ + + float adjustHigh; /* High adjust value */ + + + + + + /* + + * Check for legal key and adjust values. This is made a bit + + * tricky because of the following: + + * + + * 1. Different field types have different legal key value + + * ranges. + + * + + * 2. Replace value ranges depend on the operation being + + * performed, as well as upon the adjust field's type. + + * + + * Key fields are restricted as follows: + + * mono: 0 to 1 + + * red, green, blue, alpha, index8, wprot: 0 to 255 + + * index16: 0 to 65535 + + * z: 0 to maxint + + * idata: any int + + * fdata: any float + + * hue: 0 to 360.0 + + * saturation, intensity: 0 to 1.0 + + * + + * Adjust fields are restricted as follows: + + * + + * Add and subtract: + + * It doesn't make much sense to add more, or subtract more + + * than the max or min field value. Doing so puts the result + + * beyond the max or min, and will just get clamped back to + + * the max or min anyway. + + * mono: -1 to 1 + + * red, green, blue, alpha, index8, wprot: -255 to 255 + + * index16: -65535 to 65535 + + * z, idata, fdata: anything + + * hue: -360.0 to 360.0 + + * saturation, intensity: -1.0 to 1.0 + + * + + * Multiply: + + * Multiplication factors can be anything except negative. + + * Negative factors would produce negative results, which + + * would just get clamped back to 0 anyway. + + * mono: 0 to 1 + + * red, green, blue, alpha, index8, wprot: 0 to anything + + * index16: 0 to anything + + * z: 0 to anything + + * idata, fdata: anything + + * hue: 0 to anything + + * saturation, intensity: 0 to anything + + * + + * Divide: + + * Division factors can be anything except zero or negative. + + * Zero causes horrible things. Negative values would produce + + * negative results, which would just get clamped back to + + * 0 anyway. + + * mono: >0 to 1 + + * red, green, blue, alpha, index8, wprot: >0 to anything + + * index16: >0 to anything + + * z: >0 to anything + + * idata, fdata: !=0, anything + + * hue: >0 to anything + + * saturation, intensity: >0 to anything + + * + + * Set: + + * Set can be anything legal for the field. + + * mono: 0 to 1 + + * red, green, blue, alpha, index8, wprot: 0 to 255 + + * index16: 0 to 65535 + + * z: 0 to anything + + * idata, fdata: anything + + * hue: 0 to 360.0 + + * saturation, intensity: 0 to 1.0 + + */ + + + + /* + + * Get the low and high key and adjust values to make testing + + * extent faster. + + */ + + if ( keyStart < keyEnd ) + + { + + keyLow = keyStart; + + keyHigh = keyEnd; + + } + + else + + { + + keyHigh = keyStart; + + keyLow = keyEnd; + + } + + if ( adjustStart < adjustEnd ) + + { + + adjustLow = adjustStart; + + adjustHigh = adjustEnd; + + } + + else + + { + + adjustHigh = adjustStart; + + adjustLow = adjustEnd; + + } + + + + + + /* + + * Make sure the key field and key range are good. + + */ + + switch ( keyField ) + + { + + case IMMONO: + + if ( keyLow < 0.0 || keyHigh > 1.0 ) + + IM_ERROR( IMEKEY ); + + break; + + + + case IMRED: + + case IMGREEN: + + case IMBLUE: + + case IMALPHA: + + case IMINDEX8: + + case IMWPROT: + + if ( keyLow < 0.0 || keyHigh > 255.0 ) + + IM_ERROR( IMEKEY ); + + break; + + + + case IMINDEX16: + + if ( keyLow < 0.0 || keyHigh > 65535.0 ) + + IM_ERROR( IMEKEY ); + + break; + + + + case IMHUE: + + if ( keyLow < 0.0 || keyHigh > 360.0 ) + + IM_ERROR( IMEKEY ); + + break; + + + + case IMSATURATION: + + case IMINTENSITY: + + if ( keyLow < 0.0 || keyHigh > 1.0 ) + + IM_ERROR( IMEKEY ); + + break; + + + + case IMZ: + + if ( keyLow < 0.0 ) + + IM_ERROR( IMEKEY ); + + break; + + + + case IMIDATA: + + case IMFDATA: + + /* All values allowed. */ + + break; + + + + default: + + IM_ERROR( IMEKEYFIELD ); + + } + + + + + + /* + + * Make sure the operation is good. + + */ + + switch ( operation ) + + { + + case IMADD: + + case IMSUBTRACT: + + case IMMULTIPLY: + + case IMDIVIDE: + + case IMSET: + + break; + + default: + + IM_ERROR( IMEOPERATION ); + + } + + + + + + /* + + * Make sure the adjust field and adjust range are good. + + */ + + switch ( adjustField ) + + { + + case IMMONO: + + imCheckField( 1.0, IMEADJUST ); + + break; + + + + case IMRED: + + case IMGREEN: + + case IMBLUE: + + case IMALPHA: + + case IMINDEX8: + + case IMWPROT: + + imCheckField( 255.0, IMEADJUST ); + + break; + + + + case IMINDEX16: + + imCheckField( 65535.0, IMEADJUST ); + + break; + + + + case IMZ: + + switch ( operation ) + + { + + case IMADD: + + case IMSUBTRACT: + + break; + + case IMMULTIPLY: + + case IMSET: + + if ( adjustLow < 0.0 ) + + IM_ERROR( IMEADJUST ); + + break; + + case IMDIVIDE: + + if ( adjustLow == 0.0 || adjustHigh == 0.0 ) + + IM_ERROR( IMEADJUST ); + + break; + + } + + break; + + + + case IMIDATA: + + case IMFDATA: + + if ( operation == IMDIVIDE && + + ( adjustLow == 0.0 || adjustHigh == 0.0 ) ) + + IM_ERROR( IMEADJUST ); + + break; + + + + case IMHUE: + + imCheckField( 360.0, IMEADJUST ); + + break; + + + + case IMSATURATION: + + case IMINTENSITY: + + imCheckField( 1.0, IMEADJUST ); + + break; + + } + + + + + + /* + + * Check that the source VFB has the fields we need to do the + + * adjustment. + + * + + * Key fields: + + * red, green, blue: source may have any color field type + + * hue, saturation, inten: source may have any color field type + + * mono: source must have MONO + + * index8: source must have INDEX8 + + * index16: source must have INDEX16 + + * alpha: source must have ALPHA + + * wprot: source must have WPROT + + * z: source must have Z + + * idata: source must have IDATA + + * fdata: source must have FDATA + + * + + * Replace fields: + + * red, green, blue: destination must have RGB. + + * hue, saturation, inten: destination must have RGB. + + * index8: destination must have INDEX8. + + * index16: destination must have INDEX16. + + * alpha: destination must have ALPHA. + + * wprot: destination must have WPROT + + * z: destination must have Z + + * idata: destination must have IDATA + + * fdata: destination must have FDATA + + */ + + fields = ImVfbQFields( srcVfb ); + + switch ( keyField ) + + { + + case IMMONO: + + if ( !(fields & IMVFBMONO) ) + + IM_ERROR( IMEKEYFIELD ); + + break; + + + + case IMRED: + + case IMGREEN: + + case IMBLUE: + + case IMHUE: + + case IMSATURATION: + + case IMINTENSITY: + + if ( !(fields&(IMVFBRGB|IMVFBINDEX8|IMVFBINDEX16|IMVFBMONO))) + + IM_ERROR( IMEKEYFIELD ); + + break; + + + + case IMINDEX8: + + if ( !(fields & IMVFBINDEX8) ) + + IM_ERROR( IMEKEYFIELD ); + + break; + + + + case IMINDEX16: + + if ( !(fields & IMVFBINDEX16) ) + + IM_ERROR( IMEKEYFIELD ); + + break; + + + + case IMALPHA: + + if ( !(fields & IMVFBALPHA) ) + + IM_ERROR( IMEKEYFIELD ); + + break; + + + + case IMWPROT: + + if ( !(fields & IMVFBWPROT) ) + + IM_ERROR( IMEKEYFIELD ); + + break; + + + + case IMZ: + + if ( !(fields & IMVFBZ) ) + + IM_ERROR( IMEKEYFIELD ); + + break; + + + + case IMIDATA: + + if ( !(fields & IMVFBIDATA) ) + + IM_ERROR( IMEKEYFIELD ); + + break; + + + + case IMFDATA: + + if ( !(fields & IMVFBFDATA) ) + + IM_ERROR( IMEKEYFIELD ); + + break; + + } + + switch ( adjustField ) + + { + + case IMMONO: + + if ( !(fields & IMVFBMONO) ) + + IM_ERROR( IMEADJUSTFIELD ); + + break; + + + + case IMRED: + + case IMGREEN: + + case IMBLUE: + + case IMHUE: + + case IMSATURATION: + + case IMINTENSITY: + + if ( !(fields & IMVFBRGB) ) + + IM_ERROR( IMEADJUSTFIELD ); + + break; + + + + case IMINDEX8: + + if ( !(fields & IMVFBINDEX8) ) + + IM_ERROR( IMEADJUSTFIELD ); + + break; + + + + case IMINDEX16: + + if ( !(fields & IMVFBINDEX16) ) + + IM_ERROR( IMEADJUSTFIELD ); + + break; + + + + case IMALPHA: + + if ( !(fields & IMVFBALPHA) ) + + IM_ERROR( IMEADJUSTFIELD ); + + break; + + + + case IMWPROT: + + if ( !(fields & IMVFBWPROT) ) + + IM_ERROR( IMEADJUSTFIELD ); + + break; + + + + case IMZ: + + if ( !(fields & IMVFBZ) ) + + IM_ERROR( IMEADJUSTFIELD ); + + break; + + + + case IMIDATA: + + if ( !(fields & IMVFBIDATA) ) + + IM_ERROR( IMEADJUSTFIELD ); + + break; + + + + case IMFDATA: + + if ( !(fields & IMVFBFDATA) ) + + IM_ERROR( IMEADJUSTFIELD ); + + break; + + } + + + + + + + + /* + + * Copy the source into the destination, creating a new destination + + * if needed. ImVfbCopy() will catch problems where the given + + * destination VFB (if not IMVFBNEW) doesn't have the same fields as + + * the source, or isn't at least as large as the source. + + */ + + x = ImVfbQWidth( srcVfb ); + + y = ImVfbQHeight( srcVfb ); + + if ( srcVfb == IMVFBNULL ) + + IM_ERROR( IMENOVFB ); + + if ( srcVfb != dstVfb ) + + { + + dstVfb = ImVfbCopy( srcVfb, 0, 0, x, y, fields, dstVfb, 0,0); + + if ( dstVfb == IMVFBNULL ) + + return ( IMVFBNULL ); /* ImErrNo already set */ + + } + + + + + + /* + + * Prepare to adjust the image. + + */ + + if ( keyEnd != keyStart ) + + factor = (adjustEnd - adjustStart) / (keyEnd - keyStart); + + else + + factor = 0; + + + + clt = ImVfbQClt( dstVfb ); /* Might be IMCLTNULL */ + + pDst = ImVfbQFirst( dstVfb ); + + fields = ImVfbQFields( dstVfb ); + + + + + + /* + + * Simplify the field mask to the single key field we care about. + + */ + + switch ( keyField ) + + { + + case IMRED: + + case IMGREEN: + + case IMBLUE: + + case IMHUE: + + case IMSATURATION: + + case IMINTENSITY: + + fields &= IMVFBIMAGEMASK; + + break; + + default: + + fields = keyField; + + } + + + + + + + + /* + + * Adjust the image. + + * + + * Note that there are a lot of checks in the innermost pixel scan + + * loop that are loop-invariant. It would certainly speed things + + * up if they were moved out of the loop. Unfortunately, this + + * considerably increases the amount of the code since it requires + + * duplicating the loop for each combination of: + + * + + * keyField + + * red 4 combinations of dst VFB fields + + * green 4 combinations of dst VFB fields + + * blue 4 combinations of dst VFB fields + + * hue 4 combinations of dst VFB fields + + * saturation 4 combinations of dst VFB fields + + * intensity 4 combinations of dst VFB fields + + * index8 2 combinations of dst CLT existance + + * index16 2 combinations of dst CLT existance + + * mono 1 combination + + * alpha 1 combination + + * wprot 1 combination + + * z 1 combination + + * idata 1 combination + + * fdata 1 combination + + * -- + + * subtotal 34 + + * + + * operator 5 possible values + + * adjustField 10 possible values + + * --- + + * TOTAL 34 * 5 * 10 = 1700 variants + + * + + * Things clearly get out of hand quick. Additionally, the basic + + * algorithm gets spread out all over everywhere, even with liberal + + * use of macros. + + * + + * So, we'll pay the speed penalty in order to get less code and + + * easier maintenance. + + */ + + for ( i = 0; i < y; i++ ) + + { + + for ( j = 0; j < x; j++ ) + + { + + /* Get the pixel value depending upon keyfield. */ + + switch ( keyField ) + + { + + case IMRED: + + /* Get pixel value from VFB depending upon fields. */ + + switch ( fields ) + + { + + case IMVFBRGB: + + pixel = ImVfbQRed( dstVfb, pDst ); + + break; + + case IMVFBINDEX8: + + pixel = ImVfbQIndex8( dstVfb, pDst ); + + if ( clt != IMCLTNULL ) + + pixel = ImCltQRed( ImCltQPtr( clt, (int)pixel)); + + break; + + case IMVFBINDEX16: + + pixel = ImVfbQIndex16( dstVfb, pDst ); + + if ( clt != IMCLTNULL ) + + pixel = ImCltQRed( ImCltQPtr( clt, (int)pixel)); + + else + + pixel /= 255; + + break; + + case IMVFBMONO: + + pixel = ImVfbQMono( dstVfb, pDst ) * 255; + + break; + + } + + break; + + case IMGREEN: + + /* Get pixel value from VFB depending upon fields. */ + + switch ( fields ) + + { + + case IMVFBRGB: + + pixel = ImVfbQGreen( dstVfb, pDst ); + + break; + + case IMVFBINDEX8: + + pixel = ImVfbQIndex8( dstVfb, pDst ); + + if ( clt != IMCLTNULL ) + + pixel = ImCltQGreen( ImCltQPtr( clt, (int)pixel)); + + break; + + case IMVFBINDEX16: + + pixel = ImVfbQIndex16( dstVfb, pDst ); + + if ( clt != IMCLTNULL ) + + pixel = ImCltQGreen( ImCltQPtr( clt, (int)pixel)); + + else + + pixel /= 255; + + break; + + case IMVFBMONO: + + pixel = ImVfbQMono( dstVfb, pDst ) * 255; + + break; + + } + + break; + + case IMBLUE: + + /* Get pixel value from VFB depending upon fields. */ + + switch ( fields ) + + { + + case IMVFBRGB: + + pixel = ImVfbQBlue( dstVfb, pDst ); + + break; + + case IMVFBINDEX8: + + pixel = ImVfbQIndex8( dstVfb, pDst ); + + if ( clt != IMCLTNULL ) + + pixel = ImCltQBlue( ImCltQPtr( clt, (int)pixel)); + + break; + + case IMVFBINDEX16: + + pixel = ImVfbQIndex16( dstVfb, pDst ); + + if ( clt != IMCLTNULL ) + + pixel = ImCltQBlue( ImCltQPtr( clt, (int)pixel)); + + else + + pixel /= 255; + + break; + + case IMVFBMONO: + + pixel = ImVfbQMono( dstVfb, pDst ) * 255; + + break; + + } + + break; + + case IMINDEX8: + + pixel = ImVfbQIndex8( dstVfb, pDst ); + + break; + + case IMINDEX16: + + pixel = ImVfbQIndex16( dstVfb, pDst ); + + break; + + case IMMONO: + + pixel = ImVfbQMono( dstVfb, pDst ); + + break; + + case IMALPHA: + + pixel = ImVfbQAlpha( dstVfb, pDst ); + + break; + + case IMWPROT: + + pixel = ImVfbQWProt( dstVfb, pDst ); + + break; + + case IMZ: + + pixel = ImVfbQZ( dstVfb, pDst ); + + break; + + case IMIDATA: + + pixel = ImVfbQIData( dstVfb, pDst ); + + break; + + case IMFDATA: + + pixel = ImVfbQFData( dstVfb, pDst ); + + break; + + case IMHUE: + + case IMSATURATION: + + case IMINTENSITY: + + /* Get pixel value from VFB depending upon fields. */ + + switch ( fields ) + + { + + case IMVFBRGB: + + pixelRgb[0] = ImVfbQRed( dstVfb, pDst ); + + pixelRgb[1] = ImVfbQGreen( dstVfb, pDst ); + + pixelRgb[2] = ImVfbQBlue( dstVfb, pDst ); + + break; + + case IMVFBINDEX8: + + pixel = ImVfbQIndex8( dstVfb, pDst ); + + if ( clt != IMCLTNULL ) + + { + + pixelRgb[0] = ImCltQRed( ImCltQPtr( clt, (int)pixel)); + + pixelRgb[1] = ImCltQGreen( ImCltQPtr( clt, (int)pixel)); + + pixelRgb[2] = ImCltQBlue( ImCltQPtr( clt, (int)pixel)); + + } + + else + + { + + pixelRgb[0] = (int)pixel; + + pixelRgb[1] = (int)pixel; + + pixelRgb[2] = (int)pixel; + + } + + break; + + case IMVFBINDEX16: + + pixel = ImVfbQIndex16( dstVfb, pDst ); + + if ( clt != IMCLTNULL ) + + { + + pixelRgb[0] = ImCltQRed( ImCltQPtr( clt, (int)pixel)); + + pixelRgb[1] = ImCltQGreen( ImCltQPtr( clt, (int)pixel)); + + pixelRgb[2] = ImCltQBlue( ImCltQPtr( clt, (int)pixel)); + + } + + else + + { + + pixelRgb[0] = (int)pixel / 255; + + pixelRgb[1] = (int)pixel / 255; + + pixelRgb[2] = (int)pixel / 255; + + } + + break; + + case IMVFBMONO: + + pixel = ImVfbQMono( dstVfb, pDst ) * 255; + + pixelRgb[0] = (int)pixel; + + pixelRgb[1] = (int)pixel; + + pixelRgb[2] = (int)pixel; + + break; + + } + + ImRgbToHsi( pixelRgb, pixelHsi ); + + switch ( keyField ) + + { + + case IMHUE: pixel = pixelHsi[0]; break; + + case IMSATURATION: pixel = pixelHsi[1]; break; + + case IMINTENSITY: pixel = pixelHsi[2]; break; + + } + + break; + + } + + + + /* Check if the pixel is within the key range. */ + + if ( !(pixel >= keyStart && pixel <= keyEnd) ) + + { + + /* Nope! */ + + ImVfbSInc( dstVfb, pDst ); + + continue; + + } + + + + /* Compute the value to add, subtract, etc. */ + + value = adjustStart + factor * (pixel - keyStart); + + + + /* Update the pixel depending upon the operation. */ + + switch ( operation ) + + { + + case IMADD: + + imAdjustPixel( += ); + + break; + + case IMSUBTRACT: + + imAdjustPixel( -= ); + + break; + + case IMMULTIPLY: + + imAdjustPixel( *= ); + + break; + + case IMDIVIDE: + + imAdjustPixel( /= ); + + break; + + case IMSET: + + imAdjustPixel( = ); + + break; + + } + + ImVfbSInc( dstVfb, pDst ); + + } + + } + + + + return( dstVfb); + +} + diff --git a/utils/roq2/libim/imvfbchan.c b/utils/roq2/libim/imvfbchan.c new file mode 100644 index 0000000..edef2ef --- /dev/null +++ b/utils/roq2/libim/imvfbchan.c @@ -0,0 +1,1776 @@ +/** + + ** $Header: /roq/libim/imvfbchan.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/imvfbchan.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** imvfbchan.c - Image Library VFB channel interchanging + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** These functions permute / copy channels between VFBs. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** ImVfbCopyChannel f copy one channel of one vfb to + + ** another channel of a another vfb + + ** ImVfbProcessMapRequests f Permute the channels of a vfb + + ** + + ** PRIVATE CONTENTS + + ** none + + ** + + ** HISTORY + + ** $Log: /roq/libim/imvfbchan.c $ + * + * 1 11/02/99 4:38p Zaphod + + * Revision 1.6 1995/09/28 05:11:40 bduggan + + * fixed -inmap, -outmap bug + + * (default is to copy rgb channels + + * now when the alpha is copied.) + + * / + + * + + * Revision 1.5 1995/06/30 22:11:26 bduggan + + * removed strings.h + + * + + * Revision 1.4 1995/06/29 00:28:04 bduggan + + * updated copyright year + + * + + * Revision 1.3 1995/06/16 08:51:54 bduggan + + * added some casts. took out some unused vars + + * + + * Revision 1.2 1995/05/17 23:49:12 bduggan + + * Added copying for a few more channels (PROT, Z, DATA) + + * + + * Revision 1.1 1995/02/16 21:43:00 bduggan + + * Initial revision + + * + + **/ + + + +/** + + ** CODE CREDITS + + ** Custom development, Brian Duggan, San Diego Supercomputer Center, 1995 + + **/ + + + +#include "iminternal.h" + +#include + + + +#ifdef __STDC__ + +static int imGetOutChannel(char* expression); + +static int imGetInChannel(char* expression); + +static ImVfb* imAllocateNewVfb(TagTable* flagsTable, ImVfb* inVfb); + +static int imCheckChannelMapEntries(TagTable* flagsTable, ImVfb* inVfb); + +static int imGiveInfoAboutChannel(TagTable* flagsTable, char* chanName); + +#else + +static int imGetOutChannel(); + +static int imGetInChannel(); + +static ImVfb* imAllocateNewVfb(); + +static int imCheckChannelMapEntries(); + +static int imGiveInfoAboutChannel(); + +#endif + + + +/* + + * MACRO + + * ImVfbQChannel (value, vfb, vfbptr, channel) + + * + + * DESCRIPTION + + * Query a channel of the vfb into value + + * + + */ + + + +#define ImVfbQChannel(value, vfb, vfbptr, channel) \ +switch (channel & IMVFBOTHERMASK) \ +{ \ + case IMVFBGREY : /* same as INDEX8 */ \ + value = ImVfbQGrey (vfb, vfbptr); \ + break; \ + case IMVFBRED : \ + value = ImVfbQRed (vfb, vfbptr); \ + break; \ + case IMVFBGREEN : \ + value = ImVfbQGreen(vfb, vfbptr); \ + break; \ + case IMVFBBLUE : \ + value = ImVfbQBlue (vfb, vfbptr); \ + break; \ + case IMVFBALPHA : \ + value = ImVfbQAlpha (vfb, vfbptr); \ + break; \ + case IMVFBWPROT : \ + value = ImVfbQWProt (vfb, vfbptr); \ + break; \ + case IMVFBZ : \ + value = ImVfbQZ(vfb, vfbptr ); \ + break; \ + case IMVFBFDATA : \ + /* value = ImVfbQFData(vfb, vfbptr ); */ \ + break; \ + case IMVFBIDATA : \ + value = ImVfbQIData(vfb, vfbptr ); \ + break; \ + default : \ + if (channel & IMVFBINDEX8) \ + value = ImVfbQGrey (vfb, vfbptr); \ + else if (channel & IMVFBINDEX16) \ + value = ImVfbQIndex16 (vfb, vfbptr); \ + else \ + value = 0; \ + break; \ +} + + + + + +/* + + * MACRO + + * ImVfbSChannel (value, vfb, vfbptr, channel) + + * + + * DESCRIPTION + + * Put value into this channel of the vfb + + * + + */ + + + +#define ImVfbSChannel(value, vfb, vfbptr, channel) \ +switch (channel & IMVFBOTHERMASK) \ +{ \ + case IMVFBRED : \ + ImVfbSRed (vfb, vfbptr, value); \ + break; \ + case IMVFBGREEN : \ + ImVfbSGreen(vfb, vfbptr, value); \ + break; \ + case IMVFBBLUE : \ + ImVfbSBlue (vfb, vfbptr, value); \ + break; \ + case IMVFBALPHA : \ + ImVfbSAlpha (vfb, vfbptr, value); \ + break; \ + case IMVFBWPROT : \ + ImVfbSWProt (vfb, vfbptr, value); \ + break; \ + case IMVFBZ : \ + ImVfbSZ(vfb, vfbptr, value); \ + break; \ + case IMVFBFDATA : \ + /* ImVfbSFData(vfb, vfbptr, value); */ \ + break; \ + case IMVFBIDATA : \ + ImVfbSIData(vfb, vfbptr, value); \ + break; \ + default : \ + if (channel & IMVFBGREY) \ + ImVfbSGrey (vfb, vfbptr, value); \ + else \ + if (channel & IMVFBINDEX16) \ + ImVfbSIndex16 (vfb, vfbptr, value); \ + break; \ +} + +/* + + * FUNCTION + + * ImVfbCopyChannel + + * + + * DESCRIPTION + + * Copy a specified channel from one vfb to + + * a specified channel of another vfb + + * + + * The channels which can be specifed are as follows: + + * IMVFBRED red + + * IMVFBBLUE blue + + * IMVFBGREEN green + + * IMVFBALPHA alpha + + * IMVFBINDEX8 index8 / gray + + * IMVFBINDEX16 index16 + + * NULL none + + * + + * Copying "none" to a channel is equivalent to filling the outgoing + + * channel with zeros. + + * + + * (It does not make the channel dissappear from the vfb. If this is + + * desired, then the -outnoalpha option, for instance, should be used.) + + * + + */ + +ImVfb * /* Returns the destination vfb */ + +#ifdef __STDC__ + +ImVfbCopyChannel( ImVfb* inVfb, int inFieldMask, ImVfb* outVfb, int outFieldMask) + +#else + +ImVfbCopyChannel( inVfb, inFieldMask, outVfb, outFieldMask) + +ImVfb* inVfb; + +int inFieldMask; + +ImVfb* outVfb; + +int outFieldMask; + +#endif + +{ + + int width, height; /* Dimensions of incoming vfb */ + + int w, h; /* Loop indices */ + + ImVfbPtr inVfbPtr; /* pointer into a vfb */ + + ImVfbPtr outVfbPtr; /* pointer into a vfb */ + + unsigned int chanData; /* temporary variable for data */ + + unsigned int tempMask; /* holds masks during error checking */ + + int inOK, outOK; /* flags indicating that masks are ok*/ + + int inFields, outFields; /* Fields in the vfbs */ + + char message[300]; /* Buffer for messages */ + + + + if (inVfb==IMVFBNULL) + + { + + ImErrNo = IMENOVFB; + + return IMVFBNULL; + + } + + + + /* Get width and height of incoming vfb */ + + width = ImVfbQWidth (inVfb); + + height = ImVfbQHeight (inVfb); + + + + /* Allocate a new Vfb if necessary */ + + if (outVfb==IMVFBNEW) + + { + + int allocFields; + + + + /* Make it the same type as the incoming vfb */ + + allocFields = ImVfbQFields (inVfb) & IMVFBIMAGEMASK; + + + + outVfb = ImVfbAlloc(width, height, allocFields | outFieldMask); + + if (outVfb == IMVFBNULL) + + { + + ImErrNo = IMEMALLOC; + + return IMVFBNULL; + + } + + + + } + + + + /* Discern the fields that are present */ + + inFields = ImVfbQFields(inVfb); + + outFields = ImVfbQFields(outVfb); + + if (inFields & IMVFBRGB) + + { + + inFields |= IMVFBRED; + + inFields |= IMVFBGREEN; + + inFields |= IMVFBBLUE; + + } + + if (outFields & IMVFBRGB) + + { + + outFields |= IMVFBRED; + + outFields |= IMVFBGREEN; + + outFields |= IMVFBBLUE; + + } + + + + /* Confirm that inVfb has the specified field + + * (if it's not NULL) + + */ + + if ((inFieldMask!=NULL) && !(inFields & inFieldMask)) + + { + + sprintf(message,"The incoming vfb does not have the requested channel"); + + ImErrorFatal( message, IMVFBNULL, IMEBADCHANNEL ); + + + + } + + + + /* + + * Confirm that outVfb has the specified field + + */ + + if (!(outFields & outFieldMask)) + + { + + sprintf(message,"The requested channel is not supported by the outgoing vfb"); + + ImErrorFatal( message, IMVFBNULL, IMEFIELD ); + + + + } + + + + /* Confirm that the vfbs are the same size */ + + if ( width!=ImVfbQWidth(outVfb) || height!=ImVfbQHeight(outVfb)) + + { + + ImErrNo = IMEDIFFSIZE; + + return IMVFBNULL; + + } + + + + /* Verify that the field masks are okay */ + + /* i.e. only one field can be copied */ + + inOK = (inFieldMask==NULL); /* Okay to copy from "none" */ + + outOK = 0; /* Can't copy to "none" */ + + for (tempMask = (((unsigned int)1)<<31); (tempMask!=0) ; tempMask >>= 1) + + { + + if (tempMask==inFieldMask) + + inOK = 1; + + if (tempMask==outFieldMask) + + outOK = 1; + + } + + if (!inOK || !outOK) + + { + + ImErrNo = IMEFIELD; + + return IMVFBNULL; + + } + + + + /* Copy the data (finally)! */ + + /* Note that a 16-bit value will automatically be truncated by the cast + + in the ImVfbSChannel macro, if index16 is copied to another channel */ + + inVfbPtr = ImVfbQPtr(inVfb, 0, 0); + + outVfbPtr = ImVfbQPtr(outVfb, 0, 0); + + for (w=0;w0 means okay, <=0 means not okay. */ + +#ifdef __STDC__ + +imCheckChannelMapEntries(TagTable* flagsTable, ImVfb* inVfb) + +#else + +imCheckChannelMapEntries(flagsTable, inVfb) + +TagTable * flagsTable; + +ImVfb* inVfb; + +#endif + +{ + + int fieldsToTest; /* fields we are checking for */ + + int numMappings; /* number of mappings for this channel */ + + char* newString; /* new string to add to the table */ + + int inFields; /* fields in inVfb */ + + + + /* Discern the fields that are present */ + + inFields = ImVfbQFields(inVfb); + + if (inFields & IMVFBRGB) + + { + + inFields |= IMVFBRED; + + inFields |= IMVFBGREEN; + + inFields |= IMVFBBLUE; + + } + + + + fieldsToTest = inFields; + + + + /* + + * For each channel, ensure that there is precisely + + * ONE channel mapped to it. + + * + + * If there are more than one, report an error and quit. + + * If there are none + + * if this field exists in the inVfb, add channel=channel + + * if this field doesn't exist in inVfb, add channel=none + + */ + + + + /* + + * Red + + */ + + if (fieldsToTest & IMVFBRED) + + { + + numMappings = numOfMappingsToChannel(flagsTable, IMVFBRED); + + if (numMappings>1) + + { + + ImErrorFatal("Too many mappings to red", -1, IMEBADCHANNELS); + + } + + if (numMappings==0) + + { /* Add a mapping to red to the flags table */ + + if (inFields & IMVFBRED) + + { + + /* Add "red=red" to the flagsTable */ + + ImMalloc(newString, char *, 10 * sizeof(char)); + + strcpy(newString, "red=red"); + + } + + else + + { /* Add "red=none" to the flagsTable */ + + ImMalloc(newString, char *, 10 * sizeof(char)); + + strcpy(newString, "red=none"); + + } + + TagTableAppend(flagsTable, + + TagEntryAlloc("channel map request",POINTER, &newString)); + + } + + /* Give an imInfo message */ + + imGiveInfoAboutChannel(flagsTable,"red"); + + } + + + + /* + + * Green + + */ + + if (fieldsToTest & IMVFBGREEN) + + { + + numMappings = numOfMappingsToChannel(flagsTable, IMVFBGREEN); + + if (numMappings>1) + + { + + ImErrorFatal("Too many mappings to green", -1, IMEBADCHANNELS); + + } + + if (numMappings==0) + + { /* Add a mapping to red to the flags table */ + + if (inFields & IMVFBGREEN) + + { + + /* Add "green=green" to the flagsTable */ + + ImMalloc(newString, char *, 15 * sizeof(char)); + + strcpy(newString, "green=green"); + + } + + else + + { /* Add "green=none" to the flagsTable */ + + ImMalloc(newString, char *, 15 * sizeof(char)); + + strcpy(newString, "green=none"); + + } + + TagTableAppend(flagsTable, + + TagEntryAlloc("channel map request",POINTER, &newString)); + + } + + /* Give an imInfo message */ + + imGiveInfoAboutChannel(flagsTable,"green"); + + } + + /* + + * Blue + + */ + + if (fieldsToTest & IMVFBBLUE) + + { + + numMappings = numOfMappingsToChannel(flagsTable, IMVFBBLUE); + + if (numMappings>1) + + { + + ImErrorFatal("Too many mappings to blue", -1, IMEBADCHANNELS); + + } + + if (numMappings==0) + + { /* Add a mapping to red to the flags table */ + + if (inFields & IMVFBBLUE) + + { + + /* Add "blue=blue" to the flagsTable */ + + ImMalloc(newString, char *, 15 * sizeof(char)); + + strcpy(newString, "blue=blue"); + + } + + else + + { /* Add "blue=none" to the flagsTable */ + + ImMalloc(newString, char *, 15 * sizeof(char)); + + strcpy(newString, "blue=none"); + + } + + TagTableAppend(flagsTable, + + TagEntryAlloc("channel map request",POINTER, &newString)); + + } + + /* Give an imInfo message */ + + imGiveInfoAboutChannel(flagsTable,"blue"); + + } + + /* + + * Grey/Index8 + + */ + + if (fieldsToTest & IMVFBGREY) + + { + + numMappings = numOfMappingsToChannel(flagsTable, IMVFBGREY); + + if (numMappings>1) + + { + + ImErrorFatal("Too many mappings to grey", -1, IMEBADCHANNELS); + + } + + if (numMappings==0) + + { /* Add a mapping to grey to the flags table */ + + if (inFields & IMVFBGREY) + + { + + /* Add "grey=grey" to the flagsTable */ + + ImMalloc(newString, char *, 10 * sizeof(char)); + + strcpy(newString, "grey=grey"); + + } + + else + + { /* Add "grey=none" to the flagsTable */ + + ImMalloc(newString, char *, 10 * sizeof(char)); + + strcpy(newString, "grey=none"); + + } + + TagTableAppend(flagsTable, + + TagEntryAlloc("channel map request",POINTER, &newString)); + + } + + /* Give an imInfo message */ + + imGiveInfoAboutChannel(flagsTable,"grey"); + + } + + /* + + * Alpha + + */ + + if (fieldsToTest & IMVFBALPHA) + + { + + numMappings = numOfMappingsToChannel(flagsTable, IMVFBALPHA); + + if (numMappings>1) + + { + + ImErrorFatal("Too many mappings to alpha", -1, IMEBADCHANNELS); + + } + + if (numMappings==0) + + { /* Add a mapping to alpha to the flags table */ + + if (inFields & IMVFBALPHA) + + { + + /* Add "alpha=alpha" to the flagsTable */ + + ImMalloc(newString, char *, 10 * sizeof(char)); + + strcpy(newString, "alpha=alpha"); + + } + + else + + { /* Add "alpha=none" to the flagsTable */ + + ImMalloc(newString, char *, 10 * sizeof(char)); + + strcpy(newString, "alpha=none"); + + } + + TagTableAppend(flagsTable, + + TagEntryAlloc("channel map request",POINTER, &newString)); + + } + + /* Give an imInfo message */ + + imGiveInfoAboutChannel(flagsTable,"alpha"); + + } + + + + + + /* + + * Index16 + + */ + + if (fieldsToTest & IMVFBINDEX16) + + { + + numMappings = numOfMappingsToChannel(flagsTable, IMVFBINDEX16); + + if (numMappings>1) + + { + + ImErrorFatal("Too many mappings to index16", -1, IMEBADCHANNELS); + + } + + if (numMappings==0) + + { /* Add a mapping to red to the flags table */ + + if (inFields & IMVFBINDEX16) + + { + + /* Add "index16=index16" to the flagsTable */ + + ImMalloc(newString, char *, 20 * sizeof(char)); + + strcpy(newString, "index16=index16"); + + } + + else + + { /* Add "index16=none" to the flagsTable */ + + ImMalloc(newString, char *, 20 * sizeof(char)); + + strcpy(newString, "index16=none"); + + } + + TagTableAppend(flagsTable, + + TagEntryAlloc("channel map request",POINTER, &newString)); + + } + + /* Give an imInfo message */ + + imGiveInfoAboutChannel(flagsTable,"index16"); + + } + + + + return (1); + +} + + + + + +/* + + * FUNCTION + + * imGiveInfoAboutChannel + + * + + * DESCRIPTION + + * Spit out an ImInfo message describing what + + * is being mapped to this channel, according to + + * the flags table + + * + + * e.g. passing "red" will search the flags + + * table for something like red=blue, then + + * print "red = blue" + + */ + + + +static int /* returns status */ + +#ifdef __STDC__ + +imGiveInfoAboutChannel(TagTable* flagsTable, char* chanName) + +#else + +imGiveInfoAboutChannel(flagsTable,chanName) + +TagTable* flagsTable; + +char* chanName; + +#endif + +{ + + TagEntry* tmpEntry; /* temporary tag table entry */ + + char* request; /* Request from entry */ + + int numChannelEntries; /* # of 'channel map request's */ + + int i; /* Loop index */ + + char tmpRequest[50]; /* Holds the request */ + + char* ptr; /* temp char* pointer */ + + char message[300]; /* Buffer for messages */ + + + + numChannelEntries = TagTableQNEntry(flagsTable, "channel map request"); + + + + for (i=0; i ImVfbQWidth( fgVfb ) ) + + fgXSize = ImVfbQWidth( fgVfb ); + + + + if ( fgYSize < 0 ) + + fgYSize = 0; + + else if ( fgYSize > ImVfbQHeight( fgVfb ) ) + + fgYSize = ImVfbQHeight( fgVfb ); + + + + if ( fgX < 0 ) + + fgX = 0; + + else if ( fgX > ImVfbQWidth( fgVfb ) ) + + fgX = ImVfbQWidth( fgVfb ); + + + + if ( fgY < 0 ) + + fgY = 0; + + else if ( fgY > ImVfbQHeight( fgVfb ) ) + + fgY = ImVfbQHeight( fgVfb ); + + + + if ( bgXInto > ImVfbQWidth( bgVfb ) ) + + bgXInto = ImVfbQWidth( bgVfb ); + + + + if ( bgYInto > ImVfbQHeight( bgVfb ) ) + + bgYInto = ImVfbQHeight( bgVfb ); + + + + + + /* + + * When the user decides to use to xstart and/or ystart options, + + * we must adjust the width so that we don't scan past the actual + + * width of the foreground. + + */ + + + + fgXSize = ImVfbQWidth( fgVfb ) - fgX; + + fgYSize = ImVfbQHeight( fgVfb ) - fgY; + + + + + + /* + + * Make sure the sources each have the fields listed in the + + * field mask. + + */ + + if ( fieldMask == 0 ) + + { + + ImErrNo = IMEFIELD; + + return ( IMVFBNULL ); + + } + + if ( fieldMask & IMVFBRGB ) + + fieldMask = (fieldMask & ~IMVFBRGB) | IMRED | IMGREEN | IMBLUE; + + if ( fieldMask & (IMHUE | IMSATURATION | IMINTENSITY) ) + + neededFields = IMVFBRGB | (fieldMask & ~(IMHUE | IMSATURATION | IMINTENSITY)); + + else if ( fieldMask & (IMRED | IMGREEN | IMBLUE) ) + + neededFields = IMVFBRGB | (fieldMask & ~(IMRED | IMGREEN | IMBLUE)); + + else + + neededFields = fieldMask; + + fgField = ImVfbQFields( fgVfb ); + + bgField = ImVfbQFields( bgVfb ); + + if ( (fgField & neededFields) != neededFields ) + + { + + ImErrNo = IMEFIELD; + + return ( IMVFBNULL ); + + } + + if ( (bgField & neededFields) != neededFields ) + + { + + ImErrNo = IMEFIELD; + + return ( IMVFBNULL ); + + } + + + + + + /* + + * Make sure the zOperator is valid and that the right set of + + * alpha channels are available. + + */ + + switch ( zOperator ) + + { + + case IMCOMPOVER: + + if ( !(fgField & IMALPHA) ) + + { + + ImErrNo = IMENOALPHA; + + return ( IMVFBNULL ); + + } + + break; + + + + case IMCOMPATOP: + + if ( !(fgField & IMALPHA) ) + + { + + ImErrNo = IMENOALPHA; + + return ( IMVFBNULL ); + + } + + if ( !(bgField & IMALPHA) ) + + { + + ImErrNo = IMENOALPHA; + + return ( IMVFBNULL ); + + } + + break; + + + + case IMCOMPINSIDE: + + case IMCOMPOUTSIDE: + + if ( !(bgField & IMALPHA) ) + + { + + ImErrNo = IMENOALPHA; + + return ( IMVFBNULL ); + + } + + break; + + + + default: + + ImErrNo = IMEOPERATION; + + break; + + } + + + + + + /* + + * If the needed fields don't match up, then is is an error. + + */ + + if ( (ImVfbQFields( bgVfb ) & neededFields) != neededFields ) + + { + + ImErrNo = IMEFIELD; + + return ( IMVFBNULL ); + + } + + + + + + /* + + * Composite them! + + */ + + switch ( zOperator ) + + { + + case IMCOMPOVER: + + imVfbCompOver( fgVfb, fgX, fgY, fgXSize, fgYSize, fieldMask, + + bgXInto, bgYInto, bgVfb, ImVfbQWidth( bgVfb), + + ImVfbQHeight( bgVfb )); + + break; + + + + case IMCOMPINSIDE: + + imVfbCompInside( fgVfb, fgX, fgY, fgXSize, fgYSize, fieldMask, + + bgXInto, bgYInto, bgVfb, ImVfbQWidth( bgVfb), + + ImVfbQHeight( bgVfb )); + + break; + + + + case IMCOMPOUTSIDE: + + imVfbCompOutside( fgVfb, fgX, fgY, fgXSize, fgYSize, fieldMask, + + bgXInto, bgYInto, bgVfb, ImVfbQWidth( bgVfb), + + ImVfbQHeight( bgVfb )); + + break; + + + + case IMCOMPATOP: + + imVfbCompAtop( fgVfb, fgX, fgY, fgXSize, fgYSize, fieldMask, + + bgXInto, bgYInto, bgVfb, ImVfbQWidth( bgVfb), + + ImVfbQHeight( bgVfb )); + + break; + + } + + + + + + return( bgVfb); + + + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imVfbCompOver - composite using the IM_OVER operator + + * imVfbCompAtop - composite using the IM_ATOP operator + + * imVfbCompInside - composite using the IM_INSIDE operator + + * imVfbCompOutside- composite using the IM_OUTSIDE operator + + * + + * DESCRIPTION + + * The two source images are composited into the destination image based + + * upon the operator: + + * + + * over: + + * OutChannel = In1Channel * In1Alpha + + + * In2Channel * (1.0 - In1Alpha) + + * + + * atop: + + * OutChannel = In1Channel * In2Alpha + + + * In2Channel * (1.0 - In1Alpha) + + * + + * inside: + + * OutChannel = In1Channel * In2Alpha + + * + + * outside: + + * OutChannel = In1Channel * (1.0 - In2Alpha) + + * + + * Each of the following four routines are essentially identical + + * except for the application of the operator equations above. + + */ + + + +#define IM_OVER( query, set ) \ +{ \ + set( bgVfb, pBg, \ + (float)(query( fgVfb, pFg )) * alpha1 + \ + (float)(query( bgVfb, pBg )) * (1.0 - alpha1) ); \ +} + + +#define IM_ATOP( query, set ) \ +{ \ + set( bgVfb, pBg, \ + (float)(query( fgVfb, pFg )) * alpha2 + \ + (float)(query( bgVfb, pBg )) * (1.0 - alpha1) ); \ +} + + +#define IM_INSIDE( query, set ) \ +{ \ + set( bgVfb, pBg, \ + (float)(query( fgVfb, pFg )) * alpha2 ); \ +} + + +#define IM_OUTSIDE( query, set ) \ +{ \ + set( bgVfb, pBg, \ + (float)(query( fgVfb, pFg )) * (1.0 - alpha2) ); \ +} + + +#define IM_GETHSI( hsi, rgb, srcVfb, pSrc ) \ + rgb[0] = ImVfbQRed( srcVfb, pSrc ); \ + rgb[1] = ImVfbQGreen( srcVfb, pSrc ); \ + rgb[2] = ImVfbQBlue( srcVfb, pSrc ); \ + ImRgbToHsi( rgb, hsi ); + + +#define IM_PUTRGB( hsi, rgb, srcVfb, pSrc ) \ + ImHsiToRgb( hsi, rgb ); \ + ImVfbSRed( srcVfb, pSrc, rgb[0] ); \ + ImVfbSGreen( srcVfb, pSrc, rgb[1] ); \ + ImVfbSBlue( srcVfb, pSrc, rgb[2] ); + + +static void /* Returns nothin */ + +#ifdef __STDC__ + +imVfbCompOver( ImVfb *fgVfb, int fgX, int fgY, int fgXSize, int fgYSize, int fieldMask, + + int bgXInto, int bgYInto,ImVfb *bgVfb, int bgXSize, int bgYSize ) + +#else + +imVfbCompOver( fgVfb, fgX, fgY, fgXSize, fgYSize, fieldMask, bgXInto, bgYInto, + + bgVfb, bgXSize, bgYSize ) + + ImVfb *fgVfb; /* incoming vfb foreground */ + + int fgX; /* X offset into foreground */ + + int fgY; /* Y offset into background */ + + int fgXSize; /* width of foreground to copy */ + + int fgYSize; /* height of foregroiund to copy */ + + int fieldMask; /* what field to replace */ + + int bgXInto; /* X offset of foreground into background */ + + int bgYInto; /* Y offset of foreground into background */ + + ImVfb *bgVfb; /* incoming vfb background */ + + int bgXSize; /* width of background */ + + int bgYSize; /* height of background */ + +#endif + +{ + + int i,j; /* counters */ + + ImVfbPtr pFg; /* VFB pointer into fgVfb */ + + ImVfbPtr pBg; /* VFB pointer into bgVfb */ + + float hsi1[3], hsi2[3];/* HSI values */ + + int rgb1[3], rgb2[3];/* RGB values */ + + float alpha1; /* image 1 alpha */ + + int minXSize; /* minimum width between fg and bg */ + + int minYSize; /* minimum height between fg and bg */ + + + + + + /* + + * Handle the case when the user wishes to offset the foreground by a + + * negative amount. Set their offset to zero and modify offset into + + * foreground accordingly. + + */ + + if (bgXInto < 0) + + { + + fgX -= bgXInto; + + fgXSize += bgXInto; + + bgXInto = 0; + + } + + if (bgYInto < 0) + + { + + fgY -= bgYInto; + + fgYSize += bgYInto; + + bgYInto = 0; + + } + + + + + + /* + + */ + + if ( fgX > ImVfbQWidth( fgVfb ) ) return; + + if ( fgY > ImVfbQHeight( fgVfb ) ) return; + + + + + + /* + + * Find the minimum width and height between the fg and bg VFBs. + + */ + + + + if ( bgYSize < fgYSize ) minYSize = bgYSize; + + else minYSize = fgYSize; + + + + if ( bgXSize < fgXSize ) minXSize = bgXSize; + + else minXSize = fgXSize; + + + + + + /* + + * Do the actual composite point by point. Whenever the forground + + * overlaps of the left and lower end of the background, it is cut off. + + */ + + + + for ( i = 0; i < minYSize; i++ ) + + if ( (bgYInto + i) < bgYSize ) + + { + + pFg = ImVfbQPtr( fgVfb, fgX, i+fgY ); + + pBg = ImVfbQPtr( bgVfb, bgXInto, i+bgYInto ); + + + + for ( j = 0; j < minXSize; j++ ) + + if ( (bgXInto + j) < bgXSize ) + + { + + alpha1 = ImVfbQAlpha( fgVfb, pFg )/255.0; + + if ( fieldMask & IMRED ) IM_OVER(ImVfbQRed, ImVfbSRed) + + if ( fieldMask & IMGREEN ) IM_OVER(ImVfbQGreen, ImVfbSGreen) + + if ( fieldMask & IMBLUE ) IM_OVER(ImVfbQBlue, ImVfbSBlue) + + if ( fieldMask & IMMONO ) IM_OVER(ImVfbQMono, ImVfbSMono) + + if ( fieldMask & IMINDEX8 ) IM_OVER(ImVfbQIndex8,ImVfbSIndex8) + + if ( fieldMask & IMINDEX16) IM_OVER(ImVfbQIndex16,ImVfbSIndex16) + + if ( fieldMask & IMWPROT ) IM_OVER(ImVfbQWProt, ImVfbSWProt) + + if ( fieldMask & IMIDATA ) IM_OVER(ImVfbQIData, ImVfbSIData) + + if ( fieldMask & IMFDATA ) IM_OVER(ImVfbQFData, ImVfbSFData) + + if ( fieldMask & IMZ ) IM_OVER(ImVfbQZ, ImVfbSZ) + + if ( fieldMask & (IMHUE | IMSATURATION | IMINTENSITY) ) + + { + + IM_GETHSI( hsi1, rgb1, fgVfb, pFg ); + + IM_GETHSI( hsi2, rgb2, bgVfb, pBg ); + + + + if ( fieldMask & IMHUE ) + + hsi2[0] = hsi1[0] * alpha1 + hsi2[0] * (1.0 - alpha1); + + if ( fieldMask & IMSATURATION ) + + hsi2[1] = hsi1[1] * alpha1 + hsi2[1] * (1.0 - alpha1); + + if ( fieldMask & IMINTENSITY ) + + hsi2[2] = hsi1[2] * alpha1 + hsi2[2] * (1.0 - alpha1); + + + + IM_PUTRGB( hsi2, rgb2, bgVfb, pBg ); + + } + + if ( fieldMask & IMALPHA ) IM_OVER( ImVfbQAlpha, ImVfbSAlpha ) + + ImVfbSInc( fgVfb, pFg ); + + ImVfbSInc( bgVfb, pBg ); + + } + + } + +} + + + + + +static void /* Returns nothin */ + +#ifdef __STDC__ + +imVfbCompAtop( ImVfb *fgVfb, int fgX, int fgY, int fgXSize, int fgYSize, int fieldMask, + + int bgXInto, int bgYInto,ImVfb *bgVfb, int bgXSize, int bgYSize ) + +#else + +imVfbCompAtop( fgVfb, fgX, fgY, fgXSize, fgYSize, fieldMask, bgXInto, bgYInto, + + bgVfb, bgXSize, bgYSize ) + + ImVfb *fgVfb; /* incoming vfb foreground */ + + int fgX; /* X offset into foreground */ + + int fgY; /* Y offset into background */ + + int fgXSize; /* width of foreground to copy */ + + int fgYSize; /* height of foregroiund to copy */ + + int fieldMask; /* what field to replace */ + + int bgXInto; /* X offset of foreground into background */ + + int bgYInto; /* Y offset of foreground into background */ + + ImVfb *bgVfb; /* incoming vfb background */ + + int bgXSize; /* width of background */ + + int bgYSize; /* height of background */ + +#endif + +{ + + int i,j; /* counters */ + + ImVfbPtr pFg; /* VFB pointer into fgVfb */ + + ImVfbPtr pBg; /* VFB pointer into bgVfb */ + + float hsi1[3], hsi2[3];/* HSI values */ + + int rgb1[3], rgb2[3];/* RGB values */ + + float alpha1; /* image 1 alpha */ + + float alpha2; /* image 2 alpha */ + + int minXSize; /* minimum width between fg and bg */ + + int minYSize; /* minimum height between fg and bg */ + + + + + + /* + + * Handle the case when the user wishes to offset the foreground by a + + * negative amount. Set their offset to zero and modify offset into + + * foreground accordingly. + + */ + + if (bgXInto < 0) + + { + + fgX -= bgXInto; + + fgXSize += bgXInto; + + bgXInto = 0; + + } + + if (bgYInto < 0) + + { + + fgY -= bgYInto; + + fgYSize += bgYInto; + + bgYInto = 0; + + } + + + + + + /* + + * Find the minimum width and height between the fg and bg VFBs. + + */ + + + + if ( bgYSize < fgYSize ) minYSize = bgYSize; + + else minYSize = fgYSize; + + + + if ( bgXSize < fgXSize ) minXSize = bgXSize; + + else minXSize = fgXSize; + + + + + + /* + + * Do the actual composite point by point. Whenever the forground + + * overlaps of the left and lower end of the background, it is cut off. + + */ + + + + for ( i = 0; i < minYSize; i++ ) + + if ( (bgYInto + i) < bgYSize ) + + { + + pFg = ImVfbQPtr( fgVfb, fgX, i+fgY ); + + pBg = ImVfbQPtr( bgVfb, bgXInto, i+bgYInto ); + + + + for ( j = 0; j < minXSize; j++ ) + + if ( (bgXInto + j) < bgXSize ) + + { + + alpha1 = ImVfbQAlpha( fgVfb, pFg )/255.0; + + alpha2 = ImVfbQAlpha( bgVfb, pBg )/255.0; + + if ( fieldMask & IMRED ) IM_ATOP(ImVfbQRed, ImVfbSRed) + + if ( fieldMask & IMGREEN ) IM_ATOP(ImVfbQGreen, ImVfbSGreen) + + if ( fieldMask & IMBLUE ) IM_ATOP(ImVfbQBlue, ImVfbSBlue) + + if ( fieldMask & IMMONO ) IM_ATOP(ImVfbQMono, ImVfbSMono) + + if ( fieldMask & IMINDEX8 ) IM_ATOP(ImVfbQIndex8,ImVfbSIndex8) + + if ( fieldMask & IMINDEX16) IM_ATOP(ImVfbQIndex16,ImVfbSIndex16) + + if ( fieldMask & IMWPROT ) IM_ATOP(ImVfbQWProt, ImVfbSWProt) + + if ( fieldMask & IMIDATA ) IM_ATOP(ImVfbQIData, ImVfbSIData) + + if ( fieldMask & IMFDATA ) IM_ATOP(ImVfbQFData, ImVfbSFData) + + if ( fieldMask & IMZ ) IM_ATOP(ImVfbQZ, ImVfbSZ) + + if ( fieldMask & (IMHUE | IMSATURATION | IMINTENSITY) ) + + { + + IM_GETHSI( hsi1, rgb1, fgVfb, pFg ); + + IM_GETHSI( hsi2, rgb2, bgVfb, pBg ); + + + + if ( fieldMask & IMHUE ) + + hsi2[0] = hsi1[0] * alpha2 + hsi2[0] * (1.0 - alpha1); + + if ( fieldMask & IMSATURATION ) + + hsi2[1] = hsi1[1] * alpha2 + hsi2[1] * (1.0 - alpha1); + + if ( fieldMask & IMINTENSITY ) + + hsi2[2] = hsi1[2] * alpha2 + hsi2[2] * (1.0 - alpha1); + + + + IM_PUTRGB( hsi2, rgb2, bgVfb, pBg ); + + } + + if ( fieldMask & IMALPHA ) IM_ATOP( ImVfbQAlpha, ImVfbSAlpha ) + + ImVfbSInc( fgVfb, pFg ); + + ImVfbSInc( bgVfb, pBg ); + + } + + } + +} + + + + + +static void /* Returns nothin */ + +#ifdef __STDC__ + +imVfbCompInside( ImVfb *fgVfb, int fgX, int fgY, int fgXSize, int fgYSize, int fieldMask, + + int bgXInto, int bgYInto,ImVfb *bgVfb, int bgXSize, int bgYSize ) + +#else + +imVfbCompInside( fgVfb, fgX, fgY, fgXSize, fgYSize, fieldMask, bgXInto, bgYInto, + + bgVfb, bgXSize, bgYSize ) + + ImVfb *fgVfb; /* incoming vfb foreground */ + + int fgX; /* X offset into foreground */ + + int fgY; /* Y offset into background */ + + int fgXSize; /* width of foreground to copy */ + + int fgYSize; /* height of foregroiund to copy */ + + int fieldMask; /* what field to replace */ + + int bgXInto; /* X offset of foreground into background */ + + int bgYInto; /* Y offset of foreground into background */ + + ImVfb *bgVfb; /* incoming vfb background */ + + int bgXSize; /* width of background */ + + int bgYSize; /* height of background */ + +#endif + +{ + + int i,j; /* counters */ + + ImVfbPtr pFg; /* VFB pointer into fgVfb */ + + ImVfbPtr pBg; /* VFB pointer into bgVfb */ + + float hsi1[3], hsi2[3];/* HSI values */ + + int rgb1[3], rgb2[3];/* RGB values */ + + float alpha2; /* image 2 alpha */ + + int minXSize; /* minimum width between fg and bg */ + + int minYSize; /* minimum height between fg and bg */ + + + + + + /* + + * Handle the case when the user wishes to offset the foreground by a + + * negative amount. Set their offset to zero and modify offset into + + * foreground accordingly. + + */ + + if (bgXInto < 0) + + { + + fgX -= bgXInto; + + fgXSize += bgXInto; + + bgXInto = 0; + + } + + if (bgYInto < 0) + + { + + fgY -= bgYInto; + + fgYSize += bgYInto; + + bgYInto = 0; + + } + + + + + + /* + + * Find the minimum width and height between the fg and bg VFBs. + + */ + + + + if ( bgYSize < fgYSize ) minYSize = bgYSize; + + else minYSize = fgYSize; + + + + if ( bgXSize < fgXSize ) minXSize = bgXSize; + + else minXSize = fgXSize; + + + + + + /* + + * Do the actual composite point by point. Whenever the forground + + * overlaps of the left and lower end of the background, it is cut off. + + */ + + + + for ( i = 0; i < minYSize; i++ ) + + if ( (bgYInto + i) < bgYSize ) + + { + + pFg = ImVfbQPtr( fgVfb, fgX, i+fgY ); + + pBg = ImVfbQPtr( bgVfb, bgXInto, i+bgYInto ); + + + + for ( j = 0; j < minXSize; j++ ) + + if ( (bgXInto + j) < bgXSize ) + + { + + alpha2 = ImVfbQAlpha( bgVfb, pBg )/255.0; + + if ( fieldMask & IMRED ) IM_INSIDE(ImVfbQRed, ImVfbSRed) + + if ( fieldMask & IMGREEN ) IM_INSIDE(ImVfbQGreen, ImVfbSGreen) + + if ( fieldMask & IMBLUE ) IM_INSIDE(ImVfbQBlue, ImVfbSBlue) + + if ( fieldMask & IMMONO ) IM_INSIDE(ImVfbQMono, ImVfbSMono) + + if ( fieldMask & IMINDEX8 ) IM_INSIDE(ImVfbQIndex8,ImVfbSIndex8) + + if ( fieldMask & IMINDEX16) IM_INSIDE(ImVfbQIndex16,ImVfbSIndex16) + + if ( fieldMask & IMWPROT ) IM_INSIDE(ImVfbQWProt, ImVfbSWProt) + + if ( fieldMask & IMIDATA ) IM_INSIDE(ImVfbQIData, ImVfbSIData) + + if ( fieldMask & IMFDATA ) IM_INSIDE(ImVfbQFData, ImVfbSFData) + + if ( fieldMask & IMZ ) IM_INSIDE(ImVfbQZ, ImVfbSZ) + + if ( fieldMask & (IMHUE | IMSATURATION | IMINTENSITY) ) + + { + + IM_GETHSI( hsi1, rgb1, fgVfb, pFg ); + + IM_GETHSI( hsi2, rgb2, bgVfb, pBg ); + + + + if ( fieldMask & IMHUE ) + + hsi2[0] = hsi1[0] * alpha2; + + if ( fieldMask & IMSATURATION ) + + hsi2[1] = hsi1[1] * alpha2; + + if ( fieldMask & IMINTENSITY ) + + hsi2[2] = hsi1[2] * alpha2; + + + + IM_PUTRGB( hsi2, rgb2, bgVfb, pBg ); + + } + + if ( fieldMask & IMALPHA ) IM_INSIDE(ImVfbQAlpha, ImVfbSAlpha) + + ImVfbSInc( fgVfb, pFg ); + + ImVfbSInc( bgVfb, pBg ); + + } + + } + +} + + + + + +static void /* Returns nothin */ + +#ifdef __STDC__ + +imVfbCompOutside( ImVfb *fgVfb, int fgX, int fgY, int fgXSize, int fgYSize, int fieldMask, + + int bgXInto, int bgYInto,ImVfb *bgVfb, int bgXSize, int bgYSize ) + +#else + +imVfbCompOutside( fgVfb, fgX, fgY, fgXSize, fgYSize, fieldMask, bgXInto, + + bgYInto, bgVfb, bgXSize, bgYSize ) + + ImVfb *fgVfb; /* incoming vfb foreground */ + + int fgX; /* X offset into foreground */ + + int fgY; /* Y offset into background */ + + int fgXSize; /* width of foreground to copy */ + + int fgYSize; /* height of foregroiund to copy */ + + int fieldMask; /* what field to replace */ + + int bgXInto; /* X offset of foreground into background */ + + int bgYInto; /* Y offset of foreground into background */ + + ImVfb *bgVfb; /* incoming vfb background */ + + int bgXSize; /* width of background */ + + int bgYSize; /* height of background */ + +#endif + +{ + + int i,j; /* counters */ + + ImVfbPtr pFg; /* VFB pointer into fgVfb */ + + ImVfbPtr pBg; /* VFB pointer into bgVfb */ + + float hsi1[3], hsi2[3];/* HSI values */ + + int rgb1[3], rgb2[3];/* RGB values */ + + float alpha2; /* image 2 alpha */ + + int minXSize; /* minimum width between fg and bg */ + + int minYSize; /* minimum height between fg and bg */ + + + + + + /* + + * Handle the case when the user wishes to offset the foreground by a + + * negative amount. Set their offset to zero and modify offset into + + * foreground accordingly. + + */ + + if (bgXInto < 0) + + { + + fgX -= bgXInto; + + fgXSize += bgXInto; + + bgXInto = 0; + + } + + if (bgYInto < 0) + + { + + fgY -= bgYInto; + + fgYSize += bgYInto; + + bgYInto = 0; + + } + + + + + + /* + + * Find the minimum width and height between the fg and bg VFBs. + + */ + + + + if ( bgYSize < fgYSize ) minYSize = bgYSize; + + else minYSize = fgYSize; + + + + if ( bgXSize < fgXSize ) minXSize = bgXSize; + + else minXSize = fgXSize; + + + + + + /* + + * Do the actual composite point by point. Whenever the forground + + * overlaps of the left and lower end of the background, it is cut off. + + */ + + + + for ( i = 0; i < minYSize; i++ ) + + if ( (bgYInto + i) < bgYSize ) + + { + + pFg = ImVfbQPtr( fgVfb, fgX, i+fgY ); + + pBg = ImVfbQPtr( bgVfb, bgXInto, i+bgYInto ); + + + + for ( j = 0; j < minXSize; j++ ) + + if ( (bgXInto + j) < bgXSize ) + + { + + alpha2 = ImVfbQAlpha( bgVfb, pBg )/255.0; + + if ( fieldMask & IMRED ) IM_OUTSIDE(ImVfbQRed, ImVfbSRed) + + if ( fieldMask & IMGREEN ) IM_OUTSIDE(ImVfbQGreen, ImVfbSGreen) + + if ( fieldMask & IMBLUE ) IM_OUTSIDE(ImVfbQBlue, ImVfbSBlue) + + if ( fieldMask & IMMONO ) IM_OUTSIDE(ImVfbQMono, ImVfbSMono) + + if ( fieldMask & IMINDEX8 ) IM_OUTSIDE(ImVfbQIndex8,ImVfbSIndex8) + + if ( fieldMask & IMINDEX16) IM_OUTSIDE(ImVfbQIndex16,ImVfbSIndex16) + + if ( fieldMask & IMWPROT ) IM_OUTSIDE(ImVfbQWProt, ImVfbSWProt) + + if ( fieldMask & IMIDATA ) IM_OUTSIDE(ImVfbQIData, ImVfbSIData) + + if ( fieldMask & IMFDATA ) IM_OUTSIDE(ImVfbQFData, ImVfbSFData) + + if ( fieldMask & IMZ ) IM_OUTSIDE(ImVfbQZ, ImVfbSZ) + + if ( fieldMask & (IMHUE | IMSATURATION | IMINTENSITY) ) + + { + + IM_GETHSI( hsi1, rgb1, fgVfb, pFg ); + + IM_GETHSI( hsi2, rgb2, bgVfb, pBg ); + + + + if ( fieldMask & IMHUE ) + + hsi2[0] = hsi1[0] * (1.0 - alpha2); + + if ( fieldMask & IMSATURATION ) + + hsi2[1] = hsi1[1] * (1.0 - alpha2); + + if ( fieldMask & IMINTENSITY ) + + hsi2[2] = hsi1[2] * (1.0 - alpha2); + + + + IM_PUTRGB( hsi2, rgb2, bgVfb, pBg ); + + } + + if ( fieldMask & IMALPHA ) IM_OUTSIDE(ImVfbQAlpha,ImVfbSAlpha) + + ImVfbSInc( fgVfb, pFg ); + + ImVfbSInc( bgVfb, pBg ); + + } + + } + +} + diff --git a/utils/roq2/libim/imvfbflip.c b/utils/roq2/libim/imvfbflip.c new file mode 100644 index 0000000..0110ac5 --- /dev/null +++ b/utils/roq2/libim/imvfbflip.c @@ -0,0 +1,534 @@ +/** + + ** $Header: /roq/libim/imvfbflip.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/imvfbflip.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** imvfbflip.c - Function to Flip a Virtual Frame Buffer + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** imvfbflip.c contains the routine to flip a vfb for + + ** the IM package. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** ImVfbFlip f flip a VFB + + ** + + ** PRIVATE CONTENTS + + ** none + + ** + + ** HISTORY + + ** $Log: /roq/libim/imvfbflip.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.9 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.8 1995/06/16 08:52:59 bduggan + + ** changed bcopy to memcpy + + ** + + ** Revision 1.7 94/10/03 11:29:49 nadeau + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Updated comments. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.6 92/08/31 17:38:04 vle + + ** Updated copyright notice. + + ** + + ** Revision 1.5 91/09/18 17:26:52 mcleodj + + ** Modified routine to correctly handle destination` + + ** vfb's that are not equivalent to the source vfb. + + ** + + ** Revision 1.4 91/09/13 14:50:07 nadeau + + ** Removed unnecessary inclusion of + + ** + + ** Revision 1.3 91/07/12 10:31:21 nadeau + + ** Removed extra include statement. + + ** + + ** Revision 1.2 90/07/23 13:55:39 nadeau + + ** Totally rewrote. The original version wouldn't compile. If it had + + ** compiled, it wouldn't have linked. If it had linked, it wouldn't + + ** have properly flipped images. If it had properly flipped images, + + ** it would have done it in the slowest possible way. The shear + + ** stupidity of the original version is mind-boggeling. + + ** + + ** Revision 1.1 90/03/06 17:33:56 mercurio + + ** Initial revision + + ** + + **/ + + + +/** + + ** CODE CREDITS + + ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1990. + + **/ + + + +#include "iminternal.h" + + + + + + + + + + + +/* + + * FUNCTION + + * ImVfbFlip - flip a VFB + + * + + * DESCRIPTION + + * If no destination VFB is given, a new destination VFB is created. + + * + + * The source VFB is then copied into the destination VFB, flipping + + * its pixels horizontally, vertically, or both. + + */ + + + +ImVfb * /* Returns flipped VFB */ + +#ifdef __STDC__ + +ImVfbFlip( ImVfb *vfbSrc, int flipDirection, ImVfb *vfbDst ) + +#else + +ImVfbFlip( vfbSrc, flipDirection, vfbDst ) + + ImVfb *vfbSrc; /* VFB to flip */ + + int flipDirection; /* Direction to flip */ + + ImVfb *vfbDst; /* VFB to overwrite with flipped source */ + +#endif + +{ + + int x, y; /* column, row indices */ + + int hw; /* Half image width */ + + int hh; /* Half image height */ + + int wm1; /* Width of image, minus 1 */ + + int hm1; /* Height of image, minus 1 */ + + int nBytes; /* # bytes per pixel */ + + unsigned char *pixel; /* Temporary pixel holder */ + + ImVfbPtr pSrc, pDst; /* src, dst pixel pointers */ + + + + + + /* Check the flip direction */ + + if ( flipDirection != IMVFBXFLIP && flipDirection != IMVFBYFLIP && + + flipDirection != IMVFBXYFLIP ) + + { + + ImErrNo = IMEBADFLIP; + + return( IMVFBNULL ); + + } + + + + + + /* Allocate a new VFB, if necessary */ + + if ( vfbDst == IMVFBNEW ) + + { + + vfbDst = ImVfbAlloc( ImVfbQWidth( vfbSrc ), + + ImVfbQHeight( vfbSrc ), ImVfbQFields( vfbSrc ) ); + + if ( vfbDst == IMVFBNULL ) + + { + + ImErrNo = IMEMALLOC; + + return( IMVFBNULL ); + + } + + + + /* + + * For a new vfb, All the infomation (including any colortables) + + * in the source vfb must be re-written to the destination + + * in a flipped order therfore the range of pixel information + + * to parse (ie: hh and hw) is the entire source vfb + + * range (ie: h x w) + + */ + + ImVfbSClt( vfbDst, ImVfbQClt( vfbSrc ) ); + + hh = ImVfbQHeight( vfbDst ); + + hw = ImVfbQWidth( vfbDst ); + + } + + else if ( vfbDst == vfbSrc ) + + { + + /* + + * If the request is to replace the source Vfb with the flipped + + * pixel information thereby overwriting the original pixel + + * data, a short cut can be performed. Only half of the pixels + + * need to be parsed through to achieve the flipped effect + + * NOTE: the color table ( if any ) automatically remains + + * attached to the source vfb. + + */ + + hh = ImVfbQHeight( vfbDst )/2; + + hw = ImVfbQWidth( vfbDst )/2; + + + + } + + else if ( ImVfbQWidth( vfbSrc ) != ImVfbQWidth( vfbDst ) || + + ImVfbQHeight( vfbSrc ) != ImVfbQHeight( vfbDst ) || + + ImVfbQFields( vfbSrc ) != ImVfbQFields( vfbDst ) ) + + { + + /* If different VFB's, must have the same size. */ + + ImErrNo = IMEDIFFSIZE; + + return ( IMVFBNULL ); + + } + + else + + { + + /* + + * If the request is to overwrite a destination Vfb that + + * has already been allocated before the request and + + * is the same size as the source, then the full parsing + + * of pixel data must occur. + + */ + + hh = ImVfbQHeight( vfbDst ); + + hw = ImVfbQWidth( vfbDst ); + + } + + + + /* Allocate temporary space for one pixel. */ + + nBytes = ImVfbQNBytes( vfbSrc ); + + if ( (pixel = (unsigned char *) malloc( nBytes )) == NULL ) + + { + + ImErrNo = IMEMALLOC; + + return( IMVFBNULL ); + + } + + + + /* Flip the source into the destination. */ + + if ( flipDirection == IMVFBXFLIP || flipDirection == IMVFBXYFLIP ) + + { + + /* Flip horizontally. */ + + wm1 = ImVfbQWidth( vfbDst ) - 1; + + + + for ( y = 0; y < ImVfbQHeight( vfbSrc ); y++ ) + + { + + pSrc = ImVfbQPtr( vfbSrc, 0, y ); + + pDst = ImVfbQPtr( vfbDst, wm1, y ); + + for ( x = 0; x < hw; x++, ImVfbSRight( vfbSrc, pSrc ), + + ImVfbSLeft( vfbDst, pDst ) ) + + { + + memcpy( pixel, (void*) pSrc, nBytes ); + + memcpy( (void *) pSrc, pDst, nBytes ); + + memcpy( (void *) pDst, pixel, nBytes ); + + } + + } + + + + if ( flipDirection == IMVFBXFLIP ) + + { + + free( (char *)pixel ); + + return ( vfbDst ); + + } + + + + /* + + * If the execution reaches this point, then a flip of the + + * pixel data in the y direction must be performed. + + * Execution time can be saved by making the previously + + * x-flipped destination vfb the source vfb, in which + + * case the amount of pixel data of the vfb's height to parse + + * must be reduced in half. + + * NOTE: this is only necessary for requests to flip a source + + * vfb into a new destination vfb . + + */ + + vfbSrc = vfbDst; + + hh = ImVfbQHeight( vfbDst )/2; + + } + + + + + + /* Flip vertically. */ + + hm1 = ImVfbQHeight( vfbDst ) - 1; + + for ( x = 0; x < ImVfbQWidth( vfbSrc ); x++ ) + + { + + pSrc = ImVfbQPtr( vfbSrc, x, 0 ); + + pDst = ImVfbQPtr( vfbDst, x, hm1 ); + + for ( y = 0; y < hh; y++, ImVfbSDown( vfbSrc, pSrc ), + + ImVfbSUp( vfbDst, pDst ) ) + + { + + memcpy( pixel, pSrc, nBytes ); + + memcpy( (void *)pSrc, pDst, nBytes ); + + memcpy( (void *)pDst, pixel, nBytes ); + + } + + } + + free( (char *)pixel ); + + return ( vfbDst ); + +} + diff --git a/utils/roq2/libim/imvfbgamma.c b/utils/roq2/libim/imvfbgamma.c new file mode 100644 index 0000000..cde26d9 --- /dev/null +++ b/utils/roq2/libim/imvfbgamma.c @@ -0,0 +1,556 @@ +/** + + ** $Header: /roq/libim/imvfbgamma.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92186-9784 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/imvfbgamma.c 1 11/02/99 4:38p Zaphod $ " + + + +/** + + ** FILE + + ** imvfbgamma.c - Change a VFB's colors' gamma correction + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** + + ** + + ** HISTORY + + ** $Log: /roq/libim/imvfbgamma.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.3 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.2 1994/10/03 11:29:50 nadeau + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Updated comments. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.1 92/12/03 01:57:58 nadeau + + ** Initial revision + + ** + + ** + + ** + + **/ + + + +#include + +#include "iminternal.h" + + + + + + + +/* range of integer values: */ + + + +#define IM_MAXR 255 + +#define IM_MAXG 255 + +#define IM_MAXB 255 + + + + + +/* types of framebuffers: */ + + + +#define IM_FBINDEX 0 + +#define IM_FBRGB 1 + +#define IM_FBGRAY 2 + + + + + + + + + +/* + + * FUNCTION + + * ImVfbGamma - change a vfb's colors to be brighter or darker + + * + + * DESCRIPTION + + */ + + + +ImVfb * /* Returns gamma-corrected VFB */ + +ImVfbGamma( srcVfb, gamma, dstVfb ) + + ImVfb *srcVfb; /* source vfb */ + + double gamma; /* gamma correction */ + + ImVfb *dstVfb; /* destination vfb */ + +{ + + int r, g, b; /* red, green, blue values */ + + int i; /* counter */ + + int f; /* vfb field description */ + + int type; /* framebuffer type */ + + ImClt *srcClt; /* source CLT */ + + ImClt *dstClt; /* destination CLT */ + + float fr, fg, fb; /* floating point r, g, b */ + + ImCltPtr cp; /* internal clt pointer */ + + ImVfbPtr psrc, pdst; /* internal vfb pointers */ + + int ncolors; /* # colors in clt */ + + + + + + /* determine the fields on this vfb: */ + + + + f = ImVfbQFields( srcVfb ); + + + + + + /* determine vfb type: */ + + + + if( f & IMVFBRGB ) + + type = IM_FBRGB; + + else + + { + + if( (f&IMVFBINDEX8) || (f&IMVFBINDEX16) ) + + type = IM_FBINDEX; + + else + + type = IM_FBGRAY; + + } + + + + + + /* allocate the new vfb and clt: */ + + + + if( dstVfb == IMVFBNEW ) + + { + + dstVfb = ImVfbAlloc( ImVfbQWidth(srcVfb), ImVfbQHeight(srcVfb), f ); + + if( dstVfb == IMVFBNULL ) + + { + + ImErrNo = IMEMALLOC; + + return( IMVFBNULL ); + + } + + + + + + /* allocate the new clt, if necessary: */ + + + + if( type == IM_FBINDEX ) + + { + + srcClt = ImVfbQClt( srcVfb ); + + ncolors = ImCltQNColors( srcClt ); + + dstClt = ImCltAlloc( ncolors ); + + if( dstClt == IMCLTNULL ) + + { + + ImVfbFree( dstVfb ); + + ImErrNo = IMEMALLOC; + + return( IMVFBNULL ); + + } + + + + ImVfbSClt( dstVfb, dstClt ); + + } + + + + } + + + + + + + + /* if a clt situation, walk through the clt changing the colors: */ + + + + if( type == IM_FBINDEX ) + + { + + + + for( i = 0; i < ncolors; i++ ) + + { + + cp = ImCltQPtr( srcClt, i ); + + r = ImCltQRed( cp ); + + g = ImCltQGreen( cp ); + + b = ImCltQBlue( cp ); + + + + if( r > 0 ) + + { + + fr = (float) r / (float) IM_MAXR; + + fr = pow( fr, gamma ); + + r = (int) ( fr * (float) IM_MAXR + 0.5 ); + + } + + + + if( g > 0 ) + + { + + fg = (float) g / (float) IM_MAXG; + + fg = pow( fg, gamma ); + + g = (int) ( fg * (float) IM_MAXG + 0.5 ); + + } + + + + if( b > 0 ) + + { + + fb = (float) b / (float) IM_MAXB; + + fb = pow( fb, gamma ); + + b = (int) ( fb * (float) IM_MAXB + 0.5 ); + + } + + + + if( r < 0 ) r = 0; + + if( r > IM_MAXR ) r = IM_MAXR; + + if( g < 0 ) g = 0; + + if( g > IM_MAXG ) g = IM_MAXG; + + if( b < 0 ) b = 0; + + if( b > IM_MAXB ) b = IM_MAXB; + + + + cp = ImCltQPtr( dstClt, i ); + + ImCltSRed( cp, r ); + + ImCltSGreen( cp, g ); + + ImCltSBlue( cp, b ); + + } + + + + + + return( dstVfb ); + + } + + + + + + /* walk through the vfb, changing the gamma of the colors: */ + + + + for( psrc=ImVfbQFirst(srcVfb), pdst=ImVfbQFirst(dstVfb); + + psrc <= ImVfbQLast(srcVfb); + + ImVfbSInc(srcVfb,psrc), ImVfbSInc(dstVfb,pdst) ) + + { + + switch( type ) + + { + + case IM_FBRGB: + + r = ImVfbQRed( srcVfb, psrc ); + + g = ImVfbQGreen( srcVfb, psrc ); + + b = ImVfbQBlue( srcVfb, psrc ); + + + + if( r > 0 ) + + { + + fr = (float) r / (float) IM_MAXR; + + fr = pow( fr, gamma ); + + r = (int) ( fr * (float) IM_MAXR + 0.5 ); + + } + + + + if( g > 0 ) + + { + + fg = (float) g / (float) IM_MAXG; + + fg = pow( fg, gamma ); + + g = (int) ( fg * (float) IM_MAXG + 0.5 ); + + } + + + + if( b > 0 ) + + { + + fb = (float) b / (float) IM_MAXB; + + fb = pow( fb, gamma ); + + b = (int) ( fb * (float) IM_MAXB + 0.5 ); + + } + + + + if( r < 0 ) r = 0; + + if( r > IM_MAXR ) r = IM_MAXR; + + if( g < 0 ) g = 0; + + if( g > IM_MAXG ) g = IM_MAXG; + + if( b < 0 ) b = 0; + + if( b > IM_MAXB ) b = IM_MAXB; + + + + ImVfbSRed( dstVfb, pdst, r ); + + ImVfbSGreen( dstVfb, pdst, g ); + + ImVfbSBlue( dstVfb, pdst, b ); + + + + break; + + + + + + case IM_FBGRAY: + + g = ImVfbQGray( srcVfb, psrc ); + + + + if( g > 0 ) + + { + + fg = (float) g / (float) IM_MAXG; + + fg = pow( fg, gamma ); + + g = (int) ( fg * (float) IM_MAXG + 0.5 ); + + } + + + + if( g < 0 ) g = 0; + + if( g > IM_MAXG ) g = IM_MAXG; + + + + ImVfbSGray( dstVfb, pdst, g ); + + + + break; + + } + + + + } + + + + return( dstVfb ); + +} + diff --git a/utils/roq2/libim/imvfbhist.c b/utils/roq2/libim/imvfbhist.c new file mode 100644 index 0000000..a62147e --- /dev/null +++ b/utils/roq2/libim/imvfbhist.c @@ -0,0 +1,2744 @@ +/** + ** $Header: /roq/libim/imvfbhist.c 1 11/02/99 4:38p Zaphod $ + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + ** a division of General Atomics, San Diego, California, USA + ** + ** Users and possessors of this source code are hereby granted a + ** nonexclusive, royalty-free copyright and design patent license to + ** use this code in individual software. License is not granted for + ** commercial resale, in whole or in part, without prior written + ** permission from SDSC. This source is provided "AS IS" without express + ** or implied warranty of any kind. + ** + ** For further information contact: + ** E-Mail: info@sds.sdsc.edu + ** + ** Surface Mail: Information Center + ** San Diego Supercomputer Center + ** P.O. Box 85608 + ** San Diego, CA 92138-5608 + ** (619) 534-5000 + **/ + +#define HEADER " $Header: /roq/libim/imvfbhist.c 1 11/02/99 4:38p Zaphod $" + + + +/** + ** FILE + ** imhist.c - VFB histogram and statistics functions + ** + ** PROJECT + ** libim - SDSC image manipulation library + ** + ** DESCRIPTION + ** imhist compute image statistics and histograms. + ** + ** PUBLIC CONTENTS + ** d =defined constant + ** f =function + ** m =defined macro + ** t =typedef/struct/union + ** v =variable + ** ? =other + ** + ** ImVfbStat f compute simple image statistics + ** ImVfbHist f compute a histogram of pixel values + ** ImVfbHistTableFree f free histogram results + ** + ** PRIVATE CONTENTS + ** imVfbHistTableSort f sort histogram results + ** imVfbHistTableAlloc f allocate a histogram data table + ** imVfbHistFieldOrder f determine the best ordering for field hashing + ** imVfbHistAddValue f add value to histogram table + ** imVfbHistHashFree f free a hash table + ** + ** imXXXHist f histogram computation functions + ** + ** imHistBucket t bucket in collision list + ** imHistBucketHeader t collision list header + ** + ** imHistRGBA4WayFuncs v function table for 4-way RGBA hashing + ** imHistRGBA3WayFuncs v function table for 3-way RGBA hashing + ** imHistRGBA2WayFuncs v function table for 2-way RGBA hashing + ** + ** imHistHSIA4WayFuncs v function table for 4-way HSIA hashing + ** imHistHSIA3WayFuncs v function table for 3-way HSIA hashing + ** imHistHSIA2WayFuncs v function table for 2-way HSIA hashing + ** + ** IM_HIST1FIELD m build a function for single-field histograms + ** IM_HIST1FIELDHSI m same deal but for HSI fields + ** + ** IM_HIST2FIELD m build a function for double-field histograms + ** IM_HIST2FIELD1HSI m same deal but for 1 HSI field + ** IM_HIST2FIELD2HSI m same deal but for 2 HSI fields + ** + ** IM_HIST3FIELD m build a function for triple-field histograms + ** IM_HIST3FIELD1HSI m same deal but for 1 HSI field + ** IM_HIST3FIELD2HSI m same deal but for 2 HSI fields + ** IM_HIST3FIELD3HSI m same deal but for 2 HSI fields + ** + ** IM_HIST4FIELD m build a function for quadruple-field histograms + ** IM_HIST4FIELD3HSI m same deal but for 3 HSI fields + ** IM_HIST4FIELD3HSI2 m same deal but for 3 HSI fields, different alpha loc + ** + ** HISTORY + ** $Log: /roq/libim/imvfbhist.c $ +* +* 1 11/02/99 4:38p Zaphod + ** Revision 1.9 1995/06/29 00:28:04 bduggan + ** updated copyright year + ** + ** Revision 1.8 1995/06/16 08:54:12 bduggan + ** changed bzero to memset + ** + ** Revision 1.7 1994/10/03 11:29:51 nadeau + ** Updated to ANSI C and C++ compatibility. + ** Removed all use of register keyword. + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + ** Changed all float arguments to double. + ** Added forward declarations. + ** Added misc. casts to passify SGI and DEC compilers. + ** Changed all macros and defined constants to have names + ** starting with IM. + ** Updated comments. + ** Updated indenting on some code. + ** Updated copyright message. + ** + ** Revision 1.7 1994/10/03 11:29:51 nadeau + ** Updated to ANSI C and C++ compatibility. + ** Removed all use of register keyword. + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + ** Changed all float arguments to double. + ** Added forward declarations. + ** Added misc. casts to passify SGI and DEC compilers. + ** Changed all macros and defined constants to have names + ** starting with IM. + ** Updated comments. + ** Updated indenting on some code. + ** Updated copyright message. + ** + ** Revision 1.6 92/12/03 02:39:26 nadeau + ** Removed bogus semi-colons where they should be. + ** + ** Revision 1.5 92/12/03 01:55:03 nadeau + ** Total rewrite. + ** + ** Revision 1.4 92/08/31 17:38:40 vle + ** Updated copyright notice. + ** + ** Revision 1.3 92/08/26 12:46:23 groening + ** minor error corrections + ** + ** Revision 1.2 92/08/07 14:38:30 groening + ** Imitial revision + ** + **/ + +/** + ** CODE CREDITS + ** Custom development, Chris Groening, San Diego Supercomputer Center, 1992. + ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1992. + **/ + +#include "iminternal.h" + + + + +/* + * STRUCT & TYPEDEF + * imHistBucket - bucket in collision list + * + * DESCRIPTION + * Hash table collisions result in searching a collision list of + * buckets containing 3rd and 4th histogram field values (the 1st and + * 2nd histogram field values are used as hash table indexes). Each + * bucket holds an occurrence count and the two field values for which + * the bucket exists. + */ +#define IM_STARTSIZE 5 +#define IM_SIZEINC 20 +typedef struct imHistBucket +{ + sdsc_uint16 imhist_value1; /* First value in bucket. */ + sdsc_uint16 imhist_value2; /* Second value in bucket. */ + sdsc_uint32 imhist_nOccur; /* Number of times combo has occurred */ +} imHistBucket; + + + + + +/* + * STRUCT & TYPEDEF + * imHistBucketHeader - collision list header + * + * DESCRIPTION + * Collision lists are pointed to by a collision list header that is + * pointed at by each hash table entry. The header gives the size + * of the collision list, the next slot in the list that's available, + * and a pointer to the list itself. + */ +typedef struct imHistBucketHeader +{ + int imhist_allocSize; /* Allocated size of imhist_list*/ + int imhist_nextSlot; /* Next free slot in imhist_list*/ + imHistBucket *imhist_list; /* Collision list of buckets */ +} imHistBucketHeader; + + +#ifdef __STDC__ +static struct ImHistTable * imVfbHistTableSort( ImHistTable *histTable, int left, int right ); +static void imVfbHistFieldOrder( int fieldMask, int hashFields[3], ImVfb *vfb ); +static ImHistTable * imVfbHistTableAlloc( int numEntry, int fieldMask, int numChannels ); + +extern ImHistTable * imRHist(ImVfb* vfb ); +extern ImHistTable * imBHist(ImVfb* vfb ); +extern ImHistTable * imGHist(ImVfb* vfb ); +extern ImHistTable * imAHist(ImVfb* vfb ); + +extern ImHistTable * imHHist( ImVfb* vfb); +extern ImHistTable * imSHist( ImVfb* vfb); +extern ImHistTable * imIHist( ImVfb* vfb); + +extern ImHistTable * imMHist( ImVfb* vfb); +extern ImHistTable * imMAHist( ImVfb* vfb); +extern ImHistTable * im8Hist( ImVfb* vfb); +extern ImHistTable * im8AHist( ImVfb* vfb); +extern ImHistTable * im16Hist( ImVfb* vfb); +extern ImHistTable * im16AHist( ImVfb* vfb); + +extern ImHistTable * imRGHist( ImVfb* vfb); +extern ImHistTable * imRBHist( ImVfb* vfb); +extern ImHistTable * imRAHist( ImVfb* vfb); +extern ImHistTable * imGBHist( ImVfb* vfb); +extern ImHistTable * imGAHist( ImVfb* vfb); +extern ImHistTable * imBAHist( ImVfb* vfb); + +extern ImHistTable * imHSHist( ImVfb* vfb); +extern ImHistTable * imHIHist( ImVfb* vfb); +extern ImHistTable * imHAHist( ImVfb* vfb); +extern ImHistTable * imSIHist( ImVfb* vfb); +extern ImHistTable * imSAHist( ImVfb* vfb); +extern ImHistTable * imIAHist( ImVfb* vfb); +#else +extern struct ImHistTable * imVfbHistTableSort( ); +extern void imVfbHistFieldOrder( ); +extern ImHistTable * imVfbHistTableAlloc( ); + +extern ImHistTable * imRHist( ); +extern ImHistTable * imBHist( ); +extern ImHistTable * imGHist( ); +extern ImHistTable * imAHist( ); + +extern ImHistTable * imHHist( ); +extern ImHistTable * imSHist( ); +extern ImHistTable * imIHist( ); + +extern ImHistTable * imMHist( ); +extern ImHistTable * imMAHist( ); +extern ImHistTable * im8Hist( ); +extern ImHistTable * im8AHist( ); +extern ImHistTable * im16Hist( ); +extern ImHistTable * im16AHist( ); + +extern ImHistTable * imRGHist( ); +extern ImHistTable * imRBHist( ); +extern ImHistTable * imRAHist( ); +extern ImHistTable * imGBHist( ); +extern ImHistTable * imGAHist( ); +extern ImHistTable * imBAHist( ); + +extern ImHistTable * imHSHist( ); +extern ImHistTable * imHIHist( ); +extern ImHistTable * imHAHist( ); +extern ImHistTable * imSIHist( ); +extern ImHistTable * imSAHist( ); +extern ImHistTable * imIAHist( ); + +#endif + +#ifdef __STDC__ + +ImHistTable *imRGBHist(ImVfb * vfb); +ImHistTable *imRGAHist(ImVfb *vfb); +ImHistTable *imRBGHist(ImVfb *vfb); +ImHistTable *imRBAHist(ImVfb *vfb); +ImHistTable *imRAGHist(ImVfb *vfb); +ImHistTable *imRABHist(ImVfb *vfb); +ImHistTable *imBGRHist(ImVfb *vfb); +ImHistTable *imBGAHist(ImVfb *vfb); +ImHistTable *imGARHist(ImVfb *vfb); +ImHistTable *imGABHist(ImVfb *vfb); +ImHistTable *imBARHist(ImVfb *vfb); +ImHistTable *imBAGHist(ImVfb *vfb); +ImHistTable *imHSIHist(ImVfb *vfb); +ImHistTable *imHSAHist(ImVfb *vfb); +ImHistTable *imHISHist(ImVfb *vfb); +ImHistTable *imHIAHist(ImVfb *vfb); +ImHistTable *imHASHist(ImVfb *vfb); +ImHistTable *imHAIHist(ImVfb *vfb); +ImHistTable *imSIHHist(ImVfb *vfb); +ImHistTable *imSIAHist(ImVfb *vfb); +ImHistTable *imSAHHist(ImVfb *vfb); +ImHistTable *imSAIHist(ImVfb *vfb); +ImHistTable *imIAHHist(ImVfb *vfb); +ImHistTable *imIASHist(ImVfb *vfb); +ImHistTable *imRGBAHist(ImVfb *vfb); +ImHistTable *imRBGAHist(ImVfb *vfb); +ImHistTable *imRAGBHist(ImVfb *vfb); +ImHistTable *imRGBAHist(ImVfb *vfb); +ImHistTable *imGBRAHist(ImVfb *vfb); +ImHistTable *imGARBHist(ImVfb *vfb); +ImHistTable *imBARGHist(ImVfb *vfb); +ImHistTable *imHSIAHist(ImVfb *vfb); +ImHistTable *imHISAHist(ImVfb *vfb); +ImHistTable *imHAISHist(ImVfb *vfb); +ImHistTable *imSIHAHist(ImVfb *vfb); +ImHistTable *imSAHIHist(ImVfb *vfb); +ImHistTable *imIAHSHist(ImVfb *vfb); +ImHistTable *imHAISHist(ImVfb *vfb); + +#else + +ImHistTable *imRGBHist(); +ImHistTable *imRGAHist(); +ImHistTable *imRBGHist(); +ImHistTable *imRBAHist(); +ImHistTable *imRAGHist(); +ImHistTable *imRABHist(); +ImHistTable *imBGRHist(); +ImHistTable *imBGAHist(); +ImHistTable *imGARHist(); +ImHistTable *imGABHist(); +ImHistTable *imBARHist(); +ImHistTable *imBAGHist(); +ImHistTable *imHSIHist(); +ImHistTable *imHSAHist(); +ImHistTable *imHISHist(); +ImHistTable *imHIAHist(); +ImHistTable *imHASHist(); +ImHistTable *imHAIHist(); +ImHistTable *imSIHHist(); +ImHistTable *imSIAHist(); +ImHistTable *imSAHHist(); +ImHistTable *imSAIHist(); +ImHistTable *imIAHHist(); +ImHistTable *imIASHist(); +ImHistTable *imRGBAHist(); +ImHistTable *imRBGAHist(); +ImHistTable *imRAGBHist(); +ImHistTable *imRGBAHist(); +ImHistTable *imGBRAHist(); +ImHistTable *imGARBHist(); +ImHistTable *imBARGHist(); +ImHistTable *imHSIAHist(); +ImHistTable *imHISAHist(); +ImHistTable *imHAISHist(); +ImHistTable *imSIHAHist(); +ImHistTable *imSAHIHist(); +ImHistTable *imIAHSHist(); +ImHistTable *imHAISHist(); + +#endif + + + +/* + * GLOBALS + * imHistRGBA4WayFuncs - function table for 4-way RGBA hashing + * imHistRGBA3WayFuncs - function table for 3-way RGBA hashing + * + * imHistHSIA4WayFuncs - function table for 4-way HSIA hashing + * imHistHSIA3WayFuncs - function table for 3-way HSIA hashing + * + * DESCRIPTION + * A zillion individual histogram computation functions are vectored + * to based upon the choice of histogram fields. Vectoring is by + * way of the tables below, indexed into by the field numbers + * involved. + */ + +#define IMVFBH_RED 0 +#define IMVFBH_GREEN 1 +#define IMVFBH_BLUE 2 +#define IMVFBH_ALPHA 3 +#define IMVFBH_HUE 0 +#define IMVFBH_SATURATION 1 +#define IMVFBH_INTENSITY 2 + +#ifdef __STDC__ +static ImHistTable *(*imHistRGBA4WayFuncs[4][4])(ImVfb* ) = +#else +static ImHistTable *(*imHistRGBA4WayFuncs[4][4])( ) = +#endif +{ + /* RED GREEN BLUE ALPHA */ + /* RED */ { NULL, imRGBAHist, imRBGAHist, imRAGBHist }, + /* GREEN */ { imRGBAHist, NULL, imGBRAHist, imGARBHist }, + /* BLUE */ { imRBGAHist, imGBRAHist, NULL, imBARGHist }, + /* ALPHA */ { imRAGBHist, imGARBHist, imBARGHist, NULL } +}; + +#ifdef __STDC__ +static ImHistTable *(*imHistRGBA3WayFuncs[4][4][4])( ImVfb* ) = +#else +static ImHistTable *(*imHistRGBA3WayFuncs[4][4][4])( ) = +#endif +{ + /* RED GREEN BLUE ALPHA */ + /* RED */ {{NULL,NULL,NULL,NULL}, + {NULL,NULL,imRGBHist,imRGAHist}, + {NULL,imRBGHist,NULL,imRBAHist}, + {NULL,imRAGHist,imRABHist,NULL}}, + /* GREEN */ {{NULL,NULL,imRGBHist,imRGAHist}, + {NULL,NULL,NULL,NULL}, + {imBGRHist,NULL,NULL,imBGAHist}, + {imGARHist,NULL,imGABHist,NULL}}, + /* BLUE */ {{NULL,imRBGHist,NULL,imRBAHist}, + {imBGRHist,NULL,NULL,imBGAHist}, + {NULL,NULL,NULL,NULL}, + {imBARHist,imBAGHist,NULL,NULL}}, + /* ALPHA */ {{NULL,imRAGHist,imRABHist,NULL}, + {imGARHist,NULL,imGABHist,NULL}, + {imBARHist,imBAGHist,NULL,NULL}, + {NULL,NULL,NULL,NULL}} +}; + +#ifdef __STDC__ +static ImHistTable *(*imHistHSIA4WayFuncs[4][4])( ImVfb* ) = +#else +static ImHistTable *(*imHistHSIA4WayFuncs[4][4])( ) = +#endif +{ + /* HUE SAT INTENT ALPHA */ + /* HUE */ { NULL, imHSIAHist, imHISAHist, imHAISHist }, + /* SAT */ { imHSIAHist, NULL, imSIHAHist, imSAHIHist }, + /* INTENT */ { imHISAHist, imSIHAHist, NULL, imIAHSHist }, + /* ALPHA */ { imHAISHist, imSAHIHist, imIAHSHist, NULL } +}; + +#ifdef __STDC__ +static ImHistTable *(*imHistHSIA3WayFuncs[4][4][4])( ImVfb* ) = +#else +static ImHistTable *(*imHistHSIA3WayFuncs[4][4][4])( ) = +#endif +{ + /* HUE SAT INTEN ALPHA */ + /* HUE */ {{NULL,NULL,NULL,NULL}, + {NULL,NULL,imHSIHist,imHSAHist}, + {NULL,imHISHist,NULL,imHIAHist}, + {NULL,imHASHist,imHAIHist,NULL}}, + /* SAT */ {{NULL,NULL,imHSIHist,imHSAHist}, + {NULL,NULL,NULL,NULL}, + {imSIHHist,NULL,NULL,imSIAHist}, + {imSAHHist,NULL,imSAIHist,NULL}}, + /* INTEN */ {{NULL,imHISHist,NULL,imHIAHist}, + {imSIHHist,NULL,NULL,imSIAHist}, + {NULL,NULL,NULL,NULL}, + {imIAHHist,imIASHist,NULL,NULL}}, + /* ALPHA */ {{NULL,imHASHist,imHAIHist,NULL}, + {imSAHHist,NULL,imSAIHist,NULL}, + {imIAHHist,imIASHist,NULL,NULL}, + {NULL,NULL,NULL,NULL}} +}; + + +/* + * FUNCTION + * ImVfbStat - compute simple image statistics + * + * DESCRIPTION + * Scan the image and compute the minimum, maximum, and number of + * unique values for the field. All this is accomplished by scanning + * the image and using the pixel's field value as an index into a + * hash table of value occurrence counts (all initially zero). + * + * Note that the hash table size varies depending upon the type of + * field being scanned. For 8-bit integer fields, the table size + * is 256. For 16-bit integers its 65536. For saturation, + * and intensity floating point fields, we map from the range 0.0 to 1.0 + * to 0 to 1024. For the hue floating point field, we map from the + * range 0.0 to 360.0 to 0 to 1024. + */ + + +#define IMVFBH_HASH( vfb, query, hash ) \ +{ \ + int i, j; /* Counters */\ + int h, w; /* Height and width of image */\ + ImVfbPtr pPixel; /* Pixel pointer */\ + \ + pPixel = ImVfbQFirst( vfb ); \ + h = ImVfbQHeight( vfb ); \ + w = ImVfbQWidth( vfb ); \ + for ( i = 0; i < h; i++ ) \ + { \ + for ( j = 0; j < w; j++ ) \ + { \ + hash[ query( vfb, pPixel ) ]++; \ + ImVfbSInc( vfb, pPixel ); \ + } \ + } \ +} +#define IMVFBH_HASHHSI( vfb, which, factor, hash ) \ +{ \ + int i, j; /* Counters */\ + int h, w; /* Height and width of image */\ + ImVfbPtr pPixel; /* Pixel pointer */\ + int rgb[3]; /* RGB triplet */\ + float hsi[3]; /* HSI equivalent */\ + \ + pPixel = ImVfbQFirst( vfb ); \ + h = ImVfbQHeight( vfb ); \ + w = ImVfbQWidth( vfb ); \ + for ( i = 0; i < h; i++ ) \ + { \ + for ( j = 0; j < w; j++ ) \ + { \ + rgb[0] = ImVfbQRed( vfb, pPixel ); \ + rgb[1] = ImVfbQGreen( vfb, pPixel ); \ + rgb[2] = ImVfbQBlue( vfb, pPixel ); \ + ImRgbToHsi( rgb, hsi ); \ + hash[ (int)(hsi[which] * (factor)) ]++; \ + ImVfbSInc( vfb, pPixel ); \ + } \ + } \ +} + + + +float * /* Returns pointer to results */ +#ifdef __STDC__ +ImVfbStat( ImVfb *vfb, int field, float data[IMMAXNUMSTATS] ) +#else +ImVfbStat( vfb, field, data ) + ImVfb *vfb; /* incoming vfb */ + int field; /* What field to perform operation on */ + float data[IMMAXNUMSTATS]; /* array to return results in */ +#endif +{ + sdsc_int32 *hash; /* Hash table */ + int hashSize; /* Hash table size */ + int i; /* Counter */ + + + /* + * Create the hash table and zero it out. + */ + hashSize = 256; + if ( field & IMVFBINDEX16 ) + hashSize = 65536; + if ( field & (IMHUE|IMSATURATION|IMINTENSITY) ) + hashSize = 1024; + if ( (hash = (sdsc_int32 *) malloc( sizeof( sdsc_int32 ) * hashSize )) == NULL ) + { + ImErrNo = IMEMALLOC; + return ( NULL ); + } + for ( i = 0; i < hashSize; i++ ) + hash[i] = 0; + + + /* + * Hash the field values into the table, counting occurrences. + */ + if ( field & IMMONO ) IMVFBH_HASH( vfb, ImVfbQMono, hash ); + if ( field & IMINDEX8 ) IMVFBH_HASH( vfb, ImVfbQIndex8, hash ); + if ( field & IMINDEX16 ) IMVFBH_HASH( vfb, ImVfbQIndex16, hash ); + if ( field & IMRED ) IMVFBH_HASH( vfb, ImVfbQRed, hash ); + if ( field & IMGREEN ) IMVFBH_HASH( vfb, ImVfbQGreen, hash ); + if ( field & IMBLUE ) IMVFBH_HASH( vfb, ImVfbQBlue, hash ); + if ( field & IMALPHA ) IMVFBH_HASH( vfb, ImVfbQAlpha, hash ); + + if ( field & IMHUE ) IMVFBH_HASHHSI( vfb, 0, 1023.0/360.0, hash ); + if ( field & IMSATURATION ) IMVFBH_HASHHSI( vfb, 1, 1023.0, hash ); + if ( field & IMINTENSITY ) IMVFBH_HASHHSI( vfb, 2, 1023.0, hash ); + + + /* + * Scan the hash table. The first non-zero value near the top + * of the hash table is the lowest value occurring in the image. + * Similarly, the first non-zero value near the bottom of the + * hash table is the highest value occurring in the image. + * Finally, we count the number of non-zero entries to get a + * count of the number of unique values for the field. + */ + data[IMMINIMUM] = -1.0; + for ( i = 0; i < hashSize; i++ ) + { + if ( hash[i] != 0 ) + { + /* First non-zero entry at top of table. */ + data[IMMINIMUM] = (float)i; + break; + } + } + data[IMMAXIMUM] = -1.0; + for ( i = hashSize-1; i >= 0; i-- ) + { + if ( hash[i] != 0 ) + { + /* First non-zero entry at bottom of table. */ + data[IMMAXIMUM] = (float)i; + break; + } + } + data[IMUNIQUEVAL] = 0.0; + for ( i = 0; i < hashSize; i++ ) + if ( hash[i] != 0 ) + data[IMUNIQUEVAL] += 1.0; + + if ( field & IMHUE ) + { + data[IMMINIMUM] *= (360.0/1023.0); + data[IMMAXIMUM] *= (360.0/1023.0); + } + if ( field & (IMSATURATION|IMINTENSITY) ) + { + data[IMMINIMUM] /= 1023.0; + data[IMMAXIMUM] /= 1023.0; + } + + free( (char *)hash ); + + return ( data ); +} + + + + + +/* + * FUNCTION + * ImVfbHist - compute a histogram of pixel values + * + * DESCRIPTION + * Based upon image attributes and the field selection for creating + * the histogram, we vector to any of umpteen individualized histogram + * computation routines. The finished results are then sorted and + * returned. + * + * Our argument checking insures that color spaces are not mixed and + * that unsupported fields are excluded. This leaves the following + * histogram combinations: + * + * R, G, B, H, S, I, A, I8, and I16 individually + * I8A and I16A + * any 2 of RGBA + * any 2 of HSIA + * any 3 of RGBA + * any 3 of HSIA + * all 4 of RGBA + * all 4 of HSIA + */ + +ImHistTable * /* Returns histogram data */ +#ifdef __STDC__ +ImVfbHist( ImVfb *vfb, int fieldMask, int sort ) +#else +ImVfbHist( vfb, fieldMask, sort ) + ImVfb *vfb; /* incoming vfb to do histogram on */ + int fieldMask; /* what fields to do histogram on */ + int sort; /* whether or not to sort histogram results */ +#endif +{ + ImHistTable *histTable; /* return data structure */ + int hashFields[3]; /* Optimal hash ordering of fields */ + int nColorSpaces; /* Number of color spaces asked for */ + int nRGBA; /* Number of RGBA fields asked for */ + int nHSIA; /* Number of HSIA fields asked for */ + + + /* + * Check that the user hasn't requested any illegal field mixes. + */ + nColorSpaces = 0; + if ( fieldMask & (IMRED|IMGREEN|IMBLUE) ) + nColorSpaces++; + if ( fieldMask & (IMHUE|IMSATURATION|IMINTENSITY) ) + nColorSpaces++; + if ( fieldMask & (IMMONO|IMINDEX8|IMINDEX16) ) + nColorSpaces++; + if ( nColorSpaces > 1 ) + { + /* Can't mix color spaces. */ + ImErrNo = IMEFIELD; + return ( NULL ); + } + if ( fieldMask & (IMZ|IMWPROT|IMIDATA|IMFDATA) ) + { + /* Don't support these pixel fields. */ + ImErrNo = IMEFIELD; + return ( NULL ); + } + + + /* + * Build the histogram. The number of fields to use in computing + * the histogram depends upon the fieldMask and varies from 1 to 4 + * in several combinations. To keep things speedy and memory + * efficient, each combination of fields is treated separately. + */ + nRGBA = 0; + nHSIA = 0; + if ( fieldMask & IMRED ) nRGBA++; + if ( fieldMask & IMGREEN ) nRGBA++; + if ( fieldMask & IMBLUE ) nRGBA++; + if ( fieldMask & IMHUE ) nHSIA++; + if ( fieldMask & IMSATURATION ) nHSIA++; + if ( fieldMask & IMINTENSITY ) nHSIA++; + if ( fieldMask & IMALPHA ) nRGBA++, nHSIA++; + + /* + * When more than 2 fields are hashed, the choice of which two + * fields to hash on is important. For instance, if we hash on + * red and green, but the image has loads of different shades of + * blue, then the hash table will be only sparsely used, but we'll + * have a huge collision list. Since hashing is more efficient, we + * want to choose which two fields to hash on intelligently. This + * is done by selecting the two fields with the most unique values + * using imVfbHistFieldOrder(). + * + * When hashing on 2 or fewer fields, we don't worry about this. + */ + if ( nRGBA == 4 ) + { + /* + * Red, green, blue, and alpha, all at once. That's 4 + * fields. We'll create a hash table for hashing the first + * two fields, then use a collision list afterwards for + * the remaining 2 fields. + */ + imVfbHistFieldOrder( fieldMask, hashFields, vfb ); + histTable = (*imHistRGBA4WayFuncs[hashFields[0]][hashFields[1]])( vfb ); + } + else if ( nRGBA == 3 ) + { + /* + * Any 3 of Red, green, blue, and alpha. We'll create a + * hash table for hashing the first two fields, then use + * a collision list afterwards for the remaining field. + */ + imVfbHistFieldOrder( fieldMask, hashFields, vfb ); + histTable = (*imHistRGBA3WayFuncs[hashFields[0]][hashFields[1]][hashFields[2]])( vfb ); + } + else if ( nRGBA == 2 ) + { + /* + * Any 2 of red, green, blue, and alpha. We'll just hash + * with both of them. + */ + if ( fieldMask & IMRED && fieldMask & IMGREEN ) + histTable = imRGHist( vfb ); + if ( fieldMask & IMRED && fieldMask & IMBLUE ) + histTable = imRBHist( vfb ); + if ( fieldMask & IMRED && fieldMask & IMALPHA ) + histTable = imRAHist( vfb ); + if ( fieldMask & IMGREEN && fieldMask & IMBLUE ) + histTable = imGBHist( vfb ); + if ( fieldMask & IMGREEN && fieldMask & IMALPHA ) + histTable = imGAHist( vfb ); + if ( fieldMask & IMBLUE && fieldMask & IMALPHA ) + histTable = imBAHist( vfb ); + } + else if ( nHSIA == 4 ) + { + /* + * Hue, saturation, intensity, and alpha all at once. That's + * 4 fields. We'll create a hash table for hashing the first + * two fields, then use a collision list afterwards for + * the remaining 2 fields. + */ + imVfbHistFieldOrder( fieldMask, hashFields, vfb ); + histTable = (*imHistHSIA4WayFuncs[hashFields[0]][hashFields[1]])( vfb ); + } + else if ( nHSIA == 3 ) + { + /* + * Any 3 of Hue, saturation, intensity, and alpha. + * We'll create a hash table for hashing the first + * two fields, then use a collision list afterwards for + * the remaining field. + */ + imVfbHistFieldOrder( fieldMask, hashFields, vfb ); + histTable = (*imHistHSIA3WayFuncs[hashFields[0]][hashFields[1]][hashFields[2]])( vfb ); + } + else if ( nHSIA == 2 ) + { + /* + * Any 2 of hue, saturation, intensity, and alpha. We'll + * just hash with both of them. + */ + if ( fieldMask & IMHUE && fieldMask & IMSATURATION ) + histTable = imHSHist( vfb ); + if ( fieldMask & IMHUE && fieldMask & IMINTENSITY ) + histTable = imHIHist( vfb ); + if ( fieldMask & IMHUE && fieldMask & IMALPHA ) + histTable = imHAHist( vfb ); + if ( fieldMask & IMSATURATION && fieldMask & IMINTENSITY ) + histTable = imSIHist( vfb ); + if ( fieldMask & IMSATURATION && fieldMask & IMALPHA ) + histTable = imSAHist( vfb ); + if ( fieldMask & IMINTENSITY && fieldMask & IMALPHA ) + histTable = imIAHist( vfb ); + } + else if ( (fieldMask & (IMMONO|IMALPHA)) == (IMMONO|IMALPHA) ) + histTable = imMAHist( vfb ); + else if ( (fieldMask & (IMINDEX8|IMALPHA)) == (IMINDEX8|IMALPHA) ) + histTable = im8AHist( vfb ); + else if ( (fieldMask & (IMINDEX16|IMALPHA)) == (IMINDEX16|IMALPHA) ) + histTable = im16AHist( vfb ); + else if ( fieldMask & IMMONO ) + histTable = imMHist( vfb ); + else if ( fieldMask & IMINDEX8 ) + histTable = im8Hist( vfb ); + else if ( fieldMask & IMINDEX16 ) + histTable = im16Hist( vfb ); + else if ( fieldMask & IMRED ) + histTable = imRHist( vfb ); + else if ( fieldMask & IMGREEN ) + histTable = imGHist( vfb ); + else if ( fieldMask & IMBLUE ) + histTable = imBHist( vfb ); + else if ( fieldMask & IMHUE ) + histTable = imHHist( vfb ); + else if ( fieldMask & IMSATURATION ) + histTable = imSHist( vfb ); + else if ( fieldMask & IMINTENSITY ) + histTable = imIHist( vfb ); + else if ( fieldMask & IMALPHA ) + histTable = imAHist( vfb ); + else + { + ImErrNo = IMEFIELD; + return ( NULL ); + } + if ( histTable == NULL ) + { + /* ImErrNo already set. */ + return ( NULL ); + } + + + /* + * Sort the results. + */ + if ( !sort ) + return ( histTable ); + histTable = imVfbHistTableSort( histTable, 0, histTable->imhist_nEntries-1 ); + if ( histTable == NULL ) + { + ImErrNo = IMEMALLOC; + return ( NULL ); + } + return ( histTable ); +} + + + + + +/* + * FUNCTION + * imVfbHistTableSort - sort histogram results + * + * DESCRIPTION + * Recursively sort a portion of a histogram results structure. + * Sort based upon the count value, from high to low. + */ + + +#define IM_SWAP( field, i, j, results ) \ +{ \ + int tmp; /* Temp value holder */\ + tmp = (results)->field[i]; \ + (results)->field[i] = (results)->field[j]; \ + (results)->field[j] = tmp; \ +} + +#define IM_SWAPF( field, i, j, results ) \ +{ \ + float tmp; /* Temp value holder */\ + tmp = (results)->field[i]; \ + (results)->field[i] = (results)->field[j]; \ + (results)->field[j] = tmp; \ +} + +static ImHistTable * /* Returns sorted results */ +#ifdef __STDC__ +imVfbHistTableSort( ImHistTable *histTable, int left, int right ) +#else +imVfbHistTableSort( histTable, left, right ) + ImHistTable *histTable; /* Data to sort */ + int left; /* left indicie to sort */ + int right; /* right indicie to sort */ +#endif +{ + sdsc_uint32 v; /* Sort value */ + int i, j; /* counters */ + + + if ( right <= left ) + /* End recursion and just return. */ + return ( histTable ); + + v = histTable->imhist_nOccur[right]; /* start with right side*/ + i = left - 1; + j = right; + + for ( ; ; ) + { + /* Collapse the sort area to unsorted entries. */ + while ( histTable->imhist_nOccur[++i] > v ) + ; + while ( histTable->imhist_nOccur[--j] < v ) + ; + if ( i >= j ) + /* They're already sorted. */ + break; + + /* Swap the two values. */ + IM_SWAP( imhist_nOccur, i, j, histTable ); + + /* And swap the individual field values as well. */ + if ( histTable->imhist_mono != NULL ) + IM_SWAP( imhist_mono, i, j, histTable ); + if ( histTable->imhist_index8 != NULL ) + IM_SWAP( imhist_index8, i, j, histTable ); + if ( histTable->imhist_index16 != NULL ) + IM_SWAP( imhist_index16, i, j, histTable ); + if ( histTable->imhist_red != NULL ) + IM_SWAP( imhist_red, i, j, histTable ); + if ( histTable->imhist_green != NULL ) + IM_SWAP( imhist_green, i, j, histTable ); + if ( histTable->imhist_blue != NULL ) + IM_SWAP( imhist_blue, i, j, histTable ); + if ( histTable->imhist_alpha != NULL ) + IM_SWAP( imhist_alpha, i, j, histTable ); + if ( histTable->imhist_hue != NULL ) + IM_SWAPF( imhist_hue, i, j, histTable ); + if ( histTable->imhist_saturation != NULL ) + IM_SWAPF( imhist_saturation, i, j, histTable ); + if ( histTable->imhist_intensity != NULL ) + IM_SWAPF( imhist_intensity, i, j, histTable ); + } + + + /* Swap the current and right edge values. */ + IM_SWAP( imhist_nOccur, right, i, histTable ); + + /* And swap the individual field values as well. */ + if ( histTable->imhist_mono != NULL ) + IM_SWAP( imhist_mono, right, i, histTable ); + if ( histTable->imhist_index8 != NULL ) + IM_SWAP( imhist_index8, right, i, histTable ); + if ( histTable->imhist_index16 != NULL ) + IM_SWAP( imhist_index16, right, i, histTable ); + if ( histTable->imhist_red != NULL ) + IM_SWAP( imhist_red, right, i, histTable ); + if ( histTable->imhist_green != NULL ) + IM_SWAP( imhist_green, right, i, histTable ); + if ( histTable->imhist_blue != NULL ) + IM_SWAP( imhist_blue, right, i, histTable ); + if ( histTable->imhist_alpha != NULL ) + IM_SWAP( imhist_alpha, right, i, histTable ); + if ( histTable->imhist_hue != NULL ) + IM_SWAPF( imhist_hue, right, i, histTable ); + if ( histTable->imhist_saturation != NULL ) + IM_SWAPF( imhist_saturation, right, i, histTable ); + if ( histTable->imhist_intensity != NULL ) + IM_SWAPF( imhist_intensity, right, i, histTable ); + + + /* Recurse on the two sublists. */ + imVfbHistTableSort( histTable, left, i-1 ); + imVfbHistTableSort( histTable, i+1, right ); + + return ( histTable ); +} + +#undef IM_SWAP +#undef IM_SWAPF + + + + + +/* + * FUNCTION + * ImVfbHistTableFree - free histogram results + * + * DESCRUPTION + * Each of the field data lists is tossed. + */ +void /* Returns nothing */ +#ifdef __STDC__ +ImVfbHistTableFree( ImHistTable *histTable ) +#else +ImVfbHistTableFree( histTable ) + ImHistTable *histTable; /* Data to toss */ +#endif +{ + if ( histTable->imhist_nOccur != NULL ) + free( (char *)histTable->imhist_nOccur ); + if ( histTable->imhist_red != NULL ) + free( (char *)histTable->imhist_red ); + if ( histTable->imhist_green != NULL ) + free( (char *)histTable->imhist_green ); + if ( histTable->imhist_blue != NULL ) + free( (char *)histTable->imhist_blue ); + if ( histTable->imhist_alpha != NULL ) + free( (char *)histTable->imhist_alpha ); + if ( histTable->imhist_mono != NULL ) + free( (char *)histTable->imhist_mono ); + if ( histTable->imhist_index8 != NULL ) + free( (char *)histTable->imhist_index8 ); + if ( histTable->imhist_index16 != NULL ) + free( (char *)histTable->imhist_index16 ); + if ( histTable->imhist_hue != NULL ) + free( (char *)histTable->imhist_hue ); + if ( histTable->imhist_saturation != NULL ) + free( (char *)histTable->imhist_saturation ); + if ( histTable->imhist_intensity != NULL ) + free( (char *)histTable->imhist_intensity ); + + free( (char *)histTable ); +} + + + + + +/* + * FUNCTION + * imVfbHistTableAlloc - allocate a histogram data table + * + * DESCRIPTION + * Space is allocated for the histogram data table based upon the + * fields we're histogramming and the number of table entries. + */ + +#define IM_HISTALLOC( mask, field, name, type, num, table ) \ +{ \ + if ( ((mask) & (field)) && ((table)->name = (type *)malloc( \ + sizeof( type ) * (num)) ) == NULL ) \ + { \ + ImVfbHistTableFree( table ); \ + ImErrNo = IMEMALLOC; \ + return ( NULL ); \ + } \ +} + + + +static ImHistTable * /* Returns histogram table */ +#ifdef __STDC__ +imVfbHistTableAlloc( int numEntry, int fieldMask, int numChannels ) +#else +imVfbHistTableAlloc( numEntry, fieldMask, numChannels ) + int numEntry; /* number of entries in data strusture */ + int fieldMask; /* what fields to do histogram on */ + int numChannels; /* how many channels to do histogram on */ +#endif +{ + ImHistTable *histTable; /* return data structure */ + + /* Allocate the table itself. */ + if ((histTable = (ImHistTable *)malloc( sizeof( ImHistTable )) ) + == NULL ) + { + ImErrNo = IMEMALLOC; + return( NULL ); + } + memset( (void *) histTable, 0x00, sizeof( ImHistTable ) ); + + + /* Allocate the occurrence count list. */ + if ( (histTable->imhist_nOccur = (unsigned int *)malloc( sizeof( sdsc_uint32 ) * + numEntry)) == NULL ) + { + ImErrNo = IMEMALLOC; + return ( NULL ); + } + + /* Allocate the individual field data lists. */ + IM_HISTALLOC( fieldMask, IMMONO, imhist_mono, unsigned char, + numEntry, histTable ); + IM_HISTALLOC( fieldMask, IMINDEX8, imhist_index8, unsigned char, + numEntry, histTable ); + IM_HISTALLOC( fieldMask, IMINDEX16, imhist_index16, sdsc_uint16, + numEntry, histTable ); + IM_HISTALLOC( fieldMask, IMALPHA, imhist_alpha, unsigned char, + numEntry, histTable ); + IM_HISTALLOC( fieldMask, IMRED, imhist_red, unsigned char, + numEntry, histTable ); + IM_HISTALLOC( fieldMask, IMGREEN, imhist_green, unsigned char, + numEntry, histTable ); + IM_HISTALLOC( fieldMask, IMBLUE, imhist_blue, unsigned char, + numEntry, histTable ); + IM_HISTALLOC( fieldMask, IMHUE, imhist_hue, float, + numEntry, histTable ); + IM_HISTALLOC( fieldMask, IMSATURATION, imhist_saturation, float, + numEntry, histTable ); + IM_HISTALLOC( fieldMask, IMINTENSITY, imhist_intensity, float, + numEntry, histTable ); + + + /* set up other header fields */ + histTable->imhist_nEntries = numEntry; + histTable->imhist_fieldMask = fieldMask; + histTable->imhist_nFields = numChannels; + + return ( histTable ); +} + +#undef IM_HISTALLOC + + + + + +/* + * FUNCTION + * imVfbHistFieldOrder - determine the best ordering for field hasing + * + * DESCRIPTION + * To construct the histogram, pixel field data will be hashed into + * a hash table. For multi-channel histograms, such as RGB histograms, + * two fields are combined to construct a single hash index (such as + * index = (red << 8) | green). The selection of which two fields to + * combine is important. + * + * If, for instance, we have an image with lots of shades of blue, but + * use red and green for the hash index, then the hash table will be + * sparsly filled and we'll have huge collision chains. So, we'd like + * to pick the two hash fields as being the ones representing the most + * number of colors in the image. + * + * To do this we compute image statistics on the number of unique values + * for each of the fields and pick the top two. + */ + +static void /* Returns nothing */ +#ifdef __STDC__ +imVfbHistFieldOrder( int fieldMask, int hashFields[3], ImVfb *vfb ) +#else +imVfbHistFieldOrder( fieldMask, hashFields, vfb ) + int fieldMask; /* what fields to do histogram on */ + int hashFields[3]; /* Returned field order */ + ImVfb *vfb; /* incoming vfb to do histogram on*/ +#endif +{ + int i; /* Counter */ + float stats[IMMAXNUMSTATS]; /* Image statistics */ + int hashFieldUniques[4]; /* Number of unique values for fields*/ + int biggest, biggestUniques;/* Biggest number of unique values*/ + int bigger, biggerUniques; /* Bigger number of unique values*/ + int smaller, smallerUniques;/* Smaller number of unique values*/ + + + /* + * Zero out our count of unique values for each of the fields. + */ + hashFieldUniques[IMVFBH_RED] = 0; + hashFieldUniques[IMVFBH_GREEN] = 0; + hashFieldUniques[IMVFBH_BLUE] = 0; + hashFieldUniques[IMVFBH_ALPHA] = 0; + hashFieldUniques[IMVFBH_HUE] = 0; + hashFieldUniques[IMVFBH_SATURATION] = 0; + hashFieldUniques[IMVFBH_INTENSITY] = 0; + + + /* + * Compute the number of unique values for each of the fields. + */ + if ( fieldMask & IMRED ) + { + ImVfbStat( vfb, IMRED, stats ); + hashFieldUniques[IMVFBH_RED] = (int)stats[IMUNIQUEVAL]; + } + if ( fieldMask & IMGREEN ) + { + ImVfbStat( vfb, IMGREEN, stats ); + hashFieldUniques[IMVFBH_GREEN] = (int)stats[IMUNIQUEVAL]; + } + if ( fieldMask & IMBLUE ) + { + ImVfbStat( vfb, IMBLUE, stats ); + hashFieldUniques[IMVFBH_BLUE] = (int)stats[IMUNIQUEVAL]; + } + if ( fieldMask & IMALPHA ) + { + ImVfbStat( vfb, IMALPHA, stats ); + hashFieldUniques[IMVFBH_ALPHA] = (int)stats[IMUNIQUEVAL]; + } + if ( fieldMask & IMHUE ) + { + ImVfbStat( vfb, IMHUE, stats ); + hashFieldUniques[IMVFBH_HUE] = (int)stats[IMUNIQUEVAL]; + } + if ( fieldMask & IMSATURATION ) + { + ImVfbStat( vfb, IMSATURATION, stats ); + hashFieldUniques[IMVFBH_SATURATION] = (int)stats[IMUNIQUEVAL]; + } + if ( fieldMask & IMINTENSITY ) + { + ImVfbStat( vfb, IMINTENSITY, stats ); + hashFieldUniques[IMVFBH_INTENSITY] = (int)stats[IMUNIQUEVAL]; + } + + + /* + * Determine which fields have the largest number of unique values + * and select them as the best choices for the hash field order. + * + * NOTE: RGB and HSI are mutually exclusive, but both can have + * alpha fields. + */ + biggestUniques = biggerUniques = smallerUniques = -1; + biggest = bigger = smaller = -1; + for ( i = 0; i < 4; i++ ) + { + if ( hashFieldUniques[i] > biggestUniques ) + { + biggestUniques = hashFieldUniques[i]; + biggest = i; + } + } + for ( i = 0; i < 4; i++ ) + { + if ( hashFieldUniques[i] <= biggestUniques && + i != biggest && hashFieldUniques[i] > biggerUniques ) + { + biggerUniques = hashFieldUniques[i]; + bigger = i; + } + } + for ( i = 0; i < 4; i++ ) + { + if ( hashFieldUniques[i] <= biggerUniques && + i != biggest && i != bigger && + hashFieldUniques[i] > smallerUniques ) + { + smallerUniques = hashFieldUniques[i]; + smaller = i; + } + } + + hashFields[0] = biggest; + hashFields[1] = bigger; + hashFields[2] = smaller; +} + + + + + +/* + * FUNCTION + * imVfbHistAddValue - add value to histogram table + * + * DESCRUPTION + * When histogramming more than 2 fields at a time, we build a hash + * table where each entry points to a collision list. Two of the + * field values are used to index into the hash table. The remaining + * one or two fields are found in the collision list. + * + * imVfbHistAddValue( ) adds a new pair of values to the hash table + * and collision list. If the hashed-to entry doesn't have a + * collision list yet, one is allocated and initialized. The pair of + * values are then looked up in the collision list. If they're found, + * the entry's occurrence count is incremented. Otherwise the pair of + * values are put into a new collision list entry, possibly requiring + * a realloc to extend the collision list size to accomidate them. + */ + +static int imVfbHistNColors; /* Number of unique colors */ + +static int /* Returns status */ +#ifdef __STDC__ +imVfbHistAddValue( imHistBucketHeader **hash, int index, int value1, int value2 ) +#else +imVfbHistAddValue( hash, index, value1, value2 ) + imHistBucketHeader **hash; /* Hash table to add to */ + int index; /* Hash table index */ + int value1; /* First value to add */ + int value2; /* Second value to add */ +#endif +{ + imHistBucketHeader *pHash;/* Pointer to hash table entry */ + int k; /* Counter */ + + + /* + * If there's no collision list there yet, allocate one. + */ + pHash = hash[ index ]; + if ( pHash == NULL ) + { + /* Allocate a header. */ + if ( (hash[ index ] = pHash = (imHistBucketHeader *)malloc( + sizeof( imHistBucketHeader ))) == NULL ) + { + ImErrNo = IMEMALLOC; + return ( -1 ); + } + pHash->imhist_allocSize = IM_STARTSIZE; + pHash->imhist_nextSlot = 0; + + /* Allocate an initial collision list. */ + if ( (pHash->imhist_list = (imHistBucket *)malloc( + sizeof( imHistBucket ) * IM_STARTSIZE )) == NULL ) + { + ImErrNo = IMEMALLOC; + return ( -1 ); + } + } + + /* + * Walk the collision list looking for this value. + */ + for ( k = 0; k < pHash->imhist_nextSlot; k++ ) + { + if ( pHash->imhist_list[k].imhist_value1 == value1 && + pHash->imhist_list[k].imhist_value2 == value2 ) + { + ++pHash->imhist_list[k].imhist_nOccur; + return ( 0 ); + } + } + + /* + * Not found in the collision list. If there's no room left in + * the list, increase the size. + */ + if ( pHash->imhist_nextSlot == pHash->imhist_allocSize ) + { + pHash->imhist_allocSize += IM_SIZEINC; + if ( (pHash->imhist_list = (imHistBucket *)realloc( + (char *)pHash->imhist_list, + sizeof( imHistBucket ) * pHash->imhist_allocSize )) == NULL ) + { + ImErrNo = IMEMALLOC; + return ( -1 ); + } + } + + /* + * Add a new collision list entry. + */ + pHash->imhist_list[k].imhist_value1 = value1; + pHash->imhist_list[k].imhist_value2 = value2; + pHash->imhist_list[k].imhist_nOccur = 1; + pHash->imhist_nextSlot++; + imVfbHistNColors++; + return( 0 ); +} + + + + + +/* + * FUNCTION + * imVfbHistHashFree - free a hash table + * + * DESCRIPTION + * The hash table and all its collision lists are deallocated. + */ + +static void /* Returns nothing */ +#ifdef __STDC__ +imVfbHistHashFree( imHistBucketHeader **hash, int hashSize ) +#else +imVfbHistHashFree( hash, hashSize ) + imHistBucketHeader **hash; /* Hash table to toss */ + int hashSize; /* Size of hash table */ +#endif +{ + int j; /* Counter */ + + for ( j = 0; j < hashSize; j++ ) + { + if ( hash[ j ] == NULL ) + continue; + if ( hash[j]->imhist_list ) + free( (char *)hash[j]->imhist_list ); + free( (char *)hash[j] ); + } + free( (char *)hash ); +} + + + + + +/* + * SINGLE FIELD HISTOGRAMS + */ + +/* + * MACRO + * IM_HIST1FIELD - build a function for single-field histograms + * IM_HIST1FIELDHSI - same deal but for HSI fields + * + * DESCRIPTION + * Each of the histogram functions that compute for a single pixel + * field look nearly identical. They could all be combined into a + * single function, but we'd have to embedd lots of loop-invariant + * if-statements to select which field to work on. Instead, we + * split them into several nearly identical routines. + * + * These single-field histogram routines are all automatically + * generated by the IM_HIST1FIELD macro. Parameters to the macro + * select the function name to create, the field to compute on, + * the VFB query call to use to get that field value, the histogram + * table field to contain the value list, and the size of the hash + * table to work with. + * + * IM_HIST1FIELD hashes the field value into a hash table then counts + * the number of unique field values. A histogram data table is + * then allocated and those entries copied into it. + * + * IM_HIST1FIELDHSI is nearly identical to IM_HIST1FIELD, but does the + * extra work needed for dealing with HSI virtual fields. + */ +#ifdef __STDC__ +#define IM_VFBHEADER(fn) fn( ImVfb * vfb) +#else +#define IM_VFBHEADER(fn) fn(vfb) ImVfb *vfb; +#endif + + +#define IM_HIST1FIELD( funcName, field, query, tableField, hashSize ) \ +ImHistTable * /* Returns histogram table */\ +IM_VFBHEADER(funcName) \ +{ \ + int i, j; /* Counters */\ + int hash[hashSize]; /* Hash table counting unique pixels */\ + int nEntry; /* Number of unique pixels */\ + ImHistTable *histTable;/* Histogram table */\ + \ + /* Hash the color index into an occurrence table. */\ + memset( (void *)hash, 0x00, sizeof( int ) * (hashSize) ); \ + IMVFBH_HASH( vfb, query, hash ); \ + \ + /* Count the number of unique values. */\ + for ( nEntry = i = 0; i < hashSize; i++ ) \ + if ( hash[i] != 0 ) \ + ++nEntry; \ + \ + /* Allocate the histogram table. */\ + if ( (histTable = imVfbHistTableAlloc( nEntry, field, 1 )) == NULL )\ + { \ + /* ImErrNo already set. */\ + return( NULL ); \ + } \ + \ + /* Set the histogram table. */\ + for ( i = j = 0; i < (hashSize); i++ ) \ + { \ + if ( hash[i] != 0 ) \ + { \ + histTable->tableField[j] = i; \ + histTable->imhist_nOccur[j++] = hash[i]; \ + } \ + } \ + return ( histTable ); \ +} + +#define IM_HIST1FIELDHSI( funcName, field, which, factor, tableField, hashSize )\ +ImHistTable * /* Returns histogram table */\ +IM_VFBHEADER(funcName) \ +{ \ + int i, j; /* Counters */\ + int hash[hashSize]; /* Hash table counting unique pixels */\ + int nEntry; /* Number of unique pixels */\ + ImHistTable *histTable;/* Histogram table */\ + \ + /* Hash the color index into an occurrence table. */\ + memset( (void *)hash, 0x00, sizeof( int ) * (hashSize) ); \ + IMVFBH_HASHHSI( vfb, which, factor, hash ); \ + \ + /* Count the number of unique values. */\ + for ( nEntry = i = 0; i < hashSize; i++ ) \ + if ( hash[i] != 0 ) \ + ++nEntry; \ + \ + /* Allocate the histogram table. */\ + if ( (histTable = imVfbHistTableAlloc( nEntry, field, 1 )) == NULL )\ + { \ + /* ImErrNo already set. */\ + return( NULL ); \ + } \ + \ + /* Set the histogram table. */\ + for ( i = j = 0; i < (hashSize); i++ ) \ + { \ + if ( hash[i] != 0 ) \ + { \ + histTable->tableField[j] = (float)i/(factor); \ + histTable->imhist_nOccur[j++] = hash[i]; \ + } \ + } \ + return ( histTable ); \ +} + + +IM_HIST1FIELD( imMHist, IMMONO, ImVfbQMono, imhist_mono, 2 ) +IM_HIST1FIELD( im8Hist, IMINDEX8, ImVfbQIndex8, imhist_index8, 256 ) +IM_HIST1FIELD( im16Hist, IMINDEX16, ImVfbQIndex16, imhist_index16, 65536 ) + +IM_HIST1FIELD( imRHist, IMRED, ImVfbQRed, imhist_red, 256 ) +IM_HIST1FIELD( imGHist, IMGREEN, ImVfbQGreen, imhist_green, 256 ) +IM_HIST1FIELD( imBHist, IMBLUE, ImVfbQBlue, imhist_blue, 256 ) +IM_HIST1FIELD( imAHist, IMALPHA, ImVfbQAlpha, imhist_alpha, 256 ) + +IM_HIST1FIELDHSI( imHHist, IMHUE, 0, (1023.0/360.0), + imhist_hue, 1024 ) +IM_HIST1FIELDHSI( imSHist, IMSATURATION, 1, (1023.0), + imhist_saturation, 1024 ) +IM_HIST1FIELDHSI( imIHist, IMINTENSITY, 2, (1023.0), + imhist_intensity, 1024 ) + + + + + +/* + * DOUBLE FIELD HISTOGRAMS + */ + +/* + * MACRO + * IM_HIST2FIELD - build a function for double-field histograms + * IM_HIST2FIELD1HSI - same deal but for 1 HSI field + * IM_HIST2FIELD2HSI - same deal but for 2 HSI fields + * + * DESCRIPTION + * As with IM_HIST1FIELD, all of the double-field histogram functions + * are automatically generated. + * + * IM_HIST2FIELD hashes the field values into a hash table then counts + * the number of unique field values. A histogram data table is + * then allocated and those entries copied into it. + * + * IM_HIST1FIELD and IM_HIST2FIELD differ in that the former uses one + * pixel field in the hash function, and the later uses two. + * + * IM_HIST2FIELD1HSI and IM_HIST2FIELD2HSI are nearly identical to IM_HIST2FIELD, + * but include the extra work needed for dealing with HSI virtual fields. + */ + + +#define IM_HIST2FIELD( funcName, fields, query1, query2, tableField1, tableField2, hashSize1, hashSize2, shift ) \ +ImHistTable * /* Returns histogram table */\ +IM_VFBHEADER(funcName) \ +{ \ + int i, j, k; /* Counters */\ + int h, w; /* Height and width of image */\ + ImVfbPtr pPixel; /* Pixel pointer */\ + int hash[(hashSize1)*(hashSize2)];/* Table counting unique pixels*/\ + int nEntry; /* Number of unique pixels */\ + ImHistTable *histTable;/* Histogram table */\ + int hashSize; /* Hash table size */\ + int index; /* Hash table index */\ + \ + hashSize = (hashSize1) * (hashSize2); \ + \ + /* Hash the color index into an occurrence table. */\ + memset( (void *)hash, 0x00, sizeof( int ) * hashSize ); \ + pPixel = ImVfbQFirst( vfb ); \ + h = ImVfbQHeight( vfb ); \ + w = ImVfbQWidth( vfb ); \ + for ( i = 0; i < h; i++ ) \ + { \ + for ( k = 0; k < w; k++ ) \ + { \ + index = (query1( vfb, pPixel ) << (shift)) | \ + query2( vfb, pPixel ); \ + hash[ index ]++; \ + ImVfbSInc( vfb, pPixel ); \ + } \ + } \ + \ + /* Count the number of unique values. */\ + for ( nEntry = i = 0; i < hashSize; i++ ) \ + if ( hash[i] != 0 ) \ + ++nEntry; \ + \ + /* Allocate the histogram table. */\ + if ( (histTable = imVfbHistTableAlloc( nEntry, fields, 2 )) == NULL )\ + { \ + /* ImErrNo already set. */\ + return( NULL ); \ + } \ + \ + /* Set the histogram table. */\ + for ( i = j = 0; i < (hashSize1); i++ ) \ + { \ + for ( k = 0; k < (hashSize2); k++ ) \ + { \ + index = (i << (shift)) | k; \ + if ( hash[index] != 0 ) \ + { \ + histTable->tableField1[j] = i; \ + histTable->tableField2[j] = k; \ + histTable->imhist_nOccur[j++] = hash[index];\ + } \ + } \ + } \ + return ( histTable ); \ +} + + +#define IM_HIST2FIELD1HSI( funcName, fields, which1, factor1, query2, tableField1, tableField2, hashSize1, hashSize2, shift ) \ +ImHistTable * /* Returns histogram table */\ +IM_VFBHEADER(funcName) \ +{ \ + int i, j, k; /* Counters */\ + int h, w; /* Height and width of image */\ + ImVfbPtr pPixel; /* Pixel pointer */\ + int hash[(hashSize1)*(hashSize2)];/* Table counting unique pixels*/\ + int nEntry; /* Number of unique pixels */\ + ImHistTable *histTable;/* Histogram table */\ + int hashSize; /* Hash table size */\ + int index; /* Hash table index */\ + int rgb[3]; /* RGB triplet */\ + float hsi[3]; /* HSI equivalent */\ + \ + hashSize = (hashSize1) * (hashSize2); \ + \ + /* Hash the color index into an occurrence table. */\ + memset( (void *)hash, 0x00, sizeof( int ) * hashSize ); \ + pPixel = ImVfbQFirst( vfb ); \ + h = ImVfbQHeight( vfb ); \ + w = ImVfbQWidth( vfb ); \ + for ( i = 0; i < h; i++ ) \ + { \ + for ( k = 0; k < w; k++ ) \ + { \ + rgb[0] = ImVfbQRed( vfb, pPixel ); \ + rgb[1] = ImVfbQGreen( vfb, pPixel ); \ + rgb[2] = ImVfbQBlue( vfb, pPixel ); \ + ImRgbToHsi( rgb, hsi ); \ + index = ((int)(hsi[which1] * (factor1)) << (shift)) |\ + query2( vfb, pPixel ); \ + hash[ index ]++; \ + ImVfbSInc( vfb, pPixel ); \ + } \ + } \ + \ + /* Count the number of unique values. */\ + for ( nEntry = i = 0; i < hashSize; i++ ) \ + if ( hash[i] != 0 ) \ + ++nEntry; \ + \ + /* Allocate the histogram table. */\ + if ( (histTable = imVfbHistTableAlloc( nEntry, fields, 2 )) == NULL )\ + { \ + /* ImErrNo already set. */\ + return( NULL ); \ + } \ + \ + /* Set the histogram table. */\ + for ( i = j = 0; i < (hashSize1); i++ ) \ + { \ + for ( k = 0; k < (hashSize2); k++ ) \ + { \ + index = (i << (shift)) | k; \ + if ( hash[index] != 0 ) \ + { \ + histTable->tableField1[j] = (float)i/(factor1);\ + histTable->tableField2[j] = k; \ + histTable->imhist_nOccur[j++] = hash[index];\ + } \ + } \ + } \ + return ( histTable ); \ +} + + +#define IM_HIST2FIELD2HSI( funcName, fields, which1, factor1, which2, factor2, tableField1, tableField2, hashSize1, hashSize2, shift ) \ +ImHistTable * /* Returns histogram table */\ +IM_VFBHEADER(funcName) \ +{ \ + int i, j, k; /* Counters */\ + int h, w; /* Height and width of image */\ + ImVfbPtr pPixel; /* Pixel pointer */\ + int hash[(hashSize1)*(hashSize2)];/* Table counting unique pixels*/\ + int nEntry; /* Number of unique pixels */\ + ImHistTable *histTable;/* Histogram table */\ + int hashSize; /* Hash table size */\ + int index; /* Hash table index */\ + int rgb[3]; /* RGB triplet */\ + float hsi[3]; /* HSI equivalent */\ + \ + hashSize = (hashSize1) * (hashSize2); \ + \ + /* Hash the color index into an occurrence table. */\ + memset( (void *)hash, 0x00, sizeof( int ) * hashSize ); \ + pPixel = ImVfbQFirst( vfb ); \ + h = ImVfbQHeight( vfb ); \ + w = ImVfbQWidth( vfb ); \ + for ( i = 0; i < h; i++ ) \ + { \ + for ( k = 0; k < w; k++ ) \ + { \ + rgb[0] = ImVfbQRed( vfb, pPixel ); \ + rgb[1] = ImVfbQGreen( vfb, pPixel ); \ + rgb[2] = ImVfbQBlue( vfb, pPixel ); \ + ImRgbToHsi( rgb, hsi ); \ + index = ((int)(hsi[which1] * (factor1)) << (shift)) |\ + (int)(hsi[which2] * (factor2)); \ + hash[ index ]++; \ + ImVfbSInc( vfb, pPixel ); \ + } \ + } \ + \ + /* Count the number of unique values. */\ + for ( nEntry = i = 0; i < hashSize; i++ ) \ + if ( hash[i] != 0 ) \ + ++nEntry; \ + \ + /* Allocate the histogram table. */\ + if ( (histTable = imVfbHistTableAlloc( nEntry, fields, 2 )) == NULL )\ + { \ + /* ImErrNo already set. */\ + return( NULL ); \ + } \ + \ + /* Set the histogram table. */\ + for ( i = j = 0; i < (hashSize1); i++ ) \ + { \ + for ( k = 0; k < (hashSize2); k++ ) \ + { \ + index = (i << (shift)) | k; \ + if ( hash[index] != 0 ) \ + { \ + histTable->tableField1[j] = (float)i/(factor1);\ + histTable->tableField2[j] = (float)k/(factor2);\ + histTable->imhist_nOccur[j++] = hash[index];\ + } \ + } \ + } \ + return ( histTable ); \ +} + +/* + * All possible legal combinations of 2 fields: + * ( different orderings of the first 2 fields are equivalent ) + * + * R G + * R B + * R A + * + * G R (same as R G) + * G B + * G A + * + * B R (same as R B) + * B G (same as G B) + * B A + * + * M A + * 8 A + * 16 A + * + * H S + * H I + * H A + * + * S H (same as H S) + * S I + * S A + * + * I H (same as H I) + * I S (same as S I) + * I A + */ + +IM_HIST2FIELD( imRGHist, IMRED|IMGREEN, + ImVfbQRed, ImVfbQGreen, + imhist_red, imhist_green, + 256, 256, 8 ) +IM_HIST2FIELD( imRBHist, IMRED|IMBLUE, + ImVfbQRed, ImVfbQBlue, + imhist_red, imhist_blue, + 256, 256, 8 ) +IM_HIST2FIELD( imRAHist, IMRED|IMALPHA, + ImVfbQRed, ImVfbQAlpha, + imhist_red, imhist_alpha, + 256, 256, 8 ) + +IM_HIST2FIELD( imGAHist, IMGREEN|IMALPHA, + ImVfbQGreen, ImVfbQAlpha, + imhist_green, imhist_alpha, + 256, 256, 8 ) +IM_HIST2FIELD( imGBHist, IMGREEN|IMBLUE, + ImVfbQGreen, ImVfbQBlue, + imhist_green, imhist_blue, + 256, 256, 8 ) + +IM_HIST2FIELD( imBAHist, IMBLUE|IMALPHA, + ImVfbQBlue, ImVfbQAlpha, + imhist_blue, imhist_alpha, + 256, 256, 8 ) + + +IM_HIST2FIELD( imMAHist, IMMONO|IMALPHA, + ImVfbQMono, ImVfbQAlpha, + imhist_mono, imhist_alpha, + 2, 256, 8 ) +IM_HIST2FIELD( im8AHist, IMINDEX8|IMALPHA, + ImVfbQIndex8, ImVfbQAlpha, + imhist_index8, imhist_alpha, + 256, 256, 8 ) +IM_HIST2FIELD( im16AHist, IMINDEX16|IMALPHA, + ImVfbQIndex16, ImVfbQAlpha, + imhist_index16, imhist_alpha, + 65536, 256, 8 ) + + +IM_HIST2FIELD2HSI( imHSHist, IMHUE|IMSATURATION, + 0, (1023.0/360.0), 1, 1023.0, + imhist_hue, imhist_saturation, + 1024, 1024, 10 ) +IM_HIST2FIELD2HSI( imHIHist, IMHUE|IMINTENSITY, + 0, (1023.0/360.0), 2, 1023.0, + imhist_hue, imhist_intensity, + 1024, 1024, 10 ) +IM_HIST2FIELD1HSI( imHAHist, IMHUE|IMALPHA, + 0, (1023.0/360.0), ImVfbQAlpha, + imhist_hue, imhist_alpha, + 1024, 256, 8 ) + +IM_HIST2FIELD2HSI( imSIHist, IMSATURATION|IMINTENSITY, + 1, 1023.0, 2, (1023.0), + imhist_saturation, imhist_intensity, + 1024, 1024, 10 ) +IM_HIST2FIELD1HSI( imSAHist, IMSATURATION|IMALPHA, + 1, 1023.0, ImVfbQAlpha, + imhist_saturation, imhist_alpha, + 1024, 256, 8 ) + +IM_HIST2FIELD1HSI( imIAHist, IMINTENSITY|IMALPHA, + 2, 1023.0, ImVfbQAlpha, + imhist_intensity, imhist_alpha, + 1024, 256, 8 ) + + + + + +/* + * TRIPLE FIELD HISTOGRAMS + */ + +/* + * MACRO + * IM_HIST3FIELD - build a function for triple-field histograms + * IM_HIST3FIELD1HSI - same deal but for 1 HSI field + * IM_HIST3FIELD2HSI - same deal but for 2 HSI fields + * IM_HIST3FIELD3HSI - same deal but for 2 HSI fields + * + * DESCRIPTION + * As with IM_HIST1FIELD, all of the triple-field histogram functions + * are automatically generated. + * + * IM_HIST3FIELD hashes two field values together, then follows a + * collision chain along until it finds the third field's value, or + * reaches the end. In the first case it just increments the occurrence + * count. In the second case it adds a new entry to the collision list + * and sets the occurrence count to 1. + * + * After hasing is complete, IM_HIST3FIELD functions allocate a histogram + * data table and copy the data into it. + */ + + +#define IM_HIST3FIELD( funcName, fields, query1, query2, query3, tableField1, tableField2, tableField3, hashSize1, hashSize2, shift ) \ +ImHistTable * /* Returns histogram table */\ +IM_VFBHEADER(funcName) \ +{ \ + int i, j, k; /* Counters */\ + int h, w; /* Height and width of image */\ + ImVfbPtr pPixel; /* Pixel pointer */\ + imHistBucketHeader **hash;/* Hash table */\ + imHistBucketHeader *pHash;/* Hash table pointer */\ + int nEntry; /* Number of unique pixels */\ + ImHistTable *histTable;/* Histogram table */\ + int hashSize; /* Hash table size */\ + int index; /* Hash table index */\ + \ + /* Allocate the hash table as a table of collision lists. */\ + hashSize = (hashSize1) * (hashSize2); \ + if ( (hash = (imHistBucketHeader **)malloc( \ + sizeof( imHistBucketHeader * ) * hashSize )) == NULL ) \ + { \ + ImErrNo = IMEMALLOC; \ + return ( NULL ); \ + } \ + memset( (void *)hash, 0x00, sizeof( imHistBucketHeader * ) * hashSize );\ + \ + /* Hash the color index into an occurrence table. */\ + pPixel = ImVfbQFirst( vfb ); \ + h = ImVfbQHeight( vfb ); \ + w = ImVfbQWidth( vfb ); \ + imVfbHistNColors = 0; \ + for ( i = 0; i < h; i++ ) \ + { \ + for ( k = 0; k < w; k++ ) \ + { \ + index = (query1( vfb, pPixel ) << (shift)) | \ + query2( vfb, pPixel ); \ + if ( imVfbHistAddValue( hash, index, query3( vfb, pPixel ), 0 ) == -1 )\ + { \ + /* ImErrNo already set. */\ + imVfbHistHashFree( hash, hashSize ); \ + return ( NULL ); \ + } \ + ImVfbSInc( vfb, pPixel ); \ + } \ + } \ + \ + /* Allocate the histogram table. */\ + if ( (histTable = imVfbHistTableAlloc( imVfbHistNColors, fields, 3 )) == NULL )\ + { \ + /* ImErrNo already set. */\ + return( NULL ); \ + } \ + \ + /* Set the histogram table. */\ + for ( nEntry = i = 0; i < (hashSize1); i++ ) \ + { \ + for ( k = 0; k < (hashSize2); k++ ) \ + { \ + index = (i << (shift)) | k; \ + pHash = hash[ index ]; \ + if ( pHash == NULL ) \ + continue; \ + for ( j = 0; j < pHash->imhist_nextSlot; j++ ) \ + { \ + histTable->tableField1[nEntry] = i; \ + histTable->tableField2[nEntry] = k; \ + histTable->tableField3[nEntry] = pHash->imhist_list[j].imhist_value1;\ + histTable->imhist_nOccur[nEntry++] = pHash->imhist_list[j].imhist_nOccur;\ + } \ + /* Toss the collision list. */\ + free( (char *)pHash->imhist_list ); \ + free( (char *)pHash ); \ + } \ + } \ + free( (char *)hash ); \ + return ( histTable ); \ +} + + +#define IM_HIST3FIELD1HSI( funcName, fields, which1, factor1, query2, which3, factor3, tableField1, tableField2, tableField3, hashSize1, hashSize2, shift ) \ +ImHistTable * /* Returns histogram table */\ +IM_VFBHEADER(funcName) \ +{ \ + int i, j, k; /* Counters */\ + int h, w; /* Height and width of image */\ + ImVfbPtr pPixel; /* Pixel pointer */\ + imHistBucketHeader **hash;/* Hash table */\ + imHistBucketHeader *pHash;/* Hash table pointer */\ + int nEntry; /* Number of unique pixels */\ + ImHistTable *histTable;/* Histogram table */\ + int hashSize; /* Hash table size */\ + int index; /* Hash table index */\ + int rgb[3]; /* RGB triplet */\ + float hsi[3]; /* HSI equivalent */\ + \ + /* Allocate the hash table as a table of collision lists. */\ + hashSize = (hashSize1) * (hashSize2); \ + if ( (hash = (imHistBucketHeader **)malloc( \ + sizeof( imHistBucketHeader * ) * hashSize )) == NULL ) \ + { \ + ImErrNo = IMEMALLOC; \ + return ( NULL ); \ + } \ + memset( (void *)hash, 0x00, sizeof( imHistBucketHeader * ) * hashSize );\ + \ + /* Hash the color index into an occurrence table. */\ + pPixel = ImVfbQFirst( vfb ); \ + h = ImVfbQHeight( vfb ); \ + w = ImVfbQWidth( vfb ); \ + imVfbHistNColors = 0; \ + for ( i = 0; i < h; i++ ) \ + { \ + for ( k = 0; k < w; k++ ) \ + { \ + rgb[0] = ImVfbQRed( vfb, pPixel ); \ + rgb[1] = ImVfbQGreen( vfb, pPixel ); \ + rgb[2] = ImVfbQBlue( vfb, pPixel ); \ + ImRgbToHsi( rgb, hsi ); \ + index = ((int)(hsi[which1] * (factor1)) << (shift)) |\ + query2( vfb, pPixel ); \ + if ( imVfbHistAddValue( hash, index, (int)(hsi[which3] * (factor3)), 0 ) == -1 )\ + { \ + /* ImErrNo already set. */\ + imVfbHistHashFree( hash, hashSize ); \ + return ( NULL ); \ + } \ + ImVfbSInc( vfb, pPixel ); \ + } \ + } \ + \ + /* Allocate the histogram table. */\ + if ( (histTable = imVfbHistTableAlloc( imVfbHistNColors, fields, 3 )) == NULL )\ + { \ + /* ImErrNo already set. */\ + return( NULL ); \ + } \ + \ + /* Set the histogram table. */\ + for ( nEntry = i = 0; i < (hashSize1); i++ ) \ + { \ + for ( k = 0; k < (hashSize2); k++ ) \ + { \ + index = (i << (shift)) | k; \ + pHash = hash[ index ]; \ + if ( pHash == NULL ) \ + continue; \ + for ( j = 0; j < pHash->imhist_nextSlot; j++ ) \ + { \ + histTable->tableField1[nEntry] = (float)i/(factor1);\ + histTable->tableField2[nEntry] = k;\ + histTable->tableField3[nEntry] = (float)(pHash->imhist_list[j].imhist_value1)/(factor3);\ + histTable->imhist_nOccur[nEntry++] = pHash->imhist_list[j].imhist_nOccur;\ + } \ + /* Toss the collision list. */\ + free( (char *)pHash->imhist_list ); \ + free( (char *)pHash ); \ + } \ + } \ + free( (char *)hash ); \ + return ( histTable ); \ +} + + +#define IM_HIST3FIELD2HSI( funcName, fields, which1, factor1, which2, factor2, query3, tableField1, tableField2, tableField3, hashSize1, hashSize2, shift ) \ +ImHistTable * /* Returns histogram table */\ +IM_VFBHEADER(funcName) \ +{ \ + int i, j, k; /* Counters */\ + int h, w; /* Height and width of image */\ + ImVfbPtr pPixel; /* Pixel pointer */\ + imHistBucketHeader **hash;/* Hash table */\ + imHistBucketHeader *pHash;/* Hash table pointer */\ + int nEntry; /* Number of unique pixels */\ + ImHistTable *histTable;/* Histogram table */\ + int hashSize; /* Hash table size */\ + int index; /* Hash table index */\ + int rgb[3]; /* RGB triplet */\ + float hsi[3]; /* HSI equivalent */\ + \ + /* Allocate the hash table as a table of collision lists. */\ + hashSize = (hashSize1) * (hashSize2); \ + if ( (hash = (imHistBucketHeader **)malloc( \ + sizeof( imHistBucketHeader * ) * hashSize )) == NULL ) \ + { \ + ImErrNo = IMEMALLOC; \ + return ( NULL ); \ + } \ + memset( (void *)hash, 0x00, sizeof( imHistBucketHeader * ) * hashSize );\ + \ + /* Hash the color index into an occurrence table. */\ + pPixel = ImVfbQFirst( vfb ); \ + h = ImVfbQHeight( vfb ); \ + w = ImVfbQWidth( vfb ); \ + imVfbHistNColors = 0; \ + for ( i = 0; i < h; i++ ) \ + { \ + for ( k = 0; k < w; k++ ) \ + { \ + rgb[0] = ImVfbQRed( vfb, pPixel ); \ + rgb[1] = ImVfbQGreen( vfb, pPixel ); \ + rgb[2] = ImVfbQBlue( vfb, pPixel ); \ + ImRgbToHsi( rgb, hsi ); \ + index = ((int)(hsi[which1] * (factor1)) << (shift)) |\ + (int)(hsi[which2] * (factor2)); \ + if ( imVfbHistAddValue( hash, index, query3( vfb, pPixel ), 0 ) == -1 )\ + { \ + /* ImErrNo already set. */\ + imVfbHistHashFree( hash, hashSize ); \ + return ( NULL ); \ + } \ + ImVfbSInc( vfb, pPixel ); \ + } \ + } \ + \ + /* Allocate the histogram table. */\ + if ( (histTable = imVfbHistTableAlloc( imVfbHistNColors, fields, 3 )) == NULL )\ + { \ + /* ImErrNo already set. */\ + return( NULL ); \ + } \ + \ + /* Set the histogram table. */\ + for ( nEntry = i = 0; i < (hashSize1); i++ ) \ + { \ + for ( k = 0; k < (hashSize2); k++ ) \ + { \ + index = (i << (shift)) | k; \ + pHash = hash[ index ]; \ + if ( pHash == NULL ) \ + continue; \ + for ( j = 0; j < pHash->imhist_nextSlot; j++ ) \ + { \ + histTable->tableField1[nEntry] = (float)i/(factor1);\ + histTable->tableField2[nEntry] = (float)k/(factor2);\ + histTable->tableField3[nEntry] = pHash->imhist_list[j].imhist_value1;\ + histTable->imhist_nOccur[nEntry++] = pHash->imhist_list[j].imhist_nOccur;\ + } \ + /* Toss the collision list. */\ + free( (char *)pHash->imhist_list ); \ + free( (char *)pHash ); \ + } \ + } \ + free( (char *)hash ); \ + return ( histTable ); \ +} + + +#define IM_HIST3FIELD3HSI( funcName, fields, which1, factor1, which2, factor2, which3, factor3, tableField1, tableField2, tableField3, hashSize1, hashSize2, shift ) \ +ImHistTable * /* Returns histogram table */\ +IM_VFBHEADER(funcName) \ +{ \ + int i, j, k; /* Counters */\ + int h, w; /* Height and width of image */\ + ImVfbPtr pPixel; /* Pixel pointer */\ + imHistBucketHeader **hash;/* Hash table */\ + imHistBucketHeader *pHash;/* Hash table pointer */\ + int nEntry; /* Number of unique pixels */\ + ImHistTable *histTable;/* Histogram table */\ + int hashSize; /* Hash table size */\ + int index; /* Hash table index */\ + int rgb[3]; /* RGB triplet */\ + float hsi[3]; /* HSI equivalent */\ + \ + /* Allocate the hash table as a table of collision lists. */\ + hashSize = (hashSize1) * (hashSize2); \ + if ( (hash = (imHistBucketHeader **)malloc( \ + sizeof( imHistBucketHeader * ) * hashSize )) == NULL ) \ + { \ + ImErrNo = IMEMALLOC; \ + return ( NULL ); \ + } \ + memset( (void *)hash, 0x00, sizeof( imHistBucketHeader * ) * hashSize );\ + \ + /* Hash the color index into an occurrence table. */\ + pPixel = ImVfbQFirst( vfb ); \ + h = ImVfbQHeight( vfb ); \ + w = ImVfbQWidth( vfb ); \ + imVfbHistNColors = 0; \ + for ( i = 0; i < h; i++ ) \ + { \ + for ( k = 0; k < w; k++ ) \ + { \ + rgb[0] = ImVfbQRed( vfb, pPixel ); \ + rgb[1] = ImVfbQGreen( vfb, pPixel ); \ + rgb[2] = ImVfbQBlue( vfb, pPixel ); \ + ImRgbToHsi( rgb, hsi ); \ + index = ((int)(hsi[which1] * (factor1)) << (shift)) |\ + (int)(hsi[which2] * (factor2)); \ + if ( imVfbHistAddValue( hash, index, (int)(hsi[which3] * (factor3)), 0 ) == -1 )\ + { \ + /* ImErrNo already set. */\ + imVfbHistHashFree( hash, hashSize ); \ + return ( NULL ); \ + } \ + ImVfbSInc( vfb, pPixel ); \ + } \ + } \ + \ + /* Allocate the histogram table. */\ + if ( (histTable = imVfbHistTableAlloc( imVfbHistNColors, fields, 3 )) == NULL )\ + { \ + /* ImErrNo already set. */\ + return( NULL ); \ + } \ + \ + /* Set the histogram table. */\ + for ( nEntry = i = 0; i < (hashSize1); i++ ) \ + { \ + for ( k = 0; k < (hashSize2); k++ ) \ + { \ + index = (i << (shift)) | k; \ + pHash = hash[ index ]; \ + if ( pHash == NULL ) \ + continue; \ + for ( j = 0; j < pHash->imhist_nextSlot; j++ ) \ + { \ + histTable->tableField1[nEntry] = (float)i/(factor1);\ + histTable->tableField2[nEntry] = (float)k/(factor2);\ + histTable->tableField3[nEntry] = (float)pHash->imhist_list[j].imhist_value1/(factor3);\ + histTable->imhist_nOccur[nEntry++] = pHash->imhist_list[j].imhist_nOccur;\ + } \ + /* Toss the collision list. */\ + free( (char *)pHash->imhist_list ); \ + free( (char *)pHash ); \ + } \ + } \ + free( (char *)hash ); \ + return ( histTable ); \ +} + +/* + * All possible legal combinations of 3 fields: + * ( different orderings of the first 2 fields are equivalent ) + * + * R G B + * R G A + * R B G + * R B A + * R A B + * R A G + * + * B R G (same as R B G) + * B R A (same as R B A) + * B G R + * B G A + * B A R + * B A G + * + * G B R (same as B G R) + * G B A (same as B G A) + * G R B (same as R G B) + * G R A (same as R G A) + * G A R + * G A B + * + * A R B (same as R A B) + * A R G (same as R A G) + * A B R (same as B A R) + * A B G (same as B A G) + * A G R (same as G A R) + * A G B (same as G A B) + * + * H S I + * H S A + * H I S + * H I A + * H A S + * H A I + * + * S H I (same as H S I) + * S H A (same as H S A) + * S I H + * S I A + * S A H + * S A I + * + * I H S (same as H I S) + * I H A (same as H I A) + * I S H (same as S I H) + * I S A (same as S I A) + * I A H + * I A S + * + * A H S (same as H A S) + * A H I (same as H A I) + * A S H (same as S A H) + * A S I (same as S A I) + * A I H (same as I A H) + * A I S (same as I A S) + */ + +IM_HIST3FIELD( imRGBHist, IMRED|IMGREEN|IMBLUE, + ImVfbQRed, ImVfbQGreen, ImVfbQBlue, + imhist_red, imhist_green, imhist_blue, + 256, 256, 8 ) +IM_HIST3FIELD( imRGAHist, IMRED|IMGREEN|IMALPHA, + ImVfbQRed, ImVfbQGreen, ImVfbQAlpha, + imhist_red, imhist_green, imhist_alpha, + 256, 256, 8 ) +IM_HIST3FIELD( imRBGHist, IMRED|IMBLUE|IMGREEN, + ImVfbQRed, ImVfbQBlue, ImVfbQGreen, + imhist_red, imhist_blue, imhist_green, + 256, 256, 8 ) +IM_HIST3FIELD( imRBAHist, IMRED|IMBLUE|IMALPHA, + ImVfbQRed, ImVfbQBlue, ImVfbQAlpha, + imhist_red, imhist_blue, imhist_alpha, + 256, 256, 8 ) +IM_HIST3FIELD( imRABHist, IMRED|IMALPHA|IMBLUE, + ImVfbQRed, ImVfbQAlpha, ImVfbQBlue, + imhist_red, imhist_alpha, imhist_blue, + 256, 256, 8 ) +IM_HIST3FIELD( imRAGHist, IMRED|IMALPHA|IMGREEN, + ImVfbQRed, ImVfbQAlpha, ImVfbQGreen, + imhist_red, imhist_alpha, imhist_green, + 256, 256, 8 ) + +IM_HIST3FIELD( imBGRHist, IMBLUE|IMGREEN|IMRED, + ImVfbQBlue, ImVfbQGreen, ImVfbQRed, + imhist_blue, imhist_green, imhist_red, + 256, 256, 8 ) +IM_HIST3FIELD( imBGAHist, IMBLUE|IMGREEN|IMALPHA, + ImVfbQBlue, ImVfbQGreen, ImVfbQAlpha, + imhist_blue, imhist_green, imhist_alpha, + 256, 256, 8 ) +IM_HIST3FIELD( imBARHist, IMBLUE|IMALPHA|IMRED, + ImVfbQBlue, ImVfbQAlpha, ImVfbQRed, + imhist_blue, imhist_alpha, imhist_red, + 256, 256, 8 ) +IM_HIST3FIELD( imBAGHist, IMBLUE|IMALPHA|IMGREEN, + ImVfbQBlue, ImVfbQAlpha, ImVfbQGreen, + imhist_blue, imhist_alpha, imhist_green, + 256, 256, 8 ) + +IM_HIST3FIELD( imGARHist, IMGREEN|IMALPHA|IMRED, + ImVfbQGreen, ImVfbQAlpha, ImVfbQRed, + imhist_green, imhist_alpha, imhist_red, + 256, 256, 8 ) +IM_HIST3FIELD( imGABHist, IMGREEN|IMALPHA|IMBLUE, + ImVfbQGreen, ImVfbQAlpha, ImVfbQBlue, + imhist_green, imhist_alpha, imhist_blue, + 256, 256, 8 ) + +IM_HIST3FIELD3HSI( imHSIHist, IMHUE|IMSATURATION|IMINTENSITY, + 0, (1023.0/360.0), 1, 1023.0, 2, 1023.0, + imhist_hue, imhist_saturation, imhist_intensity, + 1024, 1024, 10 ) +IM_HIST3FIELD2HSI( imHSAHist, IMHUE|IMSATURATION|IMALPHA, + 0, (1023.0/360.0), 1, 1023.0, ImVfbQAlpha, + imhist_hue, imhist_saturation, imhist_alpha, + 1024, 1024, 10 ) +IM_HIST3FIELD3HSI( imHISHist, IMHUE|IMINTENSITY|IMSATURATION, + 0, (1023.0/360.0), 2, 1023.0, 1, 1023.0, + imhist_hue, imhist_intensity, imhist_saturation, + 1024, 1024, 10 ) +IM_HIST3FIELD2HSI( imHIAHist, IMHUE|IMINTENSITY|IMALPHA, + 0, (1023.0/360.0), 2, 1023.0, ImVfbQAlpha, + imhist_hue, imhist_intensity, imhist_alpha, + 1024, 1024, 10 ) +IM_HIST3FIELD1HSI( imHASHist, IMHUE|IMALPHA|IMSATURATION, + 0, (1023.0/360.0), ImVfbQAlpha, 1, 1023.0, + imhist_hue, imhist_alpha, imhist_saturation, + 1024, 256, 8 ) +IM_HIST3FIELD1HSI( imHAIHist, IMHUE|IMALPHA|IMINTENSITY, + 0, (1023.0/360.0), ImVfbQAlpha, 2, 1023.0, + imhist_hue, imhist_alpha, imhist_intensity, + 1024, 256, 8 ) + +IM_HIST3FIELD3HSI( imSIHHist, IMSATURATION|IMINTENSITY|IMHUE, + 1, 1023.0, 2, 1023.0, 0, (1023.0/360.0), + imhist_saturation, imhist_intensity, imhist_hue, + 1024, 1024, 10 ) +IM_HIST3FIELD2HSI( imSIAHist, IMSATURATION|IMINTENSITY|IMALPHA, + 1, 1023.0, 2, 1023.0, ImVfbQAlpha, + imhist_saturation, imhist_intensity, imhist_alpha, + 1024, 1024, 10 ) +IM_HIST3FIELD1HSI( imSAHHist, IMSATURATION|IMALPHA|IMHUE, + 1, 1023.0, ImVfbQAlpha, 0, (1023.0/360.0), + imhist_saturation, imhist_alpha, imhist_hue, + 1024, 256, 8 ) +IM_HIST3FIELD1HSI( imSAIHist, IMSATURATION|IMALPHA|IMINTENSITY, + 1, 1023.0, ImVfbQAlpha, 2, 1023.0, + imhist_saturation, imhist_alpha, imhist_intensity, + 1024, 256, 8 ) + +IM_HIST3FIELD1HSI( imIAHHist, IMINTENSITY|IMALPHA|IMHUE, + 2, 1023.0, ImVfbQAlpha, 0, (1023.0/360.0), + imhist_intensity, imhist_alpha, imhist_hue, + 1024, 256, 8 ) +IM_HIST3FIELD1HSI( imIASHist, IMINTENSITY|IMALPHA|IMSATURATION, + 2, 1023.0, ImVfbQAlpha, 1, 1023.0, + imhist_intensity, imhist_alpha, imhist_saturation, + 1024, 256, 8 ) + + + + + +/* + * QUADRUPLE FIELD HISTOGRAMS + */ + +/* + * MACRO + * IM_HIST4FIELD - build a function for quadruple-field histograms + * IM_HIST4FIELD3HSI - same deal but for 3 HSI fields + * IM_HIST4FIELD3HSI2 - same deal but for 3 HSI fields, different alpha loc + * + * DESCRIPTION + * As with IM_HIST1FIELD, all of the quadruple-field histogram functions + * are automatically generated. + * + * IM_HIST4FIELD hashes two field values together, then follows a + * collision chain along until it finds the third and fourth field values, + * or reaches the end. In the first case it just increments the occurrence + * count. In the second case it adds a new entry to the collision list + * and sets the occurrence count to 1. + * + * After hasing is complete, IM_HIST4FIELD functions allocate a histogram + * data table and copy the data into it. + */ + + +#define IM_HIST4FIELD( funcName, fields, query1, query2, query3, query4, tableField1, tableField2, tableField3, tableField4, hashSize1, hashSize2, shift ) \ +ImHistTable * /* Returns histogram table */\ +IM_VFBHEADER(funcName) \ +{ \ + int i, j, k; /* Counters */\ + int h, w; /* Height and width of image */\ + ImVfbPtr pPixel; /* Pixel pointer */\ + imHistBucketHeader **hash;/* Hash table */\ + imHistBucketHeader *pHash;/* Hash table pointer */\ + int nEntry; /* Number of unique pixels */\ + ImHistTable *histTable;/* Histogram table */\ + int hashSize; /* Hash table size */\ + int index; /* Hash table index */\ + \ + /* Allocate the hash table as a table of collision lists. */\ + hashSize = (hashSize1) * (hashSize2); \ + if ( (hash = (imHistBucketHeader **)malloc( \ + sizeof( imHistBucketHeader * ) * hashSize )) == NULL ) \ + { \ + ImErrNo = IMEMALLOC; \ + return ( NULL ); \ + } \ + memset( (void *)hash, 0x00, sizeof( imHistBucketHeader * ) * hashSize );\ + \ + /* Hash the color index into an occurrence table. */\ + pPixel = ImVfbQFirst( vfb ); \ + h = ImVfbQHeight( vfb ); \ + w = ImVfbQWidth( vfb ); \ + imVfbHistNColors = 0; \ + for ( i = 0; i < h; i++ ) \ + { \ + for ( k = 0; k < w; k++ ) \ + { \ + index = (query1( vfb, pPixel ) << (shift)) | \ + query2( vfb, pPixel ); \ + if ( imVfbHistAddValue( hash, index, query3( vfb, pPixel ), query4( vfb, pPixel ) ) == -1 )\ + { \ + /* ImErrNo already set. */\ + imVfbHistHashFree( hash, hashSize ); \ + return ( NULL ); \ + } \ + ImVfbSInc( vfb, pPixel ); \ + } \ + } \ + \ + /* Allocate the histogram table. */\ + if ( (histTable = imVfbHistTableAlloc( imVfbHistNColors, fields, 3 )) == NULL )\ + { \ + /* ImErrNo already set. */\ + return( NULL ); \ + } \ + \ + /* Set the histogram table. */\ + for ( nEntry = i = 0; i < (hashSize1); i++ ) \ + { \ + for ( k = 0; k < (hashSize2); k++ ) \ + { \ + index = (i << (shift)) | k; \ + pHash = hash[ index ]; \ + if ( pHash == NULL ) \ + continue; \ + for ( j = 0; j < pHash->imhist_nextSlot; j++ ) \ + { \ + histTable->tableField1[nEntry] = i; \ + histTable->tableField2[nEntry] = k; \ + histTable->tableField3[nEntry] = pHash->imhist_list[j].imhist_value1;\ + histTable->tableField4[nEntry] = pHash->imhist_list[j].imhist_value2;\ + histTable->imhist_nOccur[nEntry++] = pHash->imhist_list[j].imhist_nOccur;\ + } \ + /* Toss the collision list. */\ + free( (char *)pHash->imhist_list ); \ + free( (char *)pHash ); \ + } \ + } \ + free( (char *)hash ); \ + return ( histTable ); \ +} + + +#define IM_HIST4FIELD3HSI( funcName, fields, which1, factor1, query2, which3, factor3, which4, factor4, tableField1, tableField2, tableField3, tableField4, hashSize1, hashSize2, shift ) \ +ImHistTable * /* Returns histogram table */\ +IM_VFBHEADER(funcName) \ +{ \ + int i, j, k; /* Counters */\ + int h, w; /* Height and width of image */\ + ImVfbPtr pPixel; /* Pixel pointer */\ + imHistBucketHeader **hash;/* Hash table */\ + imHistBucketHeader *pHash;/* Hash table pointer */\ + int nEntry; /* Number of unique pixels */\ + ImHistTable *histTable;/* Histogram table */\ + int hashSize; /* Hash table size */\ + int index; /* Hash table index */\ + int rgb[3]; /* RGB triplet */\ + float hsi[3]; /* HSI equivalent */\ + \ + /* Allocate the hash table as a table of collision lists. */\ + hashSize = (hashSize1) * (hashSize2); \ + if ( (hash = (imHistBucketHeader **)malloc( \ + sizeof( imHistBucketHeader * ) * hashSize )) == NULL ) \ + { \ + ImErrNo = IMEMALLOC; \ + return ( NULL ); \ + } \ + memset( (void *)hash, 0x00, sizeof( imHistBucketHeader * ) * hashSize );\ + \ + /* Hash the color index into an occurrence table. */\ + pPixel = ImVfbQFirst( vfb ); \ + h = ImVfbQHeight( vfb ); \ + w = ImVfbQWidth( vfb ); \ + imVfbHistNColors = 0; \ + for ( i = 0; i < h; i++ ) \ + { \ + for ( k = 0; k < w; k++ ) \ + { \ + rgb[0] = ImVfbQRed( vfb, pPixel ); \ + rgb[1] = ImVfbQGreen( vfb, pPixel ); \ + rgb[2] = ImVfbQBlue( vfb, pPixel ); \ + ImRgbToHsi( rgb, hsi ); \ + index = ((int)(hsi[which1] * (factor1)) << (shift)) |\ + query2( vfb, pPixel ); \ + if ( imVfbHistAddValue( hash, index, (int)(hsi[which3] * (factor3)), (int)(hsi[which4] * (factor4)) ) == -1 )\ + { \ + /* ImErrNo already set. */\ + imVfbHistHashFree( hash, hashSize ); \ + return ( NULL ); \ + } \ + ImVfbSInc( vfb, pPixel ); \ + } \ + } \ + \ + /* Allocate the histogram table. */\ + if ( (histTable = imVfbHistTableAlloc( imVfbHistNColors, fields, 3 )) == NULL )\ + { \ + /* ImErrNo already set. */\ + return( NULL ); \ + } \ + \ + /* Set the histogram table. */\ + for ( nEntry = i = 0; i < (hashSize1); i++ ) \ + { \ + for ( k = 0; k < (hashSize2); k++ ) \ + { \ + index = (i << (shift)) | k; \ + pHash = hash[ index ]; \ + if ( pHash == NULL ) \ + continue; \ + for ( j = 0; j < pHash->imhist_nextSlot; j++ ) \ + { \ + histTable->tableField1[nEntry] = (float)i/(factor1); \ + histTable->tableField2[nEntry] = k; \ + histTable->tableField3[nEntry] = (float)pHash->imhist_list[j].imhist_value1/(factor3);\ + histTable->tableField4[nEntry] = (float)pHash->imhist_list[j].imhist_value2/(factor4);\ + histTable->imhist_nOccur[nEntry++] = pHash->imhist_list[j].imhist_nOccur;\ + } \ + /* Toss the collision list. */\ + free( (char *)pHash->imhist_list ); \ + free( (char *)pHash ); \ + } \ + } \ + free( (char *)hash ); \ + return ( histTable ); \ +} + +#define IM_HIST4FIELD3HSI2( funcName, fields, which1, factor1, which2, factor2, which3, factor3, query4, tableField1, tableField2, tableField3, tableField4, hashSize1, hashSize2, shift ) \ +ImHistTable * /* Returns histogram table */\ +IM_VFBHEADER(funcName) \ +{ \ + int i, j, k; /* Counters */\ + int h, w; /* Height and width of image */\ + ImVfbPtr pPixel; /* Pixel pointer */\ + imHistBucketHeader **hash;/* Hash table */\ + imHistBucketHeader *pHash;/* Hash table pointer */\ + int nEntry; /* Number of unique pixels */\ + ImHistTable *histTable;/* Histogram table */\ + int hashSize; /* Hash table size */\ + int index; /* Hash table index */\ + int rgb[3]; /* RGB triplet */\ + float hsi[3]; /* HSI equivalent */\ + \ + /* Allocate the hash table as a table of collision lists. */\ + hashSize = (hashSize1) * (hashSize2); \ + if ( (hash = (imHistBucketHeader **)malloc( \ + sizeof( imHistBucketHeader * ) * hashSize )) == NULL ) \ + { \ + ImErrNo = IMEMALLOC; \ + return ( NULL ); \ + } \ + memset( (void *)hash, 0x00, sizeof( imHistBucketHeader * ) * hashSize );\ + \ + /* Hash the color index into an occurrence table. */\ + pPixel = ImVfbQFirst( vfb ); \ + h = ImVfbQHeight( vfb ); \ + w = ImVfbQWidth( vfb ); \ + imVfbHistNColors = 0; \ + for ( i = 0; i < h; i++ ) \ + { \ + for ( k = 0; k < w; k++ ) \ + { \ + rgb[0] = ImVfbQRed( vfb, pPixel ); \ + rgb[1] = ImVfbQGreen( vfb, pPixel ); \ + rgb[2] = ImVfbQBlue( vfb, pPixel ); \ + ImRgbToHsi( rgb, hsi ); \ + index = ((int)(hsi[which1] * (factor1)) << (shift)) |\ + (int)(hsi[which2] * (factor2)); \ + if ( imVfbHistAddValue( hash, index, (int)(hsi[which3] * (factor3)), query4( vfb, pPixel ) ) == -1 )\ + { \ + /* ImErrNo already set. */\ + imVfbHistHashFree( hash, hashSize ); \ + return ( NULL ); \ + } \ + ImVfbSInc( vfb, pPixel ); \ + } \ + } \ + \ + /* Allocate the histogram table. */\ + if ( (histTable = imVfbHistTableAlloc( imVfbHistNColors, fields, 3 )) == NULL )\ + { \ + /* ImErrNo already set. */\ + return( NULL ); \ + } \ + \ + /* Set the histogram table. */\ + for ( nEntry = i = 0; i < (hashSize1); i++ ) \ + { \ + for ( k = 0; k < (hashSize2); k++ ) \ + { \ + index = (i << (shift)) | k; \ + pHash = hash[ index ]; \ + if ( pHash == NULL ) \ + continue; \ + for ( j = 0; j < pHash->imhist_nextSlot; j++ ) \ + { \ + histTable->tableField1[nEntry] = (float)i/(factor1); \ + histTable->tableField2[nEntry] = (float)k/(factor2); \ + histTable->tableField3[nEntry] = (float)pHash->imhist_list[j].imhist_value1/(factor3);\ + histTable->tableField4[nEntry] = pHash->imhist_list[j].imhist_value2;\ + histTable->imhist_nOccur[nEntry++] = pHash->imhist_list[j].imhist_nOccur;\ + } \ + /* Toss the collision list. */\ + free( (char *)pHash->imhist_list ); \ + free( (char *)pHash ); \ + } \ + } \ + free( (char *)hash ); \ + return ( histTable ); \ +} + + +/* + * All possible legal combinations of 4 fields: + * ( different orderings of the first 2 fields are equivalent ) + * ( different orderings of the last 2 fields are equivalent ) + * + * R G B A + * R G A B (same as R G B A) + * R B G A + * R B A G (same as R B G A) + * R A G B + * R A B G (same as R A G B) + * + * G R B A (same as R G B A) + * G R A B (same as R G B A) + * G B R A + * G B A R (same as G B R A) + * G A R B + * G A B R (same as G A R B) + * + * B R G A (same as R B G A) + * B R A G (same as R B G A) + * B G R A (same as G B R A) + * B G A R (same as G B R A) + * B A R G + * B A G R (same as B A R G) + * + * A R G B (same as R A B G) + * A R B G (same as R A B G) + * A B R G (same as B A R G) + * A B G R (same as B A R G) + * A G R B (same as G A R B) + * A G B R (same as G A R B) + * + * H S I A + * H S A I (same as H S I A) + * H I S A + * H I A S (same as H I S A) + * H A I S + * H A S I (same as H A I S) + * + * S H I A (same as H S I A) + * S H A I (same as H S I A) + * S I H A + * S I A H (same as S I H A) + * S A H I + * S A I H (same as S A H I) + * + * I H S A (same as H I S A) + * I H A S (same as H I S A) + * I S H A (same as S I H A) + * I S A H (same as S I H A) + * I A H S + * I A S H (same as I A H S) + * + * A H S I (same as H A I S) + * A H I S (same as H A I S) + * A I H S (same as I A H S) + * A I S H (same as I A H S) + * A S H I (same as S A H I) + * A S I H (same as S A H I) + */ + +IM_HIST4FIELD( imRGBAHist, IMRED|IMGREEN|IMBLUE|IMALPHA, + ImVfbQRed, ImVfbQGreen, ImVfbQBlue, ImVfbQAlpha, + imhist_red, imhist_green, imhist_blue, imhist_alpha, + 256, 256, 8 ) +IM_HIST4FIELD( imRBGAHist, IMRED|IMBLUE|IMGREEN|IMALPHA, + ImVfbQRed, ImVfbQBlue, ImVfbQGreen, ImVfbQAlpha, + imhist_red, imhist_blue, imhist_green, imhist_alpha, + 256, 256, 8 ) +IM_HIST4FIELD( imRAGBHist, IMRED|IMALPHA|IMGREEN|IMBLUE, + ImVfbQRed, ImVfbQAlpha, ImVfbQGreen, ImVfbQBlue, + imhist_red, imhist_alpha, imhist_green, imhist_blue, + 256, 256, 8 ) + +IM_HIST4FIELD( imGBRAHist, IMGREEN|IMBLUE|IMRED|IMALPHA, + ImVfbQGreen, ImVfbQBlue, ImVfbQRed, ImVfbQAlpha, + imhist_green, imhist_blue, imhist_red, imhist_alpha, + 256, 256, 8 ) +IM_HIST4FIELD( imGARBHist, IMGREEN|IMALPHA|IMRED|IMBLUE, + ImVfbQGreen, ImVfbQAlpha, ImVfbQRed, ImVfbQBlue, + imhist_green, imhist_alpha, imhist_red, imhist_blue, + 256, 256, 8 ) + +IM_HIST4FIELD( imBARGHist, IMBLUE|IMALPHA|IMRED|IMGREEN, + ImVfbQBlue, ImVfbQAlpha, ImVfbQRed, ImVfbQGreen, + imhist_blue, imhist_alpha, imhist_red, imhist_green, + 256, 256, 8 ) + +IM_HIST4FIELD3HSI2( imHSIAHist, IMHUE|IMSATURATION|IMINTENSITY|IMALPHA, + 0, (1023.0/360.0),1, 1023.0, 2, 1023.0, ImVfbQAlpha, + imhist_hue, imhist_saturation,imhist_intensity,imhist_alpha, + 1024, 1024, 10 ) +IM_HIST4FIELD3HSI2( imHISAHist, IMHUE|IMINTENSITY|IMSATURATION|IMALPHA, + 0, (1023.0/360.0),2, 1023.0, 1, 1023.0, ImVfbQAlpha, + imhist_hue, imhist_intensity,imhist_saturation,imhist_alpha, + 1024, 1024, 10 ) +IM_HIST4FIELD3HSI( imHAISHist, IMHUE|IMALPHA|IMINTENSITY|IMSATURATION, + 0, (1023.0/360.0),ImVfbQAlpha, 2, 1023.0, 1, 1023.0, + imhist_hue, imhist_alpha, imhist_intensity,imhist_saturation, + 1024, 256, 8 ) +IM_HIST4FIELD3HSI2( imSIHAHist, IMSATURATION|IMINTENSITY|IMHUE|IMALPHA, + 1, 1023.0, 2, 1023.0, 0, (1023.0/360.0),ImVfbQAlpha, + imhist_saturation,imhist_intensity,imhist_hue, imhist_alpha, + 1024, 1024, 10 ) +IM_HIST4FIELD3HSI( imSAHIHist, IMSATURATION|IMALPHA|IMHUE|IMINTENSITY, + 1, 1023.0, ImVfbQAlpha, 0, (1023.0/360.0),2, 1023.0, + imhist_saturation,imhist_alpha, imhist_hue, imhist_intensity, + 1024, 256, 8 ) +IM_HIST4FIELD3HSI( imIAHSHist, IMINTENSITY|IMALPHA|IMHUE|IMSATURATION, + 2, 1023.0, ImVfbQAlpha, 0, (1023.0/360.0),1, 1023.0, + imhist_intensity,imhist_alpha, imhist_hue, imhist_saturation, + 1024, 256, 8 ) diff --git a/utils/roq2/libim/imvfblight.c b/utils/roq2/libim/imvfblight.c new file mode 100644 index 0000000..57a5b17 --- /dev/null +++ b/utils/roq2/libim/imvfblight.c @@ -0,0 +1,630 @@ +/** + + ** $Header: /roq/libim/imvfblight.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92186-9784 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/imvfblight.c 1 11/02/99 4:38p Zaphod $ " + + + +/** + + ** FILE + + ** imvfblight.c - Change a VFB's colors to be brighter/darker + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** + + ** imvfblight.c contains routines to modify the lightness/darkness of a VFB + + ** + + ** HISTORY + + ** $Log: /roq/libim/imvfblight.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.9 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.8 1995/06/16 08:55:44 bduggan + + ** added a cast + + ** /. + + ** + + ** Revision 1.7 94/10/03 11:29:54 nadeau + + ** Added support for grayscale image lightening. + + ** Fixed code that missed obtaining a pointer to the CLT in some + + ** cases, causing a core dump. + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Updated comments. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.6 92/11/04 12:12:02 groening + + ** changed macro names + + ** + + ** Revision 1.5 92/08/31 17:42:02 vle + + ** Updated copyright notice. + + ** + + ** Revision 1.4 91/10/03 09:21:11 nadeau + + ** Fixed #includes. + + ** + + ** Revision 1.3 91/05/14 07:46:19 mjb + + ** Bug fix + + ** + + ** Revision 1.2 91/03/08 14:37:01 nadeau + + ** Changed IMVFBINDEX32 to IMVFBINDEX16. + + ** + + ** Revision 1.1 91/02/05 13:46:58 nadeau + + ** Initial revision + + ** + + ** + + **/ + + + +/** + + ** CODE CREDITS + + ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1991. + + **/ + + + +#include + +#include "iminternal.h" + + + + + + + +/* range of integer values: */ + + + +#define IMVFBL_MAXR 255 + +#define IMVFBL_MAXG 255 + +#define IMVFBL_MAXB 255 + + + + + +/** + + ** helpful information: + + ** + + ** Y = 0.30*R + 0.59*G + 0.11*B + + ** U = B - Y + + ** V = R - Y + + ** + + **/ + + + + + + + + + +/* macros to define YUV as functions of RGB: */ + + + +#define IM_YFRGB(r,g,b) ( 0.30*(r)/(float)IMVFBL_MAXR + 0.59*(g)/(float)IMVFBL_MAXG + 0.11*(b)/(float)IMVFBL_MAXB ) + +#define IM_UFRGB(r,g,b) ( -0.30*(r)/(float)IMVFBL_MAXR - 0.59*(g)/(float)IMVFBL_MAXG + 0.89*(b)/(float)IMVFBL_MAXB ) + +#define IM_VFRGB(r,g,b) ( 0.70*(r)/(float)IMVFBL_MAXR - 0.59*(g)/(float)IMVFBL_MAXG - 0.11*(b)/(float)IMVFBL_MAXB ) + + + + + +/* macros to define RGB as functions of YUV: */ + + + +#define IM_RFYUV(y,u,v) (int) ( (float)IMVFBL_MAXR * ( (y) + (v) ) ) + +#define IM_GFYUV(y,u,v) (int) ( (float)IMVFBL_MAXG * ( (0.70-0.11)*(y) + (-0.11)*(u) + (0.70-1.00)*(v) ) / 0.59 ) + +#define IM_BFYUV(y,u,v) (int) ( (float)IMVFBL_MAXB * ( (y) + (u) ) ) + + + + + + + + + + + +/* + + * FUNCTION + + * ImVfbLightness - change a vfb's colors to be brighter or darker + + * + + * DESCRIPTION + + * The given scale factor is applied to each pixel in the image by + + * extracting the pixel's color, converting it to YUV space, + + * multiplying the Y value (intensity) by the factor, then converting + + * back to the original color space. + + */ + + + +ImVfb * /* Returns lighter VFB */ + +#ifdef __STDC__ + +ImVfbLightness( ImVfb *srcVfb, double factor, ImVfb *dstVfb ) + +#else + +ImVfbLightness( srcVfb, factor, dstVfb ) + + ImVfb *srcVfb; /* source vfb */ + + double factor; /* scale factor for lightness */ + + ImVfb *dstVfb; /* destination vfb */ + +#endif + +{ + + int r, g, b; /* red, green, blue values */ + + int i; /* counter */ + + int type; /* framebuffer type */ + + ImClt *srcClt; /* source CLT */ + + ImClt *dstClt; /* destination CLT */ + + float y, u, v; /* y, u, v values */ + + ImCltPtr cp; /* internal clt pointer */ + + ImVfbPtr psrc, pdst; /* internal vfb pointers */ + + int ncolors; /* # colors in clt */ + + + + + + /* + + * Get the image type and make sure we can handle it. + + */ + + type = ImVfbQFields( srcVfb ) & IMVFBIMAGEMASK; + + if ( type == 0 || type == IMVFBMONO ) + + { + + /* Can't handle it. */ + + ImErrNo = IMEFIELD; + + return ( IMVFBNULL ); + + } + + + + + + /* + + * If there's no destination VFB, make one. Give it a CLT if + + * required. + + */ + + srcClt = ImVfbQClt( srcVfb ); + + dstClt = IMCLTNULL; + + if ( dstVfb == IMVFBNEW ) + + { + + dstVfb = ImVfbAlloc( ImVfbQWidth( srcVfb ), + + ImVfbQHeight( srcVfb ), type ); + + if( dstVfb == IMVFBNULL ) + + { + + ImErrNo = IMEMALLOC; + + return( IMVFBNULL ); + + } + + + + + + /* Allocate the new clt, if necessary */ + + if ( srcClt != IMCLTNULL ) + + { + + ncolors = ImCltQNColors( srcClt ); + + dstClt = ImCltAlloc( ncolors ); + + if( dstClt == IMCLTNULL ) + + { + + ImVfbFree( dstVfb ); + + ImErrNo = IMEMALLOC; + + return( IMVFBNULL ); + + } + + ImVfbSClt( dstVfb, dstClt ); + + } + + } + + else if ( srcClt != IMCLTNULL ) + + { + + dstClt = ImVfbQClt( dstVfb ); + + if ( dstClt == IMCLTNULL ) + + { + + ImErrNo = IMEFIELD; + + return ( IMVFBNULL ); + + } + + ncolors = ImCltQNColors( srcClt ); + + if ( ncolors > ImCltQNColors( dstClt ) ) + + ncolors = ImCltQNColors( dstClt ); + + } + + + + + + /* + + * Color indexed image. Walk through it's CLT and change each + + * color. + + */ + + if ( srcClt != IMCLTNULL && + + (type == IMVFBINDEX8 || type == IMVFBINDEX16) ) + + { + + for ( i = 0; i < ncolors; i++ ) + + { + + cp = ImCltQPtr( srcClt, i ); + + r = ImCltQRed( cp ); + + g = ImCltQGreen( cp ); + + b = ImCltQBlue( cp ); + + + + y = IM_YFRGB( r, g, b ); + + u = IM_UFRGB( r, g, b ); + + v = IM_VFRGB( r, g, b ); + + + + y *= factor; + + + + r = IM_RFYUV( y, u, v ); + + g = IM_GFYUV( y, u, v ); + + b = IM_BFYUV( y, u, v ); + + + + if ( r < 0 ) r = 0; + + if ( r > IMVFBL_MAXR ) r = IMVFBL_MAXR; + + if ( g < 0 ) g = 0; + + if ( g > IMVFBL_MAXG ) g = IMVFBL_MAXG; + + if ( b < 0 ) b = 0; + + if ( b > IMVFBL_MAXB ) b = IMVFBL_MAXB; + + + + cp = ImCltQPtr( dstClt, i ); + + ImCltSRed( cp, r ); + + ImCltSGreen( cp, g ); + + ImCltSBlue( cp, b ); + + } + + return( dstVfb ); + + } + + + + + + /* + + * RGB image. Walk through it's pixels and change each one. + + */ + + if ( type == IMVFBRGB ) + + { + + for ( psrc=ImVfbQFirst(srcVfb), pdst=ImVfbQFirst(dstVfb); + + psrc <= ImVfbQLast(srcVfb); + + ImVfbSInc(srcVfb,psrc), ImVfbSInc(dstVfb,pdst) ) + + { + + r = ImVfbQRed( srcVfb, psrc ); + + g = ImVfbQGreen( srcVfb, psrc ); + + b = ImVfbQBlue( srcVfb, psrc ); + + + + y = IM_YFRGB( r, g, b ); + + u = IM_UFRGB( r, g, b ); + + v = IM_VFRGB( r, g, b ); + + + + y *= factor; + + + + r = IM_RFYUV( y, u, v ); + + g = IM_GFYUV( y, u, v ); + + b = IM_BFYUV( y, u, v ); + + + + if ( r < 0 ) r = 0; + + if ( r > IMVFBL_MAXR ) r = IMVFBL_MAXR; + + if ( g < 0 ) g = 0; + + if ( g > IMVFBL_MAXG ) g = IMVFBL_MAXG; + + if ( b < 0 ) b = 0; + + if ( b > IMVFBL_MAXB ) b = IMVFBL_MAXB; + + + + ImVfbSRed( dstVfb, pdst, r ); + + ImVfbSGreen( dstVfb, pdst, g ); + + ImVfbSBlue( dstVfb, pdst, b ); + + } + + return ( dstVfb ); + + } + + + + + + /* + + * Grayscale image. Pixels are treated as single integer gray + + * values rather than indexes into a color lookup table. To + + * lighten them, apply the lighten factor to each index. + + */ + + for ( psrc=ImVfbQFirst(srcVfb), pdst=ImVfbQFirst(dstVfb); + + psrc <= ImVfbQLast(srcVfb); + + ImVfbSInc(srcVfb,psrc), ImVfbSInc(dstVfb,pdst) ) + + { + + g = ImVfbQGray( srcVfb, psrc ); + + + + g = (int) (g * factor); + + + + if ( g < 0 ) g = 0; + + if ( g > IMVFBL_MAXG ) g = IMVFBL_MAXG; + + + + ImVfbSGray( dstVfb, pdst, g ); + + } + + return ( dstVfb ); + +} + diff --git a/utils/roq2/libim/imvfbresize.c b/utils/roq2/libim/imvfbresize.c new file mode 100644 index 0000000..2c3fcf3 --- /dev/null +++ b/utils/roq2/libim/imvfbresize.c @@ -0,0 +1,505 @@ +/** + ** $Header: /roq/libim/imvfbresize.c 1 11/02/99 4:38p Zaphod $ + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + ** a division of General Atomics, San Diego, California, USA + ** + ** Users and possessors of this source code are hereby granted a + ** nonexclusive, royalty-free copyright and design patent license to + ** use this code in individual software. License is not granted for + ** commercial resale, in whole or in part, without prior written + ** permission from SDSC. This source is provided "AS IS" without express + ** or implied warranty of any kind. + ** + ** For further information contact: + ** E-Mail: info@sds.sdsc.edu + ** + ** Surface Mail: Information Center + ** San Diego Supercomputer Center + ** P.O. Box 85608 + ** San Diego, CA 92186-9784 + ** (619) 534-5000 + **/ + +#define HEADER " $Header: /roq/libim/imvfbresize.c 1 11/02/99 4:38p Zaphod $ " + +/** + ** FILE + ** imvfbresize.c - Change a VFB to a new size + ** + ** PROJECT + ** libim - SDSC image manipulation library + ** + ** DESCRIPTION + ** imvfbresize.c contains code to change the resolution of an image. + ** + ** PUBLIC CONTENTS + ** d =defined constant + ** f =function + ** m =defined macro + ** t =typedef/struct/union + ** v =variable + ** ? =other + ** + ** ImVfbResize f change the resolution of a vfb + ** + ** PRIVATE CONTENTS + ** none + ** + ** HISTORY + ** $Log: /roq/libim/imvfbresize.c $ + * + * 1 11/02/99 4:38p Zaphod + ** Revision 1.11 1995/06/30 22:11:56 bduggan + ** added some casts + ** + ** Revision 1.10 1995/06/29 00:28:04 bduggan + ** updated copyright year + ** + ** Revision 1.9 1995/06/16 09:00:36 bduggan + ** added some casts + ** + ** Revision 1.8 94/10/03 11:29:55 nadeau + ** Updated to ANSI C and C++ compatibility. + ** Removed all use of register keyword. + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + ** Changed all float arguments to double. + ** Added forward declarations. + ** Added misc. casts to passify SGI and DEC compilers. + ** Changed all macros and defined constants to have names + ** starting with IM. + ** Updated comments. + ** Updated indenting on some code. + ** Updated copyright message. + ** + ** Revision 1.7 92/08/31 17:42:42 vle + ** Updated copyright notice. + ** + ** Revision 1.6 92/08/26 11:19:28 nadeau + ** Added additional error checks. + ** + ** Revision 1.5 92/08/25 16:08:00 groening + ** added pixel replication feature + ** + ** Revision 1.4 92/02/27 16:21:55 nadeau + ** Fixed bug in resizing of FDATA field info. + ** + ** Revision 1.3 91/10/03 09:22:21 nadeau + ** Added handling of a non-new destination VFB. Added + ** handling of IDATA, FDATA, MONO, Z, and WPROT fields. + ** Fixed problem that stepped beyond the right and bottom + ** edges of the VFB. Fixed problem that caused jaggy + ** edges on all images scaled up. + ** + ** Revision 1.2 91/03/08 14:37:31 nadeau + ** Changed name from ImVfbNewRes to ImVfbReSize. Added comments. + ** Deleted comments. Changed IMVFBINDEX32 to IMVFBINDEX16. + ** + ** Revision 1.1 91/02/05 13:46:55 mjb + ** Initial revision + **/ + +/** + ** CODE CREDITS + ** Custom development, Michael Bailey, San Diego Supercomputer Center, 1991. + **/ + +#include +#include "iminternal.h" + + + + + +/* + * FUNCTION + * ImVfbResize - change the resolution of a vfb + * + * DESCRIPTION + * Increase or decrease the size of the image using pixel replication + * or bilinear interpolation. In either case a new + * destination VFB is created (unless one has been passed in) and + * the source image copied into it, changing its size as we go. + */ + +ImVfb * /* Returns resized VFB */ +#ifdef __STDC__ +ImVfbResize( ImVfb *srcVfb, int algorithm, ImVfb *dstVfb, int width, int height ) +#else +ImVfbResize( srcVfb, algorithm, dstVfb, width, height ) + ImVfb *srcVfb; /* VFB to resize */ + int algorithm; /* Algorithm to use */ + ImVfb *dstVfb; /* Result VFB */ + int width, height;/* Desired dimensions */ +#endif +{ + ImVfbPtr pdst; /* Destination VFB pointer */ + ImVfbPtr psrc; /* Destination VFB pointer */ + ImVfbPtr p00, p10, p01, p11; /* corner pointers */ + int i,j,k,l; /* generic integer value */ + int fields; /* vfb field description */ + ImVfb *tmpVfb; /* Temporary VFB */ + float f; /* Generic float value */ + int sw, sh; /* Source size */ + int dw, dh; /* Destination size */ + int intdx, intdy; /* Step through dst vfb */ + int intsx, intsy; /* Integer source locations */ + int intsx2, intsy2;/* Second integer source loc */ + float sclw, sclh; /* mapping scale factors */ + float c00, c10, c01, c11; /* corner coefficients */ + float floatsx, floatsy; /* fp source pos */ + float fracsx, fracsy; /* fractional source pos*/ + float omfracsx, omfracsy; /* 1.0 - fractional values*/ + int scaleFacOne, scaleFacTwo; /* scale factors */ + + + /* + * Check what algorithm the user wants us to use. + */ + switch ( algorithm ) + { + case IMVFBPIXELREP: + /* + * Check that the new size is larger than the source size, + * and that it is an even multiple. + */ + if ( width < ImVfbQWidth( srcVfb ) ) + { + ImErrNo = IMEWIDTH; + return ( IMVFBNULL ); + } + if ( height < ImVfbQHeight( srcVfb ) ) + { + ImErrNo = IMEHEIGHT; + return ( IMVFBNULL ); + } + scaleFacOne = width % ImVfbQWidth(srcVfb); + scaleFacTwo = height % ImVfbQHeight(srcVfb); + if ( (scaleFacOne!=0) || (scaleFacTwo!=0) ) + { + ImErrNo = IMEPIXELREP; + return(IMVFBNULL); + } + break; + + case IMVFBBILINEAR: + break; + + default: + ImErrNo = IMEBADALGORITHM; + return( IMVFBNULL ); + } + + + /* + * If the user hasn't given us a destination VFB, make one. + * If they have given us a destination VFB, make sure it is usable. + */ + fields = ImVfbQFields( srcVfb ); + sw = ImVfbQWidth( srcVfb ); + sh = ImVfbQHeight( srcVfb ); + tmpVfb = IMVFBNULL; + if ( dstVfb == IMVFBNEW ) + { + dstVfb = ImVfbAlloc( width, height, fields ); + if( dstVfb == IMVFBNULL ) + { + ImErrNo = IMEMALLOC; + return( IMVFBNULL ); + } + } + else + { + if ( ImVfbQWidth( dstVfb ) != width ) + { + ImErrNo = IMEWIDTH; + return ( IMVFBNULL ); + } + if ( ImVfbQHeight( dstVfb ) != height ) + { + ImErrNo = IMEHEIGHT; + return ( IMVFBNULL ); + } + if ( ImVfbQFields( dstVfb ) != fields ) + { + ImErrNo = IMECONFLICT; + return ( IMVFBNULL ); + } + } + + + + if (algorithm == IMVFBPIXELREP) + { + /* + * Perform pixel replication to increase the image's size. + */ + scaleFacOne = width / ImVfbQWidth(srcVfb); + scaleFacTwo = height / ImVfbQHeight(srcVfb); + + psrc = ImVfbQFirst( srcVfb); + pdst = ImVfbQFirst( dstVfb); + + for (i=0; i sh-1 ) + intsy2 = sh-1; + fracsy = floatsy - (float) intsy; + omfracsy = 1.0 - fracsy; + + for ( floatsx = 0.0, intdx = 0; intdx < dw; + floatsx += sclw, intdx++, ImVfbSInc( dstVfb, pdst ) ) + { + /* + * Compute our integer and fractional X positions + * in the source image. + */ + intsx = (int)floatsx; + intsx2 = intsx + 1; + if ( intsx2 > sw-1 ) + intsx2 = sw-1; + fracsx = floatsx - (float) intsx; + omfracsx = 1.0 - fracsx; + + + /* + * Advance our position in the source image. + */ + p00 = ImVfbQPtr( srcVfb, intsx , intsy ); + p10 = ImVfbQPtr( srcVfb, intsx2, intsy ); + p01 = ImVfbQPtr( srcVfb, intsx , intsy2 ); + p11 = ImVfbQPtr( srcVfb, intsx2, intsy2 ); + + + /* + * Compute corner coefficients. + */ + c00 = omfracsx * omfracsy; + c10 = fracsx * omfracsy; + c01 = omfracsx * fracsy; + c11 = fracsx * fracsy; + + + /* + * Interpolate each of the VFB fields. + */ + if ( fields & IMVFBRGB ) + { + i = (int) + (c00 * ImVfbQRed( srcVfb, p00 ) + + c10 * ImVfbQRed( srcVfb, p10 ) + + c01 * ImVfbQRed( srcVfb, p01 ) + + c11 * ImVfbQRed( srcVfb, p11 )); + ImVfbSRed( dstVfb, pdst, i & 0xFF ); + + i = (int) + (c00 * ImVfbQGreen( srcVfb, p00 ) + + c10 * ImVfbQGreen( srcVfb, p10 ) + + c01 * ImVfbQGreen( srcVfb, p01 ) + + c11 * ImVfbQGreen( srcVfb, p11 )); + ImVfbSGreen( dstVfb, pdst, i & 0xFF ); + + i = (int) + (c00 * ImVfbQBlue( srcVfb, p00 ) + + c10 * ImVfbQBlue( srcVfb, p10 ) + + c01 * ImVfbQBlue( srcVfb, p01 ) + + c11 * ImVfbQBlue( srcVfb, p11 )); + ImVfbSBlue( dstVfb, pdst, i & 0xFF ); + } + + if ( fields & IMVFBINDEX8 ) + { + i = (int) + (c00 * ImVfbQIndex8( srcVfb, p00 ) + + c10 * ImVfbQIndex8( srcVfb, p10 ) + + c01 * ImVfbQIndex8( srcVfb, p01 ) + + c11 * ImVfbQIndex8( srcVfb, p11 )); + ImVfbSIndex8( dstVfb, pdst, i & 0xFF ); + } + + if ( fields & IMVFBINDEX16 ) + { + i = (int) + (c00 * ImVfbQIndex16( srcVfb, p00 ) + + c10 * ImVfbQIndex16( srcVfb, p10 ) + + c01 * ImVfbQIndex16( srcVfb, p01 ) + + c11 * ImVfbQIndex16( srcVfb, p11 )); + ImVfbSIndex16( dstVfb, pdst, i & 0xFFFF ); + } + + if ( fields & IMVFBMONO ) + { + i = (int) + (c00 * ImVfbQMono( srcVfb, p00 ) + + c10 * ImVfbQMono( srcVfb, p10 ) + + c01 * ImVfbQMono( srcVfb, p01 ) + + c11 * ImVfbQMono( srcVfb, p11 )); + ImVfbSMono( dstVfb, pdst, i & 0x1 ); + } + + if ( fields & IMVFBALPHA ) + { + i = (int) + (c00 * ImVfbQAlpha( srcVfb, p00 ) + + c10 * ImVfbQAlpha( srcVfb, p10 ) + + c01 * ImVfbQAlpha( srcVfb, p01 ) + + c11 * ImVfbQAlpha( srcVfb, p11 )); + ImVfbSAlpha( dstVfb, pdst, i & 0xFF ); + } + + if ( fields & IMVFBWPROT ) + { + i = (int) + (c00 * ImVfbQWProt( srcVfb, p00 ) + + c10 * ImVfbQWProt( srcVfb, p10 ) + + c01 * ImVfbQWProt( srcVfb, p01 ) + + c11 * ImVfbQWProt( srcVfb, p11 )); + ImVfbSWProt( dstVfb, pdst, i & 0xFF ); + } + + if ( fields & IMVFBZ ) + { + i = (int) + (c00 * ImVfbQZ( srcVfb, p00 ) + + c10 * ImVfbQZ( srcVfb, p10 ) + + c01 * ImVfbQZ( srcVfb, p01 ) + + c11 * ImVfbQZ( srcVfb, p11 )); + ImVfbSZ( dstVfb, pdst, i ); + } + + if ( fields & IMVFBIDATA ) + { + i = (int) + (c00 * ImVfbQIData( srcVfb, p00 ) + + c10 * ImVfbQIData( srcVfb, p10 ) + + c01 * ImVfbQIData( srcVfb, p01 ) + + c11 * ImVfbQIData( srcVfb, p11 )); + ImVfbSIData( dstVfb, pdst, i ); + } + + if ( fields & IMVFBFDATA ) + { + f = (int) + (c00 * ImVfbQFData( srcVfb, p00 ) + + c10 * ImVfbQFData( srcVfb, p10 ) + + c01 * ImVfbQFData( srcVfb, p01 ) + + c11 * ImVfbQFData( srcVfb, p11 )); + ImVfbSFData( dstVfb, pdst, f ); + } + } + } + if ( srcVfb == tmpVfb ) + ImVfbFree( tmpVfb ); + + return( dstVfb ); +} diff --git a/utils/roq2/libim/imvfbrotate.c b/utils/roq2/libim/imvfbrotate.c new file mode 100644 index 0000000..5c50fe2 --- /dev/null +++ b/utils/roq2/libim/imvfbrotate.c @@ -0,0 +1,2070 @@ +/** + + ** $Header: /roq/libim/imvfbrotate.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92186-9784 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/imvfbrotate.c 1 11/02/99 4:38p Zaphod $ " + + + +/** + + ** FILE + + ** imvfbrotate.c - Rotate a VFB by a certain angle + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** imvfbrotate.c contains code to rotate an image. + + ** It also has the code to shear an image in the x or y direction + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** + + ** ImVfbRotate f rotate the vfb + + ** ImVfbXShear f shear the vfb in the x direction + + ** ImVfbYShear f shear the vfb in the y direction + + ** + + ** PRIVATE CONTENTS + + ** none + + ** + + ** HISTORY + + ** $Log: /roq/libim/imvfbrotate.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.10 1995/06/30 22:12:11 bduggan + + ** added some casts + + ** + + ** Revision 1.9 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.8 1995/06/16 09:01:05 bduggan + + ** added some casts + + ** + + ** Revision 1.7 94/10/03 11:29:57 nadeau + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Updated comments. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.6 92/12/03 01:56:03 nadeau + + ** Total rewrite. + + ** + + ** Revision 1.5 92/10/19 14:07:23 groening + + ** *** empty log message *** + + ** + + ** Revision 1.4 92/09/17 14:50:14 vle + + ** Added optional include for M_PI declaration to make some + + ** compilers happy. + + ** + + ** Revision 1.3 92/09/03 16:41:14 groening + + ** Added more error checks. + + ** + + ** Revision 1.2 92/09/02 11:17:02 vle + + ** Updated copyright notice. + + ** + + ** Revision 1.1 92/09/02 11:13:57 groening + + ** Initial revision + + ** + + **/ + + + +/** + + ** CODE CREDITS + + ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1992. + + **/ + + + +#include + + + +#ifndef M_PI + +//#include + +#define M_PI 3.1415926 + +#endif + + + +#include "iminternal.h" + + + + + +/* + + * FUNCTION + + * ImVfbXShear - shear a vfb in the X-direction + + * + + * DESCRIPTION + + * implements this formula x1 = x - tan(degree/2)*y + + * + + */ + + + +ImVfb * /* Returns resized VFB */ + +#ifdef __STDC__ + +ImVfbXShear( ImVfb *srcVfb, double degree, ImVfb *dstVfb) + +#else + +ImVfbXShear( srcVfb, degree, dstVfb) + + ImVfb *srcVfb; /* VFB to resize */ + + double degree; /* Amount to rotate */ + + ImVfb *dstVfb; /* Result VFB */ + +#endif + +{ + + ImVfbPtr pdst; /* Destination VFB pointer */ + + ImVfbPtr psrc; /* Destination VFB pointer */ + + int i,j; /* generic integer value */ + + int fields; /* vfb field description */ + + int dw, dh; /* width an height */ + + int newX; /* new x position */ + + int xDif; /* how much wider new vfb is */ + + double tandegree; /* tangent of degree/2 */ + + + + + + /* + + * make sure there is a source vfb then make sure dst vfb + + * has the same fields as the source. + + */ + + if ( srcVfb == IMVFBNULL ) + + { + + ImErrNo = IMENOVFB ; + + return (IMVFBNULL); + + } + + + + + + /* + + * Floating point modulo 'degree' by 360.0. + + */ + + degree *= 2.0; + + degree -= ((int)(degree / 360.0)) * 360.0; + + + + + + /* + + * Make sure that a valid shear degree was requested. + + */ + + if ( (degree <= -180.0) || (degree >= 180.0) ) + + { + + ImErrNo = IMEIMPSHEAR; + + return (IMVFBNULL); + + } + + + + if ( degree < 0.0 ) + + degree += 360.0; + + degree *= -(M_PI/180); + + + + + + /* + + * figure out the necessary size for the resulting vfb + + */ + + if ( degree > -M_PI ) + + { + + dw = (int)( 0.5 + ImVfbQWidth(srcVfb) - + + tan(degree/2)*ImVfbQHeight( srcVfb )); + + xDif = 0; + + dh = ImVfbQHeight( srcVfb ); + + } + + else + + { + + dw = (int)( 0.5 + ImVfbQWidth(srcVfb) + + + tan(degree/2)*ImVfbQHeight( srcVfb )); + + xDif = dw-ImVfbQWidth(srcVfb); + + dh = ImVfbQHeight( srcVfb ); + + } + + + + /* + + * If the user hasn't given us a destination VFB, make one. + + * If they have given us a destination VFB, make sure it is usable. + + */ + + fields = ImVfbQFields( srcVfb ); + + + + if ( dstVfb == IMVFBNEW ) + + { + + dstVfb = ImVfbAlloc( dw, dh, fields ); + + if( dstVfb == IMVFBNULL ) + + { + + ImErrNo = IMEMALLOC; + + return( IMVFBNULL ); + + } + + ImVfbClear( fields, 0, dstVfb ); + + } + + else + + { + + /* make sure that the passed vfb is the correct size */ + + if ( ImVfbQWidth( dstVfb ) < dw ) + + { + + ImErrNo = IMEWIDTH; + + return ( IMVFBNULL ); + + } + + if ( ImVfbQHeight( dstVfb ) < dh ) + + { + + ImErrNo = IMEHEIGHT; + + return ( IMVFBNULL ); + + } + + if (( ImVfbQFields(srcVfb) & ImVfbQFields(dstVfb)) != + + ImVfbQFields(srcVfb)) + + { + + ImErrNo = IMEFIELD; + + return (IMVFBNULL); + + } + + } + + + + /* + + * Now shear the vfb + + */ + + tandegree = tan( degree / 2 ); + + psrc = ImVfbQFirst(srcVfb); + + + + for (i=0; i= 180.0) ) + + { + + ImErrNo = IMEIMPSHEAR; + + return (IMVFBNULL); + + } + + + + if ( degree < 0.0 ) + + degree += 360.0; + + degree *= -(M_PI/180); + + + + + + /* + + * figure out the necessary size for the resulting vfb + + */ + + if ( degree>-M_PI ) + + { + + dh = (int)(( 0.5 + ImVfbQHeight(srcVfb) - + + sin(degree)*ImVfbQWidth( srcVfb ))); + + dw = ImVfbQWidth( srcVfb ); + + yDif = dh-ImVfbQHeight(srcVfb); + + } + + + + else + + { + + dh = (int)(( 0.5 + ImVfbQHeight(srcVfb) + + + sin(degree)*ImVfbQWidth( srcVfb ))); + + dw = ImVfbQWidth( srcVfb ); + + yDif = 0; + + } + + + + /* + + * If the user hasn't given us a destination VFB, make one. + + * If they have given us a destination VFB, make sure it is usable. + + */ + + fields = ImVfbQFields( srcVfb ); + + + + if ( dstVfb == IMVFBNEW ) + + { + + dstVfb = ImVfbAlloc( dw, dh, fields ); + + if( dstVfb == IMVFBNULL ) + + { + + ImErrNo = IMEMALLOC; + + return( IMVFBNULL ); + + } + + ImVfbClear( fields, 0, dstVfb ); + + } + + else + + { + + /* make sure that passed vfb is the correct size */ + + if ( ImVfbQWidth( dstVfb ) < dw ) + + { + + ImErrNo = IMEWIDTH; + + return ( IMVFBNULL ); + + } + + if ( ImVfbQHeight( dstVfb ) < dh ) + + { + + ImErrNo = IMEHEIGHT; + + return ( IMVFBNULL ); + + } + + if (( ImVfbQFields(srcVfb) & ImVfbQFields(dstVfb)) != + + ImVfbQFields(srcVfb)) + + { + + ImErrNo = IMEFIELD; + + return (IMVFBNULL); + + } + + } + + + + + + /* + + * Now shear the vfb + + */ + + psrc = ImVfbQFirst(srcVfb); + + sindegree = sin( degree ); + + for (i=0; i= 90.0 ) + + { + + if ( (tmpVfb = ImVfb90Rotate( srcVfb, IMVFBNEW )) == IMVFBNULL ) + + { + + ImErrNo = IMEMALLOC; + + return (IMVFBNULL); + + } + + rad -= (M_PI/2.0); + + rotation -= 90.0; + + if ( srcVfb != sorceVfb ) + + ImVfbFree( srcVfb ); + + srcVfb = tmpVfb; + + } + + W = ImVfbQWidth( srcVfb ); + + H = ImVfbQHeight( srcVfb ); + + + + if ( rotation == 0.0 ) + + { + + /* + + * Nothing more to do. Easy! Copy the rotated image into + + * the destination VFB and return. + + */ + + if ( ImVfbCopy( srcVfb , 0, 0, W, H, fieldMask, + + dstVfb, 0, 0 ) == IMVFBNULL) + + { + + /* ImErrNo already set. */ + + if ( srcVfb != sorceVfb ) + + ImVfbFree( srcVfb ); + + return ( IMVFBNULL ); + + } + + if ( srcVfb != sorceVfb ) + + ImVfbFree( srcVfb ); + + return ( dstVfb ); + + } + + + + + + /* + + * Compute ultimate image size. + + */ + + Wone = (int) (W + 0.5 + H * fabs(tan(rad/2))); + + Hone = (int) (H + 0.5 + fabs(sin(rad)) * Wone); + + Wtwo = (int) (0.5 + Wone + Hone*fabs(tan(rad/2))); + + + + srcYTop = (int)(0.5 + fabs(tan(rad/2))*(fabs(sin(rad))*H)); + + srcXLeft = (int) (0.5 + fabs(sin(rad)) * ( (W)*fabs(tan(rad/2)))); + + + + srcDX = (int) (0.5 + fabs(sin(rad))*H + fabs (cos(rad)*W)); + + srcDY = (int) (0.5 + fabs(sin(rad))*W + fabs (cos(rad)*H)); + + srcDY +=2; + + if (srcDY>Hone) + + srcDY=Hone; + + if (srcYTop>1.0) + + srcYTop--; + + rotation /= 2.0; + + + + + + /* + + * Create a dummy monochrome VFB filled with 1's. + + */ + + if ( (dummyVfb = ImVfbAlloc( W, H, IMVFBMONO )) == IMVFBNULL) + + { + + /* ImErrNo already set. */ + + if ( srcVfb != sorceVfb ) + + ImVfbFree( srcVfb ); + + return (IMVFBNULL); + + } + + if ( ImVfbFill( dummyVfb, 0, 0, W, H, IMMONO, 1.0, 1.0, + + IMVFBINSIDE, IMGRADNONE, dummyVfb ) == IMVFBNULL ) + + { + + /* ImErrNo already set. */ + + if ( srcVfb != sorceVfb ) + + ImVfbFree( srcVfb ); + + ImVfbFree( dummyVfb ); + + return (IMVFBNULL); + + } + + + + + + /* + + * Shear in X. + + */ + + if ( (tmpVfb = ImVfbXShear( srcVfb, rotation, IMVFBNEW ) ) == IMVFBNULL) + + { + + /* ImErrNo already set. */ + + if ( srcVfb != sorceVfb ) + + ImVfbFree( srcVfb ); + + ImVfbFree( dummyVfb ); + + return (IMVFBNULL); + + } + + if ( srcVfb != sorceVfb ) + + ImVfbFree( srcVfb ); + + srcVfb = tmpVfb; + + if ( (tmpVfb = ImVfbXShear( dummyVfb, rotation, IMVFBNEW)) == IMVFBNULL) + + { + + /* ImErrNo already set. */ + + ImVfbFree( srcVfb ); + + ImVfbFree( dummyVfb ); + + return (IMVFBNULL); + + } + + ImVfbFree( dummyVfb ); + + dummyVfb = tmpVfb; + + + + + + /* + + * Shear in Y. + + */ + + if ( (tmpVfb = ImVfbYShear( srcVfb, rotation, IMVFBNEW ) ) == IMVFBNULL) + + { + + /* ImErrNo already set. */ + + ImVfbFree( srcVfb ); + + ImVfbFree( dummyVfb ); + + return (IMVFBNULL); + + } + + ImVfbFree( srcVfb ); + + srcVfb = tmpVfb; + + if ( (tmpVfb = ImVfbYShear( dummyVfb, rotation, IMVFBNEW)) == IMVFBNULL) + + { + + /* ImErrNo already set. */ + + ImVfbFree( srcVfb ); + + ImVfbFree( dummyVfb ); + + return (IMVFBNULL); + + } + + ImVfbFree( dummyVfb ); + + dummyVfb = tmpVfb; + + + + + + /* + + * Trim off the excess that comes about by increasing the size to + + * shear in X, then increasing the size to shear in Y. We end up + + * with a lot of extra "empty" space around the image. By trimming + + * it off here, we save time later by having fewer pixels to move + + * about. + + */ + + if ( (tmpVfb = ImVfbCopy( srcVfb, 0, srcYTop, Wone, srcDY, + + fieldMask, IMVFBNEW, 0, 0)) == IMVFBNULL) + + { + + /* ImErrNo already set. */ + + ImVfbFree( srcVfb ); + + ImVfbFree( dummyVfb ); + + return (IMVFBNULL); + + } + + ImVfbFree( srcVfb ); + + srcVfb = tmpVfb; + + if ( (tmpVfb = ImVfbCopy( dummyVfb, 0, srcYTop, Wone, srcDY, + + IMVFBMONO, IMVFBNEW, 0, 0)) == IMVFBNULL) + + { + + /* ImErrNo already set. */ + + ImVfbFree( srcVfb ); + + ImVfbFree( dummyVfb ); + + return (IMVFBNULL); + + } + + ImVfbFree( dummyVfb ); + + dummyVfb = tmpVfb; + + + + + + /* + + * Shear in X again. + + */ + + if ( (tmpVfb = ImVfbXShear( srcVfb, rotation, IMVFBNEW ) ) == IMVFBNULL) + + { + + /* ImErrNo already set. */ + + ImVfbFree( srcVfb ); + + ImVfbFree( dummyVfb ); + + return (IMVFBNULL); + + } + + ImVfbFree( srcVfb ); + + srcVfb = tmpVfb; + + if ( (tmpVfb = ImVfbXShear( dummyVfb, rotation, IMVFBNEW)) == IMVFBNULL) + + { + + /* ImErrNo already set. */ + + ImVfbFree( srcVfb ); + + ImVfbFree( dummyVfb ); + + return (IMVFBNULL); + + } + + ImVfbFree( dummyVfb ); + + dummyVfb = tmpVfb; + + + + + + /* + + * Trim off the excess again. + + */ + + if ( (tmpVfb = ImVfbCopy( srcVfb, srcXLeft, 0, srcDX, + + srcDY, fieldMask, IMVFBNEW, 0, 0 )) == IMVFBNULL ) + + { + + /* ImErrNo already set. */ + + ImVfbFree( srcVfb ); + + ImVfbFree( dummyVfb ); + + return (IMVFBNULL); + + } + + ImVfbFree( srcVfb ); + + srcVfb = tmpVfb; + + if ( (tmpVfb = ImVfbCopy( dummyVfb, srcXLeft, 0, srcDX, + + srcDY, IMVFBMONO, IMVFBNEW, 0, 0 )) == IMVFBNULL ) + + { + + /* ImErrNo already set. */ + + ImVfbFree( srcVfb ); + + ImVfbFree( dummyVfb ); + + return (IMVFBNULL); + + } + + ImVfbFree( dummyVfb ); + + dummyVfb = tmpVfb; + + + + + + /* + + * Use the dummyVfb has a mask for the rotated srcVfb to determine + + * which pixels of the srcVfb to copy to the dstVfb. 1's in the + + * dummyVfb mean copy. 0's mean don't. + + */ + + pSrc = ImVfbQFirst( srcVfb ); + + pDummy = ImVfbQFirst( dummyVfb ); + + pDst = ImVfbQFirst( dstVfb ); + + + + for ( i = 0; i < ImVfbQHeight( dummyVfb ); i++ ) + + { + + for ( j = 0; j < ImVfbQWidth( dummyVfb) ; j++ ) + + { + + if ( !ImVfbQMono( dummyVfb, pDummy ) ) + + { + + /* Don't copy. */ + + ImVfbSInc( dummyVfb, pDummy ); + + ImVfbSInc( srcVfb, pSrc ); + + ImVfbSInc( dstVfb, pDst ); + + continue; + + } + + + + /* Do copy. */ + + if (fieldMask&IMVFBRGB) + + { + + ImVfbSRed( dstVfb, pDst, + + ImVfbQRed( srcVfb, pSrc)); + + ImVfbSGreen( dstVfb, pDst, + + ImVfbQGreen( srcVfb, pSrc)); + + ImVfbSBlue( dstVfb, pDst, + + ImVfbQBlue( srcVfb, pSrc)); + + } + + + + if (fieldMask&IMVFBZ) + + ImVfbSZ( dstVfb, pDst, + + ImVfbQZ( srcVfb, pSrc ) ); + + if (fieldMask&IMVFBWPROT) + + ImVfbSWProt( dstVfb, pDst, + + ImVfbQWProt( srcVfb, pSrc ) ); + + if (fieldMask&IMVFBIDATA) + + ImVfbSIData( dstVfb, pDst, + + ImVfbQIData( srcVfb, pSrc ) ); + + if (fieldMask&IMVFBFDATA) + + ImVfbSFData( dstVfb, pDst, + + ImVfbQFData( srcVfb, pSrc ) ); + + if (fieldMask&IMVFBMONO) + + ImVfbSMono( dstVfb, pDst, + + ImVfbQMono( srcVfb, pSrc ) ); + + if (fieldMask&IMVFBALPHA) + + ImVfbSAlpha( dstVfb, pDst, + + ImVfbQAlpha( srcVfb, pSrc ) ); + + if (fieldMask&IMVFBINDEX8) + + ImVfbSIndex8( dstVfb, pDst, + + ImVfbQIndex8( srcVfb, pSrc ) ); + + if (fieldMask&IMVFBINDEX16) + + ImVfbSIndex16( dstVfb, pDst, + + ImVfbQIndex16( srcVfb, pSrc ) ); + + ImVfbSInc( dummyVfb, pDummy ); + + ImVfbSInc( srcVfb, pSrc ); + + ImVfbSInc( dstVfb, pDst ); + + } + + } + + + + ImVfbFree( srcVfb ); + + ImVfbFree( dummyVfb ); + + + + return( dstVfb ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * ImVfb90Rotate - rotate by 90 degrees + + * + + * DESCRIPTION + + * When rotating by exactly 90 degrees counter-clockwise (right-hand + + * rule), we can do the rotation quicker than by using 3 shears. + + */ + + + +ImVfb * /* Returns rotated VFB */ + +#ifdef __STDC__ + +ImVfb90Rotate( ImVfb* sourceVfb, ImVfb* dstVfb ) + +#else + +ImVfb90Rotate( sourceVfb, dstVfb ) + + ImVfb *sourceVfb; /* VFB to rotated */ + + ImVfb *dstVfb; /* VFB to return rotated */ + +#endif + +{ + + ImVfbPtr psrc; /* pointer into source vfb */ + + ImVfbPtr pdst; /* pointer into destination vfb */ + + int i,j; /* Counters */ + + int wDst, hDst; /* Destination width and height */ + + int fieldMask; /* Fields to deal with */ + + + + + + /* + + * Get destination width and height... reversed from source's + + * `cause we're rotating by 90.0. + + */ + + hDst = ImVfbQWidth( sourceVfb ); + + wDst = ImVfbQHeight( sourceVfb ); + + fieldMask = ImVfbQFields( sourceVfb ); + + + + + + /* + + * If a destination VFB wasn't given, allocate one at the same + + * size and depth as the source. Otherwise make sure the desination + + * is the same size as the source and has at least the same fields + + * as the source. + + */ + + if (dstVfb == IMVFBNEW) + + { + + if ( (dstVfb = ImVfbAlloc( wDst, hDst, fieldMask )) ==IMVFBNULL) + + { + + ImErrNo = IMEMALLOC; + + return (IMVFBNULL); + + } + + } + + else + + { + + if ( ImVfbQWidth( dstVfb ) != wDst ) + + { + + ImErrNo = IMEWIDTH; + + return (IMVFBNULL); + + } + + if ( ImVfbQHeight( dstVfb ) != hDst ) + + { + + ImErrNo = IMEHEIGHT; + + return (IMVFBNULL); + + } + + if ( (fieldMask & ImVfbQFields(dstVfb)) != fieldMask ) + + { + + ImErrNo = IMEFIELD; + + return (IMVFBNULL); + + } + + } + + + + + + /* + + * Walk the image and rotate it by 90 into the destination VFB. + + */ + + psrc = ImVfbQFirst( sourceVfb ); + + for ( i = 0; i < wDst; i++ ) + + { + + for ( j = hDst-1; j >= 0; j-- ) + + { + + pdst = ImVfbQPtr( dstVfb, i, j ); + + + + if (fieldMask&IMVFBRGB) + + { + + ImVfbSRed (dstVfb, pdst, ImVfbQRed(sourceVfb, psrc)); + + ImVfbSGreen (dstVfb, pdst, ImVfbQGreen(sourceVfb, psrc)); + + ImVfbSBlue (dstVfb, pdst, ImVfbQBlue(sourceVfb, psrc)); + + } + + + + if (fieldMask&IMVFBZ) + + { + + ImVfbSZ (dstVfb, pdst, ImVfbQZ(sourceVfb, psrc)); + + } + + + + if (fieldMask&IMVFBWPROT) + + { + + ImVfbSWProt (dstVfb, pdst, ImVfbQWProt(sourceVfb, psrc)); + + } + + + + if (fieldMask&IMVFBIDATA) + + { + + ImVfbSIData (dstVfb, pdst, ImVfbQIData(sourceVfb, psrc)); + + } + + + + if (fieldMask&IMVFBFDATA) + + { + + ImVfbSFData (dstVfb, pdst, ImVfbQFData(sourceVfb, psrc)); + + } + + + + if (fieldMask&IMVFBMONO) + + { + + ImVfbSMono (dstVfb, pdst, ImVfbQMono(sourceVfb, psrc)); + + } + + + + if (fieldMask&IMVFBALPHA) + + { + + ImVfbSAlpha (dstVfb, pdst, ImVfbQAlpha(sourceVfb, psrc)); + + } + + + + if (fieldMask&IMVFBINDEX8) + + { + + ImVfbSIndex8 (dstVfb, pdst, ImVfbQIndex8(sourceVfb, psrc)); + + } + + + + if (fieldMask&IMVFBINDEX16) + + { + + ImVfbSIndex16 (dstVfb, pdst, ImVfbQIndex16(sourceVfb, psrc)); + + } + + + + ImVfbSInc( sourceVfb, psrc ); + + } + + } + + + + return (dstVfb); + +} + diff --git a/utils/roq2/libim/imvfbto.c b/utils/roq2/libim/imvfbto.c new file mode 100644 index 0000000..2e4d058 --- /dev/null +++ b/utils/roq2/libim/imvfbto.c @@ -0,0 +1,2946 @@ +/** + ** $Header: /roq/libim/imvfbto.c 1 11/02/99 4:38p Zaphod $ + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + ** a division of General Atomics, San Diego, California, USA + ** + ** Users and possessors of this source code are hereby granted a + ** nonexclusive, royalty-free copyright and design patent license to + ** use this code in individual software. License is not granted for + ** commercial resale, in whole or in part, without prior written + ** permission from SDSC. This source is provided "AS IS" without express + ** or implied warranty of any kind. + ** + ** For further information contact: + ** E-Mail: info@sds.sdsc.edu + ** + ** Surface Mail: Information Center + ** San Diego Supercomputer Center + ** P.O. Box 85608 + ** San Diego, CA 92138-5608 + ** (619) 534-5000 + **/ + +#define HEADER " $Header: /roq/libim/imvfbto.c 1 11/02/99 4:38p Zaphod $" + +/** + ** FILE + ** imvfbto.c - Image Library VFB depth conversions + ** + ** PROJECT + ** libim - SDSC image manipulation library + ** + ** DESCRIPTION + ** These functions convert images from their current depth to + ** images of a specific depth: + ** to 24-bit true color + ** to 8-bit color index + CLT + ** to 16-bit color index + CLT + ** to 8-bit grey index + ** to 1-bit mono + ** + ** PUBLIC CONTENTS + ** d =defined constant + ** f =function + ** m =defined macro + ** t =typedef/struct/union + ** v =variable + ** ? =other + ** + ** ImVfbToRgb f convert a vfb to 24-bit color + ** ImVfbToMono f convert to 8-bit monochrome + ** ImVfbFSDitherToMono f dither a Vfb to monochrome using + ** Floyd-Steinberg dithering + ** ImVfbToGray f convert to 8-bit grayscale + ** ImVfbToIndex f convert to n-bit color + ** ImVfbToIndex8 f convert to 8-bit color + ** ImVfbToIndex16 f convert to 8-bit color + ** + ** PRIVATE CONTENTS + ** none + ** + ** HISTORY + ** $Log: /roq/libim/imvfbto.c $ +* +* 1 11/02/99 4:38p Zaphod + ** Revision 1.18 1995/06/29 00:28:04 bduggan + ** updated copyright year + ** + ** Revision 1.17 1995/06/16 09:01:34 bduggan + ** changed bzero to memset. removed some unused vars + ** + ** Revision 1.16 1995/03/31 05:50:22 bduggan + ** Grab the lower 8-bits when converting to index8 from index16 + ** instead of the upper 8-bits. + ** + ** Revision 1.15 1995/02/16 21:43:47 bduggan + ** Removed threshold variable from dither function + ** + ** Revision 1.14 1995/01/18 21:49:37 bduggan + ** Put in ImCallocRetOnError macro so that -1 + ** is not returned from a function that's supposed to return + ** a pointer + ** + ** Revision 1.13 1995/01/12 00:36:02 bduggan + ** Added dithering support + ** + ** Revision 1.12 94/10/03 11:29:58 nadeau + ** Updated to ANSI C and C++ compatibility. + ** Removed all use of register keyword. + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + ** Changed all float arguments to double. + ** Added forward declarations. + ** Added misc. casts to passify SGI and DEC compilers. + ** Changed all macros and defined constants to have names + ** starting with IM. + ** Updated comments. + ** Updated indenting on some code. + ** Updated copyright message. + ** + ** Revision 1.11 92/09/18 12:39:07 vle + ** Fixed bug in ImVfbToMono() that caused RGB images to be monochromed + ** incorrectly. + ** + ** Revision 1.10 92/09/17 14:58:08 vle + ** Added ImVfbToIndex() which converts any VFB type to an N color + ** indexed VFB. Modified TryRGB() and TryYUV() to accommadate + ** the changes. Fixed bug in ImVfbToIndex16() that caused image + ** data to be shifted when it shouldn't be. Updated copyright + ** notice. + ** + ** Revision 1.9 91/10/03 09:23:49 nadeau + ** Fixed problem that mapped mono white to (1,1,1) instead + ** of (255,255,255) on RGB conversion. Added another decimal + ** place to RED/GREEN/BLUE percentages for grayscale conversion. + ** Fixed #includes. + ** + ** Revision 1.8 91/03/13 12:55:43 nadeau + ** Typo fixed. + ** + ** Revision 1.7 91/03/13 12:49:15 nadeau + ** Fixed minor CLT grayscale filling bug that always counted to + ** 256, whether the CLT was that big or not. + ** + ** Revision 1.6 91/03/08 14:38:32 nadeau + ** Added lots of comments. Added more robust support for the + ** various VFB types. Added IMVFBINDEX16 and IMVFBMONO support. + ** + ** Revision 1.5 91/01/30 18:14:22 nadeau + ** Added mono conversion and check and fixed bug in gray conversion. + ** + ** Revision 1.4 90/10/22 16:40:53 nadeau + ** Moved imvfbto8color.c into the file. + ** + ** Revision 1.3 90/07/23 12:57:12 todd + ** Add grayscale (no CLT) to ImVfbTo24() + ** + ** Revision 1.2 90/07/02 13:23:07 nadeau + ** Updated comment and added inclusion of iminternal.h. + ** + ** Revision 1.1 90/05/11 14:29:07 nadeau + ** Initial revision + ** + **/ + +/** + ** CODE CREDITS + ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1990. + **/ + +#include +#include "iminternal.h" + +#ifdef __STDC__ +ImVfb *ImVfbToGray( ImVfb *srcVfb, ImVfb *dstVfb ); +#else +ImVfb *ImVfbToGray(); +#endif + + +/* + * FUNCTION + * ImVfbToRgb - convert a vfb to RGB + * + * DESCRIPTION + * Promotion: + * Mono VFB: + * Index8 VFB: + * Index 16 VFB: + * Copied to RGB VFB. CLT used, if any. Otherwise + * grayscale assumed. + * + * No change: + * RGB VFB: + * Copied to RGB VFB. + * + * CLT HANDLING + * When a CLT is not present, the RGB values are generated assuming + * the incoming image is grayscale. Each grayscale index is copied + * to all three channels (red, green, blue) of the RGB VFB. For 16-bit + * index VFB's, the indexes are (unavoidably) truncated to the lower + * 8-bits. + * + * When a CLT is present, the RGB values are generated by looking up + * each VFB index in the CLT and copying the corresponding red, green, + * and blue values to the RGB VFB. + */ + +ImVfb * /* Returns changed VFB */ +#ifdef __STDC__ +ImVfbToRgb( ImVfb *srcVfb, ImVfb *dstVfb ) +#else +ImVfbToRgb( srcVfb, dstVfb ) + ImVfb *srcVfb; /* input vfb */ + ImVfb *dstVfb; /* possibly the output vfb */ +#endif +{ + ImVfb *rgbVfb; /* the 24-bit vfb */ + ImVfbPtr in; /* input pointer into srcVfb */ + ImVfbPtr out; /* output pointer into rgbVfb */ + ImClt *clt; /* the clt of the source vfb */ + ImCltPtr cp; /* pointer into clt */ + int fields; /* Input VFB's field mask */ + int type; /* Type of input VFB */ + ImVfbPtr last; /* Last pointer for input VFB */ + int index; /* Color index */ + + + /* + * Create the new VFB, if needed. + */ + if ( dstVfb == IMVFBNEW ) + { + if ( (rgbVfb = ImVfbAlloc( ImVfbQWidth( srcVfb ), + ImVfbQHeight( srcVfb ), IMVFBRGB )) == IMVFBNULL ) + { + ImErrNo = IMEMALLOC; + return( IMVFBNULL ); + } + } + else + { + /* + * Confirm the given VFB has an RGB field and that it has + * the same image dimensions. + */ + if ( !(ImVfbQFields( dstVfb ) & IMVFBRGB) ) + { + ImErrNo = IMENOTRGB; + return ( IMVFBNULL ); + } + if ( ImVfbQWidth( srcVfb ) != ImVfbQWidth( dstVfb ) ) + { + ImErrNo = IMEWIDTH; + return ( IMVFBNULL ); + } + if ( ImVfbQHeight( srcVfb ) != ImVfbQHeight( dstVfb ) ) + { + ImErrNo = IMEHEIGHT; + return ( IMVFBNULL ); + } + rgbVfb = dstVfb; + } + + + /* + * Figure out where to get input colors to be put into the RGB VFB. + */ + clt = ImVfbQClt( srcVfb ); /* Might be IMCLTNULL */ + fields = ImVfbQFields( srcVfb ); + if ( fields & IMVFBINDEX16 ) + type = IMVFBINDEX16; /* 16-bit color index */ + else if ( fields & IMVFBINDEX8 ) + type = IMVFBINDEX8; /* 8-bit color index */ + else if ( fields & IMVFBMONO ) + type = IMVFBMONO; /* 1-bit mono */ + else if ( fields & IMVFBRGB ) + { + if ( srcVfb == dstVfb ) + return ( srcVfb ); /* Already RGB */ + + last = ImVfbQLast( srcVfb ); + for( in=ImVfbQFirst(srcVfb), out=ImVfbQFirst(rgbVfb); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(rgbVfb,out) ) + { + ImVfbSRed( rgbVfb, out, ImVfbQRed( srcVfb, in ) ); + ImVfbSGreen( rgbVfb, out, ImVfbQGreen( srcVfb, in ) ); + ImVfbSBlue( rgbVfb, out, ImVfbQBlue( srcVfb, in ) ); + } + return ( rgbVfb ); + } + else + { + /* + * No image to convert? + */ + if ( dstVfb != rgbVfb ) + ImVfbFree( rgbVfb ); + ImErrNo = IMENOTINFO; + return ( IMVFBNULL ); + } + + + if ( clt == IMCLTNULL ) + { + /* + * No color table. Assume grayscale. + */ + switch ( type ) + { + case IMVFBMONO: /* Monochrome */ + last = ImVfbQLast( srcVfb ); + for( in=ImVfbQFirst(srcVfb), out=ImVfbQFirst(rgbVfb); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(rgbVfb,out) ) + { + index = (ImVfbQMono( srcVfb, in ) & 0x1); + ImVfbSRed( rgbVfb, out, index * 255 ); + ImVfbSGreen( rgbVfb, out, index * 255 ); + ImVfbSBlue( rgbVfb, out, index * 255 ); + } + return ( rgbVfb ); + + case IMVFBINDEX8: /* 8-bit color */ + last = ImVfbQLast( srcVfb ); + for( in=ImVfbQFirst(srcVfb), out=ImVfbQFirst(rgbVfb); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(rgbVfb,out) ) + { + index = ImVfbQIndex8( srcVfb, in ); + ImVfbSRed( rgbVfb, out, index ); + ImVfbSGreen( rgbVfb, out, index ); + ImVfbSBlue( rgbVfb, out, index ); + } + return ( rgbVfb ); + + case IMVFBINDEX16: /* 16-bit color */ + /* + * Grab the lower 8-bits of each index and treat it + * as a grayscale value. + */ + last = ImVfbQLast( srcVfb ); + for( in=ImVfbQFirst(srcVfb), out=ImVfbQFirst(rgbVfb); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(rgbVfb,out) ) + { + index = ((ImVfbQIndex16( srcVfb, in )) & 0x00FF); + ImVfbSRed( rgbVfb, out, index ); + ImVfbSGreen( rgbVfb, out, index ); + ImVfbSBlue( rgbVfb, out, index ); + } + return ( rgbVfb ); + } + } + + + /* + * Has color table. + */ + switch ( type ) + { + case IMVFBMONO: /* Monochrome */ + last = ImVfbQLast( srcVfb ); + for( in=ImVfbQFirst(srcVfb), out=ImVfbQFirst(rgbVfb); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(rgbVfb,out) ) + { + cp = ImCltQPtr( clt, ImVfbQMono( srcVfb, in ) ) ; + ImVfbSRed( rgbVfb, out, ImCltQRed( cp ) ); + ImVfbSGreen( rgbVfb, out, ImCltQGreen( cp ) ); + ImVfbSBlue( rgbVfb, out, ImCltQBlue( cp ) ); + } + break; + + case IMVFBINDEX8: /* 8-bit color */ + last = ImVfbQLast( srcVfb ); + for( in=ImVfbQFirst(srcVfb), out=ImVfbQFirst(rgbVfb); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(rgbVfb,out) ) + { + cp = ImCltQPtr( clt, ImVfbQIndex8( srcVfb, in ) ) ; + ImVfbSRed( rgbVfb, out, ImCltQRed( cp ) ); + ImVfbSGreen( rgbVfb, out, ImCltQGreen( cp ) ); + ImVfbSBlue( rgbVfb, out, ImCltQBlue( cp ) ); + } + break; + + case IMVFBINDEX16: /* 16-bit color */ + last = ImVfbQLast( srcVfb ); + for( in=ImVfbQFirst(srcVfb), out=ImVfbQFirst(rgbVfb); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(rgbVfb,out) ) + { + cp = ImCltQPtr( clt, ImVfbQIndex16( srcVfb, in ) ) ; + ImVfbSRed( rgbVfb, out, ImCltQRed( cp ) ); + ImVfbSGreen( rgbVfb, out, ImCltQGreen( cp ) ); + ImVfbSBlue( rgbVfb, out, ImCltQBlue( cp ) ); + } + break; + } + return ( rgbVfb ); +} + + + + + +/* + * CONSTANTS & MACRO + * *_PERCENT - percent contribution to computation of YIQ gray + * YIQ - compute YIQ grayscale from RGB + * + * DESCRIPTION + * The red, green, and blue components of a color are combined + * to get a gray value via the formula: + * + * gray = 0.30 * red + 0.59 * green + 0.11 * blue + 0.5 + * + * This is based upon the NTSC YIQ Y equation that computes the intensity + * value to be used in displaying a color image on a black-n-white TV. + */ + +#define IM_RED_PERCENT 0.299 +#define IM_GREEN_PERCENT 0.587 +#define IM_BLUE_PERCENT 0.114 + +#define IM_NTSCY( red, green, blue ) \ +((int)( IM_RED_PERCENT * (float) (red) + \ + IM_GREEN_PERCENT * (float) (green) + \ + IM_BLUE_PERCENT * (float) (blue) + 0.5 )) + + + + + +/* + * FUNCTION + * ImVfbToMono - convert to 1-bit monochrome + * + * DESCRIPTION + * No change: + * Mono VFB: + * Copied to 1-bit mono VFB. + * + * Demotion: + * Index8 VFB: + * Index 16 VFB: + * If no CLT, assumed already grayscale and just + * thresholded and copied. Otherwise pixels converted + * to NTSC Y space to generate grayscale. Those are + * thresholded and copied. + * RGB VFB: + * Pixels converted to NTSC Y space to generate gray. + * Those are thresholded and copied. + * + * NTSC Y SPACE CONVERSION + * The red, green, and blue components of each pixel color are combined + * to get a gray value via the formula: + * + * gray = 0.30 * red + 0.59 * green + 0.11 * blue + 0.5 + * + * This is based upon the NTSC YIQ Y equation that computes the intensity + * value to be used in displaying a color image on a black-n-white TV. + * + * MONO THRESHOLD + * Grayscale values less than or equal to the monoThreshold argument + * are considered 'black' and given a mono value of 0. Those above + * the threshold are considered 'white' and given a mono value of 1. + */ + +ImVfb * /* Returns mono VFB */ +#ifdef __STDC__ +ImVfbToMono( ImVfb *srcVfb, int monoThreshold, ImVfb *dstVfb ) +#else +ImVfbToMono( srcVfb, monoThreshold, dstVfb ) + ImVfb *srcVfb; /* the vfb to change to mono */ + int monoThreshold; /* Threshold index */ + ImVfb *dstVfb; /* the destination vfb */ +#endif +{ + int i; /* color index counter */ + int g; /* computed gray */ + int fields; /* Source fields */ + ImVfb *monoVfb; /* the new vfb to create */ + ImVfbPtr in; /* input pointer */ + ImVfbPtr out; /* output pointer */ + ImClt *clt; /* color lookup table */ + ImCltPtr cp; /* lookup table pointer */ + int glt[256]; /* gray lookup table */ + ImVfbPtr last; /* Last pointer for input VFB */ + int type; /* Type of VFB */ + int index; /* Color index holder */ + + + /* + * Create the new VFB, if needed. + */ + if ( dstVfb == IMVFBNEW ) + { + if ( (monoVfb = ImVfbAlloc( ImVfbQWidth( srcVfb ), + ImVfbQHeight( srcVfb ), IMVFBMONO )) == IMVFBNULL ) + { + ImErrNo = IMEMALLOC; + return( IMVFBNULL ); + } + } + else + { + /* + * Confirm the given VFB has a mono field and that it has + * the same image dimensions. + */ + if ( !(ImVfbQFields( dstVfb ) & IMVFBMONO) ) + { + ImErrNo = IMENOTMONO; + return ( IMVFBNULL ); + } + if ( ImVfbQWidth( srcVfb ) != ImVfbQWidth( dstVfb ) ) + { + ImErrNo = IMEWIDTH; + return ( IMVFBNULL ); + } + if ( ImVfbQHeight( srcVfb ) != ImVfbQHeight( dstVfb ) ) + { + ImErrNo = IMEHEIGHT; + return ( IMVFBNULL ); + } + monoVfb = dstVfb; + } + + + /* + * Figure out where to get input colors to be put into the mono VFB. + */ + clt = ImVfbQClt( srcVfb ); /* Might be IMCLTNULL */ + fields = ImVfbQFields( srcVfb ); + if ( fields & IMVFBINDEX16 ) + type = IMVFBINDEX16; /* 16-bit color index */ + else if ( fields & IMVFBINDEX8 ) + type = IMVFBINDEX8; /* 8-bit color index */ + else if ( fields & IMVFBRGB ) + type = IMVFBRGB; /* 24-bit RGB color */ + else if ( fields & IMVFBMONO ) + { + if ( srcVfb == dstVfb ) + return ( srcVfb ); /* Already mono */ + + last = ImVfbQLast( srcVfb ); + for( in=ImVfbQFirst(srcVfb), out=ImVfbQFirst(monoVfb); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(monoVfb,out) ) + { + ImVfbSMono( monoVfb, out, ImVfbQMono( srcVfb, in ) ); + } + return ( monoVfb ); + } + else + { + /* + * No image to convert? + */ + if ( dstVfb != monoVfb ) + ImVfbFree( monoVfb ); + ImErrNo = IMENOTINFO; + return ( IMVFBNULL ); + } + + + + if ( clt == IMCLTNULL ) + { + /* + * No color table. Assume grayscale. + */ + switch ( type ) + { + case IMVFBINDEX8: /* 8-bit color */ + last = ImVfbQLast( srcVfb ); + for( in=ImVfbQFirst(srcVfb), out=ImVfbQFirst(monoVfb); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(monoVfb,out) ) + { + index = ImVfbQIndex8( srcVfb, in ); + if ( index > monoThreshold ) + index = 1; + else + index = 0; + ImVfbSMono( monoVfb, out, index ); + } + return ( monoVfb ); + + case IMVFBINDEX16: /* 16-bit color */ + last = ImVfbQLast( srcVfb ); + for( in=ImVfbQFirst(srcVfb), out=ImVfbQFirst(monoVfb); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(monoVfb,out) ) + { + index = ImVfbQIndex16( srcVfb, in ); + if ( index > monoThreshold ) + index = 1; + else + index = 0; + ImVfbSMono( monoVfb, out, index ); + } + return ( monoVfb ); + + case IMVFBRGB: /* RGB true-color */ + last = ImVfbQLast( srcVfb ); + for( in=ImVfbQFirst(srcVfb), out=ImVfbQFirst( monoVfb ); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(monoVfb,out) ) + { + g = IM_NTSCY( ImVfbQRed( srcVfb, in ), + ImVfbQGreen( srcVfb, in ), + ImVfbQBlue( srcVfb, in ) ); + if ( g > monoThreshold ) + g = 1; + else + g = 0; + ImVfbSMono( monoVfb, out, g ); + } + return ( monoVfb ); + } + } + + + /* + * Has color table. Use NTSC Y conversion. + */ + switch ( type ) + { + case IMVFBINDEX8: /* 8-bit color */ + /* + * Scan the CLT and create a temporary gray + * lookup table. Then scan the image, looking up each + * 8-bit index in the gray (mono) CLT to get a mono value. + */ + for( i=0, cp=ImCltQFirst(clt); i monoThreshold ) + glt[i] = 1; + else + glt[i] = 0; + } + for ( ; i < 256; i++ ) + glt[i] = 0; + + last = ImVfbQLast( srcVfb ); + for( in = ImVfbQFirst(srcVfb), out = ImVfbQFirst( monoVfb ); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(monoVfb,out) ) + { + g = glt[ ImVfbQIndex8( srcVfb, in ) ]; + ImVfbSMono( monoVfb, out, g ); + } + return ( monoVfb ); + + case IMVFBINDEX16: /* 16-bit color */ + /* + * 16-bit (or less) index. Creating a temporary CLT + * capable of holding 2^16 = 65535 entries is a bit much. + * So, the image is scanned and each pixel looked up in + * the VFB's CLT, then converted a gray-scale value. + * Slower, but more practical. + */ + last = ImVfbQLast( srcVfb ); + for( in = ImVfbQFirst(srcVfb), out = ImVfbQFirst( monoVfb ); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(monoVfb,out) ) + { + cp = ImCltQPtr( clt, ImVfbQIndex16( srcVfb, in ) ); + g = IM_NTSCY( ImCltQRed( cp ), ImCltQGreen( cp ), + ImCltQBlue( cp ) ); + if ( g > monoThreshold ) + g = 1; + else + g = 0; + ImVfbSMono( monoVfb, out, g ); + } + return ( monoVfb ); + + case IMVFBRGB: /* RGB true-color */ + /* + * Don't know what it means to have an RGB VFB with a CLT? + * Ignore the CLT. + */ + last = ImVfbQLast( srcVfb ); + for( in=ImVfbQFirst(srcVfb), out=ImVfbQFirst( monoVfb ); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(monoVfb,out) ) + { + g = IM_NTSCY( ImVfbQRed( srcVfb, in ), + ImVfbQGreen( srcVfb, in ), + ImVfbQBlue( srcVfb, in ) ); + if ( g > monoThreshold ) + g = 1; + else + g = 0; + ImVfbSMono( monoVfb, out, g ); + } + return ( monoVfb ); + } + return IMVFBNULL; +} + + + +/* + * FUNCTION + * ImVfbFSDitherToMono + * + * DESCRIPTION + * Dither a color image to black and white, using the Floyd Steinberg + * algorithm. + * + * FLOYD-STEINBERG ALGORITHM + * First, we convert the image to greyscale. Then we traverse the + * image from top to bottom, and left to right. If a given pixel + * has a value greater than or equal to 255, then this pixel is + * set to white in the outgoing mono vfb, and the error (i.e. how much + * more than 255 this pixel is) is propogated to the surrounding pixels. + * + * Seven-sixteenths of the error propogates to the right neighbor, + * three-sixteenths to the bottom-left, five-sixteenths to the bottom, + * and one-sixteenths to the bottom-right. + * + * ---- xx 7/16 + * 3/16 5/16 1/16 + * + * The purpose of the algorithm is to enable one to dither an image in + * a single top-bottom, left-right pass. + * + * RETURN VALUE + * The destination VFB, or IMVFBNULL if an error has occurred. + * + */ + +ImVfb * /* Returns mono VFB */ +#ifdef __STDC__ +ImVfbFSDitherToMono(ImVfb *sourceVfb, ImVfb *dstVfb) +#else +ImVfbFSDitherToMono(sourceVfb, dstVfb) +ImVfb *sourceVfb; +ImVfb *dstVfb; +#endif +{ + ImVfb *grayVfb; /* VFB to hold grayscale image */ + ImVfbPtr vfbptr; /* Ptr into a vfb */ + int *dithered; /* Dithered values */ + int row, column; /* Loop indices */ + int width, height; /* Width, height of image */ + int error; /* Error value to propogate */ + + /* + * Create a new vfb if necessary + */ + + if ( dstVfb == IMVFBNEW ) + { + if ( (dstVfb = ImVfbAlloc( ImVfbQWidth( sourceVfb ), + ImVfbQHeight( sourceVfb ), IMVFBMONO )) == IMVFBNULL ) + { + ImErrNo = IMEMALLOC; + return( IMVFBNULL ); + } + } + else + { + /* + * Confirm that the given VFB has a mono field and that it + * has the same image dimensions. + */ + + if ( !(ImVfbQFields( dstVfb ) & IMVFBMONO) ) + { + ImErrNo = IMENOTMONO; + return ( IMVFBNULL ); + } + if ( ImVfbQWidth( sourceVfb ) != ImVfbQWidth( dstVfb ) ) + { + ImErrNo = IMEWIDTH; + return ( IMVFBNULL ); + } + if ( ImVfbQHeight( sourceVfb ) != ImVfbQHeight( dstVfb ) ) + { + ImErrNo = IMEHEIGHT; + return ( IMVFBNULL ); + } + } + + /* + * First, create a Grayscale version of the image with the same + * height and width + */ + + width = ImVfbQWidth (sourceVfb); + height = ImVfbQHeight (sourceVfb); + + grayVfb = ImVfbAlloc (width, height, IMVFBGRAY); + if (grayVfb == IMVFBNULL) + { + ImErrNo = IMEMALLOC; + return ( IMVFBNULL ); + } + + /* + * Next create space for the dithered image + */ + + ImCallocRetOnError( dithered, int *, (width * height), (sizeof(int)), NULL); + + if (dithered == NULL) + { + ImErrNo = IMEMALLOC; + return (IMVFBNULL); + } + + /* + * Convert the color image to grayscale and then loop through + * the entire image to pull out the individual grayscale values. + * + * The grayscale values are between 0 and 255, with 0 being black + * and 255 being white. + */ + + if ((ImVfbToGray (sourceVfb, grayVfb)) == IMVFBNULL) + { + /* ImErrNo will have been set by ImVfbToGray */ + return ( IMVFBNULL ); + } + + + vfbptr = ImVfbQFirst (grayVfb); + for (row = 0; row < height; row++) + { + for (column = 0; column < width; column++) + { + dithered[row*width+column] = ImVfbQGray (grayVfb, vfbptr); + ImVfbSInc (grayVfb, vfbptr); + } + } + + ImVfbFree (grayVfb); /* Free the grayscale vfb */ + + + /* + * Loop through the image and set the monochrome vfb to black or + * white depending on the current image value. Then propogate + * the error to the neighboring pixels. + */ + + vfbptr = ImVfbQFirst (dstVfb); + for (row = 0; row < height; row++) + { + for (column = 0; column < width; column++) + { + + /* + * If the value is over the threshold, then set the pixel + * white, otherwise set it to black + */ + + if (dithered[row*width+column] > 255) + { + ImVfbSMono (dstVfb, vfbptr, 1); + error = (dithered[row*width+column]) - 255; + } + else + { + ImVfbSMono (dstVfb, vfbptr, 0); + error = dithered[row*width+column]; + } + + /* + * Propogate the error to the right neighbor + */ + + if (column < width - 1) + dithered[row*width+column+1] += ((7 * error) / 16); + + /* + * Propogate the error to the next row + */ + + if (row < height - 1) + { + dithered[(row+1)*width+column] += ((5 * error) / 16); + if (column > 0) + dithered[(row+1)*width+column-1] += ((3 * error) / 16); + if (column < width -1) + dithered[(row+1)*width+column+1] += (error / 16); + } + ImVfbSInc (dstVfb, vfbptr); + } + } + + /* + * Free the dithered image + */ + + free (dithered); + return (dstVfb); + +} + + + +/* + * FUNCTION + * ImVfbToGray - convert to 8-bit grayscale + * + * DESCRIPTION + * Promotion: + * Mono VFB: + * Copied to 8-bit gray VFB. CLT copied, if any. + * Otherwise grayscale assumed. + * + * Demotion: + * Index8 VFB: + * If no CLT, assumed already grayscale and just copied. + * Otherwise pixels converted to NTSC Y space to generate + * grayscale. + * Index 16 VFB: + * If no CLT, assumed already grayscale and just copied + * (truncating indexes!). Otherwise pixels converted + * to NTSC Y space to generate grayscale. + * RGB VFB: + * Pixels converted to NTSC Y space to generate gray. + * + * NTSC Y SPACE CONVERSION + * The red, green, and blue components of each pixel color are combined + * to get a gray value via the formula: + * + * gray = 0.30 * red + 0.59 * green + 0.11 * blue + 0.5 + * + * This is based upon the NTSC YIQ Y equation that computes the intensity + * value to be used in displaying a color image on a black-n-white TV. + */ + +ImVfb * /* Returns gray VFB */ +#ifdef __STDC__ +ImVfbToGray( ImVfb *srcVfb, ImVfb *dstVfb ) +#else +ImVfbToGray( srcVfb, dstVfb ) + ImVfb *srcVfb; /* the vfb to change to gray */ + ImVfb *dstVfb; /* the destination vfb */ +#endif +{ + int i; /* color index counter */ + int g; /* computed gray */ + int fields; /* Source fields */ + ImVfb *grayVfb; /* the new vfb to create */ + ImVfbPtr in; /* input pointer */ + ImVfbPtr out; /* output pointer */ + ImClt *clt; /* color lookup table */ + ImCltPtr cp; /* lookup table pointer */ + int glt[256]; /* gray lookup table */ + ImVfbPtr last; /* Last pointer for input VFB */ + int type; /* Type of VFB */ + int index; /* Color index holder */ + + + /* + * Create the new VFB, if needed. + */ + if ( dstVfb == IMVFBNEW ) + { + if ( (grayVfb = ImVfbAlloc( ImVfbQWidth( srcVfb ), + ImVfbQHeight( srcVfb ), IMVFBINDEX8 )) == IMVFBNULL ) + { + ImErrNo = IMEMALLOC; + return( IMVFBNULL ); + } + } + else + { + /* + * Confirm the given VFB has a gray field and that it has + * the same image dimensions. + */ + if ( !(ImVfbQFields( dstVfb ) & IMVFBINDEX8) ) + { + ImErrNo = IMENOTINDEX8; + return ( IMVFBNULL ); + } + if ( ImVfbQWidth( srcVfb ) != ImVfbQWidth( dstVfb ) ) + { + ImErrNo = IMEWIDTH; + return ( IMVFBNULL ); + } + if ( ImVfbQHeight( srcVfb ) != ImVfbQHeight( dstVfb ) ) + { + ImErrNo = IMEHEIGHT; + return ( IMVFBNULL ); + } + grayVfb = dstVfb; + } + + + /* + * Figure out where to get input colors to be put into the gray VFB. + */ + clt = ImVfbQClt( srcVfb ); /* Might be IMCLTNULL */ + fields = ImVfbQFields( srcVfb ); + if ( fields & IMVFBINDEX16 ) + type = IMVFBINDEX16; /* 16-bit color index */ + else if ( fields & IMVFBINDEX8 ) + type = IMVFBINDEX8; /* 8-bit color index */ + else if ( fields & IMVFBRGB ) + type = IMVFBRGB; /* 24-bit RGB color */ + else if ( fields & IMVFBMONO ) + type = IMVFBMONO; /* 1-bit mono */ + else + { + /* + * No image to convert? + */ + if ( dstVfb != grayVfb ) + ImVfbFree( grayVfb ); + ImErrNo = IMENOTINFO; + return ( IMVFBNULL ); + } + + + + if ( clt == IMCLTNULL ) + { + /* + * No color table. Assume grayscale. + */ + switch ( type ) + { + case IMVFBMONO: /* Monochrome */ + last = ImVfbQLast( srcVfb ); + for( in=ImVfbQFirst(srcVfb), out=ImVfbQFirst(grayVfb); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(grayVfb,out) ) + { + index = ImVfbQMono( srcVfb, in ); + if ( index == 0 ) + ImVfbSIndex8( grayVfb, out, 0 ); + else + ImVfbSIndex8( grayVfb, out, 255 ); + } + return ( grayVfb ); + + case IMVFBINDEX8: /* 8-bit color */ + last = ImVfbQLast( srcVfb ); + for( in=ImVfbQFirst(srcVfb), out=ImVfbQFirst(grayVfb); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(grayVfb,out) ) + { + ImVfbSIndex8( grayVfb, out, ImVfbQIndex8( srcVfb, in ) ); + } + return ( grayVfb ); + + case IMVFBINDEX16: /* 16-bit color */ + /* + * Grab the upper 8-bits of each index and treat it + * as a grayscale value. + */ + last = ImVfbQLast( srcVfb ); + for( in=ImVfbQFirst(srcVfb), out=ImVfbQFirst(grayVfb); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(grayVfb,out) ) + { + index = ((ImVfbQIndex16( srcVfb, in )) & 0xFF00)>>8; + ImVfbSIndex8( grayVfb, out, index ); + } + return ( grayVfb ); + + case IMVFBRGB: /* RGB true-color */ + last = ImVfbQLast( srcVfb ); + for( in=ImVfbQFirst(srcVfb), out=ImVfbQFirst( grayVfb ); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(grayVfb,out) ) + { + g = IM_NTSCY( ImVfbQRed( srcVfb, in ), + ImVfbQGreen( srcVfb, in ), + ImVfbQBlue( srcVfb, in ) ); + ImVfbSIndex8( grayVfb, out, g ); + } + return ( grayVfb ); + } + } + + + /* + * Has color table. Use NTSC Y conversion. + */ + switch ( type ) + { + case IMVFBMONO: /* Monochrome */ + /* + * Create a 2-entry temporary gray lookup table. Then scan + * the image, looking up each 1-bit index in the gray CLT + * to get a gray scale value. + */ + cp = ImCltQFirst( clt ); + glt[0] = IM_NTSCY( ImCltQRed( cp ), ImCltQGreen( cp ), + ImCltQBlue( cp ) ); + ImCltSInc( clt, cp ); + glt[1] = IM_NTSCY( ImCltQRed( cp ), ImCltQGreen( cp ), + ImCltQBlue( cp ) ); + + last = ImVfbQLast( srcVfb ); + for( in = ImVfbQFirst(srcVfb), out = ImVfbQFirst( grayVfb ); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(grayVfb,out) ) + { + g = glt[ ((ImVfbQMono( srcVfb, in )) & 0x1) ]; + ImVfbSIndex8( grayVfb, out, g ); + } + return ( grayVfb ); + + case IMVFBINDEX8: /* 8-bit color */ + /* + * Scan the CLT and create a temporary gray lookup table. + * Then scan the image, looking up each 8-bit index in the + * gray CLT to get a gray scale value. + */ + for( i=0, cp=ImCltQFirst(clt); i 16 ) + { + /* nColors too big */ + } + + /* + * Create the new VFB, if needed. + */ + if ( dstVfb == IMVFBNEW ) + { + if( bitsNeeded < 9 ) + { + if ( (indexVfb = ImVfbAlloc( ImVfbQWidth( srcVfb ), + ImVfbQHeight( srcVfb ), IMVFBINDEX8 )) == + IMVFBNULL ) + { + ImErrNo = IMEMALLOC; + return( IMVFBNULL ); + } + } + else + { + if ( (indexVfb = ImVfbAlloc( ImVfbQWidth( srcVfb ), + ImVfbQHeight( srcVfb ), IMVFBINDEX16 )) == + IMVFBNULL ) + { + ImErrNo = IMEMALLOC; + return( IMVFBNULL ); + } + } + } + else + { + /* + * Confirm the given VFB has enough bits for the desired + * number of colors and the same image dimensions. + */ + if( bitsNeeded < 9 ) + { + if( ( !(ImVfbQFields( dstVfb ) & IMVFBINDEX8) ) && + ( !(ImVfbQFields( dstVfb ) & IMVFBINDEX16) ) ) + { + ImErrNo = IMENOTINDEX8; + return ( IMVFBNULL ); + } + } + else + { + if( !(ImVfbQFields( dstVfb ) & IMVFBINDEX16) ) + { + ImErrNo = IMENOTINDEX16; + return ( IMVFBNULL ); + } + } + if ( ImVfbQWidth( srcVfb ) != ImVfbQWidth( dstVfb ) ) + { + ImErrNo = IMEWIDTH; + return ( IMVFBNULL ); + } + if ( ImVfbQHeight( srcVfb ) != ImVfbQHeight( dstVfb ) ) + { + ImErrNo = IMEHEIGHT; + return ( IMVFBNULL ); + } + indexVfb = dstVfb; + } + + + /* + * Figure out where to get input colors to be put into the Index8 VFB. + */ + clt = ImVfbQClt( srcVfb ); /* Might be IMCLTNULL */ + fields = ImVfbQFields( srcVfb ); + if ( fields & IMVFBMONO ) + { + /* Copy the mono VFB to the index 8 VFB. */ + last = ImVfbQLast( srcVfb ); + for( in=ImVfbQFirst(srcVfb), out=ImVfbQFirst(indexVfb); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(indexVfb,out) ) + { + index = ImVfbQMono( srcVfb, in ); + if ( index == 0 ) + ImVfbSIndex( indexVfb, out, 0 ); + else + ImVfbSIndex( indexVfb, out, nColors-1 ); + } + + if ( clt != IMCLTNULL ) + dstClt = ImCltDup( clt ); + else + dstClt = ImCltGrayRamp( IMCLTNULL, 0, nColors-1, + 0, nColors-1, IMCLTNEW ); + if ( dstClt == IMCLTNULL ) + { + if ( dstVfb != indexVfb ) + ImVfbFree( indexVfb ); + return ( IMVFBNULL ); /* ImErrNo already set */ + } + ImVfbSClt( indexVfb, dstClt ); + return ( indexVfb ); + } + else if( fields & IMVFBINDEX8 ) + type = IMVFBINDEX8; /* 8-bit color index */ + else if ( fields & IMVFBINDEX16 ) + type = IMVFBINDEX16; /* 16-bit color index */ + else if ( fields & IMVFBRGB ) + type = IMVFBRGB; /* 24-bit RGB color */ + else + { + /* + * No image to convert? + */ + if ( dstVfb != indexVfb ) + ImVfbFree( indexVfb ); + ImErrNo = IMENOTINFO; + return ( IMVFBNULL ); + } + + + /* + * At this point we have either an Index8, Index16, or RGB VFB + * to convert. + */ + + + /* + * If the destination doesn't already have a CLT, add one. + * Remember, the source and destination could be the same VFB. + */ + dstClt = ImVfbQClt( indexVfb ); + if ( dstClt == IMCLTNULL ) + { + indexClt = ImCltAlloc( nColors-1 ); + if ( indexClt == IMCLTNULL ) + { + if ( dstVfb != indexVfb ) + ImVfbFree( indexVfb ); + ImErrNo = IMEMALLOC; + return( IMVFBNULL ); + } + ImVfbSClt( indexVfb, indexClt ); + } + else + indexClt = dstClt; + + + /* Try using masked RGB values. */ + if ( TryRGB( srcVfb, indexVfb, nColors-1 ) >= 0 ) + return( indexVfb ); + + /* If that didn't work, cut according to YUV values. */ + if ( TryYUV( srcVfb, indexVfb, nColors-1 ) >= 0 ) + return( indexVfb ); + + /* If didn't work (can't think why it wouldn't), return error. */ + if ( dstClt != indexClt ) + ImCltFree( indexClt ); + if ( dstVfb != indexVfb ) + ImVfbFree( indexVfb ); + ImErrNo = IMENOTINFO; + return( IMVFBNULL ); +} + + + + +/* + * FUNCTION + * ImVfbToIndex8 - change a vfb to have 8-bit color + * + * DESCRIPTION + * Promotion: + * Mono VFB: + * Copied to 8-bit VFB. CLT copied, if any. Otherwise + * 2-entry grayscale VFB generated. + * No change: + * Index 8 VFB: + * Copied to 8-bit VFB. CLT copied, if any. + * + * Demotion: + * Index 16 VFB: + * RGB VFB: + * Scanned for number of unique colors in RGB and YUV + * space. If too many, colors are masked to reduce + * their precision and the new number of unique colors + * counted. Continues until we get a reasonable number + * of colors. + */ + +ImVfb * /* Returns converted VFB */ +#ifdef __STDC__ +ImVfbToIndex8( ImVfb *srcVfb, ImVfb *dstVfb ) +#else +ImVfbToIndex8( srcVfb, dstVfb ) + ImVfb *srcVfb; /* Source VFB */ + ImVfb *dstVfb; /* Destination VFB */ +#endif +{ + int fields; /* vfb field description */ + ImClt *dstClt; /* destination CLT */ + ImClt *indexClt; /* destination CLT */ + ImClt *clt; /* Source CLT */ + ImVfbPtr in; /* input pointer */ + ImVfbPtr out; /* output pointer */ + int type; /* Type of VFB */ + int index; /* Color index holder */ + ImVfbPtr last; /* Last pointer for input VFB */ + ImVfb *indexVfb; /* New 8-bit color index VFB */ + + + /* + * Create the new VFB, if needed. + */ + if ( dstVfb == IMVFBNEW ) + { + if ( (indexVfb = ImVfbAlloc( ImVfbQWidth( srcVfb ), + ImVfbQHeight( srcVfb ), IMVFBINDEX8 )) == IMVFBNULL ) + { + ImErrNo = IMEMALLOC; + return( IMVFBNULL ); + } + } + else + { + /* + * Confirm the given VFB has an Index8 field and that it has + * the same image dimensions. + */ + if ( !(ImVfbQFields( dstVfb ) & IMVFBINDEX8) ) + { + ImErrNo = IMENOTINDEX8; + return ( IMVFBNULL ); + } + if ( ImVfbQWidth( srcVfb ) != ImVfbQWidth( dstVfb ) ) + { + ImErrNo = IMEWIDTH; + return ( IMVFBNULL ); + } + if ( ImVfbQHeight( srcVfb ) != ImVfbQHeight( dstVfb ) ) + { + ImErrNo = IMEHEIGHT; + return ( IMVFBNULL ); + } + indexVfb = dstVfb; + } + + + /* + * Figure out where to get input colors to be put into the Index8 VFB. + */ + clt = ImVfbQClt( srcVfb ); /* Might be IMCLTNULL */ + fields = ImVfbQFields( srcVfb ); + if ( fields & IMVFBINDEX16 ) + type = IMVFBINDEX16; /* 16-bit color index */ + else if ( fields & IMVFBRGB ) + type = IMVFBRGB; /* 24-bit RGB color */ + else if ( fields & IMVFBMONO ) + { + /* Copy the mono VFB to the index 8 VFB. */ + last = ImVfbQLast( srcVfb ); + for( in=ImVfbQFirst(srcVfb), out=ImVfbQFirst(indexVfb); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(indexVfb,out) ) + { + index = ImVfbQMono( srcVfb, in ); + if ( index == 0 ) + ImVfbSIndex8( indexVfb, out, 0 ); + else + ImVfbSIndex8( indexVfb, out, 255 ); + } + + if ( clt != IMCLTNULL ) + dstClt = ImCltDup( clt ); + else + dstClt = ImCltGrayRamp( IMCLTNULL, 0, 255, 0, 255, + IMCLTNEW ); + if ( dstClt == IMCLTNULL ) + { + if ( dstVfb != indexVfb ) + ImVfbFree( indexVfb ); + return ( IMVFBNULL ); /* ImErrNo already set */ + } + ImVfbSClt( indexVfb, dstClt ); + return ( indexVfb ); + } + else if ( fields & IMVFBINDEX8 ) + { + if ( srcVfb == dstVfb ) + return ( srcVfb ); /* Already Index8 */ + + last = ImVfbQLast( srcVfb ); + for( in=ImVfbQFirst(srcVfb), out=ImVfbQFirst(indexVfb); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(indexVfb,out) ) + { + ImVfbSIndex8( indexVfb, out, ImVfbQIndex8( srcVfb, in)); + } + + if ( clt != IMCLTNULL ) + dstClt = ImCltDup( clt ); + else + dstClt = ImCltGrayRamp( IMCLTNULL, 0, 255, 0, 255, + IMCLTNEW ); + if ( dstClt == IMCLTNULL ) + { + if ( dstVfb != indexVfb ) + ImVfbFree( indexVfb ); + return ( IMVFBNULL ); /* ImErrNo already set */ + } + ImVfbSClt( indexVfb, dstClt ); + return ( indexVfb ); + } + else + { + /* + * No image to convert? + */ + if ( dstVfb != indexVfb ) + ImVfbFree( indexVfb ); + ImErrNo = IMENOTINFO; + return ( IMVFBNULL ); + } + + + /* + * At this point we have either an RGB or an Index16 VFB to convert. + */ + + + /* + * If the destination doesn't already have a CLT, add one. + * Remember, the source and destination could be the same VFB. + */ + dstClt = ImVfbQClt( indexVfb ); + if ( dstClt == IMCLTNULL ) + { + indexClt = ImCltAlloc( 256 ); + if ( indexClt == IMCLTNULL ) + { + if ( dstVfb != indexVfb ) + ImVfbFree( indexVfb ); + ImErrNo = IMEMALLOC; + return( IMVFBNULL ); + } + ImVfbSClt( indexVfb, indexClt ); + } + else + { + indexClt = dstClt; + } + + + /* Try using masked RGB values. */ + if ( TryRGB( srcVfb, indexVfb, 256 ) >= 0 ) + return( indexVfb ); + + /* If that didn't work, cut according to YUV values. */ + if ( TryYUV( srcVfb, indexVfb, 256 ) >= 0 ) + return( indexVfb ); + + /* If didn't work (can't think why it wouldn't), return error. */ + if ( dstClt != indexClt ) + ImCltFree( indexClt ); + if ( dstVfb != indexVfb ) + ImVfbFree( indexVfb ); + ImErrNo = IMENOTINFO; + return( IMVFBNULL ); +} + + + + + +/* + * FUNCTION + * ImVfbToIndex16 - change a vfb to have 16-bit color + * + * DESCRIPTION + * Promotion: + * Mono VFB: + * Index8 VFB: + * Copied to 16-bit VFB. CLT copied, if any. Otherwise + * 256-entry grayscale VFB generated. + * No change: + * Index 16 VFB: + * Copied to 16-bit VFB. CLT copied, if any. + * + * Demotion: + * RGB VFB: + * Scanned for number of unique colors in RGB and YUV + * space. If too many, colors are masked to reduce + * their precision and the new number of unique colors + * counted. Continues until we get a reasonable number + * of colors. + */ + +ImVfb * /* Returns converted VFB */ +#ifdef __STDC__ +ImVfbToIndex16( ImVfb *srcVfb, ImVfb *dstVfb ) +#else +ImVfbToIndex16( srcVfb, dstVfb ) + ImVfb *srcVfb; /* Source VFB */ + ImVfb *dstVfb; /* Destination VFB */ +#endif +{ + int fields; /* vfb field description */ + ImClt *dstClt; /* destination CLT */ + ImClt *indexClt; /* destination CLT */ + ImClt *clt; /* Source CLT */ + ImVfbPtr in; /* input pointer */ + ImVfbPtr out; /* output pointer */ + int index; /* Color index holder */ + ImVfbPtr last; /* Last pointer for input VFB */ + ImVfb *indexVfb; /* New 8-bit color index VFB */ + + + /* + * Create the new VFB, if needed. + */ + if ( dstVfb == IMVFBNEW ) + { + if ( (indexVfb = ImVfbAlloc( ImVfbQWidth( srcVfb ), + ImVfbQHeight( srcVfb ), IMVFBINDEX16 )) == IMVFBNULL ) + { + ImErrNo = IMEMALLOC; + return( IMVFBNULL ); + } + } + else + { + /* + * Confirm the given VFB has an Index16 field and that it has + * the same image dimensions. + */ + if ( !(ImVfbQFields( dstVfb ) & IMVFBINDEX16) ) + { + ImErrNo = IMENOTINDEX16; + return ( IMVFBNULL ); + } + if ( ImVfbQWidth( srcVfb ) != ImVfbQWidth( dstVfb ) ) + { + ImErrNo = IMEWIDTH; + return ( IMVFBNULL ); + } + if ( ImVfbQHeight( srcVfb ) != ImVfbQHeight( dstVfb ) ) + { + ImErrNo = IMEHEIGHT; + return ( IMVFBNULL ); + } + indexVfb = dstVfb; + } + + + /* + * Figure out where to get input colors to be put into the Index16 VFB. + */ + clt = ImVfbQClt( srcVfb ); /* Might be IMCLTNULL */ + fields = ImVfbQFields( srcVfb ); + if ( fields & IMVFBINDEX16 ) + { + if ( srcVfb == dstVfb ) + return ( srcVfb ); /* Already Index16 */ + + last = ImVfbQLast( srcVfb ); + for( in=ImVfbQFirst(srcVfb), out=ImVfbQFirst(indexVfb); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(indexVfb,out) ) + { + ImVfbSIndex16( indexVfb, out, ImVfbQIndex16(srcVfb,in)); + } + + if ( clt != IMCLTNULL ) + dstClt = ImCltDup( clt ); + else + dstClt = ImCltGrayRamp( IMCLTNULL, 0, 65535, 0, 65535, + IMCLTNEW ); + if ( dstClt == IMCLTNULL ) + { + if ( dstVfb != indexVfb ) + ImVfbFree( indexVfb ); + return ( IMVFBNULL ); /* ImErrNo already set */ + } + ImVfbSClt( indexVfb, dstClt ); + return ( indexVfb ); + } + else if ( fields & IMVFBINDEX8 ) + { + /* Copy the index 8 VFB to the index 16 VFB. */ + last = ImVfbQLast( srcVfb ); + for( in=ImVfbQFirst(srcVfb), out=ImVfbQFirst(indexVfb); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(indexVfb,out) ) + { + ImVfbSIndex16( indexVfb, out, + (ImVfbQIndex8( srcVfb,in) ) ); + } + + if ( clt != IMCLTNULL ) + dstClt = ImCltDup( clt ); + else + dstClt = ImCltGrayRamp( IMCLTNULL, 0, 65535, 0, 65535, + IMCLTNEW ); + if ( dstClt == IMCLTNULL ) + { + if ( dstVfb != indexVfb ) + ImVfbFree( indexVfb ); + return ( IMVFBNULL ); /* ImErrNo already set */ + } + ImVfbSClt( indexVfb, dstClt ); + return ( indexVfb ); + } + else if ( fields & IMVFBMONO ) + { + /* Copy the mono VFB to the index 16 VFB. */ + last = ImVfbQLast( srcVfb ); + for( in=ImVfbQFirst(srcVfb), out=ImVfbQFirst(indexVfb); + in <= last; + ImVfbSInc(srcVfb,in), ImVfbSInc(indexVfb,out) ) + { + index = ImVfbQMono( srcVfb, in ); + if ( index == 0 ) + ImVfbSIndex16( indexVfb, out, 0 ); + else + ImVfbSIndex16( indexVfb, out, 65535 ); + } + + if ( clt != IMCLTNULL ) + dstClt = ImCltDup( clt ); + else + dstClt = ImCltGrayRamp( IMCLTNULL, 0, 65535, 0, 65535, + IMCLTNEW ); + if ( dstClt == IMCLTNULL ) + { + if ( dstVfb != indexVfb ) + ImVfbFree( indexVfb ); + return ( IMVFBNULL ); /* ImErrNo already set */ + } + ImVfbSClt( indexVfb, dstClt ); + return ( indexVfb ); + } + else if ( !(fields & IMVFBRGB) ) + { + /* + * No image to convert? + */ + if ( dstVfb != indexVfb ) + ImVfbFree( indexVfb ); + ImErrNo = IMENOTINFO; + return ( IMVFBNULL ); + } + + + /* + * At this point we have an RGB VFB to convert. + */ + + + /* + * If the destination doesn't already have a CLT, add one. + * Remember, the source and destination could be the same VFB. + */ + dstClt = ImVfbQClt( indexVfb ); + if ( dstClt == IMCLTNULL ) + { + indexClt = ImCltAlloc( 65536 ); + if ( indexClt == IMCLTNULL ) + { + if ( dstVfb != indexVfb ) + ImVfbFree( indexVfb ); + ImErrNo = IMEMALLOC; + return( IMVFBNULL ); + } + ImVfbSClt( indexVfb, indexClt ); + } + else + indexClt = dstClt; + + + /* Try using masked RGB values. */ + if ( TryRGB( srcVfb, indexVfb, 65536 ) >= 0 ) + return( indexVfb ); + + /* If that didn't work, cut according to YUV values. */ + if ( TryYUV( srcVfb, indexVfb, 65536 ) >= 0 ) + return( indexVfb ); + + /* If didn't work (can't think why it wouldn't), return error. */ + if ( dstClt != indexClt ) + ImCltFree( indexClt ); + if ( dstVfb != indexVfb ) + ImVfbFree( indexVfb ); + ImErrNo = IMENOTINFO; + return( IMVFBNULL ); +} + + + + + +/* + * FUNCTION + * CutBox - cut the largest box into 2 boxes + * + * DESCRIPTION + * The largest box is at the top of the Blist + * Cut it in the direction of the largest range + * Cut it on its median + * Put the resulting boxes back in the Blist according to their new + * ranges + * + */ + +static void /* Returns nothing */ +CutBox( ) +{ + struct box *bp; /* largest box */ + struct box *bpnew; /* new box to be created */ + int i, j, k; /* yuv indices */ + int i1, i2, i3; /* indices in range order */ + int *iy, *iu, *iv; /* where are y,u,v in i,j,k */ + int mid; /* mid point (median) of yuv */ + int num; /* counter to get to mid */ + int in; /* Freq index */ + int lastnum; /* shadow of 'num' */ + int split; /* where to split the box */ + + + /* pointer to current box and new box: */ + + bp = Blist; Blist = Blist->b_next; + bpnew = Bfree; Bfree = Bfree->b_next; + + *bpnew = *bp; /* identical copy, for now */ + + + /* determine which dimension needs to be cut: */ + + if ( bp->b_maxrange == bp->b_max[IMVT_Y] - bp->b_min[IMVT_Y] + 1 ) + { + i1 = IMVT_Y; i2 = IMVT_U; i3 = IMVT_V; + iy = &i; iu = &j; iv = &k; +#ifdef DEBUG + fprintf( stderr, "Cut along Y: " ); +#endif + } + else + { + if ( bp->b_maxrange == bp->b_max[IMVT_U] - bp->b_min[IMVT_U] + 1 ) + { + i1 = IMVT_U; i2 = IMVT_Y; i3 = IMVT_V; + iu = &i; iy = &j; iv = &k; +#ifdef DEBUG + fprintf( stderr, "Cut along U: " ); +#endif + } + else + { + i1 = IMVT_V; i2 = IMVT_Y; i3 = IMVT_U; + iv = &i; iy = &j; iu = &k; +#ifdef DEBUG + fprintf( stderr, "Cut along V: " ); +#endif + } + } + + + /* determine the median location: */ + + mid = 0; + + for( i = bp->b_min[i1]; i <= bp->b_max[i1]; i++ ) + { + for( j = bp->b_min[i2]; j <= bp->b_max[i2]; j++ ) + { + for( k = bp->b_min[i3]; k <= bp->b_max[i3]; k++ ) + { + in = IM_INDEX( *iy, *iu, *iv ); + mid += Freq[in]; + } + } + } + + mid = mid / 2; + + + /* find where that median occurred: */ + + num = 0; + lastnum = -1; + + for( i = bp->b_min[i1]; i <= bp->b_max[i1]; i++, lastnum = num ) + { + for( j = bp->b_min[i2]; j <= bp->b_max[i2]; j++ ) + { + for( k = bp->b_min[i3]; k <= bp->b_max[i3]; k++ ) + { + in = IM_INDEX( *iy, *iu, *iv ); + num += Freq[in]; + } + } + if ( num >= mid ) + break; + } + + + /* determine where to put the split: */ + /* divide between 'split' and 'split+1' */ + + if ( i <= bp->b_min[i1] ) + { + split = i; + } + else + { + if ( i >= bp->b_max[i1] ) + { + split = i - 1; + } + else + { + if ( num == mid ) + split = i; + else + split = i - 1; + } + } + + +#ifdef DEBUG + fprintf( stderr, "%3d - %3d + %3d - %3d\n", + bp->b_min[i1], split, split+1, bp->b_max[i1] ); +#endif + + + /* make the proper assignments into the 2 box structures: */ + + bp->b_max[i1] = split; + bpnew->b_min[i1] = split+1; + + + /* clean-up the 2 box structures: */ + + FixUp( bp ); + FixUp( bpnew ); + + + + /* insert the 2 box structures where they belong: */ + + Insert( bp ); + Insert( bpnew ); + + + /* done: */ + +} + + + + + +/* + * FUNCTION + * FixUp - cleanup new or changed box structures + * + * DESCRIPTION + * Reset b_min and b_max + * Re-compute b_maxrange + * + */ + +static void /* Returns nothing */ +#ifdef __STDC__ +FixUp( struct box *bp ) +#else +FixUp( bp ) + struct box *bp; /* the box to fixup */ +#endif +{ + int iy, iu, iv; /* yuv values */ + int in; /* Freq index */ + int iymin, iymax; /* y range */ + int iumin, iumax; /* u range */ + int ivmin, ivmax; /* v range */ + int ry, ru, rv; /* yuv ranges */ + + + /* possibly the mins and maxes: */ + /* these are most likely more spread out than they need */ + /* to be - we will fix that below */ + + iymin = bp->b_max[IMVT_Y]; iymax = bp->b_min[IMVT_Y]; + iumin = bp->b_max[IMVT_U]; iumax = bp->b_min[IMVT_U]; + ivmin = bp->b_max[IMVT_V]; ivmax = bp->b_min[IMVT_V]; + + + /* loop through the ranges setting correct mins and maxes: */ + + for( iy = bp->b_min[IMVT_Y]; iy <= bp->b_max[IMVT_Y]; iy++ ) + { + for( iu = bp->b_min[IMVT_U]; iu <= bp->b_max[IMVT_U]; iu++ ) + { + for( iv = bp->b_min[IMVT_V]; iv <= bp->b_max[IMVT_V]; iv++ ) + { + in = IM_INDEX( iy, iu, iv ); + if ( Freq[in] != 0 ) + { + if ( iy < iymin ) iymin = iy; + if ( iy > iymax ) iymax = iy; + if ( iu < iumin ) iumin = iu; + if ( iu > iumax ) iumax = iu; + if ( iv < ivmin ) ivmin = iv; + if ( iv > ivmax ) ivmax = iv; + } + } + } + } + + + /* set the new min and max values: */ + + bp->b_min[IMVT_Y] = iymin; bp->b_max[IMVT_Y] = iymax; + bp->b_min[IMVT_U] = iumin; bp->b_max[IMVT_U] = iumax; + bp->b_min[IMVT_V] = ivmin; bp->b_max[IMVT_V] = ivmax; + + + /* determine the maximum range for this box: */ + + ry = bp->b_max[IMVT_Y] - bp->b_min[IMVT_Y] + 1; + ru = bp->b_max[IMVT_U] - bp->b_min[IMVT_U] + 1; + rv = bp->b_max[IMVT_V] - bp->b_min[IMVT_V] + 1; + + bp->b_maxrange = ry; + + if ( ru >= ry && ru >= rv ) + bp->b_maxrange = ru; + + if ( rv >= ry && rv >= ru ) + bp->b_maxrange = rv; + + + /* done: */ + +} + + + + + +/* + * FUNCTION + * Hash8 - hash code for an 8-bit color + * Hash12 - hash code for a 12-bit color + * Hash16 - hash code for a 16-bit color + * + */ + +static int /* Returns hash code */ +#ifdef __STDC__ +Hash8( unsigned char r, unsigned char g, unsigned char b ) +#else +Hash8( r, g, b ) + unsigned char r, g, b; +#endif +{ + return( ( r ^ g ^ b ) & 0xff ); +} + +static int /* Returns hash code */ +#ifdef __STDC__ +Hash12( unsigned char r, unsigned char g, unsigned char b ) +#else +Hash12( r, g, b ) + unsigned char r, g, b; +#endif +{ + int i1, i2; /* temporary integers */ + + i1 = ( ( r << 4 ) & 0x0ff0 ) | ( g >> 4 ); + i2 = ( ( g << 8 ) & 0x0f00 ) | b; + return( ( i1 ^ i2 ) & 0x0fff ); +} + +static int /* Returns hash code */ +#ifdef __STDC__ +Hash16( unsigned char r, unsigned char g, unsigned char b ) +#else +Hash16( r, g, b ) + unsigned char r, g, b; +#endif +{ + int i1, i2; /* temporary integers */ + + i1 = ( ( r << 4 ) & 0x0ff0 ) | ( g >> 4 ); + i2 = ( ( g << 8 ) & 0x0f00 ) | b; + return( ( i1 ^ i2 ) & 0x0fff ); +} + + + + + +/* + * FUNCTION + * Insert - insert a box into the box list + * + */ + +static void /* Returns nothing */ +#ifdef __STDC__ +Insert( struct box *b ) +#else +Insert( b ) + struct box *b; +#endif +{ + struct box *bp; /* box pointer */ + struct box *last; /* shadow box pointer */ + + + /* if list is empty, create it with this element: */ + + if ( Blist == NULL ) + { + Blist = b; + b->b_next = NULL; + return; + } + + + + /* otherwise, do an insertion sort: */ + + for( bp = Blist, last = NULL; bp != NULL; last = bp, bp = bp->b_next ) + { + /* loop until bp->b_maxrange gets small enough: */ + + if ( b->b_maxrange < bp->b_maxrange ) + continue; + + /* if at first point, make it head of list: */ + + if ( last == NULL ) + { + b->b_next = Blist; + Blist = b; + } + else /* otherwise, link it in */ + { + b->b_next = bp; + last->b_next = b; + } + + return; + + } + + + + /* if got here then add to end of list: */ + + b->b_next = NULL; + last->b_next = b; + +} + + + + + +/* + * FUNCTION + * NColors - count the # colors that will result if + * the given mask is applied to all RGB values + * + * DESCRIPTION + * This routine looks through all RGB values in the given vfb + * (applying a bitmask to each) and keeps a table of unique + * combinations. If that table overflows, then this mask + * cannot be used to quantize the colors. + * + * A return of -1 indicates that the table overflowed + * A return of >= 0 indicates the number of unique combinations + */ + +static int /* Returns # of colors */ +#ifdef __STDC__ +NColors( struct rgb *rgb, int n, int mask, + int (*hash)(unsigned char r, unsigned char g, unsigned char b), ImVfb *vfb ) +#else +NColors( rgb, n, mask, hash, vfb ) + struct rgb *rgb; /* rgb array (already alloc'd) */ + int n; /* max # colors allowed */ + int mask; /* bitmask to apply to all rgb's */ + int (*hash)(); /* hash function to index into rgb[] */ + ImVfb *vfb; /* the vfb to browse */ +#endif +{ + int i; /* counter */ + int r, g, b; /* rgb values */ + int h; /* hash index */ + int type; /* type of vfb */ + int nunique; /* # unique combinations */ + struct rgb *rgbp; /* pointer into rgb[] */ + ImVfbPtr p; /* pointer into the vfb */ + ImClt *clt; /* clt attached to vfb */ + ImCltPtr cp; /* pointer into clt */ + + + + /* determine vfb type: */ + + if ( ImVfbQFields(vfb) & IMVFBRGB ) + type = IMVFBRGB; + else + { + if ( ImVfbQFields(vfb) & IMVFBINDEX8 ) + type = IMVFBINDEX8; + else + type = IMVFBINDEX16; + + clt = ImVfbQClt( vfb ); + } + + + /* set all rgb[].r_used to unused: */ + + for( i = 0, rgbp = rgb; i < n; i++, rgbp++ ) + rgbp->r_used = 0; + + + /* count the unique values: */ + /* if table overflows, return an error */ + + nunique = 0; + + for( p = ImVfbQFirst( vfb ); p <= ImVfbQLast( vfb ); ImVfbSInc( vfb, p ) ) + { + if ( type == IMVFBRGB ) + { + r = ImVfbQRed( vfb, p ) & mask; + g = ImVfbQGreen( vfb, p ) & mask; + b = ImVfbQBlue( vfb, p ) & mask; + } + else + { + cp = ImCltQPtr( clt, ImVfbQIndex( vfb, p ) ); + r = ImCltQRed( cp ) & mask; + g = ImCltQGreen( cp ) & mask; + b = ImCltQBlue( cp ) & mask; + } + + h = (*hash)( r, g, b ); /* hash index */ + + + /* find this rgb combo in rgb[]: */ + /* if cannot find it, try to create it */ + /* if cannot create it, then table is full */ + + for( i = 0, rgbp = &rgb[h]; i < n; i++, rgbp++ ) + { + if ( rgbp >= &rgb[n] ) + rgbp = rgb; + + + /* if find an unused slot, create this rgb: */ + + if ( rgbp->r_used == 0 ) + { + nunique++; + rgbp->r_used = 1; + rgbp->r_r = r; + rgbp->r_g = g; + rgbp->r_b = b; + break; + } + + + /* if slot is used, see if it matches r, g, and b:*/ + + if ( rgbp->r_r == r && rgbp->r_g == g && rgbp->r_b == b ) + break; + + + /* if got to here, then haven't found an exact */ + /* match, and haven't found an empty slot to */ + /* create a new entry in - keep looking */ + + } + + + + /* if got to here because no more array locations to */ + /* look at, table is full and colors cannot be */ + /* quantized with this bitmask: */ + + if ( i >= n ) + return( -1 ); + + + /* keep browsing through the vfb: */ + + } + + + /* if get here, then colors can be quantized with this bitmask: */ + + return( nunique ); +} + + + + + + + +/* + * FUNCTION + * TryRGB - see if an RGB model will work with this + * number of colors + * + * DESCRIPTION + * This routine will see if there are <= N unique RGB combinations + * If not, it will then start to mask off bits from the end of + * the RGB components and see if there are <= N unique combinations + * of those + * + * A return of -1 indicates that the destination vfb is screwy + * A return of -2 indicates that a malloc failed + * A return of -3 indicates that there were too many unique + * combinations and that these colors will need to be quantized further + */ + +static int /* REturns status */ +#ifdef __STDC__ +TryRGB( ImVfb *srcVfb, ImVfb *dstVfb, int maxcolors ) +#else +TryRGB( srcVfb, dstVfb, maxcolors ) + ImVfb *srcVfb; /* source vfb */ + ImVfb *dstVfb; /* destination vfb */ + int maxcolors; /* max # colors allowed */ +#endif +{ + int mask; /* bit mask */ + int comp; /* complement to the bitmask */ + struct rgb *rgb; /* array of rgb values */ + struct rgb *rgbp; /* pointer into rgb array */ + int i; /* counter */ + int r, g, b; /* rgb values */ + int h; /* hash index */ + int index; /* index into clt */ + int nc; /* return from NColors() */ + int type; /* type of frame buffer */ + ImVfbPtr psrc, pdst; /* pointers into the vfbs */ + ImClt *clt; /* color lookup table */ + ImCltPtr cp; /* clt pointer */ +#ifdef __STDC__ + int (*hash)(unsigned char r, unsigned char g, unsigned char b); /* hash function */ +#else + int (*hash)(); /* hash function */ +#endif + + + if( ( maxcolors < 0 ) || ( maxcolors > 65536 ) ) + { + return( -1 ); + } + else if( maxcolors > 4096 ) + { + hash = Hash16; + } + else if( maxcolors > 256 ) + { + hash = Hash12; + } + else + { + hash = Hash8; + } + + /* allocate rgb array: */ + + rgb = (struct rgb *) malloc( maxcolors * sizeof( struct rgb ) ); + if ( rgb == NULL ) + { + return( -2 ); + } + + + + /* try to fit all the colors into maxcolors slots: */ + + for( i=0, comp=0; i <= IM_MAX_BITS_TO_MASK; i++, comp = ( comp << 1 ) | 1 ) + { + mask = ~comp; + nc = NColors( rgb, maxcolors, mask, hash, srcVfb ); + +#ifdef DEBUG + fprintf( stderr, "NColors( rgb, %d, 0x%0x, hash, srcVfb ) = %d\n", + maxcolors, mask, nc ); +#endif + if ( nc >= 0 ) /* successful ! */ + break; + } + + + /* if all calls to NColors() failed, return an error: */ + + if ( nc < 0 ) + { + free( (char *) rgb ); + return( -3 ); + } + + + + /* if a call to NColors() succeeded, fill dstVfb: */ + + + /* first, pack colors in top of clt (point to top anyway): */ + + for( i = 0, rgbp = &rgb[0], index = 0; i < maxcolors; i++, rgbp++ ) + { + if ( rgbp->r_used == 0 ) + continue; + + rgbp->r_index = index; + index++; + } + + + /* determine type of vfb the input is: */ + + if ( ImVfbQFields(srcVfb) & IMVFBRGB ) + type = IMVFBRGB; + else + { + if ( ImVfbQFields(srcVfb) & IMVFBINDEX8 ) + type = IMVFBINDEX8; + else + type = IMVFBINDEX16; + + clt = ImVfbQClt( srcVfb ); + } + + + /* fill the output vfb with correct color indices: */ + + for( psrc = ImVfbQFirst(srcVfb), pdst = ImVfbQFirst(dstVfb); + psrc <= ImVfbQLast(srcVfb); + ImVfbSInc(srcVfb,psrc), ImVfbSInc(dstVfb,pdst) ) + { + if ( type == IMVFBRGB ) + { + r = ImVfbQRed( srcVfb, psrc ) & mask; + g = ImVfbQGreen( srcVfb, psrc ) & mask; + b = ImVfbQBlue( srcVfb, psrc ) & mask; + } + else + { + cp = ImCltQPtr( clt, ImVfbQIndex( srcVfb, psrc ) ); + r = ImCltQRed( cp ) & mask; + g = ImCltQGreen( cp ) & mask; + b = ImCltQBlue( cp ) & mask; + } + + + /* locate that RGB combination in the hash table: */ + + h = (*hash)( r, g, b ); + + for( i = 0, rgbp = &rgb[h]; i < maxcolors; i++, rgbp++ ) + { + if ( rgbp >= &rgb[maxcolors] ) + rgbp = rgb; + + if ( rgbp->r_used == 0 ) + continue; + + if ( rgbp->r_r == r && rgbp->r_g == g + && rgbp->r_b == b ) + break; + } + + /* we know that this will succeed sooner or later... */ + + index = rgbp - rgb; + ImVfbSIndex( dstVfb, pdst, rgb[index].r_index ); + + } + + + /* fill the new clt: */ + + + clt = ImVfbQClt( dstVfb ); + + if ( nc != maxcolors ) + { + ImCltFree( clt ); /* get rid of full clt */ + clt = ImCltAlloc( nc ); + ImVfbSClt( dstVfb, clt ); + } + + for( i = 0, cp = ImCltQFirst(clt), rgbp = rgb; + i < maxcolors; + i++, rgbp++ ) + { + if ( rgbp->r_used ) + { + ImCltSRed( cp, rgbp->r_r ); + ImCltSGreen( cp, rgbp->r_g ); + ImCltSBlue( cp, rgbp->r_b ); + ImCltSInc( clt, cp ); + } + } + + + /* end game: */ + + free( (char *) rgb ); + + return( nc ); +} + + + + + +/* + * FUNCTION + * TryYUV - quantize the colors down with a YUV model + * + * DESCRIPTION + * This routine will see if there are <= N unique YUV combinations + * If not, it will then start to mask off bits from the end of + * the YUV components and see if there are <= N unique combinations + * of those + * + * A return of -1 indicates that the destination vfb is screwy + * A return of -2 indicates that a malloc failed + */ + +static int /* Returns status */ +#ifdef __STDC__ +TryYUV( ImVfb *srcVfb, ImVfb *dstVfb, int maxcolors ) +#else +TryYUV( srcVfb, dstVfb, maxcolors ) + ImVfb *srcVfb; /* source vfb */ + ImVfb *dstVfb; /* destination vfb */ + int maxcolors; /* # colors */ +#endif +{ + int i; /* counter */ + int in; /* Freq index */ + int iy, iu, iv; /* yuv indices */ + int num; /* total # colors */ + int r, g, b; /* rgb values */ + int iymin, iymax, iumin, iumax, ivmin, ivmax; /* yuv ranges */ + int type; /* type of vfb */ + int ymean, umean, vmean; /* accumulate avg yuv values */ + int denom; /* denominator for the avg yuv values */ + struct box *bp; /* box pointer */ + ImVfbPtr psrc, pdst; /* vfb pointers */ + ImClt *clt; /* clt attached to srcVfb / dstVfb */ + ImCltPtr cp; /* clt pointer */ + + + + /* # colors to quantize to: */ + if ( ( maxcolors < 0 ) || ( maxcolors > 65536 ) ) + { + return( -1 ); + } + + + /* allocate the Frequency array: */ + + Freq = (int *) malloc( (IMVT_MAXY+1)*(IMVT_MAXU+1)*(IMVT_MAXV+1)*sizeof(int) ); + if ( Freq == NULL ) + return( -2 ); + + + /* allocate Boxes structure: */ + + Boxes = (struct box *) malloc( maxcolors * sizeof( struct box ) ); + if ( Boxes == NULL ) + { + free( (char *) Freq ); + return( -2 ); + } + + + /* setup used and free lists: */ + + Blist = NULL; + Bfree = Boxes; + for( i=0; i iymax ) iymax = iy; + if ( iu < iumin ) iumin = iu; + if ( iu > iumax ) iumax = iu; + if ( iv < ivmin ) ivmin = iv; + if ( iv > ivmax ) ivmax = iv; + } + +#ifdef DEBUG + fprintf( stderr, "\nYUV bounds on original data:\n" ); + fprintf( stderr, "\tY :\t%4d - %4d\n", iymin, iymax ); + fprintf( stderr, "\tU :\t%4d - %4d\n", iumin, iumax ); + fprintf( stderr, "\tV :\t%4d - %4d\n", ivmin, ivmax ); + fprintf( stderr, "%d unique YUV combinations\n", num ); +#endif + + + /* setup the first box: */ + + Blist = bp = Bfree; + Bfree = Bfree->b_next; + bp->b_next = NULL; + + bp->b_min[IMVT_Y] = iymin; + bp->b_max[IMVT_Y] = iymax; + bp->b_min[IMVT_U] = iumin; + bp->b_max[IMVT_U] = iumax; + bp->b_min[IMVT_V] = ivmin; + bp->b_max[IMVT_V] = ivmax; + + bp->b_maxrange = iymax - iymin; + if ( iumax - iumin > bp->b_maxrange ) + bp->b_maxrange = iumax - iumin; + if ( ivmax - ivmin > bp->b_maxrange ) + bp->b_maxrange = ivmax - ivmin; + + bp->b_maxrange++; /* range is actually 1 larger */ + + + + /* cut the box (n-1) times: */ + + for( i = 1; i < maxcolors; i++ ) + CutBox(); + + + + /* re-use the Frequency array to hold index into the box #: */ + /* at the same time, compute the RGB mean in each box: */ + + for( i = 0, bp = Blist; i < maxcolors; i++, bp = bp->b_next ) + { + ymean = umean = vmean = 0; + denom = 0; + + for( iy = bp->b_min[IMVT_Y]; iy <= bp->b_max[IMVT_Y]; iy++ ) + { + for( iu = bp->b_min[IMVT_U]; iu <= bp->b_max[IMVT_U]; iu++ ) + { + for( iv = bp->b_min[IMVT_V]; iv <= bp->b_max[IMVT_V]; iv++ ) + { + in = IM_INDEX( iy, iu, iv ); + + if ( Freq[in] > 0 ) + { + ymean += iy * Freq[in]; + umean += iu * Freq[in]; + vmean += iv * Freq[in]; + denom += Freq[in]; + } + + Freq[in] = i; + } + } + } + + if ( denom > 0 ) + { + ymean /= denom; + umean /= denom; + vmean /= denom; + } + + bp->b_mean[IMVT_R] = IM_RFYUV( IM_YFIY(ymean), IM_UFIU(umean), IM_VFIV(vmean) ); + bp->b_mean[IMVT_G] = IM_GFYUV( IM_YFIY(ymean), IM_UFIU(umean), IM_VFIV(vmean) ); + bp->b_mean[IMVT_B] = IM_BFYUV( IM_YFIY(ymean), IM_UFIU(umean), IM_VFIV(vmean) ); + + if ( bp->b_mean[IMVT_R] > 255 ) bp->b_mean[IMVT_R] = 255; + if ( bp->b_mean[IMVT_G] > 255 ) bp->b_mean[IMVT_G] = 255; + if ( bp->b_mean[IMVT_B] > 255 ) bp->b_mean[IMVT_B] = 255; + + if ( bp->b_mean[IMVT_R] < 0 ) bp->b_mean[IMVT_R] = 0; + if ( bp->b_mean[IMVT_G] < 0 ) bp->b_mean[IMVT_G] = 0; + if ( bp->b_mean[IMVT_B] < 0 ) bp->b_mean[IMVT_B] = 0; + +#ifdef DEBUG + fprintf( stderr, "Color %3d: YUV = %4d , %4d , %4d\t\t", i, + ymean, umean, vmean ); + fprintf( stderr, "RGB = %3d , %3d , %3d\n", + bp->b_mean[R], bp->b_mean[G], bp->b_mean[B] ); +#endif + + + } + + + + /* insert the indices into the destination vfb: */ + + for( psrc = ImVfbQFirst(srcVfb), pdst = ImVfbQFirst(dstVfb); + psrc <= ImVfbQLast(srcVfb); + ImVfbSInc(srcVfb,psrc), ImVfbSInc(dstVfb,pdst) ) + { + if ( type == IMVFBRGB ) + { + r = ImVfbQRed( srcVfb, psrc ); + g = ImVfbQGreen( srcVfb, psrc ); + b = ImVfbQBlue( srcVfb, psrc ); + } + else + { + cp = ImCltQPtr( clt, ImVfbQIndex( srcVfb, psrc ) ); + r = ImCltQRed( cp ); + g = ImCltQGreen( cp ); + b = ImCltQBlue( cp ); + } + + + /* locate that RGB combination in the Freq array: */ + + iy = IM_IYFY( IM_YFRGB( r, g, b ) ); + iu = IM_IUFU( IM_UFRGB( r, g, b ) ); + iv = IM_IVFV( IM_VFRGB( r, g, b ) ); + in = IM_INDEX( iy, iu, iv ); + + ImVfbSIndex( dstVfb, pdst, Freq[in] ); + + } + + + /* fill the new clt: */ + + + clt = ImVfbQClt( dstVfb ); + + for( i = 0, cp = ImCltQFirst(clt), bp = Blist; + i < maxcolors; + i++, ImCltSInc(clt,cp), bp = bp->b_next ) + { + ImCltSRed( cp, bp->b_mean[IMVT_R] ); + ImCltSGreen( cp, bp->b_mean[IMVT_G] ); + ImCltSBlue( cp, bp->b_mean[IMVT_B] ); + } + + + /* end game: */ + + free( (char *) Boxes ); + free( (char *) Freq ); + + return( maxcolors ); +} diff --git a/utils/roq2/libim/imviff.c b/utils/roq2/libim/imviff.c new file mode 100644 index 0000000..2ceaa77 --- /dev/null +++ b/utils/roq2/libim/imviff.c @@ -0,0 +1,2305 @@ +/** + ** $Header: /roq/libim/imviff.c 1 11/02/99 4:38p Zaphod $ + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + ** a division of General Atomics, San Diego, California, USA + ** + ** Users and possessors of this source code are hereby granted a + ** nonexclusive, royalty-free copyright and design patent license to + ** use this code in individual software. License is not granted for + ** commercial resale, in whole or in part, without prior written + ** permission from SDSC. This source is provided "AS IS" without express + ** or implied warranty of any kind. + ** + ** For further information contact: + ** E-Mail: info@sds.sdsc.edu + ** + ** Surface Mail: Information Center + ** San Diego Supercomputer Center + ** P.O. Box 85608 + ** San Diego, CA 92138-5608 + ** (619) 534-5000 + **/ + +#define HEADER " $Header: /roq/libim/imviff.c 1 11/02/99 4:38p Zaphod $" + +/** + ** FILE + ** imviff.c - Khoros image storage type + ** + ** PROJECT + ** libim - SDSC image manipulation library + ** + ** DESCRIPTION + ** imviff.c contains routines to read and write VIFF image files for + ** the image manipulation library. Raster data read in is stored + ** in a VFB. Raster data written out is taken from a tag table. + ** + ** PUBLIC CONTENTS + ** d =defined constant + ** f =function + ** m =defined macro + ** t =typedef/struct/union + ** v =variable + ** ? =other + ** + ** none + ** + ** PRIVATE CONTENTS + ** imViffRead f read a Targa file + ** imViffWrite f write an uncompressed RGB file + ** + ** + ** + ** HISTORY + + ** $Log: /roq/libim/imviff.c $ + ** + ** 1 11/02/99 4:38p Zaphod + ** Revision 1.12 1995/06/29 00:28:04 bduggan + ** updated copyright year + ** + ** Revision 1.11 1995/06/15 21:22:38 bduggan + ** changed bzero to memset + ** + ** Revision 1.10 1995/04/03 21:40:22 bduggan + ** took out #ifdef NEWMAGIC + ** + ** Revision 1.9 1995/01/10 23:50:04 bduggan + ** made read/write routines static + ** + ** Revision 1.8 94/10/03 11:31:09 nadeau + ** Updated to ANSI C and C++ compatibility. + ** Removed all use of register keyword. + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + ** Changed all float arguments to double. + ** Added forward declarations. + ** Added misc. casts to passify SGI and DEC compilers. + ** Changed all macros and defined constants to have names + ** starting with IM. + ** Rearranged magic number structures for format handlers. + ** Made format handler routines static (i.e., local to file). + ** Updated comments, adding format descriptions and references. + ** Updated indenting on some code. + ** Updated copyright message. + ** + ** Revision 1.7 93/08/26 17:11:05 secoskyj + ** Modified the read routine error messages + ** + ** Revision 1.6 92/12/03 01:56:17 nadeau + ** Corrected info messages. + ** + ** Revision 1.5 92/11/04 12:10:02 groening + ** put ImFIleFormat info and magic number info + ** from imfmt.c into this file. + ** + ** Revision 1.4 92/10/19 14:06:11 groening + ** added ImInfo features + ** + ** Revision 1.3 92/08/31 17:43:23 vle + ** Updated copyright notice. + ** + ** Revision 1.2 92/08/24 14:29:18 groening + ** minor stuff + ** + ** Revision 1.1 92/06/02 09:34:49 groening + ** Initial revision + ** + **/ + + +/** + ** + ** FORMAT + ** VIFF - Khoros viff images + ** + ** AKA + ** xv + ** + ** FORMAT REFERENCES + ** Writing Programs/VIFF Format, Chapter 1, Khoros Programmer's + ** Manual, Volume II, University of New Mexico + ** + ** CODE CREDITS + ** Custom development, Chris Groening, San Diego Supercomputer Center, 1992. + ** + ** DESCRIPTION + ** + ** Viff images are very flexible: they can store images in a variety + ** of encoding schemes and dimensions. More than 3 dimensions are + ** possible in order to store data in Viff files that is not necessarily + ** meant to be visualized. + ** + ** A Viff file has the following sections: + ** + ** - header (1024 bytes) + ** - map(s) + ** - location data + ** - image data + ** + ** The locations of the last three sections are specified + ** in the last three fields of the header. The (very verbose) + ** header is described in detail below. + ** + ** + ** The Viff format can deal with many things, however not amny of these + ** are compatible with the way image tools is implemented now. Right now + ** multiple images are not handled. + ** + ** What can be translated is 1 or 3 band image stored in bit or byte. + ** 1 band image can also be stored in 2 byte format (short). + ** All of these formats can have optional color lookup tables. + ** The color lookup table can only be stored in byte format. + ** + ** + ** + ** + ** Here is the complete description of the viff format header file, + ** taken directly from Chapter 1 Volume 2 of the khoros manuals + ** + The header is 1024 bytes long. Its fields can be grouped into five categories: + - administration : file management information + - data storiage : describes how the data is stored, but not how it + it is interpreted. + - location data : describes the spatial location of the data (optional) + - data mapping : describes how the data should be mapped or + interpreted. + - color space : for images, indicates what coordinate space and + model is being used. + + +______________________________________________________________________________ +______________________________________________________________________________ +char A one byte magic number that identifies a VIFF file +identifier; + +DEFINES: XV_FILE_MAGIC_NUM + +______________________________________________________________________________ +char A one byte code indicating Khoros file type +file_type; (currently only VIFF). + +DEFINES: XV_FILE_TYPE_XVIFF + +______________________________________________________________________________ +char A one byte code indicating the specific release of the +release; viff.h file (currently 0); this does not have to agree + with the Khoros system release number. + +DEFINES: IM_XV_IMAGE_REL_NUM + +______________________________________________________________________________ +char A one byte code indicating the specific version of the +version; viff.h file (currently 3); this does not have to agree + with the Khoros system version number. For example, + version 1 release 0 is referred to as viff 1.0. + +DEFINES: IM_XV_IMAGE_VER_NUM + +______________________________________________________________________________ +char A 512 byte space available for any use, but currently is +comment[512]; used in Khoros as a comment field to document the VIFF + data file. + +DEFINES: none + +______________________________________________________________________________ +char A one byte code indicating how the particular architecture +machine_dep; that the image was last processed on treats data. Currently + supported are DEC order, IEEE order and NS order. Supported + machines include the VAX, SUN, SONY News, Silicon Graphics, + Motorola, Encore, Sequent, MIPS, DEC, IBM, Apollo, and NeXT. + +DEFINES: VIFF_DEP_IEEEORDER + VIFF_DEP_DECORDER + VIFF_DEP_NSORDER + VIFF_DEP_BIGENDIAN + VIFF_DEP_LITENDIAN + +______________________________________________________________________________ +unsigned long These two unsigned long fields indicate data size, +row_size, specifically, the number of data items in a band. +col_size; row_size indicates the length of a row (the number of columns, + or the image width) in pixels. col_size indicates the + length of a column (the number of rows, or the image height) + in pixels. Images with row and column sizes of zero are + sometimes valid, and indicate that only the map information + is important. The product of the two values is the total + number of data items present. + +DEFINES: none + +______________________________________________________________________________ +unsigned long This unsigned long field specifies the length of any subrows +subrow_size in the image. This is useful when one wants pixel vectors + to represent 2D objects (images). The size of each pixel + "image" would be subrow_size (columns) by + num_data_bands/subrow_size (rows). This field may be + ignored except by routines that need the 2D interpretation. + +DEFINES: none + +______________________________________________________________________________ +unsigned long These two long fields indicate the location of a +startx, subimage in a parent image. The image is a sub image +starty; if startx and starty have values greater than zero. + startx and starty locate the upper left hand corner + of where the image was extracted. + This applies to 2D data that has implicit locations. + +DEFINES: IMVFF_NOTSUB + +______________________________________________________________________________ +float These two floats specify the actual pixel size in meters +pixsizx, at the time of digitization. This information is needed +pixsizy; to do true measurements and calculate true frequencies. + The ratio of these fields will give you the aspect ratio + of the digitized pixel. Most CCD camera's are not one-to-one. + These values may not have a meaning if the data is + IMVFF_LOC_EXPLICIT. + +DEFINES: none + +______________________________________________________________________________ +unsigned long This unsigned long field indicates +location_type; whether the image data has implicit or explicit locations. + + If the locations are implicit, the field location_dim must + be set to zero (0), and the location data will be empty. + + If the locations are explicit, the field location_dim + indicates the dimensionality of the space (1D, 2D and 3D + will be most common). The explicit location data is pointed + to by location, and is stored as bands of coordinates. + For example, if (location_dim = 2), implying 2D locations, + the location data would be stored; x1, x2, . . . , xn; + y1, y2, . . . , yn. + +DEFINES: IMVFF_LOC_IMPLICIT + IMVFF_LOC_EXPLICIT + +______________________________________________________________________________ +unsigned long This unsigned long indicates the dimensionality of the +location_dim location space of data. Zero-, one-, and two- + dimensionalities will be most common. If the location_type + is implicit, this field must be set to zero (0). If the + location_type is explicit, this field is set to the + dimensionality correctly describing the data. Remember that + 3-dimensional image data is represented with + location_dim = 2 (x & y); the third dimension (pixel intensity) + is stored in the imagedata. + +DEFINES: none + +______________________________________________________________________________ +unsigned long This unsigned long indicates the +num_of_images the number of images (not bands) pointed to by *imagedata. + +DEFINES: none + +______________________________________________________________________________ +unsigned long This unsigned long indicates the number of +num_data_bands bands per image or the dimensionality of vectors. + In some cases, it may be convenient to think of an + image pixel as a vector (when there is more than one band). + +DEFINES: none + +______________________________________________________________________________ +unsigned long This unsigned long field indicates the data storage type +data_storage_type; of the pixel or vector data. Currently, bit, byte, + short, integer, float, float complex, double, and double + complex data types are supported. The BIT storage type + stores the bits in packed unsigned chars and pads to + a byte; the order is Least Significant Bit (LSB) first. + +DEFINES: IMVFF_TYP_BIT + IMVFF_TYP_1_BYTE + IMVFF_TYP_2_BYTE + IMVFF_TYP_4_BYTE + IMVFF_TYP_FLOAT + IMVFF_TYP_COMPLEX + IMVFF_TYP_DOUBLE + IMVFF_TYP_DCOMPLEX + +______________________________________________________________________________ +unsigned long This unsigned long field contains the information that +data_encode_scheme specifies the encoding or compression method used for + storage of the data. Only the raw and compress encoding + schemes are supported in Khoros 1.0. + +DEFINES: IMVFF_DES_RAW + IMVFF_DES_COMPRESS + IMVFF_DES_RLE + IMVFF_DES_TRANSFORM + IMVFF_DES_CCITT + IMVFF_DES_ADPCM + IMVFF_DES_GENERIC + +______________________________________________________________________________ +unsigned long This unsigned long field specifies the type of mapping +map_scheme that should occur. The map is an array, where the data + pointed to by *imagedata is used as an index into rows + of the array. + + ONEPERBAND indicates that each data band has a map. CYCLE is + for when a single band is displayed by cycling through maps. + SHARED is for when there is only one map for all bands to + share. The GROUP mapping scheme is for future Khoros + development, and should not be used as of Khoros 1.0. + +DEFINES: IMVFF_MS_NONE + IMVFF_MS_ONEPERBAND + IMVFF_MS_CYCLE + IMVFF_MS_SHARED + IMVFF_MS_GROUP + +______________________________________________________________________________ +unsigned long This unsigned long field indicates the type +map_storage_type; of data in the map. Data in the map may be of type + char, short, integer, float, or complex. This is also + the resulting data type after a mapping has been done. + +DEFINES: IMVFF_MAPTYP_NONE + IMVFF_MAPTYP_1_BYTE + IMVFF_MAPTYP_2_BYTE + IMVFF_MAPTYP_4_BYTE + IMVFF_MAPTYP_FLOAT + IMVFF_MAPTYP_COMPLEX + +______________________________________________________________________________ +unsigned long These two unsigned long fields indicate the number of +map_row_size, rows in the 2D map and the number of columns in the map. +map_col_size; The maps are stored as a sequence of columns (stacked columns). + +DEFINES: none + +______________________________________________________________________________ +unsigned long This unsigned long field specifies the number of subrows +map_subrow_size; in a map. This is useful when using the output vector from + the map is a 2D image, rather than just a vector. + The size of the 2D image would be: map_subrow_size (columns) by + map_row_size/map_subrow_size (rows). This field may be ignored + except by routines that need the 2D interpretation. + +DEFINES: none + +______________________________________________________________________________ +unsigned long This unsigned long field specifies if the image data will be +map_enable; valid regardless of whether or not it has been sent through + a map. In some cases the mapping is optional + (IMVFF_MAP_OPTIONAL), while in others, the data must be mapped + in order for it to have a valid meaning (IMVFF_MAP_FORCE). + See the previous section on FILE FORMAT TERMINOLOGY for + a more detailed explanation of the map_enable field. + +DEFINES: IMVFF_MAP_OPTIONAL + IMVFF_MAP_FORCE + +______________________________________________________________________________ +unsigned long This unsigned long field specifies the number of +maps_per_cycle; maps that would constitute one cycle when the cycled map + scheme type is used. Of course, this field is only valid + when map_scheme is set to IMVFF_MS_CYCLE. + +DEFINES: none +______________________________________________________________________________ +unsigned long This unsigned long field indicates the color space or the +color_space_model; coordinate system being used to interpret the image bands. + + The color space model defines use the following convention: + + NTSC: National Television Systems Committee + CIE: Commission Internationale de L'Eclairage + UCS: Universal Chromaticity Scale + RGB: Red Component, Green Component, Blue Component + CMY: Cyan Component, Magenta Component, Yellow Component + YIQ: Luminance, I and Q represent chrominance + HSV: Hue, Saturation & Value + IHS: Intensity, Hue & Saturation + + + Most color map models only make sense when the map_scheme is + set to IMVFF_MS_ONEPERBAND and the map_row_size = 3 and the + num_data_bands = 1; alternatively, the map_scheme may be set + to IMVFF_MS_NONE and the map_row_size = 0, and the + num_data_bands = 3. The exceptions to this are: + + IMVFF_CM_NONE indicates that no color space model has been + assigned, + IMVFF_CM_GENERIC indicates that a color space is valid, but is + being defined by the user. + IMVFF_CM_genericRGB would imply that IMVFF_MS_ONEPERBAND is set + with map_row_size = 3 and num_data_bands = 1. genericRGB is + an RGB image but doesn't conform to a standard. + +DEFINES: IMVFF_CM_NONE + IMVFF_CM_ntscRGB + IMVFF_CM_ntscCMY + IMVFF_CM_ntscYIQ + IMVFF_CM_HSV + IMVFF_CM_HLS + IMVFF_CM_IHS + IMVFF_CM_cieRGB + IMVFF_CM_cieXYZ + IMVFF_CM_cieUVW + IMVFF_CM_cieucsUVW + IMVFF_CM_cieucsSOW + IMVFF_CM_cieucsLab + IMVFF_CM_cieucsLuv + IMVFF_CM_GENERIC + IMVFF_CM_genericRGB + +______________________________________________________________________________ +unsigned long Spare fields available for user defined fields. These +ispare1, fields are not supported except for reading and writing +ispare2; correctly with respect to machine dependencies. +float +fspare1, +fspare2; + +DEFINES: none + +______________________________________________________________________________ +reserve Reserve space is allocated so that the total length of the + header is 1024 bytes and so that more fields can be added. + +DEFINES: none + +______________________________________________________________________________ +char *maps; This is a pointer to the beginning of the map data. + When it is used, it must be cast to the proper data type. + +DEFINES: none + + +______________________________________________________________________________ +float *location This is a pointer to the beginning of the location data. + The dimensionality of the location data is given in the field + location_dim. (location_dim specifies the number of + bands of coordinates.) The number of coordinate values per + band is row_size*col_size. + +DEFINES: none + +______________________________________________________________________________ +char This is a pointer to the beginning of the image data. When it +*imagedata is used in an image processing routine it must be cast to the + proper data type. + +DEFINES: none + +______________________________________________________________________________ +*/ + +#include "iminternal.h" + +/* + * VIFF - Khoros Visualization Image File Format + * For information on these structures, how to use them, etc. please + * see imfmt.c. + */ +#ifdef __STDC__ +static int imViffRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); +static int imViffWriteRGBCLT (ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + TagTable *flagsTable, TagTable *tagTable); +static int imViffWriteRGBNOCLT( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + TagTable *flagsTable, TagTable *tagTable); +static int imViffWriteINDEX8CLT(ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + TagTable *flagsTable, TagTable *tagTable ); +static int imViffWriteINDEX8NOCLT( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + TagTable *flagsTable, TagTable *tagTable); +static int imViffWriteINDEX16CLT( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + TagTable *flagsTable, TagTable *tagTable); +static int imViffWriteINDEX16NOCLT( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + TagTable *flagsTable, TagTable *tagTable); +static int imViffWriteMONO( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + TagTable *flagsTable, TagTable *tagTable); +#else +static int imViffRead( ); +static int imViffWriteRGBCLT( ); +static int imViffWriteRGBNOCLT( ); +static int imViffWriteINDEX8CLT( ); +static int imViffWriteINDEX8NOCLT( ); +static int imViffWriteINDEX16CLT( ); +static int imViffWriteINDEX16NOCLT( ); +static int imViffWriteMONO( ); +#endif +static char *imViffNames[ ] = { "viff", "xv", NULL }; +static ImFileFormatReadMap imViffReadMap[ ] = +{ + /* in out */ + /* type,ch,dep, attr. VFB type attr. */ + { RGB,3,8, 0, IMVFBRGB, 0 }, + { RGB,3,8, C, IMVFBRGB, C }, + { IN,1,1, 0, IMVFBMONO, 0 }, + { IN,1,8, C, IMVFBINDEX8, C }, + { IN,1,8, 0, IMVFBINDEX8, 0 }, + { IN,1,16, C, IMVFBINDEX16, C }, + { IN,1,16, 0, IMVFBINDEX16, 0 }, + { -1, 0, -1, 0 }, + +}; +static ImFileFormatWriteMap imViffWriteMap[ ] = +{ + /* in out */ + /* VFB type, attr., type,ch,dep, attr., func */ + { IMVFBRGB, C, RGB,3,8, C, imViffWriteRGBCLT }, + { IMVFBRGB, 0, RGB,3,8, 0, imViffWriteRGBNOCLT }, + { IMVFBINDEX8, 0, IN,1,8, 0, imViffWriteINDEX8NOCLT}, { IMVFBINDEX8, C, IN,1,8, C, imViffWriteINDEX8CLT}, + { IMVFBINDEX16, C, IN,1,16, C, imViffWriteINDEX16CLT}, + { IMVFBINDEX16, 0, IN,1,16, 0, imViffWriteINDEX16NOCLT}, { IMVFBMONO, 0, IN,1,1, 0, imViffWriteMONO}, + { -1, 0, -1, 0, NULL }, +}; + +static ImFileMagic imFileViffMagic []= +{ + { 0, 0, NULL}, +}; + +ImFileFormat ImFileViffFormat = +{ + imViffNames, "Khoros Visualization image file", + "Khoros", + "1-bit monochrome, 8- and 16-bit color index, and 24-bit RGB color\n\ +mages, uncompressed.", + "1-bit monochrome, 8- and 16-bit color index, and 24-bit RGB color\n\ +mages, uncompressed.", + imFileViffMagic, + IMNOMULTI, IMNOPIPE, + IMMULTI, IMNOPIPE, + imViffRead, imViffReadMap, imViffWriteMap +}; + +#ifdef __STDC__ +static int imReadViff1Byte(int ioType, int fd, FILE *fp, ImVfb *vfb, int q, unsigned long h, unsigned long w, unsigned long c); +static int imReadViff2Byte(int ioType, int fd, FILE *fp, ImVfb *vfb, int q, unsigned long h, unsigned long w, unsigned long c); +static int imReadViffBit(int ioType, int fd, FILE *fp, ImVfb *vfb, int q, unsigned long h, unsigned long w, unsigned long c); +static int imViffWriteHeader( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable,unsigned long numDataBands, + unsigned long dataStorageType, unsigned long dataEncodeScheme, unsigned long mapScheme, + unsigned long colorSpaceModel, unsigned long mapRowSize ); +static int imViffWriteRGBData (int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable); +static int imViffWriteINDEX8Data (int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable); +static int imViffWriteINDEX16Data (int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable); +#else +static int imReadViff1Byte(); +static int imReadViff2Byte(); +static int imReadViffBit(); +static int imViffWriteHeader(); +static int imViffWriteRGBData(); +static int imViffWriteINDEX8Data(); +static int imViffWriteINDEX16Data(); +#endif + +/* definitions for version number, + char release; */ +#define IM_XV_IMAGE_VER_NUM 3 /* Version 3 (3.1) */ + + +/* definitions for release number, + char version; */ +#define IM_XV_IMAGE_REL_NUM 1 /* Release 1 */ + +/* definitions for subimage information, + long startx, starty; */ +#define IMVFF_NOTSUB ~0 /* a negative number indicates that + the image is not a subimage */ + +/* definitions for machine dependencies, + char machine_dep; */ +#define IMVFF_DEP_IEEEORDER 0x2 /* IEEE byte ordering */ +#define IMVFF_DEP_DECORDER 0x4 /* DEC (VAX) byte ordering */ +#define IMVFF_DEP_NSORDER 0x8 /* NS32000 byte ordering */ +#define IMVFF_DEP_CRAYORDER 0xA /* Cray byte size and ordering */ + +#define IMVFF_DEP_BIGENDIAN IMVFF_DEP_IEEEORDER +#define IMVFF_DEP_LITENDIAN IMVFF_DEP_NSORDER + + +/* definitions for data storage type, + unsigned long data_storage_type; */ +#define IMVFF_TYP_BIT 0 /* pixels are on or off (binary image)*/ + /* Note: This is an X11 XBitmap + with bits packed into a byte and + padded to a byte */ +#define IMVFF_TYP_1_BYTE 1 /* pixels are byte (unsigned char) */ +#define IMVFF_TYP_2_BYTE 2 /* pixels are two byte (short int) */ +#define IMVFF_TYP_4_BYTE 4 /* pixels are four byte (integer) */ +#define IMVFF_TYP_FLOAT 5 /* pixels are float (single precision)*/ +#define IMVFF_TYP_COMPLEX 6 /* pixels are complex float */ +#define IMVFF_TYP_DOUBLE 9 /* pixels are float (double precision)*/ + +#define IMVFF_TYP_DCOMPLEX 10 /* double complex */ + +/* definitions for data encoding scheme on disk - i.e. it may be + compressed using RLE, or uncompressed (RAW). + unsigned long data_encode_scheme; */ +#define IMVFF_DES_RAW 0 /* Raw - no compression */ +#define IMVFF_DES_COMPRESS 1 /* Compressed using ALZ */ +#define IMVFF_DES_RLE 2 /* Compressed using RLE */ +#define IMVFF_DES_TRANSFORM 3 /* Transform based compression */ +#define IMVFF_DES_CCITT 4 /* CCITT standard compression */ +#define IMVFF_DES_ADPCM 5 /* ADPCM compression */ +#define IMVFF_DES_GENERIC 6 /* User-specified compression */ + +/* definitions for map data or cells storage type, + unsigned long map_storage_type; */ +#define IMVFF_MAPTYP_NONE 0 /* No cell type is assigned */ +#define IMVFF_MAPTYP_1_BYTE 1 /* cells are byte (unsigned char) */ +#define IMVFF_MAPTYP_2_BYTE 2 /* cells are two byte (short int) */ +#define IMVFF_MAPTYP_4_BYTE 4 /* cells are four byte (integer) */ +#define IMVFF_MAPTYP_FLOAT 5 /* cells are float (single precision) */ +#define IMVFF_MAPTYP_COMPLEX 6 /* cells are complex FLOAT */ +#define IMVFF_MAPTYP_DOUBLE 7 /* cells are float (double precision) */ + +/* definitions for mapping schemes, + unsigned long map_scheme; */ +#define IMVFF_MS_NONE 0 /* No mapping is to be done, and no + maps are to be stored. */ +#define IMVFF_MS_ONEPERBAND 1 /* Each data band has its own map */ +#define IMVFF_MS_CYCLE 2 /* An array of maps is selected in order + by groups of maps_per_cycle, allowing + "rotating the color map" */ +#define IMVFF_MS_SHARED 3 /* All data band share the same map */ +#define IMVFF_MS_GROUP 4 /* All data bands are "grouped" + together to point into one map */ +/* definitions for enabling the map, + unsigned long map_enable; */ +#define IMVFF_MAP_OPTIONAL 1 /* The data is valid without being + sent thru the color map. If a + map is defined, the data may + optionally be sent thru it. */ +#define IMVFF_MAP_FORCE 2 /* The data MUST be sent thru the map + to be interpreted */ +#define IMVFF_CM_NONE 0 +#define IMVFF_CM_ntscRGB 1 +#define IMVFF_CM_ntscCMY 2 +#define IMVFF_CM_ntscYIQ 3 +#define IMVFF_CM_HSV 4 +#define IMVFF_CM_HLS 5 +#define IMVFF_CM_IHS 6 +#define IMVFF_CM_cieRGB 7 +#define IMVFF_CM_cieXYZ 8 +#define IMVFF_CM_cieUVW 9 +#define IMVFF_CM_cieucsUVW 10 +#define IMVFF_CM_cieucsSOW 11 +#define IMVFF_CM_cieucsLab 12 +#define IMVFF_CM_cieucsLuv 13 +#define IMVFF_CM_GENERIC 14 /* the color space is user defined */ +#define IMVFF_CM_genericRGB 15 /* an RGB image but not conforming + to any standard */ + +/* definitions for location type, + unsigned long location_type; */ +#define IMVFF_LOC_IMPLICIT 1 /* The location of image pixels + or vector data is given by using + the implied 2D array given by + row_size and col_size. */ +#define IMVFF_LOC_EXPLICIT 2 /* The location of the image pixels + or the vectors is explicit */ + +#define IMVIFF_HEADERSIZE 1024 + +/* TYPEDEF & STRUCTURE + * imViffHeaderInfo - VIFF file header information + * imViffTgaHeaderFields - VIFF file header fields for binary package + */ + +typedef struct imViffHeaderInfo +{ + /* Administrative or file management information */ + + char viff_identifier; /* a magic number that tells + the world that this is an + Khoros file */ + + char viff_file_type; /* tells if this file is a VIFF file */ + + char viff_release; /* release number */ + + char viff_version; /* version number */ + + char viff_machine_dep; /* indicates peculiarities of */ + /* machine architecture */ + + char viff_trash[3]; /* preserves word boundaries */ + /* groups of 4 bytes */ + + char viff_comment[512]; /* text for image commentary */ + +/* Things that specify the spatial properties of the image, pixel + organization, and data storage arrangement. */ + + unsigned long viff_row_size; /* length of row in pixels, + i.e. number of columns */ + + unsigned long viff_col_size; /* length of column in pixels, + i.e. number or rows */ + unsigned long viff_subrow_size; /* Length of subrows. + This is useful + when one wants pixel vectors to + represent 2D objects (images). + The size of each pixel "image" + would be subrow_size (columns) + by num_data_bands/subrow_size (rows). + This field may be ignored except + by routines that need the 2D + interpretation. */ + +/* The product of row_size and col_size is used to indicate + the number of locations when the location type is explicit, + the product also indicates the number of pixels in a band, + or the number of vectors. */ + + long viff_startx, viff_starty; /* subimage starting position (upper + left hand corner), negative indicates + that it is not a subimage */ + + float viff_pixsizx, viff_pixsizy; /* Actual size of pixel + at time of digitization in meters */ + + unsigned long viff_location_type; /* implied or explicit location + data (implied locations are + derived from row_size and + col_size */ + + unsigned long viff_location_dim; /* explicit locations can be of + any dimension */ + + unsigned long viff_num_of_images; /* number of images + pointed to by *imagedata, + do not confuse with number of + bands */ + + unsigned long viff_num_data_bands; /* Number of bands per data pixel, + or number of bands per image, or + dimension of vector data, or + number of elements in a vector */ + + unsigned long viff_data_storage_type; /*storage type for disk data */ + + unsigned long viff_data_encode_scheme; /*encoding scheme of diskdata*/ + +/* Things that determine how the mapping (if any) of data bands is + to be done to obtain the actual "image" or data. */ + + unsigned long viff_map_scheme; /* How mapping (if any) is to occur */ + + unsigned long viff_map_storage_type; /* Storage type of + cells in the maps */ + + unsigned long viff_map_row_size; /* number of columns in map array */ + + unsigned long viff_map_col_size; /* number of entries in map (rows) */ + + unsigned long viff_map_subrow_size;/*Length of subrows. This is useful + when using the output vector from + the map as a 2-D image, rather + than just a vector. The size of + the 2-D image would be: + map_subrow_size (columns) by + map_row_size/map_subrow_size + (rows). This field may be ignored + except by routines that need the 2D + interpretation */ + + unsigned long viff_map_enable; /* Tells if the disk data is valid + with or without being sent thru the + map. Some data MUST be mapped to be + valid. */ + + unsigned long viff_maps_per_cycle; /* number of maps to constitue + a "cycle" for IMVFF_MS_CYCLE */ + +/* Specification of the particular color model in use when working with a + color image. This just tells what the coordinate system and axis orientation + of the color space is. */ + + unsigned long viff_color_space_model; + +/* Extra fields for use by the user as needed. These are NOT SUPPORTED + in any way, except for being read and written correctly with respect + to machine dependencies. */ + + unsigned long viff_ispare1,viff_ispare2; /* Spare long ints */ + + float viff_fspare1,viff_fspare2; /* Spare floats */ + +/* Pointers to the actual data - these are valid only when in memory! */ + + char viff_reserve[IMVIFF_HEADERSIZE - (21*4) + - (520*1) + - (2*4) - (4*4) + - (4*1)]; + /* maximum header information is + 1024 bytes, what is not currently + used is saved in reserve */ + + char *viff_maps; /* a pointer to the maps, must be cast into + the proper type */ + + float *viff_location; /* a pointer to the location data (for + explicit locations, each location is + paired with data pointed to by + *imagedata, all locations are + in float */ + + char *viff_imagedata; /* a pointer to the input data (straight + off of disk, must be cast into the proper type */ + } imViffHeaderInfo; + +static BinField imViffHeaderFields[]= +{ + { UCHAR, 1, 1 }, /* viff_identifier */ + { UCHAR, 1, 1 }, /* viff_file_type */ + { UCHAR, 1, 1 }, /* viff_release */ + { UCHAR, 1, 1 }, /* viff_version */ + { UCHAR, 1, 1 }, /* viff_machine_dep */ + { UCHAR, 1, 3 }, /* viff_trash */ + { UCHAR, 1, 512 }, /* viff_comment */ + { ULONG, 4, 1 }, /* viff_row_size */ + { ULONG, 4, 1 }, /* viff_col_size */ + { ULONG, 4, 1 }, /* viff_subrow_size */ + { LONG, 4, 1 }, /* viff_startx */ + { LONG, 4, 1 }, /* viff_starty */ + { FLOAT, 4, 1 }, /* viff_pixsizx */ + { FLOAT, 4, 1 }, /* viff_pixsizy */ + { ULONG, 4, 1 }, /* viff_location_type */ + { ULONG, 4, 1 }, /* viff_location_dim */ + { ULONG, 4, 1 }, /* viff_num_of_images */ + { ULONG, 4, 1 }, /* viff_num_data_bands */ + { ULONG, 4, 1 }, /* viff_data_storage_type */ + { ULONG, 4, 1 }, /* viff_data_encode_scheme */ + { ULONG, 4, 1 }, /* viff_map_scheme */ + { ULONG, 4, 1 }, /* viff_map_storage_type */ + { ULONG, 4, 1 }, /* viff_map_row_size */ + { ULONG, 4, 1 }, /* viff_map_col_size */ + { ULONG, 4, 1 }, /* viff_map_subrow_size */ + { ULONG, 4, 1 }, /* viff_map_enable */ + { ULONG, 4, 1 }, /* viff_maps_per_cycle */ + { ULONG, 4, 1 }, /* viff_color_space_model */ + { ULONG, 4, 1 }, /* viff_ispare1 */ + { ULONG, 4, 1 }, /* viff_ipsare2 */ + { FLOAT, 4, 1 }, /* viff_fpsare1 */ + { FLOAT, 4, 1 }, /* viff_fpsare2 */ + { CHAR, 1, (IMVIFF_HEADERSIZE - (21 * 4) + - (520*1) + - (2 *4) - (4*4) + - (4))},/*viff_reserve*/ + { CHAR , 4, 1 }, /* viff_maps */ + { FLOAT , 4, 1 }, /* viff_location */ + { CHAR , 4, 1 }, /* viff_imagedata */ + { 0, 0, 0} +}; + + +static int /* returns number of tags read in */ +#ifdef __STDC__ +imViffRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) +#else +imViffRead( ioType, fd, fp, flagsTable, tagTable ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag table to add to */ +#endif +{ + unsigned char *pBuffer; /* buffer pointer */ + ImVfbPtr pPixel; /* VFB pixel pointer */ + ImVfb *vfb; /* Read in image */ + ImVfbPtr pptr; /* Pixel pointer */ + ImClt *clt; /* New clt */ + ImCltPtr pColor; /* CLT entry pointer */ + imViffHeaderInfo header; /* Viff header info pointer */ + unsigned char *bufferuc; /* Run buffer(type uchar) */ + unsigned char *buffer; /* Run buffer(type uchar) */ + long *bufferl; /* Run buffer (type long) */ + int i,x,y; /* Counter */ + int colortable; /* color map flag */ + unsigned long c,n,m,h,w,z,q; /* convenient short names */ + char message[100]; /* Tmp error message text */ + char *viffName; /* storgae foe comment */ + + colortable=0; + BinByteOrder (BINMBF); + if (ImBinReadStruct (ioType, fd, fp, &header, imViffHeaderFields)==-1) + { + ImReturnBinError(); + } + + switch ( header.viff_machine_dep ) + { + case IMVFF_DEP_NSORDER: + case IMVFF_DEP_DECORDER: + BinByteOrder (BINLBF); + ImSeek (ioType,fd,fp,0,0); + if (ImBinReadStruct (ioType, fd, fp, &header, imViffHeaderFields)==-1) + { + ImReturnBinError(); + } + break; + case IMVFF_DEP_IEEEORDER: + case IMVFF_DEP_CRAYORDER: + break; + default: + ImErrorFatal( "Unknown header byte order\n", -1, IMESYNTAX ); + } + + + + +/* Below is error checking to make sure that the Viff file we are reading in + is the type of Viff file that we wish to translate. Right now there are + a myriad of permutaions of a Viff file only a small subsection that we + actually support. More will be added as requested. */ + + if (header.viff_file_type!=1) { + sprintf (message, "Invalid Viff file type, may not be a Viff image.\n"); + ImErrorFatal (message, -1, IMESYNTAX); + } + + if (header.viff_machine_dep==IMVFF_DEP_NSORDER) { + } + + if ((header.viff_machine_dep!=IMVFF_DEP_IEEEORDER) & + (header.viff_machine_dep!=IMVFF_DEP_NSORDER)) + { + sprintf (message, "Cannot read non-IEEE ordering.\n"); + ImErrorFatal (message, -1, IMESYNTAX); + } + + if (header.viff_location_type!=IMVFF_LOC_IMPLICIT) { + sprintf (message, "Can only read implicit images.\n"); + ImErrorFatal (message, -1, IMESYNTAX); + } + + if ((header.viff_data_storage_type==IMVFF_TYP_DCOMPLEX) || + (header.viff_data_storage_type==IMVFF_TYP_4_BYTE) || + (header.viff_data_storage_type==IMVFF_TYP_DOUBLE) || + (header.viff_data_storage_type==IMVFF_TYP_COMPLEX) || + (header.viff_data_storage_type==IMVFF_TYP_FLOAT)) { + sprintf (message, "Cannot read data storage type %d images.\n", + header.viff_data_storage_type); + ImErrorFatal (message, -1, IMESYNTAX); + } + + if ((header.viff_data_storage_type==IMVFF_TYP_2_BYTE) & + (header.viff_num_data_bands==3)) { + sprintf (message, "Cannot read 2 bytes per pixel and a 3 band images.\n"); + ImErrorFatal (message, -1, IMESYNTAX); + } + + if ((header.viff_data_encode_scheme!=IMVFF_DES_RAW) & + (header.viff_data_encode_scheme!=IMVFF_DES_RLE)) { + sprintf (message, "Cannot read encoding scheme %d images.\n", + header.viff_data_encode_scheme); + ImErrorFatal (message, -1, IMESYNTAX); + } + + if ((header.viff_map_storage_type!=IMVFF_MAPTYP_NONE) & + (header.viff_map_storage_type!=IMVFF_MAPTYP_1_BYTE)) { + sprintf (message, "Cannot read map storage type %d images.\n", + header.viff_map_storage_type); + ImErrorFatal (message, -1, IMESYNTAX); + } + + if ((header.viff_color_space_model!=IMVFF_CM_genericRGB) & + (header.viff_color_space_model!=IMVFF_CM_NONE)) { + sprintf (message, "Cannot read color space scheme %d images.\n", + header.viff_color_space_model); + ImErrorFatal (message, -1, IMESYNTAX); + } + + if (header.viff_num_of_images!=1) { + sprintf (message, "%d images in file. Can only read single image file.\n", + header.viff_num_of_images); + ImErrorFatal (message, -1, IMESYNTAX); + } + + + if ((header.viff_num_data_bands!=1) & (header.viff_num_data_bands!=3)){ + sprintf (message, "There are %d data bands in image. Can only read 1 or 3. \n",header.viff_num_data_bands); + ImErrorFatal (message, -1, IMESYNTAX); + } + + + ImInfo ("Image Name", header.viff_comment); + sprintf (message, "%d",header.viff_release); + ImInfo ("Release",message); + + sprintf (message, "%d",header.viff_version); + ImInfo ("Version",message); + + if (header.viff_machine_dep==IMVFF_DEP_IEEEORDER) + sprintf (message, "Most Significant Byte First"); + else sprintf (message, "Least Significant Byte First"); + ImInfo ("Byte Order",message); + + sprintf (message, "%d",header.viff_num_data_bands); + ImInfo ("Number of Data Bands",message); + + sprintf (message, "%d x %d",header.viff_row_size, header.viff_col_size); ImInfo ("Resolution",message); + + if (header.viff_data_storage_type==IMVFF_TYP_BIT) + sprintf (message, "1-bit Monochrome"); + + if (header.viff_data_storage_type==IMVFF_TYP_1_BYTE) + { + if (header.viff_num_data_bands==1) + sprintf (message, "%d-bit Color Indexed",header.viff_num_data_bands*8); + else sprintf (message, "%d-bit RGB",header.viff_num_data_bands*8); + } + + if (header.viff_data_storage_type==IMVFF_TYP_2_BYTE) + { + if (header.viff_num_data_bands==1) + sprintf (message, "%d-bit Color Indexed",header.viff_num_data_bands*16); + else sprintf (message, "%d-bit RGB",header.viff_num_data_bands*16); + } + ImInfo ("Type",message); + + + + w = header.viff_row_size; /* get the width of the image */ + h = header.viff_col_size; /* get the height of the image */ + c = header.viff_data_encode_scheme; /* get the encode type */ + + /* Allocate a VFB of the required size and type */ + if (header.viff_num_data_bands==3) /* RGB */ + { + if ( (vfb=ImVfbAlloc (w,h,IMVFBRGB)) == IMVFBNULL) + ImErrorFatal (ImQError(), -1, IMEMALLOC); + } + else if (header.viff_data_storage_type==IMVFF_TYP_1_BYTE) + { /* index8 */ + if ( (vfb=ImVfbAlloc (w,h,IMVFBINDEX8)) == IMVFBNULL) + ImErrorFatal (ImQError(), -1, IMEMALLOC); + } + else if ( header.viff_data_storage_type == IMVFF_TYP_BIT) + { /* monochrome image */ + if ( (vfb=ImVfbAlloc (w,h,IMVFBMONO)) == IMVFBNULL) + ImErrorFatal (ImQError(), -1, IMEMALLOC); + } + else if (header.viff_data_storage_type==IMVFF_TYP_2_BYTE) + { /* index16 */ + if ( (vfb=ImVfbAlloc (w,h,IMVFBINDEX16)) == IMVFBNULL) + ImErrorFatal (ImQError(), -1, IMEMALLOC); + } + + + if (header.viff_map_scheme) + { + colortable=1; + n= header.viff_map_col_size; + + sprintf (message, "%d Entries",header.viff_map_col_size); + ImInfo ("Color Table",message); + + if ( (m=header.viff_map_storage_type)!=IMVFF_MAPTYP_1_BYTE) + { + sprintf (message, "Map storage type %f invalid", + header.viff_map_storage_type); + ImErrorFatal (ImQError(), -1, IMEMALLOC); + } + + if ((clt = ImCltAlloc (n)) == IMCLTNULL) + ImErrorFatal (ImQError(), -1, IMEMALLOC); + + ImMalloc ( buffer, unsigned char *, sizeof(unsigned char)*n*3); + pColor = ImCltQFirst (clt); + + if (header.viff_map_row_size==3) + { + if (ImBinRead (ioType,fd,fp,buffer, UCHAR, 1, n*3)== -1) + { + free ( (char *)buffer); + ImReturnBinError(); + } + for (i= 0; i1) + { + ImMalloc (viffName, char *, strlen (header.viff_comment)+1); + strcpy(viffName,header.viff_comment); + TagTableAppend (tagTable,TagEntryAlloc("image name",POINTER,&viffName)); } + + /* Attach (set) the vfb's color lookup table if one */ + if (colortable) ImVfbSClt(vfb, clt); + + /* Attach vfb to tag table */ + TagTableAppend (tagTable, TagEntryAlloc ("image vfb", POINTER, &vfb)); + return(1); +} + + +/* + * FUNCTION + * imReadViff1Byte - read a file with 1 byte per index that could have + * 1 or 3 bytes of info per pixel. + * 1 byte per pixel info is index8 + * 3 bytes per pixel info is RGB + * + */ +static int /* Returns 1 on successful read */ +#ifdef __STDC__ +imReadViff1Byte (int ioType, int fd, FILE *fp, ImVfb *vfb, int q, unsigned long h, unsigned long w, unsigned long c) +#else +imReadViff1Byte (ioType,fd,fp,vfb, q,h,w,c) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + ImVfb *vfb; /* Read in Image */ + int q; /* How many bits per pixel */ + unsigned long h; /* Height of image */ + unsigned long w; /* width of image */ + unsigned long c; /* compression type */ +#endif +{ + long *bufferl; /* Run buffer (type long) */ + long *pBufferl; /* bufferl pointer */ + ImVfbPtr pPixel; /*VFB pixel pointer */ + int x,y,z; /* Counters */ + unsigned char *buffer; /* Run buffer (type uchar) */ + unsigned char *pBuffer; /* buffer pointer */ + int total; /* Counter for number of bytes encode */ + int count; /* Number of bytes to decode */ + unsigned char rle; /* wether something is rle or not */ + int field; /* red,green, or blue color */ + unsigned char leadByte; /* Read in the lead byte */ + int expred; /* The five red bits */ + int expgreen; /* The five green bits */ + int expblue; /* The five blue bits */ + + pPixel = ImVfbQFirst(vfb); + if (q==1) /* one byte per index */ + { + if (c==IMVFF_DES_RAW) + { + ImMalloc (buffer, unsigned char *, w * sizeof(unsigned char) ); + pPixel = ImVfbQFirst (vfb); + for (y =0; y0); + free ( (char *)buffer); + } + } + + + else + { + if (c==IMVFF_DES_RAW) + { + + ImMalloc (buffer, unsigned char *, w * sizeof(unsigned char) ); + + pPixel = ImVfbQFirst (vfb); + for (y =0; y0); + free ( (char *)buffer); + + } + } + return(1); +} +/* + * FUNCTION + * imReadViff2Byte - read a file with 4 byte per index that could have + * 1 or 3 bytes of info per pixel. + * 1 byte per pixel info is index8 + * 3 bytes per pixel info is RGB + * + */ +static int /* Returns 1 on successful read */ +#ifdef __STDC__ +imReadViff2Byte (int ioType, int fd, FILE *fp, ImVfb *vfb, int q, unsigned long h, unsigned long w, unsigned long c) +#else +imReadViff2Byte (ioType,fd,fp,vfb, q,h,w,c) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + ImVfb *vfb; /* Read in Image */ + int q; /* How many bits per pixel */ + unsigned long h; /* Height of image */ + unsigned long w; /* width of image */ + unsigned long c; /* compression type */ +#endif +{ + ImVfbPtr pPixel; /*VFB pixel pointer */ + int x,y,z; /* Counters */ + sdsc_uint16 *buffer; /* Run buffer (type unsigned char) */ + sdsc_uint16 *pBuffer; /* buffer pointer */ + + BinByteOrder (BINLBF); + pPixel = ImVfbQFirst(vfb); + if (q==1) /* one byte per index */ + { + if (c==IMVFF_DES_RAW) + { + ImMalloc (buffer, sdsc_uint16 *, w* sizeof(sdsc_uint16) ); + for (y =0; y>=1; + count++; + } + } + } + } + free ( (char *) buffer); + break; + } + return(1); +} +/* FUNCTION + * imViffWriteMONO - write a Viff monochrome file + * + * DESCRIPTION + * + */ +static int +#ifdef __STDC__ +imViffWriteMONO (ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable) +#else +imViffWriteMONO (pMap, ioType, fd, fp, flagsTable, tagTable) + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag table to read from */ +#endif +{ + ImVfb *vfb; /* Read in image */ + ImVfbPtr pptr; /* Pixel pointer */ + unsigned char *buffer; /* line buffer */ + unsigned char *pBuffer; /* current location in buffer */ + int w,h,n,x,y,z,i,j; /* Counter */ + TagEntry *tagEntry; /* Tmp table entry */ + unsigned char temp; /* byte to pack with bits */ + int count; /* count of number of bits */ + unsigned long numDataBands; /* how many data bands it is */ + unsigned long dataStorageType; /* storage type of disk data */ + unsigned long dataEncodeScheme; /* encodingscheme of disk data */ + unsigned long mapScheme; /* how mapping is to happen */ + unsigned long colorSpaceModel; /* how mapping is to happen */ + unsigned long mapRowSize; /* wether a RGB or greyscale map */ + int regular; + unsigned char pixel; + + + BinByteOrder (BINLBF); + mapRowSize =0; + numDataBands =1; + dataStorageType = IMVFF_TYP_BIT; + dataEncodeScheme = IMVFF_DES_RAW; + mapScheme = IMVFF_MS_NONE; + colorSpaceModel = IMVFF_CM_NONE; + + imViffWriteHeader (ioType, fd, fp, flagsTable, tagTable, + numDataBands, dataStorageType, dataEncodeScheme, mapScheme, + colorSpaceModel,mapRowSize); + + TagEntryQValue (TagTableQDirect (tagTable, "image vfb", 0), &vfb); + w = ImVfbQWidth(vfb); + h = ImVfbQHeight(vfb); + + /* allocate buffer space for (width*length*3) scan lines */ + + regular = ((w+7)/8); + ImMalloc (buffer, unsigned char *, regular); + pptr = ImVfbQFirst (vfb); + for (y=0;y>24) & 0xFF ); + ImVfbSRed( vfb, pPixel, ((*pBuffer)>>16) & 0xFF ); + ImVfbSGreen( vfb, pPixel, ((*pBuffer)>>8) & 0xFF ); + ImVfbSBlue( vfb, pPixel, (*pBuffer) & 0xFF ); + ImVfbSInc( vfb, pPixel ); + } + } + free( (char *)buffer ); + + TagTableAppend( tagTable, TagEntryAlloc( "image vfb", POINTER, &vfb ) ); + + return ( 1 ); +} + + + + + +/* + * FUNCTION + * imXWrite - write a Stardent AVS X file + * + * DESCRIPTION + * That VFB is queried, and the X file header written out. + * The VFB data is then copied to the file. + */ + +static int /* Returns # of tags used */ +#ifdef __STDC__ +imXWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) +#else +imXWrite( pMap, ioType, fd, fp, flagsTable, tagTable ) + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag table to read from */ +#endif +{ + ImVfb *vfb; /* Read in image */ + ImVfbPtr pPixel; /* Pixel pointer */ + sdsc_uint32 *buffer; /* Run buffer */ + sdsc_uint32 *pBuffer; /* Run buffer pointer */ + int width, height; /* Image dimensions */ + int i; /* Counter */ + char message[200]; /* contains information for ImInfo */ + + + /* + * Write out the image size. + */ + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + width = ImVfbQWidth( vfb ); + height = ImVfbQHeight( vfb ); + BinByteOrder( BINMBF ); + if ( ImBinWrite( ioType, fd, fp, &width, INT, 4, 1 ) == -1 ) + { + ImReturnBinError( ); + } + if ( ImBinWrite( ioType, fd, fp, &height, INT, 4, 1 ) == -1 ) + { + ImReturnBinError( ); + } + + + sprintf (message, "Most Significant Byte First"); + ImInfo ("Byte Order",message); + sprintf (message, "%d x %d", width, height); + ImInfo ("Resolution",message); + ImInfo ("Type","24-bit RGB" ); + ImInfo ("Alpha Channel","8-bit"); + + /* + * Copy the image to the file. If there's an alpha channel, add it. + */ + pPixel = ImVfbQFirst( vfb ); + ImMalloc( buffer, sdsc_uint32 *, sizeof( sdsc_uint32 ) * width ); + if ( ImVfbQFields( vfb ) & IMVFBALPHA ) + { + while ( height-- ) + { + pBuffer = buffer; + for ( i = 0; i < width; i++, pBuffer++ ) + { + *pBuffer = + (((ImVfbQAlpha(vfb,pPixel))&0xFF)<<24) | + (((ImVfbQRed(vfb,pPixel))&0xFF)<<16)| + (((ImVfbQGreen(vfb,pPixel))&0xFF)<<8)| + ((ImVfbQBlue(vfb,pPixel))&0xFF); + ImVfbSInc( vfb, pPixel ); + } + + if ( ImBinWrite( ioType, fd, fp, buffer, UINT32, 4, width ) == -1 ) + { + free( (char *)buffer ); + ImReturnBinError( ); + } + } + free( (char *)buffer ); + return ( 1 ); + } + + while ( height-- ) + { + pBuffer = buffer; + for ( i = 0; i < width; i++, pBuffer++ ) + { + *pBuffer = + (((ImVfbQRed(vfb,pPixel))&0xFF)<<16) | + (((ImVfbQGreen(vfb,pPixel))&0xFF)<<8) | + ((ImVfbQBlue(vfb,pPixel))&0xFF); + ImVfbSInc( vfb, pPixel ); + } + + if ( ImBinWrite( ioType, fd, fp, buffer, UINT32, 4, width)== -1) + { + free( (char *)buffer ); + ImReturnBinError( ); + } + } + free( (char *)buffer ); + return ( 1 ); +} diff --git a/utils/roq2/libim/imxbm.c b/utils/roq2/libim/imxbm.c new file mode 100644 index 0000000..4a43a31 --- /dev/null +++ b/utils/roq2/libim/imxbm.c @@ -0,0 +1,1294 @@ +/** + + ** $Header: /roq/libim/imxbm.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/imxbm.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** imxbm.c - X11 bitmap file I/O + + ** + + ** PROJECT + + ** libim - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** imxbm.c contains routines to read and write Sun Icon files for + + ** the image manipulation library. Raster data read in is stored + + ** in a VFB and optional CLT in a tag list. Raster data written + + ** out is taken from a tag list. + + ** + + ** PUBLIC CONTENTS + + ** d =defined constant + + ** f =function + + ** m =defined macro + + ** t =typedef/struct/union + + ** v =variable + + ** ? =other + + ** none + + ** + + ** PRIVATE CONTENTS + + ** + + ** imXbmRead f read a X11 Bitmap file + + ** imXbmWrite f write a X11 Bitmap file + + ** + + ** + + ** HISTORY + + ** $Log: /roq/libim/imxbm.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.15 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.14 1995/05/16 22:12:25 bduggan + + ** made HotSpot static (since its address is used in the tagtable) + + ** + + ** Revision 1.13 1995/04/03 21:41:29 bduggan + + ** took out #ifdef NEWMAGIC + + ** + + ** Revision 1.12 1995/01/10 23:50:58 bduggan + + ** made read/write routines static + + ** put in IMMULTI, IMPIPE instead of TRUE/FALSE + + ** + + ** Revision 1.11 94/10/03 11:31:13 nadeau + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Rearranged magic number structures for format handlers. + + ** Made format handler routines static (i.e., local to file). + + ** Updated comments, adding format descriptions and references. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.10 92/11/23 18:43:20 nadeau + + ** Removed use of IMINFOMSG. + + ** + + ** Revision 1.9 92/11/04 12:10:54 groening + + ** put ImFIleFormat info and magic number info + + ** from imfmt.c into this file. + + ** + + ** Revision 1.8 92/09/29 17:56:55 vle + + ** Added ImInfo messages. + + ** + + ** Revision 1.7 92/09/17 13:58:22 vle + + ** Moved a misplaced line of code. + + ** + + ** Revision 1.6 92/09/01 20:16:05 vle + + ** Updated copyright notice. Added code to read/write hotspots, instead + + ** of just skipping them. + + ** + + ** Revision 1.5 91/10/04 08:52:26 nadeau + + ** Fixed strcpy typo. + + ** + + ** Revision 1.4 91/10/03 09:25:08 nadeau + + ** Minor comment updating. + + ** + + ** Revision 1.3 91/02/20 12:44:37 nadeau + + ** Added hotspot read handling. + + ** + + ** Revision 1.2 91/02/12 10:48:15 nadeau + + ** Removed the tag table checking and VFB conversion now + + ** handled by ImFileRead and ImFileWrite. Changed grayscale + + ** to mono support. + + ** + + ** Revision 1.1 91/01/30 18:18:31 nadeau + + ** Initial revision + + **/ + + + +#include + +#include "iminternal.h" + + + + + +/** + + ** FORMAT + + ** xbm - X11 Bitmap + + ** + + ** AKA + + ** bm + + ** + + ** FORMAT REFERENCES + + ** Graphics File Formats, David C. Kay, John R. Levine + + ** + + ** + + ** CODE CREDITS + + ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1991. + + ** + + ** DESCRIPTION + + ** X11 bitmaps are simple C code to declare and initialize a character + + ** array with bits for an X11 cursor, icon, or glyph of some other sort. + + ** Preceding the array declaration are a few #define-ed constants to + + ** give the image width and height, and optionally a cursor's hot spot + + ** location. + + ** + + ** #define name_width xxx + + ** #define name_height yyy + + ** #define name_x_hot xhot + + ** #define name_y_hot yhot + + ** static char name_bits[] = { + + ** ... ASCII hex bytes ... + + ** }; + + ** + + ** xxx is the image width, in pixels. + + ** + + ** yyy is the image height, in pixels. + + ** + + ** xhot and yhot are the hot spot location. These two lines are + + ** optional. imxbm.c ignores the hot spot. + + ** + + ** name is the name of the glyph. imxbm.c write code generates the + + ** name based upon the outgoing file name given in the flags table. + + **/ + + + +/* + + * XBM - MIT X11 Window System BitMap + + * For information on these structures, how to use them, etc. please + + * see imfmt.c. + + */ + +#ifdef __STDC__ + +static int imXbmRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imXbmWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, + + TagTable *tagTable ); + +#else + +static int imXbmRead( ); + +static int imXbmWrite( ); + +#endif + + + +static char *imXbmNames[ ] = { "xbm", "bm", NULL }; + +static ImFileFormatReadMap imXbmReadMap[ ] = + +{ + + /* in out */ + + /* type,ch,dep, attr. VFB type attr. */ + + { IN,1,1, 0, IMVFBMONO, 0 }, + + { -1, 0, -1, 0 }, + +}; + +static ImFileFormatWriteMap imXbmWriteMap[ ] = + +{ + + /* in out */ + + /* VFB type, attr., type,ch,dep, attr., func */ + + { IMVFBMONO, 0, IN,1,1, 0, imXbmWrite }, + + { -1, 0, -1, 0, NULL }, + +}; + + + +static ImFileMagic imFileXbmMagic []= + +{ + + { 0, 0, NULL}, + +}; + + + +ImFileFormat ImFileXbmFormat = + +{ + + imXbmNames, "X11 bitmap file", + + "X Consortium / MIT", + + "1-bit ASCII bitmap files.", + + "1-bit ASCII bitmap files.", + + imFileXbmMagic, + + IMNOMULTI, IMPIPE, + + IMNOMULTI, IMPIPE, + + imXbmRead, imXbmReadMap, imXbmWriteMap + +}; + + + + + + + + + +/* + + * FUNCTION + + * imXbmRead - read an X11 bitmap file + + * + + * DESCRIPTION + + * The file is read and it's mono image written to a new mono VFB. + + */ + + + +static int /* Returns # tags read in */ + +#ifdef __STDC__ + +imXbmRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imXbmRead( ioType, fd, fp, flagsTable, tagTable ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + +#endif + +{ + + int xdim; /* # columns */ + + int ydim; /* # rows */ + + int xdim2; /* # columns (nearest 8bit bndry)*/ + + int xdimExtra; /* # columns mod 8 */ + + int xhot = 0; /* hotspot x coordinate */ + + int yhot = 0; /* hotspot y coordinate */ + + int hotspotflag = FALSE; /* TRUE if there's a + + hotspot */ + + static ImHotSpot hotspot; /* Cursor hotspot */ + + ImHotSpotPtr hotspotptr; /* Cursor hotspot pointer */ + + ImVfb *vfb; /* Read in image */ + + ImVfbPtr pPixel; /* Pixel pointer */ + + int c; /* Character holder */ + + int pixel; /* 8-bits of pixels */ + + int i, j, k; /* Counters */ + + char message[1024]; /* Error message holder */ + + + + + + /* + + * Read in the header. + + */ + + if ( ioType & IMFILEIOFD ) + + { + + /* + + * Given file descriptor. More convenient to deal with + + * a file pointer for this format. + + */ + + if ( (fp = fdopen( fd, "r" )) == NULL ) + + { + + ImErrNo = IMESYS; + + return ( -1 ); + + } + + } + + + + /* Read in width: #define name_width xdim */ + + if ( fscanf( fp, "%*s %*s %d", &xdim ) != 1 ) + + { + + ImErrorFatal( "Syntax error in giving image width", -1, IMESYNTAX ); + + } + + for ( c = fgetc( fp ); c != EOF && c != '\n'; c = fgetc( fp ) ) + + ; + + if ( c == EOF ) + + { + + ImErrorFatal( "Syntax error in giving image width", + + -1, IMESYNTAX ); + + } + + + + /* Read in height: #define name_height ydim */ + + if ( fscanf( fp, "%*s %*s %d", &ydim ) != 1 ) + + { + + ImErrorFatal( "Syntax error in giving image height", -1, IMESYNTAX ); + + } + + for ( c = fgetc( fp ); c != EOF && c != '\n'; c = fgetc( fp ) ) + + ; + + if ( c == EOF ) + + { + + ImErrorFatal( "Syntax error in giving image height", + + -1, IMESYNTAX ); + + } + + + + sprintf( message, "%d x %d", xdim, ydim ); + + ImInfo( "Resolution", message ); + + ImInfo( "Type", "1-bit Monochrome" ); + + + + /* Read in x_hot (optional): #define name_x_hot xhot */ + + if ( (c = fgetc( fp )) == '#' ) + + { + + if ( fscanf( fp, "%*s %*s %d", &xhot ) != 1 ) + + { + + ImErrorFatal( "Syntax error in giving image hotspot x", -1, + + IMESYNTAX ); + + } + + for ( c = fgetc( fp ); c != EOF && c != '\n'; c = fgetc( fp ) ) + + ; + + if ( c == EOF ) + + { + + ImErrorFatal( "Syntax error in giving image hotspot x", + + -1, IMESYNTAX ); + + } + + hotspotflag = TRUE; + + } + + + + /* Read in y_hot (optional): #define name_y_hot xhot */ + + if ( (c = fgetc( fp )) == '#' ) + + { + + if ( fscanf( fp, "%*s %*s %d", &yhot ) != 1 ) + + { + + ImErrorFatal( "Syntax error in giving image hotspot y", -1, + + IMESYNTAX ); + + } + + for ( c = fgetc( fp ); c != EOF && c != '\n'; c = fgetc( fp ) ) + + ; + + if ( c == EOF ) + + { + + ImErrorFatal( "Syntax error in giving image hotspot y", + + -1, IMESYNTAX ); + + } + + hotspotflag = TRUE; + + } + + + + /* + + * Store hotspot in TagTable, if there's at least one coordinate in the file + + */ + + if( hotspotflag == TRUE ) + + { + + hotspotptr = &hotspot; + + ImHotSpotSX( hotspotptr, xhot ); + + ImHotSpotSY( hotspotptr, yhot ); + + TagTableAppend( tagTable, TagEntryAlloc( "image hot spot", + + POINTER, &hotspotptr ) ); + + + + sprintf( message, "%d, %d", xhot, yhot ); + + ImInfo( "Hot Spot", message ); + + } + + else + + { + + ImInfo( "Hot Spot", "none" ); + + } + + + + /* + + * Skip: + + * static char name_bits[] = { + + */ + + i = 1; + + while ( i-- ) + + { + + for ( c = fgetc( fp ); c != EOF && c != '\n'; c = fgetc( fp ) ) + + ; + + if ( c == EOF ) + + { + + if ( i == 0 ) + + ImErrorFatal( "Syntax error in bits array declaration", + + -1, IMESYNTAX ); + + ImErrorFatal( "Syntax error in giving hot spot location", + + -1, IMESYNTAX ); + + } + + } + + + + + + /* + + * Allocate a VFB of the required size. + + */ + + if ( (vfb = ImVfbAlloc( xdim, ydim, IMVFBMONO )) == IMVFBNULL ) + + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + + + + + /* + + * Scan the file, reading in each hex character value holding + + * 8 bits. Unpack the bits into gray-scale values (0 = black, + + * 255 = white). + + */ + + xdimExtra = xdim & 0x7; + + xdim2 = xdim - xdimExtra; + + + + + + pPixel = ImVfbQFirst( vfb ); + + for ( i = 0; i < ydim; i++ ) + + { + + for ( j = 0; j < xdim2; j += 8 ) + + { + + /* Skip up to the 'x' in hex number. */ + + for ( c = fgetc( fp ); c != EOF && c != 'x'; ) + + c = fgetc( fp ); + + if ( c == EOF ) + + { + + sprintf( message, + + "Unexpected EOF in bits array element %d", + + i * (xdim2/8 + (xdimExtra+7)/8) + j/8 ); + + ImErrorFatal( message, -1, IMESYNTAX ); + + } + + + + /* Read in the pixel value. */ + + if ( fscanf( fp, "%x", &pixel ) != 1 ) + + { + + sprintf( message, + + "Syntax error in bits array element %d", + + i * (xdim2/8 + (xdimExtra+7)/8) + j/8 ); + + ImErrorFatal( message, -1, IMESYNTAX ); + + } + + for( k = 0; k < 8; k++ ) + + { + + ImVfbSMono( vfb, pPixel, (pixel&1) ); + + ImVfbSInc( vfb, pPixel ); + + pixel >>= 1; + + } + + } + + if ( xdimExtra == 0 ) + + continue; + + + + /* Skip up to the 'x' in hex number. */ + + for ( c = fgetc( fp ); c != EOF && c != 'x'; ) + + c = fgetc( fp ); + + if ( c == EOF ) + + { + + sprintf( message, + + "Unexpected EOF in bits array element %d", + + i * (xdim2/8 + (xdimExtra+7)/8) + j/8 ); + + ImErrorFatal( message, -1, IMESYNTAX ); + + } + + if ( fscanf( fp, "%x", &pixel ) != 1 ) + + { + + sprintf( message, + + "Syntax error in bits array element %d", + + i * (xdim2/8 + (xdimExtra+7)/8) + xdim2 ); + + ImErrorFatal( message, -1, IMESYNTAX ); + + } + + for( k = 0; k < xdimExtra; k++ ) + + { + + ImVfbSMono( vfb, pPixel, (pixel&1) ); + + ImVfbSInc( vfb, pPixel ); + + pixel >>= 1; + + } + + } + + + + + + /* + + * Add the VFB to the tagTable. + + */ + + TagTableAppend( tagTable, TagEntryAlloc( "image vfb", POINTER, &vfb ) ); + + + + return ( 1 ); + +} + + + + + + + + + + + +/* + + * FUNCTION + + * imXbmWrite - write a X11 bitmap file + + * + + * DESCRIPTION + + * The X11 bitmap header and image content are written out. + + */ + + + +static int /* Returns # of entries used */ + +#ifdef __STDC__ + +imXbmWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imXbmWrite( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* Type of I/O to perform */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Table of info to write */ + +#endif + +{ + + int xdim; /* # columns */ + + int xdim2; /* # columns (nearest 8bit bndry)*/ + + int xdimExtra; /* # columns mod 8 */ + + int ydim; /* # rows */ + + ImHotSpotPtr hotspotptr; /* hotspot pointer */ + + int hotspotflag = FALSE; /* TRUE, if there's a + + hotspot */ + + int xhot; /* hotspot x coordinate */ + + int yhot; /* hotspot y coordinate */ + + ImVfb *srcVfb; /* VFB to convert */ + + ImVfbPtr pPixel; /* gray pixel pointer */ + + char buf[1024]; /* Output buffer */ + + int pixel; /* Gray pixel color */ + + int i, j, k; /* Counters */ + + int n; /* # of tag table entries */ + + char name[1024]; /* Bitmap name */ + + char *s; /* String pointer */ + + char message[100]; /* ImInfo message */ + + + + + + /* + + * Use the output file's name, sans leading path and dot + + * extensions, to make up the root name of the #define's + + * and bits array declaration. + + */ + + s = &ImErrorFileName[strlen(ImErrorFileName)-1]; + + while ( s != (ImErrorFileName-1) && *s != '/' ) + + s--; + + ++s; + + if ( !isalpha( *s ) ) + + { + + /* Make it a legal variable name by adding an 'x'.*/ + + name[0] = 'x'; + + name[1] = '\0'; + + strcat( name, s ); + + } + + else + + strcpy( name, s ); + + s = name; + + while ( isalpha( *s ) || isdigit( *s ) || *s == '_' ) + + s++; + + *s = '\0'; + + + + + + /* + + * Get hotspot info if available + + */ + + if( TagEntryQValue( TagTableQDirect( tagTable, "image hot spot", 0 ), + + &hotspotptr ) != -1 ) + + { + + xhot = ImHotSpotQX( hotspotptr ); + + yhot = ImHotSpotQY( hotspotptr ); + + hotspotflag = TRUE; + + } + + + + + + /* + + * Write out the header. + + */ + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &srcVfb ); + + xdim = ImVfbQWidth( srcVfb ); + + ydim = ImVfbQHeight( srcVfb ); + + if ( ioType & IMFILEIOFD ) + + { + + sprintf( buf, "#define %s_width %d\n", name, xdim ); + + write( fd, buf, strlen( buf ) ); + + sprintf( buf, "#define %s_height %d\n", name, ydim ); + + write( fd, buf, strlen( buf ) ); + + if( hotspotflag == TRUE ) + + { + + sprintf( buf, "#define %s_x_hot %d\n", name, xhot ); + + write( fd, buf, strlen( buf ) ); + + sprintf( buf, "#define %s_y_hot %d\n", name, yhot ); + + write( fd, buf, strlen( buf ) ); + + } + + sprintf( buf, "static char %s_bits[] = {\n", name ); + + write( fd, buf, strlen( buf ) ); + + } + + else + + { + + fprintf( fp, "#define %s_width %d\n", name, xdim ); + + fprintf( fp, "#define %s_height %d\n", name, ydim ); + + if( hotspotflag == TRUE ) + + { + + fprintf( fp, "#define %s_x_hot %d\n", name, xhot ); + + fprintf( fp, "#define %s_y_hot %d\n", name, yhot ); + + } + + fprintf( fp, "static char %s_bits[] = {\n", name ); + + } + + xdimExtra = xdim & 0x7; + + xdim2 = xdim - xdimExtra; + + + + /* + + * Output -verbose message + + */ + + sprintf( message, "%d x %d", xdim, ydim ); + + ImInfo( "Resolution", message ); + + ImInfo( "Type", "1-bit Monochrome" ); + + + + if( hotspotflag == TRUE ) + + { + + sprintf( message, "%d, %d", xhot, yhot ); + + ImInfo( "Hot Spot", message ); + + } + + else + + { + + ImInfo( "Hot Spot", "none" ); + + } + + + + /* + + * Write out the image. + + */ + + pPixel = ImVfbQFirst( srcVfb ); + + for ( i = 0; i < ydim; i++ ) + + { + + for ( j = 0; j < xdim2; j += 8 ) + + { + + pixel = 0; + + for ( k = 0; k < 8; k++ ) + + { + + if ( ImVfbQMono( srcVfb, pPixel ) > 0 ) + + pixel |= (1< 0 ) + + pixel |= (1< + +#include "iminternal.h" + +#include "imxpm.h" + + + + + +/** + + ** FORMAT + + ** xpm - X11 Pixel Map + + ** + + ** AKA + + ** pm + + ** + + ** FORMAT REFERENCES + + ** XPM Manual by Arnaud Le Hors + + ** (Available in postscript format from ftp.x.org in /R5contrib/xpm-3.4a.tar.gz) + + ** + + ** + + ** CODE CREDITS + + ** Custom Development, Brian Dugggan, San Diego SuperComputer Center, 1995 + + ** + + ** DESCRIPTION + + ** X11 pixmaps are simple C code to declare and initialize an array of strings. + + ** The elements of the array are indexes into a color lookup table, which is + + ** described in the beginning of the file. + + ** + + ** For instance, here's a typical xpm file: + + ** (Replace all the \'s below with /'s. I put them in + + ** backwards so that this example wouldn't mess up compilation + + ** of this file (imxpm.c). ) + + ** + + ** static char* my_pixmap[] = { + + ** \* width height ncolors charsperpixel [xhot yhot] [XPMEXT] *\ + + ** "10 10 3 2 0 0 XPMEXT", + + ** \* colors *\ + + ** " c red m white s name_1", + + ** "xx c green m black s name_2", + + ** "yy c blue m white s name_3", + + ** \* pixels *\ + + ** " xxyy", + + ** "yy yy yy yy yy", + + ** " yy yy yy yy ", + + ** "xx xx xx xx xx", + + ** " xx xx xx xx ", + + ** " xxyy", + + ** "yy yy yy yy yy", + + ** " yy yy yy yy ", + + ** "xx xx xx xx xx", + + ** " xxyy", + + ** "yy yy yy yy yy", + + ** \* extension data *\ + + ** "XPMEXT ext1 data1", + + ** "XPMENDEXT" + + ** }; + + ** + + ** The stuff at the end of the file (XPMEXT..) is an extension for the + + ** the picture. We don't touch those here. + + ** + + ** The color lookup table works as follows: + + ** The first 2 characters (since there are 2 character per pixel in this particular + + ** image) reference the number of the clt. Then "c red" indicates that if color + + ** is possible this color should be read. "m white" means if only mono is possible, this + + ** value should be white. "g4 val" means use val for 4-bit grayscale. "g val" means + + ** Use val for >4 bit grayscale. "s name" means 'name' is the name of this entry. (We + + ** don't use the s flag.) + + ** + + ** xhot and yhot are the hot spot location. + + ** We read and write hotspots. + + ** + + **/ + + + +/* + + * TYPEDEF + + * charTable + + * + + * DESCRIPTION + + * This is the hash array that we're going to use to store the + + * various character combination. That is, 2 characters determines + + * an index value. So the array element corresponding to the 2 characters + + * will contain this index value. + + */ + +typedef int *charTable; + + + +/* + + * XPM - MIT X11 Window System PixMap + + * For information on these structures, how to use them, etc. please + + * see imfmt.c. + + */ + +#ifdef __STDC__ + +static int imXpmRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); + +static int imXpmWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, + + TagTable *tagTable ); + +static int imXpmReadClt(FILE* fp,int numColors, TagTable* tagTable, charTable cltTable, int charsPerPixel, ImClt* clt); + +static int imXpmGetRgb(char* buffer, unsigned char *red, unsigned char *green, unsigned char *blue, int *transparent); + +static char* strHasFlag(char* buffer, char c); + +static int imXpmLookUpRgb(char *colorName,unsigned char*red,unsigned char *green,unsigned char *blue, int* transparent); + +#else + +static int imXpmRead( ); + +static int imXpmWrite( ); + +static int imXpmReadClt(); + +static int imXpmGetRgb(); + +static char* strHasFlag(); + +static int imXpmLookUpRgb(); + +#endif + + + +static char *imXpmNames[ ] = { "xpm", "pm", NULL }; + +static ImFileFormatReadMap imXpmReadMap[ ] = + +{ + + /* in out */ + + /* type,ch,dep, attr. VFB type attr. */ + + { IN,1,8, 0, IMVFBINDEX8, 0 }, + + { -1, 0, -1, 0 }, + +}; + +static ImFileFormatWriteMap imXpmWriteMap[ ] = + +{ + + /* in out */ + + /* VFB type, attr., type,ch,dep, attr., func */ + + { IMVFBINDEX8, 0, IN,1,8, 0, imXpmWrite }, + + { -1, 0, -1, 0, NULL }, + +}; + + + +static ImFileMagic imFileXpmMagic []= + +{ + + { 0, 0, NULL}, + +}; + + + +ImFileFormat ImFileXpmFormat = + +{ + + imXpmNames, "X11 pixmap file", + + "X Consortium / MIT", + + "ASCII pixmap color indexed files.", + + "ASCII pixmap color indexed files.", + + imFileXpmMagic, + + IMNOMULTI, IMPIPE, + + IMNOMULTI, IMPIPE, + + imXpmRead, imXpmReadMap, imXpmWriteMap + +}; + + + + + + + + + + + +/* + + * FUNCTION + + * imXpmRead - read an X11 pixmap file + + * + + * DESCRIPTION + + * The file is read and it's mono image written to a new mono VFB. + + * + + * Xpm files have characters which are associated with index values. + + * Thus, we need to associate an integer (the index value) with + + * several different one or two character strings. This is done using + + * a very primitive hash table; An array of integers with 128x128 entries. + + * i.e. one for each two letter comibination. When we read in the colors, + + * we set the entry in the array corresponding to the two letter code, to its + + * corresponding index value. + + */ + + + +static int /* Returns # tags read in */ + +#ifdef __STDC__ + +imXpmRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imXpmRead( ioType, fd, fp, flagsTable, tagTable ) + + int ioType; /* I/O flags */ + + int fd; /* Input file descriptor */ + + FILE *fp; /* Input file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Tag table to add to */ + +#endif + +{ + + char message[1024]; /* Error message holder */ + + char buffer[1024]; /* really big char buffer */ + + char c,c1,c2; /* characters */ + + int xSize, ySize; /* dimensions */ + + int charsPerPixel; /* chars per pixel */ + + int numColors; /* number of colors */ + + ImClt* clt; /* clt */ + + charTable cltTable; /* table w/ clt values */ + + int i,j; /* indices */ + + ImVfb* vfb; /* vfb */ + + ImVfbPtr vfbPtr; /* vfb pointer */ + + int index; /* hash index */ + + ImHotSpotPtr hotspotptr; /* points to hot spot */ + + int xHot, yHot; /* hot spot values */ + + + + /* + + * Please notice that in this routine, I have decided NOT + + * to rewrite a C-language parser. Instead, I'm only going + + * to be able to read nice xpm files. So xpm's with quotation + + * marks in the comment blocks, hex values instead of strings, + + * extreme amounts of whitespace, or other devious sorts of things + + * will not be handled very well here. Sorry. + + */ + + + + + + if ( ioType & IMFILEIOFD ) + + { + + /* + + * Given file descriptor. More convenient to deal with + + * a file pointer for this format. + + */ + + if ( (fp = fdopen( fd, "rb" )) == NULL ) + + { + + ImErrNo = IMESYS; + + return ( -1 ); + + } + + } + + + + /* + + * Find the first quote in the file. Then read + + * in width, height, number of colors, chars per pixel. + + */ + + c = '0'; + + while (c!='"' && !feof(fp)) + + c = fgetc(fp); + + if (c!='"') + + ImErrorFatal("Unexpected end of file", -1, IMESYS); + + fscanf(fp, "%d %d %d %d", &xSize, &ySize, &numColors, &charsPerPixel); + + + + sprintf(message,"%d x %d",xSize,ySize); + + ImInfo("Resolution",message); + + ImInfo("Type","8-bit Color Indexed"); + + sprintf(message,"%d entries",numColors); + + ImInfo("Color Table",message); + + ImInfo("Compression Type","none"); + + ImInfo("Alpha Channel","none"); + + + + /* Read the rest of this string into a buffer */ + + i = 0; + + while (c!=',' && !feof(fp)) + + { + + c = fgetc(fp); + + buffer[i++] = c; + + } + + if (c!=',') + + ImErrorFatal("Unexpected end of file", -1, IMESYS); + + buffer[i] = '\0'; + + if (sscanf(buffer,"%d %d",&xHot,&yHot)==2) + + { /* we have a hot spot */ + + sprintf(message,"%d, %d",xHot, yHot); + + ImInfo("Hot Spot",message); + + ImMalloc(hotspotptr, ImHotSpotPtr, sizeof(ImHotSpot) * 1); + + ImHotSpotSX( hotspotptr, xHot); + + ImHotSpotSY( hotspotptr, yHot); + + TagTableAppend( tagTable, TagEntryAlloc( "image hot spot", + + POINTER, &hotspotptr) ); + + } + + + + if (numColors > 256) + + { + + ImErrorFatal( "Too many colors", -1, IMEUNSUPPORTED ); + + } + + if (charsPerPixel > 2) + + { + + ImErrorFatal( "Too many characters per pixel. (Max is 2). ", -1, IMEUNSUPPORTED); + + } + + + + /* + + * Allocate our hash table + + */ + + ImMalloc(cltTable, charTable, (sizeof(int) * 128 * 128) ); + + clt = ImCltAlloc( numColors ); + + + + /* + + * Read the clt + + */ + + imXpmReadClt(fp,numColors, tagTable, cltTable, charsPerPixel, clt); + + + + /* + + * Read the pixels + + */ + + vfb = ImVfbAlloc(xSize, ySize, IMVFBINDEX8); + + ImVfbSClt(vfb,clt); + + vfbPtr = ImVfbQFirst(vfb); + + for (i=0;ic red" + + * This means that the color is red. + + * The 'c' indicates that a color is next. A 'c' may + + * also be followed by an rgb value, as follows: + + * + + * "c #AA00AB" + + * This indicates 0xaa red, 0x00 blue, 0xab green. + + * + + * The 'c' may be replaced by a .. + + * 'g' to indicate >4 bit grayscale + + * 'g4' to indicate 4-bit grayscale + + * 'm' to indicate mono + + * 's' to indicate a name (which we don't care about) + + * + + * More than one of the above flags may be given. + + * We'll check the string in the order given above + + * ('c', 'g', 'g4', 'm'), and use the first thing we find. + + * + + * There is no support for the g4 flag here. (I have yet + + * to see an image with ONLY the g4 flag, and no 'c' flag.) + + * If somebody wants to add g4 support, then the strHasFlag + + * routine must be duplicated, and modified to check for a + + * flag of length 2. + + */ + + if (colorName=strHasFlag(buffer,'c')) + + { + + /* We have a color */ + + if (colorName[0] == '#') + + { + + sscanf(colorName,"#%2x%2x%2x",&redInt, &greenInt, &blueInt); + + *red = (unsigned char)redInt; + + *green = (unsigned char)greenInt; + + *blue = (unsigned char)blueInt; + + *transparent = 0; + + return 1; + + } + + else + + return imXpmLookUpRgb(colorName,red,green,blue,transparent); + + } + + + + if (colorName=strHasFlag(buffer,'g')) + + { + + /* We have something like "sgi grey 2" */ + + if (colorName[0] == '#') + + { + + sscanf(colorName,"#%2x%2x%2x",&redInt, &greenInt, &blueInt); + + *red = (unsigned char)redInt; + + *green = (unsigned char)greenInt; + + *blue = (unsigned char)blueInt; + + *transparent = 0; + + return 1; + + } + + else + + return imXpmLookUpRgb(colorName,red,green,blue,transparent); + + + + } + + + + if (colorName=strHasFlag(buffer,'m')) + + { + + /* we hopefully have black or white */ + + if (colorName[0] == '#') + + { + + sscanf(colorName,"#%2x%2x%2x",&redInt, &greenInt, &blueInt); + + *red = (unsigned char)redInt; + + *green = (unsigned char)greenInt; + + *blue = (unsigned char)blueInt; + + *transparent = 0; + + return 1; + + } + + else + + return imXpmLookUpRgb(colorName,red,green,blue,transparent); + + + + } + + + + ImErrorFatal("Couldn't parse line!", -1, IMESYNTAX); + +} + + + + + +#define IS_WHITESPACE(s) ((s)==' ' || (s)=='\t') + +#define IS_SPECIAL_CHAR(xxx) ((xxx)=='m' || (xxx)=='s' || (xxx)=='g' || (xxx)=='c') + + + +/* + + * FUNCTION + + * strHasFlag + + * + + * DESCRIPTION + + * Check to see if a string has a given character, + + * surrounded by whitespace. If it does, return the + + * entry that follows it. + + * + + * The string must be null terminated for this to + + * work. + + */ + +static char* + +#ifdef __STDC__ + +strHasFlag(char* str,char c) + +#else + +strHasFlag(str , c) + +char* str; + +char c; + +#endif + +{ + + char* ptr; /* points into the string */ + + int found = 0; /* means we found it */ + + static char *ret = NULL; /* return this */ + + + + if (ret==NULL) + + { + + ImMallocRetOnError(ret, char *, 100,NULL); + + } + + ptr = str; + + while (*ptr!='\0' && !found) + + { + + /* Advance to the next occurence of c */ + + while (*ptr!='\0' && *ptr!=c) + + { + + ptr++; + + } + + if (*ptr==c) + + { + + /* is it surrounded by whitespace? */ + + if (ptr==str && IS_WHITESPACE(*(ptr+1)) ) + + found = 1; + + if (IS_WHITESPACE(*(ptr-1)) && IS_WHITESPACE(*(ptr+1))) + + found = 1; + + } + + if (!found && *ptr!='\0') + + ptr++; + + } + + if (!found) + + return NULL; /* boo hoo */ + + + + /* + + * Copy the next word into a new string. Return that. + + * Well, we don't really want to copy just the next word. + + * There are colors named "dark slate grey" and "sgi gray 0". + + * What a pain. + + * So, we'll copy the next words that occur, as long as + + * the words are not one of { "m", "s", "g4", "g", "c" }. + + */ + + strcpy(ret, ptr+2); + + strcat(ret," "); /* for checking past the end */ + + ptr = ret; + + while (*ptr!='\0') + + { + + ptr++; + + if (*ptr=='"') + + *ptr = '\0'; + + + + /* + + * Check for m,s,g,c + + */ + + if (IS_WHITESPACE(*ptr) && IS_WHITESPACE(*(ptr+2)) + + && IS_SPECIAL_CHAR(*(ptr+1))) + + *ptr = '\0'; + + + + /* + + * Check for g4 + + */ + + if (IS_WHITESPACE(*ptr) && IS_WHITESPACE(*(ptr+3)) && + + *(ptr+1)=='g' && *(ptr+2)=='4') + + *ptr = '\0'; + + } + + + + /* + + * Take any whitespace off of the end of the word + + */ + + ptr--; + + while (IS_WHITESPACE(*ptr)) + + { + + *ptr = '\0'; + + ptr--; + + } + + return ret; + +} + + + + + +/* + + * FUNCTION + + * imXpmLookUpRgb + + * + + * DESCRIPTION + + * Figure out the r,g,b values based on the name of the color. + + * + + */ + +static int /* returns status */ + +#ifdef __STDC__ + +imXpmLookUpRgb(char *colorName,unsigned char*red,unsigned char *green,unsigned char *blue, int* transparent) + +#else + +imXpmLookUpRgb(colorName,red,green,blue,transparent) + +char* colorName; + +unsigned char* red; + +unsigned char* green; + +unsigned char* blue; + +int* transparent; + +#endif + +{ + + static struct imXpmRecordStruct *rgbList=NULL; /* List that we read in */ + + static int first_time = 1; /* First time in this routine? */ + + imXpmColor index; /* One color entry */ + + char message[500]; /* message buffer */ + + char filename[1024]; /* Name of file with color names */ + + char* env_var; /* environment variable */ + + FILE* fp; /* file pointer */ + + int nColors=0; /* How many colors we read in */ + + char str[100]; /* string we read in */ + + char tmpStr[100]; /* Holds the name for a sec */ + + int tmpRed,tmpGreen,tmpBlue; /* holds these as ints */ + + + + /* + + * Well, here's how we do this. + + * + + * On the first pass through this routine, we'll load into memory + + * a table with all of the color names and rgb values. + + * + + * On subsequent passes we'll look stuff up. + + * + + * We'll store the rgb value information in a tagTable. + + * + + * Where do we get the information? + + * 1. Check for the environment variable 'IM_XPM_COLORTABLE' + + * If it is set, use that filename instead of /usr/lib/X11/rgb.txt. + + * 2. Look in /usr/lib/X11 for rgb.txt. + + * + + * There are some very annoying things about rgb colornames. + + * Most significantly, the rgb.txt file is often different + + * from the names of the colors in the rgb.dir and rgb.pag + + * files. If you don't believe me, type 'showrgb'. This'll + + * show you the names of the colors in the database. Then look + + * through rgb.txt. Capital letters and spaces are thrown around + + * like mashed potatoes in a grade school cafeteria. + + * + + * The database contained in imxpm.h came from an execution of + + * the showrgb command. Hopefully by looking for rgb.txt we'll + + * be okay... If we really wanted to be thorough, we'd use the + + * dbm library routines to read the rgb.dir file. But, these + + * routines seem to be on their way to obsolesence. + + * + + */ + + + + /* + + * Is this the first pass? + + * If so, load in the color list. + + */ + + if (first_time==1) + + { + + first_time = 0; + + + + /* Get environment variable. */ + + env_var = getenv("IM_XPM_COLORTABLE"); + + if (env_var) + + strcpy(filename,env_var); + + else + + strcpy(filename,"/usr/lib/X11/rgb.txt"); + + + + /* Load list */ + + fp = fopen(filename,"rb"); + + if (fp) + + { + + /* + + * We don't know the size of the file ahead of time, + + * and we want to read into an array. + + * + + * Rather than use a complicated memory scheme, we'll + + * just read the file twice. Once to see how big it is, + + * the second time to read the stuff in. + + */ + + + + nColors = 0; + + while (!feof(fp)) + + { + + fgets(str, 100, fp); + + nColors++; + + } + + ImMalloc(rgbList, struct imXpmRecordStruct *,(nColors+1)* sizeof(struct imXpmRecordStruct)); + + + + /* Okay, now reopen the file, and read the stuff in */ + + fclose(fp); + + index = rgbList; + + fp = fopen(filename,"rb"); + + while (!feof(fp)) + + { + + fgets(str,100,fp); + + if (str[0]!='\0') + + { + + sscanf(str,"%d %d %d %[^\n]", + + &tmpRed, + + &tmpGreen, + + &tmpBlue, + + tmpStr); + + index->red = tmpRed; + + index->green = tmpGreen; + + index->blue = tmpBlue; + + ImMalloc(index->name,char *, sizeof(char) * (strlen(tmpStr)+1)); + + strcpy(index->name,tmpStr); + + index++; + + } + + } + + /* Set last entry to NULL */ + + index->name = NULL; + + } /* End of if-fp */ + + } /* End of if-first time */ + + + + /* + + * First check external list for color name + + */ + + + + if (rgbList) + + { + + index = rgbList; + + while (index->name != NULL && strcmp(index->name,colorName)!=0) + + { + + index++; + + } + + if (index->name!=NULL) + + { /* Found in file */ + + *red = index->red; + + *green = index->green; + + *blue = index->blue; + + if (strcmp(colorName,"None")==0) + + *transparent = 1; + + else + + *transparent = 0; + + return 1; + + } + + } + + + + /* + + * Check internal list for color name + + */ + + + + index = imXpmColorList; + + while (index->name != NULL && strcmp(index->name,colorName)!=0) + + { + + index++; + + } + + if (index->name==NULL) + + { /* Couldn't find it */ + + sprintf(message,"Unknown color: '%s'",colorName); + + ImErrorFatal( message, -1, IMEUNSUPPORTED); + + } + + *red = index->red; + + *green = index->green; + + *blue = index->blue; + + if (strcmp(colorName,"None")==0) + + *transparent = 1; + + else + + *transparent = 0; + + return 1; + +} + + + + + + + + + +/* + + * FUNCTION + + * imXpmWrite - write an X11 pixmap file + + * + + * DESCRIPTION + + * The X11 pixmap header and image content are written out. + + */ + + + +static int /* Returns # of entries used */ + +#ifdef __STDC__ + +imXpmWrite( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) + +#else + +imXpmWrite( pMap, ioType, fd, fp, flagsTable, tagTable ) + + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + + int ioType; /* Type of I/O to perform */ + + int fd; /* Output file descriptor */ + + FILE *fp; /* Output file pointer */ + + TagTable *flagsTable; /* Flags */ + + TagTable *tagTable; /* Table of info to write */ + +#endif + +{ + + char message[300]; /* various messages */ + + int xSize, ySize; /* dimensions */ + + ImVfb* vfb; /* a very furry buffalo */ + + ImVfbPtr vfbPtr; /* points to a vfb */ + + ImClt* clt; /* a clt */ + + ImCltPtr cltPtr; /* points to a clt */ + + char var_name[100]; /* Name of the variable (in xpm header) */ + + int nColors; /* number of colors in the clt */ + + char** cltCodes; /* list of codes for indexes */ + + int i,j; /* indexes */ + + int transparency_color=-1; /* transparent color */ + + char c,d; /* characters we loop with */ + + char colorName[100]; /* name of a color */ + + ImHotSpotPtr hotspotptr; /* points to a hotspot */ + + int xHot, yHot; /* hot spot values */ + + + + ImInfo("Type","8-bit Color Indexed"); + + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + + clt = ImVfbQClt( vfb ); + + + + if (ioType & IMFILEIOFD) + + { /* I prefer an fp, thank you very much */ + + if ( (fp = fdopen( fd, "rb" )) == NULL ) + + { + + ImErrNo = IMESYS; + + return ( -1 ); + + } + + } + + + + nColors = ImCltQNColors(clt); + + xSize = ImVfbQWidth(vfb); + + ySize = ImVfbQHeight(vfb); + + + + sprintf(message,"%d x %d",xSize,ySize); + + ImInfo("Resolution",message); + + sprintf(message,"%d entries",nColors); + + ImInfo("Color Table",message); + + ImInfo("Compression Type","none"); + + ImInfo("Alpha Channel","none"); + + fprintf(fp,"/* XPM */\nstatic char*%s[] = {\n",var_name); + + if (TagTableQNEntry(tagTable, "image hot spot")==0) + + { + + /* no hot spot */ + + fprintf(fp,"/* width height ncolors chars_per_pixel */\n"); + + fprintf(fp,"\"%d %d %d 2\",\n",xSize,ySize,nColors); + + } + + else + + { + + /* there is a hot spot */ + + fprintf(fp,"/* width height ncolors chars_per_pixel hotspotx hotspoty*/\n"); + + TagEntryQValue( TagTableQDirect( tagTable, "image hot spot", 0), + + &hotspotptr); + + xHot = ImHotSpotQX( hotspotptr ); + + yHot = ImHotSpotQY( hotspotptr ); + + sprintf(message,"%d, %d",xHot, yHot); + + ImInfo("Hot Spot",message); + + fprintf(fp,"\"%d %d %d 2 %d %d\",\n",xSize,ySize,nColors,xHot, yHot); + + + + } + + + + + + + + fprintf(fp,"/* colors */\n"); + + + + /* + + * Print out the clt + + */ + + + + /* + + * For each entry in the clt we want to make a new two character + + * code. Then we want to print this code, followed by 'c '. + + * + + * We also need to store this code at the array index corresponding to + + * it's color entry in the clt for later reference. + + */ + + ImMalloc(cltCodes, char**, nColors * sizeof(char *) ); /* list of strings of length 2 */ + + for (i=0; i< nColors; i++) + + { + + ImMalloc(cltCodes[i], char*, 3*sizeof(char)); + + } + + + + /* + + * Fill up the array of codes. If we have a transparent color, + + * make that " ", since then the text file will look cool. + + */ + + transparency_color = ImGetTransparency(tagTable, flagsTable, vfb); + + if (transparency_color!=-1) + + { + + sprintf(message,"Pixels with index %d.",transparency_color); + + ImInfo("Transparency",message); + + } + + c = 'a'; + + d = 'a'; + + cltPtr = ImCltQFirst(clt); + + for (i=0;iname!=NULL) + + * ptr++; + + */ + + + + + +typedef struct imXpmRecordStruct* imXpmColor; + + + +struct imXpmRecordStruct + +{ + + char* name; + + unsigned char red; + + unsigned char green; + + unsigned char blue; + +}; + + + + + +struct imXpmRecordStruct imXpmColorList[] = + + + +/* + + * This list was taken from /usr/lib/X11/rgb*, + + * using the showrgb command on an sgi. + + */ + +{ + +{"None",0,0,0}, /* transparent. We'll make it black. */ + +{"gainsboro",220,220,220}, + +{"honeydew",240,255,240}, + +{"mistyrose",255,228,225}, + +{"slategrey",112,128,144}, + +{"skyblue",135,206,235}, + +{"light steel blue",176,196,222}, + +{"lightcyan",224,255,255}, + +{"limegreen",50,205,50}, + +{"yellowgreen",154,205,50}, + +{"goldenrod",218,165,32}, + +{"peru",205,133,63}, + +{"light salmon",255,160,122}, + +{"medium violet red",199,21,133}, + +{"snow4",139,137,137}, + +{"bisque2",238,213,183}, + +{"bisque3",205,183,158}, + +{"azure1",240,255,255}, + +{"slateblue1",131,111,255}, + +{"slateblue4",71,60,139}, + +{"skyblue1",135,206,255}, + +{"lightskyblue3",141,182,205}, + +{"paleturquoise4",102,139,139}, + +{"cadetblue2",142,229,238}, + +{"aquamarine4",69,139,116}, + +{"springgreen2",0,238,118}, + +{"olivedrab1",192,255,62}, + +{"yellow2",238,238,0}, + +{"goldenrod3",205,155,29}, + +{"goldenrod4",139,105,20}, + +{"burlywood1",255,211,155}, + +{"orange3",205,133,0}, + +{"tomato3",205,79,57}, + +{"darkorchid2",178,58,238}, + +{"purple4",85,26,139}, + +{"mediumpurple2",159,121,238}, + +{"gray26",66,66,66}, + +{"gray33",84,84,84}, + +{"grey34",87,87,87}, + +{"gray37",94,94,94}, + +{"gray44",112,112,112}, + +{"gray63",161,161,161}, + +{"gray99",252,252,252}, + +{"sgi gray 0",0,0,0}, + +{"sgigray12",30,30,30}, + +{"sgi grey 24",61,61,61}, + +{"sgi gray 36",91,91,91}, + +{"sgigrey44",112,112,112}, + +{"sgigrey96",244,244,244}, + +{"sgichartreuse",113,198,113}, + +{"sgi bright gray",197,193,170}, + +{"deep sky blue",0,191,255}, + +{"darkgreen",0,100,0}, + +{"dark khaki",189,183,107}, + +{"wheat",245,222,179}, + +{"dark orange",255,140,0}, + +{"coral",255,127,80}, + +{"medium purple",147,112,219}, + +{"thistle",216,191,216}, + +{"navajowhite3",205,179,139}, + +{"lemonchiffon1",255,250,205}, + +{"honeydew1",240,255,240}, + +{"paleturquoise1",187,255,255}, + +{"goldenrod2",238,180,34}, + +{"burlywood2",238,197,145}, + +{"tan4",139,90,43}, + +{"chocolate2",238,118,33}, + +{"brown4",139,35,35}, + +{"maroon1",255,52,179}, + +{"maroon2",238,48,167}, + +{"darkorchid1",191,62,255}, + +{"mediumpurple1",171,130,255}, + +{"thistle1",255,225,255}, + +{"thistle2",238,210,238}, + +{"thistle3",205,181,205}, + +{"gray16",41,41,41}, + +{"gray23",59,59,59}, + +{"grey25",64,64,64}, + +{"gray27",69,69,69}, + +{"gray35",89,89,89}, + +{"gray41",105,105,105}, + +{"grey44",112,112,112}, + +{"gray45",115,115,115}, + +{"grey53",135,135,135}, + +{"gray74",189,189,189}, + +{"sgigrey4",10,10,10}, + +{"sgi gray 16",40,40,40}, + +{"sgigray28",71,71,71}, + +{"sgigrey28",71,71,71}, + +{"sgi grey 48",122,122,122}, + +{"sgi grey 84",214,214,214}, + +{"sgigrey88",224,224,224}, + +{"sgilightblue",125,158,192}, + +{"sgislateblue",113,113,198}, + +{"navy blue",0,0,128}, + +{"medium spring green",0,250,154}, + +{"olive drab",107,142,35}, + +{"darkgoldenrod",184,134,11}, + +{"rosybrown",188,143,143}, + +{"violetred",208,32,144}, + +{"slateblue3",105,89,205}, + +{"deepskyblue2",0,178,238}, + +{"deepskyblue3",0,154,205}, + +{"deepskyblue4",0,104,139}, + +{"cadetblue4",83,134,139}, + +{"indianred1",255,106,106}, + +{"wheat4",139,126,102}, + +{"firebrick1",255,48,48}, + +{"lightsalmon2",238,149,114}, + +{"darkorange1",255,127,0}, + +{"grey61",156,156,156}, + +{"gray65",166,166,166}, + +{"grey100",255,255,255}, + +{"sgigrey36",91,91,91}, + +{"sgigrey40",102,102,102}, + +{"sgi grey 88",224,224,224}, + +{"crimson",220,20,60}, + +{"floralwhite",255,250,240}, + +{"lemon chiffon",255,250,205}, + +{"light slate grey",119,136,153}, + +{"mediumblue",0,0,205}, + +{"dodger blue",30,144,255}, + +{"steelblue",70,130,180}, + +{"turquoise",64,224,208}, + +{"medium aquamarine",102,205,170}, + +{"sea green",46,139,87}, + +{"seagreen",46,139,87}, + +{"lawngreen",124,252,0}, + +{"green",0,255,0}, + +{"lightyellow",255,255,224}, + +{"dark goldenrod",184,134,11}, + +{"brown",165,42,42}, + +{"violet",238,130,238}, + +{"medium orchid",186,85,211}, + +{"navajowhite4",139,121,94}, + +{"ivory1",255,255,240}, + +{"honeydew4",131,139,131}, + +{"deepskyblue1",0,191,255}, + +{"darkolivegreen3",162,205,90}, + +{"lightgoldenrod3",205,190,112}, + +{"darkgoldenrod1",255,185,15}, + +{"burlywood4",139,115,85}, + +{"wheat1",255,231,186}, + +{"chocolate3",205,102,29}, + +{"chocolate4",139,69,19}, + +{"pink2",238,169,184}, + +{"orchid4",139,71,137}, + +{"gray0",0,0,0}, + +{"grey4",10,10,10}, + +{"grey5",13,13,13}, + +{"grey17",43,43,43}, + +{"gray29",74,74,74}, + +{"grey30",77,77,77}, + +{"grey46",117,117,117}, + +{"gray47",120,120,120}, + +{"grey47",120,120,120}, + +{"grey48",122,122,122}, + +{"grey55",140,140,140}, + +{"grey56",143,143,143}, + +{"grey57",145,145,145}, + +{"grey85",217,217,217}, + +{"grey88",224,224,224}, + +{"sgigrey0",0,0,0}, + +{"sgi gray 8",20,20,20}, + +{"sgigray24",61,61,61}, + +{"sgigrey64",163,163,163}, + +{"sgigray84",214,214,214}, + +{"sgi very light grey",214,214,214}, + +{"dim grey",105,105,105}, + +{"lightslategrey",119,136,153}, + +{"navyblue",0,0,128}, + +{"lightskyblue",135,206,250}, + +{"forestgreen",34,139,34}, + +{"rosy brown",188,143,143}, + +{"beige",245,245,220}, + +{"deeppink",255,20,147}, + +{"mediumvioletred",199,21,133}, + +{"dark orchid",153,50,204}, + +{"peachpuff3",205,175,149}, + +{"blue4",0,0,139}, + +{"dodgerblue4",16,78,139}, + +{"lightcyan4",122,139,139}, + +{"palegreen3",124,205,124}, + +{"indianred3",205,85,85}, + +{"indianred4",139,58,58}, + +{"salmon3",205,112,84}, + +{"salmon4",139,76,57}, + +{"lightsalmon4",139,87,66}, + +{"orangered2",238,64,0}, + +{"plum3",205,150,205}, + +{"gray8",20,20,20}, + +{"gray9",23,23,23}, + +{"gray11",28,28,28}, + +{"gray57",145,145,145}, + +{"grey77",196,196,196}, + +{"sgigray8",20,20,20}, + +{"sgigrey52",132,132,132}, + +{"sgi gray 64",163,163,163}, + +{"sgigray68",173,173,173}, + +{"sgigrey76",193,193,193}, + +{"floral white",255,250,240}, + +{"antique white",250,235,215}, + +{"aliceblue",240,248,255}, + +{"light gray",211,211,211}, + +{"dodgerblue",30,144,255}, + +{"mediumaquamarine",102,205,170}, + +{"dark green",0,100,0}, + +{"khaki",240,230,140}, + +{"lightgoldenrod",238,221,130}, + +{"hot pink",255,105,180}, + +{"light pink",255,182,193}, + +{"antiquewhite4",139,131,120}, + +{"steelblue2",92,172,238}, + +{"turquoise3",0,197,205}, + +{"darkslategray4",82,139,139}, + +{"aquamarine1",127,255,212}, + +{"darkseagreen2",180,238,180}, + +{"darkseagreen3",155,205,155}, + +{"seagreen1",84,255,159}, + +{"springgreen4",0,139,69}, + +{"lightyellow4",139,139,122}, + +{"gold4",139,117,0}, + +{"rosybrown2",238,180,180}, + +{"indianred2",238,99,99}, + +{"sienna2",238,121,66}, + +{"brown2",238,59,59}, + +{"deeppink4",139,10,80}, + +{"magenta1",255,0,255}, + +{"orchid1",255,131,250}, + +{"grey1",3,3,3}, + +{"grey19",48,48,48}, + +{"grey42",107,107,107}, + +{"grey51",130,130,130}, + +{"gray61",156,156,156}, + +{"gray85",217,217,217}, + +{"grey86",219,219,219}, + +{"grey87",222,222,222}, + +{"gray94",240,240,240}, + +{"grey94",240,240,240}, + +{"grey97",247,247,247}, + +{"gray100",255,255,255}, + +{"sgi grey 0",0,0,0}, + +{"sgi grey 16",40,40,40}, + +{"sgigray20",51,51,51}, + +{"sgigray48",122,122,122}, + +{"sgigray76",193,193,193}, + +{"sgigray80",204,204,204}, + +{"sgi gray 88",224,224,224}, + +{"sgigray88",224,224,224}, + +{"sgi dark grey",85,85,85}, + +{"sgibeet",142,56,142}, + +{"sgi teal",56,142,142}, + +{"sgimediumgray",132,132,132}, + +{"bisque",255,228,196}, + +{"mint cream",245,255,250}, + +{"slate grey",112,128,144}, + +{"lightslategray",119,136,153}, + +{"slateblue",106,90,205}, + +{"powderblue",176,224,230}, + +{"light sea green",32,178,170}, + +{"greenyellow",173,255,47}, + +{"darkkhaki",189,183,107}, + +{"sienna",160,82,45}, + +{"seashell2",238,229,222}, + +{"peachpuff2",238,203,173}, + +{"lemonchiffon2",238,233,191}, + +{"royalblue3",58,95,205}, + +{"lightsteelblue1",202,225,255}, + +{"lightsteelblue2",188,210,238}, + +{"palegreen2",144,238,144}, + +{"springgreen1",0,255,127}, + +{"chartreuse2",118,238,0}, + +{"red1",255,0,0}, + +{"deeppink2",238,18,137}, + +{"maroon4",139,28,98}, + +{"grey18",46,46,46}, + +{"gray25",64,64,64}, + +{"gray43",110,110,110}, + +{"gray59",150,150,150}, + +{"grey67",171,171,171}, + +{"grey71",181,181,181}, + +{"sgigray0",0,0,0}, + +{"sgigray4",10,10,10}, + +{"sgi very dark gray",40,40,40}, + +{"whitesmoke",245,245,245}, + +{"navajowhite",255,222,173}, + +{"alice blue",240,248,255}, + +{"lightgrey",211,211,211}, + +{"cornflowerblue",100,149,237}, + +{"lawn green",124,252,0}, + +{"olivedrab",107,142,35}, + +{"chocolate",210,105,30}, + +{"maroon",176,48,96}, + +{"magenta",255,0,255}, + +{"snow3",205,201,201}, + +{"antiquewhite1",255,239,219}, + +{"bisque1",255,228,196}, + +{"navajowhite1",255,222,173}, + +{"navajowhite2",238,207,161}, + +{"steelblue1",99,184,255}, + +{"lightskyblue2",164,211,238}, + +{"lightblue3",154,192,205}, + +{"turquoise2",0,229,238}, + +{"khaki4",139,134,78}, + +{"lightyellow3",205,205,180}, + +{"yellow1",255,255,0}, + +{"wheat2",238,216,174}, + +{"chocolate1",255,127,36}, + +{"brown1",255,64,64}, + +{"orange1",255,165,0}, + +{"orange2",238,154,0}, + +{"darkorange3",205,102,0}, + +{"lightpink4",139,95,101}, + +{"magenta3",205,0,205}, + +{"magenta4",139,0,139}, + +{"purple2",145,44,238}, + +{"purple3",125,38,205}, + +{"grey11",28,28,28}, + +{"grey15",38,38,38}, + +{"gray22",56,56,56}, + +{"gray34",87,87,87}, + +{"gray40",102,102,102}, + +{"grey41",105,105,105}, + +{"grey50",127,127,127}, + +{"gray58",148,148,148}, + +{"gray80",204,204,204}, + +{"grey80",204,204,204}, + +{"grey95",242,242,242}, + +{"grey96",245,245,245}, + +{"sgi grey 20",51,51,51}, + +{"sgigray44",112,112,112}, + +{"sgi grey 64",163,163,163}, + +{"sgigray92",234,234,234}, + +{"sgi grey 96",244,244,244}, + +{"sgidarkgrey",85,85,85}, + +{"sgi salmon",198,113,113}, + +{"lemonchiffon",255,250,205}, + +{"lavender blush",255,240,245}, + +{"midnightblue",25,25,112}, + +{"darkslateblue",72,61,139}, + +{"slate blue",106,90,205}, + +{"light cyan",224,255,255}, + +{"cadet blue",95,158,160}, + +{"mediumseagreen",60,179,113}, + +{"green yellow",173,255,47}, + +{"lime green",50,205,50}, + +{"tan",210,180,140}, + +{"darksalmon",233,150,122}, + +{"lightsalmon",255,160,122}, + +{"darkorange",255,140,0}, + +{"light coral",240,128,128}, + +{"orange red",255,69,0}, + +{"red",255,0,0}, + +{"deep pink",255,20,147}, + +{"pale violet red",219,112,147}, + +{"darkviolet",148,0,211}, + +{"snow2",238,233,233}, + +{"royalblue4",39,64,139}, + +{"dodgerblue3",24,116,205}, + +{"lightskyblue1",176,226,255}, + +{"khaki2",238,230,133}, + +{"lightgoldenrod4",139,129,76}, + +{"sienna4",139,71,38}, + +{"lightsalmon1",255,160,122}, + +{"darkorange4",139,69,0}, + +{"red3",205,0,0}, + +{"red4",139,0,0}, + +{"pink3",205,145,158}, + +{"magenta2",238,0,238}, + +{"mediumorchid2",209,95,238}, + +{"gray1",3,3,3}, + +{"grey7",18,18,18}, + +{"grey13",33,33,33}, + +{"gray31",79,79,79}, + +{"grey35",89,89,89}, + +{"gray51",130,130,130}, + +{"gray55",140,140,140}, + +{"grey68",173,173,173}, + +{"grey69",176,176,176}, + +{"grey73",186,186,186}, + +{"grey74",189,189,189}, + +{"grey75",191,191,191}, + +{"gray81",207,207,207}, + +{"gray90",229,229,229}, + +{"sgi gray 28",71,71,71}, + +{"sgi grey 40",102,102,102}, + +{"sgi beet",142,56,142}, + +{"sgimediumgrey",132,132,132}, + +{"indigo",75,0,130}, + +{"old lace",253,245,230}, + +{"peach puff",255,218,185}, + +{"dark slate gray",47,79,79}, + +{"grey",190,190,190}, + +{"lightgray",211,211,211}, + +{"navy",0,0,128}, + +{"light slate blue",132,112,255}, + +{"powder blue",176,224,230}, + +{"dark olive green",85,107,47}, + +{"lightgoldenrodyellow",250,250,210}, + +{"gold",255,215,0}, + +{"saddlebrown",139,69,19}, + +{"lightpink",255,182,193}, + +{"orchid",218,112,214}, + +{"bisque4",139,125,107}, + +{"cornsilk1",255,248,220}, + +{"honeydew2",224,238,224}, + +{"lightblue4",104,131,139}, + +{"olivedrab4",105,139,34}, + +{"khaki3",205,198,115}, + +{"sienna1",255,130,71}, + +{"brown3",205,51,51}, + +{"red2",238,0,0}, + +{"palevioletred3",205,104,137}, + +{"darkorchid3",154,50,205}, + +{"darkorchid4",104,34,139}, + +{"mediumpurple3",137,104,205}, + +{"mediumpurple4",93,71,139}, + +{"grey8",20,20,20}, + +{"grey10",26,26,26}, + +{"grey14",36,36,36}, + +{"gray20",51,51,51}, + +{"gray21",54,54,54}, + +{"grey26",66,66,66}, + +{"grey43",110,110,110}, + +{"grey52",133,133,133}, + +{"gray53",135,135,135}, + +{"grey72",184,184,184}, + +{"gray75",191,191,191}, + +{"grey82",209,209,209}, + +{"grey83",212,212,212}, + +{"grey84",214,214,214}, + +{"sgi gray 4",10,10,10}, + +{"sgi gray 12",30,30,30}, + +{"sgi grey 12",30,30,30}, + +{"sgigrey12",30,30,30}, + +{"sgi gray 20",51,51,51}, + +{"sgigrey24",61,61,61}, + +{"sgi grey 36",91,91,91}, + +{"sgi gray 56",142,142,142}, + +{"sgi grey 56",142,142,142}, + +{"sgi grey 92",234,234,234}, + +{"sgi gray 100",255,255,255}, + +{"blanched almond",255,235,205}, + +{"peachpuff",255,218,185}, + +{"moccasin",255,228,181}, + +{"mintcream",245,255,250}, + +{"lavenderblush",255,240,245}, + +{"light slate gray",119,136,153}, + +{"gray",190,190,190}, + +{"cornflower blue",100,149,237}, + +{"blue",0,0,255}, + +{"darkolivegreen",85,107,47}, + +{"palegreen",152,251,152}, + +{"chartreuse",127,255,0}, + +{"salmon",250,128,114}, + +{"lightcoral",240,128,128}, + +{"pink",255,192,203}, + +{"violet red",208,32,144}, + +{"lavenderblush1",255,240,245}, + +{"lavenderblush2",238,224,229}, + +{"lavenderblush3",205,193,197}, + +{"mistyrose1",255,228,225}, + +{"mistyrose4",139,125,123}, + +{"azure4",131,139,139}, + +{"skyblue2",126,192,238}, + +{"lightcyan3",180,205,205}, + +{"aquamarine2",118,238,198}, + +{"olivedrab2",179,238,58}, + +{"darkolivegreen1",202,255,112}, + +{"tan3",205,133,63}, + +{"tomato1",255,99,71}, + +{"maroon3",205,41,144}, + +{"orchid3",205,105,201}, + +{"grey3",8,8,8}, + +{"gray15",38,38,38}, + +{"gray19",48,48,48}, + +{"gray30",77,77,77}, + +{"grey37",94,94,94}, + +{"grey38",97,97,97}, + +{"grey39",99,99,99}, + +{"gray50",127,127,127}, + +{"gray54",138,138,138}, + +{"grey62",158,158,158}, + +{"gray66",168,168,168}, + +{"grey81",207,207,207}, + +{"gray83",212,212,212}, + +{"gray92",235,235,235}, + +{"sgigrey16",40,40,40}, + +{"sgi grey 28",71,71,71}, + +{"sgigray40",102,102,102}, + +{"sgigrey56",142,142,142}, + +{"sgi gray 72",183,183,183}, + +{"indigo2",33,136,104}, + +{"antiquewhite",250,235,215}, + +{"midnight blue",25,25,112}, + +{"sky blue",135,206,235}, + +{"light sky blue",135,206,250}, + +{"steel blue",70,130,180}, + +{"darkseagreen",143,188,143}, + +{"yellow green",154,205,50}, + +{"palegoldenrod",238,232,170}, + +{"orangered",255,69,0}, + +{"lightsteelblue4",110,123,139}, + +{"darkslategray1",151,255,255}, + +{"darkslategray2",141,238,238}, + +{"darkslategray3",121,205,205}, + +{"chartreuse4",69,139,0}, + +{"olivedrab3",154,205,50}, + +{"darkolivegreen4",110,139,61}, + +{"coral1",255,114,86}, + +{"coral4",139,62,47}, + +{"lightpink2",238,162,173}, + +{"palevioletred4",139,71,93}, + +{"orchid2",238,122,233}, + +{"plum1",255,187,255}, + +{"thistle4",139,123,139}, + +{"grey9",23,23,23}, + +{"grey28",71,71,71}, + +{"grey29",74,74,74}, + +{"grey36",92,92,92}, + +{"gray77",196,196,196}, + +{"gray78",199,199,199}, + +{"gray79",201,201,201}, + +{"sgigrey60",153,153,153}, + +{"dark slate blue",72,61,139}, + +{"royalblue",65,105,225}, + +{"medium turquoise",72,209,204}, + +{"cyan",0,255,255}, + +{"cadetblue",95,158,160}, + +{"springgreen",0,255,127}, + +{"indian red",205,92,92}, + +{"hotpink",255,105,180}, + +{"mediumorchid",186,85,211}, + +{"antiquewhite3",205,192,176}, + +{"lemonchiffon4",139,137,112}, + +{"mistyrose2",238,213,210}, + +{"blue2",0,0,238}, + +{"steelblue3",79,148,205}, + +{"skyblue3",108,166,205}, + +{"lightcyan2",209,238,238}, + +{"turquoise4",0,134,139}, + +{"darkseagreen1",193,255,193}, + +{"yellow3",205,205,0}, + +{"gold1",255,215,0}, + +{"gold3",205,173,0}, + +{"tan2",238,154,73}, + +{"firebrick2",238,44,44}, + +{"salmon1",255,140,105}, + +{"orange4",139,90,0}, + +{"plum4",139,102,139}, + +{"gray6",15,15,15}, + +{"grey6",15,15,15}, + +{"gray14",36,36,36}, + +{"gray18",46,46,46}, + +{"gray39",99,99,99}, + +{"gray56",143,143,143}, + +{"grey90",229,229,229}, + +{"sgi gray 40",102,102,102}, + +{"sgigray64",163,163,163}, + +{"sgi gray 80",204,204,204}, + +{"sgigrey80",204,204,204}, + +{"sgi slate blue",113,113,198}, + +{"sgiverydarkgray",40,40,40}, + +{"sgibrightgray",197,193,170}, + +{"ivory",255,255,240}, + +{"dark salmon",233,150,122}, + +{"dark violet",148,0,211}, + +{"lightblue1",191,239,255}, + +{"lightblue2",178,223,238}, + +{"turquoise1",0,245,255}, + +{"lightyellow2",238,238,209}, + +{"yellow4",139,139,0}, + +{"coral3",205,91,69}, + +{"deeppink3",205,16,118}, + +{"hotpink1",255,110,180}, + +{"lightpink1",255,174,185}, + +{"mediumorchid4",122,55,139}, + +{"gray2",5,5,5}, + +{"gray4",10,10,10}, + +{"grey20",51,51,51}, + +{"grey24",61,61,61}, + +{"grey31",79,79,79}, + +{"grey49",125,125,125}, + +{"grey58",148,148,148}, + +{"grey63",161,161,161}, + +{"gray67",171,171,171}, + +{"gray73",186,186,186}, + +{"grey89",227,227,227}, + +{"sgigray56",142,142,142}, + +{"sgi gray 76",193,193,193}, + +{"sgigray96",244,244,244}, + +{"sgi medium gray",132,132,132}, + +{"ghostwhite",248,248,255}, + +{"dimgrey",105,105,105}, + +{"slategray",112,128,144}, + +{"lightslateblue",132,112,255}, + +{"royal blue",65,105,225}, + +{"pale green",152,251,152}, + +{"forest green",34,139,34}, + +{"seashell3",205,197,191}, + +{"peachpuff4",139,119,101}, + +{"cornsilk2",238,232,205}, + +{"cornsilk4",139,136,120}, + +{"ivory3",205,205,193}, + +{"honeydew3",193,205,193}, + +{"azure2",224,238,238}, + +{"slategray2",185,211,238}, + +{"slategray3",159,182,205}, + +{"slategray4",108,123,139}, + +{"cadetblue3",122,197,205}, + +{"darkseagreen4",105,139,105}, + +{"palegreen4",84,139,84}, + +{"springgreen3",0,205,102}, + +{"khaki1",255,246,143}, + +{"lightgoldenrod2",238,220,130}, + +{"goldenrod1",255,193,37}, + +{"firebrick3",205,38,38}, + +{"firebrick4",139,26,26}, + +{"orangered3",205,55,0}, + +{"mediumorchid3",180,82,205}, + +{"purple1",155,48,255}, + +{"gray3",8,8,8}, + +{"grey12",31,31,31}, + +{"grey22",56,56,56}, + +{"grey32",82,82,82}, + +{"gray49",125,125,125}, + +{"gray60",153,153,153}, + +{"grey60",153,153,153}, + +{"gray64",163,163,163}, + +{"grey70",179,179,179}, + +{"gray71",181,181,181}, + +{"gray89",227,227,227}, + +{"gray98",250,250,250}, + +{"sgi gray 32",81,81,81}, + +{"sgigray60",153,153,153}, + +{"sgi gray 68",173,173,173}, + +{"sgi grey 68",173,173,173}, + +{"sgigrey84",214,214,214}, + +{"sgiteal",56,142,142}, + +{"sgi light grey",170,170,170}, + +{"sgiverylightgrey",214,214,214}, + +{"navajo white",255,222,173}, + +{"white",255,255,255}, + +{"black",0,0,0}, + +{"paleturquoise2",174,238,238}, + +{"cadetblue1",152,245,255}, + +{"cyan3",0,205,205}, + +{"aquamarine3",102,205,170}, + +{"green2",0,238,0}, + +{"lightyellow1",255,255,224}, + +{"burlywood3",205,170,125}, + +{"wheat3",205,186,150}, + +{"tomato2",238,92,66}, + +{"tomato4",139,54,38}, + +{"lightpink3",205,140,149}, + +{"palevioletred1",255,130,171}, + +{"palevioletred2",238,121,159}, + +{"grey23",59,59,59}, + +{"gray52",133,133,133}, + +{"gray62",158,158,158}, + +{"gray72",184,184,184}, + +{"sgi gray 24",61,61,61}, + +{"sgi grey 76",193,193,193}, + +{"sgi light blue",125,158,192}, + +{"sgi light gray",170,170,170}, + +{"sgilightgray",170,170,170}, + +{"sgiverylightgray",214,214,214}, + +{"sgi medium grey",132,132,132}, + +{"sgiverydarkgrey",40,40,40}, + +{"sgibrightgrey",197,193,170}, + +{"deepskyblue",0,191,255}, + +{"saddle brown",139,69,19}, + +{"firebrick",178,34,34}, + +{"snow1",255,250,250}, + +{"azure3",193,205,205}, + +{"slateblue2",122,103,238}, + +{"darkgoldenrod2",238,173,14}, + +{"darkgoldenrod3",205,149,12}, + +{"darkgoldenrod4",139,101,8}, + +{"tan1",255,165,79}, + +{"violetred1",255,62,150}, + +{"violetred2",238,58,140}, + +{"violetred3",205,50,120}, + +{"gray10",26,26,26}, + +{"gray13",33,33,33}, + +{"gray32",82,82,82}, + +{"gray36",92,92,92}, + +{"grey76",194,194,194}, + +{"grey79",201,201,201}, + +{"gray84",214,214,214}, + +{"gray93",237,237,237}, + +{"sgigray36",91,91,91}, + +{"sgi gray 44",112,112,112}, + +{"sgi grey 44",112,112,112}, + +{"sgigrey48",122,122,122}, + +{"sgi grey 72",183,183,183}, + +{"sgi grey 80",204,204,204}, + +{"sgi dark gray",85,85,85}, + +{"sgi olive drab",142,142,56}, + +{"sgi bright grey",197,193,170}, + +{"oldlace",253,245,230}, + +{"linen",250,240,230}, + +{"lavender",230,230,250}, + +{"misty rose",255,228,225}, + +{"darkslategrey",47,79,79}, + +{"dark turquoise",0,206,209}, + +{"darkturquoise",0,206,209}, + +{"light goldenrod yellow",250,250,210}, + +{"indianred",205,92,92}, + +{"lavenderblush4",139,131,134}, + +{"seagreen2",78,238,148}, + +{"rosybrown4",139,105,105}, + +{"lightsalmon3",205,129,98}, + +{"deeppink1",255,20,147}, + +{"gray12",31,31,31}, + +{"grey21",54,54,54}, + +{"gray38",97,97,97}, + +{"grey45",115,115,115}, + +{"grey54",138,138,138}, + +{"gray70",179,179,179}, + +{"grey78",199,199,199}, + +{"gray86",219,219,219}, + +{"gray87",222,222,222}, + +{"gray88",224,224,224}, + +{"gray95",242,242,242}, + +{"gray96",245,245,245}, + +{"gray97",247,247,247}, + +{"sgigrey100",255,255,255}, + +{"sgidarkgray",85,85,85}, + +{"snow",255,250,250}, + +{"ghost white",248,248,255}, + +{"white smoke",245,245,245}, + +{"mediumslateblue",123,104,238}, + +{"medium blue",0,0,205}, + +{"spring green",0,255,127}, + +{"light yellow",255,255,224}, + +{"light goldenrod",238,221,130}, + +{"blueviolet",138,43,226}, + +{"seashell4",139,134,130}, + +{"peachpuff1",255,218,185}, + +{"blue1",0,0,255}, + +{"steelblue4",54,100,139}, + +{"lightskyblue4",96,123,139}, + +{"paleturquoise3",150,205,205}, + +{"cyan1",0,255,255}, + +{"cyan2",0,238,238}, + +{"palegreen1",154,255,154}, + +{"lightgoldenrod1",255,236,139}, + +{"rosybrown3",205,155,155}, + +{"sienna3",205,104,57}, + +{"gray5",13,13,13}, + +{"grey40",102,102,102}, + +{"gray82",209,209,209}, + +{"gray91",232,232,232}, + +{"sgi grey 32",81,81,81}, + +{"sgi gray 84",214,214,214}, + +{"sgigrey92",234,234,234}, + +{"sgigray100",255,255,255}, + +{"sgisalmon",198,113,113}, + +{"sgi very light gray",214,214,214}, + +{"blanchedalmond",255,235,205}, + +{"cornsilk",255,248,220}, + +{"darkslategray",47,79,79}, + +{"lightseagreen",32,178,170}, + +{"plum",221,160,221}, + +{"lemonchiffon3",205,201,165}, + +{"ivory2",238,238,224}, + +{"blue3",0,0,205}, + +{"cyan4",0,139,139}, + +{"rosybrown1",255,193,193}, + +{"salmon2",238,130,98}, + +{"darkorange2",238,118,0}, + +{"pink1",255,181,197}, + +{"grey2",5,5,5}, + +{"gray7",18,18,18}, + +{"grey16",41,41,41}, + +{"gray17",43,43,43}, + +{"gray48",122,122,122}, + +{"grey91",232,232,232}, + +{"grey92",235,235,235}, + +{"grey93",237,237,237}, + +{"sgigrey20",51,51,51}, + +{"sgigray32",81,81,81}, + +{"sgi grey 52",132,132,132}, + +{"sgi gray 60",153,153,153}, + +{"sgigrey72",183,183,183}, + +{"sgiolivedrab",142,142,56}, + +{"papaya whip",255,239,213}, + +{"papayawhip",255,239,213}, + +{"azure",240,255,255}, + +{"dimgray",105,105,105}, + +{"lightsteelblue",176,196,222}, + +{"aquamarine",127,255,212}, + +{"mediumspringgreen",0,250,154}, + +{"pale goldenrod",238,232,170}, + +{"sandy brown",244,164,96}, + +{"tomato",255,99,71}, + +{"mistyrose3",205,183,181}, + +{"royalblue1",72,118,255}, + +{"royalblue2",67,110,238}, + +{"skyblue4",74,112,139}, + +{"lightcyan1",224,255,255}, + +{"chartreuse3",102,205,0}, + +{"darkolivegreen2",188,238,104}, + +{"hotpink2",238,106,167}, + +{"hotpink3",205,96,144}, + +{"hotpink4",139,58,98}, + +{"violetred4",139,34,82}, + +{"grey0",0,0,0}, + +{"grey27",69,69,69}, + +{"grey33",84,84,84}, + +{"grey64",163,163,163}, + +{"grey65",166,166,166}, + +{"grey66",168,168,168}, + +{"gray68",173,173,173}, + +{"gray69",176,176,176}, + +{"gray76",194,194,194}, + +{"sgigrey32",81,81,81}, + +{"sgi gray 48",122,122,122}, + +{"sgigray52",132,132,132}, + +{"sgi grey 100",255,255,255}, + +{"sgi very dark grey",40,40,40}, + +{"seashell",255,245,238}, + +{"dark slate grey",47,79,79}, + +{"dim gray",105,105,105}, + +{"light grey",211,211,211}, + +{"medium slate blue",123,104,238}, + +{"lightblue",173,216,230}, + +{"pale turquoise",175,238,238}, + +{"paleturquoise",175,238,238}, + +{"medium sea green",60,179,113}, + +{"yellow",255,255,0}, + +{"burlywood",222,184,135}, + +{"sandybrown",244,164,96}, + +{"orange",255,165,0}, + +{"purple",160,32,240}, + +{"antiquewhite2",238,223,204}, + +{"ivory4",139,139,131}, + +{"seagreen3",67,205,128}, + +{"seagreen4",46,139,87}, + +{"green1",0,255,0}, + +{"green4",0,139,0}, + +{"chartreuse1",127,255,0}, + +{"gold2",238,201,0}, + +{"coral2",238,106,80}, + +{"orangered1",255,69,0}, + +{"pink4",139,99,108}, + +{"plum2",238,174,238}, + +{"grey99",252,252,252}, + +{"sgigrey8",20,20,20}, + +{"sgi gray 52",132,132,132}, + +{"sgi grey 60",153,153,153}, + +{"sgi gray 96",244,244,244}, + +{"sgi chartreuse",113,198,113}, + +{"sgilightgrey",170,170,170}, + +{"slate gray",112,128,144}, + +{"light blue",173,216,230}, + +{"mediumturquoise",72,209,204}, + +{"dark sea green",143,188,143}, + +{"palevioletred",219,112,147}, + +{"darkorchid",153,50,204}, + +{"blue violet",138,43,226}, + +{"mediumpurple",147,112,219}, + +{"seashell1",255,245,238}, + +{"cornsilk3",205,200,177}, + +{"dodgerblue1",30,144,255}, + +{"dodgerblue2",28,134,238}, + +{"slategray1",198,226,255}, + +{"lightsteelblue3",162,181,205}, + +{"green3",0,205,0}, + +{"orangered4",139,37,0}, + +{"mediumorchid1",224,102,255}, + +{"gray24",61,61,61}, + +{"gray28",71,71,71}, + +{"gray42",107,107,107}, + +{"gray46",117,117,117}, + +{"grey59",150,150,150}, + +{"grey98",250,250,250}, + +{"sgi grey 4",10,10,10}, + +{"sgi grey 8",20,20,20}, + +{"sgigray16",40,40,40}, + +{"sgigrey68",173,173,173}, + +{"sgigray72",183,183,183}, + +{"sgi gray 92",234,234,234}, + +{ NULL, 0, 0, 0} + +}; + + + + + +#endif /* __IMXPM_H__ */ + + + + + diff --git a/utils/roq2/libim/imxwd.c b/utils/roq2/libim/imxwd.c new file mode 100644 index 0000000..ac7a640 --- /dev/null +++ b/utils/roq2/libim/imxwd.c @@ -0,0 +1,1859 @@ +/** + ** $Header: /roq/libim/imxwd.c 1 11/02/99 4:38p Zaphod $ + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + ** a division of General Atomics, San Diego, California, USA + ** + ** Users and possessors of this source code are hereby granted a + ** nonexclusive, royalty-free copyright and design patent license to + ** use this code in individual software. License is not granted for + ** commercial resale, in whole or in part, without prior written + ** permission from SDSC. This source is provided "AS IS" without express + ** or implied warranty of any kind. + ** + ** For further information contact: + ** E-Mail: info@sds.sdsc.edu + ** + ** Surface Mail: Information Center + ** San Diego Supercomputer Center + ** P.O. Box 85608 + ** San Diego, CA 92138-5608 + ** (619) 534-5000 + **/ + +#define HEADER " $Header: /roq/libim/imxwd.c 1 11/02/99 4:38p Zaphod $" + +/** + ** FILE + ** imxwd.c - X Window System window dump file I/O + ** + ** PROJECT + ** libim - SDSC image manipulation library + ** + ** DESCRIPTION + ** imxwd.c contains routines to read and write X XWD files for + ** the image manipulation library. Raster data read in is stored + ** in a VFB. Raster data written out is taken from a tag table. + ** + ** PUBLIC CONTENTS + ** d =defined constant + ** f =function + ** m =defined macro + ** t =typedef/struct/union + ** v =variable + ** ? =other + ** + ** none + ** + ** PRIVATE CONTENTS + ** imXwdRead f read an X XWD file + ** imXwdWrite8 f write an 8-bit X XWD file + ** imXwdWriteRGB f write a 24-bit X XWD file + ** + ** imXwdHeaderInfo t header for X window dump files + ** imXwdHeaderFields v header description for binary I/O package + ** imXwdColor t CLT entry + ** + ** imXwd8Read8 f read 8-bit image in 8-bit bitmapUnit + ** imXwd8Read16 f read 8-bit image in 16-bit bitmapUnit + ** imXwd8Read32 f read 8-bit image in 32-bit bitmapUnit + ** imXwd24Read32 f read 24-bit image in 32-bit bitmapUnit + ** + ** imXwdWriteHeader f write an XWD file header + ** + ** HISTORY + ** $Log: /roq/libim/imxwd.c $ +* +* 1 11/02/99 4:38p Zaphod + ** Revision 1.12 1995/06/29 00:28:04 bduggan + ** updated copyright year + ** + ** Revision 1.11 1995/04/03 21:42:00 bduggan + ** took out #ifdef NEWMAGIC + ** + ** Revision 1.10 1995/01/10 23:51:41 bduggan + ** made read/write routines static + ** + ** Revision 1.9 94/10/03 11:31:15 nadeau + ** Updated to ANSI C and C++ compatibility. + ** Removed all use of register keyword. + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + ** Changed all float arguments to double. + ** Added forward declarations. + ** Added misc. casts to passify SGI and DEC compilers. + ** Changed all macros and defined constants to have names + ** starting with IM. + ** Rearranged magic number structures for format handlers. + ** Made format handler routines static (i.e., local to file). + ** Updated comments, adding format descriptions and references. + ** Updated indenting on some code. + ** Updated copyright message. + ** + ** Revision 1.8 92/12/03 01:56:59 nadeau + ** Corrected info messages. + ** + ** Revision 1.7 92/11/04 12:11:11 groening + ** put ImFIleFormat info and magic number info + ** from imfmt.c into this file. + ** + ** Revision 1.6 92/09/25 17:08:11 vle + ** Modified to use byte ordering info in header to read in data. This + ** fixes the Decstation bug. Added code to read in window name in + ** header, instead of just ignoring it. Updated copyright notice. + ** Added ImInfo() messages. + ** + ** Revision 1.5 92/05/05 16:51:42 vle + ** Fixed 24-bit write bug that caused imconv to segmentation fault. + ** + ** Revision 1.4 92/03/26 11:21:07 vle + ** Fixed 24-bit read bug that caused imconv to segmentation fault. + ** Added code to more closely match xwud.c's interpretation of + ** the "flags" field. + ** + ** Revision 1.3 91/10/03 09:25:42 nadeau + ** Added 24-bit image read and write support. + ** + ** Revision 1.2 91/02/12 10:49:11 nadeau + ** Removed the tag table checking and VFB conversion now + ** handled by ImFileRead and ImFileWrite. + ** + ** Revision 1.1 90/07/25 16:21:46 nadeau + ** Initial revision + **/ + +#include "iminternal.h" + + + + + +/** + ** FORMAT + ** XWD - X Window System window dump + ** + ** AKA + ** x11 + ** + ** FORMAT REFERENCES + ** Xlib - C Language X Interface, MIT X Window System, Version 11. + ** + ** CODE CREDITS + ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1990. + ** + ** DESCRIPTION + ** The X Window System's XWD (X Window Dump) format is that produced + ** by the 'xwd' utility in the X tool set. XWD files may be redisplayed + ** using 'xwud' (un-dump). + ** + ** Version 11 of X upgraded the XWD format from its larval stages in + ** version 10. This code only supports the X11 version. + ** + ** XWD files start with a fixed size header describing the file's contents: + ** + ** headerSize The size of the "entire" header, in bytes. The "entire" + ** header includes the fixed size header, and a window + ** name string immediately following the header. + ** + ** version The format's version number. For X10, this is a 6. + ** For X11, this is a 7. + ** + ** pixmapFormat The storage format for the image. One of: + ** + ** XY bitmap A 1-bit deep image. Consecutive + ** bits are for consecutive pixels. + ** + ** XY pixmap A multi-bit deep image. Consecutive + ** bits are for consecutive pixels, one + ** plane at a time. (ie., all of bit + ** plane 1, then all of bit plane 2, ...). + ** + ** Z pixmap A multi-bit deep image. Consecutive + ** bits make up a complete multi-bit pixel, + ** followed by the next pixel, ... + ** + ** pixmapDepth The number of bits that make up one pixel. + ** + ** pixmapWidth The width of the image, in pixels. + ** + ** pixmapHeight The height of the image, in pixels. + ** + ** xOffset An offset for the image in the X direction. Ignored + ** by this and most software. + ** + ** byteOrder The byte order for multi-byte data. Only relevant + ** when the bitmapUnit (see below) is greater than one + ** byte. + ** + ** bitmapUnit The unit of storage for pixel data. Pixels of size + ** bitsPerPixel (see blow) are packed into chunks of + ** size bitmapUnit bits. For example, 8-bit index + ** images might have bitsPerPixel = 8, and bitmapUnit + ** = 32. + ** + ** bitmapBitOrder The order of bits within a byte of data. + ** + ** bitmapPad The unit of storage to which scanlines are padded. + ** Usually the same as bitmapUnit. + ** + ** bitsPerPixel The number of bits that make up one pixel, as stored + ** in the file. For a 24-bit image, the pixmapDepth + ** would be 24, but the bitsPerPixel could be 32. This + ** indicates that each 32-bit file quantity contains + ** 24 bits of pixel data and 8 bits that are to be ignored. + ** + ** bytesPerLine The number of bytes of data per scanline, including + ** padding (see bitmapPad). + ** + ** visualClass How colormaps are handled. One of + ** + ** StaticGrey Fixed colormap greyscale + ** GreyScale Variable colormap greyscale + ** + ** StaticColor Fixed colormap color + ** PseudoColor Variable colormap color + ** + ** TrueColor Fixed RGB + ** DirectColor Variable RGB + ** + ** 8-bit index displays are StaticColor if the CLT is + ** not changeable (owned by the server?), and PseudoColor + ** if the CLT can be changed to that of the image. A + ** StaticColor image displayed by 'xwud' uses the server's + ** default CLT. A PseudoColor image displayed by 'xwud' + ** uses the CLT given with the image. When the cursor + ** is outside the image's window, the normal CLT's are + ** used. When the cursor is inside the window, the + ** image's CLT is used. + ** + ** RGB displays are almost always TrueColor. DirectColor + ** displays use a CLT at the "backend" of the display + ** hardware and are usually only there for fancy gamma + ** correction. + ** + ** redMask A mask to get the red-component bits from an RGB pixel. + ** + ** greenMask A mask to get the green-component bits from an RGB pixel + ** + ** blueMask A mask to get the blue-component bits from an RGB pixel. + ** + ** bitsPerRGB The number of bits for one component of an RGB image. + ** Typically 8. + ** + ** colormapEntries The maximum number of entries allowed in a CLT. This + ** is 2 to the power bitsPerPixel. For 8-bit images, + ** this would be 256. + ** + ** nColors The number of CLT entries actually given in the image + ** file. This may be less than or equal to + ** colormapEntries. + ** + ** windowWidth The width of the window to display the image. + ** + ** windowHeight The height of the window to display the image. + ** + ** windowX The X location of the window to display the image. + ** + ** windowY The Y location of the window to display the image. + ** + ** windowBorderWith The width of the border around the window to display + ** the image. + ** + ** headerEnd 32-bits of padding to make the header an even number + ** of 32-bit words. However, it appears that even the + ** Cray does not use this field, so we drop it. + ** + ** Immediately following the fixed size header is a variable number of + ** bytes giving the name of the window, NULL terminated. The number of + ** bytes is given by (headerSize - sizeof( header )), where sizeof( + ** header ) is, of course, given in a non-machine-specific way. + ** + ** If the window has no name, 'xwd' uses the name 'xwdump'. + ** + ** Following the header (headerSize bytes into the file) are zero or more + ** CLT entries (see nColors). Each CLT entry gives: + ** + ** pixel The CLT index at which to store the color. + ** + ** red,green,blue The RGB components of the color. Color components are + ** each 16-bits wide. Color values are scaled down to + ** fit into standard 8-bit per component CLTs: + ** + ** CLT[pixel].red = red * 256 / 65536 + ** CLT[pixel].green = green * 256 / 65536 + ** CLT[pixel].blue = blue * 256 / 65536 + ** + ** flags Flags indicating which of the components should be + ** stored into the CLT entry. Flags is the bitwise OR of: + ** + ** DoRed store the red component + ** DoGreen store the green component + ** DoBlue store the blue component + ** + ** Note: X11R5 xwud incorrectly ignores this field, + ** so images that xwud displays uses the whole CLT. + ** This does not follow the X Window specs! + ** + ** pad Unused space to pad structure to a multiple of 4 bytes + ** in length. + ** + ** After all that is the image data. + ** + ** IMAGE DATA READ ALGORITHM + ** With all of the bitsPerThis and bytesPerThat fields in the header, + ** a very wide range of pixel storage arrangements are possible. A + ** generic read algorithm might be structured as follows: + ** + ** -- read in the raw data + ** nBytes = bytesPerLine * pixmap_Height + ** read nBytes of data into buffer + ** + ** -- byte swap based on byteOrder and bitmapUnit + ** if byteOrder is MBF, + ** for each group of (bitmapUnit/8) bytes, + ** reverse their order + ** + ** -- bit swap based on bitmapBitOrder + ** if bitmapBitOrder is MBF, + ** for each byte, + ** reverse the bit order + ** + ** -- copy them to the VFB + ** for each scanline, + ** for each pixel in the scanline, + ** copy the next pixmapDepth bits into VFB + ** advance to next VFB pixel + ** advance in buffer by bitsPerPixel bits + ** skip to the next bitmapPad bit boundary + ** + ** This algorithm is generic and handles all of the quirky features of + ** the header. It is also painfully slow. + ** + ** The reality of image storage is that there are only a couple common + ** configurations: + ** + ** pixmapDepth 8 or 24 + ** byteOrder MBF or LBF (but usually MBF) + ** bitmapUnit 8, 16, or 32 (but usually 32) + ** bitmapBitOrder LBF + ** bitmapPad 8, 16, or 32 (but usually 32) + ** bitsPerPixel 8 or 32 + ** + ** Rather than use a generic, but very slow algorithm for image data + ** read, this code uses four depth-specific read routines that handle + ** each of the common image depth cases: + ** + ** pixmapDepth 8 + ** bitmapUnit 8 + ** + ** pixmapDepth 8 + ** bitmapUnit 16 + ** + ** pixmapDepth 8 + ** bitmapUnit 32 + ** + ** pixmapDepth 24 + ** bitmapUnit 32 + ** + ** All other cases are considered unreadable. + ** + ** RESTRICTIONS + ** In addition to the read restrictions discussed above, this code + ** handles XWD file header information as follows on read operations: + ** + ** version Must be 7. + ** pixmapFormat Must be a Z pixmap. + ** xOffset Ignored. + ** visualClass Ignored. + ** window* Ignored. + ** + ** Images written by this code set header values to the following: + ** + ** version Always 7. + ** pixmapFormat Always a Z pixmap. + ** pixmapDepth Always 8 or 24. + ** xOffset Always 0. + ** byteOrder Always MBF. + ** bitmapUnit Always 8 or 32 bits. + ** bitmapBitOrder Always MBF. + ** bitmapPad Always the same as bitmapUnit. + ** bitsPerPixel Always 8 or 32. + ** visualClass Always PseudoColor (8-bit) or TrueColor (RGB). + ** windowWidth Always the same as pixmapWidth. + ** windowHeight Always the same as pixmapHeight. + ** windowX Always 0. + ** windowY Always 0. + ** windowBorderWidth Always 0. + **/ + + +/* + * Flag the removal of the optional 'headerEnd' field of the header. This + * field is flagged in the standard X XWDFile.h include file as only being + * included on 64-bit systems. However, the Cray doesn't even include it. + * So, we don't either. + */ +#define no_headerEnd + + +/* + * XWD - MIT X11 Window System Window Dump + * For information on these structures, how to use them, etc. please + * see imfmt.c. + */ +#ifdef __STDC__ +static int imXwdRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); +static int imXwdWrite8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + TagTable *flagsTable, TagTable *tagTable ); +static int imXwdWriteRGB( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + TagTable *flagsTable, TagTable *tagTable ); +#else +static int imXwdRead( ); +static int imXwdWrite8( ), imXwdWriteRGB( ); +#endif + +#ifdef notdef +static int imXwdWrite1( ); +#endif +static char *imXwdNames[ ] = { "xwd", "x11", NULL }; +static ImFileFormatReadMap imXwdReadMap[ ] = +{ + /* in out */ + /* type,ch,dep, attr. VFB type attr. */ +#ifdef notdef + { IN,1,1, 0, IMVFBMONO, 0 }, + { IN,1,1, C, IMVFBMONO, C }, +#endif + { IN,1,8, 0, IMVFBINDEX8, 0 }, + { IN,1,8, C, IMVFBINDEX8, C }, + { RGB,3,8, 0, IMVFBRGB, 0 }, + { RGB,3,8, C, IMVFBRGB, 0 }, + { -1, 0, -1, 0 }, +}; + +static ImFileFormatWriteMap imXwdWriteMap[ ] = +{ + /* in out */ + /* VFB type, attr., type,ch,dep, attr., func */ +#ifdef notdef + { IMVFBMONO, 0, IN,1,1, 0, imXwdWrite1 }, + { IMVFBMONO, C, IN,1,1, C, imXwdWrite1 }, + { IMVFBINDEX8, 0, IN,1,8, 0, imXwdWrite8 }, +#endif + { IMVFBINDEX8, C, IN,1,8, C, imXwdWrite8 }, + { IMVFBRGB, 0, RGB,3,8, 0, imXwdWriteRGB }, + { -1, 0, -1, 0, NULL }, +}; + + +static ImFileMagic imFileXwdMagic []= +{ + { 0, 0, NULL }, +}; + +ImFileFormat ImFileXwdFormat = +{ + imXwdNames, "X Window System window dump image file", + "X Consortium / MIT", +#ifdef notdef + "1-bit XY format bitmaps. 8-bit and 24-bit RGB Z format pixmaps.\n\ +8-bit and 24-bit RGB Z format pixmaps, with or without a CLT.", + "1-bit XY format bitmaps. 8-bit and 24-bit RGB Z format pixmaps.\n\ +8-bit and 24-bit RGB Z format pixmaps, with or without a CLT.", +#else + "8-bit and 24-bit RGB Z format pixmaps, with or without a CLT.", + "8-bit with CLT and 24-bit RGB Z format pixmaps without CLT.", +#endif + imFileXwdMagic, + IMNOMULTI, IMPIPE, + IMNOMULTI, IMPIPE, + imXwdRead, imXwdReadMap, imXwdWriteMap +}; + + + + + + + +/* + * TYPEDEF & STRUCT + * imXwdHeaderInfo - header for X window dump files + * imXwdHeaderFields - header description for binary I/O package + * + * DESCRIPTION + * XWD files start with a header that gives various details of the + * image contained in the file. Different formats of image storage + * will use different numbers of fields in the header, the other fields + * being ignored. + * + * Immediately following the header is the name of the window, if any. + */ + +typedef struct imXwdHeaderInfo /* X11 window dump, version 7 */ +{ + unsigned long xwd_pixmapFormat; /* Pixmap format */ + + unsigned long xwd_pixmapDepth; /* Pixmap depth */ + unsigned long xwd_pixmapWidth; /* Pixmap width */ + unsigned long xwd_pixmapHeight; /* Pixmap height */ + + unsigned long xwd_xOffset; /* Bitmap x offset */ + + unsigned long xwd_byteOrder; /* MSBFirst, LSBFirst */ + + unsigned long xwd_bitmapUnit; /* Bitmap unit */ + unsigned long xwd_bitmapBitOrder; /* MSBFirst, LSBFirst */ + unsigned long xwd_bitmapPad; /* Bitmap scanline pad */ + + unsigned long xwd_bitsPerPixel; /* Bits per pixel */ + unsigned long xwd_bytesPerLine; /* Bytes per scanline */ + + unsigned long xwd_visual; /* Class of colormap */ + + unsigned long xwd_redMask; /* Red mask */ + unsigned long xwd_greenMask; /* Green mask */ + unsigned long xwd_blueMask; /* Blue mask */ + + unsigned long xwd_bitsPerRGB; /* Log base 2 of distinct color */ + + unsigned long xwd_colormapEntries; /* Number of entries in colormap*/ + unsigned long xwd_nColors; /* Number of Color structures */ + + unsigned long xwd_windowWidth; /* Window width */ + unsigned long xwd_windowHeight; /* Window height */ + long xwd_windowX; /* Window upper left X coordinate*/ + long xwd_windowY; /* Window upper left Y coordinate*/ + unsigned long xwd_windowBorderWidth; /* Window border width */ + +#ifndef no_headerEnd + /* Removed since even the Cray doesn't include it in the header.*/ + unsigned long xwd_headerEnd; /* Header padding */ +#endif +} imXwdHeaderInfo; + +static BinField imXwdHeaderFields[ ] = +{ + /* xwd_pixmapFormat */ ULONG, 4, 1, + /* xwd_pixmapDepth */ ULONG, 4, 1, + /* xwd_pixmapWidth */ ULONG, 4, 1, + /* xwd_pixmapHeight */ ULONG, 4, 1, + /* xwd_xOffset */ ULONG, 4, 1, + /* xwd_byteOrder */ ULONG, 4, 1, + /* xwd_bitmapUnit */ ULONG, 4, 1, + /* xwd_bitmapBitOrder */ ULONG, 4, 1, + /* xwd_bitmapPad */ ULONG, 4, 1, + /* xwd_bitsPerPixel */ ULONG, 4, 1, + /* xwd_bytesPerLine */ ULONG, 4, 1, + /* xwd_visual */ ULONG, 4, 1, + /* xwd_redMask */ ULONG, 4, 1, + /* xwd_greenMask */ ULONG, 4, 1, + /* xwd_blueMask */ ULONG, 4, 1, + /* xwd_bitsPerRGB */ ULONG, 4, 1, + /* xwd_colormapEntries */ ULONG, 4, 1, + /* xwd_nColors */ ULONG, 4, 1, + /* xwd_windowWidth */ ULONG, 4, 1, + /* xwd_windowHeight */ ULONG, 4, 1, + /* xwd_windowX */ LONG, 4, 1, + /* xwd_windowY */ LONG, 4, 1, + /* xwd_windowBorderWidth */ ULONG, 4, 1, +#ifndef no_headerEnd + /* Removed since even the Cray doesn't include it in the header.*/ + /* xwd_headerEnd */ ULONG, 4, 1, +#endif + 0, 0, 0, +}; + + +unsigned char *xwd_windowName = NULL; /* XWD window name in file */ + + +#ifndef no_headerEnd +#define IMXWDHEADERSIZE (26 * 4) /* 26 4-byte integers */ + /* includes header size & version*/ + /* fields not in above struct */ +#else +#define IMXWDHEADERSIZE (25 * 4) /* 25 4-byte integers */ + /* includes header size & version*/ + /* fields not in above struct */ +#endif + + +/* + * Value for xwd_version: + */ +#define IMXWDVERSION 7 /* 1st X11 version */ + +/* + * Values for xwd_byteOrder and xwd_bitmapBitOrder: + */ +#define IMXWDLBF 0 /* Least significatn first */ +#define IMXWDMBF 1 /* Most significatn first */ + +/* + * Values for xwd_pixmapFormat: + */ +#define IMXWDXYBITMAP 0 /* 1-bit plane bitmap */ +#define IMXWDXYPIXMAP 1 /* plane interleaved pixmap */ +#define IMXWDZPIXMAP 2 /* uninterleaved pixmap */ + +/* + * Values for xwd_visual: + */ +#define IMXWDSTATICGREY 0 /* Fixed greyscale */ +#define IMXWDGREYSCALE 1 /* Adjustable greyscale */ +#define IMXWDSTATICCOLOR 2 /* Fixed colormap */ +#define IMXWDPSEUDOCOLOR 3 /* Adjustable colormap */ +#define IMXWDTRUECOLOR 4 /* Fixed RGB */ +#define IMXWDDIRECTCOLOR 5 /* Adjustable RGB */ + + + + + +/* + * TYPEDEF & STRUCT + * imXwdColor - CLT entry + * + * DESCRIPTION + * Rather than dump the entire CLT to the file, XWD files may contain + * only those CLT entries actually used by the image. Each Color + * structure gives the CLT index to fill and the RGB value to place + * there. + */ + +typedef struct imXwdColor +{ + unsigned long xwd_pixel; /* CLT index */ + unsigned short xwd_red, xwd_green, xwd_blue;/* RGB value for that location*/ + char xwd_flags; /* DoRed, DoGreen, DoBlue */ + char xwd_pad; /* Unused space */ +} imXwdColor; + +static BinField imXwdColorFields[ ] = +{ + /* xwd_pixel */ ULONG, 4, 1, + /* xwd_red */ USHORT, 2, 1, + /* xwd_green */ USHORT, 2, 1, + /* xwd_blue */ USHORT, 2, 1, + /* xwd_flags */ CHAR, 1, 1, + /* xwd_pad */ CHAR, 1, 1, + 0, 0, 0, +}; + +/* + * Values for xwd_flags: + */ + +#define IMXWDDORED (1<<0) +#define IMXWDDOGREEN (1<<1) +#define IMXWDDOBLUE (1<<2) + + +#define IMXWDNONAME "xwdump" + + +#ifdef __STDC__ +static int imXwd8Read8(int ioType, int fd, FILE *fp, imXwdHeaderInfo *header, ImVfb **vfb); +static int imXwd8Read16(int ioType, int fd, FILE *fp, imXwdHeaderInfo *header, ImVfb **vfb); +static int imXwd8Read32(int ioType, int fd, FILE *fp, imXwdHeaderInfo *header, ImVfb **vfb); +static int imXwd24Read32( int ioType, int fd, FILE *fp, imXwdHeaderInfo *header, ImVfb **vfb, ImClt *clt ); +#else +static int imXwd8Read8(); +static int imXwd8Read16(); +static int imXwd8Read32(); +static int imXwd24Read32(); +#endif + + + + +/* + * FUNCTION + * imXwdRead - read an X XWD file + * imXwd8Read16 - read 16-bit bitmapUnit image + * imXwd8Read32 - read 32-bit bitmapUnit image + * + * DESCRIPTION + * The file header is read, followed by the CLT (if any). Based upon + * the bitmapUnit one of three routines are called to read the image + * data into a new VFB. The VFB (and CLT?) are added to the tag table. + */ + +static int /* Returns # tags read in */ +#ifdef __STDC__ +imXwdRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) +#else +imXwdRead( ioType, fd, fp, flagsTable, tagTable ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag table to add to */ +#endif +{ + ImVfb *vfb; /* Read in image */ + ImVfbPtr pPixel; /* Xwdel pointer */ + + ImClt *clt; /* Image CLT */ + ImCltPtr pColor; /* Color pointer */ + imXwdColor color; /* XWD color */ + unsigned long mask; /* Pixel index mask */ + + unsigned long headerSize; /* Size, in bytes, of header */ + unsigned long version; /* Format version */ + imXwdHeaderInfo header; /* XWD file header */ + + unsigned char swapBuffer[sizeof(unsigned long)]; /* Byte swapping buffer */ + int i; /* Counter */ + char message[100]; /* Error message buffer */ + + + /* + * Read in the header size and version number (each 4 bytes long). + * + * We don't know what byte order to use, so we assume MBF and + * read in these two values. If the version number is not one + * we recognize, we try swapping bytes. If we recognize it then, + * then we have an LBF file instead. If we still don't recognize + * it, then probably the file is in a version we don't understand + * and we give up. + */ + BinByteOrder( BINMBF ); + if ( ImBinRead( ioType, fd, fp, &headerSize, ULONG, 4, 1 ) == -1 ) + ImReturnBinError( ); + if ( ImBinRead( ioType, fd, fp, &version, ULONG, 4, 1 ) == -1 ) + ImReturnBinError( ); + + if ( version != IMXWDVERSION ) + { + /* + * Swap bytes of version value. Don't depend upon a unsigned long + * being 4 bytes long. + */ + for ( i = 0; i < sizeof( unsigned long ); i++ ) + swapBuffer[i] = (version>>(i*8)) & 0xFF; + version = 0; + for ( i = 0; i < sizeof( unsigned long ); i++ ) + version = (version << 8) | swapBuffer[i]; + if ( version != IMXWDVERSION ) + { + sprintf( message, "Unsupported XWD format version. Only support version %d.", IMXWDVERSION ); + ImErrorFatal( message, -1, IMEUNSUPPORTED ); + } + for ( i = 0; i < sizeof( unsigned long ); i++ ) + swapBuffer[i] = (headerSize>>(i*8)) & 0xFF; + headerSize = 0; + for ( i = 0; i < sizeof( unsigned long ); i++ ) + headerSize = (headerSize << 8) | swapBuffer[i]; + BinByteOrder( BINLBF ); + } + + /* + * Read in the rest of the header. Skip the window name. + */ + if ( ImBinReadStruct( ioType, fd, fp, &header, imXwdHeaderFields )== -1) + ImReturnBinError( ); + i = headerSize - IMXWDHEADERSIZE; + if ( i < 0 ) + { + /* Whoa! Stated header size is bogus. Give up! */ + ImErrorFatal( "Bogus XWD file header size!", -1, IMESYNTAX ); + } + if ( i > 0 ) /* read window name */ + { + ImMalloc( xwd_windowName, unsigned char*, i+1 ); + if( ImBinRead( ioType, fd, fp, xwd_windowName, UCHAR, + 1, i ) == -1 ) + { + ImReturnBinError( ); + } + TagTableAppend( tagTable, TagEntryAlloc( "image name", + POINTER, &xwd_windowName ) ); + } + + + /* + * Check for unsupported variations. + */ + if ( header.xwd_pixmapFormat != IMXWDZPIXMAP ) + { + ImErrorFatal( "Only XWD file Z format pixmaps are supported", + -1, IMEUNSUPPORTED ); + } +#ifdef bogus + if ( header.xwd_pixmapDepth != header.xwd_bitsPerPixel ) + { + ImErrorFatal( "XWD file image depth not the same as bits per pixel?", + -1, IMEUNSUPPORTED ); + } +#endif + if ( header.xwd_pixmapDepth != 8 && header.xwd_pixmapDepth != 24 ) + { + ImErrorFatal( "Only 8-bit and 24-bit XWD image depths supported", + -1, IMEUNSUPPORTED ); + } + if ( header.xwd_bitmapUnit != 8 && header.xwd_bitmapUnit != 16 && + header.xwd_bitmapUnit != 32 ) + { + ImErrorFatal( "XWD bitmap units must be 8-, 16-, or 32-bits wide", + -1, IMEUNSUPPORTED ); + } + if ( (header.xwd_bitmapPad % 8) != 0 ) + { + ImErrorFatal( "XWD bitmap pad must be a multiple of 8 bits", + -1, IMEUNSUPPORTED ); + } + if ( header.xwd_pixmapDepth == 24 && + (header.xwd_bitmapPad != 32 || header.xwd_bitmapUnit != 32 || + header.xwd_bitsPerPixel != 32 ) ) + { + ImErrorFatal( "XWD 24-bit images must be stored in 32-bit units", + -1, IMEUNSUPPORTED ); + } + + /* + * Output -verbose file info + */ + if( xwd_windowName != NULL ) + ImInfo( "Window Name", xwd_windowName ); + + sprintf( message, "%d", version ); + ImInfo( "Version", message ); + + if( header.xwd_byteOrder == IMXWDMBF ) + { + ImInfo( "Byte Order", "Most Significant Byte First" ); + } + else + { + ImInfo( "Byte Order", "Least Significant Byte First" ); + } + + sprintf( message, "%d x %d", header.xwd_pixmapWidth, + header.xwd_pixmapHeight ); + ImInfo( "Resolution", message ); + + switch( header.xwd_visual ) + { + case IMXWDSTATICGREY: + sprintf( message, "%d-bit Grayscale (Static Grey)", + header.xwd_pixmapDepth ); + break; + case IMXWDGREYSCALE: + sprintf( message, "%d-bit Grayscale (Grey Scale)", + header.xwd_pixmapDepth ); + break; + case IMXWDSTATICCOLOR: + sprintf( message, "%d-bit Color Indexed (Static Color)", + header.xwd_pixmapDepth ); + break; + case IMXWDPSEUDOCOLOR: + sprintf( message, "%d-bit Color Indexed (Pseudo Color)", + header.xwd_pixmapDepth ); + break; + case IMXWDTRUECOLOR: + sprintf( message, "%d-bit RGB (True Color)", + header.xwd_pixmapDepth ); + break; + case IMXWDDIRECTCOLOR: + sprintf( message, "%d-bit RGB (Direct Color)", + header.xwd_pixmapDepth ); + break; + default: + strcpy( message, "unknown" ); + break; + } + ImInfo( "Type", message ); + + /* + * Read in the CLT, if any. + */ + clt = IMCLTNULL; + if ( header.xwd_colormapEntries != 0 && header.xwd_nColors != 0 ) + { + if ( (clt = ImCltAlloc( header.xwd_colormapEntries )) == + IMCLTNULL ) + { + ImErrorFatal( ImQError( ), -1, ImErrNo ); + } + + /* + * Output -verbose info + */ + sprintf( message, "%d Entries", + header.xwd_colormapEntries ); + ImInfo( "Color Table", message ); + + /* Initialize the CLT to black entries. */ + pColor = ImCltQFirst( clt ); + for ( i = 0; i < header.xwd_colormapEntries; i++ ) + { + ImCltSRed( pColor, 0 ); + ImCltSGreen( pColor, 0 ); + ImCltSBlue( pColor, 0 ); + ImCltSInc( clt, pColor ); + } + + if ( header.xwd_visual == IMXWDDIRECTCOLOR ) + mask = ~((~0) << header.xwd_bitsPerRGB); + else + mask = ~0; /* Any pixel index */ + + /* + * Read in the CLT entries. Scale color components down + * to 8-bit values. + */ + for ( i = 0; i < header.xwd_nColors; i++ ) + { + if ( ImBinReadStruct( ioType, fd, fp, &color, + imXwdColorFields ) == -1 ) + { + ImReturnBinError( ); + } + + /* + * Check to see if xwd_flags is zero. If + * yes, assume that all the primary color + * components, RGB, should be used. This + * is to compensate for the xwud code in X11R5 + * which incorrectly ignores the flags field. + * + * Remove this conditional if xwud.c is fixed + * to correctly use the flags field. + * + * (see xwud.c, Do_Pseudo(), in X11R5 distribution) + */ + if( color.xwd_flags == 0 ) + { + color.xwd_flags = IMXWDDORED|IMXWDDOGREEN| + IMXWDDOBLUE; + } + + /* + * if color index given in file is out of range + * of the colortable, just make it the ith + * one. + */ + if( color.xwd_pixel > header.xwd_nColors ) + { + pColor = ImCltQPtr( clt, i & mask); + } + else + { + pColor = ImCltQPtr( clt, + (int) color.xwd_pixel & mask ); + } + if ( color.xwd_flags & IMXWDDORED ) + ImCltSRed( pColor, + (color.xwd_red *255/65535)); + if ( color.xwd_flags & IMXWDDOGREEN ) + ImCltSGreen( pColor, + (color.xwd_green *255/65535)); + if ( color.xwd_flags & IMXWDDOBLUE ) + ImCltSBlue( pColor, + (color.xwd_blue *255/65535)); + } + } + else /* no colortable */ + { + /* + * Output -verbose info + */ + ImInfo( "Color Table", "none" ); + } + + /* + * Use byteOrder field in header to set Bin package byte + * order for image data. + */ + if ( header.xwd_byteOrder == IMXWDMBF ) + { + BinByteOrder( BINMBF ); + } + else if ( header.xwd_byteOrder == IMXWDLBF ) + { + BinByteOrder( BINLBF ); + } + else + { + sprintf( message, "Unknown XWD file byte order: '%d'", + header.xwd_byteOrder ); + ImErrorFatal( message, -1, IMEUNSUPPORTED ); + } + + /* + * Read in the image. + */ + if ( header.xwd_pixmapDepth == 8 ) + { + /* + * 8-bit index image. + * + * Image may be stored in units of 8, 16 and 32 bit quantities, + * with padding. + */ + if ( (vfb = ImVfbAlloc( header.xwd_pixmapWidth, + header.xwd_pixmapHeight, IMVFBINDEX8 )) == IMVFBNULL ) + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + ImVfbSClt( vfb, clt ); /* CLT could be NULL */ + + switch ( header.xwd_bitmapUnit ) + { + case 8: + if ( imXwd8Read8( ioType, fd, fp, &header, &vfb)== -1) + { + ImVfbFree( vfb ); + return ( -1 ); /* Error already handled */ + } + break; + + case 16: + if ( imXwd8Read16( ioType, fd, fp, &header,&vfb)== -1) + { + ImVfbFree( vfb ); + return ( -1 ); /* Error already handled */ + } + break; + + case 32: + if ( imXwd8Read32( ioType, fd, fp, &header,&vfb)== -1) + { + ImVfbFree( vfb ); + return ( -1 ); /* Error already handled */ + } + break; + } + + TagTableAppend( tagTable, + TagEntryAlloc( "image clt", POINTER, &clt ) ); + TagTableAppend( tagTable, + TagEntryAlloc( "image vfb", POINTER, &vfb ) ); + + if ( ImVfbQClt( vfb ) != IMCLTNULL ) + return ( 2 ); + return ( 1 ); + } + + + /* + * RGB image. + * + * Image must be stored in 32-bit quantities, with padding. + */ + if ( (vfb = ImVfbAlloc( header.xwd_pixmapWidth, + header.xwd_pixmapHeight, IMVFBRGB )) == IMVFBNULL ) + ImErrorFatal( ImQError( ), -1, ImErrNo ); + + if ( imXwd24Read32( ioType, fd, fp, &header, &vfb, clt ) == -1 ) + { + if ( clt != IMCLTNULL ) + ImCltFree( clt ); + ImVfbFree( vfb ); + return ( -1 ); /* Error already handled */ + } + + /* + * Add the VFB to the tagTable. + */ + TagTableAppend( tagTable, TagEntryAlloc( "image vfb", POINTER, &vfb ) ); + +#ifdef notdef + if ( clt != IMCLTNULL ) + ImCltFree( clt ); +#endif + return ( 1 ); +} + + + + + +/* + * FUNCTION + * imXwd8Read8 - read 8-bit image in 8-bit bitmapUnit + * imXwd8Read16 - read 8-bit image in 16-bit bitmapUnit + * imXwd8Read32 - read 8-bit image in 32-bit bitmapUnit + * imXwd24Read32 - read 24-bit image in 32-bit bitmapUnit + * + * DESCRIPTION + * 8-bit pixel data is read in where: + * + * bitmapUnit = 8 bits + * byteOrder = irrelevant + * bitmapBitOrder = MBF (does any machine use LBF?) + * bitmapPad = 8, 16, or 32 + * bitsPerPixel = 8 + * bytesPerLine = whatever + */ + +static int /* Returns status */ +#ifdef __STDC__ +imXwd8Read8( int ioType, int fd, FILE *fp, imXwdHeaderInfo *header, ImVfb **vfb ) +#else +imXwd8Read8( ioType, fd, fp, header, vfb ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + imXwdHeaderInfo *header; /* XWD file header */ + ImVfb **vfb; /* Read in image */ +#endif +{ + ImVfbPtr pPixel; /* Pixel pointer */ + int y; /* Scanline counter */ + int pad; /* # of bytes into next padding */ + int nBytes; /* # of bytes per scanline */ + unsigned char *buffer; /* Input buffer */ + unsigned char *pBufferEnd; /* End of buffer */ + unsigned char *pBuffer; /* Buffer pointer */ + + + /* Get space enough for one scanline. */ + nBytes = header->xwd_pixmapWidth; + if ( header->xwd_bytesPerLine == 0 ) + { + pad = nBytes % (header->xwd_bitmapPad/8); + if ( pad != 0 ) + header->xwd_bytesPerLine = nBytes + + header->xwd_bitmapPad / 8 - pad; + else + header->xwd_bytesPerLine = nBytes; + } + ImMalloc( buffer, unsigned char *, header->xwd_bytesPerLine ); + + + /* Read in each scanline and copy it into the VFB. */ + pPixel = ImVfbQFirst( *vfb ); + pBufferEnd = buffer + nBytes; + + for ( y = 0; y < header->xwd_pixmapHeight; y++ ) + { + pBuffer = buffer; + if ( ImBinRead( ioType, fd, fp, buffer, UCHAR, 1, header->xwd_bytesPerLine ) == -1 ) + { + free( (char *)buffer ); + ImReturnBinError( ); + } + + while ( pBuffer != pBufferEnd ) + { + ImVfbSIndex8( *vfb, pPixel, *pBuffer++ ); + ImVfbSInc( *vfb, pPixel ); + } + } + free( (char *)buffer ); + + return ( 0 ); +} + +static int /* Returns status */ +#ifdef __STDC__ +imXwd8Read16( int ioType, int fd, FILE *fp, imXwdHeaderInfo *header, ImVfb **vfb ) +#else +imXwd8Read16( ioType, fd, fp, header, vfb ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + imXwdHeaderInfo *header; /* XWD file header */ + ImVfb **vfb; /* Read in image */ +#endif +{ + ImVfbPtr pPixel; /* Pixel pointer */ + int y; /* Scanline counter */ + int pad; /* # of bytes into next padding */ + int nBytes; /* # of bytes per scanline */ + sdsc_uint16 *buffer; /* Input buffer */ + sdsc_uint16 *pBufferEnd; /* End of buffer */ + sdsc_uint16 *pBuffer; /* Buffer pointer */ + + + /* + * Read 8-bit image stored in 16-bit units. + */ + nBytes = header->xwd_pixmapWidth; + pad = nBytes % (header->xwd_bitmapPad/8); + if ( pad != 0 ) + nBytes += header->xwd_bitmapPad / 8 - pad; + ImMalloc( buffer, sdsc_uint16 *, nBytes ); + nBytes /= 2; + + pPixel = ImVfbQFirst( *vfb ); + + /* Compute end. Don't include last partially filled unit. */ + pBufferEnd = buffer + (header->xwd_pixmapWidth/2); + + + /* + * Read in each scanline and copy it into VFB pixels. For speed, + * this is split into two slightly different loops, one for LBF + * and one for MBF data. In both cases each 16-bit quantity is read + * into a host short (which may or may not be 16 bits long). The + * 16-bit value is then split into two pixels by shifting and masking. + * The difference between these two loops is only in the order of + * the pixels in the 16-bit value and the shifting and masking needed. + */ + if ( header->xwd_bitmapBitOrder == IMXWDLBF ) + { + /* 1st pixel is at the bottom of the sdsc_uint16. */ + for ( y = 0; y < header->xwd_pixmapHeight; y++ ) + { + pBuffer = buffer; + if ( ImBinRead( ioType, fd, fp, buffer, UINT16, 2, + nBytes ) == -1 ) + { + free( (char *)buffer ); + ImReturnBinError( ); + } + + while ( pBuffer != pBufferEnd ) + { + ImVfbSIndex8( *vfb, pPixel, *pBuffer&0xFF ); + ImVfbSInc( *vfb, pPixel ); + + ImVfbSIndex8( *vfb, pPixel, (*pBuffer++>>8)&0xFF ); + ImVfbSInc( *vfb, pPixel ); + } + + if ( pad != 0 ) + { + ImVfbSIndex8( *vfb, pPixel, *pBuffer&0xFF ); + ImVfbSInc( *vfb, pPixel ); + } + } + free( (char *)buffer ); + return ( 0 ); + } + + /* 1st pixel is at the top of the sdsc_uint16. */ + for ( y = 0; y < header->xwd_pixmapHeight; y++ ) + { + pBuffer = buffer; + if ( ImBinRead( ioType, fd, fp, buffer, UINT16, 2, + nBytes ) == -1 ) + { + free( (char *)buffer ); + ImReturnBinError( ); + } + + while ( pBuffer != pBufferEnd ) + { + ImVfbSIndex8( *vfb, pPixel, (*pBuffer>>8)&0xFF ); + ImVfbSInc( *vfb, pPixel ); + + ImVfbSIndex8( *vfb, pPixel, *pBuffer++&0xFF ); + ImVfbSInc( *vfb, pPixel ); + } + + if ( pad != 0 ) + { + ImVfbSIndex8( *vfb, pPixel, (*pBuffer>>8)&0xFF ); + ImVfbSInc( *vfb, pPixel ); + } + } + free( (char *)buffer ); + return ( 0 ); +} + +static int /* Returns status */ +#ifdef __STDC__ +imXwd8Read32( int ioType, int fd, FILE *fp, imXwdHeaderInfo *header, ImVfb **vfb ) +#else +imXwd8Read32( ioType, fd, fp, header, vfb ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + imXwdHeaderInfo *header; /* XWD file header */ + ImVfb **vfb; /* Read in image */ +#endif +{ + ImVfbPtr pPixel; /* Pixel pointer */ + int y; /* Scanline counter */ + int pad; /* # of bytes into next padding */ + int nBytes; /* # of bytes per scanline */ + sdsc_uint32 *buffer; /* Input buffer */ + sdsc_uint32 *pBufferEnd; /* End of buffer */ + sdsc_uint32 *pBuffer; /* Buffer pointer */ + int i; /* Counter */ + + + /* + * Read 8-bit image stored in 32-bit units. + */ + nBytes = header->xwd_pixmapWidth; + pad = nBytes % (header->xwd_bitmapPad/8); + if ( pad != 0 ) + nBytes += header->xwd_bitmapPad / 8 - pad; + ImMalloc( buffer, sdsc_uint32 *, nBytes ); + nBytes /= 4; + + pPixel = ImVfbQFirst( *vfb ); + + /* Compute end. Don't include last partially filled unit. */ + pBufferEnd = buffer + (header->xwd_pixmapWidth/4); + + /* + * Read in each scanline and copy it into VFB pixels. For speed, + * this is split into two slightly different loops, one for LBF + * and one for MBF data. In both cases each 32-bit quantity is read + * into a host int (which may or may not be 32 bits long). The + * 32-bit value is then split into four pixels by shifting and masking. + * The difference between these two loops is only in the order of + * the pixels in the 32-bit value and the shifting and masking needed. + */ + if ( header->xwd_bitmapBitOrder == IMXWDLBF ) + { + /* 1st pixel is at bottom of sdsc_uint32. */ + for ( y = 0; y < header->xwd_pixmapHeight; y++ ) + { + pBuffer = buffer; + if ( ImBinRead( ioType, fd, fp, buffer, UINT32, 4, + nBytes ) == -1 ) + { + free( (char *)buffer ); + ImReturnBinError( ); + } + + while ( pBuffer != pBufferEnd ) + { + ImVfbSIndex8( *vfb, pPixel, *pBuffer&0xFF ); + ImVfbSInc( *vfb, pPixel ); + + ImVfbSIndex8( *vfb, pPixel, (*pBuffer>>8)&0xFF ); + ImVfbSInc( *vfb, pPixel ); + + ImVfbSIndex8( *vfb, pPixel, (*pBuffer>>16)&0xFF ); + ImVfbSInc( *vfb, pPixel ); + + ImVfbSIndex8( *vfb, pPixel, (*pBuffer>>24)&0xFF ); + ImVfbSInc( *vfb, pPixel ); + + pBuffer++; + } + + for ( i = 0; i < pad; i++ ) + { + ImVfbSIndex8( *vfb, pPixel, (*pBuffer>>(i*8))&0xFF ); + ImVfbSInc( *vfb, pPixel ); + } + } + free( (char *)buffer ); + return ( 0 ); + } + + /* 1st pixel is at top of sdsc_uint32. */ + for ( y = 0; y < header->xwd_pixmapHeight; y++ ) + { + pBuffer = buffer; + if ( ImBinRead( ioType, fd, fp, buffer, UINT32, 4, + nBytes ) == -1 ) + { + free( (char *)buffer ); + ImReturnBinError( ); + } + + while ( pBuffer != pBufferEnd ) + { + ImVfbSIndex8( *vfb, pPixel, (*pBuffer>>24)&0xFF ); + ImVfbSInc( *vfb, pPixel ); + + ImVfbSIndex8( *vfb, pPixel, (*pBuffer>>16)&0xFF ); + ImVfbSInc( *vfb, pPixel ); + + ImVfbSIndex8( *vfb, pPixel, (*pBuffer>>8)&0xFF ); + ImVfbSInc( *vfb, pPixel ); + + ImVfbSIndex8( *vfb, pPixel, *pBuffer&0xFF ); + ImVfbSInc( *vfb, pPixel ); + + pBuffer++; + } + + for ( i = 0; i < pad; i++ ) + { + ImVfbSIndex8( *vfb, pPixel, (*pBuffer>>(24-i*8))&0xFF ); + ImVfbSInc( *vfb, pPixel ); + } + } + free( (char *)buffer ); + return ( 0 ); +} + + +static int /* Returns status */ +#ifdef __STDC__ +imXwd24Read32( int ioType, int fd, FILE *fp, imXwdHeaderInfo *header, ImVfb **vfb, ImClt *clt ) +#else +imXwd24Read32( ioType, fd, fp, header, vfb, clt ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + imXwdHeaderInfo *header; /* XWD file header */ + ImVfb **vfb; /* Read in image */ + ImClt *clt; /* CLT to apply */ +#endif +{ + ImVfbPtr pPixel; /* Pixel pointer */ + int y; /* Scanline counter */ + int nPix; /* # of pixels per scanline */ + sdsc_uint32 *buffer; /* Input buffer */ + sdsc_uint32 *pBufferEnd; /* End of buffer */ + sdsc_uint32 *pBuffer; /* Buffer pointer */ + sdsc_uint32 redMask; /* Mask to get red value */ + sdsc_uint32 greenMask; /* Mask to get green value */ + sdsc_uint32 blueMask; /* Mask to get blue value */ + int redShift; /* Shift to put red into 8-bits */ + int greenShift; /* Shift to put green into 8-bits*/ + int blueShift; /* Shift to put blue into 8-bits*/ + sdsc_uint32 tmp; /* Temp value holder */ + ImCltPtr pColor; /* CLT entry pointer */ + int index; /* CLT index */ + + + /* + * Read 24-bit image stored in 32-bit units. + */ + nPix = header->xwd_pixmapWidth; + ImMalloc( buffer, sdsc_uint32 *, nPix * sizeof( sdsc_uint32 ) ); + pPixel = ImVfbQFirst( *vfb ); + pBufferEnd = buffer + header->xwd_pixmapWidth; + + + /* + * Compute shift values. + */ + tmp = redMask = header->xwd_redMask; + for ( redShift = 0; (tmp&0x1) == 0; redShift++, tmp >>= 1 ) + ; + tmp = greenMask = header->xwd_greenMask; + for ( greenShift = 0; (tmp&0x1) == 0; greenShift++, tmp >>= 1 ) + ; + tmp = blueMask = header->xwd_blueMask; + for ( blueShift = 0; (tmp&0x1) == 0; blueShift++, tmp >>= 1 ) + ; + + + /* + * Read in each scanline. For each 32-bit quantity, pull out the + * red, green, and blue components as indicated by the header's + * masks. + */ + for ( y = 0; y < header->xwd_pixmapHeight; y++ ) + { + pBuffer = buffer; + if ( ImBinRead( ioType, fd, fp, buffer, UINT32, 4, nPix)== -1) + { + free( (char *)buffer ); + ImReturnBinError( ); + } + + while ( pBuffer != pBufferEnd ) + { + tmp = *pBuffer++; + if ( clt == IMCLTNULL ) + { + ImVfbSRed( *vfb, pPixel, + (tmp&redMask)>>redShift); + ImVfbSGreen( *vfb, pPixel, + (tmp&greenMask)>>greenShift); + ImVfbSBlue( *vfb, pPixel, + (tmp&blueMask)>>blueShift); + } + else + { + index = (tmp&redMask)>>redShift; + pColor = ImCltQPtr( clt, index ); + ImVfbSRed( *vfb, pPixel, ImCltQRed( pColor )); + + index = (tmp&greenMask)>>greenShift; + pColor = ImCltQPtr( clt, index ); + ImVfbSGreen( *vfb, pPixel, ImCltQGreen(pColor)); + + index = (tmp&blueMask)>>blueShift; + pColor = ImCltQPtr( clt, index ); + ImVfbSBlue( *vfb, pPixel, ImCltQBlue( pColor)); + } + ImVfbSInc( *vfb, pPixel ); + } + } + free( (char *)buffer ); + return ( 0 ); +} + + + + + +/* + * FUNCTION + * imXwdWriteHeader - write an XWD file header + * + * DESCRIPTION + * The XWD file header is written out for 8-bit or 24-bit images. + * The header includes the image's name, if any. + */ + +static int /* Returns status */ +#ifdef __STDC__ +imXwdWriteHeader( int ioType, int fd, FILE *fp, ImVfb *vfb, char *name, int writeClt ) +#else +imXwdWriteHeader( ioType, fd, fp, vfb, name, writeClt ) + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + ImVfb *vfb; /* Read in image */ + char *name; /* Window name */ + int writeClt; /* Include CLT in XWD file? */ +#endif +{ + ImClt *clt; /* VFB's CLT */ + ImCltPtr pColor; /* CLT color pointer */ + imXwdHeaderInfo header; /* XWD file header */ + imXwdColor color; /* XWD color holder */ + unsigned long headerSize; /* Size of header, in bytes */ + unsigned long version; /* Header version number */ + int n; /* Counter */ + char message[100]; /* ImInfo message */ + + + /* + * Set up the header and write it out. Include a default window + * name after the header. + */ + headerSize = IMXWDHEADERSIZE + strlen( name ) + 1; + version = IMXWDVERSION; + + BinByteOrder( BINMBF ); + if ( ImBinWrite( ioType, fd, fp, &headerSize, ULONG, 4, 1 ) == -1 ) + { + ImReturnBinError( ); + } + if ( ImBinWrite( ioType, fd, fp, &version, ULONG, 4, 1 ) == -1 ) + { + ImReturnBinError( ); + } + + header.xwd_pixmapFormat = IMXWDZPIXMAP; + if ( ImVfbQFields( vfb ) & IMVFBINDEX8 ) + { + /* 8-bit image. */ + header.xwd_pixmapDepth = 8; + header.xwd_bitmapUnit = 8; + header.xwd_redMask = 0; + header.xwd_greenMask = 0; + header.xwd_blueMask = 0; + header.xwd_visual = IMXWDPSEUDOCOLOR; + } + else + { + /* RGB image. */ + header.xwd_pixmapDepth = 24; + header.xwd_bitmapUnit = 32; + header.xwd_redMask = 0xFF0000; + header.xwd_greenMask = 0x00FF00; + header.xwd_blueMask = 0x0000FF; + header.xwd_visual = IMXWDTRUECOLOR; + } + header.xwd_pixmapWidth = ImVfbQWidth( vfb ); + header.xwd_pixmapHeight = ImVfbQHeight( vfb ); + header.xwd_xOffset = 0; + header.xwd_byteOrder = IMXWDMBF; /* Always MBF from us */ + header.xwd_bitmapBitOrder = IMXWDMBF; + header.xwd_bitmapPad = header.xwd_bitmapUnit; + header.xwd_bitsPerPixel = header.xwd_bitmapUnit; + header.xwd_bytesPerLine = header.xwd_pixmapWidth * header.xwd_bitsPerPixel / 8; + header.xwd_bitsPerRGB = 8; /* Always 8 */ + if ( writeClt ) + { + clt = ImVfbQClt( vfb ); + header.xwd_colormapEntries = 256; + header.xwd_nColors = ImCltQNColors( clt ); + } + else + { + header.xwd_colormapEntries = 0; + header.xwd_nColors = 0; + } + header.xwd_windowWidth = header.xwd_pixmapWidth; + header.xwd_windowHeight = header.xwd_pixmapHeight; + header.xwd_windowX = 0; + header.xwd_windowY = 0; + header.xwd_windowBorderWidth = 0; +#ifndef no_headerEnd + header.xwd_headerEnd = 0; +#endif + + if ( ImBinWriteStruct( ioType, fd, fp, &header, imXwdHeaderFields )==-1) + { + ImReturnBinError( ); + } + if ( ImBinWrite( ioType, fd, fp, name, UCHAR, 1, + strlen( name ) + 1 ) == -1 ) + { + ImReturnBinError( ); + } + + + /* + * Write out the CLT, if any. + */ + if ( writeClt ) + { + pColor = ImCltQFirst( clt ); + color.xwd_flags = IMXWDDORED | IMXWDDOGREEN | IMXWDDOBLUE; + color.xwd_pad = 0; + for ( n = 0; n < ImCltQNColors( clt ); n++ ) + { + color.xwd_pixel = n; + color.xwd_red = ImCltQRed( pColor ) *65535/255; + color.xwd_green = ImCltQGreen( pColor ) *65535/255; + color.xwd_blue = ImCltQBlue( pColor ) *65535/255; + ImCltSInc( clt, pColor ); + if ( ImBinWriteStruct( ioType, fd, fp, &color, + imXwdColorFields ) == -1 ) + { + ImReturnBinError( ); + } + } + } + + /* + * Output -verbose file info + */ + if( name != NULL ) + ImInfo( "Window Name", name ); + + sprintf( message, "%d", version ); + ImInfo( "Version", message ); + + if( header.xwd_byteOrder == IMXWDMBF ) + { + ImInfo( "Byte Order", "Most Significant Byte First" ); + } + else + { + ImInfo( "Byte Order", "Least Significant Byte First" ); + } + + sprintf( message, "%d x %d", header.xwd_pixmapWidth, + header.xwd_pixmapHeight ); + ImInfo( "Resolution", message ); + + switch( header.xwd_visual ) + { + case IMXWDSTATICGREY: + sprintf( message, "%d-bit Grayscale (Static Grey)", + header.xwd_pixmapDepth ); + break; + case IMXWDGREYSCALE: + sprintf( message, "%d-bit Grayscale (Grey Scale)", + header.xwd_pixmapDepth ); + break; + case IMXWDSTATICCOLOR: + sprintf( message, "%d-bit Color Indexed (Static Color)", + header.xwd_pixmapDepth ); + break; + case IMXWDPSEUDOCOLOR: + sprintf( message, "%d-bit Color Indexed (Pseudo Color)", + header.xwd_pixmapDepth ); + break; + case IMXWDTRUECOLOR: + sprintf( message, "%d-bit RGB (True Color)", + header.xwd_pixmapDepth ); + break; + case IMXWDDIRECTCOLOR: + sprintf( message, "%d-bit RGB (Direct Color)", + header.xwd_pixmapDepth ); + break; + default: + strcpy( message, "unknown" ); + break; + } + ImInfo( "Type", message ); + + if( header.xwd_colormapEntries ) + { + sprintf( message, "%d Entries", + header.xwd_colormapEntries ); + ImInfo( "Color Table", message ); + } + else + ImInfo( "Color Table", "none" ); + + return ( 0 ); +} + + + + + +/* + * FUNCTION + * imXwdWrite8 - write an 8-bit X XWD file + * + * DESCRIPTION + * The XWD file header set up and written out, followed by the CLT + * (if any). The VFB is then written into the file. + */ + +static int /* Returns # of tags used */ +#ifdef __STDC__ +imXwdWrite8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) +#else +imXwdWrite8( pMap, ioType, fd, fp, flagsTable, tagTable ) + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag table to read from */ +#endif +{ + ImVfb *vfb; /* Read in image */ + ImVfbPtr pPixel; /* Pixel pointer */ + unsigned char *buffer; /* Output buffer */ + unsigned char *pBuffer; /* Buffer pointer */ + char *name; /* Window name */ + int x, y; /* Pixel counters */ + int width, height; /* Image size */ + + + /* + * Get the image, and its name, and write out the XWD file header. + */ + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + if ( TagTableQNEntry( tagTable, "image name" ) != 0 ) + TagEntryQValue( TagTableQDirect( tagTable, "image name", 0 ), + &name ); + else + name = IMXWDNONAME; + + if ( pMap->map_outAttributes & IMCLTYES ) + { + if ( imXwdWriteHeader( ioType, fd, fp, vfb, name, TRUE ) == -1 ) + return ( -1 ); /* Error already set */ + } + else + { + if ( imXwdWriteHeader( ioType, fd, fp, vfb, name, FALSE )== -1 ) + return ( -1 ); /* Error already set */ + } + + + /* + * Copy the VFB to the file, one scanline at a time. + */ + pPixel = ImVfbQFirst( vfb ); + width = ImVfbQWidth( vfb ); + height = ImVfbQHeight( vfb ); + ImMalloc( buffer, unsigned char *, width ); + for ( y = 0; y < height; y++ ) + { + pBuffer = buffer; + for ( x = 0; x < width; x++ ) + { + *pBuffer++ = ImVfbQIndex8( vfb, pPixel ); + ImVfbSInc( vfb, pPixel ); + } + + /* + * Write out the buffer. + */ + if ( ImBinWrite( ioType, fd, fp, buffer, UCHAR, 1, width)== -1 ) + { + free( (char *)buffer ); + ImReturnBinError( ); + } + } + + free( (char *)buffer ); + return ( 1 ); +} + + + + + +/* + * FUNCTION + * imXwdWriteRGB - write a 24-bit X XWD file + * + * DESCRIPTION + * The XWD file header set up and written out, followed by the CLT + * (if any). The VFB is then written into the file. + */ + +static int /* Returns # of tags used */ +#ifdef __STDC__ +imXwdWriteRGB( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, + TagTable *flagsTable, TagTable *tagTable ) +#else +imXwdWriteRGB( pMap, ioType, fd, fp, flagsTable, tagTable ) + ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ + int ioType; /* I/O flags */ + int fd; /* Input file descriptor */ + FILE *fp; /* Input file pointer */ + TagTable *flagsTable; /* Flags */ + TagTable *tagTable; /* Tag table to read from */ +#endif +{ + ImVfb *vfb; /* Read in image */ + ImVfbPtr pPixel; /* Pixel pointer */ + sdsc_uint32 *buffer; /* Output buffer */ + sdsc_uint32 *pBuffer; /* Buffer pointer */ + char *name; /* Window name */ + int x, y; /* Pixel counters */ + int width, height; /* Image size */ + + + /* + * Get the image, and its name, and write out the XWD file header. + */ + TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); + if ( TagTableQNEntry( tagTable, "image name" ) != 0 ) + TagEntryQValue( TagTableQDirect( tagTable, "image name", 0 ), + &name ); + else + name = IMXWDNONAME; + + if ( pMap->map_outAttributes & IMCLTYES ) + { + if ( imXwdWriteHeader( ioType, fd, fp, vfb, name, TRUE ) == -1 ) + return ( -1 ); /* Error already set */ + } + else + { + if ( imXwdWriteHeader( ioType, fd, fp, vfb, name, FALSE )== -1 ) + return ( -1 ); /* Error already set */ + } + + + /* + * Copy the VFB to the file, one scanline at a time. + */ + pPixel = ImVfbQFirst( vfb ); + width = ImVfbQWidth( vfb ); + height = ImVfbQHeight( vfb ); + ImMalloc( buffer, sdsc_uint32 *, sizeof( sdsc_uint32 ) * width ); + for ( y = 0; y < height; y++ ) + { + pBuffer = buffer; + for ( x = 0; x < width; x++ ) + { + *pBuffer++ = ((sdsc_uint32) ImVfbQRed( vfb, pPixel ) << 16) | + ((sdsc_uint32) ImVfbQGreen( vfb, pPixel ) << 8) | + (sdsc_uint32) ImVfbQBlue( vfb, pPixel ); + ImVfbSInc( vfb, pPixel ); + } + + /* + * Write out the buffer. + */ + if ( ImBinWrite( ioType, fd, fp, buffer, UINT32, 4, width)== -1) + { + free( (char *)buffer ); + ImReturnBinError( ); + } + } + + free( (char *)buffer ); + return ( 1 ); +} diff --git a/utils/roq2/libim/libim.dsp b/utils/roq2/libim/libim.dsp new file mode 100644 index 0000000..7a4d989 --- /dev/null +++ b/utils/roq2/libim/libim.dsp @@ -0,0 +1,360 @@ +# Microsoft Developer Studio Project File - Name="libim" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=libim - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "libim.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "libim.mak" CFG="libim - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "libim - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "libim - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName ""$/roq/libim", NCEAAAAA" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "libim - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir ".\Release" +# PROP BASE Intermediate_Dir ".\Release" +# PROP BASE Target_Dir "." +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir ".\Release" +# PROP Intermediate_Dir ".\Release" +# PROP Target_Dir "." +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\libsdsc" /I "..\jpeg" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "USE_JPEG_LIB" /YX /FD /c +# ADD BASE RSC /l 0x409 +# ADD RSC /l 0x409 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "libim - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir ".\Debug" +# PROP BASE Intermediate_Dir ".\Debug" +# PROP BASE Target_Dir "." +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir ".\Debug" +# PROP Intermediate_Dir ".\Debug" +# PROP Target_Dir "." +# ADD BASE CPP /nologo /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /W3 /GX /Z7 /Od /I "..\libsdsc" /I "..\jpeg" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "USE_JPEG_LIB" /YX /FD /c +# ADD BASE RSC /l 0x409 +# ADD RSC /l 0x409 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "libim - Win32 Release" +# Name "libim - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=im.c +# End Source File +# Begin Source File + +SOURCE=imbmp.c +# End Source File +# Begin Source File + +SOURCE=imclt.c +# End Source File +# Begin Source File + +SOURCE=imcur.c +# End Source File +# Begin Source File + +SOURCE=imeps.c +# End Source File +# Begin Source File + +SOURCE=imerrno.c +# End Source File +# Begin Source File + +SOURCE=imfile.c +# End Source File +# Begin Source File + +SOURCE=imfmt.c +# End Source File +# Begin Source File + +SOURCE=imgif.c +# End Source File +# Begin Source File + +SOURCE=imgiflzw.c +# End Source File +# Begin Source File + +SOURCE=imhdfread.c +# End Source File +# Begin Source File + +SOURCE=imhdfwrite.c +# End Source File +# Begin Source File + +SOURCE=imico.c +# End Source File +# Begin Source File + +SOURCE=imicon.c +# End Source File +# Begin Source File + +SOURCE=imiff.c +# End Source File +# Begin Source File + +SOURCE=imipw.c +# End Source File +# Begin Source File + +SOURCE=imjpeg.c +# End Source File +# Begin Source File + +SOURCE=imlzw.c +# End Source File +# Begin Source File + +SOURCE=immiff.c +# End Source File +# Begin Source File + +SOURCE=immpnt.c +# End Source File +# Begin Source File + +SOURCE=impbm.c +# End Source File +# Begin Source File + +SOURCE=impcx.c +# End Source File +# Begin Source File + +SOURCE=impic.c +# End Source File +# Begin Source File + +SOURCE=impict.c +# End Source File +# Begin Source File + +SOURCE=impix.c +# End Source File +# Begin Source File + +SOURCE=imras.c +# End Source File +# Begin Source File + +SOURCE=imrgb.c +# End Source File +# Begin Source File + +SOURCE=imrla.c +# End Source File +# Begin Source File + +SOURCE=imrle.c +# End Source File +# Begin Source File + +SOURCE=imschemes.c +# End Source File +# Begin Source File + +SOURCE=imsoftimage.c +# End Source File +# Begin Source File + +SOURCE=imsynu.c +# End Source File +# Begin Source File + +SOURCE=imtga.c +# End Source File +# Begin Source File + +SOURCE=imtiff.c +# End Source File +# Begin Source File + +SOURCE=imvfb.c +# End Source File +# Begin Source File + +SOURCE=imvfbadjust.c +# End Source File +# Begin Source File + +SOURCE=imvfbchan.c +# End Source File +# Begin Source File + +SOURCE=imvfbcomp.c +# End Source File +# Begin Source File + +SOURCE=imvfbflip.c +# End Source File +# Begin Source File + +SOURCE=imvfbgamma.c +# End Source File +# Begin Source File + +SOURCE=imvfbhist.c +# End Source File +# Begin Source File + +SOURCE=imvfblight.c +# End Source File +# Begin Source File + +SOURCE=imvfbresize.c +# End Source File +# Begin Source File + +SOURCE=imvfbrotate.c +# End Source File +# Begin Source File + +SOURCE=imvfbto.c +# End Source File +# Begin Source File + +SOURCE=imviff.c +# End Source File +# Begin Source File + +SOURCE=imx.c +# End Source File +# Begin Source File + +SOURCE=imxbm.c +# End Source File +# Begin Source File + +SOURCE=imxpm.c +# End Source File +# Begin Source File + +SOURCE=imxwd.c +# End Source File +# Begin Source File + +SOURCE=macpack.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=..\libsdsc\arg.h +# End Source File +# Begin Source File + +SOURCE=..\libsdsc\bin.h +# End Source File +# Begin Source File + +SOURCE=.\im.h +# End Source File +# Begin Source File + +SOURCE=.\imhdfinternal.h +# End Source File +# Begin Source File + +SOURCE=.\iminternal.h +# End Source File +# Begin Source File + +SOURCE=.\impref.h +# End Source File +# Begin Source File + +SOURCE=.\imxpm.h +# End Source File +# Begin Source File + +SOURCE=..\jpeg\jconfig.h +# End Source File +# Begin Source File + +SOURCE=..\jpeg\jmorecfg.h +# End Source File +# Begin Source File + +SOURCE=..\jpeg\jpeglib.h +# End Source File +# Begin Source File + +SOURCE=..\libsdsc\sdsc.h +# End Source File +# Begin Source File + +SOURCE=..\libsdsc\sdscconfig.h +# End Source File +# Begin Source File + +SOURCE=..\libsdsc\sdsccopyright.h +# End Source File +# Begin Source File + +SOURCE=..\libsdsc\tag.h +# End Source File +# Begin Source File + +SOURCE=.\unistd.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/utils/roq2/libim/macpack.c b/utils/roq2/libim/macpack.c new file mode 100644 index 0000000..d8fb1a8 --- /dev/null +++ b/utils/roq2/libim/macpack.c @@ -0,0 +1,1024 @@ + + +/** + + ** $Header: /roq/libim/macpack.c 1 11/02/99 4:38p Zaphod $ + + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + + ** a division of General Atomics, San Diego, California, USA + + ** + + ** Users and possessors of this source code are hereby granted a + + ** nonexclusive, royalty-free copyright and design patent license to + + ** use this code in individual software. License is not granted for + + ** commercial resale, in whole or in part, without prior written + + ** permission from SDSC. This source is provided "AS IS" without express + + ** or implied warranty of any kind. + + ** + + ** For further information contact: + + ** E-Mail: info@sds.sdsc.edu + + ** + + ** Surface Mail: Information Center + + ** San Diego Supercomputer Center + + ** P.O. Box 85608 + + ** San Diego, CA 92138-5608 + + ** (619) 534-5000 + + **/ + + + +#define HEADER " $Header: /roq/libim/macpack.c 1 11/02/99 4:38p Zaphod $" + + + +/** + + ** FILE + + ** macpack.c - Apple Macintosh BitMap/PixMap packing/unpacking routines + + ** + + ** PROJECT + + ** libimage - SDSC image manipulation library + + ** + + ** DESCRIPTION + + ** macpack.c contains routines to compress/uncompress (pack/unpack) Apple + + ** Macintosh BitMap/PixMap raster graphics for the image manipulation library. + + ** + + ** PUBLIC CONTENTS + + ** + + ** PackBits Pack BitMap/PixMap raster. + + ** UnpackBits Unpack BitMap/PixMap raster. + + ** + + ** PackBits3 Pack BitMap/PixMap raster (16 bit). + + ** UnpackBits3 Unpack BitMap/PixMap raster (16 bit). + + ** (3 refers to the PixMap "packType" field value) + + ** + + ** PRIVATE CONTENTS + + ** NULL d an empty pointer + + ** + + ** HISTORY + + ** $Log: /roq/libim/macpack.c $ + * + * 1 11/02/99 4:38p Zaphod + + ** Revision 1.10 1995/06/29 00:28:04 bduggan + + ** updated copyright year + + ** + + ** Revision 1.9 1994/10/03 11:31:18 nadeau + + ** Updated to ANSI C and C++ compatibility. + + ** Removed all use of register keyword. + + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + + ** Changed all float arguments to double. + + ** Added forward declarations. + + ** Added misc. casts to passify SGI and DEC compilers. + + ** Changed all macros and defined constants to have names + + ** starting with IM. + + ** Rearranged magic number structures for format handlers. + + ** Made format handler routines static (i.e., local to file). + + ** Updated comments, adding format descriptions and references. + + ** Updated indenting on some code. + + ** Updated copyright message. + + ** + + ** Revision 1.8 93/10/21 15:17:53 moreland + + ** Added 16-bit word PackBits3 and UnpackBits3 in order to support + + ** the 16-bit direct color pixel spec of the PICT/Quickdraw formats. + + ** + + ** Revision 1.7 92/08/31 17:45:32 vle + + ** Updated copyright notice. + + ** + + ** Revision 1.6 91/03/21 08:51:24 nadeau + + ** Fixed typo. + + ** + + ** Revision 1.5 91/03/15 13:58:34 nadeau + + ** Attempted optimization of UnpackBits. Changed things a bit + + ** to make it through the loop unrolling problems of the + + ** Alliant FX2800 compiler. + + ** + + ** Revision 1.4 91/03/14 18:16:36 todd + + ** Tried to fool the fx2800 compiler into not + + ** unrolling two loops in UnpackBits. + + ** + + ** Revision 1.3 91/02/18 16:23:43 moreland + + ** fixed ! + + ** + + ** Revision 1.2 91/01/29 11:13:24 todd + + ** add comments + + ** + + ** Revision 1.1 90/10/10 09:20:41 moreland + + ** Initial revision + + ** + + ** + + **/ + + + +#include "iminternal.h" + + + + + +/* + + * FUNCTION + + * PackBits - Use macintosh byte packing algorithm + + * + + * DESCRIPTION + + * PackBits compresses "width" bytes of data starting at "cIn" and + + * stores the compressed data at "cOut". Bytes are compressed when there + + * are three or more consecutive equal bytes. After the data is + + * compressed, the value of "width" is the number of compressed data + + * bytes stored at "cOut". In the worst case, the compressed data can + + * be one byte longer than the original data. + + */ + + + +void + +#ifdef __STDC__ + +PackBits( unsigned char *cIn, unsigned char *cOut, unsigned int *width ) + +#else + +PackBits( cIn, cOut, width ) + + unsigned char *cIn; + + unsigned char *cOut; + + unsigned int * width; + +#endif + +{ + + int cnt; + + int len; + + unsigned int wid; + + + + wid = *width; + + len = 0; + + + + /* Loop thru the unencoded bytes */ + + while ( wid > 0 ) + + { + + if ( wid == 1 ) + + { + + cOut[0] = 0; + + cOut[1] = cIn[0]; + + len += 2; + + wid = 0; + + } + + else if ( wid == 2 ) + + { + + cOut[0] = 1; + + cOut[1] = cIn[0]; + + cOut[2] = cIn[1]; + + len += 3; + + wid = 0; + + } + + else if ( wid == 3 ) + + { + + if ( (cIn[0] == cIn[1]) && (cIn[1] == cIn[2]) ) + + { + + cOut[0] = (256 - 3) + 1; + + cOut[1] = cIn[0]; + + len += 2; + + wid = 0; + + } + + else + + { + + cOut[0] = 2; + + cOut[1] = cIn[0]; + + cOut[2] = cIn[1]; + + cOut[3] = cIn[2]; + + len += 4; + + wid = 0; + + } + + } + + else if ( (cIn[0]==cIn[1]) && (cIn[1]==cIn[2]) ) + + { + + /* PACKED RUN */ + + /* We have >= 3 equal bytes */ + + cnt=3; + + while ( (cnt= 3 unequal bytes */ + + cnt=0; + + while ( (cnt 0*/ + + + + *width = len; + +} + + + + + + + +/* + + * FUNCTION + + * UnpackBits - Use macintosh byte unpacking algorithm + + * + + * DESCRIPTION + + * Given a pointer in srcPtr to data that was compressed by PackBits, + + * UnpackBits expands the data and stores the result at dstPtr. cnt is + + * passed IN as the number of compressed bytes to unpack. cnt, when + + * passed back OUT, is the number of uncompressed bytes. + + */ + + + + + +#ifndef new + +void + +#ifdef __STDC__ + +UnpackBits( unsigned char *srcPtr, unsigned char *dstPtr, unsigned int *cnt ) + +#else + +UnpackBits( srcPtr, dstPtr, cnt ) + + unsigned char * srcPtr; + + unsigned char * dstPtr; + + unsigned int * cnt; + +#endif /* STDC */ + +{ + + int i=0, p=0, u=0, run=0; + + unsigned int packed = *cnt; + +#ifndef older + + unsigned char *pDst, *pSrc; + +#endif + + + + while ( p < packed ) + + { + + if ( srcPtr[p] >= 128 ) + + { /* PACKED RUN */ + + run = 256 - srcPtr[p] + 1; + +#ifdef older + + for ( i=0; i 0 ) + + { + + run = *srcPtr++; + + if ( run >= 128 ) + + { + + /* PACKED RUN */ + + unpacked += (run = 257 - run); + + packed -= 2; + + while ( run-- ) + + *dstPtr++ = *srcPtr; + + srcPtr++; + + } + + else + + { + + /* NONPACKED RUN */ + + run++; + + unpacked += run; + + packed -= run + 1; + + while ( run-- ) + + *dstPtr++ = *srcPtr++; + + } + + } + + + + *cnt = unpacked; + +} + +#endif + + + + + +/* + + * FUNCTION + + * PackBits3 - Use macintosh byte packing algorithm (16 bit data) + + * + + * DESCRIPTION + + * PackBits compresses "width" bytes of data (as 16 bit words) starting + + * at "cIn" and stores the compressed data at "cOut". The 16 bits words + + * are compressed when there are three or more consecutive equal words. + + * After the data is compressed, the value of "width" is the number of + + * compressed bytes stored at "cOut". In the worst case, the compressed + + * data can be one 16 bit word (2 bytes) longer than the original data. + + */ + + + +void + +#ifdef __STDC__ + +PackBits3( unsigned char *cIn, unsigned char *cOut, unsigned int * width ) + +#else + +PackBits3( cIn, cOut, width ) + + unsigned char *cIn; + + unsigned char *cOut; + + unsigned int * width; + +#endif + +{ + + int cnt; + + int len; + + unsigned int wid; + + + + wid = *width; + + len = 0; + + + + /* Loop thru the unencoded 16 bit words */ + + while ( wid > 0 ) + + { + + + + if ( wid == 1 ) + + { /* WE HAVE 1 BYTE LEFT */ + + cOut[0] = 0; /* THIS CASE SHOULD NEVER HAPPEN: ODD BYTE COUNT */ + + cOut[1] = 0; /* BUT, PAD THE ODD BYTE WITH A ZERO BYTE */ + + cOut[2] = cIn[0]; + + len += 3; + + wid = 0; + + } + + else if ( (wid == 2) || (wid == 3) ) + + { /* WE HAVE 2 OR 3 BYTES LEFT */ + + cOut[0] = 0; + + cOut[1] = cIn[0]; + + cOut[2] = cIn[1]; + + len += 3; + + wid -= 2; + + } + + else if ( (wid == 4) || (wid == 5) ) + + { /* WE HAVE 4 OR 5 BYTES LEFT */ + + cOut[0] = 1; + + cOut[1] = cIn[0]; + + cOut[2] = cIn[1]; + + cOut[3] = cIn[2]; + + cOut[4] = cIn[3]; + + len += 5; + + wid -= 4; + + } + + else if ( wid == 6 ) + + { + + if ( (cIn[0] == cIn[2]) + + && (cIn[1] == cIn[3]) + + && (cIn[2] == cIn[4]) + + && (cIn[3] == cIn[5]) ) + + { + + cOut[0] = (256 - 3) + 1; + + cOut[1] = cIn[0]; + + cOut[2] = cIn[1]; + + len += 3; + + wid = 0; + + } else + + { + + cOut[0] = 2; + + cOut[1] = cIn[0]; + + cOut[2] = cIn[1]; + + cOut[3] = cIn[2]; + + cOut[4] = cIn[3]; + + cOut[5] = cIn[4]; + + cOut[6] = cIn[5]; + + len += 7; + + wid = 0; + + } + + } + + else if ( (cIn[0] == cIn[2]) + + && (cIn[1] == cIn[3]) + + && (cIn[2] == cIn[4]) + + && (cIn[3] == cIn[5]) ) + + { + + /* PACKED RUN */ + + /* We have >= 3 equal words */ + + cnt=3; + + while ( ( cnt= 3 unequal words */ + + cnt=0; + + while ( ( cnt 0 ) + + { + + run = *srcPtr++; + + if ( run >= 128 ) + + { + + /* PACKED RUN */ + + run = 256 - run + 1; + + unpacked += (run*2); + + packed -= 3; + + while ( run-- ) + + { + + *dstPtr++ = srcPtr[0]; + + *dstPtr++ = srcPtr[1]; + + } + + srcPtr++; + + srcPtr++; + + } + + else + + { + + /* NONPACKED RUN */ + + run++; + + unpacked += (run*2); + + packed -= (run*2 + 1); + + while ( run-- ) { + + *dstPtr++ = *srcPtr++; + + *dstPtr++ = *srcPtr++; + + } + + } + + } + + + + *cnt = unpacked; + +} + + + diff --git a/utils/roq2/libim/unistd.h b/utils/roq2/libim/unistd.h new file mode 100644 index 0000000..faeb5a9 --- /dev/null +++ b/utils/roq2/libim/unistd.h @@ -0,0 +1,16 @@ +/* unistd.h */ + + + + typedef unsigned char u_char; + + typedef unsigned short u_short; + + typedef unsigned int u_int; + + typedef unsigned int uint; + + typedef unsigned long u_long; + + typedef unsigned short ushort; /* sys III compat */ + diff --git a/utils/roq2/libsdsc/arg.c b/utils/roq2/libsdsc/arg.c new file mode 100644 index 0000000..0365b2a --- /dev/null +++ b/utils/roq2/libsdsc/arg.c @@ -0,0 +1,3062 @@ +/** + ** $Header: /roq/libsdsc/arg.c 1 11/02/99 4:38p Zaphod $ + ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) + ** a division of General Atomics, San Diego, California, USA + ** + ** Users and possessors of this source code are hereby granted a + ** nonexclusive, royalty-free copyright and design patent license to + ** use this code in individual software. License is not granted for + ** commercial resale, in whole or in part, without prior written + ** permission from SDSC. This source is provided "AS IS" without express + ** or implied warranty of any kind. + ** + ** For further information contact: + ** E-Mail: info@sds.sdsc.edu + ** + ** Surface Mail: Information Center + ** San Diego Supercomputer Center + ** P.O. Box 85608 + ** San Diego, CA 92138-5608 + ** (619) 534-5000 + **/ + +#define HEADER " $Header: /roq/libsdsc/arg.c 1 11/02/99 4:38p Zaphod $" + +/** + ** FILE + ** arg.c - Argument Parsing Package + ** + ** PROJECT + ** libsdsc - SDSC Utility Library + ** + ** DESCRIPTION + ** This file contains source for the argument parsing package. + ** These functions simplify the parsing of complex command line + ** argument groupings and partially enforce the SDSC tool conventions. + ** + ** PUBLIC CONTENTS + ** d =defined constant + ** f =function + ** m =defined macro + ** t =typedef/struct/union + ** v =variable + ** ? =other + ** + ** ArgErrNo v error number + ** ArgNNrr v number of error messages + ** ArgErrList v error messages + ** ArgPError f Print error message + ** ArgQError f Query error message + ** + ** ArgHelp f print a short help message + ** ArgFullHelp f print a full-length help message + ** ArgFeedback f dump a feedback from to a file + ** ArgRegister f dump a user-registration form to a file + ** ArgVersion f print version number info to stderr + ** + ** ArgParse f Parse command line arguments + ** + ** ArgQNOpt f Query # of Options on Command-Line + ** ArgQOpt f Query Option on Command-Line + ** + ** ArgQNOccur f query # of occurrences of an option + ** ArgQNValue f query # of values for an occurrence of an option + ** ArgQOccurOpt f query which command-line option this occurrence is + ** ArgQValue f query value for an occurrence of an option + ** + ** PRIVATE CONTENTS + ** + ** argValue t info on one value of an occurrence of an option + ** argOccur t info on one occurrence of an option + ** argInfo t info on an option and all its occurrences + ** argKeyword t info on a keyword in the hash table + ** + ** ARGNSTANDARD d # of standard options + ** argStandard v standard options + ** argStandardRegister v standard -register option + ** argStandardFeedback v standard -feedback option + ** + ** argKeywordTable v Keyword hash table + ** argKeywordTableLength v Length of the hash table + ** + ** argHelpOption t alphabetical option list entry + ** argHelpCommandName v name of the command (argv[0]) + ** argHelpCommand v command struct for help + ** argHelpNOptionList v # of options in list + ** argHelpOptionList v options list for help + ** + ** argOptionOrder v Options in command-line order + ** argNOpt v Number of options on command-line + ** + ** argPrintForm f print a form to a file + ** argHash f hash a keyword to get its hash table index + ** argFind f find a keyword's entry in the hash table + ** argAdd f add an entry to the hash table + ** argGetValue f parse a value out of an argument + ** argSortCompare f Comparison function for qsort + ** + ** argCacheKeyword v keyword of last option queried + ** argCacheInfo v info for last option queried + ** argCacheNOccur v # of last occurrence queried + ** argCacheOccur v occurrence for last occurrence queried + ** argCacheNValue v # of last value queried + ** argCacheValue v value for last value queried + ** + ** HISTORY + ** $Log: /roq/libsdsc/arg.c $ +* +* 1 11/02/99 4:38p Zaphod + ** Revision 1.18 1995/06/29 00:17:39 bduggan + ** updated copyright + ** + ** Revision 1.17 1995/06/29 00:15:29 bduggan + ** added prototype for argPrintForm, added include file for sun + ** + ** Revision 1.16 1995/05/18 18:41:30 bduggan + ** Fixed bug w/ gcc on sun's (Can't change char*'s in + ** protected memory) + ** + ** Revision 1.15 1995/04/21 17:55:23 bduggan + ** Added command-specific help capability. + ** Removed protoypes for standard ansi functions. + ** Added include files. + ** + ** Revision 1.15 1995/04/21 17:55:23 bduggan + ** Added command-specific help capability. + ** Removed protoypes for standard ansi functions. + ** Added include files. + ** + ** Revision 1.14 94/10/03 16:09:41 nadeau + ** Updated to ANSI C and C++ compatibility. + ** Removed all use of register keyword. + ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) + ** Added forward declarations. + ** Added misc. casts to passify SGI and DEC compilers. + ** Updated comments. + ** Updated indenting on some code. + ** Updated copyright message. + ** + ** Revision 1.13 93/07/26 09:24:46 allans + ** fixed bug in ArgParse initialization loop. Now all NEXT pointers + ** are set to NIL. + ** + ** Revision 1.12 92/09/10 16:05:51 vle + ** Added extern declaration of argPrintForm() to make SGI + ** compiler happy. + ** + ** Revision 1.11 92/09/02 13:54:41 vle + ** Updated copyright notice. + ** + ** Revision 1.10 91/10/03 13:11:55 nadeau + ** Comment updates. + ** + ** Revision 1.9 91/09/17 20:04:12 nadeau + ** Added support for arg_fullusage field of ArgCommand struct. + ** + ** Revision 1.8 91/09/17 19:32:46 nadeau + ** Fixed a few minor bugs relating to splitting off -register + ** and -feedback into their own standard option tables and + ** adding -fullhelp. Added hash table debug print routine. + ** Added code to copy the help text to a tmp buffer before + ** modifying it prior to printing. This was necessary for + ** the NeXT, which defaults to putting initialized strings + ** into read-only segments. When we tried to modify the help + ** text, we died. + ** + ** Revision 1.7 91/09/01 17:12:08 nadeau + ** Changed help support. Added support for -fullhelp. Changed + ** ArgHelp to display user's usage line and copyright message.` + ** Added ArgFullHelp. Changed ArgVersion to display user's + ** version and copyright messages. Changed ArgRegister and + ** ArgFeedback to use the user's form strings. + ** + ** Revision 1.6 91/08/25 13:56:38 nadeau + ** Added support for ArgQOccurOpt. + ** + ** Revision 1.5 91/03/11 12:58:56 nadeau + ** Updated feedback and registration forms. + ** + ** Revision 1.4 91/01/11 11:34:16 nadeau + ** Added #ifndef's around INDEX and RINDEX macros. + ** + ** Revision 1.3 91/01/09 16:34:23 nadeau + ** Added ArgQError() and enhanced handling of multiple occurrence + ** implied keywords. + ** + ** Revision 1.2 90/05/16 12:19:09 nadeau + ** Removed bogus extra check on keywords before adding them. It caused + ** later equivs that were the same as earlier abreviations of equivs to + ** bomb out with a duplicate keyword message when it should have just + ** removed the extra colliding earlier abreviations. + ** + ** Revision 1.1 90/05/16 11:53:54 nadeau + ** Initial revision + ** + **/ +/*LINTLIBRARY*/ + +#include "sdsccopyright.h" +#include +#include +#include +#include +#include +#include +#ifdef sun +#include /* for strtod() */ +#endif +#include "sdsc.h" + + +#ifdef __STDC__ +static int argPrintForm( char *, char *, char *, int ); +#else +static int argPrintForm( ); +#endif + + + + + +#ifndef NULL +#define NULL 0 +#endif + +#ifndef MAXINT +#define MAXINT (((unsigned long)(~((unsigned long)0)))>>1) +#endif + +#ifndef W_OK +#define W_OK 0x02 +#define F_OK 0x00 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + + + + +/* + * FUNCTION PROTOTYPES + */ + +#ifdef __STDC__ + +static int argPrintForm( char *, char *, char *, int ); +static void argPrintWithNames(char *, char *); + +#else + +static int argPrintForm( ); +static void argPrintWithNames(); + +#endif + + + + + +/* + * GLOBAL VARIABLE + * ArgErrNo - error number + * ArgNErr - number of error messages + * ArgErrList - error messages + * + * DESCRIPTION + * On an error, the argument parsing package routines return -1 and set + * ArgErrNo to an error code. The programmer may call ArgPError + * to print the associated error message to stderr, or may do the + * message lookup in ArgErrList themselves. + */ +int ArgErrNo = -1; /* Arg package error number */ +char *ArgErrList[] = /* Error message list */ +{ + /* ARGESYS */ "System error: see errno", + /* ARGEUNKKEYWORD */ "Unknown keyword", + /* ARGENOPT */ "Bad option number", + /* ARGENOCCUR */ "Bad occurrence number", + /* ARGENVALUE */ "Bad value number", + /* ARGENOVALUE */ "Option has no values", + /* ARGEREG */ "Cannot create registration form file", + /* ARGEFBK */ "Cannot create feedback form file", +}; +int ArgNErr = 8; /* Number of error messages */ + + + + + +/* + * TYPEDEF & STRUCTURE + * argValue - info on one value of an occurrence of an option + * argOccur - info on one occurrence of an option + * argInfo - info on an option and all its occurrences + * argKeyword - info on a keyword in the hash table + * + * DESCRIPTION + * From top down, the keyword hash table is constructed of argKeyword + * structs, each one containing the keyword hashed, and a pointer to + * an argInfo struct for it. Hash collisions are handled by constructing + * a linked list of argKeyword structs hanging off the initial argKeyword + * struct in the table. + * + * An argInfo struct contains a pointer to the caller's original + * ArgOption description, a count of the number of occurrences of + * the option on the parsed command line (initially zero), and a pointer + * to a linked list of such occurrences (initially NULL). + * + * An argOccur struct contains info on a single occurrence of an option + * and its values on the command line. It contains a count of the + * number of values and a linked list of that many argValue structs. + * argOccur struct's are also linked together into a linked list in the + * order in which options occur on the command-line. arg_next points to + * the next in this list, while arg_option is a back-pointer to the + * ArgOption struct for the option. + * + * An argValue struct is just an ArgValue struct with a linked list pointer + * to point to the next value in the list. + */ + +typedef struct argValue +{ + ArgValue arg_value; /* Value information */ + struct argValue *arg_next; /* Next in value list */ +} argValue; + +typedef struct argOccur +{ + int arg_nvalue; /* Number of values in occurrence*/ + argValue *arg_value; /* List of values for option */ + struct argOccur *arg_next; /* Next in occurrence list */ + struct ArgOption *arg_option; /* Option owning this data */ + struct argOccur *arg_clnext; /* Next in command line order */ + int arg_noption; /* Option # in command line order*/ +} argOccur; + +typedef struct argInfo +{ + ArgOption *arg_option; /* Option for argument */ + int arg_noccur; /* Number of occurrences of option*/ + argOccur *arg_occur; /* List of occurrences */ +} argInfo; + +typedef struct argKeyword +{ + char arg_keyword[ARGKMAXLEN];/* Keyword name */ + argInfo *arg_info; /* Information on option */ + struct argKeyword *arg_next; /* Next in collision list */ +} argKeyword; + + + + + +/* + * GLOBAL + * argStandard - standard options + * argStandardRegister - standard -register option + * argStandardFeedback - standard -feedback option + * + * DESCRIPTION + * argStandard provides information on the standard options -help, + * -feedback, -register, and -version. The hash table is primed with + * these options before parsing the command-line. + */ + +#define ARGNSTANDARD 3 +static ArgOption argStandard[ARGNSTANDARD] = +{ + { "help", NULL, "Give help about specific options", + ARGFNONE, 0, 0, ARGTNONE }, + { "fullhelp", NULL, "Display a full-length help message", + ARGFNONE, 0, 0, ARGTNONE }, + { "version", NULL, "Display the command's version numbers", + ARGFFULLHELP, 0, 0, ARGTNONE } +}; + +static ArgOption argStandardRegister[1] = +{ + { "register", NULL, "Generate a user registration form%end\ + Create a software user registration form in the file %command.reg.0", + ARGFFULLHELP, 0, 0, ARGTNONE }, +}; + +static ArgOption argStandardFeedback[1] = +{ + { "feedback", NULL, "Generate a feedback form (bug report)%end\ + Create a software feedback (bug report) form in the file %command.fbk.0", + ARGFFULLHELP, 0, 0, ARGTNONE }, +}; +#define ARGNSTANDARDOPT (ARGNSTANDARD + 2) + + + + + +/* + * GLOBALS + * argKeywordTable - Keyword hash table + * argKeywordTableLength - Length of the hash table + * + * DESCRIPTION + * The keyword hash table is the primary data structure for referencing + * parsed command-line information. All keywords, equivalent keywords, + * and their truncation abbreviations are entered into the hash table. + * Keywords, equivalent keywords and abbreviations that all refer to + * the same option, all point to the same argInfo structure. + * + * The size of the hash table is determined and the space dynamically + * allocated by ArgParse(). + */ +static int argKeywordTableLength = 0;/* Size of hash table */ +static argKeyword *argKeywordTable = NULL;/* Hash table of keywords */ + +/* + * MACROS + * EndOfLineString + * EndOfLineStringLength + * + * DESCRIPTION + * This symbol in the help string seperates the one line help string + * from the detailed help string. i.e. The detailed help string is only + * printed when -help -option is specified. The one line help string + * is printed when the options are listed. + * + * EndOfLineStringLength is the length of the complete string. + */ + +#define EndOfLineString "%end" +#define EndOfLineStringLength 4 + + + + + +/* + * TYPEDEF & STRUCTURE + * argHelpOption - alphabetical option list entry + * + * DESCRIPTION + * For the printing of the option list information in a help message, + * ArgParse() creates alphabetically sorted lists of the option + * structures. Each entry in the list is an argHelpOption struct. + * Each struct gives the keyword or equivalent keyword and a pointer + * to the option information. Note that abbreviated keywords and + * equivalent keywords are not entered in this list. + */ +typedef struct argHelpOption +{ + char *arg_keyword; + ArgOption *arg_option; +} argHelpOption; + +#ifdef __STDC__ +static int argSortCompare( argHelpOption *, argHelpOption * ); +static char* argPrintOneLineHelp( argHelpOption *); +#else +static int argSortCompare( ); +static char* argPrintOneLineHelp( ); +#endif + + + + + +/* + * GLOBALS + * argHelpCommandName - name of the command (argv[0]) + * argHelpCommand - command struct for -help and -fullhelp + * argHelpNOptionList - # of options in argHelpOptionList + * argHelpOptionList - options list for -help + * argFullHelpNOptionList - # of options in argFullHelpOptionList + * argFullHelpOptionList - options list for -fullhelp + * + * DESCRIPTION + * These globals provide information needed by ArgHelp() in printing + * a help message. + * + * argHelpCommandName is the name of the command, as invoked. This + * is simply argv[0]. + * + * argHelpCommand is a pointer to the user's ArgCommand struct. + * + * ArgHelpNOptionList and ArgFullHelpNOptionList are the lengths of the + * alphabetical option lists, and ArgHelpOptionList and ArgFullHelpOption- + * List are pointers to the heads of these lists. The alphabetical + * option lists are used in printing the list of options in help messages. + */ + +static char *argHelpCommandName; /* argv[0] */ +static ArgCommand *argHelpCommand; /* Command info */ +static int argHelpNOptionList; /* Length of list */ +static argHelpOption *argHelpOptionList; /* Sorted option list */ +static int argFullHelpNOptionList; /* Length of list */ +static argHelpOption *argFullHelpOptionList; /* Sorted option list */ + + + + + +/* + * GLOBALS + * argOptionOrder - Options in command-line order + * argNOpt - Number of options on command-line + * + * DESCRIPTION + * During the parsing of the command-line, the options are linked + * in to a command-line order list of occurrences and the number of + * options counted. + */ +static int argNOpt = 0; /* # of options */ +static argOccur *argOptionOrder = NULL; /* Command-line order list*/ + + + + + +/* + * FUNCTION + * ArgPError - Print error message + * + * DESCRIPTION + * The error text associated with the current ArgErrNo is printed + * to stderr, preceded by the given leader string. + */ + +void /* Returns nothing */ +#ifdef __STDC__ +ArgPError( char *s ) +#else +ArgPError( s ) + char *s; /* Leader string */ +#endif +{ + if ( ArgErrNo == ARGESYS ) + perror( s ); + else if ( ArgErrNo < 0 || ArgErrNo >= ArgNErr ) + (void)fprintf( stderr, "Unknown error\n" ); + else if ( s && *s ) + (void)fprintf( stderr, "%s: %s\n", s, ArgErrList[ArgErrNo] ); + else + (void)fprintf( stderr, "%s\n", ArgErrList[ArgErrNo] ); +} + + + + + +/* + * FUNCTION + * ArgQError - Query error message + * + * DESCRIPTION + * The error text associated with the current ArgErrNo is returned. + */ + +extern int errno; /* System call error code */ +extern int sys_nerr; /* # of system call error codes */ +extern char *sys_errlist[]; /* Error code message strings */ + +char * /* Returns error text */ +#ifdef __STDC__ +ArgQError( void ) +#else +ArgQError( ) +#endif +{ + if ( ArgErrNo == ARGESYS ) + { + if ( errno < 0 || errno >= sys_nerr ) + return ( "Unknown error" ); + return ( sys_errlist[errno] ); + } + if ( ArgErrNo < 0 || ArgErrNo >= ArgNErr ) + return ( "Unknown error" ); + return ( ArgErrList[ArgErrNo] ); +} + + + + + +/* + * FUNCTION + * argHash - hash a keyword to get its hash table index + * + * DESCRIPTION + * The keyword's ASCII characters are added together and the result + * modulo the hash table size returned as the hash table index. + */ + +static int /* Returns hash index */ +#ifdef __STDC__ +argHash( char *keyword ) +#else +argHash( keyword ) + char *keyword; /* Keyword to hash */ +#endif +{ + int i = 0; /* Hash index */ + + while ( *keyword ) + i += *keyword++; + return ( i % argKeywordTableLength ); +} + + + + + +#ifdef DEBUG +/* + * FUNCTION + * argPrint - print the hash table for debugging + * + * DESCRIPTION + * The hash table is walked and each entry, and its collision chain + * printed to stderr. + */ + +static void /* Returns nothing */ +#ifdef __STDC__ +argPrint( void ) +#else +argPrint( ) +#endif +{ + int i = 0; /* Hash table index */ + argKeyword *ak; /* Hash table pointer */ + + (void)fprintf( stderr, "Hash table dump (%d entries):\n", argKeywordTableLength ); + for ( i = 0; i < argKeywordTableLength; i++ ) + { + (void)fprintf( stderr, "[%2d] ", i ); + ak = &argKeywordTable[i]; + if ( ak->arg_info == NULL ) + { + (void)fprintf( stderr, "empty\n" ); + continue; + } + (void)fprintf( stderr, "%s (%s)\n", ak->arg_keyword, + ak->arg_info->arg_option->arg_keyword ); + for ( ak = ak->arg_next; ak; ak = ak->arg_next ) + (void)fprintf( stderr, " %s (%s)\n", ak->arg_keyword, + ak->arg_info->arg_option->arg_keyword ); + } +} +#endif /* DEBUG */ + + + + + +/* + * FUNCTION + * argFind - find a keyword's entry in the hash table + * + * DESCRIPTION + * The keyword is hashed and looked up in the hash table. The table's + * first entry, and its collision list, are searched until an entry + * is found that matches the keyword. A pointer to the entry found, + * or NULL if none is found, is returned. + */ + +static argInfo * /* Returns table entry */ +#ifdef __STDC__ +argFind( char *keyword ) +#else +argFind( keyword ) + char *keyword; /* Keyword to find in table */ +#endif +{ + argKeyword *ak; /* Keyword entry found */ + + ak = &argKeywordTable[argHash( keyword )]; + while ( ak && strcmp( ak->arg_keyword, keyword ) != 0 ) + ak = ak->arg_next; + if ( !ak ) + return ( NULL ); + return ( ak->arg_info ); +} + + + + + +/* + * FUNCTION + * argAdd - add an entry to the hash table + * + * DESCRIPTION + * The keyword, and all its truncation abbreviations, are added to + * the hash table. For each one of these, the name is hashed and + * looked up in the hash table. The table entry, and each entry in + * its collision list is searched to see if the name is already in + * the table. If it is, then either we have an error (if both + * that in the table, and the one we are adding are not abbreviations), + * or we have two keywords with a common set of abbreviations. + * In the later case we search for those abbreviations and remove them. + */ + +static int /* Returns status */ +#ifdef __STDC__ +argAdd( char *keyword, argInfo *info ) +#else +argAdd( keyword, info ) + char *keyword; /* Keyword to add to hash table */ + argInfo *info; /* Options occurrence info */ +#endif +{ + char *s; /* String pointer */ + argKeyword *ak; /* Hash table entry */ + char str[ARGKMAXLEN]; /* Temporary keyword holder */ + argKeyword *aktmp; /* Temp hash table entry */ + argKeyword *akfirst; /* First Hash table entry */ + int len; /* Length of temp keyword holder*/ + argInfo *oldinfo; /* Old option's info */ + + /* + * For every abbreviation of the keyword, add the option and its + * info structure to the hash table. + */ + s = str; + (void)strcpy( s, keyword ); + for ( len = strlen( s ); *s; s[--len] = '\0' ) + { + akfirst = ak = &argKeywordTable[argHash( s )]; + if ( ak->arg_info == NULL ) + { + /* + * Use this entry for the new keyword and option info. + */ + (void)strcpy( ak->arg_keyword, s ); + ak->arg_info = info; + continue; + } + + /* + * Hash table entry already in use. For this entry, + * and each entry in the collision chain, check to + * see if we have the same keyword. + */ + do + { + if ( strcmp( ak->arg_keyword, s ) != 0 ) + continue; /* Not the same */ + + /* + * Keyword's are the same. One of four situations + * has occurred: + * + * 1. Keyword in table is unabbreviated, and our + * new keyword is unabbreviated: + * The programmer goofed. Issue an + * error and exit. + * + * 2. Keyword in table is unabbreviated, and our + * new keyword is abbreviated: + * We can't use this abbreviation, or + * any further ones for our new keyword. + * Remove all abbreviations of table + * keyword. + * + * 3. Keyword in table is abbreviated, and our + * new keyword is unabbreviated: + * We can't use any of our new keyword's + * abbreviations and all shorter + * abbreviations of the table keyword must + * be removed from the table. + * + * 4. Keyword in table is abbreviated, and our new + * keyword is abbreviated: + * This and all further abbreviations of + * our new keyword and the table keyword + * are non-unique and must be removed + * from the table. + */ + oldinfo = ak->arg_info; + if ( strcmp( oldinfo->arg_option->arg_keyword, ak->arg_keyword ) == 0 ) + { + /* + * The keyword in the table already is not + * an abbreviation. + */ + if ( strcmp( s, keyword ) == 0 ) + { + /* + * Neither is this an abbreviation. + * So, we have two keywords that are + * identical. Programmer error. + */ + return ( -2 ); + } + + /* + * All abbreviations of the keyword already + * in the table will collide with those of + * the new keyword. Fall through and delete + * those entries from the table. + */ + s[--len] = '\0'; + } + else + { + /* + * The keyword in the table already is + * an abbreviation. + */ + if ( strcmp( s, keyword ) == 0 ) + { + /* + * Our new keyword isn't yet an + * abbreviation. Take over the + * table entry that used to be for + * some other keyword's abbreviation. + */ + ak->arg_info = info; + s[--len] = '\0'; + } + + /* + * All abbreviations of the keyword already + * in the table will collide with those of + * the new keyword. Fall through and delete + * those entries from the table. + */ + } + + /* + * Remove non-unique abbreviations. + */ + for ( ; *s ; s[--len] = '\0' ) + { + ak = &argKeywordTable[argHash( s )]; + if ( ak->arg_info == NULL ) + continue; /* Nothing there */ + + if ( ak->arg_info == oldinfo && + strcmp( ak->arg_keyword, s ) == 0 ) + { + /* + * Remove entry from table and move + * 1st collision list entry up. + */ + if ( ak->arg_next == NULL ) + { + ak->arg_info = NULL; + continue; + } + aktmp = ak->arg_next; + (void)strcpy( ak->arg_keyword, aktmp->arg_keyword ); + ak->arg_info = aktmp->arg_info; + ak->arg_next = aktmp->arg_next; + free( aktmp ); + /* fall thru to check collisions too */ + } + + /* + * Walk the collision list and unlink the + * abbreviation's entry (if found). + */ + if ( ak->arg_next == NULL ) + continue; + for ( aktmp = ak, ak = ak->arg_next; ak; + aktmp = ak, ak = ak->arg_next ) + { + if ( ak->arg_info != oldinfo ) + continue; + if ( strcmp( ak->arg_keyword, s ) != 0 ) + continue; + aktmp->arg_next = ak->arg_next; + free( ak ); + break; + } + } + return ( 0 ); + } while ( (ak = ak->arg_next) ); + + /* + * None of the collision entries were for this keyword. + * Add us to the front of the collision chain. + */ + if ( (ak = (argKeyword *)malloc( (unsigned int )sizeof( argKeyword ))) == NULL ) + return ( -1 ); + (void)strcpy( ak->arg_keyword, s ); + ak->arg_info = info; + ak->arg_next = akfirst->arg_next; + akfirst->arg_next = ak; + } + return ( 0 ); +} + + + + + +/* + * FUNCTION + * ArgHelp - print a -help message + * ArgFullHelp - print a -fullhelp message + * + * DESCRIPTION + * A help message containing a usage line, the first part of the help + * text, a list of options, and the second part of the help text is + * printed to stderr. + */ + +int /* Returns status */ +#ifdef __STDC__ +ArgHelp( void ) +#else +ArgHelp( ) +#endif +{ + char line[81]; /* Output line buffer */ + char tmp[81]; /* Temporary line buffer */ + char *help1tmp; /* Temporary help string holder */ + char *help2tmp; /* Temporary help string holder */ + int i, j; /* Counter */ + int len; /* Current line length */ + argHelpOption *ah; /* Help option list pointer */ + int nOpt; /* Number of options */ + int specificHelp = 0; /* Flag: Are we doing help for specific options? */ + char *optionName; /* Name of one option */ + int nOccur; /* number of occurences of an option */ + char message[80]; /* Brief message */ + char* tmpStr; /* temporary char * */ + + + /* Give help for specific options, if any are given */ + + if ( (nOpt = ArgQNOpt()) > 1) + specificHelp = 1; + + /* + * Print a usage line of the form: + * + * Usage: name options... + * + * 'name' is argv[0]. + * + * 'options' is either the arg_usage text given in the caller's + * ArgCommand structure, or it is generated automatically as a + * list of the option keywords and their valuenames strings with + * square brackets around optional options and implied keywords. + * Hidden and FullHelp options are omitted. + */ + if ( argHelpCommand->arg_usage ) + (void)fprintf( stderr, "Usage : %s %s\n", argHelpCommandName, + argHelpCommand->arg_usage ); + else + { + (void)sprintf( line, "Usage : %s ", argHelpCommandName ); + len = strlen( line ); + for ( i=0, ah=argHelpOptionList; iarg_option->arg_flags & ARGFHIDDEN) || + (ah->arg_option->arg_flags & ARGFFULLHELP) ) + continue; + if ( ah->arg_option->arg_valuenames && + ah->arg_option->arg_valuenames[0] != '\0' ) + { + if ( ah->arg_option->arg_flags & ARGFREQUIRED ) + { + if ( ah->arg_option->arg_flags & ARGFIMPKEYWORD) + (void)sprintf( tmp, "[-%s] %s ", + ah->arg_keyword, + ah->arg_option->arg_valuenames); + else + (void)sprintf( tmp, "-%s %s ", + ah->arg_keyword, + ah->arg_option->arg_valuenames); + } + else + { + if ( ah->arg_option->arg_flags & ARGFIMPKEYWORD) + (void)sprintf( tmp, "[[-%s] %s] ", + ah->arg_keyword, + ah->arg_option->arg_valuenames); + else + (void)sprintf( tmp, "[-%s %s] ", + ah->arg_keyword, + ah->arg_option->arg_valuenames); + } + } + else + { + if ( ah->arg_option->arg_flags & ARGFREQUIRED ) + { + if ( ah->arg_option->arg_flags & ARGFIMPKEYWORD) + (void)sprintf( tmp, "[-%s] ", + ah->arg_keyword ); + else + (void)sprintf( tmp, "-%s ", + ah->arg_keyword ); + } + else + (void)sprintf( tmp, "[-%s] ", ah->arg_keyword ); + } + + if ( strlen( tmp ) + len > 80 ) + { + (void)fprintf( stderr, "%s\n", line ); + (void)sprintf( line, " " ); + len = strlen( line ); + } + (void)strcat( line, tmp ); + len += strlen( tmp ); + } + (void)fprintf( stderr, "%s\n", line ); + } + + /* + * Print a copyright message, if any. + */ + if ( argHelpCommand->arg_copyright && specificHelp==0) + (void)fprintf( stderr, "\n%s\n\n", argHelpCommand->arg_copyright ); + + + /* + * Make a temporary private copy of the help text before we scan, + * potentially modify, and print it. + * + * This may seem like a dumb, inefficient, and unneccesary thing + * to do, and it is. However, it really is necessary for portability. + * A command's help text is typically assigned to the ArgCommand + * structure by a compile-time initialization. On some hosts (such + * as the NeXT and its MACH Gnu C compiler) such compile-time + * initialized data is placed into a write-protected data segment + * that cannot be changed at run-time without causing a memory error. + * Since we do need to make minor changes at run-time, we are forced + * to make a private copy in an unprotected segment and change the + * copy instead of the original. + */ + if ( argHelpCommand->arg_help1 && specificHelp==0) + { + if ( (help1tmp = (char *)malloc( (unsigned int )(strlen( argHelpCommand->arg_help1 ) + 1) )) == NULL ) + { + (void)fprintf( stderr, "%s: Out of memory in argument parsing!\n", + argHelpCommandName ); + exit( 1 ); + /*NOTREACHED*/ + } + (void)strcpy( help1tmp, argHelpCommand->arg_help1 ); + } + else + help1tmp = NULL; + if ( argHelpCommand->arg_help2 && specificHelp==0) + { + if ( (help2tmp = (char *)malloc( (unsigned int)(strlen( argHelpCommand->arg_help2 ) + 1) )) == NULL ) + { + (void)fprintf( stderr, "%s: Out of memory in argument parsing!\n", + argHelpCommandName ); + exit( 1 ); + /*NOTREACHED*/ + } + (void)strcpy( help2tmp, argHelpCommand->arg_help2 ); + } + else + help2tmp = NULL; + + /* + * If there are options beyond the help option, then + * for each option, print the help text accompanying + * this option (if any). + * + * If there are no options beyond the help option, then + * print the command help text. + */ + if ( specificHelp==1 ) + { + /* Print help for each command that was given. */ + for (i=1; i < nOpt; i++) + { + /* + * Copy the help string into a buffer (for the same + * reasons given above). Then search for the + * string EndOfLineString in the line. If it's there + * then give specific help about this option. + * If it's not there, explain that there is no + * specific help for this option. + * + * You may be wondering why there is simply not + * another element of the structure containing + * extra help for an option. + * + * There should be. However, this package was + * not designed with this function in mind, so + * to preserve backwards compatibility, we're + * doing it this way. + */ + + /* Get the name of option number i */ + optionName = ArgQOpt (i, &nOccur); + + for ( j=0, ah=argFullHelpOptionList; jarg_keyword, optionName)==0) + break; + } + + if (j